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; 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:
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)");
return 1;
}
#endif