5757#include "gpujpeg_util.h" // for ARR_SIZE
5858#include "gpujpeg_writer.h" // for gpujpeg_writer, gpujpeg_writer...
5959
60+ #define MOD_NAME "[Exif] "
61+
6062enum exif_tag_type {
6163 ET_NONE = 0 ,
6264 ET_BYTE = 1 , ///< 8-bit unsigned integer
7779 T_RATIONAL = 1 << 3 , // 2 items of .uvalue
7880};
7981
80- static const struct
82+ static const struct exif_tag_type_info_t
8183{
8284 unsigned size ;
8385 const char * name ;
@@ -94,6 +96,7 @@ static const struct
9496};
9597
9698enum exif_tiff_tag {
99+ TAG_NONE ,
97100 // 0th IFD TIFF Tags
98101 ETIFF_ORIENTATION , ///< Image resolution in width direction (recommended)
99102 ETIFF_XRESOLUTION , ///< Image resolution in width direction (mandatory)
@@ -111,6 +114,7 @@ enum exif_tiff_tag {
111114 EEXIF_COLOR_SPACE , ///< Color space information (mandatory)
112115 EEXIF_PIXEL_X_DIMENSION , ///< Valid image width (mandatory)
113116 EEXIF_PIXEL_Y_DIMENSION , ///< Valid image height (mandatory)
117+ NUM_TAGS
114118};
115119
116120const struct exif_tiff_tag_info_t {
@@ -119,6 +123,7 @@ const struct exif_tiff_tag_info_t {
119123 unsigned count ;
120124 const char * name ;
121125} exif_tiff_tag_info [] = {
126+ [TAG_NONE ] = {0 , 0 , 0 , "Unknown" },
122127 [ETIFF_ORIENTATION ] = {0x112 , ET_SHORT , 1 , "Orientation" },
123128 [ETIFF_XRESOLUTION ] = {0x11A , ET_RATIONAL , 1 , "XResolution" },
124129 [ETIFF_YRESOLUTION ] = {0x11B , ET_RATIONAL , 1 , "YResolution" },
@@ -147,6 +152,7 @@ enum {
147152 IFD_ITEM_SZ = 12 ,
148153 DPI_DEFAULT = 72 ,
149154 EEXIF_FIRST = 0x827A , // (Exposure time) first tag id of Exif Private Tags
155+ TIFF_HDR_TAG = 0x002a ,
150156};
151157
152158union value_u {
@@ -386,7 +392,7 @@ gpujpeg_writer_write_exif(struct gpujpeg_encoder* encoder)
386392 gpujpeg_writer_emit_byte (writer , 'M' );
387393 gpujpeg_writer_emit_byte (writer , 'M' );
388394
389- gpujpeg_writer_emit_2byte (writer , 0x002a ); // TIFF header
395+ gpujpeg_writer_emit_2byte (writer , TIFF_HDR_TAG ); // TIFF header
390396 gpujpeg_writer_emit_4byte (writer , 0x08 ); // IFD offset - follows immediately
391397
392398 gpujpeg_write_0th (encoder , start );
@@ -558,3 +564,167 @@ gpujpeg_exif_tags_destroy(struct gpujpeg_exif_tags* exif_tags)
558564 }
559565 free (exif_tags );
560566}
567+
568+ ////////////
569+ // READER //
570+ ////////////
571+ static enum exif_tiff_tag
572+ get_tag_from_id (uint16_t tag_id )
573+ {
574+ for ( unsigned i = TAG_NONE + 1 ; i < NUM_TAGS ; ++ i ) {
575+ if ( exif_tiff_tag_info [i ].id == tag_id ) {
576+ return i ;
577+ }
578+ }
579+ return TAG_NONE ;
580+ }
581+
582+ static uint8_t
583+ read_byte (uint8_t * * image )
584+ {
585+ return * (* image )++ ;
586+ }
587+ static uint16_t
588+ read_2byte_be (uint8_t * * image ) {
589+ uint16_t ret = (* image )[0 ] << 8 | (* image )[1 ];
590+ * image += 2 ;
591+ return ret ;
592+ }
593+ static uint32_t
594+ read_4byte_be (uint8_t * * image ) {
595+ uint32_t ret = (* image )[0 ] << 24 | (* image )[1 ] << 16 | (* image )[2 ] << 8 | (* image )[3 ];
596+ * image += 4 ;
597+ return ret ;
598+ }
599+ static uint16_t
600+ read_2byte_le (uint8_t * * image ) {
601+ uint16_t ret = (* image )[1 ] << 8 | (* image )[0 ];
602+ * image += 2 ;
603+ return ret ;
604+ }
605+ static uint32_t
606+ read_4byte_le (uint8_t * * image ) {
607+ uint32_t ret = (* image )[3 ] << 24 | (* image )[2 ] << 16 | (* image )[1 ] << 8 | (* image )[0 ];
608+ * image += 4 ;
609+ return ret ;
610+ }
611+
612+ static bool
613+ read_0th_ifd (uint8_t * * image , const uint8_t * image_end , int verbose , uint16_t (* read_2byte )(uint8_t * * ),
614+ uint32_t (* read_4byte )(uint8_t * * ))
615+ {
616+ if ( * image + 2 > image_end ) {
617+ WARN_MSG ("Unexpected end of file!\n" );
618+ return false;
619+ }
620+ size_t num_interop = read_2byte (image );
621+ if ( * image + num_interop * IFD_ITEM_SZ > image_end ) {
622+ WARN_MSG (MOD_NAME "Insufficient space to hold %zu IFD0 items!\n" , num_interop );
623+ return false;
624+ }
625+ DEBUG_MSG (verbose , "Found %zu IFD0 items.\n" , num_interop );
626+
627+ for ( unsigned i = 0 ; i < num_interop ; ++ i ) {
628+ uint16_t tag_id = read_2byte (image );
629+ uint16_t type = read_2byte (image );
630+ uint32_t count = read_4byte (image );
631+ uint32_t val = read_4byte (image );
632+ unsigned size = 0 ;
633+ enum exif_tiff_tag tag = get_tag_from_id (tag_id );
634+ const char * type_name = "WRONG" ;
635+ if ( type < ET_END && exif_tag_type_info [type ].name != NULL ) {
636+ type_name = exif_tag_type_info [type ].name ;
637+ if ( (exif_tag_type_info [type ].type_flags & T_NUMERIC ) != 0 ) {
638+ if (read_2byte == read_2byte_be ) {
639+ val >>= 8 * exif_tag_type_info [type ].size ;
640+ }
641+ }
642+ size = exif_tag_type_info [type ].size ;
643+ }
644+ DEBUG_MSG (verbose , MOD_NAME "Found IFD0 tag %s (%#x) type %s: count=%u, %s=%#x\n" , exif_tiff_tag_info [tag ].name ,
645+ tag_id , type_name , count , count * size <= 4 ? "value" : "offset" , val );
646+ if ( tag == ETIFF_ORIENTATION && val != ETIFF_ORIENT_HORIZONTAL ) {
647+ WARN_MSG (MOD_NAME "Orientation %d not handled!\n" , val );
648+ }
649+ }
650+
651+ DEBUG_MSG (verbose , MOD_NAME "Skipping data after IFD0 marker (eg. Exif SubIFD)\n" );
652+ // TODO: Exif Private Tags SubIFD
653+
654+ return true;
655+ }
656+
657+ /**
658+ * parse the header
659+ *
660+ * Currently only the basic validity is cheecked. If verbosity is set to higher value,
661+ * the basic tags from 0th IFD are printed out (not Exif SubIFD).
662+ *
663+ * JPEG Orientation is checked and of not horizontal, warning is issued.
664+ */
665+ bool
666+ gpujpeg_exif_parse (uint8_t * * image , const uint8_t * image_end , int verbose )
667+ {
668+ enum {
669+ EXIF_HDR_MIN_LEN = 18 , // with empty 0th IFD
670+ };
671+ if (image_end - * image < 7 ) {
672+ WARN_MSG ("Unexpected end of file!\n" );
673+ return false;
674+ }
675+ uint8_t * image_start = * image ;
676+ uint16_t length = read_2byte_be (image );
677+ if (length > image_end - * image - 2 ) {
678+ WARN_MSG ("Unexpected end of file!\n" );
679+ * image = image_start ;
680+ return false;
681+ }
682+ if (length < EXIF_HDR_MIN_LEN ) {
683+ WARN_MSG ("Insufficient Exif header length %u!\n" , (unsigned )length );
684+ * image = image_start ;
685+ return false;
686+ }
687+ char exif [5 ];
688+ for (int i = 0 ; i < 5 ; ++ i ) {
689+ exif [i ] = read_byte (image );
690+ }
691+ if (strncmp (exif , "Exif" , sizeof exif ) != 0 ) {
692+ WARN_MSG ("APP1 marker is not Exif, got \"%.4s\"\n" , exif );
693+ * image = image_start ;
694+ return false;
695+ }
696+ read_byte (image ); // drop (padding)
697+
698+ uint8_t * const base = * image ;
699+
700+ uint16_t endian_tag = read_2byte_be (image );
701+ uint16_t (* read_2byte )(uint8_t * * ) = read_2byte_be ;
702+ uint32_t (* read_4byte )(uint8_t * * ) = read_4byte_be ;
703+
704+ if ( endian_tag == ('I' << 8 | 'I' ) ) {
705+ DEBUG_MSG (verbose , "Little endian Exif detected.\n" );
706+ read_2byte = read_2byte_le ;
707+ read_4byte = read_4byte_le ;
708+ }
709+ else if ( endian_tag == ('M' << 8 | 'M' ) ) {
710+ DEBUG_MSG (verbose , "Big endian Exif detected.\n" );
711+ }
712+ else {
713+ WARN_MSG ("Unexpected endianity!\n" );
714+ * image = image_start ;
715+ return false;
716+ }
717+ uint16_t tiff_hdr = read_2byte (image );
718+ if (tiff_hdr != TIFF_HDR_TAG ) {
719+ WARN_MSG ("Wrong TIFF tag, expected 0x%04x!\n" , TIFF_HDR_TAG );
720+ * image = image_start ;
721+ return false;
722+ }
723+
724+ uint32_t offset = read_4byte (image ); // 0th IFD offset
725+ * image = base + offset ;
726+ bool ret = read_0th_ifd (image , image_end , verbose , read_2byte , read_4byte );
727+
728+ * image = image_start + length ;
729+ return ret ;
730+ }
0 commit comments