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 62 : if (pszOption == nullptr &&
831 62 : EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "j2k"))
832 2 : pszOption = "YES";
833 33 : if (pszOption != nullptr)
834 12 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_CODESTREAM_ONLY,
835 6 : CPLTestBool(pszOption));
836 :
837 33 : pszOption = CSLFetchNameValue(papszOptions, "LEVELS");
838 33 : if (pszOption != nullptr)
839 0 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_LEVELS,
840 0 : (UINT32)atoi(pszOption));
841 :
842 33 : pszOption = CSLFetchNameValue(papszOptions, "LAYERS");
843 33 : if (pszOption != nullptr)
844 3 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_LAYERS,
845 3 : (UINT32)atoi(pszOption));
846 :
847 33 : pszOption = CSLFetchNameValue(papszOptions, "PRECINCT_WIDTH");
848 33 : if (pszOption != nullptr)
849 0 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_PRECINCT_WIDTH,
850 0 : (UINT32)atoi(pszOption));
851 :
852 33 : pszOption = CSLFetchNameValue(papszOptions, "PRECINCT_HEIGHT");
853 33 : if (pszOption != nullptr)
854 0 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_PRECINCT_HEIGHT,
855 0 : (UINT32)atoi(pszOption));
856 :
857 33 : pszOption = CSLFetchNameValue(papszOptions, "TILE_WIDTH");
858 33 : if (pszOption != nullptr)
859 0 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_TILE_WIDTH,
860 0 : (UINT32)atoi(pszOption));
861 :
862 33 : pszOption = CSLFetchNameValue(papszOptions, "TILE_HEIGHT");
863 33 : if (pszOption != nullptr)
864 0 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_TILE_HEIGHT,
865 0 : (UINT32)atoi(pszOption));
866 :
867 33 : pszOption = CSLFetchNameValue(papszOptions, "INCLUDE_SOP");
868 33 : if (pszOption != nullptr)
869 0 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_INCLUDE_SOP,
870 0 : CPLTestBool(pszOption));
871 :
872 33 : pszOption = CSLFetchNameValue(papszOptions, "INCLUDE_EPH");
873 33 : if (pszOption != nullptr)
874 0 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_INCLUDE_EPH,
875 0 : CPLTestBool(pszOption));
876 :
877 33 : pszOption = CSLFetchNameValue(papszOptions, "PROGRESSION");
878 33 : if (pszOption != nullptr && EQUAL(pszOption, "LRCP"))
879 0 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_PROGRESSION_LRCP);
880 :
881 33 : else if (pszOption != nullptr && EQUAL(pszOption, "RLCP"))
882 0 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_PROGRESSION_RLCP);
883 :
884 33 : else if (pszOption != nullptr && EQUAL(pszOption, "RPCL"))
885 0 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_PROGRESSION_RPCL);
886 :
887 33 : pszOption = CSLFetchNameValue(papszOptions, "GEODATA_USAGE");
888 33 : if (pszOption == nullptr)
889 : // Default to suppressing ECW SDK geodata, just use our own stuff.
890 33 : SetGeodataUsage(JP2_GEODATA_USE_NONE);
891 0 : else if (EQUAL(pszOption, "NONE"))
892 0 : SetGeodataUsage(JP2_GEODATA_USE_NONE);
893 0 : else if (EQUAL(pszOption, "PCS_ONLY"))
894 0 : SetGeodataUsage(JP2_GEODATA_USE_PCS_ONLY);
895 0 : else if (EQUAL(pszOption, "GML_ONLY"))
896 0 : SetGeodataUsage(JP2_GEODATA_USE_GML_ONLY);
897 0 : else if (EQUAL(pszOption, "PCS_GML"))
898 0 : SetGeodataUsage(JP2_GEODATA_USE_PCS_GML);
899 0 : else if (EQUAL(pszOption, "GML_PCS"))
900 0 : SetGeodataUsage(JP2_GEODATA_USE_GML_PCS);
901 0 : else if (EQUAL(pszOption, "ALL"))
902 0 : SetGeodataUsage(JP2_GEODATA_USE_GML_PCS_WLD);
903 :
904 33 : pszOption = CSLFetchNameValue(papszOptions, "DECOMPRESS_LAYERS");
905 33 : if (pszOption != nullptr)
906 0 : SetParameter(CNCSJP2FileView::JP2_DECOMPRESS_LAYERS,
907 0 : (UINT32)atoi(pszOption));
908 :
909 33 : pszOption = CSLFetchNameValue(papszOptions,
910 : "DECOMPRESS_RECONSTRUCTION_PARAMETER");
911 33 : if (pszOption != nullptr)
912 0 : SetParameter(
913 : CNCSJP2FileView::JPC_DECOMPRESS_RECONSTRUCTION_PARAMETER,
914 0 : (IEEE4)CPLAtof(pszOption));
915 : }
916 :
917 : /* -------------------------------------------------------------------- */
918 : /* Georeferencing. */
919 : /* -------------------------------------------------------------------- */
920 :
921 37 : psClient->fOriginX = 0.0;
922 37 : psClient->fOriginY = psClient->nSizeY;
923 37 : psClient->fCellIncrementX = 1.0;
924 37 : psClient->fCellIncrementY = -1.0;
925 37 : psClient->fCWRotationDegrees = 0.0;
926 :
927 37 : if (padfGeoTransform[2] != 0.0 || padfGeoTransform[4] != 0.0)
928 0 : CPLError(CE_Warning, CPLE_NotSupported,
929 : "Rotational coefficients ignored, georeferencing of\n"
930 : "output ECW file will be incorrect.\n");
931 : else
932 : {
933 37 : psClient->fOriginX = padfGeoTransform[0];
934 37 : psClient->fOriginY = padfGeoTransform[3];
935 37 : psClient->fCellIncrementX = padfGeoTransform[1];
936 37 : psClient->fCellIncrementY = padfGeoTransform[5];
937 : }
938 :
939 : /* -------------------------------------------------------------------- */
940 : /* Projection. */
941 : /* -------------------------------------------------------------------- */
942 : char szProjection[128];
943 : char szDatum[128];
944 : char szUnits[128];
945 :
946 37 : strcpy(szProjection, "RAW");
947 37 : strcpy(szDatum, "RAW");
948 :
949 37 : if (CSLFetchNameValue(papszOptions, "PROJ") != nullptr)
950 : {
951 0 : strncpy(szProjection, CSLFetchNameValue(papszOptions, "PROJ"),
952 : sizeof(szProjection));
953 0 : szProjection[sizeof(szProjection) - 1] = 0;
954 : }
955 :
956 37 : if (CSLFetchNameValue(papszOptions, "DATUM") != nullptr)
957 : {
958 0 : strncpy(szDatum, CSLFetchNameValue(papszOptions, "DATUM"),
959 : sizeof(szDatum));
960 0 : szDatum[sizeof(szDatum) - 1] = 0;
961 0 : if (EQUAL(szProjection, "RAW"))
962 0 : strcpy(szProjection, "GEODETIC");
963 : }
964 :
965 37 : const char *pszUnits = CSLFetchNameValue(papszOptions, "UNITS");
966 37 : if (pszUnits != nullptr)
967 : {
968 0 : psClient->eCellSizeUnits = ECWTranslateToCellSizeUnits(pszUnits);
969 : }
970 :
971 37 : if (EQUAL(szProjection, "RAW") && poSRS != nullptr && !poSRS->IsEmpty())
972 : {
973 24 : ECWTranslateFromWKT(poSRS, szProjection, sizeof(szProjection), szDatum,
974 : sizeof(szDatum), szUnits);
975 24 : psClient->eCellSizeUnits = ECWTranslateToCellSizeUnits(szUnits);
976 : }
977 :
978 37 : NCSFree(psClient->szDatum);
979 37 : psClient->szDatum = NCSStrDup(szDatum);
980 37 : NCSFree(psClient->szProjection);
981 37 : psClient->szProjection = NCSStrDup(szProjection);
982 :
983 37 : CPLDebug("ECW", "Writing with PROJ=%s, DATUM=%s, UNITS=%s", szProjection,
984 : szDatum, ECWTranslateFromCellSizeUnits(psClient->eCellSizeUnits));
985 :
986 : /* -------------------------------------------------------------------- */
987 : /* Setup GML and GeoTIFF information. */
988 : /* -------------------------------------------------------------------- */
989 25 : if ((poSRS != nullptr && !poSRS->IsEmpty()) ||
990 13 : !(padfGeoTransform[0] == 0.0 && padfGeoTransform[1] == 1.0 &&
991 11 : padfGeoTransform[2] == 0.0 && padfGeoTransform[3] == 0.0 &&
992 11 : padfGeoTransform[4] == 0.0 && padfGeoTransform[5] == 1.0) ||
993 74 : nGCPCount > 0 || papszRPCMD != nullptr)
994 : {
995 72 : GDALJP2Metadata oJP2MD;
996 :
997 36 : oJP2MD.SetSpatialRef(poSRS);
998 36 : oJP2MD.SetGeoTransform(padfGeoTransform);
999 36 : oJP2MD.SetGCPs(nGCPCount, pasGCPList);
1000 36 : oJP2MD.bPixelIsPoint = bPixelIsPoint;
1001 36 : oJP2MD.SetRPCMD(papszRPCMD);
1002 :
1003 36 : if (bIsJPEG2000)
1004 : {
1005 32 : if (CPLFetchBool(papszOptions, "WRITE_METADATA", false))
1006 : {
1007 6 : if (!CPLFetchBool(papszOptions, "MAIN_MD_DOMAIN_ONLY", false))
1008 : {
1009 6 : WriteXMLBoxes();
1010 : }
1011 6 : WriteJP2Box(
1012 : GDALJP2Metadata::CreateGDALMultiDomainMetadataXMLBox(
1013 6 : m_poSrcDS, CPLFetchBool(papszOptions,
1014 : "MAIN_MD_DOMAIN_ONLY", false)));
1015 : }
1016 32 : if (CPLFetchBool(papszOptions, "GMLJP2", true))
1017 : {
1018 : const char *pszGMLJP2V2Def =
1019 29 : CSLFetchNameValue(papszOptions, "GMLJP2V2_DEF");
1020 29 : if (pszGMLJP2V2Def != nullptr)
1021 : {
1022 0 : WriteJP2Box(oJP2MD.CreateGMLJP2V2(nXSize, nYSize,
1023 : pszGMLJP2V2Def, poSrcDS));
1024 : }
1025 : else
1026 : {
1027 48 : if (!poSRS || poSRS->IsEmpty() ||
1028 19 : GDALJP2Metadata::IsSRSCompatible(poSRS))
1029 : {
1030 28 : WriteJP2Box(oJP2MD.CreateGMLJP2(nXSize, nYSize));
1031 : }
1032 1 : else if (CSLFetchNameValue(papszOptions, "GMLJP2"))
1033 : {
1034 0 : CPLError(CE_Warning, CPLE_AppDefined,
1035 : "GMLJP2 box was explicitly required but "
1036 : "cannot be written due "
1037 : "to lack of georeferencing and/or unsupported "
1038 : "georeferencing "
1039 : "for GMLJP2");
1040 : }
1041 : else
1042 : {
1043 1 : CPLDebug(
1044 : "JP2ECW",
1045 : "Cannot write GMLJP2 box due to unsupported SRS");
1046 : }
1047 : }
1048 : }
1049 32 : if (CPLFetchBool(papszOptions, "GeoJP2", true))
1050 29 : WriteJP2Box(oJP2MD.CreateJP2GeoTIFF());
1051 38 : if (CPLFetchBool(papszOptions, "WRITE_METADATA", false) &&
1052 6 : !CPLFetchBool(papszOptions, "MAIN_MD_DOMAIN_ONLY", false))
1053 : {
1054 6 : WriteJP2Box(GDALJP2Metadata::CreateXMPBox(m_poSrcDS));
1055 : }
1056 : }
1057 : }
1058 : /* -------------------------------------------------------------------- */
1059 : /* We handle all jpeg2000 files via the VSIIOStream, but ECW */
1060 : /* files cannot be done this way for some reason. */
1061 : /* -------------------------------------------------------------------- */
1062 37 : VSILFILE *fpVSIL = nullptr;
1063 :
1064 37 : if (bIsJPEG2000)
1065 : {
1066 66 : int bSeekable = !(STARTS_WITH(pszFilename, "/vsistdout/") ||
1067 33 : STARTS_WITH(pszFilename, "/vsizip/") ||
1068 33 : STARTS_WITH(pszFilename, "/vsigzip/"));
1069 33 : fpVSIL = VSIFOpenL(pszFilename, (bSeekable) ? "wb+" : "wb");
1070 33 : if (fpVSIL == nullptr)
1071 : {
1072 2 : CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open/create %s.",
1073 : pszFilename);
1074 2 : return CE_Failure;
1075 : }
1076 :
1077 31 : m_OStream->Access(fpVSIL, TRUE, (BOOLEAN)bSeekable, pszFilename, 0, -1);
1078 : }
1079 : else
1080 : {
1081 4 : if (!STARTS_WITH(pszFilename, "/vsi"))
1082 : {
1083 : // Try now to create the file to avoid memory leaks if it is
1084 : // the SDK that fails to do it.
1085 4 : fpVSIL = VSIFOpenL(pszFilename, "wb");
1086 4 : if (fpVSIL == nullptr)
1087 : {
1088 2 : CPLError(CE_Failure, CPLE_OpenFailed,
1089 : "Failed to open/create %s.", pszFilename);
1090 2 : return CE_Failure;
1091 : }
1092 2 : VSIFCloseL(fpVSIL);
1093 2 : VSIUnlink(pszFilename);
1094 2 : fpVSIL = nullptr;
1095 : }
1096 : }
1097 :
1098 : /* -------------------------------------------------------------------- */
1099 : /* Check if we can enable large files. This option should only */
1100 : /* be set when the application is adhering to one of the */
1101 : /* ERMapper options for licensing larger than 500MB input */
1102 : /* files. See Bug 767. This option no longer exists with */
1103 : /* version 4+. */
1104 : /* -------------------------------------------------------------------- */
1105 : #if ECWSDK_VERSION < 40
1106 33 : const char *pszLargeOK = CSLFetchNameValue(papszOptions, "LARGE_OK");
1107 33 : if (pszLargeOK == nullptr)
1108 33 : pszLargeOK = "NO";
1109 :
1110 33 : pszLargeOK = CPLGetConfigOption("ECW_LARGE_OK", pszLargeOK);
1111 :
1112 33 : if (CPLTestBool(pszLargeOK))
1113 : {
1114 0 : CNCSFile::SetKeySize();
1115 0 : CPLDebug("ECW", "Large file generation enabled.");
1116 : }
1117 : #endif /* ECWSDK_VERSION < 40 */
1118 : /* -------------------------------------------------------------------- */
1119 : /* Infer metadata information from source dataset if possible */
1120 : /* -------------------------------------------------------------------- */
1121 : #if ECWSDK_VERSION >= 50
1122 : if (psClient->nFormatVersion > 2)
1123 : {
1124 : if (psClient->pFileMetaData == nullptr)
1125 : {
1126 : NCSEcwInitMetaData(&psClient->pFileMetaData);
1127 : }
1128 : if (m_poSrcDS && m_poSrcDS->GetMetadataItem(
1129 : "FILE_METADATA_ACQUISITION_DATE") != nullptr)
1130 : {
1131 : psClient->pFileMetaData->sAcquisitionDate =
1132 : NCSStrDupT(NCS::CString(m_poSrcDS->GetMetadataItem(
1133 : "FILE_METADATA_ACQUISITION_DATE"))
1134 : .c_str());
1135 : }
1136 :
1137 : if (m_poSrcDS &&
1138 : m_poSrcDS->GetMetadataItem(
1139 : "FILE_METADATA_ACQUISITION_SENSOR_NAME") != nullptr)
1140 : {
1141 : psClient->pFileMetaData->sAcquisitionSensorName = NCSStrDupT(
1142 : NCS::CString(m_poSrcDS->GetMetadataItem(
1143 : "FILE_METADATA_ACQUISITION_SENSOR_NAME"))
1144 : .c_str());
1145 : }
1146 : if (m_poSrcDS &&
1147 : m_poSrcDS->GetMetadataItem("FILE_METADATA_ADDRESS") != nullptr)
1148 : {
1149 : psClient->pFileMetaData->sAddress =
1150 : NCSStrDupT(NCS::CString(m_poSrcDS->GetMetadataItem(
1151 : "FILE_METADATA_ADDRESS"))
1152 : .c_str());
1153 : }
1154 : if (m_poSrcDS &&
1155 : m_poSrcDS->GetMetadataItem("FILE_METADATA_AUTHOR") != nullptr)
1156 : {
1157 : psClient->pFileMetaData->sAuthor = NCSStrDupT(
1158 : NCS::CString(m_poSrcDS->GetMetadataItem("FILE_METADATA_AUTHOR"))
1159 : .c_str());
1160 : }
1161 : if (m_poSrcDS && m_poSrcDS->GetMetadataItem(
1162 : "FILE_METADATA_CLASSIFICATION") != nullptr)
1163 : {
1164 : psClient->pFileMetaData->sClassification =
1165 : NCSStrDupT(NCS::CString(m_poSrcDS->GetMetadataItem(
1166 : "FILE_METADATA_CLASSIFICATION"))
1167 : .c_str());
1168 : }
1169 : if (pszECWCompany != nullptr &&
1170 : CPLTestBool(CPLGetConfigOption("GDAL_ECW_WRITE_COMPANY", "YES")))
1171 : {
1172 : psClient->pFileMetaData->sCompany =
1173 : NCSStrDupT(NCS::CString(pszECWCompany).c_str());
1174 : }
1175 : CPLString osCompressionSoftware = GetCompressionSoftwareName();
1176 : if (!osCompressionSoftware.empty())
1177 : {
1178 : psClient->pFileMetaData->sCompressionSoftware =
1179 : NCSStrDupT(NCS::CString(osCompressionSoftware.c_str()).c_str());
1180 : }
1181 : if (m_poSrcDS &&
1182 : m_poSrcDS->GetMetadataItem("FILE_METADATA_COPYRIGHT") != nullptr)
1183 : {
1184 : psClient->pFileMetaData->sCopyright =
1185 : NCSStrDupT(NCS::CString(m_poSrcDS->GetMetadataItem(
1186 : "FILE_METADATA_COPYRIGHT"))
1187 : .c_str());
1188 : }
1189 : if (m_poSrcDS &&
1190 : m_poSrcDS->GetMetadataItem("FILE_METADATA_EMAIL") != nullptr)
1191 : {
1192 : psClient->pFileMetaData->sEmail = NCSStrDupT(
1193 : NCS::CString(m_poSrcDS->GetMetadataItem("FILE_METADATA_EMAIL"))
1194 : .c_str());
1195 : }
1196 : if (m_poSrcDS &&
1197 : m_poSrcDS->GetMetadataItem("FILE_METADATA_TELEPHONE") != nullptr)
1198 : {
1199 : psClient->pFileMetaData->sTelephone =
1200 : NCSStrDupT(NCS::CString(m_poSrcDS->GetMetadataItem(
1201 : "FILE_METADATA_TELEPHONE"))
1202 : .c_str());
1203 : }
1204 : }
1205 : #endif
1206 : /* -------------------------------------------------------------------- */
1207 : /* Set the file info. */
1208 : /* -------------------------------------------------------------------- */
1209 66 : CNCSError oError = SetFileInfo(sFileInfo);
1210 :
1211 33 : if (oError.GetErrorNumber() == NCS_SUCCESS)
1212 : {
1213 33 : if (fpVSIL == nullptr)
1214 : {
1215 : #if ECWSDK_VERSION >= 40 && defined(_WIN32)
1216 : if (CPLTestBool(CPLGetConfigOption("GDAL_FILENAME_IS_UTF8", "YES")))
1217 : {
1218 : wchar_t *pwszFilename =
1219 : CPLRecodeToWChar(pszFilename, CPL_ENC_UTF8, CPL_ENC_UCS2);
1220 : oError = GetCNCSError(Open(pwszFilename, false, true));
1221 : CPLFree(pwszFilename);
1222 : }
1223 : else
1224 : #endif
1225 : {
1226 2 : oError = GetCNCSError(Open((char *)pszFilename, false, true));
1227 : }
1228 : }
1229 : else
1230 : {
1231 : #if ECWSDK_VERSION >= 55
1232 : oError = CNCSJP2FileView::Open(m_OStream);
1233 : #else
1234 31 : oError = CNCSJP2FileView::Open(m_OStream.get());
1235 : #endif
1236 : }
1237 : }
1238 :
1239 33 : if (oError.GetErrorNumber() == NCS_SUCCESS)
1240 33 : return CE_None;
1241 0 : else if (oError.GetErrorNumber() == NCS_INPUT_SIZE_EXCEEDED)
1242 : {
1243 0 : CPLError(CE_Failure, CPLE_AppDefined,
1244 : "ECW SDK compress limit exceeded.");
1245 0 : return CE_Failure;
1246 : }
1247 : else
1248 : {
1249 0 : ECWReportError(oError);
1250 :
1251 0 : return CE_Failure;
1252 : }
1253 : }
1254 :
1255 : /************************************************************************/
1256 : /* ECWIsInputRGBColorSpace() */
1257 : /************************************************************************/
1258 :
1259 37 : static int ECWIsInputRGBColorSpace(GDALDataset *poSrcDS)
1260 : {
1261 37 : int nBands = poSrcDS->GetRasterCount();
1262 :
1263 : /* -------------------------------------------------------------------- */
1264 : /* Is the input RGB or RGBA? */
1265 : /* -------------------------------------------------------------------- */
1266 37 : int bRGBColorSpace = FALSE;
1267 37 : int bRGB = FALSE;
1268 37 : if (nBands >= 3)
1269 : {
1270 8 : bRGB = (poSrcDS->GetRasterBand(1)->GetColorInterpretation() ==
1271 : GCI_RedBand);
1272 8 : bRGB &= (poSrcDS->GetRasterBand(2)->GetColorInterpretation() ==
1273 : GCI_GreenBand);
1274 8 : bRGB &= (poSrcDS->GetRasterBand(3)->GetColorInterpretation() ==
1275 : GCI_BlueBand);
1276 : }
1277 37 : if (nBands == 3)
1278 : {
1279 6 : bRGBColorSpace = bRGB;
1280 : }
1281 31 : else if (nBands == 4 && bRGB)
1282 : {
1283 0 : bRGBColorSpace = (poSrcDS->GetRasterBand(4)->GetColorInterpretation() ==
1284 : GCI_AlphaBand);
1285 : }
1286 :
1287 37 : return bRGBColorSpace;
1288 : }
1289 :
1290 : /************************************************************************/
1291 : /* ECWCreateCopy() */
1292 : /************************************************************************/
1293 :
1294 35 : static GDALDataset *ECWCreateCopy(const char *pszFilename, GDALDataset *poSrcDS,
1295 : int bStrict, char **papszOptions,
1296 : GDALProgressFunc pfnProgress,
1297 : void *pProgressData, int bIsJPEG2000)
1298 :
1299 : {
1300 35 : ECWInitialize();
1301 :
1302 : /* -------------------------------------------------------------------- */
1303 : /* Get various values from the source dataset. */
1304 : /* -------------------------------------------------------------------- */
1305 35 : int nBands = poSrcDS->GetRasterCount();
1306 35 : int nXSize = poSrcDS->GetRasterXSize();
1307 35 : int nYSize = poSrcDS->GetRasterYSize();
1308 :
1309 35 : if (nBands == 0)
1310 : {
1311 0 : CPLError(
1312 : CE_Failure, CPLE_NotSupported,
1313 : "ECW driver does not support source dataset with zero band.\n");
1314 0 : return nullptr;
1315 : }
1316 :
1317 35 : GDALDataType eType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
1318 :
1319 35 : const OGRSpatialReference *poSRS = poSrcDS->GetSpatialRef();
1320 35 : double adfGeoTransform[6] = {0, 1, 0, 0, 0, 1};
1321 : ;
1322 :
1323 35 : poSrcDS->GetGeoTransform(adfGeoTransform);
1324 :
1325 35 : if (poSrcDS->GetGCPCount() > 0)
1326 1 : poSRS = poSrcDS->GetGCPSpatialRef();
1327 :
1328 : /* --------------------------------------------------------------------
1329 : */
1330 : /* For ECW, confirm the datatype is 8bit (or uint16 for ECW v3) */
1331 : /* --------------------------------------------------------------------
1332 : */
1333 : #if ECWSDK_VERSION >= 50
1334 : bool bECWV3 = false;
1335 : if (bIsJPEG2000 == FALSE)
1336 : {
1337 : const char *pszOption =
1338 : CSLFetchNameValue(papszOptions, "ECW_FORMAT_VERSION");
1339 : if (pszOption != nullptr)
1340 : {
1341 : bECWV3 = (3 == atoi(pszOption));
1342 : }
1343 : }
1344 : #endif
1345 35 : if (!(eType == GDT_Byte ||
1346 : #if ECWSDK_VERSION >= 50
1347 : (bECWV3 && eType == GDT_UInt16) ||
1348 : #endif
1349 : bIsJPEG2000))
1350 : {
1351 0 : if (bStrict)
1352 : {
1353 0 : CPLError(
1354 : CE_Failure, CPLE_AppDefined,
1355 : "Attempt to create ECW file with pixel data type %s failed.\n"
1356 : "Only Byte data type supported for ECW version 2 files."
1357 : #if ECWSDK_VERSION >= 50
1358 : " ECW version 3 files supports UInt16 as well."
1359 : " Specify ECW_FORMAT_VERSION=3 creation option to write "
1360 : "version 3 file. \n"
1361 : #else
1362 : ". \n"
1363 : #endif
1364 : ,
1365 : GDALGetDataTypeName(eType));
1366 : }
1367 : else
1368 : {
1369 : #if ECWSDK_VERSION >= 50
1370 : if (eType == GDT_UInt16)
1371 : {
1372 : CPLError(CE_Warning, CPLE_AppDefined,
1373 : "ECW version 2 does not support UInt16 data type, "
1374 : "truncating to Byte."
1375 : " Consider specifying ECW_FORMAT_VERSION=3 for full "
1376 : "UInt16 support available in ECW version 3. \n");
1377 : }
1378 : else
1379 : #endif
1380 0 : CPLError(CE_Warning, CPLE_AppDefined,
1381 : "ECW v2 does not support data type, ignoring request "
1382 : "for %s. \n",
1383 : GDALGetDataTypeName(eType));
1384 :
1385 0 : eType = GDT_Byte;
1386 : }
1387 : }
1388 :
1389 : /* -------------------------------------------------------------------- */
1390 : /* Is the input RGB or RGBA? */
1391 : /* -------------------------------------------------------------------- */
1392 35 : int bRGBColorSpace = ECWIsInputRGBColorSpace(poSrcDS);
1393 :
1394 : /* -------------------------------------------------------------------- */
1395 : /* Setup the compressor. */
1396 : /* -------------------------------------------------------------------- */
1397 70 : GDALECWCompressor oCompressor;
1398 :
1399 35 : oCompressor.pfnProgress = pfnProgress;
1400 35 : oCompressor.pProgressData = pProgressData;
1401 35 : oCompressor.m_poSrcDS = poSrcDS;
1402 :
1403 70 : CPLStringList aosBandDescriptions;
1404 90 : for (int i = 0; i < nBands; i++)
1405 : {
1406 : /* Make a copy since ECWGetColorInterpretationName() can return a string
1407 : * generated */
1408 : /* by CPLSPrintf(), which has just a few rotating entries. */
1409 : aosBandDescriptions.AddString(ECWGetColorInterpretationName(
1410 55 : poSrcDS->GetRasterBand(i + 1)->GetColorInterpretation(), i));
1411 : }
1412 :
1413 35 : const char *pszAreaOrPoint = poSrcDS->GetMetadataItem(GDALMD_AREA_OR_POINT);
1414 35 : int bPixelIsPoint =
1415 35 : pszAreaOrPoint != nullptr && EQUAL(pszAreaOrPoint, GDALMD_AOP_POINT);
1416 :
1417 35 : if (oCompressor.Initialize(pszFilename, papszOptions, nXSize, nYSize,
1418 35 : nBands, aosBandDescriptions.List(),
1419 : bRGBColorSpace, eType, poSRS, adfGeoTransform,
1420 35 : poSrcDS->GetGCPCount(), poSrcDS->GetGCPs(),
1421 : bIsJPEG2000, bPixelIsPoint,
1422 70 : poSrcDS->GetMetadata("RPC"), poSrcDS) != CE_None)
1423 : {
1424 4 : return nullptr;
1425 : }
1426 :
1427 : /* -------------------------------------------------------------------- */
1428 : /* Start the compression. */
1429 : /* -------------------------------------------------------------------- */
1430 :
1431 31 : if (!pfnProgress(0.0, nullptr, pProgressData))
1432 0 : return nullptr;
1433 :
1434 62 : CNCSError oErr = oCompressor.Write();
1435 :
1436 31 : if (oErr.GetErrorNumber() != NCS_SUCCESS)
1437 : {
1438 0 : ECWReportError(oErr);
1439 0 : return nullptr;
1440 : }
1441 :
1442 : /* -------------------------------------------------------------------- */
1443 : /* Cleanup, and return read-only handle. */
1444 : /* -------------------------------------------------------------------- */
1445 31 : oCompressor.CloseDown();
1446 31 : pfnProgress(1.001, nullptr, pProgressData);
1447 :
1448 : /* -------------------------------------------------------------------- */
1449 : /* Re-open dataset, and copy any auxiliary pam information. */
1450 : /* -------------------------------------------------------------------- */
1451 31 : GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
1452 31 : GDALPamDataset *poDS = nullptr;
1453 :
1454 31 : if (bIsJPEG2000)
1455 29 : poDS = (GDALPamDataset *)ECWDatasetOpenJPEG2000(&oOpenInfo);
1456 : else
1457 2 : poDS = (GDALPamDataset *)ECWDataset::OpenECW(&oOpenInfo);
1458 :
1459 31 : if (poDS)
1460 : {
1461 : #if ECWSDK_VERSION >= 50
1462 : for (int i = 1; i <= poSrcDS->GetRasterCount(); i++)
1463 : {
1464 : double dMin, dMax, dMean, dStdDev;
1465 : if (poSrcDS->GetRasterBand(i)->GetStatistics(
1466 : FALSE, FALSE, &dMin, &dMax, &dMean, &dStdDev) == CE_None)
1467 : {
1468 : poDS->GetRasterBand(i)->SetStatistics(dMin, dMax, dMean,
1469 : dStdDev);
1470 : }
1471 : double dHistMin, dHistMax;
1472 : int nBuckets;
1473 : GUIntBig *pHistogram = nullptr;
1474 : if (poSrcDS->GetRasterBand(i)->GetDefaultHistogram(
1475 : &dHistMin, &dHistMax, &nBuckets, &pHistogram, FALSE,
1476 : nullptr, nullptr) == CE_None)
1477 : {
1478 : poDS->GetRasterBand(i)->SetDefaultHistogram(
1479 : dHistMin, dHistMax, nBuckets, pHistogram);
1480 : VSIFree(pHistogram);
1481 : }
1482 : }
1483 : #endif
1484 :
1485 31 : ((ECWDataset *)poDS)->SetPreventCopyingSomeMetadata(TRUE);
1486 31 : int nFlags = GCIF_PAM_DEFAULT;
1487 31 : if (bIsJPEG2000 && !CPLFetchBool(papszOptions, "WRITE_METADATA", false))
1488 23 : nFlags &= ~GCIF_METADATA;
1489 31 : poDS->CloneInfo(poSrcDS, nFlags);
1490 31 : ((ECWDataset *)poDS)->SetPreventCopyingSomeMetadata(FALSE);
1491 : }
1492 :
1493 31 : return poDS;
1494 : }
1495 :
1496 : /************************************************************************/
1497 : /* ECWCreateCopyECW() */
1498 : /************************************************************************/
1499 :
1500 20 : GDALDataset *ECWCreateCopyECW(const char *pszFilename, GDALDataset *poSrcDS,
1501 : int bStrict, char **papszOptions,
1502 : GDALProgressFunc pfnProgress, void *pProgressData)
1503 :
1504 : {
1505 20 : int nBands = poSrcDS->GetRasterCount();
1506 20 : if (nBands == 0)
1507 : {
1508 1 : CPLError(
1509 : CE_Failure, CPLE_NotSupported,
1510 : "ECW driver does not support source dataset with zero band.\n");
1511 1 : return nullptr;
1512 : }
1513 :
1514 19 : if (!EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "ecw"))
1515 : {
1516 0 : CPLError(CE_Failure, CPLE_AppDefined,
1517 : "ECW driver does not support creating ECW files\n"
1518 : "with an extension other than .ecw");
1519 0 : return nullptr;
1520 : }
1521 :
1522 : #if ECWSDK_VERSION >= 50
1523 : bool bECWV3 = false;
1524 : const char *pszOption =
1525 : CSLFetchNameValue(papszOptions, "ECW_FORMAT_VERSION");
1526 : if (pszOption != nullptr)
1527 : {
1528 : bECWV3 = (3 == atoi(pszOption));
1529 : }
1530 :
1531 : #endif
1532 :
1533 19 : GDALDataType eDataType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
1534 19 : if (eDataType != GDT_Byte
1535 : #if ECWSDK_VERSION >= 50
1536 : && !(bECWV3 && (eDataType == GDT_UInt16))
1537 : #endif
1538 10 : && bStrict)
1539 : {
1540 : #if ECWSDK_VERSION >= 50
1541 : if (eDataType == GDT_UInt16)
1542 : {
1543 : CPLError(CE_Failure, CPLE_NotSupported,
1544 : "ECW v2 does not support UInt16 data type. Consider "
1545 : " specifying ECW_FORMAT_VERSION=3 for full UInt16 support "
1546 : "available in ECW v3. \n");
1547 : }
1548 : else
1549 : #endif
1550 : {
1551 10 : CPLError(CE_Failure, CPLE_NotSupported,
1552 : "ECW driver doesn't support data type %s. "
1553 : "Only unsigned eight "
1554 : #if ECWSDK_VERSION >= 50
1555 : "or sixteen "
1556 : #endif
1557 : "bit bands supported. \n",
1558 : GDALGetDataTypeName(eDataType));
1559 : }
1560 :
1561 10 : return nullptr;
1562 : }
1563 :
1564 9 : if (poSrcDS->GetRasterXSize() < 128 || poSrcDS->GetRasterYSize() < 128)
1565 : {
1566 5 : CPLError(CE_Failure, CPLE_NotSupported,
1567 : "ECW driver requires image to be at least 128x128,\n"
1568 : "the source image is %dx%d.\n",
1569 : poSrcDS->GetRasterXSize(), poSrcDS->GetRasterYSize());
1570 :
1571 5 : return nullptr;
1572 : }
1573 :
1574 4 : if (poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr)
1575 : {
1576 0 : CPLError((bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
1577 : "ECW driver ignores color table. "
1578 : "The source raster band will be considered as grey level.\n"
1579 : "Consider using color table expansion (-expand option in "
1580 : "gdal_translate)\n");
1581 0 : if (bStrict)
1582 0 : return nullptr;
1583 : }
1584 :
1585 4 : return ECWCreateCopy(pszFilename, poSrcDS, bStrict, papszOptions,
1586 4 : pfnProgress, pProgressData, FALSE);
1587 : }
1588 :
1589 : /************************************************************************/
1590 : /* ECWCreateCopyJPEG2000() */
1591 : /************************************************************************/
1592 :
1593 37 : GDALDataset *ECWCreateCopyJPEG2000(const char *pszFilename,
1594 : GDALDataset *poSrcDS, int bStrict,
1595 : char **papszOptions,
1596 : GDALProgressFunc pfnProgress,
1597 : void *pProgressData)
1598 :
1599 : {
1600 37 : int nBands = poSrcDS->GetRasterCount();
1601 37 : if (nBands == 0)
1602 : {
1603 1 : CPLError(
1604 : CE_Failure, CPLE_NotSupported,
1605 : "JP2ECW driver does not support source dataset with zero band.\n");
1606 1 : return nullptr;
1607 : }
1608 :
1609 36 : if (EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "ecw"))
1610 : {
1611 0 : CPLError(CE_Failure, CPLE_AppDefined,
1612 : "JP2ECW driver does not support creating JPEG2000 files\n"
1613 : "with a .ecw extension. Please use anything else.");
1614 0 : return nullptr;
1615 : }
1616 :
1617 36 : GDALDataType eDataType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
1618 36 : if (eDataType != GDT_Byte && eDataType != GDT_Int16 &&
1619 8 : eDataType != GDT_UInt16 && eDataType != GDT_Int32 &&
1620 6 : eDataType != GDT_UInt32 && eDataType != GDT_Float32
1621 : #if ECWSDK_VERSION >= 40
1622 : && eDataType != GDT_Float64
1623 : #endif
1624 5 : && bStrict)
1625 : {
1626 5 : CPLError(CE_Failure, CPLE_NotSupported,
1627 : "JP2ECW driver doesn't support data type %s. ",
1628 : GDALGetDataTypeName(eDataType));
1629 :
1630 5 : return nullptr;
1631 : }
1632 :
1633 31 : if (poSrcDS->GetRasterBand(1)->GetColorTable() != nullptr)
1634 : {
1635 0 : CPLError((bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
1636 : "JP2ECW driver ignores color table. "
1637 : "The source raster band will be considered as grey level.\n"
1638 : "Consider using color table expansion (-expand option in "
1639 : "gdal_translate)\n");
1640 0 : if (bStrict)
1641 0 : return nullptr;
1642 : }
1643 :
1644 31 : return ECWCreateCopy(pszFilename, poSrcDS, bStrict, papszOptions,
1645 31 : pfnProgress, pProgressData, TRUE);
1646 : }
1647 :
1648 : /************************************************************************/
1649 : /************************************************************************
1650 :
1651 : ECW/JPEG200 Create() Support
1652 : ----------------------------
1653 :
1654 : The remainder of the file is code to implement the Create() method.
1655 : New dataset and raster band classes are defined specifically for the
1656 : purpose of being write-only. In particular, you cannot read back data
1657 : from these datasets, and writing must occur in a pretty specific order.
1658 :
1659 : That is, you need to write all metadata (projection, georef, etc) first
1660 : and then write the image data. All bands data for the first scanline
1661 : should be written followed by all bands for the second scanline and so on.
1662 :
1663 : Creation supports the same virtual subfile names as CreateCopy() supports.
1664 :
1665 : ************************************************************************/
1666 : /************************************************************************/
1667 :
1668 : /************************************************************************/
1669 : /* ==================================================================== */
1670 : /* ECWWriteDataset */
1671 : /* ==================================================================== */
1672 : /************************************************************************/
1673 :
1674 : class ECWWriteRasterBand;
1675 :
1676 : #ifdef OPTIMIZED_FOR_GDALWARP
1677 : class IRasterIORequest
1678 : {
1679 : public:
1680 : GDALRasterBand *poBand;
1681 : int nXOff;
1682 : int nYOff;
1683 : int nXSize;
1684 : int nYSize;
1685 : GByte *pabyData;
1686 : int nBufXSize;
1687 : int nBufYSize;
1688 :
1689 0 : IRasterIORequest(GDALRasterBand *poBandIn, int nXOffIn, int nYOffIn,
1690 : int nXSizeIn, int nYSizeIn, void *pData, int nBufXSizeIn,
1691 : int nBufYSizeIn, GDALDataType eBufType,
1692 : GSpacing nPixelSpace, GSpacing nLineSpace)
1693 0 : : poBand(poBandIn), nXOff(nXOffIn), nYOff(nYOffIn), nXSize(nXSizeIn),
1694 : nYSize(nYSizeIn), pabyData(nullptr), nBufXSize(nBufXSizeIn),
1695 0 : nBufYSize(nBufYSizeIn)
1696 : {
1697 0 : GDALDataType eDataType = poBand->GetRasterDataType();
1698 0 : int nDataTypeSize = GDALGetDataTypeSize(eDataType) / 8;
1699 0 : pabyData = (GByte *)CPLMalloc(nBufXSize * nBufYSize * nDataTypeSize);
1700 0 : for (int iY = 0; iY < nBufYSize; iY++)
1701 : {
1702 0 : GDALCopyWords((GByte *)pData + iY * nLineSpace, eBufType,
1703 : static_cast<int>(nPixelSpace),
1704 0 : pabyData + iY * nBufXSize * nDataTypeSize, eDataType,
1705 : nDataTypeSize, nBufXSize);
1706 : }
1707 0 : }
1708 :
1709 0 : ~IRasterIORequest()
1710 0 : {
1711 0 : CPLFree(pabyData);
1712 0 : }
1713 : };
1714 : #endif
1715 :
1716 : class ECWWriteDataset final : public GDALDataset
1717 : {
1718 : friend class ECWWriteRasterBand;
1719 :
1720 : char *pszFilename;
1721 :
1722 : int bIsJPEG2000;
1723 : GDALDataType eDataType;
1724 : char **papszOptions;
1725 :
1726 : OGRSpatialReference m_oSRS{};
1727 : double adfGeoTransform[6];
1728 :
1729 : GDALECWCompressor oCompressor;
1730 : int bCrystalized; // TODO: Spelling.
1731 :
1732 : int nLoadedLine;
1733 : GByte *pabyBILBuffer;
1734 :
1735 : int bOutOfOrderWriteOccurred;
1736 : #ifdef OPTIMIZED_FOR_GDALWARP
1737 : int nPrevIRasterIOBand;
1738 : #endif
1739 :
1740 : CPLErr Crystalize(); // TODO: Spelling.
1741 : CPLErr FlushLine();
1742 :
1743 : public:
1744 : ECWWriteDataset(const char *, int, int, int, GDALDataType,
1745 : char **papszOptions, int);
1746 : ~ECWWriteDataset();
1747 :
1748 : virtual CPLErr FlushCache(bool bAtClosing) override;
1749 :
1750 : virtual CPLErr GetGeoTransform(double *) override;
1751 : virtual CPLErr SetGeoTransform(double *) override;
1752 : const OGRSpatialReference *GetSpatialRef() const override;
1753 : CPLErr SetSpatialRef(const OGRSpatialReference *poSRS) override;
1754 :
1755 : #ifdef OPTIMIZED_FOR_GDALWARP
1756 : virtual CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
1757 : int nXSize, int nYSize, void *pData, int nBufXSize,
1758 : int nBufYSize, GDALDataType eBufType,
1759 : int nBandCount, BANDMAP_TYPE panBandMap,
1760 : GSpacing nPixelSpace, GSpacing nLineSpace,
1761 : GSpacing nBandSpace,
1762 : GDALRasterIOExtraArg *psExtraArg) override;
1763 : #endif
1764 : };
1765 :
1766 : /************************************************************************/
1767 : /* ==================================================================== */
1768 : /* ECWWriteRasterBand */
1769 : /* ==================================================================== */
1770 : /************************************************************************/
1771 :
1772 : class ECWWriteRasterBand final : public GDALRasterBand
1773 : {
1774 : friend class ECWWriteDataset;
1775 :
1776 : // NOTE: poDS may be altered for NITF/JPEG2000 files!
1777 : ECWWriteDataset *poGDS;
1778 :
1779 : GDALColorInterp eInterp;
1780 :
1781 : #ifdef OPTIMIZED_FOR_GDALWARP
1782 : IRasterIORequest *poIORequest;
1783 : #endif
1784 :
1785 : public:
1786 : ECWWriteRasterBand(ECWWriteDataset *, int);
1787 : ~ECWWriteRasterBand();
1788 :
1789 6 : virtual CPLErr SetColorInterpretation(GDALColorInterp eInterpIn) override
1790 : {
1791 6 : eInterp = eInterpIn;
1792 6 : if (strlen(GetDescription()) == 0)
1793 3 : SetDescription(ECWGetColorInterpretationName(eInterp, nBand - 1));
1794 6 : return CE_None;
1795 : }
1796 :
1797 6 : virtual GDALColorInterp GetColorInterpretation() override
1798 : {
1799 6 : return eInterp;
1800 : }
1801 :
1802 : virtual CPLErr IReadBlock(int, int, void *) override;
1803 : virtual CPLErr IWriteBlock(int, int, void *) override;
1804 :
1805 : #ifdef OPTIMIZED_FOR_GDALWARP
1806 : virtual CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
1807 : int nXSize, int nYSize, void *pData, int nBufXSize,
1808 : int nBufYSize, GDALDataType eBufType,
1809 : GSpacing nPixelSpace, GSpacing nLineSpace,
1810 : GDALRasterIOExtraArg *psExtraArg) override;
1811 : #endif
1812 : };
1813 :
1814 : /************************************************************************/
1815 : /* ECWWriteDataset() */
1816 : /************************************************************************/
1817 :
1818 33 : ECWWriteDataset::ECWWriteDataset(const char *pszFilenameIn, int nXSize,
1819 : int nYSize, int nBandCount, GDALDataType eType,
1820 33 : char **papszOptionsIn, int bIsJPEG2000In)
1821 :
1822 : {
1823 33 : bCrystalized = FALSE;
1824 33 : pabyBILBuffer = nullptr;
1825 33 : nLoadedLine = -1;
1826 :
1827 33 : eAccess = GA_Update;
1828 :
1829 33 : this->bIsJPEG2000 = bIsJPEG2000In;
1830 33 : this->eDataType = eType;
1831 33 : this->papszOptions = CSLDuplicate(papszOptionsIn);
1832 33 : this->pszFilename = CPLStrdup(pszFilenameIn);
1833 :
1834 33 : nRasterXSize = nXSize;
1835 33 : nRasterYSize = nYSize;
1836 :
1837 33 : adfGeoTransform[0] = 0.0;
1838 33 : adfGeoTransform[1] = 1.0;
1839 33 : adfGeoTransform[2] = 0.0;
1840 33 : adfGeoTransform[3] = 0.0;
1841 33 : adfGeoTransform[4] = 0.0;
1842 33 : adfGeoTransform[5] = 1.0;
1843 :
1844 : // create band objects.
1845 104 : for (int iBand = 1; iBand <= nBandCount; iBand++)
1846 : {
1847 71 : SetBand(iBand, new ECWWriteRasterBand(this, iBand));
1848 : }
1849 :
1850 33 : bOutOfOrderWriteOccurred = FALSE;
1851 : #ifdef OPTIMIZED_FOR_GDALWARP
1852 33 : nPrevIRasterIOBand = -1;
1853 : #endif
1854 33 : }
1855 :
1856 : /************************************************************************/
1857 : /* ~ECWWriteDataset() */
1858 : /************************************************************************/
1859 :
1860 66 : ECWWriteDataset::~ECWWriteDataset()
1861 :
1862 : {
1863 33 : ECWWriteDataset::FlushCache(true);
1864 :
1865 33 : if (bCrystalized)
1866 : {
1867 2 : if (bOutOfOrderWriteOccurred)
1868 : {
1869 : /* Otherwise there's a hang-up in the destruction of the oCompressor
1870 : * object */
1871 0 : while (nLoadedLine < nRasterYSize - 1)
1872 0 : FlushLine();
1873 : }
1874 2 : if (nLoadedLine == nRasterYSize - 1)
1875 2 : FlushLine();
1876 2 : oCompressor.CloseDown();
1877 : }
1878 :
1879 33 : CPLFree(pabyBILBuffer);
1880 33 : CSLDestroy(papszOptions);
1881 33 : CPLFree(pszFilename);
1882 66 : }
1883 :
1884 : /************************************************************************/
1885 : /* FlushCache() */
1886 : /************************************************************************/
1887 :
1888 35 : CPLErr ECWWriteDataset::FlushCache(bool bAtClosing)
1889 :
1890 : {
1891 35 : return BlockBasedFlushCache(bAtClosing);
1892 : }
1893 :
1894 : /************************************************************************/
1895 : /* GetSpatialRef() */
1896 : /************************************************************************/
1897 :
1898 0 : const OGRSpatialReference *ECWWriteDataset::GetSpatialRef() const
1899 : {
1900 0 : return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
1901 : }
1902 :
1903 : /************************************************************************/
1904 : /* GetGeoTransform() */
1905 : /************************************************************************/
1906 :
1907 31 : CPLErr ECWWriteDataset::GetGeoTransform(double *padfGeoTransform)
1908 :
1909 : {
1910 31 : memcpy(padfGeoTransform, adfGeoTransform, sizeof(double) * 6);
1911 31 : return CE_None;
1912 : }
1913 :
1914 : /************************************************************************/
1915 : /* SetGeoTransform() */
1916 : /************************************************************************/
1917 :
1918 32 : CPLErr ECWWriteDataset::SetGeoTransform(double *padfGeoTransform)
1919 :
1920 : {
1921 32 : memcpy(adfGeoTransform, padfGeoTransform, sizeof(double) * 6);
1922 32 : return CE_None;
1923 : }
1924 :
1925 : /************************************************************************/
1926 : /* SetSpatialRef() */
1927 : /************************************************************************/
1928 :
1929 32 : CPLErr ECWWriteDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
1930 :
1931 : {
1932 32 : m_oSRS.Clear();
1933 32 : if (poSRS)
1934 32 : m_oSRS = *poSRS;
1935 :
1936 32 : return CE_None;
1937 : }
1938 :
1939 : /************************************************************************/
1940 : /* Crystalize() */
1941 : /************************************************************************/
1942 :
1943 2 : CPLErr ECWWriteDataset::Crystalize()
1944 :
1945 : {
1946 2 : int nWordSize = GDALGetDataTypeSize(eDataType) / 8;
1947 :
1948 : CPLErr eErr;
1949 :
1950 2 : if (bCrystalized)
1951 0 : return CE_None;
1952 :
1953 : const char **paszBandDescriptions =
1954 2 : (const char **)CPLMalloc(nBands * sizeof(char *));
1955 6 : for (int i = 0; i < nBands; i++)
1956 : {
1957 4 : paszBandDescriptions[i] = GetRasterBand(i + 1)->GetDescription();
1958 : }
1959 :
1960 2 : int bRGBColorSpace = ECWIsInputRGBColorSpace(this);
1961 :
1962 4 : eErr = oCompressor.Initialize(
1963 2 : pszFilename, papszOptions, nRasterXSize, nRasterYSize, nBands,
1964 2 : paszBandDescriptions, bRGBColorSpace, eDataType, &m_oSRS,
1965 2 : adfGeoTransform, 0, nullptr, bIsJPEG2000, FALSE, nullptr);
1966 :
1967 2 : if (eErr == CE_None)
1968 2 : bCrystalized = TRUE;
1969 :
1970 2 : nLoadedLine = -1;
1971 2 : pabyBILBuffer = (GByte *)CPLMalloc(nWordSize * nBands * nRasterXSize);
1972 :
1973 2 : CPLFree(paszBandDescriptions);
1974 :
1975 2 : return eErr;
1976 : }
1977 :
1978 : /************************************************************************/
1979 : /* FlushLine() */
1980 : /************************************************************************/
1981 :
1982 202 : CPLErr ECWWriteDataset::FlushLine()
1983 :
1984 : {
1985 202 : int nWordSize = GDALGetDataTypeSize(eDataType) / 8;
1986 : CPLErr eErr;
1987 :
1988 : /* -------------------------------------------------------------------- */
1989 : /* Crystallize if not already done. */
1990 : /* -------------------------------------------------------------------- */
1991 202 : if (!bCrystalized)
1992 : {
1993 2 : eErr = Crystalize();
1994 :
1995 2 : if (eErr != CE_None)
1996 0 : return eErr;
1997 : }
1998 :
1999 : /* -------------------------------------------------------------------- */
2000 : /* Write out the currently loaded line. */
2001 : /* -------------------------------------------------------------------- */
2002 202 : if (nLoadedLine != -1)
2003 : {
2004 :
2005 200 : void **papOutputLine = (void **)CPLMalloc(sizeof(void *) * nBands);
2006 600 : for (int i = 0; i < nBands; i++)
2007 400 : papOutputLine[i] =
2008 400 : (void *)(pabyBILBuffer + i * nWordSize * nRasterXSize);
2009 :
2010 200 : eErr = oCompressor.ourWriteLineBIL((UINT16)nBands, papOutputLine);
2011 200 : CPLFree(papOutputLine);
2012 200 : if (eErr != CE_None)
2013 : {
2014 0 : return eErr;
2015 : }
2016 : }
2017 :
2018 : /* -------------------------------------------------------------------- */
2019 : /* Clear the buffer and increment the "current line" indicator. */
2020 : /* -------------------------------------------------------------------- */
2021 202 : memset(pabyBILBuffer, 0, nWordSize * nRasterXSize * nBands);
2022 202 : nLoadedLine++;
2023 :
2024 202 : return CE_None;
2025 : }
2026 :
2027 : #ifdef OPTIMIZED_FOR_GDALWARP
2028 : /************************************************************************/
2029 : /* IRasterIO() */
2030 : /************************************************************************/
2031 :
2032 200 : CPLErr ECWWriteDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
2033 : int nXSize, int nYSize, void *pData,
2034 : int nBufXSize, int nBufYSize,
2035 : GDALDataType eBufType, int nBandCount,
2036 : BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
2037 : GSpacing nLineSpace, GSpacing nBandSpace,
2038 : GDALRasterIOExtraArg *psExtraArg)
2039 : {
2040 200 : ECWWriteRasterBand *po4thBand = nullptr;
2041 200 : IRasterIORequest *poIORequest = nullptr;
2042 :
2043 200 : if (bOutOfOrderWriteOccurred)
2044 0 : return CE_Failure;
2045 :
2046 200 : if (eRWFlag == GF_Write && nBandCount == 3 && nBands == 4)
2047 : {
2048 0 : po4thBand = (ECWWriteRasterBand *)GetRasterBand(4);
2049 0 : poIORequest = po4thBand->poIORequest;
2050 0 : if (poIORequest != nullptr)
2051 : {
2052 0 : if (nXOff != poIORequest->nXOff || nYOff != poIORequest->nYOff ||
2053 0 : nXSize != poIORequest->nXSize ||
2054 0 : nYSize != poIORequest->nYSize ||
2055 0 : nBufXSize != poIORequest->nBufXSize ||
2056 0 : nBufYSize != poIORequest->nBufYSize)
2057 : {
2058 0 : CPLError(CE_Failure, CPLE_AppDefined, "Out of order write");
2059 0 : bOutOfOrderWriteOccurred = TRUE;
2060 0 : return CE_Failure;
2061 : }
2062 : }
2063 : }
2064 :
2065 200 : int nDataTypeSize = GDALGetDataTypeSize(eDataType) / 8;
2066 200 : if (eRWFlag == GF_Write && nXOff == 0 && nXSize == nRasterXSize &&
2067 200 : nBufXSize == nXSize && nBufYSize == nYSize && eBufType == eDataType &&
2068 100 : (nBandCount == nBands ||
2069 0 : (nBandCount == 3 && poIORequest != nullptr && nBands == 4)) &&
2070 100 : nPixelSpace == nDataTypeSize &&
2071 100 : nLineSpace == nPixelSpace * nRasterXSize)
2072 : {
2073 100 : CPLErr eErr = CE_None;
2074 100 : GByte *pabyData = (GByte *)pData;
2075 200 : for (int iY = 0; iY < nYSize; iY++)
2076 : {
2077 200 : for (int iBand = 0; iBand < nBandCount && eErr == CE_None; iBand++)
2078 : {
2079 100 : eErr = GetRasterBand(panBandMap[iBand])
2080 200 : ->WriteBlock(0, iY + nYOff,
2081 100 : pabyData + iY * nLineSpace +
2082 100 : iBand * nBandSpace);
2083 : }
2084 :
2085 100 : if (poIORequest != nullptr && eErr == CE_None)
2086 : {
2087 0 : eErr = po4thBand->WriteBlock(0, iY + nYOff,
2088 0 : poIORequest->pabyData +
2089 0 : iY * nDataTypeSize * nXSize);
2090 : }
2091 : }
2092 :
2093 100 : if (poIORequest != nullptr)
2094 : {
2095 0 : delete poIORequest;
2096 0 : po4thBand->poIORequest = nullptr;
2097 : }
2098 :
2099 100 : return eErr;
2100 : }
2101 : else
2102 100 : return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
2103 : pData, nBufXSize, nBufYSize, eBufType,
2104 : nBandCount, panBandMap, nPixelSpace,
2105 100 : nLineSpace, nBandSpace, psExtraArg);
2106 : }
2107 : #endif
2108 :
2109 : /************************************************************************/
2110 : /* ==================================================================== */
2111 : /* ECWWriteRasterBand */
2112 : /* ==================================================================== */
2113 : /************************************************************************/
2114 :
2115 : /************************************************************************/
2116 : /* ECWWriteRasterBand() */
2117 : /************************************************************************/
2118 :
2119 71 : ECWWriteRasterBand::ECWWriteRasterBand(ECWWriteDataset *poDSIn, int nBandIn)
2120 :
2121 : {
2122 71 : nBand = nBandIn;
2123 71 : poDS = poDSIn;
2124 71 : poGDS = poDSIn;
2125 71 : nBlockXSize = poDSIn->GetRasterXSize();
2126 71 : nBlockYSize = 1;
2127 71 : eDataType = poDSIn->eDataType;
2128 71 : eInterp = GCI_Undefined;
2129 : #ifdef OPTIMIZED_FOR_GDALWARP
2130 71 : poIORequest = nullptr;
2131 : #endif
2132 71 : }
2133 :
2134 : /************************************************************************/
2135 : /* ~ECWWriteRasterBand() */
2136 : /************************************************************************/
2137 :
2138 142 : ECWWriteRasterBand::~ECWWriteRasterBand()
2139 :
2140 : {
2141 : #ifdef OPTIMIZED_FOR_GDALWARP
2142 71 : delete poIORequest;
2143 : #endif
2144 142 : }
2145 :
2146 : /************************************************************************/
2147 : /* IReadBlock() */
2148 : /************************************************************************/
2149 :
2150 0 : CPLErr ECWWriteRasterBand::IReadBlock(CPL_UNUSED int nBlockX,
2151 : CPL_UNUSED int nBlockY, void *pBuffer)
2152 : {
2153 0 : int nWordSize = GDALGetDataTypeSize(eDataType) / 8;
2154 :
2155 : // We zero stuff out here, but we can't really read stuff from
2156 : // a write only stream.
2157 :
2158 0 : memset(pBuffer, 0, nBlockXSize * nWordSize);
2159 :
2160 0 : return CE_None;
2161 : }
2162 :
2163 : #ifdef OPTIMIZED_FOR_GDALWARP
2164 : /************************************************************************/
2165 : /* IRasterIO() */
2166 : /************************************************************************/
2167 :
2168 300 : CPLErr ECWWriteRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
2169 : int nXSize, int nYSize, void *pData,
2170 : int nBufXSize, int nBufYSize,
2171 : GDALDataType eBufType,
2172 : GSpacing nPixelSpace, GSpacing nLineSpace,
2173 : GDALRasterIOExtraArg *psExtraArg)
2174 : {
2175 300 : if (eRWFlag == GF_Write && nBand == 4 && poGDS->nBands == 4 &&
2176 0 : poGDS->nPrevIRasterIOBand < 0)
2177 : {
2178 : /* Triggered when gdalwarp outputs an alpha band */
2179 : /* It is called before GDALDatasetRasterIO() on the 3 first bands */
2180 0 : if (poIORequest != nullptr)
2181 0 : return CE_Failure;
2182 0 : poIORequest = new IRasterIORequest(this, nXOff, nYOff, nXSize, nYSize,
2183 : pData, nBufXSize, nBufYSize,
2184 0 : eBufType, nPixelSpace, nLineSpace);
2185 0 : return CE_None;
2186 : }
2187 :
2188 300 : poGDS->nPrevIRasterIOBand = nBand;
2189 300 : return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
2190 : pData, nBufXSize, nBufYSize, eBufType,
2191 300 : nPixelSpace, nLineSpace, psExtraArg);
2192 : }
2193 : #endif
2194 :
2195 : /************************************************************************/
2196 : /* IWriteBlock() */
2197 : /************************************************************************/
2198 :
2199 400 : CPLErr ECWWriteRasterBand::IWriteBlock(CPL_UNUSED int nBlockX, int nBlockY,
2200 : void *pBuffer)
2201 : {
2202 400 : int nWordSize = GDALGetDataTypeSize(eDataType) / 8;
2203 : CPLErr eErr;
2204 :
2205 400 : if (poGDS->bOutOfOrderWriteOccurred)
2206 0 : return CE_Failure;
2207 :
2208 : /* -------------------------------------------------------------------- */
2209 : /* Flush previous line if needed. */
2210 : /* -------------------------------------------------------------------- */
2211 400 : if (nBlockY == poGDS->nLoadedLine + 1)
2212 : {
2213 200 : eErr = poGDS->FlushLine();
2214 200 : if (eErr != CE_None)
2215 0 : return eErr;
2216 : }
2217 :
2218 : /* -------------------------------------------------------------------- */
2219 : /* Blow a gasket if we have been asked to write something out */
2220 : /* of order. */
2221 : /* -------------------------------------------------------------------- */
2222 400 : if (nBlockY != poGDS->nLoadedLine)
2223 : {
2224 0 : CPLError(CE_Failure, CPLE_AppDefined,
2225 : "Apparent attempt to write to ECW non-sequentially.\n"
2226 : "Loaded line is %d, but %d of band %d was written to.",
2227 0 : poGDS->nLoadedLine, nBlockY, nBand);
2228 0 : poGDS->bOutOfOrderWriteOccurred = TRUE;
2229 0 : return CE_Failure;
2230 : }
2231 :
2232 : /* -------------------------------------------------------------------- */
2233 : /* Copy passed data into current line buffer. */
2234 : /* -------------------------------------------------------------------- */
2235 400 : memcpy(poGDS->pabyBILBuffer + (nBand - 1) * nWordSize * nRasterXSize,
2236 400 : pBuffer, nWordSize * nRasterXSize);
2237 :
2238 400 : return CE_None;
2239 : }
2240 :
2241 : /************************************************************************/
2242 : /* ECWCreateJPEG2000() */
2243 : /************************************************************************/
2244 :
2245 51 : GDALDataset *ECWCreateJPEG2000(const char *pszFilename, int nXSize, int nYSize,
2246 : int nBands, GDALDataType eType,
2247 : char **papszOptions)
2248 :
2249 : {
2250 51 : if (nBands == 0)
2251 : {
2252 18 : CPLError(CE_Failure, CPLE_NotSupported, "0 band not supported");
2253 18 : return nullptr;
2254 : }
2255 33 : ECWInitialize();
2256 :
2257 : return new ECWWriteDataset(pszFilename, nXSize, nYSize, nBands, eType,
2258 33 : papszOptions, TRUE);
2259 : }
2260 :
2261 : /************************************************************************/
2262 : /* ECWCreateECW() */
2263 : /************************************************************************/
2264 :
2265 0 : GDALDataset *ECWCreateECW(const char *pszFilename, int nXSize, int nYSize,
2266 : int nBands, GDALDataType eType, char **papszOptions)
2267 :
2268 : {
2269 0 : if (nBands == 0)
2270 : {
2271 0 : CPLError(CE_Failure, CPLE_NotSupported, "0 band not supported");
2272 0 : return nullptr;
2273 : }
2274 0 : ECWInitialize();
2275 :
2276 : return new ECWWriteDataset(pszFilename, nXSize, nYSize, nBands, eType,
2277 0 : papszOptions, FALSE);
2278 : }
2279 :
2280 : #endif /* def HAVE_COMPRESS */
|