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