1 /******************************************************************************
2  * $Id: fitdataset.cpp 28785 2015-03-26 20:46:45Z goatbar $
3  *
4  * Project:  FIT Driver
5  * Purpose:  Implement FIT Support - not using the SGI iflFIT library.
6  * Author:   Philip Nemec, nemec@keyholecorp.com
7  *
8  ******************************************************************************
9  * Copyright (c) 2001, Keyhole, Inc.
10  * Copyright (c) 2007-2011, Even Rouault <even dot rouault at mines-paris dot org>
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a
13  * copy of this software and associated documentation files (the "Software"),
14  * to deal in the Software without restriction, including without limitation
15  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16  * and/or sell copies of the Software, and to permit persons to whom the
17  * Software is furnished to do so, subject to the following conditions:
18  *
19  * The above copyright notice and this permission notice shall be included
20  * in all copies or substantial portions of the Software.
21  *
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28  * DEALINGS IN THE SOFTWARE.
29  ****************************************************************************/
30 
31 #include "fit.h"
32 #include "gstEndian.h"
33 #include "gdal_pam.h"
34 #include "cpl_string.h"
35 
36 CPL_CVSID("$Id: fitdataset.cpp 28785 2015-03-26 20:46:45Z goatbar $");
37 
38 CPL_C_START
39 
40 void	GDALRegister_FIT(void);
41 CPL_C_END
42 
43 #define FIT_WRITE
44 
45 #define FIT_PAGE_SIZE 128
46 
47 using namespace gstEndian;
48 
49 /************************************************************************/
50 /* ==================================================================== */
51 /*				FITDataset				*/
52 /* ==================================================================== */
53 /************************************************************************/
54 
55 class FITRasterBand;
56 
57 class FITDataset : public GDALPamDataset
58 {
59     friend class FITRasterBand;
60 
61     VSILFILE	*fp;
62     FITinfo	*info;
63     double      adfGeoTransform[6];
64 
65   public:
66     FITDataset();
67     ~FITDataset();
68     static GDALDataset *Open( GDALOpenInfo * );
69 //     virtual CPLErr GetGeoTransform( double * );
70 };
71 
72 #ifdef FIT_WRITE
73 static GDALDataset *FITCreateCopy(const char * pszFilename,
74                                   GDALDataset *poSrcDS,
75                                   int bStrict, char ** papszOptions,
76                                   GDALProgressFunc pfnProgress,
77                                   void * pProgressData );
78 #endif // FIT_WRITE
79 
80 /************************************************************************/
81 /* ==================================================================== */
82 /*                            FITRasterBand                             */
83 /* ==================================================================== */
84 /************************************************************************/
85 
86 class FITRasterBand : public GDALPamRasterBand
87 {
88     friend class FITDataset;
89 
90     unsigned long recordSize; // number of bytes of a single page/block/record
91     unsigned long numXBlocks; // number of pages in the X direction
92     unsigned long numYBlocks; // number of pages in the Y direction
93     unsigned long bytesPerComponent;
94     unsigned long bytesPerPixel;
95     char *tmpImage;
96 
97 public:
98 
99     FITRasterBand( FITDataset *, int );
100     ~FITRasterBand();
101 
102     // should override RasterIO eventually.
103 
104     virtual CPLErr IReadBlock( int, int, void * );
105 //     virtual CPLErr WriteBlock( int, int, void * );
106     virtual double GetMinimum( int *pbSuccess );
107     virtual double GetMaximum( int *pbSuccess );
108     virtual GDALColorInterp GetColorInterpretation();
109 };
110 
111 
112 /************************************************************************/
113 /*                           FITRasterBand()                            */
114 /************************************************************************/
115 
FITRasterBand(FITDataset * poDS,int nBand)116 FITRasterBand::FITRasterBand( FITDataset *poDS, int nBand ) : tmpImage( NULL )
117 
118 {
119     this->poDS = poDS;
120     this->nBand = nBand;
121 
122 /* -------------------------------------------------------------------- */
123 /*      Get the GDAL data type.                                         */
124 /* -------------------------------------------------------------------- */
125     eDataType = fitDataType(poDS->info->dtype);
126 
127 /* -------------------------------------------------------------------- */
128 /*      Get the page sizes.                                             */
129 /* -------------------------------------------------------------------- */
130     nBlockXSize = poDS->info->xPageSize;
131     nBlockYSize = poDS->info->yPageSize;
132 
133 /* -------------------------------------------------------------------- */
134 /*      Caculate the values for record offset calculations.             */
135 /* -------------------------------------------------------------------- */
136     bytesPerComponent = (GDALGetDataTypeSize(eDataType) / 8);
137     bytesPerPixel = poDS->nBands * bytesPerComponent;
138     recordSize = bytesPerPixel * nBlockXSize * nBlockYSize;
139     numXBlocks =
140         (unsigned long) ceil((double) poDS->info->xSize / nBlockXSize);
141     numYBlocks =
142         (unsigned long) ceil((double) poDS->info->ySize / nBlockYSize);
143 
144     tmpImage = (char *) malloc(recordSize);
145     if (! tmpImage)
146         CPLError(CE_Fatal, CPLE_NotSupported,
147                  "FITRasterBand couldn't allocate %lu bytes", recordSize);
148 
149 /* -------------------------------------------------------------------- */
150 /*      Set the access flag.  For now we set it the same as the         */
151 /*      whole dataset, but eventually this should take account of       */
152 /*      locked channels, or read-only secondary data files.             */
153 /* -------------------------------------------------------------------- */
154     /* ... */
155 }
156 
157 
~FITRasterBand()158 FITRasterBand::~FITRasterBand()
159 {
160     if ( tmpImage )
161         free ( tmpImage );
162 }
163 
164 
165 /************************************************************************/
166 /*                            IReadBlock()                              */
167 /************************************************************************/
168 
169 #define COPY_XFIRST(t) { \
170                 t *dstp = (t *) pImage; \
171                 t *srcp = (t *) tmpImage; \
172                 srcp += nBand-1; \
173                 long i = 0; \
174                 for(long y=ystart; y != ystop; y+= yinc) \
175                     for(long x=xstart; x != xstop; x+= xinc, i++) { \
176                         dstp[i] = srcp[(y * nBlockXSize + x) * \
177                                        poFIT_DS->nBands]; \
178                     } \
179     }
180 
181 
182 #define COPY_YFIRST(t) { \
183                 t *dstp = (t *) pImage; \
184                 t *srcp = (t *) tmpImage; \
185                 srcp += nBand-1; \
186                 long i = 0; \
187                 for(long x=xstart; x != xstop; x+= xinc, i++) \
188                     for(long y=ystart; y != ystop; y+= yinc) { \
189                         dstp[i] = srcp[(x * nBlockYSize + y) * \
190                                        poFIT_DS->nBands]; \
191                     } \
192     }
193 
IReadBlock(int nBlockXOff,int nBlockYOff,void * pImage)194 CPLErr FITRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
195                                   void * pImage )
196 
197 {
198     FITDataset	*poFIT_DS = (FITDataset *) poDS;
199 
200     uint64 tilenum = 0;
201 
202     switch (poFIT_DS->info->space) {
203     case 1:
204         // iflUpperLeftOrigin - from upper left corner
205         // scan right then down
206         tilenum = nBlockYOff * numXBlocks + nBlockXOff;
207         break;
208     case 2:
209         // iflUpperRightOrigin - from upper right corner
210         // scan left then down
211         tilenum = numYBlocks * numXBlocks + (numXBlocks-1-nBlockXOff);
212         break;
213     case 3:
214         // iflLowerRightOrigin - from lower right corner
215         // scan left then up
216         tilenum = (numYBlocks-1-nBlockYOff) * numXBlocks +
217             (numXBlocks-1-nBlockXOff);
218         break;
219     case 4:
220         // iflLowerLeftOrigin - from lower left corner
221         // scan right then up
222         tilenum = (numYBlocks-1-nBlockYOff) * numXBlocks + nBlockXOff;
223         break;
224     case 5:
225         // iflLeftUpperOrigin -* from upper left corner
226         // scan down then right
227         tilenum = nBlockXOff * numYBlocks + nBlockYOff;
228         break;
229     case 6:
230         // iflRightUpperOrigin - from upper right corner
231         // scan down then left
232         tilenum = (numXBlocks-1-nBlockXOff) * numYBlocks + nBlockYOff;
233         break;
234     case 7:
235         // iflRightLowerOrigin - from lower right corner
236         // scan up then left
237         tilenum = nBlockXOff * numYBlocks + (numYBlocks-1-nBlockYOff);
238         break;
239     case 8:
240         // iflLeftLowerOrigin -* from lower left corner
241         // scan up then right
242         tilenum = (numXBlocks-1-nBlockXOff) * numYBlocks +
243             (numYBlocks-1-nBlockYOff);
244         break;
245     default:
246         CPLError(CE_Failure, CPLE_NotSupported,
247                  "FIT - unrecognized image space %i",
248                  poFIT_DS->info->space);
249         tilenum = 0;
250     } // switch
251 
252     uint64 offset = poFIT_DS->info->dataOffset + recordSize * tilenum;
253 //     CPLDebug("FIT", "%i RasterBand::IReadBlock %i %i (out of %i %i) -- %i",
254 //              poFIT_DS->info->space,
255 //              nBlockXOff, nBlockYOff, numXBlocks, numYBlocks, tilenum);
256 
257     if ( VSIFSeekL( poFIT_DS->fp, offset, SEEK_SET ) == -1 ) {
258         CPLError(CE_Failure, CPLE_NotSupported,
259                  "FIT - 64bit file seek failure, handle=%p", poFIT_DS->fp );
260 	    return CE_Failure;
261     }
262 
263     // XXX - should handle status
264     // fast path is single component (ll?) - no copy needed
265     char *p;
266     int fastpath = FALSE;
267 
268     if ((poFIT_DS->nBands == 1) && (poFIT_DS->info->space == 1)) // upper left
269         fastpath = TRUE;
270 
271     if (! fastpath) {
272         VSIFReadL( tmpImage, recordSize, 1, poFIT_DS->fp );
273         // offset to correct component to swap
274         p = (char *) tmpImage + nBand-1;
275     }
276     else {
277         VSIFReadL( pImage, recordSize, 1, poFIT_DS->fp );
278         p = (char *) pImage;
279     }
280 
281 
282 #ifdef swapping
283     unsigned long i = 0;
284 
285     switch(bytesPerComponent) {
286     case 1:
287         // do nothing
288         break;
289     case 2:
290         for(i=0; i < recordSize; i+= bytesPerPixel)
291             gst_swap16(p + i);
292         break;
293     case 4:
294         for(i=0; i < recordSize; i+= bytesPerPixel)
295             gst_swap32(p + i);
296         break;
297     case 8:
298         for(i=0; i < recordSize; i+= bytesPerPixel)
299             gst_swap64(p + i);
300         break;
301     default:
302         CPLError(CE_Failure, CPLE_NotSupported,
303                  "FITRasterBand::IReadBlock unsupported bytesPerPixel %lu",
304                  bytesPerComponent);
305     } // switch
306 #else
307     (void) p; // avoid warnings.
308 #endif // swapping
309 
310     if (! fastpath) {
311         long xinc, yinc, xstart, ystart, xstop, ystop;
312         if (poFIT_DS->info->space <= 4) {
313             // scan left/right first
314 
315             switch (poFIT_DS->info->space) {
316             case 1:
317                 // iflUpperLeftOrigin - from upper left corner
318                 // scan right then down
319                 xinc = 1;
320                 yinc = 1;
321                 break;
322             case 2:
323                 // iflUpperRightOrigin - from upper right corner
324                 // scan left then down
325                 xinc = -1;
326                 yinc = 1;
327                 break;
328             case 3:
329                 // iflLowerRightOrigin - from lower right corner
330                 // scan left then up
331                 xinc = -1;
332                 yinc = -1;
333                 break;
334             case 4:
335                 // iflLowerLeftOrigin - from lower left corner
336                 // scan right then up
337                 xinc = 1;
338                 yinc = -1;
339                break;
340             default:
341                 CPLError(CE_Failure, CPLE_NotSupported,
342                          "FIT - unrecognized image space %i",
343                          poFIT_DS->info->space);
344                 xinc = 1;
345                 yinc = 1;
346             } // switch
347 
348 
349             if (xinc == 1) {
350                 xstart = 0;
351                 xstop = nBlockXSize;
352             }
353             else {
354                 xstart = nBlockXSize-1;
355                 xstop = -1;
356             }
357             if (yinc == 1) {
358                 ystart = 0;
359                 ystop = nBlockYSize;
360             }
361             else {
362                 int localBlockYSize = nBlockYSize;
363                 long maxy_full =
364                     (long) floor(poFIT_DS->info->ySize / (double) nBlockYSize);
365                 if (nBlockYOff >= maxy_full)
366                     localBlockYSize = poFIT_DS->info->ySize % nBlockYSize;
367                 ystart = localBlockYSize-1;
368                 ystop = -1;
369             }
370 
371             switch(bytesPerComponent) {
372             case 1:
373                 COPY_XFIRST(char);
374                 break;
375             case 2:
376                 COPY_XFIRST(uint16);
377                 break;
378             case 4:
379                 COPY_XFIRST(uint32);
380                 break;
381             case 8:
382                 COPY_XFIRST(uint64);
383                 break;
384             default:
385                 CPLError(CE_Failure, CPLE_NotSupported,
386                          "FITRasterBand::IReadBlock unsupported "
387                          "bytesPerComponent %lu", bytesPerComponent);
388             } // switch
389 
390         } // scan left/right first
391         else {
392             // scan up/down first
393 
394             switch (poFIT_DS->info->space) {
395             case 5:
396                 // iflLeftUpperOrigin -* from upper left corner
397                 // scan down then right
398                 xinc = 1;
399                 yinc = 1;
400                 break;
401             case 6:
402                 // iflRightUpperOrigin - from upper right corner
403                 // scan down then left
404                 xinc = -1;
405                 yinc = 1;
406                 break;
407             case 7:
408                 // iflRightLowerOrigin - from lower right corner
409                 // scan up then left
410                 xinc = -1;
411                 yinc = -1;
412                 break;
413             case 8:
414                 // iflLeftLowerOrigin -* from lower left corner
415                 // scan up then right
416                 xinc = 1;
417                 yinc = -1;
418                 break;
419             default:
420                 CPLError(CE_Failure, CPLE_NotSupported,
421                          "FIT - unrecognized image space %i",
422                          poFIT_DS->info->space);
423                 xinc = 1;
424                 yinc = 1;
425             } // switch
426 
427             if (xinc == 1) {
428                 xstart = 0;
429                 xstop = nBlockXSize;
430             }
431             else {
432                 int localBlockXSize = nBlockXSize;
433                 long maxx_full =
434                     (long) floor(poFIT_DS->info->xSize / (double) nBlockXSize);
435                 if (nBlockXOff >= maxx_full)
436                     localBlockXSize = poFIT_DS->info->xSize % nBlockXSize;
437                 xstart = localBlockXSize-1;
438                 xstop = -1;
439             }
440             if (yinc == 1) {
441                 ystart = 0;
442                 ystop = nBlockYSize;
443             }
444             else {
445                 ystart = nBlockYSize-1;
446                 ystop = -1;
447             }
448 
449             switch(bytesPerComponent) {
450             case 1:
451                 COPY_YFIRST(char);
452                 break;
453             case 2:
454                 COPY_YFIRST(uint16);
455                 break;
456             case 4:
457                 COPY_YFIRST(uint32);
458                 break;
459             case 8:
460                 COPY_YFIRST(uint64);
461                 break;
462             default:
463                 CPLError(CE_Failure, CPLE_NotSupported,
464                          "FITRasterBand::IReadBlock unsupported "
465                          "bytesPerComponent %lu", bytesPerComponent);
466             } // switch
467 
468         } // scan up/down first
469 
470     } // ! fastpath
471     return CE_None;
472 }
473 
474 #if 0
475 /************************************************************************/
476 /*                             ReadBlock()                              */
477 /************************************************************************/
478 
479 CPLErr FITRasterBand::ReadBlock( int nBlockXOff, int nBlockYOff,
480                                  void * pImage )
481 
482 {
483     FITDataset	*poFIT_DS = (FITDataset *) poDS;
484 
485 
486 
487     return CE_None;
488 }
489 
490 /************************************************************************/
491 /*                             WriteBlock()                             */
492 /************************************************************************/
493 
494 CPLErr FITRasterBand::WriteBlock( int nBlockXOff, int nBlockYOff,
495                                  void * pImage )
496 
497 {
498     FITDataset	*poFIT_DS = (FITDataset *) poDS;
499 
500 
501 
502     return CE_None;
503 }
504 #endif
505 
506 /************************************************************************/
507 /*                             GetMinimum()                             */
508 /************************************************************************/
509 
GetMinimum(int * pbSuccess)510 double FITRasterBand::GetMinimum( int *pbSuccess )
511 {
512     FITDataset *poFIT_DS = (FITDataset *) poDS;
513 
514     if ((! poFIT_DS) || (! poFIT_DS->info))
515         return GDALRasterBand::GetMinimum( pbSuccess );
516 
517     if (pbSuccess)
518         *pbSuccess = TRUE;
519 
520     if (poFIT_DS->info->version &&
521         EQUALN((const char *) &(poFIT_DS->info->version), "02", 2)) {
522         return poFIT_DS->info->minValue;
523     }
524     else {
525         return GDALRasterBand::GetMinimum( pbSuccess );
526     }
527 }
528 
529 /************************************************************************/
530 /*                             GetMaximum()                             */
531 /************************************************************************/
532 
GetMaximum(int * pbSuccess)533 double FITRasterBand::GetMaximum( int *pbSuccess )
534 {
535     FITDataset *poFIT_DS = (FITDataset *) poDS;
536 
537     if ((! poFIT_DS) || (! poFIT_DS->info))
538         return GDALRasterBand::GetMaximum( pbSuccess );
539 
540     if (pbSuccess)
541         *pbSuccess = TRUE;
542 
543     if (EQUALN((const char *) &poFIT_DS->info->version, "02", 2)) {
544         return poFIT_DS->info->maxValue;
545     }
546     else {
547         return GDALRasterBand::GetMaximum( pbSuccess );
548     }
549 }
550 
551 /************************************************************************/
552 /*                       GetColorInterpretation()                       */
553 /************************************************************************/
554 
GetColorInterpretation()555 GDALColorInterp FITRasterBand::GetColorInterpretation()
556 {
557     FITDataset	*poFIT_DS = (FITDataset *) poDS;
558 
559     if ((! poFIT_DS) || (! poFIT_DS->info))
560         return GCI_Undefined;
561 
562     switch(poFIT_DS->info->cm) {
563     case 1: // iflNegative - inverted luminance (min value is white)
564         CPLError( CE_Warning, CPLE_NotSupported,
565                   "FIT - color model Negative not supported - ignoring model");
566             return GCI_Undefined;
567 
568     case 2: // iflLuminance - luminance
569         if (poFIT_DS->nBands != 1) {
570             CPLError( CE_Failure, CPLE_NotSupported,
571                       "FIT - color model Luminance mismatch with %i bands",
572                       poFIT_DS->nBands);
573             return GCI_Undefined;
574         }
575         switch (nBand) {
576         case 1:
577             return GCI_GrayIndex;
578         default:
579             CPLError( CE_Failure, CPLE_NotSupported,
580                       "FIT - color model Luminance unknown band %i", nBand);
581             return GCI_Undefined;
582         } // switch nBand
583 
584     case 3: // iflRGB - full color (Red, Green, Blue triplets)
585         if (poFIT_DS->nBands != 3) {
586             CPLError( CE_Failure, CPLE_NotSupported,
587                       "FIT - color model RGB mismatch with %i bands",
588                       poFIT_DS->nBands);
589             return GCI_Undefined;
590         }
591         switch (nBand) {
592         case 1:
593             return GCI_RedBand;
594         case 2:
595             return GCI_GreenBand;
596         case 3:
597             return GCI_BlueBand;
598         default:
599             CPLError( CE_Failure, CPLE_NotSupported,
600                       "FIT - color model RGB unknown band %i", nBand);
601             return GCI_Undefined;
602         } // switch nBand
603 
604     case 4: // iflRGBPalette - color mapped values
605         CPLError( CE_Warning, CPLE_NotSupported,
606                   "FIT - color model  RGBPalette not supported - "
607                   "ignoring model");
608             return GCI_Undefined;
609 
610     case 5: // iflRGBA - full color with transparency (alpha channel)
611         if (poFIT_DS->nBands != 4) {
612             CPLError( CE_Failure, CPLE_NotSupported,
613                       "FIT - color model RGBA mismatch with %i bands",
614                       poFIT_DS->nBands);
615             return GCI_Undefined;
616         }
617         switch (nBand) {
618         case 1:
619             return GCI_RedBand;
620         case 2:
621             return GCI_GreenBand;
622         case 3:
623             return GCI_BlueBand;
624         case 4:
625             return GCI_AlphaBand;
626         default:
627             CPLError( CE_Failure, CPLE_NotSupported,
628                       "FIT - color model RGBA unknown band %i", nBand);
629             return GCI_Undefined;
630         } // switch nBand
631 
632     case 6: // iflHSV - Hue, Saturation, Value
633         if (poFIT_DS->nBands != 3) {
634             CPLError( CE_Failure, CPLE_NotSupported,
635                       "FIT - color model HSV mismatch with %i bands",
636                       poFIT_DS->nBands);
637             return GCI_Undefined;
638         }
639         switch (nBand) {
640         case 1:
641             return GCI_HueBand;
642         case 2:
643             return GCI_SaturationBand;
644         case 3:
645             return GCI_LightnessBand;
646         default:
647             CPLError( CE_Failure, CPLE_NotSupported,
648                       "FIT - color model HSV unknown band %i", nBand);
649             return GCI_Undefined;
650         } // switch nBand
651 
652     case 7: // iflCMY - Cyan, Magenta, Yellow
653         if (poFIT_DS->nBands != 3) {
654             CPLError( CE_Failure, CPLE_NotSupported,
655                       "FIT - color model CMY mismatch with %i bands",
656                       poFIT_DS->nBands);
657             return GCI_Undefined;
658         }
659         switch (nBand) {
660         case 1:
661             return GCI_CyanBand;
662         case 2:
663             return GCI_MagentaBand;
664         case 3:
665             return GCI_YellowBand;
666         default:
667             CPLError( CE_Failure, CPLE_NotSupported,
668                       "FIT - color model CMY unknown band %i", nBand);
669             return GCI_Undefined;
670         } // switch nBand
671 
672     case 8: // iflCMYK - Cyan, Magenta, Yellow, Black
673         if (poFIT_DS->nBands != 4) {
674             CPLError( CE_Failure, CPLE_NotSupported,
675                       "FIT - color model CMYK mismatch with %i bands",
676                       poFIT_DS->nBands);
677             return GCI_Undefined;
678         }
679         switch (nBand) {
680         case 1:
681             return GCI_CyanBand;
682         case 2:
683             return GCI_MagentaBand;
684         case 3:
685             return GCI_YellowBand;
686         case 4:
687             return GCI_BlackBand;
688         default:
689             CPLError( CE_Failure, CPLE_NotSupported,
690                       "FIT - color model CMYK unknown band %i", nBand);
691             return GCI_Undefined;
692         } // switch nBand
693 
694     case 9: // iflBGR - full color (ordered Blue, Green, Red)
695         if (poFIT_DS->nBands != 3) {
696             CPLError( CE_Failure, CPLE_NotSupported,
697                       "FIT - color model BGR mismatch with %i bands",
698                       poFIT_DS->nBands);
699             return GCI_Undefined;
700         }
701         switch (nBand) {
702         case 1:
703             return GCI_BlueBand;
704         case 2:
705             return GCI_GreenBand;
706         case 3:
707             return GCI_RedBand;
708         default:
709             CPLError( CE_Failure, CPLE_NotSupported,
710                       "FIT - color model BGR unknown band %i", nBand);
711             return GCI_Undefined;
712         } // switch nBand
713 
714     case 10: // iflABGR - Alpha, Blue, Green, Red (SGI frame buffers)
715         if (poFIT_DS->nBands != 4) {
716             CPLError( CE_Failure, CPLE_NotSupported,
717                       "FIT - color model ABGR mismatch with %i bands",
718                       poFIT_DS->nBands);
719             return GCI_Undefined;
720         }
721         switch (nBand) {
722         case 1:
723             return GCI_AlphaBand;
724         case 2:
725             return GCI_BlueBand;
726         case 3:
727             return GCI_GreenBand;
728         case 4:
729             return GCI_RedBand;
730         default:
731             CPLError( CE_Failure, CPLE_NotSupported,
732                       "FIT - color model ABGR unknown band %i", nBand);
733             return GCI_Undefined;
734         } // switch nBand
735 
736     case 11: // iflMultiSpectral - multi-spectral data, arbitrary number of
737         // chans
738         return GCI_Undefined;
739 
740     case 12: // iflYCC PhotoCD color model (Luminance, Chrominance)
741         CPLError( CE_Warning, CPLE_NotSupported,
742                   "FIT - color model YCC not supported - ignoring model");
743             return GCI_Undefined;
744 
745     case 13: // iflLuminanceAlpha - Luminance plus alpha
746         if (poFIT_DS->nBands != 2) {
747             CPLError( CE_Failure, CPLE_NotSupported,
748                       "FIT - color model LuminanceAlpha mismatch with "
749                       "%i bands",
750                       poFIT_DS->nBands);
751             return GCI_Undefined;
752         }
753         switch (nBand) {
754         case 1:
755             return GCI_GrayIndex;
756         case 2:
757             return GCI_AlphaBand;
758         default:
759             CPLError( CE_Failure, CPLE_NotSupported,
760                       "FIT - color model LuminanceAlpha unknown band %i",
761                       nBand);
762             return GCI_Undefined;
763         } // switch nBand
764 
765     default:
766         CPLError( CE_Warning, CPLE_NotSupported,
767                   "FIT - unrecognized color model %i - ignoring model",
768                   poFIT_DS->info->cm);
769         return GCI_Undefined;
770     } // switch
771 }
772 
773 /************************************************************************/
774 /*                             FITDataset()                             */
775 /************************************************************************/
776 
FITDataset()777 FITDataset::FITDataset() : fp( NULL ), info( NULL )
778 {
779 
780     adfGeoTransform[0] = 0.0; // x origin (top left corner)
781     adfGeoTransform[1] = 1.0; // x pixel size
782     adfGeoTransform[2] = 0.0;
783 
784     adfGeoTransform[3] = 0.0; // y origin (top left corner)
785     adfGeoTransform[4] = 0.0;
786     adfGeoTransform[5] = 1.0; // y pixel size
787 }
788 
789 /************************************************************************/
790 /*                             ~FITDataset()                             */
791 /************************************************************************/
792 
~FITDataset()793 FITDataset::~FITDataset()
794 {
795     FlushCache();
796     if (info)
797         delete(info);
798     if(fp)
799         VSIFCloseL(fp);
800 }
801 
802 // simple guard object to delete memory
803 // when the guard goes out of scope
804 template< class T >
805 class DeleteGuard
806 {
807 public:
DeleteGuard(T * p)808     DeleteGuard( T *p ) : _ptr( p ) { }
~DeleteGuard()809     ~DeleteGuard()
810     {
811 	    delete _ptr;
812     }
813 
take()814     T *take()
815     {
816         T *tmp = _ptr;
817 	    _ptr = 0;
818 	    return tmp;
819     }
820 
821 private:
822     T *_ptr;
823 	// prevent default copy constructor and assignment operator
824     DeleteGuard( const DeleteGuard & );
825     DeleteGuard &operator=( const DeleteGuard & );
826 };
827 
828 // simple guard object to free memory
829 // when the guard goes out of scope
830 template< class T >
831 class FreeGuard
832 {
833 public:
FreeGuard(T * p)834     FreeGuard( T *p ) : _ptr( p ) { }
~FreeGuard()835     ~FreeGuard()
836     {
837 	    if ( _ptr )
838 			free( _ptr );
839     }
840 
take()841     T *take()
842     {
843         T *tmp = _ptr;
844 	    _ptr = 0;
845 	    return tmp;
846     }
847 
848 private:
849     T *_ptr;
850 	// prevent default copy constructor and assignment operator
851     FreeGuard( const FreeGuard & );
852     FreeGuard &operator=( const FreeGuard & );
853 };
854 
855 /************************************************************************/
856 /*                                Open()                                */
857 /************************************************************************/
858 
Open(GDALOpenInfo * poOpenInfo)859 GDALDataset *FITDataset::Open( GDALOpenInfo * poOpenInfo )
860 {
861 /* -------------------------------------------------------------------- */
862 /*	First we check to see if the file has the expected header	*/
863 /*	bytes.								*/
864 /* -------------------------------------------------------------------- */
865 
866     if( poOpenInfo->nHeaderBytes < 5 )
867         return NULL;
868 
869 
870     if( !EQUALN((const char *) poOpenInfo->pabyHeader, "IT01", 4) &&
871         !EQUALN((const char *) poOpenInfo->pabyHeader, "IT02", 4) )
872         return NULL;
873 
874     if( poOpenInfo->eAccess == GA_Update )
875     {
876         CPLError( CE_Failure, CPLE_NotSupported,
877                   "The FIT driver does not support update access to existing"
878                   " files.\n" );
879         return NULL;
880     }
881 
882 /* -------------------------------------------------------------------- */
883 /*      Create a corresponding GDALDataset.                             */
884 /* -------------------------------------------------------------------- */
885     FITDataset 	*poDS;
886 
887     poDS = new FITDataset();
888     DeleteGuard<FITDataset> guard( poDS );
889 
890 	// re-open file for large file (64bit) access
891     if ( poOpenInfo->eAccess == GA_ReadOnly )
892 	poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "rb" );
893     else
894 	poDS->fp = VSIFOpenL( poOpenInfo->pszFilename, "r+b" );
895 
896     if ( !poDS->fp ) {
897 	    CPLError( CE_Failure, CPLE_OpenFailed,
898 		"Failed to re-open %s with FIT driver.\n",
899 		poOpenInfo->pszFilename );
900 	    return NULL;
901     }
902     poDS->eAccess = poOpenInfo->eAccess;
903 
904 
905     poDS->info = new FITinfo;
906     FITinfo *info = poDS->info;
907 
908 /* -------------------------------------------------------------------- */
909 /*      Read other header values.                                       */
910 /* -------------------------------------------------------------------- */
911     FIThead02 *head = (FIThead02 *) poOpenInfo->pabyHeader;
912 
913     // extract the image attributes from the file header
914     if (EQUALN((const char *) &head->version, "02", 2)) {
915         // incomplete header
916         if( poOpenInfo->nHeaderBytes < (signed) sizeof(FIThead02) )
917             return NULL;
918 
919         CPLDebug("FIT", "Loading file with header version 02");
920 
921         gst_swapb(head->minValue);
922 	info->minValue = head->minValue;
923         gst_swapb(head->maxValue);
924 	info->maxValue = head->maxValue;
925         gst_swapb(head->dataOffset);
926 	info->dataOffset = head->dataOffset;
927 
928         info->userOffset = sizeof(FIThead02);
929     }
930     else if (EQUALN((const char *) &head->version, "01", 2)) {
931         // incomplete header
932         if( poOpenInfo->nHeaderBytes < (signed) sizeof(FIThead01) )
933             return NULL;
934 
935         CPLDebug("FIT", "Loading file with header version 01");
936 
937         // map old style header into new header structure
938 	FIThead01* head01 = (FIThead01*)head;
939         gst_swapb(head->dataOffset);
940 	info->dataOffset = head01->dataOffset;
941 
942         info->userOffset = sizeof(FIThead01);
943     }
944     else {
945         // unrecognized header version
946         CPLError( CE_Failure, CPLE_NotSupported,
947                   "FIT - unsupported header version %.2s\n",
948                   (const char*) &head->version);
949         return NULL;
950     }
951 
952     CPLDebug("FIT", "userOffset %i, dataOffset %i",
953              info->userOffset, info->dataOffset);
954 
955     info->magic = head->magic;
956     info->version = head->version;
957 
958     gst_swapb(head->xSize);
959     info->xSize = head->xSize;
960     gst_swapb(head->ySize);
961     info->ySize = head->ySize;
962     gst_swapb(head->zSize);
963     info->zSize = head->zSize;
964     gst_swapb(head->cSize);
965     info->cSize = head->cSize;
966     gst_swapb(head->dtype);
967     info->dtype = head->dtype;
968     gst_swapb(head->order);
969     info->order = head->order;
970     gst_swapb(head->space);
971     info->space = head->space;
972     gst_swapb(head->cm);
973     info->cm = head->cm;
974     gst_swapb(head->xPageSize);
975     info->xPageSize = head->xPageSize;
976     gst_swapb(head->yPageSize);
977     info->yPageSize = head->yPageSize;
978     gst_swapb(head->zPageSize);
979     info->zPageSize = head->zPageSize;
980     gst_swapb(head->cPageSize);
981     info->cPageSize = head->cPageSize;
982 
983     CPLDebug("FIT", "size %i %i %i %i, pageSize %i %i %i %i",
984              info->xSize, info->ySize, info->zSize, info->cSize,
985              info->xPageSize, info->yPageSize, info->zPageSize,
986              info->cPageSize);
987 
988     CPLDebug("FIT", "dtype %i order %i space %i cm %i",
989              info->dtype, info->order, info->space, info->cm);
990 
991     /**************************/
992 
993     poDS->nRasterXSize = head->xSize;
994     poDS->nRasterYSize = head->ySize;
995     poDS->nBands = head->cSize;
996 
997 /* -------------------------------------------------------------------- */
998 /*      Check if 64 bit seek is needed.                                 */
999 /* -------------------------------------------------------------------- */
1000     uint64 bytesPerComponent =
1001         (GDALGetDataTypeSize(fitDataType(poDS->info->dtype)) / 8);
1002     uint64 bytesPerPixel = head->cSize * bytesPerComponent;
1003     uint64 recordSize = bytesPerPixel * head->xPageSize *
1004         head->yPageSize;
1005     uint64 numXBlocks =
1006         (uint64) ceil((double) head->xSize / head->xPageSize);
1007     uint64 numYBlocks =
1008         (uint64) ceil((double) head->ySize / head->yPageSize);
1009 
1010     uint64 maxseek = recordSize * numXBlocks * numYBlocks;
1011 
1012 //     CPLDebug("FIT", "(sizeof %i) max seek %llx ==> %llx\n", sizeof(uint64),
1013 //              maxseek, maxseek >> 31);
1014     if (maxseek >> 31) // signed long
1015 #ifdef VSI_LARGE_API_SUPPORTED
1016         CPLDebug("FIT", "Using 64 bit version of fseek");
1017 #else
1018         CPLError(CE_Fatal, CPLE_NotSupported,
1019                  "FIT - need 64 bit version of fseek");
1020 #endif
1021 
1022 /* -------------------------------------------------------------------- */
1023 /*      Verify all "unused" header values.                              */
1024 /* -------------------------------------------------------------------- */
1025 
1026     if( info->zSize != 1 )
1027     {
1028         CPLError( CE_Failure, CPLE_NotSupported,
1029                   "FIT driver - unsupported zSize %i\n", info->zSize);
1030         return NULL;
1031     }
1032 
1033     if( info->order != 1 ) // interleaved - RGBRGB
1034     {
1035         CPLError( CE_Failure, CPLE_NotSupported,
1036                   "FIT driver - unsupported order %i\n", info->order);
1037         return NULL;
1038     }
1039 
1040     if( info->zPageSize != 1 )
1041     {
1042         CPLError( CE_Failure, CPLE_NotSupported,
1043                   "FIT driver - unsupported zPageSize %i\n", info->zPageSize);
1044         return NULL;
1045     }
1046 
1047     if( info->cPageSize != info->cSize )
1048     {
1049         CPLError( CE_Failure, CPLE_NotSupported,
1050                   "FIT driver - unsupported cPageSize %i (!= %i)\n",
1051                   info->cPageSize, info->cSize);
1052         return NULL;
1053     }
1054 
1055 /* -------------------------------------------------------------------- */
1056 /*      Create band information objects.                                */
1057 /* -------------------------------------------------------------------- */
1058     for( int i = 0; i < poDS->nBands; i++ )
1059     {
1060         poDS->SetBand( i+1,  new FITRasterBand( poDS, i+1 ) ) ;
1061     }
1062 
1063 /* -------------------------------------------------------------------- */
1064 /*      Initialize any PAM information.                                 */
1065 /* -------------------------------------------------------------------- */
1066     poDS->SetDescription( poOpenInfo->pszFilename );
1067     poDS->TryLoadXML();
1068 
1069 /* -------------------------------------------------------------------- */
1070 /*      Check for external overviews.                                   */
1071 /* -------------------------------------------------------------------- */
1072     poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename, poOpenInfo->GetSiblingFiles() );
1073 
1074     return guard.take();
1075 }
1076 
1077 /************************************************************************/
1078 /*                           FITCreateCopy()                            */
1079 /************************************************************************/
1080 
1081 #ifdef FIT_WRITE
FITCreateCopy(const char * pszFilename,GDALDataset * poSrcDS,int bStrict,char ** papszOptions,GDALProgressFunc pfnProgress,void * pProgressData)1082 static GDALDataset *FITCreateCopy(const char * pszFilename,
1083                                   GDALDataset *poSrcDS,
1084                                   int bStrict, char ** papszOptions,
1085                                   GDALProgressFunc pfnProgress,
1086                                   void * pProgressData )
1087 {
1088     CPLDebug("FIT", "CreateCopy %s - %i", pszFilename, bStrict);
1089 
1090     int nBands = poSrcDS->GetRasterCount();
1091     if (nBands == 0)
1092     {
1093         CPLError( CE_Failure, CPLE_NotSupported,
1094                   "FIT driver does not support source dataset with zero band.\n");
1095         return NULL;
1096     }
1097 
1098 /* -------------------------------------------------------------------- */
1099 /*      Create the dataset.                                             */
1100 /* -------------------------------------------------------------------- */
1101     VSILFILE	*fpImage;
1102 
1103     if( !pfnProgress( 0.0, NULL, pProgressData ) )
1104     {
1105         CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" );
1106         return NULL;
1107     }
1108 
1109     fpImage = VSIFOpenL( pszFilename, "wb" );
1110     if( fpImage == NULL )
1111     {
1112         CPLError( CE_Failure, CPLE_OpenFailed,
1113                   "FIT - unable to create file %s.\n",
1114                   pszFilename );
1115         return NULL;
1116     }
1117 
1118 /* -------------------------------------------------------------------- */
1119 /*      Generate header.                                                */
1120 /* -------------------------------------------------------------------- */
1121     // XXX - should FIT_PAGE_SIZE be based on file page size ??
1122     int size = MAX(sizeof(FIThead02), FIT_PAGE_SIZE);
1123     FIThead02 *head = (FIThead02 *) malloc(size);
1124     FreeGuard<FIThead02> guardHead( head );
1125 
1126     // clean header so padding (past real header) is all zeros
1127     memset( head, 0, size );
1128 
1129     strncpy((char *) &head->magic, "IT", 2);
1130     strncpy((char *) &head->version, "02", 2);
1131 
1132     head->xSize = poSrcDS->GetRasterXSize();
1133     gst_swapb(head->xSize);
1134     head->ySize = poSrcDS->GetRasterYSize();
1135     gst_swapb(head->ySize);
1136     head->zSize = 1;
1137     gst_swapb(head->zSize);
1138 
1139     head->cSize = nBands;
1140     gst_swapb(head->cSize);
1141 
1142     GDALRasterBand *firstBand = poSrcDS->GetRasterBand(1);
1143     if (! firstBand) {
1144         VSIFCloseL(fpImage);
1145         return NULL;
1146     }
1147 
1148     head->dtype = fitGetDataType(firstBand->GetRasterDataType());
1149     if (! head->dtype) {
1150         VSIFCloseL(fpImage);
1151         return NULL;
1152     }
1153     gst_swapb(head->dtype);
1154     head->order = 1; // interleaved - RGBRGB
1155     gst_swapb(head->order);
1156     head->space = 1; // upper left
1157     gst_swapb(head->space);
1158 
1159     // XXX - need to check all bands
1160     head->cm = fitGetColorModel(firstBand->GetColorInterpretation(), nBands);
1161     gst_swapb(head->cm);
1162 
1163     int blockX, blockY;
1164     firstBand->GetBlockSize(&blockX, &blockY);
1165     CPLDebug("FIT write", "inherited block size %ix%i", blockX, blockY);
1166 
1167     if( CSLFetchNameValue(papszOptions,"PAGESIZE") != NULL )
1168     {
1169         const char *str = CSLFetchNameValue(papszOptions,"PAGESIZE");
1170         int newBlockX, newBlockY;
1171         sscanf(str, "%i,%i", &newBlockX, &newBlockY);
1172         if (newBlockX && newBlockY) {
1173             blockX = newBlockX;
1174             blockY = newBlockY;
1175         }
1176         else {
1177             CPLError(CE_Failure, CPLE_OpenFailed,
1178                      "FIT - Unable to parse option PAGESIZE values [%s]", str);
1179         }
1180     }
1181 
1182     // XXX - need to do lots of checking of block size
1183     // * provide ability to override block size with options
1184     // * handle non-square block size (like scanline)
1185     //   - probably default from non-tiled image - have default block size
1186     // * handle block size bigger than image size
1187     // * undesirable block size (non power of 2, others?)
1188     // * mismatched block sizes for different bands
1189     // * image that isn't even pages (ie. partially empty pages at edge)
1190     CPLDebug("FIT write", "using block size %ix%i", blockX, blockY);
1191 
1192     head->xPageSize = blockX;
1193     gst_swapb(head->xPageSize);
1194     head->yPageSize = blockY;
1195     gst_swapb(head->yPageSize);
1196     head->zPageSize = 1;
1197     gst_swapb(head->zPageSize);
1198     head->cPageSize = nBands;
1199     gst_swapb(head->cPageSize);
1200 
1201     // XXX - need to check all bands
1202     head->minValue = firstBand->GetMinimum();
1203     gst_swapb(head->minValue);
1204     // XXX - need to check all bands
1205     head->maxValue = firstBand->GetMaximum();
1206     gst_swapb(head->maxValue);
1207     head->dataOffset = size;
1208     gst_swapb(head->dataOffset);
1209 
1210     VSIFWriteL(head, size, 1, fpImage);
1211 
1212 /* -------------------------------------------------------------------- */
1213 /*      Loop over image, copying image data.                            */
1214 /* -------------------------------------------------------------------- */
1215     unsigned long bytesPerComponent =
1216         (GDALGetDataTypeSize(firstBand->GetRasterDataType()) / 8);
1217     unsigned long bytesPerPixel = nBands * bytesPerComponent;
1218 
1219     unsigned long pageBytes = blockX * blockY * bytesPerPixel;
1220     char *output = (char *) malloc(pageBytes);
1221     if (! output)
1222         CPLError(CE_Fatal, CPLE_NotSupported,
1223                  "FITRasterBand couldn't allocate %lu bytes", pageBytes);
1224     FreeGuard<char> guardOutput( output );
1225 
1226     long maxx = (long) ceil(poSrcDS->GetRasterXSize() / (double) blockX);
1227     long maxy = (long) ceil(poSrcDS->GetRasterYSize() / (double) blockY);
1228     long maxx_full = (long) floor(poSrcDS->GetRasterXSize() / (double) blockX);
1229     long maxy_full = (long) floor(poSrcDS->GetRasterYSize() / (double) blockY);
1230 
1231     CPLDebug("FIT", "about to write %ld x %ld blocks", maxx, maxy);
1232 
1233     for(long y=0; y < maxy; y++)
1234         for(long x=0; x < maxx; x++) {
1235             long readX = blockX;
1236             long readY = blockY;
1237             int do_clean = FALSE;
1238 
1239             // handle cases where image size isn't an exact multiple
1240             // of page size
1241             if (x >= maxx_full) {
1242                 readX = poSrcDS->GetRasterXSize() % blockX;
1243                 do_clean = TRUE;
1244             }
1245             if (y >= maxy_full) {
1246                 readY = poSrcDS->GetRasterYSize() % blockY;
1247                 do_clean = TRUE;
1248             }
1249 
1250             // clean out image if only doing partial reads
1251             if (do_clean)
1252                 memset( output, 0, pageBytes );
1253 
1254             for( int iBand = 0; iBand < nBands; iBand++ ) {
1255                 GDALRasterBand * poBand = poSrcDS->GetRasterBand( iBand+1 );
1256                 CPLErr eErr =
1257                     poBand->RasterIO( GF_Read, // eRWFlag
1258                                       x * blockX, // nXOff
1259                                       y * blockY, // nYOff
1260                                       readX, // nXSize
1261                                       readY, // nYSize
1262                                       output + iBand * bytesPerComponent,
1263                                       // pData
1264                                       blockX, // nBufXSize
1265                                       blockY, // nBufYSize
1266                                       firstBand->GetRasterDataType(),
1267                                       // eBufType
1268                                       bytesPerPixel, // nPixelSpace
1269                                       bytesPerPixel * blockX, NULL); // nLineSpace
1270                 if (eErr != CE_None)
1271                     CPLError(CE_Failure, CPLE_FileIO,
1272                              "FIT write - CreateCopy got read error %i", eErr);
1273             } // for iBand
1274 
1275 #ifdef swapping
1276             char *p = output;
1277             unsigned long i;
1278             switch(bytesPerComponent) {
1279             case 1:
1280                 // do nothing
1281                 break;
1282             case 2:
1283                 for(i=0; i < pageBytes; i+= bytesPerComponent)
1284                     gst_swap16(p + i);
1285                 break;
1286             case 4:
1287                 for(i=0; i < pageBytes; i+= bytesPerComponent)
1288                     gst_swap32(p + i);
1289                 break;
1290             case 8:
1291                 for(i=0; i < pageBytes; i+= bytesPerComponent)
1292                     gst_swap64(p + i);
1293                 break;
1294             default:
1295                 CPLError(CE_Failure, CPLE_NotSupported,
1296                          "FIT write - unsupported bytesPerPixel %lu",
1297                          bytesPerComponent);
1298             } // switch
1299 #endif // swapping
1300 
1301             VSIFWriteL(output, pageBytes, 1, fpImage);
1302 
1303             double perc = ((double) (y * maxx + x)) / (maxx * maxy);
1304 //             printf("progress %f\n", perc);
1305             if( !pfnProgress( perc, NULL, pProgressData ) )
1306             {
1307                 CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" );
1308                 //free(output);
1309                 VSIFCloseL( fpImage );
1310                 VSIUnlink( pszFilename );
1311                 return NULL;
1312             }
1313         } // for x
1314 
1315     //free(output);
1316 
1317     VSIFCloseL( fpImage );
1318 
1319     pfnProgress( 1.0, NULL, pProgressData );
1320 
1321 /* -------------------------------------------------------------------- */
1322 /*      Re-open dataset, and copy any auxiliary pam information.         */
1323 /* -------------------------------------------------------------------- */
1324     GDALPamDataset *poDS = (GDALPamDataset *)
1325         GDALOpen( pszFilename, GA_ReadOnly );
1326 
1327     if( poDS )
1328         poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT );
1329 
1330     return poDS;
1331 }
1332 #endif // FIT_WRITE
1333 
1334 /************************************************************************/
1335 /*                           GetGeoTransform()                          */
1336 /************************************************************************/
1337 
1338 // CPLErr FITDataset::GetGeoTransform( double * padfTransform )
1339 // {
1340 //     CPLDebug("FIT", "FITDataset::GetGeoTransform");
1341 //     memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 );
1342 //     return( CE_None );
1343 // }
1344 
1345 /************************************************************************/
1346 /*                          GDALRegister_FIT()                          */
1347 /************************************************************************/
1348 
GDALRegister_FIT()1349 void GDALRegister_FIT()
1350 
1351 {
1352     GDALDriver	*poDriver;
1353 
1354     if( GDALGetDriverByName( "FIT" ) == NULL )
1355     {
1356         poDriver = new GDALDriver();
1357 
1358         poDriver->SetDescription( "FIT" );
1359         poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" );
1360         poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
1361                                    "FIT Image" );
1362         poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
1363                                    "frmt_various.html#" );
1364         poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "" );
1365         poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
1366 
1367         poDriver->pfnOpen = FITDataset::Open;
1368 #ifdef FIT_WRITE
1369         poDriver->pfnCreateCopy = FITCreateCopy;
1370         poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES,
1371                                    "Byte UInt16 Int16 UInt32 Int32 Float32 Float64" );
1372 #endif // FIT_WRITE
1373 
1374         GetGDALDriverManager()->RegisterDriver( poDriver );
1375     }
1376 }
1377