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