diff --git a/src/PIL/TiffImagePlugin.py b/src/PIL/TiffImagePlugin.py index ecfe8d890..d12f411fe 100644 --- a/src/PIL/TiffImagePlugin.py +++ b/src/PIL/TiffImagePlugin.py @@ -257,14 +257,14 @@ OPEN_INFO = { (II, 5, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0)): ("CMYK", "CMYKXX"), (MM, 5, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0)): ("CMYK", "CMYKXX"), - (II, 6, (1,), 1, (8, 8, 8), ()): ("YCbCr", "YCbCr"), - (MM, 6, (1,), 1, (8, 8, 8), ()): ("YCbCr", "YCbCr"), - (II, 6, (1,), 1, (8, 8, 8, 8), (0,)): ("YCbCr", "YCbCrX"), - (MM, 6, (1,), 1, (8, 8, 8, 8), (0,)): ("YCbCr", "YCbCrX"), - (II, 6, (1,), 1, (8, 8, 8, 8, 8), (0, 0)): ("YCbCr", "YCbCrXX"), - (MM, 6, (1,), 1, (8, 8, 8, 8, 8), (0, 0)): ("YCbCr", "YCbCrXX"), - (II, 6, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0, 0)): ("YCbCr", "YCbCrXXX"), - (MM, 6, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0, 0)): ("YCbCr", "YCbCrXXX"), + (II, 6, (1,), 1, (8, 8, 8), ()): ("RGB", "RGB"), + (MM, 6, (1,), 1, (8, 8, 8), ()): ("RGB", "RGB"), + (II, 6, (1,), 1, (8, 8, 8, 8), (0,)): ("RGB", "RGBX"), + (MM, 6, (1,), 1, (8, 8, 8, 8), (0,)): ("RGB", "RGBX"), + (II, 6, (1,), 1, (8, 8, 8, 8, 8), (0, 0)): ("RGB", "RGBXX"), + (MM, 6, (1,), 1, (8, 8, 8, 8, 8), (0, 0)): ("RGB", "RGBXX"), + (II, 6, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0, 0)): ("RGB", "RGBXXX"), + (MM, 6, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0, 0)): ("RGB", "RGBXXX"), (II, 8, (1,), 1, (8, 8, 8), ()): ("LAB", "LAB"), (MM, 8, (1,), 1, (8, 8, 8), ()): ("LAB", "LAB"), @@ -1062,6 +1062,8 @@ class TiffImageFile(ImageFile.ImageFile): compression = self._compression if compression == "raw": args = (rawmode, 0, 1) + if compression == "jpeg": + args = ("RGB", "") elif compression == "packbits": args = rawmode @@ -1187,6 +1189,7 @@ class TiffImageFile(ImageFile.ImageFile): print("- photometric_interpretation:", photo) print("- planar_configuration:", self._planar_configuration) print("- fill_order:", fillorder) + print("- YCbCr subsampling:", self.tag.get(530)) # size xsize = self.tag_v2.get(IMAGEWIDTH) @@ -1258,87 +1261,71 @@ class TiffImageFile(ImageFile.ImageFile): # build tile descriptors x = y = layer = 0 self.tile = [] - self.use_load_libtiff = False - if STRIPOFFSETS in self.tag_v2: + self.use_load_libtiff = self._compression != 'raw' + if self.use_load_libtiff: + # Decoder expects entire file as one tile. + # There's a buffer size limit in load (64k) + # so large g4 images will fail if we use that + # function. + # + # Setup the one tile for the whole image, then + # use the _load_libtiff function. + + # libtiff handles the fillmode for us, so 1;IR should + # actually be 1;I. Including the R double reverses the + # bits, so stripes of the image are reversed. See + # https://github.com/python-pillow/Pillow/issues/279 + if fillorder == 2: + key = ( + self.tag_v2.prefix, photo, sampleFormat, 1, + self.tag_v2.get(BITSPERSAMPLE, (1,)), + self.tag_v2.get(EXTRASAMPLES, ()) + ) + if DEBUG: + print("format key:", key) + # this should always work, since all the + # fillorder==2 modes have a corresponding + # fillorder=1 mode + self.mode, rawmode = OPEN_INFO[key] + # libtiff always returns the bytes in native order. + # we're expecting image byte order. So, if the rawmode + # contains I;16, we need to convert from native to image + # byte order. + if rawmode == 'I;16': + rawmode = 'I;16N' + if ';16B' in rawmode: + rawmode = rawmode.replace(';16B', ';16N') + if ';16L' in rawmode: + rawmode = rawmode.replace(';16L', ';16N') + + # Offset in the tile tuple is 0, we go from 0,0 to + # w,h, and we only do this once -- eds + a = (rawmode, self._compression, False) + self.tile.append( + (self._compression, + (0, 0, xsize, ysize), + 0, a)) + + elif STRIPOFFSETS in self.tag_v2 or TILEOFFSETS in self.tag_v2: # striped image - offsets = self.tag_v2[STRIPOFFSETS] - h = self.tag_v2.get(ROWSPERSTRIP, ysize) - w = self.size[0] - if READ_LIBTIFF or self._compression != 'raw': - # Decoder expects entire file as one tile. - # There's a buffer size limit in load (64k) - # so large g4 images will fail if we use that - # function. - # - # Setup the one tile for the whole image, then - # use the _load_libtiff function. - - self.use_load_libtiff = True - - # libtiff handles the fillmode for us, so 1;IR should - # actually be 1;I. Including the R double reverses the - # bits, so stripes of the image are reversed. See - # https://github.com/python-pillow/Pillow/issues/279 - if fillorder == 2: - key = ( - self.tag_v2.prefix, photo, sampleFormat, 1, - self.tag_v2.get(BITSPERSAMPLE, (1,)), - self.tag_v2.get(EXTRASAMPLES, ()) - ) - if DEBUG: - print("format key:", key) - # this should always work, since all the - # fillorder==2 modes have a corresponding - # fillorder=1 mode - self.mode, rawmode = OPEN_INFO[key] - # libtiff always returns the bytes in native order. - # we're expecting image byte order. So, if the rawmode - # contains I;16, we need to convert from native to image - # byte order. - if rawmode == 'I;16': - rawmode = 'I;16N' - if ';16B' in rawmode: - rawmode = rawmode.replace(';16B', ';16N') - if ';16L' in rawmode: - rawmode = rawmode.replace(';16L', ';16N') - - # Offset in the tile tuple is 0, we go from 0,0 to - # w,h, and we only do this once -- eds - a = (rawmode, self._compression, False) - self.tile.append( - (self._compression, - (0, 0, w, ysize), - 0, a)) - a = None - + if STRIPOFFSETS in self.tag_v2: + offsets = self.tag_v2[STRIPOFFSETS] + h = self.tag_v2.get(ROWSPERSTRIP, ysize) + w = self.size[0] else: - for i, offset in enumerate(offsets): - a = self._decoder(rawmode, layer, i) - self.tile.append( - (self._compression, - (0, min(y, ysize), w, min(y+h, ysize)), - offset, a)) - if DEBUG: - print("tiles: ", self.tile) - y = y + h - if y >= self.size[1]: - x = y = 0 - layer += 1 - a = None - elif TILEOFFSETS in self.tag_v2: - # tiled image - w = self.tag_v2.get(322) - h = self.tag_v2.get(323) + # tiled image + offsets = self.tag_v2[TILEOFFSETS] + w = self.tag_v2.get(322) + h = self.tag_v2.get(323) + a = None - for o in self.tag_v2[TILEOFFSETS]: + for offset in offsets: if not a: a = self._decoder(rawmode, layer) - # FIXME: this doesn't work if the image size - # is not a multiple of the tile size... self.tile.append( (self._compression, - (x, y, x+w, y+h), - o, a)) + (min(x, xsize), min(y, ysize), min(x+w, xsize), min(y+h, ysize)), + offset, a)) x = x + w if x >= self.size[0]: x, y = 0, y + h @@ -1346,6 +1333,7 @@ class TiffImageFile(ImageFile.ImageFile): x = y = 0 layer += 1 a = None + self.tile_prefix = self.tag_v2.get(JPEGTABLES, b"") else: if DEBUG: print("- unsupported data organization") diff --git a/src/libImaging/TiffDecode.c b/src/libImaging/TiffDecode.c index f292da388..96106b6e3 100644 --- a/src/libImaging/TiffDecode.c +++ b/src/libImaging/TiffDecode.c @@ -173,7 +173,6 @@ int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int char *filename = "tempfile.tif"; char *mode = "r"; TIFF *tiff; - tsize_t size; /* buffer is the encoded file, bytes is the length of the encoded file */ @@ -235,36 +234,96 @@ int ImagingLibTiffDecode(Imaging im, ImagingCodecState state, UINT8* buffer, int } } - size = TIFFScanlineSize(tiff); - TRACE(("ScanlineSize: %d \n", size)); - if (size > state->bytes) { - TRACE(("Error, scanline size > buffer size\n")); - state->errcode = IMAGING_CODEC_BROKEN; - TIFFClose(tiff); - return -1; - } + TIFFSetField(tiff, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB); - // Have to do this row by row and shove stuff into the buffer that way, - // with shuffle. (or, just alloc a buffer myself, then figure out how to get it - // back in. Can't use read encoded stripe. + if (TIFFIsTiled(tiff)) { + state->bytes = TIFFTileSize(tiff); - // This thing pretty much requires that I have the whole image in one shot. - // Perhaps a stub version would work better??? - while(state->y < state->ysize){ - if (TIFFReadScanline(tiff, (tdata_t)state->buffer, (uint32)state->y, 0) == -1) { - TRACE(("Decode Error, row %d\n", state->y)); - state->errcode = IMAGING_CODEC_BROKEN; - TIFFClose(tiff); - return -1; - } - /* TRACE(("Decoded row %d \n", state->y)); */ - state->shuffle((UINT8*) im->image[state->y + state->yoff] + - state->xoff * im->pixelsize, - state->buffer, - state->xsize); + /* overflow check for malloc */ + if (state->bytes > INT_MAX - 1) { + state->errcode = IMAGING_CODEC_MEMORY; + TIFFClose(tiff); + return -1; + } - state->y++; - } + /* realloc to fit whole tile */ + UINT8 *new = realloc (state->buffer, state->bytes); + if (!new) { + state->errcode = IMAGING_CODEC_MEMORY; + TIFFClose(tiff); + return -1; + } + + state->buffer = new; + + TRACE(("TIFFTileSize: %d\n", state->bytes)); + + uint32 x, y, tile_y; + uint32 tileWidth, tileLength; + + TIFFGetField(tiff, TIFFTAG_TILEWIDTH, &tileWidth); + TIFFGetField(tiff, TIFFTAG_TILELENGTH, &tileLength); + + for (y = state->yoff; y < state->ysize; y += tileLength) { + for (x = state->xoff; x < state->xsize; x += tileWidth) { + 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; + TIFFClose(tiff); + return -1; + } + + TRACE(("Read tile at %dx%d; \n\n", x, y)); + + // iterate over each line in the tile and stuff data into image + for (tile_y = 0; tile_y < min(tileLength, state->ysize - y); tile_y++) { + + TRACE(("Writing tile data at %dx%d using tilwWidth: %d; \n", tile_y + y, x, min(tileWidth, state->xsize - x))); + + // UINT8 * bbb = state->buffer + tile_y * (state->bytes / tileLength); + // TRACE(("chars: %x%x%x%x\n", ((UINT8 *)bbb)[0], ((UINT8 *)bbb)[1], ((UINT8 *)bbb)[2], ((UINT8 *)bbb)[3])); + + state->shuffle((UINT8*) im->image[tile_y + y] + x * im->pixelsize, + state->buffer + tile_y * (state->bytes / tileLength), + min(tileWidth, state->xsize - x) + ); + } + } + } + } else { + tsize_t size; + + size = TIFFScanlineSize(tiff); + TRACE(("ScanlineSize: %lu \n", size)); + if (size > state->bytes) { + TRACE(("Error, scanline size > buffer size\n")); + state->errcode = IMAGING_CODEC_BROKEN; + TIFFClose(tiff); + return -1; + } + + // Have to do this row by row and shove stuff into the buffer that way, + // with shuffle. (or, just alloc a buffer myself, then figure out how to get it + // back in. Can't use read encoded stripe. + + // This thing pretty much requires that I have the whole image in one shot. + // Perhaps a stub version would work better??? + while(state->y < state->ysize){ + if (TIFFReadScanline(tiff, (tdata_t)state->buffer, (uint32)state->y, 0) == -1) { + TRACE(("Decode Error, row %d\n", state->y)); + state->errcode = IMAGING_CODEC_BROKEN; + TIFFClose(tiff); + return -1; + } + /* TRACE(("Decoded row %d \n", state->y)); */ + state->shuffle((UINT8*) im->image[state->y + state->yoff] + + state->xoff * im->pixelsize, + state->buffer, + state->xsize); + + state->y++; + } + } TIFFClose(tiff); TRACE(("Done Decoding, Returning \n"));