Mercurial > octave-libtiff
comparison libinterp/dldfcn/__tiff__.cc @ 31146:50402b8dfb4a
Tiff: added writeEncodedTile function for writing tiled images
* __tiff__.cc(F__tiff_write_encoded_tile__): added internal function for handling writeEncodedStrip.
* __tiff__.cc(process_strip_or_tile): refactored common logic for strips and tiles to a function.
* __tiff__.cc(write_tile): implemented logic for checking and writing tile data to image.
* Tiff.m: added writeEncodedStrip method.
author | magedrifaat <magedrifaat@gmail.com> |
---|---|
date | Sun, 31 Jul 2022 18:34:59 +0200 |
parents | 2e11f9cb30b8 |
children | 7af78a63d3c3 |
comparison
equal
deleted
inserted
replaced
31145:2e11f9cb30b8 | 31146:50402b8dfb4a |
---|---|
297 // TODO(maged): is it necessary to check FillOrder? | 297 // TODO(maged): is it necessary to check FillOrder? |
298 uint8_t bit_number = 7 - pixel % 8; | 298 uint8_t bit_number = 7 - pixel % 8; |
299 uint8_t * img_u8 = reinterpret_cast<uint8_t *> (img_fvec); | 299 uint8_t * img_u8 = reinterpret_cast<uint8_t *> (img_fvec); |
300 img_fvec[pixel]= (img_u8[pixel / 8] >> bit_number) & 0x01; | 300 img_fvec[pixel]= (img_u8[pixel / 8] >> bit_number) & 0x01; |
301 } | 301 } |
302 break; | |
303 } | 302 } |
304 else if (image_data->bits_per_sample == 4) | 303 else if (image_data->bits_per_sample == 4) |
305 { | 304 { |
306 if (image_data->samples_per_pixel != 1) | 305 if (image_data->samples_per_pixel != 1) |
307 error ("4-bit images are only supported for grayscale"); | 306 error ("4-bit images are only supported for grayscale"); |
320 { | 319 { |
321 uint8_t shift = pixel % 2 == 0? 4: 0; | 320 uint8_t shift = pixel % 2 == 0? 4: 0; |
322 uint8_t * img_u8 = reinterpret_cast<uint8_t *> (img_fvec); | 321 uint8_t * img_u8 = reinterpret_cast<uint8_t *> (img_fvec); |
323 img_fvec[pixel] = (img_u8[pixel / 2] >> shift) & 0x0F; | 322 img_fvec[pixel] = (img_u8[pixel / 2] >> shift) & 0x0F; |
324 } | 323 } |
325 break; | |
326 } | 324 } |
327 else if (image_data->bits_per_sample != 8 && | 325 else if (image_data->bits_per_sample != 8 && |
328 image_data->bits_per_sample != 16 && | 326 image_data->bits_per_sample != 16 && |
329 image_data->bits_per_sample != 32 && | 327 image_data->bits_per_sample != 32 && |
330 image_data->bits_per_sample != 64) | 328 image_data->bits_per_sample != 64) |
903 | 901 |
904 uint32_t strip_count = TIFFNumberOfStrips (tif); | 902 uint32_t strip_count = TIFFNumberOfStrips (tif); |
905 dim_vector strip_dimensions; | 903 dim_vector strip_dimensions; |
906 | 904 |
907 // Calculate the expected number of elements in the strip data array | 905 // Calculate the expected number of elements in the strip data array |
908 // All strips have equal number of rows excpet strips at the bottom | 906 // All strips have equal number of rows except strips at the bottom |
909 // of the image can have less number of rows | 907 // of the image can have less number of rows |
910 if (image_data->planar_configuration == PLANARCONFIG_CONTIG) | 908 if (image_data->planar_configuration == PLANARCONFIG_CONTIG) |
911 { | 909 { |
912 // All strips have equal number of rows excpet strips at the bottom | 910 // All strips have equal number of rows excpet strips at the bottom |
913 // of the image can have less number of rows | 911 // of the image can have less number of rows |
932 } | 930 } |
933 else | 931 else |
934 error ("Planar configuration not supported"); | 932 error ("Planar configuration not supported"); |
935 | 933 |
936 if (strip_data.dim1 () > rows_in_strip) | 934 if (strip_data.dim1 () > rows_in_strip) |
937 warning ("The strip is composed of %ld rows but the input has %ld rows.", | 935 warning ("The strip is composed of %u rows but the input has %ld rows.", |
938 rows_in_strip, | 936 rows_in_strip, |
939 strip_data.dim1 ()); | 937 strip_data.dim1 ()); |
940 | 938 |
941 if (strip_data.dim2 () > image_data->width) | 939 if (strip_data.dim2 () > image_data->width) |
942 warning ("The image width is %ld but the input has %ld columns.", | 940 warning ("The image width is %u but the input has %ld columns.", |
943 image_data->width, | 941 image_data->width, |
944 strip_data.dim2 ()); | 942 strip_data.dim2 ()); |
945 | 943 |
946 if (strip_data.ndims () > 2) | 944 if (strip_data.ndims () > 2) |
947 { | 945 { |
948 if (image_data->planar_configuration == PLANARCONFIG_CONTIG | 946 if (image_data->planar_configuration == PLANARCONFIG_CONTIG |
949 && strip_data.dim3 () > image_data->samples_per_pixel) | 947 && strip_data.dim3 () > image_data->samples_per_pixel) |
950 warning ("The strip is composed of %ld channels but the input has %ld channels.", | 948 warning ("The strip is composed of %u channels but the input has %ld channels.", |
951 image_data->samples_per_pixel, | 949 image_data->samples_per_pixel, |
952 strip_data.dim3 ()); | 950 strip_data.dim3 ()); |
953 else if (image_data->planar_configuration == PLANARCONFIG_SEPARATE | 951 else if (image_data->planar_configuration == PLANARCONFIG_SEPARATE |
954 && strip_data.dim3 () > 1) | 952 && strip_data.dim3 () > 1) |
955 warning ("The strip is composed of %ld channel but the input has %ld channels.", | 953 warning ("The strip is composed of %u channel but the input has %ld channels.", |
956 1, strip_data.dim3 ()); | 954 1, strip_data.dim3 ()); |
957 } | 955 } |
958 | 956 |
959 // TODO(maged): check dimesnions of boundary strips in matlab | 957 // TODO(maged): check dimesnions of boundary strips in matlab |
960 strip_data.resize (strip_dimensions); | 958 strip_data.resize (strip_dimensions); |
1011 if (TIFFWriteEncodedStrip (tif, strip_no, strip_buf, strip_size) == -1) | 1009 if (TIFFWriteEncodedStrip (tif, strip_no, strip_buf, strip_size) == -1) |
1012 error ("Failed to write strip data to image"); | 1010 error ("Failed to write strip data to image"); |
1013 } | 1011 } |
1014 else | 1012 else |
1015 { | 1013 { |
1014 error ("Unsupported bit depth"); | |
1015 } | |
1016 } | |
1017 | |
1018 template <typename T> | |
1019 void | |
1020 write_tile (TIFF *tif, uint32_t tile_no, T tile_data, | |
1021 tiff_image_data *image_data) | |
1022 { | |
1023 // TODO(maged): error for tiles not divisible by 16? | |
1024 uint32_t tile_width, tile_height; | |
1025 if (! TIFFGetField (tif, TIFFTAG_TILEWIDTH, &tile_width)) | |
1026 error ("Failed to get the tile width"); | |
1027 if (! TIFFGetField (tif, TIFFTAG_TILELENGTH, &tile_height)) | |
1028 error ("Failed to get the tile length"); | |
1029 | |
1030 if (tile_no < 1 || tile_no > TIFFNumberOfTiles (tif)) | |
1031 error ("Tile number out of bounds"); | |
1032 | |
1033 // TODO(maged): what does matlab do for boundary tiles? | |
1034 if (tile_data.dim1 () > tile_height) | |
1035 warning ("The tile is composed of %u rows but input has %ld rows", | |
1036 tile_height, tile_data.dim1 ()); | |
1037 if (tile_data.dim2 () > tile_width) | |
1038 warning ("The tile is composed of %u columns but input has %ld columns", | |
1039 tile_width, tile_data.dim2 ()); | |
1040 if (tile_data.ndims () > 2) | |
1041 { | |
1042 if (image_data->planar_configuration == PLANARCONFIG_CONTIG | |
1043 && tile_data.dim3 () > image_data->samples_per_pixel) | |
1044 warning ("The tile is composed of %u channels but input has %ld channels", | |
1045 image_data->samples_per_pixel, tile_data.dim3 ()); | |
1046 else if (image_data->planar_configuration == PLANARCONFIG_SEPARATE | |
1047 && tile_data.dim3 () > 1) | |
1048 warning ("The tile is composed of %u channels but input has %ld channels", | |
1049 1, tile_data.dim3 ()); | |
1050 } | |
1051 | |
1052 dim_vector tile_dimensions; | |
1053 if (image_data->planar_configuration == PLANARCONFIG_CONTIG) | |
1054 tile_dimensions = dim_vector (tile_height, tile_width, | |
1055 image_data->samples_per_pixel); | |
1056 else if (image_data->planar_configuration == PLANARCONFIG_SEPARATE) | |
1057 tile_dimensions = dim_vector (tile_height, tile_width, 1); | |
1058 else | |
1059 error ("Planar configuration not supported"); | |
1060 | |
1061 tile_data.resize (tile_dimensions); | |
1062 Array<octave_idx_type> perm (dim_vector (3, 1)); | |
1063 perm(0) = 2; | |
1064 perm(1) = 1; | |
1065 perm(2) = 0; | |
1066 tile_data = tile_data.permute (perm); | |
1067 | |
1068 // Octave indexing is 1-based while LibTIFF is zero-based | |
1069 tile_no--; | |
1070 void *data_vec = tile_data.fortran_vec (); | |
1071 if (image_data->bits_per_sample == 8 | |
1072 || image_data->bits_per_sample == 16 | |
1073 || image_data->bits_per_sample == 32 | |
1074 || image_data->bits_per_sample == 64) | |
1075 { | |
1076 if (TIFFWriteEncodedTile (tif, tile_no, data_vec, | |
1077 TIFFTileSize (tif)) == -1) | |
1078 error ("Failed to write tile data to image"); | |
1079 | |
1080 } | |
1081 else if (image_data->bits_per_sample == 1) | |
1082 { | |
1083 if (image_data->samples_per_pixel != 1) | |
1084 error ("Bi-Level images must have one channel only"); | |
1085 | |
1086 // Create a buffer to hold the packed tile data | |
1087 // Unique pointers are faster than vectors for constant size buffers | |
1088 std::unique_ptr<uint8_t []> tile_ptr | |
1089 = std::make_unique<uint8_t []> (TIFFTileSize (tif)); | |
1090 uint8_t *tile_buf = tile_ptr.get (); | |
1091 uint8_t *data_u8 = reinterpret_cast<uint8_t *> (data_vec); | |
1092 // Packing the pixel data into bits | |
1093 for (uint32_t row = 0; row < tile_height; row++) | |
1094 { | |
1095 for (uint32_t col = 0; col < tile_width; col++) | |
1096 { | |
1097 uint8_t shift = 7 - col % 8; | |
1098 tile_buf[row * tile_width/8 + col/8] |= data_u8[col] << shift; | |
1099 } | |
1100 data_u8 += tile_width; | |
1101 } | |
1102 if (TIFFWriteEncodedTile (tif, tile_no, tile_buf, | |
1103 TIFFTileSize (tif)) == -1) | |
1104 error ("Failed to write tile data to image"); | |
1105 } | |
1106 else | |
1107 { | |
1108 error ("Unsupported bit depth"); | |
1109 } | |
1110 } | |
1111 | |
1112 template <typename T> | |
1113 void | |
1114 write_strip_or_tile (TIFF *tif, uint32_t strip_tile_no, T strip_data, | |
1115 tiff_image_data *image_data) | |
1116 { | |
1117 if (image_data->is_tiled) | |
1118 write_tile<T> (tif, strip_tile_no, strip_data, image_data); | |
1119 else | |
1120 write_strip<T> (tif, strip_tile_no, strip_data, image_data); | |
1121 } | |
1122 | |
1123 void | |
1124 process_strip_or_tile (TIFF *tif, uint32_t strip_tile_no, | |
1125 octave_value data_ov, tiff_image_data *image_data) | |
1126 { | |
1127 | |
1128 // SampleFormat tag is not a required field and has a default value of 1 | |
1129 // So we need to use TIFFGetFieldDefaulted in case it is not present in | |
1130 // the file | |
1131 uint16_t sample_format; | |
1132 if (! TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLEFORMAT, &sample_format)) | |
1133 error ("Failed to obtain a value for sample format"); | |
1134 | |
1135 // TODO(maged): add support for signed integer images | |
1136 if (sample_format == 3) | |
1137 { | |
1138 if (image_data->bits_per_sample != 32 | |
1139 && image_data->bits_per_sample != 64) | |
1140 error ("Floating point images are only supported for bit depths of 32 and 64"); | |
1141 } | |
1142 | |
1143 // The standard specifies that a SampleFormat of 4 should be treated | |
1144 // the same as 1 (unsigned integer) | |
1145 else if (sample_format != 1 && sample_format != 4) | |
1146 error ("Unsupported sample format"); | |
1147 | |
1148 switch (image_data->bits_per_sample) | |
1149 { | |
1150 case 1: | |
1151 // We need to check for both scalar and matrix types to handle single | |
1152 // element strip | |
1153 if (data_ov.is_bool_scalar () || data_ov.is_bool_matrix ()) | |
1154 write_strip_or_tile<boolNDArray> (tif, strip_tile_no, | |
1155 data_ov.bool_array_value (), | |
1156 image_data); | |
1157 else | |
1158 error ("Expected logical matrix for BiLevel image"); | |
1159 break; | |
1160 case 8: | |
1161 if (data_ov.is_uint8_type ()) | |
1162 write_strip_or_tile<uint8NDArray> (tif, strip_tile_no, | |
1163 data_ov.uint8_array_value (), | |
1164 image_data); | |
1165 else | |
1166 error ("Only uint8 data is allowed for uint images with bit depth of 8"); | |
1167 break; | |
1168 case 16: | |
1169 if (data_ov.is_uint16_type ()) | |
1170 write_strip_or_tile<uint16NDArray> (tif, strip_tile_no, | |
1171 data_ov.uint16_array_value (), | |
1172 image_data); | |
1173 else | |
1174 error ("Only uint16 data is allowed for uint images with bit depth of 16"); | |
1175 break; | |
1176 case 32: | |
1177 if (sample_format == 3) | |
1178 if (data_ov.is_single_type () || data_ov.is_double_type ()) | |
1179 write_strip_or_tile<FloatNDArray> (tif, strip_tile_no, | |
1180 data_ov.float_array_value (), | |
1181 image_data); | |
1182 else | |
1183 error ("Only single and double data are allowed for floating-point images"); | |
1184 else | |
1185 if (data_ov.is_uint32_type ()) | |
1186 write_strip_or_tile<uint32NDArray> (tif, strip_tile_no, | |
1187 data_ov.uint32_array_value (), | |
1188 image_data); | |
1189 else | |
1190 error ("Only uint32 data is allowed for uint images with bit depth of 32"); | |
1191 break; | |
1192 case 64: | |
1193 if (sample_format == 3) | |
1194 if (data_ov.is_single_type () || data_ov.is_double_type ()) | |
1195 write_strip_or_tile<NDArray> (tif, strip_tile_no, | |
1196 data_ov.array_value (), | |
1197 image_data); | |
1198 else | |
1199 error ("Only single and double data are allowed for floating-point images"); | |
1200 else | |
1201 if (data_ov.is_uint64_type ()) | |
1202 write_strip_or_tile<uint64NDArray> (tif, strip_tile_no, | |
1203 data_ov.uint64_array_value (), | |
1204 image_data); | |
1205 else | |
1206 error ("Only uint64 data is allowed for uint images with bit depth of 64"); | |
1207 break; | |
1208 default: | |
1016 error ("Unsupported bit depth"); | 1209 error ("Unsupported bit depth"); |
1017 } | 1210 } |
1018 } | 1211 } |
1019 | 1212 |
1020 #endif | 1213 #endif |
1255 "Write an encoded strip to the image") | 1448 "Write an encoded strip to the image") |
1256 { | 1449 { |
1257 #if defined (HAVE_TIFF) | 1450 #if defined (HAVE_TIFF) |
1258 int nargin = args.length (); | 1451 int nargin = args.length (); |
1259 | 1452 |
1453 // TODO(maged): add support for YCbCr data | |
1260 if (nargin < 3) | 1454 if (nargin < 3) |
1261 error ("Too few arguments provided\n"); | 1455 error ("Too few arguments provided\n"); |
1262 | 1456 |
1263 TIFF *tif = (TIFF *)(args (0).uint64_value ()); | 1457 TIFF *tif = (TIFF *)(args (0).uint64_value ()); |
1264 | 1458 |
1274 | 1468 |
1275 uint32_t strip_no = args (1).uint32_scalar_value (); | 1469 uint32_t strip_no = args (1).uint32_scalar_value (); |
1276 if (strip_no < 1 || strip_no > TIFFNumberOfStrips (tif)) | 1470 if (strip_no < 1 || strip_no > TIFFNumberOfStrips (tif)) |
1277 error ("Strip number out of range"); | 1471 error ("Strip number out of range"); |
1278 | 1472 |
1279 // SampleFormat tag is not a required field and has a default value of 1 | 1473 process_strip_or_tile (tif, strip_no, args(2), &image_data); |
1280 // So we need to use TIFFGetFieldDefaulted in case it is not present in | |
1281 // the file | |
1282 uint16_t sample_format; | |
1283 if (! TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLEFORMAT, &sample_format)) | |
1284 error ("Failed to obtain a value for sample format"); | |
1285 | |
1286 // TODO(maged): add support for signed integer images | |
1287 if (sample_format == 3) | |
1288 { | |
1289 if (image_data.bits_per_sample != 32 && image_data.bits_per_sample != 64) | |
1290 error ("Floating point images are only supported for bit depths of 32 and 64"); | |
1291 } | |
1292 // The standard specifies that a SampleFormat of 4 should be treated | |
1293 // the same as 1 (unsigned integer) | |
1294 else if (sample_format != 1 && sample_format != 4) | |
1295 error ("Unsupported sample format"); | |
1296 | |
1297 switch (image_data.bits_per_sample) | |
1298 { | |
1299 case 1: | |
1300 // We need to check for both scalar and matrix types to handle single | |
1301 // element strip | |
1302 if (args(2).is_bool_scalar () || args(2).is_bool_matrix ()) | |
1303 write_strip<boolNDArray> (tif, strip_no, | |
1304 args(2).bool_array_value (), &image_data); | |
1305 else | |
1306 error ("Expected logical matrix for BiLevel image"); | |
1307 break; | |
1308 case 8: | |
1309 if (args(2).is_uint8_type ()) | |
1310 write_strip<uint8NDArray> (tif, strip_no, | |
1311 args(2).uint8_array_value (), | |
1312 &image_data); | |
1313 else | |
1314 error ("Only uint8 data is allowed for uint images with bit depth of 8"); | |
1315 break; | |
1316 case 16: | |
1317 if (args(2).is_uint16_type ()) | |
1318 write_strip<uint16NDArray> (tif, strip_no, | |
1319 args(2).uint16_array_value (), | |
1320 &image_data); | |
1321 else | |
1322 error ("Only uint16 data is allowed for uint images with bit depth of 16"); | |
1323 break; | |
1324 case 32: | |
1325 if (sample_format == 3) | |
1326 if (args(2).is_single_type () || args(2).is_double_type ()) | |
1327 write_strip<FloatNDArray> (tif, strip_no, | |
1328 args(2).float_array_value (), | |
1329 &image_data); | |
1330 else | |
1331 error ("Only single and double data are allowed for floating-point images"); | |
1332 else | |
1333 if (args(2).is_uint32_type ()) | |
1334 write_strip<uint32NDArray> (tif, strip_no, | |
1335 args(2).uint32_array_value (), | |
1336 &image_data); | |
1337 else | |
1338 error ("Only uint32 data is allowed for uint images with bit depth of 32"); | |
1339 break; | |
1340 case 64: | |
1341 if (sample_format == 3) | |
1342 if (args(2).is_single_type () || args(2).is_double_type ()) | |
1343 write_strip<NDArray> (tif, strip_no, | |
1344 args(2).array_value (), | |
1345 &image_data); | |
1346 else | |
1347 error ("Only single and double data are allowed for floating-point images"); | |
1348 else | |
1349 if (args(2).is_uint64_type ()) | |
1350 write_strip<uint64NDArray> (tif, strip_no, | |
1351 args(2).uint64_array_value (), | |
1352 &image_data); | |
1353 else | |
1354 error ("Only uint64 data is allowed for uint images with bit depth of 64"); | |
1355 break; | |
1356 default: | |
1357 error ("Unsupported bit depth"); | |
1358 } | |
1359 | 1474 |
1360 return octave_value_list (); | 1475 return octave_value_list (); |
1361 #else | 1476 #else |
1362 err_disabled_feature ("writeEncodedStrip", "Tiff"); | 1477 err_disabled_feature ("writeEncodedStrip", "Tiff"); |
1363 #endif | 1478 #endif |
1364 } | 1479 } |
1480 | |
1481 DEFUN_DLD (__tiff_write_encoded_tile__, args, , | |
1482 "Write an encoded tile to the image") | |
1483 { | |
1484 #if defined (HAVE_TIFF) | |
1485 int nargin = args.length (); | |
1486 | |
1487 // TODO(maged): add support for YCbCr data | |
1488 if (nargin < 3) | |
1489 error ("Too few arguments provided\n"); | |
1490 | |
1491 TIFF *tif = (TIFF *)(args (0).uint64_value ()); | |
1492 | |
1493 // TODO(maged): check on windows | |
1494 if (TIFFGetMode (tif) == O_RDONLY) | |
1495 error ("Can't write data to a file opened in read-only mode"); | |
1496 | |
1497 // Obtain all necessary tags | |
1498 tiff_image_data image_data (tif); | |
1499 | |
1500 if (! image_data.is_tiled) | |
1501 error ("Can't write tiles to a stripped image"); | |
1502 | |
1503 uint32_t tile_no = args (1).uint32_scalar_value (); | |
1504 if (tile_no < 1 || tile_no > TIFFNumberOfTiles (tif)) | |
1505 error ("Tile number out of range"); | |
1506 | |
1507 process_strip_or_tile (tif, tile_no, args(2), &image_data); | |
1508 | |
1509 return octave_value_list (); | |
1510 #else | |
1511 err_disabled_feature ("writeEncodedTile", "Tiff"); | |
1512 #endif | |
1513 } | |
1365 } | 1514 } |