@@ -149,8 +149,7 @@ class TIFFOutput final : public ImageOutput {
149149 void fix_bitdepth (void * data, int nvals);
150150
151151 // Add a parameter to the output
152- bool put_parameter (const std::string& name, TypeDesc type,
153- const void * data);
152+ bool put_parameter (const ParamValue& metadata);
154153 bool write_exif_data ();
155154
156155 // Make our best guess about whether the spec is describing data that
@@ -921,10 +920,8 @@ TIFFOutput::open(const std::string& name, const ImageSpec& userspec,
921920 }
922921
923922 // Deal with all other params
924- for (size_t p = 0 ; p < m_spec.extra_attribs .size (); ++p)
925- put_parameter (m_spec.extra_attribs [p].name ().string (),
926- m_spec.extra_attribs [p].type (),
927- m_spec.extra_attribs [p].data ());
923+ for (const auto & p : m_spec.extra_attribs )
924+ put_parameter (p);
928925
929926 if (m_spec.get_int_attribute (" tiff:write_iptc" )) {
930927 // Enable IPTC block writing only if "tiff_write_iptc" hint is explicitly
@@ -956,120 +953,125 @@ TIFFOutput::open(const std::string& name, const ImageSpec& userspec,
956953
957954
958955
956+ inline int
957+ resunit_to_code (string_view s)
958+ {
959+ if (Strutil::iequals (s, " none" ))
960+ return RESUNIT_NONE;
961+ else if (Strutil::iequals (s, " in" ) || Strutil::iequals (s, " inch" ))
962+ return RESUNIT_INCH;
963+ else if (Strutil::iequals (s, " cm" ))
964+ return RESUNIT_CENTIMETER;
965+ return 0 ;
966+ }
967+
968+
969+
959970bool
960- TIFFOutput::put_parameter (const std::string& name, TypeDesc type,
961- const void * data)
971+ TIFFOutput::put_parameter (const ParamValue& param)
962972{
963- if (!data || (type == TypeString && *(char **)data == nullptr )) {
973+ ustring name = param.uname ();
974+ TypeDesc type = param.type ();
975+ if (!param.data ()
976+ || (type == TypeString && *(char **)param.data () == nullptr )) {
964977 // we got a null pointer, don't set the field
965978 return false ;
966979 }
967- if (Strutil::iequals (name, " Artist" ) && type == TypeDesc::STRING ) {
968- TIFFSetField (m_tif, TIFFTAG_ARTIST, *( char **)data );
980+ if (Strutil::iequals (name, " Artist" )) {
981+ TIFFSetField (m_tif, TIFFTAG_ARTIST, param. get_string (). c_str () );
969982 return true ;
970983 }
971- if (Strutil::iequals (name, " Copyright" ) && type == TypeDesc::STRING ) {
972- TIFFSetField (m_tif, TIFFTAG_COPYRIGHT, *( char **)data );
984+ if (Strutil::iequals (name, " Copyright" )) {
985+ TIFFSetField (m_tif, TIFFTAG_COPYRIGHT, param. get_string (). c_str () );
973986 return true ;
974987 }
975- if (Strutil::iequals (name, " DateTime" ) && type == TypeDesc::STRING ) {
976- TIFFSetField (m_tif, TIFFTAG_DATETIME, *( char **)data );
988+ if (Strutil::iequals (name, " DateTime" ) && type == TypeString ) {
989+ TIFFSetField (m_tif, TIFFTAG_DATETIME, param. get_string (). c_str () );
977990 return true ;
978991 }
979- if ((Strutil::iequals (name, " name" )
980- || Strutil::iequals (name, " DocumentName" ))
981- && type == TypeDesc::STRING) {
982- TIFFSetField (m_tif, TIFFTAG_DOCUMENTNAME, *(char **)data);
992+ if (Strutil::iequals (name, " name" )
993+ || Strutil::iequals (name, " DocumentName" )) {
994+ TIFFSetField (m_tif, TIFFTAG_DOCUMENTNAME, param.get_string ().c_str ());
983995 return true ;
984996 }
985- if (Strutil::iequals (name, " fovcot" ) && type == TypeDesc::FLOAT) {
986- double d = *(float *)data;
987- TIFFSetField (m_tif, TIFFTAG_PIXAR_FOVCOT, d);
997+ if (Strutil::iequals (name, " fovcot" ) && type == TypeFloat) {
998+ TIFFSetField (m_tif, TIFFTAG_PIXAR_FOVCOT, param.get_float ());
988999 return true ;
9891000 }
990- if ((Strutil::iequals (name, " host" )
991- || Strutil::iequals (name, " HostComputer" ))
992- && type == TypeDesc::STRING) {
993- TIFFSetField (m_tif, TIFFTAG_HOSTCOMPUTER, *(char **)data);
1001+ if (Strutil::iequals (name, " host" )
1002+ || Strutil::iequals (name, " HostComputer" )) {
1003+ TIFFSetField (m_tif, TIFFTAG_HOSTCOMPUTER, param.get_string ().c_str ());
9941004 return true ;
9951005 }
9961006 if ((Strutil::iequals (name, " description" )
9971007 || Strutil::iequals (name, " ImageDescription" ))
998- && type == TypeDesc::STRING) {
999- TIFFSetField (m_tif, TIFFTAG_IMAGEDESCRIPTION, *(char **)data);
1008+ && type == TypeString) {
1009+ TIFFSetField (m_tif, TIFFTAG_IMAGEDESCRIPTION,
1010+ param.get_string ().c_str ());
10001011 return true ;
10011012 }
1002- if (Strutil::iequals (name, " tiff:Predictor" ) && type == TypeDesc::INT ) {
1003- m_predictor = *( int *)data ;
1013+ if (Strutil::iequals (name, " tiff:Predictor" )) {
1014+ m_predictor = param. get_int () ;
10041015 TIFFSetField (m_tif, TIFFTAG_PREDICTOR, m_predictor);
10051016 return true ;
10061017 }
1007- if (Strutil::iequals (name, " ResolutionUnit" ) && type == TypeDesc::STRING) {
1008- const char * s = *(char **)data;
1009- bool ok = true ;
1010- if (Strutil::iequals (s, " none" ))
1011- TIFFSetField (m_tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_NONE);
1012- else if (Strutil::iequals (s, " in" ) || Strutil::iequals (s, " inch" ))
1013- TIFFSetField (m_tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
1014- else if (Strutil::iequals (s, " cm" ))
1015- TIFFSetField (m_tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_CENTIMETER);
1016- else
1017- ok = false ;
1018- return ok;
1018+ if (Strutil::iequals (name, " ResolutionUnit" ) && type == TypeString) {
1019+ if (int r = resunit_to_code (param.get_string ())) {
1020+ TIFFSetField (m_tif, TIFFTAG_RESOLUTIONUNIT, r);
1021+ return true ;
1022+ }
1023+ return false ;
10191024 }
10201025 if (Strutil::iequals (name, " tiff:RowsPerStrip" )
10211026 && !m_spec.tile_width /* don't set rps for tiled files */
10221027 && m_planarconfig == PLANARCONFIG_CONTIG /* only for contig */ ) {
1023- if (type == TypeDesc::INT) {
1024- m_rowsperstrip = *(int *)data;
1025- } else if (type == TypeDesc::STRING) {
1026- // Back-compatibility with Entropy and PRMan
1027- m_rowsperstrip = Strutil::stoi (*(char **)data);
1028- } else {
1028+ int rps = param.get_int ();
1029+ if (rps <= 0 )
10291030 return false ;
1030- }
1031- m_rowsperstrip = clamp (m_rowsperstrip, 1 , m_spec.height );
1031+ m_rowsperstrip = clamp (rps, 1 , m_spec.height );
10321032 TIFFSetField (m_tif, TIFFTAG_ROWSPERSTRIP, m_rowsperstrip);
10331033 return true ;
10341034 }
1035- if (Strutil::iequals (name, " Make" ) && type == TypeDesc::STRING ) {
1036- TIFFSetField (m_tif, TIFFTAG_MAKE, *( char **)data );
1035+ if (Strutil::iequals (name, " Make" )) {
1036+ TIFFSetField (m_tif, TIFFTAG_MAKE, param. get_string (). c_str () );
10371037 return true ;
10381038 }
1039- if (Strutil::iequals (name, " Model" ) && type == TypeDesc::STRING ) {
1040- TIFFSetField (m_tif, TIFFTAG_MODEL, *( char **)data );
1039+ if (Strutil::iequals (name, " Model" )) {
1040+ TIFFSetField (m_tif, TIFFTAG_MODEL, param. get_string (). c_str () );
10411041 return true ;
10421042 }
1043- if (Strutil::iequals (name, " Software" ) && type == TypeDesc::STRING ) {
1044- TIFFSetField (m_tif, TIFFTAG_SOFTWARE, *( char **)data );
1043+ if (Strutil::iequals (name, " Software" )) {
1044+ TIFFSetField (m_tif, TIFFTAG_SOFTWARE, param. get_string (). c_str () );
10451045 return true ;
10461046 }
1047- if (Strutil::iequals (name, " tiff:SubFileType" ) && type == TypeDesc::INT ) {
1048- TIFFSetField (m_tif, TIFFTAG_SUBFILETYPE, *( int *)data );
1047+ if (Strutil::iequals (name, " tiff:SubFileType" )) {
1048+ TIFFSetField (m_tif, TIFFTAG_SUBFILETYPE, param. get_int () );
10491049 return true ;
10501050 }
1051- if (Strutil::iequals (name, " textureformat" ) && type == TypeDesc::STRING) {
1052- TIFFSetField (m_tif, TIFFTAG_PIXAR_TEXTUREFORMAT, *(char **)data);
1051+ if (Strutil::iequals (name, " textureformat" )) {
1052+ TIFFSetField (m_tif, TIFFTAG_PIXAR_TEXTUREFORMAT,
1053+ param.get_string ().c_str ());
10531054 return true ;
10541055 }
1055- if (Strutil::iequals (name, " wrapmodes" ) && type == TypeDesc::STRING) {
1056- TIFFSetField (m_tif, TIFFTAG_PIXAR_WRAPMODES, *(char **)data);
1056+ if (Strutil::iequals (name, " wrapmodes" )) {
1057+ TIFFSetField (m_tif, TIFFTAG_PIXAR_WRAPMODES,
1058+ param.get_string ().c_str ());
10571059 return true ;
10581060 }
10591061 if (Strutil::iequals (name, " worldtocamera" ) && type == TypeMatrix) {
1060- TIFFSetField (m_tif, TIFFTAG_PIXAR_MATRIX_WORLDTOCAMERA, data);
1062+ TIFFSetField (m_tif, TIFFTAG_PIXAR_MATRIX_WORLDTOCAMERA, param. data () );
10611063 return true ;
10621064 }
10631065 if (Strutil::iequals (name, " worldtoscreen" ) && type == TypeMatrix) {
1064- TIFFSetField (m_tif, TIFFTAG_PIXAR_MATRIX_WORLDTOSCREEN, data);
1066+ TIFFSetField (m_tif, TIFFTAG_PIXAR_MATRIX_WORLDTOSCREEN, param. data () );
10651067 return true ;
10661068 }
1067- if (Strutil::iequals (name, " XResolution" ) && type == TypeDesc::FLOAT ) {
1068- TIFFSetField (m_tif, TIFFTAG_XRESOLUTION, *( float *)data );
1069+ if (Strutil::iequals (name, " XResolution" )) {
1070+ TIFFSetField (m_tif, TIFFTAG_XRESOLUTION, param. get_float () );
10691071 return true ;
10701072 }
1071- if (Strutil::iequals (name, " YResolution" ) && type == TypeDesc::FLOAT ) {
1072- TIFFSetField (m_tif, TIFFTAG_YRESOLUTION, *( float *)data );
1073+ if (Strutil::iequals (name, " YResolution" )) {
1074+ TIFFSetField (m_tif, TIFFTAG_YRESOLUTION, param. get_float () );
10731075 return true ;
10741076 }
10751077 return false ;
@@ -1134,27 +1136,82 @@ TIFFOutput::write_exif_data()
11341136 int tag, tifftype, count;
11351137 if (exif_tag_lookup (p.name (), tag, tifftype, count)
11361138 && tifftype != TIFF_NOTYPE) {
1139+ bool ok = false ;
1140+ bool handled = false ;
1141+ // Some special cases first
11371142 if (tag == EXIF_SECURITYCLASSIFICATION || tag == EXIF_IMAGEHISTORY
1138- || tag == EXIF_PHOTOGRAPHICSENSITIVITY)
1143+ || tag == EXIF_PHOTOGRAPHICSENSITIVITY) {
11391144 continue ; // libtiff doesn't understand these
1140- bool ok = false ;
1141- if (tifftype == TIFF_ASCII) {
1142- ok = TIFFSetField (m_tif, tag, *(char **)p.data ());
1143- } else if ((tifftype == TIFF_SHORT || tifftype == TIFF_LONG)
1144- && p.type () == TypeDesc::SHORT && count == 1 ) {
1145- ok = TIFFSetField (m_tif, tag, (int )*(short *)p.data ());
1146- } else if ((tifftype == TIFF_SHORT || tifftype == TIFF_LONG)
1147- && p.type () == TypeDesc::INT && count == 1 ) {
1148- ok = TIFFSetField (m_tif, tag, *(int *)p.data ());
1149- } else if ((tifftype == TIFF_RATIONAL || tifftype == TIFF_SRATIONAL)
1150- && p.type () == TypeDesc::FLOAT && count == 1 ) {
1151- ok = TIFFSetField (m_tif, tag, *(float *)p.data ());
1145+ }
1146+ if (tag == TIFFTAG_RESOLUTIONUNIT && p.type () == TypeString) {
1147+ // OIIO stores resolution unit as a string, but libtiff wants
1148+ // it as a short code, so we have to convert.
1149+ if (int r = resunit_to_code (p.get_string ())) {
1150+ ok = TIFFSetField (m_tif, TIFFTAG_RESOLUTIONUNIT, r);
1151+ }
1152+ handled = true ;
1153+ } else if (tag == EXIF_EXIFVERSION || tag == EXIF_FLASHPIXVERSION) {
1154+ if (p.type () == TypeString) {
1155+ // These tags are a 4-byte array of chars, but we
1156+ // allow users to set it as a string. Convert it if needed.
1157+ std::string version = p.get_string ();
1158+ if (version.size () >= 4 ) {
1159+ ok = TIFFSetField (m_tif, tag, version.c_str ());
1160+ }
1161+ handled = true ;
1162+ } else if (p.type () == TypeInt) {
1163+ std::string s = Strutil::fmt::format (" {:04}" , p.get_int ());
1164+ if (s.size () == 4 )
1165+ ok = TIFFSetField (m_tif, tag, s.c_str ());
1166+ handled = true ;
1167+ }
1168+ }
1169+ // General cases...
1170+ else if (tifftype == TIFF_ASCII) {
1171+ ok = TIFFSetField (m_tif, tag, p.get_string ().c_str ());
1172+ handled = true ;
1173+ } else if (tifftype == TIFF_SHORT || tifftype == TIFF_SSHORT
1174+ || tifftype == TIFF_LONG || tifftype == TIFF_SLONG) {
1175+ if ((p.type () == TypeInt16 || p.type () == TypeInt32
1176+ || p.type () == TypeUInt16 || p.type () == TypeUInt32)
1177+ && count == 1 ) {
1178+ // Passing our kinda-int as TIFF kinda-int
1179+ ok = TIFFSetField (m_tif, tag, p.get_int ());
1180+ handled = true ;
1181+ } else if (p.type () == TypeString && count == 1 ) {
1182+ // Passing our string as TIFF kinda-int -- convert as long
1183+ // as the string looks like an int.
1184+ std::string s = p.get_string ();
1185+ if (Strutil::string_is_int (s)) {
1186+ int val = Strutil::stoi (s);
1187+ ok = TIFFSetField (m_tif, tag, val);
1188+ handled = true ;
1189+ }
1190+ }
11521191 } else if ((tifftype == TIFF_RATIONAL || tifftype == TIFF_SRATIONAL)
1153- && p.type () == TypeDesc::DOUBLE && count == 1 ) {
1154- ok = TIFFSetField (m_tif, tag, *(double *)p.data ());
1192+ && (p.type () == TypeFloat || p.type () == TypeDesc::DOUBLE
1193+ || p.type () == TypeUInt16 || p.type () == TypeUInt32
1194+ || p.type () == TypeInt16 || p.type () == TypeInt32
1195+ || p.type () == TypeRational
1196+ || p.type () == TypeURational)
1197+ && count == 1 ) {
1198+ // If the tag is a rational, there are a number of types we
1199+ // can force into that form by converting to and then passing
1200+ // a float.
1201+ ok = TIFFSetField (m_tif, tag, p.get_float ());
1202+ handled = true ;
1203+ }
1204+ if (!handled) {
1205+ # ifndef NDEBUG
1206+ print (" Unhandled EXIF {} ({}) / tag {} tifftype {} count {}\n " ,
1207+ p.name (), p.type (), tag, tifftype, count);
1208+ # endif
11551209 }
1210+ // NOTE: We are not handling arrays of values, just scalars.
11561211 if (!ok) {
1157- // std::cout << "Unhandled EXIF " << p.name() << " " << p.type() << "\n";
1212+ // print(
1213+ // "Error handling EXIF {} ({}) / tag {} tifftype {} count {}\n",
1214+ // p.name(), p.type(), tag, tifftype, count);
11581215 }
11591216 }
11601217 }
0 commit comments