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