mirror of
https://github.com/darlinghq/darling-cocotron.git
synced 2024-10-07 09:33:28 +00:00
Add JFIF metadata to the EXIF decoder (which is actually more a JPEG metadata decoder) - needed to get the proper DPI info
This commit is contained in:
parent
e168c0496f
commit
5a6bbb7b81
@ -7,6 +7,7 @@
|
||||
//
|
||||
|
||||
#import "O2EXIFDecoder.h"
|
||||
#import "O2ImageSource.h"
|
||||
|
||||
@implementation O2EXIFDecoder
|
||||
|
||||
@ -18,6 +19,7 @@ enum {
|
||||
kEXIF,
|
||||
kInterop,
|
||||
kGPS,
|
||||
kJFIF,
|
||||
kModes
|
||||
};
|
||||
|
||||
@ -374,6 +376,9 @@ static unsigned long _getLong(const unsigned char*data, size_t offset, bool bige
|
||||
case kEXIF:
|
||||
typeKey = @"{Exif}";
|
||||
break;
|
||||
case kJFIF:
|
||||
typeKey = @"{JFIF}";
|
||||
break;
|
||||
default:
|
||||
typeKey = [NSString stringWithFormat:@"{%d}", type];
|
||||
break;
|
||||
@ -393,7 +398,8 @@ static unsigned long _getLong(const unsigned char*data, size_t offset, bool bige
|
||||
info++;
|
||||
}
|
||||
if (numberKey == nil) {
|
||||
numberKey = [NSString stringWithFormat:@"<%d>", tagNumber];
|
||||
// We'll ignore unexpected tags - but we could add them anyway
|
||||
// numberKey = [NSString stringWithFormat:@"<%d>", tagNumber];
|
||||
}
|
||||
return numberKey;
|
||||
}
|
||||
@ -409,6 +415,17 @@ static unsigned long _getLong(const unsigned char*data, size_t offset, bool bige
|
||||
}
|
||||
}
|
||||
|
||||
- (id)tagForTypeKey:(id)typeKey numberKey:(id)numberKey
|
||||
{
|
||||
if (typeKey && numberKey) {
|
||||
return [[_tags objectForKey:typeKey] objectForKey:numberKey];
|
||||
} else if (numberKey) {
|
||||
return [_tags objectForKey:numberKey];
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setTagForTypeKey:(id)typeKey numberKey:(id)numberKey value:(id)tagValue
|
||||
{
|
||||
NSMutableDictionary *dict = _tags;
|
||||
@ -497,9 +514,9 @@ static unsigned long _getLong(const unsigned char*data, size_t offset, bool bige
|
||||
}
|
||||
else {
|
||||
val = [NSMutableArray arrayWithCapacity:count];
|
||||
for (int i = 0; i < count; ++i) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
value = _getShort(rawValue, i*2, bigendian);
|
||||
[val addObject:[NSNumber numberWithShort:value]];
|
||||
[val addObject:[NSNumber numberWithShort:value]];
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -605,6 +622,86 @@ static unsigned long _getLong(const unsigned char*data, size_t offset, bool bige
|
||||
return _getLong(data, base+offset, bigendian);
|
||||
}
|
||||
|
||||
- (void)_decodeJFIF:(const uint8_t *)bytes length:(int)length
|
||||
{
|
||||
if (length < 12) {
|
||||
return;
|
||||
}
|
||||
if (bcmp(bytes, "JFIF\0", 5)) {
|
||||
return;
|
||||
}
|
||||
BOOL bigendian = YES;
|
||||
size_t offset = 5;
|
||||
uint8_t versionH = _getByte(bytes, offset, bigendian);
|
||||
offset += 1;
|
||||
uint8_t versionL = _getByte(bytes, offset, bigendian);
|
||||
offset += 1;
|
||||
uint8_t density = _getByte(bytes, offset, bigendian);
|
||||
offset += 1;
|
||||
uint16_t xResolution = _getShort(bytes, offset, bigendian);
|
||||
offset += 2;
|
||||
uint16_t yResolution = _getShort(bytes, offset, bigendian);
|
||||
offset += 2;
|
||||
|
||||
id typekey = [self keyForType:kJFIF];
|
||||
[self setTagForTypeKey:typekey numberKey:@"DensityUnit" value:[NSNumber numberWithInt:density]];
|
||||
[self setTagForTypeKey:typekey numberKey:@"XDensity" value:[NSNumber numberWithInt:xResolution]];
|
||||
[self setTagForTypeKey:typekey numberKey:@"YDensity" value:[NSNumber numberWithInt:yResolution]];
|
||||
[self setTagForTypeKey:typekey numberKey:@"JFIFVersion" value:[NSArray arrayWithObjects:
|
||||
[NSNumber numberWithInt:versionH],
|
||||
[NSNumber numberWithInt:versionL],
|
||||
nil]];
|
||||
float DPIWidth = 0;
|
||||
float DPIHeight = 0;
|
||||
if (density == 1 || density == 2) {
|
||||
DPIWidth = xResolution;
|
||||
DPIHeight = yResolution;
|
||||
if (density == 2) {
|
||||
// pixels/cm
|
||||
DPIWidth /= 2.54;
|
||||
DPIHeight /= 2.54;
|
||||
}
|
||||
}
|
||||
// Set the global DPI settings to the one from the JFIF
|
||||
if (DPIWidth > 0) {
|
||||
[self setTagForTypeKey:nil numberKey:kO2ImagePropertyDPIWidth value:[NSNumber numberWithFloat:DPIWidth]];
|
||||
}
|
||||
if (DPIHeight > 0) {
|
||||
[self setTagForTypeKey:nil numberKey:kO2ImagePropertyDPIHeight value:[NSNumber numberWithFloat:DPIHeight]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_decodeEXIF:(const uint8_t *)bytes length:(int)length
|
||||
{
|
||||
if (length < 6) {
|
||||
return;
|
||||
}
|
||||
if (bcmp(bytes, "Exif\0\0", 6)) {
|
||||
return;
|
||||
}
|
||||
bytes += 6;
|
||||
length -= 6;
|
||||
short endianness = _getShort(bytes, 0, true);
|
||||
BOOL bigendian = (endianness == 0x4d4d);
|
||||
|
||||
short align = _getShort(bytes, 2, bigendian);
|
||||
if (align != 0x2a) {
|
||||
return;
|
||||
}
|
||||
|
||||
long offsetIFD0 = _getLong(bytes, 4, bigendian);
|
||||
|
||||
long offset = 0;
|
||||
if (offsetIFD0 && offsetIFD0 != -1) {
|
||||
offset = [self _readIFD:bytes base:0 offset:offsetIFD0 bigendian:bigendian mode:0];
|
||||
if (offset && offset != -1) {
|
||||
long offset2 = [self _readIFD:bytes base:0 offset:offset bigendian:bigendian mode:1];
|
||||
if (offset2) {
|
||||
offset = offset2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_analyze:(const uint8_t *)data length:(size_t)length
|
||||
{
|
||||
@ -617,10 +714,8 @@ static unsigned long _getLong(const unsigned char*data, size_t offset, bool bige
|
||||
return; // ERROR: File is not a JPEG
|
||||
}
|
||||
|
||||
// Scan for the EXIF block
|
||||
bool done = false;
|
||||
|
||||
while (!done) {
|
||||
// Scan for the EXIF & JFIF blocks
|
||||
while (1) {
|
||||
int marker;
|
||||
|
||||
// First, skip any non 0xFF bytes
|
||||
@ -657,61 +752,75 @@ static unsigned long _getLong(const unsigned char*data, size_t offset, bool bige
|
||||
break;
|
||||
}
|
||||
if (marker == 0xE1) {
|
||||
done = true;
|
||||
break;
|
||||
[self _decodeEXIF:data+index length:length];
|
||||
}
|
||||
|
||||
if (marker == 0xE0) {
|
||||
[self _decodeJFIF:data+index length:length];
|
||||
}
|
||||
|
||||
index += len;
|
||||
}
|
||||
|
||||
if (done) {
|
||||
const uint8_t* bytes = data+index;
|
||||
if (length < 6) {
|
||||
return;
|
||||
}
|
||||
if (bcmp(bytes, "Exif\0\0", 6)) {
|
||||
return;
|
||||
}
|
||||
bytes += 6;
|
||||
length -= 6;
|
||||
short endianness = _getShort(bytes, 0, true);
|
||||
BOOL bigendian = (endianness == 0x4d4d);
|
||||
|
||||
short align = _getShort(bytes, 2, bigendian);
|
||||
if (align != 0x2a) {
|
||||
return;
|
||||
}
|
||||
|
||||
long offsetIFD0 = _getLong(bytes, 4, bigendian);
|
||||
|
||||
|
||||
if (offsetIFD0 && offsetIFD0 != -1) {
|
||||
long offset = [self _readIFD:bytes base:0 offset:offsetIFD0 bigendian:bigendian mode:0];
|
||||
if (offset && offset != -1) {
|
||||
offset = [self _readIFD:bytes base:0 offset:offset bigendian:bigendian mode:1];
|
||||
|
||||
// promote some TIFF & EXIF keys to the main level (orientation, dpi, pixel size...)
|
||||
id value = [self tagForKey:kTIFF number:kTIFF_TIFFOrientation];
|
||||
if (value) {
|
||||
[self setTagForTypeKey:nil numberKey:@"Orientation" value:value];
|
||||
}
|
||||
|
||||
// Try to use the EXIF one if none is set by the JFIF block - else, update the EXIF one so
|
||||
// everything is consistent - that's what Quartz is doing
|
||||
value = [self tagForKey:kTIFF number:kTIFF_TIFFXResolution];
|
||||
if (value) {
|
||||
id currentValue = [self tagForTypeKey:nil numberKey:kO2ImagePropertyDPIWidth];
|
||||
if (currentValue == nil) {
|
||||
int unit = [[self tagForKey:kTIFF number:kTIFF_TIFFResolutionUnit] intValue];
|
||||
if (unit == 2 || unit == 3) {
|
||||
float dpi = [value floatValue];
|
||||
if (unit == 3) {
|
||||
// pixels/cm
|
||||
dpi /= 2.54;
|
||||
}
|
||||
[self setTagForTypeKey:nil numberKey:kO2ImagePropertyDPIWidth value:[NSNumber numberWithFloat:dpi]];
|
||||
}
|
||||
} else {
|
||||
// Update the TIFF info
|
||||
[self setTagForType:kTIFF number:kTIFF_TIFFXResolution value:currentValue];
|
||||
// Set the resolution unit to inch
|
||||
[self setTagForType:kTIFF number:kTIFF_TIFFResolutionUnit value:[NSNumber numberWithInt:2]];
|
||||
}
|
||||
// promote some TIFF & EXIF keys to the main level (orientation, dpi, pixel size...)
|
||||
id value = [self tagForKey:kTIFF number:kTIFF_TIFFOrientation];
|
||||
if (value) {
|
||||
[self setTagForTypeKey:nil numberKey:@"Orientation" value:value];
|
||||
}
|
||||
value = [self tagForKey:kTIFF number:kTIFF_TIFFXResolution];
|
||||
if (value) {
|
||||
[self setTagForTypeKey:nil numberKey:@"DPIWidth" value:value];
|
||||
}
|
||||
value = [self tagForKey:kTIFF number:kTIFF_TIFFYResolution];
|
||||
if (value) {
|
||||
[self setTagForTypeKey:nil numberKey:@"DPIHeight" value:value];
|
||||
}
|
||||
value = [self tagForKey:kEXIF number:kEXIF_PixelXDimension];
|
||||
if (value) {
|
||||
[self setTagForTypeKey:nil numberKey:@"PixelWidth" value:value];
|
||||
}
|
||||
value = [self tagForKey:kEXIF number:kEXIF_PixelYDimension];
|
||||
if (value) {
|
||||
[self setTagForTypeKey:nil numberKey:@"PixelHeight" value:value];
|
||||
}
|
||||
|
||||
value = [self tagForKey:kTIFF number:kTIFF_TIFFYResolution];
|
||||
if (value) {
|
||||
id currentValue = [self tagForTypeKey:0 numberKey:kO2ImagePropertyDPIHeight];
|
||||
if (currentValue == nil) {
|
||||
int unit = [[self tagForKey:kTIFF number:kTIFF_TIFFResolutionUnit] intValue];
|
||||
if (unit == 2 || unit == 3) {
|
||||
float dpi = [value floatValue];
|
||||
if (unit == 3) {
|
||||
// pixels/cm
|
||||
dpi /= 2.54;
|
||||
}
|
||||
[self setTagForTypeKey:nil numberKey:kO2ImagePropertyDPIHeight value:[NSNumber numberWithFloat:dpi]];
|
||||
}
|
||||
} else {
|
||||
// Update the TIFF info
|
||||
[self setTagForType:kTIFF number:kTIFF_TIFFYResolution value:currentValue];
|
||||
// Set the resolution unit to inch
|
||||
[self setTagForType:kTIFF number:kTIFF_TIFFResolutionUnit value:[NSNumber numberWithInt:2]];
|
||||
}
|
||||
}
|
||||
|
||||
value = [self tagForKey:kEXIF number:kEXIF_PixelXDimension];
|
||||
if (value) {
|
||||
[self setTagForTypeKey:nil numberKey:kO2ImagePropertyPixelWidth value:value];
|
||||
}
|
||||
value = [self tagForKey:kEXIF number:kEXIF_PixelYDimension];
|
||||
if (value) {
|
||||
[self setTagForTypeKey:nil numberKey:kO2ImagePropertyPixelHeight value:value];
|
||||
}
|
||||
}
|
||||
|
||||
- (id)initWithBytes:(const uint8_t *)s length:(size_t)length
|
||||
|
@ -11,8 +11,8 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
|
||||
#import <Onyx2D/O2DataProvider.h>
|
||||
#import <Onyx2D/O2Exceptions.h>
|
||||
|
||||
NSString *kO2ImagePropertyDPIWidth=@"kCGImagePropertyDPIWidth";
|
||||
NSString *kO2ImagePropertyDPIHeight=@"kCGImagePropertyDPIHeight";
|
||||
NSString *kO2ImagePropertyDPIWidth=@"DPIWidth";
|
||||
NSString *kO2ImagePropertyDPIHeight=@"DPIHeight";
|
||||
NSString *kO2ImagePropertyPixelHeight=@"PixelHeight";
|
||||
NSString *kO2ImagePropertyPixelWidth=@"PixelWidth";
|
||||
NSString *kO2ImagePropertyOrientation=@"Orientation";
|
||||
|
Loading…
Reference in New Issue
Block a user