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