mirror of
https://git.code.sf.net/p/libpng/code.git
synced 2025-07-10 18:04:09 +02:00

This implements a new chunk parse implementation that can be shared, it is currently shared by the progressive reader and the sequential one (not, yet, the writer). The patch also implements shared transform handling that is used throughout. Signed-off-by: John Bowler <jbowler@acm.org>
379 lines
12 KiB
C
379 lines
12 KiB
C
|
|
/* pngwtran.c - transforms the data in a row for PNG writers
|
|
*
|
|
* Last changed in libpng 1.6.17 [(PENDING RELEASE)]
|
|
* Copyright (c) 1998-2015 Glenn Randers-Pehrson
|
|
* (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
|
|
* (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
|
|
*
|
|
* This code is released under the libpng license.
|
|
* For conditions of distribution and use, see the disclaimer
|
|
* and license in png.h
|
|
*/
|
|
|
|
#include "pngpriv.h"
|
|
#define PNG_SRC_FILE PNG_SRC_FILE_pngwtran
|
|
|
|
#ifdef PNG_WRITE_INTERLACING_SUPPORTED
|
|
/* Pick out the correct pixels for the interlace pass.
|
|
* The basic idea here is to go through the row with a source
|
|
* pointer and a destination pointer (sp and dp), and copy the
|
|
* correct pixels for the pass. As the row gets compacted,
|
|
* sp will always be >= dp, so we should never overwrite anything.
|
|
* See the default: case for the easiest code to understand.
|
|
*/
|
|
static void
|
|
png_do_write_interlace_lbd(png_transformp *transform, png_transform_controlp tc)
|
|
{
|
|
const png_const_structrp png_ptr = tc->png_ptr;
|
|
const unsigned int pass = png_ptr->pass;
|
|
const png_uint_32 row_width = tc->width;
|
|
const png_uint_32 output_width = PNG_PASS_COLS(row_width, pass);
|
|
png_uint_32 i = PNG_PASS_START_COL(pass);
|
|
|
|
|
|
png_debug(1, "in png_do_write_interlace");
|
|
debug(!tc->init);
|
|
|
|
/* The data can be used in place (tc->sp) if the width isn't changed or
|
|
* the first pixel in the output is the first in the input and there is
|
|
* only one pixel in the output; this covers the last pass (PNG pass 7,
|
|
* libpng 6) and PNG passes 1, 3 and 5 with narrow images.
|
|
*/
|
|
tc->width = output_width;
|
|
|
|
if (row_width != output_width && (output_width != 1 || i > 0))
|
|
{
|
|
/* For passes before the last the pixels must be picked from the input
|
|
* row (tc->sp) and placed into the output row (tc->dp).
|
|
*/
|
|
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
|
|
png_bytep dp = png_voidcast(png_bytep, tc->dp);
|
|
const unsigned int inc = PNG_PASS_COL_OFFSET(pass);
|
|
unsigned int B = (*transform)->args & 0x3; /* 0, 1 or 2 */
|
|
|
|
/* The row data will be moved, so do this now before 'dp' is advanced:
|
|
*/
|
|
tc->sp = dp;
|
|
|
|
/* For pixels less than one byte wide the correct pixels have to be
|
|
* extracted from the input bytes. Because we are reading data in
|
|
* the application memory format we cannot rely on the PNG big
|
|
* endian order. Notice that this was apparently broken before
|
|
* 1.7.0.
|
|
*
|
|
* In libpng 1.7.0 libpng uses a classic bit-pump to optimize the
|
|
* extraction. In all passes before the last (6/7) no two pixels
|
|
* are adjacent in the input, so we are always extracting 1 bit.
|
|
* At present the code uses an 8-bit buffer to avoid coding for
|
|
* different byte sexes, but this could easily be changed.
|
|
*
|
|
* 'i' is the bit-index of bit in the input (sp[]), so,
|
|
* considering the 1-bit per pixel case, sp[i>>3] is the byte
|
|
* and the bit is bit (i&7) (0 lowest) on swapped (little endian)
|
|
* data or 7-(i&7) on PNG default (big-endian) data.
|
|
*
|
|
* Define these macros, where:
|
|
*
|
|
* B: the log2 bit depth (0, 1, 2 for 1bpp, 2bpp or 4bpp) of
|
|
* the data; this should be a constant.
|
|
* sp: the source pointer (sp) (a png_const_bytep)
|
|
* i: the pixel index in the input (png_uint_32)
|
|
* j: the bit index in the output (unsigned int)
|
|
*
|
|
* Unlike 'i', 'j' is interpreted directly; for LSB bytes it counts
|
|
* up, for MSB it counts down.
|
|
*
|
|
* NOTE: this could all be expanded to eliminate the code below by
|
|
* the time honoured copy'n'paste into three separate functions. This
|
|
* might be worth doing in the future.
|
|
*/
|
|
# define PIXEL_MASK ((1U << (1<<B))-1U)
|
|
# define BIT_MASK ((1U << (3-(B)))-1U) /* within a byte */
|
|
# define SP_BYTE (sp[i>>(3-(B))]) /* byte to use */
|
|
# define SP_OFFSET_LSB ((BIT_MASK & i) << (B))
|
|
# define SP_OFFSET_MSB ((BIT_MASK & ~i) << (B))
|
|
# define SP_PIXEL(sex) ((SP_BYTE >> SP_OFFSET_ ## sex) & PIXEL_MASK)
|
|
{
|
|
unsigned int j;
|
|
unsigned int d;
|
|
|
|
# ifdef PNG_WRITE_PACKSWAP_SUPPORTED
|
|
if (tc->format & PNG_FORMAT_FLAG_SWAPPED)
|
|
for (j = 0, d = 0; i < row_width; i += inc)
|
|
{ /* little-endian */
|
|
d |= SP_PIXEL(LSB) << j;
|
|
j += 1<<B;
|
|
if (j == 8) *dp++ = png_check_byte(png_ptr, d), j = 0, d = 0;
|
|
}
|
|
|
|
else
|
|
# endif /* WRITE_PACKSWAP */
|
|
for (j = 8, d = 0; i < row_width; i += inc)
|
|
{ /* big-endian */
|
|
j -= 1<<B;
|
|
d |= SP_PIXEL(MSB) << j;
|
|
if (j == 0) *dp++ = png_check_byte(png_ptr, d), j = 8, d = 0;
|
|
}
|
|
|
|
/* The end condition: if j is not 0 the last byte was not
|
|
* written:
|
|
*/
|
|
if (j != 0) *dp = png_check_byte(png_ptr, d);
|
|
}
|
|
# undef PIXEL_MASK
|
|
# undef BIT_MASK
|
|
# undef SP_BYTE
|
|
# undef SP_OFFSET_MSB
|
|
# undef SP_OFFSET_LSB
|
|
# undef SP_PIXEL
|
|
}
|
|
|
|
/* The transform is removed on the last pass. It can be removed earlier
|
|
* in other cases if the row width (the image width) is only 1, however
|
|
* this does not seem worth the overhead to check; PNG pass 5(4) happens
|
|
* if there are just three rows.
|
|
*/
|
|
else /* the source can be used in place */ if (pass == 6)
|
|
(*transform)->fn = NULL; /* remove me to caller */
|
|
}
|
|
|
|
static void
|
|
png_do_write_interlace_byte(png_transformp *transform,
|
|
png_transform_controlp tc)
|
|
{
|
|
const png_const_structrp png_ptr = tc->png_ptr;
|
|
const unsigned int pass = png_ptr->pass;
|
|
const png_uint_32 row_width = tc->width;
|
|
const png_uint_32 output_width = PNG_PASS_COLS(row_width, pass);
|
|
png_uint_32 i = PNG_PASS_START_COL(pass);
|
|
|
|
png_debug(1, "in png_do_write_interlace");
|
|
debug(!tc->init);
|
|
|
|
/* The data can be used in place (tc->sp) if the width isn't changed or
|
|
* the first pixel in the output is the first in the input and there is
|
|
* only one pixel in the output; this covers the last pass (PNG pass 7,
|
|
* libpng 6) and PNG passes 1, 3 and 5 with narrow images.
|
|
*/
|
|
tc->width = output_width;
|
|
|
|
if (row_width != output_width && (output_width != 1 || i > 0))
|
|
{
|
|
/* For passes before the last the pixels must be picked from the input
|
|
* row (tc->sp) and placed into the output row (tc->dp).
|
|
*/
|
|
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
|
|
png_bytep dp = png_voidcast(png_bytep, tc->dp);
|
|
const unsigned int inc = PNG_PASS_COL_OFFSET(pass);
|
|
unsigned int cbytes = (*transform)->args;
|
|
|
|
/* The row data will be moved, so do this now before 'dp' is advanced:
|
|
*/
|
|
tc->sp = dp;
|
|
|
|
/* Loop through the input copying each pixel to the correct place
|
|
* in the output. Note that the loop may be executed 0 times if
|
|
* this is called on a narrow image that does not contain this
|
|
* pass.
|
|
*/
|
|
for (sp += i * cbytes; i < row_width;
|
|
i += inc, sp += inc * cbytes, dp += cbytes)
|
|
if (dp != sp) /* cannot happen in practice */
|
|
memcpy(dp, sp, cbytes);
|
|
}
|
|
|
|
/* The transform is removed on the last pass. It can be removed earlier
|
|
* in other cases if the row width (the image width) is only 1, however
|
|
* this does not seem worth the overhead to check; PNG pass 5(4) happens
|
|
* if there are just three rows.
|
|
*/
|
|
else /* the source can be used in place */ if (pass == 6)
|
|
(*transform)->fn = NULL; /* remove me to caller */
|
|
}
|
|
|
|
static void
|
|
png_init_write_interlace(png_transformp *transform, png_transform_controlp tc)
|
|
{
|
|
# define png_ptr (tc->png_ptr)
|
|
|
|
png_debug(1, "in png_do_write_interlace");
|
|
debug(tc->init);
|
|
|
|
/* Do nothing on PNG_TC_INIT_FORMAT because we don't change the format, bit
|
|
* depth or gamma of the data.
|
|
*/
|
|
if (tc->init == PNG_TC_INIT_FINAL)
|
|
{
|
|
png_transformp tf = *transform;
|
|
unsigned int pixel_depth = PNG_TC_PIXEL_DEPTH(*tc);
|
|
png_uint_16 B = 0;
|
|
|
|
switch (pixel_depth)
|
|
{
|
|
case 4: /* B == 2 */
|
|
++B;
|
|
/* FALL THROUGH */
|
|
case 2: /* B == 1 */
|
|
++B;
|
|
/* FALL THROUGH */
|
|
case 1: /* B == 0 */
|
|
/* This is the low bit depth case: */
|
|
tf->args = B;
|
|
tf->fn = png_do_write_interlace_lbd;
|
|
break;
|
|
|
|
default:
|
|
affirm((pixel_depth & 7) == 0);
|
|
pixel_depth >>= 3;
|
|
affirm(pixel_depth > 0 && pixel_depth <= 8);
|
|
tf->args = pixel_depth & 0xf;
|
|
tf->fn = png_do_write_interlace_byte;
|
|
break;
|
|
}
|
|
}
|
|
# undef png_ptr
|
|
}
|
|
|
|
void /* PRIVATE */
|
|
png_set_write_interlace(png_structrp png_ptr)
|
|
{
|
|
/* This is checked in the caller: */
|
|
debug(png_ptr->interlaced == PNG_INTERLACE_ADAM7);
|
|
png_add_transform(png_ptr, 0, png_init_write_interlace, PNG_TR_INTERLACE);
|
|
}
|
|
#endif /* WRITE_INTERLACING */
|
|
|
|
#ifdef PNG_WRITE_PACK_SUPPORTED
|
|
/* Pack pixels into bytes. */
|
|
static void
|
|
png_do_write_pack(png_transformp *transform, png_transform_controlp tc)
|
|
{
|
|
png_alloc_size_t rowbytes = PNG_TC_ROWBYTES(*tc);
|
|
png_const_bytep sp = png_voidcast(png_const_bytep, tc->sp);
|
|
png_const_bytep ep = png_upcast(png_const_bytep, tc->sp) + rowbytes;
|
|
png_bytep dp = png_voidcast(png_bytep, tc->dp);
|
|
|
|
png_debug(1, "in png_do_pack");
|
|
|
|
# define png_ptr tc->png_ptr
|
|
|
|
switch ((*transform)->args)
|
|
{
|
|
case 1:
|
|
{
|
|
unsigned int mask = 0x80, v = 0;
|
|
|
|
while (sp < ep)
|
|
{
|
|
if (*sp++ != 0)
|
|
v |= mask;
|
|
|
|
mask >>= 1;
|
|
|
|
if (mask == 0)
|
|
{
|
|
mask = 0x80;
|
|
*dp++ = PNG_BYTE(v);
|
|
v = 0;
|
|
}
|
|
}
|
|
|
|
if (mask != 0x80)
|
|
*dp++ = PNG_BYTE(v);
|
|
break;
|
|
}
|
|
|
|
case 2:
|
|
{
|
|
unsigned int shift = 8, v = 0;
|
|
|
|
while (sp < ep)
|
|
{
|
|
shift -= 2;
|
|
v |= (*sp++ & 0x3) << shift;
|
|
|
|
if (shift == 0)
|
|
{
|
|
shift = 8;
|
|
*dp++ = PNG_BYTE(v);
|
|
v = 0;
|
|
}
|
|
}
|
|
|
|
if (shift != 8)
|
|
*dp++ = PNG_BYTE(v);
|
|
break;
|
|
}
|
|
|
|
case 4:
|
|
{
|
|
unsigned int shift = 8, v = 0;
|
|
|
|
while (sp < ep)
|
|
{
|
|
shift -= 4;
|
|
v |= ((*sp++ & 0xf) << shift);
|
|
|
|
if (shift == 0)
|
|
{
|
|
shift = 8;
|
|
*dp++ = PNG_BYTE(v);
|
|
v = 0;
|
|
}
|
|
}
|
|
|
|
if (shift != 8)
|
|
*dp++ = PNG_BYTE(v);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
impossible("bit depth");
|
|
}
|
|
|
|
if ((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0 &&
|
|
--(tc->range) == 0)
|
|
tc->format &= PNG_BIC_MASK(PNG_FORMAT_FLAG_RANGE);
|
|
|
|
tc->bit_depth = (*transform)->args;
|
|
tc->sp = tc->dp;
|
|
# undef png_ptr
|
|
}
|
|
|
|
void /* PRIVATE */
|
|
png_init_write_pack(png_transformp *transform, png_transform_controlp tc)
|
|
{
|
|
# define png_ptr tc->png_ptr
|
|
debug(tc->init);
|
|
# undef png_ptr
|
|
|
|
/* The init routine is called *forward* so the transform control we get has
|
|
* the required bit depth and the transform routine will increase it to 8
|
|
* bits per channel. The code doesn't really care how many channels there
|
|
* are, but the only way to get a channel depth of less than 8 is to have
|
|
* just one channel.
|
|
*/
|
|
if (tc->bit_depth < 8) /* else no packing/unpacking */
|
|
{
|
|
if (tc->init == PNG_TC_INIT_FINAL)
|
|
{
|
|
(*transform)->fn = png_do_write_pack;
|
|
/* Record this for the backwards run: */
|
|
(*transform)->args = tc->bit_depth & 0xf;
|
|
}
|
|
|
|
if ((tc->format & PNG_FORMAT_FLAG_COLORMAP) == 0)
|
|
{
|
|
tc->range++;
|
|
tc->format |= PNG_FORMAT_FLAG_RANGE; /* forwards: backwards cancels */
|
|
}
|
|
|
|
tc->bit_depth = 8;
|
|
}
|
|
|
|
else /* the transform is not applicable */
|
|
(*transform)->fn = NULL;
|
|
}
|
|
#endif /* WRITE_PACK */
|