forked from psemiletov/tea-qt
-
Notifications
You must be signed in to change notification settings - Fork 0
/
exif_reader.cpp
378 lines (287 loc) · 11.8 KB
/
exif_reader.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
#include <stdlib.h>
#include <QtGlobal>
#include <QList>
#include <QDebug>
#include <QByteArray>
#include "exif_reader.h"
rint8u readByte (QFile &file)
{
char a;
file.getChar (&a);
return (rint8u)a;
}
//--------------------------------------------------------------------------
// Parse the marker stream until SOS or EOI is seen;
//--------------------------------------------------------------------------
int Exif::readJpegSections (QFile &file, int *Orientation)
{
QByteArray *data;
rint8u a = readByte (file);
if (a != 0xff)
return -1;
else
{
rint8u b = readByte (file);
if (b != M_SOI)
return -1;
}
//int SectionsRead=0;
for (;;)
{
int itemlen;
int prev;
rint8u marker = 0;
rint8u ll, lh;
prev = 0;
for (int i = 0; ; i++)
{
marker = readByte (file);
if (marker != 0xff && prev == 0xff)
break;
prev = marker;
}
// Read the length of the section.
lh = readByte (file);
ll = readByte (file);
itemlen = (lh << 8) | ll;
if (itemlen < 2) // Invalid marker
return -1;
data = new QByteArray (file.read (itemlen - 2)); // Read the whole section.
if (data->isEmpty())
return -1; // Could not allocate memory
if(data->size() != itemlen - 2)
return -1; // Premature end of file?
switch (marker)
{
case M_SOS: // stop before hitting compressed data
return 0;
case M_EOI: // in case it's a tables-only JPEG stream
return -1;
case M_COM: // Comment section
delete (data);
break;
case M_JFIF:
// Regular jpegs always have this tag, exif images have the exif
// marker instead, althogh ACDsee will write images with both markers.
// this program will re-create this marker on absence of exif marker.
// hence no need to keep the copy from the file.
if (itemlen >= 16){ // if Jfif header not too short
// skipped
}
delete (data);
break;
case M_EXIF:
// There can be different section using the same marker.
if (data->left(4) == "Exif")
{
processEXIF (data, itemlen, Orientation);
break;
}
// Oterwise, discard this section.
delete (data);
break;
case M_IPTC:
delete (data);
break;
default:
// Skip any other sections.
break;
} // switch
} // for(;;)
return 0;
}
// Convert a 16 bit unsigned value from file's native byte order
int Get16u (const void * Short, int MotorolaOrder)
{
if (MotorolaOrder)
return (((uchar *)Short)[0] << 8) | ((uchar *)Short)[1];
else
return (((uchar *)Short)[1] << 8) | ((uchar *)Short)[0];
}
// Convert a 32 bit signed value from file's native byte order
int Get32s(const void * Long, int MotorolaOrder)
{
if (MotorolaOrder)
return ((( char *)Long)[0] << 24) | (((uchar *)Long)[1] << 16)
| (((uchar *)Long)[2] << 8 ) | (((uchar *)Long)[3] << 0 );
else
return ((( char *)Long)[3] << 24) | (((uchar *)Long)[2] << 16)
| (((uchar *)Long)[1] << 8 ) | (((uchar *)Long)[0] << 0 );
}
// Convert a 32 bit unsigned value from file's native byte order
unsigned Get32u (const void * Long, int MotorolaOrder)
{
return (unsigned)Get32s(Long, MotorolaOrder) & 0xffffffff;
}
#define NUM_FORMATS 12
#define FMT_BYTE 1
#define FMT_STRING 2
#define FMT_USHORT 3
#define FMT_ULONG 4
#define FMT_URATIONAL 5
#define FMT_SBYTE 6
#define FMT_UNDEFINED 7
#define FMT_SSHORT 8
#define FMT_SLONG 9
#define FMT_SRATIONAL 10
#define FMT_SINGLE 11
#define FMT_DOUBLE 12
// Evaluate number, be it int, rational, or float from directory.
double ConvertAnyFormat (const void * ValuePtr, int Format, int MotorolaOrder)
{
double Value = 0.0;
switch (Format)
{
case FMT_SBYTE: Value = *(signed char *)ValuePtr;
break;
case FMT_BYTE: Value = *(uchar *)ValuePtr;
break;
case FMT_USHORT: Value = Get16u(ValuePtr, MotorolaOrder); break;
case FMT_ULONG: Value = Get32u(ValuePtr, MotorolaOrder); break;
case FMT_URATIONAL:
case FMT_SRATIONAL:
{
int Num = Get32s(ValuePtr, MotorolaOrder);
int Den = Get32s(4+(char *)ValuePtr, MotorolaOrder);
if (Den == 0)
Value = 0;
else
Value = (double) Num / Den;
break;
}
case FMT_SSHORT: Value = (signed short)Get16u(ValuePtr, MotorolaOrder);
break;
case FMT_SLONG: Value = Get32s(ValuePtr, MotorolaOrder);
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;
default: Value = 100;// Illegal format code
}
return Value;
}
#define TAG_ORIENTATION 0x0112
#define TAG_INTEROP_OFFSET 0xA005
#define TAG_EXIF_OFFSET 0x8769
#define DIR_ENTRY_ADDR(Start, Entry) (Start+2+12*(Entry))
const int BytesPerFormat[] = {0,1,1,2,4,8,1,1,2,4,8,4,8};
// Process one of the nested EXIF directories.
int Exif::processEXIFDir (const char *DirStart, const char *OffsetBase, rint32u exifSize, rint32u nesting, int MotorolaOrder, int *NumOrientations, int *Orientation)
{
int numDirEntries;
if(nesting > 4)
return -1; // Maximum Exif directory nesting exceeded (corrupt Exif header)
numDirEntries = Get16u (DirStart, MotorolaOrder);
//qDebug() << "num entries: " << numDirEntries;
for (int de=0; de<numDirEntries; de++)
{
const char *ValuePtr;
//const char *DirEntry = DIR_ENTRY_ADDR(DirStart, de);
const char *DirEntry = DirStart + 2 + (12 * de);
int Tag = Get16u(DirEntry, MotorolaOrder);
int Format = Get16u(DirEntry+2, MotorolaOrder);
int Components = Get32u(DirEntry+4, MotorolaOrder);
//qDebug() << "tag:" << Tag << "format:" << Format << "components:" << Components;
if (Format-1 >= NUM_FORMATS)
continue; // (-1) catches illegal zero case as unsigned underflows to positive large.
if ((unsigned)Components > 0x10000)
continue; // Too many components
int ByteCount = Components * BytesPerFormat[Format];
//qDebug() << "byte count" << ByteCount;
if (ByteCount > 4)
{ // If its bigger than 4 bytes, the dir entry contains an offset.
unsigned OffsetVal = Get32u (DirEntry + 8, MotorolaOrder);
if (OffsetVal+ByteCount > exifSize)
continue; // Bogus pointer offset and / or bytecount value
ValuePtr = OffsetBase + OffsetVal;
}
else // 4 bytes or less and value is in the dir entry itself
ValuePtr = DirEntry+8;
// Extract useful components of tag
switch (Tag) {
case TAG_ORIENTATION:
if (*NumOrientations >= 2)
// Can have another orientation tag for the thumbnail, but if there's
// a third one, things are stringae.
break;
if (*NumOrientations == 0)
*Orientation = (int)ConvertAnyFormat(ValuePtr, Format, MotorolaOrder);
//qDebug() << "orientation:" << *Orientation;
if (*Orientation < 0 || *Orientation > 8)
// Undefined rotation value
*Orientation = 0;
*NumOrientations += 1;
break;
case TAG_EXIF_OFFSET:
case TAG_INTEROP_OFFSET:
const char *SubdirStart = OffsetBase + Get32u(ValuePtr, MotorolaOrder);
if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ exifSize)
; // Illegal Exif or interop ofset directory link
else
processEXIFDir (SubdirStart, OffsetBase, exifSize, nesting+ 1, MotorolaOrder, NumOrientations, Orientation);
}
}
return 0;
}
// Process a EXIF marker
// Describes all the drivel that most digital cameras include...
int Exif::processEXIF(QByteArray *data, int itemlen, int *Orientation)
{
int MotorolaOrder = 0;
// Check the EXIF header component
if (data->left(6) == "Exif\0\0")
qDebug() << data->left(4);
if(data->mid(6,2) == "II") // Exif section in Intel order
//qDebug() << data->mid(6,2);
MotorolaOrder = 0;
else
{
if(data->mid(6,2) == "II") // Exif section in Motorola order
//qDebug() << data->mid(6,2);
MotorolaOrder = 1;
else
return -1; // Invalid Exif alignment marker.
}
// get first offset
QByteArray ttt (data->mid (10, 4));
const char *ttt2 = ttt.constData();
rint32u FirstOffset = Get32u (ttt2, MotorolaOrder);
//qDebug() << "fist offset: " << FirstOffset;
if (FirstOffset < 8 || FirstOffset > 16)
if (FirstOffset < 16 || int (FirstOffset) > itemlen - 16)
return -1; // invalid offset for first Exif IFD value ;
const char *dirStart = data->constData();
const char *offsetBase = data->constData();
dirStart += 6 + FirstOffset;
offsetBase += 6;
int numOrientations = 0;
// First directory starts 16 bytes in. All offset are relative to 8 bytes in.
processEXIFDir (dirStart, offsetBase, itemlen - 8, 0, MotorolaOrder, &numOrientations, Orientation);
//qDebug() << "num orientations:" << numOrientations;
return 0;
}
int Exif::readJpegFile (QFile &file, int *Orientation)
{
readJpegSections (file, Orientation);
return 0;
}
int Exif::getExifOrientation (QFile &file)
{
int r = 0;
readJpegFile (file, &r);
return r;
}
int get_exif_orientation (const QString &fname)
{
Exif exif;
int o = 0;
QFile file (fname);
if (file.open(QIODevice::ReadOnly))
{
o = exif.getExifOrientation(file/*, &o*/);
file.close();
}
return o;
}