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 /* 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
;
168 static fb_data
*converted_image
__attribute__ ((aligned (16))); /* the (color) converted image */
170 static fb_data
*converted_image
; /* the (color) converted image */
172 static size_t converted_image_size
;
174 static unsigned char *decoded_image
; /* the decoded image */
175 static size_t decoded_image_size
;
178 static fb_data
*resized_image
__attribute__ ((aligned (16))); /* the decoded image */
180 static fb_data
*resized_image
; /* the decoded image */
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
211 static struct png_settings png_settings
=
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
225 static fb_data
* old_backdrop
;
228 /* Min memory allowing us to use the plugin buffer
229 * and thus not stopping the music
230 * *Very* rough estimation:
231 * Max 10 000 dir entries * 4bytes/entry (char **) = 40000 bytes
232 * + 30k code size = 70 000
233 * + 50k min for png = 130 000
235 #define MIN_MEM 130000
237 static int slideshow_enabled
= false; /* run slideshow */
238 static int running_slideshow
= false; /* loading image because of slideshw */
240 static int immediate_ata_off
= false; /* power down disk after loading */
243 static unsigned ds
, ds_min
, ds_max
; /* downscaling and limits */
246 The two functions below (LodePNG_decompress and LodePNG_compress) directly call the
247 LodeZlib_decompress and LodeZlib_compress functions. The only purpose of the functions
248 below, is to provide the ability to let LodePNG use a different Zlib encoder by only
249 changing the two functions below, instead of changing it inside the vareous places
250 in the other LodePNG functions.
252 *out must be NULL and *outsize must be 0 initially, and after the function is done,
253 *out must point to the decompressed data, *outsize must be the size of it, and must
254 be the size of the useful data in bytes, not the alloc size.
257 static unsigned LodePNG_decompress(unsigned char* out
, size_t* outsize
, const unsigned char* in
, size_t insize
, char *error_msg
)
264 stream
.next_in
= (Bytef
*)in
;
265 stream
.avail_in
= (uInt
)insize
;
267 stream
.next_out
= out
;
268 stream
.avail_out
= (uInt
)*outsize
;
270 stream
.zalloc
= (alloc_func
)0;
271 stream
.zfree
= (free_func
)0;
273 err
= inflateInit(&stream
);
274 if (err
!= Z_OK
) return err
;
276 err
= inflate(&stream
, Z_FINISH
);
277 if (err
!= Z_STREAM_END
) {
279 if (err
== Z_NEED_DICT
|| (err
== Z_BUF_ERROR
&& stream
.avail_in
== 0))
283 *outsize
= stream
.total_out
;
285 err
= inflateEnd(&stream
);
286 error_msg
= stream
.msg
;
291 /* ////////////////////////////////////////////////////////////////////////// */
292 /* / Reading and writing single bits and bytes from/to stream for LodePNG / */
293 /* ////////////////////////////////////////////////////////////////////////// */
295 static unsigned char readBitFromReversedStream(size_t* bitpointer
, const unsigned char* bitstream
)
297 unsigned char result
= (unsigned char)((bitstream
[(*bitpointer
) >> 3] >> (7 - ((*bitpointer
) & 0x7))) & 1);
302 static unsigned readBitsFromReversedStream(size_t* bitpointer
, const unsigned char* bitstream
, size_t nbits
)
306 for (i
= nbits
- 1; i
< nbits
; i
--) result
+= (unsigned)readBitFromReversedStream(bitpointer
, bitstream
) << i
;
310 static void setBitOfReversedStream0(size_t* bitpointer
, unsigned char* bitstream
, unsigned char bit
)
312 /*the current bit in bitstream must be 0 for this to work*/
313 if (bit
) bitstream
[(*bitpointer
) >> 3] |= (bit
<< (7 - ((*bitpointer
) & 0x7))); /*earlier bit of huffman code is in a lesser significant bit of an earlier byte*/
317 static void setBitOfReversedStream(size_t* bitpointer
, unsigned char* bitstream
, unsigned char bit
)
319 /*the current bit in bitstream may be 0 or 1 for this to work*/
320 if (bit
== 0) bitstream
[(*bitpointer
) >> 3] &= (unsigned char)(~(1 << (7 - ((*bitpointer
) & 0x7))));
321 else bitstream
[(*bitpointer
) >> 3] |= (1 << (7 - ((*bitpointer
) & 0x7)));
325 static unsigned LodePNG_read32bitInt(const unsigned char* buffer
)
327 return (buffer
[0] << 24) | (buffer
[1] << 16) | (buffer
[2] << 8) | buffer
[3];
330 /* ////////////////////////////////////////////////////////////////////////// */
332 /* ////////////////////////////////////////////////////////////////////////// */
334 unsigned LodePNG_chunk_length(const unsigned char* chunk
) /*get the length of the data of the chunk. Total chunk length has 12 bytes more.*/
336 return LodePNG_read32bitInt(&chunk
[0]);
339 void LodePNG_chunk_type(char type
[5], const unsigned char* chunk
) /*puts the 4-byte type in null terminated string*/
342 for (i
= 0; i
< 4; i
++) type
[i
] = chunk
[4 + i
];
343 type
[4] = 0; /*null termination char*/
346 unsigned char LodePNG_chunk_type_equals(const unsigned char* chunk
, const char* type
) /*check if the type is the given type*/
348 if (type
[4] != 0) return 0;
349 return (chunk
[4] == type
[0] && chunk
[5] == type
[1] && chunk
[6] == type
[2] && chunk
[7] == type
[3]);
352 /*properties of PNG chunks gotten from capitalization of chunk type name, as defined by the standard*/
353 unsigned char LodePNG_chunk_critical(const unsigned char* chunk
) /*0: ancillary chunk, 1: it's one of the critical chunk types*/
355 return((chunk
[4] & 32) == 0);
358 unsigned char LodePNG_chunk_private(const unsigned char* chunk
) /*0: public, 1: private*/
360 return((chunk
[6] & 32) != 0);
363 unsigned char LodePNG_chunk_safetocopy(const unsigned char* chunk
) /*0: the chunk is unsafe to copy, 1: the chunk is safe to copy*/
365 return((chunk
[7] & 32) != 0);
368 unsigned char* LodePNG_chunk_data(unsigned char* chunk
) /*get pointer to the data of the chunk*/
373 const unsigned char* LodePNG_chunk_data_const(const unsigned char* chunk
) /*get pointer to the data of the chunk*/
378 unsigned LodePNG_chunk_check_crc(const unsigned char* chunk
) /*returns 0 if the crc is correct, error code if it's incorrect*/
380 unsigned length
= LodePNG_chunk_length(chunk
);
381 unsigned CRC
= LodePNG_read32bitInt(&chunk
[length
+ 8]);
382 unsigned checksum
= crc32(0L, &chunk
[4], length
+ 4); /*the CRC is taken of the data and the 4 chunk type letters, not the length*/
383 if (CRC
!= checksum
) return 1;
387 unsigned char* LodePNG_chunk_next(unsigned char* chunk
) /*don't use on IEND chunk, as there is no next chunk then*/
389 unsigned total_chunk_length
= LodePNG_chunk_length(chunk
) + 12;
390 return &chunk
[total_chunk_length
];
393 const unsigned char* LodePNG_chunk_next_const(const unsigned char* chunk
) /*don't use on IEND chunk, as there is no next chunk then*/
395 unsigned total_chunk_length
= LodePNG_chunk_length(chunk
) + 12;
396 return &chunk
[total_chunk_length
];
399 /* ////////////////////////////////////////////////////////////////////////// */
400 /* / Color types and such / */
401 /* ////////////////////////////////////////////////////////////////////////// */
403 /*return type is a LodePNG error code*/
404 static unsigned checkColorValidity(unsigned colorType
, unsigned bd
) /*bd = bitDepth*/
409 if (!(bd
== 1 || bd
== 2 || bd
== 4 || bd
== 8 || bd
== 16)) return 37; break; /*grey*/
411 if (!( bd
== 8 || bd
== 16)) return 37; break; /*RGB*/
413 if (!(bd
== 1 || bd
== 2 || bd
== 4 || bd
== 8 )) return 37; break; /*palette*/
415 if (!( bd
== 8 || bd
== 16)) return 37; break; /*grey + alpha*/
417 if (!( bd
== 8 || bd
== 16)) return 37; break; /*RGBA*/
421 return 0; /*allowed color type / bits combination*/
424 static unsigned getNumColorChannels(unsigned colorType
)
433 return 1; /*palette*/
435 return 2; /*grey + alpha*/
439 return 0; /*unexisting color type*/
442 static unsigned getBpp(unsigned colorType
, unsigned bitDepth
)
444 return getNumColorChannels(colorType
) * bitDepth
; /*bits per pixel is amount of channels * bits per channel*/
447 /* ////////////////////////////////////////////////////////////////////////// */
449 void LodePNG_InfoColor_init(LodePNG_InfoColor
* info
)
451 info
->key_defined
= 0;
452 info
->key_r
= info
->key_g
= info
->key_b
= 0;
455 memset(info
->palette
, 0, 256 * 4 * sizeof(unsigned char));
456 info
->palettesize
= 0;
459 void LodePNG_InfoColor_cleanup(LodePNG_InfoColor
* info
)
461 info
->palettesize
= 0;
464 unsigned LodePNG_InfoColor_getBpp(const LodePNG_InfoColor
* info
) { return getBpp(info
->colorType
, info
->bitDepth
); } /*calculate bits per pixel out of colorType and bitDepth*/
465 unsigned LodePNG_InfoColor_isGreyscaleType(const LodePNG_InfoColor
* info
) { return info
->colorType
== 0 || info
->colorType
== 4; }
467 unsigned LodePNG_InfoColor_equal(const LodePNG_InfoColor
* info1
, const LodePNG_InfoColor
* info2
)
469 return info1
->colorType
== info2
->colorType
470 && info1
->bitDepth
== info2
->bitDepth
; /*palette and color key not compared*/
473 void LodePNG_InfoPng_init(LodePNG_InfoPng
* info
)
475 info
->width
= info
->height
= 0;
476 LodePNG_InfoColor_init(&info
->color
);
477 info
->interlaceMethod
= 0;
478 info
->compressionMethod
= 0;
479 info
->filterMethod
= 0;
480 info
->background_defined
= 0;
481 info
->background_r
= info
->background_g
= info
->background_b
= 0;
483 info
->time_defined
= 0;
484 info
->phys_defined
= 0;
487 void LodePNG_InfoPng_cleanup(LodePNG_InfoPng
* info
)
489 LodePNG_InfoColor_cleanup(&info
->color
);
492 unsigned LodePNG_InfoColor_copy(LodePNG_InfoColor
* dest
, const LodePNG_InfoColor
* source
)
495 LodePNG_InfoColor_cleanup(dest
);
497 for (i
= 0; i
< source
->palettesize
* 4; i
++) dest
->palette
[i
] = source
->palette
[i
];
501 unsigned LodePNG_InfoPng_copy(LodePNG_InfoPng
* dest
, const LodePNG_InfoPng
* source
)
504 LodePNG_InfoPng_cleanup(dest
);
506 LodePNG_InfoColor_init(&dest
->color
);
507 error
= LodePNG_InfoColor_copy(&dest
->color
, &source
->color
); if (error
) return error
;
511 void LodePNG_InfoPng_swap(LodePNG_InfoPng
* a
, LodePNG_InfoPng
* b
)
513 LodePNG_InfoPng temp
= *a
;
518 void LodePNG_InfoRaw_init(LodePNG_InfoRaw
* info
)
520 LodePNG_InfoColor_init(&info
->color
);
523 void LodePNG_InfoRaw_cleanup(LodePNG_InfoRaw
* info
)
525 LodePNG_InfoColor_cleanup(&info
->color
);
528 unsigned LodePNG_InfoRaw_copy(LodePNG_InfoRaw
* dest
, const LodePNG_InfoRaw
* source
)
531 LodePNG_InfoRaw_cleanup(dest
);
533 LodePNG_InfoColor_init(&dest
->color
);
534 error
= LodePNG_InfoColor_copy(&dest
->color
, &source
->color
); if (error
) return error
;
538 /* ////////////////////////////////////////////////////////////////////////// */
541 converts from any color type to 24-bit or 32-bit (later maybe more supported). return value = LodePNG error code
542 the out buffer must have (w * h * bpp + 7) / 8 bytes, where bpp is the bits per pixel of the output color type (LodePNG_InfoColor_getBpp)
543 for < 8 bpp images, there may _not_ be padding bits at the end of scanlines.
545 unsigned LodePNG_convert(fb_data
* out
, const unsigned char* in
, LodePNG_InfoColor
* infoOut
, LodePNG_InfoColor
* infoIn
, unsigned w
, unsigned h
)
547 size_t i
, j
, bp
= 0; /*bitpointer, used by less-than-8-bit color types*/
551 if (!running_slideshow
)
553 rb
->snprintf(print
, sizeof(print
), "color conversion in progress");
554 rb
->lcd_puts(0, 3, print
);
558 /*cases where in and out already have the same format*/
559 if (LodePNG_InfoColor_equal(infoIn
, infoOut
))
564 for (y
= 0 ; y
< h
; y
++) {
565 for (x
= 0 ; x
< w
; x
++) {
566 unsigned char r
= in
[i
++];
567 unsigned char g
= in
[i
++];
568 unsigned char b
= in
[i
++];
569 out
[j
++] = LCD_RGBPACK(r
,g
,b
);
575 if ((infoOut
->colorType
== 2 || infoOut
->colorType
== 6) && infoOut
->bitDepth
== 8)
577 if (infoIn
->bitDepth
== 8)
579 switch (infoIn
->colorType
)
581 case 0: /*greyscale color*/
583 for (y
= 0 ; y
< h
; y
++) {
584 for (x
= 0 ; x
< w
; x
++) {
586 //unsigned char r = in[i];
587 //unsigned char g = in[i];
588 //unsigned char b = in[i];
589 out
[i
++] = LCD_RGBPACK(c
,c
,c
);
593 case 2: /*RGB color*/
595 for (y
= 0 ; y
< h
; y
++) {
596 for (x
= 0 ; x
< w
; x
++) {
598 unsigned char r
= in
[j
];
599 unsigned char g
= in
[j
+ 1];
600 unsigned char b
= in
[j
+ 2];
601 out
[i
++] = LCD_RGBPACK(r
,g
,b
);
605 case 3: /*indexed color (palette)*/
607 for (y
= 0 ; y
< h
; y
++) {
608 for (x
= 0 ; x
< w
; x
++) {
609 if (in
[i
] >= infoIn
->palettesize
) return 46;
611 unsigned char r
= infoIn
->palette
[j
];
612 unsigned char g
= infoIn
->palette
[j
+ 1];
613 unsigned char b
= infoIn
->palette
[j
+ 2];
614 out
[i
++] = LCD_RGBPACK(r
,g
,b
);
618 case 4: /*greyscale with alpha*/
620 for (y
= 0 ; y
< h
; y
++) {
621 for (x
= 0 ; x
< w
; x
++) {
623 //unsigned char r = in[i<<1];
624 //unsigned char g = in[i<<1];
625 //unsigned char b = in[i<<1];
626 out
[i
++] = LCD_RGBPACK(c
,c
,c
);
630 case 6: /*RGB with alpha*/
632 for (y
= 0 ; y
< h
; y
++) {
633 for (x
= 0 ; x
< w
; x
++) {
635 unsigned char r
= in
[j
];
636 unsigned char g
= in
[j
+ 1];
637 unsigned char b
= in
[j
+ 2];
638 out
[i
++] = LCD_RGBPACK(r
,g
,b
);
646 else if (infoIn
->bitDepth
== 16)
648 switch (infoIn
->colorType
)
650 case 0: /*greyscale color*/
652 for (y
= 0 ; y
< h
; y
++) {
653 for (x
= 0 ; x
< w
; x
++) {
655 //unsigned char r = in[2 * i];
656 //unsigned char g = in[2 * i];
657 //unsigned char b = in[2 * i];
658 out
[i
++] = LCD_RGBPACK(c
,c
,c
);
662 case 2: /*RGB color*/
664 for (y
= 0 ; y
< h
; y
++) {
665 for (x
= 0 ; x
< w
; x
++) {
667 unsigned char r
= in
[j
];
668 unsigned char g
= in
[j
+ 2];
669 unsigned char b
= in
[j
+ 4];
670 out
[i
++] = LCD_RGBPACK(r
,g
,b
);
674 case 4: /*greyscale with alpha*/
676 for (y
= 0 ; y
< h
; y
++) {
677 for (x
= 0 ; x
< w
; x
++) {
679 //unsigned char r = in[4 * i];
680 //unsigned char g = in[4 * i];
681 //unsigned char b = in[4 * i];
682 out
[i
++] = LCD_RGBPACK(c
,c
,c
);
686 case 6: /*RGB with alpha*/
688 for (y
= 0 ; y
< h
; y
++) {
689 for (x
= 0 ; x
< w
; x
++) {
691 unsigned char r
= in
[j
];
692 unsigned char g
= in
[j
+ 2];
693 unsigned char b
= in
[j
+ 4];
694 out
[i
++] = LCD_RGBPACK(r
,g
,b
);
702 else /*infoIn->bitDepth is less than 8 bit per channel*/
704 switch (infoIn
->colorType
)
706 case 0: /*greyscale color*/
708 for (y
= 0 ; y
< h
; y
++) {
709 for (x
= 0 ; x
< w
; x
++) {
710 unsigned value
= readBitsFromReversedStream(&bp
, in
, infoIn
->bitDepth
);
711 value
= (value
* 255) / ((1 << infoIn
->bitDepth
) - 1); /*scale value from 0 to 255*/
712 unsigned char r
= (unsigned char)value
;
713 unsigned char g
= (unsigned char)value
;
714 unsigned char b
= (unsigned char)value
;
715 out
[i
++] = LCD_RGBPACK(r
,g
,b
);
719 case 3: /*indexed color (palette)*/
721 for (y
= 0 ; y
< h
; y
++) {
722 for (x
= 0 ; x
< w
; x
++) {
723 unsigned value
= readBitsFromReversedStream(&bp
, in
, infoIn
->bitDepth
);
724 if (value
>= infoIn
->palettesize
) return 47;
726 unsigned char r
= infoIn
->palette
[j
];
727 unsigned char g
= infoIn
->palette
[j
+ 1];
728 unsigned char b
= infoIn
->palette
[j
+ 2];
729 out
[i
++] = LCD_RGBPACK(r
,g
,b
);
738 else if (LodePNG_InfoColor_isGreyscaleType(infoOut
) && infoOut
->bitDepth
== 8) /*conversion from greyscale to greyscale*/
740 if (!LodePNG_InfoColor_isGreyscaleType(infoIn
)) return 62;
741 if (infoIn
->bitDepth
== 8)
743 switch (infoIn
->colorType
)
745 case 0: /*greyscale color*/
747 for (y
= 0 ; y
< h
; y
++) {
748 for (x
= 0 ; x
< w
; x
++) {
750 //unsigned char r = in[i];
751 //unsigned char g = in[i];
752 //unsigned char b = in[i];
753 out
[i
++] = LCD_RGBPACK(c
,c
,c
);
757 case 4: /*greyscale with alpha*/
759 for (y
= 0 ; y
< h
; y
++) {
760 for (x
= 0 ; x
< w
; x
++) {
761 c
= in
[(i
<< 1) + 1];
762 //unsigned char r = in[2 * i + 1];
763 //unsigned char g = in[2 * i + 1];
764 //unsigned char b = in[2 * i + 1];
765 out
[i
++] = LCD_RGBPACK(c
,c
,c
);
773 else if (infoIn
->bitDepth
== 16)
775 switch (infoIn
->colorType
)
777 case 0: /*greyscale color*/
779 for (y
= 0 ; y
< h
; y
++) {
780 for (x
= 0 ; x
< w
; x
++) {
782 //unsigned char r = in[2 * i];
783 //unsigned char g = in[2 * i];
784 //unsigned char b = in[2 * i];
785 out
[i
++] = LCD_RGBPACK(c
,c
,c
);
789 case 4: /*greyscale with alpha*/
791 for (y
= 0 ; y
< h
; y
++) {
792 for (x
= 0 ; x
< w
; x
++) {
794 //unsigned char r = in[4 * i];
795 //unsigned char g = in[4 * i];
796 //unsigned char b = in[4 * i];
797 out
[i
++] = LCD_RGBPACK(c
,c
,c
);
805 else /*infoIn->bitDepth is less than 8 bit per channel*/
807 if (infoIn
->colorType
!= 0) return 31; /*colorType 0 is the only greyscale type with < 8 bits per channel*/
809 for (y
= 0 ; y
< h
; y
++) {
810 for (x
= 0 ; x
< w
; x
++) {
811 unsigned value
= readBitsFromReversedStream(&bp
, in
, infoIn
->bitDepth
);
812 value
= (value
* 255) / ((1 << infoIn
->bitDepth
) - 1); /*scale value from 0 to 255*/
813 unsigned char r
= (unsigned char)value
;
814 unsigned char g
= (unsigned char)value
;
815 unsigned char b
= (unsigned char)value
;
816 out
[i
++] = LCD_RGBPACK(r
,g
,b
);
826 /*Paeth predicter, used by PNG filter type 4*/
827 static int paethPredictor(int a
, int b
, int c
)
830 int pa
= p
> a
? p
- a
: a
- p
;
831 int pb
= p
> b
? p
- b
: b
- p
;
832 int pc
= p
> c
? p
- c
: c
- p
;
834 if (pa
<= pb
&& pa
<= pc
) return a
;
835 else if (pb
<= pc
) return b
;
839 /*shared values used by multiple Adam7 related functions*/
841 static const unsigned ADAM7_IX
[7] = { 0, 4, 0, 2, 0, 1, 0 }; /*x start values*/
842 static const unsigned ADAM7_IY
[7] = { 0, 0, 4, 0, 2, 0, 1 }; /*y start values*/
843 static const unsigned ADAM7_DX
[7] = { 8, 8, 4, 4, 2, 2, 1 }; /*x delta values*/
844 static const unsigned ADAM7_DY
[7] = { 8, 8, 8, 4, 4, 2, 2 }; /*y delta values*/
846 static void Adam7_getpassvalues(unsigned passw
[7], unsigned passh
[7], size_t filter_passstart
[8], size_t padded_passstart
[8], size_t passstart
[8], unsigned w
, unsigned h
, unsigned bpp
)
848 /*the passstart values have 8 values: the 8th one actually indicates the byte after the end of the 7th (= last) pass*/
851 /*calculate width and height in pixels of each pass*/
852 for (i
= 0; i
< 7; i
++)
854 passw
[i
] = (w
+ ADAM7_DX
[i
] - ADAM7_IX
[i
] - 1) / ADAM7_DX
[i
];
855 passh
[i
] = (h
+ ADAM7_DY
[i
] - ADAM7_IY
[i
] - 1) / ADAM7_DY
[i
];
856 if (passw
[i
] == 0) passh
[i
] = 0;
857 if (passh
[i
] == 0) passw
[i
] = 0;
860 filter_passstart
[0] = padded_passstart
[0] = passstart
[0] = 0;
861 for (i
= 0; i
< 7; i
++)
863 filter_passstart
[i
+ 1] = filter_passstart
[i
] + ((passw
[i
] && passh
[i
]) ? passh
[i
] * (1 + (passw
[i
] * bpp
+ 7) / 8) : 0); /*if passw[i] is 0, it's 0 bytes, not 1 (no filtertype-byte)*/
864 padded_passstart
[i
+ 1] = padded_passstart
[i
] + passh
[i
] * ((passw
[i
] * bpp
+ 7) / 8); /*bits padded if needed to fill full byte at end of each scanline*/
865 passstart
[i
+ 1] = passstart
[i
] + (passh
[i
] * passw
[i
] * bpp
+ 7) / 8; /*only padded at end of reduced image*/
869 /* ////////////////////////////////////////////////////////////////////////// */
870 /* / PNG Decoder / */
871 /* ////////////////////////////////////////////////////////////////////////// */
873 /*read the information from the header and store it in the LodePNG_Info. return value is error*/
874 void LodePNG_inspect(LodePNG_Decoder
* decoder
, const unsigned char* in
, size_t inlength
)
876 if (inlength
== 0 || in
== 0) { decoder
->error
= 48; return; } /*the given data is empty*/
877 if (inlength
< 29) { decoder
->error
= 27; return; } /*error: the data length is smaller than the length of the header*/
879 /*when decoding a new PNG image, make sure all parameters created after previous decoding are reset*/
880 LodePNG_InfoPng_cleanup(&decoder
->infoPng
);
881 LodePNG_InfoPng_init(&decoder
->infoPng
);
884 if (in
[0] != 137 || in
[1] != 80 || in
[2] != 78 || in
[3] != 71 || in
[4] != 13 || in
[5] != 10 || in
[6] != 26 || in
[7] != 10) { decoder
->error
= 28; return; } /*error: the first 8 bytes are not the correct PNG signature*/
885 if (in
[12] != 'I' || in
[13] != 'H' || in
[14] != 'D' || in
[15] != 'R') { decoder
->error
= 29; return; } /*error: it doesn't start with a IHDR chunk!*/
887 /*read the values given in the header*/
888 decoder
->infoPng
.width
= LodePNG_read32bitInt(&in
[16]);
889 decoder
->infoPng
.height
= LodePNG_read32bitInt(&in
[20]);
890 decoder
->infoPng
.color
.bitDepth
= in
[24];
891 decoder
->infoPng
.color
.colorType
= in
[25];
892 decoder
->infoPng
.compressionMethod
= in
[26];
893 decoder
->infoPng
.filterMethod
= in
[27];
894 decoder
->infoPng
.interlaceMethod
= in
[28];
896 unsigned CRC
= LodePNG_read32bitInt(&in
[29]);
897 unsigned checksum
= crc32(0L, &in
[12], 17);
898 if (CRC
!= checksum
) { decoder
->error
= 57; return; }
900 if (decoder
->infoPng
.compressionMethod
!= 0) { decoder
->error
= 32; return; } /*error: only compression method 0 is allowed in the specification*/
901 if (decoder
->infoPng
.filterMethod
!= 0) { decoder
->error
= 33; return; } /*error: only filter method 0 is allowed in the specification*/
902 if (decoder
->infoPng
.interlaceMethod
> 1) { decoder
->error
= 34; return; } /*error: only interlace methods 0 and 1 exist in the specification*/
904 decoder
->error
= checkColorValidity(decoder
->infoPng
.color
.colorType
, decoder
->infoPng
.color
.bitDepth
);
907 static unsigned unfilterScanline(unsigned char* recon
, const unsigned char* scanline
, const unsigned char* precon
, size_t bytewidth
, unsigned char filterType
, size_t length
)
910 For PNG filter method 0
911 unfilter a PNG image scanline by scanline. when the pixels are smaller than 1 byte, the filter works byte per byte (bytewidth = 1)
912 precon is the previous unfiltered scanline, recon the result, scanline the current one
913 the incoming scanlines do NOT include the filtertype byte, that one is given in the parameter filterType instead
914 recon and scanline MAY be the same memory address! precon must be disjoint.
921 //for(i = 0; i < length; i++) recon[i] = scanline[i];
922 memcpy(recon
, scanline
, length
* sizeof(unsigned char));
925 //for(i = 0; i < bytewidth; i++) recon[i] = scanline[i];
926 memcpy(recon
, scanline
, bytewidth
* sizeof(unsigned char));
927 for (i
= bytewidth
; i
< length
; i
++) recon
[i
] = scanline
[i
] + recon
[i
- bytewidth
];
930 if (precon
) for (i
= 0; i
< length
; i
++) recon
[i
] = scanline
[i
] + precon
[i
];
931 else //for(i = 0; i < length; i++) recon[i] = scanline[i];
932 memcpy(recon
, scanline
, length
* sizeof(unsigned char));
937 for (i
= 0; i
< bytewidth
; i
++) recon
[i
] = scanline
[i
] + precon
[i
] / 2;
938 for (i
= bytewidth
; i
< length
; i
++) recon
[i
] = scanline
[i
] + ((recon
[i
- bytewidth
] + precon
[i
]) / 2);
942 //for(i = 0; i < bytewidth; i++) recon[i] = scanline[i];
943 memcpy(recon
, scanline
, bytewidth
* sizeof(unsigned char));
944 for (i
= bytewidth
; i
< length
; i
++) recon
[i
] = scanline
[i
] + recon
[i
- bytewidth
] / 2;
950 for (i
= 0; i
< bytewidth
; i
++) recon
[i
] = (unsigned char)(scanline
[i
] + paethPredictor(0, precon
[i
], 0));
951 for (i
= bytewidth
; i
< length
; i
++) recon
[i
] = (unsigned char)(scanline
[i
] + paethPredictor(recon
[i
- bytewidth
], precon
[i
], precon
[i
- bytewidth
]));
955 //for(i = 0; i < bytewidth; i++) recon[i] = scanline[i];
956 memcpy(recon
, scanline
, bytewidth
* sizeof(unsigned char));
957 for (i
= bytewidth
; i
< length
; i
++) recon
[i
] = (unsigned char)(scanline
[i
] + paethPredictor(recon
[i
- bytewidth
], 0, 0));
961 return 36; /*error: unexisting filter type given*/
966 static unsigned unfilter(unsigned char* out
, const unsigned char* in
, unsigned w
, unsigned h
, unsigned bpp
)
969 For PNG filter method 0
970 this function unfilters a single image (e.g. without interlacing this is called once, with Adam7 it's called 7 times)
971 out must have enough bytes allocated already, in must have the scanlines + 1 filtertype byte per scanline
972 w and h are image dimensions or dimensions of reduced image, bpp is bits per pixel
973 in and out are allowed to be the same memory address!
977 unsigned char* prevline
= 0;
979 size_t bytewidth
= (bpp
+ 7) / 8; /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/
980 size_t linebytes
= (w
* bpp
+ 7) / 8;
982 for (y
= 0; y
< h
; y
++)
984 size_t outindex
= linebytes
* y
;
985 size_t inindex
= (1 + linebytes
) * y
; /*the extra filterbyte added to each row*/
986 unsigned char filterType
= in
[inindex
];
988 unsigned error
= unfilterScanline(&out
[outindex
], &in
[inindex
+ 1], prevline
, bytewidth
, filterType
, linebytes
);
989 if (error
) return error
;
991 prevline
= &out
[outindex
];
997 static void Adam7_deinterlace(unsigned char* out
, const unsigned char* in
, unsigned w
, unsigned h
, unsigned bpp
)
999 /*Note: this function works on image buffers WITHOUT padding bits at end of scanlines with non-multiple-of-8 bit amounts, only between reduced images is padding
1000 out must be big enough AND must be 0 everywhere if bpp < 8 in the current implementation (because that's likely a little bit faster)*/
1001 unsigned passw
[7], passh
[7]; size_t filter_passstart
[8], padded_passstart
[8], passstart
[8];
1004 Adam7_getpassvalues(passw
, passh
, filter_passstart
, padded_passstart
, passstart
, w
, h
, bpp
);
1008 for (i
= 0; i
< 7; i
++)
1011 size_t bytewidth
= bpp
/ 8;
1012 for (y
= 0; y
< passh
[i
]; y
++)
1013 for (x
= 0; x
< passw
[i
]; x
++)
1015 size_t pixelinstart
= passstart
[i
] + (y
* passw
[i
] + x
) * bytewidth
;
1016 size_t pixeloutstart
= ((ADAM7_IY
[i
] + y
* ADAM7_DY
[i
]) * w
+ ADAM7_IX
[i
] + x
* ADAM7_DX
[i
]) * bytewidth
;
1017 for (b
= 0; b
< bytewidth
; b
++)
1019 out
[pixeloutstart
+ b
] = in
[pixelinstart
+ b
];
1024 else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/
1026 for (i
= 0; i
< 7; i
++)
1029 unsigned ilinebits
= bpp
* passw
[i
];
1030 unsigned olinebits
= bpp
* w
;
1031 size_t obp
, ibp
; /*bit pointers (for out and in buffer)*/
1032 for (y
= 0; y
< passh
[i
]; y
++)
1033 for (x
= 0; x
< passw
[i
]; x
++)
1035 ibp
= (8 * passstart
[i
]) + (y
* ilinebits
+ x
* bpp
);
1036 obp
= (ADAM7_IY
[i
] + y
* ADAM7_DY
[i
]) * olinebits
+ (ADAM7_IX
[i
] + x
* ADAM7_DX
[i
]) * bpp
;
1037 for (b
= 0; b
< bpp
; b
++)
1039 unsigned char bit
= readBitFromReversedStream(&ibp
, in
);
1040 setBitOfReversedStream0(&obp
, out
, bit
); /*note that this function assumes the out buffer is completely 0, use setBitOfReversedStream otherwise*/
1047 static void removePaddingBits(unsigned char* out
, const unsigned char* in
, size_t olinebits
, size_t ilinebits
, unsigned h
)
1050 After filtering there are still padding bits if scanlines have non multiple of 8 bit amounts. They need to be removed (except at last scanline of (Adam7-reduced) image) before working with pure image buffers for the Adam7 code, the color convert code and the output to the user.
1051 in and out are allowed to be the same buffer, in may also be higher but still overlapping; in must have >= ilinebits*h bits, out must have >= olinebits*h bits, olinebits must be <= ilinebits
1052 also used to move bits after earlier such operations happened, e.g. in a sequence of reduced images from Adam7
1053 only useful if (ilinebits - olinebits) is a value in the range 1..7
1056 size_t diff
= ilinebits
- olinebits
;
1057 size_t obp
= 0, ibp
= 0; /*bit pointers*/
1058 for (y
= 0; y
< h
; y
++)
1061 for (x
= 0; x
< olinebits
; x
++)
1063 unsigned char bit
= readBitFromReversedStream(&ibp
, in
);
1064 setBitOfReversedStream(&obp
, out
, bit
);
1070 /*out must be buffer big enough to contain full image, and in must contain the full decompressed data from the IDAT chunks*/
1071 static unsigned postProcessScanlines(unsigned char* out
, unsigned char* in
, const LodePNG_Decoder
* decoder
) /*return value is error*/
1074 This function converts the filtered-padded-interlaced data into pure 2D image buffer with the PNG's colortype. Steps:
1075 *) if no Adam7: 1) unfilter 2) remove padding bits (= posible extra bits per scanline if bpp < 8)
1076 *) if adam7: 1) 7x unfilter 2) 7x remove padding bits 3) Adam7_deinterlace
1077 NOTE: the in buffer will be overwritten with intermediate data!
1079 unsigned bpp
= LodePNG_InfoColor_getBpp(&decoder
->infoPng
.color
);
1080 unsigned w
= decoder
->infoPng
.width
;
1081 unsigned h
= decoder
->infoPng
.height
;
1083 if (bpp
== 0) return 31; /*error: invalid colortype*/
1085 if (decoder
->infoPng
.interlaceMethod
== 0)
1087 if (bpp
< 8 && w
* bpp
!= ((w
* bpp
+ 7) / 8) * 8)
1089 error
= unfilter(in
, in
, w
, h
, bpp
);
1090 if (error
) return error
;
1091 removePaddingBits(out
, in
, w
* bpp
, ((w
* bpp
+ 7) / 8) * 8, h
);
1093 else error
= unfilter(out
, in
, w
, h
, bpp
); /*we can immediatly filter into the out buffer, no other steps needed*/
1095 else /*interlaceMethod is 1 (Adam7)*/
1097 unsigned passw
[7], passh
[7]; size_t filter_passstart
[8], padded_passstart
[8], passstart
[8];
1100 Adam7_getpassvalues(passw
, passh
, filter_passstart
, padded_passstart
, passstart
, w
, h
, bpp
);
1102 for (i
= 0; i
< 7; i
++)
1104 error
= unfilter(&in
[padded_passstart
[i
]], &in
[filter_passstart
[i
]], passw
[i
], passh
[i
], bpp
);
1105 if (error
) return error
;
1106 if (bpp
< 8) /*TODO: possible efficiency improvement: if in this reduced image the bits fit nicely in 1 scanline, move bytes instead of bits or move not at all*/
1108 /*remove padding bits in scanlines; after this there still may be padding bits between the different reduced images: each reduced image still starts nicely at a byte*/
1109 removePaddingBits(&in
[passstart
[i
]], &in
[padded_passstart
[i
]], passw
[i
] * bpp
, ((passw
[i
] * bpp
+ 7) / 8) * 8, passh
[i
]);
1113 Adam7_deinterlace(out
, in
, w
, h
, bpp
);
1119 /*read a PNG, the result will be in the same color type as the PNG (hence "generic")*/
1120 static void decodeGeneric(LodePNG_Decoder
* decoder
, unsigned char* in
, size_t size
, void (*pf_progress
)(int current
, int total
))
1122 if (pf_progress
!= NULL
)
1123 pf_progress(0, 100);
1124 unsigned char IEND
= 0;
1125 const unsigned char* chunk
;
1127 unsigned char *idat
= memory
;
1128 size_t idat_size
= 0;
1130 /*for unknown chunk order*/
1131 unsigned unknown
= 0;
1132 unsigned critical_pos
= 1; /*1 = after IHDR, 2 = after PLTE, 3 = after IDAT*/
1134 /*provide some proper output values if error will happen*/
1135 decoded_image_size
= 0;
1137 if (size
== 0 || in
== 0) { decoder
->error
= 48; return; } /*the given data is empty*/
1139 LodePNG_inspect(decoder
, in
, size
); /*reads header and resets other parameters in decoder->infoPng*/
1140 if (decoder
->error
) return;
1142 chunk
= &in
[33]; /*first byte of the first chunk after the header*/
1144 while (!IEND
) /*loop through the chunks, ignoring unknown chunks and stopping at IEND chunk. IDAT data is put at the start of the in buffer*/
1146 unsigned chunkLength
;
1147 const unsigned char* data
; /*the data in the chunk*/
1149 if ((size_t)((chunk
- in
) + 12) > size
|| chunk
< in
) { decoder
->error
= 30; break; } /*error: size of the in buffer too small to contain next chunk*/
1150 chunkLength
= LodePNG_chunk_length(chunk
); /*length of the data of the chunk, excluding the length bytes, chunk type and CRC bytes*/
1151 if (chunkLength
> 2147483647) { decoder
->error
= 63; break; }
1152 if ((size_t)((chunk
- in
) + chunkLength
+ 12) > size
|| (chunk
+ chunkLength
+ 12) < in
) { decoder
->error
= 35; break; } /*error: size of the in buffer too small to contain next chunk*/
1153 data
= LodePNG_chunk_data_const(chunk
);
1155 /*IDAT chunk, containing compressed image data*/
1156 if (LodePNG_chunk_type_equals(chunk
, "IDAT"))
1158 size_t oldsize
= idat_size
;
1159 idat_size
+= chunkLength
;
1160 if (idat
+ idat_size
>= image
) { decoder
->error
= OUT_OF_MEMORY
; break; }
1161 memcpy(idat
+oldsize
, data
, chunkLength
* sizeof(unsigned char));
1165 else if (LodePNG_chunk_type_equals(chunk
, "IEND"))
1169 /*palette chunk (PLTE)*/
1170 else if (LodePNG_chunk_type_equals(chunk
, "PLTE"))
1173 decoder
->infoPng
.color
.palettesize
= chunkLength
/ 3;
1174 if (decoder
->infoPng
.color
.palettesize
> 256) { decoder
->error
= 38; break; } /*error: palette too big*/
1175 for (i
= 0; i
< decoder
->infoPng
.color
.palettesize
; i
++)
1177 decoder
->infoPng
.color
.palette
[4 * i
+ 0] = data
[pos
++]; /*R*/
1178 decoder
->infoPng
.color
.palette
[4 * i
+ 1] = data
[pos
++]; /*G*/
1179 decoder
->infoPng
.color
.palette
[4 * i
+ 2] = data
[pos
++]; /*B*/
1180 decoder
->infoPng
.color
.palette
[4 * i
+ 3] = 255; /*alpha*/
1184 /*palette transparency chunk (tRNS)*/
1185 else if (LodePNG_chunk_type_equals(chunk
, "tRNS"))
1187 if (decoder
->infoPng
.color
.colorType
== 3)
1189 if (chunkLength
> decoder
->infoPng
.color
.palettesize
) { decoder
->error
= 39; break; } /*error: more alpha values given than there are palette entries*/
1190 for (i
= 0; i
< chunkLength
; i
++) decoder
->infoPng
.color
.palette
[4 * i
+ 3] = data
[i
];
1192 else if (decoder
->infoPng
.color
.colorType
== 0)
1194 if (chunkLength
!= 2) { decoder
->error
= 40; break; } /*error: this chunk must be 2 bytes for greyscale image*/
1195 decoder
->infoPng
.color
.key_defined
= 1;
1196 decoder
->infoPng
.color
.key_r
= decoder
->infoPng
.color
.key_g
= decoder
->infoPng
.color
.key_b
= 256 * data
[0] + data
[1];
1198 else if (decoder
->infoPng
.color
.colorType
== 2)
1200 if (chunkLength
!= 6) { decoder
->error
= 41; break; } /*error: this chunk must be 6 bytes for RGB image*/
1201 decoder
->infoPng
.color
.key_defined
= 1;
1202 decoder
->infoPng
.color
.key_r
= 256 * data
[0] + data
[1];
1203 decoder
->infoPng
.color
.key_g
= 256 * data
[2] + data
[3];
1204 decoder
->infoPng
.color
.key_b
= 256 * data
[4] + data
[5];
1206 else { decoder
->error
= 42; break; } /*error: tRNS chunk not allowed for other color models*/
1208 /*background color chunk (bKGD)*/
1209 else if (LodePNG_chunk_type_equals(chunk
, "bKGD"))
1211 if (decoder
->infoPng
.color
.colorType
== 3)
1213 if (chunkLength
!= 1) { decoder
->error
= 43; break; } /*error: this chunk must be 1 byte for indexed color image*/
1214 decoder
->infoPng
.background_defined
= 1;
1215 decoder
->infoPng
.background_r
= decoder
->infoPng
.background_g
= decoder
->infoPng
.background_g
= data
[0];
1217 else if (decoder
->infoPng
.color
.colorType
== 0 || decoder
->infoPng
.color
.colorType
== 4)
1219 if (chunkLength
!= 2) { decoder
->error
= 44; break; } /*error: this chunk must be 2 bytes for greyscale image*/
1220 decoder
->infoPng
.background_defined
= 1;
1221 decoder
->infoPng
.background_r
= decoder
->infoPng
.background_g
= decoder
->infoPng
.background_b
= 256 * data
[0] + data
[1];
1223 else if (decoder
->infoPng
.color
.colorType
== 2 || decoder
->infoPng
.color
.colorType
== 6)
1225 if (chunkLength
!= 6) { decoder
->error
= 45; break; } /*error: this chunk must be 6 bytes for greyscale image*/
1226 decoder
->infoPng
.background_defined
= 1;
1227 decoder
->infoPng
.background_r
= 256 * data
[0] + data
[1];
1228 decoder
->infoPng
.background_g
= 256 * data
[2] + data
[3];
1229 decoder
->infoPng
.background_b
= 256 * data
[4] + data
[5];
1232 else if (LodePNG_chunk_type_equals(chunk
, "tIME"))
1234 if (chunkLength
!= 7) { decoder
->error
= 73; break; }
1235 decoder
->infoPng
.time_defined
= 1;
1236 decoder
->infoPng
.time
.year
= 256 * data
[0] + data
[+ 1];
1237 decoder
->infoPng
.time
.month
= data
[2];
1238 decoder
->infoPng
.time
.day
= data
[3];
1239 decoder
->infoPng
.time
.hour
= data
[4];
1240 decoder
->infoPng
.time
.minute
= data
[5];
1241 decoder
->infoPng
.time
.second
= data
[6];
1243 else if (LodePNG_chunk_type_equals(chunk
, "pHYs"))
1245 if (chunkLength
!= 9) { decoder
->error
= 74; break; }
1246 decoder
->infoPng
.phys_defined
= 1;
1247 decoder
->infoPng
.phys_x
= 16777216 * data
[0] + 65536 * data
[1] + 256 * data
[2] + data
[3];
1248 decoder
->infoPng
.phys_y
= 16777216 * data
[4] + 65536 * data
[5] + 256 * data
[6] + data
[7];
1249 decoder
->infoPng
.phys_unit
= data
[8];
1251 else /*it's not an implemented chunk type, so ignore it: skip over the data*/
1253 if (LodePNG_chunk_critical(chunk
)) { decoder
->error
= 69; break; } /*error: unknown critical chunk (5th bit of first byte of chunk type is 0)*/
1257 if (!unknown
) /*check CRC if wanted, only on known chunk types*/
1259 long time
= *rb
->current_tick
;
1260 if (LodePNG_chunk_check_crc(chunk
)) { decoder
->error
= 57; break; }
1261 time
= *rb
->current_tick
-time
;
1264 if (!IEND
) chunk
= LodePNG_chunk_next_const(chunk
);
1267 if (!decoder
->error
)
1269 unsigned char *scanlines
= idat
+ idat_size
;
1270 size_t scanlines_size
= (size_t)memory_max
- idat_size
+ 1;
1271 long time
= *rb
->current_tick
;
1272 decoder
->error
= LodePNG_decompress(scanlines
, &scanlines_size
, idat
, idat_size
, decoder
->error_msg
); /*decompress with the Zlib decompressor*/
1273 if (pf_progress
) pf_progress(100, 100);
1274 time
= *rb
->current_tick
-time
;
1276 if (!decoder
->error
)
1278 decoded_image_size
= (decoder
->infoPng
.height
* decoder
->infoPng
.width
* LodePNG_InfoColor_getBpp(&decoder
->infoPng
.color
) + 7) / 8;
1279 if (decoded_image_size
> memory_size
) { decoder
->error
= OUT_OF_MEMORY
; return; }
1280 decoded_image
= memory_max
- decoded_image_size
+ 1;
1281 if (scanlines
+ scanlines_size
>= decoded_image
) { decoder
->error
= OUT_OF_MEMORY
; return; }
1282 memset(decoded_image
, 0, decoded_image_size
* sizeof(unsigned char));
1283 if (!running_slideshow
)
1285 rb
->snprintf(print
, sizeof(print
), "unfiltering scanlines");
1286 rb
->lcd_puts(0, 3, print
);
1289 decoder
->error
= postProcessScanlines(decoded_image
, scanlines
, decoder
);
1294 void LodePNG_decode(LodePNG_Decoder
* decoder
, unsigned char* in
, size_t insize
, void (*pf_progress
)(int current
, int total
))
1296 decodeGeneric(decoder
, in
, insize
, pf_progress
);
1297 if (decoder
->error
) return;
1299 /*TODO: check if this works according to the statement in the documentation: "The converter can convert from greyscale input color type, to 8-bit greyscale or greyscale with alpha"*/
1300 if (!(decoder
->infoRaw
.color
.colorType
== 2 || decoder
->infoRaw
.color
.colorType
== 6) && !(decoder
->infoRaw
.color
.bitDepth
== 8)) { decoder
->error
= 56; return; }
1301 converted_image
= (fb_data
*)((intptr_t)(memory
+ 3) & ~3);
1302 converted_image_size
= decoder
->infoPng
.width
*decoder
->infoPng
.height
;
1303 if ((unsigned char *)(converted_image
+ converted_image_size
) >= decoded_image
) { decoder
->error
= OUT_OF_MEMORY
; }
1304 if (!decoder
->error
) decoder
->error
= LodePNG_convert(converted_image
, decoded_image
, &decoder
->infoRaw
.color
, &decoder
->infoPng
.color
, decoder
->infoPng
.width
, decoder
->infoPng
.height
);
1307 void LodePNG_DecodeSettings_init(LodePNG_DecodeSettings
* settings
)
1309 settings
->color_convert
= 1;
1312 void LodePNG_Decoder_init(LodePNG_Decoder
* decoder
)
1314 LodePNG_DecodeSettings_init(&decoder
->settings
);
1315 LodePNG_InfoRaw_init(&decoder
->infoRaw
);
1316 LodePNG_InfoPng_init(&decoder
->infoPng
);
1320 void LodePNG_Decoder_cleanup(LodePNG_Decoder
* decoder
)
1322 LodePNG_InfoRaw_cleanup(&decoder
->infoRaw
);
1323 LodePNG_InfoPng_cleanup(&decoder
->infoPng
);
1326 bool png_ext(const char ext
[])
1330 if (!rb
->strcasecmp(ext
,".png"))
1336 /*Read directory contents for scrolling. */
1337 void get_pic_list(void)
1340 struct entry
*dircache
;
1342 tree
= rb
->tree_get_context();
1343 dircache
= tree
->dircache
;
1345 file_pt
= (char **) memory
;
1347 /* Remove path and leave only the name.*/
1348 pname
= rb
->strrchr(np_file
,'/');
1351 for (i
= 0; i
< tree
->filesindir
; i
++)
1353 if (!(dircache
[i
].attr
& ATTR_DIRECTORY
)
1354 && png_ext(rb
->strrchr(dircache
[i
].name
, '.')))
1356 file_pt
[entries
] = dircache
[i
].name
;
1357 /* Set Selected File. */
1358 if (!rb
->strcmp(file_pt
[entries
], pname
))
1364 memory
+= (entries
* sizeof(char**));
1365 memory_size
-= (entries
* sizeof(char**));
1368 int change_filename(int direct
)
1370 bool file_erased
= (file_pt
[curfile
] == NULL
);
1373 curfile
+= (direct
== DIR_PREV
? entries
- 1: 1);
1374 if (curfile
>= entries
)
1379 /* remove 'erased' file names from list. */
1381 for (count
= i
= 0; i
< entries
; i
++)
1385 if (file_pt
[i
] != NULL
)
1386 file_pt
[count
++] = file_pt
[i
];
1393 rb
->splash(HZ
, "No supported files");
1394 return PLUGIN_ERROR
;
1397 if (rb
->strlen(tree
->currdir
) > 1)
1399 rb
->strcpy(np_file
, tree
->currdir
);
1400 rb
->strcat(np_file
, "/");
1403 rb
->strcpy(np_file
, tree
->currdir
);
1405 rb
->strcat(np_file
, file_pt
[curfile
]);
1407 return PLUGIN_OTHER
;
1410 /* switch off overlay, for handling SYS_ events */
1411 void cleanup(void *parameter
)
1416 #define VSCROLL (LCD_HEIGHT/8)
1417 #define HSCROLL (LCD_WIDTH/10)
1419 #define ZOOM_IN 100 /* return codes for below function */
1420 #define ZOOM_OUT 101
1422 int show_menu(void) /* return 1 to quit */
1425 rb
->lcd_set_backdrop(old_backdrop
);
1426 #ifdef HAVE_LCD_COLOR
1427 rb
->lcd_set_foreground(rb
->global_settings
->fg_color
);
1428 rb
->lcd_set_background(rb
->global_settings
->bg_color
);
1430 rb
->lcd_set_foreground(LCD_BLACK
);
1431 rb
->lcd_set_background(LCD_WHITE
);
1439 MIID_TOGGLE_SS_MODE
,
1440 MIID_CHANGE_SS_MODE
,
1441 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
1442 MIID_SHOW_PLAYBACK_MENU
,
1447 MENUITEM_STRINGLIST(menu
, "Png Menu", NULL
,
1448 "Return", "Toggle Slideshow Mode",
1449 "Change Slideshow Time",
1450 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
1451 "Show Playback Menu",
1455 static const struct opt_items slideshow
[2] = {
1460 result
=rb
->do_menu(&menu
, NULL
, NULL
, false);
1466 case MIID_TOGGLE_SS_MODE
:
1467 rb
->set_option("Toggle Slideshow", &slideshow_enabled
, INT
,
1468 slideshow
, 2, NULL
);
1470 case MIID_CHANGE_SS_MODE
:
1471 rb
->set_int("Slideshow Time", "s", UNIT_SEC
,
1472 &png_settings
.ss_timeout
, NULL
, 1,
1473 SS_MIN_TIMEOUT
, SS_MAX_TIMEOUT
, NULL
);
1475 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
1476 case MIID_SHOW_PLAYBACK_MENU
:
1479 playback_control(NULL
);
1483 rb
->splash(HZ
, "Cannot restart playback");
1492 #if !defined(SIMULATOR) && defined(HAVE_DISK_STORAGE)
1493 /* change ata spindown time based on slideshow time setting */
1494 immediate_ata_off
= false;
1495 rb
->storage_spindown(rb
->global_settings
->disk_spindown
);
1497 if (slideshow_enabled
)
1499 if (png_settings
.ss_timeout
< 10)
1501 /* slideshow times < 10s keep disk spinning */
1502 rb
->storage_spindown(0);
1504 else if (!rb
->mp3_is_playing())
1506 /* slideshow times > 10s and not playing: ata_off after load */
1507 immediate_ata_off
= true;
1512 rb
->lcd_set_backdrop(NULL
);
1513 rb
->lcd_set_foreground(LCD_WHITE
);
1514 rb
->lcd_set_background(LCD_BLACK
);
1516 rb
->lcd_clear_display();
1520 void draw_image(struct LodePNG_Decoder
* decoder
)
1522 rb
->lcd_bitmap_part(resized_image
, decoder
->x
, decoder
->y
, decoder
->infoPng
.width
/ds
/*stride*/,
1523 MAX(0, (LCD_WIDTH
- (int)decoder
->infoPng
.width
/(int)ds
) / 2),
1524 MAX(0, (LCD_HEIGHT
- (int)decoder
->infoPng
.height
/(int)ds
) / 2),
1525 decoder
->infoPng
.width
/ds
- decoder
->x
,
1526 decoder
->infoPng
.height
/ds
- decoder
->y
);
1529 /* Pan the viewing window right - move image to the left and fill in
1530 the right-hand side */
1531 static void pan_view_right(struct LodePNG_Decoder
* decoder
)
1535 move
= MIN(HSCROLL
, (int)(decoder
->infoPng
.width
/ds
) - decoder
->x
- LCD_WIDTH
);
1539 draw_image(decoder
);
1544 /* Pan the viewing window left - move image to the right and fill in
1545 the left-hand side */
1546 static void pan_view_left(struct LodePNG_Decoder
* decoder
)
1550 move
= MIN(HSCROLL
, decoder
->x
);
1554 draw_image(decoder
);
1560 /* Pan the viewing window up - move image down and fill in
1562 static void pan_view_up(struct LodePNG_Decoder
* decoder
)
1566 move
= MIN(VSCROLL
, decoder
->y
);
1570 draw_image(decoder
);
1575 /* Pan the viewing window down - move image up and fill in
1577 static void pan_view_down(struct LodePNG_Decoder
* decoder
)
1581 move
= MIN(VSCROLL
, (int)(decoder
->infoPng
.height
/ds
) - decoder
->y
- LCD_HEIGHT
);
1585 draw_image(decoder
);
1590 /* interactively scroll around the image */
1591 int scroll_bmp(struct LodePNG_Decoder
* decoder
)
1598 if (slideshow_enabled
)
1599 button
= rb
->button_get_w_tmo(png_settings
.ss_timeout
* HZ
);
1600 else button
= rb
->button_get(true);
1602 running_slideshow
= false;
1607 if (entries
> 1 && decoder
->infoPng
.width
/ds
<= LCD_WIDTH
1608 && decoder
->infoPng
.height
/ds
<= LCD_HEIGHT
)
1609 return change_filename(DIR_PREV
);
1610 case PNG_LEFT
| BUTTON_REPEAT
:
1611 pan_view_left(decoder
);
1615 if (entries
> 1 && decoder
->infoPng
.width
/ds
<= LCD_WIDTH
1616 && decoder
->infoPng
.height
/ds
<= LCD_HEIGHT
)
1617 return change_filename(DIR_NEXT
);
1618 case PNG_RIGHT
| BUTTON_REPEAT
:
1619 pan_view_right(decoder
);
1623 case PNG_UP
| BUTTON_REPEAT
:
1624 pan_view_up(decoder
);
1628 case PNG_DOWN
| BUTTON_REPEAT
:
1629 pan_view_down(decoder
);
1633 if (!slideshow_enabled
)
1635 running_slideshow
= true;
1637 return change_filename(DIR_NEXT
);
1640 #ifdef PNG_SLIDE_SHOW
1641 case PNG_SLIDE_SHOW
:
1642 slideshow_enabled
= !slideshow_enabled
;
1643 running_slideshow
= slideshow_enabled
;
1647 #ifdef PNG_NEXT_REPEAT
1648 case PNG_NEXT_REPEAT
:
1652 return change_filename(DIR_NEXT
);
1655 #ifdef PNG_PREVIOUS_REPEAT
1656 case PNG_PREVIOUS_REPEAT
:
1660 return change_filename(DIR_PREV
);
1665 if (lastbutton
!= PNG_ZOOM_PRE
)
1673 if (lastbutton
!= PNG_ZOOM_PRE
)
1683 if (show_menu() == 1)
1686 return PLUGIN_REFRESH
;
1690 if (rb
->default_event_handler_ex(button
, cleanup
, NULL
)
1691 == SYS_USB_CONNECTED
)
1692 return PLUGIN_USB_CONNECTED
;
1697 if (button
!= BUTTON_NONE
)
1698 lastbutton
= button
;
1699 } /* while (true) */
1702 /* set the view to the given center point, limit if necessary */
1703 void set_view (struct LodePNG_Decoder
* decoder
, int cx
, int cy
)
1707 /* plain center to available width/height */
1708 x
= cx
- MIN(LCD_WIDTH
, decoder
->infoPng
.width
/ds
) / 2;
1709 y
= cy
- MIN(LCD_HEIGHT
, decoder
->infoPng
.height
/ds
) / 2;
1711 /* limit against upper image size */
1712 x
= MIN((int)(decoder
->infoPng
.width
/ds
) - LCD_WIDTH
, x
);
1713 y
= MIN((int)(decoder
->infoPng
.height
/ds
) - LCD_HEIGHT
, y
);
1715 /* limit against negative side */
1719 decoder
->x
= x
; /* set the values */
1724 /* callback updating a progress meter while PNG decoding */
1725 void cb_progress(int current
, int total
)
1728 if (current
& 1) rb
->yield(); /* be nice to the other threads */
1729 if (!running_slideshow
)
1731 rb
->gui_scrollbar_draw(rb
->screens
[SCREEN_MAIN
],
1732 0, LCD_HEIGHT
-8, LCD_WIDTH
, 8,
1733 total
, 0, current
, HORIZONTAL
);
1734 rb
->lcd_update_rect(0, LCD_HEIGHT
-8, LCD_WIDTH
, 8);
1738 /* in slideshow mode, keep gui interference to a minimum */
1739 rb
->gui_scrollbar_draw(rb
->screens
[SCREEN_MAIN
],
1740 0, LCD_HEIGHT
-4, LCD_WIDTH
, 4,
1741 total
, 0, current
, HORIZONTAL
);
1742 rb
->lcd_update_rect(0, LCD_HEIGHT
-4, LCD_WIDTH
, 4);
1746 int pngmem(struct LodePNG_Decoder
* decoder
, int ds
)
1748 return (decoder
->infoPng
.width
/ds
) * (decoder
->infoPng
.height
/ds
) * FB_DATA_SZ
;
1751 /* how far can we zoom in without running out of memory */
1752 int min_downscale(struct LodePNG_Decoder
* decoder
, int bufsize
)
1756 if (pngmem(decoder
, 8) > bufsize
)
1757 return 0; /* error, too large, even 1:8 doesn't fit */
1759 while (downscale
> 1 && pngmem(decoder
, downscale
/2) <= bufsize
)
1765 /* how far can we zoom out, to fit image into the LCD */
1766 unsigned max_downscale(struct LodePNG_Decoder
* decoder
)
1768 unsigned downscale
= 1;
1770 while (downscale
< 8 && (decoder
->infoPng
.width
/downscale
> LCD_WIDTH
1771 || decoder
->infoPng
.height
/downscale
> LCD_HEIGHT
))
1779 /* calculate the view center based on the bitmap position */
1780 void get_view(struct LodePNG_Decoder
* decoder
, int* p_cx
, int* p_cy
)
1782 *p_cx
= decoder
->x
+ MIN(LCD_WIDTH
, decoder
->infoPng
.width
/ds
) / 2;
1783 *p_cy
= decoder
->y
+ MIN(LCD_HEIGHT
, decoder
->infoPng
.height
/ds
) / 2;
1786 /* return decoded or cached image */
1787 fb_data
*get_image(struct LodePNG_Decoder
* decoder
)
1789 fb_data
* p_disp
= disp
[ds
]; /* short cut */
1793 DEBUGF("Found an image in cache\n");
1794 return p_disp
; /* we still have it */
1797 /* assign image buffer */
1799 if (!running_slideshow
)
1801 rb
->snprintf(print
, sizeof(print
), "resizing %d*%d",
1802 decoder
->infoPng
.width
/ds
, decoder
->infoPng
.height
/ds
);
1803 rb
->lcd_puts(0, 3, print
);
1806 static struct bitmap bmp_src
, bmp_dst
;
1808 int size
= (decoder
->infoPng
.width
/ds
) * (decoder
->infoPng
.height
/ds
);
1809 disp
[ds
] = disp_buf
;
1811 if ((unsigned char *)(disp
[ds
] + size
) >= memory_max
) {
1812 //rb->splash(HZ, "Out of Memory");
1813 // Still display the original image which is already decoded in RAM
1814 disp
[ds
] = converted_image
;
1816 return converted_image
;
1818 bmp_src
.width
= decoder
->infoPng
.width
;
1819 bmp_src
.height
= decoder
->infoPng
.height
;
1820 bmp_src
.data
= (unsigned char *)converted_image
;
1822 bmp_dst
.width
= decoder
->infoPng
.width
/ds
;
1823 bmp_dst
.height
= decoder
->infoPng
.height
/ds
;
1824 bmp_dst
.data
= (unsigned char *)disp
[ds
];
1825 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1826 rb
->cpu_boost(true);
1827 smooth_resize_bitmap(&bmp_src
, &bmp_dst
);
1828 rb
->cpu_boost(false);
1830 smooth_resize_bitmap(&bmp_src
, &bmp_dst
);
1831 #endif /*HAVE_ADJUSTABLE_CPU_FREQ*/
1833 disp_buf
= (fb_data
*)((intptr_t)(disp
[ds
] + size
+ 3) & ~3);
1836 disp
[ds
] = converted_image
;
1837 return converted_image
;
1843 /* load, decode, display the image */
1844 int load_and_show(char* filename
)
1848 long time
=0; /* measured ticks */
1849 int cx
=0, cy
=0; /* view center */
1850 int w
, h
; /* used to center output */
1852 LodePNG_Decoder_init(&decoder
);
1854 rb
->lcd_clear_display();
1856 fd
= rb
->open(filename
, O_RDONLY
);
1859 rb
->snprintf(print
,sizeof(print
),"err opening %s:%d",filename
,fd
);
1860 rb
->splash(HZ
, print
);
1861 return PLUGIN_ERROR
;
1863 image_size
= rb
->filesize(fd
);
1864 memset(&disp
, 0, sizeof(disp
));
1866 DEBUGF("reading file '%s'\n", filename
);
1868 if (!running_slideshow
) {
1870 rb
->lcd_set_foreground(LCD_WHITE
);
1871 rb
->lcd_set_background(LCD_BLACK
);
1872 rb
->lcd_set_backdrop(NULL
);
1875 rb
->lcd_clear_display();
1876 rb
->snprintf(print
, sizeof(print
), "%s:", rb
->strrchr(filename
,'/')+1);
1877 rb
->lcd_puts(0, 0, print
);
1881 if (rb
->button_get(false) == PNG_MENU
) {
1882 decoder
.error
= PLUGIN_ABORT
;
1885 } else if (image_size
> memory_size
) {
1886 decoder
.error
= FILE_TOO_LARGE
;
1890 if (!running_slideshow
) {
1891 rb
->snprintf(print
, sizeof(print
), "loading %lu bytes", image_size
);
1892 rb
->lcd_puts(0, 1, print
);
1896 image
= memory_max
- image_size
+ 1;
1897 rb
->read(fd
, image
, image_size
);
1900 if (!running_slideshow
) {
1901 rb
->snprintf(print
, sizeof(print
), "decoding image");
1902 rb
->lcd_puts(0, 2, print
);
1906 else if (immediate_ata_off
) {
1907 /* running slideshow and time is long enough: power down disk */
1908 rb
->storage_sleep();
1912 decoder
.settings
.color_convert
= 1;
1913 decoder
.infoRaw
.color
.colorType
= 2;
1914 decoder
.infoRaw
.color
.bitDepth
= 8;
1916 if (rb
->button_get(false) == PNG_MENU
) {
1917 decoder
.error
= PLUGIN_ABORT
;
1919 LodePNG_inspect(&decoder
, image
, image_size
);
1922 if (!decoder
.error
) {
1924 if (!running_slideshow
) {
1925 rb
->snprintf(print
, sizeof(print
), "image %dx%d", decoder
.infoPng
.width
, decoder
.infoPng
.height
);
1926 rb
->lcd_puts(0, 2, print
);
1930 ds_max
= max_downscale(&decoder
); /* check display constraint */
1932 ds
= ds_max
; /* initials setting */
1934 if (!running_slideshow
)
1936 rb
->snprintf(print
, sizeof(print
), "decoding %d*%d",
1937 decoder
.infoPng
.width
, decoder
.infoPng
.height
);
1938 rb
->lcd_puts(0, 3, print
);
1942 /* the actual decoding */
1943 time
= *rb
->current_tick
;
1944 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1945 rb
->cpu_boost(true);
1946 LodePNG_decode(&decoder
, image
, image_size
, cb_progress
);
1947 rb
->cpu_boost(false);
1949 LodePNG_decode(&decoder
, image
, image_size
, cb_progress
);
1950 #endif /*HAVE_ADJUSTABLE_CPU_FREQ*/
1952 disp_buf
= (fb_data
*)((intptr_t)(converted_image
+ converted_image_size
+ 3) & ~3);
1953 ds_min
= min_downscale(&decoder
, memory_max
- (unsigned char*)disp_buf
); /* check memory constraint */
1956 // Could not resize the image
1957 ds_min
= ds
= ds_max
= 1;
1962 if (decoder
.error
== PLUGIN_ABORT
|| decoder
.error
== FILE_TOO_LARGE
) {
1964 if (immediate_ata_off
) {
1965 /* running slideshow and time is long enough: power down disk */
1966 rb
->storage_sleep();
1971 time
= *rb
->current_tick
- time
;
1973 if (!running_slideshow
&& !decoder
.error
)
1975 rb
->snprintf(print
, sizeof(print
), " %ld.%02ld sec ", time
/HZ
, time
%HZ
);
1976 rb
->lcd_getstringsize(print
, &w
, &h
); /* centered in progress bar */
1977 rb
->lcd_putsxy((LCD_WIDTH
- w
)/2, LCD_HEIGHT
- h
, print
);
1981 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
1982 if (plug_buf
&& (decoder
.error
== FILE_TOO_LARGE
|| decoder
.error
== OUT_OF_MEMORY
|| decoder
.error
== Z_MEM_ERROR
))
1984 rb
->lcd_setfont(FONT_SYSFIXED
);
1985 rb
->lcd_clear_display();
1986 rb
->snprintf(print
,sizeof(print
),"%s:",rb
->strrchr(filename
,'/')+1);
1987 rb
->lcd_puts(0,0,print
);
1988 rb
->lcd_puts(0,1,"Not enough plugin memory!");
1989 rb
->lcd_puts(0,2,"Zoom In: Stop playback.");
1991 rb
->lcd_puts(0,3,"Left/Right: Skip File.");
1992 rb
->lcd_puts(0,4,"Off: Quit.");
1994 rb
->lcd_setfont(FONT_UI
);
1996 rb
->button_clear_queue();
2000 int button
= rb
->button_get(true);
2005 memory
= rb
->plugin_get_audio_buffer((size_t *)&memory_size
);
2006 memory_max
= memory
+ memory_size
- 1;
2007 /*try again this file, now using the audio buffer */
2008 return PLUGIN_OTHER
;
2018 rb
->lcd_clear_display();
2019 return change_filename(DIR_PREV
);
2026 rb
->lcd_clear_display();
2027 return change_filename(DIR_NEXT
);
2031 if (rb
->default_event_handler_ex(button
, cleanup
, NULL
)
2032 == SYS_USB_CONNECTED
)
2033 return PLUGIN_USB_CONNECTED
;
2041 if (decoder
.error
) {
2043 switch (decoder
.error
) {
2045 rb
->splash(HZ
, "aborted");break;
2047 rb
->splash(HZ
, "png file smaller than a png header");break;
2049 rb
->splash(HZ
, "incorrect png signature");break;
2051 rb
->splash(HZ
, "first chunk is not IHDR");break;
2053 rb
->splash(HZ
, "chunk length too large");break;
2055 rb
->splash(HZ
, "illegal PNG color type or bpp");break;
2057 rb
->splash(HZ
, "illegal PNG compression method");break;
2059 rb
->splash(HZ
, "illegal PNG filter method");break;
2061 rb
->splash(HZ
, "illegal PNG interlace method");break;
2063 rb
->splash(HZ
, "chunk length of a chunk is too large or the chunk too small");break;
2065 rb
->splash(HZ
, "illegal PNG filter type encountered");break;
2067 rb
->splash(HZ
, "illegal bit depth for this color type given");break;
2069 rb
->splash(HZ
, "the palette is too big (more than 256 colors)");break;
2071 rb
->splash(HZ
, "more palette alpha values given in tRNS, than there are colors in the palette");break;
2073 rb
->splash(HZ
, "tRNS chunk has wrong size for greyscale image");break;
2075 rb
->splash(HZ
, "tRNS chunk has wrong size for RGB image");break;
2077 rb
->splash(HZ
, "tRNS chunk appeared while it was not allowed for this color type");break;
2079 rb
->splash(HZ
, "bKGD chunk has wrong size for palette image");break;
2081 rb
->splash(HZ
, "bKGD chunk has wrong size for greyscale image");break;
2083 rb
->splash(HZ
, "bKGD chunk has wrong size for RGB image");break;
2086 rb
->splash(HZ
, "value encountered in indexed image is larger than the palette size");break;
2088 rb
->splash(HZ
, "input file is empty");break;
2091 rb
->splash(HZ
, "Out of Memory");break;
2093 rb
->splash(HZ
, "invalid CRC");break;
2095 rb
->splash(HZ
, "conversion to unexisting or unsupported color type or bit depth");break;
2097 rb
->splash(HZ
, "png chunk too long");break;
2099 rb
->splash(HZ
, "unknown critical chunk");break;
2101 rb
->splash(HZ
, "invalid tIME chunk size");break;
2103 rb
->splash(HZ
, "invalid pHYs chunk size");break;
2104 case FILE_TOO_LARGE
:
2105 rb
->splash(HZ
, "File too large");break;
2107 rb
->splash(HZ
, decoder
.error_msg
);break;
2109 rb
->splashf(HZ
, "other error : %ld", decoder
.error
);break;
2112 if (decoder
.error
== PLUGIN_ABORT
) {
2115 file_pt
[curfile
] = NULL
;
2116 return change_filename(direction
);
2121 resized_image
= get_image(&decoder
); /* decode or fetch from cache */
2123 cx
= decoder
.infoPng
.width
/ds
/2; /* center the view */
2124 cy
= decoder
.infoPng
.height
/ds
/2;
2126 set_view(&decoder
, cx
, cy
);
2128 if (!running_slideshow
)
2130 rb
->snprintf(print
, sizeof(print
), "showing %dx%d",
2131 decoder
.infoPng
.width
/ds
, decoder
.infoPng
.height
/ds
);
2132 rb
->lcd_puts(0, 3, print
);
2136 rb
->lcd_clear_display();
2137 draw_image(&decoder
);
2140 /* drawing is now finished, play around with scrolling
2141 * until you press OFF or connect USB
2145 status
= scroll_bmp(&decoder
);
2146 if (status
== ZOOM_IN
)
2152 ds
/= 2; /* reduce downscaling to zoom in */
2153 get_view(&decoder
, &cx
, &cy
);
2154 cx
*= 2; /* prepare the position in the new image */
2156 if (disp
[ds
] != converted_image
|| ds
<= ds_min
) break;
2163 if (status
== ZOOM_OUT
)
2169 ds
*= 2; /* increase downscaling to zoom out */
2170 get_view(&decoder
, &cx
, &cy
);
2171 cx
/= 2; /* prepare the position in the new image */
2173 if (disp
[ds
] != converted_image
|| ds
>= ds_max
) break;
2181 rb
->lcd_clear_display();
2183 while (status
!= PLUGIN_OK
&& status
!= PLUGIN_USB_CONNECTED
2184 && status
!= PLUGIN_OTHER
);
2189 /******************** Plugin entry point *********************/
2191 enum plugin_status
plugin_start(const void* parameter
)
2195 old_backdrop
= rb
->lcd_get_backdrop();
2198 if (!parameter
) return PLUGIN_ERROR
;
2200 #if PLUGIN_BUFFER_SIZE >= MIN_MEM
2201 memory
= rb
->plugin_get_buffer((size_t *)&memory_size
);
2203 memory
= rb
->plugin_get_audio_buffer((size_t *)&memory_size
);
2206 rb
->strcpy(np_file
, parameter
);
2209 if (!entries
) return PLUGIN_ERROR
;
2211 #if (PLUGIN_BUFFER_SIZE >= MIN_MEM) && !defined(SIMULATOR)
2212 if (rb
->audio_status()) {
2215 memory
= rb
->plugin_get_audio_buffer((size_t *)&memory_size
);
2219 memory_max
= memory
+ memory_size
- 1;
2221 /* should be ok to just load settings since the plugin itself has
2222 just been loaded from disk and the drive should be spinning */
2223 configfile_load(PNG_CONFIGFILE
, png_config
,
2224 ARRAYLEN(png_config
), PNG_SETTINGS_MINVERSION
);
2225 old_settings
= png_settings
;
2227 /* Turn off backlight timeout */
2228 backlight_force_on(); /* backlight control in lib/helper.c */
2232 condition
= load_and_show(np_file
);
2233 }while (condition
!= PLUGIN_OK
&& condition
!= PLUGIN_USB_CONNECTED
2234 && condition
!= PLUGIN_ERROR
);
2236 if (rb
->memcmp(&png_settings
, &old_settings
, sizeof (png_settings
)))
2238 /* Just in case drive has to spin, keep it from looking locked */
2239 rb
->splash(0, "Saving Settings");
2240 configfile_save(PNG_CONFIGFILE
, png_config
,
2241 ARRAYLEN(png_config
), PNG_SETTINGS_VERSION
);
2244 #if !defined(SIMULATOR) && defined(HAVE_DISK_STORAGE)
2245 /* set back ata spindown time in case we changed it */
2246 rb
->storage_spindown(rb
->global_settings
->disk_spindown
);
2249 /* Turn on backlight timeout (revert to settings) */
2250 backlight_use_settings(); /* backlight control in lib/helper.c */