µGFX  2.9
version 2.9
gdisp_image_gif.c
1 /*
2  * This file is subject to the terms of the GFX License. If a copy of
3  * the license was not distributed with this file, you can obtain one at:
4  *
5  * http://ugfx.io/license.html
6  */
7 
8 #include "../../gfx.h"
9 
10 #if GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_GIF
11 
12 #include "gdisp_image_support.h"
13 
14 // We need a special error to indicate the end of file (which may not actually be an error)
15 #define GDISP_IMAGE_GIF_EOF ((gdispImageError)-1)
16 #define GDISP_IMAGE_GIF_LOOP ((gdispImageError)-2)
17 
18 #define GIF_MAX_CODE_BITS 12
19 #define GIF_CODE_MAX ((1<<GIF_MAX_CODE_BITS)-1) // Maximum legal code value
20 #define GIF_CODE_FLUSH (GIF_CODE_MAX+1) // Illegal code to signal flush
21 #define GIF_CODE_FIRST (GIF_CODE_MAX+2) // Illegal code to signal first
22 #define GIF_CODE_NONE (GIF_CODE_MAX+3) // Illegal code to signal empty
23 
24 // Convert bits to masks for that number of bits
25 static const gU16 GifBitMask[] = {
26  0x0000, 0x0001, 0x0003, 0x0007,
27  0x000f, 0x001f, 0x003f, 0x007f,
28  0x00ff, 0x01ff, 0x03ff, 0x07ff,
29  0x0fff
30  };
31 
32 // Structure for decoding a single frame
33 typedef struct gifimgdecode {
34  gU8 blocksz; // The size of the block currently being processed
35  gU8 maxpixel; // The maximum allowed pixel value
36  gU8 bitsperpixel;
37  gU8 bitspercode;
38  gU8 shiftbits;
39  gU16 maxcodesz;
40  gU16 stackcnt; // The number of items on the stack
41  gU16 code_clear;
42  gU16 code_eof;
43  gU16 code_max;
44  gU16 code_last;
45  gU32 shiftdata;
46  gColor * palette;
47  gU8 buf[GDISP_IMAGE_GIF_BLIT_BUFFER_SIZE]; // Buffer for decoded pixels
48  gU16 prefix[1<<GIF_MAX_CODE_BITS]; // The LZW table
49  gU8 suffix[1<<GIF_MAX_CODE_BITS]; // So we can trace the codes
50  gU8 stack[1<<GIF_MAX_CODE_BITS]; // Decoded pixels might be stacked here
51 } gifimgdecode;
52 
53 // The data on a single frame
54 typedef struct gifimgframe {
55  gCoord x, y; // position relative to full image
56  gCoord width, height; // size of frame
57  gU16 delay; // delay after processing
58  gU8 flags; // Local flags
59  #define GIFL_TRANSPARENT 0x01 // There exists a transparent color
60  #define GIFL_DISPOSECLEAR 0x02 // Dispose this frame by clearing
61  #define GIFL_DISPOSEREST 0x04 // Dispose this frame by restoring
62  #define GIFL_INTERLACE 0x08 // Current frame is interlaced
63  gU8 paltrans; // Transparency
64  gU16 palsize; // Local palette size
65  gFileSize posstart; // The file position of the start of the image
66  gFileSize pospal; // The file position of the palette
67  gFileSize posimg; // The file position of the image bits
68  gFileSize posend; // The file position of the end of the frame
69 } gifimgframe;
70 
71 // The data for a cache
72 typedef struct gifimgcache {
73  gifimgframe frame;
74  gColor * palette; // Local palette
75  gU8 * imagebits; // Image bits - only saved when caching
76  struct gifimgcache *next; // Next cached frame
77 } gifimgcache;
78 
79 // The data for a dispose area
80 typedef struct gifimgdispose {
81  gU8 flags; // Frame flags
82  gU8 paltrans; // Transparency
83  gCoord x, y; // position relative to full image
84  gCoord width, height; // size of dispose area
85 } gifimgdispose;
86 
87 typedef struct gdispImagePrivate_GIF {
88  gU8 flags; // Flags (global)
89  #define GIF_LOOP 0x01 // Loop back to first frame
90  #define GIF_LOOPFOREVER 0x02 // Looping is forever
91  gU8 bgcolor; // Background Color (global)
92  gU16 loops; // Remaining frame loops (if animated)
93  gU16 palsize; // Global palette size (global)
94  gPixel *palette; // Global palette (global)
95  gFileSize frame0pos; // The position of the first frame
96  gifimgcache * cache; // The list of cached frames
97  gifimgcache * curcache; // The cache of the current frame (if created)
98  gifimgdecode * decode; // The decode data for the decode in progress
99  gifimgframe frame;
100  gifimgdispose dispose;
101  gPixel buf[GDISP_IMAGE_GIF_BLIT_BUFFER_SIZE]; // Buffer for reading and blitting
102  } gdispImagePrivate_GIF;
103 
104 /**
105  * Get ready for decoding a frame.
106  *
107  * Pre: Frame info has been read.
108  */
109 static gdispImageError startDecodeGif(gImage *img) {
110  gdispImagePrivate_GIF * priv;
111  gifimgdecode * decode;
112  gU16 cnt;
113 
114  priv = (gdispImagePrivate_GIF *)img->priv;
115 
116  // We need the decode ram, and possibly a palette
117  if (!(decode = (gifimgdecode *)gdispImageAlloc(img, sizeof(gifimgdecode)+priv->frame.palsize*sizeof(gColor))))
118  return GDISP_IMAGE_ERR_NOMEMORY;
119 
120  // We currently have not read any image data block
121  decode->blocksz = 0;
122 
123  // Set the palette
124  if (priv->frame.palsize) {
125  // Local palette
126  decode->maxpixel = priv->frame.palsize-1;
127  decode->palette = (gColor *)(decode+1);
128  gfileSetPos(img->f, priv->frame.pospal);
129  for(cnt = 0; cnt < priv->frame.palsize; cnt++) {
130  if (gfileRead(img->f, &decode->buf, 3) != 3)
131  goto baddatacleanup;
132  decode->palette[cnt] = RGB2COLOR(decode->buf[0], decode->buf[1], decode->buf[2]);
133  }
134  } else if (priv->palette) {
135  // Global palette
136  decode->maxpixel = priv->palsize-1;
137  decode->palette = priv->palette;
138  } else {
139  // Oops - we must have a palette
140  goto baddatacleanup;
141  }
142 
143  // Get the initial lzw code size and values
144  gfileSetPos(img->f, priv->frame.posimg);
145  if (gfileRead(img->f, &decode->bitsperpixel, 1) != 1 || decode->bitsperpixel >= GIF_MAX_CODE_BITS)
146  goto baddatacleanup;
147  decode->code_clear = 1 << decode->bitsperpixel;
148  decode->code_eof = decode->code_clear + 1;
149  decode->code_max = decode->code_clear + 2;
150  decode->code_last = GIF_CODE_NONE;
151  decode->bitspercode = decode->bitsperpixel+1;
152  decode->maxcodesz = 1 << decode->bitspercode;
153  decode->shiftbits = 0;
154  decode->shiftdata = 0;
155  decode->stackcnt = 0;
156  for(cnt = 0; cnt <= GIF_CODE_MAX; cnt++)
157  decode->prefix[cnt] = GIF_CODE_NONE;
158 
159  // All ready to go
160  priv->decode = decode;
161  return GDISP_IMAGE_ERR_OK;
162 
163 baddatacleanup:
164  gdispImageFree(img, decode, sizeof(gifimgdecode)+priv->frame.palsize*sizeof(gColor));
165  return GDISP_IMAGE_ERR_BADDATA;
166 }
167 
168 /**
169  * Stop decoding a frame.
170  *
171  * Pre: Frame info has been read.
172  */
173 static void stopDecodeGif(gImage *img) {
174  gdispImagePrivate_GIF * priv;
175 
176  priv = (gdispImagePrivate_GIF *)img->priv;
177 
178  // Free the decode data
179  if (priv->decode) {
180  gdispImageFree(img, (void *)priv->decode, sizeof(gifimgdecode)+priv->frame.palsize*sizeof(gColor));
181  priv->decode = 0;
182  }
183 }
184 
185 static gU16 getPrefixGif(gifimgdecode *decode, gU16 code) {
186  gU16 i;
187 
188  for(i=0; code > decode->code_clear && i <= GIF_CODE_MAX; i++, code = decode->prefix[code]) {
189  if (code > GIF_CODE_MAX)
190  return GIF_CODE_NONE;
191  }
192  return code;
193 }
194 
195 /**
196  * Decode some pixels from a frame.
197  *
198  * Pre: We are ready for decoding.
199  *
200  * Return: The number of pixels decoded 0 .. GDISP_IMAGE_GIF_BLIT_BUFFER_SIZE-1. 0 means EOF
201  *
202  * Note: The resulting pixels are stored in decode->buf
203  */
204 static gU16 getBytesGif(gImage *img) {
205  gdispImagePrivate_GIF * priv;
206  gifimgdecode * decode;
207  gU16 cnt;
208  gU16 code, prefix;
209  gU8 bdata;
210 
211  priv = (gdispImagePrivate_GIF *)img->priv;
212  decode = priv->decode;
213  cnt = 0;
214 
215  // At EOF
216  if (decode->code_last == decode->code_eof)
217  return 0;
218 
219  while(cnt < sizeof(decode->buf)) {
220  // Use the stack up first
221  if (decode->stackcnt > 0) {
222  decode->buf[cnt++] = decode->stack[--decode->stackcnt];
223  continue;
224  }
225 
226  // Get another code - a code is made up of decode->bitspercode bits.
227  while (decode->shiftbits < decode->bitspercode) {
228  // Get a byte - we may have to start a new data block
229  if ((!decode->blocksz && (gfileRead(img->f, &decode->blocksz, 1) != 1 || !decode->blocksz))
230  || gfileRead(img->f, &bdata, 1) != 1) {
231  // Pretend we got the EOF code - some encoders seem to just end the file
232  decode->code_last = decode->code_eof;
233  return cnt;
234  }
235  decode->blocksz--;
236 
237  decode->shiftdata |= ((unsigned long)bdata) << decode->shiftbits;
238  decode->shiftbits += 8;
239  }
240  code = decode->shiftdata & GifBitMask[decode->bitspercode];
241  decode->shiftdata >>= decode->bitspercode;
242  decode->shiftbits -= decode->bitspercode;
243  /**
244  * If code cannot fit into bitspercode bits we must raise its size.
245  * Note that codes above GIF_CODE_MAX are used for special signaling.
246  * If we're using GIF_MAX_CODE_BITS bits already and we're at the max code, just
247  * keep using the table as it is, don't increment decode->bitspercode.
248  */
249  if (decode->code_max < GIF_CODE_MAX + 2 && ++decode->code_max > decode->maxcodesz && decode->bitspercode < GIF_MAX_CODE_BITS) {
250  decode->maxcodesz <<= 1;
251  decode->bitspercode++;
252  }
253 
254  // EOF - the appropriate way to stop decoding
255  if (code == decode->code_eof) {
256  // Skip to the end of the data blocks
257  do {
258  gfileSetPos(img->f, gfileGetPos(img->f)+decode->blocksz);
259  } while (gfileRead(img->f, &decode->blocksz, 1) == 1 && decode->blocksz);
260 
261  // Mark the end
262  decode->code_last = decode->code_eof;
263  break;
264  }
265 
266  if (code == decode->code_clear) {
267  // Start again
268  for(prefix = 0; prefix <= GIF_CODE_MAX; prefix++)
269  decode->prefix[prefix] = GIF_CODE_NONE;
270  decode->code_max = decode->code_eof + 1;
271  decode->bitspercode = decode->bitsperpixel + 1;
272  decode->maxcodesz = 1 << decode->bitspercode;
273  decode->code_last = GIF_CODE_NONE;
274  continue;
275  }
276 
277  if (code < decode->code_clear) {
278  // Simple unencoded pixel - add it
279  decode->buf[cnt++] = code;
280 
281  } else {
282  /**
283  * Its a LZW code - trace the linked list until the prefix is a
284  * valid pixel while pushing the suffix pixels on the stack.
285  * If done, pop the stack in reverse order adding the pixels
286  */
287  if (decode->prefix[code] != GIF_CODE_NONE)
288  prefix = code;
289 
290  /**
291  * Only allowed if the code equals the partial code.
292  * In that case code = XXXCode, CrntCode or the
293  * prefix code is last code and the suffix char is
294  * exactly the prefix of last code!
295  */
296  else if (code == decode->code_max - 2 && decode->stackcnt < sizeof(decode->stack)) {
297  prefix = decode->code_last;
298  decode->suffix[decode->code_max - 2] = decode->stack[decode->stackcnt++] = getPrefixGif(decode, decode->code_last);
299  } else
300  return 0;
301 
302  /**
303  * If the image is OK we should not get a GIF_CODE_NONE while tracing.
304  * To prevent looping with a bad image we use StackPtr as loop counter
305  * and stop before overflowing Stack[].
306  */
307  while (decode->stackcnt < sizeof(decode->stack) && prefix > decode->code_clear && prefix <= GIF_CODE_MAX) {
308  decode->stack[decode->stackcnt++] = decode->suffix[prefix];
309  prefix = decode->prefix[prefix];
310  }
311  if (decode->stackcnt >= sizeof(decode->stack) || prefix > GIF_CODE_MAX)
312  return 0;
313 
314  /* Push the last character on stack: */
315  decode->stack[decode->stackcnt++] = prefix;
316  }
317 
318  if (decode->code_last != GIF_CODE_NONE && decode->prefix[decode->code_max - 2] == GIF_CODE_NONE) {
319  decode->prefix[decode->code_max - 2] = decode->code_last;
320 
321  /* Only allowed if code is exactly the running code:
322  * In that case code = XXXCode, CrntCode or the
323  * prefix code is last code and the suffix char is
324  * exactly the prefix of last code! */
325  decode->suffix[decode->code_max - 2] = getPrefixGif(decode, code == decode->code_max - 2 ? decode->code_last : code);
326  }
327  decode->code_last = code;
328  }
329  return cnt;
330 }
331 
332 /**
333  * Read the info on a frame.
334  *
335  * Pre: The file position is at the start of the frame.
336  */
337 static gdispImageError initFrameGif(gImage *img) {
338  gdispImagePrivate_GIF * priv;
339  gifimgcache * cache;
340  gU8 blocktype;
341  gU8 blocksz;
342 
343  priv = (gdispImagePrivate_GIF *)img->priv;
344 
345  // Save the dispose info from the existing frame
346  priv->dispose.flags = priv->frame.flags;
347  priv->dispose.paltrans = priv->frame.paltrans;
348  priv->dispose.x = priv->frame.x;
349  priv->dispose.y = priv->frame.y;
350  priv->dispose.width = priv->frame.width;
351  priv->dispose.height = priv->frame.height;
352 
353  // Check for a cached version of this image
354  for(cache=priv->cache; cache && cache->frame.posstart <= gfileGetPos(img->f); cache=cache->next) {
355  if (cache->frame.posstart == gfileGetPos(img->f)) {
356  priv->frame = cache->frame;
357  priv->curcache = cache;
358  return GDISP_IMAGE_ERR_OK;
359  }
360  }
361 
362  // Get ready for a new image
363  priv->curcache = 0;
364  priv->frame.posstart = gfileGetPos(img->f);
365  priv->frame.flags = 0;
366  priv->frame.delay = 0;
367  priv->frame.palsize = 0;
368 
369  // Process blocks until we reach the image descriptor
370  while(1) {
371  if (gfileRead(img->f, &blocktype, 1) != 1)
372  return GDISP_IMAGE_ERR_BADDATA;
373 
374  switch(blocktype) {
375  case 0x2C: //',' - IMAGE_DESC_RECORD_TYPE;
376  // Read the Image Descriptor
377  if (gfileRead(img->f, priv->buf, 9) != 9)
378  return GDISP_IMAGE_ERR_BADDATA;
379  priv->frame.x = gdispImageGetAlignedLE16(priv->buf, 0);
380  priv->frame.y = gdispImageGetAlignedLE16(priv->buf, 2);
381  priv->frame.width = gdispImageGetAlignedLE16(priv->buf, 4);
382  priv->frame.height = gdispImageGetAlignedLE16(priv->buf, 6);
383  if (((gU8 *)priv->buf)[8] & 0x80) // Local color table?
384  priv->frame.palsize = 2 << (((gU8 *)priv->buf)[8] & 0x07);
385  if (((gU8 *)priv->buf)[8] & 0x40) // Interlaced?
386  priv->frame.flags |= GIFL_INTERLACE;
387 
388  // We are ready to go for the actual palette read and image decode
389  priv->frame.pospal = gfileGetPos(img->f);
390  priv->frame.posimg = priv->frame.pospal+priv->frame.palsize*3;
391  priv->frame.posend = 0;
392 
393  // Mark this as an animated image if more than 1 frame.
394  if (priv->frame.posstart != priv->frame0pos)
395  img->flags |= GDISP_IMAGE_FLG_ANIMATED;
396  return GDISP_IMAGE_ERR_OK;
397 
398  case 0x21: //'!' - EXTENSION_RECORD_TYPE;
399  // Read the extension type
400  if (gfileRead(img->f, &blocktype, 1) != 1)
401  return GDISP_IMAGE_ERR_BADDATA;
402 
403  switch(blocktype) {
404  case 0xF9: // EXTENSION - Graphics Control Block
405  // Read the GCB
406  if (gfileRead(img->f, priv->buf, 6) != 6)
407  return GDISP_IMAGE_ERR_BADDATA;
408  // Check we have read a 4 byte data block and a data block terminator (0)
409  if (((gU8 *)priv->buf)[0] != 4 || ((gU8 *)priv->buf)[5] != 0)
410  return GDISP_IMAGE_ERR_BADDATA;
411  // Process the flags
412  switch(((gU8 *)priv->buf)[1] & 0x1C) {
413  case 0x00: case 0x04: break; // Dispose = do nothing
414  case 0x08: priv->frame.flags |= GIFL_DISPOSECLEAR; break; // Dispose = clear
415  case 0x0C: case 0x10: priv->frame.flags |= GIFL_DISPOSEREST; break; // Dispose = restore. Value 0x10 is a hack for bad encoders
416  default: return GDISP_IMAGE_ERR_UNSUPPORTED;
417  }
418  if (((gU8 *)priv->buf)[1] & 0x01) {
419  priv->frame.flags |= GIFL_TRANSPARENT;
420  img->flags |= GDISP_IMAGE_FLG_TRANSPARENT; // We set this but never clear it
421  }
422  if (((gU8 *)priv->buf)[1] & 0x02) // Wait for user input?
423  img->flags |= GDISP_IMAGE_FLG_MULTIPAGE;
424  else
425  img->flags &= ~GDISP_IMAGE_FLG_MULTIPAGE;
426  // Process frame delay and the transparent color (if any)
427  priv->frame.delay = gdispImageGetAlignedLE16(priv->buf, 2);
428  priv->frame.paltrans = ((gU8 *)priv->buf)[4];
429  break;
430 
431  case 0xFF: // EXTENSION - Application
432  // We only handle this for the special Netscape loop counter for animation
433  if (priv->flags & GIF_LOOP)
434  goto skipdatablocks;
435  // Read the Application header
436  if (gfileRead(img->f, priv->buf, 16) != 16)
437  return GDISP_IMAGE_ERR_BADDATA;
438  // Check we have read a 11 byte data block
439  if (((gU8 *)priv->buf)[0] != 11 && ((gU8 *)priv->buf)[12] != 3)
440  return GDISP_IMAGE_ERR_BADDATA;
441  // Check the vendor
442  if (((gU8 *)priv->buf)[1] == 'N' && ((gU8 *)priv->buf)[2] == 'E' && ((gU8 *)priv->buf)[3] == 'T'
443  && ((gU8 *)priv->buf)[4] == 'S' && ((gU8 *)priv->buf)[5] == 'C' && ((gU8 *)priv->buf)[6] == 'A'
444  && ((gU8 *)priv->buf)[7] == 'P' && ((gU8 *)priv->buf)[8] == 'E' && ((gU8 *)priv->buf)[9] == '2'
445  && ((gU8 *)priv->buf)[10] == '.' && ((gU8 *)priv->buf)[11] == '0') {
446  if (((gU8 *)priv->buf)[13] == 1) {
447  priv->loops = gdispImageGetAlignedLE16(priv->buf, 14);
448  priv->flags |= GIF_LOOP;
449  if (!priv->loops)
450  priv->flags |= GIF_LOOPFOREVER;
451  }
452  }
453  goto skipdatablocks;
454 
455  case 0x01: // EXTENSION - Plain Text (Graphics Rendering)
456  case 0xFE: // EXTENSION - Comment
457  default:
458  // 0x00-0x7F (0-127) are the Graphic Rendering blocks
459  if (blocktype <= 0x7F)
460  return GDISP_IMAGE_ERR_UNSUPPORTED;
461  // 0x80-0xF9 (128-249) are the Control blocks
462  // 0xFA-0xFF (250-255) are the Special Purpose blocks
463  // We don't understand this extension - just skip it by skipping data blocks
464  skipdatablocks:
465  while(1) {
466  if (gfileRead(img->f, &blocksz, 1) != 1)
467  return GDISP_IMAGE_ERR_BADDATA;
468  if (!blocksz)
469  break;
470  gfileSetPos(img->f, gfileGetPos(img->f) + blocksz);
471  }
472  break;
473  }
474  break;
475 
476  case 0x3B: //';' - TERMINATE_RECORD_TYPE;
477  // Are we an looping animation
478  if (!(priv->flags & GIF_LOOP))
479  return GDISP_IMAGE_GIF_EOF;
480  if (!(priv->flags & GIF_LOOPFOREVER)) {
481  if (!priv->loops)
482  return GDISP_IMAGE_GIF_EOF;
483  priv->loops--;
484  }
485 
486  // Seek back to frame0
487  gfileSetPos(img->f, priv->frame0pos);
488  return GDISP_IMAGE_GIF_LOOP;
489 
490  default: // UNDEFINED_RECORD_TYPE;
491  return GDISP_IMAGE_ERR_UNSUPPORTED;
492  }
493  }
494 }
495 
496 void gdispImageClose_GIF(gImage *img) {
497  gdispImagePrivate_GIF * priv;
498  gifimgcache * cache;
499  gifimgcache * ncache;
500 
501  priv = (gdispImagePrivate_GIF *)img->priv;
502  if (priv) {
503  // Free any stored frames
504  cache = priv->cache;
505  while(cache) {
506  ncache = cache->next;
507  gdispImageFree(img, (void *)cache, sizeof(gifimgcache)+cache->frame.width*cache->frame.height+cache->frame.palsize*sizeof(gColor));
508  cache = ncache;
509  }
510  if (priv->palette)
511  gdispImageFree(img, (void *)priv->palette, priv->palsize*sizeof(gColor));
512  gdispImageFree(img, (void *)priv, sizeof(gdispImagePrivate_GIF));
513  img->priv = 0;
514  }
515 }
516 
517 gdispImageError gdispImageOpen_GIF(gImage *img) {
518  gdispImagePrivate_GIF *priv;
519  gU8 hdr[6];
520  gU16 aword;
521 
522  /* Read the file identifier */
523  if (gfileRead(img->f, hdr, 6) != 6)
524  return GDISP_IMAGE_ERR_BADFORMAT; // It can't be us
525 
526  /* Process the GIFFILEHEADER structure */
527 
528  if (hdr[0] != 'G' || hdr[1] != 'I' || hdr[2] != 'F'
529  || hdr[3] != '8' || (hdr[4] != '7' && hdr[4] != '9') || hdr[5] != 'a')
530  return GDISP_IMAGE_ERR_BADFORMAT; // It can't be us
531 
532  /* We know we are a GIF format image */
533  img->flags = 0;
534 
535  /* Allocate our private area */
536  if (!(img->priv = gdispImageAlloc(img, sizeof(gdispImagePrivate_GIF))))
537  return GDISP_IMAGE_ERR_NOMEMORY;
538 
539  /* Initialise the essential bits in the private area */
540  priv = (gdispImagePrivate_GIF *)img->priv;
541  priv->flags = 0;
542  priv->palsize = 0;
543  priv->palette = 0;
544  priv->frame.flags = 0;
545  priv->cache = 0;
546  priv->curcache = 0;
547  priv->decode = 0;
548 
549  /* Process the Screen Descriptor structure */
550 
551  // Read the screen descriptor
552  if (gfileRead(img->f, priv->buf, 7) != 7)
553  goto baddatacleanup;
554  // Get the width
555  img->width = gdispImageGetAlignedLE16(priv->buf, 0);
556  // Get the height
557  img->height = gdispImageGetAlignedLE16(priv->buf, 2);
558  if (((gU8 *)priv->buf)[4] & 0x80) {
559  // Global color table
560  priv->palsize = 2 << (((gU8 *)priv->buf)[4] & 0x07);
561  // Allocate the global palette
562  if (!(priv->palette = (gColor *)gdispImageAlloc(img, priv->palsize*sizeof(gColor))))
563  goto nomemcleanup;
564  // Read the global palette
565  for(aword = 0; aword < priv->palsize; aword++) {
566  if (gfileRead(img->f, &priv->buf, 3) != 3)
567  goto baddatacleanup;
568  priv->palette[aword] = RGB2COLOR(((gU8 *)priv->buf)[0], ((gU8 *)priv->buf)[1], ((gU8 *)priv->buf)[2]);
569  }
570  }
571  priv->bgcolor = ((gU8 *)priv->buf)[5];
572 
573  // Save the fram0pos
574  priv->frame0pos = gfileGetPos(img->f);
575 
576  // Read the first frame descriptor
577  switch(initFrameGif(img)) {
578  case GDISP_IMAGE_ERR_OK: // Everything OK
579  img->type = GDISP_IMAGE_TYPE_GIF;
580  return GDISP_IMAGE_ERR_OK;
581  case GDISP_IMAGE_ERR_UNSUPPORTED: // Unsupported
582  gdispImageClose_GIF(img); // Clean up the private data area
583  return GDISP_IMAGE_ERR_UNSUPPORTED;
584  case GDISP_IMAGE_ERR_NOMEMORY: // Out of Memory
585  nomemcleanup:
586  gdispImageClose_GIF(img); // Clean up the private data area
587  return GDISP_IMAGE_ERR_NOMEMORY;
588  case GDISP_IMAGE_GIF_EOF: // We should have a frame but we don't seem to
589  case GDISP_IMAGE_GIF_LOOP: // We should have a frame but we don't seem to
590  case GDISP_IMAGE_ERR_BADDATA: // Oops - something wrong with the data
591  default:
592  baddatacleanup:
593  gdispImageClose_GIF(img); // Clean up the private data area
594  return GDISP_IMAGE_ERR_BADDATA;
595  }
596 }
597 
598 gdispImageError gdispImageCache_GIF(gImage *img) {
599  gdispImagePrivate_GIF * priv;
600  gifimgcache * cache;
601  gifimgdecode * decode;
602  gU8 * p;
603  gU8 * q;
604  gCoord mx, my;
605  gU16 cnt;
606 
607  /* If we are already cached - just return OK */
608  priv = (gdispImagePrivate_GIF *)img->priv;
609  if (priv->curcache)
610  return GDISP_IMAGE_ERR_OK;
611 
612  /* We need to allocate the frame, the palette and bits for the image */
613  if (!(cache = (gifimgcache *)gdispImageAlloc(img, sizeof(gifimgcache) + priv->frame.palsize*sizeof(gColor) + priv->frame.width*priv->frame.height)))
614  return GDISP_IMAGE_ERR_NOMEMORY;
615 
616  /* Initialise the cache */
617  decode = 0;
618  cache->frame = priv->frame;
619  cache->imagebits = (gU8 *)(cache+1) + cache->frame.palsize*sizeof(gColor);
620  cache->next = 0;
621 
622  /* Start the decode */
623  switch(startDecodeGif(img)) {
624  case GDISP_IMAGE_ERR_OK: break;
625  case GDISP_IMAGE_ERR_NOMEMORY: goto nomemcleanup;
626  case GDISP_IMAGE_ERR_BADDATA:
627  default: goto baddatacleanup;
628  }
629  decode = priv->decode;
630 
631  // Save the palette
632  if (cache->frame.palsize) {
633  cache->palette = (gColor *)(cache+1);
634 
635  /* Copy the local palette into the cache */
636  for(cnt = 0; cnt < cache->frame.palsize; cnt++)
637  cache->palette[cnt] = decode->palette[cnt];
638  } else
639  cache->palette = priv->palette;
640 
641  // Check for interlacing
642  cnt = 0;
643  q = 0;
644  if (cache->frame.flags & GIFL_INTERLACE) {
645  // Every 8th row starting at row 0
646  for(p=cache->imagebits, my=0; my < cache->frame.height; my+=8, p += cache->frame.width*7) {
647  for(mx=0; mx < cache->frame.width; mx++) {
648  if (!cnt) {
649  if (!(cnt = getBytesGif(img))) {
650  // Sometimes the image EOF is a bit early - treat the rest as transparent
651  if (decode->code_last != decode->code_eof)
652  goto baddatacleanup;
653  while(cnt < sizeof(decode->buf))
654  decode->buf[cnt++] = (cache->frame.flags & GIFL_TRANSPARENT) ? cache->frame.paltrans : 0;
655  }
656  q = decode->buf;
657  }
658  *p++ = *q++;
659  cnt--;
660  }
661  }
662  // Every 8th row starting at row 4
663  for(p=cache->imagebits+cache->frame.width*4, my=4; my < cache->frame.height; my+=8, p += cache->frame.width*7) {
664  for(mx=0; mx < cache->frame.width; mx++) {
665  if (!cnt) {
666  if (!(cnt = getBytesGif(img))) {
667  // Sometimes the image EOF is a bit early - treat the rest as transparent
668  if (decode->code_last != decode->code_eof)
669  goto baddatacleanup;
670  while(cnt < sizeof(decode->buf))
671  decode->buf[cnt++] = (cache->frame.flags & GIFL_TRANSPARENT) ? cache->frame.paltrans : 0;
672  }
673  q = decode->buf;
674  }
675  *p++ = *q++;
676  cnt--;
677  }
678  }
679  // Every 4th row starting at row 2
680  for(p=cache->imagebits+cache->frame.width*2, my=2; my < cache->frame.height; my+=4, p += cache->frame.width*3) {
681  for(mx=0; mx < cache->frame.width; mx++) {
682  if (!cnt) {
683  if (!(cnt = getBytesGif(img))) {
684  // Sometimes the image EOF is a bit early - treat the rest as transparent
685  if (decode->code_last != decode->code_eof)
686  goto baddatacleanup;
687  while(cnt < sizeof(decode->buf))
688  decode->buf[cnt++] = (cache->frame.flags & GIFL_TRANSPARENT) ? cache->frame.paltrans : 0;
689  }
690  q = decode->buf;
691  }
692  *p++ = *q++;
693  cnt--;
694  }
695  }
696  // Every 2nd row starting at row 1
697  for(p=cache->imagebits+cache->frame.width, my=1; my < cache->frame.height; my+=2, p += cache->frame.width) {
698  for(mx=0; mx < cache->frame.width; mx++) {
699  if (!cnt) {
700  if (!(cnt = getBytesGif(img))) {
701  // Sometimes the image EOF is a bit early - treat the rest as transparent
702  if (decode->code_last != decode->code_eof)
703  goto baddatacleanup;
704  while(cnt < sizeof(decode->buf))
705  decode->buf[cnt++] = (cache->frame.flags & GIFL_TRANSPARENT) ? cache->frame.paltrans : 0;
706  }
707  q = decode->buf;
708  }
709  *p++ = *q++;
710  cnt--;
711  }
712  }
713  } else {
714  // Every row in sequence
715  p=cache->imagebits;
716  for(my=0; my < cache->frame.height; my++) {
717  for(mx=0; mx < cache->frame.width; mx++) {
718  if (!cnt) {
719  if (!(cnt = getBytesGif(img))) {
720  // Sometimes the image EOF is a bit early - treat the rest as transparent
721  if (decode->code_last != decode->code_eof)
722  goto baddatacleanup;
723  while(cnt < sizeof(decode->buf))
724  decode->buf[cnt++] = (cache->frame.flags & GIFL_TRANSPARENT) ? cache->frame.paltrans : 0;
725  }
726  q = decode->buf;
727  }
728  *p++ = *q++;
729  cnt--;
730  }
731  }
732  }
733  // We could be pedantic here but extra bytes won't hurt us
734  while(getBytesGif(img));
735  priv->frame.posend = cache->frame.posend = gfileGetPos(img->f);
736 
737  // Save everything
738  priv->curcache = cache;
739  if (!priv->cache)
740  priv->cache = cache;
741  else if (priv->cache->frame.posstart > cache->frame.posstart) {
742  cache->next = priv->cache;
743  priv->cache = cache;
744  } else {
745  gifimgcache *pc;
746 
747  for(pc = priv->cache; pc; pc = pc->next) {
748  if (!pc->next || pc->next->frame.posstart > cache->frame.posstart) {
749  cache->next = pc->next;
750  pc->next = cache;
751  break;
752  }
753  }
754  }
755  stopDecodeGif(img);
756  return GDISP_IMAGE_ERR_OK;
757 
758 nomemcleanup:
759  stopDecodeGif(img);
760  gdispImageFree(img, cache, sizeof(gifimgcache) + priv->frame.palsize*sizeof(gColor) + priv->frame.width*priv->frame.height);
761  return GDISP_IMAGE_ERR_NOMEMORY;
762 
763 baddatacleanup:
764  stopDecodeGif(img);
765  gdispImageFree(img, cache, sizeof(gifimgcache) + priv->frame.palsize*sizeof(gColor) + priv->frame.width*priv->frame.height);
766  return GDISP_IMAGE_ERR_BADDATA;
767 }
768 
769 gdispImageError gdispGImageDraw_GIF(GDisplay *g, gImage *img, gCoord x, gCoord y, gCoord cx, gCoord cy, gCoord sx, gCoord sy) {
770  gdispImagePrivate_GIF * priv;
771  gifimgdecode * decode;
772  gU8 * q = 0;
773  gCoord mx, my, fx, fy;
774  gU16 cnt, gcnt;
775  gU8 col;
776 
777  priv = (gdispImagePrivate_GIF *)img->priv;
778 
779  /* Handle previous frame disposing */
780  if (priv->dispose.flags & (GIFL_DISPOSECLEAR|GIFL_DISPOSEREST)) {
781  // Clip to the disposal area - clip area = mx,my -> fx, fy (sx,sy,cx,cy are unchanged)
782  mx = priv->dispose.x;
783  my = priv->dispose.y;
784  fx = priv->dispose.x+priv->dispose.width;
785  fy = priv->dispose.y+priv->dispose.height;
786  if (sx > mx) mx = sx;
787  if (sy > my) my = sy;
788  if (sx+cx <= fx) fx = sx+cx;
789  if (sy+cy <= fy) fy = sy+cy;
790  if (fx > mx && fy > my) {
791  // We only support clearing (not restoring). The specification says that we are allowed to do this.
792  // Calculate the bgcolor
793  // The spec says to restore the backgound color (priv->bgcolor) but in practice if there is transparency
794  // image decoders tend to assume that a restore to the transparent color is required instead
795  if (((priv->dispose.flags & GIFL_TRANSPARENT) /*&& priv->dispose.paltrans == priv->bgcolor*/) || priv->bgcolor >= priv->palsize)
796  gdispGFillArea(g, x+mx-sx, y+my-sy, fx-mx, fy-my, img->bgcolor);
797  else
798  gdispGFillArea(g, x+mx-sx, y+my-sy, fx-mx, fy-my, priv->palette[priv->bgcolor]);
799  }
800  }
801 
802  /* Clip to just this frame - clip area = sx,sy -> fx, fy */
803  fx = priv->frame.x+priv->frame.width;
804  fy = priv->frame.y+priv->frame.height;
805  if (sx >= fx || sy >= fy || sx+cx < priv->frame.x || sy+cy < priv->frame.y) return GDISP_IMAGE_ERR_OK;
806  if (sx < priv->frame.x) { mx = priv->frame.x - sx; x += mx; cx -= mx; sx = priv->frame.x; }
807  if (sy < priv->frame.y) { my = priv->frame.y - sy; y += my; cy -= my; sy = priv->frame.y; }
808  if (sx+cx > fx) cx = fx-sx;
809  if (sy+cy > fy) cy = fy-sy;
810 
811  // Make sx, sy relative to this frame so we are not adding priv->frame.x & priv->frame.y each time
812  sx -= priv->frame.x; sy -= priv->frame.y;
813  fx = sx + cx;
814  fy = sy + cy;
815 
816  /* Draw from the image cache - if it exists */
817  if (priv->curcache) {
818  gifimgcache * cache;
819 
820  cache = priv->curcache;
821  q = cache->imagebits+priv->frame.width*sy+sx;
822 
823  for(my=sy; my < fy; my++, q += priv->frame.width - cx) {
824  for(gcnt=0, mx=sx, cnt=0; mx < fx; mx++) {
825  col = *q++;
826  if ((priv->frame.flags & GIFL_TRANSPARENT) && col == priv->frame.paltrans) {
827  // We have a transparent pixel - dump the buffer to the display
828  switch(gcnt) {
829  case 0: break;
830  case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break;
831  default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break;
832  }
833  continue;
834  }
835  priv->buf[gcnt++] = cache->palette[col];
836  if (gcnt >= GDISP_IMAGE_GIF_BLIT_BUFFER_SIZE) {
837  // We have run out of buffer - dump it to the display
838  gdispGBlitArea(g, x+mx-sx-gcnt+1, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf);
839  gcnt = 0;
840  }
841  }
842  // We have finished the line - dump the buffer to the display
843  switch(gcnt) {
844  case 0: break;
845  case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); break;
846  default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); break;
847  }
848  }
849 
850  return GDISP_IMAGE_ERR_OK;
851  }
852 
853  /* Start the decode */
854  switch(startDecodeGif(img)) {
855  case GDISP_IMAGE_ERR_OK: break;
856  case GDISP_IMAGE_ERR_NOMEMORY: return GDISP_IMAGE_ERR_NOMEMORY;
857  case GDISP_IMAGE_ERR_BADDATA:
858  default: return GDISP_IMAGE_ERR_BADDATA;
859  }
860  decode = priv->decode;
861 
862  // Check for interlacing
863  cnt = 0;
864  if (priv->frame.flags & GIFL_INTERLACE) {
865  // Every 8th row starting at row 0
866  for(my=0; my < priv->frame.height; my+=8) {
867  for(gcnt=0, mx=0; mx < priv->frame.width; mx++, q++, cnt--) {
868  if (!cnt) {
869  if (!(cnt = getBytesGif(img))) {
870  // Sometimes the image EOF is a bit early - treat the rest as transparent
871  if (decode->code_last != decode->code_eof)
872  goto baddatacleanup;
873  mx++;
874  break;
875  }
876  q = decode->buf;
877  }
878  if (my >= sy && my < fy && mx >= sx && mx < fx) {
879  col = *q;
880  if ((priv->frame.flags & GIFL_TRANSPARENT) && col == priv->frame.paltrans) {
881  // We have a transparent pixel - dump the buffer to the display
882  switch(gcnt) {
883  case 0: break;
884  case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break;
885  default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break;
886  }
887  continue;
888  }
889  priv->buf[gcnt++] = decode->palette[col];
890  if (gcnt >= GDISP_IMAGE_GIF_BLIT_BUFFER_SIZE) {
891  // We have run out of buffer - dump it to the display
892  gdispGBlitArea(g, x+mx-sx-gcnt+1, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf);
893  gcnt = 0;
894  }
895  continue;
896  }
897  // We have finished the visible area - dump the buffer to the display
898  switch(gcnt) {
899  case 0: break;
900  case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break;
901  default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break;
902  }
903  }
904  // We have finished the line - dump the buffer to the display
905  switch(gcnt) {
906  case 0: break;
907  case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); break;
908  default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); break;
909  }
910  }
911  // Every 8th row starting at row 4
912  for(my=4; my < priv->frame.height; my+=8) {
913  for(gcnt=0, mx=0; mx < priv->frame.width; mx++, q++, cnt--) {
914  if (!cnt) {
915  if (!(cnt = getBytesGif(img))) {
916  // Sometimes the image EOF is a bit early - treat the rest as transparent
917  if (decode->code_last != decode->code_eof)
918  goto baddatacleanup;
919  mx++;
920  break;
921  }
922  q = decode->buf;
923  }
924  if (my >= sy && my < fy && mx >= sx && mx < fx) {
925  col = *q;
926  if ((priv->frame.flags & GIFL_TRANSPARENT) && col == priv->frame.paltrans) {
927  // We have a transparent pixel - dump the buffer to the display
928  switch(gcnt) {
929  case 0: break;
930  case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break;
931  default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break;
932  }
933  continue;
934  }
935  priv->buf[gcnt++] = decode->palette[col];
936  if (gcnt >= GDISP_IMAGE_GIF_BLIT_BUFFER_SIZE) {
937  // We have run out of buffer - dump it to the display
938  gdispGBlitArea(g, x+mx-sx-gcnt+1, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf);
939  gcnt = 0;
940  }
941  continue;
942  }
943  // We have finished the visible area - dump the buffer to the display
944  switch(gcnt) {
945  case 0: break;
946  case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break;
947  default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break;
948  }
949  }
950  // We have finished the line - dump the buffer to the display
951  switch(gcnt) {
952  case 0: break;
953  case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); break;
954  default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); break;
955  }
956  }
957  // Every 4th row starting at row 2
958  for(my=2; my < priv->frame.height; my+=4) {
959  for(gcnt=0, mx=0; mx < priv->frame.width; mx++, q++, cnt--) {
960  if (!cnt) {
961  if (!(cnt = getBytesGif(img))) {
962  // Sometimes the image EOF is a bit early - treat the rest as transparent
963  if (decode->code_last != decode->code_eof)
964  goto baddatacleanup;
965  mx++;
966  break;
967  }
968  q = decode->buf;
969  }
970  if (my >= sy && my < fy && mx >= sx && mx < fx) {
971  col = *q;
972  if ((priv->frame.flags & GIFL_TRANSPARENT) && col == priv->frame.paltrans) {
973  // We have a transparent pixel - dump the buffer to the display
974  switch(gcnt) {
975  case 0: break;
976  case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break;
977  default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break;
978  }
979  continue;
980  }
981  priv->buf[gcnt++] = decode->palette[col];
982  if (gcnt >= GDISP_IMAGE_GIF_BLIT_BUFFER_SIZE) {
983  // We have run out of buffer - dump it to the display
984  gdispGBlitArea(g, x+mx-sx-gcnt+1, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf);
985  gcnt = 0;
986  }
987  continue;
988  }
989  // We have finished the visible area - dump the buffer to the display
990  switch(gcnt) {
991  case 0: break;
992  case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break;
993  default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break;
994  }
995  }
996  // We have finished the line - dump the buffer to the display
997  switch(gcnt) {
998  case 0: break;
999  case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); break;
1000  default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); break;
1001  }
1002  }
1003  // Every 2nd row starting at row 1
1004  for(my=1; my < priv->frame.height; my+=2) {
1005  for(gcnt=0, mx=0; mx < priv->frame.width; mx++, q++, cnt--) {
1006  if (!cnt) {
1007  if (!(cnt = getBytesGif(img))) {
1008  // Sometimes the image EOF is a bit early - treat the rest as transparent
1009  if (decode->code_last != decode->code_eof)
1010  goto baddatacleanup;
1011  mx++;
1012  break;
1013  }
1014  q = decode->buf;
1015  }
1016  if (my >= sy && my < fy && mx >= sx && mx < fx) {
1017  col = *q;
1018  if ((priv->frame.flags & GIFL_TRANSPARENT) && col == priv->frame.paltrans) {
1019  // We have a transparent pixel - dump the buffer to the display
1020  switch(gcnt) {
1021  case 0: break;
1022  case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break;
1023  default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break;
1024  }
1025  continue;
1026  }
1027  priv->buf[gcnt++] = decode->palette[col];
1028  if (gcnt >= GDISP_IMAGE_GIF_BLIT_BUFFER_SIZE) {
1029  // We have run out of buffer - dump it to the display
1030  gdispGBlitArea(g, x+mx-sx-gcnt+1, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf);
1031  gcnt = 0;
1032  }
1033  continue;
1034  }
1035  // We have finished the visible area - dump the buffer to the display
1036  switch(gcnt) {
1037  case 0: break;
1038  case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break;
1039  default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break;
1040  }
1041  }
1042  // We have finished the line - dump the buffer to the display
1043  switch(gcnt) {
1044  case 0: break;
1045  case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); break;
1046  default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); break;
1047  }
1048  }
1049  } else {
1050  // Every row in sequence
1051  for(my=0; my < priv->frame.height; my++) {
1052  for(gcnt=0, mx=0; mx < priv->frame.width; mx++, q++, cnt--) {
1053  if (!cnt) {
1054  if (!(cnt = getBytesGif(img))) {
1055  // Sometimes the image EOF is a bit early - treat the rest as transparent
1056  if (decode->code_last != decode->code_eof)
1057  goto baddatacleanup;
1058  mx++;
1059  break;
1060  }
1061  q = decode->buf;
1062  }
1063  if (my >= sy && my < fy && mx >= sx && mx < fx) {
1064  col = *q;
1065  if ((priv->frame.flags & GIFL_TRANSPARENT) && col == priv->frame.paltrans) {
1066  // We have a transparent pixel - dump the buffer to the display
1067  switch(gcnt) {
1068  case 0: break;
1069  case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break;
1070  default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break;
1071  }
1072  continue;
1073  }
1074  priv->buf[gcnt++] = decode->palette[col];
1075  if (gcnt >= GDISP_IMAGE_GIF_BLIT_BUFFER_SIZE) {
1076  // We have run out of buffer - dump it to the display
1077  gdispGBlitArea(g, x+mx-sx-gcnt+1, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf);
1078  gcnt = 0;
1079  }
1080  continue;
1081  }
1082  // We have finished the visible area - dump the buffer to the display
1083  switch(gcnt) {
1084  case 0: break;
1085  case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break;
1086  default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break;
1087  }
1088  }
1089  // We have finished the line - dump the buffer to the display
1090  switch(gcnt) {
1091  case 0: break;
1092  case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); break;
1093  default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); break;
1094  }
1095  }
1096  }
1097  // We could be pedantic here but extra bytes won't hurt us
1098  while (getBytesGif(img));
1099  priv->frame.posend = gfileGetPos(img->f);
1100 
1101  stopDecodeGif(img);
1102  return GDISP_IMAGE_ERR_OK;
1103 
1104 baddatacleanup:
1105  stopDecodeGif(img);
1106  return GDISP_IMAGE_ERR_BADDATA;
1107 }
1108 
1109 gDelay gdispImageNext_GIF(gImage *img) {
1110  gdispImagePrivate_GIF * priv;
1111  gDelay delay;
1112  gU8 blocksz;
1113 
1114  priv = (gdispImagePrivate_GIF *)img->priv;
1115 
1116  // Save the delay and convert to millisecs
1117  delay = (gDelay)priv->frame.delay * 10;
1118 
1119  // We need to get to the end of this frame
1120  if (!priv->frame.posend) {
1121  // We don't know where the end of the frame is yet - find it!
1122  gfileSetPos(img->f, priv->frame.posimg+1); // Skip the code size byte too
1123  while(1) {
1124  if (gfileRead(img->f, &blocksz, 1) != 1)
1125  return gDelayForever;
1126  if (!blocksz)
1127  break;
1128  gfileSetPos(img->f, gfileGetPos(img->f) + blocksz);
1129  }
1130  priv->frame.posend = gfileGetPos(img->f);
1131  }
1132 
1133  // Seek to the end of this frame
1134  gfileSetPos(img->f, priv->frame.posend);
1135 
1136  // Read the next frame descriptor
1137  for(blocksz=0; blocksz < 2; blocksz++) { // 2 loops max to prevent cycling forever with a bad file
1138  switch(initFrameGif(img)) {
1139  case GDISP_IMAGE_ERR_OK: // Everything OK
1140  return delay;
1141  case GDISP_IMAGE_GIF_LOOP: // Back to the beginning
1142  break;
1143  case GDISP_IMAGE_GIF_EOF: // The real End-Of-File
1144  case GDISP_IMAGE_ERR_BADDATA: // Oops - something wrong with the data
1145  case GDISP_IMAGE_ERR_NOMEMORY: // Out of Memory
1146  case GDISP_IMAGE_ERR_UNSUPPORTED: // Unsupported
1147  default:
1148  return gDelayForever;
1149  }
1150  }
1151  return gDelayForever;
1152 }
1153 
1154 #endif /* GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_GIF */
GDISP image support routines header file.
#define RGB2COLOR(r, g, b)
Convert red, green, blue (each 0 to 255) into a color value.
Definition: gdisp_colors.h:196
COLOR_TYPE gColor
The color type definition.
Definition: gdisp_colors.h:437
#define GDISP_IMAGE_GIF_BLIT_BUFFER_SIZE
The GIF blit buffer size.
void gdispGDrawPixel(GDisplay *g, gCoord x, gCoord y, gColor color)
Set a pixel in the specified color.
void gdispGBlitArea(GDisplay *g, gCoord x, gCoord y, gCoord cx, gCoord cy, gCoord srcx, gCoord srcy, gCoord srccx, const gPixel *buffer)
Fill an area using the supplied bitmap.
gColor gPixel
The pixel format.
Definition: gdisp.h:226
void gdispGFillArea(GDisplay *g, gCoord x, gCoord y, gCoord cx, gCoord cy, gColor color)
Fill an area with a color.
gI16 gCoord
The type for a coordinate or length on the screen.
Definition: gdisp.h:39
gBool gfileSetPos(GFILE *f, gFileSize pos)
Set the position of the read/write cursor.
gMemSize gfileRead(GFILE *f, void *buf, gMemSize len)
Read from file.
gFileSize gfileGetPos(GFILE *f)
Get the current position of the read/write cursor.
gU16 gdispImageError
An image error code.
Definition: gdisp_image.h:37
The structure for an image.
Definition: gdisp_image.h:59