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