mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-11-10 19:56:47 +03:00
278 lines
9.2 KiB
C
278 lines
9.2 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 "Zip.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, int 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;
|
|
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);
|
|
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);
|
|
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);
|
|
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 */
|
|
|
|
}
|
|
|
|
#endif
|