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