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 : CSLConstList 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 : const int nXOff = nBlockXOff * nBlockXSize;
1061 1 : const int nXSize = std::min(nBlockXSize, nRasterXSize - nXOff);
1062 1 : const int nYOff = nBlockYOff * nBlockYSize;
1063 1 : const int nYSize = std::min(nBlockYSize, nRasterYSize - nYOff);
1064 :
1065 1 : const GSpacing nPixelSpace = GDALGetDataTypeSizeBytes(eDataType);
1066 1 : const GSpacing nLineSpace = nPixelSpace * nBlockXSize;
1067 :
1068 : GDALRasterIOExtraArg sExtraArg;
1069 1 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
1070 :
1071 1 : return IRasterIO(GF_Read, nXOff, nYOff, nXSize, nYSize, pImage, nXSize,
1072 2 : nYSize, eDataType, nPixelSpace, nLineSpace, &sExtraArg);
1073 : }
1074 :
1075 : /************************************************************************/
1076 : /* ==================================================================== */
1077 : /* ECWDataset */
1078 : /* ==================================================================== */
1079 : /************************************************************************/
1080 :
1081 : /************************************************************************/
1082 : /* ECWDataset() */
1083 : /************************************************************************/
1084 :
1085 123 : ECWDataset::ECWDataset(int bIsJPEG2000In)
1086 :
1087 : {
1088 123 : this->bIsJPEG2000 = bIsJPEG2000In;
1089 123 : bUsingCustomStream = FALSE;
1090 123 : poFileView = nullptr;
1091 123 : bWinActive = FALSE;
1092 123 : panWinBandList = nullptr;
1093 123 : eRasterDataType = GDT_UInt8;
1094 123 : papszGMLMetadata = nullptr;
1095 :
1096 123 : bHdrDirty = FALSE;
1097 123 : bGeoTransformChanged = FALSE;
1098 123 : bProjectionChanged = FALSE;
1099 123 : bProjCodeChanged = FALSE;
1100 123 : bDatumCodeChanged = FALSE;
1101 123 : bUnitsCodeChanged = FALSE;
1102 :
1103 123 : bUseOldBandRasterIOImplementation = FALSE;
1104 : #if ECWSDK_VERSION >= 50
1105 :
1106 : pStatistics = nullptr;
1107 : bStatisticsDirty = FALSE;
1108 : bStatisticsInitialized = FALSE;
1109 : bFileMetaDataDirty = FALSE;
1110 :
1111 : #endif
1112 :
1113 123 : sCachedMultiBandIO.bEnabled = FALSE;
1114 123 : sCachedMultiBandIO.nBandsTried = 0;
1115 123 : sCachedMultiBandIO.nXOff = 0;
1116 123 : sCachedMultiBandIO.nYOff = 0;
1117 123 : sCachedMultiBandIO.nXSize = 0;
1118 123 : sCachedMultiBandIO.nYSize = 0;
1119 123 : sCachedMultiBandIO.nBufXSize = 0;
1120 123 : sCachedMultiBandIO.nBufYSize = 0;
1121 123 : sCachedMultiBandIO.eBufType = GDT_Unknown;
1122 123 : sCachedMultiBandIO.pabyData = nullptr;
1123 :
1124 123 : bPreventCopyingSomeMetadata = FALSE;
1125 :
1126 123 : nBandIndexToPromoteTo8Bit = -1;
1127 :
1128 123 : poDriver =
1129 123 : (GDALDriver *)GDALGetDriverByName(bIsJPEG2000 ? "JP2ECW" : "ECW");
1130 :
1131 123 : psFileInfo = nullptr;
1132 123 : eNCSRequestDataType = NCSCT_UINT8;
1133 123 : nWinXOff = 0;
1134 123 : nWinYOff = 0;
1135 123 : nWinXSize = 0;
1136 123 : nWinYSize = 0;
1137 123 : nWinBufXSize = 0;
1138 123 : nWinBufYSize = 0;
1139 123 : nWinBandCount = 0;
1140 123 : nWinBufLoaded = FALSE;
1141 123 : papCurLineBuf = nullptr;
1142 :
1143 123 : m_nAdviseReadXOff = -1;
1144 123 : m_nAdviseReadYOff = -1;
1145 123 : m_nAdviseReadXSize = -1;
1146 123 : m_nAdviseReadYSize = -1;
1147 123 : m_nAdviseReadBufXSize = -1;
1148 123 : m_nAdviseReadBufYSize = -1;
1149 123 : m_nAdviseReadBandCount = -1;
1150 123 : m_panAdviseReadBandList = nullptr;
1151 123 : }
1152 :
1153 : /************************************************************************/
1154 : /* ~ECWDataset() */
1155 : /************************************************************************/
1156 :
1157 246 : ECWDataset::~ECWDataset()
1158 :
1159 : {
1160 123 : GDALPamDataset::FlushCache(true);
1161 123 : CleanupWindow();
1162 :
1163 : #if ECWSDK_VERSION >= 50
1164 : NCSFileMetaData *pFileMetaDataCopy = nullptr;
1165 : if (bFileMetaDataDirty)
1166 : {
1167 : NCSCopyMetaData(&pFileMetaDataCopy, psFileInfo->pFileMetaData);
1168 : }
1169 : #endif
1170 :
1171 : /* -------------------------------------------------------------------- */
1172 : /* Release / dereference iostream. */
1173 : /* -------------------------------------------------------------------- */
1174 : // The underlying iostream of the CNCSJP2FileView (poFileView) object may
1175 : // also be the underlying iostream of other CNCSJP2FileView (poFileView)
1176 : // objects. Consequently, when we delete the CNCSJP2FileView (poFileView)
1177 : // object, we must decrement the nFileViewCount attribute of the underlying
1178 : // VSIIOStream object, and only delete the VSIIOStream object when
1179 : // nFileViewCount is equal to zero.
1180 :
1181 246 : CPLMutexHolder oHolder(&hECWDatasetMutex);
1182 :
1183 123 : if (poFileView != nullptr)
1184 : {
1185 : #if ECWSDK_VERSION >= 55
1186 : delete poFileView;
1187 : #else
1188 123 : VSIIOStream *poUnderlyingIOStream = (VSIIOStream *)nullptr;
1189 :
1190 123 : if (bUsingCustomStream)
1191 : {
1192 52 : poUnderlyingIOStream = ((VSIIOStream *)(poFileView->GetStream()));
1193 : }
1194 123 : delete poFileView;
1195 :
1196 123 : if (bUsingCustomStream)
1197 : {
1198 52 : if (--poUnderlyingIOStream->nFileViewCount == 0)
1199 52 : delete poUnderlyingIOStream;
1200 : }
1201 : #endif
1202 123 : poFileView = nullptr;
1203 : }
1204 :
1205 : /* WriteHeader() must be called after closing the file handle to work */
1206 : /* on Windows */
1207 123 : if (bHdrDirty)
1208 4 : WriteHeader();
1209 : #if ECWSDK_VERSION >= 50
1210 : if (bStatisticsDirty)
1211 : {
1212 : StatisticsWrite();
1213 : }
1214 : CleanupStatistics();
1215 :
1216 : if (bFileMetaDataDirty)
1217 : {
1218 : WriteFileMetaData(pFileMetaDataCopy);
1219 : NCSFreeMetaData(pFileMetaDataCopy);
1220 : }
1221 : #endif
1222 :
1223 123 : CSLDestroy(papszGMLMetadata);
1224 :
1225 123 : CPLFree(sCachedMultiBandIO.pabyData);
1226 :
1227 123 : CPLFree(m_panAdviseReadBandList);
1228 246 : }
1229 :
1230 : #if ECWSDK_VERSION >= 50
1231 :
1232 : /************************************************************************/
1233 : /* StatisticsEnsureInitialized() */
1234 : /************************************************************************/
1235 :
1236 : NCS::CError ECWDataset::StatisticsEnsureInitialized()
1237 : {
1238 : if (bStatisticsInitialized == TRUE)
1239 : {
1240 : return NCS_SUCCESS;
1241 : }
1242 :
1243 : NCS::CError error = poFileView->GetClientStatistics(&pStatistics);
1244 : if (error.Success())
1245 : {
1246 : bStatisticsInitialized = TRUE;
1247 : }
1248 : return error;
1249 : }
1250 :
1251 : /************************************************************************/
1252 : /* StatisticsWrite() */
1253 : /************************************************************************/
1254 :
1255 : NCS::CError ECWDataset::StatisticsWrite()
1256 : {
1257 : CPLDebug("ECW", "In StatisticsWrite()");
1258 : NCSFileView *view = NCSEcwEditOpen(GetDescription());
1259 : NCS::CError error;
1260 : if (view != nullptr)
1261 : {
1262 : error = NCSEcwEditSetStatistics(view, pStatistics);
1263 : if (error.Success())
1264 : {
1265 : error = NCSEcwEditFlushAll(view);
1266 : if (error.Success())
1267 : {
1268 : error = NCSEcwEditClose(view);
1269 : }
1270 : }
1271 : }
1272 :
1273 : bStatisticsDirty = FALSE;
1274 :
1275 : return error;
1276 : }
1277 :
1278 : /************************************************************************/
1279 : /* CleanupStatistics() */
1280 : /************************************************************************/
1281 :
1282 : void ECWDataset::CleanupStatistics()
1283 : {
1284 : if (bStatisticsInitialized == TRUE && pStatistics != nullptr)
1285 : {
1286 : NCSEcwFreeStatistics(pStatistics);
1287 : }
1288 : }
1289 :
1290 : #endif // #if ECWSDK_VERSION>=50
1291 :
1292 : /************************************************************************/
1293 : /* SetGeoTransform() */
1294 : /************************************************************************/
1295 :
1296 7 : CPLErr ECWDataset::SetGeoTransform(const GDALGeoTransform >)
1297 : {
1298 7 : if (bIsJPEG2000 || eAccess == GA_ReadOnly)
1299 5 : return GDALPamDataset::SetGeoTransform(gt);
1300 :
1301 2 : if (!bGeoTransformValid || gt != m_gt)
1302 : {
1303 2 : m_gt = gt;
1304 2 : bGeoTransformValid = TRUE;
1305 2 : bHdrDirty = TRUE;
1306 2 : bGeoTransformChanged = TRUE;
1307 : }
1308 :
1309 2 : return CE_None;
1310 : }
1311 :
1312 : /************************************************************************/
1313 : /* SetSpatialRef() */
1314 : /************************************************************************/
1315 :
1316 7 : CPLErr ECWDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
1317 : {
1318 7 : if (bIsJPEG2000 || eAccess == GA_ReadOnly)
1319 5 : return GDALPamDataset::SetSpatialRef(poSRS);
1320 :
1321 4 : if (!((m_oSRS.IsEmpty() && poSRS == nullptr) ||
1322 2 : (!m_oSRS.IsEmpty() && poSRS != nullptr && m_oSRS.IsSame(poSRS))))
1323 : {
1324 2 : m_oSRS.Clear();
1325 2 : if (poSRS)
1326 2 : m_oSRS = *poSRS;
1327 :
1328 2 : bHdrDirty = TRUE;
1329 2 : bProjectionChanged = TRUE;
1330 : }
1331 :
1332 2 : return CE_None;
1333 : }
1334 :
1335 : /************************************************************************/
1336 : /* SetMetadataItem() */
1337 : /************************************************************************/
1338 :
1339 4 : CPLErr ECWDataset::SetMetadataItem(const char *pszName, const char *pszValue,
1340 : const char *pszDomain)
1341 : {
1342 4 : if (!bIsJPEG2000 && eAccess == GA_Update &&
1343 3 : (pszDomain == nullptr || EQUAL(pszDomain, "") ||
1344 3 : (pszDomain != nullptr && EQUAL(pszDomain, "ECW"))) &&
1345 3 : pszName != nullptr &&
1346 3 : (strcmp(pszName, "PROJ") == 0 || strcmp(pszName, "DATUM") == 0 ||
1347 1 : strcmp(pszName, "UNITS") == 0))
1348 : {
1349 3 : CPLString osNewVal = pszValue ? pszValue : "";
1350 3 : if (osNewVal.size() > 31)
1351 0 : osNewVal.resize(31);
1352 3 : if (strcmp(pszName, "PROJ") == 0)
1353 : {
1354 1 : bProjCodeChanged = (osNewVal != m_osProjCode);
1355 1 : m_osProjCode = std::move(osNewVal);
1356 1 : bHdrDirty |= bProjCodeChanged;
1357 : }
1358 2 : else if (strcmp(pszName, "DATUM") == 0)
1359 : {
1360 1 : bDatumCodeChanged |= (osNewVal != m_osDatumCode) ? TRUE : FALSE;
1361 1 : m_osDatumCode = std::move(osNewVal);
1362 1 : bHdrDirty |= bDatumCodeChanged;
1363 : }
1364 : else
1365 : {
1366 1 : bUnitsCodeChanged |= (osNewVal != m_osUnitsCode) ? TRUE : FALSE;
1367 1 : m_osUnitsCode = std::move(osNewVal);
1368 1 : bHdrDirty |= bUnitsCodeChanged;
1369 : }
1370 3 : return CE_None;
1371 : }
1372 : #if ECWSDK_VERSION >= 50
1373 : else if (psFileInfo != nullptr && psFileInfo->nFormatVersion >= 3 &&
1374 : eAccess == GA_Update &&
1375 : (pszDomain == nullptr || EQUAL(pszDomain, "")) &&
1376 : pszName != nullptr && STARTS_WITH(pszName, "FILE_METADATA_"))
1377 : {
1378 : bFileMetaDataDirty = TRUE;
1379 :
1380 : if (psFileInfo->pFileMetaData == nullptr)
1381 : NCSInitMetaData(&(psFileInfo->pFileMetaData));
1382 :
1383 : if (strcmp(pszName, "FILE_METADATA_CLASSIFICATION") == 0)
1384 : {
1385 : NCSFree(psFileInfo->pFileMetaData->sClassification);
1386 : psFileInfo->pFileMetaData->sClassification =
1387 : pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
1388 : return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1389 : }
1390 : else if (strcmp(pszName, "FILE_METADATA_ACQUISITION_DATE") == 0)
1391 : {
1392 : NCSFree(psFileInfo->pFileMetaData->sAcquisitionDate);
1393 : psFileInfo->pFileMetaData->sAcquisitionDate =
1394 : pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
1395 : return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1396 : }
1397 : else if (strcmp(pszName, "FILE_METADATA_ACQUISITION_SENSOR_NAME") == 0)
1398 : {
1399 : NCSFree(psFileInfo->pFileMetaData->sAcquisitionSensorName);
1400 : psFileInfo->pFileMetaData->sAcquisitionSensorName =
1401 : pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
1402 : return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1403 : }
1404 : else if (strcmp(pszName, "FILE_METADATA_COMPRESSION_SOFTWARE") == 0)
1405 : {
1406 : NCSFree(psFileInfo->pFileMetaData->sCompressionSoftware);
1407 : psFileInfo->pFileMetaData->sCompressionSoftware =
1408 : pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
1409 : return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1410 : }
1411 : else if (strcmp(pszName, "FILE_METADATA_AUTHOR") == 0)
1412 : {
1413 : NCSFree(psFileInfo->pFileMetaData->sAuthor);
1414 : psFileInfo->pFileMetaData->sAuthor =
1415 : pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
1416 : return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1417 : }
1418 : else if (strcmp(pszName, "FILE_METADATA_COPYRIGHT") == 0)
1419 : {
1420 : NCSFree(psFileInfo->pFileMetaData->sCopyright);
1421 : psFileInfo->pFileMetaData->sCopyright =
1422 : pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
1423 : return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1424 : }
1425 : else if (strcmp(pszName, "FILE_METADATA_COMPANY") == 0)
1426 : {
1427 : NCSFree(psFileInfo->pFileMetaData->sCompany);
1428 : psFileInfo->pFileMetaData->sCompany =
1429 : pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
1430 : return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1431 : }
1432 : else if (strcmp(pszName, "FILE_METADATA_EMAIL") == 0)
1433 : {
1434 : NCSFree(psFileInfo->pFileMetaData->sEmail);
1435 : psFileInfo->pFileMetaData->sEmail =
1436 : pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
1437 : return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1438 : }
1439 : else if (strcmp(pszName, "FILE_METADATA_ADDRESS") == 0)
1440 : {
1441 : NCSFree(psFileInfo->pFileMetaData->sAddress);
1442 : psFileInfo->pFileMetaData->sAddress =
1443 : pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
1444 : return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1445 : }
1446 : else if (strcmp(pszName, "FILE_METADATA_TELEPHONE") == 0)
1447 : {
1448 : NCSFree(psFileInfo->pFileMetaData->sTelephone);
1449 : psFileInfo->pFileMetaData->sTelephone =
1450 : pszValue ? NCSStrDupT(NCS::CString(pszValue).c_str()) : nullptr;
1451 : return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1452 : }
1453 : else
1454 : {
1455 : return GDALPamDataset::SetMetadataItem(pszName, pszValue,
1456 : pszDomain);
1457 : }
1458 : }
1459 : #endif
1460 : else
1461 1 : return GDALPamDataset::SetMetadataItem(pszName, pszValue, pszDomain);
1462 : }
1463 :
1464 : /************************************************************************/
1465 : /* SetMetadata() */
1466 : /************************************************************************/
1467 :
1468 3 : CPLErr ECWDataset::SetMetadata(CSLConstList papszMetadata,
1469 : const char *pszDomain)
1470 : {
1471 : /* The bPreventCopyingSomeMetadata is set by ECWCreateCopy() */
1472 : /* just before calling poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT ); */
1473 3 : if (bPreventCopyingSomeMetadata &&
1474 1 : (pszDomain == nullptr || EQUAL(pszDomain, "")))
1475 : {
1476 1 : char **papszMetadataDup = nullptr;
1477 1 : CSLConstList papszIter = papszMetadata;
1478 2 : while (*papszIter)
1479 : {
1480 1 : char *pszKey = nullptr;
1481 1 : CPLParseNameValue(*papszIter, &pszKey);
1482 : /* Remove a few metadata item from the source that we don't want in
1483 : */
1484 : /* the target metadata */
1485 1 : if (pszKey != nullptr &&
1486 1 : (EQUAL(pszKey, "VERSION") ||
1487 1 : EQUAL(pszKey, "COMPRESSION_RATE_TARGET") ||
1488 1 : EQUAL(pszKey, "COMPRESSION_RATE_ACTUAL") ||
1489 1 : EQUAL(pszKey, "CLOCKWISE_ROTATION_DEG") ||
1490 1 : EQUAL(pszKey, "COLORSPACE") ||
1491 1 : EQUAL(pszKey, "COMPRESSION_DATE") ||
1492 1 : STARTS_WITH_CI(pszKey, "FILE_METADATA_")))
1493 : {
1494 : /* do nothing */
1495 : }
1496 : else
1497 : {
1498 1 : papszMetadataDup = CSLAddString(papszMetadataDup, *papszIter);
1499 : }
1500 1 : CPLFree(pszKey);
1501 1 : papszIter++;
1502 : }
1503 :
1504 1 : bPreventCopyingSomeMetadata = FALSE;
1505 1 : CPLErr eErr = SetMetadata(papszMetadataDup, pszDomain);
1506 1 : bPreventCopyingSomeMetadata = TRUE;
1507 1 : CSLDestroy(papszMetadataDup);
1508 1 : return eErr;
1509 : }
1510 :
1511 2 : if (((pszDomain == nullptr || EQUAL(pszDomain, "") ||
1512 4 : EQUAL(pszDomain, "ECW")) &&
1513 2 : (CSLFetchNameValue(papszMetadata, "PROJ") != nullptr ||
1514 2 : CSLFetchNameValue(papszMetadata, "DATUM") != nullptr ||
1515 2 : CSLFetchNameValue(papszMetadata, "UNITS") != nullptr))
1516 : #if ECWSDK_VERSION >= 50
1517 : ||
1518 : (psFileInfo != nullptr && psFileInfo->nFormatVersion >= 3 &&
1519 : eAccess == GA_Update &&
1520 : (pszDomain == nullptr || EQUAL(pszDomain, "")) &&
1521 : (CSLFetchNameValue(papszMetadata, "FILE_METADATA_CLASSIFICATION") !=
1522 : nullptr ||
1523 : CSLFetchNameValue(papszMetadata, "FILE_METADATA_ACQUISITION_DATE") !=
1524 : nullptr ||
1525 : CSLFetchNameValue(papszMetadata,
1526 : "FILE_METADATA_ACQUISITION_SENSOR_NAME") !=
1527 : nullptr ||
1528 : CSLFetchNameValue(papszMetadata,
1529 : "FILE_METADATA_COMPRESSION_SOFTWARE") != nullptr ||
1530 : CSLFetchNameValue(papszMetadata, "FILE_METADATA_AUTHOR") != nullptr ||
1531 : CSLFetchNameValue(papszMetadata, "FILE_METADATA_COPYRIGHT") !=
1532 : nullptr ||
1533 : CSLFetchNameValue(papszMetadata, "FILE_METADATA_COMPANY") !=
1534 : nullptr ||
1535 : CSLFetchNameValue(papszMetadata, "FILE_METADATA_EMAIL") != nullptr ||
1536 : CSLFetchNameValue(papszMetadata, "FILE_METADATA_ADDRESS") !=
1537 : nullptr ||
1538 : CSLFetchNameValue(papszMetadata, "FILE_METADATA_TELEPHONE") !=
1539 : nullptr))
1540 : #endif
1541 : )
1542 : {
1543 0 : CPLStringList osNewMetadata;
1544 0 : CSLConstList papszIter = papszMetadata;
1545 0 : while (papszIter && *papszIter)
1546 : {
1547 0 : if (STARTS_WITH(*papszIter, "PROJ=") ||
1548 0 : STARTS_WITH(*papszIter, "DATUM=") ||
1549 0 : STARTS_WITH(*papszIter, "UNITS=") ||
1550 0 : (STARTS_WITH(*papszIter, "FILE_METADATA_") &&
1551 0 : strchr(*papszIter, '=') != nullptr))
1552 : {
1553 0 : char *pszKey = nullptr;
1554 0 : const char *pszValue = CPLParseNameValue(*papszIter, &pszKey);
1555 0 : SetMetadataItem(pszKey, pszValue, pszDomain);
1556 0 : CPLFree(pszKey);
1557 : }
1558 : else
1559 0 : osNewMetadata.AddString(*papszIter);
1560 0 : papszIter++;
1561 : }
1562 0 : if (!osNewMetadata.empty())
1563 0 : return GDALPamDataset::SetMetadata(osNewMetadata.List(), pszDomain);
1564 : else
1565 0 : return CE_None;
1566 : }
1567 : else
1568 2 : return GDALPamDataset::SetMetadata(papszMetadata, pszDomain);
1569 : }
1570 :
1571 : /************************************************************************/
1572 : /* WriteHeader() */
1573 : /************************************************************************/
1574 :
1575 4 : void ECWDataset::WriteHeader()
1576 : {
1577 4 : if (!bHdrDirty)
1578 0 : return;
1579 :
1580 4 : CPLAssert(eAccess == GA_Update);
1581 4 : CPLAssert(!bIsJPEG2000);
1582 :
1583 4 : bHdrDirty = FALSE;
1584 :
1585 4 : NCSEcwEditInfo *psEditInfo = nullptr;
1586 : NCSError eErr;
1587 :
1588 : /* Load original header info */
1589 : #if ECWSDK_VERSION < 50
1590 4 : eErr = NCSEcwEditReadInfo((char *)GetDescription(), &psEditInfo);
1591 : #else
1592 : eErr = NCSEcwEditReadInfo(
1593 : NCS::CString::Utf8Decode(GetDescription()).c_str(), &psEditInfo);
1594 : #endif
1595 4 : if (eErr != NCS_SUCCESS)
1596 : {
1597 0 : CPLError(CE_Failure, CPLE_AppDefined, "NCSEcwEditReadInfo() failed");
1598 0 : return;
1599 : }
1600 :
1601 : /* To avoid potential cross-heap issues, we keep the original */
1602 : /* strings, and restore them before freeing the structure */
1603 4 : char *pszOriginalCode = psEditInfo->szDatum;
1604 4 : char *pszOriginalProj = psEditInfo->szProjection;
1605 :
1606 : /* Alter the structure with user modified information */
1607 : char szProjCode[32], szDatumCode[32], szUnits[32];
1608 4 : if (bProjectionChanged)
1609 : {
1610 2 : if (ECWTranslateFromWKT(&m_oSRS, szProjCode, sizeof(szProjCode),
1611 2 : szDatumCode, sizeof(szDatumCode), szUnits))
1612 : {
1613 2 : psEditInfo->szDatum = szDatumCode;
1614 2 : psEditInfo->szProjection = szProjCode;
1615 2 : psEditInfo->eCellSizeUnits = ECWTranslateToCellSizeUnits(szUnits);
1616 2 : CPLDebug("ECW", "Rewrite DATUM : %s", psEditInfo->szDatum);
1617 2 : CPLDebug("ECW", "Rewrite PROJ : %s", psEditInfo->szProjection);
1618 2 : CPLDebug("ECW", "Rewrite UNITS : %s",
1619 : ECWTranslateFromCellSizeUnits(psEditInfo->eCellSizeUnits));
1620 : }
1621 : }
1622 :
1623 4 : if (bDatumCodeChanged)
1624 : {
1625 1 : psEditInfo->szDatum =
1626 1 : (char *)((m_osDatumCode.size()) ? m_osDatumCode.c_str() : "RAW");
1627 1 : CPLDebug("ECW", "Rewrite DATUM : %s", psEditInfo->szDatum);
1628 : }
1629 4 : if (bProjCodeChanged)
1630 : {
1631 1 : psEditInfo->szProjection =
1632 1 : (char *)((m_osProjCode.size()) ? m_osProjCode.c_str() : "RAW");
1633 1 : CPLDebug("ECW", "Rewrite PROJ : %s", psEditInfo->szProjection);
1634 : }
1635 4 : if (bUnitsCodeChanged)
1636 : {
1637 2 : psEditInfo->eCellSizeUnits =
1638 1 : ECWTranslateToCellSizeUnits(m_osUnitsCode.c_str());
1639 1 : CPLDebug("ECW", "Rewrite UNITS : %s",
1640 : ECWTranslateFromCellSizeUnits(psEditInfo->eCellSizeUnits));
1641 : }
1642 :
1643 4 : if (bGeoTransformChanged)
1644 : {
1645 2 : psEditInfo->fOriginX = m_gt.xorig;
1646 2 : psEditInfo->fCellIncrementX = m_gt.xscale;
1647 2 : psEditInfo->fOriginY = m_gt.yorig;
1648 2 : psEditInfo->fCellIncrementY = m_gt.yscale;
1649 2 : CPLDebug("ECW", "Rewrite Geotransform");
1650 : }
1651 :
1652 : /* Write modified header info */
1653 : #if ECWSDK_VERSION < 50
1654 4 : eErr = NCSEcwEditWriteInfo((char *)GetDescription(), psEditInfo, nullptr,
1655 : nullptr, nullptr);
1656 : #else
1657 : eErr =
1658 : NCSEcwEditWriteInfo(NCS::CString::Utf8Decode(GetDescription()).c_str(),
1659 : psEditInfo, nullptr, nullptr, nullptr);
1660 : #endif
1661 4 : if (eErr != NCS_SUCCESS)
1662 : {
1663 0 : CPLError(CE_Failure, CPLE_AppDefined, "NCSEcwEditWriteInfo() failed");
1664 : }
1665 :
1666 : /* Restore original pointers before free'ing */
1667 4 : psEditInfo->szDatum = pszOriginalCode;
1668 4 : psEditInfo->szProjection = pszOriginalProj;
1669 :
1670 4 : NCSEcwEditFreeInfo(psEditInfo);
1671 : }
1672 :
1673 : /************************************************************************/
1674 : /* AdviseRead() */
1675 : /************************************************************************/
1676 :
1677 249 : CPLErr ECWDataset::AdviseRead(int nXOff, int nYOff, int nXSize, int nYSize,
1678 : int nBufXSize, int nBufYSize,
1679 : CPL_UNUSED GDALDataType eDT, int nBandCount,
1680 : int *panBandList,
1681 : CPL_UNUSED CSLConstList papszOptions)
1682 : {
1683 249 : CPLDebug("ECW", "ECWDataset::AdviseRead(%d,%d,%d,%d->%d,%d)", nXOff, nYOff,
1684 : nXSize, nYSize, nBufXSize, nBufYSize);
1685 :
1686 : #if !defined(SDK_CAN_DO_SUPERSAMPLING)
1687 249 : if (nBufXSize > nXSize || nBufYSize > nYSize)
1688 : {
1689 0 : CPLError(CE_Warning, CPLE_AppDefined,
1690 : "Supersampling not directly supported by ECW toolkit,\n"
1691 : "ignoring AdviseRead() request.");
1692 0 : return CE_Warning;
1693 : }
1694 : #endif
1695 :
1696 : /* -------------------------------------------------------------------- */
1697 : /* Do some validation of parameters. */
1698 : /* -------------------------------------------------------------------- */
1699 :
1700 : CPLErr eErr;
1701 249 : int bStopProcessing = FALSE;
1702 249 : eErr = ValidateRasterIOOrAdviseReadParameters(
1703 : "AdviseRead()", &bStopProcessing, nXOff, nYOff, nXSize, nYSize,
1704 : nBufXSize, nBufYSize, nBandCount, panBandList);
1705 249 : if (eErr != CE_None || bStopProcessing)
1706 0 : return eErr;
1707 :
1708 249 : if (nBandCount > 100)
1709 : {
1710 0 : ReportError(CE_Failure, CPLE_IllegalArg,
1711 : "AdviseRead(): Too many bands : %d", nBandCount);
1712 0 : return CE_Failure;
1713 : }
1714 :
1715 249 : if (nBufXSize != nXSize || nBufYSize != nYSize)
1716 : {
1717 : // This early exit is because experimentally we found that
1718 : // performance of requesting at 50% is much slower with
1719 : // AdviseRead()...
1720 : // At least on JPEG2000 images with SDK 3.3
1721 200 : CPLDebug("ECW",
1722 : "Ignoring AdviseRead() for non full resolution request");
1723 200 : return CE_None;
1724 : }
1725 :
1726 : // We don't setup the reading window right away, in case the actual read
1727 : // pattern wouldn't be compatible of it. Which might be the case for
1728 : // example if AdviseRead() requests a full image, but we don't read by
1729 : // chunks of the full width of one or several lines
1730 49 : m_nAdviseReadXOff = nXOff;
1731 49 : m_nAdviseReadYOff = nYOff;
1732 49 : m_nAdviseReadXSize = nXSize;
1733 49 : m_nAdviseReadYSize = nYSize;
1734 49 : m_nAdviseReadBufXSize = nBufXSize;
1735 49 : m_nAdviseReadBufYSize = nBufYSize;
1736 49 : m_nAdviseReadBandCount = nBandCount;
1737 49 : CPLFree(m_panAdviseReadBandList);
1738 49 : if (panBandList)
1739 : {
1740 41 : m_panAdviseReadBandList =
1741 41 : static_cast<int *>(CPLMalloc(sizeof(int) * nBandCount));
1742 41 : memcpy(m_panAdviseReadBandList, panBandList, sizeof(int) * nBandCount);
1743 : }
1744 : else
1745 : {
1746 8 : m_panAdviseReadBandList = nullptr;
1747 : }
1748 :
1749 49 : return CE_None;
1750 : }
1751 :
1752 : /************************************************************************/
1753 : /* RunDeferredAdviseRead() */
1754 : /************************************************************************/
1755 :
1756 44 : CPLErr ECWDataset::RunDeferredAdviseRead()
1757 : {
1758 44 : CPLAssert(m_nAdviseReadXOff >= 0);
1759 :
1760 44 : const int nXOff = m_nAdviseReadXOff;
1761 44 : const int nYOff = m_nAdviseReadYOff;
1762 44 : const int nXSize = m_nAdviseReadXSize;
1763 44 : const int nYSize = m_nAdviseReadYSize;
1764 44 : const int nBufXSize = m_nAdviseReadBufXSize;
1765 44 : const int nBufYSize = m_nAdviseReadBufYSize;
1766 44 : const int nBandCount = m_nAdviseReadBandCount;
1767 44 : int *panBandList = m_panAdviseReadBandList;
1768 :
1769 44 : m_nAdviseReadXOff = -1;
1770 44 : m_nAdviseReadYOff = -1;
1771 44 : m_nAdviseReadXSize = -1;
1772 44 : m_nAdviseReadYSize = -1;
1773 44 : m_nAdviseReadBufXSize = -1;
1774 44 : m_nAdviseReadBufYSize = -1;
1775 44 : m_nAdviseReadBandCount = -1;
1776 44 : m_panAdviseReadBandList = nullptr;
1777 :
1778 : /* -------------------------------------------------------------------- */
1779 : /* Adjust band numbers to be zero based. */
1780 : /* -------------------------------------------------------------------- */
1781 : UINT32 *panAdjustedBandList =
1782 44 : (UINT32 *)CPLMalloc(sizeof(UINT32) * nBandCount);
1783 44 : nBandIndexToPromoteTo8Bit = -1;
1784 97 : for (int ii = 0; ii < nBandCount; ii++)
1785 : {
1786 53 : const int nIdx = (panBandList != nullptr) ? panBandList[ii] - 1 : ii;
1787 : ;
1788 53 : panAdjustedBandList[ii] = nIdx;
1789 53 : if (cpl::down_cast<ECWRasterBand *>(GetRasterBand(nIdx + 1))
1790 53 : ->bPromoteTo8Bit)
1791 1 : nBandIndexToPromoteTo8Bit = ii;
1792 : }
1793 :
1794 : /* -------------------------------------------------------------------- */
1795 : /* Cleanup old window cache information. */
1796 : /* -------------------------------------------------------------------- */
1797 44 : CleanupWindow();
1798 :
1799 : /* -------------------------------------------------------------------- */
1800 : /* Set the new requested window. */
1801 : /* -------------------------------------------------------------------- */
1802 44 : CNCSError oErr = poFileView->SetView(
1803 44 : nBandCount, panAdjustedBandList, nXOff, nYOff, nXOff + nXSize - 1,
1804 88 : nYOff + nYSize - 1, nBufXSize, nBufYSize);
1805 :
1806 44 : CPLFree(panAdjustedBandList);
1807 44 : if (oErr.GetErrorNumber() != NCS_SUCCESS)
1808 : {
1809 0 : ECWReportError(oErr);
1810 :
1811 0 : bWinActive = FALSE;
1812 0 : CPLFree(panBandList);
1813 0 : return CE_Failure;
1814 : }
1815 :
1816 44 : bWinActive = TRUE;
1817 :
1818 : /* -------------------------------------------------------------------- */
1819 : /* Record selected window. */
1820 : /* -------------------------------------------------------------------- */
1821 44 : nWinXOff = nXOff;
1822 44 : nWinYOff = nYOff;
1823 44 : nWinXSize = nXSize;
1824 44 : nWinYSize = nYSize;
1825 44 : nWinBufXSize = nBufXSize;
1826 44 : nWinBufYSize = nBufYSize;
1827 :
1828 44 : panWinBandList = (int *)CPLMalloc(sizeof(int) * nBandCount);
1829 44 : if (panBandList != nullptr)
1830 40 : memcpy(panWinBandList, panBandList, sizeof(int) * nBandCount);
1831 : else
1832 : {
1833 17 : for (int ii = 0; ii < nBandCount; ii++)
1834 : {
1835 13 : panWinBandList[ii] = ii + 1;
1836 : }
1837 : }
1838 44 : nWinBandCount = nBandCount;
1839 :
1840 44 : nWinBufLoaded = -1;
1841 :
1842 : /* -------------------------------------------------------------------- */
1843 : /* Allocate current scanline buffer. */
1844 : /* -------------------------------------------------------------------- */
1845 44 : papCurLineBuf = (void **)CPLMalloc(sizeof(void *) * nWinBandCount);
1846 97 : for (int iBand = 0; iBand < nWinBandCount; iBand++)
1847 106 : papCurLineBuf[iBand] =
1848 53 : CPLMalloc(static_cast<size_t>(nBufXSize) *
1849 53 : GDALGetDataTypeSizeBytes(eRasterDataType));
1850 :
1851 44 : CPLFree(panBandList);
1852 :
1853 44 : return CE_None;
1854 : }
1855 :
1856 : /************************************************************************/
1857 : /* TryWinRasterIO() */
1858 : /* */
1859 : /* Try to satisfy the given request based on the currently */
1860 : /* defined window. Return TRUE on success or FALSE on */
1861 : /* failure. On failure, the caller should satisfy the request */
1862 : /* another way (not report an error). */
1863 : /************************************************************************/
1864 :
1865 1931 : int ECWDataset::TryWinRasterIO(CPL_UNUSED GDALRWFlag eFlag, int nXOff,
1866 : int nYOff, int nXSize, int nYSize,
1867 : GByte *pabyData, int nBufXSize, int nBufYSize,
1868 : GDALDataType eDT, int nBandCount,
1869 : const int *panBandList, GSpacing nPixelSpace,
1870 : GSpacing nLineSpace, GSpacing nBandSpace,
1871 : GDALRasterIOExtraArg *psExtraArg)
1872 : {
1873 : int iBand, i;
1874 :
1875 : /* -------------------------------------------------------------------- */
1876 : /* Provide default buffer organization. */
1877 : /* -------------------------------------------------------------------- */
1878 1931 : if (nPixelSpace == 0)
1879 0 : nPixelSpace = GDALGetDataTypeSizeBytes(eDT);
1880 1931 : if (nLineSpace == 0)
1881 0 : nLineSpace = nPixelSpace * nBufXSize;
1882 1931 : if (nBandSpace == 0)
1883 1 : nBandSpace = nLineSpace * nBufYSize;
1884 :
1885 : /* -------------------------------------------------------------------- */
1886 : /* Do some simple tests to see if the current window can */
1887 : /* satisfy our requirement. */
1888 : /* -------------------------------------------------------------------- */
1889 : #ifdef NOISY_DEBUG
1890 : CPLDebug("ECW", "TryWinRasterIO(%d,%d,%d,%d,%d,%d)", nXOff, nYOff, nXSize,
1891 : nYSize, nBufXSize, nBufYSize);
1892 : #endif
1893 :
1894 1931 : if (!bWinActive)
1895 : {
1896 528 : if (nXOff == m_nAdviseReadXOff && nXSize == m_nAdviseReadXSize &&
1897 44 : nBufXSize == m_nAdviseReadBufXSize)
1898 : {
1899 44 : if (RunDeferredAdviseRead() != CE_None)
1900 0 : return FALSE;
1901 : }
1902 528 : if (!bWinActive)
1903 : {
1904 484 : return FALSE;
1905 : }
1906 : }
1907 :
1908 1447 : if (nXOff != nWinXOff || nXSize != nWinXSize)
1909 0 : return FALSE;
1910 :
1911 1447 : if (nBufXSize != nWinBufXSize)
1912 0 : return FALSE;
1913 :
1914 2907 : for (iBand = 0; iBand < nBandCount; iBand++)
1915 : {
1916 1488 : for (i = 0; i < nWinBandCount; i++)
1917 : {
1918 1486 : if (panWinBandList[i] == panBandList[iBand])
1919 1460 : break;
1920 : }
1921 :
1922 1462 : if (i == nWinBandCount)
1923 2 : return FALSE;
1924 : }
1925 :
1926 1445 : if (nYOff < nWinYOff || nYOff + nYSize > nWinYOff + nWinYSize)
1927 0 : return FALSE;
1928 :
1929 : /* -------------------------------------------------------------------- */
1930 : /* Now we try more subtle tests. */
1931 : /* -------------------------------------------------------------------- */
1932 : {
1933 : static int nDebugCount = 0;
1934 :
1935 1445 : if (nDebugCount < 30)
1936 30 : CPLDebug(
1937 : "ECW",
1938 : "TryWinRasterIO(%d,%d,%d,%d -> %dx%d) - doing advised read.",
1939 : nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize);
1940 :
1941 1445 : if (nDebugCount == 29)
1942 1 : CPLDebug("ECW", "No more TryWinRasterIO messages will be reported");
1943 :
1944 1445 : nDebugCount++;
1945 : }
1946 :
1947 : /* -------------------------------------------------------------------- */
1948 : /* Actually load data one buffer line at a time. */
1949 : /* -------------------------------------------------------------------- */
1950 : int iBufLine;
1951 :
1952 4165 : for (iBufLine = 0; iBufLine < nBufYSize; iBufLine++)
1953 : {
1954 2788 : double fFileLine = ((iBufLine + 0.5) / nBufYSize) * nYSize + nYOff;
1955 2788 : int iWinLine =
1956 2788 : (int)(((fFileLine - nWinYOff) / nWinYSize) * nWinBufYSize);
1957 :
1958 2788 : if (iWinLine == nWinBufLoaded + 1)
1959 2720 : LoadNextLine();
1960 :
1961 2788 : if (iWinLine != nWinBufLoaded)
1962 68 : return FALSE;
1963 :
1964 : /* --------------------------------------------------------------------
1965 : */
1966 : /* Copy out all our target bands. */
1967 : /* --------------------------------------------------------------------
1968 : */
1969 : int iWinBand;
1970 8290 : for (iBand = 0; iBand < nBandCount; iBand++)
1971 : {
1972 10070 : for (iWinBand = 0; iWinBand < nWinBandCount; iWinBand++)
1973 : {
1974 10070 : if (panWinBandList[iWinBand] == panBandList[iBand])
1975 5570 : break;
1976 : }
1977 :
1978 5570 : GDALCopyWords(papCurLineBuf[iWinBand], eRasterDataType,
1979 : GDALGetDataTypeSizeBytes(eRasterDataType),
1980 5570 : pabyData + nBandSpace * iBand + iBufLine * nLineSpace,
1981 : eDT, (int)nPixelSpace, nBufXSize);
1982 : }
1983 :
1984 2870 : if (psExtraArg->pfnProgress != nullptr &&
1985 150 : !psExtraArg->pfnProgress(1.0 * (iBufLine + 1) / nBufYSize, "",
1986 : psExtraArg->pProgressData))
1987 : {
1988 0 : return -1;
1989 : }
1990 : }
1991 :
1992 1377 : return TRUE;
1993 : }
1994 :
1995 : /************************************************************************/
1996 : /* LoadNextLine() */
1997 : /************************************************************************/
1998 :
1999 2720 : CPLErr ECWDataset::LoadNextLine()
2000 :
2001 : {
2002 2720 : if (!bWinActive)
2003 0 : return CE_Failure;
2004 :
2005 2720 : if (nWinBufLoaded == nWinBufYSize - 1)
2006 : {
2007 0 : CleanupWindow();
2008 0 : return CE_Failure;
2009 : }
2010 :
2011 : NCSEcwReadStatus eRStatus;
2012 5440 : eRStatus = poFileView->ReadLineBIL(eNCSRequestDataType,
2013 2720 : (UINT16)nWinBandCount, papCurLineBuf);
2014 2720 : if (eRStatus != NCSECW_READ_OK)
2015 0 : return CE_Failure;
2016 :
2017 2720 : if (nBandIndexToPromoteTo8Bit >= 0)
2018 : {
2019 24450 : for (int iX = 0; iX < nWinBufXSize; iX++)
2020 : {
2021 24300 : ((GByte *)papCurLineBuf[nBandIndexToPromoteTo8Bit])[iX] *= 255;
2022 : }
2023 : }
2024 :
2025 2720 : nWinBufLoaded++;
2026 :
2027 2720 : return CE_None;
2028 : }
2029 :
2030 : /************************************************************************/
2031 : /* CleanupWindow() */
2032 : /************************************************************************/
2033 :
2034 479 : void ECWDataset::CleanupWindow()
2035 :
2036 : {
2037 479 : if (!bWinActive)
2038 435 : return;
2039 :
2040 44 : bWinActive = FALSE;
2041 44 : CPLFree(panWinBandList);
2042 44 : panWinBandList = nullptr;
2043 :
2044 97 : for (int iBand = 0; iBand < nWinBandCount; iBand++)
2045 53 : CPLFree(papCurLineBuf[iBand]);
2046 44 : CPLFree(papCurLineBuf);
2047 44 : papCurLineBuf = nullptr;
2048 : }
2049 :
2050 : /************************************************************************/
2051 : /* IRasterIO() */
2052 : /************************************************************************/
2053 :
2054 1699 : CPLErr ECWDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
2055 : int nXSize, int nYSize, void *pData, int nBufXSize,
2056 : int nBufYSize, GDALDataType eBufType,
2057 : int nBandCount, BANDMAP_TYPE panBandMap,
2058 : GSpacing nPixelSpace, GSpacing nLineSpace,
2059 : GSpacing nBandSpace,
2060 : GDALRasterIOExtraArg *psExtraArg)
2061 :
2062 : {
2063 1699 : if (eRWFlag == GF_Write)
2064 0 : return CE_Failure;
2065 :
2066 1699 : if (nBandCount > 100)
2067 0 : return CE_Failure;
2068 :
2069 1699 : if (bUseOldBandRasterIOImplementation)
2070 : /* Sanity check. Should not happen */
2071 0 : return CE_Failure;
2072 1699 : int nDataTypeSize = GDALGetDataTypeSizeBytes(eRasterDataType);
2073 :
2074 1699 : if (nPixelSpace == 0)
2075 : {
2076 0 : nPixelSpace = nDataTypeSize;
2077 : }
2078 :
2079 1699 : if (nLineSpace == 0)
2080 : {
2081 0 : nLineSpace = nPixelSpace * nBufXSize;
2082 : }
2083 1699 : if (nBandSpace == 0)
2084 : {
2085 18 : nBandSpace =
2086 18 : static_cast<GSpacing>(nDataTypeSize) * nBufXSize * nBufYSize;
2087 : }
2088 :
2089 : // Use GDAL upsampling if non nearest
2090 1699 : if ((nBufXSize > nXSize || nBufYSize > nYSize) &&
2091 3 : psExtraArg->eResampleAlg != GRIORA_NearestNeighbour)
2092 : {
2093 2 : const int nBufDataTypeSize = GDALGetDataTypeSizeBytes(eBufType);
2094 2 : GByte *pabyTemp = (GByte *)VSI_MALLOC3_VERBOSE(
2095 : nXSize, nYSize, nBufDataTypeSize * nBandCount);
2096 2 : if (pabyTemp == nullptr)
2097 : {
2098 0 : return CE_Failure;
2099 : }
2100 :
2101 : GDALRasterIOExtraArg sExtraArgDefault;
2102 2 : INIT_RASTERIO_EXTRA_ARG(sExtraArgDefault);
2103 2 : sExtraArgDefault.pfnProgress = psExtraArg->pfnProgress;
2104 2 : sExtraArgDefault.pProgressData = psExtraArg->pProgressData;
2105 :
2106 4 : CPLErr eErr = IRasterIO(
2107 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pabyTemp, nXSize, nYSize,
2108 : eBufType, nBandCount, panBandMap, nBufDataTypeSize,
2109 2 : (GIntBig)nBufDataTypeSize * nXSize,
2110 2 : (GIntBig)nBufDataTypeSize * nXSize * nYSize, &sExtraArgDefault);
2111 :
2112 2 : if (eErr == CE_None)
2113 : {
2114 : /* Create a MEM dataset that wraps the input buffer */
2115 : auto poMEMDS = std::unique_ptr<MEMDataset>(
2116 2 : MEMDataset::Create("", nXSize, nYSize, 0, eBufType, nullptr));
2117 :
2118 7 : for (int i = 0; i < nBandCount; i++)
2119 : {
2120 5 : auto hBand = MEMCreateRasterBandEx(
2121 5 : poMEMDS.get(), i + 1,
2122 5 : pabyTemp + static_cast<size_t>(i) * nBufDataTypeSize *
2123 5 : nXSize * nYSize,
2124 : eBufType, 0, 0, false);
2125 5 : poMEMDS->AddMEMBand(hBand);
2126 :
2127 5 : const char *pszNBITS = GetRasterBand(i + 1)->GetMetadataItem(
2128 5 : "NBITS", "IMAGE_STRUCTURE");
2129 5 : if (pszNBITS)
2130 0 : poMEMDS->GetRasterBand(i + 1)->SetMetadataItem(
2131 0 : "NBITS", pszNBITS, "IMAGE_STRUCTURE");
2132 : }
2133 :
2134 : GDALRasterIOExtraArg sExtraArgTmp;
2135 2 : INIT_RASTERIO_EXTRA_ARG(sExtraArgTmp);
2136 2 : CPL_IGNORE_RET_VAL(sExtraArgTmp.eResampleAlg);
2137 2 : sExtraArgTmp.eResampleAlg = psExtraArg->eResampleAlg;
2138 :
2139 2 : CPL_IGNORE_RET_VAL(poMEMDS->RasterIO(
2140 : GF_Read, 0, 0, nXSize, nYSize, pData, nBufXSize, nBufYSize,
2141 : eBufType, nBandCount, nullptr, nPixelSpace, nLineSpace,
2142 : nBandSpace, &sExtraArgTmp));
2143 : }
2144 :
2145 2 : VSIFree(pabyTemp);
2146 :
2147 2 : return eErr;
2148 : }
2149 :
2150 : /* -------------------------------------------------------------------- */
2151 : /* ECW SDK 3.3 has a bug with the ECW format when we query the */
2152 : /* number of bands of the dataset, but not in the "natural order". */
2153 : /* It ignores the content of panBandMap. (#4234) */
2154 : /* -------------------------------------------------------------------- */
2155 : #if ECWSDK_VERSION < 40
2156 1697 : if (!bIsJPEG2000 && nBandCount == nBands)
2157 : {
2158 : int i;
2159 11 : int bDoBandIRasterIO = FALSE;
2160 44 : for (i = 0; i < nBandCount; i++)
2161 : {
2162 33 : if (panBandMap[i] != i + 1)
2163 : {
2164 2 : bDoBandIRasterIO = TRUE;
2165 : }
2166 : }
2167 11 : if (bDoBandIRasterIO)
2168 : {
2169 1 : return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
2170 : pData, nBufXSize, nBufYSize, eBufType,
2171 : nBandCount, panBandMap, nPixelSpace,
2172 1 : nLineSpace, nBandSpace, psExtraArg);
2173 : }
2174 : }
2175 : #endif
2176 :
2177 : /* -------------------------------------------------------------------- */
2178 : /* Check if we can directly return the data in case we have cached */
2179 : /* it from a previous call in a multi-band reading pattern. */
2180 : /* -------------------------------------------------------------------- */
2181 1696 : if (nBandCount == 1 && *panBandMap > 1 && *panBandMap <= nBands &&
2182 67 : sCachedMultiBandIO.nXOff == nXOff &&
2183 67 : sCachedMultiBandIO.nYOff == nYOff &&
2184 30 : sCachedMultiBandIO.nXSize == nXSize &&
2185 22 : sCachedMultiBandIO.nYSize == nYSize &&
2186 22 : sCachedMultiBandIO.nBufXSize == nBufXSize &&
2187 22 : sCachedMultiBandIO.nBufYSize == nBufYSize &&
2188 22 : sCachedMultiBandIO.eBufType == eBufType)
2189 : {
2190 22 : sCachedMultiBandIO.nBandsTried++;
2191 :
2192 22 : if (sCachedMultiBandIO.bEnabled &&
2193 7 : sCachedMultiBandIO.pabyData != nullptr)
2194 : {
2195 : int j;
2196 7 : const int nBufTypeSize = GDALGetDataTypeSizeBytes(eBufType);
2197 357 : for (j = 0; j < nBufYSize; j++)
2198 : {
2199 350 : GDALCopyWords(
2200 350 : sCachedMultiBandIO.pabyData +
2201 350 : static_cast<size_t>(*panBandMap - 1) * nBufXSize *
2202 350 : nBufYSize * nBufTypeSize +
2203 350 : static_cast<size_t>(j) * nBufXSize * nBufTypeSize,
2204 350 : eBufType, nBufTypeSize, ((GByte *)pData) + j * nLineSpace,
2205 : eBufType, (int)nPixelSpace, nBufXSize);
2206 : }
2207 7 : return CE_None;
2208 : }
2209 :
2210 45 : if (!(sCachedMultiBandIO.bEnabled) &&
2211 22 : sCachedMultiBandIO.nBandsTried == nBands &&
2212 7 : CPLTestBool(CPLGetConfigOption("ECW_CLEVER", "YES")))
2213 : {
2214 7 : sCachedMultiBandIO.bEnabled = TRUE;
2215 7 : CPLDebug(
2216 : "ECW",
2217 : "Detecting successive band reading pattern (for next time)");
2218 : }
2219 : }
2220 :
2221 : /* -------------------------------------------------------------------- */
2222 : /* Try to do it based on existing "advised" access. */
2223 : /* -------------------------------------------------------------------- */
2224 : int nRet =
2225 1689 : TryWinRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, (GByte *)pData,
2226 : nBufXSize, nBufYSize, eBufType, nBandCount, panBandMap,
2227 : nPixelSpace, nLineSpace, nBandSpace, psExtraArg);
2228 1689 : if (nRet == TRUE)
2229 1371 : return CE_None;
2230 318 : else if (nRet < 0)
2231 0 : return CE_Failure;
2232 :
2233 : /* -------------------------------------------------------------------- */
2234 : /* If we are requesting a single line at 1:1, we do a multi-band */
2235 : /* AdviseRead() and then TryWinRasterIO() again. */
2236 : /* */
2237 : /* Except for reading a 1x1 window when reading a scanline might */
2238 : /* be longer. */
2239 : /* -------------------------------------------------------------------- */
2240 318 : if (nXSize == 1 && nYSize == 1 && nBufXSize == 1 && nBufYSize == 1)
2241 : {
2242 : /* do nothing */
2243 : }
2244 :
2245 : #if !defined(SDK_CAN_DO_SUPERSAMPLING)
2246 : /* -------------------------------------------------------------------- */
2247 : /* If we are supersampling we need to fall into the general */
2248 : /* purpose logic. */
2249 : /* -------------------------------------------------------------------- */
2250 314 : else if (nXSize < nBufXSize || nYSize < nBufYSize)
2251 : {
2252 1 : bUseOldBandRasterIOImplementation = TRUE;
2253 1 : CPLErr eErr = GDALDataset::IRasterIO(
2254 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
2255 : eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace,
2256 : nBandSpace, psExtraArg);
2257 1 : bUseOldBandRasterIOImplementation = FALSE;
2258 1 : return eErr;
2259 : }
2260 : #endif
2261 :
2262 313 : else if (nBufYSize == 1)
2263 : {
2264 : // This is tricky, because it expects the rest of the image
2265 : // with this buffer width to be read. The preferred way to
2266 : // achieve this behavior would be to call AdviseRead before
2267 : // call IRasterIO. The logic could be improved to detect
2268 : // successive pattern of single line reading before doing an
2269 : // AdviseRead.
2270 : CPLErr eErr;
2271 :
2272 241 : eErr = AdviseRead(nXOff, nYOff, nXSize, GetRasterYSize() - nYOff,
2273 241 : nBufXSize, (nRasterYSize - nYOff) / nYSize, eBufType,
2274 : nBandCount, const_cast<int *>(panBandMap), nullptr);
2275 482 : if (eErr == CE_None &&
2276 241 : TryWinRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
2277 : (GByte *)pData, nBufXSize, nBufYSize, eBufType,
2278 : nBandCount, panBandMap, nPixelSpace, nLineSpace,
2279 : nBandSpace, psExtraArg))
2280 6 : return CE_None;
2281 : }
2282 :
2283 311 : CPLDebug("ECW", "RasterIO(%d,%d,%d,%d -> %dx%d) - doing interleaved read.",
2284 : nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize);
2285 :
2286 : /* -------------------------------------------------------------------- */
2287 : /* Setup view. */
2288 : /* -------------------------------------------------------------------- */
2289 : UINT32 anBandIndices[100];
2290 : int i;
2291 : NCSError eNCSErr;
2292 622 : CNCSError oErr(GetCNCSError(NCS_SUCCESS));
2293 :
2294 641 : for (i = 0; i < nBandCount; i++)
2295 330 : anBandIndices[i] = panBandMap[i] - 1;
2296 :
2297 311 : CleanupWindow();
2298 :
2299 : /* -------------------------------------------------------------------- */
2300 : /* Cache data in the context of a multi-band reading pattern. */
2301 : /* -------------------------------------------------------------------- */
2302 311 : if (nBandCount == 1 && *panBandMap == 1 && (nBands == 3 || nBands == 4))
2303 : {
2304 219 : if (sCachedMultiBandIO.bEnabled &&
2305 4 : sCachedMultiBandIO.nBandsTried != nBands)
2306 : {
2307 1 : sCachedMultiBandIO.bEnabled = FALSE;
2308 1 : CPLDebug("ECW", "Disabling successive band reading pattern");
2309 : }
2310 :
2311 219 : sCachedMultiBandIO.nXOff = nXOff;
2312 219 : sCachedMultiBandIO.nYOff = nYOff;
2313 219 : sCachedMultiBandIO.nXSize = nXSize;
2314 219 : sCachedMultiBandIO.nYSize = nYSize;
2315 219 : sCachedMultiBandIO.nBufXSize = nBufXSize;
2316 219 : sCachedMultiBandIO.nBufYSize = nBufYSize;
2317 219 : sCachedMultiBandIO.eBufType = eBufType;
2318 219 : sCachedMultiBandIO.nBandsTried = 1;
2319 :
2320 219 : const int nBufTypeSize = GDALGetDataTypeSizeBytes(eBufType);
2321 :
2322 219 : if (sCachedMultiBandIO.bEnabled)
2323 : {
2324 : GByte *pNew =
2325 6 : (GByte *)VSIRealloc(sCachedMultiBandIO.pabyData,
2326 3 : static_cast<size_t>(nBufXSize) * nBufYSize *
2327 3 : nBands * nBufTypeSize);
2328 3 : if (pNew == nullptr)
2329 0 : CPLFree(sCachedMultiBandIO.pabyData);
2330 3 : sCachedMultiBandIO.pabyData = pNew;
2331 : }
2332 :
2333 219 : if (sCachedMultiBandIO.bEnabled &&
2334 3 : sCachedMultiBandIO.pabyData != nullptr)
2335 : {
2336 3 : nBandIndexToPromoteTo8Bit = -1;
2337 12 : for (i = 0; i < nBands; i++)
2338 : {
2339 9 : if (cpl::down_cast<ECWRasterBand *>(GetRasterBand(i + 1))
2340 9 : ->bPromoteTo8Bit)
2341 0 : nBandIndexToPromoteTo8Bit = i;
2342 9 : anBandIndices[i] = i;
2343 : }
2344 :
2345 3 : oErr = poFileView->SetView(nBands, anBandIndices, nXOff, nYOff,
2346 3 : nXOff + nXSize - 1, nYOff + nYSize - 1,
2347 3 : nBufXSize, nBufYSize);
2348 3 : eNCSErr = oErr.GetErrorNumber();
2349 :
2350 3 : if (eNCSErr != NCS_SUCCESS)
2351 : {
2352 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
2353 : NCSGetErrorText(eNCSErr));
2354 :
2355 0 : return CE_Failure;
2356 : }
2357 :
2358 6 : CPLErr eErr = ReadBands(
2359 3 : sCachedMultiBandIO.pabyData, nBufXSize, nBufYSize, eBufType,
2360 3 : nBands, nBufTypeSize, nBufXSize * nBufTypeSize,
2361 3 : nBufXSize * nBufYSize * nBufTypeSize, psExtraArg);
2362 3 : if (eErr != CE_None)
2363 0 : return eErr;
2364 :
2365 : int j;
2366 153 : for (j = 0; j < nBufYSize; j++)
2367 : {
2368 150 : GDALCopyWords(
2369 150 : sCachedMultiBandIO.pabyData +
2370 150 : static_cast<size_t>(j) * nBufXSize * nBufTypeSize,
2371 150 : eBufType, nBufTypeSize, ((GByte *)pData) + j * nLineSpace,
2372 : eBufType, (int)nPixelSpace, nBufXSize);
2373 : }
2374 3 : return CE_None;
2375 : }
2376 : }
2377 :
2378 308 : nBandIndexToPromoteTo8Bit = -1;
2379 635 : for (i = 0; i < nBandCount; i++)
2380 : {
2381 327 : if (((ECWRasterBand *)GetRasterBand(anBandIndices[i] + 1))
2382 327 : ->bPromoteTo8Bit)
2383 4 : nBandIndexToPromoteTo8Bit = i;
2384 : }
2385 308 : oErr = poFileView->SetView(nBandCount, anBandIndices, nXOff, nYOff,
2386 308 : nXOff + nXSize - 1, nYOff + nYSize - 1,
2387 308 : nBufXSize, nBufYSize);
2388 308 : eNCSErr = oErr.GetErrorNumber();
2389 :
2390 308 : if (eNCSErr != NCS_SUCCESS)
2391 : {
2392 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", NCSGetErrorText(eNCSErr));
2393 :
2394 0 : return CE_Failure;
2395 : }
2396 :
2397 308 : return ReadBands(pData, nBufXSize, nBufYSize, eBufType, nBandCount,
2398 308 : nPixelSpace, nLineSpace, nBandSpace, psExtraArg);
2399 : }
2400 :
2401 : /************************************************************************/
2402 : /* ReadBandsDirectly() */
2403 : /************************************************************************/
2404 :
2405 39 : CPLErr ECWDataset::ReadBandsDirectly(void *pData, int nBufXSize, int nBufYSize,
2406 : CPL_UNUSED GDALDataType eBufType,
2407 : int nBandCount,
2408 : CPL_UNUSED GSpacing nPixelSpace,
2409 : GSpacing nLineSpace, GSpacing nBandSpace,
2410 : GDALRasterIOExtraArg *psExtraArg)
2411 : {
2412 39 : CPLDebug("ECW", "ReadBandsDirectly(-> %dx%d) - reading lines directly.",
2413 : nBufXSize, nBufYSize);
2414 :
2415 39 : UINT8 **pBIL = (UINT8 **)NCSMalloc(nBandCount * sizeof(UINT8 *), FALSE);
2416 :
2417 101 : for (int nB = 0; nB < nBandCount; nB++)
2418 : {
2419 62 : pBIL[nB] = ((UINT8 *)pData) + (nBandSpace * nB); // for any bit depth
2420 : }
2421 :
2422 39 : CPLErr eErr = CE_None;
2423 5574 : for (int nR = 0; nR < nBufYSize; nR++)
2424 : {
2425 11070 : if (poFileView->ReadLineBIL(eNCSRequestDataType, (UINT16)nBandCount,
2426 5535 : (void **)pBIL) != NCSECW_READ_OK)
2427 : {
2428 0 : eErr = CE_Failure;
2429 0 : break;
2430 : }
2431 13722 : for (int nB = 0; nB < nBandCount; nB++)
2432 : {
2433 8187 : if (nB == nBandIndexToPromoteTo8Bit)
2434 : {
2435 48999 : for (int iX = 0; iX < nBufXSize; iX++)
2436 : {
2437 48690 : pBIL[nB][iX] *= 255;
2438 : }
2439 : }
2440 8187 : pBIL[nB] += nLineSpace;
2441 : }
2442 :
2443 5535 : if (psExtraArg->pfnProgress != nullptr &&
2444 0 : !psExtraArg->pfnProgress(1.0 * (nR + 1) / nBufYSize, "",
2445 : psExtraArg->pProgressData))
2446 : {
2447 0 : eErr = CE_Failure;
2448 0 : break;
2449 : }
2450 : }
2451 39 : if (pBIL)
2452 : {
2453 39 : NCSFree(pBIL);
2454 : }
2455 39 : return eErr;
2456 : }
2457 :
2458 : /************************************************************************/
2459 : /* ReadBands() */
2460 : /************************************************************************/
2461 :
2462 311 : CPLErr ECWDataset::ReadBands(void *pData, int nBufXSize, int nBufYSize,
2463 : GDALDataType eBufType, int nBandCount,
2464 : GSpacing nPixelSpace, GSpacing nLineSpace,
2465 : GSpacing nBandSpace,
2466 : GDALRasterIOExtraArg *psExtraArg)
2467 : {
2468 : int i;
2469 : /* -------------------------------------------------------------------- */
2470 : /* Setup working scanline, and the pointers into it. */
2471 : /* -------------------------------------------------------------------- */
2472 311 : const int nDataTypeSizeBytes = GDALGetDataTypeSizeBytes(eRasterDataType);
2473 311 : bool bDirect =
2474 45 : (eBufType == eRasterDataType) && nDataTypeSizeBytes == nPixelSpace &&
2475 395 : nLineSpace == (nPixelSpace * nBufXSize) &&
2476 : nBandSpace ==
2477 39 : (static_cast<GSpacing>(nDataTypeSizeBytes) * nBufXSize * nBufYSize);
2478 311 : if (bDirect)
2479 : {
2480 39 : return ReadBandsDirectly(pData, nBufXSize, nBufYSize, eBufType,
2481 : nBandCount, nPixelSpace, nLineSpace,
2482 39 : nBandSpace, psExtraArg);
2483 : }
2484 272 : CPLDebug("ECW", "ReadBands(-> %dx%d) - reading lines using GDALCopyWords.",
2485 : nBufXSize, nBufYSize);
2486 272 : CPLErr eErr = CE_None;
2487 544 : GByte *pabyBILScanline = (GByte *)CPLMalloc(
2488 272 : static_cast<size_t>(nBufXSize) * nDataTypeSizeBytes * nBandCount);
2489 272 : GByte **papabyBIL = (GByte **)CPLMalloc(nBandCount * sizeof(void *));
2490 :
2491 546 : for (i = 0; i < nBandCount; i++)
2492 274 : papabyBIL[i] = pabyBILScanline +
2493 274 : static_cast<size_t>(i) * nBufXSize * nDataTypeSizeBytes;
2494 :
2495 : /* -------------------------------------------------------------------- */
2496 : /* Read back all the data for the requested view. */
2497 : /* -------------------------------------------------------------------- */
2498 4272 : for (int iScanline = 0; iScanline < nBufYSize; iScanline++)
2499 : {
2500 : NCSEcwReadStatus eRStatus;
2501 :
2502 8000 : eRStatus = poFileView->ReadLineBIL(
2503 4000 : eNCSRequestDataType, (UINT16)nBandCount, (void **)papabyBIL);
2504 4000 : if (eRStatus != NCSECW_READ_OK)
2505 : {
2506 0 : eErr = CE_Failure;
2507 0 : CPLError(CE_Failure, CPLE_AppDefined,
2508 : "NCScbmReadViewLineBIL failed.");
2509 0 : break;
2510 : }
2511 :
2512 8080 : for (i = 0; i < nBandCount; i++)
2513 : {
2514 4080 : if (i == nBandIndexToPromoteTo8Bit)
2515 : {
2516 24450 : for (int iX = 0; iX < nBufXSize; iX++)
2517 : {
2518 24300 : papabyBIL[i][iX] *= 255;
2519 : }
2520 : }
2521 :
2522 4080 : GDALCopyWords(pabyBILScanline + static_cast<size_t>(i) *
2523 4080 : nDataTypeSizeBytes * nBufXSize,
2524 : eRasterDataType, nDataTypeSizeBytes,
2525 4080 : ((GByte *)pData) + nLineSpace * iScanline +
2526 4080 : nBandSpace * i,
2527 : eBufType, (int)nPixelSpace, nBufXSize);
2528 : }
2529 :
2530 4000 : if (psExtraArg->pfnProgress != nullptr &&
2531 0 : !psExtraArg->pfnProgress(1.0 * (iScanline + 1) / nBufYSize, "",
2532 : psExtraArg->pProgressData))
2533 : {
2534 0 : eErr = CE_Failure;
2535 0 : break;
2536 : }
2537 : }
2538 :
2539 272 : CPLFree(pabyBILScanline);
2540 272 : CPLFree(papabyBIL);
2541 :
2542 272 : return eErr;
2543 : }
2544 :
2545 : /************************************************************************/
2546 : /* OpenJPEG2000() */
2547 : /* */
2548 : /* Open method that only supports JPEG2000 files. */
2549 : /************************************************************************/
2550 :
2551 89 : GDALDataset *ECWDataset::OpenJPEG2000(GDALOpenInfo *poOpenInfo)
2552 :
2553 : {
2554 89 : if (!ECWDatasetIdentifyJPEG2000(poOpenInfo))
2555 0 : return nullptr;
2556 :
2557 89 : return Open(poOpenInfo, TRUE);
2558 : }
2559 :
2560 : /************************************************************************/
2561 : /* OpenECW() */
2562 : /* */
2563 : /* Open method that only supports ECW files. */
2564 : /************************************************************************/
2565 :
2566 35 : GDALDataset *ECWDataset::OpenECW(GDALOpenInfo *poOpenInfo)
2567 :
2568 : {
2569 35 : if (!ECWDatasetIdentifyECW(poOpenInfo))
2570 0 : return nullptr;
2571 :
2572 35 : return Open(poOpenInfo, FALSE);
2573 : }
2574 :
2575 : /************************************************************************/
2576 : /* OpenFileView() */
2577 : /************************************************************************/
2578 :
2579 124 : CNCSJP2FileView *ECWDataset::OpenFileView(const char *pszDatasetName,
2580 : bool bProgressive,
2581 : int &bUsingCustomStream,
2582 : CPL_UNUSED bool bWrite)
2583 : {
2584 : /* -------------------------------------------------------------------- */
2585 : /* First we try to open it as a normal CNCSFile, letting the */
2586 : /* ECW SDK manage the IO itself. This will only work for real */
2587 : /* files, and ecwp: or ecwps: sources. */
2588 : /* -------------------------------------------------------------------- */
2589 124 : CNCSJP2FileView *poFileView = nullptr;
2590 : NCSError eErr;
2591 248 : CNCSError oErr(GetCNCSError(NCS_SUCCESS));
2592 :
2593 124 : bUsingCustomStream = FALSE;
2594 124 : poFileView = new CNCSFile();
2595 : // we always open in read only mode. This should be improved in the future.
2596 : try
2597 : {
2598 : #ifdef _WIN32
2599 : if (CPLTestBool(CPLGetConfigOption("GDAL_FILENAME_IS_UTF8", "YES")))
2600 : {
2601 : wchar_t *pwszDatasetName =
2602 : CPLRecodeToWChar(pszDatasetName, CPL_ENC_UTF8, CPL_ENC_UCS2);
2603 : oErr = poFileView->Open(pwszDatasetName, bProgressive, false);
2604 : CPLFree(pwszDatasetName);
2605 : }
2606 : else
2607 : #endif
2608 : {
2609 : oErr =
2610 124 : poFileView->Open((char *)pszDatasetName, bProgressive, false);
2611 : }
2612 : }
2613 0 : catch (...)
2614 : {
2615 0 : CPLError(CE_Failure, CPLE_AppDefined,
2616 : "Unexpected exception occurred in ECW SDK");
2617 0 : delete poFileView;
2618 0 : return nullptr;
2619 : }
2620 124 : eErr = oErr.GetErrorNumber();
2621 :
2622 : /* -------------------------------------------------------------------- */
2623 : /* If that did not work, trying opening as a virtual file. */
2624 : /* -------------------------------------------------------------------- */
2625 124 : if (eErr != NCS_SUCCESS)
2626 : {
2627 53 : CPLDebug("ECW",
2628 : "NCScbmOpenFileView(%s): eErr=%d, will try VSIL stream.",
2629 : pszDatasetName, (int)eErr);
2630 :
2631 53 : delete poFileView;
2632 :
2633 53 : VSILFILE *fpVSIL = VSIFOpenL(pszDatasetName, "rb");
2634 53 : if (fpVSIL == nullptr)
2635 : {
2636 0 : CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open %s.",
2637 : pszDatasetName);
2638 0 : return nullptr;
2639 : }
2640 :
2641 53 : if (hECWDatasetMutex == nullptr)
2642 : {
2643 0 : hECWDatasetMutex = CPLCreateMutex();
2644 : }
2645 53 : else if (!CPLAcquireMutex(hECWDatasetMutex, 60.0))
2646 : {
2647 0 : CPLDebug("ECW", "Failed to acquire mutex in 60s.");
2648 : }
2649 : else
2650 : {
2651 53 : CPLDebug("ECW", "Got mutex.");
2652 : }
2653 :
2654 53 : poFileView = new CNCSJP2FileView();
2655 :
2656 : #if ECWSDK_VERSION >= 55
2657 : NCS::CString streamName(pszDatasetName);
2658 : auto vsiIoStream =
2659 : NCS::CView::FindSteamByStreamNameFromOpenDatasets(streamName);
2660 : if (!vsiIoStream)
2661 : {
2662 : vsiIoStream = std::make_shared<VSIIOStream>();
2663 : std::static_pointer_cast<VSIIOStream>(vsiIoStream)
2664 : ->Access(fpVSIL, FALSE, TRUE, pszDatasetName, 0, -1);
2665 : bUsingCustomStream = TRUE;
2666 : }
2667 : oErr = poFileView->Open(vsiIoStream, bProgressive);
2668 : #else
2669 53 : auto vsiIoStream = new VSIIOStream();
2670 53 : vsiIoStream->Access(fpVSIL, FALSE, TRUE, pszDatasetName, 0, -1);
2671 53 : oErr = poFileView->Open(vsiIoStream, bProgressive);
2672 :
2673 : // The CNCSJP2FileView (poFileView) object may not use the iostream
2674 : // (poIOStream) passed to the CNCSJP2FileView::Open() method if an
2675 : // iostream is already available to the ECW JPEG 2000 SDK for a given
2676 : // file. Consequently, if the iostream passed to
2677 : // CNCSJP2FileView::Open() does not become the underlying iostream
2678 : // of the CNCSJP2FileView object, then it should be deleted.
2679 : //
2680 : // In addition, the underlying iostream of the CNCSJP2FileView object
2681 : // should not be deleted until all CNCSJP2FileView objects using the
2682 : // underlying iostream are deleted. Consequently, each time a
2683 : // CNCSJP2FileView object is created, the nFileViewCount attribute
2684 : // of the underlying VSIIOStream object must be incremented for use
2685 : // in the ECWDataset destructor.
2686 :
2687 : VSIIOStream *poUnderlyingIOStream =
2688 53 : ((VSIIOStream *)(poFileView->GetStream()));
2689 :
2690 53 : if (poUnderlyingIOStream)
2691 52 : poUnderlyingIOStream->nFileViewCount++;
2692 :
2693 53 : if (vsiIoStream != poUnderlyingIOStream)
2694 : {
2695 1 : delete vsiIoStream;
2696 : }
2697 : else
2698 : {
2699 52 : bUsingCustomStream = TRUE;
2700 : }
2701 : #endif
2702 :
2703 53 : CPLReleaseMutex(hECWDatasetMutex);
2704 :
2705 53 : if (oErr.GetErrorNumber() != NCS_SUCCESS)
2706 : {
2707 1 : delete poFileView;
2708 1 : ECWReportError(oErr);
2709 :
2710 1 : return nullptr;
2711 : }
2712 : }
2713 :
2714 123 : return poFileView;
2715 : }
2716 :
2717 : /************************************************************************/
2718 : /* Open() */
2719 : /************************************************************************/
2720 :
2721 124 : GDALDataset *ECWDataset::Open(GDALOpenInfo *poOpenInfo, int bIsJPEG2000)
2722 :
2723 : {
2724 124 : CNCSJP2FileView *poFileView = nullptr;
2725 : int i;
2726 124 : int bUsingCustomStream = FALSE;
2727 248 : CPLString osFilename = poOpenInfo->pszFilename;
2728 :
2729 124 : ECWInitialize();
2730 :
2731 : /* Note: J2K_SUBFILE is somehow an obsolete concept that predates
2732 : * /vsisubfile/ */
2733 : /* syntax and was used mainly(only?) by the NITF driver before its switch */
2734 : /* to /vsisubfile */
2735 :
2736 : /* -------------------------------------------------------------------- */
2737 : /* If we get a J2K_SUBFILE style name, convert it into the */
2738 : /* corresponding /vsisubfile/ path. */
2739 : /* */
2740 : /* From: J2K_SUBFILE:offset,size,filename */
2741 : /* To: /vsisubfile/offset_size,filename */
2742 : /* -------------------------------------------------------------------- */
2743 124 : if (STARTS_WITH_CI(osFilename, "J2K_SUBFILE:"))
2744 : {
2745 : char **papszTokens =
2746 0 : CSLTokenizeString2(osFilename.c_str() + 12, ",", 0);
2747 0 : if (CSLCount(papszTokens) >= 3)
2748 : {
2749 : osFilename.Printf("/vsisubfile/%s_%s,%s", papszTokens[0],
2750 0 : papszTokens[1], papszTokens[2]);
2751 : }
2752 : else
2753 : {
2754 0 : CPLError(CE_Failure, CPLE_OpenFailed,
2755 : "Failed to parse J2K_SUBFILE specification.");
2756 0 : CSLDestroy(papszTokens);
2757 0 : return nullptr;
2758 : }
2759 0 : CSLDestroy(papszTokens);
2760 : }
2761 :
2762 : /* -------------------------------------------------------------------- */
2763 : /* Open the client interface. */
2764 : /* -------------------------------------------------------------------- */
2765 124 : poFileView = OpenFileView(osFilename.c_str(), false, bUsingCustomStream,
2766 124 : poOpenInfo->eAccess == GA_Update);
2767 124 : if (poFileView == nullptr)
2768 : {
2769 : #if ECWSDK_VERSION < 50
2770 : /* Detect what is apparently the ECW v3 file format signature */
2771 2 : if (EQUAL(CPLGetExtensionSafe(osFilename).c_str(), "ECW") &&
2772 3 : poOpenInfo->nHeaderBytes > 0x30 &&
2773 1 : STARTS_WITH_CI((const char *)(poOpenInfo->pabyHeader + 0x20),
2774 : "ecw ECW3"))
2775 : {
2776 1 : CPLError(CE_Failure, CPLE_AppDefined,
2777 : "Cannot open %s which looks like a ECW format v3 file, "
2778 : "that requires ECW SDK 5.0 or later",
2779 : osFilename.c_str());
2780 : }
2781 : #endif
2782 1 : return nullptr;
2783 : }
2784 :
2785 : /* -------------------------------------------------------------------- */
2786 : /* Create a corresponding GDALDataset. */
2787 : /* -------------------------------------------------------------------- */
2788 123 : ECWDataset *poDS = new ECWDataset(bIsJPEG2000);
2789 123 : poDS->poFileView = poFileView;
2790 123 : poDS->eAccess = poOpenInfo->eAccess;
2791 :
2792 : // Disable .aux.xml writing for subfiles and such. Unfortunately
2793 : // this will also disable it in some cases where it might be
2794 : // applicable.
2795 123 : if (bUsingCustomStream)
2796 52 : poDS->nPamFlags |= GPF_DISABLED;
2797 :
2798 123 : poDS->bUsingCustomStream = bUsingCustomStream;
2799 :
2800 : /* -------------------------------------------------------------------- */
2801 : /* Fetch general file information. */
2802 : /* -------------------------------------------------------------------- */
2803 123 : poDS->psFileInfo = poFileView->GetFileInfo();
2804 :
2805 123 : CPLDebug("ECW",
2806 : "FileInfo: SizeXY=%d,%d Bands=%d\n"
2807 : " OriginXY=%g,%g CellIncrementXY=%g,%g\n"
2808 : " ColorSpace=%d, eCellType=%d\n",
2809 123 : poDS->psFileInfo->nSizeX, poDS->psFileInfo->nSizeY,
2810 123 : poDS->psFileInfo->nBands, poDS->psFileInfo->fOriginX,
2811 123 : poDS->psFileInfo->fOriginY, poDS->psFileInfo->fCellIncrementX,
2812 123 : poDS->psFileInfo->fCellIncrementY,
2813 123 : (int)poDS->psFileInfo->eColorSpace,
2814 123 : (int)poDS->psFileInfo->eCellType);
2815 :
2816 : /* -------------------------------------------------------------------- */
2817 : /* Establish raster info. */
2818 : /* -------------------------------------------------------------------- */
2819 123 : poDS->nRasterXSize = poDS->psFileInfo->nSizeX;
2820 123 : poDS->nRasterYSize = poDS->psFileInfo->nSizeY;
2821 :
2822 : /* -------------------------------------------------------------------- */
2823 : /* Establish the GDAL data type that corresponds. A few NCS */
2824 : /* data types have no direct corresponding value in GDAL so we */
2825 : /* will coerce to something sufficiently similar. */
2826 : /* -------------------------------------------------------------------- */
2827 123 : poDS->eNCSRequestDataType = poDS->psFileInfo->eCellType;
2828 123 : switch (poDS->psFileInfo->eCellType)
2829 : {
2830 103 : case NCSCT_UINT8:
2831 103 : poDS->eRasterDataType = GDT_UInt8;
2832 103 : break;
2833 :
2834 10 : case NCSCT_UINT16:
2835 10 : poDS->eRasterDataType = GDT_UInt16;
2836 10 : break;
2837 :
2838 2 : case NCSCT_UINT32:
2839 : case NCSCT_UINT64:
2840 2 : poDS->eRasterDataType = GDT_UInt32;
2841 2 : poDS->eNCSRequestDataType = NCSCT_UINT32;
2842 2 : break;
2843 :
2844 6 : case NCSCT_INT8:
2845 : case NCSCT_INT16:
2846 6 : poDS->eRasterDataType = GDT_Int16;
2847 6 : poDS->eNCSRequestDataType = NCSCT_INT16;
2848 6 : break;
2849 :
2850 2 : case NCSCT_INT32:
2851 : case NCSCT_INT64:
2852 2 : poDS->eRasterDataType = GDT_Int32;
2853 2 : poDS->eNCSRequestDataType = NCSCT_INT32;
2854 2 : break;
2855 :
2856 0 : case NCSCT_IEEE4:
2857 0 : poDS->eRasterDataType = GDT_Float32;
2858 0 : break;
2859 :
2860 0 : case NCSCT_IEEE8:
2861 0 : poDS->eRasterDataType = GDT_Float64;
2862 0 : break;
2863 :
2864 0 : default:
2865 0 : CPLDebug("ECW", "Unhandled case : eCellType = %d",
2866 0 : (int)poDS->psFileInfo->eCellType);
2867 0 : break;
2868 : }
2869 :
2870 : /* -------------------------------------------------------------------- */
2871 : /* If decoding a UInt32 image, check that the SDK is not buggy */
2872 : /* There are issues at least in the 5.x series. */
2873 : /* -------------------------------------------------------------------- */
2874 : #if ECWSDK_VERSION >= 40
2875 : constexpr const char *szDETECT_BUG_FILENAME =
2876 : "__detect_ecw_uint32_bug__.j2k";
2877 : if (bIsJPEG2000 && poDS->eNCSRequestDataType == NCSCT_UINT32 &&
2878 : CPLTestBool(CPLGetConfigOption("ECW_CHECK_CORRECT_DECODING", "TRUE")) &&
2879 : strstr(poOpenInfo->pszFilename, szDETECT_BUG_FILENAME) == nullptr)
2880 : {
2881 : static bool bUINT32_Ok = false;
2882 : {
2883 : CPLMutexHolder oHolder(&hECWDatasetMutex);
2884 : static bool bTestDone = false;
2885 : if (!bTestDone)
2886 : {
2887 : bTestDone = true;
2888 : // Minimal J2K 2x2 image with NBITS=20, unsigned, reversible
2889 : // compression and following values 0 1048575 1048574 524288
2890 :
2891 : static const GByte abyTestUInt32ImageData[] = {
2892 : 0xFF, 0x4F, 0xFF, 0x51, 0x00, 0x29, 0x00, 0x02, 0x00, 0x00,
2893 : 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
2894 : 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
2895 : 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2896 : 0x00, 0x01, 0x13, 0x01, 0x01, 0xFF, 0x52, 0x00, 0x0D, 0x01,
2897 : 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x04, 0x00, 0x01, 0x99,
2898 : 0xFF, 0x5C, 0x00, 0x04, 0x40, 0xA0, 0xFF, 0x90, 0x00, 0x0A,
2899 : 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x01, 0xFF, 0x93,
2900 : 0xDF, 0xF9, 0x40, 0x50, 0x07, 0x68, 0xE0, 0x12, 0xD2, 0xDA,
2901 : 0xDF, 0xFF, 0x7F, 0x5F, 0xFF, 0xD9};
2902 :
2903 : const std::string osTmpFilename =
2904 : VSIMemGenerateHiddenFilename(szDETECT_BUG_FILENAME);
2905 : VSIFCloseL(VSIFileFromMemBuffer(
2906 : osTmpFilename.c_str(),
2907 : const_cast<GByte *>(abyTestUInt32ImageData),
2908 : sizeof(abyTestUInt32ImageData), false));
2909 : GDALOpenInfo oOpenInfo(osTmpFilename.c_str(), GA_ReadOnly);
2910 : auto poTmpDS =
2911 : std::unique_ptr<GDALDataset>(Open(&oOpenInfo, true));
2912 : if (poTmpDS)
2913 : {
2914 : uint32_t anValues[4] = {0};
2915 : if (poTmpDS->GetRasterBand(1)->RasterIO(
2916 : GF_Read, 0, 0, 2, 2, anValues, 2, 2, GDT_UInt32, 0,
2917 : 0, nullptr) == CE_None &&
2918 : anValues[0] == 0 && anValues[1] == 1048575 &&
2919 : anValues[2] == 1048574 && anValues[3] == 524288)
2920 : {
2921 : bUINT32_Ok = true;
2922 : }
2923 : }
2924 : VSIUnlink(osTmpFilename.c_str());
2925 : }
2926 : }
2927 :
2928 : if (!bUINT32_Ok)
2929 : {
2930 : CPLDebug("ECW", "ECW SDK used cannot correctly decode UInt32 "
2931 : "images. Giving up");
2932 : delete poDS;
2933 : return nullptr;
2934 : }
2935 : }
2936 : #endif
2937 :
2938 : /* -------------------------------------------------------------------- */
2939 : /* Create band information objects. */
2940 : /* -------------------------------------------------------------------- */
2941 336 : for (i = 0; i < poDS->psFileInfo->nBands; i++)
2942 213 : poDS->SetBand(i + 1, new ECWRasterBand(poDS, i + 1, -1,
2943 213 : poOpenInfo->papszOpenOptions));
2944 :
2945 : /* -------------------------------------------------------------------- */
2946 : /* Look for supporting coordinate system information. */
2947 : /* -------------------------------------------------------------------- */
2948 123 : if (bIsJPEG2000)
2949 : {
2950 89 : poDS->LoadJP2Metadata(poOpenInfo, osFilename);
2951 : }
2952 : else
2953 : {
2954 34 : poDS->ECW2WKTProjection();
2955 :
2956 : /* --------------------------------------------------------------------
2957 : */
2958 : /* Check for world file. */
2959 : /* --------------------------------------------------------------------
2960 : */
2961 34 : if (!poDS->bGeoTransformValid)
2962 : {
2963 0 : poDS->bGeoTransformValid |=
2964 0 : GDALReadWorldFile2(osFilename, nullptr, poDS->m_gt,
2965 0 : poOpenInfo->GetSiblingFiles(), nullptr) ||
2966 0 : GDALReadWorldFile2(osFilename, ".wld", poDS->m_gt,
2967 0 : poOpenInfo->GetSiblingFiles(), nullptr);
2968 : }
2969 : }
2970 :
2971 123 : if (poDS->psFileInfo->nCompressionRate > 0)
2972 67 : poDS->GDALDataset::SetMetadataItem(
2973 : "COMPRESSION_RATE_TARGET",
2974 134 : CPLString().Printf("%d", poDS->psFileInfo->nCompressionRate));
2975 123 : poDS->GDALDataset::SetMetadataItem(
2976 123 : "COLORSPACE", ECWGetColorSpaceName(poDS->psFileInfo->eColorSpace));
2977 : #if ECWSDK_VERSION >= 50
2978 : if (!bIsJPEG2000)
2979 : poDS->GDALDataset::SetMetadataItem(
2980 : "VERSION",
2981 : CPLString().Printf("%d", poDS->psFileInfo->nFormatVersion));
2982 : #if ECWSDK_VERSION >= 51
2983 : // output jp2 header info
2984 : if (bIsJPEG2000 && poDS->poFileView)
2985 : {
2986 : // comments
2987 : char *csComments = nullptr;
2988 : poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:COMMENTS",
2989 : &csComments);
2990 : if (csComments)
2991 : {
2992 : std::string osComments(csComments);
2993 :
2994 : // Strip off boring Kakadu COM content
2995 : if (STARTS_WITH(osComments.c_str(), "Kakadu-"))
2996 : {
2997 : const auto nEOLPos = osComments.find('\n');
2998 : if (nEOLPos == std::string::npos)
2999 : osComments.clear();
3000 : osComments = osComments.substr(nEOLPos + 1);
3001 : }
3002 : if (STARTS_WITH(
3003 : osComments.c_str(),
3004 : "Kdu-Layer-Info: "
3005 : "log_2{Delta-D(squared-error)/Delta-L(bytes)}, L(bytes)\n"))
3006 : {
3007 : while (true)
3008 : {
3009 : const auto nEOLPos = osComments.find('\n');
3010 : if (nEOLPos == std::string::npos)
3011 : {
3012 : osComments.clear();
3013 : break;
3014 : }
3015 : osComments = osComments.substr(nEOLPos + 1);
3016 : if (osComments.find(", ") == std::string::npos)
3017 : break;
3018 : }
3019 : }
3020 :
3021 : // Strip off boring OpenJPEG COM content
3022 : if (STARTS_WITH(osComments.c_str(),
3023 : "Created by OpenJPEG version ") &&
3024 : osComments.find('\n') == std::string::npos)
3025 : {
3026 : osComments.clear();
3027 : }
3028 :
3029 : if (!osComments.empty())
3030 : poDS->GDALDataset::SetMetadataItem("ALL_COMMENTS",
3031 : osComments.c_str());
3032 : NCSFree(csComments);
3033 : }
3034 :
3035 : // Profile
3036 : UINT32 nProfile = 2;
3037 : UINT32 nRsiz = 0;
3038 : poDS->poFileView->GetParameter((char *)"JP2:COMPLIANCE:PROFILE:TYPE",
3039 : &nRsiz);
3040 : if (nRsiz == 0)
3041 : nProfile = 2; // Profile 2 (no restrictions)
3042 : else if (nRsiz == 1)
3043 : nProfile = 0; // Profile 0
3044 : else if (nRsiz == 2)
3045 : nProfile = 1; // Profile 1, NITF_BIIF_NPJE, NITF_BIIF_EPJE
3046 : poDS->GDALDataset::SetMetadataItem("PROFILE",
3047 : CPLString().Printf("%d", nProfile),
3048 : JPEG2000_DOMAIN_NAME);
3049 :
3050 : // number of tiles on X axis
3051 : UINT32 nTileNrX = 1;
3052 : poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:TILENR:X",
3053 : &nTileNrX);
3054 : poDS->GDALDataset::SetMetadataItem("TILES_X",
3055 : CPLString().Printf("%d", nTileNrX),
3056 : JPEG2000_DOMAIN_NAME);
3057 :
3058 : // number of tiles on X axis
3059 : UINT32 nTileNrY = 1;
3060 : poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:TILENR:Y",
3061 : &nTileNrY);
3062 : poDS->GDALDataset::SetMetadataItem("TILES_Y",
3063 : CPLString().Printf("%d", nTileNrY),
3064 : JPEG2000_DOMAIN_NAME);
3065 :
3066 : // Tile Width
3067 : UINT32 nTileSizeX = 0;
3068 : poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:TILESIZE:X",
3069 : &nTileSizeX);
3070 : poDS->GDALDataset::SetMetadataItem("TILE_WIDTH",
3071 : CPLString().Printf("%d", nTileSizeX),
3072 : JPEG2000_DOMAIN_NAME);
3073 :
3074 : // Tile Height
3075 : UINT32 nTileSizeY = 0;
3076 : poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:TILESIZE:Y",
3077 : &nTileSizeY);
3078 : poDS->GDALDataset::SetMetadataItem("TILE_HEIGHT",
3079 : CPLString().Printf("%d", nTileSizeY),
3080 : JPEG2000_DOMAIN_NAME);
3081 :
3082 : // Precinct Sizes on X axis
3083 : char *csPreSizeX = nullptr;
3084 : poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:PRECINCTSIZE:X",
3085 : &csPreSizeX);
3086 : if (csPreSizeX)
3087 : {
3088 : poDS->GDALDataset::SetMetadataItem("PRECINCT_SIZE_X", csPreSizeX,
3089 : JPEG2000_DOMAIN_NAME);
3090 : NCSFree(csPreSizeX);
3091 : }
3092 :
3093 : // Precinct Sizes on Y axis
3094 : char *csPreSizeY = nullptr;
3095 : poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:PRECINCTSIZE:Y",
3096 : &csPreSizeY);
3097 : if (csPreSizeY)
3098 : {
3099 : poDS->GDALDataset::SetMetadataItem("PRECINCT_SIZE_Y", csPreSizeY,
3100 : JPEG2000_DOMAIN_NAME);
3101 : NCSFree(csPreSizeY);
3102 : }
3103 :
3104 : // Code Block Size on X axis
3105 : UINT32 nCodeBlockSizeX = 0;
3106 : poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:CODEBLOCK:X",
3107 : &nCodeBlockSizeX);
3108 : poDS->GDALDataset::SetMetadataItem(
3109 : "CODE_BLOCK_SIZE_X", CPLString().Printf("%d", nCodeBlockSizeX),
3110 : JPEG2000_DOMAIN_NAME);
3111 :
3112 : // Code Block Size on Y axis
3113 : UINT32 nCodeBlockSizeY = 0;
3114 : poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:CODEBLOCK:Y",
3115 : &nCodeBlockSizeY);
3116 : poDS->GDALDataset::SetMetadataItem(
3117 : "CODE_BLOCK_SIZE_Y", CPLString().Printf("%d", nCodeBlockSizeY),
3118 : JPEG2000_DOMAIN_NAME);
3119 :
3120 : // Bitdepth
3121 : char *csBitdepth = nullptr;
3122 : poDS->poFileView->GetParameter((char *)"JPC:DECOMPRESS:BITDEPTH",
3123 : &csBitdepth);
3124 : if (csBitdepth)
3125 : {
3126 : poDS->GDALDataset::SetMetadataItem("PRECISION", csBitdepth,
3127 : JPEG2000_DOMAIN_NAME);
3128 : NCSFree(csBitdepth);
3129 : }
3130 :
3131 : // Resolution Levels
3132 : UINT32 nLevels = 0;
3133 : poDS->poFileView->GetParameter(
3134 : (char *)"JPC:DECOMPRESS:RESOLUTION:LEVELS", &nLevels);
3135 : poDS->GDALDataset::SetMetadataItem("RESOLUTION_LEVELS",
3136 : CPLString().Printf("%d", nLevels),
3137 : JPEG2000_DOMAIN_NAME);
3138 :
3139 : // Qualaity Layers
3140 : UINT32 nLayers = 0;
3141 : poDS->poFileView->GetParameter((char *)"JP2:DECOMPRESS:LAYERS",
3142 : &nLayers);
3143 : poDS->GDALDataset::SetMetadataItem("QUALITY_LAYERS",
3144 : CPLString().Printf("%d", nLayers),
3145 : JPEG2000_DOMAIN_NAME);
3146 :
3147 : // Progression Order
3148 : char *csOrder = nullptr;
3149 : poDS->poFileView->GetParameter(
3150 : (char *)"JPC:DECOMPRESS:PROGRESSION:ORDER", &csOrder);
3151 : if (csOrder)
3152 : {
3153 : poDS->GDALDataset::SetMetadataItem("PROGRESSION_ORDER", csOrder,
3154 : JPEG2000_DOMAIN_NAME);
3155 : NCSFree(csOrder);
3156 : }
3157 :
3158 : // DWT Filter
3159 : const char *csFilter = nullptr;
3160 : UINT32 nFilter;
3161 : poDS->poFileView->GetParameter((char *)"JP2:TRANSFORMATION:TYPE",
3162 : &nFilter);
3163 : if (nFilter)
3164 : csFilter = "5x3";
3165 : else
3166 : csFilter = "9x7";
3167 : poDS->GDALDataset::SetMetadataItem("TRANSFORMATION_TYPE", csFilter,
3168 : JPEG2000_DOMAIN_NAME);
3169 :
3170 : // SOP used?
3171 : bool bSOP = 0;
3172 : poDS->poFileView->GetParameter((char *)"JP2:DECOMPRESS:SOP:EXISTS",
3173 : &bSOP);
3174 : poDS->SetMetadataItem("USE_SOP", (bSOP) ? "TRUE" : "FALSE",
3175 : JPEG2000_DOMAIN_NAME);
3176 :
3177 : // EPH used?
3178 : bool bEPH = 0;
3179 : poDS->poFileView->GetParameter((char *)"JP2:DECOMPRESS:EPH:EXISTS",
3180 : &bEPH);
3181 : poDS->SetMetadataItem("USE_EPH", (bEPH) ? "TRUE" : "FALSE",
3182 : JPEG2000_DOMAIN_NAME);
3183 :
3184 : // GML JP2 data contained?
3185 : bool bGML = 0;
3186 : poDS->poFileView->GetParameter((char *)"JP2:GML:JP2:BOX:EXISTS", &bGML);
3187 : poDS->SetMetadataItem("GML_JP2_DATA", (bGML) ? "TRUE" : "FALSE",
3188 : JPEG2000_DOMAIN_NAME);
3189 : }
3190 : #endif // ECWSDK_VERSION>=51
3191 : if (!bIsJPEG2000 && poDS->psFileInfo->nFormatVersion >= 3)
3192 : {
3193 : poDS->GDALDataset::SetMetadataItem(
3194 : "COMPRESSION_RATE_ACTUAL",
3195 : CPLString().Printf("%f", poDS->psFileInfo->fActualCompressionRate));
3196 : poDS->GDALDataset::SetMetadataItem(
3197 : "CLOCKWISE_ROTATION_DEG",
3198 : CPLString().Printf("%f", poDS->psFileInfo->fCWRotationDegrees));
3199 : poDS->GDALDataset::SetMetadataItem("COMPRESSION_DATE",
3200 : poDS->psFileInfo->sCompressionDate);
3201 : // Get file metadata.
3202 : poDS->ReadFileMetaDataFromFile();
3203 : }
3204 : #else
3205 123 : poDS->GDALDataset::SetMetadataItem(
3206 246 : "VERSION", CPLString().Printf("%d", bIsJPEG2000 ? 1 : 2));
3207 : #endif
3208 :
3209 : /* -------------------------------------------------------------------- */
3210 : /* Initialize any PAM information. */
3211 : /* -------------------------------------------------------------------- */
3212 123 : poDS->SetDescription(osFilename);
3213 123 : poDS->TryLoadXML(poOpenInfo->GetSiblingFiles());
3214 :
3215 : /* -------------------------------------------------------------------- */
3216 : /* Vector layers */
3217 : /* -------------------------------------------------------------------- */
3218 123 : if (bIsJPEG2000 && poOpenInfo->nOpenFlags & GDAL_OF_VECTOR)
3219 : {
3220 1 : poDS->LoadVectorLayers(CPLFetchBool(poOpenInfo->papszOpenOptions,
3221 : "OPEN_REMOTE_GML", false));
3222 :
3223 : // If file opened in vector-only mode and there's no vector,
3224 : // return
3225 1 : if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0 &&
3226 0 : poDS->GetLayerCount() == 0)
3227 : {
3228 0 : delete poDS;
3229 0 : return nullptr;
3230 : }
3231 : }
3232 :
3233 123 : return poDS;
3234 : }
3235 :
3236 : /************************************************************************/
3237 : /* GetMetadataDomainList() */
3238 : /************************************************************************/
3239 :
3240 4 : char **ECWDataset::GetMetadataDomainList()
3241 : {
3242 4 : return BuildMetadataDomainList(
3243 : GDALJP2AbstractDataset::GetMetadataDomainList(), TRUE, "ECW", "GML",
3244 4 : nullptr);
3245 : }
3246 :
3247 : /************************************************************************/
3248 : /* GetMetadataItem() */
3249 : /************************************************************************/
3250 :
3251 55 : const char *ECWDataset::GetMetadataItem(const char *pszName,
3252 : const char *pszDomain)
3253 : {
3254 55 : if (!bIsJPEG2000 && pszDomain != nullptr && EQUAL(pszDomain, "ECW") &&
3255 : pszName != nullptr)
3256 : {
3257 6 : if (EQUAL(pszName, "PROJ"))
3258 2 : return m_osProjCode.size() ? m_osProjCode.c_str() : "RAW";
3259 4 : if (EQUAL(pszName, "DATUM"))
3260 2 : return m_osDatumCode.size() ? m_osDatumCode.c_str() : "RAW";
3261 2 : if (EQUAL(pszName, "UNITS"))
3262 2 : return m_osUnitsCode.size() ? m_osUnitsCode.c_str() : "METERS";
3263 : }
3264 49 : return GDALJP2AbstractDataset::GetMetadataItem(pszName, pszDomain);
3265 : }
3266 :
3267 : /************************************************************************/
3268 : /* GetMetadata() */
3269 : /************************************************************************/
3270 :
3271 90 : CSLConstList ECWDataset::GetMetadata(const char *pszDomain)
3272 :
3273 : {
3274 90 : if (!bIsJPEG2000 && pszDomain != nullptr && EQUAL(pszDomain, "ECW"))
3275 : {
3276 0 : oECWMetadataList.Clear();
3277 : oECWMetadataList.AddString(
3278 0 : CPLSPrintf("%s=%s", "PROJ", GetMetadataItem("PROJ", "ECW")));
3279 : oECWMetadataList.AddString(
3280 0 : CPLSPrintf("%s=%s", "DATUM", GetMetadataItem("DATUM", "ECW")));
3281 : oECWMetadataList.AddString(
3282 0 : CPLSPrintf("%s=%s", "UNITS", GetMetadataItem("UNITS", "ECW")));
3283 0 : return oECWMetadataList.List();
3284 : }
3285 90 : else if (pszDomain == nullptr || !EQUAL(pszDomain, "GML"))
3286 86 : return GDALJP2AbstractDataset::GetMetadata(pszDomain);
3287 : else
3288 4 : return papszGMLMetadata;
3289 : }
3290 :
3291 : /************************************************************************/
3292 : /* ReadFileMetaDataFromFile() */
3293 : /* */
3294 : /* Gets relevant information from NCSFileMetadata and populates */
3295 : /* GDAL metadata. */
3296 : /* */
3297 : /************************************************************************/
3298 : #if ECWSDK_VERSION >= 50
3299 : void ECWDataset::ReadFileMetaDataFromFile()
3300 : {
3301 : if (psFileInfo->pFileMetaData == nullptr)
3302 : return;
3303 :
3304 : if (psFileInfo->pFileMetaData->sClassification != nullptr)
3305 : GDALDataset::SetMetadataItem(
3306 : "FILE_METADATA_CLASSIFICATION",
3307 : NCS::CString(psFileInfo->pFileMetaData->sClassification));
3308 : if (psFileInfo->pFileMetaData->sAcquisitionDate != nullptr)
3309 : GDALDataset::SetMetadataItem(
3310 : "FILE_METADATA_ACQUISITION_DATE",
3311 : NCS::CString(psFileInfo->pFileMetaData->sAcquisitionDate));
3312 : if (psFileInfo->pFileMetaData->sAcquisitionSensorName != nullptr)
3313 : GDALDataset::SetMetadataItem(
3314 : "FILE_METADATA_ACQUISITION_SENSOR_NAME",
3315 : NCS::CString(psFileInfo->pFileMetaData->sAcquisitionSensorName));
3316 : if (psFileInfo->pFileMetaData->sCompressionSoftware != nullptr)
3317 : GDALDataset::SetMetadataItem(
3318 : "FILE_METADATA_COMPRESSION_SOFTWARE",
3319 : NCS::CString(psFileInfo->pFileMetaData->sCompressionSoftware));
3320 : if (psFileInfo->pFileMetaData->sAuthor != nullptr)
3321 : GDALDataset::SetMetadataItem(
3322 : "FILE_METADATA_AUTHOR",
3323 : NCS::CString(psFileInfo->pFileMetaData->sAuthor));
3324 : if (psFileInfo->pFileMetaData->sCopyright != nullptr)
3325 : GDALDataset::SetMetadataItem(
3326 : "FILE_METADATA_COPYRIGHT",
3327 : NCS::CString(psFileInfo->pFileMetaData->sCopyright));
3328 : if (psFileInfo->pFileMetaData->sCompany != nullptr)
3329 : GDALDataset::SetMetadataItem(
3330 : "FILE_METADATA_COMPANY",
3331 : NCS::CString(psFileInfo->pFileMetaData->sCompany));
3332 : if (psFileInfo->pFileMetaData->sEmail != nullptr)
3333 : GDALDataset::SetMetadataItem(
3334 : "FILE_METADATA_EMAIL",
3335 : NCS::CString(psFileInfo->pFileMetaData->sEmail));
3336 : if (psFileInfo->pFileMetaData->sAddress != nullptr)
3337 : GDALDataset::SetMetadataItem(
3338 : "FILE_METADATA_ADDRESS",
3339 : NCS::CString(psFileInfo->pFileMetaData->sAddress));
3340 : if (psFileInfo->pFileMetaData->sTelephone != nullptr)
3341 : GDALDataset::SetMetadataItem(
3342 : "FILE_METADATA_TELEPHONE",
3343 : NCS::CString(psFileInfo->pFileMetaData->sTelephone));
3344 : }
3345 :
3346 : /************************************************************************/
3347 : /* WriteFileMetaData() */
3348 : /************************************************************************/
3349 :
3350 : void ECWDataset::WriteFileMetaData(NCSFileMetaData *pFileMetaDataCopy)
3351 : {
3352 : if (!bFileMetaDataDirty)
3353 : return;
3354 :
3355 : CPLAssert(eAccess == GA_Update);
3356 : CPLAssert(!bIsJPEG2000);
3357 :
3358 : bFileMetaDataDirty = FALSE;
3359 :
3360 : NCSFileView *psFileView = nullptr;
3361 : NCSError eErr;
3362 :
3363 : psFileView = NCSEditOpen(GetDescription());
3364 : if (psFileView == nullptr)
3365 : {
3366 : CPLError(CE_Failure, CPLE_AppDefined, "NCSEditOpen() failed");
3367 : return;
3368 : }
3369 :
3370 : eErr = NCSEditSetFileMetaData(psFileView, pFileMetaDataCopy);
3371 : if (eErr != NCS_SUCCESS)
3372 : {
3373 : CPLError(CE_Failure, CPLE_AppDefined,
3374 : "NCSEditSetFileMetaData() failed : %s",
3375 : NCSGetLastErrorText(eErr));
3376 : }
3377 :
3378 : NCSEditFlushAll(psFileView);
3379 : NCSEditClose(psFileView);
3380 : }
3381 :
3382 : #endif
3383 : /************************************************************************/
3384 : /* ECW2WKTProjection() */
3385 : /* */
3386 : /* Set the dataset pszProjection string in OGC WKT format by */
3387 : /* looking up the ECW (GDT) coordinate system info in */
3388 : /* ecw_cs.wkt support data file. */
3389 : /* */
3390 : /* This code is likely still broken in some circumstances. For */
3391 : /* instance, I haven't been careful about changing the linear */
3392 : /* projection parameters (false easting/northing) if the units */
3393 : /* is feet. Lots of cases missing here, and in ecw_cs.wkt. */
3394 : /************************************************************************/
3395 :
3396 34 : void ECWDataset::ECW2WKTProjection()
3397 :
3398 : {
3399 34 : if (psFileInfo == nullptr)
3400 20 : return;
3401 :
3402 : /* -------------------------------------------------------------------- */
3403 : /* Capture Geotransform. */
3404 : /* */
3405 : /* We will try to ignore the provided file information if it is */
3406 : /* origin (0,0) and pixel size (1,1). I think sometimes I have */
3407 : /* also seen pixel increments of 0 on invalid datasets. */
3408 : /* -------------------------------------------------------------------- */
3409 34 : if (psFileInfo->fOriginX != 0.0 || psFileInfo->fOriginY != 0.0 ||
3410 0 : (psFileInfo->fCellIncrementX != 0.0 &&
3411 0 : psFileInfo->fCellIncrementX != 1.0) ||
3412 0 : (psFileInfo->fCellIncrementY != 0.0 &&
3413 0 : psFileInfo->fCellIncrementY != 1.0))
3414 : {
3415 34 : bGeoTransformValid = TRUE;
3416 :
3417 34 : m_gt.xorig = psFileInfo->fOriginX;
3418 34 : m_gt.xscale = psFileInfo->fCellIncrementX;
3419 34 : m_gt.xrot = 0.0;
3420 :
3421 34 : m_gt.yorig = psFileInfo->fOriginY;
3422 34 : m_gt.yrot = 0.0;
3423 :
3424 : /* By default, set Y-resolution negative assuming images always */
3425 : /* have "Upward" orientation (Y coordinates increase "Upward"). */
3426 : /* Setting ECW_ALWAYS_UPWARD=FALSE option relexes that policy */
3427 : /* and makes the driver rely on the actual Y-resolution */
3428 : /* value (sign) of an image. This allows correctly processing */
3429 : /* rare images with "Downward" orientation, where Y coordinates */
3430 : /* increase "Downward" and Y-resolution is positive. */
3431 34 : if (CPLTestBool(CPLGetConfigOption("ECW_ALWAYS_UPWARD", "TRUE")))
3432 33 : m_gt.yscale = -fabs(psFileInfo->fCellIncrementY);
3433 : else
3434 1 : m_gt.yscale = psFileInfo->fCellIncrementY;
3435 : }
3436 :
3437 : /* -------------------------------------------------------------------- */
3438 : /* do we have projection and datum? */
3439 : /* -------------------------------------------------------------------- */
3440 : CPLString osUnits =
3441 34 : ECWTranslateFromCellSizeUnits(psFileInfo->eCellSizeUnits);
3442 :
3443 34 : CPLDebug("ECW", "projection=%s, datum=%s, units=%s",
3444 34 : psFileInfo->szProjection, psFileInfo->szDatum, osUnits.c_str());
3445 :
3446 34 : if (EQUAL(psFileInfo->szProjection, "RAW"))
3447 20 : return;
3448 :
3449 : /* -------------------------------------------------------------------- */
3450 : /* Set projection if we have it. */
3451 : /* -------------------------------------------------------------------- */
3452 28 : OGRSpatialReference oSRS;
3453 :
3454 : /* For backward-compatible with previous behavior. Should we only */
3455 : /* restrict to those 2 values ? */
3456 14 : if (psFileInfo->eCellSizeUnits != ECW_CELL_UNITS_METERS &&
3457 4 : psFileInfo->eCellSizeUnits != ECW_CELL_UNITS_FEET)
3458 2 : osUnits = ECWTranslateFromCellSizeUnits(ECW_CELL_UNITS_METERS);
3459 :
3460 14 : m_osDatumCode = psFileInfo->szDatum;
3461 14 : m_osProjCode = psFileInfo->szProjection;
3462 14 : m_osUnitsCode = osUnits;
3463 14 : if (oSRS.importFromERM(psFileInfo->szProjection, psFileInfo->szDatum,
3464 14 : osUnits) == OGRERR_NONE)
3465 : {
3466 14 : m_oSRS = std::move(oSRS);
3467 14 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
3468 : }
3469 :
3470 14 : CPLErrorReset(); /* see #4187 */
3471 : }
3472 :
3473 : /************************************************************************/
3474 : /* ECWTranslateFromWKT() */
3475 : /************************************************************************/
3476 :
3477 27 : int ECWTranslateFromWKT(const OGRSpatialReference *poSRS, char *pszProjection,
3478 : int nProjectionLen, char *pszDatum, int nDatumLen,
3479 : char *pszUnits)
3480 :
3481 : {
3482 54 : OGRSpatialReference oSRS;
3483 :
3484 27 : strcpy(pszProjection, "RAW");
3485 27 : strcpy(pszDatum, "RAW");
3486 27 : strcpy(pszUnits, "METERS");
3487 :
3488 27 : if (poSRS == nullptr || poSRS->IsEmpty())
3489 0 : return FALSE;
3490 :
3491 27 : oSRS = *poSRS;
3492 :
3493 27 : if (oSRS.IsLocal())
3494 0 : return TRUE;
3495 :
3496 : /* -------------------------------------------------------------------- */
3497 : /* Do we have an overall EPSG number for this coordinate system? */
3498 : /* -------------------------------------------------------------------- */
3499 27 : const char *pszAuthorityCode = nullptr;
3500 27 : const char *pszAuthorityName = nullptr;
3501 27 : UINT32 nEPSGCode = 0;
3502 :
3503 27 : if (oSRS.IsProjected())
3504 : {
3505 8 : pszAuthorityCode = oSRS.GetAuthorityCode("PROJCS");
3506 8 : pszAuthorityName = oSRS.GetAuthorityName("PROJCS");
3507 : }
3508 19 : else if (oSRS.IsGeographic())
3509 : {
3510 19 : pszAuthorityCode = oSRS.GetAuthorityCode("GEOGCS");
3511 19 : pszAuthorityName = oSRS.GetAuthorityName("GEOGCS");
3512 : }
3513 :
3514 27 : if (pszAuthorityName != nullptr && EQUAL(pszAuthorityName, "EPSG") &&
3515 11 : pszAuthorityCode != nullptr && atoi(pszAuthorityCode) > 0)
3516 11 : nEPSGCode = (UINT32)atoi(pszAuthorityCode);
3517 :
3518 27 : if (nEPSGCode != 0)
3519 : {
3520 11 : char *pszEPSGProj = nullptr, *pszEPSGDatum = nullptr;
3521 : CNCSError oErr = CNCSJP2FileView::GetProjectionAndDatum(
3522 11 : atoi(pszAuthorityCode), &pszEPSGProj, &pszEPSGDatum);
3523 :
3524 22 : CPLDebug("ECW", "GetGDTProjDat(%d) = %s/%s", atoi(pszAuthorityCode),
3525 11 : pszEPSGProj ? pszEPSGProj : "(null)",
3526 11 : pszEPSGDatum ? pszEPSGDatum : "(null)");
3527 :
3528 22 : if (oErr.GetErrorNumber() == NCS_SUCCESS && pszEPSGProj != nullptr &&
3529 11 : pszEPSGDatum != nullptr)
3530 : {
3531 11 : strncpy(pszProjection, pszEPSGProj, nProjectionLen);
3532 11 : strncpy(pszDatum, pszEPSGDatum, nDatumLen);
3533 11 : pszProjection[nProjectionLen - 1] = 0;
3534 11 : pszDatum[nDatumLen - 1] = 0;
3535 11 : NCSFree(pszEPSGProj);
3536 11 : NCSFree(pszEPSGDatum);
3537 11 : return TRUE;
3538 : }
3539 :
3540 0 : NCSFree(pszEPSGProj);
3541 0 : NCSFree(pszEPSGDatum);
3542 : }
3543 :
3544 : /* -------------------------------------------------------------------- */
3545 : /* Fallback to translating based on the ecw_cs.wkt file, and */
3546 : /* various jiffy rules. */
3547 : /* -------------------------------------------------------------------- */
3548 :
3549 16 : return oSRS.exportToERM(pszProjection, pszDatum, pszUnits) == OGRERR_NONE;
3550 : }
3551 :
3552 : /************************************************************************/
3553 : /* ECWTranslateToCellSizeUnits() */
3554 : /************************************************************************/
3555 :
3556 28 : CellSizeUnits ECWTranslateToCellSizeUnits(const char *pszUnits)
3557 : {
3558 28 : if (EQUAL(pszUnits, "METERS"))
3559 26 : return ECW_CELL_UNITS_METERS;
3560 2 : else if (EQUAL(pszUnits, "DEGREES"))
3561 0 : return ECW_CELL_UNITS_DEGREES;
3562 2 : else if (EQUAL(pszUnits, "FEET"))
3563 2 : return ECW_CELL_UNITS_FEET;
3564 0 : else if (EQUAL(pszUnits, "UNKNOWN"))
3565 0 : return ECW_CELL_UNITS_UNKNOWN;
3566 0 : else if (EQUAL(pszUnits, "INVALID"))
3567 0 : return ECW_CELL_UNITS_INVALID;
3568 : else
3569 : {
3570 0 : CPLError(CE_Warning, CPLE_AppDefined,
3571 : "Unrecognized value for UNITS : %s", pszUnits);
3572 0 : return ECW_CELL_UNITS_INVALID;
3573 : }
3574 : }
3575 :
3576 : /************************************************************************/
3577 : /* ECWGetColorInterpretationByName() */
3578 : /************************************************************************/
3579 :
3580 228 : GDALColorInterp ECWGetColorInterpretationByName(const char *pszName)
3581 : {
3582 228 : if (EQUAL(pszName, NCS_BANDDESC_AllOpacity))
3583 7 : return GCI_AlphaBand;
3584 221 : else if (EQUAL(pszName, NCS_BANDDESC_Blue))
3585 51 : return GCI_BlueBand;
3586 170 : else if (EQUAL(pszName, NCS_BANDDESC_Green))
3587 51 : return GCI_GreenBand;
3588 119 : else if (EQUAL(pszName, NCS_BANDDESC_Red))
3589 51 : return GCI_RedBand;
3590 68 : else if (EQUAL(pszName, NCS_BANDDESC_Greyscale))
3591 0 : return GCI_GrayIndex;
3592 68 : else if (EQUAL(pszName, NCS_BANDDESC_GreyscaleOpacity))
3593 0 : return GCI_AlphaBand;
3594 68 : return GCI_Undefined;
3595 : }
3596 :
3597 : /************************************************************************/
3598 : /* ECWGetColorInterpretationName() */
3599 : /************************************************************************/
3600 :
3601 59 : const char *ECWGetColorInterpretationName(GDALColorInterp eColorInterpretation,
3602 : int nBandNumber)
3603 : {
3604 59 : const char *pszResult = nullptr;
3605 59 : switch (eColorInterpretation)
3606 : {
3607 0 : case GCI_AlphaBand:
3608 0 : pszResult = NCS_BANDDESC_AllOpacity;
3609 0 : break;
3610 17 : case GCI_GrayIndex:
3611 17 : pszResult = NCS_BANDDESC_Greyscale;
3612 17 : break;
3613 12 : case GCI_RedBand:
3614 : case GCI_GreenBand:
3615 : case GCI_BlueBand:
3616 12 : pszResult = GDALGetColorInterpretationName(eColorInterpretation);
3617 12 : break;
3618 30 : case GCI_Undefined:
3619 30 : if (nBandNumber == 0)
3620 : {
3621 20 : pszResult = "Red";
3622 : }
3623 10 : else if (nBandNumber == 1)
3624 : {
3625 4 : pszResult = "Green";
3626 : }
3627 6 : else if (nBandNumber == 2)
3628 : {
3629 3 : pszResult = "Blue";
3630 : }
3631 : else
3632 : {
3633 3 : pszResult = CPLSPrintf(NCS_BANDDESC_Band, nBandNumber + 1);
3634 : }
3635 30 : break;
3636 0 : default:
3637 0 : pszResult = CPLSPrintf(NCS_BANDDESC_Band, nBandNumber + 1);
3638 0 : break;
3639 : }
3640 59 : return pszResult;
3641 : }
3642 :
3643 : /************************************************************************/
3644 : /* ECWGetColorSpaceName() */
3645 : /************************************************************************/
3646 :
3647 123 : const char *ECWGetColorSpaceName(NCSFileColorSpace colorSpace)
3648 : {
3649 123 : switch (colorSpace)
3650 : {
3651 0 : case NCSCS_NONE:
3652 0 : return "NONE";
3653 : break;
3654 54 : case NCSCS_GREYSCALE:
3655 54 : return "GREYSCALE";
3656 : break;
3657 0 : case NCSCS_YUV:
3658 0 : return "YUV";
3659 : break;
3660 36 : case NCSCS_MULTIBAND:
3661 36 : return "MULTIBAND";
3662 : break;
3663 33 : case NCSCS_sRGB:
3664 33 : return "RGB";
3665 : break;
3666 0 : case NCSCS_YCbCr:
3667 0 : return "YCbCr";
3668 : break;
3669 0 : default:
3670 0 : return "unrecognized";
3671 : }
3672 : }
3673 :
3674 : /************************************************************************/
3675 : /* ECWTranslateFromCellSizeUnits() */
3676 : /************************************************************************/
3677 :
3678 77 : const char *ECWTranslateFromCellSizeUnits(CellSizeUnits eUnits)
3679 : {
3680 77 : if (eUnits == ECW_CELL_UNITS_METERS)
3681 71 : return "METERS";
3682 6 : else if (eUnits == ECW_CELL_UNITS_DEGREES)
3683 2 : return "DEGREES";
3684 4 : else if (eUnits == ECW_CELL_UNITS_FEET)
3685 4 : return "FEET";
3686 0 : else if (eUnits == ECW_CELL_UNITS_UNKNOWN)
3687 0 : return "UNKNOWN";
3688 : else
3689 0 : return "INVALID";
3690 : }
3691 :
3692 : /************************************************************************/
3693 : /* ECWInitialize() */
3694 : /* */
3695 : /* Initialize NCS library. We try to defer this as late as */
3696 : /* possible since de-initializing it seems to be expensive/slow */
3697 : /* on some system. */
3698 : /************************************************************************/
3699 :
3700 193 : void ECWInitialize()
3701 :
3702 : {
3703 193 : CPLMutexHolder oHolder(&hECWDatasetMutex);
3704 :
3705 193 : if (bNCSInitialized)
3706 187 : return;
3707 :
3708 : #ifndef _WIN32
3709 6 : NCSecwInit();
3710 : #endif
3711 6 : bNCSInitialized = TRUE;
3712 :
3713 : /* -------------------------------------------------------------------- */
3714 : /* This will disable automatic conversion of YCbCr to RGB by */
3715 : /* the toolkit. */
3716 : /* -------------------------------------------------------------------- */
3717 6 : if (!CPLTestBool(CPLGetConfigOption("CONVERT_YCBCR_TO_RGB", "YES")))
3718 0 : NCSecwSetConfig(NCSCFG_JP2_MANAGE_ICC, FALSE);
3719 : #if ECWSDK_VERSION >= 50
3720 : NCSecwSetConfig(NCSCFG_ECWP_CLIENT_HTTP_USER_AGENT,
3721 : "ECW GDAL Driver/" NCS_ECWJP2_FULL_VERSION_STRING_DOT_DEL);
3722 : #endif
3723 : /* -------------------------------------------------------------------- */
3724 : /* Initialize cache memory limit. Default is apparently 1/4 RAM. */
3725 : /* -------------------------------------------------------------------- */
3726 : const char *pszEcwCacheSize =
3727 6 : CPLGetConfigOption("GDAL_ECW_CACHE_MAXMEM", nullptr);
3728 6 : if (pszEcwCacheSize == nullptr)
3729 6 : pszEcwCacheSize = CPLGetConfigOption("ECW_CACHE_MAXMEM", nullptr);
3730 :
3731 6 : if (pszEcwCacheSize != nullptr)
3732 0 : NCSecwSetConfig(NCSCFG_CACHE_MAXMEM, (UINT32)atoi(pszEcwCacheSize));
3733 :
3734 : /* -------------------------------------------------------------------- */
3735 : /* Version 3.x and 4.x of the ECWJP2 SDK did not resolve datum and */
3736 : /* projection to EPSG code using internal mapping. */
3737 : /* Version 5.x do so we provide means to achieve old */
3738 : /* behavior. */
3739 : /* -------------------------------------------------------------------- */
3740 : #if ECWSDK_VERSION >= 50
3741 : if (CPLTestBool(CPLGetConfigOption("ECW_DO_NOT_RESOLVE_DATUM_PROJECTION",
3742 : "NO")) == TRUE)
3743 : NCSecwSetConfig(NCSCFG_PROJECTION_FORMAT,
3744 : NCS_PROJECTION_ERMAPPER_FORMAT);
3745 : #endif
3746 : /* -------------------------------------------------------------------- */
3747 : /* Allow configuration of a local cache based on configuration */
3748 : /* options. Setting the location turns things on. */
3749 : /* -------------------------------------------------------------------- */
3750 6 : const char *pszOpt = nullptr;
3751 6 : CPL_IGNORE_RET_VAL(pszOpt);
3752 :
3753 : #if ECWSDK_VERSION >= 40
3754 : pszOpt = CPLGetConfigOption("ECWP_CACHE_SIZE_MB", nullptr);
3755 : if (pszOpt)
3756 : NCSecwSetConfig(NCSCFG_ECWP_CACHE_SIZE_MB, (INT32)atoi(pszOpt));
3757 :
3758 : pszOpt = CPLGetConfigOption("ECWP_CACHE_LOCATION", nullptr);
3759 : if (pszOpt)
3760 : {
3761 : NCSecwSetConfig(NCSCFG_ECWP_CACHE_LOCATION, pszOpt);
3762 : NCSecwSetConfig(NCSCFG_ECWP_CACHE_ENABLED, (BOOLEAN)TRUE);
3763 : }
3764 : #endif
3765 :
3766 : /* -------------------------------------------------------------------- */
3767 : /* Various other configuration items. */
3768 : /* -------------------------------------------------------------------- */
3769 6 : pszOpt = CPLGetConfigOption("ECWP_BLOCKING_TIME_MS", nullptr);
3770 6 : if (pszOpt)
3771 0 : NCSecwSetConfig(NCSCFG_BLOCKING_TIME_MS, (NCSTimeStampMs)atoi(pszOpt));
3772 :
3773 : // I believe 10s means we wait for complete data back from
3774 : // ECWP almost all the time which is good for our blocking model.
3775 6 : pszOpt = CPLGetConfigOption("ECWP_REFRESH_TIME_MS", "10000");
3776 6 : if (pszOpt)
3777 6 : NCSecwSetConfig(NCSCFG_REFRESH_TIME_MS, (NCSTimeStampMs)atoi(pszOpt));
3778 :
3779 6 : pszOpt = CPLGetConfigOption("ECW_TEXTURE_DITHER", nullptr);
3780 6 : if (pszOpt)
3781 0 : NCSecwSetConfig(NCSCFG_TEXTURE_DITHER, (BOOLEAN)CPLTestBool(pszOpt));
3782 :
3783 6 : pszOpt = CPLGetConfigOption("ECW_FORCE_FILE_REOPEN", nullptr);
3784 6 : if (pszOpt)
3785 0 : NCSecwSetConfig(NCSCFG_FORCE_FILE_REOPEN, (BOOLEAN)CPLTestBool(pszOpt));
3786 :
3787 6 : pszOpt = CPLGetConfigOption("ECW_CACHE_MAXOPEN", nullptr);
3788 6 : if (pszOpt)
3789 0 : NCSecwSetConfig(NCSCFG_CACHE_MAXOPEN, (UINT32)atoi(pszOpt));
3790 :
3791 : #if ECWSDK_VERSION >= 40
3792 : pszOpt = CPLGetConfigOption("ECW_AUTOGEN_J2I", nullptr);
3793 : if (pszOpt)
3794 : NCSecwSetConfig(NCSCFG_JP2_AUTOGEN_J2I, (BOOLEAN)CPLTestBool(pszOpt));
3795 :
3796 : pszOpt = CPLGetConfigOption("ECW_OPTIMIZE_USE_NEAREST_NEIGHBOUR", nullptr);
3797 : if (pszOpt)
3798 : NCSecwSetConfig(NCSCFG_OPTIMIZE_USE_NEAREST_NEIGHBOUR,
3799 : (BOOLEAN)CPLTestBool(pszOpt));
3800 :
3801 : pszOpt = CPLGetConfigOption("ECW_RESILIENT_DECODING", nullptr);
3802 : if (pszOpt)
3803 : NCSecwSetConfig(NCSCFG_RESILIENT_DECODING,
3804 : (BOOLEAN)CPLTestBool(pszOpt));
3805 : #endif
3806 : }
3807 :
3808 : /************************************************************************/
3809 : /* GDALDeregister_ECW() */
3810 : /************************************************************************/
3811 :
3812 9 : static void GDALDeregister_ECW(GDALDriver *)
3813 :
3814 : {
3815 : /* For unknown reason, this cleanup can take up to 3 seconds (see #3134) for
3816 : * SDK 3.3. */
3817 : /* Not worth it */
3818 : #if ECWSDK_VERSION >= 50
3819 : #ifndef _WIN32
3820 : if (bNCSInitialized)
3821 : {
3822 : bNCSInitialized = FALSE;
3823 : NCSecwShutdown();
3824 : }
3825 : #endif
3826 : #endif
3827 :
3828 9 : if (hECWDatasetMutex != nullptr)
3829 : {
3830 4 : CPLDestroyMutex(hECWDatasetMutex);
3831 4 : hECWDatasetMutex = nullptr;
3832 : }
3833 9 : }
3834 :
3835 : #if ECWSDK_VERSION < 40
3836 : namespace
3837 : {
3838 82 : NCSError NCS_CALL EcwFileOpenForReadACB(char *szFileName, void **ppClientData)
3839 : {
3840 82 : *ppClientData = VSIFOpenL(szFileName, "rb");
3841 82 : if (*ppClientData == nullptr)
3842 : {
3843 15 : return NCS_FILE_OPEN_FAILED;
3844 : }
3845 : else
3846 : {
3847 67 : return NCS_SUCCESS;
3848 : }
3849 : }
3850 :
3851 0 : NCSError NCS_CALL EcwFileOpenForReadWCB(wchar_t *wszFileName,
3852 : void **ppClientData)
3853 : {
3854 : char *szFileName =
3855 0 : CPLRecodeFromWChar(wszFileName, CPL_ENC_UCS2, CPL_ENC_UTF8);
3856 0 : *ppClientData = VSIFOpenL(szFileName, "rb");
3857 0 : CPLFree(szFileName);
3858 0 : if (*ppClientData == nullptr)
3859 : {
3860 0 : return NCS_FILE_OPEN_FAILED;
3861 : }
3862 : else
3863 : {
3864 0 : return NCS_SUCCESS;
3865 : }
3866 : }
3867 :
3868 67 : NCSError NCS_CALL EcwFileCloseCB(void *pClientData)
3869 : {
3870 67 : if (0 == VSIFCloseL(reinterpret_cast<VSILFILE *>(pClientData)))
3871 : {
3872 67 : return NCS_SUCCESS;
3873 : }
3874 : else
3875 : {
3876 0 : return NCS_FILE_CLOSE_ERROR;
3877 : }
3878 : }
3879 :
3880 1557 : NCSError NCS_CALL EcwFileReadCB(void *pClientData, void *pBuffer,
3881 : UINT32 nLength)
3882 : {
3883 1557 : if (nLength == VSIFReadL(pBuffer, 1, nLength,
3884 : reinterpret_cast<VSILFILE *>(pClientData)))
3885 : {
3886 1557 : return NCS_SUCCESS;
3887 : }
3888 : else
3889 : {
3890 0 : return NCS_FILE_IO_ERROR;
3891 : }
3892 : }
3893 :
3894 208 : NCSError NCS_CALL EcwFileSeekCB(void *pClientData, UINT64 nOffset)
3895 : {
3896 208 : if (0 ==
3897 208 : VSIFSeekL(reinterpret_cast<VSILFILE *>(pClientData), nOffset, SEEK_SET))
3898 : {
3899 208 : return NCS_SUCCESS;
3900 : }
3901 : else
3902 : {
3903 0 : return NCS_FILE_SEEK_ERROR;
3904 : }
3905 : }
3906 :
3907 108 : NCSError NCS_CALL EcwFileTellCB(void *pClientData, UINT64 *pOffset)
3908 : {
3909 108 : *pOffset = VSIFTellL(reinterpret_cast<VSILFILE *>(pClientData));
3910 108 : return NCS_SUCCESS;
3911 : }
3912 : } // namespace
3913 : #endif // ECWSDK_VERSION < 40
3914 :
3915 : /************************************************************************/
3916 : /* GDALRegister_ECW() */
3917 : /************************************************************************/
3918 :
3919 14 : void GDALRegister_ECW()
3920 :
3921 : {
3922 14 : if (!GDAL_CHECK_VERSION("ECW driver"))
3923 0 : return;
3924 :
3925 14 : if (GDALGetDriverByName(ECW_DRIVER_NAME) != nullptr)
3926 0 : return;
3927 : #if ECWSDK_VERSION < 40
3928 14 : CNCSJPCFileIOStream::SetIOCallbacks(
3929 : EcwFileOpenForReadACB, EcwFileOpenForReadWCB, EcwFileCloseCB,
3930 : EcwFileReadCB, EcwFileSeekCB, EcwFileTellCB);
3931 : #endif // ECWSDK_VERSION < 40
3932 14 : GDALDriver *poDriver = new GDALDriver();
3933 :
3934 14 : ECWDriverSetCommonMetadata(poDriver);
3935 14 : poDriver->pfnOpen = ECWDataset::OpenECW;
3936 14 : poDriver->pfnUnloadDriver = GDALDeregister_ECW;
3937 : #ifdef HAVE_COMPRESS
3938 : // The create method does not work with SDK 3.3 ( crash in
3939 : // CNCSJP2FileView::WriteLineBIL() due to m_pFile being nullptr ).
3940 : #if ECWSDK_VERSION >= 50
3941 : poDriver->pfnCreate = ECWCreateECW;
3942 : #endif
3943 14 : poDriver->pfnCreateCopy = ECWCreateCopyECW;
3944 : #endif
3945 :
3946 14 : GetGDALDriverManager()->RegisterDriver(poDriver);
3947 : }
3948 :
3949 : /************************************************************************/
3950 : /* GDALRegister_ECW_JP2ECW() */
3951 : /* */
3952 : /* This function exists so that when built as a plugin, there */
3953 : /* is a function that will register both drivers. */
3954 : /************************************************************************/
3955 :
3956 14 : void GDALRegister_ECW_JP2ECW()
3957 :
3958 : {
3959 14 : GDALRegister_ECW();
3960 14 : GDALRegister_JP2ECW();
3961 14 : }
3962 :
3963 : /************************************************************************/
3964 : /* ECWDatasetOpenJPEG2000() */
3965 : /************************************************************************/
3966 29 : GDALDataset *ECWDatasetOpenJPEG2000(GDALOpenInfo *poOpenInfo)
3967 : {
3968 29 : return ECWDataset::OpenJPEG2000(poOpenInfo);
3969 : }
3970 :
3971 : /************************************************************************/
3972 : /* GDALRegister_JP2ECW() */
3973 : /************************************************************************/
3974 14 : void GDALRegister_JP2ECW()
3975 :
3976 : {
3977 14 : if (!GDAL_CHECK_VERSION("JP2ECW driver"))
3978 0 : return;
3979 :
3980 14 : if (GDALGetDriverByName(JP2ECW_DRIVER_NAME) != nullptr)
3981 0 : return;
3982 :
3983 14 : GDALDriver *poDriver = new GDALDriver();
3984 14 : JP2ECWDriverSetCommonMetadata(poDriver);
3985 14 : poDriver->pfnOpen = ECWDataset::OpenJPEG2000;
3986 : #ifdef HAVE_COMPRESS
3987 14 : poDriver->pfnCreate = ECWCreateJPEG2000;
3988 14 : poDriver->pfnCreateCopy = ECWCreateCopyJPEG2000;
3989 : #endif
3990 :
3991 14 : GetGDALDriverManager()->RegisterDriver(poDriver);
3992 : }
|