@@ -265,25 +265,37 @@ class TIFFInput final : public ImageInput {
265265 }
266266
267267 OIIO_NODISCARD
268- TypeDesc tiffgetfieldtype (int tag)
268+ TypeDesc tiffgetfieldtype (int tag, OIIO_MAYBE_UNUSED string_view name = " " ,
269+ int * readcount_ = nullptr ,
270+ int * passcount_ = nullptr ,
271+ TIFFDataType* tiffdatatype_ = nullptr )
269272 {
270273 auto field = find_field (tag);
271274 if (!field)
272275 return TypeUnknown;
273- TIFFDataType tiffdatatype = TIFFFieldDataType (field);
274- int passcount = TIFFFieldPassCount (field);
275- int readcount = TIFFFieldReadCount (field);
276- if (!passcount && readcount > 0 )
277- return tiff_datatype_to_typedesc (tiffdatatype, readcount);
278- return TypeUnknown;
276+ auto tiffdatatype = TIFFFieldDataType (field);
277+ int readcount = TIFFFieldReadCount (field);
278+ int passcount = TIFFFieldPassCount (field);
279+ TypeDesc type = tiff_datatype_to_typedesc (tiffdatatype,
280+ std::max (1 , readcount));
281+ if (readcount == TIFF_SPP)
282+ type.arraylen = m_spec.nchannels ;
283+ if (readcount_)
284+ *readcount_ = readcount;
285+ if (passcount_)
286+ *passcount_ = passcount;
287+ if (tiffdatatype_)
288+ *tiffdatatype_ = tiffdatatype;
289+ return type;
279290 }
280291
281292 OIIO_NODISCARD
282293 bool safe_tiffgetfield (string_view name OIIO_MAYBE_UNUSED, int tag,
283294 TypeDesc expected, void * dest,
284- const uint32_t * count = nullptr )
295+ uint32_t * count = nullptr )
285296 {
286- TypeDesc type = tiffgetfieldtype (tag);
297+ int readcount = 0 , passcount = 0 ;
298+ TypeDesc type = tiffgetfieldtype (tag, name, &readcount, &passcount);
287299 // Caller expects a specific type and the tag doesn't match? Punt.
288300 if (expected != TypeUnknown && !equivalent (expected, type))
289301 return false ;
@@ -292,8 +304,8 @@ class TIFFInput final : public ImageInput {
292304 return false ;
293305
294306 // TIFFDataType tiffdatatype = TIFFFieldDataType(field);
295- int passcount = TIFFFieldPassCount (field);
296- int readcount = TIFFFieldReadCount (field);
307+ passcount = TIFFFieldPassCount (field);
308+ readcount = TIFFFieldReadCount (field);
297309 if (!passcount && readcount > 0 ) {
298310 return TIFFGetField (m_tif, tag, dest);
299311 } else if (passcount && readcount <= 0 ) {
@@ -370,31 +382,120 @@ class TIFFInput final : public ImageInput {
370382 m_spec.attribute (name, TypeMatrix, f);
371383 }
372384
373- // Get a float tiff tag field and put it into extra_params
374- void get_float_attribute (string_view name, int tag)
375- {
376- float f[16 ];
377- if (safe_tiffgetfield (name, tag, TypeUnknown, f))
378- m_spec.attribute (name, f[0 ]);
379- }
380-
381- // Get an int tiff tag field and put it into extra_params
382- void get_int_attribute (string_view name, int tag)
383- {
384- int i = 0 ;
385- if (safe_tiffgetfield (name, tag, TypeUnknown, &i))
386- m_spec.attribute (name, i);
387- }
388-
389- // Get an int tiff tag field and put it into extra_params
390- void get_short_attribute (string_view name, int tag)
385+ // Get a tiff tag field whose C type will match T, and put it into
386+ // extra_params. If it must be a very specific TypeDesc, it can be
387+ // supplied as expected_type. The hard part is that libtiff is horribly
388+ // complicated because some tags have variable lengths, and there are even
389+ // several different parameter passing conventions to the incredibly
390+ // unsafe TIFFGetField function.
391+ template <typename T>
392+ void get_attribute_from_tag (string_view name, int tag,
393+ TypeDesc expected_type = TypeUnknown)
391394 {
392- // Make room for two shorts, in case the tag is not the type we
393- // expect, and libtiff writes a long instead.
394- unsigned short s[2 ] = { 0 , 0 };
395- if (safe_tiffgetfield (name, tag, TypeUInt16, &s)) {
396- int i = s[0 ];
397- m_spec.attribute (name, i);
395+ int readcount = 0 , passcount = 0 ;
396+ TIFFDataType tiffdatatype = TIFF_NOTYPE;
397+ TypeDesc oiiotype = tiffgetfieldtype (tag, name, &readcount, &passcount,
398+ &tiffdatatype);
399+ if (oiiotype == TypeUnknown)
400+ return ;
401+ OIIO_ASSERT ((readcount > 0 && passcount == 0 )
402+ || (readcount == 0 && passcount == 0 )
403+ || (readcount < 0 && passcount > 0 ));
404+ oiiotype.basetype = BaseTypeFromC_v<T>;
405+ if (expected_type != TypeUnknown) {
406+ // If a specific OIIO type is demanded, skip if what we got
407+ // doesn't really match in base type and total number of elements.
408+ // But allow a ushort tag to make a int attribute.
409+ if (oiiotype.basetype != expected_type.basetype )
410+ return ;
411+ if (oiiotype.basevalues () != expected_type.basevalues ())
412+ return ;
413+ oiiotype = expected_type;
414+ } else {
415+ oiiotype.aggregate = TypeDesc::SCALAR;
416+ oiiotype.vecsemantics = TypeDesc::NOSEMANTICS;
417+ }
418+ if (readcount > 0 && passcount == 0 ) {
419+ // We know how many are passed, so we pass TIFFGetField a pointer
420+ // to where we want the (already sized) data to go.
421+ OIIO_ASSERT (size_t (readcount) == oiiotype.basevalues ());
422+ if constexpr (std::is_same_v<T, uint16_t >) {
423+ // Special case: we save a single tiff ushort as an int.
424+ if (tiffdatatype == TIFF_SHORT && readcount == 1 ) {
425+ T val;
426+ if (TIFFGetField (m_tif, tag, &val))
427+ m_spec.attribute (name, int (val));
428+ return ;
429+ }
430+ // Fun, there are some quirky special cases in libtiff where
431+ // certain uint16[2] fields are retrieved with two pointers!
432+ if (tiffdatatype == TIFF_SHORT && readcount == 2
433+ && (tag == TIFFTAG_PAGENUMBER
434+ || tag == TIFFTAG_HALFTONEHINTS
435+ || tag == TIFFTAG_DOTRANGE
436+ || tag == TIFFTAG_YCBCRSUBSAMPLING)) {
437+ T vals[2 ] = { 0 , 0 };
438+ if (TIFFGetField (m_tif, tag, &(vals[0 ]), &(vals[1 ]))) {
439+ constexpr TypeDesc TypeUInt16_2 (TypeDesc::UINT16, 2 );
440+ m_spec.attribute (name, TypeUInt16_2, vals);
441+ }
442+ return ;
443+ }
444+ }
445+ if (readcount > 1 ) {
446+ // If there are multiple values, we pass a T** and it puts the
447+ // address of the real data in our pointer. There are very few
448+ // TIFF tags like this.
449+ const T* ptr = nullptr ;
450+ if (TIFFGetField (m_tif, tag, &ptr) && ptr)
451+ m_spec.attribute (name, oiiotype, ptr);
452+ return ;
453+ } else {
454+ // If there is just one value, we pass a pointer to our data,
455+ // and libtiff fills it in.
456+ T val;
457+ if (TIFFGetField (m_tif, tag, &val))
458+ m_spec.attribute (name, oiiotype, &val);
459+ }
460+ return ;
461+ } else if (readcount == TIFF_VARIABLE) {
462+ // Must pass a uin16_t to find out how many, and we get a data
463+ // pointer instead of providing a pointer.
464+ uint16_t count = 0 ;
465+ const T* vals = nullptr ;
466+ if (TIFFGetField (m_tif, tag, &count, &vals) && vals && count) {
467+ oiiotype.unarray ();
468+ oiiotype.arraylen = count;
469+ m_spec.attribute (name, oiiotype, make_span (vals, count));
470+ }
471+ return ;
472+ } else if (readcount == TIFF_VARIABLE2) {
473+ // Must pass a uin32t_t to find out how many, and we get a data
474+ // pointer instead of providing a pointer.
475+ uint32_t count = 0 ;
476+ const T* vals = nullptr ;
477+ if (TIFFGetField (m_tif, tag, &count, &vals) && vals && count) {
478+ if (count > 1 )
479+ oiiotype.arraylen = count;
480+ else
481+ oiiotype.unarray ();
482+ m_spec.attribute (name, oiiotype, make_span (vals, count));
483+ }
484+ return ;
485+ } else if (readcount == TIFF_SPP) {
486+ // The number of values is equal to the number of channels.
487+ uint32_t count = static_cast <uint32_t >(m_spec.nchannels );
488+ const T* vals = nullptr ;
489+ if (TIFFGetField (m_tif, tag, &vals) && vals) {
490+ if (count > 1 )
491+ oiiotype.arraylen = count;
492+ else
493+ oiiotype.unarray ();
494+ m_spec.attribute (name, oiiotype, make_span (vals, count));
495+ }
496+ return ;
497+ } else {
498+ // print("UNHANDLED CASE!\n");
398499 }
399500 }
400501
@@ -416,14 +517,14 @@ class TIFFInput final : public ImageInput {
416517 get_string_attribute (oiioname, tifftag);
417518 return ;
418519 } else if (tifftype == TIFF_SHORT) {
419- get_short_attribute (oiioname, tifftag);
520+ get_attribute_from_tag< uint16_t > (oiioname, tifftag);
420521 return ;
421522 } else if (tifftype == TIFF_LONG) {
422- get_int_attribute (oiioname, tifftag);
523+ get_attribute_from_tag< int > (oiioname, tifftag);
423524 return ;
424525 } else if (tifftype == TIFF_RATIONAL || tifftype == TIFF_SRATIONAL
425526 || tifftype == TIFF_FLOAT || tifftype == TIFF_DOUBLE) {
426- get_float_attribute (oiioname, tifftag);
527+ get_attribute_from_tag< float > (oiioname, tifftag);
427528 return ;
428529 }
429530 // special cases follow
@@ -1295,11 +1396,13 @@ TIFFInput::readspec(bool read_meta)
12951396 if (xdensity && ydensity)
12961397 m_spec.attribute (" PixelAspectRatio" , ydensity / xdensity);
12971398
1298- get_matrix_attribute (" worldtocamera" , TIFFTAG_PIXAR_MATRIX_WORLDTOCAMERA);
1299- get_matrix_attribute (" worldtoscreen" , TIFFTAG_PIXAR_MATRIX_WORLDTOSCREEN);
1300- get_int_attribute (" tiff:subfiletype" , TIFFTAG_SUBFILETYPE);
1301- // FIXME -- should subfiletype be "conventionized" and used for all
1302- // plugins uniformly?
1399+ get_attribute_from_tag<float >(" worldtocamera" ,
1400+ TIFFTAG_PIXAR_MATRIX_WORLDTOCAMERA,
1401+ TypeMatrix);
1402+ get_attribute_from_tag<float >(" worldtoscreen" ,
1403+ TIFFTAG_PIXAR_MATRIX_WORLDTOSCREEN,
1404+ TypeMatrix);
1405+ get_attribute_from_tag<int >(" tiff:subfiletype" , TIFFTAG_SUBFILETYPE);
13031406
13041407 // Special names for shadow maps
13051408 char * s = NULL ;
@@ -1357,6 +1460,21 @@ TIFFInput::readspec(bool read_meta)
13571460 TIFFSetDirectory (m_tif, m_actual_subimage);
13581461 }
13591462
1463+ #if OIIO_TIFFLIB_VERSION >= 40200
1464+ // Search for an GPS IFD in the TIFF file, and if found, rummage
1465+ // around for GPS fields.
1466+ toff_t gpsoffset = 0 ;
1467+ if (TIFFGetField (m_tif, TIFFTAG_GPSIFD, &gpsoffset)) {
1468+ if (TIFFReadEXIFDirectory (m_tif, gpsoffset)) {
1469+ for (const auto & tag : tag_table (" GPS" ))
1470+ find_tag (tag.tifftag , tag.tifftype , tag.name );
1471+ }
1472+ // TIFFReadEXIFDirectory seems to do something to the internal state
1473+ // that requires a TIFFSetDirectory to set things straight again.
1474+ TIFFSetDirectory (m_tif, m_actual_subimage);
1475+ }
1476+ #endif
1477+
13601478 // Search for IPTC metadata in IIM form -- but older versions of
13611479 // libtiff botch the size, so ignore it for very old libtiff.
13621480 int iptcsize = 0 ;
0 commit comments