Line data Source code
1 : /*
2 : Copyright 2013-2021 Esri
3 : Licensed under the Apache License, Version 2.0 (the "License");
4 : you may not use this file except in compliance with the License.
5 : You may obtain a copy of the License at
6 : http://www.apache.org/licenses/LICENSE-2.0
7 : Unless required by applicable law or agreed to in writing, software
8 : distributed under the License is distributed on an "AS IS" BASIS,
9 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 : See the License for the specific language governing permissions and
11 : limitations under the License.
12 : A local copy of the license and additional notices are located with the
13 : source distribution at:
14 : http://github.com/Esri/lerc/
15 :
16 : LERC band implementation
17 : LERC page compression and decompression functions
18 :
19 : Authors: Lucian Plesea
20 : */
21 :
22 : #include "marfa.h"
23 : #include <algorithm>
24 : #include <vector>
25 : #include "LERCV1/Lerc1Image.h"
26 :
27 : #include "gdal_priv_templates.hpp"
28 : #include "gdal_openinfo.h"
29 :
30 : // Requires lerc at least 2v4, where the c_api changed, but there is no good way
31 : // to check
32 : #include <Lerc_c_api.h>
33 : #include <Lerc_types.h>
34 :
35 : #ifndef LERC_AT_LEAST_VERSION
36 : #define LERC_AT_LEAST_VERSION(maj, min, patch) 0
37 : #endif
38 :
39 : // name of internal or external libLerc namespace
40 : #if defined(USING_NAMESPACE_LERC)
41 : #define L2NS GDAL_LercNS
42 : #else
43 : // External lerc
44 : #define L2NS LercNS
45 : #endif
46 :
47 : #define INFOIDX(T) static_cast<size_t>(L2NS::InfoArrOrder::T)
48 :
49 : USING_NAMESPACE_LERC1
50 : NAMESPACE_MRF_START
51 :
52 : // Read an unaligned 4 byte little endian int from location p, advances pointer
53 160 : static void READ_GINT32(int &X, const char *&p)
54 : {
55 160 : memcpy(&X, p, sizeof(GInt32));
56 160 : p += sizeof(GInt32);
57 160 : }
58 :
59 16 : static void READ_FLOAT(float &X, const char *&p)
60 : {
61 16 : memcpy(&X, p, sizeof(float));
62 16 : p += sizeof(float);
63 16 : }
64 :
65 : //
66 : // Check that a buffer contains a supported Lerc1 blob, the type supported by
67 : // MRF Can't really check everything without decoding, this just checks the main
68 : // structure returns actual size if it is Lerc1 with size < sz returns 0 if
69 : // format doesn't match returns -1 if Lerc1 but size can't be determined
70 : //
71 : // returns -<actual size> if actual size > sz
72 :
73 16 : static int checkV1(const char *s, size_t sz)
74 : {
75 : GInt32 nBytesMask, nBytesData;
76 :
77 : // Header is 34 bytes
78 : // band header is 16, first mask band then data band
79 16 : if (sz < static_cast<size_t>(
80 16 : Lerc1Image::computeNumBytesNeededToWriteVoidImage()))
81 0 : return 0;
82 : // First ten bytes are ASCII signature
83 16 : if (!STARTS_WITH(s, "CntZImage "))
84 0 : return 0;
85 16 : s += 10;
86 :
87 : // Version 11
88 : int i;
89 16 : READ_GINT32(i, s);
90 16 : if (i != 11)
91 0 : return 0;
92 :
93 : // Type 8 is CntZ
94 16 : READ_GINT32(i, s);
95 16 : if (i != 8)
96 0 : return 0;
97 :
98 : // Height
99 16 : READ_GINT32(i, s); // Arbitrary number in Lerc1Image::read()
100 16 : if (i > 20000 || i <= 0)
101 0 : return 0;
102 :
103 : // Width
104 16 : READ_GINT32(i, s);
105 16 : if (i > 20000 || i <= 0)
106 0 : return 0;
107 :
108 : // Skip the max val stored as double
109 16 : s += sizeof(double);
110 :
111 : // First header should be the mask, which mean 0 blocks
112 : // Height
113 16 : READ_GINT32(i, s);
114 16 : if (i != 0)
115 0 : return 0;
116 :
117 : // WIDTH
118 16 : READ_GINT32(i, s);
119 16 : if (i != 0)
120 0 : return 0;
121 :
122 16 : READ_GINT32(nBytesMask, s);
123 16 : if (nBytesMask < 0)
124 0 : return 0;
125 :
126 : // mask max value, 0 or 1 as float
127 : float val;
128 16 : READ_FLOAT(val, s);
129 16 : if (val != 0.0f && val != 1.0f)
130 0 : return 0;
131 :
132 : // If data header can't be read the actual size is unknown
133 16 : if (nBytesMask > INT_MAX - 66 || static_cast<size_t>(66 + nBytesMask) >= sz)
134 : {
135 0 : return -1;
136 : }
137 :
138 16 : s += nBytesMask;
139 :
140 : // Data Band header
141 16 : READ_GINT32(i,
142 : s); // number of full height blocks, never single pixel blocks
143 16 : if (i <= 0 || i > 10000)
144 0 : return 0;
145 :
146 16 : READ_GINT32(i,
147 : s); // number of full width blocks, never single pixel blocks
148 16 : if (i <= 0 || i > 10000)
149 0 : return 0;
150 :
151 16 : READ_GINT32(nBytesData, s);
152 16 : if (nBytesData < 0)
153 0 : return 0;
154 :
155 : // Actual LERC blob size
156 16 : if (66 + nBytesMask > INT_MAX - nBytesData)
157 0 : return -1;
158 16 : int size = static_cast<int>(66 + nBytesMask + nBytesData);
159 16 : return (static_cast<size_t>(size) > sz) ? -size : size;
160 : }
161 :
162 : // Load a buffer of type T into a LERC1 zImg, with a given stride
163 : template <typename T>
164 12 : static void Lerc1ImgFill(Lerc1Image &zImg, T *src, const ILImage &img,
165 : GInt32 stride)
166 : {
167 12 : int w = img.pagesize.x;
168 12 : int h = img.pagesize.y;
169 12 : zImg.resize(w, h);
170 12 : const float ndv = static_cast<float>(img.hasNoData ? img.NoDataValue : 0);
171 12 : if (stride == 1)
172 : {
173 4617 : for (int row = 0; row < h; row++)
174 2363904 : for (int col = 0; col < w; col++)
175 : {
176 2359296 : float val = static_cast<float>(*src++);
177 2359296 : zImg(row, col) = val;
178 2359296 : zImg.SetMask(row, col, !CPLIsEqual(ndv, val));
179 : }
180 9 : return;
181 : }
182 1539 : for (int row = 0; row < h; row++)
183 787968 : for (int col = 0; col < w; col++)
184 : {
185 786432 : float val = static_cast<float>(*src);
186 786432 : src += stride;
187 786432 : zImg(row, col) = val;
188 786432 : zImg.SetMask(row, col, !CPLIsEqual(ndv, val));
189 : }
190 : }
191 :
192 : // Unload LERC1 zImg into a type T buffer
193 : template <typename T>
194 16 : static bool Lerc1ImgUFill(Lerc1Image &zImg, T *dst, const ILImage &img,
195 : GInt32 stride)
196 : {
197 14 : const T ndv =
198 14 : static_cast<T>(img.hasNoData && GDALIsValueInRange<T>(img.NoDataValue)
199 10 : ? img.NoDataValue
200 : : 0);
201 16 : if (img.pagesize.y != zImg.getHeight() || img.pagesize.x != zImg.getWidth())
202 0 : return false;
203 16 : int w = img.pagesize.x;
204 16 : int h = img.pagesize.y;
205 16 : if (1 == stride)
206 : {
207 6672 : for (int row = 0; row < h; row++)
208 : {
209 3417610 : for (int col = 0; col < w; col++)
210 : {
211 3410950 : if (zImg.IsValid(row, col))
212 : {
213 27592 : GDALCopyWord(zImg(row, col), *dst);
214 : }
215 : else
216 : {
217 3383350 : *dst = ndv;
218 : }
219 3410950 : ++dst;
220 : }
221 : }
222 13 : return true;
223 : }
224 1539 : for (int row = 0; row < h; row++)
225 : {
226 787968 : for (int col = 0; col < w; col++)
227 : {
228 786432 : if (zImg.IsValid(row, col))
229 : {
230 239713 : GDALCopyWord(zImg(row, col), *dst);
231 : }
232 : else
233 : {
234 546719 : *dst = ndv;
235 : }
236 786432 : dst += stride;
237 : }
238 : }
239 3 : return true;
240 : }
241 :
242 10 : static CPLErr CompressLERC1(buf_mgr &dst, buf_mgr &src, const ILImage &img,
243 : double precision)
244 : {
245 20 : Lerc1Image zImg;
246 10 : GInt32 stride = img.pagesize.c;
247 10 : Lerc1NS::Byte *ptr = reinterpret_cast<Lerc1NS::Byte *>(dst.buffer);
248 :
249 22 : for (int c = 0; c < stride; c++)
250 : {
251 : #define FILL(T) \
252 : Lerc1ImgFill(zImg, reinterpret_cast<T *>(src.buffer) + c, img, stride)
253 12 : switch (img.dt)
254 : {
255 5 : case GDT_UInt8:
256 5 : FILL(GByte);
257 5 : break;
258 1 : case GDT_UInt16:
259 1 : FILL(GUInt16);
260 1 : break;
261 1 : case GDT_Int16:
262 1 : FILL(GInt16);
263 1 : break;
264 1 : case GDT_Int32:
265 1 : FILL(GInt32);
266 1 : break;
267 1 : case GDT_UInt32:
268 1 : FILL(GUInt32);
269 1 : break;
270 2 : case GDT_Float32:
271 2 : FILL(float);
272 2 : break;
273 1 : case GDT_Float64:
274 1 : FILL(double);
275 1 : break;
276 0 : default:
277 0 : break;
278 : }
279 : #undef FILL
280 12 : if (!zImg.write(&ptr, precision))
281 : {
282 0 : CPLError(CE_Failure, CPLE_AppDefined,
283 : "MRF: Error during LERC compression");
284 0 : return CE_Failure;
285 : }
286 : }
287 :
288 : // write changes the value of the pointer, we can find the size by testing
289 : // how far it moved Add a couple of bytes, to avoid buffer overflow on
290 : // reading
291 10 : dst.size = reinterpret_cast<char *>(ptr) - dst.buffer + PADDING_BYTES;
292 10 : CPLDebug("MRF_LERC", "LERC Compressed to %d\n", (int)dst.size);
293 10 : return CE_None;
294 : }
295 :
296 : // LERC 1 Decompression
297 14 : static CPLErr DecompressLERC1(buf_mgr &dst, const buf_mgr &src,
298 : const ILImage &img)
299 : {
300 28 : Lerc1Image zImg;
301 :
302 : // need to add the padding bytes so that out-of-buffer-access
303 14 : size_t nRemainingBytes = src.size + PADDING_BYTES;
304 14 : Lerc1NS::Byte *ptr = reinterpret_cast<Lerc1NS::Byte *>(src.buffer);
305 14 : GInt32 stride = img.pagesize.c;
306 30 : for (int c = 0; c < stride; c++)
307 : {
308 : // Check that input passes snicker test
309 16 : if (checkV1(reinterpret_cast<char *>(ptr), nRemainingBytes) <= 0)
310 : {
311 0 : CPLError(CE_Failure, CPLE_AppDefined,
312 : "MRF: LERC1 tile format error");
313 0 : return CE_Failure;
314 : }
315 :
316 16 : if (!zImg.read(&ptr, nRemainingBytes, 1e12))
317 : {
318 0 : CPLError(CE_Failure, CPLE_AppDefined,
319 : "MRF: Error during LERC decompression");
320 0 : return CE_Failure;
321 : }
322 :
323 : // Unpack from zImg to dst buffer, calling the right type
324 16 : bool success = false;
325 : #define UFILL(T) \
326 : success = Lerc1ImgUFill(zImg, reinterpret_cast<T *>(dst.buffer) + c, img, \
327 : stride)
328 16 : switch (img.dt)
329 : {
330 7 : case GDT_UInt8:
331 7 : UFILL(GByte);
332 7 : break;
333 0 : case GDT_Int8:
334 0 : UFILL(GInt8);
335 0 : break;
336 1 : case GDT_UInt16:
337 1 : UFILL(GUInt16);
338 1 : break;
339 1 : case GDT_Int16:
340 1 : UFILL(GInt16);
341 1 : break;
342 1 : case GDT_Int32:
343 1 : UFILL(GInt32);
344 1 : break;
345 1 : case GDT_UInt32:
346 1 : UFILL(GUInt32);
347 1 : break;
348 4 : case GDT_Float32:
349 4 : UFILL(float);
350 4 : break;
351 1 : case GDT_Float64:
352 1 : UFILL(double);
353 1 : break;
354 0 : default:
355 0 : break;
356 : }
357 : #undef UFILL
358 16 : if (!success)
359 : {
360 0 : CPLError(CE_Failure, CPLE_AppDefined,
361 : "MRF: Error during LERC decompression");
362 0 : return CE_Failure;
363 : }
364 : }
365 :
366 14 : return CE_None;
367 : }
368 :
369 : // Lerc2
370 :
371 2 : static GDALDataType L2toGDT(L2NS::DataType L2type)
372 : {
373 : GDALDataType dt;
374 2 : switch (L2type)
375 : {
376 0 : case L2NS::DataType::dt_short:
377 0 : dt = GDT_Int16;
378 0 : break;
379 0 : case L2NS::DataType::dt_ushort:
380 0 : dt = GDT_UInt16;
381 0 : break;
382 0 : case L2NS::DataType::dt_int:
383 0 : dt = GDT_Int32;
384 0 : break;
385 0 : case L2NS::DataType::dt_uint:
386 0 : dt = GDT_UInt32;
387 0 : break;
388 1 : case L2NS::DataType::dt_float:
389 1 : dt = GDT_Float32;
390 1 : break;
391 0 : case L2NS::DataType::dt_double:
392 0 : dt = GDT_Float64;
393 0 : break;
394 0 : case L2NS::DataType::dt_char:
395 0 : dt = GDT_Int8;
396 0 : break;
397 1 : default: // Unsigned byte
398 1 : dt = GDT_UInt8;
399 : }
400 2 : return dt;
401 : }
402 :
403 6425 : static L2NS::DataType GDTtoL2(GDALDataType dt)
404 : {
405 : L2NS::DataType L2dt;
406 6425 : switch (dt)
407 : {
408 2 : case GDT_Int16:
409 2 : L2dt = L2NS::DataType::dt_short;
410 2 : break;
411 2 : case GDT_UInt16:
412 2 : L2dt = L2NS::DataType::dt_ushort;
413 2 : break;
414 2 : case GDT_Int32:
415 2 : L2dt = L2NS::DataType::dt_int;
416 2 : break;
417 2 : case GDT_UInt32:
418 2 : L2dt = L2NS::DataType::dt_uint;
419 2 : break;
420 3 : case GDT_Float32:
421 3 : L2dt = L2NS::DataType::dt_float;
422 3 : break;
423 4 : case GDT_Float64:
424 4 : L2dt = L2NS::DataType::dt_double;
425 4 : break;
426 6410 : default:
427 6410 : L2dt = L2NS::DataType::dt_uchar;
428 : }
429 6425 : return L2dt;
430 : }
431 :
432 : // Populate a LERC2 bitmask based on comparison with the image no data value
433 : // Returns the number of NoData values found
434 : template <typename T>
435 1 : static size_t MaskFill(std::vector<Lerc1NS::Byte> &bm, const T *src,
436 : const ILImage &img)
437 : {
438 1 : size_t w = static_cast<size_t>(img.pagesize.x);
439 1 : size_t h = static_cast<size_t>(img.pagesize.y);
440 1 : size_t stride = static_cast<size_t>(img.pagesize.c);
441 1 : size_t nndv = 0;
442 :
443 1 : bm.resize(w * h);
444 :
445 1 : T ndv = static_cast<T>(img.NoDataValue);
446 1 : if (!img.hasNoData)
447 0 : ndv = 0; // It really doesn't get called when img doesn't have
448 : // NoDataValue
449 262145 : for (size_t i = 0; i < bm.size(); i++)
450 : {
451 262144 : if (ndv == src[i * stride])
452 : {
453 57 : bm[i] = 0;
454 57 : nndv++;
455 : }
456 : else
457 : {
458 262087 : bm[i] = 1;
459 : }
460 : }
461 :
462 1 : return nndv;
463 : }
464 :
465 : // Fill in no data values based on a LERC2 bitmask
466 : template <typename T>
467 1 : static void UnMask(std::vector<Lerc1NS::Byte> &bm, T *data, const ILImage &img)
468 : {
469 1 : size_t w = static_cast<size_t>(img.pagesize.x);
470 1 : size_t h = static_cast<size_t>(img.pagesize.y);
471 1 : size_t stride = static_cast<size_t>(img.pagesize.c);
472 :
473 1 : if (bm.size() != w * h)
474 0 : return;
475 :
476 1 : T ndv = T(img.NoDataValue);
477 1 : if (stride == 1)
478 : {
479 262145 : for (size_t i = 0; i < w * h; i++)
480 262144 : if (!bm[i])
481 0 : data[i] = ndv;
482 : }
483 : else
484 : {
485 0 : for (size_t i = 0; i < w * h; i++)
486 0 : if (!bm[i])
487 0 : for (size_t c = 0; c < stride; c++)
488 0 : data[i * stride + c] = ndv;
489 : }
490 : }
491 :
492 4812 : static CPLErr CompressLERC2(buf_mgr &dst, buf_mgr &src, const ILImage &img,
493 : double precision, int l2ver)
494 : {
495 4812 : auto w = static_cast<int>(img.pagesize.x);
496 4812 : auto h = static_cast<int>(img.pagesize.y);
497 4812 : auto stride = static_cast<int>(img.pagesize.c);
498 :
499 : // build a mask
500 9624 : std::vector<Lerc1NS::Byte> bm;
501 4812 : size_t nndv = 0;
502 4812 : if (img.hasNoData)
503 : { // Only build a bitmask if no data value is defined
504 1 : switch (img.dt)
505 : {
506 :
507 : #define MASK(T) nndv = MaskFill(bm, reinterpret_cast<T *>(src.buffer), img)
508 :
509 1 : case GDT_UInt8:
510 1 : MASK(GByte);
511 1 : break;
512 0 : case GDT_UInt16:
513 0 : MASK(GUInt16);
514 0 : break;
515 0 : case GDT_Int16:
516 0 : MASK(GInt16);
517 0 : break;
518 0 : case GDT_Int32:
519 0 : MASK(GInt32);
520 0 : break;
521 0 : case GDT_UInt32:
522 0 : MASK(GUInt32);
523 0 : break;
524 0 : case GDT_Float32:
525 0 : MASK(float);
526 0 : break;
527 0 : case GDT_Float64:
528 0 : MASK(double);
529 0 : break;
530 0 : default:
531 0 : break;
532 :
533 : #undef MASK
534 : }
535 : }
536 :
537 4812 : unsigned int sz = 0;
538 4812 : auto pbm = bm.data();
539 4812 : if (!bm.empty() && nndv != bm.size())
540 1 : pbm = nullptr;
541 14436 : auto status = lerc_encodeForVersion(
542 4812 : reinterpret_cast<void *>(src.buffer), l2ver,
543 4812 : static_cast<unsigned int>(GDTtoL2(img.dt)), stride, w, h, 1,
544 : #if LERC_AT_LEAST_VERSION(3, 0, 0)
545 : pbm ? 1 : 0,
546 : #endif
547 4812 : pbm, precision, reinterpret_cast<Lerc1NS::Byte *>(dst.buffer),
548 4812 : static_cast<unsigned int>(dst.size), &sz);
549 :
550 4812 : if (L2NS::ErrCode::Ok != static_cast<L2NS::ErrCode>(status) ||
551 4812 : sz > (dst.size - PADDING_BYTES))
552 : {
553 0 : CPLError(CE_Failure, CPLE_AppDefined,
554 : "MRF: Error during LERC2 compression");
555 0 : return CE_Failure;
556 : }
557 :
558 4812 : dst.size = static_cast<size_t>(sz) + PADDING_BYTES;
559 4812 : return CE_None;
560 : }
561 :
562 : // LERC1 splits of early, so this is mostly LERC2
563 1627 : CPLErr LERC_Band::Decompress(buf_mgr &dst, buf_mgr &src)
564 : {
565 2695 : if (src.size >= Lerc1Image::computeNumBytesNeededToWriteVoidImage() &&
566 1068 : IsLerc1(src.buffer))
567 14 : return DecompressLERC1(dst, src, img);
568 :
569 : // Can only be LERC2 here, verify
570 1613 : if (src.size < 50 || !IsLerc2(src.buffer))
571 : {
572 0 : CPLError(CE_Failure, CPLE_AppDefined, "MRF: Not a lerc tile");
573 0 : return CE_Failure;
574 : }
575 :
576 1613 : auto w = static_cast<int>(img.pagesize.x);
577 1613 : auto h = static_cast<int>(img.pagesize.y);
578 1613 : auto stride = static_cast<int>(img.pagesize.c);
579 :
580 3226 : std::vector<Lerc1NS::Byte> bm;
581 1613 : int nMasks = 0;
582 : #if LERC_AT_LEAST_VERSION(3, 0, 0)
583 : {
584 : // Determine the number of masks
585 : std::vector<unsigned int> info(INFOIDX(nMasks) + 1);
586 : auto status =
587 : lerc_getBlobInfo(reinterpret_cast<Lerc1NS::Byte *>(src.buffer),
588 : static_cast<unsigned int>(src.size), info.data(),
589 : nullptr, static_cast<int>(info.size()), 0);
590 : if (L2NS::ErrCode::Ok == static_cast<L2NS::ErrCode>(status) &&
591 : 1 == info[INFOIDX(nBands)])
592 : {
593 : nMasks = info[INFOIDX(nMasks)];
594 : }
595 : }
596 : #else
597 1613 : if (img.hasNoData)
598 1 : nMasks = 1;
599 : #endif
600 1613 : if (nMasks > 0)
601 1 : bm.resize(static_cast<size_t>(w) * static_cast<size_t>(h) * nMasks);
602 1613 : auto pbm = bm.data();
603 1613 : if (bm.empty())
604 1612 : pbm = nullptr;
605 :
606 : // Decoding may fail for many different reasons, including input not
607 : // matching tile expectations
608 : auto status =
609 3226 : lerc_decode(reinterpret_cast<Lerc1NS::Byte *>(src.buffer),
610 1613 : static_cast<unsigned int>(src.size),
611 : #if LERC_AT_LEAST_VERSION(3, 0, 0)
612 : nMasks,
613 : #endif
614 : pbm, stride, w, h, 1,
615 1613 : static_cast<unsigned int>(GDTtoL2(img.dt)), dst.buffer);
616 1613 : if (L2NS::ErrCode::Ok != static_cast<L2NS::ErrCode>(status))
617 : {
618 0 : CPLError(CE_Failure, CPLE_AppDefined,
619 : "MRF: Error decoding Lerc: status=%d", status);
620 0 : return CE_Failure;
621 : }
622 :
623 : // No mask means we're done
624 1613 : if (bm.empty())
625 1612 : return CE_None;
626 :
627 : // Fill in no data values
628 1 : switch (img.dt)
629 : {
630 : #define UNMASK(T) UnMask(bm, reinterpret_cast<T *>(dst.buffer), img)
631 1 : case GDT_UInt8:
632 1 : UNMASK(GByte);
633 1 : break;
634 0 : case GDT_UInt16:
635 0 : UNMASK(GUInt16);
636 0 : break;
637 0 : case GDT_Int16:
638 0 : UNMASK(GInt16);
639 0 : break;
640 0 : case GDT_Int32:
641 0 : UNMASK(GInt32);
642 0 : break;
643 0 : case GDT_UInt32:
644 0 : UNMASK(GUInt32);
645 0 : break;
646 0 : case GDT_Float32:
647 0 : UNMASK(float);
648 0 : break;
649 0 : case GDT_Float64:
650 0 : UNMASK(double);
651 0 : break;
652 0 : default:
653 0 : break;
654 : #undef DECODE
655 : }
656 1 : return CE_None;
657 : }
658 :
659 4822 : CPLErr LERC_Band::Compress(buf_mgr &dst, buf_mgr &src)
660 : {
661 4822 : if (version == 2)
662 4812 : return CompressLERC2(dst, src, img, precision, l2ver);
663 : else
664 10 : return CompressLERC1(dst, src, img, precision);
665 : }
666 :
667 7 : CPLXMLNode *LERC_Band::GetMRFConfig(GDALOpenInfo *poOpenInfo)
668 : {
669 : // Header of Lerc2 takes 58 bytes, an empty area 62 or more, depending on
670 : // the subversion. Size of Lerc1 empty file is 67 Anything under 50 bytes
671 : // can't be lerc
672 7 : if (poOpenInfo->eAccess != GA_ReadOnly ||
673 7 : poOpenInfo->pszFilename == nullptr ||
674 7 : poOpenInfo->pabyHeader == nullptr ||
675 7 : strlen(poOpenInfo->pszFilename) < 1 || poOpenInfo->nHeaderBytes < 50)
676 0 : return nullptr;
677 :
678 : // Check the header too
679 7 : char *psz = reinterpret_cast<char *>(poOpenInfo->pabyHeader);
680 14 : CPLString sHeader;
681 7 : sHeader.assign(psz, psz + poOpenInfo->nHeaderBytes);
682 7 : if (!(IsLerc1(sHeader) || IsLerc2(sHeader)))
683 0 : return nullptr;
684 :
685 7 : GDALDataType dt = GDT_Unknown; // Use this as a validity flag
686 :
687 : // Use this structure to fetch width and height
688 7 : ILSize size(-1, -1, 1, 1, 1);
689 :
690 12 : if (IsLerc1(sHeader) &&
691 5 : sHeader.size() >= Lerc1Image::computeNumBytesNeededToWriteVoidImage())
692 : {
693 5 : if (Lerc1Image::getwh(reinterpret_cast<Lerc1NS::Byte *>(psz),
694 5 : poOpenInfo->nHeaderBytes, size.x, size.y))
695 5 : dt = GDALGetDataTypeByName(CSLFetchNameValueDef(
696 5 : poOpenInfo->papszOpenOptions, "DATATYPE", "Byte"));
697 : }
698 2 : else if (IsLerc2(sHeader))
699 : {
700 : // getBlobInfo will fail without the whole LERC blob
701 : // Wasteful, but that's the only choice given by the LERC C API
702 : // This will only work if the Lerc2 file is under the constant defined
703 : // here
704 : static const GIntBig MAX_L2SIZE(10 * 1024 * 1024); // 10MB
705 2 : GByte *buffer = nullptr;
706 : vsi_l_offset l2size;
707 :
708 2 : if (VSIIngestFile(nullptr, poOpenInfo->pszFilename, &buffer, &l2size,
709 2 : MAX_L2SIZE))
710 : {
711 : //! Info returned in infoArray is { version, dataType, nDim, nCols,
712 : //! nRows, nBands, nValidPixels... }, see Lerc_types.h .
713 4 : std::vector<unsigned int> info(INFOIDX(nValidPixels) + 1);
714 : auto status =
715 2 : lerc_getBlobInfo(reinterpret_cast<Lerc1NS::Byte *>(buffer),
716 : static_cast<unsigned int>(l2size), info.data(),
717 2 : nullptr, static_cast<int>(info.size()), 0);
718 2 : VSIFree(buffer);
719 4 : if (L2NS::ErrCode::Ok == static_cast<L2NS::ErrCode>(status) &&
720 2 : 1 == info[INFOIDX(nBands)])
721 : {
722 2 : size.x = info[INFOIDX(nCols)];
723 2 : size.y = info[INFOIDX(nRows)];
724 2 : if (info[INFOIDX(version)] > 3) // Single band before version 4
725 0 : size.c = info[INFOIDX(nDim)];
726 2 : dt = L2toGDT(
727 2 : static_cast<L2NS::DataType>(info[INFOIDX(dataType)]));
728 : }
729 : }
730 : }
731 :
732 7 : if (size.x <= 0 || size.y <= 0 || dt == GDT_Unknown)
733 0 : return nullptr;
734 :
735 : // Build and return the MRF configuration for a single tile reader
736 7 : CPLXMLNode *config = CPLCreateXMLNode(nullptr, CXT_Element, "MRF_META");
737 7 : CPLXMLNode *raster = CPLCreateXMLNode(config, CXT_Element, "Raster");
738 7 : XMLSetAttributeVal(raster, "Size", size, "%.0f");
739 7 : XMLSetAttributeVal(raster, "PageSize", size, "%.0f");
740 7 : CPLCreateXMLElementAndValue(raster, "Compression", CompName(IL_LERC));
741 7 : CPLCreateXMLElementAndValue(raster, "DataType", GDALGetDataTypeName(dt));
742 7 : CPLCreateXMLElementAndValue(raster, "DataFile", poOpenInfo->pszFilename);
743 : // Set a magic index file name to prevent the driver from attempting to open
744 : // it
745 7 : CPLCreateXMLElementAndValue(raster, "IndexFile", "(null)");
746 : // The NDV could be passed as an open option
747 : const char *pszNDV =
748 7 : CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "NDV", "");
749 7 : if (strlen(pszNDV) > 0)
750 : {
751 : CPLXMLNode *values =
752 4 : CPLCreateXMLNode(raster, CXT_Element, "DataValues");
753 4 : XMLSetAttributeVal(values, "NoData", pszNDV);
754 : }
755 7 : return config;
756 : }
757 :
758 76 : LERC_Band::LERC_Band(MRFDataset *pDS, const ILImage &image, int b, int level)
759 76 : : MRFRasterBand(pDS, image, b, level)
760 : {
761 : // Lerc doesn't handle 64bit int types
762 76 : if (image.dt == GDT_UInt64 || image.dt == GDT_Int64)
763 : {
764 0 : CPLError(CE_Failure, CPLE_NotSupported,
765 : "Lerc compression of 64 bit integers is not supported");
766 0 : return;
767 : }
768 :
769 : // Pick 1/1000 for floats and 0.5 losless for integers.
770 76 : if (eDataType == GDT_Float32 || eDataType == GDT_Float64)
771 18 : precision = strtod(GetOptionValue("LERC_PREC", ".001"), nullptr);
772 : else
773 58 : precision =
774 58 : std::max(0.5, strtod(GetOptionValue("LERC_PREC", ".5"), nullptr));
775 :
776 : // Encode in V2 by default.
777 76 : version = GetOptlist().FetchBoolean("V1", FALSE) ? 1 : 2;
778 : // For LERC 2 there are multiple versions too, -1 means use the library
779 : // default Use v2.2 for single band encoding
780 152 : l2ver = atoi(GetOptlist().FetchNameValueDef(
781 76 : "L2_VER", (img.pagesize.c == 1) ? "2" : "-1"));
782 :
783 76 : if (image.pageSizeBytes > INT_MAX / 4)
784 : {
785 0 : CPLError(CE_Failure, CPLE_AppDefined, "LERC page too large");
786 0 : return;
787 : }
788 : // Enlarge the page buffer, LERC may expand data.
789 76 : pDS->SetPBufferSize(2 * image.pageSizeBytes);
790 : }
791 :
792 152 : LERC_Band::~LERC_Band()
793 : {
794 152 : }
795 :
796 : NAMESPACE_MRF_END
|