changeset 31111:5d79d99c96b9

Tiff read: Added support for bi-level and 4-bit-grayscale images * __tiff__.cc (F__tiff_read__): added the necessary logic for handling bit depths that are less than one byte in width (1 or 4 bits).
author magedrifaat <magedrifaat@gmail.com>
date Fri, 08 Jul 2022 14:06:55 +0200
parents 2daeeff33980
children e3d8443585fe
files libinterp/dldfcn/__tiff__.cc
diffstat 1 files changed, 46 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/dldfcn/__tiff__.cc	Thu Jul 07 01:54:20 2022 +0200
+++ b/libinterp/dldfcn/__tiff__.cc	Fri Jul 08 14:06:55 2022 +0200
@@ -596,12 +596,12 @@
     // 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);
-    uint32_t row_size = width * pixel_size;
     for (uint32_t row = 0; row < height; row++)
       {
         image[row] = (void **)malloc (sizeof(void **) * width);
         for (uint32_t column = 0; column < width; column++)
-          image[row][column] = malloc (pixel_size);
+          // Allocate at least one byte per pixel (For BitsPerSample < 8)
+          image[row][column] = malloc (pixel_size > 0? pixel_size: 1);
       }
 
     if (is_tiled)
@@ -616,23 +616,61 @@
           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;
-            if ((strip_bytes = TIFFReadEncodedStrip (tif, strip, buf, -1)) == -1)
+            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 / row_size;
+                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++)
-                      memcpy (image[row_index + row_subindex][column],
-                              buf + row_size * row_subindex
-                              + column * pixel_size, pixel_size);
+                      {
+                        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;
               }
@@ -647,7 +685,6 @@
     // TODO(maged): what about palette images? are they handled internally?
     switch (bits_per_sample)
       {
-      // TODO(maged): this is probably incorrect as bilevel has no BitsPerSample
       case 1:
         {
           boolNDArray arr(arr_dims);
@@ -659,7 +696,6 @@
           retval(0) = arr;
           break;
         }
-      // TODO(maged): bits_per_sample of 4 will probably require more logic
       case 4:
       case 8:
         {