Pillow/src/libImaging/ZipDecode.c
2021-01-03 14:17:51 +11:00

300 lines
10 KiB
C

/*
* The Python Imaging Library.
* $Id$
*
* decoder for ZIP (deflated) image data.
*
* history:
* 1996-12-14 fl Created (for PNG)
* 1997-01-15 fl Prepared to read TIFF/ZIP
* 2001-11-19 fl PNG incomplete read patch (from Bernhard Herzog)
*
* Copyright (c) Fredrik Lundh 1996.
* Copyright (c) Secret Labs AB 1997-2001.
*
* See the README file for information on usage and redistribution.
*/
#include "Imaging.h"
#ifdef HAVE_LIBZ
#include "ZipCodecs.h"
static const int OFFSET[] = {7, 3, 3, 1, 1, 0, 0};
static const int STARTING_COL[] = {0, 4, 0, 2, 0, 1, 0};
static const int STARTING_ROW[] = {0, 0, 4, 0, 2, 0, 1};
static const int COL_INCREMENT[] = {8, 8, 4, 4, 2, 2, 1};
static const int ROW_INCREMENT[] = {8, 8, 8, 4, 4, 2, 2};
/* Get the length in bytes of a scanline in the pass specified,
* for interlaced images */
static int
get_row_len(ImagingCodecState state, int pass) {
int row_len = (state->xsize + OFFSET[pass]) / COL_INCREMENT[pass];
return ((row_len * state->bits) + 7) / 8;
}
/* -------------------------------------------------------------------- */
/* Decoder */
/* -------------------------------------------------------------------- */
int
ImagingZipDecode(Imaging im, ImagingCodecState state, UINT8 *buf, Py_ssize_t bytes) {
ZIPSTATE *context = (ZIPSTATE *)state->context;
int err;
int n;
UINT8 *ptr;
int i, bpp;
int row_len;
if (!state->state) {
/* Initialization */
if (context->mode == ZIP_PNG || context->mode == ZIP_PNG_PALETTE) {
context->prefix = 1; /* PNG */
}
/* overflow check for malloc */
if (state->bytes > INT_MAX - 1) {
state->errcode = IMAGING_CODEC_MEMORY;
return -1;
}
/* Expand standard buffer to make room for the (optional) filter
prefix, and allocate a buffer to hold the previous line */
free(state->buffer);
/* malloc check ok, overflow checked above */
state->buffer = (UINT8 *)malloc(state->bytes + 1);
context->previous = (UINT8 *)malloc(state->bytes + 1);
if (!state->buffer || !context->previous) {
state->errcode = IMAGING_CODEC_MEMORY;
return -1;
}
context->last_output = 0;
/* Initialize to black */
memset(context->previous, 0, state->bytes + 1);
/* Setup decompression context */
context->z_stream.zalloc = (alloc_func)NULL;
context->z_stream.zfree = (free_func)NULL;
context->z_stream.opaque = (voidpf)NULL;
err = inflateInit(&context->z_stream);
if (err < 0) {
state->errcode = IMAGING_CODEC_CONFIG;
free(context->previous);
context->previous = NULL;
return -1;
}
if (context->interlaced) {
context->pass = 0;
state->y = STARTING_ROW[context->pass];
}
/* Ready to decode */
state->state = 1;
}
if (context->interlaced) {
row_len = get_row_len(state, context->pass);
} else {
row_len = state->bytes;
}
/* Setup the source buffer */
context->z_stream.next_in = buf;
context->z_stream.avail_in = bytes;
/* Decompress what we've got this far */
while (context->z_stream.avail_in > 0) {
context->z_stream.next_out = state->buffer + context->last_output;
context->z_stream.avail_out = row_len + context->prefix - context->last_output;
err = inflate(&context->z_stream, Z_NO_FLUSH);
if (err < 0) {
/* Something went wrong inside the compression library */
if (err == Z_DATA_ERROR) {
state->errcode = IMAGING_CODEC_BROKEN;
} else if (err == Z_MEM_ERROR) {
state->errcode = IMAGING_CODEC_MEMORY;
} else {
state->errcode = IMAGING_CODEC_CONFIG;
}
free(context->previous);
context->previous = NULL;
inflateEnd(&context->z_stream);
return -1;
}
n = row_len + context->prefix - context->z_stream.avail_out;
if (n < row_len + context->prefix) {
context->last_output = n;
break; /* need more input data */
}
/* Apply predictor */
switch (context->mode) {
case ZIP_PNG:
switch (state->buffer[0]) {
case 0:
break;
case 1:
/* prior */
bpp = (state->bits + 7) / 8;
for (i = bpp + 1; i <= row_len; i++) {
state->buffer[i] += state->buffer[i - bpp];
}
break;
case 2:
/* up */
for (i = 1; i <= row_len; i++) {
state->buffer[i] += context->previous[i];
}
break;
case 3:
/* average */
bpp = (state->bits + 7) / 8;
for (i = 1; i <= bpp; i++) {
state->buffer[i] += context->previous[i] / 2;
}
for (; i <= row_len; i++) {
state->buffer[i] +=
(state->buffer[i - bpp] + context->previous[i]) / 2;
}
break;
case 4:
/* paeth filtering */
bpp = (state->bits + 7) / 8;
for (i = 1; i <= bpp; i++) {
state->buffer[i] += context->previous[i];
}
for (; i <= row_len; i++) {
int a, b, c;
int pa, pb, pc;
/* fetch pixels */
a = state->buffer[i - bpp];
b = context->previous[i];
c = context->previous[i - bpp];
/* distances to surrounding pixels */
pa = abs(b - c);
pb = abs(a - c);
pc = abs(a + b - 2 * c);
/* pick predictor with the shortest distance */
state->buffer[i] += (pa <= pb && pa <= pc) ? a
: (pb <= pc) ? b
: c;
}
break;
default:
state->errcode = IMAGING_CODEC_UNKNOWN;
free(context->previous);
context->previous = NULL;
inflateEnd(&context->z_stream);
return -1;
}
break;
case ZIP_TIFF_PREDICTOR:
bpp = (state->bits + 7) / 8;
for (i = bpp + 1; i <= row_len; i++) {
state->buffer[i] += state->buffer[i - bpp];
}
break;
}
/* Stuff data into the image */
if (context->interlaced) {
int col = STARTING_COL[context->pass];
if (state->bits >= 8) {
/* Stuff pixels in their correct location, one by one */
for (i = 0; i < row_len; i += ((state->bits + 7) / 8)) {
state->shuffle(
(UINT8 *)im->image[state->y] + col * im->pixelsize,
state->buffer + context->prefix + i,
1);
col += COL_INCREMENT[context->pass];
}
} else {
/* Handle case with more than a pixel in each byte */
int row_bits = ((state->xsize + OFFSET[context->pass]) /
COL_INCREMENT[context->pass]) *
state->bits;
for (i = 0; i < row_bits; i += state->bits) {
UINT8 byte = *(state->buffer + context->prefix + (i / 8));
byte <<= (i % 8);
state->shuffle(
(UINT8 *)im->image[state->y] + col * im->pixelsize, &byte, 1);
col += COL_INCREMENT[context->pass];
}
}
/* Find next valid scanline */
state->y += ROW_INCREMENT[context->pass];
while (state->y >= state->ysize || row_len <= 0) {
context->pass++;
if (context->pass == 7) {
/* Force exit below */
state->y = state->ysize;
break;
}
state->y = STARTING_ROW[context->pass];
row_len = get_row_len(state, context->pass);
/* Since we're moving to the "first" line, the previous line
* should be black to make filters work correctly */
memset(state->buffer, 0, state->bytes + 1);
}
} else {
state->shuffle(
(UINT8 *)im->image[state->y + state->yoff] +
state->xoff * im->pixelsize,
state->buffer + context->prefix,
state->xsize);
state->y++;
}
/* all inflate output has been consumed */
context->last_output = 0;
if (state->y >= state->ysize || err == Z_STREAM_END) {
/* The image and the data should end simultaneously */
/* if (state->y < state->ysize || err != Z_STREAM_END)
state->errcode = IMAGING_CODEC_BROKEN; */
free(context->previous);
context->previous = NULL;
inflateEnd(&context->z_stream);
return -1; /* end of file (errcode=0) */
}
/* Swap buffer pointers */
ptr = state->buffer;
state->buffer = context->previous;
context->previous = ptr;
}
return bytes; /* consumed all of it */
}
int
ImagingZipDecodeCleanup(ImagingCodecState state) {
/* called to free the decompression engine when the decode terminates
due to a corrupt or truncated image
*/
ZIPSTATE *context = (ZIPSTATE *)state->context;
/* Clean up */
if (context->previous) {
inflateEnd(&context->z_stream);
free(context->previous);
context->previous = NULL;
}
return -1;
}
#endif