Add a png viewer plugin
[kugel-rb.git] / apps / plugins / png / png.c
blobd54d839a676a778ebbe4ca4c71a8c70589049373
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 static fb_data *previous_disp;
156 static size_t size[9];
157 static size_t previous_size;
159 /* my memory pool (from the mp3 buffer) */
160 static char print[128]; /* use a common snprintf() buffer */
162 unsigned char *memory, *memory_max;
163 static size_t memory_size;
165 static unsigned char *image; /* where we put the content of the file */
166 static size_t image_size;
168 #if LCD_DEPTH >= 8
169 static fb_data *converted_image __attribute__ ((aligned (16))); /* the (color) converted image */
170 #else
171 static fb_data *converted_image; /* the (color) converted image */
172 #endif
173 static size_t converted_image_size;
175 static unsigned char *decoded_image; /* the decoded image */
176 static size_t decoded_image_size;
178 #if LCD_DEPTH >= 8
179 static fb_data *resized_image __attribute__ ((aligned (16))); /* the decoded image */
180 #else
181 static fb_data *resized_image; /* the decoded image */
182 #endif
184 static struct tree_context *tree;
186 /* the current full file name */
187 static char np_file[MAX_PATH];
188 static int curfile = 0, direction = DIR_NONE, entries = 0;
190 static LodePNG_Decoder decoder;
192 /* list of the jpeg files */
193 static char **file_pt;
194 /* are we using the plugin buffer or the audio buffer? */
195 bool plug_buf = false;
197 /* Persistent configuration */
198 #define PNG_CONFIGFILE "png.cfg"
199 #define PNG_SETTINGS_MINVERSION 1
200 #define PNG_SETTINGS_VERSION 1
202 /* Slideshow times */
203 #define SS_MIN_TIMEOUT 1
204 #define SS_MAX_TIMEOUT 20
205 #define SS_DEFAULT_TIMEOUT 5
207 struct png_settings
209 int ss_timeout;
212 static struct png_settings png_settings =
214 SS_DEFAULT_TIMEOUT
216 static struct png_settings old_settings;
218 static struct configdata png_config[] =
220 { TYPE_INT, SS_MIN_TIMEOUT, SS_MAX_TIMEOUT,
221 { .int_p = &png_settings.ss_timeout }, "Slideshow Time", NULL
225 #if LCD_DEPTH > 1
226 static fb_data* old_backdrop;
227 #endif
229 #define MAX_X_SIZE LCD_WIDTH*8
231 /* Min memory allowing us to use the plugin buffer
232 * and thus not stopping the music
233 * *Very* rough estimation:
234 * Max 10 000 dir entries * 4bytes/entry (char **) = 40000 bytes
235 * + 30k code size = 70 000
236 * + 50k min for png = 130 000
238 #define MIN_MEM 130000
240 static int slideshow_enabled = false; /* run slideshow */
241 static int running_slideshow = false; /* loading image because of slideshw */
242 #ifndef SIMULATOR
243 static int immediate_ata_off = false; /* power down disk after loading */
244 #endif
246 static unsigned ds, ds_min, ds_max; /* downscaling and limits */
249 The two functions below (LodePNG_decompress and LodePNG_compress) directly call the
250 LodeZlib_decompress and LodeZlib_compress functions. The only purpose of the functions
251 below, is to provide the ability to let LodePNG use a different Zlib encoder by only
252 changing the two functions below, instead of changing it inside the vareous places
253 in the other LodePNG functions.
255 *out must be NULL and *outsize must be 0 initially, and after the function is done,
256 *out must point to the decompressed data, *outsize must be the size of it, and must
257 be the size of the useful data in bytes, not the alloc size.
260 static unsigned LodePNG_decompress(unsigned char* out, size_t* outsize, const unsigned char* in, size_t insize, char *error_msg)
262 z_stream stream;
263 int err;
265 error_msg = "";
267 stream.next_in = (Bytef*)in;
268 stream.avail_in = (uInt)insize;
270 stream.next_out = out;
271 stream.avail_out = (uInt)*outsize;
273 stream.zalloc = (alloc_func)0;
274 stream.zfree = (free_func)0;
276 err = inflateInit(&stream);
277 if (err != Z_OK) return err;
279 err = inflate(&stream, Z_FINISH);
280 if (err != Z_STREAM_END) {
281 inflateEnd(&stream);
282 if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0))
283 return Z_DATA_ERROR;
284 return err;
286 *outsize = stream.total_out;
288 err = inflateEnd(&stream);
289 error_msg = stream.msg;
290 return err;
294 /* ////////////////////////////////////////////////////////////////////////// */
295 /* / Reading and writing single bits and bytes from/to stream for LodePNG / */
296 /* ////////////////////////////////////////////////////////////////////////// */
298 static unsigned char readBitFromReversedStream(size_t* bitpointer, const unsigned char* bitstream)
300 unsigned char result = (unsigned char)((bitstream[(*bitpointer) >> 3] >> (7 - ((*bitpointer) & 0x7))) & 1);
301 (*bitpointer)++;
302 return result;
305 static unsigned readBitsFromReversedStream(size_t* bitpointer, const unsigned char* bitstream, size_t nbits)
307 unsigned result = 0;
308 size_t i;
309 for (i = nbits - 1; i < nbits; i--) result += (unsigned)readBitFromReversedStream(bitpointer, bitstream) << i;
310 return result;
313 static void setBitOfReversedStream0(size_t* bitpointer, unsigned char* bitstream, unsigned char bit)
315 /*the current bit in bitstream must be 0 for this to work*/
316 if (bit) bitstream[(*bitpointer) >> 3] |= (bit << (7 - ((*bitpointer) & 0x7))); /*earlier bit of huffman code is in a lesser significant bit of an earlier byte*/
317 (*bitpointer)++;
320 static void setBitOfReversedStream(size_t* bitpointer, unsigned char* bitstream, unsigned char bit)
322 /*the current bit in bitstream may be 0 or 1 for this to work*/
323 if (bit == 0) bitstream[(*bitpointer) >> 3] &= (unsigned char)(~(1 << (7 - ((*bitpointer) & 0x7))));
324 else bitstream[(*bitpointer) >> 3] |= (1 << (7 - ((*bitpointer) & 0x7)));
325 (*bitpointer)++;
328 static unsigned LodePNG_read32bitInt(const unsigned char* buffer)
330 return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
333 /* ////////////////////////////////////////////////////////////////////////// */
334 /* / PNG chunks / */
335 /* ////////////////////////////////////////////////////////////////////////// */
337 unsigned LodePNG_chunk_length(const unsigned char* chunk) /*get the length of the data of the chunk. Total chunk length has 12 bytes more.*/
339 return LodePNG_read32bitInt(&chunk[0]);
342 void LodePNG_chunk_type(char type[5], const unsigned char* chunk) /*puts the 4-byte type in null terminated string*/
344 unsigned i;
345 for (i = 0; i < 4; i++) type[i] = chunk[4 + i];
346 type[4] = 0; /*null termination char*/
349 unsigned char LodePNG_chunk_type_equals(const unsigned char* chunk, const char* type) /*check if the type is the given type*/
351 if (type[4] != 0) return 0;
352 return (chunk[4] == type[0] && chunk[5] == type[1] && chunk[6] == type[2] && chunk[7] == type[3]);
355 /*properties of PNG chunks gotten from capitalization of chunk type name, as defined by the standard*/
356 unsigned char LodePNG_chunk_critical(const unsigned char* chunk) /*0: ancillary chunk, 1: it's one of the critical chunk types*/
358 return((chunk[4] & 32) == 0);
361 unsigned char LodePNG_chunk_private(const unsigned char* chunk) /*0: public, 1: private*/
363 return((chunk[6] & 32) != 0);
366 unsigned char LodePNG_chunk_safetocopy(const unsigned char* chunk) /*0: the chunk is unsafe to copy, 1: the chunk is safe to copy*/
368 return((chunk[7] & 32) != 0);
371 unsigned char* LodePNG_chunk_data(unsigned char* chunk) /*get pointer to the data of the chunk*/
373 return &chunk[8];
376 const unsigned char* LodePNG_chunk_data_const(const unsigned char* chunk) /*get pointer to the data of the chunk*/
378 return &chunk[8];
381 unsigned LodePNG_chunk_check_crc(const unsigned char* chunk) /*returns 0 if the crc is correct, error code if it's incorrect*/
383 unsigned length = LodePNG_chunk_length(chunk);
384 unsigned CRC = LodePNG_read32bitInt(&chunk[length + 8]);
385 unsigned checksum = crc32(0L, &chunk[4], length + 4); /*the CRC is taken of the data and the 4 chunk type letters, not the length*/
386 if (CRC != checksum) return 1;
387 else return 0;
390 unsigned char* LodePNG_chunk_next(unsigned char* chunk) /*don't use on IEND chunk, as there is no next chunk then*/
392 unsigned total_chunk_length = LodePNG_chunk_length(chunk) + 12;
393 return &chunk[total_chunk_length];
396 const unsigned char* LodePNG_chunk_next_const(const unsigned char* chunk) /*don't use on IEND chunk, as there is no next chunk then*/
398 unsigned total_chunk_length = LodePNG_chunk_length(chunk) + 12;
399 return &chunk[total_chunk_length];
402 /* ////////////////////////////////////////////////////////////////////////// */
403 /* / Color types and such / */
404 /* ////////////////////////////////////////////////////////////////////////// */
406 /*return type is a LodePNG error code*/
407 static unsigned checkColorValidity(unsigned colorType, unsigned bd) /*bd = bitDepth*/
409 switch (colorType)
411 case 0:
412 if (!(bd == 1 || bd == 2 || bd == 4 || bd == 8 || bd == 16)) return 37; break; /*grey*/
413 case 2:
414 if (!( bd == 8 || bd == 16)) return 37; break; /*RGB*/
415 case 3:
416 if (!(bd == 1 || bd == 2 || bd == 4 || bd == 8 )) return 37; break; /*palette*/
417 case 4:
418 if (!( bd == 8 || bd == 16)) return 37; break; /*grey + alpha*/
419 case 6:
420 if (!( bd == 8 || bd == 16)) return 37; break; /*RGBA*/
421 default:
422 return 31;
424 return 0; /*allowed color type / bits combination*/
427 static unsigned getNumColorChannels(unsigned colorType)
429 switch (colorType)
431 case 0:
432 return 1; /*grey*/
433 case 2:
434 return 3; /*RGB*/
435 case 3:
436 return 1; /*palette*/
437 case 4:
438 return 2; /*grey + alpha*/
439 case 6:
440 return 4; /*RGBA*/
442 return 0; /*unexisting color type*/
445 static unsigned getBpp(unsigned colorType, unsigned bitDepth)
447 return getNumColorChannels(colorType) * bitDepth; /*bits per pixel is amount of channels * bits per channel*/
450 /* ////////////////////////////////////////////////////////////////////////// */
452 void LodePNG_InfoColor_init(LodePNG_InfoColor* info)
454 info->key_defined = 0;
455 info->key_r = info->key_g = info->key_b = 0;
456 info->colorType = 6;
457 info->bitDepth = 8;
458 memset(info->palette, 0, 256 * 4 * sizeof(unsigned char));
459 info->palettesize = 0;
462 void LodePNG_InfoColor_cleanup(LodePNG_InfoColor* info)
464 info->palettesize = 0;
467 unsigned LodePNG_InfoColor_getBpp(const LodePNG_InfoColor* info) { return getBpp(info->colorType, info->bitDepth); } /*calculate bits per pixel out of colorType and bitDepth*/
468 unsigned LodePNG_InfoColor_isGreyscaleType(const LodePNG_InfoColor* info) { return info->colorType == 0 || info->colorType == 4; }
470 unsigned LodePNG_InfoColor_equal(const LodePNG_InfoColor* info1, const LodePNG_InfoColor* info2)
472 return info1->colorType == info2->colorType
473 && info1->bitDepth == info2->bitDepth; /*palette and color key not compared*/
476 void LodePNG_InfoPng_init(LodePNG_InfoPng* info)
478 info->width = info->height = 0;
479 LodePNG_InfoColor_init(&info->color);
480 info->interlaceMethod = 0;
481 info->compressionMethod = 0;
482 info->filterMethod = 0;
483 info->background_defined = 0;
484 info->background_r = info->background_g = info->background_b = 0;
486 info->time_defined = 0;
487 info->phys_defined = 0;
490 void LodePNG_InfoPng_cleanup(LodePNG_InfoPng* info)
492 LodePNG_InfoColor_cleanup(&info->color);
495 unsigned LodePNG_InfoColor_copy(LodePNG_InfoColor* dest, const LodePNG_InfoColor* source)
497 size_t i;
498 LodePNG_InfoColor_cleanup(dest);
499 *dest = *source;
500 for (i = 0; i < source->palettesize * 4; i++) dest->palette[i] = source->palette[i];
501 return 0;
504 unsigned LodePNG_InfoPng_copy(LodePNG_InfoPng* dest, const LodePNG_InfoPng* source)
506 unsigned error = 0;
507 LodePNG_InfoPng_cleanup(dest);
508 *dest = *source;
509 LodePNG_InfoColor_init(&dest->color);
510 error = LodePNG_InfoColor_copy(&dest->color, &source->color); if (error) return error;
511 return error;
514 void LodePNG_InfoPng_swap(LodePNG_InfoPng* a, LodePNG_InfoPng* b)
516 LodePNG_InfoPng temp = *a;
517 *a = *b;
518 *b = temp;
521 void LodePNG_InfoRaw_init(LodePNG_InfoRaw* info)
523 LodePNG_InfoColor_init(&info->color);
526 void LodePNG_InfoRaw_cleanup(LodePNG_InfoRaw* info)
528 LodePNG_InfoColor_cleanup(&info->color);
531 unsigned LodePNG_InfoRaw_copy(LodePNG_InfoRaw* dest, const LodePNG_InfoRaw* source)
533 unsigned error = 0;
534 LodePNG_InfoRaw_cleanup(dest);
535 *dest = *source;
536 LodePNG_InfoColor_init(&dest->color);
537 error = LodePNG_InfoColor_copy(&dest->color, &source->color); if (error) return error;
538 return error;
541 /* ////////////////////////////////////////////////////////////////////////// */
544 converts from any color type to 24-bit or 32-bit (later maybe more supported). return value = LodePNG error code
545 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)
546 for < 8 bpp images, there may _not_ be padding bits at the end of scanlines.
548 unsigned LodePNG_convert(fb_data* out, const unsigned char* in, LodePNG_InfoColor* infoOut, LodePNG_InfoColor* infoIn, unsigned w, unsigned h)
550 size_t i, j, bp = 0; /*bitpointer, used by less-than-8-bit color types*/
551 size_t x, y;
552 unsigned char c;
554 if (!running_slideshow)
556 rb->snprintf(print, sizeof(print), "color conversion in progress");
557 rb->lcd_puts(0, 3, print);
558 rb->lcd_update();
561 /*cases where in and out already have the same format*/
562 if (LodePNG_InfoColor_equal(infoIn, infoOut))
565 i = 0;
566 j = 0;
567 for (y = 0 ; y < h ; y++) {
568 for (x = 0 ; x < w ; x++) {
569 unsigned char r = in[i++];
570 unsigned char g = in[i++];
571 unsigned char b = in[i++];
572 out[j++] = LCD_RGBPACK(r,g,b);
575 return 0;
578 if ((infoOut->colorType == 2 || infoOut->colorType == 6) && infoOut->bitDepth == 8)
580 if (infoIn->bitDepth == 8)
582 switch (infoIn->colorType)
584 case 0: /*greyscale color*/
585 i = 0;
586 for (y = 0 ; y < h ; y++) {
587 for (x = 0 ; x < w ; x++) {
588 c=in[i];
589 //unsigned char r = in[i];
590 //unsigned char g = in[i];
591 //unsigned char b = in[i];
592 out[i++] = LCD_RGBPACK(c,c,c);
595 break;
596 case 2: /*RGB color*/
597 i = 0;
598 for (y = 0 ; y < h ; y++) {
599 for (x = 0 ; x < w ; x++) {
600 j = 3 * i;
601 unsigned char r = in[j];
602 unsigned char g = in[j + 1];
603 unsigned char b = in[j + 2];
604 out[i++] = LCD_RGBPACK(r,g,b);
607 break;
608 case 3: /*indexed color (palette)*/
609 i = 0;
610 for (y = 0 ; y < h ; y++) {
611 for (x = 0 ; x < w ; x++) {
612 if (in[i] >= infoIn->palettesize) return 46;
613 j = in[i] << 2;
614 unsigned char r = infoIn->palette[j];
615 unsigned char g = infoIn->palette[j + 1];
616 unsigned char b = infoIn->palette[j + 2];
617 out[i++] = LCD_RGBPACK(r,g,b);
620 break;
621 case 4: /*greyscale with alpha*/
622 i = 0;
623 for (y = 0 ; y < h ; y++) {
624 for (x = 0 ; x < w ; x++) {
625 c = in[i << 1];
626 //unsigned char r = in[i<<1];
627 //unsigned char g = in[i<<1];
628 //unsigned char b = in[i<<1];
629 out[i++] = LCD_RGBPACK(c,c,c);
632 break;
633 case 6: /*RGB with alpha*/
634 i = 0;
635 for (y = 0 ; y < h ; y++) {
636 for (x = 0 ; x < w ; x++) {
637 j = i << 2;
638 unsigned char r = in[j];
639 unsigned char g = in[j + 1];
640 unsigned char b = in[j + 2];
641 out[i++] = LCD_RGBPACK(r,g,b);
644 break;
645 default:
646 break;
649 else if (infoIn->bitDepth == 16)
651 switch (infoIn->colorType)
653 case 0: /*greyscale color*/
654 i = 0;
655 for (y = 0 ; y < h ; y++) {
656 for (x = 0 ; x < w ; x++) {
657 c = in[i << 1];
658 //unsigned char r = in[2 * i];
659 //unsigned char g = in[2 * i];
660 //unsigned char b = in[2 * i];
661 out[i++] = LCD_RGBPACK(c,c,c);
664 break;
665 case 2: /*RGB color*/
666 i = 0;
667 for (y = 0 ; y < h ; y++) {
668 for (x = 0 ; x < w ; x++) {
669 j = 6 * i;
670 unsigned char r = in[j];
671 unsigned char g = in[j + 2];
672 unsigned char b = in[j + 4];
673 out[i++] = LCD_RGBPACK(r,g,b);
676 break;
677 case 4: /*greyscale with alpha*/
678 i = 0;
679 for (y = 0 ; y < h ; y++) {
680 for (x = 0 ; x < w ; x++) {
681 c = in[i << 2];
682 //unsigned char r = in[4 * i];
683 //unsigned char g = in[4 * i];
684 //unsigned char b = in[4 * i];
685 out[i++] = LCD_RGBPACK(c,c,c);
688 break;
689 case 6: /*RGB with alpha*/
690 i = 0;
691 for (y = 0 ; y < h ; y++) {
692 for (x = 0 ; x < w ; x++) {
693 j = i << 3;
694 unsigned char r = in[j];
695 unsigned char g = in[j + 2];
696 unsigned char b = in[j + 4];
697 out[i++] = LCD_RGBPACK(r,g,b);
700 break;
701 default:
702 break;
705 else /*infoIn->bitDepth is less than 8 bit per channel*/
707 switch (infoIn->colorType)
709 case 0: /*greyscale color*/
710 i = 0;
711 for (y = 0 ; y < h ; y++) {
712 for (x = 0 ; x < w ; x++) {
713 unsigned value = readBitsFromReversedStream(&bp, in, infoIn->bitDepth);
714 value = (value * 255) / ((1 << infoIn->bitDepth) - 1); /*scale value from 0 to 255*/
715 unsigned char r = (unsigned char)value;
716 unsigned char g = (unsigned char)value;
717 unsigned char b = (unsigned char)value;
718 out[i++] = LCD_RGBPACK(r,g,b);
721 break;
722 case 3: /*indexed color (palette)*/
723 i = 0;
724 for (y = 0 ; y < h ; y++) {
725 for (x = 0 ; x < w ; x++) {
726 unsigned value = readBitsFromReversedStream(&bp, in, infoIn->bitDepth);
727 if (value >= infoIn->palettesize) return 47;
728 j = value << 2;
729 unsigned char r = infoIn->palette[j];
730 unsigned char g = infoIn->palette[j + 1];
731 unsigned char b = infoIn->palette[j + 2];
732 out[i++] = LCD_RGBPACK(r,g,b);
735 break;
736 default:
737 break;
741 else if (LodePNG_InfoColor_isGreyscaleType(infoOut) && infoOut->bitDepth == 8) /*conversion from greyscale to greyscale*/
743 if (!LodePNG_InfoColor_isGreyscaleType(infoIn)) return 62;
744 if (infoIn->bitDepth == 8)
746 switch (infoIn->colorType)
748 case 0: /*greyscale color*/
749 i = 0;
750 for (y = 0 ; y < h ; y++) {
751 for (x = 0 ; x < w ; x++) {
752 c = in[i];
753 //unsigned char r = in[i];
754 //unsigned char g = in[i];
755 //unsigned char b = in[i];
756 out[i++] = LCD_RGBPACK(c,c,c);
759 break;
760 case 4: /*greyscale with alpha*/
761 i = 0;
762 for (y = 0 ; y < h ; y++) {
763 for (x = 0 ; x < w ; x++) {
764 c = in[(i << 1) + 1];
765 //unsigned char r = in[2 * i + 1];
766 //unsigned char g = in[2 * i + 1];
767 //unsigned char b = in[2 * i + 1];
768 out[i++] = LCD_RGBPACK(c,c,c);
771 break;
772 default:
773 return 31;
776 else if (infoIn->bitDepth == 16)
778 switch (infoIn->colorType)
780 case 0: /*greyscale color*/
781 i = 0;
782 for (y = 0 ; y < h ; y++) {
783 for (x = 0 ; x < w ; x++) {
784 c = in[i << 1];
785 //unsigned char r = in[2 * i];
786 //unsigned char g = in[2 * i];
787 //unsigned char b = in[2 * i];
788 out[i++] = LCD_RGBPACK(c,c,c);
791 break;
792 case 4: /*greyscale with alpha*/
793 i = 0;
794 for (y = 0 ; y < h ; y++) {
795 for (x = 0 ; x < w ; x++) {
796 c = in[i << 2];
797 //unsigned char r = in[4 * i];
798 //unsigned char g = in[4 * i];
799 //unsigned char b = in[4 * i];
800 out[i++] = LCD_RGBPACK(c,c,c);
803 break;
804 default:
805 return 31;
808 else /*infoIn->bitDepth is less than 8 bit per channel*/
810 if (infoIn->colorType != 0) return 31; /*colorType 0 is the only greyscale type with < 8 bits per channel*/
811 i = 0;
812 for (y = 0 ; y < h ; y++) {
813 for (x = 0 ; x < w ; x++) {
814 unsigned value = readBitsFromReversedStream(&bp, in, infoIn->bitDepth);
815 value = (value * 255) / ((1 << infoIn->bitDepth) - 1); /*scale value from 0 to 255*/
816 unsigned char r = (unsigned char)value;
817 unsigned char g = (unsigned char)value;
818 unsigned char b = (unsigned char)value;
819 out[i++] = LCD_RGBPACK(r,g,b);
824 else return 59;
826 return 0;
829 /*Paeth predicter, used by PNG filter type 4*/
830 static int paethPredictor(int a, int b, int c)
832 int p = a + b - c;
833 int pa = p > a ? p - a : a - p;
834 int pb = p > b ? p - b : b - p;
835 int pc = p > c ? p - c : c - p;
837 if (pa <= pb && pa <= pc) return a;
838 else if (pb <= pc) return b;
839 else return c;
842 /*shared values used by multiple Adam7 related functions*/
844 static const unsigned ADAM7_IX[7] = { 0, 4, 0, 2, 0, 1, 0 }; /*x start values*/
845 static const unsigned ADAM7_IY[7] = { 0, 0, 4, 0, 2, 0, 1 }; /*y start values*/
846 static const unsigned ADAM7_DX[7] = { 8, 8, 4, 4, 2, 2, 1 }; /*x delta values*/
847 static const unsigned ADAM7_DY[7] = { 8, 8, 8, 4, 4, 2, 2 }; /*y delta values*/
849 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)
851 /*the passstart values have 8 values: the 8th one actually indicates the byte after the end of the 7th (= last) pass*/
852 unsigned i;
854 /*calculate width and height in pixels of each pass*/
855 for (i = 0; i < 7; i++)
857 passw[i] = (w + ADAM7_DX[i] - ADAM7_IX[i] - 1) / ADAM7_DX[i];
858 passh[i] = (h + ADAM7_DY[i] - ADAM7_IY[i] - 1) / ADAM7_DY[i];
859 if (passw[i] == 0) passh[i] = 0;
860 if (passh[i] == 0) passw[i] = 0;
863 filter_passstart[0] = padded_passstart[0] = passstart[0] = 0;
864 for (i = 0; i < 7; i++)
866 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)*/
867 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*/
868 passstart[i + 1] = passstart[i] + (passh[i] * passw[i] * bpp + 7) / 8; /*only padded at end of reduced image*/
872 /* ////////////////////////////////////////////////////////////////////////// */
873 /* / PNG Decoder / */
874 /* ////////////////////////////////////////////////////////////////////////// */
876 /*read the information from the header and store it in the LodePNG_Info. return value is error*/
877 void LodePNG_inspect(LodePNG_Decoder* decoder, const unsigned char* in, size_t inlength)
879 if (inlength == 0 || in == 0) { decoder->error = 48; return; } /*the given data is empty*/
880 if (inlength < 29) { decoder->error = 27; return; } /*error: the data length is smaller than the length of the header*/
882 /*when decoding a new PNG image, make sure all parameters created after previous decoding are reset*/
883 LodePNG_InfoPng_cleanup(&decoder->infoPng);
884 LodePNG_InfoPng_init(&decoder->infoPng);
885 decoder->error = 0;
887 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*/
888 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!*/
890 /*read the values given in the header*/
891 decoder->infoPng.width = LodePNG_read32bitInt(&in[16]);
892 decoder->infoPng.height = LodePNG_read32bitInt(&in[20]);
893 decoder->infoPng.color.bitDepth = in[24];
894 decoder->infoPng.color.colorType = in[25];
895 decoder->infoPng.compressionMethod = in[26];
896 decoder->infoPng.filterMethod = in[27];
897 decoder->infoPng.interlaceMethod = in[28];
899 unsigned CRC = LodePNG_read32bitInt(&in[29]);
900 unsigned checksum = crc32(0L, &in[12], 17);
901 if (CRC != checksum) { decoder->error = 57; return; }
903 if (decoder->infoPng.compressionMethod != 0) { decoder->error = 32; return; } /*error: only compression method 0 is allowed in the specification*/
904 if (decoder->infoPng.filterMethod != 0) { decoder->error = 33; return; } /*error: only filter method 0 is allowed in the specification*/
905 if (decoder->infoPng.interlaceMethod > 1) { decoder->error = 34; return; } /*error: only interlace methods 0 and 1 exist in the specification*/
907 decoder->error = checkColorValidity(decoder->infoPng.color.colorType, decoder->infoPng.color.bitDepth);
910 static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scanline, const unsigned char* precon, size_t bytewidth, unsigned char filterType, size_t length)
913 For PNG filter method 0
914 unfilter a PNG image scanline by scanline. when the pixels are smaller than 1 byte, the filter works byte per byte (bytewidth = 1)
915 precon is the previous unfiltered scanline, recon the result, scanline the current one
916 the incoming scanlines do NOT include the filtertype byte, that one is given in the parameter filterType instead
917 recon and scanline MAY be the same memory address! precon must be disjoint.
920 size_t i;
921 switch (filterType)
923 case 0:
924 //for(i = 0; i < length; i++) recon[i] = scanline[i];
925 memcpy(recon, scanline, length * sizeof(unsigned char));
926 break;
927 case 1:
928 //for(i = 0; i < bytewidth; i++) recon[i] = scanline[i];
929 memcpy(recon, scanline, bytewidth * sizeof(unsigned char));
930 for (i = bytewidth; i < length; i++) recon[i] = scanline[i] + recon[i - bytewidth];
931 break;
932 case 2:
933 if (precon) for (i = 0; i < length; i++) recon[i] = scanline[i] + precon[i];
934 else //for(i = 0; i < length; i++) recon[i] = scanline[i];
935 memcpy(recon, scanline, length * sizeof(unsigned char));
936 break;
937 case 3:
938 if (precon)
940 for (i = 0; i < bytewidth; i++) recon[i] = scanline[i] + precon[i] / 2;
941 for (i = bytewidth; i < length; i++) recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) / 2);
943 else
945 //for(i = 0; i < bytewidth; i++) recon[i] = scanline[i];
946 memcpy(recon, scanline, bytewidth * sizeof(unsigned char));
947 for (i = bytewidth; i < length; i++) recon[i] = scanline[i] + recon[i - bytewidth] / 2;
949 break;
950 case 4:
951 if (precon)
953 for (i = 0; i < bytewidth; i++) recon[i] = (unsigned char)(scanline[i] + paethPredictor(0, precon[i], 0));
954 for (i = bytewidth; i < length; i++) recon[i] = (unsigned char)(scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[i - bytewidth]));
956 else
958 //for(i = 0; i < bytewidth; i++) recon[i] = scanline[i];
959 memcpy(recon, scanline, bytewidth * sizeof(unsigned char));
960 for (i = bytewidth; i < length; i++) recon[i] = (unsigned char)(scanline[i] + paethPredictor(recon[i - bytewidth], 0, 0));
962 break;
963 default:
964 return 36; /*error: unexisting filter type given*/
966 return 0;
969 static unsigned unfilter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp)
972 For PNG filter method 0
973 this function unfilters a single image (e.g. without interlacing this is called once, with Adam7 it's called 7 times)
974 out must have enough bytes allocated already, in must have the scanlines + 1 filtertype byte per scanline
975 w and h are image dimensions or dimensions of reduced image, bpp is bits per pixel
976 in and out are allowed to be the same memory address!
979 unsigned y;
980 unsigned char* prevline = 0;
982 size_t bytewidth = (bpp + 7) / 8; /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/
983 size_t linebytes = (w * bpp + 7) / 8;
985 for (y = 0; y < h; y++)
987 size_t outindex = linebytes * y;
988 size_t inindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/
989 unsigned char filterType = in[inindex];
991 unsigned error = unfilterScanline(&out[outindex], &in[inindex + 1], prevline, bytewidth, filterType, linebytes);
992 if (error) return error;
994 prevline = &out[outindex];
997 return 0;
1000 static void Adam7_deinterlace(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp)
1002 /*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
1003 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)*/
1004 unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8];
1005 unsigned i;
1007 Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp);
1009 if (bpp >= 8)
1011 for (i = 0; i < 7; i++)
1013 unsigned x, y, b;
1014 size_t bytewidth = bpp / 8;
1015 for (y = 0; y < passh[i]; y++)
1016 for (x = 0; x < passw[i]; x++)
1018 size_t pixelinstart = passstart[i] + (y * passw[i] + x) * bytewidth;
1019 size_t pixeloutstart = ((ADAM7_IY[i] + y * ADAM7_DY[i]) * w + ADAM7_IX[i] + x * ADAM7_DX[i]) * bytewidth;
1020 for (b = 0; b < bytewidth; b++)
1022 out[pixeloutstart + b] = in[pixelinstart + b];
1027 else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/
1029 for (i = 0; i < 7; i++)
1031 unsigned x, y, b;
1032 unsigned ilinebits = bpp * passw[i];
1033 unsigned olinebits = bpp * w;
1034 size_t obp, ibp; /*bit pointers (for out and in buffer)*/
1035 for (y = 0; y < passh[i]; y++)
1036 for (x = 0; x < passw[i]; x++)
1038 ibp = (8 * passstart[i]) + (y * ilinebits + x * bpp);
1039 obp = (ADAM7_IY[i] + y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + x * ADAM7_DX[i]) * bpp;
1040 for (b = 0; b < bpp; b++)
1042 unsigned char bit = readBitFromReversedStream(&ibp, in);
1043 setBitOfReversedStream0(&obp, out, bit); /*note that this function assumes the out buffer is completely 0, use setBitOfReversedStream otherwise*/
1050 static void removePaddingBits(unsigned char* out, const unsigned char* in, size_t olinebits, size_t ilinebits, unsigned h)
1053 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.
1054 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
1055 also used to move bits after earlier such operations happened, e.g. in a sequence of reduced images from Adam7
1056 only useful if (ilinebits - olinebits) is a value in the range 1..7
1058 unsigned y;
1059 size_t diff = ilinebits - olinebits;
1060 size_t obp = 0, ibp = 0; /*bit pointers*/
1061 for (y = 0; y < h; y++)
1063 size_t x;
1064 for (x = 0; x < olinebits; x++)
1066 unsigned char bit = readBitFromReversedStream(&ibp, in);
1067 setBitOfReversedStream(&obp, out, bit);
1069 ibp += diff;
1073 /*out must be buffer big enough to contain full image, and in must contain the full decompressed data from the IDAT chunks*/
1074 static unsigned postProcessScanlines(unsigned char* out, unsigned char* in, const LodePNG_Decoder* decoder) /*return value is error*/
1077 This function converts the filtered-padded-interlaced data into pure 2D image buffer with the PNG's colortype. Steps:
1078 *) if no Adam7: 1) unfilter 2) remove padding bits (= posible extra bits per scanline if bpp < 8)
1079 *) if adam7: 1) 7x unfilter 2) 7x remove padding bits 3) Adam7_deinterlace
1080 NOTE: the in buffer will be overwritten with intermediate data!
1082 unsigned bpp = LodePNG_InfoColor_getBpp(&decoder->infoPng.color);
1083 unsigned w = decoder->infoPng.width;
1084 unsigned h = decoder->infoPng.height;
1085 unsigned error = 0;
1086 if (bpp == 0) return 31; /*error: invalid colortype*/
1088 if (decoder->infoPng.interlaceMethod == 0)
1090 if (bpp < 8 && w * bpp != ((w * bpp + 7) / 8) * 8)
1092 error = unfilter(in, in, w, h, bpp);
1093 if (error) return error;
1094 removePaddingBits(out, in, w * bpp, ((w * bpp + 7) / 8) * 8, h);
1096 else error = unfilter(out, in, w, h, bpp); /*we can immediatly filter into the out buffer, no other steps needed*/
1098 else /*interlaceMethod is 1 (Adam7)*/
1100 unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8];
1101 unsigned i;
1103 Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp);
1105 for (i = 0; i < 7; i++)
1107 error = unfilter(&in[padded_passstart[i]], &in[filter_passstart[i]], passw[i], passh[i], bpp);
1108 if (error) return error;
1109 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*/
1111 /*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*/
1112 removePaddingBits(&in[passstart[i]], &in[padded_passstart[i]], passw[i] * bpp, ((passw[i] * bpp + 7) / 8) * 8, passh[i]);
1116 Adam7_deinterlace(out, in, w, h, bpp);
1119 return error;
1122 /*read a PNG, the result will be in the same color type as the PNG (hence "generic")*/
1123 static void decodeGeneric(LodePNG_Decoder* decoder, unsigned char* in, size_t size, void (*pf_progress)(int current, int total))
1125 if (pf_progress != NULL)
1126 pf_progress(0, 100);
1127 unsigned char IEND = 0;
1128 const unsigned char* chunk;
1129 size_t i;
1130 unsigned char *idat = memory;
1131 size_t idat_size = 0;
1133 /*for unknown chunk order*/
1134 unsigned unknown = 0;
1135 unsigned critical_pos = 1; /*1 = after IHDR, 2 = after PLTE, 3 = after IDAT*/
1137 /*provide some proper output values if error will happen*/
1138 decoded_image_size = 0;
1140 if (size == 0 || in == 0) { decoder->error = 48; return; } /*the given data is empty*/
1142 LodePNG_inspect(decoder, in, size); /*reads header and resets other parameters in decoder->infoPng*/
1143 if (decoder->error) return;
1145 chunk = &in[33]; /*first byte of the first chunk after the header*/
1147 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*/
1149 unsigned chunkLength;
1150 const unsigned char* data; /*the data in the chunk*/
1152 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*/
1153 chunkLength = LodePNG_chunk_length(chunk); /*length of the data of the chunk, excluding the length bytes, chunk type and CRC bytes*/
1154 if (chunkLength > 2147483647) { decoder->error = 63; break; }
1155 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*/
1156 data = LodePNG_chunk_data_const(chunk);
1158 /*IDAT chunk, containing compressed image data*/
1159 if (LodePNG_chunk_type_equals(chunk, "IDAT"))
1161 size_t oldsize = idat_size;
1162 idat_size += chunkLength;
1163 if (idat + idat_size >= image) { decoder->error = OUT_OF_MEMORY; break; }
1164 memcpy(idat+oldsize, data, chunkLength * sizeof(unsigned char));
1165 critical_pos = 3;
1167 /*IEND chunk*/
1168 else if (LodePNG_chunk_type_equals(chunk, "IEND"))
1170 IEND = 1;
1172 /*palette chunk (PLTE)*/
1173 else if (LodePNG_chunk_type_equals(chunk, "PLTE"))
1175 unsigned pos = 0;
1176 decoder->infoPng.color.palettesize = chunkLength / 3;
1177 if (decoder->infoPng.color.palettesize > 256) { decoder->error = 38; break; } /*error: palette too big*/
1178 for (i = 0; i < decoder->infoPng.color.palettesize; i++)
1180 decoder->infoPng.color.palette[4 * i + 0] = data[pos++]; /*R*/
1181 decoder->infoPng.color.palette[4 * i + 1] = data[pos++]; /*G*/
1182 decoder->infoPng.color.palette[4 * i + 2] = data[pos++]; /*B*/
1183 decoder->infoPng.color.palette[4 * i + 3] = 255; /*alpha*/
1185 critical_pos = 2;
1187 /*palette transparency chunk (tRNS)*/
1188 else if (LodePNG_chunk_type_equals(chunk, "tRNS"))
1190 if (decoder->infoPng.color.colorType == 3)
1192 if (chunkLength > decoder->infoPng.color.palettesize) { decoder->error = 39; break; } /*error: more alpha values given than there are palette entries*/
1193 for (i = 0; i < chunkLength; i++) decoder->infoPng.color.palette[4 * i + 3] = data[i];
1195 else if (decoder->infoPng.color.colorType == 0)
1197 if (chunkLength != 2) { decoder->error = 40; break; } /*error: this chunk must be 2 bytes for greyscale image*/
1198 decoder->infoPng.color.key_defined = 1;
1199 decoder->infoPng.color.key_r = decoder->infoPng.color.key_g = decoder->infoPng.color.key_b = 256 * data[0] + data[1];
1201 else if (decoder->infoPng.color.colorType == 2)
1203 if (chunkLength != 6) { decoder->error = 41; break; } /*error: this chunk must be 6 bytes for RGB image*/
1204 decoder->infoPng.color.key_defined = 1;
1205 decoder->infoPng.color.key_r = 256 * data[0] + data[1];
1206 decoder->infoPng.color.key_g = 256 * data[2] + data[3];
1207 decoder->infoPng.color.key_b = 256 * data[4] + data[5];
1209 else { decoder->error = 42; break; } /*error: tRNS chunk not allowed for other color models*/
1211 /*background color chunk (bKGD)*/
1212 else if (LodePNG_chunk_type_equals(chunk, "bKGD"))
1214 if (decoder->infoPng.color.colorType == 3)
1216 if (chunkLength != 1) { decoder->error = 43; break; } /*error: this chunk must be 1 byte for indexed color image*/
1217 decoder->infoPng.background_defined = 1;
1218 decoder->infoPng.background_r = decoder->infoPng.background_g = decoder->infoPng.background_g = data[0];
1220 else if (decoder->infoPng.color.colorType == 0 || decoder->infoPng.color.colorType == 4)
1222 if (chunkLength != 2) { decoder->error = 44; break; } /*error: this chunk must be 2 bytes for greyscale image*/
1223 decoder->infoPng.background_defined = 1;
1224 decoder->infoPng.background_r = decoder->infoPng.background_g = decoder->infoPng.background_b = 256 * data[0] + data[1];
1226 else if (decoder->infoPng.color.colorType == 2 || decoder->infoPng.color.colorType == 6)
1228 if (chunkLength != 6) { decoder->error = 45; break; } /*error: this chunk must be 6 bytes for greyscale image*/
1229 decoder->infoPng.background_defined = 1;
1230 decoder->infoPng.background_r = 256 * data[0] + data[1];
1231 decoder->infoPng.background_g = 256 * data[2] + data[3];
1232 decoder->infoPng.background_b = 256 * data[4] + data[5];
1235 else if (LodePNG_chunk_type_equals(chunk, "tIME"))
1237 if (chunkLength != 7) { decoder->error = 73; break; }
1238 decoder->infoPng.time_defined = 1;
1239 decoder->infoPng.time.year = 256 * data[0] + data[+ 1];
1240 decoder->infoPng.time.month = data[2];
1241 decoder->infoPng.time.day = data[3];
1242 decoder->infoPng.time.hour = data[4];
1243 decoder->infoPng.time.minute = data[5];
1244 decoder->infoPng.time.second = data[6];
1246 else if (LodePNG_chunk_type_equals(chunk, "pHYs"))
1248 if (chunkLength != 9) { decoder->error = 74; break; }
1249 decoder->infoPng.phys_defined = 1;
1250 decoder->infoPng.phys_x = 16777216 * data[0] + 65536 * data[1] + 256 * data[2] + data[3];
1251 decoder->infoPng.phys_y = 16777216 * data[4] + 65536 * data[5] + 256 * data[6] + data[7];
1252 decoder->infoPng.phys_unit = data[8];
1254 else /*it's not an implemented chunk type, so ignore it: skip over the data*/
1256 if (LodePNG_chunk_critical(chunk)) { decoder->error = 69; break; } /*error: unknown critical chunk (5th bit of first byte of chunk type is 0)*/
1257 unknown = 1;
1260 if (!unknown) /*check CRC if wanted, only on known chunk types*/
1262 long time = *rb->current_tick;
1263 if (LodePNG_chunk_check_crc(chunk)) { decoder->error = 57; break; }
1264 time = *rb->current_tick-time;
1267 if (!IEND) chunk = LodePNG_chunk_next_const(chunk);
1270 if (!decoder->error)
1272 unsigned char *scanlines = idat + idat_size;
1273 size_t scanlines_size = (size_t)memory_max - idat_size + 1;
1274 long time = *rb->current_tick;
1275 decoder->error = LodePNG_decompress(scanlines, &scanlines_size, idat, idat_size, decoder->error_msg); /*decompress with the Zlib decompressor*/
1276 if (pf_progress) pf_progress(100, 100);
1277 time = *rb->current_tick-time;
1279 if (!decoder->error)
1281 decoded_image_size = (decoder->infoPng.height * decoder->infoPng.width * LodePNG_InfoColor_getBpp(&decoder->infoPng.color) + 7) / 8;
1282 if (decoded_image_size > memory_size) { decoder->error = OUT_OF_MEMORY; return; }
1283 decoded_image = memory_max - decoded_image_size + 1;
1284 if (scanlines + scanlines_size >= decoded_image) { decoder->error = OUT_OF_MEMORY; return; }
1285 memset(decoded_image, 0, decoded_image_size * sizeof(unsigned char));
1286 if (!running_slideshow)
1288 rb->snprintf(print, sizeof(print), "unfiltering scanlines");
1289 rb->lcd_puts(0, 3, print);
1290 rb->lcd_update();
1292 decoder->error = postProcessScanlines(decoded_image, scanlines, decoder);
1297 void LodePNG_decode(LodePNG_Decoder* decoder, unsigned char* in, size_t insize, void (*pf_progress)(int current, int total))
1299 decodeGeneric(decoder, in, insize, pf_progress);
1300 if (decoder->error) return;
1302 /*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"*/
1303 if (!(decoder->infoRaw.color.colorType == 2 || decoder->infoRaw.color.colorType == 6) && !(decoder->infoRaw.color.bitDepth == 8)) { decoder->error = 56; return; }
1304 converted_image = (fb_data *)((int)(memory + 3) & ~3);
1305 converted_image_size = FB_DATA_SZ*decoder->infoPng.width*decoder->infoPng.height;
1306 if ((unsigned char *)(converted_image + converted_image_size) >= decoded_image) { decoder->error = OUT_OF_MEMORY; }
1307 if (!decoder->error) decoder->error = LodePNG_convert(converted_image, decoded_image, &decoder->infoRaw.color, &decoder->infoPng.color, decoder->infoPng.width, decoder->infoPng.height);
1310 void LodePNG_DecodeSettings_init(LodePNG_DecodeSettings* settings)
1312 settings->color_convert = 1;
1315 void LodePNG_Decoder_init(LodePNG_Decoder* decoder)
1317 LodePNG_DecodeSettings_init(&decoder->settings);
1318 LodePNG_InfoRaw_init(&decoder->infoRaw);
1319 LodePNG_InfoPng_init(&decoder->infoPng);
1320 decoder->error = 1;
1323 void LodePNG_Decoder_cleanup(LodePNG_Decoder* decoder)
1325 LodePNG_InfoRaw_cleanup(&decoder->infoRaw);
1326 LodePNG_InfoPng_cleanup(&decoder->infoPng);
1329 /* support function for qsort() */
1330 static int compare(const void* p1, const void* p2)
1332 return rb->strcasecmp(*((char **)p1), *((char **)p2));
1335 bool png_ext(const char ext[])
1337 if (!ext)
1338 return false;
1339 if (!rb->strcasecmp(ext,".png"))
1340 return true;
1341 else
1342 return false;
1345 /*Read directory contents for scrolling. */
1346 void get_pic_list(void)
1348 int i;
1349 long int str_len = 0;
1350 char *pname;
1351 tree = rb->tree_get_context();
1353 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
1354 file_pt = rb->plugin_get_buffer((size_t *)&image_size);
1355 #else
1356 file_pt = rb->plugin_get_audio_buffer((size_t *)&image_size);
1357 #endif
1359 for (i = 0; i < tree->filesindir; i++)
1361 if (png_ext(rb->strrchr(&tree->name_buffer[str_len],'.')))
1362 file_pt[entries++] = &tree->name_buffer[str_len];
1364 str_len += rb->strlen(&tree->name_buffer[str_len]) + 1;
1367 rb->qsort(file_pt, entries, sizeof(char**), compare);
1369 /* Remove path and leave only the name.*/
1370 pname = rb->strrchr(np_file,'/');
1371 pname++;
1373 /* Find Selected File. */
1374 for (i = 0; i < entries; i++)
1375 if (!rb->strcmp(file_pt[i], pname))
1376 curfile = i;
1379 int change_filename(int direct)
1381 int count = 0;
1382 direction = direct;
1384 if (direct == DIR_PREV)
1388 count++;
1389 if (curfile == 0)
1390 curfile = entries - 1;
1391 else
1392 curfile--;
1393 }while (file_pt[curfile] == '\0' && count < entries);
1394 /* we "erase" the file name if we encounter
1395 * a non-supported file, so skip it now */
1397 else /* DIR_NEXT/DIR_NONE */
1401 count++;
1402 if (curfile == entries - 1)
1403 curfile = 0;
1404 else
1405 curfile++;
1406 }while (file_pt[curfile] == '\0' && count < entries);
1409 if (count == entries && file_pt[curfile] == '\0')
1411 rb->splash(HZ, "No supported files");
1412 return PLUGIN_ERROR;
1414 if (rb->strlen(tree->currdir) > 1)
1416 rb->strcpy(np_file, tree->currdir);
1417 rb->strcat(np_file, "/");
1419 else
1420 rb->strcpy(np_file, tree->currdir);
1422 rb->strcat(np_file, file_pt[curfile]);
1424 return PLUGIN_OTHER;
1427 /* switch off overlay, for handling SYS_ events */
1428 void cleanup(void *parameter)
1430 (void)parameter;
1433 #define VSCROLL (LCD_HEIGHT/8)
1434 #define HSCROLL (LCD_WIDTH/10)
1436 #define ZOOM_IN 100 /* return codes for below function */
1437 #define ZOOM_OUT 101
1439 int show_menu(void) /* return 1 to quit */
1441 #if LCD_DEPTH > 1
1442 rb->lcd_set_backdrop(old_backdrop);
1443 #ifdef HAVE_LCD_COLOR
1444 rb->lcd_set_foreground(rb->global_settings->fg_color);
1445 rb->lcd_set_background(rb->global_settings->bg_color);
1446 #else
1447 rb->lcd_set_foreground(LCD_BLACK);
1448 rb->lcd_set_background(LCD_WHITE);
1449 #endif
1450 #endif
1451 int result;
1453 enum menu_id
1455 MIID_RETURN = 0,
1456 MIID_TOGGLE_SS_MODE,
1457 MIID_CHANGE_SS_MODE,
1458 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
1459 MIID_SHOW_PLAYBACK_MENU,
1460 #endif
1461 MIID_QUIT,
1464 MENUITEM_STRINGLIST(menu, "Png Menu", NULL,
1465 "Return", "Toggle Slideshow Mode",
1466 "Change Slideshow Time",
1467 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
1468 "Show Playback Menu",
1469 #endif
1470 "Quit");
1472 static const struct opt_items slideshow[2] = {
1473 { "Disable", -1 },
1474 { "Enable", -1 },
1477 result=rb->do_menu(&menu, NULL, NULL, false);
1479 switch (result)
1481 case MIID_RETURN:
1482 break;
1483 case MIID_TOGGLE_SS_MODE:
1484 rb->set_option("Toggle Slideshow", &slideshow_enabled, INT,
1485 slideshow , 2, NULL);
1486 break;
1487 case MIID_CHANGE_SS_MODE:
1488 rb->set_int("Slideshow Time", "s", UNIT_SEC,
1489 &png_settings.ss_timeout, NULL, 1,
1490 SS_MIN_TIMEOUT, SS_MAX_TIMEOUT, NULL);
1491 break;
1492 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
1493 case MIID_SHOW_PLAYBACK_MENU:
1494 if (plug_buf)
1496 playback_control(NULL);
1498 else
1500 rb->splash(HZ, "Cannot restart playback");
1502 break;
1503 #endif
1504 case MIID_QUIT:
1505 return 1;
1506 break;
1509 #if !defined(SIMULATOR) && defined(HAVE_DISK_STORAGE)
1510 /* change ata spindown time based on slideshow time setting */
1511 immediate_ata_off = false;
1512 rb->storage_spindown(rb->global_settings->disk_spindown);
1514 if (slideshow_enabled)
1516 if (png_settings.ss_timeout < 10)
1518 /* slideshow times < 10s keep disk spinning */
1519 rb->storage_spindown(0);
1521 else if (!rb->mp3_is_playing())
1523 /* slideshow times > 10s and not playing: ata_off after load */
1524 immediate_ata_off = true;
1527 #endif
1528 #if LCD_DEPTH > 1
1529 rb->lcd_set_backdrop(NULL);
1530 rb->lcd_set_foreground(LCD_WHITE);
1531 rb->lcd_set_background(LCD_BLACK);
1532 #endif
1533 rb->lcd_clear_display();
1534 return 0;
1537 /* Pan the viewing window right - move image to the left and fill in
1538 the right-hand side */
1539 static void pan_view_right(struct LodePNG_Decoder* decoder)
1541 int move;
1543 move = MIN(HSCROLL, decoder->infoPng.width/ds - decoder->x - LCD_WIDTH);
1544 if (move > 0 && decoder->infoPng.width/ds > LCD_WIDTH)
1546 decoder->x += move;
1547 rb->lcd_bitmap_part(resized_image, decoder->x, decoder->y, decoder->infoPng.width/ds /*stride*/,
1548 MAX(0, (LCD_WIDTH - (int)decoder->infoPng.width/(int)ds) / 2),
1549 MAX(0, (LCD_HEIGHT - (int)decoder->infoPng.height/(int)ds) / 2),
1550 MIN(LCD_WIDTH, decoder->infoPng.width/ds),
1551 MIN(LCD_HEIGHT, decoder->infoPng.height/ds));
1552 rb->lcd_update();
1556 /* Pan the viewing window left - move image to the right and fill in
1557 the left-hand side */
1558 static void pan_view_left(struct LodePNG_Decoder* decoder)
1560 int move;
1562 move = MIN(HSCROLL, decoder->x);
1563 if (move > 0)
1565 decoder->x -= move;
1566 rb->lcd_bitmap_part(resized_image, decoder->x, decoder->y, decoder->infoPng.width/ds /*stride*/,
1567 MAX(0, (LCD_WIDTH - (int)decoder->infoPng.width/(int)ds) / 2),
1568 MAX(0, (LCD_HEIGHT - (int)decoder->infoPng.height/(int)ds) / 2),
1569 MIN(LCD_WIDTH, decoder->infoPng.width/ds),
1570 MIN(LCD_HEIGHT, decoder->infoPng.height/ds));
1571 rb->lcd_update();
1576 /* Pan the viewing window up - move image down and fill in
1577 the top */
1578 static void pan_view_up(struct LodePNG_Decoder* decoder)
1580 int move;
1582 move = MIN(VSCROLL, decoder->y);
1583 if (move > 0)
1585 decoder->y -= move;
1586 rb->lcd_bitmap_part(resized_image, decoder->x, decoder->y, decoder->infoPng.width/ds /*stride*/,
1587 MAX(0, (LCD_WIDTH - (int)decoder->infoPng.width/(int)ds) / 2),
1588 MAX(0, (LCD_HEIGHT - (int)decoder->infoPng.height/(int)ds) / 2),
1589 MIN(LCD_WIDTH, decoder->infoPng.width/ds),
1590 MIN(LCD_HEIGHT, decoder->infoPng.height/ds));
1591 rb->lcd_update();
1595 /* Pan the viewing window down - move image up and fill in
1596 the bottom */
1597 static void pan_view_down(struct LodePNG_Decoder* decoder)
1599 int move;
1601 move = MIN(VSCROLL, decoder->infoPng.height/ds - decoder->y - LCD_HEIGHT);
1602 if (move > 0 && decoder->infoPng.height/ds > LCD_HEIGHT)
1604 decoder->y += move;
1605 rb->lcd_bitmap_part(resized_image, decoder->x, decoder->y, decoder->infoPng.width/ds /*stride*/,
1606 MAX(0, (LCD_WIDTH - (int)decoder->infoPng.width/(int)ds) / 2),
1607 MAX(0, (LCD_HEIGHT - (int)decoder->infoPng.height/(int)ds) / 2),
1608 MIN(LCD_WIDTH, decoder->infoPng.width/ds),
1609 MIN(LCD_HEIGHT, decoder->infoPng.height/ds));
1610 rb->lcd_update();
1614 /* interactively scroll around the image */
1615 int scroll_bmp(struct LodePNG_Decoder* decoder)
1617 int button;
1618 int lastbutton = 0;
1620 while (true)
1622 if (slideshow_enabled)
1623 button = rb->button_get_w_tmo(png_settings.ss_timeout * HZ);
1624 else button = rb->button_get(true);
1626 running_slideshow = false;
1628 switch (button)
1630 case PNG_LEFT:
1631 if (!(ds < ds_max) && entries > 0 && decoder->infoPng.width <= MAX_X_SIZE)
1632 return change_filename(DIR_PREV);
1633 case PNG_LEFT | BUTTON_REPEAT:
1634 pan_view_left(decoder);
1635 break;
1637 case PNG_RIGHT:
1638 if (!(ds < ds_max) && entries > 0 && decoder->infoPng.width <= MAX_X_SIZE)
1639 return change_filename(DIR_NEXT);
1640 case PNG_RIGHT | BUTTON_REPEAT:
1641 pan_view_right(decoder);
1642 break;
1644 case PNG_UP:
1645 case PNG_UP | BUTTON_REPEAT:
1646 pan_view_up(decoder);
1647 break;
1649 case PNG_DOWN:
1650 case PNG_DOWN | BUTTON_REPEAT:
1651 pan_view_down(decoder);
1652 break;
1654 case BUTTON_NONE:
1655 if (!slideshow_enabled)
1656 break;
1657 running_slideshow = true;
1658 if (entries > 0)
1659 return change_filename(DIR_NEXT);
1660 break;
1662 #ifdef PNG_SLIDE_SHOW
1663 case PNG_SLIDE_SHOW:
1664 slideshow_enabled = !slideshow_enabled;
1665 running_slideshow = slideshow_enabled;
1666 break;
1667 #endif
1669 #ifdef PNG_NEXT_REPEAT
1670 case PNG_NEXT_REPEAT:
1671 #endif
1672 case PNG_NEXT:
1673 if (entries > 0)
1674 return change_filename(DIR_NEXT);
1675 break;
1677 #ifdef PNG_PREVIOUS_REPEAT
1678 case PNG_PREVIOUS_REPEAT:
1679 #endif
1680 case PNG_PREVIOUS:
1681 if (entries > 0)
1682 return change_filename(DIR_PREV);
1683 break;
1685 case PNG_ZOOM_IN:
1686 #ifdef PNG_ZOOM_PRE
1687 if (lastbutton != PNG_ZOOM_PRE)
1688 break;
1689 #endif
1690 return ZOOM_IN;
1691 break;
1693 case PNG_ZOOM_OUT:
1694 #ifdef PNG_ZOOM_PRE
1695 if (lastbutton != PNG_ZOOM_PRE)
1696 break;
1697 #endif
1698 return ZOOM_OUT;
1699 break;
1701 #ifdef PNG_RC_MENU
1702 case PNG_RC_MENU:
1703 #endif
1704 case PNG_MENU:
1705 if (show_menu() == 1)
1706 return PLUGIN_OK;
1707 else
1708 return PLUGIN_REFRESH;
1710 break;
1711 default:
1712 if (rb->default_event_handler_ex(button, cleanup, NULL)
1713 == SYS_USB_CONNECTED)
1714 return PLUGIN_USB_CONNECTED;
1715 break;
1717 } /* switch */
1719 if (button != BUTTON_NONE)
1720 lastbutton = button;
1721 } /* while (true) */
1724 /* set the view to the given center point, limit if necessary */
1725 void set_view (struct LodePNG_Decoder* decoder, int cx, int cy)
1727 int x, y;
1729 /* plain center to available width/height */
1730 x = cx - MIN(LCD_WIDTH, decoder->infoPng.width/ds) / 2;
1731 y = cy - MIN(LCD_HEIGHT, decoder->infoPng.height/ds) / 2;
1733 /* limit against upper image size */
1734 x = MIN((int)(decoder->infoPng.width/ds) - LCD_WIDTH, x);
1735 y = MIN((int)(decoder->infoPng.height/ds) - LCD_HEIGHT, y);
1737 /* limit against negative side */
1738 x = MAX(0, x);
1739 y = MAX(0, y);
1741 decoder->x = x; /* set the values */
1742 decoder->y = y;
1746 /* callback updating a progress meter while PNG decoding */
1747 void cb_progress(int current, int total)
1750 if (current & 1) rb->yield(); /* be nice to the other threads */
1751 if (!running_slideshow)
1753 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, LCD_HEIGHT-8, LCD_WIDTH, 8, total, 0,
1754 current, HORIZONTAL);
1755 rb->lcd_update_rect(0, LCD_HEIGHT-8, LCD_WIDTH, 8);
1757 else
1759 /* in slideshow mode, keep gui interference to a minimum */
1760 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, LCD_HEIGHT-4, LCD_WIDTH, 4, total, 0,
1761 current, HORIZONTAL);
1762 rb->lcd_update_rect(0, LCD_HEIGHT-4, LCD_WIDTH, 4);
1766 int pngmem(struct LodePNG_Decoder* decoder, int ds)
1768 return decoder->infoPng.width * decoder->infoPng.height * FB_DATA_SZ / ds;
1771 /* how far can we zoom in without running out of memory */
1772 int min_downscale(struct LodePNG_Decoder* decoder, int bufsize)
1774 int downscale = 8;
1776 if (pngmem(decoder, 8) > bufsize)
1777 return 0; /* error, too large, even 1:8 doesn't fit */
1779 while (downscale > 1 && pngmem(decoder, downscale/2) <= bufsize)
1780 downscale /= 2;
1782 return downscale;
1785 /* how far can we zoom out, to fit image into the LCD */
1786 unsigned max_downscale(struct LodePNG_Decoder* decoder)
1788 unsigned downscale = 1;
1790 while (downscale < 8 && (decoder->infoPng.width > LCD_WIDTH*downscale
1791 || decoder->infoPng.height > LCD_HEIGHT*downscale))
1793 downscale *= 2;
1796 return downscale;
1799 /* calculate the view center based on the bitmap position */
1800 void get_view(struct LodePNG_Decoder* decoder, int* p_cx, int* p_cy)
1802 *p_cx = decoder->x + MIN(LCD_WIDTH, decoder->infoPng.width/ds) / 2;
1803 *p_cy = decoder->y + MIN(LCD_HEIGHT, decoder->infoPng.height/ds) / 2;
1806 /* return decoded or cached image */
1807 fb_data *get_image(struct LodePNG_Decoder* decoder)
1809 fb_data * p_disp = disp[ds]; /* short cut */
1811 if (p_disp != NULL)
1813 DEBUGF("Found an image in cache\n");
1814 return p_disp; /* we still have it */
1817 if (previous_disp == NULL) {
1818 previous_disp = converted_image;
1819 previous_size = converted_image_size;
1822 size[ds] = decoder->infoPng.width * decoder->infoPng.height * FB_DATA_SZ / ds;
1824 /* assign image buffer */
1825 if (ds > 1) {
1826 if (!running_slideshow)
1828 rb->snprintf(print, sizeof(print), "resizing %d*%d",
1829 decoder->infoPng.width/ds, decoder->infoPng.height/ds);
1830 rb->lcd_puts(0, 3, print);
1831 rb->lcd_update();
1833 static struct bitmap bmp_src, bmp_dst;
1835 disp[ds] = (fb_data *)((int)(previous_disp + previous_size + 3) & ~3);
1837 if ((unsigned char *)(disp[ds] + size[ds]) >= memory_max) {
1838 //rb->splash(HZ, "Out of Memory");
1839 // Still display the original image which is already decoded in RAM
1840 disp[ds] = NULL;
1841 ds = 1;
1842 return converted_image;
1843 } else {
1844 bmp_src.width = decoder->infoPng.width;
1845 bmp_src.height = decoder->infoPng.height;
1846 bmp_src.data = (unsigned char *)converted_image;
1848 bmp_dst.width = decoder->infoPng.width/ds;
1849 bmp_dst.height = decoder->infoPng.height/ds;
1850 bmp_dst.data = (unsigned char *)disp[ds];
1851 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1852 rb->cpu_boost(true);
1853 smooth_resize_bitmap(&bmp_src, &bmp_dst);
1854 rb->cpu_boost(false);
1855 #else
1856 smooth_resize_bitmap(&bmp_src, &bmp_dst);
1857 #endif /*HAVE_ADJUSTABLE_CPU_FREQ*/
1859 } else {
1860 disp[ds] = converted_image;
1865 previous_disp = disp[ds];
1866 previous_size = size[ds];
1868 return disp[ds];
1873 /* load, decode, display the image */
1874 int load_and_show(char* filename)
1876 int fd;
1877 int status;
1878 long time=0; /* measured ticks */
1879 int cx=0, cy=0; /* view center */
1880 int w, h; /* used to center output */
1882 LodePNG_Decoder_init(&decoder);
1884 rb->lcd_clear_display();
1886 fd = rb->open(filename, O_RDONLY);
1887 if (fd < 0)
1889 rb->snprintf(print,sizeof(print),"err opening %s:%d",filename,fd);
1890 rb->splash(HZ, print);
1891 return PLUGIN_ERROR;
1893 image_size = rb->filesize(fd);
1894 memset(&disp, 0, sizeof(disp));
1895 previous_disp = NULL;
1896 previous_size = 0;
1898 DEBUGF("reading file '%s'\n", filename);
1900 if (!running_slideshow) {
1901 #if LCD_DEPTH > 1
1902 rb->lcd_set_foreground(LCD_WHITE);
1903 rb->lcd_set_background(LCD_BLACK);
1904 rb->lcd_set_backdrop(NULL);
1905 #endif
1907 rb->lcd_clear_display();
1908 rb->snprintf(print, sizeof(print), "%s:", rb->strrchr(filename,'/')+1);
1909 rb->lcd_puts(0, 0, print);
1910 rb->lcd_update();
1913 if (rb->button_get(false) == PNG_MENU) {
1914 decoder.error = PLUGIN_ABORT;
1915 rb->close(fd);
1917 } else if (image_size > memory_size) {
1918 decoder.error = FILE_TOO_LARGE;
1919 rb->close(fd);
1921 } else {
1922 if (!running_slideshow) {
1923 rb->snprintf(print, sizeof(print), "loading %lu bytes", image_size);
1924 rb->lcd_puts(0, 1, print);
1925 rb->lcd_update();
1928 image = memory_max - image_size + 1;
1929 rb->read(fd, image, image_size);
1930 rb->close(fd);
1932 if (!running_slideshow) {
1933 rb->snprintf(print, sizeof(print), "decoding image");
1934 rb->lcd_puts(0, 2, print);
1935 rb->lcd_update();
1937 #ifndef SIMULATOR
1938 else if (immediate_ata_off) {
1939 /* running slideshow and time is long enough: power down disk */
1940 rb->storage_sleep();
1942 #endif
1944 decoder.settings.color_convert = 1;
1945 decoder.infoRaw.color.colorType = 2;
1946 decoder.infoRaw.color.bitDepth = 8;
1948 if (rb->button_get(false) == PNG_MENU) {
1949 decoder.error = PLUGIN_ABORT;
1950 } else {
1951 LodePNG_inspect(&decoder, image, image_size);
1954 if (!decoder.error) {
1956 if (!running_slideshow) {
1957 rb->snprintf(print, sizeof(print), "image %dx%d", decoder.infoPng.width, decoder.infoPng.height);
1958 rb->lcd_puts(0, 2, print);
1959 rb->lcd_update();
1961 ds_max = max_downscale(&decoder); /* check display constraint */
1963 ds = ds_max; /* initials setting */
1965 if (!running_slideshow)
1967 rb->snprintf(print, sizeof(print), "decoding %d*%d",
1968 decoder.infoPng.width, decoder.infoPng.height);
1969 rb->lcd_puts(0, 3, print);
1970 rb->lcd_update();
1973 /* the actual decoding */
1974 time = *rb->current_tick;
1975 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1976 rb->cpu_boost(true);
1977 LodePNG_decode(&decoder, image, image_size, cb_progress);
1978 rb->cpu_boost(false);
1979 #else
1980 LodePNG_decode(&decoder, image, image_size, cb_progress);
1981 #endif /*HAVE_ADJUSTABLE_CPU_FREQ*/
1983 ds_min = min_downscale(&decoder, memory_max - (unsigned char*)(converted_image + converted_image_size)); /* check memory constraint */
1985 if (ds_min == 0) {
1986 // Could not resize the image
1987 ds_min = ds = ds_max = 1;
1992 if (decoder.error == PLUGIN_ABORT || decoder.error == FILE_TOO_LARGE) {
1993 rb->close(fd);
1994 #ifndef SIMULATOR
1995 if (immediate_ata_off) {
1996 /* running slideshow and time is long enough: power down disk */
1997 rb->storage_sleep();
1999 #endif
2002 time = *rb->current_tick - time;
2004 if (!running_slideshow && !decoder.error)
2006 rb->snprintf(print, sizeof(print), " %ld.%02ld sec ", time/HZ, time%HZ);
2007 rb->lcd_getstringsize(print, &w, &h); /* centered in progress bar */
2008 rb->lcd_putsxy((LCD_WIDTH - w)/2, LCD_HEIGHT - h, print);
2009 rb->lcd_update();
2012 do {
2013 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
2014 if (plug_buf && (decoder.error == FILE_TOO_LARGE || decoder.error == OUT_OF_MEMORY || decoder.error == Z_MEM_ERROR))
2016 rb->lcd_setfont(FONT_SYSFIXED);
2017 rb->lcd_clear_display();
2018 rb->snprintf(print,sizeof(print),"%s:",rb->strrchr(filename,'/')+1);
2019 rb->lcd_puts(0,0,print);
2020 rb->lcd_puts(0,1,"Not enough plugin memory!");
2021 rb->lcd_puts(0,2,"Zoom In: Stop playback.");
2022 if (entries>1)
2023 rb->lcd_puts(0,3,"Left/Right: Skip File.");
2024 rb->lcd_puts(0,4,"Off: Quit.");
2025 rb->lcd_update();
2026 rb->lcd_setfont(FONT_UI);
2028 rb->button_clear_queue();
2030 while (1)
2032 int button = rb->button_get(true);
2033 switch (button)
2035 case PNG_ZOOM_IN:
2036 plug_buf = false;
2037 memory = rb->plugin_get_audio_buffer(
2038 (size_t *)&memory_size);
2039 memory += (entries * sizeof(char**));
2040 memory_size -= (entries * sizeof(char**));
2041 memory_max = memory + memory_size - 1;
2042 /*try again this file, now using the audio buffer */
2043 return PLUGIN_OTHER;
2044 #ifdef PNG_RC_MENU
2045 case PNG_RC_MENU:
2046 #endif
2047 case PNG_MENU:
2048 return PLUGIN_OK;
2050 case PNG_LEFT:
2051 if (entries>1)
2053 rb->lcd_clear_display();
2054 return change_filename(DIR_PREV);
2056 break;
2058 case PNG_RIGHT:
2059 if (entries>1)
2061 rb->lcd_clear_display();
2062 return change_filename(DIR_NEXT);
2064 break;
2065 default:
2066 if (rb->default_event_handler_ex(button, cleanup, NULL)
2067 == SYS_USB_CONNECTED)
2068 return PLUGIN_USB_CONNECTED;
2073 //else
2074 #endif
2076 if (!decoder.error) {
2077 resized_image = get_image(&decoder); /* decode or fetch from cache */
2079 if (decoder.error) {
2081 switch (decoder.error) {
2082 case PLUGIN_ABORT:
2083 rb->splash(HZ, "aborted");break;
2084 case 27:
2085 rb->splash(HZ, "png file smaller than a png header");break;
2086 case 28:
2087 rb->splash(HZ, "incorrect png signature");break;
2088 case 29:
2089 rb->splash(HZ, "first chunk is not IHDR");break;
2090 case 30:
2091 rb->splash(HZ, "chunk length too large");break;
2092 case 31:
2093 rb->splash(HZ, "illegal PNG color type or bpp");break;
2094 case 32:
2095 rb->splash(HZ, "illegal PNG compression method");break;
2096 case 33:
2097 rb->splash(HZ, "illegal PNG filter method");break;
2098 case 34:
2099 rb->splash(HZ, "illegal PNG interlace method");break;
2100 case 35:
2101 rb->splash(HZ, "chunk length of a chunk is too large or the chunk too small");break;
2102 case 36:
2103 rb->splash(HZ, "illegal PNG filter type encountered");break;
2104 case 37:
2105 rb->splash(HZ, "illegal bit depth for this color type given");break;
2106 case 38:
2107 rb->splash(HZ, "the palette is too big (more than 256 colors)");break;
2108 case 39:
2109 rb->splash(HZ, "more palette alpha values given in tRNS, than there are colors in the palette");break;
2110 case 40:
2111 rb->splash(HZ, "tRNS chunk has wrong size for greyscale image");break;
2112 case 41:
2113 rb->splash(HZ, "tRNS chunk has wrong size for RGB image");break;
2114 case 42:
2115 rb->splash(HZ, "tRNS chunk appeared while it was not allowed for this color type");break;
2116 case 43:
2117 rb->splash(HZ, "bKGD chunk has wrong size for palette image");break;
2118 case 44:
2119 rb->splash(HZ, "bKGD chunk has wrong size for greyscale image");break;
2120 case 45:
2121 rb->splash(HZ, "bKGD chunk has wrong size for RGB image");break;
2122 case 46:
2123 case 47:
2124 rb->splash(HZ, "value encountered in indexed image is larger than the palette size");break;
2125 case 48:
2126 rb->splash(HZ, "input file is empty");break;
2127 case OUT_OF_MEMORY:
2128 case Z_MEM_ERROR:
2129 rb->splash(HZ, "Out of Memory");break;
2130 case 57:
2131 rb->splash(HZ, "invalid CRC");break;
2132 case 59:
2133 rb->splash(HZ, "conversion to unexisting or unsupported color type or bit depth");break;
2134 case 63:
2135 rb->splash(HZ, "png chunk too long");break;
2136 case 69:
2137 rb->splash(HZ, "unknown critical chunk");break;
2138 case 73:
2139 rb->splash(HZ, "invalid tIME chunk size");break;
2140 case 74:
2141 rb->splash(HZ, "invalid pHYs chunk size");break;
2142 case FILE_TOO_LARGE:
2143 rb->splash(HZ, "File too large");break;
2144 case Z_DATA_ERROR:
2145 rb->splash(HZ, decoder.error_msg);break;
2146 default:
2147 rb->splashf(HZ, "other error : %ld", decoder.error);break;
2150 if (decoder.error == PLUGIN_ABORT) {
2151 return PLUGIN_OK;
2152 } else if (decoder.error == OUT_OF_MEMORY && entries == 1) {
2153 return PLUGIN_ERROR;
2154 } else {
2155 return change_filename(direction);
2160 cx = decoder.infoPng.width/ds/2; /* center the view */
2161 cy = decoder.infoPng.height/ds/2;
2163 set_view(&decoder, cx, cy);
2165 if (!running_slideshow)
2167 rb->snprintf(print, sizeof(print), "showing %dx%d",
2168 decoder.infoPng.width/ds, decoder.infoPng.height/ds);
2169 rb->lcd_puts(0, 3, print);
2170 rb->lcd_update();
2173 rb->lcd_clear_display();
2175 rb->lcd_bitmap_part(resized_image, decoder.x, decoder.y, decoder.infoPng.width/ds /*stride*/,
2176 MAX(0, (LCD_WIDTH - (int)decoder.infoPng.width/(int)ds) / 2),
2177 MAX(0, (LCD_HEIGHT - (int)decoder.infoPng.height/(int)ds) / 2),
2178 MIN(LCD_WIDTH, decoder.infoPng.width/ds),
2179 MIN(LCD_HEIGHT, decoder.infoPng.height/ds));
2181 rb->lcd_update();
2186 /* drawing is now finished, play around with scrolling
2187 * until you press OFF or connect USB
2189 while (1)
2191 status = scroll_bmp(&decoder);
2192 if (status == ZOOM_IN)
2194 if (ds > ds_min)
2196 while (1)
2198 ds /= 2; /* reduce downscaling to zoom in */
2199 get_view(&decoder, &cx, &cy);
2200 cx *= 2; /* prepare the position in the new image */
2201 cy *= 2;
2202 if (disp[ds] != converted_image || ds <= ds_min) break;
2205 else
2206 continue;
2209 if (status == ZOOM_OUT)
2211 if (ds < ds_max)
2213 while (1)
2215 ds *= 2; /* increase downscaling to zoom out */
2216 get_view(&decoder, &cx, &cy);
2217 cx /= 2; /* prepare the position in the new image */
2218 cy /= 2;
2219 if (disp[ds] != converted_image || ds >= ds_max) break;
2222 else
2223 continue;
2225 break;
2227 rb->lcd_clear_display();
2229 while (status != PLUGIN_OK && status != PLUGIN_USB_CONNECTED
2230 && status != PLUGIN_OTHER);
2232 return status;
2235 /******************** Plugin entry point *********************/
2237 enum plugin_status plugin_start(const void* parameter)
2239 int condition;
2240 #if LCD_DEPTH > 1
2241 old_backdrop = rb->lcd_get_backdrop();
2242 #endif
2244 if (!parameter) return PLUGIN_ERROR;
2246 rb->strcpy(np_file, parameter);
2247 get_pic_list();
2249 if (!entries) return PLUGIN_ERROR;
2251 #if (PLUGIN_BUFFER_SIZE >= MIN_MEM) && !defined(SIMULATOR)
2252 if (rb->audio_status()) {
2253 memory = (unsigned char *)rb->plugin_get_buffer((size_t *)&memory_size);
2254 plug_buf = true;
2255 } else {
2256 memory = (unsigned char *)rb->plugin_get_audio_buffer((size_t *)&memory_size);
2258 #else
2259 memory = (unsigned char *)rb->plugin_get_audio_buffer((size_t *)&memory_size);
2260 #endif
2262 memory += (entries * sizeof(char**));
2263 memory_size -= (entries * sizeof(char**));
2264 memory_max = memory + memory_size - 1;
2266 /* should be ok to just load settings since the plugin itself has
2267 just been loaded from disk and the drive should be spinning */
2268 configfile_load(PNG_CONFIGFILE, png_config,
2269 ARRAYLEN(png_config), PNG_SETTINGS_MINVERSION);
2270 old_settings = png_settings;
2272 /* Turn off backlight timeout */
2273 backlight_force_on(); /* backlight control in lib/helper.c */
2277 condition = load_and_show(np_file);
2278 }while (condition != PLUGIN_OK && condition != PLUGIN_USB_CONNECTED
2279 && condition != PLUGIN_ERROR);
2281 if (rb->memcmp(&png_settings, &old_settings, sizeof (png_settings)))
2283 /* Just in case drive has to spin, keep it from looking locked */
2284 rb->splash(0, "Saving Settings");
2285 configfile_save(PNG_CONFIGFILE, png_config,
2286 ARRAYLEN(png_config), PNG_SETTINGS_VERSION);
2289 #if !defined(SIMULATOR) && defined(HAVE_DISK_STORAGE)
2290 /* set back ata spindown time in case we changed it */
2291 rb->storage_spindown(rb->global_settings->disk_spindown);
2292 #endif
2294 /* Turn on backlight timeout (revert to settings) */
2295 backlight_use_settings(); /* backlight control in lib/helper.c */
2297 return condition;