@@ -101,6 +101,7 @@ enum exif_tiff_tag {
101101 ETIFF_RESOLUTION_UNIT , ///< Unit of X and Y resolution (mandatory)
102102 ETIFF_SOFTWARE , ///< Software used (optional)
103103 ETIFF_DATE_TIME , ///< File change date and time (recommeneded)
104+ ETIFF_WHITE_POINT , ///< White Point
104105 ETIFF_YCBCR_POSITIONING , ///< Y and C positioning (mandatory)
105106 ETIFF_EXIF_IFD_POINTER , ///< EXIF tag (mandatory)
106107 // 0th SubIFD Exif Private Tags
@@ -124,6 +125,7 @@ const struct exif_tiff_tag_info_t {
124125 [ETIFF_RESOLUTION_UNIT ] = {0x128 , ET_SHORT , 1 , "ResolutionUnit" },
125126 [ETIFF_SOFTWARE ] = {0x131 , ET_ASCII , 0 , "Sofware" },
126127 [ETIFF_DATE_TIME ] = {0x132 , ET_ASCII , 20 , "DateTime" },
128+ [ETIFF_WHITE_POINT ] = {0x13E , ET_RATIONAL , 2 , "WhitePoint" },
127129 [ETIFF_YCBCR_POSITIONING ] = {0x213 , ET_SHORT , 1 , "YCbCrPositioning" },
128130 [ETIFF_EXIF_IFD_POINTER ] = {0x8769 , ET_LONG , 1 , "Exif IFD Pointer" },
129131 // Exif SubIFD
@@ -226,6 +228,7 @@ struct custom_tag_value
226228 uint16_t tag_id ;
227229 enum exif_tag_type type ;
228230 union value_u value ;
231+ size_t val_count ;
229232};
230233enum { CT_TIFF , CT_EXIF , CT_NUM };
231234/// custom exif tags given by user
@@ -276,8 +279,8 @@ gpujpeg_write_ifd(struct gpujpeg_writer* writer, const uint8_t* start, size_t co
276279 }
277280 if ( custom_tags != NULL ) { // add user custom tags
278281 for ( unsigned i = 0 ; i < custom_tags -> count ; ++ i ) {
279- write_exif_tag (writer , custom_tags -> vals [i ].type , custom_tags -> vals [i ].tag_id , 1 ,
280- custom_tags -> vals [i ].value , start , & end );
282+ write_exif_tag (writer , custom_tags -> vals [i ].type , custom_tags -> vals [i ].tag_id ,
283+ custom_tags -> vals [i ].val_count , custom_tags -> vals [ i ]. value , start , & end );
281284 }
282285 // ensure custom_tags are in-ordered
283286 qsort (first_rec , (writer -> buffer_current - first_rec ) / IFD_ITEM_SZ , IFD_ITEM_SZ , ifd_sort );
@@ -433,9 +436,13 @@ usage()
433436 "\t" GPUJPEG_ENC_OPT_EXIF_TAG "=<ID>:<type>=<value>\n"
434437 "\t" GPUJPEG_ENC_OPT_EXIF_TAG "=<name>=<value>\n"
435438 "\t\tname must be a tag name known to GPUJPEG\n" );
436- printf ("\nrecognized tag name (type):\n" );
439+ printf ("\n" );
440+ printf ("If multple values required, separate with a comma; rationals are in format num/den.\n" );
441+ printf ("\n" );
442+ printf ("recognized tag name (type, count):\n" );
437443 for ( unsigned i = 0 ; i < ARR_SIZE (exif_tiff_tag_info ); ++ i ) {
438- printf ("\t- %s (%s)\n" , exif_tiff_tag_info [i ].name , exif_tag_type_info [exif_tiff_tag_info [i ].type ].name );
444+ printf ("\t- %s (%s, %u)\n" , exif_tiff_tag_info [i ].name , exif_tag_type_info [exif_tiff_tag_info [i ].type ].name ,
445+ exif_tiff_tag_info [i ].count );
439446 }
440447}
441448
@@ -473,15 +480,42 @@ gpujpeg_exif_add_tag(struct gpujpeg_exif_tags** exif_tags, const char* cfg)
473480 }
474481 }
475482
476- unsigned numeric_unsigned = T_NUMERIC | T_UNSIGNED ;
477- if ( (exif_tag_type_info [type ].type_flags & numeric_unsigned ) != numeric_unsigned ) {
478- ERROR_MSG ("Only unsigned integers currently supported!\n" );
479- return false;
480- }
481483 endptr += 1 ;
482- unsigned long long val = strtoull (endptr , & endptr , 0 );
483- if (* endptr != '\0' ) {
484- ERROR_MSG ("Trainling data in Exif value!\n" );
484+ void * val_alloc = NULL ;
485+ size_t val_count = 0 ;
486+ do {
487+ if ( * endptr == ',' ) {
488+ endptr += 1 ;
489+ }
490+ if ( (exif_tag_type_info [type ].type_flags & T_NUMERIC ) != 0U ) {
491+ unsigned long long val = strtoull (endptr , & endptr , 0 );
492+ val_count += 1 ;
493+ uint32_t * val_a = realloc (val_alloc , val_count * sizeof * val_a );
494+ val_a [val_count - 1 ] = val ;
495+ val_alloc = val_a ;
496+ }
497+ else if ( (exif_tag_type_info [type ].type_flags & T_RATIONAL ) != 0U ) {
498+ unsigned long long num = strtoull (endptr , & endptr , 0 );
499+ if ( * endptr != '/' ) {
500+ ERROR_MSG ("[Exif] Malformed rational, expected '/', got '%c'!\n" , * endptr );
501+ }
502+ endptr += 1 ;
503+ unsigned long long den = strtoull (endptr , & endptr , 0 );
504+ val_count += 1 ;
505+ uint32_t * val_a = realloc (val_alloc , val_count * 2 * sizeof * val_a );
506+ val_a [2 * (val_count - 1 )] = num ;
507+ val_a [(2 * (val_count - 1 )) + 1 ] = den ;
508+ val_alloc = val_a ;
509+ }
510+ else {
511+ ERROR_MSG ("Only integer or rational values are currently supported!\n" );
512+ return false;
513+ }
514+ } while ( * endptr == ',' );
515+
516+ if ( * endptr != '\0' ) {
517+ free (val_alloc );
518+ ERROR_MSG ("Trainling data in Exif value: %s\n" , endptr );
485519 return false;
486520 }
487521
@@ -495,9 +529,9 @@ gpujpeg_exif_add_tag(struct gpujpeg_exif_tags** exif_tags, const char* cfg)
495529 new_size * sizeof (* exif_tags )-> tags [table_idx ].vals [0 ]);
496530 (* exif_tags )-> tags [table_idx ].vals [new_size - 1 ].tag_id = tag_id ;
497531 (* exif_tags )-> tags [table_idx ].vals [new_size - 1 ].type = type ;
498- uint32_t * val_a = malloc ( sizeof * val_a );
499- * val_a = val ;
500- (* exif_tags )-> tags [table_idx ].vals [new_size - 1 ].value . uvalue = val_a ;
532+ assert ( val_alloc != NULL );
533+ ( * exif_tags ) -> tags [ table_idx ]. vals [ new_size - 1 ]. value . uvalue = val_alloc ;
534+ (* exif_tags )-> tags [table_idx ].vals [new_size - 1 ].val_count = val_count ;
501535
502536 return true;
503537}
0 commit comments