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