Png.cpp revision 98aa3ad6e46e3b0270785c8b3f9798e37e8af140
198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski/* 298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski * Copyright (C) 2015 The Android Open Source Project 398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski * 498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski * Licensed under the Apache License, Version 2.0 (the "License"); 598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski * you may not use this file except in compliance with the License. 698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski * You may obtain a copy of the License at 798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski * 898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski * http://www.apache.org/licenses/LICENSE-2.0 998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski * 1098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski * Unless required by applicable law or agreed to in writing, software 1198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski * distributed under the License is distributed on an "AS IS" BASIS, 1298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski * See the License for the specific language governing permissions and 1498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski * limitations under the License. 1598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski */ 1698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 1798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski#include "Logger.h" 1898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski#include "Png.h" 1998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski#include "Source.h" 2098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski#include "Util.h" 2198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 2298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski#include <androidfw/ResourceTypes.h> 2398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski#include <iostream> 2498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski#include <png.h> 2598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski#include <sstream> 2698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski#include <string> 2798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski#include <vector> 2898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski#include <zlib.h> 2998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 3098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskinamespace aapt { 3198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 3298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskiconstexpr bool kDebug = false; 3398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskiconstexpr size_t kPngSignatureSize = 8u; 3498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 3598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskistruct PngInfo { 3698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski ~PngInfo() { 3798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski for (png_bytep row : rows) { 3898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (row != nullptr) { 3998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski delete[] row; 4098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 4198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 4298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 4398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski delete[] xDivs; 4498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski delete[] yDivs; 4598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 4698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 4798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski void* serialize9Patch() { 4898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski void* serialized = android::Res_png_9patch::serialize(info9Patch, xDivs, yDivs, 4998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski colors.data()); 5098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski reinterpret_cast<android::Res_png_9patch*>(serialized)->deviceToFile(); 5198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return serialized; 5298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 5398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 5498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski uint32_t width = 0; 5598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski uint32_t height = 0; 5698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski std::vector<png_bytep> rows; 5798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 5898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski bool is9Patch = false; 5998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski android::Res_png_9patch info9Patch; 6098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int32_t* xDivs = nullptr; 6198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int32_t* yDivs = nullptr; 6298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski std::vector<uint32_t> colors; 6398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 6498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Layout padding. 6598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski bool haveLayoutBounds = false; 6698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int32_t layoutBoundsLeft; 6798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int32_t layoutBoundsTop; 6898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int32_t layoutBoundsRight; 6998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int32_t layoutBoundsBottom; 7098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 7198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Round rect outline description. 7298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int32_t outlineInsetsLeft; 7398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int32_t outlineInsetsTop; 7498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int32_t outlineInsetsRight; 7598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int32_t outlineInsetsBottom; 7698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski float outlineRadius; 7798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski uint8_t outlineAlpha; 7898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski}; 7998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 8098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskistatic void readDataFromStream(png_structp readPtr, png_bytep data, png_size_t length) { 8198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski std::istream* input = reinterpret_cast<std::istream*>(png_get_io_ptr(readPtr)); 8298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (!input->read(reinterpret_cast<char*>(data), length)) { 8398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_error(readPtr, strerror(errno)); 8498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 8598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski} 8698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 8798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskistatic void writeDataToStream(png_structp writePtr, png_bytep data, png_size_t length) { 8898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski std::ostream* output = reinterpret_cast<std::ostream*>(png_get_io_ptr(writePtr)); 8998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (!output->write(reinterpret_cast<const char*>(data), length)) { 9098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_error(writePtr, strerror(errno)); 9198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 9298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski} 9398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 9498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskistatic void flushDataToStream(png_structp writePtr) { 9598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski std::ostream* output = reinterpret_cast<std::ostream*>(png_get_io_ptr(writePtr)); 9698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (!output->flush()) { 9798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_error(writePtr, strerror(errno)); 9898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 9998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski} 10098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 10198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskistatic void logWarning(png_structp readPtr, png_const_charp warningMessage) { 10298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski SourceLogger* logger = reinterpret_cast<SourceLogger*>(png_get_error_ptr(readPtr)); 10398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski logger->warn() << warningMessage << "." << std::endl; 10498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski} 10598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 10698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 10798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskistatic bool readPng(png_structp readPtr, png_infop infoPtr, PngInfo* outInfo, 10898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski std::string* outError) { 10998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (setjmp(png_jmpbuf(readPtr))) { 11098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *outError = "failed reading png"; 11198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return false; 11298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 11398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 11498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_set_sig_bytes(readPtr, kPngSignatureSize); 11598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_read_info(readPtr, infoPtr); 11698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 11798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int colorType, bitDepth, interlaceType, compressionType; 11898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_get_IHDR(readPtr, infoPtr, &outInfo->width, &outInfo->height, &bitDepth, &colorType, 11998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski &interlaceType, &compressionType, nullptr); 12098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 12198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (colorType == PNG_COLOR_TYPE_PALETTE) { 12298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_set_palette_to_rgb(readPtr); 12398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 12498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 12598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) { 12698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_set_expand_gray_1_2_4_to_8(readPtr); 12798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 12898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 12998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (png_get_valid(readPtr, infoPtr, PNG_INFO_tRNS)) { 13098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_set_tRNS_to_alpha(readPtr); 13198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 13298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 13398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (bitDepth == 16) { 13498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_set_strip_16(readPtr); 13598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 13698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 13798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (!(colorType & PNG_COLOR_MASK_ALPHA)) { 13898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_set_add_alpha(readPtr, 0xFF, PNG_FILLER_AFTER); 13998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 14098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 14198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) { 14298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_set_gray_to_rgb(readPtr); 14398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 14498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 14598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_set_interlace_handling(readPtr); 14698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_read_update_info(readPtr, infoPtr); 14798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 14898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski const uint32_t rowBytes = png_get_rowbytes(readPtr, infoPtr); 14998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski outInfo->rows.resize(outInfo->height); 15098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski for (size_t i = 0; i < outInfo->height; i++) { 15198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski outInfo->rows[i] = new png_byte[rowBytes]; 15298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 15398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 15498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_read_image(readPtr, outInfo->rows.data()); 15598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_read_end(readPtr, infoPtr); 15698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return true; 15798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski} 15898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 15998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskistatic void checkNinePatchSerialization(android::Res_png_9patch* inPatch, void* data) { 16098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski size_t patchSize = inPatch->serializedSize(); 16198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski void* newData = malloc(patchSize); 16298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski memcpy(newData, data, patchSize); 16398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski android::Res_png_9patch* outPatch = inPatch->deserialize(newData); 16498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski outPatch->fileToDevice(); 16598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // deserialization is done in place, so outPatch == newData 16698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski assert(outPatch == newData); 16798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski assert(outPatch->numXDivs == inPatch->numXDivs); 16898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski assert(outPatch->numYDivs == inPatch->numYDivs); 16998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski assert(outPatch->paddingLeft == inPatch->paddingLeft); 17098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski assert(outPatch->paddingRight == inPatch->paddingRight); 17198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski assert(outPatch->paddingTop == inPatch->paddingTop); 17298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski assert(outPatch->paddingBottom == inPatch->paddingBottom); 17398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski/* for (int i = 0; i < outPatch->numXDivs; i++) { 17498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski assert(outPatch->getXDivs()[i] == inPatch->getXDivs()[i]); 17598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 17698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski for (int i = 0; i < outPatch->numYDivs; i++) { 17798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski assert(outPatch->getYDivs()[i] == inPatch->getYDivs()[i]); 17898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 17998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski for (int i = 0; i < outPatch->numColors; i++) { 18098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski assert(outPatch->getColors()[i] == inPatch->getColors()[i]); 18198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski }*/ 18298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski free(newData); 18398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski} 18498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 18598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski/*static void dump_image(int w, int h, const png_byte* const* rows, int color_type) { 18698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int i, j, rr, gg, bb, aa; 18798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 18898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int bpp; 18998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (color_type == PNG_COLOR_TYPE_PALETTE || color_type == PNG_COLOR_TYPE_GRAY) { 19098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski bpp = 1; 19198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { 19298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski bpp = 2; 19398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } else if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA) { 19498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // We use a padding byte even when there is no alpha 19598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski bpp = 4; 19698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } else { 19798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski printf("Unknown color type %d.\n", color_type); 19898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 19998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 20098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski for (j = 0; j < h; j++) { 20198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski const png_byte* row = rows[j]; 20298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski for (i = 0; i < w; i++) { 20398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski rr = row[0]; 20498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski gg = row[1]; 20598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski bb = row[2]; 20698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski aa = row[3]; 20798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski row += bpp; 20898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 20998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (i == 0) { 21098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski printf("Row %d:", j); 21198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 21298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski switch (bpp) { 21398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski case 1: 21498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski printf(" (%d)", rr); 21598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski break; 21698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski case 2: 21798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski printf(" (%d %d", rr, gg); 21898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski break; 21998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski case 3: 22098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski printf(" (%d %d %d)", rr, gg, bb); 22198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski break; 22298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski case 4: 22398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski printf(" (%d %d %d %d)", rr, gg, bb, aa); 22498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski break; 22598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 22698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (i == (w - 1)) { 22798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski printf("\n"); 22898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 22998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 23098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 23198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski}*/ 23298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 23398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski#define MAX(a,b) ((a)>(b)?(a):(b)) 23498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski#define ABS(a) ((a)<0?-(a):(a)) 23598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 23698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskistatic void analyze_image(SourceLogger* logger, const PngInfo& imageInfo, int grayscaleTolerance, 23798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_colorp rgbPalette, png_bytep alphaPalette, 23898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int *paletteEntries, bool *hasTransparency, int *colorType, 23998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_bytepp outRows) { 24098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int w = imageInfo.width; 24198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int h = imageInfo.height; 24298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int i, j, rr, gg, bb, aa, idx; 24398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski uint32_t colors[256], col; 24498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int num_colors = 0; 24598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int maxGrayDeviation = 0; 24698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 24798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski bool isOpaque = true; 24898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski bool isPalette = true; 24998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski bool isGrayscale = true; 25098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 25198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Scan the entire image and determine if: 25298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // 1. Every pixel has R == G == B (grayscale) 25398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // 2. Every pixel has A == 255 (opaque) 25498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // 3. There are no more than 256 distinct RGBA colors 25598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 25698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (kDebug) { 25798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski printf("Initial image data:\n"); 25898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski //dump_image(w, h, imageInfo.rows.data(), PNG_COLOR_TYPE_RGB_ALPHA); 25998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 26098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 26198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski for (j = 0; j < h; j++) { 26298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski const png_byte* row = imageInfo.rows[j]; 26398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_bytep out = outRows[j]; 26498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski for (i = 0; i < w; i++) { 26598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski rr = *row++; 26698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski gg = *row++; 26798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski bb = *row++; 26898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski aa = *row++; 26998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 27098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int odev = maxGrayDeviation; 27198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski maxGrayDeviation = MAX(ABS(rr - gg), maxGrayDeviation); 27298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski maxGrayDeviation = MAX(ABS(gg - bb), maxGrayDeviation); 27398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski maxGrayDeviation = MAX(ABS(bb - rr), maxGrayDeviation); 27498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (maxGrayDeviation > odev) { 27598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (kDebug) { 27698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski printf("New max dev. = %d at pixel (%d, %d) = (%d %d %d %d)\n", 27798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski maxGrayDeviation, i, j, rr, gg, bb, aa); 27898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 27998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 28098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 28198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Check if image is really grayscale 28298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (isGrayscale) { 28398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (rr != gg || rr != bb) { 28498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (kDebug) { 28598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski printf("Found a non-gray pixel at %d, %d = (%d %d %d %d)\n", 28698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski i, j, rr, gg, bb, aa); 28798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 28898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski isGrayscale = false; 28998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 29098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 29198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 29298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Check if image is really opaque 29398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (isOpaque) { 29498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (aa != 0xff) { 29598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (kDebug) { 29698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski printf("Found a non-opaque pixel at %d, %d = (%d %d %d %d)\n", 29798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski i, j, rr, gg, bb, aa); 29898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 29998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski isOpaque = false; 30098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 30198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 30298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 30398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Check if image is really <= 256 colors 30498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (isPalette) { 30598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski col = (uint32_t) ((rr << 24) | (gg << 16) | (bb << 8) | aa); 30698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski bool match = false; 30798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski for (idx = 0; idx < num_colors; idx++) { 30898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (colors[idx] == col) { 30998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski match = true; 31098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski break; 31198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 31298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 31398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 31498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Write the palette index for the pixel to outRows optimistically 31598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // We might overwrite it later if we decide to encode as gray or 31698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // gray + alpha 31798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *out++ = idx; 31898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (!match) { 31998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (num_colors == 256) { 32098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (kDebug) { 32198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski printf("Found 257th color at %d, %d\n", i, j); 32298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 32398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski isPalette = false; 32498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } else { 32598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski colors[num_colors++] = col; 32698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 32798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 32898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 32998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 33098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 33198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 33298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *paletteEntries = 0; 33398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *hasTransparency = !isOpaque; 33498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int bpp = isOpaque ? 3 : 4; 33598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int paletteSize = w * h + bpp * num_colors; 33698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 33798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (kDebug) { 33898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski printf("isGrayscale = %s\n", isGrayscale ? "true" : "false"); 33998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski printf("isOpaque = %s\n", isOpaque ? "true" : "false"); 34098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski printf("isPalette = %s\n", isPalette ? "true" : "false"); 34198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski printf("Size w/ palette = %d, gray+alpha = %d, rgb(a) = %d\n", 34298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski paletteSize, 2 * w * h, bpp * w * h); 34398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski printf("Max gray deviation = %d, tolerance = %d\n", maxGrayDeviation, grayscaleTolerance); 34498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 34598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 34698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Choose the best color type for the image. 34798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // 1. Opaque gray - use COLOR_TYPE_GRAY at 1 byte/pixel 34898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // 2. Gray + alpha - use COLOR_TYPE_PALETTE if the number of distinct combinations 34998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // is sufficiently small, otherwise use COLOR_TYPE_GRAY_ALPHA 35098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // 3. RGB(A) - use COLOR_TYPE_PALETTE if the number of distinct colors is sufficiently 35198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // small, otherwise use COLOR_TYPE_RGB{_ALPHA} 35298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (isGrayscale) { 35398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (isOpaque) { 35498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *colorType = PNG_COLOR_TYPE_GRAY; // 1 byte/pixel 35598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } else { 35698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Use a simple heuristic to determine whether using a palette will 35798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // save space versus using gray + alpha for each pixel. 35898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // This doesn't take into account chunk overhead, filtering, LZ 35998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // compression, etc. 36098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (isPalette && (paletteSize < 2 * w * h)) { 36198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *colorType = PNG_COLOR_TYPE_PALETTE; // 1 byte/pixel + 4 bytes/color 36298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } else { 36398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *colorType = PNG_COLOR_TYPE_GRAY_ALPHA; // 2 bytes per pixel 36498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 36598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 36698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } else if (isPalette && (paletteSize < bpp * w * h)) { 36798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *colorType = PNG_COLOR_TYPE_PALETTE; 36898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } else { 36998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (maxGrayDeviation <= grayscaleTolerance) { 37098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski logger->note() << "forcing image to gray (max deviation = " << maxGrayDeviation 37198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski << ")." 37298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski << std::endl; 37398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *colorType = isOpaque ? PNG_COLOR_TYPE_GRAY : PNG_COLOR_TYPE_GRAY_ALPHA; 37498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } else { 37598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *colorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA; 37698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 37798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 37898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 37998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Perform postprocessing of the image or palette data based on the final 38098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // color type chosen 38198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 38298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (*colorType == PNG_COLOR_TYPE_PALETTE) { 38398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Create separate RGB and Alpha palettes and set the number of colors 38498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *paletteEntries = num_colors; 38598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 38698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Create the RGB and alpha palettes 38798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski for (int idx = 0; idx < num_colors; idx++) { 38898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski col = colors[idx]; 38998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski rgbPalette[idx].red = (png_byte) ((col >> 24) & 0xff); 39098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski rgbPalette[idx].green = (png_byte) ((col >> 16) & 0xff); 39198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski rgbPalette[idx].blue = (png_byte) ((col >> 8) & 0xff); 39298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski alphaPalette[idx] = (png_byte) (col & 0xff); 39398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 39498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } else if (*colorType == PNG_COLOR_TYPE_GRAY || *colorType == PNG_COLOR_TYPE_GRAY_ALPHA) { 39598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // If the image is gray or gray + alpha, compact the pixels into outRows 39698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski for (j = 0; j < h; j++) { 39798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski const png_byte* row = imageInfo.rows[j]; 39898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_bytep out = outRows[j]; 39998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski for (i = 0; i < w; i++) { 40098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski rr = *row++; 40198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski gg = *row++; 40298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski bb = *row++; 40398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski aa = *row++; 40498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 40598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (isGrayscale) { 40698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *out++ = rr; 40798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } else { 40898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *out++ = (png_byte) (rr * 0.2126f + gg * 0.7152f + bb * 0.0722f); 40998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 41098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (!isOpaque) { 41198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *out++ = aa; 41298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 41398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 41498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 41598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 41698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski} 41798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 41898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskistatic bool writePng(png_structp writePtr, png_infop infoPtr, PngInfo* info, 41998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int grayScaleTolerance, SourceLogger* logger, std::string* outError) { 42098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (setjmp(png_jmpbuf(writePtr))) { 42198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *outError = "failed to write png"; 42298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return false; 42398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 42498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 42598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski uint32_t width, height; 42698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int colorType, bitDepth, interlaceType, compressionType; 42798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 42898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_unknown_chunk unknowns[3]; 42998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski unknowns[0].data = nullptr; 43098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski unknowns[1].data = nullptr; 43198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski unknowns[2].data = nullptr; 43298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 43398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_bytepp outRows = (png_bytepp) malloc((int) info->height * sizeof(png_bytep)); 43498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (outRows == (png_bytepp) 0) { 43598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski printf("Can't allocate output buffer!\n"); 43698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski exit(1); 43798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 43898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski for (uint32_t i = 0; i < info->height; i++) { 43998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski outRows[i] = (png_bytep) malloc(2 * (int) info->width); 44098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (outRows[i] == (png_bytep) 0) { 44198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski printf("Can't allocate output buffer!\n"); 44298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski exit(1); 44398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 44498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 44598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 44698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_set_compression_level(writePtr, Z_BEST_COMPRESSION); 44798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 44898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (kDebug) { 44998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski logger->note() << "writing image: w = " << info->width 45098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski << ", h = " << info->height 45198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski << std::endl; 45298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 45398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 45498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_color rgbPalette[256]; 45598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_byte alphaPalette[256]; 45698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski bool hasTransparency; 45798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int paletteEntries; 45898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 45998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski analyze_image(logger, *info, grayScaleTolerance, rgbPalette, alphaPalette, 46098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski &paletteEntries, &hasTransparency, &colorType, outRows); 46198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 46298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // If the image is a 9-patch, we need to preserve it as a ARGB file to make 46398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // sure the pixels will not be pre-dithered/clamped until we decide they are 46498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (info->is9Patch && (colorType == PNG_COLOR_TYPE_RGB || 46598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_PALETTE)) { 46698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski colorType = PNG_COLOR_TYPE_RGB_ALPHA; 46798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 46898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 46998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (kDebug) { 47098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski switch (colorType) { 47198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski case PNG_COLOR_TYPE_PALETTE: 47298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski logger->note() << "has " << paletteEntries 47398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski << " colors" << (hasTransparency ? " (with alpha)" : "") 47498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski << ", using PNG_COLOR_TYPE_PALLETTE." 47598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski << std::endl; 47698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski break; 47798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski case PNG_COLOR_TYPE_GRAY: 47898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski logger->note() << "is opaque gray, using PNG_COLOR_TYPE_GRAY." << std::endl; 47998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski break; 48098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski case PNG_COLOR_TYPE_GRAY_ALPHA: 48198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski logger->note() << "is gray + alpha, using PNG_COLOR_TYPE_GRAY_ALPHA." << std::endl; 48298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski break; 48398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski case PNG_COLOR_TYPE_RGB: 48498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski logger->note() << "is opaque RGB, using PNG_COLOR_TYPE_RGB." << std::endl; 48598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski break; 48698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski case PNG_COLOR_TYPE_RGB_ALPHA: 48798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski logger->note() << "is RGB + alpha, using PNG_COLOR_TYPE_RGB_ALPHA." << std::endl; 48898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski break; 48998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 49098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 49198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 49298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_set_IHDR(writePtr, infoPtr, info->width, info->height, 8, colorType, 49398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); 49498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 49598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (colorType == PNG_COLOR_TYPE_PALETTE) { 49698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_set_PLTE(writePtr, infoPtr, rgbPalette, paletteEntries); 49798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (hasTransparency) { 49898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_set_tRNS(writePtr, infoPtr, alphaPalette, paletteEntries, (png_color_16p) 0); 49998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 50098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_set_filter(writePtr, 0, PNG_NO_FILTERS); 50198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } else { 50298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_set_filter(writePtr, 0, PNG_ALL_FILTERS); 50398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 50498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 50598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (info->is9Patch) { 50698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int chunkCount = 2 + (info->haveLayoutBounds ? 1 : 0); 50798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int pIndex = info->haveLayoutBounds ? 2 : 1; 50898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int bIndex = 1; 50998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int oIndex = 0; 51098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 51198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Chunks ordered thusly because older platforms depend on the base 9 patch data being last 51298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_bytep chunkNames = info->haveLayoutBounds 51398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski ? (png_bytep)"npOl\0npLb\0npTc\0" 51498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski : (png_bytep)"npOl\0npTc"; 51598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 51698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // base 9 patch data 51798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (kDebug) { 51898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski logger->note() << "adding 9-patch info..." << std::endl; 51998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 52098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski strcpy((char*)unknowns[pIndex].name, "npTc"); 52198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski unknowns[pIndex].data = (png_byte*) info->serialize9Patch(); 52298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski unknowns[pIndex].size = info->info9Patch.serializedSize(); 52398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // TODO: remove the check below when everything works 52498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski checkNinePatchSerialization(&info->info9Patch, unknowns[pIndex].data); 52598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 52698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // automatically generated 9 patch outline data 52798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int chunkSize = sizeof(png_uint_32) * 6; 52898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski strcpy((char*)unknowns[oIndex].name, "npOl"); 52998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski unknowns[oIndex].data = (png_byte*) calloc(chunkSize, 1); 53098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_byte outputData[chunkSize]; 53198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski memcpy(&outputData, &info->outlineInsetsLeft, 4 * sizeof(png_uint_32)); 53298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski ((float*) outputData)[4] = info->outlineRadius; 53398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski ((png_uint_32*) outputData)[5] = info->outlineAlpha; 53498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski memcpy(unknowns[oIndex].data, &outputData, chunkSize); 53598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski unknowns[oIndex].size = chunkSize; 53698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 53798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // optional optical inset / layout bounds data 53898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (info->haveLayoutBounds) { 53998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int chunkSize = sizeof(png_uint_32) * 4; 54098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski strcpy((char*)unknowns[bIndex].name, "npLb"); 54198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski unknowns[bIndex].data = (png_byte*) calloc(chunkSize, 1); 54298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski memcpy(unknowns[bIndex].data, &info->layoutBoundsLeft, chunkSize); 54398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski unknowns[bIndex].size = chunkSize; 54498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 54598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 54698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski for (int i = 0; i < chunkCount; i++) { 54798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski unknowns[i].location = PNG_HAVE_PLTE; 54898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 54998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_set_keep_unknown_chunks(writePtr, PNG_HANDLE_CHUNK_ALWAYS, 55098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski chunkNames, chunkCount); 55198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_set_unknown_chunks(writePtr, infoPtr, unknowns, chunkCount); 55298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 55398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski#if PNG_LIBPNG_VER < 10600 55498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Deal with unknown chunk location bug in 1.5.x and earlier. 55598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_set_unknown_chunk_location(writePtr, infoPtr, 0, PNG_HAVE_PLTE); 55698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (info->haveLayoutBounds) { 55798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_set_unknown_chunk_location(writePtr, infoPtr, 1, PNG_HAVE_PLTE); 55898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 55998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski#endif 56098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 56198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 56298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_write_info(writePtr, infoPtr); 56398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 56498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_bytepp rows; 56598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (colorType == PNG_COLOR_TYPE_RGB || colorType == PNG_COLOR_TYPE_RGB_ALPHA) { 56698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (colorType == PNG_COLOR_TYPE_RGB) { 56798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_set_filler(writePtr, 0, PNG_FILLER_AFTER); 56898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 56998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski rows = info->rows.data(); 57098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } else { 57198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski rows = outRows; 57298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 57398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_write_image(writePtr, rows); 57498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 57598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (kDebug) { 57698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski printf("Final image data:\n"); 57798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski //dump_image(info->width, info->height, rows, colorType); 57898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 57998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 58098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_write_end(writePtr, infoPtr); 58198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 58298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski for (uint32_t i = 0; i < info->height; i++) { 58398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski free(outRows[i]); 58498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 58598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski free(outRows); 58698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski free(unknowns[0].data); 58798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski free(unknowns[1].data); 58898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski free(unknowns[2].data); 58998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 59098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_get_IHDR(writePtr, infoPtr, &width, &height, &bitDepth, &colorType, &interlaceType, 59198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski &compressionType, nullptr); 59298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 59398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (kDebug) { 59498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski logger->note() << "image written: w = " << width << ", h = " << height 59598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski << ", d = " << bitDepth << ", colors = " << colorType 59698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski << ", inter = " << interlaceType << ", comp = " << compressionType 59798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski << std::endl; 59898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 59998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return true; 60098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski} 60198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 60298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskiconstexpr uint32_t kColorWhite = 0xffffffffu; 60398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskiconstexpr uint32_t kColorTick = 0xff000000u; 60498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskiconstexpr uint32_t kColorLayoutBoundsTick = 0xff0000ffu; 60598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 60698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskienum class TickType { 60798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski kNone, 60898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski kTick, 60998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski kLayoutBounds, 61098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski kBoth 61198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski}; 61298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 61398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskistatic TickType tickType(png_bytep p, bool transparent, const char** outError) { 61498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_uint_32 color = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); 61598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 61698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (transparent) { 61798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (p[3] == 0) { 61898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return TickType::kNone; 61998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 62098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (color == kColorLayoutBoundsTick) { 62198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return TickType::kLayoutBounds; 62298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 62398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (color == kColorTick) { 62498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return TickType::kTick; 62598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 62698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 62798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Error cases 62898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (p[3] != 0xff) { 62998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *outError = "Frame pixels must be either solid or transparent " 63098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski "(not intermediate alphas)"; 63198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return TickType::kNone; 63298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 63398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 63498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (p[0] != 0 || p[1] != 0 || p[2] != 0) { 63598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *outError = "Ticks in transparent frame must be black or red"; 63698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 63798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return TickType::kTick; 63898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 63998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 64098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (p[3] != 0xFF) { 64198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *outError = "White frame must be a solid color (no alpha)"; 64298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 64398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (color == kColorWhite) { 64498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return TickType::kNone; 64598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 64698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (color == kColorTick) { 64798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return TickType::kTick; 64898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 64998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (color == kColorLayoutBoundsTick) { 65098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return TickType::kLayoutBounds; 65198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 65298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 65398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (p[0] != 0 || p[1] != 0 || p[2] != 0) { 65498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *outError = "Ticks in white frame must be black or red"; 65598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return TickType::kNone; 65698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 65798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return TickType::kTick; 65898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski} 65998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 66098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskienum class TickState { 66198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski kStart, 66298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski kInside1, 66398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski kOutside1 66498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski}; 66598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 66698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskistatic bool getHorizontalTicks(png_bytep row, int width, bool transparent, bool required, 66798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int32_t* outLeft, int32_t* outRight, const char** outError, 66898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski uint8_t* outDivs, bool multipleAllowed) { 66998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *outLeft = *outRight = -1; 67098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski TickState state = TickState::kStart; 67198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski bool found = false; 67298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 67398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski for (int i = 1; i < width - 1; i++) { 67498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (tickType(row+i*4, transparent, outError) == TickType::kTick) { 67598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (state == TickState::kStart || 67698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski (state == TickState::kOutside1 && multipleAllowed)) { 67798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *outLeft = i-1; 67898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *outRight = width-2; 67998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski found = true; 68098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (outDivs != NULL) { 68198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *outDivs += 2; 68298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 68398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski state = TickState::kInside1; 68498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } else if (state == TickState::kOutside1) { 68598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *outError = "Can't have more than one marked region along edge"; 68698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *outLeft = i; 68798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return false; 68898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 68998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } else if (!*outError) { 69098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (state == TickState::kInside1) { 69198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // We're done with this div. Move on to the next. 69298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *outRight = i-1; 69398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski outRight += 2; 69498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski outLeft += 2; 69598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski state = TickState::kOutside1; 69698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 69798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } else { 69898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *outLeft = i; 69998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return false; 70098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 70198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 70298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 70398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (required && !found) { 70498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *outError = "No marked region found along edge"; 70598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *outLeft = -1; 70698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return false; 70798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 70898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return true; 70998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski} 71098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 71198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskistatic bool getVerticalTicks(png_bytepp rows, int offset, int height, bool transparent, 71298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski bool required, int32_t* outTop, int32_t* outBottom, 71398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski const char** outError, uint8_t* outDivs, bool multipleAllowed) { 71498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *outTop = *outBottom = -1; 71598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski TickState state = TickState::kStart; 71698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski bool found = false; 71798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 71898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski for (int i = 1; i < height - 1; i++) { 71998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (tickType(rows[i]+offset, transparent, outError) == TickType::kTick) { 72098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (state == TickState::kStart || 72198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski (state == TickState::kOutside1 && multipleAllowed)) { 72298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *outTop = i-1; 72398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *outBottom = height-2; 72498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski found = true; 72598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (outDivs != NULL) { 72698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *outDivs += 2; 72798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 72898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski state = TickState::kInside1; 72998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } else if (state == TickState::kOutside1) { 73098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *outError = "Can't have more than one marked region along edge"; 73198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *outTop = i; 73298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return false; 73398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 73498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } else if (!*outError) { 73598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (state == TickState::kInside1) { 73698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // We're done with this div. Move on to the next. 73798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *outBottom = i-1; 73898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski outTop += 2; 73998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski outBottom += 2; 74098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski state = TickState::kOutside1; 74198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 74298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } else { 74398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *outTop = i; 74498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return false; 74598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 74698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 74798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 74898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (required && !found) { 74998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *outError = "No marked region found along edge"; 75098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *outTop = -1; 75198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return false; 75298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 75398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return true; 75498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski} 75598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 75698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskistatic bool getHorizontalLayoutBoundsTicks(png_bytep row, int width, bool transparent, 75798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski bool /* required */, int32_t* outLeft, 75898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int32_t* outRight, const char** outError) { 75998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *outLeft = *outRight = 0; 76098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 76198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Look for left tick 76298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (tickType(row + 4, transparent, outError) == TickType::kLayoutBounds) { 76398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Starting with a layout padding tick 76498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int i = 1; 76598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski while (i < width - 1) { 76698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski (*outLeft)++; 76798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski i++; 76898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (tickType(row + i * 4, transparent, outError) != TickType::kLayoutBounds) { 76998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski break; 77098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 77198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 77298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 77398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 77498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Look for right tick 77598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (tickType(row + (width - 2) * 4, transparent, outError) == TickType::kLayoutBounds) { 77698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Ending with a layout padding tick 77798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int i = width - 2; 77898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski while (i > 1) { 77998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski (*outRight)++; 78098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski i--; 78198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (tickType(row+i*4, transparent, outError) != TickType::kLayoutBounds) { 78298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski break; 78398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 78498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 78598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 78698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return true; 78798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski} 78898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 78998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskistatic bool getVerticalLayoutBoundsTicks(png_bytepp rows, int offset, int height, bool transparent, 79098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski bool /* required */, int32_t* outTop, int32_t* outBottom, 79198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski const char** outError) { 79298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *outTop = *outBottom = 0; 79398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 79498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Look for top tick 79598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (tickType(rows[1] + offset, transparent, outError) == TickType::kLayoutBounds) { 79698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Starting with a layout padding tick 79798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int i = 1; 79898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski while (i < height - 1) { 79998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski (*outTop)++; 80098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski i++; 80198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (tickType(rows[i] + offset, transparent, outError) != TickType::kLayoutBounds) { 80298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski break; 80398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 80498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 80598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 80698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 80798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Look for bottom tick 80898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (tickType(rows[height - 2] + offset, transparent, outError) == TickType::kLayoutBounds) { 80998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Ending with a layout padding tick 81098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int i = height - 2; 81198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski while (i > 1) { 81298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski (*outBottom)++; 81398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski i--; 81498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (tickType(rows[i] + offset, transparent, outError) != TickType::kLayoutBounds) { 81598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski break; 81698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 81798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 81898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 81998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return true; 82098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski} 82198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 82298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskistatic void findMaxOpacity(png_bytepp rows, int startX, int startY, int endX, int endY, 82398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int dX, int dY, int* outInset) { 82498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski uint8_t maxOpacity = 0; 82598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int inset = 0; 82698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *outInset = 0; 82798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski for (int x = startX, y = startY; x != endX && y != endY; x += dX, y += dY, inset++) { 82898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_byte* color = rows[y] + x * 4; 82998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski uint8_t opacity = color[3]; 83098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (opacity > maxOpacity) { 83198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski maxOpacity = opacity; 83298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *outInset = inset; 83398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 83498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (opacity == 0xff) return; 83598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 83698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski} 83798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 83898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskistatic uint8_t maxAlphaOverRow(png_bytep row, int startX, int endX) { 83998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski uint8_t maxAlpha = 0; 84098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski for (int x = startX; x < endX; x++) { 84198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski uint8_t alpha = (row + x * 4)[3]; 84298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (alpha > maxAlpha) maxAlpha = alpha; 84398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 84498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return maxAlpha; 84598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski} 84698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 84798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskistatic uint8_t maxAlphaOverCol(png_bytepp rows, int offsetX, int startY, int endY) { 84898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski uint8_t maxAlpha = 0; 84998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski for (int y = startY; y < endY; y++) { 85098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski uint8_t alpha = (rows[y] + offsetX * 4)[3]; 85198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (alpha > maxAlpha) maxAlpha = alpha; 85298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 85398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return maxAlpha; 85498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski} 85598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 85698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskistatic void getOutline(PngInfo* image) { 85798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int midX = image->width / 2; 85898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int midY = image->height / 2; 85998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int endX = image->width - 2; 86098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int endY = image->height - 2; 86198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 86298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // find left and right extent of nine patch content on center row 86398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (image->width > 4) { 86498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski findMaxOpacity(image->rows.data(), 1, midY, midX, -1, 1, 0, &image->outlineInsetsLeft); 86598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski findMaxOpacity(image->rows.data(), endX, midY, midX, -1, -1, 0, 86698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski &image->outlineInsetsRight); 86798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } else { 86898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski image->outlineInsetsLeft = 0; 86998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski image->outlineInsetsRight = 0; 87098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 87198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 87298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // find top and bottom extent of nine patch content on center column 87398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (image->height > 4) { 87498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski findMaxOpacity(image->rows.data(), midX, 1, -1, midY, 0, 1, &image->outlineInsetsTop); 87598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski findMaxOpacity(image->rows.data(), midX, endY, -1, midY, 0, -1, 87698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski &image->outlineInsetsBottom); 87798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } else { 87898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski image->outlineInsetsTop = 0; 87998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski image->outlineInsetsBottom = 0; 88098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 88198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 88298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int innerStartX = 1 + image->outlineInsetsLeft; 88398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int innerStartY = 1 + image->outlineInsetsTop; 88498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int innerEndX = endX - image->outlineInsetsRight; 88598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int innerEndY = endY - image->outlineInsetsBottom; 88698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int innerMidX = (innerEndX + innerStartX) / 2; 88798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int innerMidY = (innerEndY + innerStartY) / 2; 88898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 88998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // assuming the image is a round rect, compute the radius by marching 89098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // diagonally from the top left corner towards the center 89198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski image->outlineAlpha = std::max( 89298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski maxAlphaOverRow(image->rows[innerMidY], innerStartX, innerEndX), 89398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski maxAlphaOverCol(image->rows.data(), innerMidX, innerStartY, innerStartY)); 89498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 89598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int diagonalInset = 0; 89698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski findMaxOpacity(image->rows.data(), innerStartX, innerStartY, innerMidX, innerMidY, 1, 1, 89798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski &diagonalInset); 89898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 89998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski /* Determine source radius based upon inset: 90098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski * sqrt(r^2 + r^2) = sqrt(i^2 + i^2) + r 90198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski * sqrt(2) * r = sqrt(2) * i + r 90298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski * (sqrt(2) - 1) * r = sqrt(2) * i 90398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski * r = sqrt(2) / (sqrt(2) - 1) * i 90498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski */ 90598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski image->outlineRadius = 3.4142f * diagonalInset; 90698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 90798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (kDebug) { 90898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski printf("outline insets %d %d %d %d, rad %f, alpha %x\n", 90998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski image->outlineInsetsLeft, 91098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski image->outlineInsetsTop, 91198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski image->outlineInsetsRight, 91298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski image->outlineInsetsBottom, 91398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski image->outlineRadius, 91498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski image->outlineAlpha); 91598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 91698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski} 91798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 91898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskistatic uint32_t getColor(png_bytepp rows, int left, int top, int right, int bottom) { 91998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_bytep color = rows[top] + left*4; 92098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 92198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (left > right || top > bottom) { 92298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return android::Res_png_9patch::TRANSPARENT_COLOR; 92398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 92498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 92598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski while (top <= bottom) { 92698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski for (int i = left; i <= right; i++) { 92798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_bytep p = rows[top]+i*4; 92898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (color[3] == 0) { 92998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (p[3] != 0) { 93098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return android::Res_png_9patch::NO_COLOR; 93198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 93298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } else if (p[0] != color[0] || p[1] != color[1] || 93398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski p[2] != color[2] || p[3] != color[3]) { 93498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return android::Res_png_9patch::NO_COLOR; 93598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 93698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 93798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski top++; 93898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 93998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 94098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (color[3] == 0) { 94198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return android::Res_png_9patch::TRANSPARENT_COLOR; 94298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 94398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return (color[3]<<24) | (color[0]<<16) | (color[1]<<8) | color[2]; 94498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski} 94598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 94698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskistatic bool do9Patch(PngInfo* image, std::string* outError) { 94798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski image->is9Patch = true; 94898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 94998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int W = image->width; 95098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int H = image->height; 95198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int i, j; 95298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 95398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski const int maxSizeXDivs = W * sizeof(int32_t); 95498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski const int maxSizeYDivs = H * sizeof(int32_t); 95598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int32_t* xDivs = image->xDivs = new int32_t[W]; 95698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int32_t* yDivs = image->yDivs = new int32_t[H]; 95798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski uint8_t numXDivs = 0; 95898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski uint8_t numYDivs = 0; 95998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 96098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int8_t numColors; 96198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int numRows; 96298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int numCols; 96398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int top; 96498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int left; 96598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int right; 96698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int bottom; 96798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski memset(xDivs, -1, maxSizeXDivs); 96898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski memset(yDivs, -1, maxSizeYDivs); 96998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski image->info9Patch.paddingLeft = image->info9Patch.paddingRight = -1; 97098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski image->info9Patch.paddingTop = image->info9Patch.paddingBottom = -1; 97198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski image->layoutBoundsLeft = image->layoutBoundsRight = 0; 97298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski image->layoutBoundsTop = image->layoutBoundsBottom = 0; 97398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 97498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_bytep p = image->rows[0]; 97598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski bool transparent = p[3] == 0; 97698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski bool hasColor = false; 97798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 97898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski const char* errorMsg = nullptr; 97998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int errorPixel = -1; 98098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski const char* errorEdge = nullptr; 98198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 98298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int colorIndex = 0; 98398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski std::vector<png_bytep> newRows; 98498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 98598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Validate size... 98698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (W < 3 || H < 3) { 98798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski errorMsg = "Image must be at least 3x3 (1x1 without frame) pixels"; 98898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski goto getout; 98998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 99098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 99198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Validate frame... 99298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (!transparent && 99398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski (p[0] != 0xFF || p[1] != 0xFF || p[2] != 0xFF || p[3] != 0xFF)) { 99498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski errorMsg = "Must have one-pixel frame that is either transparent or white"; 99598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski goto getout; 99698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 99798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 99898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Find left and right of sizing areas... 99998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (!getHorizontalTicks(p, W, transparent, true, &xDivs[0], &xDivs[1], &errorMsg, &numXDivs, 100098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski true)) { 100198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski errorPixel = xDivs[0]; 100298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski errorEdge = "top"; 100398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski goto getout; 100498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 100598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 100698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Find top and bottom of sizing areas... 100798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (!getVerticalTicks(image->rows.data(), 0, H, transparent, true, &yDivs[0], &yDivs[1], 100898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski &errorMsg, &numYDivs, true)) { 100998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski errorPixel = yDivs[0]; 101098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski errorEdge = "left"; 101198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski goto getout; 101298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 101398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 101498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Copy patch size data into image... 101598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski image->info9Patch.numXDivs = numXDivs; 101698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski image->info9Patch.numYDivs = numYDivs; 101798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 101898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Find left and right of padding area... 101998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (!getHorizontalTicks(image->rows[H-1], W, transparent, false, 102098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski &image->info9Patch.paddingLeft, &image->info9Patch.paddingRight, 102198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski &errorMsg, nullptr, false)) { 102298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski errorPixel = image->info9Patch.paddingLeft; 102398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski errorEdge = "bottom"; 102498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski goto getout; 102598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 102698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 102798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Find top and bottom of padding area... 102898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (!getVerticalTicks(image->rows.data(), (W-1)*4, H, transparent, false, 102998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski &image->info9Patch.paddingTop, &image->info9Patch.paddingBottom, 103098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski &errorMsg, nullptr, false)) { 103198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski errorPixel = image->info9Patch.paddingTop; 103298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski errorEdge = "right"; 103398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski goto getout; 103498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 103598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 103698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Find left and right of layout padding... 103798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski getHorizontalLayoutBoundsTicks(image->rows[H-1], W, transparent, false, 103898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski &image->layoutBoundsLeft, &image->layoutBoundsRight, &errorMsg); 103998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 104098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski getVerticalLayoutBoundsTicks(image->rows.data(), (W-1)*4, H, transparent, false, 104198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski &image->layoutBoundsTop, &image->layoutBoundsBottom, &errorMsg); 104298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 104398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski image->haveLayoutBounds = image->layoutBoundsLeft != 0 104498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski || image->layoutBoundsRight != 0 104598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski || image->layoutBoundsTop != 0 104698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski || image->layoutBoundsBottom != 0; 104798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 104898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (image->haveLayoutBounds) { 104998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (kDebug) { 105098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski printf("layoutBounds=%d %d %d %d\n", image->layoutBoundsLeft, image->layoutBoundsTop, 105198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski image->layoutBoundsRight, image->layoutBoundsBottom); 105298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 105398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 105498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 105598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // use opacity of pixels to estimate the round rect outline 105698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski getOutline(image); 105798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 105898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // If padding is not yet specified, take values from size. 105998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (image->info9Patch.paddingLeft < 0) { 106098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski image->info9Patch.paddingLeft = xDivs[0]; 106198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski image->info9Patch.paddingRight = W - 2 - xDivs[1]; 106298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } else { 106398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Adjust value to be correct! 106498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski image->info9Patch.paddingRight = W - 2 - image->info9Patch.paddingRight; 106598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 106698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (image->info9Patch.paddingTop < 0) { 106798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski image->info9Patch.paddingTop = yDivs[0]; 106898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski image->info9Patch.paddingBottom = H - 2 - yDivs[1]; 106998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } else { 107098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Adjust value to be correct! 107198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski image->info9Patch.paddingBottom = H - 2 - image->info9Patch.paddingBottom; 107298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 107398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 107498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski/* if (kDebug) { 107598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski printf("Size ticks for %s: x0=%d, x1=%d, y0=%d, y1=%d\n", imageName, 107698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski xDivs[0], xDivs[1], 107798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski yDivs[0], yDivs[1]); 107898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski printf("padding ticks for %s: l=%d, r=%d, t=%d, b=%d\n", imageName, 107998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski image->info9Patch.paddingLeft, image->info9Patch.paddingRight, 108098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski image->info9Patch.paddingTop, image->info9Patch.paddingBottom); 108198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski }*/ 108298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 108398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Remove frame from image. 108498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski newRows.resize(H - 2); 108598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski for (i = 0; i < H - 2; i++) { 108698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski newRows[i] = image->rows[i + 1]; 108798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski memmove(newRows[i], newRows[i] + 4, (W - 2) * 4); 108898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 108998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski image->rows.swap(newRows); 109098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 109198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski image->width -= 2; 109298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski W = image->width; 109398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski image->height -= 2; 109498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski H = image->height; 109598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 109698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Figure out the number of rows and columns in the N-patch 109798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski numCols = numXDivs + 1; 109898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (xDivs[0] == 0) { // Column 1 is strechable 109998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski numCols--; 110098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 110198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (xDivs[numXDivs - 1] == W) { 110298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski numCols--; 110398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 110498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski numRows = numYDivs + 1; 110598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (yDivs[0] == 0) { // Row 1 is strechable 110698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski numRows--; 110798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 110898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (yDivs[numYDivs - 1] == H) { 110998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski numRows--; 111098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 111198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 111298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Make sure the amount of rows and columns will fit in the number of 111398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // colors we can use in the 9-patch format. 111498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (numRows * numCols > 0x7F) { 111598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski errorMsg = "Too many rows and columns in 9-patch perimeter"; 111698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski goto getout; 111798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 111898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 111998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski numColors = numRows * numCols; 112098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski image->info9Patch.numColors = numColors; 112198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski image->colors.resize(numColors); 112298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 112398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Fill in color information for each patch. 112498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 112598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski uint32_t c; 112698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski top = 0; 112798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 112898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // The first row always starts with the top being at y=0 and the bottom 112998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // being either yDivs[1] (if yDivs[0]=0) of yDivs[0]. In the former case 113098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // the first row is stretchable along the Y axis, otherwise it is fixed. 113198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // The last row always ends with the bottom being bitmap.height and the top 113298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // being either yDivs[numYDivs-2] (if yDivs[numYDivs-1]=bitmap.height) or 113398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // yDivs[numYDivs-1]. In the former case the last row is stretchable along 113498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // the Y axis, otherwise it is fixed. 113598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // 113698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // The first and last columns are similarly treated with respect to the X 113798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // axis. 113898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // 113998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // The above is to help explain some of the special casing that goes on the 114098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // code below. 114198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 114298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // The initial yDiv and whether the first row is considered stretchable or 114398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // not depends on whether yDiv[0] was zero or not. 114498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski for (j = (yDivs[0] == 0 ? 1 : 0); j <= numYDivs && top < H; j++) { 114598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (j == numYDivs) { 114698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski bottom = H; 114798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } else { 114898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski bottom = yDivs[j]; 114998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 115098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski left = 0; 115198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // The initial xDiv and whether the first column is considered 115298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // stretchable or not depends on whether xDiv[0] was zero or not. 115398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski for (i = xDivs[0] == 0 ? 1 : 0; i <= numXDivs && left < W; i++) { 115498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (i == numXDivs) { 115598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski right = W; 115698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } else { 115798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski right = xDivs[i]; 115898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 115998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski c = getColor(image->rows.data(), left, top, right - 1, bottom - 1); 116098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski image->colors[colorIndex++] = c; 116198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (kDebug) { 116298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (c != android::Res_png_9patch::NO_COLOR) { 116398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski hasColor = true; 116498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 116598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 116698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski left = right; 116798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 116898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski top = bottom; 116998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 117098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 117198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski assert(colorIndex == numColors); 117298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 117398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (kDebug && hasColor) { 117498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski for (i = 0; i < numColors; i++) { 117598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (i == 0) printf("Colors:\n"); 117698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski printf(" #%08x", image->colors[i]); 117798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (i == numColors - 1) printf("\n"); 117898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 117998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 118098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskigetout: 118198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (errorMsg) { 118298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski std::stringstream err; 118398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski err << "9-patch malformed: " << errorMsg; 118498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (!errorEdge) { 118598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski err << "." << std::endl; 118698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (errorPixel >= 0) { 118798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski err << "Found at pixel #" << errorPixel << " along " << errorEdge << " edge"; 118898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } else { 118998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski err << "Found along " << errorEdge << " edge"; 119098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 119198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 119298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *outError = err.str(); 119398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return false; 119498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 119598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return true; 119698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski} 119798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 119898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 119998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskibool Png::process(const Source& source, std::istream& input, std::ostream& output, 120098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski const Options& options, std::string* outError) { 120198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_byte signature[kPngSignatureSize]; 120298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 120398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Read the PNG signature first. 120498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (!input.read(reinterpret_cast<char*>(signature), kPngSignatureSize)) { 120598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *outError = strerror(errno); 120698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return false; 120798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 120898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 120998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // If the PNG signature doesn't match, bail early. 121098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (png_sig_cmp(signature, 0, kPngSignatureSize) != 0) { 121198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *outError = "not a valid png file"; 121298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return false; 121398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 121498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 121598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski SourceLogger logger(source); 121698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski bool result = false; 121798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_structp readPtr = nullptr; 121898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_infop infoPtr = nullptr; 121998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_structp writePtr = nullptr; 122098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_infop writeInfoPtr = nullptr; 122198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski PngInfo pngInfo = {}; 122298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 122398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski readPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, nullptr, nullptr); 122498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (!readPtr) { 122598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *outError = "failed to allocate read ptr"; 122698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski goto bail; 122798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 122898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 122998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski infoPtr = png_create_info_struct(readPtr); 123098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (!infoPtr) { 123198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *outError = "failed to allocate info ptr"; 123298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski goto bail; 123398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 123498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 123598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_set_error_fn(readPtr, reinterpret_cast<png_voidp>(&logger), nullptr, logWarning); 123698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 123798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Set the read function to read from std::istream. 123898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_set_read_fn(readPtr, (png_voidp)&input, readDataFromStream); 123998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 124098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (!readPng(readPtr, infoPtr, &pngInfo, outError)) { 124198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski goto bail; 124298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 124398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 124498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (util::stringEndsWith(source.path, ".9.png")) { 124598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (!do9Patch(&pngInfo, outError)) { 124698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski goto bail; 124798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 124898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 124998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 125098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski writePtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, nullptr, nullptr); 125198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (!writePtr) { 125298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *outError = "failed to allocate write ptr"; 125398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski goto bail; 125498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 125598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 125698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski writeInfoPtr = png_create_info_struct(writePtr); 125798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (!writeInfoPtr) { 125898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski *outError = "failed to allocate write info ptr"; 125998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski goto bail; 126098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 126198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 126298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_set_error_fn(writePtr, nullptr, nullptr, logWarning); 126398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 126498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // Set the write function to write to std::ostream. 126598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_set_write_fn(writePtr, (png_voidp)&output, writeDataToStream, flushDataToStream); 126698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 126798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (!writePng(writePtr, writeInfoPtr, &pngInfo, options.grayScaleTolerance, &logger, 126898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski outError)) { 126998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski goto bail; 127098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 127198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 127298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski result = true; 127398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskibail: 127498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (readPtr) { 127598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_destroy_read_struct(&readPtr, &infoPtr, nullptr); 127698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 127798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 127898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (writePtr) { 127998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_destroy_write_struct(&writePtr, &writeInfoPtr); 128098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 128198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return result; 128298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski} 128398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 128498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski} // namespace aapt 1285