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 : ~GDALECWCompressor() override;
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 : void WriteStatus(UINT32 nCurrentLine) override;
73 : #endif
74 :
75 : 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, CSLConstList 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 : 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, CSLConstList papszRPCMD,
387 : GDALDataset *poSrcDS)
388 :
389 : {
390 : /* -------------------------------------------------------------------- */
391 : /* For 4.x and beyond you need a license key to compress data. */
392 : /* Check for it as a configuration option or a creation option. */
393 : /* -------------------------------------------------------------------- */
394 : #if ECWSDK_VERSION >= 40
395 : const char *pszECWKey = CSLFetchNameValue(papszOptions, "ECW_ENCODE_KEY");
396 : if (pszECWKey == nullptr)
397 : pszECWKey = CPLGetConfigOption("ECW_ENCODE_KEY", nullptr);
398 :
399 : const char *pszECWCompany =
400 : CSLFetchNameValue(papszOptions, "ECW_ENCODE_COMPANY");
401 : if (pszECWCompany == nullptr)
402 : pszECWCompany = CPLGetConfigOption("ECW_ENCODE_COMPANY", nullptr);
403 :
404 : if (pszECWKey && pszECWCompany)
405 : {
406 : CPLDebug("ECW", "SetOEMKey(%s,%s)", pszECWCompany, pszECWKey);
407 : CNCSFile::SetOEMKey((char *)pszECWCompany, (char *)pszECWKey);
408 : }
409 : else if (pszECWKey || pszECWCompany)
410 : {
411 : CPLError(CE_Failure, CPLE_AppDefined,
412 : "Only one of ECW_ENCODE_KEY and ECW_ENCODE_COMPANY were "
413 : "provided.\nBoth are required.");
414 : return CE_Failure;
415 : }
416 : else
417 : {
418 : CPLError(CE_Failure, CPLE_AppDefined,
419 : "None of ECW_ENCODE_KEY and ECW_ENCODE_COMPANY were "
420 : "provided.\nBoth are required.");
421 : return CE_Failure;
422 : }
423 :
424 : #endif /* ECWSDK_VERSION >= 40 */
425 :
426 : /* -------------------------------------------------------------------- */
427 : /* Do some rudimentary checking in input. */
428 : /* -------------------------------------------------------------------- */
429 38 : if (nBands == 0)
430 : {
431 0 : CPLError(CE_Failure, CPLE_NotSupported,
432 : "ECW driver requires at least one band.");
433 0 : return CE_Failure;
434 : }
435 :
436 : /* -------------------------------------------------------------------- */
437 : /* Parse out some known options. */
438 : /* -------------------------------------------------------------------- */
439 : float fTargetCompression;
440 :
441 : // Default compression based on image type per request from Paul Beaty.
442 38 : if (nBands > 1)
443 11 : fTargetCompression = 95.0;
444 : else
445 27 : fTargetCompression = 90.0;
446 :
447 38 : if (CSLFetchNameValue(papszOptions, "TARGET") != nullptr)
448 : {
449 10 : fTargetCompression =
450 10 : (float)CPLAtof(CSLFetchNameValue(papszOptions, "TARGET"));
451 :
452 : /* The max allowed value should be 100 - 100 / 65535 = 99.9984740978 */
453 : /* so that nCompressionRate fits on a uint16 (see below) */
454 : /* No need to be so pedantic, so we will limit to 99.99 % */
455 : /* (compression rate = 10 000) */
456 10 : if (fTargetCompression < 0.0 || fTargetCompression > 99.99)
457 : {
458 0 : CPLError(CE_Failure, CPLE_NotSupported,
459 : "TARGET compression of %.3f invalid, should be a\n"
460 : "value between 0 and 99.99 percent.\n",
461 : (double)fTargetCompression);
462 0 : return CE_Failure;
463 : }
464 : }
465 :
466 : /* -------------------------------------------------------------------- */
467 : /* Create and initialize compressor. */
468 : /* -------------------------------------------------------------------- */
469 38 : NCSFileViewFileInfoEx *psClient = &(sFileInfo);
470 38 : const char *pszOption = nullptr;
471 : #if ECWSDK_VERSION >= 50
472 : if (bIsJPEG2000 == FALSE)
473 : {
474 : bool bECWV3 = false;
475 : pszOption = CSLFetchNameValue(papszOptions, "ECW_FORMAT_VERSION");
476 : if (pszOption != nullptr)
477 : {
478 : bECWV3 = (3 == atoi(pszOption));
479 : }
480 : psClient->nFormatVersion = (bECWV3) ? 3 : 2;
481 : }
482 : else
483 : {
484 : psClient->nFormatVersion = 1;
485 : }
486 : #endif
487 38 : psClient->nBands = (UINT16)nBands;
488 38 : psClient->nSizeX = nXSize;
489 38 : psClient->nSizeY = nYSize;
490 38 : psClient->nCompressionRate =
491 38 : (UINT16)MAX(1, 100 / (100 - fTargetCompression));
492 38 : psClient->eCellSizeUnits = ECW_CELL_UNITS_METERS;
493 :
494 38 : if (nBands == 1)
495 27 : psClient->eColorSpace = NCSCS_GREYSCALE;
496 11 : else if (nBands == 3 && bRGBColorSpace)
497 5 : psClient->eColorSpace = NCSCS_sRGB;
498 : #if ECWSDK_VERSION >= 40
499 : else if (nBands == 4 && bRGBColorSpace)
500 : psClient->eColorSpace = NCSCS_sRGB;
501 : #endif
502 : else
503 6 : psClient->eColorSpace = NCSCS_MULTIBAND;
504 :
505 : /* -------------------------------------------------------------------- */
506 : /* Figure out the data type. */
507 : /* -------------------------------------------------------------------- */
508 38 : int bSigned = FALSE;
509 38 : int nBits = 8;
510 38 : eWorkDT = eType;
511 :
512 38 : switch (eWorkDT)
513 : {
514 30 : case GDT_UInt8:
515 : #if ECWSDK_VERSION >= 50
516 : psClient->nCellBitDepth = 8;
517 : #endif
518 30 : psClient->eCellType = NCSCT_UINT8;
519 30 : nBits = 8;
520 30 : bSigned = FALSE;
521 30 : break;
522 :
523 2 : case GDT_UInt16:
524 : #if ECWSDK_VERSION >= 50
525 : psClient->nCellBitDepth = 16;
526 : #endif
527 2 : psClient->eCellType = NCSCT_UINT16;
528 2 : nBits = 16;
529 2 : bSigned = FALSE;
530 2 : break;
531 :
532 1 : case GDT_UInt32:
533 : #if ECWSDK_VERSION >= 50
534 : psClient->nCellBitDepth = 32;
535 : #endif
536 1 : psClient->eCellType = NCSCT_UINT32;
537 1 : nBits = 32;
538 1 : bSigned = FALSE;
539 1 : break;
540 :
541 3 : case GDT_Int16:
542 : #if ECWSDK_VERSION >= 50
543 : psClient->nCellBitDepth = 16;
544 : #endif
545 3 : psClient->eCellType = NCSCT_INT16;
546 3 : nBits = 16;
547 3 : bSigned = TRUE;
548 3 : break;
549 :
550 1 : case GDT_Int32:
551 : #if ECWSDK_VERSION >= 50
552 : psClient->nCellBitDepth = 32;
553 : #endif
554 1 : psClient->eCellType = NCSCT_INT32;
555 1 : nBits = 32;
556 1 : bSigned = TRUE;
557 1 : break;
558 :
559 1 : case GDT_Float32:
560 1 : psClient->eCellType = NCSCT_IEEE4;
561 1 : nBits = 32;
562 1 : bSigned = TRUE;
563 1 : break;
564 :
565 : #if ECWSDK_VERSION >= 40
566 : case GDT_Float64:
567 : psClient->eCellType = NCSCT_IEEE8;
568 : nBits = 64;
569 : bSigned = TRUE;
570 : break;
571 : #endif
572 :
573 0 : default:
574 : // We treat complex types as float.
575 0 : psClient->eCellType = NCSCT_IEEE4;
576 0 : nBits = 32;
577 0 : bSigned = TRUE;
578 0 : eWorkDT = GDT_Float32;
579 0 : break;
580 : }
581 :
582 : /* -------------------------------------------------------------------- */
583 : /* Create band information structures. */
584 : /* -------------------------------------------------------------------- */
585 : int iBand;
586 :
587 38 : psClient->pBands =
588 38 : (NCSFileBandInfo *)NCSMalloc(sizeof(NCSFileBandInfo) * nBands, true);
589 98 : for (iBand = 0; iBand < nBands; iBand++)
590 : {
591 60 : const char *pszNBITS = CSLFetchNameValue(papszOptions, "NBITS");
592 60 : if (pszNBITS && atoi(pszNBITS) > 0)
593 2 : psClient->pBands[iBand].nBits = (UINT8)atoi(pszNBITS);
594 : else
595 58 : psClient->pBands[iBand].nBits = (UINT8)nBits;
596 60 : psClient->pBands[iBand].bSigned = (BOOLEAN)bSigned;
597 : #if ECWSDK_VERSION >= 50
598 : psClient->pBands[iBand].szDesc =
599 : NCSStrDup(papszBandDescriptions[iBand]);
600 : #else
601 120 : psClient->pBands[iBand].szDesc =
602 60 : NCSStrDup((char *)papszBandDescriptions[iBand]);
603 : #endif
604 : }
605 :
606 : /* -------------------------------------------------------------------- */
607 : /* Allow CNCSFile::SetParameter() requests. */
608 : /* -------------------------------------------------------------------- */
609 :
610 38 : if (bIsJPEG2000)
611 : {
612 33 : pszOption = CSLFetchNameValue(papszOptions, "PROFILE");
613 33 : if (pszOption != nullptr && EQUAL(pszOption, "BASELINE_0"))
614 0 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_PROFILE_BASELINE_0);
615 33 : else if (pszOption != nullptr && EQUAL(pszOption, "BASELINE_1"))
616 0 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_PROFILE_BASELINE_1);
617 33 : else if (pszOption != nullptr && EQUAL(pszOption, "BASELINE_2"))
618 0 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_PROFILE_BASELINE_2);
619 33 : else if (pszOption != nullptr && EQUAL(pszOption, "NPJE"))
620 7 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_PROFILE_NITF_BIIF_NPJE);
621 26 : else if (pszOption != nullptr && EQUAL(pszOption, "EPJE"))
622 0 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_PROFILE_NITF_BIIF_EPJE);
623 :
624 33 : pszOption = CSLFetchNameValue(papszOptions, "CODESTREAM_ONLY");
625 62 : if (pszOption == nullptr &&
626 62 : EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "j2k"))
627 2 : pszOption = "YES";
628 33 : if (pszOption != nullptr)
629 12 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_CODESTREAM_ONLY,
630 6 : CPLTestBool(pszOption));
631 :
632 33 : pszOption = CSLFetchNameValue(papszOptions, "LEVELS");
633 33 : if (pszOption != nullptr)
634 0 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_LEVELS,
635 0 : (UINT32)atoi(pszOption));
636 :
637 33 : pszOption = CSLFetchNameValue(papszOptions, "LAYERS");
638 33 : if (pszOption != nullptr)
639 3 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_LAYERS,
640 3 : (UINT32)atoi(pszOption));
641 :
642 33 : pszOption = CSLFetchNameValue(papszOptions, "PRECINCT_WIDTH");
643 33 : if (pszOption != nullptr)
644 0 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_PRECINCT_WIDTH,
645 0 : (UINT32)atoi(pszOption));
646 :
647 33 : pszOption = CSLFetchNameValue(papszOptions, "PRECINCT_HEIGHT");
648 33 : if (pszOption != nullptr)
649 0 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_PRECINCT_HEIGHT,
650 0 : (UINT32)atoi(pszOption));
651 :
652 33 : pszOption = CSLFetchNameValue(papszOptions, "TILE_WIDTH");
653 33 : if (pszOption != nullptr)
654 0 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_TILE_WIDTH,
655 0 : (UINT32)atoi(pszOption));
656 :
657 33 : pszOption = CSLFetchNameValue(papszOptions, "TILE_HEIGHT");
658 33 : if (pszOption != nullptr)
659 0 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_TILE_HEIGHT,
660 0 : (UINT32)atoi(pszOption));
661 :
662 33 : pszOption = CSLFetchNameValue(papszOptions, "INCLUDE_SOP");
663 33 : if (pszOption != nullptr)
664 0 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_INCLUDE_SOP,
665 0 : CPLTestBool(pszOption));
666 :
667 33 : pszOption = CSLFetchNameValue(papszOptions, "INCLUDE_EPH");
668 33 : if (pszOption != nullptr)
669 0 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_INCLUDE_EPH,
670 0 : CPLTestBool(pszOption));
671 :
672 33 : pszOption = CSLFetchNameValue(papszOptions, "PROGRESSION");
673 33 : if (pszOption != nullptr && EQUAL(pszOption, "LRCP"))
674 0 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_PROGRESSION_LRCP);
675 :
676 33 : else if (pszOption != nullptr && EQUAL(pszOption, "RLCP"))
677 0 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_PROGRESSION_RLCP);
678 :
679 33 : else if (pszOption != nullptr && EQUAL(pszOption, "RPCL"))
680 0 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_PROGRESSION_RPCL);
681 :
682 33 : pszOption = CSLFetchNameValue(papszOptions, "GEODATA_USAGE");
683 33 : if (pszOption == nullptr)
684 : // Default to suppressing ECW SDK geodata, just use our own stuff.
685 33 : SetGeodataUsage(JP2_GEODATA_USE_NONE);
686 0 : else if (EQUAL(pszOption, "NONE"))
687 0 : SetGeodataUsage(JP2_GEODATA_USE_NONE);
688 0 : else if (EQUAL(pszOption, "PCS_ONLY"))
689 0 : SetGeodataUsage(JP2_GEODATA_USE_PCS_ONLY);
690 0 : else if (EQUAL(pszOption, "GML_ONLY"))
691 0 : SetGeodataUsage(JP2_GEODATA_USE_GML_ONLY);
692 0 : else if (EQUAL(pszOption, "PCS_GML"))
693 0 : SetGeodataUsage(JP2_GEODATA_USE_PCS_GML);
694 0 : else if (EQUAL(pszOption, "GML_PCS"))
695 0 : SetGeodataUsage(JP2_GEODATA_USE_GML_PCS);
696 0 : else if (EQUAL(pszOption, "ALL"))
697 0 : SetGeodataUsage(JP2_GEODATA_USE_GML_PCS_WLD);
698 :
699 33 : pszOption = CSLFetchNameValue(papszOptions, "DECOMPRESS_LAYERS");
700 33 : if (pszOption != nullptr)
701 0 : SetParameter(CNCSJP2FileView::JP2_DECOMPRESS_LAYERS,
702 0 : (UINT32)atoi(pszOption));
703 :
704 33 : pszOption = CSLFetchNameValue(papszOptions,
705 : "DECOMPRESS_RECONSTRUCTION_PARAMETER");
706 33 : if (pszOption != nullptr)
707 0 : SetParameter(
708 : CNCSJP2FileView::JPC_DECOMPRESS_RECONSTRUCTION_PARAMETER,
709 0 : (IEEE4)CPLAtof(pszOption));
710 : }
711 :
712 : /* -------------------------------------------------------------------- */
713 : /* Georeferencing. */
714 : /* -------------------------------------------------------------------- */
715 :
716 38 : psClient->fOriginX = 0.0;
717 38 : psClient->fOriginY = psClient->nSizeY;
718 38 : psClient->fCellIncrementX = 1.0;
719 38 : psClient->fCellIncrementY = -1.0;
720 38 : psClient->fCWRotationDegrees = 0.0;
721 :
722 38 : if (gt[2] != 0.0 || gt[4] != 0.0)
723 0 : CPLError(CE_Warning, CPLE_NotSupported,
724 : "Rotational coefficients ignored, georeferencing of\n"
725 : "output ECW file will be incorrect.\n");
726 : else
727 : {
728 38 : psClient->fOriginX = gt[0];
729 38 : psClient->fOriginY = gt[3];
730 38 : psClient->fCellIncrementX = gt[1];
731 38 : psClient->fCellIncrementY = gt[5];
732 : }
733 :
734 : /* -------------------------------------------------------------------- */
735 : /* Projection. */
736 : /* -------------------------------------------------------------------- */
737 : char szProjection[128];
738 : char szDatum[128];
739 : char szUnits[128];
740 :
741 38 : strcpy(szProjection, "RAW");
742 38 : strcpy(szDatum, "RAW");
743 :
744 38 : if (CSLFetchNameValue(papszOptions, "PROJ") != nullptr)
745 : {
746 0 : strncpy(szProjection, CSLFetchNameValue(papszOptions, "PROJ"),
747 : sizeof(szProjection));
748 0 : szProjection[sizeof(szProjection) - 1] = 0;
749 : }
750 :
751 38 : if (CSLFetchNameValue(papszOptions, "DATUM") != nullptr)
752 : {
753 0 : strncpy(szDatum, CSLFetchNameValue(papszOptions, "DATUM"),
754 : sizeof(szDatum));
755 0 : szDatum[sizeof(szDatum) - 1] = 0;
756 0 : if (EQUAL(szProjection, "RAW"))
757 0 : strcpy(szProjection, "GEODETIC");
758 : }
759 :
760 38 : const char *pszUnits = CSLFetchNameValue(papszOptions, "UNITS");
761 38 : if (pszUnits != nullptr)
762 : {
763 0 : psClient->eCellSizeUnits = ECWTranslateToCellSizeUnits(pszUnits);
764 : }
765 :
766 38 : if (EQUAL(szProjection, "RAW") && poSRS != nullptr && !poSRS->IsEmpty())
767 : {
768 25 : ECWTranslateFromWKT(poSRS, szProjection, sizeof(szProjection), szDatum,
769 : sizeof(szDatum), szUnits);
770 25 : psClient->eCellSizeUnits = ECWTranslateToCellSizeUnits(szUnits);
771 : }
772 :
773 38 : NCSFree(psClient->szDatum);
774 38 : psClient->szDatum = NCSStrDup(szDatum);
775 38 : NCSFree(psClient->szProjection);
776 38 : psClient->szProjection = NCSStrDup(szProjection);
777 :
778 38 : CPLDebug("ECW", "Writing with PROJ=%s, DATUM=%s, UNITS=%s", szProjection,
779 : szDatum, ECWTranslateFromCellSizeUnits(psClient->eCellSizeUnits));
780 :
781 : /* -------------------------------------------------------------------- */
782 : /* Setup GML and GeoTIFF information. */
783 : /* -------------------------------------------------------------------- */
784 38 : if ((poSRS != nullptr && !poSRS->IsEmpty()) || gt != GDALGeoTransform() ||
785 76 : nGCPCount > 0 || papszRPCMD != nullptr)
786 : {
787 74 : GDALJP2Metadata oJP2MD;
788 :
789 37 : oJP2MD.SetSpatialRef(poSRS);
790 37 : oJP2MD.SetGeoTransform(gt);
791 37 : oJP2MD.SetGCPs(nGCPCount, pasGCPList);
792 37 : oJP2MD.bPixelIsPoint = bPixelIsPoint;
793 37 : oJP2MD.SetRPCMD(papszRPCMD);
794 :
795 37 : if (bIsJPEG2000)
796 : {
797 32 : if (CPLFetchBool(papszOptions, "WRITE_METADATA", false))
798 : {
799 6 : if (!CPLFetchBool(papszOptions, "MAIN_MD_DOMAIN_ONLY", false))
800 : {
801 6 : WriteXMLBoxes();
802 : }
803 6 : WriteJP2Box(
804 : GDALJP2Metadata::CreateGDALMultiDomainMetadataXMLBox(
805 6 : m_poSrcDS, CPLFetchBool(papszOptions,
806 : "MAIN_MD_DOMAIN_ONLY", false)));
807 : }
808 32 : if (CPLFetchBool(papszOptions, "GMLJP2", true))
809 : {
810 : const char *pszGMLJP2V2Def =
811 29 : CSLFetchNameValue(papszOptions, "GMLJP2V2_DEF");
812 29 : if (pszGMLJP2V2Def != nullptr)
813 : {
814 0 : WriteJP2Box(oJP2MD.CreateGMLJP2V2(nXSize, nYSize,
815 : pszGMLJP2V2Def, poSrcDS));
816 : }
817 : else
818 : {
819 48 : if (!poSRS || poSRS->IsEmpty() ||
820 19 : GDALJP2Metadata::IsSRSCompatible(poSRS))
821 : {
822 28 : WriteJP2Box(oJP2MD.CreateGMLJP2(nXSize, nYSize));
823 : }
824 1 : else if (CSLFetchNameValue(papszOptions, "GMLJP2"))
825 : {
826 0 : CPLError(CE_Warning, CPLE_AppDefined,
827 : "GMLJP2 box was explicitly required but "
828 : "cannot be written due "
829 : "to lack of georeferencing and/or unsupported "
830 : "georeferencing "
831 : "for GMLJP2");
832 : }
833 : else
834 : {
835 1 : CPLDebug(
836 : "JP2ECW",
837 : "Cannot write GMLJP2 box due to unsupported SRS");
838 : }
839 : }
840 : }
841 32 : if (CPLFetchBool(papszOptions, "GeoJP2", true))
842 29 : WriteJP2Box(oJP2MD.CreateJP2GeoTIFF());
843 38 : if (CPLFetchBool(papszOptions, "WRITE_METADATA", false) &&
844 6 : !CPLFetchBool(papszOptions, "MAIN_MD_DOMAIN_ONLY", false))
845 : {
846 6 : WriteJP2Box(GDALJP2Metadata::CreateXMPBox(m_poSrcDS));
847 : }
848 : }
849 : }
850 : /* -------------------------------------------------------------------- */
851 : /* We handle all jpeg2000 files via the VSIIOStream, but ECW */
852 : /* files cannot be done this way for some reason. */
853 : /* -------------------------------------------------------------------- */
854 38 : VSILFILE *fpVSIL = nullptr;
855 :
856 38 : if (bIsJPEG2000)
857 : {
858 66 : int bSeekable = !(STARTS_WITH(pszFilename, "/vsistdout/") ||
859 33 : STARTS_WITH(pszFilename, "/vsizip/") ||
860 33 : STARTS_WITH(pszFilename, "/vsigzip/"));
861 33 : fpVSIL = VSIFOpenL(pszFilename, (bSeekable) ? "wb+" : "wb");
862 33 : if (fpVSIL == nullptr)
863 : {
864 2 : CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open/create %s.",
865 : pszFilename);
866 2 : return CE_Failure;
867 : }
868 :
869 31 : m_OStream->Access(fpVSIL, TRUE, (BOOLEAN)bSeekable, pszFilename, 0, -1);
870 : }
871 : else
872 : {
873 5 : if (!STARTS_WITH(pszFilename, "/vsi"))
874 : {
875 : // Try now to create the file to avoid memory leaks if it is
876 : // the SDK that fails to do it.
877 5 : fpVSIL = VSIFOpenL(pszFilename, "wb");
878 5 : if (fpVSIL == nullptr)
879 : {
880 2 : CPLError(CE_Failure, CPLE_OpenFailed,
881 : "Failed to open/create %s.", pszFilename);
882 2 : return CE_Failure;
883 : }
884 3 : VSIFCloseL(fpVSIL);
885 3 : VSIUnlink(pszFilename);
886 3 : fpVSIL = nullptr;
887 : }
888 : }
889 :
890 : /* -------------------------------------------------------------------- */
891 : /* Check if we can enable large files. This option should only */
892 : /* be set when the application is adhering to one of the */
893 : /* ERMapper options for licensing larger than 500MB input */
894 : /* files. See Bug 767. This option no longer exists with */
895 : /* version 4+. */
896 : /* -------------------------------------------------------------------- */
897 : #if ECWSDK_VERSION < 40
898 34 : const char *pszLargeOK = CSLFetchNameValue(papszOptions, "LARGE_OK");
899 34 : if (pszLargeOK == nullptr)
900 34 : pszLargeOK = "NO";
901 :
902 34 : pszLargeOK = CPLGetConfigOption("ECW_LARGE_OK", pszLargeOK);
903 :
904 34 : if (CPLTestBool(pszLargeOK))
905 : {
906 0 : CNCSFile::SetKeySize();
907 0 : CPLDebug("ECW", "Large file generation enabled.");
908 : }
909 : #endif /* ECWSDK_VERSION < 40 */
910 : /* -------------------------------------------------------------------- */
911 : /* Infer metadata information from source dataset if possible */
912 : /* -------------------------------------------------------------------- */
913 : #if ECWSDK_VERSION >= 50
914 : if (psClient->nFormatVersion > 2)
915 : {
916 : if (psClient->pFileMetaData == nullptr)
917 : {
918 : NCSEcwInitMetaData(&psClient->pFileMetaData);
919 : }
920 : if (m_poSrcDS && m_poSrcDS->GetMetadataItem(
921 : "FILE_METADATA_ACQUISITION_DATE") != nullptr)
922 : {
923 : psClient->pFileMetaData->sAcquisitionDate =
924 : NCSStrDupT(NCS::CString(m_poSrcDS->GetMetadataItem(
925 : "FILE_METADATA_ACQUISITION_DATE"))
926 : .c_str());
927 : }
928 :
929 : if (m_poSrcDS &&
930 : m_poSrcDS->GetMetadataItem(
931 : "FILE_METADATA_ACQUISITION_SENSOR_NAME") != nullptr)
932 : {
933 : psClient->pFileMetaData->sAcquisitionSensorName = NCSStrDupT(
934 : NCS::CString(m_poSrcDS->GetMetadataItem(
935 : "FILE_METADATA_ACQUISITION_SENSOR_NAME"))
936 : .c_str());
937 : }
938 : if (m_poSrcDS &&
939 : m_poSrcDS->GetMetadataItem("FILE_METADATA_ADDRESS") != nullptr)
940 : {
941 : psClient->pFileMetaData->sAddress =
942 : NCSStrDupT(NCS::CString(m_poSrcDS->GetMetadataItem(
943 : "FILE_METADATA_ADDRESS"))
944 : .c_str());
945 : }
946 : if (m_poSrcDS &&
947 : m_poSrcDS->GetMetadataItem("FILE_METADATA_AUTHOR") != nullptr)
948 : {
949 : psClient->pFileMetaData->sAuthor = NCSStrDupT(
950 : NCS::CString(m_poSrcDS->GetMetadataItem("FILE_METADATA_AUTHOR"))
951 : .c_str());
952 : }
953 : if (m_poSrcDS && m_poSrcDS->GetMetadataItem(
954 : "FILE_METADATA_CLASSIFICATION") != nullptr)
955 : {
956 : psClient->pFileMetaData->sClassification =
957 : NCSStrDupT(NCS::CString(m_poSrcDS->GetMetadataItem(
958 : "FILE_METADATA_CLASSIFICATION"))
959 : .c_str());
960 : }
961 : if (pszECWCompany != nullptr &&
962 : CPLTestBool(CPLGetConfigOption("GDAL_ECW_WRITE_COMPANY", "YES")))
963 : {
964 : psClient->pFileMetaData->sCompany =
965 : NCSStrDupT(NCS::CString(pszECWCompany).c_str());
966 : }
967 : CPLString osCompressionSoftware = GetCompressionSoftwareName();
968 : if (!osCompressionSoftware.empty())
969 : {
970 : psClient->pFileMetaData->sCompressionSoftware =
971 : NCSStrDupT(NCS::CString(osCompressionSoftware.c_str()).c_str());
972 : }
973 : if (m_poSrcDS &&
974 : m_poSrcDS->GetMetadataItem("FILE_METADATA_COPYRIGHT") != nullptr)
975 : {
976 : psClient->pFileMetaData->sCopyright =
977 : NCSStrDupT(NCS::CString(m_poSrcDS->GetMetadataItem(
978 : "FILE_METADATA_COPYRIGHT"))
979 : .c_str());
980 : }
981 : if (m_poSrcDS &&
982 : m_poSrcDS->GetMetadataItem("FILE_METADATA_EMAIL") != nullptr)
983 : {
984 : psClient->pFileMetaData->sEmail = NCSStrDupT(
985 : NCS::CString(m_poSrcDS->GetMetadataItem("FILE_METADATA_EMAIL"))
986 : .c_str());
987 : }
988 : if (m_poSrcDS &&
989 : m_poSrcDS->GetMetadataItem("FILE_METADATA_TELEPHONE") != nullptr)
990 : {
991 : psClient->pFileMetaData->sTelephone =
992 : NCSStrDupT(NCS::CString(m_poSrcDS->GetMetadataItem(
993 : "FILE_METADATA_TELEPHONE"))
994 : .c_str());
995 : }
996 : }
997 : #endif
998 : /* -------------------------------------------------------------------- */
999 : /* Set the file info. */
1000 : /* -------------------------------------------------------------------- */
1001 68 : CNCSError oError = SetFileInfo(sFileInfo);
1002 :
1003 34 : if (oError.GetErrorNumber() == NCS_SUCCESS)
1004 : {
1005 34 : if (fpVSIL == nullptr)
1006 : {
1007 : #if ECWSDK_VERSION >= 40 && defined(_WIN32)
1008 : if (CPLTestBool(CPLGetConfigOption("GDAL_FILENAME_IS_UTF8", "YES")))
1009 : {
1010 : wchar_t *pwszFilename =
1011 : CPLRecodeToWChar(pszFilename, CPL_ENC_UTF8, CPL_ENC_UCS2);
1012 : oError = GetCNCSError(Open(pwszFilename, false, true));
1013 : CPLFree(pwszFilename);
1014 : }
1015 : else
1016 : #endif
1017 : {
1018 3 : oError = GetCNCSError(Open((char *)pszFilename, false, true));
1019 : }
1020 : }
1021 : else
1022 : {
1023 : #if ECWSDK_VERSION >= 55
1024 : oError = CNCSJP2FileView::Open(m_OStream);
1025 : #else
1026 31 : oError = CNCSJP2FileView::Open(m_OStream.get());
1027 : #endif
1028 : }
1029 : }
1030 :
1031 34 : if (oError.GetErrorNumber() == NCS_SUCCESS)
1032 34 : return CE_None;
1033 0 : else if (oError.GetErrorNumber() == NCS_INPUT_SIZE_EXCEEDED)
1034 : {
1035 0 : CPLError(CE_Failure, CPLE_AppDefined,
1036 : "ECW SDK compress limit exceeded.");
1037 0 : return CE_Failure;
1038 : }
1039 : else
1040 : {
1041 0 : ECWReportError(oError);
1042 :
1043 0 : return CE_Failure;
1044 : }
1045 : }
1046 :
1047 : /************************************************************************/
1048 : /* ECWIsInputRGBColorSpace() */
1049 : /************************************************************************/
1050 :
1051 38 : static int ECWIsInputRGBColorSpace(GDALDataset *poSrcDS)
1052 : {
1053 38 : int nBands = poSrcDS->GetRasterCount();
1054 :
1055 : /* -------------------------------------------------------------------- */
1056 : /* Is the input RGB or RGBA? */
1057 : /* -------------------------------------------------------------------- */
1058 38 : int bRGBColorSpace = FALSE;
1059 38 : int bRGB = FALSE;
1060 38 : if (nBands >= 3)
1061 : {
1062 8 : bRGB = (poSrcDS->GetRasterBand(1)->GetColorInterpretation() ==
1063 : GCI_RedBand);
1064 8 : bRGB &= (poSrcDS->GetRasterBand(2)->GetColorInterpretation() ==
1065 : GCI_GreenBand);
1066 8 : bRGB &= (poSrcDS->GetRasterBand(3)->GetColorInterpretation() ==
1067 : GCI_BlueBand);
1068 : }
1069 38 : if (nBands == 3)
1070 : {
1071 6 : bRGBColorSpace = bRGB;
1072 : }
1073 32 : else if (nBands == 4 && bRGB)
1074 : {
1075 0 : bRGBColorSpace = (poSrcDS->GetRasterBand(4)->GetColorInterpretation() ==
1076 : GCI_AlphaBand);
1077 : }
1078 :
1079 38 : return bRGBColorSpace;
1080 : }
1081 :
1082 : /************************************************************************/
1083 : /* ECWCreateCopy() */
1084 : /************************************************************************/
1085 :
1086 36 : static GDALDataset *ECWCreateCopy(const char *pszFilename, GDALDataset *poSrcDS,
1087 : int bStrict, char **papszOptions,
1088 : GDALProgressFunc pfnProgress,
1089 : void *pProgressData, int bIsJPEG2000)
1090 :
1091 : {
1092 36 : ECWInitialize();
1093 :
1094 : /* -------------------------------------------------------------------- */
1095 : /* Get various values from the source dataset. */
1096 : /* -------------------------------------------------------------------- */
1097 36 : int nBands = poSrcDS->GetRasterCount();
1098 36 : int nXSize = poSrcDS->GetRasterXSize();
1099 36 : int nYSize = poSrcDS->GetRasterYSize();
1100 :
1101 36 : if (nBands == 0)
1102 : {
1103 0 : CPLError(
1104 : CE_Failure, CPLE_NotSupported,
1105 : "ECW driver does not support source dataset with zero band.\n");
1106 0 : return nullptr;
1107 : }
1108 :
1109 36 : GDALDataType eType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
1110 :
1111 36 : const OGRSpatialReference *poSRS = poSrcDS->GetSpatialRef();
1112 36 : GDALGeoTransform gt;
1113 36 : poSrcDS->GetGeoTransform(gt);
1114 :
1115 36 : if (poSrcDS->GetGCPCount() > 0)
1116 1 : poSRS = poSrcDS->GetGCPSpatialRef();
1117 :
1118 : /* --------------------------------------------------------------------
1119 : */
1120 : /* For ECW, confirm the datatype is 8bit (or uint16 for ECW v3) */
1121 : /* --------------------------------------------------------------------
1122 : */
1123 : #if ECWSDK_VERSION >= 50
1124 : bool bECWV3 = false;
1125 : if (bIsJPEG2000 == FALSE)
1126 : {
1127 : const char *pszOption =
1128 : CSLFetchNameValue(papszOptions, "ECW_FORMAT_VERSION");
1129 : if (pszOption != nullptr)
1130 : {
1131 : bECWV3 = (3 == atoi(pszOption));
1132 : }
1133 : }
1134 : #endif
1135 36 : if (!(eType == GDT_UInt8 ||
1136 : #if ECWSDK_VERSION >= 50
1137 : (bECWV3 && eType == GDT_UInt16) ||
1138 : #endif
1139 : bIsJPEG2000))
1140 : {
1141 0 : if (bStrict)
1142 : {
1143 0 : CPLError(
1144 : CE_Failure, CPLE_AppDefined,
1145 : "Attempt to create ECW file with pixel data type %s failed.\n"
1146 : "Only Byte data type supported for ECW version 2 files."
1147 : #if ECWSDK_VERSION >= 50
1148 : " ECW version 3 files supports UInt16 as well."
1149 : " Specify ECW_FORMAT_VERSION=3 creation option to write "
1150 : "version 3 file. \n"
1151 : #else
1152 : ". \n"
1153 : #endif
1154 : ,
1155 : GDALGetDataTypeName(eType));
1156 : }
1157 : else
1158 : {
1159 : #if ECWSDK_VERSION >= 50
1160 : if (eType == GDT_UInt16)
1161 : {
1162 : CPLError(CE_Warning, CPLE_AppDefined,
1163 : "ECW version 2 does not support UInt16 data type, "
1164 : "truncating to Byte."
1165 : " Consider specifying ECW_FORMAT_VERSION=3 for full "
1166 : "UInt16 support available in ECW version 3. \n");
1167 : }
1168 : else
1169 : #endif
1170 0 : CPLError(CE_Warning, CPLE_AppDefined,
1171 : "ECW v2 does not support data type, ignoring request "
1172 : "for %s. \n",
1173 : GDALGetDataTypeName(eType));
1174 :
1175 0 : eType = GDT_UInt8;
1176 : }
1177 : }
1178 :
1179 : /* -------------------------------------------------------------------- */
1180 : /* Is the input RGB or RGBA? */
1181 : /* -------------------------------------------------------------------- */
1182 36 : int bRGBColorSpace = ECWIsInputRGBColorSpace(poSrcDS);
1183 :
1184 : /* -------------------------------------------------------------------- */
1185 : /* Setup the compressor. */
1186 : /* -------------------------------------------------------------------- */
1187 72 : GDALECWCompressor oCompressor;
1188 :
1189 36 : oCompressor.pfnProgress = pfnProgress;
1190 36 : oCompressor.pProgressData = pProgressData;
1191 36 : oCompressor.m_poSrcDS = poSrcDS;
1192 :
1193 72 : CPLStringList aosBandDescriptions;
1194 92 : for (int i = 0; i < nBands; i++)
1195 : {
1196 : /* Make a copy since ECWGetColorInterpretationName() can return a string
1197 : * generated */
1198 : /* by CPLSPrintf(), which has just a few rotating entries. */
1199 : aosBandDescriptions.AddString(ECWGetColorInterpretationName(
1200 56 : poSrcDS->GetRasterBand(i + 1)->GetColorInterpretation(), i));
1201 : }
1202 :
1203 36 : const char *pszAreaOrPoint = poSrcDS->GetMetadataItem(GDALMD_AREA_OR_POINT);
1204 36 : int bPixelIsPoint =
1205 36 : pszAreaOrPoint != nullptr && EQUAL(pszAreaOrPoint, GDALMD_AOP_POINT);
1206 :
1207 36 : if (oCompressor.Initialize(
1208 : pszFilename, papszOptions, nXSize, nYSize, nBands,
1209 36 : aosBandDescriptions.List(), bRGBColorSpace, eType, poSRS, gt,
1210 36 : poSrcDS->GetGCPCount(), poSrcDS->GetGCPs(), bIsJPEG2000,
1211 72 : bPixelIsPoint, poSrcDS->GetMetadata("RPC"), poSrcDS) != CE_None)
1212 : {
1213 4 : return nullptr;
1214 : }
1215 :
1216 : /* -------------------------------------------------------------------- */
1217 : /* Start the compression. */
1218 : /* -------------------------------------------------------------------- */
1219 :
1220 32 : if (!pfnProgress(0.0, nullptr, pProgressData))
1221 0 : return nullptr;
1222 :
1223 64 : CNCSError oErr = oCompressor.Write();
1224 :
1225 32 : if (oErr.GetErrorNumber() != NCS_SUCCESS)
1226 : {
1227 0 : ECWReportError(oErr);
1228 0 : return nullptr;
1229 : }
1230 :
1231 : /* -------------------------------------------------------------------- */
1232 : /* Cleanup, and return read-only handle. */
1233 : /* -------------------------------------------------------------------- */
1234 32 : oCompressor.CloseDown();
1235 32 : pfnProgress(1.0, nullptr, pProgressData);
1236 :
1237 : /* -------------------------------------------------------------------- */
1238 : /* Re-open dataset, and copy any auxiliary pam information. */
1239 : /* -------------------------------------------------------------------- */
1240 32 : GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
1241 32 : GDALPamDataset *poDS = nullptr;
1242 :
1243 32 : if (bIsJPEG2000)
1244 29 : poDS = cpl::down_cast<GDALPamDataset *>(
1245 : ECWDatasetOpenJPEG2000(&oOpenInfo));
1246 : else
1247 : poDS =
1248 3 : cpl::down_cast<GDALPamDataset *>(ECWDataset::OpenECW(&oOpenInfo));
1249 :
1250 32 : if (poDS)
1251 : {
1252 : #if ECWSDK_VERSION >= 50
1253 : for (int i = 1; i <= poSrcDS->GetRasterCount(); i++)
1254 : {
1255 : double dMin, dMax, dMean, dStdDev;
1256 : if (poSrcDS->GetRasterBand(i)->GetStatistics(
1257 : FALSE, FALSE, &dMin, &dMax, &dMean, &dStdDev) == CE_None)
1258 : {
1259 : poDS->GetRasterBand(i)->SetStatistics(dMin, dMax, dMean,
1260 : dStdDev);
1261 : }
1262 : double dHistMin, dHistMax;
1263 : int nBuckets;
1264 : GUIntBig *pHistogram = nullptr;
1265 : if (poSrcDS->GetRasterBand(i)->GetDefaultHistogram(
1266 : &dHistMin, &dHistMax, &nBuckets, &pHistogram, FALSE,
1267 : nullptr, nullptr) == CE_None)
1268 : {
1269 : poDS->GetRasterBand(i)->SetDefaultHistogram(
1270 : dHistMin, dHistMax, nBuckets, pHistogram);
1271 : VSIFree(pHistogram);
1272 : }
1273 : }
1274 : #endif
1275 :
1276 32 : cpl::down_cast<ECWDataset *>(poDS)->SetPreventCopyingSomeMetadata(TRUE);
1277 32 : int nFlags = GCIF_PAM_DEFAULT;
1278 32 : if (bIsJPEG2000 && !CPLFetchBool(papszOptions, "WRITE_METADATA", false))
1279 23 : nFlags &= ~GCIF_METADATA;
1280 32 : poDS->CloneInfo(poSrcDS, nFlags);
1281 32 : cpl::down_cast<ECWDataset *>(poDS)->SetPreventCopyingSomeMetadata(
1282 : FALSE);
1283 : }
1284 :
1285 32 : return poDS;
1286 : }
1287 :
1288 : /************************************************************************/
1289 : /* ECWCreateCopyECW() */
1290 : /************************************************************************/
1291 :
1292 21 : GDALDataset *ECWCreateCopyECW(const char *pszFilename, GDALDataset *poSrcDS,
1293 : int bStrict, char **papszOptions,
1294 : GDALProgressFunc pfnProgress, void *pProgressData)
1295 :
1296 : {
1297 21 : int nBands = poSrcDS->GetRasterCount();
1298 21 : if (nBands == 0)
1299 : {
1300 1 : CPLError(
1301 : CE_Failure, CPLE_NotSupported,
1302 : "ECW driver does not support source dataset with zero band.\n");
1303 1 : return nullptr;
1304 : }
1305 :
1306 20 : if (!EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "ecw"))
1307 : {
1308 0 : CPLError(CE_Failure, CPLE_AppDefined,
1309 : "ECW driver does not support creating ECW files\n"
1310 : "with an extension other than .ecw");
1311 0 : return nullptr;
1312 : }
1313 :
1314 : #if ECWSDK_VERSION >= 50
1315 : bool bECWV3 = false;
1316 : const char *pszOption =
1317 : CSLFetchNameValue(papszOptions, "ECW_FORMAT_VERSION");
1318 : if (pszOption != nullptr)
1319 : {
1320 : bECWV3 = (3 == atoi(pszOption));
1321 : }
1322 :
1323 : #endif
1324 :
1325 20 : GDALDataType eDataType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
1326 20 : if (eDataType != GDT_UInt8
1327 : #if ECWSDK_VERSION >= 50
1328 : && !(bECWV3 && (eDataType == GDT_UInt16))
1329 : #endif
1330 10 : && bStrict)
1331 : {
1332 : #if ECWSDK_VERSION >= 50
1333 : if (eDataType == GDT_UInt16)
1334 : {
1335 : CPLError(CE_Failure, CPLE_NotSupported,
1336 : "ECW v2 does not support UInt16 data type. Consider "
1337 : " specifying ECW_FORMAT_VERSION=3 for full UInt16 support "
1338 : "available in ECW v3. \n");
1339 : }
1340 : else
1341 : #endif
1342 : {
1343 10 : CPLError(CE_Failure, CPLE_NotSupported,
1344 : "ECW driver doesn't support data type %s. "
1345 : "Only unsigned eight "
1346 : #if ECWSDK_VERSION >= 50
1347 : "or sixteen "
1348 : #endif
1349 : "bit bands supported. \n",
1350 : GDALGetDataTypeName(eDataType));
1351 : }
1352 :
1353 10 : return nullptr;
1354 : }
1355 :
1356 10 : if (poSrcDS->GetRasterXSize() < 128 || poSrcDS->GetRasterYSize() < 128)
1357 : {
1358 5 : CPLError(CE_Failure, CPLE_NotSupported,
1359 : "ECW driver requires image to be at least 128x128,\n"
1360 : "the source image is %dx%d.\n",
1361 : poSrcDS->GetRasterXSize(), poSrcDS->GetRasterYSize());
1362 :
1363 5 : return nullptr;
1364 : }
1365 :
1366 5 : if (poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr)
1367 : {
1368 0 : CPLError((bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
1369 : "ECW driver ignores color table. "
1370 : "The source raster band will be considered as grey level.\n"
1371 : "Consider using color table expansion (-expand option in "
1372 : "gdal_translate)\n");
1373 0 : if (bStrict)
1374 0 : return nullptr;
1375 : }
1376 :
1377 5 : return ECWCreateCopy(pszFilename, poSrcDS, bStrict, papszOptions,
1378 5 : pfnProgress, pProgressData, FALSE);
1379 : }
1380 :
1381 : /************************************************************************/
1382 : /* ECWCreateCopyJPEG2000() */
1383 : /************************************************************************/
1384 :
1385 37 : GDALDataset *ECWCreateCopyJPEG2000(const char *pszFilename,
1386 : GDALDataset *poSrcDS, int bStrict,
1387 : char **papszOptions,
1388 : GDALProgressFunc pfnProgress,
1389 : void *pProgressData)
1390 :
1391 : {
1392 37 : int nBands = poSrcDS->GetRasterCount();
1393 37 : if (nBands == 0)
1394 : {
1395 1 : CPLError(
1396 : CE_Failure, CPLE_NotSupported,
1397 : "JP2ECW driver does not support source dataset with zero band.\n");
1398 1 : return nullptr;
1399 : }
1400 :
1401 36 : if (EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "ecw"))
1402 : {
1403 0 : CPLError(CE_Failure, CPLE_AppDefined,
1404 : "JP2ECW driver does not support creating JPEG2000 files\n"
1405 : "with a .ecw extension. Please use anything else.");
1406 0 : return nullptr;
1407 : }
1408 :
1409 36 : GDALDataType eDataType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
1410 36 : if (eDataType != GDT_UInt8 && eDataType != GDT_Int16 &&
1411 8 : eDataType != GDT_UInt16 && eDataType != GDT_Int32 &&
1412 6 : eDataType != GDT_UInt32 && eDataType != GDT_Float32
1413 : #if ECWSDK_VERSION >= 40
1414 : && eDataType != GDT_Float64
1415 : #endif
1416 5 : && bStrict)
1417 : {
1418 5 : CPLError(CE_Failure, CPLE_NotSupported,
1419 : "JP2ECW driver doesn't support data type %s. ",
1420 : GDALGetDataTypeName(eDataType));
1421 :
1422 5 : return nullptr;
1423 : }
1424 :
1425 31 : if (poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr)
1426 : {
1427 0 : CPLError((bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
1428 : "JP2ECW driver ignores color table. "
1429 : "The source raster band will be considered as grey level.\n"
1430 : "Consider using color table expansion (-expand option in "
1431 : "gdal_translate)\n");
1432 0 : if (bStrict)
1433 0 : return nullptr;
1434 : }
1435 :
1436 31 : return ECWCreateCopy(pszFilename, poSrcDS, bStrict, papszOptions,
1437 31 : pfnProgress, pProgressData, TRUE);
1438 : }
1439 :
1440 : /************************************************************************/
1441 : /************************************************************************
1442 :
1443 : ECW/JPEG200 Create() Support
1444 : ----------------------------
1445 :
1446 : The remainder of the file is code to implement the Create() method.
1447 : New dataset and raster band classes are defined specifically for the
1448 : purpose of being write-only. In particular, you cannot read back data
1449 : from these datasets, and writing must occur in a pretty specific order.
1450 :
1451 : That is, you need to write all metadata (projection, georef, etc) first
1452 : and then write the image data. All bands data for the first scanline
1453 : should be written followed by all bands for the second scanline and so on.
1454 :
1455 : Creation supports the same virtual subfile names as CreateCopy() supports.
1456 :
1457 : ************************************************************************/
1458 : /************************************************************************/
1459 :
1460 : /************************************************************************/
1461 : /* ==================================================================== */
1462 : /* ECWWriteDataset */
1463 : /* ==================================================================== */
1464 : /************************************************************************/
1465 :
1466 : class ECWWriteRasterBand;
1467 :
1468 : #ifdef OPTIMIZED_FOR_GDALWARP
1469 : class IRasterIORequest
1470 : {
1471 : public:
1472 : GDALRasterBand *poBand;
1473 : int nXOff;
1474 : int nYOff;
1475 : int nXSize;
1476 : int nYSize;
1477 : GByte *pabyData;
1478 : int nBufXSize;
1479 : int nBufYSize;
1480 :
1481 0 : IRasterIORequest(GDALRasterBand *poBandIn, int nXOffIn, int nYOffIn,
1482 : int nXSizeIn, int nYSizeIn, void *pData, int nBufXSizeIn,
1483 : int nBufYSizeIn, GDALDataType eBufType,
1484 : GSpacing nPixelSpace, GSpacing nLineSpace)
1485 0 : : poBand(poBandIn), nXOff(nXOffIn), nYOff(nYOffIn), nXSize(nXSizeIn),
1486 : nYSize(nYSizeIn), pabyData(nullptr), nBufXSize(nBufXSizeIn),
1487 0 : nBufYSize(nBufYSizeIn)
1488 : {
1489 0 : GDALDataType eDataType = poBand->GetRasterDataType();
1490 0 : const int nDataTypeSize = GDALGetDataTypeSizeBytes(eDataType);
1491 0 : pabyData = (GByte *)CPLMalloc(static_cast<size_t>(nBufXSize) *
1492 0 : nBufYSize * nDataTypeSize);
1493 0 : for (int iY = 0; iY < nBufYSize; iY++)
1494 : {
1495 0 : GDALCopyWords((GByte *)pData + iY * nLineSpace, eBufType,
1496 : static_cast<int>(nPixelSpace),
1497 0 : pabyData + static_cast<size_t>(iY) * nBufXSize *
1498 0 : nDataTypeSize,
1499 : eDataType, nDataTypeSize, nBufXSize);
1500 : }
1501 0 : }
1502 :
1503 0 : ~IRasterIORequest()
1504 0 : {
1505 0 : CPLFree(pabyData);
1506 0 : }
1507 : };
1508 : #endif
1509 :
1510 : class ECWWriteDataset final : public GDALDataset
1511 : {
1512 : friend class ECWWriteRasterBand;
1513 :
1514 : char *pszFilename;
1515 :
1516 : int bIsJPEG2000;
1517 : GDALDataType eDataType;
1518 : char **papszOptions;
1519 :
1520 : OGRSpatialReference m_oSRS{};
1521 : GDALGeoTransform m_gt{};
1522 :
1523 : GDALECWCompressor oCompressor;
1524 : int bCrystalized; // TODO: Spelling.
1525 :
1526 : int nLoadedLine;
1527 : GByte *pabyBILBuffer;
1528 :
1529 : int bOutOfOrderWriteOccurred;
1530 : #ifdef OPTIMIZED_FOR_GDALWARP
1531 : int nPrevIRasterIOBand;
1532 : #endif
1533 :
1534 : CPLErr Crystalize(); // TODO: Spelling.
1535 : CPLErr FlushLine();
1536 :
1537 : public:
1538 : ECWWriteDataset(const char *, int, int, int, GDALDataType,
1539 : char **papszOptions, int);
1540 : ~ECWWriteDataset() override;
1541 :
1542 : CPLErr FlushCache(bool bAtClosing) override;
1543 :
1544 : CPLErr GetGeoTransform(GDALGeoTransform >) const override;
1545 : CPLErr SetGeoTransform(const GDALGeoTransform >) override;
1546 : const OGRSpatialReference *GetSpatialRef() const override;
1547 : CPLErr SetSpatialRef(const OGRSpatialReference *poSRS) override;
1548 :
1549 : #ifdef OPTIMIZED_FOR_GDALWARP
1550 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
1551 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
1552 : GDALDataType eBufType, int nBandCount,
1553 : BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
1554 : GSpacing nLineSpace, 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() override;
1581 :
1582 6 : 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 : GDALColorInterp GetColorInterpretation() override
1591 : {
1592 6 : return eInterp;
1593 : }
1594 :
1595 : CPLErr IReadBlock(int, int, void *) override;
1596 : CPLErr IWriteBlock(int, int, void *) override;
1597 :
1598 : #ifdef OPTIMIZED_FOR_GDALWARP
1599 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
1600 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
1601 : GDALDataType eBufType, GSpacing nPixelSpace,
1602 : 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 */
|