# HG changeset patch # User magedrifaat # Date 1657914073 -7200 # Node ID e3d8443585fe3f81cb9084e198dac2f50031b5ff # Parent 5d79d99c96b96523cbafe1f558c29d85babda8e2 Tiff read refactored reading stripped images into a separate function * __tiff__.cc: made a separate function to handle stripped images for better code reuse. diff -r 5d79d99c96b9 -r e3d8443585fe libinterp/dldfcn/__tiff__.cc --- a/libinterp/dldfcn/__tiff__.cc Fri Jul 08 14:06:55 2022 +0200 +++ b/libinterp/dldfcn/__tiff__.cc Fri Jul 15 21:41:13 2022 +0200 @@ -21,6 +21,100 @@ namespace octve { #if defined (HAVE_TIFF) + + struct tiff_image_data + { + public: + uint32_t width; + uint32_t height; + uint16_t samples_per_pixel; + uint16_t bits_per_sample; + uint16_t planar_configuration; + uint16_t pixel_size; + uint16_t is_tiled; + }; + + void + read_stripped_image (TIFF *tif, tiff_image_data *image_data, + void ***pixel_buffer) + { + // Obtain the necessary data for handling the strips + uint32_t strip_count = TIFFNumberOfStrips (tif); + tdata_t buf = _TIFFmalloc (TIFFStripSize (tif)); + if (! buf) + error ("Failed to allocate buffer for strip data"); + + uint32_t row_index = 0; + uint32_t row_size_in_bits = image_data->width + * image_data->samples_per_pixel + * image_data->bits_per_sample; + // According to the Tiff format specification, the row size is + // padded at least up to the next byte, so we add padding to + // complete the byte + row_size_in_bits += (8 - row_size_in_bits % 8) % 8; + for (uint32_t strip = 0; strip < strip_count; strip++) + { + uint32_t strip_bytes = TIFFReadEncodedStrip (tif, strip, buf, -1); + if (strip_bytes == -1) + error ("Failed to read strip data"); + + if (image_data->planar_configuration == PLANARCONFIG_CONTIG) + { + uint32_t rows_in_strip = strip_bytes * 8 / row_size_in_bits; + 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 + // for palette images + if (image_data->bits_per_sample >= 8 + && image_data->bits_per_sample % 8 == 0) + { + // TODO(maged): copy entire strip at once? + memcpy (pixel_buffer[row_index + row_subindex][column], + buf + row_offset + + column * image_data->pixel_size, + image_data->pixel_size); + } + else if (image_data->bits_per_sample == 4 + && image_data->samples_per_pixel == 1) + { + // TODO(maged): Check FillOrder for completeness + uint8_t byte + = ((uint8_t *)buf)[row_offset + column / 2]; + // Extract the needed nibble from the byte + byte = (column % 2 == 0? byte >> 4: byte) & 0x0F; + uint8_t ***u8_img = (uint8_t ***)pixel_buffer; + u8_img[row_index + row_subindex][column][0] = byte; + } + 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; + uint8_t ***u8_img = (uint8_t ***)pixel_buffer; + u8_img[row_index + row_subindex][column][0] = bit; + } + else + error ("Unsupported bit depth"); + } + } + row_index += rows_in_strip; + } + else + error ("Images with multiple planes are not implemented yet"); + } + _TIFFfree (buf); + } + // Error if status is not 1 (success status for TIFFGetField) void validate_tiff_get_field (bool status, void *p_to_free=NULL) @@ -33,8 +127,9 @@ } } + // Convert tag value to double octave_value_list - interpret_scalar (void *data, TIFFDataType tag_datatype) + interpret_scalar_tag_data (void *data, TIFFDataType tag_datatype) { double retval; @@ -115,14 +210,14 @@ // Convert memory buffer into suitable octave values // depending on tag_datatype octave_value_list - interpret_data (void *data, uint32_t count, TIFFDataType tag_datatype) + interpret_tag_data (void *data, uint32_t count, TIFFDataType tag_datatype) { octave_value_list ovl_data; // Apparently matlab converts scalar numerical values into double // but doesn't do the same for arrays if (count == 1 && tag_datatype != TIFF_ASCII) { - ovl_data = interpret_scalar (data, tag_datatype); + ovl_data = interpret_scalar_tag_data (data, tag_datatype); } else { @@ -288,7 +383,7 @@ int type_size = TIFFDataWidth (TIFFFieldDataType (fip)); void *data = _TIFFmalloc (type_size); validate_tiff_get_field (TIFFGetField (tif, tag_id, data), data); - tag_data_ovl = interpret_data (data, 1, TIFFFieldDataType (fip)); + tag_data_ovl = interpret_tag_data (data, 1, TIFFFieldDataType (fip)); _TIFFfree (data); return tag_data_ovl; @@ -300,7 +395,7 @@ void *data; validate_tiff_get_field (TIFFGetField (tif, TIFFFieldTag (fip), &data)); - return interpret_data (data, array_size, TIFFFieldDataType (fip)); + return interpret_tag_data (data, array_size, TIFFFieldDataType (fip)); } octave_value_list @@ -350,13 +445,13 @@ validate_tiff_get_field (TIFFGetField (tif, TIFFTAG_COLORMAP, &red, &green, &blue)); tag_data_ovl(0) - = octave_value (interpret_data (red, count, + = octave_value (interpret_tag_data (red, count, TIFFFieldDataType (fip))); tag_data_ovl(1) - = octave_value (interpret_data (green, count, + = octave_value (interpret_tag_data (green, count, TIFFFieldDataType (fip))); tag_data_ovl(2) - = octave_value (interpret_data (blue, count, + = octave_value (interpret_tag_data (blue, count, TIFFFieldDataType (fip))); break; @@ -377,7 +472,7 @@ { validate_tiff_get_field (TIFFGetField (tif, TIFFTAG_COLORMAP, &ch1)); tag_data_ovl(0) - = octave_value (interpret_data (ch1, count, + = octave_value (interpret_tag_data (ch1, count, TIFFFieldDataType (fip))); } else @@ -385,13 +480,13 @@ validate_tiff_get_field (TIFFGetField (tif, TIFFTAG_COLORMAP, &ch1, &ch2, &ch3)); tag_data_ovl(0) - = octave_value (interpret_data (ch1, count, + = octave_value (interpret_tag_data (ch1, count, TIFFFieldDataType (fip))); tag_data_ovl(1) - = octave_value (interpret_data (ch2, count, + = octave_value (interpret_tag_data (ch2, count, TIFFFieldDataType (fip))); tag_data_ovl(2) - = octave_value (interpret_data (ch3, count, + = octave_value (interpret_tag_data (ch3, count, TIFFFieldDataType (fip))); } break; @@ -405,10 +500,10 @@ validate_tiff_get_field (TIFFGetField (tif, tag_id, &tag_part1, &tag_part2)); tag_data_ovl(0) - = octave_value (interpret_data (&tag_part1, 1, + = octave_value (interpret_tag_data (&tag_part1, 1, TIFFFieldDataType (fip))); tag_data_ovl(1) - = octave_value (interpret_data (&tag_part2, 1, + = octave_value (interpret_tag_data (&tag_part2, 1, TIFFFieldDataType (fip))); break; } @@ -417,7 +512,7 @@ uint16_t count; uint64_t *offsets; validate_tiff_get_field (TIFFGetField (tif, tag_id, &count, &offsets)); - tag_data_ovl = interpret_data (offsets, count, TIFFFieldDataType (fip)); + tag_data_ovl = interpret_tag_data (offsets, count, TIFFFieldDataType (fip)); break; } case TIFFTAG_EXTRASAMPLES: @@ -425,7 +520,7 @@ uint16_t count; uint16_t *types; validate_tiff_get_field (TIFFGetField (tif, tag_id, &count, &types)); - tag_data_ovl = interpret_data (types, count, TIFFFieldDataType (fip)); + tag_data_ovl = interpret_tag_data (types, count, TIFFFieldDataType (fip)); break; } // TODO(maged): These tags are more complex to implement @@ -571,126 +666,65 @@ // What about floating point images? // Obtain all necessary tags - uint32_t width, height; - if (! TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &width)) + // The main purpose of this struct is to hold all the necessary tags that + // will be common among all the different cases of handling the the image + // data to avoid repeating the same calls to TIFFGetField in the different + // functions as each call is a possible point of failure + tiff_image_data image_data; + if (! TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &image_data.width)) error ("Failed to read image width"); - if (! TIFFGetField (tif, TIFFTAG_IMAGELENGTH, &height)) + if (! TIFFGetField (tif, TIFFTAG_IMAGELENGTH, &image_data.height)) error ("Failed to read image height"); - uint16_t samples_per_pixel; - if (! TIFFGetField (tif, TIFFTAG_SAMPLESPERPIXEL, &samples_per_pixel)) + if (! TIFFGetField (tif, TIFFTAG_SAMPLESPERPIXEL, + &image_data.samples_per_pixel)) error ("Failed to read the SamplesPerPixel tag"); - uint16_t bits_per_sample; - if (! TIFFGetField (tif, TIFFTAG_BITSPERSAMPLE, &bits_per_sample)) + if (! TIFFGetField (tif, TIFFTAG_BITSPERSAMPLE, + &image_data.bits_per_sample)) error ("Failed to read the BitsPerSample tag"); - uint16_t planar_configuration; - if (! TIFFGetField (tif, TIFFTAG_PLANARCONFIG, &planar_configuration)) + if (! TIFFGetField (tif, TIFFTAG_PLANARCONFIG, + &image_data.planar_configuration)) error ("Failed to read the PlanarConfiguration tag"); - int is_tiled = TIFFIsTiled(tif); + image_data.pixel_size = image_data.samples_per_pixel + * (image_data.bits_per_sample / 8); + image_data.is_tiled = TIFFIsTiled(tif); // Create memory for storing the image data // TODO(maged): replace malloc with a suitable C++ structure - void ***image = (void ***)malloc (sizeof(void ***) * height); - uint32_t pixel_size = samples_per_pixel * (bits_per_sample / 8); - for (uint32_t row = 0; row < height; row++) + void ***image = (void ***)malloc (sizeof(void ***) * image_data.height); + for (uint32_t row = 0; row < image_data.height; row++) { - image[row] = (void **)malloc (sizeof(void **) * width); - for (uint32_t column = 0; column < width; column++) + image[row] = (void **)malloc (sizeof(void **) * image_data.width); + for (uint32_t column = 0; column < image_data.width; column++) // Allocate at least one byte per pixel (For BitsPerSample < 8) - image[row][column] = malloc (pixel_size > 0? pixel_size: 1); + image[row][column] = malloc (image_data.pixel_size > 0 ? + image_data.pixel_size: 1); } - if (is_tiled) + if (image_data.is_tiled) // TODO(maged): Implement tiled images error ("Tiled images are not implemented yet"); else { - // Obtain the necessary data for handling the strips - uint32_t strip_count = TIFFNumberOfStrips (tif); - tdata_t buf = _TIFFmalloc (TIFFStripSize (tif)); - if (! buf) - error ("Failed to allocate buffer for strip data"); - - uint32_t row_index = 0; - uint32_t row_size_in_bits = width * samples_per_pixel - * bits_per_sample; - // According to the Tiff format specification, the row size is - // padded at least up to the next byte, so we add padding to - // complete the byte - row_size_in_bits += (8 - row_size_in_bits % 8) % 8; - for (uint32_t strip = 0; strip < strip_count; strip++) - { - uint32_t strip_bytes = TIFFReadEncodedStrip (tif, strip, buf, -1); - if (strip_bytes == -1) - error ("Failed to read strip data"); - - if (planar_configuration == PLANARCONFIG_CONTIG) - { - uint32_t rows_in_strip = strip_bytes * 8 / row_size_in_bits; - for (uint32_t row_subindex = 0; - row_subindex < rows_in_strip; - row_subindex++) - { - for (uint32_t column = 0; column < width; column++) - { - uint32_t row_offset = row_size_in_bits / 8 - * row_subindex; - // TODO(maged): support arbitrary BitsPerSample - // for palette images - if (bits_per_sample >= 8 && bits_per_sample % 8 == 0) - { - memcpy (image[row_index + row_subindex][column], - buf + row_offset + column * pixel_size, - pixel_size); - } - else if (bits_per_sample == 4 - && samples_per_pixel == 1) - { - // TODO(maged): Check FillOrder for completeness - uint8_t byte - = ((uint8_t *)buf)[row_offset + column / 2]; - // Extract the needed nibble from the byte - byte = (column % 2 == 0? byte >> 4: byte) & 0x0F; - uint8_t ***u8_img = (uint8_t ***)image; - u8_img[row_index + row_subindex][column][0] = byte; - } - else if (bits_per_sample == 1 - && 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; - uint8_t ***u8_img = (uint8_t ***)image; - u8_img[row_index + row_subindex][column][0] = bit; - } - else - error ("Unsupported bit depth"); - } - } - row_index += rows_in_strip; - } - else - error ("Images with multiple planes are not implemented yet"); - } - _TIFFfree (buf); + read_stripped_image (tif, &image_data, image); } octave_value_list retval; - dim_vector arr_dims (height, width, samples_per_pixel); + dim_vector arr_dims (image_data.height, image_data.width, + image_data.samples_per_pixel); // TODO(maged): what about palette images? are they handled internally? - switch (bits_per_sample) + switch (image_data.bits_per_sample) { case 1: { boolNDArray arr(arr_dims); - for (uint32_t row = 0; row < height; row++) - for (uint32_t column = 0; column < width; column++) - for (uint16_t sample = 0; sample < samples_per_pixel; sample++) + for (uint32_t row = 0; row < image_data.height; row++) + for (uint32_t column = 0; column < image_data.width; column++) + for (uint16_t sample = 0; sample < image_data.samples_per_pixel; sample++) arr(row, column, sample) = ((uint8_t ***)image)[row][column][sample]; retval(0) = arr; @@ -700,9 +734,9 @@ case 8: { uint8NDArray arr(arr_dims); - for (uint32_t row = 0; row < height; row++) - for (uint32_t column = 0; column < width; column++) - for (uint16_t sample = 0; sample < samples_per_pixel; sample++) + for (uint32_t row = 0; row < image_data.height; row++) + for (uint32_t column = 0; column < image_data.width; column++) + for (uint16_t sample = 0; sample < image_data.samples_per_pixel; sample++) arr(row, column, sample) = ((uint8_t ***)image)[row][column][sample]; retval(0) = arr; @@ -711,9 +745,9 @@ case 16: { uint16NDArray arr(arr_dims); - for (uint32_t row = 0; row < height; row++) - for (uint32_t column = 0; column < width; column++) - for (uint16_t sample = 0; sample < samples_per_pixel; sample++) + for (uint32_t row = 0; row < image_data.height; row++) + for (uint32_t column = 0; column < image_data.width; column++) + for (uint16_t sample = 0; sample < image_data.samples_per_pixel; sample++) arr(row, column, sample) = ((uint16_t ***)image)[row][column][sample]; retval(0) = arr; @@ -722,9 +756,9 @@ case 32: { uint32NDArray arr(arr_dims); - for (uint32_t row = 0; row < height; row++) - for (uint32_t column = 0; column < width; column++) - for (uint16_t sample = 0; sample < samples_per_pixel; sample++) + for (uint32_t row = 0; row < image_data.height; row++) + for (uint32_t column = 0; column < image_data.width; column++) + for (uint16_t sample = 0; sample < image_data.samples_per_pixel; sample++) arr(row, column, sample) = ((uint32_t ***)image)[row][column][sample]; retval(0) = arr; @@ -733,9 +767,9 @@ case 64: { uint64NDArray arr(arr_dims); - for (uint32_t row = 0; row < height; row++) - for (uint32_t column = 0; column < width; column++) - for (uint16_t sample = 0; sample < samples_per_pixel; sample++) + for (uint32_t row = 0; row < image_data.height; row++) + for (uint32_t column = 0; column < image_data.width; column++) + for (uint16_t sample = 0; sample < image_data.samples_per_pixel; sample++) arr(row, column, sample) = ((uint64_t ***)image)[row][column][sample]; retval(0) = arr; @@ -745,9 +779,9 @@ error ("Unsupported bit depth"); } - for (uint32_t row = 0; row < height; row++) + for (uint32_t row = 0; row < image_data.height; row++) { - for (uint32_t column = 0; column < width; column++) + for (uint32_t column = 0; column < image_data.width; column++) free (image[row][column]); free (image[row]);