gif.c

#ifdef LIL_USE_GIF
#include <gif_lib.h>

#include "inc.h"

struct GifFakeFile{
	const char* buf;
	unsigned size;
	unsigned cur;
};

static int readFunc(GifFileType* gif, GifByteType* data, int n){
	struct GifFakeFile* file = gif->UserData;
	if(n > file->cur - file->size)
		n = file->cur - file->size;
	memcpy(data, file->buf+file->cur, n);
	file->cur += n;
	return n;
}
static const uint8_t interOffset[4] = { 0, 4, 2, 1 }, interJumps[4] = { 8, 8, 4, 2 };
static void convertRow(lil_Image* img, int w, int y, GifPixelType* row, int trans, ColorMapObject* map){
	for(int x = 0; x < w; x++){
		size_t o = XY(img, x, y);
		GifPixelType p = row[x];
		if(trans >= 0 && trans == p)
			continue;
		GifColorType* colour = &map->Colors[p];
		img->d[o].r = colour->Red;
		img->d[o].g = colour->Green;
		img->d[o].b = colour->Blue;
		img->d[o].a = 0xff;
	}
}

LUAFUNC(importGif){
	size_t len;
	const uint8_t* data = (const uint8_t*)lua_tolstring(L, 1, &len);
	struct GifFakeFile ff = {.buf = (char*)data, .size = len, .cur = 0};

	GifFileType* gif = DGifOpen((void*)&ff, readFunc, NULL);
	if(!gif)
		return 0;

	GifPixelType* row = malloc(gif->SWidth * sizeof(GifPixelType));
	if(!row){
		DGifCloseFile(gif, NULL);
		return 0;
	}

	lil_Image* img = lil_newImageCalloc(L, gif->SWidth, gif->SHeight);
	if(!img){
		free(row);
		DGifCloseFile(gif, NULL);
		return 0;
	}

	int trans = -1, didFail = false;
	GifRecordType type;
	do{
		if(DGifGetRecordType(gif, &type) != GIF_OK)
			goto failed;
		switch(type){
			case IMAGE_DESC_RECORD_TYPE: (void)0;
				if(DGifGetImageDesc(gif) != GIF_OK)
					goto failed;
				GifImageDesc* imgd = &(gif->Image);
				int ox, oy, w, h;
				ox = imgd->Left;
				oy = imgd->Top;
				w = imgd->Width;
				h = imgd->Height;
				if(ox < 0 || oy < 0 || ox + w > img->w || oy + h > img->h)
					goto failed;
				ColorMapObject* map = imgd->ColorMap ? imgd->ColorMap : gif->SColorMap;
				if(!map)
					goto failed;
				if(imgd->Interlace){
					for(uint8_t pass = 0; pass < 4; pass++){
						uint8_t j = interJumps[pass];
						for(int y = interOffset[pass]; y < h; y += j){
							if(DGifGetLine(gif, row, w) == GIF_ERROR)
								goto failed;
							convertRow(img, w, y, row, trans, map);
						}
					}
				} else {
					for(int y = 0; y < h; y++){
						if(DGifGetLine(gif, row, w) == GIF_ERROR)
							goto failed;
						convertRow(img, w, y, row, trans, map);
					}
				}
				goto succ; // XXX Only care about first img for now
				break;
			case EXTENSION_RECORD_TYPE: (void)0;
				GifByteType* extension;
				int extCode;
				if(DGifGetExtension(gif, &extCode, &extension) != GIF_OK)
					goto failed;
				switch(extCode){
					case GRAPHICS_EXT_FUNC_CODE:
						if(extension != NULL)
							trans = (extension[1] & 0x01) ? extension[4] : -1;
						break;
					case COMMENT_EXT_FUNC_CODE:
					case PLAINTEXT_EXT_FUNC_CODE:
					case APPLICATION_EXT_FUNC_CODE:
						break;
				}
				while(extension != NULL){
					if(DGifGetExtensionNext(gif, &extension) != GIF_OK)
						goto failed;
				}
				break;
			case TERMINATE_RECORD_TYPE:
			case SCREEN_DESC_RECORD_TYPE:
				break;
			case UNDEFINED_RECORD_TYPE:
			default:
				goto failed;
		}
	}while(type != TERMINATE_RECORD_TYPE);

	goto succ;

failed:
	/* printf("FAILED: %i %s\n", gif->Error, GifErrorString(gif->Error)); */
	didFail = true;
	lil_freeImage(img);
succ:
	free(row);
	DGifCloseFile(gif, NULL);
	if(didFail)
		return 0;
	lil_pushImage(L, img);
	return 1;
}

LUAFUNC(exportGif){
	luaL_error(L, "Exporting gifs isn't implemented (yet)");
	/* const lil_Image* img = lil_getImage(L, 1); */
	/* lua_pushlstring(L, (const char*)data, len); */
	return 1;
}

#endif
generated by LDoc 1.4.6 Last updated 2023-04-13 13:58:34