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 "Png.h" 1898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 1998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski#include <png.h> 20cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski#include <zlib.h> 21d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski 22cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski#include <iostream> 2398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski#include <sstream> 2498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski#include <string> 2598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski#include <vector> 2698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 27d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski#include "androidfw/ResourceTypes.h" 28d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski 29d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski#include "Source.h" 30d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski#include "util/BigBuffer.h" 31d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski#include "util/Util.h" 32d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski 3398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskinamespace aapt { 3498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 3598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskiconstexpr bool kDebug = false; 3698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 3798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskistruct PngInfo { 38cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski ~PngInfo() { 39cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski for (png_bytep row : rows) { 40cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (row != nullptr) { 41cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski delete[] row; 42cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 43cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 44cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 45cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski delete[] xDivs; 46cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski delete[] yDivs; 47cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 48cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 49cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski void* serialize9Patch() { 50cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski void* serialized = android::Res_png_9patch::serialize(info9Patch, xDivs, 51cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski yDivs, colors.data()); 52cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski reinterpret_cast<android::Res_png_9patch*>(serialized)->deviceToFile(); 53cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return serialized; 54cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 55cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 56cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski uint32_t width = 0; 57cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski uint32_t height = 0; 58cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski std::vector<png_bytep> rows; 59cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 60cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski bool is9Patch = false; 61cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski android::Res_png_9patch info9Patch; 62cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int32_t* xDivs = nullptr; 63cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int32_t* yDivs = nullptr; 64cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski std::vector<uint32_t> colors; 65cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 66cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Layout padding. 67cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski bool haveLayoutBounds = false; 68cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int32_t layoutBoundsLeft; 69cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int32_t layoutBoundsTop; 70cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int32_t layoutBoundsRight; 71cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int32_t layoutBoundsBottom; 72cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 73cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Round rect outline description. 74cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int32_t outlineInsetsLeft; 75cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int32_t outlineInsetsTop; 76cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int32_t outlineInsetsRight; 77cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int32_t outlineInsetsBottom; 78cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski float outlineRadius; 79cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski uint8_t outlineAlpha; 8098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski}; 8198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 82cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinskistatic void readDataFromStream(png_structp readPtr, png_bytep data, 83cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_size_t length) { 84cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski std::istream* input = 85cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski reinterpret_cast<std::istream*>(png_get_io_ptr(readPtr)); 86cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (!input->read(reinterpret_cast<char*>(data), length)) { 87cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_error(readPtr, strerror(errno)); 88cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 8998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski} 9098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 91cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinskistatic void writeDataToStream(png_structp writePtr, png_bytep data, 92cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_size_t length) { 93cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski BigBuffer* outBuffer = reinterpret_cast<BigBuffer*>(png_get_io_ptr(writePtr)); 94ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski png_bytep buf = outBuffer->NextBlock<png_byte>(length); 95cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski memcpy(buf, data, length); 9698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski} 9798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 98cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinskistatic void flushDataToStream(png_structp /*writePtr*/) {} 9998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 10098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskistatic void logWarning(png_structp readPtr, png_const_charp warningMessage) { 101cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski IDiagnostics* diag = 102cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski reinterpret_cast<IDiagnostics*>(png_get_error_ptr(readPtr)); 103ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski diag->Warn(DiagMessage() << warningMessage); 10498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski} 10598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 106cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinskistatic bool readPng(IDiagnostics* diag, png_structp readPtr, png_infop infoPtr, 107cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski PngInfo* outInfo) { 108cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (setjmp(png_jmpbuf(readPtr))) { 109ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski diag->Error(DiagMessage() << "failed reading png"); 110cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return false; 111cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 112cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 113cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_set_sig_bytes(readPtr, kPngSignatureSize); 114cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_read_info(readPtr, infoPtr); 115cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 116cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int colorType, bitDepth, interlaceType, compressionType; 117cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_get_IHDR(readPtr, infoPtr, &outInfo->width, &outInfo->height, &bitDepth, 118cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski &colorType, &interlaceType, &compressionType, nullptr); 119cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 120cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (colorType == PNG_COLOR_TYPE_PALETTE) { 121cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_set_palette_to_rgb(readPtr); 122cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 123cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 124cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) { 125cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_set_expand_gray_1_2_4_to_8(readPtr); 126cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 127cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 128cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (png_get_valid(readPtr, infoPtr, PNG_INFO_tRNS)) { 129cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_set_tRNS_to_alpha(readPtr); 130cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 131cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 132cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (bitDepth == 16) { 133cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_set_strip_16(readPtr); 134cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 135cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 136cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (!(colorType & PNG_COLOR_MASK_ALPHA)) { 137cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_set_add_alpha(readPtr, 0xFF, PNG_FILLER_AFTER); 138cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 139cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 140cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (colorType == PNG_COLOR_TYPE_GRAY || 141cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski colorType == PNG_COLOR_TYPE_GRAY_ALPHA) { 142cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_set_gray_to_rgb(readPtr); 143cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 144cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 145cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_set_interlace_handling(readPtr); 146cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_read_update_info(readPtr, infoPtr); 147cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 148cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski const uint32_t rowBytes = png_get_rowbytes(readPtr, infoPtr); 149cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski outInfo->rows.resize(outInfo->height); 150cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski for (size_t i = 0; i < outInfo->height; i++) { 151cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski outInfo->rows[i] = new png_byte[rowBytes]; 152cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 153cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 154cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_read_image(readPtr, outInfo->rows.data()); 155cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_read_end(readPtr, infoPtr); 156cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return true; 15798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski} 15898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 159cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinskistatic void checkNinePatchSerialization(android::Res_png_9patch* inPatch, 160cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski void* data) { 161cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski size_t patchSize = inPatch->serializedSize(); 162cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski void* newData = malloc(patchSize); 163cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski memcpy(newData, data, patchSize); 164cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski android::Res_png_9patch* outPatch = inPatch->deserialize(newData); 165cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski outPatch->fileToDevice(); 166cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // deserialization is done in place, so outPatch == newData 167cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski assert(outPatch == newData); 168cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski assert(outPatch->numXDivs == inPatch->numXDivs); 169cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski assert(outPatch->numYDivs == inPatch->numYDivs); 170cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski assert(outPatch->paddingLeft == inPatch->paddingLeft); 171cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski assert(outPatch->paddingRight == inPatch->paddingRight); 172cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski assert(outPatch->paddingTop == inPatch->paddingTop); 173cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski assert(outPatch->paddingBottom == inPatch->paddingBottom); 174cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski /* for (int i = 0; i < outPatch->numXDivs; i++) { 175cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski assert(outPatch->getXDivs()[i] == inPatch->getXDivs()[i]); 176cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 177cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski for (int i = 0; i < outPatch->numYDivs; i++) { 178cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski assert(outPatch->getYDivs()[i] == inPatch->getYDivs()[i]); 179cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 180cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski for (int i = 0; i < outPatch->numColors; i++) { 181cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski assert(outPatch->getColors()[i] == inPatch->getColors()[i]); 182cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski }*/ 183cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski free(newData); 18498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski} 18598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 186cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski/*static void dump_image(int w, int h, const png_byte* const* rows, int 187cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinskicolor_type) { 18898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int i, j, rr, gg, bb, aa; 18998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 19098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski int bpp; 191cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (color_type == PNG_COLOR_TYPE_PALETTE || color_type == 192cacb28f2d60858106e2819cc7d95a65e8bda890bAdam LesinskiPNG_COLOR_TYPE_GRAY) { 19398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski bpp = 1; 19498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { 19598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski bpp = 2; 196cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } else if (color_type == PNG_COLOR_TYPE_RGB || color_type == 197cacb28f2d60858106e2819cc7d95a65e8bda890bAdam LesinskiPNG_COLOR_TYPE_RGB_ALPHA) { 19898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski // We use a padding byte even when there is no alpha 19998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski bpp = 4; 20098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } else { 20198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski printf("Unknown color type %d.\n", color_type); 20298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 20398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 20498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski for (j = 0; j < h; j++) { 20598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski const png_byte* row = rows[j]; 20698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski for (i = 0; i < w; i++) { 20798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski rr = row[0]; 20898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski gg = row[1]; 20998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski bb = row[2]; 21098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski aa = row[3]; 21198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski row += bpp; 21298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 21398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (i == 0) { 21498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski printf("Row %d:", j); 21598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 21698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski switch (bpp) { 21798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski case 1: 21898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski printf(" (%d)", rr); 21998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski break; 22098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski case 2: 22198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski printf(" (%d %d", rr, gg); 22298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski break; 22398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski case 3: 22498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski printf(" (%d %d %d)", rr, gg, bb); 22598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski break; 22698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski case 4: 22798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski printf(" (%d %d %d %d)", rr, gg, bb, aa); 22898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski break; 22998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 23098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (i == (w - 1)) { 23198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski printf("\n"); 23298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 23398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 23498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 23598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski}*/ 23698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 237383db5ebcc3a4a615faf249bf4f126f42e80b82eTamas Berghammer#ifdef MAX 238383db5ebcc3a4a615faf249bf4f126f42e80b82eTamas Berghammer#undef MAX 239383db5ebcc3a4a615faf249bf4f126f42e80b82eTamas Berghammer#endif 240383db5ebcc3a4a615faf249bf4f126f42e80b82eTamas Berghammer#ifdef ABS 241383db5ebcc3a4a615faf249bf4f126f42e80b82eTamas Berghammer#undef ABS 242383db5ebcc3a4a615faf249bf4f126f42e80b82eTamas Berghammer#endif 243383db5ebcc3a4a615faf249bf4f126f42e80b82eTamas Berghammer 244cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski#define MAX(a, b) ((a) > (b) ? (a) : (b)) 245cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski#define ABS(a) ((a) < 0 ? -(a) : (a)) 24698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 247cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinskistatic void analyze_image(IDiagnostics* diag, const PngInfo& imageInfo, 248cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int grayscaleTolerance, png_colorp rgbPalette, 249cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_bytep alphaPalette, int* paletteEntries, 250cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski bool* hasTransparency, int* colorType, 25198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski png_bytepp outRows) { 252cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int w = imageInfo.width; 253cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int h = imageInfo.height; 254cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int i, j, rr, gg, bb, aa, idx; 255cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski uint32_t colors[256], col; 256cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int num_colors = 0; 257cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int maxGrayDeviation = 0; 258cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 259cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski bool isOpaque = true; 260cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski bool isPalette = true; 261cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski bool isGrayscale = true; 262cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 263cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Scan the entire image and determine if: 264cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // 1. Every pixel has R == G == B (grayscale) 265cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // 2. Every pixel has A == 255 (opaque) 266cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // 3. There are no more than 256 distinct RGBA colors 267cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 268cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (kDebug) { 269cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski printf("Initial image data:\n"); 270cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // dump_image(w, h, imageInfo.rows.data(), PNG_COLOR_TYPE_RGB_ALPHA); 271cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 272cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 273cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski for (j = 0; j < h; j++) { 274cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski const png_byte* row = imageInfo.rows[j]; 275cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_bytep out = outRows[j]; 276cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski for (i = 0; i < w; i++) { 277cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski rr = *row++; 278cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski gg = *row++; 279cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski bb = *row++; 280cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski aa = *row++; 281cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 282cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int odev = maxGrayDeviation; 283cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski maxGrayDeviation = MAX(ABS(rr - gg), maxGrayDeviation); 284cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski maxGrayDeviation = MAX(ABS(gg - bb), maxGrayDeviation); 285cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski maxGrayDeviation = MAX(ABS(bb - rr), maxGrayDeviation); 286cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (maxGrayDeviation > odev) { 287cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (kDebug) { 288cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski printf("New max dev. = %d at pixel (%d, %d) = (%d %d %d %d)\n", 289cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski maxGrayDeviation, i, j, rr, gg, bb, aa); 29098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 291cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 292cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 293cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Check if image is really grayscale 294cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (isGrayscale) { 295cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (rr != gg || rr != bb) { 296cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (kDebug) { 297cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski printf("Found a non-gray pixel at %d, %d = (%d %d %d %d)\n", i, j, 298cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski rr, gg, bb, aa); 299cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 300cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski isGrayscale = false; 30198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 302cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 303cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 304cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Check if image is really opaque 305cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (isOpaque) { 306cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (aa != 0xff) { 307cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (kDebug) { 308cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski printf("Found a non-opaque pixel at %d, %d = (%d %d %d %d)\n", i, j, 309cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski rr, gg, bb, aa); 310cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 311cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski isOpaque = false; 31298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 313cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 314cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 315cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Check if image is really <= 256 colors 316cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (isPalette) { 317cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski col = (uint32_t)((rr << 24) | (gg << 16) | (bb << 8) | aa); 318cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski bool match = false; 319cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski for (idx = 0; idx < num_colors; idx++) { 320cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (colors[idx] == col) { 321cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski match = true; 32298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski break; 323cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 32498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 32598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 326cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Write the palette index for the pixel to outRows optimistically 327cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // We might overwrite it later if we decide to encode as gray or 328cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // gray + alpha 329cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *out++ = idx; 330cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (!match) { 331cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (num_colors == 256) { 332cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (kDebug) { 333cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski printf("Found 257th color at %d, %d\n", i, j); 334cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 335cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski isPalette = false; 336cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } else { 337cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski colors[num_colors++] = col; 338cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 33998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 340cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 341cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 342cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 343cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 344cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *paletteEntries = 0; 345cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *hasTransparency = !isOpaque; 346cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int bpp = isOpaque ? 3 : 4; 347cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int paletteSize = w * h + bpp * num_colors; 348cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 349cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (kDebug) { 350cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski printf("isGrayscale = %s\n", isGrayscale ? "true" : "false"); 351cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski printf("isOpaque = %s\n", isOpaque ? "true" : "false"); 352cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski printf("isPalette = %s\n", isPalette ? "true" : "false"); 353cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski printf("Size w/ palette = %d, gray+alpha = %d, rgb(a) = %d\n", paletteSize, 354cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 2 * w * h, bpp * w * h); 355cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski printf("Max gray deviation = %d, tolerance = %d\n", maxGrayDeviation, 356cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski grayscaleTolerance); 357cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 358cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 359cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Choose the best color type for the image. 360cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // 1. Opaque gray - use COLOR_TYPE_GRAY at 1 byte/pixel 361cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // 2. Gray + alpha - use COLOR_TYPE_PALETTE if the number of distinct 362cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // combinations 363cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // is sufficiently small, otherwise use COLOR_TYPE_GRAY_ALPHA 364cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // 3. RGB(A) - use COLOR_TYPE_PALETTE if the number of distinct colors is 365cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // sufficiently 366cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // small, otherwise use COLOR_TYPE_RGB{_ALPHA} 367cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (isGrayscale) { 368cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (isOpaque) { 369cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *colorType = PNG_COLOR_TYPE_GRAY; // 1 byte/pixel 370cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } else { 371cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Use a simple heuristic to determine whether using a palette will 372cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // save space versus using gray + alpha for each pixel. 373cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // This doesn't take into account chunk overhead, filtering, LZ 374cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // compression, etc. 375cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (isPalette && (paletteSize < 2 * w * h)) { 376cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *colorType = PNG_COLOR_TYPE_PALETTE; // 1 byte/pixel + 4 bytes/color 377cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } else { 378cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *colorType = PNG_COLOR_TYPE_GRAY_ALPHA; // 2 bytes per pixel 379cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 380cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 381cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } else if (isPalette && (paletteSize < bpp * w * h)) { 382cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *colorType = PNG_COLOR_TYPE_PALETTE; 383cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } else { 384cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (maxGrayDeviation <= grayscaleTolerance) { 385ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski diag->Note(DiagMessage() << "forcing image to gray (max deviation = " 386cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski << maxGrayDeviation << ")"); 387cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *colorType = isOpaque ? PNG_COLOR_TYPE_GRAY : PNG_COLOR_TYPE_GRAY_ALPHA; 38898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } else { 389cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *colorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA; 39098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 391cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 39298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 393cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Perform postprocessing of the image or palette data based on the final 394cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // color type chosen 39598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 396cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (*colorType == PNG_COLOR_TYPE_PALETTE) { 397cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Create separate RGB and Alpha palettes and set the number of colors 398cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *paletteEntries = num_colors; 39998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 400cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Create the RGB and alpha palettes 401cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski for (int idx = 0; idx < num_colors; idx++) { 402cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski col = colors[idx]; 403cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski rgbPalette[idx].red = (png_byte)((col >> 24) & 0xff); 404cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski rgbPalette[idx].green = (png_byte)((col >> 16) & 0xff); 405cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski rgbPalette[idx].blue = (png_byte)((col >> 8) & 0xff); 406cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski alphaPalette[idx] = (png_byte)(col & 0xff); 40798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 408cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } else if (*colorType == PNG_COLOR_TYPE_GRAY || 409cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *colorType == PNG_COLOR_TYPE_GRAY_ALPHA) { 410cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // If the image is gray or gray + alpha, compact the pixels into outRows 411cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski for (j = 0; j < h; j++) { 412cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski const png_byte* row = imageInfo.rows[j]; 413cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_bytep out = outRows[j]; 414cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski for (i = 0; i < w; i++) { 415cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski rr = *row++; 416cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski gg = *row++; 417cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski bb = *row++; 418cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski aa = *row++; 419cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 420cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (isGrayscale) { 421cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *out++ = rr; 422cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } else { 423cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *out++ = (png_byte)(rr * 0.2126f + gg * 0.7152f + bb * 0.0722f); 42498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 425cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (!isOpaque) { 426cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *out++ = aa; 427cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 428cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 42998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 430cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 431cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski} 43298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 433cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinskistatic bool writePng(IDiagnostics* diag, png_structp writePtr, 434cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_infop infoPtr, PngInfo* info, int grayScaleTolerance) { 435cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (setjmp(png_jmpbuf(writePtr))) { 436ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski diag->Error(DiagMessage() << "failed to write png"); 437cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return false; 438cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 439cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 440cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski uint32_t width, height; 441cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int colorType, bitDepth, interlaceType, compressionType; 442cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 443cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_unknown_chunk unknowns[3]; 444cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski unknowns[0].data = nullptr; 445cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski unknowns[1].data = nullptr; 446cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski unknowns[2].data = nullptr; 447cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 448cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_bytepp outRows = 449cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski (png_bytepp)malloc((int)info->height * sizeof(png_bytep)); 450cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (outRows == (png_bytepp)0) { 451cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski printf("Can't allocate output buffer!\n"); 452cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski exit(1); 453cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 454cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski for (uint32_t i = 0; i < info->height; i++) { 455cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski outRows[i] = (png_bytep)malloc(2 * (int)info->width); 456cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (outRows[i] == (png_bytep)0) { 457cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski printf("Can't allocate output buffer!\n"); 458cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski exit(1); 459cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 460cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 461cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 462cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_set_compression_level(writePtr, Z_BEST_COMPRESSION); 463cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 464cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (kDebug) { 465ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski diag->Note(DiagMessage() << "writing image: w = " << info->width 466cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski << ", h = " << info->height); 467cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 468cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 469cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_color rgbPalette[256]; 470cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_byte alphaPalette[256]; 471cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski bool hasTransparency; 472cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int paletteEntries; 473cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 474cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski analyze_image(diag, *info, grayScaleTolerance, rgbPalette, alphaPalette, 475cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski &paletteEntries, &hasTransparency, &colorType, outRows); 476cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 477cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // If the image is a 9-patch, we need to preserve it as a ARGB file to make 478cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // sure the pixels will not be pre-dithered/clamped until we decide they are 479cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (info->is9Patch && 480cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski (colorType == PNG_COLOR_TYPE_RGB || colorType == PNG_COLOR_TYPE_GRAY || 481cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski colorType == PNG_COLOR_TYPE_PALETTE)) { 482cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski colorType = PNG_COLOR_TYPE_RGB_ALPHA; 483cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 484cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 485cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (kDebug) { 486cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski switch (colorType) { 487cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski case PNG_COLOR_TYPE_PALETTE: 488ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski diag->Note(DiagMessage() << "has " << paletteEntries << " colors" 489cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski << (hasTransparency ? " (with alpha)" : "") 490cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski << ", using PNG_COLOR_TYPE_PALLETTE"); 491cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski break; 492cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski case PNG_COLOR_TYPE_GRAY: 493ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski diag->Note(DiagMessage() 494cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski << "is opaque gray, using PNG_COLOR_TYPE_GRAY"); 495cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski break; 496cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski case PNG_COLOR_TYPE_GRAY_ALPHA: 497ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski diag->Note(DiagMessage() 498cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski << "is gray + alpha, using PNG_COLOR_TYPE_GRAY_ALPHA"); 499cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski break; 500cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski case PNG_COLOR_TYPE_RGB: 501ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski diag->Note(DiagMessage() << "is opaque RGB, using PNG_COLOR_TYPE_RGB"); 502cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski break; 503cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski case PNG_COLOR_TYPE_RGB_ALPHA: 504ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski diag->Note(DiagMessage() 505cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski << "is RGB + alpha, using PNG_COLOR_TYPE_RGB_ALPHA"); 506cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski break; 507cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 508cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 509cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 510cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_set_IHDR(writePtr, infoPtr, info->width, info->height, 8, colorType, 511cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, 512cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski PNG_FILTER_TYPE_DEFAULT); 513cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 514cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (colorType == PNG_COLOR_TYPE_PALETTE) { 515cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_set_PLTE(writePtr, infoPtr, rgbPalette, paletteEntries); 516cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (hasTransparency) { 517cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_set_tRNS(writePtr, infoPtr, alphaPalette, paletteEntries, 518cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski (png_color_16p)0); 519cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 520cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_set_filter(writePtr, 0, PNG_NO_FILTERS); 521cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } else { 522cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_set_filter(writePtr, 0, PNG_ALL_FILTERS); 523cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 524cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 525cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (info->is9Patch) { 526cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int chunkCount = 2 + (info->haveLayoutBounds ? 1 : 0); 527cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int pIndex = info->haveLayoutBounds ? 2 : 1; 528cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int bIndex = 1; 529cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int oIndex = 0; 530cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 531cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Chunks ordered thusly because older platforms depend on the base 9 patch 532cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // data being last 533cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_bytep chunkNames = info->haveLayoutBounds 534cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski ? (png_bytep) "npOl\0npLb\0npTc\0" 535cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski : (png_bytep) "npOl\0npTc"; 536cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 537cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // base 9 patch data 53898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (kDebug) { 539ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski diag->Note(DiagMessage() << "adding 9-patch info.."); 540cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 541cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski strcpy((char*)unknowns[pIndex].name, "npTc"); 542cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski unknowns[pIndex].data = (png_byte*)info->serialize9Patch(); 543cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski unknowns[pIndex].size = info->info9Patch.serializedSize(); 544cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // TODO: remove the check below when everything works 545cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski checkNinePatchSerialization(&info->info9Patch, unknowns[pIndex].data); 546cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 547cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // automatically generated 9 patch outline data 548cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int chunkSize = sizeof(png_uint_32) * 6; 549cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski strcpy((char*)unknowns[oIndex].name, "npOl"); 550cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski unknowns[oIndex].data = (png_byte*)calloc(chunkSize, 1); 551cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_byte outputData[chunkSize]; 552cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski memcpy(&outputData, &info->outlineInsetsLeft, 4 * sizeof(png_uint_32)); 553cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski ((float*)outputData)[4] = info->outlineRadius; 554cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski ((png_uint_32*)outputData)[5] = info->outlineAlpha; 555cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski memcpy(unknowns[oIndex].data, &outputData, chunkSize); 556cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski unknowns[oIndex].size = chunkSize; 557cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 558cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // optional optical inset / layout bounds data 559cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (info->haveLayoutBounds) { 560cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int chunkSize = sizeof(png_uint_32) * 4; 561cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski strcpy((char*)unknowns[bIndex].name, "npLb"); 562cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski unknowns[bIndex].data = (png_byte*)calloc(chunkSize, 1); 563cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski memcpy(unknowns[bIndex].data, &info->layoutBoundsLeft, chunkSize); 564cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski unknowns[bIndex].size = chunkSize; 565cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 566cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 567cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski for (int i = 0; i < chunkCount; i++) { 568cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski unknowns[i].location = PNG_HAVE_PLTE; 569cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 570cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_set_keep_unknown_chunks(writePtr, PNG_HANDLE_CHUNK_ALWAYS, chunkNames, 571cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski chunkCount); 572cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_set_unknown_chunks(writePtr, infoPtr, unknowns, chunkCount); 57398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 574cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski#if PNG_LIBPNG_VER < 10600 575cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Deal with unknown chunk location bug in 1.5.x and earlier. 576cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_set_unknown_chunk_location(writePtr, infoPtr, 0, PNG_HAVE_PLTE); 577cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (info->haveLayoutBounds) { 578cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_set_unknown_chunk_location(writePtr, infoPtr, 1, PNG_HAVE_PLTE); 57998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 580cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski#endif 581cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 582cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 583cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_write_info(writePtr, infoPtr); 584cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 585cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_bytepp rows; 586cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (colorType == PNG_COLOR_TYPE_RGB || 587cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski colorType == PNG_COLOR_TYPE_RGB_ALPHA) { 588cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (colorType == PNG_COLOR_TYPE_RGB) { 589cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_set_filler(writePtr, 0, PNG_FILLER_AFTER); 590cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 591cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski rows = info->rows.data(); 592cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } else { 593cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski rows = outRows; 594cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 595cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_write_image(writePtr, rows); 596cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 597cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (kDebug) { 598cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski printf("Final image data:\n"); 599cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // dump_image(info->width, info->height, rows, colorType); 600cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 601cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 602cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_write_end(writePtr, infoPtr); 603cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 604cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski for (uint32_t i = 0; i < info->height; i++) { 605cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski free(outRows[i]); 606cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 607cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski free(outRows); 608cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski free(unknowns[0].data); 609cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski free(unknowns[1].data); 610cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski free(unknowns[2].data); 611cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 612cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_get_IHDR(writePtr, infoPtr, &width, &height, &bitDepth, &colorType, 613cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski &interlaceType, &compressionType, nullptr); 614cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 615cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (kDebug) { 616ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski diag->Note(DiagMessage() << "image written: w = " << width 617cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski << ", h = " << height << ", d = " << bitDepth 618cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski << ", colors = " << colorType 619cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski << ", inter = " << interlaceType 620cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski << ", comp = " << compressionType); 621cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 622cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return true; 62398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski} 62498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 62598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskiconstexpr uint32_t kColorWhite = 0xffffffffu; 62698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskiconstexpr uint32_t kColorTick = 0xff000000u; 62798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskiconstexpr uint32_t kColorLayoutBoundsTick = 0xff0000ffu; 62898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 629cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinskienum class TickType { kNone, kTick, kLayoutBounds, kBoth }; 63098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 63198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskistatic TickType tickType(png_bytep p, bool transparent, const char** outError) { 632cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_uint_32 color = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); 63398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 634cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (transparent) { 635cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (p[3] == 0) { 636cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return TickType::kNone; 63798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 638cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (color == kColorLayoutBoundsTick) { 639cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return TickType::kLayoutBounds; 64098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 64198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (color == kColorTick) { 642cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return TickType::kTick; 64398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 644cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 645cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Error cases 646cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (p[3] != 0xff) { 647cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *outError = 648cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski "Frame pixels must be either solid or transparent " 649cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski "(not intermediate alphas)"; 650cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return TickType::kNone; 65198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 65298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 65398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski if (p[0] != 0 || p[1] != 0 || p[2] != 0) { 654cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *outError = "Ticks in transparent frame must be black or red"; 65598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 65698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return TickType::kTick; 657cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 658cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 659cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (p[3] != 0xFF) { 660cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *outError = "White frame must be a solid color (no alpha)"; 661cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 662cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (color == kColorWhite) { 663cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return TickType::kNone; 664cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 665cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (color == kColorTick) { 666cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return TickType::kTick; 667cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 668cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (color == kColorLayoutBoundsTick) { 669cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return TickType::kLayoutBounds; 670cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 671cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 672cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (p[0] != 0 || p[1] != 0 || p[2] != 0) { 673cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *outError = "Ticks in white frame must be black or red"; 674cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return TickType::kNone; 675cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 676cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return TickType::kTick; 67798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski} 67898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 679cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinskienum class TickState { kStart, kInside1, kOutside1 }; 68098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 681cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinskistatic bool getHorizontalTicks(png_bytep row, int width, bool transparent, 682cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski bool required, int32_t* outLeft, 683cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int32_t* outRight, const char** outError, 68498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski uint8_t* outDivs, bool multipleAllowed) { 685cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *outLeft = *outRight = -1; 686cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski TickState state = TickState::kStart; 687cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski bool found = false; 688cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 689cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski for (int i = 1; i < width - 1; i++) { 690cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (tickType(row + i * 4, transparent, outError) == TickType::kTick) { 691cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (state == TickState::kStart || 692cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski (state == TickState::kOutside1 && multipleAllowed)) { 693cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *outLeft = i - 1; 694cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *outRight = width - 2; 695cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski found = true; 696cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (outDivs != NULL) { 697cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *outDivs += 2; 69898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 699cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski state = TickState::kInside1; 700cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } else if (state == TickState::kOutside1) { 701cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *outError = "Can't have more than one marked region along edge"; 702cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *outLeft = i; 70398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return false; 704cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 705cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } else if (!*outError) { 706cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (state == TickState::kInside1) { 707cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // We're done with this div. Move on to the next. 708cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *outRight = i - 1; 709cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski outRight += 2; 710cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski outLeft += 2; 711cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski state = TickState::kOutside1; 712cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 713cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } else { 714cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *outLeft = i; 715cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return false; 71698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 717cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 718cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 719cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (required && !found) { 720cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *outError = "No marked region found along edge"; 721cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *outLeft = -1; 722cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return false; 723cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 724cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return true; 72598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski} 72698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 727cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinskistatic bool getVerticalTicks(png_bytepp rows, int offset, int height, 728cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski bool transparent, bool required, int32_t* outTop, 729cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int32_t* outBottom, const char** outError, 730cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski uint8_t* outDivs, bool multipleAllowed) { 731cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *outTop = *outBottom = -1; 732cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski TickState state = TickState::kStart; 733cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski bool found = false; 734cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 735cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski for (int i = 1; i < height - 1; i++) { 736cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (tickType(rows[i] + offset, transparent, outError) == TickType::kTick) { 737cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (state == TickState::kStart || 738cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski (state == TickState::kOutside1 && multipleAllowed)) { 739cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *outTop = i - 1; 740cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *outBottom = height - 2; 741cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski found = true; 742cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (outDivs != NULL) { 743cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *outDivs += 2; 74498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 745cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski state = TickState::kInside1; 746cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } else if (state == TickState::kOutside1) { 747cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *outError = "Can't have more than one marked region along edge"; 748cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *outTop = i; 74998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski return false; 750cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 751cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } else if (!*outError) { 752cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (state == TickState::kInside1) { 753cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // We're done with this div. Move on to the next. 754cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *outBottom = i - 1; 755cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski outTop += 2; 756cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski outBottom += 2; 757cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski state = TickState::kOutside1; 758cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 759cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } else { 760cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *outTop = i; 761cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return false; 76298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 763cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 76498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 765cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (required && !found) { 766cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *outError = "No marked region found along edge"; 767cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *outTop = -1; 768cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return false; 769cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 770cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return true; 771cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski} 77298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 773cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinskistatic bool getHorizontalLayoutBoundsTicks(png_bytep row, int width, 774cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski bool transparent, 775cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski bool /* required */, 776cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int32_t* outLeft, int32_t* outRight, 777cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski const char** outError) { 778cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *outLeft = *outRight = 0; 779cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 780cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Look for left tick 781cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (tickType(row + 4, transparent, outError) == TickType::kLayoutBounds) { 782cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Starting with a layout padding tick 783cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int i = 1; 784cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski while (i < width - 1) { 785cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski (*outLeft)++; 786cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski i++; 787cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (tickType(row + i * 4, transparent, outError) != 788cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski TickType::kLayoutBounds) { 789cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski break; 790cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 791cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 792cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 793cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 794cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Look for right tick 795cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (tickType(row + (width - 2) * 4, transparent, outError) == 796cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski TickType::kLayoutBounds) { 797cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Ending with a layout padding tick 798cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int i = width - 2; 799cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski while (i > 1) { 800cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski (*outRight)++; 801cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski i--; 802cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (tickType(row + i * 4, transparent, outError) != 803cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski TickType::kLayoutBounds) { 804cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski break; 805cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 806cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 807cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 808cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return true; 80998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski} 81098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 811cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinskistatic bool getVerticalLayoutBoundsTicks(png_bytepp rows, int offset, 812cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int height, bool transparent, 813cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski bool /* required */, int32_t* outTop, 814cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int32_t* outBottom, 81598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski const char** outError) { 816cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *outTop = *outBottom = 0; 817cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 818cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Look for top tick 819cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (tickType(rows[1] + offset, transparent, outError) == 820cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski TickType::kLayoutBounds) { 821cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Starting with a layout padding tick 822cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int i = 1; 823cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski while (i < height - 1) { 824cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski (*outTop)++; 825cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski i++; 826cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (tickType(rows[i] + offset, transparent, outError) != 827cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski TickType::kLayoutBounds) { 828cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski break; 829cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 830cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 831cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 832cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 833cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Look for bottom tick 834cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (tickType(rows[height - 2] + offset, transparent, outError) == 835cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski TickType::kLayoutBounds) { 836cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Ending with a layout padding tick 837cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int i = height - 2; 838cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski while (i > 1) { 839cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski (*outBottom)++; 840cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski i--; 841cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (tickType(rows[i] + offset, transparent, outError) != 842cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski TickType::kLayoutBounds) { 843cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski break; 844cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 845cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 846cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 847cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return true; 84898aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski} 84998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 850cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinskistatic void findMaxOpacity(png_bytepp rows, int startX, int startY, int endX, 851cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int endY, int dX, int dY, int* outInset) { 852cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski uint8_t maxOpacity = 0; 853cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int inset = 0; 854cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *outInset = 0; 855cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski for (int x = startX, y = startY; x != endX && y != endY; 856cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski x += dX, y += dY, inset++) { 857cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_byte* color = rows[y] + x * 4; 858cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski uint8_t opacity = color[3]; 859cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (opacity > maxOpacity) { 860cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski maxOpacity = opacity; 861cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *outInset = inset; 862cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 863cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (opacity == 0xff) return; 864cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 86598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski} 86698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 86798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskistatic uint8_t maxAlphaOverRow(png_bytep row, int startX, int endX) { 868cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski uint8_t maxAlpha = 0; 869cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski for (int x = startX; x < endX; x++) { 870cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski uint8_t alpha = (row + x * 4)[3]; 871cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (alpha > maxAlpha) maxAlpha = alpha; 872cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 873cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return maxAlpha; 87498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski} 87598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 876cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinskistatic uint8_t maxAlphaOverCol(png_bytepp rows, int offsetX, int startY, 877cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int endY) { 878cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski uint8_t maxAlpha = 0; 879cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski for (int y = startY; y < endY; y++) { 880cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski uint8_t alpha = (rows[y] + offsetX * 4)[3]; 881cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (alpha > maxAlpha) maxAlpha = alpha; 882cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 883cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return maxAlpha; 88498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski} 88598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 88698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskistatic void getOutline(PngInfo* image) { 887cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int midX = image->width / 2; 888cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int midY = image->height / 2; 889cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int endX = image->width - 2; 890cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int endY = image->height - 2; 891cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 892cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // find left and right extent of nine patch content on center row 893cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (image->width > 4) { 894cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski findMaxOpacity(image->rows.data(), 1, midY, midX, -1, 1, 0, 895cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski &image->outlineInsetsLeft); 896cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski findMaxOpacity(image->rows.data(), endX, midY, midX, -1, -1, 0, 897cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski &image->outlineInsetsRight); 898cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } else { 899cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski image->outlineInsetsLeft = 0; 900cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski image->outlineInsetsRight = 0; 901cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 902cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 903cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // find top and bottom extent of nine patch content on center column 904cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (image->height > 4) { 905cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski findMaxOpacity(image->rows.data(), midX, 1, -1, midY, 0, 1, 906cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski &image->outlineInsetsTop); 907cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski findMaxOpacity(image->rows.data(), midX, endY, -1, midY, 0, -1, 908cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski &image->outlineInsetsBottom); 909cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } else { 910cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski image->outlineInsetsTop = 0; 911cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski image->outlineInsetsBottom = 0; 912cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 913cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 914cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int innerStartX = 1 + image->outlineInsetsLeft; 915cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int innerStartY = 1 + image->outlineInsetsTop; 916cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int innerEndX = endX - image->outlineInsetsRight; 917cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int innerEndY = endY - image->outlineInsetsBottom; 918cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int innerMidX = (innerEndX + innerStartX) / 2; 919cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int innerMidY = (innerEndY + innerStartY) / 2; 920cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 921cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // assuming the image is a round rect, compute the radius by marching 922cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // diagonally from the top left corner towards the center 923cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski image->outlineAlpha = std::max( 924cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski maxAlphaOverRow(image->rows[innerMidY], innerStartX, innerEndX), 925cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski maxAlphaOverCol(image->rows.data(), innerMidX, innerStartY, innerStartY)); 926cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 927cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int diagonalInset = 0; 928cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski findMaxOpacity(image->rows.data(), innerStartX, innerStartY, innerMidX, 929cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski innerMidY, 1, 1, &diagonalInset); 930cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 931cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski /* Determine source radius based upon inset: 932cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski * sqrt(r^2 + r^2) = sqrt(i^2 + i^2) + r 933cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski * sqrt(2) * r = sqrt(2) * i + r 934cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski * (sqrt(2) - 1) * r = sqrt(2) * i 935cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski * r = sqrt(2) / (sqrt(2) - 1) * i 936cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski */ 937cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski image->outlineRadius = 3.4142f * diagonalInset; 938cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 939cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (kDebug) { 940cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski printf("outline insets %d %d %d %d, rad %f, alpha %x\n", 941cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski image->outlineInsetsLeft, image->outlineInsetsTop, 942cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski image->outlineInsetsRight, image->outlineInsetsBottom, 943cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski image->outlineRadius, image->outlineAlpha); 944cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 94598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski} 94698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 947cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinskistatic uint32_t getColor(png_bytepp rows, int left, int top, int right, 948cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int bottom) { 949cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_bytep color = rows[top] + left * 4; 95098aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 951cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (left > right || top > bottom) { 952cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return android::Res_png_9patch::TRANSPARENT_COLOR; 953cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 95498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 955cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski while (top <= bottom) { 956cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski for (int i = left; i <= right; i++) { 957cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_bytep p = rows[top] + i * 4; 958cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (color[3] == 0) { 959cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (p[3] != 0) { 960cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return android::Res_png_9patch::NO_COLOR; 96198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 962cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } else if (p[0] != color[0] || p[1] != color[1] || p[2] != color[2] || 963cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski p[3] != color[3]) { 964cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return android::Res_png_9patch::NO_COLOR; 965cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 966cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 967cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski top++; 968cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 969cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 970cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (color[3] == 0) { 971cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return android::Res_png_9patch::TRANSPARENT_COLOR; 972cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 973cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return (color[3] << 24) | (color[0] << 16) | (color[1] << 8) | color[2]; 97498aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski} 97598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 97698aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskistatic bool do9Patch(PngInfo* image, std::string* outError) { 977cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski image->is9Patch = true; 978cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 979cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int W = image->width; 980cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int H = image->height; 981cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int i, j; 982cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 983cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski const int maxSizeXDivs = W * sizeof(int32_t); 984cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski const int maxSizeYDivs = H * sizeof(int32_t); 985cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int32_t* xDivs = image->xDivs = new int32_t[W]; 986cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int32_t* yDivs = image->yDivs = new int32_t[H]; 987cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski uint8_t numXDivs = 0; 988cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski uint8_t numYDivs = 0; 989cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 990cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int8_t numColors; 991cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int numRows; 992cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int numCols; 993cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int top; 994cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int left; 995cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int right; 996cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int bottom; 997cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski memset(xDivs, -1, maxSizeXDivs); 998cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski memset(yDivs, -1, maxSizeYDivs); 999cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski image->info9Patch.paddingLeft = image->info9Patch.paddingRight = -1; 1000cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski image->info9Patch.paddingTop = image->info9Patch.paddingBottom = -1; 1001cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski image->layoutBoundsLeft = image->layoutBoundsRight = 0; 1002cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski image->layoutBoundsTop = image->layoutBoundsBottom = 0; 1003cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1004cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_bytep p = image->rows[0]; 1005cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski bool transparent = p[3] == 0; 1006cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski bool hasColor = false; 1007cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1008cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski const char* errorMsg = nullptr; 1009cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int errorPixel = -1; 1010cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski const char* errorEdge = nullptr; 1011cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1012cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski int colorIndex = 0; 1013cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski std::vector<png_bytep> newRows; 1014cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1015cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Validate size... 1016cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (W < 3 || H < 3) { 1017cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski errorMsg = "Image must be at least 3x3 (1x1 without frame) pixels"; 1018cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski goto getout; 1019cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 1020cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1021cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Validate frame... 1022cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (!transparent && 1023cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski (p[0] != 0xFF || p[1] != 0xFF || p[2] != 0xFF || p[3] != 0xFF)) { 1024cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski errorMsg = "Must have one-pixel frame that is either transparent or white"; 1025cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski goto getout; 1026cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 1027cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1028cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Find left and right of sizing areas... 1029cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (!getHorizontalTicks(p, W, transparent, true, &xDivs[0], &xDivs[1], 1030cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski &errorMsg, &numXDivs, true)) { 1031cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski errorPixel = xDivs[0]; 1032cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski errorEdge = "top"; 1033cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski goto getout; 1034cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 1035cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1036cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Find top and bottom of sizing areas... 1037cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (!getVerticalTicks(image->rows.data(), 0, H, transparent, true, &yDivs[0], 1038cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski &yDivs[1], &errorMsg, &numYDivs, true)) { 1039cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski errorPixel = yDivs[0]; 1040cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski errorEdge = "left"; 1041cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski goto getout; 1042cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 1043cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1044cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Copy patch size data into image... 1045cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski image->info9Patch.numXDivs = numXDivs; 1046cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski image->info9Patch.numYDivs = numYDivs; 1047cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1048cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Find left and right of padding area... 1049cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (!getHorizontalTicks(image->rows[H - 1], W, transparent, false, 1050cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski &image->info9Patch.paddingLeft, 1051cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski &image->info9Patch.paddingRight, &errorMsg, nullptr, 1052cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski false)) { 1053cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski errorPixel = image->info9Patch.paddingLeft; 1054cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski errorEdge = "bottom"; 1055cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski goto getout; 1056cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 1057cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1058cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Find top and bottom of padding area... 1059cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (!getVerticalTicks(image->rows.data(), (W - 1) * 4, H, transparent, false, 1060cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski &image->info9Patch.paddingTop, 1061cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski &image->info9Patch.paddingBottom, &errorMsg, nullptr, 1062cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski false)) { 1063cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski errorPixel = image->info9Patch.paddingTop; 1064cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski errorEdge = "right"; 1065cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski goto getout; 1066cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 1067cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1068cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Find left and right of layout padding... 1069cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski getHorizontalLayoutBoundsTicks(image->rows[H - 1], W, transparent, false, 1070cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski &image->layoutBoundsLeft, 1071cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski &image->layoutBoundsRight, &errorMsg); 1072cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1073cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski getVerticalLayoutBoundsTicks(image->rows.data(), (W - 1) * 4, H, transparent, 1074cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski false, &image->layoutBoundsTop, 1075cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski &image->layoutBoundsBottom, &errorMsg); 1076cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1077cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski image->haveLayoutBounds = 1078cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski image->layoutBoundsLeft != 0 || image->layoutBoundsRight != 0 || 1079cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski image->layoutBoundsTop != 0 || image->layoutBoundsBottom != 0; 1080cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1081cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (image->haveLayoutBounds) { 1082cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (kDebug) { 1083cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski printf("layoutBounds=%d %d %d %d\n", image->layoutBoundsLeft, 1084cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski image->layoutBoundsTop, image->layoutBoundsRight, 1085cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski image->layoutBoundsBottom); 1086cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 1087cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 1088cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1089cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // use opacity of pixels to estimate the round rect outline 1090cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski getOutline(image); 1091cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1092cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // If padding is not yet specified, take values from size. 1093cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (image->info9Patch.paddingLeft < 0) { 1094cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski image->info9Patch.paddingLeft = xDivs[0]; 1095cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski image->info9Patch.paddingRight = W - 2 - xDivs[1]; 1096cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } else { 1097cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Adjust value to be correct! 1098cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski image->info9Patch.paddingRight = W - 2 - image->info9Patch.paddingRight; 1099cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 1100cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (image->info9Patch.paddingTop < 0) { 1101cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski image->info9Patch.paddingTop = yDivs[0]; 1102cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski image->info9Patch.paddingBottom = H - 2 - yDivs[1]; 1103cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } else { 1104cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Adjust value to be correct! 1105cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski image->info9Patch.paddingBottom = H - 2 - image->info9Patch.paddingBottom; 1106cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 1107cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1108cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski /* if (kDebug) { 1109cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski printf("Size ticks for %s: x0=%d, x1=%d, y0=%d, y1=%d\n", imageName, 1110cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski xDivs[0], xDivs[1], 1111cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski yDivs[0], yDivs[1]); 1112cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski printf("padding ticks for %s: l=%d, r=%d, t=%d, b=%d\n", imageName, 1113cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski image->info9Patch.paddingLeft, image->info9Patch.paddingRight, 1114cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski image->info9Patch.paddingTop, 1115cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski image->info9Patch.paddingBottom); 1116cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski }*/ 1117cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1118cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Remove frame from image. 1119cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski newRows.resize(H - 2); 1120cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski for (i = 0; i < H - 2; i++) { 1121cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski newRows[i] = image->rows[i + 1]; 1122cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski memmove(newRows[i], newRows[i] + 4, (W - 2) * 4); 1123cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 1124cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski image->rows.swap(newRows); 1125cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1126cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski image->width -= 2; 1127cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski W = image->width; 1128cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski image->height -= 2; 1129cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski H = image->height; 1130cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1131cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Figure out the number of rows and columns in the N-patch 1132cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski numCols = numXDivs + 1; 1133cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (xDivs[0] == 0) { // Column 1 is strechable 1134cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski numCols--; 1135cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 1136cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (xDivs[numXDivs - 1] == W) { 1137cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski numCols--; 1138cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 1139cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski numRows = numYDivs + 1; 1140cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (yDivs[0] == 0) { // Row 1 is strechable 1141cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski numRows--; 1142cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 1143cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (yDivs[numYDivs - 1] == H) { 1144cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski numRows--; 1145cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 1146cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1147cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Make sure the amount of rows and columns will fit in the number of 1148cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // colors we can use in the 9-patch format. 1149cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (numRows * numCols > 0x7F) { 1150cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski errorMsg = "Too many rows and columns in 9-patch perimeter"; 1151cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski goto getout; 1152cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 1153cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1154cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski numColors = numRows * numCols; 1155cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski image->info9Patch.numColors = numColors; 1156cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski image->colors.resize(numColors); 1157cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1158cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Fill in color information for each patch. 1159cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1160cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski uint32_t c; 1161cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski top = 0; 1162cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1163cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // The first row always starts with the top being at y=0 and the bottom 1164cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // being either yDivs[1] (if yDivs[0]=0) of yDivs[0]. In the former case 1165cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // the first row is stretchable along the Y axis, otherwise it is fixed. 1166cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // The last row always ends with the bottom being bitmap.height and the top 1167cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // being either yDivs[numYDivs-2] (if yDivs[numYDivs-1]=bitmap.height) or 1168cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // yDivs[numYDivs-1]. In the former case the last row is stretchable along 1169cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // the Y axis, otherwise it is fixed. 1170cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // 1171cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // The first and last columns are similarly treated with respect to the X 1172cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // axis. 1173cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // 1174cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // The above is to help explain some of the special casing that goes on the 1175cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // code below. 1176cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1177cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // The initial yDiv and whether the first row is considered stretchable or 1178cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // not depends on whether yDiv[0] was zero or not. 1179cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski for (j = (yDivs[0] == 0 ? 1 : 0); j <= numYDivs && top < H; j++) { 1180cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (j == numYDivs) { 1181cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski bottom = H; 118298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } else { 1183cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski bottom = yDivs[j]; 1184cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 1185cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski left = 0; 1186cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // The initial xDiv and whether the first column is considered 1187cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // stretchable or not depends on whether xDiv[0] was zero or not. 1188cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski for (i = xDivs[0] == 0 ? 1 : 0; i <= numXDivs && left < W; i++) { 1189cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (i == numXDivs) { 1190cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski right = W; 1191cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } else { 1192cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski right = xDivs[i]; 1193cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 1194cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski c = getColor(image->rows.data(), left, top, right - 1, bottom - 1); 1195cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski image->colors[colorIndex++] = c; 1196cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (kDebug) { 1197cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (c != android::Res_png_9patch::NO_COLOR) { 1198cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski hasColor = true; 119998aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 1200cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 1201cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski left = right; 120298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 1203cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski top = bottom; 1204cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 120598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 1206cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski assert(colorIndex == numColors); 120798aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 1208cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (kDebug && hasColor) { 1209cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski for (i = 0; i < numColors; i++) { 1210cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (i == 0) printf("Colors:\n"); 1211cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski printf(" #%08x", image->colors[i]); 1212cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (i == numColors - 1) printf("\n"); 121398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski } 1214cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 121598aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskigetout: 1216cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (errorMsg) { 1217cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski std::stringstream err; 1218cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski err << "9-patch malformed: " << errorMsg; 1219cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (errorEdge) { 1220cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski err << "." << std::endl; 1221cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (errorPixel >= 0) { 1222cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski err << "Found at pixel #" << errorPixel << " along " << errorEdge 1223cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski << " edge"; 1224cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } else { 1225cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski err << "Found along " << errorEdge << " edge"; 1226cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 1227cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 1228cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski *outError = err.str(); 1229cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return false; 1230cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 1231cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return true; 123298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski} 123398aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 1234cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinskibool Png::process(const Source& source, std::istream* input, 1235cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski BigBuffer* outBuffer, const PngOptions& options) { 1236cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_byte signature[kPngSignatureSize]; 1237cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1238cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Read the PNG signature first. 1239cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (!input->read(reinterpret_cast<char*>(signature), kPngSignatureSize)) { 1240ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski mDiag->Error(DiagMessage() << strerror(errno)); 1241cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return false; 1242cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 1243cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1244cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // If the PNG signature doesn't match, bail early. 1245cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (png_sig_cmp(signature, 0, kPngSignatureSize) != 0) { 1246ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski mDiag->Error(DiagMessage() << "not a valid png file"); 1247cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return false; 1248cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 1249cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1250cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski bool result = false; 1251cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_structp readPtr = nullptr; 1252cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_infop infoPtr = nullptr; 1253cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_structp writePtr = nullptr; 1254cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_infop writeInfoPtr = nullptr; 1255cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski PngInfo pngInfo = {}; 1256cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1257cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski readPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, nullptr, nullptr); 1258cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (!readPtr) { 1259ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski mDiag->Error(DiagMessage() << "failed to allocate read ptr"); 1260cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski goto bail; 1261cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 1262cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1263cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski infoPtr = png_create_info_struct(readPtr); 1264cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (!infoPtr) { 1265ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski mDiag->Error(DiagMessage() << "failed to allocate info ptr"); 1266cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski goto bail; 1267cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 1268cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1269cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_set_error_fn(readPtr, reinterpret_cast<png_voidp>(mDiag), nullptr, 1270cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski logWarning); 1271cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1272cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Set the read function to read from std::istream. 1273cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_set_read_fn(readPtr, (png_voidp)input, readDataFromStream); 1274cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1275cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (!readPng(mDiag, readPtr, infoPtr, &pngInfo)) { 1276cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski goto bail; 1277cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 1278cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1279ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski if (util::EndsWith(source.path, ".9.png")) { 1280cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski std::string errorMsg; 1281cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (!do9Patch(&pngInfo, &errorMsg)) { 1282ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski mDiag->Error(DiagMessage() << errorMsg); 1283cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski goto bail; 1284cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 1285cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 1286cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1287cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski writePtr = 1288cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, nullptr, nullptr); 1289cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (!writePtr) { 1290ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski mDiag->Error(DiagMessage() << "failed to allocate write ptr"); 1291cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski goto bail; 1292cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 1293cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1294cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski writeInfoPtr = png_create_info_struct(writePtr); 1295cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (!writeInfoPtr) { 1296ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski mDiag->Error(DiagMessage() << "failed to allocate write info ptr"); 1297cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski goto bail; 1298cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 1299cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1300cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_set_error_fn(writePtr, nullptr, nullptr, logWarning); 1301cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1302cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski // Set the write function to write to std::ostream. 1303cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_set_write_fn(writePtr, (png_voidp)outBuffer, writeDataToStream, 1304cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski flushDataToStream); 1305cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1306cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (!writePng(mDiag, writePtr, writeInfoPtr, &pngInfo, 1307ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski options.grayscale_tolerance)) { 1308cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski goto bail; 1309cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 1310cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1311cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski result = true; 131298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinskibail: 1313cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (readPtr) { 1314cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_destroy_read_struct(&readPtr, &infoPtr, nullptr); 1315cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 1316cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski 1317cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski if (writePtr) { 1318cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski png_destroy_write_struct(&writePtr, &writeInfoPtr); 1319cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski } 1320cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski return result; 132198aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski} 132298aa3ad6e46e3b0270785c8b3f9798e37e8af140Adam Lesinski 1323cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski} // namespace aapt 1324