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,8 +152,12 @@ 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
158+ ////////////////////////////////////////////////////////////////////////////////
159+ // WRITER //
160+ ////////////////////////////////////////////////////////////////////////////////
152161union value_u {
153162 const uint32_t * uvalue ;
154163 const char * csvalue ; // ET_STRING (must be 0-terminated) or ET_UNDEFINED
@@ -386,7 +395,7 @@ gpujpeg_writer_write_exif(struct gpujpeg_encoder* encoder)
386395 gpujpeg_writer_emit_byte (writer , 'M' );
387396 gpujpeg_writer_emit_byte (writer , 'M' );
388397
389- gpujpeg_writer_emit_2byte (writer , 0x002a ); // TIFF header
398+ gpujpeg_writer_emit_2byte (writer , TIFF_HDR_TAG ); // TIFF header
390399 gpujpeg_writer_emit_4byte (writer , 0x08 ); // IFD offset - follows immediately
391400
392401 gpujpeg_write_0th (encoder , start );
@@ -558,3 +567,155 @@ gpujpeg_exif_tags_destroy(struct gpujpeg_exif_tags* exif_tags)
558567 }
559568 free (exif_tags );
560569}
570+
571+ ////////////////////////////////////////////////////////////////////////////////
572+ // READER //
573+ ////////////////////////////////////////////////////////////////////////////////
574+ static enum exif_tiff_tag
575+ get_tag_from_id (uint16_t tag_id )
576+ {
577+ for ( unsigned i = TAG_NONE + 1 ; i < NUM_TAGS ; ++ i ) {
578+ if ( exif_tiff_tag_info [i ].id == tag_id ) {
579+ return i ;
580+ }
581+ }
582+ return TAG_NONE ;
583+ }
584+
585+ static uint8_t
586+ read_byte (uint8_t * * image )
587+ {
588+ return * (* image )++ ;
589+ }
590+ static uint16_t
591+ read_2byte_be (uint8_t * * image ) {
592+ uint16_t ret = (* image )[0 ] << 8 | (* image )[1 ];
593+ * image += 2 ;
594+ return ret ;
595+ }
596+ static uint32_t
597+ read_4byte_be (uint8_t * * image ) {
598+ uint32_t ret = (* image )[0 ] << 24 | (* image )[1 ] << 16 | (* image )[2 ] << 8 | (* image )[3 ];
599+ * image += 4 ;
600+ return ret ;
601+ }
602+ static uint16_t
603+ read_2byte_le (uint8_t * * image ) {
604+ uint16_t ret = (* image )[1 ] << 8 | (* image )[0 ];
605+ * image += 2 ;
606+ return ret ;
607+ }
608+ static uint32_t
609+ read_4byte_le (uint8_t * * image ) {
610+ uint32_t ret = (* image )[3 ] << 24 | (* image )[2 ] << 16 | (* image )[1 ] << 8 | (* image )[0 ];
611+ * image += 4 ;
612+ return ret ;
613+ }
614+
615+ static void
616+ read_0th_ifd (uint8_t * * image , const uint8_t * image_end , int verbose , uint16_t (* read_2byte )(uint8_t * * ),
617+ uint32_t (* read_4byte )(uint8_t * * ))
618+ {
619+ if ( * image + 2 > image_end ) {
620+ WARN_MSG ("Unexpected end of file!\n" );
621+ return ;
622+ }
623+ size_t num_interop = read_2byte (image );
624+ if ( * image + num_interop * IFD_ITEM_SZ > image_end ) {
625+ WARN_MSG (MOD_NAME "Insufficient space to hold %zu IFD0 items!\n" , num_interop );
626+ return ;
627+ }
628+ DEBUG_MSG (verbose , "Found %zu IFD0 items.\n" , num_interop );
629+
630+ for ( unsigned i = 0 ; i < num_interop ; ++ i ) {
631+ uint16_t tag_id = read_2byte (image );
632+ uint16_t type = read_2byte (image );
633+ uint32_t count = read_4byte (image );
634+ uint32_t val = read_4byte (image );
635+ unsigned size = 0 ;
636+ enum exif_tiff_tag tag = get_tag_from_id (tag_id );
637+ const char * type_name = "WRONG" ;
638+ if ( type < ET_END && exif_tag_type_info [type ].name != NULL ) {
639+ type_name = exif_tag_type_info [type ].name ;
640+ if ( (exif_tag_type_info [type ].type_flags & T_NUMERIC ) != 0 ) {
641+ if (read_2byte == read_2byte_be ) {
642+ val >>= 8 * exif_tag_type_info [type ].size ;
643+ }
644+ }
645+ size = exif_tag_type_info [type ].size ;
646+ }
647+ DEBUG_MSG (verbose , MOD_NAME "Found IFD0 tag %s (%#x) type %s: count=%u, %s=%#x\n" , exif_tiff_tag_info [tag ].name ,
648+ tag_id , type_name , count , count * size <= 4 ? "value" : "offset" , val );
649+ if ( tag == ETIFF_ORIENTATION && val != ETIFF_ORIENT_HORIZONTAL ) {
650+ WARN_MSG (MOD_NAME "Orientation %d not handled!\n" , val );
651+ }
652+ }
653+
654+ DEBUG_MSG (verbose , MOD_NAME "Skipping data after IFD0 marker (eg. Exif SubIFD)\n" );
655+ // TODO: Exif Private Tags SubIFD
656+ }
657+
658+ /**
659+ * parse the header
660+ *
661+ * Currently only the basic validity is cheecked. If verbosity is set to higher value,
662+ * the basic tags from 0th IFD are printed out (not Exif SubIFD).
663+ *
664+ * JPEG Orientation is checked and of not horizontal, warning is issued.
665+ */
666+ void
667+ gpujpeg_exif_parse (uint8_t * * image , const uint8_t * image_end , int verbose )
668+ {
669+ #define HANDLE_ERROR (...) \
670+ WARN_MSG(__VA_ARGS__); \
671+ *image = image_start + length; \
672+ return
673+
674+ enum {
675+ EXIF_HDR_MIN_LEN = 18 , // with empty 0th IFD
676+ };
677+ assert (image_end - * image > 2 );
678+ uint8_t * image_start = * image ;
679+ uint16_t length = read_2byte_be (image );
680+ if (length > image_end - * image - 2 ) {
681+ HANDLE_ERROR ("Unexpected end of file!\n" );
682+ }
683+ if (length < EXIF_HDR_MIN_LEN ) {
684+ HANDLE_ERROR ("Insufficient Exif header length %u!\n" , (unsigned )length );
685+ }
686+ uint8_t exif [5 ];
687+ for (int i = 0 ; i < 5 ; ++ i ) {
688+ exif [i ] = read_byte (image );
689+ }
690+ assert (strncmp ((char * ) exif , "Exif" , sizeof exif ) == 0 ); // otherwise fn shouldn't be called
691+ read_byte (image ); // drop (padding)
692+
693+ uint8_t * const base = * image ;
694+
695+ uint16_t endian_tag = read_2byte_be (image );
696+ uint16_t (* read_2byte )(uint8_t * * ) = read_2byte_be ;
697+ uint32_t (* read_4byte )(uint8_t * * ) = read_4byte_be ;
698+
699+ if ( endian_tag == ('I' << 8 | 'I' ) ) {
700+ DEBUG_MSG (verbose , "Little endian Exif detected.\n" );
701+ read_2byte = read_2byte_le ;
702+ read_4byte = read_4byte_le ;
703+ }
704+ else if ( endian_tag == ('M' << 8 | 'M' ) ) {
705+ DEBUG_MSG (verbose , "Big endian Exif detected.\n" );
706+ }
707+ else {
708+ HANDLE_ERROR ("Unexpected endianity!\n" );
709+ }
710+ uint16_t tiff_hdr = read_2byte (image );
711+ if (tiff_hdr != TIFF_HDR_TAG ) {
712+ HANDLE_ERROR ("Wrong TIFF tag, expected 0x%04x!\n" , TIFF_HDR_TAG );
713+ }
714+
715+ uint32_t offset = read_4byte (image ); // 0th IFD offset
716+ * image = base + offset ;
717+ read_0th_ifd (image , image_end , verbose , read_2byte , read_4byte );
718+
719+ * image = image_start + length ;
720+ #undef HANDLE_ERROR
721+ }
0 commit comments