/* * Invalidate the unsafe-to-copy metadata that cannot reliably remain * after transformations. */ static void opng_transform_invalidate_meta(png_structp libpng_ptr, png_infop info_ptr) { int color_type; color_type = png_get_color_type(libpng_ptr, info_ptr); if (!(color_type & PNG_COLOR_MASK_PALETTE)) { /* PLTE, if it exists in this situation, has the same role as sPLT. */ png_set_invalid(libpng_ptr, info_ptr, PNG_INFO_PLTE); } #ifdef PNG_sPLT_SUPPORTED png_set_invalid(libpng_ptr, info_ptr, PNG_INFO_sPLT); #endif #ifdef PNG_hIST_SUPPORTED png_set_invalid(libpng_ptr, info_ptr, PNG_INFO_hIST); #endif #ifdef PNG_sBIT_SUPPORTED png_set_invalid(libpng_ptr, info_ptr, PNG_INFO_hIST); #endif /* FIXME: Invalidate the unknown unsafe-to-copy chunks. */ }
/* * Reduce the palette. (Only the fast method is implemented.) * The parameter reductions indicates the intended reductions. * The function returns the successful reductions. */ static png_uint_32 opng_reduce_palette(png_structp png_ptr, png_infop info_ptr, png_uint_32 reductions) { png_colorp palette; png_bytep trans_alpha; png_uint_32 width, height; int bit_depth, color_type, interlace_type, compression_type, filter_type; int num_palette, num_trans; int last_color_index, last_trans_index; png_byte crt_trans_value, last_trans_value; png_byte is_used[256]; png_color_16 gray_trans; png_uint_32 result = OPNG_REDUCE_NONE; png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_type); if (!height || !width){ return OPNG_REDUCE_NONE; } png_bytepp row_ptr = png_get_rows(png_ptr, info_ptr); if (!png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette)) { palette = 0; num_palette = 0; } if (!png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, 0)) { trans_alpha = 0; num_trans = 0; } else OPNG_ASSERT(trans_alpha && num_trans > 0); opng_analyze_sample_usage(png_ptr, info_ptr, is_used); /* Palette-to-gray does not work (yet) if the bit depth is below 8. */ int is_gray = (reductions & OPNG_REDUCE_PALETTE_TO_GRAY) && (bit_depth == 8); last_color_index = last_trans_index = -1; int k; for (k= 0; k < 256; ++k) { if (!is_used[k]) continue; last_color_index = k; if (k < num_trans && trans_alpha[k] < 255) last_trans_index = k; if (is_gray) if (palette[k].red != palette[k].green || palette[k].red != palette[k].blue) is_gray = 0; } OPNG_ASSERT(last_color_index >= 0); OPNG_ASSERT(last_color_index >= last_trans_index); /* Check the integrity of PLTE and tRNS. */ if (last_color_index >= num_palette) { png_warning(png_ptr, "Too few colors in PLTE"); /* Fix the palette by adding blank entries at the end. */ opng_realloc_PLTE(png_ptr, info_ptr, last_color_index + 1); png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); OPNG_ASSERT(num_palette == last_color_index + 1); result |= OPNG_REDUCE_REPAIR; } if (num_trans > num_palette) { png_warning(png_ptr, "Too many alpha values in tRNS"); /* Transparency will be fixed further below. */ result |= OPNG_REDUCE_REPAIR; } /* Check if tRNS can be reduced to grayscale. */ if (is_gray && last_trans_index >= 0) { gray_trans.gray = palette[last_trans_index].red; last_trans_value = trans_alpha[last_trans_index]; for (k = 0; k <= last_color_index; ++k) { if (!is_used[k]) continue; if (k <= last_trans_index) { crt_trans_value = trans_alpha[k]; /* Cannot reduce if different colors have transparency. */ if (crt_trans_value < 255 && palette[k].red != gray_trans.gray) { is_gray = 0; break; } } else crt_trans_value = 255; /* Cannot reduce if same color has multiple transparency levels. */ if (palette[k].red == gray_trans.gray && crt_trans_value != last_trans_value) { is_gray = 0; break; } } } /* Remove tRNS if it is entirely sterile. */ if (num_trans > 0 && last_trans_index < 0) { num_trans = 0; png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, -1); png_set_invalid(png_ptr, info_ptr, PNG_INFO_tRNS); result |= OPNG_REDUCE_PALETTE_FAST; } if (reductions & OPNG_REDUCE_PALETTE_FAST) { if (num_palette != last_color_index + 1) { /* Reduce PLTE. */ /* hIST is reduced automatically. */ opng_realloc_PLTE(png_ptr, info_ptr, last_color_index + 1); png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); OPNG_ASSERT(num_palette == last_color_index + 1); result |= OPNG_REDUCE_PALETTE_FAST; } if (num_trans > 0 && num_trans != last_trans_index + 1) { /* Reduce tRNS. */ opng_realloc_tRNS(png_ptr, info_ptr, last_trans_index + 1); png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, 0); OPNG_ASSERT(num_trans == last_trans_index + 1); result |= OPNG_REDUCE_PALETTE_FAST; } } if (reductions & OPNG_REDUCE_8_TO_4_2_1) { result |= opng_reduce_palette_bits(png_ptr, info_ptr, reductions); /* Refresh the image information. */ bit_depth = png_get_bit_depth(png_ptr, info_ptr); } if ((bit_depth < 8) || !is_gray) return result; /* Reduce palette --> grayscale. */ for (png_uint_32 i = 0; i < height; ++i) { for (png_uint_32 j = 0; j < width; ++j) row_ptr[i][j] = palette[row_ptr[i][j]].red; } /* Update the ancillary information. */ if (num_trans > 0) png_set_tRNS(png_ptr, info_ptr, 0, 0, &gray_trans); #ifdef PNG_bKGD_SUPPORTED png_color_16p background; if (png_get_bKGD(png_ptr, info_ptr, &background)) background->gray = palette[background->index].red; #endif #ifdef PNG_hIST_SUPPORTED png_uint_16p hist; if (png_get_hIST(png_ptr, info_ptr, &hist)) { png_free_data(png_ptr, info_ptr, PNG_FREE_HIST, -1); png_set_invalid(png_ptr, info_ptr, PNG_INFO_hIST); } #endif #ifdef PNG_sBIT_SUPPORTED png_color_8p sig_bits; if (png_get_sBIT(png_ptr, info_ptr, &sig_bits)) { png_byte max_sig_bits = sig_bits->red; if (max_sig_bits < sig_bits->green) max_sig_bits = sig_bits->green; if (max_sig_bits < sig_bits->blue) max_sig_bits = sig_bits->blue; sig_bits->gray = max_sig_bits; } #endif /* Update the image information. */ png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, PNG_COLOR_TYPE_GRAY, interlace_type, compression_type, filter_type); png_free_data(png_ptr, info_ptr, PNG_FREE_PLTE, -1); png_set_invalid(png_ptr, info_ptr, PNG_INFO_PLTE); return OPNG_REDUCE_PALETTE_TO_GRAY; /* ignore the former result */ }
/* * Reduce the image type to a lower bit depth and color type, * by removing redundant bits. * Possible reductions: 16bpp to 8bpp; RGB to gray; strip alpha. * The parameter reductions indicates the intended reductions. * The function returns the successful reductions. * All reductions are performed in a single step. */ static png_uint_32 opng_reduce_bits(png_structp png_ptr, png_infop info_ptr, png_uint_32 reductions) { png_bytep src_ptr, dest_ptr; png_uint_32 width, height; int interlace_type, compression_type, filter_type, src_bit_depth, dest_bit_depth, src_color_type; /* See which reductions may be performed. */ reductions = opng_analyze_bits(png_ptr, info_ptr, reductions); if (reductions == OPNG_REDUCE_NONE) return OPNG_REDUCE_NONE; /* exit early */ png_get_IHDR(png_ptr, info_ptr, &width, &height, &src_bit_depth, &src_color_type, &interlace_type, &compression_type, &filter_type); if (!height || !width){ return OPNG_REDUCE_NONE; } /* Compute the new image parameters bit_depth, color_type, etc. */ OPNG_ASSERT(src_bit_depth >= 8); if (reductions & OPNG_REDUCE_16_TO_8) { OPNG_ASSERT(src_bit_depth == 16); dest_bit_depth = 8; } else dest_bit_depth = src_bit_depth; int src_byte_depth = src_bit_depth / 8; int dest_byte_depth = dest_bit_depth / 8; int dest_color_type = src_color_type; if (reductions & OPNG_REDUCE_RGB_TO_GRAY) { OPNG_ASSERT(src_color_type & PNG_COLOR_MASK_COLOR); dest_color_type &= ~PNG_COLOR_MASK_COLOR; } if (reductions & OPNG_REDUCE_STRIP_ALPHA) { OPNG_ASSERT(src_color_type & PNG_COLOR_MASK_ALPHA); dest_color_type &= ~PNG_COLOR_MASK_ALPHA; } int src_channels = png_get_channels(png_ptr, info_ptr); int dest_channels = ((dest_color_type & PNG_COLOR_MASK_COLOR) ? 3 : 1) + ((dest_color_type & PNG_COLOR_MASK_ALPHA) ? 1 : 0); int src_sample_size = src_channels * src_byte_depth; int dest_sample_size = dest_channels * dest_byte_depth; /* Pre-compute the intra-sample translation table. */ int k; int tran_tbl[8]; for (k = 0; k < 4 * dest_byte_depth; ++k) tran_tbl[k] = k * src_bit_depth / dest_bit_depth; /* If rgb --> gray, shift the alpha component two positions to the left. */ if ((reductions & OPNG_REDUCE_RGB_TO_GRAY) && (dest_color_type & PNG_COLOR_MASK_ALPHA)) { tran_tbl[dest_byte_depth] = tran_tbl[3 * dest_byte_depth]; if (dest_byte_depth == 2) tran_tbl[dest_byte_depth + 1] = tran_tbl[3 * dest_byte_depth + 1]; } /* Translate the samples to the new image type. */ OPNG_ASSERT(src_sample_size > dest_sample_size); png_bytepp row_ptr = png_get_rows(png_ptr, info_ptr); for (png_uint_32 i = 0; i < height; ++i, ++row_ptr) { src_ptr = dest_ptr = *row_ptr; for (png_uint_32 j = 0; j < width; ++j) { for (k = 0; k < dest_sample_size; ++k) dest_ptr[k] = src_ptr[tran_tbl[k]]; src_ptr += src_sample_size; dest_ptr += dest_sample_size; } } png_color_16p trans_color; /* Update the ancillary information. */ if (png_get_tRNS(png_ptr, info_ptr, 0, 0, &trans_color)) { if (reductions & OPNG_REDUCE_16_TO_8) { if (trans_color->red % 257 == 0 && trans_color->green % 257 == 0 && trans_color->blue % 257 == 0 && trans_color->gray % 257 == 0) { trans_color->red &= 255; trans_color->green &= 255; trans_color->blue &= 255; trans_color->gray &= 255; } else { /* 16-bit tRNS in 8-bit samples: all pixels are 100% opaque. */ png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, -1); png_set_invalid(png_ptr, info_ptr, PNG_INFO_tRNS); } } if (reductions & OPNG_REDUCE_RGB_TO_GRAY) { if (trans_color->red == trans_color->green || trans_color->red == trans_color->blue) trans_color->gray = trans_color->red; else { /* Non-gray tRNS in grayscale image: all pixels are 100% opaque. */ png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, -1); png_set_invalid(png_ptr, info_ptr, PNG_INFO_tRNS); } } } #ifdef PNG_bKGD_SUPPORTED png_color_16p background; if (png_get_bKGD(png_ptr, info_ptr, &background)) { if (reductions & OPNG_REDUCE_16_TO_8) { background->red &= 255; background->green &= 255; background->blue &= 255; background->gray &= 255; } if (reductions & OPNG_REDUCE_RGB_TO_GRAY) background->gray = background->red; } #endif #ifdef PNG_sBIT_SUPPORTED png_color_8p sig_bits; if (png_get_sBIT(png_ptr, info_ptr, &sig_bits)) { if (reductions & OPNG_REDUCE_16_TO_8) { if (sig_bits->red > 8) sig_bits->red = 8; if (sig_bits->green > 8) sig_bits->green = 8; if (sig_bits->blue > 8) sig_bits->blue = 8; if (sig_bits->gray > 8) sig_bits->gray = 8; if (sig_bits->alpha > 8) sig_bits->alpha = 8; } if (reductions & OPNG_REDUCE_RGB_TO_GRAY) { png_byte max_sig_bits = sig_bits->red; if (max_sig_bits < sig_bits->green) max_sig_bits = sig_bits->green; if (max_sig_bits < sig_bits->blue) max_sig_bits = sig_bits->blue; sig_bits->gray = max_sig_bits; } } #endif /* Update the image information. */ png_set_IHDR(png_ptr, info_ptr, width, height, dest_bit_depth, dest_color_type, interlace_type, compression_type, filter_type); return reductions; }
BOOL OutputPNG::OutputPNGHeader(CCLexFile *File, LPBITMAPINFOHEADER pInfo, BOOL InterlaceState, INT32 TransparentColour, LPLOGPALETTE pPalette, LPRGBQUAD pQuadPalette) { ERROR2IF(File==NULL,FALSE,"OutputPNG::OutputPNGHeader File pointer is null"); if (pInfo == NULL) pInfo = &(DestBitmapInfo->bmiHeader); ERROR2IF(pInfo==NULL,FALSE,"OutputPNG::OutputPNGHeader BitmapInfo pointer is null"); //ERROR2IF(pPalette==NULL && pQuadPalette==NULL,FALSE,"OutputPNG::OutputPNGHeader Bitmap palette pointer is null"); TRACEUSER( "Jonathan", _T("PNG write: Interlace: %s\n"), InterlaceState ? _T("Yes") : _T("No")); // Note file in our class variable as used by all the low level routines OutputFile = File; // Note the specified transparency and interlace states in our class variables Interlace = InterlaceState; if (TransparentColour != -1) Transparent = TRUE; else Transparent = FALSE; // We are just about to start so set the PNG exception handling up with our CCFile pointer PNGUtil::SetCCFilePointer(File); // Must set the exception throwing flag to True and force reporting of errors to False. // This means that the caller must report an error if the function returns False. // Any calls to CCFile::GotError will now throw a file exception and should fall into // the catch handler at the end of the function. // Replaces the goto's that handled this before. BOOL OldThrowingState = File->SetThrowExceptions( TRUE ); BOOL OldReportingState = File->SetReportErrors( FALSE ); // PNG related items (NOTE: p at end means pointer and hence implied *) png_ptr = NULL; info_ptr = NULL; palette = NULL; try { // Work out the palette size INT32 PalSize = pInfo->biClrUsed; // How many entries in palette TRACEUSER( "Jonathan", _T("PNG write: PalSize = %d\n"),PalSize); // Set up the class variables // First the width/height of the bitmap Width = pInfo->biWidth; Height = pInfo->biHeight; TRACEUSER( "Jonathan", _T("PNG write: Width = %d Height = %d\n"),Width,Height); BitsPerPixel = pInfo->biBitCount; // Start up the PNG writing code // allocate the necessary structures // Use the default handlers png_ptr = png_create_write_struct_2( PNG_LIBPNG_VER_STRING, // libpng version 0, // Optional pointer to be sent with errors camelot_png_error, // Function called in case of error camelot_png_warning, // Function called for warnings 0, // Optional pointer to be sent with mem ops camelot_png_malloc, // Function called to alloc memory camelot_png_free // Function called to free memory ); if (!png_ptr) File->GotError( _R(IDS_OUT_OF_MEMORY) ); info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_write_struct(&png_ptr, (png_infopp)NULL); File->GotError( _R(IDS_OUT_OF_MEMORY) ); } // set up the input control to the fstream class // If not a disk file then panic for the present moment // Could use the memfile functions for reading and writing as they give us what // we want. Use the io_ptr and iostream* pFStream = File->GetIOFile(); if (pFStream == NULL) { TRACEUSER( "Jonathan", _T("PNG write: OutputPNG::OutputPNGHeader No access to IOStream!")); File->GotError( _R(IDS_UNKNOWN_PNG_ERROR) ); } // Should use our own function png_set_write_fn(png_ptr, pFStream, camelot_png_write_data, camelot_png_flush_data); // png_init_io(png_ptr, pFStream); // You now have the option of modifying how the compression library // will run. The following functions are mainly for testing, but // may be useful in certain special cases, like if you need to // write png files extremely fast and are willing to give up some // compression, or if you want to get the maximum possible compression // at the expense of slower writing. If you have no special needs // in this area, let the library do what it wants, as it has been // carefully tuned to deliver the best speed/compression ratio. // See the compression library for more details. // turn on or off filtering (1 or 0) //png_set_filtering(png_ptr, 1); // compression level (0 - none, 6 - default, 9 - maximum) //png_set_compression_level(png_ptr, Z_BEST_COMPRESSION); //png_set_compression_mem_level(png_ptr, 8); //png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY); //png_set_compression_window_bits(png_ptr, 15); //png_set_compression_method(png_ptr, 8); // - this describes which optional chunks to write to the // file. Note that if you are writing a // PNG_COLOR_TYPE_PALETTE file, the PLTE chunk is not // optional, but must still be marked for writing. To // mark chunks for writing, OR valid with the // appropriate PNG_INFO_<chunk name> define. png_get_valid(png_ptr, info_ptr, 0); // resolution of image png_set_invalid(png_ptr, info_ptr, PNG_INFO_pHYs); png_set_pHYs(png_ptr, info_ptr, pInfo->biXPelsPerMeter, pInfo->biYPelsPerMeter, 1); //meter TRACEUSER( "Jonathan", _T("PNG write: x,y px per cm = %d %d\n"), png_get_x_pixels_per_meter(png_ptr, info_ptr) / 1000, png_get_y_pixels_per_meter(png_ptr, info_ptr) / 1000); BitsPerPixel = pInfo->biBitCount; TRACEUSER( "Jonathan", _T("PNG write: Bitdepth = %d\n"), BitsPerPixel); palette = NULL; num_palette = 0; trans = NULL; // - array of transparent entries for paletted images num_trans = 0; // - number of transparent entries TRACEUSER( "Jonathan", _T("PNG write: TransColour = %d\n"), TransparentColour); if ( BitsPerPixel <= 8 ) { png_set_IHDR(png_ptr, info_ptr, Width, Height, BitsPerPixel, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); // set the palette if there is one png_set_invalid(png_ptr, info_ptr, PNG_INFO_PLTE); INT32 PaletteEntries = pInfo->biClrUsed; palette = (png_colorp)png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH * sizeof (png_color)); if (palette == NULL) File->GotError( _R(IDS_OUT_OF_MEMORY) ); png_set_PLTE(png_ptr, info_ptr, palette, num_palette); png_color_struct * pPNGPalette = palette; // ... set palette colors ... if (pQuadPalette && PaletteEntries > 0) { // Palette supplied in RGBQUAD form for (INT32 i = 0; i < PaletteEntries; i++) { pPNGPalette->red = pQuadPalette->rgbRed; pPNGPalette->green = pQuadPalette->rgbGreen; pPNGPalette->blue = pQuadPalette->rgbBlue; // skip to the next palette entry pQuadPalette++; pPNGPalette++; } } else if (pPalette && PaletteEntries > 0) { // Palette supplied in LOGPALETTE form for (INT32 i = 0; i < PaletteEntries; i++) { pPNGPalette->red = pPalette->palPalEntry[i].peRed; pPNGPalette->green = pPalette->palPalEntry[i].peGreen; pPNGPalette->blue = pPalette->palPalEntry[i].peBlue; pPNGPalette++; } } else File->GotError(_R(IDS_PNG_ERR_WRITE_PALETTE)); // Now check to see if transparency is present or not if (TransparentColour >= 0 && TransparentColour <= PaletteEntries ) { // Create the array of transparent entries for this palette // 0 is fully transparent, 255 is fully opaque, regardless of image bit depth // We will only create as many as we require, i.e. up to the transparent colour entry // rather a full palettes worth INT32 NumEntries = TransparentColour + 1; trans = (png_byte*)png_malloc(png_ptr, NumEntries * sizeof (png_byte)); if (trans) { // Set the number of transparent entries num_trans = NumEntries; png_byte * pTransEntry = trans; png_set_invalid(png_ptr, info_ptr, PNG_INFO_tRNS); for (INT32 i = 0; i < TransparentColour; i++) { *pTransEntry = 255; // set it fully opaque pTransEntry++; } // We should now be at the transparent entry so set it fully transparent *pTransEntry = 0; } } } else if (BitsPerPixel == 24) { png_set_IHDR(png_ptr, info_ptr, Width, Height, 8, /* bit_depth */ PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); } else if (BitsPerPixel == 32) { png_set_IHDR(png_ptr, info_ptr, Width, Height, 8, /* bit_depth */ PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); } else ERROR2(FALSE,"OutputPNG::OutputPNGHeader Unknown bit depth"); TRACEUSER( "Jonathan", _T("PNG write: bit_depth = %d color_type = %d\n"), png_get_bit_depth(png_ptr, info_ptr), png_get_color_type(png_ptr, info_ptr)); // Could use:- // if we are dealing with a grayscale image then //info_ptr->sig_bit.gray = true_bit_depth; png_set_hIST(png_ptr, info_ptr, NULL); png_set_text(png_ptr, info_ptr, NULL, 0); // write the file information png_write_info(png_ptr, info_ptr); TRACEUSER( "Jonathan", _T("PNG write: pixel_depth %d channels %d\n"), png_get_bit_depth(png_ptr, info_ptr), png_get_channels(png_ptr, info_ptr)); TRACEUSER( "Jonathan", _T("PNG write: rowbytes %d color_type %d\n"), png_get_rowbytes(png_ptr, info_ptr), png_get_color_type(png_ptr, info_ptr)); // Set up the transformations you want. // Note: that these are all optional. Only call them if you want them // invert monocrome pixels //png_set_invert(png_ptr); // shift the pixels up to a legal bit depth and fill in as appropriate // to correctly scale the image //png_set_shift(png_ptr, &(info_ptr->sig_bit)); // pack pixels into bytes //png_set_packing(png_ptr); png_set_bgr(png_ptr); // swap bytes of 16 bit files to most significant bit first png_set_swap(png_ptr); // Must set the exception throwing and reporting flags back to their entry states File->SetThrowExceptions( OldThrowingState ); File->SetReportErrors( OldReportingState ); // er, we seem to have finished OK so say so return TRUE; } catch (...) { // catch our form of a file exception TRACE( _T("OutputPNG::OutputPNGHeader CC catch handler\n")); // Call up function to clean up the png structures CleanUpPngStructures(); // Must set the exception throwing and reporting flags back to their entry states File->SetThrowExceptions( OldThrowingState ); File->SetReportErrors( OldReportingState ); // We have finished so reset the PNG exception handling PNGUtil::SetCCFilePointer(NULL); return FALSE; } ERROR2( FALSE, "Escaped exception clause somehow" ); }
/* * Sets the precision of the alpha channel to a given value; * if this value is 0, resets (i.e. sets to MAX) the alpha channel. * Returns 1 if this operation is applied, or 0 otherwise. */ static int opng_transform_set_alpha_precision(png_structp libpng_ptr, png_infop info_ptr, int alpha_precision) { int result; png_uint_32 width, height; int bit_depth, color_type; png_bytep trans_alpha; int num_trans; png_bytepp row_ptr; png_bytep alpha_ptr; unsigned int num_channels, channel_size, pixel_size; unsigned int alpha_offset; png_uint_32 i, j; int k; void (*set_precision_fn)(png_bytep, int); png_get_IHDR(libpng_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL); if (bit_depth <= 8) { if (alpha_precision >= 8) return 0; set_precision_fn = opng_set_precision8; } else { if (alpha_precision >= 16) return 0; set_precision_fn = opng_set_precision16; } result = 0; if (png_get_valid(libpng_ptr, info_ptr, PNG_INFO_tRNS)) { if (alpha_precision > 0) { /* Set the precision of tRNS samples. */ png_get_tRNS(libpng_ptr, info_ptr, &trans_alpha, &num_trans, NULL); if (trans_alpha != NULL) { for (k = 0; k < num_trans; ++k) opng_set_precision8(trans_alpha + k, alpha_precision); result = 1; } /* trans_color needs no transformation */ } else /* alpha_precision == 0 */ { /* Invalidate tRNS. */ png_set_invalid(libpng_ptr, info_ptr, PNG_INFO_tRNS); result = 1; } /* Continue regardless whether PLTE is present. * PLTE may exist even if the color type is not palette. */ } if (color_type & PNG_COLOR_MASK_ALPHA) { row_ptr = png_get_rows(libpng_ptr, info_ptr); channel_size = (bit_depth > 8) ? 2 : 1; num_channels = png_get_channels(libpng_ptr, info_ptr); pixel_size = num_channels * channel_size; alpha_offset = pixel_size - channel_size; if (alpha_precision > 0) { /* Set the precision of the alpha channel. */ for (i = 0; i < height; ++i, ++row_ptr) { alpha_ptr = *row_ptr + alpha_offset; for (j = 0; j < width; ++j, alpha_ptr += pixel_size) (*set_precision_fn)(alpha_ptr, alpha_precision); } } else /* alpha_precision == 0 */ { /* Set the alpha channel to opaque. */ for (i = 0; i < height; ++i, ++row_ptr) { alpha_ptr = *row_ptr + alpha_offset; for (j = 0; j < width; ++j, alpha_ptr += pixel_size) { alpha_ptr[0] = 255; alpha_ptr[channel_size - 1] = 255; } } } result = 1; } return result; }