Surface to&from a base64 string


Link to Surface to&from a base64 string

for those who do save thumbnails in a gamemaker game and have the unfortunate fate of having to do a console port.

function niklog(msg) {
	show_debug_message(msg);
}

/// @param {Id.Surface} surf input surface
/// @returns {String|Undefined} base64 string or undefined on invalid arguments
function surface_to_string(surf) {
	if (is_undefined(surf)) {
		niklog("surface_to_string: expected a `surf` argument");
		return undefined;
	}
	
	if (!surface_exists(surf)) {
		// or ""? I'm not sure.
		niklog("surface_to_string: invalid `surf` argument: surface does not exist");
		return undefined;
	}
	
	// these cannot be 0.
	var surfw = surface_get_width(surf),
		surfh = surface_get_height(surf);
	var surfbuffer = buffer_create(
		// assume it's 8bpp+alpha
		8 + (surfw * surfh * 4),
		buffer_fixed,
		1
	);
	var surfbuffersize = buffer_get_size(surfbuffer);
	// make sure all the memory is preallocated and filled with 0's
	// for buffer_get_surface
	buffer_fill(surfbuffer, 0, buffer_u8, 0, surfbuffersize);
	// we know for sure neither of these can be 0
	buffer_write(surfbuffer, buffer_u32, surfw);
	buffer_write(surfbuffer, buffer_u32, surfh);
	buffer_get_surface(surfbuffer, surf, buffer_tell(surfbuffer));
	var surfzlibbed = buffer_compress(surfbuffer, 0, surfbuffersize);
	buffer_delete(surfbuffer);
	var surfstring = buffer_base64_encode(surfzlibbed, 0, buffer_get_size(surfzlibbed));
	buffer_delete(surfzlibbed);
	return surfstring;
}

/// @param {String} b64string base64 string to decode
/// @returns {Id.Surface} surface or -1 on error
function surface_from_string(b64string) {
	if (!is_string(b64string) || string_length(b64string) <= 0) {
		// throw an error here??
		niklog("surface_from_string: invalid `b64string` argument: invalid or empty string");
		return -1;
	}
	
	var surfzlibbed = buffer_base64_decode(b64string);
	if (surfzlibbed < 0) {
		// invalid input string?
		niklog("surface_from_string: invalid `b64string` argument: invalid base64 data");
		return -1;
	}
	
	var surfbuffer = buffer_decompress(surfzlibbed);
	buffer_delete(surfzlibbed);
	if (surfbuffer < 0) {
		// invalid compressed data?
		niklog("surface_from_string: invalid zlib data");
		return -1;
	}
	
	if (buffer_get_size(surfbuffer) < 4+4+4) {
		// a valid buffer must be at least 12 bytes long:
		// u32: width (4 bytes)
		// u32: height (4 bytes)
		// u32[]: pixels... (at least 4 bytes for 1 pixel)
		// 4 + 4 + 4 = 12 bytes required for a valid surface.
		buffer_delete(surfbuffer);
		niklog("surface_from_string: invalid decompressed data (too small)");
		return -1;
	}
	
	var surfw = buffer_read(surfbuffer, buffer_u32);
	var surfh = buffer_read(surfbuffer, buffer_u32);
	if (surfw * surfh <= 0) {
		// neither width nor height can be 0
		// probably an empty buffer?
		buffer_delete(surfbuffer);
		niklog("surface_from_string: invalid decompressed data (width or height are 0)");
		return -1;
	}
	var surf = surface_create(surfw, surfh);
	// make sure all pixels are zeroed out.
	surface_set_target(surf);
	draw_clear_alpha(c_black, 0);
	surface_reset_target();
	buffer_set_surface(surfbuffer, surf, buffer_tell(surfbuffer));
	buffer_delete(surfbuffer);
	return surf;
}