# HG changeset patch # User magedrifaat # Date 1658073578 -7200 # Node ID f8be3654caef06e6b45538e5c2c6c71449b4d75b # Parent 530dbd1d6b07346465c865eb3adc9035adcd585a Tiff read: Support for tiled images with normal planar configuration * __tiff__.cc(read_tiled_image): Implemented support for images organized in tiles, only normal planar configuration is currently supported. diff -r 530dbd1d6b07 -r f8be3654caef libinterp/dldfcn/__tiff__.cc --- a/libinterp/dldfcn/__tiff__.cc Sat Jul 16 23:40:15 2022 +0200 +++ b/libinterp/dldfcn/__tiff__.cc Sun Jul 17 17:59:38 2022 +0200 @@ -33,6 +33,18 @@ uint16_t is_tiled; }; + // Error if status is not 1 (success status for TIFFGetField) + void + validate_tiff_get_field (bool status, void *p_to_free=NULL) + { + if (status != 1) + { + if (p_to_free != NULL) + _TIFFfree (p_to_free); + error ("Failed to read tag"); + } + } + template octave_value read_stripped_image (TIFF *tif, tiff_image_data *image_data) @@ -63,7 +75,7 @@ // Can't rely on StripByteCounts because in compressed images // the byte count reflect the actual number of bytes stored // in the file not the size of the uncompressed strip - uint32_t strip_bytes = TIFFReadEncodedStrip (tif, strip, buf, -1); + int64_t strip_bytes = TIFFReadEncodedStrip (tif, strip, buf, -1); if (strip_bytes == -1) error ("Failed to read strip data"); @@ -72,76 +84,124 @@ for (uint32_t row_subindex = 0; row_subindex < rows_in_strip; row_subindex++) - { - for (uint32_t column = 0; - column < image_data->width; - column++) - { - uint32_t row_offset = row_size_in_bits / 8 - * row_subindex; - // TODO(maged): support arbitrary BitsPerSample? - if (image_data->bits_per_sample >= 8 - && image_data->bits_per_sample % 8 == 0) - { - // TODO(maged): clean up the math for both cases - if (image_data->planar_configuration == PLANARCONFIG_CONTIG) - for (uint16_t sample = 0; - sample < image_data->samples_per_pixel; sample++) - // The memory organization of fvec is inverted from - // what would be expected for a normal C-like array. - // It is treated as samples * columns * rows as - // opposed to rows * columns * samples. - img_fvec[sample * plane_size + for (uint32_t column = 0; + column < image_data->width; + column++) + { + uint32_t row_offset = row_size_in_bits / 8 + * row_subindex; + // TODO(maged): support arbitrary BitsPerSample? + if (image_data->bits_per_sample >= 8 + && image_data->bits_per_sample % 8 == 0) + { + // TODO(maged): clean up the math for both cases + if (image_data->planar_configuration == PLANARCONFIG_CONTIG) + for (uint16_t sample = 0; + sample < image_data->samples_per_pixel; sample++) + // The memory organization of fvec is inverted from + // what would be expected for a normal C-like array. + // It is treated as samples * columns * rows as + // opposed to rows * columns * samples. + img_fvec[sample * plane_size + + column * image_data->height + + row_index + row_subindex] + = ((P *)buf)[row_subindex * image_data->width + * image_data->samples_per_pixel + + column * image_data->samples_per_pixel + + sample]; + else + { + uint16_t channel_no = strip + * image_data->samples_per_pixel + / strip_count; + uint16_t corrected_row = (row_index + row_subindex) + % image_data->height; + img_fvec[channel_no * plane_size + column * image_data->height - + row_index + row_subindex] - = ((P *)buf)[row_subindex * image_data->width - * image_data->samples_per_pixel - + column * image_data->samples_per_pixel - + sample]; - else - { - uint16_t channel_no = strip - * image_data->samples_per_pixel - / strip_count; - uint16_t corrected_row = (row_index + row_subindex) - % image_data->height; - img_fvec[channel_no * plane_size - + column * image_data->height - + corrected_row] - = ((P *)buf)[row_subindex * image_data->width - + column]; - } - } - else if (image_data->bits_per_sample == 4 - && image_data->samples_per_pixel == 1) - { - // TODO(maged): Check FillOrder for completeness - uint8_t nibble - = ((uint8_t *)buf)[row_offset + column / 2]; - // Extract the needed nibble from the byte - nibble = (column % 2 == 0? nibble >> 4: nibble) & 0x0F; - img_fvec[(row_index + row_subindex) - + column * image_data->height] = nibble; - } - else if (image_data->bits_per_sample == 1 - && image_data->samples_per_pixel == 1) - { - uint8_t byte - = ((uint8_t *)buf)[row_offset + column / 8]; - // Extract the needed bit from the byte - uint8_t bit = (byte >> (7 - column % 8)) & 0x01; - img_fvec[(row_index + row_subindex) - + column * image_data->height] = bit; - } - else - error ("Unsupported bit depth"); - } - } + + corrected_row] + = ((P *)buf)[row_subindex * image_data->width + + column]; + } + } + else if (image_data->bits_per_sample == 4 + && image_data->samples_per_pixel == 1) + { + // TODO(maged): Check FillOrder for completeness + uint8_t nibble + = ((uint8_t *)buf)[row_offset + column / 2]; + // Extract the needed nibble from the byte + nibble = (column % 2 == 0? nibble >> 4: nibble) & 0x0F; + img_fvec[(row_index + row_subindex) + + column * image_data->height] = nibble; + } + else if (image_data->bits_per_sample == 1 + && image_data->samples_per_pixel == 1) + { + uint8_t byte + = ((uint8_t *)buf)[row_offset + column / 8]; + // Extract the needed bit from the byte + uint8_t bit = (byte >> (7 - column % 8)) & 0x01; + img_fvec[(row_index + row_subindex) + + column * image_data->height] = bit; + } + else + error ("Unsupported bit depth"); + } row_index += rows_in_strip; } _TIFFfree (buf); - return octave_value(img); + return octave_value (img); + } + + template + octave_value + read_tiled_image (TIFF *tif, tiff_image_data *image_data) + { + typedef typename T::element_type P; + + T img = T (dim_vector (image_data->height, image_data->width, + image_data->samples_per_pixel)); + P *img_fvec = img.fortran_vec (); + + // Obtain the necessary data for handling the tiles + uint32_t tile_width, tile_height; + validate_tiff_get_field (TIFFGetField (tif, TIFFTAG_TILEWIDTH, + &tile_width)); + validate_tiff_get_field (TIFFGetField (tif, TIFFTAG_TILELENGTH, + &tile_height)); + + tdata_t buf = _TIFFmalloc (TIFFTileSize (tif)); + if (! buf) + error ("Failed to allocate buffer for tile data"); + + // TODO(maged): implement support for ImageDepth? + uint32_t tile_size; + for (uint32_t base_row = 0; base_row < image_data->height; base_row += tile_height) + for (uint32_t base_col = 0; base_col < image_data->width; base_col += tile_width) + if (image_data->planar_configuration == PLANARCONFIG_CONTIG) + { + if ((tile_size = TIFFReadTile(tif, buf, base_col, base_row, 0, 0)) == -1) + error ("Failed to read tile data"); + + // Boundary tiles are zero padded so we might need to stop earlier than the end of the tile + for (uint32_t sub_row = 0; sub_row < tile_height && base_row + sub_row < image_data->height; sub_row++) + for (uint32_t sub_col = 0; sub_col < tile_width && base_col + sub_col < image_data->width; sub_col++) + for (uint16_t sample = 0; sample < image_data->samples_per_pixel; sample++) + img_fvec[sample * image_data->width * image_data->height + + (base_col + sub_col) * image_data->height + + base_row + sub_row] + = ((P *)buf)[sub_row * tile_width * image_data->samples_per_pixel + + sub_col * image_data->samples_per_pixel + + sample]; + } + else + // TODO(maged): Implement for separated planes + error ("Tiled images with separate planar configuration not supported"); + + _TIFFfree (buf); + + return octave_value (img); } template @@ -149,24 +209,11 @@ read_image (TIFF *tif, tiff_image_data *image_data) { if (image_data->is_tiled) - // TODO(maged): Implement tiled images - error ("Tiled images are not implemented yet"); + return read_tiled_image (tif, image_data); else return read_stripped_image (tif, image_data); } - // Error if status is not 1 (success status for TIFFGetField) - void - validate_tiff_get_field (bool status, void *p_to_free=NULL) - { - if (status != 1) - { - if (p_to_free != NULL) - _TIFFfree (p_to_free); - error ("Failed to read tag"); - } - } - // Convert tag value to double octave_value interpret_scalar_tag_data (void *data, TIFFDataType tag_datatype) @@ -566,7 +613,6 @@ case TIFFTAG_DOTRANGE: case TIFFTAG_YCBCRSUBSAMPLING: { - // TODO(maged): fix bug where only first one is returned uint16_t tag_part1, tag_part2; validate_tiff_get_field (TIFFGetField (tif, tag_id, &tag_part1, &tag_part2));