Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL ECW Driver
4 : * Purpose: ECW CreateCopy method implementation.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2001, 2004, Frank Warmerdam <warmerdam@pobox.com>
9 : * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : // ncsjpcbuffer.h needs the min and max macros.
15 : #undef NOMINMAX
16 :
17 : #include "gdal_ecw.h"
18 : #include "gdaljp2metadata.h"
19 : #include "ogr_spatialref.h"
20 :
21 : #if defined(HAVE_COMPRESS)
22 :
23 : #define OPTIMIZED_FOR_GDALWARP
24 :
25 : #if ECWSDK_VERSION >= 50
26 : static CPLString GetCompressionSoftwareName()
27 : {
28 : CPLString osRet;
29 : char szProcessName[2048];
30 :
31 : /* For privacy reason, allow the user to not write the software name in the
32 : * ECW */
33 : if (!CPLTestBool(
34 : CPLGetConfigOption("GDAL_ECW_WRITE_COMPRESSION_SOFTWARE", "YES")))
35 : return osRet;
36 :
37 : if (CPLGetExecPath(szProcessName, sizeof(szProcessName) - 1))
38 : {
39 : szProcessName[sizeof(szProcessName) - 1] = 0;
40 : #ifdef _WIN32
41 : char *szLastSlash = strrchr(szProcessName, '\\');
42 : #else
43 : char *szLastSlash = strrchr(szProcessName, '/');
44 : #endif
45 : if (szLastSlash != nullptr)
46 : memmove(szProcessName, szLastSlash + 1,
47 : strlen(szLastSlash + 1) + 1);
48 : }
49 : else
50 : strcpy(szProcessName, "Unknown");
51 :
52 : osRet.Printf("%s/GDAL v%d.%d.%d.%d/ECWJP2 SDK v%s", szProcessName,
53 : GDAL_VERSION_MAJOR, GDAL_VERSION_MINOR, GDAL_VERSION_REV,
54 : GDAL_VERSION_BUILD, NCS_ECWJP2_FULL_VERSION_STRING_DOT_DEL);
55 : return osRet;
56 : }
57 : #endif
58 :
59 : class GDALECWCompressor final : public CNCSFile
60 : {
61 :
62 : public:
63 : GDALECWCompressor();
64 : virtual ~GDALECWCompressor();
65 : virtual CNCSError WriteReadLine(UINT32 nNextLine,
66 : void **ppInputArray) override;
67 : #if ECWSDK_VERSION >= 50
68 : virtual void WriteStatus(IEEE4 fPercentComplete,
69 : const NCS::CString &sStatusText,
70 : const CompressionCounters &Counters) override;
71 : #else
72 : virtual void WriteStatus(UINT32 nCurrentLine) override;
73 : #endif
74 :
75 : virtual bool WriteCancel() override;
76 :
77 : CPLErr Initialize(const char *pszFilename, char **papszOptions, int nXSize,
78 : int nYSize, int nBands,
79 : const char *const *papszBandDescriptions,
80 : int bRGBColorSpace, GDALDataType eType,
81 : const OGRSpatialReference *poSRS,
82 : const GDALGeoTransform >, int nGCPCount,
83 : const GDAL_GCP *pasGCPList, int bIsJPEG2000,
84 : int bPixelIsPoint, char **papszRPCMD,
85 : GDALDataset *poSrcDS = nullptr);
86 : CPLErr CloseDown();
87 :
88 : CPLErr WriteJP2Box(GDALJP2Box *);
89 : void WriteXMLBoxes();
90 : CPLErr ourWriteLineBIL(UINT16 nBands, void **ppOutputLine,
91 : UINT32 *pLineSteps = nullptr);
92 : #if ECWSDK_VERSION >= 50
93 : virtual NCSEcwCellType WriteReadLineGetCellType() override
94 : {
95 : return sFileInfo.eCellType;
96 : }
97 : #endif
98 : #ifdef ECW_FW
99 : CNCSJP2File::CNCSJPXAssocBox m_oGMLAssoc;
100 : #endif
101 :
102 : // Data
103 :
104 : GDALDataset *m_poSrcDS;
105 :
106 : std::shared_ptr<VSIIOStream> m_OStream;
107 : int m_nPercentComplete;
108 :
109 : int m_bCanceled;
110 :
111 : GDALProgressFunc pfnProgress;
112 : void *pProgressData;
113 :
114 : GDALDataType eWorkDT;
115 : int m_nSwathLines;
116 : UINT32 m_nSwathOffset;
117 : GByte *m_pabySwathBuf;
118 : JP2UserBox **papoJP2UserBox;
119 : int nJP2UserBox;
120 : std::vector<int> m_anBandMap{};
121 :
122 : private:
123 : NCSFileViewFileInfoEx sFileInfo;
124 :
125 : /* To fix 'warning: ‘virtual NCS::CView& NCS::CView::operator=(const
126 : * NCS::CView&)’ was hidden ' with SDK 5 */
127 : #if ECWSDK_VERSION >= 50
128 : using CNCSFile::operator=;
129 : #endif
130 : CPL_DISALLOW_COPY_ASSIGN(GDALECWCompressor)
131 : };
132 :
133 : /************************************************************************/
134 : /* GDALECWCompressor() */
135 : /************************************************************************/
136 :
137 69 : GDALECWCompressor::GDALECWCompressor()
138 : : m_OStream(std::make_shared<VSIIOStream>()), eWorkDT(GDT_Unknown),
139 69 : m_nSwathLines(0), m_nSwathOffset(0), m_pabySwathBuf(nullptr)
140 : {
141 69 : m_poSrcDS = nullptr;
142 69 : m_nPercentComplete = -1;
143 69 : m_bCanceled = FALSE;
144 69 : pfnProgress = GDALDummyProgress;
145 69 : pProgressData = nullptr;
146 69 : papoJP2UserBox = nullptr;
147 69 : nJP2UserBox = 0;
148 : #if ECWSDK_VERSION >= 50
149 : NCSInitFileInfo(&sFileInfo);
150 : #else
151 69 : NCSInitFileInfoEx(&sFileInfo);
152 : #endif
153 69 : m_anBandMap.resize(sFileInfo.nBands);
154 69 : for (int iBand = 0; iBand < sFileInfo.nBands; iBand++)
155 0 : m_anBandMap[iBand] = iBand + 1;
156 69 : }
157 :
158 : /************************************************************************/
159 : /* ~GDALECWCompressor() */
160 : /************************************************************************/
161 :
162 69 : GDALECWCompressor::~GDALECWCompressor()
163 :
164 : {
165 : int i;
166 131 : for (i = 0; i < nJP2UserBox; i++)
167 62 : delete papoJP2UserBox[i];
168 69 : CPLFree(papoJP2UserBox);
169 :
170 : #if ECWSDK_VERSION >= 50
171 : NCSFreeFileInfo(&sFileInfo);
172 : #else
173 69 : NCSFreeFileInfoEx(&sFileInfo);
174 : #endif
175 69 : CPLFree(m_pabySwathBuf);
176 69 : }
177 :
178 : /************************************************************************/
179 : /* CloseDown() */
180 : /************************************************************************/
181 :
182 34 : CPLErr GDALECWCompressor::CloseDown()
183 :
184 : {
185 34 : Close(true);
186 34 : m_OStream->Close();
187 :
188 34 : return CE_None;
189 : }
190 :
191 : /************************************************************************/
192 : /* WriteReadLine() */
193 : /************************************************************************/
194 :
195 3979 : CNCSError GDALECWCompressor::WriteReadLine(UINT32 nNextLine,
196 : void **ppInputArray)
197 :
198 : {
199 : CPLErr eErr;
200 :
201 : #ifdef DEBUG_VERBOSE
202 : CPLDebug("ECW", "nNextLine = %d", nNextLine);
203 : #endif
204 :
205 3979 : if (m_poSrcDS == nullptr || m_poSrcDS->GetRasterBand(1) == nullptr)
206 : {
207 0 : return GetCNCSError(NCS_FILEIO_ERROR);
208 : }
209 3979 : if (m_nSwathLines <= 0)
210 : {
211 : int nBlockX;
212 32 : constexpr int MIN_SWATH_LINES = 256;
213 32 : m_poSrcDS->GetRasterBand(1)->GetBlockSize(&nBlockX, &m_nSwathLines);
214 32 : if (m_nSwathLines < MIN_SWATH_LINES)
215 30 : m_nSwathLines = MIN_SWATH_LINES;
216 : }
217 :
218 3979 : const GSpacing nPixelSpace = GDALGetDataTypeSizeBytes(eWorkDT);
219 3979 : const GSpacing nLineSpace = sFileInfo.nSizeX * nPixelSpace;
220 3979 : const GSpacing nBandSpace = nLineSpace * m_nSwathLines;
221 :
222 3979 : if (m_pabySwathBuf == nullptr)
223 : {
224 32 : size_t nBufSize = static_cast<size_t>(nBandSpace * sFileInfo.nBands);
225 32 : m_pabySwathBuf = (GByte *)VSI_MALLOC_VERBOSE(nBufSize);
226 : }
227 3979 : if (m_pabySwathBuf == nullptr)
228 : {
229 0 : return GetCNCSError(NCS_FILE_NO_MEMORY);
230 : }
231 :
232 3979 : if (nNextLine == 0 || nNextLine >= m_nSwathOffset + m_nSwathLines)
233 : {
234 41 : int nSwathLines = m_nSwathLines;
235 41 : if (nNextLine + nSwathLines > sFileInfo.nSizeY)
236 : {
237 29 : nSwathLines = sFileInfo.nSizeY - nNextLine;
238 : }
239 123 : eErr = m_poSrcDS->RasterIO(
240 41 : GF_Read, 0, nNextLine, sFileInfo.nSizeX, nSwathLines,
241 41 : m_pabySwathBuf, sFileInfo.nSizeX, nSwathLines, eWorkDT,
242 41 : sFileInfo.nBands, &m_anBandMap[0], nPixelSpace, nLineSpace,
243 : nBandSpace, nullptr);
244 41 : m_nSwathOffset = nNextLine;
245 41 : UINT32 nNextSwathLine = nNextLine + nSwathLines;
246 41 : if (nNextSwathLine < sFileInfo.nSizeY)
247 : {
248 9 : if (nNextSwathLine + nSwathLines > sFileInfo.nSizeY)
249 : {
250 3 : nSwathLines = sFileInfo.nSizeY - nNextSwathLine;
251 : }
252 18 : m_poSrcDS->AdviseRead(0, nNextSwathLine, sFileInfo.nSizeX,
253 9 : nSwathLines, sFileInfo.nSizeX, nSwathLines,
254 9 : eWorkDT, sFileInfo.nBands, &m_anBandMap[0],
255 9 : nullptr);
256 41 : }
257 : }
258 : else
259 : {
260 3938 : eErr = CE_None;
261 : }
262 :
263 9928 : for (int iBand = 0; iBand < (int)sFileInfo.nBands; iBand++)
264 : {
265 5949 : memcpy(ppInputArray[iBand],
266 5949 : m_pabySwathBuf + nLineSpace * (nNextLine - m_nSwathOffset) +
267 5949 : nBandSpace * iBand,
268 5949 : static_cast<size_t>(nPixelSpace * sFileInfo.nSizeX));
269 : }
270 :
271 3979 : if (eErr == CE_None)
272 3979 : return GetCNCSError(NCS_SUCCESS);
273 : else
274 0 : return GetCNCSError(NCS_FILEIO_ERROR);
275 : }
276 :
277 : /************************************************************************/
278 : /* WriteStatus() */
279 : /************************************************************************/
280 : #if ECWSDK_VERSION >= 50
281 : void GDALECWCompressor::WriteStatus(IEEE4 fPercentComplete,
282 : const NCS::CString &sStatusText,
283 : const CompressionCounters &Counters)
284 : {
285 : std::string sStatusUTF8;
286 : sStatusText.utf8_str(sStatusUTF8);
287 :
288 : m_bCanceled = !pfnProgress(fPercentComplete / 100.0, sStatusUTF8.c_str(),
289 : pProgressData);
290 : }
291 : #else
292 :
293 3979 : void GDALECWCompressor::WriteStatus(UINT32 nCurrentLine)
294 :
295 : {
296 3979 : m_bCanceled = !pfnProgress(nCurrentLine / (float)sFileInfo.nSizeY, nullptr,
297 : pProgressData);
298 3979 : }
299 : #endif
300 : /************************************************************************/
301 : /* WriteCancel() */
302 : /************************************************************************/
303 :
304 3979 : bool GDALECWCompressor::WriteCancel()
305 :
306 : {
307 3979 : return (bool)m_bCanceled;
308 : }
309 :
310 : /************************************************************************/
311 : /* WriteJP2Box() */
312 : /************************************************************************/
313 :
314 70 : CPLErr GDALECWCompressor::WriteJP2Box(GDALJP2Box *poBox)
315 :
316 : {
317 : JP2UserBox *poECWBox;
318 :
319 70 : if (poBox == nullptr)
320 8 : return CE_None;
321 :
322 62 : poECWBox = new JP2UserBox();
323 62 : memcpy(&(poECWBox->m_nTBox), poBox->GetType(), 4);
324 62 : CPL_MSBPTR32(&(poECWBox->m_nTBox));
325 :
326 62 : poECWBox->SetData((int)poBox->GetDataLength(), poBox->GetWritableData());
327 :
328 62 : AddBox(poECWBox);
329 :
330 62 : delete poBox;
331 :
332 124 : papoJP2UserBox = (JP2UserBox **)CPLRealloc(
333 62 : papoJP2UserBox, (nJP2UserBox + 1) * sizeof(JP2UserBox *));
334 62 : papoJP2UserBox[nJP2UserBox] = poECWBox;
335 62 : nJP2UserBox++;
336 :
337 62 : return CE_None;
338 : }
339 :
340 : /************************************************************************/
341 : /* WriteXMLBoxes() */
342 : /************************************************************************/
343 :
344 6 : void GDALECWCompressor::WriteXMLBoxes()
345 : {
346 6 : int nBoxes = 0;
347 : GDALJP2Box **papoBoxes =
348 6 : GDALJP2Metadata::CreateXMLBoxes(m_poSrcDS, &nBoxes);
349 7 : for (int i = 0; i < nBoxes; i++)
350 : {
351 1 : WriteJP2Box(papoBoxes[i]);
352 : }
353 6 : CPLFree(papoBoxes);
354 6 : }
355 :
356 : /************************************************************************/
357 : /* ourWriteLineBIL() */
358 : /************************************************************************/
359 :
360 200 : CPLErr GDALECWCompressor::ourWriteLineBIL(UINT16 nBands, void **ppOutputLine,
361 : UINT32 *pLineSteps)
362 : {
363 :
364 : CNCSError oError = CNCSFile::WriteLineBIL(sFileInfo.eCellType, nBands,
365 400 : ppOutputLine, pLineSteps);
366 :
367 200 : if (oError.GetErrorNumber() != NCS_SUCCESS)
368 : {
369 0 : ECWReportError(oError, "Scanline write write failed.\n");
370 0 : return CE_Failure;
371 : }
372 200 : return CE_None;
373 : }
374 :
375 : /************************************************************************/
376 : /* Initialize() */
377 : /* */
378 : /* Initialize compressor output. */
379 : /************************************************************************/
380 :
381 38 : CPLErr GDALECWCompressor::Initialize(
382 : const char *pszFilename, char **papszOptions, int nXSize, int nYSize,
383 : int nBands, const char *const *papszBandDescriptions, int bRGBColorSpace,
384 : GDALDataType eType, const OGRSpatialReference *poSRS,
385 : const GDALGeoTransform >, int nGCPCount, const GDAL_GCP *pasGCPList,
386 : int bIsJPEG2000, int bPixelIsPoint, char **papszRPCMD, GDALDataset *poSrcDS)
387 :
388 : {
389 : /* -------------------------------------------------------------------- */
390 : /* For 4.x and beyond you need a license key to compress data. */
391 : /* Check for it as a configuration option or a creation option. */
392 : /* -------------------------------------------------------------------- */
393 : #if ECWSDK_VERSION >= 40
394 : const char *pszECWKey = CSLFetchNameValue(papszOptions, "ECW_ENCODE_KEY");
395 : if (pszECWKey == nullptr)
396 : pszECWKey = CPLGetConfigOption("ECW_ENCODE_KEY", nullptr);
397 :
398 : const char *pszECWCompany =
399 : CSLFetchNameValue(papszOptions, "ECW_ENCODE_COMPANY");
400 : if (pszECWCompany == nullptr)
401 : pszECWCompany = CPLGetConfigOption("ECW_ENCODE_COMPANY", nullptr);
402 :
403 : if (pszECWKey && pszECWCompany)
404 : {
405 : CPLDebug("ECW", "SetOEMKey(%s,%s)", pszECWCompany, pszECWKey);
406 : CNCSFile::SetOEMKey((char *)pszECWCompany, (char *)pszECWKey);
407 : }
408 : else if (pszECWKey || pszECWCompany)
409 : {
410 : CPLError(CE_Failure, CPLE_AppDefined,
411 : "Only one of ECW_ENCODE_KEY and ECW_ENCODE_COMPANY were "
412 : "provided.\nBoth are required.");
413 : return CE_Failure;
414 : }
415 : else
416 : {
417 : CPLError(CE_Failure, CPLE_AppDefined,
418 : "None of ECW_ENCODE_KEY and ECW_ENCODE_COMPANY were "
419 : "provided.\nBoth are required.");
420 : return CE_Failure;
421 : }
422 :
423 : #endif /* ECWSDK_VERSION >= 40 */
424 :
425 : /* -------------------------------------------------------------------- */
426 : /* Do some rudimentary checking in input. */
427 : /* -------------------------------------------------------------------- */
428 38 : if (nBands == 0)
429 : {
430 0 : CPLError(CE_Failure, CPLE_NotSupported,
431 : "ECW driver requires at least one band.");
432 0 : return CE_Failure;
433 : }
434 :
435 : /* -------------------------------------------------------------------- */
436 : /* Parse out some known options. */
437 : /* -------------------------------------------------------------------- */
438 : float fTargetCompression;
439 :
440 : // Default compression based on image type per request from Paul Beaty.
441 38 : if (nBands > 1)
442 11 : fTargetCompression = 95.0;
443 : else
444 27 : fTargetCompression = 90.0;
445 :
446 38 : if (CSLFetchNameValue(papszOptions, "TARGET") != nullptr)
447 : {
448 10 : fTargetCompression =
449 10 : (float)CPLAtof(CSLFetchNameValue(papszOptions, "TARGET"));
450 :
451 : /* The max allowed value should be 100 - 100 / 65535 = 99.9984740978 */
452 : /* so that nCompressionRate fits on a uint16 (see below) */
453 : /* No need to be so pedantic, so we will limit to 99.99 % */
454 : /* (compression rate = 10 000) */
455 10 : if (fTargetCompression < 0.0 || fTargetCompression > 99.99)
456 : {
457 0 : CPLError(CE_Failure, CPLE_NotSupported,
458 : "TARGET compression of %.3f invalid, should be a\n"
459 : "value between 0 and 99.99 percent.\n",
460 : (double)fTargetCompression);
461 0 : return CE_Failure;
462 : }
463 : }
464 :
465 : /* -------------------------------------------------------------------- */
466 : /* Create and initialize compressor. */
467 : /* -------------------------------------------------------------------- */
468 38 : NCSFileViewFileInfoEx *psClient = &(sFileInfo);
469 38 : const char *pszOption = nullptr;
470 : #if ECWSDK_VERSION >= 50
471 : if (bIsJPEG2000 == FALSE)
472 : {
473 : bool bECWV3 = false;
474 : pszOption = CSLFetchNameValue(papszOptions, "ECW_FORMAT_VERSION");
475 : if (pszOption != nullptr)
476 : {
477 : bECWV3 = (3 == atoi(pszOption));
478 : }
479 : psClient->nFormatVersion = (bECWV3) ? 3 : 2;
480 : }
481 : else
482 : {
483 : psClient->nFormatVersion = 1;
484 : }
485 : #endif
486 38 : psClient->nBands = (UINT16)nBands;
487 38 : psClient->nSizeX = nXSize;
488 38 : psClient->nSizeY = nYSize;
489 38 : psClient->nCompressionRate =
490 38 : (UINT16)MAX(1, 100 / (100 - fTargetCompression));
491 38 : psClient->eCellSizeUnits = ECW_CELL_UNITS_METERS;
492 :
493 38 : if (nBands == 1)
494 27 : psClient->eColorSpace = NCSCS_GREYSCALE;
495 11 : else if (nBands == 3 && bRGBColorSpace)
496 5 : psClient->eColorSpace = NCSCS_sRGB;
497 : #if ECWSDK_VERSION >= 40
498 : else if (nBands == 4 && bRGBColorSpace)
499 : psClient->eColorSpace = NCSCS_sRGB;
500 : #endif
501 : else
502 6 : psClient->eColorSpace = NCSCS_MULTIBAND;
503 :
504 : /* -------------------------------------------------------------------- */
505 : /* Figure out the data type. */
506 : /* -------------------------------------------------------------------- */
507 38 : int bSigned = FALSE;
508 38 : int nBits = 8;
509 38 : eWorkDT = eType;
510 :
511 38 : switch (eWorkDT)
512 : {
513 30 : case GDT_Byte:
514 : #if ECWSDK_VERSION >= 50
515 : psClient->nCellBitDepth = 8;
516 : #endif
517 30 : psClient->eCellType = NCSCT_UINT8;
518 30 : nBits = 8;
519 30 : bSigned = FALSE;
520 30 : break;
521 :
522 2 : case GDT_UInt16:
523 : #if ECWSDK_VERSION >= 50
524 : psClient->nCellBitDepth = 16;
525 : #endif
526 2 : psClient->eCellType = NCSCT_UINT16;
527 2 : nBits = 16;
528 2 : bSigned = FALSE;
529 2 : break;
530 :
531 1 : case GDT_UInt32:
532 : #if ECWSDK_VERSION >= 50
533 : psClient->nCellBitDepth = 32;
534 : #endif
535 1 : psClient->eCellType = NCSCT_UINT32;
536 1 : nBits = 32;
537 1 : bSigned = FALSE;
538 1 : break;
539 :
540 3 : case GDT_Int16:
541 : #if ECWSDK_VERSION >= 50
542 : psClient->nCellBitDepth = 16;
543 : #endif
544 3 : psClient->eCellType = NCSCT_INT16;
545 3 : nBits = 16;
546 3 : bSigned = TRUE;
547 3 : break;
548 :
549 1 : case GDT_Int32:
550 : #if ECWSDK_VERSION >= 50
551 : psClient->nCellBitDepth = 32;
552 : #endif
553 1 : psClient->eCellType = NCSCT_INT32;
554 1 : nBits = 32;
555 1 : bSigned = TRUE;
556 1 : break;
557 :
558 1 : case GDT_Float32:
559 1 : psClient->eCellType = NCSCT_IEEE4;
560 1 : nBits = 32;
561 1 : bSigned = TRUE;
562 1 : break;
563 :
564 : #if ECWSDK_VERSION >= 40
565 : case GDT_Float64:
566 : psClient->eCellType = NCSCT_IEEE8;
567 : nBits = 64;
568 : bSigned = TRUE;
569 : break;
570 : #endif
571 :
572 0 : default:
573 : // We treat complex types as float.
574 0 : psClient->eCellType = NCSCT_IEEE4;
575 0 : nBits = 32;
576 0 : bSigned = TRUE;
577 0 : eWorkDT = GDT_Float32;
578 0 : break;
579 : }
580 :
581 : /* -------------------------------------------------------------------- */
582 : /* Create band information structures. */
583 : /* -------------------------------------------------------------------- */
584 : int iBand;
585 :
586 38 : psClient->pBands =
587 38 : (NCSFileBandInfo *)NCSMalloc(sizeof(NCSFileBandInfo) * nBands, true);
588 98 : for (iBand = 0; iBand < nBands; iBand++)
589 : {
590 60 : const char *pszNBITS = CSLFetchNameValue(papszOptions, "NBITS");
591 60 : if (pszNBITS && atoi(pszNBITS) > 0)
592 2 : psClient->pBands[iBand].nBits = (UINT8)atoi(pszNBITS);
593 : else
594 58 : psClient->pBands[iBand].nBits = (UINT8)nBits;
595 60 : psClient->pBands[iBand].bSigned = (BOOLEAN)bSigned;
596 : #if ECWSDK_VERSION >= 50
597 : psClient->pBands[iBand].szDesc =
598 : NCSStrDup(papszBandDescriptions[iBand]);
599 : #else
600 120 : psClient->pBands[iBand].szDesc =
601 60 : NCSStrDup((char *)papszBandDescriptions[iBand]);
602 : #endif
603 : }
604 :
605 : /* -------------------------------------------------------------------- */
606 : /* Allow CNCSFile::SetParameter() requests. */
607 : /* -------------------------------------------------------------------- */
608 :
609 38 : if (bIsJPEG2000)
610 : {
611 33 : pszOption = CSLFetchNameValue(papszOptions, "PROFILE");
612 33 : if (pszOption != nullptr && EQUAL(pszOption, "BASELINE_0"))
613 0 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_PROFILE_BASELINE_0);
614 33 : else if (pszOption != nullptr && EQUAL(pszOption, "BASELINE_1"))
615 0 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_PROFILE_BASELINE_1);
616 33 : else if (pszOption != nullptr && EQUAL(pszOption, "BASELINE_2"))
617 0 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_PROFILE_BASELINE_2);
618 33 : else if (pszOption != nullptr && EQUAL(pszOption, "NPJE"))
619 7 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_PROFILE_NITF_BIIF_NPJE);
620 26 : else if (pszOption != nullptr && EQUAL(pszOption, "EPJE"))
621 0 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_PROFILE_NITF_BIIF_EPJE);
622 :
623 33 : pszOption = CSLFetchNameValue(papszOptions, "CODESTREAM_ONLY");
624 62 : if (pszOption == nullptr &&
625 62 : EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "j2k"))
626 2 : pszOption = "YES";
627 33 : if (pszOption != nullptr)
628 12 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_CODESTREAM_ONLY,
629 6 : CPLTestBool(pszOption));
630 :
631 33 : pszOption = CSLFetchNameValue(papszOptions, "LEVELS");
632 33 : if (pszOption != nullptr)
633 0 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_LEVELS,
634 0 : (UINT32)atoi(pszOption));
635 :
636 33 : pszOption = CSLFetchNameValue(papszOptions, "LAYERS");
637 33 : if (pszOption != nullptr)
638 3 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_LAYERS,
639 3 : (UINT32)atoi(pszOption));
640 :
641 33 : pszOption = CSLFetchNameValue(papszOptions, "PRECINCT_WIDTH");
642 33 : if (pszOption != nullptr)
643 0 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_PRECINCT_WIDTH,
644 0 : (UINT32)atoi(pszOption));
645 :
646 33 : pszOption = CSLFetchNameValue(papszOptions, "PRECINCT_HEIGHT");
647 33 : if (pszOption != nullptr)
648 0 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_PRECINCT_HEIGHT,
649 0 : (UINT32)atoi(pszOption));
650 :
651 33 : pszOption = CSLFetchNameValue(papszOptions, "TILE_WIDTH");
652 33 : if (pszOption != nullptr)
653 0 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_TILE_WIDTH,
654 0 : (UINT32)atoi(pszOption));
655 :
656 33 : pszOption = CSLFetchNameValue(papszOptions, "TILE_HEIGHT");
657 33 : if (pszOption != nullptr)
658 0 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_TILE_HEIGHT,
659 0 : (UINT32)atoi(pszOption));
660 :
661 33 : pszOption = CSLFetchNameValue(papszOptions, "INCLUDE_SOP");
662 33 : if (pszOption != nullptr)
663 0 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_INCLUDE_SOP,
664 0 : CPLTestBool(pszOption));
665 :
666 33 : pszOption = CSLFetchNameValue(papszOptions, "INCLUDE_EPH");
667 33 : if (pszOption != nullptr)
668 0 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_INCLUDE_EPH,
669 0 : CPLTestBool(pszOption));
670 :
671 33 : pszOption = CSLFetchNameValue(papszOptions, "PROGRESSION");
672 33 : if (pszOption != nullptr && EQUAL(pszOption, "LRCP"))
673 0 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_PROGRESSION_LRCP);
674 :
675 33 : else if (pszOption != nullptr && EQUAL(pszOption, "RLCP"))
676 0 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_PROGRESSION_RLCP);
677 :
678 33 : else if (pszOption != nullptr && EQUAL(pszOption, "RPCL"))
679 0 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_PROGRESSION_RPCL);
680 :
681 33 : pszOption = CSLFetchNameValue(papszOptions, "GEODATA_USAGE");
682 33 : if (pszOption == nullptr)
683 : // Default to suppressing ECW SDK geodata, just use our own stuff.
684 33 : SetGeodataUsage(JP2_GEODATA_USE_NONE);
685 0 : else if (EQUAL(pszOption, "NONE"))
686 0 : SetGeodataUsage(JP2_GEODATA_USE_NONE);
687 0 : else if (EQUAL(pszOption, "PCS_ONLY"))
688 0 : SetGeodataUsage(JP2_GEODATA_USE_PCS_ONLY);
689 0 : else if (EQUAL(pszOption, "GML_ONLY"))
690 0 : SetGeodataUsage(JP2_GEODATA_USE_GML_ONLY);
691 0 : else if (EQUAL(pszOption, "PCS_GML"))
692 0 : SetGeodataUsage(JP2_GEODATA_USE_PCS_GML);
693 0 : else if (EQUAL(pszOption, "GML_PCS"))
694 0 : SetGeodataUsage(JP2_GEODATA_USE_GML_PCS);
695 0 : else if (EQUAL(pszOption, "ALL"))
696 0 : SetGeodataUsage(JP2_GEODATA_USE_GML_PCS_WLD);
697 :
698 33 : pszOption = CSLFetchNameValue(papszOptions, "DECOMPRESS_LAYERS");
699 33 : if (pszOption != nullptr)
700 0 : SetParameter(CNCSJP2FileView::JP2_DECOMPRESS_LAYERS,
701 0 : (UINT32)atoi(pszOption));
702 :
703 33 : pszOption = CSLFetchNameValue(papszOptions,
704 : "DECOMPRESS_RECONSTRUCTION_PARAMETER");
705 33 : if (pszOption != nullptr)
706 0 : SetParameter(
707 : CNCSJP2FileView::JPC_DECOMPRESS_RECONSTRUCTION_PARAMETER,
708 0 : (IEEE4)CPLAtof(pszOption));
709 : }
710 :
711 : /* -------------------------------------------------------------------- */
712 : /* Georeferencing. */
713 : /* -------------------------------------------------------------------- */
714 :
715 38 : psClient->fOriginX = 0.0;
716 38 : psClient->fOriginY = psClient->nSizeY;
717 38 : psClient->fCellIncrementX = 1.0;
718 38 : psClient->fCellIncrementY = -1.0;
719 38 : psClient->fCWRotationDegrees = 0.0;
720 :
721 38 : if (gt[2] != 0.0 || gt[4] != 0.0)
722 0 : CPLError(CE_Warning, CPLE_NotSupported,
723 : "Rotational coefficients ignored, georeferencing of\n"
724 : "output ECW file will be incorrect.\n");
725 : else
726 : {
727 38 : psClient->fOriginX = gt[0];
728 38 : psClient->fOriginY = gt[3];
729 38 : psClient->fCellIncrementX = gt[1];
730 38 : psClient->fCellIncrementY = gt[5];
731 : }
732 :
733 : /* -------------------------------------------------------------------- */
734 : /* Projection. */
735 : /* -------------------------------------------------------------------- */
736 : char szProjection[128];
737 : char szDatum[128];
738 : char szUnits[128];
739 :
740 38 : strcpy(szProjection, "RAW");
741 38 : strcpy(szDatum, "RAW");
742 :
743 38 : if (CSLFetchNameValue(papszOptions, "PROJ") != nullptr)
744 : {
745 0 : strncpy(szProjection, CSLFetchNameValue(papszOptions, "PROJ"),
746 : sizeof(szProjection));
747 0 : szProjection[sizeof(szProjection) - 1] = 0;
748 : }
749 :
750 38 : if (CSLFetchNameValue(papszOptions, "DATUM") != nullptr)
751 : {
752 0 : strncpy(szDatum, CSLFetchNameValue(papszOptions, "DATUM"),
753 : sizeof(szDatum));
754 0 : szDatum[sizeof(szDatum) - 1] = 0;
755 0 : if (EQUAL(szProjection, "RAW"))
756 0 : strcpy(szProjection, "GEODETIC");
757 : }
758 :
759 38 : const char *pszUnits = CSLFetchNameValue(papszOptions, "UNITS");
760 38 : if (pszUnits != nullptr)
761 : {
762 0 : psClient->eCellSizeUnits = ECWTranslateToCellSizeUnits(pszUnits);
763 : }
764 :
765 38 : if (EQUAL(szProjection, "RAW") && poSRS != nullptr && !poSRS->IsEmpty())
766 : {
767 25 : ECWTranslateFromWKT(poSRS, szProjection, sizeof(szProjection), szDatum,
768 : sizeof(szDatum), szUnits);
769 25 : psClient->eCellSizeUnits = ECWTranslateToCellSizeUnits(szUnits);
770 : }
771 :
772 38 : NCSFree(psClient->szDatum);
773 38 : psClient->szDatum = NCSStrDup(szDatum);
774 38 : NCSFree(psClient->szProjection);
775 38 : psClient->szProjection = NCSStrDup(szProjection);
776 :
777 38 : CPLDebug("ECW", "Writing with PROJ=%s, DATUM=%s, UNITS=%s", szProjection,
778 : szDatum, ECWTranslateFromCellSizeUnits(psClient->eCellSizeUnits));
779 :
780 : /* -------------------------------------------------------------------- */
781 : /* Setup GML and GeoTIFF information. */
782 : /* -------------------------------------------------------------------- */
783 38 : if ((poSRS != nullptr && !poSRS->IsEmpty()) || gt != GDALGeoTransform() ||
784 76 : nGCPCount > 0 || papszRPCMD != nullptr)
785 : {
786 74 : GDALJP2Metadata oJP2MD;
787 :
788 37 : oJP2MD.SetSpatialRef(poSRS);
789 37 : oJP2MD.SetGeoTransform(gt);
790 37 : oJP2MD.SetGCPs(nGCPCount, pasGCPList);
791 37 : oJP2MD.bPixelIsPoint = bPixelIsPoint;
792 37 : oJP2MD.SetRPCMD(papszRPCMD);
793 :
794 37 : if (bIsJPEG2000)
795 : {
796 32 : if (CPLFetchBool(papszOptions, "WRITE_METADATA", false))
797 : {
798 6 : if (!CPLFetchBool(papszOptions, "MAIN_MD_DOMAIN_ONLY", false))
799 : {
800 6 : WriteXMLBoxes();
801 : }
802 6 : WriteJP2Box(
803 : GDALJP2Metadata::CreateGDALMultiDomainMetadataXMLBox(
804 6 : m_poSrcDS, CPLFetchBool(papszOptions,
805 : "MAIN_MD_DOMAIN_ONLY", false)));
806 : }
807 32 : if (CPLFetchBool(papszOptions, "GMLJP2", true))
808 : {
809 : const char *pszGMLJP2V2Def =
810 29 : CSLFetchNameValue(papszOptions, "GMLJP2V2_DEF");
811 29 : if (pszGMLJP2V2Def != nullptr)
812 : {
813 0 : WriteJP2Box(oJP2MD.CreateGMLJP2V2(nXSize, nYSize,
814 : pszGMLJP2V2Def, poSrcDS));
815 : }
816 : else
817 : {
818 48 : if (!poSRS || poSRS->IsEmpty() ||
819 19 : GDALJP2Metadata::IsSRSCompatible(poSRS))
820 : {
821 28 : WriteJP2Box(oJP2MD.CreateGMLJP2(nXSize, nYSize));
822 : }
823 1 : else if (CSLFetchNameValue(papszOptions, "GMLJP2"))
824 : {
825 0 : CPLError(CE_Warning, CPLE_AppDefined,
826 : "GMLJP2 box was explicitly required but "
827 : "cannot be written due "
828 : "to lack of georeferencing and/or unsupported "
829 : "georeferencing "
830 : "for GMLJP2");
831 : }
832 : else
833 : {
834 1 : CPLDebug(
835 : "JP2ECW",
836 : "Cannot write GMLJP2 box due to unsupported SRS");
837 : }
838 : }
839 : }
840 32 : if (CPLFetchBool(papszOptions, "GeoJP2", true))
841 29 : WriteJP2Box(oJP2MD.CreateJP2GeoTIFF());
842 38 : if (CPLFetchBool(papszOptions, "WRITE_METADATA", false) &&
843 6 : !CPLFetchBool(papszOptions, "MAIN_MD_DOMAIN_ONLY", false))
844 : {
845 6 : WriteJP2Box(GDALJP2Metadata::CreateXMPBox(m_poSrcDS));
846 : }
847 : }
848 : }
849 : /* -------------------------------------------------------------------- */
850 : /* We handle all jpeg2000 files via the VSIIOStream, but ECW */
851 : /* files cannot be done this way for some reason. */
852 : /* -------------------------------------------------------------------- */
853 38 : VSILFILE *fpVSIL = nullptr;
854 :
855 38 : if (bIsJPEG2000)
856 : {
857 66 : int bSeekable = !(STARTS_WITH(pszFilename, "/vsistdout/") ||
858 33 : STARTS_WITH(pszFilename, "/vsizip/") ||
859 33 : STARTS_WITH(pszFilename, "/vsigzip/"));
860 33 : fpVSIL = VSIFOpenL(pszFilename, (bSeekable) ? "wb+" : "wb");
861 33 : if (fpVSIL == nullptr)
862 : {
863 2 : CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open/create %s.",
864 : pszFilename);
865 2 : return CE_Failure;
866 : }
867 :
868 31 : m_OStream->Access(fpVSIL, TRUE, (BOOLEAN)bSeekable, pszFilename, 0, -1);
869 : }
870 : else
871 : {
872 5 : if (!STARTS_WITH(pszFilename, "/vsi"))
873 : {
874 : // Try now to create the file to avoid memory leaks if it is
875 : // the SDK that fails to do it.
876 5 : fpVSIL = VSIFOpenL(pszFilename, "wb");
877 5 : if (fpVSIL == nullptr)
878 : {
879 2 : CPLError(CE_Failure, CPLE_OpenFailed,
880 : "Failed to open/create %s.", pszFilename);
881 2 : return CE_Failure;
882 : }
883 3 : VSIFCloseL(fpVSIL);
884 3 : VSIUnlink(pszFilename);
885 3 : fpVSIL = nullptr;
886 : }
887 : }
888 :
889 : /* -------------------------------------------------------------------- */
890 : /* Check if we can enable large files. This option should only */
891 : /* be set when the application is adhering to one of the */
892 : /* ERMapper options for licensing larger than 500MB input */
893 : /* files. See Bug 767. This option no longer exists with */
894 : /* version 4+. */
895 : /* -------------------------------------------------------------------- */
896 : #if ECWSDK_VERSION < 40
897 34 : const char *pszLargeOK = CSLFetchNameValue(papszOptions, "LARGE_OK");
898 34 : if (pszLargeOK == nullptr)
899 34 : pszLargeOK = "NO";
900 :
901 34 : pszLargeOK = CPLGetConfigOption("ECW_LARGE_OK", pszLargeOK);
902 :
903 34 : if (CPLTestBool(pszLargeOK))
904 : {
905 0 : CNCSFile::SetKeySize();
906 0 : CPLDebug("ECW", "Large file generation enabled.");
907 : }
908 : #endif /* ECWSDK_VERSION < 40 */
909 : /* -------------------------------------------------------------------- */
910 : /* Infer metadata information from source dataset if possible */
911 : /* -------------------------------------------------------------------- */
912 : #if ECWSDK_VERSION >= 50
913 : if (psClient->nFormatVersion > 2)
914 : {
915 : if (psClient->pFileMetaData == nullptr)
916 : {
917 : NCSEcwInitMetaData(&psClient->pFileMetaData);
918 : }
919 : if (m_poSrcDS && m_poSrcDS->GetMetadataItem(
920 : "FILE_METADATA_ACQUISITION_DATE") != nullptr)
921 : {
922 : psClient->pFileMetaData->sAcquisitionDate =
923 : NCSStrDupT(NCS::CString(m_poSrcDS->GetMetadataItem(
924 : "FILE_METADATA_ACQUISITION_DATE"))
925 : .c_str());
926 : }
927 :
928 : if (m_poSrcDS &&
929 : m_poSrcDS->GetMetadataItem(
930 : "FILE_METADATA_ACQUISITION_SENSOR_NAME") != nullptr)
931 : {
932 : psClient->pFileMetaData->sAcquisitionSensorName = NCSStrDupT(
933 : NCS::CString(m_poSrcDS->GetMetadataItem(
934 : "FILE_METADATA_ACQUISITION_SENSOR_NAME"))
935 : .c_str());
936 : }
937 : if (m_poSrcDS &&
938 : m_poSrcDS->GetMetadataItem("FILE_METADATA_ADDRESS") != nullptr)
939 : {
940 : psClient->pFileMetaData->sAddress =
941 : NCSStrDupT(NCS::CString(m_poSrcDS->GetMetadataItem(
942 : "FILE_METADATA_ADDRESS"))
943 : .c_str());
944 : }
945 : if (m_poSrcDS &&
946 : m_poSrcDS->GetMetadataItem("FILE_METADATA_AUTHOR") != nullptr)
947 : {
948 : psClient->pFileMetaData->sAuthor = NCSStrDupT(
949 : NCS::CString(m_poSrcDS->GetMetadataItem("FILE_METADATA_AUTHOR"))
950 : .c_str());
951 : }
952 : if (m_poSrcDS && m_poSrcDS->GetMetadataItem(
953 : "FILE_METADATA_CLASSIFICATION") != nullptr)
954 : {
955 : psClient->pFileMetaData->sClassification =
956 : NCSStrDupT(NCS::CString(m_poSrcDS->GetMetadataItem(
957 : "FILE_METADATA_CLASSIFICATION"))
958 : .c_str());
959 : }
960 : if (pszECWCompany != nullptr &&
961 : CPLTestBool(CPLGetConfigOption("GDAL_ECW_WRITE_COMPANY", "YES")))
962 : {
963 : psClient->pFileMetaData->sCompany =
964 : NCSStrDupT(NCS::CString(pszECWCompany).c_str());
965 : }
966 : CPLString osCompressionSoftware = GetCompressionSoftwareName();
967 : if (!osCompressionSoftware.empty())
968 : {
969 : psClient->pFileMetaData->sCompressionSoftware =
970 : NCSStrDupT(NCS::CString(osCompressionSoftware.c_str()).c_str());
971 : }
972 : if (m_poSrcDS &&
973 : m_poSrcDS->GetMetadataItem("FILE_METADATA_COPYRIGHT") != nullptr)
974 : {
975 : psClient->pFileMetaData->sCopyright =
976 : NCSStrDupT(NCS::CString(m_poSrcDS->GetMetadataItem(
977 : "FILE_METADATA_COPYRIGHT"))
978 : .c_str());
979 : }
980 : if (m_poSrcDS &&
981 : m_poSrcDS->GetMetadataItem("FILE_METADATA_EMAIL") != nullptr)
982 : {
983 : psClient->pFileMetaData->sEmail = NCSStrDupT(
984 : NCS::CString(m_poSrcDS->GetMetadataItem("FILE_METADATA_EMAIL"))
985 : .c_str());
986 : }
987 : if (m_poSrcDS &&
988 : m_poSrcDS->GetMetadataItem("FILE_METADATA_TELEPHONE") != nullptr)
989 : {
990 : psClient->pFileMetaData->sTelephone =
991 : NCSStrDupT(NCS::CString(m_poSrcDS->GetMetadataItem(
992 : "FILE_METADATA_TELEPHONE"))
993 : .c_str());
994 : }
995 : }
996 : #endif
997 : /* -------------------------------------------------------------------- */
998 : /* Set the file info. */
999 : /* -------------------------------------------------------------------- */
1000 68 : CNCSError oError = SetFileInfo(sFileInfo);
1001 :
1002 34 : if (oError.GetErrorNumber() == NCS_SUCCESS)
1003 : {
1004 34 : if (fpVSIL == nullptr)
1005 : {
1006 : #if ECWSDK_VERSION >= 40 && defined(_WIN32)
1007 : if (CPLTestBool(CPLGetConfigOption("GDAL_FILENAME_IS_UTF8", "YES")))
1008 : {
1009 : wchar_t *pwszFilename =
1010 : CPLRecodeToWChar(pszFilename, CPL_ENC_UTF8, CPL_ENC_UCS2);
1011 : oError = GetCNCSError(Open(pwszFilename, false, true));
1012 : CPLFree(pwszFilename);
1013 : }
1014 : else
1015 : #endif
1016 : {
1017 3 : oError = GetCNCSError(Open((char *)pszFilename, false, true));
1018 : }
1019 : }
1020 : else
1021 : {
1022 : #if ECWSDK_VERSION >= 55
1023 : oError = CNCSJP2FileView::Open(m_OStream);
1024 : #else
1025 31 : oError = CNCSJP2FileView::Open(m_OStream.get());
1026 : #endif
1027 : }
1028 : }
1029 :
1030 34 : if (oError.GetErrorNumber() == NCS_SUCCESS)
1031 34 : return CE_None;
1032 0 : else if (oError.GetErrorNumber() == NCS_INPUT_SIZE_EXCEEDED)
1033 : {
1034 0 : CPLError(CE_Failure, CPLE_AppDefined,
1035 : "ECW SDK compress limit exceeded.");
1036 0 : return CE_Failure;
1037 : }
1038 : else
1039 : {
1040 0 : ECWReportError(oError);
1041 :
1042 0 : return CE_Failure;
1043 : }
1044 : }
1045 :
1046 : /************************************************************************/
1047 : /* ECWIsInputRGBColorSpace() */
1048 : /************************************************************************/
1049 :
1050 38 : static int ECWIsInputRGBColorSpace(GDALDataset *poSrcDS)
1051 : {
1052 38 : int nBands = poSrcDS->GetRasterCount();
1053 :
1054 : /* -------------------------------------------------------------------- */
1055 : /* Is the input RGB or RGBA? */
1056 : /* -------------------------------------------------------------------- */
1057 38 : int bRGBColorSpace = FALSE;
1058 38 : int bRGB = FALSE;
1059 38 : if (nBands >= 3)
1060 : {
1061 8 : bRGB = (poSrcDS->GetRasterBand(1)->GetColorInterpretation() ==
1062 : GCI_RedBand);
1063 8 : bRGB &= (poSrcDS->GetRasterBand(2)->GetColorInterpretation() ==
1064 : GCI_GreenBand);
1065 8 : bRGB &= (poSrcDS->GetRasterBand(3)->GetColorInterpretation() ==
1066 : GCI_BlueBand);
1067 : }
1068 38 : if (nBands == 3)
1069 : {
1070 6 : bRGBColorSpace = bRGB;
1071 : }
1072 32 : else if (nBands == 4 && bRGB)
1073 : {
1074 0 : bRGBColorSpace = (poSrcDS->GetRasterBand(4)->GetColorInterpretation() ==
1075 : GCI_AlphaBand);
1076 : }
1077 :
1078 38 : return bRGBColorSpace;
1079 : }
1080 :
1081 : /************************************************************************/
1082 : /* ECWCreateCopy() */
1083 : /************************************************************************/
1084 :
1085 36 : static GDALDataset *ECWCreateCopy(const char *pszFilename, GDALDataset *poSrcDS,
1086 : int bStrict, char **papszOptions,
1087 : GDALProgressFunc pfnProgress,
1088 : void *pProgressData, int bIsJPEG2000)
1089 :
1090 : {
1091 36 : ECWInitialize();
1092 :
1093 : /* -------------------------------------------------------------------- */
1094 : /* Get various values from the source dataset. */
1095 : /* -------------------------------------------------------------------- */
1096 36 : int nBands = poSrcDS->GetRasterCount();
1097 36 : int nXSize = poSrcDS->GetRasterXSize();
1098 36 : int nYSize = poSrcDS->GetRasterYSize();
1099 :
1100 36 : if (nBands == 0)
1101 : {
1102 0 : CPLError(
1103 : CE_Failure, CPLE_NotSupported,
1104 : "ECW driver does not support source dataset with zero band.\n");
1105 0 : return nullptr;
1106 : }
1107 :
1108 36 : GDALDataType eType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
1109 :
1110 36 : const OGRSpatialReference *poSRS = poSrcDS->GetSpatialRef();
1111 36 : GDALGeoTransform gt;
1112 36 : poSrcDS->GetGeoTransform(gt);
1113 :
1114 36 : if (poSrcDS->GetGCPCount() > 0)
1115 1 : poSRS = poSrcDS->GetGCPSpatialRef();
1116 :
1117 : /* --------------------------------------------------------------------
1118 : */
1119 : /* For ECW, confirm the datatype is 8bit (or uint16 for ECW v3) */
1120 : /* --------------------------------------------------------------------
1121 : */
1122 : #if ECWSDK_VERSION >= 50
1123 : bool bECWV3 = false;
1124 : if (bIsJPEG2000 == FALSE)
1125 : {
1126 : const char *pszOption =
1127 : CSLFetchNameValue(papszOptions, "ECW_FORMAT_VERSION");
1128 : if (pszOption != nullptr)
1129 : {
1130 : bECWV3 = (3 == atoi(pszOption));
1131 : }
1132 : }
1133 : #endif
1134 36 : if (!(eType == GDT_Byte ||
1135 : #if ECWSDK_VERSION >= 50
1136 : (bECWV3 && eType == GDT_UInt16) ||
1137 : #endif
1138 : bIsJPEG2000))
1139 : {
1140 0 : if (bStrict)
1141 : {
1142 0 : CPLError(
1143 : CE_Failure, CPLE_AppDefined,
1144 : "Attempt to create ECW file with pixel data type %s failed.\n"
1145 : "Only Byte data type supported for ECW version 2 files."
1146 : #if ECWSDK_VERSION >= 50
1147 : " ECW version 3 files supports UInt16 as well."
1148 : " Specify ECW_FORMAT_VERSION=3 creation option to write "
1149 : "version 3 file. \n"
1150 : #else
1151 : ". \n"
1152 : #endif
1153 : ,
1154 : GDALGetDataTypeName(eType));
1155 : }
1156 : else
1157 : {
1158 : #if ECWSDK_VERSION >= 50
1159 : if (eType == GDT_UInt16)
1160 : {
1161 : CPLError(CE_Warning, CPLE_AppDefined,
1162 : "ECW version 2 does not support UInt16 data type, "
1163 : "truncating to Byte."
1164 : " Consider specifying ECW_FORMAT_VERSION=3 for full "
1165 : "UInt16 support available in ECW version 3. \n");
1166 : }
1167 : else
1168 : #endif
1169 0 : CPLError(CE_Warning, CPLE_AppDefined,
1170 : "ECW v2 does not support data type, ignoring request "
1171 : "for %s. \n",
1172 : GDALGetDataTypeName(eType));
1173 :
1174 0 : eType = GDT_Byte;
1175 : }
1176 : }
1177 :
1178 : /* -------------------------------------------------------------------- */
1179 : /* Is the input RGB or RGBA? */
1180 : /* -------------------------------------------------------------------- */
1181 36 : int bRGBColorSpace = ECWIsInputRGBColorSpace(poSrcDS);
1182 :
1183 : /* -------------------------------------------------------------------- */
1184 : /* Setup the compressor. */
1185 : /* -------------------------------------------------------------------- */
1186 72 : GDALECWCompressor oCompressor;
1187 :
1188 36 : oCompressor.pfnProgress = pfnProgress;
1189 36 : oCompressor.pProgressData = pProgressData;
1190 36 : oCompressor.m_poSrcDS = poSrcDS;
1191 :
1192 72 : CPLStringList aosBandDescriptions;
1193 92 : for (int i = 0; i < nBands; i++)
1194 : {
1195 : /* Make a copy since ECWGetColorInterpretationName() can return a string
1196 : * generated */
1197 : /* by CPLSPrintf(), which has just a few rotating entries. */
1198 : aosBandDescriptions.AddString(ECWGetColorInterpretationName(
1199 56 : poSrcDS->GetRasterBand(i + 1)->GetColorInterpretation(), i));
1200 : }
1201 :
1202 36 : const char *pszAreaOrPoint = poSrcDS->GetMetadataItem(GDALMD_AREA_OR_POINT);
1203 36 : int bPixelIsPoint =
1204 36 : pszAreaOrPoint != nullptr && EQUAL(pszAreaOrPoint, GDALMD_AOP_POINT);
1205 :
1206 36 : if (oCompressor.Initialize(
1207 : pszFilename, papszOptions, nXSize, nYSize, nBands,
1208 36 : aosBandDescriptions.List(), bRGBColorSpace, eType, poSRS, gt,
1209 36 : poSrcDS->GetGCPCount(), poSrcDS->GetGCPs(), bIsJPEG2000,
1210 72 : bPixelIsPoint, poSrcDS->GetMetadata("RPC"), poSrcDS) != CE_None)
1211 : {
1212 4 : return nullptr;
1213 : }
1214 :
1215 : /* -------------------------------------------------------------------- */
1216 : /* Start the compression. */
1217 : /* -------------------------------------------------------------------- */
1218 :
1219 32 : if (!pfnProgress(0.0, nullptr, pProgressData))
1220 0 : return nullptr;
1221 :
1222 64 : CNCSError oErr = oCompressor.Write();
1223 :
1224 32 : if (oErr.GetErrorNumber() != NCS_SUCCESS)
1225 : {
1226 0 : ECWReportError(oErr);
1227 0 : return nullptr;
1228 : }
1229 :
1230 : /* -------------------------------------------------------------------- */
1231 : /* Cleanup, and return read-only handle. */
1232 : /* -------------------------------------------------------------------- */
1233 32 : oCompressor.CloseDown();
1234 32 : pfnProgress(1.0, nullptr, pProgressData);
1235 :
1236 : /* -------------------------------------------------------------------- */
1237 : /* Re-open dataset, and copy any auxiliary pam information. */
1238 : /* -------------------------------------------------------------------- */
1239 32 : GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
1240 32 : GDALPamDataset *poDS = nullptr;
1241 :
1242 32 : if (bIsJPEG2000)
1243 29 : poDS = cpl::down_cast<GDALPamDataset *>(
1244 : ECWDatasetOpenJPEG2000(&oOpenInfo));
1245 : else
1246 : poDS =
1247 3 : cpl::down_cast<GDALPamDataset *>(ECWDataset::OpenECW(&oOpenInfo));
1248 :
1249 32 : if (poDS)
1250 : {
1251 : #if ECWSDK_VERSION >= 50
1252 : for (int i = 1; i <= poSrcDS->GetRasterCount(); i++)
1253 : {
1254 : double dMin, dMax, dMean, dStdDev;
1255 : if (poSrcDS->GetRasterBand(i)->GetStatistics(
1256 : FALSE, FALSE, &dMin, &dMax, &dMean, &dStdDev) == CE_None)
1257 : {
1258 : poDS->GetRasterBand(i)->SetStatistics(dMin, dMax, dMean,
1259 : dStdDev);
1260 : }
1261 : double dHistMin, dHistMax;
1262 : int nBuckets;
1263 : GUIntBig *pHistogram = nullptr;
1264 : if (poSrcDS->GetRasterBand(i)->GetDefaultHistogram(
1265 : &dHistMin, &dHistMax, &nBuckets, &pHistogram, FALSE,
1266 : nullptr, nullptr) == CE_None)
1267 : {
1268 : poDS->GetRasterBand(i)->SetDefaultHistogram(
1269 : dHistMin, dHistMax, nBuckets, pHistogram);
1270 : VSIFree(pHistogram);
1271 : }
1272 : }
1273 : #endif
1274 :
1275 32 : cpl::down_cast<ECWDataset *>(poDS)->SetPreventCopyingSomeMetadata(TRUE);
1276 32 : int nFlags = GCIF_PAM_DEFAULT;
1277 32 : if (bIsJPEG2000 && !CPLFetchBool(papszOptions, "WRITE_METADATA", false))
1278 23 : nFlags &= ~GCIF_METADATA;
1279 32 : poDS->CloneInfo(poSrcDS, nFlags);
1280 32 : cpl::down_cast<ECWDataset *>(poDS)->SetPreventCopyingSomeMetadata(
1281 : FALSE);
1282 : }
1283 :
1284 32 : return poDS;
1285 : }
1286 :
1287 : /************************************************************************/
1288 : /* ECWCreateCopyECW() */
1289 : /************************************************************************/
1290 :
1291 21 : GDALDataset *ECWCreateCopyECW(const char *pszFilename, GDALDataset *poSrcDS,
1292 : int bStrict, char **papszOptions,
1293 : GDALProgressFunc pfnProgress, void *pProgressData)
1294 :
1295 : {
1296 21 : int nBands = poSrcDS->GetRasterCount();
1297 21 : if (nBands == 0)
1298 : {
1299 1 : CPLError(
1300 : CE_Failure, CPLE_NotSupported,
1301 : "ECW driver does not support source dataset with zero band.\n");
1302 1 : return nullptr;
1303 : }
1304 :
1305 20 : if (!EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "ecw"))
1306 : {
1307 0 : CPLError(CE_Failure, CPLE_AppDefined,
1308 : "ECW driver does not support creating ECW files\n"
1309 : "with an extension other than .ecw");
1310 0 : return nullptr;
1311 : }
1312 :
1313 : #if ECWSDK_VERSION >= 50
1314 : bool bECWV3 = false;
1315 : const char *pszOption =
1316 : CSLFetchNameValue(papszOptions, "ECW_FORMAT_VERSION");
1317 : if (pszOption != nullptr)
1318 : {
1319 : bECWV3 = (3 == atoi(pszOption));
1320 : }
1321 :
1322 : #endif
1323 :
1324 20 : GDALDataType eDataType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
1325 20 : if (eDataType != GDT_Byte
1326 : #if ECWSDK_VERSION >= 50
1327 : && !(bECWV3 && (eDataType == GDT_UInt16))
1328 : #endif
1329 10 : && bStrict)
1330 : {
1331 : #if ECWSDK_VERSION >= 50
1332 : if (eDataType == GDT_UInt16)
1333 : {
1334 : CPLError(CE_Failure, CPLE_NotSupported,
1335 : "ECW v2 does not support UInt16 data type. Consider "
1336 : " specifying ECW_FORMAT_VERSION=3 for full UInt16 support "
1337 : "available in ECW v3. \n");
1338 : }
1339 : else
1340 : #endif
1341 : {
1342 10 : CPLError(CE_Failure, CPLE_NotSupported,
1343 : "ECW driver doesn't support data type %s. "
1344 : "Only unsigned eight "
1345 : #if ECWSDK_VERSION >= 50
1346 : "or sixteen "
1347 : #endif
1348 : "bit bands supported. \n",
1349 : GDALGetDataTypeName(eDataType));
1350 : }
1351 :
1352 10 : return nullptr;
1353 : }
1354 :
1355 10 : if (poSrcDS->GetRasterXSize() < 128 || poSrcDS->GetRasterYSize() < 128)
1356 : {
1357 5 : CPLError(CE_Failure, CPLE_NotSupported,
1358 : "ECW driver requires image to be at least 128x128,\n"
1359 : "the source image is %dx%d.\n",
1360 : poSrcDS->GetRasterXSize(), poSrcDS->GetRasterYSize());
1361 :
1362 5 : return nullptr;
1363 : }
1364 :
1365 5 : if (poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr)
1366 : {
1367 0 : CPLError((bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
1368 : "ECW driver ignores color table. "
1369 : "The source raster band will be considered as grey level.\n"
1370 : "Consider using color table expansion (-expand option in "
1371 : "gdal_translate)\n");
1372 0 : if (bStrict)
1373 0 : return nullptr;
1374 : }
1375 :
1376 5 : return ECWCreateCopy(pszFilename, poSrcDS, bStrict, papszOptions,
1377 5 : pfnProgress, pProgressData, FALSE);
1378 : }
1379 :
1380 : /************************************************************************/
1381 : /* ECWCreateCopyJPEG2000() */
1382 : /************************************************************************/
1383 :
1384 37 : GDALDataset *ECWCreateCopyJPEG2000(const char *pszFilename,
1385 : GDALDataset *poSrcDS, int bStrict,
1386 : char **papszOptions,
1387 : GDALProgressFunc pfnProgress,
1388 : void *pProgressData)
1389 :
1390 : {
1391 37 : int nBands = poSrcDS->GetRasterCount();
1392 37 : if (nBands == 0)
1393 : {
1394 1 : CPLError(
1395 : CE_Failure, CPLE_NotSupported,
1396 : "JP2ECW driver does not support source dataset with zero band.\n");
1397 1 : return nullptr;
1398 : }
1399 :
1400 36 : if (EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "ecw"))
1401 : {
1402 0 : CPLError(CE_Failure, CPLE_AppDefined,
1403 : "JP2ECW driver does not support creating JPEG2000 files\n"
1404 : "with a .ecw extension. Please use anything else.");
1405 0 : return nullptr;
1406 : }
1407 :
1408 36 : GDALDataType eDataType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
1409 36 : if (eDataType != GDT_Byte && eDataType != GDT_Int16 &&
1410 8 : eDataType != GDT_UInt16 && eDataType != GDT_Int32 &&
1411 6 : eDataType != GDT_UInt32 && eDataType != GDT_Float32
1412 : #if ECWSDK_VERSION >= 40
1413 : && eDataType != GDT_Float64
1414 : #endif
1415 5 : && bStrict)
1416 : {
1417 5 : CPLError(CE_Failure, CPLE_NotSupported,
1418 : "JP2ECW driver doesn't support data type %s. ",
1419 : GDALGetDataTypeName(eDataType));
1420 :
1421 5 : return nullptr;
1422 : }
1423 :
1424 31 : if (poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr)
1425 : {
1426 0 : CPLError((bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
1427 : "JP2ECW driver ignores color table. "
1428 : "The source raster band will be considered as grey level.\n"
1429 : "Consider using color table expansion (-expand option in "
1430 : "gdal_translate)\n");
1431 0 : if (bStrict)
1432 0 : return nullptr;
1433 : }
1434 :
1435 31 : return ECWCreateCopy(pszFilename, poSrcDS, bStrict, papszOptions,
1436 31 : pfnProgress, pProgressData, TRUE);
1437 : }
1438 :
1439 : /************************************************************************/
1440 : /************************************************************************
1441 :
1442 : ECW/JPEG200 Create() Support
1443 : ----------------------------
1444 :
1445 : The remainder of the file is code to implement the Create() method.
1446 : New dataset and raster band classes are defined specifically for the
1447 : purpose of being write-only. In particular, you cannot read back data
1448 : from these datasets, and writing must occur in a pretty specific order.
1449 :
1450 : That is, you need to write all metadata (projection, georef, etc) first
1451 : and then write the image data. All bands data for the first scanline
1452 : should be written followed by all bands for the second scanline and so on.
1453 :
1454 : Creation supports the same virtual subfile names as CreateCopy() supports.
1455 :
1456 : ************************************************************************/
1457 : /************************************************************************/
1458 :
1459 : /************************************************************************/
1460 : /* ==================================================================== */
1461 : /* ECWWriteDataset */
1462 : /* ==================================================================== */
1463 : /************************************************************************/
1464 :
1465 : class ECWWriteRasterBand;
1466 :
1467 : #ifdef OPTIMIZED_FOR_GDALWARP
1468 : class IRasterIORequest
1469 : {
1470 : public:
1471 : GDALRasterBand *poBand;
1472 : int nXOff;
1473 : int nYOff;
1474 : int nXSize;
1475 : int nYSize;
1476 : GByte *pabyData;
1477 : int nBufXSize;
1478 : int nBufYSize;
1479 :
1480 0 : IRasterIORequest(GDALRasterBand *poBandIn, int nXOffIn, int nYOffIn,
1481 : int nXSizeIn, int nYSizeIn, void *pData, int nBufXSizeIn,
1482 : int nBufYSizeIn, GDALDataType eBufType,
1483 : GSpacing nPixelSpace, GSpacing nLineSpace)
1484 0 : : poBand(poBandIn), nXOff(nXOffIn), nYOff(nYOffIn), nXSize(nXSizeIn),
1485 : nYSize(nYSizeIn), pabyData(nullptr), nBufXSize(nBufXSizeIn),
1486 0 : nBufYSize(nBufYSizeIn)
1487 : {
1488 0 : GDALDataType eDataType = poBand->GetRasterDataType();
1489 0 : const int nDataTypeSize = GDALGetDataTypeSizeBytes(eDataType);
1490 0 : pabyData = (GByte *)CPLMalloc(static_cast<size_t>(nBufXSize) *
1491 0 : nBufYSize * nDataTypeSize);
1492 0 : for (int iY = 0; iY < nBufYSize; iY++)
1493 : {
1494 0 : GDALCopyWords((GByte *)pData + iY * nLineSpace, eBufType,
1495 : static_cast<int>(nPixelSpace),
1496 0 : pabyData + static_cast<size_t>(iY) * nBufXSize *
1497 0 : nDataTypeSize,
1498 : eDataType, nDataTypeSize, nBufXSize);
1499 : }
1500 0 : }
1501 :
1502 0 : ~IRasterIORequest()
1503 0 : {
1504 0 : CPLFree(pabyData);
1505 0 : }
1506 : };
1507 : #endif
1508 :
1509 : class ECWWriteDataset final : public GDALDataset
1510 : {
1511 : friend class ECWWriteRasterBand;
1512 :
1513 : char *pszFilename;
1514 :
1515 : int bIsJPEG2000;
1516 : GDALDataType eDataType;
1517 : char **papszOptions;
1518 :
1519 : OGRSpatialReference m_oSRS{};
1520 : GDALGeoTransform m_gt{};
1521 :
1522 : GDALECWCompressor oCompressor;
1523 : int bCrystalized; // TODO: Spelling.
1524 :
1525 : int nLoadedLine;
1526 : GByte *pabyBILBuffer;
1527 :
1528 : int bOutOfOrderWriteOccurred;
1529 : #ifdef OPTIMIZED_FOR_GDALWARP
1530 : int nPrevIRasterIOBand;
1531 : #endif
1532 :
1533 : CPLErr Crystalize(); // TODO: Spelling.
1534 : CPLErr FlushLine();
1535 :
1536 : public:
1537 : ECWWriteDataset(const char *, int, int, int, GDALDataType,
1538 : char **papszOptions, int);
1539 : ~ECWWriteDataset();
1540 :
1541 : virtual CPLErr FlushCache(bool bAtClosing) override;
1542 :
1543 : virtual CPLErr GetGeoTransform(GDALGeoTransform >) const override;
1544 : virtual CPLErr SetGeoTransform(const GDALGeoTransform >) override;
1545 : const OGRSpatialReference *GetSpatialRef() const override;
1546 : CPLErr SetSpatialRef(const OGRSpatialReference *poSRS) override;
1547 :
1548 : #ifdef OPTIMIZED_FOR_GDALWARP
1549 : virtual CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
1550 : int nXSize, int nYSize, void *pData, int nBufXSize,
1551 : int nBufYSize, GDALDataType eBufType,
1552 : int nBandCount, BANDMAP_TYPE panBandMap,
1553 : GSpacing nPixelSpace, GSpacing nLineSpace,
1554 : GSpacing nBandSpace,
1555 : GDALRasterIOExtraArg *psExtraArg) override;
1556 : #endif
1557 : };
1558 :
1559 : /************************************************************************/
1560 : /* ==================================================================== */
1561 : /* ECWWriteRasterBand */
1562 : /* ==================================================================== */
1563 : /************************************************************************/
1564 :
1565 : class ECWWriteRasterBand final : public GDALRasterBand
1566 : {
1567 : friend class ECWWriteDataset;
1568 :
1569 : // NOTE: poDS may be altered for NITF/JPEG2000 files!
1570 : ECWWriteDataset *poGDS;
1571 :
1572 : GDALColorInterp eInterp;
1573 :
1574 : #ifdef OPTIMIZED_FOR_GDALWARP
1575 : IRasterIORequest *poIORequest;
1576 : #endif
1577 :
1578 : public:
1579 : ECWWriteRasterBand(ECWWriteDataset *, int);
1580 : ~ECWWriteRasterBand();
1581 :
1582 6 : virtual CPLErr SetColorInterpretation(GDALColorInterp eInterpIn) override
1583 : {
1584 6 : eInterp = eInterpIn;
1585 6 : if (strlen(GetDescription()) == 0)
1586 3 : SetDescription(ECWGetColorInterpretationName(eInterp, nBand - 1));
1587 6 : return CE_None;
1588 : }
1589 :
1590 6 : virtual GDALColorInterp GetColorInterpretation() override
1591 : {
1592 6 : return eInterp;
1593 : }
1594 :
1595 : virtual CPLErr IReadBlock(int, int, void *) override;
1596 : virtual CPLErr IWriteBlock(int, int, void *) override;
1597 :
1598 : #ifdef OPTIMIZED_FOR_GDALWARP
1599 : virtual CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
1600 : int nXSize, int nYSize, void *pData, int nBufXSize,
1601 : int nBufYSize, GDALDataType eBufType,
1602 : GSpacing nPixelSpace, GSpacing nLineSpace,
1603 : GDALRasterIOExtraArg *psExtraArg) override;
1604 : #endif
1605 : };
1606 :
1607 : /************************************************************************/
1608 : /* ECWWriteDataset() */
1609 : /************************************************************************/
1610 :
1611 33 : ECWWriteDataset::ECWWriteDataset(const char *pszFilenameIn, int nXSize,
1612 : int nYSize, int nBandCount, GDALDataType eType,
1613 33 : char **papszOptionsIn, int bIsJPEG2000In)
1614 :
1615 : {
1616 33 : bCrystalized = FALSE;
1617 33 : pabyBILBuffer = nullptr;
1618 33 : nLoadedLine = -1;
1619 :
1620 33 : eAccess = GA_Update;
1621 :
1622 33 : this->bIsJPEG2000 = bIsJPEG2000In;
1623 33 : this->eDataType = eType;
1624 33 : this->papszOptions = CSLDuplicate(papszOptionsIn);
1625 33 : this->pszFilename = CPLStrdup(pszFilenameIn);
1626 :
1627 33 : nRasterXSize = nXSize;
1628 33 : nRasterYSize = nYSize;
1629 :
1630 : // create band objects.
1631 104 : for (int iBand = 1; iBand <= nBandCount; iBand++)
1632 : {
1633 71 : SetBand(iBand, new ECWWriteRasterBand(this, iBand));
1634 : }
1635 :
1636 33 : bOutOfOrderWriteOccurred = FALSE;
1637 : #ifdef OPTIMIZED_FOR_GDALWARP
1638 33 : nPrevIRasterIOBand = -1;
1639 : #endif
1640 33 : }
1641 :
1642 : /************************************************************************/
1643 : /* ~ECWWriteDataset() */
1644 : /************************************************************************/
1645 :
1646 66 : ECWWriteDataset::~ECWWriteDataset()
1647 :
1648 : {
1649 33 : ECWWriteDataset::FlushCache(true);
1650 :
1651 33 : if (bCrystalized)
1652 : {
1653 2 : if (bOutOfOrderWriteOccurred)
1654 : {
1655 : /* Otherwise there's a hang-up in the destruction of the oCompressor
1656 : * object */
1657 0 : while (nLoadedLine < nRasterYSize - 1)
1658 0 : FlushLine();
1659 : }
1660 2 : if (nLoadedLine == nRasterYSize - 1)
1661 2 : FlushLine();
1662 2 : oCompressor.CloseDown();
1663 : }
1664 :
1665 33 : CPLFree(pabyBILBuffer);
1666 33 : CSLDestroy(papszOptions);
1667 33 : CPLFree(pszFilename);
1668 66 : }
1669 :
1670 : /************************************************************************/
1671 : /* FlushCache() */
1672 : /************************************************************************/
1673 :
1674 35 : CPLErr ECWWriteDataset::FlushCache(bool bAtClosing)
1675 :
1676 : {
1677 35 : return BlockBasedFlushCache(bAtClosing);
1678 : }
1679 :
1680 : /************************************************************************/
1681 : /* GetSpatialRef() */
1682 : /************************************************************************/
1683 :
1684 0 : const OGRSpatialReference *ECWWriteDataset::GetSpatialRef() const
1685 : {
1686 0 : return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
1687 : }
1688 :
1689 : /************************************************************************/
1690 : /* GetGeoTransform() */
1691 : /************************************************************************/
1692 :
1693 31 : CPLErr ECWWriteDataset::GetGeoTransform(GDALGeoTransform >) const
1694 :
1695 : {
1696 31 : gt = m_gt;
1697 31 : return CE_None;
1698 : }
1699 :
1700 : /************************************************************************/
1701 : /* SetGeoTransform() */
1702 : /************************************************************************/
1703 :
1704 32 : CPLErr ECWWriteDataset::SetGeoTransform(const GDALGeoTransform >)
1705 :
1706 : {
1707 32 : m_gt = gt;
1708 32 : return CE_None;
1709 : }
1710 :
1711 : /************************************************************************/
1712 : /* SetSpatialRef() */
1713 : /************************************************************************/
1714 :
1715 32 : CPLErr ECWWriteDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
1716 :
1717 : {
1718 32 : m_oSRS.Clear();
1719 32 : if (poSRS)
1720 32 : m_oSRS = *poSRS;
1721 :
1722 32 : return CE_None;
1723 : }
1724 :
1725 : /************************************************************************/
1726 : /* Crystalize() */
1727 : /************************************************************************/
1728 :
1729 2 : CPLErr ECWWriteDataset::Crystalize()
1730 :
1731 : {
1732 2 : const int nWordSize = GDALGetDataTypeSizeBytes(eDataType);
1733 :
1734 : CPLErr eErr;
1735 :
1736 2 : if (bCrystalized)
1737 0 : return CE_None;
1738 :
1739 : const char **paszBandDescriptions =
1740 2 : (const char **)CPLMalloc(nBands * sizeof(char *));
1741 6 : for (int i = 0; i < nBands; i++)
1742 : {
1743 4 : paszBandDescriptions[i] = GetRasterBand(i + 1)->GetDescription();
1744 : }
1745 :
1746 2 : int bRGBColorSpace = ECWIsInputRGBColorSpace(this);
1747 :
1748 4 : eErr = oCompressor.Initialize(pszFilename, papszOptions, nRasterXSize,
1749 : nRasterYSize, nBands, paszBandDescriptions,
1750 2 : bRGBColorSpace, eDataType, &m_oSRS, m_gt, 0,
1751 : nullptr, bIsJPEG2000, FALSE, nullptr);
1752 :
1753 2 : if (eErr == CE_None)
1754 2 : bCrystalized = TRUE;
1755 :
1756 2 : nLoadedLine = -1;
1757 4 : pabyBILBuffer = (GByte *)CPLMalloc(static_cast<size_t>(nWordSize) * nBands *
1758 2 : nRasterXSize);
1759 :
1760 2 : CPLFree(paszBandDescriptions);
1761 :
1762 2 : return eErr;
1763 : }
1764 :
1765 : /************************************************************************/
1766 : /* FlushLine() */
1767 : /************************************************************************/
1768 :
1769 202 : CPLErr ECWWriteDataset::FlushLine()
1770 :
1771 : {
1772 202 : const int nWordSize = GDALGetDataTypeSizeBytes(eDataType);
1773 : CPLErr eErr;
1774 :
1775 : /* -------------------------------------------------------------------- */
1776 : /* Crystallize if not already done. */
1777 : /* -------------------------------------------------------------------- */
1778 202 : if (!bCrystalized)
1779 : {
1780 2 : eErr = Crystalize();
1781 :
1782 2 : if (eErr != CE_None)
1783 0 : return eErr;
1784 : }
1785 :
1786 : /* -------------------------------------------------------------------- */
1787 : /* Write out the currently loaded line. */
1788 : /* -------------------------------------------------------------------- */
1789 202 : if (nLoadedLine != -1)
1790 : {
1791 :
1792 200 : void **papOutputLine = (void **)CPLMalloc(sizeof(void *) * nBands);
1793 600 : for (int i = 0; i < nBands; i++)
1794 400 : papOutputLine[i] =
1795 400 : (void *)(pabyBILBuffer +
1796 400 : static_cast<size_t>(i) * nWordSize * nRasterXSize);
1797 :
1798 200 : eErr = oCompressor.ourWriteLineBIL((UINT16)nBands, papOutputLine);
1799 200 : CPLFree(papOutputLine);
1800 200 : if (eErr != CE_None)
1801 : {
1802 0 : return eErr;
1803 : }
1804 : }
1805 :
1806 : /* -------------------------------------------------------------------- */
1807 : /* Clear the buffer and increment the "current line" indicator. */
1808 : /* -------------------------------------------------------------------- */
1809 202 : memset(pabyBILBuffer, 0,
1810 202 : static_cast<size_t>(nWordSize) * nRasterXSize * nBands);
1811 202 : nLoadedLine++;
1812 :
1813 202 : return CE_None;
1814 : }
1815 :
1816 : #ifdef OPTIMIZED_FOR_GDALWARP
1817 : /************************************************************************/
1818 : /* IRasterIO() */
1819 : /************************************************************************/
1820 :
1821 200 : CPLErr ECWWriteDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
1822 : int nXSize, int nYSize, void *pData,
1823 : int nBufXSize, int nBufYSize,
1824 : GDALDataType eBufType, int nBandCount,
1825 : BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
1826 : GSpacing nLineSpace, GSpacing nBandSpace,
1827 : GDALRasterIOExtraArg *psExtraArg)
1828 : {
1829 200 : ECWWriteRasterBand *po4thBand = nullptr;
1830 200 : IRasterIORequest *poIORequest = nullptr;
1831 :
1832 200 : if (bOutOfOrderWriteOccurred)
1833 0 : return CE_Failure;
1834 :
1835 200 : if (eRWFlag == GF_Write && nBandCount == 3 && nBands == 4)
1836 : {
1837 0 : po4thBand = cpl::down_cast<ECWWriteRasterBand *>(GetRasterBand(4));
1838 0 : poIORequest = po4thBand->poIORequest;
1839 0 : if (poIORequest != nullptr)
1840 : {
1841 0 : if (nXOff != poIORequest->nXOff || nYOff != poIORequest->nYOff ||
1842 0 : nXSize != poIORequest->nXSize ||
1843 0 : nYSize != poIORequest->nYSize ||
1844 0 : nBufXSize != poIORequest->nBufXSize ||
1845 0 : nBufYSize != poIORequest->nBufYSize)
1846 : {
1847 0 : CPLError(CE_Failure, CPLE_AppDefined, "Out of order write");
1848 0 : bOutOfOrderWriteOccurred = TRUE;
1849 0 : return CE_Failure;
1850 : }
1851 : }
1852 : }
1853 :
1854 200 : const int nDataTypeSize = GDALGetDataTypeSizeBytes(eDataType);
1855 200 : if (eRWFlag == GF_Write && nXOff == 0 && nXSize == nRasterXSize &&
1856 200 : nBufXSize == nXSize && nBufYSize == nYSize && eBufType == eDataType &&
1857 100 : (nBandCount == nBands ||
1858 0 : (nBandCount == 3 && poIORequest != nullptr && nBands == 4)) &&
1859 100 : nPixelSpace == nDataTypeSize &&
1860 100 : nLineSpace == nPixelSpace * nRasterXSize)
1861 : {
1862 100 : CPLErr eErr = CE_None;
1863 100 : GByte *pabyData = (GByte *)pData;
1864 200 : for (int iY = 0; iY < nYSize; iY++)
1865 : {
1866 200 : for (int iBand = 0; iBand < nBandCount && eErr == CE_None; iBand++)
1867 : {
1868 100 : eErr = GetRasterBand(panBandMap[iBand])
1869 200 : ->WriteBlock(0, iY + nYOff,
1870 100 : pabyData + iY * nLineSpace +
1871 100 : iBand * nBandSpace);
1872 : }
1873 :
1874 100 : if (poIORequest != nullptr && eErr == CE_None)
1875 : {
1876 0 : eErr = po4thBand->WriteBlock(0, iY + nYOff,
1877 0 : poIORequest->pabyData +
1878 0 : iY * nDataTypeSize * nXSize);
1879 : }
1880 : }
1881 :
1882 100 : if (poIORequest != nullptr)
1883 : {
1884 0 : delete poIORequest;
1885 0 : po4thBand->poIORequest = nullptr;
1886 : }
1887 :
1888 100 : return eErr;
1889 : }
1890 : else
1891 100 : return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
1892 : pData, nBufXSize, nBufYSize, eBufType,
1893 : nBandCount, panBandMap, nPixelSpace,
1894 100 : nLineSpace, nBandSpace, psExtraArg);
1895 : }
1896 : #endif
1897 :
1898 : /************************************************************************/
1899 : /* ==================================================================== */
1900 : /* ECWWriteRasterBand */
1901 : /* ==================================================================== */
1902 : /************************************************************************/
1903 :
1904 : /************************************************************************/
1905 : /* ECWWriteRasterBand() */
1906 : /************************************************************************/
1907 :
1908 71 : ECWWriteRasterBand::ECWWriteRasterBand(ECWWriteDataset *poDSIn, int nBandIn)
1909 :
1910 : {
1911 71 : nBand = nBandIn;
1912 71 : poDS = poDSIn;
1913 71 : poGDS = poDSIn;
1914 71 : nBlockXSize = poDSIn->GetRasterXSize();
1915 71 : nBlockYSize = 1;
1916 71 : eDataType = poDSIn->eDataType;
1917 71 : eInterp = GCI_Undefined;
1918 : #ifdef OPTIMIZED_FOR_GDALWARP
1919 71 : poIORequest = nullptr;
1920 : #endif
1921 71 : }
1922 :
1923 : /************************************************************************/
1924 : /* ~ECWWriteRasterBand() */
1925 : /************************************************************************/
1926 :
1927 142 : ECWWriteRasterBand::~ECWWriteRasterBand()
1928 :
1929 : {
1930 : #ifdef OPTIMIZED_FOR_GDALWARP
1931 71 : delete poIORequest;
1932 : #endif
1933 142 : }
1934 :
1935 : /************************************************************************/
1936 : /* IReadBlock() */
1937 : /************************************************************************/
1938 :
1939 0 : CPLErr ECWWriteRasterBand::IReadBlock(CPL_UNUSED int nBlockX,
1940 : CPL_UNUSED int nBlockY, void *pBuffer)
1941 : {
1942 0 : const int nWordSize = GDALGetDataTypeSizeBytes(eDataType);
1943 :
1944 : // We zero stuff out here, but we can't really read stuff from
1945 : // a write only stream.
1946 :
1947 0 : memset(pBuffer, 0, static_cast<size_t>(nBlockXSize) * nWordSize);
1948 :
1949 0 : return CE_None;
1950 : }
1951 :
1952 : #ifdef OPTIMIZED_FOR_GDALWARP
1953 : /************************************************************************/
1954 : /* IRasterIO() */
1955 : /************************************************************************/
1956 :
1957 300 : CPLErr ECWWriteRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
1958 : int nXSize, int nYSize, void *pData,
1959 : int nBufXSize, int nBufYSize,
1960 : GDALDataType eBufType,
1961 : GSpacing nPixelSpace, GSpacing nLineSpace,
1962 : GDALRasterIOExtraArg *psExtraArg)
1963 : {
1964 300 : if (eRWFlag == GF_Write && nBand == 4 && poGDS->nBands == 4 &&
1965 0 : poGDS->nPrevIRasterIOBand < 0)
1966 : {
1967 : /* Triggered when gdalwarp outputs an alpha band */
1968 : /* It is called before GDALDatasetRasterIO() on the 3 first bands */
1969 0 : if (poIORequest != nullptr)
1970 0 : return CE_Failure;
1971 0 : poIORequest = new IRasterIORequest(this, nXOff, nYOff, nXSize, nYSize,
1972 : pData, nBufXSize, nBufYSize,
1973 0 : eBufType, nPixelSpace, nLineSpace);
1974 0 : return CE_None;
1975 : }
1976 :
1977 300 : poGDS->nPrevIRasterIOBand = nBand;
1978 300 : return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
1979 : pData, nBufXSize, nBufYSize, eBufType,
1980 300 : nPixelSpace, nLineSpace, psExtraArg);
1981 : }
1982 : #endif
1983 :
1984 : /************************************************************************/
1985 : /* IWriteBlock() */
1986 : /************************************************************************/
1987 :
1988 400 : CPLErr ECWWriteRasterBand::IWriteBlock(CPL_UNUSED int nBlockX, int nBlockY,
1989 : void *pBuffer)
1990 : {
1991 400 : const int nWordSize = GDALGetDataTypeSizeBytes(eDataType);
1992 : CPLErr eErr;
1993 :
1994 400 : if (poGDS->bOutOfOrderWriteOccurred)
1995 0 : return CE_Failure;
1996 :
1997 : /* -------------------------------------------------------------------- */
1998 : /* Flush previous line if needed. */
1999 : /* -------------------------------------------------------------------- */
2000 400 : if (nBlockY == poGDS->nLoadedLine + 1)
2001 : {
2002 200 : eErr = poGDS->FlushLine();
2003 200 : if (eErr != CE_None)
2004 0 : return eErr;
2005 : }
2006 :
2007 : /* -------------------------------------------------------------------- */
2008 : /* Blow a gasket if we have been asked to write something out */
2009 : /* of order. */
2010 : /* -------------------------------------------------------------------- */
2011 400 : if (nBlockY != poGDS->nLoadedLine)
2012 : {
2013 0 : CPLError(CE_Failure, CPLE_AppDefined,
2014 : "Apparent attempt to write to ECW non-sequentially.\n"
2015 : "Loaded line is %d, but %d of band %d was written to.",
2016 0 : poGDS->nLoadedLine, nBlockY, nBand);
2017 0 : poGDS->bOutOfOrderWriteOccurred = TRUE;
2018 0 : return CE_Failure;
2019 : }
2020 :
2021 : /* -------------------------------------------------------------------- */
2022 : /* Copy passed data into current line buffer. */
2023 : /* -------------------------------------------------------------------- */
2024 400 : memcpy(poGDS->pabyBILBuffer + (nBand - 1) * nWordSize * nRasterXSize,
2025 400 : pBuffer, nWordSize * nRasterXSize);
2026 :
2027 400 : return CE_None;
2028 : }
2029 :
2030 : /************************************************************************/
2031 : /* ECWCreateJPEG2000() */
2032 : /************************************************************************/
2033 :
2034 51 : GDALDataset *ECWCreateJPEG2000(const char *pszFilename, int nXSize, int nYSize,
2035 : int nBands, GDALDataType eType,
2036 : char **papszOptions)
2037 :
2038 : {
2039 51 : if (nBands == 0)
2040 : {
2041 18 : CPLError(CE_Failure, CPLE_NotSupported, "0 band not supported");
2042 18 : return nullptr;
2043 : }
2044 33 : ECWInitialize();
2045 :
2046 : return new ECWWriteDataset(pszFilename, nXSize, nYSize, nBands, eType,
2047 33 : papszOptions, TRUE);
2048 : }
2049 :
2050 : /************************************************************************/
2051 : /* ECWCreateECW() */
2052 : /************************************************************************/
2053 :
2054 0 : GDALDataset *ECWCreateECW(const char *pszFilename, int nXSize, int nYSize,
2055 : int nBands, GDALDataType eType, char **papszOptions)
2056 :
2057 : {
2058 0 : if (nBands == 0)
2059 : {
2060 0 : CPLError(CE_Failure, CPLE_NotSupported, "0 band not supported");
2061 0 : return nullptr;
2062 : }
2063 0 : ECWInitialize();
2064 :
2065 : return new ECWWriteDataset(pszFilename, nXSize, nYSize, nBands, eType,
2066 0 : papszOptions, FALSE);
2067 : }
2068 :
2069 : #endif /* def HAVE_COMPRESS */
|