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