1 /*
2 * Copyright (C) 2016 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 "compile/Png.h"
18
19 #include <png.h>
20 #include <zlib.h>
21
22 #include <algorithm>
23 #include <unordered_map>
24 #include <unordered_set>
25
26 #include "android-base/errors.h"
27 #include "android-base/logging.h"
28 #include "android-base/macros.h"
29
30 #include "trace/TraceBuffer.h"
31
32 namespace aapt {
33
34 // Custom deleter that destroys libpng read and info structs.
35 class PngReadStructDeleter {
36 public:
PngReadStructDeleter(png_structp read_ptr,png_infop info_ptr)37 PngReadStructDeleter(png_structp read_ptr, png_infop info_ptr)
38 : read_ptr_(read_ptr), info_ptr_(info_ptr) {}
39
~PngReadStructDeleter()40 ~PngReadStructDeleter() {
41 png_destroy_read_struct(&read_ptr_, &info_ptr_, nullptr);
42 }
43
44 private:
45 png_structp read_ptr_;
46 png_infop info_ptr_;
47
48 DISALLOW_COPY_AND_ASSIGN(PngReadStructDeleter);
49 };
50
51 // Custom deleter that destroys libpng write and info structs.
52 class PngWriteStructDeleter {
53 public:
PngWriteStructDeleter(png_structp write_ptr,png_infop info_ptr)54 PngWriteStructDeleter(png_structp write_ptr, png_infop info_ptr)
55 : write_ptr_(write_ptr), info_ptr_(info_ptr) {}
56
~PngWriteStructDeleter()57 ~PngWriteStructDeleter() {
58 png_destroy_write_struct(&write_ptr_, &info_ptr_);
59 }
60
61 private:
62 png_structp write_ptr_;
63 png_infop info_ptr_;
64
65 DISALLOW_COPY_AND_ASSIGN(PngWriteStructDeleter);
66 };
67
68 // Custom warning logging method that uses IDiagnostics.
LogWarning(png_structp png_ptr,png_const_charp warning_msg)69 static void LogWarning(png_structp png_ptr, png_const_charp warning_msg) {
70 IDiagnostics* diag = (IDiagnostics*)png_get_error_ptr(png_ptr);
71 diag->Warn(DiagMessage() << warning_msg);
72 }
73
74 // Custom error logging method that uses IDiagnostics.
LogError(png_structp png_ptr,png_const_charp error_msg)75 static void LogError(png_structp png_ptr, png_const_charp error_msg) {
76 IDiagnostics* diag = (IDiagnostics*)png_get_error_ptr(png_ptr);
77 diag->Error(DiagMessage() << error_msg);
78
79 // Causes libpng to longjmp to the spot where setjmp was set. This is how libpng does
80 // error handling. If this custom error handler method were to return, libpng would, by
81 // default, print the error message to stdout and call the same png_longjmp method.
82 png_longjmp(png_ptr, 1);
83 }
84
ReadDataFromStream(png_structp png_ptr,png_bytep buffer,png_size_t len)85 static void ReadDataFromStream(png_structp png_ptr, png_bytep buffer, png_size_t len) {
86 io::InputStream* in = (io::InputStream*)png_get_io_ptr(png_ptr);
87
88 const void* in_buffer;
89 size_t in_len;
90 if (!in->Next(&in_buffer, &in_len)) {
91 if (in->HadError()) {
92 std::stringstream error_msg_builder;
93 error_msg_builder << "failed reading from input";
94 if (!in->GetError().empty()) {
95 error_msg_builder << ": " << in->GetError();
96 }
97 std::string err = error_msg_builder.str();
98 png_error(png_ptr, err.c_str());
99 }
100 return;
101 }
102
103 const size_t bytes_read = std::min(in_len, len);
104 memcpy(buffer, in_buffer, bytes_read);
105 if (bytes_read != in_len) {
106 in->BackUp(in_len - bytes_read);
107 }
108 }
109
WriteDataToStream(png_structp png_ptr,png_bytep buffer,png_size_t len)110 static void WriteDataToStream(png_structp png_ptr, png_bytep buffer, png_size_t len) {
111 io::OutputStream* out = (io::OutputStream*)png_get_io_ptr(png_ptr);
112
113 void* out_buffer;
114 size_t out_len;
115 while (len > 0) {
116 if (!out->Next(&out_buffer, &out_len)) {
117 if (out->HadError()) {
118 std::stringstream err_msg_builder;
119 err_msg_builder << "failed writing to output";
120 if (!out->GetError().empty()) {
121 err_msg_builder << ": " << out->GetError();
122 }
123 std::string err = out->GetError();
124 png_error(png_ptr, err.c_str());
125 }
126 return;
127 }
128
129 const size_t bytes_written = std::min(out_len, len);
130 memcpy(out_buffer, buffer, bytes_written);
131
132 // Advance the input buffer.
133 buffer += bytes_written;
134 len -= bytes_written;
135
136 // Advance the output buffer.
137 out_len -= bytes_written;
138 }
139
140 // If the entire output buffer wasn't used, backup.
141 if (out_len > 0) {
142 out->BackUp(out_len);
143 }
144 }
145
ReadPng(IAaptContext * context,const Source & source,io::InputStream * in)146 std::unique_ptr<Image> ReadPng(IAaptContext* context, const Source& source, io::InputStream* in) {
147 TRACE_CALL();
148 // Create a diagnostics that has the source information encoded.
149 SourcePathDiagnostics source_diag(source, context->GetDiagnostics());
150
151 // Read the first 8 bytes of the file looking for the PNG signature.
152 // Bail early if it does not match.
153 const png_byte* signature;
154 size_t buffer_size;
155 if (!in->Next((const void**)&signature, &buffer_size)) {
156 if (in->HadError()) {
157 source_diag.Error(DiagMessage() << "failed to read PNG signature: " << in->GetError());
158 } else {
159 source_diag.Error(DiagMessage() << "not enough data for PNG signature");
160 }
161 return {};
162 }
163
164 if (buffer_size < kPngSignatureSize || png_sig_cmp(signature, 0, kPngSignatureSize) != 0) {
165 source_diag.Error(DiagMessage() << "file signature does not match PNG signature");
166 return {};
167 }
168
169 // Start at the beginning of the first chunk.
170 in->BackUp(buffer_size - kPngSignatureSize);
171
172 // Create and initialize the png_struct with the default error and warning handlers.
173 // The header version is also passed in to ensure that this was built against the same
174 // version of libpng.
175 png_structp read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
176 if (read_ptr == nullptr) {
177 source_diag.Error(DiagMessage() << "failed to create libpng read png_struct");
178 return {};
179 }
180
181 // Create and initialize the memory for image header and data.
182 png_infop info_ptr = png_create_info_struct(read_ptr);
183 if (info_ptr == nullptr) {
184 source_diag.Error(DiagMessage() << "failed to create libpng read png_info");
185 png_destroy_read_struct(&read_ptr, nullptr, nullptr);
186 return {};
187 }
188
189 // Automatically release PNG resources at end of scope.
190 PngReadStructDeleter png_read_deleter(read_ptr, info_ptr);
191
192 // libpng uses longjmp to jump to an error handling routine.
193 // setjmp will only return true if it was jumped to, aka there was
194 // an error.
195 if (setjmp(png_jmpbuf(read_ptr))) {
196 return {};
197 }
198
199 // Handle warnings ourselves via IDiagnostics.
200 png_set_error_fn(read_ptr, (png_voidp)&source_diag, LogError, LogWarning);
201
202 // Set up the read functions which read from our custom data sources.
203 png_set_read_fn(read_ptr, (png_voidp)in, ReadDataFromStream);
204
205 // Skip the signature that we already read.
206 png_set_sig_bytes(read_ptr, kPngSignatureSize);
207
208 // Read the chunk headers.
209 png_read_info(read_ptr, info_ptr);
210
211 // Extract image meta-data from the various chunk headers.
212 uint32_t width, height;
213 int bit_depth, color_type, interlace_method, compression_method, filter_method;
214 png_get_IHDR(read_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
215 &interlace_method, &compression_method, &filter_method);
216
217 // When the image is read, expand it so that it is in RGBA 8888 format
218 // so that image handling is uniform.
219
220 if (color_type == PNG_COLOR_TYPE_PALETTE) {
221 png_set_palette_to_rgb(read_ptr);
222 }
223
224 if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
225 png_set_expand_gray_1_2_4_to_8(read_ptr);
226 }
227
228 if (png_get_valid(read_ptr, info_ptr, PNG_INFO_tRNS)) {
229 png_set_tRNS_to_alpha(read_ptr);
230 }
231
232 if (bit_depth == 16) {
233 png_set_strip_16(read_ptr);
234 }
235
236 if (!(color_type & PNG_COLOR_MASK_ALPHA)) {
237 png_set_add_alpha(read_ptr, 0xFF, PNG_FILLER_AFTER);
238 }
239
240 if (color_type == PNG_COLOR_TYPE_GRAY ||
241 color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
242 png_set_gray_to_rgb(read_ptr);
243 }
244
245 if (interlace_method != PNG_INTERLACE_NONE) {
246 png_set_interlace_handling(read_ptr);
247 }
248
249 // Once all the options for reading have been set, we need to flush
250 // them to libpng.
251 png_read_update_info(read_ptr, info_ptr);
252
253 // 9-patch uses int32_t to index images, so we cap the image dimensions to
254 // something
255 // that can always be represented by 9-patch.
256 if (width > std::numeric_limits<int32_t>::max() || height > std::numeric_limits<int32_t>::max()) {
257 source_diag.Error(DiagMessage()
258 << "PNG image dimensions are too large: " << width << "x" << height);
259 return {};
260 }
261
262 std::unique_ptr<Image> output_image = util::make_unique<Image>();
263 output_image->width = static_cast<int32_t>(width);
264 output_image->height = static_cast<int32_t>(height);
265
266 const size_t row_bytes = png_get_rowbytes(read_ptr, info_ptr);
267 CHECK(row_bytes == 4 * width); // RGBA
268
269 // Allocate one large block to hold the image.
270 output_image->data = std::unique_ptr<uint8_t[]>(new uint8_t[height * row_bytes]);
271
272 // Create an array of rows that index into the data block.
273 output_image->rows = std::unique_ptr<uint8_t* []>(new uint8_t*[height]);
274 for (uint32_t h = 0; h < height; h++) {
275 output_image->rows[h] = output_image->data.get() + (h * row_bytes);
276 }
277
278 // Actually read the image pixels.
279 png_read_image(read_ptr, output_image->rows.get());
280
281 // Finish reading. This will read any other chunks after the image data.
282 png_read_end(read_ptr, info_ptr);
283
284 return output_image;
285 }
286
287 // Experimentally chosen constant to be added to the overhead of using color type
288 // PNG_COLOR_TYPE_PALETTE to account for the uncompressability of the palette chunk.
289 // Without this, many small PNGs encoded with palettes are larger after compression than
290 // the same PNGs encoded as RGBA.
291 constexpr static const size_t kPaletteOverheadConstant = 1024u * 10u;
292
293 // Pick a color type by which to encode the image, based on which color type will take
294 // the least amount of disk space.
295 //
296 // 9-patch images traditionally have not been encoded with palettes.
297 // The original rationale was to avoid dithering until after scaling,
298 // but I don't think this would be an issue with palettes. Either way,
299 // our naive size estimation tends to be wrong for small images like 9-patches
300 // and using palettes balloons the size of the resulting 9-patch.
301 // In order to not regress in size, restrict 9-patch to not use palettes.
302
303 // The options are:
304 //
305 // - RGB
306 // - RGBA
307 // - RGB + cheap alpha
308 // - Color palette
309 // - Color palette + cheap alpha
310 // - Color palette + alpha palette
311 // - Grayscale
312 // - Grayscale + cheap alpha
313 // - Grayscale + alpha
314 //
PickColorType(int32_t width,int32_t height,bool grayscale,bool convertible_to_grayscale,bool has_nine_patch,size_t color_palette_size,size_t alpha_palette_size)315 static int PickColorType(int32_t width, int32_t height, bool grayscale,
316 bool convertible_to_grayscale, bool has_nine_patch,
317 size_t color_palette_size, size_t alpha_palette_size) {
318 const size_t palette_chunk_size = 16 + color_palette_size * 3;
319 const size_t alpha_chunk_size = 16 + alpha_palette_size;
320 const size_t color_alpha_data_chunk_size = 16 + 4 * width * height;
321 const size_t color_data_chunk_size = 16 + 3 * width * height;
322 const size_t grayscale_alpha_data_chunk_size = 16 + 2 * width * height;
323 const size_t palette_data_chunk_size = 16 + width * height;
324
325 if (grayscale) {
326 if (alpha_palette_size == 0) {
327 // This is the smallest the data can be.
328 return PNG_COLOR_TYPE_GRAY;
329 } else if (color_palette_size <= 256 && !has_nine_patch) {
330 // This grayscale has alpha and can fit within a palette.
331 // See if it is worth fitting into a palette.
332 const size_t palette_threshold = palette_chunk_size + alpha_chunk_size +
333 palette_data_chunk_size +
334 kPaletteOverheadConstant;
335 if (grayscale_alpha_data_chunk_size > palette_threshold) {
336 return PNG_COLOR_TYPE_PALETTE;
337 }
338 }
339 return PNG_COLOR_TYPE_GRAY_ALPHA;
340 }
341
342 if (color_palette_size <= 256 && !has_nine_patch) {
343 // This image can fit inside a palette. Let's see if it is worth it.
344 size_t total_size_with_palette =
345 palette_data_chunk_size + palette_chunk_size;
346 size_t total_size_without_palette = color_data_chunk_size;
347 if (alpha_palette_size > 0) {
348 total_size_with_palette += alpha_palette_size;
349 total_size_without_palette = color_alpha_data_chunk_size;
350 }
351
352 if (total_size_without_palette >
353 total_size_with_palette + kPaletteOverheadConstant) {
354 return PNG_COLOR_TYPE_PALETTE;
355 }
356 }
357
358 if (convertible_to_grayscale) {
359 if (alpha_palette_size == 0) {
360 return PNG_COLOR_TYPE_GRAY;
361 } else {
362 return PNG_COLOR_TYPE_GRAY_ALPHA;
363 }
364 }
365
366 if (alpha_palette_size == 0) {
367 return PNG_COLOR_TYPE_RGB;
368 }
369 return PNG_COLOR_TYPE_RGBA;
370 }
371
372 // Assigns indices to the color and alpha palettes, encodes them, and then invokes
373 // png_set_PLTE/png_set_tRNS.
374 // This must be done before writing image data.
375 // Image data must be transformed to use the indices assigned within the palette.
WritePalette(png_structp write_ptr,png_infop write_info_ptr,std::unordered_map<uint32_t,int> * color_palette,std::unordered_set<uint32_t> * alpha_palette)376 static void WritePalette(png_structp write_ptr, png_infop write_info_ptr,
377 std::unordered_map<uint32_t, int>* color_palette,
378 std::unordered_set<uint32_t>* alpha_palette) {
379 CHECK(color_palette->size() <= 256);
380 CHECK(alpha_palette->size() <= 256);
381
382 // Populate the PNG palette struct and assign indices to the color palette.
383
384 // Colors in the alpha palette should have smaller indices.
385 // This will ensure that we can truncate the alpha palette if it is
386 // smaller than the color palette.
387 int index = 0;
388 for (uint32_t color : *alpha_palette) {
389 (*color_palette)[color] = index++;
390 }
391
392 // Assign the rest of the entries.
393 for (auto& entry : *color_palette) {
394 if (entry.second == -1) {
395 entry.second = index++;
396 }
397 }
398
399 // Create the PNG color palette struct.
400 auto color_palette_bytes = std::unique_ptr<png_color[]>(new png_color[color_palette->size()]);
401
402 std::unique_ptr<png_byte[]> alpha_palette_bytes;
403 if (!alpha_palette->empty()) {
404 alpha_palette_bytes = std::unique_ptr<png_byte[]>(new png_byte[alpha_palette->size()]);
405 }
406
407 for (const auto& entry : *color_palette) {
408 const uint32_t color = entry.first;
409 const int index = entry.second;
410 CHECK(index >= 0);
411 CHECK(static_cast<size_t>(index) < color_palette->size());
412
413 png_colorp slot = color_palette_bytes.get() + index;
414 slot->red = color >> 24;
415 slot->green = color >> 16;
416 slot->blue = color >> 8;
417
418 const png_byte alpha = color & 0x000000ff;
419 if (alpha != 0xff && alpha_palette_bytes) {
420 CHECK(static_cast<size_t>(index) < alpha_palette->size());
421 alpha_palette_bytes[index] = alpha;
422 }
423 }
424
425 // The bytes get copied here, so it is safe to release color_palette_bytes at
426 // the end of function
427 // scope.
428 png_set_PLTE(write_ptr, write_info_ptr, color_palette_bytes.get(), color_palette->size());
429
430 if (alpha_palette_bytes) {
431 png_set_tRNS(write_ptr, write_info_ptr, alpha_palette_bytes.get(), alpha_palette->size(),
432 nullptr);
433 }
434 }
435
436 // Write the 9-patch custom PNG chunks to write_info_ptr. This must be done
437 // before writing image data.
WriteNinePatch(png_structp write_ptr,png_infop write_info_ptr,const NinePatch * nine_patch)438 static void WriteNinePatch(png_structp write_ptr, png_infop write_info_ptr,
439 const NinePatch* nine_patch) {
440 // The order of the chunks is important.
441 // 9-patch code in older platforms expects the 9-patch chunk to be last.
442
443 png_unknown_chunk unknown_chunks[3];
444 memset(unknown_chunks, 0, sizeof(unknown_chunks));
445
446 size_t index = 0;
447 size_t chunk_len = 0;
448
449 std::unique_ptr<uint8_t[]> serialized_outline =
450 nine_patch->SerializeRoundedRectOutline(&chunk_len);
451 strcpy((char*)unknown_chunks[index].name, "npOl");
452 unknown_chunks[index].size = chunk_len;
453 unknown_chunks[index].data = (png_bytep)serialized_outline.get();
454 unknown_chunks[index].location = PNG_HAVE_PLTE;
455 index++;
456
457 std::unique_ptr<uint8_t[]> serialized_layout_bounds;
458 if (nine_patch->layout_bounds.nonZero()) {
459 serialized_layout_bounds = nine_patch->SerializeLayoutBounds(&chunk_len);
460 strcpy((char*)unknown_chunks[index].name, "npLb");
461 unknown_chunks[index].size = chunk_len;
462 unknown_chunks[index].data = (png_bytep)serialized_layout_bounds.get();
463 unknown_chunks[index].location = PNG_HAVE_PLTE;
464 index++;
465 }
466
467 std::unique_ptr<uint8_t[]> serialized_nine_patch = nine_patch->SerializeBase(&chunk_len);
468 strcpy((char*)unknown_chunks[index].name, "npTc");
469 unknown_chunks[index].size = chunk_len;
470 unknown_chunks[index].data = (png_bytep)serialized_nine_patch.get();
471 unknown_chunks[index].location = PNG_HAVE_PLTE;
472 index++;
473
474 // Handle all unknown chunks. We are manually setting the chunks here,
475 // so we will only ever handle our custom chunks.
476 png_set_keep_unknown_chunks(write_ptr, PNG_HANDLE_CHUNK_ALWAYS, nullptr, 0);
477
478 // Set the actual chunks here. The data gets copied, so our buffers can
479 // safely go out of scope.
480 png_set_unknown_chunks(write_ptr, write_info_ptr, unknown_chunks, index);
481 }
482
WritePng(IAaptContext * context,const Image * image,const NinePatch * nine_patch,io::OutputStream * out,const PngOptions & options)483 bool WritePng(IAaptContext* context, const Image* image,
484 const NinePatch* nine_patch, io::OutputStream* out,
485 const PngOptions& options) {
486 TRACE_CALL();
487 // Create and initialize the write png_struct with the default error and
488 // warning handlers.
489 // The header version is also passed in to ensure that this was built against the same
490 // version of libpng.
491 png_structp write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
492 if (write_ptr == nullptr) {
493 context->GetDiagnostics()->Error(DiagMessage() << "failed to create libpng write png_struct");
494 return false;
495 }
496
497 // Allocate memory to store image header data.
498 png_infop write_info_ptr = png_create_info_struct(write_ptr);
499 if (write_info_ptr == nullptr) {
500 context->GetDiagnostics()->Error(DiagMessage() << "failed to create libpng write png_info");
501 png_destroy_write_struct(&write_ptr, nullptr);
502 return false;
503 }
504
505 // Automatically release PNG resources at end of scope.
506 PngWriteStructDeleter png_write_deleter(write_ptr, write_info_ptr);
507
508 // libpng uses longjmp to jump to error handling routines.
509 // setjmp will return true only if it was jumped to, aka, there was an error.
510 if (setjmp(png_jmpbuf(write_ptr))) {
511 return false;
512 }
513
514 // Handle warnings with our IDiagnostics.
515 png_set_error_fn(write_ptr, (png_voidp)context->GetDiagnostics(), LogError, LogWarning);
516
517 // Set up the write functions which write to our custom data sources.
518 png_set_write_fn(write_ptr, (png_voidp)out, WriteDataToStream, nullptr);
519
520 // We want small files and can take the performance hit to achieve this goal.
521 png_set_compression_level(write_ptr, Z_BEST_COMPRESSION);
522
523 // Begin analysis of the image data.
524 // Scan the entire image and determine if:
525 // 1. Every pixel has R == G == B (grayscale)
526 // 2. Every pixel has A == 255 (opaque)
527 // 3. There are no more than 256 distinct RGBA colors (palette).
528 std::unordered_map<uint32_t, int> color_palette;
529 std::unordered_set<uint32_t> alpha_palette;
530 bool needs_to_zero_rgb_channels_of_transparent_pixels = false;
531 bool grayscale = true;
532 int max_gray_deviation = 0;
533
534 for (int32_t y = 0; y < image->height; y++) {
535 const uint8_t* row = image->rows[y];
536 for (int32_t x = 0; x < image->width; x++) {
537 int red = *row++;
538 int green = *row++;
539 int blue = *row++;
540 int alpha = *row++;
541
542 if (alpha == 0) {
543 // The color is completely transparent.
544 // For purposes of palettes and grayscale optimization,
545 // treat all channels as 0x00.
546 needs_to_zero_rgb_channels_of_transparent_pixels =
547 needs_to_zero_rgb_channels_of_transparent_pixels ||
548 (red != 0 || green != 0 || blue != 0);
549 red = green = blue = 0;
550 }
551
552 // Insert the color into the color palette.
553 const uint32_t color = red << 24 | green << 16 | blue << 8 | alpha;
554 color_palette[color] = -1;
555
556 // If the pixel has non-opaque alpha, insert it into the
557 // alpha palette.
558 if (alpha != 0xff) {
559 alpha_palette.insert(color);
560 }
561
562 // Check if the image is indeed grayscale.
563 if (grayscale) {
564 if (red != green || red != blue) {
565 grayscale = false;
566 }
567 }
568
569 // Calculate the gray scale deviation so that it can be compared
570 // with the threshold.
571 max_gray_deviation = std::max(std::abs(red - green), max_gray_deviation);
572 max_gray_deviation = std::max(std::abs(green - blue), max_gray_deviation);
573 max_gray_deviation = std::max(std::abs(blue - red), max_gray_deviation);
574 }
575 }
576
577 if (context->IsVerbose()) {
578 DiagMessage msg;
579 msg << " paletteSize=" << color_palette.size()
580 << " alphaPaletteSize=" << alpha_palette.size()
581 << " maxGrayDeviation=" << max_gray_deviation
582 << " grayScale=" << (grayscale ? "true" : "false");
583 context->GetDiagnostics()->Note(msg);
584 }
585
586 const bool convertible_to_grayscale = max_gray_deviation <= options.grayscale_tolerance;
587
588 const int new_color_type = PickColorType(
589 image->width, image->height, grayscale, convertible_to_grayscale,
590 nine_patch != nullptr, color_palette.size(), alpha_palette.size());
591
592 if (context->IsVerbose()) {
593 DiagMessage msg;
594 msg << "encoding PNG ";
595 if (nine_patch) {
596 msg << "(with 9-patch) as ";
597 }
598 switch (new_color_type) {
599 case PNG_COLOR_TYPE_GRAY:
600 msg << "GRAY";
601 break;
602 case PNG_COLOR_TYPE_GRAY_ALPHA:
603 msg << "GRAY + ALPHA";
604 break;
605 case PNG_COLOR_TYPE_RGB:
606 msg << "RGB";
607 break;
608 case PNG_COLOR_TYPE_RGB_ALPHA:
609 msg << "RGBA";
610 break;
611 case PNG_COLOR_TYPE_PALETTE:
612 msg << "PALETTE";
613 break;
614 default:
615 msg << "unknown type " << new_color_type;
616 break;
617 }
618 context->GetDiagnostics()->Note(msg);
619 }
620
621 png_set_IHDR(write_ptr, write_info_ptr, image->width, image->height, 8,
622 new_color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
623 PNG_FILTER_TYPE_DEFAULT);
624
625 if (new_color_type & PNG_COLOR_MASK_PALETTE) {
626 // Assigns indices to the palette, and writes the encoded palette to the
627 // libpng writePtr.
628 WritePalette(write_ptr, write_info_ptr, &color_palette, &alpha_palette);
629 png_set_filter(write_ptr, 0, PNG_NO_FILTERS);
630 } else {
631 png_set_filter(write_ptr, 0, PNG_ALL_FILTERS);
632 }
633
634 if (nine_patch) {
635 WriteNinePatch(write_ptr, write_info_ptr, nine_patch);
636 }
637
638 // Flush our updates to the header.
639 png_write_info(write_ptr, write_info_ptr);
640
641 // Write out each row of image data according to its encoding.
642 if (new_color_type == PNG_COLOR_TYPE_PALETTE) {
643 // 1 byte/pixel.
644 auto out_row = std::unique_ptr<png_byte[]>(new png_byte[image->width]);
645
646 for (int32_t y = 0; y < image->height; y++) {
647 png_const_bytep in_row = image->rows[y];
648 for (int32_t x = 0; x < image->width; x++) {
649 int rr = *in_row++;
650 int gg = *in_row++;
651 int bb = *in_row++;
652 int aa = *in_row++;
653 if (aa == 0) {
654 // Zero out color channels when transparent.
655 rr = gg = bb = 0;
656 }
657
658 const uint32_t color = rr << 24 | gg << 16 | bb << 8 | aa;
659 const int idx = color_palette[color];
660 CHECK(idx != -1);
661 out_row[x] = static_cast<png_byte>(idx);
662 }
663 png_write_row(write_ptr, out_row.get());
664 }
665 } else if (new_color_type == PNG_COLOR_TYPE_GRAY ||
666 new_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
667 const size_t bpp = new_color_type == PNG_COLOR_TYPE_GRAY ? 1 : 2;
668 auto out_row =
669 std::unique_ptr<png_byte[]>(new png_byte[image->width * bpp]);
670
671 for (int32_t y = 0; y < image->height; y++) {
672 png_const_bytep in_row = image->rows[y];
673 for (int32_t x = 0; x < image->width; x++) {
674 int rr = in_row[x * 4];
675 int gg = in_row[x * 4 + 1];
676 int bb = in_row[x * 4 + 2];
677 int aa = in_row[x * 4 + 3];
678 if (aa == 0) {
679 // Zero out the gray channel when transparent.
680 rr = gg = bb = 0;
681 }
682
683 if (grayscale) {
684 // The image was already grayscale, red == green == blue.
685 out_row[x * bpp] = in_row[x * 4];
686 } else {
687 // The image is convertible to grayscale, use linear-luminance of
688 // sRGB colorspace:
689 // https://en.wikipedia.org/wiki/Grayscale#Colorimetric_.28luminance-preserving.29_conversion_to_grayscale
690 out_row[x * bpp] =
691 (png_byte)(rr * 0.2126f + gg * 0.7152f + bb * 0.0722f);
692 }
693
694 if (bpp == 2) {
695 // Write out alpha if we have it.
696 out_row[x * bpp + 1] = aa;
697 }
698 }
699 png_write_row(write_ptr, out_row.get());
700 }
701 } else if (new_color_type == PNG_COLOR_TYPE_RGB || new_color_type == PNG_COLOR_TYPE_RGBA) {
702 const size_t bpp = new_color_type == PNG_COLOR_TYPE_RGB ? 3 : 4;
703 if (needs_to_zero_rgb_channels_of_transparent_pixels) {
704 // The source RGBA data can't be used as-is, because we need to zero out
705 // the RGB values of transparent pixels.
706 auto out_row = std::unique_ptr<png_byte[]>(new png_byte[image->width * bpp]);
707
708 for (int32_t y = 0; y < image->height; y++) {
709 png_const_bytep in_row = image->rows[y];
710 for (int32_t x = 0; x < image->width; x++) {
711 int rr = *in_row++;
712 int gg = *in_row++;
713 int bb = *in_row++;
714 int aa = *in_row++;
715 if (aa == 0) {
716 // Zero out the RGB channels when transparent.
717 rr = gg = bb = 0;
718 }
719 out_row[x * bpp] = rr;
720 out_row[x * bpp + 1] = gg;
721 out_row[x * bpp + 2] = bb;
722 if (bpp == 4) {
723 out_row[x * bpp + 3] = aa;
724 }
725 }
726 png_write_row(write_ptr, out_row.get());
727 }
728 } else {
729 // The source image can be used as-is, just tell libpng whether or not to
730 // ignore the alpha channel.
731 if (new_color_type == PNG_COLOR_TYPE_RGB) {
732 // Delete the extraneous alpha values that we appended to our buffer
733 // when reading the original values.
734 png_set_filler(write_ptr, 0, PNG_FILLER_AFTER);
735 }
736 png_write_image(write_ptr, image->rows.get());
737 }
738 } else {
739 LOG(FATAL) << "unreachable";
740 }
741
742 png_write_end(write_ptr, write_info_ptr);
743 return true;
744 }
745
746 } // namespace aapt
747