changeset 31112:e3d8443585fe

Tiff read refactored reading stripped images into a separate function * __tiff__.cc: made a separate function to handle stripped images for better code reuse.
author magedrifaat <magedrifaat@gmail.com>
date Fri, 15 Jul 2022 21:41:13 +0200
parents 5d79d99c96b9
children a74059523d74
files libinterp/dldfcn/__tiff__.cc
diffstat 1 files changed, 155 insertions(+), 121 deletions(-) [+]
line wrap: on
line diff
--- 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]);