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