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