aboutsummaryrefslogtreecommitdiff
path: root/host/gif.c
diff options
context:
space:
mode:
Diffstat (limited to 'host/gif.c')
-rw-r--r--host/gif.c115
1 files changed, 79 insertions, 36 deletions
diff --git a/host/gif.c b/host/gif.c
index 5b30e1d..6474269 100644
--- a/host/gif.c
+++ b/host/gif.c
@@ -2,6 +2,7 @@
#include "gif.h"
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
typedef struct {
uint8_t *data;
@@ -9,8 +10,8 @@ typedef struct {
unsigned int length;
} readBuffer_t;
-int gifBufferInput (GifFileType *gif, GifByteType *dest, int n){
- readBuffer_t *rb = gif->userData;
+int gif_buffer_input (GifFileType *gif, GifByteType *dest, int n){
+ readBuffer_t *rb = gif->UserData;
if(rb->index+n > rb->length)
n = rb->length - rb->index;
memcpy(dest, rb->data, n);
@@ -20,20 +21,32 @@ int gifBufferInput (GifFileType *gif, GifByteType *dest, int n){
struct _gifAnimationState {
int idx;
GifFileType *gif;
- framebuffer_t frame;
+ framebuffer_t *frame;
};
+void color_fill(color_t *dest, color_t src, size_t size){
+ /* Some magic to reduce the amount of calls to small-block memcpys */
+ dest[0] = src;
+ size_t i = 1;
+ size_t j = 2;
+ while(j < size){
+ memcpy(dest+i, dest, i*sizeof(color_t));
+ i = j;
+ j *= 2;
+ }
+ memcpy(dest+i, dest, (size-i)*sizeof(color_t));
+}
+
gifAnimationState_t *gif_read(uint8_t *buf, unsigned int buflength){
- /* On first invocation, parse gif and store it in the state struct */
- gifAnimationState_t *st = malloc(sizeof(*state));
+ gifAnimationState_t *st = malloc(sizeof(gifAnimationState_t));
if(!st){
- fprintf(stderr, "Failed to allocate %d bytes\n", sizeof(*state));
+ fprintf(stderr, "Failed to allocate %lu bytes\n", sizeof(*st));
return 0;
}
readBuffer_t readBuf = {buf, 0, buflength};
int err = 0;
- GifFileType *gif = DGifOpen(readBuf, OutputFunc writeFunc, &err);
+ GifFileType *gif = DGifOpen(&readBuf, gif_buffer_input, &err);
if(err){
fprintf(stderr, "Could not read GIF data: %s\n", GifErrorString(err));
goto error;
@@ -50,18 +63,9 @@ gifAnimationState_t *gif_read(uint8_t *buf, unsigned int buflength){
goto error;
}
if(gif->SColorMap){ /* Initially fill framebuffer with background color */
- GifColorType *col = gif->SColorMap[gif->SBackGroundColor]->Colors;
+ GifColorType *col = gif->SColorMap[gif->SBackGroundColor].Colors;
color_t c = {col->Red, col->Green, col->Blue, 255};
- /* Some magic to reduce the amount of calls to small-block memcpys */
- fb[0] = c;
- int i = 1;
- int j = 2;
- while(j < framesize){
- memcpy(fb+i*sizeof(color_t), fb, i*sizeof(color_t));
- i = j;
- j *= 2;
- }
- memcpy(fb+i*sizeof(color_t), fb, (framesize-i)*sizeof(color_t))
+ color_fill(fb, c, framesize);
}else{
/* Set all pixels to a transparent black */
memset(fb, 0, framesize*sizeof(color_t));
@@ -79,12 +83,21 @@ error:
return 0;
}
+void gifAnimationState_free(gifAnimationState_t *st){
+ if(st)
+ framebuffer_free(st->frame);
+ free(st);
+}
+
/* buf ⇜ input data
- * state ⇜ internal state, initialize as NULL */
-framebuffer_t* gif_render(uint8_t *buf, unsigned int buflength, gifAnimationState_t **state){
+ * state ⇜ internal state, initialize as NULL
+ * delay ⇜ is filled with this frame's delay value in milliseconds (if it is not NULL). -1 means "No delay given".
+ * Small note: I mean, rendering a GIF in python is not quite trivial, either (about 20loc). But come on, this is just ridiculous. */
+framebuffer_t *gif_render(uint8_t *buf, unsigned int buflength, gifAnimationState_t **state, int *delay){
gifAnimationState_t *st;
if(*state == NULL){
- st = gif_read(buf, buflength, state);
+ /* On first invocation, parse gif and store it in the state struct */
+ st = gif_read(buf, buflength);
if(!st)
goto error;
*state = st;
@@ -94,41 +107,71 @@ framebuffer_t* gif_render(uint8_t *buf, unsigned int buflength, gifAnimationStat
GifFileType *gif = st->gif;
- SavedImage *img = gif->images[st->idx];
- cmo = img->ImageDesc->ColorMap;
+ /* Find this image's color map */
+ SavedImage *img = gif->SavedImages + st->idx;
+ ColorMapObject *cmo = img->ImageDesc.ColorMap;
if(!cmo){
- ColorMapObject *cmo = gif->SColorMap;
+ cmo = gif->SColorMap;
if(!cmo){
fprintf(stderr, "Missing color table for GIF frame %d\n", st->idx);
goto error;
}
}
- unsigned int ix = img->ImageDesc->Left;
- unsigned int iy = img->ImageDesc->Top;
- unsigned int iw = img->ImageDesc->Width;
- unsigned int ih = img->ImageDesc->Height;
- if((iy+ih)*gif->SWidth + ix+iw > st->frame->w*st->frame->h;){
+
+ /* Extract and validate image's bounds*/
+ unsigned int ix = img->ImageDesc.Left;
+ unsigned int iy = img->ImageDesc.Top;
+ unsigned int iw = img->ImageDesc.Width;
+ unsigned int ih = img->ImageDesc.Height;
+ if((iy+ih)*gif->SWidth + ix+iw > st->frame->w*st->frame->h){
fprintf(stderr, "Invalid gif: Image %d (x %d y %d w %d h %d) out of bounds (w %d h %d)\n",
st->idx, ix, iy, iw, ih, gif->SWidth, gif->SHeight);
goto error;
}
- uint8_t *p = img->RasterBits;
+
+ /* Find and parse this image's GCB (if it exists) */
+ int transparent = -1;
+ int disposal = DISPOSAL_UNSPECIFIED;
+ if(delay)
+ *delay = -1; /* in milliseconds */
+ for(unsigned int i=0; i<img->ExtensionBlockCount; i++){
+ ExtensionBlock *eb = img->ExtensionBlocks + i;
+ if(eb->Function == GRAPHICS_EXT_FUNC_CODE){
+ GraphicsControlBlock *gcb = (GraphicsControlBlock *)eb->Bytes;
+ transparent = gcb->TransparentColor;
+ disposal = gcb->DisposalMode;
+ if(delay)
+ *delay = gcb->DelayTime * 10; /* 10ms → 1ms */
+ }
+ }
+
color_t *fb = st->frame->data;
+ if(disposal == DISPOSE_BACKGROUND || disposal == DISPOSAL_UNSPECIFIED){
+ GifColorType *gc = gif->SColorMap[gif->SBackGroundColor].Colors;
+ color_t c = {gc->Red, gc->Green, gc->Blue, 255};
+ color_fill(fb, c, st->frame->w*st->frame->h);
+ }
+ /* FIXME DISPOSE_PREVIOUS is currently unhandled. */
+
+ /* Render gif bitmap to RGB */
+ uint8_t *p = img->RasterBits;
for(unsigned int y = iy; y < iy+ih; y++){
for(unsigned int x = ix; x < ix+iw; x++){
- GifColorType col = cmo[*p++]->Colors;
- fb[y*st->frame->w + x] = {col.Red, col.Green, col.Blue, 255};
+ int c = *p++;
+ if(c != transparent){
+ GifColorType *col = cmo[c].Colors;
+ color_t ct = {col->Red, col->Green, col->Blue, 255};
+ fb[y*st->frame->w + x] = ct;
+ }
}
}
st->idx++;
if(st->idx >= gif->ImageCount)
st->idx = 0;
- return fb;
+ return st->frame;
error:
- if(st)
- free(st->frame->data);
- free(st);
+ gifAnimationState_free(st);
return 0;
}