From a97e08abd27e4e8999a1c6d049e2ac3b532e6841 Mon Sep 17 00:00:00 2001 From: Eric Soroos Date: Thu, 31 Dec 2020 13:01:35 +0100 Subject: [PATCH] Implementation for PlanarConfiguration=2 Tiffs, manually merged from f566c8a --- src/libImaging/TiffDecode.c | 202 ++++++++++++++++++++++++------------ 1 file changed, 134 insertions(+), 68 deletions(-) diff --git a/src/libImaging/TiffDecode.c b/src/libImaging/TiffDecode.c index 5cbbe7380..8cacc8a4c 100644 --- a/src/libImaging/TiffDecode.c +++ b/src/libImaging/TiffDecode.c @@ -185,7 +185,7 @@ int _decodeStripYCbCr(Imaging im, ImagingCodecState state, TIFF *tiff) { // To avoid dealing with YCbCr subsampling, let libtiff handle it // Use a TIFFRGBAImage wrapping the tiff image, and let libtiff handle // all of the conversion. Metadata read from the TIFFRGBAImage could - // be different from the metadata that the base tiff returns. + // be different from the metadata that the base tiff returns. INT32 strip_row; UINT8 *new_data; @@ -221,9 +221,9 @@ int _decodeStripYCbCr(Imaging im, ImagingCodecState state, TIFF *tiff) { if (INT_MAX / 4 < img.width) { state->errcode = IMAGING_CODEC_MEMORY; goto decodeycbcr_err; - } - - // TiffRGBAImages are 32bits/pixel. + } + + // TiffRGBAImages are 32bits/pixel. row_byte_size = img.width * 4; /* overflow check for realloc */ @@ -247,7 +247,7 @@ int _decodeStripYCbCr(Imaging im, ImagingCodecState state, TIFF *tiff) { state->buffer = new_data; for (; state->y < state->ysize; state->y += rows_per_strip) { - img.row_offset = state->y; + img.row_offset = state->y; rows_to_read = min(rows_per_strip, img.height - state->y); if (TIFFRGBAImageGet(&img, (UINT32 *)state->buffer, img.width, rows_to_read) == -1) { @@ -280,8 +280,8 @@ int _decodeStripYCbCr(Imaging im, ImagingCodecState state, TIFF *tiff) { return 0; } -int _decodeStrip(Imaging im, ImagingCodecState state, TIFF *tiff) { - INT32 strip_row; +int _decodeStrip(Imaging im, ImagingCodecState state, TIFF *tiff, UINT8 planes, ImagingShuffler *unpackers) { + INT32 strip_row = 0; UINT8 *new_data; UINT32 rows_per_strip, row_byte_size; int ret; @@ -293,7 +293,7 @@ int _decodeStrip(Imaging im, ImagingCodecState state, TIFF *tiff) { TRACE(("RowsPerStrip: %u \n", rows_per_strip)); // We could use TIFFStripSize, but for YCbCr data it returns subsampled data size - row_byte_size = (state->xsize * state->bits + 7) / 8; + row_byte_size = (state->xsize * state->bits / planes + 7) / 8; /* overflow check for realloc */ if (INT_MAX / row_byte_size < rows_per_strip) { @@ -325,27 +325,32 @@ int _decodeStrip(Imaging im, ImagingCodecState state, TIFF *tiff) { state->buffer = new_data; for (; state->y < state->ysize; state->y += rows_per_strip) { - if (TIFFReadEncodedStrip(tiff, TIFFComputeStrip(tiff, state->y, 0), (tdata_t)state->buffer, -1) == -1) { - TRACE(("Decode Error, strip %d\n", TIFFComputeStrip(tiff, state->y, 0))); - state->errcode = IMAGING_CODEC_BROKEN; - return -1; - } + UINT8 plane; + for (plane = 0; plane < planes; plane++) { + ImagingShuffler shuffler = unpackers[plane]; + if (TIFFReadEncodedStrip(tiff, TIFFComputeStrip(tiff, state->y, plane), (tdata_t)state->buffer, -1) == -1) { + TRACE(("Decode Error, strip %d\n", TIFFComputeStrip(tiff, state->y, 0))); + state->errcode = IMAGING_CODEC_BROKEN; + return -1; + } - TRACE(("Decoded strip for row %d \n", state->y)); + TRACE(("Decoded strip for row %d \n", state->y)); - // iterate over each row in the strip and stuff data into image - for (strip_row = 0; strip_row < min((INT32) rows_per_strip, state->ysize - state->y); strip_row++) { - TRACE(("Writing data into line %d ; \n", state->y + strip_row)); + // iterate over each row in the strip and stuff data into image + for (strip_row = 0; strip_row < min((INT32) rows_per_strip, state->ysize - state->y); strip_row++) { + TRACE(("Writing data into line %d ; \n", state->y + strip_row)); - // UINT8 * bbb = state->buffer + strip_row * (state->bytes / rows_per_strip); - // TRACE(("chars: %x %x %x %x\n", ((UINT8 *)bbb)[0], ((UINT8 *)bbb)[1], ((UINT8 *)bbb)[2], ((UINT8 *)bbb)[3])); + // UINT8 * bbb = state->buffer + strip_row * (state->bytes / rows_per_strip); + // TRACE(("chars: %x %x %x %x\n", ((UINT8 *)bbb)[0], ((UINT8 *)bbb)[1], ((UINT8 *)bbb)[2], ((UINT8 *)bbb)[3])); - state->shuffle((UINT8*) im->image[state->y + state->yoff + strip_row] + - state->xoff * im->pixelsize, - state->buffer + strip_row * row_byte_size, - state->xsize); + shuffler((UINT8*) im->image[state->y + state->yoff + strip_row] + + state->xoff * im->pixelsize, + state->buffer + strip_row * row_byte_size, + state->xsize); + } } } + return 0; } @@ -356,6 +361,9 @@ int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, UINT8* buffer, Py_ TIFF *tiff; uint16 photometric = 0; // init to not PHOTOMETRIC_YCBCR int isYCbCr = 0; + UINT8 planarconfig = 0; + UINT8 planes = 1; + ImagingShuffler unpackers[4]; /* buffer is the encoded file, bytes is the length of the encoded file */ /* it all ends up in state->buffer, which is a uint8* from Imaging.h */ @@ -416,10 +424,39 @@ int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, UINT8* buffer, Py_ } } - + TIFFGetField(tiff, TIFFTAG_PHOTOMETRIC, &photometric); isYCbCr = photometric == PHOTOMETRIC_YCBCR; - + TIFFGetFieldDefaulted(tiff, TIFFTAG_PLANARCONFIG, &planarconfig); + + // YCbCr data is read as RGB by libtiff and we don't need to worry about planar storage in that case + // if number of bands is 1, there is no difference with contig case + if (planarconfig == PLANARCONFIG_SEPARATE && + im->bands > 1 && + photometric != PHOTOMETRIC_YCBCR) { + + uint16 bits_per_sample = 8; + + TIFFGetFieldDefaulted(tiff, TIFFTAG_BITSPERSAMPLE, &bits_per_sample); + if (bits_per_sample != 8 && bits_per_sample != 16) { + TRACE(("Invalid value for bits per sample: %d\n", bits_per_sample)); + state->errcode = IMAGING_CODEC_BROKEN; + goto decode_err; + } + + planes = im->bands; + + // We'll pick appropriate set of unpackers depending on planar_configuration + // It does not matter if data is RGB(A), CMYK or LUV really, + // we just copy it plane by plane + unpackers[0] = ImagingFindUnpacker("RGBA", bits_per_sample == 16 ? "R;16N" : "R", NULL); + unpackers[1] = ImagingFindUnpacker("RGBA", bits_per_sample == 16 ? "G;16N" : "G", NULL); + unpackers[2] = ImagingFindUnpacker("RGBA", bits_per_sample == 16 ? "B;16N" : "B", NULL); + unpackers[3] = ImagingFindUnpacker("RGBA", bits_per_sample == 16 ? "A;16N" : "A", NULL); + } else { + unpackers[0] = state->shuffle; + } + if (TIFFIsTiled(tiff)) { INT32 x, y, tile_y; UINT32 tile_width, tile_length, current_tile_length, current_line, current_tile_width, row_byte_size; @@ -444,7 +481,7 @@ int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, UINT8* buffer, Py_ } } else { // We could use TIFFTileSize, but for YCbCr data it returns subsampled data size - row_byte_size = (tile_width * state->bits + 7) / 8; + row_byte_size = (tile_width * state->bits / planes + 7) / 8; } /* overflow check for realloc */ @@ -456,7 +493,7 @@ int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, UINT8* buffer, Py_ state->bytes = row_byte_size * tile_length; if (TIFFTileSize(tiff) > state->bytes) { - // If the strip size as expected by LibTiff isn't what we're expecting, abort. + // If the tile size as expected by LibTiff isn't what we're expecting, abort. state->errcode = IMAGING_CODEC_MEMORY; goto decode_err; } @@ -474,61 +511,90 @@ int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, UINT8* buffer, Py_ TRACE(("TIFFTileSize: %d\n", state->bytes)); for (y = state->yoff; y < state->ysize; y += tile_length) { - for (x = state->xoff; x < state->xsize; x += tile_width) { - if (isYCbCr) { - /* To avoid dealing with YCbCr subsampling, let libtiff handle it */ - if (!TIFFReadRGBATile(tiff, x, y, (UINT32 *)state->buffer)) { - TRACE(("Decode Error, Tile at %dx%d\n", x, y)); - state->errcode = IMAGING_CODEC_BROKEN; - goto decode_err; - } - } else { - if (TIFFReadTile(tiff, (tdata_t)state->buffer, x, y, 0, 0) == -1) { - TRACE(("Decode Error, Tile at %dx%d\n", x, y)); - state->errcode = IMAGING_CODEC_BROKEN; - goto decode_err; - } - } - - TRACE(("Read tile at %dx%d; \n\n", x, y)); - - current_tile_width = min((INT32) tile_width, state->xsize - x); - current_tile_length = min((INT32) tile_length, state->ysize - y); - // iterate over each line in the tile and stuff data into image - for (tile_y = 0; tile_y < current_tile_length; tile_y++) { - TRACE(("Writing tile data at %dx%d using tile_width: %d; \n", tile_y + y, x, current_tile_width)); - - // UINT8 * bbb = state->buffer + tile_y * row_byte_size; - // TRACE(("chars: %x%x%x%x\n", ((UINT8 *)bbb)[0], ((UINT8 *)bbb)[1], ((UINT8 *)bbb)[2], ((UINT8 *)bbb)[3])); - /* - * For some reason the TIFFReadRGBATile() function - * chooses the lower left corner as the origin. - * Vertically mirror by shuffling the scanlines - * backwards - */ - + UINT8 plane; + for (plane = 0; plane < planes; plane++) { + ImagingShuffler shuffler = unpackers[plane]; + for (x = state->xoff; x < state->xsize; x += tile_width) { if (isYCbCr) { - current_line = tile_length - tile_y - 1; + /* To avoid dealing with YCbCr subsampling, let libtiff handle it */ + if (!TIFFReadRGBATile(tiff, x, y, (UINT32 *)state->buffer)) { + TRACE(("Decode Error, Tile at %dx%d\n", x, y)); + state->errcode = IMAGING_CODEC_BROKEN; + goto decode_err; + } } else { - current_line = tile_y; + if (TIFFReadTile(tiff, (tdata_t)state->buffer, x, y, 0, plane) == -1) { + TRACE(("Decode Error, Tile at %dx%d\n", x, y)); + state->errcode = IMAGING_CODEC_BROKEN; + goto decode_err; + } + } + + TRACE(("Read tile at %dx%d; \n\n", x, y)); + + current_tile_width = min((INT32) tile_width, state->xsize - x); + current_tile_length = min((INT32) tile_length, state->ysize - y); + // iterate over each line in the tile and stuff data into image + for (tile_y = 0; tile_y < current_tile_length; tile_y++) { + TRACE(("Writing tile data at %dx%d using tile_width: %d; \n", tile_y + y, x, current_tile_width)); + + // UINT8 * bbb = state->buffer + tile_y * row_byte_size; + // TRACE(("chars: %x%x%x%x\n", ((UINT8 *)bbb)[0], ((UINT8 *)bbb)[1], ((UINT8 *)bbb)[2], ((UINT8 *)bbb)[3])); + /* + * For some reason the TIFFReadRGBATile() function + * chooses the lower left corner as the origin. + * Vertically mirror by shuffling the scanlines + * backwards + */ + + if (isYCbCr) { + current_line = tile_length - tile_y - 1; + } else { + current_line = tile_y; + } + + shuffler((UINT8*) im->image[tile_y + y] + x * im->pixelsize, + state->buffer + current_line * row_byte_size, + current_tile_width + ); } - - state->shuffle((UINT8*) im->image[tile_y + y] + x * im->pixelsize, - state->buffer + current_line * row_byte_size, - current_tile_width - ); } } } } else { if (!isYCbCr) { - _decodeStrip(im, state, tiff); + _decodeStrip(im, state, tiff, planes, unpackers); } else { _decodeStripYCbCr(im, state, tiff); } } + if (!state->errcode) { + // Check if raw mode was RGBa and it was stored on separate planes + // so we have to convert it to RGBA + if (planes > 3 && strcmp(im->mode, "RGBA") == 0) { + uint16 extrasamples; + uint16* sampleinfo; + ImagingShuffler shuffle; + INT32 y; + + TIFFGetFieldDefaulted(tiff, TIFFTAG_EXTRASAMPLES, &extrasamples, &sampleinfo); + + if (extrasamples >= 1 && + (sampleinfo[0] == EXTRASAMPLE_UNSPECIFIED || sampleinfo[0] == EXTRASAMPLE_ASSOCALPHA) + ) { + shuffle = ImagingFindUnpacker("RGBA", "RGBa", NULL); + + for (y = state->yoff; y < state->ysize; y++) { + UINT8* ptr = (UINT8*) im->image[y + state->yoff] + + state->xoff * im->pixelsize; + shuffle(ptr, ptr, state->xsize); + } + } + } + } + decode_err: TIFFClose(tiff); TRACE(("Done Decoding, Returning \n"));