legacy-imlib2/src/modules/loaders/loader_gif.c

327 lines
8.7 KiB
C

#include "config.h"
#include "Imlib2_Loader.h"
#include <gif_lib.h>
#define DBG_PFX "LDR-gif"
static const char *const _formats[] = { "gif" };
static struct {
const unsigned char *data, *dptr;
unsigned int size;
} mdata;
static void
mm_init(const void *src, unsigned int size)
{
mdata.data = mdata.dptr = src;
mdata.size = size;
}
static int
mm_read(GifFileType * gif, GifByteType * dst, int len)
{
if (mdata.dptr + len > mdata.data + mdata.size)
return -1; /* Out of data */
memcpy(dst, mdata.dptr, len);
mdata.dptr += len;
return len;
}
static void
make_colormap(uint32_t * cmi, ColorMapObject * cmg, int bg, int tr)
{
int i, r, g, b;
memset(cmi, 0, 256 * sizeof(uint32_t));
if (!cmg)
return;
for (i = cmg->ColorCount > 256 ? 256 : cmg->ColorCount; i-- > 0;)
{
r = cmg->Colors[i].Red;
g = cmg->Colors[i].Green;
b = cmg->Colors[i].Blue;
cmi[i] = PIXEL_ARGB(0xff, r, g, b);
}
/* if bg > cmg->ColorCount, it is transparent black already */
if (tr >= 0 && tr < 256)
cmi[tr] = bg >= 0 && bg < 256 ? cmi[bg] & 0x00ffffff : 0x00000000;
}
static int
_load(ImlibImage * im, int load_data)
{
int rc, err;
uint32_t *ptr;
GifFileType *gif;
GifRowType *rows;
GifRecordType rec;
ColorMapObject *cmap;
int i, j, bg, bits;
int transp;
uint32_t colormap[256];
int fcount, frame;
bool multiframe;
ImlibImageFrame *pf;
rc = LOAD_FAIL;
rows = NULL;
mm_init(im->fi->fdata, im->fi->fsize);
#if GIFLIB_MAJOR >= 5
gif = DGifOpen(NULL, mm_read, &err);
#else
gif = DGifOpen(NULL, mm_read);
#endif
if (!gif)
goto quit;
rc = LOAD_BADIMAGE; /* Format accepted */
pf = NULL;
transp = -1;
fcount = 0;
frame = im->frame;
if (frame > 0)
{
#if 0
if (frame > 1 && frame > gif->ImageCount)
QUIT_WITH_RC(LOAD_BADFRAME);
#endif
pf = __imlib_GetFrame(im);
if (!pf)
QUIT_WITH_RC(LOAD_OOM);
pf->frame_count = gif->ImageCount;
pf->loop_count = 0; /* Loop forever */
if (pf->frame_count > 1)
pf->frame_flags |= FF_IMAGE_ANIMATED;
pf->canvas_w = gif->SWidth;
pf->canvas_h = gif->SHeight;
D("Canvas WxH=%dx%d frames=%d repeat=%d\n",
pf->canvas_w, pf->canvas_h, pf->frame_count, pf->loop_count);
}
else
{
frame = 1;
}
bg = gif->SBackGroundColor;
cmap = gif->SColorMap;
for (;;)
{
if (DGifGetRecordType(gif, &rec) == GIF_ERROR)
{
/* PrintGifError(); */
DL("err1\n");
break;
}
if (rec == TERMINATE_RECORD_TYPE)
{
DL(" TERMINATE_RECORD_TYPE(%d)\n", rec);
break;
}
if (rec == IMAGE_DESC_RECORD_TYPE)
{
if (DGifGetImageDesc(gif) == GIF_ERROR)
{
/* PrintGifError(); */
DL("err2\n");
break;
}
DL(" IMAGE_DESC_RECORD_TYPE(%d): ic=%d x,y=%d,%d wxh=%dx%d\n",
rec, gif->ImageCount, gif->Image.Left, gif->Image.Top,
gif->Image.Width, gif->Image.Height);
fcount += 1;
if (gif->ImageCount != frame)
{
int size = 0;
GifByteType *data;
if (DGifGetCode(gif, &size, &data) == GIF_ERROR)
goto quit;
DL("DGifGetCode: size=%d data=%p\n", size, data);
while (data)
{
if (DGifGetCodeNext(gif, &data) == GIF_ERROR)
{
E("Corrupt image frame\n");
goto done;
}
DL(" DGifGetCodeNext: size=%d data=%p\n", size, data);
}
continue;
}
im->w = gif->Image.Width;
im->h = gif->Image.Height;
if (pf)
{
pf->frame_x = gif->Image.Left;
pf->frame_y = gif->Image.Top;
D("Canvas WxH=%dx%d frame=%d/%d X,Y=%d,%d WxH=%dx%d\n",
pf->canvas_w, pf->canvas_h, gif->ImageCount,
pf->frame_count, pf->frame_x, pf->frame_y, im->w, im->h);
}
else
{
D("WxH=%dx%d\n", im->w, im->h);
}
if (!IMAGE_DIMENSIONS_OK(im->w, im->h))
goto quit;
DL(" CM S=%p I=%p\n", cmap, gif->Image.ColorMap);
if (gif->Image.ColorMap)
cmap = gif->Image.ColorMap;
if (load_data)
make_colormap(colormap, cmap, bg, transp);
rows = calloc(im->h, sizeof(GifRowType));
if (!rows)
QUIT_WITH_RC(LOAD_OOM);
for (i = 0; i < im->h; i++)
{
rows[i] = calloc(im->w, sizeof(GifPixelType));
if (!rows[i])
QUIT_WITH_RC(LOAD_OOM);
}
if (gif->Image.Interlace)
{
static const int intoffset[] = { 0, 4, 2, 1 };
static const int intjump[] = { 8, 8, 4, 2 };
for (i = 0; i < 4; i++)
{
for (j = intoffset[i]; j < im->h; j += intjump[i])
{
DGifGetLine(gif, rows[j], im->w);
}
}
}
else
{
for (i = 0; i < im->h; i++)
{
DGifGetLine(gif, rows[i], im->w);
}
}
/* Break if no specific frame was requested */
if (!pf)
break;
}
else if (rec == EXTENSION_RECORD_TYPE)
{
int ext_code, disp;
GifByteType *ext;
ext = NULL;
DGifGetExtension(gif, &ext_code, &ext);
while (ext)
{
DL(" EXTENSION_RECORD_TYPE(%d): ic=%d: ext_code=%02x: %02x %02x %02x %02x %02x\n", //
rec, gif->ImageCount, ext_code,
ext[0], ext[1], ext[2], ext[3], ext[4]);
if (pf && ext_code == GRAPHICS_EXT_FUNC_CODE
&& gif->ImageCount == frame - 1)
{
bits = ext[1];
pf->frame_delay = 10 * (0x100 * ext[3] + ext[2]);
if (bits & 1)
transp = ext[4];
disp = (bits >> 2) & 0x7;
if (disp == 2 || disp == 3)
pf->frame_flags |= FF_FRAME_DISPOSE_CLEAR;
pf->frame_flags |= FF_FRAME_BLEND;
D(" Frame %d: disp=%d ui=%d tr=%d, delay=%d transp = #%02x\n", //
gif->ImageCount + 1, disp, (bits >> 1) & 1, bits & 1,
pf->frame_delay, transp);
}
ext = NULL;
DGifGetExtensionNext(gif, &ext);
}
}
else
{
DL(" Unknown record type(%d)\n", rec);
}
}
done:
im->has_alpha = transp >= 0;
multiframe = false;
if (pf)
{
pf->frame_count = fcount;
multiframe = pf->frame_count > 1;
if (multiframe)
pf->frame_flags |= FF_IMAGE_ANIMATED;
}
if (!rows)
{
if (pf && frame > 1 && frame > pf->frame_count)
QUIT_WITH_RC(LOAD_BADFRAME);
goto quit;
}
if (!load_data)
QUIT_WITH_RC(LOAD_SUCCESS);
/* Load data */
ptr = __imlib_AllocateData(im);
if (!ptr)
QUIT_WITH_RC(LOAD_OOM);
for (i = 0; i < im->h; i++)
{
for (j = 0; j < im->w; j++)
{
*ptr++ = colormap[rows[i][j]];
}
if (!multiframe && im->lc && __imlib_LoadProgressRows(im, i, 1))
QUIT_WITH_RC(LOAD_BREAK);
}
if (multiframe && im->lc)
__imlib_LoadProgress(im, 0, 0, im->w, im->h);
rc = LOAD_SUCCESS;
quit:
if (rows)
{
for (i = 0; i < im->h; i++)
free(rows[i]);
free(rows);
}
#if GIFLIB_MAJOR > 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR >= 1)
DGifCloseFile(gif, NULL);
#else
DGifCloseFile(gif);
#endif
return rc;
}
IMLIB_LOADER(_formats, _load, NULL);