/* * 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 */ /* Expand standard buffer to make room for the (optional) filter prefix, and allocate a buffer to hold the previous line */ free(state->buffer); 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 corectly */ 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