1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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
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.
57 #include <lib/playback_control.h>
58 #include <lib/helper.h>
59 #include <lib/configfile.h>
61 #include <lib/pluginlib_bmp.h>
67 /* ////////////////////////////////////////////////////////////////////////// */
68 /* LodeFlate & LodeZlib Setting structs */
69 /* ////////////////////////////////////////////////////////////////////////// */
71 typedef struct LodePNG_InfoColor
/*info about the color type of an image*/
74 unsigned colorType
; /*color type*/
75 unsigned bitDepth
; /*bits per sample*/
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*/
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)*/
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*/
118 /*phys chunk (pHYs)*/
119 unsigned phys_defined
; /*is pHYs chunk defined?*/
122 unsigned char phys_unit
; /*may be 0 (unknown unit) or 1 (metre)*/
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
;
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*/
146 #define VERSION_STRING "20080927"
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
;
169 static fb_data
*converted_image
__attribute__ ((aligned (16))); /* the (color) converted image */
171 static fb_data
*converted_image
; /* the (color) converted image */
173 static size_t converted_image_size
;
175 static unsigned char *decoded_image
; /* the decoded image */
176 static size_t decoded_image_size
;
179 static fb_data
*resized_image
__attribute__ ((aligned (16))); /* the decoded image */
181 static fb_data
*resized_image
; /* the decoded image */
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
212 static struct png_settings png_settings
=
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
226 static fb_data
* old_backdrop
;
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 */
243 static int immediate_ata_off
= false; /* power down disk after loading */
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
)
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
) {
282 if (err
== Z_NEED_DICT
|| (err
== Z_BUF_ERROR
&& stream
.avail_in
== 0))
286 *outsize
= stream
.total_out
;
288 err
= inflateEnd(&stream
);
289 error_msg
= stream
.msg
;
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);
305 static unsigned readBitsFromReversedStream(size_t* bitpointer
, const unsigned char* bitstream
, size_t nbits
)
309 for (i
= nbits
- 1; i
< nbits
; i
--) result
+= (unsigned)readBitFromReversedStream(bitpointer
, bitstream
) << i
;
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*/
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)));
328 static unsigned LodePNG_read32bitInt(const unsigned char* buffer
)
330 return (buffer
[0] << 24) | (buffer
[1] << 16) | (buffer
[2] << 8) | buffer
[3];
333 /* ////////////////////////////////////////////////////////////////////////// */
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*/
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*/
376 const unsigned char* LodePNG_chunk_data_const(const unsigned char* chunk
) /*get pointer to the data of the chunk*/
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;
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*/
412 if (!(bd
== 1 || bd
== 2 || bd
== 4 || bd
== 8 || bd
== 16)) return 37; break; /*grey*/
414 if (!( bd
== 8 || bd
== 16)) return 37; break; /*RGB*/
416 if (!(bd
== 1 || bd
== 2 || bd
== 4 || bd
== 8 )) return 37; break; /*palette*/
418 if (!( bd
== 8 || bd
== 16)) return 37; break; /*grey + alpha*/
420 if (!( bd
== 8 || bd
== 16)) return 37; break; /*RGBA*/
424 return 0; /*allowed color type / bits combination*/
427 static unsigned getNumColorChannels(unsigned colorType
)
436 return 1; /*palette*/
438 return 2; /*grey + alpha*/
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;
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
)
498 LodePNG_InfoColor_cleanup(dest
);
500 for (i
= 0; i
< source
->palettesize
* 4; i
++) dest
->palette
[i
] = source
->palette
[i
];
504 unsigned LodePNG_InfoPng_copy(LodePNG_InfoPng
* dest
, const LodePNG_InfoPng
* source
)
507 LodePNG_InfoPng_cleanup(dest
);
509 LodePNG_InfoColor_init(&dest
->color
);
510 error
= LodePNG_InfoColor_copy(&dest
->color
, &source
->color
); if (error
) return error
;
514 void LodePNG_InfoPng_swap(LodePNG_InfoPng
* a
, LodePNG_InfoPng
* b
)
516 LodePNG_InfoPng temp
= *a
;
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
)
534 LodePNG_InfoRaw_cleanup(dest
);
536 LodePNG_InfoColor_init(&dest
->color
);
537 error
= LodePNG_InfoColor_copy(&dest
->color
, &source
->color
); if (error
) 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*/
554 if (!running_slideshow
)
556 rb
->snprintf(print
, sizeof(print
), "color conversion in progress");
557 rb
->lcd_puts(0, 3, print
);
561 /*cases where in and out already have the same format*/
562 if (LodePNG_InfoColor_equal(infoIn
, infoOut
))
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
);
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*/
586 for (y
= 0 ; y
< h
; y
++) {
587 for (x
= 0 ; x
< w
; x
++) {
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
);
596 case 2: /*RGB color*/
598 for (y
= 0 ; y
< h
; y
++) {
599 for (x
= 0 ; x
< w
; x
++) {
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
);
608 case 3: /*indexed color (palette)*/
610 for (y
= 0 ; y
< h
; y
++) {
611 for (x
= 0 ; x
< w
; x
++) {
612 if (in
[i
] >= infoIn
->palettesize
) return 46;
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
);
621 case 4: /*greyscale with alpha*/
623 for (y
= 0 ; y
< h
; y
++) {
624 for (x
= 0 ; x
< w
; x
++) {
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
);
633 case 6: /*RGB with alpha*/
635 for (y
= 0 ; y
< h
; y
++) {
636 for (x
= 0 ; x
< w
; x
++) {
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
);
649 else if (infoIn
->bitDepth
== 16)
651 switch (infoIn
->colorType
)
653 case 0: /*greyscale color*/
655 for (y
= 0 ; y
< h
; y
++) {
656 for (x
= 0 ; x
< w
; x
++) {
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
);
665 case 2: /*RGB color*/
667 for (y
= 0 ; y
< h
; y
++) {
668 for (x
= 0 ; x
< w
; x
++) {
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
);
677 case 4: /*greyscale with alpha*/
679 for (y
= 0 ; y
< h
; y
++) {
680 for (x
= 0 ; x
< w
; x
++) {
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
);
689 case 6: /*RGB with alpha*/
691 for (y
= 0 ; y
< h
; y
++) {
692 for (x
= 0 ; x
< w
; x
++) {
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
);
705 else /*infoIn->bitDepth is less than 8 bit per channel*/
707 switch (infoIn
->colorType
)
709 case 0: /*greyscale color*/
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
);
722 case 3: /*indexed color (palette)*/
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;
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
);
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*/
750 for (y
= 0 ; y
< h
; y
++) {
751 for (x
= 0 ; x
< w
; x
++) {
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
);
760 case 4: /*greyscale with alpha*/
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
);
776 else if (infoIn
->bitDepth
== 16)
778 switch (infoIn
->colorType
)
780 case 0: /*greyscale color*/
782 for (y
= 0 ; y
< h
; y
++) {
783 for (x
= 0 ; x
< w
; x
++) {
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
);
792 case 4: /*greyscale with alpha*/
794 for (y
= 0 ; y
< h
; y
++) {
795 for (x
= 0 ; x
< w
; x
++) {
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
);
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*/
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
);
829 /*Paeth predicter, used by PNG filter type 4*/
830 static int paethPredictor(int a
, int b
, int 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
;
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*/
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
);
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.
924 //for(i = 0; i < length; i++) recon[i] = scanline[i];
925 memcpy(recon
, scanline
, length
* sizeof(unsigned char));
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
];
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));
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);
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;
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
]));
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));
964 return 36; /*error: unexisting filter type given*/
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!
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
];
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];
1007 Adam7_getpassvalues(passw
, passh
, filter_passstart
, padded_passstart
, passstart
, w
, h
, bpp
);
1011 for (i
= 0; i
< 7; i
++)
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
++)
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
1059 size_t diff
= ilinebits
- olinebits
;
1060 size_t obp
= 0, ibp
= 0; /*bit pointers*/
1061 for (y
= 0; y
< h
; y
++)
1064 for (x
= 0; x
< olinebits
; x
++)
1066 unsigned char bit
= readBitFromReversedStream(&ibp
, in
);
1067 setBitOfReversedStream(&obp
, out
, bit
);
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
;
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];
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
);
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
;
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));
1168 else if (LodePNG_chunk_type_equals(chunk
, "IEND"))
1172 /*palette chunk (PLTE)*/
1173 else if (LodePNG_chunk_type_equals(chunk
, "PLTE"))
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*/
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)*/
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
);
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
);
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
[])
1339 if (!rb
->strcasecmp(ext
,".png"))
1345 /*Read directory contents for scrolling. */
1346 void get_pic_list(void)
1349 long int str_len
= 0;
1351 tree
= rb
->tree_get_context();
1353 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
1354 file_pt
= rb
->plugin_get_buffer((size_t *)&image_size
);
1356 file_pt
= rb
->plugin_get_audio_buffer((size_t *)&image_size
);
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
,'/');
1373 /* Find Selected File. */
1374 for (i
= 0; i
< entries
; i
++)
1375 if (!rb
->strcmp(file_pt
[i
], pname
))
1379 int change_filename(int direct
)
1384 if (direct
== DIR_PREV
)
1390 curfile
= entries
- 1;
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 */
1402 if (curfile
== entries
- 1)
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
, "/");
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
)
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 */
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
);
1447 rb
->lcd_set_foreground(LCD_BLACK
);
1448 rb
->lcd_set_background(LCD_WHITE
);
1456 MIID_TOGGLE_SS_MODE
,
1457 MIID_CHANGE_SS_MODE
,
1458 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
1459 MIID_SHOW_PLAYBACK_MENU
,
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",
1472 static const struct opt_items slideshow
[2] = {
1477 result
=rb
->do_menu(&menu
, NULL
, NULL
, false);
1483 case MIID_TOGGLE_SS_MODE
:
1484 rb
->set_option("Toggle Slideshow", &slideshow_enabled
, INT
,
1485 slideshow
, 2, NULL
);
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
);
1492 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
1493 case MIID_SHOW_PLAYBACK_MENU
:
1496 playback_control(NULL
);
1500 rb
->splash(HZ
, "Cannot restart playback");
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;
1529 rb
->lcd_set_backdrop(NULL
);
1530 rb
->lcd_set_foreground(LCD_WHITE
);
1531 rb
->lcd_set_background(LCD_BLACK
);
1533 rb
->lcd_clear_display();
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
)
1552 move
= MIN(HSCROLL
, decoder
->infoPng
.width
/ds
- decoder
->x
- LCD_WIDTH
);
1556 draw_image(decoder
);
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
)
1567 move
= MIN(HSCROLL
, decoder
->x
);
1571 draw_image(decoder
);
1577 /* Pan the viewing window up - move image down and fill in
1579 static void pan_view_up(struct LodePNG_Decoder
* decoder
)
1583 move
= MIN(VSCROLL
, decoder
->y
);
1587 draw_image(decoder
);
1592 /* Pan the viewing window down - move image up and fill in
1594 static void pan_view_down(struct LodePNG_Decoder
* decoder
)
1598 move
= MIN(VSCROLL
, decoder
->infoPng
.height
/ds
- decoder
->y
- LCD_HEIGHT
);
1602 draw_image(decoder
);
1607 /* interactively scroll around the image */
1608 int scroll_bmp(struct LodePNG_Decoder
* decoder
)
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;
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
);
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
);
1638 case PNG_UP
| BUTTON_REPEAT
:
1639 pan_view_up(decoder
);
1643 case PNG_DOWN
| BUTTON_REPEAT
:
1644 pan_view_down(decoder
);
1648 if (!slideshow_enabled
)
1650 running_slideshow
= true;
1652 return change_filename(DIR_NEXT
);
1655 #ifdef PNG_SLIDE_SHOW
1656 case PNG_SLIDE_SHOW
:
1657 slideshow_enabled
= !slideshow_enabled
;
1658 running_slideshow
= slideshow_enabled
;
1662 #ifdef PNG_NEXT_REPEAT
1663 case PNG_NEXT_REPEAT
:
1667 return change_filename(DIR_NEXT
);
1670 #ifdef PNG_PREVIOUS_REPEAT
1671 case PNG_PREVIOUS_REPEAT
:
1675 return change_filename(DIR_PREV
);
1680 if (lastbutton
!= PNG_ZOOM_PRE
)
1688 if (lastbutton
!= PNG_ZOOM_PRE
)
1698 if (show_menu() == 1)
1701 return PLUGIN_REFRESH
;
1705 if (rb
->default_event_handler_ex(button
, cleanup
, NULL
)
1706 == SYS_USB_CONNECTED
)
1707 return PLUGIN_USB_CONNECTED
;
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
)
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 */
1734 decoder
->x
= x
; /* set the values */
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);
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
)
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
)
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
))
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 */
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 */
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
);
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
;
1835 return converted_image
;
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);
1849 smooth_resize_bitmap(&bmp_src
, &bmp_dst
);
1850 #endif /*HAVE_ADJUSTABLE_CPU_FREQ*/
1853 disp
[ds
] = converted_image
;
1854 return converted_image
;
1857 previous_disp
= disp
[ds
];
1858 previous_size
= size
[ds
];
1865 /* load, decode, display the image */
1866 int load_and_show(char* filename
)
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
);
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
;
1890 DEBUGF("reading file '%s'\n", filename
);
1892 if (!running_slideshow
) {
1894 rb
->lcd_set_foreground(LCD_WHITE
);
1895 rb
->lcd_set_background(LCD_BLACK
);
1896 rb
->lcd_set_backdrop(NULL
);
1899 rb
->lcd_clear_display();
1900 rb
->snprintf(print
, sizeof(print
), "%s:", rb
->strrchr(filename
,'/')+1);
1901 rb
->lcd_puts(0, 0, print
);
1905 if (rb
->button_get(false) == PNG_MENU
) {
1906 decoder
.error
= PLUGIN_ABORT
;
1909 } else if (image_size
> memory_size
) {
1910 decoder
.error
= FILE_TOO_LARGE
;
1914 if (!running_slideshow
) {
1915 rb
->snprintf(print
, sizeof(print
), "loading %lu bytes", image_size
);
1916 rb
->lcd_puts(0, 1, print
);
1920 image
= memory_max
- image_size
+ 1;
1921 rb
->read(fd
, image
, image_size
);
1924 if (!running_slideshow
) {
1925 rb
->snprintf(print
, sizeof(print
), "decoding image");
1926 rb
->lcd_puts(0, 2, print
);
1930 else if (immediate_ata_off
) {
1931 /* running slideshow and time is long enough: power down disk */
1932 rb
->storage_sleep();
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
;
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
);
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
);
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);
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 */
1979 // Could not resize the image
1980 ds_min
= ds
= ds_max
= 1;
1985 if (decoder
.error
== PLUGIN_ABORT
|| decoder
.error
== FILE_TOO_LARGE
) {
1988 if (immediate_ata_off
) {
1989 /* running slideshow and time is long enough: power down disk */
1990 rb
->storage_sleep();
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
);
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.");
2015 rb
->lcd_puts(0,3,"Left/Right: Skip File.");
2016 rb
->lcd_puts(0,4,"Off: Quit.");
2018 rb
->lcd_setfont(FONT_UI
);
2020 rb
->button_clear_queue();
2024 int button
= rb
->button_get(true);
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
;
2045 rb
->lcd_clear_display();
2046 return change_filename(DIR_PREV
);
2053 rb
->lcd_clear_display();
2054 return change_filename(DIR_NEXT
);
2058 if (rb
->default_event_handler_ex(button
, cleanup
, NULL
)
2059 == SYS_USB_CONNECTED
)
2060 return PLUGIN_USB_CONNECTED
;
2068 if (decoder
.error
) {
2070 switch (decoder
.error
) {
2072 rb
->splash(HZ
, "aborted");break;
2074 rb
->splash(HZ
, "png file smaller than a png header");break;
2076 rb
->splash(HZ
, "incorrect png signature");break;
2078 rb
->splash(HZ
, "first chunk is not IHDR");break;
2080 rb
->splash(HZ
, "chunk length too large");break;
2082 rb
->splash(HZ
, "illegal PNG color type or bpp");break;
2084 rb
->splash(HZ
, "illegal PNG compression method");break;
2086 rb
->splash(HZ
, "illegal PNG filter method");break;
2088 rb
->splash(HZ
, "illegal PNG interlace method");break;
2090 rb
->splash(HZ
, "chunk length of a chunk is too large or the chunk too small");break;
2092 rb
->splash(HZ
, "illegal PNG filter type encountered");break;
2094 rb
->splash(HZ
, "illegal bit depth for this color type given");break;
2096 rb
->splash(HZ
, "the palette is too big (more than 256 colors)");break;
2098 rb
->splash(HZ
, "more palette alpha values given in tRNS, than there are colors in the palette");break;
2100 rb
->splash(HZ
, "tRNS chunk has wrong size for greyscale image");break;
2102 rb
->splash(HZ
, "tRNS chunk has wrong size for RGB image");break;
2104 rb
->splash(HZ
, "tRNS chunk appeared while it was not allowed for this color type");break;
2106 rb
->splash(HZ
, "bKGD chunk has wrong size for palette image");break;
2108 rb
->splash(HZ
, "bKGD chunk has wrong size for greyscale image");break;
2110 rb
->splash(HZ
, "bKGD chunk has wrong size for RGB image");break;
2113 rb
->splash(HZ
, "value encountered in indexed image is larger than the palette size");break;
2115 rb
->splash(HZ
, "input file is empty");break;
2118 rb
->splash(HZ
, "Out of Memory");break;
2120 rb
->splash(HZ
, "invalid CRC");break;
2122 rb
->splash(HZ
, "conversion to unexisting or unsupported color type or bit depth");break;
2124 rb
->splash(HZ
, "png chunk too long");break;
2126 rb
->splash(HZ
, "unknown critical chunk");break;
2128 rb
->splash(HZ
, "invalid tIME chunk size");break;
2130 rb
->splash(HZ
, "invalid pHYs chunk size");break;
2131 case FILE_TOO_LARGE
:
2132 rb
->splash(HZ
, "File too large");break;
2134 rb
->splash(HZ
, decoder
.error_msg
);break;
2136 rb
->splashf(HZ
, "other error : %ld", decoder
.error
);break;
2139 if (decoder
.error
== PLUGIN_ABORT
) {
2141 } else if (decoder
.error
== OUT_OF_MEMORY
&& entries
== 1) {
2142 return PLUGIN_ERROR
;
2144 file_pt
[curfile
] = '\0';
2145 return change_filename(direction
);
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
);
2165 rb
->lcd_clear_display();
2166 draw_image(&decoder
);
2169 /* drawing is now finished, play around with scrolling
2170 * until you press OFF or connect USB
2174 status
= scroll_bmp(&decoder
);
2175 if (status
== ZOOM_IN
)
2181 ds
/= 2; /* reduce downscaling to zoom in */
2182 get_view(&decoder
, &cx
, &cy
);
2183 cx
*= 2; /* prepare the position in the new image */
2185 if (disp
[ds
] != converted_image
|| ds
<= ds_min
) break;
2192 if (status
== ZOOM_OUT
)
2198 ds
*= 2; /* increase downscaling to zoom out */
2199 get_view(&decoder
, &cx
, &cy
);
2200 cx
/= 2; /* prepare the position in the new image */
2202 if (disp
[ds
] != converted_image
|| ds
>= ds_max
) break;
2210 rb
->lcd_clear_display();
2212 while (status
!= PLUGIN_OK
&& status
!= PLUGIN_USB_CONNECTED
2213 && status
!= PLUGIN_OTHER
);
2218 /******************** Plugin entry point *********************/
2220 enum plugin_status
plugin_start(const void* parameter
)
2224 old_backdrop
= rb
->lcd_get_backdrop();
2227 if (!parameter
) return PLUGIN_ERROR
;
2229 rb
->strcpy(np_file
, parameter
);
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
);
2239 memory
= (unsigned char *)rb
->plugin_get_audio_buffer((size_t *)&memory_size
);
2242 memory
= (unsigned char *)rb
->plugin_get_audio_buffer((size_t *)&memory_size
);
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
);
2277 /* Turn on backlight timeout (revert to settings) */
2278 backlight_use_settings(); /* backlight control in lib/helper.c */