1337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// Copyright 2013 Google Inc. All Rights Reserved. 2337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// 3337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// Licensed under the Apache License, Version 2.0 (the "License"); 4337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// you may not use this file except in compliance with the License. 5337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// You may obtain a copy of the License at 6337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// 7337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// http://www.apache.org/licenses/LICENSE-2.0 8337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// 9337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// Unless required by applicable law or agreed to in writing, software 10337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// distributed under the License is distributed on an "AS IS" BASIS, 11337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// See the License for the specific language governing permissions and 13337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// limitations under the License. 14337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// 15337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// Author: lode.vandevenne@gmail.com (Lode Vandevenne) 16337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala) 17337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 18337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// See zopflipng_lib.h 19337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 20337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne#include "zopflipng_lib.h" 21337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 22337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne#include <stdio.h> 2308b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne#include <set> 24337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne#include <vector> 25337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 26337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne#include "lodepng/lodepng.h" 27337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne#include "lodepng/lodepng_util.h" 28337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne#include "../zopfli/deflate.h" 29337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 30337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode VandevenneZopfliPNGOptions::ZopfliPNGOptions() 31337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne : lossy_transparent(false) 32337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne , lossy_8bit(false) 33337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne , auto_filter_strategy(true) 34337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne , use_zopfli(true) 35337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne , num_iterations(15) 36337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne , num_iterations_large(5) 37337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne , block_split_strategy(1) { 38337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne} 39337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 40337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// Deflate compressor passed as fuction pointer to LodePNG to have it use Zopfli 41337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// as its compression backend. 42337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenneunsigned CustomPNGDeflate(unsigned char** out, size_t* outsize, 43337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne const unsigned char* in, size_t insize, 44337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne const LodePNGCompressSettings* settings) { 45337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne const ZopfliPNGOptions* png_options = 46337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne static_cast<const ZopfliPNGOptions*>(settings->custom_context); 47337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne unsigned char bp = 0; 48337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne ZopfliOptions options; 49337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne ZopfliInitOptions(&options); 50337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 51337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne options.numiterations = insize < 200000 52337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne ? png_options->num_iterations : png_options->num_iterations_large; 53337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 54337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne if (png_options->block_split_strategy == 3) { 55337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne // Try both block splitting first and last. 56337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne unsigned char* out2 = 0; 57337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne size_t outsize2 = 0; 58337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne options.blocksplittinglast = 0; 59337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne ZopfliDeflate(&options, 2 /* Dynamic */, 1, in, insize, &bp, out, outsize); 60337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne bp = 0; 61337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne options.blocksplittinglast = 1; 62337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne ZopfliDeflate(&options, 2 /* Dynamic */, 1, 63337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne in, insize, &bp, &out2, &outsize2); 64337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 65337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne if (outsize2 < *outsize) { 66337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne free(*out); 67337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne *out = out2; 68337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne *outsize = outsize2; 69337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne printf("Block splitting last was better\n"); 70337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } else { 71337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne free(out2); 72337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 73337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } else { 74337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne if (png_options->block_split_strategy == 0) options.blocksplitting = 0; 75337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne options.blocksplittinglast = png_options->block_split_strategy == 2; 76337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne ZopfliDeflate(&options, 2 /* Dynamic */, 1, in, insize, &bp, out, outsize); 77337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 78337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 79337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne return 0; // OK 80337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne} 81337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 8208b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne// Returns 32-bit integer value for RGBA color. 8308b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevennestatic unsigned ColorIndex(const unsigned char* color) { 8408b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne return color[0] + 256u * color[1] + 65536u * color[1] + 16777216u * color[3]; 8508b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne} 8608b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne 8708b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne// Counts amount of colors in the image, up to 257. If transparent_counts_as_one 8808b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne// is enabled, any color with alpha channel 0 is treated as a single color with 8908b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne// index 0. 9008b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevennevoid CountColors(std::set<unsigned>* unique, 9108b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne const unsigned char* image, unsigned w, unsigned h, 9208b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne bool transparent_counts_as_one) { 9308b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne unique->clear(); 9408b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne for (size_t i = 0; i < w * h; i++) { 9508b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne unsigned index = ColorIndex(&image[i * 4]); 9608b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne if (transparent_counts_as_one && image[i * 4 + 3] == 0) index = 0; 9708b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne unique->insert(index); 9808b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne if (unique->size() > 256) break; 9908b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne } 10008b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne} 10108b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne 102337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// Remove RGB information from pixels with alpha=0 10308b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevennevoid LossyOptimizeTransparent(lodepng::State* inputstate, unsigned char* image, 10408b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne unsigned w, unsigned h) { 105337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne // First check if we want to preserve potential color-key background color, 106337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne // or instead use the last encountered RGB value all the time to save bytes. 107337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne bool key = true; 108337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne for (size_t i = 0; i < w * h; i++) { 109337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne if (image[i * 4 + 3] > 0 && image[i * 4 + 3] < 255) { 110337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne key = false; 111337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne break; 112337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 113337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 11408b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne std::set<unsigned> count; // Color count, up to 257. 11508b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne CountColors(&count, image, w, h, true); 11608b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne // If true, means palette is possible so avoid using different RGB values for 11708b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne // the transparent color. 11808b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne bool palette = count.size() <= 256; 119337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 12008b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne // Choose the color key or first initial background color. 121337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne int r = 0, g = 0, b = 0; 12208b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne if (key || palette) { 123337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne for (size_t i = 0; i < w * h; i++) { 124337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne if (image[i * 4 + 3] == 0) { 12508b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne // Use RGB value of first encountered transparent pixel. This can be 12608b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne // used as a valid color key, or in case of palette ensures a color 12708b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne // existing in the input image palette is used. 128337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne r = image[i * 4 + 0]; 129337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne g = image[i * 4 + 1]; 130337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne b = image[i * 4 + 2]; 131337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 132337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 133337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 134337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 135337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne for (size_t i = 0; i < w * h; i++) { 13608b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne // if alpha is 0, alter the RGB value to a possibly more efficient one. 137337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne if (image[i * 4 + 3] == 0) { 138337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne image[i * 4 + 0] = r; 139337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne image[i * 4 + 1] = g; 140337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne image[i * 4 + 2] = b; 141337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } else { 14208b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne if (!key && !palette) { 14308b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne // Use the last encountered RGB value if no key or palette is used: that 14408b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne // way more values can be 0 thanks to the PNG filter types. 145337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne r = image[i * 4 + 0]; 146337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne g = image[i * 4 + 1]; 147337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne b = image[i * 4 + 2]; 148337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 149337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 150337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 15108b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne 15208b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne // If there are now less colors, update palette of input image to match this. 15308b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne if (palette && inputstate->info_png.color.palettesize > 0) { 15408b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne CountColors(&count, image, w, h, false); 15508b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne if (count.size() < inputstate->info_png.color.palettesize) { 15608b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne std::vector<unsigned char> palette_out; 15708b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne unsigned char* palette_in = inputstate->info_png.color.palette; 15808b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne for (size_t i = 0; i < inputstate->info_png.color.palettesize; i++) { 15908b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne if (count.count(ColorIndex(&palette_in[i * 4])) != 0) { 16008b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne palette_out.push_back(palette_in[i * 4 + 0]); 16108b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne palette_out.push_back(palette_in[i * 4 + 1]); 16208b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne palette_out.push_back(palette_in[i * 4 + 2]); 16308b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne palette_out.push_back(palette_in[i * 4 + 3]); 16408b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne } 16508b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne } 16608b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne inputstate->info_png.color.palettesize = palette_out.size() / 4; 16708b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne for (size_t i = 0; i < palette_out.size(); i++) { 16808b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne palette_in[i] = palette_out[i]; 16908b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne } 17008b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne } 17108b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne } 172337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne} 173337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 174337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// Tries to optimize given a single PNG filter strategy. 175337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// Returns 0 if ok, other value for error 176337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenneunsigned TryOptimize( 177337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne const std::vector<unsigned char>& image, unsigned w, unsigned h, 178337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne const lodepng::State& inputstate, bool bit16, 179337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne const std::vector<unsigned char>& origfile, 180337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne ZopfliPNGFilterStrategy filterstrategy, 181337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne bool use_zopfli, int windowsize, const ZopfliPNGOptions* png_options, 182337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne std::vector<unsigned char>* out) { 183337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne unsigned error = 0; 184337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 185337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne lodepng::State state; 186337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne state.encoder.zlibsettings.windowsize = windowsize; 187337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne if (use_zopfli && png_options->use_zopfli) { 188337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne state.encoder.zlibsettings.custom_deflate = CustomPNGDeflate; 1899ec1023fe7b16c10be9a96b1cd90cf298f7f3d90Lode Vandevenne state.encoder.zlibsettings.custom_context = png_options; 190337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 191337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 192337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne if (inputstate.info_png.color.colortype == LCT_PALETTE) { 193337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne // Make it preserve the original palette order 194337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne lodepng_color_mode_copy(&state.info_raw, &inputstate.info_png.color); 195337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne state.info_raw.colortype = LCT_RGBA; 196337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne state.info_raw.bitdepth = 8; 197337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 198337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne if (bit16) { 199337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne state.info_raw.bitdepth = 16; 200337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 201337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 202337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne state.encoder.filter_palette_zero = 0; 203337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 204337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne std::vector<unsigned char> filters; 205337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne switch (filterstrategy) { 206337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne case kStrategyZero: 207337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne state.encoder.filter_strategy = LFS_ZERO; 208337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne break; 209337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne case kStrategyMinSum: 210337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne state.encoder.filter_strategy = LFS_MINSUM; 211337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne break; 212337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne case kStrategyEntropy: 213337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne state.encoder.filter_strategy = LFS_ENTROPY; 214337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne break; 215337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne case kStrategyBruteForce: 216337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne state.encoder.filter_strategy = LFS_BRUTE_FORCE; 217337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne break; 218337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne case kStrategyOne: 219337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne case kStrategyTwo: 220337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne case kStrategyThree: 221337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne case kStrategyFour: 222337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne // Set the filters of all scanlines to that number. 223337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne filters.resize(h, filterstrategy); 224337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne state.encoder.filter_strategy = LFS_PREDEFINED; 225337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne state.encoder.predefined_filters = &filters[0]; 226337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne break; 227337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne case kStrategyPredefined: 228337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne lodepng::getFilterTypes(filters, origfile); 229337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne state.encoder.filter_strategy = LFS_PREDEFINED; 230337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne state.encoder.predefined_filters = &filters[0]; 231337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne break; 232337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne default: 233337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne break; 234337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 235337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 236337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne state.encoder.add_id = false; 237337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne state.encoder.text_compression = 1; 238337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 239337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne error = lodepng::encode(*out, image, w, h, state); 240337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 241337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne // For very small output, also try without palette, it may be smaller thanks 242337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne // to no palette storage overhead. 243337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne if (!error && out->size() < 4096) { 244337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne lodepng::State teststate; 245337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne std::vector<unsigned char> temp; 246337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne lodepng::decode(temp, w, h, teststate, *out); 247337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne LodePNGColorMode& color = teststate.info_png.color; 248337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne if (color.colortype == LCT_PALETTE) { 249337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne std::vector<unsigned char> out2; 250337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne state.encoder.auto_convert = LAC_ALPHA; 251337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne bool grey = true; 252337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne for (size_t i = 0; i < color.palettesize; i++) { 253337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne if (color.palette[i * 4 + 0] != color.palette[i * 4 + 2] 254337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne || color.palette[i * 4 + 1] != color.palette[i * 4 + 2]) { 255337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne grey = false; 256337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne break; 257337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 258337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 259337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne if (grey) state.info_png.color.colortype = LCT_GREY_ALPHA; 260337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 261337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne error = lodepng::encode(out2, image, w, h, state); 262337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne if (out2.size() < out->size()) out->swap(out2); 263337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 264337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 265337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 266337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne if (error) { 2679ec1023fe7b16c10be9a96b1cd90cf298f7f3d90Lode Vandevenne printf("Encoding error %u: %s\n", error, lodepng_error_text(error)); 268337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne return error; 269337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 270337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 271337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne return 0; 272337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne} 273337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 274337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// Use fast compression to check which PNG filter strategy gives the smallest 275337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// output. This allows to then do the slow and good compression only on that 276337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// filter type. 277337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenneunsigned AutoChooseFilterStrategy(const std::vector<unsigned char>& image, 278337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne unsigned w, unsigned h, 279337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne const lodepng::State& inputstate, bool bit16, 280337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne const std::vector<unsigned char>& origfile, 281337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne int numstrategies, 282337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne ZopfliPNGFilterStrategy* strategies, 283337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne bool* enable) { 284337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne std::vector<unsigned char> out; 285337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne size_t bestsize = 0; 286337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne int bestfilter = 0; 287337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 288337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne // A large window size should still be used to do the quick compression to 289337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne // try out filter strategies: which filter strategy is the best depends 290337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne // largely on the window size, the closer to the actual used window size the 291337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne // better. 292337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne int windowsize = 8192; 293337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 294337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne for (int i = 0; i < numstrategies; i++) { 295337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne out.clear(); 296337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne unsigned error = TryOptimize(image, w, h, inputstate, bit16, origfile, 297337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne strategies[i], false, windowsize, 0, &out); 298337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne if (error) return error; 299337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne if (bestsize == 0 || out.size() < bestsize) { 300337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne bestsize = out.size(); 301337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne bestfilter = i; 302337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 303337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 304337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 305337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne for (int i = 0; i < numstrategies; i++) { 306337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne enable[i] = (i == bestfilter); 307337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 308337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 309337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne return 0; /* OK */ 310337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne} 311337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 312337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// Keeps chunks with given names from the original png by literally copying them 313337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne// into the new png 314337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevennevoid KeepChunks(const std::vector<unsigned char>& origpng, 315337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne const std::vector<std::string>& keepnames, 316337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne std::vector<unsigned char>* png) { 317337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne std::vector<std::string> names[3]; 318337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne std::vector<std::vector<unsigned char> > chunks[3]; 319337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 320337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne lodepng::getChunks(names, chunks, origpng); 321337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne std::vector<std::vector<unsigned char> > keepchunks[3]; 322337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 323337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne // There are 3 distinct locations in a PNG file for chunks: between IHDR and 324337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne // PLTE, between PLTE and IDAT, and between IDAT and IEND. Keep each chunk at 325337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne // its corresponding location in the new PNG. 326337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne for (size_t i = 0; i < 3; i++) { 327337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne for (size_t j = 0; j < names[i].size(); j++) { 328337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne for (size_t k = 0; k < keepnames.size(); k++) { 329337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne if (keepnames[k] == names[i][j]) { 330337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne keepchunks[i].push_back(chunks[i][j]); 331337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 332337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 333337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 334337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 335337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 336337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne lodepng::insertChunks(*png, keepchunks); 337337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne} 338337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 339337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenneint ZopfliPNGOptimize(const std::vector<unsigned char>& origpng, 340337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne const ZopfliPNGOptions& png_options, 341337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne bool verbose, 342337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne std::vector<unsigned char>* resultpng) { 343337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne // Use the largest possible deflate window size 344337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne int windowsize = 32768; 345337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 346337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne ZopfliPNGFilterStrategy filterstrategies[kNumFilterStrategies] = { 347337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne kStrategyZero, kStrategyOne, kStrategyTwo, kStrategyThree, kStrategyFour, 348337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne kStrategyMinSum, kStrategyEntropy, kStrategyPredefined, kStrategyBruteForce 349337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne }; 350337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne bool strategy_enable[kNumFilterStrategies] = { 351337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne false, false, false, false, false, false, false, false, false 352337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne }; 353337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne std::string strategy_name[kNumFilterStrategies] = { 354337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne "zero", "one", "two", "three", "four", 355337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne "minimum sum", "entropy", "predefined", "brute force" 356337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne }; 357337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne for (size_t i = 0; i < png_options.filter_strategies.size(); i++) { 358337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne strategy_enable[png_options.filter_strategies[i]] = true; 359337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 360337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 361337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne std::vector<unsigned char> image; 362337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne unsigned w, h; 363337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne unsigned error; 364337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne lodepng::State inputstate; 365337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne error = lodepng::decode(image, w, h, inputstate, origpng); 366337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 367337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne if (error) { 368337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne if (verbose) { 369337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne printf("Decoding error %i: %s\n", error, lodepng_error_text(error)); 370337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 371337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne return error; 372337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 373337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 374337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne bool bit16 = false; // Using 16-bit per channel raw image 375337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne if (inputstate.info_png.color.bitdepth == 16 && !png_options.lossy_8bit) { 376337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne // Decode as 16-bit 377337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne image.clear(); 378337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne error = lodepng::decode(image, w, h, origpng, LCT_RGBA, 16); 379337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne bit16 = true; 380337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 381337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 382337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne if (!error) { 383337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne // If lossy_transparent, remove RGB information from pixels with alpha=0 384337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne if (png_options.lossy_transparent && !bit16) { 38508b97947ac58e1c824ce401d9cc08721bedd1808Lode Vandevenne LossyOptimizeTransparent(&inputstate, &image[0], w, h); 386337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 387337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 388337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne if (png_options.auto_filter_strategy) { 389337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne error = AutoChooseFilterStrategy(image, w, h, inputstate, bit16, 390337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne origpng, 391337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne /* Don't try brute force */ 392337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne kNumFilterStrategies - 1, 393337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne filterstrategies, strategy_enable); 394337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 395337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 396337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 397337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne if (!error) { 398337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne size_t bestsize = 0; 399337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 400337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne for (int i = 0; i < kNumFilterStrategies; i++) { 401337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne if (!strategy_enable[i]) continue; 402337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 403337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne std::vector<unsigned char> temp; 404337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne error = TryOptimize(image, w, h, inputstate, bit16, origpng, 405337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne filterstrategies[i], true /* use_zopfli */, 406337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne windowsize, &png_options, &temp); 407337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne if (!error) { 408337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne if (verbose) { 409337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne printf("Filter strategy %s: %d bytes\n", 410337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne strategy_name[i].c_str(), (int) temp.size()); 411337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 412337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne if (bestsize == 0 || temp.size() < bestsize) { 413337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne bestsize = temp.size(); 414337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne (*resultpng).swap(temp); // Store best result so far in the output. 415337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 416337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 417337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 418337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 419337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne if (!png_options.keepchunks.empty()) { 420337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne KeepChunks(origpng, png_options.keepchunks, resultpng); 421337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 422337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne } 423337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne 424337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne return error; 425337d27f25ef15a6cf34fef2acd0613fddc411cb1Lode Vandevenne} 426