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