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