From 8da4803c71d9fba0fb5a70c8cc5feccbaf4fdac0 Mon Sep 17 00:00:00 2001 From: Cyril Hrubis Date: Sat, 2 Jun 2012 00:08:53 +0200 Subject: [PATCH] loaders: Added Exif to MetaData parser. --- demos/c_simple/meta_data.c | 4 +- include/loaders/GP_MetaData.h | 23 +- libs/loaders/GP_JPG.c | 17 +- libs/loaders/GP_MetaData.c | 57 ++++- libs/loaders/GP_MetaExif.c | 487 ++++++++++++++++++++++++++++++++++++++++++ libs/loaders/GP_PNG.c | 4 +- 6 files changed, 566 insertions(+), 26 deletions(-) create mode 100644 libs/loaders/GP_MetaExif.c diff --git a/demos/c_simple/meta_data.c b/demos/c_simple/meta_data.c index b5f29051..746f6b76 100644 --- a/demos/c_simple/meta_data.c +++ b/demos/c_simple/meta_data.c @@ -60,8 +60,8 @@ int main(void) * The last parameter says, if the string should be duplicated * in the metadata storage. */ - GP_MetaDataCreateString(data, "author", "Foo Bar ", 1); - GP_MetaDataCreateString(data, "comment", "Created in hurry.", 1); + GP_MetaDataCreateString(data, "author", "Foo Bar ", 0, 1); + GP_MetaDataCreateString(data, "comment", "Created in hurry.", 0, 1); GP_MetaDataCreateDouble(data, "pi", 3.141592); const char *ret; diff --git a/include/loaders/GP_MetaData.h b/include/loaders/GP_MetaData.h index 4da30f18..2c529d2a 100644 --- a/include/loaders/GP_MetaData.h +++ b/include/loaders/GP_MetaData.h @@ -23,18 +23,25 @@ #ifndef LOADERS_METADATA_H #define LOADERS_METADATA_H -#define GP_META_RECORD_ID_MAX 16 +#define GP_META_RECORD_ID_MAX 32 enum GP_MetaType { GP_META_INT, GP_META_STRING, GP_META_DOUBLE, + GP_META_RATIONAL, +}; + +struct GP_MetaRational { + int num; + int den; }; union GP_MetaValue { int i; double d; const char *str; + struct GP_MetaRational r; }; typedef struct GP_MetaRecord { @@ -98,6 +105,9 @@ GP_MetaRecord *GP_MetaDataCreateRecord(GP_MetaData *self, const char *id); */ GP_MetaRecord *GP_MetaDataCreateInt(GP_MetaData *self, const char *id, int val); +GP_MetaRecord *GP_MetaDataCreateRat(GP_MetaData *self, const char *id, + int num, int den); + /* * Creates an double record and returns pointer to it. */ @@ -107,10 +117,19 @@ GP_MetaRecord *GP_MetaDataCreateDouble(GP_MetaData *self, const char *id, /* * Creates an string record and returns pointer to it. * + * If len == 0, string is copied to the terminating '\0', otherwise len + * characters is copied. This has no effect if dup == 0. + * * If dup is set to 1, the string is duplicated inside of the MetaData * structure, otherwise only the pointer is saved. */ GP_MetaRecord *GP_MetaDataCreateString(GP_MetaData *self, const char *id, - const char *str, int dup); + const char *str, int len, int dup); + +/* + * Parses Exif data from passed buffer. The start of the buffer must point to + * the ASCII 'Exif' string. + */ +int GP_MetaDataFromExif(GP_MetaData *self, void *buf, size_t buf_len); #endif /* LOADERS_GP_METADATA_H */ diff --git a/libs/loaders/GP_JPG.c b/libs/loaders/GP_JPG.c index f600b57c..72e4c86c 100644 --- a/libs/loaders/GP_JPG.c +++ b/libs/loaders/GP_JPG.c @@ -214,24 +214,15 @@ static void read_jpg_metadata(struct jpeg_decompress_struct *cinfo, for (marker = cinfo->marker_list; marker != NULL; marker = marker->next) { switch (marker->marker) { - case JPEG_COM: { - char buf[JPEG_COM_MAX+1]; - - memcpy(buf, marker->data, marker->data_length); - buf[marker->data_length] = 0; - - /* Strip newline at the end of the commment */ - if (buf[marker->data_length-1] == '\n') - buf[marker->data_length-1] = 0; - - GP_MetaDataCreateString(data, "comment", buf, 1); - } + case JPEG_COM: + GP_MetaDataCreateString(data, "comment", (void*)marker->data, + marker->data_length, 1); break; case JPEG_APP0: GP_DEBUG(0, "TODO: JFIF"); break; case JPEG_APP0 + 1: - GP_DEBUG(0, "TODO: EXIF"); + GP_MetaDataFromExif(data, marker->data, marker->data_length); break; } } diff --git a/libs/loaders/GP_MetaData.c b/libs/loaders/GP_MetaData.c index e62af188..1403e018 100644 --- a/libs/loaders/GP_MetaData.c +++ b/libs/loaders/GP_MetaData.c @@ -29,6 +29,7 @@ struct GP_MetaData { struct GP_MetaRecord *root; + struct GP_MetaRecord *last; unsigned int rec_count; size_t size; size_t free; @@ -63,6 +64,7 @@ GP_MetaData *GP_MetaDataCreate(unsigned int expected_records) } data->root = NULL; + data->last = NULL; data->rec_count = 0; data->size = size; data->free = size; @@ -76,6 +78,7 @@ void GP_MetaDataClear(GP_MetaData *self) self, self->rec_count); self->root = NULL; + self->last = NULL; self->rec_count = 0; self->free = self->size; } @@ -93,12 +96,15 @@ void GP_MetaDataPrint(GP_MetaData *self) printf("MetaData %u record(s)\n", self->rec_count); for (rec = self->root; rec != NULL; rec = rec->next) { - printf("%-16s: ", rec->id); + printf("%-32s: ", rec->id); switch (rec->type) { case GP_META_INT: printf("%i\n", rec->val.i); break; + case GP_META_RATIONAL: + printf("%i/%i\n", rec->val.r.num, rec->val.r.den); + break; case GP_META_STRING: printf("'%s'\n", rec->val.str); break; @@ -153,9 +159,16 @@ static GP_MetaRecord *record_create(GP_MetaData *self, const char *id, strcpy(rec->id, id); rec->hash = hash; - rec->next = self->root; - self->root = rec; - + + if (self->root == NULL) { + self->root = rec; + self->last = rec; + } else { + self->last->next = rec; + self->last = rec; + rec->next = NULL; + } + self->rec_count++; return rec; @@ -263,6 +276,30 @@ GP_MetaRecord *GP_MetaDataCreateInt(GP_MetaData *self, const char *id, int val) return rec; } +GP_MetaRecord *GP_MetaDataCreateRat(GP_MetaData *self, const char *id, + int num, int den) +{ + GP_MetaRecord *rec; + + GP_DEBUG(2, "Creating GP_META_RATIONAL id '%s' = %i/%i", id, num, den); + + if (den == 0) { + GP_DEBUG(1, "Would not create '%s' with denominator == 0", id); + return NULL; + } + + rec = GP_MetaDataCreateRecord(self, id); + + if (rec == NULL) + return NULL; + + rec->type = GP_META_RATIONAL; + rec->val.r.num = num; + rec->val.r.den = den; + + return rec; +} + GP_MetaRecord *GP_MetaDataCreateDouble(GP_MetaData *self, const char *id, double val) { @@ -282,7 +319,7 @@ GP_MetaRecord *GP_MetaDataCreateDouble(GP_MetaData *self, const char *id, } GP_MetaRecord *GP_MetaDataCreateString(GP_MetaData *self, const char *id, - const char *str, int dup) + const char *str, int len, int dup) { GP_MetaRecord *rec; @@ -294,8 +331,13 @@ GP_MetaRecord *GP_MetaDataCreateString(GP_MetaData *self, const char *id, return NULL; if (dup) { - size_t size = strlen(str) + 1; + size_t size; char *s; + + if (len == 0) + size = strlen(str) + 1; + else + size = len + 1; /* Play safe with aligment */ if (size % 8) @@ -303,7 +345,8 @@ GP_MetaRecord *GP_MetaDataCreateString(GP_MetaData *self, const char *id, //TODO: allocation error s = do_alloc(self, size); - strcpy(s, str); + strncpy(s, str, size - 1); + s[size - 1] = '\0'; str = s; } diff --git a/libs/loaders/GP_MetaExif.c b/libs/loaders/GP_MetaExif.c new file mode 100644 index 00000000..e6a89bc4 --- /dev/null +++ b/libs/loaders/GP_MetaExif.c @@ -0,0 +1,487 @@ +/***************************************************************************** + * This file is part of gfxprim library. * + * * + * Gfxprim is free software; you can redistribute it and/or * + * modify it under the terms of the GNU Lesser General Public * + * License as published by the Free Software Foundation; either * + * version 2.1 of the License, or (at your option) any later version. * + * * + * Gfxprim is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with gfxprim; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, * + * Boston, MA 02110-1301 USA * + * * + * Copyright (C) 2009-2012 Cyril Hrubis * + * * + *****************************************************************************/ + +#include +#include + +#include "core/GP_Debug.h" + +#include "GP_MetaData.h" + +enum IFD_formats { + /* 1 bytes/components */ + IFD_UNSIGNED_BYTE = 0x01, + /* 1 bytes/components */ + IFD_ASCII_STRING = 0x02, + /* 2 bytes/components */ + IFD_UNSIGNED_SHORT = 0x03, + /* 4 bytes/components */ + IFD_UNSIGNED_LONG = 0x04, + /* 8 bytes/components */ + IFD_UNSIGNED_RATIONAL = 0x05, + /* 1 bytes/components */ + IFD_SIGNED_BYTE = 0x06, + /* 1 bytes/components */ + IFD_UNDEFINED = 0x07, + /* 2 bytes/components */ + IFD_SIGNED_SHORT = 0x08, + /* 4 bytes/components */ + IFD_SIGNED_LONG = 0x09, + /* 8 bytes/components */ + IFD_SIGNED_RATIONAL = 0x0a, + /* 4 bytes/components */ + IFD_SINGLE_FLOAT = 0x0b, + /* 8 bytes/components */ + IFD_SINGLE_DOUBLE = 0x0c, + IFD_FORMAT_LAST = IFD_SINGLE_DOUBLE, +}; + +static const char *IFD_format_names[] = { + "Unsigned Byte", + "ASCII String", + "Unsigned Short", + "Unsigned Long", + "Unsigned Rational", + "Signed Byte", + "Undefined", + "Signed Short", + "Signed Long", + "Signed Rational", + "Single Float", + "Double Float", +}; + +enum IFD_tags { + /* image description */ + IFD_IMAGE_DESCRIPTION = 0x010e, + /* camera manufacturer */ + IFD_MAKE = 0x010f, + /* camera model */ + IFD_MODEL = 0x0110, + /* 1 = upper left, 3 = lower right, 6 = upper right, * + * 8 = lower left, 9 = undefined */ + IFD_ORIENTATION = 0x0112, + /* x resolution */ + IFD_X_RESOLUTION = 0x011a, + /* y resolution */ + IFD_Y_RESOLUTION = 0x011b, + /* 1 = no unit, 2 = inch, 3 = centimeter */ + IFD_RESOLUTION_UNIT = 0x0128, + /* software */ + IFD_SOFTWARE = 0x0131, + /* date time */ + IFD_DATE_TIME = 0x0132, + /* white point */ + IFD_WHITE_POINT = 0x013e, + /* primary chromaticies */ + IFD_PRIMARY_CHROMATICIES = 0x013f, + /* YCbCr coefficients */ + IFD_Y_CB_CR_COEFFICIENTS = 0x0211, + /* YCbCr positioning */ + IFD_Y_CB_CR_POSITIONING = 0x0213, + /* reference black white */ + IFD_REFERENCE_BLACK_WHITE = 0x0214, + /* copyright */ + IFD_COPYRIGHT = 0x8298, + /* exif offset */ + IFD_EXIF_OFFSET = 0x8769, + + /* TAGs from Exif SubIFD */ + + IFD_EXPOSURE_TIME = 0x829a, + /* Actual F-Number of lens when image was taken */ + IFD_F_NUMBER = 0x829d, + /* 1 manual, 2 normal, 3 aperture priority, 4 shutter priority, * + * 5 creative (slow), 6 action (high-speed), 7 portrait mode, * + * 8 landscape mode */ + IFD_EXPOSURE_PROGRAM = 0x8822, + /* CCD sensitivity */ + IFD_ISO_SPEED_RATINGS = 0x8827, + /* ASCII 4 byte Exif version */ + IFD_EXIF_VERSION = 0x9000, + /* Original time should not be modified by user program */ + IFD_DATE_TIME_ORIGINAL = 0x9003, + IFD_DATE_TIME_DIGITIZED = 0x9004, + /* Undefined commonly 0x00, 0x01, 0x02, 0x03 */ + IFD_COMPONENT_CONFIGURATION = 0x9101, + /* Average compression ration */ + IFD_COMPRESSED_BITS_PER_PIXEL = 0x9102, + /* Shutter speed as 1 / (2 ^ val) */ + IFD_SHUTTER_SPEED_VALUE = 0x9201, + /* Aperture to convert to F-Number do 1.4142 ^ val */ + IFD_APERTURE_VALUE = 0x9202, + /* Brightness in EV */ + IFD_BRIGHTNESS_VALUE = 0x9203, + /* Exposure bias in EV */ + IFD_EXPOSURE_BIAS_VALUE = 0x9204, + /* Max Aperture in the same format as IFD_APERTURE_VALUE */ + IFD_MAX_APERTURE_VALUE = 0x9205, + /* Distance to focus point in meters */ + IFD_SUBJECT_DISTANCE = 0x9206, + /* Exposure metering method, 1 average, 2 center weighted average, * + * 3 spot, 4 multi-spot, 5 multi-segment */ + IFD_METERING_MODE = 0x9207, + /* White balance 0 auto, 1 daylight, 2 flourescent, 3 tungsten, 10 flash */ + IFD_LIGHT_SOURCE = 0x9208, + /* 0 off, 1 on */ + IFD_FLASH = 0x9209, + /* Focal length in milimeters */ + IFD_FOCAL_LENGTH = 0x920a, + /* Maker note, undefined may be IFD block */ + IFD_MAKER_NOTE = 0x927c, + /* Comment */ + IFD_USER_COMMENT = 0x9286, +}; + +struct IFD_tag { + uint16_t tag; + const char *name; + uint16_t format; + /* 0 == not defined */ + uint32_t num_components; +}; + +/* These are sorted by tag */ +static const struct IFD_tag IFD_tags[] = { + /* TAGs from IFD0 */ + {IFD_IMAGE_DESCRIPTION, "Image Description", IFD_ASCII_STRING, 0}, + {IFD_MAKE, "Camera Manufacturer", IFD_ASCII_STRING, 0}, + {IFD_MODEL, "Camera Model", IFD_ASCII_STRING, 0}, + {IFD_ORIENTATION, "Orientation", IFD_UNSIGNED_SHORT, 1}, + {IFD_X_RESOLUTION, "X Resolution", IFD_UNSIGNED_RATIONAL, 1}, + {IFD_Y_RESOLUTION, "Y Resolution", IFD_UNSIGNED_RATIONAL, 1}, + {IFD_RESOLUTION_UNIT, "Resolution Unit", IFD_UNSIGNED_SHORT, 1}, + {IFD_SOFTWARE, "Software", IFD_ASCII_STRING, 0}, + {IFD_DATE_TIME, "Date Time", IFD_ASCII_STRING, 20}, + {IFD_WHITE_POINT, "White Point", IFD_UNSIGNED_RATIONAL, 2}, + {IFD_PRIMARY_CHROMATICIES, "Primary Chromaticies", IFD_UNSIGNED_RATIONAL, 6}, + {IFD_Y_CB_CR_COEFFICIENTS, "YCbCr Conefficients", IFD_UNSIGNED_RATIONAL, 3}, + {IFD_Y_CB_CR_POSITIONING, "YCbCr Positioning", IFD_UNSIGNED_SHORT, 1}, + {IFD_REFERENCE_BLACK_WHITE, "Reference Black White", IFD_UNSIGNED_RATIONAL, 6}, + {IFD_COPYRIGHT, "Copyright", IFD_ASCII_STRING, 0}, + + /* TAGs from Exif SubIFD */ + {IFD_EXPOSURE_TIME, "Exposure Time", IFD_UNSIGNED_RATIONAL, 1}, + {IFD_F_NUMBER, "F-Number", IFD_UNSIGNED_RATIONAL, 1}, + + /* TAG from IFD0 */ + {IFD_EXIF_OFFSET, "Exif Offset", IFD_UNSIGNED_LONG, 1}, + + /* TAGs from Exif SubIFD */ + {IFD_EXPOSURE_PROGRAM, "Exposure Program", IFD_UNSIGNED_SHORT, 1}, + {IFD_ISO_SPEED_RATINGS, "ISO Speed Ratings", IFD_UNSIGNED_SHORT, 2}, + {IFD_EXIF_VERSION, "Exif Version", IFD_UNDEFINED, 4}, + {IFD_DATE_TIME_ORIGINAL, "Date Time Original", IFD_ASCII_STRING, 20}, + {IFD_DATE_TIME_DIGITIZED, "Date Time Digitized", IFD_ASCII_STRING, 20}, + {IFD_COMPONENT_CONFIGURATION, "Component Configuration", IFD_UNDEFINED, 0}, + {IFD_COMPRESSED_BITS_PER_PIXEL, "Compressed Bits Per Pixel", IFD_UNSIGNED_RATIONAL, 1}, + {IFD_SHUTTER_SPEED_VALUE, "Shutter Speed", IFD_SIGNED_RATIONAL, 1}, + {IFD_APERTURE_VALUE, "Aperture", IFD_UNSIGNED_RATIONAL, 1}, + {IFD_BRIGHTNESS_VALUE, "Brightness", IFD_SIGNED_RATIONAL, 1}, + {IFD_EXPOSURE_BIAS_VALUE, "Exposure Bias", IFD_SIGNED_RATIONAL, 1}, + {IFD_MAX_APERTURE_VALUE, "Max Aperture", IFD_UNSIGNED_RATIONAL, 1}, + {IFD_SUBJECT_DISTANCE, "Subject Distance", IFD_SIGNED_RATIONAL, 1}, + {IFD_METERING_MODE, "Metering Mode", IFD_UNSIGNED_SHORT, 1}, + {IFD_LIGHT_SOURCE, "Light Source", IFD_UNSIGNED_SHORT, 1}, + {IFD_FLASH, "Flash", IFD_UNSIGNED_SHORT, 1}, + {IFD_FOCAL_LENGTH, "Focal Length", IFD_UNSIGNED_RATIONAL, 1}, + {IFD_MAKER_NOTE, "Maker Note", IFD_UNDEFINED, 0}, + {IFD_USER_COMMENT, "User Comment", IFD_UNDEFINED, 0}, +}; + +static const char *IFD_format_name(uint16_t format) +{ + if (format == 0 || format > IFD_FORMAT_LAST) + return "Unknown"; + + return IFD_format_names[format - 1]; +} + +static const struct IFD_tag *IFD_tag_get(uint16_t tag) +{ + int left = 0; + int right = sizeof(IFD_tags)/sizeof(struct IFD_tag) - 1; + + while (right - left > 1) { + int middle = (right + left)/2; + + if (IFD_tags[middle].tag == tag) + return &IFD_tags[middle]; + + + if (IFD_tags[middle].tag > tag) + right = middle; + else + left = middle; + } + + if (IFD_tags[left].tag == tag) + return &IFD_tags[left]; + + if (IFD_tags[right].tag == tag) + return &IFD_tags[right]; + + return NULL; +} + +static const char *IFD_tag_name(uint16_t tag) +{ + const struct IFD_tag *res = IFD_tag_get(tag); + + if (res == NULL) + return "Unknown"; + else + return res->name; +} + +static int buf_char(void *buf, size_t pos, size_t buf_len) +{ + if (pos >= buf_len) { + GP_DEBUG(1, "Byte position %zu out of buffer len %zu", pos, buf_len); + return -1; + } + + return ((char*)buf)[pos]; +} + +#define GET_16(res, buf, pos, buf_len, swap) do { \ + if (pos + 1 >= buf_len) { \ + GP_DEBUG(1, "2-byte position %zu out of buffer len %zu", \ + (size_t)pos, buf_len); \ + return -1; \ + } \ + \ + if (swap) \ + res = ((uint8_t*)buf)[pos]<<8 | ((uint8_t*)buf)[pos+1]; \ + else \ + res = ((uint8_t*)buf)[pos] | ((uint8_t*)buf)[pos+1]<<8; \ +} while (0) + +#define GET_32(res, buf, pos, buf_len, swap) do { \ + if (pos + 3 >= buf_len) { \ + GP_DEBUG(1, "4-byte position %zu out of buffer len %zu", \ + (size_t)pos, buf_len); \ + return -1; \ + } \ + \ + if (swap) \ + res = (((uint8_t*)buf)[pos])<<24 | (((uint8_t*)buf)[pos+1])<<16 | \ + (((uint8_t*)buf)[pos+2])<<8 | ((uint8_t*)buf)[pos+3]; \ + else \ + res = ((uint8_t*)buf)[pos] | (((uint8_t*)buf)[pos+1])<<8 | \ + (((uint8_t*)buf)[pos+2])<<16 | (((uint8_t*)buf)[pos+3])<<24; \ +} while (0) + +#define GET_16_INC(res, buf, pos, buf_len, swap) do { \ + GET_16(res, buf, pos, buf_len, swap); \ + pos += 2; \ +} while (0) + +#define GET_32_INC(res, buf, pos, buf_len, swap) do { \ + GET_32(res, buf, pos, buf_len, swap); \ + pos += 4; \ +} while (0) + +static const char *get_string(void *buf, size_t buf_len, + uint32_t num_comp, uint32_t *val) +{ + if (num_comp <= 4) + return (const char*)val; + + if (*val + num_comp >= buf_len) { + GP_DEBUG(1, "String out of buffer offset 0x%08x length %u", + *val, num_comp); + return NULL; + } + + return ((const char*)buf) + *val; +} + +static int rat_num(void *buf, uint32_t offset, size_t buf_len, int swap) +{ + int ret; + + GET_32(ret, buf, offset, buf_len, swap); + + return ret; +} + +static int rat_den(void *buf, uint32_t offset, size_t buf_len, int swap) +{ + int ret; + + GET_32(ret, buf, offset + 4, buf_len, swap); + + return ret; +} + +static void load_tag(GP_MetaData *self, void *buf, size_t buf_len, int swap, + uint16_t tag, uint16_t format, + uint32_t num_comp, uint32_t val) +{ + const struct IFD_tag *res = IFD_tag_get(tag); + + if (res == NULL) { + GP_DEBUG(1, "Skipping unknown IFD tag 0x%02x", tag); + return; + } + + if (res->format != format) { + GP_DEBUG(1, "Unexpected tag '%s' format '%s' (0x%02x) " + "expected '%s'", res->name, + IFD_format_name(format), format, + IFD_format_name(res->format)); + } + + if ((res->num_components != 0) && + (res->num_components != num_comp)) { + GP_DEBUG(1, "Unexpected tag '%s' num_components %u expected %u", + res->name, num_comp, res->num_components); + } + + switch (format) { + case IFD_ASCII_STRING: { + const char *addr = get_string(buf, buf_len, num_comp, &val); + + if (addr == NULL) + return; + + GP_MetaDataCreateString(self, res->name, addr, num_comp, 1); + } break; + case IFD_UNSIGNED_SHORT: + if (num_comp == 1) + GP_MetaDataCreateInt(self, res->name, val); + else + goto unused; + break; + case IFD_UNSIGNED_RATIONAL: + case IFD_SIGNED_RATIONAL: + if (num_comp == 1) + GP_MetaDataCreateRat(self, res->name, + rat_num(buf, val, buf_len, swap), + rat_den(buf, val, buf_len, swap)); + else + goto unused; + break; + unused: + default: + GP_DEBUG(1, "Unused record '%s' format '%s' (0x%02x)", res->name, + IFD_format_name(format), format); + } +} + +/* + * Loads IFD block. + */ +static int load_IFD(GP_MetaData *self, void *buf, size_t buf_len, + uint32_t IFD_offset, int swap) +{ + uint16_t IFD_entries_count; + + GET_16_INC(IFD_entries_count, buf, IFD_offset, buf_len, swap); + + GP_DEBUG(2, "-- IFD Offset 0x%08x Entries 0x%04x --", + IFD_offset, IFD_entries_count); + + int i; + + for (i = 0; i < IFD_entries_count; i++) { + uint16_t tag, format; + uint32_t num_components, val; + + GET_16_INC(tag, buf, IFD_offset, buf_len, swap); + GET_16_INC(format, buf, IFD_offset, buf_len, swap); + GET_32_INC(num_components, buf, IFD_offset, buf_len, swap); + GET_32_INC(val, buf, IFD_offset, buf_len, swap); + + GP_DEBUG(3, "IFD Entry tag 0x%04x format (0x%04x) components 0x%08x val 0x%08x", + tag, format, num_components, val); + + GP_DEBUG(3, "IFD Entry tag '%s' format '%s'", + IFD_tag_name(tag), IFD_format_name(format)); + + if (tag == IFD_EXIF_OFFSET) + load_IFD(self, buf, buf_len, val, swap); + else + load_tag(self, buf, buf_len, swap, tag, format, num_components, val); + } +/* + GET_32(IFD_offset, buf, IFD_offset, buf_len, swap); + + if (IFD_offset != 0x00000000) + load_IFD(self, buf, buf_len, IFD_offset, swap); +*/ + return 0; +} + +/* Offset from the start of the Exit to TIFF header */ +#define TIFF_OFFSET 6 + +int GP_MetaDataFromExif(GP_MetaData *self, void *buf, size_t buf_len) +{ + static int swap = 0; + int c1, c2; + + if (buf_char(buf, 0, buf_len) != 'E' || + buf_char(buf, 1, buf_len) != 'x' || + buf_char(buf, 2, buf_len) != 'i' || + buf_char(buf, 3, buf_len) != 'f' || + buf_char(buf, 4, buf_len) != 0 || + buf_char(buf, 5, buf_len) != 0) { + GP_DEBUG(1, "Missing ASCII 'Exif\\0\\0' string at " + "the start of the buffer"); + return 1; + } + + if (((c1 = buf_char(buf, 6, buf_len)) != + (c2 = buf_char(buf, 7, buf_len))) + || (c1 != 'I' && c1 != 'M')) { + GP_DEBUG(1, "Expected II or MM got %x%x, corrupt header?", + c1, c2); + return 1; + } + + swap = (c1 == 'M'); + + GP_DEBUG(2, "TIFF aligment is '%c%c' swap = %i", c1, c1, swap); + + uint16_t tag; + + GET_16(tag, buf, 8, buf_len, swap); + + if (tag != 0x002a) { + GP_DEBUG(1, "Expected TIFF TAG '0x002a' got '0x%04x'", tag); + return 1; + } + + uint32_t IFD_offset; + + GET_32(IFD_offset, buf, 10, buf_len, swap); + + GP_DEBUG(2, "IFD offset is 0x%08x", IFD_offset); + + /* The offset starts from the II or MM */ + load_IFD(self, (char*)buf + TIFF_OFFSET, buf_len - TIFF_OFFSET, IFD_offset, swap); + + + return 0; +} diff --git a/libs/loaders/GP_PNG.c b/libs/loaders/GP_PNG.c index 5f324aa6..2d099fdb 100644 --- a/libs/loaders/GP_PNG.c +++ b/libs/loaders/GP_PNG.c @@ -267,7 +267,7 @@ static void load_meta_data(png_structp png, png_infop png_info, GP_MetaData *dat else unit_name = "unknown"; - GP_MetaDataCreateString(data, "res_unit", unit_name, 0); + GP_MetaDataCreateString(data, "res_unit", unit_name, 0, 0); } png_timep mod_time; @@ -302,7 +302,7 @@ static void load_meta_data(png_structp png, png_infop png_info, GP_MetaData *dat char buf[GP_META_RECORD_ID_MAX]; snprintf(buf, GP_META_RECORD_ID_MAX, "text:%s", text_ptr[i].key); - GP_MetaDataCreateString(data, buf, text_ptr[i].text, 1); + GP_MetaDataCreateString(data, buf, text_ptr[i].text, 0, 1); } } } -- 2.11.4.GIT