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
30namespace aapt {
31
32// Custom deleter that destroys libpng read and info structs.
33class PngReadStructDeleter {
34 public:
35  PngReadStructDeleter(png_structp read_ptr, png_infop info_ptr)
36      : read_ptr_(read_ptr), info_ptr_(info_ptr) {}
37
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.
50class PngWriteStructDeleter {
51 public:
52  PngWriteStructDeleter(png_structp write_ptr, png_infop info_ptr)
53      : write_ptr_(write_ptr), info_ptr_(info_ptr) {}
54
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.
67static 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.
73static 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
83static 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
108static 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
144std::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.
288constexpr 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//
312static 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.
373static 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.
435static 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
480bool 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