• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "compile/Png.h"
18 
19 #include <png.h>
20 #include <zlib.h>
21 
22 #include <algorithm>
23 #include <unordered_map>
24 #include <unordered_set>
25 
26 #include "android-base/errors.h"
27 #include "android-base/logging.h"
28 #include "android-base/macros.h"
29 
30 namespace aapt {
31 
32 // Custom deleter that destroys libpng read and info structs.
33 class PngReadStructDeleter {
34  public:
PngReadStructDeleter(png_structp read_ptr,png_infop info_ptr)35   PngReadStructDeleter(png_structp read_ptr, png_infop info_ptr)
36       : read_ptr_(read_ptr), info_ptr_(info_ptr) {}
37 
~PngReadStructDeleter()38   ~PngReadStructDeleter() {
39     png_destroy_read_struct(&read_ptr_, &info_ptr_, nullptr);
40   }
41 
42  private:
43   png_structp read_ptr_;
44   png_infop info_ptr_;
45 
46   DISALLOW_COPY_AND_ASSIGN(PngReadStructDeleter);
47 };
48 
49 // Custom deleter that destroys libpng write and info structs.
50 class PngWriteStructDeleter {
51  public:
PngWriteStructDeleter(png_structp write_ptr,png_infop info_ptr)52   PngWriteStructDeleter(png_structp write_ptr, png_infop info_ptr)
53       : write_ptr_(write_ptr), info_ptr_(info_ptr) {}
54 
~PngWriteStructDeleter()55   ~PngWriteStructDeleter() {
56     png_destroy_write_struct(&write_ptr_, &info_ptr_);
57   }
58 
59  private:
60   png_structp write_ptr_;
61   png_infop info_ptr_;
62 
63   DISALLOW_COPY_AND_ASSIGN(PngWriteStructDeleter);
64 };
65 
66 // Custom warning logging method that uses IDiagnostics.
LogWarning(png_structp png_ptr,png_const_charp warning_msg)67 static void LogWarning(png_structp png_ptr, png_const_charp warning_msg) {
68   IDiagnostics* diag = (IDiagnostics*)png_get_error_ptr(png_ptr);
69   diag->Warn(DiagMessage() << warning_msg);
70 }
71 
72 // Custom error logging method that uses IDiagnostics.
LogError(png_structp png_ptr,png_const_charp error_msg)73 static void LogError(png_structp png_ptr, png_const_charp error_msg) {
74   IDiagnostics* diag = (IDiagnostics*)png_get_error_ptr(png_ptr);
75   diag->Error(DiagMessage() << error_msg);
76 
77   // Causes libpng to longjmp to the spot where setjmp was set. This is how libpng does
78   // error handling. If this custom error handler method were to return, libpng would, by
79   // default, print the error message to stdout and call the same png_longjmp method.
80   png_longjmp(png_ptr, 1);
81 }
82 
ReadDataFromStream(png_structp png_ptr,png_bytep buffer,png_size_t len)83 static void ReadDataFromStream(png_structp png_ptr, png_bytep buffer, png_size_t len) {
84   io::InputStream* in = (io::InputStream*)png_get_io_ptr(png_ptr);
85 
86   const void* in_buffer;
87   size_t in_len;
88   if (!in->Next(&in_buffer, &in_len)) {
89     if (in->HadError()) {
90       std::stringstream error_msg_builder;
91       error_msg_builder << "failed reading from input";
92       if (!in->GetError().empty()) {
93         error_msg_builder << ": " << in->GetError();
94       }
95       std::string err = error_msg_builder.str();
96       png_error(png_ptr, err.c_str());
97     }
98     return;
99   }
100 
101   const size_t bytes_read = std::min(in_len, len);
102   memcpy(buffer, in_buffer, bytes_read);
103   if (bytes_read != in_len) {
104     in->BackUp(in_len - bytes_read);
105   }
106 }
107 
WriteDataToStream(png_structp png_ptr,png_bytep buffer,png_size_t len)108 static void WriteDataToStream(png_structp png_ptr, png_bytep buffer, png_size_t len) {
109   io::OutputStream* out = (io::OutputStream*)png_get_io_ptr(png_ptr);
110 
111   void* out_buffer;
112   size_t out_len;
113   while (len > 0) {
114     if (!out->Next(&out_buffer, &out_len)) {
115       if (out->HadError()) {
116         std::stringstream err_msg_builder;
117         err_msg_builder << "failed writing to output";
118         if (!out->GetError().empty()) {
119           err_msg_builder << ": " << out->GetError();
120         }
121         std::string err = out->GetError();
122         png_error(png_ptr, err.c_str());
123       }
124       return;
125     }
126 
127     const size_t bytes_written = std::min(out_len, len);
128     memcpy(out_buffer, buffer, bytes_written);
129 
130     // Advance the input buffer.
131     buffer += bytes_written;
132     len -= bytes_written;
133 
134     // Advance the output buffer.
135     out_len -= bytes_written;
136   }
137 
138   // If the entire output buffer wasn't used, backup.
139   if (out_len > 0) {
140     out->BackUp(out_len);
141   }
142 }
143 
ReadPng(IAaptContext * context,const Source & source,io::InputStream * in)144 std::unique_ptr<Image> ReadPng(IAaptContext* context, const Source& source, io::InputStream* in) {
145   // Create a diagnostics that has the source information encoded.
146   SourcePathDiagnostics source_diag(source, context->GetDiagnostics());
147 
148   // Read the first 8 bytes of the file looking for the PNG signature.
149   // Bail early if it does not match.
150   const png_byte* signature;
151   size_t buffer_size;
152   if (!in->Next((const void**)&signature, &buffer_size)) {
153     if (in->HadError()) {
154       source_diag.Error(DiagMessage() << "failed to read PNG signature: " << in->GetError());
155     } else {
156       source_diag.Error(DiagMessage() << "not enough data for PNG signature");
157     }
158     return {};
159   }
160 
161   if (buffer_size < kPngSignatureSize || png_sig_cmp(signature, 0, kPngSignatureSize) != 0) {
162     source_diag.Error(DiagMessage() << "file signature does not match PNG signature");
163     return {};
164   }
165 
166   // Start at the beginning of the first chunk.
167   in->BackUp(buffer_size - kPngSignatureSize);
168 
169   // Create and initialize the png_struct with the default error and warning handlers.
170   // The header version is also passed in to ensure that this was built against the same
171   // version of libpng.
172   png_structp read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
173   if (read_ptr == nullptr) {
174     source_diag.Error(DiagMessage() << "failed to create libpng read png_struct");
175     return {};
176   }
177 
178   // Create and initialize the memory for image header and data.
179   png_infop info_ptr = png_create_info_struct(read_ptr);
180   if (info_ptr == nullptr) {
181     source_diag.Error(DiagMessage() << "failed to create libpng read png_info");
182     png_destroy_read_struct(&read_ptr, nullptr, nullptr);
183     return {};
184   }
185 
186   // Automatically release PNG resources at end of scope.
187   PngReadStructDeleter png_read_deleter(read_ptr, info_ptr);
188 
189   // libpng uses longjmp to jump to an error handling routine.
190   // setjmp will only return true if it was jumped to, aka there was
191   // an error.
192   if (setjmp(png_jmpbuf(read_ptr))) {
193     return {};
194   }
195 
196   // Handle warnings ourselves via IDiagnostics.
197   png_set_error_fn(read_ptr, (png_voidp)&source_diag, LogError, LogWarning);
198 
199   // Set up the read functions which read from our custom data sources.
200   png_set_read_fn(read_ptr, (png_voidp)in, ReadDataFromStream);
201 
202   // Skip the signature that we already read.
203   png_set_sig_bytes(read_ptr, kPngSignatureSize);
204 
205   // Read the chunk headers.
206   png_read_info(read_ptr, info_ptr);
207 
208   // Extract image meta-data from the various chunk headers.
209   uint32_t width, height;
210   int bit_depth, color_type, interlace_method, compression_method, filter_method;
211   png_get_IHDR(read_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
212                &interlace_method, &compression_method, &filter_method);
213 
214   // When the image is read, expand it so that it is in RGBA 8888 format
215   // so that image handling is uniform.
216 
217   if (color_type == PNG_COLOR_TYPE_PALETTE) {
218     png_set_palette_to_rgb(read_ptr);
219   }
220 
221   if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
222     png_set_expand_gray_1_2_4_to_8(read_ptr);
223   }
224 
225   if (png_get_valid(read_ptr, info_ptr, PNG_INFO_tRNS)) {
226     png_set_tRNS_to_alpha(read_ptr);
227   }
228 
229   if (bit_depth == 16) {
230     png_set_strip_16(read_ptr);
231   }
232 
233   if (!(color_type & PNG_COLOR_MASK_ALPHA)) {
234     png_set_add_alpha(read_ptr, 0xFF, PNG_FILLER_AFTER);
235   }
236 
237   if (color_type == PNG_COLOR_TYPE_GRAY ||
238       color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
239     png_set_gray_to_rgb(read_ptr);
240   }
241 
242   if (interlace_method != PNG_INTERLACE_NONE) {
243     png_set_interlace_handling(read_ptr);
244   }
245 
246   // Once all the options for reading have been set, we need to flush
247   // them to libpng.
248   png_read_update_info(read_ptr, info_ptr);
249 
250   // 9-patch uses int32_t to index images, so we cap the image dimensions to
251   // something
252   // that can always be represented by 9-patch.
253   if (width > std::numeric_limits<int32_t>::max() || height > std::numeric_limits<int32_t>::max()) {
254     source_diag.Error(DiagMessage()
255                       << "PNG image dimensions are too large: " << width << "x" << height);
256     return {};
257   }
258 
259   std::unique_ptr<Image> output_image = util::make_unique<Image>();
260   output_image->width = static_cast<int32_t>(width);
261   output_image->height = static_cast<int32_t>(height);
262 
263   const size_t row_bytes = png_get_rowbytes(read_ptr, info_ptr);
264   CHECK(row_bytes == 4 * width);  // RGBA
265 
266   // Allocate one large block to hold the image.
267   output_image->data = std::unique_ptr<uint8_t[]>(new uint8_t[height * row_bytes]);
268 
269   // Create an array of rows that index into the data block.
270   output_image->rows = std::unique_ptr<uint8_t* []>(new uint8_t*[height]);
271   for (uint32_t h = 0; h < height; h++) {
272     output_image->rows[h] = output_image->data.get() + (h * row_bytes);
273   }
274 
275   // Actually read the image pixels.
276   png_read_image(read_ptr, output_image->rows.get());
277 
278   // Finish reading. This will read any other chunks after the image data.
279   png_read_end(read_ptr, info_ptr);
280 
281   return output_image;
282 }
283 
284 // Experimentally chosen constant to be added to the overhead of using color type
285 // PNG_COLOR_TYPE_PALETTE to account for the uncompressability of the palette chunk.
286 // Without this, many small PNGs encoded with palettes are larger after compression than
287 // the same PNGs encoded as RGBA.
288 constexpr static const size_t kPaletteOverheadConstant = 1024u * 10u;
289 
290 // Pick a color type by which to encode the image, based on which color type will take
291 // the least amount of disk space.
292 //
293 // 9-patch images traditionally have not been encoded with palettes.
294 // The original rationale was to avoid dithering until after scaling,
295 // but I don't think this would be an issue with palettes. Either way,
296 // our naive size estimation tends to be wrong for small images like 9-patches
297 // and using palettes balloons the size of the resulting 9-patch.
298 // In order to not regress in size, restrict 9-patch to not use palettes.
299 
300 // The options are:
301 //
302 // - RGB
303 // - RGBA
304 // - RGB + cheap alpha
305 // - Color palette
306 // - Color palette + cheap alpha
307 // - Color palette + alpha palette
308 // - Grayscale
309 // - Grayscale + cheap alpha
310 // - Grayscale + alpha
311 //
PickColorType(int32_t width,int32_t height,bool grayscale,bool convertible_to_grayscale,bool has_nine_patch,size_t color_palette_size,size_t alpha_palette_size)312 static int PickColorType(int32_t width, int32_t height, bool grayscale,
313                          bool convertible_to_grayscale, bool has_nine_patch,
314                          size_t color_palette_size, size_t alpha_palette_size) {
315   const size_t palette_chunk_size = 16 + color_palette_size * 3;
316   const size_t alpha_chunk_size = 16 + alpha_palette_size;
317   const size_t color_alpha_data_chunk_size = 16 + 4 * width * height;
318   const size_t color_data_chunk_size = 16 + 3 * width * height;
319   const size_t grayscale_alpha_data_chunk_size = 16 + 2 * width * height;
320   const size_t palette_data_chunk_size = 16 + width * height;
321 
322   if (grayscale) {
323     if (alpha_palette_size == 0) {
324       // This is the smallest the data can be.
325       return PNG_COLOR_TYPE_GRAY;
326     } else if (color_palette_size <= 256 && !has_nine_patch) {
327       // This grayscale has alpha and can fit within a palette.
328       // See if it is worth fitting into a palette.
329       const size_t palette_threshold = palette_chunk_size + alpha_chunk_size +
330                                        palette_data_chunk_size +
331                                        kPaletteOverheadConstant;
332       if (grayscale_alpha_data_chunk_size > palette_threshold) {
333         return PNG_COLOR_TYPE_PALETTE;
334       }
335     }
336     return PNG_COLOR_TYPE_GRAY_ALPHA;
337   }
338 
339   if (color_palette_size <= 256 && !has_nine_patch) {
340     // This image can fit inside a palette. Let's see if it is worth it.
341     size_t total_size_with_palette =
342         palette_data_chunk_size + palette_chunk_size;
343     size_t total_size_without_palette = color_data_chunk_size;
344     if (alpha_palette_size > 0) {
345       total_size_with_palette += alpha_palette_size;
346       total_size_without_palette = color_alpha_data_chunk_size;
347     }
348 
349     if (total_size_without_palette >
350         total_size_with_palette + kPaletteOverheadConstant) {
351       return PNG_COLOR_TYPE_PALETTE;
352     }
353   }
354 
355   if (convertible_to_grayscale) {
356     if (alpha_palette_size == 0) {
357       return PNG_COLOR_TYPE_GRAY;
358     } else {
359       return PNG_COLOR_TYPE_GRAY_ALPHA;
360     }
361   }
362 
363   if (alpha_palette_size == 0) {
364     return PNG_COLOR_TYPE_RGB;
365   }
366   return PNG_COLOR_TYPE_RGBA;
367 }
368 
369 // Assigns indices to the color and alpha palettes, encodes them, and then invokes
370 // png_set_PLTE/png_set_tRNS.
371 // This must be done before writing image data.
372 // Image data must be transformed to use the indices assigned within the palette.
WritePalette(png_structp write_ptr,png_infop write_info_ptr,std::unordered_map<uint32_t,int> * color_palette,std::unordered_set<uint32_t> * alpha_palette)373 static void WritePalette(png_structp write_ptr, png_infop write_info_ptr,
374                          std::unordered_map<uint32_t, int>* color_palette,
375                          std::unordered_set<uint32_t>* alpha_palette) {
376   CHECK(color_palette->size() <= 256);
377   CHECK(alpha_palette->size() <= 256);
378 
379   // Populate the PNG palette struct and assign indices to the color palette.
380 
381   // Colors in the alpha palette should have smaller indices.
382   // This will ensure that we can truncate the alpha palette if it is
383   // smaller than the color palette.
384   int index = 0;
385   for (uint32_t color : *alpha_palette) {
386     (*color_palette)[color] = index++;
387   }
388 
389   // Assign the rest of the entries.
390   for (auto& entry : *color_palette) {
391     if (entry.second == -1) {
392       entry.second = index++;
393     }
394   }
395 
396   // Create the PNG color palette struct.
397   auto color_palette_bytes = std::unique_ptr<png_color[]>(new png_color[color_palette->size()]);
398 
399   std::unique_ptr<png_byte[]> alpha_palette_bytes;
400   if (!alpha_palette->empty()) {
401     alpha_palette_bytes = std::unique_ptr<png_byte[]>(new png_byte[alpha_palette->size()]);
402   }
403 
404   for (const auto& entry : *color_palette) {
405     const uint32_t color = entry.first;
406     const int index = entry.second;
407     CHECK(index >= 0);
408     CHECK(static_cast<size_t>(index) < color_palette->size());
409 
410     png_colorp slot = color_palette_bytes.get() + index;
411     slot->red = color >> 24;
412     slot->green = color >> 16;
413     slot->blue = color >> 8;
414 
415     const png_byte alpha = color & 0x000000ff;
416     if (alpha != 0xff && alpha_palette_bytes) {
417       CHECK(static_cast<size_t>(index) < alpha_palette->size());
418       alpha_palette_bytes[index] = alpha;
419     }
420   }
421 
422   // The bytes get copied here, so it is safe to release color_palette_bytes at
423   // the end of function
424   // scope.
425   png_set_PLTE(write_ptr, write_info_ptr, color_palette_bytes.get(), color_palette->size());
426 
427   if (alpha_palette_bytes) {
428     png_set_tRNS(write_ptr, write_info_ptr, alpha_palette_bytes.get(), alpha_palette->size(),
429                  nullptr);
430   }
431 }
432 
433 // Write the 9-patch custom PNG chunks to write_info_ptr. This must be done
434 // before writing image data.
WriteNinePatch(png_structp write_ptr,png_infop write_info_ptr,const NinePatch * nine_patch)435 static void WriteNinePatch(png_structp write_ptr, png_infop write_info_ptr,
436                            const NinePatch* nine_patch) {
437   // The order of the chunks is important.
438   // 9-patch code in older platforms expects the 9-patch chunk to be last.
439 
440   png_unknown_chunk unknown_chunks[3];
441   memset(unknown_chunks, 0, sizeof(unknown_chunks));
442 
443   size_t index = 0;
444   size_t chunk_len = 0;
445 
446   std::unique_ptr<uint8_t[]> serialized_outline =
447       nine_patch->SerializeRoundedRectOutline(&chunk_len);
448   strcpy((char*)unknown_chunks[index].name, "npOl");
449   unknown_chunks[index].size = chunk_len;
450   unknown_chunks[index].data = (png_bytep)serialized_outline.get();
451   unknown_chunks[index].location = PNG_HAVE_PLTE;
452   index++;
453 
454   std::unique_ptr<uint8_t[]> serialized_layout_bounds;
455   if (nine_patch->layout_bounds.nonZero()) {
456     serialized_layout_bounds = nine_patch->SerializeLayoutBounds(&chunk_len);
457     strcpy((char*)unknown_chunks[index].name, "npLb");
458     unknown_chunks[index].size = chunk_len;
459     unknown_chunks[index].data = (png_bytep)serialized_layout_bounds.get();
460     unknown_chunks[index].location = PNG_HAVE_PLTE;
461     index++;
462   }
463 
464   std::unique_ptr<uint8_t[]> serialized_nine_patch = nine_patch->SerializeBase(&chunk_len);
465   strcpy((char*)unknown_chunks[index].name, "npTc");
466   unknown_chunks[index].size = chunk_len;
467   unknown_chunks[index].data = (png_bytep)serialized_nine_patch.get();
468   unknown_chunks[index].location = PNG_HAVE_PLTE;
469   index++;
470 
471   // Handle all unknown chunks. We are manually setting the chunks here,
472   // so we will only ever handle our custom chunks.
473   png_set_keep_unknown_chunks(write_ptr, PNG_HANDLE_CHUNK_ALWAYS, nullptr, 0);
474 
475   // Set the actual chunks here. The data gets copied, so our buffers can
476   // safely go out of scope.
477   png_set_unknown_chunks(write_ptr, write_info_ptr, unknown_chunks, index);
478 }
479 
WritePng(IAaptContext * context,const Image * image,const NinePatch * nine_patch,io::OutputStream * out,const PngOptions & options)480 bool WritePng(IAaptContext* context, const Image* image,
481               const NinePatch* nine_patch, io::OutputStream* out,
482               const PngOptions& options) {
483   // Create and initialize the write png_struct with the default error and
484   // warning handlers.
485   // The header version is also passed in to ensure that this was built against the same
486   // version of libpng.
487   png_structp write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
488   if (write_ptr == nullptr) {
489     context->GetDiagnostics()->Error(DiagMessage() << "failed to create libpng write png_struct");
490     return false;
491   }
492 
493   // Allocate memory to store image header data.
494   png_infop write_info_ptr = png_create_info_struct(write_ptr);
495   if (write_info_ptr == nullptr) {
496     context->GetDiagnostics()->Error(DiagMessage() << "failed to create libpng write png_info");
497     png_destroy_write_struct(&write_ptr, nullptr);
498     return false;
499   }
500 
501   // Automatically release PNG resources at end of scope.
502   PngWriteStructDeleter png_write_deleter(write_ptr, write_info_ptr);
503 
504   // libpng uses longjmp to jump to error handling routines.
505   // setjmp will return true only if it was jumped to, aka, there was an error.
506   if (setjmp(png_jmpbuf(write_ptr))) {
507     return false;
508   }
509 
510   // Handle warnings with our IDiagnostics.
511   png_set_error_fn(write_ptr, (png_voidp)context->GetDiagnostics(), LogError, LogWarning);
512 
513   // Set up the write functions which write to our custom data sources.
514   png_set_write_fn(write_ptr, (png_voidp)out, WriteDataToStream, nullptr);
515 
516   // We want small files and can take the performance hit to achieve this goal.
517   png_set_compression_level(write_ptr, Z_BEST_COMPRESSION);
518 
519   // Begin analysis of the image data.
520   // Scan the entire image and determine if:
521   // 1. Every pixel has R == G == B (grayscale)
522   // 2. Every pixel has A == 255 (opaque)
523   // 3. There are no more than 256 distinct RGBA colors (palette).
524   std::unordered_map<uint32_t, int> color_palette;
525   std::unordered_set<uint32_t> alpha_palette;
526   bool needs_to_zero_rgb_channels_of_transparent_pixels = false;
527   bool grayscale = true;
528   int max_gray_deviation = 0;
529 
530   for (int32_t y = 0; y < image->height; y++) {
531     const uint8_t* row = image->rows[y];
532     for (int32_t x = 0; x < image->width; x++) {
533       int red = *row++;
534       int green = *row++;
535       int blue = *row++;
536       int alpha = *row++;
537 
538       if (alpha == 0) {
539         // The color is completely transparent.
540         // For purposes of palettes and grayscale optimization,
541         // treat all channels as 0x00.
542         needs_to_zero_rgb_channels_of_transparent_pixels =
543             needs_to_zero_rgb_channels_of_transparent_pixels ||
544             (red != 0 || green != 0 || blue != 0);
545         red = green = blue = 0;
546       }
547 
548       // Insert the color into the color palette.
549       const uint32_t color = red << 24 | green << 16 | blue << 8 | alpha;
550       color_palette[color] = -1;
551 
552       // If the pixel has non-opaque alpha, insert it into the
553       // alpha palette.
554       if (alpha != 0xff) {
555         alpha_palette.insert(color);
556       }
557 
558       // Check if the image is indeed grayscale.
559       if (grayscale) {
560         if (red != green || red != blue) {
561           grayscale = false;
562         }
563       }
564 
565       // Calculate the gray scale deviation so that it can be compared
566       // with the threshold.
567       max_gray_deviation = std::max(std::abs(red - green), max_gray_deviation);
568       max_gray_deviation = std::max(std::abs(green - blue), max_gray_deviation);
569       max_gray_deviation = std::max(std::abs(blue - red), max_gray_deviation);
570     }
571   }
572 
573   if (context->IsVerbose()) {
574     DiagMessage msg;
575     msg << " paletteSize=" << color_palette.size()
576         << " alphaPaletteSize=" << alpha_palette.size()
577         << " maxGrayDeviation=" << max_gray_deviation
578         << " grayScale=" << (grayscale ? "true" : "false");
579     context->GetDiagnostics()->Note(msg);
580   }
581 
582   const bool convertible_to_grayscale = max_gray_deviation <= options.grayscale_tolerance;
583 
584   const int new_color_type = PickColorType(
585       image->width, image->height, grayscale, convertible_to_grayscale,
586       nine_patch != nullptr, color_palette.size(), alpha_palette.size());
587 
588   if (context->IsVerbose()) {
589     DiagMessage msg;
590     msg << "encoding PNG ";
591     if (nine_patch) {
592       msg << "(with 9-patch) as ";
593     }
594     switch (new_color_type) {
595       case PNG_COLOR_TYPE_GRAY:
596         msg << "GRAY";
597         break;
598       case PNG_COLOR_TYPE_GRAY_ALPHA:
599         msg << "GRAY + ALPHA";
600         break;
601       case PNG_COLOR_TYPE_RGB:
602         msg << "RGB";
603         break;
604       case PNG_COLOR_TYPE_RGB_ALPHA:
605         msg << "RGBA";
606         break;
607       case PNG_COLOR_TYPE_PALETTE:
608         msg << "PALETTE";
609         break;
610       default:
611         msg << "unknown type " << new_color_type;
612         break;
613     }
614     context->GetDiagnostics()->Note(msg);
615   }
616 
617   png_set_IHDR(write_ptr, write_info_ptr, image->width, image->height, 8,
618                new_color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
619                PNG_FILTER_TYPE_DEFAULT);
620 
621   if (new_color_type & PNG_COLOR_MASK_PALETTE) {
622     // Assigns indices to the palette, and writes the encoded palette to the
623     // libpng writePtr.
624     WritePalette(write_ptr, write_info_ptr, &color_palette, &alpha_palette);
625     png_set_filter(write_ptr, 0, PNG_NO_FILTERS);
626   } else {
627     png_set_filter(write_ptr, 0, PNG_ALL_FILTERS);
628   }
629 
630   if (nine_patch) {
631     WriteNinePatch(write_ptr, write_info_ptr, nine_patch);
632   }
633 
634   // Flush our updates to the header.
635   png_write_info(write_ptr, write_info_ptr);
636 
637   // Write out each row of image data according to its encoding.
638   if (new_color_type == PNG_COLOR_TYPE_PALETTE) {
639     // 1 byte/pixel.
640     auto out_row = std::unique_ptr<png_byte[]>(new png_byte[image->width]);
641 
642     for (int32_t y = 0; y < image->height; y++) {
643       png_const_bytep in_row = image->rows[y];
644       for (int32_t x = 0; x < image->width; x++) {
645         int rr = *in_row++;
646         int gg = *in_row++;
647         int bb = *in_row++;
648         int aa = *in_row++;
649         if (aa == 0) {
650           // Zero out color channels when transparent.
651           rr = gg = bb = 0;
652         }
653 
654         const uint32_t color = rr << 24 | gg << 16 | bb << 8 | aa;
655         const int idx = color_palette[color];
656         CHECK(idx != -1);
657         out_row[x] = static_cast<png_byte>(idx);
658       }
659       png_write_row(write_ptr, out_row.get());
660     }
661   } else if (new_color_type == PNG_COLOR_TYPE_GRAY ||
662              new_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
663     const size_t bpp = new_color_type == PNG_COLOR_TYPE_GRAY ? 1 : 2;
664     auto out_row =
665         std::unique_ptr<png_byte[]>(new png_byte[image->width * bpp]);
666 
667     for (int32_t y = 0; y < image->height; y++) {
668       png_const_bytep in_row = image->rows[y];
669       for (int32_t x = 0; x < image->width; x++) {
670         int rr = in_row[x * 4];
671         int gg = in_row[x * 4 + 1];
672         int bb = in_row[x * 4 + 2];
673         int aa = in_row[x * 4 + 3];
674         if (aa == 0) {
675           // Zero out the gray channel when transparent.
676           rr = gg = bb = 0;
677         }
678 
679         if (grayscale) {
680           // The image was already grayscale, red == green == blue.
681           out_row[x * bpp] = in_row[x * 4];
682         } else {
683           // The image is convertible to grayscale, use linear-luminance of
684           // sRGB colorspace:
685           // https://en.wikipedia.org/wiki/Grayscale#Colorimetric_.28luminance-preserving.29_conversion_to_grayscale
686           out_row[x * bpp] =
687               (png_byte)(rr * 0.2126f + gg * 0.7152f + bb * 0.0722f);
688         }
689 
690         if (bpp == 2) {
691           // Write out alpha if we have it.
692           out_row[x * bpp + 1] = aa;
693         }
694       }
695       png_write_row(write_ptr, out_row.get());
696     }
697   } else if (new_color_type == PNG_COLOR_TYPE_RGB || new_color_type == PNG_COLOR_TYPE_RGBA) {
698     const size_t bpp = new_color_type == PNG_COLOR_TYPE_RGB ? 3 : 4;
699     if (needs_to_zero_rgb_channels_of_transparent_pixels) {
700       // The source RGBA data can't be used as-is, because we need to zero out
701       // the RGB values of transparent pixels.
702       auto out_row = std::unique_ptr<png_byte[]>(new png_byte[image->width * bpp]);
703 
704       for (int32_t y = 0; y < image->height; y++) {
705         png_const_bytep in_row = image->rows[y];
706         for (int32_t x = 0; x < image->width; x++) {
707           int rr = *in_row++;
708           int gg = *in_row++;
709           int bb = *in_row++;
710           int aa = *in_row++;
711           if (aa == 0) {
712             // Zero out the RGB channels when transparent.
713             rr = gg = bb = 0;
714           }
715           out_row[x * bpp] = rr;
716           out_row[x * bpp + 1] = gg;
717           out_row[x * bpp + 2] = bb;
718           if (bpp == 4) {
719             out_row[x * bpp + 3] = aa;
720           }
721         }
722         png_write_row(write_ptr, out_row.get());
723       }
724     } else {
725       // The source image can be used as-is, just tell libpng whether or not to
726       // ignore the alpha channel.
727       if (new_color_type == PNG_COLOR_TYPE_RGB) {
728         // Delete the extraneous alpha values that we appended to our buffer
729         // when reading the original values.
730         png_set_filler(write_ptr, 0, PNG_FILLER_AFTER);
731       }
732       png_write_image(write_ptr, image->rows.get());
733     }
734   } else {
735     LOG(FATAL) << "unreachable";
736   }
737 
738   png_write_end(write_ptr, write_info_ptr);
739   return true;
740 }
741 
742 }  // namespace aapt
743