3434#include <stdint.h>
3535#include <stdlib.h>
3636#include <string.h>
37+ #include <time.h> // for strftime
3738
3839#ifdef _WIN32
3940#define strncasecmp _strnicmp
@@ -86,6 +87,7 @@ enum exif_tiff_tag {
8687 ETIFF_YRESOLUTION , ///< Image resolution in height direction (mandatory)
8788 ETIFF_RESOLUTION_UNIT , ///< Unit of X and Y resolution (mandatory)
8889 ETIFF_SOFTWARE , ///< Software used (optional)
90+ ETIFF_DATE_TIME , ///< File change date and time (recommeneded)
8991 ETIFF_YCBCR_POSITIONING , ///< Y and C positioning (mandatory)
9092 ETIFF_EXIF_IFD_POINTER , ///< EXIF tag (mandatory)
9193 // 0th SubIFD Exif Private Tags
@@ -107,6 +109,7 @@ const struct exif_tiff_tag_info_t {
107109 [ETIFF_YRESOLUTION ] = {0x11B , ET_RATIONAL , "YResolution" },
108110 [ETIFF_RESOLUTION_UNIT ] = {0x128 , ET_SHORT , "ResolutionUnit" },
109111 [ETIFF_SOFTWARE ] = {0x131 , ET_ASCII , "Sofware" },
112+ [ETIFF_DATE_TIME ] = {0x132 , ET_ASCII , "DateTime" },
110113 [ETIFF_YCBCR_POSITIONING ] = {0x213 , ET_SHORT , "YCbCrPositioning" },
111114 [ETIFF_EXIF_IFD_POINTER ] = {0x8769 , ET_LONG , "Exif IFD Pointer" },
112115 // Exif SubIFD
@@ -120,6 +123,7 @@ const struct exif_tiff_tag_info_t {
120123
121124// misc constants
122125enum {
126+ ETIFF_ORIENT_HORIZONTAL = 1 , // normal
123127 ETIFF_CENTER = 1 ,
124128 ETIFF_SRGB = 1 ,
125129 ETIFF_INCHES = 2 ,
@@ -294,35 +298,66 @@ gpujpeg_write_ifd(struct gpujpeg_writer* writer, const uint8_t* start, size_t co
294298 writer -> buffer_current = end ; // jump after the section Value longer than 4Byte of 0th IFD
295299}
296300
301+ /**
302+ * from tags remove the items that are overriden by custom_tags
303+ */
304+ static size_t
305+ remove_overriden (size_t count , struct tag_value tags [], const struct custom_exif_tags * custom_tags )
306+ {
307+ for ( unsigned i = 0 ; i < custom_tags -> count ; ++ i ) {
308+ for ( unsigned j = 0 ; j < count ; ++ j ) {
309+ if ( custom_tags -> vals [i ].tag_id == exif_tiff_tag_info [tags [j ].tag ].id ) {
310+ memmove (tags + j , tags + j + 1 , (count - j - 1 ) * sizeof (tags [0 ]));
311+ count -= 1 ;
312+ break ;
313+ }
314+ }
315+ }
316+ return count ;
317+ }
318+
297319static void
298320gpujpeg_write_0th (struct gpujpeg_encoder * encoder , const uint8_t * start )
299321{
300- const struct tag_value tags [] = {
301- {ETIFF_XRESOLUTION , {.urational = {72 , 1 }} },
302- {ETIFF_YRESOLUTION , {.urational = {72 , 1 }} },
303- {ETIFF_RESOLUTION_UNIT , {.uvalue = ETIFF_INCHES }},
304- {ETIFF_SOFTWARE , {.csvalue = "GPUJPEG" } },
305- {ETIFF_YCBCR_POSITIONING , {.uvalue = ETIFF_CENTER }}, // center
306- {ETIFF_EXIF_IFD_POINTER , {0 } }, // value later; should be last
322+ char date_time [] = " : : : : " ; // unknown val by Exif 2.3
323+ time_t now = time (NULL );
324+ (void ) strftime (date_time , sizeof date_time , "%Y:%m:%d %H:%M:%S" , localtime (& now ));
325+ struct tag_value tags [] = {
326+ {ETIFF_ORIENTATION , {.uvalue = ETIFF_ORIENT_HORIZONTAL }},
327+ {ETIFF_XRESOLUTION , {.urational = {72 , 1 }} },
328+ {ETIFF_YRESOLUTION , {.urational = {72 , 1 }} },
329+ {ETIFF_RESOLUTION_UNIT , {.uvalue = ETIFF_INCHES } },
330+ {ETIFF_SOFTWARE , {.csvalue = "GPUJPEG" } },
331+ {ETIFF_DATE_TIME , {.csvalue = date_time } },
332+ {ETIFF_YCBCR_POSITIONING , {.uvalue = ETIFF_CENTER } },
333+ {ETIFF_EXIF_IFD_POINTER , {0 } }, // value will be set later
307334 };
308- const struct custom_exif_tags * custom_tags =
309- encoder -> writer -> exif_tags != NULL ? & encoder -> writer -> exif_tags -> tags [CT_TIFF ] : NULL ;
335+ size_t tag_count = ARR_SIZE (tags );
336+ const struct custom_exif_tags * custom_tags = NULL ;
337+ if (encoder -> writer -> exif_tags != NULL ) {
338+ custom_tags = & encoder -> writer -> exif_tags -> tags [CT_TIFF ];
339+ tag_count = remove_overriden (tag_count , tags , custom_tags );
340+ }
310341
311- gpujpeg_write_ifd (encoder -> writer , start , ARR_SIZE ( tags ) , tags , custom_tags );
342+ gpujpeg_write_ifd (encoder -> writer , start , tag_count , tags , custom_tags );
312343}
313344
314345static void gpujpeg_write_exif_ifd (struct gpujpeg_encoder * encoder , const uint8_t * start )
315346{
316- const struct tag_value tags [] = {
347+ struct tag_value tags [] = {
317348 {EEXIF_EXIF_VERSION , {.csvalue = "0230" } }, // 2.30
318349 {EEXIF_COMPONENTS_CONFIGURATION , {.csvalue = "\1\2\3\0" } }, // YCbCr
319350 {EEXIF_FLASHPIX_VERSION , {.csvalue = "0100" } }, // "0100"
320351 {EEXIF_COLOR_SPACE , {.uvalue = ETIFF_SRGB } },
321352 {EEXIF_PIXEL_X_DIMENSION , {encoder -> coder .param_image .width } },
322353 {EEXIF_PIXEL_Y_DIMENSION , {encoder -> coder .param_image .height }},
323354 };
324- const struct custom_exif_tags * custom_tags =
325- encoder -> writer -> exif_tags != NULL ? & encoder -> writer -> exif_tags -> tags [CT_EXIF ] : NULL ;
355+ size_t tag_count = ARR_SIZE (tags );
356+ const struct custom_exif_tags * custom_tags = NULL ;
357+ if (encoder -> writer -> exif_tags != NULL ) {
358+ custom_tags = & encoder -> writer -> exif_tags -> tags [CT_EXIF ];
359+ tag_count = remove_overriden (tag_count , tags , custom_tags );
360+ }
326361
327362 gpujpeg_write_ifd (encoder -> writer , start , ARR_SIZE (tags ), tags , custom_tags );
328363}
0 commit comments