Diffstat (limited to 'noncore/graphics/opie-eye/slave/jpeg_slave.cpp') (more/less context) (show whitespace changes)
-rw-r--r-- | noncore/graphics/opie-eye/slave/jpeg_slave.cpp | 1426 |
1 files changed, 1426 insertions, 0 deletions
diff --git a/noncore/graphics/opie-eye/slave/jpeg_slave.cpp b/noncore/graphics/opie-eye/slave/jpeg_slave.cpp new file mode 100644 index 0000000..95055fd --- a/dev/null +++ b/noncore/graphics/opie-eye/slave/jpeg_slave.cpp | |||
@@ -0,0 +1,1426 @@ | |||
1 | #include "jpeg_slave.h" | ||
2 | |||
3 | #include "thumbnailtool.h" | ||
4 | |||
5 | PHUNK_VIEW_INTERFACE( "JPEG", JpegSlave ) | ||
6 | |||
7 | #include <qtopia/timestring.h> | ||
8 | #include <qobject.h> | ||
9 | #include <qimage.h> | ||
10 | |||
11 | /** | ||
12 | exif.h | ||
13 | */ | ||
14 | |||
15 | #include <stdio.h> | ||
16 | #include <stdlib.h> | ||
17 | #include <math.h> | ||
18 | #include <time.h> | ||
19 | |||
20 | #include <qstring.h> | ||
21 | #include <qfile.h> | ||
22 | #include <qimage.h> | ||
23 | |||
24 | typedef enum { | ||
25 | READ_EXIF = 1, | ||
26 | READ_IMAGE = 2, | ||
27 | READ_ALL = 3 | ||
28 | }ReadMode_t; | ||
29 | |||
30 | //-------------------------------------------------------------------------- | ||
31 | // This structure is used to store jpeg file sections in memory. | ||
32 | typedef struct { | ||
33 | uchar * Data; | ||
34 | int Type; | ||
35 | unsigned Size; | ||
36 | }Section_t; | ||
37 | |||
38 | typedef unsigned char uchar; | ||
39 | |||
40 | typedef struct { | ||
41 | unsigned short Tag; | ||
42 | const char*const Desc; | ||
43 | }TagTable_t; | ||
44 | |||
45 | #define MAX_SECTIONS 20 | ||
46 | #define PSEUDO_IMAGE_MARKER 0x123; // Extra value. | ||
47 | |||
48 | class ExifData { | ||
49 | Section_t Sections[MAX_SECTIONS]; | ||
50 | |||
51 | QString CameraMake; | ||
52 | QString CameraModel; | ||
53 | QString DateTime; | ||
54 | int Orientation; | ||
55 | int Height, Width; | ||
56 | int ExifImageLength, ExifImageWidth; | ||
57 | int IsColor; | ||
58 | int Process; | ||
59 | int FlashUsed; | ||
60 | float FocalLength; | ||
61 | float ExposureTime; | ||
62 | float ApertureFNumber; | ||
63 | float Distance; | ||
64 | int Whitebalance; | ||
65 | int MeteringMode; | ||
66 | float CCDWidth; | ||
67 | float ExposureBias; | ||
68 | int ExposureProgram; | ||
69 | int ISOequivalent; | ||
70 | int CompressionLevel; | ||
71 | QString UserComment; | ||
72 | QString Comment; | ||
73 | QImage Thumbnail; | ||
74 | |||
75 | int ReadJpegSections (QFile & infile, ReadMode_t ReadMode); | ||
76 | void DiscardData(void); | ||
77 | int Get16u(void * Short); | ||
78 | int Get32s(void * Long); | ||
79 | unsigned Get32u(void * Long); | ||
80 | double ConvertAnyFormat(void * ValuePtr, int Format); | ||
81 | void ProcessExifDir(unsigned char * DirStart, unsigned char * OffsetBase, unsigned ExifLength); | ||
82 | void process_COM (const uchar * Data, int length); | ||
83 | void process_SOFn (const uchar * Data, int marker); | ||
84 | int Get16m(const void * Short); | ||
85 | void process_EXIF(unsigned char * CharBuf, unsigned int length); | ||
86 | int Exif2tm(struct tm * timeptr, char * ExifTime); | ||
87 | |||
88 | public: | ||
89 | ExifData(); | ||
90 | bool scan(const QString &); | ||
91 | QString getCameraMake() { return CameraMake; } | ||
92 | QString getCameraModel() { return CameraModel; } | ||
93 | QString getDateTime() { return DateTime; } | ||
94 | int getOrientation() { return Orientation; } | ||
95 | int getHeight() { return Height; } | ||
96 | int getWidth() { return Width; } | ||
97 | int getIsColor() { return IsColor; } | ||
98 | int getProcess() { return Process; } | ||
99 | int getFlashUsed() { return FlashUsed; } | ||
100 | float getFocalLength() { return FocalLength; } | ||
101 | float getExposureTime() { return ExposureTime; } | ||
102 | float getApertureFNumber() { return ApertureFNumber; } | ||
103 | float getDistance() { return Distance; } | ||
104 | int getWhitebalance() { return Whitebalance; } | ||
105 | int getMeteringMode() { return MeteringMode; } | ||
106 | float getCCDWidth() { return CCDWidth; } | ||
107 | float getExposureBias() { return ExposureBias; } | ||
108 | int getExposureProgram() { return ExposureProgram; } | ||
109 | int getISOequivalent() { return ISOequivalent; } | ||
110 | int getCompressionLevel() { return CompressionLevel; } | ||
111 | QString getUserComment() { return UserComment; } | ||
112 | QString getComment() { return Comment; } | ||
113 | QImage getThumbnail(); | ||
114 | bool isThumbnailSane(); | ||
115 | bool isNullThumbnail() { return !isThumbnailSane(); } | ||
116 | }; | ||
117 | |||
118 | class FatalError { | ||
119 | const char* ex; | ||
120 | public: | ||
121 | FatalError(const char* s) { ex = s; } | ||
122 | void debug_print() const { qWarning("exception: %s", ex ); } | ||
123 | }; | ||
124 | |||
125 | |||
126 | |||
127 | static unsigned char * LastExifRefd; | ||
128 | static int ExifSettingsLength; | ||
129 | static double FocalplaneXRes; | ||
130 | static double FocalplaneUnits; | ||
131 | static int MotorolaOrder = 0; | ||
132 | static int SectionsRead; | ||
133 | //static int HaveAll; | ||
134 | |||
135 | //-------------------------------------------------------------------------- | ||
136 | // Table of Jpeg encoding process names | ||
137 | |||
138 | #define M_SOF0 0xC0 // Start Of Frame N | ||
139 | #define M_SOF1 0xC1 // N indicates which compression process | ||
140 | #define M_SOF2 0xC2 // Only SOF0-SOF2 are now in common use | ||
141 | #define M_SOF3 0xC3 | ||
142 | #define M_SOF5 0xC5 // NB: codes C4 and CC are NOT SOF markers | ||
143 | #define M_SOF6 0xC6 | ||
144 | #define M_SOF7 0xC7 | ||
145 | #define M_SOF9 0xC9 | ||
146 | #define M_SOF10 0xCA | ||
147 | #define M_SOF11 0xCB | ||
148 | #define M_SOF13 0xCD | ||
149 | #define M_SOF14 0xCE | ||
150 | #define M_SOF15 0xCF | ||
151 | #define M_SOI 0xD8 // Start Of Image (beginning of datastream) | ||
152 | #define M_EOI 0xD9 // End Of Image (end of datastream) | ||
153 | #define M_SOS 0xDA // Start Of Scan (begins compressed data) | ||
154 | #define M_JFIF 0xE0 // Jfif marker | ||
155 | #define M_EXIF 0xE1 // Exif marker | ||
156 | #define M_COM 0xFE // COMment | ||
157 | |||
158 | |||
159 | TagTable_t ProcessTable[] = { | ||
160 | { M_SOF0, "Baseline"}, | ||
161 | { M_SOF1, "Extended sequential"}, | ||
162 | { M_SOF2, "Progressive"}, | ||
163 | { M_SOF3, "Lossless"}, | ||
164 | { M_SOF5, "Differential sequential"}, | ||
165 | { M_SOF6, "Differential progressive"}, | ||
166 | { M_SOF7, "Differential lossless"}, | ||
167 | { M_SOF9, "Extended sequential, arithmetic coding"}, | ||
168 | { M_SOF10, "Progressive, arithmetic coding"}, | ||
169 | { M_SOF11, "Lossless, arithmetic coding"}, | ||
170 | { M_SOF13, "Differential sequential, arithmetic coding"}, | ||
171 | { M_SOF14, "Differential progressive, arithmetic coding"}, | ||
172 | { M_SOF15, "Differential lossless, arithmetic coding"}, | ||
173 | { 0, "Unknown"} | ||
174 | }; | ||
175 | |||
176 | |||
177 | |||
178 | //-------------------------------------------------------------------------- | ||
179 | // Describes format descriptor | ||
180 | static int BytesPerFormat[] = {0,1,1,2,4,8,1,1,2,4,8,4,8}; | ||
181 | #define NUM_FORMATS 12 | ||
182 | |||
183 | #define FMT_BYTE 1 | ||
184 | #define FMT_STRING 2 | ||
185 | #define FMT_USHORT 3 | ||
186 | #define FMT_ULONG 4 | ||
187 | #define FMT_URATIONAL 5 | ||
188 | #define FMT_SBYTE 6 | ||
189 | #define FMT_UNDEFINED 7 | ||
190 | #define FMT_SSHORT 8 | ||
191 | #define FMT_SLONG 9 | ||
192 | #define FMT_SRATIONAL 10 | ||
193 | #define FMT_SINGLE 11 | ||
194 | #define FMT_DOUBLE 12 | ||
195 | |||
196 | //-------------------------------------------------------------------------- | ||
197 | // Describes tag values | ||
198 | |||
199 | #define TAG_EXIF_OFFSET 0x8769 | ||
200 | #define TAG_INTEROP_OFFSET 0xa005 | ||
201 | |||
202 | #define TAG_MAKE 0x010F | ||
203 | #define TAG_MODEL 0x0110 | ||
204 | #define TAG_ORIENTATION 0x0112 | ||
205 | |||
206 | #define TAG_EXPOSURETIME 0x829A | ||
207 | #define TAG_FNUMBER 0x829D | ||
208 | |||
209 | #define TAG_SHUTTERSPEED 0x9201 | ||
210 | #define TAG_APERTURE 0x9202 | ||
211 | #define TAG_MAXAPERTURE 0x9205 | ||
212 | #define TAG_FOCALLENGTH 0x920A | ||
213 | |||
214 | #define TAG_DATETIME_ORIGINAL 0x9003 | ||
215 | #define TAG_USERCOMMENT 0x9286 | ||
216 | |||
217 | #define TAG_SUBJECT_DISTANCE 0x9206 | ||
218 | #define TAG_FLASH 0x9209 | ||
219 | |||
220 | #define TAG_FOCALPLANEXRES 0xa20E | ||
221 | #define TAG_FOCALPLANEUNITS 0xa210 | ||
222 | #define TAG_EXIF_IMAGEWIDTH 0xA002 | ||
223 | #define TAG_EXIF_IMAGELENGTH 0xA003 | ||
224 | |||
225 | // the following is added 05-jan-2001 vcs | ||
226 | #define TAG_EXPOSURE_BIAS 0x9204 | ||
227 | #define TAG_WHITEBALANCE 0x9208 | ||
228 | #define TAG_METERING_MODE 0x9207 | ||
229 | #define TAG_EXPOSURE_PROGRAM 0x8822 | ||
230 | #define TAG_ISO_EQUIVALENT 0x8827 | ||
231 | #define TAG_COMPRESSION_LEVEL 0x9102 | ||
232 | |||
233 | #define TAG_THUMBNAIL_OFFSET 0x0201 | ||
234 | #define TAG_THUMBNAIL_LENGTH 0x0202 | ||
235 | |||
236 | |||
237 | |||
238 | |||
239 | //-------------------------------------------------------------------------- | ||
240 | // Parse the marker stream until SOS or EOI is seen; | ||
241 | //-------------------------------------------------------------------------- | ||
242 | int ExifData::ReadJpegSections (QFile & infile, ReadMode_t ReadMode) | ||
243 | { | ||
244 | int a; | ||
245 | |||
246 | a = infile.getch(); | ||
247 | |||
248 | if (a != 0xff || infile.getch() != M_SOI) { | ||
249 | SectionsRead = 0; | ||
250 | return false; | ||
251 | } | ||
252 | for(SectionsRead = 0; SectionsRead < MAX_SECTIONS-1; ){ | ||
253 | int marker = 0; | ||
254 | int got; | ||
255 | unsigned int ll,lh; | ||
256 | unsigned int itemlen; | ||
257 | uchar * Data; | ||
258 | |||
259 | for (a=0;a<7;a++){ | ||
260 | marker = infile.getch(); | ||
261 | if (marker != 0xff) break; | ||
262 | |||
263 | if (a >= 6){ | ||
264 | |||
265 | qWarning( "too many padding bytes" ); | ||
266 | return false; | ||
267 | |||
268 | } | ||
269 | } | ||
270 | |||
271 | if (marker == 0xff){ | ||
272 | // 0xff is legal padding, but if we get that many, something's wrong. | ||
273 | return false; | ||
274 | } | ||
275 | |||
276 | Sections[SectionsRead].Type = marker; | ||
277 | |||
278 | // Read the length of the section. | ||
279 | lh = (uchar) infile.getch(); | ||
280 | ll = (uchar) infile.getch(); | ||
281 | |||
282 | itemlen = (lh << 8) | ll; | ||
283 | |||
284 | if (itemlen < 2) { | ||
285 | return false;; | ||
286 | } | ||
287 | |||
288 | Sections[SectionsRead].Size = itemlen; | ||
289 | |||
290 | Data = (uchar *)malloc(itemlen+1); // Add 1 to allow sticking a 0 at the end. | ||
291 | Sections[SectionsRead].Data = Data; | ||
292 | |||
293 | // Store first two pre-read bytes. | ||
294 | Data[0] = (uchar)lh; | ||
295 | Data[1] = (uchar)ll; | ||
296 | |||
297 | got = infile.readBlock((char*)Data+2, itemlen-2); // Read the whole section. | ||
298 | if (( unsigned ) got != itemlen-2){ | ||
299 | return false; | ||
300 | } | ||
301 | SectionsRead++; | ||
302 | |||
303 | switch(marker){ | ||
304 | |||
305 | case M_SOS: // stop before hitting compressed data | ||
306 | // If reading entire image is requested, read the rest of the data. | ||
307 | if (ReadMode & READ_IMAGE){ | ||
308 | unsigned long size; | ||
309 | |||
310 | size = QMAX( 0ul, infile.size()-infile.at() ); | ||
311 | Data = (uchar *)malloc(size); | ||
312 | if (Data == NULL){ | ||
313 | return false; | ||
314 | } | ||
315 | |||
316 | got = infile.readBlock((char*)Data, size); | ||
317 | if (( unsigned ) got != size){ | ||
318 | return false; | ||
319 | } | ||
320 | |||
321 | Sections[SectionsRead].Data = Data; | ||
322 | Sections[SectionsRead].Size = size; | ||
323 | Sections[SectionsRead].Type = PSEUDO_IMAGE_MARKER; | ||
324 | SectionsRead ++; | ||
325 | //HaveAll = 1; | ||
326 | } | ||
327 | return true; | ||
328 | |||
329 | case M_EOI: // in case it's a tables-only JPEG stream | ||
330 | qWarning( "No image in jpeg!" ); | ||
331 | return false; | ||
332 | |||
333 | case M_COM: // Comment section | ||
334 | // pieczy 2002-02-12 | ||
335 | // now the User comment goes to UserComment | ||
336 | // so we can store a Comment section also in READ_EXIF mode | ||
337 | process_COM(Data, itemlen); | ||
338 | break; | ||
339 | |||
340 | case M_JFIF: | ||
341 | // Regular jpegs always have this tag, exif images have the exif | ||
342 | // marker instead, althogh ACDsee will write images with both markers. | ||
343 | // this program will re-create this marker on absence of exif marker. | ||
344 | // hence no need to keep the copy from the file. | ||
345 | free(Sections[--SectionsRead].Data); | ||
346 | break; | ||
347 | |||
348 | case M_EXIF: | ||
349 | // Seen files from some 'U-lead' software with Vivitar scanner | ||
350 | // that uses marker 31 for non exif stuff. Thus make sure | ||
351 | // it says 'Exif' in the section before treating it as exif. | ||
352 | if ((ReadMode & READ_EXIF) && memcmp(Data+2, "Exif", 4) == 0){ | ||
353 | process_EXIF((uchar *)Data, itemlen); | ||
354 | }else{ | ||
355 | // Discard this section. | ||
356 | free(Sections[--SectionsRead].Data); | ||
357 | } | ||
358 | break; | ||
359 | |||
360 | case M_SOF0: | ||
361 | case M_SOF1: | ||
362 | case M_SOF2: | ||
363 | case M_SOF3: | ||
364 | case M_SOF5: | ||
365 | case M_SOF6: | ||
366 | case M_SOF7: | ||
367 | case M_SOF9: | ||
368 | case M_SOF10: | ||
369 | case M_SOF11: | ||
370 | case M_SOF13: | ||
371 | case M_SOF14: | ||
372 | case M_SOF15: | ||
373 | process_SOFn(Data, marker); | ||
374 | default: | ||
375 | break; | ||
376 | break; | ||
377 | } | ||
378 | } | ||
379 | return true; | ||
380 | } | ||
381 | |||
382 | |||
383 | //-------------------------------------------------------------------------- | ||
384 | // Discard read data. | ||
385 | //-------------------------------------------------------------------------- | ||
386 | void ExifData::DiscardData(void) | ||
387 | { | ||
388 | for (int a=0; a < SectionsRead; a++) | ||
389 | free(Sections[a].Data); | ||
390 | SectionsRead = 0; | ||
391 | } | ||
392 | |||
393 | //-------------------------------------------------------------------------- | ||
394 | // Convert a 16 bit unsigned value from file's native byte order | ||
395 | //-------------------------------------------------------------------------- | ||
396 | int ExifData::Get16u(void * Short) | ||
397 | { | ||
398 | if (MotorolaOrder){ | ||
399 | return (((uchar *)Short)[0] << 8) | ((uchar *)Short)[1]; | ||
400 | }else{ | ||
401 | return (((uchar *)Short)[1] << 8) | ((uchar *)Short)[0]; | ||
402 | } | ||
403 | } | ||
404 | |||
405 | //-------------------------------------------------------------------------- | ||
406 | // Convert a 32 bit signed value from file's native byte order | ||
407 | //-------------------------------------------------------------------------- | ||
408 | int ExifData::Get32s(void * Long) | ||
409 | { | ||
410 | if (MotorolaOrder){ | ||
411 | return ((( char *)Long)[0] << 24) | (((uchar *)Long)[1] << 16) | ||
412 | | (((uchar *)Long)[2] << 8 ) | (((uchar *)Long)[3] << 0 ); | ||
413 | }else{ | ||
414 | return ((( char *)Long)[3] << 24) | (((uchar *)Long)[2] << 16) | ||
415 | | (((uchar *)Long)[1] << 8 ) | (((uchar *)Long)[0] << 0 ); | ||
416 | } | ||
417 | } | ||
418 | |||
419 | //-------------------------------------------------------------------------- | ||
420 | // Convert a 32 bit unsigned value from file's native byte order | ||
421 | //-------------------------------------------------------------------------- | ||
422 | unsigned ExifData::Get32u(void * Long) | ||
423 | { | ||
424 | return (unsigned)Get32s(Long) & 0xffffffff; | ||
425 | } | ||
426 | |||
427 | //-------------------------------------------------------------------------- | ||
428 | // Evaluate number, be it int, rational, or float from directory. | ||
429 | //-------------------------------------------------------------------------- | ||
430 | double ExifData::ConvertAnyFormat(void * ValuePtr, int Format) | ||
431 | { | ||
432 | double Value; | ||
433 | Value = 0; | ||
434 | |||
435 | switch(Format){ | ||
436 | case FMT_SBYTE: Value = *(signed char *)ValuePtr; break; | ||
437 | case FMT_BYTE: Value = *(uchar *)ValuePtr; break; | ||
438 | |||
439 | case FMT_USHORT: Value = Get16u(ValuePtr); break; | ||
440 | |||
441 | case FMT_ULONG: Value = Get32u(ValuePtr); break; | ||
442 | |||
443 | case FMT_URATIONAL: | ||
444 | case FMT_SRATIONAL: | ||
445 | { | ||
446 | int Num,Den; | ||
447 | Num = Get32s(ValuePtr); | ||
448 | Den = Get32s(4+(char *)ValuePtr); | ||
449 | if (Den == 0){ | ||
450 | Value = 0; | ||
451 | }else{ | ||
452 | Value = (double)Num/Den; | ||
453 | } | ||
454 | break; | ||
455 | } | ||
456 | |||
457 | case FMT_SSHORT: Value = (signed short)Get16u(ValuePtr); break; | ||
458 | case FMT_SLONG: Value = Get32s(ValuePtr); break; | ||
459 | |||
460 | // Not sure if this is correct (never seen float used in Exif format) | ||
461 | case FMT_SINGLE: Value = (double)*(float *)ValuePtr; break; | ||
462 | case FMT_DOUBLE: Value = *(double *)ValuePtr; break; | ||
463 | } | ||
464 | return Value; | ||
465 | } | ||
466 | |||
467 | //-------------------------------------------------------------------------- | ||
468 | // Process one of the nested EXIF directories. | ||
469 | //-------------------------------------------------------------------------- | ||
470 | void ExifData::ProcessExifDir(unsigned char * DirStart, unsigned char * OffsetBase, unsigned ExifLength) | ||
471 | { | ||
472 | int de; | ||
473 | int a; | ||
474 | int NumDirEntries; | ||
475 | unsigned ThumbnailOffset = 0; | ||
476 | unsigned ThumbnailSize = 0; | ||
477 | |||
478 | NumDirEntries = Get16u(DirStart); | ||
479 | #define DIR_ENTRY_ADDR(Start, Entry) (Start+2+12*(Entry)) | ||
480 | |||
481 | { | ||
482 | unsigned char * DirEnd; | ||
483 | DirEnd = DIR_ENTRY_ADDR(DirStart, NumDirEntries); | ||
484 | if (DirEnd+4 > (OffsetBase+ExifLength)){ | ||
485 | if (DirEnd+2 == OffsetBase+ExifLength || DirEnd == OffsetBase+ExifLength){ | ||
486 | // Version 1.3 of jhead would truncate a bit too much. | ||
487 | // This also caught later on as well. | ||
488 | }else{ | ||
489 | // Note: Files that had thumbnails trimmed with jhead 1.3 or earlier | ||
490 | // might trigger this. | ||
491 | return; | ||
492 | } | ||
493 | } | ||
494 | if (DirEnd < LastExifRefd) LastExifRefd = DirEnd; | ||
495 | } | ||
496 | |||
497 | for (de=0;de<NumDirEntries;de++){ | ||
498 | int Tag, Format, Components; | ||
499 | unsigned char * ValuePtr; | ||
500 | int ByteCount; | ||
501 | char * DirEntry; | ||
502 | DirEntry = (char *)DIR_ENTRY_ADDR(DirStart, de); | ||
503 | |||
504 | Tag = Get16u(DirEntry); | ||
505 | Format = Get16u(DirEntry+2); | ||
506 | Components = Get32u(DirEntry+4); | ||
507 | |||
508 | if ((Format-1) >= NUM_FORMATS) { | ||
509 | // (-1) catches illegal zero case as unsigned underflows to positive large. | ||
510 | return; | ||
511 | } | ||
512 | |||
513 | ByteCount = Components * BytesPerFormat[Format]; | ||
514 | |||
515 | if (ByteCount > 4){ | ||
516 | unsigned OffsetVal; | ||
517 | OffsetVal = Get32u(DirEntry+8); | ||
518 | // If its bigger than 4 bytes, the dir entry contains an offset. | ||
519 | if (OffsetVal+ByteCount > ExifLength){ | ||
520 | // Bogus pointer offset and / or bytecount value | ||
521 | //printf("Offset %d bytes %d ExifLen %d\n",OffsetVal, ByteCount, ExifLength); | ||
522 | |||
523 | return; | ||
524 | } | ||
525 | ValuePtr = OffsetBase+OffsetVal; | ||
526 | }else{ | ||
527 | // 4 bytes or less and value is in the dir entry itself | ||
528 | ValuePtr = (unsigned char *)DirEntry+8; | ||
529 | } | ||
530 | |||
531 | if (LastExifRefd < ValuePtr+ByteCount){ | ||
532 | // Keep track of last byte in the exif header that was actually referenced. | ||
533 | // That way, we know where the discardable thumbnail data begins. | ||
534 | LastExifRefd = ValuePtr+ByteCount; | ||
535 | } | ||
536 | |||
537 | // Extract useful components of tag | ||
538 | switch(Tag){ | ||
539 | |||
540 | case TAG_MAKE: | ||
541 | ExifData::CameraMake = QString((char*)ValuePtr); | ||
542 | break; | ||
543 | |||
544 | case TAG_MODEL: | ||
545 | ExifData::CameraModel = QString((char*)ValuePtr); | ||
546 | break; | ||
547 | |||
548 | case TAG_ORIENTATION: | ||
549 | Orientation = (int)ConvertAnyFormat(ValuePtr, Format); | ||
550 | break; | ||
551 | |||
552 | case TAG_DATETIME_ORIGINAL: | ||
553 | DateTime = QString((char*)ValuePtr); | ||
554 | break; | ||
555 | |||
556 | case TAG_USERCOMMENT: | ||
557 | // Olympus has this padded with trailing spaces. Remove these first. | ||
558 | for (a=ByteCount;;){ | ||
559 | a--; | ||
560 | if ((ValuePtr)[a] == ' '){ | ||
561 | (ValuePtr)[a] = '\0'; | ||
562 | }else{ | ||
563 | break; | ||
564 | } | ||
565 | if (a == 0) break; | ||
566 | } | ||
567 | |||
568 | // Copy the comment | ||
569 | if (memcmp(ValuePtr, "ASCII",5) == 0){ | ||
570 | for (a=5;a<10;a++){ | ||
571 | int c; | ||
572 | c = (ValuePtr)[a]; | ||
573 | if (c != '\0' && c != ' '){ | ||
574 | //strncpy(ImageInfo.Comments, (const char*)(a+ValuePtr), 199); | ||
575 | UserComment.sprintf("%s", (const char*)(a+ValuePtr)); | ||
576 | break; | ||
577 | } | ||
578 | } | ||
579 | }else{ | ||
580 | //strncpy(ImageInfo.Comments, (const char*)ValuePtr, 199); | ||
581 | UserComment.sprintf("%s", (const char*)ValuePtr); | ||
582 | } | ||
583 | break; | ||
584 | |||
585 | case TAG_FNUMBER: | ||
586 | // Simplest way of expressing aperture, so I trust it the most. | ||
587 | // (overwrite previously computd value if there is one) | ||
588 | ExifData::ApertureFNumber = (float)ConvertAnyFormat(ValuePtr, Format); | ||
589 | break; | ||
590 | |||
591 | case TAG_APERTURE: | ||
592 | case TAG_MAXAPERTURE: | ||
593 | // More relevant info always comes earlier, so only use this field if we don't | ||
594 | // have appropriate aperture information yet. | ||
595 | if (ExifData::ApertureFNumber == 0){ | ||
596 | ExifData::ApertureFNumber | ||
597 | = (float)exp(ConvertAnyFormat(ValuePtr, Format)*log(2)*0.5); | ||
598 | } | ||
599 | break; | ||
600 | |||
601 | case TAG_FOCALLENGTH: | ||
602 | // Nice digital cameras actually save the focal length as a function | ||
603 | // of how farthey are zoomed in. | ||
604 | ExifData::FocalLength = (float)ConvertAnyFormat(ValuePtr, Format); | ||
605 | break; | ||
606 | |||
607 | case TAG_SUBJECT_DISTANCE: | ||
608 | // Inidcates the distacne the autofocus camera is focused to. | ||
609 | // Tends to be less accurate as distance increases. | ||
610 | ExifData::Distance = (float)ConvertAnyFormat(ValuePtr, Format); | ||
611 | break; | ||
612 | |||
613 | case TAG_EXPOSURETIME: | ||
614 | // Simplest way of expressing exposure time, so I trust it most. | ||
615 | // (overwrite previously computd value if there is one) | ||
616 | ExifData::ExposureTime = (float)ConvertAnyFormat(ValuePtr, Format); | ||
617 | break; | ||
618 | |||
619 | case TAG_SHUTTERSPEED: | ||
620 | // More complicated way of expressing exposure time, so only use | ||
621 | // this value if we don't already have it from somewhere else. | ||
622 | if (ExifData::ExposureTime == 0){ | ||
623 | ExifData::ExposureTime | ||
624 | = (float)(1/exp(ConvertAnyFormat(ValuePtr, Format)*log(2))); | ||
625 | } | ||
626 | break; | ||
627 | |||
628 | case TAG_FLASH: | ||
629 | if (ConvertAnyFormat(ValuePtr, Format)){ | ||
630 | ExifData::FlashUsed = 1; | ||
631 | } | ||
632 | break; | ||
633 | |||
634 | case TAG_EXIF_IMAGELENGTH: | ||
635 | ExifImageLength = (int)ConvertAnyFormat(ValuePtr, Format); | ||
636 | break; | ||
637 | |||
638 | case TAG_EXIF_IMAGEWIDTH: | ||
639 | ExifImageWidth = (int)ConvertAnyFormat(ValuePtr, Format); | ||
640 | break; | ||
641 | |||
642 | case TAG_FOCALPLANEXRES: | ||
643 | FocalplaneXRes = ConvertAnyFormat(ValuePtr, Format); | ||
644 | break; | ||
645 | |||
646 | case TAG_FOCALPLANEUNITS: | ||
647 | switch((int)ConvertAnyFormat(ValuePtr, Format)){ | ||
648 | case 1: FocalplaneUnits = 25.4; break; // inch | ||
649 | case 2: | ||
650 | // According to the information I was using, 2 means meters. | ||
651 | // But looking at the Cannon powershot's files, inches is the only | ||
652 | // sensible value. | ||
653 | FocalplaneUnits = 25.4; | ||
654 | break; | ||
655 | |||
656 | case 3: FocalplaneUnits = 10; break; // centimeter | ||
657 | case 4: FocalplaneUnits = 1; break; // milimeter | ||
658 | case 5: FocalplaneUnits = .001; break; // micrometer | ||
659 | } | ||
660 | break; | ||
661 | |||
662 | // Remaining cases contributed by: Volker C. Schoech (schoech@gmx.de) | ||
663 | |||
664 | case TAG_EXPOSURE_BIAS: | ||
665 | ExifData::ExposureBias = (float)ConvertAnyFormat(ValuePtr, Format); | ||
666 | break; | ||
667 | |||
668 | case TAG_WHITEBALANCE: | ||
669 | ExifData::Whitebalance = (int)ConvertAnyFormat(ValuePtr, Format); | ||
670 | break; | ||
671 | |||
672 | case TAG_METERING_MODE: | ||
673 | ExifData::MeteringMode = (int)ConvertAnyFormat(ValuePtr, Format); | ||
674 | break; | ||
675 | |||
676 | case TAG_EXPOSURE_PROGRAM: | ||
677 | ExifData::ExposureProgram = (int)ConvertAnyFormat(ValuePtr, Format); | ||
678 | break; | ||
679 | |||
680 | case TAG_ISO_EQUIVALENT: | ||
681 | ExifData::ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format); | ||
682 | if ( ExifData::ISOequivalent < 50 ) ExifData::ISOequivalent *= 200; | ||
683 | break; | ||
684 | |||
685 | case TAG_COMPRESSION_LEVEL: | ||
686 | ExifData::CompressionLevel = (int)ConvertAnyFormat(ValuePtr, Format); | ||
687 | break; | ||
688 | |||
689 | case TAG_THUMBNAIL_OFFSET: | ||
690 | ThumbnailOffset = (unsigned)ConvertAnyFormat(ValuePtr, Format); | ||
691 | break; | ||
692 | |||
693 | case TAG_THUMBNAIL_LENGTH: | ||
694 | ThumbnailSize = (unsigned)ConvertAnyFormat(ValuePtr, Format); | ||
695 | break; | ||
696 | |||
697 | } | ||
698 | |||
699 | if (Tag == TAG_EXIF_OFFSET || Tag == TAG_INTEROP_OFFSET){ | ||
700 | unsigned char * SubdirStart; | ||
701 | SubdirStart = OffsetBase + Get32u(ValuePtr); | ||
702 | if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength){ | ||
703 | return; | ||
704 | } | ||
705 | ProcessExifDir(SubdirStart, OffsetBase, ExifLength); | ||
706 | continue; | ||
707 | } | ||
708 | } | ||
709 | |||
710 | { | ||
711 | // In addition to linking to subdirectories via exif tags, | ||
712 | // there's also a potential link to another directory at the end of each | ||
713 | // directory. this has got to be the result of a comitee! | ||
714 | unsigned char * SubdirStart; | ||
715 | unsigned Offset; | ||
716 | |||
717 | if (DIR_ENTRY_ADDR(DirStart, NumDirEntries) + 4 <= OffsetBase+ExifLength){ | ||
718 | Offset = Get32u(DIR_ENTRY_ADDR(DirStart, NumDirEntries)); | ||
719 | // There is at least one jpeg from an HP camera having an Offset of almost MAXUINT. | ||
720 | // Adding OffsetBase to it produces an overflow, so compare with ExifLength here. | ||
721 | // See http://bugs.kde.org/show_bug.cgi?id=54542 | ||
722 | if (Offset && Offset < ExifLength){ | ||
723 | SubdirStart = OffsetBase + Offset; | ||
724 | if (SubdirStart > OffsetBase+ExifLength){ | ||
725 | if (SubdirStart < OffsetBase+ExifLength+20){ | ||
726 | // Jhead 1.3 or earlier would crop the whole directory! | ||
727 | // As Jhead produces this form of format incorrectness, | ||
728 | // I'll just let it pass silently | ||
729 | qWarning( "Thumbnail removed with Jhead 1.3 or earlier" ); | ||
730 | }else{ | ||
731 | return; | ||
732 | } | ||
733 | }else{ | ||
734 | if (SubdirStart <= OffsetBase+ExifLength){ | ||
735 | ProcessExifDir(SubdirStart, OffsetBase, ExifLength); | ||
736 | } | ||
737 | } | ||
738 | } | ||
739 | }else{ | ||
740 | // The exif header ends before the last next directory pointer. | ||
741 | } | ||
742 | } | ||
743 | |||
744 | if (ThumbnailSize && ThumbnailOffset){ | ||
745 | if (ThumbnailSize + ThumbnailOffset <= ExifLength){ | ||
746 | // The thumbnail pointer appears to be valid. Store it. | ||
747 | Thumbnail.loadFromData(OffsetBase + ThumbnailOffset, ThumbnailSize, "JPEG"); | ||
748 | } | ||
749 | } | ||
750 | } | ||
751 | |||
752 | //-------------------------------------------------------------------------- | ||
753 | // Process a COM marker. We want to leave the bytes unchanged. The | ||
754 | // progam that displays this text may decide to remove blanks, convert | ||
755 | // newlines, or otherwise modify the text. In particular we want to be | ||
756 | // safe for passing utf-8 text. | ||
757 | //-------------------------------------------------------------------------- | ||
758 | void ExifData::process_COM (const uchar * Data, int length) | ||
759 | { | ||
760 | QChar ch; | ||
761 | int a; | ||
762 | |||
763 | for (a=2;a<length;a++){ | ||
764 | ch = Data[a]; | ||
765 | if (ch == '\000') continue; // Remove nulls | ||
766 | Comment.append(ch); | ||
767 | } | ||
768 | } | ||
769 | |||
770 | |||
771 | //-------------------------------------------------------------------------- | ||
772 | // Process a SOFn marker. This is useful for the image dimensions | ||
773 | //-------------------------------------------------------------------------- | ||
774 | void ExifData::process_SOFn (const uchar * Data, int marker) | ||
775 | { | ||
776 | int data_precision, num_components; | ||
777 | |||
778 | data_precision = Data[2]; | ||
779 | ExifData::Height = Get16m(Data+3); | ||
780 | ExifData::Width = Get16m(Data+5); | ||
781 | num_components = Data[7]; | ||
782 | |||
783 | if (num_components == 3){ | ||
784 | ExifData::IsColor = 1; | ||
785 | }else{ | ||
786 | ExifData::IsColor = 0; | ||
787 | } | ||
788 | |||
789 | ExifData::Process = marker; | ||
790 | |||
791 | } | ||
792 | |||
793 | //-------------------------------------------------------------------------- | ||
794 | // Get 16 bits motorola order (always) for jpeg header stuff. | ||
795 | //-------------------------------------------------------------------------- | ||
796 | int ExifData::Get16m(const void * Short) | ||
797 | { | ||
798 | return (((uchar *)Short)[0] << 8) | ((uchar *)Short)[1]; | ||
799 | } | ||
800 | |||
801 | |||
802 | //-------------------------------------------------------------------------- | ||
803 | // Process a EXIF marker | ||
804 | // Describes all the drivel that most digital cameras include... | ||
805 | //-------------------------------------------------------------------------- | ||
806 | void ExifData::process_EXIF(unsigned char * CharBuf, unsigned int length) | ||
807 | { | ||
808 | ExifData::FlashUsed = 0; // If it s from a digicam, and it used flash, it says so. | ||
809 | |||
810 | FocalplaneXRes = 0; | ||
811 | FocalplaneUnits = 0; | ||
812 | ExifImageWidth = 0; | ||
813 | ExifImageLength = 0; | ||
814 | |||
815 | { // Check the EXIF header component | ||
816 | static const uchar ExifHeader[] = "Exif\0\0"; | ||
817 | if (memcmp(CharBuf+2, ExifHeader,6)){ | ||
818 | return; | ||
819 | } | ||
820 | } | ||
821 | |||
822 | if (memcmp(CharBuf+8,"II",2) == 0){ | ||
823 | // printf("Exif section in Intel order\n"); | ||
824 | MotorolaOrder = 0; | ||
825 | }else{ | ||
826 | if (memcmp(CharBuf+8,"MM",2) == 0){ | ||
827 | // printf("Exif section in Motorola order\n"); | ||
828 | MotorolaOrder = 1; | ||
829 | }else{ | ||
830 | return; | ||
831 | } | ||
832 | } | ||
833 | |||
834 | // Check the next two values for correctness. | ||
835 | if (Get16u(CharBuf+10) != 0x2a | ||
836 | || Get32u(CharBuf+12) != 0x08){ | ||
837 | return; | ||
838 | } | ||
839 | |||
840 | LastExifRefd = CharBuf; | ||
841 | |||
842 | // First directory starts 16 bytes in. Offsets start at 8 bytes in. | ||
843 | ProcessExifDir(CharBuf+16, CharBuf+8, length-6); | ||
844 | |||
845 | // This is how far the interesting (non thumbnail) part of the exif went. | ||
846 | ExifSettingsLength = LastExifRefd - CharBuf; | ||
847 | |||
848 | // Compute the CCD width, in milimeters. | ||
849 | if (FocalplaneXRes != 0){ | ||
850 | ExifData::CCDWidth = (float)(ExifImageWidth * FocalplaneUnits / FocalplaneXRes); | ||
851 | } | ||
852 | } | ||
853 | |||
854 | //-------------------------------------------------------------------------- | ||
855 | // Convert exif time to Unix time structure | ||
856 | //-------------------------------------------------------------------------- | ||
857 | int ExifData::Exif2tm(struct tm * timeptr, char * ExifTime) | ||
858 | { | ||
859 | int a; | ||
860 | |||
861 | timeptr->tm_wday = -1; | ||
862 | |||
863 | // Check for format: YYYY:MM:DD HH:MM:SS format. | ||
864 | a = sscanf(ExifTime, "%d:%d:%d %d:%d:%d", | ||
865 | &timeptr->tm_year, &timeptr->tm_mon, &timeptr->tm_mday, | ||
866 | &timeptr->tm_hour, &timeptr->tm_min, &timeptr->tm_sec); | ||
867 | |||
868 | if (a == 6){ | ||
869 | timeptr->tm_isdst = -1; | ||
870 | timeptr->tm_mon -= 1; // Adjust for unix zero-based months | ||
871 | timeptr->tm_year -= 1900; // Adjust for year starting at 1900 | ||
872 | return true; // worked. | ||
873 | } | ||
874 | |||
875 | return false; // Wasn't in Exif date format. | ||
876 | } | ||
877 | |||
878 | //-------------------------------------------------------------------------- | ||
879 | // Contructor for initialising | ||
880 | //-------------------------------------------------------------------------- | ||
881 | ExifData::ExifData() | ||
882 | { | ||
883 | ExifData::Whitebalance = -1; | ||
884 | ExifData::MeteringMode = -1; | ||
885 | ExifData::FlashUsed = -1; | ||
886 | Orientation = 0; | ||
887 | Height = 0; | ||
888 | Width = 0; | ||
889 | IsColor = 0; | ||
890 | Process = 0; | ||
891 | FocalLength = 0; | ||
892 | ExposureTime = 0; | ||
893 | ApertureFNumber = 0; | ||
894 | Distance = 0; | ||
895 | CCDWidth = 0; | ||
896 | ExposureBias = 0; | ||
897 | ExposureProgram = 0; | ||
898 | ISOequivalent = 0; | ||
899 | CompressionLevel = 0; | ||
900 | } | ||
901 | |||
902 | //-------------------------------------------------------------------------- | ||
903 | // process a EXIF jpeg file | ||
904 | //-------------------------------------------------------------------------- | ||
905 | bool ExifData::scan(const QString & path) | ||
906 | { | ||
907 | int ret; | ||
908 | |||
909 | QFile f(path); | ||
910 | f.open(IO_ReadOnly); | ||
911 | |||
912 | // Scan the JPEG headers. | ||
913 | ret = ReadJpegSections(f, READ_EXIF); | ||
914 | |||
915 | if (ret == false){ | ||
916 | qWarning( "Not JPEG file!" ); | ||
917 | DiscardData(); | ||
918 | f.close(); | ||
919 | return false; | ||
920 | } | ||
921 | f.close(); | ||
922 | DiscardData(); | ||
923 | |||
924 | //now make the strings clean, | ||
925 | // for exmaple my Casio is a "QV-4000 " | ||
926 | CameraMake = CameraMake.stripWhiteSpace(); | ||
927 | CameraModel = CameraModel.stripWhiteSpace(); | ||
928 | UserComment = UserComment.stripWhiteSpace(); | ||
929 | Comment = Comment.stripWhiteSpace(); | ||
930 | return true; | ||
931 | } | ||
932 | |||
933 | //-------------------------------------------------------------------------- | ||
934 | // Does the embedded thumbnail match the jpeg image? | ||
935 | //-------------------------------------------------------------------------- | ||
936 | #ifndef JPEG_TOL | ||
937 | #define JPEG_TOL 0.02 | ||
938 | #endif | ||
939 | bool ExifData::isThumbnailSane() { | ||
940 | if (Thumbnail.isNull()) return false; | ||
941 | |||
942 | // check whether thumbnail dimensions match the image | ||
943 | // not foolproof, but catches some altered images (jpegtran -rotate) | ||
944 | if (ExifImageLength != 0 && ExifImageLength != Height) return false; | ||
945 | if (ExifImageWidth != 0 && ExifImageWidth != Width) return false; | ||
946 | if (Thumbnail.width() == 0 || Thumbnail.height() == 0) return false; | ||
947 | if (Height == 0 || Width == 0) return false; | ||
948 | double d = (double)Height/Width*Thumbnail.width()/Thumbnail.height(); | ||
949 | return (1-JPEG_TOL < d) && (d < 1+JPEG_TOL); | ||
950 | } | ||
951 | |||
952 | |||
953 | |||
954 | static QImage flip_image( const QImage& img ); | ||
955 | static QImage rotate_90( const QImage& img ); | ||
956 | static QImage rotate_180( const QImage& ); | ||
957 | static QImage rotate_270( const QImage& ); | ||
958 | |||
959 | //-------------------------------------------------------------------------- | ||
960 | // return a thumbnail that respects the orientation flag | ||
961 | // only if it seems sane | ||
962 | //-------------------------------------------------------------------------- | ||
963 | QImage ExifData::getThumbnail() { | ||
964 | if (!isThumbnailSane()) return NULL; | ||
965 | if (!Orientation || Orientation == 1) return Thumbnail; | ||
966 | |||
967 | // now fix orientation | ||
968 | |||
969 | QImage dest = Thumbnail; | ||
970 | switch (Orientation) { // notice intentional fallthroughs | ||
971 | case 2: dest = flip_image( dest ); break; | ||
972 | case 4: dest = flip_image( dest ); | ||
973 | case 3: dest =rotate_180( dest ); break; | ||
974 | case 5: dest = flip_image( dest ); | ||
975 | case 6: dest = rotate_90( dest ); break; | ||
976 | case 7: dest = flip_image( dest ); | ||
977 | case 8: dest = rotate_270( dest ); break; | ||
978 | default: break; // should never happen | ||
979 | } | ||
980 | return dest; | ||
981 | } | ||
982 | |||
983 | |||
984 | /* | ||
985 | * | ||
986 | */ | ||
987 | static QImage flip_image( const QImage& img ) { | ||
988 | return img.mirror( TRUE, FALSE ); | ||
989 | } | ||
990 | |||
991 | |||
992 | static QImage dest; | ||
993 | static int x, y; | ||
994 | static unsigned int *srcData, *destData; // we're not threaded anyway | ||
995 | static unsigned char *srcData8, *destData8; // 8 bit is char | ||
996 | static unsigned int *srcTable, *destTable; // destination table | ||
997 | |||
998 | |||
999 | static QImage rotate_90_8( const QImage &img ) { | ||
1000 | dest.create(img.height(), img.width(), img.depth()); | ||
1001 | dest.setNumColors(img.numColors()); | ||
1002 | srcTable = (unsigned int *)img.colorTable(); | ||
1003 | destTable = (unsigned int *)dest.colorTable(); | ||
1004 | for ( x=0; x < img.numColors(); ++x ) | ||
1005 | destTable[x] = srcTable[x]; | ||
1006 | for ( y=0; y < img.height(); ++y ){ | ||
1007 | srcData8 = (unsigned char *)img.scanLine(y); | ||
1008 | for ( x=0; x < img.width(); ++x ){ | ||
1009 | destData8 = (unsigned char *)dest.scanLine(x); | ||
1010 | destData8[img.height()-y-1] = srcData8[x]; | ||
1011 | } | ||
1012 | } | ||
1013 | return dest; | ||
1014 | } | ||
1015 | |||
1016 | static QImage rotate_90_all( const QImage& img ) { | ||
1017 | dest.create(img.height(), img.width(), img.depth()); | ||
1018 | for ( y=0; y < img.height(); ++y ) { | ||
1019 | srcData = (unsigned int *)img.scanLine(y); | ||
1020 | for ( x=0; x < img.width(); ++x ) { | ||
1021 | destData = (unsigned int *)dest.scanLine(x); | ||
1022 | destData[img.height()-y-1] = srcData[x]; | ||
1023 | } | ||
1024 | } | ||
1025 | |||
1026 | return dest; | ||
1027 | } | ||
1028 | |||
1029 | |||
1030 | static QImage rotate_90( const QImage & img ) { | ||
1031 | if ( img.depth() > 8) | ||
1032 | return rotate_90_all( img ); | ||
1033 | else | ||
1034 | return rotate_90_8( img ); | ||
1035 | } | ||
1036 | |||
1037 | static QImage rotate_180_all( const QImage& img ) { | ||
1038 | dest.create(img.width(), img.height(), img.depth()); | ||
1039 | for ( y=0; y < img.height(); ++y ){ | ||
1040 | srcData = (unsigned int *)img.scanLine(y); | ||
1041 | destData = (unsigned int *)dest.scanLine(img.height()-y-1); | ||
1042 | for ( x=0; x < img.width(); ++x ) | ||
1043 | destData[img.width()-x-1] = srcData[x]; | ||
1044 | } | ||
1045 | return dest; | ||
1046 | } | ||
1047 | |||
1048 | static QImage rotate_180_8( const QImage& img ) { | ||
1049 | dest.create(img.width(), img.height(), img.depth()); | ||
1050 | dest.setNumColors(img.numColors()); | ||
1051 | srcTable = (unsigned int *)img.colorTable(); | ||
1052 | destTable = (unsigned int *)dest.colorTable(); | ||
1053 | for ( x=0; x < img.numColors(); ++x ) | ||
1054 | destTable[x] = srcTable[x]; | ||
1055 | for ( y=0; y < img.height(); ++y ){ | ||
1056 | srcData8 = (unsigned char *)img.scanLine(y); | ||
1057 | destData8 = (unsigned char *)dest.scanLine(img.height()-y-1); | ||
1058 | for ( x=0; x < img.width(); ++x ) | ||
1059 | destData8[img.width()-x-1] = srcData8[x]; | ||
1060 | } | ||
1061 | return dest; | ||
1062 | } | ||
1063 | |||
1064 | static QImage rotate_180( const QImage& img ) { | ||
1065 | if ( img.depth() > 8 ) | ||
1066 | return rotate_180_all( img ); | ||
1067 | else | ||
1068 | return rotate_180_8( img ); | ||
1069 | } | ||
1070 | |||
1071 | |||
1072 | static QImage rotate_270_8( const QImage& img ) { | ||
1073 | dest.create(img.height(), img.width(), img.depth()); | ||
1074 | dest.setNumColors(img.numColors()); | ||
1075 | srcTable = (unsigned int *)img.colorTable(); | ||
1076 | destTable = (unsigned int *)dest.colorTable(); | ||
1077 | for ( x=0; x < img.numColors(); ++x ) | ||
1078 | destTable[x] = srcTable[x]; | ||
1079 | for ( y=0; y < img.height(); ++y ){ | ||
1080 | srcData8 = (unsigned char *)img.scanLine(y); | ||
1081 | for ( x=0; x < img.width(); ++x ){ | ||
1082 | destData8 = (unsigned char *)dest.scanLine(img.width()-x-1); | ||
1083 | destData8[y] = srcData8[x]; | ||
1084 | } | ||
1085 | } | ||
1086 | |||
1087 | return dest; | ||
1088 | } | ||
1089 | |||
1090 | static QImage rotate_270_all( const QImage& img ) { | ||
1091 | dest.create(img.height(), img.width(), img.depth()); | ||
1092 | for ( y=0; y < img.height(); ++y ){ | ||
1093 | srcData = (unsigned int *)img.scanLine(y); | ||
1094 | for ( x=0; x < img.width(); ++x ){ | ||
1095 | destData = (unsigned int *)dest.scanLine(img.width()-x-1); | ||
1096 | destData[y] = srcData[x]; | ||
1097 | } | ||
1098 | } | ||
1099 | return dest; | ||
1100 | } | ||
1101 | |||
1102 | static QImage rotate_270( const QImage& img ) { | ||
1103 | if ( img.depth() > 8 ) | ||
1104 | return rotate_270_all( img ); | ||
1105 | else | ||
1106 | return rotate_270_8( img ); | ||
1107 | } | ||
1108 | |||
1109 | |||
1110 | static QString color_mode_to_string( bool b ) { | ||
1111 | return b ? QObject::tr( "Colormode: Color\n" ) : QObject::tr( "Colormode: Black and white\n" ); | ||
1112 | } | ||
1113 | |||
1114 | static QString compression_to_string( int level ) { | ||
1115 | QString str; | ||
1116 | switch( level ) { | ||
1117 | case 1: | ||
1118 | str = QObject::tr( "Basic" ); | ||
1119 | break; | ||
1120 | case 2: | ||
1121 | str = QObject::tr( "Normal" ); | ||
1122 | break; | ||
1123 | case 4: | ||
1124 | str = QObject::tr( "Fine" ); | ||
1125 | break; | ||
1126 | default: | ||
1127 | str = QObject::tr( "Unknown" ); | ||
1128 | |||
1129 | } | ||
1130 | return QObject::tr("Quality: %1\n").arg(str); | ||
1131 | } | ||
1132 | |||
1133 | |||
1134 | static QDateTime parseDateTime( const QString& string ) | ||
1135 | { | ||
1136 | QDateTime dt; | ||
1137 | if ( string.length() != 19 ) | ||
1138 | return dt; | ||
1139 | |||
1140 | QString year = string.left( 4 ); | ||
1141 | QString month = string.mid( 5, 2 ); | ||
1142 | QString day = string.mid( 8, 2 ); | ||
1143 | QString hour = string.mid( 11, 2 ); | ||
1144 | QString minute = string.mid( 14, 2 ); | ||
1145 | QString seconds = string.mid( 18, 2 ); | ||
1146 | |||
1147 | bool ok; | ||
1148 | bool allOk = true; | ||
1149 | int y = year.toInt( &ok ); | ||
1150 | allOk &= ok; | ||
1151 | |||
1152 | int mo = month.toInt( &ok ); | ||
1153 | allOk &= ok; | ||
1154 | |||
1155 | int d = day.toInt( &ok ); | ||
1156 | allOk &= ok; | ||
1157 | |||
1158 | int h = hour.toInt( &ok ); | ||
1159 | allOk &= ok; | ||
1160 | |||
1161 | int mi = minute.toInt( &ok ); | ||
1162 | allOk &= ok; | ||
1163 | |||
1164 | int s = seconds.toInt( &ok ); | ||
1165 | allOk &= ok; | ||
1166 | |||
1167 | if ( allOk ) { | ||
1168 | dt.setDate( QDate( y, mo, d ) ); | ||
1169 | dt.setTime( QTime( h, mi, s ) ); | ||
1170 | } | ||
1171 | |||
1172 | return dt; | ||
1173 | } | ||
1174 | |||
1175 | static QString white_balance_string( int i ) { | ||
1176 | QString balance; | ||
1177 | switch ( i ) { | ||
1178 | case 0: | ||
1179 | balance = QObject::tr( "Unknown" ); | ||
1180 | break; | ||
1181 | case 1: | ||
1182 | balance = QObject::tr( "Daylight" ); | ||
1183 | break; | ||
1184 | case 2: | ||
1185 | balance = QObject::tr( "Fluorescent" ); | ||
1186 | break; | ||
1187 | case 3: | ||
1188 | balance = QObject::tr( "Tungsten" ); | ||
1189 | break; | ||
1190 | case 17: | ||
1191 | balance = QObject::tr( "Standard light A" ); | ||
1192 | break; | ||
1193 | case 18: | ||
1194 | balance = QObject::tr( "Standard light B" ); | ||
1195 | break; | ||
1196 | case 19: | ||
1197 | balance = QObject::tr( "Standard light C" ); | ||
1198 | break; | ||
1199 | case 20: | ||
1200 | balance = QObject::tr( "D55" ); | ||
1201 | break; | ||
1202 | case 21: | ||
1203 | balance = QObject::tr( "D65" ); | ||
1204 | break; | ||
1205 | case 22: | ||
1206 | balance = QObject::tr( "D75" ); | ||
1207 | break; | ||
1208 | case 255: | ||
1209 | balance = QObject::tr( "Other" ); | ||
1210 | break; | ||
1211 | default: | ||
1212 | balance = QObject::tr( "Unknown" ); | ||
1213 | } | ||
1214 | return QObject::tr( "White Balance: %1\n" ).arg( balance ); | ||
1215 | |||
1216 | } | ||
1217 | |||
1218 | |||
1219 | static QString metering_mode( int i) { | ||
1220 | QString meter; | ||
1221 | switch( i ) { | ||
1222 | case 0: | ||
1223 | meter = QObject::tr( "Unknown" ); | ||
1224 | break; | ||
1225 | case 1: | ||
1226 | meter = QObject::tr( "Average" ); | ||
1227 | break; | ||
1228 | case 2: | ||
1229 | meter = QObject::tr( "Center weighted average" ); | ||
1230 | break; | ||
1231 | case 3: | ||
1232 | meter = QObject::tr( "Spot" ); | ||
1233 | break; | ||
1234 | case 4: | ||
1235 | meter = QObject::tr( "MultiSpot" ); | ||
1236 | break; | ||
1237 | case 5: | ||
1238 | meter = QObject::tr( "Pattern" ); | ||
1239 | break; | ||
1240 | case 6: | ||
1241 | meter = QObject::tr( "Partial" ); | ||
1242 | break; | ||
1243 | case 255: | ||
1244 | meter = QObject::tr( "Other" ); | ||
1245 | break; | ||
1246 | default: | ||
1247 | meter = QObject::tr( "Unknown" ); | ||
1248 | } | ||
1249 | |||
1250 | return QObject::tr( "Metering Mode: %1\n" ).arg( meter ); | ||
1251 | } | ||
1252 | |||
1253 | |||
1254 | static QString exposure_program( int i ) { | ||
1255 | QString exp; | ||
1256 | switch( i ) { | ||
1257 | case 0: | ||
1258 | exp = QObject::tr( "Not defined" ); | ||
1259 | break; | ||
1260 | case 1: | ||
1261 | exp = QObject::tr( "Manual" ); | ||
1262 | break; | ||
1263 | case 2: | ||
1264 | exp = QObject::tr( "Normal progam" ); | ||
1265 | break; | ||
1266 | case 3: | ||
1267 | exp = QObject::tr( "Aperture priority" ); | ||
1268 | break; | ||
1269 | case 4: | ||
1270 | exp = QObject::tr( "Shutter priority" ); | ||
1271 | break; | ||
1272 | case 5: | ||
1273 | exp = QObject::tr( "Creative progam\n(biased toward fast shutter speed" ); | ||
1274 | break; | ||
1275 | case 6: | ||
1276 | exp = QObject::tr( "Action progam\n(biased toward fast shutter speed)" ); | ||
1277 | break; | ||
1278 | case 7: | ||
1279 | exp = QObject::tr( "Portrait mode\n(for closeup photos with the background out of focus)" ); | ||
1280 | break; | ||
1281 | case 8: | ||
1282 | exp = QObject::tr( "Landscape mode\n(for landscape photos with the background in focus)" ); | ||
1283 | break; | ||
1284 | default: | ||
1285 | exp = QObject::tr( "Unknown" ); | ||
1286 | } | ||
1287 | |||
1288 | return QObject::tr( "Exposure Program: %1\n" ).arg( exp ); | ||
1289 | } | ||
1290 | |||
1291 | JpegSlave::JpegSlave() | ||
1292 | : SlaveInterface( QStringList::split( " ", "jpeg jpg" ) ) | ||
1293 | {} | ||
1294 | |||
1295 | JpegSlave::~JpegSlave() {} | ||
1296 | |||
1297 | QString JpegSlave::iconViewName( const QString& path) { | ||
1298 | ExifData ImageInfo; | ||
1299 | if ( !ImageInfo.scan( path ) ) | ||
1300 | return QString::null; | ||
1301 | |||
1302 | QString tag; | ||
1303 | tag = QObject::tr( "<qt>Comment: %1\n" ).arg( ImageInfo.getComment() ); | ||
1304 | { | ||
1305 | // ODP fixme | ||
1306 | QString timestring = TimeString::dateString( parseDateTime( ImageInfo.getDateTime() ), FALSE ); | ||
1307 | tag += QObject::tr( "Date/Time: %1\n" ).arg( timestring ); | ||
1308 | } | ||
1309 | tag += QObject::tr( "Dimensions: %1x%2\n" ).arg(ImageInfo.getWidth()) | ||
1310 | .arg(ImageInfo.getHeight() ); | ||
1311 | |||
1312 | tag += color_mode_to_string( ImageInfo.getIsColor() ); | ||
1313 | |||
1314 | tag += compression_to_string( ImageInfo.getCompressionLevel() ); | ||
1315 | tag += QObject::tr( "</qt>" ); | ||
1316 | |||
1317 | return tag; | ||
1318 | } | ||
1319 | |||
1320 | |||
1321 | /* | ||
1322 | * messy messy string creation | ||
1323 | */ | ||
1324 | QString JpegSlave::fullImageInfo( const QString& path) { | ||
1325 | ExifData ImageInfo; | ||
1326 | if ( !ImageInfo.scan( path ) ) | ||
1327 | return QString::null; | ||
1328 | |||
1329 | QString tag, tmp; | ||
1330 | tag = QObject::tr( "Comment: %1\n" ).arg( ImageInfo.getComment() ); | ||
1331 | |||
1332 | tmp = ImageInfo.getCameraMake(); | ||
1333 | if ( tmp.length() ) | ||
1334 | tag += QObject::tr( "Manufacturer: %1\n" ).arg( tmp ); | ||
1335 | tmp = ImageInfo.getCameraModel(); | ||
1336 | if ( tmp.length() ) | ||
1337 | tag += QObject::tr( "Model: %1\n" ).arg( tmp ); | ||
1338 | { | ||
1339 | // ODP fixme | ||
1340 | tmp = TimeString::dateString( parseDateTime( ImageInfo.getDateTime() ), FALSE ); | ||
1341 | tag += QObject::tr( "Date/Time: %1\n" ).arg( tmp ); | ||
1342 | } | ||
1343 | tag += QObject::tr( "Dimensions: %1x%2\n" ).arg(ImageInfo.getWidth()) | ||
1344 | .arg(ImageInfo.getHeight() ); | ||
1345 | |||
1346 | tag += color_mode_to_string( ImageInfo.getIsColor() ); | ||
1347 | |||
1348 | tag += compression_to_string( ImageInfo.getCompressionLevel() ); | ||
1349 | if ( ImageInfo.getOrientation() ) | ||
1350 | tag += QObject::tr( "Orientation: %1\n" ).arg(ImageInfo.getOrientation() ); | ||
1351 | |||
1352 | |||
1353 | { | ||
1354 | int flash_used = ImageInfo.getFlashUsed(); | ||
1355 | if ( flash_used >= 0 ) | ||
1356 | tag += QObject::tr( "Flash used\n" ); | ||
1357 | } | ||
1358 | |||
1359 | if ( ImageInfo.getFocalLength() ) { | ||
1360 | tag += QObject::tr( "Focal length: %1\n" ).arg( QString().sprintf( "%4.1f", ImageInfo.getFocalLength() ) ); | ||
1361 | if ( ImageInfo.getCCDWidth() ) | ||
1362 | tag += QObject::tr( "35mm equivalent: %1\n" ).arg( (int)(ImageInfo.getFocalLength()/ImageInfo.getCCDWidth()*35 + 0.5) ); | ||
1363 | |||
1364 | } | ||
1365 | |||
1366 | if ( ImageInfo.getCCDWidth() ) | ||
1367 | tag += QObject::tr( "CCD width: %1" ).arg( ImageInfo.getCCDWidth() ); | ||
1368 | if ( ImageInfo.getExposureTime() ) { | ||
1369 | tmp = QString().sprintf("%4.2f", ImageInfo.getExposureTime() ); | ||
1370 | float exposureTime = ImageInfo.getExposureTime(); | ||
1371 | if ( exposureTime > 0 && exposureTime <= 0.5 ) | ||
1372 | tmp += QString().sprintf(" (1/%d)", (int)(0.5 +1/exposureTime) ); | ||
1373 | tag += QObject::tr( "Exposure time: %1\n" ).arg( tmp ); | ||
1374 | } | ||
1375 | |||
1376 | if ( ImageInfo.getApertureFNumber() ) | ||
1377 | tag += QObject::tr( "Aperture: %1\n" ).arg( QString().sprintf("f/%3.1f", (double)ImageInfo.getApertureFNumber() ) ); | ||
1378 | |||
1379 | if ( ImageInfo.getDistance() ) { | ||
1380 | if ( ImageInfo.getDistance() < 0 ) | ||
1381 | tag += QObject::tr( "Distance: %1\n" ).arg( QObject::tr( "Infinite" ) ); | ||
1382 | else | ||
1383 | tag += QObject::tr( "Distance: %1\n" ).arg( QString().sprintf( "%5.2fm", (double)ImageInfo.getDistance() ) ); | ||
1384 | } | ||
1385 | |||
1386 | if ( ImageInfo.getExposureBias() ) { | ||
1387 | tag += QObject::tr( "Exposure bias: %1\n", QString().sprintf("%4.2f", (double)ImageInfo.getExposureBias() ) ); | ||
1388 | } | ||
1389 | |||
1390 | if ( ImageInfo.getWhitebalance() != -1 ) | ||
1391 | tag += white_balance_string( ImageInfo.getWhitebalance() ); | ||
1392 | |||
1393 | |||
1394 | if( ImageInfo.getMeteringMode() != -1 ) | ||
1395 | tag += metering_mode( ImageInfo.getMeteringMode() ); | ||
1396 | |||
1397 | if ( ImageInfo.getExposureProgram() ) | ||
1398 | tag += exposure_program( ImageInfo.getExposureProgram() ); | ||
1399 | if ( ImageInfo.getISOequivalent() ) | ||
1400 | tag += QObject::tr( "ISO equivalent: %1\n" ).arg( QString().sprintf("%2d", ImageInfo.getISOequivalent() ) ); | ||
1401 | |||
1402 | tmp = ImageInfo.getUserComment(); | ||
1403 | if ( tmp.length() ) | ||
1404 | tag += QObject::tr( "EXIF comment: %1" ).arg( tmp ); | ||
1405 | |||
1406 | tag += QObject::tr( "</qt>" ); | ||
1407 | |||
1408 | |||
1409 | |||
1410 | return tag; | ||
1411 | } | ||
1412 | |||
1413 | QPixmap JpegSlave::pixmap( const QString& path, int wid, int hei) { | ||
1414 | ExifData ImageInfo; | ||
1415 | if ( !ImageInfo.scan( path ) || ImageInfo.isNullThumbnail() ) { | ||
1416 | QImage img; | ||
1417 | QImageIO iio( path, 0l ); | ||
1418 | QString str = QString( "Fast Shrink( 4 ) Scale( %1, %2, ScaleFree)" ).arg( wid ).arg( hei ); | ||
1419 | iio.setParameters( str.latin1() );// will be strdupped anyway | ||
1420 | img = iio.read() ? iio.image() : QImage(); | ||
1421 | return ThumbNailTool::scaleImage( img, wid,hei ); | ||
1422 | }else{ | ||
1423 | QImage img = ImageInfo.getThumbnail(); | ||
1424 | return ThumbNailTool::scaleImage( img, wid,hei ); | ||
1425 | } | ||
1426 | } | ||