/* * 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. */ png_uint_32 /* PRIVATE */ opng_reduce_bits(png_structp png_ptr, png_infop info_ptr, png_uint_32 reductions) { png_bytepp row_ptr; png_bytep src_ptr, dest_ptr; png_uint_32 height, width, i, j; unsigned int src_bit_depth, dest_bit_depth; unsigned int src_byte_depth, dest_byte_depth; unsigned int src_color_type, dest_color_type; unsigned int src_channels, dest_channels; unsigned int src_sample_size, dest_sample_size; unsigned int dest_pixel_depth; unsigned int src_offset_alpha; unsigned int tran_tbl[8]; unsigned int k; opng_debug(1, "in opng_reduce_bits"); /* See which reductions may be performed. */ reductions = opng_analyze_bits(png_ptr, info_ptr, reductions); /* Strip the filler even if it is not an alpha channel. */ if (png_ptr->transformations & PNG_FILLER) reductions |= OPNG_REDUCE_STRIP_ALPHA; if (reductions == OPNG_REDUCE_NONE) return OPNG_REDUCE_NONE; /* nothing can be reduced */ /* Compute the new image parameters bit_depth, color_type, etc. */ src_bit_depth = info_ptr->bit_depth; 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; src_byte_depth = src_bit_depth / 8; dest_byte_depth = dest_bit_depth / 8; src_color_type = dest_color_type = info_ptr->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; } src_channels = (png_ptr->usr_channels > 0) ? png_ptr->usr_channels : info_ptr->channels; dest_channels = ((dest_color_type & PNG_COLOR_MASK_COLOR) ? 3 : 1) + ((dest_color_type & PNG_COLOR_MASK_ALPHA) ? 1 : 0); src_sample_size = src_channels * src_byte_depth; dest_sample_size = dest_channels * dest_byte_depth; dest_pixel_depth = dest_channels * dest_bit_depth; if (!(png_ptr->transformations & PNG_FILLER) || (png_ptr->flags & PNG_FLAG_FILLER_AFTER)) src_offset_alpha = (src_channels - 1) * src_byte_depth; else src_offset_alpha = 0; /* Pre-compute the intra-sample translation table. */ for (k = 0; k < 4 * dest_byte_depth; ++k) tran_tbl[k] = k * src_bit_depth / dest_bit_depth; /* If rgb -> gray and the alpha channel remains in the right, shift the alpha component two positions to the left. */ if ((reductions & OPNG_REDUCE_RGB_TO_GRAY) && (dest_color_type & PNG_COLOR_MASK_ALPHA) && (src_offset_alpha != 0)) { 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]; } /* If alpha is in the left, and it is being stripped, shift the components that come after it. */ if ((src_channels == 2 || src_channels == 4) /* alpha or filler */ && !(dest_color_type & PNG_COLOR_MASK_ALPHA) && (src_offset_alpha == 0)) { for (k = 0; k < dest_sample_size; ) { if (dest_byte_depth == 1) { tran_tbl[k] = tran_tbl[k + 1]; ++k; } else { tran_tbl[k] = tran_tbl[k + 2]; tran_tbl[k + 1] = tran_tbl[k + 3]; k += 2; } } } /* Translate the samples to the new image type. */ OPNG_ASSERT(src_sample_size > dest_sample_size); row_ptr = info_ptr->row_pointers; height = info_ptr->height; width = info_ptr->width; for (i = 0; i < height; ++i, ++row_ptr) { src_ptr = dest_ptr = *row_ptr; for (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; } } #if defined(PNG_bKGD_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) /* Update the ancillary chunk info. */ if (info_ptr->valid & PNG_INFO_bKGD) { png_color_16p background = &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 #if defined(PNG_sBIT_SUPPORTED) if (info_ptr->valid & PNG_INFO_sBIT) { png_color_8p sig_bits = &info_ptr->sig_bit; if (reductions & OPNG_REDUCE_16_TO_8) { if (sig_bits->red > 8) png_ptr->sig_bit.red = sig_bits->red = 8; if (sig_bits->green > 8) png_ptr->sig_bit.green = sig_bits->green = 8; if (sig_bits->blue > 8) png_ptr->sig_bit.blue = sig_bits->blue = 8; if (sig_bits->gray > 8) png_ptr->sig_bit.gray = sig_bits->gray = 8; if (sig_bits->alpha > 8) png_ptr->sig_bit.alpha = sig_bits->alpha = 8; } if (reductions & OPNG_REDUCE_RGB_TO_GRAY) { png_byte max_sig_bit = sig_bits->red; if (max_sig_bit < sig_bits->green) max_sig_bit = sig_bits->green; if (max_sig_bit < sig_bits->blue) max_sig_bit = sig_bits->blue; png_ptr->sig_bit.gray = sig_bits->gray = max_sig_bit; } } #endif if (info_ptr->valid & PNG_INFO_tRNS) { png_color_16p trans_color = &info_ptr->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); info_ptr->valid &= ~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); info_ptr->valid &= ~PNG_INFO_tRNS; } } } /* Update the image info. */ png_ptr->rowbytes = info_ptr->rowbytes = 0; png_ptr->bit_depth = info_ptr->bit_depth = (png_byte)dest_bit_depth; png_ptr->color_type = info_ptr->color_type = (png_byte)dest_color_type; png_ptr->channels = info_ptr->channels = (png_byte)dest_channels; png_ptr->pixel_depth = info_ptr->pixel_depth = (png_byte)dest_pixel_depth; if (reductions & OPNG_REDUCE_STRIP_ALPHA) { png_ptr->transformations &= ~PNG_FILLER; if (png_ptr->usr_channels > 0) --png_ptr->usr_channels; } return reductions; }
/* * 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; }