This should fix FS#10917, and also fix channel swapping i introduced in r23784. Lets...
[kugel-rb.git] / apps / plugins / png / png.c
blob3f826001c94071501f3182a6f6eb75ec5ea518dd
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$id $
10 * Copyright (C) 2009 by Christophe Gouiran <bechris13250 -at- gmail -dot- com>
12 * Based on lodepng, a lightweight png decoder/encoder
13 * (c) 2005-2008 Lode Vandevenne
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
23 ****************************************************************************/
26 LodePNG version 20080927
28 Copyright (c) 2005-2008 Lode Vandevenne
30 This software is provided 'as-is', without any express or implied
31 warranty. In no event will the authors be held liable for any damages
32 arising from the use of this software.
34 Permission is granted to anyone to use this software for any purpose,
35 including commercial applications, and to alter it and redistribute it
36 freely, subject to the following restrictions:
38 1. The origin of this software must not be misrepresented; you must not
39 claim that you wrote the original software. If you use this software
40 in a product, an acknowledgment in the product documentation would be
41 appreciated but is not required.
43 2. Altered source versions must be plainly marked as such, and must not be
44 misrepresented as being the original software.
46 3. This notice may not be removed or altered from any source
47 distribution.
51 The manual and changelog can be found in the header file "lodepng.h"
52 You are free to name this file lodepng.cpp or lodepng.c depending on your usage.
55 #include "plugin.h"
56 #include "lcd.h"
57 #include <lib/playback_control.h>
58 #include <lib/helper.h>
59 #include <lib/configfile.h>
60 #include <lib/grey.h>
61 #include <lib/pluginlib_bmp.h>
62 #include "zlib.h"
63 #include "png.h"
65 PLUGIN_HEADER
67 /* ////////////////////////////////////////////////////////////////////////// */
68 /* LodeFlate & LodeZlib Setting structs */
69 /* ////////////////////////////////////////////////////////////////////////// */
71 typedef struct LodePNG_InfoColor /*info about the color type of an image*/
73 /*header (IHDR)*/
74 unsigned colorType; /*color type*/
75 unsigned bitDepth; /*bits per sample*/
77 /*palette (PLTE)*/
78 unsigned char palette[256 * 4]; /*palette in RGBARGBA... order*/
79 size_t palettesize; /*palette size in number of colors (amount of bytes is 4 * palettesize)*/
81 /*transparent color key (tRNS)*/
82 unsigned key_defined; /*is a transparent color key given?*/
83 unsigned key_r; /*red component of color key*/
84 unsigned key_g; /*green component of color key*/
85 unsigned key_b; /*blue component of color key*/
86 } LodePNG_InfoColor;
88 typedef struct LodePNG_Time /*LodePNG's encoder does not generate the current time. To make it add a time chunk the correct time has to be provided*/
90 unsigned year; /*2 bytes*/
91 unsigned char month; /*1-12*/
92 unsigned char day; /*1-31*/
93 unsigned char hour; /*0-23*/
94 unsigned char minute; /*0-59*/
95 unsigned char second; /*0-60 (to allow for leap seconds)*/
96 } LodePNG_Time;
98 typedef struct LodePNG_InfoPng /*information about the PNG image, except pixels and sometimes except width and height*/
100 /*header (IHDR), palette (PLTE) and transparency (tRNS)*/
101 unsigned width; /*width of the image in pixels (ignored by encoder, but filled in by decoder)*/
102 unsigned height; /*height of the image in pixels (ignored by encoder, but filled in by decoder)*/
103 unsigned compressionMethod; /*compression method of the original file*/
104 unsigned filterMethod; /*filter method of the original file*/
105 unsigned interlaceMethod; /*interlace method of the original file*/
106 LodePNG_InfoColor color; /*color type and bits, palette, transparency*/
108 /*suggested background color (bKGD)*/
109 unsigned background_defined; /*is a suggested background color given?*/
110 unsigned background_r; /*red component of suggested background color*/
111 unsigned background_g; /*green component of suggested background color*/
112 unsigned background_b; /*blue component of suggested background color*/
114 /*time chunk (tIME)*/
115 unsigned char time_defined; /*if 0, no tIME chunk was or will be generated in the PNG image*/
116 LodePNG_Time time;
118 /*phys chunk (pHYs)*/
119 unsigned phys_defined; /*is pHYs chunk defined?*/
120 unsigned phys_x;
121 unsigned phys_y;
122 unsigned char phys_unit; /*may be 0 (unknown unit) or 1 (metre)*/
124 } LodePNG_InfoPng;
126 typedef struct LodePNG_InfoRaw /*contains user-chosen information about the raw image data, which is independent of the PNG image*/
128 LodePNG_InfoColor color;
129 } LodePNG_InfoRaw;
131 typedef struct LodePNG_DecodeSettings
133 unsigned color_convert; /*whether to convert the PNG to the color type you want. Default: yes*/
134 } LodePNG_DecodeSettings;
136 typedef struct LodePNG_Decoder
138 LodePNG_DecodeSettings settings;
139 LodePNG_InfoRaw infoRaw;
140 LodePNG_InfoPng infoPng; /*info of the PNG image obtained after decoding*/
141 long error;
142 char error_msg[128];
143 int x,y;
144 int width,height;
145 } LodePNG_Decoder;
147 #define VERSION_STRING "20080927"
149 /* Min memory allowing us to use the plugin buffer
150 * and thus not stopping the music
151 * *Very* rough estimation:
152 * Max 10 000 dir entries * 4bytes/entry (char **) = 40000 bytes
153 * + 30k code size = 70 000
154 * + 50k min for png = 130 000
156 #define MIN_MEM 130000
158 /* Headings */
159 #define DIR_PREV 1
160 #define DIR_NEXT -1
161 #define DIR_NONE 0
163 /* decompressed image in the possible sizes (1,2,4,8), wasting the other */
164 static fb_data *disp[9];
165 /* up to here currently used by image(s) */
166 static fb_data *disp_buf;
168 /* my memory pool (from the mp3 buffer) */
169 static char print[128]; /* use a common snprintf() buffer */
171 unsigned char *memory, *memory_max;
172 static size_t memory_size;
174 static unsigned char *image; /* where we put the content of the file */
175 static size_t image_size;
177 static fb_data *converted_image; /* the (color) converted image */
178 static size_t converted_image_size;
180 static unsigned char *decoded_image; /* the decoded image */
181 static size_t decoded_image_size;
183 static fb_data *resized_image; /* the decoded image */
185 static struct tree_context *tree;
187 /* the current full file name */
188 static char np_file[MAX_PATH];
189 static int curfile = 0, direction = DIR_NONE, entries = 0;
191 static LodePNG_Decoder decoder;
193 /* list of the png files */
194 static char **file_pt;
195 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
196 /* are we using the plugin buffer or the audio buffer? */
197 static bool plug_buf = true;
198 #endif
200 /* Persistent configuration */
201 #define PNG_CONFIGFILE "png.cfg"
202 #define PNG_SETTINGS_MINVERSION 1
203 #define PNG_SETTINGS_VERSION 1
205 /* Slideshow times */
206 #define SS_MIN_TIMEOUT 1
207 #define SS_MAX_TIMEOUT 20
208 #define SS_DEFAULT_TIMEOUT 5
210 struct png_settings
212 int ss_timeout;
215 static struct png_settings png_settings =
217 SS_DEFAULT_TIMEOUT
219 static struct png_settings old_settings;
221 static struct configdata png_config[] =
223 { TYPE_INT, SS_MIN_TIMEOUT, SS_MAX_TIMEOUT,
224 { .int_p = &png_settings.ss_timeout }, "Slideshow Time", NULL
228 #if LCD_DEPTH > 1
229 static fb_data* old_backdrop;
230 #endif
232 static int slideshow_enabled = false; /* run slideshow */
233 static int running_slideshow = false; /* loading image because of slideshw */
234 #ifndef SIMULATOR
235 static int immediate_ata_off = false; /* power down disk after loading */
236 #endif
238 static unsigned ds, ds_min, ds_max; /* downscaling and limits */
241 The two functions below (LodePNG_decompress and LodePNG_compress) directly call the
242 LodeZlib_decompress and LodeZlib_compress functions. The only purpose of the functions
243 below, is to provide the ability to let LodePNG use a different Zlib encoder by only
244 changing the two functions below, instead of changing it inside the vareous places
245 in the other LodePNG functions.
247 *out must be NULL and *outsize must be 0 initially, and after the function is done,
248 *out must point to the decompressed data, *outsize must be the size of it, and must
249 be the size of the useful data in bytes, not the alloc size.
252 static unsigned LodePNG_decompress(unsigned char* out, size_t* outsize, const unsigned char* in, size_t insize, char *error_msg)
254 z_stream stream;
255 int err;
257 rb->strcpy(error_msg, "");
259 stream.next_in = (Bytef*)in;
260 stream.avail_in = (uInt)insize;
262 stream.next_out = out;
263 stream.avail_out = (uInt)*outsize;
265 stream.zalloc = (alloc_func)0;
266 stream.zfree = (free_func)0;
268 err = inflateInit(&stream);
269 if (err != Z_OK) return err;
271 err = inflate(&stream, Z_FINISH);
272 if (err != Z_STREAM_END) {
273 inflateEnd(&stream);
274 if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0))
275 return Z_DATA_ERROR;
276 return err;
278 *outsize = stream.total_out;
280 err = inflateEnd(&stream);
281 if (stream.msg != Z_NULL)
282 rb->strcpy(error_msg, stream.msg);
283 return err;
287 /* ////////////////////////////////////////////////////////////////////////// */
288 /* / Reading and writing single bits and bytes from/to stream for LodePNG / */
289 /* ////////////////////////////////////////////////////////////////////////// */
291 static unsigned char readBitFromReversedStream(size_t* bitpointer, const unsigned char* bitstream)
293 unsigned char result = (unsigned char)((bitstream[(*bitpointer) >> 3] >> (7 - ((*bitpointer) & 0x7))) & 1);
294 (*bitpointer)++;
295 return result;
298 static unsigned readBitsFromReversedStream(size_t* bitpointer, const unsigned char* bitstream, size_t nbits)
300 unsigned result = 0;
301 size_t i;
302 for (i = nbits - 1; i < nbits; i--) result += (unsigned)readBitFromReversedStream(bitpointer, bitstream) << i;
303 return result;
306 static void setBitOfReversedStream0(size_t* bitpointer, unsigned char* bitstream, unsigned char bit)
308 /*the current bit in bitstream must be 0 for this to work*/
309 if (bit) bitstream[(*bitpointer) >> 3] |= (bit << (7 - ((*bitpointer) & 0x7))); /*earlier bit of huffman code is in a lesser significant bit of an earlier byte*/
310 (*bitpointer)++;
313 static void setBitOfReversedStream(size_t* bitpointer, unsigned char* bitstream, unsigned char bit)
315 /*the current bit in bitstream may be 0 or 1 for this to work*/
316 if (bit == 0) bitstream[(*bitpointer) >> 3] &= (unsigned char)(~(1 << (7 - ((*bitpointer) & 0x7))));
317 else bitstream[(*bitpointer) >> 3] |= (1 << (7 - ((*bitpointer) & 0x7)));
318 (*bitpointer)++;
321 static unsigned LodePNG_read32bitInt(const unsigned char* buffer)
323 return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
326 /* ////////////////////////////////////////////////////////////////////////// */
327 /* / PNG chunks / */
328 /* ////////////////////////////////////////////////////////////////////////// */
330 unsigned LodePNG_chunk_length(const unsigned char* chunk) /*get the length of the data of the chunk. Total chunk length has 12 bytes more.*/
332 return LodePNG_read32bitInt(&chunk[0]);
335 void LodePNG_chunk_type(char type[5], const unsigned char* chunk) /*puts the 4-byte type in null terminated string*/
337 unsigned i;
338 for (i = 0; i < 4; i++) type[i] = chunk[4 + i];
339 type[4] = 0; /*null termination char*/
342 unsigned char LodePNG_chunk_type_equals(const unsigned char* chunk, const char* type) /*check if the type is the given type*/
344 if (type[4] != 0) return 0;
345 return (chunk[4] == type[0] && chunk[5] == type[1] && chunk[6] == type[2] && chunk[7] == type[3]);
348 /*properties of PNG chunks gotten from capitalization of chunk type name, as defined by the standard*/
349 unsigned char LodePNG_chunk_critical(const unsigned char* chunk) /*0: ancillary chunk, 1: it's one of the critical chunk types*/
351 return((chunk[4] & 32) == 0);
354 unsigned char LodePNG_chunk_private(const unsigned char* chunk) /*0: public, 1: private*/
356 return((chunk[6] & 32) != 0);
359 unsigned char LodePNG_chunk_safetocopy(const unsigned char* chunk) /*0: the chunk is unsafe to copy, 1: the chunk is safe to copy*/
361 return((chunk[7] & 32) != 0);
364 unsigned char* LodePNG_chunk_data(unsigned char* chunk) /*get pointer to the data of the chunk*/
366 return &chunk[8];
369 const unsigned char* LodePNG_chunk_data_const(const unsigned char* chunk) /*get pointer to the data of the chunk*/
371 return &chunk[8];
374 unsigned LodePNG_chunk_check_crc(const unsigned char* chunk) /*returns 0 if the crc is correct, error code if it's incorrect*/
376 unsigned length = LodePNG_chunk_length(chunk);
377 unsigned CRC = LodePNG_read32bitInt(&chunk[length + 8]);
378 unsigned checksum = crc32(0L, &chunk[4], length + 4); /*the CRC is taken of the data and the 4 chunk type letters, not the length*/
379 if (CRC != checksum) return 1;
380 else return 0;
383 unsigned char* LodePNG_chunk_next(unsigned char* chunk) /*don't use on IEND chunk, as there is no next chunk then*/
385 unsigned total_chunk_length = LodePNG_chunk_length(chunk) + 12;
386 return &chunk[total_chunk_length];
389 const unsigned char* LodePNG_chunk_next_const(const unsigned char* chunk) /*don't use on IEND chunk, as there is no next chunk then*/
391 unsigned total_chunk_length = LodePNG_chunk_length(chunk) + 12;
392 return &chunk[total_chunk_length];
395 /* ////////////////////////////////////////////////////////////////////////// */
396 /* / Color types and such / */
397 /* ////////////////////////////////////////////////////////////////////////// */
399 /*return type is a LodePNG error code*/
400 static unsigned checkColorValidity(unsigned colorType, unsigned bd) /*bd = bitDepth*/
402 switch (colorType)
404 case 0:
405 if (!(bd == 1 || bd == 2 || bd == 4 || bd == 8 || bd == 16)) return 37; break; /*grey*/
406 case 2:
407 if (!( bd == 8 || bd == 16)) return 37; break; /*RGB*/
408 case 3:
409 if (!(bd == 1 || bd == 2 || bd == 4 || bd == 8 )) return 37; break; /*palette*/
410 case 4:
411 if (!( bd == 8 || bd == 16)) return 37; break; /*grey + alpha*/
412 case 6:
413 if (!( bd == 8 || bd == 16)) return 37; break; /*RGBA*/
414 default:
415 return 31;
417 return 0; /*allowed color type / bits combination*/
420 static unsigned getNumColorChannels(unsigned colorType)
422 switch (colorType)
424 case 0:
425 return 1; /*grey*/
426 case 2:
427 return 3; /*RGB*/
428 case 3:
429 return 1; /*palette*/
430 case 4:
431 return 2; /*grey + alpha*/
432 case 6:
433 return 4; /*RGBA*/
435 return 0; /*unexisting color type*/
438 static unsigned getBpp(unsigned colorType, unsigned bitDepth)
440 return getNumColorChannels(colorType) * bitDepth; /*bits per pixel is amount of channels * bits per channel*/
443 /* ////////////////////////////////////////////////////////////////////////// */
445 void LodePNG_InfoColor_init(LodePNG_InfoColor* info)
447 info->key_defined = 0;
448 info->key_r = info->key_g = info->key_b = 0;
449 info->colorType = 6;
450 info->bitDepth = 8;
451 memset(info->palette, 0, 256 * 4 * sizeof(unsigned char));
452 info->palettesize = 0;
455 void LodePNG_InfoColor_cleanup(LodePNG_InfoColor* info)
457 info->palettesize = 0;
460 unsigned LodePNG_InfoColor_getBpp(const LodePNG_InfoColor* info) { return getBpp(info->colorType, info->bitDepth); } /*calculate bits per pixel out of colorType and bitDepth*/
461 unsigned LodePNG_InfoColor_isGreyscaleType(const LodePNG_InfoColor* info) { return info->colorType == 0 || info->colorType == 4; }
463 unsigned LodePNG_InfoColor_equal(const LodePNG_InfoColor* info1, const LodePNG_InfoColor* info2)
465 return info1->colorType == info2->colorType
466 && info1->bitDepth == info2->bitDepth; /*palette and color key not compared*/
469 void LodePNG_InfoPng_init(LodePNG_InfoPng* info)
471 info->width = info->height = 0;
472 LodePNG_InfoColor_init(&info->color);
473 info->interlaceMethod = 0;
474 info->compressionMethod = 0;
475 info->filterMethod = 0;
476 info->background_defined = 0;
477 info->background_r = info->background_g = info->background_b = 0;
479 info->time_defined = 0;
480 info->phys_defined = 0;
483 void LodePNG_InfoPng_cleanup(LodePNG_InfoPng* info)
485 LodePNG_InfoColor_cleanup(&info->color);
488 unsigned LodePNG_InfoColor_copy(LodePNG_InfoColor* dest, const LodePNG_InfoColor* source)
490 size_t i;
491 LodePNG_InfoColor_cleanup(dest);
492 *dest = *source;
493 for (i = 0; i < source->palettesize * 4; i++) dest->palette[i] = source->palette[i];
494 return 0;
497 unsigned LodePNG_InfoPng_copy(LodePNG_InfoPng* dest, const LodePNG_InfoPng* source)
499 unsigned error = 0;
500 LodePNG_InfoPng_cleanup(dest);
501 *dest = *source;
502 LodePNG_InfoColor_init(&dest->color);
503 error = LodePNG_InfoColor_copy(&dest->color, &source->color); if (error) return error;
504 return error;
507 void LodePNG_InfoPng_swap(LodePNG_InfoPng* a, LodePNG_InfoPng* b)
509 LodePNG_InfoPng temp = *a;
510 *a = *b;
511 *b = temp;
514 void LodePNG_InfoRaw_init(LodePNG_InfoRaw* info)
516 LodePNG_InfoColor_init(&info->color);
519 void LodePNG_InfoRaw_cleanup(LodePNG_InfoRaw* info)
521 LodePNG_InfoColor_cleanup(&info->color);
524 unsigned LodePNG_InfoRaw_copy(LodePNG_InfoRaw* dest, const LodePNG_InfoRaw* source)
526 unsigned error = 0;
527 LodePNG_InfoRaw_cleanup(dest);
528 *dest = *source;
529 LodePNG_InfoColor_init(&dest->color);
530 error = LodePNG_InfoColor_copy(&dest->color, &source->color); if (error) return error;
531 return error;
534 /* ////////////////////////////////////////////////////////////////////////// */
537 converts from any color type to 24-bit or 32-bit (later maybe more supported). return value = LodePNG error code
538 the out buffer must have (w * h * bpp + 7) / 8 bytes, where bpp is the bits per pixel of the output color type (LodePNG_InfoColor_getBpp)
539 for < 8 bpp images, there may _not_ be padding bits at the end of scanlines.
541 unsigned LodePNG_convert(fb_data* out, const unsigned char* in, LodePNG_InfoColor* infoOut, LodePNG_InfoColor* infoIn, unsigned w, unsigned h)
543 size_t i, j, bp = 0; /*bitpointer, used by less-than-8-bit color types*/
544 size_t x, y;
545 unsigned char c;
547 if (!running_slideshow)
549 rb->snprintf(print, sizeof(print), "color conversion in progress");
550 rb->lcd_puts(0, 3, print);
551 rb->lcd_update();
554 /*cases where in and out already have the same format*/
555 if (LodePNG_InfoColor_equal(infoIn, infoOut))
558 i = 0;
559 j = 0;
560 for (y = 0 ; y < h ; y++) {
561 for (x = 0 ; x < w ; x++) {
562 unsigned char r = in[i++];
563 unsigned char g = in[i++];
564 unsigned char b = in[i++];
565 out[j++] = LCD_RGBPACK(r,g,b);
568 return 0;
571 if ((infoOut->colorType == 2 || infoOut->colorType == 6) && infoOut->bitDepth == 8)
573 if (infoIn->bitDepth == 8)
575 switch (infoIn->colorType)
577 case 0: /*greyscale color*/
578 i = 0;
579 for (y = 0 ; y < h ; y++) {
580 for (x = 0 ; x < w ; x++) {
581 c=in[i];
582 //unsigned char r = in[i];
583 //unsigned char g = in[i];
584 //unsigned char b = in[i];
585 out[i++] = LCD_RGBPACK(c,c,c);
588 break;
589 case 2: /*RGB color*/
590 i = 0;
591 for (y = 0 ; y < h ; y++) {
592 for (x = 0 ; x < w ; x++) {
593 j = 3 * i;
594 unsigned char r = in[j];
595 unsigned char g = in[j + 1];
596 unsigned char b = in[j + 2];
597 out[i++] = LCD_RGBPACK(r,g,b);
600 break;
601 case 3: /*indexed color (palette)*/
602 i = 0;
603 for (y = 0 ; y < h ; y++) {
604 for (x = 0 ; x < w ; x++) {
605 if (in[i] >= infoIn->palettesize) return 46;
606 j = in[i] << 2;
607 unsigned char r = infoIn->palette[j];
608 unsigned char g = infoIn->palette[j + 1];
609 unsigned char b = infoIn->palette[j + 2];
610 out[i++] = LCD_RGBPACK(r,g,b);
613 break;
614 case 4: /*greyscale with alpha*/
615 i = 0;
616 for (y = 0 ; y < h ; y++) {
617 for (x = 0 ; x < w ; x++) {
618 c = in[i << 1];
619 //unsigned char r = in[i<<1];
620 //unsigned char g = in[i<<1];
621 //unsigned char b = in[i<<1];
622 out[i++] = LCD_RGBPACK(c,c,c);
625 break;
626 case 6: /*RGB with alpha*/
627 i = 0;
628 for (y = 0 ; y < h ; y++) {
629 for (x = 0 ; x < w ; x++) {
630 j = i << 2;
631 unsigned char r = in[j];
632 unsigned char g = in[j + 1];
633 unsigned char b = in[j + 2];
634 out[i++] = LCD_RGBPACK(r,g,b);
637 break;
638 default:
639 break;
642 else if (infoIn->bitDepth == 16)
644 switch (infoIn->colorType)
646 case 0: /*greyscale color*/
647 i = 0;
648 for (y = 0 ; y < h ; y++) {
649 for (x = 0 ; x < w ; x++) {
650 c = in[i << 1];
651 //unsigned char r = in[2 * i];
652 //unsigned char g = in[2 * i];
653 //unsigned char b = in[2 * i];
654 out[i++] = LCD_RGBPACK(c,c,c);
657 break;
658 case 2: /*RGB color*/
659 i = 0;
660 for (y = 0 ; y < h ; y++) {
661 for (x = 0 ; x < w ; x++) {
662 j = 6 * i;
663 unsigned char r = in[j];
664 unsigned char g = in[j + 2];
665 unsigned char b = in[j + 4];
666 out[i++] = LCD_RGBPACK(r,g,b);
669 break;
670 case 4: /*greyscale with alpha*/
671 i = 0;
672 for (y = 0 ; y < h ; y++) {
673 for (x = 0 ; x < w ; x++) {
674 c = in[i << 2];
675 //unsigned char r = in[4 * i];
676 //unsigned char g = in[4 * i];
677 //unsigned char b = in[4 * i];
678 out[i++] = LCD_RGBPACK(c,c,c);
681 break;
682 case 6: /*RGB with alpha*/
683 i = 0;
684 for (y = 0 ; y < h ; y++) {
685 for (x = 0 ; x < w ; x++) {
686 j = i << 3;
687 unsigned char r = in[j];
688 unsigned char g = in[j + 2];
689 unsigned char b = in[j + 4];
690 out[i++] = LCD_RGBPACK(r,g,b);
693 break;
694 default:
695 break;
698 else /*infoIn->bitDepth is less than 8 bit per channel*/
700 switch (infoIn->colorType)
702 case 0: /*greyscale color*/
703 i = 0;
704 for (y = 0 ; y < h ; y++) {
705 for (x = 0 ; x < w ; x++) {
706 unsigned value = readBitsFromReversedStream(&bp, in, infoIn->bitDepth);
707 value = (value * 255) / ((1 << infoIn->bitDepth) - 1); /*scale value from 0 to 255*/
708 unsigned char r = (unsigned char)value;
709 unsigned char g = (unsigned char)value;
710 unsigned char b = (unsigned char)value;
711 out[i++] = LCD_RGBPACK(r,g,b);
714 break;
715 case 3: /*indexed color (palette)*/
716 i = 0;
717 for (y = 0 ; y < h ; y++) {
718 for (x = 0 ; x < w ; x++) {
719 unsigned value = readBitsFromReversedStream(&bp, in, infoIn->bitDepth);
720 if (value >= infoIn->palettesize) return 47;
721 j = value << 2;
722 unsigned char r = infoIn->palette[j];
723 unsigned char g = infoIn->palette[j + 1];
724 unsigned char b = infoIn->palette[j + 2];
725 out[i++] = LCD_RGBPACK(r,g,b);
728 break;
729 default:
730 break;
734 else if (LodePNG_InfoColor_isGreyscaleType(infoOut) && infoOut->bitDepth == 8) /*conversion from greyscale to greyscale*/
736 if (!LodePNG_InfoColor_isGreyscaleType(infoIn)) return 62;
737 if (infoIn->bitDepth == 8)
739 switch (infoIn->colorType)
741 case 0: /*greyscale color*/
742 i = 0;
743 for (y = 0 ; y < h ; y++) {
744 for (x = 0 ; x < w ; x++) {
745 c = in[i];
746 //unsigned char r = in[i];
747 //unsigned char g = in[i];
748 //unsigned char b = in[i];
749 out[i++] = LCD_RGBPACK(c,c,c);
752 break;
753 case 4: /*greyscale with alpha*/
754 i = 0;
755 for (y = 0 ; y < h ; y++) {
756 for (x = 0 ; x < w ; x++) {
757 c = in[(i << 1) + 1];
758 //unsigned char r = in[2 * i + 1];
759 //unsigned char g = in[2 * i + 1];
760 //unsigned char b = in[2 * i + 1];
761 out[i++] = LCD_RGBPACK(c,c,c);
764 break;
765 default:
766 return 31;
769 else if (infoIn->bitDepth == 16)
771 switch (infoIn->colorType)
773 case 0: /*greyscale color*/
774 i = 0;
775 for (y = 0 ; y < h ; y++) {
776 for (x = 0 ; x < w ; x++) {
777 c = in[i << 1];
778 //unsigned char r = in[2 * i];
779 //unsigned char g = in[2 * i];
780 //unsigned char b = in[2 * i];
781 out[i++] = LCD_RGBPACK(c,c,c);
784 break;
785 case 4: /*greyscale with alpha*/
786 i = 0;
787 for (y = 0 ; y < h ; y++) {
788 for (x = 0 ; x < w ; x++) {
789 c = in[i << 2];
790 //unsigned char r = in[4 * i];
791 //unsigned char g = in[4 * i];
792 //unsigned char b = in[4 * i];
793 out[i++] = LCD_RGBPACK(c,c,c);
796 break;
797 default:
798 return 31;
801 else /*infoIn->bitDepth is less than 8 bit per channel*/
803 if (infoIn->colorType != 0) return 31; /*colorType 0 is the only greyscale type with < 8 bits per channel*/
804 i = 0;
805 for (y = 0 ; y < h ; y++) {
806 for (x = 0 ; x < w ; x++) {
807 unsigned value = readBitsFromReversedStream(&bp, in, infoIn->bitDepth);
808 value = (value * 255) / ((1 << infoIn->bitDepth) - 1); /*scale value from 0 to 255*/
809 unsigned char r = (unsigned char)value;
810 unsigned char g = (unsigned char)value;
811 unsigned char b = (unsigned char)value;
812 out[i++] = LCD_RGBPACK(r,g,b);
817 else return 59;
819 return 0;
822 /*Paeth predicter, used by PNG filter type 4*/
823 static int paethPredictor(int a, int b, int c)
825 int p = a + b - c;
826 int pa = p > a ? p - a : a - p;
827 int pb = p > b ? p - b : b - p;
828 int pc = p > c ? p - c : c - p;
830 if (pa <= pb && pa <= pc) return a;
831 else if (pb <= pc) return b;
832 else return c;
835 /*shared values used by multiple Adam7 related functions*/
837 static const unsigned ADAM7_IX[7] = { 0, 4, 0, 2, 0, 1, 0 }; /*x start values*/
838 static const unsigned ADAM7_IY[7] = { 0, 0, 4, 0, 2, 0, 1 }; /*y start values*/
839 static const unsigned ADAM7_DX[7] = { 8, 8, 4, 4, 2, 2, 1 }; /*x delta values*/
840 static const unsigned ADAM7_DY[7] = { 8, 8, 8, 4, 4, 2, 2 }; /*y delta values*/
842 static void Adam7_getpassvalues(unsigned passw[7], unsigned passh[7], size_t filter_passstart[8], size_t padded_passstart[8], size_t passstart[8], unsigned w, unsigned h, unsigned bpp)
844 /*the passstart values have 8 values: the 8th one actually indicates the byte after the end of the 7th (= last) pass*/
845 unsigned i;
847 /*calculate width and height in pixels of each pass*/
848 for (i = 0; i < 7; i++)
850 passw[i] = (w + ADAM7_DX[i] - ADAM7_IX[i] - 1) / ADAM7_DX[i];
851 passh[i] = (h + ADAM7_DY[i] - ADAM7_IY[i] - 1) / ADAM7_DY[i];
852 if (passw[i] == 0) passh[i] = 0;
853 if (passh[i] == 0) passw[i] = 0;
856 filter_passstart[0] = padded_passstart[0] = passstart[0] = 0;
857 for (i = 0; i < 7; i++)
859 filter_passstart[i + 1] = filter_passstart[i] + ((passw[i] && passh[i]) ? passh[i] * (1 + (passw[i] * bpp + 7) / 8) : 0); /*if passw[i] is 0, it's 0 bytes, not 1 (no filtertype-byte)*/
860 padded_passstart[i + 1] = padded_passstart[i] + passh[i] * ((passw[i] * bpp + 7) / 8); /*bits padded if needed to fill full byte at end of each scanline*/
861 passstart[i + 1] = passstart[i] + (passh[i] * passw[i] * bpp + 7) / 8; /*only padded at end of reduced image*/
865 /* ////////////////////////////////////////////////////////////////////////// */
866 /* / PNG Decoder / */
867 /* ////////////////////////////////////////////////////////////////////////// */
869 /*read the information from the header and store it in the LodePNG_Info. return value is error*/
870 void LodePNG_inspect(LodePNG_Decoder* decoder, const unsigned char* in, size_t inlength)
872 if (inlength == 0 || in == 0) { decoder->error = 48; return; } /*the given data is empty*/
873 if (inlength < 29) { decoder->error = 27; return; } /*error: the data length is smaller than the length of the header*/
875 /*when decoding a new PNG image, make sure all parameters created after previous decoding are reset*/
876 LodePNG_InfoPng_cleanup(&decoder->infoPng);
877 LodePNG_InfoPng_init(&decoder->infoPng);
878 decoder->error = 0;
880 if (in[0] != 137 || in[1] != 80 || in[2] != 78 || in[3] != 71 || in[4] != 13 || in[5] != 10 || in[6] != 26 || in[7] != 10) { decoder->error = 28; return; } /*error: the first 8 bytes are not the correct PNG signature*/
881 if (in[12] != 'I' || in[13] != 'H' || in[14] != 'D' || in[15] != 'R') { decoder->error = 29; return; } /*error: it doesn't start with a IHDR chunk!*/
883 /*read the values given in the header*/
884 decoder->infoPng.width = LodePNG_read32bitInt(&in[16]);
885 decoder->infoPng.height = LodePNG_read32bitInt(&in[20]);
886 decoder->infoPng.color.bitDepth = in[24];
887 decoder->infoPng.color.colorType = in[25];
888 decoder->infoPng.compressionMethod = in[26];
889 decoder->infoPng.filterMethod = in[27];
890 decoder->infoPng.interlaceMethod = in[28];
892 unsigned CRC = LodePNG_read32bitInt(&in[29]);
893 unsigned checksum = crc32(0L, &in[12], 17);
894 if (CRC != checksum) { decoder->error = 57; return; }
896 if (decoder->infoPng.compressionMethod != 0) { decoder->error = 32; return; } /*error: only compression method 0 is allowed in the specification*/
897 if (decoder->infoPng.filterMethod != 0) { decoder->error = 33; return; } /*error: only filter method 0 is allowed in the specification*/
898 if (decoder->infoPng.interlaceMethod > 1) { decoder->error = 34; return; } /*error: only interlace methods 0 and 1 exist in the specification*/
900 decoder->error = checkColorValidity(decoder->infoPng.color.colorType, decoder->infoPng.color.bitDepth);
903 static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scanline, const unsigned char* precon, size_t bytewidth, unsigned char filterType, size_t length)
906 For PNG filter method 0
907 unfilter a PNG image scanline by scanline. when the pixels are smaller than 1 byte, the filter works byte per byte (bytewidth = 1)
908 precon is the previous unfiltered scanline, recon the result, scanline the current one
909 the incoming scanlines do NOT include the filtertype byte, that one is given in the parameter filterType instead
910 recon and scanline MAY be the same memory address! precon must be disjoint.
913 size_t i;
914 switch (filterType)
916 case 0:
917 //for(i = 0; i < length; i++) recon[i] = scanline[i];
918 memcpy(recon, scanline, length * sizeof(unsigned char));
919 break;
920 case 1:
921 //for(i = 0; i < bytewidth; i++) recon[i] = scanline[i];
922 memcpy(recon, scanline, bytewidth * sizeof(unsigned char));
923 for (i = bytewidth; i < length; i++) recon[i] = scanline[i] + recon[i - bytewidth];
924 break;
925 case 2:
926 if (precon) for (i = 0; i < length; i++) recon[i] = scanline[i] + precon[i];
927 else //for(i = 0; i < length; i++) recon[i] = scanline[i];
928 memcpy(recon, scanline, length * sizeof(unsigned char));
929 break;
930 case 3:
931 if (precon)
933 for (i = 0; i < bytewidth; i++) recon[i] = scanline[i] + precon[i] / 2;
934 for (i = bytewidth; i < length; i++) recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) / 2);
936 else
938 //for(i = 0; i < bytewidth; i++) recon[i] = scanline[i];
939 memcpy(recon, scanline, bytewidth * sizeof(unsigned char));
940 for (i = bytewidth; i < length; i++) recon[i] = scanline[i] + recon[i - bytewidth] / 2;
942 break;
943 case 4:
944 if (precon)
946 for (i = 0; i < bytewidth; i++) recon[i] = (unsigned char)(scanline[i] + paethPredictor(0, precon[i], 0));
947 for (i = bytewidth; i < length; i++) recon[i] = (unsigned char)(scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[i - bytewidth]));
949 else
951 //for(i = 0; i < bytewidth; i++) recon[i] = scanline[i];
952 memcpy(recon, scanline, bytewidth * sizeof(unsigned char));
953 for (i = bytewidth; i < length; i++) recon[i] = (unsigned char)(scanline[i] + paethPredictor(recon[i - bytewidth], 0, 0));
955 break;
956 default:
957 return 36; /*error: unexisting filter type given*/
959 return 0;
962 static unsigned unfilter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp)
965 For PNG filter method 0
966 this function unfilters a single image (e.g. without interlacing this is called once, with Adam7 it's called 7 times)
967 out must have enough bytes allocated already, in must have the scanlines + 1 filtertype byte per scanline
968 w and h are image dimensions or dimensions of reduced image, bpp is bits per pixel
969 in and out are allowed to be the same memory address!
972 unsigned y;
973 unsigned char* prevline = 0;
975 size_t bytewidth = (bpp + 7) / 8; /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/
976 size_t linebytes = (w * bpp + 7) / 8;
978 for (y = 0; y < h; y++)
980 size_t outindex = linebytes * y;
981 size_t inindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/
982 unsigned char filterType = in[inindex];
984 unsigned error = unfilterScanline(&out[outindex], &in[inindex + 1], prevline, bytewidth, filterType, linebytes);
985 if (error) return error;
987 prevline = &out[outindex];
990 return 0;
993 static void Adam7_deinterlace(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp)
995 /*Note: this function works on image buffers WITHOUT padding bits at end of scanlines with non-multiple-of-8 bit amounts, only between reduced images is padding
996 out must be big enough AND must be 0 everywhere if bpp < 8 in the current implementation (because that's likely a little bit faster)*/
997 unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8];
998 unsigned i;
1000 Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp);
1002 if (bpp >= 8)
1004 for (i = 0; i < 7; i++)
1006 unsigned x, y, b;
1007 size_t bytewidth = bpp / 8;
1008 for (y = 0; y < passh[i]; y++)
1009 for (x = 0; x < passw[i]; x++)
1011 size_t pixelinstart = passstart[i] + (y * passw[i] + x) * bytewidth;
1012 size_t pixeloutstart = ((ADAM7_IY[i] + y * ADAM7_DY[i]) * w + ADAM7_IX[i] + x * ADAM7_DX[i]) * bytewidth;
1013 for (b = 0; b < bytewidth; b++)
1015 out[pixeloutstart + b] = in[pixelinstart + b];
1020 else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/
1022 for (i = 0; i < 7; i++)
1024 unsigned x, y, b;
1025 unsigned ilinebits = bpp * passw[i];
1026 unsigned olinebits = bpp * w;
1027 size_t obp, ibp; /*bit pointers (for out and in buffer)*/
1028 for (y = 0; y < passh[i]; y++)
1029 for (x = 0; x < passw[i]; x++)
1031 ibp = (8 * passstart[i]) + (y * ilinebits + x * bpp);
1032 obp = (ADAM7_IY[i] + y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + x * ADAM7_DX[i]) * bpp;
1033 for (b = 0; b < bpp; b++)
1035 unsigned char bit = readBitFromReversedStream(&ibp, in);
1036 setBitOfReversedStream0(&obp, out, bit); /*note that this function assumes the out buffer is completely 0, use setBitOfReversedStream otherwise*/
1043 static void removePaddingBits(unsigned char* out, const unsigned char* in, size_t olinebits, size_t ilinebits, unsigned h)
1046 After filtering there are still padding bits if scanlines have non multiple of 8 bit amounts. They need to be removed (except at last scanline of (Adam7-reduced) image) before working with pure image buffers for the Adam7 code, the color convert code and the output to the user.
1047 in and out are allowed to be the same buffer, in may also be higher but still overlapping; in must have >= ilinebits*h bits, out must have >= olinebits*h bits, olinebits must be <= ilinebits
1048 also used to move bits after earlier such operations happened, e.g. in a sequence of reduced images from Adam7
1049 only useful if (ilinebits - olinebits) is a value in the range 1..7
1051 unsigned y;
1052 size_t diff = ilinebits - olinebits;
1053 size_t obp = 0, ibp = 0; /*bit pointers*/
1054 for (y = 0; y < h; y++)
1056 size_t x;
1057 for (x = 0; x < olinebits; x++)
1059 unsigned char bit = readBitFromReversedStream(&ibp, in);
1060 setBitOfReversedStream(&obp, out, bit);
1062 ibp += diff;
1066 /*out must be buffer big enough to contain full image, and in must contain the full decompressed data from the IDAT chunks*/
1067 static unsigned postProcessScanlines(unsigned char* out, unsigned char* in, const LodePNG_Decoder* decoder) /*return value is error*/
1070 This function converts the filtered-padded-interlaced data into pure 2D image buffer with the PNG's colortype. Steps:
1071 *) if no Adam7: 1) unfilter 2) remove padding bits (= posible extra bits per scanline if bpp < 8)
1072 *) if adam7: 1) 7x unfilter 2) 7x remove padding bits 3) Adam7_deinterlace
1073 NOTE: the in buffer will be overwritten with intermediate data!
1075 unsigned bpp = LodePNG_InfoColor_getBpp(&decoder->infoPng.color);
1076 unsigned w = decoder->infoPng.width;
1077 unsigned h = decoder->infoPng.height;
1078 unsigned error = 0;
1079 if (bpp == 0) return 31; /*error: invalid colortype*/
1081 if (decoder->infoPng.interlaceMethod == 0)
1083 if (bpp < 8 && w * bpp != ((w * bpp + 7) / 8) * 8)
1085 error = unfilter(in, in, w, h, bpp);
1086 if (error) return error;
1087 removePaddingBits(out, in, w * bpp, ((w * bpp + 7) / 8) * 8, h);
1089 else error = unfilter(out, in, w, h, bpp); /*we can immediatly filter into the out buffer, no other steps needed*/
1091 else /*interlaceMethod is 1 (Adam7)*/
1093 unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8];
1094 unsigned i;
1096 Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp);
1098 for (i = 0; i < 7; i++)
1100 error = unfilter(&in[padded_passstart[i]], &in[filter_passstart[i]], passw[i], passh[i], bpp);
1101 if (error) return error;
1102 if (bpp < 8) /*TODO: possible efficiency improvement: if in this reduced image the bits fit nicely in 1 scanline, move bytes instead of bits or move not at all*/
1104 /*remove padding bits in scanlines; after this there still may be padding bits between the different reduced images: each reduced image still starts nicely at a byte*/
1105 removePaddingBits(&in[passstart[i]], &in[padded_passstart[i]], passw[i] * bpp, ((passw[i] * bpp + 7) / 8) * 8, passh[i]);
1109 Adam7_deinterlace(out, in, w, h, bpp);
1112 return error;
1115 /*read a PNG, the result will be in the same color type as the PNG (hence "generic")*/
1116 static void decodeGeneric(LodePNG_Decoder* decoder, unsigned char* in, size_t size, void (*pf_progress)(int current, int total))
1118 if (pf_progress != NULL)
1119 pf_progress(0, 100);
1120 unsigned char IEND = 0;
1121 const unsigned char* chunk;
1122 size_t i;
1123 unsigned char *idat = memory;
1124 size_t idat_size = 0;
1126 /*for unknown chunk order*/
1127 unsigned unknown = 0;
1128 unsigned critical_pos = 1; /*1 = after IHDR, 2 = after PLTE, 3 = after IDAT*/
1130 /*provide some proper output values if error will happen*/
1131 decoded_image_size = 0;
1133 if (size == 0 || in == 0) { decoder->error = 48; return; } /*the given data is empty*/
1135 LodePNG_inspect(decoder, in, size); /*reads header and resets other parameters in decoder->infoPng*/
1136 if (decoder->error) return;
1138 chunk = &in[33]; /*first byte of the first chunk after the header*/
1140 while (!IEND) /*loop through the chunks, ignoring unknown chunks and stopping at IEND chunk. IDAT data is put at the start of the in buffer*/
1142 unsigned chunkLength;
1143 const unsigned char* data; /*the data in the chunk*/
1145 if ((size_t)((chunk - in) + 12) > size || chunk < in) { decoder->error = 30; break; } /*error: size of the in buffer too small to contain next chunk*/
1146 chunkLength = LodePNG_chunk_length(chunk); /*length of the data of the chunk, excluding the length bytes, chunk type and CRC bytes*/
1147 if (chunkLength > 2147483647) { decoder->error = 63; break; }
1148 if ((size_t)((chunk - in) + chunkLength + 12) > size || (chunk + chunkLength + 12) < in) { decoder->error = 35; break; } /*error: size of the in buffer too small to contain next chunk*/
1149 data = LodePNG_chunk_data_const(chunk);
1151 /*IDAT chunk, containing compressed image data*/
1152 if (LodePNG_chunk_type_equals(chunk, "IDAT"))
1154 size_t oldsize = idat_size;
1155 idat_size += chunkLength;
1156 if (idat + idat_size >= image) { decoder->error = OUT_OF_MEMORY; break; }
1157 memcpy(idat+oldsize, data, chunkLength * sizeof(unsigned char));
1158 critical_pos = 3;
1160 /*IEND chunk*/
1161 else if (LodePNG_chunk_type_equals(chunk, "IEND"))
1163 IEND = 1;
1165 /*palette chunk (PLTE)*/
1166 else if (LodePNG_chunk_type_equals(chunk, "PLTE"))
1168 unsigned pos = 0;
1169 decoder->infoPng.color.palettesize = chunkLength / 3;
1170 if (decoder->infoPng.color.palettesize > 256) { decoder->error = 38; break; } /*error: palette too big*/
1171 for (i = 0; i < decoder->infoPng.color.palettesize; i++)
1173 decoder->infoPng.color.palette[4 * i + 0] = data[pos++]; /*R*/
1174 decoder->infoPng.color.palette[4 * i + 1] = data[pos++]; /*G*/
1175 decoder->infoPng.color.palette[4 * i + 2] = data[pos++]; /*B*/
1176 decoder->infoPng.color.palette[4 * i + 3] = 255; /*alpha*/
1178 critical_pos = 2;
1180 /*palette transparency chunk (tRNS)*/
1181 else if (LodePNG_chunk_type_equals(chunk, "tRNS"))
1183 if (decoder->infoPng.color.colorType == 3)
1185 if (chunkLength > decoder->infoPng.color.palettesize) { decoder->error = 39; break; } /*error: more alpha values given than there are palette entries*/
1186 for (i = 0; i < chunkLength; i++) decoder->infoPng.color.palette[4 * i + 3] = data[i];
1188 else if (decoder->infoPng.color.colorType == 0)
1190 if (chunkLength != 2) { decoder->error = 40; break; } /*error: this chunk must be 2 bytes for greyscale image*/
1191 decoder->infoPng.color.key_defined = 1;
1192 decoder->infoPng.color.key_r = decoder->infoPng.color.key_g = decoder->infoPng.color.key_b = 256 * data[0] + data[1];
1194 else if (decoder->infoPng.color.colorType == 2)
1196 if (chunkLength != 6) { decoder->error = 41; break; } /*error: this chunk must be 6 bytes for RGB image*/
1197 decoder->infoPng.color.key_defined = 1;
1198 decoder->infoPng.color.key_r = 256 * data[0] + data[1];
1199 decoder->infoPng.color.key_g = 256 * data[2] + data[3];
1200 decoder->infoPng.color.key_b = 256 * data[4] + data[5];
1202 else { decoder->error = 42; break; } /*error: tRNS chunk not allowed for other color models*/
1204 /*background color chunk (bKGD)*/
1205 else if (LodePNG_chunk_type_equals(chunk, "bKGD"))
1207 if (decoder->infoPng.color.colorType == 3)
1209 if (chunkLength != 1) { decoder->error = 43; break; } /*error: this chunk must be 1 byte for indexed color image*/
1210 decoder->infoPng.background_defined = 1;
1211 decoder->infoPng.background_r = decoder->infoPng.background_g = decoder->infoPng.background_g = data[0];
1213 else if (decoder->infoPng.color.colorType == 0 || decoder->infoPng.color.colorType == 4)
1215 if (chunkLength != 2) { decoder->error = 44; break; } /*error: this chunk must be 2 bytes for greyscale image*/
1216 decoder->infoPng.background_defined = 1;
1217 decoder->infoPng.background_r = decoder->infoPng.background_g = decoder->infoPng.background_b = 256 * data[0] + data[1];
1219 else if (decoder->infoPng.color.colorType == 2 || decoder->infoPng.color.colorType == 6)
1221 if (chunkLength != 6) { decoder->error = 45; break; } /*error: this chunk must be 6 bytes for greyscale image*/
1222 decoder->infoPng.background_defined = 1;
1223 decoder->infoPng.background_r = 256 * data[0] + data[1];
1224 decoder->infoPng.background_g = 256 * data[2] + data[3];
1225 decoder->infoPng.background_b = 256 * data[4] + data[5];
1228 else if (LodePNG_chunk_type_equals(chunk, "tIME"))
1230 if (chunkLength != 7) { decoder->error = 73; break; }
1231 decoder->infoPng.time_defined = 1;
1232 decoder->infoPng.time.year = 256 * data[0] + data[+ 1];
1233 decoder->infoPng.time.month = data[2];
1234 decoder->infoPng.time.day = data[3];
1235 decoder->infoPng.time.hour = data[4];
1236 decoder->infoPng.time.minute = data[5];
1237 decoder->infoPng.time.second = data[6];
1239 else if (LodePNG_chunk_type_equals(chunk, "pHYs"))
1241 if (chunkLength != 9) { decoder->error = 74; break; }
1242 decoder->infoPng.phys_defined = 1;
1243 decoder->infoPng.phys_x = 16777216 * data[0] + 65536 * data[1] + 256 * data[2] + data[3];
1244 decoder->infoPng.phys_y = 16777216 * data[4] + 65536 * data[5] + 256 * data[6] + data[7];
1245 decoder->infoPng.phys_unit = data[8];
1247 else /*it's not an implemented chunk type, so ignore it: skip over the data*/
1249 if (LodePNG_chunk_critical(chunk)) { decoder->error = 69; break; } /*error: unknown critical chunk (5th bit of first byte of chunk type is 0)*/
1250 unknown = 1;
1253 if (!unknown) /*check CRC if wanted, only on known chunk types*/
1255 long time = *rb->current_tick;
1256 if (LodePNG_chunk_check_crc(chunk)) { decoder->error = 57; break; }
1257 time = *rb->current_tick-time;
1260 if (!IEND) chunk = LodePNG_chunk_next_const(chunk);
1263 if (!decoder->error)
1265 unsigned char *scanlines = idat + idat_size;
1266 size_t scanlines_size = (size_t)memory_max - idat_size + 1;
1267 long time = *rb->current_tick;
1268 decoder->error = LodePNG_decompress(scanlines, &scanlines_size, idat, idat_size, decoder->error_msg); /*decompress with the Zlib decompressor*/
1269 if (pf_progress) pf_progress(100, 100);
1270 time = *rb->current_tick-time;
1272 if (!decoder->error)
1274 decoded_image_size = (decoder->infoPng.height * decoder->infoPng.width * LodePNG_InfoColor_getBpp(&decoder->infoPng.color) + 7) / 8;
1275 if (decoded_image_size > memory_size) { decoder->error = OUT_OF_MEMORY; return; }
1276 decoded_image = memory_max - decoded_image_size + 1;
1277 if (scanlines + scanlines_size >= decoded_image) { decoder->error = OUT_OF_MEMORY; return; }
1278 memset(decoded_image, 0, decoded_image_size * sizeof(unsigned char));
1279 if (!running_slideshow)
1281 rb->snprintf(print, sizeof(print), "unfiltering scanlines");
1282 rb->lcd_puts(0, 3, print);
1283 rb->lcd_update();
1285 decoder->error = postProcessScanlines(decoded_image, scanlines, decoder);
1290 void LodePNG_decode(LodePNG_Decoder* decoder, unsigned char* in, size_t insize, void (*pf_progress)(int current, int total))
1292 decodeGeneric(decoder, in, insize, pf_progress);
1293 if (decoder->error) return;
1295 /*TODO: check if this works according to the statement in the documentation: "The converter can convert from greyscale input color type, to 8-bit greyscale or greyscale with alpha"*/
1296 if (!(decoder->infoRaw.color.colorType == 2 || decoder->infoRaw.color.colorType == 6) && !(decoder->infoRaw.color.bitDepth == 8)) { decoder->error = 56; return; }
1297 converted_image = (fb_data *)((intptr_t)(memory + 3) & ~3);
1298 converted_image_size = decoder->infoPng.width*decoder->infoPng.height;
1299 if ((unsigned char *)(converted_image + converted_image_size) >= decoded_image) { decoder->error = OUT_OF_MEMORY; }
1300 if (!decoder->error) decoder->error = LodePNG_convert(converted_image, decoded_image, &decoder->infoRaw.color, &decoder->infoPng.color, decoder->infoPng.width, decoder->infoPng.height);
1303 void LodePNG_DecodeSettings_init(LodePNG_DecodeSettings* settings)
1305 settings->color_convert = 1;
1308 void LodePNG_Decoder_init(LodePNG_Decoder* decoder)
1310 LodePNG_DecodeSettings_init(&decoder->settings);
1311 LodePNG_InfoRaw_init(&decoder->infoRaw);
1312 LodePNG_InfoPng_init(&decoder->infoPng);
1313 decoder->error = 1;
1316 void LodePNG_Decoder_cleanup(LodePNG_Decoder* decoder)
1318 LodePNG_InfoRaw_cleanup(&decoder->infoRaw);
1319 LodePNG_InfoPng_cleanup(&decoder->infoPng);
1322 #define PNG_ERROR_MIN 27
1323 #define PNG_ERROR_MAX 74
1324 static const unsigned char *png_error_messages[PNG_ERROR_MAX-PNG_ERROR_MIN+1] =
1326 "png file smaller than a png header", /*27*/
1327 "incorrect png signature", /*28*/
1328 "first chunk is not IHDR", /*29*/
1329 "chunk length too large", /*30*/
1330 "illegal PNG color type or bpp", /*31*/
1331 "illegal PNG compression method", /*32*/
1332 "illegal PNG filter method", /*33*/
1333 "illegal PNG interlace method", /*34*/
1334 "chunk length of a chunk is too large or the chunk too small", /*35*/
1335 "illegal PNG filter type encountered", /*36*/
1336 "illegal bit depth for this color type given", /*37*/
1337 "the palette is too big (more than 256 colors)", /*38*/
1338 "more palette alpha values given in tRNS, than there are colors in the palette", /*39*/
1339 "tRNS chunk has wrong size for greyscale image", /*40*/
1340 "tRNS chunk has wrong size for RGB image", /*41*/
1341 "tRNS chunk appeared while it was not allowed for this color type", /*42*/
1342 "bKGD chunk has wrong size for palette image", /*43*/
1343 "bKGD chunk has wrong size for greyscale image", /*44*/
1344 "bKGD chunk has wrong size for RGB image", /*45*/
1345 "value encountered in indexed image is larger than the palette size", /*46*/
1346 "value encountered in indexed image is larger than the palette size", /*47*/
1347 "input file is empty", /*48*/
1348 NULL, /*49*/
1349 NULL, /*50*/
1350 NULL, /*51*/
1351 NULL, /*52*/
1352 NULL, /*53*/
1353 NULL, /*54*/
1354 NULL, /*55*/
1355 NULL, /*56*/
1356 "invalid CRC", /*57*/
1357 NULL, /*58*/
1358 "conversion to unexisting or unsupported color type or bit depth", /*59*/
1359 NULL, /*60*/
1360 NULL, /*61*/
1361 NULL, /*62*/
1362 "png chunk too long", /*63*/
1363 NULL, /*64*/
1364 NULL, /*65*/
1365 NULL, /*66*/
1366 NULL, /*67*/
1367 NULL, /*68*/
1368 "unknown critical chunk", /*69*/
1369 NULL, /*70*/
1370 NULL, /*71*/
1371 NULL, /*72*/
1372 "invalid tIME chunk size", /*73*/
1373 "invalid pHYs chunk size", /*74*/
1376 bool png_ext(const char ext[])
1378 if (!ext)
1379 return false;
1380 if (!rb->strcasecmp(ext,".png"))
1381 return true;
1382 else
1383 return false;
1386 /*Read directory contents for scrolling. */
1387 void get_pic_list(void)
1389 int i;
1390 struct entry *dircache;
1391 char *pname;
1392 tree = rb->tree_get_context();
1393 dircache = tree->dircache;
1395 file_pt = (char **) memory;
1397 /* Remove path and leave only the name.*/
1398 pname = rb->strrchr(np_file,'/');
1399 pname++;
1401 for (i = 0; i < tree->filesindir; i++)
1403 if (!(dircache[i].attr & ATTR_DIRECTORY)
1404 && png_ext(rb->strrchr(dircache[i].name, '.')))
1406 file_pt[entries] = dircache[i].name;
1407 /* Set Selected File. */
1408 if (!rb->strcmp(file_pt[entries], pname))
1409 curfile = entries;
1410 entries++;
1414 memory += (entries * sizeof(char**));
1415 memory_size -= (entries * sizeof(char**));
1418 int change_filename(int direct)
1420 bool file_erased = (file_pt[curfile] == NULL);
1421 direction = direct;
1423 curfile += (direct == DIR_PREV? entries - 1: 1);
1424 if (curfile >= entries)
1425 curfile -= entries;
1427 if (file_erased)
1429 /* remove 'erased' file names from list. */
1430 int count, i;
1431 for (count = i = 0; i < entries; i++)
1433 if (curfile == i)
1434 curfile = count;
1435 if (file_pt[i] != NULL)
1436 file_pt[count++] = file_pt[i];
1438 entries = count;
1441 if (entries == 0)
1443 rb->splash(HZ, "No supported files");
1444 return PLUGIN_ERROR;
1447 if (rb->strlen(tree->currdir) > 1)
1449 rb->strcpy(np_file, tree->currdir);
1450 rb->strcat(np_file, "/");
1452 else
1453 rb->strcpy(np_file, tree->currdir);
1455 rb->strcat(np_file, file_pt[curfile]);
1457 return PLUGIN_OTHER;
1460 /* switch off overlay, for handling SYS_ events */
1461 void cleanup(void *parameter)
1463 (void)parameter;
1466 #define VSCROLL (LCD_HEIGHT/8)
1467 #define HSCROLL (LCD_WIDTH/10)
1469 #define ZOOM_IN 100 /* return codes for below function */
1470 #define ZOOM_OUT 101
1472 int show_menu(void) /* return 1 to quit */
1474 #if LCD_DEPTH > 1
1475 rb->lcd_set_backdrop(old_backdrop);
1476 #ifdef HAVE_LCD_COLOR
1477 rb->lcd_set_foreground(rb->global_settings->fg_color);
1478 rb->lcd_set_background(rb->global_settings->bg_color);
1479 #else
1480 rb->lcd_set_foreground(LCD_BLACK);
1481 rb->lcd_set_background(LCD_WHITE);
1482 #endif
1483 #endif
1484 int result;
1486 enum menu_id
1488 MIID_RETURN = 0,
1489 MIID_TOGGLE_SS_MODE,
1490 MIID_CHANGE_SS_MODE,
1491 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
1492 MIID_SHOW_PLAYBACK_MENU,
1493 #endif
1494 MIID_QUIT,
1497 MENUITEM_STRINGLIST(menu, "Png Menu", NULL,
1498 "Return", "Toggle Slideshow Mode",
1499 "Change Slideshow Time",
1500 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
1501 "Show Playback Menu",
1502 #endif
1503 "Quit");
1505 static const struct opt_items slideshow[2] = {
1506 { "Disable", -1 },
1507 { "Enable", -1 },
1510 result=rb->do_menu(&menu, NULL, NULL, false);
1512 switch (result)
1514 case MIID_RETURN:
1515 break;
1516 case MIID_TOGGLE_SS_MODE:
1517 rb->set_option("Toggle Slideshow", &slideshow_enabled, INT,
1518 slideshow , 2, NULL);
1519 break;
1520 case MIID_CHANGE_SS_MODE:
1521 rb->set_int("Slideshow Time", "s", UNIT_SEC,
1522 &png_settings.ss_timeout, NULL, 1,
1523 SS_MIN_TIMEOUT, SS_MAX_TIMEOUT, NULL);
1524 break;
1525 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
1526 case MIID_SHOW_PLAYBACK_MENU:
1527 if (plug_buf)
1529 playback_control(NULL);
1531 else
1533 rb->splash(HZ, "Cannot restart playback");
1535 break;
1536 #endif
1537 case MIID_QUIT:
1538 return 1;
1539 break;
1542 #if !defined(SIMULATOR) && defined(HAVE_DISK_STORAGE)
1543 /* change ata spindown time based on slideshow time setting */
1544 immediate_ata_off = false;
1545 rb->storage_spindown(rb->global_settings->disk_spindown);
1547 if (slideshow_enabled)
1549 if (png_settings.ss_timeout < 10)
1551 /* slideshow times < 10s keep disk spinning */
1552 rb->storage_spindown(0);
1554 else if (!rb->mp3_is_playing())
1556 /* slideshow times > 10s and not playing: ata_off after load */
1557 immediate_ata_off = true;
1560 #endif
1561 #if LCD_DEPTH > 1
1562 rb->lcd_set_backdrop(NULL);
1563 rb->lcd_set_foreground(LCD_WHITE);
1564 rb->lcd_set_background(LCD_BLACK);
1565 #endif
1566 rb->lcd_clear_display();
1567 return 0;
1570 void draw_image(struct LodePNG_Decoder* decoder)
1572 rb->lcd_bitmap_part(resized_image, decoder->x, decoder->y, decoder->width,
1573 MAX(0, (LCD_WIDTH - decoder->width) / 2),
1574 MAX(0, (LCD_HEIGHT - decoder->height) / 2),
1575 decoder->width - decoder->x,
1576 decoder->height - decoder->y);
1579 /* Pan the viewing window right - move image to the left and fill in
1580 the right-hand side */
1581 static void pan_view_right(struct LodePNG_Decoder* decoder)
1583 int move;
1585 move = MIN(HSCROLL, decoder->width - decoder->x - LCD_WIDTH);
1586 if (move > 0)
1588 decoder->x += move;
1589 draw_image(decoder);
1590 rb->lcd_update();
1594 /* Pan the viewing window left - move image to the right and fill in
1595 the left-hand side */
1596 static void pan_view_left(struct LodePNG_Decoder* decoder)
1598 int move;
1600 move = MIN(HSCROLL, decoder->x);
1601 if (move > 0)
1603 decoder->x -= move;
1604 draw_image(decoder);
1605 rb->lcd_update();
1610 /* Pan the viewing window up - move image down and fill in
1611 the top */
1612 static void pan_view_up(struct LodePNG_Decoder* decoder)
1614 int move;
1616 move = MIN(VSCROLL, decoder->y);
1617 if (move > 0)
1619 decoder->y -= move;
1620 draw_image(decoder);
1621 rb->lcd_update();
1625 /* Pan the viewing window down - move image up and fill in
1626 the bottom */
1627 static void pan_view_down(struct LodePNG_Decoder* decoder)
1629 int move;
1631 move = MIN(VSCROLL, decoder->height - decoder->y - LCD_HEIGHT);
1632 if (move > 0)
1634 decoder->y += move;
1635 draw_image(decoder);
1636 rb->lcd_update();
1640 /* interactively scroll around the image */
1641 int scroll_bmp(struct LodePNG_Decoder* decoder)
1643 int button;
1644 int lastbutton = 0;
1646 while (true)
1648 if (slideshow_enabled)
1649 button = rb->button_get_w_tmo(png_settings.ss_timeout * HZ);
1650 else
1651 button = rb->button_get(true);
1653 running_slideshow = false;
1655 switch (button)
1657 case PNG_LEFT:
1658 if (entries > 1 && decoder->width <= LCD_WIDTH
1659 && decoder->height <= LCD_HEIGHT)
1660 return change_filename(DIR_PREV);
1661 case PNG_LEFT | BUTTON_REPEAT:
1662 pan_view_left(decoder);
1663 break;
1665 case PNG_RIGHT:
1666 if (entries > 1 && decoder->width <= LCD_WIDTH
1667 && decoder->height <= LCD_HEIGHT)
1668 return change_filename(DIR_NEXT);
1669 case PNG_RIGHT | BUTTON_REPEAT:
1670 pan_view_right(decoder);
1671 break;
1673 case PNG_UP:
1674 case PNG_UP | BUTTON_REPEAT:
1675 pan_view_up(decoder);
1676 break;
1678 case PNG_DOWN:
1679 case PNG_DOWN | BUTTON_REPEAT:
1680 pan_view_down(decoder);
1681 break;
1683 case BUTTON_NONE:
1684 if (!slideshow_enabled)
1685 break;
1686 running_slideshow = true;
1687 if (entries > 1)
1688 return change_filename(DIR_NEXT);
1689 break;
1691 #ifdef PNG_SLIDE_SHOW
1692 case PNG_SLIDE_SHOW:
1693 slideshow_enabled = !slideshow_enabled;
1694 running_slideshow = slideshow_enabled;
1695 break;
1696 #endif
1698 #ifdef PNG_NEXT_REPEAT
1699 case PNG_NEXT_REPEAT:
1700 #endif
1701 case PNG_NEXT:
1702 if (entries > 1)
1703 return change_filename(DIR_NEXT);
1704 break;
1706 #ifdef PNG_PREVIOUS_REPEAT
1707 case PNG_PREVIOUS_REPEAT:
1708 #endif
1709 case PNG_PREVIOUS:
1710 if (entries > 1)
1711 return change_filename(DIR_PREV);
1712 break;
1714 case PNG_ZOOM_IN:
1715 #ifdef PNG_ZOOM_PRE
1716 if (lastbutton != PNG_ZOOM_PRE)
1717 break;
1718 #endif
1719 return ZOOM_IN;
1720 break;
1722 case PNG_ZOOM_OUT:
1723 #ifdef PNG_ZOOM_PRE
1724 if (lastbutton != PNG_ZOOM_PRE)
1725 break;
1726 #endif
1727 return ZOOM_OUT;
1728 break;
1730 #ifdef PNG_RC_MENU
1731 case PNG_RC_MENU:
1732 #endif
1733 case PNG_MENU:
1735 if (show_menu() == 1)
1736 return PLUGIN_OK;
1738 draw_image(decoder);
1739 rb->lcd_update();
1741 break;
1742 default:
1743 if (rb->default_event_handler_ex(button, cleanup, NULL)
1744 == SYS_USB_CONNECTED)
1745 return PLUGIN_USB_CONNECTED;
1746 break;
1748 } /* switch */
1750 if (button != BUTTON_NONE)
1751 lastbutton = button;
1752 } /* while (true) */
1755 /* callback updating a progress meter while PNG decoding */
1756 void cb_progress(int current, int total)
1759 if (current & 1) rb->yield(); /* be nice to the other threads */
1760 if (!running_slideshow)
1762 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],
1763 0, LCD_HEIGHT-8, LCD_WIDTH, 8,
1764 total, 0, current, HORIZONTAL);
1765 rb->lcd_update_rect(0, LCD_HEIGHT-8, LCD_WIDTH, 8);
1767 else
1769 /* in slideshow mode, keep gui interference to a minimum */
1770 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],
1771 0, LCD_HEIGHT-4, LCD_WIDTH, 4,
1772 total, 0, current, HORIZONTAL);
1773 rb->lcd_update_rect(0, LCD_HEIGHT-4, LCD_WIDTH, 4);
1777 int pngmem(struct LodePNG_Decoder* decoder, int ds)
1779 return (decoder->infoPng.width/ds) * (decoder->infoPng.height/ds) * FB_DATA_SZ;
1782 /* how far can we zoom in without running out of memory */
1783 int min_downscale(struct LodePNG_Decoder* decoder, int bufsize)
1785 int downscale = 8;
1787 if (pngmem(decoder, 8) > bufsize)
1788 return 0; /* error, too large, even 1:8 doesn't fit */
1790 while (downscale > 1 && pngmem(decoder, downscale/2) <= bufsize)
1791 downscale /= 2;
1793 return downscale;
1796 /* how far can we zoom out, to fit image into the LCD */
1797 unsigned max_downscale(struct LodePNG_Decoder* decoder)
1799 unsigned downscale = 1;
1801 while (downscale < 8 && (decoder->infoPng.width/downscale > LCD_WIDTH
1802 || decoder->infoPng.height/downscale > LCD_HEIGHT))
1804 downscale *= 2;
1807 return downscale;
1810 /* load image from filename. */
1811 int load_image(char* filename, struct LodePNG_Decoder* decoder)
1813 int fd;
1814 long time = 0; /* measured ticks */
1815 int w, h; /* used to center output */
1817 fd = rb->open(filename, O_RDONLY);
1818 if (fd < 0)
1820 rb->splashf(HZ, "err opening %s:%d", filename, fd);
1821 return PLUGIN_ERROR;
1823 image_size = rb->filesize(fd);
1825 DEBUGF("reading file '%s'\n", filename);
1827 if (!running_slideshow) {
1828 rb->snprintf(print, sizeof(print), "%s:", rb->strrchr(filename,'/')+1);
1829 rb->lcd_puts(0, 0, print);
1830 rb->lcd_update();
1833 if (image_size > memory_size) {
1834 decoder->error = FILE_TOO_LARGE;
1835 rb->close(fd);
1836 #ifndef SIMULATOR
1837 if (running_slideshow && immediate_ata_off) {
1838 /* running slideshow and time is long enough: power down disk */
1839 rb->storage_sleep();
1841 #endif
1843 } else {
1844 if (!running_slideshow) {
1845 rb->snprintf(print, sizeof(print), "loading %lu bytes", image_size);
1846 rb->lcd_puts(0, 1, print);
1847 rb->lcd_update();
1850 image = memory_max - image_size + 1;
1851 rb->read(fd, image, image_size);
1852 rb->close(fd);
1854 if (!running_slideshow) {
1855 rb->snprintf(print, sizeof(print), "decoding image");
1856 rb->lcd_puts(0, 2, print);
1857 rb->lcd_update();
1859 #ifndef SIMULATOR
1860 else if (immediate_ata_off) {
1861 /* running slideshow and time is long enough: power down disk */
1862 rb->storage_sleep();
1864 #endif
1866 decoder->settings.color_convert = 1;
1867 decoder->infoRaw.color.colorType = 2;
1868 decoder->infoRaw.color.bitDepth = 8;
1870 LodePNG_inspect(decoder, image, image_size);
1872 if (!decoder->error) {
1874 if (!running_slideshow) {
1875 rb->snprintf(print, sizeof(print), "image %dx%d",
1876 decoder->infoPng.width, decoder->infoPng.height);
1877 rb->lcd_puts(0, 2, print);
1878 rb->lcd_update();
1880 rb->snprintf(print, sizeof(print), "decoding %d*%d",
1881 decoder->infoPng.width, decoder->infoPng.height);
1882 rb->lcd_puts(0, 3, print);
1883 rb->lcd_update();
1886 /* the actual decoding */
1887 time = *rb->current_tick;
1888 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1889 rb->cpu_boost(true);
1890 LodePNG_decode(decoder, image, image_size, cb_progress);
1891 rb->cpu_boost(false);
1892 #else
1893 LodePNG_decode(decoder, image, image_size, cb_progress);
1894 #endif /*HAVE_ADJUSTABLE_CPU_FREQ*/
1898 time = *rb->current_tick - time;
1900 if (!running_slideshow && !decoder->error)
1902 rb->snprintf(print, sizeof(print), " %ld.%02ld sec ", time/HZ, time%HZ);
1903 rb->lcd_getstringsize(print, &w, &h); /* centered in progress bar */
1904 rb->lcd_putsxy((LCD_WIDTH - w)/2, LCD_HEIGHT - h, print);
1905 rb->lcd_update();
1908 if (decoder->error) {
1909 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
1910 if (plug_buf && (decoder->error == FILE_TOO_LARGE
1911 || decoder->error == OUT_OF_MEMORY || decoder->error == Z_MEM_ERROR))
1912 return PLUGIN_OUTOFMEM;
1913 #endif
1915 if (decoder->error >= PNG_ERROR_MIN && decoder->error <= PNG_ERROR_MAX
1916 && png_error_messages[decoder->error-PNG_ERROR_MIN] != NULL)
1918 rb->splash(HZ, png_error_messages[decoder->error-PNG_ERROR_MIN]);
1920 else
1922 switch (decoder->error) {
1923 case PLUGIN_ABORT:
1924 break;
1925 case OUT_OF_MEMORY:
1926 case Z_MEM_ERROR:
1927 rb->splash(HZ, "Out of Memory");break;
1928 case FILE_TOO_LARGE:
1929 rb->splash(HZ, "File too large");break;
1930 case Z_DATA_ERROR:
1931 rb->splash(HZ, decoder->error_msg);break;
1932 default:
1933 rb->splashf(HZ, "other error : %ld", decoder->error);break;
1937 if (decoder->error == PLUGIN_ABORT)
1938 return PLUGIN_ABORT;
1939 else
1940 return PLUGIN_ERROR;
1942 return PLUGIN_OK;
1945 /* return decoded or cached image */
1946 fb_data *get_image(struct LodePNG_Decoder* decoder, int ds)
1948 fb_data * p_disp = disp[ds]; /* short cut */
1950 decoder->width = decoder->infoPng.width / ds;
1951 decoder->height = decoder->infoPng.height / ds;
1953 if (p_disp != NULL)
1955 DEBUGF("Found an image in cache\n");
1956 return p_disp; /* we still have it */
1959 /* assign image buffer */
1960 if (ds > 1) {
1961 if (!running_slideshow)
1963 rb->snprintf(print, sizeof(print), "resizing %d*%d",
1964 decoder->width, decoder->height);
1965 rb->lcd_puts(0, 3, print);
1966 rb->lcd_update();
1968 struct bitmap bmp_src, bmp_dst;
1970 int size = decoder->width * decoder->height;
1972 if ((unsigned char *)(disp_buf + size) >= memory_max) {
1973 /* have to discard the current */
1974 int i;
1975 for (i=1; i<=8; i++)
1976 disp[i] = NULL; /* invalidate all bitmaps */
1977 /* start again from the beginning of the buffer */
1978 disp_buf = (fb_data *)((intptr_t)(converted_image + converted_image_size + 3) & ~3);
1981 disp[ds] = disp_buf;
1982 disp_buf = (fb_data *)((intptr_t)(disp[ds] + size + 3) & ~3);
1984 bmp_src.width = decoder->infoPng.width;
1985 bmp_src.height = decoder->infoPng.height;
1986 bmp_src.data = (unsigned char *)converted_image;
1988 bmp_dst.width = decoder->width;
1989 bmp_dst.height = decoder->height;
1990 bmp_dst.data = (unsigned char *)disp[ds];
1991 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1992 rb->cpu_boost(true);
1993 smooth_resize_bitmap(&bmp_src, &bmp_dst);
1994 rb->cpu_boost(false);
1995 #else
1996 smooth_resize_bitmap(&bmp_src, &bmp_dst);
1997 #endif /*HAVE_ADJUSTABLE_CPU_FREQ*/
1998 } else {
1999 disp[ds] = converted_image;
2000 return converted_image;
2003 return disp[ds];
2006 /* set the view to the given center point, limit if necessary */
2007 void set_view (struct LodePNG_Decoder* decoder, int cx, int cy)
2009 int x, y;
2011 /* plain center to available width/height */
2012 x = cx - MIN(LCD_WIDTH, decoder->width) / 2;
2013 y = cy - MIN(LCD_HEIGHT, decoder->height) / 2;
2015 /* limit against upper image size */
2016 x = MIN(decoder->width - LCD_WIDTH, x);
2017 y = MIN(decoder->height - LCD_HEIGHT, y);
2019 /* limit against negative side */
2020 x = MAX(0, x);
2021 y = MAX(0, y);
2023 decoder->x = x; /* set the values */
2024 decoder->y = y;
2027 /* calculate the view center based on the bitmap position */
2028 void get_view(struct LodePNG_Decoder* decoder, int* p_cx, int* p_cy)
2030 *p_cx = decoder->x + MIN(LCD_WIDTH, decoder->width) / 2;
2031 *p_cy = decoder->y + MIN(LCD_HEIGHT, decoder->height) / 2;
2034 /* load, decode, display the image */
2035 int load_and_show(char* filename)
2037 int status;
2038 int cx=0, cy=0; /* view center */
2040 #if LCD_DEPTH > 1
2041 rb->lcd_set_foreground(LCD_WHITE);
2042 rb->lcd_set_background(LCD_BLACK);
2043 rb->lcd_set_backdrop(NULL);
2044 #endif
2045 rb->lcd_clear_display();
2047 memset(&disp, 0, sizeof(disp));
2048 LodePNG_Decoder_init(&decoder);
2050 if (rb->button_get(false) == PNG_MENU)
2051 status = PLUGIN_ABORT;
2052 else
2053 status = load_image(filename, &decoder);
2055 if (status == PLUGIN_OUTOFMEM)
2057 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
2058 if (plug_buf)
2060 rb->lcd_setfont(FONT_SYSFIXED);
2061 rb->lcd_clear_display();
2062 rb->snprintf(print,sizeof(print),"%s:",rb->strrchr(filename,'/')+1);
2063 rb->lcd_puts(0,0,print);
2064 rb->lcd_puts(0,1,"Not enough plugin memory!");
2065 rb->lcd_puts(0,2,"Zoom In: Stop playback.");
2066 if (entries>1)
2067 rb->lcd_puts(0,3,"Left/Right: Skip File.");
2068 rb->lcd_puts(0,4,"Show Menu: Quit.");
2069 rb->lcd_update();
2070 rb->lcd_setfont(FONT_UI);
2072 rb->button_clear_queue();
2074 while (1)
2076 int button = rb->button_get(true);
2077 switch (button)
2079 case PNG_ZOOM_IN:
2080 plug_buf = false;
2081 memory = rb->plugin_get_audio_buffer((size_t *)&memory_size);
2082 memory_max = memory + memory_size - 1;
2083 /*try again this file, now using the audio buffer */
2084 return PLUGIN_OTHER;
2085 #ifdef PNG_RC_MENU
2086 case PNG_RC_MENU:
2087 #endif
2088 case PNG_MENU:
2089 return PLUGIN_OK;
2091 case PNG_LEFT:
2092 if (entries>1)
2094 rb->lcd_clear_display();
2095 return change_filename(DIR_PREV);
2097 break;
2099 case PNG_RIGHT:
2100 if (entries>1)
2102 rb->lcd_clear_display();
2103 return change_filename(DIR_NEXT);
2105 break;
2106 default:
2107 if (rb->default_event_handler_ex(button, cleanup, NULL)
2108 == SYS_USB_CONNECTED)
2109 return PLUGIN_USB_CONNECTED;
2114 else
2115 #endif
2117 rb->splash(HZ, "Out of Memory");
2118 file_pt[curfile] = NULL;
2119 return change_filename(direction);
2122 else if (status == PLUGIN_ERROR)
2124 file_pt[curfile] = NULL;
2125 return change_filename(direction);
2127 else if (status == PLUGIN_ABORT) {
2128 rb->splash(HZ, "aborted");
2129 return PLUGIN_OK;
2132 disp_buf = (fb_data *)((intptr_t)(converted_image + converted_image_size + 3) & ~3);
2133 ds_max = max_downscale(&decoder); /* check display constraint */
2134 ds_min = min_downscale(&decoder, memory_max - (unsigned char*)disp_buf); /* check memory constraint */
2135 if (ds_min == 0) {
2136 /* Can not resize the image */
2137 ds_min = ds_max = 1;
2138 } else if (ds_max < ds_min) {
2139 ds_max = ds_min;
2142 ds = ds_max; /* initialize setting */
2143 cx = decoder.infoPng.width/ds/2; /* center the view */
2144 cy = decoder.infoPng.height/ds/2;
2146 do {
2147 resized_image = get_image(&decoder, ds); /* decode or fetch from cache */
2149 set_view(&decoder, cx, cy);
2151 if (!running_slideshow)
2153 rb->snprintf(print, sizeof(print), "showing %dx%d",
2154 decoder.width, decoder.height);
2155 rb->lcd_puts(0, 3, print);
2156 rb->lcd_update();
2159 rb->lcd_clear_display();
2160 draw_image(&decoder);
2161 rb->lcd_update();
2163 /* drawing is now finished, play around with scrolling
2164 * until you press OFF or connect USB
2166 while (1)
2168 status = scroll_bmp(&decoder);
2169 if (status == ZOOM_IN)
2171 if (ds > 1)
2173 /* as 1/1 is always available, jump ds to 1 if ds is ds_min. */
2174 int zoom = (ds == ds_min)? ds_min: 2;
2175 ds /= zoom; /* reduce downscaling to zoom in */
2176 get_view(&decoder, &cx, &cy);
2177 cx *= zoom; /* prepare the position in the new image */
2178 cy *= zoom;
2180 else
2181 continue;
2184 if (status == ZOOM_OUT)
2186 if (ds < ds_max)
2188 /* if ds is 1 and ds_min is greater than 1, jump ds to ds_min. */
2189 int zoom = (ds < ds_min)? ds_min: 2;
2190 ds *= zoom; /* increase downscaling to zoom out */
2191 get_view(&decoder, &cx, &cy);
2192 cx /= zoom; /* prepare the position in the new image */
2193 cy /= zoom;
2195 else
2196 continue;
2198 break;
2200 rb->lcd_clear_display();
2202 while (status != PLUGIN_OK && status != PLUGIN_USB_CONNECTED
2203 && status != PLUGIN_OTHER);
2205 return status;
2208 /******************** Plugin entry point *********************/
2210 enum plugin_status plugin_start(const void* parameter)
2212 int condition;
2213 #if LCD_DEPTH > 1
2214 old_backdrop = rb->lcd_get_backdrop();
2215 #endif
2217 if (!parameter) return PLUGIN_ERROR;
2219 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
2220 memory = rb->plugin_get_buffer((size_t *)&memory_size);
2221 #else
2222 memory = rb->plugin_get_audio_buffer((size_t *)&memory_size);
2223 #endif
2225 rb->strcpy(np_file, parameter);
2226 get_pic_list();
2228 if (!entries) return PLUGIN_ERROR;
2230 #if (PLUGIN_BUFFER_SIZE >= MIN_MEM) && !defined(SIMULATOR)
2231 if (!rb->audio_status()) {
2232 plug_buf = false;
2233 memory = rb->plugin_get_audio_buffer((size_t *)&memory_size);
2235 #endif
2237 memory_max = memory + memory_size - 1;
2239 /* should be ok to just load settings since the plugin itself has
2240 just been loaded from disk and the drive should be spinning */
2241 configfile_load(PNG_CONFIGFILE, png_config,
2242 ARRAYLEN(png_config), PNG_SETTINGS_MINVERSION);
2243 old_settings = png_settings;
2245 /* Turn off backlight timeout */
2246 backlight_force_on(); /* backlight control in lib/helper.c */
2250 condition = load_and_show(np_file);
2251 } while (condition != PLUGIN_OK && condition != PLUGIN_USB_CONNECTED
2252 && condition != PLUGIN_ERROR);
2254 if (rb->memcmp(&png_settings, &old_settings, sizeof (png_settings)))
2256 /* Just in case drive has to spin, keep it from looking locked */
2257 rb->splash(0, "Saving Settings");
2258 configfile_save(PNG_CONFIGFILE, png_config,
2259 ARRAYLEN(png_config), PNG_SETTINGS_VERSION);
2262 #if !defined(SIMULATOR) && defined(HAVE_DISK_STORAGE)
2263 /* set back ata spindown time in case we changed it */
2264 rb->storage_spindown(rb->global_settings->disk_spindown);
2265 #endif
2267 /* Turn on backlight timeout (revert to settings) */
2268 backlight_use_settings(); /* backlight control in lib/helper.c */
2270 return condition;