# HG changeset patch # User magedrifaat # Date 1658604887 -7200 # Node ID 7851c5b9c950d5fd95bcd332f088cae65087d281 # Parent 3b775b939de4e3216ffe044af138b13c7d911a8a Tiff: implemented writeEncodedStrip function for writing a strip to an image * Tiff.m(writeEncodedStrip): added a method for writing strip to the image. * __tiff__.cc(F__tiff_write_encoded_strip): implemented an internal function to preprocess the wwriteEncodedStrip function call. * __tiff__.cc(write_strip): implemented the logic to write the strip data to the file. diff -r 3b775b939de4 -r 7851c5b9c950 libinterp/dldfcn/__tiff__.cc --- a/libinterp/dldfcn/__tiff__.cc Fri Jul 22 05:07:00 2022 +0200 +++ b/libinterp/dldfcn/__tiff__.cc Sat Jul 23 21:34:47 2022 +0200 @@ -864,6 +864,84 @@ error ("Failed to set tag value"); } + template + void + write_strip (TIFF *tif, uint32_t strip_no, octave_value strip_data_ov, + tiff_image_data *image_data) + { + T strip_data = T (strip_data_ov.array_value ()); + + uint32_t rows_in_strip; + if (! TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rows_in_strip)) + error ("Failed to obtain the RowsPerStrip tag"); + + uint32_t strip_count = TIFFNumberOfStrips (tif); + uint32_t expected_numel; + + // Calculate the expected number of elements in the strip data array + // All strips have equal number of rows excpet strips at the bottom + // of the image can have less number of rows + if (image_data->planar_configuration == PLANARCONFIG_CONTIG) + { + // All strips have equal number of rows excpet strips at the bottom + // of the image can have less number of rows + if (strip_no == strip_count) + rows_in_strip = image_data->height - rows_in_strip * (strip_no - 1); + expected_numel = rows_in_strip * image_data->width + * image_data->samples_per_pixel; + } + else if (image_data->planar_configuration == PLANARCONFIG_SEPARATE) + { + // For separate planes, we should check the last strip of each channel + uint32_t strips_per_channel + = strip_count / image_data->samples_per_pixel; + for (uint32_t boundary_strip = strips_per_channel; + boundary_strip <= strip_count; + boundary_strip += strips_per_channel) + if (strip_no == boundary_strip) + rows_in_strip = image_data->height - rows_in_strip + * ((strip_no - 1) + % (strips_per_channel)); + expected_numel = rows_in_strip * image_data->width; + } + else + error ("Planar configuration not supported"); + + if (strip_data.numel () != expected_numel) + error ("Size of strip data is different from the expected size of the strip"); + + + // Put the strip in a consistent shape for the subsequent permutation + if (image_data->planar_configuration == PLANARCONFIG_CONTIG) + { + strip_data + = strip_data.reshape (dim_vector (rows_in_strip, image_data->width, + image_data->samples_per_pixel)); + } + else if (image_data->planar_configuration == PLANARCONFIG_SEPARATE) + { + strip_data = strip_data.reshape (dim_vector (rows_in_strip, + image_data->width, 1)); + } + + // Permute the dimesions of the strip to match the expected memory + // arrangement of LibTIFF (channel x width x height) + Array perm (dim_vector (3, 1)); + perm(0) = 2; + perm(1) = 1; + perm(2) = 0; + strip_data = strip_data.permute (perm); + + // LibTIFF uses zero-based indexing as opposed to Octave's 1-based + strip_no--; + // Can't rely in LibTIFF's TIFFStripSize because boundary strips + // can be smaller in size + tsize_t strip_size = expected_numel * image_data->bits_per_sample / 8; + if (TIFFWriteEncodedStrip (tif, strip_no, + strip_data.fortran_vec (), strip_size) == -1) + error ("Failed to write strip data to image"); + } + #endif DEFUN_DLD (__open_tiff__, args, nargout, @@ -1025,12 +1103,7 @@ TIFF *tif = (TIFF *)(args (0).uint64_value ()); - // Check: Strips vs Tiles - // Planar Configuration - // SamplesPerPixel and bits_per_smaple - // nargout and ycbcr - // SampleFormat? ExtendedSamples? TransferFunction? GrayResponse? ColorMap? - // What about floating point images? + // TODO(maged): nargout and ycbcr // Obtain all necessary tags // The main purpose of this struct is to hold all the necessary tags that @@ -1065,7 +1138,6 @@ retval(0) = read_image (tif, &image_data); break; case 32: - // TODO(maged): do we need to check for Min and MaxSampleValue if (sample_format == 3) retval(0) = read_image (tif, &image_data); else @@ -1086,4 +1158,101 @@ err_disabled_feature ("read", "Tiff"); #endif } + + DEFUN_DLD (__tiff_write_encoded_strip__, args, nargout, + "Write an encoded strip to the image") + { +#if defined (HAVE_TIFF) + int nargin = args.length (); + + if (nargin < 3) + error ("Too few arguments provided\n"); + + TIFF *tif = (TIFF *)(args (0).uint64_value ()); + + // Obtain all necessary tags + tiff_image_data image_data (tif); + + if (image_data.is_tiled) + error ("Can't rite strips to a tiled image"); + + uint32_t strip_no = args (1).uint_value (); + if (strip_no < 1 || strip_no > TIFFNumberOfStrips (tif)) + error ("Strip number out of range"); + + // SampleFormat tag is not a required field and has a default value of 1 + // So we need to use TIFFGetFieldDefaulted in case it is not present in + // the file + uint16_t sample_format; + if (! TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLEFORMAT, &sample_format)) + error ("Failed to obtain a value for sample format"); + + if (sample_format == 3) + { + if (image_data.bits_per_sample != 32 && image_data.bits_per_sample != 64) + error ("Floating point images are only supported for bit depths of 32 and 64"); + } + // The standard specifies that a SampleFormat of 4 should be treated + // the same as 1 (unsigned integer) + else if (sample_format != 1 && sample_format != 4) + error ("Unsupported sample format"); + + std::string type_name = args(2).type_name (); + switch (image_data.bits_per_sample) + { + case 1: + if (type_name == "bool matrix") + write_strip (tif, strip_no, args(2), &image_data); + else + error ("Expected logical matrix for BiLevel image"); + break; + case 4: + case 8: + if (type_name == "uint8 matrix" || type_name == "int8 matrix") + write_strip (tif, strip_no, args(2), &image_data); + else + error ("Only uint8 and int8 data are allowed for images with bit depth of 8"); + break; + case 16: + // Type conversion from signed to unsigned is handled in the function + // TODO(maged): what is the behavior if the input matrix has + // negative numbers? + if (type_name == "uint16 matrix" || type_name == "int16 matrix") + write_strip (tif, strip_no, args(2), &image_data); + else + error ("Only uint16 and int16 data are allowed for images with bit depth of 16"); + break; + case 32: + if (sample_format == 3) + if (type_name == "float matrix" || type_name == "matrix") + write_strip (tif, strip_no, args(2), &image_data); + else + error ("Only single and double data are allowed for floating-point images"); + else + if (type_name == "uint32 matrix" || type_name == "int32 matrix") + write_strip (tif, strip_no, args(2), &image_data); + else + error ("Only uint32 and int32 data are allowed for images with bit depth of 32"); + break; + case 64: + if (sample_format == 3) + if (type_name == "float matrix" || type_name == "matrix") + write_strip (tif, strip_no, args(2), &image_data); + else + error ("Only single and double data are allowed for floating-point images"); + else + if (type_name == "uint64 matrix" || type_name == "int64 matrix") + write_strip (tif, strip_no, args(2), &image_data); + else + error ("Only uint64 and int64 data are allowed for images with bit depth of 64"); + break; + default: + error ("Unsupported bit depth"); + } + + return octave_value_list (); +#else + err_disabled_feature ("writeEncodedStrip", "Tiff"); +#endif + } } diff -r 3b775b939de4 -r 7851c5b9c950 scripts/io/Tiff.m --- a/scripts/io/Tiff.m Fri Jul 22 05:07:00 2022 +0200 +++ b/scripts/io/Tiff.m Sat Jul 23 21:34:47 2022 +0200 @@ -79,29 +79,33 @@ endproperties methods - function t = Tiff(filename, mode="r") + function t = Tiff (filename, mode="r") if (nargin == 0 || nargin > 2) % print_usage(); error("Usage: Tiff(filename[, mode])"); endif - t.tiff_handle = __open_tiff__(filename, mode); + t.tiff_handle = __open_tiff__ (filename, mode); endfunction - function close(t) - __close_tiff__(t.tiff_handle); + function close (t) + __close_tiff__ (t.tiff_handle); + endfunction + + function tag = getTag (t, tag_name) + tag = __tiff_get_tag__ (t.tiff_handle, tag_name); endfunction - function tag = getTag(t, tag_name) - tag = __tiff_get_tag__(t.tiff_handle, tag_name); + function setTag (t, tag_name, tag_value) + __tiff_set_tag__ (t.tiff_handle, tag_name, tag_value); endfunction - function setTag(t, tag_name, tag_value) - __tiff_set_tag__(t.tiff_handle, tag_name, tag_value); + function argout = read (t) + argout = __tiff_read__ (t.tiff_handle); endfunction - function argout = read(t) - argout = __tiff_read__(t.tiff_handle); + function writeEncodedStrip (t, stripNumber, imageData) + __tiff_write_encoded_strip__ (t.tiff_handle, stripNumber, imageData); endfunction % TODO(maged): add documentation and make print_usage work