Skip to content

Instantly share code, notes, and snippets.

@tinkerer-red
Last active March 2, 2025 03:54
Show Gist options
  • Save tinkerer-red/ae6c05e0362fb38b7b55aa17c7cef156 to your computer and use it in GitHub Desktop.
Save tinkerer-red/ae6c05e0362fb38b7b55aa17c7cef156 to your computer and use it in GitHub Desktop.
`buffer_create_from_surface` and `surface_create_from_buffer`
#region jsDoc
/// @func buffer_create_from_surface
/// @desc Creates a fixed-size buffer containing the pixel data from the given surface. The buffer size is determined
/// by the surface's dimensions and format. The buffer is populated with the surface's pixel data starting at byte offset 0.
/// @param {Id.Surface} _surf - The source surface from which to read pixel data.
/// @returns {Id.Buffer} A buffer containing the surface's pixel data.
#endregion
function buffer_create_from_surface(_surf) {
// Get surface properties
var _width = surface_get_width(_surf);
var _height = surface_get_height(_surf);
var _format = surface_get_format(_surf);
// Determine bytes per pixel based on format
var _bytes_per_pixel;
switch (_format) {
case surface_rgba4unorm: _bytes_per_pixel = 2; break;
case surface_rgba8unorm: _bytes_per_pixel = 4; break;
case surface_rgba16float: _bytes_per_pixel = 8; break;
case surface_rgba32float: _bytes_per_pixel = 16; break;
case surface_r8unorm: _bytes_per_pixel = 1; break;
case surface_r16float: _bytes_per_pixel = 2; break;
case surface_r32float: _bytes_per_pixel = 16; break;
case surface_rg8unorm: _bytes_per_pixel = 2; break;
default: throw "Unsupported format"
}
// Create a buffer large enough to store the surface data
var _buff_size = _width * _height * _bytes_per_pixel;
var _buff = buffer_create(_buff_size, buffer_fixed, 1);
// Write surface data into buffer
buffer_get_surface(_buff, _surf, 0);
buffer_seek(_buff, buffer_seek_start, 0);
return _buff;
}
#region jsDoc
/// @func surface_create_from_buffer
/// @desc Creates a new surface with the specified width, height, and format, and populates it with pixel data
/// read from the provided buffer. The buffer data is written to the surface starting at byte offset 0.
/// @param {Id.Buffer} _buff - The buffer containing the pixel data to write to the surface.
/// @param {Real} _width - The width of the new surface.
/// @param {Real} _height - The height of the new surface.
/// @param {Constant.SurfaceFormatConstant} _format - The format to use for the new surface.
/// @returns {Id.Surface} The newly created surface containing the pixel data from the buffer.
#endregion
function surface_create_from_buffer(_buff, _width, _height, _format=surface_rgba8unorm) {
// Create a new surface with the specified width, height, and format.
var _surf = surface_create(_width, _height, _format);
// Determine bytes per pixel based on format
var _bytes_per_pixel;
switch (_format) {
case surface_rgba4unorm: _bytes_per_pixel = 2; break;
case surface_rgba8unorm: _bytes_per_pixel = 4; break;
case surface_rgba16float: _bytes_per_pixel = 8; break;
case surface_rgba32float: _bytes_per_pixel = 16; break;
case surface_r8unorm: _bytes_per_pixel = 1; break;
case surface_r16float: _bytes_per_pixel = 2; break;
case surface_r32float: _bytes_per_pixel = 16; break;
case surface_rg8unorm: _bytes_per_pixel = 2; break;
default: throw "Unsupported format"
}
// Calculate the expected buffer size based on the surface dimensions and bytes per pixel.
var _expected_size = _width * _height * _bytes_per_pixel;
var _actual_size = buffer_get_size(_buff);
// Safety check: Ensure the buffer size matches the expected size.
if (_actual_size != _expected_size) {
show_error("Buffer size mismatch: expected " + string(_expected_size) + " but got " + string(_actual_size), true);
}
// Write the pixel data from the buffer into the surface, starting at byte offset 0.
buffer_set_surface(_buff, _surf, 0);
// Return the newly created surface.
return _surf;
}
function string_real_size(_text, _font) {
// Store previous render settings
var prev_font = draw_get_font();
// Set the font
draw_set_font(_font);
// Get the estimated width and height
var _approx_width = string_width(_text);
var _approx_height = string_height(_text);
// Create a temporary surface
var _surf = surface_create(_approx_width, _approx_height);
surface_set_target(_surf);
draw_clear_alpha(c_black, 0); // Fully transparent background
// Draw the text
draw_set_color(c_white);
draw_text(0, 0, _text);
// Reset render target
surface_reset_target();
// Create a buffer and read the pixel data
var _buff = buffer_create_from_surface(_surf);
var x1 = infinity,
var y1 = infinity,
var x2 = -infinity;
var y2 = -infinity;
buffer_seek(_buff, buffer_seek_start, 0);
// Scan the buffer to find non-transparent pixels
for (var _y = 0; _y < _approx_height; _y++) {
for (var _x = 0; _x < _approx_width; _x++) {
var r = buffer_read(_buff, buffer_u8);
var g = buffer_read(_buff, buffer_u8);
var b = buffer_read(_buff, buffer_u8);
var a = buffer_read(_buff, buffer_u8); // Alpha channel
if (a > 0) { // If the pixel is not fully transparent
if (_x < x1) x1 = _x;
if (_x > x2) x2 = _x;
if (_y < y1) y1 = _y;
if (_y > y2) y2 = _y;
}
}
}
// Calculate the actual width & height
var real_width = (x2 >= x1) ? (x2 - x1 + 1) : 0;
var real_height = (y2 >= y1) ? (y2 - y1 + 1) : 0;
// Cleanup
buffer_delete(_buff);
surface_free(_surf);
draw_set_font(prev_font); // Restore previous font
// Return the real text size and bounding box
return {
width: real_width,
height: real_height,
x1: x1, // Left bound
y1: y1, // Top bound
x2: x2, // Right bound
y2: y2 // Bottom bound
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment