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