michael@0: michael@0: /* pngwtran.c - transforms the data in a row for PNG writers michael@0: * michael@0: * Last changed in libpng 1.6.9 [February 6, 2014] michael@0: * Copyright (c) 1998-2014 Glenn Randers-Pehrson michael@0: * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) michael@0: * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) michael@0: * michael@0: * This code is released under the libpng license. michael@0: * For conditions of distribution and use, see the disclaimer michael@0: * and license in png.h michael@0: */ michael@0: michael@0: #include "pngpriv.h" michael@0: michael@0: #ifdef PNG_WRITE_SUPPORTED michael@0: #ifdef PNG_WRITE_TRANSFORMS_SUPPORTED michael@0: michael@0: #ifdef PNG_WRITE_PACK_SUPPORTED michael@0: /* Pack pixels into bytes. Pass the true bit depth in bit_depth. The michael@0: * row_info bit depth should be 8 (one pixel per byte). The channels michael@0: * should be 1 (this only happens on grayscale and paletted images). michael@0: */ michael@0: static void michael@0: png_do_pack(png_row_infop row_info, png_bytep row, png_uint_32 bit_depth) michael@0: { michael@0: png_debug(1, "in png_do_pack"); michael@0: michael@0: if (row_info->bit_depth == 8 && michael@0: row_info->channels == 1) michael@0: { michael@0: switch ((int)bit_depth) michael@0: { michael@0: case 1: michael@0: { michael@0: png_bytep sp, dp; michael@0: int mask, v; michael@0: png_uint_32 i; michael@0: png_uint_32 row_width = row_info->width; michael@0: michael@0: sp = row; michael@0: dp = row; michael@0: mask = 0x80; michael@0: v = 0; michael@0: michael@0: for (i = 0; i < row_width; i++) michael@0: { michael@0: if (*sp != 0) michael@0: v |= mask; michael@0: michael@0: sp++; michael@0: michael@0: if (mask > 1) michael@0: mask >>= 1; michael@0: michael@0: else michael@0: { michael@0: mask = 0x80; michael@0: *dp = (png_byte)v; michael@0: dp++; michael@0: v = 0; michael@0: } michael@0: } michael@0: michael@0: if (mask != 0x80) michael@0: *dp = (png_byte)v; michael@0: michael@0: break; michael@0: } michael@0: michael@0: case 2: michael@0: { michael@0: png_bytep sp, dp; michael@0: int shift, v; michael@0: png_uint_32 i; michael@0: png_uint_32 row_width = row_info->width; michael@0: michael@0: sp = row; michael@0: dp = row; michael@0: shift = 6; michael@0: v = 0; michael@0: michael@0: for (i = 0; i < row_width; i++) michael@0: { michael@0: png_byte value; michael@0: michael@0: value = (png_byte)(*sp & 0x03); michael@0: v |= (value << shift); michael@0: michael@0: if (shift == 0) michael@0: { michael@0: shift = 6; michael@0: *dp = (png_byte)v; michael@0: dp++; michael@0: v = 0; michael@0: } michael@0: michael@0: else michael@0: shift -= 2; michael@0: michael@0: sp++; michael@0: } michael@0: michael@0: if (shift != 6) michael@0: *dp = (png_byte)v; michael@0: michael@0: break; michael@0: } michael@0: michael@0: case 4: michael@0: { michael@0: png_bytep sp, dp; michael@0: int shift, v; michael@0: png_uint_32 i; michael@0: png_uint_32 row_width = row_info->width; michael@0: michael@0: sp = row; michael@0: dp = row; michael@0: shift = 4; michael@0: v = 0; michael@0: michael@0: for (i = 0; i < row_width; i++) michael@0: { michael@0: png_byte value; michael@0: michael@0: value = (png_byte)(*sp & 0x0f); michael@0: v |= (value << shift); michael@0: michael@0: if (shift == 0) michael@0: { michael@0: shift = 4; michael@0: *dp = (png_byte)v; michael@0: dp++; michael@0: v = 0; michael@0: } michael@0: michael@0: else michael@0: shift -= 4; michael@0: michael@0: sp++; michael@0: } michael@0: michael@0: if (shift != 4) michael@0: *dp = (png_byte)v; michael@0: michael@0: break; michael@0: } michael@0: michael@0: default: michael@0: break; michael@0: } michael@0: michael@0: row_info->bit_depth = (png_byte)bit_depth; michael@0: row_info->pixel_depth = (png_byte)(bit_depth * row_info->channels); michael@0: row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, michael@0: row_info->width); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: #ifdef PNG_WRITE_SHIFT_SUPPORTED michael@0: /* Shift pixel values to take advantage of whole range. Pass the michael@0: * true number of bits in bit_depth. The row should be packed michael@0: * according to row_info->bit_depth. Thus, if you had a row of michael@0: * bit depth 4, but the pixels only had values from 0 to 7, you michael@0: * would pass 3 as bit_depth, and this routine would translate the michael@0: * data to 0 to 15. michael@0: */ michael@0: static void michael@0: png_do_shift(png_row_infop row_info, png_bytep row, michael@0: png_const_color_8p bit_depth) michael@0: { michael@0: png_debug(1, "in png_do_shift"); michael@0: michael@0: if (row_info->color_type != PNG_COLOR_TYPE_PALETTE) michael@0: { michael@0: int shift_start[4], shift_dec[4]; michael@0: int channels = 0; michael@0: michael@0: if (row_info->color_type & PNG_COLOR_MASK_COLOR) michael@0: { michael@0: shift_start[channels] = row_info->bit_depth - bit_depth->red; michael@0: shift_dec[channels] = bit_depth->red; michael@0: channels++; michael@0: michael@0: shift_start[channels] = row_info->bit_depth - bit_depth->green; michael@0: shift_dec[channels] = bit_depth->green; michael@0: channels++; michael@0: michael@0: shift_start[channels] = row_info->bit_depth - bit_depth->blue; michael@0: shift_dec[channels] = bit_depth->blue; michael@0: channels++; michael@0: } michael@0: michael@0: else michael@0: { michael@0: shift_start[channels] = row_info->bit_depth - bit_depth->gray; michael@0: shift_dec[channels] = bit_depth->gray; michael@0: channels++; michael@0: } michael@0: michael@0: if (row_info->color_type & PNG_COLOR_MASK_ALPHA) michael@0: { michael@0: shift_start[channels] = row_info->bit_depth - bit_depth->alpha; michael@0: shift_dec[channels] = bit_depth->alpha; michael@0: channels++; michael@0: } michael@0: michael@0: /* With low row depths, could only be grayscale, so one channel */ michael@0: if (row_info->bit_depth < 8) michael@0: { michael@0: png_bytep bp = row; michael@0: png_size_t i; michael@0: unsigned int mask; michael@0: png_size_t row_bytes = row_info->rowbytes; michael@0: michael@0: if (bit_depth->gray == 1 && row_info->bit_depth == 2) michael@0: mask = 0x55; michael@0: michael@0: else if (row_info->bit_depth == 4 && bit_depth->gray == 3) michael@0: mask = 0x11; michael@0: michael@0: else michael@0: mask = 0xff; michael@0: michael@0: for (i = 0; i < row_bytes; i++, bp++) michael@0: { michael@0: int j; michael@0: unsigned int v, out; michael@0: michael@0: v = *bp; michael@0: out = 0; michael@0: michael@0: for (j = shift_start[0]; j > -shift_dec[0]; j -= shift_dec[0]) michael@0: { michael@0: if (j > 0) michael@0: out |= v << j; michael@0: michael@0: else michael@0: out |= (v >> (-j)) & mask; michael@0: } michael@0: michael@0: *bp = (png_byte)(out & 0xff); michael@0: } michael@0: } michael@0: michael@0: else if (row_info->bit_depth == 8) michael@0: { michael@0: png_bytep bp = row; michael@0: png_uint_32 i; michael@0: png_uint_32 istop = channels * row_info->width; michael@0: michael@0: for (i = 0; i < istop; i++, bp++) michael@0: { michael@0: michael@0: const unsigned int c = i%channels; michael@0: int j; michael@0: unsigned int v, out; michael@0: michael@0: v = *bp; michael@0: out = 0; michael@0: michael@0: for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) michael@0: { michael@0: if (j > 0) michael@0: out |= v << j; michael@0: michael@0: else michael@0: out |= v >> (-j); michael@0: } michael@0: michael@0: *bp = (png_byte)(out & 0xff); michael@0: } michael@0: } michael@0: michael@0: else michael@0: { michael@0: png_bytep bp; michael@0: png_uint_32 i; michael@0: png_uint_32 istop = channels * row_info->width; michael@0: michael@0: for (bp = row, i = 0; i < istop; i++) michael@0: { michael@0: const unsigned int c = i%channels; michael@0: int j; michael@0: unsigned int value, v; michael@0: michael@0: v = png_get_uint_16(bp); michael@0: value = 0; michael@0: michael@0: for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) michael@0: { michael@0: if (j > 0) michael@0: value |= v << j; michael@0: michael@0: else michael@0: value |= v >> (-j); michael@0: } michael@0: *bp++ = (png_byte)((value >> 8) & 0xff); michael@0: *bp++ = (png_byte)(value & 0xff); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: #ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED michael@0: static void michael@0: png_do_write_swap_alpha(png_row_infop row_info, png_bytep row) michael@0: { michael@0: png_debug(1, "in png_do_write_swap_alpha"); michael@0: michael@0: { michael@0: if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) michael@0: { michael@0: if (row_info->bit_depth == 8) michael@0: { michael@0: /* This converts from ARGB to RGBA */ michael@0: png_bytep sp, dp; michael@0: png_uint_32 i; michael@0: png_uint_32 row_width = row_info->width; michael@0: michael@0: for (i = 0, sp = dp = row; i < row_width; i++) michael@0: { michael@0: png_byte save = *(sp++); michael@0: *(dp++) = *(sp++); michael@0: *(dp++) = *(sp++); michael@0: *(dp++) = *(sp++); michael@0: *(dp++) = save; michael@0: } michael@0: } michael@0: michael@0: #ifdef PNG_WRITE_16BIT_SUPPORTED michael@0: else michael@0: { michael@0: /* This converts from AARRGGBB to RRGGBBAA */ michael@0: png_bytep sp, dp; michael@0: png_uint_32 i; michael@0: png_uint_32 row_width = row_info->width; michael@0: michael@0: for (i = 0, sp = dp = row; i < row_width; i++) michael@0: { michael@0: png_byte save[2]; michael@0: save[0] = *(sp++); michael@0: save[1] = *(sp++); michael@0: *(dp++) = *(sp++); michael@0: *(dp++) = *(sp++); michael@0: *(dp++) = *(sp++); michael@0: *(dp++) = *(sp++); michael@0: *(dp++) = *(sp++); michael@0: *(dp++) = *(sp++); michael@0: *(dp++) = save[0]; michael@0: *(dp++) = save[1]; michael@0: } michael@0: } michael@0: #endif /* PNG_WRITE_16BIT_SUPPORTED */ michael@0: } michael@0: michael@0: else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) michael@0: { michael@0: if (row_info->bit_depth == 8) michael@0: { michael@0: /* This converts from AG to GA */ michael@0: png_bytep sp, dp; michael@0: png_uint_32 i; michael@0: png_uint_32 row_width = row_info->width; michael@0: michael@0: for (i = 0, sp = dp = row; i < row_width; i++) michael@0: { michael@0: png_byte save = *(sp++); michael@0: *(dp++) = *(sp++); michael@0: *(dp++) = save; michael@0: } michael@0: } michael@0: michael@0: #ifdef PNG_WRITE_16BIT_SUPPORTED michael@0: else michael@0: { michael@0: /* This converts from AAGG to GGAA */ michael@0: png_bytep sp, dp; michael@0: png_uint_32 i; michael@0: png_uint_32 row_width = row_info->width; michael@0: michael@0: for (i = 0, sp = dp = row; i < row_width; i++) michael@0: { michael@0: png_byte save[2]; michael@0: save[0] = *(sp++); michael@0: save[1] = *(sp++); michael@0: *(dp++) = *(sp++); michael@0: *(dp++) = *(sp++); michael@0: *(dp++) = save[0]; michael@0: *(dp++) = save[1]; michael@0: } michael@0: } michael@0: #endif /* PNG_WRITE_16BIT_SUPPORTED */ michael@0: } michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: #ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED michael@0: static void michael@0: png_do_write_invert_alpha(png_row_infop row_info, png_bytep row) michael@0: { michael@0: png_debug(1, "in png_do_write_invert_alpha"); michael@0: michael@0: { michael@0: if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) michael@0: { michael@0: if (row_info->bit_depth == 8) michael@0: { michael@0: /* This inverts the alpha channel in RGBA */ michael@0: png_bytep sp, dp; michael@0: png_uint_32 i; michael@0: png_uint_32 row_width = row_info->width; michael@0: michael@0: for (i = 0, sp = dp = row; i < row_width; i++) michael@0: { michael@0: /* Does nothing michael@0: *(dp++) = *(sp++); michael@0: *(dp++) = *(sp++); michael@0: *(dp++) = *(sp++); michael@0: */ michael@0: sp+=3; dp = sp; michael@0: *(dp++) = (png_byte)(255 - *(sp++)); michael@0: } michael@0: } michael@0: michael@0: #ifdef PNG_WRITE_16BIT_SUPPORTED michael@0: else michael@0: { michael@0: /* This inverts the alpha channel in RRGGBBAA */ michael@0: png_bytep sp, dp; michael@0: png_uint_32 i; michael@0: png_uint_32 row_width = row_info->width; michael@0: michael@0: for (i = 0, sp = dp = row; i < row_width; i++) michael@0: { michael@0: /* Does nothing michael@0: *(dp++) = *(sp++); michael@0: *(dp++) = *(sp++); michael@0: *(dp++) = *(sp++); michael@0: *(dp++) = *(sp++); michael@0: *(dp++) = *(sp++); michael@0: *(dp++) = *(sp++); michael@0: */ michael@0: sp+=6; dp = sp; michael@0: *(dp++) = (png_byte)(255 - *(sp++)); michael@0: *(dp++) = (png_byte)(255 - *(sp++)); michael@0: } michael@0: } michael@0: #endif /* PNG_WRITE_16BIT_SUPPORTED */ michael@0: } michael@0: michael@0: else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) michael@0: { michael@0: if (row_info->bit_depth == 8) michael@0: { michael@0: /* This inverts the alpha channel in GA */ michael@0: png_bytep sp, dp; michael@0: png_uint_32 i; michael@0: png_uint_32 row_width = row_info->width; michael@0: michael@0: for (i = 0, sp = dp = row; i < row_width; i++) michael@0: { michael@0: *(dp++) = *(sp++); michael@0: *(dp++) = (png_byte)(255 - *(sp++)); michael@0: } michael@0: } michael@0: michael@0: #ifdef PNG_WRITE_16BIT_SUPPORTED michael@0: else michael@0: { michael@0: /* This inverts the alpha channel in GGAA */ michael@0: png_bytep sp, dp; michael@0: png_uint_32 i; michael@0: png_uint_32 row_width = row_info->width; michael@0: michael@0: for (i = 0, sp = dp = row; i < row_width; i++) michael@0: { michael@0: /* Does nothing michael@0: *(dp++) = *(sp++); michael@0: *(dp++) = *(sp++); michael@0: */ michael@0: sp+=2; dp = sp; michael@0: *(dp++) = (png_byte)(255 - *(sp++)); michael@0: *(dp++) = (png_byte)(255 - *(sp++)); michael@0: } michael@0: } michael@0: #endif /* PNG_WRITE_16BIT_SUPPORTED */ michael@0: } michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: /* Transform the data according to the user's wishes. The order of michael@0: * transformations is significant. michael@0: */ michael@0: void /* PRIVATE */ michael@0: png_do_write_transformations(png_structrp png_ptr, png_row_infop row_info) michael@0: { michael@0: png_debug(1, "in png_do_write_transformations"); michael@0: michael@0: if (png_ptr == NULL) michael@0: return; michael@0: michael@0: #ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED michael@0: if (png_ptr->transformations & PNG_USER_TRANSFORM) michael@0: if (png_ptr->write_user_transform_fn != NULL) michael@0: (*(png_ptr->write_user_transform_fn)) /* User write transform michael@0: function */ michael@0: (png_ptr, /* png_ptr */ michael@0: row_info, /* row_info: */ michael@0: /* png_uint_32 width; width of row */ michael@0: /* png_size_t rowbytes; number of bytes in row */ michael@0: /* png_byte color_type; color type of pixels */ michael@0: /* png_byte bit_depth; bit depth of samples */ michael@0: /* png_byte channels; number of channels (1-4) */ michael@0: /* png_byte pixel_depth; bits per pixel (depth*channels) */ michael@0: png_ptr->row_buf + 1); /* start of pixel data for row */ michael@0: #endif michael@0: michael@0: #ifdef PNG_WRITE_FILLER_SUPPORTED michael@0: if (png_ptr->transformations & PNG_FILLER) michael@0: png_do_strip_channel(row_info, png_ptr->row_buf + 1, michael@0: !(png_ptr->flags & PNG_FLAG_FILLER_AFTER)); michael@0: #endif michael@0: michael@0: #ifdef PNG_WRITE_PACKSWAP_SUPPORTED michael@0: if (png_ptr->transformations & PNG_PACKSWAP) michael@0: png_do_packswap(row_info, png_ptr->row_buf + 1); michael@0: #endif michael@0: michael@0: #ifdef PNG_WRITE_PACK_SUPPORTED michael@0: if (png_ptr->transformations & PNG_PACK) michael@0: png_do_pack(row_info, png_ptr->row_buf + 1, michael@0: (png_uint_32)png_ptr->bit_depth); michael@0: #endif michael@0: michael@0: #ifdef PNG_WRITE_SWAP_SUPPORTED michael@0: if (png_ptr->transformations & PNG_SWAP_BYTES) michael@0: png_do_swap(row_info, png_ptr->row_buf + 1); michael@0: #endif michael@0: michael@0: #ifdef PNG_WRITE_SHIFT_SUPPORTED michael@0: if (png_ptr->transformations & PNG_SHIFT) michael@0: png_do_shift(row_info, png_ptr->row_buf + 1, michael@0: &(png_ptr->shift)); michael@0: #endif michael@0: michael@0: #ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED michael@0: if (png_ptr->transformations & PNG_SWAP_ALPHA) michael@0: png_do_write_swap_alpha(row_info, png_ptr->row_buf + 1); michael@0: #endif michael@0: michael@0: #ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED michael@0: if (png_ptr->transformations & PNG_INVERT_ALPHA) michael@0: png_do_write_invert_alpha(row_info, png_ptr->row_buf + 1); michael@0: #endif michael@0: michael@0: #ifdef PNG_WRITE_BGR_SUPPORTED michael@0: if (png_ptr->transformations & PNG_BGR) michael@0: png_do_bgr(row_info, png_ptr->row_buf + 1); michael@0: #endif michael@0: michael@0: #ifdef PNG_WRITE_INVERT_SUPPORTED michael@0: if (png_ptr->transformations & PNG_INVERT_MONO) michael@0: png_do_invert(row_info, png_ptr->row_buf + 1); michael@0: #endif michael@0: } michael@0: #endif /* PNG_WRITE_TRANSFORMS_SUPPORTED */ michael@0: #endif /* PNG_WRITE_SUPPORTED */