Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: ECW (ERDAS Wavelet Compression Format) Driver
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2001, Frank Warmerdam <warmerdam@pobox.com>
9 : * Copyright (c) 2007-2013, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : // ncsjpcbuffer.h needs the min and max macros.
15 : #undef NOMINMAX
16 :
17 : #include "cpl_minixml.h"
18 : #include "gdal_ecw.h"
19 : #include "gdal_frmts.h"
20 : #include "ogr_spatialref.h"
21 : #include "ogr_api.h"
22 :
23 : #include "../mem/memdataset.h"
24 :
25 : #include "ecwdrivercore.h"
26 :
27 : #include <algorithm>
28 : #include <cmath>
29 :
30 : #undef NOISY_DEBUG
31 :
32 : static CPLMutex *hECWDatasetMutex = nullptr;
33 : static int bNCSInitialized = FALSE;
34 :
35 : void ECWInitialize(void);
36 :
37 : constexpr int DEFAULT_BLOCK_SIZE = 256;
38 :
39 : GDALDataset *ECWDatasetOpenJPEG2000(GDALOpenInfo *poOpenInfo);
40 :
41 : /************************************************************************/
42 : /* ECWReportError() */
43 : /************************************************************************/
44 :
45 1 : void ECWReportError(CNCSError &oErr, const char *pszMsg)
46 : {
47 : #if ECWSDK_VERSION < 50
48 1 : char *pszErrorMessage = oErr.GetErrorMessage();
49 1 : CPLError(CE_Failure, CPLE_AppDefined, "%s%s", pszMsg, pszErrorMessage);
50 1 : NCSFree(pszErrorMessage);
51 : #else
52 : CPLError(CE_Failure, CPLE_AppDefined, "%s%s", pszMsg,
53 : NCSGetLastErrorText(oErr));
54 : #endif
55 1 : }
56 :
57 : /************************************************************************/
58 : /* ECWRasterBand() */
59 : /************************************************************************/
60 :
61 295 : ECWRasterBand::ECWRasterBand(ECWDataset *poDSIn, int nBandIn, int iOverviewIn,
62 295 : char **papszOpenOptions)
63 :
64 : {
65 295 : this->poDS = poDSIn;
66 295 : poGDS = poDSIn;
67 :
68 295 : this->iOverview = iOverviewIn;
69 295 : this->nBand = nBandIn;
70 295 : eDataType = poDSIn->eRasterDataType;
71 :
72 295 : nRasterXSize = poDS->GetRasterXSize() / (1 << (iOverview + 1));
73 295 : nRasterYSize = poDS->GetRasterYSize() / (1 << (iOverview + 1));
74 :
75 : #if ECWSDK_VERSION >= 51
76 : if (poDSIn->bIsJPEG2000 && poDSIn->poFileView)
77 : {
78 : UINT32 nTileWidth = 0;
79 : poDSIn->poFileView->GetParameter(
80 : const_cast<char *>("JPC:DECOMPRESS:TILESIZE:X"), &nTileWidth);
81 : if (nTileWidth <= static_cast<UINT32>(INT_MAX))
82 : {
83 : nBlockXSize = static_cast<int>(nTileWidth);
84 : }
85 : nBlockXSize = MIN(nBlockXSize, nRasterXSize);
86 :
87 : UINT32 nTileHeight = 0;
88 : poDSIn->poFileView->GetParameter(
89 : const_cast<char *>("JPC:DECOMPRESS:TILESIZE:Y"), &nTileHeight);
90 : if (nTileHeight <= static_cast<UINT32>(INT_MAX))
91 : {
92 : nBlockYSize = static_cast<int>(nTileHeight);
93 : }
94 : nBlockYSize = MIN(nBlockYSize, nRasterYSize);
95 : }
96 : #endif
97 :
98 : // Slightly arbitrary value. Too large values would defeat the purpose
99 : // of the block concept.
100 295 : constexpr int LIMIT_FOR_BLOCK_SIZE = 2048;
101 295 : if (nBlockXSize <= 0 || nBlockYSize <= 0 ||
102 0 : nBlockXSize > LIMIT_FOR_BLOCK_SIZE ||
103 0 : nBlockYSize > LIMIT_FOR_BLOCK_SIZE)
104 : {
105 295 : nBlockXSize = DEFAULT_BLOCK_SIZE;
106 295 : nBlockYSize = DEFAULT_BLOCK_SIZE;
107 : }
108 :
109 : /* -------------------------------------------------------------------- */
110 : /* Work out band color interpretation. */
111 : /* -------------------------------------------------------------------- */
112 295 : if (poDSIn->psFileInfo->eColorSpace == NCSCS_NONE)
113 0 : eBandInterp = GCI_Undefined;
114 295 : else if (poDSIn->psFileInfo->eColorSpace == NCSCS_GREYSCALE)
115 : {
116 67 : eBandInterp = GCI_GrayIndex;
117 : // we could also have alpha band.
118 67 : if (strcmp(poDSIn->psFileInfo->pBands[nBand - 1].szDesc,
119 67 : NCS_BANDDESC_AllOpacity) == 0 ||
120 67 : strcmp(poDSIn->psFileInfo->pBands[nBand - 1].szDesc,
121 : NCS_BANDDESC_GreyscaleOpacity) == 0)
122 : {
123 0 : eBandInterp = GCI_AlphaBand;
124 : }
125 : }
126 228 : else if (poDSIn->psFileInfo->eColorSpace == NCSCS_MULTIBAND)
127 : {
128 62 : eBandInterp = ECWGetColorInterpretationByName(
129 62 : poDSIn->psFileInfo->pBands[nBand - 1].szDesc);
130 : }
131 166 : else if (poDSIn->psFileInfo->eColorSpace == NCSCS_sRGB)
132 : {
133 332 : eBandInterp = ECWGetColorInterpretationByName(
134 166 : poDSIn->psFileInfo->pBands[nBand - 1].szDesc);
135 166 : if (eBandInterp == GCI_Undefined)
136 : {
137 6 : if (nBand == 1)
138 2 : eBandInterp = GCI_RedBand;
139 4 : else if (nBand == 2)
140 2 : eBandInterp = GCI_GreenBand;
141 2 : else if (nBand == 3)
142 2 : eBandInterp = GCI_BlueBand;
143 0 : else if (nBand == 4)
144 : {
145 0 : if (strcmp(poDSIn->psFileInfo->pBands[nBand - 1].szDesc,
146 : NCS_BANDDESC_AllOpacity) == 0)
147 0 : eBandInterp = GCI_AlphaBand;
148 : else
149 0 : eBandInterp = GCI_Undefined;
150 : }
151 : else
152 : {
153 0 : eBandInterp = GCI_Undefined;
154 : }
155 : }
156 : }
157 0 : else if (poDSIn->psFileInfo->eColorSpace == NCSCS_YCbCr)
158 : {
159 0 : if (CPLTestBool(CPLGetConfigOption("CONVERT_YCBCR_TO_RGB", "YES")))
160 : {
161 0 : if (nBand == 1)
162 0 : eBandInterp = GCI_RedBand;
163 0 : else if (nBand == 2)
164 0 : eBandInterp = GCI_GreenBand;
165 0 : else if (nBand == 3)
166 0 : eBandInterp = GCI_BlueBand;
167 : else
168 0 : eBandInterp = GCI_Undefined;
169 : }
170 : else
171 : {
172 0 : if (nBand == 1)
173 0 : eBandInterp = GCI_YCbCr_YBand;
174 0 : else if (nBand == 2)
175 0 : eBandInterp = GCI_YCbCr_CbBand;
176 0 : else if (nBand == 3)
177 0 : eBandInterp = GCI_YCbCr_CrBand;
178 : else
179 0 : eBandInterp = GCI_Undefined;
180 : }
181 : }
182 : else
183 0 : eBandInterp = GCI_Undefined;
184 :
185 : /* -------------------------------------------------------------------- */
186 : /* If this is the base level, create a set of overviews. */
187 : /* -------------------------------------------------------------------- */
188 295 : if (iOverview == -1)
189 : {
190 : int i;
191 295 : for (i = 0; nRasterXSize / (1 << (i + 1)) > 128 &&
192 100 : nRasterYSize / (1 << (i + 1)) > 128;
193 : i++)
194 : {
195 168 : apoOverviews.push_back(
196 84 : new ECWRasterBand(poDSIn, nBandIn, i, papszOpenOptions));
197 : }
198 : }
199 :
200 295 : bPromoteTo8Bit =
201 32 : poDSIn->psFileInfo->nBands == 4 && nBand == 4 &&
202 8 : poDSIn->psFileInfo->pBands[0].nBits == 8 &&
203 4 : poDSIn->psFileInfo->pBands[1].nBits == 8 &&
204 4 : poDSIn->psFileInfo->pBands[2].nBits == 8 &&
205 4 : poDSIn->psFileInfo->pBands[3].nBits == 1 &&
206 329 : eBandInterp == GCI_AlphaBand &&
207 2 : CPLFetchBool(papszOpenOptions, "1BIT_ALPHA_PROMOTION",
208 2 : CPLTestBool(CPLGetConfigOption(
209 : "GDAL_ECW_PROMOTE_1BIT_ALPHA_AS_8BIT", "YES")));
210 295 : if (bPromoteTo8Bit)
211 1 : CPLDebug("ECW", "Fourth (alpha) band is promoted from 1 bit to 8 bit");
212 :
213 295 : if ((poDSIn->psFileInfo->pBands[nBand - 1].nBits % 8) != 0 &&
214 22 : !bPromoteTo8Bit)
215 21 : GDALPamRasterBand::SetMetadataItem(
216 : "NBITS",
217 42 : CPLString().Printf("%d",
218 21 : poDSIn->psFileInfo->pBands[nBand - 1].nBits),
219 : "IMAGE_STRUCTURE");
220 :
221 295 : GDALRasterBand::SetDescription(
222 295 : poDSIn->psFileInfo->pBands[nBand - 1].szDesc);
223 295 : }
224 :
225 : /************************************************************************/
226 : /* ~ECWRasterBand() */
227 : /************************************************************************/
228 :
229 590 : ECWRasterBand::~ECWRasterBand()
230 : {
231 295 : GDALRasterBand::FlushCache(true);
232 :
233 379 : while (!apoOverviews.empty())
234 : {
235 84 : delete apoOverviews.back();
236 84 : apoOverviews.pop_back();
237 : }
238 590 : }
239 :
240 : /************************************************************************/
241 : /* GetOverview() */
242 : /************************************************************************/
243 :
244 2 : GDALRasterBand *ECWRasterBand::GetOverview(int iOverviewIn)
245 :
246 : {
247 2 : if (iOverviewIn >= 0 && iOverviewIn < (int)apoOverviews.size())
248 2 : return apoOverviews[iOverviewIn];
249 : else
250 0 : return nullptr;
251 : }
252 :
253 : /************************************************************************/
254 : /* GetColorInterpretation() */
255 : /************************************************************************/
256 :
257 87 : GDALColorInterp ECWRasterBand::GetColorInterpretation()
258 :
259 : {
260 87 : return eBandInterp;
261 : }
262 :
263 : /************************************************************************/
264 : /* SetColorInterpretation() */
265 : /* */
266 : /* This would normally just be used by folks using the ECW code */
267 : /* to read JP2 streams in other formats (such as NITF) and */
268 : /* providing their own color interpretation regardless of what */
269 : /* ECW might think the stream itself says. */
270 : /************************************************************************/
271 :
272 13 : CPLErr ECWRasterBand::SetColorInterpretation(GDALColorInterp eNewInterp)
273 :
274 : {
275 13 : eBandInterp = eNewInterp;
276 :
277 13 : return CE_None;
278 : }
279 :
280 : /************************************************************************/
281 : /* AdviseRead() */
282 : /************************************************************************/
283 :
284 0 : CPLErr ECWRasterBand::AdviseRead(int nXOff, int nYOff, int nXSize, int nYSize,
285 : int nBufXSize, int nBufYSize, GDALDataType eDT,
286 : char **papszOptions)
287 : {
288 0 : const int nResFactor = 1 << (iOverview + 1);
289 :
290 0 : return poGDS->AdviseRead(nXOff * nResFactor, nYOff * nResFactor,
291 : nXSize * nResFactor, nYSize * nResFactor,
292 : nBufXSize, nBufYSize, eDT, 1, &nBand,
293 0 : papszOptions);
294 : }
295 :
296 : // statistics support:
297 : #if ECWSDK_VERSION >= 50
298 :
299 : /************************************************************************/
300 : /* GetDefaultHistogram() */
301 : /************************************************************************/
302 :
303 : CPLErr ECWRasterBand::GetDefaultHistogram(double *pdfMin, double *pdfMax,
304 : int *pnBuckets,
305 : GUIntBig **ppanHistogram, int bForce,
306 : GDALProgressFunc f,
307 : void *pProgressData)
308 : {
309 : int bForceCoalesced = bForce;
310 : // If file version is smaller than 3, there will be no statistics in the
311 : // file. But if it is version 3 or higher we don't want underlying
312 : // implementation to compute histogram so we set bForceCoalesced to FALSE.
313 : if (poGDS->psFileInfo->nFormatVersion >= 3)
314 : {
315 : bForceCoalesced = FALSE;
316 : }
317 : // We check if we have PAM histogram. If we have them we return them. This
318 : // will allow to override statistics stored in the file.
319 : CPLErr pamError = GDALPamRasterBand::GetDefaultHistogram(
320 : pdfMin, pdfMax, pnBuckets, ppanHistogram, bForceCoalesced, f,
321 : pProgressData);
322 : if (pamError == CE_None || poGDS->psFileInfo->nFormatVersion < 3 ||
323 : eBandInterp == GCI_AlphaBand)
324 : {
325 : return pamError;
326 : }
327 :
328 : NCS::CError error = poGDS->StatisticsEnsureInitialized();
329 : if (!error.Success())
330 : {
331 : CPLError(CE_Warning, CPLE_AppDefined,
332 : "ECWRDataset::StatisticsEnsureInitialized failed in "
333 : "ECWRasterBand::GetDefaultHistogram. ");
334 : return CE_Failure;
335 : }
336 : GetBandIndexAndCountForStatistics(nStatsBandIndex, nStatsBandCount);
337 : bool bHistogramFromFile = false;
338 :
339 : if (poGDS->pStatistics != nullptr)
340 : {
341 : NCSBandStats &bandStats =
342 : poGDS->pStatistics->BandsStats[nStatsBandIndex];
343 : if (bandStats.Histogram != nullptr && bandStats.nHistBucketCount > 0)
344 : {
345 : *pnBuckets = bandStats.nHistBucketCount;
346 : *ppanHistogram = static_cast<GUIntBig *>(
347 : VSIMalloc(bandStats.nHistBucketCount * sizeof(GUIntBig)));
348 : for (size_t i = 0; i < bandStats.nHistBucketCount; i++)
349 : {
350 : (*ppanHistogram)[i] =
351 : static_cast<GUIntBig>(bandStats.Histogram[i]);
352 : }
353 : // JTO: this is not perfect as You can't tell who wrote the
354 : // histogram !!! It will offset it unnecessarily for files with
355 : // hists not modified by GDAL.
356 : const double dfHalfBucket =
357 : (bandStats.fMaxHist - bandStats.fMinHist) /
358 : (2 * (*pnBuckets - 1));
359 : if (pdfMin != nullptr)
360 : {
361 : *pdfMin = bandStats.fMinHist - dfHalfBucket;
362 : }
363 : if (pdfMax != nullptr)
364 : {
365 : *pdfMax = bandStats.fMaxHist + dfHalfBucket;
366 : }
367 : bHistogramFromFile = true;
368 : }
369 : }
370 :
371 : if (!bHistogramFromFile)
372 : {
373 : if (bForce == TRUE)
374 : {
375 : // compute. Save.
376 : pamError = GDALPamRasterBand::GetDefaultHistogram(
377 : pdfMin, pdfMax, pnBuckets, ppanHistogram, TRUE, f,
378 : pProgressData);
379 : if (pamError == CE_None)
380 : {
381 : const CPLErr error2 = SetDefaultHistogram(
382 : *pdfMin, *pdfMax, *pnBuckets, *ppanHistogram);
383 : if (error2 != CE_None)
384 : {
385 : // Histogram is there but we failed to save it back to file.
386 : CPLError(CE_Warning, CPLE_AppDefined,
387 : "SetDefaultHistogram failed in "
388 : "ECWRasterBand::GetDefaultHistogram. Histogram "
389 : "might not be saved in .ecw file.");
390 : }
391 : return CE_None;
392 : }
393 : return pamError;
394 : }
395 : // No histogram, no forced computation.
396 : return CE_Warning;
397 : }
398 : // Statistics were already there and were used.
399 : return CE_None;
400 : }
401 :
402 : /************************************************************************/
403 : /* SetDefaultHistogram() */
404 : /************************************************************************/
405 :
406 : CPLErr ECWRasterBand::SetDefaultHistogram(double dfMin, double dfMax,
407 : int nBuckets, GUIntBig *panHistogram)
408 : {
409 : // Only version 3 supports saving statistics.
410 : if (poGDS->psFileInfo->nFormatVersion < 3 || eBandInterp == GCI_AlphaBand)
411 : {
412 : return GDALPamRasterBand::SetDefaultHistogram(dfMin, dfMax, nBuckets,
413 : panHistogram);
414 : }
415 :
416 : // determine if there are statistics in PAM file.
417 : double dummy;
418 : int dummy_i;
419 : GUIntBig *dummy_histogram = nullptr;
420 : bool hasPAMDefaultHistogram =
421 : GDALPamRasterBand::GetDefaultHistogram(&dummy, &dummy, &dummy_i,
422 : &dummy_histogram, FALSE, nullptr,
423 : nullptr) == CE_None;
424 : if (hasPAMDefaultHistogram)
425 : {
426 : VSIFree(dummy_histogram);
427 : }
428 :
429 : // ECW SDK ignores statistics for opacity bands. So we need to compute
430 : // number of bands without opacity.
431 : GetBandIndexAndCountForStatistics(nStatsBandIndex, nStatsBandCount);
432 : UINT32 bucketCounts[256];
433 : std::fill_n(bucketCounts, nStatsBandCount, 0);
434 : bucketCounts[nStatsBandIndex] = nBuckets;
435 :
436 : NCS::CError error = poGDS->StatisticsEnsureInitialized();
437 : if (!error.Success())
438 : {
439 : CPLError(CE_Warning, CPLE_AppDefined,
440 : "ECWRDataset::StatisticsEnsureInitialized failed in "
441 : "ECWRasterBand::SetDefaultHistogram. Default histogram will "
442 : "be written to PAM. ");
443 : return GDALPamRasterBand::SetDefaultHistogram(dfMin, dfMax, nBuckets,
444 : panHistogram);
445 : }
446 :
447 : NCSFileStatistics *pStatistics = poGDS->pStatistics;
448 :
449 : if (pStatistics == nullptr)
450 : {
451 : error =
452 : NCSEcwInitStatistics(&pStatistics, nStatsBandCount, bucketCounts);
453 : poGDS->bStatisticsDirty = TRUE;
454 : poGDS->pStatistics = pStatistics;
455 : if (!error.Success())
456 : {
457 : CPLError(CE_Warning, CPLE_AppDefined,
458 : "NCSEcwInitStatistics failed in "
459 : "ECWRasterBand::SetDefaultHistogram.");
460 : return GDALPamRasterBand::SetDefaultHistogram(
461 : dfMin, dfMax, nBuckets, panHistogram);
462 : }
463 : // no error statistics properly initialized but there were no statistics
464 : // previously.
465 : }
466 : else
467 : {
468 : // is there a room for our band already?
469 : // This should account for following cases:
470 : // 1. Existing histogram (for this or different band) has smaller bucket
471 : // count.
472 : // 2. There is no existing histogram but statistics are set for one or
473 : // more bands (pStatistics->nHistBucketCounts is zero).
474 : if ((int)pStatistics->BandsStats[nStatsBandIndex].nHistBucketCount !=
475 : nBuckets)
476 : {
477 : // no. There is no room. We need more!
478 : NCSFileStatistics *pNewStatistics = nullptr;
479 : for (size_t i = 0; i < pStatistics->nNumberOfBands; i++)
480 : {
481 : bucketCounts[i] = pStatistics->BandsStats[i].nHistBucketCount;
482 : }
483 : bucketCounts[nStatsBandIndex] = nBuckets;
484 : if (nBuckets <
485 : static_cast<int>(
486 : pStatistics->BandsStats[nStatsBandIndex].nHistBucketCount))
487 : {
488 : pStatistics->BandsStats[nStatsBandIndex].nHistBucketCount =
489 : nBuckets;
490 : }
491 : error = NCSEcwInitStatistics(&pNewStatistics, nStatsBandCount,
492 : bucketCounts);
493 : if (!error.Success())
494 : {
495 : CPLError(CE_Warning, CPLE_AppDefined,
496 : "NCSEcwInitStatistics failed in "
497 : "ECWRasterBand::SetDefaultHistogram (reallocate).");
498 : return GDALPamRasterBand::SetDefaultHistogram(
499 : dfMin, dfMax, nBuckets, panHistogram);
500 : }
501 : // we need to copy existing statistics.
502 : error = NCSEcwCopyStatistics(&pNewStatistics, pStatistics);
503 : if (!error.Success())
504 : {
505 : CPLError(CE_Warning, CPLE_AppDefined,
506 : "NCSEcwCopyStatistics failed in "
507 : "ECWRasterBand::SetDefaultHistogram.");
508 : NCSEcwFreeStatistics(pNewStatistics);
509 : return GDALPamRasterBand::SetDefaultHistogram(
510 : dfMin, dfMax, nBuckets, panHistogram);
511 : }
512 : pNewStatistics->nNumberOfBands = nStatsBandCount;
513 : NCSEcwFreeStatistics(pStatistics);
514 : pStatistics = pNewStatistics;
515 : poGDS->pStatistics = pStatistics;
516 : poGDS->bStatisticsDirty = TRUE;
517 : }
518 : }
519 :
520 : // at this point we have allocated statistics structure.
521 : double dfHalfBucket = (dfMax - dfMin) / (2 * nBuckets);
522 : pStatistics->BandsStats[nStatsBandIndex].fMinHist =
523 : static_cast<IEEE4>(dfMin + dfHalfBucket);
524 : pStatistics->BandsStats[nStatsBandIndex].fMaxHist =
525 : static_cast<IEEE4>(dfMax - dfHalfBucket);
526 : for (int i = 0; i < nBuckets; i++)
527 : {
528 : pStatistics->BandsStats[nStatsBandIndex].Histogram[i] =
529 : static_cast<UINT64>(panHistogram[i]);
530 : }
531 :
532 : if (hasPAMDefaultHistogram)
533 : {
534 : CPLError(CE_Debug, CPLE_AppDefined,
535 : "PAM default histogram will be overwritten.");
536 : return GDALPamRasterBand::SetDefaultHistogram(dfMin, dfMax, nBuckets,
537 : panHistogram);
538 : }
539 : return CE_None;
540 : }
541 :
542 : /************************************************************************/
543 : /* GetBandIndexAndCountForStatistics() */
544 : /************************************************************************/
545 :
546 : void ECWRasterBand::GetBandIndexAndCountForStatistics(int &bandIndex,
547 : int &bandCount) const
548 : {
549 : bandIndex = nBand - 1;
550 : bandCount = poGDS->nBands;
551 : for (int i = 0; i < poGDS->nBands; i++)
552 : {
553 : if (poDS->GetRasterBand(i + 1)->GetColorInterpretation() ==
554 : GCI_AlphaBand)
555 : {
556 : bandCount--;
557 : if (i < nBand - 1)
558 : {
559 : bandIndex--;
560 : }
561 : }
562 : }
563 : }
564 :
565 : /************************************************************************/
566 : /* GetMinimum() */
567 : /************************************************************************/
568 :
569 : double ECWRasterBand::GetMinimum(int *pbSuccess)
570 : {
571 : if (poGDS->psFileInfo->nFormatVersion >= 3)
572 : {
573 : NCS::CError error = poGDS->StatisticsEnsureInitialized();
574 : if (error.Success())
575 : {
576 : GetBandIndexAndCountForStatistics(nStatsBandIndex, nStatsBandCount);
577 : if (poGDS->pStatistics != nullptr)
578 : {
579 : NCSBandStats &bandStats =
580 : poGDS->pStatistics->BandsStats[nStatsBandIndex];
581 : if (!std::isnan(bandStats.fMinVal))
582 : {
583 : if (pbSuccess)
584 : *pbSuccess = TRUE;
585 : return bandStats.fMinVal;
586 : }
587 : }
588 : }
589 : }
590 : return GDALPamRasterBand::GetMinimum(pbSuccess);
591 : }
592 :
593 : /************************************************************************/
594 : /* GetMaximum() */
595 : /************************************************************************/
596 :
597 : double ECWRasterBand::GetMaximum(int *pbSuccess)
598 : {
599 : if (poGDS->psFileInfo->nFormatVersion >= 3)
600 : {
601 : NCS::CError error = poGDS->StatisticsEnsureInitialized();
602 : if (error.Success())
603 : {
604 : GetBandIndexAndCountForStatistics(nStatsBandIndex, nStatsBandCount);
605 : if (poGDS->pStatistics != nullptr)
606 : {
607 : NCSBandStats &bandStats =
608 : poGDS->pStatistics->BandsStats[nStatsBandIndex];
609 : if (!std::isnan(bandStats.fMaxVal))
610 : {
611 : if (pbSuccess)
612 : *pbSuccess = TRUE;
613 : return bandStats.fMaxVal;
614 : }
615 : }
616 : }
617 : }
618 : return GDALPamRasterBand::GetMaximum(pbSuccess);
619 : }
620 :
621 : /************************************************************************/
622 : /* SetMetadataItem() */
623 : /************************************************************************/
624 :
625 : CPLErr ECWRasterBand::SetMetadataItem(const char *pszName, const char *pszValue,
626 : const char *pszDomain)
627 : {
628 : if (EQUAL(pszName, "STATISTICS_VALID_PERCENT"))
629 : return CE_None;
630 : return GDALPamRasterBand::SetMetadataItem(pszName, pszValue, pszDomain);
631 : }
632 :
633 : /************************************************************************/
634 : /* GetStatistics() */
635 : /************************************************************************/
636 :
637 : CPLErr ECWRasterBand::GetStatistics(int bApproxOK, int bForce, double *pdfMin,
638 : double *pdfMax, double *pdfMean,
639 : double *padfStdDev)
640 : {
641 : int bForceCoalesced = bForce;
642 : // If file version is smaller than 3, there will be no statistics in the
643 : // file. But if it is version 3 or higher we don't want underlying
644 : // implementation to compute histogram so we set bForceCoalesced to FALSE.
645 : if (poGDS->psFileInfo->nFormatVersion >= 3)
646 : {
647 : bForceCoalesced = FALSE;
648 : }
649 : // We check if we have PAM histogram. If we have them we return them. This
650 : // will allow to override statistics stored in the file.
651 : CPLErr pamError = GDALPamRasterBand::GetStatistics(
652 : bApproxOK, bForceCoalesced, pdfMin, pdfMax, pdfMean, padfStdDev);
653 : if (pamError == CE_None || poGDS->psFileInfo->nFormatVersion < 3 ||
654 : eBandInterp == GCI_AlphaBand)
655 : {
656 : return pamError;
657 : }
658 :
659 : NCS::CError error = poGDS->StatisticsEnsureInitialized();
660 : if (!error.Success())
661 : {
662 : CPLError(CE_Failure, CPLE_AppDefined,
663 : "ECWRDataset::StatisticsEnsureInitialized failed in "
664 : "ECWRasterBand::GetStatistic. ");
665 : return CE_Failure;
666 : }
667 : GetBandIndexAndCountForStatistics(nStatsBandIndex, nStatsBandCount);
668 : bool bStatisticsFromFile = false;
669 :
670 : if (poGDS->pStatistics != nullptr)
671 : {
672 : bStatisticsFromFile = true;
673 : NCSBandStats &bandStats =
674 : poGDS->pStatistics->BandsStats[nStatsBandIndex];
675 : if (pdfMin != nullptr && !std::isnan(bandStats.fMinVal))
676 : {
677 : *pdfMin = bandStats.fMinVal;
678 : }
679 : else
680 : {
681 : bStatisticsFromFile = false;
682 : }
683 : if (pdfMax != nullptr && !std::isnan(bandStats.fMaxVal))
684 : {
685 : *pdfMax = bandStats.fMaxVal;
686 : }
687 : else
688 : {
689 : bStatisticsFromFile = false;
690 : }
691 : if (pdfMean != nullptr && !std::isnan(bandStats.fMeanVal))
692 : {
693 : *pdfMean = bandStats.fMeanVal;
694 : }
695 : else
696 : {
697 : bStatisticsFromFile = false;
698 : }
699 : if (padfStdDev != nullptr && !std::isnan(bandStats.fStandardDev))
700 : {
701 : *padfStdDev = bandStats.fStandardDev;
702 : }
703 : else
704 : {
705 : bStatisticsFromFile = false;
706 : }
707 : if (bStatisticsFromFile)
708 : return CE_None;
709 : }
710 : // no required statistics.
711 : if (!bStatisticsFromFile && bForce == TRUE)
712 : {
713 : double dfMin, dfMax, dfMean, dfStdDev;
714 : pamError = GDALPamRasterBand::GetStatistics(bApproxOK, TRUE, &dfMin,
715 : &dfMax, &dfMean, &dfStdDev);
716 : if (pdfMin != nullptr)
717 : {
718 : *pdfMin = dfMin;
719 : }
720 : if (pdfMax != nullptr)
721 : {
722 : *pdfMax = dfMax;
723 : }
724 : if (pdfMean != nullptr)
725 : {
726 : *pdfMean = dfMean;
727 : }
728 : if (padfStdDev != nullptr)
729 : {
730 : *padfStdDev = dfStdDev;
731 : }
732 : if (pamError == CE_None)
733 : {
734 : const CPLErr err = SetStatistics(dfMin, dfMax, dfMean, dfStdDev);
735 : if (err != CE_None)
736 : {
737 : CPLError(
738 : CE_Warning, CPLE_AppDefined,
739 : "SetStatistics failed in ECWRasterBand::GetStatistics. "
740 : "Statistics might not be saved in .ecw file.");
741 : }
742 : return CE_None;
743 : }
744 : // whatever happened we return.
745 : return pamError;
746 : }
747 : // no statistics and we are not forced to return.
748 : return CE_Warning;
749 : }
750 :
751 : /************************************************************************/
752 : /* SetStatistics() */
753 : /************************************************************************/
754 :
755 : CPLErr ECWRasterBand::SetStatistics(double dfMin, double dfMax, double dfMean,
756 : double dfStdDev)
757 : {
758 : if (poGDS->psFileInfo->nFormatVersion < 3 || eBandInterp == GCI_AlphaBand)
759 : {
760 : return GDALPamRasterBand::SetStatistics(dfMin, dfMax, dfMean, dfStdDev);
761 : }
762 : double dummy;
763 : bool hasPAMStatistics =
764 : GDALPamRasterBand::GetStatistics(TRUE, FALSE, &dummy, &dummy, &dummy,
765 : &dummy) == CE_None;
766 :
767 : NCS::CError error = poGDS->StatisticsEnsureInitialized();
768 : if (!error.Success())
769 : {
770 : CPLError(
771 : CE_Warning, CPLE_AppDefined,
772 : "ECWRDataset::StatisticsEnsureInitialized failed in "
773 : "ECWRasterBand::SetStatistic. Statistics will be written to PAM. ");
774 : return GDALPamRasterBand::SetStatistics(dfMin, dfMax, dfMean, dfStdDev);
775 : }
776 : GetBandIndexAndCountForStatistics(nStatsBandIndex, nStatsBandCount);
777 : if (poGDS->pStatistics == nullptr)
778 : {
779 : error =
780 : NCSEcwInitStatistics(&poGDS->pStatistics, nStatsBandCount, nullptr);
781 : if (!error.Success())
782 : {
783 : CPLError(
784 : CE_Warning, CPLE_AppDefined,
785 : "NCSEcwInitStatistics failed in ECWRasterBand::SetStatistic. "
786 : "Statistics will be written to PAM.");
787 : return GDALPamRasterBand::SetStatistics(dfMin, dfMax, dfMean,
788 : dfStdDev);
789 : }
790 : }
791 :
792 : poGDS->pStatistics->BandsStats[nStatsBandIndex].fMinVal =
793 : static_cast<IEEE4>(dfMin);
794 : poGDS->pStatistics->BandsStats[nStatsBandIndex].fMaxVal =
795 : static_cast<IEEE4>(dfMax);
796 : poGDS->pStatistics->BandsStats[nStatsBandIndex].fMeanVal =
797 : static_cast<IEEE4>(dfMean);
798 : poGDS->pStatistics->BandsStats[nStatsBandIndex].fStandardDev =
799 : static_cast<IEEE4>(dfStdDev);
800 : poGDS->bStatisticsDirty = TRUE;
801 : // if we have PAM statistics we need to save them as well. Better option
802 : // would be to remove them from PAM file but I don't know how to do that
803 : // without messing in PAM internals.
804 : if (hasPAMStatistics)
805 : {
806 : CPLError(CE_Debug, CPLE_AppDefined,
807 : "PAM statistics will be overwritten.");
808 : return GDALPamRasterBand::SetStatistics(dfMin, dfMax, dfMean, dfStdDev);
809 : }
810 :
811 : return CE_None;
812 : }
813 : #endif
814 :
815 : // #if !defined(SDK_CAN_DO_SUPERSAMPLING)
816 : /************************************************************************/
817 : /* OldIRasterIO() */
818 : /************************************************************************/
819 :
820 : /* This implementation of IRasterIO(), derived from the one of GDAL 1.9 */
821 : /* and older versions, is meant at making over-sampling */
822 : /* work with ECW SDK 3.3. Newer versions of the SDK can do super-sampling in
823 : * their */
824 : /* SetView() call. */
825 :
826 1 : CPLErr ECWRasterBand::OldIRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
827 : int nXSize, int nYSize, void *pData,
828 : int nBufXSize, int nBufYSize,
829 : GDALDataType eBufType, GSpacing nPixelSpace,
830 : GSpacing nLineSpace,
831 : GDALRasterIOExtraArg *psExtraArg)
832 :
833 : {
834 : int iBand;
835 1 : GByte *pabyWorkBuffer = nullptr;
836 1 : const int nResFactor = 1 << (iOverview + 1);
837 :
838 1 : nXOff *= nResFactor;
839 1 : nYOff *= nResFactor;
840 1 : nXSize *= nResFactor;
841 1 : nYSize *= nResFactor;
842 :
843 : /* -------------------------------------------------------------------- */
844 : /* Try to do it based on existing "advised" access. */
845 : /* -------------------------------------------------------------------- */
846 2 : int nRet = poGDS->TryWinRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
847 : static_cast<GByte *>(pData), nBufXSize,
848 1 : nBufYSize, eBufType, 1, &nBand,
849 : nPixelSpace, nLineSpace, 0, psExtraArg);
850 1 : if (nRet == TRUE)
851 0 : return CE_None;
852 1 : if (nRet < 0)
853 0 : return CE_Failure;
854 :
855 : /* -------------------------------------------------------------------- */
856 : /* The ECW SDK doesn't supersample, so adjust for this case. */
857 : /* -------------------------------------------------------------------- */
858 :
859 1 : int nNewXSize = nBufXSize;
860 1 : int nNewYSize = nBufYSize;
861 :
862 1 : if (nXSize < nBufXSize)
863 1 : nNewXSize = nXSize;
864 :
865 1 : if (nYSize < nBufYSize)
866 1 : nNewYSize = nYSize;
867 :
868 : /* -------------------------------------------------------------------- */
869 : /* Can we perform direct loads, or must we load into a working */
870 : /* buffer, and transform? */
871 : /* -------------------------------------------------------------------- */
872 1 : const int nRawPixelSize = GDALGetDataTypeSize(poGDS->eRasterDataType) / 8;
873 :
874 1 : int bDirect = nPixelSpace == 1 && eBufType == GDT_Byte &&
875 2 : nNewXSize == nBufXSize && nNewYSize == nBufYSize;
876 1 : if (!bDirect)
877 : pabyWorkBuffer =
878 1 : static_cast<GByte *>(CPLMalloc(nNewXSize * nRawPixelSize));
879 :
880 : /* -------------------------------------------------------------------- */
881 : /* Establish access at the desired resolution. */
882 : /* -------------------------------------------------------------------- */
883 1 : poGDS->CleanupWindow();
884 :
885 1 : iBand = nBand - 1;
886 1 : poGDS->nBandIndexToPromoteTo8Bit = (bPromoteTo8Bit) ? 0 : -1;
887 : // TODO: Fix writable strings issue.
888 1 : CNCSError oErr = poGDS->poFileView->SetView(
889 : 1, reinterpret_cast<unsigned int *>(&iBand), nXOff, nYOff,
890 2 : nXOff + nXSize - 1, nYOff + nYSize - 1, nNewXSize, nNewYSize);
891 1 : if (oErr.GetErrorNumber() != NCS_SUCCESS)
892 : {
893 0 : CPLFree(pabyWorkBuffer);
894 0 : ECWReportError(oErr);
895 :
896 0 : return CE_Failure;
897 : }
898 :
899 : /* -------------------------------------------------------------------- */
900 : /* Read back one scanline at a time, till request is satisfied. */
901 : /* Supersampling is not supported by the ECW API, so we will do */
902 : /* it ourselves. */
903 : /* -------------------------------------------------------------------- */
904 1 : double dfSrcYInc = static_cast<double>(nNewYSize) / nBufYSize;
905 1 : double dfSrcXInc = static_cast<double>(nNewXSize) / nBufXSize;
906 : int iSrcLine, iDstLine;
907 1 : CPLErr eErr = CE_None;
908 :
909 801 : for (iSrcLine = 0, iDstLine = 0; iDstLine < nBufYSize; iDstLine++)
910 : {
911 : NCSEcwReadStatus eRStatus;
912 800 : GPtrDiff_t iDstLineOff = iDstLine * (GPtrDiff_t)nLineSpace;
913 : unsigned char *pabySrcBuf;
914 :
915 800 : if (bDirect)
916 0 : pabySrcBuf = ((GByte *)pData) + iDstLineOff;
917 : else
918 800 : pabySrcBuf = pabyWorkBuffer;
919 :
920 800 : if (nNewYSize == nBufYSize || iSrcLine == (int)(iDstLine * dfSrcYInc))
921 : {
922 800 : eRStatus = poGDS->poFileView->ReadLineBIL(
923 400 : poGDS->eNCSRequestDataType, 1, (void **)&pabySrcBuf);
924 :
925 400 : if (eRStatus != NCSECW_READ_OK)
926 : {
927 0 : CPLDebug("ECW", "ReadLineBIL status=%d", (int)eRStatus);
928 0 : CPLError(CE_Failure, CPLE_AppDefined,
929 : "NCScbmReadViewLineBIL failed.");
930 0 : eErr = CE_Failure;
931 0 : break;
932 : }
933 :
934 400 : if (bPromoteTo8Bit)
935 : {
936 0 : for (int iX = 0; iX < nNewXSize; iX++)
937 : {
938 0 : pabySrcBuf[iX] *= 255;
939 : }
940 : }
941 :
942 400 : if (!bDirect)
943 : {
944 400 : if (nNewXSize == nBufXSize)
945 : {
946 0 : GDALCopyWords(pabyWorkBuffer, poGDS->eRasterDataType,
947 : nRawPixelSize,
948 0 : ((GByte *)pData) + iDstLine * nLineSpace,
949 : eBufType, (int)nPixelSpace, nBufXSize);
950 : }
951 : else
952 : {
953 : int iPixel;
954 :
955 320400 : for (iPixel = 0; iPixel < nBufXSize; iPixel++)
956 : {
957 320000 : GDALCopyWords(
958 320000 : pabyWorkBuffer +
959 320000 : nRawPixelSize * ((int)(iPixel * dfSrcXInc)),
960 320000 : poGDS->eRasterDataType, nRawPixelSize,
961 320000 : (GByte *)pData + iDstLineOff + iPixel * nPixelSpace,
962 : eBufType, (int)nPixelSpace, 1);
963 : }
964 : }
965 : }
966 :
967 400 : iSrcLine++;
968 : }
969 : else
970 : {
971 : // Just copy the previous line in this case
972 400 : GDALCopyWords((GByte *)pData + (iDstLineOff - nLineSpace), eBufType,
973 400 : (int)nPixelSpace, (GByte *)pData + iDstLineOff,
974 : eBufType, (int)nPixelSpace, nBufXSize);
975 : }
976 :
977 800 : if (psExtraArg->pfnProgress != nullptr &&
978 0 : !psExtraArg->pfnProgress(1.0 * (iDstLine + 1) / nBufYSize, "",
979 : psExtraArg->pProgressData))
980 : {
981 0 : eErr = CE_Failure;
982 0 : break;
983 : }
984 : }
985 :
986 1 : CPLFree(pabyWorkBuffer);
987 :
988 1 : return eErr;
989 : }
990 :
991 : // #endif !defined(SDK_CAN_DO_SUPERSAMPLING)
992 :
993 : /************************************************************************/
994 : /* IRasterIO() */
995 : /************************************************************************/
996 :
997 1664 : CPLErr ECWRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
998 : int nXSize, int nYSize, void *pData,
999 : int nBufXSize, int nBufYSize,
1000 : GDALDataType eBufType, GSpacing nPixelSpace,
1001 : GSpacing nLineSpace,
1002 : GDALRasterIOExtraArg *psExtraArg)
1003 : {
1004 1664 : if (eRWFlag == GF_Write)
1005 0 : return CE_Failure;
1006 :
1007 : /* -------------------------------------------------------------------- */
1008 : /* Default line and pixel spacing if needed. */
1009 : /* -------------------------------------------------------------------- */
1010 1664 : if (nPixelSpace == 0)
1011 0 : nPixelSpace = GDALGetDataTypeSize(eBufType) / 8;
1012 :
1013 1664 : if (nLineSpace == 0)
1014 0 : nLineSpace = nPixelSpace * nBufXSize;
1015 :
1016 1664 : CPLDebug("ECWRasterBand",
1017 : "RasterIO(nBand=%d,iOverview=%d,nXOff=%d,nYOff=%d,nXSize=%d,"
1018 : "nYSize=%d -> %dx%d)",
1019 : nBand, iOverview, nXOff, nYOff, nXSize, nYSize, nBufXSize,
1020 : nBufYSize);
1021 :
1022 : #if !defined(SDK_CAN_DO_SUPERSAMPLING)
1023 1664 : if (poGDS->bUseOldBandRasterIOImplementation)
1024 : {
1025 1 : return OldIRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
1026 : nBufXSize, nBufYSize, eBufType, nPixelSpace,
1027 1 : nLineSpace, psExtraArg);
1028 : }
1029 :
1030 : #endif
1031 :
1032 1663 : int nResFactor = 1 << (iOverview + 1);
1033 :
1034 : GDALRasterIOExtraArg sExtraArgTmp;
1035 1663 : INIT_RASTERIO_EXTRA_ARG(sExtraArgTmp);
1036 1663 : CPL_IGNORE_RET_VAL(sExtraArgTmp.eResampleAlg);
1037 1663 : sExtraArgTmp.eResampleAlg = psExtraArg->eResampleAlg;
1038 1663 : sExtraArgTmp.pfnProgress = psExtraArg->pfnProgress;
1039 1663 : sExtraArgTmp.pProgressData = psExtraArg->pProgressData;
1040 :
1041 3301 : return poGDS->IRasterIO(
1042 : eRWFlag, nXOff * nResFactor, nYOff * nResFactor,
1043 1663 : (nXSize == nRasterXSize) ? poGDS->nRasterXSize : nXSize * nResFactor,
1044 25 : (nYSize == nRasterYSize) ? poGDS->nRasterYSize : nYSize * nResFactor,
1045 : pData, nBufXSize, nBufYSize, eBufType, 1, &nBand, nPixelSpace,
1046 3326 : nLineSpace, nLineSpace * nBufYSize, &sExtraArgTmp);
1047 : }
1048 :
1049 : /************************************************************************/
1050 : /* IReadBlock() */
1051 : /************************************************************************/
1052 :
1053 1 : CPLErr ECWRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
1054 :
1055 : {
1056 1 : int nXOff = nBlockXOff * nBlockXSize, nYOff = nBlockYOff * nBlockYSize,
1057 1 : nXSize = nBlockXSize, nYSize = nBlockYSize;
1058 :
1059 1 : if (nXOff + nXSize > nRasterXSize)
1060 0 : nXSize = nRasterXSize - nXOff;
1061 1 : if (nYOff + nYSize > nRasterYSize)
1062 0 : nYSize = nRasterYSize - nYOff;
1063 :
1064 1 : int nPixelSpace = GDALGetDataTypeSize(eDataType) / 8;
1065 1 : int nLineSpace = nPixelSpace * nBlockXSize;
1066 :
1067 : GDALRasterIOExtraArg sExtraArg;
1068 1 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
1069 :
1070 1 : return IRasterIO(GF_Read, nXOff, nYOff, nXSize, nYSize, pImage, nXSize,
1071 2 : nYSize, eDataType, nPixelSpace, nLineSpace, &sExtraArg);
1072 : }
1073 :
1074 : /************************************************************************/
1075 : /* ==================================================================== */
1076 : /* ECWDataset */
1077 : /* ==================================================================== */
1078 : /************************************************************************/
1079 :
1080 : /************************************************************************/
1081 : /* ECWDataset() */
1082 : /************************************************************************/
1083 :
1084 121 : ECWDataset::ECWDataset(int bIsJPEG2000In)
1085 :
1086 : {
1087 121 : this->bIsJPEG2000 = bIsJPEG2000In;
1088 121 : bUsingCustomStream = FALSE;
1089 121 : poFileView = nullptr;
1090 121 : bWinActive = FALSE;
1091 121 : panWinBandList = nullptr;
1092 121 : eRasterDataType = GDT_Byte;
1093 121 : papszGMLMetadata = nullptr;
1094 :
1095 121 : bHdrDirty = FALSE;
1096 121 : bGeoTransformChanged = FALSE;
1097 121 : bProjectionChanged = FALSE;
1098 121 : bProjCodeChanged = FALSE;
1099 121 : bDatumCodeChanged = FALSE;
1100 121 : bUnitsCodeChanged = FALSE;
1101 :
1102 121 : bUseOldBandRasterIOImplementation = FALSE;
1103 : #if ECWSDK_VERSION >= 50
1104 :
1105 : pStatistics = nullptr;
1106 : bStatisticsDirty = FALSE;
1107 : bStatisticsInitialized = FALSE;
1108 : bFileMetaDataDirty = FALSE;
1109 :
1110 : #endif
1111 :
1112 121 : sCachedMultiBandIO.bEnabled = FALSE;
1113 121 : sCachedMultiBandIO.nBandsTried = 0;
1114 121 : sCachedMultiBandIO.nXOff = 0;
1115 121 : sCachedMultiBandIO.nYOff = 0;
1116 121 : sCachedMultiBandIO.nXSize = 0;
1117 121 : sCachedMultiBandIO.nYSize = 0;
1118 121 : sCachedMultiBandIO.nBufXSize = 0;
1119 121 : sCachedMultiBandIO.nBufYSize = 0;
1120 121 : sCachedMultiBandIO.eBufType = GDT_Unknown;
1121 121 : sCachedMultiBandIO.pabyData = nullptr;
1122 :
1123 121 : bPreventCopyingSomeMetadata = FALSE;
1124 :
1125 121 : nBandIndexToPromoteTo8Bit = -1;
1126 :
1127 121 : poDriver =
1128 121 : (GDALDriver *)GDALGetDriverByName(bIsJPEG2000 ? "JP2ECW" : "ECW");
1129 :
1130 121 : psFileInfo = nullptr;
1131 121 : eNCSRequestDataType = NCSCT_UINT8;
1132 121 : nWinXOff = 0;
1133 121 : nWinYOff = 0;
1134 121 : nWinXSize = 0;
1135 121 : nWinYSize = 0;
1136 121 : nWinBufXSize = 0;
1137 121 : nWinBufYSize = 0;
1138 121 : nWinBandCount = 0;
1139 121 : nWinBufLoaded = FALSE;
1140 121 : papCurLineBuf = nullptr;
1141 :
1142 121 : m_nAdviseReadXOff = -1;
1143 121 : m_nAdviseReadYOff = -1;
1144 121 : m_nAdviseReadXSize = -1;
1145 121 : m_nAdviseReadYSize = -1;
1146 121 : m_nAdviseReadBufXSize = -1;
1147 121 : m_nAdviseReadBufYSize = -1;
1148 121 : m_nAdviseReadBandCount = -1;
1149 121 : m_panAdviseReadBandList = nullptr;
1150 121 : }
1151 :
1152 : /************************************************************************/
1153 : /* ~ECWDataset() */
1154 : /************************************************************************/
1155 :
1156 242 : ECWDataset::~ECWDataset()
1157 :
1158 : {
1159 121 : GDALPamDataset::FlushCache(true);
1160 121 : CleanupWindow();
1161 :
1162 : #if ECWSDK_VERSION >= 50
1163 : NCSFileMetaData *pFileMetaDataCopy = nullptr;
1164 : if (bFileMetaDataDirty)
1165 : {
1166 : NCSCopyMetaData(&pFileMetaDataCopy, psFileInfo->pFileMetaData);
1167 : }
1168 : #endif
1169 :
1170 : /* -------------------------------------------------------------------- */
1171 : /* Release / dereference iostream. */
1172 : /* -------------------------------------------------------------------- */
1173 : // The underlying iostream of the CNCSJP2FileView (poFileView) object may
1174 : // also be the underlying iostream of other CNCSJP2FileView (poFileView)
1175 : // objects. Consequently, when we delete the CNCSJP2FileView (poFileView)
1176 : // object, we must decrement the nFileViewCount attribute of the underlying
1177 : // VSIIOStream object, and only delete the VSIIOStream object when
1178 : // nFileViewCount is equal to zero.
1179 :
1180 242 : CPLMutexHolder oHolder(&hECWDatasetMutex);
1181 :
1182 121 : if (poFileView != nullptr)
1183 : {
1184 : #if ECWSDK_VERSION >= 55
1185 : delete poFileView;
1186 : #else
1187 121 : VSIIOStream *poUnderlyingIOStream = (VSIIOStream *)nullptr;
1188 :
1189 121 : if (bUsingCustomStream)
1190 : {
1191 52 : poUnderlyingIOStream = ((VSIIOStream *)(poFileView->GetStream()));
1192 : }
1193 121 : delete poFileView;
1194 :
1195 121 : if (bUsingCustomStream)
1196 : {
1197 52 : if (--poUnderlyingIOStream->nFileViewCount == 0)
1198 52 : delete poUnderlyingIOStream;
1199 : }
1200 : #endif
1201 121 : poFileView = nullptr;
1202 : }
1203 :
1204 : /* WriteHeader() must be called after closing the file handle to work */
1205 : /* on Windows */
1206 121 : if (bHdrDirty)
1207 3 : WriteHeader();
1208 : #if ECWSDK_VERSION >= 50
1209 : if (bStatisticsDirty)
1210 : {
1211 : StatisticsWrite();
1212 : }
1213 : CleanupStatistics();
1214 :
1215 : if (bFileMetaDataDirty)
1216 : {
1217 : WriteFileMetaData(pFileMetaDataCopy);
1218 : NCSFreeMetaData(pFileMetaDataCopy);
1219 : }
1220 : #endif
1221 :
1222 121 : CSLDestroy(papszGMLMetadata);
1223 :
1224 121 : CPLFree(sCachedMultiBandIO.pabyData);
1225 :
1226 121 : CPLFree(m_panAdviseReadBandList);
1227 242 : }
1228 :
1229 : #if ECWSDK_VERSION >= 50
1230 :
1231 : /************************************************************************/
1232 : /* StatisticsEnsureInitialized() */
1233 : /************************************************************************/
1234 :
1235 : NCS::CError ECWDataset::StatisticsEnsureInitialized()
1236 : {
1237 : if (bStatisticsInitialized == TRUE)
1238 : {
1239 : return NCS_SUCCESS;
1240 : }
1241 :
1242 : NCS::CError error = poFileView->GetClientStatistics(&pStatistics);
1243 : if (error.Success())
1244 : {
1245 : bStatisticsInitialized = TRUE;
1246 : }
1247 : return error;
1248 : }
1249 :
1250 : /************************************************************************/
1251 : /* StatisticsWrite() */
1252 : /************************************************************************/
1253 :
1254 : NCS::CError ECWDataset::StatisticsWrite()
1255 : {
1256 : CPLDebug("ECW", "In StatisticsWrite()");
1257 : NCSFileView *view = NCSEcwEditOpen(GetDescription());
1258 : NCS::CError error;
1259 : if (view != nullptr)
1260 : {
1261 : error = NCSEcwEditSetStatistics(view, pStatistics);
1262 : if (error.Success())
1263 : {
1264 : error = NCSEcwEditFlushAll(view);
1265 : if (error.Success())
1266 : {
1267 : error = NCSEcwEditClose(view);
1268 : }
1269 : }
1270 : }
1271 :
1272 : bStatisticsDirty = FALSE;
1273 :
1274 : return error;
1275 : }
1276 :
1277 : /************************************************************************/
1278 : /* CleanupStatistics() */
1279 : /************************************************************************/
1280 :
1281 : void ECWDataset::CleanupStatistics()
1282 : {
1283 : if (bStatisticsInitialized == TRUE && pStatistics != nullptr)
1284 : {
1285 : NCSEcwFreeStatistics(pStatistics);
1286 : }
1287 : }
1288 :
1289 : #endif // #if ECWSDK_VERSION>=50
1290 :
1291 : /************************************************************************/
1292 : /* SetGeoTransform() */
1293 : /************************************************************************/
1294 :
1295 6 : CPLErr ECWDataset::SetGeoTransform(double *padfGeoTransform)
1296 : {
1297 6 : if (bIsJPEG2000 || eAccess == GA_ReadOnly)
1298 5 : return GDALPamDataset::SetGeoTransform(padfGeoTransform);
1299 :
1300 1 : if (!bGeoTransformValid || adfGeoTransform[0] != padfGeoTransform[0] ||
1301 0 : adfGeoTransform[1] != padfGeoTransform[1] ||
1302 0 : adfGeoTransform[2] != padfGeoTransform[2] ||
1303 0 : adfGeoTransform[3] != padfGeoTransform[3] ||
1304 0 : adfGeoTransform[4] != padfGeoTransform[4] ||
1305 0 : adfGeoTransform[5] != padfGeoTransform[5])
1306 : {
1307 1 : memcpy(adfGeoTransform, padfGeoTransform, 6 * sizeof(double));
1308 1 : bGeoTransformValid = TRUE;
1309 1 : bHdrDirty = TRUE;
1310 1 : bGeoTransformChanged = TRUE;
1311 : }
1312 :
1313 1 : return CE_None;
1314 : }
1315 :
1316 : /************************************************************************/
1317 : /* SetSpatialRef() */
1318 : /************************************************************************/
1319 :
1320 6 : CPLErr ECWDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
1321 : {
1322 6 : if (bIsJPEG2000 || eAccess == GA_ReadOnly)
1323 5 : return GDALPamDataset::SetSpatialRef(poSRS);
1324 :
1325 2 : if (!((m_oSRS.IsEmpty() && poSRS == nullptr) ||
1326 1 : (!m_oSRS.IsEmpty() && poSRS != nullptr && m_oSRS.IsSame(poSRS))))
1327 : {
1328 1 : m_oSRS.Clear();
1329 1 : if (poSRS)
1330 1 : m_oSRS = *poSRS;
1331 :
1332 1 : bHdrDirty = TRUE;
1333 1 : bProjectionChanged = TRUE;
1334 : }
1335 :
1336 1 : return CE_None;
1337 : }
1338 :
1339 : /************************************************************************/
1340 : /* SetMetadataItem() */
1341 : /************************************************************************/
1342 :
1343 4 : CPLErr ECWDataset::SetMetadataItem(const char *pszName, const char *pszValue,
1344 : const char *pszDomain)
1345 : {
1346 4 : if (!bIsJPEG2000 && eAccess == GA_Update &&
1347 3 : (pszDomain == nullptr || EQUAL(pszDomain, "") ||
1348 3 : (pszDomain != nullptr && EQUAL(pszDomain, "ECW"))) &&
1349 3 : pszName != nullptr &&
1350 3 : (strcmp(pszName, "PROJ") == 0 || strcmp(pszName, "DATUM") == 0 ||
1351 1 : strcmp(pszName, "UNITS") == 0))
1352 : {
1353 3 : CPLString osNewVal = pszValue ? pszValue : "";
1354 3 : if (osNewVal.size() > 31)
1355 0 : osNewVal.resize(31);
1356 3 : if (strcmp(pszName, "PROJ") == 0)
1357 : {
1358 1 : bProjCodeChanged = (osNewVal != m_osProjCode);
1359 1 : m_osProjCode = std::move(osNewVal);
1360 1 : bHdrDirty |= bProjCodeChanged;
1361 : }
1362 2 : else if (strcmp(pszName, "DATUM") == 0)
1363 : {
1364 1 : bDatumCodeChanged |= (osNewVal != m_osDatumCode) ? TRUE : FALSE;
1365 1 : m_osDatumCode = std::move(osNewVal);
1366 1 : bHdrDirty |= bDatumCodeChanged;
1367 : }
1368 : else
1369 : {
1370 1 : bUnitsCodeChanged |= (osNewVal != m_osUnitsCode) ? TRUE : FALSE;
1371 1 : m_osUnitsCode = std::move(osNewVal);
1372 1 : bHdrDirty |= bUnitsCodeChanged;
1373 : }
1374 3 : return CE_None;
1375 : }
1376 : #if ECWSDK_VERSION >= 50
1377 : else if (psFileInfo != nullptr && psFileInfo->nFormatVersion >= 3 &&
1378 : eAccess == GA_Update &&
1379 : (pszDomain == nullptr || EQUAL(pszDomain, "")) &&
1380 : pszName != nullptr && STARTS_WITH(pszName, "FILE_METADATA_"))
1381 : {
1382 : bFileMetaDataDirty = TRUE;
1383 :
1384 : if (psFileInfo->pFileMetaData == nullptr)
1385 : NCSInitMetaData(&(psFileInfo->pFileMetaData));
1386 :
1387 : if (strcmp(pszName, "FILE_METADATA_CLASSIFICATION") == 0)
1388 : {
1389 : NCSFree(psFileInfo->pFileMetaData->sClassification);
1390 : psFileInfo->pFileMetaData->sClassification =
1391 : pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
1392 : return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1393 : }
1394 : else if (strcmp(pszName, "FILE_METADATA_ACQUISITION_DATE") == 0)
1395 : {
1396 : NCSFree(psFileInfo->pFileMetaData->sAcquisitionDate);
1397 : psFileInfo->pFileMetaData->sAcquisitionDate =
1398 : pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
1399 : return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1400 : }
1401 : else if (strcmp(pszName, "FILE_METADATA_ACQUISITION_SENSOR_NAME") == 0)
1402 : {
1403 : NCSFree(psFileInfo->pFileMetaData->sAcquisitionSensorName);
1404 : psFileInfo->pFileMetaData->sAcquisitionSensorName =
1405 : pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
1406 : return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1407 : }
1408 : else if (strcmp(pszName, "FILE_METADATA_COMPRESSION_SOFTWARE") == 0)
1409 : {
1410 : NCSFree(psFileInfo->pFileMetaData->sCompressionSoftware);
1411 : psFileInfo->pFileMetaData->sCompressionSoftware =
1412 : pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
1413 : return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1414 : }
1415 : else if (strcmp(pszName, "FILE_METADATA_AUTHOR") == 0)
1416 : {
1417 : NCSFree(psFileInfo->pFileMetaData->sAuthor);
1418 : psFileInfo->pFileMetaData->sAuthor =
1419 : pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
1420 : return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1421 : }
1422 : else if (strcmp(pszName, "FILE_METADATA_COPYRIGHT") == 0)
1423 : {
1424 : NCSFree(psFileInfo->pFileMetaData->sCopyright);
1425 : psFileInfo->pFileMetaData->sCopyright =
1426 : pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
1427 : return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1428 : }
1429 : else if (strcmp(pszName, "FILE_METADATA_COMPANY") == 0)
1430 : {
1431 : NCSFree(psFileInfo->pFileMetaData->sCompany);
1432 : psFileInfo->pFileMetaData->sCompany =
1433 : pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
1434 : return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1435 : }
1436 : else if (strcmp(pszName, "FILE_METADATA_EMAIL") == 0)
1437 : {
1438 : NCSFree(psFileInfo->pFileMetaData->sEmail);
1439 : psFileInfo->pFileMetaData->sEmail =
1440 : pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
1441 : return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1442 : }
1443 : else if (strcmp(pszName, "FILE_METADATA_ADDRESS") == 0)
1444 : {
1445 : NCSFree(psFileInfo->pFileMetaData->sAddress);
1446 : psFileInfo->pFileMetaData->sAddress =
1447 : pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
1448 : return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1449 : }
1450 : else if (strcmp(pszName, "FILE_METADATA_TELEPHONE") == 0)
1451 : {
1452 : NCSFree(psFileInfo->pFileMetaData->sTelephone);
1453 : psFileInfo->pFileMetaData->sTelephone =
1454 : pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
1455 : return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1456 : }
1457 : else
1458 : {
1459 : return GDALPamDataset::SetMetadataItem(pszName, pszValue,
1460 : pszDomain);
1461 : }
1462 : }
1463 : #endif
1464 : else
1465 1 : return GDALPamDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1466 : }
1467 :
1468 : /************************************************************************/
1469 : /* SetMetadata() */
1470 : /************************************************************************/
1471 :
1472 2 : CPLErr ECWDataset::SetMetadata(char **papszMetadata, const char *pszDomain)
1473 : {
1474 : /* The bPreventCopyingSomeMetadata is set by ECWCreateCopy() */
1475 : /* just before calling poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT ); */
1476 2 : if (bPreventCopyingSomeMetadata &&
1477 1 : (pszDomain == nullptr || EQUAL(pszDomain, "")))
1478 : {
1479 1 : char **papszMetadataDup = nullptr;
1480 1 : char **papszIter = papszMetadata;
1481 2 : while (*papszIter)
1482 : {
1483 1 : char *pszKey = nullptr;
1484 1 : CPLParseNameValue(*papszIter, &pszKey);
1485 : /* Remove a few metadata item from the source that we don't want in
1486 : */
1487 : /* the target metadata */
1488 1 : if (pszKey != nullptr &&
1489 1 : (EQUAL(pszKey, "VERSION") ||
1490 1 : EQUAL(pszKey, "COMPRESSION_RATE_TARGET") ||
1491 1 : EQUAL(pszKey, "COMPRESSION_RATE_ACTUAL") ||
1492 1 : EQUAL(pszKey, "CLOCKWISE_ROTATION_DEG") ||
1493 1 : EQUAL(pszKey, "COLORSPACE") ||
1494 1 : EQUAL(pszKey, "COMPRESSION_DATE") ||
1495 1 : STARTS_WITH_CI(pszKey, "FILE_METADATA_")))
1496 : {
1497 : /* do nothing */
1498 : }
1499 : else
1500 : {
1501 1 : papszMetadataDup = CSLAddString(papszMetadataDup, *papszIter);
1502 : }
1503 1 : CPLFree(pszKey);
1504 1 : papszIter++;
1505 : }
1506 :
1507 1 : bPreventCopyingSomeMetadata = FALSE;
1508 1 : CPLErr eErr = SetMetadata(papszMetadataDup, pszDomain);
1509 1 : bPreventCopyingSomeMetadata = TRUE;
1510 1 : CSLDestroy(papszMetadataDup);
1511 1 : return eErr;
1512 : }
1513 :
1514 1 : if (((pszDomain == nullptr || EQUAL(pszDomain, "") ||
1515 2 : EQUAL(pszDomain, "ECW")) &&
1516 1 : (CSLFetchNameValue(papszMetadata, "PROJ") != nullptr ||
1517 1 : CSLFetchNameValue(papszMetadata, "DATUM") != nullptr ||
1518 1 : CSLFetchNameValue(papszMetadata, "UNITS") != nullptr))
1519 : #if ECWSDK_VERSION >= 50
1520 : ||
1521 : (psFileInfo != nullptr && psFileInfo->nFormatVersion >= 3 &&
1522 : eAccess == GA_Update &&
1523 : (pszDomain == nullptr || EQUAL(pszDomain, "")) &&
1524 : (CSLFetchNameValue(papszMetadata, "FILE_METADATA_CLASSIFICATION") !=
1525 : nullptr ||
1526 : CSLFetchNameValue(papszMetadata, "FILE_METADATA_ACQUISITION_DATE") !=
1527 : nullptr ||
1528 : CSLFetchNameValue(papszMetadata,
1529 : "FILE_METADATA_ACQUISITION_SENSOR_NAME") !=
1530 : nullptr ||
1531 : CSLFetchNameValue(papszMetadata,
1532 : "FILE_METADATA_COMPRESSION_SOFTWARE") != nullptr ||
1533 : CSLFetchNameValue(papszMetadata, "FILE_METADATA_AUTHOR") != nullptr ||
1534 : CSLFetchNameValue(papszMetadata, "FILE_METADATA_COPYRIGHT") !=
1535 : nullptr ||
1536 : CSLFetchNameValue(papszMetadata, "FILE_METADATA_COMPANY") !=
1537 : nullptr ||
1538 : CSLFetchNameValue(papszMetadata, "FILE_METADATA_EMAIL") != nullptr ||
1539 : CSLFetchNameValue(papszMetadata, "FILE_METADATA_ADDRESS") !=
1540 : nullptr ||
1541 : CSLFetchNameValue(papszMetadata, "FILE_METADATA_TELEPHONE") !=
1542 : nullptr))
1543 : #endif
1544 : )
1545 : {
1546 0 : CPLStringList osNewMetadata;
1547 0 : char **papszIter = papszMetadata;
1548 0 : while (papszIter && *papszIter)
1549 : {
1550 0 : if (STARTS_WITH(*papszIter, "PROJ=") ||
1551 0 : STARTS_WITH(*papszIter, "DATUM=") ||
1552 0 : STARTS_WITH(*papszIter, "UNITS=") ||
1553 0 : (STARTS_WITH(*papszIter, "FILE_METADATA_") &&
1554 0 : strchr(*papszIter, '=') != nullptr))
1555 : {
1556 0 : char *pszKey = nullptr;
1557 0 : const char *pszValue = CPLParseNameValue(*papszIter, &pszKey);
1558 0 : SetMetadataItem(pszKey, pszValue, pszDomain);
1559 0 : CPLFree(pszKey);
1560 : }
1561 : else
1562 0 : osNewMetadata.AddString(*papszIter);
1563 0 : papszIter++;
1564 : }
1565 0 : if (!osNewMetadata.empty())
1566 0 : return GDALPamDataset::SetMetadata(osNewMetadata.List(), pszDomain);
1567 : else
1568 0 : return CE_None;
1569 : }
1570 : else
1571 1 : return GDALPamDataset::SetMetadata(papszMetadata, pszDomain);
1572 : }
1573 :
1574 : /************************************************************************/
1575 : /* WriteHeader() */
1576 : /************************************************************************/
1577 :
1578 3 : void ECWDataset::WriteHeader()
1579 : {
1580 3 : if (!bHdrDirty)
1581 0 : return;
1582 :
1583 3 : CPLAssert(eAccess == GA_Update);
1584 3 : CPLAssert(!bIsJPEG2000);
1585 :
1586 3 : bHdrDirty = FALSE;
1587 :
1588 3 : NCSEcwEditInfo *psEditInfo = nullptr;
1589 : NCSError eErr;
1590 :
1591 : /* Load original header info */
1592 : #if ECWSDK_VERSION < 50
1593 3 : eErr = NCSEcwEditReadInfo((char *)GetDescription(), &psEditInfo);
1594 : #else
1595 : eErr = NCSEcwEditReadInfo(
1596 : NCS::CString::Utf8Decode(GetDescription()).c_str(), &psEditInfo);
1597 : #endif
1598 3 : if (eErr != NCS_SUCCESS)
1599 : {
1600 0 : CPLError(CE_Failure, CPLE_AppDefined, "NCSEcwEditReadInfo() failed");
1601 0 : return;
1602 : }
1603 :
1604 : /* To avoid potential cross-heap issues, we keep the original */
1605 : /* strings, and restore them before freeing the structure */
1606 3 : char *pszOriginalCode = psEditInfo->szDatum;
1607 3 : char *pszOriginalProj = psEditInfo->szProjection;
1608 :
1609 : /* Alter the structure with user modified information */
1610 : char szProjCode[32], szDatumCode[32], szUnits[32];
1611 3 : if (bProjectionChanged)
1612 : {
1613 1 : if (ECWTranslateFromWKT(&m_oSRS, szProjCode, sizeof(szProjCode),
1614 1 : szDatumCode, sizeof(szDatumCode), szUnits))
1615 : {
1616 1 : psEditInfo->szDatum = szDatumCode;
1617 1 : psEditInfo->szProjection = szProjCode;
1618 1 : psEditInfo->eCellSizeUnits = ECWTranslateToCellSizeUnits(szUnits);
1619 1 : CPLDebug("ECW", "Rewrite DATUM : %s", psEditInfo->szDatum);
1620 1 : CPLDebug("ECW", "Rewrite PROJ : %s", psEditInfo->szProjection);
1621 1 : CPLDebug("ECW", "Rewrite UNITS : %s",
1622 : ECWTranslateFromCellSizeUnits(psEditInfo->eCellSizeUnits));
1623 : }
1624 : }
1625 :
1626 3 : if (bDatumCodeChanged)
1627 : {
1628 1 : psEditInfo->szDatum =
1629 1 : (char *)((m_osDatumCode.size()) ? m_osDatumCode.c_str() : "RAW");
1630 1 : CPLDebug("ECW", "Rewrite DATUM : %s", psEditInfo->szDatum);
1631 : }
1632 3 : if (bProjCodeChanged)
1633 : {
1634 1 : psEditInfo->szProjection =
1635 1 : (char *)((m_osProjCode.size()) ? m_osProjCode.c_str() : "RAW");
1636 1 : CPLDebug("ECW", "Rewrite PROJ : %s", psEditInfo->szProjection);
1637 : }
1638 3 : if (bUnitsCodeChanged)
1639 : {
1640 2 : psEditInfo->eCellSizeUnits =
1641 1 : ECWTranslateToCellSizeUnits(m_osUnitsCode.c_str());
1642 1 : CPLDebug("ECW", "Rewrite UNITS : %s",
1643 : ECWTranslateFromCellSizeUnits(psEditInfo->eCellSizeUnits));
1644 : }
1645 :
1646 3 : if (bGeoTransformChanged)
1647 : {
1648 1 : psEditInfo->fOriginX = adfGeoTransform[0];
1649 1 : psEditInfo->fCellIncrementX = adfGeoTransform[1];
1650 1 : psEditInfo->fOriginY = adfGeoTransform[3];
1651 1 : psEditInfo->fCellIncrementY = adfGeoTransform[5];
1652 1 : CPLDebug("ECW", "Rewrite Geotransform");
1653 : }
1654 :
1655 : /* Write modified header info */
1656 : #if ECWSDK_VERSION < 50
1657 3 : eErr = NCSEcwEditWriteInfo((char *)GetDescription(), psEditInfo, nullptr,
1658 : nullptr, nullptr);
1659 : #else
1660 : eErr =
1661 : NCSEcwEditWriteInfo(NCS::CString::Utf8Decode(GetDescription()).c_str(),
1662 : psEditInfo, nullptr, nullptr, nullptr);
1663 : #endif
1664 3 : if (eErr != NCS_SUCCESS)
1665 : {
1666 0 : CPLError(CE_Failure, CPLE_AppDefined, "NCSEcwEditWriteInfo() failed");
1667 : }
1668 :
1669 : /* Restore original pointers before free'ing */
1670 3 : psEditInfo->szDatum = pszOriginalCode;
1671 3 : psEditInfo->szProjection = pszOriginalProj;
1672 :
1673 3 : NCSEcwEditFreeInfo(psEditInfo);
1674 : }
1675 :
1676 : /************************************************************************/
1677 : /* AdviseRead() */
1678 : /************************************************************************/
1679 :
1680 249 : CPLErr ECWDataset::AdviseRead(int nXOff, int nYOff, int nXSize, int nYSize,
1681 : int nBufXSize, int nBufYSize,
1682 : CPL_UNUSED GDALDataType eDT, int nBandCount,
1683 : int *panBandList, CPL_UNUSED char **papszOptions)
1684 : {
1685 249 : CPLDebug("ECW", "ECWDataset::AdviseRead(%d,%d,%d,%d->%d,%d)", nXOff, nYOff,
1686 : nXSize, nYSize, nBufXSize, nBufYSize);
1687 :
1688 : #if !defined(SDK_CAN_DO_SUPERSAMPLING)
1689 249 : if (nBufXSize > nXSize || nBufYSize > nYSize)
1690 : {
1691 0 : CPLError(CE_Warning, CPLE_AppDefined,
1692 : "Supersampling not directly supported by ECW toolkit,\n"
1693 : "ignoring AdviseRead() request.");
1694 0 : return CE_Warning;
1695 : }
1696 : #endif
1697 :
1698 : /* -------------------------------------------------------------------- */
1699 : /* Do some validation of parameters. */
1700 : /* -------------------------------------------------------------------- */
1701 :
1702 : CPLErr eErr;
1703 249 : int bStopProcessing = FALSE;
1704 249 : eErr = ValidateRasterIOOrAdviseReadParameters(
1705 : "AdviseRead()", &bStopProcessing, nXOff, nYOff, nXSize, nYSize,
1706 : nBufXSize, nBufYSize, nBandCount, panBandList);
1707 249 : if (eErr != CE_None || bStopProcessing)
1708 0 : return eErr;
1709 :
1710 249 : if (nBandCount > 100)
1711 : {
1712 0 : ReportError(CE_Failure, CPLE_IllegalArg,
1713 : "AdviseRead(): Too many bands : %d", nBandCount);
1714 0 : return CE_Failure;
1715 : }
1716 :
1717 249 : if (nBufXSize != nXSize || nBufYSize != nYSize)
1718 : {
1719 : // This early exit is because experimentally we found that
1720 : // performance of requesting at 50% is much slower with
1721 : // AdviseRead()...
1722 : // At least on JPEG2000 images with SDK 3.3
1723 200 : CPLDebug("ECW",
1724 : "Ignoring AdviseRead() for non full resolution request");
1725 200 : return CE_None;
1726 : }
1727 :
1728 : // We don't setup the reading window right away, in case the actual read
1729 : // pattern wouldn't be compatible of it. Which might be the case for
1730 : // example if AdviseRead() requests a full image, but we don't read by
1731 : // chunks of the full width of one or several lines
1732 49 : m_nAdviseReadXOff = nXOff;
1733 49 : m_nAdviseReadYOff = nYOff;
1734 49 : m_nAdviseReadXSize = nXSize;
1735 49 : m_nAdviseReadYSize = nYSize;
1736 49 : m_nAdviseReadBufXSize = nBufXSize;
1737 49 : m_nAdviseReadBufYSize = nBufYSize;
1738 49 : m_nAdviseReadBandCount = nBandCount;
1739 49 : CPLFree(m_panAdviseReadBandList);
1740 49 : if (panBandList)
1741 : {
1742 41 : m_panAdviseReadBandList =
1743 41 : static_cast<int *>(CPLMalloc(sizeof(int) * nBandCount));
1744 41 : memcpy(m_panAdviseReadBandList, panBandList, sizeof(int) * nBandCount);
1745 : }
1746 : else
1747 : {
1748 8 : m_panAdviseReadBandList = nullptr;
1749 : }
1750 :
1751 49 : return CE_None;
1752 : }
1753 :
1754 : /************************************************************************/
1755 : /* RunDeferredAdviseRead() */
1756 : /************************************************************************/
1757 :
1758 44 : CPLErr ECWDataset::RunDeferredAdviseRead()
1759 : {
1760 44 : CPLAssert(m_nAdviseReadXOff >= 0);
1761 :
1762 44 : const int nXOff = m_nAdviseReadXOff;
1763 44 : const int nYOff = m_nAdviseReadYOff;
1764 44 : const int nXSize = m_nAdviseReadXSize;
1765 44 : const int nYSize = m_nAdviseReadYSize;
1766 44 : const int nBufXSize = m_nAdviseReadBufXSize;
1767 44 : const int nBufYSize = m_nAdviseReadBufYSize;
1768 44 : const int nBandCount = m_nAdviseReadBandCount;
1769 44 : int *panBandList = m_panAdviseReadBandList;
1770 :
1771 44 : m_nAdviseReadXOff = -1;
1772 44 : m_nAdviseReadYOff = -1;
1773 44 : m_nAdviseReadXSize = -1;
1774 44 : m_nAdviseReadYSize = -1;
1775 44 : m_nAdviseReadBufXSize = -1;
1776 44 : m_nAdviseReadBufYSize = -1;
1777 44 : m_nAdviseReadBandCount = -1;
1778 44 : m_panAdviseReadBandList = nullptr;
1779 :
1780 : /* -------------------------------------------------------------------- */
1781 : /* Adjust band numbers to be zero based. */
1782 : /* -------------------------------------------------------------------- */
1783 44 : int *panAdjustedBandList = (int *)CPLMalloc(sizeof(int) * nBandCount);
1784 44 : nBandIndexToPromoteTo8Bit = -1;
1785 97 : for (int ii = 0; ii < nBandCount; ii++)
1786 : {
1787 53 : panAdjustedBandList[ii] =
1788 53 : (panBandList != nullptr) ? panBandList[ii] - 1 : ii;
1789 53 : if (((ECWRasterBand *)GetRasterBand(panAdjustedBandList[ii] + 1))
1790 53 : ->bPromoteTo8Bit)
1791 1 : nBandIndexToPromoteTo8Bit = ii;
1792 : }
1793 :
1794 : /* -------------------------------------------------------------------- */
1795 : /* Cleanup old window cache information. */
1796 : /* -------------------------------------------------------------------- */
1797 44 : CleanupWindow();
1798 :
1799 : /* -------------------------------------------------------------------- */
1800 : /* Set the new requested window. */
1801 : /* -------------------------------------------------------------------- */
1802 44 : CNCSError oErr = poFileView->SetView(
1803 : nBandCount, (UINT32 *)panAdjustedBandList, nXOff, nYOff,
1804 88 : nXOff + nXSize - 1, nYOff + nYSize - 1, nBufXSize, nBufYSize);
1805 :
1806 44 : CPLFree(panAdjustedBandList);
1807 44 : if (oErr.GetErrorNumber() != NCS_SUCCESS)
1808 : {
1809 0 : ECWReportError(oErr);
1810 :
1811 0 : bWinActive = FALSE;
1812 0 : CPLFree(panBandList);
1813 0 : return CE_Failure;
1814 : }
1815 :
1816 44 : bWinActive = TRUE;
1817 :
1818 : /* -------------------------------------------------------------------- */
1819 : /* Record selected window. */
1820 : /* -------------------------------------------------------------------- */
1821 44 : nWinXOff = nXOff;
1822 44 : nWinYOff = nYOff;
1823 44 : nWinXSize = nXSize;
1824 44 : nWinYSize = nYSize;
1825 44 : nWinBufXSize = nBufXSize;
1826 44 : nWinBufYSize = nBufYSize;
1827 :
1828 44 : panWinBandList = (int *)CPLMalloc(sizeof(int) * nBandCount);
1829 44 : if (panBandList != nullptr)
1830 40 : memcpy(panWinBandList, panBandList, sizeof(int) * nBandCount);
1831 : else
1832 : {
1833 17 : for (int ii = 0; ii < nBandCount; ii++)
1834 : {
1835 13 : panWinBandList[ii] = ii + 1;
1836 : }
1837 : }
1838 44 : nWinBandCount = nBandCount;
1839 :
1840 44 : nWinBufLoaded = -1;
1841 :
1842 : /* -------------------------------------------------------------------- */
1843 : /* Allocate current scanline buffer. */
1844 : /* -------------------------------------------------------------------- */
1845 44 : papCurLineBuf = (void **)CPLMalloc(sizeof(void *) * nWinBandCount);
1846 97 : for (int iBand = 0; iBand < nWinBandCount; iBand++)
1847 106 : papCurLineBuf[iBand] =
1848 53 : CPLMalloc(nBufXSize * (GDALGetDataTypeSize(eRasterDataType) / 8));
1849 :
1850 44 : CPLFree(panBandList);
1851 :
1852 44 : return CE_None;
1853 : }
1854 :
1855 : /************************************************************************/
1856 : /* TryWinRasterIO() */
1857 : /* */
1858 : /* Try to satisfy the given request based on the currently */
1859 : /* defined window. Return TRUE on success or FALSE on */
1860 : /* failure. On failure, the caller should satisfy the request */
1861 : /* another way (not report an error). */
1862 : /************************************************************************/
1863 :
1864 1931 : int ECWDataset::TryWinRasterIO(CPL_UNUSED GDALRWFlag eFlag, int nXOff,
1865 : int nYOff, int nXSize, int nYSize,
1866 : GByte *pabyData, int nBufXSize, int nBufYSize,
1867 : GDALDataType eDT, int nBandCount,
1868 : const int *panBandList, GSpacing nPixelSpace,
1869 : GSpacing nLineSpace, GSpacing nBandSpace,
1870 : GDALRasterIOExtraArg *psExtraArg)
1871 : {
1872 : int iBand, i;
1873 :
1874 : /* -------------------------------------------------------------------- */
1875 : /* Provide default buffer organization. */
1876 : /* -------------------------------------------------------------------- */
1877 1931 : if (nPixelSpace == 0)
1878 0 : nPixelSpace = GDALGetDataTypeSize(eDT) / 8;
1879 1931 : if (nLineSpace == 0)
1880 0 : nLineSpace = nPixelSpace * nBufXSize;
1881 1931 : if (nBandSpace == 0)
1882 1 : nBandSpace = nLineSpace * nBufYSize;
1883 :
1884 : /* -------------------------------------------------------------------- */
1885 : /* Do some simple tests to see if the current window can */
1886 : /* satisfy our requirement. */
1887 : /* -------------------------------------------------------------------- */
1888 : #ifdef NOISY_DEBUG
1889 : CPLDebug("ECW", "TryWinRasterIO(%d,%d,%d,%d,%d,%d)", nXOff, nYOff, nXSize,
1890 : nYSize, nBufXSize, nBufYSize);
1891 : #endif
1892 :
1893 1931 : if (!bWinActive)
1894 : {
1895 528 : if (nXOff == m_nAdviseReadXOff && nXSize == m_nAdviseReadXSize &&
1896 44 : nBufXSize == m_nAdviseReadBufXSize)
1897 : {
1898 44 : if (RunDeferredAdviseRead() != CE_None)
1899 0 : return FALSE;
1900 : }
1901 528 : if (!bWinActive)
1902 : {
1903 484 : return FALSE;
1904 : }
1905 : }
1906 :
1907 1447 : if (nXOff != nWinXOff || nXSize != nWinXSize)
1908 0 : return FALSE;
1909 :
1910 1447 : if (nBufXSize != nWinBufXSize)
1911 0 : return FALSE;
1912 :
1913 2907 : for (iBand = 0; iBand < nBandCount; iBand++)
1914 : {
1915 1488 : for (i = 0; i < nWinBandCount; i++)
1916 : {
1917 1486 : if (panWinBandList[i] == panBandList[iBand])
1918 1460 : break;
1919 : }
1920 :
1921 1462 : if (i == nWinBandCount)
1922 2 : return FALSE;
1923 : }
1924 :
1925 1445 : if (nYOff < nWinYOff || nYOff + nYSize > nWinYOff + nWinYSize)
1926 0 : return FALSE;
1927 :
1928 : /* -------------------------------------------------------------------- */
1929 : /* Now we try more subtle tests. */
1930 : /* -------------------------------------------------------------------- */
1931 : {
1932 : static int nDebugCount = 0;
1933 :
1934 1445 : if (nDebugCount < 30)
1935 30 : CPLDebug(
1936 : "ECW",
1937 : "TryWinRasterIO(%d,%d,%d,%d -> %dx%d) - doing advised read.",
1938 : nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize);
1939 :
1940 1445 : if (nDebugCount == 29)
1941 1 : CPLDebug("ECW", "No more TryWinRasterIO messages will be reported");
1942 :
1943 1445 : nDebugCount++;
1944 : }
1945 :
1946 : /* -------------------------------------------------------------------- */
1947 : /* Actually load data one buffer line at a time. */
1948 : /* -------------------------------------------------------------------- */
1949 : int iBufLine;
1950 :
1951 4165 : for (iBufLine = 0; iBufLine < nBufYSize; iBufLine++)
1952 : {
1953 2788 : double fFileLine = ((iBufLine + 0.5) / nBufYSize) * nYSize + nYOff;
1954 2788 : int iWinLine =
1955 2788 : (int)(((fFileLine - nWinYOff) / nWinYSize) * nWinBufYSize);
1956 :
1957 2788 : if (iWinLine == nWinBufLoaded + 1)
1958 2720 : LoadNextLine();
1959 :
1960 2788 : if (iWinLine != nWinBufLoaded)
1961 68 : return FALSE;
1962 :
1963 : /* --------------------------------------------------------------------
1964 : */
1965 : /* Copy out all our target bands. */
1966 : /* --------------------------------------------------------------------
1967 : */
1968 : int iWinBand;
1969 8290 : for (iBand = 0; iBand < nBandCount; iBand++)
1970 : {
1971 10070 : for (iWinBand = 0; iWinBand < nWinBandCount; iWinBand++)
1972 : {
1973 10070 : if (panWinBandList[iWinBand] == panBandList[iBand])
1974 5570 : break;
1975 : }
1976 :
1977 11140 : GDALCopyWords(papCurLineBuf[iWinBand], eRasterDataType,
1978 5570 : GDALGetDataTypeSize(eRasterDataType) / 8,
1979 5570 : pabyData + nBandSpace * iBand + iBufLine * nLineSpace,
1980 : eDT, (int)nPixelSpace, nBufXSize);
1981 : }
1982 :
1983 2870 : if (psExtraArg->pfnProgress != nullptr &&
1984 150 : !psExtraArg->pfnProgress(1.0 * (iBufLine + 1) / nBufYSize, "",
1985 : psExtraArg->pProgressData))
1986 : {
1987 0 : return -1;
1988 : }
1989 : }
1990 :
1991 1377 : return TRUE;
1992 : }
1993 :
1994 : /************************************************************************/
1995 : /* LoadNextLine() */
1996 : /************************************************************************/
1997 :
1998 2720 : CPLErr ECWDataset::LoadNextLine()
1999 :
2000 : {
2001 2720 : if (!bWinActive)
2002 0 : return CE_Failure;
2003 :
2004 2720 : if (nWinBufLoaded == nWinBufYSize - 1)
2005 : {
2006 0 : CleanupWindow();
2007 0 : return CE_Failure;
2008 : }
2009 :
2010 : NCSEcwReadStatus eRStatus;
2011 5440 : eRStatus = poFileView->ReadLineBIL(eNCSRequestDataType,
2012 2720 : (UINT16)nWinBandCount, papCurLineBuf);
2013 2720 : if (eRStatus != NCSECW_READ_OK)
2014 0 : return CE_Failure;
2015 :
2016 2720 : if (nBandIndexToPromoteTo8Bit >= 0)
2017 : {
2018 24450 : for (int iX = 0; iX < nWinBufXSize; iX++)
2019 : {
2020 24300 : ((GByte *)papCurLineBuf[nBandIndexToPromoteTo8Bit])[iX] *= 255;
2021 : }
2022 : }
2023 :
2024 2720 : nWinBufLoaded++;
2025 :
2026 2720 : return CE_None;
2027 : }
2028 :
2029 : /************************************************************************/
2030 : /* CleanupWindow() */
2031 : /************************************************************************/
2032 :
2033 477 : void ECWDataset::CleanupWindow()
2034 :
2035 : {
2036 477 : if (!bWinActive)
2037 433 : return;
2038 :
2039 44 : bWinActive = FALSE;
2040 44 : CPLFree(panWinBandList);
2041 44 : panWinBandList = nullptr;
2042 :
2043 97 : for (int iBand = 0; iBand < nWinBandCount; iBand++)
2044 53 : CPLFree(papCurLineBuf[iBand]);
2045 44 : CPLFree(papCurLineBuf);
2046 44 : papCurLineBuf = nullptr;
2047 : }
2048 :
2049 : /************************************************************************/
2050 : /* IRasterIO() */
2051 : /************************************************************************/
2052 :
2053 1699 : CPLErr ECWDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
2054 : int nXSize, int nYSize, void *pData, int nBufXSize,
2055 : int nBufYSize, GDALDataType eBufType,
2056 : int nBandCount, BANDMAP_TYPE panBandMap,
2057 : GSpacing nPixelSpace, GSpacing nLineSpace,
2058 : GSpacing nBandSpace,
2059 : GDALRasterIOExtraArg *psExtraArg)
2060 :
2061 : {
2062 1699 : if (eRWFlag == GF_Write)
2063 0 : return CE_Failure;
2064 :
2065 1699 : if (nBandCount > 100)
2066 0 : return CE_Failure;
2067 :
2068 1699 : if (bUseOldBandRasterIOImplementation)
2069 : /* Sanity check. Should not happen */
2070 0 : return CE_Failure;
2071 1699 : int nDataTypeSize = (GDALGetDataTypeSize(eRasterDataType) / 8);
2072 :
2073 1699 : if (nPixelSpace == 0)
2074 : {
2075 0 : nPixelSpace = nDataTypeSize;
2076 : }
2077 :
2078 1699 : if (nLineSpace == 0)
2079 : {
2080 0 : nLineSpace = nPixelSpace * nBufXSize;
2081 : }
2082 1699 : if (nBandSpace == 0)
2083 : {
2084 18 : nBandSpace =
2085 18 : static_cast<GSpacing>(nDataTypeSize) * nBufXSize * nBufYSize;
2086 : }
2087 :
2088 : // Use GDAL upsampling if non nearest
2089 1699 : if ((nBufXSize > nXSize || nBufYSize > nYSize) &&
2090 3 : psExtraArg->eResampleAlg != GRIORA_NearestNeighbour)
2091 : {
2092 2 : int nBufDataTypeSize = (GDALGetDataTypeSize(eBufType) / 8);
2093 2 : GByte *pabyTemp = (GByte *)VSI_MALLOC3_VERBOSE(
2094 : nXSize, nYSize, nBufDataTypeSize * nBandCount);
2095 2 : if (pabyTemp == nullptr)
2096 : {
2097 0 : return CE_Failure;
2098 : }
2099 :
2100 : GDALRasterIOExtraArg sExtraArgDefault;
2101 2 : INIT_RASTERIO_EXTRA_ARG(sExtraArgDefault);
2102 2 : sExtraArgDefault.pfnProgress = psExtraArg->pfnProgress;
2103 2 : sExtraArgDefault.pProgressData = psExtraArg->pProgressData;
2104 :
2105 4 : CPLErr eErr = IRasterIO(
2106 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pabyTemp, nXSize, nYSize,
2107 : eBufType, nBandCount, panBandMap, nBufDataTypeSize,
2108 2 : (GIntBig)nBufDataTypeSize * nXSize,
2109 2 : (GIntBig)nBufDataTypeSize * nXSize * nYSize, &sExtraArgDefault);
2110 :
2111 2 : if (eErr == CE_None)
2112 : {
2113 : /* Create a MEM dataset that wraps the input buffer */
2114 : auto poMEMDS = std::unique_ptr<MEMDataset>(
2115 2 : MEMDataset::Create("", nXSize, nYSize, 0, eBufType, nullptr));
2116 :
2117 7 : for (int i = 0; i < nBandCount; i++)
2118 : {
2119 5 : auto hBand = MEMCreateRasterBandEx(
2120 5 : poMEMDS.get(), i + 1,
2121 5 : pabyTemp + static_cast<size_t>(i) * nBufDataTypeSize *
2122 5 : nXSize * nYSize,
2123 : eBufType, 0, 0, false);
2124 5 : poMEMDS->AddMEMBand(hBand);
2125 :
2126 5 : const char *pszNBITS = GetRasterBand(i + 1)->GetMetadataItem(
2127 5 : "NBITS", "IMAGE_STRUCTURE");
2128 5 : if (pszNBITS)
2129 0 : poMEMDS->GetRasterBand(i + 1)->SetMetadataItem(
2130 0 : "NBITS", pszNBITS, "IMAGE_STRUCTURE");
2131 : }
2132 :
2133 : GDALRasterIOExtraArg sExtraArgTmp;
2134 2 : INIT_RASTERIO_EXTRA_ARG(sExtraArgTmp);
2135 2 : CPL_IGNORE_RET_VAL(sExtraArgTmp.eResampleAlg);
2136 2 : sExtraArgTmp.eResampleAlg = psExtraArg->eResampleAlg;
2137 :
2138 2 : CPL_IGNORE_RET_VAL(poMEMDS->RasterIO(
2139 : GF_Read, 0, 0, nXSize, nYSize, pData, nBufXSize, nBufYSize,
2140 : eBufType, nBandCount, nullptr, nPixelSpace, nLineSpace,
2141 : nBandSpace, &sExtraArgTmp));
2142 : }
2143 :
2144 2 : VSIFree(pabyTemp);
2145 :
2146 2 : return eErr;
2147 : }
2148 :
2149 : /* -------------------------------------------------------------------- */
2150 : /* ECW SDK 3.3 has a bug with the ECW format when we query the */
2151 : /* number of bands of the dataset, but not in the "natural order". */
2152 : /* It ignores the content of panBandMap. (#4234) */
2153 : /* -------------------------------------------------------------------- */
2154 : #if ECWSDK_VERSION < 40
2155 1697 : if (!bIsJPEG2000 && nBandCount == nBands)
2156 : {
2157 : int i;
2158 11 : int bDoBandIRasterIO = FALSE;
2159 44 : for (i = 0; i < nBandCount; i++)
2160 : {
2161 33 : if (panBandMap[i] != i + 1)
2162 : {
2163 2 : bDoBandIRasterIO = TRUE;
2164 : }
2165 : }
2166 11 : if (bDoBandIRasterIO)
2167 : {
2168 1 : return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
2169 : pData, nBufXSize, nBufYSize, eBufType,
2170 : nBandCount, panBandMap, nPixelSpace,
2171 1 : nLineSpace, nBandSpace, psExtraArg);
2172 : }
2173 : }
2174 : #endif
2175 :
2176 : /* -------------------------------------------------------------------- */
2177 : /* Check if we can directly return the data in case we have cached */
2178 : /* it from a previous call in a multi-band reading pattern. */
2179 : /* -------------------------------------------------------------------- */
2180 1696 : if (nBandCount == 1 && *panBandMap > 1 && *panBandMap <= nBands &&
2181 67 : sCachedMultiBandIO.nXOff == nXOff &&
2182 67 : sCachedMultiBandIO.nYOff == nYOff &&
2183 30 : sCachedMultiBandIO.nXSize == nXSize &&
2184 22 : sCachedMultiBandIO.nYSize == nYSize &&
2185 22 : sCachedMultiBandIO.nBufXSize == nBufXSize &&
2186 22 : sCachedMultiBandIO.nBufYSize == nBufYSize &&
2187 22 : sCachedMultiBandIO.eBufType == eBufType)
2188 : {
2189 22 : sCachedMultiBandIO.nBandsTried++;
2190 :
2191 22 : if (sCachedMultiBandIO.bEnabled &&
2192 7 : sCachedMultiBandIO.pabyData != nullptr)
2193 : {
2194 : int j;
2195 7 : int nBufTypeSize = GDALGetDataTypeSize(eBufType) / 8;
2196 357 : for (j = 0; j < nBufYSize; j++)
2197 : {
2198 350 : GDALCopyWords(sCachedMultiBandIO.pabyData +
2199 350 : (*panBandMap - 1) * nBufXSize * nBufYSize *
2200 350 : nBufTypeSize +
2201 350 : j * nBufXSize * nBufTypeSize,
2202 : eBufType, nBufTypeSize,
2203 350 : ((GByte *)pData) + j * nLineSpace, eBufType,
2204 : (int)nPixelSpace, nBufXSize);
2205 : }
2206 7 : return CE_None;
2207 : }
2208 :
2209 45 : if (!(sCachedMultiBandIO.bEnabled) &&
2210 22 : sCachedMultiBandIO.nBandsTried == nBands &&
2211 7 : CPLTestBool(CPLGetConfigOption("ECW_CLEVER", "YES")))
2212 : {
2213 7 : sCachedMultiBandIO.bEnabled = TRUE;
2214 7 : CPLDebug(
2215 : "ECW",
2216 : "Detecting successive band reading pattern (for next time)");
2217 : }
2218 : }
2219 :
2220 : /* -------------------------------------------------------------------- */
2221 : /* Try to do it based on existing "advised" access. */
2222 : /* -------------------------------------------------------------------- */
2223 : int nRet =
2224 1689 : TryWinRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, (GByte *)pData,
2225 : nBufXSize, nBufYSize, eBufType, nBandCount, panBandMap,
2226 : nPixelSpace, nLineSpace, nBandSpace, psExtraArg);
2227 1689 : if (nRet == TRUE)
2228 1371 : return CE_None;
2229 318 : else if (nRet < 0)
2230 0 : return CE_Failure;
2231 :
2232 : /* -------------------------------------------------------------------- */
2233 : /* If we are requesting a single line at 1:1, we do a multi-band */
2234 : /* AdviseRead() and then TryWinRasterIO() again. */
2235 : /* */
2236 : /* Except for reading a 1x1 window when reading a scanline might */
2237 : /* be longer. */
2238 : /* -------------------------------------------------------------------- */
2239 318 : if (nXSize == 1 && nYSize == 1 && nBufXSize == 1 && nBufYSize == 1)
2240 : {
2241 : /* do nothing */
2242 : }
2243 :
2244 : #if !defined(SDK_CAN_DO_SUPERSAMPLING)
2245 : /* -------------------------------------------------------------------- */
2246 : /* If we are supersampling we need to fall into the general */
2247 : /* purpose logic. */
2248 : /* -------------------------------------------------------------------- */
2249 314 : else if (nXSize < nBufXSize || nYSize < nBufYSize)
2250 : {
2251 1 : bUseOldBandRasterIOImplementation = TRUE;
2252 1 : CPLErr eErr = GDALDataset::IRasterIO(
2253 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
2254 : eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
2255 : nBandSpace, psExtraArg);
2256 1 : bUseOldBandRasterIOImplementation = FALSE;
2257 1 : return eErr;
2258 : }
2259 : #endif
2260 :
2261 313 : else if (nBufYSize == 1)
2262 : {
2263 : // This is tricky, because it expects the rest of the image
2264 : // with this buffer width to be read. The preferred way to
2265 : // achieve this behavior would be to call AdviseRead before
2266 : // call IRasterIO. The logic could be improved to detect
2267 : // successive pattern of single line reading before doing an
2268 : // AdviseRead.
2269 : CPLErr eErr;
2270 :
2271 241 : eErr = AdviseRead(nXOff, nYOff, nXSize, GetRasterYSize() - nYOff,
2272 241 : nBufXSize, (nRasterYSize - nYOff) / nYSize, eBufType,
2273 : nBandCount, const_cast<int *>(panBandMap), nullptr);
2274 482 : if (eErr == CE_None &&
2275 241 : TryWinRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
2276 : (GByte *)pData, nBufXSize, nBufYSize, eBufType,
2277 : nBandCount, panBandMap, nPixelSpace, nLineSpace,
2278 : nBandSpace, psExtraArg))
2279 6 : return CE_None;
2280 : }
2281 :
2282 311 : CPLDebug("ECW", "RasterIO(%d,%d,%d,%d -> %dx%d) - doing interleaved read.",
2283 : nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize);
2284 :
2285 : /* -------------------------------------------------------------------- */
2286 : /* Setup view. */
2287 : /* -------------------------------------------------------------------- */
2288 : UINT32 anBandIndices[100];
2289 : int i;
2290 : NCSError eNCSErr;
2291 622 : CNCSError oErr(GetCNCSError(NCS_SUCCESS));
2292 :
2293 641 : for (i = 0; i < nBandCount; i++)
2294 330 : anBandIndices[i] = panBandMap[i] - 1;
2295 :
2296 311 : CleanupWindow();
2297 :
2298 : /* -------------------------------------------------------------------- */
2299 : /* Cache data in the context of a multi-band reading pattern. */
2300 : /* -------------------------------------------------------------------- */
2301 311 : if (nBandCount == 1 && *panBandMap == 1 && (nBands == 3 || nBands == 4))
2302 : {
2303 219 : if (sCachedMultiBandIO.bEnabled &&
2304 4 : sCachedMultiBandIO.nBandsTried != nBands)
2305 : {
2306 1 : sCachedMultiBandIO.bEnabled = FALSE;
2307 1 : CPLDebug("ECW", "Disabling successive band reading pattern");
2308 : }
2309 :
2310 219 : sCachedMultiBandIO.nXOff = nXOff;
2311 219 : sCachedMultiBandIO.nYOff = nYOff;
2312 219 : sCachedMultiBandIO.nXSize = nXSize;
2313 219 : sCachedMultiBandIO.nYSize = nYSize;
2314 219 : sCachedMultiBandIO.nBufXSize = nBufXSize;
2315 219 : sCachedMultiBandIO.nBufYSize = nBufYSize;
2316 219 : sCachedMultiBandIO.eBufType = eBufType;
2317 219 : sCachedMultiBandIO.nBandsTried = 1;
2318 :
2319 219 : int nBufTypeSize = GDALGetDataTypeSize(eBufType) / 8;
2320 :
2321 219 : if (sCachedMultiBandIO.bEnabled)
2322 : {
2323 6 : GByte *pNew = (GByte *)VSIRealloc(sCachedMultiBandIO.pabyData,
2324 3 : nBufXSize * nBufYSize * nBands *
2325 : nBufTypeSize);
2326 3 : if (pNew == nullptr)
2327 0 : CPLFree(sCachedMultiBandIO.pabyData);
2328 3 : sCachedMultiBandIO.pabyData = pNew;
2329 : }
2330 :
2331 219 : if (sCachedMultiBandIO.bEnabled &&
2332 3 : sCachedMultiBandIO.pabyData != nullptr)
2333 : {
2334 3 : nBandIndexToPromoteTo8Bit = -1;
2335 12 : for (i = 0; i < nBands; i++)
2336 : {
2337 9 : if (((ECWRasterBand *)GetRasterBand(i + 1))->bPromoteTo8Bit)
2338 0 : nBandIndexToPromoteTo8Bit = i;
2339 9 : anBandIndices[i] = i;
2340 : }
2341 :
2342 3 : oErr = poFileView->SetView(nBands, anBandIndices, nXOff, nYOff,
2343 3 : nXOff + nXSize - 1, nYOff + nYSize - 1,
2344 3 : nBufXSize, nBufYSize);
2345 3 : eNCSErr = oErr.GetErrorNumber();
2346 :
2347 3 : if (eNCSErr != NCS_SUCCESS)
2348 : {
2349 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
2350 : NCSGetErrorText(eNCSErr));
2351 :
2352 0 : return CE_Failure;
2353 : }
2354 :
2355 6 : CPLErr eErr = ReadBands(
2356 3 : sCachedMultiBandIO.pabyData, nBufXSize, nBufYSize, eBufType,
2357 3 : nBands, nBufTypeSize, nBufXSize * nBufTypeSize,
2358 3 : nBufXSize * nBufYSize * nBufTypeSize, psExtraArg);
2359 3 : if (eErr != CE_None)
2360 0 : return eErr;
2361 :
2362 : int j;
2363 153 : for (j = 0; j < nBufYSize; j++)
2364 : {
2365 150 : GDALCopyWords(
2366 150 : sCachedMultiBandIO.pabyData + j * nBufXSize * nBufTypeSize,
2367 150 : eBufType, nBufTypeSize, ((GByte *)pData) + j * nLineSpace,
2368 : eBufType, (int)nPixelSpace, nBufXSize);
2369 : }
2370 3 : return CE_None;
2371 : }
2372 : }
2373 :
2374 308 : nBandIndexToPromoteTo8Bit = -1;
2375 635 : for (i = 0; i < nBandCount; i++)
2376 : {
2377 327 : if (((ECWRasterBand *)GetRasterBand(anBandIndices[i] + 1))
2378 327 : ->bPromoteTo8Bit)
2379 4 : nBandIndexToPromoteTo8Bit = i;
2380 : }
2381 308 : oErr = poFileView->SetView(nBandCount, anBandIndices, nXOff, nYOff,
2382 308 : nXOff + nXSize - 1, nYOff + nYSize - 1,
2383 308 : nBufXSize, nBufYSize);
2384 308 : eNCSErr = oErr.GetErrorNumber();
2385 :
2386 308 : if (eNCSErr != NCS_SUCCESS)
2387 : {
2388 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", NCSGetErrorText(eNCSErr));
2389 :
2390 0 : return CE_Failure;
2391 : }
2392 :
2393 308 : return ReadBands(pData, nBufXSize, nBufYSize, eBufType, nBandCount,
2394 308 : nPixelSpace, nLineSpace, nBandSpace, psExtraArg);
2395 : }
2396 :
2397 : /************************************************************************/
2398 : /* ReadBandsDirectly() */
2399 : /************************************************************************/
2400 :
2401 39 : CPLErr ECWDataset::ReadBandsDirectly(void *pData, int nBufXSize, int nBufYSize,
2402 : CPL_UNUSED GDALDataType eBufType,
2403 : int nBandCount,
2404 : CPL_UNUSED GSpacing nPixelSpace,
2405 : GSpacing nLineSpace, GSpacing nBandSpace,
2406 : GDALRasterIOExtraArg *psExtraArg)
2407 : {
2408 39 : CPLDebug("ECW", "ReadBandsDirectly(-> %dx%d) - reading lines directly.",
2409 : nBufXSize, nBufYSize);
2410 :
2411 39 : UINT8 **pBIL = (UINT8 **)NCSMalloc(nBandCount * sizeof(UINT8 *), FALSE);
2412 :
2413 101 : for (int nB = 0; nB < nBandCount; nB++)
2414 : {
2415 62 : pBIL[nB] = ((UINT8 *)pData) + (nBandSpace * nB); // for any bit depth
2416 : }
2417 :
2418 39 : CPLErr eErr = CE_None;
2419 5574 : for (int nR = 0; nR < nBufYSize; nR++)
2420 : {
2421 11070 : if (poFileView->ReadLineBIL(eNCSRequestDataType, (UINT16)nBandCount,
2422 5535 : (void **)pBIL) != NCSECW_READ_OK)
2423 : {
2424 0 : eErr = CE_Failure;
2425 0 : break;
2426 : }
2427 13722 : for (int nB = 0; nB < nBandCount; nB++)
2428 : {
2429 8187 : if (nB == nBandIndexToPromoteTo8Bit)
2430 : {
2431 48999 : for (int iX = 0; iX < nBufXSize; iX++)
2432 : {
2433 48690 : pBIL[nB][iX] *= 255;
2434 : }
2435 : }
2436 8187 : pBIL[nB] += nLineSpace;
2437 : }
2438 :
2439 5535 : if (psExtraArg->pfnProgress != nullptr &&
2440 0 : !psExtraArg->pfnProgress(1.0 * (nR + 1) / nBufYSize, "",
2441 : psExtraArg->pProgressData))
2442 : {
2443 0 : eErr = CE_Failure;
2444 0 : break;
2445 : }
2446 : }
2447 39 : if (pBIL)
2448 : {
2449 39 : NCSFree(pBIL);
2450 : }
2451 39 : return eErr;
2452 : }
2453 :
2454 : /************************************************************************/
2455 : /* ReadBands() */
2456 : /************************************************************************/
2457 :
2458 311 : CPLErr ECWDataset::ReadBands(void *pData, int nBufXSize, int nBufYSize,
2459 : GDALDataType eBufType, int nBandCount,
2460 : GSpacing nPixelSpace, GSpacing nLineSpace,
2461 : GSpacing nBandSpace,
2462 : GDALRasterIOExtraArg *psExtraArg)
2463 : {
2464 : int i;
2465 : /* -------------------------------------------------------------------- */
2466 : /* Setup working scanline, and the pointers into it. */
2467 : /* -------------------------------------------------------------------- */
2468 311 : int nDataTypeSize = (GDALGetDataTypeSize(eRasterDataType) / 8);
2469 311 : bool bDirect =
2470 45 : (eBufType == eRasterDataType) && nDataTypeSize == nPixelSpace &&
2471 395 : nLineSpace == (nPixelSpace * nBufXSize) &&
2472 : nBandSpace ==
2473 39 : (static_cast<GSpacing>(nDataTypeSize) * nBufXSize * nBufYSize);
2474 311 : if (bDirect)
2475 : {
2476 39 : return ReadBandsDirectly(pData, nBufXSize, nBufYSize, eBufType,
2477 : nBandCount, nPixelSpace, nLineSpace,
2478 39 : nBandSpace, psExtraArg);
2479 : }
2480 272 : CPLDebug("ECW", "ReadBands(-> %dx%d) - reading lines using GDALCopyWords.",
2481 : nBufXSize, nBufYSize);
2482 272 : CPLErr eErr = CE_None;
2483 : GByte *pabyBILScanline =
2484 272 : (GByte *)CPLMalloc(nBufXSize * nDataTypeSize * nBandCount);
2485 272 : GByte **papabyBIL = (GByte **)CPLMalloc(nBandCount * sizeof(void *));
2486 :
2487 546 : for (i = 0; i < nBandCount; i++)
2488 274 : papabyBIL[i] = pabyBILScanline + i * nBufXSize * nDataTypeSize;
2489 :
2490 : /* -------------------------------------------------------------------- */
2491 : /* Read back all the data for the requested view. */
2492 : /* -------------------------------------------------------------------- */
2493 4272 : for (int iScanline = 0; iScanline < nBufYSize; iScanline++)
2494 : {
2495 : NCSEcwReadStatus eRStatus;
2496 :
2497 8000 : eRStatus = poFileView->ReadLineBIL(
2498 4000 : eNCSRequestDataType, (UINT16)nBandCount, (void **)papabyBIL);
2499 4000 : if (eRStatus != NCSECW_READ_OK)
2500 : {
2501 0 : eErr = CE_Failure;
2502 0 : CPLError(CE_Failure, CPLE_AppDefined,
2503 : "NCScbmReadViewLineBIL failed.");
2504 0 : break;
2505 : }
2506 :
2507 8080 : for (i = 0; i < nBandCount; i++)
2508 : {
2509 4080 : if (i == nBandIndexToPromoteTo8Bit)
2510 : {
2511 24450 : for (int iX = 0; iX < nBufXSize; iX++)
2512 : {
2513 24300 : papabyBIL[i][iX] *= 255;
2514 : }
2515 : }
2516 :
2517 4080 : GDALCopyWords(pabyBILScanline + i * nDataTypeSize * nBufXSize,
2518 : eRasterDataType, nDataTypeSize,
2519 4080 : ((GByte *)pData) + nLineSpace * iScanline +
2520 4080 : nBandSpace * i,
2521 : eBufType, (int)nPixelSpace, nBufXSize);
2522 : }
2523 :
2524 4000 : if (psExtraArg->pfnProgress != nullptr &&
2525 0 : !psExtraArg->pfnProgress(1.0 * (iScanline + 1) / nBufYSize, "",
2526 : psExtraArg->pProgressData))
2527 : {
2528 0 : eErr = CE_Failure;
2529 0 : break;
2530 : }
2531 : }
2532 :
2533 272 : CPLFree(pabyBILScanline);
2534 272 : CPLFree(papabyBIL);
2535 :
2536 272 : return eErr;
2537 : }
2538 :
2539 : /************************************************************************/
2540 : /* OpenJPEG2000() */
2541 : /* */
2542 : /* Open method that only supports JPEG2000 files. */
2543 : /************************************************************************/
2544 :
2545 89 : GDALDataset *ECWDataset::OpenJPEG2000(GDALOpenInfo *poOpenInfo)
2546 :
2547 : {
2548 89 : if (!ECWDatasetIdentifyJPEG2000(poOpenInfo))
2549 0 : return nullptr;
2550 :
2551 89 : return Open(poOpenInfo, TRUE);
2552 : }
2553 :
2554 : /************************************************************************/
2555 : /* OpenECW() */
2556 : /* */
2557 : /* Open method that only supports ECW files. */
2558 : /************************************************************************/
2559 :
2560 33 : GDALDataset *ECWDataset::OpenECW(GDALOpenInfo *poOpenInfo)
2561 :
2562 : {
2563 33 : if (!ECWDatasetIdentifyECW(poOpenInfo))
2564 0 : return nullptr;
2565 :
2566 33 : return Open(poOpenInfo, FALSE);
2567 : }
2568 :
2569 : /************************************************************************/
2570 : /* OpenFileView() */
2571 : /************************************************************************/
2572 :
2573 122 : CNCSJP2FileView *ECWDataset::OpenFileView(const char *pszDatasetName,
2574 : bool bProgressive,
2575 : int &bUsingCustomStream,
2576 : CPL_UNUSED bool bWrite)
2577 : {
2578 : /* -------------------------------------------------------------------- */
2579 : /* First we try to open it as a normal CNCSFile, letting the */
2580 : /* ECW SDK manage the IO itself. This will only work for real */
2581 : /* files, and ecwp: or ecwps: sources. */
2582 : /* -------------------------------------------------------------------- */
2583 122 : CNCSJP2FileView *poFileView = nullptr;
2584 : NCSError eErr;
2585 244 : CNCSError oErr(GetCNCSError(NCS_SUCCESS));
2586 :
2587 122 : bUsingCustomStream = FALSE;
2588 122 : poFileView = new CNCSFile();
2589 : // we always open in read only mode. This should be improved in the future.
2590 : try
2591 : {
2592 : #ifdef _WIN32
2593 : if (CPLTestBool(CPLGetConfigOption("GDAL_FILENAME_IS_UTF8", "YES")))
2594 : {
2595 : wchar_t *pwszDatasetName =
2596 : CPLRecodeToWChar(pszDatasetName, CPL_ENC_UTF8, CPL_ENC_UCS2);
2597 : oErr = poFileView->Open(pwszDatasetName, bProgressive, false);
2598 : CPLFree(pwszDatasetName);
2599 : }
2600 : else
2601 : #endif
2602 : {
2603 : oErr =
2604 122 : poFileView->Open((char *)pszDatasetName, bProgressive, false);
2605 : }
2606 : }
2607 0 : catch (...)
2608 : {
2609 0 : CPLError(CE_Failure, CPLE_AppDefined,
2610 : "Unexpected exception occurred in ECW SDK");
2611 0 : delete poFileView;
2612 0 : return nullptr;
2613 : }
2614 122 : eErr = oErr.GetErrorNumber();
2615 :
2616 : /* -------------------------------------------------------------------- */
2617 : /* If that did not work, trying opening as a virtual file. */
2618 : /* -------------------------------------------------------------------- */
2619 122 : if (eErr != NCS_SUCCESS)
2620 : {
2621 53 : CPLDebug("ECW",
2622 : "NCScbmOpenFileView(%s): eErr=%d, will try VSIL stream.",
2623 : pszDatasetName, (int)eErr);
2624 :
2625 53 : delete poFileView;
2626 :
2627 53 : VSILFILE *fpVSIL = VSIFOpenL(pszDatasetName, "rb");
2628 53 : if (fpVSIL == nullptr)
2629 : {
2630 0 : CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open %s.",
2631 : pszDatasetName);
2632 0 : return nullptr;
2633 : }
2634 :
2635 53 : if (hECWDatasetMutex == nullptr)
2636 : {
2637 0 : hECWDatasetMutex = CPLCreateMutex();
2638 : }
2639 53 : else if (!CPLAcquireMutex(hECWDatasetMutex, 60.0))
2640 : {
2641 0 : CPLDebug("ECW", "Failed to acquire mutex in 60s.");
2642 : }
2643 : else
2644 : {
2645 53 : CPLDebug("ECW", "Got mutex.");
2646 : }
2647 :
2648 53 : poFileView = new CNCSJP2FileView();
2649 :
2650 : #if ECWSDK_VERSION >= 55
2651 : NCS::CString streamName(pszDatasetName);
2652 : auto vsiIoStream =
2653 : NCS::CView::FindSteamByStreamNameFromOpenDatasets(streamName);
2654 : if (!vsiIoStream)
2655 : {
2656 : vsiIoStream = std::make_shared<VSIIOStream>();
2657 : std::static_pointer_cast<VSIIOStream>(vsiIoStream)
2658 : ->Access(fpVSIL, FALSE, TRUE, pszDatasetName, 0, -1);
2659 : bUsingCustomStream = TRUE;
2660 : }
2661 : oErr = poFileView->Open(vsiIoStream, bProgressive);
2662 : #else
2663 53 : auto vsiIoStream = new VSIIOStream();
2664 53 : vsiIoStream->Access(fpVSIL, FALSE, TRUE, pszDatasetName, 0, -1);
2665 53 : oErr = poFileView->Open(vsiIoStream, bProgressive);
2666 :
2667 : // The CNCSJP2FileView (poFileView) object may not use the iostream
2668 : // (poIOStream) passed to the CNCSJP2FileView::Open() method if an
2669 : // iostream is already available to the ECW JPEG 2000 SDK for a given
2670 : // file. Consequently, if the iostream passed to
2671 : // CNCSJP2FileView::Open() does not become the underlying iostream
2672 : // of the CNCSJP2FileView object, then it should be deleted.
2673 : //
2674 : // In addition, the underlying iostream of the CNCSJP2FileView object
2675 : // should not be deleted until all CNCSJP2FileView objects using the
2676 : // underlying iostream are deleted. Consequently, each time a
2677 : // CNCSJP2FileView object is created, the nFileViewCount attribute
2678 : // of the underlying VSIIOStream object must be incremented for use
2679 : // in the ECWDataset destructor.
2680 :
2681 : VSIIOStream *poUnderlyingIOStream =
2682 53 : ((VSIIOStream *)(poFileView->GetStream()));
2683 :
2684 53 : if (poUnderlyingIOStream)
2685 52 : poUnderlyingIOStream->nFileViewCount++;
2686 :
2687 53 : if (vsiIoStream != poUnderlyingIOStream)
2688 : {
2689 1 : delete vsiIoStream;
2690 : }
2691 : else
2692 : {
2693 52 : bUsingCustomStream = TRUE;
2694 : }
2695 : #endif
2696 :
2697 53 : CPLReleaseMutex(hECWDatasetMutex);
2698 :
2699 53 : if (oErr.GetErrorNumber() != NCS_SUCCESS)
2700 : {
2701 1 : delete poFileView;
2702 1 : ECWReportError(oErr);
2703 :
2704 1 : return nullptr;
2705 : }
2706 : }
2707 :
2708 121 : return poFileView;
2709 : }
2710 :
2711 : /************************************************************************/
2712 : /* Open() */
2713 : /************************************************************************/
2714 :
2715 122 : GDALDataset *ECWDataset::Open(GDALOpenInfo *poOpenInfo, int bIsJPEG2000)
2716 :
2717 : {
2718 122 : CNCSJP2FileView *poFileView = nullptr;
2719 : int i;
2720 122 : int bUsingCustomStream = FALSE;
2721 244 : CPLString osFilename = poOpenInfo->pszFilename;
2722 :
2723 122 : ECWInitialize();
2724 :
2725 : /* Note: J2K_SUBFILE is somehow an obsolete concept that predates
2726 : * /vsisubfile/ */
2727 : /* syntax and was used mainly(only?) by the NITF driver before its switch */
2728 : /* to /vsisubfile */
2729 :
2730 : /* -------------------------------------------------------------------- */
2731 : /* If we get a J2K_SUBFILE style name, convert it into the */
2732 : /* corresponding /vsisubfile/ path. */
2733 : /* */
2734 : /* From: J2K_SUBFILE:offset,size,filename */
2735 : /* To: /vsisubfile/offset_size,filename */
2736 : /* -------------------------------------------------------------------- */
2737 122 : if (STARTS_WITH_CI(osFilename, "J2K_SUBFILE:"))
2738 : {
2739 : char **papszTokens =
2740 0 : CSLTokenizeString2(osFilename.c_str() + 12, ",", 0);
2741 0 : if (CSLCount(papszTokens) >= 3)
2742 : {
2743 : osFilename.Printf("/vsisubfile/%s_%s,%s", papszTokens[0],
2744 0 : papszTokens[1], papszTokens[2]);
2745 : }
2746 : else
2747 : {
2748 0 : CPLError(CE_Failure, CPLE_OpenFailed,
2749 : "Failed to parse J2K_SUBFILE specification.");
2750 0 : CSLDestroy(papszTokens);
2751 0 : return nullptr;
2752 : }
2753 0 : CSLDestroy(papszTokens);
2754 : }
2755 :
2756 : /* -------------------------------------------------------------------- */
2757 : /* Open the client interface. */
2758 : /* -------------------------------------------------------------------- */
2759 122 : poFileView = OpenFileView(osFilename.c_str(), false, bUsingCustomStream,
2760 122 : poOpenInfo->eAccess == GA_Update);
2761 122 : if (poFileView == nullptr)
2762 : {
2763 : #if ECWSDK_VERSION < 50
2764 : /* Detect what is apparently the ECW v3 file format signature */
2765 1 : if (EQUAL(CPLGetExtension(osFilename), "ECW") &&
2766 2 : poOpenInfo->nHeaderBytes > 0x30 &&
2767 1 : STARTS_WITH_CI((const char *)(poOpenInfo->pabyHeader + 0x20),
2768 : "ecw ECW3"))
2769 : {
2770 1 : CPLError(CE_Failure, CPLE_AppDefined,
2771 : "Cannot open %s which looks like a ECW format v3 file, "
2772 : "that requires ECW SDK 5.0 or later",
2773 : osFilename.c_str());
2774 : }
2775 : #endif
2776 1 : return nullptr;
2777 : }
2778 :
2779 : /* -------------------------------------------------------------------- */
2780 : /* Create a corresponding GDALDataset. */
2781 : /* -------------------------------------------------------------------- */
2782 121 : ECWDataset *poDS = new ECWDataset(bIsJPEG2000);
2783 121 : poDS->poFileView = poFileView;
2784 121 : poDS->eAccess = poOpenInfo->eAccess;
2785 :
2786 : // Disable .aux.xml writing for subfiles and such. Unfortunately
2787 : // this will also disable it in some cases where it might be
2788 : // applicable.
2789 121 : if (bUsingCustomStream)
2790 52 : poDS->nPamFlags |= GPF_DISABLED;
2791 :
2792 121 : poDS->bUsingCustomStream = bUsingCustomStream;
2793 :
2794 : /* -------------------------------------------------------------------- */
2795 : /* Fetch general file information. */
2796 : /* -------------------------------------------------------------------- */
2797 121 : poDS->psFileInfo = poFileView->GetFileInfo();
2798 :
2799 121 : CPLDebug("ECW",
2800 : "FileInfo: SizeXY=%d,%d Bands=%d\n"
2801 : " OriginXY=%g,%g CellIncrementXY=%g,%g\n"
2802 : " ColorSpace=%d, eCellType=%d\n",
2803 121 : poDS->psFileInfo->nSizeX, poDS->psFileInfo->nSizeY,
2804 121 : poDS->psFileInfo->nBands, poDS->psFileInfo->fOriginX,
2805 121 : poDS->psFileInfo->fOriginY, poDS->psFileInfo->fCellIncrementX,
2806 121 : poDS->psFileInfo->fCellIncrementY,
2807 121 : (int)poDS->psFileInfo->eColorSpace,
2808 121 : (int)poDS->psFileInfo->eCellType);
2809 :
2810 : /* -------------------------------------------------------------------- */
2811 : /* Establish raster info. */
2812 : /* -------------------------------------------------------------------- */
2813 121 : poDS->nRasterXSize = poDS->psFileInfo->nSizeX;
2814 121 : poDS->nRasterYSize = poDS->psFileInfo->nSizeY;
2815 :
2816 : /* -------------------------------------------------------------------- */
2817 : /* Establish the GDAL data type that corresponds. A few NCS */
2818 : /* data types have no direct corresponding value in GDAL so we */
2819 : /* will coerce to something sufficiently similar. */
2820 : /* -------------------------------------------------------------------- */
2821 121 : poDS->eNCSRequestDataType = poDS->psFileInfo->eCellType;
2822 121 : switch (poDS->psFileInfo->eCellType)
2823 : {
2824 101 : case NCSCT_UINT8:
2825 101 : poDS->eRasterDataType = GDT_Byte;
2826 101 : break;
2827 :
2828 10 : case NCSCT_UINT16:
2829 10 : poDS->eRasterDataType = GDT_UInt16;
2830 10 : break;
2831 :
2832 2 : case NCSCT_UINT32:
2833 : case NCSCT_UINT64:
2834 2 : poDS->eRasterDataType = GDT_UInt32;
2835 2 : poDS->eNCSRequestDataType = NCSCT_UINT32;
2836 2 : break;
2837 :
2838 6 : case NCSCT_INT8:
2839 : case NCSCT_INT16:
2840 6 : poDS->eRasterDataType = GDT_Int16;
2841 6 : poDS->eNCSRequestDataType = NCSCT_INT16;
2842 6 : break;
2843 :
2844 2 : case NCSCT_INT32:
2845 : case NCSCT_INT64:
2846 2 : poDS->eRasterDataType = GDT_Int32;
2847 2 : poDS->eNCSRequestDataType = NCSCT_INT32;
2848 2 : break;
2849 :
2850 0 : case NCSCT_IEEE4:
2851 0 : poDS->eRasterDataType = GDT_Float32;
2852 0 : break;
2853 :
2854 0 : case NCSCT_IEEE8:
2855 0 : poDS->eRasterDataType = GDT_Float64;
2856 0 : break;
2857 :
2858 0 : default:
2859 0 : CPLDebug("ECW", "Unhandled case : eCellType = %d",
2860 0 : (int)poDS->psFileInfo->eCellType);
2861 0 : break;
2862 : }
2863 :
2864 : /* -------------------------------------------------------------------- */
2865 : /* If decoding a UInt32 image, check that the SDK is not buggy */
2866 : /* There are issues at least in the 5.x series. */
2867 : /* -------------------------------------------------------------------- */
2868 : #if ECWSDK_VERSION >= 40
2869 : constexpr const char *szDETECT_BUG_FILENAME =
2870 : "__detect_ecw_uint32_bug__.j2k";
2871 : if (bIsJPEG2000 && poDS->eNCSRequestDataType == NCSCT_UINT32 &&
2872 : CPLTestBool(CPLGetConfigOption("ECW_CHECK_CORRECT_DECODING", "TRUE")) &&
2873 : strstr(poOpenInfo->pszFilename, szDETECT_BUG_FILENAME) == nullptr)
2874 : {
2875 : static bool bUINT32_Ok = false;
2876 : {
2877 : CPLMutexHolder oHolder(&hECWDatasetMutex);
2878 : static bool bTestDone = false;
2879 : if (!bTestDone)
2880 : {
2881 : bTestDone = true;
2882 : // Minimal J2K 2x2 image with NBITS=20, unsigned, reversible
2883 : // compression and following values 0 1048575 1048574 524288
2884 :
2885 : static const GByte abyTestUInt32ImageData[] = {
2886 : 0xFF, 0x4F, 0xFF, 0x51, 0x00, 0x29, 0x00, 0x02, 0x00, 0x00,
2887 : 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
2888 : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
2889 : 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2890 : 0x00, 0x01, 0x13, 0x01, 0x01, 0xFF, 0x52, 0x00, 0x0D, 0x01,
2891 : 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x04, 0x00, 0x01, 0x99,
2892 : 0xFF, 0x5C, 0x00, 0x04, 0x40, 0xA0, 0xFF, 0x90, 0x00, 0x0A,
2893 : 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x01, 0xFF, 0x93,
2894 : 0xDF, 0xF9, 0x40, 0x50, 0x07, 0x68, 0xE0, 0x12, 0xD2, 0xDA,
2895 : 0xDF, 0xFF, 0x7F, 0x5F, 0xFF, 0xD9};
2896 :
2897 : const std::string osTmpFilename =
2898 : VSIMemGenerateHiddenFilename(szDETECT_BUG_FILENAME);
2899 : VSIFCloseL(VSIFileFromMemBuffer(
2900 : osTmpFilename.c_str(),
2901 : const_cast<GByte *>(abyTestUInt32ImageData),
2902 : sizeof(abyTestUInt32ImageData), false));
2903 : GDALOpenInfo oOpenInfo(osTmpFilename.c_str(), GA_ReadOnly);
2904 : auto poTmpDS =
2905 : std::unique_ptr<GDALDataset>(Open(&oOpenInfo, true));
2906 : if (poTmpDS)
2907 : {
2908 : uint32_t anValues[4] = {0};
2909 : if (poTmpDS->GetRasterBand(1)->RasterIO(
2910 : GF_Read, 0, 0, 2, 2, anValues, 2, 2, GDT_UInt32, 0,
2911 : 0, nullptr) == CE_None &&
2912 : anValues[0] == 0 && anValues[1] == 1048575 &&
2913 : anValues[2] == 1048574 && anValues[3] == 524288)
2914 : {
2915 : bUINT32_Ok = true;
2916 : }
2917 : }
2918 : VSIUnlink(osTmpFilename.c_str());
2919 : }
2920 : }
2921 :
2922 : if (!bUINT32_Ok)
2923 : {
2924 : CPLDebug("ECW", "ECW SDK used cannot correctly decode UInt32 "
2925 : "images. Giving up");
2926 : delete poDS;
2927 : return nullptr;
2928 : }
2929 : }
2930 : #endif
2931 :
2932 : /* -------------------------------------------------------------------- */
2933 : /* Create band information objects. */
2934 : /* -------------------------------------------------------------------- */
2935 332 : for (i = 0; i < poDS->psFileInfo->nBands; i++)
2936 211 : poDS->SetBand(i + 1, new ECWRasterBand(poDS, i + 1, -1,
2937 211 : poOpenInfo->papszOpenOptions));
2938 :
2939 : /* -------------------------------------------------------------------- */
2940 : /* Look for supporting coordinate system information. */
2941 : /* -------------------------------------------------------------------- */
2942 121 : if (bIsJPEG2000)
2943 : {
2944 89 : poDS->LoadJP2Metadata(poOpenInfo, osFilename);
2945 : }
2946 : else
2947 : {
2948 32 : poDS->ECW2WKTProjection();
2949 :
2950 : /* --------------------------------------------------------------------
2951 : */
2952 : /* Check for world file. */
2953 : /* --------------------------------------------------------------------
2954 : */
2955 32 : if (!poDS->bGeoTransformValid)
2956 : {
2957 0 : poDS->bGeoTransformValid |=
2958 0 : GDALReadWorldFile2(osFilename, nullptr, poDS->adfGeoTransform,
2959 0 : poOpenInfo->GetSiblingFiles(), nullptr) ||
2960 0 : GDALReadWorldFile2(osFilename, ".wld", poDS->adfGeoTransform,
2961 0 : poOpenInfo->GetSiblingFiles(), nullptr);
2962 : }
2963 : }
2964 :
2965 121 : if (poDS->psFileInfo->nCompressionRate > 0)
2966 65 : poDS->GDALDataset::SetMetadataItem(
2967 : "COMPRESSION_RATE_TARGET",
2968 130 : CPLString().Printf("%d", poDS->psFileInfo->nCompressionRate));
2969 121 : poDS->GDALDataset::SetMetadataItem(
2970 121 : "COLORSPACE", ECWGetColorSpaceName(poDS->psFileInfo->eColorSpace));
2971 : #if ECWSDK_VERSION >= 50
2972 : if (!bIsJPEG2000)
2973 : poDS->GDALDataset::SetMetadataItem(
2974 : "VERSION",
2975 : CPLString().Printf("%d", poDS->psFileInfo->nFormatVersion));
2976 : #if ECWSDK_VERSION >= 51
2977 : // output jp2 header info
2978 : if (bIsJPEG2000 && poDS->poFileView)
2979 : {
2980 : // comments
2981 : char *csComments = nullptr;
2982 : poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:COMMENTS",
2983 : &csComments);
2984 : if (csComments)
2985 : {
2986 : std::string osComments(csComments);
2987 :
2988 : // Strip off boring Kakadu COM content
2989 : if (STARTS_WITH(osComments.c_str(), "Kakadu-"))
2990 : {
2991 : const auto nEOLPos = osComments.find('\n');
2992 : if (nEOLPos == std::string::npos)
2993 : osComments.clear();
2994 : osComments = osComments.substr(nEOLPos + 1);
2995 : }
2996 : if (STARTS_WITH(
2997 : osComments.c_str(),
2998 : "Kdu-Layer-Info: "
2999 : "log_2{Delta-D(squared-error)/Delta-L(bytes)}, L(bytes)\n"))
3000 : {
3001 : while (true)
3002 : {
3003 : const auto nEOLPos = osComments.find('\n');
3004 : if (nEOLPos == std::string::npos)
3005 : {
3006 : osComments.clear();
3007 : break;
3008 : }
3009 : osComments = osComments.substr(nEOLPos + 1);
3010 : if (osComments.find(", ") == std::string::npos)
3011 : break;
3012 : }
3013 : }
3014 :
3015 : // Strip off boring OpenJPEG COM content
3016 : if (STARTS_WITH(osComments.c_str(),
3017 : "Created by OpenJPEG version ") &&
3018 : osComments.find('\n') == std::string::npos)
3019 : {
3020 : osComments.clear();
3021 : }
3022 :
3023 : if (!osComments.empty())
3024 : poDS->GDALDataset::SetMetadataItem("ALL_COMMENTS",
3025 : osComments.c_str());
3026 : NCSFree(csComments);
3027 : }
3028 :
3029 : // Profile
3030 : UINT32 nProfile = 2;
3031 : UINT32 nRsiz = 0;
3032 : poDS->poFileView->GetParameter((char *)"JP2:COMPLIANCE:PROFILE:TYPE",
3033 : &nRsiz);
3034 : if (nRsiz == 0)
3035 : nProfile = 2; // Profile 2 (no restrictions)
3036 : else if (nRsiz == 1)
3037 : nProfile = 0; // Profile 0
3038 : else if (nRsiz == 2)
3039 : nProfile = 1; // Profile 1, NITF_BIIF_NPJE, NITF_BIIF_EPJE
3040 : poDS->GDALDataset::SetMetadataItem("PROFILE",
3041 : CPLString().Printf("%d", nProfile),
3042 : JPEG2000_DOMAIN_NAME);
3043 :
3044 : // number of tiles on X axis
3045 : UINT32 nTileNrX = 1;
3046 : poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:TILENR:X",
3047 : &nTileNrX);
3048 : poDS->GDALDataset::SetMetadataItem("TILES_X",
3049 : CPLString().Printf("%d", nTileNrX),
3050 : JPEG2000_DOMAIN_NAME);
3051 :
3052 : // number of tiles on X axis
3053 : UINT32 nTileNrY = 1;
3054 : poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:TILENR:Y",
3055 : &nTileNrY);
3056 : poDS->GDALDataset::SetMetadataItem("TILES_Y",
3057 : CPLString().Printf("%d", nTileNrY),
3058 : JPEG2000_DOMAIN_NAME);
3059 :
3060 : // Tile Width
3061 : UINT32 nTileSizeX = 0;
3062 : poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:TILESIZE:X",
3063 : &nTileSizeX);
3064 : poDS->GDALDataset::SetMetadataItem("TILE_WIDTH",
3065 : CPLString().Printf("%d", nTileSizeX),
3066 : JPEG2000_DOMAIN_NAME);
3067 :
3068 : // Tile Height
3069 : UINT32 nTileSizeY = 0;
3070 : poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:TILESIZE:Y",
3071 : &nTileSizeY);
3072 : poDS->GDALDataset::SetMetadataItem("TILE_HEIGHT",
3073 : CPLString().Printf("%d", nTileSizeY),
3074 : JPEG2000_DOMAIN_NAME);
3075 :
3076 : // Precinct Sizes on X axis
3077 : char *csPreSizeX = nullptr;
3078 : poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:PRECINCTSIZE:X",
3079 : &csPreSizeX);
3080 : if (csPreSizeX)
3081 : {
3082 : poDS->GDALDataset::SetMetadataItem("PRECINCT_SIZE_X", csPreSizeX,
3083 : JPEG2000_DOMAIN_NAME);
3084 : NCSFree(csPreSizeX);
3085 : }
3086 :
3087 : // Precinct Sizes on Y axis
3088 : char *csPreSizeY = nullptr;
3089 : poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:PRECINCTSIZE:Y",
3090 : &csPreSizeY);
3091 : if (csPreSizeY)
3092 : {
3093 : poDS->GDALDataset::SetMetadataItem("PRECINCT_SIZE_Y", csPreSizeY,
3094 : JPEG2000_DOMAIN_NAME);
3095 : NCSFree(csPreSizeY);
3096 : }
3097 :
3098 : // Code Block Size on X axis
3099 : UINT32 nCodeBlockSizeX = 0;
3100 : poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:CODEBLOCK:X",
3101 : &nCodeBlockSizeX);
3102 : poDS->GDALDataset::SetMetadataItem(
3103 : "CODE_BLOCK_SIZE_X", CPLString().Printf("%d", nCodeBlockSizeX),
3104 : JPEG2000_DOMAIN_NAME);
3105 :
3106 : // Code Block Size on Y axis
3107 : UINT32 nCodeBlockSizeY = 0;
3108 : poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:CODEBLOCK:Y",
3109 : &nCodeBlockSizeY);
3110 : poDS->GDALDataset::SetMetadataItem(
3111 : "CODE_BLOCK_SIZE_Y", CPLString().Printf("%d", nCodeBlockSizeY),
3112 : JPEG2000_DOMAIN_NAME);
3113 :
3114 : // Bitdepth
3115 : char *csBitdepth = nullptr;
3116 : poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:BITDEPTH",
3117 : &csBitdepth);
3118 : if (csBitdepth)
3119 : {
3120 : poDS->GDALDataset::SetMetadataItem("PRECISION", csBitdepth,
3121 : JPEG2000_DOMAIN_NAME);
3122 : NCSFree(csBitdepth);
3123 : }
3124 :
3125 : // Resolution Levels
3126 : UINT32 nLevels = 0;
3127 : poDS->poFileView->GetParameter(
3128 : (char *)"JPC:DECOMPRESS:RESOLUTION:LEVELS", &nLevels);
3129 : poDS->GDALDataset::SetMetadataItem("RESOLUTION_LEVELS",
3130 : CPLString().Printf("%d", nLevels),
3131 : JPEG2000_DOMAIN_NAME);
3132 :
3133 : // Qualaity Layers
3134 : UINT32 nLayers = 0;
3135 : poDS->poFileView->GetParameter((char *)"JP2:DECOMPRESS:LAYERS",
3136 : &nLayers);
3137 : poDS->GDALDataset::SetMetadataItem("QUALITY_LAYERS",
3138 : CPLString().Printf("%d", nLayers),
3139 : JPEG2000_DOMAIN_NAME);
3140 :
3141 : // Progression Order
3142 : char *csOrder = nullptr;
3143 : poDS->poFileView->GetParameter(
3144 : (char *)"JPC:DECOMPRESS:PROGRESSION:ORDER", &csOrder);
3145 : if (csOrder)
3146 : {
3147 : poDS->GDALDataset::SetMetadataItem("PROGRESSION_ORDER", csOrder,
3148 : JPEG2000_DOMAIN_NAME);
3149 : NCSFree(csOrder);
3150 : }
3151 :
3152 : // DWT Filter
3153 : const char *csFilter = nullptr;
3154 : UINT32 nFilter;
3155 : poDS->poFileView->GetParameter((char *)"JP2:TRANSFORMATION:TYPE",
3156 : &nFilter);
3157 : if (nFilter)
3158 : csFilter = "5x3";
3159 : else
3160 : csFilter = "9x7";
3161 : poDS->GDALDataset::SetMetadataItem("TRANSFORMATION_TYPE", csFilter,
3162 : JPEG2000_DOMAIN_NAME);
3163 :
3164 : // SOP used?
3165 : bool bSOP = 0;
3166 : poDS->poFileView->GetParameter((char *)"JP2:DECOMPRESS:SOP:EXISTS",
3167 : &bSOP);
3168 : poDS->SetMetadataItem("USE_SOP", (bSOP) ? "TRUE" : "FALSE",
3169 : JPEG2000_DOMAIN_NAME);
3170 :
3171 : // EPH used?
3172 : bool bEPH = 0;
3173 : poDS->poFileView->GetParameter((char *)"JP2:DECOMPRESS:EPH:EXISTS",
3174 : &bEPH);
3175 : poDS->SetMetadataItem("USE_EPH", (bEPH) ? "TRUE" : "FALSE",
3176 : JPEG2000_DOMAIN_NAME);
3177 :
3178 : // GML JP2 data contained?
3179 : bool bGML = 0;
3180 : poDS->poFileView->GetParameter((char *)"JP2:GML:JP2:BOX:EXISTS", &bGML);
3181 : poDS->SetMetadataItem("GML_JP2_DATA", (bGML) ? "TRUE" : "FALSE",
3182 : JPEG2000_DOMAIN_NAME);
3183 : }
3184 : #endif // ECWSDK_VERSION>=51
3185 : if (!bIsJPEG2000 && poDS->psFileInfo->nFormatVersion >= 3)
3186 : {
3187 : poDS->GDALDataset::SetMetadataItem(
3188 : "COMPRESSION_RATE_ACTUAL",
3189 : CPLString().Printf("%f", poDS->psFileInfo->fActualCompressionRate));
3190 : poDS->GDALDataset::SetMetadataItem(
3191 : "CLOCKWISE_ROTATION_DEG",
3192 : CPLString().Printf("%f", poDS->psFileInfo->fCWRotationDegrees));
3193 : poDS->GDALDataset::SetMetadataItem("COMPRESSION_DATE",
3194 : poDS->psFileInfo->sCompressionDate);
3195 : // Get file metadata.
3196 : poDS->ReadFileMetaDataFromFile();
3197 : }
3198 : #else
3199 121 : poDS->GDALDataset::SetMetadataItem(
3200 242 : "VERSION", CPLString().Printf("%d", bIsJPEG2000 ? 1 : 2));
3201 : #endif
3202 :
3203 : /* -------------------------------------------------------------------- */
3204 : /* Initialize any PAM information. */
3205 : /* -------------------------------------------------------------------- */
3206 121 : poDS->SetDescription(osFilename);
3207 121 : poDS->TryLoadXML(poOpenInfo->GetSiblingFiles());
3208 :
3209 : /* -------------------------------------------------------------------- */
3210 : /* Vector layers */
3211 : /* -------------------------------------------------------------------- */
3212 121 : if (bIsJPEG2000 && poOpenInfo->nOpenFlags & GDAL_OF_VECTOR)
3213 : {
3214 1 : poDS->LoadVectorLayers(CPLFetchBool(poOpenInfo->papszOpenOptions,
3215 : "OPEN_REMOTE_GML", false));
3216 :
3217 : // If file opened in vector-only mode and there's no vector,
3218 : // return
3219 1 : if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0 &&
3220 0 : poDS->GetLayerCount() == 0)
3221 : {
3222 0 : delete poDS;
3223 0 : return nullptr;
3224 : }
3225 : }
3226 :
3227 121 : return poDS;
3228 : }
3229 :
3230 : /************************************************************************/
3231 : /* GetMetadataDomainList() */
3232 : /************************************************************************/
3233 :
3234 4 : char **ECWDataset::GetMetadataDomainList()
3235 : {
3236 4 : return BuildMetadataDomainList(
3237 : GDALJP2AbstractDataset::GetMetadataDomainList(), TRUE, "ECW", "GML",
3238 4 : nullptr);
3239 : }
3240 :
3241 : /************************************************************************/
3242 : /* GetMetadataItem() */
3243 : /************************************************************************/
3244 :
3245 55 : const char *ECWDataset::GetMetadataItem(const char *pszName,
3246 : const char *pszDomain)
3247 : {
3248 55 : if (!bIsJPEG2000 && pszDomain != nullptr && EQUAL(pszDomain, "ECW") &&
3249 : pszName != nullptr)
3250 : {
3251 6 : if (EQUAL(pszName, "PROJ"))
3252 2 : return m_osProjCode.size() ? m_osProjCode.c_str() : "RAW";
3253 4 : if (EQUAL(pszName, "DATUM"))
3254 2 : return m_osDatumCode.size() ? m_osDatumCode.c_str() : "RAW";
3255 2 : if (EQUAL(pszName, "UNITS"))
3256 2 : return m_osUnitsCode.size() ? m_osUnitsCode.c_str() : "METERS";
3257 : }
3258 49 : return GDALJP2AbstractDataset::GetMetadataItem(pszName, pszDomain);
3259 : }
3260 :
3261 : /************************************************************************/
3262 : /* GetMetadata() */
3263 : /************************************************************************/
3264 :
3265 90 : char **ECWDataset::GetMetadata(const char *pszDomain)
3266 :
3267 : {
3268 90 : if (!bIsJPEG2000 && pszDomain != nullptr && EQUAL(pszDomain, "ECW"))
3269 : {
3270 0 : oECWMetadataList.Clear();
3271 : oECWMetadataList.AddString(
3272 0 : CPLSPrintf("%s=%s", "PROJ", GetMetadataItem("PROJ", "ECW")));
3273 : oECWMetadataList.AddString(
3274 0 : CPLSPrintf("%s=%s", "DATUM", GetMetadataItem("DATUM", "ECW")));
3275 : oECWMetadataList.AddString(
3276 0 : CPLSPrintf("%s=%s", "UNITS", GetMetadataItem("UNITS", "ECW")));
3277 0 : return oECWMetadataList.List();
3278 : }
3279 90 : else if (pszDomain == nullptr || !EQUAL(pszDomain, "GML"))
3280 86 : return GDALJP2AbstractDataset::GetMetadata(pszDomain);
3281 : else
3282 4 : return papszGMLMetadata;
3283 : }
3284 :
3285 : /************************************************************************/
3286 : /* ReadFileMetaDataFromFile() */
3287 : /* */
3288 : /* Gets relevant information from NCSFileMetadata and populates */
3289 : /* GDAL metadata. */
3290 : /* */
3291 : /************************************************************************/
3292 : #if ECWSDK_VERSION >= 50
3293 : void ECWDataset::ReadFileMetaDataFromFile()
3294 : {
3295 : if (psFileInfo->pFileMetaData == nullptr)
3296 : return;
3297 :
3298 : if (psFileInfo->pFileMetaData->sClassification != nullptr)
3299 : GDALDataset::SetMetadataItem(
3300 : "FILE_METADATA_CLASSIFICATION",
3301 : NCS::CString(psFileInfo->pFileMetaData->sClassification));
3302 : if (psFileInfo->pFileMetaData->sAcquisitionDate != nullptr)
3303 : GDALDataset::SetMetadataItem(
3304 : "FILE_METADATA_ACQUISITION_DATE",
3305 : NCS::CString(psFileInfo->pFileMetaData->sAcquisitionDate));
3306 : if (psFileInfo->pFileMetaData->sAcquisitionSensorName != nullptr)
3307 : GDALDataset::SetMetadataItem(
3308 : "FILE_METADATA_ACQUISITION_SENSOR_NAME",
3309 : NCS::CString(psFileInfo->pFileMetaData->sAcquisitionSensorName));
3310 : if (psFileInfo->pFileMetaData->sCompressionSoftware != nullptr)
3311 : GDALDataset::SetMetadataItem(
3312 : "FILE_METADATA_COMPRESSION_SOFTWARE",
3313 : NCS::CString(psFileInfo->pFileMetaData->sCompressionSoftware));
3314 : if (psFileInfo->pFileMetaData->sAuthor != nullptr)
3315 : GDALDataset::SetMetadataItem(
3316 : "FILE_METADATA_AUTHOR",
3317 : NCS::CString(psFileInfo->pFileMetaData->sAuthor));
3318 : if (psFileInfo->pFileMetaData->sCopyright != nullptr)
3319 : GDALDataset::SetMetadataItem(
3320 : "FILE_METADATA_COPYRIGHT",
3321 : NCS::CString(psFileInfo->pFileMetaData->sCopyright));
3322 : if (psFileInfo->pFileMetaData->sCompany != nullptr)
3323 : GDALDataset::SetMetadataItem(
3324 : "FILE_METADATA_COMPANY",
3325 : NCS::CString(psFileInfo->pFileMetaData->sCompany));
3326 : if (psFileInfo->pFileMetaData->sEmail != nullptr)
3327 : GDALDataset::SetMetadataItem(
3328 : "FILE_METADATA_EMAIL",
3329 : NCS::CString(psFileInfo->pFileMetaData->sEmail));
3330 : if (psFileInfo->pFileMetaData->sAddress != nullptr)
3331 : GDALDataset::SetMetadataItem(
3332 : "FILE_METADATA_ADDRESS",
3333 : NCS::CString(psFileInfo->pFileMetaData->sAddress));
3334 : if (psFileInfo->pFileMetaData->sTelephone != nullptr)
3335 : GDALDataset::SetMetadataItem(
3336 : "FILE_METADATA_TELEPHONE",
3337 : NCS::CString(psFileInfo->pFileMetaData->sTelephone));
3338 : }
3339 :
3340 : /************************************************************************/
3341 : /* WriteFileMetaData() */
3342 : /************************************************************************/
3343 :
3344 : void ECWDataset::WriteFileMetaData(NCSFileMetaData *pFileMetaDataCopy)
3345 : {
3346 : if (!bFileMetaDataDirty)
3347 : return;
3348 :
3349 : CPLAssert(eAccess == GA_Update);
3350 : CPLAssert(!bIsJPEG2000);
3351 :
3352 : bFileMetaDataDirty = FALSE;
3353 :
3354 : NCSFileView *psFileView = nullptr;
3355 : NCSError eErr;
3356 :
3357 : psFileView = NCSEditOpen(GetDescription());
3358 : if (psFileView == nullptr)
3359 : {
3360 : CPLError(CE_Failure, CPLE_AppDefined, "NCSEditOpen() failed");
3361 : return;
3362 : }
3363 :
3364 : eErr = NCSEditSetFileMetaData(psFileView, pFileMetaDataCopy);
3365 : if (eErr != NCS_SUCCESS)
3366 : {
3367 : CPLError(CE_Failure, CPLE_AppDefined,
3368 : "NCSEditSetFileMetaData() failed : %s",
3369 : NCSGetLastErrorText(eErr));
3370 : }
3371 :
3372 : NCSEditFlushAll(psFileView);
3373 : NCSEditClose(psFileView);
3374 : }
3375 :
3376 : #endif
3377 : /************************************************************************/
3378 : /* ECW2WKTProjection() */
3379 : /* */
3380 : /* Set the dataset pszProjection string in OGC WKT format by */
3381 : /* looking up the ECW (GDT) coordinate system info in */
3382 : /* ecw_cs.wkt support data file. */
3383 : /* */
3384 : /* This code is likely still broken in some circumstances. For */
3385 : /* instance, I haven't been careful about changing the linear */
3386 : /* projection parameters (false easting/northing) if the units */
3387 : /* is feet. Lots of cases missing here, and in ecw_cs.wkt. */
3388 : /************************************************************************/
3389 :
3390 32 : void ECWDataset::ECW2WKTProjection()
3391 :
3392 : {
3393 32 : if (psFileInfo == nullptr)
3394 20 : return;
3395 :
3396 : /* -------------------------------------------------------------------- */
3397 : /* Capture Geotransform. */
3398 : /* */
3399 : /* We will try to ignore the provided file information if it is */
3400 : /* origin (0,0) and pixel size (1,1). I think sometimes I have */
3401 : /* also seen pixel increments of 0 on invalid datasets. */
3402 : /* -------------------------------------------------------------------- */
3403 32 : if (psFileInfo->fOriginX != 0.0 || psFileInfo->fOriginY != 0.0 ||
3404 0 : (psFileInfo->fCellIncrementX != 0.0 &&
3405 0 : psFileInfo->fCellIncrementX != 1.0) ||
3406 0 : (psFileInfo->fCellIncrementY != 0.0 &&
3407 0 : psFileInfo->fCellIncrementY != 1.0))
3408 : {
3409 32 : bGeoTransformValid = TRUE;
3410 :
3411 32 : adfGeoTransform[0] = psFileInfo->fOriginX;
3412 32 : adfGeoTransform[1] = psFileInfo->fCellIncrementX;
3413 32 : adfGeoTransform[2] = 0.0;
3414 :
3415 32 : adfGeoTransform[3] = psFileInfo->fOriginY;
3416 32 : adfGeoTransform[4] = 0.0;
3417 :
3418 : /* By default, set Y-resolution negative assuming images always */
3419 : /* have "Upward" orientation (Y coordinates increase "Upward"). */
3420 : /* Setting ECW_ALWAYS_UPWARD=FALSE option relexes that policy */
3421 : /* and makes the driver rely on the actual Y-resolution */
3422 : /* value (sign) of an image. This allows correctly processing */
3423 : /* rare images with "Downward" orientation, where Y coordinates */
3424 : /* increase "Downward" and Y-resolution is positive. */
3425 32 : if (CPLTestBool(CPLGetConfigOption("ECW_ALWAYS_UPWARD", "TRUE")))
3426 31 : adfGeoTransform[5] = -fabs(psFileInfo->fCellIncrementY);
3427 : else
3428 1 : adfGeoTransform[5] = psFileInfo->fCellIncrementY;
3429 : }
3430 :
3431 : /* -------------------------------------------------------------------- */
3432 : /* do we have projection and datum? */
3433 : /* -------------------------------------------------------------------- */
3434 : CPLString osUnits =
3435 32 : ECWTranslateFromCellSizeUnits(psFileInfo->eCellSizeUnits);
3436 :
3437 32 : CPLDebug("ECW", "projection=%s, datum=%s, units=%s",
3438 32 : psFileInfo->szProjection, psFileInfo->szDatum, osUnits.c_str());
3439 :
3440 32 : if (EQUAL(psFileInfo->szProjection, "RAW"))
3441 20 : return;
3442 :
3443 : /* -------------------------------------------------------------------- */
3444 : /* Set projection if we have it. */
3445 : /* -------------------------------------------------------------------- */
3446 24 : OGRSpatialReference oSRS;
3447 :
3448 : /* For backward-compatible with previous behavior. Should we only */
3449 : /* restrict to those 2 values ? */
3450 12 : if (psFileInfo->eCellSizeUnits != ECW_CELL_UNITS_METERS &&
3451 2 : psFileInfo->eCellSizeUnits != ECW_CELL_UNITS_FEET)
3452 0 : osUnits = ECWTranslateFromCellSizeUnits(ECW_CELL_UNITS_METERS);
3453 :
3454 12 : m_osDatumCode = psFileInfo->szDatum;
3455 12 : m_osProjCode = psFileInfo->szProjection;
3456 12 : m_osUnitsCode = osUnits;
3457 12 : if (oSRS.importFromERM(psFileInfo->szProjection, psFileInfo->szDatum,
3458 12 : osUnits) == OGRERR_NONE)
3459 : {
3460 12 : m_oSRS = std::move(oSRS);
3461 12 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
3462 : }
3463 :
3464 12 : CPLErrorReset(); /* see #4187 */
3465 : }
3466 :
3467 : /************************************************************************/
3468 : /* ECWTranslateFromWKT() */
3469 : /************************************************************************/
3470 :
3471 25 : int ECWTranslateFromWKT(const OGRSpatialReference *poSRS, char *pszProjection,
3472 : int nProjectionLen, char *pszDatum, int nDatumLen,
3473 : char *pszUnits)
3474 :
3475 : {
3476 50 : OGRSpatialReference oSRS;
3477 :
3478 25 : strcpy(pszProjection, "RAW");
3479 25 : strcpy(pszDatum, "RAW");
3480 25 : strcpy(pszUnits, "METERS");
3481 :
3482 25 : if (poSRS == nullptr || poSRS->IsEmpty())
3483 0 : return FALSE;
3484 :
3485 25 : oSRS = *poSRS;
3486 :
3487 25 : if (oSRS.IsLocal())
3488 0 : return TRUE;
3489 :
3490 : /* -------------------------------------------------------------------- */
3491 : /* Do we have an overall EPSG number for this coordinate system? */
3492 : /* -------------------------------------------------------------------- */
3493 25 : const char *pszAuthorityCode = nullptr;
3494 25 : const char *pszAuthorityName = nullptr;
3495 25 : UINT32 nEPSGCode = 0;
3496 :
3497 25 : if (oSRS.IsProjected())
3498 : {
3499 8 : pszAuthorityCode = oSRS.GetAuthorityCode("PROJCS");
3500 8 : pszAuthorityName = oSRS.GetAuthorityName("PROJCS");
3501 : }
3502 17 : else if (oSRS.IsGeographic())
3503 : {
3504 17 : pszAuthorityCode = oSRS.GetAuthorityCode("GEOGCS");
3505 17 : pszAuthorityName = oSRS.GetAuthorityName("GEOGCS");
3506 : }
3507 :
3508 25 : if (pszAuthorityName != nullptr && EQUAL(pszAuthorityName, "EPSG") &&
3509 9 : pszAuthorityCode != nullptr && atoi(pszAuthorityCode) > 0)
3510 9 : nEPSGCode = (UINT32)atoi(pszAuthorityCode);
3511 :
3512 25 : if (nEPSGCode != 0)
3513 : {
3514 9 : char *pszEPSGProj = nullptr, *pszEPSGDatum = nullptr;
3515 : CNCSError oErr = CNCSJP2FileView::GetProjectionAndDatum(
3516 9 : atoi(pszAuthorityCode), &pszEPSGProj, &pszEPSGDatum);
3517 :
3518 18 : CPLDebug("ECW", "GetGDTProjDat(%d) = %s/%s", atoi(pszAuthorityCode),
3519 9 : pszEPSGProj ? pszEPSGProj : "(null)",
3520 9 : pszEPSGDatum ? pszEPSGDatum : "(null)");
3521 :
3522 18 : if (oErr.GetErrorNumber() == NCS_SUCCESS && pszEPSGProj != nullptr &&
3523 9 : pszEPSGDatum != nullptr)
3524 : {
3525 9 : strncpy(pszProjection, pszEPSGProj, nProjectionLen);
3526 9 : strncpy(pszDatum, pszEPSGDatum, nDatumLen);
3527 9 : pszProjection[nProjectionLen - 1] = 0;
3528 9 : pszDatum[nDatumLen - 1] = 0;
3529 9 : NCSFree(pszEPSGProj);
3530 9 : NCSFree(pszEPSGDatum);
3531 9 : return TRUE;
3532 : }
3533 :
3534 0 : NCSFree(pszEPSGProj);
3535 0 : NCSFree(pszEPSGDatum);
3536 : }
3537 :
3538 : /* -------------------------------------------------------------------- */
3539 : /* Fallback to translating based on the ecw_cs.wkt file, and */
3540 : /* various jiffy rules. */
3541 : /* -------------------------------------------------------------------- */
3542 :
3543 16 : return oSRS.exportToERM(pszProjection, pszDatum, pszUnits) == OGRERR_NONE;
3544 : }
3545 :
3546 : /************************************************************************/
3547 : /* ECWTranslateToCellSizeUnits() */
3548 : /************************************************************************/
3549 :
3550 26 : CellSizeUnits ECWTranslateToCellSizeUnits(const char *pszUnits)
3551 : {
3552 26 : if (EQUAL(pszUnits, "METERS"))
3553 24 : return ECW_CELL_UNITS_METERS;
3554 2 : else if (EQUAL(pszUnits, "DEGREES"))
3555 0 : return ECW_CELL_UNITS_DEGREES;
3556 2 : else if (EQUAL(pszUnits, "FEET"))
3557 2 : return ECW_CELL_UNITS_FEET;
3558 0 : else if (EQUAL(pszUnits, "UNKNOWN"))
3559 0 : return ECW_CELL_UNITS_UNKNOWN;
3560 0 : else if (EQUAL(pszUnits, "INVALID"))
3561 0 : return ECW_CELL_UNITS_INVALID;
3562 : else
3563 : {
3564 0 : CPLError(CE_Warning, CPLE_AppDefined,
3565 : "Unrecognized value for UNITS : %s", pszUnits);
3566 0 : return ECW_CELL_UNITS_INVALID;
3567 : }
3568 : }
3569 :
3570 : /************************************************************************/
3571 : /* ECWGetColorInterpretationByName() */
3572 : /************************************************************************/
3573 :
3574 228 : GDALColorInterp ECWGetColorInterpretationByName(const char *pszName)
3575 : {
3576 228 : if (EQUAL(pszName, NCS_BANDDESC_AllOpacity))
3577 7 : return GCI_AlphaBand;
3578 221 : else if (EQUAL(pszName, NCS_BANDDESC_Blue))
3579 51 : return GCI_BlueBand;
3580 170 : else if (EQUAL(pszName, NCS_BANDDESC_Green))
3581 51 : return GCI_GreenBand;
3582 119 : else if (EQUAL(pszName, NCS_BANDDESC_Red))
3583 51 : return GCI_RedBand;
3584 68 : else if (EQUAL(pszName, NCS_BANDDESC_Greyscale))
3585 0 : return GCI_GrayIndex;
3586 68 : else if (EQUAL(pszName, NCS_BANDDESC_GreyscaleOpacity))
3587 0 : return GCI_AlphaBand;
3588 68 : return GCI_Undefined;
3589 : }
3590 :
3591 : /************************************************************************/
3592 : /* ECWGetColorInterpretationName() */
3593 : /************************************************************************/
3594 :
3595 58 : const char *ECWGetColorInterpretationName(GDALColorInterp eColorInterpretation,
3596 : int nBandNumber)
3597 : {
3598 58 : const char *pszResult = nullptr;
3599 58 : switch (eColorInterpretation)
3600 : {
3601 0 : case GCI_AlphaBand:
3602 0 : pszResult = NCS_BANDDESC_AllOpacity;
3603 0 : break;
3604 16 : case GCI_GrayIndex:
3605 16 : pszResult = NCS_BANDDESC_Greyscale;
3606 16 : break;
3607 12 : case GCI_RedBand:
3608 : case GCI_GreenBand:
3609 : case GCI_BlueBand:
3610 12 : pszResult = GDALGetColorInterpretationName(eColorInterpretation);
3611 12 : break;
3612 30 : case GCI_Undefined:
3613 30 : if (nBandNumber == 0)
3614 : {
3615 20 : pszResult = "Red";
3616 : }
3617 10 : else if (nBandNumber == 1)
3618 : {
3619 4 : pszResult = "Green";
3620 : }
3621 6 : else if (nBandNumber == 2)
3622 : {
3623 3 : pszResult = "Blue";
3624 : }
3625 : else
3626 : {
3627 3 : pszResult = CPLSPrintf(NCS_BANDDESC_Band, nBandNumber + 1);
3628 : }
3629 30 : break;
3630 0 : default:
3631 0 : pszResult = CPLSPrintf(NCS_BANDDESC_Band, nBandNumber + 1);
3632 0 : break;
3633 : }
3634 58 : return pszResult;
3635 : }
3636 :
3637 : /************************************************************************/
3638 : /* ECWGetColorSpaceName() */
3639 : /************************************************************************/
3640 :
3641 121 : const char *ECWGetColorSpaceName(NCSFileColorSpace colorSpace)
3642 : {
3643 121 : switch (colorSpace)
3644 : {
3645 0 : case NCSCS_NONE:
3646 0 : return "NONE";
3647 : break;
3648 52 : case NCSCS_GREYSCALE:
3649 52 : return "GREYSCALE";
3650 : break;
3651 0 : case NCSCS_YUV:
3652 0 : return "YUV";
3653 : break;
3654 36 : case NCSCS_MULTIBAND:
3655 36 : return "MULTIBAND";
3656 : break;
3657 33 : case NCSCS_sRGB:
3658 33 : return "RGB";
3659 : break;
3660 0 : case NCSCS_YCbCr:
3661 0 : return "YCbCr";
3662 : break;
3663 0 : default:
3664 0 : return "unrecognized";
3665 : }
3666 : }
3667 :
3668 : /************************************************************************/
3669 : /* ECWTranslateFromCellSizeUnits() */
3670 : /************************************************************************/
3671 :
3672 71 : const char *ECWTranslateFromCellSizeUnits(CellSizeUnits eUnits)
3673 : {
3674 71 : if (eUnits == ECW_CELL_UNITS_METERS)
3675 67 : return "METERS";
3676 4 : else if (eUnits == ECW_CELL_UNITS_DEGREES)
3677 0 : return "DEGREES";
3678 4 : else if (eUnits == ECW_CELL_UNITS_FEET)
3679 4 : return "FEET";
3680 0 : else if (eUnits == ECW_CELL_UNITS_UNKNOWN)
3681 0 : return "UNKNOWN";
3682 : else
3683 0 : return "INVALID";
3684 : }
3685 :
3686 : /************************************************************************/
3687 : /* ECWInitialize() */
3688 : /* */
3689 : /* Initialize NCS library. We try to defer this as late as */
3690 : /* possible since de-initializing it seems to be expensive/slow */
3691 : /* on some system. */
3692 : /************************************************************************/
3693 :
3694 190 : void ECWInitialize()
3695 :
3696 : {
3697 190 : CPLMutexHolder oHolder(&hECWDatasetMutex);
3698 :
3699 190 : if (bNCSInitialized)
3700 184 : return;
3701 :
3702 : #ifndef _WIN32
3703 6 : NCSecwInit();
3704 : #endif
3705 6 : bNCSInitialized = TRUE;
3706 :
3707 : /* -------------------------------------------------------------------- */
3708 : /* This will disable automatic conversion of YCbCr to RGB by */
3709 : /* the toolkit. */
3710 : /* -------------------------------------------------------------------- */
3711 6 : if (!CPLTestBool(CPLGetConfigOption("CONVERT_YCBCR_TO_RGB", "YES")))
3712 0 : NCSecwSetConfig(NCSCFG_JP2_MANAGE_ICC, FALSE);
3713 : #if ECWSDK_VERSION >= 50
3714 : NCSecwSetConfig(NCSCFG_ECWP_CLIENT_HTTP_USER_AGENT,
3715 : "ECW GDAL Driver/" NCS_ECWJP2_FULL_VERSION_STRING_DOT_DEL);
3716 : #endif
3717 : /* -------------------------------------------------------------------- */
3718 : /* Initialize cache memory limit. Default is apparently 1/4 RAM. */
3719 : /* -------------------------------------------------------------------- */
3720 : const char *pszEcwCacheSize =
3721 6 : CPLGetConfigOption("GDAL_ECW_CACHE_MAXMEM", nullptr);
3722 6 : if (pszEcwCacheSize == nullptr)
3723 6 : pszEcwCacheSize = CPLGetConfigOption("ECW_CACHE_MAXMEM", nullptr);
3724 :
3725 6 : if (pszEcwCacheSize != nullptr)
3726 0 : NCSecwSetConfig(NCSCFG_CACHE_MAXMEM, (UINT32)atoi(pszEcwCacheSize));
3727 :
3728 : /* -------------------------------------------------------------------- */
3729 : /* Version 3.x and 4.x of the ECWJP2 SDK did not resolve datum and */
3730 : /* projection to EPSG code using internal mapping. */
3731 : /* Version 5.x do so we provide means to achieve old */
3732 : /* behavior. */
3733 : /* -------------------------------------------------------------------- */
3734 : #if ECWSDK_VERSION >= 50
3735 : if (CPLTestBool(CPLGetConfigOption("ECW_DO_NOT_RESOLVE_DATUM_PROJECTION",
3736 : "NO")) == TRUE)
3737 : NCSecwSetConfig(NCSCFG_PROJECTION_FORMAT,
3738 : NCS_PROJECTION_ERMAPPER_FORMAT);
3739 : #endif
3740 : /* -------------------------------------------------------------------- */
3741 : /* Allow configuration of a local cache based on configuration */
3742 : /* options. Setting the location turns things on. */
3743 : /* -------------------------------------------------------------------- */
3744 6 : const char *pszOpt = nullptr;
3745 6 : CPL_IGNORE_RET_VAL(pszOpt);
3746 :
3747 : #if ECWSDK_VERSION >= 40
3748 : pszOpt = CPLGetConfigOption("ECWP_CACHE_SIZE_MB", nullptr);
3749 : if (pszOpt)
3750 : NCSecwSetConfig(NCSCFG_ECWP_CACHE_SIZE_MB, (INT32)atoi(pszOpt));
3751 :
3752 : pszOpt = CPLGetConfigOption("ECWP_CACHE_LOCATION", nullptr);
3753 : if (pszOpt)
3754 : {
3755 : NCSecwSetConfig(NCSCFG_ECWP_CACHE_LOCATION, pszOpt);
3756 : NCSecwSetConfig(NCSCFG_ECWP_CACHE_ENABLED, (BOOLEAN)TRUE);
3757 : }
3758 : #endif
3759 :
3760 : /* -------------------------------------------------------------------- */
3761 : /* Various other configuration items. */
3762 : /* -------------------------------------------------------------------- */
3763 6 : pszOpt = CPLGetConfigOption("ECWP_BLOCKING_TIME_MS", nullptr);
3764 6 : if (pszOpt)
3765 0 : NCSecwSetConfig(NCSCFG_BLOCKING_TIME_MS, (NCSTimeStampMs)atoi(pszOpt));
3766 :
3767 : // I believe 10s means we wait for complete data back from
3768 : // ECWP almost all the time which is good for our blocking model.
3769 6 : pszOpt = CPLGetConfigOption("ECWP_REFRESH_TIME_MS", "10000");
3770 6 : if (pszOpt)
3771 6 : NCSecwSetConfig(NCSCFG_REFRESH_TIME_MS, (NCSTimeStampMs)atoi(pszOpt));
3772 :
3773 6 : pszOpt = CPLGetConfigOption("ECW_TEXTURE_DITHER", nullptr);
3774 6 : if (pszOpt)
3775 0 : NCSecwSetConfig(NCSCFG_TEXTURE_DITHER, (BOOLEAN)CPLTestBool(pszOpt));
3776 :
3777 6 : pszOpt = CPLGetConfigOption("ECW_FORCE_FILE_REOPEN", nullptr);
3778 6 : if (pszOpt)
3779 0 : NCSecwSetConfig(NCSCFG_FORCE_FILE_REOPEN, (BOOLEAN)CPLTestBool(pszOpt));
3780 :
3781 6 : pszOpt = CPLGetConfigOption("ECW_CACHE_MAXOPEN", nullptr);
3782 6 : if (pszOpt)
3783 0 : NCSecwSetConfig(NCSCFG_CACHE_MAXOPEN, (UINT32)atoi(pszOpt));
3784 :
3785 : #if ECWSDK_VERSION >= 40
3786 : pszOpt = CPLGetConfigOption("ECW_AUTOGEN_J2I", nullptr);
3787 : if (pszOpt)
3788 : NCSecwSetConfig(NCSCFG_JP2_AUTOGEN_J2I, (BOOLEAN)CPLTestBool(pszOpt));
3789 :
3790 : pszOpt = CPLGetConfigOption("ECW_OPTIMIZE_USE_NEAREST_NEIGHBOUR", nullptr);
3791 : if (pszOpt)
3792 : NCSecwSetConfig(NCSCFG_OPTIMIZE_USE_NEAREST_NEIGHBOUR,
3793 : (BOOLEAN)CPLTestBool(pszOpt));
3794 :
3795 : pszOpt = CPLGetConfigOption("ECW_RESILIENT_DECODING", nullptr);
3796 : if (pszOpt)
3797 : NCSecwSetConfig(NCSCFG_RESILIENT_DECODING,
3798 : (BOOLEAN)CPLTestBool(pszOpt));
3799 : #endif
3800 : }
3801 :
3802 : /************************************************************************/
3803 : /* GDALDeregister_ECW() */
3804 : /************************************************************************/
3805 :
3806 9 : static void GDALDeregister_ECW(GDALDriver *)
3807 :
3808 : {
3809 : /* For unknown reason, this cleanup can take up to 3 seconds (see #3134) for
3810 : * SDK 3.3. */
3811 : /* Not worth it */
3812 : #if ECWSDK_VERSION >= 50
3813 : #ifndef _WIN32
3814 : if (bNCSInitialized)
3815 : {
3816 : bNCSInitialized = FALSE;
3817 : NCSecwShutdown();
3818 : }
3819 : #endif
3820 : #endif
3821 :
3822 9 : if (hECWDatasetMutex != nullptr)
3823 : {
3824 4 : CPLDestroyMutex(hECWDatasetMutex);
3825 4 : hECWDatasetMutex = nullptr;
3826 : }
3827 9 : }
3828 :
3829 : #if ECWSDK_VERSION < 40
3830 : namespace
3831 : {
3832 78 : NCSError NCS_CALL EcwFileOpenForReadACB(char *szFileName, void **ppClientData)
3833 : {
3834 78 : *ppClientData = VSIFOpenL(szFileName, "rb");
3835 78 : if (*ppClientData == nullptr)
3836 : {
3837 15 : return NCS_FILE_OPEN_FAILED;
3838 : }
3839 : else
3840 : {
3841 63 : return NCS_SUCCESS;
3842 : }
3843 : }
3844 :
3845 0 : NCSError NCS_CALL EcwFileOpenForReadWCB(wchar_t *wszFileName,
3846 : void **ppClientData)
3847 : {
3848 : char *szFileName =
3849 0 : CPLRecodeFromWChar(wszFileName, CPL_ENC_UCS2, CPL_ENC_UTF8);
3850 0 : *ppClientData = VSIFOpenL(szFileName, "rb");
3851 0 : CPLFree(szFileName);
3852 0 : if (*ppClientData == nullptr)
3853 : {
3854 0 : return NCS_FILE_OPEN_FAILED;
3855 : }
3856 : else
3857 : {
3858 0 : return NCS_SUCCESS;
3859 : }
3860 : }
3861 :
3862 63 : NCSError NCS_CALL EcwFileCloseCB(void *pClientData)
3863 : {
3864 63 : if (0 == VSIFCloseL(reinterpret_cast<VSILFILE *>(pClientData)))
3865 : {
3866 63 : return NCS_SUCCESS;
3867 : }
3868 : else
3869 : {
3870 0 : return NCS_FILE_CLOSE_ERROR;
3871 : }
3872 : }
3873 :
3874 1449 : NCSError NCS_CALL EcwFileReadCB(void *pClientData, void *pBuffer,
3875 : UINT32 nLength)
3876 : {
3877 1449 : if (nLength == VSIFReadL(pBuffer, 1, nLength,
3878 : reinterpret_cast<VSILFILE *>(pClientData)))
3879 : {
3880 1449 : return NCS_SUCCESS;
3881 : }
3882 : else
3883 : {
3884 0 : return NCS_FILE_IO_ERROR;
3885 : }
3886 : }
3887 :
3888 196 : NCSError NCS_CALL EcwFileSeekCB(void *pClientData, UINT64 nOffset)
3889 : {
3890 196 : if (0 ==
3891 196 : VSIFSeekL(reinterpret_cast<VSILFILE *>(pClientData), nOffset, SEEK_SET))
3892 : {
3893 196 : return NCS_SUCCESS;
3894 : }
3895 : else
3896 : {
3897 0 : return NCS_FILE_SEEK_ERROR;
3898 : }
3899 : }
3900 :
3901 96 : NCSError NCS_CALL EcwFileTellCB(void *pClientData, UINT64 *pOffset)
3902 : {
3903 96 : *pOffset = VSIFTellL(reinterpret_cast<VSILFILE *>(pClientData));
3904 96 : return NCS_SUCCESS;
3905 : }
3906 : } // namespace
3907 : #endif // ECWSDK_VERSION < 40
3908 :
3909 : /************************************************************************/
3910 : /* GDALRegister_ECW() */
3911 : /************************************************************************/
3912 :
3913 13 : void GDALRegister_ECW()
3914 :
3915 : {
3916 13 : if (!GDAL_CHECK_VERSION("ECW driver"))
3917 0 : return;
3918 :
3919 13 : if (GDALGetDriverByName(ECW_DRIVER_NAME) != nullptr)
3920 0 : return;
3921 : #if ECWSDK_VERSION < 40
3922 13 : CNCSJPCFileIOStream::SetIOCallbacks(
3923 : EcwFileOpenForReadACB, EcwFileOpenForReadWCB, EcwFileCloseCB,
3924 : EcwFileReadCB, EcwFileSeekCB, EcwFileTellCB);
3925 : #endif // ECWSDK_VERSION < 40
3926 13 : GDALDriver *poDriver = new GDALDriver();
3927 :
3928 13 : ECWDriverSetCommonMetadata(poDriver);
3929 13 : poDriver->pfnOpen = ECWDataset::OpenECW;
3930 13 : poDriver->pfnUnloadDriver = GDALDeregister_ECW;
3931 : #ifdef HAVE_COMPRESS
3932 : // The create method does not work with SDK 3.3 ( crash in
3933 : // CNCSJP2FileView::WriteLineBIL() due to m_pFile being nullptr ).
3934 : #if ECWSDK_VERSION >= 50
3935 : poDriver->pfnCreate = ECWCreateECW;
3936 : #endif
3937 13 : poDriver->pfnCreateCopy = ECWCreateCopyECW;
3938 : #endif
3939 :
3940 13 : GetGDALDriverManager()->RegisterDriver(poDriver);
3941 : }
3942 :
3943 : /************************************************************************/
3944 : /* GDALRegister_ECW_JP2ECW() */
3945 : /* */
3946 : /* This function exists so that when built as a plugin, there */
3947 : /* is a function that will register both drivers. */
3948 : /************************************************************************/
3949 :
3950 13 : void GDALRegister_ECW_JP2ECW()
3951 :
3952 : {
3953 13 : GDALRegister_ECW();
3954 13 : GDALRegister_JP2ECW();
3955 13 : }
3956 :
3957 : /************************************************************************/
3958 : /* ECWDatasetOpenJPEG2000() */
3959 : /************************************************************************/
3960 29 : GDALDataset *ECWDatasetOpenJPEG2000(GDALOpenInfo *poOpenInfo)
3961 : {
3962 29 : return ECWDataset::OpenJPEG2000(poOpenInfo);
3963 : }
3964 :
3965 : /************************************************************************/
3966 : /* GDALRegister_JP2ECW() */
3967 : /************************************************************************/
3968 13 : void GDALRegister_JP2ECW()
3969 :
3970 : {
3971 13 : if (!GDAL_CHECK_VERSION("JP2ECW driver"))
3972 0 : return;
3973 :
3974 13 : if (GDALGetDriverByName(JP2ECW_DRIVER_NAME) != nullptr)
3975 0 : return;
3976 :
3977 13 : GDALDriver *poDriver = new GDALDriver();
3978 13 : JP2ECWDriverSetCommonMetadata(poDriver);
3979 13 : poDriver->pfnOpen = ECWDataset::OpenJPEG2000;
3980 : #ifdef HAVE_COMPRESS
3981 13 : poDriver->pfnCreate = ECWCreateJPEG2000;
3982 13 : poDriver->pfnCreateCopy = ECWCreateCopyJPEG2000;
3983 : #endif
3984 :
3985 13 : GetGDALDriverManager()->RegisterDriver(poDriver);
3986 : }
|