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