summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLode <lvandeve@gmail.com>2015-04-19 16:05:31 +0200
committerLode <lvandeve@gmail.com>2015-04-19 16:05:31 +0200
commitc451d74802333e861ca60f8a5a95ac11a83b5829 (patch)
treea5956a493e21cf3f75455142c009b1fdaa404ca1
parent72c81801212e123abca1faeaf3192d0f0f4bade0 (diff)
boundary package merge
-rw-r--r--lodepng.cpp290
-rw-r--r--lodepng.h3
-rw-r--r--lodepng_benchmark.cpp138
-rw-r--r--lodepng_unittest.cpp8
4 files changed, 236 insertions, 203 deletions
diff --git a/lodepng.cpp b/lodepng.cpp
index c5298cb..e911c40 100644
--- a/lodepng.cpp
+++ b/lodepng.cpp
@@ -1,5 +1,5 @@
/*
-LodePNG version 20150321
+LodePNG version 20150418
Copyright (c) 2005-2015 Lode Vandevenne
@@ -42,7 +42,7 @@ Rename this file to lodepng.cpp to use it for C++, or to lodepng.c to use it for
#pragma warning( disable : 4996 ) /*VS does not like fopen, but fopen_s is not standard C so unusable here*/
#endif /*_MSC_VER */
-const char* LODEPNG_VERSION_STRING = "20150321";
+const char* LODEPNG_VERSION_STRING = "20150418";
/*
This source file is built up in the following large parts. The code sections
@@ -199,15 +199,6 @@ static unsigned uivector_push_back(uivector* p, unsigned c)
p->data[p->size - 1] = c;
return 1;
}
-
-/*copy q to p, returns 1 if success, 0 if failure ==> nothing done*/
-static unsigned uivector_copy(uivector* p, const uivector* q)
-{
- size_t i;
- if(!uivector_resize(p, q->size)) return 0;
- for(i = 0; i != q->size; ++i) p->data[i] = q->data[i];
- return 1;
-}
#endif /*LODEPNG_COMPILE_ENCODER*/
#endif /*LODEPNG_COMPILE_ZLIB*/
@@ -668,101 +659,136 @@ static unsigned HuffmanTree_makeFromLengths(HuffmanTree* tree, const unsigned* b
#ifdef LODEPNG_COMPILE_ENCODER
-/*
-A coin, this is the terminology used for the package-merge algorithm and the
-coin collector's problem. This is used to generate the huffman tree.
-A coin can be multiple coins (when they're merged)
-*/
-typedef struct Coin
-{
- uivector symbols;
- float weight; /*the sum of all weights in this coin*/
-} Coin;
+/*BPM: Boundary Package Merge, see "A Fast and Space-Economical Algorithm for Length-Limited Coding",
+Jyrki Katajainen, Alistair Moffat, Andrew Turpin, 1995.*/
-static void coin_init(Coin* c)
+/*chain node for boundary package merge*/
+typedef struct BPMNode
{
- uivector_init(&c->symbols);
-}
+ int weight; /*the sum of all weights in this chain*/
+ unsigned index; /*index of this leaf node (called "count" in the paper)*/
+ struct BPMNode* tail; /*the next nodes in this chain (null if last)*/
+ int in_use;
+} BPMNode;
-/*argument c is void* so that this dtor can be given as function pointer to the vector resize function*/
-static void coin_cleanup(void* c)
+/*lists of chains*/
+typedef struct BPMLists
{
- uivector_cleanup(&((Coin*)c)->symbols);
-}
+ /*memory pool*/
+ unsigned memsize;
+ BPMNode* memory;
+ unsigned numfree;
+ unsigned nextfree;
+ BPMNode** freelist;
+ /*two heads of lookahead chains per list*/
+ unsigned listsize;
+ BPMNode** chains0;
+ BPMNode** chains1;
+} BPMLists;
-static void coin_copy(Coin* c1, const Coin* c2)
+/*creates a new chain node with the given parameters, from the memory in the lists */
+static BPMNode* bpmnode_create(BPMLists* lists, int weight, unsigned index, BPMNode* tail)
{
- c1->weight = c2->weight;
- uivector_copy(&c1->symbols, &c2->symbols);
-}
+ unsigned i;
+ BPMNode* result;
-static void add_coins(Coin* c1, const Coin* c2)
-{
- size_t i;
- for(i = 0; i != c2->symbols.size; ++i) uivector_push_back(&c1->symbols, c2->symbols.data[i]);
- c1->weight += c2->weight;
-}
+ /*memory full, so garbage collect*/
+ if(lists->nextfree >= lists->numfree)
+ {
+ /*mark only those that are in use*/
+ for(i = 0; i != lists->memsize; ++i) lists->memory[i].in_use = 0;
+ for(i = 0; i != lists->listsize; ++i)
+ {
+ BPMNode* node;
+ for(node = lists->chains0[i]; node != 0; node = node->tail) node->in_use = 1;
+ for(node = lists->chains1[i]; node != 0; node = node->tail) node->in_use = 1;
+ }
+ /*collect those that are free*/
+ lists->numfree = 0;
+ for(i = 0; i != lists->memsize; ++i)
+ {
+ if(!lists->memory[i].in_use) lists->freelist[lists->numfree++] = &lists->memory[i];
+ }
+ lists->nextfree = 0;
+ }
-static void init_coins(Coin* coins, size_t num)
-{
- size_t i;
- for(i = 0; i != num; ++i) coin_init(&coins[i]);
+ result = lists->freelist[lists->nextfree++];
+ result->weight = weight;
+ result->index = index;
+ result->tail = tail;
+ return result;
}
-static void cleanup_coins(Coin* coins, size_t num)
+static int bpmnode_compare(const void* a, const void* b)
{
- size_t i;
- for(i = 0; i != num; ++i) coin_cleanup(&coins[i]);
-}
-
-static int coin_compare(const void* a, const void* b) {
- float wa = ((const Coin*)a)->weight;
- float wb = ((const Coin*)b)->weight;
- return wa > wb ? 1 : wa < wb ? -1 : 0;
+ int wa = ((const BPMNode*)a)->weight;
+ int wb = ((const BPMNode*)b)->weight;
+ if(wa < wb) return -1;
+ if(wa > wb) return 1;
+ /*make the qsort a stable sort*/
+ return ((const BPMNode*)a)->index < ((const BPMNode*)b)->index ? 1 : -1;
}
-static unsigned append_symbol_coins(Coin* coins, const unsigned* frequencies, unsigned numcodes, size_t sum)
+/*Boundary Package Merge step, numpresent is the amount of leaves, and c is the current chain.*/
+static void boundaryPM(BPMLists* lists, BPMNode* leaves, size_t numpresent, int c, int num)
{
- unsigned i;
- unsigned j = 0; /*index of present symbols*/
- for(i = 0; i != numcodes; ++i)
+ unsigned lastindex = lists->chains1[c]->index;
+
+ if(c == 0)
+ {
+ if(lastindex >= numpresent) return;
+ lists->chains0[c] = lists->chains1[c];
+ lists->chains1[c] = bpmnode_create(lists, leaves[lastindex].weight, lastindex + 1, 0);
+ }
+ else
{
- if(frequencies[i] != 0) /*only include symbols that are present*/
+ /*sum of the weights of the head nodes of the previous lookahead chains.*/
+ int sum = lists->chains0[c - 1]->weight + lists->chains1[c - 1]->weight;
+ lists->chains0[c] = lists->chains1[c];
+ if(lastindex < numpresent && sum > leaves[lastindex].weight)
+ {
+ lists->chains1[c] = bpmnode_create(lists, leaves[lastindex].weight, lastindex + 1, lists->chains1[c]->tail);
+ return;
+ }
+ lists->chains1[c] = bpmnode_create(lists, sum, lastindex, lists->chains1[c - 1]);
+ /*in the end we are only interested in the chain of the last list, so no
+ need to recurse if we're at the last one (this gives measurable speedup)*/
+ if(num + 1 < (int)(2 * numpresent - 2))
{
- coins[j].weight = frequencies[i] / (float)sum;
- uivector_push_back(&coins[j].symbols, i);
- ++j;
+ boundaryPM(lists, leaves, numpresent, c - 1, num);
+ boundaryPM(lists, leaves, numpresent, c - 1, num);
}
}
- return 0;
}
unsigned lodepng_huffman_code_lengths(unsigned* lengths, const unsigned* frequencies,
size_t numcodes, unsigned maxbitlen)
{
- unsigned i, j;
- size_t sum = 0, numpresent = 0;
unsigned error = 0;
- Coin* coins; /*the coins of the currently calculated row*/
- Coin* prev_row; /*the previous row of coins*/
- size_t numcoins;
- size_t coinmem;
+ unsigned i;
+ size_t numpresent = 0; /*number of symbols with non-zero frequency*/
+ BPMNode* leaves; /*the symbols, only those with > 0 frequency*/
if(numcodes == 0) return 80; /*error: a tree of 0 symbols is not supposed to be made*/
+ if((1u << maxbitlen) < numcodes) return 80; /*error: represent all symbols*/
+
+ leaves = (BPMNode*)lodepng_malloc(numcodes * sizeof(*leaves));
+ if(!leaves) return 83; /*alloc fail*/
for(i = 0; i != numcodes; ++i)
{
if(frequencies[i] > 0)
{
+ leaves[numpresent].weight = frequencies[i];
+ leaves[numpresent].index = i;
++numpresent;
- sum += frequencies[i];
}
}
for(i = 0; i != numcodes; ++i) lengths[i] = 0;
/*ensure at least two present symbols. There should be at least one symbol
- according to RFC 1951 section 3.2.7. To decoders incorrectly require two. To
+ according to RFC 1951 section 3.2.7. Some decoders incorrectly require two. To
make these work as well ensure there are at least two symbols. The
Package-Merge code below also doesn't work correctly if there's only one
symbol, it'd give it the theoritical 0 bits but in practice zlib wants 1 bit*/
@@ -772,87 +798,55 @@ unsigned lodepng_huffman_code_lengths(unsigned* lengths, const unsigned* frequen
}
else if(numpresent == 1)
{
- for(i = 0; i != numcodes; ++i)
- {
- if(frequencies[i])
- {
- lengths[i] = 1;
- lengths[i == 0 ? 1 : 0] = 1;
- break;
- }
- }
+ lengths[leaves[0].index] = 1;
+ lengths[leaves[0].index == 0 ? 1 : 0] = 1;
}
else
{
- /*Package-Merge algorithm represented by coin collector's problem
- For every symbol, maxbitlen coins will be created*/
+ BPMLists lists;
+ BPMNode* node;
- coinmem = numpresent * 2; /*max amount of coins needed with the current algo*/
- coins = (Coin*)lodepng_malloc(sizeof(Coin) * coinmem);
- prev_row = (Coin*)lodepng_malloc(sizeof(Coin) * coinmem);
- if(!coins || !prev_row)
- {
- lodepng_free(coins);
- lodepng_free(prev_row);
- return 83; /*alloc fail*/
- }
- init_coins(coins, coinmem);
- init_coins(prev_row, coinmem);
+ qsort(leaves, numpresent, sizeof(BPMNode), bpmnode_compare);
+
+ lists.listsize = maxbitlen;
+ lists.memsize = 2 * maxbitlen * (maxbitlen + 1);
+ lists.nextfree = 0;
+ lists.numfree = lists.memsize;
+ lists.memory = (BPMNode*)lodepng_malloc(lists.memsize * sizeof(*lists.memory));
+ lists.freelist = (BPMNode**)lodepng_malloc(lists.memsize * sizeof(BPMNode*));
+ lists.chains0 = (BPMNode**)lodepng_malloc(lists.listsize * sizeof(BPMNode*));
+ lists.chains1 = (BPMNode**)lodepng_malloc(lists.listsize * sizeof(BPMNode*));
+ if(!lists.memory || !lists.freelist || !lists.chains0 || !lists.chains1) error = 83; /*alloc fail*/
- /*first row, lowest denominator*/
- error = append_symbol_coins(coins, frequencies, numcodes, sum);
- numcoins = numpresent;
- qsort(coins, numcoins, sizeof(Coin), coin_compare);
if(!error)
{
- unsigned numprev = 0;
- for(j = 1; j <= maxbitlen && !error; ++j) /*each of the remaining rows*/
- {
- unsigned tempnum;
- Coin* tempcoins;
- /*swap prev_row and coins, and their amounts*/
- tempcoins = prev_row; prev_row = coins; coins = tempcoins;
- tempnum = numprev; numprev = numcoins; numcoins = tempnum;
+ for(i = 0; i != lists.memsize; ++i) lists.freelist[i] = &lists.memory[i];
- cleanup_coins(coins, numcoins);
- init_coins(coins, numcoins);
+ bpmnode_create(&lists, leaves[0].weight, 1, 0);
+ bpmnode_create(&lists, leaves[1].weight, 2, 0);
- numcoins = 0;
-
- /*fill in the merged coins of the previous row*/
- for(i = 0; i + 1 < numprev; i += 2)
- {
- /*merge prev_row[i] and prev_row[i + 1] into new coin*/
- Coin* coin = &coins[numcoins++];
- coin_copy(coin, &prev_row[i]);
- add_coins(coin, &prev_row[i + 1]);
- }
- /*fill in all the original symbols again*/
- if(j < maxbitlen)
- {
- error = append_symbol_coins(coins + numcoins, frequencies, numcodes, sum);
- numcoins += numpresent;
- }
- qsort(coins, numcoins, sizeof(Coin), coin_compare);
+ for(i = 0; i != lists.listsize; ++i)
+ {
+ lists.chains0[i] = &lists.memory[0];
+ lists.chains1[i] = &lists.memory[1];
}
- }
- if(!error)
- {
- /*calculate the lengths of each symbol, as the amount of times a coin of each symbol is used*/
- for(i = 0; i + 1 < numpresent; ++i)
+ /*each boundaryPM call adds one chain to the last list, and we need 2 * numpresent - 2 chains.*/
+ for(i = 2; i != 2 * numpresent - 2; ++i) boundaryPM(&lists, leaves, numpresent, maxbitlen - 1, i);
+
+ for(node = lists.chains1[maxbitlen - 1]; node; node = node->tail)
{
- Coin* coin = &coins[i];
- for(j = 0; j < coin->symbols.size; ++j) ++lengths[coin->symbols.data[j]];
+ for(i = 0; i != node->index; ++i) ++lengths[leaves[i].index];
}
}
- cleanup_coins(coins, coinmem);
- lodepng_free(coins);
- cleanup_coins(prev_row, coinmem);
- lodepng_free(prev_row);
+ lodepng_free(lists.memory);
+ lodepng_free(lists.freelist);
+ lodepng_free(lists.chains0);
+ lodepng_free(lists.chains1);
}
+ lodepng_free(leaves);
return error;
}
@@ -1028,7 +1022,7 @@ static unsigned getTreeInflateDynamic(HuffmanTree* tree_ll, HuffmanTree* tree_d,
unsigned replength = 3; /*read in the 2 bits that indicate repeat length (3-6)*/
unsigned value; /*set value to the previous code*/
- if (i == 0) ERROR_BREAK(54); /*can't repeat previous if i is 0*/
+ if(i == 0) ERROR_BREAK(54); /*can't repeat previous if i is 0*/
if((*bp + 2) > inbitlength) ERROR_BREAK(50); /*error, bit pointer jumps past memory*/
replength += readBitsFromStream(bp, in, 2);
@@ -1404,7 +1398,7 @@ static void hash_cleanup(Hash* hash)
static unsigned getHash(const unsigned char* data, size_t size, size_t pos)
{
unsigned result = 0;
- if (pos + 2 < size)
+ if(pos + 2 < size)
{
/*A simple shift and xor hash is used. Since the data of PNGs is dominated
by zeroes due to the filters, a better hash does not have a significant
@@ -1428,7 +1422,7 @@ static unsigned countZeros(const unsigned char* data, size_t size, size_t pos)
const unsigned char* end = start + MAX_SUPPORTED_DEFLATE_LENGTH;
if(end > data + size) end = data + size;
data = start;
- while (data != end && *data == 0) ++data;
+ while(data != end && *data == 0) ++data;
/*subtracting two addresses returned as 32-bit number (max value is MAX_SUPPORTED_DEFLATE_LENGTH)*/
return (unsigned)(data - start);
}
@@ -1491,8 +1485,8 @@ static unsigned encodeLZ77(uivector* out, Hash* hash,
if(usezeros && hashval == 0)
{
- if (numzeros == 0) numzeros = countZeros(in, insize, pos);
- else if (pos + numzeros > insize || in[pos + numzeros - 1] != 0) --numzeros;
+ if(numzeros == 0) numzeros = countZeros(in, insize, pos);
+ else if(pos + numzeros > insize || in[pos + numzeros - 1] != 0) --numzeros;
}
else
{
@@ -1552,10 +1546,13 @@ static unsigned encodeLZ77(uivector* out, Hash* hash,
if(hashpos == hash->chain[hashpos]) break;
- if(numzeros >= 3 && length > numzeros) {
+ if(numzeros >= 3 && length > numzeros)
+ {
hashpos = hash->chainz[hashpos];
if(hash->zeros[hashpos] != numzeros) break;
- } else {
+ }
+ else
+ {
hashpos = hash->chain[hashpos];
/*outdated hash value, happens if particular value was not encountered in whole last window*/
if(hash->val[hashpos] != (int)hashval) break;
@@ -1613,8 +1610,8 @@ static unsigned encodeLZ77(uivector* out, Hash* hash,
hashval = getHash(in, insize, pos);
if(usezeros && hashval == 0)
{
- if (numzeros == 0) numzeros = countZeros(in, insize, pos);
- else if (pos + numzeros > insize || in[pos + numzeros - 1] != 0) --numzeros;
+ if(numzeros == 0) numzeros = countZeros(in, insize, pos);
+ else if(pos + numzeros > insize || in[pos + numzeros - 1] != 0) --numzeros;
}
else
{
@@ -2007,8 +2004,10 @@ static unsigned lodepng_deflatev(ucvector* out, const unsigned char* in, size_t
else if(settings->btype == 1) blocksize = insize;
else /*if(settings->btype == 2)*/
{
+ /*on PNGs, deflate blocks of 65-262k seem to give most dense encoding*/
blocksize = insize / 8 + 8;
- if(blocksize < 65535) blocksize = 65535;
+ if(blocksize < 65536) blocksize = 65536;
+ if(blocksize > 262144) blocksize = 262144;
}
numdeflateblocks = (insize + blocksize - 1) / blocksize;
@@ -2226,7 +2225,7 @@ static unsigned zlib_compress(unsigned char** out, size_t* outsize, const unsign
static unsigned zlib_decompress(unsigned char** out, size_t* outsize, const unsigned char* in,
size_t insize, const LodePNGDecompressSettings* settings)
{
- if (!settings->custom_zlib) return 87; /*no custom zlib function provided */
+ if(!settings->custom_zlib) return 87; /*no custom zlib function provided */
return settings->custom_zlib(out, outsize, in, insize, settings);
}
#endif /*LODEPNG_COMPILE_DECODER*/
@@ -2234,7 +2233,7 @@ static unsigned zlib_decompress(unsigned char** out, size_t* outsize, const unsi
static unsigned zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in,
size_t insize, const LodePNGCompressSettings* settings)
{
- if (!settings->custom_zlib) return 87; /*no custom zlib function provided */
+ if(!settings->custom_zlib) return 87; /*no custom zlib function provided */
return settings->custom_zlib(out, outsize, in, insize, settings);
}
#endif /*LODEPNG_COMPILE_ENCODER*/
@@ -3688,7 +3687,8 @@ unsigned lodepng_auto_choose_color(LodePNGColorMode* mode_out,
if(error) return error;
mode_out->key_defined = 0;
- if(prof.key && w * h <= 16) {
+ if(prof.key && w * h <= 16)
+ {
prof.alpha = 1; /*too few pixels to justify tRNS chunk overhead*/
if(prof.bits < 8) prof.bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/
}
diff --git a/lodepng.h b/lodepng.h
index 9246cb3..74e4495 100644
--- a/lodepng.h
+++ b/lodepng.h
@@ -1,5 +1,5 @@
/*
-LodePNG version 20150321
+LodePNG version 20150418
Copyright (c) 2005-2015 Lode Vandevenne
@@ -1562,6 +1562,7 @@ yyyymmdd.
Some changes aren't backwards compatible. Those are indicated with a (!)
symbol.
+*) 18 apr 2015: Boundary PM instead of just package-merge for faster encoding.
*) 23 aug 2014: Reduced needless memory usage of decoder.
*) 28 jun 2014: Removed fix_png setting, always support palette OOB for
simplicity. Made ColorProfile public.
diff --git a/lodepng_benchmark.cpp b/lodepng_benchmark.cpp
index e4c0525..791ced0 100644
--- a/lodepng_benchmark.cpp
+++ b/lodepng_benchmark.cpp
@@ -24,7 +24,7 @@ freely, subject to the following restrictions:
*/
//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
+//g++ lodepng.cpp lodepng_benchmark.cpp -Wall -Wextra -pedantic -ansi -lSDL -O3 && ./a.out
#include "lodepng.h"
@@ -44,6 +44,9 @@ freely, subject to the following restrictions:
double total_dec_time = 0;
double total_enc_time = 0;
size_t total_enc_size = 0;
+size_t total_in_size = 0; // This is the uncompressed data in the raw color format
+
+bool verbose = false;
////////////////////////////////////////////////////////////////////////////////
@@ -119,7 +122,7 @@ void doCodecTest(Image& image)
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);
@@ -136,21 +139,27 @@ void doCodecTest(Image& image)
}
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);
+ LodePNGColorMode colormode;
+ colormode.colortype = image.colorType;
+ colormode.bitdepth = image.bitDepth;
+ total_in_size += lodepng_get_raw_size(image.width, image.height, &colormode);
- std::cout << std::endl;
+ if(verbose)
+ {
+ 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;
+ if(NUM_DECODE> 0) printValue("decoding time", t_dec1 - t_dec0, "/", NUM_DECODE, " s");
+ std::cout << std::endl;
+ }
//LodePNG_saveFile(encoded, encoded_size, "test.png");
@@ -162,14 +171,14 @@ static const int IMGSIZE = 4096;
void testPatternSine()
{
- std::cout << "sine pattern" << std::endl;
+ if(verbose) 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;
@@ -193,14 +202,14 @@ void testPatternSine()
void testPatternSineNoAlpha()
{
- std::cout << "sine pattern w/o alpha" << std::endl;
+ if(verbose) 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;
@@ -223,7 +232,7 @@ void testPatternSineNoAlpha()
void testPatternXor()
{
- std::cout << "xor pattern" << std::endl;
+ if(verbose) std::cout << "xor pattern" << std::endl;
Image image;
int w = IMGSIZE;
@@ -257,7 +266,7 @@ unsigned int getRandomUint()
void testPatternPseudoRan()
{
- std::cout << "pseudorandom pattern" << std::endl;
+ if(verbose) std::cout << "pseudorandom pattern" << std::endl;
Image image;
int w = IMGSIZE / 2;
@@ -281,8 +290,8 @@ void testPatternPseudoRan()
void testPatternSineXor()
{
- std::cout << "sine+xor pattern" << std::endl;
-
+ if(verbose) std::cout << "sine+xor pattern" << std::endl;
+
Image image;
int w = IMGSIZE / 2;
int h = IMGSIZE / 2;
@@ -310,8 +319,8 @@ void testPatternSineXor()
void testPatternGreyMandel()
{
- std::cout << "grey mandelbrot pattern" << std::endl;
-
+ if(verbose) std::cout << "grey mandelbrot pattern" << std::endl;
+
Image image;
int w = IMGSIZE / 2;
int h = IMGSIZE / 2;
@@ -326,7 +335,7 @@ void testPatternGreyMandel()
// 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++)
{
@@ -350,8 +359,8 @@ void testPatternGreyMandel()
void testPatternGreyMandelSmall()
{
- std::cout << "grey mandelbrot pattern" << std::endl;
-
+ if(verbose) std::cout << "grey mandelbrot pattern" << std::endl;
+
Image image;
int w = IMGSIZE / 8;
int h = IMGSIZE / 8;
@@ -366,7 +375,7 @@ void testPatternGreyMandelSmall()
// 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++)
{
@@ -390,7 +399,7 @@ void testPatternGreyMandelSmall()
void testPatternX()
{
- std::cout << "x pattern" << std::endl;
+ if(verbose) std::cout << "x pattern" << std::endl;
Image image;
int w = IMGSIZE;
@@ -411,7 +420,7 @@ void testPatternX()
void testPatternY()
{
- std::cout << "y pattern" << std::endl;
+ if(verbose) std::cout << "y pattern" << std::endl;
Image image;
int w = IMGSIZE;
@@ -432,8 +441,8 @@ void testPatternY()
void testPatternDisk(const std::string& filename)
{
- std::cout << "file " << filename << std::endl;
-
+ if(verbose) std::cout << "file " << filename << std::endl;
+
Image image;
image.colorType = LCT_RGB;
image.bitDepth = 8;
@@ -442,37 +451,58 @@ void testPatternDisk(const std::string& filename)
doCodecTest(image);
}
-
-int main()
+int main(int argc, char *argv[])
{
+ verbose = false;
+
+ std::vector<std::string> files;
+
+ for(int i = 1; i < argc; i++)
+ {
+ std::string arg = argv[i];
+ if(arg == "-v") verbose = true;
+ else files.push_back(arg);
+ }
+
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();*/
+ if(files.empty())
+ {
+ //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();*/
+ }
+ else
+ {
+ for(size_t i = 0; i < files.size(); i++)
+ {
+ testPatternDisk(files[i]);
+ }
+ }
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 << "Total input size : " << total_in_size << std::endl;
+ std::cout << "Total encoded size: " << total_enc_size << std::endl;
- std::cout << "benchmark done" << std::endl;
+ if(verbose) std::cout << "benchmark done" << std::endl;
}
diff --git a/lodepng_unittest.cpp b/lodepng_unittest.cpp
index 93801a2..92fc370 100644
--- a/lodepng_unittest.cpp
+++ b/lodepng_unittest.cpp
@@ -1,7 +1,7 @@
/*
LodePNG Unit Test
-Copyright (c) 2005-2014 Lode Vandevenne
+Copyright (c) 2005-2015 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
@@ -39,6 +39,7 @@ mv lodepng.cpp lodepng.c ; gcc -I ./ lodepng.c examples/example_decode.c -pedant
*) try lodepng_benchmark.cpp
g++ lodepng.cpp lodepng_benchmark.cpp -Wall -Wextra -pedantic -ansi -lSDL -O3 && ./a.out
+g++ lodepng.cpp lodepng_benchmark.cpp -Wall -Wextra -pedantic -ansi -lSDL -O3 && ./a.out corpus/''*
*) Check if all examples compile without warnings:
g++ -I ./ lodepng.cpp examples/''*.cpp -W -Wall -ansi -pedantic -O3 -c
@@ -77,8 +78,9 @@ 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
+cat lodepng.cpp | grep "#include <"
-*) check version dates in copyright message and "#define LODEPNG_VERSION_STRING"
+*) check version dates in copyright message and LODEPNG_VERSION_STRING
*) check year in copyright message at top of all files as well as at bottom of lodepng.h
@@ -1630,7 +1632,7 @@ void testPredefinedFilters() {
assertNoError(error);
ASSERT_EQUALS(outfilters.size(), h);
- for (size_t i = 0; i < h; i++) ASSERT_EQUALS(3, outfilters[i]);
+ for(size_t i = 0; i < h; i++) ASSERT_EQUALS(3, outfilters[i]);
}
void testWrongWindowSizeGivesError() {