1 : /******************************************************************************
2 : * $Id: ecwcreatecopy.cpp 25804 2013-03-25 18:50:26Z rouault $
3 : *
4 : * Project: GDAL ECW Driver
5 : * Purpose: ECW CreateCopy method implementation.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2001, 2004, Frank Warmerdam <warmerdam@pobox.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 : #include "gdal_ecw.h"
31 : #include "ogr_spatialref.h"
32 :
33 : CPL_CVSID("$Id: ecwcreatecopy.cpp 25804 2013-03-25 18:50:26Z rouault $");
34 :
35 : #if defined(FRMT_ecw) && defined(HAVE_COMPRESS)
36 :
37 : class GDALECWCompressor : public CNCSFile {
38 :
39 : public:
40 : GDALECWCompressor();
41 : virtual ~GDALECWCompressor();
42 : virtual CNCSError WriteReadLine(UINT32 nNextLine, void **ppInputArray);
43 : #if ECWSDK_VERSION>=50
44 : virtual void WriteStatus(IEEE4 fPercentComplete, const NCS::CString &sStatusText, const CompressionCounters &Counters);
45 : #else
46 : virtual void WriteStatus(UINT32 nCurrentLine);
47 :
48 : #endif
49 :
50 : virtual bool WriteCancel();
51 :
52 : CPLErr Initialize( const char *pszFilename, char **papszOptions,
53 : int nXSize, int nYSize, int nBands, const char * const * papszBandDescriptions, int bRGBColorSpace,
54 : GDALDataType eType,
55 : const char *pszWKT, double *padfGeoTransform,
56 : int nGCPCount, const GDAL_GCP *pasGCPList,
57 : int bIsJPEG2000 );
58 : CPLErr CloseDown();
59 :
60 : CPLErr PrepareCoverageBox( const char *pszWKT, double *padfGeoTransform );
61 : CPLErr WriteJP2Box( GDALJP2Box * );
62 : CPLErr WriteLineBIL(UINT16 nBands, void **ppOutputLine, UINT32 *pLineSteps = NULL);
63 0 : virtual NCSEcwCellType WriteReadLineGetCellType() {
64 0 : return sFileInfo.eCellType;
65 : }
66 : #ifdef ECW_FW
67 : CNCSJP2File::CNCSJPXAssocBox m_oGMLAssoc;
68 : #endif
69 :
70 : // Data
71 :
72 : GDALDataset *m_poSrcDS;
73 :
74 : VSIIOStream m_OStream;
75 : int m_nPercentComplete;
76 :
77 : int m_bCancelled;
78 :
79 : GDALProgressFunc pfnProgress;
80 : void *pProgressData;
81 :
82 :
83 : GDALDataType eWorkDT;
84 :
85 : JP2UserBox** papoJP2UserBox;
86 : int nJP2UserBox;
87 :
88 : private :
89 : NCSFileViewFileInfoEx sFileInfo;
90 : };
91 :
92 : /************************************************************************/
93 : /* GDALECWCompressor() */
94 : /************************************************************************/
95 :
96 50 : GDALECWCompressor::GDALECWCompressor()
97 :
98 : {
99 50 : m_poSrcDS = NULL;
100 50 : m_nPercentComplete = -1;
101 50 : m_bCancelled = FALSE;
102 50 : pfnProgress = GDALDummyProgress;
103 50 : pProgressData = NULL;
104 50 : papoJP2UserBox = NULL;
105 50 : nJP2UserBox = 0;
106 50 : NCSInitFileInfoEx(&sFileInfo);
107 50 : }
108 :
109 : /************************************************************************/
110 : /* ~GDALECWCompressor() */
111 : /************************************************************************/
112 :
113 50 : GDALECWCompressor::~GDALECWCompressor()
114 :
115 : {
116 : int i;
117 85 : for(i=0;i<nJP2UserBox;i++)
118 35 : delete papoJP2UserBox[i];
119 50 : CPLFree(papoJP2UserBox);
120 50 : }
121 :
122 : /************************************************************************/
123 : /* CloseDown() */
124 : /************************************************************************/
125 :
126 20 : CPLErr GDALECWCompressor::CloseDown()
127 :
128 : {
129 60 : for( int i = 0; i < sFileInfo.nBands; i++ )
130 : {
131 40 : CPLFree( sFileInfo.pBands[i].szDesc );
132 : }
133 20 : CPLFree( sFileInfo.pBands );
134 :
135 20 : Close( true );
136 20 : m_OStream.Close();
137 :
138 20 : return CE_None;
139 : }
140 :
141 : /************************************************************************/
142 : /* WriteReadLine() */
143 : /************************************************************************/
144 :
145 1714 : CNCSError GDALECWCompressor::WriteReadLine( UINT32 nNextLine,
146 : void **ppInputArray )
147 :
148 : {
149 : int iBand, *panBandMap;
150 : CPLErr eErr;
151 : GByte *pabyLineBuf;
152 1714 : int nWordSize = GDALGetDataTypeSize( eWorkDT ) / 8;
153 :
154 1714 : panBandMap = (int *) CPLMalloc(sizeof(int) * sFileInfo.nBands);
155 4598 : for( iBand = 0; iBand < sFileInfo.nBands; iBand++ )
156 2884 : panBandMap[iBand] = iBand+1;
157 :
158 : pabyLineBuf = (GByte *) CPLMalloc( sFileInfo.nSizeX * sFileInfo.nBands
159 1714 : * nWordSize );
160 :
161 : eErr = m_poSrcDS->RasterIO( GF_Read, 0, nNextLine, sFileInfo.nSizeX, 1,
162 : pabyLineBuf, sFileInfo.nSizeX, 1,
163 : eWorkDT,
164 : sFileInfo.nBands, panBandMap,
165 1714 : nWordSize, 0, nWordSize * sFileInfo.nSizeX );
166 :
167 4598 : for( iBand = 0; iBand < (int) sFileInfo.nBands; iBand++ )
168 : {
169 : memcpy( ppInputArray[iBand],
170 : pabyLineBuf + nWordSize * sFileInfo.nSizeX * iBand,
171 2884 : nWordSize * sFileInfo.nSizeX );
172 : }
173 :
174 1714 : CPLFree( pabyLineBuf );
175 1714 : CPLFree( panBandMap );
176 :
177 1714 : if( eErr == CE_None )
178 1714 : return NCS_SUCCESS;
179 : else
180 0 : return NCS_FILEIO_ERROR;
181 : }
182 :
183 : /************************************************************************/
184 : /* WriteStatus() */
185 : /************************************************************************/
186 : #if ECWSDK_VERSION>=50
187 : void GDALECWCompressor::WriteStatus(IEEE4 fPercentComplete, const NCS::CString &sStatusText, const CompressionCounters &Counters)
188 : {
189 : char szBuff[2048];
190 : sStatusText.utf8_str(szBuff, 2047);
191 : szBuff[2047] = '\0';
192 :
193 : m_bCancelled = !pfnProgress(
194 : fPercentComplete/100.0,
195 : szBuff,
196 : pProgressData );
197 : }
198 : #else
199 :
200 1714 : void GDALECWCompressor::WriteStatus( UINT32 nCurrentLine )
201 :
202 : {
203 : m_bCancelled =
204 : !pfnProgress( nCurrentLine / (float) sFileInfo.nSizeY,
205 1714 : NULL, pProgressData );
206 1714 : }
207 : #endif
208 : /************************************************************************/
209 : /* WriteCancel() */
210 : /************************************************************************/
211 :
212 1714 : bool GDALECWCompressor::WriteCancel()
213 :
214 : {
215 1714 : return (bool) m_bCancelled;
216 : }
217 :
218 : /************************************************************************/
219 : /* PrepareCoverageBox() */
220 : /************************************************************************/
221 :
222 0 : CPLErr GDALECWCompressor::PrepareCoverageBox( const char *pszWKT,
223 : double *padfGeoTransform )
224 :
225 : {
226 : #ifndef ECW_FW
227 0 : return CE_Failure;
228 : #else
229 : /* -------------------------------------------------------------------- */
230 : /* Try do determine a PCS or GCS code we can use. */
231 : /* -------------------------------------------------------------------- */
232 : OGRSpatialReference oSRS;
233 : char *pszWKTCopy = (char *) pszWKT;
234 : int nEPSGCode = 0;
235 : char szSRSName[100];
236 :
237 : if( oSRS.importFromWkt( &pszWKTCopy ) != OGRERR_NONE )
238 : return CE_Failure;
239 :
240 : if( oSRS.IsProjected() )
241 : {
242 : const char *pszAuthName = oSRS.GetAuthorityName( "PROJCS" );
243 :
244 : if( pszAuthName != NULL && EQUAL(pszAuthName,"epsg") )
245 : {
246 : nEPSGCode = atoi(oSRS.GetAuthorityCode( "PROJCS" ));
247 : }
248 : }
249 : else if( oSRS.IsGeographic() )
250 : {
251 : const char *pszAuthName = oSRS.GetAuthorityName( "GEOGCS" );
252 :
253 : if( pszAuthName != NULL && EQUAL(pszAuthName,"epsg") )
254 : {
255 : nEPSGCode = atoi(oSRS.GetAuthorityCode( "GEOGCS" ));
256 : }
257 : }
258 :
259 : if( nEPSGCode != 0 )
260 : sprintf( szSRSName, "urn:ogc:def:crs:EPSG::%d", nEPSGCode );
261 : else
262 : strcpy( szSRSName,
263 : "gmljp2://xml/CRSDictionary.gml#ogrcrs1" );
264 :
265 : /* -------------------------------------------------------------------- */
266 : /* For now we hardcode for a minimal instance format. */
267 : /* -------------------------------------------------------------------- */
268 : char szDoc[4000];
269 :
270 : sprintf( szDoc,
271 : "<gml:FeatureCollection\n"
272 : " xmlns:gml=\"http://www.opengis.net/gml\"\n"
273 : " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
274 : " xsi:schemaLocation=\"http://www.opengis.net/gml http://www.math.ubc.ca/~burggraf/gml/gml4jp2.xsd\">\n"
275 : " <gml:boundedBy>\n"
276 : " <gml:Null>withheld</gml:Null>\n"
277 : " </gml:boundedBy>\n"
278 : " <gml:featureMember>\n"
279 : " <gml:FeatureCollection>\n"
280 : " <gml:featureMember>\n"
281 : " <gml:RectifiedGridCoverage dimension=\"2\" gml:id=\"RGC0001\">\n"
282 : " <gml:rectifiedGridDomain>\n"
283 : " <gml:RectifiedGrid dimension=\"2\">\n"
284 : " <gml:limits>\n"
285 : " <gml:GridEnvelope>\n"
286 : " <gml:low>0 0</gml:low>\n"
287 : " <gml:high>%d %d</gml:high>\n"
288 : " </gml:GridEnvelope>\n"
289 : " </gml:limits>\n"
290 : " <gml:axisName>x</gml:axisName>\n"
291 : " <gml:axisName>y</gml:axisName>\n"
292 : " <gml:origin>\n"
293 : " <gml:Point gml:id=\"P0001\" srsName=\"%s\">\n"
294 : " <gml:pos>%.15g %.15g</gml:pos>\n"
295 : " </gml:Point>\n"
296 : " </gml:origin>\n"
297 : " <gml:offsetVector srsName=\"%s\">%.15g %.15g</gml:offsetVector>\n"
298 : " <gml:offsetVector srsName=\"%s\">%.15g %.15g</gml:offsetVector>\n"
299 : " </gml:RectifiedGrid>\n"
300 : " </gml:rectifiedGridDomain>\n"
301 : " <gml:rangeSet>\n"
302 : " <gml:File>\n"
303 : " <gml:fileName>urn:ogc:tc:gmljp2:codestream:0</gml:fileName>\n"
304 : " <gml:fileStructure>Record Interleaved</gml:fileStructure>\n"
305 : " </gml:File>\n"
306 : " </gml:rangeSet>\n"
307 : " </gml:RectifiedGridCoverage>\n"
308 : " </gml:featureMember>\n"
309 : " </gml:FeatureCollection>\n"
310 : " </gml:featureMember>\n"
311 : "</gml:FeatureCollection>\n",
312 : sFileInfo.nSizeX-1, sFileInfo.nSizeY-1,
313 : szSRSName,
314 : padfGeoTransform[0] + padfGeoTransform[1] * 0.5
315 : + padfGeoTransform[4] * 0.5,
316 : padfGeoTransform[3] + padfGeoTransform[2] * 0.5
317 : + padfGeoTransform[5] * 0.5,
318 : szSRSName,
319 : padfGeoTransform[1], padfGeoTransform[2],
320 : szSRSName,
321 : padfGeoTransform[4], padfGeoTransform[5] );
322 :
323 : /* -------------------------------------------------------------------- */
324 : /* If we need a user defined CRSDictionary entry, prepare it */
325 : /* here. */
326 : /* -------------------------------------------------------------------- */
327 : char *pszDictBox = NULL;
328 :
329 : if( nEPSGCode == 0 )
330 : {
331 : char *pszGMLDef = NULL;
332 :
333 : if( oSRS.exportToXML( &pszGMLDef, NULL ) == OGRERR_NONE )
334 : {
335 : pszDictBox = (char *) CPLMalloc(strlen(pszGMLDef) + 4000);
336 :
337 : sprintf( pszDictBox,
338 : "<gml:Dictionary gml:id=\"CRSU1\" \n"
339 : " xmlns:gml=\"http://www.opengis.net/gml\"\n"
340 : " xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"
341 : " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n"
342 : " <gml:dictionaryEntry>\n"
343 : "%s\n"
344 : " </gml:dictionaryEntry>\n"
345 : "</gml:Dictionary>\n",
346 : pszGMLDef );
347 : }
348 : CPLFree( pszGMLDef );
349 : }
350 :
351 : /* -------------------------------------------------------------------- */
352 : /* Setup the various required boxes. */
353 : /* -------------------------------------------------------------------- */
354 : JP2UserBox *poGMLData;
355 : CNCSJP2File::CNCSJPXAssocBox *poAssoc;
356 : CNCSJP2File::CNCSJPXLabelBox *poLabel;
357 :
358 : poLabel = new CNCSJP2File::CNCSJPXLabelBox();
359 : poLabel->SetLabel( "gml.data" );
360 : poLabel->m_bValid = true;
361 : m_oGMLAssoc.m_OtherBoxes.push_back( poLabel );
362 : m_oGMLAssoc.m_OwnedBoxes.push_back( poLabel );
363 :
364 : poAssoc = new CNCSJP2File::CNCSJPXAssocBox();
365 : m_oGMLAssoc.m_OtherBoxes.push_back( poAssoc );
366 : m_oGMLAssoc.m_OwnedBoxes.push_back( poAssoc );
367 : poAssoc->m_bValid = true;
368 :
369 : poLabel = new CNCSJP2File::CNCSJPXLabelBox();
370 : poLabel->SetLabel( "gml.root-instance" );
371 : poLabel->m_bValid = true;
372 : poAssoc->m_OtherBoxes.push_back( poLabel );
373 : poAssoc->m_OwnedBoxes.push_back( poLabel );
374 :
375 : poGMLData = new JP2UserBox();
376 : poGMLData->m_nTBox = 'xml '; /* Is it correct on a big-endian host ? Does ECW work on big-endian hosts ;-) */
377 : poGMLData->SetData( strlen( szDoc ), (unsigned char *) szDoc );
378 : poAssoc->m_OtherBoxes.push_back( poGMLData );
379 : poAssoc->m_OwnedBoxes.push_back( poGMLData );
380 :
381 : if( pszDictBox != NULL )
382 : {
383 : poAssoc = new CNCSJP2File::CNCSJPXAssocBox();
384 : m_oGMLAssoc.m_OtherBoxes.push_back( poAssoc );
385 : m_oGMLAssoc.m_OwnedBoxes.push_back( poAssoc );
386 : poAssoc->m_bValid = true;
387 :
388 : poLabel = new CNCSJP2File::CNCSJPXLabelBox();
389 : poLabel->SetLabel( "CRSDictionary.gml" );
390 : poLabel->m_bValid = true;
391 : poAssoc->m_OtherBoxes.push_back( poLabel );
392 : poAssoc->m_OwnedBoxes.push_back( poLabel );
393 :
394 : poGMLData = new JP2UserBox();
395 : poGMLData->m_nTBox = 'xml '; /* Is it correct on a big-endian host ? Does ECW work on big-endian hosts ;-) */
396 : poGMLData->SetData( strlen(pszDictBox),
397 : (unsigned char *) pszDictBox );
398 : poAssoc->m_OtherBoxes.push_back( poGMLData );
399 : poAssoc->m_OwnedBoxes.push_back( poGMLData );
400 :
401 : CPLFree( pszDictBox );
402 : }
403 :
404 : m_oGMLAssoc.m_bValid = true;
405 : AddBox( &m_oGMLAssoc );
406 :
407 : return CE_None;
408 : #endif /* def ECW_FW */
409 : }
410 :
411 : /************************************************************************/
412 : /* WriteJP2Box() */
413 : /************************************************************************/
414 :
415 36 : CPLErr GDALECWCompressor::WriteJP2Box( GDALJP2Box * poBox )
416 :
417 : {
418 : JP2UserBox *poECWBox;
419 :
420 36 : if( poBox == NULL )
421 1 : return CE_None;
422 :
423 35 : poECWBox = new JP2UserBox();
424 35 : memcpy( &(poECWBox->m_nTBox), poBox->GetType(), 4 );
425 35 : CPL_MSBPTR32( &(poECWBox->m_nTBox) );
426 :
427 : poECWBox->SetData( (int) poBox->GetDataLength(),
428 35 : poBox->GetWritableData() );
429 :
430 35 : AddBox( poECWBox );
431 :
432 70 : delete poBox;
433 :
434 : papoJP2UserBox =(JP2UserBox**) CPLRealloc(papoJP2UserBox,
435 35 : (nJP2UserBox + 1) * sizeof(JP2UserBox*));
436 35 : papoJP2UserBox[nJP2UserBox] = poECWBox;
437 35 : nJP2UserBox ++;
438 :
439 35 : return CE_None;
440 : }
441 :
442 : /************************************************************************/
443 : /* WriteLineBIL() */
444 : /************************************************************************/
445 :
446 200 : CPLErr GDALECWCompressor::WriteLineBIL(UINT16 nBands, void **ppOutputLine, UINT32 *pLineSteps){
447 :
448 200 : CNCSError oError = CNCSFile::WriteLineBIL(sFileInfo.eCellType,nBands, ppOutputLine, pLineSteps);
449 :
450 200 : if( oError.GetErrorNumber() != NCS_SUCCESS )
451 : {
452 0 : ECWReportError(oError, "Scanline write write failed.\n");
453 0 : return CE_Failure;
454 : }
455 200 : return CE_None;
456 : }
457 : /************************************************************************/
458 : /* Initialize() */
459 : /* */
460 : /* Initialize compressor output. */
461 : /************************************************************************/
462 :
463 24 : CPLErr GDALECWCompressor::Initialize(
464 : const char *pszFilename, char **papszOptions,
465 : int nXSize, int nYSize, int nBands, const char * const * papszBandDescriptions, int bRGBColorSpace,
466 : GDALDataType eType,
467 : const char *pszWKT, double *padfGeoTransform,
468 : int nGCPCount, const GDAL_GCP *pasGCPList,
469 : int bIsJPEG2000 )
470 :
471 : {
472 : const char *pszOption;
473 : /* -------------------------------------------------------------------- */
474 : /* For 4.x and beyond you need a license key to compress data. */
475 : /* Check for it as a configuration option or a creation option. */
476 : /* -------------------------------------------------------------------- */
477 : #if ECWSDK_VERSION >= 40
478 : const char* pszECWKey = CSLFetchNameValue( papszOptions, "ECW_ENCODE_KEY");
479 : if( pszECWKey == NULL )
480 : pszECWKey = CPLGetConfigOption( "ECW_ENCODE_KEY", NULL );
481 :
482 : const char* pszECWCompany =
483 : CSLFetchNameValue( papszOptions, "ECW_ENCODE_COMPANY");
484 : if( pszECWCompany == NULL )
485 : pszECWCompany = CPLGetConfigOption( "ECW_ENCODE_COMPANY", NULL );
486 :
487 : if( pszECWKey && pszECWCompany)
488 : {
489 : CPLDebug( "ECW", "SetOEMKey(%s,%s)", pszECWCompany, pszECWKey );
490 : CNCSFile::SetOEMKey( (char *) pszECWCompany, (char *)pszECWKey );
491 : }
492 : else if( pszECWKey || pszECWCompany )
493 : {
494 : CPLError( CE_Failure, CPLE_AppDefined,
495 : "Only one of ECW_ENCODE_KEY and ECW_ENCODE_COMPANY were provided.\nBoth are required." );
496 : return CE_Failure;
497 : }else{
498 : CPLError( CE_Failure, CPLE_AppDefined,
499 : "None of ECW_ENCODE_KEY and ECW_ENCODE_COMPANY were provided.\nBoth are required." );
500 : return CE_Failure;
501 : }
502 :
503 : #endif /* ECWSDK_VERSION >= 40 */
504 :
505 : /* -------------------------------------------------------------------- */
506 : /* Do some rudimentary checking in input. */
507 : /* -------------------------------------------------------------------- */
508 24 : if( nBands == 0 )
509 : {
510 : CPLError( CE_Failure, CPLE_NotSupported,
511 0 : "ECW driver requires at least one band." );
512 0 : return CE_Failure;
513 : }
514 :
515 : /* -------------------------------------------------------------------- */
516 : /* Parse out some known options. */
517 : /* -------------------------------------------------------------------- */
518 : float fTargetCompression;
519 :
520 : // Default compression based on image type per request from Paul Beaty.
521 24 : if( nBands > 1 )
522 10 : fTargetCompression = 95.0;
523 : else
524 14 : fTargetCompression = 90.0;
525 :
526 24 : if( CSLFetchNameValue(papszOptions, "TARGET") != NULL )
527 : {
528 : fTargetCompression = (float)
529 6 : atof(CSLFetchNameValue(papszOptions, "TARGET"));
530 :
531 : /* The max allowed value should be 100 - 100 / 65535 = 99.9984740978 */
532 : /* so that nCompressionRate fits on a uint16 (see below) */
533 : /* No need to be so pedantic, so we will limit to 99.99 % */
534 : /* (compression rate = 10 000) */
535 6 : if( fTargetCompression < 0.0 || fTargetCompression > 99.99 )
536 : {
537 : CPLError( CE_Failure, CPLE_NotSupported,
538 : "TARGET compression of %.3f invalid, should be a\n"
539 : "value between 0 and 99.99 percent.\n",
540 0 : (double) fTargetCompression );
541 0 : return CE_Failure;
542 : }
543 : }
544 :
545 : /* -------------------------------------------------------------------- */
546 : /* Create and initialize compressor. */
547 : /* -------------------------------------------------------------------- */
548 24 : NCSFileViewFileInfoEx *psClient = &(sFileInfo);
549 : #if ECWSDK_VERSION >= 50
550 : bool bECWV3 = false;
551 : pszOption = CSLFetchNameValue(papszOptions, "ECW_FORMAT_VERSION");
552 : if( pszOption != NULL )
553 : {
554 : bECWV3 = (3 == atoi(pszOption));
555 : }
556 : psClient->nFormatVersion = (bECWV3)?3:2;
557 : #endif
558 24 : psClient->nBands = (UINT16) nBands;
559 24 : psClient->nSizeX = nXSize;
560 24 : psClient->nSizeY = nYSize;
561 24 : psClient->nCompressionRate = (UINT16) MAX(1,100/(100-fTargetCompression));
562 24 : psClient->eCellSizeUnits = ECW_CELL_UNITS_METERS;
563 :
564 24 : if( nBands == 1 )
565 14 : psClient->eColorSpace = NCSCS_GREYSCALE;
566 15 : else if( nBands == 3 && bRGBColorSpace)
567 5 : psClient->eColorSpace = NCSCS_sRGB;
568 : #if ECWSDK_VERSION >= 40
569 : else if( nBands == 4 && bRGBColorSpace )
570 : psClient->eColorSpace = NCSCS_sRGB;
571 : #endif
572 : else
573 5 : psClient->eColorSpace = NCSCS_MULTIBAND;
574 :
575 : /* -------------------------------------------------------------------- */
576 : /* Figure out the data type. */
577 : /* -------------------------------------------------------------------- */
578 24 : int bSigned = FALSE;
579 24 : int nBits = 8;
580 24 : eWorkDT = eType;
581 :
582 24 : switch( eWorkDT )
583 : {
584 : case GDT_Byte:
585 : #if ECWSDK_VERSION >=50
586 : psClient->nCellBitDepth = 8;
587 : #endif
588 18 : psClient->eCellType = NCSCT_UINT8;
589 18 : nBits = 8;
590 18 : bSigned = FALSE;
591 18 : break;
592 :
593 : case GDT_UInt16:
594 : #if ECWSDK_VERSION >=50
595 : psClient->nCellBitDepth = 16;
596 : #endif
597 1 : psClient->eCellType = NCSCT_UINT16;
598 1 : nBits = 16;
599 1 : bSigned = FALSE;
600 1 : break;
601 :
602 : case GDT_UInt32:
603 : #if ECWSDK_VERSION >=50
604 : psClient->nCellBitDepth = 32;
605 : #endif
606 1 : psClient->eCellType = NCSCT_UINT32;
607 1 : nBits = 32;
608 1 : bSigned = FALSE;
609 1 : break;
610 :
611 : case GDT_Int16:
612 : #if ECWSDK_VERSION >=50
613 : psClient->nCellBitDepth = 16;
614 : #endif
615 2 : psClient->eCellType = NCSCT_INT16;
616 2 : nBits = 16;
617 2 : bSigned = TRUE;
618 2 : break;
619 :
620 : case GDT_Int32:
621 : #if ECWSDK_VERSION >=50
622 : psClient->nCellBitDepth = 32;
623 : #endif
624 1 : psClient->eCellType = NCSCT_INT32;
625 1 : nBits = 32;
626 1 : bSigned = TRUE;
627 1 : break;
628 :
629 : case GDT_Float32:
630 1 : psClient->eCellType = NCSCT_IEEE4;
631 1 : nBits = 32;
632 1 : bSigned = TRUE;
633 1 : break;
634 :
635 : case GDT_Float64:
636 0 : psClient->eCellType = NCSCT_IEEE8;
637 0 : nBits = 64;
638 0 : bSigned = TRUE;
639 0 : break;
640 :
641 : default:
642 : // We treat complex types as float.
643 0 : psClient->eCellType = NCSCT_IEEE4;
644 0 : nBits = 32;
645 0 : bSigned = TRUE;
646 0 : eWorkDT = GDT_Float32;
647 : break;
648 : }
649 :
650 : /* -------------------------------------------------------------------- */
651 : /* Create band information structures. */
652 : /* -------------------------------------------------------------------- */
653 : int iBand;
654 :
655 : psClient->pBands = (NCSFileBandInfo *)
656 24 : CPLMalloc( sizeof(NCSFileBandInfo) * nBands );
657 68 : for( iBand = 0; iBand < nBands; iBand++ )
658 : {
659 44 : psClient->pBands[iBand].nBits = (UINT8) nBits;
660 44 : psClient->pBands[iBand].bSigned = (BOOLEAN)bSigned;
661 44 : psClient->pBands[iBand].szDesc = CPLStrdup(papszBandDescriptions[iBand]);
662 : }
663 :
664 : /* -------------------------------------------------------------------- */
665 : /* Allow CNCSFile::SetParameter() requests. */
666 : /* -------------------------------------------------------------------- */
667 :
668 24 : if( bIsJPEG2000 )
669 : {
670 21 : pszOption = CSLFetchNameValue(papszOptions, "PROFILE");
671 21 : if( pszOption != NULL && EQUAL(pszOption,"BASELINE_0") )
672 : SetParameter(
673 0 : CNCSJP2FileView::JP2_COMPRESS_PROFILE_BASELINE_0 );
674 21 : else if( pszOption != NULL && EQUAL(pszOption,"BASELINE_1") )
675 : SetParameter(
676 0 : CNCSJP2FileView::JP2_COMPRESS_PROFILE_BASELINE_1 );
677 21 : else if( pszOption != NULL && EQUAL(pszOption,"BASELINE_2") )
678 : SetParameter(
679 0 : CNCSJP2FileView::JP2_COMPRESS_PROFILE_BASELINE_2 );
680 27 : else if( pszOption != NULL && EQUAL(pszOption,"NPJE") )
681 : SetParameter(
682 6 : CNCSJP2FileView::JP2_COMPRESS_PROFILE_NITF_BIIF_NPJE );
683 15 : else if( pszOption != NULL && EQUAL(pszOption,"EPJE") )
684 : SetParameter(
685 0 : CNCSJP2FileView::JP2_COMPRESS_PROFILE_NITF_BIIF_EPJE );
686 :
687 21 : pszOption = CSLFetchNameValue(papszOptions, "CODESTREAM_ONLY" );
688 21 : if( pszOption != NULL )
689 : SetParameter(
690 : CNCSJP2FileView::JP2_COMPRESS_CODESTREAM_ONLY,
691 3 : (bool) CSLTestBoolean( pszOption ) );
692 :
693 21 : pszOption = CSLFetchNameValue(papszOptions, "LEVELS");
694 21 : if( pszOption != NULL )
695 : SetParameter( CNCSJP2FileView::JP2_COMPRESS_LEVELS,
696 0 : (UINT32) atoi(pszOption) );
697 :
698 21 : pszOption = CSLFetchNameValue(papszOptions, "LAYERS");
699 21 : if( pszOption != NULL )
700 : SetParameter( CNCSJP2FileView::JP2_COMPRESS_LAYERS,
701 3 : (UINT32) atoi(pszOption) );
702 :
703 21 : pszOption = CSLFetchNameValue(papszOptions, "PRECINCT_WIDTH");
704 21 : if( pszOption != NULL )
705 : SetParameter( CNCSJP2FileView::JP2_COMPRESS_PRECINCT_WIDTH,
706 0 : (UINT32) atoi(pszOption) );
707 :
708 21 : pszOption = CSLFetchNameValue(papszOptions, "PRECINCT_HEIGHT");
709 21 : if( pszOption != NULL )
710 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_PRECINCT_HEIGHT,
711 0 : (UINT32) atoi(pszOption) );
712 :
713 21 : pszOption = CSLFetchNameValue(papszOptions, "TILE_WIDTH");
714 21 : if( pszOption != NULL )
715 : SetParameter( CNCSJP2FileView::JP2_COMPRESS_TILE_WIDTH,
716 0 : (UINT32) atoi(pszOption) );
717 :
718 21 : pszOption = CSLFetchNameValue(papszOptions, "TILE_HEIGHT");
719 21 : if( pszOption != NULL )
720 : SetParameter( CNCSJP2FileView::JP2_COMPRESS_TILE_HEIGHT,
721 0 : (UINT32) atoi(pszOption) );
722 :
723 21 : pszOption = CSLFetchNameValue(papszOptions, "INCLUDE_SOP");
724 21 : if( pszOption != NULL )
725 : SetParameter( CNCSJP2FileView::JP2_COMPRESS_INCLUDE_SOP,
726 0 : (bool) CSLTestBoolean( pszOption ) );
727 :
728 21 : pszOption = CSLFetchNameValue(papszOptions, "INCLUDE_EPH");
729 21 : if( pszOption != NULL )
730 : SetParameter( CNCSJP2FileView::JP2_COMPRESS_INCLUDE_EPH,
731 0 : (bool) CSLTestBoolean( pszOption ) );
732 :
733 21 : pszOption = CSLFetchNameValue(papszOptions, "PROGRESSION");
734 21 : if( pszOption != NULL && EQUAL(pszOption,"LRCP") )
735 : SetParameter(
736 0 : CNCSJP2FileView::JP2_COMPRESS_PROGRESSION_LRCP );
737 :
738 21 : else if( pszOption != NULL && EQUAL(pszOption,"RLCP") )
739 : SetParameter(
740 0 : CNCSJP2FileView::JP2_COMPRESS_PROGRESSION_RLCP );
741 :
742 21 : else if( pszOption != NULL && EQUAL(pszOption,"RPCL") )
743 : SetParameter(
744 0 : CNCSJP2FileView::JP2_COMPRESS_PROGRESSION_RPCL );
745 :
746 21 : pszOption = CSLFetchNameValue(papszOptions, "GEODATA_USAGE");
747 21 : if( pszOption == NULL )
748 : // Default to supressing ECW SDK geodata, just use our own stuff.
749 21 : SetGeodataUsage( JP2_GEODATA_USE_NONE );
750 0 : else if( EQUAL(pszOption,"NONE") )
751 0 : SetGeodataUsage( JP2_GEODATA_USE_NONE );
752 0 : else if( EQUAL(pszOption,"PCS_ONLY") )
753 0 : SetGeodataUsage( JP2_GEODATA_USE_PCS_ONLY );
754 0 : else if( EQUAL(pszOption,"GML_ONLY") )
755 0 : SetGeodataUsage( JP2_GEODATA_USE_GML_ONLY );
756 0 : else if( EQUAL(pszOption,"PCS_GML") )
757 0 : SetGeodataUsage( JP2_GEODATA_USE_PCS_GML );
758 0 : else if( EQUAL(pszOption,"GML_PCS") )
759 0 : SetGeodataUsage( JP2_GEODATA_USE_GML_PCS );
760 0 : else if( EQUAL(pszOption,"ALL") )
761 0 : SetGeodataUsage( JP2_GEODATA_USE_GML_PCS_WLD );
762 :
763 21 : pszOption = CSLFetchNameValue(papszOptions, "DECOMPRESS_LAYERS");
764 21 : if( pszOption != NULL )
765 : SetParameter(
766 : CNCSJP2FileView::JP2_DECOMPRESS_LAYERS,
767 0 : (UINT32) atoi(pszOption) );
768 :
769 : pszOption = CSLFetchNameValue(papszOptions,
770 21 : "DECOMPRESS_RECONSTRUCTION_PARAMETER");
771 21 : if( pszOption != NULL )
772 : SetParameter(
773 : CNCSJP2FileView::JPC_DECOMPRESS_RECONSTRUCTION_PARAMETER,
774 0 : (IEEE4) atof(pszOption) );
775 : }
776 :
777 : /* -------------------------------------------------------------------- */
778 : /* Georeferencing. */
779 : /* -------------------------------------------------------------------- */
780 :
781 24 : psClient->fOriginX = 0.0;
782 24 : psClient->fOriginY = psClient->nSizeY;
783 24 : psClient->fCellIncrementX = 1.0;
784 24 : psClient->fCellIncrementY = -1.0;
785 24 : psClient->fCWRotationDegrees = 0.0;
786 :
787 24 : if( padfGeoTransform[2] != 0.0 || padfGeoTransform[4] != 0.0 )
788 : CPLError( CE_Warning, CPLE_NotSupported,
789 : "Rotational coefficients ignored, georeferencing of\n"
790 0 : "output ECW file will be incorrect.\n" );
791 : else
792 : {
793 24 : psClient->fOriginX = padfGeoTransform[0];
794 24 : psClient->fOriginY = padfGeoTransform[3];
795 24 : psClient->fCellIncrementX = padfGeoTransform[1];
796 24 : psClient->fCellIncrementY = padfGeoTransform[5];
797 : }
798 :
799 : /* -------------------------------------------------------------------- */
800 : /* Projection. */
801 : /* -------------------------------------------------------------------- */
802 : char szProjection[128];
803 : char szDatum[128];
804 : char szUnits[128];
805 :
806 24 : strcpy( szProjection, "RAW" );
807 24 : strcpy( szDatum, "RAW" );
808 :
809 24 : if( CSLFetchNameValue(papszOptions, "PROJ") != NULL )
810 : {
811 : strncpy( szProjection,
812 0 : CSLFetchNameValue(papszOptions, "PROJ"), sizeof(szProjection) );
813 0 : szProjection[sizeof(szProjection)-1] = 0;
814 : }
815 :
816 24 : if( CSLFetchNameValue(papszOptions, "DATUM") != NULL )
817 : {
818 0 : strncpy( szDatum, CSLFetchNameValue(papszOptions, "DATUM"), sizeof(szDatum) );
819 0 : szDatum[sizeof(szDatum)-1] = 0;
820 0 : if( EQUAL(szProjection,"RAW") )
821 0 : strcpy( szProjection, "GEODETIC" );
822 : }
823 :
824 24 : const char* pszUnits = CSLFetchNameValue(papszOptions, "UNITS");
825 24 : if( pszUnits != NULL )
826 : {
827 0 : psClient->eCellSizeUnits = ECWTranslateToCellSizeUnits(pszUnits);
828 : }
829 :
830 24 : if( EQUAL(szProjection,"RAW") && pszWKT != NULL )
831 : {
832 23 : ECWTranslateFromWKT( pszWKT, szProjection, sizeof(szProjection), szDatum, sizeof(szDatum), szUnits );
833 23 : psClient->eCellSizeUnits = ECWTranslateToCellSizeUnits(szUnits);
834 : }
835 :
836 24 : psClient->szDatum = szDatum;
837 24 : psClient->szProjection = szProjection;
838 :
839 : CPLDebug( "ECW", "Writing with PROJ=%s, DATUM=%s, UNITS=%s",
840 24 : szProjection, szDatum, ECWTranslateFromCellSizeUnits(psClient->eCellSizeUnits) );
841 :
842 : /* -------------------------------------------------------------------- */
843 : /* Setup GML and GeoTIFF information. */
844 : /* -------------------------------------------------------------------- */
845 24 : GDALJP2Metadata oJP2MD;
846 :
847 24 : oJP2MD.SetProjection( pszWKT );
848 24 : oJP2MD.SetGeoTransform( padfGeoTransform );
849 24 : oJP2MD.SetGCPs( nGCPCount, pasGCPList );
850 24 : if (bIsJPEG2000) {
851 :
852 21 : if( CSLFetchBoolean( papszOptions, "GMLJP2", TRUE ) )
853 18 : WriteJP2Box( oJP2MD.CreateGMLJP2(nXSize,nYSize) );
854 21 : if( CSLFetchBoolean( papszOptions, "GeoJP2", TRUE ) )
855 18 : WriteJP2Box( oJP2MD.CreateJP2GeoTIFF() );
856 :
857 : }
858 : /* -------------------------------------------------------------------- */
859 : /* We handle all jpeg2000 files via the VSIIOStream, but ECW */
860 : /* files cannot be done this way for some reason. */
861 : /* -------------------------------------------------------------------- */
862 24 : VSILFILE *fpVSIL = NULL;
863 :
864 24 : if( bIsJPEG2000 )
865 : {
866 21 : fpVSIL = VSIFOpenL( pszFilename, "wb+" );
867 21 : if( fpVSIL == NULL )
868 : {
869 : CPLError( CE_Failure, CPLE_OpenFailed,
870 2 : "Failed to open %s.", pszFilename );
871 2 : return CE_Failure;
872 : }
873 :
874 19 : m_OStream.Access( fpVSIL, TRUE, pszFilename, 0, -1 );
875 : }
876 :
877 : /* -------------------------------------------------------------------- */
878 : /* Check if we can enable large files. This option should only */
879 : /* be set when the application is adhering to one of the */
880 : /* ERMapper options for licensing larger than 500MB input */
881 : /* files. See Bug 767. This option no longer exists with */
882 : /* version 4+. */
883 : /* -------------------------------------------------------------------- */
884 : #if ECWSDK_VERSION < 40
885 22 : const char *pszLargeOK = CSLFetchNameValue(papszOptions, "LARGE_OK");
886 22 : if( pszLargeOK == NULL )
887 22 : pszLargeOK = "NO";
888 :
889 22 : pszLargeOK = CPLGetConfigOption( "ECW_LARGE_OK", pszLargeOK );
890 :
891 22 : if( CSLTestBoolean(pszLargeOK) )
892 : {
893 0 : CNCSFile::SetKeySize();
894 0 : CPLDebug( "ECW", "Large file generation enabled." );
895 : }
896 : #endif /* ECWSDK_VERSION < 40 */
897 :
898 : /* -------------------------------------------------------------------- */
899 : /* Set the file info. */
900 : /* -------------------------------------------------------------------- */
901 22 : CNCSError oError;
902 :
903 22 : oError = SetFileInfo( sFileInfo );
904 :
905 22 : if( oError.GetErrorNumber() == NCS_SUCCESS )
906 : {
907 22 : if( fpVSIL == NULL )
908 3 : oError = Open( (char *) pszFilename, false, true );
909 : else
910 19 : oError = CNCSJP2FileView::Open( &(m_OStream) );
911 : }
912 :
913 22 : if( oError.GetErrorNumber() == NCS_SUCCESS )
914 20 : return CE_None;
915 2 : else if( oError.GetErrorNumber() == NCS_INPUT_SIZE_EXCEEDED )
916 : {
917 : CPLError( CE_Failure, CPLE_AppDefined,
918 0 : "ECW SDK compress limit exceeded." );
919 0 : return CE_Failure;
920 : }
921 : else
922 : {
923 2 : ECWReportError(oError);
924 :
925 2 : return CE_Failure;
926 0 : }
927 : }
928 :
929 : /************************************************************************/
930 : /* ECWIsInputRGBColorSpace() */
931 : /************************************************************************/
932 :
933 24 : static int ECWIsInputRGBColorSpace(GDALDataset* poSrcDS)
934 : {
935 24 : int nBands = poSrcDS->GetRasterCount();
936 :
937 : /* -------------------------------------------------------------------- */
938 : /* Is the input RGB or RGBA? */
939 : /* -------------------------------------------------------------------- */
940 24 : int bRGBColorSpace = FALSE;
941 24 : int bRGB = FALSE;
942 24 : if ( nBands>=3 ) {
943 7 : bRGB = (poSrcDS->GetRasterBand(1)->GetColorInterpretation()
944 14 : == GCI_RedBand);
945 7 : bRGB &= (poSrcDS->GetRasterBand(2)->GetColorInterpretation()
946 14 : == GCI_GreenBand);
947 7 : bRGB &= (poSrcDS->GetRasterBand(3)->GetColorInterpretation()
948 14 : == GCI_BlueBand);
949 : }
950 24 : if( nBands == 3 )
951 : {
952 5 : bRGBColorSpace = bRGB;
953 : }
954 19 : else if( nBands == 4 && bRGB )
955 : {
956 1 : bRGBColorSpace = (poSrcDS->GetRasterBand(4)->GetColorInterpretation()
957 2 : == GCI_AlphaBand);
958 : }
959 :
960 24 : return bRGBColorSpace;
961 : }
962 :
963 : /************************************************************************/
964 : /* ECWCreateCopy() */
965 : /************************************************************************/
966 :
967 : static GDALDataset *
968 22 : ECWCreateCopy( const char * pszFilename, GDALDataset *poSrcDS,
969 : int bStrict, char ** papszOptions,
970 : GDALProgressFunc pfnProgress, void * pProgressData,
971 : int bIsJPEG2000 )
972 :
973 : {
974 22 : ECWInitialize();
975 :
976 : /* -------------------------------------------------------------------- */
977 : /* Get various values from the source dataset. */
978 : /* -------------------------------------------------------------------- */
979 22 : int nBands = poSrcDS->GetRasterCount();
980 22 : int nXSize = poSrcDS->GetRasterXSize();
981 22 : int nYSize = poSrcDS->GetRasterYSize();
982 :
983 22 : if (nBands == 0)
984 : {
985 : CPLError( CE_Failure, CPLE_NotSupported,
986 0 : "ECW driver does not support source dataset with zero band.\n");
987 0 : return NULL;
988 : }
989 :
990 22 : GDALDataType eType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
991 :
992 22 : const char *pszWKT = poSrcDS->GetProjectionRef();
993 22 : double adfGeoTransform[6] = { 0, 1, 0, 0, 0, 1 };;
994 :
995 22 : poSrcDS->GetGeoTransform( adfGeoTransform );
996 :
997 22 : if( poSrcDS->GetGCPCount() > 0 )
998 1 : pszWKT = poSrcDS->GetGCPProjection();
999 :
1000 : /* -------------------------------------------------------------------- */
1001 : /* Confirm the datatype is 8bit. It appears no other datatype */
1002 : /* is supported in ECW format. */
1003 : /* -------------------------------------------------------------------- */
1004 22 : bool bECWV3 = false;
1005 :
1006 : #if ECWSDK_VERSION >= 50
1007 : if (bIsJPEG2000 == FALSE){
1008 : const char* pszOption = CSLFetchNameValue(papszOptions, "ECW_FORMAT_VERSION");
1009 : if( pszOption != NULL )
1010 : {
1011 : bECWV3 = (3 == atoi(pszOption));
1012 : }
1013 : }
1014 : #endif
1015 22 : if(! (eType == GDT_Byte || (bECWV3 && eType==GDT_UInt16 ) || bIsJPEG2000 ) )
1016 : {
1017 0 : if( bStrict )
1018 : {
1019 : CPLError( CE_Failure, CPLE_AppDefined,
1020 : "Attempt to create ECW file with pixel data type %s failed.\n"
1021 : "Only Byte data type supported for ECW version 2 files. ECW version 3 files supports UInt16 and Int16 as well.",
1022 0 : GDALGetDataTypeName( eType ) );
1023 : }
1024 : else
1025 : {
1026 : CPLError( CE_Warning, CPLE_AppDefined,
1027 : "ECW does not support data type, ignoring request for %s.",
1028 0 : GDALGetDataTypeName( eType ) );
1029 0 : eType = GDT_Byte;
1030 : }
1031 : }
1032 :
1033 : /* -------------------------------------------------------------------- */
1034 : /* Is the input RGB or RGBA? */
1035 : /* -------------------------------------------------------------------- */
1036 22 : int bRGBColorSpace = ECWIsInputRGBColorSpace(poSrcDS);
1037 :
1038 : /* -------------------------------------------------------------------- */
1039 : /* Setup the compressor. */
1040 : /* -------------------------------------------------------------------- */
1041 22 : GDALECWCompressor oCompressor;
1042 22 : CNCSError oErr;
1043 :
1044 22 : oCompressor.pfnProgress = pfnProgress;
1045 22 : oCompressor.pProgressData = pProgressData;
1046 22 : oCompressor.m_poSrcDS = poSrcDS;
1047 :
1048 22 : if( !pfnProgress( 0.0, NULL, pProgressData ) )
1049 0 : return NULL;
1050 :
1051 22 : char** papszBandDescriptions = (char**) CPLMalloc(nBands * sizeof(char*));
1052 : int i;
1053 62 : for (i=0;i<nBands;i++){
1054 : /* Make a copy since ECWGetColorInterpretationName() can return a string generated */
1055 : /* by CPLSPrintf(), which has just a few rotating entries. */
1056 40 : papszBandDescriptions[i] = CPLStrdup(ECWGetColorInterpretationName(poSrcDS->GetRasterBand(i+1)->GetColorInterpretation(), i));
1057 : }
1058 44 : if( oCompressor.Initialize( pszFilename, papszOptions,
1059 : nXSize, nYSize, nBands, papszBandDescriptions, bRGBColorSpace,
1060 : eType, pszWKT, adfGeoTransform,
1061 22 : poSrcDS->GetGCPCount(),
1062 22 : poSrcDS->GetGCPs(),
1063 : bIsJPEG2000 )
1064 : != CE_None )
1065 : {
1066 8 : for (i=0;i<nBands;i++)
1067 4 : CPLFree(papszBandDescriptions[i]);
1068 4 : CPLFree(papszBandDescriptions);
1069 4 : return NULL;
1070 : }
1071 :
1072 : /* -------------------------------------------------------------------- */
1073 : /* Start the compression. */
1074 : /* -------------------------------------------------------------------- */
1075 18 : oErr = oCompressor.Write();
1076 :
1077 18 : if( oErr.GetErrorNumber() != NCS_SUCCESS )
1078 : {
1079 0 : ECWReportError(oErr);
1080 0 : return NULL;
1081 : }
1082 :
1083 : /* -------------------------------------------------------------------- */
1084 : /* Cleanup, and return read-only handle. */
1085 : /* -------------------------------------------------------------------- */
1086 18 : oCompressor.CloseDown();
1087 54 : for (i=0;i<nBands;i++)
1088 36 : CPLFree(papszBandDescriptions[i]);
1089 18 : CPLFree(papszBandDescriptions);
1090 18 : pfnProgress( 1.001, NULL, pProgressData );
1091 :
1092 : /* -------------------------------------------------------------------- */
1093 : /* Re-open dataset, and copy any auxilary pam information. */
1094 : /* -------------------------------------------------------------------- */
1095 18 : GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
1096 : GDALPamDataset *poDS;
1097 :
1098 18 : if (bIsJPEG2000)
1099 17 : poDS = (GDALPamDataset*) ECWDatasetOpenJPEG2000(&oOpenInfo);
1100 : else
1101 1 : poDS = (GDALPamDataset*) ECWDataset::OpenECW(&oOpenInfo);
1102 :
1103 18 : if( poDS )
1104 18 : poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT );
1105 :
1106 18 : return poDS;
1107 : }
1108 :
1109 : /************************************************************************/
1110 : /* ECWCreateCopyECW() */
1111 : /************************************************************************/
1112 :
1113 : GDALDataset *
1114 19 : ECWCreateCopyECW( const char * pszFilename, GDALDataset *poSrcDS,
1115 : int bStrict, char ** papszOptions,
1116 : GDALProgressFunc pfnProgress, void * pProgressData )
1117 :
1118 : {
1119 19 : int nBands = poSrcDS->GetRasterCount();
1120 19 : if (nBands == 0)
1121 : {
1122 : CPLError( CE_Failure, CPLE_NotSupported,
1123 1 : "ECW driver does not support source dataset with zero band.\n");
1124 1 : return NULL;
1125 : }
1126 :
1127 18 : if( !EQUAL(CPLGetExtension(pszFilename),"ecw") )
1128 : {
1129 : CPLError( CE_Failure, CPLE_AppDefined,
1130 : "ECW driver does not support creating ECW files\n"
1131 0 : "with an extension other than .ecw" );
1132 0 : return NULL;
1133 : }
1134 18 : bool bECWV3 = false;
1135 :
1136 : #if ECWSDK_VERSION >= 50
1137 :
1138 : const char* pszOption = CSLFetchNameValue(papszOptions, "ECW_FORMAT_VERSION");
1139 : if( pszOption != NULL )
1140 : {
1141 : bECWV3 = (3 == atoi(pszOption));
1142 : }
1143 :
1144 : #endif
1145 :
1146 18 : if( poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte
1147 : && !(bECWV3 && (poSrcDS->GetRasterBand(1)->GetRasterDataType() == GDT_UInt16))
1148 : && bStrict )
1149 : {
1150 :
1151 10 : if (poSrcDS->GetRasterBand(1)->GetRasterDataType() == GDT_UInt16 || poSrcDS->GetRasterBand(1)->GetRasterDataType() == GDT_Int16){
1152 : CPLError( CE_Failure, CPLE_NotSupported,
1153 : "ECW driver doesn't support data type %s. "
1154 : "Enable ECW version 3 write by using ECWV3=ON option.\n",
1155 : GDALGetDataTypeName(
1156 2 : poSrcDS->GetRasterBand(1)->GetRasterDataType()) );
1157 : }else{
1158 : CPLError( CE_Failure, CPLE_NotSupported,
1159 : "ECW driver doesn't support data type %s. "
1160 : "Only eight or sixteen bit bands supported. \n",
1161 : GDALGetDataTypeName(
1162 8 : poSrcDS->GetRasterBand(1)->GetRasterDataType()) );
1163 : }
1164 :
1165 10 : return NULL;
1166 : }
1167 :
1168 8 : if( poSrcDS->GetRasterXSize() < 128 || poSrcDS->GetRasterYSize() < 128 )
1169 : {
1170 : CPLError( CE_Failure, CPLE_NotSupported,
1171 : "ECW driver requires image to be at least 128x128,\n"
1172 : "the source image is %dx%d.\n",
1173 : poSrcDS->GetRasterXSize(),
1174 5 : poSrcDS->GetRasterYSize() );
1175 :
1176 5 : return NULL;
1177 : }
1178 :
1179 3 : if (poSrcDS->GetRasterBand(1)->GetColorTable() != NULL)
1180 : {
1181 : CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
1182 : "ECW driver ignores color table. "
1183 : "The source raster band will be considered as grey level.\n"
1184 0 : "Consider using color table expansion (-expand option in gdal_translate)\n");
1185 0 : if (bStrict)
1186 0 : return NULL;
1187 : }
1188 :
1189 : return ECWCreateCopy( pszFilename, poSrcDS, bStrict, papszOptions,
1190 3 : pfnProgress, pProgressData, FALSE );
1191 : }
1192 :
1193 : /************************************************************************/
1194 : /* ECWCreateCopyJPEG2000() */
1195 : /************************************************************************/
1196 :
1197 : GDALDataset *
1198 24 : ECWCreateCopyJPEG2000( const char * pszFilename, GDALDataset *poSrcDS,
1199 : int bStrict, char ** papszOptions,
1200 : GDALProgressFunc pfnProgress, void * pProgressData )
1201 :
1202 : {
1203 24 : int nBands = poSrcDS->GetRasterCount();
1204 24 : if (nBands == 0)
1205 : {
1206 : CPLError( CE_Failure, CPLE_NotSupported,
1207 1 : "JP2ECW driver does not support source dataset with zero band.\n");
1208 1 : return NULL;
1209 : }
1210 :
1211 23 : if( EQUAL(CPLGetExtension(pszFilename),"ecw") )
1212 : {
1213 : CPLError( CE_Failure, CPLE_AppDefined,
1214 : "JP2ECW driver does not support creating JPEG2000 files\n"
1215 0 : "with a .ecw extension. Please use anything else." );
1216 0 : return NULL;
1217 : }
1218 :
1219 23 : if( poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte
1220 : && poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte
1221 : && poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Int16
1222 : && poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_UInt16
1223 : && poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Int32
1224 : && poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_UInt32
1225 : && poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Float32
1226 : && poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Float64
1227 : && bStrict )
1228 : {
1229 : CPLError( CE_Failure, CPLE_NotSupported,
1230 : "JP2ECW driver doesn't support data type %s. ",
1231 : GDALGetDataTypeName(
1232 4 : poSrcDS->GetRasterBand(1)->GetRasterDataType()) );
1233 :
1234 4 : return NULL;
1235 : }
1236 :
1237 19 : if (poSrcDS->GetRasterBand(1)->GetColorTable() != NULL)
1238 : {
1239 : CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
1240 : "JP2ECW driver ignores color table. "
1241 : "The source raster band will be considered as grey level.\n"
1242 0 : "Consider using color table expansion (-expand option in gdal_translate)\n");
1243 0 : if (bStrict)
1244 0 : return NULL;
1245 : }
1246 :
1247 : return ECWCreateCopy( pszFilename, poSrcDS, bStrict, papszOptions,
1248 19 : pfnProgress, pProgressData, TRUE );
1249 : }
1250 :
1251 : /************************************************************************/
1252 : /************************************************************************
1253 :
1254 : ECW/JPEG200 Create() Support
1255 : ----------------------------
1256 :
1257 : The remainder of the file is code to implement the Create() method.
1258 : New dataset and raster band classes are defined specifically for the
1259 : purpose of being write-only. In particular, you cannot read back data
1260 : from these datasets, and writing must occur in a pretty specific order.
1261 :
1262 : That is, you need to write all metadata (projection, georef, etc) first
1263 : and then write the image data. All bands data for the first scanline
1264 : should be written followed by all bands for the second scanline and so on.
1265 :
1266 : Creation supports the same virtual subfile names as CreateCopy() supports.
1267 :
1268 : ************************************************************************/
1269 : /************************************************************************/
1270 :
1271 : /************************************************************************/
1272 : /* ==================================================================== */
1273 : /* ECWWriteDataset */
1274 : /* ==================================================================== */
1275 : /************************************************************************/
1276 :
1277 : class ECWWriteRasterBand;
1278 :
1279 : class CPL_DLL ECWWriteDataset : public GDALDataset
1280 : {
1281 : friend class ECWWriteRasterBand;
1282 :
1283 : char *pszFilename;
1284 :
1285 : int bIsJPEG2000;
1286 : GDALDataType eDataType;
1287 : char **papszOptions;
1288 :
1289 : char *pszProjection;
1290 : double adfGeoTransform[6];
1291 :
1292 : GDALECWCompressor oCompressor;
1293 : int bCrystalized;
1294 :
1295 : int nLoadedLine;
1296 : GByte *pabyBILBuffer;
1297 :
1298 : CPLErr Crystalize();
1299 : CPLErr FlushLine();
1300 :
1301 : public:
1302 : ECWWriteDataset( const char *, int, int, int,
1303 : GDALDataType, char **papszOptions,
1304 : int );
1305 : ~ECWWriteDataset();
1306 :
1307 : virtual void FlushCache( void );
1308 :
1309 : virtual CPLErr GetGeoTransform( double * );
1310 : virtual const char* GetProjectionRef();
1311 : virtual CPLErr SetGeoTransform( double * );
1312 : virtual CPLErr SetProjection( const char *pszWKT );
1313 : };
1314 :
1315 : /************************************************************************/
1316 : /* ==================================================================== */
1317 : /* ECWWriteRasterBand */
1318 : /* ==================================================================== */
1319 : /************************************************************************/
1320 :
1321 : class ECWWriteRasterBand : public GDALRasterBand
1322 59 : {
1323 : friend class ECWWriteDataset;
1324 :
1325 : // NOTE: poDS may be altered for NITF/JPEG2000 files!
1326 : ECWWriteDataset *poGDS;
1327 :
1328 : GDALColorInterp eInterp;
1329 :
1330 : public:
1331 :
1332 : ECWWriteRasterBand( ECWWriteDataset *, int );
1333 :
1334 6 : virtual CPLErr SetColorInterpretation( GDALColorInterp eInterpIn )
1335 6 : { eInterp = eInterpIn; return CE_None; }
1336 6 : virtual GDALColorInterp GetColorInterpretation()
1337 6 : { return eInterp; }
1338 :
1339 : virtual CPLErr IReadBlock( int, int, void * );
1340 : virtual CPLErr IWriteBlock( int, int, void * );
1341 : };
1342 :
1343 : /************************************************************************/
1344 : /* ECWWriteDataset() */
1345 : /************************************************************************/
1346 :
1347 28 : ECWWriteDataset::ECWWriteDataset( const char *pszFilename,
1348 : int nXSize, int nYSize, int nBandCount,
1349 : GDALDataType eType,
1350 28 : char **papszOptions, int bIsJPEG2000 )
1351 :
1352 : {
1353 28 : bCrystalized = FALSE;
1354 28 : pabyBILBuffer = NULL;
1355 28 : nLoadedLine = -1;
1356 :
1357 28 : eAccess = GA_Update;
1358 :
1359 28 : this->bIsJPEG2000 = bIsJPEG2000;
1360 28 : this->eDataType = eType;
1361 28 : this->papszOptions = CSLDuplicate( papszOptions );
1362 28 : this->pszFilename = CPLStrdup( pszFilename );
1363 :
1364 28 : nRasterXSize = nXSize;
1365 28 : nRasterYSize = nYSize;
1366 28 : pszProjection = NULL;
1367 :
1368 28 : adfGeoTransform[0] = 0.0;
1369 28 : adfGeoTransform[1] = 1.0;
1370 28 : adfGeoTransform[2] = 0.0;
1371 28 : adfGeoTransform[3] = 0.0;
1372 28 : adfGeoTransform[4] = 0.0;
1373 28 : adfGeoTransform[5] = 1.0;
1374 :
1375 : // create band objects.
1376 87 : for( int iBand = 1; iBand <= nBandCount; iBand++ )
1377 : {
1378 59 : SetBand( iBand, new ECWWriteRasterBand( this, iBand ) );
1379 : }
1380 28 : }
1381 :
1382 : /************************************************************************/
1383 : /* ~ECWWriteDataset() */
1384 : /************************************************************************/
1385 :
1386 28 : ECWWriteDataset::~ECWWriteDataset()
1387 :
1388 : {
1389 28 : FlushCache();
1390 :
1391 28 : if( bCrystalized )
1392 : {
1393 2 : if( nLoadedLine == nRasterYSize - 1 )
1394 2 : FlushLine();
1395 2 : oCompressor.CloseDown();
1396 : }
1397 :
1398 28 : CPLFree( pabyBILBuffer );
1399 28 : CPLFree( pszProjection );
1400 28 : CSLDestroy( papszOptions );
1401 28 : CPLFree( pszFilename );
1402 28 : }
1403 :
1404 : /************************************************************************/
1405 : /* FlushCache() */
1406 : /************************************************************************/
1407 :
1408 29 : void ECWWriteDataset::FlushCache()
1409 :
1410 : {
1411 29 : BlockBasedFlushCache();
1412 29 : }
1413 :
1414 : /************************************************************************/
1415 : /* GetProjectionRef() */
1416 : /************************************************************************/
1417 :
1418 0 : const char* ECWWriteDataset::GetProjectionRef()
1419 : {
1420 0 : return pszProjection;
1421 : }
1422 :
1423 : /************************************************************************/
1424 : /* GetGeoTransform() */
1425 : /************************************************************************/
1426 :
1427 26 : CPLErr ECWWriteDataset::GetGeoTransform( double *padfGeoTransform )
1428 :
1429 : {
1430 26 : memcpy( padfGeoTransform, adfGeoTransform, sizeof(double) * 6 );
1431 26 : return CE_None;
1432 : }
1433 :
1434 : /************************************************************************/
1435 : /* SetGeoTransform() */
1436 : /************************************************************************/
1437 :
1438 27 : CPLErr ECWWriteDataset::SetGeoTransform( double *padfGeoTransform )
1439 :
1440 : {
1441 27 : memcpy( adfGeoTransform, padfGeoTransform, sizeof(double) * 6 );
1442 27 : return CE_None;
1443 : }
1444 :
1445 : /************************************************************************/
1446 : /* SetProjection() */
1447 : /************************************************************************/
1448 :
1449 27 : CPLErr ECWWriteDataset::SetProjection( const char *pszWKT )
1450 :
1451 : {
1452 27 : CPLFree( pszProjection );
1453 27 : pszProjection = CPLStrdup( pszWKT );
1454 :
1455 27 : return CE_None;
1456 : }
1457 :
1458 :
1459 : /************************************************************************/
1460 : /* Crystalize() */
1461 : /************************************************************************/
1462 :
1463 2 : CPLErr ECWWriteDataset::Crystalize()
1464 :
1465 : {
1466 2 : int nWordSize = GDALGetDataTypeSize( eDataType ) / 8;
1467 :
1468 : CPLErr eErr;
1469 2 : CNCSError oError;
1470 :
1471 2 : if( bCrystalized )
1472 0 : return CE_None;
1473 :
1474 2 : const char **paszBandDescriptions = (const char **) CPLMalloc(nBands * sizeof(char *));
1475 6 : for (int i = 0; i < GetRasterCount(); i++ ){
1476 4 : paszBandDescriptions[i] = GetRasterBand(i+1)->GetDescription();
1477 : }
1478 :
1479 2 : int bRGBColorSpace = ECWIsInputRGBColorSpace(this);
1480 :
1481 : eErr = oCompressor.Initialize( pszFilename, papszOptions,
1482 : nRasterXSize, nRasterYSize, nBands,paszBandDescriptions, bRGBColorSpace,
1483 : eDataType,
1484 : pszProjection, adfGeoTransform,
1485 : 0, NULL,
1486 2 : bIsJPEG2000 );
1487 :
1488 2 : if( eErr == CE_None )
1489 2 : bCrystalized = TRUE;
1490 :
1491 2 : nLoadedLine = -1;
1492 2 : pabyBILBuffer = (GByte *) CPLMalloc( nWordSize * nBands * nRasterXSize );
1493 :
1494 2 : CPLFree(paszBandDescriptions);
1495 :
1496 2 : return eErr;
1497 : }
1498 :
1499 : /************************************************************************/
1500 : /* FlushLine() */
1501 : /************************************************************************/
1502 :
1503 202 : CPLErr ECWWriteDataset::FlushLine()
1504 :
1505 : {
1506 202 : int nWordSize = GDALGetDataTypeSize( eDataType ) / 8;
1507 : CPLErr eErr;
1508 :
1509 : /* -------------------------------------------------------------------- */
1510 : /* Crystalize if not already done. */
1511 : /* -------------------------------------------------------------------- */
1512 202 : if( !bCrystalized )
1513 : {
1514 2 : eErr = Crystalize();
1515 :
1516 2 : if( eErr != CE_None )
1517 0 : return eErr;
1518 : }
1519 :
1520 : /* -------------------------------------------------------------------- */
1521 : /* Write out the currently loaded line. */
1522 : /* -------------------------------------------------------------------- */
1523 202 : if( nLoadedLine != -1 )
1524 : {
1525 :
1526 : void **papOutputLine;
1527 :
1528 200 : papOutputLine = (void **) CPLMalloc(sizeof(void*) * nBands);
1529 600 : for( int i = 0; i < nBands; i++ )
1530 400 : papOutputLine[i] =
1531 400 : (void *) (pabyBILBuffer + i * nWordSize * nRasterXSize);
1532 :
1533 :
1534 200 : eErr = oCompressor.WriteLineBIL( (UINT16) nBands, papOutputLine );
1535 200 : CPLFree( papOutputLine );
1536 200 : if (eErr!=CE_None){
1537 0 : return eErr;
1538 : }
1539 : }
1540 :
1541 : /* -------------------------------------------------------------------- */
1542 : /* Clear the buffer and increment the "current line" indicator. */
1543 : /* -------------------------------------------------------------------- */
1544 202 : memset( pabyBILBuffer, 0, nWordSize * nRasterXSize * nBands );
1545 202 : nLoadedLine++;
1546 :
1547 202 : return CE_None;
1548 : }
1549 :
1550 :
1551 : /************************************************************************/
1552 : /* ==================================================================== */
1553 : /* ECWWriteRasterBand */
1554 : /* ==================================================================== */
1555 : /************************************************************************/
1556 :
1557 : /************************************************************************/
1558 : /* ECWWriteRasterBand() */
1559 : /************************************************************************/
1560 :
1561 59 : ECWWriteRasterBand::ECWWriteRasterBand( ECWWriteDataset *poDSIn,
1562 59 : int nBandIn )
1563 :
1564 : {
1565 59 : nBand = nBandIn;
1566 59 : poDS = poDSIn;
1567 59 : poGDS = poDSIn;
1568 59 : nBlockXSize = poDSIn->GetRasterXSize();
1569 59 : nBlockYSize = 1;
1570 59 : eDataType = poDSIn->eDataType;
1571 59 : eInterp = GCI_Undefined;
1572 59 : }
1573 :
1574 : /************************************************************************/
1575 : /* IReadBlock() */
1576 : /************************************************************************/
1577 :
1578 0 : CPLErr ECWWriteRasterBand::IReadBlock( int nBlockX, int nBlockY,
1579 : void *pBuffer )
1580 :
1581 : {
1582 0 : int nWordSize = GDALGetDataTypeSize( eDataType ) / 8;
1583 :
1584 : // We zero stuff out here, but we can't really read stuff from
1585 : // a write only stream.
1586 :
1587 0 : memset( pBuffer, 0, nBlockXSize * nWordSize );
1588 :
1589 0 : return CE_None;
1590 : }
1591 :
1592 : /************************************************************************/
1593 : /* IWriteBlock() */
1594 : /************************************************************************/
1595 :
1596 400 : CPLErr ECWWriteRasterBand::IWriteBlock( int nBlockX, int nBlockY,
1597 : void *pBuffer )
1598 :
1599 : {
1600 400 : int nWordSize = GDALGetDataTypeSize( eDataType ) / 8;
1601 : CPLErr eErr;
1602 :
1603 : /* -------------------------------------------------------------------- */
1604 : /* Flush previous line if needed. */
1605 : /* -------------------------------------------------------------------- */
1606 400 : if( nBlockY == poGDS->nLoadedLine + 1 )
1607 : {
1608 200 : eErr = poGDS->FlushLine();
1609 200 : if( eErr != CE_None )
1610 0 : return eErr;
1611 : }
1612 :
1613 : /* -------------------------------------------------------------------- */
1614 : /* Blow a gasket if we have been asked to write something out */
1615 : /* of order. */
1616 : /* -------------------------------------------------------------------- */
1617 400 : if( nBlockY != poGDS->nLoadedLine )
1618 : {
1619 : CPLError( CE_Failure, CPLE_AppDefined,
1620 : "Apparent attempt to write to ECW non-sequentially.\n"
1621 : "Loaded line is %d, but %d of band %d was written to.",
1622 0 : poGDS->nLoadedLine, nBlockY, nBand );
1623 0 : return CE_Failure;
1624 : }
1625 :
1626 : /* -------------------------------------------------------------------- */
1627 : /* Copy passed data into current line buffer. */
1628 : /* -------------------------------------------------------------------- */
1629 : memcpy( poGDS->pabyBILBuffer + (nBand-1) * nWordSize * nRasterXSize,
1630 : pBuffer,
1631 400 : nWordSize * nRasterXSize );
1632 :
1633 400 : return CE_None;
1634 : }
1635 :
1636 : /************************************************************************/
1637 : /* ECWCreateJPEG2000() */
1638 : /************************************************************************/
1639 :
1640 : GDALDataset *
1641 28 : ECWCreateJPEG2000(const char *pszFilename, int nXSize, int nYSize, int nBands,
1642 : GDALDataType eType, char **papszOptions )
1643 :
1644 : {
1645 28 : ECWInitialize();
1646 :
1647 : return new ECWWriteDataset( pszFilename, nXSize, nYSize, nBands,
1648 28 : eType, papszOptions, TRUE );
1649 : }
1650 :
1651 : /************************************************************************/
1652 : /* ECWCreateECW() */
1653 : /************************************************************************/
1654 :
1655 : GDALDataset *
1656 0 : ECWCreateECW( const char *pszFilename, int nXSize, int nYSize, int nBands,
1657 : GDALDataType eType, char **papszOptions )
1658 :
1659 : {
1660 0 : ECWInitialize();
1661 :
1662 : return new ECWWriteDataset( pszFilename, nXSize, nYSize, nBands,
1663 0 : eType, papszOptions, FALSE );
1664 : }
1665 :
1666 : #endif /* def FRMT_ecw && def HAVE_COMPRESS */
|