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