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