1 /*
2 * Copyright (c) 2002-2012, California Institute of Technology.
3 * All rights reserved.  Based on Government Sponsored Research under contracts NAS7-1407 and/or NAS7-03001.
4 *
5 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
6 *   1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
7 *   2. Redistributions in binary form must reproduce the above copyright notice,
8 *      this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
9 *   3. Neither the name of the California Institute of Technology (Caltech), its operating division the Jet Propulsion Laboratory (JPL),
10 *      the National Aeronautics and Space Administration (NASA), nor the names of its contributors may be used to
11 *      endorse or promote products derived from this software without specific prior written permission.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
14 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
15 * IN NO EVENT SHALL THE CALIFORNIA INSTITUTE OF TECHNOLOGY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
16 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
17 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
18 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
19 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
20 *
21 * Copyright 2014-2021 Esri
22 *
23 * Licensed under the Apache License, Version 2.0 (the "License");
24 * you may not use this file except in compliance with the License.
25 * You may obtain a copy of the License at
26 *
27 * http://www.apache.org/licenses/LICENSE-2.0
28 *
29 * Unless required by applicable law or agreed to in writing, software
30 * distributed under the License is distributed on an "AS IS" BASIS,
31 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
32 * See the License for the specific language governing permissions and
33 * limitations under the License.
34 *
35 *  Functions used by the driver, should have prototypes in the header file
36 *
37 *  Author: Lucian Plesea
38 */
39 
40 #include "marfa.h"
41 #include <zlib.h>
42 #include <algorithm>
43 #include <limits>
44 
45 // LERC is not ready for big endian hosts for now
46 #if defined(LERC) && defined(WORDS_BIGENDIAN)
47 #undef LERC
48 #endif
49 
50 CPL_C_START
51 void GDALRegister_mrf(void);
52 CPL_C_END
53 
54 NAMESPACE_MRF_START
55 
56 // These have to be positionally in sync with the enums in marfa.h
57 static const char * const ILC_N[] = { "PNG", "PPNG", "JPEG", "JPNG", "NONE", "DEFLATE", "TIF",
58 #if defined(LERC)
59         "LERC",
60 #endif
61         "Unknown" };
62 
63 static const char * const ILC_E[]={ ".ppg", ".ppg", ".pjg", ".pjp", ".til", ".pzp", ".ptf",
64 #if defined(LERC)
65         ".lrc" ,
66 #endif
67         "" };
68 
69 static const char * const ILO_N[]={ "PIXEL", "BAND", "LINE", "Unknown" };
70 
71 char const * const * ILComp_Name=ILC_N;
72 char const * const * ILComp_Ext=ILC_E;
73 char const * const * ILOrder_Name=ILO_N;
74 
75 /**
76  *  Get the string for a compression type
77  */
CompName(ILCompression comp)78 const char *CompName(ILCompression comp) {
79     if (comp>=IL_ERR_COMP) return ILComp_Name[IL_ERR_COMP];
80     return ILComp_Name[comp];
81 }
82 
83 /**
84  *  Get the string for an order type
85  */
OrderName(ILOrder val)86 const char *OrderName(ILOrder val) {
87     if (val>=IL_ERR_ORD) return ILOrder_Name[IL_ERR_ORD];
88     return ILOrder_Name[val];
89 }
90 
CompToken(const char * opt,ILCompression def)91 ILCompression CompToken(const char *opt, ILCompression def) {
92     int i;
93     if (nullptr==opt) return def;
94     for (i=0; ILCompression(i) < IL_ERR_COMP; i++)
95         if (EQUAL(opt,ILComp_Name[i]))
96             break;
97     if (IL_ERR_COMP == ILCompression(i))
98         return def;
99     return ILCompression(i);
100 }
101 
102 /**
103  *  Find a compression token
104  */
OrderToken(const char * opt,ILOrder def)105 ILOrder OrderToken(const char *opt, ILOrder def) {
106     int i;
107     if (nullptr==opt) return def;
108     for (i=0; ILOrder(i)<IL_ERR_ORD; i++)
109         if (EQUAL(opt,ILOrder_Name[i]))
110             break;
111     if (IL_ERR_ORD==ILOrder(i))
112         return def;
113     return ILOrder(i);
114 }
115 
116 //
117 //  Inserters for ILSize and ILIdx types
118 //
operator <<(std::ostream & out,const ILSize & sz)119 std::ostream& operator<<(std::ostream &out, const ILSize& sz) {
120     out << "X=" << sz.x << ",Y=" << sz.y << ",Z=" << sz.z
121         << ",C=" << sz.c << ",L=" << sz.l;
122     return out;
123 }
124 
operator <<(std::ostream & out,const ILIdx & t)125 std::ostream& operator<<(std::ostream &out, const ILIdx& t) {
126     out << "offset=" << t.offset << ",size=" << t.size;
127     return out;
128 }
129 
130 // Define PPMW to enable this handy debug function
131 #ifdef PPMW
ppmWrite(const char * fname,const char * data,const ILSize & sz)132 void ppmWrite(const char *fname, const char *data, const ILSize &sz) {
133     FILE *fp = fopen(fname, "wb");
134     switch (sz.c) {
135     case 4: // Strip the alpha
136         fprintf(fp, "P6 %d %d 255\n", sz.x, sz.y);
137         {
138             char *d = (char *)data;
139             for (int i = sz.x*sz.y; i; i--) {
140                 fwrite(d, 3, 1, fp);
141                 d += 4;
142             }
143         }
144         break;
145     case 3:
146         fprintf(fp, "P6 %d %d 255\n", sz.x, sz.y);
147         fwrite(data, sz.x*sz.y, 3, fp);
148         break;
149     case 1:
150         fprintf(fp, "P5 %d %d 255\n", sz.x, sz.y);
151         fwrite(data, sz.x, sz.y, fp);
152         break;
153     default:
154         fprintf(stderr, "Can't write ppm file with %d bands\n", sz.c);/*ok*/
155     }
156     fclose(fp);
157     return;
158 }
159 #endif
160 
161 // Returns the size of the index for image and overlays
162 // If scale is zero, only base image
IdxSize(const ILImage & full,const int scale)163 GIntBig IdxSize(const ILImage &full, const int scale) {
164     ILImage img = full;
165     img.pagecount = pcount(img.size, img.pagesize);
166     GIntBig sz = img.pagecount.l;
167     while (scale != 0 && 1 != img.pagecount.x * img.pagecount.y) {
168         img.size.x = pcount(img.size.x, scale);
169         img.size.y = pcount(img.size.y, scale);
170         img.pagecount = pcount(img.size, img.pagesize);
171         sz += img.pagecount.l;
172     }
173 
174     if (sz > std::numeric_limits<GIntBig>::max() / static_cast<int>(sizeof(ILIdx))) {
175         CPLError(CE_Failure, CPLE_AppDefined, "IdxSize: integer overflow");
176         return 0;
177     }
178     return sz*sizeof(ILIdx);
179 }
180 
ILImage()181 ILImage::ILImage() :
182     dataoffset(0),
183     idxoffset(0),
184     quality(85),
185     pageSizeBytes(0),
186     size(ILSize(1, 1, 1, 1, 0)),
187     pagesize(ILSize(384, 384, 1, 1, 0)),
188     pagecount(pcount(size, pagesize)),
189     comp(IL_PNG),
190     order(IL_Interleaved),
191     nbo(false),
192     hasNoData(FALSE),
193     NoDataValue(0.0),
194     dt(GDT_Unknown),
195     ci(GCI_Undefined)
196 {}
197 
198 /**
199  *\brief Get a file name by replacing the extension.
200  * pass the data file name and the default extension starting with .
201  * If name length is not sufficient, it returns the extension
202  * If the input name is curl with parameters, the base file extension gets changed and
203  * parameters are preserved.
204  */
205 
getFname(const CPLString & in,const char * ext)206 CPLString getFname(const CPLString &in, const char *ext) {
207     if (strlen(in) < strlen(ext))
208         return CPLString(ext);
209 
210     CPLString ret(in);
211     // Is it a web file with parameters?
212     size_t extlen = strlen(ext);
213     size_t qmark = ret.find_first_of('?');
214     if (!(qmark != std::string::npos && 0 == in.find("/vsicurl/http") && qmark >= extlen))
215         qmark = ret.size();
216     return ret.replace(qmark - extlen, extlen, ext);
217 }
218 
219 /**
220  *\brief Get a file name, either from the configuration or from the default file name
221  * If the token is not defined by CPLGetXMLValue, if the extension of the in name is .xml,
222  * it returns the token with the extension changed to defext.
223  * Otherwise it returns the token itself
224  * It is pretty hard to separate local vs remote due to the gdal file name ornaments
225  * Absolute file names start with: ?:/ or /
226  *
227  */
228 
getFname(CPLXMLNode * node,const char * token,const CPLString & in,const char * def)229 CPLString getFname(CPLXMLNode *node, const char *token, const CPLString &in, const char *def) {
230     CPLString fn = CPLGetXMLValue(node, token, "");
231     if (fn.empty()) // Not provided
232         return getFname(in, def);
233     size_t slashPos = fn.find_first_of("\\/");
234 
235     // Does it look like an absolute path or we won't find the basename of 'in'
236     if (slashPos == 0                               // Starts with slash
237         || (slashPos == 2 && fn[1] == ':')          // Starts with disk letter column
238         // Does not start with dots then slash
239         || (slashPos != fn.npos && slashPos != fn.find_first_not_of('.'))
240         || EQUALN(in,"<MRF_META>", 10)              // XML string input
241         || in.find_first_of("\\/") == in.npos)      // We can't get a basename from 'in'
242         return fn;
243 
244     // Relative path, prepend the path from the in file name
245     return in.substr(0, in.find_last_of("\\/")+1) + fn;
246 }
247 
248 /**
249  *\brief Extracts a numerical value from a XML node
250  * It works like CPLGetXMLValue except for the default value being
251  * a number instead of a string
252  */
253 
getXMLNum(CPLXMLNode * node,const char * pszPath,double def)254 double getXMLNum(CPLXMLNode *node, const char *pszPath, double def) {
255     const char *textval=CPLGetXMLValue(node,pszPath,nullptr);
256     if (textval) return atof(textval);
257     return def;
258 }
259 
260 //
261 // Calculate offset of index, pos is in pages
262 //
263 
IdxOffset(const ILSize & pos,const ILImage & img)264 GIntBig IdxOffset(const ILSize &pos, const ILImage &img) {
265     return img.idxoffset + sizeof(ILIdx) * (pos.c + img.pagecount.c * (
266             pos.x + img.pagecount.x * (pos.y + img.pagecount.y * static_cast<GIntBig>(pos.z))));
267 }
268 
269 // Is compression type endianness dependent?
is_Endianess_Dependent(GDALDataType dt,ILCompression comp)270 bool is_Endianess_Dependent(GDALDataType dt, ILCompression comp) {
271     // Add here all endianness dependent compressions
272     if (IL_ZLIB == comp || IL_NONE == comp)
273         if (GDALGetDataTypeSize( dt ) > 8)
274             return true;
275     return false;
276 }
277 
newMRFRasterBand(MRFDataset * pDS,const ILImage & image,int b,int level)278 MRFRasterBand *newMRFRasterBand(MRFDataset *pDS, const ILImage &image, int b, int level)
279 {
280     MRFRasterBand *bnd = nullptr;
281     CPLErrorReset();
282     switch (pDS->current.comp) {
283     case IL_PPNG: // Uses the PNG code, just has a palette in each PNG
284     case IL_PNG:  bnd = new PNG_Band(pDS, image, b, level);  break;
285     case IL_JPEG: bnd = new JPEG_Band(pDS, image, b, level); break;
286     case IL_JPNG: bnd = new JPNG_Band(pDS, image, b, level); break;
287     case IL_NONE: bnd = new Raw_Band(pDS, image, b, level);  break;
288 #if defined(LERC)
289     case IL_LERC: bnd = new LERC_Band(pDS, image, b, level); break;
290 #endif
291         // ZLIB is just raw + deflate
292     case IL_ZLIB:
293         bnd = new Raw_Band(pDS, image, b, level);
294         bnd->SetDeflate(1);
295         break;
296     case IL_TIF:
297         if (image.pageSizeBytes > INT_MAX - 1024)
298             return nullptr;
299         bnd = new TIF_Band(pDS, image, b, level);
300         break;
301     default:
302         return nullptr;
303     }
304 
305     // If something was flagged during band creation
306     if (CPLGetLastErrorNo() != CE_None) {
307         delete bnd;
308         return nullptr;
309     }
310 
311     // Copy the RW mode from the dataset
312     bnd->SetAccess(pDS->eAccess);
313     return bnd;
314 }
315 
316 /**
317  *\brief log in a given base
318  */
logbase(double val,double base)319 double logbase(double val, double base) {
320     return log(val) / log(base);
321 }
322 
323 /**
324  *\brief Is logbase(val, base) an integer?
325  *
326  */
IsPower(double value,double base)327 int IsPower(double value, double base) {
328     double v = logbase(value, base);
329     return CPLIsEqual(v, int(v + 0.5));
330 }
331 
332 /************************************************************************/
333 /*                           SearchXMLSiblings()                        */
334 /************************************************************************/
335 
336 /**
337  *\brief Search for a sibling of the root node with a given name.
338  *
339  * Searches only the next siblings of the node passed in for the named element or attribute.
340  * If the first character of the pszElement is '=', the search includes the psRoot node
341  *
342  * @param psRoot the root node to search.  This should be a node of type
343  * CXT_Element.  NULL is safe.
344  *
345  * @param pszElement the name of the element or attribute to search for.
346  *
347  * @return The first matching node or NULL on failure.
348  */
349 
SearchXMLSiblings(CPLXMLNode * psRoot,const char * pszElement)350 CPLXMLNode *SearchXMLSiblings( CPLXMLNode *psRoot, const char *pszElement ) {
351     if( psRoot == nullptr || pszElement == nullptr )
352         return nullptr;
353 
354     // If the strings starts with '=', skip it and test the root
355     // If not, start testing with the next sibling
356     if (pszElement[0]=='=')
357         pszElement++;
358     else
359         psRoot=psRoot->psNext;
360 
361     for (;psRoot!=nullptr;psRoot=psRoot->psNext)
362         if ((psRoot->eType == CXT_Element || psRoot->eType == CXT_Attribute) && EQUAL(pszElement,psRoot->pszValue))
363             return psRoot;
364     return nullptr;
365 }
366 
367 //
368 // Extension to CSL, set an entry if it doesn't already exist
369 //
CSLAddIfMissing(char ** papszList,const char * pszName,const char * pszValue)370 char **CSLAddIfMissing(char **papszList, const char *pszName, const char *pszValue) {
371     if (CSLFetchNameValue(papszList, pszName))
372         return papszList;
373     return CSLSetNameValue(papszList, pszName, pszValue);
374 }
375 
376 //
377 // Print a double so it can be read with strod while preserving precision
378 // Unfortunately this is not quite possible or portable enough at this time
379 //
PrintDouble(double d,const char * frmt)380 CPLString PrintDouble(double d, const char *frmt) {
381     CPLString res;
382     res.FormatC(d, nullptr);
383     if (CPLStrtod(res.c_str(), nullptr) == d)
384         return res;
385 
386     //  This would be the right code with a C99 compiler that supports %a readback in strod()
387     //    return CPLString().Printf("%a",d);
388 
389     return CPLString().FormatC(d, frmt);
390 }
391 
XMLSetAttributeVal(CPLXMLNode * parent,const char * pszName,const char * pszVal)392 void XMLSetAttributeVal(CPLXMLNode *parent, const char* pszName, const char *pszVal) {
393     CPLCreateXMLNode(parent, CXT_Attribute, pszName);
394     CPLSetXMLValue(parent, pszName, pszVal);
395 }
396 
XMLSetAttributeVal(CPLXMLNode * parent,const char * pszName,const double val,const char * frmt)397 void XMLSetAttributeVal(CPLXMLNode *parent, const char* pszName, const double val, const char *frmt) {
398     XMLSetAttributeVal(parent, pszName, PrintDouble(val, frmt));
399 }
400 
XMLSetAttributeVal(CPLXMLNode * parent,const char * pszName,const ILSize & sz,const char * frmt)401 CPLXMLNode *XMLSetAttributeVal(CPLXMLNode *parent, const char*pszName, const ILSize &sz, const char *frmt) {
402     CPLXMLNode *node = CPLCreateXMLNode(parent, CXT_Element, pszName);
403     XMLSetAttributeVal(node, "x", sz.x, frmt);
404     XMLSetAttributeVal(node, "y", sz.y, frmt);
405     if (sz.z != 1)
406         XMLSetAttributeVal(node, "z", sz.z, frmt);
407     XMLSetAttributeVal(node, "c", sz.c, frmt);
408     return node;
409 }
410 
411 //
412 // Prints a vector of doubles into a string and sets that string as the value of an XML attribute
413 // If all values are the same, it only prints one
414 //
XMLSetAttributeVal(CPLXMLNode * parent,const char * pszName,std::vector<double> const & values)415 void XMLSetAttributeVal(CPLXMLNode *parent, const char*pszName, std::vector<double> const &values) {
416     if (values.empty())
417         return;
418 
419     CPLString value;
420     double val = values[0];
421     int single_val = true;
422     for (int i = 0; i < int(values.size()); i++) {
423         if (val != values[i])
424             single_val = false;
425         value.append(PrintDouble(values[i]) + " ");
426         value.resize(value.size() - 1); // Cut the last space
427     }
428     if (single_val)
429         value = PrintDouble(values[0]);
430     CPLCreateXMLNode(parent, CXT_Attribute, pszName);
431     CPLSetXMLValue(parent, pszName, value);
432 }
433 
434 /**
435  *\brief Read a ColorEntry XML node, return a GDALColorEntry structure
436  *
437  */
438 
GetXMLColorEntry(CPLXMLNode * p)439 GDALColorEntry GetXMLColorEntry(CPLXMLNode *p) {
440     GDALColorEntry ce;
441     ce.c1 = static_cast<short>(getXMLNum(p, "c1", 0));
442     ce.c2 = static_cast<short>(getXMLNum(p, "c2", 0));
443     ce.c3 = static_cast<short>(getXMLNum(p, "c3", 0));
444     ce.c4 = static_cast<short>(getXMLNum(p, "c4", 255));
445     return ce;
446 }
447 
448 /**
449  *\brief Verify or make a file that big
450  *
451  * @return true if size is OK or if extend succeeded
452  */
453 
CheckFileSize(const char * fname,GIntBig sz,GDALAccess eAccess)454 int CheckFileSize(const char *fname, GIntBig sz, GDALAccess eAccess) {
455 
456     VSIStatBufL statb;
457     if (VSIStatL(fname, &statb))
458         return false;
459     if (statb.st_size >= sz)
460         return true;
461 
462     // Don't change anything unless updating
463     if (eAccess != GA_Update)
464         return false;
465 
466     // There is no ftruncate in VSI, only truncate()
467     VSILFILE *ifp = VSIFOpenL(fname, "r+b");
468     if( ifp == nullptr )
469         return false;
470 
471     int ret = VSIFTruncateL(ifp, sz);
472     VSIFCloseL(ifp);
473     return !ret;
474 }
475 
476 // Similar to compress2() but with flags to control zlib features
477 // Returns true if it worked
ZPack(const buf_mgr & src,buf_mgr & dst,int flags)478 int ZPack(const buf_mgr &src, buf_mgr &dst, int flags) {
479     z_stream stream;
480     int err;
481 
482     memset(&stream, 0, sizeof(stream));
483     stream.next_in = (Bytef*)src.buffer;
484     stream.avail_in = (uInt)src.size;
485     stream.next_out = (Bytef*)dst.buffer;
486     stream.avail_out = (uInt)dst.size;
487 
488     int level = std::min(9, flags & ZFLAG_LMASK);
489     int wb = MAX_WBITS;
490     // if gz flag is set, ignore raw request
491     if (flags & ZFLAG_GZ) wb += 16;
492     else if (flags & ZFLAG_RAW) wb = -wb;
493     int memlevel = 8; // Good compromise
494     int strategy = (flags & ZFLAG_SMASK) >> 6;
495     if (strategy > 4) strategy = 0;
496 
497     err = deflateInit2(&stream, level, Z_DEFLATED, wb, memlevel, strategy);
498     if (err != Z_OK) return err;
499 
500     err = deflate(&stream, Z_FINISH);
501     if (err != Z_STREAM_END) {
502         deflateEnd(&stream);
503         return false;
504     }
505     dst.size = stream.total_out;
506     err = deflateEnd(&stream);
507     return err == Z_OK;
508 }
509 
510 // Similar to uncompress() from zlib, accepts the ZFLAG_RAW
511 // Return true if it worked
ZUnPack(const buf_mgr & src,buf_mgr & dst,int flags)512 int ZUnPack(const buf_mgr &src, buf_mgr &dst, int flags) {
513 
514     z_stream stream;
515     int err;
516 
517     memset(&stream, 0, sizeof(stream));
518     stream.next_in = (Bytef*)src.buffer;
519     stream.avail_in = (uInt)src.size;
520     stream.next_out = (Bytef*)dst.buffer;
521     stream.avail_out = (uInt)dst.size;
522 
523     // 32 means autodetec gzip or zlib header, negative 15 is for raw
524     int wb = (ZFLAG_RAW & flags) ? -MAX_WBITS: 32 + MAX_WBITS;
525     err = inflateInit2(&stream, wb);
526     if (err != Z_OK) return false;
527 
528     err = inflate(&stream, Z_FINISH);
529     if (err != Z_STREAM_END) {
530         inflateEnd(&stream);
531         return false;
532     }
533     dst.size = stream.total_out;
534     err = inflateEnd(&stream);
535     return err == Z_OK;
536 }
537 
538 NAMESPACE_MRF_END
539 
540 /************************************************************************/
541 /*                          GDALRegister_mrf()                          */
542 /************************************************************************/
543 
544 USING_NAMESPACE_MRF
545 
GDALRegister_mrf()546 void GDALRegister_mrf() {
547     if( GDALGetDriverByName("MRF") != nullptr )
548         return;
549 
550     GDALDriver *driver = new GDALDriver();
551     driver->SetDescription("MRF");
552     driver->SetMetadataItem(GDAL_DMD_LONGNAME, "Meta Raster Format");
553     driver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/marfa.html");
554     driver->SetMetadataItem(GDAL_DMD_EXTENSION, "mrf");
555     driver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
556     driver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
557 
558     // These will need to be revisited, do we support complex data types too?
559     driver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
560                             "Byte UInt16 Int16 Int32 UInt32 Float32 Float64");
561 
562     driver->SetMetadataItem(
563         GDAL_DMD_CREATIONOPTIONLIST,
564         "<CreationOptionList>"
565         "   <Option name='COMPRESS' type='string-select' default='PNG' description='PPNG = Palette PNG; DEFLATE = zlib '>"
566         "       <Value>JPEG</Value><Value>PNG</Value><Value>PPNG</Value><Value>JPNG</Value>"
567         "       <Value>TIF</Value><Value>DEFLATE</Value><Value>NONE</Value>"
568 #if defined(LERC)
569         "       <Value>LERC</Value>"
570 #endif
571         "   </Option>"
572         "   <Option name='INTERLEAVE' type='string-select' default='PIXEL'>"
573         "       <Value>PIXEL</Value>"
574         "       <Value>BAND</Value>"
575         "   </Option>\n"
576         "   <Option name='ZSIZE' type='int' description='Third dimension size' default='1'/>"
577         "   <Option name='QUALITY' type='int' description='best=99, bad=0, default=85'/>\n"
578         "   <Option name='OPTIONS' type='string' description='Freeform dataset parameters'/>\n"
579         "   <Option name='BLOCKSIZE' type='int' description='Block size, both x and y, default 512'/>\n"
580         "   <Option name='BLOCKXSIZE' type='int' description='Block x size, default=512'/>\n"
581         "   <Option name='BLOCKYSIZE' type='int' description='Block y size, default=512'/>\n"
582         "   <Option name='NETBYTEORDER' type='boolean' "
583                     "description='Force endian for certain compress options, default is host order'/>\n"
584         "   <Option name='CACHEDSOURCE' type='string' "
585                     "description='The source raster, if this is a cache'/>\n"
586         "   <Option name='UNIFORM_SCALE' type='int' description='Scale of overlays in MRF, usually 2'/>\n"
587         "   <Option name='NOCOPY' type='boolean' description='Leave created MRF empty, default=no'/>\n"
588         "   <Option name='DATANAME' type='string' description='Data file name'/>\n"
589         "   <Option name='INDEXNAME' type='string' description='Index file name'/>\n"
590         "   <Option name='SPACING' type='int' "
591                     "description='Leave this many unused bytes before each tile, default=0'/>\n"
592         "   <Option name='PHOTOMETRIC' type='string-select' default='DEFAULT' "
593                     "description='Band interpretation, may affect block encoding'>\n"
594         "       <Value>MULTISPECTRAL</Value>"
595         "       <Value>RGB</Value>"
596         "       <Value>YCC</Value>"
597         "   </Option>\n"
598         "</CreationOptionList>\n");
599 
600     driver->SetMetadataItem(
601       GDAL_DMD_OPENOPTIONLIST,
602       "<OpenOptionList>"
603       "    <Option name='NOERRORS' type='boolean' description='Ignore decompression errors' default='FALSE'/>"
604       "    <Option name='ZSLICE' type='int' description='For a third dimension MRF, pick a slice' default='0'/>"
605       "</OpenOptionList>"
606       );
607 
608     driver->pfnOpen = MRFDataset::Open;
609     driver->pfnIdentify = MRFDataset::Identify;
610     driver->pfnCreateCopy = MRFDataset::CreateCopy;
611     driver->pfnCreate = MRFDataset::Create;
612     driver->pfnDelete = MRFDataset::Delete;
613     GetGDALDriverManager()->RegisterDriver(driver);
614 }
615