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