PngCrunch.cpp revision cacb28f2d60858106e2819cc7d95a65e8bda890b
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 <android-base/errors.h>
2021efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski#include <android-base/macros.h>
2121efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski#include <png.h>
22cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski#include <zlib.h>
23cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski#include <algorithm>
2421efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski#include <unordered_map>
2521efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski#include <unordered_set>
2621efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski
2721efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinskinamespace aapt {
2821efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski
2921efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski// Size in bytes of the PNG signature.
3021efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinskiconstexpr size_t kPngSignatureSize = 8u;
3121efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski
3221efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski/**
3321efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski * Custom deleter that destroys libpng read and info structs.
3421efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski */
3521efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinskiclass PngReadStructDeleter {
36cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski public:
37cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  explicit PngReadStructDeleter(png_structp readPtr, png_infop infoPtr)
38cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      : mReadPtr(readPtr), mInfoPtr(infoPtr) {}
3921efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski
40cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ~PngReadStructDeleter() {
41cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    png_destroy_read_struct(&mReadPtr, &mInfoPtr, nullptr);
42cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
4321efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski
44cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski private:
45cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  png_structp mReadPtr;
46cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  png_infop mInfoPtr;
4721efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski
48cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  DISALLOW_COPY_AND_ASSIGN(PngReadStructDeleter);
4921efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski};
5021efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski
5121efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski/**
5221efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski * Custom deleter that destroys libpng write and info structs.
5321efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski */
5421efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinskiclass PngWriteStructDeleter {
55cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski public:
56cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  explicit PngWriteStructDeleter(png_structp writePtr, png_infop infoPtr)
57cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      : mWritePtr(writePtr), mInfoPtr(infoPtr) {}
5821efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski
59cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  ~PngWriteStructDeleter() { png_destroy_write_struct(&mWritePtr, &mInfoPtr); }
6021efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski
61cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski private:
62cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  png_structp mWritePtr;
63cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  png_infop mInfoPtr;
6421efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski
65cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  DISALLOW_COPY_AND_ASSIGN(PngWriteStructDeleter);
6621efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski};
6721efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski
6821efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski// Custom warning logging method that uses IDiagnostics.
6921efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinskistatic void logWarning(png_structp pngPtr, png_const_charp warningMsg) {
70cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  IDiagnostics* diag = (IDiagnostics*)png_get_error_ptr(pngPtr);
71cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  diag->warn(DiagMessage() << warningMsg);
7221efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski}
7321efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski
7421efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski// Custom error logging method that uses IDiagnostics.
7521efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinskistatic void logError(png_structp pngPtr, png_const_charp errorMsg) {
76cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  IDiagnostics* diag = (IDiagnostics*)png_get_error_ptr(pngPtr);
77cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  diag->error(DiagMessage() << errorMsg);
7821efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski}
7921efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski
80cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinskistatic void readDataFromStream(png_structp pngPtr, png_bytep buffer,
81cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                               png_size_t len) {
82cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  io::InputStream* in = (io::InputStream*)png_get_io_ptr(pngPtr);
83cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
84cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  const void* inBuffer;
85cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  int inLen;
86cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (!in->Next(&inBuffer, &inLen)) {
87cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (in->HadError()) {
88cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      std::string err = in->GetError();
89cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      png_error(pngPtr, err.c_str());
90cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
91cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return;
92cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
93cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
94cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  const size_t bytesRead = std::min(static_cast<size_t>(inLen), len);
95cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  memcpy(buffer, inBuffer, bytesRead);
96cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (bytesRead != static_cast<size_t>(inLen)) {
97cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    in->BackUp(inLen - static_cast<int>(bytesRead));
98cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
99cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski}
10021efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski
101cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinskistatic void writeDataToStream(png_structp pngPtr, png_bytep buffer,
102cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                              png_size_t len) {
103cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  io::OutputStream* out = (io::OutputStream*)png_get_io_ptr(pngPtr);
10421efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski
105cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  void* outBuffer;
106cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  int outLen;
107cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  while (len > 0) {
108cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (!out->Next(&outBuffer, &outLen)) {
109cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      if (out->HadError()) {
110cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        std::string err = out->GetError();
111cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        png_error(pngPtr, err.c_str());
112cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
113cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return;
11421efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski    }
11521efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski
116cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    const size_t bytesWritten = std::min(static_cast<size_t>(outLen), len);
117cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    memcpy(outBuffer, buffer, bytesWritten);
11821efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski
119cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Advance the input buffer.
120cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    buffer += bytesWritten;
121cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    len -= bytesWritten;
12221efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski
123cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Advance the output buffer.
124cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    outLen -= static_cast<int>(bytesWritten);
125cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
12621efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski
127cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // If the entire output buffer wasn't used, backup.
128cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (outLen > 0) {
129cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    out->BackUp(outLen);
130cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
13121efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski}
13221efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski
13321efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinskistd::unique_ptr<Image> readPng(IAaptContext* context, io::InputStream* in) {
134cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Read the first 8 bytes of the file looking for the PNG signature.
135cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Bail early if it does not match.
136cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  const png_byte* signature;
137cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  int bufferSize;
138cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (!in->Next((const void**)&signature, &bufferSize)) {
139cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    context->getDiagnostics()->error(
140cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        DiagMessage() << android::base::SystemErrorCodeToString(errno));
141cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return {};
142cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
143cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
144cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (static_cast<size_t>(bufferSize) < kPngSignatureSize ||
145cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      png_sig_cmp(signature, 0, kPngSignatureSize) != 0) {
146cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    context->getDiagnostics()->error(
147cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        DiagMessage() << "file signature does not match PNG signature");
148cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return {};
149cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
150cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
151cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Start at the beginning of the first chunk.
152cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  in->BackUp(bufferSize - static_cast<int>(kPngSignatureSize));
153cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
154cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Create and initialize the png_struct with the default error and warning
155cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // handlers.
156cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // The header version is also passed in to ensure that this was built against
157cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // the same
158cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // version of libpng.
159cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  png_structp readPtr =
160cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
161cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (readPtr == nullptr) {
162cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    context->getDiagnostics()->error(
163cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        DiagMessage() << "failed to create libpng read png_struct");
164cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return {};
165cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
166cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
167cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Create and initialize the memory for image header and data.
168cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  png_infop infoPtr = png_create_info_struct(readPtr);
169cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (infoPtr == nullptr) {
170cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    context->getDiagnostics()->error(
171cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        DiagMessage() << "failed to create libpng read png_info");
172cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    png_destroy_read_struct(&readPtr, nullptr, nullptr);
173cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return {};
174cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
175cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
176cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Automatically release PNG resources at end of scope.
177cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  PngReadStructDeleter pngReadDeleter(readPtr, infoPtr);
178cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
179cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // libpng uses longjmp to jump to an error handling routine.
180cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // setjmp will only return true if it was jumped to, aka there was
181cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // an error.
182cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (setjmp(png_jmpbuf(readPtr))) {
183cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return {};
184cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
185cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
186cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Handle warnings ourselves via IDiagnostics.
187cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  png_set_error_fn(readPtr, (png_voidp)context->getDiagnostics(), logError,
188cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                   logWarning);
189cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
190cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Set up the read functions which read from our custom data sources.
191cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  png_set_read_fn(readPtr, (png_voidp)in, readDataFromStream);
192cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
193cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Skip the signature that we already read.
194cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  png_set_sig_bytes(readPtr, kPngSignatureSize);
195cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
196cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Read the chunk headers.
197cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  png_read_info(readPtr, infoPtr);
198cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
199cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Extract image meta-data from the various chunk headers.
200cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  uint32_t width, height;
201cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  int bitDepth, colorType, interlaceMethod, compressionMethod, filterMethod;
202cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  png_get_IHDR(readPtr, infoPtr, &width, &height, &bitDepth, &colorType,
203cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski               &interlaceMethod, &compressionMethod, &filterMethod);
204cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
205cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // When the image is read, expand it so that it is in RGBA 8888 format
206cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // so that image handling is uniform.
207cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
208cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (colorType == PNG_COLOR_TYPE_PALETTE) {
209cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    png_set_palette_to_rgb(readPtr);
210cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
211cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
212cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) {
213cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    png_set_expand_gray_1_2_4_to_8(readPtr);
214cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
215cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
216cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (png_get_valid(readPtr, infoPtr, PNG_INFO_tRNS)) {
217cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    png_set_tRNS_to_alpha(readPtr);
218cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
219cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
220cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (bitDepth == 16) {
221cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    png_set_strip_16(readPtr);
222cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
223cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
224cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (!(colorType & PNG_COLOR_MASK_ALPHA)) {
225cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    png_set_add_alpha(readPtr, 0xFF, PNG_FILLER_AFTER);
226cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
227cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
228cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (colorType == PNG_COLOR_TYPE_GRAY ||
229cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
230cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    png_set_gray_to_rgb(readPtr);
231cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
232cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
233cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (interlaceMethod != PNG_INTERLACE_NONE) {
234cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    png_set_interlace_handling(readPtr);
235cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
236cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
237cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Once all the options for reading have been set, we need to flush
238cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // them to libpng.
239cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  png_read_update_info(readPtr, infoPtr);
240cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
241cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // 9-patch uses int32_t to index images, so we cap the image dimensions to
242cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // something
243cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // that can always be represented by 9-patch.
244cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (width > std::numeric_limits<int32_t>::max() ||
245cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      height > std::numeric_limits<int32_t>::max()) {
246cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    context->getDiagnostics()->error(DiagMessage()
247cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                                     << "PNG image dimensions are too large: "
248cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                                     << width << "x" << height);
249cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return {};
250cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
251cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
252cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::unique_ptr<Image> outputImage = util::make_unique<Image>();
253cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  outputImage->width = static_cast<int32_t>(width);
254cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  outputImage->height = static_cast<int32_t>(height);
255cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
256cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  const size_t rowBytes = png_get_rowbytes(readPtr, infoPtr);
257cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  assert(rowBytes == 4 * width);  // RGBA
258cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
259cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Allocate one large block to hold the image.
260cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  outputImage->data =
261cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      std::unique_ptr<uint8_t[]>(new uint8_t[height * rowBytes]);
262cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
263cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Create an array of rows that index into the data block.
264cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  outputImage->rows = std::unique_ptr<uint8_t* []>(new uint8_t*[height]);
265cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  for (uint32_t h = 0; h < height; h++) {
266cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    outputImage->rows[h] = outputImage->data.get() + (h * rowBytes);
267cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
268cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
269cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Actually read the image pixels.
270cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  png_read_image(readPtr, outputImage->rows.get());
271cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
272cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Finish reading. This will read any other chunks after the image data.
273cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  png_read_end(readPtr, infoPtr);
274cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
275cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return outputImage;
27621efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski}
27721efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski
27821efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski/**
279cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski * Experimentally chosen constant to be added to the overhead of using color
280cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski * type
281cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski * PNG_COLOR_TYPE_PALETTE to account for the uncompressability of the palette
282cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski * chunk.
283cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski * Without this, many small PNGs encoded with palettes are larger after
284cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski * compression than
28521efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski * the same PNGs encoded as RGBA.
28621efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski */
28721efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinskiconstexpr static const size_t kPaletteOverheadConstant = 1024u * 10u;
28821efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski
289cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski// Pick a color type by which to encode the image, based on which color type
290cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski// 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//
312cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinskistatic int pickColorType(int32_t width, int32_t height, bool grayScale,
313cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                         bool convertibleToGrayScale, bool hasNinePatch,
31421efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski                         size_t colorPaletteSize, size_t alphaPaletteSize) {
315cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  const size_t paletteChunkSize = 16 + colorPaletteSize * 3;
316cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  const size_t alphaChunkSize = 16 + alphaPaletteSize;
317cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  const size_t colorAlphaDataChunkSize = 16 + 4 * width * height;
318cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  const size_t colorDataChunkSize = 16 + 3 * width * height;
319cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  const size_t grayScaleAlphaDataChunkSize = 16 + 2 * width * height;
320cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  const size_t paletteDataChunkSize = 16 + width * height;
321cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
322cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (grayScale) {
323cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (alphaPaletteSize == 0) {
324cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // This is the smallest the data can be.
325cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return PNG_COLOR_TYPE_GRAY;
326cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    } else if (colorPaletteSize <= 256 && !hasNinePatch) {
327cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // This grayscale has alpha and can fit within a palette.
328cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // See if it is worth fitting into a palette.
329cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      const size_t paletteThreshold = paletteChunkSize + alphaChunkSize +
330cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                                      paletteDataChunkSize +
331cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                                      kPaletteOverheadConstant;
332cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      if (grayScaleAlphaDataChunkSize > paletteThreshold) {
333cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        return PNG_COLOR_TYPE_PALETTE;
334cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
335cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
336cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return PNG_COLOR_TYPE_GRAY_ALPHA;
337cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
338cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
339cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (colorPaletteSize <= 256 && !hasNinePatch) {
340cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // This image can fit inside a palette. Let's see if it is worth it.
341cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    size_t totalSizeWithPalette = paletteDataChunkSize + paletteChunkSize;
342cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    size_t totalSizeWithoutPalette = colorDataChunkSize;
343cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (alphaPaletteSize > 0) {
344cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      totalSizeWithPalette += alphaPaletteSize;
345cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      totalSizeWithoutPalette = colorAlphaDataChunkSize;
346cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
347cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
348cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (totalSizeWithoutPalette >
349cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        totalSizeWithPalette + kPaletteOverheadConstant) {
350cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return PNG_COLOR_TYPE_PALETTE;
351cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
352cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
353cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
354cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (convertibleToGrayScale) {
35521efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski    if (alphaPaletteSize == 0) {
356cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return PNG_COLOR_TYPE_GRAY;
357cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    } else {
358cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      return PNG_COLOR_TYPE_GRAY_ALPHA;
35921efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski    }
360cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
361cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
362cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (alphaPaletteSize == 0) {
363cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return PNG_COLOR_TYPE_RGB;
364cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
365cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return PNG_COLOR_TYPE_RGBA;
36621efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski}
36721efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski
368cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski// Assigns indices to the color and alpha palettes, encodes them, and then
369cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski// invokes
37021efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski// png_set_PLTE/png_set_tRNS.
37121efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski// This must be done before writing image data.
372cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski// Image data must be transformed to use the indices assigned within the
373cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski// palette.
37421efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinskistatic void writePalette(png_structp writePtr, png_infop writeInfoPtr,
375cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                         std::unordered_map<uint32_t, int>* colorPalette,
376cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                         std::unordered_set<uint32_t>* alphaPalette) {
377cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  assert(colorPalette->size() <= 256);
378cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  assert(alphaPalette->size() <= 256);
379cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
380cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Populate the PNG palette struct and assign indices to the color
381cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // palette.
382cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
383cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Colors in the alpha palette should have smaller indices.
384cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // This will ensure that we can truncate the alpha palette if it is
385cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // smaller than the color palette.
386cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  int index = 0;
387cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  for (uint32_t color : *alphaPalette) {
388cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    (*colorPalette)[color] = index++;
389cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
390cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
391cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Assign the rest of the entries.
392cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  for (auto& entry : *colorPalette) {
393cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (entry.second == -1) {
394cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      entry.second = index++;
395cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
396cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
397cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
398cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Create the PNG color palette struct.
399cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  auto colorPaletteBytes =
400cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      std::unique_ptr<png_color[]>(new png_color[colorPalette->size()]);
401cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
402cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::unique_ptr<png_byte[]> alphaPaletteBytes;
403cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (!alphaPalette->empty()) {
404cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    alphaPaletteBytes =
405cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        std::unique_ptr<png_byte[]>(new png_byte[alphaPalette->size()]);
406cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
407cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
408cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  for (const auto& entry : *colorPalette) {
409cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    const uint32_t color = entry.first;
410cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    const int index = entry.second;
411cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    assert(index >= 0);
412cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    assert(static_cast<size_t>(index) < colorPalette->size());
413cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
414cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    png_colorp slot = colorPaletteBytes.get() + index;
415cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    slot->red = color >> 24;
416cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    slot->green = color >> 16;
417cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    slot->blue = color >> 8;
418cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
419cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    const png_byte alpha = color & 0x000000ff;
420cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (alpha != 0xff && alphaPaletteBytes) {
421cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      assert(static_cast<size_t>(index) < alphaPalette->size());
422cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      alphaPaletteBytes[index] = alpha;
423cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
424cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
425cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
426cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // The bytes get copied here, so it is safe to release colorPaletteBytes at
427cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // the end of function
428cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // scope.
429cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  png_set_PLTE(writePtr, writeInfoPtr, colorPaletteBytes.get(),
430cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski               colorPalette->size());
431cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
432cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (alphaPaletteBytes) {
433cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    png_set_tRNS(writePtr, writeInfoPtr, alphaPaletteBytes.get(),
434cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                 alphaPalette->size(), nullptr);
435cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
43621efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski}
43721efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski
43821efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski// Write the 9-patch custom PNG chunks to writeInfoPtr. This must be done before
43921efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski// writing image data.
44021efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinskistatic void writeNinePatch(png_structp writePtr, png_infop writeInfoPtr,
44121efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski                           const NinePatch* ninePatch) {
442cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // The order of the chunks is important.
443cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // 9-patch code in older platforms expects the 9-patch chunk to
444cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // be last.
445cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
446cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  png_unknown_chunk unknownChunks[3];
447cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  memset(unknownChunks, 0, sizeof(unknownChunks));
448cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
449cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  size_t index = 0;
450cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  size_t chunkLen = 0;
451cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
452cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::unique_ptr<uint8_t[]> serializedOutline =
453cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      ninePatch->serializeRoundedRectOutline(&chunkLen);
454cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  strcpy((char*)unknownChunks[index].name, "npOl");
455cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  unknownChunks[index].size = chunkLen;
456cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  unknownChunks[index].data = (png_bytep)serializedOutline.get();
457cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  unknownChunks[index].location = PNG_HAVE_PLTE;
458cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  index++;
459cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
460cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::unique_ptr<uint8_t[]> serializedLayoutBounds;
461cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (ninePatch->layoutBounds.nonZero()) {
462cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    serializedLayoutBounds = ninePatch->serializeLayoutBounds(&chunkLen);
463cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    strcpy((char*)unknownChunks[index].name, "npLb");
46421efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski    unknownChunks[index].size = chunkLen;
465cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    unknownChunks[index].data = (png_bytep)serializedLayoutBounds.get();
46621efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski    unknownChunks[index].location = PNG_HAVE_PLTE;
46721efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski    index++;
468cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
469cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
470cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::unique_ptr<uint8_t[]> serializedNinePatch =
471cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      ninePatch->serializeBase(&chunkLen);
472cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  strcpy((char*)unknownChunks[index].name, "npTc");
473cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  unknownChunks[index].size = chunkLen;
474cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  unknownChunks[index].data = (png_bytep)serializedNinePatch.get();
475cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  unknownChunks[index].location = PNG_HAVE_PLTE;
476cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  index++;
477cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
478cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Handle all unknown chunks. We are manually setting the chunks here,
479cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // so we will only ever handle our custom chunks.
480cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  png_set_keep_unknown_chunks(writePtr, PNG_HANDLE_CHUNK_ALWAYS, nullptr, 0);
481cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
482cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Set the actual chunks here. The data gets copied, so our buffers can
483cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // safely go out of scope.
484cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  png_set_unknown_chunks(writePtr, writeInfoPtr, unknownChunks, index);
48521efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski}
48621efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski
487cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinskibool writePng(IAaptContext* context, const Image* image,
488cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski              const NinePatch* ninePatch, io::OutputStream* out,
489cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski              const PngOptions& options) {
490cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Create and initialize the write png_struct with the default error and
491cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // warning handlers.
492cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // The header version is also passed in to ensure that this was built against
493cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // the same
494cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // version of libpng.
495cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  png_structp writePtr =
496cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
497cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (writePtr == nullptr) {
498cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    context->getDiagnostics()->error(
499cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        DiagMessage() << "failed to create libpng write png_struct");
500cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
501cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
502cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
503cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Allocate memory to store image header data.
504cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  png_infop writeInfoPtr = png_create_info_struct(writePtr);
505cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (writeInfoPtr == nullptr) {
506cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    context->getDiagnostics()->error(
507cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        DiagMessage() << "failed to create libpng write png_info");
508cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    png_destroy_write_struct(&writePtr, nullptr);
509cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
510cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
511cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
512cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Automatically release PNG resources at end of scope.
513cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  PngWriteStructDeleter pngWriteDeleter(writePtr, writeInfoPtr);
514cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
515cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // libpng uses longjmp to jump to error handling routines.
516cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // setjmp will return true only if it was jumped to, aka, there was an error.
517cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (setjmp(png_jmpbuf(writePtr))) {
518cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return false;
519cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
520cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
521cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Handle warnings with our IDiagnostics.
522cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  png_set_error_fn(writePtr, (png_voidp)context->getDiagnostics(), logError,
523cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                   logWarning);
524cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
525cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Set up the write functions which write to our custom data sources.
526cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  png_set_write_fn(writePtr, (png_voidp)out, writeDataToStream, nullptr);
527cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
528cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // We want small files and can take the performance hit to achieve this goal.
529cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  png_set_compression_level(writePtr, Z_BEST_COMPRESSION);
530cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
531cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Begin analysis of the image data.
532cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Scan the entire image and determine if:
533cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // 1. Every pixel has R == G == B (grayscale)
534cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // 2. Every pixel has A == 255 (opaque)
535cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // 3. There are no more than 256 distinct RGBA colors (palette).
536cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::unordered_map<uint32_t, int> colorPalette;
537cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  std::unordered_set<uint32_t> alphaPalette;
538cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  bool needsToZeroRGBChannelsOfTransparentPixels = false;
539cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  bool grayScale = true;
540cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  int maxGrayDeviation = 0;
541cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
542cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  for (int32_t y = 0; y < image->height; y++) {
543cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    const uint8_t* row = image->rows[y];
544cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    for (int32_t x = 0; x < image->width; x++) {
545cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      int red = *row++;
546cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      int green = *row++;
547cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      int blue = *row++;
548cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      int alpha = *row++;
549cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
550cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      if (alpha == 0) {
551cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        // The color is completely transparent.
552cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        // For purposes of palettes and grayscale optimization,
553cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        // treat all channels as 0x00.
554cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        needsToZeroRGBChannelsOfTransparentPixels =
555cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            needsToZeroRGBChannelsOfTransparentPixels ||
556cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            (red != 0 || green != 0 || blue != 0);
557cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        red = green = blue = 0;
558cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
559cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
560cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // Insert the color into the color palette.
561cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      const uint32_t color = red << 24 | green << 16 | blue << 8 | alpha;
562cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      colorPalette[color] = -1;
563cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
564cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // If the pixel has non-opaque alpha, insert it into the
565cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // alpha palette.
566cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      if (alpha != 0xff) {
567cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        alphaPalette.insert(color);
568cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
569cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
570cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // Check if the image is indeed grayscale.
571cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      if (grayScale) {
572cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        if (red != green || red != blue) {
573cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          grayScale = false;
574cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        }
575cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
576cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
577cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // Calculate the gray scale deviation so that it can be compared
578cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // with the threshold.
579cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      maxGrayDeviation = std::max(std::abs(red - green), maxGrayDeviation);
580cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      maxGrayDeviation = std::max(std::abs(green - blue), maxGrayDeviation);
581cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      maxGrayDeviation = std::max(std::abs(blue - red), maxGrayDeviation);
582cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
583cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
584cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
585cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (context->verbose()) {
586cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    DiagMessage msg;
587cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    msg << " paletteSize=" << colorPalette.size()
588cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        << " alphaPaletteSize=" << alphaPalette.size()
589cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        << " maxGrayDeviation=" << maxGrayDeviation
590cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        << " grayScale=" << (grayScale ? "true" : "false");
591cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    context->getDiagnostics()->note(msg);
592cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
593cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
594cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  const bool convertibleToGrayScale =
595cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      maxGrayDeviation <= options.grayScaleTolerance;
596cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
597cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  const int newColorType = pickColorType(
598cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      image->width, image->height, grayScale, convertibleToGrayScale,
599cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      ninePatch != nullptr, colorPalette.size(), alphaPalette.size());
600cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
601cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (context->verbose()) {
602cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    DiagMessage msg;
603cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    msg << "encoding PNG ";
604cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (ninePatch) {
605cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      msg << "(with 9-patch) as ";
606cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
607cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    switch (newColorType) {
608cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      case PNG_COLOR_TYPE_GRAY:
609cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        msg << "GRAY";
610cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        break;
611cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      case PNG_COLOR_TYPE_GRAY_ALPHA:
612cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        msg << "GRAY + ALPHA";
613cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        break;
614cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      case PNG_COLOR_TYPE_RGB:
615cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        msg << "RGB";
616cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        break;
617cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      case PNG_COLOR_TYPE_RGB_ALPHA:
618cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        msg << "RGBA";
619cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        break;
620cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      case PNG_COLOR_TYPE_PALETTE:
621cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        msg << "PALETTE";
622cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        break;
623cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      default:
624cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        msg << "unknown type " << newColorType;
625cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        break;
626cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
627cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    context->getDiagnostics()->note(msg);
628cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
629cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
630cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  png_set_IHDR(writePtr, writeInfoPtr, image->width, image->height, 8,
631cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski               newColorType, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
632cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski               PNG_FILTER_TYPE_DEFAULT);
633cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
634cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (newColorType & PNG_COLOR_MASK_PALETTE) {
635cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Assigns indices to the palette, and writes the encoded palette to the
636cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // libpng writePtr.
637cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    writePalette(writePtr, writeInfoPtr, &colorPalette, &alphaPalette);
638cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    png_set_filter(writePtr, 0, PNG_NO_FILTERS);
639cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  } else {
640cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    png_set_filter(writePtr, 0, PNG_ALL_FILTERS);
641cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
642cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
643cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (ninePatch) {
644cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    writeNinePatch(writePtr, writeInfoPtr, ninePatch);
645cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
646cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
647cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Flush our updates to the header.
648cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  png_write_info(writePtr, writeInfoPtr);
649cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
650cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Write out each row of image data according to its encoding.
651cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (newColorType == PNG_COLOR_TYPE_PALETTE) {
652cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // 1 byte/pixel.
653cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    auto outRow = std::unique_ptr<png_byte[]>(new png_byte[image->width]);
65421efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski
65521efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski    for (int32_t y = 0; y < image->height; y++) {
656cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      png_const_bytep inRow = image->rows[y];
657cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      for (int32_t x = 0; x < image->width; x++) {
658cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        int rr = *inRow++;
659cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        int gg = *inRow++;
660cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        int bb = *inRow++;
661cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        int aa = *inRow++;
662cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        if (aa == 0) {
663cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          // Zero out color channels when transparent.
664cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          rr = gg = bb = 0;
66521efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski        }
66621efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski
667cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        const uint32_t color = rr << 24 | gg << 16 | bb << 8 | aa;
668cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        const int idx = colorPalette[color];
669cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        assert(idx != -1);
670cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        outRow[x] = static_cast<png_byte>(idx);
671cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
672cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      png_write_row(writePtr, outRow.get());
67321efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski    }
674cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  } else if (newColorType == PNG_COLOR_TYPE_GRAY ||
675cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski             newColorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
676cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    const size_t bpp = newColorType == PNG_COLOR_TYPE_GRAY ? 1 : 2;
677cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    auto outRow = std::unique_ptr<png_byte[]>(new png_byte[image->width * bpp]);
67821efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski
679cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    for (int32_t y = 0; y < image->height; y++) {
680cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      png_const_bytep inRow = image->rows[y];
681cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      for (int32_t x = 0; x < image->width; x++) {
682cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        int rr = inRow[x * 4];
683cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        int gg = inRow[x * 4 + 1];
684cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        int bb = inRow[x * 4 + 2];
685cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        int aa = inRow[x * 4 + 3];
686cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        if (aa == 0) {
687cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          // Zero out the gray channel when transparent.
688cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          rr = gg = bb = 0;
68921efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski        }
69021efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski
691cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        if (grayScale) {
692cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          // The image was already grayscale, red == green == blue.
693cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          outRow[x * bpp] = inRow[x * 4];
694cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        } else {
695cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          // The image is convertible to grayscale, use linear-luminance of
696cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          // sRGB colorspace:
697cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          // https://en.wikipedia.org/wiki/Grayscale#Colorimetric_.28luminance-preserving.29_conversion_to_grayscale
698cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          outRow[x * bpp] =
699cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski              (png_byte)(rr * 0.2126f + gg * 0.7152f + bb * 0.0722f);
70021efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski        }
701cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
702cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        if (bpp == 2) {
703cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          // Write out alpha if we have it.
704cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          outRow[x * bpp + 1] = aa;
70521efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski        }
706cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
707cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      png_write_row(writePtr, outRow.get());
708cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
709cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  } else if (newColorType == PNG_COLOR_TYPE_RGB ||
710cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski             newColorType == PNG_COLOR_TYPE_RGBA) {
711cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    const size_t bpp = newColorType == PNG_COLOR_TYPE_RGB ? 3 : 4;
712cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (needsToZeroRGBChannelsOfTransparentPixels) {
713cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // The source RGBA data can't be used as-is, because we need to zero out
714cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // the RGB
715cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // values of transparent pixels.
716cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      auto outRow =
717cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          std::unique_ptr<png_byte[]>(new png_byte[image->width * bpp]);
718cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
719cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      for (int32_t y = 0; y < image->height; y++) {
720cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        png_const_bytep inRow = image->rows[y];
721cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        for (int32_t x = 0; x < image->width; x++) {
722cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          int rr = *inRow++;
723cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          int gg = *inRow++;
724cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          int bb = *inRow++;
725cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          int aa = *inRow++;
726cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          if (aa == 0) {
727cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            // Zero out the RGB channels when transparent.
728cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            rr = gg = bb = 0;
729cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          }
730cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          outRow[x * bpp] = rr;
731cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          outRow[x * bpp + 1] = gg;
732cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          outRow[x * bpp + 2] = bb;
733cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          if (bpp == 4) {
734cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski            outRow[x * bpp + 3] = aa;
735cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          }
73621efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski        }
737cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        png_write_row(writePtr, outRow.get());
738cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
73921efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski    } else {
740cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // The source image can be used as-is, just tell libpng whether or not to
741cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // ignore
742cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // the alpha channel.
743cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      if (newColorType == PNG_COLOR_TYPE_RGB) {
744cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        // Delete the extraneous alpha values that we appended to our buffer
745cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        // when reading the original values.
746cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        png_set_filler(writePtr, 0, PNG_FILLER_AFTER);
747cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
748cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      png_write_image(writePtr, image->rows.get());
749cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
750cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  } else {
751cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    assert(false && "unreachable");
752cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
753cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
754cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  png_write_end(writePtr, writeInfoPtr);
755cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return true;
75621efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski}
75721efb6827cede06c2ab708de6cdb64d052dddcceAdam Lesinski
758cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski}  // namespace aapt
759