]> Creatis software - clitk.git/blob - utilities/CxImage/ximaexif.cpp
cosmetic
[clitk.git] / utilities / CxImage / ximaexif.cpp
1 /*
2  * File:        ximaexif.cpp
3  * Purpose:     EXIF reader
4  * 18/Aug/2002 Davide Pizzolato - www.xdp.it
5  * CxImage version 6.0.0 02/Feb/2008
6  * based on jhead-1.8 by Matthias Wandel <mwandel(at)rim(dot)net>
7  */
8
9 #include "ximajpg.h"
10
11 #if CXIMAGEJPG_SUPPORT_EXIF
12
13 ////////////////////////////////////////////////////////////////////////////////
14 CxImageJPG::CxExifInfo::CxExifInfo(EXIFINFO* info)
15 {
16         if (info) {
17                 m_exifinfo = info;
18                 freeinfo = false;
19         } else {
20                 m_exifinfo = new EXIFINFO;
21                 memset(m_exifinfo,0,sizeof(EXIFINFO));
22                 freeinfo = true;
23         }
24
25         m_szLastError[0]='\0';
26         ExifImageWidth = MotorolaOrder = 0;
27         SectionsRead=0;
28         memset(&Sections, 0, MAX_SECTIONS * sizeof(Section_t));
29 }
30 ////////////////////////////////////////////////////////////////////////////////
31 CxImageJPG::CxExifInfo::~CxExifInfo()
32 {
33         for(int i=0;i<MAX_SECTIONS;i++) if(Sections[i].Data) free(Sections[i].Data);
34         if (freeinfo) delete m_exifinfo;
35 }
36 ////////////////////////////////////////////////////////////////////////////////
37 bool CxImageJPG::CxExifInfo::DecodeExif(CxFile * hFile, int nReadMode)
38 {
39     int a;
40     int HaveCom = FALSE;
41
42     a = hFile->GetC();
43
44     if (a != 0xff || hFile->GetC() != M_SOI){
45         return FALSE;
46     }
47
48     for(;;){
49         int itemlen;
50         int marker = 0;
51         int ll,lh, got;
52         BYTE * Data;
53
54         if (SectionsRead >= MAX_SECTIONS){
55                         strcpy(m_szLastError,"Too many sections in jpg file");
56                         return false;
57         }
58
59         for (a=0;a<7;a++){
60             marker = hFile->GetC();
61             if (marker != 0xff) break;
62
63             if (a >= 6){
64                 printf("too many padding bytes\n");
65                 return false;
66             }
67         }
68
69         if (marker == 0xff){
70             // 0xff is legal padding, but if we get that many, something's wrong.
71             strcpy(m_szLastError,"too many padding bytes!");
72                         return false;
73         }
74
75         Sections[SectionsRead].Type = marker;
76
77         // Read the length of the section.
78         lh = hFile->GetC();
79         ll = hFile->GetC();
80
81         itemlen = (lh << 8) | ll;
82
83         if (itemlen < 2){
84             strcpy(m_szLastError,"invalid marker");
85                         return false;
86         }
87
88         Sections[SectionsRead].Size = itemlen;
89
90         Data = (BYTE *)malloc(itemlen);
91         if (Data == NULL){
92             strcpy(m_szLastError,"Could not allocate memory");
93                         return false;
94         }
95         Sections[SectionsRead].Data = Data;
96
97         // Store first two pre-read bytes.
98         Data[0] = (BYTE)lh;
99         Data[1] = (BYTE)ll;
100
101         got = hFile->Read(Data+2, 1, itemlen-2); // Read the whole section.
102         if (got != itemlen-2){
103             strcpy(m_szLastError,"Premature end of file?");
104                         return false;
105         }
106         SectionsRead += 1;
107
108         switch(marker){
109
110             case M_SOS:   // stop before hitting compressed data 
111                 // If reading entire image is requested, read the rest of the data.
112                 if (nReadMode & EXIF_READ_IMAGE){
113                     int cp, ep, size;
114                     // Determine how much file is left.
115                     cp = hFile->Tell();
116                     hFile->Seek(0, SEEK_END);
117                     ep = hFile->Tell();
118                     hFile->Seek(cp, SEEK_SET);
119
120                     size = ep-cp;
121                     Data = (BYTE *)malloc(size);
122                     if (Data == NULL){
123                         strcpy(m_szLastError,"could not allocate data for entire image");
124                                                 return false;
125                     }
126
127                     got = hFile->Read(Data, 1, size);
128                     if (got != size){
129                         strcpy(m_szLastError,"could not read the rest of the image");
130                                                 return false;
131                     }
132
133                     Sections[SectionsRead].Data = Data;
134                     Sections[SectionsRead].Size = size;
135                     Sections[SectionsRead].Type = PSEUDO_IMAGE_MARKER;
136                     SectionsRead ++;
137                 }
138                 return true;
139
140             case M_EOI:   // in case it's a tables-only JPEG stream
141                 printf("No image in jpeg!\n");
142                 return FALSE;
143
144             case M_COM: // Comment section
145                 if (HaveCom || ((nReadMode & EXIF_READ_EXIF) == 0)){
146                     // Discard this section.
147                     free(Sections[--SectionsRead].Data);
148                                         Sections[SectionsRead].Data=0;
149                 }else{
150                     process_COM(Data, itemlen);
151                     HaveCom = TRUE;
152                 }
153                 break;
154
155             case M_JFIF:
156                 // Regular jpegs always have this tag, exif images have the exif
157                 // marker instead, althogh ACDsee will write images with both markers.
158                 // this program will re-create this marker on absence of exif marker.
159                 // hence no need to keep the copy from the file.
160                 free(Sections[--SectionsRead].Data);
161                                 Sections[SectionsRead].Data=0;
162                 break;
163
164             case M_EXIF:
165                 // Seen files from some 'U-lead' software with Vivitar scanner
166                 // that uses marker 31 for non exif stuff.  Thus make sure 
167                 // it says 'Exif' in the section before treating it as exif.
168                 if ((nReadMode & EXIF_READ_EXIF) && memcmp(Data+2, "Exif", 4) == 0){
169                     m_exifinfo->IsExif = process_EXIF((BYTE *)Data+2, itemlen);
170                 }else{
171                     // Discard this section.
172                     free(Sections[--SectionsRead].Data);
173                                         Sections[SectionsRead].Data=0;
174                 }
175                 break;
176
177             case M_SOF0: 
178             case M_SOF1: 
179             case M_SOF2: 
180             case M_SOF3: 
181             case M_SOF5: 
182             case M_SOF6: 
183             case M_SOF7: 
184             case M_SOF9: 
185             case M_SOF10:
186             case M_SOF11:
187             case M_SOF13:
188             case M_SOF14:
189             case M_SOF15:
190                 process_SOFn(Data, marker);
191                 break;
192             default:
193                 // Skip any other sections.
194                 //if (ShowTags) printf("Jpeg section marker 0x%02x size %d\n",marker, itemlen);
195                 break;
196         }
197     }
198         return true;
199 }
200 ////////////////////////////////////////////////////////////////////////////////
201 /*--------------------------------------------------------------------------
202    Process a EXIF marker
203    Describes all the drivel that most digital cameras include...
204 --------------------------------------------------------------------------*/
205 bool CxImageJPG::CxExifInfo::process_EXIF(unsigned char * CharBuf, unsigned int length)
206 {
207     m_exifinfo->FlashUsed = 0; 
208     /* If it's from a digicam, and it used flash, it says so. */
209     m_exifinfo->Comments[0] = '\0';  /* Initial value - null string */
210
211     ExifImageWidth = 0;
212
213     {   /* Check the EXIF header component */
214         static const unsigned char ExifHeader[] = "Exif\0\0";
215         if (memcmp(CharBuf+0, ExifHeader,6)){
216                         strcpy(m_szLastError,"Incorrect Exif header");
217                         return false;
218                 }
219     }
220
221     if (memcmp(CharBuf+6,"II",2) == 0){
222         MotorolaOrder = 0;
223     }else{
224         if (memcmp(CharBuf+6,"MM",2) == 0){
225             MotorolaOrder = 1;
226         }else{
227             strcpy(m_szLastError,"Invalid Exif alignment marker.");
228                         return false;
229         }
230     }
231
232     /* Check the next two values for correctness. */
233     if (Get16u(CharBuf+8) != 0x2a){
234         strcpy(m_szLastError,"Invalid Exif start (1)");
235                 return false;
236     }
237
238         int FirstOffset = Get32u(CharBuf+10);
239     /* <Richard Collins> 
240         if (FirstOffset < 8 || FirstOffset > 16){
241         // I used to ensure this was set to 8 (website I used indicated its 8)
242         // but PENTAX Optio 230 has it set differently, and uses it as offset. (Sept 11 2002)
243         strcpy(m_szLastError,"Suspicious offset of first IFD value");
244                 return false;
245     }*/
246
247     unsigned char * LastExifRefd = CharBuf;
248
249     /* First directory starts 16 bytes in.  Offsets start at 8 bytes in. */
250     if (!ProcessExifDir(CharBuf+14, CharBuf+6, length-6, m_exifinfo, &LastExifRefd))
251                 return false;
252
253         /* <Richard Collins> give a chance for a second directory */
254         if (FirstOffset > 8) {
255                 if (!ProcessExifDir(CharBuf+14+FirstOffset-8, CharBuf+6, length-6, m_exifinfo, &LastExifRefd))
256                         return false;
257         }
258
259     /* This is how far the interesting (non thumbnail) part of the exif went. */
260     // int ExifSettingsLength = LastExifRefd - CharBuf;
261
262     /* Compute the CCD width, in milimeters. */
263     if (m_exifinfo->FocalplaneXRes != 0){
264         m_exifinfo->CCDWidth = (float)(ExifImageWidth * m_exifinfo->FocalplaneUnits / m_exifinfo->FocalplaneXRes);
265     }
266
267         return true;
268 }
269 //--------------------------------------------------------------------------
270 // Get 16 bits motorola order (always) for jpeg header stuff.
271 //--------------------------------------------------------------------------
272 int CxImageJPG::CxExifInfo::Get16m(void * Short)
273 {
274     return (((unsigned char *)Short)[0] << 8) | ((unsigned char *)Short)[1];
275 }
276 ////////////////////////////////////////////////////////////////////////////////
277 /*--------------------------------------------------------------------------
278    Convert a 16 bit unsigned value from file's native byte order
279 --------------------------------------------------------------------------*/
280 int CxImageJPG::CxExifInfo::Get16u(void * Short)
281 {
282     if (MotorolaOrder){
283         return (((unsigned char *)Short)[0] << 8) | ((unsigned char *)Short)[1];
284     }else{
285         return (((unsigned char *)Short)[1] << 8) | ((unsigned char *)Short)[0];
286     }
287 }
288 ////////////////////////////////////////////////////////////////////////////////
289 /*--------------------------------------------------------------------------
290    Convert a 32 bit signed value from file's native byte order
291 --------------------------------------------------------------------------*/
292 long CxImageJPG::CxExifInfo::Get32s(void * Long)
293 {
294     if (MotorolaOrder){
295         return  ((( char *)Long)[0] << 24) | (((unsigned char *)Long)[1] << 16)
296               | (((unsigned char *)Long)[2] << 8 ) | (((unsigned char *)Long)[3] << 0 );
297     }else{
298         return  ((( char *)Long)[3] << 24) | (((unsigned char *)Long)[2] << 16)
299               | (((unsigned char *)Long)[1] << 8 ) | (((unsigned char *)Long)[0] << 0 );
300     }
301 }
302 ////////////////////////////////////////////////////////////////////////////////
303 /*--------------------------------------------------------------------------
304    Convert a 32 bit unsigned value from file's native byte order
305 --------------------------------------------------------------------------*/
306 unsigned long CxImageJPG::CxExifInfo::Get32u(void * Long)
307 {
308     return (unsigned long)Get32s(Long) & 0xffffffff;
309 }
310 ////////////////////////////////////////////////////////////////////////////////
311
312 /* Describes format descriptor */
313 static const int BytesPerFormat[] = {0,1,1,2,4,8,1,1,2,4,8,4,8};
314 #define NUM_FORMATS 12
315
316 #define FMT_BYTE       1 
317 #define FMT_STRING     2
318 #define FMT_USHORT     3
319 #define FMT_ULONG      4
320 #define FMT_URATIONAL  5
321 #define FMT_SBYTE      6
322 #define FMT_UNDEFINED  7
323 #define FMT_SSHORT     8
324 #define FMT_SLONG      9
325 #define FMT_SRATIONAL 10
326 #define FMT_SINGLE    11
327 #define FMT_DOUBLE    12
328
329 /* Describes tag values */
330
331 #define TAG_EXIF_VERSION      0x9000
332 #define TAG_EXIF_OFFSET       0x8769
333 #define TAG_INTEROP_OFFSET    0xa005
334
335 #define TAG_MAKE              0x010F
336 #define TAG_MODEL             0x0110
337
338 #define TAG_ORIENTATION       0x0112
339 #define TAG_XRESOLUTION       0x011A
340 #define TAG_YRESOLUTION       0x011B
341 #define TAG_RESOLUTIONUNIT    0x0128
342
343 #define TAG_EXPOSURETIME      0x829A
344 #define TAG_FNUMBER           0x829D
345
346 #define TAG_SHUTTERSPEED      0x9201
347 #define TAG_APERTURE          0x9202
348 #define TAG_BRIGHTNESS        0x9203
349 #define TAG_MAXAPERTURE       0x9205
350 #define TAG_FOCALLENGTH       0x920A
351
352 #define TAG_DATETIME_ORIGINAL 0x9003
353 #define TAG_USERCOMMENT       0x9286
354
355 #define TAG_SUBJECT_DISTANCE  0x9206
356 #define TAG_FLASH             0x9209
357
358 #define TAG_FOCALPLANEXRES    0xa20E
359 #define TAG_FOCALPLANEYRES    0xa20F
360 #define TAG_FOCALPLANEUNITS   0xa210
361 #define TAG_EXIF_IMAGEWIDTH   0xA002
362 #define TAG_EXIF_IMAGELENGTH  0xA003
363
364 /* the following is added 05-jan-2001 vcs */
365 #define TAG_EXPOSURE_BIAS     0x9204
366 #define TAG_WHITEBALANCE      0x9208
367 #define TAG_METERING_MODE     0x9207
368 #define TAG_EXPOSURE_PROGRAM  0x8822
369 #define TAG_ISO_EQUIVALENT    0x8827
370 #define TAG_COMPRESSION_LEVEL 0x9102
371
372 #define TAG_THUMBNAIL_OFFSET  0x0201
373 #define TAG_THUMBNAIL_LENGTH  0x0202
374
375
376 /*--------------------------------------------------------------------------
377    Process one of the nested EXIF directories.
378 --------------------------------------------------------------------------*/
379 bool CxImageJPG::CxExifInfo::ProcessExifDir(unsigned char * DirStart, unsigned char * OffsetBase, unsigned ExifLength,
380                            EXIFINFO * const m_exifinfo, unsigned char ** const LastExifRefdP, int NestingLevel)
381 {
382     int de;
383     int a;
384     int NumDirEntries;
385     unsigned ThumbnailOffset = 0;
386     unsigned ThumbnailSize = 0;
387
388     if (NestingLevel > 4){
389         strcpy(m_szLastError,"Maximum directory nesting exceeded (corrupt exif header)");
390         return false;
391     }
392
393     NumDirEntries = Get16u(DirStart);
394
395     if ((DirStart+2+NumDirEntries*12) > (OffsetBase+ExifLength)){
396         strcpy(m_szLastError,"Illegally sized directory");
397                 return false;
398     }
399
400     for (de=0;de<NumDirEntries;de++){
401         int Tag, Format, Components;
402         unsigned char * ValuePtr;
403             /* This actually can point to a variety of things; it must be
404                cast to other types when used.  But we use it as a byte-by-byte
405                cursor, so we declare it as a pointer to a generic byte here.
406             */
407         int ByteCount;
408         unsigned char * DirEntry;
409         DirEntry = DirStart+2+12*de;
410
411         Tag = Get16u(DirEntry);
412         Format = Get16u(DirEntry+2);
413         Components = Get32u(DirEntry+4);
414
415         if ((Format-1) >= NUM_FORMATS) {
416             /* (-1) catches illegal zero case as unsigned underflows to positive large */
417             strcpy(m_szLastError,"Illegal format code in EXIF dir");
418                         return false;
419                 }
420
421         ByteCount = Components * BytesPerFormat[Format];
422
423         if (ByteCount > 4){
424             unsigned OffsetVal;
425             OffsetVal = Get32u(DirEntry+8);
426             /* If its bigger than 4 bytes, the dir entry contains an offset.*/
427             if (OffsetVal+ByteCount > ExifLength){
428                 /* Bogus pointer offset and / or bytecount value */
429                 strcpy(m_szLastError,"Illegal pointer offset value in EXIF.");
430                                 return false;
431             }
432             ValuePtr = OffsetBase+OffsetVal;
433         }else{
434             /* 4 bytes or less and value is in the dir entry itself */
435             ValuePtr = DirEntry+8;
436         }
437
438         if (*LastExifRefdP < ValuePtr+ByteCount){
439             /* Keep track of last byte in the exif header that was
440                actually referenced.  That way, we know where the
441                discardable thumbnail data begins.
442             */
443             *LastExifRefdP = ValuePtr+ByteCount;
444         }
445
446         /* Extract useful components of tag */
447         switch(Tag){
448
449             case TAG_MAKE:
450                 strncpy(m_exifinfo->CameraMake, (char*)ValuePtr, 31);
451                 break;
452
453             case TAG_MODEL:
454                 strncpy(m_exifinfo->CameraModel, (char*)ValuePtr, 39);
455                 break;
456
457                         case TAG_EXIF_VERSION:
458                                 strncpy(m_exifinfo->Version,(char*)ValuePtr, 4);
459                                 break;
460
461             case TAG_DATETIME_ORIGINAL:
462                 strncpy(m_exifinfo->DateTime, (char*)ValuePtr, 19);
463                 break;
464
465             case TAG_USERCOMMENT:
466                 // Olympus has this padded with trailing spaces. Remove these first. 
467                 for (a=ByteCount;;){
468                     a--;
469                     if (((char*)ValuePtr)[a] == ' '){
470                         ((char*)ValuePtr)[a] = '\0';
471                     }else{
472                         break;
473                     }
474                     if (a == 0) break;
475                 }
476
477                 /* Copy the comment */
478                 if (memcmp(ValuePtr, "ASCII",5) == 0){
479                     for (a=5;a<10;a++){
480                         char c;
481                         c = ((char*)ValuePtr)[a];
482                         if (c != '\0' && c != ' '){
483                             strncpy(m_exifinfo->Comments, (char*)ValuePtr+a, 199);
484                             break;
485                         }
486                     }
487                     
488                 }else{
489                     strncpy(m_exifinfo->Comments, (char*)ValuePtr, 199);
490                 }
491                 break;
492
493             case TAG_FNUMBER:
494                 /* Simplest way of expressing aperture, so I trust it the most.
495                    (overwrite previously computd value if there is one)
496                    */
497                 m_exifinfo->ApertureFNumber = (float)ConvertAnyFormat(ValuePtr, Format);
498                 break;
499
500             case TAG_APERTURE:
501             case TAG_MAXAPERTURE:
502                 /* More relevant info always comes earlier, so only
503                  use this field if we don't have appropriate aperture
504                  information yet. 
505                 */
506                 if (m_exifinfo->ApertureFNumber == 0){
507                     m_exifinfo->ApertureFNumber = (float)exp(ConvertAnyFormat(ValuePtr, Format)*log(2.0f)*0.5);
508                 }
509                 break;
510
511                         case TAG_BRIGHTNESS:
512                                 m_exifinfo->Brightness = (float)ConvertAnyFormat(ValuePtr, Format);
513                                 break;
514
515             case TAG_FOCALLENGTH:
516                 /* Nice digital cameras actually save the focal length
517                    as a function of how farthey are zoomed in. 
518                 */
519
520                 m_exifinfo->FocalLength = (float)ConvertAnyFormat(ValuePtr, Format);
521                 break;
522
523             case TAG_SUBJECT_DISTANCE:
524                 /* Inidcates the distacne the autofocus camera is focused to.
525                    Tends to be less accurate as distance increases.
526                 */
527                 m_exifinfo->Distance = (float)ConvertAnyFormat(ValuePtr, Format);
528                 break;
529
530             case TAG_EXPOSURETIME:
531                 /* Simplest way of expressing exposure time, so I
532                    trust it most.  (overwrite previously computd value
533                    if there is one) 
534                 */
535                 m_exifinfo->ExposureTime = 
536                     (float)ConvertAnyFormat(ValuePtr, Format);
537                 break;
538
539             case TAG_SHUTTERSPEED:
540                 /* More complicated way of expressing exposure time,
541                    so only use this value if we don't already have it
542                    from somewhere else.  
543                 */
544                 if (m_exifinfo->ExposureTime == 0){
545                     m_exifinfo->ExposureTime = (float)
546                         (1/exp(ConvertAnyFormat(ValuePtr, Format)*log(2.0f)));
547                 }
548                 break;
549
550             case TAG_FLASH:
551                 if ((int)ConvertAnyFormat(ValuePtr, Format) & 7){
552                     m_exifinfo->FlashUsed = 1;
553                 }else{
554                     m_exifinfo->FlashUsed = 0;
555                 }
556                 break;
557
558             case TAG_ORIENTATION:
559                 m_exifinfo->Orientation = (int)ConvertAnyFormat(ValuePtr, Format);
560                 if (m_exifinfo->Orientation < 1 || m_exifinfo->Orientation > 8){
561                     strcpy(m_szLastError,"Undefined rotation value");
562                     m_exifinfo->Orientation = 0;
563                 }
564                 break;
565
566             case TAG_EXIF_IMAGELENGTH:
567             case TAG_EXIF_IMAGEWIDTH:
568                 /* Use largest of height and width to deal with images
569                    that have been rotated to portrait format.  
570                 */
571                 a = (int)ConvertAnyFormat(ValuePtr, Format);
572                 if (ExifImageWidth < a) ExifImageWidth = a;
573                 break;
574
575             case TAG_FOCALPLANEXRES:
576                 m_exifinfo->FocalplaneXRes = (float)ConvertAnyFormat(ValuePtr, Format);
577                 break;
578
579             case TAG_FOCALPLANEYRES:
580                 m_exifinfo->FocalplaneYRes = (float)ConvertAnyFormat(ValuePtr, Format);
581                 break;
582
583                         case TAG_RESOLUTIONUNIT:
584                 switch((int)ConvertAnyFormat(ValuePtr, Format)){
585                     case 1: m_exifinfo->ResolutionUnit = 1.0f; break; /* 1 inch */
586                     case 2:     m_exifinfo->ResolutionUnit = 1.0f; break;
587                     case 3: m_exifinfo->ResolutionUnit = 0.3937007874f;    break;  /* 1 centimeter*/
588                     case 4: m_exifinfo->ResolutionUnit = 0.03937007874f;   break;  /* 1 millimeter*/
589                     case 5: m_exifinfo->ResolutionUnit = 0.00003937007874f;  /* 1 micrometer*/
590                 }
591                 break;
592
593             case TAG_FOCALPLANEUNITS:
594                 switch((int)ConvertAnyFormat(ValuePtr, Format)){
595                     case 1: m_exifinfo->FocalplaneUnits = 1.0f; break; /* 1 inch */
596                     case 2:     m_exifinfo->FocalplaneUnits = 1.0f; break;
597                     case 3: m_exifinfo->FocalplaneUnits = 0.3937007874f;    break;  /* 1 centimeter*/
598                     case 4: m_exifinfo->FocalplaneUnits = 0.03937007874f;   break;  /* 1 millimeter*/
599                     case 5: m_exifinfo->FocalplaneUnits = 0.00003937007874f;  /* 1 micrometer*/
600                 }
601                 break;
602
603                 // Remaining cases contributed by: Volker C. Schoech <schoech(at)gmx(dot)de>
604
605             case TAG_EXPOSURE_BIAS:
606                 m_exifinfo->ExposureBias = (float) ConvertAnyFormat(ValuePtr, Format);
607                 break;
608
609             case TAG_WHITEBALANCE:
610                 m_exifinfo->Whitebalance = (int)ConvertAnyFormat(ValuePtr, Format);
611                 break;
612
613             case TAG_METERING_MODE:
614                 m_exifinfo->MeteringMode = (int)ConvertAnyFormat(ValuePtr, Format);
615                 break;
616
617             case TAG_EXPOSURE_PROGRAM:
618                 m_exifinfo->ExposureProgram = (int)ConvertAnyFormat(ValuePtr, Format);
619                 break;
620
621             case TAG_ISO_EQUIVALENT:
622                 m_exifinfo->ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format);
623                 if ( m_exifinfo->ISOequivalent < 50 ) m_exifinfo->ISOequivalent *= 200;
624                 break;
625
626             case TAG_COMPRESSION_LEVEL:
627                 m_exifinfo->CompressionLevel = (int)ConvertAnyFormat(ValuePtr, Format);
628                 break;
629
630             case TAG_XRESOLUTION:
631                 m_exifinfo->Xresolution = (float)ConvertAnyFormat(ValuePtr, Format);
632                 break;
633             case TAG_YRESOLUTION:
634                 m_exifinfo->Yresolution = (float)ConvertAnyFormat(ValuePtr, Format);
635                 break;
636
637             case TAG_THUMBNAIL_OFFSET:
638                 ThumbnailOffset = (unsigned)ConvertAnyFormat(ValuePtr, Format);
639                 break;
640
641             case TAG_THUMBNAIL_LENGTH:
642                 ThumbnailSize = (unsigned)ConvertAnyFormat(ValuePtr, Format);
643                 break;
644
645         }
646
647         if (Tag == TAG_EXIF_OFFSET || Tag == TAG_INTEROP_OFFSET){
648             unsigned char * SubdirStart;
649                         unsigned Offset = Get32u(ValuePtr);
650                         if (Offset>8){
651                                 SubdirStart = OffsetBase + Offset;
652                                 if (SubdirStart < OffsetBase || 
653                                         SubdirStart > OffsetBase+ExifLength){
654                                         strcpy(m_szLastError,"Illegal subdirectory link");
655                                         return false;
656                                 }
657                                 ProcessExifDir(SubdirStart, OffsetBase, ExifLength, m_exifinfo, LastExifRefdP, NestingLevel+1);
658                         }
659             continue;
660         }
661     }
662
663
664     {
665         /* In addition to linking to subdirectories via exif tags,
666            there's also a potential link to another directory at the end
667            of each directory.  This has got to be the result of a
668            committee!  
669         */
670         unsigned char * SubdirStart;
671         unsigned Offset;
672         Offset = Get16u(DirStart+2+12*NumDirEntries);
673         if (Offset){
674             SubdirStart = OffsetBase + Offset;
675             if (SubdirStart < OffsetBase 
676                 || SubdirStart > OffsetBase+ExifLength){
677                 strcpy(m_szLastError,"Illegal subdirectory link");
678                                 return false;
679             }
680             ProcessExifDir(SubdirStart, OffsetBase, ExifLength, m_exifinfo, LastExifRefdP, NestingLevel+1);
681         }
682     }
683
684
685     if (ThumbnailSize && ThumbnailOffset){
686         if (ThumbnailSize + ThumbnailOffset <= ExifLength){
687             /* The thumbnail pointer appears to be valid.  Store it. */
688             m_exifinfo->ThumbnailPointer = OffsetBase + ThumbnailOffset;
689             m_exifinfo->ThumbnailSize = ThumbnailSize;
690         }
691     }
692
693         return true;
694 }
695 ////////////////////////////////////////////////////////////////////////////////
696 /*--------------------------------------------------------------------------
697    Evaluate number, be it int, rational, or float from directory.
698 --------------------------------------------------------------------------*/
699 double CxImageJPG::CxExifInfo::ConvertAnyFormat(void * ValuePtr, int Format)
700 {
701     double Value;
702     Value = 0;
703
704     switch(Format){
705         case FMT_SBYTE:     Value = *(signed char *)ValuePtr;  break;
706         case FMT_BYTE:      Value = *(unsigned char *)ValuePtr;        break;
707
708         case FMT_USHORT:    Value = Get16u(ValuePtr);          break;
709         case FMT_ULONG:     Value = Get32u(ValuePtr);          break;
710
711         case FMT_URATIONAL:
712         case FMT_SRATIONAL: 
713             {
714                 int Num,Den;
715                 Num = Get32s(ValuePtr);
716                 Den = Get32s(4+(char *)ValuePtr);
717                 if (Den == 0){
718                     Value = 0;
719                 }else{
720                     Value = (double)Num/Den;
721                 }
722                 break;
723             }
724
725         case FMT_SSHORT:    Value = (signed short)Get16u(ValuePtr);  break;
726         case FMT_SLONG:     Value = Get32s(ValuePtr);                break;
727
728         /* Not sure if this is correct (never seen float used in Exif format)
729          */
730         case FMT_SINGLE:    Value = (double)*(float *)ValuePtr;      break;
731         case FMT_DOUBLE:    Value = *(double *)ValuePtr;             break;
732     }
733     return Value;
734 }
735 ////////////////////////////////////////////////////////////////////////////////
736 void CxImageJPG::CxExifInfo::process_COM (const BYTE * Data, int length)
737 {
738     int ch;
739     char Comment[MAX_COMMENT+1];
740     int nch;
741     int a;
742
743     nch = 0;
744
745     if (length > MAX_COMMENT) length = MAX_COMMENT; // Truncate if it won't fit in our structure.
746
747     for (a=2;a<length;a++){
748         ch = Data[a];
749
750         if (ch == '\r' && Data[a+1] == '\n') continue; // Remove cr followed by lf.
751
752         if (isprint(ch) || ch == '\n' || ch == '\t'){
753             Comment[nch++] = (char)ch;
754         }else{
755             Comment[nch++] = '?';
756         }
757     }
758
759     Comment[nch] = '\0'; // Null terminate
760
761     //if (ShowTags) printf("COM marker comment: %s\n",Comment);
762
763     strcpy(m_exifinfo->Comments,Comment);
764 }
765 ////////////////////////////////////////////////////////////////////////////////
766 void CxImageJPG::CxExifInfo::process_SOFn (const BYTE * Data, int marker)
767 {
768     int data_precision, num_components;
769
770     data_precision = Data[2];
771     m_exifinfo->Height = Get16m((void*)(Data+3));
772     m_exifinfo->Width = Get16m((void*)(Data+5));
773     num_components = Data[7];
774
775     if (num_components == 3){
776         m_exifinfo->IsColor = 1;
777     }else{
778         m_exifinfo->IsColor = 0;
779     }
780
781     m_exifinfo->Process = marker;
782
783     //if (ShowTags) printf("JPEG image is %uw * %uh, %d color components, %d bits per sample\n",
784     //               ImageInfo.Width, ImageInfo.Height, num_components, data_precision);
785 }
786 ////////////////////////////////////////////////////////////////////////////////
787 /**
788  * this will work only on a CxImageJPG object, if the image originally has valid EXIF data
789  \verbatim
790         CxImageJPG jpg;
791         CxIOFile in,out;
792         in.Open("D:\\exif_in.jpg","rb");
793         out.Open("D:\\exif_out.jpg","w+b");
794         jpg.Decode(&in);
795         if (jpg.IsValid()){
796                 jpg.RotateLeft();
797                 jpg.Encode(&out);
798         }
799  \endverbatim
800 */
801 bool CxImageJPG::CxExifInfo::EncodeExif(CxFile * hFile)
802 {
803     int a;
804
805     if (FindSection(M_SOS)==NULL){
806                 strcpy(m_szLastError,"Can't write exif : didn't read all");
807                 return false;
808     }
809
810     // Initial static jpeg marker.
811         hFile->PutC(0xff);
812         hFile->PutC(0xd8);
813     
814     if (Sections[0].Type != M_EXIF && Sections[0].Type != M_JFIF){
815         // The image must start with an exif or jfif marker.  If we threw those away, create one.
816         static BYTE JfifHead[18] = {
817             0xff, M_JFIF,
818             0x00, 0x10, 'J' , 'F' , 'I' , 'F' , 0x00, 0x01, 
819             0x01, 0x01, 0x01, 0x2C, 0x01, 0x2C, 0x00, 0x00 
820         };
821         hFile->Write(JfifHead, 18, 1);
822     }
823
824     // Write all the misc sections
825     for (a=0;a<SectionsRead-1;a++){
826         hFile->PutC(0xff);
827         hFile->PutC((unsigned char)(Sections[a].Type));
828         hFile->Write(Sections[a].Data, Sections[a].Size, 1);
829     }
830
831     // Write the remaining image data.
832     hFile->Write(Sections[a].Data, Sections[a].Size, 1);
833
834         return true;
835 }
836 ////////////////////////////////////////////////////////////////////////////////
837 void CxImageJPG::CxExifInfo::DiscardAllButExif()
838 {
839     Section_t ExifKeeper;
840     Section_t CommentKeeper;
841     int a;
842
843     memset(&ExifKeeper, 0, sizeof(ExifKeeper));
844     memset(&CommentKeeper, 0, sizeof(ExifKeeper));
845
846     for (a=0;a<SectionsRead;a++){
847         if (Sections[a].Type == M_EXIF && ExifKeeper.Type == 0){
848             ExifKeeper = Sections[a];
849         }else if (Sections[a].Type == M_COM && CommentKeeper.Type == 0){
850             CommentKeeper = Sections[a];
851         }else{
852             free(Sections[a].Data);
853                         Sections[a].Data = 0;
854         }
855     }
856     SectionsRead = 0;
857     if (ExifKeeper.Type){
858         Sections[SectionsRead++] = ExifKeeper;
859     }
860     if (CommentKeeper.Type){
861         Sections[SectionsRead++] = CommentKeeper;
862     }
863 }
864 ////////////////////////////////////////////////////////////////////////////////
865 void* CxImageJPG::CxExifInfo::FindSection(int SectionType)
866 {
867     int a;
868     for (a=0;a<SectionsRead-1;a++){
869         if (Sections[a].Type == SectionType){
870             return &Sections[a];
871         }
872     }
873     // Could not be found.
874     return NULL;
875 }
876 ////////////////////////////////////////////////////////////////////////////////
877 #endif  // CXIMAGEJPG_SUPPORT_EXIF
878