Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: LERC driver
5 : * Author: Even Rouault <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2026, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "lercdrivercore.h"
14 :
15 : #include "cpl_vsi.h"
16 : #include "cpl_vsi_virtual.h"
17 :
18 : #include "gdal_frmts.h"
19 : #include "gdal_pam.h"
20 : #include "gdal_priv.h"
21 :
22 : #include "Lerc_c_api.h"
23 :
24 : #include <algorithm>
25 : #include <limits>
26 :
27 : #ifndef LERC_AT_LEAST_VERSION
28 : #define LERC_AT_LEAST_VERSION(maj, min, patch) 0
29 : #endif
30 :
31 : namespace gdal
32 : {
33 : class LERCBand;
34 :
35 : /************************************************************************/
36 : /* LERCDataset */
37 : /************************************************************************/
38 :
39 22 : class LERCDataset final : public GDALPamDataset
40 : {
41 : public:
42 11 : LERCDataset() = default;
43 : ~LERCDataset() override;
44 :
45 : static GDALDataset *Open(GDALOpenInfo *poOpenInfo);
46 :
47 : private:
48 : friend class LERCBand;
49 : friend class LERCMaskBand;
50 :
51 : bool m_bUseNoData = true;
52 : double m_dfNoDataValue = std::numeric_limits<double>::quiet_NaN();
53 : int m_nLercDataType = 0;
54 : int m_nDepth = 0;
55 : int m_nLercBands = 0;
56 : int m_nMaskCount = 0;
57 : unsigned m_nBlobSize = 0;
58 : std::unique_ptr<unsigned char, VSIFreeReleaser> m_pabyBlob{};
59 : std::unique_ptr<unsigned char, VSIFreeReleaser> m_pabyDecodedImage{};
60 : std::unique_ptr<unsigned char, VSIFreeReleaser> m_pabyDecodedMask{};
61 :
62 : const unsigned char *GetDecodedImage();
63 : const unsigned char *GetDecodedMask();
64 : };
65 :
66 : LERCDataset::~LERCDataset() = default;
67 :
68 : /************************************************************************/
69 : /* LERCBand */
70 : /************************************************************************/
71 :
72 : class LERCBand final : public GDALPamRasterBand
73 : {
74 : public:
75 : LERCBand(LERCDataset *poDSIn, int nBandIn, GDALDataType eDT);
76 :
77 : double GetNoDataValue(int *pbHasNoData) override;
78 : int GetMaskFlags() override;
79 : GDALRasterBand *GetMaskBand() override;
80 :
81 : protected:
82 : CPLErr IReadBlock(int nBlockXOff, int nBlockYOff, void *pData) override;
83 :
84 : private:
85 : std::unique_ptr<GDALRasterBand> m_poMaskBand{};
86 : };
87 :
88 : /************************************************************************/
89 : /* LERCMaskBand */
90 : /************************************************************************/
91 :
92 : class LERCMaskBand final : public GDALRasterBand
93 : {
94 : public:
95 3 : LERCMaskBand(LERCDataset *poDSIn, int nMaskIdx) : m_nMaskIdx(nMaskIdx)
96 : {
97 3 : poDS = poDSIn;
98 3 : nBand = 0;
99 3 : eDataType = GDT_Byte;
100 3 : nRasterXSize = poDS->GetRasterXSize();
101 3 : nRasterYSize = poDS->GetRasterYSize();
102 3 : nBlockXSize = nRasterXSize;
103 3 : nBlockYSize = 1;
104 3 : }
105 :
106 : protected:
107 : CPLErr IReadBlock(int nBlockXOff, int nBlockYOff, void *pData) override;
108 :
109 : private:
110 : const int m_nMaskIdx;
111 : };
112 :
113 : /************************************************************************/
114 : /* LERCDataset::GetDecodedImage() */
115 : /************************************************************************/
116 :
117 1466 : const unsigned char *LERCDataset::GetDecodedImage()
118 : {
119 1466 : if (!m_pabyBlob)
120 1455 : return m_pabyDecodedImage.get();
121 :
122 11 : m_pabyDecodedImage.reset(static_cast<unsigned char *>(VSI_MALLOC_VERBOSE(
123 : static_cast<size_t>(nRasterXSize) * nRasterYSize * m_nDepth *
124 : m_nLercBands *
125 : GDALGetDataTypeSizeBytes(GetRasterBand(1)->GetRasterDataType()))));
126 11 : if (m_nMaskCount)
127 : {
128 3 : m_pabyDecodedMask.reset(static_cast<unsigned char *>(VSI_MALLOC_VERBOSE(
129 : static_cast<size_t>(nRasterXSize) * nRasterYSize * m_nMaskCount)));
130 3 : if (!m_pabyDecodedMask)
131 0 : m_pabyDecodedImage.reset();
132 : }
133 :
134 11 : if (m_pabyDecodedImage)
135 : {
136 11 : const lerc_status status = lerc_decode(
137 11 : m_pabyBlob.get(), m_nBlobSize,
138 : #if LERC_AT_LEAST_VERSION(3, 0, 0)
139 : m_nMaskCount,
140 : #endif
141 : m_pabyDecodedMask.get(), m_nDepth, nRasterXSize, nRasterYSize,
142 11 : m_nLercBands, m_nLercDataType, m_pabyDecodedImage.get());
143 11 : if (status != 0)
144 : {
145 0 : m_pabyDecodedImage.reset();
146 0 : CPLError(CE_Failure, CPLE_AppDefined,
147 : "lerc_decode() failed with status %d", status);
148 : }
149 : }
150 11 : m_pabyBlob.reset();
151 :
152 11 : return m_pabyDecodedImage.get();
153 : }
154 :
155 : /************************************************************************/
156 : /* LERCDataset::GetDecodedMask() */
157 : /************************************************************************/
158 :
159 120 : const unsigned char *LERCDataset::GetDecodedMask()
160 : {
161 120 : GetDecodedImage();
162 120 : return m_pabyDecodedMask.get();
163 : }
164 :
165 : /************************************************************************/
166 : /* LERCBand::LERCBand() */
167 : /************************************************************************/
168 :
169 13 : LERCBand::LERCBand(LERCDataset *poDSIn, int nBandIn, GDALDataType eDT)
170 : {
171 13 : poDS = poDSIn;
172 13 : nBand = nBandIn;
173 13 : eDataType = eDT;
174 13 : nRasterXSize = poDS->GetRasterXSize();
175 13 : nRasterYSize = poDS->GetRasterYSize();
176 13 : nBlockXSize = nRasterXSize;
177 13 : nBlockYSize = 1;
178 13 : }
179 :
180 : /************************************************************************/
181 : /* SetNodataWhereMaskIsInvalid() */
182 : /************************************************************************/
183 :
184 : template <class T>
185 48 : static void SetNodataWhereMaskIsInvalid(T *pLine, const unsigned char *pSrcMask,
186 : int nPixels, T nodataValue)
187 : {
188 1104 : for (int i = 0; i < nPixels; ++i)
189 : {
190 1056 : if (!pSrcMask[i])
191 : {
192 256 : pLine[i] = nodataValue;
193 : }
194 : }
195 48 : }
196 :
197 : /************************************************************************/
198 : /* LERCBand::IReadBlock() */
199 : /************************************************************************/
200 :
201 1346 : CPLErr LERCBand::IReadBlock(int, int nBlockYOff, void *pData)
202 : {
203 1346 : auto poGDS = cpl::down_cast<LERCDataset *>(poDS);
204 1346 : const unsigned char *pabyImage = poGDS->GetDecodedImage();
205 1346 : if (!pabyImage)
206 0 : return CE_Failure;
207 :
208 1346 : const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
209 4038 : GDALCopyWords64(
210 2692 : pabyImage + ((nBand - 1) + static_cast<size_t>(nBlockYOff) *
211 2692 : nRasterXSize * poGDS->GetRasterCount()) *
212 1346 : nDTSize,
213 1346 : eDataType, poGDS->GetRasterCount() * nDTSize, pData, eDataType, nDTSize,
214 1346 : nRasterXSize);
215 :
216 1346 : if (poGDS->m_bUseNoData &&
217 1322 : (eDataType == GDT_Float32 || eDataType == GDT_Float64) &&
218 48 : poGDS->m_nMaskCount > 0)
219 : {
220 48 : const unsigned char *pabyMask = poGDS->GetDecodedMask();
221 48 : if (pabyMask)
222 : {
223 48 : const unsigned char *pSrcMask =
224 : pabyMask +
225 48 : static_cast<size_t>(poGDS->m_nMaskCount == 1 ? 0 : nBand - 1) *
226 48 : nRasterXSize * nRasterYSize +
227 48 : static_cast<size_t>(nBlockYOff) * nRasterXSize;
228 48 : if (eDataType == GDT_Float32)
229 : {
230 24 : SetNodataWhereMaskIsInvalid(
231 : static_cast<float *>(pData), pSrcMask, nRasterXSize,
232 24 : static_cast<float>(poGDS->m_dfNoDataValue));
233 : }
234 : else
235 : {
236 24 : SetNodataWhereMaskIsInvalid(static_cast<double *>(pData),
237 : pSrcMask, nRasterXSize,
238 : poGDS->m_dfNoDataValue);
239 : }
240 : }
241 : }
242 :
243 1346 : return CE_None;
244 : }
245 :
246 : /************************************************************************/
247 : /* LERCBand::GetNoDataValue() */
248 : /************************************************************************/
249 :
250 17 : double LERCBand::GetNoDataValue(int *pbHasNoData)
251 : {
252 17 : auto poGDS = cpl::down_cast<LERCDataset *>(poDS);
253 17 : if (poGDS->m_bUseNoData &&
254 16 : (eDataType == GDT_Float32 || eDataType == GDT_Float64) &&
255 2 : poGDS->m_nMaskCount > 0)
256 : {
257 2 : if (pbHasNoData)
258 2 : *pbHasNoData = true;
259 2 : return std::numeric_limits<double>::quiet_NaN();
260 : }
261 15 : if (pbHasNoData)
262 15 : *pbHasNoData = false;
263 15 : return 0;
264 : }
265 :
266 : /************************************************************************/
267 : /* LERCBand::GetMaskFlags() */
268 : /************************************************************************/
269 :
270 10 : int LERCBand::GetMaskFlags()
271 : {
272 10 : auto poGDS = cpl::down_cast<LERCDataset *>(poDS);
273 10 : if (poGDS->m_bUseNoData &&
274 9 : (eDataType == GDT_Float32 || eDataType == GDT_Float64) &&
275 2 : poGDS->m_nMaskCount > 0)
276 : {
277 2 : return GMF_NODATA;
278 : }
279 8 : if (poGDS->m_nMaskCount == 0)
280 7 : return GMF_ALL_VALID;
281 1 : if (poGDS->m_nMaskCount == 1)
282 1 : return GMF_PER_DATASET;
283 0 : return 0;
284 : }
285 :
286 : /************************************************************************/
287 : /* LERCBand::GetMaskBand() */
288 : /************************************************************************/
289 :
290 13 : GDALRasterBand *LERCBand::GetMaskBand()
291 : {
292 13 : auto poGDS = cpl::down_cast<LERCDataset *>(poDS);
293 13 : if (poGDS->m_nMaskCount == 0)
294 7 : return GDALPamRasterBand::GetMaskBand();
295 6 : if (!m_poMaskBand)
296 3 : m_poMaskBand = std::make_unique<LERCMaskBand>(
297 6 : poGDS, poGDS->m_nMaskCount == 1 ? 0 : nBand - 1);
298 6 : return m_poMaskBand.get();
299 : }
300 :
301 : /************************************************************************/
302 : /* LERCMaskBand::IReadBlock() */
303 : /************************************************************************/
304 :
305 72 : CPLErr LERCMaskBand::IReadBlock(int, int nBlockYOff, void *pData)
306 : {
307 72 : auto poGDS = cpl::down_cast<LERCDataset *>(poDS);
308 72 : const unsigned char *pabyMask = poGDS->GetDecodedMask();
309 72 : if (!pabyMask)
310 0 : return CE_Failure;
311 :
312 72 : const unsigned char *pSrc =
313 : pabyMask +
314 72 : static_cast<size_t>(m_nMaskIdx) * nRasterXSize * nRasterYSize +
315 72 : static_cast<size_t>(nBlockYOff) * nRasterXSize;
316 72 : unsigned char *pDst = static_cast<unsigned char *>(pData);
317 1656 : for (int i = 0; i < nRasterXSize; ++i)
318 : {
319 1584 : pDst[i] = pSrc[i] ? 255 : 0;
320 : }
321 :
322 72 : return CE_None;
323 : }
324 :
325 : /************************************************************************/
326 : /* LERCDataset::Open() */
327 : /************************************************************************/
328 :
329 11 : GDALDataset *LERCDataset::Open(GDALOpenInfo *poOpenInfo)
330 : {
331 11 : if (poOpenInfo->eAccess == GA_Update || poOpenInfo->fpL == nullptr)
332 0 : return nullptr;
333 :
334 : VSIStatBufL sStat;
335 11 : if (VSIStatL(poOpenInfo->pszFilename, &sStat) != 0)
336 0 : return nullptr;
337 :
338 11 : const unsigned nBlobSize = static_cast<unsigned>(std::min<uint64_t>(
339 11 : sStat.st_size, std::numeric_limits<unsigned>::max()));
340 11 : if (nBlobSize != static_cast<uint64_t>(sStat.st_size))
341 : {
342 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too large file");
343 0 : return nullptr;
344 : }
345 :
346 11 : const auto nRAMSize = CPLGetUsablePhysicalRAM();
347 11 : if (nRAMSize > 0 && sStat.st_size > nRAMSize)
348 : {
349 0 : CPLError(CE_Failure, CPLE_AppDefined,
350 : "Too large file compared to usable RAM");
351 0 : return nullptr;
352 : }
353 : std::unique_ptr<unsigned char, VSIFreeReleaser> pabyBlob(
354 22 : static_cast<unsigned char *>(VSI_MALLOC_VERBOSE(nBlobSize)));
355 11 : if (!pabyBlob)
356 0 : return nullptr;
357 22 : if (poOpenInfo->fpL->Seek(0, SEEK_SET) != 0 ||
358 11 : poOpenInfo->fpL->Read(pabyBlob.get(), nBlobSize) != nBlobSize)
359 : {
360 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot read file");
361 0 : return nullptr;
362 : }
363 :
364 : unsigned int infoArray[9];
365 : /* Info returned in infoArray is { version, dataType, nDim/nDepth, nCols,
366 : nRows, nBands, nValidPixels, blobSize,
367 : and starting with liblerc 3.0 nRequestedMasks } */
368 :
369 : double arrayRange[3];
370 : /* Info returned in infoArray is { zMin, zMax, maxZErrUsed } */
371 :
372 : int lerc_ret =
373 11 : lerc_getBlobInfo(pabyBlob.get(), nBlobSize, infoArray, arrayRange,
374 11 : CPL_ARRAYSIZE(infoArray), CPL_ARRAYSIZE(arrayRange));
375 11 : if (lerc_ret != 0)
376 : {
377 0 : CPLError(CE_Failure, CPLE_AppDefined, "lerc_getBlobInfo() failed");
378 0 : return nullptr;
379 : }
380 :
381 11 : GDALDataType eDT = GDT_Unknown;
382 11 : const unsigned nLercDataType = infoArray[1];
383 11 : switch (nLercDataType)
384 : {
385 1 : case 0:
386 1 : eDT = GDT_Int8;
387 1 : break;
388 3 : case 1:
389 3 : eDT = GDT_UInt8;
390 3 : break;
391 1 : case 2:
392 1 : eDT = GDT_Int16;
393 1 : break;
394 1 : case 3:
395 1 : eDT = GDT_UInt16;
396 1 : break;
397 1 : case 4:
398 1 : eDT = GDT_Int32;
399 1 : break;
400 1 : case 5:
401 1 : eDT = GDT_UInt32;
402 1 : break;
403 2 : case 6:
404 2 : eDT = GDT_Float32;
405 2 : break;
406 1 : case 7:
407 1 : eDT = GDT_Float64;
408 1 : break;
409 0 : default:
410 0 : CPLError(CE_Failure, CPLE_NotSupported,
411 : "Unhandled LERC data type %u", nLercDataType);
412 0 : return nullptr;
413 : }
414 :
415 11 : const unsigned nDepth = infoArray[2];
416 11 : const unsigned nCols = infoArray[3];
417 11 : const unsigned nRows = infoArray[4];
418 11 : const unsigned nLercBands = infoArray[5];
419 11 : if (nCols == 0 || nRows == 0 || nDepth == 0 || nLercBands == 0 ||
420 11 : nCols > static_cast<unsigned>(INT_MAX) ||
421 11 : nRows > static_cast<unsigned>(INT_MAX) ||
422 11 : nDepth > static_cast<unsigned>(INT_MAX) ||
423 11 : nLercBands > static_cast<unsigned>(INT_MAX))
424 : {
425 0 : CPLError(CE_Failure, CPLE_NotSupported,
426 : "Invalid dimensions: cols=%u x rows=%u x depth=%u x bands=%u",
427 : nCols, nRows, nDepth, nLercBands);
428 0 : return nullptr;
429 : }
430 11 : const int nDTSize = std::max(1, GDALGetDataTypeSizeBytes(eDT));
431 : // nCols * nRows * nDepth limitation is due to liblerc assuming it fits on int
432 : // Cf third_party/LercLib/Lerc.cpp#L381
433 11 : if (nCols > std::numeric_limits<int>::max() / nRows ||
434 11 : nCols * nRows > std::numeric_limits<int>::max() / nDepth ||
435 11 : nDepth > static_cast<unsigned>(std::numeric_limits<int>::max()) /
436 11 : nLercBands ||
437 11 : static_cast<size_t>(nCols) * nRows >
438 11 : (std::numeric_limits<size_t>::max() / 2) / nDTSize ||
439 11 : static_cast<size_t>(nCols) * nRows * nDTSize >
440 11 : (std::numeric_limits<size_t>::max() / 2) /
441 33 : (static_cast<size_t>(nDepth) * nLercBands) ||
442 11 : (nRAMSize > 0 &&
443 11 : static_cast<GIntBig>(nCols) * nRows * nDTSize * nDepth * nLercBands >
444 11 : nRAMSize - sStat.st_size))
445 : {
446 0 : CPLError(CE_Failure, CPLE_NotSupported,
447 : "Too large dimensions cols=%u x rows=%u x depth=%u x bands=%u",
448 : nCols, nRows, nDepth, nLercBands);
449 0 : return nullptr;
450 : }
451 :
452 11 : const int nGDALBands =
453 11 : static_cast<int>(nDepth) * static_cast<int>(nLercBands);
454 :
455 11 : if (nLercBands != 1)
456 : {
457 0 : CPLError(CE_Failure, CPLE_NotSupported,
458 : "LercBands=%u != 1 not supported", nLercBands);
459 0 : return nullptr;
460 : }
461 :
462 22 : auto poDS = std::make_unique<LERCDataset>();
463 11 : poDS->nRasterXSize = static_cast<int>(nCols);
464 11 : poDS->nRasterYSize = static_cast<int>(nRows);
465 22 : if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) ||
466 11 : !GDALCheckBandCount(nGDALBands, /* bIsZeroAllowed =*/false))
467 : {
468 0 : return nullptr;
469 : }
470 :
471 11 : poDS->m_nLercDataType = nLercDataType;
472 11 : poDS->m_nBlobSize = nBlobSize;
473 11 : poDS->m_nDepth = static_cast<int>(nDepth);
474 11 : poDS->m_nLercBands = static_cast<int>(nLercBands);
475 : // Use by WMS driver in MRF/LERC mode
476 11 : const char *pszNDV = CSLFetchNameValue(poOpenInfo->papszOpenOptions, "NDV");
477 11 : if (pszNDV)
478 : {
479 1 : if (EQUAL(pszNDV, "none"))
480 1 : poDS->m_bUseNoData = false;
481 : else
482 0 : poDS->m_dfNoDataValue = CPLAtof(pszNDV);
483 : }
484 : #if LERC_AT_LEAST_VERSION(3, 0, 0)
485 : poDS->m_nMaskCount = static_cast<int>(infoArray[8]);
486 : if (poDS->m_nMaskCount != 0 && poDS->m_nMaskCount != nGDALBands)
487 : {
488 : CPLError(CE_Failure, CPLE_NotSupported,
489 : "Mask count=%d != 0 and != band count=%d not supported",
490 : poDS->m_nMaskCount, nGDALBands);
491 : return nullptr;
492 : }
493 : #else
494 11 : const unsigned nValidPixels = infoArray[6];
495 11 : const size_t nTotalPixels = static_cast<size_t>(nCols) * nRows;
496 22 : if (nTotalPixels <= std::numeric_limits<unsigned>::max() &&
497 11 : nValidPixels < nTotalPixels)
498 3 : poDS->m_nMaskCount = 1;
499 : #endif
500 11 : poDS->m_pabyBlob = std::move(pabyBlob);
501 :
502 24 : for (int i = 0; i < nGDALBands; ++i)
503 : {
504 13 : poDS->SetBand(i + 1, std::make_unique<LERCBand>(poDS.get(), i, eDT));
505 : }
506 :
507 11 : if (nGDALBands > 1)
508 : {
509 1 : poDS->GDALDataset::SetMetadataItem("INTERLEAVE", "PIXEL",
510 : "IMAGE_STRUCTURE");
511 : }
512 11 : poDS->GDALDataset::SetMetadataItem("COMPRESSION", "LERC",
513 : "IMAGE_STRUCTURE");
514 11 : poDS->GDALDataset::SetMetadataItem(
515 : "MAX_Z_ERROR", CPLSPrintf("%g", arrayRange[2]), "IMAGE_STRUCTURE");
516 :
517 : // Initialize any PAM information.
518 11 : poDS->SetDescription(poOpenInfo->pszFilename);
519 11 : poDS->TryLoadXML(poOpenInfo->GetSiblingFiles());
520 11 : poDS->oOvManager.Initialize(poDS.get(), poOpenInfo);
521 :
522 11 : return poDS.release();
523 : }
524 :
525 : } // namespace gdal
526 :
527 : /************************************************************************/
528 : /* GDALRegister_LERC() */
529 : /************************************************************************/
530 :
531 2138 : void GDALRegister_LERC()
532 : {
533 2138 : if (GDALGetDriverByName(DRIVER_NAME) != nullptr)
534 263 : return;
535 :
536 3750 : auto poDriver = std::make_unique<GDALDriver>();
537 1875 : LERCDriverSetCommonMetadata(poDriver.get());
538 :
539 : #ifdef LERC_VERSION_MAJOR
540 : #define XSTRINGIFY(X) #X
541 : #define STRINGIFY(X) XSTRINGIFY(X)
542 : poDriver->SetMetadataItem(
543 : "LIBLERC_VERSION",
544 : STRINGIFY(LERC_VERSION_MAJOR) "." STRINGIFY(
545 : LERC_VERSION_MINOR) "." STRINGIFY(LERC_VERSION_PATCH));
546 : #endif
547 :
548 1875 : poDriver->pfnOpen = gdal::LERCDataset::Open;
549 1875 : GetGDALDriverManager()->RegisterDriver(poDriver.release());
550 : }
|