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