summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLode <lvandeve@gmail.com>2014-08-24 20:09:39 +0200
committerLode <lvandeve@gmail.com>2014-08-24 20:09:39 +0200
commitad8aab2a14a7b4dc01e33312d151b6f535f3c41e (patch)
tree0af431f67446a8e102263787a26a1b90ca5e9b1b
parent148987254da889a317e2d249c5f7f68849267de5 (diff)
add example and dev files to LodePNG repository
-rw-r--r--example_4bit_palette.cpp106
-rw-r--r--example_bmp2png.cpp134
-rw-r--r--example_decode.c113
-rw-r--r--example_decode.cpp95
-rw-r--r--example_encode.c116
-rw-r--r--example_encode.cpp97
-rw-r--r--example_gzip.cpp93
-rw-r--r--example_opengl.cpp162
-rw-r--r--example_optimize_png.cpp133
-rw-r--r--example_png2bmp.cpp132
-rw-r--r--example_png_info.cpp324
-rw-r--r--example_reencode.cpp76
-rw-r--r--example_sdl.c142
-rw-r--r--example_sdl.cpp132
-rw-r--r--lodepng_benchmark.cpp478
-rw-r--r--lodepng_unittest.cpp1658
-rw-r--r--lodepng_util.cpp653
-rw-r--r--lodepng_util.h151
-rw-r--r--pngdetail.cpp766
19 files changed, 5561 insertions, 0 deletions
diff --git a/example_4bit_palette.cpp b/example_4bit_palette.cpp
new file mode 100644
index 0000000..f495cca
--- /dev/null
+++ b/example_4bit_palette.cpp
@@ -0,0 +1,106 @@
+/*
+LodePNG Examples
+
+Copyright (c) 2005-2012 Lode Vandevenne
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+//g++ lodepng.cpp example_4bit_palette.cpp -ansi -pedantic -Wall -Wextra -O3
+
+
+
+/*
+LodePNG 4-bit palette example.
+This example encodes a 511x511 PNG with a 4-bit palette.
+Both image and palette contain sine waves, resulting in a sort of plasma.
+The 511 (rather than power of two 512) size is of course chosen on purpose to
+confirm that scanlines not filling up an entire byte size are working.
+
+NOTE: a PNG image with a translucent palette is perfectly valid. However there
+exist some programs that cannot correctly read those, including, surprisingly,
+Gimp 2.8 image editor (until you set mode to RGB).
+*/
+
+#include <cmath>
+#include <iostream>
+
+#include "lodepng.h"
+
+int main(int argc, char *argv[])
+{
+ //check if user gave a filename
+ if(argc < 2)
+ {
+ std::cout << "please provide a filename to save to" << std::endl;
+ return 0;
+ }
+
+ //create encoder and set settings and info (optional)
+ lodepng::State state;
+
+ //generate palette
+ for(int i = 0; i < 16; i++)
+ {
+ unsigned char r = 127 * (1 + std::sin(5 * i * 6.28318531 / 16));
+ unsigned char g = 127 * (1 + std::sin(2 * i * 6.28318531 / 16));
+ unsigned char b = 127 * (1 + std::sin(3 * i * 6.28318531 / 16));
+ unsigned char a = 63 * (1 + std::sin(8 * i * 6.28318531 / 16)) + 128; /*alpha channel of the palette (tRNS chunk)*/
+
+ //palette must be added both to input and output color mode, because in this
+ //sample both the raw image and the expected PNG image use that palette.
+ lodepng_palette_add(&state.info_png.color, r, g, b, a);
+ lodepng_palette_add(&state.info_raw, r, g, b, a);
+ }
+
+ //both the raw image and the encoded image must get colorType 3 (palette)
+ state.info_png.color.colortype = LCT_PALETTE; //if you comment this line, and create the above palette in info_raw instead, then you get the same image in a RGBA PNG.
+ state.info_png.color.bitdepth = 4;
+ state.info_raw.colortype = LCT_PALETTE;
+ state.info_raw.bitdepth = 4;
+ state.encoder.auto_convert = 0; //we specify ourselves exactly what output PNG color mode we want
+
+ //generate some image
+ const unsigned w = 511;
+ const unsigned h = 511;
+ std::vector<unsigned char> image;
+ image.resize((w * h * 4 + 7) / 8, 0);
+ for(unsigned y = 0; y < h; y++)
+ for(unsigned x = 0; x < w; x++)
+ {
+ size_t byte_index = (y * w + x) / 2;
+ bool byte_half = (y * w + x) % 2 == 1;
+
+ int color = (int)(4 * ((1 + std::sin(2.0 * 6.28318531 * x / (double)w))
+ + (1 + std::sin(2.0 * 6.28318531 * y / (double)h))) );
+
+ image[byte_index] |= (unsigned char)(color << (byte_half ? 0 : 4));
+ }
+
+ //encode and save
+ std::vector<unsigned char> buffer;
+ unsigned error = lodepng::encode(buffer, image.empty() ? 0 : &image[0], w, h, state);
+ if(error)
+ {
+ std::cout << "encoder error " << error << ": "<< lodepng_error_text(error) << std::endl;
+ return 0;
+ }
+ lodepng::save_file(buffer, argv[1]);
+}
diff --git a/example_bmp2png.cpp b/example_bmp2png.cpp
new file mode 100644
index 0000000..c6bdd24
--- /dev/null
+++ b/example_bmp2png.cpp
@@ -0,0 +1,134 @@
+/*
+LodePNG Examples
+
+Copyright (c) 2005-2010 Lode Vandevenne
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+/*
+Load a BMP image and convert it to a PNG image. This example also shows how
+to use other data with the same memory structure as BMP, such as the image
+format native to win32, GDI (HBITMAP, BITMAPINFO, ...) often encountered if
+you're programming for Windows in Visual Studio.
+
+This example only supports uncompressed 24-bit RGB or 32-bit RGBA bitmaps.
+For other types of BMP's, use a full fledged BMP decoder, or convert the
+bitmap to 24-bit or 32-bit format.
+
+NOTE: it overwrites the output file without warning if it exists!
+*/
+
+//g++ lodepng.cpp example_bmp2png.cpp -ansi -pedantic -Wall -Wextra -O3
+
+#include "lodepng.h"
+
+#include <iostream>
+
+//returns 0 if all went ok, non-0 if error
+//output image is always given in RGBA (with alpha channel), even if it's a BMP without alpha channel
+unsigned decodeBMP(std::vector<unsigned char>& image, unsigned& w, unsigned& h, const std::vector<unsigned char>& bmp)
+{
+ static const unsigned MINHEADER = 54; //minimum BMP header size
+
+ if(bmp.size() < MINHEADER) return -1;
+ if(bmp[0] != 'B' || bmp[1] != 'M') return 1; //It's not a BMP file if it doesn't start with marker 'BM'
+ unsigned pixeloffset = bmp[10] + 256 * bmp[11]; //where the pixel data starts
+ //read width and height from BMP header
+ w = bmp[18] + bmp[19] * 256;
+ h = bmp[22] + bmp[23] * 256;
+ //read number of channels from BMP header
+ if(bmp[28] != 24 && bmp[28] != 32) return 2; //only 24-bit and 32-bit BMPs are supported.
+ unsigned numChannels = bmp[28] / 8;
+
+ //The amount of scanline bytes is width of image times channels, with extra bytes added if needed
+ //to make it a multiple of 4 bytes.
+ unsigned scanlineBytes = w * numChannels;
+ if(scanlineBytes % 4 != 0) scanlineBytes = (scanlineBytes / 4) * 4 + 4;
+
+ unsigned dataSize = scanlineBytes * h;
+ if(bmp.size() < dataSize + pixeloffset) return 3; //BMP file too small to contain all pixels
+
+ image.resize(w * h * 4);
+
+ /*
+ There are 3 differences between BMP and the raw image buffer for LodePNG:
+ -it's upside down
+ -it's in BGR instead of RGB format (or BRGA instead of RGBA)
+ -each scanline has padding bytes to make it a multiple of 4 if needed
+ The 2D for loop below does all these 3 conversions at once.
+ */
+ for(unsigned y = 0; y < h; y++)
+ for(unsigned x = 0; x < w; x++)
+ {
+ //pixel start byte position in the BMP
+ unsigned bmpos = pixeloffset + (h - y - 1) * scanlineBytes + numChannels * x;
+ //pixel start byte position in the new raw image
+ unsigned newpos = 4 * y * w + 4 * x;
+ if(numChannels == 3)
+ {
+ image[newpos + 0] = bmp[bmpos + 2]; //R
+ image[newpos + 1] = bmp[bmpos + 1]; //G
+ image[newpos + 2] = bmp[bmpos + 0]; //B
+ image[newpos + 3] = 255; //A
+ }
+ else
+ {
+ image[newpos + 0] = bmp[bmpos + 3]; //R
+ image[newpos + 1] = bmp[bmpos + 2]; //G
+ image[newpos + 2] = bmp[bmpos + 1]; //B
+ image[newpos + 3] = bmp[bmpos + 0]; //A
+ }
+ }
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ if(argc < 3)
+ {
+ std::cout << "Please provice input PNG and output BMP file names" << std::endl;
+ return 0;
+ }
+
+ std::vector<unsigned char> bmp;
+ lodepng::load_file(bmp, argv[1]);
+ std::vector<unsigned char> image;
+ unsigned w, h;
+ unsigned error = decodeBMP(image, w, h, bmp);
+
+ if(error)
+ {
+ std::cout << "BMP decoding error " << error << std::endl;
+ return 0;
+ }
+
+ std::vector<unsigned char> png;
+ error = lodepng::encode(png, image, w, h);
+
+ if(error)
+ {
+ std::cout << "PNG encoding error " << error << ": " << lodepng_error_text(error) << std::endl;
+ return 0;
+ }
+
+ lodepng::save_file(png, argv[2]);
+
+}
diff --git a/example_decode.c b/example_decode.c
new file mode 100644
index 0000000..1ecec3d
--- /dev/null
+++ b/example_decode.c
@@ -0,0 +1,113 @@
+/*
+LodePNG Examples
+
+Copyright (c) 2005-2012 Lode Vandevenne
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include "lodepng.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/*
+3 ways to decode a PNG from a file to RGBA pixel data (and 2 in-memory ways).
+*/
+
+/*
+Example 1
+Decode from disk to raw pixels with a single function call
+*/
+void decodeOneStep(const char* filename)
+{
+ unsigned error;
+ unsigned char* image;
+ unsigned width, height;
+
+ error = lodepng_decode32_file(&image, &width, &height, filename);
+ if(error) printf("error %u: %s\n", error, lodepng_error_text(error));
+
+ /*use image here*/
+
+ free(image);
+}
+
+/*
+Example 2
+Load PNG file from disk to memory first, then decode to raw pixels in memory.
+*/
+void decodeTwoSteps(const char* filename)
+{
+ unsigned error;
+ unsigned char* image;
+ unsigned width, height;
+ unsigned char* png;
+ size_t pngsize;
+
+ lodepng_load_file(&png, &pngsize, filename);
+ error = lodepng_decode32(&image, &width, &height, png, pngsize);
+ if(error) printf("error %u: %s\n", error, lodepng_error_text(error));
+
+ free(png);
+
+ /*use image here*/
+
+ free(image);
+}
+
+/*
+Example 3
+Load PNG file from disk using a State, normally needed for more advanced usage.
+*/
+void decodeWithState(const char* filename)
+{
+ unsigned error;
+ unsigned char* image;
+ unsigned width, height;
+ unsigned char* png;
+ size_t pngsize;
+ LodePNGState state;
+
+ lodepng_state_init(&state);
+ /*optionally customize the state*/
+
+ lodepng_load_file(&png, &pngsize, filename);
+ error = lodepng_decode(&image, &width, &height, &state, png, pngsize);
+ if(error) printf("error %u: %s\n", error, lodepng_error_text(error));
+
+ free(png);
+
+ /*use image here*/
+ /*state contains extra information about the PNG such as text chunks, ...*/
+
+ lodepng_state_cleanup(&state);
+ free(image);
+}
+
+int main(int argc, char *argv[])
+{
+ const char* filename = argc > 1 ? argv[1] : "test.png";
+
+ decodeOneStep(filename);
+
+ return 0;
+}
+
diff --git a/example_decode.cpp b/example_decode.cpp
new file mode 100644
index 0000000..4eee6b8
--- /dev/null
+++ b/example_decode.cpp
@@ -0,0 +1,95 @@
+/*
+LodePNG Examples
+
+Copyright (c) 2005-2012 Lode Vandevenne
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include "lodepng.h"
+#include <iostream>
+
+/*
+3 ways to decode a PNG from a file to RGBA pixel data (and 2 in-memory ways).
+*/
+
+//g++ lodepng.cpp example_decode.cpp -ansi -pedantic -Wall -Wextra -O3
+
+
+//Example 1
+//Decode from disk to raw pixels with a single function call
+void decodeOneStep(const char* filename)
+{
+ std::vector<unsigned char> image; //the raw pixels
+ unsigned width, height;
+
+ //decode
+ unsigned error = lodepng::decode(image, width, height, filename);
+
+ //if there's an error, display it
+ if(error) std::cout << "decoder error " << error << ": " << lodepng_error_text(error) << std::endl;
+
+ //the pixels are now in the vector "image", 4 bytes per pixel, ordered RGBARGBA..., use it as texture, draw it, ...
+}
+
+//Example 2
+//Load PNG file from disk to memory first, then decode to raw pixels in memory.
+void decodeTwoSteps(const char* filename)
+{
+ std::vector<unsigned char> png;
+ std::vector<unsigned char> image; //the raw pixels
+ unsigned width, height;
+
+ //load and decode
+ lodepng::load_file(png, filename);
+ unsigned error = lodepng::decode(image, width, height, png);
+
+ //if there's an error, display it
+ if(error) std::cout << "decoder error " << error << ": " << lodepng_error_text(error) << std::endl;
+
+ //the pixels are now in the vector "image", 4 bytes per pixel, ordered RGBARGBA..., use it as texture, draw it, ...
+}
+
+//Example 3
+//Load PNG file from disk using a State, normally needed for more advanced usage.
+void decodeWithState(const char* filename)
+{
+ std::vector<unsigned char> png;
+ std::vector<unsigned char> image; //the raw pixels
+ unsigned width, height;
+ lodepng::State state; //optionally customize this one
+
+ lodepng::load_file(png, filename); //load the image file with given filename
+ unsigned error = lodepng::decode(image, width, height, state, png);
+
+ //if there's an error, display it
+ if(error) std::cout << "decoder error " << error << ": "<< lodepng_error_text(error) << std::endl;
+
+ //the pixels are now in the vector "image", 4 bytes per pixel, ordered RGBARGBA..., use it as texture, draw it, ...
+ //State state contains extra information about the PNG such as text chunks, ...
+}
+
+int main(int argc, char *argv[])
+{
+ const char* filename = argc > 1 ? argv[1] : "test.png";
+
+ decodeOneStep(filename);
+}
+
diff --git a/example_encode.c b/example_encode.c
new file mode 100644
index 0000000..0e0f8bf
--- /dev/null
+++ b/example_encode.c
@@ -0,0 +1,116 @@
+/*
+LodePNG Examples
+
+Copyright (c) 2005-2012 Lode Vandevenne
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include "lodepng.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/*
+3 ways to encode a PNG from RGBA pixel data to a file (and 2 in-memory ways).
+NOTE: this samples overwrite the file or test.png without warning!
+*/
+
+/*
+Example 1
+Encode from raw pixels to disk with a single function call
+The image argument has width * height RGBA pixels or width * height * 4 bytes
+*/
+void encodeOneStep(const char* filename, const unsigned char* image, unsigned width, unsigned height)
+{
+ /*Encode the image*/
+ unsigned error = lodepng_encode32_file(filename, image, width, height);
+
+ /*if there's an error, display it*/
+ if(error) printf("error %u: %s\n", error, lodepng_error_text(error));
+}
+
+/*
+Example 2
+Encode from raw pixels to an in-memory PNG file first, then write it to disk
+The image argument has width * height RGBA pixels or width * height * 4 bytes
+*/
+void encodeTwoSteps(const char* filename, const unsigned char* image, unsigned width, unsigned height)
+{
+ unsigned char* png;
+ size_t pngsize;
+
+ unsigned error = lodepng_encode32(&png, &pngsize, image, width, height);
+ if(!error) lodepng_save_file(png, pngsize, filename);
+
+ /*if there's an error, display it*/
+ if(error) printf("error %u: %s\n", error, lodepng_error_text(error));
+
+ free(png);
+}
+
+/*
+Example 3
+Save a PNG file to disk using a State, normally needed for more advanced usage.
+The image argument has width * height RGBA pixels or width * height * 4 bytes
+*/
+void encodeWithState(const char* filename, const unsigned char* image, unsigned width, unsigned height)
+{
+ unsigned error;
+ unsigned char* png;
+ size_t pngsize;
+ LodePNGState state;
+
+ lodepng_state_init(&state);
+ /*optionally customize the state*/
+
+ error = lodepng_encode(&png, &pngsize, image, width, height, &state);
+ if(!error) lodepng_save_file(png, pngsize, filename);
+
+ /*if there's an error, display it*/
+ if(error) printf("error %u: %s\n", error, lodepng_error_text(error));
+
+ lodepng_state_cleanup(&state);
+ free(png);
+}
+
+int main(int argc, char *argv[])
+{
+ const char* filename = argc > 1 ? argv[1] : "test.png";
+
+ /*generate some image*/
+ unsigned width = 512, height = 512;
+ unsigned char* image = malloc(width * height * 4);
+ unsigned x, y;
+ for(y = 0; y < height; y++)
+ for(x = 0; x < width; x++)
+ {
+ image[4 * width * y + 4 * x + 0] = 255 * !(x & y);
+ image[4 * width * y + 4 * x + 1] = x ^ y;
+ image[4 * width * y + 4 * x + 2] = x | y;
+ image[4 * width * y + 4 * x + 3] = 255;
+ }
+
+ /*run an example*/
+ encodeOneStep(filename, image, width, height);
+
+ free(image);
+ return 0;
+}
diff --git a/example_encode.cpp b/example_encode.cpp
new file mode 100644
index 0000000..31dd0b8
--- /dev/null
+++ b/example_encode.cpp
@@ -0,0 +1,97 @@
+/*
+LodePNG Examples
+
+Copyright (c) 2005-2012 Lode Vandevenne
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include "lodepng.h"
+#include <iostream>
+
+/*
+3 ways to encode a PNG from RGBA pixel data to a file (and 2 in-memory ways).
+NOTE: this samples overwrite the file or test.png without warning!
+*/
+
+//g++ lodepng.cpp example_encode.cpp -ansi -pedantic -Wall -Wextra -O3
+
+//Example 1
+//Encode from raw pixels to disk with a single function call
+//The image argument has width * height RGBA pixels or width * height * 4 bytes
+void encodeOneStep(const char* filename, std::vector<unsigned char>& image, unsigned width, unsigned height)
+{
+ //Encode the image
+ unsigned error = lodepng::encode(filename, image, width, height);
+
+ //if there's an error, display it
+ if(error) std::cout << "encoder error " << error << ": "<< lodepng_error_text(error) << std::endl;
+}
+
+//Example 2
+//Encode from raw pixels to an in-memory PNG file first, then write it to disk
+//The image argument has width * height RGBA pixels or width * height * 4 bytes
+void encodeTwoSteps(const char* filename, std::vector<unsigned char>& image, unsigned width, unsigned height)
+{
+ std::vector<unsigned char> png;
+
+ unsigned error = lodepng::encode(png, image, width, height);
+ if(!error) lodepng::save_file(png, filename);
+
+ //if there's an error, display it
+ if(error) std::cout << "encoder error " << error << ": "<< lodepng_error_text(error) << std::endl;
+}
+
+//Example 3
+//Save a PNG file to disk using a State, normally needed for more advanced usage.
+//The image argument has width * height RGBA pixels or width * height * 4 bytes
+void encodeWithState(const char* filename, std::vector<unsigned char>& image, unsigned width, unsigned height)
+{
+ std::vector<unsigned char> png;
+ lodepng::State state; //optionally customize this one
+
+ unsigned error = lodepng::encode(png, image, width, height, state);
+ if(!error) lodepng::save_file(png, filename);
+
+ //if there's an error, display it
+ if(error) std::cout << "encoder error " << error << ": "<< lodepng_error_text(error) << std::endl;
+}
+
+//saves image to filename given as argument. Warning, this overwrites the file without warning!
+int main(int argc, char *argv[])
+{
+ //NOTE: this sample will overwrite the file or test.png without warning!
+ const char* filename = argc > 1 ? argv[1] : "test.png";
+
+ //generate some image
+ unsigned width = 512, height = 512;
+ std::vector<unsigned char> image;
+ image.resize(width * height * 4);
+ for(unsigned y = 0; y < height; y++)
+ for(unsigned x = 0; x < width; x++)
+ {
+ image[4 * width * y + 4 * x + 0] = 255 * !(x & y);
+ image[4 * width * y + 4 * x + 1] = x ^ y;
+ image[4 * width * y + 4 * x + 2] = x | y;
+ image[4 * width * y + 4 * x + 3] = 255;
+ }
+
+ encodeOneStep(filename, image, width, height);
+}
diff --git a/example_gzip.cpp b/example_gzip.cpp
new file mode 100644
index 0000000..b2ddf17
--- /dev/null
+++ b/example_gzip.cpp
@@ -0,0 +1,93 @@
+/*
+LodePNG Examples
+
+Copyright (c) 2005-2012 Lode Vandevenne
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include "lodepng.h"
+#include <iostream>
+#include <stdlib.h>
+
+/*
+Encodes given file as a gzip file.
+
+See also the gzip specification, RFC 1952: http://www.gzip.org/zlib/rfc-gzip.html
+*/
+
+//g++ lodepng.cpp example_gzip.cpp -ansi -pedantic -Wall -Wextra -O3
+
+//saves image to filename given as argument. Warning, this overwrites the file without warning!
+int main(int argc, char *argv[])
+{
+ if(argc < 2)
+ {
+ std::cout << "Please provide input filename (output is input with .gz)" << std::endl;
+ return 0;
+ }
+
+ //NOTE: this sample will overwrite the output file without warning!
+ std::string infilename = argv[1];
+ std::string outfilename = infilename + ".gz";
+
+ std::vector<unsigned char> in;
+ lodepng::load_file(in, infilename);
+
+ size_t outsize = 10;
+ unsigned char* out = (unsigned char*)malloc(outsize);
+ out[0] = 31; //ID1
+ out[1] = 139; //ID2
+ out[2] = 8; //CM
+ out[3] = 0; //FLG
+ //MTIME
+ out[4] = 0;
+ out[5] = 0;
+ out[6] = 0;
+ out[7] = 0;
+
+ out[8] = 2; //2 = slow, 4 = fast compression
+ out[9] = 255; //OS unknown
+
+ lodepng_deflate(&out, &outsize, &in[0], in.size(), &lodepng_default_compress_settings);
+
+ unsigned crc = lodepng_crc32(&in[0], in.size());
+
+ size_t footer = outsize;
+
+ outsize += 8;
+ out = (unsigned char*)realloc(out, outsize);
+
+ //CRC
+ out[footer + 0] = crc % 256;
+ out[footer + 1] = (crc >> 8) % 256;
+ out[footer + 2] = (crc >> 16) % 256;
+ out[footer + 3] = (crc >> 24) % 256;
+
+ //ISIZE
+ out[footer + 4] = in.size() % 256;
+ out[footer + 5] = (in.size() >> 8) % 256;
+ out[footer + 6] = (in.size() >> 16) % 256;
+ out[footer + 7] = (in.size() >> 24) % 256;
+
+ lodepng_save_file(out, outsize, outfilename.c_str());
+
+ free(out);
+}
diff --git a/example_opengl.cpp b/example_opengl.cpp
new file mode 100644
index 0000000..1879864
--- /dev/null
+++ b/example_opengl.cpp
@@ -0,0 +1,162 @@
+/*
+LodePNG Examples
+
+Copyright (c) 2005-2012 Lode Vandevenne
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+//Compile command for Linux:
+//g++ lodepng.cpp example_opengl.cpp -lSDL -lGL -O3
+
+/*
+LodePNG OpenGL example. Decodes a PNG and shows it in OpenGL. PNG filename
+should be given as a command line parameter.
+
+It's written for the most basic old OpenGL version, and a correction for non
+power of two textures had to be added.
+
+Only very few lines on the sample are about loading the PNG. Most of the
+sample lines show a way to render a texture in 2D in OpenGL.
+
+No fancy 3D graphics are shown, it only shows the image statically. The sample
+shows LodePNG can be used to load PNG images as textures in OpenGL.
+*/
+
+#include "lodepng.h"
+
+#include <iostream>
+#include <SDL/SDL.h>
+#include <GL/gl.h>
+
+int main(int argc, char *argv[])
+{
+ if(argc < 2)
+ {
+ std::cout << "Please provide a filename." << std::endl;
+ return 1;
+ }
+ const char* filename = argv[1];
+
+ // Load file and decode image.
+ std::vector<unsigned char> image;
+ unsigned width, height;
+ unsigned error = lodepng::decode(image, width, height, filename);
+
+ // If there's an error, display it.
+ if(error != 0)
+ {
+ std::cout << "error " << error << ": " << lodepng_error_text(error) << std::endl;
+ return 1;
+ }
+
+ // Here the PNG is loaded in "image". All the rest of the code is SDL and OpenGL stuff.
+
+ int screenw = width;
+ if(screenw > 1024) screenw = 1024;
+ int screenh = height;
+ if(screenh > 768) screenw = 768;
+
+ if(SDL_Init(SDL_INIT_VIDEO) < 0)
+ {
+ std::cout << "Error: Unable to init SDL: " << SDL_GetError() << std::endl;
+ return 1;
+ }
+
+ SDL_Surface* scr = SDL_SetVideoMode(screenw, screenh, 32, SDL_OPENGL);
+
+ if(scr == 0)
+ {
+ std::cout << "Error: Unable to set video. SDL error message: " << SDL_GetError() << std::endl;
+ return 1;
+ }
+
+ // The official code for "Setting Your Raster Position to a Pixel Location" (i.e. set up a camera for 2D screen)
+ glViewport(0, 0, screenw, screenh);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(0, screenw, screenh, 0, -1, 1);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ // Make some OpenGL properties better for 2D and enable alpha channel.
+ glDisable(GL_CULL_FACE);
+ glDisable(GL_DEPTH_TEST);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable(GL_BLEND);
+ glDisable(GL_ALPHA_TEST);
+
+ if(glGetError() != GL_NO_ERROR)
+ {
+ std::cout << "Error initing GL" << std::endl;
+ return 1;
+ }
+
+ // Texture size must be power of two for the primitive OpenGL version this is written for. Find next power of two.
+ size_t u2 = 1; while(u2 < width) u2 *= 2;
+ size_t v2 = 1; while(v2 < height) v2 *= 2;
+ // Ratio for power of two version compared to actual version, to render the non power of two image with proper size.
+ double u3 = (double)width / u2;
+ double v3 = (double)height / v2;
+
+ // Make power of two version of the image.
+ std::vector<unsigned char> image2(u2 * v2 * 4);
+ for(size_t y = 0; y < height; y++)
+ for(size_t x = 0; x < width; x++)
+ for(size_t c = 0; c < 4; c++)
+ {
+ image2[4 * u2 * y + 4 * x + c] = image[4 * width * y + 4 * x + c];
+ }
+
+ // Enable the texture for OpenGL.
+ glEnable(GL_TEXTURE_2D);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); //GL_NEAREST = no smoothing
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexImage2D(GL_TEXTURE_2D, 0, 4, u2, v2, 0, GL_RGBA, GL_UNSIGNED_BYTE, &image2[0]);
+
+ bool done = false;
+ SDL_Event event = {0};
+ glColor4ub(255, 255, 255, 255);
+
+ while(!done)
+ {
+ // Quit the loop when receiving quit event.
+ while(SDL_PollEvent(&event))
+ {
+ if(event.type == SDL_QUIT) done = 1;
+ }
+
+ // Draw the texture on a quad, using u3 and v3 to correct non power of two texture size.
+ glBegin(GL_QUADS);
+ glTexCoord2d( 0, 0); glVertex2f( 0, 0);
+ glTexCoord2d(u3, 0); glVertex2f(width, 0);
+ glTexCoord2d(u3, v3); glVertex2f(width, height);
+ glTexCoord2d( 0, v3); glVertex2f( 0, height);
+ glEnd();
+
+ // Redraw and clear screen.
+ SDL_GL_SwapBuffers();
+ glClearColor(0, 0, 0, 0);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ //Limit frames per second, to not heat up the CPU and GPU too much.
+ SDL_Delay(16);
+ }
+}
diff --git a/example_optimize_png.cpp b/example_optimize_png.cpp
new file mode 100644
index 0000000..ecf25cf
--- /dev/null
+++ b/example_optimize_png.cpp
@@ -0,0 +1,133 @@
+/*
+LodePNG Examples
+
+Copyright (c) 2005-2012 Lode Vandevenne
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+/*
+This example saves the PNG with the best compression LodePNG can do, and with
+unnecesary chunks removed. It tries out several combinations of settings and
+keeps the smallest one.
+
+NOTE: This is not as good as a true PNG optimizer like optipng or pngcrush.
+*/
+
+//g++ lodepng.cpp example_optimize_png.cpp -ansi -pedantic -Wall -Wextra -O3
+
+#include "lodepng.h"
+
+#include <iostream>
+
+int main(int argc, char *argv[])
+{
+ std::vector<unsigned char> image;
+ unsigned w, h;
+ std::vector<unsigned char> buffer;
+ unsigned error;
+
+ //check if user gave a filename
+ if(argc < 3)
+ {
+ std::cout << "please provide in and out filename" << std::endl;
+ return 0;
+ }
+
+ lodepng::load_file(buffer, argv[1]);
+ error = lodepng::decode(image, w, h, buffer);
+
+ if(error)
+ {
+ std::cout << "decoding error " << error << ": " << lodepng_error_text(error) << std::endl;
+ return 0;
+ }
+
+ size_t origsize = buffer.size();
+ std::cout << "Original size: " << origsize << " (" << (origsize / 1024) << "K)" << std::endl;
+ buffer.clear();
+
+ //Now encode as hard as possible with several filter types and window sizes
+
+ lodepng::State state;
+ state.encoder.filter_palette_zero = 0; //We try several filter types, including zero, allow trying them all on palette images too.
+ state.encoder.add_id = false; //Don't add LodePNG version chunk to save more bytes
+ state.encoder.text_compression = 1; //Not needed because we don't add text chunks, but this demonstrates another optimization setting
+ state.encoder.zlibsettings.nicematch = 258; //Set this to the max possible, otherwise it can hurt compression
+ state.encoder.zlibsettings.lazymatching = 1; //Definitely use lazy matching for better compression
+ state.encoder.zlibsettings.windowsize = 32768; //Use maximum possible window size for best compression
+
+ size_t bestsize = 0;
+ bool inited = false;
+
+ int beststrategy = 0;
+ LodePNGFilterStrategy strategies[4] = { LFS_ZERO, LFS_MINSUM, LFS_ENTROPY, LFS_BRUTE_FORCE };
+ std::string strategynames[4] = { "LFS_ZERO", "LFS_MINSUM", "LFS_ENTROPY", "LFS_BRUTE_FORCE" };
+
+ // min match 3 allows all deflate lengths. min match 6 is similar to "Z_FILTERED" of zlib.
+ int minmatches[2] = { 3, 6 };
+ int bestminmatch = 0;
+
+ int autoconverts[2] = { 0, 1 };
+ std::string autoconvertnames[2] = { "0", "1" };
+ int bestautoconvert = 0;
+
+ int bestblocktype = 0;
+
+ // Try out all combinations of everything
+ for(int i = 0; i < 4; i++) //filter strategy
+ for(int j = 0; j < 2; j++) //min match
+ for(int k = 0; k < 2; k++) //block type (for small images only)
+ for(int l = 0; l < 2; l++) //color convert strategy
+ {
+ if(bestsize > 3000 && (k > 0 || l > 0)) continue; /* these only make sense on small images */
+ std::vector<unsigned char> temp;
+ state.encoder.filter_strategy = strategies[i];
+ state.encoder.zlibsettings.minmatch = minmatches[j];
+ state.encoder.zlibsettings.btype = k == 0 ? 2 : 1;
+ state.encoder.auto_convert = autoconverts[l];
+ error = lodepng::encode(temp, image, w, h, state);
+
+ if(error)
+ {
+ std::cout << "encoding error " << error << ": " << lodepng_error_text(error) << std::endl;
+ return 0;
+ }
+
+ if(!inited || temp.size() < bestsize)
+ {
+ bestsize = temp.size();
+ beststrategy = i;
+ bestminmatch = state.encoder.zlibsettings.minmatch;
+ bestautoconvert = l;
+ bestblocktype = state.encoder.zlibsettings.btype;
+ temp.swap(buffer);
+ inited = true;
+ }
+ }
+
+ std::cout << "Chosen filter strategy: " << strategynames[beststrategy] << std::endl;
+ std::cout << "Chosen min match: " << bestminmatch << std::endl;
+ std::cout << "Chosen block type: " << bestblocktype << std::endl;
+ std::cout << "Chosen auto convert: " << autoconvertnames[bestautoconvert] << std::endl;
+
+ lodepng::save_file(buffer, argv[2]);
+ std::cout << "New size: " << buffer.size() << " (" << (buffer.size() / 1024) << "K)" << std::endl;
+}
diff --git a/example_png2bmp.cpp b/example_png2bmp.cpp
new file mode 100644
index 0000000..3fd8328
--- /dev/null
+++ b/example_png2bmp.cpp
@@ -0,0 +1,132 @@
+/*
+LodePNG Examples
+
+Copyright (c) 2005-2012 Lode Vandevenne
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include "lodepng.h"
+#include <iostream>
+
+/*
+This example converts a PNG file to a BMP file.
+NOTE: it overwrites the output file without warning if it exists!
+Give the PNG and the BMP file names as command line arguments.
+*/
+
+/*
+g++ lodepng.cpp example_png2bmp.cpp -Wall -Wextra -pedantic -ansi -lSDL -O3
+*/
+
+
+
+//Input image must be RGB buffer (3 bytes per pixel), but you can easily make it
+//support RGBA input and output by changing the inputChannels and/or outputChannels
+//in the function to 4.
+void encodeBMP(std::vector<unsigned char>& bmp, const unsigned char* image, int w, int h)
+{
+ //3 bytes per pixel used for both input and output.
+ int inputChannels = 3;
+ int outputChannels = 3;
+
+ //bytes 0-13
+ bmp.push_back('B'); bmp.push_back('M'); //0: bfType
+ bmp.push_back(0); bmp.push_back(0); bmp.push_back(0); bmp.push_back(0); //2: bfSize; size not yet known for now, filled in later.
+ bmp.push_back(0); bmp.push_back(0); //6: bfReserved1
+ bmp.push_back(0); bmp.push_back(0); //8: bfReserved2
+ bmp.push_back(54 % 256); bmp.push_back(54 / 256); bmp.push_back(0); bmp.push_back(0); //10: bfOffBits (54 header bytes)
+
+ //bytes 14-53
+ bmp.push_back(40); bmp.push_back(0); bmp.push_back(0); bmp.push_back(0); //14: biSize
+ bmp.push_back(w % 256); bmp.push_back(w / 256); bmp.push_back(0); bmp.push_back(0); //18: biWidth
+ bmp.push_back(h % 256); bmp.push_back(h / 256); bmp.push_back(0); bmp.push_back(0); //22: biHeight
+ bmp.push_back(1); bmp.push_back(0); //26: biPlanes
+ bmp.push_back(outputChannels * 8); bmp.push_back(0); //28: biBitCount
+ bmp.push_back(0); bmp.push_back(0); bmp.push_back(0); bmp.push_back(0); //30: biCompression
+ bmp.push_back(0); bmp.push_back(0); bmp.push_back(0); bmp.push_back(0); //34: biSizeImage
+ bmp.push_back(0); bmp.push_back(0); bmp.push_back(0); bmp.push_back(0); //38: biXPelsPerMeter
+ bmp.push_back(0); bmp.push_back(0); bmp.push_back(0); bmp.push_back(0); //42: biYPelsPerMeter
+ bmp.push_back(0); bmp.push_back(0); bmp.push_back(0); bmp.push_back(0); //46: biClrUsed
+ bmp.push_back(0); bmp.push_back(0); bmp.push_back(0); bmp.push_back(0); //50: biClrImportant
+
+ /*
+ Convert the input RGBRGBRGB pixel buffer to the BMP pixel buffer format. There are 3 differences with the input buffer:
+ -BMP stores the rows inversed, from bottom to top
+ -BMP stores the color channels in BGR instead of RGB order
+ -BMP requires each row to have a multiple of 4 bytes, so sometimes padding bytes are added between rows
+ */
+
+ int imagerowbytes = outputChannels * w;
+ imagerowbytes = imagerowbytes % 4 == 0 ? imagerowbytes : imagerowbytes + (4 - imagerowbytes % 4); //must be multiple of 4
+
+ for(int y = h - 1; y >= 0; y--) //the rows are stored inversed in bmp
+ {
+ int c = 0;
+ for(int x = 0; x < imagerowbytes; x++)
+ {
+ if(x < w * outputChannels)
+ {
+ int inc = c;
+ //Convert RGB(A) into BGR(A)
+ if(c == 0) inc = 2;
+ else if(c == 2) inc = 0;
+ bmp.push_back(image[inputChannels * (w * y + x / outputChannels) + inc]);
+ }
+ else bmp.push_back(0);
+ c++;
+ if(c >= outputChannels) c = 0;
+ }
+ }
+
+ // Fill in the size
+ bmp[2] = bmp.size() % 256;
+ bmp[3] = (bmp.size() / 256) % 256;
+ bmp[4] = (bmp.size() / 65536) % 256;
+ bmp[5] = bmp.size() / 16777216;
+}
+
+int main(int argc, char *argv[])
+{
+ if(argc < 3)
+ {
+ std::cout << "Please provice input PNG and output BMP file names" << std::endl;
+ return 0;
+ }
+ const char* infile = argv[1];
+ const char* outfile = argv[2];
+
+
+ std::vector<unsigned char> image; //the raw pixels
+ unsigned width, height;
+
+ unsigned error = lodepng::decode(image, width, height, infile, LCT_RGB, 8);
+
+ if(error)
+ {
+ std::cout << "error " << error << ": " << lodepng_error_text(error) << std::endl;
+ return 0;
+ }
+
+ std::vector<unsigned char> bmp;
+ encodeBMP(bmp, &image[0], width, height);
+
+ lodepng::save_file(bmp, outfile);
+}
diff --git a/example_png_info.cpp b/example_png_info.cpp
new file mode 100644
index 0000000..86f664a
--- /dev/null
+++ b/example_png_info.cpp
@@ -0,0 +1,324 @@
+/*
+LodePNG Examples
+
+Copyright (c) 2005-2012 Lode Vandevenne
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+//g++ lodepng.cpp example_png_info.cpp -ansi -pedantic -Wall -Wextra -lSDL -O3
+
+/*
+This sample shows a lot of information in the console about a PNG file,
+including color type, text chunks, the names of all chunks in the image,
+etc...
+*/
+
+
+#include "lodepng.h"
+#include <iostream>
+
+/*
+Display general info about the PNG.
+*/
+void displayPNGInfo(const LodePNGInfo& info)
+{
+ const LodePNGColorMode& color = info.color;
+
+ std::cout << "Compression method: " << info.compression_method << std::endl;
+ std::cout << "Filter method: " << info.filter_method << std::endl;
+ std::cout << "Interlace method: " << info.interlace_method << std::endl;
+ std::cout << "Color type: " << color.colortype << std::endl;
+ std::cout << "Bit depth: " << color.bitdepth << std::endl;
+ std::cout << "Bits per pixel: " << lodepng_get_bpp(&color) << std::endl;
+ std::cout << "Channels per pixel: " << lodepng_get_channels(&color) << std::endl;
+ std::cout << "Is greyscale type: " << lodepng_is_greyscale_type(&color) << std::endl;
+ std::cout << "Can have alpha: " << lodepng_can_have_alpha(&color) << std::endl;
+ std::cout << "Palette size: " << color.palettesize << std::endl;
+ std::cout << "Has color key: " << color.key_defined << std::endl;
+ if(color.key_defined)
+ {
+ std::cout << "Color key r: " << color.key_r << std::endl;
+ std::cout << "Color key g: " << color.key_g << std::endl;
+ std::cout << "Color key b: " << color.key_b << std::endl;
+ }
+ std::cout << "Texts: " << info.text_num << std::endl;
+ for(size_t i = 0; i < info.text_num; i++)
+ {
+ std::cout << "Text: " << info.text_keys[i] << ": " << info.text_strings[i] << std::endl << std::endl;
+ }
+ std::cout << "International texts: " << info.itext_num << std::endl;
+ for(size_t i = 0; i < info.itext_num; i++)
+ {
+ std::cout << "Text: "
+ << info.itext_keys[i] << ", "
+ << info.itext_langtags[i] << ", "
+ << info.itext_transkeys[i] << ": "
+ << info.itext_strings[i] << std::endl << std::endl;
+ }
+ std::cout << "Time defined: " << info.time_defined << std::endl;
+ if(info.time_defined)
+ {
+ const LodePNGTime& time = info.time;
+ std::cout << "year: " << time.year << std::endl;
+ std::cout << "month: " << time.month << std::endl;
+ std::cout << "day: " << time.day << std::endl;
+ std::cout << "hour: " << time.hour << std::endl;
+ std::cout << "minute: " << time.minute << std::endl;
+ std::cout << "second: " << time.second << std::endl;
+ }
+ std::cout << "Physics defined: " << info.phys_defined << std::endl;
+ if(info.phys_defined)
+ {
+ std::cout << "physics X: " << info.phys_x << std::endl;
+ std::cout << "physics Y: " << info.phys_y << std::endl;
+ std::cout << "physics unit: " << info.phys_unit << std::endl;
+ }
+}
+
+
+/*
+Display the names and sizes of all chunks in the PNG file.
+*/
+void displayChunkNames(const std::vector<unsigned char>& buffer)
+{
+ // Listing chunks is based on the original file, not the decoded png info.
+ const unsigned char *chunk, *begin, *end;
+ end = &buffer.back() + 1;
+ begin = chunk = &buffer.front() + 8;
+
+ std::cout << std::endl << "Chunks:" << std::endl;
+ std::cout << " type: length(s)";
+ std::string last_type;
+ while(chunk + 8 < end && chunk >= begin)
+ {
+ char type[5];
+ lodepng_chunk_type(type, chunk);
+ if(std::string(type).size() != 4)
+ {
+ std::cout << "this is probably not a PNG" << std::endl;
+ return;
+ }
+
+ if(last_type != type)
+ {
+ std::cout << std::endl;
+ std::cout << " " << type << ": ";
+ }
+ last_type = type;
+
+ std::cout << lodepng_chunk_length(chunk) << ", ";
+
+ chunk = lodepng_chunk_next_const(chunk);
+ }
+ std::cout << std::endl;
+}
+
+
+/*
+Show ASCII art preview of the image
+*/
+void displayAsciiArt(const std::vector<unsigned char>& image, unsigned w, unsigned h)
+{
+ if(w > 0 && h > 0)
+ {
+ std::cout << std::endl << "ASCII Art Preview: " << std::endl;
+ unsigned w2 = 48;
+ if(w < w2) w2 = w;
+ unsigned h2 = h * w2 / w;
+ h2 = (h2 * 2) / 3; //compensate for non-square characters in terminal
+ if(h2 > (w2 * 2)) h2 = w2 * 2; //avoid too large output
+
+ std::cout << '+';
+ for(unsigned x = 0; x < w2; x++) std::cout << '-';
+ std::cout << '+' << std::endl;
+ for(unsigned y = 0; y < h2; y++)
+ {
+ std::cout << "|";
+ for(unsigned x = 0; x < w2; x++)
+ {
+ unsigned x2 = x * w / w2;
+ unsigned y2 = y * h / h2;
+ int r = image[y2 * w * 4 + x2 * 4 + 0];
+ int g = image[y2 * w * 4 + x2 * 4 + 1];
+ int b = image[y2 * w * 4 + x2 * 4 + 2];
+ int a = image[y2 * w * 4 + x2 * 4 + 3];
+ int lightness = ((r + g + b) / 3) * a / 255;
+ int min = (r < g && r < b) ? r : (g < b ? g : b);
+ int max = (r > g && r > b) ? r : (g > b ? g : b);
+ int saturation = max - min;
+ int letter = 'i'; //i for grey, or r,y,g,c,b,m for colors
+ if(saturation > 32)
+ {
+ int h = lightness >= (min + max) / 2;
+ if(h) letter = (min == r ? 'c' : (min == g ? 'm' : 'y'));
+ else letter = (max == r ? 'r' : (max == g ? 'g' : 'b'));
+ }
+ int symbol = ' ';
+ if(lightness > 224) symbol = '@';
+ else if(lightness > 128) symbol = letter - 32;
+ else if(lightness > 32) symbol = letter;
+ else if(lightness > 16) symbol = '.';
+ std::cout << (char)symbol;
+ }
+ std::cout << "|";
+ std::cout << std::endl;
+ }
+ std::cout << '+';
+ for(unsigned x = 0; x < w2; x++) std::cout << '-';
+ std::cout << '+' << std::endl;
+ }
+}
+
+
+/*
+Show the filtertypes of each scanline in this PNG image.
+*/
+void displayFilterTypes(const std::vector<unsigned char>& buffer)
+{
+ //Get color type and interlace type
+ lodepng::State state;
+ unsigned w, h;
+ unsigned error;
+ error = lodepng_inspect(&w, &h, &state, &buffer[0], buffer.size());
+
+ if(error)
+ {
+ std::cout << "inspect error " << error << ": " << lodepng_error_text(error) << std::endl;
+ return;
+ }
+
+ if(state.info_png.interlace_method == 1)
+ {
+ std::cout << "showing filtertypes for interlaced PNG not supported by this example" << std::endl;
+ return;
+ }
+
+ //Read literal data from all IDAT chunks
+ const unsigned char *chunk, *begin, *end;
+ end = &buffer.back() + 1;
+ begin = chunk = &buffer.front() + 8;
+
+ std::vector<unsigned char> zdata;
+
+ while(chunk + 8 < end && chunk >= begin)
+ {
+ char type[5];
+ lodepng_chunk_type(type, chunk);
+ if(std::string(type).size() != 4)
+ {
+ std::cout << "this is probably not a PNG" << std::endl;
+ return;
+ }
+
+ if(std::string(type) == "IDAT")
+ {
+ const unsigned char* cdata = lodepng_chunk_data_const(chunk);
+ unsigned clength = lodepng_chunk_length(chunk);
+
+ for(unsigned i = 0; i < clength; i++)
+ {
+ zdata.push_back(cdata[i]);
+ }
+ }
+
+ chunk = lodepng_chunk_next_const(chunk);
+ }
+
+ //Decompress all IDAT data
+ std::vector<unsigned char> data;
+ error = lodepng::decompress(data, &zdata[0], zdata.size());
+
+ if(error)
+ {
+ std::cout << "decompress error " << error << ": " << lodepng_error_text(error) << std::endl;
+ return;
+ }
+
+ //A line is 1 filter byte + all pixels
+ size_t linebytes = 1 + lodepng_get_raw_size(w, 1, &state.info_png.color);
+
+ if(linebytes == 0)
+ {
+ std::cout << "error: linebytes is 0" << std::endl;
+ return;
+ }
+
+ std::cout << "Filter types: ";
+ for(size_t i = 0; i < data.size(); i += linebytes)
+ {
+ std::cout << (int)(data[i]) << " ";
+ }
+ std::cout << std::endl;
+
+}
+
+
+/*
+Main
+*/
+int main(int argc, char *argv[]) /*list the chunks*/
+{
+ if(argc < 2)
+ {
+ std::cout << "Please provide a filename to preview" << std::endl;
+ return 0;
+ }
+ const char* filename = argv[1];
+
+ std::vector<unsigned char> buffer;
+ std::vector<unsigned char> image;
+ unsigned w, h;
+
+ lodepng::load_file(buffer, filename); //load the image file with given filename
+
+ lodepng::State state;
+ unsigned error = lodepng::decode(image, w, h, state, buffer);
+
+ if(error)
+ {
+ std::cout << "decoder error " << error << ": " << lodepng_error_text(error) << std::endl;
+ return 0;
+ }
+
+ std::cout << "Filesize: " << buffer.size() << " (" << buffer.size() / 1024 << "K)" << std::endl;
+ std::cout << "Width: " << w << std::endl;
+ std::cout << "Height: " << h << std::endl;
+ std::cout << "Num pixels: " << w * h << std::endl;
+
+ if(w > 0 && h > 0)
+ {
+ std::cout << "Top left pixel color:"
+ << " r: " << (int)image[0]
+ << " g: " << (int)image[1]
+ << " b: " << (int)image[2]
+ << " a: " << (int)image[3]
+ << std::endl;
+ }
+
+
+ displayPNGInfo(state.info_png);
+ std::cout << std::endl;
+ displayChunkNames(buffer);
+ std::cout << std::endl;
+ displayFilterTypes(buffer);
+ std::cout << std::endl;
+ displayAsciiArt(image, w, h);
+}
diff --git a/example_reencode.cpp b/example_reencode.cpp
new file mode 100644
index 0000000..d438612
--- /dev/null
+++ b/example_reencode.cpp
@@ -0,0 +1,76 @@
+/*
+LodePNG Examples
+
+Copyright (c) 2005-2010 Lode Vandevenne
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+/*
+LodePNG decode-encode: decodes the image, then encodes it again, with all the
+same information, chunks, color types, etc... as the original image had.
+This sample shows how LodePNG can be used for a conforming PNG editor.
+*/
+
+//g++ lodepng.cpp example_reencode.cpp -ansi -pedantic -Wall -Wextra -lSDL -O3 -o reencode
+
+#include "lodepng.h"
+
+#include <iostream>
+
+int main(int argc, char *argv[])
+{
+ std::vector<unsigned char> image;
+ unsigned w, h;
+ std::vector<unsigned char> buffer;
+ lodepng::State state;
+ unsigned error;
+
+ //check if user gave a filename
+ if(argc < 3)
+ {
+ std::cout << "please provide in and out filename" << std::endl;
+ return 0;
+ }
+
+ state.decoder.color_convert = 0;
+ state.decoder.remember_unknown_chunks = 1; //make it reproduce even unknown chunks in the saved image
+
+ lodepng::load_file(buffer, argv[1]);
+ error = lodepng::decode(image, w, h, state, buffer);
+ if(error)
+ {
+ std::cout << "decoder error " << error << ": " << lodepng_error_text(error) << std::endl;
+ return 0;
+ }
+
+ buffer.clear();
+
+ state.encoder.text_compression = 1;
+
+ error = lodepng::encode(buffer, image, w, h, state);
+ if(error)
+ {
+ std::cout << "encoder error " << error << ": " << lodepng_error_text(error) << std::endl;
+ return 0;
+ }
+
+ lodepng::save_file(buffer, argv[2]);
+}
diff --git a/example_sdl.c b/example_sdl.c
new file mode 100644
index 0000000..e4ae3de
--- /dev/null
+++ b/example_sdl.c
@@ -0,0 +1,142 @@
+/*
+LodePNG Examples
+
+Copyright (c) 2005-2012 Lode Vandevenne
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+/*
+Compile command for Linux:
+gcc lodepng.c example_sdl.c -ansi -pedantic -Wall -Wextra -lSDL -O3 -o showpng
+
+*/
+
+/*
+LodePNG SDL example
+This example displays a PNG with a checkerboard pattern to show tranparency.
+It requires the SDL library to compile and run.
+If multiple filenames are given to the command line, it shows all of them.
+Press any key to see next image, or esc to quit.
+*/
+
+#include "lodepng.h"
+
+#include <SDL/SDL.h>
+
+/*shows image with SDL. Returns 1 if user wants to fully quit, 0 if user wants to see next image.*/
+int show(const char* filename)
+{
+ unsigned error;
+ unsigned char* image;
+ unsigned w, h, x, y;
+ SDL_Surface* scr;
+ SDL_Event event;
+ int done;
+ size_t jump = 1;
+
+ printf("showing %s\n", filename);
+
+ /*load the PNG in one function call*/
+ error = lodepng_decode32_file(&image, &w, &h, filename);
+
+ /*stop if there is an error*/
+ if(error)
+ {
+ printf("decoder error %u: %s\n", error, lodepng_error_text(error));
+ return 0;
+ }
+
+ /*avoid too large window size by downscaling large image*/
+
+ if(w / 1024 >= jump) jump = w / 1024 + 1;
+ if(h / 1024 >= jump) jump = h / 1024 + 1;
+
+ /*init SDL*/
+ if(SDL_Init(SDL_INIT_VIDEO) < 0)
+ {
+ printf("Error, SDL video init failed\n");
+ return 0;
+ }
+ scr = SDL_SetVideoMode(w / jump, h / jump, 32, SDL_HWSURFACE);
+ if(!scr)
+ {
+ printf("Error, no SDL screen\n");
+ return 0;
+ }
+ SDL_WM_SetCaption(filename, NULL); /*set window caption*/
+
+ /*plot the pixels of the PNG file*/
+ for(y = 0; y + jump - 1 < h; y += jump)
+ for(x = 0; x + jump - 1 < w; x += jump)
+ {
+ int checkerColor;
+ Uint32* bufp;
+ Uint32 r, g, b, a;
+
+ /*get RGBA components*/
+ r = image[4 * y * w + 4 * x + 0]; /*red*/
+ g = image[4 * y * w + 4 * x + 1]; /*green*/
+ b = image[4 * y * w + 4 * x + 2]; /*blue*/
+ a = image[4 * y * w + 4 * x + 3]; /*alpha*/
+
+ /*make translucency visible by placing checkerboard pattern behind image*/
+ checkerColor = 191 + 64 * (((x / 16) % 2) == ((y / 16) % 2));
+ r = (a * r + (255 - a) * checkerColor) / 255;
+ g = (a * g + (255 - a) * checkerColor) / 255;
+ b = (a * b + (255 - a) * checkerColor) / 255;
+
+ /*give the color value to the pixel of the screenbuffer*/
+ bufp = (Uint32 *)scr->pixels + (y * scr->pitch / 4) / jump + (x / jump);
+ *bufp = 65536 * r + 256 * g + b;
+ }
+
+ /*pause until you press escape and meanwhile redraw screen*/
+ done = 0;
+ while(done == 0)
+ {
+ while(SDL_PollEvent(&event))
+ {
+ if(event.type == SDL_QUIT) done = 2;
+ else if(SDL_GetKeyState(NULL)[SDLK_ESCAPE]) done = 2;
+ else if(event.type == SDL_KEYDOWN) done = 1; /*press any other key for next image*/
+ }
+ SDL_UpdateRect(scr, 0, 0, 0, 0); /*redraw screen*/
+ SDL_Delay(5); /*pause 5 ms so it consumes less processing power*/
+ }
+
+ /*cleanup*/
+ free(image);
+ SDL_Quit();
+ return done == 2 ? 1 : 0;
+}
+
+int main(int argc, char* argv[])
+{
+ int i;
+
+ if(argc <= 1) printf("Please enter PNG file name(s) to display\n");;
+
+ for(i = 1; i < argc; i++)
+ {
+ if(show(argv[i])) return 0;
+ }
+ return 0;
+}
diff --git a/example_sdl.cpp b/example_sdl.cpp
new file mode 100644
index 0000000..0c68ef5
--- /dev/null
+++ b/example_sdl.cpp
@@ -0,0 +1,132 @@
+/*
+LodePNG Examples
+
+Copyright (c) 2005-2012 Lode Vandevenne
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+//Compile command for Linux:
+//g++ lodepng.cpp example_sdl.cpp -lSDL -O3 -o showpng
+
+/*
+LodePNG SDL example
+This example displays a PNG with a checkerboard pattern to show tranparency.
+It requires the SDL library to compile and run.
+If multiple filenames are given to the command line, it shows all of them.
+Press any key to see next image, or esc to quit.
+*/
+
+#include "lodepng.h"
+
+#include <iostream>
+#include <SDL/SDL.h>
+
+int show(const std::string& caption, const unsigned char* rgba, unsigned w, unsigned h)
+{
+ //avoid too large window size by downscaling large image
+ unsigned jump = 1;
+ if(w / 1024 >= jump) jump = w / 1024 + 1;
+ if(h / 1024 >= jump) jump = h / 1024 + 1;
+
+ //init SDL
+ if(SDL_Init(SDL_INIT_VIDEO) < 0)
+ {
+ std::cout << "error, SDL video init failed" << std::endl;
+ return 0;
+ }
+ SDL_Surface* scr = SDL_SetVideoMode(w / jump, h / jump, 32, SDL_HWSURFACE);
+ if(!scr)
+ {
+ std::cout << "error, no SDL screen" << std::endl;
+ return 0;
+ }
+ SDL_WM_SetCaption(caption.c_str(), NULL); //set window caption
+
+ //plot the pixels of the PNG file
+ for(unsigned y = 0; y + jump - 1 < h; y += jump)
+ for(unsigned x = 0; x + jump - 1 < w; x += jump)
+ {
+ //get RGBA components
+ Uint32 r = rgba[4 * y * w + 4 * x + 0]; //red
+ Uint32 g = rgba[4 * y * w + 4 * x + 1]; //green
+ Uint32 b = rgba[4 * y * w + 4 * x + 2]; //blue
+ Uint32 a = rgba[4 * y * w + 4 * x + 3]; //alpha
+
+ //make translucency visible by placing checkerboard pattern behind image
+ int checkerColor = 191 + 64 * (((x / 16) % 2) == ((y / 16) % 2));
+ r = (a * r + (255 - a) * checkerColor) / 255;
+ g = (a * g + (255 - a) * checkerColor) / 255;
+ b = (a * b + (255 - a) * checkerColor) / 255;
+
+ //give the color value to the pixel of the screenbuffer
+ Uint32* bufp;
+ bufp = (Uint32 *)scr->pixels + (y * scr->pitch / 4) / jump + (x / jump);
+ *bufp = 65536 * r + 256 * g + b;
+ }
+
+ //pause until you press escape and meanwhile redraw screen
+ SDL_Event event;
+ int done = 0;
+ while(done == 0)
+ {
+ while(SDL_PollEvent(&event))
+ {
+ if(event.type == SDL_QUIT) done = 2;
+ else if(SDL_GetKeyState(NULL)[SDLK_ESCAPE]) done = 2;
+ else if(event.type == SDL_KEYDOWN) done = 1; //press any other key for next image
+ }
+ SDL_UpdateRect(scr, 0, 0, 0, 0); //redraw screen
+ SDL_Delay(5); //pause 5 ms so it consumes less processing power
+ }
+
+ SDL_Quit();
+ return done == 2 ? 1 : 0;
+}
+
+/*shows image with SDL. Returns 1 if user wants to fully quit, 0 if user wants to see next image.*/
+int showfile(const char* filename)
+{
+ std::cout << "showing " << filename << std::endl;
+
+ std::vector<unsigned char> buffer, image;
+ lodepng::load_file(buffer, filename); //load the image file with given filename
+ unsigned w, h;
+ unsigned error = lodepng::decode(image, w, h, buffer); //decode the png
+
+ //stop if there is an error
+ if(error)
+ {
+ std::cout << "decoder error " << error << ": " << lodepng_error_text(error) << std::endl;
+ return 0;
+ }
+
+ return show(filename, &image[0], w, h);
+}
+
+int main(int argc, char* argv[])
+{
+ if(argc <= 1) std::cout << "Please enter PNG file name(s) to display" << std::endl;
+
+ for(int i = 1; i < argc; i++)
+ {
+ if(showfile(argv[i])) return 0;
+ }
+}
diff --git a/lodepng_benchmark.cpp b/lodepng_benchmark.cpp
new file mode 100644
index 0000000..e4c0525
--- /dev/null
+++ b/lodepng_benchmark.cpp
@@ -0,0 +1,478 @@
+/*
+LodePNG Benchmark
+
+Copyright (c) 2005-2014 Lode Vandevenne
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+//g++ lodepng.cpp lodepng_benchmark.cpp -Wall -Wextra -pedantic -ansi -lSDL -O3
+//g++ lodepng.cpp lodepng_benchmark.cpp -Wall -Wextra -pedantic -ansi -lSDL -O3 && time ./a.out
+
+#include "lodepng.h"
+
+#include <cmath>
+#include <string>
+#include <vector>
+#include <iostream>
+#include <sstream>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <SDL/SDL.h> //SDL is used for timing.
+
+#define NUM_DECODE 5 //set to 0 for not benchmarking encoding at all, 1 for normal, higher for decoding multiple times to measure better
+
+double total_dec_time = 0;
+double total_enc_time = 0;
+size_t total_enc_size = 0;
+
+////////////////////////////////////////////////////////////////////////////////
+
+double getTime()
+{
+ return SDL_GetTicks() / 1000.0;
+}
+
+void fail()
+{
+ throw 1; //that's how to let a unittest fail
+}
+
+template<typename T, typename U>
+void assertEquals(const T& expected, const U& actual, const std::string& message = "")
+{
+ if(expected != (T)actual)
+ {
+ std::cout << "Error: Not equal! Expected " << expected << " got " << actual << "." << std::endl;
+ std::cout << "Message: " << message << std::endl;
+ fail();
+ }
+}
+
+void assertTrue(bool value, const std::string& message = "")
+{
+ if(!value)
+ {
+ std::cout << "Error: expected true." << std::endl;
+ std::cout << "Message: " << message << std::endl;
+ fail();
+ }
+}
+
+//Test image data
+struct Image
+{
+ std::vector<unsigned char> data;
+ unsigned width;
+ unsigned height;
+ LodePNGColorType colorType;
+ unsigned bitDepth;
+};
+
+//Utility for debug messages
+template<typename T>
+std::string valtostr(const T& val)
+{
+ std::ostringstream sstream;
+ sstream << val;
+ return sstream.str();
+}
+
+template<typename T>
+void printValue(const std::string& name, const T& value, const std::string& unit = "")
+{
+ std::cout << name << ": " << value << unit << std::endl;
+}
+
+template<typename T, typename U>
+void printValue(const std::string& name, const T& value, const std::string& s2, const U& value2, const std::string& unit = "")
+{
+ std::cout << name << ": " << value << s2 << value2 << unit << std::endl;
+}
+
+//Test LodePNG encoding and decoding the encoded result, using the C interface
+void doCodecTest(Image& image)
+{
+ unsigned char* encoded = 0;
+ size_t encoded_size = 0;
+ unsigned char* decoded = 0;
+ unsigned decoded_w;
+ unsigned decoded_h;
+
+ double t_enc0 = getTime();
+
+ unsigned error_enc = lodepng_encode_memory(&encoded, &encoded_size, &image.data[0],
+ image.width, image.height, image.colorType, image.bitDepth);
+
+ double t_enc1 = getTime();
+
+ assertEquals(0, error_enc, "encoder error C");
+
+ double t_dec0 = getTime();
+ for(int i = 0; i < NUM_DECODE; i++)
+ {
+ unsigned error_dec = lodepng_decode_memory(&decoded, &decoded_w, &decoded_h,
+ encoded, encoded_size, image.colorType, image.bitDepth);
+ assertEquals(0, error_dec, "decoder error C");
+ }
+ double t_dec1 = getTime();
+
+
+ assertEquals(image.width, decoded_w);
+ assertEquals(image.height, decoded_h);
+
+ printValue("encoding time", t_enc1 - t_enc0, "s");
+ std::cout << "compression: " << ((double)(encoded_size) / (double)(image.data.size())) * 100 << "%"
+ << " ratio: " << ((double)(image.data.size()) / (double)(encoded_size))
+ << " size: " << encoded_size << std::endl;
+ total_enc_size += encoded_size;
+ total_enc_time += (t_enc1 - t_enc0);
+
+ if(NUM_DECODE> 0) printValue("decoding time", t_dec1 - t_dec0, "/", NUM_DECODE, " s");
+ total_dec_time += (t_dec1 - t_dec0);
+
+ std::cout << std::endl;
+
+ //LodePNG_saveFile(encoded, encoded_size, "test.png");
+
+ free(encoded);
+ free(decoded);
+}
+
+static const int IMGSIZE = 4096;
+
+void testPatternSine()
+{
+ std::cout << "sine pattern" << std::endl;
+
+ /*
+ There's something annoying about this pattern: it encodes worse, slower and with worse compression,
+ when adjusting the parameters, while all other images go faster and higher compression, and vice versa.
+ It responds opposite to optimizations...
+ */
+
+ Image image;
+ int w = IMGSIZE / 2;
+ int h = IMGSIZE / 2;
+ image.width = w;
+ image.height = h;
+ image.colorType = LCT_RGBA;
+ image.bitDepth = 8;
+ image.data.resize(w * h * 4);
+ for(int y = 0; y < h; y++)
+ for(int x = 0; x < w; x++)
+ {
+ //pattern 1
+ image.data[4 * w * y + 4 * x + 0] = (unsigned char)(127 * (1 + std::sin(( x * x + y * y) / (w * h / 8.0))));
+ image.data[4 * w * y + 4 * x + 1] = (unsigned char)(127 * (1 + std::sin(((w - x - 1) * (w - x - 1) + y * y) / (w * h / 8.0))));
+ image.data[4 * w * y + 4 * x + 2] = (unsigned char)(127 * (1 + std::sin(( x * x + (h - y - 1) * (h - y - 1)) / (w * h / 8.0))));
+ image.data[4 * w * y + 4 * x + 3] = (unsigned char)(127 * (1 + std::sin(((w - x - 1) * (w - x - 1) + (h - y - 1) * (h - y - 1)) / (w * h / 8.0))));
+ }
+
+ doCodecTest(image);
+}
+
+void testPatternSineNoAlpha()
+{
+ std::cout << "sine pattern w/o alpha" << std::endl;
+
+ /*
+ There's something annoying about this pattern: it encodes worse, slower and with worse compression,
+ when adjusting the parameters, while all other images go faster and higher compression, and vice versa.
+ It responds opposite to optimizations...
+ */
+
+ Image image;
+ int w = IMGSIZE / 2;
+ int h = IMGSIZE / 2;
+ image.width = w;
+ image.height = h;
+ image.colorType = LCT_RGB;
+ image.bitDepth = 8;
+ image.data.resize(w * h * 3);
+ for(int y = 0; y < h; y++)
+ for(int x = 0; x < w; x++)
+ {
+ //pattern 1
+ image.data[3 * w * y + 3 * x + 0] = (unsigned char)(127 * (1 + std::sin(( x * x + y * y) / (w * h / 8.0))));
+ image.data[3 * w * y + 3 * x + 1] = (unsigned char)(127 * (1 + std::sin(((w - x - 1) * (w - x - 1) + y * y) / (w * h / 8.0))));
+ image.data[3 * w * y + 3 * x + 2] = (unsigned char)(127 * (1 + std::sin(( x * x + (h - y - 1) * (h - y - 1)) / (w * h / 8.0))));
+ }
+
+ doCodecTest(image);
+}
+
+void testPatternXor()
+{
+ std::cout << "xor pattern" << std::endl;
+
+ Image image;
+ int w = IMGSIZE;
+ int h = IMGSIZE;
+ image.width = w;
+ image.height = h;
+ image.colorType = LCT_RGB;
+ image.bitDepth = 8;
+ image.data.resize(w * h * 3);
+ for(int y = 0; y < h; y++)
+ for(int x = 0; x < w; x++)
+ {
+ image.data[3 * w * y + 3 * x + 0] = x ^ y;
+ image.data[3 * w * y + 3 * x + 1] = x ^ y;
+ image.data[3 * w * y + 3 * x + 2] = x ^ y;
+ }
+
+ doCodecTest(image);
+}
+
+static unsigned int m_w = 1;
+static unsigned int m_z = 2;
+
+//"Multiply-With-Carry" generator of G. Marsaglia
+unsigned int getRandomUint()
+{
+ m_z = 36969 * (m_z & 65535) + (m_z >> 16);
+ m_w = 18000 * (m_w & 65535) + (m_w >> 16);
+ return (m_z << 16) + m_w; //32-bit result
+}
+
+void testPatternPseudoRan()
+{
+ std::cout << "pseudorandom pattern" << std::endl;
+
+ Image image;
+ int w = IMGSIZE / 2;
+ int h = IMGSIZE / 2;
+ image.width = w;
+ image.height = h;
+ image.colorType = LCT_RGB;
+ image.bitDepth = 8;
+ image.data.resize(w * h * 3);
+ for(int y = 0; y < h; y++)
+ for(int x = 0; x < w; x++)
+ {
+ unsigned int random = getRandomUint();
+ image.data[3 * w * y + 3 * x + 0] = random % 256;
+ image.data[3 * w * y + 3 * x + 1] = (random >> 8) % 256;
+ image.data[3 * w * y + 3 * x + 2] = (random >> 16) % 256;
+ }
+
+ doCodecTest(image);
+}
+
+void testPatternSineXor()
+{
+ std::cout << "sine+xor pattern" << std::endl;
+
+ Image image;
+ int w = IMGSIZE / 2;
+ int h = IMGSIZE / 2;
+ image.width = w;
+ image.height = h;
+ image.colorType = LCT_RGBA;
+ image.bitDepth = 8;
+ image.data.resize(w * h * 4);
+ for(int y = 0; y < h; y++)
+ for(int x = 0; x < w; x++)
+ {
+ //pattern 1
+ image.data[4 * w * y + 4 * x + 0] = (unsigned char)(127 * (1 + std::sin(( x * x + y * y) / (w * h / 8.0))));
+ image.data[4 * w * y + 4 * x + 1] = (unsigned char)(127 * (1 + std::sin(((w - x - 1) * (w - x - 1) + y * y) / (w * h / 8.0))));
+ image.data[4 * w * y + 4 * x + 2] = (unsigned char)(127 * (1 + std::sin(( x * x + (h - y - 1) * (h - y - 1)) / (w * h / 8.0))));
+ image.data[4 * w * y + 4 * x + 3] = (unsigned char)(127 * (1 + std::sin(((w - x - 1) * (w - x - 1) + (h - y - 1) * (h - y - 1)) / (w * h / 8.0))));
+ image.data[4 * w * y + 4 * x + 0] = image.data[4 * w * y + 4 * x + 0] / 2 + ((x ^ y) % 256) / 2;
+ image.data[4 * w * y + 4 * x + 1] = image.data[4 * w * y + 4 * x + 1] / 2 + ((x ^ y) % 256) / 2;
+ image.data[4 * w * y + 4 * x + 2] = image.data[4 * w * y + 4 * x + 2] / 2 + ((x ^ y) % 256) / 2;
+ image.data[4 * w * y + 4 * x + 3] = image.data[4 * w * y + 4 * x + 3] / 2 + ((x ^ y) % 256) / 2;
+ }
+
+ doCodecTest(image);
+}
+
+void testPatternGreyMandel()
+{
+ std::cout << "grey mandelbrot pattern" << std::endl;
+
+ Image image;
+ int w = IMGSIZE / 2;
+ int h = IMGSIZE / 2;
+ image.width = w;
+ image.height = h;
+ image.colorType = LCT_GREY;
+ image.bitDepth = 8;
+ image.data.resize(w * h);
+
+ double pr, pi;
+ double newRe, newIm, oldRe, oldIm;
+ // go to a position in the mandelbrot where there's lots of entropy
+ double zoom = 1779.8, moveX = -0.7431533999637661, moveY = -0.1394057861346605;
+ int maxIterations = 300;
+
+ for(int y = 0; y < h; y++)
+ for(int x = 0; x < w; x++)
+ {
+ pr = 1.5 * (x - w / 2) / (0.5 * zoom * w) + moveX;
+ pi = (y - h / 2) / (0.5 * zoom * h) + moveY;
+ newRe = newIm = oldRe = oldIm = 0; //these should start at 0,0
+ int i;
+ for(i = 0; i < maxIterations; i++)
+ {
+ oldRe = newRe;
+ oldIm = newIm;
+ newRe = oldRe * oldRe - oldIm * oldIm + pr;
+ newIm = 2 * oldRe * oldIm + pi;
+ if((newRe * newRe + newIm * newIm) > 4) break;
+ }
+ image.data[w * y + x] = i % 256;
+ }
+
+ doCodecTest(image);
+}
+
+void testPatternGreyMandelSmall()
+{
+ std::cout << "grey mandelbrot pattern" << std::endl;
+
+ Image image;
+ int w = IMGSIZE / 8;
+ int h = IMGSIZE / 8;
+ image.width = w;
+ image.height = h;
+ image.colorType = LCT_GREY;
+ image.bitDepth = 8;
+ image.data.resize(w * h);
+
+ double pr, pi;
+ double newRe, newIm, oldRe, oldIm;
+ // go to a position in the mandelbrot where there's lots of entropy
+ double zoom = 1779.8, moveX = -0.7431533999637661, moveY = -0.1394057861346605;
+ int maxIterations = 300;
+
+ for(int y = 0; y < h; y++)
+ for(int x = 0; x < w; x++)
+ {
+ pr = 1.5 * (x - w / 2) / (0.5 * zoom * w) + moveX;
+ pi = (y - h / 2) / (0.5 * zoom * h) + moveY;
+ newRe = newIm = oldRe = oldIm = 0; //these should start at 0,0
+ int i;
+ for(i = 0; i < maxIterations; i++)
+ {
+ oldRe = newRe;
+ oldIm = newIm;
+ newRe = oldRe * oldRe - oldIm * oldIm + pr;
+ newIm = 2 * oldRe * oldIm + pi;
+ if((newRe * newRe + newIm * newIm) > 4) break;
+ }
+ image.data[w * y + x] = i % 256;
+ }
+
+ doCodecTest(image);
+}
+
+void testPatternX()
+{
+ std::cout << "x pattern" << std::endl;
+
+ Image image;
+ int w = IMGSIZE;
+ int h = IMGSIZE;
+ image.width = w;
+ image.height = h;
+ image.colorType = LCT_GREY;
+ image.bitDepth = 8;
+ image.data.resize(w * h * 4);
+ for(int y = 0; y < h; y++)
+ for(int x = 0; x < w; x++)
+ {
+ image.data[w * y + x + 0] = x % 256;
+ }
+
+ doCodecTest(image);
+}
+
+void testPatternY()
+{
+ std::cout << "y pattern" << std::endl;
+
+ Image image;
+ int w = IMGSIZE;
+ int h = IMGSIZE;
+ image.width = w;
+ image.height = h;
+ image.colorType = LCT_GREY;
+ image.bitDepth = 8;
+ image.data.resize(w * h * 4);
+ for(int y = 0; y < h; y++)
+ for(int x = 0; x < w; x++)
+ {
+ image.data[w * y + x + 0] = y % 256;
+ }
+
+ doCodecTest(image);
+}
+
+void testPatternDisk(const std::string& filename)
+{
+ std::cout << "file " << filename << std::endl;
+
+ Image image;
+ image.colorType = LCT_RGB;
+ image.bitDepth = 8;
+ lodepng::decode(image.data, image.width, image.height, filename, image.colorType, image.bitDepth);
+
+ doCodecTest(image);
+}
+
+
+int main()
+{
+ std::cout << "NUM_DECODE: " << NUM_DECODE << std::endl;
+
+ //testPatternDisk("testdata/frymire.png");
+ //testPatternGreyMandel();
+
+ testPatternDisk("testdata/Ecce_homo_by_Hieronymus_Bosch.png");
+ testPatternDisk("testdata/ephyse_franco-chon-s-butchery.png");
+ testPatternDisk("testdata/jwbalsley_subway-rats.png");
+ testPatternDisk("testdata/Biomenace_complete.png");
+ testPatternDisk("testdata/frymire.png");
+ testPatternDisk("testdata/lena.png");
+ testPatternDisk("testdata/linedrawing.png");
+ //testPatternSine();
+ //testPatternSineNoAlpha();
+ testPatternXor();
+ testPatternPseudoRan();
+ //testPatternSineXor();
+ testPatternGreyMandel();
+ //testPatternX();
+ //testPatternY();
+ //testPatternDisk("Data/purplesmall.png");
+
+ /*testPatternDisk("testdata/Ecce_homo_by_Hieronymus_Bosch.png");
+ testPatternSine();*/
+
+ std::cout << "Total decoding time: " << total_dec_time/NUM_DECODE << "s" << std::endl;
+ std::cout << "Total encoding time: " << total_enc_time << "s" << std::endl;
+ std::cout << "Total size: " << total_enc_size << std::endl;
+
+ std::cout << "benchmark done" << std::endl;
+}
diff --git a/lodepng_unittest.cpp b/lodepng_unittest.cpp
new file mode 100644
index 0000000..1e4a49c
--- /dev/null
+++ b/lodepng_unittest.cpp
@@ -0,0 +1,1658 @@
+/*
+LodePNG Unit Test
+
+Copyright (c) 2005-2014 Lode Vandevenne
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+//g++ lodepng.cpp lodepng_util.cpp lodepng_unittest.cpp -Wall -Wextra -Wsign-conversion -pedantic -ansi -O3
+
+/*
+Testing instructions:
+
+*) Compile with g++ with all warnings and run the unit test
+g++ lodepng.cpp lodepng_util.cpp lodepng_unittest.cpp -Wall -Wextra -Wshadow -pedantic -ansi -O3 && ./a.out
+
+*) Compile with pure ISO C90 and all warnings:
+mv lodepng.cpp lodepng.c ; gcc lodepng.c example_decode.c -ansi -pedantic -Wall -Wextra -O3 ; mv lodepng.c lodepng.cpp
+
+*) try lodepng_benchmark.cpp
+g++ lodepng.cpp lodepng_benchmark.cpp -Wall -Wextra -pedantic -ansi -lSDL -O3 && ./a.out
+
+*) Check if all examples compile without warnings:
+g++ lodepng.cpp example*.cpp -W -Wall -ansi -pedantic -O3 -c
+mv lodepng.cpp lodepng.c ; gcc lodepng.c example*.c -W -Wall -ansi -pedantic -O3 -c ; mv lodepng.c lodepng.cpp
+
+*) Check pngdetail.cpp:
+g++ lodepng.cpp lodepng_util.cpp pngdetail.cpp -W -Wall -ansi -pedantic -O3 -o pngdetail
+./pnginfo testdata/PngSuite/basi0g01.png
+
+*) Test compiling with some code sections with #defines disabled, for unused static function warnings etc...
+g++ lodepng.cpp -W -Wall -ansi -pedantic -O3 -c -DLODEPNG_NO_COMPILE_ZLIB
+g++ lodepng.cpp -W -Wall -ansi -pedantic -O3 -c -DLODEPNG_NO_COMPILE_PNG
+g++ lodepng.cpp -W -Wall -ansi -pedantic -O3 -c -DLODEPNG_NO_COMPILE_DECODER
+g++ lodepng.cpp -W -Wall -ansi -pedantic -O3 -c -DLODEPNG_NO_COMPILE_ENCODER
+g++ lodepng.cpp -W -Wall -ansi -pedantic -O3 -c -DLODEPNG_NO_COMPILE_DISK
+g++ lodepng.cpp -W -Wall -ansi -pedantic -O3 -c -DLODEPNG_NO_COMPILE_ANCILLARY_CHUNKS
+g++ lodepng.cpp -W -Wall -ansi -pedantic -O3 -c -DLODEPNG_NO_COMPILE_ERROR_TEXT
+g++ lodepng.cpp -W -Wall -ansi -pedantic -O3 -c -DLODEPNG_NO_COMPILE_CPP
+g++ lodepng.cpp -W -Wall -ansi -pedantic -O3 -c -DLODEPNG_NO_COMPILE_ZLIB -DLODEPNG_NO_COMPILE_DECODER
+g++ lodepng.cpp -W -Wall -ansi -pedantic -O3 -c -DLODEPNG_NO_COMPILE_ZLIB -DLODEPNG_NO_COMPILE_ENCODER
+g++ lodepng.cpp -W -Wall -ansi -pedantic -O3 -c -DLODEPNG_NO_COMPILE_PNG -DLODEPNG_NO_COMPILE_DECODER
+g++ lodepng.cpp -W -Wall -ansi -pedantic -O3 -c -DLODEPNG_NO_COMPILE_PNG -DLODEPNG_NO_COMPILE_ENCODER
+rm *.o
+
+*) analyze met clang:
+clang++ lodepng.cpp --analyze
+More verbose:
+clang++ --analyze -Xanalyzer -analyzer-output=text lodepng.cpp
+Or html, look under lodepng.plist dir afterwards and find the numbered locations in the pages:
+clang++ --analyze -Xanalyzer -analyzer-output=html lodepng.cpp
+
+*) check for memory leaks and vulnerabilities with valgrind
+comment out the large files tests because they're slow with valgrind
+g++ lodepng.cpp lodepng_util.cpp lodepng_unittest.cpp -Wall -Wextra -pedantic -ansi -g3
+valgrind --leak-check=full --track-origins=yes ./a.out
+
+*) remove "#include <iostream>" from lodepng.cpp if it's still in there
+cat lodepng.cpp | grep iostream
+
+*) check version dates in copyright message and "#define VERSION_STRING"
+
+*) check year in copyright message at top of all files as well as at bottom of lodepng.h
+
+*) check example_sdl.cpp with the png test suite images (the "x" ones are expected to show error)
+g++ lodepng.cpp example_sdl.cpp -Wall -Wextra -pedantic -ansi -O3 -lSDL -o showpng
+*/
+// ./showpng testdata/PngSuite/*.png
+/*
+
+*) strip trailing spaces
+
+*) check diff of lodepng.cpp and lodepng.h before submitting
+
+*/
+
+#include "lodepng.h"
+#include "lodepng_util.h"
+
+#include <cmath>
+#include <map>
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+////////////////////////////////////////////////////////////////////////////////
+
+void fail()
+{
+ throw 1; //that's how to let a unittest fail
+}
+
+template<typename T, typename U>
+void assertEquals(const T& expected, const U& actual, const std::string& message = "")
+{
+ if(expected != (T)actual)
+ {
+ std::cout << "Error: Not equal! Expected " << expected << " got " << (T)actual << ". "
+ << "Message: " << message << std::endl;
+ fail();
+ }
+}
+
+void assertTrue(bool value, const std::string& message = "")
+{
+ if(!value)
+ {
+ std::cout << "Error: expected true. " << "Message: " << message << std::endl;
+ fail();
+ }
+}
+
+//assert that no error
+void assertNoPNGError(unsigned error, const std::string& message = "")
+{
+ if(error)
+ {
+ std::string msg = (message == "") ? lodepng_error_text(error)
+ : message + std::string(": ") + lodepng_error_text(error);
+ assertEquals(0, error, msg);
+ }
+}
+
+void assertNoError(unsigned error)
+{
+ if(error)
+ {
+ assertEquals(0, error, "Expected no error");
+ }
+}
+
+#define STR_EXPAND(s) #s
+#define STR(s) STR_EXPAND(s)
+#define ASSERT_EQUALS(e, v) \
+{\
+ assertEquals(e, v, std::string() + "line " + STR(__LINE__) + ": " + STR(v) + " ASSERT_EQUALS(" + #e + ", " + #v + ")");\
+}
+
+static const std::string BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+
+
+//T and U can be std::string or std::vector<unsigned char>
+template<typename T, typename U>
+void toBase64(T& out, const U& in)
+{
+ for(size_t i = 0; i < in.size(); i += 3)
+ {
+ int v = 65536 * in[i];
+ if(i + 1 < in.size()) v += 256 * in[i + 1];
+ if(i + 2 < in.size()) v += in[i + 2];
+ out.push_back(BASE64[(v >> 18) & 0x3f]);
+ out.push_back(BASE64[(v >> 12) & 0x3f]);
+ if(i + 1 < in.size()) out.push_back(BASE64[(v >> 6) & 0x3f]);
+ else out.push_back('=');
+ if(i + 2 < in.size()) out.push_back(BASE64[(v >> 0) & 0x3f]);
+ else out.push_back('=');
+ }
+}
+
+int fromBase64(int v)
+{
+ if(v >= 'A' && v <= 'Z') return (v - 'A');
+ if(v >= 'a' && v <= 'z') return (v - 'a' + 26);
+ if(v >= '0' && v <= '9') return (v - '0' + 52);
+ if(v == '+') return 62;
+ if(v == '/') return 63;
+ return 0; //v == '='
+}
+
+//T and U can be std::string or std::vector<unsigned char>
+template<typename T, typename U>
+void fromBase64(T& out, const U& in)
+{
+ for(size_t i = 0; i + 3 < in.size(); i += 4)
+ {
+ int v = 262144 * fromBase64(in[i]) + 4096 * fromBase64(in[i + 1]) + 64 * fromBase64(in[i + 2]) + fromBase64(in[i + 3]);
+ out.push_back((v >> 16) & 0xff);
+ if(in[i + 3] != '=') out.push_back((v >> 8) & 0xff);
+ if(in[i + 2] != '=') out.push_back((v >> 0) & 0xff);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+//Test image data
+struct Image
+{
+ std::vector<unsigned char> data;
+ unsigned width;
+ unsigned height;
+ LodePNGColorType colorType;
+ unsigned bitDepth;
+};
+
+//Utility for debug messages
+template<typename T>
+std::string valtostr(const T& val)
+{
+ std::ostringstream sstream;
+ sstream << val;
+ return sstream.str();
+}
+
+//Get number of color channels for a given PNG color type
+unsigned getNumColorChannels(unsigned colorType)
+{
+ switch(colorType)
+ {
+ case 0: return 1; /*grey*/
+ case 2: return 3; /*RGB*/
+ case 3: return 1; /*palette*/
+ case 4: return 2; /*grey + alpha*/
+ case 6: return 4; /*RGBA*/
+ }
+ return 0; /*unexisting color type*/
+}
+
+//Generate a test image with some data in it, the contents of the data is unspecified,
+//except the content is not just one plain color, and not true random either to be compressible.
+void generateTestImage(Image& image, unsigned width, unsigned height, LodePNGColorType colorType = LCT_RGBA, unsigned bitDepth = 8)
+{
+ image.width = width;
+ image.height = height;
+ image.colorType = colorType;
+ image.bitDepth = bitDepth;
+
+ size_t bits = bitDepth * getNumColorChannels(colorType); //bits per pixel
+ size_t size = (width * height * bits + 7) / 8; //total image size in bytes
+ image.data.resize(size);
+ unsigned char value = 128;
+ for(size_t i = 0; i < size; i++)
+ {
+ image.data[i] = value++;
+ }
+}
+
+//Check that the decoded PNG pixels are the same as the pixels in the image
+void assertPixels(Image& image, const unsigned char* decoded, const std::string& message)
+{
+ for(size_t i = 0; i < image.data.size(); i++)
+ {
+ int byte_expected = image.data[i];
+ int byte_actual = decoded[i];
+
+ //last byte is special due to possible random padding bits which need not to be equal
+ if(i == image.data.size() - 1)
+ {
+ size_t numbits = getNumColorChannels(image.colorType) * image.bitDepth * image.width * image.height;
+ size_t padding = 8u - (numbits - 8u * (numbits / 8u));
+ if(padding != 8u)
+ {
+ //set all padding bits of both to 0
+ for(size_t j = 0; j < padding; j++)
+ {
+ byte_expected = (byte_expected & (~(1 << j))) % 256;
+ byte_actual = (byte_actual & (~(1 << j))) % 256;
+ }
+ }
+ }
+
+ assertEquals(byte_expected, byte_actual, message + " " + valtostr(i));
+ }
+}
+
+//Test LodePNG encoding and decoding the encoded result, using the C interface
+void doCodecTestC(Image& image)
+{
+ unsigned char* encoded = 0;
+ size_t encoded_size = 0;
+ unsigned char* decoded = 0;
+ unsigned decoded_w;
+ unsigned decoded_h;
+
+ struct OnExitScope
+ {
+ unsigned char* a;
+ unsigned char* b;
+ OnExitScope(unsigned char* ca, unsigned char* cb) : a(ca), b(cb) {}
+ ~OnExitScope()
+ {
+ free(a);
+ free(b);
+ }
+ } onExitScope(encoded, decoded);
+
+ unsigned error_enc = lodepng_encode_memory(&encoded, &encoded_size, &image.data[0],
+ image.width, image.height, image.colorType, image.bitDepth);
+
+ if(error_enc != 0) std::cout << "Error: " << lodepng_error_text(error_enc) << std::endl;
+ assertNoPNGError(error_enc, "encoder error C");
+
+ //if the image is large enough, compressing it should result in smaller size
+ if(image.data.size() > 512) assertTrue(encoded_size < image.data.size(), "compressed size");
+
+ unsigned error_dec = lodepng_decode_memory(&decoded, &decoded_w, &decoded_h,
+ encoded, encoded_size, image.colorType, image.bitDepth);
+
+ if(error_dec != 0) std::cout << "Error: " << lodepng_error_text(error_dec) << std::endl;
+ assertNoPNGError(error_dec, "decoder error C");
+
+ ASSERT_EQUALS(image.width, decoded_w);
+ ASSERT_EQUALS(image.height, decoded_h);
+ assertPixels(image, decoded, "Pixels C");
+
+ free(decoded);
+ free(encoded);
+}
+
+//Test LodePNG encoding and decoding the encoded result, using the C++ interface
+void doCodecTestCPP(Image& image)
+{
+ std::vector<unsigned char> encoded;
+ std::vector<unsigned char> decoded;
+ unsigned decoded_w;
+ unsigned decoded_h;
+
+ unsigned error_enc = lodepng::encode(encoded, image.data, image.width, image.height,
+ image.colorType, image.bitDepth);
+
+ assertNoPNGError(error_enc, "encoder error C++");
+
+ //if the image is large enough, compressing it should result in smaller size
+ if(image.data.size() > 512) assertTrue(encoded.size() < image.data.size(), "compressed size");
+
+ unsigned error_dec = lodepng::decode(decoded, decoded_w, decoded_h, encoded, image.colorType, image.bitDepth);
+
+ assertNoPNGError(error_dec, "decoder error C++");
+
+ ASSERT_EQUALS(image.width, decoded_w);
+ ASSERT_EQUALS(image.height, decoded_h);
+ ASSERT_EQUALS(image.data.size(), decoded.size());
+ assertPixels(image, &decoded[0], "Pixels C++");
+}
+
+//Test LodePNG encoding and decoding the encoded result
+void doCodecTest(Image& image)
+{
+ doCodecTestC(image);
+ doCodecTestCPP(image);
+}
+
+
+//Test LodePNG encoding and decoding using some image generated with the given parameters
+void codecTest(unsigned width, unsigned height, LodePNGColorType colorType = LCT_RGBA, unsigned bitDepth = 8)
+{
+ std::cout << "codec test " << width << " " << height << std::endl;
+ Image image;
+ generateTestImage(image, width, height, colorType, bitDepth);
+ doCodecTest(image);
+}
+
+std::string removeSpaces(const std::string& s)
+{
+ std::string result;
+ for(size_t i = 0; i < s.size(); i++) if(s[i] != ' ') result += s[i];
+ return result;
+}
+
+void bitStringToBytes(std::vector<unsigned char>& bytes, const std::string& bits_)
+{
+ std::string bits = removeSpaces(bits_);
+ bytes.resize((bits.size()) + 7 / 8);
+ for(size_t i = 0; i < bits.size(); i++)
+ {
+ size_t j = i / 8;
+ size_t k = i % 8;
+ char c = bits[i];
+ if(k == 0) bytes[j] = 0;
+ if(c == '1') bytes[j] |= (1 << (7 - k));
+ }
+}
+
+/*
+test color convert on a single pixel. Testing palette and testing color keys is
+not supported by this function. Pixel values given using bits in an std::string
+of 0's and 1's.
+*/
+void colorConvertTest(const std::string& bits_in, LodePNGColorType colorType_in, unsigned bitDepth_in,
+ const std::string& bits_out, LodePNGColorType colorType_out, unsigned bitDepth_out)
+{
+ std::cout << "color convert test " << bits_in << " - " << bits_out << std::endl;
+
+ std::vector<unsigned char> expected, actual, image;
+ bitStringToBytes(expected, bits_out);
+ actual.resize(expected.size());
+ bitStringToBytes(image, bits_in);
+ LodePNGColorMode mode_in, mode_out;
+ lodepng_color_mode_init(&mode_in);
+ lodepng_color_mode_init(&mode_out);
+ mode_in.colortype = colorType_in;
+ mode_in.bitdepth = bitDepth_in;
+ mode_out.colortype = colorType_out;
+ mode_out.bitdepth = bitDepth_out;
+ unsigned error = lodepng_convert(&actual[0], &image[0], &mode_out, &mode_in, 1, 1);
+
+ assertNoPNGError(error, "convert error");
+
+ for(size_t i = 0; i < expected.size(); i++)
+ {
+ assertEquals((int)expected[i], (int)actual[i], "byte " + valtostr(i));
+ }
+
+ lodepng_color_mode_cleanup(&mode_in);
+ lodepng_color_mode_cleanup(&mode_out);
+}
+
+void testOtherPattern1()
+{
+ std::cout << "codec other pattern 1" << std::endl;
+
+ Image image1;
+ size_t w = 192;
+ size_t h = 192;
+ image1.width = w;
+ image1.height = h;
+ image1.colorType = LCT_RGBA;
+ image1.bitDepth = 8;
+ image1.data.resize(w * h * 4u);
+ for(size_t y = 0; y < h; y++)
+ for(size_t x = 0; x < w; x++)
+ {
+ //pattern 1
+ image1.data[4u * w * y + 4u * x + 0u] = (unsigned char)(127 * (1 + std::sin(( x * x + y * y) / (w * h / 8.0))));
+ image1.data[4u * w * y + 4u * x + 1u] = (unsigned char)(127 * (1 + std::sin(((w - x - 1) * (w - x - 1) + y * y) / (w * h / 8.0))));
+ image1.data[4u * w * y + 4u * x + 2u] = (unsigned char)(127 * (1 + std::sin(( x * x + (h - y - 1) * (h - y - 1)) / (w * h / 8.0))));
+ image1.data[4u * w * y + 4u * x + 3u] = (unsigned char)(127 * (1 + std::sin(((w - x - 1) * (w - x - 1) + (h - y - 1) * (h - y - 1)) / (w * h / 8.0))));
+ }
+
+ doCodecTest(image1);
+}
+
+void testOtherPattern2()
+{
+ std::cout << "codec other pattern 2" << std::endl;
+
+ Image image1;
+ size_t w = 192;
+ size_t h = 192;
+ image1.width = w;
+ image1.height = h;
+ image1.colorType = LCT_RGBA;
+ image1.bitDepth = 8;
+ image1.data.resize(w * h * 4u);
+ for(size_t y = 0; y < h; y++)
+ for(size_t x = 0; x < w; x++)
+ {
+ image1.data[4u * w * y + 4u * x + 0u] = 255 * !(x & y);
+ image1.data[4u * w * y + 4u * x + 1u] = x ^ y;
+ image1.data[4u * w * y + 4u * x + 2u] = x | y;
+ image1.data[4u * w * y + 4u * x + 3u] = 255;
+ }
+
+ doCodecTest(image1);
+}
+
+void testPNGCodec()
+{
+ codecTest(1, 1);
+ codecTest(2, 2);
+ codecTest(1, 1, LCT_GREY, 1);
+ codecTest(7, 7, LCT_GREY, 1);
+ codecTest(127, 127);
+ codecTest(127, 127, LCT_GREY, 1);
+
+ testOtherPattern1();
+ testOtherPattern2();
+}
+
+//Tests some specific color conversions with specific color bit combinations
+void testColorConvert()
+{
+ //test color conversions to RGBA8
+ colorConvertTest("1", LCT_GREY, 1, "11111111 11111111 11111111 11111111", LCT_RGBA, 8);
+ colorConvertTest("10", LCT_GREY, 2, "10101010 10101010 10101010 11111111", LCT_RGBA, 8);
+ colorConvertTest("1001", LCT_GREY, 4, "10011001 10011001 10011001 11111111", LCT_RGBA, 8);
+ colorConvertTest("10010101", LCT_GREY, 8, "10010101 10010101 10010101 11111111", LCT_RGBA, 8);
+ colorConvertTest("10010101 11111110", LCT_GREY_ALPHA, 8, "10010101 10010101 10010101 11111110", LCT_RGBA, 8);
+ colorConvertTest("10010101 00000001 11111110 00000001", LCT_GREY_ALPHA, 16, "10010101 10010101 10010101 11111110", LCT_RGBA, 8);
+ colorConvertTest("01010101 00000000 00110011", LCT_RGB, 8, "01010101 00000000 00110011 11111111", LCT_RGBA, 8);
+ colorConvertTest("01010101 00000000 00110011 10101010", LCT_RGBA, 8, "01010101 00000000 00110011 10101010", LCT_RGBA, 8);
+ colorConvertTest("10101010 01010101 11111111 00000000 11001100 00110011", LCT_RGB, 16, "10101010 11111111 11001100 11111111", LCT_RGBA, 8);
+ colorConvertTest("10101010 01010101 11111111 00000000 11001100 00110011 11100111 00011000", LCT_RGBA, 16, "10101010 11111111 11001100 11100111", LCT_RGBA, 8);
+
+ //test color conversions to RGB8
+ colorConvertTest("1", LCT_GREY, 1, "11111111 11111111 11111111", LCT_RGB, 8);
+ colorConvertTest("10", LCT_GREY, 2, "10101010 10101010 10101010", LCT_RGB, 8);
+ colorConvertTest("1001", LCT_GREY, 4, "10011001 10011001 10011001", LCT_RGB, 8);
+ colorConvertTest("10010101", LCT_GREY, 8, "10010101 10010101 10010101", LCT_RGB, 8);
+ colorConvertTest("10010101 11111110", LCT_GREY_ALPHA, 8, "10010101 10010101 10010101", LCT_RGB, 8);
+ colorConvertTest("10010101 00000001 11111110 00000001", LCT_GREY_ALPHA, 16, "10010101 10010101 10010101", LCT_RGB, 8);
+ colorConvertTest("01010101 00000000 00110011", LCT_RGB, 8, "01010101 00000000 00110011", LCT_RGB, 8);
+ colorConvertTest("01010101 00000000 00110011 10101010", LCT_RGBA, 8, "01010101 00000000 00110011", LCT_RGB, 8);
+ colorConvertTest("10101010 01010101 11111111 00000000 11001100 00110011", LCT_RGB, 16, "10101010 11111111 11001100", LCT_RGB, 8);
+ colorConvertTest("10101010 01010101 11111111 00000000 11001100 00110011 11100111 00011000", LCT_RGBA, 16, "10101010 11111111 11001100", LCT_RGB, 8);
+
+ //test color conversions to RGBA16
+ colorConvertTest("1", LCT_GREY, 1, "11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111", LCT_RGBA, 16);
+ colorConvertTest("10", LCT_GREY, 2, "10101010 10101010 10101010 10101010 10101010 10101010 11111111 11111111", LCT_RGBA, 16);
+
+ //test greyscale color conversions
+ colorConvertTest("1", LCT_GREY, 1, "11111111", LCT_GREY, 8);
+ colorConvertTest("1", LCT_GREY, 1, "1111111111111111", LCT_GREY, 16);
+ colorConvertTest("0", LCT_GREY, 1, "00000000", LCT_GREY, 8);
+ colorConvertTest("0", LCT_GREY, 1, "0000000000000000", LCT_GREY, 16);
+ colorConvertTest("11", LCT_GREY, 2, "11111111", LCT_GREY, 8);
+ colorConvertTest("11", LCT_GREY, 2, "1111111111111111", LCT_GREY, 16);
+ colorConvertTest("10", LCT_GREY, 2, "10101010", LCT_GREY, 8);
+ colorConvertTest("10", LCT_GREY, 2, "1010101010101010", LCT_GREY, 16);
+ colorConvertTest("1000", LCT_GREY, 4, "10001000", LCT_GREY, 8);
+ colorConvertTest("1000", LCT_GREY, 4, "1000100010001000", LCT_GREY, 16);
+ colorConvertTest("10110101", LCT_GREY, 8, "1011010110110101", LCT_GREY, 16);
+ colorConvertTest("1011010110110101", LCT_GREY, 16, "10110101", LCT_GREY, 8);
+
+ //others
+ colorConvertTest("11111111 11111111 11111111 00000000 00000000 00000000", LCT_RGB, 1, "10", LCT_GREY, 1);
+}
+
+//This tests color conversions from any color model to any color model, with any bit depth
+//But it tests only with colors black and white, because that are the only colors every single model supports
+void testColorConvert2()
+{
+ std::cout << "testColorConvert2" << std::endl;
+ struct Combo
+ {
+ LodePNGColorType colortype;
+ unsigned bitdepth;
+ };
+
+ Combo combos[15] =
+ {
+ { LCT_GREY, 1},
+ { LCT_GREY, 2},
+ { LCT_GREY, 4},
+ { LCT_GREY, 8},
+ { LCT_GREY, 16},
+ { LCT_RGB, 8},
+ { LCT_RGB, 16},
+ { LCT_PALETTE, 1},
+ { LCT_PALETTE, 2},
+ { LCT_PALETTE, 4},
+ { LCT_PALETTE, 8},
+ { LCT_GREY_ALPHA, 8},
+ { LCT_GREY_ALPHA, 16},
+ { LCT_RGBA, 8},
+ { LCT_RGBA, 16},
+ };
+
+ lodepng::State state;
+ LodePNGColorMode& mode_in = state.info_png.color;
+ LodePNGColorMode& mode_out = state.info_raw;
+ LodePNGColorMode mode_8;
+ lodepng_color_mode_init(&mode_8);
+
+ for(size_t i = 0; i < 256; i++)
+ {
+ size_t j = i == 1 ? 255 : i;
+ lodepng_palette_add(&mode_in, j, j, j, 255);
+ lodepng_palette_add(&mode_out, j, j, j, 255);
+ }
+
+ for(size_t i = 0; i < 15; i++)
+ {
+ mode_in.colortype = combos[i].colortype;
+ mode_in.bitdepth = combos[i].bitdepth;
+
+ for(size_t j = 0; j < 15; j++)
+ {
+ mode_out.colortype = combos[i].colortype;
+ mode_out.bitdepth = combos[i].bitdepth;
+
+ unsigned char eight[36] = {
+ 0,0,0,255, 255,255,255,255,
+ 0,0,0,255, 255,255,255,255,
+ 255,255,255,255, 0,0,0,255,
+ 255,255,255,255, 255,255,255,255,
+ 0,0,0,255 }; //input in RGBA8
+ unsigned char in[72]; //custom input color type
+ unsigned char out[72]; //custom output color type
+ unsigned char eight2[36]; //back in RGBA8 after all conversions to check correctness
+ unsigned error = 0;
+
+ error |= lodepng_convert(in, eight, &mode_in, &mode_8, 3, 3);
+ if(!error) error |= lodepng_convert(out, in, &mode_out, &mode_in, 3, 3); //Test input to output type
+ if(!error) error |= lodepng_convert(eight2, out, &mode_8, &mode_out, 3, 3);
+
+ if(!error)
+ {
+ for(size_t k = 0; k < 36; k++)
+ {
+ if(eight[k] != eight2[k])
+ {
+ error = 99999;
+ break;
+ }
+ }
+ }
+
+ if(error)
+ {
+ std::cout << "Error " << error << " i: " << i << " j: " << j
+ << " colortype i: " << combos[i].colortype
+ << " bitdepth i: " << combos[i].bitdepth
+ << " colortype j: " << combos[j].colortype
+ << " bitdepth j: " << combos[j].bitdepth
+ << std::endl;
+ if(error != 99999) assertNoPNGError(error);
+ else fail();
+ }
+ }
+ }
+}
+
+//if compressible is true, the test will also assert that the compressed string is smaller
+void testCompressStringZlib(const std::string& text, bool compressible)
+{
+ if(text.size() < 500) std::cout << "compress test with text: " << text << std::endl;
+ else std::cout << "compress test with text length: " << text.size() << std::endl;
+
+ std::vector<unsigned char> in(text.size());
+ for(size_t i = 0; i < text.size(); i++) in[i] = (unsigned char)text[i];
+ unsigned char* out = 0;
+ size_t outsize = 0;
+ unsigned error = 0;
+
+ error = lodepng_zlib_compress(&out, &outsize, in.empty() ? 0 : &in[0], in.size(), &lodepng_default_compress_settings);
+ assertNoPNGError(error);
+ if(compressible) assertTrue(outsize < in.size());
+
+ unsigned char* out2 = 0;
+ size_t outsize2 = 0;
+
+ error = lodepng_zlib_decompress(&out2, &outsize2, out, outsize, &lodepng_default_decompress_settings);
+ assertNoPNGError(error);
+ ASSERT_EQUALS(outsize2, in.size());
+ for(size_t i = 0; i < in.size(); i++) ASSERT_EQUALS(in[i], out2[i]);
+
+ free(out);
+ free(out2);
+}
+
+void testCompressZlib()
+{
+ testCompressStringZlib("", false);
+ testCompressStringZlib("a", false);
+ testCompressStringZlib("aa", false);
+ testCompressStringZlib("ababababababababababababababababababababababababababababababababababababababababababab", true);
+ testCompressStringZlib("abaaaabaabbbaabbabbababbbbabababbbaabbbaaaabbbbabbbabbbaababbbbbaaabaabbabaaaabbbbbbab", true);
+ testCompressStringZlib("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab", true);
+ testCompressStringZlib("omnomnomnomnomnomnomnomnomnomnom", true);
+ testCompressStringZlib("the quick brown fox jumps over the lazy dog. the quick brown fox jumps over the lazy dog.", true);
+ testCompressStringZlib("abracadabra", false);
+ testCompressStringZlib("hello hello hello hello hello hello hello hello hello hello hello?", true);
+ testCompressStringZlib("WPgZX2D*um0H::,4/KU\"kt\"Ne\"#Qa.&#<aF9{jag]|{hv,IXez\
+\\DKn5zYdV{XxBi=n|1J-TwakWvp[b8|-kOcZ@QkAxJSMeZ0l&<*w0BP/CXM(LFH'", false);
+ testCompressStringZlib("asdfhlkhfafsduyfbasiuytfgbiasuidygiausygdifaubsydfsdf", false);
+ testCompressStringZlib("418541499849814614617987416457317375467441841687487", true);
+ testCompressStringZlib("3.141592653589793238462643383279502884197169399375105820974944592307816406286", true);
+ testCompressStringZlib("lodepng_zlib_decompress(&out2, &outsize2, out, outsize, &lodepng_default_decompress_settings);", true);
+}
+
+void testDiskCompressZlib(const std::string& filename)
+{
+ std::cout << "testDiskCompressZlib: File " << filename << std::endl;
+
+ std::vector<unsigned char> buffer;
+ lodepng::load_file(buffer, filename);
+ std::string f;
+ for(size_t i = 0; i < buffer.size(); i++) f += (char)buffer[i];
+ testCompressStringZlib(f, false);
+}
+
+void testDiskPNG(const std::string& filename)
+{
+ std::cout << "testDiskPNG: File " << filename << std::endl;
+
+ Image image;
+ image.colorType = LCT_RGB;
+ image.bitDepth = 8;
+ unsigned error = lodepng::decode(image.data, image.width, image.height, filename, image.colorType, image.bitDepth);
+ assertNoPNGError(error);
+
+ doCodecTest(image);
+}
+
+std::vector<unsigned> strtovector(const std::string& numbers)
+{
+ std::vector<unsigned> result;
+ std::stringstream ss(numbers);
+ unsigned i;
+ while(ss >> i) result.push_back(i);
+ return result;
+}
+
+void doTestHuffmanCodeLengths(const std::string& expectedstr, const std::string& counts, size_t bitlength)
+{
+ std::vector<unsigned> expected = strtovector(expectedstr);
+ std::vector<unsigned> count = strtovector(counts);
+ std::cout << "doTestHuffmanCodeLengths: " << counts << std::endl;
+ std::vector<unsigned> result(count.size());
+ unsigned error = lodepng_huffman_code_lengths(&result[0], &count[0], count.size(), bitlength);
+ assertNoPNGError(error, "errorcode");
+ std::stringstream ss1, ss2;
+ for(size_t i = 0; i < count.size(); i++)
+ {
+ ss1 << expected[i] << " ";
+ ss2 << result[i] << " ";
+ }
+ assertEquals(ss1.str(), ss2.str(), "value");
+}
+
+void testHuffmanCodeLengths()
+{
+ bool atleasttwo = true; //LodePNG generates at least two, instead of at least one, symbol
+ if(atleasttwo)
+ {
+ doTestHuffmanCodeLengths("1 1", "0 0", 16);
+ doTestHuffmanCodeLengths("1 1 0", "0 0 0", 16);
+ doTestHuffmanCodeLengths("1 1", "1 0", 16);
+ doTestHuffmanCodeLengths("1 1 0 0 0 0 0 0 0", "0 0 0 0 0 0 0 0 0", 16);
+ doTestHuffmanCodeLengths("1 1 0 0 0 0 0 0 0", "1 0 0 0 0 0 0 0 0", 16);
+ doTestHuffmanCodeLengths("1 1 0 0 0 0 0 0 0", "0 1 0 0 0 0 0 0 0", 16);
+ doTestHuffmanCodeLengths("1 0 0 0 0 0 0 0 1", "0 0 0 0 0 0 0 0 1", 16);
+ doTestHuffmanCodeLengths("0 0 0 0 0 0 0 1 1", "0 0 0 0 0 0 0 1 1", 16);
+ }
+ else
+ {
+ doTestHuffmanCodeLengths("1 0", "0 0", 16);
+ doTestHuffmanCodeLengths("1 0 0", "0 0 0", 16);
+ doTestHuffmanCodeLengths("1 0", "1 0", 16);
+ doTestHuffmanCodeLengths("1", "1", 16);
+ doTestHuffmanCodeLengths("1", "0", 16);
+ }
+ doTestHuffmanCodeLengths("1 1", "1 1", 16);
+ doTestHuffmanCodeLengths("1 1", "1 100", 16);
+ doTestHuffmanCodeLengths("2 2 1", "1 2 3", 16);
+ doTestHuffmanCodeLengths("2 1 2", "2 3 1", 16);
+ doTestHuffmanCodeLengths("1 2 2", "3 1 2", 16);
+ doTestHuffmanCodeLengths("3 3 2 1", "1 30 31 32", 16);
+ doTestHuffmanCodeLengths("2 2 2 2", "1 30 31 32", 2);
+ doTestHuffmanCodeLengths("5 5 4 4 4 3 3 1", "1 2 3 4 5 6 7 500", 16);
+}
+
+/*
+Create a PNG image with all known chunks (except only one of tEXt or zTXt) plus
+unknown chunks, and a palette.
+*/
+void createComplexPNG(std::vector<unsigned char>& png)
+{
+ unsigned w = 16, h = 17;
+ std::vector<unsigned char> image(w * h);
+ for(size_t i = 0; i < w * h; i++)
+ {
+ image[i] = i % 256;
+ }
+
+ lodepng::State state;
+ LodePNGInfo& info = state.info_png;
+ info.color.colortype = LCT_PALETTE;
+ info.color.bitdepth = 8;
+ state.info_raw.colortype = LCT_PALETTE;
+ state.info_raw.bitdepth = 8;
+ state.encoder.auto_convert = false;
+ state.encoder.text_compression = 1;
+ state.encoder.add_id = 1;
+ for(size_t i = 0; i < 256; i++)
+ {
+ lodepng_palette_add(&info.color, i, i, i, i);
+ lodepng_palette_add(&state.info_raw, i, i, i, i);
+ }
+
+ info.background_defined = 1;
+ info.background_r = 127;
+
+ lodepng_add_text(&info, "key0", "string0");
+ lodepng_add_text(&info, "key1", "string1");
+
+ lodepng_add_itext(&info, "ikey0", "ilangtag0", "itranskey0", "istring0");
+ lodepng_add_itext(&info, "ikey1", "ilangtag1", "itranskey1", "istring1");
+
+ info.time_defined = 1;
+ info.time.year = 2012;
+ info.time.month = 1;
+ info.time.day = 2;
+ info.time.hour = 3;
+ info.time.minute = 4;
+ info.time.second = 5;
+
+ info.phys_defined = 1;
+ info.phys_x = 1;
+ info.phys_y = 2;
+ info.phys_unit = 1;
+
+ lodepng_chunk_create(&info.unknown_chunks_data[0], &info.unknown_chunks_size[0], 3, "uNKa", (unsigned char*)"a00");
+ lodepng_chunk_create(&info.unknown_chunks_data[0], &info.unknown_chunks_size[0], 3, "uNKa", (unsigned char*)"a01");
+ lodepng_chunk_create(&info.unknown_chunks_data[1], &info.unknown_chunks_size[1], 3, "uNKb", (unsigned char*)"b00");
+ lodepng_chunk_create(&info.unknown_chunks_data[2], &info.unknown_chunks_size[2], 3, "uNKc", (unsigned char*)"c00");
+
+ unsigned error = lodepng::encode(png, &image[0], w, h, state);
+ assertNoPNGError(error);
+}
+
+std::string extractChunkNames(const std::vector<unsigned char>& png)
+{
+ const unsigned char* chunk = &png[8];
+ char name[5];
+ std::string result = "";
+ for(;;)
+ {
+ lodepng_chunk_type(name, chunk);
+ result += (std::string(" ") + name);
+ if(std::string(name) == "IEND") break;
+ chunk = lodepng_chunk_next_const(chunk);
+ assertTrue(chunk < &png.back(), "jumped out of chunks");
+ }
+ return result;
+}
+
+void testComplexPNG()
+{
+ std::cout << "testComplexPNG" << std::endl;
+
+ std::vector<unsigned char> png;
+ createComplexPNG(png);
+
+ lodepng::State state;
+ LodePNGInfo& info = state.info_png;
+ unsigned w, h;
+ std::vector<unsigned char> image;
+ unsigned error = lodepng::decode(image, w, h, state, &png[0], png.size());
+ assertNoPNGError(error);
+
+ ASSERT_EQUALS(16, w);
+ ASSERT_EQUALS(17, h);
+ ASSERT_EQUALS(1, info.background_defined);
+ ASSERT_EQUALS(127, info.background_r);
+ ASSERT_EQUALS(1, info.time_defined);
+ ASSERT_EQUALS(2012, info.time.year);
+ ASSERT_EQUALS(1, info.time.month);
+ ASSERT_EQUALS(2, info.time.day);
+ ASSERT_EQUALS(3, info.time.hour);
+ ASSERT_EQUALS(4, info.time.minute);
+ ASSERT_EQUALS(5, info.time.second);
+ ASSERT_EQUALS(1, info.phys_defined);
+ ASSERT_EQUALS(1, info.phys_x);
+ ASSERT_EQUALS(2, info.phys_y);
+ ASSERT_EQUALS(1, info.phys_unit);
+
+ std::string chunknames = extractChunkNames(png);
+ //std::string expectednames = " IHDR uNKa uNKa PLTE tRNS bKGD pHYs uNKb IDAT tIME tEXt tEXt tEXt iTXt iTXt uNKc IEND";
+ std::string expectednames = " IHDR uNKa uNKa PLTE tRNS bKGD pHYs uNKb IDAT tIME zTXt zTXt tEXt iTXt iTXt uNKc IEND";
+ ASSERT_EQUALS(expectednames, chunknames);
+
+ //TODO: test strings and unknown chunks too
+}
+
+//test that, by default, it chooses filter type zero for all scanlines if the image has a palette
+void testPaletteFilterTypesZero()
+{
+ std::cout << "testPaletteFilterTypesZero" << std::endl;
+
+ std::vector<unsigned char> png;
+ createComplexPNG(png);
+
+ std::vector<unsigned char> filterTypes;
+ lodepng::getFilterTypes(filterTypes, png);
+
+ ASSERT_EQUALS(17, filterTypes.size());
+ for(size_t i = 0; i < 17; i++) ASSERT_EQUALS(0, filterTypes[i]);
+}
+
+//tests that there are no crashes with auto color chooser in case of palettes with translucency etc...
+void testPaletteToPaletteConvert()
+{
+ std::cout << "testPaletteToPaletteConvert" << std::endl;
+ unsigned error;
+ unsigned w = 16, h = 16;
+ std::vector<unsigned char> image(w * h);
+ for(size_t i = 0; i < w * h; i++) image[i] = i % 256;
+ lodepng::State state;
+ LodePNGInfo& info = state.info_png;
+ info.color.colortype = state.info_raw.colortype = LCT_PALETTE;
+ info.color.bitdepth = state.info_raw.bitdepth = 8;
+ ASSERT_EQUALS(true, state.encoder.auto_convert);
+ for(size_t i = 0; i < 256; i++)
+ {
+ lodepng_palette_add(&info.color, i, i, i, i);
+ }
+ std::vector<unsigned char> png;
+ for(size_t i = 0; i < 256; i++)
+ {
+ lodepng_palette_add(&state.info_raw, i, i, i, i);
+ }
+ error = lodepng::encode(png, &image[0], w, h, state);
+ assertNoPNGError(error);
+}
+
+//for this test, you have to choose palette colors that cause LodePNG to actually use a palette,
+//so don't use all greyscale colors for example
+void doRGBAToPaletteTest(unsigned char* palette, size_t size, LodePNGColorType expectedType = LCT_PALETTE)
+{
+ std::cout << "testRGBToPaletteConvert " << size << std::endl;
+ unsigned error;
+ unsigned w = size, h = 257 /*LodePNG encodes no palette if image is too small*/;
+ std::vector<unsigned char> image(w * h * 4);
+ for(size_t i = 0; i < image.size(); i++) image[i] = palette[i % (size * 4)];
+ std::vector<unsigned char> png;
+ error = lodepng::encode(png, &image[0], w, h);
+ assertNoPNGError(error);
+ lodepng::State state;
+ std::vector<unsigned char> image2;
+ error = lodepng::decode(image2, w, h, state, png);
+ assertNoPNGError(error);
+ ASSERT_EQUALS(image.size(), image2.size());
+ for(size_t i = 0; i < image.size(); i++) ASSERT_EQUALS(image[i], image2[i]);
+
+ ASSERT_EQUALS(expectedType, state.info_png.color.colortype);
+ if(expectedType == LCT_PALETTE)
+ {
+
+ ASSERT_EQUALS(size, state.info_png.color.palettesize);
+ for(size_t i = 0; i < size * 4; i++) ASSERT_EQUALS(state.info_png.color.palette[i], image[i]);
+ }
+}
+
+void testRGBToPaletteConvert()
+{
+ unsigned char palette1[4] = {1,2,3,4};
+ doRGBAToPaletteTest(palette1, 1);
+ unsigned char palette2[8] = {1,2,3,4, 5,6,7,8};
+ doRGBAToPaletteTest(palette2, 2);
+ unsigned char palette3[12] = {1,1,1,255, 20,20,20,255, 20,20,21,255};
+ doRGBAToPaletteTest(palette3, 3);
+
+ std::vector<unsigned char> palette;
+ for(int i = 0; i < 256; i++)
+ {
+ palette.push_back(i);
+ palette.push_back(5);
+ palette.push_back(6);
+ palette.push_back(128);
+ }
+ doRGBAToPaletteTest(&palette[0], 256);
+ palette.push_back(5);
+ palette.push_back(6);
+ palette.push_back(7);
+ palette.push_back(8);
+ doRGBAToPaletteTest(&palette[0], 257, LCT_RGBA);
+}
+
+void testColorKeyConvert()
+{
+ std::cout << "testColorKeyConvert" << std::endl;
+ unsigned error;
+ unsigned w = 32, h = 32;
+ std::vector<unsigned char> image(w * h * 4);
+ for(size_t i = 0; i < w * h; i++)
+ {
+ image[i * 4 + 0] = i % 256;
+ image[i * 4 + 1] = i / 256;
+ image[i * 4 + 2] = 0;
+ image[i * 4 + 3] = i == 23 ? 0 : 255;
+ }
+ std::vector<unsigned char> png;
+ error = lodepng::encode(png, &image[0], w, h);
+ assertNoPNGError(error);
+
+ lodepng::State state;
+ std::vector<unsigned char> image2;
+ error = lodepng::decode(image2, w, h, state, png);
+ assertNoPNGError(error);
+ ASSERT_EQUALS(32, w);
+ ASSERT_EQUALS(32, h);
+ ASSERT_EQUALS(1, state.info_png.color.key_defined);
+ ASSERT_EQUALS(23, state.info_png.color.key_r);
+ ASSERT_EQUALS(0, state.info_png.color.key_g);
+ ASSERT_EQUALS(0, state.info_png.color.key_b);
+ ASSERT_EQUALS(image.size(), image2.size());
+ for(size_t i = 0; i < image.size(); i++)
+ {
+ ASSERT_EQUALS(image[i], image2[i]);
+ }
+}
+
+unsigned char flipBit(unsigned char c, int bitpos)
+{
+ return c ^ (1 << bitpos);
+}
+
+//Test various broken inputs. Returned errors are not checked, what is tested is
+//that is doesn't crash, and, when run with valgrind, no memory warnings are
+//given.
+void testFuzzing()
+{
+ std::cout << "testFuzzing" << std::endl;
+ std::vector<unsigned char> png;
+ createComplexPNG(png);
+ std::vector<unsigned char> broken = png;
+ std::vector<unsigned char> result;
+ std::map<unsigned, unsigned> errors;
+ unsigned w, h;
+ for(size_t i = 0; i < png.size(); i++)
+ {
+ result.clear();
+ broken[i] = ~png[i];
+ errors[lodepng::decode(result, w, h, broken)]++;
+ broken[i] = 0;
+ errors[lodepng::decode(result, w, h, broken)]++;
+ for(int j = 0; j < 8; j++)
+ {
+ broken[i] = flipBit(png[i], j);
+ errors[lodepng::decode(result, w, h, broken)]++;
+ }
+ broken[i] = 255;
+ errors[lodepng::decode(result, w, h, broken)]++;
+ broken[i] = png[i]; //fix it again for the next test
+ }
+ std::cout << "testFuzzing shrinking" << std::endl;
+ broken = png;
+ while(broken.size() > 0)
+ {
+ broken.resize(broken.size() - 1);
+ errors[lodepng::decode(result, w, h, broken)]++;
+ }
+
+ //For fun, print the number of each error
+ std::cout << "Fuzzing error code counts: ";
+ for(std::map<unsigned, unsigned>::iterator it = errors.begin(); it != errors.end(); ++it)
+ {
+ std::cout << it->first << ":" << it->second << ", ";
+ }
+ std::cout << std::endl;
+}
+
+void testCustomZlibCompress()
+{
+ std::cout << "testCustomZlibCompress" << std::endl;
+ Image image;
+ generateTestImage(image, 5, 5, LCT_RGBA, 8);
+
+ std::vector<unsigned char> encoded;
+ int customcontext = 5;
+
+ struct TestFun {
+ static unsigned custom_zlib(unsigned char**, size_t*,
+ const unsigned char*, size_t,
+ const LodePNGCompressSettings* settings)
+ {
+ ASSERT_EQUALS(5, *(int*)(settings->custom_context));
+ return 5555; //return a custom error code to prove this function was called
+ }
+ };
+
+ lodepng::State state;
+ state.encoder.zlibsettings.custom_zlib = TestFun::custom_zlib;
+ state.encoder.zlibsettings.custom_context = &customcontext;
+
+ unsigned error = lodepng::encode(encoded, image.data, image.width, image.height,
+ state);
+
+ ASSERT_EQUALS(5555, error);
+}
+
+void testCustomZlibCompress2()
+{
+ std::cout << "testCustomZlibCompress2" << std::endl;
+ Image image;
+ generateTestImage(image, 5, 5, LCT_RGBA, 8);
+
+ std::vector<unsigned char> encoded;
+
+ lodepng::State state;
+ state.encoder.zlibsettings.custom_zlib = lodepng_zlib_compress;
+
+ unsigned error = lodepng::encode(encoded, image.data, image.width, image.height,
+ state);
+ assertNoPNGError(error);
+
+ std::vector<unsigned char> decoded;
+ unsigned w, h;
+ state.decoder.zlibsettings.ignore_adler32 = 0;
+ state.decoder.ignore_crc = 0;
+ error = lodepng::decode(decoded, w, h, state, encoded);
+ assertNoPNGError(error);
+ ASSERT_EQUALS(5, w);
+ ASSERT_EQUALS(5, h);
+}
+
+void testCustomDeflate()
+{
+ std::cout << "testCustomDeflate" << std::endl;
+ Image image;
+ generateTestImage(image, 5, 5, LCT_RGBA, 8);
+
+ std::vector<unsigned char> encoded;
+ int customcontext = 5;
+
+ struct TestFun {
+ static unsigned custom_deflate(unsigned char**, size_t*,
+ const unsigned char*, size_t,
+ const LodePNGCompressSettings* settings)
+ {
+ ASSERT_EQUALS(5, *(int*)(settings->custom_context));
+ return 5555; //return a custom error code to prove this function was called
+ }
+ };
+
+ lodepng::State state;
+ state.encoder.zlibsettings.custom_deflate = TestFun::custom_deflate;
+ state.encoder.zlibsettings.custom_context = &customcontext;
+
+ unsigned error = lodepng::encode(encoded, image.data, image.width, image.height,
+ state);
+
+ ASSERT_EQUALS(5555, error);
+}
+
+void testCustomZlibDecompress()
+{
+ std::cout << "testCustomZlibDecompress" << std::endl;
+ Image image;
+ generateTestImage(image, 5, 5, LCT_RGBA, 8);
+
+ std::vector<unsigned char> encoded;
+
+ unsigned error_enc = lodepng::encode(encoded, image.data, image.width, image.height,
+ image.colorType, image.bitDepth);
+ assertNoPNGError(error_enc, "encoder error not expected");
+
+
+ std::vector<unsigned char> decoded;
+ unsigned w, h;
+ int customcontext = 5;
+
+ struct TestFun {
+ static unsigned custom_zlib(unsigned char**, size_t*,
+ const unsigned char*, size_t,
+ const LodePNGDecompressSettings* settings)
+ {
+ ASSERT_EQUALS(5, *(int*)(settings->custom_context));
+ return 5555; //return a custom error code to prove this function was called
+ }
+ };
+
+ lodepng::State state;
+ state.decoder.zlibsettings.custom_zlib = TestFun::custom_zlib;
+ state.decoder.zlibsettings.custom_context = &customcontext;
+ state.decoder.zlibsettings.ignore_adler32 = 0;
+ state.decoder.ignore_crc = 0;
+ unsigned error = lodepng::decode(decoded, w, h, state, encoded);
+
+ ASSERT_EQUALS(5555, error);
+}
+
+void testCustomInflate()
+{
+ std::cout << "testCustomInflate" << std::endl;
+ Image image;
+ generateTestImage(image, 5, 5, LCT_RGBA, 8);
+
+ std::vector<unsigned char> encoded;
+
+ unsigned error_enc = lodepng::encode(encoded, image.data, image.width, image.height,
+ image.colorType, image.bitDepth);
+ assertNoPNGError(error_enc, "encoder error not expected");
+
+
+ std::vector<unsigned char> decoded;
+ unsigned w, h;
+ int customcontext = 5;
+
+ struct TestFun {
+ static unsigned custom_inflate(unsigned char**, size_t*,
+ const unsigned char*, size_t,
+ const LodePNGDecompressSettings* settings)
+ {
+ ASSERT_EQUALS(5, *(int*)(settings->custom_context));
+ return 5555; //return a custom error code to prove this function was called
+ }
+ };
+
+ lodepng::State state;
+ state.decoder.zlibsettings.custom_inflate = TestFun::custom_inflate;
+ state.decoder.zlibsettings.custom_context = &customcontext;
+ state.decoder.zlibsettings.ignore_adler32 = 0;
+ state.decoder.ignore_crc = 0;
+ unsigned error = lodepng::decode(decoded, w, h, state, encoded);
+
+ ASSERT_EQUALS(5555, error);
+}
+
+void doPngSuiteTinyTest(const std::string& base64, unsigned w, unsigned h,
+ unsigned char r, unsigned char g, unsigned char b, unsigned char a)
+{
+ lodepng::State state;
+ std::vector<unsigned char> png;
+ fromBase64(png, base64);
+ unsigned w2, h2;
+ std::vector<unsigned char> image;
+ unsigned error = lodepng::decode(image, w2, h2, state, png);
+ assertNoPNGError(error);
+ ASSERT_EQUALS(w, w2);
+ ASSERT_EQUALS(h, h2);
+ ASSERT_EQUALS((int)r, (int)image[0]);
+ ASSERT_EQUALS((int)g, (int)image[1]);
+ ASSERT_EQUALS((int)b, (int)image[2]);
+ ASSERT_EQUALS((int)a, (int)image[3]);
+
+ state.encoder.auto_convert = false;
+ std::vector<unsigned char> png2;
+ error = lodepng::encode(png2, image, w, h, state);
+ assertNoPNGError(error);
+ std::vector<unsigned char> image2;
+ error = lodepng::decode(image2, w2, h2, state, png2);
+ assertNoPNGError(error);
+ for(size_t i = 0; i < image.size(); i++) ASSERT_EQUALS(image[i], image2[i]);
+}
+
+/*checks that both png suite images have the exact same pixel content, e.g. to check that
+it decodes an interlaced and non-interlaced corresponding png suite image equally*/
+void doPngSuiteEqualTest(const std::string& base64a, const std::string& base64b)
+{
+ lodepng::State state;
+ std::vector<unsigned char> pnga, pngb;
+ fromBase64(pnga, base64a);
+ fromBase64(pngb, base64b);
+ unsigned wa, ha, wb, hb;
+ std::vector<unsigned char> imagea, imageb;
+ assertNoPNGError(lodepng::decode(imagea, wa, ha, state, pnga));
+ assertNoPNGError(lodepng::decode(imageb, wb, hb, state, pngb));
+ ASSERT_EQUALS(wa, wb);
+ ASSERT_EQUALS(ha, hb);
+
+ size_t size = wa * ha * 4;
+ for(size_t i = 0; i < size; i++)
+ {
+ if(imagea[i] != imageb[i])
+ {
+ std::cout << "x: " << ((i / 4) % wa) << " y: " << ((i / 4) / wa) << " c: " << i % 4 << std::endl;
+ ASSERT_EQUALS((int)imagea[i], (int)imageb[i]);
+ }
+ }
+}
+
+void testPngSuiteTiny()
+{
+ std::cout << "testPngSuiteTiny" << std::endl;
+ doPngSuiteTinyTest("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAFS3GZcAAAABGdBTUEAAYagMeiWXwAAAANzQklU"
+ "BAQEd/i1owAAAANQTFRFAAD/injSVwAAAApJREFUeJxjYAAAAAIAAUivpHEAAAAASUVORK5CYII=",
+ 1, 1, 0, 0, 255, 255); //s01n3p01.png
+ doPngSuiteTinyTest("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAABGdBTUEAAYagMeiWXwAAAANzQklU"
+ "BAQEd/i1owAAAANQTFRFAAD/injSVwAAAApJREFUeJxjYAAAAAIAAUivpHEAAAAASUVORK5CYII=",
+ 1, 1, 0, 0, 255, 255); //s01i3p01.png
+ doPngSuiteTinyTest("iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHAgMAAAC5PL9AAAAABGdBTUEAAYagMeiWXwAAAANzQklU"
+ "BAQEd/i1owAAAAxQTFRF/wB3AP93//8AAAD/G0OznAAAABpJREFUeJxj+P+H4WoMw605DDfmgEgg"
+ "+/8fAHF5CrkeXW0HAAAAAElFTkSuQmCC",
+ 7, 7, 0, 0, 255, 255); //s07n3p02.png
+ doPngSuiteTinyTest("iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHAgMAAAHOO4/WAAAABGdBTUEAAYagMeiWXwAAAANzQklU"
+ "BAQEd/i1owAAAAxQTFRF/wB3AP93//8AAAD/G0OznAAAACVJREFUeJxjOMBwgOEBwweGDQyvGf4z"
+ "/GFIAcI/DFdjGG7MAZIAweMMgVWC+YkAAAAASUVORK5CYII=",
+ 7, 7, 0, 0, 255, 255); //s07i3p02.png
+ doPngSuiteTinyTest("iVBORw0KGgoAAAANSUhEUgAAACAAAAAgAgMAAAAOFJJnAAAABGdBTUEAAYagMeiWXwAAAANzQklU"
+ "AQEBfC53ggAAAAxQTFRFAP8A/wAA//8AAAD/ZT8rugAAACJJREFUeJxj+B+6igGEGfAw8MnBGKug"
+ "LHwMqNL/+BiDzD0AvUl/geqJjhsAAAAASUVORK5CYII=",
+ 32, 32, 0, 0, 255, 255); //basn3p02.png
+ doPngSuiteTinyTest("iVBORw0KGgoAAAANSUhEUgAAACAAAAAgAQMAAABJtOi3AAAABGdBTUEAAYagMeiWXwAAAAZQTFRF"
+ "7v8iImb/bBrSJgAAABVJREFUeJxj4AcCBjTiAxCgEwOkDgC7Hz/Bk4JmWQAAAABJRU5ErkJggg==",
+ 32, 32, 238, 255, 34, 255); //basn3p01.png
+ doPngSuiteTinyTest("iVBORw0KGgoAAAANSUhEUgAAACAAAAAgEAAAAAAGgflrAAAABGdBTUEAAYagMeiWXwAAAF5JREFU"
+ "eJzV0jEKwDAMQ1E5W+9/xtygk8AoezLVKgSj2Y8/OICnuFcTE2OgOoJgHQiZAN2C9kDKBOgW3AZC"
+ "JkC3oD2QMgG6BbeBkAnQLWgPpExgP28H7E/0GTjPfwAW2EvYX64rn9cAAAAASUVORK5CYII=",
+ 32, 32, 0, 0, 0, 255); //basn0g16.png
+ doPngSuiteTinyTest("iVBORw0KGgoAAAANSUhEUgAAACAAAAAgEAAAAAFxhsn9AAAABGdBTUEAAYagMeiWXwAAAOJJREFU"
+ "eJy1kTsOwjAQRMdJCqj4XYHD5DAcj1Okyg2okCyBRLOSC0BDERKCI7xJVmgaa/X8PFo7oESJEtka"
+ "TeLDjdjjgCMe7eTE96FGd3AL7HvZsdNEaJMVo0GNGm775bgwW6Afj/SAjAY+JsYNXIHtz2xYxTXi"
+ "UoOek4AbFcCnDYEK4NMGsgXcMrGHJytkBX5HIP8FAhVANIMVIBVANMPfgUAFEM3wAVyG5cxcecY5"
+ "/dup3LVFa1HXmA61LY59f6Ygp1Eg1gZGQaBRILYGdxoFYmtAGgXx9YmCfPD+RMHwuuAFVpjuiRT/"
+ "//4AAAAASUVORK5CYII=",
+ 32, 32, 0, 0, 0, 255); //basi0g16.png
+
+ //s01n3p01.png s01i3p01.png
+ doPngSuiteEqualTest("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAFS3GZcAAAABGdBTUEAAYagMeiWXwAAAANzQklU"
+ "BAQEd/i1owAAAANQTFRFAAD/injSVwAAAApJREFUeJxjYAAAAAIAAUivpHEAAAAASUVORK5CYII=",
+ "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAABGdBTUEAAYagMeiWXwAAAANzQklU"
+ "BAQEd/i1owAAAANQTFRFAAD/injSVwAAAApJREFUeJxjYAAAAAIAAUivpHEAAAAASUVORK5CYII=");
+ //s07n3p02.png and s07i3p02.png
+ doPngSuiteEqualTest("iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHAgMAAAC5PL9AAAAABGdBTUEAAYagMeiWXwAAAANzQklU"
+ "BAQEd/i1owAAAAxQTFRF/wB3AP93//8AAAD/G0OznAAAABpJREFUeJxj+P+H4WoMw605DDfmgEgg"
+ "+/8fAHF5CrkeXW0HAAAAAElFTkSuQmCC",
+ "iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHAgMAAAHOO4/WAAAABGdBTUEAAYagMeiWXwAAAANzQklU"
+ "BAQEd/i1owAAAAxQTFRF/wB3AP93//8AAAD/G0OznAAAACVJREFUeJxjOMBwgOEBwweGDQyvGf4z"
+ "/GFIAcI/DFdjGG7MAZIAweMMgVWC+YkAAAAASUVORK5CYII=");
+ //basn0g16.png and basi0g16.png
+ doPngSuiteEqualTest("iVBORw0KGgoAAAANSUhEUgAAACAAAAAgEAAAAAAGgflrAAAABGdBTUEAAYagMeiWXwAAAF5JREFU"
+ "eJzV0jEKwDAMQ1E5W+9/xtygk8AoezLVKgSj2Y8/OICnuFcTE2OgOoJgHQiZAN2C9kDKBOgW3AZC"
+ "JkC3oD2QMgG6BbeBkAnQLWgPpExgP28H7E/0GTjPfwAW2EvYX64rn9cAAAAASUVORK5CYII=",
+ "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgEAAAAAFxhsn9AAAABGdBTUEAAYagMeiWXwAAAOJJREFU"
+ "eJy1kTsOwjAQRMdJCqj4XYHD5DAcj1Okyg2okCyBRLOSC0BDERKCI7xJVmgaa/X8PFo7oESJEtka"
+ "TeLDjdjjgCMe7eTE96FGd3AL7HvZsdNEaJMVo0GNGm775bgwW6Afj/SAjAY+JsYNXIHtz2xYxTXi"
+ "UoOek4AbFcCnDYEK4NMGsgXcMrGHJytkBX5HIP8FAhVANIMVIBVANMPfgUAFEM3wAVyG5cxcecY5"
+ "/dup3LVFa1HXmA61LY59f6Ygp1Eg1gZGQaBRILYGdxoFYmtAGgXx9YmCfPD+RMHwuuAFVpjuiRT/"
+ "//4AAAAASUVORK5CYII=");
+}
+
+void testChunkUtil()
+{
+ std::cout << "testChunkUtil" << std::endl;
+ std::vector<unsigned char> png;
+ createComplexPNG(png);
+
+ std::vector<std::string> names[3];
+ std::vector<std::vector<unsigned char> > chunks[3];
+
+ assertNoError(lodepng::getChunks(names, chunks, png));
+
+ std::vector<std::vector<unsigned char> > chunks2[3];
+ chunks2[0].push_back(chunks[2][2]); //zTXt
+ chunks2[1].push_back(chunks[2][3]); //tEXt
+ chunks2[2].push_back(chunks[2][4]); //iTXt
+
+ assertNoError(lodepng::insertChunks(png, chunks2));
+
+ std::string chunknames = extractChunkNames(png);
+ // chunks2[0] chunks2[1] chunks2[2]
+ // v v v
+ std::string expectednames = " IHDR uNKa uNKa zTXt PLTE tRNS bKGD pHYs uNKb tEXt IDAT tIME zTXt zTXt tEXt iTXt iTXt uNKc iTXt IEND";
+ ASSERT_EQUALS(expectednames, chunknames);
+
+ std::vector<unsigned char> image;
+ unsigned w, h;
+ assertNoPNGError(lodepng::decode(image, w, h, png));
+}
+
+//Test that when decoding to 16-bit per channel, it always uses big endian consistently.
+//It should always output big endian, the convention used inside of PNG, even though x86 CPU's are little endian.
+void test16bitColorEndianness()
+{
+ std::cout << "test16bitColorEndianness" << std::endl;
+
+ //basn0g16.png from the PNG test suite
+ std::string base64 = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgEAAAAAAGgflrAAAABGdBTUEAAYagMeiWXwAAAF5JREFU"
+ "eJzV0jEKwDAMQ1E5W+9/xtygk8AoezLVKgSj2Y8/OICnuFcTE2OgOoJgHQiZAN2C9kDKBOgW3AZC"
+ "JkC3oD2QMgG6BbeBkAnQLWgPpExgP28H7E/0GTjPfwAW2EvYX64rn9cAAAAASUVORK5CYII=";
+ std::vector<unsigned char> png;
+ fromBase64(png, base64);
+ unsigned w, h;
+ std::vector<unsigned char> image;
+ lodepng::State state;
+
+ // Decode from 16-bit grey image to 16-bit per channel RGBA
+ state.info_raw.bitdepth = 16;
+ assertNoPNGError(lodepng::decode(image, w, h, state, png));
+ ASSERT_EQUALS(0x09, image[8]);
+ ASSERT_EQUALS(0x00, image[9]);
+
+ // Decode from 16-bit grey image to 16-bit grey raw image (no conversion)
+ image.clear();
+ state = lodepng::State();
+ state.decoder.color_convert = false;
+ assertNoPNGError(lodepng::decode(image, w, h, state, png));
+ ASSERT_EQUALS(0x09, image[2]);
+ ASSERT_EQUALS(0x00, image[3]);
+
+ // Decode from 16-bit per channel RGB image to 16-bit per channel RGBA
+ base64 = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgEAIAAACsiDHgAAAABGdBTUEAAYagMeiWXwAAAANzQklU"
+ "DQ0N0DeNwQAAAH5JREFUeJztl8ENxEAIAwcJ6cpI+q8qKeNepAgelq2dCjz4AdQM1jRcf3WIDQ13"
+ "qUNsiBBQZ1gR0cARUFIz3pug3586wo5+rOcfIaBOsCSggSOgpcB8D4D3R9DgfUyECIhDbAhp4Ajo"
+ "KPD+CBq8P4IG72MiQkCdYUVEA0dAyQcwUyZpXH92ZwAAAABJRU5ErkJggg=="; //cs3n2c16.png
+ png.clear();
+ fromBase64(png, base64);
+ image.clear();
+ state = lodepng::State();
+ state.info_raw.bitdepth = 16;
+ assertNoPNGError(lodepng::decode(image, w, h, state, png));
+ ASSERT_EQUALS(0x1f, image[258]);
+ ASSERT_EQUALS(0xf9, image[259]);
+
+ // Decode from 16-bit per channel RGB image to 16-bit per channel RGBA raw image (no conversion)
+ image.clear();
+ state = lodepng::State();
+ state.decoder.color_convert = false;
+ assertNoPNGError(lodepng::decode(image, w, h, state, png));
+
+ ASSERT_EQUALS(0x1f, image[194]);
+ ASSERT_EQUALS(0xf9, image[195]);
+
+ image.clear();
+ state = lodepng::State();
+
+ // Decode from palette image to 16-bit per channel RGBA
+ base64 = "iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHAgMAAAC5PL9AAAAABGdBTUEAAYagMeiWXwAAAANzQklU"
+ "BAQEd/i1owAAAAxQTFRF/wB3AP93//8AAAD/G0OznAAAABpJREFUeJxj+P+H4WoMw605DDfmgEgg"
+ "+/8fAHF5CrkeXW0HAAAAAElFTkSuQmCC"; //s07n3p02.png
+ png.clear();
+ fromBase64(png, base64);
+ image.clear();
+ state = lodepng::State();
+ state.info_raw.bitdepth = 16;
+ assertNoPNGError(lodepng::decode(image, w, h, state, png));
+ ASSERT_EQUALS(0x77, image[84]);
+ ASSERT_EQUALS(0x77, image[85]);
+}
+
+void testPredefinedFilters() {
+ size_t w = 32, h = 32;
+ std::cout << "testPredefinedFilters" << std::endl;
+ Image image;
+ generateTestImage(image, w, h, LCT_RGBA, 8);
+
+ // everything to filter type '3'
+ std::vector<unsigned char> predefined(h, 3);
+ lodepng::State state;
+ state.encoder.filter_strategy = LFS_PREDEFINED;
+ state.encoder.filter_palette_zero = 0;
+ state.encoder.predefined_filters = &predefined[0];
+
+ std::vector<unsigned char> png;
+ unsigned error = lodepng::encode(png, &image.data[0], w, h, state);
+ assertNoError(error);
+
+ std::vector<unsigned char> outfilters;
+ error = lodepng::getFilterTypes(outfilters, png);
+ assertNoError(error);
+
+ ASSERT_EQUALS(outfilters.size(), h);
+ for (size_t i = 0; i < h; i++) ASSERT_EQUALS(3, outfilters[i]);
+}
+
+void testWrongWindowSizeGivesError() {
+ std::vector<unsigned char> png;
+ unsigned w = 32, h = 32;
+ Image image;
+ generateTestImage(image, w, h);
+ unsigned error = 0;
+
+ lodepng::State state;
+ state.encoder.zlibsettings.windowsize = 0;
+ error = lodepng::encode(png, &image.data[0], w, h, state);
+ ASSERT_EQUALS(60, error);
+ state.encoder.zlibsettings.windowsize = 65536;
+ error = lodepng::encode(png, &image.data[0], w, h, state);
+ ASSERT_EQUALS(60, error);
+ state.encoder.zlibsettings.windowsize = 1000; // not power of two
+ error = lodepng::encode(png, &image.data[0], w, h, state);
+ ASSERT_EQUALS(90, error);
+ state.encoder.zlibsettings.windowsize = 256;
+ error = lodepng::encode(png, &image.data[0], w, h, state);
+ ASSERT_EQUALS(0, error);
+}
+
+void addColor(std::vector<unsigned char>& colors, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
+{
+ colors.push_back(r);
+ colors.push_back(g);
+ colors.push_back(b);
+ colors.push_back(a);
+}
+
+void addColor16(std::vector<unsigned char>& colors, unsigned short r, unsigned short g, unsigned short b, unsigned short a)
+{
+ colors.push_back(r & 255);
+ colors.push_back((r >> 8) & 255);
+ colors.push_back(g & 255);
+ colors.push_back((g >> 8) & 255);
+ colors.push_back(b & 255);
+ colors.push_back((b >> 8) & 255);
+ colors.push_back(a & 255);
+ colors.push_back((a >> 8) & 255);
+}
+
+// colors is in RGBA, inbitdepth must be 8 or 16, the amount of bits per channel.
+// colortype and bitdepth are the expected values. insize is amount of pixels. So the amount of bytes is insize * 4 * (inbitdepth / 8)
+void testAutoColorModel(const std::vector<unsigned char>& colors, unsigned inbitdepth, LodePNGColorType colortype, unsigned bitdepth, bool key)
+{
+ std::cout << "testAutoColorModel " << inbitdepth << " " << colortype << " " << bitdepth << " " << key << std::endl;
+ size_t innum = colors.size() / 4 * inbitdepth / 8;
+ size_t num = innum < 65536 ? 65536 : innum; // Make image bigger so the convert doesn't avoid palette due to small image.
+ std::vector<unsigned char> colors2(num * 4 * (inbitdepth / 8));
+ for(size_t i = 0; i < colors2.size(); i++) colors2[i] = colors[i % colors.size()];
+
+ std::vector<unsigned char> png;
+ lodepng::encode(png, colors2, num, 1, LCT_RGBA, inbitdepth);
+
+ // now extract the color type it chose
+ unsigned w, h;
+ lodepng::State state;
+ std::vector<unsigned char> decoded;
+ lodepng::decode(decoded, w, h, state, png);
+ ASSERT_EQUALS(num, w);
+ ASSERT_EQUALS(1, h);
+ ASSERT_EQUALS(colortype, state.info_png.color.colortype);
+ ASSERT_EQUALS(bitdepth, state.info_png.color.bitdepth);
+ ASSERT_EQUALS(key, state.info_png.color.key_defined);
+ if(inbitdepth == 8) { for(size_t i = 0; i < colors.size(); i++) ASSERT_EQUALS(colors[i], decoded[i]); }
+ else { for(size_t i = 0; i < colors.size() / 2; i++) ASSERT_EQUALS(colors[i * 2], decoded[i]); }
+}
+
+void testAutoColorModels()
+{
+ std::vector<unsigned char> grey1;
+ for(size_t i = 0; i < 2; i++) addColor(grey1, i * 255, i * 255, i * 255, 255);
+ testAutoColorModel(grey1, 8, LCT_GREY, 1, false);
+
+ std::vector<unsigned char> grey2;
+ for(size_t i = 0; i < 4; i++) addColor(grey2, i * 85, i * 85, i * 85, 255);
+ testAutoColorModel(grey2, 8, LCT_GREY, 2, false);
+
+
+ std::vector<unsigned char> grey4;
+ for(size_t i = 0; i < 16; i++) addColor(grey4, i * 17, i * 17, i * 17, 255);
+ testAutoColorModel(grey4, 8, LCT_GREY, 4, false);
+
+
+ std::vector<unsigned char> grey8;
+ for(size_t i = 0; i < 256; i++) addColor(grey8, i, i, i, 255);
+ testAutoColorModel(grey8, 8, LCT_GREY, 8, false);
+
+
+ std::vector<unsigned char> grey16;
+ for(size_t i = 0; i < 257; i++) addColor16(grey16, i, i, i, 65535);
+ testAutoColorModel(grey16, 16, LCT_GREY, 16, false);
+
+ std::vector<unsigned char> palette;
+ addColor(palette, 0, 0, 1, 255);
+ testAutoColorModel(palette, 8, LCT_PALETTE, 1, false);
+ addColor(palette, 0, 0, 2, 255);
+ testAutoColorModel(palette, 8, LCT_PALETTE, 1, false);
+ for(int i = 3; i <= 4; i++) addColor(palette, 0, 0, i, 255);
+ testAutoColorModel(palette, 8, LCT_PALETTE, 2, false);
+ for(int i = 5; i <= 7; i++) addColor(palette, 0, 0, i, 255);
+ testAutoColorModel(palette, 8, LCT_PALETTE, 4, false);
+ for(int i = 8; i <= 17; i++) addColor(palette, 0, 0, i, 255);
+ testAutoColorModel(palette, 8, LCT_PALETTE, 8, false);
+ addColor(palette, 0, 0, 18, 0); // transparent
+ testAutoColorModel(palette, 8, LCT_PALETTE, 8, false);
+ addColor(palette, 0, 0, 18, 1); // translucent
+ testAutoColorModel(palette, 8, LCT_PALETTE, 8, false);
+
+ std::vector<unsigned char> rgb = grey8;
+ addColor(rgb, 255, 0, 0, 255);
+ testAutoColorModel(rgb, 8, LCT_RGB, 8, false);
+
+ std::vector<unsigned char> rgb_key = rgb;
+ addColor(rgb_key, 128, 0, 0, 0);
+ testAutoColorModel(rgb_key, 8, LCT_RGB, 8, true);
+
+ std::vector<unsigned char> rgb_key2 = rgb_key;
+ addColor(rgb_key2, 128, 0, 0, 255); // same color but opaque ==> no more key
+ testAutoColorModel(rgb_key2, 8, LCT_RGBA, 8, false);
+
+ std::vector<unsigned char> rgb_key3 = rgb_key;
+ addColor(rgb_key3, 128, 0, 0, 255); // semi-translucent ==> no more key
+ testAutoColorModel(rgb_key3, 8, LCT_RGBA, 8, false);
+
+ std::vector<unsigned char> rgb_key4 = rgb_key;
+ addColor(rgb_key4, 128, 0, 0, 255);
+ addColor(rgb_key4, 129, 0, 0, 255); // two different transparent colors ==> no more key
+ testAutoColorModel(rgb_key4, 8, LCT_RGBA, 8, false);
+
+ std::vector<unsigned char> grey1_key = grey1;
+ grey1_key[7] = 0;
+ testAutoColorModel(grey1_key, 8, LCT_GREY, 1, true);
+
+ std::vector<unsigned char> grey2_key = grey2;
+ grey2_key[7] = 0;
+ testAutoColorModel(grey2_key, 8, LCT_GREY, 2, true);
+
+ std::vector<unsigned char> grey4_key = grey4;
+ grey4_key[7] = 0;
+ testAutoColorModel(grey4_key, 8, LCT_GREY, 4, true);
+
+ std::vector<unsigned char> grey8_key = grey8;
+ grey8_key[7] = 0;
+ testAutoColorModel(grey8_key, 8, LCT_GREY, 8, true);
+
+ std::vector<unsigned char> small16;
+ addColor16(small16, 1, 0, 0, 65535);
+ testAutoColorModel(small16, 16, LCT_RGB, 16, false);
+
+ std::vector<unsigned char> small16a;
+ addColor16(small16a, 1, 0, 0, 1);
+ testAutoColorModel(small16a, 16, LCT_RGBA, 16, false);
+
+ std::vector<unsigned char> not16;
+ addColor16(not16, 257, 257, 257, 0);
+ testAutoColorModel(not16, 16, LCT_PALETTE, 1, false);
+}
+
+void doMain()
+{
+ //PNG
+ testPNGCodec();
+ testPngSuiteTiny();
+ testPaletteFilterTypesZero();
+ testComplexPNG();
+ testPredefinedFilters();
+ testFuzzing();
+ testWrongWindowSizeGivesError();
+
+ //Colors
+ testColorKeyConvert();
+ testColorConvert();
+ testColorConvert2();
+ testPaletteToPaletteConvert();
+ testRGBToPaletteConvert();
+ test16bitColorEndianness();
+ testAutoColorModels();
+
+ //Zlib
+ testCompressZlib();
+ testHuffmanCodeLengths();
+ testCustomZlibCompress();
+ testCustomZlibCompress2();
+ testCustomDeflate();
+ testCustomZlibDecompress();
+ testCustomInflate();
+
+ //lodepng_util
+ testChunkUtil();
+
+ std::cout << "\ntest successful" << std::endl;
+}
+
+int main()
+{
+ try
+ {
+ doMain();
+ }
+ catch(...)
+ {
+ std::cout << "error!" << std::endl;
+ }
+
+ return 0;
+}
diff --git a/lodepng_util.cpp b/lodepng_util.cpp
new file mode 100644
index 0000000..3784b6e
--- /dev/null
+++ b/lodepng_util.cpp
@@ -0,0 +1,653 @@
+/*
+LodePNG Utils
+
+Copyright (c) 2005-2014 Lode Vandevenne
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#include "lodepng_util.h"
+#include <iostream>
+
+namespace lodepng
+{
+
+LodePNGInfo getPNGHeaderInfo(const std::vector<unsigned char>& png)
+{
+ unsigned w, h;
+ lodepng::State state;
+ lodepng_inspect(&w, &h, &state, &png[0], png.size());
+ return state.info_png;
+}
+
+unsigned getChunkInfo(std::vector<std::string>& names, std::vector<size_t>& sizes,
+ const std::vector<unsigned char>& png)
+{
+ // Listing chunks is based on the original file, not the decoded png info.
+ const unsigned char *chunk, *begin, *end;
+ end = &png.back() + 1;
+ begin = chunk = &png.front() + 8;
+
+ while(chunk + 8 < end && chunk >= begin)
+ {
+ char type[5];
+ lodepng_chunk_type(type, chunk);
+ if(std::string(type).size() != 4) return 1;
+
+ names.push_back(type);
+ sizes.push_back(lodepng_chunk_length(chunk));
+
+ chunk = lodepng_chunk_next_const(chunk);
+ }
+ return 0;
+}
+
+unsigned getChunks(std::vector<std::string> names[3],
+ std::vector<std::vector<unsigned char> > chunks[3],
+ const std::vector<unsigned char>& png)
+{
+ const unsigned char *chunk, *next, *begin, *end;
+ end = &png.back() + 1;
+ begin = chunk = &png.front() + 8;
+
+ int location = 0;
+
+ while(chunk + 8 < end && chunk >= begin)
+ {
+ char type[5];
+ lodepng_chunk_type(type, chunk);
+ std::string name(type);
+ if(name.size() != 4) return 1;
+
+ next = lodepng_chunk_next_const(chunk);
+
+ if(name == "IHDR")
+ {
+ location = 0;
+ }
+ else if(name == "PLTE")
+ {
+ location = 1;
+ }
+ else if(name == "IDAT")
+ {
+ location = 2;
+ }
+ else if(name != "IEND")
+ {
+ names[location].push_back(name);
+ chunks[location].push_back(std::vector<unsigned char>(chunk, next));
+ }
+
+ chunk = next;
+ }
+ return 0;
+}
+
+
+unsigned insertChunks(std::vector<unsigned char>& png,
+ const std::vector<std::vector<unsigned char> > chunks[3])
+{
+ const unsigned char *chunk, *next, *begin, *end;
+ end = &png.back() + 1;
+ begin = chunk = &png.front() + 8;
+
+ long l0 = 0; //location 0: IHDR-l0-PLTE (or IHDR-l0-l1-IDAT)
+ long l1 = 0; //location 1: PLTE-l1-IDAT (or IHDR-l0-l1-IDAT)
+ long l2 = 0; //location 2: IDAT-l2-IEND
+
+ while(chunk + 8 < end && chunk >= begin)
+ {
+ char type[5];
+ lodepng_chunk_type(type, chunk);
+ std::string name(type);
+ if(name.size() != 4) return 1;
+
+ next = lodepng_chunk_next_const(chunk);
+
+ if(name == "PLTE")
+ {
+ if(l0 == 0) l0 = chunk - begin + 8;
+ }
+ else if(name == "IDAT")
+ {
+ if(l0 == 0) l0 = chunk - begin + 8;
+ if(l1 == 0) l1 = chunk - begin + 8;
+ }
+ else if(name == "IEND")
+ {
+ if(l2 == 0) l2 = chunk - begin + 8;
+ }
+
+ chunk = next;
+ }
+
+ std::vector<unsigned char> result;
+ result.insert(result.end(), png.begin(), png.begin() + l0);
+ for(size_t i = 0; i < chunks[0].size(); i++) result.insert(result.end(), chunks[0][i].begin(), chunks[0][i].end());
+ result.insert(result.end(), png.begin() + l0, png.begin() + l1);
+ for(size_t i = 0; i < chunks[1].size(); i++) result.insert(result.end(), chunks[1][i].begin(), chunks[1][i].end());
+ result.insert(result.end(), png.begin() + l1, png.begin() + l2);
+ for(size_t i = 0; i < chunks[2].size(); i++) result.insert(result.end(), chunks[2][i].begin(), chunks[2][i].end());
+ result.insert(result.end(), png.begin() + l2, png.end());
+
+ png = result;
+ return 0;
+}
+
+unsigned getFilterTypesInterlaced(std::vector<std::vector<unsigned char> >& filterTypes,
+ const std::vector<unsigned char>& png)
+{
+ //Get color type and interlace type
+ lodepng::State state;
+ unsigned w, h;
+ unsigned error;
+ error = lodepng_inspect(&w, &h, &state, &png[0], png.size());
+
+ if(error) return 1;
+
+ //Read literal data from all IDAT chunks
+ const unsigned char *chunk, *begin, *end;
+ end = &png.back() + 1;
+ begin = chunk = &png.front() + 8;
+
+ std::vector<unsigned char> zdata;
+
+ while(chunk + 8 < end && chunk >= begin)
+ {
+ char type[5];
+ lodepng_chunk_type(type, chunk);
+ if(std::string(type).size() != 4) return 1; //Probably not a PNG file
+
+ if(std::string(type) == "IDAT")
+ {
+ const unsigned char* cdata = lodepng_chunk_data_const(chunk);
+ unsigned clength = lodepng_chunk_length(chunk);
+
+ for(unsigned i = 0; i < clength; i++)
+ {
+ zdata.push_back(cdata[i]);
+ }
+ }
+
+ chunk = lodepng_chunk_next_const(chunk);
+ }
+
+ //Decompress all IDAT data
+ std::vector<unsigned char> data;
+ error = lodepng::decompress(data, &zdata[0], zdata.size());
+
+ if(error) return 1;
+
+ if(state.info_png.interlace_method == 0)
+ {
+ filterTypes.resize(1);
+
+ //A line is 1 filter byte + all pixels
+ size_t linebytes = 1 + lodepng_get_raw_size(w, 1, &state.info_png.color);
+
+ for(size_t i = 0; i < data.size(); i += linebytes)
+ {
+ filterTypes[0].push_back(data[i]);
+ }
+ }
+ else
+ {
+ //Interlaced
+ filterTypes.resize(7);
+ static const unsigned ADAM7_IX[7] = { 0, 4, 0, 2, 0, 1, 0 }; /*x start values*/
+ static const unsigned ADAM7_IY[7] = { 0, 0, 4, 0, 2, 0, 1 }; /*y start values*/
+ static const unsigned ADAM7_DX[7] = { 8, 8, 4, 4, 2, 2, 1 }; /*x delta values*/
+ static const unsigned ADAM7_DY[7] = { 8, 8, 8, 4, 4, 2, 2 }; /*y delta values*/
+ size_t pos = 0;
+ for(size_t j = 0; j < 7; j++)
+ {
+ unsigned w2 = (w - ADAM7_IX[j] + ADAM7_DX[j] - 1) / ADAM7_DX[j];
+ unsigned h2 = (h - ADAM7_IY[j] + ADAM7_DY[j] - 1) / ADAM7_DY[j];
+ if(ADAM7_IX[j] >= w || ADAM7_IY[j] >= h) w2 = h2 = 0;
+ size_t linebytes = 1 + lodepng_get_raw_size(w2, 1, &state.info_png.color);
+ for(size_t i = 0; i < h2; i++)
+ {
+ filterTypes[j].push_back(data[pos]);
+ pos += linebytes;
+ }
+ }
+ }
+ return 0; /* OK */
+}
+
+
+unsigned getFilterTypes(std::vector<unsigned char>& filterTypes, const std::vector<unsigned char>& png)
+{
+ std::vector<std::vector<unsigned char> > passes;
+ unsigned error = getFilterTypesInterlaced(passes, png);
+ if(error) return error;
+
+ if(passes.size() == 1)
+ {
+ filterTypes.swap(passes[0]);
+ }
+ else
+ {
+ lodepng::State state;
+ unsigned w, h;
+ lodepng_inspect(&w, &h, &state, &png[0], png.size());
+ /*
+ Interlaced. Simplify it: put pass 6 and 7 alternating in the one vector so
+ that one filter per scanline of the uninterlaced image is given, with that
+ filter corresponding the closest to what it would be for non-interlaced
+ image.
+ */
+ for(size_t i = 0; i < h; i++)
+ {
+ filterTypes.push_back(i % 2 == 0 ? passes[5][i / 2] : passes[6][i / 2]);
+ }
+ }
+ return 0; /* OK */
+}
+
+int getPaletteValue(const unsigned char* data, size_t i, int bits)
+{
+ if(bits == 8) return data[i];
+ else if(bits == 4) return (data[i / 2] >> ((i % 2) * 4)) & 15;
+ else if(bits == 2) return (data[i / 4] >> ((i % 4) * 2)) & 3;
+ else if(bits == 1) return (data[i / 8] >> (i % 8)) & 1;
+ else return 0;
+}
+
+//This uses a stripped down version of picoPNG to extract detailed zlib information while decompressing.
+static const unsigned long LENBASE[29] =
+ {3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258};
+static const unsigned long LENEXTRA[29] =
+ {0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0};
+static const unsigned long DISTBASE[30] =
+ {1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577};
+static const unsigned long DISTEXTRA[30] =
+ {0,0,0,0,1,1,2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13};
+static const unsigned long CLCL[19] =
+ {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; //code length code lengths
+
+struct ExtractZlib // Zlib decompression and information extraction
+{
+ std::vector<ZlibBlockInfo>* zlibinfo;
+ ExtractZlib(std::vector<ZlibBlockInfo>* info) : zlibinfo(info) {};
+ int error;
+
+ unsigned long readBitFromStream(size_t& bitp, const unsigned char* bits)
+ {
+ unsigned long result = (bits[bitp >> 3] >> (bitp & 0x7)) & 1;
+ bitp++;
+ return result;
+ }
+
+ unsigned long readBitsFromStream(size_t& bitp, const unsigned char* bits, size_t nbits)
+ {
+ unsigned long result = 0;
+ for(size_t i = 0; i < nbits; i++) result += (readBitFromStream(bitp, bits)) << i;
+ return result;
+ }
+
+ struct HuffmanTree
+ {
+ int makeFromLengths(const std::vector<unsigned long>& bitlen, unsigned long maxbitlen)
+ { //make tree given the lengths
+ unsigned long numcodes = (unsigned long)(bitlen.size()), treepos = 0, nodefilled = 0;
+ std::vector<unsigned long> tree1d(numcodes), blcount(maxbitlen + 1, 0), nextcode(maxbitlen + 1, 0);
+ //count number of instances of each code length
+ for(unsigned long bits = 0; bits < numcodes; bits++) blcount[bitlen[bits]]++;
+ for(unsigned long bits = 1; bits <= maxbitlen; bits++)
+ {
+ nextcode[bits] = (nextcode[bits - 1] + blcount[bits - 1]) << 1;
+ }
+ //generate all the codes
+ for(unsigned long n = 0; n < numcodes; n++) if(bitlen[n] != 0) tree1d[n] = nextcode[bitlen[n]]++;
+ tree2d.clear(); tree2d.resize(numcodes * 2, 32767); //32767 here means the tree2d isn't filled there yet
+ for(unsigned long n = 0; n < numcodes; n++) //the codes
+ for(unsigned long i = 0; i < bitlen[n]; i++) //the bits for this code
+ {
+ unsigned long bit = (tree1d[n] >> (bitlen[n] - i - 1)) & 1;
+ if(treepos > numcodes - 2) return 55;
+ if(tree2d[2 * treepos + bit] == 32767) //not yet filled in
+ {
+ if(i + 1 == bitlen[n])
+ {
+ //last bit
+ tree2d[2 * treepos + bit] = n;
+ treepos = 0;
+ }
+ else
+ {
+ //addresses are encoded as values > numcodes
+ tree2d[2 * treepos + bit] = ++nodefilled + numcodes;
+ treepos = nodefilled;
+ }
+ }
+ else treepos = tree2d[2 * treepos + bit] - numcodes; //subtract numcodes from address to get address value
+ }
+ return 0;
+ }
+ int decode(bool& decoded, unsigned long& result, size_t& treepos, unsigned long bit) const
+ { //Decodes a symbol from the tree
+ unsigned long numcodes = (unsigned long)tree2d.size() / 2;
+ if(treepos >= numcodes) return 11; //error: you appeared outside the codetree
+ result = tree2d[2 * treepos + bit];
+ decoded = (result < numcodes);
+ treepos = decoded ? 0 : result - numcodes;
+ return 0;
+ }
+ //2D representation of a huffman tree: one dimension is "0" or "1", the other contains all nodes and leaves.
+ std::vector<unsigned long> tree2d;
+ };
+
+ void inflate(std::vector<unsigned char>& out, const std::vector<unsigned char>& in, size_t inpos = 0)
+ {
+ size_t bp = 0, pos = 0; //bit pointer and byte pointer
+ error = 0;
+ unsigned long BFINAL = 0;
+ while(!BFINAL && !error)
+ {
+ size_t uncomprblockstart = pos;
+ size_t bpstart = bp;
+ if(bp >> 3 >= in.size()) { error = 52; return; } //error, bit pointer will jump past memory
+ BFINAL = readBitFromStream(bp, &in[inpos]);
+ unsigned long BTYPE = readBitFromStream(bp, &in[inpos]); BTYPE += 2 * readBitFromStream(bp, &in[inpos]);
+ zlibinfo->resize(zlibinfo->size() + 1);
+ zlibinfo->back().btype = BTYPE;
+ if(BTYPE == 3) { error = 20; return; } //error: invalid BTYPE
+ else if(BTYPE == 0) inflateNoCompression(out, &in[inpos], bp, pos, in.size());
+ else inflateHuffmanBlock(out, &in[inpos], bp, pos, in.size(), BTYPE);
+ size_t uncomprblocksize = pos - uncomprblockstart;
+ zlibinfo->back().compressedbits = bp - bpstart;
+ zlibinfo->back().uncompressedbytes = uncomprblocksize;
+ }
+ }
+
+ void generateFixedTrees(HuffmanTree& tree, HuffmanTree& treeD) //get the tree of a deflated block with fixed tree
+ {
+ std::vector<unsigned long> bitlen(288, 8), bitlenD(32, 5);;
+ for(size_t i = 144; i <= 255; i++) bitlen[i] = 9;
+ for(size_t i = 256; i <= 279; i++) bitlen[i] = 7;
+ tree.makeFromLengths(bitlen, 15);
+ treeD.makeFromLengths(bitlenD, 15);
+ }
+
+ //the code tree for Huffman codes, dist codes, and code length codes
+ HuffmanTree codetree, codetreeD, codelengthcodetree;
+ unsigned long huffmanDecodeSymbol(const unsigned char* in, size_t& bp, const HuffmanTree& tree, size_t inlength)
+ {
+ //decode a single symbol from given list of bits with given code tree. return value is the symbol
+ bool decoded; unsigned long ct;
+ for(size_t treepos = 0;;)
+ {
+ if((bp & 0x07) == 0 && (bp >> 3) > inlength) { error = 10; return 0; } //error: end reached without endcode
+ error = tree.decode(decoded, ct, treepos, readBitFromStream(bp, in));
+ if(error) return 0; //stop, an error happened
+ if(decoded) return ct;
+ }
+ }
+
+ void getTreeInflateDynamic(HuffmanTree& tree, HuffmanTree& treeD,
+ const unsigned char* in, size_t& bp, size_t inlength)
+ {
+ size_t bpstart = bp;
+ //get the tree of a deflated block with dynamic tree, the tree itself is also Huffman compressed with a known tree
+ std::vector<unsigned long> bitlen(288, 0), bitlenD(32, 0);
+ if(bp >> 3 >= inlength - 2) { error = 49; return; } //the bit pointer is or will go past the memory
+ size_t HLIT = readBitsFromStream(bp, in, 5) + 257; //number of literal/length codes + 257
+ size_t HDIST = readBitsFromStream(bp, in, 5) + 1; //number of dist codes + 1
+ size_t HCLEN = readBitsFromStream(bp, in, 4) + 4; //number of code length codes + 4
+ zlibinfo->back().hlit = HLIT - 257;
+ zlibinfo->back().hdist = HDIST - 1;
+ zlibinfo->back().hclen = HCLEN - 4;
+ std::vector<unsigned long> codelengthcode(19); //lengths of tree to decode the lengths of the dynamic tree
+ for(size_t i = 0; i < 19; i++) codelengthcode[CLCL[i]] = (i < HCLEN) ? readBitsFromStream(bp, in, 3) : 0;
+ //code length code lengths
+ for(size_t i = 0; i < codelengthcode.size(); i++) zlibinfo->back().clcl.push_back(codelengthcode[i]);
+ error = codelengthcodetree.makeFromLengths(codelengthcode, 7); if(error) return;
+ size_t i = 0, replength;
+ while(i < HLIT + HDIST)
+ {
+ unsigned long code = huffmanDecodeSymbol(in, bp, codelengthcodetree, inlength); if(error) return;
+ zlibinfo->back().treecodes.push_back(code); //tree symbol code
+ if(code <= 15) { if(i < HLIT) bitlen[i++] = code; else bitlenD[i++ - HLIT] = code; } //a length code
+ else if(code == 16) //repeat previous
+ {
+ if(bp >> 3 >= inlength) { error = 50; return; } //error, bit pointer jumps past memory
+ replength = 3 + readBitsFromStream(bp, in, 2);
+ unsigned long value; //set value to the previous code
+ if((i - 1) < HLIT) value = bitlen[i - 1];
+ else value = bitlenD[i - HLIT - 1];
+ for(size_t n = 0; n < replength; n++) //repeat this value in the next lengths
+ {
+ if(i >= HLIT + HDIST) { error = 13; return; } //error: i is larger than the amount of codes
+ if(i < HLIT) bitlen[i++] = value; else bitlenD[i++ - HLIT] = value;
+ }
+ }
+ else if(code == 17) //repeat "0" 3-10 times
+ {
+ if(bp >> 3 >= inlength) { error = 50; return; } //error, bit pointer jumps past memory
+ replength = 3 + readBitsFromStream(bp, in, 3);
+ zlibinfo->back().treecodes.push_back(replength); //tree symbol code repetitions
+ for(size_t n = 0; n < replength; n++) //repeat this value in the next lengths
+ {
+ if(i >= HLIT + HDIST) { error = 14; return; } //error: i is larger than the amount of codes
+ if(i < HLIT) bitlen[i++] = 0; else bitlenD[i++ - HLIT] = 0;
+ }
+ }
+ else if(code == 18) //repeat "0" 11-138 times
+ {
+ if(bp >> 3 >= inlength) { error = 50; return; } //error, bit pointer jumps past memory
+ replength = 11 + readBitsFromStream(bp, in, 7);
+ zlibinfo->back().treecodes.push_back(replength); //tree symbol code repetitions
+ for(size_t n = 0; n < replength; n++) //repeat this value in the next lengths
+ {
+ if(i >= HLIT + HDIST) { error = 15; return; } //error: i is larger than the amount of codes
+ if(i < HLIT) bitlen[i++] = 0; else bitlenD[i++ - HLIT] = 0;
+ }
+ }
+ else { error = 16; return; } //error: somehow an unexisting code appeared. This can never happen.
+ }
+ if(bitlen[256] == 0) { error = 64; return; } //the length of the end code 256 must be larger than 0
+ error = tree.makeFromLengths(bitlen, 15);
+ if(error) return; //now we've finally got HLIT and HDIST, so generate the code trees, and the function is done
+ error = treeD.makeFromLengths(bitlenD, 15);
+ if(error) return;
+ zlibinfo->back().treebits = bp - bpstart;
+ //lit/len/end symbol lengths
+ for(size_t j = 0; j < bitlen.size(); j++) zlibinfo->back().litlenlengths.push_back(bitlen[j]);
+ //dist lengths
+ for(size_t j = 0; j < bitlenD.size(); j++) zlibinfo->back().distlengths.push_back(bitlenD[j]);
+ }
+
+ void inflateHuffmanBlock(std::vector<unsigned char>& out,
+ const unsigned char* in, size_t& bp, size_t& pos, size_t inlength, unsigned long btype)
+ {
+ size_t numcodes = 0, numlit = 0, numlen = 0; //for logging
+ if(btype == 1) { generateFixedTrees(codetree, codetreeD); }
+ else if(btype == 2) { getTreeInflateDynamic(codetree, codetreeD, in, bp, inlength); if(error) return; }
+ for(;;)
+ {
+ unsigned long code = huffmanDecodeSymbol(in, bp, codetree, inlength); if(error) return;
+ numcodes++;
+ zlibinfo->back().lz77_lcode.push_back(code); //output code
+ zlibinfo->back().lz77_dcode.push_back(0);
+ zlibinfo->back().lz77_lbits.push_back(0);
+ zlibinfo->back().lz77_dbits.push_back(0);
+ zlibinfo->back().lz77_lvalue.push_back(0);
+ zlibinfo->back().lz77_dvalue.push_back(0);
+
+ if(code == 256) break; //end code
+ else if(code <= 255) //literal symbol
+ {
+ out.push_back((unsigned char)(code));
+ pos++;
+ numlit++;
+ }
+ else if(code >= 257 && code <= 285) //length code
+ {
+ size_t length = LENBASE[code - 257], numextrabits = LENEXTRA[code - 257];
+ if((bp >> 3) >= inlength) { error = 51; return; } //error, bit pointer will jump past memory
+ length += readBitsFromStream(bp, in, numextrabits);
+ unsigned long codeD = huffmanDecodeSymbol(in, bp, codetreeD, inlength); if(error) return;
+ if(codeD > 29) { error = 18; return; } //error: invalid dist code (30-31 are never used)
+ unsigned long dist = DISTBASE[codeD], numextrabitsD = DISTEXTRA[codeD];
+ if((bp >> 3) >= inlength) { error = 51; return; } //error, bit pointer will jump past memory
+ dist += readBitsFromStream(bp, in, numextrabitsD);
+ size_t start = pos, back = start - dist; //backwards
+ for(size_t i = 0; i < length; i++)
+ {
+ out.push_back(out[back++]);
+ pos++;
+ if(back >= start) back = start - dist;
+ }
+ numlen++;
+ zlibinfo->back().lz77_dcode.back() = codeD; //output distance code
+ zlibinfo->back().lz77_lbits.back() = numextrabits; //output length extra bits
+ zlibinfo->back().lz77_dbits.back() = numextrabitsD; //output dist extra bits
+ zlibinfo->back().lz77_lvalue.back() = length; //output length
+ zlibinfo->back().lz77_dvalue.back() = dist; //output dist
+ }
+ }
+ zlibinfo->back().numlit = numlit; //output number of literal symbols
+ zlibinfo->back().numlen = numlen; //output number of length symbols
+ }
+
+ void inflateNoCompression(std::vector<unsigned char>& out,
+ const unsigned char* in, size_t& bp, size_t& pos, size_t inlength)
+ {
+ while((bp & 0x7) != 0) bp++; //go to first boundary of byte
+ size_t p = bp / 8;
+ if(p >= inlength - 4) { error = 52; return; } //error, bit pointer will jump past memory
+ unsigned long LEN = in[p] + 256u * in[p + 1], NLEN = in[p + 2] + 256u * in[p + 3]; p += 4;
+ if(LEN + NLEN != 65535) { error = 21; return; } //error: NLEN is not one's complement of LEN
+ if(p + LEN > inlength) { error = 23; return; } //error: reading outside of in buffer
+ for(unsigned long n = 0; n < LEN; n++)
+ {
+ out.push_back(in[p++]); //read LEN bytes of literal data
+ pos++;
+ }
+ bp = p * 8;
+ }
+
+ int decompress(std::vector<unsigned char>& out, const std::vector<unsigned char>& in) //returns error value
+ {
+ if(in.size() < 2) { return 53; } //error, size of zlib data too small
+ //error: 256 * in[0] + in[1] must be a multiple of 31, the FCHECK value is supposed to be made that way
+ if((in[0] * 256 + in[1]) % 31 != 0) { return 24; }
+ unsigned long CM = in[0] & 15, CINFO = (in[0] >> 4) & 15, FDICT = (in[1] >> 5) & 1;
+ //error: only compression method 8: inflate with sliding window of 32k is supported by the PNG spec
+ if(CM != 8 || CINFO > 7) { return 25; }
+ //error: the PNG spec says about the zlib stream: "The additional flags shall not specify a preset dictionary."
+ if(FDICT != 0) { return 26; }
+ inflate(out, in, 2);
+ return error; //note: adler32 checksum was skipped and ignored
+ }
+};
+
+struct ExtractPNG //PNG decoding and information extraction
+{
+ std::vector<ZlibBlockInfo>* zlibinfo;
+ ExtractPNG(std::vector<ZlibBlockInfo>* info) : zlibinfo(info) {};
+ int error;
+ void decode(const unsigned char* in, size_t size)
+ {
+ error = 0;
+ if(size == 0 || in == 0) { error = 48; return; } //the given data is empty
+ readPngHeader(&in[0], size); if(error) return;
+ size_t pos = 33; //first byte of the first chunk after the header
+ std::vector<unsigned char> idat; //the data from idat chunks
+ bool IEND = false;
+ //loop through the chunks, ignoring unknown chunks and stopping at IEND chunk.
+ //IDAT data is put at the start of the in buffer
+ while(!IEND)
+ {
+ //error: size of the in buffer too small to contain next chunk
+ if(pos + 8 >= size) { error = 30; return; }
+ size_t chunkLength = read32bitInt(&in[pos]); pos += 4;
+ if(chunkLength > 2147483647) { error = 63; return; }
+ //error: size of the in buffer too small to contain next chunk
+ if(pos + chunkLength >= size) { error = 35; return; }
+ //IDAT chunk, containing compressed image data
+ if(in[pos + 0] == 'I' && in[pos + 1] == 'D' && in[pos + 2] == 'A' && in[pos + 3] == 'T')
+ {
+ idat.insert(idat.end(), &in[pos + 4], &in[pos + 4 + chunkLength]);
+ pos += (4 + chunkLength);
+ }
+ else if(in[pos + 0] == 'I' && in[pos + 1] == 'E' && in[pos + 2] == 'N' && in[pos + 3] == 'D')
+ {
+ pos += 4;
+ IEND = true;
+ }
+ else //it's not an implemented chunk type, so ignore it: skip over the data
+ {
+ pos += (chunkLength + 4); //skip 4 letters and uninterpreted data of unimplemented chunk
+ }
+ pos += 4; //step over CRC (which is ignored)
+ }
+ std::vector<unsigned char> out; //now the out buffer will be filled
+ ExtractZlib zlib(zlibinfo); //decompress with the Zlib decompressor
+ error = zlib.decompress(out, idat);
+ if(error) return; //stop if the zlib decompressor returned an error
+ }
+
+ //read the information from the header and store it in the Info
+ void readPngHeader(const unsigned char* in, size_t inlength)
+ {
+ if(inlength < 29) { error = 27; return; } //error: the data length is smaller than the length of the header
+ 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) { error = 28; return; } //no PNG signature
+ //error: it doesn't start with a IHDR chunk!
+ if(in[12] != 'I' || in[13] != 'H' || in[14] != 'D' || in[15] != 'R') { error = 29; return; }
+ }
+
+ unsigned long readBitFromReversedStream(size_t& bitp, const unsigned char* bits)
+ {
+ unsigned long result = (bits[bitp >> 3] >> (7 - (bitp & 0x7))) & 1;
+ bitp++;
+ return result;
+ }
+
+ unsigned long readBitsFromReversedStream(size_t& bitp, const unsigned char* bits, unsigned long nbits)
+ {
+ unsigned long result = 0;
+ for(size_t i = nbits - 1; i < nbits; i--) result += ((readBitFromReversedStream(bitp, bits)) << i);
+ return result;
+ }
+
+ void setBitOfReversedStream(size_t& bitp, unsigned char* bits, unsigned long bit)
+ {
+ bits[bitp >> 3] |= (bit << (7 - (bitp & 0x7))); bitp++;
+ }
+
+ unsigned long read32bitInt(const unsigned char* buffer)
+ {
+ return (unsigned int)((buffer[0] << 24u) | (buffer[1] << 16u) | (buffer[2] << 8u) | buffer[3]);
+ }
+};
+
+void extractZlibInfo(std::vector<ZlibBlockInfo>& zlibinfo, const std::vector<unsigned char>& in)
+{
+ ExtractPNG decoder(&zlibinfo);
+ decoder.decode(&in[0], in.size());
+
+ if(decoder.error) std::cout << "extract error: " << decoder.error << std::endl;
+}
+
+} // namespace lodepng
diff --git a/lodepng_util.h b/lodepng_util.h
new file mode 100644
index 0000000..e74bbb5
--- /dev/null
+++ b/lodepng_util.h
@@ -0,0 +1,151 @@
+/*
+LodePNG Utils
+
+Copyright (c) 2005-2014 Lode Vandevenne
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+/*
+Extra C++ utilities for LodePNG, for convenience.
+*/
+
+#include <string>
+#include <vector>
+#include "lodepng.h"
+
+#pragma once
+
+namespace lodepng
+{
+
+/*
+Returns info from the header of the PNG by value, purely for convenience.
+Does NOT check for errors. Returns bogus info if the PNG has an error.
+Does not require cleanup of allocated memory because no palette or text chunk
+info is in the LodePNGInfo object after checking only the header of the PNG.
+*/
+LodePNGInfo getPNGHeaderInfo(const std::vector<unsigned char>& png);
+
+/*
+Get the names and sizes of all chunks in the PNG file.
+Returns 0 if ok, non-0 if error happened.
+*/
+unsigned getChunkInfo(std::vector<std::string>& names, std::vector<size_t>& sizes,
+ const std::vector<unsigned char>& png);
+
+/*
+Returns the names and full chunks (including the name and everything else that
+makes up the chunk) for all chunks except IHDR, PLTE, IDAT and IEND.
+It separates the chunks into 3 separate lists, representing the chunks between
+certain critical chunks: 0: IHDR-PLTE, 1: PLTE-IDAT, 2: IDAT-IEND
+Returns 0 if ok, non-0 if error happened.
+*/
+unsigned getChunks(std::vector<std::string> names[3],
+ std::vector<std::vector<unsigned char> > chunks[3],
+ const std::vector<unsigned char>& png);
+
+/*
+Inserts chunks into the given png file. The chunks must be fully encoded,
+including length, type, content and CRC.
+The array index determines where it goes:
+0: between IHDR and PLTE, 1: between PLTE and IDAT, 2: between IDAT and IEND.
+They're appended at the end of those locations within the PNG.
+Returns 0 if ok, non-0 if error happened.
+*/
+unsigned insertChunks(std::vector<unsigned char>& png,
+ const std::vector<std::vector<unsigned char> > chunks[3]);
+
+/*
+Get the filtertypes of each scanline in this PNG file.
+Returns 0 if ok, 1 if PNG decoding error happened.
+
+For a non-interlaced PNG, it returns one filtertype per scanline, in order.
+
+For interlaced PNGs, it returns a result as if it's not interlaced. It returns
+one filtertype per scanline, in order. The values match pass 6 and 7 of the
+Adam7 interlacing, alternating between the two, so that the values correspond
+the most to their scanlines.
+*/
+unsigned getFilterTypes(std::vector<unsigned char>& filterTypes, const std::vector<unsigned char>& png);
+
+/*
+Get the filtertypes of each scanline in every interlace pass this PNG file.
+Returns 0 if ok, 1 if PNG decoding error happened.
+
+For a non-interlaced PNG, it returns one filtertype per scanline, in order, in
+a single std::vector in filterTypes.
+
+For an interlaced PNG, it returns 7 std::vectors in filterTypes, one for each
+Adam7 pass. The amount of values per pass can be calculated as follows, where
+w and h are the size of the image and all divisions are integer divisions:
+pass 1: (h + 7) / 8
+pass 2: w <= 4 ? 0 : (h + 7) / 8
+pass 3: h <= 4 ? 0 : (h + 7) / 8
+pass 4: w <= 2 ? 0 : (h + 3) / 4
+pass 5: h <= 2 ? 0 : (h + 3) / 4
+pass 6: w <= 1 ? 0 : (h + 1) / 2
+pass 7: h <= 1 ? 0 : (h + 1) / 2
+*/
+unsigned getFilterTypesInterlaced(std::vector<std::vector<unsigned char> >& filterTypes,
+ const std::vector<unsigned char>& png);
+
+/*
+Returns the value of the i-th pixel in an image with 1, 2, 4 or 8-bit color.
+E.g. if bits is 4 and i is 5, it returns the 5th nibble (4-bit group), which
+is the second half of the 3th byte, in big endian (PNG's endian order).
+*/
+int getPaletteValue(const unsigned char* data, size_t i, int bits);
+
+/*
+The information for extractZlibInfo.
+*/
+struct ZlibBlockInfo
+{
+ int btype; //block type (0-2)
+ size_t compressedbits; //size of compressed block in bits
+ size_t uncompressedbytes; //size of uncompressed block in bytes
+
+ // only filled in for block type 2
+ size_t treebits; //encoded tree size in bits
+ int hlit; //the HLIT value that was filled in for this tree
+ int hdist; //the HDIST value that was filled in for this tree
+ int hclen; //the HCLEN value that was filled in for this tree
+ std::vector<int> clcl; //19 code length code lengths (compressed tree's tree)
+ std::vector<int> treecodes; //N tree codes, with values 0-18. Values 17 or 18 are followed by the repetition value.
+ std::vector<int> litlenlengths; //288 code lengths for lit/len symbols
+ std::vector<int> distlengths; //32 code lengths for dist symbols
+
+ // only filled in for block types 1 or 2
+ std::vector<int> lz77_lcode; //LZ77 codes. 0-255: literals. 256: end symbol. 257-285: length code of length/dist pairs
+ // the next vectors have the same size as lz77_lcode, but an element only has meaningful value if lz77_lcode contains a length code.
+ std::vector<int> lz77_dcode;
+ std::vector<int> lz77_lbits;
+ std::vector<int> lz77_dbits;
+ std::vector<int> lz77_lvalue;
+ std::vector<int> lz77_dvalue;
+ size_t numlit; //number of lit codes in this block
+ size_t numlen; //number of len codes in this block
+};
+
+//Extracts all info needed from a PNG file to reconstruct the zlib compression exactly.
+void extractZlibInfo(std::vector<ZlibBlockInfo>& zlibinfo, const std::vector<unsigned char>& in);
+
+} // namespace lodepng
diff --git a/pngdetail.cpp b/pngdetail.cpp
new file mode 100644
index 0000000..71e7aaf
--- /dev/null
+++ b/pngdetail.cpp
@@ -0,0 +1,766 @@
+/*
+LodePNG pngdetail
+
+Copyright (c) 2005-2013 Lode Vandevenne
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+//g++ lodepng_util.cpp lodepng.cpp pngdetail.cpp -ansi -pedantic -Wall -Wextra -o pngdetail -O3
+
+
+/*
+Utility program that shows a lot of information in the console about a PNG file,
+including color type, text chunks, the names and sizes of all chunks in the
+image, all the zlib compression blocks and symbols, etc...
+
+compression info:
+./pngdetail -sfczB image.png
+
+everything:
+./pngdetail -sPlLA@cfzB7 image.png
+
+everything except huge output:
+./pngdetail -sPlAcfzB image.png
+*/
+
+#include "lodepng.h"
+#include "lodepng_util.h"
+#include <iostream>
+#include <iomanip>
+#include <sstream>
+
+struct Options
+{
+ bool show_png_summary; //show filesize, pixels and color type on single line
+ bool show_png_info; //show things like filesize, width, height, palette size, ...
+ bool show_extra_png_info; //in addition to show_png_info, show extra info
+ bool show_palette; //show all palette values
+ bool show_palette_pixels; //show palette indices of pixels
+ bool show_ascii_art; //show ascii art image of the whole PNG
+ int ascii_art_size;
+ bool show_colors_hex; //show all pixel colors in RGBA CSS hex format
+ bool show_colors_hex_16; //show all pixels colors with 16-bit per channel RGBA info
+ bool show_chunks; //show the PNG chunk names and their lengths
+ bool show_chunks2; //alternate form to print chunks
+ bool show_filters; //show the PNG filter of each scanline (not supported for interlaced PNGs currently)
+ bool zlib_info; //show basic zlib info
+ bool zlib_blocks; //show type, tree info, code length summaries and sizes for each zlib block
+ bool zlib_counts; //in addition to the zlib_blocks info, show counts of occurances all symbols
+ bool zlib_full; //in addition to the zlib_blocks info, show all symbols, one per line (huge output)
+ bool use_hex; //show some sizes or positions in hexadecimal
+
+ Options() : show_png_summary(false), show_png_info(false), show_extra_png_info(false),
+ show_palette(false), show_palette_pixels(false),
+ show_ascii_art(false), ascii_art_size(40), show_colors_hex(false), show_colors_hex_16(false),
+ show_chunks(false), show_chunks2(false), show_filters(false),
+ zlib_info(false), zlib_blocks(false), zlib_counts(false), zlib_full(false), use_hex(false)
+ {
+ }
+};
+
+std::string colorTypeString(LodePNGColorType type)
+{
+ std::string name;
+ switch(type)
+ {
+ case LCT_GREY: name = "grey"; break;
+ case LCT_RGB: name = "rgb"; break;
+ case LCT_PALETTE: name = "palette"; break;
+ case LCT_GREY_ALPHA: name = "grey with alpha"; break;
+ case LCT_RGBA: name = "rgba"; break;
+ default: name = "invalid"; break;
+ }
+ std::stringstream ss;
+ ss << type << " (" << name << ")";
+ return ss.str();
+}
+
+/*
+Display general info about the PNG.
+*/
+void displayPNGInfo(const LodePNGInfo& info, const Options& options)
+{
+ const LodePNGColorMode& color = info.color;
+
+ if(options.show_extra_png_info)
+ {
+ std::cout << "Compression method: " << info.compression_method << std::endl;
+ std::cout << "Filter method: " << info.filter_method << std::endl;
+ }
+ std::cout << "Color type: " << colorTypeString(color.colortype) << std::endl;
+ std::cout << "Bit depth: " << color.bitdepth << std::endl;
+ if(options.show_extra_png_info)
+ {
+ std::cout << "Bits per pixel: " << lodepng_get_bpp(&color) << std::endl;
+ std::cout << "Channels per pixel: " << lodepng_get_channels(&color) << std::endl;
+ std::cout << "Is greyscale type: " << lodepng_is_greyscale_type(&color) << std::endl;
+ std::cout << "Can have alpha: " << lodepng_can_have_alpha(&color) << std::endl;
+ }
+ if (!options.show_palette) std::cout << "Palette size: " << color.palettesize << std::endl;
+ if(options.show_extra_png_info) std::cout << "Has color key: " << color.key_defined << std::endl;
+ if(color.key_defined)
+ {
+ std::cout << "Color key r: " << color.key_r << std::endl;
+ std::cout << "Color key g: " << color.key_g << std::endl;
+ std::cout << "Color key b: " << color.key_b << std::endl;
+ }
+ std::cout << "Interlace method: " << info.interlace_method << std::endl;
+ if(options.show_extra_png_info) std::cout << "Texts: " << info.text_num << std::endl;
+ for(size_t i = 0; i < info.text_num; i++)
+ {
+ std::cout << "Text: " << info.text_keys[i] << ": " << info.text_strings[i] << std::endl;
+ }
+ if(options.show_extra_png_info) std::cout << "International texts: " << info.itext_num << std::endl;
+ for(size_t i = 0; i < info.itext_num; i++)
+ {
+ std::cout << "Text: "
+ << info.itext_keys[i] << ", "
+ << info.itext_langtags[i] << ", "
+ << info.itext_transkeys[i] << ": "
+ << info.itext_strings[i] << std::endl;
+ }
+ if(options.show_extra_png_info) std::cout << "Time defined: " << info.time_defined << std::endl;
+ if(info.time_defined)
+ {
+ const LodePNGTime& time = info.time;
+ std::cout << "year: " << time.year << std::endl;
+ std::cout << "month: " << time.month << std::endl;
+ std::cout << "day: " << time.day << std::endl;
+ std::cout << "hour: " << time.hour << std::endl;
+ std::cout << "minute: " << time.minute << std::endl;
+ std::cout << "second: " << time.second << std::endl;
+ }
+ if(options.show_extra_png_info) std::cout << "Physics defined: " << info.phys_defined << std::endl;
+ if(info.phys_defined)
+ {
+ std::cout << "physics X: " << info.phys_x << std::endl;
+ std::cout << "physics Y: " << info.phys_y << std::endl;
+ std::cout << "physics unit: " << info.phys_unit << std::endl;
+ }
+}
+
+
+/*
+Display the names and sizes of all chunks in the PNG file.
+*/
+void displayChunkNames(const std::vector<unsigned char>& buffer, const Options& options)
+{
+ std::vector<std::string> names;
+ std::vector<size_t> sizes;
+ lodepng::getChunkInfo(names, sizes, buffer);
+
+ if(options.show_chunks2)
+ {
+ std::cout << "Chunk types: ";
+ for(size_t i = 0; i < names.size(); i++) std::cout << names[i] << " ";
+ std::cout << std::endl;
+ std::cout << "Chunk sizes: ";
+ for(size_t i = 0; i < sizes.size(); i++) std::cout << sizes[i] << " ";
+ std::cout << std::endl;
+ }
+ else
+ {
+ std::cout << "Chunks (type: lengths):";
+ std::string last_type;
+ for(size_t i = 0; i < names.size(); i++)
+ {
+ if(last_type != names[i])
+ {
+ std::cout << std::endl;
+ std::cout << " " << names[i] << ": ";
+ }
+ last_type = names[i];
+
+ std::cout << sizes[i] << " ";
+ }
+ std::cout << std::endl;
+ }
+}
+
+
+/*
+Show ASCII art preview of the image
+image is given in 16-bit big endian
+*/
+void displayAsciiArt(const std::vector<unsigned char>& image, unsigned w, unsigned h, unsigned asciiw)
+{
+ if(w > 0 && h > 0)
+ {
+ //std::cout << "ASCII Art Preview: " << std::endl;
+ unsigned w2 = asciiw;
+ if(w < w2) w2 = w;
+ unsigned h2 = h * w2 / w;
+ h2 = (h2 * 2) / 3; //compensate for non-square characters in terminal
+ if(h2 > (w2 * 2)) h2 = w2 * 2; //avoid too large output
+
+ std::cout << '+';
+ for(unsigned x = 0; x < w2; x++) std::cout << '-';
+ std::cout << '+' << std::endl;
+ for(unsigned y = 0; y < h2; y++)
+ {
+ std::cout << "|";
+ for(unsigned x = 0; x < w2; x++)
+ {
+ unsigned x2 = x * w / w2;
+ unsigned y2 = y * h / h2;
+ int r = image[y2 * w * 8 + x2 * 8 + 0];
+ int g = image[y2 * w * 8 + x2 * 8 + 2];
+ int b = image[y2 * w * 8 + x2 * 8 + 4];
+ int a = image[y2 * w * 8 + x2 * 8 + 6];
+ int lightness = ((r + g + b) / 3) * a / 255;
+ int min = (r < g && r < b) ? r : (g < b ? g : b);
+ int max = (r > g && r > b) ? r : (g > b ? g : b);
+ int saturation = max - min;
+ int letter = 'i'; //i for grey, or r,y,g,c,b,m for colors
+ if(saturation > 32)
+ {
+ int h = lightness >= (min + max) / 2;
+ if(h) letter = (min == r ? 'c' : (min == g ? 'm' : 'y'));
+ else letter = (max == r ? 'r' : (max == g ? 'g' : 'b'));
+ }
+ int symbol = ' ';
+ if(lightness > 224) symbol = 'W';
+ else if(lightness > 128) symbol = letter - 32;
+ else if(lightness > 64) symbol = letter;
+ else if(lightness > 48) symbol = ';';
+ else if(lightness > 32) symbol = ':';
+ else if(lightness > 24) symbol = ',';
+ else if(lightness > 16) symbol = '.';
+ std::cout << (char)symbol;
+ }
+ std::cout << "|";
+ std::cout << std::endl;
+ }
+ std::cout << '+';
+ for(unsigned x = 0; x < w2; x++) std::cout << '-';
+ std::cout << '+' << std::endl;
+ }
+}
+
+//sixteen: print 16 bits per pixel
+//alpha: print alpha channel
+//input image ALWAYS given in 16-bit per channel RGBA
+void displayColorsHex(const std::vector<unsigned char>& image, unsigned w, unsigned h, bool sixteen)
+{
+ std::ios_base::fmtflags flags = std::cout.flags();
+
+ if(w > 0 && h > 0)
+ {
+ std::cout << "Colors (CSS RGBA hex format):" << std::endl;
+
+ for(unsigned y = 0; y < h; y++)
+ {
+ std::cout.flags(flags); //print line numbers in hex or dec whatever it originally was
+ std::cout << y << ":";
+ for(unsigned x = 0; x < w; x++)
+ {
+ size_t index = y * w * 8 + x * 8;
+ if (sixteen)
+ {
+ int r = image[index + 0] * 256 + image[index + 1];
+ int g = image[index + 2] * 256 + image[index + 3];
+ int b = image[index + 4] * 256 + image[index + 5];
+ int a = image[index + 6] * 256 + image[index + 7];
+ std::cout << std::hex << std::setfill('0') << " #" << std::setw(4) << r << std::setw(4) << g << std::setw(4) << b << std::setw(4) << a;
+ }
+ else
+ {
+ int r = image[index + 0];
+ int g = image[index + 2];
+ int b = image[index + 4];
+ int a = image[index + 6];
+ std::cout << std::hex << std::setfill('0') << " #" << std::setw(2) << r << std::setw(2) << g << std::setw(2) << b << std::setw(2) << a;
+ }
+ }
+ std::cout << std::endl;
+ }
+ }
+
+ std::cout.flags(flags);
+}
+
+
+/*
+Show the filtertypes of each scanline in this PNG image.
+*/
+void displayFilterTypes(const std::vector<unsigned char>& buffer)
+{
+ std::vector<std::vector<unsigned char> > types;
+ lodepng::getFilterTypesInterlaced(types, buffer);
+
+ if(types.size() == 7)
+ {
+ std::cout << "Filter types (Adam7 interlaced):" << std::endl;
+ for(int j = 0; j < 7; j++)
+ {
+ std::cout << " Pass " << (j + 1) << ": ";
+ for(size_t i = 0; i < types[j].size(); i++)
+ {
+ std::cout << (int)(types[j][i]);
+ }
+ std::cout << std::endl;
+ }
+ }
+ else
+ {
+ std::cout << "Filter types: ";
+ for(size_t i = 0; i < types[0].size(); i++)
+ {
+ std::cout << (int)(types[0][i]);
+ }
+ std::cout << std::endl;
+ }
+}
+
+//image type MUST be palette
+void displayPalette(const std::vector<unsigned char>& buffer)
+{
+ unsigned w, h;
+ lodepng::State state;
+ std::vector<unsigned char> out;
+
+ state.decoder.color_convert = 0;
+ state.decoder.fix_png = 1;
+
+ lodepng::decode(out, w, h, state, buffer);
+
+ std::cout << "Palette size: " << state.info_png.color.palettesize << std::endl;
+ std::cout << "Palette colors: ";
+ std::ios_base::fmtflags flags = std::cout.flags();
+ std::cout << std::hex << std::setfill('0');
+ for(size_t i = 0; i < state.info_png.color.palettesize; i++)
+ {
+ unsigned char* p = &state.info_png.color.palette[i * 4];
+ std::cout << "#" << std::setw(2) << (int)p[0] << std::setw(2) << (int)p[1] << std::setw(2) << (int)p[2] << std::setw(2) << (int)p[3] << " ";
+ }
+ std::cout.flags(flags);
+ std::cout << std::endl;
+
+
+ if (state.info_png.color.colortype == LCT_PALETTE)
+ {
+ std::vector<size_t> count(256, 0);
+ size_t outofbounds = 0;
+
+ for(size_t i = 0; i < w * h; i++)
+ {
+ int value = lodepng::getPaletteValue(&out[0], i, state.info_raw.bitdepth);
+ count[value]++;
+ if(value >= (int)state.info_raw.palettesize) outofbounds++;
+ }
+
+ std::cout << "Palette count: ";
+ for(size_t i = 0; i < state.info_raw.palettesize; i++)
+ {
+ std::cout << count[i] << " ";
+ }
+ std::cout << std::endl;
+
+ if(outofbounds > 0) std::cout << "Out of bounds palette values: " << outofbounds << std::endl;
+ }
+}
+
+//image type MUST be palette
+void displayPalettePixels(const std::vector<unsigned char>& buffer)
+{
+ unsigned w, h;
+ lodepng::State state;
+ std::vector<unsigned char> out;
+
+ state.decoder.color_convert = 0;
+ state.decoder.fix_png = 1;
+
+ lodepng::decode(out, w, h, state, buffer);
+
+
+ if (state.info_png.color.colortype == LCT_PALETTE)
+ {
+ std::cout << "Pixel palette indices:" << std::endl;
+ for(size_t i = 0; i < w * h; i++)
+ {
+ int value = lodepng::getPaletteValue(&out[0], i, state.info_raw.bitdepth);
+ std::cout << value << ", ";
+ if(i % w == w - 1) std::cout << std::endl;
+ }
+ }
+}
+
+void printZlibInfo(const std::vector<unsigned char>& in, const Options& options)
+{
+ if(!options.zlib_info && !options.zlib_blocks) return;
+
+ std::vector<lodepng::ZlibBlockInfo> zlibinfo;
+ lodepng::extractZlibInfo(zlibinfo, in);
+
+ if(options.zlib_info)
+ {
+ //std::cout << "Zlib info: " << std::endl;
+ size_t compressed = 0;
+ size_t uncompressed = 0;
+ std::vector<size_t> boundaries_compressed;
+ std::vector<size_t> boundaries_uncompressed;
+ for(size_t i = 0; i < zlibinfo.size(); i++)
+ {
+ compressed += zlibinfo[i].compressedbits / 8;
+ uncompressed += zlibinfo[i].uncompressedbytes;
+ boundaries_compressed.push_back(compressed);
+ boundaries_uncompressed.push_back(uncompressed);
+ }
+
+ std::cout << "Compressed size: " << compressed << std::endl;
+ std::cout << "Uncompressed size: " << uncompressed << std::endl;
+ std::cout << "Amount of zlib blocks: " << zlibinfo.size() << std::endl;
+ if(zlibinfo.size() > 1)
+ {
+ std::cout << "Block sizes (uncompressed): ";
+ for(size_t i = 0; i < zlibinfo.size(); i++)
+ std::cout << (zlibinfo[i].compressedbits / 8) << " ";
+ std::cout << std::endl;
+ std::cout << "Block sizes (compressed): ";
+ for(size_t i = 0; i < zlibinfo.size(); i++)
+ std::cout << zlibinfo[i].uncompressedbytes << " ";
+ std::cout << std::endl;
+ std::cout << "Block boundaries (uncompressed): ";
+ for(size_t i = 0; i + 1 < boundaries_uncompressed.size(); i++)
+ std::cout << boundaries_uncompressed[i] << " ";
+ std::cout << std::endl;
+ std::cout << "Block boundaries (compressed): ";
+ for(size_t i = 0; i + 1 < boundaries_compressed.size(); i++)
+ std::cout << boundaries_compressed[i] << " ";
+ std::cout << std::endl;
+ }
+ }
+
+ if(options.zlib_blocks)
+ {
+ for(size_t i = 0; i < zlibinfo.size(); i++)
+ {
+ const lodepng::ZlibBlockInfo& info = zlibinfo[i];
+
+ std::cout << "Zlib block " << i << ":" << std::endl;
+ std::cout << " block type: " << info.btype << std::endl;
+
+ size_t compressedsize = info.compressedbits / 8;
+ size_t uncompressedsize = info.uncompressedbytes;
+ std::cout << " block compressed: " << compressedsize << " (" << compressedsize / 1024 << "K) (" << info.compressedbits << " bits)" << std::endl;
+ std::cout << " block uncompressed: " << uncompressedsize << " (" << uncompressedsize / 1024 << "K)" << std::endl;
+
+ if(info.btype > 2)
+ {
+ std::cout << "Error: Invalid Block Type" << std::endl;
+ return;
+ }
+
+ if(info.btype == 2)
+ {
+ std::cout << " encoded trees size: " << info.treebits / 8 << " (" << info.treebits << " bits)" << std::endl;
+ std::cout << " HLIT: " << info.hlit << std::endl;
+ std::cout << " HDIST: " << info.hdist << std::endl;
+ std::cout << " HCLEN: " << info.hclen << std::endl;
+ std::cout << std::hex;
+ std::cout << " code length code lengths: "; for(size_t j = 0; j < 19; j++) std::cout << info.clcl[j]; std::cout << std::endl;
+ if(!options.use_hex) std::cout << std::dec;
+ if(options.zlib_full)
+ {
+ for(size_t j = 0; j < info.treecodes.size(); j++)
+ {
+ int code = info.treecodes[j];
+ if(code < 17)
+ {
+ std::cout << " tree: " << code << std::endl;
+ }
+ else
+ {
+ j++;
+ std::cout << " tree: " << code << " rep: " << info.treecodes[j] << std::endl;
+ }
+
+ }
+ }
+
+ std::cout << std::hex;
+ std::cout << " lit code lengths 0-127 : "; for(size_t j = 0; j < 128; j++) std::cout << info.litlenlengths[j]; std::cout << std::endl;
+ std::cout << " lit code lengths 128-255: "; for(size_t j = 128; j < 256; j++) std::cout << info.litlenlengths[j]; std::cout << std::endl;
+ std::cout << " end code length : "; std::cout << info.litlenlengths[256]; std::cout << std::endl;
+ std::cout << " len code lengths : "; for(size_t j = 257; j < 288; j++) std::cout << info.litlenlengths[j]; std::cout << std::endl;
+ std::cout << " dist code lengths : "; for(size_t j = 0; j < 32; j++) std::cout << info.distlengths[j]; std::cout << std::endl;
+ if(!options.use_hex) std::cout << std::dec;
+ }
+
+
+ if(info.btype != 0)
+ {
+ std::cout << " code counts: lit: " << info.numlit << ", len/dist: " << info.numlen << ", total: " << (info.numlit + info.numlen + 1) << ", with dists: " << (info.numlit + 2 * info.numlen + 1) << std::endl;
+
+ if(options.zlib_full)
+ {
+ for(size_t j = 0; j < info.lz77_lcode.size(); j++)
+ {
+ int symbol = info.lz77_lcode[j];
+ if(symbol == 256)
+ {
+ std::cout << " end" << std::endl;
+ }
+ else if(symbol < 256)
+ {
+ std::cout << " lit: " << symbol << std::endl;
+ }
+ else
+ {
+ std::cout << " len: " << info.lz77_lvalue[j] << ", dist: " << info.lz77_dvalue[j] << std::endl;
+ }
+ }
+ }
+
+ if(options.zlib_counts)
+ {
+ std::vector<size_t> ll_count(288, 0);
+ std::vector<size_t> d_count(32, 0);
+ for(size_t j = 0; j < info.lz77_lcode.size(); j++)
+ {
+ int symbol = info.lz77_lcode[j];
+ if(symbol <= 256)
+ {
+ ll_count[symbol]++;
+ }
+ else
+ {
+ ll_count[symbol]++;
+ d_count[info.lz77_dcode[j]]++;
+ }
+ }
+ std::cout << " lit code 0-63 counts : "; for(size_t j = 0; j < 64; j++) std::cout << ll_count[j] << " "; std::cout << std::endl;
+ std::cout << " lit code 64-127 counts : "; for(size_t j = 64; j < 128; j++) std::cout << ll_count[j] << " "; std::cout << std::endl;
+ std::cout << " lit code 128-191 counts: "; for(size_t j = 128; j < 192; j++) std::cout << ll_count[j] << " "; std::cout << std::endl;
+ std::cout << " lit code 192-255 counts: "; for(size_t j = 192; j < 256; j++) std::cout << ll_count[j] << " "; std::cout << std::endl;
+ std::cout << " end code count : "; std::cout << ll_count[256] << " "; std::cout << std::endl;
+ std::cout << " len code counts : "; for(size_t j = 257; j < 288; j++) std::cout << ll_count[j] << " "; std::cout << std::endl;
+ std::cout << " dist code counts : "; for(size_t j = 0; j < 32; j++) std::cout << d_count[j] << " "; std::cout << std::endl;
+ }
+ }
+ }
+ }
+}
+
+void showHelp()
+{
+ std::cout << "pngdetail by Lode Vandevenne\n"
+ "Shows detailed information about a PNG image and its compression\n"
+ "Usage: pngdetail [filename] [options]...\n"
+ "Options:\n"
+ "-s: show PNG file summary on one line\n"
+ "-p: show PNG file info\n"
+ "-P: show extra PNG file info\n"
+ "-l: show palette (if any)\n"
+ "-a: show ascii art rendering of PNG image. Letters rygcbm indicate hue.\n"
+ "-A: show larger ascii art rendering of PNG image. Adding more A's makes it larger.\n"
+ "-#: show every pixel color in CSS RGBA hex format (huge output)\n"
+ "-@: show every pixel color with 16-bit per channel (huge output)\n"
+ "-c: show PNG chunks\n"
+ "-C: show PNG chunks (alternate format)\n"
+ "-f: show PNG filters\n"
+ "-z: show Zlib info\n"
+ "-b: show Zlib blocks\n"
+ "-B: show Zlib block symbol counts\n"
+ "-7: show all lz77 values (huge output)\n"
+ "-x: print most numbers in hexadecimal\n"
+ << std::endl;
+}
+
+unsigned showFileInfo(const std::string& filename, const Options& options)
+{
+ std::vector<unsigned char> buffer;
+ std::vector<unsigned char> image;
+ unsigned w, h;
+
+ lodepng::load_file(buffer, filename); //load the image file with given filename
+
+ lodepng::State state;
+ state.info_raw.colortype = LCT_RGBA;
+ state.info_raw.bitdepth = 16;
+ unsigned error = lodepng::decode(image, w, h, state, buffer);
+
+ if(error)
+ {
+ std::cout << "Decoder error " << error << ": " << lodepng_error_text(error) << std::endl;
+ }
+
+ bool extra_newlines = false;
+
+ if(!error && options.show_png_summary)
+ {
+ std::cout << "Filesize: " << buffer.size() << " (" << buffer.size() / 1024 << "K)" << ", ";
+ std::cout << w << "x" << h << ", ";
+ std::cout << "Color: " << colorTypeString(state.info_png.color.colortype) << ", " << state.info_png.color.bitdepth << " bit" << std::endl;
+ if(extra_newlines) std::cout << std::endl;
+ }
+
+ if(!error && options.show_png_info)
+ {
+ std::cout << "Filesize: " << buffer.size() << " (" << buffer.size() / 1024 << "K)" << std::endl;
+ std::cout << "Width: " << w << std::endl;
+ std::cout << "Height: " << h << std::endl;
+
+ if(options.show_extra_png_info) std::cout << "Num pixels: " << w * h << std::endl;
+ if(options.show_extra_png_info && w > 0 && h > 0)
+ {
+ std::ios_base::fmtflags flags = std::cout.flags();
+ std::cout << "Top left pixel color: #"
+ << std::hex << std::setfill('0')
+ << std::setw(2) << (int)image[0] << std::setw(2) << (int)image[1] << std::setw(2) << (int)image[2] << std::setw(2) << (int)image[3]
+ << std::endl;
+ std::cout.flags(flags);
+ }
+
+ displayPNGInfo(state.info_png, options);
+ if(extra_newlines) std::cout << std::endl;
+ }
+
+ if(options.show_chunks || options.show_chunks2)
+ {
+ displayChunkNames(buffer, options);
+ if(extra_newlines) std::cout << std::endl;
+ }
+
+ if(options.show_filters)
+ {
+ displayFilterTypes(buffer);
+ if(extra_newlines) std::cout << std::endl;
+ }
+
+ if(options.show_palette)
+ {
+ displayPalette(buffer);
+ if(extra_newlines) std::cout << std::endl;
+ }
+
+ if(options.show_palette_pixels)
+ {
+ displayPalettePixels(buffer);
+ if(extra_newlines) std::cout << std::endl;
+ }
+
+ if (!error && options.show_ascii_art)
+ {
+ displayAsciiArt(image, w, h, options.ascii_art_size);
+ if(extra_newlines) std::cout << std::endl;
+ }
+
+ if (!error && (options.show_colors_hex || options.show_colors_hex_16))
+ {
+ displayColorsHex(image, w, h, options.show_colors_hex_16);
+ if(extra_newlines) std::cout << std::endl;
+ }
+
+ printZlibInfo(buffer, options);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ Options options;
+ bool options_chosen = false;
+
+ std::vector<std::string> filenames;
+ for (int i = 1; i < argc; i++)
+ {
+ std::string s = argv[i];
+ if(s[0] == '-' && s.size() > 1)
+ {
+ if(s != "-x") options_chosen = true; //only selecting hexadecimal is no choice, keep the defaults
+ for(size_t j = 1; j < s.size(); j++)
+ {
+ char c = s[j];
+ if(c == 'h')
+ {
+ showHelp();
+ return 0;
+ }
+ else if(c == 's') options.show_png_summary = true;
+ else if(c == 'p') options.show_png_info = true;
+ else if(c == 'P')
+ {
+ options.show_png_info = true;
+ options.show_extra_png_info = true;
+ }
+ else if(c == 'l') options.show_palette = true;
+ else if(c == 'L') options.show_palette_pixels = true;
+ else if(c == 'a') options.show_ascii_art = true;
+ else if(c == 'A')
+ {
+ options.show_ascii_art = true;
+ options.ascii_art_size += 40;
+ }
+ else if(c == '#') options.show_colors_hex = true;
+ else if(c == '@') options.show_colors_hex_16 = true;
+ else if(c == 'c') options.show_chunks = true;
+ else if(c == 'C') options.show_chunks2 = true;
+ else if(c == 'f') options.show_filters = true;
+ else if(c == 'z') options.zlib_info = true;
+ else if(c == 'b') options.zlib_blocks = true;
+ else if(c == 'B')
+ {
+ options.zlib_blocks = true;
+ options.zlib_counts = true;
+ }
+ else if(c == '7')
+ {
+ options.zlib_blocks = true;
+ options.zlib_full = true;
+ }
+ else if(c == 'x')
+ {
+ options.use_hex = true;
+ std::cout << std::hex;
+ }
+ else
+ {
+ std::cout << "Unknown flag: " << c << ". Use -h for help" << std::endl;
+ return 0;
+ }
+
+ }
+ }
+ else filenames.push_back(s);
+ }
+
+ if(filenames.empty())
+ {
+ std::cout << "Please provide a filename to preview" << std::endl;
+ showHelp();
+ return 0;
+ }
+
+ if(!options_chosen)
+ {
+ //fill in defaults
+ options.show_png_info = true;
+ options.show_chunks = true;
+ options.show_filters = true;
+ options.zlib_info = true;
+ }
+
+ for(size_t i = 0; i < filenames.size(); i++)
+ {
+ if(filenames.size() > 1) std::cout << filenames[i] << std::endl;
+ showFileInfo(filenames[i], options);
+ }
+}