xref: /aosp_15_r20/external/zopfli/src/zopflipng/zopflipng_lib.cc (revision e47783fd9ac7e78d0523d35be12ee382df490d63)
1*e47783fdSXin Li // Copyright 2013 Google Inc. All Rights Reserved.
2*e47783fdSXin Li //
3*e47783fdSXin Li // Licensed under the Apache License, Version 2.0 (the "License");
4*e47783fdSXin Li // you may not use this file except in compliance with the License.
5*e47783fdSXin Li // You may obtain a copy of the License at
6*e47783fdSXin Li //
7*e47783fdSXin Li //    http://www.apache.org/licenses/LICENSE-2.0
8*e47783fdSXin Li //
9*e47783fdSXin Li // Unless required by applicable law or agreed to in writing, software
10*e47783fdSXin Li // distributed under the License is distributed on an "AS IS" BASIS,
11*e47783fdSXin Li // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*e47783fdSXin Li // See the License for the specific language governing permissions and
13*e47783fdSXin Li // limitations under the License.
14*e47783fdSXin Li //
15*e47783fdSXin Li // Author: [email protected] (Lode Vandevenne)
16*e47783fdSXin Li // Author: [email protected] (Jyrki Alakuijala)
17*e47783fdSXin Li 
18*e47783fdSXin Li // See zopflipng_lib.h
19*e47783fdSXin Li 
20*e47783fdSXin Li #include "zopflipng_lib.h"
21*e47783fdSXin Li 
22*e47783fdSXin Li #include <errno.h>
23*e47783fdSXin Li #include <stdio.h>
24*e47783fdSXin Li #include <stdlib.h>
25*e47783fdSXin Li #include <string.h>
26*e47783fdSXin Li #include <set>
27*e47783fdSXin Li #include <vector>
28*e47783fdSXin Li 
29*e47783fdSXin Li #include "lodepng/lodepng.h"
30*e47783fdSXin Li #include "lodepng/lodepng_util.h"
31*e47783fdSXin Li #include "../zopfli/deflate.h"
32*e47783fdSXin Li 
ZopfliPNGOptions()33*e47783fdSXin Li ZopfliPNGOptions::ZopfliPNGOptions()
34*e47783fdSXin Li   : verbose(false)
35*e47783fdSXin Li   , lossy_transparent(false)
36*e47783fdSXin Li   , lossy_8bit(false)
37*e47783fdSXin Li   , auto_filter_strategy(true)
38*e47783fdSXin Li   , keep_colortype(false)
39*e47783fdSXin Li   , use_zopfli(true)
40*e47783fdSXin Li   , num_iterations(15)
41*e47783fdSXin Li   , num_iterations_large(5)
42*e47783fdSXin Li   , block_split_strategy(1) {
43*e47783fdSXin Li }
44*e47783fdSXin Li 
45*e47783fdSXin Li // Deflate compressor passed as fuction pointer to LodePNG to have it use Zopfli
46*e47783fdSXin Li // as its compression backend.
CustomPNGDeflate(unsigned char ** out,size_t * outsize,const unsigned char * in,size_t insize,const LodePNGCompressSettings * settings)47*e47783fdSXin Li unsigned CustomPNGDeflate(unsigned char** out, size_t* outsize,
48*e47783fdSXin Li                           const unsigned char* in, size_t insize,
49*e47783fdSXin Li                           const LodePNGCompressSettings* settings) {
50*e47783fdSXin Li   const ZopfliPNGOptions* png_options =
51*e47783fdSXin Li       static_cast<const ZopfliPNGOptions*>(settings->custom_context);
52*e47783fdSXin Li   unsigned char bp = 0;
53*e47783fdSXin Li   ZopfliOptions options;
54*e47783fdSXin Li   ZopfliInitOptions(&options);
55*e47783fdSXin Li 
56*e47783fdSXin Li   options.verbose = png_options->verbose;
57*e47783fdSXin Li   options.numiterations = insize < 200000
58*e47783fdSXin Li       ? png_options->num_iterations : png_options->num_iterations_large;
59*e47783fdSXin Li 
60*e47783fdSXin Li   ZopfliDeflate(&options, 2 /* Dynamic */, 1, in, insize, &bp, out, outsize);
61*e47783fdSXin Li 
62*e47783fdSXin Li   return 0;  // OK
63*e47783fdSXin Li }
64*e47783fdSXin Li 
65*e47783fdSXin Li // Returns 32-bit integer value for RGBA color.
ColorIndex(const unsigned char * color)66*e47783fdSXin Li static unsigned ColorIndex(const unsigned char* color) {
67*e47783fdSXin Li   return color[0] + 256u * color[1] + 65536u * color[2] + 16777216u * color[3];
68*e47783fdSXin Li }
69*e47783fdSXin Li 
70*e47783fdSXin Li // Counts amount of colors in the image, up to 257. If transparent_counts_as_one
71*e47783fdSXin Li // is enabled, any color with alpha channel 0 is treated as a single color with
72*e47783fdSXin Li // index 0.
CountColors(std::set<unsigned> * unique,const unsigned char * image,unsigned w,unsigned h,bool transparent_counts_as_one)73*e47783fdSXin Li void CountColors(std::set<unsigned>* unique,
74*e47783fdSXin Li                  const unsigned char* image, unsigned w, unsigned h,
75*e47783fdSXin Li                  bool transparent_counts_as_one) {
76*e47783fdSXin Li   unique->clear();
77*e47783fdSXin Li   for (size_t i = 0; i < w * h; i++) {
78*e47783fdSXin Li     unsigned index = ColorIndex(&image[i * 4]);
79*e47783fdSXin Li     if (transparent_counts_as_one && image[i * 4 + 3] == 0) index = 0;
80*e47783fdSXin Li     unique->insert(index);
81*e47783fdSXin Li     if (unique->size() > 256) break;
82*e47783fdSXin Li   }
83*e47783fdSXin Li }
84*e47783fdSXin Li 
85*e47783fdSXin Li // Remove RGB information from pixels with alpha=0
LossyOptimizeTransparent(lodepng::State * inputstate,unsigned char * image,unsigned w,unsigned h)86*e47783fdSXin Li void LossyOptimizeTransparent(lodepng::State* inputstate, unsigned char* image,
87*e47783fdSXin Li     unsigned w, unsigned h) {
88*e47783fdSXin Li   // First check if we want to preserve potential color-key background color,
89*e47783fdSXin Li   // or instead use the last encountered RGB value all the time to save bytes.
90*e47783fdSXin Li   bool key = true;
91*e47783fdSXin Li   for (size_t i = 0; i < w * h; i++) {
92*e47783fdSXin Li     if (image[i * 4 + 3] > 0 && image[i * 4 + 3] < 255) {
93*e47783fdSXin Li       key = false;
94*e47783fdSXin Li       break;
95*e47783fdSXin Li     }
96*e47783fdSXin Li   }
97*e47783fdSXin Li   std::set<unsigned> count;  // Color count, up to 257.
98*e47783fdSXin Li   CountColors(&count, image, w, h, true);
99*e47783fdSXin Li   // If true, means palette is possible so avoid using different RGB values for
100*e47783fdSXin Li   // the transparent color.
101*e47783fdSXin Li   bool palette = count.size() <= 256;
102*e47783fdSXin Li 
103*e47783fdSXin Li   // Choose the color key or first initial background color.
104*e47783fdSXin Li   int r = 0, g = 0, b = 0;
105*e47783fdSXin Li   if (key || palette) {
106*e47783fdSXin Li     for (size_t i = 0; i < w * h; i++) {
107*e47783fdSXin Li       if (image[i * 4 + 3] == 0) {
108*e47783fdSXin Li         // Use RGB value of first encountered transparent pixel. This can be
109*e47783fdSXin Li         // used as a valid color key, or in case of palette ensures a color
110*e47783fdSXin Li         // existing in the input image palette is used.
111*e47783fdSXin Li         r = image[i * 4 + 0];
112*e47783fdSXin Li         g = image[i * 4 + 1];
113*e47783fdSXin Li         b = image[i * 4 + 2];
114*e47783fdSXin Li         break;
115*e47783fdSXin Li       }
116*e47783fdSXin Li     }
117*e47783fdSXin Li   }
118*e47783fdSXin Li 
119*e47783fdSXin Li   for (size_t i = 0; i < w * h; i++) {
120*e47783fdSXin Li     // if alpha is 0, alter the RGB value to a possibly more efficient one.
121*e47783fdSXin Li     if (image[i * 4 + 3] == 0) {
122*e47783fdSXin Li       image[i * 4 + 0] = r;
123*e47783fdSXin Li       image[i * 4 + 1] = g;
124*e47783fdSXin Li       image[i * 4 + 2] = b;
125*e47783fdSXin Li     } else {
126*e47783fdSXin Li       if (!key && !palette) {
127*e47783fdSXin Li         // Use the last encountered RGB value if no key or palette is used: that
128*e47783fdSXin Li         // way more values can be 0 thanks to the PNG filter types.
129*e47783fdSXin Li         r = image[i * 4 + 0];
130*e47783fdSXin Li         g = image[i * 4 + 1];
131*e47783fdSXin Li         b = image[i * 4 + 2];
132*e47783fdSXin Li       }
133*e47783fdSXin Li     }
134*e47783fdSXin Li   }
135*e47783fdSXin Li 
136*e47783fdSXin Li   // If there are now less colors, update palette of input image to match this.
137*e47783fdSXin Li   if (palette && inputstate->info_png.color.palettesize > 0) {
138*e47783fdSXin Li     CountColors(&count, image, w, h, false);
139*e47783fdSXin Li     if (count.size() < inputstate->info_png.color.palettesize) {
140*e47783fdSXin Li       std::vector<unsigned char> palette_out;
141*e47783fdSXin Li       unsigned char* palette_in = inputstate->info_png.color.palette;
142*e47783fdSXin Li       for (size_t i = 0; i < inputstate->info_png.color.palettesize; i++) {
143*e47783fdSXin Li         if (count.count(ColorIndex(&palette_in[i * 4])) != 0) {
144*e47783fdSXin Li           palette_out.push_back(palette_in[i * 4 + 0]);
145*e47783fdSXin Li           palette_out.push_back(palette_in[i * 4 + 1]);
146*e47783fdSXin Li           palette_out.push_back(palette_in[i * 4 + 2]);
147*e47783fdSXin Li           palette_out.push_back(palette_in[i * 4 + 3]);
148*e47783fdSXin Li         }
149*e47783fdSXin Li       }
150*e47783fdSXin Li       inputstate->info_png.color.palettesize = palette_out.size() / 4;
151*e47783fdSXin Li       for (size_t i = 0; i < palette_out.size(); i++) {
152*e47783fdSXin Li         palette_in[i] = palette_out[i];
153*e47783fdSXin Li       }
154*e47783fdSXin Li     }
155*e47783fdSXin Li   }
156*e47783fdSXin Li }
157*e47783fdSXin Li 
158*e47783fdSXin Li // Tries to optimize given a single PNG filter strategy.
159*e47783fdSXin Li // Returns 0 if ok, other value for error
TryOptimize(const std::vector<unsigned char> & image,unsigned w,unsigned h,const lodepng::State & inputstate,bool bit16,bool keep_colortype,const std::vector<unsigned char> & origfile,ZopfliPNGFilterStrategy filterstrategy,bool use_zopfli,int windowsize,const ZopfliPNGOptions * png_options,std::vector<unsigned char> * out)160*e47783fdSXin Li unsigned TryOptimize(
161*e47783fdSXin Li     const std::vector<unsigned char>& image, unsigned w, unsigned h,
162*e47783fdSXin Li     const lodepng::State& inputstate, bool bit16, bool keep_colortype,
163*e47783fdSXin Li     const std::vector<unsigned char>& origfile,
164*e47783fdSXin Li     ZopfliPNGFilterStrategy filterstrategy,
165*e47783fdSXin Li     bool use_zopfli, int windowsize, const ZopfliPNGOptions* png_options,
166*e47783fdSXin Li     std::vector<unsigned char>* out) {
167*e47783fdSXin Li   unsigned error = 0;
168*e47783fdSXin Li 
169*e47783fdSXin Li   lodepng::State state;
170*e47783fdSXin Li   state.encoder.zlibsettings.windowsize = windowsize;
171*e47783fdSXin Li   if (use_zopfli && png_options->use_zopfli) {
172*e47783fdSXin Li     state.encoder.zlibsettings.custom_deflate = CustomPNGDeflate;
173*e47783fdSXin Li     state.encoder.zlibsettings.custom_context = png_options;
174*e47783fdSXin Li   }
175*e47783fdSXin Li 
176*e47783fdSXin Li   if (keep_colortype) {
177*e47783fdSXin Li     state.encoder.auto_convert = 0;
178*e47783fdSXin Li     lodepng_color_mode_copy(&state.info_png.color, &inputstate.info_png.color);
179*e47783fdSXin Li   }
180*e47783fdSXin Li   if (inputstate.info_png.color.colortype == LCT_PALETTE) {
181*e47783fdSXin Li     // Make it preserve the original palette order
182*e47783fdSXin Li     lodepng_color_mode_copy(&state.info_raw, &inputstate.info_png.color);
183*e47783fdSXin Li     state.info_raw.colortype = LCT_RGBA;
184*e47783fdSXin Li     state.info_raw.bitdepth = 8;
185*e47783fdSXin Li   }
186*e47783fdSXin Li   if (bit16) {
187*e47783fdSXin Li     state.info_raw.bitdepth = 16;
188*e47783fdSXin Li   }
189*e47783fdSXin Li 
190*e47783fdSXin Li   state.encoder.filter_palette_zero = 0;
191*e47783fdSXin Li 
192*e47783fdSXin Li   std::vector<unsigned char> filters;
193*e47783fdSXin Li   switch (filterstrategy) {
194*e47783fdSXin Li     case kStrategyZero:
195*e47783fdSXin Li       state.encoder.filter_strategy = LFS_ZERO;
196*e47783fdSXin Li       break;
197*e47783fdSXin Li     case kStrategyMinSum:
198*e47783fdSXin Li       state.encoder.filter_strategy = LFS_MINSUM;
199*e47783fdSXin Li       break;
200*e47783fdSXin Li     case kStrategyEntropy:
201*e47783fdSXin Li       state.encoder.filter_strategy = LFS_ENTROPY;
202*e47783fdSXin Li       break;
203*e47783fdSXin Li     case kStrategyBruteForce:
204*e47783fdSXin Li       state.encoder.filter_strategy = LFS_BRUTE_FORCE;
205*e47783fdSXin Li       break;
206*e47783fdSXin Li     case kStrategyOne:
207*e47783fdSXin Li       state.encoder.filter_strategy = LFS_ONE;
208*e47783fdSXin Li       break;
209*e47783fdSXin Li     case kStrategyTwo:
210*e47783fdSXin Li       state.encoder.filter_strategy = LFS_TWO;
211*e47783fdSXin Li       break;
212*e47783fdSXin Li     case kStrategyThree:
213*e47783fdSXin Li       state.encoder.filter_strategy = LFS_THREE;
214*e47783fdSXin Li       break;
215*e47783fdSXin Li     case kStrategyFour:
216*e47783fdSXin Li       state.encoder.filter_strategy = LFS_FOUR;
217*e47783fdSXin Li       break;
218*e47783fdSXin Li     case kStrategyPredefined:
219*e47783fdSXin Li       lodepng::getFilterTypes(filters, origfile);
220*e47783fdSXin Li       if (filters.size() != h) return 1;  // Error getting filters
221*e47783fdSXin Li       state.encoder.filter_strategy = LFS_PREDEFINED;
222*e47783fdSXin Li       state.encoder.predefined_filters = &filters[0];
223*e47783fdSXin Li       break;
224*e47783fdSXin Li     default:
225*e47783fdSXin Li       break;
226*e47783fdSXin Li   }
227*e47783fdSXin Li 
228*e47783fdSXin Li   state.encoder.add_id = false;
229*e47783fdSXin Li   state.encoder.text_compression = 1;
230*e47783fdSXin Li 
231*e47783fdSXin Li   error = lodepng::encode(*out, image, w, h, state);
232*e47783fdSXin Li 
233*e47783fdSXin Li   // For very small output, also try without palette, it may be smaller thanks
234*e47783fdSXin Li   // to no palette storage overhead.
235*e47783fdSXin Li   if (!error && out->size() < 4096 && !keep_colortype) {
236*e47783fdSXin Li     if (lodepng::getPNGHeaderInfo(*out).color.colortype == LCT_PALETTE) {
237*e47783fdSXin Li       LodePNGColorStats stats;
238*e47783fdSXin Li       lodepng_color_stats_init(&stats);
239*e47783fdSXin Li       lodepng_compute_color_stats(&stats, &image[0], w, h, &state.info_raw);
240*e47783fdSXin Li       // Too small for tRNS chunk overhead.
241*e47783fdSXin Li       if (w * h <= 16 && stats.key) stats.alpha = 1;
242*e47783fdSXin Li       state.encoder.auto_convert = 0;
243*e47783fdSXin Li       state.info_png.color.colortype = (stats.alpha ? LCT_RGBA : LCT_RGB);
244*e47783fdSXin Li       state.info_png.color.bitdepth = 8;
245*e47783fdSXin Li       state.info_png.color.key_defined = (stats.key && !stats.alpha);
246*e47783fdSXin Li       if (state.info_png.color.key_defined) {
247*e47783fdSXin Li         state.info_png.color.key_defined = 1;
248*e47783fdSXin Li         state.info_png.color.key_r = (stats.key_r & 255u);
249*e47783fdSXin Li         state.info_png.color.key_g = (stats.key_g & 255u);
250*e47783fdSXin Li         state.info_png.color.key_b = (stats.key_b & 255u);
251*e47783fdSXin Li       }
252*e47783fdSXin Li 
253*e47783fdSXin Li       std::vector<unsigned char> out2;
254*e47783fdSXin Li       error = lodepng::encode(out2, image, w, h, state);
255*e47783fdSXin Li       if (out2.size() < out->size()) out->swap(out2);
256*e47783fdSXin Li     }
257*e47783fdSXin Li   }
258*e47783fdSXin Li 
259*e47783fdSXin Li   if (error) {
260*e47783fdSXin Li     printf("Encoding error %u: %s\n", error, lodepng_error_text(error));
261*e47783fdSXin Li     return error;
262*e47783fdSXin Li   }
263*e47783fdSXin Li 
264*e47783fdSXin Li   return 0;
265*e47783fdSXin Li }
266*e47783fdSXin Li 
267*e47783fdSXin Li // Use fast compression to check which PNG filter strategy gives the smallest
268*e47783fdSXin Li // output. This allows to then do the slow and good compression only on that
269*e47783fdSXin Li // filter type.
AutoChooseFilterStrategy(const std::vector<unsigned char> & image,unsigned w,unsigned h,const lodepng::State & inputstate,bool bit16,bool keep_colortype,const std::vector<unsigned char> & origfile,int numstrategies,ZopfliPNGFilterStrategy * strategies,bool * enable)270*e47783fdSXin Li unsigned AutoChooseFilterStrategy(const std::vector<unsigned char>& image,
271*e47783fdSXin Li                                   unsigned w, unsigned h,
272*e47783fdSXin Li                                   const lodepng::State& inputstate,
273*e47783fdSXin Li                                   bool bit16, bool keep_colortype,
274*e47783fdSXin Li                                   const std::vector<unsigned char>& origfile,
275*e47783fdSXin Li                                   int numstrategies,
276*e47783fdSXin Li                                   ZopfliPNGFilterStrategy* strategies,
277*e47783fdSXin Li                                   bool* enable) {
278*e47783fdSXin Li   std::vector<unsigned char> out;
279*e47783fdSXin Li   size_t bestsize = 0;
280*e47783fdSXin Li   int bestfilter = 0;
281*e47783fdSXin Li 
282*e47783fdSXin Li   // A large window size should still be used to do the quick compression to
283*e47783fdSXin Li   // try out filter strategies: which filter strategy is the best depends
284*e47783fdSXin Li   // largely on the window size, the closer to the actual used window size the
285*e47783fdSXin Li   // better.
286*e47783fdSXin Li   int windowsize = 8192;
287*e47783fdSXin Li 
288*e47783fdSXin Li   for (int i = 0; i < numstrategies; i++) {
289*e47783fdSXin Li     out.clear();
290*e47783fdSXin Li     unsigned error = TryOptimize(image, w, h, inputstate, bit16, keep_colortype,
291*e47783fdSXin Li                                  origfile, strategies[i], false, windowsize, 0,
292*e47783fdSXin Li                                  &out);
293*e47783fdSXin Li     if (error) return error;
294*e47783fdSXin Li     if (bestsize == 0 || out.size() < bestsize) {
295*e47783fdSXin Li       bestsize = out.size();
296*e47783fdSXin Li       bestfilter = i;
297*e47783fdSXin Li     }
298*e47783fdSXin Li   }
299*e47783fdSXin Li 
300*e47783fdSXin Li   for (int i = 0; i < numstrategies; i++) {
301*e47783fdSXin Li     enable[i] = (i == bestfilter);
302*e47783fdSXin Li   }
303*e47783fdSXin Li 
304*e47783fdSXin Li   return 0;  /* OK */
305*e47783fdSXin Li }
306*e47783fdSXin Li 
307*e47783fdSXin Li // Outputs the intersection of keepnames and non-essential chunks which are in
308*e47783fdSXin Li // the PNG image.
ChunksToKeep(const std::vector<unsigned char> & origpng,const std::vector<std::string> & keepnames,std::set<std::string> * result)309*e47783fdSXin Li void ChunksToKeep(const std::vector<unsigned char>& origpng,
310*e47783fdSXin Li                   const std::vector<std::string>& keepnames,
311*e47783fdSXin Li                   std::set<std::string>* result) {
312*e47783fdSXin Li   std::vector<std::string> names[3];
313*e47783fdSXin Li   std::vector<std::vector<unsigned char> > chunks[3];
314*e47783fdSXin Li 
315*e47783fdSXin Li   lodepng::getChunks(names, chunks, origpng);
316*e47783fdSXin Li 
317*e47783fdSXin Li   for (size_t i = 0; i < 3; i++) {
318*e47783fdSXin Li     for (size_t j = 0; j < names[i].size(); j++) {
319*e47783fdSXin Li       for (size_t k = 0; k < keepnames.size(); k++) {
320*e47783fdSXin Li         if (keepnames[k] == names[i][j]) {
321*e47783fdSXin Li           result->insert(names[i][j]);
322*e47783fdSXin Li         }
323*e47783fdSXin Li       }
324*e47783fdSXin Li     }
325*e47783fdSXin Li   }
326*e47783fdSXin Li }
327*e47783fdSXin Li 
328*e47783fdSXin Li // Keeps chunks with given names from the original png by literally copying them
329*e47783fdSXin Li // into the new png
KeepChunks(const std::vector<unsigned char> & origpng,const std::vector<std::string> & keepnames,std::vector<unsigned char> * png)330*e47783fdSXin Li void KeepChunks(const std::vector<unsigned char>& origpng,
331*e47783fdSXin Li                 const std::vector<std::string>& keepnames,
332*e47783fdSXin Li                 std::vector<unsigned char>* png) {
333*e47783fdSXin Li   std::vector<std::string> names[3];
334*e47783fdSXin Li   std::vector<std::vector<unsigned char> > chunks[3];
335*e47783fdSXin Li 
336*e47783fdSXin Li   lodepng::getChunks(names, chunks, origpng);
337*e47783fdSXin Li   std::vector<std::vector<unsigned char> > keepchunks[3];
338*e47783fdSXin Li 
339*e47783fdSXin Li   // There are 3 distinct locations in a PNG file for chunks: between IHDR and
340*e47783fdSXin Li   // PLTE, between PLTE and IDAT, and between IDAT and IEND. Keep each chunk at
341*e47783fdSXin Li   // its corresponding location in the new PNG.
342*e47783fdSXin Li   for (size_t i = 0; i < 3; i++) {
343*e47783fdSXin Li     for (size_t j = 0; j < names[i].size(); j++) {
344*e47783fdSXin Li       for (size_t k = 0; k < keepnames.size(); k++) {
345*e47783fdSXin Li         if (keepnames[k] == names[i][j]) {
346*e47783fdSXin Li           keepchunks[i].push_back(chunks[i][j]);
347*e47783fdSXin Li         }
348*e47783fdSXin Li       }
349*e47783fdSXin Li     }
350*e47783fdSXin Li   }
351*e47783fdSXin Li 
352*e47783fdSXin Li   lodepng::insertChunks(*png, keepchunks);
353*e47783fdSXin Li }
354*e47783fdSXin Li 
ZopfliPNGOptimize(const std::vector<unsigned char> & origpng,const ZopfliPNGOptions & png_options,bool verbose,std::vector<unsigned char> * resultpng)355*e47783fdSXin Li int ZopfliPNGOptimize(const std::vector<unsigned char>& origpng,
356*e47783fdSXin Li     const ZopfliPNGOptions& png_options,
357*e47783fdSXin Li     bool verbose,
358*e47783fdSXin Li     std::vector<unsigned char>* resultpng) {
359*e47783fdSXin Li   // Use the largest possible deflate window size
360*e47783fdSXin Li   int windowsize = 32768;
361*e47783fdSXin Li 
362*e47783fdSXin Li   ZopfliPNGFilterStrategy filterstrategies[kNumFilterStrategies] = {
363*e47783fdSXin Li     kStrategyZero, kStrategyOne, kStrategyTwo, kStrategyThree, kStrategyFour,
364*e47783fdSXin Li     kStrategyMinSum, kStrategyEntropy, kStrategyPredefined, kStrategyBruteForce
365*e47783fdSXin Li   };
366*e47783fdSXin Li   bool strategy_enable[kNumFilterStrategies] = {
367*e47783fdSXin Li     false, false, false, false, false, false, false, false, false
368*e47783fdSXin Li   };
369*e47783fdSXin Li   std::string strategy_name[kNumFilterStrategies] = {
370*e47783fdSXin Li     "zero", "one", "two", "three", "four",
371*e47783fdSXin Li     "minimum sum", "entropy", "predefined", "brute force"
372*e47783fdSXin Li   };
373*e47783fdSXin Li   for (size_t i = 0; i < png_options.filter_strategies.size(); i++) {
374*e47783fdSXin Li     strategy_enable[png_options.filter_strategies[i]] = true;
375*e47783fdSXin Li   }
376*e47783fdSXin Li 
377*e47783fdSXin Li   std::vector<unsigned char> image;
378*e47783fdSXin Li   unsigned w, h;
379*e47783fdSXin Li   unsigned error;
380*e47783fdSXin Li   lodepng::State inputstate;
381*e47783fdSXin Li   error = lodepng::decode(image, w, h, inputstate, origpng);
382*e47783fdSXin Li 
383*e47783fdSXin Li   bool keep_colortype = png_options.keep_colortype;
384*e47783fdSXin Li 
385*e47783fdSXin Li   if (!png_options.keepchunks.empty()) {
386*e47783fdSXin Li     // If the user wants to keep the non-essential chunks bKGD or sBIT, the
387*e47783fdSXin Li     // input color type has to be kept since the chunks format depend on it.
388*e47783fdSXin Li     // This may severely hurt compression if it is not an ideal color type.
389*e47783fdSXin Li     // Ideally these chunks should not be kept for web images. Handling of bKGD
390*e47783fdSXin Li     // chunks could be improved by changing its color type but not done yet due
391*e47783fdSXin Li     // to its additional complexity, for sBIT such improvement is usually not
392*e47783fdSXin Li     // possible.
393*e47783fdSXin Li     std::set<std::string> keepchunks;
394*e47783fdSXin Li     ChunksToKeep(origpng, png_options.keepchunks, &keepchunks);
395*e47783fdSXin Li     if (keepchunks.count("bKGD") || keepchunks.count("sBIT")) {
396*e47783fdSXin Li       if (!keep_colortype && verbose) {
397*e47783fdSXin Li         printf("Forced to keep original color type due to keeping bKGD or sBIT"
398*e47783fdSXin Li                " chunk.\n");
399*e47783fdSXin Li       }
400*e47783fdSXin Li       keep_colortype = true;
401*e47783fdSXin Li     }
402*e47783fdSXin Li   }
403*e47783fdSXin Li 
404*e47783fdSXin Li   if (error) {
405*e47783fdSXin Li     if (verbose) {
406*e47783fdSXin Li       if (error == 1) {
407*e47783fdSXin Li         printf("Decoding error\n");
408*e47783fdSXin Li       } else {
409*e47783fdSXin Li         printf("Decoding error %u: %s\n", error, lodepng_error_text(error));
410*e47783fdSXin Li       }
411*e47783fdSXin Li     }
412*e47783fdSXin Li     return error;
413*e47783fdSXin Li   }
414*e47783fdSXin Li 
415*e47783fdSXin Li   bool bit16 = false;  // Using 16-bit per channel raw image
416*e47783fdSXin Li   if (inputstate.info_png.color.bitdepth == 16 &&
417*e47783fdSXin Li       (keep_colortype || !png_options.lossy_8bit)) {
418*e47783fdSXin Li     // Decode as 16-bit
419*e47783fdSXin Li     image.clear();
420*e47783fdSXin Li     error = lodepng::decode(image, w, h, origpng, LCT_RGBA, 16);
421*e47783fdSXin Li     bit16 = true;
422*e47783fdSXin Li   }
423*e47783fdSXin Li 
424*e47783fdSXin Li   if (!error) {
425*e47783fdSXin Li     // If lossy_transparent, remove RGB information from pixels with alpha=0
426*e47783fdSXin Li     if (png_options.lossy_transparent && !bit16) {
427*e47783fdSXin Li       LossyOptimizeTransparent(&inputstate, &image[0], w, h);
428*e47783fdSXin Li     }
429*e47783fdSXin Li 
430*e47783fdSXin Li     if (png_options.auto_filter_strategy) {
431*e47783fdSXin Li       error = AutoChooseFilterStrategy(image, w, h, inputstate, bit16,
432*e47783fdSXin Li                                        keep_colortype, origpng,
433*e47783fdSXin Li                                        /* Don't try brute force */
434*e47783fdSXin Li                                        kNumFilterStrategies - 1,
435*e47783fdSXin Li                                        filterstrategies, strategy_enable);
436*e47783fdSXin Li     }
437*e47783fdSXin Li   }
438*e47783fdSXin Li 
439*e47783fdSXin Li   if (!error) {
440*e47783fdSXin Li     size_t bestsize = 0;
441*e47783fdSXin Li 
442*e47783fdSXin Li     for (int i = 0; i < kNumFilterStrategies; i++) {
443*e47783fdSXin Li       if (!strategy_enable[i]) continue;
444*e47783fdSXin Li 
445*e47783fdSXin Li       std::vector<unsigned char> temp;
446*e47783fdSXin Li       error = TryOptimize(image, w, h, inputstate, bit16, keep_colortype,
447*e47783fdSXin Li                           origpng, filterstrategies[i], true /* use_zopfli */,
448*e47783fdSXin Li                           windowsize, &png_options, &temp);
449*e47783fdSXin Li       if (!error) {
450*e47783fdSXin Li         if (verbose) {
451*e47783fdSXin Li           printf("Filter strategy %s: %d bytes\n",
452*e47783fdSXin Li                  strategy_name[i].c_str(), (int) temp.size());
453*e47783fdSXin Li         }
454*e47783fdSXin Li         if (bestsize == 0 || temp.size() < bestsize) {
455*e47783fdSXin Li           bestsize = temp.size();
456*e47783fdSXin Li           (*resultpng).swap(temp);  // Store best result so far in the output.
457*e47783fdSXin Li         }
458*e47783fdSXin Li       }
459*e47783fdSXin Li     }
460*e47783fdSXin Li 
461*e47783fdSXin Li     if (!png_options.keepchunks.empty()) {
462*e47783fdSXin Li       KeepChunks(origpng, png_options.keepchunks, resultpng);
463*e47783fdSXin Li     }
464*e47783fdSXin Li   }
465*e47783fdSXin Li 
466*e47783fdSXin Li   return error;
467*e47783fdSXin Li }
468*e47783fdSXin Li 
CZopfliPNGSetDefaults(CZopfliPNGOptions * png_options)469*e47783fdSXin Li extern "C" void CZopfliPNGSetDefaults(CZopfliPNGOptions* png_options) {
470*e47783fdSXin Li 
471*e47783fdSXin Li   memset(png_options, 0, sizeof(*png_options));
472*e47783fdSXin Li   // Constructor sets the defaults
473*e47783fdSXin Li   ZopfliPNGOptions opts;
474*e47783fdSXin Li 
475*e47783fdSXin Li   png_options->lossy_transparent    = opts.lossy_transparent;
476*e47783fdSXin Li   png_options->lossy_8bit           = opts.lossy_8bit;
477*e47783fdSXin Li   png_options->auto_filter_strategy = opts.auto_filter_strategy;
478*e47783fdSXin Li   png_options->use_zopfli           = opts.use_zopfli;
479*e47783fdSXin Li   png_options->num_iterations       = opts.num_iterations;
480*e47783fdSXin Li   png_options->num_iterations_large = opts.num_iterations_large;
481*e47783fdSXin Li   png_options->block_split_strategy = opts.block_split_strategy;
482*e47783fdSXin Li }
483*e47783fdSXin Li 
CZopfliPNGOptimize(const unsigned char * origpng,const size_t origpng_size,const CZopfliPNGOptions * png_options,int verbose,unsigned char ** resultpng,size_t * resultpng_size)484*e47783fdSXin Li extern "C" int CZopfliPNGOptimize(const unsigned char* origpng,
485*e47783fdSXin Li                                   const size_t origpng_size,
486*e47783fdSXin Li                                   const CZopfliPNGOptions* png_options,
487*e47783fdSXin Li                                   int verbose,
488*e47783fdSXin Li                                   unsigned char** resultpng,
489*e47783fdSXin Li                                   size_t* resultpng_size) {
490*e47783fdSXin Li   ZopfliPNGOptions opts;
491*e47783fdSXin Li 
492*e47783fdSXin Li   // Copy over to the C++-style struct
493*e47783fdSXin Li   opts.lossy_transparent    = !!png_options->lossy_transparent;
494*e47783fdSXin Li   opts.lossy_8bit           = !!png_options->lossy_8bit;
495*e47783fdSXin Li   opts.auto_filter_strategy = !!png_options->auto_filter_strategy;
496*e47783fdSXin Li   opts.use_zopfli           = !!png_options->use_zopfli;
497*e47783fdSXin Li   opts.num_iterations       = png_options->num_iterations;
498*e47783fdSXin Li   opts.num_iterations_large = png_options->num_iterations_large;
499*e47783fdSXin Li   opts.block_split_strategy = png_options->block_split_strategy;
500*e47783fdSXin Li 
501*e47783fdSXin Li   for (int i = 0; i < png_options->num_filter_strategies; i++) {
502*e47783fdSXin Li     opts.filter_strategies.push_back(png_options->filter_strategies[i]);
503*e47783fdSXin Li   }
504*e47783fdSXin Li 
505*e47783fdSXin Li   for (int i = 0; i < png_options->num_keepchunks; i++) {
506*e47783fdSXin Li     opts.keepchunks.push_back(png_options->keepchunks[i]);
507*e47783fdSXin Li   }
508*e47783fdSXin Li 
509*e47783fdSXin Li   const std::vector<unsigned char> origpng_cc(origpng, origpng + origpng_size);
510*e47783fdSXin Li   std::vector<unsigned char> resultpng_cc;
511*e47783fdSXin Li 
512*e47783fdSXin Li   int ret = ZopfliPNGOptimize(origpng_cc, opts, !!verbose, &resultpng_cc);
513*e47783fdSXin Li   if (ret) {
514*e47783fdSXin Li     return ret;
515*e47783fdSXin Li   }
516*e47783fdSXin Li 
517*e47783fdSXin Li   *resultpng_size = resultpng_cc.size();
518*e47783fdSXin Li   *resultpng      = (unsigned char*) malloc(resultpng_cc.size());
519*e47783fdSXin Li   if (!(*resultpng)) {
520*e47783fdSXin Li     return ENOMEM;
521*e47783fdSXin Li   }
522*e47783fdSXin Li 
523*e47783fdSXin Li   memcpy(*resultpng,
524*e47783fdSXin Li          reinterpret_cast<unsigned char*>(&resultpng_cc[0]),
525*e47783fdSXin Li          resultpng_cc.size());
526*e47783fdSXin Li 
527*e47783fdSXin Li   return 0;
528*e47783fdSXin Li }
529