/* pngwtran.c - transforms the data in a row for png writers libpng 1.0 beta 2 - version 0.81 For conditions of distribution and use, see copyright notice in png.h Copyright (c) 1995 Guy Eric Schalnat, Group 42, Inc. August 24, 1995 */ #define PNG_INTERNAL #include "png.h" /* transform the data according to the users wishes. The order of transformations is significant. */ void png_do_write_transformations(png_struct *png_ptr) { #if defined(PNG_WRITE_FILLER_SUPPORTED) if (png_ptr->transformations & PNG_RGBA) png_do_write_filler(&(png_ptr->row_info), png_ptr->row_buf + 1, png_ptr->filler_loc); #endif #if defined(PNG_WRITE_PACK_SUPPORTED) if (png_ptr->transformations & PNG_PACK) png_do_pack(&(png_ptr->row_info), png_ptr->row_buf + 1, png_ptr->bit_depth); #endif #if defined(PNG_WRITE_SHIFT_SUPPORTED) if (png_ptr->transformations & PNG_SHIFT) png_do_shift(&(png_ptr->row_info), png_ptr->row_buf + 1, &(png_ptr->shift)); #endif #if defined(PNG_WRITE_SWAP_SUPPORTED) if (png_ptr->transformations & PNG_SWAP_BYTES) png_do_swap(&(png_ptr->row_info), png_ptr->row_buf + 1); #endif #if defined(PNG_WRITE_BGR_SUPPORTED) if (png_ptr->transformations & PNG_BGR) png_do_bgr(&(png_ptr->row_info), png_ptr->row_buf + 1); #endif #if defined(PNG_WRITE_INVERT_SUPPORTED) if (png_ptr->transformations & PNG_INVERT_MONO) png_do_invert(&(png_ptr->row_info), png_ptr->row_buf + 1); #endif } #if defined(PNG_WRITE_PACK_SUPPORTED) /* pack pixels into bytes. Pass the true bit depth in bit_depth. The row_info bit depth should be 8 (one pixel per byte). The channels should be 1 (this only happens on grayscale and paletted images) */ void png_do_pack(png_row_info *row_info, png_bytef *row, png_byte bit_depth) { if (row_info && row && row_info->bit_depth == 8 && row_info->channels == 1) { switch (bit_depth) { case 1: { png_bytef *sp; png_bytef *dp; int mask; png_int_32 i; int v; sp = row; dp = row; mask = 0x80; v = 0; for (i = 0; i < row_info->width; i++) { if (*sp) v |= mask; sp++; if (mask > 1) mask >>= 1; else { mask = 0x80; *dp = v; dp++; v = 0; } } if (mask != 0x80) *dp = v; break; } case 2: { png_bytef *sp; png_bytef *dp; int shift; png_int_32 i; int v; png_byte value; sp = row; dp = row; shift = 6; v = 0; for (i = 0; i < row_info->width; i++) { value = *sp & 0x3; v |= (value << shift); if (shift == 0) { shift = 6; *dp = v; dp++; v = 0; } else shift -= 2; sp++; } if (shift != 6) *dp = v; break; } case 4: { png_bytef *sp; png_bytef *dp; int shift; png_int_32 i; int v; png_byte value; sp = row; dp = row; shift = 4; v = 0; for (i = 0; i < row_info->width; i++) { value = *sp & 0xf; v |= (value << shift); if (shift == 0) { shift = 4; *dp = v; dp++; v = 0; } else shift -= 4; sp++; } if (shift != 4) *dp = v; break; } } row_info->bit_depth = bit_depth; row_info->pixel_depth = bit_depth * row_info->channels; row_info->rowbytes = ((row_info->width * row_info->pixel_depth + 7) >> 3); } } #endif #if defined(PNG_WRITE_SHIFT_SUPPORTED) /* shift pixel values to take advantage of whole range. Pass the true number of bits in bit_depth. The row should be packed according to row_info->bit_depth. Thus, if you had a row of bit depth 4, but the pixels only had values from 0 to 7, you would pass 3 as bit_depth, and this routine would translate the data to 0 to 15. */ void png_do_shift(png_row_info *row_info, png_bytef *row, png_color_8 *bit_depth) { if (row && row_info && row_info->color_type != PNG_COLOR_TYPE_PALETTE) { int shift_start[4], shift_dec[4]; int channels; channels = 0; if (row_info->color_type & PNG_COLOR_MASK_COLOR) { shift_start[channels] = row_info->bit_depth - bit_depth->red; shift_dec[channels] = bit_depth->red; channels++; shift_start[channels] = row_info->bit_depth - bit_depth->green; shift_dec[channels] = bit_depth->green; channels++; shift_start[channels] = row_info->bit_depth - bit_depth->blue; shift_dec[channels] = bit_depth->blue; channels++; } else { shift_start[channels] = row_info->bit_depth - bit_depth->gray; shift_dec[channels] = bit_depth->gray; channels++; } if (row_info->color_type & PNG_COLOR_MASK_ALPHA) { shift_start[channels] = row_info->bit_depth - bit_depth->alpha; shift_dec[channels] = bit_depth->alpha; channels++; } /* with low row dephts, could only be grayscale, so one channel */ if (row_info->bit_depth < 8) { png_bytef *bp; png_uint_32 i; int j; png_byte mask; if (bit_depth->gray == 1 && row_info->bit_depth == 2) mask = 0x55; else if (row_info->bit_depth == 4 && bit_depth->gray == 3) mask = 0x11; else mask = 0xff; for (bp = row, i = 0; i < row_info->rowbytes; i++, bp++) { int v; v = *bp; *bp = 0; for (j = shift_start[0]; j > -shift_dec[0]; j -= shift_dec[0]) { if (j > 0) *bp |= (png_byte)((v << j) & 0xff); else *bp |= (png_byte)((v >> (-j)) & mask); } } } else if (row_info->bit_depth == 8) { png_bytef *bp; png_uint_32 i; int j; for (bp = row, i = 0; i < row_info->width; i++) { int c; for (c = 0; c < channels; c++, bp++) { int v; v = *bp; *bp = 0; for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) { if (j > 0) *bp |= (png_byte)((v << j) & 0xff); else *bp |= (png_byte)((v >> (-j)) & 0xff); } } } } else { png_bytef *bp; png_uint_32 i; int j; for (bp = row, i = 0; i < row_info->width * row_info->channels; i++) { int c; for (c = 0; c < channels; c++, bp += 2) { png_uint_16 value, v; v = ((png_uint_16)(*bp) << 8) + (png_uint_16)(*(bp + 1)); value = 0; for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) { if (j > 0) value |= (png_uint_16)((v << j) & (png_uint_16)0xffff); else value |= (png_uint_16)((v >> (-j)) & (png_uint_16)0xffff); } *bp = value >> 8; *(bp + 1) = value & 0xff; } } } } } #endif #ifdef PNG_WRITE_FILLER_SUPPORTED /* remove filler byte */ void png_do_write_filler(png_row_info *row_info, png_bytef *row, png_byte filler_loc) { if (row && row_info && row_info->color_type == PNG_COLOR_TYPE_RGB && row_info->bit_depth == 8) { if (filler_loc == PNG_FILLER_AFTER) { png_bytef *sp, *dp; png_uint_32 i; for (i = 1, sp = row + 4, dp = row + 3; i < row_info->width; i++) { *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++; sp++; } row_info->channels = 3; row_info->pixel_depth = 24; row_info->rowbytes = row_info->width * 3; } else { png_bytef *sp, *dp; png_uint_32 i; for (i = 1, sp = row + 4, dp = row + 3; i < row_info->width; i++) { sp++; *dp++ = *sp++; *dp++ = *sp++; *dp++ = *sp++; } row_info->channels = 3; row_info->pixel_depth = 24; row_info->rowbytes = row_info->width * 3; } } } #endif