A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
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_decoder_destroy_memory_pool(struct gif_decoder *d)
119{
120 destroy_memory_pool(d->mem);
121}
122
123void gif_open(char *filename, struct gif_decoder *d)
124{
125 if ((GifFile = DGifOpenFileName(filename, &d->error)) == NULL)
126 return;
127
128 d->width = GifFile->SWidth;
129 d->height = GifFile->SHeight;
130 d->frames_count = 0;
131}
132
133static void set_canvas_background(pixel_t *out, GifFileType *GifFile)
134{
135 /* Reading Gif spec it seems one should always use background color
136 * in canvas but most real files omit this and sets background color to 0
137 * (which IS valid index). We can choose to either conform to standard
138 * (and wrongly display most of gifs with transparency) or stick to
139 * common practise and treat background color 0 as transparent.
140 * Moreover when dispose method is BACKGROUND spec suggest
141 * to reset canvas to global background color specified in gif BUT
142 * all renderers I know use transparency instead.
143 */
144 memset(out, PIXEL_TRANSPARENT, PIXELS_SZ);
145}
146
147/* var names adhere to giflib coding style */
148void gif_decode(struct gif_decoder *d,
149 void (*pf_progress)(int current, int total))
150{
151 int i, j;
152
153 int Size;
154 int Row;
155 int Col;
156 int Width;
157 int Height;
158 int ExtCode;
159
160 GifPixelType *Line;
161
162 GifRecordType RecordType;
163 GifByteType *Extension;
164
165 unsigned char *out = NULL;
166
167 /* The way Interlaced image should
168 * be read - offsets and jumps
169 */
170 const char InterlacedOffset[] = { 0, 4, 2, 1 };
171 const char InterlacedJumps[] = { 8, 8, 4, 2 };
172
173 /* used for color conversion */
174 struct bitmap bm;
175 struct scaler_context ctx = {
176 .bm = &bm,
177 .dither = 0
178 };
179
180 /* initialize struct */
181 memset(&bm, 0, sizeof(struct bitmap));
182
183 Size = GifFile->SWidth * sizeof(GifPixelType); /* Size in bytes one row.*/
184 Line = (GifPixelType *)malloc(Size);
185 if (Line == NULL)
186 {
187 /* error allocating temp space */
188 d->error = D_GIF_ERR_NOT_ENOUGH_MEM;
189 DGifCloseFile(GifFile);
190 return;
191 }
192
193 /* We use two pixel buffers if dispose method asks
194 * for restoration of the previous state.
195 * We only swap the indexes leaving data in place.
196 */
197 int buf_idx = 0;
198 pixel_t *pixels_buffer[2];
199 pixels_buffer[0] = (pixel_t *)malloc(PIXELS_SZ);
200 pixels_buffer[1] = NULL;
201
202 if (pixels_buffer[0] == NULL)
203 {
204 d->error = D_GIF_ERR_NOT_ENOUGH_MEM;
205 goto free_and_return;
206 }
207
208 /* Global background color */
209 set_canvas_background(pixels_buffer[0], GifFile);
210
211 bm.width = GifFile->SWidth;
212 bm.height = GifFile->SHeight;
213 d->native_img_size = NATIVE_SZ;
214
215 if (pf_progress != NULL)
216 pf_progress(0, 100);
217
218 /* Scan the content of the GIF file and load the image(s) in: */
219 do
220 {
221 if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR)
222 {
223 d->error = GifFile->Error;
224 goto free_and_return;
225 }
226
227 switch (RecordType)
228 {
229 case IMAGE_DESC_RECORD_TYPE:
230
231 if (DGifGetImageDesc(GifFile) == GIF_ERROR)
232 {
233 d->error = GifFile->Error;
234 goto free_and_return;
235 }
236
237 /* Image Position relative to canvas */
238 Row = GifFile->Image.Top;
239 Col = GifFile->Image.Left;
240 Width = GifFile->Image.Width;
241 Height = GifFile->Image.Height;
242
243 /* Check Color map to use */
244 if (GifFile->Image.ColorMap == NULL &&
245 GifFile->SColorMap == NULL)
246 {
247 d->error = D_GIF_ERR_NO_COLOR_MAP;
248 goto free_and_return;
249 }
250
251 /* sanity check */
252 if (GifFile->Image.Left+GifFile->Image.Width>GifFile->SWidth ||
253 GifFile->Image.Top+GifFile->Image.Height>GifFile->SHeight)
254 {
255 d->error = D_GIF_ERR_DATA_TOO_BIG;
256 goto free_and_return;
257 }
258
259 if (GifFile->Image.GCB &&
260 GifFile->Image.GCB->DisposalMode == DISPOSE_PREVIOUS)
261 {
262 /* We need to take a snapshot before processing the image
263 * in order to restore canvas to previous state after
264 * rendering
265 */
266 buf_idx ^= 1;
267
268 if (pixels_buffer[buf_idx] == NULL)
269 pixels_buffer[buf_idx] = (pixel_t *)malloc(PIXELS_SZ);
270 }
271
272 if (GifFile->Image.Interlace)
273 {
274 /* Need to perform 4 passes on the image */
275 for (i = 0; i < 4; i++)
276 {
277 for (j = Row + InterlacedOffset[i];
278 j < Row + Height;
279 j += InterlacedJumps[i])
280 {
281 if (DGifGetLine(GifFile, Line, Width) == GIF_ERROR)
282 {
283 d->error = GifFile->Error;
284 goto free_and_return;
285 }
286
287 gif2pixels(Line, pixels_buffer[buf_idx],
288 Row + j, Col, Width);
289 }
290
291 if (pf_progress != NULL)
292 pf_progress(25*(i+1), 100);
293 }
294 }
295 else
296 {
297 for (i = 0; i < Height; i++)
298 {
299 /* load single line into buffer */
300 if (DGifGetLine(GifFile, Line, Width) == GIF_ERROR)
301 {
302 d->error = GifFile->Error;
303 goto free_and_return;
304 }
305
306 gif2pixels(Line, pixels_buffer[buf_idx],
307 Row + i, Col, Width);
308
309 if (pf_progress != NULL)
310 pf_progress((i+1), Height);
311 }
312 }
313
314 /* allocate space for new frame */
315 out = realloc(out, d->native_img_size*(d->frames_count + 1));
316 if (out == NULL)
317 {
318 d->error = D_GIF_ERR_NOT_ENOUGH_MEM;
319 goto free_and_return;
320 }
321
322 bm.data = out + d->native_img_size*d->frames_count;
323
324 /* animated gif */
325 if (GifFile->Image.GCB && GifFile->Image.GCB->DelayTime != 0)
326 {
327 for (i=0; i < ctx.bm->height; i++)
328 pixels2native(&ctx, (void *)pixels_buffer[buf_idx], i);
329
330 /* restore to the background color */
331 switch (GifFile->Image.GCB->DisposalMode)
332 {
333 case DISPOSE_BACKGROUND:
334 set_canvas_background(pixels_buffer[buf_idx],
335 GifFile);
336 break;
337
338 case DISPOSE_PREVIOUS:
339 buf_idx ^= 1;
340 break;
341
342 default:
343 /* DISPOSAL_UNSPECIFIED
344 * DISPOSE_DO_NOT
345 */
346 break;
347 }
348
349 d->frames_count++;
350 }
351
352 break;
353
354 case EXTENSION_RECORD_TYPE:
355 if (DGifGetExtension(GifFile, &ExtCode, &Extension) ==
356 GIF_ERROR)
357 {
358 d->error = GifFile->Error;
359 goto free_and_return;
360 }
361
362 if (ExtCode == GRAPHICS_EXT_FUNC_CODE)
363 {
364 if (GifFile->Image.GCB == NULL)
365 GifFile->Image.GCB = (GraphicsControlBlock *)
366 malloc(sizeof(GraphicsControlBlock));
367
368 if (DGifExtensionToGCB(Extension[0],
369 Extension + 1,
370 GifFile->Image.GCB) == GIF_ERROR)
371 {
372 d->error = GifFile->Error;
373 goto free_and_return;
374 }
375 d->delay = GifFile->Image.GCB->DelayTime;
376 }
377
378 /* Skip anything else */
379 while (Extension != NULL)
380 {
381 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR)
382 {
383 d->error = GifFile->Error;
384 goto free_and_return;
385 }
386 }
387 break;
388
389 /* including TERMINATE_RECORD_TYPE */
390 default:
391 break;
392 }
393
394 } while (RecordType != TERMINATE_RECORD_TYPE);
395
396 /* free all internal allocated data */
397 if (DGifCloseFile(GifFile) == GIF_ERROR)
398 {
399 d->error = GifFile->Error;
400 free(pixels_buffer[0]);
401 if (pixels_buffer[1])
402 free(pixels_buffer[1]);
403 free(Line);
404 return;
405 }
406
407 /* not animated gif */
408 if (d->frames_count == 0)
409 {
410 for (i=0; i < ctx.bm->height; i++)
411 pixels2native(&ctx, (void *)pixels_buffer[buf_idx], i);
412
413 d->frames_count++;
414 }
415
416 free(pixels_buffer[0]);
417 if (pixels_buffer[1])
418 free(pixels_buffer[1]);
419
420 free(Line);
421
422 /* WARNING !!!! */
423 /* GifFile object is trashed from now on, DONT use it */
424 /* Move bitmap in native format to the front of the buff */
425 memmove(d->mem, out, d->frames_count*d->native_img_size);
426
427 /* correct aspect ratio */
428#if (LCD_PIXEL_ASPECT_HEIGHT != 1 || LCD_PIXEL_ASPECT_WIDTH != 1)
429 struct bitmap img_src, img_dst; /* scaler vars */
430 struct dim dim_src, dim_dst; /* recalc_dimensions vars */
431 size_t c_native_img_size; /* size of the image after correction */
432
433 dim_src.width = bm.width;
434 dim_src.height = bm.height;
435
436 dim_dst.width = bm.width;
437 dim_dst.height = bm.height;
438
439 /* defined in apps/recorder/resize.c */
440 if (!recalc_dimension(&dim_dst, &dim_src))
441 {
442 /* calculate 'corrected' image size */
443#ifdef HAVE_LCD_COLOR
444 c_native_img_size = dim_dst.width * dim_dst.height * FB_DATA_SZ;
445#else
446 c_native_img_size = dim_dst.width * dim_dst.height;
447#endif
448
449 /* check memory constraints
450 * do the correction only if there is enough
451 * free memory
452 */
453 if (d->native_img_size*d->frames_count + c_native_img_size <=
454 d->mem_size)
455 {
456 img_dst.width = dim_dst.width;
457 img_dst.height = dim_dst.height;
458 img_dst.data = (unsigned char *)d->mem +
459 d->native_img_size*d->frames_count;
460
461 for (i = 0; i < d->frames_count; i++)
462 {
463 img_src.width = dim_src.width;
464 img_src.height = dim_src.height;
465 img_src.data = (unsigned char *)d->mem + i*d->native_img_size;
466
467 /* scale the bitmap to correct physical
468 * pixel dimentions
469 */
470 resize_bitmap(&img_src, &img_dst);
471
472 /* copy back corrected image */
473 memmove(d->mem + i*c_native_img_size,
474 img_dst.data,
475 c_native_img_size);
476 }
477
478 /* update decoder struct */
479 d->width = img_dst.width;
480 d->height = img_dst.height;
481 d->native_img_size = c_native_img_size;
482 }
483 }
484#endif
485 return;
486
487free_and_return:
488 if (Line)
489 free(Line);
490 if (pixels_buffer[0])
491 free(pixels_buffer[0]);
492 if (pixels_buffer[1])
493 free(pixels_buffer[1]);
494 DGifCloseFile(GifFile);
495 return;
496}