summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcin Bukat <marcin.bukat@gmail.com>2012-11-02 13:03:58 +0100
committerMarcin Bukat <marcin.bukat@gmail.com>2012-11-13 18:13:10 +0100
commit0ceaff2b65c50b75ad8cc5b2d12e7b3f864092e5 (patch)
tree49d8d297cbba93902bc612a9aa4ded1b6e2d46e5
parentb35f82c91ff050b4405b19a3e56e9d031bf940e2 (diff)
downloadrockbox-0ceaff2b65c50b75ad8cc5b2d12e7b3f864092e5.tar.gz
rockbox-0ceaff2b65c50b75ad8cc5b2d12e7b3f864092e5.zip
imageviewer: gif viewer based on giflib-5.0.2
This adds ability to view gif images in rockbox. Works both on color and gray/monochrome targets (greylib). Aspect correction is supported as well. Limitations: - animated gifs are restricted to 32 frames - animated gifs loop always (loopcount is ignored) - plain text extension is not supported - animated gifs with interframe delay = 0 are treated as still images (web browsers usually treat delay 0 as 100ms to prevent exhaustive CPU load by such images) Change-Id: I61501f801ddcd403410e38d83e6bddc9883e7ede
-rw-r--r--apps/plugins/CATEGORIES1
-rw-r--r--apps/plugins/imageviewer/SUBDIRS1
-rw-r--r--apps/plugins/imageviewer/bmp/bmp.c3
-rw-r--r--apps/plugins/imageviewer/gif/SOURCES6
-rw-r--r--apps/plugins/imageviewer/gif/dgif_lib.c1169
-rw-r--r--apps/plugins/imageviewer/gif/gif.c241
-rw-r--r--apps/plugins/imageviewer/gif/gif.make30
-rw-r--r--apps/plugins/imageviewer/gif/gif_decoder.c491
-rw-r--r--apps/plugins/imageviewer/gif/gif_decoder.h37
-rw-r--r--apps/plugins/imageviewer/gif/gif_err.c99
-rw-r--r--apps/plugins/imageviewer/gif/gif_hash.h39
-rw-r--r--apps/plugins/imageviewer/gif/gif_lib.h308
-rw-r--r--apps/plugins/imageviewer/gif/gif_lib_private.h59
-rw-r--r--apps/plugins/imageviewer/gif/gifalloc.c405
-rw-r--r--apps/plugins/imageviewer/gif/rb_glue.h44
-rw-r--r--apps/plugins/imageviewer/image_decoder.c6
-rw-r--r--apps/plugins/imageviewer/image_decoder.h1
-rw-r--r--apps/plugins/imageviewer/imageviewer.c64
-rw-r--r--apps/plugins/imageviewer/imageviewer.h7
-rw-r--r--apps/plugins/imageviewer/jpeg/jpeg.c3
-rw-r--r--apps/plugins/imageviewer/png/png.c3
-rw-r--r--apps/plugins/imageviewer/ppm/ppm.c3
-rw-r--r--apps/plugins/viewers.config1
23 files changed, 3006 insertions, 15 deletions
diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIES
index c7f194fa94..54a316e84b 100644
--- a/apps/plugins/CATEGORIES
+++ b/apps/plugins/CATEGORIES
@@ -70,6 +70,7 @@ pictureflow,demos
70pitch_detector,apps 70pitch_detector,apps
71plasma,demos 71plasma,demos
72png,viewers 72png,viewers
73gif,viewers
73pong,games 74pong,games
74ppm,viewers 75ppm,viewers
75properties,viewers 76properties,viewers
diff --git a/apps/plugins/imageviewer/SUBDIRS b/apps/plugins/imageviewer/SUBDIRS
index 0f8d953939..1f7b4d8b52 100644
--- a/apps/plugins/imageviewer/SUBDIRS
+++ b/apps/plugins/imageviewer/SUBDIRS
@@ -4,3 +4,4 @@ png
4#ifdef HAVE_LCD_COLOR 4#ifdef HAVE_LCD_COLOR
5ppm 5ppm
6#endif 6#endif
7gif
diff --git a/apps/plugins/imageviewer/bmp/bmp.c b/apps/plugins/imageviewer/bmp/bmp.c
index b7efbb7e2c..f7e55dbe62 100644
--- a/apps/plugins/imageviewer/bmp/bmp.c
+++ b/apps/plugins/imageviewer/bmp/bmp.c
@@ -230,8 +230,9 @@ static int load_image(char *filename, struct image_info *info,
230 return PLUGIN_OK; 230 return PLUGIN_OK;
231} 231}
232 232
233static int get_image(struct image_info *info, int ds) 233static int get_image(struct image_info *info, int frame, int ds)
234{ 234{
235 (void)frame;
235 struct t_disp* p_disp = &disp[ds]; /* short cut */ 236 struct t_disp* p_disp = &disp[ds]; /* short cut */
236 237
237 info->width = bmp.width/ds; 238 info->width = bmp.width/ds;
diff --git a/apps/plugins/imageviewer/gif/SOURCES b/apps/plugins/imageviewer/gif/SOURCES
new file mode 100644
index 0000000000..032b65c73d
--- /dev/null
+++ b/apps/plugins/imageviewer/gif/SOURCES
@@ -0,0 +1,6 @@
1../../../../lib/tlsf/src/tlsf.c
2dgif_lib.c
3gifalloc.c
4gif_decoder.c
5gif.c
6gif_err.c
diff --git a/apps/plugins/imageviewer/gif/dgif_lib.c b/apps/plugins/imageviewer/gif/dgif_lib.c
new file mode 100644
index 0000000000..6dc52991a5
--- /dev/null
+++ b/apps/plugins/imageviewer/gif/dgif_lib.c
@@ -0,0 +1,1169 @@
1/******************************************************************************
2
3dgif_lib.c - GIF decoding
4
5The functions here and in egif_lib.c are partitioned carefully so that
6if you only require one of read and write capability, only one of these
7two modules will be linked. Preserve this property!
8
9*****************************************************************************/
10
11#include <stdlib.h>
12#include <limits.h>
13#include <stdint.h>
14#include <fcntl.h>
15/* #include <unistd.h> */
16/* #include <stdio.h> */
17#include <string.h>
18
19#ifdef _WIN32
20#include <io.h>
21#endif /* _WIN32 */
22
23#include "gif_lib.h"
24#include "gif_lib_private.h"
25
26/* compose unsigned little endian value */
27#define UNSIGNED_LITTLE_ENDIAN(lo, hi) ((lo) | ((hi) << 8))
28
29/* avoid extra function call in case we use fread (TVT) */
30#define READ(_gif,_buf,_len) \
31 (((GifFilePrivateType*)_gif->Private)->Read ? \
32 ((GifFilePrivateType*)_gif->Private)->Read(_gif,_buf,_len) : \
33 fread(_buf,1,_len,((GifFilePrivateType*)_gif->Private)->File))
34
35static int DGifGetWord(GifFileType *GifFile, GifWord *Word);
36static int DGifSetupDecompress(GifFileType *GifFile);
37static int DGifDecompressLine(GifFileType *GifFile, GifPixelType *Line,
38 int LineLen);
39static int DGifGetPrefixChar(GifPrefixType *Prefix, int Code, int ClearCode);
40static int DGifDecompressInput(GifFileType *GifFile, int *Code);
41static int DGifBufferedInput(GifFileType *GifFile, GifByteType *Buf,
42 GifByteType *NextByte);
43
44/******************************************************************************
45 Open a new GIF file for read, given by its name.
46 Returns dynamically allocated GifFileType pointer which serves as the GIF
47 info record.
48******************************************************************************/
49GifFileType *
50DGifOpenFileName(const char *FileName, int *Error)
51{
52 int FileHandle;
53 GifFileType *GifFile;
54
55 if ((FileHandle = rb->open(FileName, O_RDONLY)) == -1) {
56 if (Error != NULL)
57 *Error = D_GIF_ERR_OPEN_FAILED;
58 return NULL;
59 }
60
61 GifFile = DGifOpenFileHandle(FileHandle, Error);
62 // cppcheck-suppress resourceLeak
63 return GifFile;
64}
65
66/******************************************************************************
67 Update a new GIF file, given its file handle.
68 Returns dynamically allocated GifFileType pointer which serves as the GIF
69 info record.
70******************************************************************************/
71GifFileType *
72DGifOpenFileHandle(int FileHandle, int *Error)
73{
74 char Buf[GIF_STAMP_LEN + 1];
75 GifFileType *GifFile;
76 GifFilePrivateType *Private;
77 int f;
78
79 GifFile = (GifFileType *)malloc(sizeof(GifFileType));
80 if (GifFile == NULL) {
81 if (Error != NULL)
82 *Error = D_GIF_ERR_NOT_ENOUGH_MEM;
83 (void)rb->close(FileHandle);
84 return NULL;
85 }
86
87 /*@i1@*/memset(GifFile, '\0', sizeof(GifFileType));
88
89 /* Belt and suspenders, in case the null pointer isn't zero */
90 GifFile->SavedImages = NULL;
91 GifFile->SColorMap = NULL;
92
93 Private = (GifFilePrivateType *)malloc(sizeof(GifFilePrivateType));
94 if (Private == NULL) {
95 if (Error != NULL)
96 *Error = D_GIF_ERR_NOT_ENOUGH_MEM;
97 (void)rb->close(FileHandle);
98 free((char *)GifFile);
99 return NULL;
100 }
101#ifdef _WIN32
102 _setmode(FileHandle, O_BINARY); /* Make sure it is in binary mode. */
103#endif /* _WIN32 */
104
105 f = fdopen(FileHandle, "rb"); /* Make it into a stream: */
106
107 /*@-mustfreeonly@*/
108 GifFile->Private = (void *)Private;
109 Private->FileHandle = FileHandle;
110 Private->File = f;
111 Private->FileState = FILE_STATE_READ;
112 Private->Read = NULL; /* don't use alternate input method (TVT) */
113 GifFile->UserData = NULL; /* TVT */
114 /*@=mustfreeonly@*/
115
116 /* Let's see if this is a GIF file: */
117 if (READ(GifFile, (unsigned char *)Buf, GIF_STAMP_LEN) != GIF_STAMP_LEN) {
118 if (Error != NULL)
119 *Error = D_GIF_ERR_READ_FAILED;
120 (void)fclose(f);
121 free((char *)Private);
122 free((char *)GifFile);
123 return NULL;
124 }
125
126 /* Check for GIF prefix at start of file */
127 Buf[GIF_STAMP_LEN] = 0;
128 if (strncmp(GIF_STAMP, Buf, GIF_VERSION_POS) != 0) {
129 if (Error != NULL)
130 *Error = D_GIF_ERR_NOT_GIF_FILE;
131 (void)fclose(f);
132 free((char *)Private);
133 free((char *)GifFile);
134 return NULL;
135 }
136
137 if (DGifGetScreenDesc(GifFile) == GIF_ERROR) {
138 (void)fclose(f);
139 free((char *)Private);
140 free((char *)GifFile);
141 return NULL;
142 }
143
144 GifFile->Error = 0;
145
146 /* What version of GIF? */
147 Private->gif89 = (Buf[GIF_VERSION_POS] == '9');
148
149 return GifFile;
150}
151
152/******************************************************************************
153 GifFileType constructor with user supplied input function (TVT)
154******************************************************************************/
155GifFileType *
156DGifOpen(void *userData, InputFunc readFunc, int *Error)
157{
158 char Buf[GIF_STAMP_LEN + 1];
159 GifFileType *GifFile;
160 GifFilePrivateType *Private;
161
162 GifFile = (GifFileType *)malloc(sizeof(GifFileType));
163 if (GifFile == NULL) {
164 if (Error != NULL)
165 *Error = D_GIF_ERR_NOT_ENOUGH_MEM;
166 return NULL;
167 }
168
169 memset(GifFile, '\0', sizeof(GifFileType));
170
171 /* Belt and suspenders, in case the null pointer isn't zero */
172 GifFile->SavedImages = NULL;
173 GifFile->SColorMap = NULL;
174
175 Private = (GifFilePrivateType *)malloc(sizeof(GifFilePrivateType));
176 if (!Private) {
177 if (Error != NULL)
178 *Error = D_GIF_ERR_NOT_ENOUGH_MEM;
179 free((char *)GifFile);
180 return NULL;
181 }
182
183 GifFile->Private = (void *)Private;
184 Private->FileHandle = 0;
185 Private->File = 0; /* NULL */
186 Private->FileState = FILE_STATE_READ;
187
188 Private->Read = readFunc; /* TVT */
189 GifFile->UserData = userData; /* TVT */
190
191 /* Lets see if this is a GIF file: */
192 if (READ(GifFile, (unsigned char *)Buf, GIF_STAMP_LEN) != GIF_STAMP_LEN) {
193 if (Error != NULL)
194 *Error = D_GIF_ERR_READ_FAILED;
195 free((char *)Private);
196 free((char *)GifFile);
197 return NULL;
198 }
199
200 /* Check for GIF prefix at start of file */
201 Buf[GIF_STAMP_LEN] = '\0';
202 if (strncmp(GIF_STAMP, Buf, GIF_VERSION_POS) != 0) {
203 if (Error != NULL)
204 *Error = D_GIF_ERR_NOT_GIF_FILE;
205 free((char *)Private);
206 free((char *)GifFile);
207 return NULL;
208 }
209
210 if (DGifGetScreenDesc(GifFile) == GIF_ERROR) {
211 free((char *)Private);
212 free((char *)GifFile);
213 return NULL;
214 }
215
216 GifFile->Error = 0;
217
218 /* What version of GIF? */
219 Private->gif89 = (Buf[GIF_VERSION_POS] == '9');
220
221 return GifFile;
222}
223
224/******************************************************************************
225 This routine should be called before any other DGif calls. Note that
226 this routine is called automatically from DGif file open routines.
227******************************************************************************/
228int
229DGifGetScreenDesc(GifFileType *GifFile)
230{
231 int BitsPerPixel;
232 bool SortFlag;
233 GifByteType Buf[3];
234 GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
235
236 if (!IS_READABLE(Private)) {
237 /* This file was NOT open for reading: */
238 GifFile->Error = D_GIF_ERR_NOT_READABLE;
239 return GIF_ERROR;
240 }
241
242 /* Put the screen descriptor into the file: */
243 if (DGifGetWord(GifFile, &GifFile->SWidth) == GIF_ERROR ||
244 DGifGetWord(GifFile, &GifFile->SHeight) == GIF_ERROR)
245 return GIF_ERROR;
246
247 if (READ(GifFile, Buf, 3) != 3) {
248 GifFile->Error = D_GIF_ERR_READ_FAILED;
249 GifFreeMapObject(GifFile->SColorMap);
250 GifFile->SColorMap = NULL;
251 return GIF_ERROR;
252 }
253 GifFile->SColorResolution = (((Buf[0] & 0x70) + 1) >> 4) + 1;
254 SortFlag = (Buf[0] & 0x08) != 0;
255 BitsPerPixel = (Buf[0] & 0x07) + 1;
256 GifFile->SBackGroundColor = Buf[1];
257 GifFile->AspectByte = Buf[2];
258 if (Buf[0] & 0x80) { /* Do we have global color map? */
259 int i;
260
261 GifFile->SColorMap = GifMakeMapObject(1 << BitsPerPixel, NULL);
262 if (GifFile->SColorMap == NULL) {
263 GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM;
264 return GIF_ERROR;
265 }
266
267 /* Get the global color map: */
268 GifFile->SColorMap->SortFlag = SortFlag;
269 for (i = 0; i < GifFile->SColorMap->ColorCount; i++) {
270 if (READ(GifFile, Buf, 3) != 3) {
271 GifFreeMapObject(GifFile->SColorMap);
272 GifFile->SColorMap = NULL;
273 GifFile->Error = D_GIF_ERR_READ_FAILED;
274 return GIF_ERROR;
275 }
276 GifFile->SColorMap->Colors[i].Red = Buf[0];
277 GifFile->SColorMap->Colors[i].Green = Buf[1];
278 GifFile->SColorMap->Colors[i].Blue = Buf[2];
279 }
280 } else {
281 GifFile->SColorMap = NULL;
282 }
283
284 return GIF_OK;
285}
286
287/******************************************************************************
288 This routine should be called before any attempt to read an image.
289******************************************************************************/
290int
291DGifGetRecordType(GifFileType *GifFile, GifRecordType* Type)
292{
293 GifByteType Buf;
294 GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
295
296 if (!IS_READABLE(Private)) {
297 /* This file was NOT open for reading: */
298 GifFile->Error = D_GIF_ERR_NOT_READABLE;
299 return GIF_ERROR;
300 }
301
302 if (READ(GifFile, &Buf, 1) != 1) {
303 GifFile->Error = D_GIF_ERR_READ_FAILED;
304 return GIF_ERROR;
305 }
306
307 switch (Buf) {
308 case DESCRIPTOR_INTRODUCER:
309 *Type = IMAGE_DESC_RECORD_TYPE;
310 break;
311 case EXTENSION_INTRODUCER:
312 *Type = EXTENSION_RECORD_TYPE;
313 break;
314 default:
315 *Type = TERMINATE_RECORD_TYPE;
316 break;
317 }
318
319 return GIF_OK;
320}
321
322/******************************************************************************
323 This routine should be called before any attempt to read an image.
324 Note it is assumed the Image desc. header has been read.
325******************************************************************************/
326int
327DGifGetImageDesc(GifFileType *GifFile)
328{
329 unsigned int BitsPerPixel;
330 GifByteType Buf[3];
331 GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
332/* SavedImage *sp; */
333
334 if (!IS_READABLE(Private)) {
335 /* This file was NOT open for reading: */
336 GifFile->Error = D_GIF_ERR_NOT_READABLE;
337 return GIF_ERROR;
338 }
339
340 if (DGifGetWord(GifFile, &GifFile->Image.Left) == GIF_ERROR ||
341 DGifGetWord(GifFile, &GifFile->Image.Top) == GIF_ERROR ||
342 DGifGetWord(GifFile, &GifFile->Image.Width) == GIF_ERROR ||
343 DGifGetWord(GifFile, &GifFile->Image.Height) == GIF_ERROR)
344 return GIF_ERROR;
345 if (READ(GifFile, Buf, 1) != 1) {
346 GifFile->Error = D_GIF_ERR_READ_FAILED;
347 GifFreeMapObject(GifFile->Image.ColorMap);
348 GifFile->Image.ColorMap = NULL;
349 return GIF_ERROR;
350 }
351 BitsPerPixel = (Buf[0] & 0x07) + 1;
352 GifFile->Image.Interlace = (Buf[0] & 0x40) ? true : false;
353
354 /* Setup the colormap */
355 if (GifFile->Image.ColorMap) {
356 GifFreeMapObject(GifFile->Image.ColorMap);
357 GifFile->Image.ColorMap = NULL;
358 }
359 /* Does this image have local color map? */
360 if (Buf[0] & 0x80) {
361 int i;
362
363 GifFile->Image.ColorMap = GifMakeMapObject(1 << BitsPerPixel, NULL);
364 if (GifFile->Image.ColorMap == NULL) {
365 GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM;
366 return GIF_ERROR;
367 }
368
369 /* Get the image local color map: */
370 for (i = 0; i < GifFile->Image.ColorMap->ColorCount; i++) {
371 if (READ(GifFile, Buf, 3) != 3) {
372 GifFreeMapObject(GifFile->Image.ColorMap);
373 GifFile->Error = D_GIF_ERR_READ_FAILED;
374 GifFile->Image.ColorMap = NULL;
375 return GIF_ERROR;
376 }
377 GifFile->Image.ColorMap->Colors[i].Red = Buf[0];
378 GifFile->Image.ColorMap->Colors[i].Green = Buf[1];
379 GifFile->Image.ColorMap->Colors[i].Blue = Buf[2];
380 }
381 }
382
383#if 0
384 if (GifFile->SavedImages) {
385 if ((GifFile->SavedImages = (SavedImage *)realloc(GifFile->SavedImages,
386 sizeof(SavedImage) *
387 (GifFile->ImageCount + 1))) == NULL) {
388 GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM;
389 return GIF_ERROR;
390 }
391 } else {
392 if ((GifFile->SavedImages =
393 (SavedImage *) malloc(sizeof(SavedImage))) == NULL) {
394 GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM;
395 return GIF_ERROR;
396 }
397 }
398
399 sp = &GifFile->SavedImages[GifFile->ImageCount];
400 memcpy(&sp->ImageDesc, &GifFile->Image, sizeof(GifImageDesc));
401 if (GifFile->Image.ColorMap != NULL) {
402 sp->ImageDesc.ColorMap = GifMakeMapObject(
403 GifFile->Image.ColorMap->ColorCount,
404 GifFile->Image.ColorMap->Colors);
405 if (sp->ImageDesc.ColorMap == NULL) {
406 GifFile->Error = D_GIF_ERR_NOT_ENOUGH_MEM;
407 return GIF_ERROR;
408 }
409 }
410 sp->RasterBits = (unsigned char *)NULL;
411 sp->ExtensionBlockCount = 0;
412 sp->ExtensionBlocks = (ExtensionBlock *) NULL;
413
414 GifFile->ImageCount++;
415#endif
416 Private->PixelCount = (long)GifFile->Image.Width *
417 (long)GifFile->Image.Height;
418
419 /* Reset decompress algorithm parameters. */
420 (void)DGifSetupDecompress(GifFile);
421
422 return GIF_OK;
423}
424
425/******************************************************************************
426 Get one full scanned line (Line) of length LineLen from GIF file.
427******************************************************************************/
428int
429DGifGetLine(GifFileType *GifFile, GifPixelType *Line, int LineLen)
430{
431 GifByteType *Dummy;
432 GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
433
434 if (!IS_READABLE(Private)) {
435 /* This file was NOT open for reading: */
436 GifFile->Error = D_GIF_ERR_NOT_READABLE;
437 return GIF_ERROR;
438 }
439
440 if (!LineLen)
441 LineLen = GifFile->Image.Width;
442
443 if ((Private->PixelCount -= LineLen) > 0xffff0000UL) {
444 GifFile->Error = D_GIF_ERR_DATA_TOO_BIG;
445 return GIF_ERROR;
446 }
447
448 if (DGifDecompressLine(GifFile, Line, LineLen) == GIF_OK) {
449 if (Private->PixelCount == 0) {
450 /* We probably won't be called any more, so let's clean up
451 * everything before we return: need to flush out all the
452 * rest of image until an empty block (size 0)
453 * detected. We use GetCodeNext.
454 */
455 do
456 if (DGifGetCodeNext(GifFile, &Dummy) == GIF_ERROR)
457 return GIF_ERROR;
458 while (Dummy != NULL) ;
459 }
460 return GIF_OK;
461 } else
462 return GIF_ERROR;
463}
464
465/******************************************************************************
466 Put one pixel (Pixel) into GIF file.
467******************************************************************************/
468int
469DGifGetPixel(GifFileType *GifFile, GifPixelType Pixel)
470{
471 GifByteType *Dummy;
472 GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
473
474 if (!IS_READABLE(Private)) {
475 /* This file was NOT open for reading: */
476 GifFile->Error = D_GIF_ERR_NOT_READABLE;
477 return GIF_ERROR;
478 }
479 if (--Private->PixelCount > 0xffff0000UL)
480 {
481 GifFile->Error = D_GIF_ERR_DATA_TOO_BIG;
482 return GIF_ERROR;
483 }
484
485 if (DGifDecompressLine(GifFile, &Pixel, 1) == GIF_OK) {
486 if (Private->PixelCount == 0) {
487 /* We probably won't be called any more, so let's clean up
488 * everything before we return: need to flush out all the
489 * rest of image until an empty block (size 0)
490 * detected. We use GetCodeNext.
491 */
492 do
493 if (DGifGetCodeNext(GifFile, &Dummy) == GIF_ERROR)
494 return GIF_ERROR;
495 while (Dummy != NULL) ;
496 }
497 return GIF_OK;
498 } else
499 return GIF_ERROR;
500}
501
502/******************************************************************************
503 Get an extension block (see GIF manual) from GIF file. This routine only
504 returns the first data block, and DGifGetExtensionNext should be called
505 after this one until NULL extension is returned.
506 The Extension should NOT be freed by the user (not dynamically allocated).
507 Note it is assumed the Extension description header has been read.
508******************************************************************************/
509int
510DGifGetExtension(GifFileType *GifFile, int *ExtCode, GifByteType **Extension)
511{
512 GifByteType Buf;
513 GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
514
515 if (!IS_READABLE(Private)) {
516 /* This file was NOT open for reading: */
517 GifFile->Error = D_GIF_ERR_NOT_READABLE;
518 return GIF_ERROR;
519 }
520
521 if (READ(GifFile, &Buf, 1) != 1) {
522 GifFile->Error = D_GIF_ERR_READ_FAILED;
523 return GIF_ERROR;
524 }
525 *ExtCode = Buf;
526
527 return DGifGetExtensionNext(GifFile, Extension);
528}
529
530/******************************************************************************
531 Get a following extension block (see GIF manual) from GIF file. This
532 routine should be called until NULL Extension is returned.
533 The Extension should NOT be freed by the user (not dynamically allocated).
534******************************************************************************/
535int
536DGifGetExtensionNext(GifFileType *GifFile, GifByteType ** Extension)
537{
538 GifByteType Buf;
539 GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
540
541 if (READ(GifFile, &Buf, 1) != 1) {
542 GifFile->Error = D_GIF_ERR_READ_FAILED;
543 return GIF_ERROR;
544 }
545 if (Buf > 0) {
546 *Extension = Private->Buf; /* Use private unused buffer. */
547 (*Extension)[0] = Buf; /* Pascal strings notation (pos. 0 is len.). */
548 /* coverity[tainted_data] */
549 if (READ(GifFile, &((*Extension)[1]), Buf) != Buf) {
550 GifFile->Error = D_GIF_ERR_READ_FAILED;
551 return GIF_ERROR;
552 }
553 } else
554 *Extension = NULL;
555
556 return GIF_OK;
557}
558
559/******************************************************************************
560 Extract a Graphics Control Block from raw extension data
561******************************************************************************/
562
563int DGifExtensionToGCB(const size_t GifExtensionLength,
564 const GifByteType *GifExtension,
565 GraphicsControlBlock *GCB)
566{
567 if (GifExtensionLength != 4) {
568 return GIF_ERROR;
569 }
570
571 GCB->DisposalMode = (GifExtension[0] >> 2) & 0x07;
572 GCB->UserInputFlag = (GifExtension[0] & 0x02) != 0;
573 GCB->DelayTime = UNSIGNED_LITTLE_ENDIAN(GifExtension[1], GifExtension[2]);
574 if (GifExtension[0] & 0x01)
575 GCB->TransparentColor = (int)GifExtension[3];
576 else
577 GCB->TransparentColor = NO_TRANSPARENT_COLOR;
578
579 return GIF_OK;
580}
581
582/******************************************************************************
583 Extract the Graphics Control Block for a saved image, if it exists.
584******************************************************************************/
585
586int DGifSavedExtensionToGCB(GifFileType *GifFile,
587 int ImageIndex, GraphicsControlBlock *GCB)
588{
589 int i;
590
591 if (ImageIndex < 0 || ImageIndex > GifFile->ImageCount - 1)
592 return GIF_ERROR;
593
594 GCB->DisposalMode = DISPOSAL_UNSPECIFIED;
595 GCB->UserInputFlag = false;
596 GCB->DelayTime = 0;
597 GCB->TransparentColor = NO_TRANSPARENT_COLOR;
598
599 for (i = 0; i < GifFile->SavedImages[ImageIndex].ExtensionBlockCount; i++) {
600 ExtensionBlock *ep = &GifFile->SavedImages[ImageIndex].ExtensionBlocks[i];
601 if (ep->Function == GRAPHICS_EXT_FUNC_CODE)
602 return DGifExtensionToGCB(ep->ByteCount, ep->Bytes, GCB);
603 }
604
605 return GIF_ERROR;
606}
607
608/******************************************************************************
609 This routine should be called last, to close the GIF file.
610******************************************************************************/
611int
612DGifCloseFile(GifFileType *GifFile)
613{
614 GifFilePrivateType *Private;
615
616 if (GifFile == NULL || GifFile->Private == NULL)
617 return GIF_ERROR;
618
619 if (GifFile->Image.ColorMap) {
620 GifFreeMapObject(GifFile->Image.ColorMap);
621 GifFile->Image.ColorMap = NULL;
622 }
623
624 if (GifFile->Image.GCB) {
625 free(GifFile->Image.GCB);
626 GifFile->Image.GCB = NULL;
627 }
628
629 if (GifFile->SColorMap) {
630 GifFreeMapObject(GifFile->SColorMap);
631 GifFile->SColorMap = NULL;
632 }
633
634 if (GifFile->SavedImages) {
635 GifFreeSavedImages(GifFile);
636 GifFile->SavedImages = NULL;
637 }
638
639 GifFreeExtensions(&GifFile->ExtensionBlockCount, &GifFile->ExtensionBlocks);
640
641 Private = (GifFilePrivateType *) GifFile->Private;
642
643 if (!IS_READABLE(Private)) {
644 /* This file was NOT open for reading: */
645 GifFile->Error = D_GIF_ERR_NOT_READABLE;
646 return GIF_ERROR;
647 }
648
649 if (Private->File && (fclose(Private->File) != 0)) {
650 GifFile->Error = D_GIF_ERR_CLOSE_FAILED;
651 return GIF_ERROR;
652 }
653
654 free((char *)GifFile->Private);
655
656 /*
657 * Without the #ifndef, we get spurious warnings because Coverity mistakenly
658 * thinks the GIF structure is freed on an error return.
659 */
660#ifndef __COVERITY__
661 free(GifFile);
662#endif /* __COVERITY__ */
663
664 return GIF_OK;
665}
666
667/******************************************************************************
668 Get 2 bytes (word) from the given file:
669******************************************************************************/
670static int
671DGifGetWord(GifFileType *GifFile, GifWord *Word)
672{
673 unsigned char c[2];
674
675 if (READ(GifFile, c, 2) != 2) {
676 GifFile->Error = D_GIF_ERR_READ_FAILED;
677 return GIF_ERROR;
678 }
679
680 *Word = (GifWord)UNSIGNED_LITTLE_ENDIAN(c[0], c[1]);
681 return GIF_OK;
682}
683
684/******************************************************************************
685 Get the image code in compressed form. This routine can be called if the
686 information needed to be piped out as is. Obviously this is much faster
687 than decoding and encoding again. This routine should be followed by calls
688 to DGifGetCodeNext, until NULL block is returned.
689 The block should NOT be freed by the user (not dynamically allocated).
690******************************************************************************/
691int
692DGifGetCode(GifFileType *GifFile, int *CodeSize, GifByteType **CodeBlock)
693{
694 GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
695
696 if (!IS_READABLE(Private)) {
697 /* This file was NOT open for reading: */
698 GifFile->Error = D_GIF_ERR_NOT_READABLE;
699 return GIF_ERROR;
700 }
701
702 *CodeSize = Private->BitsPerPixel;
703
704 return DGifGetCodeNext(GifFile, CodeBlock);
705}
706
707/******************************************************************************
708 Continue to get the image code in compressed form. This routine should be
709 called until NULL block is returned.
710 The block should NOT be freed by the user (not dynamically allocated).
711******************************************************************************/
712int
713DGifGetCodeNext(GifFileType *GifFile, GifByteType **CodeBlock)
714{
715 GifByteType Buf;
716 GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
717
718 /* coverity[tainted_data_argument] */
719 if (READ(GifFile, &Buf, 1) != 1) {
720 GifFile->Error = D_GIF_ERR_READ_FAILED;
721 return GIF_ERROR;
722 }
723
724 /* coverity[lower_bounds] */
725 if (Buf > 0) {
726 *CodeBlock = Private->Buf; /* Use private unused buffer. */
727 (*CodeBlock)[0] = Buf; /* Pascal strings notation (pos. 0 is len.). */
728 /* coverity[tainted_data] */
729 if (READ(GifFile, &((*CodeBlock)[1]), Buf) != Buf) {
730 GifFile->Error = D_GIF_ERR_READ_FAILED;
731 return GIF_ERROR;
732 }
733 } else {
734 *CodeBlock = NULL;
735 Private->Buf[0] = 0; /* Make sure the buffer is empty! */
736 Private->PixelCount = 0; /* And local info. indicate image read. */
737 }
738
739 return GIF_OK;
740}
741
742/******************************************************************************
743 Setup the LZ decompression for this image:
744******************************************************************************/
745static int
746DGifSetupDecompress(GifFileType *GifFile)
747{
748 int i, BitsPerPixel;
749 GifByteType CodeSize;
750 GifPrefixType *Prefix;
751 GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
752
753 READ(GifFile, &CodeSize, 1); /* Read Code size from file. */
754 BitsPerPixel = CodeSize;
755
756 Private->Buf[0] = 0; /* Input Buffer empty. */
757 Private->BitsPerPixel = BitsPerPixel;
758 Private->ClearCode = (1 << BitsPerPixel);
759 Private->EOFCode = Private->ClearCode + 1;
760 Private->RunningCode = Private->EOFCode + 1;
761 Private->RunningBits = BitsPerPixel + 1; /* Number of bits per code. */
762 Private->MaxCode1 = 1 << Private->RunningBits; /* Max. code + 1. */
763 Private->StackPtr = 0; /* No pixels on the pixel stack. */
764 Private->LastCode = NO_SUCH_CODE;
765 Private->CrntShiftState = 0; /* No information in CrntShiftDWord. */
766 Private->CrntShiftDWord = 0;
767
768 Prefix = Private->Prefix;
769 for (i = 0; i <= LZ_MAX_CODE; i++)
770 Prefix[i] = NO_SUCH_CODE;
771
772 return GIF_OK;
773}
774
775/******************************************************************************
776 The LZ decompression routine:
777 This version decompress the given GIF file into Line of length LineLen.
778 This routine can be called few times (one per scan line, for example), in
779 order the complete the whole image.
780******************************************************************************/
781static int
782DGifDecompressLine(GifFileType *GifFile, GifPixelType *Line, int LineLen)
783{
784 int i = 0;
785 int j, CrntCode, EOFCode, ClearCode, CrntPrefix, LastCode, StackPtr;
786 GifByteType *Stack, *Suffix;
787 GifPrefixType *Prefix;
788 GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private;
789
790 StackPtr = Private->StackPtr;
791 Prefix = Private->Prefix;
792 Suffix = Private->Suffix;
793 Stack = Private->Stack;
794 EOFCode = Private->EOFCode;
795 ClearCode = Private->ClearCode;
796 LastCode = Private->LastCode;
797
798 if (StackPtr > LZ_MAX_CODE) {
799 return GIF_ERROR;
800 }
801
802 if (StackPtr != 0) {
803 /* Let pop the stack off before continueing to read the GIF file: */
804 while (StackPtr != 0 && i < LineLen)
805 Line[i++] = Stack[--StackPtr];
806 }
807
808 while (i < LineLen) { /* Decode LineLen items. */
809 if (DGifDecompressInput(GifFile, &CrntCode) == GIF_ERROR)
810 return GIF_ERROR;
811
812 if (CrntCode == EOFCode) {
813 /* Note however that usually we will not be here as we will stop
814 * decoding as soon as we got all the pixel, or EOF code will
815 * not be read at all, and DGifGetLine/Pixel clean everything. */
816 GifFile->Error = D_GIF_ERR_EOF_TOO_SOON;
817 return GIF_ERROR;
818 } else if (CrntCode == ClearCode) {
819 /* We need to start over again: */
820 for (j = 0; j <= LZ_MAX_CODE; j++)
821 Prefix[j] = NO_SUCH_CODE;
822 Private->RunningCode = Private->EOFCode + 1;
823 Private->RunningBits = Private->BitsPerPixel + 1;
824 Private->MaxCode1 = 1 << Private->RunningBits;
825 LastCode = Private->LastCode = NO_SUCH_CODE;
826 } else {
827 /* Its regular code - if in pixel range simply add it to output
828 * stream, otherwise trace to codes linked list until the prefix
829 * is in pixel range: */
830 if (CrntCode < ClearCode) {
831 /* This is simple - its pixel scalar, so add it to output: */
832 Line[i++] = CrntCode;
833 } else {
834 /* Its a code to needed to be traced: trace the linked list
835 * until the prefix is a pixel, while pushing the suffix
836 * pixels on our stack. If we done, pop the stack in reverse
837 * (thats what stack is good for!) order to output. */
838 if (Prefix[CrntCode] == NO_SUCH_CODE) {
839 /* Only allowed if CrntCode is exactly the running code:
840 * In that case CrntCode = XXXCode, CrntCode or the
841 * prefix code is last code and the suffix char is
842 * exactly the prefix of last code! */
843 if (CrntCode == Private->RunningCode - 2) {
844 CrntPrefix = LastCode;
845 Suffix[Private->RunningCode - 2] =
846 Stack[StackPtr++] = DGifGetPrefixChar(Prefix,
847 LastCode,
848 ClearCode);
849 } else {
850 GifFile->Error = D_GIF_ERR_IMAGE_DEFECT;
851 return GIF_ERROR;
852 }
853 } else
854 CrntPrefix = CrntCode;
855
856 /* Now (if image is O.K.) we should not get a NO_SUCH_CODE
857 * during the trace. As we might loop forever, in case of
858 * defective image, we use StackPtr as loop counter and stop
859 * before overflowing Stack[]. */
860 while (StackPtr < LZ_MAX_CODE &&
861 CrntPrefix > ClearCode && CrntPrefix <= LZ_MAX_CODE) {
862 Stack[StackPtr++] = Suffix[CrntPrefix];
863 CrntPrefix = Prefix[CrntPrefix];
864 }
865 if (StackPtr >= LZ_MAX_CODE || CrntPrefix > LZ_MAX_CODE) {
866 GifFile->Error = D_GIF_ERR_IMAGE_DEFECT;
867 return GIF_ERROR;
868 }
869 /* Push the last character on stack: */
870 Stack[StackPtr++] = CrntPrefix;
871
872 /* Now lets pop all the stack into output: */
873 while (StackPtr != 0 && i < LineLen)
874 Line[i++] = Stack[--StackPtr];
875 }
876 if (LastCode != NO_SUCH_CODE) {
877 Prefix[Private->RunningCode - 2] = LastCode;
878
879 if (CrntCode == Private->RunningCode - 2) {
880 /* Only allowed if CrntCode is exactly the running code:
881 * In that case CrntCode = XXXCode, CrntCode or the
882 * prefix code is last code and the suffix char is
883 * exactly the prefix of last code! */
884 Suffix[Private->RunningCode - 2] =
885 DGifGetPrefixChar(Prefix, LastCode, ClearCode);
886 } else {
887 Suffix[Private->RunningCode - 2] =
888 DGifGetPrefixChar(Prefix, CrntCode, ClearCode);
889 }
890 }
891 LastCode = CrntCode;
892 }
893 }
894
895 Private->LastCode = LastCode;
896 Private->StackPtr = StackPtr;
897
898 return GIF_OK;
899}
900
901/******************************************************************************
902 Routine to trace the Prefixes linked list until we get a prefix which is
903 not code, but a pixel value (less than ClearCode). Returns that pixel value.
904 If image is defective, we might loop here forever, so we limit the loops to
905 the maximum possible if image O.k. - LZ_MAX_CODE times.
906******************************************************************************/
907static int
908DGifGetPrefixChar(GifPrefixType *Prefix, int Code, int ClearCode)
909{
910 int i = 0;
911
912 while (Code > ClearCode && i++ <= LZ_MAX_CODE) {
913 if (Code > LZ_MAX_CODE) {
914 return NO_SUCH_CODE;
915 }
916 Code = Prefix[Code];
917 }
918 return Code;
919}
920
921/******************************************************************************
922 Interface for accessing the LZ codes directly. Set Code to the real code
923 (12bits), or to -1 if EOF code is returned.
924******************************************************************************/
925int
926DGifGetLZCodes(GifFileType *GifFile, int *Code)
927{
928 GifByteType *CodeBlock;
929 GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
930
931 if (!IS_READABLE(Private)) {
932 /* This file was NOT open for reading: */
933 GifFile->Error = D_GIF_ERR_NOT_READABLE;
934 return GIF_ERROR;
935 }
936
937 if (DGifDecompressInput(GifFile, Code) == GIF_ERROR)
938 return GIF_ERROR;
939
940 if (*Code == Private->EOFCode) {
941 /* Skip rest of codes (hopefully only NULL terminating block): */
942 do {
943 if (DGifGetCodeNext(GifFile, &CodeBlock) == GIF_ERROR)
944 return GIF_ERROR;
945 } while (CodeBlock != NULL) ;
946
947 *Code = -1;
948 } else if (*Code == Private->ClearCode) {
949 /* We need to start over again: */
950 Private->RunningCode = Private->EOFCode + 1;
951 Private->RunningBits = Private->BitsPerPixel + 1;
952 Private->MaxCode1 = 1 << Private->RunningBits;
953 }
954
955 return GIF_OK;
956}
957
958/******************************************************************************
959 The LZ decompression input routine:
960 This routine is responsable for the decompression of the bit stream from
961 8 bits (bytes) packets, into the real codes.
962 Returns GIF_OK if read successfully.
963******************************************************************************/
964static int
965DGifDecompressInput(GifFileType *GifFile, int *Code)
966{
967 static const unsigned short CodeMasks[] = {
968 0x0000, 0x0001, 0x0003, 0x0007,
969 0x000f, 0x001f, 0x003f, 0x007f,
970 0x00ff, 0x01ff, 0x03ff, 0x07ff,
971 0x0fff
972 };
973
974 GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private;
975
976 GifByteType NextByte;
977
978 /* The image can't contain more than LZ_BITS per code. */
979 if (Private->RunningBits > LZ_BITS) {
980 GifFile->Error = D_GIF_ERR_IMAGE_DEFECT;
981 return GIF_ERROR;
982 }
983
984 while (Private->CrntShiftState < Private->RunningBits) {
985 /* Needs to get more bytes from input stream for next code: */
986 if (DGifBufferedInput(GifFile, Private->Buf, &NextByte) == GIF_ERROR) {
987 return GIF_ERROR;
988 }
989 Private->CrntShiftDWord |=
990 ((unsigned long)NextByte) << Private->CrntShiftState;
991 Private->CrntShiftState += 8;
992 }
993 *Code = Private->CrntShiftDWord & CodeMasks[Private->RunningBits];
994
995 Private->CrntShiftDWord >>= Private->RunningBits;
996 Private->CrntShiftState -= Private->RunningBits;
997
998 /* If code cannot fit into RunningBits bits, must raise its size. Note
999 * however that codes above 4095 are used for special signaling.
1000 * If we're using LZ_BITS bits already and we're at the max code, just
1001 * keep using the table as it is, don't increment Private->RunningCode.
1002 */
1003 if (Private->RunningCode < LZ_MAX_CODE + 2 &&
1004 ++Private->RunningCode > Private->MaxCode1 &&
1005 Private->RunningBits < LZ_BITS) {
1006 Private->MaxCode1 <<= 1;
1007 Private->RunningBits++;
1008 }
1009 return GIF_OK;
1010}
1011
1012/******************************************************************************
1013 This routines read one GIF data block at a time and buffers it internally
1014 so that the decompression routine could access it.
1015 The routine returns the next byte from its internal buffer (or read next
1016 block in if buffer empty) and returns GIF_OK if succesful.
1017******************************************************************************/
1018static int
1019DGifBufferedInput(GifFileType *GifFile, GifByteType *Buf, GifByteType *NextByte)
1020{
1021 if (Buf[0] == 0) {
1022 /* Needs to read the next buffer - this one is empty: */
1023 if (READ(GifFile, Buf, 1) != 1) {
1024 GifFile->Error = D_GIF_ERR_READ_FAILED;
1025 return GIF_ERROR;
1026 }
1027 /* There shouldn't be any empty data blocks here as the LZW spec
1028 * says the LZW termination code should come first. Therefore we
1029 * shouldn't be inside this routine at that point.
1030 */
1031 if (Buf[0] == 0) {
1032 GifFile->Error = D_GIF_ERR_IMAGE_DEFECT;
1033 return GIF_ERROR;
1034 }
1035 /* There shouldn't be any empty data blocks here as the LZW spec
1036 * says the LZW termination code should come first. Therefore we
1037 * shouldn't be inside this routine at that point.
1038 */
1039 if (Buf[0] == 0) {
1040 GifFile->Error = D_GIF_ERR_IMAGE_DEFECT;
1041 return GIF_ERROR;
1042 }
1043 if (READ(GifFile, &Buf[1], Buf[0]) != Buf[0]) {
1044 GifFile->Error = D_GIF_ERR_READ_FAILED;
1045 return GIF_ERROR;
1046 }
1047 *NextByte = Buf[1];
1048 Buf[1] = 2; /* We use now the second place as last char read! */
1049 Buf[0]--;
1050 } else {
1051 *NextByte = Buf[Buf[1]++];
1052 Buf[0]--;
1053 }
1054
1055 return GIF_OK;
1056}
1057
1058#if 0
1059/******************************************************************************
1060 This routine reads an entire GIF into core, hanging all its state info off
1061 the GifFileType pointer. Call DGifOpenFileName() or DGifOpenFileHandle()
1062 first to initialize I/O. Its inverse is EGifSpew().
1063*******************************************************************************/
1064int
1065DGifSlurp(GifFileType *GifFile)
1066{
1067 size_t ImageSize;
1068 GifRecordType RecordType;
1069 SavedImage *sp;
1070 GifByteType *ExtData;
1071 int ExtFunction;
1072
1073 GifFile->ExtensionBlocks = NULL;
1074 GifFile->ExtensionBlockCount = 0;
1075
1076 do {
1077 if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR)
1078 return (GIF_ERROR);
1079
1080 switch (RecordType) {
1081 case IMAGE_DESC_RECORD_TYPE:
1082 if (DGifGetImageDesc(GifFile) == GIF_ERROR)
1083 return (GIF_ERROR);
1084
1085 sp = &GifFile->SavedImages[GifFile->ImageCount - 1];
1086 /* Allocate memory for the image */
1087 if (sp->ImageDesc.Width < 0 && sp->ImageDesc.Height < 0 &&
1088 sp->ImageDesc.Width > (INT_MAX / sp->ImageDesc.Height)) {
1089 return GIF_ERROR;
1090 }
1091 ImageSize = sp->ImageDesc.Width * sp->ImageDesc.Height;
1092
1093 if (ImageSize > (SIZE_MAX / sizeof(GifPixelType))) {
1094 return GIF_ERROR;
1095 }
1096 sp->RasterBits = (unsigned char *)malloc(ImageSize *
1097 sizeof(GifPixelType));
1098
1099 if (sp->RasterBits == NULL) {
1100 return GIF_ERROR;
1101 }
1102
1103 if (sp->ImageDesc.Interlace) {
1104 int i, j;
1105 /*
1106 * The way an interlaced image should be read -
1107 * offsets and jumps...
1108 */
1109 int InterlacedOffset[] = { 0, 4, 2, 1 };
1110 int InterlacedJumps[] = { 8, 8, 4, 2 };
1111 /* Need to perform 4 passes on the image */
1112 for (i = 0; i < 4; i++)
1113 for (j = InterlacedOffset[i];
1114 j < sp->ImageDesc.Height;
1115 j += InterlacedJumps[i]) {
1116 if (DGifGetLine(GifFile,
1117 sp->RasterBits+j*sp->ImageDesc.Width,
1118 sp->ImageDesc.Width) == GIF_ERROR)
1119 return GIF_ERROR;
1120 }
1121 }
1122 else {
1123 if (DGifGetLine(GifFile,sp->RasterBits,ImageSize)==GIF_ERROR)
1124 return (GIF_ERROR);
1125 }
1126
1127 if (GifFile->ExtensionBlocks) {
1128 sp->ExtensionBlocks = GifFile->ExtensionBlocks;
1129 sp->ExtensionBlockCount = GifFile->ExtensionBlockCount;
1130
1131 GifFile->ExtensionBlocks = NULL;
1132 GifFile->ExtensionBlockCount = 0;
1133 }
1134 break;
1135
1136 case EXTENSION_RECORD_TYPE:
1137 if (DGifGetExtension(GifFile,&ExtFunction,&ExtData) == GIF_ERROR)
1138 return (GIF_ERROR);
1139 /* Create an extension block with our data */
1140 if (GifAddExtensionBlock(&GifFile->ExtensionBlockCount,
1141 &GifFile->ExtensionBlocks,
1142 ExtFunction, ExtData[0], &ExtData[1])
1143 == GIF_ERROR)
1144 return (GIF_ERROR);
1145 while (ExtData != NULL) {
1146 if (DGifGetExtensionNext(GifFile, &ExtData) == GIF_ERROR)
1147 return (GIF_ERROR);
1148 /* Continue the extension block */
1149 if (ExtData != NULL)
1150 if (GifAddExtensionBlock(&GifFile->ExtensionBlockCount,
1151 &GifFile->ExtensionBlocks,
1152 CONTINUE_EXT_FUNC_CODE,
1153 ExtData[0], &ExtData[1]) == GIF_ERROR)
1154 return (GIF_ERROR);
1155 }
1156 break;
1157
1158 case TERMINATE_RECORD_TYPE:
1159 break;
1160
1161 default: /* Should be trapped by DGifGetRecordType */
1162 break;
1163 }
1164 } while (RecordType != TERMINATE_RECORD_TYPE);
1165
1166 return (GIF_OK);
1167}
1168#endif
1169/* end */
diff --git a/apps/plugins/imageviewer/gif/gif.c b/apps/plugins/imageviewer/gif/gif.c
new file mode 100644
index 0000000000..672735af94
--- /dev/null
+++ b/apps/plugins/imageviewer/gif/gif.c
@@ -0,0 +1,241 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (c) 2012 Marcin Bukat
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
15 *
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
18 *
19 ****************************************************************************/
20
21#include "plugin.h"
22#include "lcd.h"
23#include <lib/pluginlib_bmp.h>
24#include "../imageviewer.h"
25#include "bmp.h"
26#include "gif_decoder.h"
27#include "gif_lib.h"
28
29/* decoder context struct */
30static struct gif_decoder decoder;
31
32static char print[32]; /* use a common snprintf() buffer */
33
34/* decompressed image in the possible sizes (1,2,4,8), wasting the other */
35/* max 32 frames */
36static unsigned char *disp[GIF_MAX_FRAMES][9];
37static unsigned char *disp_buf;
38
39#if defined(HAVE_LCD_COLOR)
40#define resize_bitmap smooth_resize_bitmap
41#else
42#define resize_bitmap grey_resize_bitmap
43#endif
44
45#if defined(USEGSLIB) && (CONFIG_PLATFORM & PLATFORM_HOSTED)
46/* hack: fix error "undefined reference to `_grey_info'". */
47GREY_INFO_STRUCT
48#endif /* USEGSLIB */
49
50static void draw_image_rect(struct image_info *info,
51 int x, int y, int width, int height)
52{
53 unsigned char **pdisp = (unsigned char **)info->data;
54
55#ifdef HAVE_LCD_COLOR
56 rb->lcd_bitmap_part((fb_data *)*pdisp, info->x + x, info->y + y,
57 STRIDE(SCREEN_MAIN, info->width, info->height),
58 x + MAX(0, (LCD_WIDTH-info->width)/2),
59 y + MAX(0, (LCD_HEIGHT-info->height)/2),
60 width, height);
61#else
62 mylcd_ub_gray_bitmap_part(*pdisp,
63 info->x + x, info->y + y, info->width,
64 x + MAX(0, (LCD_WIDTH-info->width)/2),
65 y + MAX(0, (LCD_HEIGHT-info->height)/2),
66 width, height);
67#endif
68}
69
70static int img_mem(int ds)
71{
72 struct gif_decoder *p_decoder = &decoder;
73 return p_decoder->native_img_size/ds;
74}
75
76static int load_image(char *filename, struct image_info *info,
77 unsigned char *buf, ssize_t *buf_size)
78{
79 int w, h;
80 long time = 0; /* measured ticks */
81 struct gif_decoder *p_decoder = &decoder;
82
83 unsigned char *memory, *memory_max;
84 size_t memory_size;
85
86 /* cleanup */
87 memset(&disp, 0, sizeof(disp));
88
89 /* align buffer */
90 memory = (unsigned char *)((intptr_t)(buf + 3) & ~3);
91 memory_max = (unsigned char *)((intptr_t)(memory + *buf_size) & ~3);
92 memory_size = memory_max - memory;
93
94#ifdef DISK_SPINDOWN
95 if (iv->running_slideshow && iv->immediate_ata_off) {
96 /* running slideshow and time is long enough: power down disk */
97 rb->storage_sleep();
98 }
99#endif
100
101 /* initialize decoder context struct, set buffer decoder is free
102 * to use.
103 */
104 gif_decoder_init(p_decoder, memory, memory_size);
105
106 /* populate internal data from gif file control structs */
107 gif_open(filename, p_decoder);
108
109 if (!p_decoder->error)
110 {
111
112 if (!iv->running_slideshow)
113 {
114 rb->lcd_putsf(0, 2, "image %dx%d",
115 p_decoder->width,
116 p_decoder->height);
117 rb->lcd_putsf(0, 3, "decoding %d*%d",
118 p_decoder->width,
119 p_decoder->height);
120 rb->lcd_update();
121 }
122
123 /* the actual decoding */
124 time = *rb->current_tick;
125
126#ifdef HAVE_ADJUSTABLE_CPU_FREQ
127 rb->cpu_boost(true);
128#endif
129 gif_decode(p_decoder, iv->cb_progress);
130
131#ifdef HAVE_ADJUSTABLE_CPU_FREQ
132 rb->cpu_boost(false);
133#endif
134 time = *rb->current_tick - time;
135 }
136
137 if (!iv->running_slideshow && !p_decoder->error)
138 {
139 rb->snprintf(print, sizeof(print), " %ld.%02ld sec ", time/HZ, time%HZ);
140 rb->lcd_getstringsize(print, &w, &h); /* centered in progress bar */
141 rb->lcd_putsxy((LCD_WIDTH - w)/2, LCD_HEIGHT - h, print);
142 rb->lcd_update();
143 }
144
145 if (p_decoder->error)
146 {
147 rb->splashf(HZ, "%s", GifErrorString(p_decoder->error));
148 return PLUGIN_ERROR;
149 }
150
151 info->x_size = p_decoder->width;
152 info->y_size = p_decoder->height;
153 info->frames_count = p_decoder->frames_count;
154 info->delay = p_decoder->delay;
155
156 //p_decoder->native_img_size = (p_decoder->native_img_size + 3) & ~3;
157 disp_buf = p_decoder->mem +
158 ((p_decoder->native_img_size*p_decoder->frames_count + 3) & ~3);
159
160 *buf_size = memory_max - disp_buf;
161
162 return PLUGIN_OK;
163}
164
165static int get_image(struct image_info *info, int frame, int ds)
166{
167 unsigned char **p_disp = &disp[frame][ds]; /* short cut */
168 struct gif_decoder *p_decoder = &decoder;
169
170 info->width = p_decoder->width / ds;
171 info->height = p_decoder->height / ds;
172 info->data = p_disp;
173
174 if (*p_disp != NULL)
175 {
176 /* we still have it */
177 return PLUGIN_OK;
178 }
179
180 /* assign image buffer */
181 if (ds > 1)
182 {
183 if (!iv->running_slideshow)
184 {
185 rb->lcd_putsf(0, 3, "resizing %d*%d", info->width, info->height);
186 rb->lcd_update();
187 }
188 struct bitmap bmp_src, bmp_dst;
189
190 /* size of the scalled image */
191 int size = img_mem(ds);
192
193 if (disp_buf + size >= p_decoder->mem + p_decoder->mem_size)
194 {
195 /* have to discard the current */
196 int i;
197 for (i=1; i<=8; i++)
198 disp[frame][i] = NULL; /* invalidate all bitmaps */
199
200 /* start again from the beginning of the buffer */
201 disp_buf = p_decoder->mem +
202 p_decoder->native_img_size*p_decoder->frames_count;
203 }
204
205 *p_disp = disp_buf;
206 disp_buf += size;
207
208 bmp_src.width = p_decoder->width;
209 bmp_src.height = p_decoder->height;
210 bmp_src.data = p_decoder->mem + p_decoder->native_img_size*frame;
211
212 bmp_dst.width = info->width;
213 bmp_dst.height = info->height;
214 bmp_dst.data = *p_disp;
215
216#ifdef HAVE_ADJUSTABLE_CPU_FREQ
217 rb->cpu_boost(true);
218#endif
219 resize_bitmap(&bmp_src, &bmp_dst);
220
221#ifdef HAVE_ADJUSTABLE_CPU_FREQ
222 rb->cpu_boost(false);
223#endif
224 }
225 else
226 {
227 *p_disp = p_decoder->mem + p_decoder->native_img_size*frame;
228 }
229
230 return PLUGIN_OK;
231}
232
233const struct image_decoder image_decoder = {
234 true,
235 img_mem,
236 load_image,
237 get_image,
238 draw_image_rect,
239};
240
241IMGDEC_HEADER
diff --git a/apps/plugins/imageviewer/gif/gif.make b/apps/plugins/imageviewer/gif/gif.make
new file mode 100644
index 0000000000..8260524ffe
--- /dev/null
+++ b/apps/plugins/imageviewer/gif/gif.make
@@ -0,0 +1,30 @@
1# __________ __ ___.
2# Open \______ \ ____ ____ | | _\_ |__ _______ ___
3# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
4# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
5# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
6# \/ \/ \/ \/ \/
7# $Id$
8#
9
10GIFSRCDIR := $(IMGVSRCDIR)/gif
11GIFBUILDDIR := $(IMGVBUILDDIR)/gif
12
13GIF_SRC := $(call preprocess, $(GIFSRCDIR)/SOURCES)
14GIF_OBJ := $(call c2obj, $(GIF_SRC))
15
16OTHER_SRC += $(GIF_SRC)
17
18ROCKS += $(GIFBUILDDIR)/gif.ovl
19
20$(GIFBUILDDIR)/gif.refmap: $(GIF_OBJ)
21$(GIFBUILDDIR)/gif.link: $(PLUGIN_LDS) $(GIFBUILDDIR)/gif.refmap
22$(GIFBUILDDIR)/gif.ovl: $(GIF_OBJ)
23
24#-Os breaks decoder - dunno why
25GIFFLAGS = $(IMGDECFLAGS) -O0
26
27# Compile PNG plugin with extra flags (adapted from ZXBox)
28$(GIFBUILDDIR)/%.o: $(GIFSRCDIR)/%.c $(GIFSRCDIR)/gif.make
29 $(SILENT)mkdir -p $(dir $@)
30 $(call PRINTS,CC $(subst $(ROOTDIR)/,,$<))$(CC) -I$(dir $<) $(GIFFLAGS) -c $< -o $@
diff --git a/apps/plugins/imageviewer/gif/gif_decoder.c b/apps/plugins/imageviewer/gif/gif_decoder.c
new file mode 100644
index 0000000000..ca9579aae3
--- /dev/null
+++ b/apps/plugins/imageviewer/gif/gif_decoder.c
@@ -0,0 +1,491 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (c) 2012 Marcin Bukat
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
15 *
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
18 *
19 ****************************************************************************/
20
21#include <lib/pluginlib_bmp.h>
22#include "bmp.h"
23#if LCD_DEPTH < 8
24#include <lib/grey.h>
25#endif
26
27#include "gif_lib.h"
28#include "gif_decoder.h"
29
30#ifndef resize_bitmap
31#if defined(HAVE_LCD_COLOR)
32#define resize_bitmap smooth_resize_bitmap
33#else
34#define resize_bitmap grey_resize_bitmap
35#endif
36#endif
37
38#if defined(HAVE_LCD_COLOR)
39typedef struct uint8_rgb pixel_t;
40#define NATIVE_SZ (GifFile->SWidth*GifFile->SHeight*FB_DATA_SZ)
41#define PIXEL_TRANSPARENT 0x00
42#else
43typedef unsigned char pixel_t;
44#define NATIVE_SZ (GifFile->SWidth*GifFile->SHeight)
45#define PIXEL_TRANSPARENT 0xff
46#endif
47
48#define PIXELS_SZ (GifFile->SWidth*GifFile->SHeight*sizeof(pixel_t))
49
50static GifFileType *GifFile;
51
52static void gif2pixels(GifPixelType *Line, pixel_t *out,
53 int Row, int Col, int Width)
54{
55 int x;
56#ifndef HAVE_LCD_COLOR
57 struct uint8_rgb rgb;
58#endif
59
60 GifColorType *ColorMapEntry;
61
62 /* Color map to use */
63 ColorMapObject *ColorMap = (GifFile->Image.ColorMap ?
64 GifFile->Image.ColorMap :
65 GifFile->SColorMap);
66
67 pixel_t *pixel = out + ((Row * GifFile->SWidth) + Col);
68
69 for (x = 0; x < Width; x++, pixel++)
70 {
71 ColorMapEntry = &ColorMap->Colors[Line[x]];
72
73 if (GifFile->Image.GCB &&
74 GifFile->Image.GCB->TransparentColor == Line[x])
75 continue;
76
77#ifdef HAVE_LCD_COLOR
78 pixel->red = ColorMapEntry->Red;
79 pixel->green = ColorMapEntry->Green;
80 pixel->blue = ColorMapEntry->Blue;
81#else
82 rgb.red = ColorMapEntry->Red;
83 rgb.green = ColorMapEntry->Green;
84 rgb.blue = ColorMapEntry->Blue;
85
86 *pixel = brightness(rgb);
87#endif
88 }
89}
90
91static void pixels2native(struct scaler_context *ctx,
92 pixel_t *pixels_buffer,
93 int Row)
94{
95#ifdef HAVE_LCD_COLOR
96 const struct custom_format *cformat = &format_native;
97#else
98 const struct custom_format *cformat = &format_grey;
99#endif
100
101 void (*output_row_8)(uint32_t, void*, struct scaler_context*) =
102 cformat->output_row_8;
103
104 output_row_8(Row, (void *)(pixels_buffer + (Row*ctx->bm->width)), ctx);
105}
106
107void gif_decoder_init(struct gif_decoder *d, void *mem, size_t size)
108{
109 memset(d, 0, sizeof(struct gif_decoder));
110
111 d->mem = mem;
112 d->mem_size = size;
113
114 /* mem allocator init */
115 init_memory_pool(d->mem_size, d->mem);
116}
117
118void gif_open(char *filename, struct gif_decoder *d)
119{
120 if ((GifFile = DGifOpenFileName(filename, &d->error)) == NULL)
121 return;
122
123 d->width = GifFile->SWidth;
124 d->height = GifFile->SHeight;
125 d->frames_count = 0;
126}
127
128static void set_canvas_background(GifPixelType *Line, pixel_t *out,
129 GifFileType *GifFile)
130{
131 int i;
132
133 /* Reading Gif spec it seems one should always use background color
134 * in canvas but most real files omit this and sets background color to 0
135 * (which IS valid index). We can choose to either conform to standard
136 * (and wrongly display most of gifs with transparency) or stick to
137 * common practise and treat background color 0 as transparent.
138 * I preffer the second.
139 */
140 if (GifFile->SColorMap && GifFile->SBackGroundColor != 0)
141 {
142 memset(Line, GifFile->SBackGroundColor, GifFile->SWidth);
143
144 for(i=0; i<GifFile->SHeight; i++)
145 gif2pixels(Line, out, i, 0, GifFile->SWidth);
146 }
147 else
148 {
149 memset(out, PIXEL_TRANSPARENT, PIXELS_SZ);
150 }
151
152}
153
154/* var names adhere to giflib coding style */
155void gif_decode(struct gif_decoder *d,
156 void (*pf_progress)(int current, int total))
157{
158 int i, j;
159
160 int Size;
161 int Row;
162 int Col;
163 int Width;
164 int Height;
165 int ExtCode;
166
167 GifPixelType *Line;
168
169 GifRecordType RecordType;
170 GifByteType *Extension;
171
172 unsigned char *out = NULL;
173
174 /* The way Interlaced image should
175 * be read - offsets and jumps
176 */
177 const char InterlacedOffset[] = { 0, 4, 2, 1 };
178 const char InterlacedJumps[] = { 8, 8, 4, 2 };
179
180 /* used for color conversion */
181 struct bitmap bm;
182 struct scaler_context ctx = {
183 .bm = &bm,
184 .dither = 0
185 };
186
187 /* initialize struct */
188 memset(&bm, 0, sizeof(struct bitmap));
189
190 Size = GifFile->SWidth * sizeof(GifPixelType); /* Size in bytes one row.*/
191 Line = (GifPixelType *)malloc(Size);
192 if (Line == NULL)
193 {
194 /* error allocating temp space */
195 d->error = D_GIF_ERR_NOT_ENOUGH_MEM;
196 return;
197 }
198
199 /* We use two pixel buffers if dispose method asks
200 * for restoration of the previous state.
201 * We only swap the indexes leaving data in place.
202 */
203 int buf_idx = 0;
204 pixel_t *pixels_buffer[2];
205 pixels_buffer[0] = (pixel_t *)malloc(PIXELS_SZ);
206 pixels_buffer[1] = NULL;
207
208 if (pixels_buffer[0] == NULL)
209 {
210 d->error = D_GIF_ERR_NOT_ENOUGH_MEM;
211 return;
212 }
213
214 /* Global background color */
215 set_canvas_background(Line, pixels_buffer[0], GifFile);
216
217 bm.width = GifFile->SWidth;
218 bm.height = GifFile->SHeight;
219 d->native_img_size = NATIVE_SZ;
220
221 if (pf_progress != NULL)
222 pf_progress(0, 100);
223
224 /* Scan the content of the GIF file and load the image(s) in: */
225 do
226 {
227 if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR)
228 {
229 d->error = GifFile->Error;
230 return;
231 }
232
233 switch (RecordType)
234 {
235 case IMAGE_DESC_RECORD_TYPE:
236
237 if (DGifGetImageDesc(GifFile) == GIF_ERROR)
238 {
239 d->error = GifFile->Error;
240 return;
241 }
242
243 /* Image Position relative to canvas */
244 Row = GifFile->Image.Top;
245 Col = GifFile->Image.Left;
246 Width = GifFile->Image.Width;
247 Height = GifFile->Image.Height;
248
249 /* Check Color map to use */
250 if (GifFile->Image.ColorMap == NULL &&
251 GifFile->SColorMap == NULL)
252 {
253 d->error = D_GIF_ERR_NO_COLOR_MAP;
254 return;
255 }
256
257 /* sanity check */
258 if (GifFile->Image.Left+GifFile->Image.Width>GifFile->SWidth ||
259 GifFile->Image.Top+GifFile->Image.Height>GifFile->SHeight)
260 {
261 d->error = D_GIF_ERR_DATA_TOO_BIG;
262 return;
263 }
264
265 if (GifFile->Image.GCB &&
266 GifFile->Image.GCB->DisposalMode == DISPOSE_PREVIOUS)
267 {
268 /* We need to take a snapshot before processing the image
269 * in order to restore canvas to previous state after
270 * rendering
271 */
272 buf_idx ^= 1;
273
274 if (pixels_buffer[buf_idx] == NULL)
275 pixels_buffer[buf_idx] = (pixel_t *)malloc(PIXELS_SZ);
276 }
277
278 if (GifFile->Image.Interlace)
279 {
280 /* Need to perform 4 passes on the image */
281 for (i = 0; i < 4; i++)
282 {
283 for (j = Row + InterlacedOffset[i];
284 j < Row + Height;
285 j += InterlacedJumps[i])
286 {
287 if (DGifGetLine(GifFile, Line, Width) == GIF_ERROR)
288 {
289 d->error = GifFile->Error;
290 return;
291 }
292
293 gif2pixels(Line, pixels_buffer[buf_idx],
294 Row + j, Col, Width);
295 }
296
297 pf_progress(25*(i+1), 100);
298 }
299 }
300 else
301 {
302 for (i = 0; i < Height; i++)
303 {
304 /* load single line into buffer */
305 if (DGifGetLine(GifFile, Line, Width) == GIF_ERROR)
306 {
307 d->error = GifFile->Error;
308 return;
309 }
310
311 gif2pixels(Line, pixels_buffer[buf_idx],
312 Row + i, Col, Width);
313
314 pf_progress(100*(i+1)/Height, 100);
315 }
316 }
317
318 /* allocate space for new frame */
319 out = realloc(out, d->native_img_size*(d->frames_count + 1));
320 if (out == NULL)
321 {
322 d->error = D_GIF_ERR_NOT_ENOUGH_MEM;
323 return;
324 }
325
326 bm.data = out + d->native_img_size*d->frames_count;
327
328 /* animated gif */
329 if (GifFile->Image.GCB && GifFile->Image.GCB->DelayTime != 0)
330 {
331 for (i=0; i < ctx.bm->height; i++)
332 pixels2native(&ctx, (void *)pixels_buffer[buf_idx], i);
333
334 /* restore to the background color */
335 switch (GifFile->Image.GCB->DisposalMode)
336 {
337 case DISPOSE_BACKGROUND:
338 set_canvas_background(Line, pixels_buffer[buf_idx],
339 GifFile);
340 break;
341
342 case DISPOSE_PREVIOUS:
343 buf_idx ^= 1;
344 break;
345
346 default:
347 /* DISPOSAL_UNSPECIFIED
348 * DISPOSE_DO_NOT
349 */
350 break;
351 }
352
353 d->frames_count++;
354
355 if (d->frames_count > GIF_MAX_FRAMES)
356 {
357 d->error = D_GIF_ERR_NOT_ENOUGH_MEM;
358 return;
359 }
360 }
361
362 break;
363
364 case EXTENSION_RECORD_TYPE:
365 if (DGifGetExtension(GifFile, &ExtCode, &Extension) ==
366 GIF_ERROR)
367 {
368 d->error = GifFile->Error;
369 return;
370 }
371
372 if (ExtCode == GRAPHICS_EXT_FUNC_CODE)
373 {
374 if (GifFile->Image.GCB == NULL)
375 GifFile->Image.GCB = (GraphicsControlBlock *)
376 malloc(sizeof(GraphicsControlBlock));
377
378 if (DGifExtensionToGCB(Extension[0],
379 Extension + 1,
380 GifFile->Image.GCB) == GIF_ERROR)
381 {
382 d->error = GifFile->Error;
383 return;
384 }
385 d->delay = GifFile->Image.GCB->DelayTime;
386 }
387
388 /* Skip anything else */
389 while (Extension != NULL)
390 {
391 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR)
392 {
393 d->error = GifFile->Error;
394 return;
395 }
396 }
397 break;
398
399 /* including TERMINATE_RECORD_TYPE */
400 default:
401 break;
402 }
403
404 } while (RecordType != TERMINATE_RECORD_TYPE);
405
406 /* free all internal allocated data */
407 if (DGifCloseFile(GifFile) == GIF_ERROR)
408 {
409 d->error = GifFile->Error;
410 return;
411 }
412
413 /* not animated gif */
414 if (d->frames_count == 0)
415 {
416 for (i=0; i < ctx.bm->height; i++)
417 pixels2native(&ctx, (void *)pixels_buffer[buf_idx], i);
418
419 d->frames_count++;
420 }
421
422 free(pixels_buffer[0]);
423 if (pixels_buffer[1])
424 free(pixels_buffer[1]);
425
426 free(Line);
427
428 /* WARNING !!!! */
429 /* GifFile object is trashed from now on, DONT use it */
430 /* Move bitmap in native format to the front of the buff */
431 memmove(d->mem, out, d->frames_count*d->native_img_size);
432
433 /* correct aspect ratio */
434#if (LCD_PIXEL_ASPECT_HEIGHT != 1 || LCD_PIXEL_ASPECT_WIDTH != 1)
435 struct bitmap img_src, img_dst; /* scaler vars */
436 struct dim dim_src, dim_dst; /* recalc_dimensions vars */
437 size_t c_native_img_size; /* size of the image after correction */
438
439 dim_src.width = bm.width;
440 dim_src.height = bm.height;
441
442 dim_dst.width = bm.width;
443 dim_dst.height = bm.height;
444
445 /* defined in apps/recorder/resize.c */
446 if (!recalc_dimension(&dim_dst, &dim_src))
447 {
448 /* calculate 'corrected' image size */
449#ifdef HAVE_LCD_COLOR
450 c_native_img_size = dim_dst.width * dim_dst.height * FB_DATA_SZ;
451#else
452 c_native_img_size = dim_dst.width * dim_dst.height;
453#endif
454
455 /* check memory constraints
456 * do the correction only if there is enough
457 * free memory
458 */
459 if (d->native_img_size*d->frames_count + c_native_img_size <=
460 d->mem_size)
461 {
462 img_dst.width = dim_dst.width;
463 img_dst.height = dim_dst.height;
464 img_dst.data = (unsigned char *)d->mem +
465 d->native_img_size*d->frames_count;
466
467 for (i = 0; i < d->frames_count; i++)
468 {
469 img_src.width = dim_src.width;
470 img_src.height = dim_src.height;
471 img_src.data = (unsigned char *)d->mem + i*d->native_img_size;
472
473 /* scale the bitmap to correct physical
474 * pixel dimentions
475 */
476 resize_bitmap(&img_src, &img_dst);
477
478 /* copy back corrected image */
479 memmove(d->mem + i*c_native_img_size,
480 img_dst.data,
481 c_native_img_size);
482 }
483
484 /* update decoder struct */
485 d->width = img_dst.width;
486 d->height = img_dst.height;
487 d->native_img_size = c_native_img_size;
488 }
489 }
490#endif
491}
diff --git a/apps/plugins/imageviewer/gif/gif_decoder.h b/apps/plugins/imageviewer/gif/gif_decoder.h
new file mode 100644
index 0000000000..444f54acf9
--- /dev/null
+++ b/apps/plugins/imageviewer/gif/gif_decoder.h
@@ -0,0 +1,37 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (c) 2012 Marcin Bukat
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
15 *
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
18 *
19 ****************************************************************************/
20
21#define GIF_MAX_FRAMES 32
22
23struct gif_decoder {
24 unsigned char *mem;
25 size_t mem_size;
26 int width;
27 int height;
28 int frames_count;
29 int delay;
30 size_t native_img_size;
31 int error;
32};
33
34void gif_decoder_init(struct gif_decoder *decoder, void *mem, size_t size);
35void gif_open(char *filename, struct gif_decoder *d);
36void gif_decode(struct gif_decoder *d, void (*pf_progress)(int current, int total));
37
diff --git a/apps/plugins/imageviewer/gif/gif_err.c b/apps/plugins/imageviewer/gif/gif_err.c
new file mode 100644
index 0000000000..abfb579b29
--- /dev/null
+++ b/apps/plugins/imageviewer/gif/gif_err.c
@@ -0,0 +1,99 @@
1/*****************************************************************************
2
3gif_err.c - handle error reporting for the GIF library.
4
5****************************************************************************/
6
7/* #include <stdio.h> */
8
9#include "gif_lib.h"
10#include "gif_lib_private.h"
11
12/*****************************************************************************
13 Return a string description of the last GIF error
14*****************************************************************************/
15char *
16GifErrorString(int ErrorCode)
17{
18 char *Err;
19
20 switch (ErrorCode) {
21#if 0
22 case E_GIF_ERR_OPEN_FAILED:
23 Err = "Failed to open given file";
24 break;
25 case E_GIF_ERR_WRITE_FAILED:
26 Err = "Failed to write to given file";
27 break;
28 case E_GIF_ERR_HAS_SCRN_DSCR:
29 Err = "Screen descriptor has already been set";
30 break;
31 case E_GIF_ERR_HAS_IMAG_DSCR:
32 Err = "Image descriptor is still active";
33 break;
34 case E_GIF_ERR_NO_COLOR_MAP:
35 Err = "Neither global nor local color map";
36 break;
37 case E_GIF_ERR_DATA_TOO_BIG:
38 Err = "Number of pixels bigger than width * height";
39 break;
40 case E_GIF_ERR_NOT_ENOUGH_MEM:
41 Err = "Failed to allocate required memory";
42 break;
43 case E_GIF_ERR_DISK_IS_FULL:
44 Err = "Write failed (disk full?)";
45 break;
46 case E_GIF_ERR_CLOSE_FAILED:
47 Err = "Failed to close given file";
48 break;
49 case E_GIF_ERR_NOT_WRITEABLE:
50 Err = "Given file was not opened for write";
51 break;
52#endif
53 case D_GIF_ERR_OPEN_FAILED:
54 Err = "Failed to open given file";
55 break;
56 case D_GIF_ERR_READ_FAILED:
57 Err = "Failed to read from given file";
58 break;
59 case D_GIF_ERR_NOT_GIF_FILE:
60 Err = "Data is not in GIF format";
61 break;
62 case D_GIF_ERR_NO_SCRN_DSCR:
63 Err = "No screen descriptor detected";
64 break;
65 case D_GIF_ERR_NO_IMAG_DSCR:
66 Err = "No Image Descriptor detected";
67 break;
68 case D_GIF_ERR_NO_COLOR_MAP:
69 Err = "Neither global nor local color map";
70 break;
71 case D_GIF_ERR_WRONG_RECORD:
72 Err = "Wrong record type detected";
73 break;
74 case D_GIF_ERR_DATA_TOO_BIG:
75 Err = "Number of pixels bigger than width * height";
76 break;
77 case D_GIF_ERR_NOT_ENOUGH_MEM:
78 Err = "Failed to allocate required memory";
79 break;
80 case D_GIF_ERR_CLOSE_FAILED:
81 Err = "Failed to close given file";
82 break;
83 case D_GIF_ERR_NOT_READABLE:
84 Err = "Given file was not opened for read";
85 break;
86 case D_GIF_ERR_IMAGE_DEFECT:
87 Err = "Image is defective, decoding aborted";
88 break;
89 case D_GIF_ERR_EOF_TOO_SOON:
90 Err = "Image EOF detected before image complete";
91 break;
92 default:
93 Err = NULL;
94 break;
95 }
96 return Err;
97}
98
99/* end */
diff --git a/apps/plugins/imageviewer/gif/gif_hash.h b/apps/plugins/imageviewer/gif/gif_hash.h
new file mode 100644
index 0000000000..a236bb56d1
--- /dev/null
+++ b/apps/plugins/imageviewer/gif/gif_hash.h
@@ -0,0 +1,39 @@
1/******************************************************************************
2
3gif_hash.h - magfic constants and declarations for GIF LZW
4
5******************************************************************************/
6
7#ifndef _GIF_HASH_H_
8#define _GIF_HASH_H_
9
10//#include <unistd.h>
11#include <stdint.h>
12
13#define HT_SIZE 8192 /* 12bits = 4096 or twice as big! */
14#define HT_KEY_MASK 0x1FFF /* 13bits keys */
15#define HT_KEY_NUM_BITS 13 /* 13bits keys */
16#define HT_MAX_KEY 8191 /* 13bits - 1, maximal code possible */
17#define HT_MAX_CODE 4095 /* Biggest code possible in 12 bits. */
18
19/* The 32 bits of the long are divided into two parts for the key & code: */
20/* 1. The code is 12 bits as our compression algorithm is limited to 12bits */
21/* 2. The key is 12 bits Prefix code + 8 bit new char or 20 bits. */
22/* The key is the upper 20 bits. The code is the lower 12. */
23#define HT_GET_KEY(l) (l >> 12)
24#define HT_GET_CODE(l) (l & 0x0FFF)
25#define HT_PUT_KEY(l) (l << 12)
26#define HT_PUT_CODE(l) (l & 0x0FFF)
27
28typedef struct GifHashTableType {
29 uint32_t HTable[HT_SIZE];
30} GifHashTableType;
31
32GifHashTableType *_InitHashTable(void);
33void _ClearHashTable(GifHashTableType *HashTable);
34void _InsertHashTable(GifHashTableType *HashTable, uint32_t Key, int Code);
35int _ExistsHashTable(GifHashTableType *HashTable, uint32_t Key);
36
37#endif /* _GIF_HASH_H_ */
38
39/* end */
diff --git a/apps/plugins/imageviewer/gif/gif_lib.h b/apps/plugins/imageviewer/gif/gif_lib.h
new file mode 100644
index 0000000000..0b77821b26
--- /dev/null
+++ b/apps/plugins/imageviewer/gif/gif_lib.h
@@ -0,0 +1,308 @@
1/******************************************************************************
2
3gif_lib.h - service library for decoding and encoding GIF images
4
5*****************************************************************************/
6
7#ifndef _GIF_LIB_H_
8#define _GIF_LIB_H_ 1
9
10#ifdef __cplusplus
11extern "C" {
12#endif /* __cplusplus */
13
14#define GIFLIB_MAJOR 5
15#define GIFLIB_MINOR 0
16#define GIFLIB_RELEASE 2
17
18#define GIF_ERROR 0
19#define GIF_OK 1
20
21#include <stdbool.h>
22#include "rb_glue.h"
23
24#define GIF_STAMP "GIFVER" /* First chars in file - GIF stamp. */
25#define GIF_STAMP_LEN sizeof(GIF_STAMP) - 1
26#define GIF_VERSION_POS 3 /* Version first character in stamp. */
27#define GIF87_STAMP "GIF87a" /* First chars in file - GIF stamp. */
28#define GIF89_STAMP "GIF89a" /* First chars in file - GIF stamp. */
29
30typedef unsigned char GifPixelType;
31typedef unsigned char *GifRowType;
32typedef unsigned char GifByteType;
33typedef unsigned int GifPrefixType;
34typedef int GifWord;
35
36/******************************************************************************
37 GIF89 structures
38******************************************************************************/
39
40typedef struct GraphicsControlBlock {
41 int DisposalMode;
42#define DISPOSAL_UNSPECIFIED 0 /* No disposal specified. */
43#define DISPOSE_DO_NOT 1 /* Leave image in place */
44#define DISPOSE_BACKGROUND 2 /* Set area too background color */
45#define DISPOSE_PREVIOUS 3 /* Restore to previous content */
46 bool UserInputFlag; /* User confirmation required before disposal */
47 int DelayTime; /* pre-display delay in 0.01sec units */
48 int TransparentColor; /* Palette index for transparency, -1 if none */
49#define NO_TRANSPARENT_COLOR -1
50} GraphicsControlBlock;
51
52typedef struct GifColorType {
53 GifByteType Red, Green, Blue;
54} GifColorType;
55
56typedef struct ColorMapObject {
57 int ColorCount;
58 int BitsPerPixel;
59 bool SortFlag;
60 GifColorType *Colors; /* on malloc(3) heap */
61} ColorMapObject;
62
63typedef struct GifImageDesc {
64 GifWord Left, Top, Width, Height; /* Current image dimensions. */
65 bool Interlace; /* Sequential/Interlaced lines. */
66 ColorMapObject *ColorMap; /* The local color map */
67 GraphicsControlBlock *GCB; /* Graphic control block */
68} GifImageDesc;
69
70typedef struct ExtensionBlock {
71 int ByteCount;
72 GifByteType *Bytes; /* on malloc(3) heap */
73 int Function; /* The block function code */
74#define CONTINUE_EXT_FUNC_CODE 0x00 /* continuation subblock */
75#define COMMENT_EXT_FUNC_CODE 0xfe /* comment */
76#define GRAPHICS_EXT_FUNC_CODE 0xf9 /* graphics control (GIF89) */
77#define PLAINTEXT_EXT_FUNC_CODE 0x01 /* plaintext */
78#define APPLICATION_EXT_FUNC_CODE 0xff /* application block */
79} ExtensionBlock;
80
81typedef struct SavedImage {
82 GifImageDesc ImageDesc;
83 GifByteType *RasterBits; /* on malloc(3) heap */
84 int ExtensionBlockCount; /* Count of extensions before image */
85 ExtensionBlock *ExtensionBlocks; /* Extensions before image */
86} SavedImage;
87
88typedef struct GifFileType {
89 GifWord SWidth, SHeight; /* Size of virtual canvas */
90 GifWord SColorResolution; /* How many colors can we generate? */
91 GifWord SBackGroundColor; /* Background color for virtual canvas */
92 GifByteType AspectByte; /* Used to compute pixel aspect ratio */
93 ColorMapObject *SColorMap; /* Global colormap, NULL if nonexistent. */
94 int ImageCount; /* Number of current image (both APIs) */
95 GifImageDesc Image; /* Current image (low-level API) */
96 SavedImage *SavedImages; /* Image sequence (high-level API) */
97 int ExtensionBlockCount; /* Count extensions past last image */
98 ExtensionBlock *ExtensionBlocks; /* Extensions past last image */
99 int Error; /* Last error condition reported */
100 void *UserData; /* hook to attach user data (TVT) */
101 void *Private; /* Don't mess with this! */
102} GifFileType;
103
104#define GIF_ASPECT_RATIO(n) ((n)+15.0/64.0)
105
106typedef enum {
107 UNDEFINED_RECORD_TYPE,
108 SCREEN_DESC_RECORD_TYPE,
109 IMAGE_DESC_RECORD_TYPE, /* Begin with ',' */
110 EXTENSION_RECORD_TYPE, /* Begin with '!' */
111 TERMINATE_RECORD_TYPE /* Begin with ';' */
112} GifRecordType;
113
114/* func type to read gif data from arbitrary sources (TVT) */
115typedef int (*InputFunc) (GifFileType *, GifByteType *, int);
116
117/* func type to write gif data to arbitrary targets.
118 * Returns count of bytes written. (MRB)
119 */
120typedef int (*OutputFunc) (GifFileType *, const GifByteType *, int);
121
122/******************************************************************************
123 GIF encoding routines
124******************************************************************************/
125
126/* Main entry points */
127GifFileType *EGifOpenFileName(const char *GifFileName,
128 const bool GifTestExistence, int *Error);
129GifFileType *EGifOpenFileHandle(const int GifFileHandle, int *Error);
130GifFileType *EGifOpen(void *userPtr, OutputFunc writeFunc, int *Error);
131int EGifSpew(GifFileType * GifFile);
132char *EGifGetGifVersion(GifFileType *GifFile); /* new in 5.x */
133int EGifCloseFile(GifFileType * GifFile);
134
135#define E_GIF_ERR_OPEN_FAILED 1 /* And EGif possible errors. */
136#define E_GIF_ERR_WRITE_FAILED 2
137#define E_GIF_ERR_HAS_SCRN_DSCR 3
138#define E_GIF_ERR_HAS_IMAG_DSCR 4
139#define E_GIF_ERR_NO_COLOR_MAP 5
140#define E_GIF_ERR_DATA_TOO_BIG 6
141#define E_GIF_ERR_NOT_ENOUGH_MEM 7
142#define E_GIF_ERR_DISK_IS_FULL 8
143#define E_GIF_ERR_CLOSE_FAILED 9
144#define E_GIF_ERR_NOT_WRITEABLE 10
145
146/* These are legacy. You probably do not want to call them directly */
147int EGifPutScreenDesc(GifFileType *GifFile,
148 const int GifWidth, const int GifHeight,
149 const int GifColorRes,
150 const int GifBackGround,
151 const ColorMapObject *GifColorMap);
152int EGifPutImageDesc(GifFileType *GifFile,
153 const int GifLeft, const int GifTop,
154 const int GifWidth, const int GifHeight,
155 const bool GifInterlace,
156 const ColorMapObject *GifColorMap);
157void EGifSetGifVersion(GifFileType *GifFile, const bool gif89);
158int EGifPutLine(GifFileType *GifFile, GifPixelType *GifLine,
159 int GifLineLen);
160int EGifPutPixel(GifFileType *GifFile, const GifPixelType GifPixel);
161int EGifPutComment(GifFileType *GifFile, const char *GifComment);
162int EGifPutExtensionLeader(GifFileType *GifFile, const int GifExtCode);
163int EGifPutExtensionBlock(GifFileType *GifFile,
164 const int GifExtLen, const void *GifExtension);
165int EGifPutExtensionTrailer(GifFileType *GifFile);
166int EGifPutExtension(GifFileType *GifFile, const int GifExtCode,
167 const int GifExtLen,
168 const void *GifExtension);
169int EGifPutCode(GifFileType *GifFile, int GifCodeSize,
170 const GifByteType *GifCodeBlock);
171int EGifPutCodeNext(GifFileType *GifFile,
172 const GifByteType *GifCodeBlock);
173
174/******************************************************************************
175 GIF decoding routines
176******************************************************************************/
177
178/* Main entry points */
179GifFileType *DGifOpenFileName(const char *GifFileName, int *Error);
180GifFileType *DGifOpenFileHandle(int GifFileHandle, int *Error);
181int DGifSlurp(GifFileType * GifFile);
182GifFileType *DGifOpen(void *userPtr, InputFunc readFunc, int *Error); /* new one (TVT) */
183int DGifCloseFile(GifFileType * GifFile);
184
185#define D_GIF_ERR_OPEN_FAILED 101 /* And DGif possible errors. */
186#define D_GIF_ERR_READ_FAILED 102
187#define D_GIF_ERR_NOT_GIF_FILE 103
188#define D_GIF_ERR_NO_SCRN_DSCR 104
189#define D_GIF_ERR_NO_IMAG_DSCR 105
190#define D_GIF_ERR_NO_COLOR_MAP 106
191#define D_GIF_ERR_WRONG_RECORD 107
192#define D_GIF_ERR_DATA_TOO_BIG 108
193#define D_GIF_ERR_NOT_ENOUGH_MEM 109
194#define D_GIF_ERR_CLOSE_FAILED 110
195#define D_GIF_ERR_NOT_READABLE 111
196#define D_GIF_ERR_IMAGE_DEFECT 112
197#define D_GIF_ERR_EOF_TOO_SOON 113
198
199/* These are legacy. You probably do not want to call them directly */
200int DGifGetScreenDesc(GifFileType *GifFile);
201int DGifGetRecordType(GifFileType *GifFile, GifRecordType *GifType);
202int DGifGetImageDesc(GifFileType *GifFile);
203int DGifGetLine(GifFileType *GifFile, GifPixelType *GifLine, int GifLineLen);
204int DGifGetPixel(GifFileType *GifFile, GifPixelType GifPixel);
205int DGifGetComment(GifFileType *GifFile, char *GifComment);
206int DGifGetExtension(GifFileType *GifFile, int *GifExtCode,
207 GifByteType **GifExtension);
208int DGifGetExtensionNext(GifFileType *GifFile, GifByteType **GifExtension);
209int DGifGetCode(GifFileType *GifFile, int *GifCodeSize,
210 GifByteType **GifCodeBlock);
211int DGifGetCodeNext(GifFileType *GifFile, GifByteType **GifCodeBlock);
212int DGifGetLZCodes(GifFileType *GifFile, int *GifCode);
213
214
215/******************************************************************************
216 Color table quantization (deprecated)
217******************************************************************************/
218int GifQuantizeBuffer(unsigned int Width, unsigned int Height,
219 int *ColorMapSize, GifByteType * RedInput,
220 GifByteType * GreenInput, GifByteType * BlueInput,
221 GifByteType * OutputBuffer,
222 GifColorType * OutputColorMap);
223
224/******************************************************************************
225 Error handling and reporting.
226******************************************************************************/
227extern char *GifErrorString(int ErrorCode); /* new in 2012 - ESR */
228
229/*****************************************************************************
230 Everything below this point is new after version 1.2, supporting `slurp
231 mode' for doing I/O in two big belts with all the image-bashing in core.
232******************************************************************************/
233
234/******************************************************************************
235 Color map handling from gif_alloc.c
236******************************************************************************/
237
238extern ColorMapObject *GifMakeMapObject(int ColorCount,
239 const GifColorType *ColorMap);
240extern void GifFreeMapObject(ColorMapObject *Object);
241extern ColorMapObject *GifUnionColorMap(const ColorMapObject *ColorIn1,
242 const ColorMapObject *ColorIn2,
243 GifPixelType ColorTransIn2[]);
244extern int GifBitSize(int n);
245
246/******************************************************************************
247 Support for the in-core structures allocation (slurp mode).
248******************************************************************************/
249
250extern void GifApplyTranslation(SavedImage *Image, GifPixelType Translation[]);
251extern int GifAddExtensionBlock(int *ExtensionBlock_Count,
252 ExtensionBlock **ExtensionBlocks,
253 int Function,
254 unsigned int Len, unsigned char ExtData[]);
255extern void GifFreeExtensions(int *ExtensionBlock_Count,
256 ExtensionBlock **ExtensionBlocks);
257extern SavedImage *GifMakeSavedImage(GifFileType *GifFile,
258 const SavedImage *CopyFrom);
259extern void GifFreeSavedImages(GifFileType *GifFile);
260
261/******************************************************************************
262 5.x functions for GIF89 graphics control blocks
263******************************************************************************/
264
265int DGifExtensionToGCB(const size_t GifExtensionLength,
266 const GifByteType *GifExtension,
267 GraphicsControlBlock *GCB);
268size_t EGifGCBToExtension(const GraphicsControlBlock *GCB,
269 GifByteType *GifExtension);
270
271int DGifSavedExtensionToGCB(GifFileType *GifFile,
272 int ImageIndex,
273 GraphicsControlBlock *GCB);
274int EGifGCBToSavedExtension(const GraphicsControlBlock *GCB,
275 GifFileType *GifFile,
276 int ImageIndex);
277
278/******************************************************************************
279 The library's internal utility font
280******************************************************************************/
281
282#define GIF_FONT_WIDTH 8
283#define GIF_FONT_HEIGHT 8
284extern const unsigned char GifAsciiTable8x8[][GIF_FONT_WIDTH];
285
286extern void GifDrawText8x8(SavedImage *Image,
287 const int x, const int y,
288 const char *legend, const int color);
289
290extern void GifDrawBox(SavedImage *Image,
291 const int x, const int y,
292 const int w, const int d, const int color);
293
294extern void GifDrawRectangle(SavedImage *Image,
295 const int x, const int y,
296 const int w, const int d, const int color);
297
298extern void GifDrawBoxedText8x8(SavedImage *Image,
299 const int x, const int y,
300 const char *legend,
301 const int border, const int bg, const int fg);
302
303#ifdef __cplusplus
304}
305#endif /* __cplusplus */
306#endif /* _GIF_LIB_H */
307
308/* end */
diff --git a/apps/plugins/imageviewer/gif/gif_lib_private.h b/apps/plugins/imageviewer/gif/gif_lib_private.h
new file mode 100644
index 0000000000..4e95aa4ee5
--- /dev/null
+++ b/apps/plugins/imageviewer/gif/gif_lib_private.h
@@ -0,0 +1,59 @@
1/****************************************************************************
2
3gif_lib_private.h - internal giflib routines and structures
4
5****************************************************************************/
6
7#ifndef _GIF_LIB_PRIVATE_H
8#define _GIF_LIB_PRIVATE_H
9
10#include "gif_lib.h"
11#include "gif_hash.h"
12
13#define EXTENSION_INTRODUCER 0x21
14#define DESCRIPTOR_INTRODUCER 0x2c
15#define TERMINATOR_INTRODUCER 0x3b
16
17#define LZ_MAX_CODE 4095 /* Biggest code possible in 12 bits. */
18#define LZ_BITS 12
19
20#define FLUSH_OUTPUT 4096 /* Impossible code, to signal flush. */
21#define FIRST_CODE 4097 /* Impossible code, to signal first. */
22#define NO_SUCH_CODE 4098 /* Impossible code, to signal empty. */
23
24#define FILE_STATE_WRITE 0x01
25#define FILE_STATE_SCREEN 0x02
26#define FILE_STATE_IMAGE 0x04
27#define FILE_STATE_READ 0x08
28
29#define IS_READABLE(Private) (Private->FileState & FILE_STATE_READ)
30#define IS_WRITEABLE(Private) (Private->FileState & FILE_STATE_WRITE)
31
32typedef struct GifFilePrivateType {
33 GifWord FileState, FileHandle, /* Where all this data goes to! */
34 BitsPerPixel, /* Bits per pixel (Codes uses at least this + 1). */
35 ClearCode, /* The CLEAR LZ code. */
36 EOFCode, /* The EOF LZ code. */
37 RunningCode, /* The next code algorithm can generate. */
38 RunningBits, /* The number of bits required to represent RunningCode. */
39 MaxCode1, /* 1 bigger than max. possible code, in RunningBits bits. */
40 LastCode, /* The code before the current code. */
41 CrntCode, /* Current algorithm code. */
42 StackPtr, /* For character stack (see below). */
43 CrntShiftState; /* Number of bits in CrntShiftDWord. */
44 unsigned long CrntShiftDWord; /* For bytes decomposition into codes. */
45 unsigned long PixelCount; /* Number of pixels in image. */
46 int File; /* File as stream. (converted to int in rb) */
47 InputFunc Read; /* function to read gif input (TVT) */
48 OutputFunc Write; /* function to write gif output (MRB) */
49 GifByteType Buf[256]; /* Compressed input is buffered here. */
50 GifByteType Stack[LZ_MAX_CODE]; /* Decoded pixels are stacked here. */
51 GifByteType Suffix[LZ_MAX_CODE + 1]; /* So we can trace the codes. */
52 GifPrefixType Prefix[LZ_MAX_CODE + 1];
53 GifHashTableType *HashTable;
54 bool gif89;
55} GifFilePrivateType;
56
57#endif /* _GIF_LIB_PRIVATE_H */
58
59/* end */
diff --git a/apps/plugins/imageviewer/gif/gifalloc.c b/apps/plugins/imageviewer/gif/gifalloc.c
new file mode 100644
index 0000000000..5810559320
--- /dev/null
+++ b/apps/plugins/imageviewer/gif/gifalloc.c
@@ -0,0 +1,405 @@
1/*****************************************************************************
2
3 GIF construction tools
4
5****************************************************************************/
6
7#include <stdlib.h>
8/* #include <stdio.h> */
9#include <string.h>
10
11#include "gif_lib.h"
12
13#ifndef MAX
14#define MAX(x, y) (((x) > (y)) ? (x) : (y))
15#endif
16
17/******************************************************************************
18 Miscellaneous utility functions
19******************************************************************************/
20
21/* return smallest bitfield size n will fit in */
22int
23GifBitSize(int n)
24{
25 register int i;
26
27 for (i = 1; i <= 8; i++)
28 if ((1 << i) >= n)
29 break;
30 return (i);
31}
32
33/******************************************************************************
34 Color map object functions
35******************************************************************************/
36
37/*
38 * Allocate a color map of given size; initialize with contents of
39 * ColorMap if that pointer is non-NULL.
40 */
41ColorMapObject *
42GifMakeMapObject(int ColorCount, const GifColorType *ColorMap)
43{
44 ColorMapObject *Object;
45
46 /*** FIXME: Our ColorCount has to be a power of two. Is it necessary to
47 * make the user know that or should we automatically round up instead? */
48 if (ColorCount != (1 << GifBitSize(ColorCount))) {
49 return ((ColorMapObject *) NULL);
50 }
51
52 Object = (ColorMapObject *)malloc(sizeof(ColorMapObject));
53 if (Object == (ColorMapObject *) NULL) {
54 return ((ColorMapObject *) NULL);
55 }
56
57 Object->Colors = (GifColorType *)calloc(ColorCount, sizeof(GifColorType));
58 if (Object->Colors == (GifColorType *) NULL) {
59 free(Object);
60 return ((ColorMapObject *) NULL);
61 }
62
63 Object->ColorCount = ColorCount;
64 Object->BitsPerPixel = GifBitSize(ColorCount);
65
66 if (ColorMap != NULL) {
67 memcpy((char *)Object->Colors,
68 (char *)ColorMap, ColorCount * sizeof(GifColorType));
69 }
70
71 return (Object);
72}
73
74/*******************************************************************************
75Free a color map object
76*******************************************************************************/
77void
78GifFreeMapObject(ColorMapObject *Object)
79{
80 if (Object != NULL) {
81 (void)free(Object->Colors);
82 (void)free(Object);
83 }
84}
85
86/* #ifdef DEBUG */
87#if 0
88void
89DumpColorMap(ColorMapObject *Object,
90 FILE * fp)
91{
92 if (Object != NULL) {
93 int i, j, Len = Object->ColorCount;
94
95 for (i = 0; i < Len; i += 4) {
96 for (j = 0; j < 4 && j < Len; j++) {
97 (void)fprintf(fp, "%3d: %02x %02x %02x ", i + j,
98 Object->Colors[i + j].Red,
99 Object->Colors[i + j].Green,
100 Object->Colors[i + j].Blue);
101 }
102 (void)fprintf(fp, "\n");
103 }
104 }
105}
106#endif /* DEBUG */
107
108/*******************************************************************************
109 Compute the union of two given color maps and return it. If result can't
110 fit into 256 colors, NULL is returned, the allocated union otherwise.
111 ColorIn1 is copied as is to ColorUnion, while colors from ColorIn2 are
112 copied iff they didn't exist before. ColorTransIn2 maps the old
113 ColorIn2 into the ColorUnion color map table./
114*******************************************************************************/
115ColorMapObject *
116GifUnionColorMap(const ColorMapObject *ColorIn1,
117 const ColorMapObject *ColorIn2,
118 GifPixelType ColorTransIn2[])
119{
120 int i, j, CrntSlot, RoundUpTo, NewGifBitSize;
121 ColorMapObject *ColorUnion;
122
123 /*
124 * We don't worry about duplicates within either color map; if
125 * the caller wants to resolve those, he can perform unions
126 * with an empty color map.
127 */
128
129 /* Allocate table which will hold the result for sure. */
130 ColorUnion = GifMakeMapObject(MAX(ColorIn1->ColorCount,
131 ColorIn2->ColorCount) * 2, NULL);
132
133 if (ColorUnion == NULL)
134 return (NULL);
135
136 /*
137 * Copy ColorIn1 to ColorUnion.
138 */
139 for (i = 0; i < ColorIn1->ColorCount; i++)
140 ColorUnion->Colors[i] = ColorIn1->Colors[i];
141 CrntSlot = ColorIn1->ColorCount;
142
143 /*
144 * Potentially obnoxious hack:
145 *
146 * Back CrntSlot down past all contiguous {0, 0, 0} slots at the end
147 * of table 1. This is very useful if your display is limited to
148 * 16 colors.
149 */
150 while (ColorIn1->Colors[CrntSlot - 1].Red == 0
151 && ColorIn1->Colors[CrntSlot - 1].Green == 0
152 && ColorIn1->Colors[CrntSlot - 1].Blue == 0)
153 CrntSlot--;
154
155 /* Copy ColorIn2 to ColorUnion (use old colors if they exist): */
156 for (i = 0; i < ColorIn2->ColorCount && CrntSlot <= 256; i++) {
157 /* Let's see if this color already exists: */
158 for (j = 0; j < ColorIn1->ColorCount; j++)
159 if (memcmp (&ColorIn1->Colors[j], &ColorIn2->Colors[i],
160 sizeof(GifColorType)) == 0)
161 break;
162
163 if (j < ColorIn1->ColorCount)
164 ColorTransIn2[i] = j; /* color exists in Color1 */
165 else {
166 /* Color is new - copy it to a new slot: */
167 ColorUnion->Colors[CrntSlot] = ColorIn2->Colors[i];
168 ColorTransIn2[i] = CrntSlot++;
169 }
170 }
171
172 if (CrntSlot > 256) {
173 GifFreeMapObject(ColorUnion);
174 return ((ColorMapObject *) NULL);
175 }
176
177 NewGifBitSize = GifBitSize(CrntSlot);
178 RoundUpTo = (1 << NewGifBitSize);
179
180 if (RoundUpTo != ColorUnion->ColorCount) {
181 register GifColorType *Map = ColorUnion->Colors;
182
183 /*
184 * Zero out slots up to next power of 2.
185 * We know these slots exist because of the way ColorUnion's
186 * start dimension was computed.
187 */
188 for (j = CrntSlot; j < RoundUpTo; j++)
189 Map[j].Red = Map[j].Green = Map[j].Blue = 0;
190
191 /* perhaps we can shrink the map? */
192 if (RoundUpTo < ColorUnion->ColorCount)
193 ColorUnion->Colors = (GifColorType *)realloc(Map,
194 sizeof(GifColorType) * RoundUpTo);
195 }
196
197 ColorUnion->ColorCount = RoundUpTo;
198 ColorUnion->BitsPerPixel = NewGifBitSize;
199
200 return (ColorUnion);
201}
202
203/*******************************************************************************
204 Apply a given color translation to the raster bits of an image
205*******************************************************************************/
206void
207GifApplyTranslation(SavedImage *Image, GifPixelType Translation[])
208{
209 register int i;
210 register int RasterSize = Image->ImageDesc.Height * Image->ImageDesc.Width;
211
212 for (i = 0; i < RasterSize; i++)
213 Image->RasterBits[i] = Translation[Image->RasterBits[i]];
214}
215
216/******************************************************************************
217 Extension record functions
218******************************************************************************/
219int
220GifAddExtensionBlock(int *ExtensionBlockCount,
221 ExtensionBlock **ExtensionBlocks,
222 int Function,
223 unsigned int Len,
224 unsigned char ExtData[])
225{
226 ExtensionBlock *ep;
227
228 if (*ExtensionBlocks == NULL)
229 *ExtensionBlocks=(ExtensionBlock *)malloc(sizeof(ExtensionBlock));
230 else
231 *ExtensionBlocks = (ExtensionBlock *)realloc(*ExtensionBlocks,
232 sizeof(ExtensionBlock) *
233 (*ExtensionBlockCount + 1));
234
235 if (*ExtensionBlocks == NULL)
236 return (GIF_ERROR);
237
238 ep = &(*ExtensionBlocks)[(*ExtensionBlockCount)++];
239
240 ep->Function = Function;
241 ep->ByteCount=Len;
242 ep->Bytes = (GifByteType *)malloc(ep->ByteCount);
243 if (ep->Bytes == NULL)
244 return (GIF_ERROR);
245
246 if (ExtData != NULL) {
247 memcpy(ep->Bytes, ExtData, Len);
248 }
249
250 return (GIF_OK);
251}
252
253void
254GifFreeExtensions(int *ExtensionBlockCount,
255 ExtensionBlock **ExtensionBlocks)
256{
257 ExtensionBlock *ep;
258
259 if (*ExtensionBlocks == NULL)
260 return;
261
262 for (ep = *ExtensionBlocks;
263 ep < (*ExtensionBlocks + *ExtensionBlockCount);
264 ep++)
265 (void)free((char *)ep->Bytes);
266 (void)free((char *)*ExtensionBlocks);
267 *ExtensionBlocks = NULL;
268 *ExtensionBlockCount = 0;
269}
270
271/******************************************************************************
272 Image block allocation functions
273******************************************************************************/
274
275/* Private Function:
276 * Frees the last image in the GifFile->SavedImages array
277 */
278void
279FreeLastSavedImage(GifFileType *GifFile)
280{
281 SavedImage *sp;
282
283 if ((GifFile == NULL) || (GifFile->SavedImages == NULL))
284 return;
285
286 /* Remove one SavedImage from the GifFile */
287 GifFile->ImageCount--;
288 sp = &GifFile->SavedImages[GifFile->ImageCount];
289
290 /* Deallocate its Colormap */
291 if (sp->ImageDesc.ColorMap != NULL) {
292 GifFreeMapObject(sp->ImageDesc.ColorMap);
293 sp->ImageDesc.ColorMap = NULL;
294 }
295
296 /* Deallocate the image data */
297 if (sp->RasterBits != NULL)
298 free((char *)sp->RasterBits);
299
300 /* Deallocate any extensions */
301 GifFreeExtensions(&sp->ExtensionBlockCount, &sp->ExtensionBlocks);
302
303 /*** FIXME: We could realloc the GifFile->SavedImages structure but is
304 * there a point to it? Saves some memory but we'd have to do it every
305 * time. If this is used in GifFreeSavedImages then it would be inefficient
306 * (The whole array is going to be deallocated.) If we just use it when
307 * we want to free the last Image it's convenient to do it here.
308 */
309}
310
311/*
312 * Append an image block to the SavedImages array
313 */
314SavedImage *
315GifMakeSavedImage(GifFileType *GifFile, const SavedImage *CopyFrom)
316{
317 SavedImage *sp;
318
319 if (GifFile->SavedImages == NULL)
320 GifFile->SavedImages = (SavedImage *)malloc(sizeof(SavedImage));
321 else
322 GifFile->SavedImages = (SavedImage *)realloc(GifFile->SavedImages,
323 sizeof(SavedImage) * (GifFile->ImageCount + 1));
324
325 if (GifFile->SavedImages == NULL)
326 return ((SavedImage *)NULL);
327 else {
328 sp = &GifFile->SavedImages[GifFile->ImageCount++];
329 memset((char *)sp, '\0', sizeof(SavedImage));
330
331 if (CopyFrom != NULL) {
332 memcpy((char *)sp, CopyFrom, sizeof(SavedImage));
333
334 /*
335 * Make our own allocated copies of the heap fields in the
336 * copied record. This guards against potential aliasing
337 * problems.
338 */
339
340 /* first, the local color map */
341 if (sp->ImageDesc.ColorMap != NULL) {
342 sp->ImageDesc.ColorMap = GifMakeMapObject(
343 CopyFrom->ImageDesc.ColorMap->ColorCount,
344 CopyFrom->ImageDesc.ColorMap->Colors);
345 if (sp->ImageDesc.ColorMap == NULL) {
346 FreeLastSavedImage(GifFile);
347 return (SavedImage *)(NULL);
348 }
349 }
350
351 /* next, the raster */
352 sp->RasterBits = (unsigned char *)malloc(sizeof(GifPixelType) *
353 CopyFrom->ImageDesc.Height *
354 CopyFrom->ImageDesc.Width);
355 if (sp->RasterBits == NULL) {
356 FreeLastSavedImage(GifFile);
357 return (SavedImage *)(NULL);
358 }
359 memcpy(sp->RasterBits, CopyFrom->RasterBits,
360 sizeof(GifPixelType) * CopyFrom->ImageDesc.Height *
361 CopyFrom->ImageDesc.Width);
362
363 /* finally, the extension blocks */
364 if (sp->ExtensionBlocks != NULL) {
365 sp->ExtensionBlocks = (ExtensionBlock *)malloc(
366 sizeof(ExtensionBlock) *
367 CopyFrom->ExtensionBlockCount);
368 if (sp->ExtensionBlocks == NULL) {
369 FreeLastSavedImage(GifFile);
370 return (SavedImage *)(NULL);
371 }
372 memcpy(sp->ExtensionBlocks, CopyFrom->ExtensionBlocks,
373 sizeof(ExtensionBlock) * CopyFrom->ExtensionBlockCount);
374 }
375 }
376
377 return (sp);
378 }
379}
380
381void
382GifFreeSavedImages(GifFileType *GifFile)
383{
384 SavedImage *sp;
385
386 if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) {
387 return;
388 }
389 for (sp = GifFile->SavedImages;
390 sp < GifFile->SavedImages + GifFile->ImageCount; sp++) {
391 if (sp->ImageDesc.ColorMap != NULL) {
392 GifFreeMapObject(sp->ImageDesc.ColorMap);
393 sp->ImageDesc.ColorMap = NULL;
394 }
395
396 if (sp->RasterBits != NULL)
397 free((char *)sp->RasterBits);
398
399 GifFreeExtensions(&sp->ExtensionBlockCount, &sp->ExtensionBlocks);
400 }
401 free((char *)GifFile->SavedImages);
402 GifFile->SavedImages = NULL;
403}
404
405/* end */
diff --git a/apps/plugins/imageviewer/gif/rb_glue.h b/apps/plugins/imageviewer/gif/rb_glue.h
new file mode 100644
index 0000000000..6cd46b0e56
--- /dev/null
+++ b/apps/plugins/imageviewer/gif/rb_glue.h
@@ -0,0 +1,44 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (c) 2012 Marcin Bukat
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
15 *
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
18 *
19 ****************************************************************************/
20
21#include "plugin.h"
22#include <tlsf.h>
23
24#undef memset
25#define memset(a,b,c) rb->memset((a),(b),(c))
26#undef memmove
27#define memmove(a,b,c) rb->memmove((a),(b),(c))
28#undef memcmp
29#define memcmp(a,b,c) rb->memcmp((a),(b),(c))
30#undef strncmp
31#define strncmp(a,b,c) rb->strncmp((a),(b),(c))
32
33#define fread(ptr, size, nmemb, stream) rb->read(stream, ptr, size*nmemb)
34#define fclose(stream) rb->close(stream)
35#define fdopen(a,b) ((a))
36
37#define malloc(a) tlsf_malloc((a))
38#define free(a) tlsf_free((a))
39#define realloc(a, b) tlsf_realloc((a),(b))
40#define calloc(a,b) tlsf_calloc((a),(b))
41
42#ifndef SIZE_MAX
43#define SIZE_MAX INT_MAX
44#endif
diff --git a/apps/plugins/imageviewer/image_decoder.c b/apps/plugins/imageviewer/image_decoder.c
index 416bda9e42..eab1c01dbc 100644
--- a/apps/plugins/imageviewer/image_decoder.c
+++ b/apps/plugins/imageviewer/image_decoder.c
@@ -28,8 +28,9 @@ static const char *decoder_names[MAX_IMAGE_TYPES] = {
28 "jpeg", 28 "jpeg",
29 "png", 29 "png",
30#ifdef HAVE_LCD_COLOR 30#ifdef HAVE_LCD_COLOR
31 "ppm" 31 "ppm",
32#endif 32#endif
33 "gif"
33}; 34};
34 35
35/* Check file type by magic number or file extension 36/* Check file type by magic number or file extension
@@ -53,6 +54,7 @@ enum image_type get_image_type(const char *name, bool quiet)
53#ifdef HAVE_LCD_COLOR 54#ifdef HAVE_LCD_COLOR
54 { ".ppm", IMAGE_PPM }, 55 { ".ppm", IMAGE_PPM },
55#endif 56#endif
57 { ".gif", IMAGE_GIF },
56 }; 58 };
57 static const struct { 59 static const struct {
58 char *magic; /* magic number */ 60 char *magic; /* magic number */
@@ -66,6 +68,8 @@ enum image_type get_image_type(const char *name, bool quiet)
66 { "P3", 2, IMAGE_PPM }, 68 { "P3", 2, IMAGE_PPM },
67 { "P6", 2, IMAGE_PPM }, 69 { "P6", 2, IMAGE_PPM },
68#endif 70#endif
71 { "GIF87a", 6, IMAGE_GIF },
72 { "GIF89a", 6, IMAGE_GIF },
69 }; 73 };
70 74
71 enum image_type type = IMAGE_UNKNOWN; 75 enum image_type type = IMAGE_UNKNOWN;
diff --git a/apps/plugins/imageviewer/image_decoder.h b/apps/plugins/imageviewer/image_decoder.h
index 3267d2af6e..c27ed33e8e 100644
--- a/apps/plugins/imageviewer/image_decoder.h
+++ b/apps/plugins/imageviewer/image_decoder.h
@@ -32,6 +32,7 @@ enum image_type {
32#ifdef HAVE_LCD_COLOR 32#ifdef HAVE_LCD_COLOR
33 IMAGE_PPM, 33 IMAGE_PPM,
34#endif 34#endif
35 IMAGE_GIF,
35 MAX_IMAGE_TYPES 36 MAX_IMAGE_TYPES
36}; 37};
37 38
diff --git a/apps/plugins/imageviewer/imageviewer.c b/apps/plugins/imageviewer/imageviewer.c
index 044c835d00..39507a1fcf 100644
--- a/apps/plugins/imageviewer/imageviewer.c
+++ b/apps/plugins/imageviewer/imageviewer.c
@@ -550,17 +550,38 @@ static void pan_view_down(struct image_info *info)
550/* interactively scroll around the image */ 550/* interactively scroll around the image */
551static int scroll_bmp(struct image_info *info) 551static int scroll_bmp(struct image_info *info)
552{ 552{
553 static long ss_timeout = 0;
554
553 int button; 555 int button;
554#if defined(IMGVIEW_ZOOM_PRE) || defined(IMGVIEW_MENU_PRE) 556#if defined(IMGVIEW_ZOOM_PRE) || defined(IMGVIEW_MENU_PRE)
555 int lastbutton = BUTTON_NONE; 557 int lastbutton = BUTTON_NONE;
556#endif 558#endif
557 559
560 if (!ss_timeout && iv_api.slideshow_enabled)
561 ss_timeout = *rb->current_tick + settings.ss_timeout * HZ;
562
558 while (true) 563 while (true)
559 { 564 {
560 if (iv_api.slideshow_enabled) 565 if (iv_api.slideshow_enabled)
561 button = rb->button_get_w_tmo(settings.ss_timeout * HZ); 566 {
567 if (info->frames_count > 1 && info->delay &&
568 settings.ss_timeout * HZ > info->delay)
569 {
570 /* animated content and delay between subsequent frames
571 * is shorter then slideshow delay
572 */
573 button = rb->button_get_w_tmo(info->delay);
574 }
575 else
576 button = rb->button_get_w_tmo(settings.ss_timeout * HZ);
577 }
562 else 578 else
563 button = rb->button_get(true); 579 {
580 if (info->frames_count > 1 && info->delay)
581 button = rb->button_get_w_tmo(info->delay);
582 else
583 button = rb->button_get(true);
584 }
564 585
565 iv_api.running_slideshow = false; 586 iv_api.running_slideshow = false;
566 587
@@ -595,9 +616,28 @@ static int scroll_bmp(struct image_info *info)
595 case BUTTON_NONE: 616 case BUTTON_NONE:
596 if (iv_api.slideshow_enabled && entries > 1) 617 if (iv_api.slideshow_enabled && entries > 1)
597 { 618 {
598 iv_api.running_slideshow = true; 619 if (info->frames_count > 1)
599 return change_filename(DIR_NEXT); 620 {
621 /* animations */
622 if (TIME_AFTER(*rb->current_tick, ss_timeout))
623 {
624 iv_api.running_slideshow = true;
625 ss_timeout = 0;
626 return change_filename(DIR_NEXT);
627 }
628 else
629 return NEXT_FRAME;
630 }
631 else
632 {
633 /* still picture */
634 iv_api.running_slideshow = true;
635 return change_filename(DIR_NEXT);
636 }
600 } 637 }
638 else
639 return NEXT_FRAME;
640
601 break; 641 break;
602 642
603#ifdef IMGVIEW_SLIDE_SHOW 643#ifdef IMGVIEW_SLIDE_SHOW
@@ -838,9 +878,11 @@ static int load_and_show(char* filename, struct image_info *info)
838 cx = info->x_size/ds/2; /* center the view */ 878 cx = info->x_size/ds/2; /* center the view */
839 cy = info->y_size/ds/2; 879 cy = info->y_size/ds/2;
840 880
881 /* used to loop through subimages in animated gifs */
882 int frame = 0;
841 do /* loop the image prepare and decoding when zoomed */ 883 do /* loop the image prepare and decoding when zoomed */
842 { 884 {
843 status = imgdec->get_image(info, ds); /* decode or fetch from cache */ 885 status = imgdec->get_image(info, frame, ds); /* decode or fetch from cache */
844 if (status == PLUGIN_ERROR) 886 if (status == PLUGIN_ERROR)
845 { 887 {
846 file_pt[curfile] = NULL; 888 file_pt[curfile] = NULL;
@@ -849,7 +891,7 @@ static int load_and_show(char* filename, struct image_info *info)
849 891
850 set_view(info, cx, cy); 892 set_view(info, cx, cy);
851 893
852 if(!iv_api.running_slideshow) 894 if(!iv_api.running_slideshow && (info->frames_count == 1))
853 { 895 {
854 rb->lcd_putsf(0, 3, "showing %dx%d", info->width, info->height); 896 rb->lcd_putsf(0, 3, "showing %dx%d", info->width, info->height);
855 rb->lcd_update(); 897 rb->lcd_update();
@@ -870,6 +912,7 @@ static int load_and_show(char* filename, struct image_info *info)
870 while (1) 912 while (1)
871 { 913 {
872 status = scroll_bmp(info); 914 status = scroll_bmp(info);
915
873 if (status == ZOOM_IN) 916 if (status == ZOOM_IN)
874 { 917 {
875 if (ds > ds_min || (imgdec->unscaled_avail && ds > 1)) 918 if (ds > ds_min || (imgdec->unscaled_avail && ds > 1))
@@ -899,16 +942,19 @@ static int load_and_show(char* filename, struct image_info *info)
899 else 942 else
900 continue; 943 continue;
901 } 944 }
945
946 /* next frame in animated content */
947 if (status == NEXT_FRAME)
948 frame = (frame + 1)%info->frames_count;
949
902 break; 950 break;
903 } 951 }
904 952
905#ifdef USEGSLIB
906 grey_show(false); /* switch off overlay */
907#endif
908 rb->lcd_clear_display(); 953 rb->lcd_clear_display();
909 } 954 }
910 while (status > PLUGIN_OTHER); 955 while (status > PLUGIN_OTHER);
911#ifdef USEGSLIB 956#ifdef USEGSLIB
957 grey_show(false); /* switch off overlay */
912 rb->lcd_update(); 958 rb->lcd_update();
913#endif 959#endif
914 return status; 960 return status;
diff --git a/apps/plugins/imageviewer/imageviewer.h b/apps/plugins/imageviewer/imageviewer.h
index 9cc1a9117a..19b5db15bb 100644
--- a/apps/plugins/imageviewer/imageviewer.h
+++ b/apps/plugins/imageviewer/imageviewer.h
@@ -58,6 +58,7 @@ enum {
58 58
59 ZOOM_IN, 59 ZOOM_IN,
60 ZOOM_OUT, 60 ZOOM_OUT,
61 NEXT_FRAME,
61}; 62};
62 63
63#if (CONFIG_PLATFORM & PLATFORM_NATIVE) && defined(HAVE_DISK_STORAGE) 64#if (CONFIG_PLATFORM & PLATFORM_NATIVE) && defined(HAVE_DISK_STORAGE)
@@ -82,6 +83,8 @@ struct image_info {
82 int x_size, y_size; /* set size of loaded image in load_image(). */ 83 int x_size, y_size; /* set size of loaded image in load_image(). */
83 int width, height; /* set size of resized image in get_image(). */ 84 int width, height; /* set size of resized image in get_image(). */
84 int x, y; /* display position */ 85 int x, y; /* display position */
86 int frames_count; /* number of subframes */
87 int delay; /* delay expressed in ticks between frames */
85 void *data; /* use freely in decoder. not touched in ui. */ 88 void *data; /* use freely in decoder. not touched in ui. */
86}; 89};
87 90
@@ -115,6 +118,7 @@ struct image_decoder {
115 /* return needed size of buffer to store downscaled image by ds. 118 /* return needed size of buffer to store downscaled image by ds.
116 * this is used to calculate min downscale. */ 119 * this is used to calculate min downscale. */
117 int (*img_mem)(int ds); 120 int (*img_mem)(int ds);
121
118 /* load image from filename. use the passed buffer to store loaded, decoded 122 /* load image from filename. use the passed buffer to store loaded, decoded
119 * or resized image later, so save it to local variables if needed. 123 * or resized image later, so save it to local variables if needed.
120 * set width and height of info properly. also, set buf_size to remaining 124 * set width and height of info properly. also, set buf_size to remaining
@@ -125,7 +129,8 @@ struct image_decoder {
125 /* downscale loaded image by ds. use the buffer passed to load_image to 129 /* downscale loaded image by ds. use the buffer passed to load_image to
126 * reszie image and/or store resized image. 130 * reszie image and/or store resized image.
127 * return PLUGIN_ERROR for error. ui will skip to next image. */ 131 * return PLUGIN_ERROR for error. ui will skip to next image. */
128 int (*get_image)(struct image_info *info, int ds); 132 int (*get_image)(struct image_info *info, int frame, int ds);
133
129 /* draw part of image */ 134 /* draw part of image */
130 void (*draw_image_rect)(struct image_info *info, 135 void (*draw_image_rect)(struct image_info *info,
131 int x, int y, int width, int height); 136 int x, int y, int width, int height);
diff --git a/apps/plugins/imageviewer/jpeg/jpeg.c b/apps/plugins/imageviewer/jpeg/jpeg.c
index 511a7054e1..3d8c9c3ac8 100644
--- a/apps/plugins/imageviewer/jpeg/jpeg.c
+++ b/apps/plugins/imageviewer/jpeg/jpeg.c
@@ -190,8 +190,9 @@ static int load_image(char *filename, struct image_info *info,
190 return PLUGIN_OK; 190 return PLUGIN_OK;
191} 191}
192 192
193static int get_image(struct image_info *info, int ds) 193static int get_image(struct image_info *info, int frame, int ds)
194{ 194{
195 (void)frame;
195 int w, h; /* used to center output */ 196 int w, h; /* used to center output */
196 int size; /* decompressed image size */ 197 int size; /* decompressed image size */
197 long time; /* measured ticks */ 198 long time; /* measured ticks */
diff --git a/apps/plugins/imageviewer/png/png.c b/apps/plugins/imageviewer/png/png.c
index 1916f9c55f..29b6713585 100644
--- a/apps/plugins/imageviewer/png/png.c
+++ b/apps/plugins/imageviewer/png/png.c
@@ -226,8 +226,9 @@ static int load_image(char *filename, struct image_info *info,
226 return PLUGIN_OK; 226 return PLUGIN_OK;
227} 227}
228 228
229static int get_image(struct image_info *info, int ds) 229static int get_image(struct image_info *info, int frame, int ds)
230{ 230{
231 (void)frame;
231 unsigned char **p_disp = &disp[ds]; /* short cut */ 232 unsigned char **p_disp = &disp[ds]; /* short cut */
232 LodePNG_Decoder *p_decoder = &decoder; 233 LodePNG_Decoder *p_decoder = &decoder;
233 234
diff --git a/apps/plugins/imageviewer/ppm/ppm.c b/apps/plugins/imageviewer/ppm/ppm.c
index 20200d812e..5e03f8c335 100644
--- a/apps/plugins/imageviewer/ppm/ppm.c
+++ b/apps/plugins/imageviewer/ppm/ppm.c
@@ -150,8 +150,9 @@ static int load_image(char *filename, struct image_info *info,
150 return PLUGIN_OK; 150 return PLUGIN_OK;
151} 151}
152 152
153static int get_image(struct image_info *info, int ds) 153static int get_image(struct image_info *info, int frame, int ds)
154{ 154{
155 (void)frame;
155 unsigned char **p_disp = &disp[ds]; /* short cut */ 156 unsigned char **p_disp = &disp[ds]; /* short cut */
156 struct ppm_info *p_ppm = &ppm; 157 struct ppm_info *p_ppm = &ppm;
157 158
diff --git a/apps/plugins/viewers.config b/apps/plugins/viewers.config
index a7aec614e1..24c888b956 100644
--- a/apps/plugins/viewers.config
+++ b/apps/plugins/viewers.config
@@ -23,6 +23,7 @@ png,viewers/imageviewer,2
23#ifdef HAVE_LCD_COLOR 23#ifdef HAVE_LCD_COLOR
24ppm,viewers/imageviewer,2 24ppm,viewers/imageviewer,2
25#endif 25#endif
26gif,viewers/imageviewer,2
26ucl,viewers/rockbox_flash,3 27ucl,viewers/rockbox_flash,3
27rvf,viewers/video,4 28rvf,viewers/video,4
28mp3,viewers/vbrfix,5 29mp3,viewers/vbrfix,5