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