changeset 31154:828b7cc9aa36

Tiff write: added support for BiLevel stripped images and added unit tests * __tiff__.cc(write_stripped_image): implemented support for writing logical images. * Tiff.m: added tests for writing stripped images using the write method.
author magedrifaat <magedrifaat@gmail.com>
date Thu, 04 Aug 2022 00:02:27 +0200
parents c66d6c7f025e
children a30b144bc10b
files libinterp/dldfcn/__tiff__.cc scripts/io/Tiff.m
diffstat 2 files changed, 203 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/dldfcn/__tiff__.cc	Wed Aug 03 22:06:43 2022 +0200
+++ b/libinterp/dldfcn/__tiff__.cc	Thu Aug 04 00:02:27 2022 +0200
@@ -1268,12 +1268,45 @@
       {
         rows_in_strip = get_rows_in_strip (strip, strip_count,
                                            row_per_strip, image_data);
-        strip_size = rows_in_strip * image_data->width * sizeof (P);
-        if (image_data->planar_configuration == PLANARCONFIG_CONTIG)
-          strip_size *= image_data->samples_per_pixel;
-        if (! TIFFWriteEncodedStrip (tif, strip, pixel_fvec, strip_size))
-          error ("Failed to rite strip data");
-        pixel_fvec += strip_size;
+        if (image_data->bits_per_sample == 8
+            || image_data->bits_per_sample == 16
+            || image_data->bits_per_sample == 32
+            || image_data->bits_per_sample == 64)
+          {
+            strip_size = rows_in_strip * image_data->width * sizeof (P);
+            if (image_data->planar_configuration == PLANARCONFIG_CONTIG)
+              strip_size *= image_data->samples_per_pixel;
+            if (! TIFFWriteEncodedStrip (tif, strip, pixel_fvec, strip_size))
+              error ("Failed to rite strip data");
+            pixel_fvec += strip_size;
+          }
+        else if (image_data->bits_per_sample == 1)
+          {
+            // Create a buffer to hold the packed strip data
+            // Unique pointers are faster than vectors for constant size buffers
+            std::unique_ptr<uint8_t []> strip_ptr
+              = std::make_unique<uint8_t []> (TIFFStripSize (tif));
+            uint8_t *strip_buf = strip_ptr.get ();
+            // According to the format specification, the row should be byte
+            // aligned so the number of bytes is rounded up to the nearest byte
+            uint32_t padded_width = (image_data->width + 7) / 8;
+            // Packing the pixel data into bits
+            for (uint32_t row = 0; row < rows_in_strip; row++)
+              {
+                for (uint32_t col = 0; col < image_data->width; col++)
+                {
+                  uint8_t shift = 7 - col % 8;
+                  strip_buf[row * padded_width + col / 8]
+                    |= pixel_fvec[col] << shift;
+                }
+                pixel_fvec += image_data->width;
+              }
+            strip_size = padded_width * rows_in_strip;
+            if (TIFFWriteEncodedStrip (tif, strip, strip_buf, strip_size) == -1)
+              error ("Failed to write strip data to image");
+          }
+        else
+          error ("Unsupported bit depth");
       }
   }
 
--- a/scripts/io/Tiff.m	Wed Aug 03 22:06:43 2022 +0200
+++ b/scripts/io/Tiff.m	Thu Aug 04 00:02:27 2022 +0200
@@ -897,3 +897,167 @@
 %!    fail ("computeTile (img, 1, 1)", "The image is stripped not tiled");
 %!  endfunction
 %!  file_wrapper (@test_fn);
+
+## test write method one pixel bilevel image
+%!testif HAVE_TIFF
+%!  function test_fn (filename)
+%!    img = Tiff (filename, "w");
+%!    setTag(img, struct ("ImageLength", 1, "ImageWidth", 1));
+%!    write (img, logical ([1]));
+%!    img.close();
+%!    verify_data (filename, logical ([1]), [1, 1]);
+%!  endfunction
+%!  file_wrapper (@test_fn);
+
+## test write method one strip bilevel image
+%!testif HAVE_TIFF
+%!  function test_fn (filename)
+%!    img = Tiff (filename, "w");
+%!    setTag(img, struct ("ImageLength", 10, "ImageWidth", 10));
+%!    data = logical (repmat ([1,0,0,0,1,0,1,1,1,0], [10, 1]));
+%!    write (img, data);
+%!    img.close();
+%!    verify_data (filename, data, [10, 10]);
+%!  endfunction
+%!  file_wrapper (@test_fn);
+
+## test write method multi-strip bilevel image
+%!testif HAVE_TIFF
+%!  function test_fn (filename)
+%!    img = Tiff (filename, "w");
+%!    setTag(img, struct ("ImageLength", 10, "ImageWidth", 10,
+%!                        "RowsPerStrip", 3));
+%!    data = logical (repmat ([1,0,0,0,1,0,1,1,1,0], [10, 1]));
+%!    write (img, data);
+%!    img.close();
+%!    verify_data (filename, data, [10, 10]);
+%!  endfunction
+%!  file_wrapper (@test_fn);
+
+## test write method one pixel grayscale image
+%!testif HAVE_TIFF
+%!  function test_fn (filename)
+%!    img = Tiff (filename, "w");
+%!    setTag(img, struct ("ImageLength", 1, "ImageWidth", 1,
+%!                        "BitsPerSample", 8));
+%!    write (img, uint8 ([255]));
+%!    img.close();
+%!    verify_data (filename, uint8 ([255]), [1, 1]);
+%!  endfunction
+%!  file_wrapper (@test_fn);
+
+## test write method one strip grayscale image
+%!testif HAVE_TIFF
+%!  function test_fn (filename)
+%!    img = Tiff (filename, "w");
+%!    setTag(img, struct ("ImageLength", 10, "ImageWidth", 10,
+%!                        "BitsPerSample", 8));
+%!    data = uint8 (reshape (1:100, [10, 10]));
+%!    write (img, data);
+%!    img.close();
+%!    verify_data (filename, data, [10, 10]);
+%!  endfunction
+%!  file_wrapper (@test_fn);
+
+## test write method multi-strip grayscale image
+%!testif HAVE_TIFF
+%!  function test_fn (filename)
+%!    img = Tiff (filename, "w");
+%!    setTag(img, struct ("ImageLength", 10, "ImageWidth", 10,
+%!                        "BitsPerSample", 8, "RowsPerStrip", 3));
+%!    data = uint8 (reshape (1:100, [10, 10]));
+%!    write (img, data);
+%!    img.close();
+%!    verify_data (filename, data, [10, 10]);
+%!  endfunction
+%!  file_wrapper (@test_fn);
+
+## test write method multi-strip RGB image chunky
+%!testif HAVE_TIFF
+%!  function test_fn (filename)
+%!    img = Tiff (filename, "w");
+%!    setTag(img, struct ("ImageLength", 10, "ImageWidth", 10,
+%!                        "BitsPerSample", 8, "RowsPerStrip", 3,
+%!                        "SamplesPerPixel", 3, "PlanarConfiguration", 1,
+%!                        "PhotometricInterpretation", 2));
+%!    data = uint8 (reshape (1:300, [10, 10, 3]));
+%!    write (img, data);
+%!    img.close();
+%!    verify_data (filename, data, [10, 10, 3]);
+%!  endfunction
+%!  file_wrapper (@test_fn);
+
+## test write method multi-strip RGB image separate
+%!testif HAVE_TIFF
+%!  function test_fn (filename)
+%!    img = Tiff (filename, "w");
+%!    setTag(img, struct ("ImageLength", 10, "ImageWidth", 10,
+%!                        "BitsPerSample", 8, "RowsPerStrip", 3,
+%!                        "SamplesPerPixel", 3, "PlanarConfiguration", 2,
+%!                        "PhotometricInterpretation", 2));
+%!    data = uint8 (reshape (1:300, [10, 10, 3]));
+%!    write (img, data);
+%!    img.close();
+%!    verify_data (filename, data, [10, 10, 3]);
+%!  endfunction
+%!  file_wrapper (@test_fn);
+
+## test write method 16-bit multi-strip image
+%!testif HAVE_TIFF
+%!  function test_fn (filename)
+%!    img = Tiff (filename, "w");
+%!    setTag(img, struct ("ImageLength", 10, "ImageWidth", 10,
+%!                        "BitsPerSample", 16, "RowsPerStrip", 3,
+%!                        "SamplesPerPixel", 3, "PlanarConfiguration", 1,
+%!                        "PhotometricInterpretation", 2));
+%!    data = uint16 (reshape (1:300, [10, 10, 3]));
+%!    write (img, data);
+%!    img.close();
+%!    verify_data (filename, data, [10, 10, 3]);
+%!  endfunction
+%!  file_wrapper (@test_fn);
+
+## test write method 32-bit multi-strip image
+%!testif HAVE_TIFF
+%!  function test_fn (filename)
+%!    img = Tiff (filename, "w");
+%!    setTag(img, struct ("ImageLength", 10, "ImageWidth", 10,
+%!                        "BitsPerSample", 32, "RowsPerStrip", 3,
+%!                        "SamplesPerPixel", 3, "PlanarConfiguration", 1,
+%!                        "PhotometricInterpretation", 2));
+%!    data = uint32 (reshape (1:300, [10, 10, 3]));
+%!    write (img, data);
+%!    img.close();
+%!    verify_data (filename, data, [10, 10, 3]);
+%!  endfunction
+%!  file_wrapper (@test_fn);
+
+## test write method single-precision image
+%!testif HAVE_TIFF
+%!  function test_fn (filename)
+%!    img = Tiff (filename, "w");
+%!    setTag(img, struct ("ImageLength", 10, "ImageWidth", 10,
+%!                        "BitsPerSample", 32, "RowsPerStrip", 3,
+%!                        "SamplesPerPixel", 3, "PlanarConfiguration", 1,
+%!                        "PhotometricInterpretation", 2, "SampleFormat", 3));
+%!    data = single (reshape (1:300, [10, 10, 3]));
+%!    write (img, data);
+%!    img.close();
+%!    verify_data (filename, data, [10, 10, 3]);
+%!  endfunction
+%!  file_wrapper (@test_fn);
+
+## test write method single-precision image
+%!testif HAVE_TIFF
+%!  function test_fn (filename)
+%!    img = Tiff (filename, "w");
+%!    setTag(img, struct ("ImageLength", 10, "ImageWidth", 10,
+%!                        "BitsPerSample", 64, "RowsPerStrip", 3,
+%!                        "SamplesPerPixel", 3, "PlanarConfiguration", 1,
+%!                        "PhotometricInterpretation", 2, "SampleFormat", 3));
+%!    data = double (reshape (1:300, [10, 10, 3]));
+%!    write (img, data);
+%!    img.close();
+%!    verify_data (filename, data, [10, 10, 3]);
+%!  endfunction
+%!  file_wrapper (@test_fn);