jpeg/png: unify code to display image to draw_image(_rect).
[kugel-rb.git] / apps / plugins / png / png.c
blob8a5d05be9a98a3d6a234d1ff40870ae9eb6015e2
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 *)((intptr_t)(memory + 3) & ~3);
1305 converted_image_size = 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 void draw_image(struct LodePNG_Decoder* decoder)
1539 rb->lcd_bitmap_part(resized_image, decoder->x, decoder->y, decoder->infoPng.width/ds /*stride*/,
1540 MAX(0, (LCD_WIDTH - (int)decoder->infoPng.width/(int)ds) / 2),
1541 MAX(0, (LCD_HEIGHT - (int)decoder->infoPng.height/(int)ds) / 2),
1542 decoder->infoPng.width/ds - decoder->x,
1543 decoder->infoPng.height/ds - decoder->y);
1546 /* Pan the viewing window right - move image to the left and fill in
1547 the right-hand side */
1548 static void pan_view_right(struct LodePNG_Decoder* decoder)
1550 int move;
1552 move = MIN(HSCROLL, decoder->infoPng.width/ds - decoder->x - LCD_WIDTH);
1553 if (move > 0)
1555 decoder->x += move;
1556 draw_image(decoder);
1557 rb->lcd_update();
1561 /* Pan the viewing window left - move image to the right and fill in
1562 the left-hand side */
1563 static void pan_view_left(struct LodePNG_Decoder* decoder)
1565 int move;
1567 move = MIN(HSCROLL, decoder->x);
1568 if (move > 0)
1570 decoder->x -= move;
1571 draw_image(decoder);
1572 rb->lcd_update();
1577 /* Pan the viewing window up - move image down and fill in
1578 the top */
1579 static void pan_view_up(struct LodePNG_Decoder* decoder)
1581 int move;
1583 move = MIN(VSCROLL, decoder->y);
1584 if (move > 0)
1586 decoder->y -= move;
1587 draw_image(decoder);
1588 rb->lcd_update();
1592 /* Pan the viewing window down - move image up and fill in
1593 the bottom */
1594 static void pan_view_down(struct LodePNG_Decoder* decoder)
1596 int move;
1598 move = MIN(VSCROLL, decoder->infoPng.height/ds - decoder->y - LCD_HEIGHT);
1599 if (move > 0)
1601 decoder->y += move;
1602 draw_image(decoder);
1603 rb->lcd_update();
1607 /* interactively scroll around the image */
1608 int scroll_bmp(struct LodePNG_Decoder* decoder)
1610 int button;
1611 int lastbutton = 0;
1613 while (true)
1615 if (slideshow_enabled)
1616 button = rb->button_get_w_tmo(png_settings.ss_timeout * HZ);
1617 else button = rb->button_get(true);
1619 running_slideshow = false;
1621 switch (button)
1623 case PNG_LEFT:
1624 if (!(ds < ds_max) && entries > 0 && decoder->infoPng.width <= MAX_X_SIZE)
1625 return change_filename(DIR_PREV);
1626 case PNG_LEFT | BUTTON_REPEAT:
1627 pan_view_left(decoder);
1628 break;
1630 case PNG_RIGHT:
1631 if (!(ds < ds_max) && entries > 0 && decoder->infoPng.width <= MAX_X_SIZE)
1632 return change_filename(DIR_NEXT);
1633 case PNG_RIGHT | BUTTON_REPEAT:
1634 pan_view_right(decoder);
1635 break;
1637 case PNG_UP:
1638 case PNG_UP | BUTTON_REPEAT:
1639 pan_view_up(decoder);
1640 break;
1642 case PNG_DOWN:
1643 case PNG_DOWN | BUTTON_REPEAT:
1644 pan_view_down(decoder);
1645 break;
1647 case BUTTON_NONE:
1648 if (!slideshow_enabled)
1649 break;
1650 running_slideshow = true;
1651 if (entries > 0)
1652 return change_filename(DIR_NEXT);
1653 break;
1655 #ifdef PNG_SLIDE_SHOW
1656 case PNG_SLIDE_SHOW:
1657 slideshow_enabled = !slideshow_enabled;
1658 running_slideshow = slideshow_enabled;
1659 break;
1660 #endif
1662 #ifdef PNG_NEXT_REPEAT
1663 case PNG_NEXT_REPEAT:
1664 #endif
1665 case PNG_NEXT:
1666 if (entries > 0)
1667 return change_filename(DIR_NEXT);
1668 break;
1670 #ifdef PNG_PREVIOUS_REPEAT
1671 case PNG_PREVIOUS_REPEAT:
1672 #endif
1673 case PNG_PREVIOUS:
1674 if (entries > 0)
1675 return change_filename(DIR_PREV);
1676 break;
1678 case PNG_ZOOM_IN:
1679 #ifdef PNG_ZOOM_PRE
1680 if (lastbutton != PNG_ZOOM_PRE)
1681 break;
1682 #endif
1683 return ZOOM_IN;
1684 break;
1686 case PNG_ZOOM_OUT:
1687 #ifdef PNG_ZOOM_PRE
1688 if (lastbutton != PNG_ZOOM_PRE)
1689 break;
1690 #endif
1691 return ZOOM_OUT;
1692 break;
1694 #ifdef PNG_RC_MENU
1695 case PNG_RC_MENU:
1696 #endif
1697 case PNG_MENU:
1698 if (show_menu() == 1)
1699 return PLUGIN_OK;
1700 else
1701 return PLUGIN_REFRESH;
1703 break;
1704 default:
1705 if (rb->default_event_handler_ex(button, cleanup, NULL)
1706 == SYS_USB_CONNECTED)
1707 return PLUGIN_USB_CONNECTED;
1708 break;
1710 } /* switch */
1712 if (button != BUTTON_NONE)
1713 lastbutton = button;
1714 } /* while (true) */
1717 /* set the view to the given center point, limit if necessary */
1718 void set_view (struct LodePNG_Decoder* decoder, int cx, int cy)
1720 int x, y;
1722 /* plain center to available width/height */
1723 x = cx - MIN(LCD_WIDTH, decoder->infoPng.width/ds) / 2;
1724 y = cy - MIN(LCD_HEIGHT, decoder->infoPng.height/ds) / 2;
1726 /* limit against upper image size */
1727 x = MIN((int)(decoder->infoPng.width/ds) - LCD_WIDTH, x);
1728 y = MIN((int)(decoder->infoPng.height/ds) - LCD_HEIGHT, y);
1730 /* limit against negative side */
1731 x = MAX(0, x);
1732 y = MAX(0, y);
1734 decoder->x = x; /* set the values */
1735 decoder->y = y;
1739 /* callback updating a progress meter while PNG decoding */
1740 void cb_progress(int current, int total)
1743 if (current & 1) rb->yield(); /* be nice to the other threads */
1744 if (!running_slideshow)
1746 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, LCD_HEIGHT-8, LCD_WIDTH, 8, total, 0,
1747 current, HORIZONTAL);
1748 rb->lcd_update_rect(0, LCD_HEIGHT-8, LCD_WIDTH, 8);
1750 else
1752 /* in slideshow mode, keep gui interference to a minimum */
1753 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, LCD_HEIGHT-4, LCD_WIDTH, 4, total, 0,
1754 current, HORIZONTAL);
1755 rb->lcd_update_rect(0, LCD_HEIGHT-4, LCD_WIDTH, 4);
1759 int pngmem(struct LodePNG_Decoder* decoder, int ds)
1761 return (decoder->infoPng.width/ds) * (decoder->infoPng.height/ds) * FB_DATA_SZ;
1764 /* how far can we zoom in without running out of memory */
1765 int min_downscale(struct LodePNG_Decoder* decoder, int bufsize)
1767 int downscale = 8;
1769 if (pngmem(decoder, 8) > bufsize)
1770 return 0; /* error, too large, even 1:8 doesn't fit */
1772 while (downscale > 1 && pngmem(decoder, downscale/2) <= bufsize)
1773 downscale /= 2;
1775 return downscale;
1778 /* how far can we zoom out, to fit image into the LCD */
1779 unsigned max_downscale(struct LodePNG_Decoder* decoder)
1781 unsigned downscale = 1;
1783 while (downscale < 8 && (decoder->infoPng.width > LCD_WIDTH*downscale
1784 || decoder->infoPng.height > LCD_HEIGHT*downscale))
1786 downscale *= 2;
1789 return downscale;
1792 /* calculate the view center based on the bitmap position */
1793 void get_view(struct LodePNG_Decoder* decoder, int* p_cx, int* p_cy)
1795 *p_cx = decoder->x + MIN(LCD_WIDTH, decoder->infoPng.width/ds) / 2;
1796 *p_cy = decoder->y + MIN(LCD_HEIGHT, decoder->infoPng.height/ds) / 2;
1799 /* return decoded or cached image */
1800 fb_data *get_image(struct LodePNG_Decoder* decoder)
1802 fb_data * p_disp = disp[ds]; /* short cut */
1804 if (p_disp != NULL)
1806 DEBUGF("Found an image in cache\n");
1807 return p_disp; /* we still have it */
1810 if (previous_disp == NULL) {
1811 previous_disp = converted_image;
1812 previous_size = converted_image_size;
1815 size[ds] = (decoder->infoPng.width/ds) * (decoder->infoPng.height/ds);
1817 /* assign image buffer */
1818 if (ds > 1) {
1819 if (!running_slideshow)
1821 rb->snprintf(print, sizeof(print), "resizing %d*%d",
1822 decoder->infoPng.width/ds, decoder->infoPng.height/ds);
1823 rb->lcd_puts(0, 3, print);
1824 rb->lcd_update();
1826 static struct bitmap bmp_src, bmp_dst;
1828 disp[ds] = (fb_data *)((intptr_t)(previous_disp + previous_size + 3) & ~3);
1830 if ((unsigned char *)(disp[ds] + size[ds]) >= memory_max) {
1831 //rb->splash(HZ, "Out of Memory");
1832 // Still display the original image which is already decoded in RAM
1833 disp[ds] = converted_image;
1834 ds = 1;
1835 return converted_image;
1836 } else {
1837 bmp_src.width = decoder->infoPng.width;
1838 bmp_src.height = decoder->infoPng.height;
1839 bmp_src.data = (unsigned char *)converted_image;
1841 bmp_dst.width = decoder->infoPng.width/ds;
1842 bmp_dst.height = decoder->infoPng.height/ds;
1843 bmp_dst.data = (unsigned char *)disp[ds];
1844 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1845 rb->cpu_boost(true);
1846 smooth_resize_bitmap(&bmp_src, &bmp_dst);
1847 rb->cpu_boost(false);
1848 #else
1849 smooth_resize_bitmap(&bmp_src, &bmp_dst);
1850 #endif /*HAVE_ADJUSTABLE_CPU_FREQ*/
1852 } else {
1853 disp[ds] = converted_image;
1854 return converted_image;
1857 previous_disp = disp[ds];
1858 previous_size = size[ds];
1860 return disp[ds];
1865 /* load, decode, display the image */
1866 int load_and_show(char* filename)
1868 int fd;
1869 int status;
1870 long time=0; /* measured ticks */
1871 int cx=0, cy=0; /* view center */
1872 int w, h; /* used to center output */
1874 LodePNG_Decoder_init(&decoder);
1876 rb->lcd_clear_display();
1878 fd = rb->open(filename, O_RDONLY);
1879 if (fd < 0)
1881 rb->snprintf(print,sizeof(print),"err opening %s:%d",filename,fd);
1882 rb->splash(HZ, print);
1883 return PLUGIN_ERROR;
1885 image_size = rb->filesize(fd);
1886 memset(&disp, 0, sizeof(disp));
1887 previous_disp = NULL;
1888 previous_size = 0;
1890 DEBUGF("reading file '%s'\n", filename);
1892 if (!running_slideshow) {
1893 #if LCD_DEPTH > 1
1894 rb->lcd_set_foreground(LCD_WHITE);
1895 rb->lcd_set_background(LCD_BLACK);
1896 rb->lcd_set_backdrop(NULL);
1897 #endif
1899 rb->lcd_clear_display();
1900 rb->snprintf(print, sizeof(print), "%s:", rb->strrchr(filename,'/')+1);
1901 rb->lcd_puts(0, 0, print);
1902 rb->lcd_update();
1905 if (rb->button_get(false) == PNG_MENU) {
1906 decoder.error = PLUGIN_ABORT;
1907 rb->close(fd);
1909 } else if (image_size > memory_size) {
1910 decoder.error = FILE_TOO_LARGE;
1911 rb->close(fd);
1913 } else {
1914 if (!running_slideshow) {
1915 rb->snprintf(print, sizeof(print), "loading %lu bytes", image_size);
1916 rb->lcd_puts(0, 1, print);
1917 rb->lcd_update();
1920 image = memory_max - image_size + 1;
1921 rb->read(fd, image, image_size);
1922 rb->close(fd);
1924 if (!running_slideshow) {
1925 rb->snprintf(print, sizeof(print), "decoding image");
1926 rb->lcd_puts(0, 2, print);
1927 rb->lcd_update();
1929 #ifndef SIMULATOR
1930 else if (immediate_ata_off) {
1931 /* running slideshow and time is long enough: power down disk */
1932 rb->storage_sleep();
1934 #endif
1936 decoder.settings.color_convert = 1;
1937 decoder.infoRaw.color.colorType = 2;
1938 decoder.infoRaw.color.bitDepth = 8;
1940 if (rb->button_get(false) == PNG_MENU) {
1941 decoder.error = PLUGIN_ABORT;
1942 } else {
1943 LodePNG_inspect(&decoder, image, image_size);
1946 if (!decoder.error) {
1948 if (!running_slideshow) {
1949 rb->snprintf(print, sizeof(print), "image %dx%d", decoder.infoPng.width, decoder.infoPng.height);
1950 rb->lcd_puts(0, 2, print);
1951 rb->lcd_update();
1954 ds_max = max_downscale(&decoder); /* check display constraint */
1956 ds = ds_max; /* initials setting */
1958 if (!running_slideshow)
1960 rb->snprintf(print, sizeof(print), "decoding %d*%d",
1961 decoder.infoPng.width, decoder.infoPng.height);
1962 rb->lcd_puts(0, 3, print);
1963 rb->lcd_update();
1966 /* the actual decoding */
1967 time = *rb->current_tick;
1968 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1969 rb->cpu_boost(true);
1970 LodePNG_decode(&decoder, image, image_size, cb_progress);
1971 rb->cpu_boost(false);
1972 #else
1973 LodePNG_decode(&decoder, image, image_size, cb_progress);
1974 #endif /*HAVE_ADJUSTABLE_CPU_FREQ*/
1976 ds_min = min_downscale(&decoder, memory_max - (unsigned char*)(converted_image + converted_image_size)); /* check memory constraint */
1978 if (ds_min == 0) {
1979 // Could not resize the image
1980 ds_min = ds = ds_max = 1;
1985 if (decoder.error == PLUGIN_ABORT || decoder.error == FILE_TOO_LARGE) {
1986 rb->close(fd);
1987 #ifndef SIMULATOR
1988 if (immediate_ata_off) {
1989 /* running slideshow and time is long enough: power down disk */
1990 rb->storage_sleep();
1992 #endif
1995 time = *rb->current_tick - time;
1997 if (!running_slideshow && !decoder.error)
1999 rb->snprintf(print, sizeof(print), " %ld.%02ld sec ", time/HZ, time%HZ);
2000 rb->lcd_getstringsize(print, &w, &h); /* centered in progress bar */
2001 rb->lcd_putsxy((LCD_WIDTH - w)/2, LCD_HEIGHT - h, print);
2002 rb->lcd_update();
2005 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
2006 if (plug_buf && (decoder.error == FILE_TOO_LARGE || decoder.error == OUT_OF_MEMORY || decoder.error == Z_MEM_ERROR))
2008 rb->lcd_setfont(FONT_SYSFIXED);
2009 rb->lcd_clear_display();
2010 rb->snprintf(print,sizeof(print),"%s:",rb->strrchr(filename,'/')+1);
2011 rb->lcd_puts(0,0,print);
2012 rb->lcd_puts(0,1,"Not enough plugin memory!");
2013 rb->lcd_puts(0,2,"Zoom In: Stop playback.");
2014 if (entries>1)
2015 rb->lcd_puts(0,3,"Left/Right: Skip File.");
2016 rb->lcd_puts(0,4,"Off: Quit.");
2017 rb->lcd_update();
2018 rb->lcd_setfont(FONT_UI);
2020 rb->button_clear_queue();
2022 while (1)
2024 int button = rb->button_get(true);
2025 switch (button)
2027 case PNG_ZOOM_IN:
2028 plug_buf = false;
2029 memory = rb->plugin_get_audio_buffer(
2030 (size_t *)&memory_size);
2031 memory += (entries * sizeof(char**));
2032 memory_size -= (entries * sizeof(char**));
2033 memory_max = memory + memory_size - 1;
2034 /*try again this file, now using the audio buffer */
2035 return PLUGIN_OTHER;
2036 #ifdef PNG_RC_MENU
2037 case PNG_RC_MENU:
2038 #endif
2039 case PNG_MENU:
2040 return PLUGIN_OK;
2042 case PNG_LEFT:
2043 if (entries>1)
2045 rb->lcd_clear_display();
2046 return change_filename(DIR_PREV);
2048 break;
2050 case PNG_RIGHT:
2051 if (entries>1)
2053 rb->lcd_clear_display();
2054 return change_filename(DIR_NEXT);
2056 break;
2057 default:
2058 if (rb->default_event_handler_ex(button, cleanup, NULL)
2059 == SYS_USB_CONNECTED)
2060 return PLUGIN_USB_CONNECTED;
2065 //else
2066 #endif
2068 if (decoder.error) {
2070 switch (decoder.error) {
2071 case PLUGIN_ABORT:
2072 rb->splash(HZ, "aborted");break;
2073 case 27:
2074 rb->splash(HZ, "png file smaller than a png header");break;
2075 case 28:
2076 rb->splash(HZ, "incorrect png signature");break;
2077 case 29:
2078 rb->splash(HZ, "first chunk is not IHDR");break;
2079 case 30:
2080 rb->splash(HZ, "chunk length too large");break;
2081 case 31:
2082 rb->splash(HZ, "illegal PNG color type or bpp");break;
2083 case 32:
2084 rb->splash(HZ, "illegal PNG compression method");break;
2085 case 33:
2086 rb->splash(HZ, "illegal PNG filter method");break;
2087 case 34:
2088 rb->splash(HZ, "illegal PNG interlace method");break;
2089 case 35:
2090 rb->splash(HZ, "chunk length of a chunk is too large or the chunk too small");break;
2091 case 36:
2092 rb->splash(HZ, "illegal PNG filter type encountered");break;
2093 case 37:
2094 rb->splash(HZ, "illegal bit depth for this color type given");break;
2095 case 38:
2096 rb->splash(HZ, "the palette is too big (more than 256 colors)");break;
2097 case 39:
2098 rb->splash(HZ, "more palette alpha values given in tRNS, than there are colors in the palette");break;
2099 case 40:
2100 rb->splash(HZ, "tRNS chunk has wrong size for greyscale image");break;
2101 case 41:
2102 rb->splash(HZ, "tRNS chunk has wrong size for RGB image");break;
2103 case 42:
2104 rb->splash(HZ, "tRNS chunk appeared while it was not allowed for this color type");break;
2105 case 43:
2106 rb->splash(HZ, "bKGD chunk has wrong size for palette image");break;
2107 case 44:
2108 rb->splash(HZ, "bKGD chunk has wrong size for greyscale image");break;
2109 case 45:
2110 rb->splash(HZ, "bKGD chunk has wrong size for RGB image");break;
2111 case 46:
2112 case 47:
2113 rb->splash(HZ, "value encountered in indexed image is larger than the palette size");break;
2114 case 48:
2115 rb->splash(HZ, "input file is empty");break;
2116 case OUT_OF_MEMORY:
2117 case Z_MEM_ERROR:
2118 rb->splash(HZ, "Out of Memory");break;
2119 case 57:
2120 rb->splash(HZ, "invalid CRC");break;
2121 case 59:
2122 rb->splash(HZ, "conversion to unexisting or unsupported color type or bit depth");break;
2123 case 63:
2124 rb->splash(HZ, "png chunk too long");break;
2125 case 69:
2126 rb->splash(HZ, "unknown critical chunk");break;
2127 case 73:
2128 rb->splash(HZ, "invalid tIME chunk size");break;
2129 case 74:
2130 rb->splash(HZ, "invalid pHYs chunk size");break;
2131 case FILE_TOO_LARGE:
2132 rb->splash(HZ, "File too large");break;
2133 case Z_DATA_ERROR:
2134 rb->splash(HZ, decoder.error_msg);break;
2135 default:
2136 rb->splashf(HZ, "other error : %ld", decoder.error);break;
2139 if (decoder.error == PLUGIN_ABORT) {
2140 return PLUGIN_OK;
2141 } else if (decoder.error == OUT_OF_MEMORY && entries == 1) {
2142 return PLUGIN_ERROR;
2143 } else {
2144 file_pt[curfile] = '\0';
2145 return change_filename(direction);
2149 do {
2150 resized_image = get_image(&decoder); /* decode or fetch from cache */
2152 cx = decoder.infoPng.width/ds/2; /* center the view */
2153 cy = decoder.infoPng.height/ds/2;
2155 set_view(&decoder, cx, cy);
2157 if (!running_slideshow)
2159 rb->snprintf(print, sizeof(print), "showing %dx%d",
2160 decoder.infoPng.width/ds, decoder.infoPng.height/ds);
2161 rb->lcd_puts(0, 3, print);
2162 rb->lcd_update();
2165 rb->lcd_clear_display();
2166 draw_image(&decoder);
2167 rb->lcd_update();
2169 /* drawing is now finished, play around with scrolling
2170 * until you press OFF or connect USB
2172 while (1)
2174 status = scroll_bmp(&decoder);
2175 if (status == ZOOM_IN)
2177 if (ds > ds_min)
2179 while (1)
2181 ds /= 2; /* reduce downscaling to zoom in */
2182 get_view(&decoder, &cx, &cy);
2183 cx *= 2; /* prepare the position in the new image */
2184 cy *= 2;
2185 if (disp[ds] != converted_image || ds <= ds_min) break;
2188 else
2189 continue;
2192 if (status == ZOOM_OUT)
2194 if (ds < ds_max)
2196 while (1)
2198 ds *= 2; /* increase downscaling to zoom out */
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_max) break;
2205 else
2206 continue;
2208 break;
2210 rb->lcd_clear_display();
2212 while (status != PLUGIN_OK && status != PLUGIN_USB_CONNECTED
2213 && status != PLUGIN_OTHER);
2215 return status;
2218 /******************** Plugin entry point *********************/
2220 enum plugin_status plugin_start(const void* parameter)
2222 int condition;
2223 #if LCD_DEPTH > 1
2224 old_backdrop = rb->lcd_get_backdrop();
2225 #endif
2227 if (!parameter) return PLUGIN_ERROR;
2229 rb->strcpy(np_file, parameter);
2230 get_pic_list();
2232 if (!entries) return PLUGIN_ERROR;
2234 #if (PLUGIN_BUFFER_SIZE >= MIN_MEM) && !defined(SIMULATOR)
2235 if (rb->audio_status()) {
2236 memory = (unsigned char *)rb->plugin_get_buffer((size_t *)&memory_size);
2237 plug_buf = true;
2238 } else {
2239 memory = (unsigned char *)rb->plugin_get_audio_buffer((size_t *)&memory_size);
2241 #else
2242 memory = (unsigned char *)rb->plugin_get_audio_buffer((size_t *)&memory_size);
2243 #endif
2245 memory += (entries * sizeof(char**));
2246 memory_size -= (entries * sizeof(char**));
2247 memory_max = memory + memory_size - 1;
2249 /* should be ok to just load settings since the plugin itself has
2250 just been loaded from disk and the drive should be spinning */
2251 configfile_load(PNG_CONFIGFILE, png_config,
2252 ARRAYLEN(png_config), PNG_SETTINGS_MINVERSION);
2253 old_settings = png_settings;
2255 /* Turn off backlight timeout */
2256 backlight_force_on(); /* backlight control in lib/helper.c */
2260 condition = load_and_show(np_file);
2261 }while (condition != PLUGIN_OK && condition != PLUGIN_USB_CONNECTED
2262 && condition != PLUGIN_ERROR);
2264 if (rb->memcmp(&png_settings, &old_settings, sizeof (png_settings)))
2266 /* Just in case drive has to spin, keep it from looking locked */
2267 rb->splash(0, "Saving Settings");
2268 configfile_save(PNG_CONFIGFILE, png_config,
2269 ARRAYLEN(png_config), PNG_SETTINGS_VERSION);
2272 #if !defined(SIMULATOR) && defined(HAVE_DISK_STORAGE)
2273 /* set back ata spindown time in case we changed it */
2274 rb->storage_spindown(rb->global_settings->disk_spindown);
2275 #endif
2277 /* Turn on backlight timeout (revert to settings) */
2278 backlight_use_settings(); /* backlight control in lib/helper.c */
2280 return condition;