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, CSLConstList papszOptions,
78 : int nXSize, 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, CSLConstList 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, GDALMD_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.xrot != 0.0 || gt.yrot != 0.0)
723 0 : CPLError(CE_Warning, CPLE_NotSupported,
724 : "Rotational coefficients ignored, georeferencing of\n"
725 : "output ECW file will be incorrect.");
726 : else
727 : {
728 38 : psClient->fOriginX = gt.xorig;
729 38 : psClient->fOriginY = gt.yorig;
730 38 : psClient->fCellIncrementX = gt.xscale;
731 38 : psClient->fCellIncrementY = gt.yscale;
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, CSLConstList 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 datasets with zero bands.");
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. ");
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 36 : bPixelIsPoint, poSrcDS->GetMetadata(GDAL_MDD_RPC),
1212 36 : poSrcDS) != CE_None)
1213 : {
1214 4 : return nullptr;
1215 : }
1216 :
1217 : /* -------------------------------------------------------------------- */
1218 : /* Start the compression. */
1219 : /* -------------------------------------------------------------------- */
1220 :
1221 32 : if (!pfnProgress(0.0, nullptr, pProgressData))
1222 0 : return nullptr;
1223 :
1224 64 : CNCSError oErr = oCompressor.Write();
1225 :
1226 32 : if (oErr.GetErrorNumber() != NCS_SUCCESS)
1227 : {
1228 0 : ECWReportError(oErr);
1229 0 : return nullptr;
1230 : }
1231 :
1232 : /* -------------------------------------------------------------------- */
1233 : /* Cleanup, and return read-only handle. */
1234 : /* -------------------------------------------------------------------- */
1235 32 : oCompressor.CloseDown();
1236 32 : pfnProgress(1.0, nullptr, pProgressData);
1237 :
1238 : /* -------------------------------------------------------------------- */
1239 : /* Re-open dataset, and copy any auxiliary pam information. */
1240 : /* -------------------------------------------------------------------- */
1241 32 : GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
1242 32 : GDALPamDataset *poDS = nullptr;
1243 :
1244 32 : if (bIsJPEG2000)
1245 29 : poDS = cpl::down_cast<GDALPamDataset *>(
1246 : ECWDatasetOpenJPEG2000(&oOpenInfo));
1247 : else
1248 : poDS =
1249 3 : cpl::down_cast<GDALPamDataset *>(ECWDataset::OpenECW(&oOpenInfo));
1250 :
1251 32 : if (poDS)
1252 : {
1253 : #if ECWSDK_VERSION >= 50
1254 : for (int i = 1; i <= poSrcDS->GetRasterCount(); i++)
1255 : {
1256 : double dMin, dMax, dMean, dStdDev;
1257 : if (poSrcDS->GetRasterBand(i)->GetStatistics(
1258 : FALSE, FALSE, &dMin, &dMax, &dMean, &dStdDev) == CE_None)
1259 : {
1260 : poDS->GetRasterBand(i)->SetStatistics(dMin, dMax, dMean,
1261 : dStdDev);
1262 : }
1263 : double dHistMin, dHistMax;
1264 : int nBuckets;
1265 : GUIntBig *pHistogram = nullptr;
1266 : if (poSrcDS->GetRasterBand(i)->GetDefaultHistogram(
1267 : &dHistMin, &dHistMax, &nBuckets, &pHistogram, FALSE,
1268 : nullptr, nullptr) == CE_None)
1269 : {
1270 : poDS->GetRasterBand(i)->SetDefaultHistogram(
1271 : dHistMin, dHistMax, nBuckets, pHistogram);
1272 : VSIFree(pHistogram);
1273 : }
1274 : }
1275 : #endif
1276 :
1277 32 : cpl::down_cast<ECWDataset *>(poDS)->SetPreventCopyingSomeMetadata(TRUE);
1278 32 : int nFlags = GCIF_PAM_DEFAULT;
1279 32 : if (bIsJPEG2000 && !CPLFetchBool(papszOptions, "WRITE_METADATA", false))
1280 23 : nFlags &= ~GCIF_METADATA;
1281 32 : poDS->CloneInfo(poSrcDS, nFlags);
1282 32 : cpl::down_cast<ECWDataset *>(poDS)->SetPreventCopyingSomeMetadata(
1283 : FALSE);
1284 : }
1285 :
1286 32 : return poDS;
1287 : }
1288 :
1289 : /************************************************************************/
1290 : /* ECWCreateCopyECW() */
1291 : /************************************************************************/
1292 :
1293 21 : GDALDataset *ECWCreateCopyECW(const char *pszFilename, GDALDataset *poSrcDS,
1294 : int bStrict, CSLConstList papszOptions,
1295 : GDALProgressFunc pfnProgress, void *pProgressData)
1296 :
1297 : {
1298 21 : int nBands = poSrcDS->GetRasterCount();
1299 21 : if (nBands == 0)
1300 : {
1301 1 : CPLError(
1302 : CE_Failure, CPLE_NotSupported,
1303 : "ECW driver does not support source datasets with zero bands.");
1304 1 : return nullptr;
1305 : }
1306 :
1307 20 : if (!EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "ecw"))
1308 : {
1309 0 : CPLError(CE_Failure, CPLE_AppDefined,
1310 : "ECW driver does not support creating ECW files\n"
1311 : "with an extension other than .ecw");
1312 0 : return nullptr;
1313 : }
1314 :
1315 : #if ECWSDK_VERSION >= 50
1316 : bool bECWV3 = false;
1317 : const char *pszOption =
1318 : CSLFetchNameValue(papszOptions, "ECW_FORMAT_VERSION");
1319 : if (pszOption != nullptr)
1320 : {
1321 : bECWV3 = (3 == atoi(pszOption));
1322 : }
1323 :
1324 : #endif
1325 :
1326 20 : GDALDataType eDataType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
1327 20 : if (eDataType != GDT_UInt8
1328 : #if ECWSDK_VERSION >= 50
1329 : && !(bECWV3 && (eDataType == GDT_UInt16))
1330 : #endif
1331 10 : && bStrict)
1332 : {
1333 : #if ECWSDK_VERSION >= 50
1334 : if (eDataType == GDT_UInt16)
1335 : {
1336 : CPLError(CE_Failure, CPLE_NotSupported,
1337 : "ECW v2 does not support UInt16 data type. Consider "
1338 : " specifying ECW_FORMAT_VERSION=3 for full UInt16 support "
1339 : "available in ECW v3. ");
1340 : }
1341 : else
1342 : #endif
1343 : {
1344 10 : CPLError(CE_Failure, CPLE_NotSupported,
1345 : "ECW driver doesn't support data type %s. "
1346 : "Only unsigned eight "
1347 : #if ECWSDK_VERSION >= 50
1348 : "or sixteen "
1349 : #endif
1350 : "bit bands supported. \n",
1351 : GDALGetDataTypeName(eDataType));
1352 : }
1353 :
1354 10 : return nullptr;
1355 : }
1356 :
1357 10 : if (poSrcDS->GetRasterXSize() < 128 || poSrcDS->GetRasterYSize() < 128)
1358 : {
1359 5 : CPLError(CE_Failure, CPLE_NotSupported,
1360 : "ECW driver requires image to be at least 128x128,\n"
1361 : "the source image is %dx%d.\n",
1362 : poSrcDS->GetRasterXSize(), poSrcDS->GetRasterYSize());
1363 :
1364 5 : return nullptr;
1365 : }
1366 :
1367 5 : if (poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr)
1368 : {
1369 0 : CPLError((bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
1370 : "ECW driver ignores color table. "
1371 : "The source raster band will be considered as grey level.\n"
1372 : "Consider using color table expansion (-expand option in "
1373 : "gdal_translate)");
1374 0 : if (bStrict)
1375 0 : return nullptr;
1376 : }
1377 :
1378 5 : return ECWCreateCopy(pszFilename, poSrcDS, bStrict, papszOptions,
1379 5 : pfnProgress, pProgressData, FALSE);
1380 : }
1381 :
1382 : /************************************************************************/
1383 : /* ECWCreateCopyJPEG2000() */
1384 : /************************************************************************/
1385 :
1386 37 : GDALDataset *ECWCreateCopyJPEG2000(const char *pszFilename,
1387 : GDALDataset *poSrcDS, int bStrict,
1388 : CSLConstList papszOptions,
1389 : GDALProgressFunc pfnProgress,
1390 : void *pProgressData)
1391 :
1392 : {
1393 37 : int nBands = poSrcDS->GetRasterCount();
1394 37 : if (nBands == 0)
1395 : {
1396 1 : CPLError(
1397 : CE_Failure, CPLE_NotSupported,
1398 : "JP2ECW driver does not support source datasets with zero bands.");
1399 1 : return nullptr;
1400 : }
1401 :
1402 36 : if (EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "ecw"))
1403 : {
1404 0 : CPLError(CE_Failure, CPLE_AppDefined,
1405 : "JP2ECW driver does not support creating JPEG2000 files\n"
1406 : "with a .ecw extension. Please use anything else.");
1407 0 : return nullptr;
1408 : }
1409 :
1410 36 : GDALDataType eDataType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
1411 36 : if (eDataType != GDT_UInt8 && eDataType != GDT_Int16 &&
1412 8 : eDataType != GDT_UInt16 && eDataType != GDT_Int32 &&
1413 6 : eDataType != GDT_UInt32 && eDataType != GDT_Float32
1414 : #if ECWSDK_VERSION >= 40
1415 : && eDataType != GDT_Float64
1416 : #endif
1417 5 : && bStrict)
1418 : {
1419 5 : CPLError(CE_Failure, CPLE_NotSupported,
1420 : "JP2ECW driver doesn't support data type %s. ",
1421 : GDALGetDataTypeName(eDataType));
1422 :
1423 5 : return nullptr;
1424 : }
1425 :
1426 31 : if (poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr)
1427 : {
1428 0 : CPLError((bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
1429 : "JP2ECW driver ignores color table. "
1430 : "The source raster band will be considered as grey level.\n"
1431 : "Consider using color table expansion (-expand option in "
1432 : "gdal_translate)");
1433 0 : if (bStrict)
1434 0 : return nullptr;
1435 : }
1436 :
1437 31 : return ECWCreateCopy(pszFilename, poSrcDS, bStrict, papszOptions,
1438 31 : pfnProgress, pProgressData, TRUE);
1439 : }
1440 :
1441 : /************************************************************************/
1442 : /************************************************************************
1443 :
1444 : ECW/JPEG200 Create() Support
1445 : ----------------------------
1446 :
1447 : The remainder of the file is code to implement the Create() method.
1448 : New dataset and raster band classes are defined specifically for the
1449 : purpose of being write-only. In particular, you cannot read back data
1450 : from these datasets, and writing must occur in a pretty specific order.
1451 :
1452 : That is, you need to write all metadata (projection, georef, etc) first
1453 : and then write the image data. All bands data for the first scanline
1454 : should be written followed by all bands for the second scanline and so on.
1455 :
1456 : Creation supports the same virtual subfile names as CreateCopy() supports.
1457 :
1458 : ************************************************************************/
1459 : /************************************************************************/
1460 :
1461 : /************************************************************************/
1462 : /* ==================================================================== */
1463 : /* ECWWriteDataset */
1464 : /* ==================================================================== */
1465 : /************************************************************************/
1466 :
1467 : class ECWWriteRasterBand;
1468 :
1469 : #ifdef OPTIMIZED_FOR_GDALWARP
1470 : class IRasterIORequest
1471 : {
1472 : public:
1473 : GDALRasterBand *poBand;
1474 : int nXOff;
1475 : int nYOff;
1476 : int nXSize;
1477 : int nYSize;
1478 : GByte *pabyData;
1479 : int nBufXSize;
1480 : int nBufYSize;
1481 :
1482 0 : IRasterIORequest(GDALRasterBand *poBandIn, int nXOffIn, int nYOffIn,
1483 : int nXSizeIn, int nYSizeIn, void *pData, int nBufXSizeIn,
1484 : int nBufYSizeIn, GDALDataType eBufType,
1485 : GSpacing nPixelSpace, GSpacing nLineSpace)
1486 0 : : poBand(poBandIn), nXOff(nXOffIn), nYOff(nYOffIn), nXSize(nXSizeIn),
1487 : nYSize(nYSizeIn), pabyData(nullptr), nBufXSize(nBufXSizeIn),
1488 0 : nBufYSize(nBufYSizeIn)
1489 : {
1490 0 : GDALDataType eDataType = poBand->GetRasterDataType();
1491 0 : const int nDataTypeSize = GDALGetDataTypeSizeBytes(eDataType);
1492 0 : pabyData = (GByte *)CPLMalloc(static_cast<size_t>(nBufXSize) *
1493 0 : nBufYSize * nDataTypeSize);
1494 0 : for (int iY = 0; iY < nBufYSize; iY++)
1495 : {
1496 0 : GDALCopyWords((GByte *)pData + iY * nLineSpace, eBufType,
1497 : static_cast<int>(nPixelSpace),
1498 0 : pabyData + static_cast<size_t>(iY) * nBufXSize *
1499 0 : nDataTypeSize,
1500 : eDataType, nDataTypeSize, nBufXSize);
1501 : }
1502 0 : }
1503 :
1504 0 : ~IRasterIORequest()
1505 0 : {
1506 0 : CPLFree(pabyData);
1507 0 : }
1508 : };
1509 : #endif
1510 :
1511 : class ECWWriteDataset final : public GDALDataset
1512 : {
1513 : friend class ECWWriteRasterBand;
1514 :
1515 : char *pszFilename;
1516 :
1517 : int bIsJPEG2000;
1518 : GDALDataType eDataType;
1519 : char **papszOptions;
1520 :
1521 : OGRSpatialReference m_oSRS{};
1522 : GDALGeoTransform m_gt{};
1523 :
1524 : GDALECWCompressor oCompressor;
1525 : int bCrystalized; // TODO: Spelling.
1526 :
1527 : int nLoadedLine;
1528 : GByte *pabyBILBuffer;
1529 :
1530 : int bOutOfOrderWriteOccurred;
1531 : #ifdef OPTIMIZED_FOR_GDALWARP
1532 : int nPrevIRasterIOBand;
1533 : #endif
1534 :
1535 : CPLErr Crystalize(); // TODO: Spelling.
1536 : CPLErr FlushLine();
1537 :
1538 : public:
1539 : ECWWriteDataset(const char *, int, int, int, GDALDataType,
1540 : CSLConstList papszOptions, int);
1541 : ~ECWWriteDataset() override;
1542 :
1543 : CPLErr FlushCache(bool bAtClosing) override;
1544 :
1545 : CPLErr GetGeoTransform(GDALGeoTransform >) const override;
1546 : CPLErr SetGeoTransform(const GDALGeoTransform >) override;
1547 : const OGRSpatialReference *GetSpatialRef() const override;
1548 : CPLErr SetSpatialRef(const OGRSpatialReference *poSRS) override;
1549 :
1550 : #ifdef OPTIMIZED_FOR_GDALWARP
1551 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
1552 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
1553 : GDALDataType eBufType, int nBandCount,
1554 : BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
1555 : GSpacing nLineSpace, GSpacing nBandSpace,
1556 : GDALRasterIOExtraArg *psExtraArg) override;
1557 : #endif
1558 : };
1559 :
1560 : /************************************************************************/
1561 : /* ==================================================================== */
1562 : /* ECWWriteRasterBand */
1563 : /* ==================================================================== */
1564 : /************************************************************************/
1565 :
1566 : class ECWWriteRasterBand final : public GDALRasterBand
1567 : {
1568 : friend class ECWWriteDataset;
1569 :
1570 : // NOTE: poDS may be altered for NITF/JPEG2000 files!
1571 : ECWWriteDataset *poGDS;
1572 :
1573 : GDALColorInterp eInterp;
1574 :
1575 : #ifdef OPTIMIZED_FOR_GDALWARP
1576 : IRasterIORequest *poIORequest;
1577 : #endif
1578 :
1579 : public:
1580 : ECWWriteRasterBand(ECWWriteDataset *, int);
1581 : ~ECWWriteRasterBand() override;
1582 :
1583 6 : CPLErr SetColorInterpretation(GDALColorInterp eInterpIn) override
1584 : {
1585 6 : eInterp = eInterpIn;
1586 6 : if (strlen(GetDescription()) == 0)
1587 3 : SetDescription(ECWGetColorInterpretationName(eInterp, nBand - 1));
1588 6 : return CE_None;
1589 : }
1590 :
1591 6 : GDALColorInterp GetColorInterpretation() override
1592 : {
1593 6 : return eInterp;
1594 : }
1595 :
1596 : CPLErr IReadBlock(int, int, void *) override;
1597 : CPLErr IWriteBlock(int, int, void *) override;
1598 :
1599 : #ifdef OPTIMIZED_FOR_GDALWARP
1600 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
1601 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
1602 : GDALDataType eBufType, GSpacing nPixelSpace,
1603 : GSpacing nLineSpace,
1604 : GDALRasterIOExtraArg *psExtraArg) override;
1605 : #endif
1606 : };
1607 :
1608 : /************************************************************************/
1609 : /* ECWWriteDataset() */
1610 : /************************************************************************/
1611 :
1612 33 : ECWWriteDataset::ECWWriteDataset(const char *pszFilenameIn, int nXSize,
1613 : int nYSize, int nBandCount, GDALDataType eType,
1614 33 : CSLConstList papszOptionsIn, int bIsJPEG2000In)
1615 :
1616 : {
1617 33 : bCrystalized = FALSE;
1618 33 : pabyBILBuffer = nullptr;
1619 33 : nLoadedLine = -1;
1620 :
1621 33 : eAccess = GA_Update;
1622 :
1623 33 : this->bIsJPEG2000 = bIsJPEG2000In;
1624 33 : this->eDataType = eType;
1625 33 : this->papszOptions = CSLDuplicate(papszOptionsIn);
1626 33 : this->pszFilename = CPLStrdup(pszFilenameIn);
1627 :
1628 33 : nRasterXSize = nXSize;
1629 33 : nRasterYSize = nYSize;
1630 :
1631 : // create band objects.
1632 104 : for (int iBand = 1; iBand <= nBandCount; iBand++)
1633 : {
1634 71 : SetBand(iBand, new ECWWriteRasterBand(this, iBand));
1635 : }
1636 :
1637 33 : bOutOfOrderWriteOccurred = FALSE;
1638 : #ifdef OPTIMIZED_FOR_GDALWARP
1639 33 : nPrevIRasterIOBand = -1;
1640 : #endif
1641 33 : }
1642 :
1643 : /************************************************************************/
1644 : /* ~ECWWriteDataset() */
1645 : /************************************************************************/
1646 :
1647 66 : ECWWriteDataset::~ECWWriteDataset()
1648 :
1649 : {
1650 33 : ECWWriteDataset::FlushCache(true);
1651 :
1652 33 : if (bCrystalized)
1653 : {
1654 2 : if (bOutOfOrderWriteOccurred)
1655 : {
1656 : /* Otherwise there's a hang-up in the destruction of the oCompressor
1657 : * object */
1658 0 : while (nLoadedLine < nRasterYSize - 1)
1659 0 : FlushLine();
1660 : }
1661 2 : if (nLoadedLine == nRasterYSize - 1)
1662 2 : FlushLine();
1663 2 : oCompressor.CloseDown();
1664 : }
1665 :
1666 33 : CPLFree(pabyBILBuffer);
1667 33 : CSLDestroy(papszOptions);
1668 33 : CPLFree(pszFilename);
1669 66 : }
1670 :
1671 : /************************************************************************/
1672 : /* FlushCache() */
1673 : /************************************************************************/
1674 :
1675 35 : CPLErr ECWWriteDataset::FlushCache(bool bAtClosing)
1676 :
1677 : {
1678 35 : return BlockBasedFlushCache(bAtClosing);
1679 : }
1680 :
1681 : /************************************************************************/
1682 : /* GetSpatialRef() */
1683 : /************************************************************************/
1684 :
1685 0 : const OGRSpatialReference *ECWWriteDataset::GetSpatialRef() const
1686 : {
1687 0 : return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
1688 : }
1689 :
1690 : /************************************************************************/
1691 : /* GetGeoTransform() */
1692 : /************************************************************************/
1693 :
1694 31 : CPLErr ECWWriteDataset::GetGeoTransform(GDALGeoTransform >) const
1695 :
1696 : {
1697 31 : gt = m_gt;
1698 31 : return CE_None;
1699 : }
1700 :
1701 : /************************************************************************/
1702 : /* SetGeoTransform() */
1703 : /************************************************************************/
1704 :
1705 32 : CPLErr ECWWriteDataset::SetGeoTransform(const GDALGeoTransform >)
1706 :
1707 : {
1708 32 : m_gt = gt;
1709 32 : return CE_None;
1710 : }
1711 :
1712 : /************************************************************************/
1713 : /* SetSpatialRef() */
1714 : /************************************************************************/
1715 :
1716 32 : CPLErr ECWWriteDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
1717 :
1718 : {
1719 32 : m_oSRS.Clear();
1720 32 : if (poSRS)
1721 32 : m_oSRS = *poSRS;
1722 :
1723 32 : return CE_None;
1724 : }
1725 :
1726 : /************************************************************************/
1727 : /* Crystalize() */
1728 : /************************************************************************/
1729 :
1730 2 : CPLErr ECWWriteDataset::Crystalize()
1731 :
1732 : {
1733 2 : const int nWordSize = GDALGetDataTypeSizeBytes(eDataType);
1734 :
1735 : CPLErr eErr;
1736 :
1737 2 : if (bCrystalized)
1738 0 : return CE_None;
1739 :
1740 : const char **paszBandDescriptions =
1741 2 : (const char **)CPLMalloc(nBands * sizeof(char *));
1742 6 : for (int i = 0; i < nBands; i++)
1743 : {
1744 4 : paszBandDescriptions[i] = GetRasterBand(i + 1)->GetDescription();
1745 : }
1746 :
1747 2 : int bRGBColorSpace = ECWIsInputRGBColorSpace(this);
1748 :
1749 4 : eErr = oCompressor.Initialize(pszFilename, papszOptions, nRasterXSize,
1750 : nRasterYSize, nBands, paszBandDescriptions,
1751 2 : bRGBColorSpace, eDataType, &m_oSRS, m_gt, 0,
1752 : nullptr, bIsJPEG2000, FALSE, nullptr);
1753 :
1754 2 : if (eErr == CE_None)
1755 2 : bCrystalized = TRUE;
1756 :
1757 2 : nLoadedLine = -1;
1758 4 : pabyBILBuffer = (GByte *)CPLMalloc(static_cast<size_t>(nWordSize) * nBands *
1759 2 : nRasterXSize);
1760 :
1761 2 : CPLFree(paszBandDescriptions);
1762 :
1763 2 : return eErr;
1764 : }
1765 :
1766 : /************************************************************************/
1767 : /* FlushLine() */
1768 : /************************************************************************/
1769 :
1770 202 : CPLErr ECWWriteDataset::FlushLine()
1771 :
1772 : {
1773 202 : const int nWordSize = GDALGetDataTypeSizeBytes(eDataType);
1774 : CPLErr eErr;
1775 :
1776 : /* -------------------------------------------------------------------- */
1777 : /* Crystallize if not already done. */
1778 : /* -------------------------------------------------------------------- */
1779 202 : if (!bCrystalized)
1780 : {
1781 2 : eErr = Crystalize();
1782 :
1783 2 : if (eErr != CE_None)
1784 0 : return eErr;
1785 : }
1786 :
1787 : /* -------------------------------------------------------------------- */
1788 : /* Write out the currently loaded line. */
1789 : /* -------------------------------------------------------------------- */
1790 202 : if (nLoadedLine != -1)
1791 : {
1792 :
1793 200 : void **papOutputLine = (void **)CPLMalloc(sizeof(void *) * nBands);
1794 600 : for (int i = 0; i < nBands; i++)
1795 400 : papOutputLine[i] =
1796 400 : (void *)(pabyBILBuffer +
1797 400 : static_cast<size_t>(i) * nWordSize * nRasterXSize);
1798 :
1799 200 : eErr = oCompressor.ourWriteLineBIL((UINT16)nBands, papOutputLine);
1800 200 : CPLFree(papOutputLine);
1801 200 : if (eErr != CE_None)
1802 : {
1803 0 : return eErr;
1804 : }
1805 : }
1806 :
1807 : /* -------------------------------------------------------------------- */
1808 : /* Clear the buffer and increment the "current line" indicator. */
1809 : /* -------------------------------------------------------------------- */
1810 202 : memset(pabyBILBuffer, 0,
1811 202 : static_cast<size_t>(nWordSize) * nRasterXSize * nBands);
1812 202 : nLoadedLine++;
1813 :
1814 202 : return CE_None;
1815 : }
1816 :
1817 : #ifdef OPTIMIZED_FOR_GDALWARP
1818 : /************************************************************************/
1819 : /* IRasterIO() */
1820 : /************************************************************************/
1821 :
1822 200 : CPLErr ECWWriteDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
1823 : int nXSize, int nYSize, void *pData,
1824 : int nBufXSize, int nBufYSize,
1825 : GDALDataType eBufType, int nBandCount,
1826 : BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
1827 : GSpacing nLineSpace, GSpacing nBandSpace,
1828 : GDALRasterIOExtraArg *psExtraArg)
1829 : {
1830 200 : ECWWriteRasterBand *po4thBand = nullptr;
1831 200 : IRasterIORequest *poIORequest = nullptr;
1832 :
1833 200 : if (bOutOfOrderWriteOccurred)
1834 0 : return CE_Failure;
1835 :
1836 200 : if (eRWFlag == GF_Write && nBandCount == 3 && nBands == 4)
1837 : {
1838 0 : po4thBand = cpl::down_cast<ECWWriteRasterBand *>(GetRasterBand(4));
1839 0 : poIORequest = po4thBand->poIORequest;
1840 0 : if (poIORequest != nullptr)
1841 : {
1842 0 : if (nXOff != poIORequest->nXOff || nYOff != poIORequest->nYOff ||
1843 0 : nXSize != poIORequest->nXSize ||
1844 0 : nYSize != poIORequest->nYSize ||
1845 0 : nBufXSize != poIORequest->nBufXSize ||
1846 0 : nBufYSize != poIORequest->nBufYSize)
1847 : {
1848 0 : CPLError(CE_Failure, CPLE_AppDefined, "Out of order write");
1849 0 : bOutOfOrderWriteOccurred = TRUE;
1850 0 : return CE_Failure;
1851 : }
1852 : }
1853 : }
1854 :
1855 200 : const int nDataTypeSize = GDALGetDataTypeSizeBytes(eDataType);
1856 200 : if (eRWFlag == GF_Write && nXOff == 0 && nXSize == nRasterXSize &&
1857 200 : nBufXSize == nXSize && nBufYSize == nYSize && eBufType == eDataType &&
1858 100 : (nBandCount == nBands ||
1859 0 : (nBandCount == 3 && poIORequest != nullptr && nBands == 4)) &&
1860 100 : nPixelSpace == nDataTypeSize &&
1861 100 : nLineSpace == nPixelSpace * nRasterXSize)
1862 : {
1863 100 : CPLErr eErr = CE_None;
1864 100 : GByte *pabyData = (GByte *)pData;
1865 200 : for (int iY = 0; iY < nYSize; iY++)
1866 : {
1867 200 : for (int iBand = 0; iBand < nBandCount && eErr == CE_None; iBand++)
1868 : {
1869 100 : eErr = GetRasterBand(panBandMap[iBand])
1870 200 : ->WriteBlock(0, iY + nYOff,
1871 100 : pabyData + iY * nLineSpace +
1872 100 : iBand * nBandSpace);
1873 : }
1874 :
1875 100 : if (poIORequest != nullptr && eErr == CE_None)
1876 : {
1877 0 : eErr = po4thBand->WriteBlock(0, iY + nYOff,
1878 0 : poIORequest->pabyData +
1879 0 : iY * nDataTypeSize * nXSize);
1880 : }
1881 : }
1882 :
1883 100 : if (poIORequest != nullptr)
1884 : {
1885 0 : delete poIORequest;
1886 0 : po4thBand->poIORequest = nullptr;
1887 : }
1888 :
1889 100 : return eErr;
1890 : }
1891 : else
1892 100 : return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
1893 : pData, nBufXSize, nBufYSize, eBufType,
1894 : nBandCount, panBandMap, nPixelSpace,
1895 100 : nLineSpace, nBandSpace, psExtraArg);
1896 : }
1897 : #endif
1898 :
1899 : /************************************************************************/
1900 : /* ==================================================================== */
1901 : /* ECWWriteRasterBand */
1902 : /* ==================================================================== */
1903 : /************************************************************************/
1904 :
1905 : /************************************************************************/
1906 : /* ECWWriteRasterBand() */
1907 : /************************************************************************/
1908 :
1909 71 : ECWWriteRasterBand::ECWWriteRasterBand(ECWWriteDataset *poDSIn, int nBandIn)
1910 :
1911 : {
1912 71 : nBand = nBandIn;
1913 71 : poDS = poDSIn;
1914 71 : poGDS = poDSIn;
1915 71 : nBlockXSize = poDSIn->GetRasterXSize();
1916 71 : nBlockYSize = 1;
1917 71 : eDataType = poDSIn->eDataType;
1918 71 : eInterp = GCI_Undefined;
1919 : #ifdef OPTIMIZED_FOR_GDALWARP
1920 71 : poIORequest = nullptr;
1921 : #endif
1922 71 : }
1923 :
1924 : /************************************************************************/
1925 : /* ~ECWWriteRasterBand() */
1926 : /************************************************************************/
1927 :
1928 142 : ECWWriteRasterBand::~ECWWriteRasterBand()
1929 :
1930 : {
1931 : #ifdef OPTIMIZED_FOR_GDALWARP
1932 71 : delete poIORequest;
1933 : #endif
1934 142 : }
1935 :
1936 : /************************************************************************/
1937 : /* IReadBlock() */
1938 : /************************************************************************/
1939 :
1940 0 : CPLErr ECWWriteRasterBand::IReadBlock(CPL_UNUSED int nBlockX,
1941 : CPL_UNUSED int nBlockY, void *pBuffer)
1942 : {
1943 0 : const int nWordSize = GDALGetDataTypeSizeBytes(eDataType);
1944 :
1945 : // We zero stuff out here, but we can't really read stuff from
1946 : // a write only stream.
1947 :
1948 0 : memset(pBuffer, 0, static_cast<size_t>(nBlockXSize) * nWordSize);
1949 :
1950 0 : return CE_None;
1951 : }
1952 :
1953 : #ifdef OPTIMIZED_FOR_GDALWARP
1954 : /************************************************************************/
1955 : /* IRasterIO() */
1956 : /************************************************************************/
1957 :
1958 300 : CPLErr ECWWriteRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
1959 : int nXSize, int nYSize, void *pData,
1960 : int nBufXSize, int nBufYSize,
1961 : GDALDataType eBufType,
1962 : GSpacing nPixelSpace, GSpacing nLineSpace,
1963 : GDALRasterIOExtraArg *psExtraArg)
1964 : {
1965 300 : if (eRWFlag == GF_Write && nBand == 4 && poGDS->nBands == 4 &&
1966 0 : poGDS->nPrevIRasterIOBand < 0)
1967 : {
1968 : /* Triggered when gdalwarp outputs an alpha band */
1969 : /* It is called before GDALDatasetRasterIO() on the 3 first bands */
1970 0 : if (poIORequest != nullptr)
1971 0 : return CE_Failure;
1972 0 : poIORequest = new IRasterIORequest(this, nXOff, nYOff, nXSize, nYSize,
1973 : pData, nBufXSize, nBufYSize,
1974 0 : eBufType, nPixelSpace, nLineSpace);
1975 0 : return CE_None;
1976 : }
1977 :
1978 300 : poGDS->nPrevIRasterIOBand = nBand;
1979 300 : return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
1980 : pData, nBufXSize, nBufYSize, eBufType,
1981 300 : nPixelSpace, nLineSpace, psExtraArg);
1982 : }
1983 : #endif
1984 :
1985 : /************************************************************************/
1986 : /* IWriteBlock() */
1987 : /************************************************************************/
1988 :
1989 400 : CPLErr ECWWriteRasterBand::IWriteBlock(CPL_UNUSED int nBlockX, int nBlockY,
1990 : void *pBuffer)
1991 : {
1992 400 : const int nWordSize = GDALGetDataTypeSizeBytes(eDataType);
1993 : CPLErr eErr;
1994 :
1995 400 : if (poGDS->bOutOfOrderWriteOccurred)
1996 0 : return CE_Failure;
1997 :
1998 : /* -------------------------------------------------------------------- */
1999 : /* Flush previous line if needed. */
2000 : /* -------------------------------------------------------------------- */
2001 400 : if (nBlockY == poGDS->nLoadedLine + 1)
2002 : {
2003 200 : eErr = poGDS->FlushLine();
2004 200 : if (eErr != CE_None)
2005 0 : return eErr;
2006 : }
2007 :
2008 : /* -------------------------------------------------------------------- */
2009 : /* Blow a gasket if we have been asked to write something out */
2010 : /* of order. */
2011 : /* -------------------------------------------------------------------- */
2012 400 : if (nBlockY != poGDS->nLoadedLine)
2013 : {
2014 0 : CPLError(CE_Failure, CPLE_AppDefined,
2015 : "Apparent attempt to write to ECW non-sequentially.\n"
2016 : "Loaded line is %d, but %d of band %d was written to.",
2017 0 : poGDS->nLoadedLine, nBlockY, nBand);
2018 0 : poGDS->bOutOfOrderWriteOccurred = TRUE;
2019 0 : return CE_Failure;
2020 : }
2021 :
2022 : /* -------------------------------------------------------------------- */
2023 : /* Copy passed data into current line buffer. */
2024 : /* -------------------------------------------------------------------- */
2025 400 : memcpy(poGDS->pabyBILBuffer + (nBand - 1) * nWordSize * nRasterXSize,
2026 400 : pBuffer, nWordSize * nRasterXSize);
2027 :
2028 400 : return CE_None;
2029 : }
2030 :
2031 : /************************************************************************/
2032 : /* ECWCreateJPEG2000() */
2033 : /************************************************************************/
2034 :
2035 51 : GDALDataset *ECWCreateJPEG2000(const char *pszFilename, int nXSize, int nYSize,
2036 : int nBands, GDALDataType eType,
2037 : CSLConstList papszOptions)
2038 :
2039 : {
2040 51 : if (nBands == 0)
2041 : {
2042 18 : CPLError(CE_Failure, CPLE_NotSupported, "0 band not supported");
2043 18 : return nullptr;
2044 : }
2045 33 : ECWInitialize();
2046 :
2047 : return new ECWWriteDataset(pszFilename, nXSize, nYSize, nBands, eType,
2048 33 : papszOptions, TRUE);
2049 : }
2050 :
2051 : /************************************************************************/
2052 : /* ECWCreateECW() */
2053 : /************************************************************************/
2054 :
2055 0 : GDALDataset *ECWCreateECW(const char *pszFilename, int nXSize, int nYSize,
2056 : int nBands, GDALDataType eType,
2057 : CSLConstList papszOptions)
2058 :
2059 : {
2060 0 : if (nBands == 0)
2061 : {
2062 0 : CPLError(CE_Failure, CPLE_NotSupported, "0 band not supported");
2063 0 : return nullptr;
2064 : }
2065 0 : ECWInitialize();
2066 :
2067 : return new ECWWriteDataset(pszFilename, nXSize, nYSize, nBands, eType,
2068 0 : papszOptions, FALSE);
2069 : }
2070 :
2071 : #endif /* def HAVE_COMPRESS */
|