A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 496 lines 16 kB view raw
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}