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