1 : /******************************************************************************
2 : * $Id: ecwcreatecopy.cpp 23601 2011-12-19 20:04:51Z 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 23601 2011-12-19 20:04:51Z 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 100 : GDALECWCompressor::GDALECWCompressor()
86 :
87 : {
88 100 : m_poSrcDS = NULL;
89 100 : m_nPercentComplete = -1;
90 100 : m_bCancelled = FALSE;
91 100 : pfnProgress = GDALDummyProgress;
92 100 : pProgressData = NULL;
93 100 : papoJP2UserBox = NULL;
94 100 : nJP2UserBox = 0;
95 100 : }
96 :
97 : /************************************************************************/
98 : /* ~GDALECWCompressor() */
99 : /************************************************************************/
100 :
101 100 : GDALECWCompressor::~GDALECWCompressor()
102 :
103 : {
104 : int i;
105 180 : for(i=0;i<nJP2UserBox;i++)
106 80 : delete papoJP2UserBox[i];
107 100 : CPLFree(papoJP2UserBox);
108 100 : }
109 :
110 : /************************************************************************/
111 : /* CloseDown() */
112 : /************************************************************************/
113 :
114 40 : CPLErr GDALECWCompressor::CloseDown()
115 :
116 : {
117 120 : for( int i = 0; i < sFileInfo.nBands; i++ )
118 : {
119 80 : CPLFree( sFileInfo.pBands[i].szDesc );
120 : }
121 40 : CPLFree( sFileInfo.pBands );
122 :
123 40 : Close( true );
124 40 : m_OStream.Close();
125 :
126 40 : return CE_None;
127 : }
128 :
129 : /************************************************************************/
130 : /* WriteReadLine() */
131 : /************************************************************************/
132 :
133 3428 : CNCSError GDALECWCompressor::WriteReadLine( UINT32 nNextLine,
134 : void **ppInputArray )
135 :
136 : {
137 : int iBand, *panBandMap;
138 : CPLErr eErr;
139 : GByte *pabyLineBuf;
140 3428 : int nWordSize = GDALGetDataTypeSize( eWorkDT ) / 8;
141 :
142 3428 : panBandMap = (int *) CPLMalloc(sizeof(int) * sFileInfo.nBands);
143 9196 : for( iBand = 0; iBand < sFileInfo.nBands; iBand++ )
144 5768 : panBandMap[iBand] = iBand+1;
145 :
146 : pabyLineBuf = (GByte *) CPLMalloc( sFileInfo.nSizeX * sFileInfo.nBands
147 3428 : * 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 3428 : nWordSize, 0, nWordSize * sFileInfo.nSizeX );
154 :
155 9196 : for( iBand = 0; iBand < (int) sFileInfo.nBands; iBand++ )
156 : {
157 : memcpy( ppInputArray[iBand],
158 : pabyLineBuf + nWordSize * sFileInfo.nSizeX * iBand,
159 5768 : nWordSize * sFileInfo.nSizeX );
160 : }
161 :
162 3428 : CPLFree( pabyLineBuf );
163 3428 : CPLFree( panBandMap );
164 :
165 3428 : if( eErr == CE_None )
166 3428 : return NCS_SUCCESS;
167 : else
168 0 : return NCS_FILEIO_ERROR;
169 : }
170 :
171 : /************************************************************************/
172 : /* WriteStatus() */
173 : /************************************************************************/
174 :
175 3428 : void GDALECWCompressor::WriteStatus( UINT32 nCurrentLine )
176 :
177 : {
178 : m_bCancelled =
179 : !pfnProgress( nCurrentLine / (float) sFileInfo.nSizeY,
180 3428 : NULL, pProgressData );
181 3428 : }
182 :
183 : /************************************************************************/
184 : /* WriteCancel() */
185 : /************************************************************************/
186 :
187 3428 : bool GDALECWCompressor::WriteCancel()
188 :
189 : {
190 3428 : 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 84 : CPLErr GDALECWCompressor::WriteJP2Box( GDALJP2Box * poBox )
391 :
392 : {
393 : JP2UserBox *poECWBox;
394 :
395 84 : if( poBox == NULL )
396 4 : return CE_None;
397 :
398 80 : poECWBox = new JP2UserBox();
399 80 : memcpy( &(poECWBox->m_nTBox), poBox->GetType(), 4 );
400 80 : CPL_MSBPTR32( &(poECWBox->m_nTBox) );
401 :
402 : poECWBox->SetData( (int) poBox->GetDataLength(),
403 80 : poBox->GetWritableData() );
404 :
405 80 : AddBox( poECWBox );
406 :
407 160 : delete poBox;
408 :
409 : papoJP2UserBox =(JP2UserBox**) CPLRealloc(papoJP2UserBox,
410 80 : (nJP2UserBox + 1) * sizeof(JP2UserBox*));
411 80 : papoJP2UserBox[nJP2UserBox] = poECWBox;
412 80 : nJP2UserBox ++;
413 :
414 80 : return CE_None;
415 : }
416 :
417 :
418 : /************************************************************************/
419 : /* Initialize() */
420 : /* */
421 : /* Initialize compressor output. */
422 : /************************************************************************/
423 :
424 48 : 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 48 : 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 48 : if( nBands > 1 )
478 20 : fTargetCompression = 95.0;
479 : else
480 28 : fTargetCompression = 90.0;
481 :
482 48 : if( CSLFetchNameValue(papszOptions, "TARGET") != NULL )
483 : {
484 : fTargetCompression = (float)
485 12 : 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 12 : 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 48 : NCSFileViewFileInfoEx *psClient = &(sFileInfo);
505 :
506 48 : psClient->nBands = (UINT16) nBands;
507 48 : psClient->nSizeX = nXSize;
508 48 : psClient->nSizeY = nYSize;
509 48 : psClient->nCompressionRate = (UINT16) MAX(1,100/(100-fTargetCompression));
510 48 : psClient->eCellSizeUnits = ECW_CELL_UNITS_METERS;
511 :
512 48 : if( nBands == 1 )
513 28 : psClient->eColorSpace = NCSCS_GREYSCALE;
514 20 : else if( nBands == 3 )
515 10 : psClient->eColorSpace = NCSCS_sRGB;
516 : #if ECWSDK_VERSION >= 40
517 : else if( nBands == 4 && bRGBA )
518 : psClient->eColorSpace = NCSCS_sRGB;
519 : #endif
520 : else
521 10 : psClient->eColorSpace = NCSCS_MULTIBAND;
522 :
523 : /* -------------------------------------------------------------------- */
524 : /* Figure out the data type. */
525 : /* -------------------------------------------------------------------- */
526 48 : int bSigned = FALSE;
527 48 : int nBits = 8;
528 48 : eWorkDT = eType;
529 :
530 48 : switch( eWorkDT )
531 : {
532 : case GDT_Byte:
533 36 : psClient->eCellType = NCSCT_UINT8;
534 36 : nBits = 8;
535 36 : bSigned = FALSE;
536 36 : break;
537 :
538 : case GDT_UInt16:
539 2 : psClient->eCellType = NCSCT_UINT16;
540 2 : nBits = 16;
541 2 : bSigned = FALSE;
542 2 : break;
543 :
544 : case GDT_UInt32:
545 2 : psClient->eCellType = NCSCT_UINT32;
546 2 : nBits = 32;
547 2 : bSigned = FALSE;
548 2 : break;
549 :
550 : case GDT_Int16:
551 4 : psClient->eCellType = NCSCT_INT16;
552 4 : nBits = 16;
553 4 : bSigned = TRUE;
554 4 : break;
555 :
556 : case GDT_Int32:
557 2 : psClient->eCellType = NCSCT_INT32;
558 2 : nBits = 32;
559 2 : bSigned = TRUE;
560 2 : break;
561 :
562 : case GDT_Float32:
563 2 : psClient->eCellType = NCSCT_IEEE4;
564 2 : nBits = 32;
565 2 : bSigned = TRUE;
566 2 : break;
567 :
568 : case GDT_Float64:
569 0 : psClient->eCellType = NCSCT_IEEE8;
570 0 : nBits = 64;
571 0 : bSigned = TRUE;
572 0 : break;
573 :
574 : default:
575 : // We treat complex types as float.
576 0 : psClient->eCellType = NCSCT_IEEE4;
577 0 : nBits = 32;
578 0 : bSigned = TRUE;
579 0 : eWorkDT = GDT_Float32;
580 : break;
581 : }
582 :
583 : /* -------------------------------------------------------------------- */
584 : /* Create band information structures. */
585 : /* -------------------------------------------------------------------- */
586 : int iBand;
587 :
588 : psClient->pBands = (NCSFileBandInfo *)
589 48 : CPLMalloc( sizeof(NCSFileBandInfo) * nBands );
590 136 : for( iBand = 0; iBand < nBands; iBand++ )
591 : {
592 88 : psClient->pBands[iBand].nBits = (UINT8) nBits;
593 88 : psClient->pBands[iBand].bSigned = (BOOLEAN)bSigned;
594 88 : psClient->pBands[iBand].szDesc = CPLStrdup(
595 88 : CPLSPrintf("Band%d",iBand+1) );
596 : }
597 :
598 : /* -------------------------------------------------------------------- */
599 : /* Allow CNCSFile::SetParameter() requests. */
600 : /* -------------------------------------------------------------------- */
601 : const char *pszOption;
602 :
603 48 : if( bIsJPEG2000 )
604 : {
605 42 : pszOption = CSLFetchNameValue(papszOptions, "PROFILE");
606 42 : if( pszOption != NULL && EQUAL(pszOption,"BASELINE_0") )
607 : SetParameter(
608 0 : CNCSJP2FileView::JP2_COMPRESS_PROFILE_BASELINE_0 );
609 42 : else if( pszOption != NULL && EQUAL(pszOption,"BASELINE_1") )
610 : SetParameter(
611 0 : CNCSJP2FileView::JP2_COMPRESS_PROFILE_BASELINE_1 );
612 42 : else if( pszOption != NULL && EQUAL(pszOption,"BASELINE_2") )
613 : SetParameter(
614 0 : CNCSJP2FileView::JP2_COMPRESS_PROFILE_BASELINE_2 );
615 54 : else if( pszOption != NULL && EQUAL(pszOption,"NPJE") )
616 : SetParameter(
617 12 : CNCSJP2FileView::JP2_COMPRESS_PROFILE_NITF_BIIF_NPJE );
618 30 : else if( pszOption != NULL && EQUAL(pszOption,"EPJE") )
619 : SetParameter(
620 0 : CNCSJP2FileView::JP2_COMPRESS_PROFILE_NITF_BIIF_EPJE );
621 :
622 42 : pszOption = CSLFetchNameValue(papszOptions, "CODESTREAM_ONLY" );
623 42 : if( pszOption != NULL )
624 : SetParameter(
625 : CNCSJP2FileView::JP2_COMPRESS_CODESTREAM_ONLY,
626 6 : (bool) CSLTestBoolean( pszOption ) );
627 :
628 42 : pszOption = CSLFetchNameValue(papszOptions, "LEVELS");
629 42 : if( pszOption != NULL )
630 : SetParameter( CNCSJP2FileView::JP2_COMPRESS_LEVELS,
631 0 : (UINT32) atoi(pszOption) );
632 :
633 42 : pszOption = CSLFetchNameValue(papszOptions, "LAYERS");
634 42 : if( pszOption != NULL )
635 : SetParameter( CNCSJP2FileView::JP2_COMPRESS_LAYERS,
636 6 : (UINT32) atoi(pszOption) );
637 :
638 42 : pszOption = CSLFetchNameValue(papszOptions, "PRECINCT_WIDTH");
639 42 : if( pszOption != NULL )
640 : SetParameter( CNCSJP2FileView::JP2_COMPRESS_PRECINCT_WIDTH,
641 0 : (UINT32) atoi(pszOption) );
642 :
643 42 : pszOption = CSLFetchNameValue(papszOptions, "PRECINCT_HEIGHT");
644 42 : if( pszOption != NULL )
645 : SetParameter(CNCSJP2FileView::JP2_COMPRESS_PRECINCT_HEIGHT,
646 0 : (UINT32) atoi(pszOption) );
647 :
648 42 : pszOption = CSLFetchNameValue(papszOptions, "TILE_WIDTH");
649 42 : if( pszOption != NULL )
650 : SetParameter( CNCSJP2FileView::JP2_COMPRESS_TILE_WIDTH,
651 0 : (UINT32) atoi(pszOption) );
652 :
653 42 : pszOption = CSLFetchNameValue(papszOptions, "TILE_HEIGHT");
654 42 : if( pszOption != NULL )
655 : SetParameter( CNCSJP2FileView::JP2_COMPRESS_TILE_HEIGHT,
656 0 : (UINT32) atoi(pszOption) );
657 :
658 42 : pszOption = CSLFetchNameValue(papszOptions, "INCLUDE_SOP");
659 42 : if( pszOption != NULL )
660 : SetParameter( CNCSJP2FileView::JP2_COMPRESS_INCLUDE_SOP,
661 0 : (bool) CSLTestBoolean( pszOption ) );
662 :
663 42 : pszOption = CSLFetchNameValue(papszOptions, "INCLUDE_EPH");
664 42 : if( pszOption != NULL )
665 : SetParameter( CNCSJP2FileView::JP2_COMPRESS_INCLUDE_EPH,
666 0 : (bool) CSLTestBoolean( pszOption ) );
667 :
668 42 : pszOption = CSLFetchNameValue(papszOptions, "PROGRESSION");
669 42 : if( pszOption != NULL && EQUAL(pszOption,"LRCP") )
670 : SetParameter(
671 0 : CNCSJP2FileView::JP2_COMPRESS_PROGRESSION_LRCP );
672 :
673 42 : else if( pszOption != NULL && EQUAL(pszOption,"RLCP") )
674 : SetParameter(
675 0 : CNCSJP2FileView::JP2_COMPRESS_PROGRESSION_RLCP );
676 :
677 42 : else if( pszOption != NULL && EQUAL(pszOption,"RPCL") )
678 : SetParameter(
679 0 : CNCSJP2FileView::JP2_COMPRESS_PROGRESSION_RPCL );
680 :
681 42 : pszOption = CSLFetchNameValue(papszOptions, "GEODATA_USAGE");
682 42 : if( pszOption == NULL )
683 : // Default to supressing ECW SDK geodata, just use our own stuff.
684 42 : SetGeodataUsage( JP2_GEODATA_USE_NONE );
685 0 : else if( EQUAL(pszOption,"NONE") )
686 0 : SetGeodataUsage( JP2_GEODATA_USE_NONE );
687 0 : else if( EQUAL(pszOption,"PCS_ONLY") )
688 0 : SetGeodataUsage( JP2_GEODATA_USE_PCS_ONLY );
689 0 : else if( EQUAL(pszOption,"GML_ONLY") )
690 0 : SetGeodataUsage( JP2_GEODATA_USE_GML_ONLY );
691 0 : else if( EQUAL(pszOption,"PCS_GML") )
692 0 : SetGeodataUsage( JP2_GEODATA_USE_PCS_GML );
693 0 : else if( EQUAL(pszOption,"GML_PCS") )
694 0 : SetGeodataUsage( JP2_GEODATA_USE_GML_PCS );
695 0 : else if( EQUAL(pszOption,"ALL") )
696 0 : SetGeodataUsage( JP2_GEODATA_USE_GML_PCS_WLD );
697 :
698 42 : pszOption = CSLFetchNameValue(papszOptions, "DECOMPRESS_LAYERS");
699 42 : if( pszOption != NULL )
700 : SetParameter(
701 : CNCSJP2FileView::JP2_DECOMPRESS_LAYERS,
702 0 : (UINT32) atoi(pszOption) );
703 :
704 : pszOption = CSLFetchNameValue(papszOptions,
705 42 : "DECOMPRESS_RECONSTRUCTION_PARAMETER");
706 42 : if( pszOption != NULL )
707 : SetParameter(
708 : CNCSJP2FileView::JPC_DECOMPRESS_RECONSTRUCTION_PARAMETER,
709 0 : (IEEE4) atof(pszOption) );
710 : }
711 :
712 : /* -------------------------------------------------------------------- */
713 : /* Georeferencing. */
714 : /* -------------------------------------------------------------------- */
715 :
716 48 : psClient->fOriginX = 0.0;
717 48 : psClient->fOriginY = psClient->nSizeY;
718 48 : psClient->fCellIncrementX = 1.0;
719 48 : psClient->fCellIncrementY = -1.0;
720 48 : psClient->fCWRotationDegrees = 0.0;
721 :
722 48 : if( padfGeoTransform[2] != 0.0 || padfGeoTransform[4] != 0.0 )
723 : CPLError( CE_Warning, CPLE_NotSupported,
724 : "Rotational coefficients ignored, georeferencing of\n"
725 0 : "output ECW file will be incorrect.\n" );
726 : else
727 : {
728 48 : psClient->fOriginX = padfGeoTransform[0];
729 48 : psClient->fOriginY = padfGeoTransform[3];
730 48 : psClient->fCellIncrementX = padfGeoTransform[1];
731 48 : psClient->fCellIncrementY = padfGeoTransform[5];
732 : }
733 :
734 : /* -------------------------------------------------------------------- */
735 : /* Projection. */
736 : /* -------------------------------------------------------------------- */
737 : char szProjection[128];
738 : char szDatum[128];
739 : char szUnits[128];
740 :
741 48 : strcpy( szProjection, "RAW" );
742 48 : strcpy( szDatum, "RAW" );
743 :
744 48 : if( CSLFetchNameValue(papszOptions, "PROJ") != NULL )
745 : {
746 : strncpy( szProjection,
747 0 : CSLFetchNameValue(papszOptions, "PROJ"), sizeof(szProjection) );
748 0 : szProjection[sizeof(szProjection)-1] = 0;
749 : }
750 :
751 48 : if( CSLFetchNameValue(papszOptions, "DATUM") != NULL )
752 : {
753 0 : strncpy( szDatum, CSLFetchNameValue(papszOptions, "DATUM"), sizeof(szDatum) );
754 0 : szDatum[sizeof(szDatum)-1] = 0;
755 0 : if( EQUAL(szProjection,"RAW") )
756 0 : strcpy( szProjection, "GEODETIC" );
757 : }
758 :
759 48 : const char* pszUnits = CSLFetchNameValue(papszOptions, "UNITS");
760 48 : if( pszUnits != NULL )
761 : {
762 0 : psClient->eCellSizeUnits = ECWTranslateToCellSizeUnits(pszUnits);
763 : }
764 :
765 48 : if( EQUAL(szProjection,"RAW") && pszWKT != NULL )
766 : {
767 46 : ECWTranslateFromWKT( pszWKT, szProjection, sizeof(szProjection), szDatum, sizeof(szDatum), szUnits );
768 46 : psClient->eCellSizeUnits = ECWTranslateToCellSizeUnits(szUnits);
769 : }
770 :
771 48 : psClient->szDatum = szDatum;
772 48 : psClient->szProjection = szProjection;
773 :
774 : CPLDebug( "ECW", "Writing with PROJ=%s, DATUM=%s, UNITS=%s",
775 48 : szProjection, szDatum, ECWTranslateFromCellSizeUnits(psClient->eCellSizeUnits) );
776 :
777 : /* -------------------------------------------------------------------- */
778 : /* Setup GML and GeoTIFF information. */
779 : /* -------------------------------------------------------------------- */
780 48 : GDALJP2Metadata oJP2MD;
781 :
782 48 : oJP2MD.SetProjection( pszWKT );
783 48 : oJP2MD.SetGeoTransform( padfGeoTransform );
784 48 : oJP2MD.SetGCPs( nGCPCount, pasGCPList );
785 :
786 48 : if( CSLFetchBoolean( papszOptions, "GMLJP2", TRUE ) )
787 42 : WriteJP2Box( oJP2MD.CreateGMLJP2(nXSize,nYSize) );
788 48 : if( CSLFetchBoolean( papszOptions, "GeoJP2", TRUE ) )
789 42 : WriteJP2Box( oJP2MD.CreateJP2GeoTIFF() );
790 :
791 : /* -------------------------------------------------------------------- */
792 : /* We handle all jpeg2000 files via the VSIIOStream, but ECW */
793 : /* files cannot be done this way for some reason. */
794 : /* -------------------------------------------------------------------- */
795 48 : VSILFILE *fpVSIL = NULL;
796 :
797 48 : if( bIsJPEG2000 )
798 : {
799 42 : fpVSIL = VSIFOpenL( pszFilename, "wb+" );
800 42 : if( fpVSIL == NULL )
801 : {
802 : CPLError( CE_Failure, CPLE_OpenFailed,
803 4 : "Failed to open %s.", pszFilename );
804 4 : return CE_Failure;
805 : }
806 :
807 38 : m_OStream.Access( fpVSIL, TRUE, pszFilename, 0, -1 );
808 : }
809 :
810 : /* -------------------------------------------------------------------- */
811 : /* Check if we can enable large files. This option should only */
812 : /* be set when the application is adhering to one of the */
813 : /* ERMapper options for licensing larger than 500MB input */
814 : /* files. See Bug 767. This option no longer exists with */
815 : /* version 4+. */
816 : /* -------------------------------------------------------------------- */
817 : #if ECWSDK_VERSION < 40
818 44 : const char *pszLargeOK = CSLFetchNameValue(papszOptions, "LARGE_OK");
819 44 : if( pszLargeOK == NULL )
820 44 : pszLargeOK = "NO";
821 :
822 44 : pszLargeOK = CPLGetConfigOption( "ECW_LARGE_OK", pszLargeOK );
823 :
824 44 : if( CSLTestBoolean(pszLargeOK) )
825 : {
826 0 : CNCSFile::SetKeySize();
827 0 : CPLDebug( "ECW", "Large file generation enabled." );
828 : }
829 : #endif /* ECWSDK_VERSION < 40 */
830 :
831 : /* -------------------------------------------------------------------- */
832 : /* Set the file info. */
833 : /* -------------------------------------------------------------------- */
834 44 : CNCSError oError;
835 :
836 44 : oError = SetFileInfo( sFileInfo );
837 :
838 44 : if( oError.GetErrorNumber() == NCS_SUCCESS )
839 : {
840 44 : if( fpVSIL == NULL )
841 6 : oError = Open( (char *) pszFilename, false, true );
842 : else
843 38 : oError = CNCSJP2FileView::Open( &(m_OStream) );
844 : }
845 :
846 44 : if( oError.GetErrorNumber() == NCS_SUCCESS )
847 40 : return CE_None;
848 4 : else if( oError.GetErrorNumber() == NCS_INPUT_SIZE_EXCEEDED )
849 : {
850 : CPLError( CE_Failure, CPLE_AppDefined,
851 0 : "ECW SDK 500MB compress limit exceeded." );
852 0 : return CE_Failure;
853 : }
854 : else
855 : {
856 4 : char* pszErrorMessage = oError.GetErrorMessage();
857 : CPLError( CE_Failure, CPLE_AppDefined,
858 4 : "%s", pszErrorMessage );
859 4 : NCSFree(pszErrorMessage);
860 :
861 4 : return CE_Failure;
862 0 : }
863 : }
864 :
865 : /************************************************************************/
866 : /* ECWCreateCopy() */
867 : /************************************************************************/
868 :
869 : static GDALDataset *
870 44 : ECWCreateCopy( const char * pszFilename, GDALDataset *poSrcDS,
871 : int bStrict, char ** papszOptions,
872 : GDALProgressFunc pfnProgress, void * pProgressData,
873 : int bIsJPEG2000 )
874 :
875 : {
876 44 : ECWInitialize();
877 :
878 : /* -------------------------------------------------------------------- */
879 : /* Get various values from the source dataset. */
880 : /* -------------------------------------------------------------------- */
881 44 : int nBands = poSrcDS->GetRasterCount();
882 44 : int nXSize = poSrcDS->GetRasterXSize();
883 44 : int nYSize = poSrcDS->GetRasterYSize();
884 :
885 44 : if (nBands == 0)
886 : {
887 : CPLError( CE_Failure, CPLE_NotSupported,
888 0 : "ECW driver does not support source dataset with zero band.\n");
889 0 : return NULL;
890 : }
891 :
892 44 : GDALDataType eType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
893 :
894 44 : const char *pszWKT = poSrcDS->GetProjectionRef();
895 44 : double adfGeoTransform[6] = { 0, 1, 0, 0, 0, 1 };;
896 :
897 44 : poSrcDS->GetGeoTransform( adfGeoTransform );
898 :
899 44 : if( poSrcDS->GetGCPCount() > 0 )
900 2 : pszWKT = poSrcDS->GetGCPProjection();
901 :
902 : /* -------------------------------------------------------------------- */
903 : /* Confirm the datatype is 8bit. It appears no other datatype */
904 : /* is supported in ECW format. */
905 : /* -------------------------------------------------------------------- */
906 44 : if( eType != GDT_Byte && !bIsJPEG2000 )
907 : {
908 0 : if( bStrict )
909 : {
910 : CPLError( CE_Failure, CPLE_AppDefined,
911 : "Attempt to create ECW file with pixel data type %s failed.\n"
912 : "Only Byte data type supported.",
913 0 : GDALGetDataTypeName( eType ) );
914 : }
915 : else
916 : {
917 : CPLError( CE_Warning, CPLE_AppDefined,
918 : "ECW only supports Byte pixel data type, ignoring request for %s.",
919 0 : GDALGetDataTypeName( eType ) );
920 0 : eType = GDT_Byte;
921 : }
922 : }
923 :
924 : /* -------------------------------------------------------------------- */
925 : /* Is the input RGBA? */
926 : /* -------------------------------------------------------------------- */
927 44 : int bRGBA = FALSE;
928 :
929 44 : if( nBands == 4 )
930 : {
931 2 : bRGBA = (poSrcDS->GetRasterBand(4)->GetColorInterpretation()
932 4 : == GCI_AlphaBand);
933 : }
934 :
935 : /* -------------------------------------------------------------------- */
936 : /* Setup the compressor. */
937 : /* -------------------------------------------------------------------- */
938 44 : GDALECWCompressor oCompressor;
939 44 : CNCSError oErr;
940 :
941 44 : oCompressor.pfnProgress = pfnProgress;
942 44 : oCompressor.pProgressData = pProgressData;
943 44 : oCompressor.m_poSrcDS = poSrcDS;
944 :
945 44 : if( !pfnProgress( 0.0, NULL, pProgressData ) )
946 0 : return NULL;
947 :
948 88 : if( oCompressor.Initialize( pszFilename, papszOptions,
949 : nXSize, nYSize, nBands, bRGBA,
950 : eType, pszWKT, adfGeoTransform,
951 44 : poSrcDS->GetGCPCount(),
952 44 : poSrcDS->GetGCPs(),
953 : bIsJPEG2000 )
954 : != CE_None )
955 8 : return NULL;
956 :
957 : /* -------------------------------------------------------------------- */
958 : /* Start the compression. */
959 : /* -------------------------------------------------------------------- */
960 36 : oErr = oCompressor.Write();
961 :
962 36 : if( oErr.GetErrorNumber() != NCS_SUCCESS )
963 : {
964 0 : char* pszErrorMessage = oErr.GetErrorMessage();
965 : CPLError( CE_Failure, CPLE_AppDefined,
966 0 : "%s", pszErrorMessage );
967 0 : NCSFree(pszErrorMessage);
968 :
969 0 : return NULL;
970 : }
971 :
972 : /* -------------------------------------------------------------------- */
973 : /* Cleanup, and return read-only handle. */
974 : /* -------------------------------------------------------------------- */
975 36 : oCompressor.CloseDown();
976 36 : pfnProgress( 1.001, NULL, pProgressData );
977 :
978 : /* -------------------------------------------------------------------- */
979 : /* Re-open dataset, and copy any auxilary pam information. */
980 : /* -------------------------------------------------------------------- */
981 36 : GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
982 : GDALPamDataset *poDS;
983 :
984 36 : if (bIsJPEG2000)
985 34 : poDS = (GDALPamDataset*) ECWDatasetOpenJPEG2000(&oOpenInfo);
986 : else
987 2 : poDS = (GDALPamDataset*) GDALOpen(pszFilename, GA_ReadOnly);
988 :
989 36 : if( poDS )
990 36 : poDS->CloneInfo( poSrcDS, GCIF_PAM_DEFAULT );
991 :
992 36 : return poDS;
993 : }
994 :
995 : /************************************************************************/
996 : /* ECWCreateCopyECW() */
997 : /************************************************************************/
998 :
999 : GDALDataset *
1000 38 : ECWCreateCopyECW( const char * pszFilename, GDALDataset *poSrcDS,
1001 : int bStrict, char ** papszOptions,
1002 : GDALProgressFunc pfnProgress, void * pProgressData )
1003 :
1004 : {
1005 38 : int nBands = poSrcDS->GetRasterCount();
1006 38 : if (nBands == 0)
1007 : {
1008 : CPLError( CE_Failure, CPLE_NotSupported,
1009 2 : "ECW driver does not support source dataset with zero band.\n");
1010 2 : return NULL;
1011 : }
1012 :
1013 36 : if( !EQUAL(CPLGetExtension(pszFilename),"ecw") )
1014 : {
1015 : CPLError( CE_Failure, CPLE_AppDefined,
1016 : "ECW driver does not support creating ECW files\n"
1017 0 : "with an extension other than .ecw" );
1018 0 : return NULL;
1019 : }
1020 :
1021 36 : if( poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte
1022 : && bStrict )
1023 : {
1024 : CPLError( CE_Failure, CPLE_NotSupported,
1025 : "ECW driver doesn't support data type %s. "
1026 : "Only eight bit bands supported.\n",
1027 : GDALGetDataTypeName(
1028 20 : poSrcDS->GetRasterBand(1)->GetRasterDataType()) );
1029 :
1030 20 : return NULL;
1031 : }
1032 :
1033 16 : if( poSrcDS->GetRasterXSize() < 128 || poSrcDS->GetRasterYSize() < 128 )
1034 : {
1035 : CPLError( CE_Failure, CPLE_NotSupported,
1036 : "ECW driver requires image to be at least 128x128,\n"
1037 : "the source image is %dx%d.\n",
1038 : poSrcDS->GetRasterXSize(),
1039 10 : poSrcDS->GetRasterYSize() );
1040 :
1041 10 : return NULL;
1042 : }
1043 :
1044 6 : if (poSrcDS->GetRasterBand(1)->GetColorTable() != NULL)
1045 : {
1046 : CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
1047 : "ECW driver ignores color table. "
1048 : "The source raster band will be considered as grey level.\n"
1049 0 : "Consider using color table expansion (-expand option in gdal_translate)\n");
1050 0 : if (bStrict)
1051 0 : return NULL;
1052 : }
1053 :
1054 : return ECWCreateCopy( pszFilename, poSrcDS, bStrict, papszOptions,
1055 6 : pfnProgress, pProgressData, FALSE );
1056 : }
1057 :
1058 : /************************************************************************/
1059 : /* ECWCreateCopyJPEG2000() */
1060 : /************************************************************************/
1061 :
1062 : GDALDataset *
1063 48 : ECWCreateCopyJPEG2000( const char * pszFilename, GDALDataset *poSrcDS,
1064 : int bStrict, char ** papszOptions,
1065 : GDALProgressFunc pfnProgress, void * pProgressData )
1066 :
1067 : {
1068 48 : int nBands = poSrcDS->GetRasterCount();
1069 48 : if (nBands == 0)
1070 : {
1071 : CPLError( CE_Failure, CPLE_NotSupported,
1072 2 : "JP2ECW driver does not support source dataset with zero band.\n");
1073 2 : return NULL;
1074 : }
1075 :
1076 46 : if( EQUAL(CPLGetExtension(pszFilename),"ecw") )
1077 : {
1078 : CPLError( CE_Failure, CPLE_AppDefined,
1079 : "JP2ECW driver does not support creating JPEG2000 files\n"
1080 0 : "with a .ecw extension. Please use anything else." );
1081 0 : return NULL;
1082 : }
1083 :
1084 46 : if( poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte
1085 : && poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Byte
1086 : && poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Int16
1087 : && poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_UInt16
1088 : && poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Int32
1089 : && poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_UInt32
1090 : && poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Float32
1091 : && poSrcDS->GetRasterBand(1)->GetRasterDataType() != GDT_Float64
1092 : && bStrict )
1093 : {
1094 : CPLError( CE_Failure, CPLE_NotSupported,
1095 : "JP2ECW driver doesn't support data type %s. ",
1096 : GDALGetDataTypeName(
1097 8 : poSrcDS->GetRasterBand(1)->GetRasterDataType()) );
1098 :
1099 8 : return NULL;
1100 : }
1101 :
1102 38 : if (poSrcDS->GetRasterBand(1)->GetColorTable() != NULL)
1103 : {
1104 : CPLError( (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
1105 : "JP2ECW driver ignores color table. "
1106 : "The source raster band will be considered as grey level.\n"
1107 0 : "Consider using color table expansion (-expand option in gdal_translate)\n");
1108 0 : if (bStrict)
1109 0 : return NULL;
1110 : }
1111 :
1112 : return ECWCreateCopy( pszFilename, poSrcDS, bStrict, papszOptions,
1113 38 : pfnProgress, pProgressData, TRUE );
1114 : }
1115 :
1116 : /************************************************************************/
1117 : /************************************************************************
1118 :
1119 : ECW/JPEG200 Create() Support
1120 : ----------------------------
1121 :
1122 : The remainder of the file is code to implement the Create() method.
1123 : New dataset and raster band classes are defined specifically for the
1124 : purpose of being write-only. In particular, you cannot read back data
1125 : from these datasets, and writing must occur in a pretty specific order.
1126 :
1127 : That is, you need to write all metadata (projection, georef, etc) first
1128 : and then write the image data. All bands data for the first scanline
1129 : should be written followed by all bands for the second scanline and so on.
1130 :
1131 : Creation supports the same virtual subfile names as CreateCopy() supports.
1132 :
1133 : ************************************************************************/
1134 : /************************************************************************/
1135 :
1136 : /************************************************************************/
1137 : /* ==================================================================== */
1138 : /* ECWWriteDataset */
1139 : /* ==================================================================== */
1140 : /************************************************************************/
1141 :
1142 : class ECWWriteRasterBand;
1143 :
1144 : class CPL_DLL ECWWriteDataset : public GDALDataset
1145 : {
1146 : friend class ECWWriteRasterBand;
1147 :
1148 : char *pszFilename;
1149 :
1150 : int bIsJPEG2000;
1151 : GDALDataType eDataType;
1152 : char **papszOptions;
1153 :
1154 : char *pszProjection;
1155 : double adfGeoTransform[6];
1156 :
1157 : GDALECWCompressor oCompressor;
1158 : int bCrystalized;
1159 :
1160 : int nLoadedLine;
1161 : GByte *pabyBILBuffer;
1162 :
1163 : CPLErr Crystalize();
1164 : CPLErr FlushLine();
1165 :
1166 : public:
1167 : ECWWriteDataset( const char *, int, int, int,
1168 : GDALDataType, char **papszOptions,
1169 : int );
1170 : ~ECWWriteDataset();
1171 :
1172 : virtual void FlushCache( void );
1173 :
1174 : virtual CPLErr GetGeoTransform( double * );
1175 : virtual const char* GetProjectionRef();
1176 : virtual CPLErr SetGeoTransform( double * );
1177 : virtual CPLErr SetProjection( const char *pszWKT );
1178 : };
1179 :
1180 : /************************************************************************/
1181 : /* ==================================================================== */
1182 : /* ECWWriteRasterBand */
1183 : /* ==================================================================== */
1184 : /************************************************************************/
1185 :
1186 : class ECWWriteRasterBand : public GDALRasterBand
1187 118 : {
1188 : friend class ECWWriteDataset;
1189 :
1190 : // NOTE: poDS may be altered for NITF/JPEG2000 files!
1191 : ECWWriteDataset *poGDS;
1192 :
1193 : GDALColorInterp eInterp;
1194 :
1195 : public:
1196 :
1197 : ECWWriteRasterBand( ECWWriteDataset *, int );
1198 :
1199 0 : virtual CPLErr SetColorInterpretation( GDALColorInterp eInterpIn )
1200 0 : { eInterp = eInterpIn; return CE_None; }
1201 6 : virtual GDALColorInterp GetColorInterpretation()
1202 6 : { return eInterp; }
1203 :
1204 : virtual CPLErr IReadBlock( int, int, void * );
1205 : virtual CPLErr IWriteBlock( int, int, void * );
1206 : };
1207 :
1208 : /************************************************************************/
1209 : /* ECWWriteDataset() */
1210 : /************************************************************************/
1211 :
1212 56 : ECWWriteDataset::ECWWriteDataset( const char *pszFilename,
1213 : int nXSize, int nYSize, int nBandCount,
1214 : GDALDataType eType,
1215 56 : char **papszOptions, int bIsJPEG2000 )
1216 :
1217 : {
1218 56 : bCrystalized = FALSE;
1219 56 : pabyBILBuffer = NULL;
1220 56 : nLoadedLine = -1;
1221 :
1222 56 : eAccess = GA_Update;
1223 :
1224 56 : this->bIsJPEG2000 = bIsJPEG2000;
1225 56 : this->eDataType = eType;
1226 56 : this->papszOptions = CSLDuplicate( papszOptions );
1227 56 : this->pszFilename = CPLStrdup( pszFilename );
1228 :
1229 56 : nRasterXSize = nXSize;
1230 56 : nRasterYSize = nYSize;
1231 56 : pszProjection = NULL;
1232 :
1233 56 : adfGeoTransform[0] = 0.0;
1234 56 : adfGeoTransform[1] = 1.0;
1235 56 : adfGeoTransform[2] = 0.0;
1236 56 : adfGeoTransform[3] = 0.0;
1237 56 : adfGeoTransform[4] = 0.0;
1238 56 : adfGeoTransform[5] = 1.0;
1239 :
1240 : // create band objects.
1241 174 : for( int iBand = 1; iBand <= nBandCount; iBand++ )
1242 : {
1243 118 : SetBand( iBand, new ECWWriteRasterBand( this, iBand ) );
1244 : }
1245 56 : }
1246 :
1247 : /************************************************************************/
1248 : /* ~ECWWriteDataset() */
1249 : /************************************************************************/
1250 :
1251 56 : ECWWriteDataset::~ECWWriteDataset()
1252 :
1253 : {
1254 56 : FlushCache();
1255 :
1256 56 : if( bCrystalized )
1257 : {
1258 4 : if( nLoadedLine == nRasterYSize - 1 )
1259 4 : FlushLine();
1260 4 : oCompressor.CloseDown();
1261 : }
1262 :
1263 56 : CPLFree( pabyBILBuffer );
1264 56 : CPLFree( pszProjection );
1265 56 : CSLDestroy( papszOptions );
1266 56 : CPLFree( pszFilename );
1267 56 : }
1268 :
1269 : /************************************************************************/
1270 : /* FlushCache() */
1271 : /************************************************************************/
1272 :
1273 58 : void ECWWriteDataset::FlushCache()
1274 :
1275 : {
1276 58 : BlockBasedFlushCache();
1277 58 : }
1278 :
1279 : /************************************************************************/
1280 : /* GetProjectionRef() */
1281 : /************************************************************************/
1282 :
1283 0 : const char* ECWWriteDataset::GetProjectionRef()
1284 : {
1285 0 : return pszProjection;
1286 : }
1287 :
1288 : /************************************************************************/
1289 : /* GetGeoTransform() */
1290 : /************************************************************************/
1291 :
1292 52 : CPLErr ECWWriteDataset::GetGeoTransform( double *padfGeoTransform )
1293 :
1294 : {
1295 52 : memcpy( padfGeoTransform, adfGeoTransform, sizeof(double) * 6 );
1296 52 : return CE_None;
1297 : }
1298 :
1299 : /************************************************************************/
1300 : /* SetGeoTransform() */
1301 : /************************************************************************/
1302 :
1303 54 : CPLErr ECWWriteDataset::SetGeoTransform( double *padfGeoTransform )
1304 :
1305 : {
1306 54 : memcpy( adfGeoTransform, padfGeoTransform, sizeof(double) * 6 );
1307 54 : return CE_None;
1308 : }
1309 :
1310 : /************************************************************************/
1311 : /* SetProjection() */
1312 : /************************************************************************/
1313 :
1314 54 : CPLErr ECWWriteDataset::SetProjection( const char *pszWKT )
1315 :
1316 : {
1317 54 : CPLFree( pszProjection );
1318 54 : pszProjection = CPLStrdup( pszWKT );
1319 :
1320 54 : return CE_None;
1321 : }
1322 :
1323 :
1324 : /************************************************************************/
1325 : /* Crystalize() */
1326 : /************************************************************************/
1327 :
1328 4 : CPLErr ECWWriteDataset::Crystalize()
1329 :
1330 : {
1331 4 : int nWordSize = GDALGetDataTypeSize( eDataType ) / 8;
1332 :
1333 : CPLErr eErr;
1334 4 : CNCSError oError;
1335 :
1336 4 : if( bCrystalized )
1337 0 : return CE_None;
1338 :
1339 : eErr = oCompressor.Initialize( pszFilename, papszOptions,
1340 : nRasterXSize, nRasterYSize, nBands, FALSE,
1341 : eDataType,
1342 : pszProjection, adfGeoTransform,
1343 : 0, NULL,
1344 4 : bIsJPEG2000 );
1345 :
1346 4 : if( eErr == CE_None )
1347 4 : bCrystalized = TRUE;
1348 :
1349 4 : nLoadedLine = -1;
1350 4 : pabyBILBuffer = (GByte *) CPLMalloc( nWordSize * nBands * nRasterXSize );
1351 :
1352 4 : return eErr;
1353 : }
1354 :
1355 : /************************************************************************/
1356 : /* FlushLine() */
1357 : /************************************************************************/
1358 :
1359 404 : CPLErr ECWWriteDataset::FlushLine()
1360 :
1361 : {
1362 404 : int nWordSize = GDALGetDataTypeSize( eDataType ) / 8;
1363 : CPLErr eErr;
1364 :
1365 : /* -------------------------------------------------------------------- */
1366 : /* Crystalize if not already done. */
1367 : /* -------------------------------------------------------------------- */
1368 404 : if( !bCrystalized )
1369 : {
1370 4 : eErr = Crystalize();
1371 :
1372 4 : if( eErr != CE_None )
1373 0 : return eErr;
1374 : }
1375 :
1376 : /* -------------------------------------------------------------------- */
1377 : /* Write out the currently loaded line. */
1378 : /* -------------------------------------------------------------------- */
1379 404 : if( nLoadedLine != -1 )
1380 : {
1381 400 : CNCSError oError;
1382 : void **papOutputLine;
1383 :
1384 400 : papOutputLine = (void **) CPLMalloc(sizeof(void*) * nBands);
1385 1200 : for( int i = 0; i < nBands; i++ )
1386 800 : papOutputLine[i] =
1387 800 : (void *) (pabyBILBuffer + i * nWordSize * nRasterXSize);
1388 :
1389 :
1390 : oError = oCompressor.WriteLineBIL( oCompressor.sFileInfo.eCellType,
1391 400 : (UINT16) nBands, papOutputLine );
1392 400 : CPLFree( papOutputLine );
1393 400 : if( oError.GetErrorNumber() != NCS_SUCCESS )
1394 : {
1395 0 : char* pszErrorMessage = oError.GetErrorMessage();
1396 : CPLError( CE_Failure, CPLE_AppDefined,
1397 : "Scanline write write failed.\n%s",
1398 0 : pszErrorMessage );
1399 0 : NCSFree(pszErrorMessage);
1400 :
1401 0 : return CE_Failure;
1402 0 : }
1403 : }
1404 :
1405 : /* -------------------------------------------------------------------- */
1406 : /* Clear the buffer and increment the "current line" indicator. */
1407 : /* -------------------------------------------------------------------- */
1408 404 : memset( pabyBILBuffer, 0, nWordSize * nRasterXSize * nBands );
1409 404 : nLoadedLine++;
1410 :
1411 404 : return CE_None;
1412 : }
1413 :
1414 :
1415 : /************************************************************************/
1416 : /* ==================================================================== */
1417 : /* ECWWriteRasterBand */
1418 : /* ==================================================================== */
1419 : /************************************************************************/
1420 :
1421 : /************************************************************************/
1422 : /* ECWWriteRasterBand() */
1423 : /************************************************************************/
1424 :
1425 118 : ECWWriteRasterBand::ECWWriteRasterBand( ECWWriteDataset *poDSIn,
1426 118 : int nBandIn )
1427 :
1428 : {
1429 118 : nBand = nBandIn;
1430 118 : poDS = poDSIn;
1431 118 : poGDS = poDSIn;
1432 118 : nBlockXSize = poDSIn->GetRasterXSize();
1433 118 : nBlockYSize = 1;
1434 118 : eDataType = poDSIn->eDataType;
1435 118 : eInterp = GCI_Undefined;
1436 118 : }
1437 :
1438 : /************************************************************************/
1439 : /* IReadBlock() */
1440 : /************************************************************************/
1441 :
1442 0 : CPLErr ECWWriteRasterBand::IReadBlock( int nBlockX, int nBlockY,
1443 : void *pBuffer )
1444 :
1445 : {
1446 0 : int nWordSize = GDALGetDataTypeSize( eDataType ) / 8;
1447 :
1448 : // We zero stuff out here, but we can't really read stuff from
1449 : // a write only stream.
1450 :
1451 0 : memset( pBuffer, 0, nBlockXSize * nWordSize );
1452 :
1453 0 : return CE_None;
1454 : }
1455 :
1456 : /************************************************************************/
1457 : /* IWriteBlock() */
1458 : /************************************************************************/
1459 :
1460 800 : CPLErr ECWWriteRasterBand::IWriteBlock( int nBlockX, int nBlockY,
1461 : void *pBuffer )
1462 :
1463 : {
1464 800 : int nWordSize = GDALGetDataTypeSize( eDataType ) / 8;
1465 : CPLErr eErr;
1466 :
1467 : /* -------------------------------------------------------------------- */
1468 : /* Flush previous line if needed. */
1469 : /* -------------------------------------------------------------------- */
1470 800 : if( nBlockY == poGDS->nLoadedLine + 1 )
1471 : {
1472 400 : eErr = poGDS->FlushLine();
1473 400 : if( eErr != CE_None )
1474 0 : return eErr;
1475 : }
1476 :
1477 : /* -------------------------------------------------------------------- */
1478 : /* Blow a gasket if we have been asked to write something out */
1479 : /* of order. */
1480 : /* -------------------------------------------------------------------- */
1481 800 : if( nBlockY != poGDS->nLoadedLine )
1482 : {
1483 : CPLError( CE_Failure, CPLE_AppDefined,
1484 : "Apparent attempt to write to ECW non-sequentially.\n"
1485 : "Loaded line is %d, but %d of band %d was written to.",
1486 0 : poGDS->nLoadedLine, nBlockY, nBand );
1487 0 : return CE_Failure;
1488 : }
1489 :
1490 : /* -------------------------------------------------------------------- */
1491 : /* Copy passed data into current line buffer. */
1492 : /* -------------------------------------------------------------------- */
1493 : memcpy( poGDS->pabyBILBuffer + (nBand-1) * nWordSize * nRasterXSize,
1494 : pBuffer,
1495 800 : nWordSize * nRasterXSize );
1496 :
1497 800 : return CE_None;
1498 : }
1499 :
1500 : /************************************************************************/
1501 : /* ECWCreateJPEG2000() */
1502 : /************************************************************************/
1503 :
1504 : GDALDataset *
1505 56 : ECWCreateJPEG2000(const char *pszFilename, int nXSize, int nYSize, int nBands,
1506 : GDALDataType eType, char **papszOptions )
1507 :
1508 : {
1509 56 : ECWInitialize();
1510 :
1511 : return new ECWWriteDataset( pszFilename, nXSize, nYSize, nBands,
1512 56 : eType, papszOptions, TRUE );
1513 : }
1514 :
1515 : /************************************************************************/
1516 : /* ECWCreateECW() */
1517 : /************************************************************************/
1518 :
1519 : GDALDataset *
1520 0 : ECWCreateECW( const char *pszFilename, int nXSize, int nYSize, int nBands,
1521 : GDALDataType eType, char **papszOptions )
1522 :
1523 : {
1524 0 : ECWInitialize();
1525 :
1526 : return new ECWWriteDataset( pszFilename, nXSize, nYSize, nBands,
1527 0 : eType, papszOptions, FALSE );
1528 4029 : }
1529 :
1530 : #endif /* def FRMT_ecw && def HAVE_COMPRESS */
|