summaryrefslogtreecommitdiff
path: root/apps/plugins/imageviewer/gif/gif_decoder.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/imageviewer/gif/gif_decoder.c')
-rw-r--r--apps/plugins/imageviewer/gif/gif_decoder.c491
1 files changed, 491 insertions, 0 deletions
diff --git a/apps/plugins/imageviewer/gif/gif_decoder.c b/apps/plugins/imageviewer/gif/gif_decoder.c
new file mode 100644
index 0000000000..ca9579aae3
--- /dev/null
+++ b/apps/plugins/imageviewer/gif/gif_decoder.c
@@ -0,0 +1,491 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (c) 2012 Marcin Bukat
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
15 *
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
18 *
19 ****************************************************************************/
20
21#include <lib/pluginlib_bmp.h>
22#include "bmp.h"
23#if LCD_DEPTH < 8
24#include <lib/grey.h>
25#endif
26
27#include "gif_lib.h"
28#include "gif_decoder.h"
29
30#ifndef resize_bitmap
31#if defined(HAVE_LCD_COLOR)
32#define resize_bitmap smooth_resize_bitmap
33#else
34#define resize_bitmap grey_resize_bitmap
35#endif
36#endif
37
38#if defined(HAVE_LCD_COLOR)
39typedef struct uint8_rgb pixel_t;
40#define NATIVE_SZ (GifFile->SWidth*GifFile->SHeight*FB_DATA_SZ)
41#define PIXEL_TRANSPARENT 0x00
42#else
43typedef unsigned char pixel_t;
44#define NATIVE_SZ (GifFile->SWidth*GifFile->SHeight)
45#define PIXEL_TRANSPARENT 0xff
46#endif
47
48#define PIXELS_SZ (GifFile->SWidth*GifFile->SHeight*sizeof(pixel_t))
49
50static GifFileType *GifFile;
51
52static void gif2pixels(GifPixelType *Line, pixel_t *out,
53 int Row, int Col, int Width)
54{
55 int x;
56#ifndef HAVE_LCD_COLOR
57 struct uint8_rgb rgb;
58#endif
59
60 GifColorType *ColorMapEntry;
61
62 /* Color map to use */
63 ColorMapObject *ColorMap = (GifFile->Image.ColorMap ?
64 GifFile->Image.ColorMap :
65 GifFile->SColorMap);
66
67 pixel_t *pixel = out + ((Row * GifFile->SWidth) + Col);
68
69 for (x = 0; x < Width; x++, pixel++)
70 {
71 ColorMapEntry = &ColorMap->Colors[Line[x]];
72
73 if (GifFile->Image.GCB &&
74 GifFile->Image.GCB->TransparentColor == Line[x])
75 continue;
76
77#ifdef HAVE_LCD_COLOR
78 pixel->red = ColorMapEntry->Red;
79 pixel->green = ColorMapEntry->Green;
80 pixel->blue = ColorMapEntry->Blue;
81#else
82 rgb.red = ColorMapEntry->Red;
83 rgb.green = ColorMapEntry->Green;
84 rgb.blue = ColorMapEntry->Blue;
85
86 *pixel = brightness(rgb);
87#endif
88 }
89}
90
91static void pixels2native(struct scaler_context *ctx,
92 pixel_t *pixels_buffer,
93 int Row)
94{
95#ifdef HAVE_LCD_COLOR
96 const struct custom_format *cformat = &format_native;
97#else
98 const struct custom_format *cformat = &format_grey;
99#endif
100
101 void (*output_row_8)(uint32_t, void*, struct scaler_context*) =
102 cformat->output_row_8;
103
104 output_row_8(Row, (void *)(pixels_buffer + (Row*ctx->bm->width)), ctx);
105}
106
107void gif_decoder_init(struct gif_decoder *d, void *mem, size_t size)
108{
109 memset(d, 0, sizeof(struct gif_decoder));
110
111 d->mem = mem;
112 d->mem_size = size;
113
114 /* mem allocator init */
115 init_memory_pool(d->mem_size, d->mem);
116}
117
118void gif_open(char *filename, struct gif_decoder *d)
119{
120 if ((GifFile = DGifOpenFileName(filename, &d->error)) == NULL)
121 return;
122
123 d->width = GifFile->SWidth;
124 d->height = GifFile->SHeight;
125 d->frames_count = 0;
126}
127
128static void set_canvas_background(GifPixelType *Line, pixel_t *out,
129 GifFileType *GifFile)
130{
131 int i;
132
133 /* Reading Gif spec it seems one should always use background color
134 * in canvas but most real files omit this and sets background color to 0
135 * (which IS valid index). We can choose to either conform to standard
136 * (and wrongly display most of gifs with transparency) or stick to
137 * common practise and treat background color 0 as transparent.
138 * I preffer the second.
139 */
140 if (GifFile->SColorMap && GifFile->SBackGroundColor != 0)
141 {
142 memset(Line, GifFile->SBackGroundColor, GifFile->SWidth);
143
144 for(i=0; i<GifFile->SHeight; i++)
145 gif2pixels(Line, out, i, 0, GifFile->SWidth);
146 }
147 else
148 {
149 memset(out, PIXEL_TRANSPARENT, PIXELS_SZ);
150 }
151
152}
153
154/* var names adhere to giflib coding style */
155void gif_decode(struct gif_decoder *d,
156 void (*pf_progress)(int current, int total))
157{
158 int i, j;
159
160 int Size;
161 int Row;
162 int Col;
163 int Width;
164 int Height;
165 int ExtCode;
166
167 GifPixelType *Line;
168
169 GifRecordType RecordType;
170 GifByteType *Extension;
171
172 unsigned char *out = NULL;
173
174 /* The way Interlaced image should
175 * be read - offsets and jumps
176 */
177 const char InterlacedOffset[] = { 0, 4, 2, 1 };
178 const char InterlacedJumps[] = { 8, 8, 4, 2 };
179
180 /* used for color conversion */
181 struct bitmap bm;
182 struct scaler_context ctx = {
183 .bm = &bm,
184 .dither = 0
185 };
186
187 /* initialize struct */
188 memset(&bm, 0, sizeof(struct bitmap));
189
190 Size = GifFile->SWidth * sizeof(GifPixelType); /* Size in bytes one row.*/
191 Line = (GifPixelType *)malloc(Size);
192 if (Line == NULL)
193 {
194 /* error allocating temp space */
195 d->error = D_GIF_ERR_NOT_ENOUGH_MEM;
196 return;
197 }
198
199 /* We use two pixel buffers if dispose method asks
200 * for restoration of the previous state.
201 * We only swap the indexes leaving data in place.
202 */
203 int buf_idx = 0;
204 pixel_t *pixels_buffer[2];
205 pixels_buffer[0] = (pixel_t *)malloc(PIXELS_SZ);
206 pixels_buffer[1] = NULL;
207
208 if (pixels_buffer[0] == NULL)
209 {
210 d->error = D_GIF_ERR_NOT_ENOUGH_MEM;
211 return;
212 }
213
214 /* Global background color */
215 set_canvas_background(Line, pixels_buffer[0], GifFile);
216
217 bm.width = GifFile->SWidth;
218 bm.height = GifFile->SHeight;
219 d->native_img_size = NATIVE_SZ;
220
221 if (pf_progress != NULL)
222 pf_progress(0, 100);
223
224 /* Scan the content of the GIF file and load the image(s) in: */
225 do
226 {
227 if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR)
228 {
229 d->error = GifFile->Error;
230 return;
231 }
232
233 switch (RecordType)
234 {
235 case IMAGE_DESC_RECORD_TYPE:
236
237 if (DGifGetImageDesc(GifFile) == GIF_ERROR)
238 {
239 d->error = GifFile->Error;
240 return;
241 }
242
243 /* Image Position relative to canvas */
244 Row = GifFile->Image.Top;
245 Col = GifFile->Image.Left;
246 Width = GifFile->Image.Width;
247 Height = GifFile->Image.Height;
248
249 /* Check Color map to use */
250 if (GifFile->Image.ColorMap == NULL &&
251 GifFile->SColorMap == NULL)
252 {
253 d->error = D_GIF_ERR_NO_COLOR_MAP;
254 return;
255 }
256
257 /* sanity check */
258 if (GifFile->Image.Left+GifFile->Image.Width>GifFile->SWidth ||
259 GifFile->Image.Top+GifFile->Image.Height>GifFile->SHeight)
260 {
261 d->error = D_GIF_ERR_DATA_TOO_BIG;
262 return;
263 }
264
265 if (GifFile->Image.GCB &&
266 GifFile->Image.GCB->DisposalMode == DISPOSE_PREVIOUS)
267 {
268 /* We need to take a snapshot before processing the image
269 * in order to restore canvas to previous state after
270 * rendering
271 */
272 buf_idx ^= 1;
273
274 if (pixels_buffer[buf_idx] == NULL)
275 pixels_buffer[buf_idx] = (pixel_t *)malloc(PIXELS_SZ);
276 }
277
278 if (GifFile->Image.Interlace)
279 {
280 /* Need to perform 4 passes on the image */
281 for (i = 0; i < 4; i++)
282 {
283 for (j = Row + InterlacedOffset[i];
284 j < Row + Height;
285 j += InterlacedJumps[i])
286 {
287 if (DGifGetLine(GifFile, Line, Width) == GIF_ERROR)
288 {
289 d->error = GifFile->Error;
290 return;
291 }
292
293 gif2pixels(Line, pixels_buffer[buf_idx],
294 Row + j, Col, Width);
295 }
296
297 pf_progress(25*(i+1), 100);
298 }
299 }
300 else
301 {
302 for (i = 0; i < Height; i++)
303 {
304 /* load single line into buffer */
305 if (DGifGetLine(GifFile, Line, Width) == GIF_ERROR)
306 {
307 d->error = GifFile->Error;
308 return;
309 }
310
311 gif2pixels(Line, pixels_buffer[buf_idx],
312 Row + i, Col, Width);
313
314 pf_progress(100*(i+1)/Height, 100);
315 }
316 }
317
318 /* allocate space for new frame */
319 out = realloc(out, d->native_img_size*(d->frames_count + 1));
320 if (out == NULL)
321 {
322 d->error = D_GIF_ERR_NOT_ENOUGH_MEM;
323 return;
324 }
325
326 bm.data = out + d->native_img_size*d->frames_count;
327
328 /* animated gif */
329 if (GifFile->Image.GCB && GifFile->Image.GCB->DelayTime != 0)
330 {
331 for (i=0; i < ctx.bm->height; i++)
332 pixels2native(&ctx, (void *)pixels_buffer[buf_idx], i);
333
334 /* restore to the background color */
335 switch (GifFile->Image.GCB->DisposalMode)
336 {
337 case DISPOSE_BACKGROUND:
338 set_canvas_background(Line, pixels_buffer[buf_idx],
339 GifFile);
340 break;
341
342 case DISPOSE_PREVIOUS:
343 buf_idx ^= 1;
344 break;
345
346 default:
347 /* DISPOSAL_UNSPECIFIED
348 * DISPOSE_DO_NOT
349 */
350 break;
351 }
352
353 d->frames_count++;
354
355 if (d->frames_count > GIF_MAX_FRAMES)
356 {
357 d->error = D_GIF_ERR_NOT_ENOUGH_MEM;
358 return;
359 }
360 }
361
362 break;
363
364 case EXTENSION_RECORD_TYPE:
365 if (DGifGetExtension(GifFile, &ExtCode, &Extension) ==
366 GIF_ERROR)
367 {
368 d->error = GifFile->Error;
369 return;
370 }
371
372 if (ExtCode == GRAPHICS_EXT_FUNC_CODE)
373 {
374 if (GifFile->Image.GCB == NULL)
375 GifFile->Image.GCB = (GraphicsControlBlock *)
376 malloc(sizeof(GraphicsControlBlock));
377
378 if (DGifExtensionToGCB(Extension[0],
379 Extension + 1,
380 GifFile->Image.GCB) == GIF_ERROR)
381 {
382 d->error = GifFile->Error;
383 return;
384 }
385 d->delay = GifFile->Image.GCB->DelayTime;
386 }
387
388 /* Skip anything else */
389 while (Extension != NULL)
390 {
391 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR)
392 {
393 d->error = GifFile->Error;
394 return;
395 }
396 }
397 break;
398
399 /* including TERMINATE_RECORD_TYPE */
400 default:
401 break;
402 }
403
404 } while (RecordType != TERMINATE_RECORD_TYPE);
405
406 /* free all internal allocated data */
407 if (DGifCloseFile(GifFile) == GIF_ERROR)
408 {
409 d->error = GifFile->Error;
410 return;
411 }
412
413 /* not animated gif */
414 if (d->frames_count == 0)
415 {
416 for (i=0; i < ctx.bm->height; i++)
417 pixels2native(&ctx, (void *)pixels_buffer[buf_idx], i);
418
419 d->frames_count++;
420 }
421
422 free(pixels_buffer[0]);
423 if (pixels_buffer[1])
424 free(pixels_buffer[1]);
425
426 free(Line);
427
428 /* WARNING !!!! */
429 /* GifFile object is trashed from now on, DONT use it */
430 /* Move bitmap in native format to the front of the buff */
431 memmove(d->mem, out, d->frames_count*d->native_img_size);
432
433 /* correct aspect ratio */
434#if (LCD_PIXEL_ASPECT_HEIGHT != 1 || LCD_PIXEL_ASPECT_WIDTH != 1)
435 struct bitmap img_src, img_dst; /* scaler vars */
436 struct dim dim_src, dim_dst; /* recalc_dimensions vars */
437 size_t c_native_img_size; /* size of the image after correction */
438
439 dim_src.width = bm.width;
440 dim_src.height = bm.height;
441
442 dim_dst.width = bm.width;
443 dim_dst.height = bm.height;
444
445 /* defined in apps/recorder/resize.c */
446 if (!recalc_dimension(&dim_dst, &dim_src))
447 {
448 /* calculate 'corrected' image size */
449#ifdef HAVE_LCD_COLOR
450 c_native_img_size = dim_dst.width * dim_dst.height * FB_DATA_SZ;
451#else
452 c_native_img_size = dim_dst.width * dim_dst.height;
453#endif
454
455 /* check memory constraints
456 * do the correction only if there is enough
457 * free memory
458 */
459 if (d->native_img_size*d->frames_count + c_native_img_size <=
460 d->mem_size)
461 {
462 img_dst.width = dim_dst.width;
463 img_dst.height = dim_dst.height;
464 img_dst.data = (unsigned char *)d->mem +
465 d->native_img_size*d->frames_count;
466
467 for (i = 0; i < d->frames_count; i++)
468 {
469 img_src.width = dim_src.width;
470 img_src.height = dim_src.height;
471 img_src.data = (unsigned char *)d->mem + i*d->native_img_size;
472
473 /* scale the bitmap to correct physical
474 * pixel dimentions
475 */
476 resize_bitmap(&img_src, &img_dst);
477
478 /* copy back corrected image */
479 memmove(d->mem + i*c_native_img_size,
480 img_dst.data,
481 c_native_img_size);
482 }
483
484 /* update decoder struct */
485 d->width = img_dst.width;
486 d->height = img_dst.height;
487 d->native_img_size = c_native_img_size;
488 }
489 }
490#endif
491}