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