jpeg/png: refactor use of buf.
[kugel-rb.git] / apps / plugins / png / png.c
blob4cbf74c71c0d2f5ca76a8a0934e4859cc6e18eab
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$id $
10 * Copyright (C) 2009 by Christophe Gouiran <bechris13250 -at- gmail -dot- com>
12 * Based on lodepng, a lightweight png decoder/encoder
13 * (c) 2005-2008 Lode Vandevenne
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
21 * KIND, either express or implied.
23 ****************************************************************************/
26 LodePNG version 20080927
28 Copyright (c) 2005-2008 Lode Vandevenne
30 This software is provided 'as-is', without any express or implied
31 warranty. In no event will the authors be held liable for any damages
32 arising from the use of this software.
34 Permission is granted to anyone to use this software for any purpose,
35 including commercial applications, and to alter it and redistribute it
36 freely, subject to the following restrictions:
38 1. The origin of this software must not be misrepresented; you must not
39 claim that you wrote the original software. If you use this software
40 in a product, an acknowledgment in the product documentation would be
41 appreciated but is not required.
43 2. Altered source versions must be plainly marked as such, and must not be
44 misrepresented as being the original software.
46 3. This notice may not be removed or altered from any source
47 distribution.
51 The manual and changelog can be found in the header file "lodepng.h"
52 You are free to name this file lodepng.cpp or lodepng.c depending on your usage.
55 #include "plugin.h"
56 #include "lcd.h"
57 #include <lib/playback_control.h>
58 #include <lib/helper.h>
59 #include <lib/configfile.h>
60 #include <lib/grey.h>
61 #include <lib/pluginlib_bmp.h>
62 #include "zlib.h"
63 #include "png.h"
65 PLUGIN_HEADER
67 /* ////////////////////////////////////////////////////////////////////////// */
68 /* LodeFlate & LodeZlib Setting structs */
69 /* ////////////////////////////////////////////////////////////////////////// */
71 typedef struct LodePNG_InfoColor /*info about the color type of an image*/
73 /*header (IHDR)*/
74 unsigned colorType; /*color type*/
75 unsigned bitDepth; /*bits per sample*/
77 /*palette (PLTE)*/
78 unsigned char palette[256 * 4]; /*palette in RGBARGBA... order*/
79 size_t palettesize; /*palette size in number of colors (amount of bytes is 4 * palettesize)*/
81 /*transparent color key (tRNS)*/
82 unsigned key_defined; /*is a transparent color key given?*/
83 unsigned key_r; /*red component of color key*/
84 unsigned key_g; /*green component of color key*/
85 unsigned key_b; /*blue component of color key*/
86 } LodePNG_InfoColor;
88 typedef struct LodePNG_Time /*LodePNG's encoder does not generate the current time. To make it add a time chunk the correct time has to be provided*/
90 unsigned year; /*2 bytes*/
91 unsigned char month; /*1-12*/
92 unsigned char day; /*1-31*/
93 unsigned char hour; /*0-23*/
94 unsigned char minute; /*0-59*/
95 unsigned char second; /*0-60 (to allow for leap seconds)*/
96 } LodePNG_Time;
98 typedef struct LodePNG_InfoPng /*information about the PNG image, except pixels and sometimes except width and height*/
100 /*header (IHDR), palette (PLTE) and transparency (tRNS)*/
101 unsigned width; /*width of the image in pixels (ignored by encoder, but filled in by decoder)*/
102 unsigned height; /*height of the image in pixels (ignored by encoder, but filled in by decoder)*/
103 unsigned compressionMethod; /*compression method of the original file*/
104 unsigned filterMethod; /*filter method of the original file*/
105 unsigned interlaceMethod; /*interlace method of the original file*/
106 LodePNG_InfoColor color; /*color type and bits, palette, transparency*/
108 /*suggested background color (bKGD)*/
109 unsigned background_defined; /*is a suggested background color given?*/
110 unsigned background_r; /*red component of suggested background color*/
111 unsigned background_g; /*green component of suggested background color*/
112 unsigned background_b; /*blue component of suggested background color*/
114 /*time chunk (tIME)*/
115 unsigned char time_defined; /*if 0, no tIME chunk was or will be generated in the PNG image*/
116 LodePNG_Time time;
118 /*phys chunk (pHYs)*/
119 unsigned phys_defined; /*is pHYs chunk defined?*/
120 unsigned phys_x;
121 unsigned phys_y;
122 unsigned char phys_unit; /*may be 0 (unknown unit) or 1 (metre)*/
124 } LodePNG_InfoPng;
126 typedef struct LodePNG_InfoRaw /*contains user-chosen information about the raw image data, which is independent of the PNG image*/
128 LodePNG_InfoColor color;
129 } LodePNG_InfoRaw;
131 typedef struct LodePNG_DecodeSettings
133 unsigned color_convert; /*whether to convert the PNG to the color type you want. Default: yes*/
134 } LodePNG_DecodeSettings;
136 typedef struct LodePNG_Decoder
138 LodePNG_DecodeSettings settings;
139 LodePNG_InfoRaw infoRaw;
140 LodePNG_InfoPng infoPng; /*info of the PNG image obtained after decoding*/
141 long error;
142 char error_msg[128];
143 int x,y;
144 } LodePNG_Decoder;
146 #define VERSION_STRING "20080927"
148 /* Headings */
149 #define DIR_PREV 1
150 #define DIR_NEXT -1
151 #define DIR_NONE 0
153 /* decompressed image in the possible sizes (1,2,4,8), wasting the other */
154 static fb_data *disp[9];
155 /* up to here currently used by image(s) */
156 static fb_data *disp_buf;
158 /* my memory pool (from the mp3 buffer) */
159 static char print[128]; /* use a common snprintf() buffer */
161 unsigned char *memory, *memory_max;
162 static size_t memory_size;
164 static unsigned char *image; /* where we put the content of the file */
165 static size_t image_size;
167 #if LCD_DEPTH >= 8
168 static fb_data *converted_image __attribute__ ((aligned (16))); /* the (color) converted image */
169 #else
170 static fb_data *converted_image; /* the (color) converted image */
171 #endif
172 static size_t converted_image_size;
174 static unsigned char *decoded_image; /* the decoded image */
175 static size_t decoded_image_size;
177 #if LCD_DEPTH >= 8
178 static fb_data *resized_image __attribute__ ((aligned (16))); /* the decoded image */
179 #else
180 static fb_data *resized_image; /* the decoded image */
181 #endif
183 static struct tree_context *tree;
185 /* the current full file name */
186 static char np_file[MAX_PATH];
187 static int curfile = 0, direction = DIR_NONE, entries = 0;
189 static LodePNG_Decoder decoder;
191 /* list of the jpeg files */
192 static char **file_pt;
193 /* are we using the plugin buffer or the audio buffer? */
194 bool plug_buf = false;
196 /* Persistent configuration */
197 #define PNG_CONFIGFILE "png.cfg"
198 #define PNG_SETTINGS_MINVERSION 1
199 #define PNG_SETTINGS_VERSION 1
201 /* Slideshow times */
202 #define SS_MIN_TIMEOUT 1
203 #define SS_MAX_TIMEOUT 20
204 #define SS_DEFAULT_TIMEOUT 5
206 struct png_settings
208 int ss_timeout;
211 static struct png_settings png_settings =
213 SS_DEFAULT_TIMEOUT
215 static struct png_settings old_settings;
217 static struct configdata png_config[] =
219 { TYPE_INT, SS_MIN_TIMEOUT, SS_MAX_TIMEOUT,
220 { .int_p = &png_settings.ss_timeout }, "Slideshow Time", NULL
224 #if LCD_DEPTH > 1
225 static fb_data* old_backdrop;
226 #endif
228 #define MAX_X_SIZE LCD_WIDTH*8
230 /* Min memory allowing us to use the plugin buffer
231 * and thus not stopping the music
232 * *Very* rough estimation:
233 * Max 10 000 dir entries * 4bytes/entry (char **) = 40000 bytes
234 * + 30k code size = 70 000
235 * + 50k min for png = 130 000
237 #define MIN_MEM 130000
239 static int slideshow_enabled = false; /* run slideshow */
240 static int running_slideshow = false; /* loading image because of slideshw */
241 #ifndef SIMULATOR
242 static int immediate_ata_off = false; /* power down disk after loading */
243 #endif
245 static unsigned ds, ds_min, ds_max; /* downscaling and limits */
248 The two functions below (LodePNG_decompress and LodePNG_compress) directly call the
249 LodeZlib_decompress and LodeZlib_compress functions. The only purpose of the functions
250 below, is to provide the ability to let LodePNG use a different Zlib encoder by only
251 changing the two functions below, instead of changing it inside the vareous places
252 in the other LodePNG functions.
254 *out must be NULL and *outsize must be 0 initially, and after the function is done,
255 *out must point to the decompressed data, *outsize must be the size of it, and must
256 be the size of the useful data in bytes, not the alloc size.
259 static unsigned LodePNG_decompress(unsigned char* out, size_t* outsize, const unsigned char* in, size_t insize, char *error_msg)
261 z_stream stream;
262 int err;
264 error_msg = "";
266 stream.next_in = (Bytef*)in;
267 stream.avail_in = (uInt)insize;
269 stream.next_out = out;
270 stream.avail_out = (uInt)*outsize;
272 stream.zalloc = (alloc_func)0;
273 stream.zfree = (free_func)0;
275 err = inflateInit(&stream);
276 if (err != Z_OK) return err;
278 err = inflate(&stream, Z_FINISH);
279 if (err != Z_STREAM_END) {
280 inflateEnd(&stream);
281 if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0))
282 return Z_DATA_ERROR;
283 return err;
285 *outsize = stream.total_out;
287 err = inflateEnd(&stream);
288 error_msg = stream.msg;
289 return err;
293 /* ////////////////////////////////////////////////////////////////////////// */
294 /* / Reading and writing single bits and bytes from/to stream for LodePNG / */
295 /* ////////////////////////////////////////////////////////////////////////// */
297 static unsigned char readBitFromReversedStream(size_t* bitpointer, const unsigned char* bitstream)
299 unsigned char result = (unsigned char)((bitstream[(*bitpointer) >> 3] >> (7 - ((*bitpointer) & 0x7))) & 1);
300 (*bitpointer)++;
301 return result;
304 static unsigned readBitsFromReversedStream(size_t* bitpointer, const unsigned char* bitstream, size_t nbits)
306 unsigned result = 0;
307 size_t i;
308 for (i = nbits - 1; i < nbits; i--) result += (unsigned)readBitFromReversedStream(bitpointer, bitstream) << i;
309 return result;
312 static void setBitOfReversedStream0(size_t* bitpointer, unsigned char* bitstream, unsigned char bit)
314 /*the current bit in bitstream must be 0 for this to work*/
315 if (bit) bitstream[(*bitpointer) >> 3] |= (bit << (7 - ((*bitpointer) & 0x7))); /*earlier bit of huffman code is in a lesser significant bit of an earlier byte*/
316 (*bitpointer)++;
319 static void setBitOfReversedStream(size_t* bitpointer, unsigned char* bitstream, unsigned char bit)
321 /*the current bit in bitstream may be 0 or 1 for this to work*/
322 if (bit == 0) bitstream[(*bitpointer) >> 3] &= (unsigned char)(~(1 << (7 - ((*bitpointer) & 0x7))));
323 else bitstream[(*bitpointer) >> 3] |= (1 << (7 - ((*bitpointer) & 0x7)));
324 (*bitpointer)++;
327 static unsigned LodePNG_read32bitInt(const unsigned char* buffer)
329 return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
332 /* ////////////////////////////////////////////////////////////////////////// */
333 /* / PNG chunks / */
334 /* ////////////////////////////////////////////////////////////////////////// */
336 unsigned LodePNG_chunk_length(const unsigned char* chunk) /*get the length of the data of the chunk. Total chunk length has 12 bytes more.*/
338 return LodePNG_read32bitInt(&chunk[0]);
341 void LodePNG_chunk_type(char type[5], const unsigned char* chunk) /*puts the 4-byte type in null terminated string*/
343 unsigned i;
344 for (i = 0; i < 4; i++) type[i] = chunk[4 + i];
345 type[4] = 0; /*null termination char*/
348 unsigned char LodePNG_chunk_type_equals(const unsigned char* chunk, const char* type) /*check if the type is the given type*/
350 if (type[4] != 0) return 0;
351 return (chunk[4] == type[0] && chunk[5] == type[1] && chunk[6] == type[2] && chunk[7] == type[3]);
354 /*properties of PNG chunks gotten from capitalization of chunk type name, as defined by the standard*/
355 unsigned char LodePNG_chunk_critical(const unsigned char* chunk) /*0: ancillary chunk, 1: it's one of the critical chunk types*/
357 return((chunk[4] & 32) == 0);
360 unsigned char LodePNG_chunk_private(const unsigned char* chunk) /*0: public, 1: private*/
362 return((chunk[6] & 32) != 0);
365 unsigned char LodePNG_chunk_safetocopy(const unsigned char* chunk) /*0: the chunk is unsafe to copy, 1: the chunk is safe to copy*/
367 return((chunk[7] & 32) != 0);
370 unsigned char* LodePNG_chunk_data(unsigned char* chunk) /*get pointer to the data of the chunk*/
372 return &chunk[8];
375 const unsigned char* LodePNG_chunk_data_const(const unsigned char* chunk) /*get pointer to the data of the chunk*/
377 return &chunk[8];
380 unsigned LodePNG_chunk_check_crc(const unsigned char* chunk) /*returns 0 if the crc is correct, error code if it's incorrect*/
382 unsigned length = LodePNG_chunk_length(chunk);
383 unsigned CRC = LodePNG_read32bitInt(&chunk[length + 8]);
384 unsigned checksum = crc32(0L, &chunk[4], length + 4); /*the CRC is taken of the data and the 4 chunk type letters, not the length*/
385 if (CRC != checksum) return 1;
386 else return 0;
389 unsigned char* LodePNG_chunk_next(unsigned char* chunk) /*don't use on IEND chunk, as there is no next chunk then*/
391 unsigned total_chunk_length = LodePNG_chunk_length(chunk) + 12;
392 return &chunk[total_chunk_length];
395 const unsigned char* LodePNG_chunk_next_const(const unsigned char* chunk) /*don't use on IEND chunk, as there is no next chunk then*/
397 unsigned total_chunk_length = LodePNG_chunk_length(chunk) + 12;
398 return &chunk[total_chunk_length];
401 /* ////////////////////////////////////////////////////////////////////////// */
402 /* / Color types and such / */
403 /* ////////////////////////////////////////////////////////////////////////// */
405 /*return type is a LodePNG error code*/
406 static unsigned checkColorValidity(unsigned colorType, unsigned bd) /*bd = bitDepth*/
408 switch (colorType)
410 case 0:
411 if (!(bd == 1 || bd == 2 || bd == 4 || bd == 8 || bd == 16)) return 37; break; /*grey*/
412 case 2:
413 if (!( bd == 8 || bd == 16)) return 37; break; /*RGB*/
414 case 3:
415 if (!(bd == 1 || bd == 2 || bd == 4 || bd == 8 )) return 37; break; /*palette*/
416 case 4:
417 if (!( bd == 8 || bd == 16)) return 37; break; /*grey + alpha*/
418 case 6:
419 if (!( bd == 8 || bd == 16)) return 37; break; /*RGBA*/
420 default:
421 return 31;
423 return 0; /*allowed color type / bits combination*/
426 static unsigned getNumColorChannels(unsigned colorType)
428 switch (colorType)
430 case 0:
431 return 1; /*grey*/
432 case 2:
433 return 3; /*RGB*/
434 case 3:
435 return 1; /*palette*/
436 case 4:
437 return 2; /*grey + alpha*/
438 case 6:
439 return 4; /*RGBA*/
441 return 0; /*unexisting color type*/
444 static unsigned getBpp(unsigned colorType, unsigned bitDepth)
446 return getNumColorChannels(colorType) * bitDepth; /*bits per pixel is amount of channels * bits per channel*/
449 /* ////////////////////////////////////////////////////////////////////////// */
451 void LodePNG_InfoColor_init(LodePNG_InfoColor* info)
453 info->key_defined = 0;
454 info->key_r = info->key_g = info->key_b = 0;
455 info->colorType = 6;
456 info->bitDepth = 8;
457 memset(info->palette, 0, 256 * 4 * sizeof(unsigned char));
458 info->palettesize = 0;
461 void LodePNG_InfoColor_cleanup(LodePNG_InfoColor* info)
463 info->palettesize = 0;
466 unsigned LodePNG_InfoColor_getBpp(const LodePNG_InfoColor* info) { return getBpp(info->colorType, info->bitDepth); } /*calculate bits per pixel out of colorType and bitDepth*/
467 unsigned LodePNG_InfoColor_isGreyscaleType(const LodePNG_InfoColor* info) { return info->colorType == 0 || info->colorType == 4; }
469 unsigned LodePNG_InfoColor_equal(const LodePNG_InfoColor* info1, const LodePNG_InfoColor* info2)
471 return info1->colorType == info2->colorType
472 && info1->bitDepth == info2->bitDepth; /*palette and color key not compared*/
475 void LodePNG_InfoPng_init(LodePNG_InfoPng* info)
477 info->width = info->height = 0;
478 LodePNG_InfoColor_init(&info->color);
479 info->interlaceMethod = 0;
480 info->compressionMethod = 0;
481 info->filterMethod = 0;
482 info->background_defined = 0;
483 info->background_r = info->background_g = info->background_b = 0;
485 info->time_defined = 0;
486 info->phys_defined = 0;
489 void LodePNG_InfoPng_cleanup(LodePNG_InfoPng* info)
491 LodePNG_InfoColor_cleanup(&info->color);
494 unsigned LodePNG_InfoColor_copy(LodePNG_InfoColor* dest, const LodePNG_InfoColor* source)
496 size_t i;
497 LodePNG_InfoColor_cleanup(dest);
498 *dest = *source;
499 for (i = 0; i < source->palettesize * 4; i++) dest->palette[i] = source->palette[i];
500 return 0;
503 unsigned LodePNG_InfoPng_copy(LodePNG_InfoPng* dest, const LodePNG_InfoPng* source)
505 unsigned error = 0;
506 LodePNG_InfoPng_cleanup(dest);
507 *dest = *source;
508 LodePNG_InfoColor_init(&dest->color);
509 error = LodePNG_InfoColor_copy(&dest->color, &source->color); if (error) return error;
510 return error;
513 void LodePNG_InfoPng_swap(LodePNG_InfoPng* a, LodePNG_InfoPng* b)
515 LodePNG_InfoPng temp = *a;
516 *a = *b;
517 *b = temp;
520 void LodePNG_InfoRaw_init(LodePNG_InfoRaw* info)
522 LodePNG_InfoColor_init(&info->color);
525 void LodePNG_InfoRaw_cleanup(LodePNG_InfoRaw* info)
527 LodePNG_InfoColor_cleanup(&info->color);
530 unsigned LodePNG_InfoRaw_copy(LodePNG_InfoRaw* dest, const LodePNG_InfoRaw* source)
532 unsigned error = 0;
533 LodePNG_InfoRaw_cleanup(dest);
534 *dest = *source;
535 LodePNG_InfoColor_init(&dest->color);
536 error = LodePNG_InfoColor_copy(&dest->color, &source->color); if (error) return error;
537 return error;
540 /* ////////////////////////////////////////////////////////////////////////// */
543 converts from any color type to 24-bit or 32-bit (later maybe more supported). return value = LodePNG error code
544 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)
545 for < 8 bpp images, there may _not_ be padding bits at the end of scanlines.
547 unsigned LodePNG_convert(fb_data* out, const unsigned char* in, LodePNG_InfoColor* infoOut, LodePNG_InfoColor* infoIn, unsigned w, unsigned h)
549 size_t i, j, bp = 0; /*bitpointer, used by less-than-8-bit color types*/
550 size_t x, y;
551 unsigned char c;
553 if (!running_slideshow)
555 rb->snprintf(print, sizeof(print), "color conversion in progress");
556 rb->lcd_puts(0, 3, print);
557 rb->lcd_update();
560 /*cases where in and out already have the same format*/
561 if (LodePNG_InfoColor_equal(infoIn, infoOut))
564 i = 0;
565 j = 0;
566 for (y = 0 ; y < h ; y++) {
567 for (x = 0 ; x < w ; x++) {
568 unsigned char r = in[i++];
569 unsigned char g = in[i++];
570 unsigned char b = in[i++];
571 out[j++] = LCD_RGBPACK(r,g,b);
574 return 0;
577 if ((infoOut->colorType == 2 || infoOut->colorType == 6) && infoOut->bitDepth == 8)
579 if (infoIn->bitDepth == 8)
581 switch (infoIn->colorType)
583 case 0: /*greyscale color*/
584 i = 0;
585 for (y = 0 ; y < h ; y++) {
586 for (x = 0 ; x < w ; x++) {
587 c=in[i];
588 //unsigned char r = in[i];
589 //unsigned char g = in[i];
590 //unsigned char b = in[i];
591 out[i++] = LCD_RGBPACK(c,c,c);
594 break;
595 case 2: /*RGB color*/
596 i = 0;
597 for (y = 0 ; y < h ; y++) {
598 for (x = 0 ; x < w ; x++) {
599 j = 3 * i;
600 unsigned char r = in[j];
601 unsigned char g = in[j + 1];
602 unsigned char b = in[j + 2];
603 out[i++] = LCD_RGBPACK(r,g,b);
606 break;
607 case 3: /*indexed color (palette)*/
608 i = 0;
609 for (y = 0 ; y < h ; y++) {
610 for (x = 0 ; x < w ; x++) {
611 if (in[i] >= infoIn->palettesize) return 46;
612 j = in[i] << 2;
613 unsigned char r = infoIn->palette[j];
614 unsigned char g = infoIn->palette[j + 1];
615 unsigned char b = infoIn->palette[j + 2];
616 out[i++] = LCD_RGBPACK(r,g,b);
619 break;
620 case 4: /*greyscale with alpha*/
621 i = 0;
622 for (y = 0 ; y < h ; y++) {
623 for (x = 0 ; x < w ; x++) {
624 c = in[i << 1];
625 //unsigned char r = in[i<<1];
626 //unsigned char g = in[i<<1];
627 //unsigned char b = in[i<<1];
628 out[i++] = LCD_RGBPACK(c,c,c);
631 break;
632 case 6: /*RGB with alpha*/
633 i = 0;
634 for (y = 0 ; y < h ; y++) {
635 for (x = 0 ; x < w ; x++) {
636 j = i << 2;
637 unsigned char r = in[j];
638 unsigned char g = in[j + 1];
639 unsigned char b = in[j + 2];
640 out[i++] = LCD_RGBPACK(r,g,b);
643 break;
644 default:
645 break;
648 else if (infoIn->bitDepth == 16)
650 switch (infoIn->colorType)
652 case 0: /*greyscale color*/
653 i = 0;
654 for (y = 0 ; y < h ; y++) {
655 for (x = 0 ; x < w ; x++) {
656 c = in[i << 1];
657 //unsigned char r = in[2 * i];
658 //unsigned char g = in[2 * i];
659 //unsigned char b = in[2 * i];
660 out[i++] = LCD_RGBPACK(c,c,c);
663 break;
664 case 2: /*RGB color*/
665 i = 0;
666 for (y = 0 ; y < h ; y++) {
667 for (x = 0 ; x < w ; x++) {
668 j = 6 * i;
669 unsigned char r = in[j];
670 unsigned char g = in[j + 2];
671 unsigned char b = in[j + 4];
672 out[i++] = LCD_RGBPACK(r,g,b);
675 break;
676 case 4: /*greyscale with alpha*/
677 i = 0;
678 for (y = 0 ; y < h ; y++) {
679 for (x = 0 ; x < w ; x++) {
680 c = in[i << 2];
681 //unsigned char r = in[4 * i];
682 //unsigned char g = in[4 * i];
683 //unsigned char b = in[4 * i];
684 out[i++] = LCD_RGBPACK(c,c,c);
687 break;
688 case 6: /*RGB with alpha*/
689 i = 0;
690 for (y = 0 ; y < h ; y++) {
691 for (x = 0 ; x < w ; x++) {
692 j = i << 3;
693 unsigned char r = in[j];
694 unsigned char g = in[j + 2];
695 unsigned char b = in[j + 4];
696 out[i++] = LCD_RGBPACK(r,g,b);
699 break;
700 default:
701 break;
704 else /*infoIn->bitDepth is less than 8 bit per channel*/
706 switch (infoIn->colorType)
708 case 0: /*greyscale color*/
709 i = 0;
710 for (y = 0 ; y < h ; y++) {
711 for (x = 0 ; x < w ; x++) {
712 unsigned value = readBitsFromReversedStream(&bp, in, infoIn->bitDepth);
713 value = (value * 255) / ((1 << infoIn->bitDepth) - 1); /*scale value from 0 to 255*/
714 unsigned char r = (unsigned char)value;
715 unsigned char g = (unsigned char)value;
716 unsigned char b = (unsigned char)value;
717 out[i++] = LCD_RGBPACK(r,g,b);
720 break;
721 case 3: /*indexed color (palette)*/
722 i = 0;
723 for (y = 0 ; y < h ; y++) {
724 for (x = 0 ; x < w ; x++) {
725 unsigned value = readBitsFromReversedStream(&bp, in, infoIn->bitDepth);
726 if (value >= infoIn->palettesize) return 47;
727 j = value << 2;
728 unsigned char r = infoIn->palette[j];
729 unsigned char g = infoIn->palette[j + 1];
730 unsigned char b = infoIn->palette[j + 2];
731 out[i++] = LCD_RGBPACK(r,g,b);
734 break;
735 default:
736 break;
740 else if (LodePNG_InfoColor_isGreyscaleType(infoOut) && infoOut->bitDepth == 8) /*conversion from greyscale to greyscale*/
742 if (!LodePNG_InfoColor_isGreyscaleType(infoIn)) return 62;
743 if (infoIn->bitDepth == 8)
745 switch (infoIn->colorType)
747 case 0: /*greyscale color*/
748 i = 0;
749 for (y = 0 ; y < h ; y++) {
750 for (x = 0 ; x < w ; x++) {
751 c = in[i];
752 //unsigned char r = in[i];
753 //unsigned char g = in[i];
754 //unsigned char b = in[i];
755 out[i++] = LCD_RGBPACK(c,c,c);
758 break;
759 case 4: /*greyscale with alpha*/
760 i = 0;
761 for (y = 0 ; y < h ; y++) {
762 for (x = 0 ; x < w ; x++) {
763 c = in[(i << 1) + 1];
764 //unsigned char r = in[2 * i + 1];
765 //unsigned char g = in[2 * i + 1];
766 //unsigned char b = in[2 * i + 1];
767 out[i++] = LCD_RGBPACK(c,c,c);
770 break;
771 default:
772 return 31;
775 else if (infoIn->bitDepth == 16)
777 switch (infoIn->colorType)
779 case 0: /*greyscale color*/
780 i = 0;
781 for (y = 0 ; y < h ; y++) {
782 for (x = 0 ; x < w ; x++) {
783 c = in[i << 1];
784 //unsigned char r = in[2 * i];
785 //unsigned char g = in[2 * i];
786 //unsigned char b = in[2 * i];
787 out[i++] = LCD_RGBPACK(c,c,c);
790 break;
791 case 4: /*greyscale with alpha*/
792 i = 0;
793 for (y = 0 ; y < h ; y++) {
794 for (x = 0 ; x < w ; x++) {
795 c = in[i << 2];
796 //unsigned char r = in[4 * i];
797 //unsigned char g = in[4 * i];
798 //unsigned char b = in[4 * i];
799 out[i++] = LCD_RGBPACK(c,c,c);
802 break;
803 default:
804 return 31;
807 else /*infoIn->bitDepth is less than 8 bit per channel*/
809 if (infoIn->colorType != 0) return 31; /*colorType 0 is the only greyscale type with < 8 bits per channel*/
810 i = 0;
811 for (y = 0 ; y < h ; y++) {
812 for (x = 0 ; x < w ; x++) {
813 unsigned value = readBitsFromReversedStream(&bp, in, infoIn->bitDepth);
814 value = (value * 255) / ((1 << infoIn->bitDepth) - 1); /*scale value from 0 to 255*/
815 unsigned char r = (unsigned char)value;
816 unsigned char g = (unsigned char)value;
817 unsigned char b = (unsigned char)value;
818 out[i++] = LCD_RGBPACK(r,g,b);
823 else return 59;
825 return 0;
828 /*Paeth predicter, used by PNG filter type 4*/
829 static int paethPredictor(int a, int b, int c)
831 int p = a + b - c;
832 int pa = p > a ? p - a : a - p;
833 int pb = p > b ? p - b : b - p;
834 int pc = p > c ? p - c : c - p;
836 if (pa <= pb && pa <= pc) return a;
837 else if (pb <= pc) return b;
838 else return c;
841 /*shared values used by multiple Adam7 related functions*/
843 static const unsigned ADAM7_IX[7] = { 0, 4, 0, 2, 0, 1, 0 }; /*x start values*/
844 static const unsigned ADAM7_IY[7] = { 0, 0, 4, 0, 2, 0, 1 }; /*y start values*/
845 static const unsigned ADAM7_DX[7] = { 8, 8, 4, 4, 2, 2, 1 }; /*x delta values*/
846 static const unsigned ADAM7_DY[7] = { 8, 8, 8, 4, 4, 2, 2 }; /*y delta values*/
848 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)
850 /*the passstart values have 8 values: the 8th one actually indicates the byte after the end of the 7th (= last) pass*/
851 unsigned i;
853 /*calculate width and height in pixels of each pass*/
854 for (i = 0; i < 7; i++)
856 passw[i] = (w + ADAM7_DX[i] - ADAM7_IX[i] - 1) / ADAM7_DX[i];
857 passh[i] = (h + ADAM7_DY[i] - ADAM7_IY[i] - 1) / ADAM7_DY[i];
858 if (passw[i] == 0) passh[i] = 0;
859 if (passh[i] == 0) passw[i] = 0;
862 filter_passstart[0] = padded_passstart[0] = passstart[0] = 0;
863 for (i = 0; i < 7; i++)
865 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)*/
866 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*/
867 passstart[i + 1] = passstart[i] + (passh[i] * passw[i] * bpp + 7) / 8; /*only padded at end of reduced image*/
871 /* ////////////////////////////////////////////////////////////////////////// */
872 /* / PNG Decoder / */
873 /* ////////////////////////////////////////////////////////////////////////// */
875 /*read the information from the header and store it in the LodePNG_Info. return value is error*/
876 void LodePNG_inspect(LodePNG_Decoder* decoder, const unsigned char* in, size_t inlength)
878 if (inlength == 0 || in == 0) { decoder->error = 48; return; } /*the given data is empty*/
879 if (inlength < 29) { decoder->error = 27; return; } /*error: the data length is smaller than the length of the header*/
881 /*when decoding a new PNG image, make sure all parameters created after previous decoding are reset*/
882 LodePNG_InfoPng_cleanup(&decoder->infoPng);
883 LodePNG_InfoPng_init(&decoder->infoPng);
884 decoder->error = 0;
886 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*/
887 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!*/
889 /*read the values given in the header*/
890 decoder->infoPng.width = LodePNG_read32bitInt(&in[16]);
891 decoder->infoPng.height = LodePNG_read32bitInt(&in[20]);
892 decoder->infoPng.color.bitDepth = in[24];
893 decoder->infoPng.color.colorType = in[25];
894 decoder->infoPng.compressionMethod = in[26];
895 decoder->infoPng.filterMethod = in[27];
896 decoder->infoPng.interlaceMethod = in[28];
898 unsigned CRC = LodePNG_read32bitInt(&in[29]);
899 unsigned checksum = crc32(0L, &in[12], 17);
900 if (CRC != checksum) { decoder->error = 57; return; }
902 if (decoder->infoPng.compressionMethod != 0) { decoder->error = 32; return; } /*error: only compression method 0 is allowed in the specification*/
903 if (decoder->infoPng.filterMethod != 0) { decoder->error = 33; return; } /*error: only filter method 0 is allowed in the specification*/
904 if (decoder->infoPng.interlaceMethod > 1) { decoder->error = 34; return; } /*error: only interlace methods 0 and 1 exist in the specification*/
906 decoder->error = checkColorValidity(decoder->infoPng.color.colorType, decoder->infoPng.color.bitDepth);
909 static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scanline, const unsigned char* precon, size_t bytewidth, unsigned char filterType, size_t length)
912 For PNG filter method 0
913 unfilter a PNG image scanline by scanline. when the pixels are smaller than 1 byte, the filter works byte per byte (bytewidth = 1)
914 precon is the previous unfiltered scanline, recon the result, scanline the current one
915 the incoming scanlines do NOT include the filtertype byte, that one is given in the parameter filterType instead
916 recon and scanline MAY be the same memory address! precon must be disjoint.
919 size_t i;
920 switch (filterType)
922 case 0:
923 //for(i = 0; i < length; i++) recon[i] = scanline[i];
924 memcpy(recon, scanline, length * sizeof(unsigned char));
925 break;
926 case 1:
927 //for(i = 0; i < bytewidth; i++) recon[i] = scanline[i];
928 memcpy(recon, scanline, bytewidth * sizeof(unsigned char));
929 for (i = bytewidth; i < length; i++) recon[i] = scanline[i] + recon[i - bytewidth];
930 break;
931 case 2:
932 if (precon) for (i = 0; i < length; i++) recon[i] = scanline[i] + precon[i];
933 else //for(i = 0; i < length; i++) recon[i] = scanline[i];
934 memcpy(recon, scanline, length * sizeof(unsigned char));
935 break;
936 case 3:
937 if (precon)
939 for (i = 0; i < bytewidth; i++) recon[i] = scanline[i] + precon[i] / 2;
940 for (i = bytewidth; i < length; i++) recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) / 2);
942 else
944 //for(i = 0; i < bytewidth; i++) recon[i] = scanline[i];
945 memcpy(recon, scanline, bytewidth * sizeof(unsigned char));
946 for (i = bytewidth; i < length; i++) recon[i] = scanline[i] + recon[i - bytewidth] / 2;
948 break;
949 case 4:
950 if (precon)
952 for (i = 0; i < bytewidth; i++) recon[i] = (unsigned char)(scanline[i] + paethPredictor(0, precon[i], 0));
953 for (i = bytewidth; i < length; i++) recon[i] = (unsigned char)(scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[i - bytewidth]));
955 else
957 //for(i = 0; i < bytewidth; i++) recon[i] = scanline[i];
958 memcpy(recon, scanline, bytewidth * sizeof(unsigned char));
959 for (i = bytewidth; i < length; i++) recon[i] = (unsigned char)(scanline[i] + paethPredictor(recon[i - bytewidth], 0, 0));
961 break;
962 default:
963 return 36; /*error: unexisting filter type given*/
965 return 0;
968 static unsigned unfilter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp)
971 For PNG filter method 0
972 this function unfilters a single image (e.g. without interlacing this is called once, with Adam7 it's called 7 times)
973 out must have enough bytes allocated already, in must have the scanlines + 1 filtertype byte per scanline
974 w and h are image dimensions or dimensions of reduced image, bpp is bits per pixel
975 in and out are allowed to be the same memory address!
978 unsigned y;
979 unsigned char* prevline = 0;
981 size_t bytewidth = (bpp + 7) / 8; /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/
982 size_t linebytes = (w * bpp + 7) / 8;
984 for (y = 0; y < h; y++)
986 size_t outindex = linebytes * y;
987 size_t inindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/
988 unsigned char filterType = in[inindex];
990 unsigned error = unfilterScanline(&out[outindex], &in[inindex + 1], prevline, bytewidth, filterType, linebytes);
991 if (error) return error;
993 prevline = &out[outindex];
996 return 0;
999 static void Adam7_deinterlace(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp)
1001 /*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
1002 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)*/
1003 unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8];
1004 unsigned i;
1006 Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp);
1008 if (bpp >= 8)
1010 for (i = 0; i < 7; i++)
1012 unsigned x, y, b;
1013 size_t bytewidth = bpp / 8;
1014 for (y = 0; y < passh[i]; y++)
1015 for (x = 0; x < passw[i]; x++)
1017 size_t pixelinstart = passstart[i] + (y * passw[i] + x) * bytewidth;
1018 size_t pixeloutstart = ((ADAM7_IY[i] + y * ADAM7_DY[i]) * w + ADAM7_IX[i] + x * ADAM7_DX[i]) * bytewidth;
1019 for (b = 0; b < bytewidth; b++)
1021 out[pixeloutstart + b] = in[pixelinstart + b];
1026 else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/
1028 for (i = 0; i < 7; i++)
1030 unsigned x, y, b;
1031 unsigned ilinebits = bpp * passw[i];
1032 unsigned olinebits = bpp * w;
1033 size_t obp, ibp; /*bit pointers (for out and in buffer)*/
1034 for (y = 0; y < passh[i]; y++)
1035 for (x = 0; x < passw[i]; x++)
1037 ibp = (8 * passstart[i]) + (y * ilinebits + x * bpp);
1038 obp = (ADAM7_IY[i] + y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + x * ADAM7_DX[i]) * bpp;
1039 for (b = 0; b < bpp; b++)
1041 unsigned char bit = readBitFromReversedStream(&ibp, in);
1042 setBitOfReversedStream0(&obp, out, bit); /*note that this function assumes the out buffer is completely 0, use setBitOfReversedStream otherwise*/
1049 static void removePaddingBits(unsigned char* out, const unsigned char* in, size_t olinebits, size_t ilinebits, unsigned h)
1052 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.
1053 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
1054 also used to move bits after earlier such operations happened, e.g. in a sequence of reduced images from Adam7
1055 only useful if (ilinebits - olinebits) is a value in the range 1..7
1057 unsigned y;
1058 size_t diff = ilinebits - olinebits;
1059 size_t obp = 0, ibp = 0; /*bit pointers*/
1060 for (y = 0; y < h; y++)
1062 size_t x;
1063 for (x = 0; x < olinebits; x++)
1065 unsigned char bit = readBitFromReversedStream(&ibp, in);
1066 setBitOfReversedStream(&obp, out, bit);
1068 ibp += diff;
1072 /*out must be buffer big enough to contain full image, and in must contain the full decompressed data from the IDAT chunks*/
1073 static unsigned postProcessScanlines(unsigned char* out, unsigned char* in, const LodePNG_Decoder* decoder) /*return value is error*/
1076 This function converts the filtered-padded-interlaced data into pure 2D image buffer with the PNG's colortype. Steps:
1077 *) if no Adam7: 1) unfilter 2) remove padding bits (= posible extra bits per scanline if bpp < 8)
1078 *) if adam7: 1) 7x unfilter 2) 7x remove padding bits 3) Adam7_deinterlace
1079 NOTE: the in buffer will be overwritten with intermediate data!
1081 unsigned bpp = LodePNG_InfoColor_getBpp(&decoder->infoPng.color);
1082 unsigned w = decoder->infoPng.width;
1083 unsigned h = decoder->infoPng.height;
1084 unsigned error = 0;
1085 if (bpp == 0) return 31; /*error: invalid colortype*/
1087 if (decoder->infoPng.interlaceMethod == 0)
1089 if (bpp < 8 && w * bpp != ((w * bpp + 7) / 8) * 8)
1091 error = unfilter(in, in, w, h, bpp);
1092 if (error) return error;
1093 removePaddingBits(out, in, w * bpp, ((w * bpp + 7) / 8) * 8, h);
1095 else error = unfilter(out, in, w, h, bpp); /*we can immediatly filter into the out buffer, no other steps needed*/
1097 else /*interlaceMethod is 1 (Adam7)*/
1099 unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8];
1100 unsigned i;
1102 Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp);
1104 for (i = 0; i < 7; i++)
1106 error = unfilter(&in[padded_passstart[i]], &in[filter_passstart[i]], passw[i], passh[i], bpp);
1107 if (error) return error;
1108 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*/
1110 /*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*/
1111 removePaddingBits(&in[passstart[i]], &in[padded_passstart[i]], passw[i] * bpp, ((passw[i] * bpp + 7) / 8) * 8, passh[i]);
1115 Adam7_deinterlace(out, in, w, h, bpp);
1118 return error;
1121 /*read a PNG, the result will be in the same color type as the PNG (hence "generic")*/
1122 static void decodeGeneric(LodePNG_Decoder* decoder, unsigned char* in, size_t size, void (*pf_progress)(int current, int total))
1124 if (pf_progress != NULL)
1125 pf_progress(0, 100);
1126 unsigned char IEND = 0;
1127 const unsigned char* chunk;
1128 size_t i;
1129 unsigned char *idat = memory;
1130 size_t idat_size = 0;
1132 /*for unknown chunk order*/
1133 unsigned unknown = 0;
1134 unsigned critical_pos = 1; /*1 = after IHDR, 2 = after PLTE, 3 = after IDAT*/
1136 /*provide some proper output values if error will happen*/
1137 decoded_image_size = 0;
1139 if (size == 0 || in == 0) { decoder->error = 48; return; } /*the given data is empty*/
1141 LodePNG_inspect(decoder, in, size); /*reads header and resets other parameters in decoder->infoPng*/
1142 if (decoder->error) return;
1144 chunk = &in[33]; /*first byte of the first chunk after the header*/
1146 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*/
1148 unsigned chunkLength;
1149 const unsigned char* data; /*the data in the chunk*/
1151 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*/
1152 chunkLength = LodePNG_chunk_length(chunk); /*length of the data of the chunk, excluding the length bytes, chunk type and CRC bytes*/
1153 if (chunkLength > 2147483647) { decoder->error = 63; break; }
1154 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*/
1155 data = LodePNG_chunk_data_const(chunk);
1157 /*IDAT chunk, containing compressed image data*/
1158 if (LodePNG_chunk_type_equals(chunk, "IDAT"))
1160 size_t oldsize = idat_size;
1161 idat_size += chunkLength;
1162 if (idat + idat_size >= image) { decoder->error = OUT_OF_MEMORY; break; }
1163 memcpy(idat+oldsize, data, chunkLength * sizeof(unsigned char));
1164 critical_pos = 3;
1166 /*IEND chunk*/
1167 else if (LodePNG_chunk_type_equals(chunk, "IEND"))
1169 IEND = 1;
1171 /*palette chunk (PLTE)*/
1172 else if (LodePNG_chunk_type_equals(chunk, "PLTE"))
1174 unsigned pos = 0;
1175 decoder->infoPng.color.palettesize = chunkLength / 3;
1176 if (decoder->infoPng.color.palettesize > 256) { decoder->error = 38; break; } /*error: palette too big*/
1177 for (i = 0; i < decoder->infoPng.color.palettesize; i++)
1179 decoder->infoPng.color.palette[4 * i + 0] = data[pos++]; /*R*/
1180 decoder->infoPng.color.palette[4 * i + 1] = data[pos++]; /*G*/
1181 decoder->infoPng.color.palette[4 * i + 2] = data[pos++]; /*B*/
1182 decoder->infoPng.color.palette[4 * i + 3] = 255; /*alpha*/
1184 critical_pos = 2;
1186 /*palette transparency chunk (tRNS)*/
1187 else if (LodePNG_chunk_type_equals(chunk, "tRNS"))
1189 if (decoder->infoPng.color.colorType == 3)
1191 if (chunkLength > decoder->infoPng.color.palettesize) { decoder->error = 39; break; } /*error: more alpha values given than there are palette entries*/
1192 for (i = 0; i < chunkLength; i++) decoder->infoPng.color.palette[4 * i + 3] = data[i];
1194 else if (decoder->infoPng.color.colorType == 0)
1196 if (chunkLength != 2) { decoder->error = 40; break; } /*error: this chunk must be 2 bytes for greyscale image*/
1197 decoder->infoPng.color.key_defined = 1;
1198 decoder->infoPng.color.key_r = decoder->infoPng.color.key_g = decoder->infoPng.color.key_b = 256 * data[0] + data[1];
1200 else if (decoder->infoPng.color.colorType == 2)
1202 if (chunkLength != 6) { decoder->error = 41; break; } /*error: this chunk must be 6 bytes for RGB image*/
1203 decoder->infoPng.color.key_defined = 1;
1204 decoder->infoPng.color.key_r = 256 * data[0] + data[1];
1205 decoder->infoPng.color.key_g = 256 * data[2] + data[3];
1206 decoder->infoPng.color.key_b = 256 * data[4] + data[5];
1208 else { decoder->error = 42; break; } /*error: tRNS chunk not allowed for other color models*/
1210 /*background color chunk (bKGD)*/
1211 else if (LodePNG_chunk_type_equals(chunk, "bKGD"))
1213 if (decoder->infoPng.color.colorType == 3)
1215 if (chunkLength != 1) { decoder->error = 43; break; } /*error: this chunk must be 1 byte for indexed color image*/
1216 decoder->infoPng.background_defined = 1;
1217 decoder->infoPng.background_r = decoder->infoPng.background_g = decoder->infoPng.background_g = data[0];
1219 else if (decoder->infoPng.color.colorType == 0 || decoder->infoPng.color.colorType == 4)
1221 if (chunkLength != 2) { decoder->error = 44; break; } /*error: this chunk must be 2 bytes for greyscale image*/
1222 decoder->infoPng.background_defined = 1;
1223 decoder->infoPng.background_r = decoder->infoPng.background_g = decoder->infoPng.background_b = 256 * data[0] + data[1];
1225 else if (decoder->infoPng.color.colorType == 2 || decoder->infoPng.color.colorType == 6)
1227 if (chunkLength != 6) { decoder->error = 45; break; } /*error: this chunk must be 6 bytes for greyscale image*/
1228 decoder->infoPng.background_defined = 1;
1229 decoder->infoPng.background_r = 256 * data[0] + data[1];
1230 decoder->infoPng.background_g = 256 * data[2] + data[3];
1231 decoder->infoPng.background_b = 256 * data[4] + data[5];
1234 else if (LodePNG_chunk_type_equals(chunk, "tIME"))
1236 if (chunkLength != 7) { decoder->error = 73; break; }
1237 decoder->infoPng.time_defined = 1;
1238 decoder->infoPng.time.year = 256 * data[0] + data[+ 1];
1239 decoder->infoPng.time.month = data[2];
1240 decoder->infoPng.time.day = data[3];
1241 decoder->infoPng.time.hour = data[4];
1242 decoder->infoPng.time.minute = data[5];
1243 decoder->infoPng.time.second = data[6];
1245 else if (LodePNG_chunk_type_equals(chunk, "pHYs"))
1247 if (chunkLength != 9) { decoder->error = 74; break; }
1248 decoder->infoPng.phys_defined = 1;
1249 decoder->infoPng.phys_x = 16777216 * data[0] + 65536 * data[1] + 256 * data[2] + data[3];
1250 decoder->infoPng.phys_y = 16777216 * data[4] + 65536 * data[5] + 256 * data[6] + data[7];
1251 decoder->infoPng.phys_unit = data[8];
1253 else /*it's not an implemented chunk type, so ignore it: skip over the data*/
1255 if (LodePNG_chunk_critical(chunk)) { decoder->error = 69; break; } /*error: unknown critical chunk (5th bit of first byte of chunk type is 0)*/
1256 unknown = 1;
1259 if (!unknown) /*check CRC if wanted, only on known chunk types*/
1261 long time = *rb->current_tick;
1262 if (LodePNG_chunk_check_crc(chunk)) { decoder->error = 57; break; }
1263 time = *rb->current_tick-time;
1266 if (!IEND) chunk = LodePNG_chunk_next_const(chunk);
1269 if (!decoder->error)
1271 unsigned char *scanlines = idat + idat_size;
1272 size_t scanlines_size = (size_t)memory_max - idat_size + 1;
1273 long time = *rb->current_tick;
1274 decoder->error = LodePNG_decompress(scanlines, &scanlines_size, idat, idat_size, decoder->error_msg); /*decompress with the Zlib decompressor*/
1275 if (pf_progress) pf_progress(100, 100);
1276 time = *rb->current_tick-time;
1278 if (!decoder->error)
1280 decoded_image_size = (decoder->infoPng.height * decoder->infoPng.width * LodePNG_InfoColor_getBpp(&decoder->infoPng.color) + 7) / 8;
1281 if (decoded_image_size > memory_size) { decoder->error = OUT_OF_MEMORY; return; }
1282 decoded_image = memory_max - decoded_image_size + 1;
1283 if (scanlines + scanlines_size >= decoded_image) { decoder->error = OUT_OF_MEMORY; return; }
1284 memset(decoded_image, 0, decoded_image_size * sizeof(unsigned char));
1285 if (!running_slideshow)
1287 rb->snprintf(print, sizeof(print), "unfiltering scanlines");
1288 rb->lcd_puts(0, 3, print);
1289 rb->lcd_update();
1291 decoder->error = postProcessScanlines(decoded_image, scanlines, decoder);
1296 void LodePNG_decode(LodePNG_Decoder* decoder, unsigned char* in, size_t insize, void (*pf_progress)(int current, int total))
1298 decodeGeneric(decoder, in, insize, pf_progress);
1299 if (decoder->error) return;
1301 /*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"*/
1302 if (!(decoder->infoRaw.color.colorType == 2 || decoder->infoRaw.color.colorType == 6) && !(decoder->infoRaw.color.bitDepth == 8)) { decoder->error = 56; return; }
1303 converted_image = (fb_data *)((intptr_t)(memory + 3) & ~3);
1304 converted_image_size = decoder->infoPng.width*decoder->infoPng.height;
1305 if ((unsigned char *)(converted_image + converted_image_size) >= decoded_image) { decoder->error = OUT_OF_MEMORY; }
1306 if (!decoder->error) decoder->error = LodePNG_convert(converted_image, decoded_image, &decoder->infoRaw.color, &decoder->infoPng.color, decoder->infoPng.width, decoder->infoPng.height);
1309 void LodePNG_DecodeSettings_init(LodePNG_DecodeSettings* settings)
1311 settings->color_convert = 1;
1314 void LodePNG_Decoder_init(LodePNG_Decoder* decoder)
1316 LodePNG_DecodeSettings_init(&decoder->settings);
1317 LodePNG_InfoRaw_init(&decoder->infoRaw);
1318 LodePNG_InfoPng_init(&decoder->infoPng);
1319 decoder->error = 1;
1322 void LodePNG_Decoder_cleanup(LodePNG_Decoder* decoder)
1324 LodePNG_InfoRaw_cleanup(&decoder->infoRaw);
1325 LodePNG_InfoPng_cleanup(&decoder->infoPng);
1328 bool png_ext(const char ext[])
1330 if (!ext)
1331 return false;
1332 if (!rb->strcasecmp(ext,".png"))
1333 return true;
1334 else
1335 return false;
1338 /*Read directory contents for scrolling. */
1339 void get_pic_list(void)
1341 int i;
1342 struct entry *dircache;
1343 char *pname;
1344 tree = rb->tree_get_context();
1345 dircache = tree->dircache;
1347 file_pt = (char **) memory;
1349 /* Remove path and leave only the name.*/
1350 pname = rb->strrchr(np_file,'/');
1351 pname++;
1353 for (i = 0; i < tree->filesindir; i++)
1355 if (!(dircache[i].attr & ATTR_DIRECTORY)
1356 && png_ext(rb->strrchr(dircache[i].name, '.')))
1358 file_pt[entries] = dircache[i].name;
1359 /* Set Selected File. */
1360 if (!rb->strcmp(file_pt[entries], pname))
1361 curfile = entries;
1362 entries++;
1366 memory += (entries * sizeof(char**));
1367 memory_size -= (entries * sizeof(char**));
1370 int change_filename(int direct)
1372 int count = 0;
1373 direction = direct;
1375 if (direct == DIR_PREV)
1379 count++;
1380 if (curfile == 0)
1381 curfile = entries - 1;
1382 else
1383 curfile--;
1384 }while (file_pt[curfile] == NULL && count < entries);
1385 /* we "erase" the file name if we encounter
1386 * a non-supported file, so skip it now */
1388 else /* DIR_NEXT/DIR_NONE */
1392 count++;
1393 if (curfile == entries - 1)
1394 curfile = 0;
1395 else
1396 curfile++;
1397 }while (file_pt[curfile] == NULL && count < entries);
1400 if (count == entries)
1402 rb->splash(HZ, "No supported files");
1403 return PLUGIN_ERROR;
1406 if (rb->strlen(tree->currdir) > 1)
1408 rb->strcpy(np_file, tree->currdir);
1409 rb->strcat(np_file, "/");
1411 else
1412 rb->strcpy(np_file, tree->currdir);
1414 rb->strcat(np_file, file_pt[curfile]);
1416 return PLUGIN_OTHER;
1419 /* switch off overlay, for handling SYS_ events */
1420 void cleanup(void *parameter)
1422 (void)parameter;
1425 #define VSCROLL (LCD_HEIGHT/8)
1426 #define HSCROLL (LCD_WIDTH/10)
1428 #define ZOOM_IN 100 /* return codes for below function */
1429 #define ZOOM_OUT 101
1431 int show_menu(void) /* return 1 to quit */
1433 #if LCD_DEPTH > 1
1434 rb->lcd_set_backdrop(old_backdrop);
1435 #ifdef HAVE_LCD_COLOR
1436 rb->lcd_set_foreground(rb->global_settings->fg_color);
1437 rb->lcd_set_background(rb->global_settings->bg_color);
1438 #else
1439 rb->lcd_set_foreground(LCD_BLACK);
1440 rb->lcd_set_background(LCD_WHITE);
1441 #endif
1442 #endif
1443 int result;
1445 enum menu_id
1447 MIID_RETURN = 0,
1448 MIID_TOGGLE_SS_MODE,
1449 MIID_CHANGE_SS_MODE,
1450 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
1451 MIID_SHOW_PLAYBACK_MENU,
1452 #endif
1453 MIID_QUIT,
1456 MENUITEM_STRINGLIST(menu, "Png Menu", NULL,
1457 "Return", "Toggle Slideshow Mode",
1458 "Change Slideshow Time",
1459 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
1460 "Show Playback Menu",
1461 #endif
1462 "Quit");
1464 static const struct opt_items slideshow[2] = {
1465 { "Disable", -1 },
1466 { "Enable", -1 },
1469 result=rb->do_menu(&menu, NULL, NULL, false);
1471 switch (result)
1473 case MIID_RETURN:
1474 break;
1475 case MIID_TOGGLE_SS_MODE:
1476 rb->set_option("Toggle Slideshow", &slideshow_enabled, INT,
1477 slideshow , 2, NULL);
1478 break;
1479 case MIID_CHANGE_SS_MODE:
1480 rb->set_int("Slideshow Time", "s", UNIT_SEC,
1481 &png_settings.ss_timeout, NULL, 1,
1482 SS_MIN_TIMEOUT, SS_MAX_TIMEOUT, NULL);
1483 break;
1484 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
1485 case MIID_SHOW_PLAYBACK_MENU:
1486 if (plug_buf)
1488 playback_control(NULL);
1490 else
1492 rb->splash(HZ, "Cannot restart playback");
1494 break;
1495 #endif
1496 case MIID_QUIT:
1497 return 1;
1498 break;
1501 #if !defined(SIMULATOR) && defined(HAVE_DISK_STORAGE)
1502 /* change ata spindown time based on slideshow time setting */
1503 immediate_ata_off = false;
1504 rb->storage_spindown(rb->global_settings->disk_spindown);
1506 if (slideshow_enabled)
1508 if (png_settings.ss_timeout < 10)
1510 /* slideshow times < 10s keep disk spinning */
1511 rb->storage_spindown(0);
1513 else if (!rb->mp3_is_playing())
1515 /* slideshow times > 10s and not playing: ata_off after load */
1516 immediate_ata_off = true;
1519 #endif
1520 #if LCD_DEPTH > 1
1521 rb->lcd_set_backdrop(NULL);
1522 rb->lcd_set_foreground(LCD_WHITE);
1523 rb->lcd_set_background(LCD_BLACK);
1524 #endif
1525 rb->lcd_clear_display();
1526 return 0;
1529 void draw_image(struct LodePNG_Decoder* decoder)
1531 rb->lcd_bitmap_part(resized_image, decoder->x, decoder->y, decoder->infoPng.width/ds /*stride*/,
1532 MAX(0, (LCD_WIDTH - (int)decoder->infoPng.width/(int)ds) / 2),
1533 MAX(0, (LCD_HEIGHT - (int)decoder->infoPng.height/(int)ds) / 2),
1534 decoder->infoPng.width/ds - decoder->x,
1535 decoder->infoPng.height/ds - decoder->y);
1538 /* Pan the viewing window right - move image to the left and fill in
1539 the right-hand side */
1540 static void pan_view_right(struct LodePNG_Decoder* decoder)
1542 int move;
1544 move = MIN(HSCROLL, (int)(decoder->infoPng.width/ds) - decoder->x - LCD_WIDTH);
1545 if (move > 0)
1547 decoder->x += move;
1548 draw_image(decoder);
1549 rb->lcd_update();
1553 /* Pan the viewing window left - move image to the right and fill in
1554 the left-hand side */
1555 static void pan_view_left(struct LodePNG_Decoder* decoder)
1557 int move;
1559 move = MIN(HSCROLL, decoder->x);
1560 if (move > 0)
1562 decoder->x -= move;
1563 draw_image(decoder);
1564 rb->lcd_update();
1569 /* Pan the viewing window up - move image down and fill in
1570 the top */
1571 static void pan_view_up(struct LodePNG_Decoder* decoder)
1573 int move;
1575 move = MIN(VSCROLL, decoder->y);
1576 if (move > 0)
1578 decoder->y -= move;
1579 draw_image(decoder);
1580 rb->lcd_update();
1584 /* Pan the viewing window down - move image up and fill in
1585 the bottom */
1586 static void pan_view_down(struct LodePNG_Decoder* decoder)
1588 int move;
1590 move = MIN(VSCROLL, (int)(decoder->infoPng.height/ds) - decoder->y - LCD_HEIGHT);
1591 if (move > 0)
1593 decoder->y += move;
1594 draw_image(decoder);
1595 rb->lcd_update();
1599 /* interactively scroll around the image */
1600 int scroll_bmp(struct LodePNG_Decoder* decoder)
1602 int button;
1603 int lastbutton = 0;
1605 while (true)
1607 if (slideshow_enabled)
1608 button = rb->button_get_w_tmo(png_settings.ss_timeout * HZ);
1609 else button = rb->button_get(true);
1611 running_slideshow = false;
1613 switch (button)
1615 case PNG_LEFT:
1616 if (!(ds < ds_max) && entries > 0 && decoder->infoPng.width <= MAX_X_SIZE)
1617 return change_filename(DIR_PREV);
1618 case PNG_LEFT | BUTTON_REPEAT:
1619 pan_view_left(decoder);
1620 break;
1622 case PNG_RIGHT:
1623 if (!(ds < ds_max) && entries > 0 && decoder->infoPng.width <= MAX_X_SIZE)
1624 return change_filename(DIR_NEXT);
1625 case PNG_RIGHT | BUTTON_REPEAT:
1626 pan_view_right(decoder);
1627 break;
1629 case PNG_UP:
1630 case PNG_UP | BUTTON_REPEAT:
1631 pan_view_up(decoder);
1632 break;
1634 case PNG_DOWN:
1635 case PNG_DOWN | BUTTON_REPEAT:
1636 pan_view_down(decoder);
1637 break;
1639 case BUTTON_NONE:
1640 if (!slideshow_enabled)
1641 break;
1642 running_slideshow = true;
1643 if (entries > 0)
1644 return change_filename(DIR_NEXT);
1645 break;
1647 #ifdef PNG_SLIDE_SHOW
1648 case PNG_SLIDE_SHOW:
1649 slideshow_enabled = !slideshow_enabled;
1650 running_slideshow = slideshow_enabled;
1651 break;
1652 #endif
1654 #ifdef PNG_NEXT_REPEAT
1655 case PNG_NEXT_REPEAT:
1656 #endif
1657 case PNG_NEXT:
1658 if (entries > 0)
1659 return change_filename(DIR_NEXT);
1660 break;
1662 #ifdef PNG_PREVIOUS_REPEAT
1663 case PNG_PREVIOUS_REPEAT:
1664 #endif
1665 case PNG_PREVIOUS:
1666 if (entries > 0)
1667 return change_filename(DIR_PREV);
1668 break;
1670 case PNG_ZOOM_IN:
1671 #ifdef PNG_ZOOM_PRE
1672 if (lastbutton != PNG_ZOOM_PRE)
1673 break;
1674 #endif
1675 return ZOOM_IN;
1676 break;
1678 case PNG_ZOOM_OUT:
1679 #ifdef PNG_ZOOM_PRE
1680 if (lastbutton != PNG_ZOOM_PRE)
1681 break;
1682 #endif
1683 return ZOOM_OUT;
1684 break;
1686 #ifdef PNG_RC_MENU
1687 case PNG_RC_MENU:
1688 #endif
1689 case PNG_MENU:
1690 if (show_menu() == 1)
1691 return PLUGIN_OK;
1692 else
1693 return PLUGIN_REFRESH;
1695 break;
1696 default:
1697 if (rb->default_event_handler_ex(button, cleanup, NULL)
1698 == SYS_USB_CONNECTED)
1699 return PLUGIN_USB_CONNECTED;
1700 break;
1702 } /* switch */
1704 if (button != BUTTON_NONE)
1705 lastbutton = button;
1706 } /* while (true) */
1709 /* set the view to the given center point, limit if necessary */
1710 void set_view (struct LodePNG_Decoder* decoder, int cx, int cy)
1712 int x, y;
1714 /* plain center to available width/height */
1715 x = cx - MIN(LCD_WIDTH, decoder->infoPng.width/ds) / 2;
1716 y = cy - MIN(LCD_HEIGHT, decoder->infoPng.height/ds) / 2;
1718 /* limit against upper image size */
1719 x = MIN((int)(decoder->infoPng.width/ds) - LCD_WIDTH, x);
1720 y = MIN((int)(decoder->infoPng.height/ds) - LCD_HEIGHT, y);
1722 /* limit against negative side */
1723 x = MAX(0, x);
1724 y = MAX(0, y);
1726 decoder->x = x; /* set the values */
1727 decoder->y = y;
1731 /* callback updating a progress meter while PNG decoding */
1732 void cb_progress(int current, int total)
1735 if (current & 1) rb->yield(); /* be nice to the other threads */
1736 if (!running_slideshow)
1738 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, LCD_HEIGHT-8, LCD_WIDTH, 8, total, 0,
1739 current, HORIZONTAL);
1740 rb->lcd_update_rect(0, LCD_HEIGHT-8, LCD_WIDTH, 8);
1742 else
1744 /* in slideshow mode, keep gui interference to a minimum */
1745 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, LCD_HEIGHT-4, LCD_WIDTH, 4, total, 0,
1746 current, HORIZONTAL);
1747 rb->lcd_update_rect(0, LCD_HEIGHT-4, LCD_WIDTH, 4);
1751 int pngmem(struct LodePNG_Decoder* decoder, int ds)
1753 return (decoder->infoPng.width/ds) * (decoder->infoPng.height/ds) * FB_DATA_SZ;
1756 /* how far can we zoom in without running out of memory */
1757 int min_downscale(struct LodePNG_Decoder* decoder, int bufsize)
1759 int downscale = 8;
1761 if (pngmem(decoder, 8) > bufsize)
1762 return 0; /* error, too large, even 1:8 doesn't fit */
1764 while (downscale > 1 && pngmem(decoder, downscale/2) <= bufsize)
1765 downscale /= 2;
1767 return downscale;
1770 /* how far can we zoom out, to fit image into the LCD */
1771 unsigned max_downscale(struct LodePNG_Decoder* decoder)
1773 unsigned downscale = 1;
1775 while (downscale < 8 && (decoder->infoPng.width > LCD_WIDTH*downscale
1776 || decoder->infoPng.height > LCD_HEIGHT*downscale))
1778 downscale *= 2;
1781 return downscale;
1784 /* calculate the view center based on the bitmap position */
1785 void get_view(struct LodePNG_Decoder* decoder, int* p_cx, int* p_cy)
1787 *p_cx = decoder->x + MIN(LCD_WIDTH, decoder->infoPng.width/ds) / 2;
1788 *p_cy = decoder->y + MIN(LCD_HEIGHT, decoder->infoPng.height/ds) / 2;
1791 /* return decoded or cached image */
1792 fb_data *get_image(struct LodePNG_Decoder* decoder)
1794 fb_data * p_disp = disp[ds]; /* short cut */
1796 if (p_disp != NULL)
1798 DEBUGF("Found an image in cache\n");
1799 return p_disp; /* we still have it */
1802 /* assign image buffer */
1803 if (ds > 1) {
1804 if (!running_slideshow)
1806 rb->snprintf(print, sizeof(print), "resizing %d*%d",
1807 decoder->infoPng.width/ds, decoder->infoPng.height/ds);
1808 rb->lcd_puts(0, 3, print);
1809 rb->lcd_update();
1811 static struct bitmap bmp_src, bmp_dst;
1813 int size = (decoder->infoPng.width/ds) * (decoder->infoPng.height/ds);
1814 disp[ds] = disp_buf;
1816 if ((unsigned char *)(disp[ds] + size) >= memory_max) {
1817 //rb->splash(HZ, "Out of Memory");
1818 // Still display the original image which is already decoded in RAM
1819 disp[ds] = converted_image;
1820 ds = 1;
1821 return converted_image;
1822 } else {
1823 bmp_src.width = decoder->infoPng.width;
1824 bmp_src.height = decoder->infoPng.height;
1825 bmp_src.data = (unsigned char *)converted_image;
1827 bmp_dst.width = decoder->infoPng.width/ds;
1828 bmp_dst.height = decoder->infoPng.height/ds;
1829 bmp_dst.data = (unsigned char *)disp[ds];
1830 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1831 rb->cpu_boost(true);
1832 smooth_resize_bitmap(&bmp_src, &bmp_dst);
1833 rb->cpu_boost(false);
1834 #else
1835 smooth_resize_bitmap(&bmp_src, &bmp_dst);
1836 #endif /*HAVE_ADJUSTABLE_CPU_FREQ*/
1838 disp_buf = (fb_data *)((intptr_t)(disp[ds] + size + 3) & ~3);
1840 } else {
1841 disp[ds] = converted_image;
1842 return converted_image;
1845 return disp[ds];
1848 /* load, decode, display the image */
1849 int load_and_show(char* filename)
1851 int fd;
1852 int status;
1853 long time=0; /* measured ticks */
1854 int cx=0, cy=0; /* view center */
1855 int w, h; /* used to center output */
1857 LodePNG_Decoder_init(&decoder);
1859 rb->lcd_clear_display();
1861 fd = rb->open(filename, O_RDONLY);
1862 if (fd < 0)
1864 rb->snprintf(print,sizeof(print),"err opening %s:%d",filename,fd);
1865 rb->splash(HZ, print);
1866 return PLUGIN_ERROR;
1868 image_size = rb->filesize(fd);
1869 memset(&disp, 0, sizeof(disp));
1871 DEBUGF("reading file '%s'\n", filename);
1873 if (!running_slideshow) {
1874 #if LCD_DEPTH > 1
1875 rb->lcd_set_foreground(LCD_WHITE);
1876 rb->lcd_set_background(LCD_BLACK);
1877 rb->lcd_set_backdrop(NULL);
1878 #endif
1880 rb->lcd_clear_display();
1881 rb->snprintf(print, sizeof(print), "%s:", rb->strrchr(filename,'/')+1);
1882 rb->lcd_puts(0, 0, print);
1883 rb->lcd_update();
1886 if (rb->button_get(false) == PNG_MENU) {
1887 decoder.error = PLUGIN_ABORT;
1888 rb->close(fd);
1890 } else if (image_size > memory_size) {
1891 decoder.error = FILE_TOO_LARGE;
1892 rb->close(fd);
1894 } else {
1895 if (!running_slideshow) {
1896 rb->snprintf(print, sizeof(print), "loading %lu bytes", image_size);
1897 rb->lcd_puts(0, 1, print);
1898 rb->lcd_update();
1901 image = memory_max - image_size + 1;
1902 rb->read(fd, image, image_size);
1903 rb->close(fd);
1905 if (!running_slideshow) {
1906 rb->snprintf(print, sizeof(print), "decoding image");
1907 rb->lcd_puts(0, 2, print);
1908 rb->lcd_update();
1910 #ifndef SIMULATOR
1911 else if (immediate_ata_off) {
1912 /* running slideshow and time is long enough: power down disk */
1913 rb->storage_sleep();
1915 #endif
1917 decoder.settings.color_convert = 1;
1918 decoder.infoRaw.color.colorType = 2;
1919 decoder.infoRaw.color.bitDepth = 8;
1921 if (rb->button_get(false) == PNG_MENU) {
1922 decoder.error = PLUGIN_ABORT;
1923 } else {
1924 LodePNG_inspect(&decoder, image, image_size);
1927 if (!decoder.error) {
1929 if (!running_slideshow) {
1930 rb->snprintf(print, sizeof(print), "image %dx%d", decoder.infoPng.width, decoder.infoPng.height);
1931 rb->lcd_puts(0, 2, print);
1932 rb->lcd_update();
1935 ds_max = max_downscale(&decoder); /* check display constraint */
1937 ds = ds_max; /* initials setting */
1939 if (!running_slideshow)
1941 rb->snprintf(print, sizeof(print), "decoding %d*%d",
1942 decoder.infoPng.width, decoder.infoPng.height);
1943 rb->lcd_puts(0, 3, print);
1944 rb->lcd_update();
1947 /* the actual decoding */
1948 time = *rb->current_tick;
1949 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1950 rb->cpu_boost(true);
1951 LodePNG_decode(&decoder, image, image_size, cb_progress);
1952 rb->cpu_boost(false);
1953 #else
1954 LodePNG_decode(&decoder, image, image_size, cb_progress);
1955 #endif /*HAVE_ADJUSTABLE_CPU_FREQ*/
1957 disp_buf = (fb_data *)((intptr_t)(converted_image + converted_image_size + 3) & ~3);
1958 ds_min = min_downscale(&decoder, memory_max - (unsigned char*)disp_buf); /* check memory constraint */
1960 if (ds_min == 0) {
1961 // Could not resize the image
1962 ds_min = ds = ds_max = 1;
1967 if (decoder.error == PLUGIN_ABORT || decoder.error == FILE_TOO_LARGE) {
1968 #ifndef SIMULATOR
1969 if (immediate_ata_off) {
1970 /* running slideshow and time is long enough: power down disk */
1971 rb->storage_sleep();
1973 #endif
1976 time = *rb->current_tick - time;
1978 if (!running_slideshow && !decoder.error)
1980 rb->snprintf(print, sizeof(print), " %ld.%02ld sec ", time/HZ, time%HZ);
1981 rb->lcd_getstringsize(print, &w, &h); /* centered in progress bar */
1982 rb->lcd_putsxy((LCD_WIDTH - w)/2, LCD_HEIGHT - h, print);
1983 rb->lcd_update();
1986 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
1987 if (plug_buf && (decoder.error == FILE_TOO_LARGE || decoder.error == OUT_OF_MEMORY || decoder.error == Z_MEM_ERROR))
1989 rb->lcd_setfont(FONT_SYSFIXED);
1990 rb->lcd_clear_display();
1991 rb->snprintf(print,sizeof(print),"%s:",rb->strrchr(filename,'/')+1);
1992 rb->lcd_puts(0,0,print);
1993 rb->lcd_puts(0,1,"Not enough plugin memory!");
1994 rb->lcd_puts(0,2,"Zoom In: Stop playback.");
1995 if (entries>1)
1996 rb->lcd_puts(0,3,"Left/Right: Skip File.");
1997 rb->lcd_puts(0,4,"Off: Quit.");
1998 rb->lcd_update();
1999 rb->lcd_setfont(FONT_UI);
2001 rb->button_clear_queue();
2003 while (1)
2005 int button = rb->button_get(true);
2006 switch (button)
2008 case PNG_ZOOM_IN:
2009 plug_buf = false;
2010 memory = rb->plugin_get_audio_buffer(
2011 (size_t *)&memory_size);
2012 memory_max = memory + memory_size - 1;
2013 /*try again this file, now using the audio buffer */
2014 return PLUGIN_OTHER;
2015 #ifdef PNG_RC_MENU
2016 case PNG_RC_MENU:
2017 #endif
2018 case PNG_MENU:
2019 return PLUGIN_OK;
2021 case PNG_LEFT:
2022 if (entries>1)
2024 rb->lcd_clear_display();
2025 return change_filename(DIR_PREV);
2027 break;
2029 case PNG_RIGHT:
2030 if (entries>1)
2032 rb->lcd_clear_display();
2033 return change_filename(DIR_NEXT);
2035 break;
2036 default:
2037 if (rb->default_event_handler_ex(button, cleanup, NULL)
2038 == SYS_USB_CONNECTED)
2039 return PLUGIN_USB_CONNECTED;
2044 //else
2045 #endif
2047 if (decoder.error) {
2049 switch (decoder.error) {
2050 case PLUGIN_ABORT:
2051 rb->splash(HZ, "aborted");break;
2052 case 27:
2053 rb->splash(HZ, "png file smaller than a png header");break;
2054 case 28:
2055 rb->splash(HZ, "incorrect png signature");break;
2056 case 29:
2057 rb->splash(HZ, "first chunk is not IHDR");break;
2058 case 30:
2059 rb->splash(HZ, "chunk length too large");break;
2060 case 31:
2061 rb->splash(HZ, "illegal PNG color type or bpp");break;
2062 case 32:
2063 rb->splash(HZ, "illegal PNG compression method");break;
2064 case 33:
2065 rb->splash(HZ, "illegal PNG filter method");break;
2066 case 34:
2067 rb->splash(HZ, "illegal PNG interlace method");break;
2068 case 35:
2069 rb->splash(HZ, "chunk length of a chunk is too large or the chunk too small");break;
2070 case 36:
2071 rb->splash(HZ, "illegal PNG filter type encountered");break;
2072 case 37:
2073 rb->splash(HZ, "illegal bit depth for this color type given");break;
2074 case 38:
2075 rb->splash(HZ, "the palette is too big (more than 256 colors)");break;
2076 case 39:
2077 rb->splash(HZ, "more palette alpha values given in tRNS, than there are colors in the palette");break;
2078 case 40:
2079 rb->splash(HZ, "tRNS chunk has wrong size for greyscale image");break;
2080 case 41:
2081 rb->splash(HZ, "tRNS chunk has wrong size for RGB image");break;
2082 case 42:
2083 rb->splash(HZ, "tRNS chunk appeared while it was not allowed for this color type");break;
2084 case 43:
2085 rb->splash(HZ, "bKGD chunk has wrong size for palette image");break;
2086 case 44:
2087 rb->splash(HZ, "bKGD chunk has wrong size for greyscale image");break;
2088 case 45:
2089 rb->splash(HZ, "bKGD chunk has wrong size for RGB image");break;
2090 case 46:
2091 case 47:
2092 rb->splash(HZ, "value encountered in indexed image is larger than the palette size");break;
2093 case 48:
2094 rb->splash(HZ, "input file is empty");break;
2095 case OUT_OF_MEMORY:
2096 case Z_MEM_ERROR:
2097 rb->splash(HZ, "Out of Memory");break;
2098 case 57:
2099 rb->splash(HZ, "invalid CRC");break;
2100 case 59:
2101 rb->splash(HZ, "conversion to unexisting or unsupported color type or bit depth");break;
2102 case 63:
2103 rb->splash(HZ, "png chunk too long");break;
2104 case 69:
2105 rb->splash(HZ, "unknown critical chunk");break;
2106 case 73:
2107 rb->splash(HZ, "invalid tIME chunk size");break;
2108 case 74:
2109 rb->splash(HZ, "invalid pHYs chunk size");break;
2110 case FILE_TOO_LARGE:
2111 rb->splash(HZ, "File too large");break;
2112 case Z_DATA_ERROR:
2113 rb->splash(HZ, decoder.error_msg);break;
2114 default:
2115 rb->splashf(HZ, "other error : %ld", decoder.error);break;
2118 if (decoder.error == PLUGIN_ABORT) {
2119 return PLUGIN_OK;
2120 } else if (decoder.error == OUT_OF_MEMORY && entries == 1) {
2121 return PLUGIN_ERROR;
2122 } else {
2123 file_pt[curfile] = NULL;
2124 return change_filename(direction);
2128 do {
2129 resized_image = get_image(&decoder); /* decode or fetch from cache */
2131 cx = decoder.infoPng.width/ds/2; /* center the view */
2132 cy = decoder.infoPng.height/ds/2;
2134 set_view(&decoder, cx, cy);
2136 if (!running_slideshow)
2138 rb->snprintf(print, sizeof(print), "showing %dx%d",
2139 decoder.infoPng.width/ds, decoder.infoPng.height/ds);
2140 rb->lcd_puts(0, 3, print);
2141 rb->lcd_update();
2144 rb->lcd_clear_display();
2145 draw_image(&decoder);
2146 rb->lcd_update();
2148 /* drawing is now finished, play around with scrolling
2149 * until you press OFF or connect USB
2151 while (1)
2153 status = scroll_bmp(&decoder);
2154 if (status == ZOOM_IN)
2156 if (ds > ds_min)
2158 while (1)
2160 ds /= 2; /* reduce downscaling to zoom in */
2161 get_view(&decoder, &cx, &cy);
2162 cx *= 2; /* prepare the position in the new image */
2163 cy *= 2;
2164 if (disp[ds] != converted_image || ds <= ds_min) break;
2167 else
2168 continue;
2171 if (status == ZOOM_OUT)
2173 if (ds < ds_max)
2175 while (1)
2177 ds *= 2; /* increase downscaling to zoom out */
2178 get_view(&decoder, &cx, &cy);
2179 cx /= 2; /* prepare the position in the new image */
2180 cy /= 2;
2181 if (disp[ds] != converted_image || ds >= ds_max) break;
2184 else
2185 continue;
2187 break;
2189 rb->lcd_clear_display();
2191 while (status != PLUGIN_OK && status != PLUGIN_USB_CONNECTED
2192 && status != PLUGIN_OTHER);
2194 return status;
2197 /******************** Plugin entry point *********************/
2199 enum plugin_status plugin_start(const void* parameter)
2201 int condition;
2202 #if LCD_DEPTH > 1
2203 old_backdrop = rb->lcd_get_backdrop();
2204 #endif
2206 if (!parameter) return PLUGIN_ERROR;
2208 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
2209 memory = rb->plugin_get_buffer((size_t *)&memory_size);
2210 #else
2211 memory = rb->plugin_get_audio_buffer((size_t *)&memory_size);
2212 #endif
2214 rb->strcpy(np_file, parameter);
2215 get_pic_list();
2217 if (!entries) return PLUGIN_ERROR;
2219 #if (PLUGIN_BUFFER_SIZE >= MIN_MEM) && !defined(SIMULATOR)
2220 if (rb->audio_status()) {
2221 plug_buf = true;
2222 } else {
2223 memory = (unsigned char *)rb->plugin_get_audio_buffer((size_t *)&memory_size);
2225 #endif
2227 memory_max = memory + memory_size - 1;
2229 /* should be ok to just load settings since the plugin itself has
2230 just been loaded from disk and the drive should be spinning */
2231 configfile_load(PNG_CONFIGFILE, png_config,
2232 ARRAYLEN(png_config), PNG_SETTINGS_MINVERSION);
2233 old_settings = png_settings;
2235 /* Turn off backlight timeout */
2236 backlight_force_on(); /* backlight control in lib/helper.c */
2240 condition = load_and_show(np_file);
2241 }while (condition != PLUGIN_OK && condition != PLUGIN_USB_CONNECTED
2242 && condition != PLUGIN_ERROR);
2244 if (rb->memcmp(&png_settings, &old_settings, sizeof (png_settings)))
2246 /* Just in case drive has to spin, keep it from looking locked */
2247 rb->splash(0, "Saving Settings");
2248 configfile_save(PNG_CONFIGFILE, png_config,
2249 ARRAYLEN(png_config), PNG_SETTINGS_VERSION);
2252 #if !defined(SIMULATOR) && defined(HAVE_DISK_STORAGE)
2253 /* set back ata spindown time in case we changed it */
2254 rb->storage_spindown(rb->global_settings->disk_spindown);
2255 #endif
2257 /* Turn on backlight timeout (revert to settings) */
2258 backlight_use_settings(); /* backlight control in lib/helper.c */
2260 return condition;