summaryrefslogtreecommitdiff
path: root/noncore/graphics/opie-eye/slave/jpeg_slave.cpp
Unidiff
Diffstat (limited to 'noncore/graphics/opie-eye/slave/jpeg_slave.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--noncore/graphics/opie-eye/slave/jpeg_slave.cpp1426
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
5PHUNK_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
24typedef 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.
32typedef struct {
33 uchar * Data;
34 int Type;
35 unsigned Size;
36}Section_t;
37
38typedef unsigned char uchar;
39
40typedef 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
48class 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
88public:
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
118class FatalError {
119 const char* ex;
120public:
121 FatalError(const char* s) { ex = s; }
122 void debug_print() const { qWarning("exception: %s", ex ); }
123};
124
125
126
127static unsigned char * LastExifRefd;
128static int ExifSettingsLength;
129static double FocalplaneXRes;
130static double FocalplaneUnits;
131static int MotorolaOrder = 0;
132static 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
159TagTable_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
180static 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//--------------------------------------------------------------------------
242int 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//--------------------------------------------------------------------------
386void 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//--------------------------------------------------------------------------
396int 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//--------------------------------------------------------------------------
408int 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//--------------------------------------------------------------------------
422unsigned 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//--------------------------------------------------------------------------
430double 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//--------------------------------------------------------------------------
470void 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//--------------------------------------------------------------------------
758void 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//--------------------------------------------------------------------------
774void 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//--------------------------------------------------------------------------
796int 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//--------------------------------------------------------------------------
806void 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//--------------------------------------------------------------------------
857int 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//--------------------------------------------------------------------------
881ExifData::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//--------------------------------------------------------------------------
905bool 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
939bool 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
954static QImage flip_image( const QImage& img );
955static QImage rotate_90( const QImage& img );
956static QImage rotate_180( const QImage& );
957static QImage rotate_270( const QImage& );
958
959//--------------------------------------------------------------------------
960// return a thumbnail that respects the orientation flag
961// only if it seems sane
962//--------------------------------------------------------------------------
963QImage 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 */
987static QImage flip_image( const QImage& img ) {
988 return img.mirror( TRUE, FALSE );
989}
990
991
992static QImage dest;
993static int x, y;
994static unsigned int *srcData, *destData; // we're not threaded anyway
995static unsigned char *srcData8, *destData8; // 8 bit is char
996static unsigned int *srcTable, *destTable; // destination table
997
998
999static 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
1016static 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
1030static 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
1037static 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
1048static 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
1064static 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
1072static 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
1090static 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
1102static 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
1110static QString color_mode_to_string( bool b ) {
1111 return b ? QObject::tr( "Colormode: Color\n" ) : QObject::tr( "Colormode: Black and white\n" );
1112}
1113
1114static 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
1134static 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
1175static 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
1219static 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
1254static 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
1291JpegSlave::JpegSlave()
1292 : SlaveInterface( QStringList::split( " ", "jpeg jpg" ) )
1293{}
1294
1295JpegSlave::~JpegSlave() {}
1296
1297QString 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 */
1324QString 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
1413QPixmap 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}