This commit is contained in:
Oleg Korshul
2020-10-15 21:04:15 +03:00
parent 251a3e58b0
commit 5f0f6ec9bc
2 changed files with 433 additions and 0 deletions

View File

@ -257,6 +257,7 @@ bool CxImageJPG::CxExifInfo::process_EXIF(uint8_t * CharBuf, uint32_t length)
uint8_t * LastExifRefd = CharBuf;
#if 0
/* First directory starts 16 bytes in. Offsets start at 8 bytes in. */
if (!ProcessExifDir(CharBuf+14, CharBuf+6, length-6, m_exifinfo, &LastExifRefd))
return false;
@ -266,6 +267,18 @@ bool CxImageJPG::CxExifInfo::process_EXIF(uint8_t * CharBuf, uint32_t length)
if (!ProcessExifDir(CharBuf+14+FirstOffset-8, CharBuf+6, length-6, m_exifinfo, &LastExifRefd))
return false;
}
#else
CSafeReader reader(CharBuf, length);
/* First directory starts 16 bytes in. Offsets start at 8 bytes in. */
if (!ProcessExifDir2(reader.Offset(14), reader.Offset(6), length-6, m_exifinfo, &LastExifRefd))
return false;
/* <Richard Collins> give a chance for a second directory */
if (FirstOffset > 8) {
if (!ProcessExifDir2(reader.Offset(14+FirstOffset-8), reader.Offset(6), length-6, m_exifinfo, &LastExifRefd))
return false;
}
#endif
/* This is how far the interesting (non thumbnail) part of the exif went. */
// int32_t ExifSettingsLength = LastExifRefd - CharBuf;
@ -703,6 +716,322 @@ bool CxImageJPG::CxExifInfo::ProcessExifDir(uint8_t * DirStart, uint8_t * Offset
return true;
}
bool CxImageJPG::CxExifInfo::ProcessExifDir2(CSafeReader& DirStart, CSafeReader& OffsetBase, unsigned ExifLength,
EXIFINFO * const m_exifinfo, uint8_t ** const LastExifRefdP, int32_t NestingLevel)
{
int32_t de;
int32_t a;
int32_t NumDirEntries;
unsigned ThumbnailOffset = 0;
unsigned ThumbnailSize = 0;
if (NestingLevel > 4){
strcpy(m_szLastError,"Maximum directory nesting exceeded (corrupt exif header)");
return false;
}
NumDirEntries = DirStart.Check(2) ? Get16u(DirStart.GetData(0)) : 0;
if ((DirStart.GetData(2+NumDirEntries*12+2)) > (OffsetBase.GetData(ExifLength))){
strcpy(m_szLastError,"Illegally sized directory");
return false;
}
for (de=0;de<NumDirEntries;de++){
int32_t Tag, Format, Components;
CSafeReader ValuePtr;
/* This actually can point to a variety of things; it must be
cast to other types when used. But we use it as a byte-by-byte
cursor, so we declare it as a pointer to a generic byte here.
*/
int32_t ByteCount;
CSafeReader DirEntry = DirStart.Offset(2+12*de);
Tag = DirEntry.Check(2) ? Get16u(DirEntry.GetData(0)) : 0;
Format = DirEntry.Check(2, 2) ? Get16u(DirEntry.GetData(2)) : 0;
Components = DirEntry.Check(4, 4) ? Get32u(DirEntry.GetData(4)) : 0;
if ((Format-1) >= NUM_FORMATS) {
/* (-1) catches illegal zero case as unsigned underflows to positive large */
strcpy(m_szLastError,"Illegal format code in EXIF dir");
return false;
}
ByteCount = Components * BytesPerFormat[Format];
if (ByteCount > 4){
unsigned OffsetVal;
OffsetVal = DirEntry.Check(8, 4) ? Get32u(DirEntry.GetData(8)) : 0;
/* If its bigger than 4 bytes, the dir entry contains an offset.*/
if (OffsetVal+ByteCount > ExifLength){
/* Bogus pointer offset and / or bytecount value */
strcpy(m_szLastError,"Illegal pointer offset value in EXIF.");
return false;
}
ValuePtr = OffsetBase.Offset(OffsetVal);
}else{
/* 4 bytes or less and value is in the dir entry itself */
ValuePtr = DirEntry.Offset(8);
}
if (*LastExifRefdP < ValuePtr.GetData(ByteCount)){
/* Keep track of last byte in the exif header that was
actually referenced. That way, we know where the
discardable thumbnail data begins.
*/
*LastExifRefdP = ValuePtr.GetData(ByteCount);
}
/* Extract useful components of tag */
switch(Tag){
case TAG_MAKE:
if (ValuePtr.Check(31)) strncpy(m_exifinfo->CameraMake, (char*)ValuePtr.GetData(0), 31);
break;
case TAG_MODEL:
if (ValuePtr.Check(39)) strncpy(m_exifinfo->CameraModel, (char*)ValuePtr.GetData(0), 39);
break;
case TAG_EXIF_VERSION:
if (ValuePtr.Check(4)) strncpy(m_exifinfo->Version,(char*)ValuePtr.GetData(0), 4);
break;
case TAG_DATETIME_ORIGINAL:
if (ValuePtr.Check(19)) strncpy(m_exifinfo->DateTime, (char*)ValuePtr.GetData(0), 19);
break;
case TAG_USERCOMMENT:
// Olympus has this padded with trailing spaces. Remove these first.
if (ValuePtr.Check(ByteCount))
{
for (a=ByteCount;;){
a--;
if (*((char*)ValuePtr.GetData(a)) == ' '){
*((char*)ValuePtr.GetData(a)) = '\0';
}else{
break;
}
if (a == 0) break;
}
}
/* Copy the comment */
if (ValuePtr.Check(5) && memcmp(ValuePtr.GetData(0), "ASCII",5) == 0){
for (a=5;a<10;a++){
char c;
c = *((char*)ValuePtr.GetData(a));
if (c != '\0' && c != ' '){
if (ValuePtr.Check(a, 199))
strncpy(m_exifinfo->Comments, (char*)ValuePtr.GetData(a), 199);
break;
}
}
}else{
if (ValuePtr.Check(199))
strncpy(m_exifinfo->Comments, (char*)ValuePtr.GetData(0), 199);
}
break;
case TAG_FNUMBER:
/* Simplest way of expressing aperture, so I trust it the most.
(overwrite previously computd value if there is one)
*/
m_exifinfo->ApertureFNumber = (float)ConvertAnyFormat2(ValuePtr, Format);
break;
case TAG_APERTURE:
case TAG_MAXAPERTURE:
/* More relevant info always comes earlier, so only
use this field if we don't have appropriate aperture
information yet.
*/
if (m_exifinfo->ApertureFNumber == 0){
m_exifinfo->ApertureFNumber = (float)exp(ConvertAnyFormat2(ValuePtr, Format)*log(2.0f)*0.5);
}
break;
case TAG_BRIGHTNESS:
m_exifinfo->Brightness = (float)ConvertAnyFormat2(ValuePtr, Format);
break;
case TAG_FOCALLENGTH:
/* Nice digital cameras actually save the focal length
as a function of how farthey are zoomed in.
*/
m_exifinfo->FocalLength = (float)ConvertAnyFormat2(ValuePtr, Format);
break;
case TAG_SUBJECT_DISTANCE:
/* Inidcates the distacne the autofocus camera is focused to.
Tends to be less accurate as distance increases.
*/
m_exifinfo->Distance = (float)ConvertAnyFormat2(ValuePtr, Format);
break;
case TAG_EXPOSURETIME:
/* Simplest way of expressing exposure time, so I
trust it most. (overwrite previously computd value
if there is one)
*/
m_exifinfo->ExposureTime =
(float)ConvertAnyFormat2(ValuePtr, Format);
break;
case TAG_SHUTTERSPEED:
/* More complicated way of expressing exposure time,
so only use this value if we don't already have it
from somewhere else.
*/
if (m_exifinfo->ExposureTime == 0){
m_exifinfo->ExposureTime = (float)
(1/exp(ConvertAnyFormat2(ValuePtr, Format)*log(2.0f)));
}
break;
case TAG_FLASH:
if ((int32_t)ConvertAnyFormat2(ValuePtr, Format) & 7){
m_exifinfo->FlashUsed = 1;
}else{
m_exifinfo->FlashUsed = 0;
}
break;
case TAG_ORIENTATION:
m_exifinfo->Orientation = (int32_t)ConvertAnyFormat2(ValuePtr, Format);
if (m_exifinfo->Orientation < 1 || m_exifinfo->Orientation > 8){
strcpy(m_szLastError,"Undefined rotation value");
m_exifinfo->Orientation = 0;
}
break;
case TAG_EXIF_IMAGELENGTH:
case TAG_EXIF_IMAGEWIDTH:
/* Use largest of height and width to deal with images
that have been rotated to portrait format.
*/
a = (int32_t)ConvertAnyFormat2(ValuePtr, Format);
if (ExifImageWidth < a) ExifImageWidth = a;
break;
case TAG_FOCALPLANEXRES:
m_exifinfo->FocalplaneXRes = (float)ConvertAnyFormat2(ValuePtr, Format);
break;
case TAG_FOCALPLANEYRES:
m_exifinfo->FocalplaneYRes = (float)ConvertAnyFormat2(ValuePtr, Format);
break;
case TAG_RESOLUTIONUNIT:
switch((int32_t)ConvertAnyFormat2(ValuePtr, Format)){
case 1: m_exifinfo->ResolutionUnit = 1.0f; break; /* 1 inch */
case 2: m_exifinfo->ResolutionUnit = 1.0f; break;
case 3: m_exifinfo->ResolutionUnit = 0.3937007874f; break; /* 1 centimeter*/
case 4: m_exifinfo->ResolutionUnit = 0.03937007874f; break; /* 1 millimeter*/
case 5: m_exifinfo->ResolutionUnit = 0.00003937007874f; /* 1 micrometer*/
}
break;
case TAG_FOCALPLANEUNITS:
switch((int32_t)ConvertAnyFormat2(ValuePtr, Format)){
case 1: m_exifinfo->FocalplaneUnits = 1.0f; break; /* 1 inch */
case 2: m_exifinfo->FocalplaneUnits = 1.0f; break;
case 3: m_exifinfo->FocalplaneUnits = 0.3937007874f; break; /* 1 centimeter*/
case 4: m_exifinfo->FocalplaneUnits = 0.03937007874f; break; /* 1 millimeter*/
case 5: m_exifinfo->FocalplaneUnits = 0.00003937007874f; /* 1 micrometer*/
}
break;
// Remaining cases contributed by: Volker C. Schoech <schoech(at)gmx(dot)de>
case TAG_EXPOSURE_BIAS:
m_exifinfo->ExposureBias = (float) ConvertAnyFormat2(ValuePtr, Format);
break;
case TAG_WHITEBALANCE:
m_exifinfo->Whitebalance = (int32_t)ConvertAnyFormat2(ValuePtr, Format);
break;
case TAG_METERING_MODE:
m_exifinfo->MeteringMode = (int32_t)ConvertAnyFormat2(ValuePtr, Format);
break;
case TAG_EXPOSURE_PROGRAM:
m_exifinfo->ExposureProgram = (int32_t)ConvertAnyFormat2(ValuePtr, Format);
break;
case TAG_ISO_EQUIVALENT:
m_exifinfo->ISOequivalent = (int32_t)ConvertAnyFormat2(ValuePtr, Format);
if ( m_exifinfo->ISOequivalent < 50 ) m_exifinfo->ISOequivalent *= 200;
break;
case TAG_COMPRESSION_LEVEL:
m_exifinfo->CompressionLevel = (int32_t)ConvertAnyFormat2(ValuePtr, Format);
break;
case TAG_XRESOLUTION:
m_exifinfo->Xresolution = (float)ConvertAnyFormat2(ValuePtr, Format);
break;
case TAG_YRESOLUTION:
m_exifinfo->Yresolution = (float)ConvertAnyFormat2(ValuePtr, Format);
break;
case TAG_THUMBNAIL_OFFSET:
ThumbnailOffset = (unsigned)ConvertAnyFormat2(ValuePtr, Format);
break;
case TAG_THUMBNAIL_LENGTH:
ThumbnailSize = (unsigned)ConvertAnyFormat2(ValuePtr, Format);
break;
}
if (Tag == TAG_EXIF_OFFSET || Tag == TAG_INTEROP_OFFSET){
unsigned Offset = ValuePtr.Check(4) ? Get32u(ValuePtr.GetData(0)) : 0;
if (Offset>8){
if (!OffsetBase.Check(Offset))
{
strcpy(m_szLastError,"Illegal subdirectory link");
return false;
}
ProcessExifDir2(OffsetBase.Offset(Offset), OffsetBase, ExifLength, m_exifinfo, LastExifRefdP, NestingLevel+1);
}
continue;
}
}
{
/* In addition to linking to subdirectories via exif tags,
there's also a potential link to another directory at the end
of each directory. This has got to be the result of a
committee!
*/
unsigned Offset;
Offset = DirStart.Check(2+12*NumDirEntries, 2) ? Get16u(DirStart.GetData(2+12*NumDirEntries)) : 0;
if (Offset){
if (!OffsetBase.Check(Offset))
{
strcpy(m_szLastError,"Illegal subdirectory link");
return false;
}
ProcessExifDir2(OffsetBase.Offset(Offset), OffsetBase, ExifLength, m_exifinfo, LastExifRefdP, NestingLevel+1);
}
}
if (ThumbnailSize && ThumbnailOffset){
if (ThumbnailSize + ThumbnailOffset <= ExifLength){
/* The thumbnail pointer appears to be valid. Store it. */
m_exifinfo->ThumbnailPointer = OffsetBase.GetData(ThumbnailOffset);
m_exifinfo->ThumbnailSize = ThumbnailSize;
}
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
/*--------------------------------------------------------------------------
Evaluate number, be it int32_t, rational, or float from directory.
@ -743,6 +1072,58 @@ double CxImageJPG::CxExifInfo::ConvertAnyFormat(void * ValuePtr, int32_t Format)
}
return Value;
}
double CxImageJPG::CxExifInfo::ConvertAnyFormat2(CSafeReader& reader, int32_t Format)
{
double Value;
Value = 0;
switch(Format){
case FMT_SBYTE:
case FMT_BYTE: if (!reader.Check(1)) {return Value;} break;
case FMT_USHORT:
case FMT_SSHORT: if (!reader.Check(2)) {return Value;} break;
case FMT_ULONG:
case FMT_SLONG: if (!reader.Check(4)) {return Value;} break;
case FMT_URATIONAL:
case FMT_SRATIONAL: if (!reader.Check(8)) {return Value;} break;
case FMT_SINGLE: if (!reader.Check(sizeof(float))) {return Value;} break;
case FMT_DOUBLE: if (!reader.Check(sizeof(double))) {return Value;} break;
default:
break;
}
void * ValuePtr = (void*)reader.GetData(0);
switch(Format){
case FMT_SBYTE: Value = *(signed char *)ValuePtr; break;
case FMT_BYTE: Value = *(uint8_t *)ValuePtr; break;
case FMT_USHORT: Value = Get16u(ValuePtr); break;
case FMT_ULONG: Value = Get32u(ValuePtr); break;
case FMT_URATIONAL:
case FMT_SRATIONAL:
{
int32_t Num,Den;
Num = Get32s(ValuePtr);
Den = Get32s(4+(char *)ValuePtr);
if (Den == 0){
Value = 0;
}else{
Value = (double)Num/Den;
}
break;
}
case FMT_SSHORT: Value = (int16_t)Get16u(ValuePtr); break;
case FMT_SLONG: Value = Get32s(ValuePtr); break;
/* Not sure if this is correct (never seen float used in Exif format)
*/
case FMT_SINGLE: Value = (double)*(float *)ValuePtr; break;
case FMT_DOUBLE: Value = *(double *)ValuePtr; break;
}
return Value;
}
////////////////////////////////////////////////////////////////////////////////
void CxImageJPG::CxExifInfo::process_COM (const uint8_t * Data, int32_t length)
{

View File

@ -101,6 +101,55 @@ typedef struct tag_Section_t{
unsigned Size;
} Section_t;
public:
class CSafeReader
{
private:
uint8_t* data;
unsigned int len;
public:
CSafeReader(uint8_t* _data = NULL, unsigned int _len = 0)
{
data = _data;
len = _len;
}
CSafeReader(const CSafeReader& src)
{
data = src.data;
len = src.len;
}
CSafeReader& operator=(const CSafeReader& src)
{
data = src.data;
len = src.len;
return *this;
}
CSafeReader Offset(unsigned int offset)
{
if (offset > len)
offset = len;
CSafeReader reader(data, len);
reader.data += offset;
reader.len -= offset;
return reader;
}
bool Check(unsigned int size)
{
if (len >= size)
return true;
return false;
}
bool Check(unsigned int offset, unsigned int size)
{
return Check(offset + size);
}
uint8_t* GetData(unsigned int offset)
{
return data + offset;
}
};
public:
EXIFINFO* m_exifinfo;
char m_szLastError[256];
@ -118,9 +167,12 @@ protected:
int32_t Get32s(void * Long);
uint32_t Get32u(void * Long);
double ConvertAnyFormat(void * ValuePtr, int32_t Format);
double ConvertAnyFormat2(CSafeReader& reader, int32_t Format);
void* FindSection(int32_t SectionType);
bool ProcessExifDir(uint8_t * DirStart, uint8_t * OffsetBase, unsigned ExifLength,
EXIFINFO * const pInfo, uint8_t ** const LastExifRefdP, int32_t NestingLevel=0);
bool ProcessExifDir2(CSafeReader& DirStart, CSafeReader& OffsetBase, unsigned ExifLength,
EXIFINFO * const pInfo, uint8_t ** const LastExifRefdP, int32_t NestingLevel=0);
int32_t ExifImageWidth;
int32_t MotorolaOrder;
Section_t Sections[MAX_SECTIONS];