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