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