void metadatamux_exif_create_chunk_from_tag_list (guint8 ** buf, guint32 * size, const GstTagList * taglist, const MetaExifWriteOptions * opts) { ExifData *ed = NULL; GstBuffer *exif_chunk = NULL; const GValue *val = NULL; if (!(buf && size)) goto done; g_free (*buf); *buf = NULL; *size = 0; val = gst_tag_list_get_value_index (taglist, GST_TAG_EXIF, 0); if (val) { exif_chunk = gst_value_get_buffer (val); if (exif_chunk) { ed = exif_data_new_from_data (GST_BUFFER_DATA (exif_chunk), GST_BUFFER_SIZE (exif_chunk)); } } if (!ed) { ed = exif_data_new (); GST_DEBUG ("setting byteorder %d", opts->byteorder); switch (opts->byteorder) { case GST_META_EXIF_BYTE_ORDER_MOTOROLA: exif_data_set_byte_order (ed, EXIF_BYTE_ORDER_MOTOROLA); break; case GST_META_EXIF_BYTE_ORDER_INTEL: exif_data_set_byte_order (ed, EXIF_BYTE_ORDER_INTEL); break; default: break; } exif_data_set_data_type (ed, EXIF_DATA_TYPE_COMPRESSED); exif_data_fix (ed); } gst_tag_list_foreach (taglist, metadatamux_exif_for_each_tag_in_list, ed); exif_data_save_data (ed, buf, size); done: if (ed) exif_data_unref (ed); return; }
static void load_exif_tags( ExifData * ed , JSON::Object & tags ) { tplog2( " LOADING EXIF TAGS" ); exif_data_set_option( ed , EXIF_DATA_OPTION_IGNORE_UNKNOWN_TAGS ); exif_data_set_option( ed , EXIF_DATA_OPTION_FOLLOW_SPECIFICATION ); exif_data_fix( ed ); ExifClosure closure; closure.exif_data = ed; closure.tags = & tags; for ( int i = 0; i < EXIF_IFD_COUNT; ++i ) { if ( ExifContent * c = ed->ifd[i] ) { exif_content_foreach_entry( c , foreach_exif_entry , & closure ); } } tplog2( " LOADED EXIF TAGS" ); }
/* Scan the exif block on the image, if any, and make a set of vips metadata * tags for what we find. */ int vips__exif_parse( VipsImage *image ) { void *data; size_t length; ExifData *ed; VipsExifParams params; const char *str; if( !vips_image_get_typeof( image, VIPS_META_EXIF_NAME ) ) return( 0 ); if( vips_image_get_blob( image, VIPS_META_EXIF_NAME, &data, &length ) ) return( -1 ); if( !(ed = vips_exif_load_data_without_fix( data, length )) ) return( -1 ); #ifdef DEBUG_VERBOSE show_tags( ed ); show_values( ed ); #endif /*DEBUG_VERBOSE*/ /* Look for resolution fields and use them to set the VIPS xres/yres * fields. * * If the fields are missing, set them from the image, which will have * previously had them set from something like JFIF. */ if( vips_image_resolution_from_exif( image, ed ) && vips_exif_resolution_from_image( ed, image ) ) { exif_data_free( ed ); return( -1 ); } /* Make sure all required fields are there before we attach the vips * metadata. */ exif_data_fix( ed ); /* Attach informational fields for what we find. */ params.image = image; params.ed = ed; exif_data_foreach_content( ed, (ExifDataForeachContentFunc) vips_exif_get_content, ¶ms ); vips_exif_get_thumbnail( image, ed ); exif_data_free( ed ); /* Orientation handling. ifd0 has the Orientation tag for the main * image. */ if( vips_image_get_typeof( image, "exif-ifd0-Orientation" ) != 0 && !vips_image_get_string( image, "exif-ifd0-Orientation", &str ) ) { int orientation; orientation = atoi( str ); orientation = VIPS_CLIP( 1, orientation, 8 ); vips_image_set_int( image, VIPS_META_ORIENTATION, orientation ); } return( 0 ); }
/* Examine the metadata tags on the image and update the EXIF block. */ int vips__exif_update( VipsImage *image ) { unsigned char *data; size_t length; unsigned int idl; ExifData *ed; /* Either parse from the embedded EXIF, or if there's none, make * some fresh EXIF we can write the resolution to. */ if( vips_image_get_typeof( image, VIPS_META_EXIF_NAME ) ) { if( vips_image_get_blob( image, VIPS_META_EXIF_NAME, (void *) &data, &length ) ) return( -1 ); if( !(ed = exif_data_new_from_data( data, length )) ) return( -1 ); } else { ed = exif_data_new(); exif_data_set_option( ed, EXIF_DATA_OPTION_FOLLOW_SPECIFICATION ); exif_data_set_data_type( ed, EXIF_DATA_TYPE_COMPRESSED ); exif_data_set_byte_order( ed, EXIF_BYTE_ORDER_INTEL ); /* Create the mandatory EXIF fields with default data. */ exif_data_fix( ed ); } /* Update EXIF tags from the image metadata. */ vips_exif_update( ed, image ); /* Update EXIF resolution from the vips image header. */ if( vips_exif_resolution_from_image( ed, image ) ) { exif_data_free( ed ); return( -1 ); } /* Update EXIF image dimensions from the vips image header. */ if( vips_exif_set_dimensions( ed, image ) ) { exif_data_free( ed ); return( -1 ); } /* Update EXIF orientation from the vips image header. */ if( vips_exif_set_orientation( ed, image ) ) { exif_data_free( ed ); return( -1 ); } /* Update the thumbnail. */ if( vips_exif_set_thumbnail( ed, image ) ) { exif_data_free( ed ); return( -1 ); } /* Reserialise and write. exif_data_save_data() returns an int for some * reason. */ exif_data_save_data( ed, &data, &idl ); if( !idl ) { vips_error( "exif", "%s", _( "error saving EXIF" ) ); exif_data_free( ed ); return( -1 ); } length = idl; #ifdef DEBUG printf( "vips__exif_update: generated %zd bytes of EXIF\n", length ); #endif /*DEBUG*/ vips_image_set_blob( image, VIPS_META_EXIF_NAME, (VipsCallbackFn) vips_free, data, length ); exif_data_free( ed ); return( 0 ); }
ExifData* createExifData(const CameraParameters& params) { ExifData* exifData = exif_data_new(); exif_data_set_option(exifData, EXIF_DATA_OPTION_FOLLOW_SPECIFICATION); exif_data_set_data_type(exifData, EXIF_DATA_TYPE_COMPRESSED); exif_data_set_byte_order(exifData, EXIF_BYTE_ORDER_INTEL); // Create mandatory exif fields and set their default values exif_data_fix(exifData); float triplet[3]; float floatValue = 0.0f; const char* stringValue; int64_t degrees; // Datetime, creating and initializing a datetime tag will automatically // set the current date and time in the tag so just do that. createEntry(exifData, EXIF_IFD_0, EXIF_TAG_DATE_TIME); // Make and model createEntry(exifData, EXIF_IFD_0, EXIF_TAG_MAKE, "Emulator-Goldfish"); createEntry(exifData, EXIF_IFD_0, EXIF_TAG_MODEL, "Emulator-Goldfish"); // Picture size int width = -1, height = -1; params.getPictureSize(&width, &height); if (width >= 0 && height >= 0) { createEntry(exifData, EXIF_IFD_EXIF, EXIF_TAG_PIXEL_X_DIMENSION, width); createEntry(exifData, EXIF_IFD_EXIF, EXIF_TAG_PIXEL_Y_DIMENSION, height); } // Orientation if (getCameraParam(params, CameraParameters::KEY_ROTATION, °rees)) { // Exif orientation values, please refer to // http://www.exif.org/Exif2-2.PDF, Section 4.6.4-A-Orientation // Or these websites: // http://sylvana.net/jpegcrop/exif_orientation.html // http://www.impulseadventure.com/photo/exif-orientation.html enum { EXIF_ROTATE_CAMERA_CW0 = 1, EXIF_ROTATE_CAMERA_CW90 = 6, EXIF_ROTATE_CAMERA_CW180 = 3, EXIF_ROTATE_CAMERA_CW270 = 8, }; uint16_t exifOrien = 1; switch (degrees) { case 0: exifOrien = EXIF_ROTATE_CAMERA_CW0; break; case 90: exifOrien = EXIF_ROTATE_CAMERA_CW90; break; case 180: exifOrien = EXIF_ROTATE_CAMERA_CW180; break; case 270: exifOrien = EXIF_ROTATE_CAMERA_CW270; break; } createEntry(exifData, EXIF_IFD_0, EXIF_TAG_ORIENTATION, exifOrien); } // Focal length if (getCameraParam(params, CameraParameters::KEY_FOCAL_LENGTH, &floatValue)) { createEntry(exifData, EXIF_IFD_EXIF, EXIF_TAG_FOCAL_LENGTH, floatValue); } // GPS latitude and reference, reference indicates sign, store unsigned if (getCameraParam(params, CameraParameters::KEY_GPS_LATITUDE, &floatValue)) { convertGpsCoordinate(floatValue, &triplet); createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_LATITUDE, triplet); const char* ref = floatValue < 0.0f ? "S" : "N"; createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_LATITUDE_REF, ref); } // GPS longitude and reference, reference indicates sign, store unsigned if (getCameraParam(params, CameraParameters::KEY_GPS_LONGITUDE, &floatValue)) { convertGpsCoordinate(floatValue, &triplet); createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_LONGITUDE, triplet); const char* ref = floatValue < 0.0f ? "W" : "E"; createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_LONGITUDE_REF, ref); } // GPS altitude and reference, reference indicates sign, store unsigned if (getCameraParam(params, CameraParameters::KEY_GPS_ALTITUDE, &floatValue)) { createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_ALTITUDE, static_cast<float>(fabs(floatValue))); // 1 indicated below sea level, 0 indicates above sea level uint8_t ref = floatValue < 0.0f ? 1 : 0; createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_ALTITUDE_REF, ref); } // GPS timestamp and datestamp int64_t timestamp = 0; if (getCameraParam(params, CameraParameters::KEY_GPS_TIMESTAMP, ×tamp)) { std::string date; if (convertTimestampToTimeAndDate(timestamp, &triplet, &date)) { createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_TIME_STAMP, triplet, 1.0f); createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_DATE_STAMP, date.c_str()); } } // GPS processing method if (getCameraParam(params, CameraParameters::KEY_GPS_PROCESSING_METHOD, &stringValue)) { std::vector<unsigned char> data; // Because this is a tag with an undefined format it has to be prefixed // with the encoding type. Insert an ASCII prefix first, then the // actual string. Undefined tags do not have to be null terminated. data.insert(data.end(), std::begin(kAsciiPrefix), std::end(kAsciiPrefix)); data.insert(data.end(), stringValue, stringValue + strlen(stringValue)); createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_PROCESSING_METHOD, &data[0], data.size()); } return exifData; }
void exif_data_load_data (ExifData *data, const unsigned char *d_orig, unsigned int ds_orig) { unsigned int l; ExifLong offset; ExifShort n; const unsigned char *d = d_orig; unsigned int ds = ds_orig, len; if (!data || !data->priv || !d || !ds) return; exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", "Parsing %i byte(s) EXIF data...\n", ds); /* * It can be that the data starts with the EXIF header. If it does * not, search the EXIF marker. */ if (ds < 6) { LOG_TOO_SMALL; return; } if (!memcmp (d, ExifHeader, 6)) { exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", "Found EXIF header."); } else { while (1) { while ((d[0] == 0xff) && ds) { d++; ds--; } /* JPEG_MARKER_SOI */ if (d[0] == JPEG_MARKER_SOI) { d++; ds--; continue; } /* JPEG_MARKER_APP0 */ if (d[0] == JPEG_MARKER_APP0) { d++; ds--; l = (d[0] << 8) | d[1]; if (l > ds) return; d += l; ds -= l; continue; } /* JPEG_MARKER_APP1 */ if (d[0] == JPEG_MARKER_APP1) break; /* Unknown marker or data. Give up. */ exif_log (data->priv->log, EXIF_LOG_CODE_CORRUPT_DATA, "ExifData", _("EXIF marker not found.")); return; } d++; ds--; if (ds < 2) { LOG_TOO_SMALL; return; } len = (d[0] << 8) | d[1]; exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", "We have to deal with %i byte(s) of EXIF data.", len); d += 2; ds -= 2; } /* * Verify the exif header * (offset 2, length 6). */ if (ds < 6) { LOG_TOO_SMALL; return; } if (memcmp (d, ExifHeader, 6)) { exif_log (data->priv->log, EXIF_LOG_CODE_CORRUPT_DATA, "ExifData", _("EXIF header not found.")); return; } exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", "Found EXIF header."); /* Byte order (offset 6, length 2) */ if (ds < 14) return; if (!memcmp (d + 6, "II", 2)) data->priv->order = EXIF_BYTE_ORDER_INTEL; else if (!memcmp (d + 6, "MM", 2)) data->priv->order = EXIF_BYTE_ORDER_MOTOROLA; else { exif_log (data->priv->log, EXIF_LOG_CODE_CORRUPT_DATA, "ExifData", _("Unknown encoding.")); return; } /* Fixed value */ if (exif_get_short (d + 8, data->priv->order) != 0x002a) return; /* IFD 0 offset */ offset = exif_get_long (d + 10, data->priv->order); exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", "IFD 0 at %i.", (int) offset); /* Parse the actual exif data (usually offset 14 from start) */ exif_data_load_data_content (data, EXIF_IFD_0, d + 6, ds - 6, offset, 0); /* IFD 1 offset */ if (offset + 6 + 2 > ds) { return; } n = exif_get_short (d + 6 + offset, data->priv->order); if (offset + 6 + 2 + 12 * n + 4 > ds) { return; } offset = exif_get_long (d + 6 + offset + 2 + 12 * n, data->priv->order); if (offset) { exif_log (data->priv->log, EXIF_LOG_CODE_DEBUG, "ExifData", "IFD 1 at %i.", (int) offset); /* Sanity check. */ if (offset > ds - 6) { exif_log (data->priv->log, EXIF_LOG_CODE_CORRUPT_DATA, "ExifData", "Bogus offset of IFD1."); } else { exif_data_load_data_content (data, EXIF_IFD_1, d + 6, ds - 6, offset, 0); } } /* * If we got an EXIF_TAG_MAKER_NOTE, try to interpret it. Some * cameras use pointers in the maker note tag that point to the * space between IFDs. Here is the only place where we have access * to that data. */ interpret_maker_note(data, d, ds); /* Fixup tags if requested */ if (data->priv->options & EXIF_DATA_OPTION_FOLLOW_SPECIFICATION) exif_data_fix (data); }
static int Dfix (lua_State *L) { /** data:fix() */ ExifData *data = checkdata(L); exif_data_fix(data); return 0; }
static int write_exif( Write *write ) { #ifdef HAVE_EXIF unsigned char *data; size_t data_length; unsigned int idl; ExifData *ed; /* Either parse from the embedded EXIF, or if there's none, make * some fresh EXIF we can write the resolution to. */ if( vips_image_get_typeof( write->in, VIPS_META_EXIF_NAME ) ) { if( vips_image_get_blob( write->in, VIPS_META_EXIF_NAME, (void *) &data, &data_length ) ) return( -1 ); if( !(ed = exif_data_new_from_data( data, data_length )) ) return( -1 ); } else { ed = exif_data_new(); exif_data_set_option( ed, EXIF_DATA_OPTION_FOLLOW_SPECIFICATION ); exif_data_set_data_type( ed, EXIF_DATA_TYPE_COMPRESSED ); exif_data_set_byte_order( ed, EXIF_BYTE_ORDER_INTEL ); /* Create the mandatory EXIF fields with default data. */ exif_data_fix( ed ); } /* Update EXIF tags from the image metadata. */ vips_exif_update( ed, write->in ); /* Update EXIF resolution from the vips image header. */ if( vips__set_exif_resolution( ed, write->in ) ) { exif_data_free( ed ); return( -1 ); } /* Update EXIF image dimensions from the vips image header. */ if( set_exif_dimensions( ed, write->in ) ) { exif_data_free( ed ); return( -1 ); } /* Update EXIF orientation from the vips image header. */ if( set_exif_orientation( ed, write->in ) ) { exif_data_free( ed ); return( -1 ); } /* Reserialise and write. exif_data_save_data() returns an int for some * reason. */ exif_data_save_data( ed, &data, &idl ); if( !idl ) { vips_error( "VipsJpeg", "%s", _( "error saving EXIF" ) ); exif_data_free( ed ); return( -1 ); } data_length = idl; #ifdef DEBUG printf( "write_exif: attaching %zd bytes of EXIF\n", data_length ); #endif /*DEBUG*/ exif_data_free( ed ); jpeg_write_marker( &write->cinfo, JPEG_APP0 + 1, data, data_length ); free( data ); #else /*!HAVE_EXIF*/ /* No libexif ... just copy the embedded EXIF over. */ if( write_blob( write, VIPS_META_EXIF_NAME, JPEG_APP0 + 1 ) ) return( -1 ); #endif /*!HAVE_EXIF*/ return( 0 ); }