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