diff options
author | Marcin Bukat <marcin.bukat@gmail.com> | 2012-11-02 13:03:58 +0100 |
---|---|---|
committer | Marcin Bukat <marcin.bukat@gmail.com> | 2012-11-13 18:13:10 +0100 |
commit | 0ceaff2b65c50b75ad8cc5b2d12e7b3f864092e5 (patch) | |
tree | 49d8d297cbba93902bc612a9aa4ded1b6e2d46e5 | |
parent | b35f82c91ff050b4405b19a3e56e9d031bf940e2 (diff) | |
download | rockbox-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
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 | |||
70 | pitch_detector,apps | 70 | pitch_detector,apps |
71 | plasma,demos | 71 | plasma,demos |
72 | png,viewers | 72 | png,viewers |
73 | gif,viewers | ||
73 | pong,games | 74 | pong,games |
74 | ppm,viewers | 75 | ppm,viewers |
75 | properties,viewers | 76 | properties,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 |
5 | ppm | 5 | ppm |
6 | #endif | 6 | #endif |
7 | gif | ||
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 | ||
233 | static int get_image(struct image_info *info, int ds) | 233 | static 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 | ||
2 | dgif_lib.c | ||
3 | gifalloc.c | ||
4 | gif_decoder.c | ||
5 | gif.c | ||
6 | gif_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 | |||
3 | dgif_lib.c - GIF decoding | ||
4 | |||
5 | The functions here and in egif_lib.c are partitioned carefully so that | ||
6 | if you only require one of read and write capability, only one of these | ||
7 | two 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 | |||
35 | static int DGifGetWord(GifFileType *GifFile, GifWord *Word); | ||
36 | static int DGifSetupDecompress(GifFileType *GifFile); | ||
37 | static int DGifDecompressLine(GifFileType *GifFile, GifPixelType *Line, | ||
38 | int LineLen); | ||
39 | static int DGifGetPrefixChar(GifPrefixType *Prefix, int Code, int ClearCode); | ||
40 | static int DGifDecompressInput(GifFileType *GifFile, int *Code); | ||
41 | static 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 | ******************************************************************************/ | ||
49 | GifFileType * | ||
50 | DGifOpenFileName(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 | ******************************************************************************/ | ||
71 | GifFileType * | ||
72 | DGifOpenFileHandle(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 | ******************************************************************************/ | ||
155 | GifFileType * | ||
156 | DGifOpen(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 | ******************************************************************************/ | ||
228 | int | ||
229 | DGifGetScreenDesc(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 | ******************************************************************************/ | ||
290 | int | ||
291 | DGifGetRecordType(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 | ******************************************************************************/ | ||
326 | int | ||
327 | DGifGetImageDesc(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 | ******************************************************************************/ | ||
428 | int | ||
429 | DGifGetLine(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 | ******************************************************************************/ | ||
468 | int | ||
469 | DGifGetPixel(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 | ******************************************************************************/ | ||
509 | int | ||
510 | DGifGetExtension(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 | ******************************************************************************/ | ||
535 | int | ||
536 | DGifGetExtensionNext(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 | |||
563 | int 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 | |||
586 | int 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 | ******************************************************************************/ | ||
611 | int | ||
612 | DGifCloseFile(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 | ******************************************************************************/ | ||
670 | static int | ||
671 | DGifGetWord(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 | ******************************************************************************/ | ||
691 | int | ||
692 | DGifGetCode(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 | ******************************************************************************/ | ||
712 | int | ||
713 | DGifGetCodeNext(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 | ******************************************************************************/ | ||
745 | static int | ||
746 | DGifSetupDecompress(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 | ******************************************************************************/ | ||
781 | static int | ||
782 | DGifDecompressLine(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 | ******************************************************************************/ | ||
907 | static int | ||
908 | DGifGetPrefixChar(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 | ******************************************************************************/ | ||
925 | int | ||
926 | DGifGetLZCodes(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 | ******************************************************************************/ | ||
964 | static int | ||
965 | DGifDecompressInput(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 | ******************************************************************************/ | ||
1018 | static int | ||
1019 | DGifBufferedInput(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 | *******************************************************************************/ | ||
1064 | int | ||
1065 | DGifSlurp(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 */ | ||
30 | static struct gif_decoder decoder; | ||
31 | |||
32 | static 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 */ | ||
36 | static unsigned char *disp[GIF_MAX_FRAMES][9]; | ||
37 | static 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'". */ | ||
47 | GREY_INFO_STRUCT | ||
48 | #endif /* USEGSLIB */ | ||
49 | |||
50 | static 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 | |||
70 | static int img_mem(int ds) | ||
71 | { | ||
72 | struct gif_decoder *p_decoder = &decoder; | ||
73 | return p_decoder->native_img_size/ds; | ||
74 | } | ||
75 | |||
76 | static 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 | |||
165 | static 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 | |||
233 | const struct image_decoder image_decoder = { | ||
234 | true, | ||
235 | img_mem, | ||
236 | load_image, | ||
237 | get_image, | ||
238 | draw_image_rect, | ||
239 | }; | ||
240 | |||
241 | IMGDEC_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 | |||
10 | GIFSRCDIR := $(IMGVSRCDIR)/gif | ||
11 | GIFBUILDDIR := $(IMGVBUILDDIR)/gif | ||
12 | |||
13 | GIF_SRC := $(call preprocess, $(GIFSRCDIR)/SOURCES) | ||
14 | GIF_OBJ := $(call c2obj, $(GIF_SRC)) | ||
15 | |||
16 | OTHER_SRC += $(GIF_SRC) | ||
17 | |||
18 | ROCKS += $(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 | ||
25 | GIFFLAGS = $(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) | ||
39 | typedef struct uint8_rgb pixel_t; | ||
40 | #define NATIVE_SZ (GifFile->SWidth*GifFile->SHeight*FB_DATA_SZ) | ||
41 | #define PIXEL_TRANSPARENT 0x00 | ||
42 | #else | ||
43 | typedef 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 | |||
50 | static GifFileType *GifFile; | ||
51 | |||
52 | static 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 | |||
91 | static 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 | |||
107 | void 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 | |||
118 | void 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 | |||
128 | static 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 */ | ||
155 | void 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 | |||
23 | struct 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 | |||
34 | void gif_decoder_init(struct gif_decoder *decoder, void *mem, size_t size); | ||
35 | void gif_open(char *filename, struct gif_decoder *d); | ||
36 | void 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 | |||
3 | gif_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 | *****************************************************************************/ | ||
15 | char * | ||
16 | GifErrorString(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 | |||
3 | gif_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 | |||
28 | typedef struct GifHashTableType { | ||
29 | uint32_t HTable[HT_SIZE]; | ||
30 | } GifHashTableType; | ||
31 | |||
32 | GifHashTableType *_InitHashTable(void); | ||
33 | void _ClearHashTable(GifHashTableType *HashTable); | ||
34 | void _InsertHashTable(GifHashTableType *HashTable, uint32_t Key, int Code); | ||
35 | int _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 | |||
3 | gif_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 | ||
11 | extern "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 | |||
30 | typedef unsigned char GifPixelType; | ||
31 | typedef unsigned char *GifRowType; | ||
32 | typedef unsigned char GifByteType; | ||
33 | typedef unsigned int GifPrefixType; | ||
34 | typedef int GifWord; | ||
35 | |||
36 | /****************************************************************************** | ||
37 | GIF89 structures | ||
38 | ******************************************************************************/ | ||
39 | |||
40 | typedef 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 | |||
52 | typedef struct GifColorType { | ||
53 | GifByteType Red, Green, Blue; | ||
54 | } GifColorType; | ||
55 | |||
56 | typedef struct ColorMapObject { | ||
57 | int ColorCount; | ||
58 | int BitsPerPixel; | ||
59 | bool SortFlag; | ||
60 | GifColorType *Colors; /* on malloc(3) heap */ | ||
61 | } ColorMapObject; | ||
62 | |||
63 | typedef 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 | |||
70 | typedef 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 | |||
81 | typedef 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 | |||
88 | typedef 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 | |||
106 | typedef 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) */ | ||
115 | typedef int (*InputFunc) (GifFileType *, GifByteType *, int); | ||
116 | |||
117 | /* func type to write gif data to arbitrary targets. | ||
118 | * Returns count of bytes written. (MRB) | ||
119 | */ | ||
120 | typedef int (*OutputFunc) (GifFileType *, const GifByteType *, int); | ||
121 | |||
122 | /****************************************************************************** | ||
123 | GIF encoding routines | ||
124 | ******************************************************************************/ | ||
125 | |||
126 | /* Main entry points */ | ||
127 | GifFileType *EGifOpenFileName(const char *GifFileName, | ||
128 | const bool GifTestExistence, int *Error); | ||
129 | GifFileType *EGifOpenFileHandle(const int GifFileHandle, int *Error); | ||
130 | GifFileType *EGifOpen(void *userPtr, OutputFunc writeFunc, int *Error); | ||
131 | int EGifSpew(GifFileType * GifFile); | ||
132 | char *EGifGetGifVersion(GifFileType *GifFile); /* new in 5.x */ | ||
133 | int 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 */ | ||
147 | int EGifPutScreenDesc(GifFileType *GifFile, | ||
148 | const int GifWidth, const int GifHeight, | ||
149 | const int GifColorRes, | ||
150 | const int GifBackGround, | ||
151 | const ColorMapObject *GifColorMap); | ||
152 | int 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); | ||
157 | void EGifSetGifVersion(GifFileType *GifFile, const bool gif89); | ||
158 | int EGifPutLine(GifFileType *GifFile, GifPixelType *GifLine, | ||
159 | int GifLineLen); | ||
160 | int EGifPutPixel(GifFileType *GifFile, const GifPixelType GifPixel); | ||
161 | int EGifPutComment(GifFileType *GifFile, const char *GifComment); | ||
162 | int EGifPutExtensionLeader(GifFileType *GifFile, const int GifExtCode); | ||
163 | int EGifPutExtensionBlock(GifFileType *GifFile, | ||
164 | const int GifExtLen, const void *GifExtension); | ||
165 | int EGifPutExtensionTrailer(GifFileType *GifFile); | ||
166 | int EGifPutExtension(GifFileType *GifFile, const int GifExtCode, | ||
167 | const int GifExtLen, | ||
168 | const void *GifExtension); | ||
169 | int EGifPutCode(GifFileType *GifFile, int GifCodeSize, | ||
170 | const GifByteType *GifCodeBlock); | ||
171 | int EGifPutCodeNext(GifFileType *GifFile, | ||
172 | const GifByteType *GifCodeBlock); | ||
173 | |||
174 | /****************************************************************************** | ||
175 | GIF decoding routines | ||
176 | ******************************************************************************/ | ||
177 | |||
178 | /* Main entry points */ | ||
179 | GifFileType *DGifOpenFileName(const char *GifFileName, int *Error); | ||
180 | GifFileType *DGifOpenFileHandle(int GifFileHandle, int *Error); | ||
181 | int DGifSlurp(GifFileType * GifFile); | ||
182 | GifFileType *DGifOpen(void *userPtr, InputFunc readFunc, int *Error); /* new one (TVT) */ | ||
183 | int 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 */ | ||
200 | int DGifGetScreenDesc(GifFileType *GifFile); | ||
201 | int DGifGetRecordType(GifFileType *GifFile, GifRecordType *GifType); | ||
202 | int DGifGetImageDesc(GifFileType *GifFile); | ||
203 | int DGifGetLine(GifFileType *GifFile, GifPixelType *GifLine, int GifLineLen); | ||
204 | int DGifGetPixel(GifFileType *GifFile, GifPixelType GifPixel); | ||
205 | int DGifGetComment(GifFileType *GifFile, char *GifComment); | ||
206 | int DGifGetExtension(GifFileType *GifFile, int *GifExtCode, | ||
207 | GifByteType **GifExtension); | ||
208 | int DGifGetExtensionNext(GifFileType *GifFile, GifByteType **GifExtension); | ||
209 | int DGifGetCode(GifFileType *GifFile, int *GifCodeSize, | ||
210 | GifByteType **GifCodeBlock); | ||
211 | int DGifGetCodeNext(GifFileType *GifFile, GifByteType **GifCodeBlock); | ||
212 | int DGifGetLZCodes(GifFileType *GifFile, int *GifCode); | ||
213 | |||
214 | |||
215 | /****************************************************************************** | ||
216 | Color table quantization (deprecated) | ||
217 | ******************************************************************************/ | ||
218 | int 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 | ******************************************************************************/ | ||
227 | extern 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 | |||
238 | extern ColorMapObject *GifMakeMapObject(int ColorCount, | ||
239 | const GifColorType *ColorMap); | ||
240 | extern void GifFreeMapObject(ColorMapObject *Object); | ||
241 | extern ColorMapObject *GifUnionColorMap(const ColorMapObject *ColorIn1, | ||
242 | const ColorMapObject *ColorIn2, | ||
243 | GifPixelType ColorTransIn2[]); | ||
244 | extern int GifBitSize(int n); | ||
245 | |||
246 | /****************************************************************************** | ||
247 | Support for the in-core structures allocation (slurp mode). | ||
248 | ******************************************************************************/ | ||
249 | |||
250 | extern void GifApplyTranslation(SavedImage *Image, GifPixelType Translation[]); | ||
251 | extern int GifAddExtensionBlock(int *ExtensionBlock_Count, | ||
252 | ExtensionBlock **ExtensionBlocks, | ||
253 | int Function, | ||
254 | unsigned int Len, unsigned char ExtData[]); | ||
255 | extern void GifFreeExtensions(int *ExtensionBlock_Count, | ||
256 | ExtensionBlock **ExtensionBlocks); | ||
257 | extern SavedImage *GifMakeSavedImage(GifFileType *GifFile, | ||
258 | const SavedImage *CopyFrom); | ||
259 | extern void GifFreeSavedImages(GifFileType *GifFile); | ||
260 | |||
261 | /****************************************************************************** | ||
262 | 5.x functions for GIF89 graphics control blocks | ||
263 | ******************************************************************************/ | ||
264 | |||
265 | int DGifExtensionToGCB(const size_t GifExtensionLength, | ||
266 | const GifByteType *GifExtension, | ||
267 | GraphicsControlBlock *GCB); | ||
268 | size_t EGifGCBToExtension(const GraphicsControlBlock *GCB, | ||
269 | GifByteType *GifExtension); | ||
270 | |||
271 | int DGifSavedExtensionToGCB(GifFileType *GifFile, | ||
272 | int ImageIndex, | ||
273 | GraphicsControlBlock *GCB); | ||
274 | int 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 | ||
284 | extern const unsigned char GifAsciiTable8x8[][GIF_FONT_WIDTH]; | ||
285 | |||
286 | extern void GifDrawText8x8(SavedImage *Image, | ||
287 | const int x, const int y, | ||
288 | const char *legend, const int color); | ||
289 | |||
290 | extern void GifDrawBox(SavedImage *Image, | ||
291 | const int x, const int y, | ||
292 | const int w, const int d, const int color); | ||
293 | |||
294 | extern void GifDrawRectangle(SavedImage *Image, | ||
295 | const int x, const int y, | ||
296 | const int w, const int d, const int color); | ||
297 | |||
298 | extern 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 | |||
3 | gif_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 | |||
32 | typedef 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 */ | ||
22 | int | ||
23 | GifBitSize(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 | */ | ||
41 | ColorMapObject * | ||
42 | GifMakeMapObject(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 | /******************************************************************************* | ||
75 | Free a color map object | ||
76 | *******************************************************************************/ | ||
77 | void | ||
78 | GifFreeMapObject(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 | ||
88 | void | ||
89 | DumpColorMap(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 | *******************************************************************************/ | ||
115 | ColorMapObject * | ||
116 | GifUnionColorMap(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 | *******************************************************************************/ | ||
206 | void | ||
207 | GifApplyTranslation(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 | ******************************************************************************/ | ||
219 | int | ||
220 | GifAddExtensionBlock(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 | |||
253 | void | ||
254 | GifFreeExtensions(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 | */ | ||
278 | void | ||
279 | FreeLastSavedImage(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 | */ | ||
314 | SavedImage * | ||
315 | GifMakeSavedImage(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 | |||
381 | void | ||
382 | GifFreeSavedImages(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 */ |
551 | static int scroll_bmp(struct image_info *info) | 551 | static 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 | ||
193 | static int get_image(struct image_info *info, int ds) | 193 | static 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 | ||
229 | static int get_image(struct image_info *info, int ds) | 229 | static 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 | ||
153 | static int get_image(struct image_info *info, int ds) | 153 | static 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 |
24 | ppm,viewers/imageviewer,2 | 24 | ppm,viewers/imageviewer,2 |
25 | #endif | 25 | #endif |
26 | gif,viewers/imageviewer,2 | ||
26 | ucl,viewers/rockbox_flash,3 | 27 | ucl,viewers/rockbox_flash,3 |
27 | rvf,viewers/video,4 | 28 | rvf,viewers/video,4 |
28 | mp3,viewers/vbrfix,5 | 29 | mp3,viewers/vbrfix,5 |