jpeg/png: Remove erased file name from list of file when change file.
[kugel-rb.git] / apps / plugins / png / png.c
blobe50605776cf59e78a70f000e35357079d21973d9
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 } LodePNG_Decoder;
146 #define VERSION_STRING "20080927"
148 /* Headings */
149 #define DIR_PREV 1
150 #define DIR_NEXT -1
151 #define DIR_NONE 0
153 /* decompressed image in the possible sizes (1,2,4,8), wasting the other */
154 static fb_data *disp[9];
155 /* up to here currently used by image(s) */
156 static fb_data *disp_buf;
158 /* my memory pool (from the mp3 buffer) */
159 static char print[128]; /* use a common snprintf() buffer */
161 unsigned char *memory, *memory_max;
162 static size_t memory_size;
164 static unsigned char *image; /* where we put the content of the file */
165 static size_t image_size;
167 #if LCD_DEPTH >= 8
168 static fb_data *converted_image __attribute__ ((aligned (16))); /* the (color) converted image */
169 #else
170 static fb_data *converted_image; /* the (color) converted image */
171 #endif
172 static size_t converted_image_size;
174 static unsigned char *decoded_image; /* the decoded image */
175 static size_t decoded_image_size;
177 #if LCD_DEPTH >= 8
178 static fb_data *resized_image __attribute__ ((aligned (16))); /* the decoded image */
179 #else
180 static fb_data *resized_image; /* the decoded image */
181 #endif
183 static struct tree_context *tree;
185 /* the current full file name */
186 static char np_file[MAX_PATH];
187 static int curfile = 0, direction = DIR_NONE, entries = 0;
189 static LodePNG_Decoder decoder;
191 /* list of the jpeg files */
192 static char **file_pt;
193 /* are we using the plugin buffer or the audio buffer? */
194 bool plug_buf = false;
196 /* Persistent configuration */
197 #define PNG_CONFIGFILE "png.cfg"
198 #define PNG_SETTINGS_MINVERSION 1
199 #define PNG_SETTINGS_VERSION 1
201 /* Slideshow times */
202 #define SS_MIN_TIMEOUT 1
203 #define SS_MAX_TIMEOUT 20
204 #define SS_DEFAULT_TIMEOUT 5
206 struct png_settings
208 int ss_timeout;
211 static struct png_settings png_settings =
213 SS_DEFAULT_TIMEOUT
215 static struct png_settings old_settings;
217 static struct configdata png_config[] =
219 { TYPE_INT, SS_MIN_TIMEOUT, SS_MAX_TIMEOUT,
220 { .int_p = &png_settings.ss_timeout }, "Slideshow Time", NULL
224 #if LCD_DEPTH > 1
225 static fb_data* old_backdrop;
226 #endif
228 /* Min memory allowing us to use the plugin buffer
229 * and thus not stopping the music
230 * *Very* rough estimation:
231 * Max 10 000 dir entries * 4bytes/entry (char **) = 40000 bytes
232 * + 30k code size = 70 000
233 * + 50k min for png = 130 000
235 #define MIN_MEM 130000
237 static int slideshow_enabled = false; /* run slideshow */
238 static int running_slideshow = false; /* loading image because of slideshw */
239 #ifndef SIMULATOR
240 static int immediate_ata_off = false; /* power down disk after loading */
241 #endif
243 static unsigned ds, ds_min, ds_max; /* downscaling and limits */
246 The two functions below (LodePNG_decompress and LodePNG_compress) directly call the
247 LodeZlib_decompress and LodeZlib_compress functions. The only purpose of the functions
248 below, is to provide the ability to let LodePNG use a different Zlib encoder by only
249 changing the two functions below, instead of changing it inside the vareous places
250 in the other LodePNG functions.
252 *out must be NULL and *outsize must be 0 initially, and after the function is done,
253 *out must point to the decompressed data, *outsize must be the size of it, and must
254 be the size of the useful data in bytes, not the alloc size.
257 static unsigned LodePNG_decompress(unsigned char* out, size_t* outsize, const unsigned char* in, size_t insize, char *error_msg)
259 z_stream stream;
260 int err;
262 error_msg = "";
264 stream.next_in = (Bytef*)in;
265 stream.avail_in = (uInt)insize;
267 stream.next_out = out;
268 stream.avail_out = (uInt)*outsize;
270 stream.zalloc = (alloc_func)0;
271 stream.zfree = (free_func)0;
273 err = inflateInit(&stream);
274 if (err != Z_OK) return err;
276 err = inflate(&stream, Z_FINISH);
277 if (err != Z_STREAM_END) {
278 inflateEnd(&stream);
279 if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0))
280 return Z_DATA_ERROR;
281 return err;
283 *outsize = stream.total_out;
285 err = inflateEnd(&stream);
286 error_msg = stream.msg;
287 return err;
291 /* ////////////////////////////////////////////////////////////////////////// */
292 /* / Reading and writing single bits and bytes from/to stream for LodePNG / */
293 /* ////////////////////////////////////////////////////////////////////////// */
295 static unsigned char readBitFromReversedStream(size_t* bitpointer, const unsigned char* bitstream)
297 unsigned char result = (unsigned char)((bitstream[(*bitpointer) >> 3] >> (7 - ((*bitpointer) & 0x7))) & 1);
298 (*bitpointer)++;
299 return result;
302 static unsigned readBitsFromReversedStream(size_t* bitpointer, const unsigned char* bitstream, size_t nbits)
304 unsigned result = 0;
305 size_t i;
306 for (i = nbits - 1; i < nbits; i--) result += (unsigned)readBitFromReversedStream(bitpointer, bitstream) << i;
307 return result;
310 static void setBitOfReversedStream0(size_t* bitpointer, unsigned char* bitstream, unsigned char bit)
312 /*the current bit in bitstream must be 0 for this to work*/
313 if (bit) bitstream[(*bitpointer) >> 3] |= (bit << (7 - ((*bitpointer) & 0x7))); /*earlier bit of huffman code is in a lesser significant bit of an earlier byte*/
314 (*bitpointer)++;
317 static void setBitOfReversedStream(size_t* bitpointer, unsigned char* bitstream, unsigned char bit)
319 /*the current bit in bitstream may be 0 or 1 for this to work*/
320 if (bit == 0) bitstream[(*bitpointer) >> 3] &= (unsigned char)(~(1 << (7 - ((*bitpointer) & 0x7))));
321 else bitstream[(*bitpointer) >> 3] |= (1 << (7 - ((*bitpointer) & 0x7)));
322 (*bitpointer)++;
325 static unsigned LodePNG_read32bitInt(const unsigned char* buffer)
327 return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
330 /* ////////////////////////////////////////////////////////////////////////// */
331 /* / PNG chunks / */
332 /* ////////////////////////////////////////////////////////////////////////// */
334 unsigned LodePNG_chunk_length(const unsigned char* chunk) /*get the length of the data of the chunk. Total chunk length has 12 bytes more.*/
336 return LodePNG_read32bitInt(&chunk[0]);
339 void LodePNG_chunk_type(char type[5], const unsigned char* chunk) /*puts the 4-byte type in null terminated string*/
341 unsigned i;
342 for (i = 0; i < 4; i++) type[i] = chunk[4 + i];
343 type[4] = 0; /*null termination char*/
346 unsigned char LodePNG_chunk_type_equals(const unsigned char* chunk, const char* type) /*check if the type is the given type*/
348 if (type[4] != 0) return 0;
349 return (chunk[4] == type[0] && chunk[5] == type[1] && chunk[6] == type[2] && chunk[7] == type[3]);
352 /*properties of PNG chunks gotten from capitalization of chunk type name, as defined by the standard*/
353 unsigned char LodePNG_chunk_critical(const unsigned char* chunk) /*0: ancillary chunk, 1: it's one of the critical chunk types*/
355 return((chunk[4] & 32) == 0);
358 unsigned char LodePNG_chunk_private(const unsigned char* chunk) /*0: public, 1: private*/
360 return((chunk[6] & 32) != 0);
363 unsigned char LodePNG_chunk_safetocopy(const unsigned char* chunk) /*0: the chunk is unsafe to copy, 1: the chunk is safe to copy*/
365 return((chunk[7] & 32) != 0);
368 unsigned char* LodePNG_chunk_data(unsigned char* chunk) /*get pointer to the data of the chunk*/
370 return &chunk[8];
373 const unsigned char* LodePNG_chunk_data_const(const unsigned char* chunk) /*get pointer to the data of the chunk*/
375 return &chunk[8];
378 unsigned LodePNG_chunk_check_crc(const unsigned char* chunk) /*returns 0 if the crc is correct, error code if it's incorrect*/
380 unsigned length = LodePNG_chunk_length(chunk);
381 unsigned CRC = LodePNG_read32bitInt(&chunk[length + 8]);
382 unsigned checksum = crc32(0L, &chunk[4], length + 4); /*the CRC is taken of the data and the 4 chunk type letters, not the length*/
383 if (CRC != checksum) return 1;
384 else return 0;
387 unsigned char* LodePNG_chunk_next(unsigned char* chunk) /*don't use on IEND chunk, as there is no next chunk then*/
389 unsigned total_chunk_length = LodePNG_chunk_length(chunk) + 12;
390 return &chunk[total_chunk_length];
393 const unsigned char* LodePNG_chunk_next_const(const unsigned char* chunk) /*don't use on IEND chunk, as there is no next chunk then*/
395 unsigned total_chunk_length = LodePNG_chunk_length(chunk) + 12;
396 return &chunk[total_chunk_length];
399 /* ////////////////////////////////////////////////////////////////////////// */
400 /* / Color types and such / */
401 /* ////////////////////////////////////////////////////////////////////////// */
403 /*return type is a LodePNG error code*/
404 static unsigned checkColorValidity(unsigned colorType, unsigned bd) /*bd = bitDepth*/
406 switch (colorType)
408 case 0:
409 if (!(bd == 1 || bd == 2 || bd == 4 || bd == 8 || bd == 16)) return 37; break; /*grey*/
410 case 2:
411 if (!( bd == 8 || bd == 16)) return 37; break; /*RGB*/
412 case 3:
413 if (!(bd == 1 || bd == 2 || bd == 4 || bd == 8 )) return 37; break; /*palette*/
414 case 4:
415 if (!( bd == 8 || bd == 16)) return 37; break; /*grey + alpha*/
416 case 6:
417 if (!( bd == 8 || bd == 16)) return 37; break; /*RGBA*/
418 default:
419 return 31;
421 return 0; /*allowed color type / bits combination*/
424 static unsigned getNumColorChannels(unsigned colorType)
426 switch (colorType)
428 case 0:
429 return 1; /*grey*/
430 case 2:
431 return 3; /*RGB*/
432 case 3:
433 return 1; /*palette*/
434 case 4:
435 return 2; /*grey + alpha*/
436 case 6:
437 return 4; /*RGBA*/
439 return 0; /*unexisting color type*/
442 static unsigned getBpp(unsigned colorType, unsigned bitDepth)
444 return getNumColorChannels(colorType) * bitDepth; /*bits per pixel is amount of channels * bits per channel*/
447 /* ////////////////////////////////////////////////////////////////////////// */
449 void LodePNG_InfoColor_init(LodePNG_InfoColor* info)
451 info->key_defined = 0;
452 info->key_r = info->key_g = info->key_b = 0;
453 info->colorType = 6;
454 info->bitDepth = 8;
455 memset(info->palette, 0, 256 * 4 * sizeof(unsigned char));
456 info->palettesize = 0;
459 void LodePNG_InfoColor_cleanup(LodePNG_InfoColor* info)
461 info->palettesize = 0;
464 unsigned LodePNG_InfoColor_getBpp(const LodePNG_InfoColor* info) { return getBpp(info->colorType, info->bitDepth); } /*calculate bits per pixel out of colorType and bitDepth*/
465 unsigned LodePNG_InfoColor_isGreyscaleType(const LodePNG_InfoColor* info) { return info->colorType == 0 || info->colorType == 4; }
467 unsigned LodePNG_InfoColor_equal(const LodePNG_InfoColor* info1, const LodePNG_InfoColor* info2)
469 return info1->colorType == info2->colorType
470 && info1->bitDepth == info2->bitDepth; /*palette and color key not compared*/
473 void LodePNG_InfoPng_init(LodePNG_InfoPng* info)
475 info->width = info->height = 0;
476 LodePNG_InfoColor_init(&info->color);
477 info->interlaceMethod = 0;
478 info->compressionMethod = 0;
479 info->filterMethod = 0;
480 info->background_defined = 0;
481 info->background_r = info->background_g = info->background_b = 0;
483 info->time_defined = 0;
484 info->phys_defined = 0;
487 void LodePNG_InfoPng_cleanup(LodePNG_InfoPng* info)
489 LodePNG_InfoColor_cleanup(&info->color);
492 unsigned LodePNG_InfoColor_copy(LodePNG_InfoColor* dest, const LodePNG_InfoColor* source)
494 size_t i;
495 LodePNG_InfoColor_cleanup(dest);
496 *dest = *source;
497 for (i = 0; i < source->palettesize * 4; i++) dest->palette[i] = source->palette[i];
498 return 0;
501 unsigned LodePNG_InfoPng_copy(LodePNG_InfoPng* dest, const LodePNG_InfoPng* source)
503 unsigned error = 0;
504 LodePNG_InfoPng_cleanup(dest);
505 *dest = *source;
506 LodePNG_InfoColor_init(&dest->color);
507 error = LodePNG_InfoColor_copy(&dest->color, &source->color); if (error) return error;
508 return error;
511 void LodePNG_InfoPng_swap(LodePNG_InfoPng* a, LodePNG_InfoPng* b)
513 LodePNG_InfoPng temp = *a;
514 *a = *b;
515 *b = temp;
518 void LodePNG_InfoRaw_init(LodePNG_InfoRaw* info)
520 LodePNG_InfoColor_init(&info->color);
523 void LodePNG_InfoRaw_cleanup(LodePNG_InfoRaw* info)
525 LodePNG_InfoColor_cleanup(&info->color);
528 unsigned LodePNG_InfoRaw_copy(LodePNG_InfoRaw* dest, const LodePNG_InfoRaw* source)
530 unsigned error = 0;
531 LodePNG_InfoRaw_cleanup(dest);
532 *dest = *source;
533 LodePNG_InfoColor_init(&dest->color);
534 error = LodePNG_InfoColor_copy(&dest->color, &source->color); if (error) return error;
535 return error;
538 /* ////////////////////////////////////////////////////////////////////////// */
541 converts from any color type to 24-bit or 32-bit (later maybe more supported). return value = LodePNG error code
542 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)
543 for < 8 bpp images, there may _not_ be padding bits at the end of scanlines.
545 unsigned LodePNG_convert(fb_data* out, const unsigned char* in, LodePNG_InfoColor* infoOut, LodePNG_InfoColor* infoIn, unsigned w, unsigned h)
547 size_t i, j, bp = 0; /*bitpointer, used by less-than-8-bit color types*/
548 size_t x, y;
549 unsigned char c;
551 if (!running_slideshow)
553 rb->snprintf(print, sizeof(print), "color conversion in progress");
554 rb->lcd_puts(0, 3, print);
555 rb->lcd_update();
558 /*cases where in and out already have the same format*/
559 if (LodePNG_InfoColor_equal(infoIn, infoOut))
562 i = 0;
563 j = 0;
564 for (y = 0 ; y < h ; y++) {
565 for (x = 0 ; x < w ; x++) {
566 unsigned char r = in[i++];
567 unsigned char g = in[i++];
568 unsigned char b = in[i++];
569 out[j++] = LCD_RGBPACK(r,g,b);
572 return 0;
575 if ((infoOut->colorType == 2 || infoOut->colorType == 6) && infoOut->bitDepth == 8)
577 if (infoIn->bitDepth == 8)
579 switch (infoIn->colorType)
581 case 0: /*greyscale color*/
582 i = 0;
583 for (y = 0 ; y < h ; y++) {
584 for (x = 0 ; x < w ; x++) {
585 c=in[i];
586 //unsigned char r = in[i];
587 //unsigned char g = in[i];
588 //unsigned char b = in[i];
589 out[i++] = LCD_RGBPACK(c,c,c);
592 break;
593 case 2: /*RGB color*/
594 i = 0;
595 for (y = 0 ; y < h ; y++) {
596 for (x = 0 ; x < w ; x++) {
597 j = 3 * i;
598 unsigned char r = in[j];
599 unsigned char g = in[j + 1];
600 unsigned char b = in[j + 2];
601 out[i++] = LCD_RGBPACK(r,g,b);
604 break;
605 case 3: /*indexed color (palette)*/
606 i = 0;
607 for (y = 0 ; y < h ; y++) {
608 for (x = 0 ; x < w ; x++) {
609 if (in[i] >= infoIn->palettesize) return 46;
610 j = in[i] << 2;
611 unsigned char r = infoIn->palette[j];
612 unsigned char g = infoIn->palette[j + 1];
613 unsigned char b = infoIn->palette[j + 2];
614 out[i++] = LCD_RGBPACK(r,g,b);
617 break;
618 case 4: /*greyscale with alpha*/
619 i = 0;
620 for (y = 0 ; y < h ; y++) {
621 for (x = 0 ; x < w ; x++) {
622 c = in[i << 1];
623 //unsigned char r = in[i<<1];
624 //unsigned char g = in[i<<1];
625 //unsigned char b = in[i<<1];
626 out[i++] = LCD_RGBPACK(c,c,c);
629 break;
630 case 6: /*RGB with alpha*/
631 i = 0;
632 for (y = 0 ; y < h ; y++) {
633 for (x = 0 ; x < w ; x++) {
634 j = i << 2;
635 unsigned char r = in[j];
636 unsigned char g = in[j + 1];
637 unsigned char b = in[j + 2];
638 out[i++] = LCD_RGBPACK(r,g,b);
641 break;
642 default:
643 break;
646 else if (infoIn->bitDepth == 16)
648 switch (infoIn->colorType)
650 case 0: /*greyscale color*/
651 i = 0;
652 for (y = 0 ; y < h ; y++) {
653 for (x = 0 ; x < w ; x++) {
654 c = in[i << 1];
655 //unsigned char r = in[2 * i];
656 //unsigned char g = in[2 * i];
657 //unsigned char b = in[2 * i];
658 out[i++] = LCD_RGBPACK(c,c,c);
661 break;
662 case 2: /*RGB color*/
663 i = 0;
664 for (y = 0 ; y < h ; y++) {
665 for (x = 0 ; x < w ; x++) {
666 j = 6 * i;
667 unsigned char r = in[j];
668 unsigned char g = in[j + 2];
669 unsigned char b = in[j + 4];
670 out[i++] = LCD_RGBPACK(r,g,b);
673 break;
674 case 4: /*greyscale with alpha*/
675 i = 0;
676 for (y = 0 ; y < h ; y++) {
677 for (x = 0 ; x < w ; x++) {
678 c = in[i << 2];
679 //unsigned char r = in[4 * i];
680 //unsigned char g = in[4 * i];
681 //unsigned char b = in[4 * i];
682 out[i++] = LCD_RGBPACK(c,c,c);
685 break;
686 case 6: /*RGB with alpha*/
687 i = 0;
688 for (y = 0 ; y < h ; y++) {
689 for (x = 0 ; x < w ; x++) {
690 j = i << 3;
691 unsigned char r = in[j];
692 unsigned char g = in[j + 2];
693 unsigned char b = in[j + 4];
694 out[i++] = LCD_RGBPACK(r,g,b);
697 break;
698 default:
699 break;
702 else /*infoIn->bitDepth is less than 8 bit per channel*/
704 switch (infoIn->colorType)
706 case 0: /*greyscale color*/
707 i = 0;
708 for (y = 0 ; y < h ; y++) {
709 for (x = 0 ; x < w ; x++) {
710 unsigned value = readBitsFromReversedStream(&bp, in, infoIn->bitDepth);
711 value = (value * 255) / ((1 << infoIn->bitDepth) - 1); /*scale value from 0 to 255*/
712 unsigned char r = (unsigned char)value;
713 unsigned char g = (unsigned char)value;
714 unsigned char b = (unsigned char)value;
715 out[i++] = LCD_RGBPACK(r,g,b);
718 break;
719 case 3: /*indexed color (palette)*/
720 i = 0;
721 for (y = 0 ; y < h ; y++) {
722 for (x = 0 ; x < w ; x++) {
723 unsigned value = readBitsFromReversedStream(&bp, in, infoIn->bitDepth);
724 if (value >= infoIn->palettesize) return 47;
725 j = value << 2;
726 unsigned char r = infoIn->palette[j];
727 unsigned char g = infoIn->palette[j + 1];
728 unsigned char b = infoIn->palette[j + 2];
729 out[i++] = LCD_RGBPACK(r,g,b);
732 break;
733 default:
734 break;
738 else if (LodePNG_InfoColor_isGreyscaleType(infoOut) && infoOut->bitDepth == 8) /*conversion from greyscale to greyscale*/
740 if (!LodePNG_InfoColor_isGreyscaleType(infoIn)) return 62;
741 if (infoIn->bitDepth == 8)
743 switch (infoIn->colorType)
745 case 0: /*greyscale color*/
746 i = 0;
747 for (y = 0 ; y < h ; y++) {
748 for (x = 0 ; x < w ; x++) {
749 c = in[i];
750 //unsigned char r = in[i];
751 //unsigned char g = in[i];
752 //unsigned char b = in[i];
753 out[i++] = LCD_RGBPACK(c,c,c);
756 break;
757 case 4: /*greyscale with alpha*/
758 i = 0;
759 for (y = 0 ; y < h ; y++) {
760 for (x = 0 ; x < w ; x++) {
761 c = in[(i << 1) + 1];
762 //unsigned char r = in[2 * i + 1];
763 //unsigned char g = in[2 * i + 1];
764 //unsigned char b = in[2 * i + 1];
765 out[i++] = LCD_RGBPACK(c,c,c);
768 break;
769 default:
770 return 31;
773 else if (infoIn->bitDepth == 16)
775 switch (infoIn->colorType)
777 case 0: /*greyscale color*/
778 i = 0;
779 for (y = 0 ; y < h ; y++) {
780 for (x = 0 ; x < w ; x++) {
781 c = in[i << 1];
782 //unsigned char r = in[2 * i];
783 //unsigned char g = in[2 * i];
784 //unsigned char b = in[2 * i];
785 out[i++] = LCD_RGBPACK(c,c,c);
788 break;
789 case 4: /*greyscale with alpha*/
790 i = 0;
791 for (y = 0 ; y < h ; y++) {
792 for (x = 0 ; x < w ; x++) {
793 c = in[i << 2];
794 //unsigned char r = in[4 * i];
795 //unsigned char g = in[4 * i];
796 //unsigned char b = in[4 * i];
797 out[i++] = LCD_RGBPACK(c,c,c);
800 break;
801 default:
802 return 31;
805 else /*infoIn->bitDepth is less than 8 bit per channel*/
807 if (infoIn->colorType != 0) return 31; /*colorType 0 is the only greyscale type with < 8 bits per channel*/
808 i = 0;
809 for (y = 0 ; y < h ; y++) {
810 for (x = 0 ; x < w ; x++) {
811 unsigned value = readBitsFromReversedStream(&bp, in, infoIn->bitDepth);
812 value = (value * 255) / ((1 << infoIn->bitDepth) - 1); /*scale value from 0 to 255*/
813 unsigned char r = (unsigned char)value;
814 unsigned char g = (unsigned char)value;
815 unsigned char b = (unsigned char)value;
816 out[i++] = LCD_RGBPACK(r,g,b);
821 else return 59;
823 return 0;
826 /*Paeth predicter, used by PNG filter type 4*/
827 static int paethPredictor(int a, int b, int c)
829 int p = a + b - c;
830 int pa = p > a ? p - a : a - p;
831 int pb = p > b ? p - b : b - p;
832 int pc = p > c ? p - c : c - p;
834 if (pa <= pb && pa <= pc) return a;
835 else if (pb <= pc) return b;
836 else return c;
839 /*shared values used by multiple Adam7 related functions*/
841 static const unsigned ADAM7_IX[7] = { 0, 4, 0, 2, 0, 1, 0 }; /*x start values*/
842 static const unsigned ADAM7_IY[7] = { 0, 0, 4, 0, 2, 0, 1 }; /*y start values*/
843 static const unsigned ADAM7_DX[7] = { 8, 8, 4, 4, 2, 2, 1 }; /*x delta values*/
844 static const unsigned ADAM7_DY[7] = { 8, 8, 8, 4, 4, 2, 2 }; /*y delta values*/
846 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)
848 /*the passstart values have 8 values: the 8th one actually indicates the byte after the end of the 7th (= last) pass*/
849 unsigned i;
851 /*calculate width and height in pixels of each pass*/
852 for (i = 0; i < 7; i++)
854 passw[i] = (w + ADAM7_DX[i] - ADAM7_IX[i] - 1) / ADAM7_DX[i];
855 passh[i] = (h + ADAM7_DY[i] - ADAM7_IY[i] - 1) / ADAM7_DY[i];
856 if (passw[i] == 0) passh[i] = 0;
857 if (passh[i] == 0) passw[i] = 0;
860 filter_passstart[0] = padded_passstart[0] = passstart[0] = 0;
861 for (i = 0; i < 7; i++)
863 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)*/
864 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*/
865 passstart[i + 1] = passstart[i] + (passh[i] * passw[i] * bpp + 7) / 8; /*only padded at end of reduced image*/
869 /* ////////////////////////////////////////////////////////////////////////// */
870 /* / PNG Decoder / */
871 /* ////////////////////////////////////////////////////////////////////////// */
873 /*read the information from the header and store it in the LodePNG_Info. return value is error*/
874 void LodePNG_inspect(LodePNG_Decoder* decoder, const unsigned char* in, size_t inlength)
876 if (inlength == 0 || in == 0) { decoder->error = 48; return; } /*the given data is empty*/
877 if (inlength < 29) { decoder->error = 27; return; } /*error: the data length is smaller than the length of the header*/
879 /*when decoding a new PNG image, make sure all parameters created after previous decoding are reset*/
880 LodePNG_InfoPng_cleanup(&decoder->infoPng);
881 LodePNG_InfoPng_init(&decoder->infoPng);
882 decoder->error = 0;
884 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*/
885 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!*/
887 /*read the values given in the header*/
888 decoder->infoPng.width = LodePNG_read32bitInt(&in[16]);
889 decoder->infoPng.height = LodePNG_read32bitInt(&in[20]);
890 decoder->infoPng.color.bitDepth = in[24];
891 decoder->infoPng.color.colorType = in[25];
892 decoder->infoPng.compressionMethod = in[26];
893 decoder->infoPng.filterMethod = in[27];
894 decoder->infoPng.interlaceMethod = in[28];
896 unsigned CRC = LodePNG_read32bitInt(&in[29]);
897 unsigned checksum = crc32(0L, &in[12], 17);
898 if (CRC != checksum) { decoder->error = 57; return; }
900 if (decoder->infoPng.compressionMethod != 0) { decoder->error = 32; return; } /*error: only compression method 0 is allowed in the specification*/
901 if (decoder->infoPng.filterMethod != 0) { decoder->error = 33; return; } /*error: only filter method 0 is allowed in the specification*/
902 if (decoder->infoPng.interlaceMethod > 1) { decoder->error = 34; return; } /*error: only interlace methods 0 and 1 exist in the specification*/
904 decoder->error = checkColorValidity(decoder->infoPng.color.colorType, decoder->infoPng.color.bitDepth);
907 static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scanline, const unsigned char* precon, size_t bytewidth, unsigned char filterType, size_t length)
910 For PNG filter method 0
911 unfilter a PNG image scanline by scanline. when the pixels are smaller than 1 byte, the filter works byte per byte (bytewidth = 1)
912 precon is the previous unfiltered scanline, recon the result, scanline the current one
913 the incoming scanlines do NOT include the filtertype byte, that one is given in the parameter filterType instead
914 recon and scanline MAY be the same memory address! precon must be disjoint.
917 size_t i;
918 switch (filterType)
920 case 0:
921 //for(i = 0; i < length; i++) recon[i] = scanline[i];
922 memcpy(recon, scanline, length * sizeof(unsigned char));
923 break;
924 case 1:
925 //for(i = 0; i < bytewidth; i++) recon[i] = scanline[i];
926 memcpy(recon, scanline, bytewidth * sizeof(unsigned char));
927 for (i = bytewidth; i < length; i++) recon[i] = scanline[i] + recon[i - bytewidth];
928 break;
929 case 2:
930 if (precon) for (i = 0; i < length; i++) recon[i] = scanline[i] + precon[i];
931 else //for(i = 0; i < length; i++) recon[i] = scanline[i];
932 memcpy(recon, scanline, length * sizeof(unsigned char));
933 break;
934 case 3:
935 if (precon)
937 for (i = 0; i < bytewidth; i++) recon[i] = scanline[i] + precon[i] / 2;
938 for (i = bytewidth; i < length; i++) recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) / 2);
940 else
942 //for(i = 0; i < bytewidth; i++) recon[i] = scanline[i];
943 memcpy(recon, scanline, bytewidth * sizeof(unsigned char));
944 for (i = bytewidth; i < length; i++) recon[i] = scanline[i] + recon[i - bytewidth] / 2;
946 break;
947 case 4:
948 if (precon)
950 for (i = 0; i < bytewidth; i++) recon[i] = (unsigned char)(scanline[i] + paethPredictor(0, precon[i], 0));
951 for (i = bytewidth; i < length; i++) recon[i] = (unsigned char)(scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[i - bytewidth]));
953 else
955 //for(i = 0; i < bytewidth; i++) recon[i] = scanline[i];
956 memcpy(recon, scanline, bytewidth * sizeof(unsigned char));
957 for (i = bytewidth; i < length; i++) recon[i] = (unsigned char)(scanline[i] + paethPredictor(recon[i - bytewidth], 0, 0));
959 break;
960 default:
961 return 36; /*error: unexisting filter type given*/
963 return 0;
966 static unsigned unfilter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp)
969 For PNG filter method 0
970 this function unfilters a single image (e.g. without interlacing this is called once, with Adam7 it's called 7 times)
971 out must have enough bytes allocated already, in must have the scanlines + 1 filtertype byte per scanline
972 w and h are image dimensions or dimensions of reduced image, bpp is bits per pixel
973 in and out are allowed to be the same memory address!
976 unsigned y;
977 unsigned char* prevline = 0;
979 size_t bytewidth = (bpp + 7) / 8; /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/
980 size_t linebytes = (w * bpp + 7) / 8;
982 for (y = 0; y < h; y++)
984 size_t outindex = linebytes * y;
985 size_t inindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/
986 unsigned char filterType = in[inindex];
988 unsigned error = unfilterScanline(&out[outindex], &in[inindex + 1], prevline, bytewidth, filterType, linebytes);
989 if (error) return error;
991 prevline = &out[outindex];
994 return 0;
997 static void Adam7_deinterlace(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp)
999 /*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
1000 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)*/
1001 unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8];
1002 unsigned i;
1004 Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp);
1006 if (bpp >= 8)
1008 for (i = 0; i < 7; i++)
1010 unsigned x, y, b;
1011 size_t bytewidth = bpp / 8;
1012 for (y = 0; y < passh[i]; y++)
1013 for (x = 0; x < passw[i]; x++)
1015 size_t pixelinstart = passstart[i] + (y * passw[i] + x) * bytewidth;
1016 size_t pixeloutstart = ((ADAM7_IY[i] + y * ADAM7_DY[i]) * w + ADAM7_IX[i] + x * ADAM7_DX[i]) * bytewidth;
1017 for (b = 0; b < bytewidth; b++)
1019 out[pixeloutstart + b] = in[pixelinstart + b];
1024 else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/
1026 for (i = 0; i < 7; i++)
1028 unsigned x, y, b;
1029 unsigned ilinebits = bpp * passw[i];
1030 unsigned olinebits = bpp * w;
1031 size_t obp, ibp; /*bit pointers (for out and in buffer)*/
1032 for (y = 0; y < passh[i]; y++)
1033 for (x = 0; x < passw[i]; x++)
1035 ibp = (8 * passstart[i]) + (y * ilinebits + x * bpp);
1036 obp = (ADAM7_IY[i] + y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + x * ADAM7_DX[i]) * bpp;
1037 for (b = 0; b < bpp; b++)
1039 unsigned char bit = readBitFromReversedStream(&ibp, in);
1040 setBitOfReversedStream0(&obp, out, bit); /*note that this function assumes the out buffer is completely 0, use setBitOfReversedStream otherwise*/
1047 static void removePaddingBits(unsigned char* out, const unsigned char* in, size_t olinebits, size_t ilinebits, unsigned h)
1050 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.
1051 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
1052 also used to move bits after earlier such operations happened, e.g. in a sequence of reduced images from Adam7
1053 only useful if (ilinebits - olinebits) is a value in the range 1..7
1055 unsigned y;
1056 size_t diff = ilinebits - olinebits;
1057 size_t obp = 0, ibp = 0; /*bit pointers*/
1058 for (y = 0; y < h; y++)
1060 size_t x;
1061 for (x = 0; x < olinebits; x++)
1063 unsigned char bit = readBitFromReversedStream(&ibp, in);
1064 setBitOfReversedStream(&obp, out, bit);
1066 ibp += diff;
1070 /*out must be buffer big enough to contain full image, and in must contain the full decompressed data from the IDAT chunks*/
1071 static unsigned postProcessScanlines(unsigned char* out, unsigned char* in, const LodePNG_Decoder* decoder) /*return value is error*/
1074 This function converts the filtered-padded-interlaced data into pure 2D image buffer with the PNG's colortype. Steps:
1075 *) if no Adam7: 1) unfilter 2) remove padding bits (= posible extra bits per scanline if bpp < 8)
1076 *) if adam7: 1) 7x unfilter 2) 7x remove padding bits 3) Adam7_deinterlace
1077 NOTE: the in buffer will be overwritten with intermediate data!
1079 unsigned bpp = LodePNG_InfoColor_getBpp(&decoder->infoPng.color);
1080 unsigned w = decoder->infoPng.width;
1081 unsigned h = decoder->infoPng.height;
1082 unsigned error = 0;
1083 if (bpp == 0) return 31; /*error: invalid colortype*/
1085 if (decoder->infoPng.interlaceMethod == 0)
1087 if (bpp < 8 && w * bpp != ((w * bpp + 7) / 8) * 8)
1089 error = unfilter(in, in, w, h, bpp);
1090 if (error) return error;
1091 removePaddingBits(out, in, w * bpp, ((w * bpp + 7) / 8) * 8, h);
1093 else error = unfilter(out, in, w, h, bpp); /*we can immediatly filter into the out buffer, no other steps needed*/
1095 else /*interlaceMethod is 1 (Adam7)*/
1097 unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8];
1098 unsigned i;
1100 Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp);
1102 for (i = 0; i < 7; i++)
1104 error = unfilter(&in[padded_passstart[i]], &in[filter_passstart[i]], passw[i], passh[i], bpp);
1105 if (error) return error;
1106 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*/
1108 /*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*/
1109 removePaddingBits(&in[passstart[i]], &in[padded_passstart[i]], passw[i] * bpp, ((passw[i] * bpp + 7) / 8) * 8, passh[i]);
1113 Adam7_deinterlace(out, in, w, h, bpp);
1116 return error;
1119 /*read a PNG, the result will be in the same color type as the PNG (hence "generic")*/
1120 static void decodeGeneric(LodePNG_Decoder* decoder, unsigned char* in, size_t size, void (*pf_progress)(int current, int total))
1122 if (pf_progress != NULL)
1123 pf_progress(0, 100);
1124 unsigned char IEND = 0;
1125 const unsigned char* chunk;
1126 size_t i;
1127 unsigned char *idat = memory;
1128 size_t idat_size = 0;
1130 /*for unknown chunk order*/
1131 unsigned unknown = 0;
1132 unsigned critical_pos = 1; /*1 = after IHDR, 2 = after PLTE, 3 = after IDAT*/
1134 /*provide some proper output values if error will happen*/
1135 decoded_image_size = 0;
1137 if (size == 0 || in == 0) { decoder->error = 48; return; } /*the given data is empty*/
1139 LodePNG_inspect(decoder, in, size); /*reads header and resets other parameters in decoder->infoPng*/
1140 if (decoder->error) return;
1142 chunk = &in[33]; /*first byte of the first chunk after the header*/
1144 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*/
1146 unsigned chunkLength;
1147 const unsigned char* data; /*the data in the chunk*/
1149 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*/
1150 chunkLength = LodePNG_chunk_length(chunk); /*length of the data of the chunk, excluding the length bytes, chunk type and CRC bytes*/
1151 if (chunkLength > 2147483647) { decoder->error = 63; break; }
1152 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*/
1153 data = LodePNG_chunk_data_const(chunk);
1155 /*IDAT chunk, containing compressed image data*/
1156 if (LodePNG_chunk_type_equals(chunk, "IDAT"))
1158 size_t oldsize = idat_size;
1159 idat_size += chunkLength;
1160 if (idat + idat_size >= image) { decoder->error = OUT_OF_MEMORY; break; }
1161 memcpy(idat+oldsize, data, chunkLength * sizeof(unsigned char));
1162 critical_pos = 3;
1164 /*IEND chunk*/
1165 else if (LodePNG_chunk_type_equals(chunk, "IEND"))
1167 IEND = 1;
1169 /*palette chunk (PLTE)*/
1170 else if (LodePNG_chunk_type_equals(chunk, "PLTE"))
1172 unsigned pos = 0;
1173 decoder->infoPng.color.palettesize = chunkLength / 3;
1174 if (decoder->infoPng.color.palettesize > 256) { decoder->error = 38; break; } /*error: palette too big*/
1175 for (i = 0; i < decoder->infoPng.color.palettesize; i++)
1177 decoder->infoPng.color.palette[4 * i + 0] = data[pos++]; /*R*/
1178 decoder->infoPng.color.palette[4 * i + 1] = data[pos++]; /*G*/
1179 decoder->infoPng.color.palette[4 * i + 2] = data[pos++]; /*B*/
1180 decoder->infoPng.color.palette[4 * i + 3] = 255; /*alpha*/
1182 critical_pos = 2;
1184 /*palette transparency chunk (tRNS)*/
1185 else if (LodePNG_chunk_type_equals(chunk, "tRNS"))
1187 if (decoder->infoPng.color.colorType == 3)
1189 if (chunkLength > decoder->infoPng.color.palettesize) { decoder->error = 39; break; } /*error: more alpha values given than there are palette entries*/
1190 for (i = 0; i < chunkLength; i++) decoder->infoPng.color.palette[4 * i + 3] = data[i];
1192 else if (decoder->infoPng.color.colorType == 0)
1194 if (chunkLength != 2) { decoder->error = 40; break; } /*error: this chunk must be 2 bytes for greyscale image*/
1195 decoder->infoPng.color.key_defined = 1;
1196 decoder->infoPng.color.key_r = decoder->infoPng.color.key_g = decoder->infoPng.color.key_b = 256 * data[0] + data[1];
1198 else if (decoder->infoPng.color.colorType == 2)
1200 if (chunkLength != 6) { decoder->error = 41; break; } /*error: this chunk must be 6 bytes for RGB image*/
1201 decoder->infoPng.color.key_defined = 1;
1202 decoder->infoPng.color.key_r = 256 * data[0] + data[1];
1203 decoder->infoPng.color.key_g = 256 * data[2] + data[3];
1204 decoder->infoPng.color.key_b = 256 * data[4] + data[5];
1206 else { decoder->error = 42; break; } /*error: tRNS chunk not allowed for other color models*/
1208 /*background color chunk (bKGD)*/
1209 else if (LodePNG_chunk_type_equals(chunk, "bKGD"))
1211 if (decoder->infoPng.color.colorType == 3)
1213 if (chunkLength != 1) { decoder->error = 43; break; } /*error: this chunk must be 1 byte for indexed color image*/
1214 decoder->infoPng.background_defined = 1;
1215 decoder->infoPng.background_r = decoder->infoPng.background_g = decoder->infoPng.background_g = data[0];
1217 else if (decoder->infoPng.color.colorType == 0 || decoder->infoPng.color.colorType == 4)
1219 if (chunkLength != 2) { decoder->error = 44; break; } /*error: this chunk must be 2 bytes for greyscale image*/
1220 decoder->infoPng.background_defined = 1;
1221 decoder->infoPng.background_r = decoder->infoPng.background_g = decoder->infoPng.background_b = 256 * data[0] + data[1];
1223 else if (decoder->infoPng.color.colorType == 2 || decoder->infoPng.color.colorType == 6)
1225 if (chunkLength != 6) { decoder->error = 45; break; } /*error: this chunk must be 6 bytes for greyscale image*/
1226 decoder->infoPng.background_defined = 1;
1227 decoder->infoPng.background_r = 256 * data[0] + data[1];
1228 decoder->infoPng.background_g = 256 * data[2] + data[3];
1229 decoder->infoPng.background_b = 256 * data[4] + data[5];
1232 else if (LodePNG_chunk_type_equals(chunk, "tIME"))
1234 if (chunkLength != 7) { decoder->error = 73; break; }
1235 decoder->infoPng.time_defined = 1;
1236 decoder->infoPng.time.year = 256 * data[0] + data[+ 1];
1237 decoder->infoPng.time.month = data[2];
1238 decoder->infoPng.time.day = data[3];
1239 decoder->infoPng.time.hour = data[4];
1240 decoder->infoPng.time.minute = data[5];
1241 decoder->infoPng.time.second = data[6];
1243 else if (LodePNG_chunk_type_equals(chunk, "pHYs"))
1245 if (chunkLength != 9) { decoder->error = 74; break; }
1246 decoder->infoPng.phys_defined = 1;
1247 decoder->infoPng.phys_x = 16777216 * data[0] + 65536 * data[1] + 256 * data[2] + data[3];
1248 decoder->infoPng.phys_y = 16777216 * data[4] + 65536 * data[5] + 256 * data[6] + data[7];
1249 decoder->infoPng.phys_unit = data[8];
1251 else /*it's not an implemented chunk type, so ignore it: skip over the data*/
1253 if (LodePNG_chunk_critical(chunk)) { decoder->error = 69; break; } /*error: unknown critical chunk (5th bit of first byte of chunk type is 0)*/
1254 unknown = 1;
1257 if (!unknown) /*check CRC if wanted, only on known chunk types*/
1259 long time = *rb->current_tick;
1260 if (LodePNG_chunk_check_crc(chunk)) { decoder->error = 57; break; }
1261 time = *rb->current_tick-time;
1264 if (!IEND) chunk = LodePNG_chunk_next_const(chunk);
1267 if (!decoder->error)
1269 unsigned char *scanlines = idat + idat_size;
1270 size_t scanlines_size = (size_t)memory_max - idat_size + 1;
1271 long time = *rb->current_tick;
1272 decoder->error = LodePNG_decompress(scanlines, &scanlines_size, idat, idat_size, decoder->error_msg); /*decompress with the Zlib decompressor*/
1273 if (pf_progress) pf_progress(100, 100);
1274 time = *rb->current_tick-time;
1276 if (!decoder->error)
1278 decoded_image_size = (decoder->infoPng.height * decoder->infoPng.width * LodePNG_InfoColor_getBpp(&decoder->infoPng.color) + 7) / 8;
1279 if (decoded_image_size > memory_size) { decoder->error = OUT_OF_MEMORY; return; }
1280 decoded_image = memory_max - decoded_image_size + 1;
1281 if (scanlines + scanlines_size >= decoded_image) { decoder->error = OUT_OF_MEMORY; return; }
1282 memset(decoded_image, 0, decoded_image_size * sizeof(unsigned char));
1283 if (!running_slideshow)
1285 rb->snprintf(print, sizeof(print), "unfiltering scanlines");
1286 rb->lcd_puts(0, 3, print);
1287 rb->lcd_update();
1289 decoder->error = postProcessScanlines(decoded_image, scanlines, decoder);
1294 void LodePNG_decode(LodePNG_Decoder* decoder, unsigned char* in, size_t insize, void (*pf_progress)(int current, int total))
1296 decodeGeneric(decoder, in, insize, pf_progress);
1297 if (decoder->error) return;
1299 /*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"*/
1300 if (!(decoder->infoRaw.color.colorType == 2 || decoder->infoRaw.color.colorType == 6) && !(decoder->infoRaw.color.bitDepth == 8)) { decoder->error = 56; return; }
1301 converted_image = (fb_data *)((intptr_t)(memory + 3) & ~3);
1302 converted_image_size = decoder->infoPng.width*decoder->infoPng.height;
1303 if ((unsigned char *)(converted_image + converted_image_size) >= decoded_image) { decoder->error = OUT_OF_MEMORY; }
1304 if (!decoder->error) decoder->error = LodePNG_convert(converted_image, decoded_image, &decoder->infoRaw.color, &decoder->infoPng.color, decoder->infoPng.width, decoder->infoPng.height);
1307 void LodePNG_DecodeSettings_init(LodePNG_DecodeSettings* settings)
1309 settings->color_convert = 1;
1312 void LodePNG_Decoder_init(LodePNG_Decoder* decoder)
1314 LodePNG_DecodeSettings_init(&decoder->settings);
1315 LodePNG_InfoRaw_init(&decoder->infoRaw);
1316 LodePNG_InfoPng_init(&decoder->infoPng);
1317 decoder->error = 1;
1320 void LodePNG_Decoder_cleanup(LodePNG_Decoder* decoder)
1322 LodePNG_InfoRaw_cleanup(&decoder->infoRaw);
1323 LodePNG_InfoPng_cleanup(&decoder->infoPng);
1326 bool png_ext(const char ext[])
1328 if (!ext)
1329 return false;
1330 if (!rb->strcasecmp(ext,".png"))
1331 return true;
1332 else
1333 return false;
1336 /*Read directory contents for scrolling. */
1337 void get_pic_list(void)
1339 int i;
1340 struct entry *dircache;
1341 char *pname;
1342 tree = rb->tree_get_context();
1343 dircache = tree->dircache;
1345 file_pt = (char **) memory;
1347 /* Remove path and leave only the name.*/
1348 pname = rb->strrchr(np_file,'/');
1349 pname++;
1351 for (i = 0; i < tree->filesindir; i++)
1353 if (!(dircache[i].attr & ATTR_DIRECTORY)
1354 && png_ext(rb->strrchr(dircache[i].name, '.')))
1356 file_pt[entries] = dircache[i].name;
1357 /* Set Selected File. */
1358 if (!rb->strcmp(file_pt[entries], pname))
1359 curfile = entries;
1360 entries++;
1364 memory += (entries * sizeof(char**));
1365 memory_size -= (entries * sizeof(char**));
1368 int change_filename(int direct)
1370 bool file_erased = (file_pt[curfile] == NULL);
1371 direction = direct;
1373 curfile += (direct == DIR_PREV? entries - 1: 1);
1374 if (curfile >= entries)
1375 curfile -= entries;
1377 if (file_erased)
1379 /* remove 'erased' file names from list. */
1380 int count, i;
1381 for (count = i = 0; i < entries; i++)
1383 if (curfile == i)
1384 curfile = count;
1385 if (file_pt[i] != NULL)
1386 file_pt[count++] = file_pt[i];
1388 entries = count;
1391 if (entries == 0)
1393 rb->splash(HZ, "No supported files");
1394 return PLUGIN_ERROR;
1397 if (rb->strlen(tree->currdir) > 1)
1399 rb->strcpy(np_file, tree->currdir);
1400 rb->strcat(np_file, "/");
1402 else
1403 rb->strcpy(np_file, tree->currdir);
1405 rb->strcat(np_file, file_pt[curfile]);
1407 return PLUGIN_OTHER;
1410 /* switch off overlay, for handling SYS_ events */
1411 void cleanup(void *parameter)
1413 (void)parameter;
1416 #define VSCROLL (LCD_HEIGHT/8)
1417 #define HSCROLL (LCD_WIDTH/10)
1419 #define ZOOM_IN 100 /* return codes for below function */
1420 #define ZOOM_OUT 101
1422 int show_menu(void) /* return 1 to quit */
1424 #if LCD_DEPTH > 1
1425 rb->lcd_set_backdrop(old_backdrop);
1426 #ifdef HAVE_LCD_COLOR
1427 rb->lcd_set_foreground(rb->global_settings->fg_color);
1428 rb->lcd_set_background(rb->global_settings->bg_color);
1429 #else
1430 rb->lcd_set_foreground(LCD_BLACK);
1431 rb->lcd_set_background(LCD_WHITE);
1432 #endif
1433 #endif
1434 int result;
1436 enum menu_id
1438 MIID_RETURN = 0,
1439 MIID_TOGGLE_SS_MODE,
1440 MIID_CHANGE_SS_MODE,
1441 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
1442 MIID_SHOW_PLAYBACK_MENU,
1443 #endif
1444 MIID_QUIT,
1447 MENUITEM_STRINGLIST(menu, "Png Menu", NULL,
1448 "Return", "Toggle Slideshow Mode",
1449 "Change Slideshow Time",
1450 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
1451 "Show Playback Menu",
1452 #endif
1453 "Quit");
1455 static const struct opt_items slideshow[2] = {
1456 { "Disable", -1 },
1457 { "Enable", -1 },
1460 result=rb->do_menu(&menu, NULL, NULL, false);
1462 switch (result)
1464 case MIID_RETURN:
1465 break;
1466 case MIID_TOGGLE_SS_MODE:
1467 rb->set_option("Toggle Slideshow", &slideshow_enabled, INT,
1468 slideshow , 2, NULL);
1469 break;
1470 case MIID_CHANGE_SS_MODE:
1471 rb->set_int("Slideshow Time", "s", UNIT_SEC,
1472 &png_settings.ss_timeout, NULL, 1,
1473 SS_MIN_TIMEOUT, SS_MAX_TIMEOUT, NULL);
1474 break;
1475 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
1476 case MIID_SHOW_PLAYBACK_MENU:
1477 if (plug_buf)
1479 playback_control(NULL);
1481 else
1483 rb->splash(HZ, "Cannot restart playback");
1485 break;
1486 #endif
1487 case MIID_QUIT:
1488 return 1;
1489 break;
1492 #if !defined(SIMULATOR) && defined(HAVE_DISK_STORAGE)
1493 /* change ata spindown time based on slideshow time setting */
1494 immediate_ata_off = false;
1495 rb->storage_spindown(rb->global_settings->disk_spindown);
1497 if (slideshow_enabled)
1499 if (png_settings.ss_timeout < 10)
1501 /* slideshow times < 10s keep disk spinning */
1502 rb->storage_spindown(0);
1504 else if (!rb->mp3_is_playing())
1506 /* slideshow times > 10s and not playing: ata_off after load */
1507 immediate_ata_off = true;
1510 #endif
1511 #if LCD_DEPTH > 1
1512 rb->lcd_set_backdrop(NULL);
1513 rb->lcd_set_foreground(LCD_WHITE);
1514 rb->lcd_set_background(LCD_BLACK);
1515 #endif
1516 rb->lcd_clear_display();
1517 return 0;
1520 void draw_image(struct LodePNG_Decoder* decoder)
1522 rb->lcd_bitmap_part(resized_image, decoder->x, decoder->y, decoder->infoPng.width/ds /*stride*/,
1523 MAX(0, (LCD_WIDTH - (int)decoder->infoPng.width/(int)ds) / 2),
1524 MAX(0, (LCD_HEIGHT - (int)decoder->infoPng.height/(int)ds) / 2),
1525 decoder->infoPng.width/ds - decoder->x,
1526 decoder->infoPng.height/ds - decoder->y);
1529 /* Pan the viewing window right - move image to the left and fill in
1530 the right-hand side */
1531 static void pan_view_right(struct LodePNG_Decoder* decoder)
1533 int move;
1535 move = MIN(HSCROLL, (int)(decoder->infoPng.width/ds) - decoder->x - LCD_WIDTH);
1536 if (move > 0)
1538 decoder->x += move;
1539 draw_image(decoder);
1540 rb->lcd_update();
1544 /* Pan the viewing window left - move image to the right and fill in
1545 the left-hand side */
1546 static void pan_view_left(struct LodePNG_Decoder* decoder)
1548 int move;
1550 move = MIN(HSCROLL, decoder->x);
1551 if (move > 0)
1553 decoder->x -= move;
1554 draw_image(decoder);
1555 rb->lcd_update();
1560 /* Pan the viewing window up - move image down and fill in
1561 the top */
1562 static void pan_view_up(struct LodePNG_Decoder* decoder)
1564 int move;
1566 move = MIN(VSCROLL, decoder->y);
1567 if (move > 0)
1569 decoder->y -= move;
1570 draw_image(decoder);
1571 rb->lcd_update();
1575 /* Pan the viewing window down - move image up and fill in
1576 the bottom */
1577 static void pan_view_down(struct LodePNG_Decoder* decoder)
1579 int move;
1581 move = MIN(VSCROLL, (int)(decoder->infoPng.height/ds) - decoder->y - LCD_HEIGHT);
1582 if (move > 0)
1584 decoder->y += move;
1585 draw_image(decoder);
1586 rb->lcd_update();
1590 /* interactively scroll around the image */
1591 int scroll_bmp(struct LodePNG_Decoder* decoder)
1593 int button;
1594 int lastbutton = 0;
1596 while (true)
1598 if (slideshow_enabled)
1599 button = rb->button_get_w_tmo(png_settings.ss_timeout * HZ);
1600 else button = rb->button_get(true);
1602 running_slideshow = false;
1604 switch (button)
1606 case PNG_LEFT:
1607 if (entries > 1 && decoder->infoPng.width/ds <= LCD_WIDTH
1608 && decoder->infoPng.height/ds <= LCD_HEIGHT)
1609 return change_filename(DIR_PREV);
1610 case PNG_LEFT | BUTTON_REPEAT:
1611 pan_view_left(decoder);
1612 break;
1614 case PNG_RIGHT:
1615 if (entries > 1 && decoder->infoPng.width/ds <= LCD_WIDTH
1616 && decoder->infoPng.height/ds <= LCD_HEIGHT)
1617 return change_filename(DIR_NEXT);
1618 case PNG_RIGHT | BUTTON_REPEAT:
1619 pan_view_right(decoder);
1620 break;
1622 case PNG_UP:
1623 case PNG_UP | BUTTON_REPEAT:
1624 pan_view_up(decoder);
1625 break;
1627 case PNG_DOWN:
1628 case PNG_DOWN | BUTTON_REPEAT:
1629 pan_view_down(decoder);
1630 break;
1632 case BUTTON_NONE:
1633 if (!slideshow_enabled)
1634 break;
1635 running_slideshow = true;
1636 if (entries > 1)
1637 return change_filename(DIR_NEXT);
1638 break;
1640 #ifdef PNG_SLIDE_SHOW
1641 case PNG_SLIDE_SHOW:
1642 slideshow_enabled = !slideshow_enabled;
1643 running_slideshow = slideshow_enabled;
1644 break;
1645 #endif
1647 #ifdef PNG_NEXT_REPEAT
1648 case PNG_NEXT_REPEAT:
1649 #endif
1650 case PNG_NEXT:
1651 if (entries > 1)
1652 return change_filename(DIR_NEXT);
1653 break;
1655 #ifdef PNG_PREVIOUS_REPEAT
1656 case PNG_PREVIOUS_REPEAT:
1657 #endif
1658 case PNG_PREVIOUS:
1659 if (entries > 1)
1660 return change_filename(DIR_PREV);
1661 break;
1663 case PNG_ZOOM_IN:
1664 #ifdef PNG_ZOOM_PRE
1665 if (lastbutton != PNG_ZOOM_PRE)
1666 break;
1667 #endif
1668 return ZOOM_IN;
1669 break;
1671 case PNG_ZOOM_OUT:
1672 #ifdef PNG_ZOOM_PRE
1673 if (lastbutton != PNG_ZOOM_PRE)
1674 break;
1675 #endif
1676 return ZOOM_OUT;
1677 break;
1679 #ifdef PNG_RC_MENU
1680 case PNG_RC_MENU:
1681 #endif
1682 case PNG_MENU:
1683 if (show_menu() == 1)
1684 return PLUGIN_OK;
1685 else
1686 return PLUGIN_REFRESH;
1688 break;
1689 default:
1690 if (rb->default_event_handler_ex(button, cleanup, NULL)
1691 == SYS_USB_CONNECTED)
1692 return PLUGIN_USB_CONNECTED;
1693 break;
1695 } /* switch */
1697 if (button != BUTTON_NONE)
1698 lastbutton = button;
1699 } /* while (true) */
1702 /* set the view to the given center point, limit if necessary */
1703 void set_view (struct LodePNG_Decoder* decoder, int cx, int cy)
1705 int x, y;
1707 /* plain center to available width/height */
1708 x = cx - MIN(LCD_WIDTH, decoder->infoPng.width/ds) / 2;
1709 y = cy - MIN(LCD_HEIGHT, decoder->infoPng.height/ds) / 2;
1711 /* limit against upper image size */
1712 x = MIN((int)(decoder->infoPng.width/ds) - LCD_WIDTH, x);
1713 y = MIN((int)(decoder->infoPng.height/ds) - LCD_HEIGHT, y);
1715 /* limit against negative side */
1716 x = MAX(0, x);
1717 y = MAX(0, y);
1719 decoder->x = x; /* set the values */
1720 decoder->y = y;
1724 /* callback updating a progress meter while PNG decoding */
1725 void cb_progress(int current, int total)
1728 if (current & 1) rb->yield(); /* be nice to the other threads */
1729 if (!running_slideshow)
1731 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],
1732 0, LCD_HEIGHT-8, LCD_WIDTH, 8,
1733 total, 0, current, HORIZONTAL);
1734 rb->lcd_update_rect(0, LCD_HEIGHT-8, LCD_WIDTH, 8);
1736 else
1738 /* in slideshow mode, keep gui interference to a minimum */
1739 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],
1740 0, LCD_HEIGHT-4, LCD_WIDTH, 4,
1741 total, 0, current, HORIZONTAL);
1742 rb->lcd_update_rect(0, LCD_HEIGHT-4, LCD_WIDTH, 4);
1746 int pngmem(struct LodePNG_Decoder* decoder, int ds)
1748 return (decoder->infoPng.width/ds) * (decoder->infoPng.height/ds) * FB_DATA_SZ;
1751 /* how far can we zoom in without running out of memory */
1752 int min_downscale(struct LodePNG_Decoder* decoder, int bufsize)
1754 int downscale = 8;
1756 if (pngmem(decoder, 8) > bufsize)
1757 return 0; /* error, too large, even 1:8 doesn't fit */
1759 while (downscale > 1 && pngmem(decoder, downscale/2) <= bufsize)
1760 downscale /= 2;
1762 return downscale;
1765 /* how far can we zoom out, to fit image into the LCD */
1766 unsigned max_downscale(struct LodePNG_Decoder* decoder)
1768 unsigned downscale = 1;
1770 while (downscale < 8 && (decoder->infoPng.width/downscale > LCD_WIDTH
1771 || decoder->infoPng.height/downscale > LCD_HEIGHT))
1773 downscale *= 2;
1776 return downscale;
1779 /* calculate the view center based on the bitmap position */
1780 void get_view(struct LodePNG_Decoder* decoder, int* p_cx, int* p_cy)
1782 *p_cx = decoder->x + MIN(LCD_WIDTH, decoder->infoPng.width/ds) / 2;
1783 *p_cy = decoder->y + MIN(LCD_HEIGHT, decoder->infoPng.height/ds) / 2;
1786 /* return decoded or cached image */
1787 fb_data *get_image(struct LodePNG_Decoder* decoder)
1789 fb_data * p_disp = disp[ds]; /* short cut */
1791 if (p_disp != NULL)
1793 DEBUGF("Found an image in cache\n");
1794 return p_disp; /* we still have it */
1797 /* assign image buffer */
1798 if (ds > 1) {
1799 if (!running_slideshow)
1801 rb->snprintf(print, sizeof(print), "resizing %d*%d",
1802 decoder->infoPng.width/ds, decoder->infoPng.height/ds);
1803 rb->lcd_puts(0, 3, print);
1804 rb->lcd_update();
1806 static struct bitmap bmp_src, bmp_dst;
1808 int size = (decoder->infoPng.width/ds) * (decoder->infoPng.height/ds);
1809 disp[ds] = disp_buf;
1811 if ((unsigned char *)(disp[ds] + size) >= memory_max) {
1812 //rb->splash(HZ, "Out of Memory");
1813 // Still display the original image which is already decoded in RAM
1814 disp[ds] = converted_image;
1815 ds = 1;
1816 return converted_image;
1817 } else {
1818 bmp_src.width = decoder->infoPng.width;
1819 bmp_src.height = decoder->infoPng.height;
1820 bmp_src.data = (unsigned char *)converted_image;
1822 bmp_dst.width = decoder->infoPng.width/ds;
1823 bmp_dst.height = decoder->infoPng.height/ds;
1824 bmp_dst.data = (unsigned char *)disp[ds];
1825 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1826 rb->cpu_boost(true);
1827 smooth_resize_bitmap(&bmp_src, &bmp_dst);
1828 rb->cpu_boost(false);
1829 #else
1830 smooth_resize_bitmap(&bmp_src, &bmp_dst);
1831 #endif /*HAVE_ADJUSTABLE_CPU_FREQ*/
1833 disp_buf = (fb_data *)((intptr_t)(disp[ds] + size + 3) & ~3);
1835 } else {
1836 disp[ds] = converted_image;
1837 return converted_image;
1840 return disp[ds];
1843 /* load, decode, display the image */
1844 int load_and_show(char* filename)
1846 int fd;
1847 int status;
1848 long time=0; /* measured ticks */
1849 int cx=0, cy=0; /* view center */
1850 int w, h; /* used to center output */
1852 LodePNG_Decoder_init(&decoder);
1854 rb->lcd_clear_display();
1856 fd = rb->open(filename, O_RDONLY);
1857 if (fd < 0)
1859 rb->snprintf(print,sizeof(print),"err opening %s:%d",filename,fd);
1860 rb->splash(HZ, print);
1861 return PLUGIN_ERROR;
1863 image_size = rb->filesize(fd);
1864 memset(&disp, 0, sizeof(disp));
1866 DEBUGF("reading file '%s'\n", filename);
1868 if (!running_slideshow) {
1869 #if LCD_DEPTH > 1
1870 rb->lcd_set_foreground(LCD_WHITE);
1871 rb->lcd_set_background(LCD_BLACK);
1872 rb->lcd_set_backdrop(NULL);
1873 #endif
1875 rb->lcd_clear_display();
1876 rb->snprintf(print, sizeof(print), "%s:", rb->strrchr(filename,'/')+1);
1877 rb->lcd_puts(0, 0, print);
1878 rb->lcd_update();
1881 if (rb->button_get(false) == PNG_MENU) {
1882 decoder.error = PLUGIN_ABORT;
1883 rb->close(fd);
1885 } else if (image_size > memory_size) {
1886 decoder.error = FILE_TOO_LARGE;
1887 rb->close(fd);
1889 } else {
1890 if (!running_slideshow) {
1891 rb->snprintf(print, sizeof(print), "loading %lu bytes", image_size);
1892 rb->lcd_puts(0, 1, print);
1893 rb->lcd_update();
1896 image = memory_max - image_size + 1;
1897 rb->read(fd, image, image_size);
1898 rb->close(fd);
1900 if (!running_slideshow) {
1901 rb->snprintf(print, sizeof(print), "decoding image");
1902 rb->lcd_puts(0, 2, print);
1903 rb->lcd_update();
1905 #ifndef SIMULATOR
1906 else if (immediate_ata_off) {
1907 /* running slideshow and time is long enough: power down disk */
1908 rb->storage_sleep();
1910 #endif
1912 decoder.settings.color_convert = 1;
1913 decoder.infoRaw.color.colorType = 2;
1914 decoder.infoRaw.color.bitDepth = 8;
1916 if (rb->button_get(false) == PNG_MENU) {
1917 decoder.error = PLUGIN_ABORT;
1918 } else {
1919 LodePNG_inspect(&decoder, image, image_size);
1922 if (!decoder.error) {
1924 if (!running_slideshow) {
1925 rb->snprintf(print, sizeof(print), "image %dx%d", decoder.infoPng.width, decoder.infoPng.height);
1926 rb->lcd_puts(0, 2, print);
1927 rb->lcd_update();
1930 ds_max = max_downscale(&decoder); /* check display constraint */
1932 ds = ds_max; /* initials setting */
1934 if (!running_slideshow)
1936 rb->snprintf(print, sizeof(print), "decoding %d*%d",
1937 decoder.infoPng.width, decoder.infoPng.height);
1938 rb->lcd_puts(0, 3, print);
1939 rb->lcd_update();
1942 /* the actual decoding */
1943 time = *rb->current_tick;
1944 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1945 rb->cpu_boost(true);
1946 LodePNG_decode(&decoder, image, image_size, cb_progress);
1947 rb->cpu_boost(false);
1948 #else
1949 LodePNG_decode(&decoder, image, image_size, cb_progress);
1950 #endif /*HAVE_ADJUSTABLE_CPU_FREQ*/
1952 disp_buf = (fb_data *)((intptr_t)(converted_image + converted_image_size + 3) & ~3);
1953 ds_min = min_downscale(&decoder, memory_max - (unsigned char*)disp_buf); /* check memory constraint */
1955 if (ds_min == 0) {
1956 // Could not resize the image
1957 ds_min = ds = ds_max = 1;
1962 if (decoder.error == PLUGIN_ABORT || decoder.error == FILE_TOO_LARGE) {
1963 #ifndef SIMULATOR
1964 if (immediate_ata_off) {
1965 /* running slideshow and time is long enough: power down disk */
1966 rb->storage_sleep();
1968 #endif
1971 time = *rb->current_tick - time;
1973 if (!running_slideshow && !decoder.error)
1975 rb->snprintf(print, sizeof(print), " %ld.%02ld sec ", time/HZ, time%HZ);
1976 rb->lcd_getstringsize(print, &w, &h); /* centered in progress bar */
1977 rb->lcd_putsxy((LCD_WIDTH - w)/2, LCD_HEIGHT - h, print);
1978 rb->lcd_update();
1981 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
1982 if (plug_buf && (decoder.error == FILE_TOO_LARGE || decoder.error == OUT_OF_MEMORY || decoder.error == Z_MEM_ERROR))
1984 rb->lcd_setfont(FONT_SYSFIXED);
1985 rb->lcd_clear_display();
1986 rb->snprintf(print,sizeof(print),"%s:",rb->strrchr(filename,'/')+1);
1987 rb->lcd_puts(0,0,print);
1988 rb->lcd_puts(0,1,"Not enough plugin memory!");
1989 rb->lcd_puts(0,2,"Zoom In: Stop playback.");
1990 if (entries>1)
1991 rb->lcd_puts(0,3,"Left/Right: Skip File.");
1992 rb->lcd_puts(0,4,"Off: Quit.");
1993 rb->lcd_update();
1994 rb->lcd_setfont(FONT_UI);
1996 rb->button_clear_queue();
1998 while (1)
2000 int button = rb->button_get(true);
2001 switch (button)
2003 case PNG_ZOOM_IN:
2004 plug_buf = false;
2005 memory = rb->plugin_get_audio_buffer((size_t *)&memory_size);
2006 memory_max = memory + memory_size - 1;
2007 /*try again this file, now using the audio buffer */
2008 return PLUGIN_OTHER;
2009 #ifdef PNG_RC_MENU
2010 case PNG_RC_MENU:
2011 #endif
2012 case PNG_MENU:
2013 return PLUGIN_OK;
2015 case PNG_LEFT:
2016 if (entries>1)
2018 rb->lcd_clear_display();
2019 return change_filename(DIR_PREV);
2021 break;
2023 case PNG_RIGHT:
2024 if (entries>1)
2026 rb->lcd_clear_display();
2027 return change_filename(DIR_NEXT);
2029 break;
2030 default:
2031 if (rb->default_event_handler_ex(button, cleanup, NULL)
2032 == SYS_USB_CONNECTED)
2033 return PLUGIN_USB_CONNECTED;
2038 //else
2039 #endif
2041 if (decoder.error) {
2043 switch (decoder.error) {
2044 case PLUGIN_ABORT:
2045 rb->splash(HZ, "aborted");break;
2046 case 27:
2047 rb->splash(HZ, "png file smaller than a png header");break;
2048 case 28:
2049 rb->splash(HZ, "incorrect png signature");break;
2050 case 29:
2051 rb->splash(HZ, "first chunk is not IHDR");break;
2052 case 30:
2053 rb->splash(HZ, "chunk length too large");break;
2054 case 31:
2055 rb->splash(HZ, "illegal PNG color type or bpp");break;
2056 case 32:
2057 rb->splash(HZ, "illegal PNG compression method");break;
2058 case 33:
2059 rb->splash(HZ, "illegal PNG filter method");break;
2060 case 34:
2061 rb->splash(HZ, "illegal PNG interlace method");break;
2062 case 35:
2063 rb->splash(HZ, "chunk length of a chunk is too large or the chunk too small");break;
2064 case 36:
2065 rb->splash(HZ, "illegal PNG filter type encountered");break;
2066 case 37:
2067 rb->splash(HZ, "illegal bit depth for this color type given");break;
2068 case 38:
2069 rb->splash(HZ, "the palette is too big (more than 256 colors)");break;
2070 case 39:
2071 rb->splash(HZ, "more palette alpha values given in tRNS, than there are colors in the palette");break;
2072 case 40:
2073 rb->splash(HZ, "tRNS chunk has wrong size for greyscale image");break;
2074 case 41:
2075 rb->splash(HZ, "tRNS chunk has wrong size for RGB image");break;
2076 case 42:
2077 rb->splash(HZ, "tRNS chunk appeared while it was not allowed for this color type");break;
2078 case 43:
2079 rb->splash(HZ, "bKGD chunk has wrong size for palette image");break;
2080 case 44:
2081 rb->splash(HZ, "bKGD chunk has wrong size for greyscale image");break;
2082 case 45:
2083 rb->splash(HZ, "bKGD chunk has wrong size for RGB image");break;
2084 case 46:
2085 case 47:
2086 rb->splash(HZ, "value encountered in indexed image is larger than the palette size");break;
2087 case 48:
2088 rb->splash(HZ, "input file is empty");break;
2089 case OUT_OF_MEMORY:
2090 case Z_MEM_ERROR:
2091 rb->splash(HZ, "Out of Memory");break;
2092 case 57:
2093 rb->splash(HZ, "invalid CRC");break;
2094 case 59:
2095 rb->splash(HZ, "conversion to unexisting or unsupported color type or bit depth");break;
2096 case 63:
2097 rb->splash(HZ, "png chunk too long");break;
2098 case 69:
2099 rb->splash(HZ, "unknown critical chunk");break;
2100 case 73:
2101 rb->splash(HZ, "invalid tIME chunk size");break;
2102 case 74:
2103 rb->splash(HZ, "invalid pHYs chunk size");break;
2104 case FILE_TOO_LARGE:
2105 rb->splash(HZ, "File too large");break;
2106 case Z_DATA_ERROR:
2107 rb->splash(HZ, decoder.error_msg);break;
2108 default:
2109 rb->splashf(HZ, "other error : %ld", decoder.error);break;
2112 if (decoder.error == PLUGIN_ABORT) {
2113 return PLUGIN_OK;
2114 } else {
2115 file_pt[curfile] = NULL;
2116 return change_filename(direction);
2120 do {
2121 resized_image = get_image(&decoder); /* decode or fetch from cache */
2123 cx = decoder.infoPng.width/ds/2; /* center the view */
2124 cy = decoder.infoPng.height/ds/2;
2126 set_view(&decoder, cx, cy);
2128 if (!running_slideshow)
2130 rb->snprintf(print, sizeof(print), "showing %dx%d",
2131 decoder.infoPng.width/ds, decoder.infoPng.height/ds);
2132 rb->lcd_puts(0, 3, print);
2133 rb->lcd_update();
2136 rb->lcd_clear_display();
2137 draw_image(&decoder);
2138 rb->lcd_update();
2140 /* drawing is now finished, play around with scrolling
2141 * until you press OFF or connect USB
2143 while (1)
2145 status = scroll_bmp(&decoder);
2146 if (status == ZOOM_IN)
2148 if (ds > ds_min)
2150 while (1)
2152 ds /= 2; /* reduce downscaling to zoom in */
2153 get_view(&decoder, &cx, &cy);
2154 cx *= 2; /* prepare the position in the new image */
2155 cy *= 2;
2156 if (disp[ds] != converted_image || ds <= ds_min) break;
2159 else
2160 continue;
2163 if (status == ZOOM_OUT)
2165 if (ds < ds_max)
2167 while (1)
2169 ds *= 2; /* increase downscaling to zoom out */
2170 get_view(&decoder, &cx, &cy);
2171 cx /= 2; /* prepare the position in the new image */
2172 cy /= 2;
2173 if (disp[ds] != converted_image || ds >= ds_max) break;
2176 else
2177 continue;
2179 break;
2181 rb->lcd_clear_display();
2183 while (status != PLUGIN_OK && status != PLUGIN_USB_CONNECTED
2184 && status != PLUGIN_OTHER);
2186 return status;
2189 /******************** Plugin entry point *********************/
2191 enum plugin_status plugin_start(const void* parameter)
2193 int condition;
2194 #if LCD_DEPTH > 1
2195 old_backdrop = rb->lcd_get_backdrop();
2196 #endif
2198 if (!parameter) return PLUGIN_ERROR;
2200 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
2201 memory = rb->plugin_get_buffer((size_t *)&memory_size);
2202 #else
2203 memory = rb->plugin_get_audio_buffer((size_t *)&memory_size);
2204 #endif
2206 rb->strcpy(np_file, parameter);
2207 get_pic_list();
2209 if (!entries) return PLUGIN_ERROR;
2211 #if (PLUGIN_BUFFER_SIZE >= MIN_MEM) && !defined(SIMULATOR)
2212 if (rb->audio_status()) {
2213 plug_buf = true;
2214 } else {
2215 memory = rb->plugin_get_audio_buffer((size_t *)&memory_size);
2217 #endif
2219 memory_max = memory + memory_size - 1;
2221 /* should be ok to just load settings since the plugin itself has
2222 just been loaded from disk and the drive should be spinning */
2223 configfile_load(PNG_CONFIGFILE, png_config,
2224 ARRAYLEN(png_config), PNG_SETTINGS_MINVERSION);
2225 old_settings = png_settings;
2227 /* Turn off backlight timeout */
2228 backlight_force_on(); /* backlight control in lib/helper.c */
2232 condition = load_and_show(np_file);
2233 }while (condition != PLUGIN_OK && condition != PLUGIN_USB_CONNECTED
2234 && condition != PLUGIN_ERROR);
2236 if (rb->memcmp(&png_settings, &old_settings, sizeof (png_settings)))
2238 /* Just in case drive has to spin, keep it from looking locked */
2239 rb->splash(0, "Saving Settings");
2240 configfile_save(PNG_CONFIGFILE, png_config,
2241 ARRAYLEN(png_config), PNG_SETTINGS_VERSION);
2244 #if !defined(SIMULATOR) && defined(HAVE_DISK_STORAGE)
2245 /* set back ata spindown time in case we changed it */
2246 rb->storage_spindown(rb->global_settings->disk_spindown);
2247 #endif
2249 /* Turn on backlight timeout (revert to settings) */
2250 backlight_use_settings(); /* backlight control in lib/helper.c */
2252 return condition;