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