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