3131
3232#include <assert.h> // for assert
3333#include <ctype.h> // for isdigit
34+ #include <stddef.h>
3435#include <stdint.h> // for uint8_t, uint32_t, uint16_t
3536#include <stdio.h> // for printf
3637#include <stdlib.h> // for size_t, NULL, free, abort, calloc
@@ -70,24 +71,26 @@ enum exif_tag_type {
7071};
7172
7273enum {
73- T_NUMERIC = 1 << 0 ,
74- T_UNSIGNED = 1 << 1 ,
74+ T_NUMERIC = 1 << 0 , // single number in .uvalue
75+ T_UNSIGNED = 1 << 1 , // number (or pair if rational) is unsigned
76+ T_BYTE_ARRAY = 1 << 2 , // data stored in .csvalue
77+ T_RATIONAL = 1 << 3 , // 2 items of .uvalue
7578};
7679
77- static const struct exif_tag_type_info_t
80+ static const struct
7881{
7982 unsigned size ;
8083 const char * name ;
8184 unsigned type_flags ;
8285} exif_tag_type_info [] = {
83- [ET_BYTE ] = {1 , "BYTE" , T_NUMERIC |T_UNSIGNED },
84- [ET_ASCII ] = {0 , "ASCII" , 0 },
85- [ET_SHORT ] = {2 , "SHORT" , T_NUMERIC |T_UNSIGNED },
86- [ET_LONG ] = {4 , "LONG" , T_NUMERIC |T_UNSIGNED },
87- [ET_RATIONAL ] = {8 , "RATIONAL" , T_UNSIGNED },
88- [ET_UNDEFINED ] = {4 , "UNDEFINED" , },
89- [ET_SLONG ] = {4 , "SLONG" , T_NUMERIC },
90- [ET_SRATIONAL ] = {8 , "SRATIONAL" , 0 }
86+ [ET_BYTE ] = {1 , "BYTE" , T_NUMERIC |T_UNSIGNED },
87+ [ET_ASCII ] = {1 , "ASCII" , T_BYTE_ARRAY },
88+ [ET_SHORT ] = {2 , "SHORT" , T_NUMERIC |T_UNSIGNED },
89+ [ET_LONG ] = {4 , "LONG" , T_NUMERIC |T_UNSIGNED },
90+ [ET_RATIONAL ] = {8 , "RATIONAL" , T_UNSIGNED | T_RATIONAL },
91+ [ET_UNDEFINED ] = {1 , "UNDEFINED" , T_BYTE_ARRAY },
92+ [ET_SLONG ] = {4 , "SLONG" , T_NUMERIC },
93+ [ET_SRATIONAL ] = {8 , "SRATIONAL" , T_RATIONAL },
9194};
9295
9396enum exif_tiff_tag {
@@ -112,23 +115,24 @@ enum exif_tiff_tag {
112115const struct exif_tiff_tag_info_t {
113116 uint16_t id ;
114117 enum exif_tag_type type ;
118+ unsigned count ;
115119 const char * name ;
116120} exif_tiff_tag_info [] = {
117- [ETIFF_ORIENTATION ] = {0x112 , ET_SHORT , "Orientation" },
118- [ETIFF_XRESOLUTION ] = {0x11A , ET_RATIONAL , "XResolution" },
119- [ETIFF_YRESOLUTION ] = {0x11B , ET_RATIONAL , "YResolution" },
120- [ETIFF_RESOLUTION_UNIT ] = {0x128 , ET_SHORT , "ResolutionUnit" },
121- [ETIFF_SOFTWARE ] = {0x131 , ET_ASCII , "Sofware" },
122- [ETIFF_DATE_TIME ] = {0x132 , ET_ASCII , "DateTime" },
123- [ETIFF_YCBCR_POSITIONING ] = {0x213 , ET_SHORT , "YCbCrPositioning" },
124- [ETIFF_EXIF_IFD_POINTER ] = {0x8769 , ET_LONG , "Exif IFD Pointer" },
121+ [ETIFF_ORIENTATION ] = {0x112 , ET_SHORT , 1 , "Orientation" },
122+ [ETIFF_XRESOLUTION ] = {0x11A , ET_RATIONAL , 1 , "XResolution" },
123+ [ETIFF_YRESOLUTION ] = {0x11B , ET_RATIONAL , 1 , "YResolution" },
124+ [ETIFF_RESOLUTION_UNIT ] = {0x128 , ET_SHORT , 1 , "ResolutionUnit" },
125+ [ETIFF_SOFTWARE ] = {0x131 , ET_ASCII , 0 , "Sofware" },
126+ [ETIFF_DATE_TIME ] = {0x132 , ET_ASCII , 20 , "DateTime" },
127+ [ETIFF_YCBCR_POSITIONING ] = {0x213 , ET_SHORT , 1 , "YCbCrPositioning" },
128+ [ETIFF_EXIF_IFD_POINTER ] = {0x8769 , ET_LONG , 1 , "Exif IFD Pointer" },
125129 // Exif SubIFD
126- [EEXIF_EXIF_VERSION ] = {0x9000 , ET_UNDEFINED , "ExifVersion" },
127- [EEXIF_COMPONENTS_CONFIGURATION ] = {0x9101 , ET_UNDEFINED , "ComponentConfiguration" },
128- [EEXIF_FLASHPIX_VERSION ] = {0xA000 , ET_UNDEFINED , "FlashPixVersion" },
129- [EEXIF_COLOR_SPACE ] = {0xA001 , ET_SHORT , "ColorSpace" },
130- [EEXIF_PIXEL_X_DIMENSION ] = {0xA002 , ET_SHORT , "PixelXDimension" }, // type can be also LONG
131- [EEXIF_PIXEL_Y_DIMENSION ] = {0xA003 , ET_SHORT , "PixelYDimension" }, // ditto
130+ [EEXIF_EXIF_VERSION ] = {0x9000 , ET_UNDEFINED , 4 , "ExifVersion" },
131+ [EEXIF_COMPONENTS_CONFIGURATION ] = {0x9101 , ET_UNDEFINED , 4 , "ComponentConfiguration" },
132+ [EEXIF_FLASHPIX_VERSION ] = {0xA000 , ET_UNDEFINED , 4 , "FlashPixVersion" },
133+ [EEXIF_COLOR_SPACE ] = {0xA001 , ET_SHORT , 1 , "ColorSpace" },
134+ [EEXIF_PIXEL_X_DIMENSION ] = {0xA002 , ET_SHORT , 1 , "PixelXDimension" }, // type can be also LONG
135+ [EEXIF_PIXEL_Y_DIMENSION ] = {0xA003 , ET_SHORT , 1 , "PixelYDimension" }, // ditto
132136};
133137
134138// misc constants
@@ -144,97 +148,70 @@ enum {
144148};
145149
146150union value_u {
147- uint32_t uvalue ;
148- const char * csvalue ; // ET_STRING or ET_UNKNOWN
149- struct
150- {
151- uint32_t uvalue_num ;
152- uint32_t uvalue_den ;
153- } urational ;
151+ const uint32_t * uvalue ;
152+ const char * csvalue ; // ET_STRING (must be 0-terminated) or ET_UNDEFINED
154153};
155154
156155static void
157- write_exif_emit_string_tag (struct gpujpeg_writer * writer , uint16_t tag , const char * str , const uint8_t * start ,
158- uint8_t * * end )
156+ write_exif_tag (struct gpujpeg_writer * writer , enum exif_tag_type type , uint16_t tag_id , unsigned count ,
157+ union value_u val , const uint8_t * start , uint8_t * * end )
159158{
160- const size_t count = strlen (str ) + 1 ;
161- gpujpeg_writer_emit_2byte (writer , tag );
162- gpujpeg_writer_emit_2byte (writer , ET_ASCII );
163- gpujpeg_writer_emit_4byte (writer , count );
164- gpujpeg_writer_emit_4byte (writer , * end - start );;
165- memcpy (* end , str , count );
166- * end += count ;
167- }
159+ assert (type < ET_END );
160+ unsigned size = exif_tag_type_info [type ].size ;
168161
169- static void
170- write_exif_emit_4bytes_tag (struct gpujpeg_writer * writer , uint16_t tag , enum exif_tag_type type , uint32_t size ,
171- uint32_t val )
172- {
173- assert (size <= 4 );
174- gpujpeg_writer_emit_2byte (writer , tag );
162+ if ( type == ET_ASCII ) {
163+ if ( count == 0 ) {
164+ count = strlen (val .csvalue ) + 1 ;
165+ }
166+ else {
167+ assert (count == strlen (val .csvalue ) + 1 );
168+ }
169+ }
170+ assert (count > 0 );
171+ assert (size > 0 );
172+
173+ gpujpeg_writer_emit_2byte (writer , tag_id );
175174 gpujpeg_writer_emit_2byte (writer , type );
176- gpujpeg_writer_emit_4byte (writer , 1 );
175+ gpujpeg_writer_emit_4byte (writer , count );
177176
178- for (unsigned i = size ; i > 0 ; -- i ) { // Exif 4.6.2 - left aligned value; we use big endian
179- gpujpeg_writer_emit_byte (writer , (val >> 8 * (i - 1 )) & 0xFFU );
177+ // we actually store rational numbers as a pair
178+ if ( (exif_tag_type_info [type ].type_flags & T_RATIONAL ) != 0 ) {
179+ count *= 2 ;
180+ size /= 2 ;
180181 }
181- for (unsigned i = size ; i < 4 ; ++ i ) {
182- gpujpeg_writer_emit_byte (writer , 0 );
182+
183+ const bool val_longer_than_4b = size * count > 4 ;
184+ uint8_t * return_pos = NULL ;
185+ if ( val_longer_than_4b ) {
186+ gpujpeg_writer_emit_4byte (writer , * end - start );
187+ return_pos = writer -> buffer_current ;
188+ writer -> buffer_current = * end ;
183189 }
184- }
185190
186- static void
187- write_exif_emit_lt_4b_tag (struct gpujpeg_writer * writer , uint16_t tag , enum exif_tag_type type , uint32_t size ,
188- union value_u val , const uint8_t * start ,
189- uint8_t * * end )
190- {
191- gpujpeg_writer_emit_2byte (writer , tag );
192- gpujpeg_writer_emit_2byte (writer , type );
193- gpujpeg_writer_emit_4byte (writer , 1 );
194- gpujpeg_writer_emit_4byte (writer , * end - start );;
195-
196- if (type == ET_RATIONAL ) {
197- assert (size == 8 );
198- uint32_t num = htobe32 (val .urational .uvalue_num );
199- uint32_t den = htobe32 (val .urational .uvalue_den );
200- memcpy (* end , & num , sizeof num );
201- memcpy (* end + 4 , & den , sizeof den );
191+ if ( (exif_tag_type_info [type ].type_flags & T_BYTE_ARRAY ) != 0 ) {
192+ assert (size == 1 );
193+ for ( unsigned i = 0 ; i < count ; ++ i ) {
194+ gpujpeg_writer_emit_byte (writer , val .csvalue [i ]);
195+ }
202196 }
203197 else {
204- abort ();
198+ for ( unsigned c = 0 ; c < count ; ++ c ) {
199+ for ( unsigned i = 0 ; i < size ; ++ i ) { // Exif 4.6.2 - left aligned value; we use big endian
200+ gpujpeg_writer_emit_byte (writer , (val .uvalue [c ] >> 8 * (size - i - 1 )) & 0xFFU );
201+ }
202+ }
205203 }
206204
207- * end += size ;
208- }
209-
210- static void
211- write_exif_tag (struct gpujpeg_writer * writer , enum exif_tag_type type , uint16_t tag_id , union value_u val ,
212- const uint8_t * start , uint8_t * * end )
213- {
214- assert (type < ET_END );
215- const unsigned size = exif_tag_type_info [type ].size ;
216-
217- // size for string is computed
218- assert (size > 0 || type == ET_ASCII );
219-
220- if ( type == ET_ASCII ) {
221- write_exif_emit_string_tag (writer , tag_id , val .csvalue , start , end );
222- return ;
205+ if ( val_longer_than_4b ) {
206+ * end += (size_t )size * count ;
207+ writer -> buffer_current = return_pos ;
223208 }
224- if ( type == ET_UNDEFINED ) {
225- assert (size == 4 );
226- gpujpeg_writer_emit_2byte (writer , tag_id );
227- gpujpeg_writer_emit_2byte (writer , type );
228- gpujpeg_writer_emit_4byte (writer , 4 ); // count - we have all 4 B, unsure if defined otherwise for other
229- memcpy (writer -> buffer_current , val .csvalue , 4 );
230- writer -> buffer_current += 4 ;
231- return ;
232- }
233- if (size <= 4 ) {
234- write_exif_emit_4bytes_tag (writer , tag_id , type , size , val .uvalue );
235- return ;
209+ else {
210+ // padding
211+ for ( unsigned i = size * count ; i < 4 ; ++ i ) {
212+ gpujpeg_writer_emit_byte (writer , 0 );
213+ }
236214 }
237- write_exif_emit_lt_4b_tag (writer , tag_id , type , size , val , start , end );
238215}
239216
240217struct tag_value
@@ -290,17 +267,17 @@ gpujpeg_write_ifd(struct gpujpeg_writer* writer, const uint8_t* start, size_t co
290267 const struct tag_value * info = & tags [i ];
291268 union value_u value = info -> value ;
292269 if ( info -> tag == ETIFF_EXIF_IFD_POINTER ) {
293- value .uvalue = end - start ;
270+ value .uvalue = ( uint32_t []) { end - start } ;
294271 }
295272 const struct exif_tiff_tag_info_t * t = & exif_tiff_tag_info [info -> tag ];
296273 assert (t -> id >= last_tag_id );
297274 last_tag_id = t -> id ;
298- write_exif_tag (writer , t -> type , t -> id , value , start , & end );
275+ write_exif_tag (writer , t -> type , t -> id , exif_tiff_tag_info [ info -> tag ]. count , value , start , & end );
299276 }
300277 if ( custom_tags != NULL ) { // add user custom tags
301278 for ( unsigned i = 0 ; i < custom_tags -> count ; ++ i ) {
302- write_exif_tag (writer , custom_tags -> vals [i ].type , custom_tags -> vals [i ].tag_id , custom_tags -> vals [ i ]. value ,
303- start , & end );
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 );
304281 }
305282 // ensure custom_tags are in-ordered
306283 qsort (first_rec , (writer -> buffer_current - first_rec ) / IFD_ITEM_SZ , IFD_ITEM_SZ , ifd_sort );
@@ -335,13 +312,13 @@ gpujpeg_write_0th(struct gpujpeg_encoder* encoder, const uint8_t* start)
335312 struct tm buf ;
336313 (void ) strftime (date_time , sizeof date_time , "%Y:%m:%d %H:%M:%S" , localtime_s (& now , & buf ));
337314 struct tag_value tags [] = {
338- {ETIFF_ORIENTATION , {.uvalue = ETIFF_ORIENT_HORIZONTAL }},
339- {ETIFF_XRESOLUTION , {.urational = {DPI_DEFAULT , 1 }} },
340- {ETIFF_YRESOLUTION , {.urational = {DPI_DEFAULT , 1 }} },
341- {ETIFF_RESOLUTION_UNIT , {.uvalue = ETIFF_INCHES } },
342- {ETIFF_SOFTWARE , {.csvalue = "GPUJPEG" } },
343- {ETIFF_DATE_TIME , {.csvalue = date_time } },
344- {ETIFF_YCBCR_POSITIONING , {.uvalue = ETIFF_CENTER } },
315+ {ETIFF_ORIENTATION , {.uvalue = ( uint32_t []){ ETIFF_ORIENT_HORIZONTAL } }},
316+ {ETIFF_XRESOLUTION , {.uvalue = ( uint32_t []) {DPI_DEFAULT , 1 }} },
317+ {ETIFF_YRESOLUTION , {.uvalue = ( uint32_t []) {DPI_DEFAULT , 1 }} },
318+ {ETIFF_RESOLUTION_UNIT , {.uvalue = ( uint32_t []){ ETIFF_INCHES } } },
319+ {ETIFF_SOFTWARE , {.csvalue = "GPUJPEG" } },
320+ {ETIFF_DATE_TIME , {.csvalue = date_time } },
321+ {ETIFF_YCBCR_POSITIONING , {.uvalue = ( uint32_t []){ ETIFF_CENTER } } },
345322 {ETIFF_EXIF_IFD_POINTER , {0 } }, // value will be set later
346323 };
347324 size_t tag_count = ARR_SIZE (tags );
@@ -357,12 +334,12 @@ gpujpeg_write_0th(struct gpujpeg_encoder* encoder, const uint8_t* start)
357334static void gpujpeg_write_exif_ifd (struct gpujpeg_encoder * encoder , const uint8_t * start )
358335{
359336 struct tag_value tags [] = {
360- {EEXIF_EXIF_VERSION , {.csvalue = "0230" } }, // 2.30
361- {EEXIF_COMPONENTS_CONFIGURATION , {.csvalue = "\1\2\3\0" } }, // YCbCr
362- {EEXIF_FLASHPIX_VERSION , {.csvalue = "0100" } }, // "0100"
363- {EEXIF_COLOR_SPACE , {.uvalue = ETIFF_SRGB } },
364- {EEXIF_PIXEL_X_DIMENSION , {encoder -> coder .param_image .width } },
365- {EEXIF_PIXEL_Y_DIMENSION , {encoder -> coder .param_image .height }},
337+ {EEXIF_EXIF_VERSION , {.csvalue = "0230" } }, // 2.30
338+ {EEXIF_COMPONENTS_CONFIGURATION , {.csvalue = "\1\2\3\0" } }, // YCbCr
339+ {EEXIF_FLASHPIX_VERSION , {.csvalue = "0100" } },
340+ {EEXIF_COLOR_SPACE , {.uvalue = ( uint32_t []){ ETIFF_SRGB }} },
341+ {EEXIF_PIXEL_X_DIMENSION , {. uvalue = ( uint32_t []){ encoder -> coder .param_image .width } } },
342+ {EEXIF_PIXEL_Y_DIMENSION , {. uvalue = ( uint32_t []){ encoder -> coder .param_image .height } }},
366343 };
367344 size_t tag_count = ARR_SIZE (tags );
368345 const struct custom_exif_tags * custom_tags = NULL ;
@@ -518,17 +495,24 @@ gpujpeg_exif_add_tag(struct gpujpeg_exif_tags** exif_tags, const char* cfg)
518495 new_size * sizeof (* exif_tags )-> tags [table_idx ].vals [0 ]);
519496 (* exif_tags )-> tags [table_idx ].vals [new_size - 1 ].tag_id = tag_id ;
520497 (* exif_tags )-> tags [table_idx ].vals [new_size - 1 ].type = type ;
521- (* exif_tags )-> tags [table_idx ].vals [new_size - 1 ].value .uvalue = val ;
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 ;
522501
523502 return true;
524503}
525504
526505void
527506gpujpeg_exif_tags_destroy (struct gpujpeg_exif_tags * exif_tags )
528507{
529- if (exif_tags == NULL ) {
508+ if ( exif_tags == NULL ) {
530509 return ;
531510 }
532- free (exif_tags -> tags [0 ].vals );
533- free (exif_tags -> tags [1 ].vals );
511+ for ( unsigned i = 0 ; i < CT_NUM ; ++ i ) {
512+ for (unsigned j = 0 ; i < exif_tags -> tags [i ].count ; ++ i ) {
513+ free ((void * ) exif_tags -> tags [i ].vals [j ].value .uvalue );
514+ }
515+ free (exif_tags -> tags [i ].vals );
516+ }
517+ free (exif_tags );
534518}
0 commit comments