Home | History | Annotate | Download | only in compile
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "util/BigBuffer.h"
     18 #include "Png.h"
     19 #include "Source.h"
     20 #include "util/Util.h"
     21 
     22 #include <androidfw/ResourceTypes.h>
     23 #include <iostream>
     24 #include <png.h>
     25 #include <sstream>
     26 #include <string>
     27 #include <vector>
     28 #include <zlib.h>
     29 
     30 namespace aapt {
     31 
     32 constexpr bool kDebug = false;
     33 constexpr size_t kPngSignatureSize = 8u;
     34 
     35 struct PngInfo {
     36     ~PngInfo() {
     37         for (png_bytep row : rows) {
     38             if (row != nullptr) {
     39                 delete[] row;
     40             }
     41         }
     42 
     43         delete[] xDivs;
     44         delete[] yDivs;
     45     }
     46 
     47     void* serialize9Patch() {
     48         void* serialized = android::Res_png_9patch::serialize(info9Patch, xDivs, yDivs,
     49                                                               colors.data());
     50         reinterpret_cast<android::Res_png_9patch*>(serialized)->deviceToFile();
     51         return serialized;
     52     }
     53 
     54     uint32_t width = 0;
     55     uint32_t height = 0;
     56     std::vector<png_bytep> rows;
     57 
     58     bool is9Patch = false;
     59     android::Res_png_9patch info9Patch;
     60     int32_t* xDivs = nullptr;
     61     int32_t* yDivs = nullptr;
     62     std::vector<uint32_t> colors;
     63 
     64     // Layout padding.
     65     bool haveLayoutBounds = false;
     66     int32_t layoutBoundsLeft;
     67     int32_t layoutBoundsTop;
     68     int32_t layoutBoundsRight;
     69     int32_t layoutBoundsBottom;
     70 
     71     // Round rect outline description.
     72     int32_t outlineInsetsLeft;
     73     int32_t outlineInsetsTop;
     74     int32_t outlineInsetsRight;
     75     int32_t outlineInsetsBottom;
     76     float outlineRadius;
     77     uint8_t outlineAlpha;
     78 };
     79 
     80 static void readDataFromStream(png_structp readPtr, png_bytep data, png_size_t length) {
     81     std::istream* input = reinterpret_cast<std::istream*>(png_get_io_ptr(readPtr));
     82     if (!input->read(reinterpret_cast<char*>(data), length)) {
     83         png_error(readPtr, strerror(errno));
     84     }
     85 }
     86 
     87 static void writeDataToStream(png_structp writePtr, png_bytep data, png_size_t length) {
     88     BigBuffer* outBuffer = reinterpret_cast<BigBuffer*>(png_get_io_ptr(writePtr));
     89     png_bytep buf = outBuffer->nextBlock<png_byte>(length);
     90     memcpy(buf, data, length);
     91 }
     92 
     93 static void flushDataToStream(png_structp /*writePtr*/) {
     94 }
     95 
     96 static void logWarning(png_structp readPtr, png_const_charp warningMessage) {
     97     IDiagnostics* diag = reinterpret_cast<IDiagnostics*>(png_get_error_ptr(readPtr));
     98     diag->warn(DiagMessage() << warningMessage);
     99 }
    100 
    101 
    102 static bool readPng(IDiagnostics* diag, png_structp readPtr, png_infop infoPtr, PngInfo* outInfo) {
    103     if (setjmp(png_jmpbuf(readPtr))) {
    104         diag->error(DiagMessage() << "failed reading png");
    105         return false;
    106     }
    107 
    108     png_set_sig_bytes(readPtr, kPngSignatureSize);
    109     png_read_info(readPtr, infoPtr);
    110 
    111     int colorType, bitDepth, interlaceType, compressionType;
    112     png_get_IHDR(readPtr, infoPtr, &outInfo->width, &outInfo->height, &bitDepth, &colorType,
    113                  &interlaceType, &compressionType, nullptr);
    114 
    115     if (colorType == PNG_COLOR_TYPE_PALETTE) {
    116         png_set_palette_to_rgb(readPtr);
    117     }
    118 
    119     if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) {
    120         png_set_expand_gray_1_2_4_to_8(readPtr);
    121     }
    122 
    123     if (png_get_valid(readPtr, infoPtr, PNG_INFO_tRNS)) {
    124         png_set_tRNS_to_alpha(readPtr);
    125     }
    126 
    127     if (bitDepth == 16) {
    128         png_set_strip_16(readPtr);
    129     }
    130 
    131     if (!(colorType & PNG_COLOR_MASK_ALPHA)) {
    132         png_set_add_alpha(readPtr, 0xFF, PNG_FILLER_AFTER);
    133     }
    134 
    135     if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
    136         png_set_gray_to_rgb(readPtr);
    137     }
    138 
    139     png_set_interlace_handling(readPtr);
    140     png_read_update_info(readPtr, infoPtr);
    141 
    142     const uint32_t rowBytes = png_get_rowbytes(readPtr, infoPtr);
    143     outInfo->rows.resize(outInfo->height);
    144     for (size_t i = 0; i < outInfo->height; i++) {
    145         outInfo->rows[i] = new png_byte[rowBytes];
    146     }
    147 
    148     png_read_image(readPtr, outInfo->rows.data());
    149     png_read_end(readPtr, infoPtr);
    150     return true;
    151 }
    152 
    153 static void checkNinePatchSerialization(android::Res_png_9patch* inPatch,  void* data) {
    154     size_t patchSize = inPatch->serializedSize();
    155     void* newData = malloc(patchSize);
    156     memcpy(newData, data, patchSize);
    157     android::Res_png_9patch* outPatch = inPatch->deserialize(newData);
    158     outPatch->fileToDevice();
    159     // deserialization is done in place, so outPatch == newData
    160     assert(outPatch == newData);
    161     assert(outPatch->numXDivs == inPatch->numXDivs);
    162     assert(outPatch->numYDivs == inPatch->numYDivs);
    163     assert(outPatch->paddingLeft == inPatch->paddingLeft);
    164     assert(outPatch->paddingRight == inPatch->paddingRight);
    165     assert(outPatch->paddingTop == inPatch->paddingTop);
    166     assert(outPatch->paddingBottom == inPatch->paddingBottom);
    167 /*    for (int i = 0; i < outPatch->numXDivs; i++) {
    168         assert(outPatch->getXDivs()[i] == inPatch->getXDivs()[i]);
    169     }
    170     for (int i = 0; i < outPatch->numYDivs; i++) {
    171         assert(outPatch->getYDivs()[i] == inPatch->getYDivs()[i]);
    172     }
    173     for (int i = 0; i < outPatch->numColors; i++) {
    174         assert(outPatch->getColors()[i] == inPatch->getColors()[i]);
    175     }*/
    176     free(newData);
    177 }
    178 
    179 /*static void dump_image(int w, int h, const png_byte* const* rows, int color_type) {
    180     int i, j, rr, gg, bb, aa;
    181 
    182     int bpp;
    183     if (color_type == PNG_COLOR_TYPE_PALETTE || color_type == PNG_COLOR_TYPE_GRAY) {
    184         bpp = 1;
    185     } else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
    186         bpp = 2;
    187     } else if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
    188         // We use a padding byte even when there is no alpha
    189         bpp = 4;
    190     } else {
    191         printf("Unknown color type %d.\n", color_type);
    192     }
    193 
    194     for (j = 0; j < h; j++) {
    195         const png_byte* row = rows[j];
    196         for (i = 0; i < w; i++) {
    197             rr = row[0];
    198             gg = row[1];
    199             bb = row[2];
    200             aa = row[3];
    201             row += bpp;
    202 
    203             if (i == 0) {
    204                 printf("Row %d:", j);
    205             }
    206             switch (bpp) {
    207             case 1:
    208                 printf(" (%d)", rr);
    209                 break;
    210             case 2:
    211                 printf(" (%d %d", rr, gg);
    212                 break;
    213             case 3:
    214                 printf(" (%d %d %d)", rr, gg, bb);
    215                 break;
    216             case 4:
    217                 printf(" (%d %d %d %d)", rr, gg, bb, aa);
    218                 break;
    219             }
    220             if (i == (w - 1)) {
    221                 printf("\n");
    222             }
    223         }
    224     }
    225 }*/
    226 
    227 #define MAX(a,b) ((a)>(b)?(a):(b))
    228 #define ABS(a)   ((a)<0?-(a):(a))
    229 
    230 static void analyze_image(IDiagnostics* diag, const PngInfo& imageInfo, int grayscaleTolerance,
    231                           png_colorp rgbPalette, png_bytep alphaPalette,
    232                           int *paletteEntries, bool *hasTransparency, int *colorType,
    233                           png_bytepp outRows) {
    234     int w = imageInfo.width;
    235     int h = imageInfo.height;
    236     int i, j, rr, gg, bb, aa, idx;
    237     uint32_t colors[256], col;
    238     int num_colors = 0;
    239     int maxGrayDeviation = 0;
    240 
    241     bool isOpaque = true;
    242     bool isPalette = true;
    243     bool isGrayscale = true;
    244 
    245     // Scan the entire image and determine if:
    246     // 1. Every pixel has R == G == B (grayscale)
    247     // 2. Every pixel has A == 255 (opaque)
    248     // 3. There are no more than 256 distinct RGBA colors
    249 
    250     if (kDebug) {
    251         printf("Initial image data:\n");
    252         //dump_image(w, h, imageInfo.rows.data(), PNG_COLOR_TYPE_RGB_ALPHA);
    253     }
    254 
    255     for (j = 0; j < h; j++) {
    256         const png_byte* row = imageInfo.rows[j];
    257         png_bytep out = outRows[j];
    258         for (i = 0; i < w; i++) {
    259             rr = *row++;
    260             gg = *row++;
    261             bb = *row++;
    262             aa = *row++;
    263 
    264             int odev = maxGrayDeviation;
    265             maxGrayDeviation = MAX(ABS(rr - gg), maxGrayDeviation);
    266             maxGrayDeviation = MAX(ABS(gg - bb), maxGrayDeviation);
    267             maxGrayDeviation = MAX(ABS(bb - rr), maxGrayDeviation);
    268             if (maxGrayDeviation > odev) {
    269                 if (kDebug) {
    270                     printf("New max dev. = %d at pixel (%d, %d) = (%d %d %d %d)\n",
    271                             maxGrayDeviation, i, j, rr, gg, bb, aa);
    272                 }
    273             }
    274 
    275             // Check if image is really grayscale
    276             if (isGrayscale) {
    277                 if (rr != gg || rr != bb) {
    278                     if (kDebug) {
    279                         printf("Found a non-gray pixel at %d, %d = (%d %d %d %d)\n",
    280                                 i, j, rr, gg, bb, aa);
    281                     }
    282                     isGrayscale = false;
    283                 }
    284             }
    285 
    286             // Check if image is really opaque
    287             if (isOpaque) {
    288                 if (aa != 0xff) {
    289                     if (kDebug) {
    290                         printf("Found a non-opaque pixel at %d, %d = (%d %d %d %d)\n",
    291                                 i, j, rr, gg, bb, aa);
    292                     }
    293                     isOpaque = false;
    294                 }
    295             }
    296 
    297             // Check if image is really <= 256 colors
    298             if (isPalette) {
    299                 col = (uint32_t) ((rr << 24) | (gg << 16) | (bb << 8) | aa);
    300                 bool match = false;
    301                 for (idx = 0; idx < num_colors; idx++) {
    302                     if (colors[idx] == col) {
    303                         match = true;
    304                         break;
    305                     }
    306                 }
    307 
    308                 // Write the palette index for the pixel to outRows optimistically
    309                 // We might overwrite it later if we decide to encode as gray or
    310                 // gray + alpha
    311                 *out++ = idx;
    312                 if (!match) {
    313                     if (num_colors == 256) {
    314                         if (kDebug) {
    315                             printf("Found 257th color at %d, %d\n", i, j);
    316                         }
    317                         isPalette = false;
    318                     } else {
    319                         colors[num_colors++] = col;
    320                     }
    321                 }
    322             }
    323         }
    324     }
    325 
    326     *paletteEntries = 0;
    327     *hasTransparency = !isOpaque;
    328     int bpp = isOpaque ? 3 : 4;
    329     int paletteSize = w * h + bpp * num_colors;
    330 
    331     if (kDebug) {
    332         printf("isGrayscale = %s\n", isGrayscale ? "true" : "false");
    333         printf("isOpaque = %s\n", isOpaque ? "true" : "false");
    334         printf("isPalette = %s\n", isPalette ? "true" : "false");
    335         printf("Size w/ palette = %d, gray+alpha = %d, rgb(a) = %d\n",
    336                 paletteSize, 2 * w * h, bpp * w * h);
    337         printf("Max gray deviation = %d, tolerance = %d\n", maxGrayDeviation, grayscaleTolerance);
    338     }
    339 
    340     // Choose the best color type for the image.
    341     // 1. Opaque gray - use COLOR_TYPE_GRAY at 1 byte/pixel
    342     // 2. Gray + alpha - use COLOR_TYPE_PALETTE if the number of distinct combinations
    343     //     is sufficiently small, otherwise use COLOR_TYPE_GRAY_ALPHA
    344     // 3. RGB(A) - use COLOR_TYPE_PALETTE if the number of distinct colors is sufficiently
    345     //     small, otherwise use COLOR_TYPE_RGB{_ALPHA}
    346     if (isGrayscale) {
    347         if (isOpaque) {
    348             *colorType = PNG_COLOR_TYPE_GRAY; // 1 byte/pixel
    349         } else {
    350             // Use a simple heuristic to determine whether using a palette will
    351             // save space versus using gray + alpha for each pixel.
    352             // This doesn't take into account chunk overhead, filtering, LZ
    353             // compression, etc.
    354             if (isPalette && (paletteSize < 2 * w * h)) {
    355                 *colorType = PNG_COLOR_TYPE_PALETTE; // 1 byte/pixel + 4 bytes/color
    356             } else {
    357                 *colorType = PNG_COLOR_TYPE_GRAY_ALPHA; // 2 bytes per pixel
    358             }
    359         }
    360     } else if (isPalette && (paletteSize < bpp * w * h)) {
    361         *colorType = PNG_COLOR_TYPE_PALETTE;
    362     } else {
    363         if (maxGrayDeviation <= grayscaleTolerance) {
    364             diag->note(DiagMessage()
    365                        << "forcing image to gray (max deviation = "
    366                        << maxGrayDeviation << ")");
    367             *colorType = isOpaque ? PNG_COLOR_TYPE_GRAY : PNG_COLOR_TYPE_GRAY_ALPHA;
    368         } else {
    369             *colorType = isOpaque ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
    370         }
    371     }
    372 
    373     // Perform postprocessing of the image or palette data based on the final
    374     // color type chosen
    375 
    376     if (*colorType == PNG_COLOR_TYPE_PALETTE) {
    377         // Create separate RGB and Alpha palettes and set the number of colors
    378         *paletteEntries = num_colors;
    379 
    380         // Create the RGB and alpha palettes
    381         for (int idx = 0; idx < num_colors; idx++) {
    382             col = colors[idx];
    383             rgbPalette[idx].red   = (png_byte) ((col >> 24) & 0xff);
    384             rgbPalette[idx].green = (png_byte) ((col >> 16) & 0xff);
    385             rgbPalette[idx].blue  = (png_byte) ((col >>  8) & 0xff);
    386             alphaPalette[idx]     = (png_byte)  (col        & 0xff);
    387         }
    388     } else if (*colorType == PNG_COLOR_TYPE_GRAY || *colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
    389         // If the image is gray or gray + alpha, compact the pixels into outRows
    390         for (j = 0; j < h; j++) {
    391             const png_byte* row = imageInfo.rows[j];
    392             png_bytep out = outRows[j];
    393             for (i = 0; i < w; i++) {
    394                 rr = *row++;
    395                 gg = *row++;
    396                 bb = *row++;
    397                 aa = *row++;
    398 
    399                 if (isGrayscale) {
    400                     *out++ = rr;
    401                 } else {
    402                     *out++ = (png_byte) (rr * 0.2126f + gg * 0.7152f + bb * 0.0722f);
    403                 }
    404                 if (!isOpaque) {
    405                     *out++ = aa;
    406                 }
    407            }
    408         }
    409     }
    410 }
    411 
    412 static bool writePng(IDiagnostics* diag, png_structp writePtr, png_infop infoPtr, PngInfo* info,
    413                      int grayScaleTolerance) {
    414     if (setjmp(png_jmpbuf(writePtr))) {
    415         diag->error(DiagMessage() << "failed to write png");
    416         return false;
    417     }
    418 
    419     uint32_t width, height;
    420     int colorType, bitDepth, interlaceType, compressionType;
    421 
    422     png_unknown_chunk unknowns[3];
    423     unknowns[0].data = nullptr;
    424     unknowns[1].data = nullptr;
    425     unknowns[2].data = nullptr;
    426 
    427     png_bytepp outRows = (png_bytepp) malloc((int) info->height * sizeof(png_bytep));
    428     if (outRows == (png_bytepp) 0) {
    429         printf("Can't allocate output buffer!\n");
    430         exit(1);
    431     }
    432     for (uint32_t i = 0; i < info->height; i++) {
    433         outRows[i] = (png_bytep) malloc(2 * (int) info->width);
    434         if (outRows[i] == (png_bytep) 0) {
    435             printf("Can't allocate output buffer!\n");
    436             exit(1);
    437         }
    438     }
    439 
    440     png_set_compression_level(writePtr, Z_BEST_COMPRESSION);
    441 
    442     if (kDebug) {
    443         diag->note(DiagMessage()
    444                    << "writing image: w = " << info->width
    445                    << ", h = " << info->height);
    446     }
    447 
    448     png_color rgbPalette[256];
    449     png_byte alphaPalette[256];
    450     bool hasTransparency;
    451     int paletteEntries;
    452 
    453     analyze_image(diag, *info, grayScaleTolerance, rgbPalette, alphaPalette,
    454                   &paletteEntries, &hasTransparency, &colorType, outRows);
    455 
    456     // If the image is a 9-patch, we need to preserve it as a ARGB file to make
    457     // sure the pixels will not be pre-dithered/clamped until we decide they are
    458     if (info->is9Patch && (colorType == PNG_COLOR_TYPE_RGB ||
    459             colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_PALETTE)) {
    460         colorType = PNG_COLOR_TYPE_RGB_ALPHA;
    461     }
    462 
    463     if (kDebug) {
    464         switch (colorType) {
    465         case PNG_COLOR_TYPE_PALETTE:
    466             diag->note(DiagMessage()
    467                        << "has " << paletteEntries
    468                        << " colors" << (hasTransparency ? " (with alpha)" : "")
    469                        << ", using PNG_COLOR_TYPE_PALLETTE");
    470             break;
    471         case PNG_COLOR_TYPE_GRAY:
    472             diag->note(DiagMessage() << "is opaque gray, using PNG_COLOR_TYPE_GRAY");
    473             break;
    474         case PNG_COLOR_TYPE_GRAY_ALPHA:
    475             diag->note(DiagMessage() << "is gray + alpha, using PNG_COLOR_TYPE_GRAY_ALPHA");
    476             break;
    477         case PNG_COLOR_TYPE_RGB:
    478             diag->note(DiagMessage() << "is opaque RGB, using PNG_COLOR_TYPE_RGB");
    479             break;
    480         case PNG_COLOR_TYPE_RGB_ALPHA:
    481             diag->note(DiagMessage() << "is RGB + alpha, using PNG_COLOR_TYPE_RGB_ALPHA");
    482             break;
    483         }
    484     }
    485 
    486     png_set_IHDR(writePtr, infoPtr, info->width, info->height, 8, colorType,
    487                  PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
    488 
    489     if (colorType == PNG_COLOR_TYPE_PALETTE) {
    490         png_set_PLTE(writePtr, infoPtr, rgbPalette, paletteEntries);
    491         if (hasTransparency) {
    492             png_set_tRNS(writePtr, infoPtr, alphaPalette, paletteEntries, (png_color_16p) 0);
    493         }
    494         png_set_filter(writePtr, 0, PNG_NO_FILTERS);
    495     } else {
    496         png_set_filter(writePtr, 0, PNG_ALL_FILTERS);
    497     }
    498 
    499     if (info->is9Patch) {
    500         int chunkCount = 2 + (info->haveLayoutBounds ? 1 : 0);
    501         int pIndex = info->haveLayoutBounds ? 2 : 1;
    502         int bIndex = 1;
    503         int oIndex = 0;
    504 
    505         // Chunks ordered thusly because older platforms depend on the base 9 patch data being last
    506         png_bytep chunkNames = info->haveLayoutBounds
    507                 ? (png_bytep)"npOl\0npLb\0npTc\0"
    508                 : (png_bytep)"npOl\0npTc";
    509 
    510         // base 9 patch data
    511         if (kDebug) {
    512             diag->note(DiagMessage() << "adding 9-patch info..");
    513         }
    514         strcpy((char*)unknowns[pIndex].name, "npTc");
    515         unknowns[pIndex].data = (png_byte*) info->serialize9Patch();
    516         unknowns[pIndex].size = info->info9Patch.serializedSize();
    517         // TODO: remove the check below when everything works
    518         checkNinePatchSerialization(&info->info9Patch, unknowns[pIndex].data);
    519 
    520         // automatically generated 9 patch outline data
    521         int chunkSize = sizeof(png_uint_32) * 6;
    522         strcpy((char*)unknowns[oIndex].name, "npOl");
    523         unknowns[oIndex].data = (png_byte*) calloc(chunkSize, 1);
    524         png_byte outputData[chunkSize];
    525         memcpy(&outputData, &info->outlineInsetsLeft, 4 * sizeof(png_uint_32));
    526         ((float*) outputData)[4] = info->outlineRadius;
    527         ((png_uint_32*) outputData)[5] = info->outlineAlpha;
    528         memcpy(unknowns[oIndex].data, &outputData, chunkSize);
    529         unknowns[oIndex].size = chunkSize;
    530 
    531         // optional optical inset / layout bounds data
    532         if (info->haveLayoutBounds) {
    533             int chunkSize = sizeof(png_uint_32) * 4;
    534             strcpy((char*)unknowns[bIndex].name, "npLb");
    535             unknowns[bIndex].data = (png_byte*) calloc(chunkSize, 1);
    536             memcpy(unknowns[bIndex].data, &info->layoutBoundsLeft, chunkSize);
    537             unknowns[bIndex].size = chunkSize;
    538         }
    539 
    540         for (int i = 0; i < chunkCount; i++) {
    541             unknowns[i].location = PNG_HAVE_PLTE;
    542         }
    543         png_set_keep_unknown_chunks(writePtr, PNG_HANDLE_CHUNK_ALWAYS,
    544                                     chunkNames, chunkCount);
    545         png_set_unknown_chunks(writePtr, infoPtr, unknowns, chunkCount);
    546 
    547 #if PNG_LIBPNG_VER < 10600
    548         // Deal with unknown chunk location bug in 1.5.x and earlier.
    549         png_set_unknown_chunk_location(writePtr, infoPtr, 0, PNG_HAVE_PLTE);
    550         if (info->haveLayoutBounds) {
    551             png_set_unknown_chunk_location(writePtr, infoPtr, 1, PNG_HAVE_PLTE);
    552         }
    553 #endif
    554     }
    555 
    556     png_write_info(writePtr, infoPtr);
    557 
    558     png_bytepp rows;
    559     if (colorType == PNG_COLOR_TYPE_RGB || colorType == PNG_COLOR_TYPE_RGB_ALPHA) {
    560         if (colorType == PNG_COLOR_TYPE_RGB) {
    561             png_set_filler(writePtr, 0, PNG_FILLER_AFTER);
    562         }
    563         rows = info->rows.data();
    564     } else {
    565         rows = outRows;
    566     }
    567     png_write_image(writePtr, rows);
    568 
    569     if (kDebug) {
    570         printf("Final image data:\n");
    571         //dump_image(info->width, info->height, rows, colorType);
    572     }
    573 
    574     png_write_end(writePtr, infoPtr);
    575 
    576     for (uint32_t i = 0; i < info->height; i++) {
    577         free(outRows[i]);
    578     }
    579     free(outRows);
    580     free(unknowns[0].data);
    581     free(unknowns[1].data);
    582     free(unknowns[2].data);
    583 
    584     png_get_IHDR(writePtr, infoPtr, &width, &height, &bitDepth, &colorType, &interlaceType,
    585                  &compressionType, nullptr);
    586 
    587     if (kDebug) {
    588         diag->note(DiagMessage()
    589                    << "image written: w = " << width << ", h = " << height
    590                    << ", d = " << bitDepth << ", colors = " << colorType
    591                    << ", inter = " << interlaceType << ", comp = " << compressionType);
    592     }
    593     return true;
    594 }
    595 
    596 constexpr uint32_t kColorWhite = 0xffffffffu;
    597 constexpr uint32_t kColorTick = 0xff000000u;
    598 constexpr uint32_t kColorLayoutBoundsTick = 0xff0000ffu;
    599 
    600 enum class TickType {
    601     kNone,
    602     kTick,
    603     kLayoutBounds,
    604     kBoth
    605 };
    606 
    607 static TickType tickType(png_bytep p, bool transparent, const char** outError) {
    608     png_uint_32 color = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
    609 
    610     if (transparent) {
    611         if (p[3] == 0) {
    612             return TickType::kNone;
    613         }
    614         if (color == kColorLayoutBoundsTick) {
    615             return TickType::kLayoutBounds;
    616         }
    617         if (color == kColorTick) {
    618             return TickType::kTick;
    619         }
    620 
    621         // Error cases
    622         if (p[3] != 0xff) {
    623             *outError = "Frame pixels must be either solid or transparent "
    624                         "(not intermediate alphas)";
    625             return TickType::kNone;
    626         }
    627 
    628         if (p[0] != 0 || p[1] != 0 || p[2] != 0) {
    629             *outError = "Ticks in transparent frame must be black or red";
    630         }
    631         return TickType::kTick;
    632     }
    633 
    634     if (p[3] != 0xFF) {
    635         *outError = "White frame must be a solid color (no alpha)";
    636     }
    637     if (color == kColorWhite) {
    638         return TickType::kNone;
    639     }
    640     if (color == kColorTick) {
    641         return TickType::kTick;
    642     }
    643     if (color == kColorLayoutBoundsTick) {
    644         return TickType::kLayoutBounds;
    645     }
    646 
    647     if (p[0] != 0 || p[1] != 0 || p[2] != 0) {
    648         *outError = "Ticks in white frame must be black or red";
    649         return TickType::kNone;
    650     }
    651     return TickType::kTick;
    652 }
    653 
    654 enum class TickState {
    655     kStart,
    656     kInside1,
    657     kOutside1
    658 };
    659 
    660 static bool getHorizontalTicks(png_bytep row, int width, bool transparent, bool required,
    661                                int32_t* outLeft, int32_t* outRight, const char** outError,
    662                                uint8_t* outDivs, bool multipleAllowed) {
    663     *outLeft = *outRight = -1;
    664     TickState state = TickState::kStart;
    665     bool found = false;
    666 
    667     for (int i = 1; i < width - 1; i++) {
    668         if (tickType(row+i*4, transparent, outError) == TickType::kTick) {
    669             if (state == TickState::kStart ||
    670                 (state == TickState::kOutside1 && multipleAllowed)) {
    671                 *outLeft = i-1;
    672                 *outRight = width-2;
    673                 found = true;
    674                 if (outDivs != NULL) {
    675                     *outDivs += 2;
    676                 }
    677                 state = TickState::kInside1;
    678             } else if (state == TickState::kOutside1) {
    679                 *outError = "Can't have more than one marked region along edge";
    680                 *outLeft = i;
    681                 return false;
    682             }
    683         } else if (!*outError) {
    684             if (state == TickState::kInside1) {
    685                 // We're done with this div.  Move on to the next.
    686                 *outRight = i-1;
    687                 outRight += 2;
    688                 outLeft += 2;
    689                 state = TickState::kOutside1;
    690             }
    691         } else {
    692             *outLeft = i;
    693             return false;
    694         }
    695     }
    696 
    697     if (required && !found) {
    698         *outError = "No marked region found along edge";
    699         *outLeft = -1;
    700         return false;
    701     }
    702     return true;
    703 }
    704 
    705 static bool getVerticalTicks(png_bytepp rows, int offset, int height, bool transparent,
    706                              bool required, int32_t* outTop, int32_t* outBottom,
    707                              const char** outError, uint8_t* outDivs, bool multipleAllowed) {
    708     *outTop = *outBottom = -1;
    709     TickState state = TickState::kStart;
    710     bool found = false;
    711 
    712     for (int i = 1; i < height - 1; i++) {
    713         if (tickType(rows[i]+offset, transparent, outError) == TickType::kTick) {
    714             if (state == TickState::kStart ||
    715                 (state == TickState::kOutside1 && multipleAllowed)) {
    716                 *outTop = i-1;
    717                 *outBottom = height-2;
    718                 found = true;
    719                 if (outDivs != NULL) {
    720                     *outDivs += 2;
    721                 }
    722                 state = TickState::kInside1;
    723             } else if (state == TickState::kOutside1) {
    724                 *outError = "Can't have more than one marked region along edge";
    725                 *outTop = i;
    726                 return false;
    727             }
    728         } else if (!*outError) {
    729             if (state == TickState::kInside1) {
    730                 // We're done with this div.  Move on to the next.
    731                 *outBottom = i-1;
    732                 outTop += 2;
    733                 outBottom += 2;
    734                 state = TickState::kOutside1;
    735             }
    736         } else {
    737             *outTop = i;
    738             return false;
    739         }
    740     }
    741 
    742     if (required && !found) {
    743         *outError = "No marked region found along edge";
    744         *outTop = -1;
    745         return false;
    746     }
    747     return true;
    748 }
    749 
    750 static bool getHorizontalLayoutBoundsTicks(png_bytep row, int width, bool transparent,
    751                                            bool /* required */, int32_t* outLeft,
    752                                            int32_t* outRight, const char** outError) {
    753     *outLeft = *outRight = 0;
    754 
    755     // Look for left tick
    756     if (tickType(row + 4, transparent, outError) == TickType::kLayoutBounds) {
    757         // Starting with a layout padding tick
    758         int i = 1;
    759         while (i < width - 1) {
    760             (*outLeft)++;
    761             i++;
    762             if (tickType(row + i * 4, transparent, outError) != TickType::kLayoutBounds) {
    763                 break;
    764             }
    765         }
    766     }
    767 
    768     // Look for right tick
    769     if (tickType(row + (width - 2) * 4, transparent, outError) == TickType::kLayoutBounds) {
    770         // Ending with a layout padding tick
    771         int i = width - 2;
    772         while (i > 1) {
    773             (*outRight)++;
    774             i--;
    775             if (tickType(row+i*4, transparent, outError) != TickType::kLayoutBounds) {
    776                 break;
    777             }
    778         }
    779     }
    780     return true;
    781 }
    782 
    783 static bool getVerticalLayoutBoundsTicks(png_bytepp rows, int offset, int height, bool transparent,
    784                                          bool /* required */, int32_t* outTop, int32_t* outBottom,
    785                                          const char** outError) {
    786     *outTop = *outBottom = 0;
    787 
    788     // Look for top tick
    789     if (tickType(rows[1] + offset, transparent, outError) == TickType::kLayoutBounds) {
    790         // Starting with a layout padding tick
    791         int i = 1;
    792         while (i < height - 1) {
    793             (*outTop)++;
    794             i++;
    795             if (tickType(rows[i] + offset, transparent, outError) != TickType::kLayoutBounds) {
    796                 break;
    797             }
    798         }
    799     }
    800 
    801     // Look for bottom tick
    802     if (tickType(rows[height - 2] + offset, transparent, outError) == TickType::kLayoutBounds) {
    803         // Ending with a layout padding tick
    804         int i = height - 2;
    805         while (i > 1) {
    806             (*outBottom)++;
    807             i--;
    808             if (tickType(rows[i] + offset, transparent, outError) != TickType::kLayoutBounds) {
    809                 break;
    810             }
    811         }
    812     }
    813     return true;
    814 }
    815 
    816 static void findMaxOpacity(png_bytepp rows, int startX, int startY, int endX, int endY,
    817                            int dX, int dY, int* outInset) {
    818     uint8_t maxOpacity = 0;
    819     int inset = 0;
    820     *outInset = 0;
    821     for (int x = startX, y = startY; x != endX && y != endY; x += dX, y += dY, inset++) {
    822         png_byte* color = rows[y] + x * 4;
    823         uint8_t opacity = color[3];
    824         if (opacity > maxOpacity) {
    825             maxOpacity = opacity;
    826             *outInset = inset;
    827         }
    828         if (opacity == 0xff) return;
    829     }
    830 }
    831 
    832 static uint8_t maxAlphaOverRow(png_bytep row, int startX, int endX) {
    833     uint8_t maxAlpha = 0;
    834     for (int x = startX; x < endX; x++) {
    835         uint8_t alpha = (row + x * 4)[3];
    836         if (alpha > maxAlpha) maxAlpha = alpha;
    837     }
    838     return maxAlpha;
    839 }
    840 
    841 static uint8_t maxAlphaOverCol(png_bytepp rows, int offsetX, int startY, int endY) {
    842     uint8_t maxAlpha = 0;
    843     for (int y = startY; y < endY; y++) {
    844         uint8_t alpha = (rows[y] + offsetX * 4)[3];
    845         if (alpha > maxAlpha) maxAlpha = alpha;
    846     }
    847     return maxAlpha;
    848 }
    849 
    850 static void getOutline(PngInfo* image) {
    851     int midX = image->width / 2;
    852     int midY = image->height / 2;
    853     int endX = image->width - 2;
    854     int endY = image->height - 2;
    855 
    856     // find left and right extent of nine patch content on center row
    857     if (image->width > 4) {
    858         findMaxOpacity(image->rows.data(), 1, midY, midX, -1, 1, 0, &image->outlineInsetsLeft);
    859         findMaxOpacity(image->rows.data(), endX, midY, midX, -1, -1, 0,
    860                        &image->outlineInsetsRight);
    861     } else {
    862         image->outlineInsetsLeft = 0;
    863         image->outlineInsetsRight = 0;
    864     }
    865 
    866     // find top and bottom extent of nine patch content on center column
    867     if (image->height > 4) {
    868         findMaxOpacity(image->rows.data(), midX, 1, -1, midY, 0, 1, &image->outlineInsetsTop);
    869         findMaxOpacity(image->rows.data(), midX, endY, -1, midY, 0, -1,
    870                        &image->outlineInsetsBottom);
    871     } else {
    872         image->outlineInsetsTop = 0;
    873         image->outlineInsetsBottom = 0;
    874     }
    875 
    876     int innerStartX = 1 + image->outlineInsetsLeft;
    877     int innerStartY = 1 + image->outlineInsetsTop;
    878     int innerEndX = endX - image->outlineInsetsRight;
    879     int innerEndY = endY - image->outlineInsetsBottom;
    880     int innerMidX = (innerEndX + innerStartX) / 2;
    881     int innerMidY = (innerEndY + innerStartY) / 2;
    882 
    883     // assuming the image is a round rect, compute the radius by marching
    884     // diagonally from the top left corner towards the center
    885     image->outlineAlpha = std::max(
    886             maxAlphaOverRow(image->rows[innerMidY], innerStartX, innerEndX),
    887             maxAlphaOverCol(image->rows.data(), innerMidX, innerStartY, innerStartY));
    888 
    889     int diagonalInset = 0;
    890     findMaxOpacity(image->rows.data(), innerStartX, innerStartY, innerMidX, innerMidY, 1, 1,
    891                    &diagonalInset);
    892 
    893     /* Determine source radius based upon inset:
    894      *     sqrt(r^2 + r^2) = sqrt(i^2 + i^2) + r
    895      *     sqrt(2) * r = sqrt(2) * i + r
    896      *     (sqrt(2) - 1) * r = sqrt(2) * i
    897      *     r = sqrt(2) / (sqrt(2) - 1) * i
    898      */
    899     image->outlineRadius = 3.4142f * diagonalInset;
    900 
    901     if (kDebug) {
    902         printf("outline insets %d %d %d %d, rad %f, alpha %x\n",
    903                 image->outlineInsetsLeft,
    904                 image->outlineInsetsTop,
    905                 image->outlineInsetsRight,
    906                 image->outlineInsetsBottom,
    907                 image->outlineRadius,
    908                 image->outlineAlpha);
    909     }
    910 }
    911 
    912 static uint32_t getColor(png_bytepp rows, int left, int top, int right, int bottom) {
    913     png_bytep color = rows[top] + left*4;
    914 
    915     if (left > right || top > bottom) {
    916         return android::Res_png_9patch::TRANSPARENT_COLOR;
    917     }
    918 
    919     while (top <= bottom) {
    920         for (int i = left; i <= right; i++) {
    921             png_bytep p = rows[top]+i*4;
    922             if (color[3] == 0) {
    923                 if (p[3] != 0) {
    924                     return android::Res_png_9patch::NO_COLOR;
    925                 }
    926             } else if (p[0] != color[0] || p[1] != color[1] ||
    927                     p[2] != color[2] || p[3] != color[3]) {
    928                 return android::Res_png_9patch::NO_COLOR;
    929             }
    930         }
    931         top++;
    932     }
    933 
    934     if (color[3] == 0) {
    935         return android::Res_png_9patch::TRANSPARENT_COLOR;
    936     }
    937     return (color[3]<<24) | (color[0]<<16) | (color[1]<<8) | color[2];
    938 }
    939 
    940 static bool do9Patch(PngInfo* image, std::string* outError) {
    941     image->is9Patch = true;
    942 
    943     int W = image->width;
    944     int H = image->height;
    945     int i, j;
    946 
    947     const int maxSizeXDivs = W * sizeof(int32_t);
    948     const int maxSizeYDivs = H * sizeof(int32_t);
    949     int32_t* xDivs = image->xDivs = new int32_t[W];
    950     int32_t* yDivs = image->yDivs = new int32_t[H];
    951     uint8_t numXDivs = 0;
    952     uint8_t numYDivs = 0;
    953 
    954     int8_t numColors;
    955     int numRows;
    956     int numCols;
    957     int top;
    958     int left;
    959     int right;
    960     int bottom;
    961     memset(xDivs, -1, maxSizeXDivs);
    962     memset(yDivs, -1, maxSizeYDivs);
    963     image->info9Patch.paddingLeft = image->info9Patch.paddingRight = -1;
    964     image->info9Patch.paddingTop = image->info9Patch.paddingBottom = -1;
    965     image->layoutBoundsLeft = image->layoutBoundsRight = 0;
    966     image->layoutBoundsTop = image->layoutBoundsBottom = 0;
    967 
    968     png_bytep p = image->rows[0];
    969     bool transparent = p[3] == 0;
    970     bool hasColor = false;
    971 
    972     const char* errorMsg = nullptr;
    973     int errorPixel = -1;
    974     const char* errorEdge = nullptr;
    975 
    976     int colorIndex = 0;
    977     std::vector<png_bytep> newRows;
    978 
    979     // Validate size...
    980     if (W < 3 || H < 3) {
    981         errorMsg = "Image must be at least 3x3 (1x1 without frame) pixels";
    982         goto getout;
    983     }
    984 
    985     // Validate frame...
    986     if (!transparent &&
    987             (p[0] != 0xFF || p[1] != 0xFF || p[2] != 0xFF || p[3] != 0xFF)) {
    988         errorMsg = "Must have one-pixel frame that is either transparent or white";
    989         goto getout;
    990     }
    991 
    992     // Find left and right of sizing areas...
    993     if (!getHorizontalTicks(p, W, transparent, true, &xDivs[0], &xDivs[1], &errorMsg, &numXDivs,
    994                             true)) {
    995         errorPixel = xDivs[0];
    996         errorEdge = "top";
    997         goto getout;
    998     }
    999 
   1000     // Find top and bottom of sizing areas...
   1001     if (!getVerticalTicks(image->rows.data(), 0, H, transparent, true, &yDivs[0], &yDivs[1],
   1002                           &errorMsg, &numYDivs, true)) {
   1003         errorPixel = yDivs[0];
   1004         errorEdge = "left";
   1005         goto getout;
   1006     }
   1007 
   1008     // Copy patch size data into image...
   1009     image->info9Patch.numXDivs = numXDivs;
   1010     image->info9Patch.numYDivs = numYDivs;
   1011 
   1012     // Find left and right of padding area...
   1013     if (!getHorizontalTicks(image->rows[H-1], W, transparent, false,
   1014                             &image->info9Patch.paddingLeft, &image->info9Patch.paddingRight,
   1015                             &errorMsg, nullptr, false)) {
   1016         errorPixel = image->info9Patch.paddingLeft;
   1017         errorEdge = "bottom";
   1018         goto getout;
   1019     }
   1020 
   1021     // Find top and bottom of padding area...
   1022     if (!getVerticalTicks(image->rows.data(), (W-1)*4, H, transparent, false,
   1023                           &image->info9Patch.paddingTop, &image->info9Patch.paddingBottom,
   1024                           &errorMsg, nullptr, false)) {
   1025         errorPixel = image->info9Patch.paddingTop;
   1026         errorEdge = "right";
   1027         goto getout;
   1028     }
   1029 
   1030     // Find left and right of layout padding...
   1031     getHorizontalLayoutBoundsTicks(image->rows[H-1], W, transparent, false,
   1032                                    &image->layoutBoundsLeft, &image->layoutBoundsRight, &errorMsg);
   1033 
   1034     getVerticalLayoutBoundsTicks(image->rows.data(), (W-1)*4, H, transparent, false,
   1035                                  &image->layoutBoundsTop, &image->layoutBoundsBottom, &errorMsg);
   1036 
   1037     image->haveLayoutBounds = image->layoutBoundsLeft != 0
   1038                                || image->layoutBoundsRight != 0
   1039                                || image->layoutBoundsTop != 0
   1040                                || image->layoutBoundsBottom != 0;
   1041 
   1042     if (image->haveLayoutBounds) {
   1043         if (kDebug) {
   1044             printf("layoutBounds=%d %d %d %d\n", image->layoutBoundsLeft, image->layoutBoundsTop,
   1045                     image->layoutBoundsRight, image->layoutBoundsBottom);
   1046         }
   1047     }
   1048 
   1049     // use opacity of pixels to estimate the round rect outline
   1050     getOutline(image);
   1051 
   1052     // If padding is not yet specified, take values from size.
   1053     if (image->info9Patch.paddingLeft < 0) {
   1054         image->info9Patch.paddingLeft = xDivs[0];
   1055         image->info9Patch.paddingRight = W - 2 - xDivs[1];
   1056     } else {
   1057         // Adjust value to be correct!
   1058         image->info9Patch.paddingRight = W - 2 - image->info9Patch.paddingRight;
   1059     }
   1060     if (image->info9Patch.paddingTop < 0) {
   1061         image->info9Patch.paddingTop = yDivs[0];
   1062         image->info9Patch.paddingBottom = H - 2 - yDivs[1];
   1063     } else {
   1064         // Adjust value to be correct!
   1065         image->info9Patch.paddingBottom = H - 2 - image->info9Patch.paddingBottom;
   1066     }
   1067 
   1068 /*    if (kDebug) {
   1069         printf("Size ticks for %s: x0=%d, x1=%d, y0=%d, y1=%d\n", imageName,
   1070                 xDivs[0], xDivs[1],
   1071                 yDivs[0], yDivs[1]);
   1072         printf("padding ticks for %s: l=%d, r=%d, t=%d, b=%d\n", imageName,
   1073                 image->info9Patch.paddingLeft, image->info9Patch.paddingRight,
   1074                 image->info9Patch.paddingTop, image->info9Patch.paddingBottom);
   1075     }*/
   1076 
   1077     // Remove frame from image.
   1078     newRows.resize(H - 2);
   1079     for (i = 0; i < H - 2; i++) {
   1080         newRows[i] = image->rows[i + 1];
   1081         memmove(newRows[i], newRows[i] + 4, (W - 2) * 4);
   1082     }
   1083     image->rows.swap(newRows);
   1084 
   1085     image->width -= 2;
   1086     W = image->width;
   1087     image->height -= 2;
   1088     H = image->height;
   1089 
   1090     // Figure out the number of rows and columns in the N-patch
   1091     numCols = numXDivs + 1;
   1092     if (xDivs[0] == 0) {  // Column 1 is strechable
   1093         numCols--;
   1094     }
   1095     if (xDivs[numXDivs - 1] == W) {
   1096         numCols--;
   1097     }
   1098     numRows = numYDivs + 1;
   1099     if (yDivs[0] == 0) {  // Row 1 is strechable
   1100         numRows--;
   1101     }
   1102     if (yDivs[numYDivs - 1] == H) {
   1103         numRows--;
   1104     }
   1105 
   1106     // Make sure the amount of rows and columns will fit in the number of
   1107     // colors we can use in the 9-patch format.
   1108     if (numRows * numCols > 0x7F) {
   1109         errorMsg = "Too many rows and columns in 9-patch perimeter";
   1110         goto getout;
   1111     }
   1112 
   1113     numColors = numRows * numCols;
   1114     image->info9Patch.numColors = numColors;
   1115     image->colors.resize(numColors);
   1116 
   1117     // Fill in color information for each patch.
   1118 
   1119     uint32_t c;
   1120     top = 0;
   1121 
   1122     // The first row always starts with the top being at y=0 and the bottom
   1123     // being either yDivs[1] (if yDivs[0]=0) of yDivs[0].  In the former case
   1124     // the first row is stretchable along the Y axis, otherwise it is fixed.
   1125     // The last row always ends with the bottom being bitmap.height and the top
   1126     // being either yDivs[numYDivs-2] (if yDivs[numYDivs-1]=bitmap.height) or
   1127     // yDivs[numYDivs-1]. In the former case the last row is stretchable along
   1128     // the Y axis, otherwise it is fixed.
   1129     //
   1130     // The first and last columns are similarly treated with respect to the X
   1131     // axis.
   1132     //
   1133     // The above is to help explain some of the special casing that goes on the
   1134     // code below.
   1135 
   1136     // The initial yDiv and whether the first row is considered stretchable or
   1137     // not depends on whether yDiv[0] was zero or not.
   1138     for (j = (yDivs[0] == 0 ? 1 : 0); j <= numYDivs && top < H; j++) {
   1139         if (j == numYDivs) {
   1140             bottom = H;
   1141         } else {
   1142             bottom = yDivs[j];
   1143         }
   1144         left = 0;
   1145         // The initial xDiv and whether the first column is considered
   1146         // stretchable or not depends on whether xDiv[0] was zero or not.
   1147         for (i = xDivs[0] == 0 ? 1 : 0; i <= numXDivs && left < W; i++) {
   1148             if (i == numXDivs) {
   1149                 right = W;
   1150             } else {
   1151                 right = xDivs[i];
   1152             }
   1153             c = getColor(image->rows.data(), left, top, right - 1, bottom - 1);
   1154             image->colors[colorIndex++] = c;
   1155             if (kDebug) {
   1156                 if (c != android::Res_png_9patch::NO_COLOR) {
   1157                     hasColor = true;
   1158                 }
   1159             }
   1160             left = right;
   1161         }
   1162         top = bottom;
   1163     }
   1164 
   1165     assert(colorIndex == numColors);
   1166 
   1167     if (kDebug && hasColor) {
   1168         for (i = 0; i < numColors; i++) {
   1169             if (i == 0) printf("Colors:\n");
   1170             printf(" #%08x", image->colors[i]);
   1171             if (i == numColors - 1) printf("\n");
   1172         }
   1173     }
   1174 getout:
   1175     if (errorMsg) {
   1176         std::stringstream err;
   1177         err << "9-patch malformed: " << errorMsg;
   1178         if (errorEdge) {
   1179             err << "." << std::endl;
   1180             if (errorPixel >= 0) {
   1181                 err << "Found at pixel #" << errorPixel << " along " << errorEdge << " edge";
   1182             } else {
   1183                 err << "Found along " << errorEdge << " edge";
   1184             }
   1185         }
   1186         *outError = err.str();
   1187         return false;
   1188     }
   1189     return true;
   1190 }
   1191 
   1192 
   1193 bool Png::process(const Source& source, std::istream* input, BigBuffer* outBuffer,
   1194                   const PngOptions& options) {
   1195     png_byte signature[kPngSignatureSize];
   1196 
   1197     // Read the PNG signature first.
   1198     if (!input->read(reinterpret_cast<char*>(signature), kPngSignatureSize)) {
   1199         mDiag->error(DiagMessage() << strerror(errno));
   1200         return false;
   1201     }
   1202 
   1203     // If the PNG signature doesn't match, bail early.
   1204     if (png_sig_cmp(signature, 0, kPngSignatureSize) != 0) {
   1205         mDiag->error(DiagMessage() << "not a valid png file");
   1206         return false;
   1207     }
   1208 
   1209     bool result = false;
   1210     png_structp readPtr = nullptr;
   1211     png_infop infoPtr = nullptr;
   1212     png_structp writePtr = nullptr;
   1213     png_infop writeInfoPtr = nullptr;
   1214     PngInfo pngInfo = {};
   1215 
   1216     readPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, nullptr, nullptr);
   1217     if (!readPtr) {
   1218         mDiag->error(DiagMessage() << "failed to allocate read ptr");
   1219         goto bail;
   1220     }
   1221 
   1222     infoPtr = png_create_info_struct(readPtr);
   1223     if (!infoPtr) {
   1224         mDiag->error(DiagMessage() << "failed to allocate info ptr");
   1225         goto bail;
   1226     }
   1227 
   1228     png_set_error_fn(readPtr, reinterpret_cast<png_voidp>(mDiag), nullptr, logWarning);
   1229 
   1230     // Set the read function to read from std::istream.
   1231     png_set_read_fn(readPtr, (png_voidp) input, readDataFromStream);
   1232 
   1233     if (!readPng(mDiag, readPtr, infoPtr, &pngInfo)) {
   1234         goto bail;
   1235     }
   1236 
   1237     if (util::stringEndsWith<char>(source.path, ".9.png")) {
   1238         std::string errorMsg;
   1239         if (!do9Patch(&pngInfo, &errorMsg)) {
   1240             mDiag->error(DiagMessage() << errorMsg);
   1241             goto bail;
   1242         }
   1243     }
   1244 
   1245     writePtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, nullptr, nullptr);
   1246     if (!writePtr) {
   1247         mDiag->error(DiagMessage() << "failed to allocate write ptr");
   1248         goto bail;
   1249     }
   1250 
   1251     writeInfoPtr = png_create_info_struct(writePtr);
   1252     if (!writeInfoPtr) {
   1253         mDiag->error(DiagMessage() << "failed to allocate write info ptr");
   1254         goto bail;
   1255     }
   1256 
   1257     png_set_error_fn(writePtr, nullptr, nullptr, logWarning);
   1258 
   1259     // Set the write function to write to std::ostream.
   1260     png_set_write_fn(writePtr, (png_voidp)outBuffer, writeDataToStream, flushDataToStream);
   1261 
   1262     if (!writePng(mDiag, writePtr, writeInfoPtr, &pngInfo, options.grayScaleTolerance)) {
   1263         goto bail;
   1264     }
   1265 
   1266     result = true;
   1267 bail:
   1268     if (readPtr) {
   1269         png_destroy_read_struct(&readPtr, &infoPtr, nullptr);
   1270     }
   1271 
   1272     if (writePtr) {
   1273         png_destroy_write_struct(&writePtr, &writeInfoPtr);
   1274     }
   1275     return result;
   1276 }
   1277 
   1278 } // namespace aapt
   1279