1 : /******************************************************************************
2 : * $Id: ecwdataset.cpp 17906 2009-10-26 19:47:21Z rouault $
3 : *
4 : * Project: GDAL
5 : * Purpose: ECW (ERMapper Wavelet Compression Format) Driver
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2001, 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_pam.h"
31 : #include "gdaljp2metadata.h"
32 : #include "ogr_spatialref.h"
33 : #include "cpl_string.h"
34 : #include "cpl_conv.h"
35 : #include "vsiiostream.h"
36 : #include "cpl_multiproc.h"
37 : #include "cpl_minixml.h"
38 : #include "ogr_api.h"
39 : #include "ogr_geometry.h"
40 :
41 : CPL_CVSID("$Id: ecwdataset.cpp 17906 2009-10-26 19:47:21Z rouault $");
42 :
43 : #ifdef FRMT_ecw
44 :
45 : static const unsigned char jpc_header[] = {0xff,0x4f};
46 : static const unsigned char jp2_header[] =
47 : {0x00,0x00,0x00,0x0c,0x6a,0x50,0x20,0x20,0x0d,0x0a,0x87,0x0a};
48 :
49 : static void *hECWDatasetMutex = NULL;
50 : static int bNCSInitialized = FALSE;
51 :
52 : CPL_C_START
53 : CPLErr CPL_DLL GTIFMemBufFromWkt( const char *pszWKT,
54 : const double *padfGeoTransform,
55 : int nGCPCount, const GDAL_GCP *pasGCPList,
56 : int *pnSize, unsigned char **ppabyBuffer );
57 : CPLErr CPL_DLL GTIFWktFromMemBuf( int nSize, unsigned char *pabyBuffer,
58 : char **ppszWKT, double *padfGeoTransform,
59 : int *pnGCPCount, GDAL_GCP **ppasGCPList );
60 : CPL_C_END
61 :
62 : void ECWInitialize( void );
63 :
64 : GDALDataset* ECWDatasetOpenJPEG2000(GDALOpenInfo* poOpenInfo);
65 :
66 : /************************************************************************/
67 : /* ==================================================================== */
68 : /* ECWDataset */
69 : /* ==================================================================== */
70 : /************************************************************************/
71 :
72 : class ECWRasterBand;
73 :
74 : class CPL_DLL ECWDataset : public GDALPamDataset
75 : {
76 : friend class ECWRasterBand;
77 :
78 : CNCSJP2FileView *poFileView;
79 : NCSFileViewFileInfoEx *psFileInfo;
80 :
81 : GDALDataType eRasterDataType;
82 : NCSEcwCellType eNCSRequestDataType;
83 :
84 : int bUsingCustomStream;
85 :
86 : // Current view window.
87 : int bWinActive;
88 : int nWinXOff, nWinYOff, nWinXSize, nWinYSize;
89 : int nWinBufXSize, nWinBufYSize;
90 : int nWinBandCount;
91 : int *panWinBandList;
92 : int nWinBufLoaded;
93 : void **papCurLineBuf;
94 :
95 : int bGeoTransformValid;
96 : double adfGeoTransform[6];
97 : char *pszProjection;
98 : int nGCPCount;
99 : GDAL_GCP *pasGCPList;
100 :
101 : char **papszGMLMetadata;
102 :
103 : void ECW2WKTProjection();
104 :
105 : void CleanupWindow();
106 : int TryWinRasterIO( GDALRWFlag, int, int, int, int,
107 : GByte *, int, int, GDALDataType,
108 : int, int *, int, int, int );
109 : CPLErr LoadNextLine();
110 :
111 : public:
112 : ECWDataset(int bIsJPEG2000);
113 : ~ECWDataset();
114 :
115 : static GDALDataset *Open( GDALOpenInfo *, int bIsJPEG2000 );
116 : static int IdentifyJPEG2000( GDALOpenInfo * poOpenInfo );
117 : static GDALDataset *OpenJPEG2000( GDALOpenInfo * );
118 : static int IdentifyECW( GDALOpenInfo * poOpenInfo );
119 : static GDALDataset *OpenECW( GDALOpenInfo * );
120 :
121 : virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int,
122 : void *, int, int, GDALDataType,
123 : int, int *, int, int, int );
124 :
125 : virtual CPLErr GetGeoTransform( double * );
126 : virtual const char *GetProjectionRef();
127 :
128 : virtual int GetGCPCount();
129 : virtual const char *GetGCPProjection();
130 : virtual const GDAL_GCP *GetGCPs();
131 :
132 : virtual char **GetMetadata( const char * pszDomain = "" );
133 :
134 : virtual CPLErr AdviseRead( int nXOff, int nYOff, int nXSize, int nYSize,
135 : int nBufXSize, int nBufYSize,
136 : GDALDataType eDT,
137 : int nBandCount, int *panBandList,
138 : char **papszOptions );
139 : };
140 :
141 : /************************************************************************/
142 : /* ==================================================================== */
143 : /* ECWRasterBand */
144 : /* ==================================================================== */
145 : /************************************************************************/
146 :
147 : class ECWRasterBand : public GDALPamRasterBand
148 : {
149 : friend class ECWDataset;
150 :
151 : // NOTE: poDS may be altered for NITF/JPEG2000 files!
152 : ECWDataset *poGDS;
153 :
154 : GDALColorInterp eBandInterp;
155 :
156 : virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int,
157 : void *, int, int, GDALDataType,
158 : int, int );
159 :
160 : public:
161 :
162 : ECWRasterBand( ECWDataset *, int );
163 : ~ECWRasterBand();
164 :
165 : virtual CPLErr IReadBlock( int, int, void * );
166 0 : virtual int HasArbitraryOverviews() { return TRUE; }
167 : virtual GDALColorInterp GetColorInterpretation();
168 : virtual CPLErr SetColorInterpretation( GDALColorInterp );
169 :
170 : virtual CPLErr AdviseRead( int nXOff, int nYOff, int nXSize, int nYSize,
171 : int nBufXSize, int nBufYSize,
172 : GDALDataType eDT, char **papszOptions );
173 : };
174 :
175 : /************************************************************************/
176 : /* ECWRasterBand() */
177 : /************************************************************************/
178 :
179 91 : ECWRasterBand::ECWRasterBand( ECWDataset *poDS, int nBand )
180 :
181 : {
182 91 : this->poDS = poDS;
183 91 : poGDS = poDS;
184 :
185 91 : this->nBand = nBand;
186 91 : eDataType = poDS->eRasterDataType;
187 91 : nBlockXSize = poDS->GetRasterXSize();
188 91 : nBlockYSize = 1;
189 :
190 : /* -------------------------------------------------------------------- */
191 : /* Work out band color interpretation. */
192 : /* -------------------------------------------------------------------- */
193 91 : if( poDS->psFileInfo->eColorSpace == NCSCS_NONE )
194 0 : eBandInterp = GCI_Undefined;
195 91 : else if( poDS->psFileInfo->eColorSpace == NCSCS_GREYSCALE )
196 12 : eBandInterp = GCI_GrayIndex;
197 79 : else if( poDS->psFileInfo->eColorSpace == NCSCS_MULTIBAND )
198 27 : eBandInterp = GCI_Undefined;
199 52 : else if( poDS->psFileInfo->eColorSpace == NCSCS_sRGB )
200 : {
201 52 : if( nBand == 1 )
202 17 : eBandInterp = GCI_RedBand;
203 35 : else if( nBand == 2 )
204 17 : eBandInterp = GCI_GreenBand;
205 18 : else if( nBand == 3 )
206 17 : eBandInterp = GCI_BlueBand;
207 : else
208 1 : eBandInterp = GCI_Undefined;
209 : }
210 0 : else if( poDS->psFileInfo->eColorSpace == NCSCS_YCbCr )
211 : {
212 0 : if( CSLTestBoolean( CPLGetConfigOption("CONVERT_YCBCR_TO_RGB","YES") ))
213 : {
214 0 : if( nBand == 1 )
215 0 : eBandInterp = GCI_RedBand;
216 0 : else if( nBand == 2 )
217 0 : eBandInterp = GCI_GreenBand;
218 0 : else if( nBand == 3 )
219 0 : eBandInterp = GCI_BlueBand;
220 : else
221 0 : eBandInterp = GCI_Undefined;
222 : }
223 : else
224 : {
225 0 : if( nBand == 1 )
226 0 : eBandInterp = GCI_YCbCr_YBand;
227 0 : else if( nBand == 2 )
228 0 : eBandInterp = GCI_YCbCr_CbBand;
229 0 : else if( nBand == 3 )
230 0 : eBandInterp = GCI_YCbCr_CrBand;
231 : else
232 0 : eBandInterp = GCI_Undefined;
233 : }
234 : }
235 : else
236 0 : eBandInterp = GCI_Undefined;
237 91 : }
238 :
239 : /************************************************************************/
240 : /* ~ECWRasterBand() */
241 : /************************************************************************/
242 :
243 182 : ECWRasterBand::~ECWRasterBand()
244 :
245 : {
246 91 : FlushCache();
247 182 : }
248 :
249 : /************************************************************************/
250 : /* GetColorInterpretation() */
251 : /************************************************************************/
252 :
253 47 : GDALColorInterp ECWRasterBand::GetColorInterpretation()
254 :
255 : {
256 47 : return eBandInterp;
257 : }
258 :
259 : /************************************************************************/
260 : /* SetColorInterpretation() */
261 : /* */
262 : /* This would normally just be used by folks using the ECW code */
263 : /* to read JP2 streams in other formats (such as NITF) and */
264 : /* providing their own color interpretation regardless of what */
265 : /* ECW might think the stream itself says. */
266 : /************************************************************************/
267 :
268 0 : CPLErr ECWRasterBand::SetColorInterpretation( GDALColorInterp eNewInterp )
269 :
270 : {
271 0 : eBandInterp = eNewInterp;
272 :
273 0 : return CE_None;
274 : }
275 :
276 : /************************************************************************/
277 : /* AdviseRead() */
278 : /************************************************************************/
279 :
280 31 : CPLErr ECWRasterBand::AdviseRead( int nXOff, int nYOff, int nXSize, int nYSize,
281 : int nBufXSize, int nBufYSize,
282 : GDALDataType eDT,
283 : char **papszOptions )
284 : {
285 : return poGDS->AdviseRead( nXOff, nYOff, nXSize, nYSize,
286 : nBufXSize, nBufYSize, eDT,
287 31 : 1, &nBand, papszOptions );
288 : }
289 :
290 : /************************************************************************/
291 : /* IRasterIO() */
292 : /************************************************************************/
293 :
294 11723 : CPLErr ECWRasterBand::IRasterIO( GDALRWFlag eRWFlag,
295 : int nXOff, int nYOff, int nXSize, int nYSize,
296 : void * pData, int nBufXSize, int nBufYSize,
297 : GDALDataType eBufType,
298 : int nPixelSpace, int nLineSpace )
299 :
300 : {
301 : int iBand, bDirect;
302 11723 : int nNewXSize = nBufXSize, nNewYSize = nBufYSize;
303 11723 : GByte *pabyWorkBuffer = NULL;
304 :
305 : /* -------------------------------------------------------------------- */
306 : /* Try to do it based on existing "advised" access. */
307 : /* -------------------------------------------------------------------- */
308 11723 : if( poGDS->TryWinRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize,
309 : (GByte *) pData, nBufXSize, nBufYSize,
310 : eBufType, 1, &nBand,
311 : nPixelSpace, nLineSpace, 0 ) )
312 11689 : return CE_None;
313 :
314 : /* -------------------------------------------------------------------- */
315 : /* We will drop down to the block oriented API if only a single */
316 : /* scanline was requested. This is based on the assumption that */
317 : /* doing lots of single scanline windows is expensive. */
318 : /* -------------------------------------------------------------------- */
319 34 : if( nYSize == 1 )
320 : {
321 : #ifdef notdef
322 : CPLDebug( "ECWRasterBand",
323 : "RasterIO(%d,%d,%d,%d -> %dx%d) - redirected.",
324 : nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize );
325 : #endif
326 : return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
327 : pData, nBufXSize, nBufYSize,
328 31 : eBufType, nPixelSpace, nLineSpace );
329 : }
330 :
331 : CPLDebug( "ECWRasterBand",
332 : "RasterIO(nXOff=%d,nYOff=%d,nXSize=%d,nYSize=%d -> %dx%d)",
333 3 : nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize );
334 :
335 :
336 3 : if ( nXSize < nBufXSize )
337 0 : nNewXSize = nXSize;
338 :
339 3 : if ( nYSize < nBufYSize )
340 0 : nNewYSize = nYSize;
341 :
342 : /* -------------------------------------------------------------------- */
343 : /* Default line and pixel spacing if needed. */
344 : /* -------------------------------------------------------------------- */
345 3 : if ( nPixelSpace == 0 )
346 0 : nPixelSpace = GDALGetDataTypeSize( eBufType ) / 8;
347 :
348 3 : if ( nLineSpace == 0 )
349 0 : nLineSpace = nPixelSpace * nBufXSize;
350 :
351 : /* -------------------------------------------------------------------- */
352 : /* Can we perform direct loads, or must we load into a working */
353 : /* buffer, and transform? */
354 : /* -------------------------------------------------------------------- */
355 3 : int nRawPixelSize = GDALGetDataTypeSize(poGDS->eRasterDataType) / 8;
356 :
357 : bDirect = nPixelSpace == 1 && eBufType == GDT_Byte
358 3 : && nNewXSize == nBufXSize && nNewYSize == nBufYSize;
359 3 : if( !bDirect )
360 1 : pabyWorkBuffer = (GByte *) CPLMalloc(nNewXSize * nRawPixelSize);
361 :
362 : /* -------------------------------------------------------------------- */
363 : /* Establish access at the desired resolution. */
364 : /* -------------------------------------------------------------------- */
365 3 : CNCSError oErr;
366 :
367 3 : poGDS->CleanupWindow();
368 :
369 3 : iBand = nBand-1;
370 : oErr = poGDS->poFileView->SetView( 1, (unsigned int *) (&iBand),
371 : nXOff, nYOff,
372 : nXOff + nXSize - 1,
373 : nYOff + nYSize - 1,
374 3 : nNewXSize, nNewYSize );
375 3 : if( oErr.GetErrorNumber() != NCS_SUCCESS )
376 : {
377 0 : CPLFree( pabyWorkBuffer );
378 0 : char* pszErrorMessage = oErr.GetErrorMessage();
379 : CPLError( CE_Failure, CPLE_AppDefined,
380 0 : "%s", pszErrorMessage );
381 0 : NCSFree(pszErrorMessage);
382 :
383 0 : return CE_Failure;
384 : }
385 :
386 : /* -------------------------------------------------------------------- */
387 : /* Read back one scanline at a time, till request is satisfied. */
388 : /* Supersampling is not supported by the ECW API, so we will do */
389 : /* it ourselves. */
390 : /* -------------------------------------------------------------------- */
391 3 : double dfSrcYInc = (double)nNewYSize / nBufYSize;
392 3 : double dfSrcXInc = (double)nNewXSize / nBufXSize;
393 : int iSrcLine, iDstLine;
394 :
395 759 : for( iSrcLine = 0, iDstLine = 0; iDstLine < nBufYSize; iDstLine++ )
396 : {
397 : NCSEcwReadStatus eRStatus;
398 756 : int iDstLineOff = iDstLine * nLineSpace;
399 : unsigned char *pabySrcBuf;
400 :
401 756 : if( bDirect )
402 736 : pabySrcBuf = ((GByte *)pData) + iDstLineOff;
403 : else
404 20 : pabySrcBuf = pabyWorkBuffer;
405 :
406 1512 : if ( nNewYSize == nBufYSize || iSrcLine == (int)(iDstLine * dfSrcYInc) )
407 : {
408 : eRStatus = poGDS->poFileView->ReadLineBIL(
409 756 : poGDS->eNCSRequestDataType, 1, (void **) &pabySrcBuf );
410 :
411 756 : if( eRStatus != NCSECW_READ_OK )
412 : {
413 0 : CPLFree( pabyWorkBuffer );
414 : CPLError( CE_Failure, CPLE_AppDefined,
415 0 : "NCScbmReadViewLineBIL failed." );
416 0 : return CE_Failure;
417 : }
418 :
419 756 : if( !bDirect )
420 : {
421 20 : if ( nNewXSize == nBufXSize )
422 : {
423 : GDALCopyWords( pabyWorkBuffer, poGDS->eRasterDataType,
424 : nRawPixelSize,
425 : ((GByte *)pData) + iDstLine * nLineSpace,
426 20 : eBufType, nPixelSpace, nBufXSize );
427 : }
428 : else
429 : {
430 : int iPixel;
431 :
432 0 : for ( iPixel = 0; iPixel < nBufXSize; iPixel++ )
433 : {
434 : GDALCopyWords( pabyWorkBuffer
435 : + nRawPixelSize*((int)(iPixel*dfSrcXInc)),
436 : poGDS->eRasterDataType, nRawPixelSize,
437 : (GByte *)pData + iDstLineOff
438 : + iPixel * nPixelSpace,
439 0 : eBufType, nPixelSpace, 1 );
440 : }
441 : }
442 : }
443 :
444 756 : iSrcLine++;
445 : }
446 : else
447 : {
448 : // Just copy the previous line in this case
449 : GDALCopyWords( (GByte *)pData + (iDstLineOff - nLineSpace),
450 : eBufType, nPixelSpace,
451 : (GByte *)pData + iDstLineOff,
452 0 : eBufType, nPixelSpace, nBufXSize );
453 : }
454 : }
455 :
456 3 : CPLFree( pabyWorkBuffer );
457 :
458 3 : return CE_None;
459 : }
460 :
461 : /************************************************************************/
462 : /* IReadBlock() */
463 : /************************************************************************/
464 :
465 31 : CPLErr ECWRasterBand::IReadBlock( int, int nBlockYOff, void * pImage )
466 :
467 : {
468 31 : CPLErr eErr = CE_None;
469 :
470 31 : if( poGDS->TryWinRasterIO( GF_Read, 0, nBlockYOff, nBlockXSize, 1,
471 : (GByte *) pImage, nBlockXSize, 1,
472 : eDataType, 1, &nBand, 0, 0, 0 ) )
473 0 : return CE_None;
474 :
475 : eErr = AdviseRead( 0, nBlockYOff, nRasterXSize, nRasterYSize - nBlockYOff,
476 : nRasterXSize, nRasterYSize - nBlockYOff,
477 31 : eDataType, NULL );
478 31 : if( eErr != CE_None )
479 0 : return eErr;
480 :
481 31 : if( poGDS->TryWinRasterIO( GF_Read, 0, nBlockYOff, nBlockXSize, 1,
482 : (GByte *) pImage, nBlockXSize, 1,
483 : eDataType, 1, &nBand, 0, 0, 0 ) )
484 31 : return CE_None;
485 :
486 : CPLError( CE_Failure, CPLE_AppDefined,
487 : "TryWinRasterIO() failed for blocked scanline %d of band %d.",
488 0 : nBlockYOff, nBand );
489 0 : return CE_Failure;
490 : }
491 :
492 : /************************************************************************/
493 : /* ==================================================================== */
494 : /* ECWDataset */
495 : /* ==================================================================== */
496 : /************************************************************************/
497 :
498 :
499 : /************************************************************************/
500 : /* ECWDataset() */
501 : /************************************************************************/
502 :
503 43 : ECWDataset::ECWDataset(int bIsJPEG2000)
504 :
505 : {
506 43 : bUsingCustomStream = FALSE;
507 43 : pszProjection = NULL;
508 43 : poFileView = NULL;
509 43 : bWinActive = FALSE;
510 43 : panWinBandList = NULL;
511 43 : eRasterDataType = GDT_Byte;
512 43 : nGCPCount = 0;
513 43 : pasGCPList = NULL;
514 43 : papszGMLMetadata = NULL;
515 :
516 43 : bGeoTransformValid = FALSE;
517 43 : adfGeoTransform[0] = 0.0;
518 43 : adfGeoTransform[1] = 1.0;
519 43 : adfGeoTransform[2] = 0.0;
520 43 : adfGeoTransform[3] = 0.0;
521 43 : adfGeoTransform[4] = 0.0;
522 43 : adfGeoTransform[5] = 1.0;
523 :
524 43 : poDriver = (GDALDriver*) GDALGetDriverByName( bIsJPEG2000 ? "JP2ECW" : "ECW" );
525 43 : }
526 :
527 : /************************************************************************/
528 : /* ~ECWDataset() */
529 : /************************************************************************/
530 :
531 86 : ECWDataset::~ECWDataset()
532 :
533 : {
534 43 : FlushCache();
535 43 : CleanupWindow();
536 43 : CPLFree( pszProjection );
537 43 : CSLDestroy( papszGMLMetadata );
538 :
539 43 : if( nGCPCount > 0 )
540 : {
541 4 : GDALDeinitGCPs( nGCPCount, pasGCPList );
542 4 : CPLFree( pasGCPList );
543 : }
544 :
545 : /* -------------------------------------------------------------------- */
546 : /* Release / dereference iostream. */
547 : /* -------------------------------------------------------------------- */
548 : // The underlying iostream of the CNCSJP2FileView (poFileView) object may
549 : // also be the underlying iostream of other CNCSJP2FileView (poFileView)
550 : // objects. Consequently, when we delete the CNCSJP2FileView (poFileView)
551 : // object, we must decrement the nFileViewCount attribute of the underlying
552 : // VSIIOStream object, and only delete the VSIIOStream object when
553 : // nFileViewCount is equal to zero.
554 :
555 43 : CPLMutexHolder oHolder( &hECWDatasetMutex );
556 :
557 43 : if( poFileView != NULL )
558 : {
559 43 : VSIIOStream *poUnderlyingIOStream = (VSIIOStream *)NULL;
560 :
561 43 : poUnderlyingIOStream = ((VSIIOStream *)(poFileView->GetStream()));
562 43 : delete poFileView;
563 :
564 43 : if( bUsingCustomStream )
565 : {
566 12 : if( --poUnderlyingIOStream->nFileViewCount == 0 )
567 12 : delete poUnderlyingIOStream;
568 : }
569 43 : }
570 86 : }
571 :
572 : /************************************************************************/
573 : /* AdviseRead() */
574 : /************************************************************************/
575 :
576 32 : CPLErr ECWDataset::AdviseRead( int nXOff, int nYOff, int nXSize, int nYSize,
577 : int nBufXSize, int nBufYSize,
578 : GDALDataType eDT,
579 : int nBandCount, int *panBandList,
580 : char **papszOptions )
581 :
582 : {
583 32 : int *panAdjustedBandList = NULL;
584 :
585 : CPLDebug( "ECW",
586 : "ECWDataset::AdviseRead(%d,%d,%d,%d->%d,%d)",
587 32 : nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize );
588 :
589 32 : if( nBufXSize > nXSize || nBufYSize > nYSize )
590 : {
591 : CPLError( CE_Warning, CPLE_AppDefined,
592 : "Supersampling not directly supported by ECW toolkit,\n"
593 0 : "ignoring AdviseRead() request." );
594 0 : return CE_Warning;
595 : }
596 :
597 : /* -------------------------------------------------------------------- */
598 : /* Adjust band numbers to be zero based. */
599 : /* -------------------------------------------------------------------- */
600 : panAdjustedBandList = (int *)
601 32 : CPLMalloc(sizeof(int) * nBandCount );
602 66 : for( int ii= 0; ii < nBandCount; ii++ )
603 34 : panAdjustedBandList[ii] = panBandList[ii] - 1;
604 :
605 : /* -------------------------------------------------------------------- */
606 : /* Cleanup old window cache information. */
607 : /* -------------------------------------------------------------------- */
608 32 : CleanupWindow();
609 :
610 : /* -------------------------------------------------------------------- */
611 : /* Set the new requested window. */
612 : /* -------------------------------------------------------------------- */
613 32 : CNCSError oErr;
614 :
615 : oErr = poFileView->SetView( nBandCount, (UINT32 *) panAdjustedBandList,
616 : nXOff, nYOff,
617 : nXOff + nXSize-1, nYOff + nYSize-1,
618 32 : nBufXSize, nBufYSize );
619 :
620 32 : CPLFree( panAdjustedBandList );
621 32 : if( oErr.GetErrorNumber() != NCS_SUCCESS )
622 : {
623 0 : char* pszErrorMessage = oErr.GetErrorMessage();
624 : CPLError( CE_Failure, CPLE_AppDefined,
625 0 : "%s", pszErrorMessage );
626 0 : NCSFree(pszErrorMessage);
627 0 : bWinActive = FALSE;
628 0 : return CE_Failure;
629 : }
630 :
631 32 : bWinActive = TRUE;
632 :
633 : /* -------------------------------------------------------------------- */
634 : /* Record selected window. */
635 : /* -------------------------------------------------------------------- */
636 32 : nWinXOff = nXOff;
637 32 : nWinYOff = nYOff;
638 32 : nWinXSize = nXSize;
639 32 : nWinYSize = nYSize;
640 32 : nWinBufXSize = nBufXSize;
641 32 : nWinBufYSize = nBufYSize;
642 :
643 32 : panWinBandList = (int *) CPLMalloc(sizeof(int)*nBandCount);
644 32 : memcpy( panWinBandList, panBandList, sizeof(int)* nBandCount);
645 32 : nWinBandCount = nBandCount;
646 :
647 32 : nWinBufLoaded = -1;
648 :
649 : /* -------------------------------------------------------------------- */
650 : /* Allocate current scanline buffer. */
651 : /* -------------------------------------------------------------------- */
652 32 : papCurLineBuf = (void **) CPLMalloc(sizeof(void*) * nWinBandCount );
653 66 : for( int iBand = 0; iBand < nWinBandCount; iBand++ )
654 34 : papCurLineBuf[iBand] =
655 34 : CPLMalloc(nBufXSize * (GDALGetDataTypeSize(eRasterDataType)/8) );
656 :
657 32 : return CE_None;
658 : }
659 :
660 : /************************************************************************/
661 : /* TryWinRasterIO() */
662 : /* */
663 : /* Try to satisfy the given request based on the currently */
664 : /* defined window. Return TRUE on success or FALSE on */
665 : /* failure. On failure, the caller should satisfy the request */
666 : /* another way (not report an error). */
667 : /************************************************************************/
668 :
669 12187 : int ECWDataset::TryWinRasterIO( GDALRWFlag eFlag,
670 : int nXOff, int nYOff, int nXSize, int nYSize,
671 : GByte *pabyData, int nBufXSize, int nBufYSize,
672 : GDALDataType eDT,
673 : int nBandCount, int *panBandList,
674 : int nPixelSpace, int nLineSpace,
675 : int nBandSpace )
676 :
677 : {
678 : int iBand, i;
679 :
680 : /* -------------------------------------------------------------------- */
681 : /* Provide default buffer organization. */
682 : /* -------------------------------------------------------------------- */
683 12187 : if( nPixelSpace == 0 )
684 62 : nPixelSpace = GDALGetDataTypeSize( eDT ) / 8;
685 12187 : if( nLineSpace == 0 )
686 62 : nLineSpace = nPixelSpace * nBufXSize;
687 12187 : if( nBandSpace == 0 )
688 11785 : nBandSpace = nLineSpace * nBufYSize;
689 :
690 : /* -------------------------------------------------------------------- */
691 : /* Do some simple tests to see if the current window can */
692 : /* satisfy our requirement. */
693 : /* -------------------------------------------------------------------- */
694 12187 : if( !bWinActive )
695 49 : return FALSE;
696 :
697 12138 : if( nXOff != nWinXOff || nXSize != nWinXSize )
698 0 : return FALSE;
699 :
700 12138 : if( nBufXSize != nWinBufXSize )
701 0 : return FALSE;
702 :
703 25058 : for( iBand = 0; iBand < nBandCount; iBand++ )
704 : {
705 12956 : for( i = 0; i < nWinBandCount; i++ )
706 : {
707 12938 : if( panWinBandList[iBand] == panBandList[iBand] )
708 12920 : break;
709 : }
710 :
711 12938 : if( i == nWinBandCount )
712 18 : return FALSE;
713 : }
714 :
715 12120 : if( nYOff < nWinYOff || nYOff + nYSize > nWinYOff + nWinYSize )
716 0 : return FALSE;
717 :
718 : /* -------------------------------------------------------------------- */
719 : /* Now we try more subtle tests. */
720 : /* -------------------------------------------------------------------- */
721 : {
722 : static int nDebugCount = 0;
723 :
724 12120 : if( nDebugCount < 30 )
725 : CPLDebug( "ECWDataset",
726 : "TryWinRasterIO(%d,%d,%d,%d -> %dx%d) - doing advised read.",
727 30 : nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize );
728 :
729 12120 : if( nDebugCount == 29 )
730 1 : CPLDebug( "ECWDataset", "No more TryWinRasterIO messages will be reported" );
731 :
732 12120 : nDebugCount++;
733 : }
734 :
735 : /* -------------------------------------------------------------------- */
736 : /* Actually load data one buffer line at a time. */
737 : /* -------------------------------------------------------------------- */
738 : int iBufLine;
739 :
740 24240 : for( iBufLine = 0; iBufLine < nBufYSize; iBufLine++ )
741 : {
742 12120 : float fFileLine = ((iBufLine+0.5) / nBufYSize) * nYSize + nYOff;
743 : int iWinLine =
744 12120 : (int) (((fFileLine - nWinYOff) / nWinYSize) * nWinBufYSize);
745 :
746 12120 : if( iWinLine == nWinBufLoaded + 1 )
747 12120 : LoadNextLine();
748 :
749 12120 : if( iWinLine != nWinBufLoaded )
750 0 : return FALSE;
751 :
752 : /* -------------------------------------------------------------------- */
753 : /* Copy out all our target bands. */
754 : /* -------------------------------------------------------------------- */
755 : int iWinBand;
756 25040 : for( iBand = 0; iBand < nBandCount; iBand++ )
757 : {
758 14120 : for( iWinBand = 0; iWinBand < nWinBandCount; iWinBand++ )
759 : {
760 14120 : if( panWinBandList[iWinBand] == panBandList[iBand] )
761 12920 : break;
762 : }
763 :
764 : GDALCopyWords( papCurLineBuf[iWinBand], eRasterDataType,
765 : GDALGetDataTypeSize( eRasterDataType ) / 8,
766 : pabyData + nBandSpace * iBand
767 : + iBufLine * nLineSpace, eDT, nPixelSpace,
768 12920 : nBufXSize );
769 : }
770 : }
771 :
772 12120 : return TRUE;
773 : }
774 :
775 : /************************************************************************/
776 : /* LoadNextLine() */
777 : /************************************************************************/
778 :
779 12120 : CPLErr ECWDataset::LoadNextLine()
780 :
781 : {
782 12120 : if( !bWinActive )
783 0 : return CE_Failure;
784 :
785 12120 : if( nWinBufLoaded == nWinBufYSize-1 )
786 : {
787 0 : CleanupWindow();
788 0 : return CE_Failure;
789 : }
790 :
791 : NCSEcwReadStatus eRStatus;
792 : eRStatus = poFileView->ReadLineBIL( eNCSRequestDataType, nWinBandCount,
793 12120 : papCurLineBuf );
794 12120 : if( eRStatus != NCSECW_READ_OK )
795 0 : return CE_Failure;
796 :
797 12120 : nWinBufLoaded++;
798 :
799 12120 : return CE_None;
800 : }
801 :
802 : /************************************************************************/
803 : /* CleanupWindow() */
804 : /************************************************************************/
805 :
806 79 : void ECWDataset::CleanupWindow()
807 :
808 : {
809 79 : if( !bWinActive )
810 47 : return;
811 :
812 32 : bWinActive = FALSE;
813 32 : CPLFree( panWinBandList );
814 32 : panWinBandList = NULL;
815 :
816 66 : for( int iBand = 0; iBand < nWinBandCount; iBand++ )
817 34 : CPLFree( papCurLineBuf[iBand] );
818 32 : CPLFree( papCurLineBuf );
819 32 : papCurLineBuf = NULL;
820 : }
821 :
822 : /************************************************************************/
823 : /* IRasterIO() */
824 : /************************************************************************/
825 :
826 401 : CPLErr ECWDataset::IRasterIO( GDALRWFlag eRWFlag,
827 : int nXOff, int nYOff, int nXSize, int nYSize,
828 : void * pData, int nBufXSize, int nBufYSize,
829 : GDALDataType eBufType,
830 : int nBandCount, int *panBandMap,
831 : int nPixelSpace, int nLineSpace, int nBandSpace)
832 :
833 : {
834 : /* -------------------------------------------------------------------- */
835 : /* Try to do it based on existing "advised" access. */
836 : /* -------------------------------------------------------------------- */
837 401 : if( TryWinRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize,
838 : (GByte *) pData, nBufXSize, nBufYSize,
839 : eBufType, nBandCount, panBandMap,
840 : nPixelSpace, nLineSpace, nBandSpace ) )
841 399 : return CE_None;
842 :
843 : /* -------------------------------------------------------------------- */
844 : /* If we are requesting a single line at 1:1, we do a multi-band */
845 : /* AdviseRead() and then TryWinRasterIO() again. */
846 : /* -------------------------------------------------------------------- */
847 2 : if( nYSize == 1 && nBufYSize == 1 && nBandCount > 1 )
848 : {
849 : CPLErr eErr;
850 :
851 : eErr = AdviseRead( nXOff, nYOff, nXSize, GetRasterYSize() - nYOff,
852 : nBufXSize, GetRasterYSize() - nYOff, eBufType,
853 1 : nBandCount, panBandMap, NULL );
854 1 : if( eErr == CE_None
855 : && TryWinRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize,
856 : (GByte *) pData, nBufXSize, nBufYSize,
857 : eBufType, nBandCount, panBandMap,
858 : nPixelSpace, nLineSpace, nBandSpace ) )
859 1 : return CE_None;
860 : }
861 :
862 : /* -------------------------------------------------------------------- */
863 : /* If we are supersampling we need to fall into the general */
864 : /* purpose logic. We also use the general logic if we are in */
865 : /* some cases unlikely to benefit from interleaved access. */
866 : /* */
867 : /* The one case we would like to handle better here is the */
868 : /* nBufYSize == 1 case (requesting a scanline at a time). We */
869 : /* should eventually have some logic similiar to the band by */
870 : /* band case where we post a big window for the view, and allow */
871 : /* sequential reads. */
872 : /* -------------------------------------------------------------------- */
873 1 : if( nXSize < nBufXSize || nYSize < nBufYSize || nYSize == 1
874 : || nBandCount > 100 || nBandCount == 1 || nBufYSize == 1
875 : || nBandCount > GetRasterCount() )
876 : {
877 : return
878 : GDALDataset::IRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize,
879 : pData, nBufXSize, nBufYSize,
880 : eBufType,
881 : nBandCount, panBandMap,
882 0 : nPixelSpace, nLineSpace, nBandSpace);
883 : }
884 :
885 : CPLDebug( "ECWDataset",
886 : "RasterIO(%d,%d,%d,%d -> %dx%d) - doing interleaved read.",
887 1 : nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize );
888 :
889 : /* -------------------------------------------------------------------- */
890 : /* Setup view. */
891 : /* -------------------------------------------------------------------- */
892 : UINT32 anBandIndices[100];
893 : int i;
894 : NCSError eNCSErr;
895 1 : CNCSError oErr;
896 :
897 4 : for( i = 0; i < nBandCount; i++ )
898 3 : anBandIndices[i] = panBandMap[i] - 1;
899 :
900 1 : CleanupWindow();
901 :
902 : oErr = poFileView->SetView( nBandCount, anBandIndices,
903 : nXOff, nYOff,
904 : nXOff + nXSize - 1,
905 : nYOff + nYSize - 1,
906 1 : nBufXSize, nBufYSize );
907 1 : eNCSErr = oErr.GetErrorNumber();
908 :
909 1 : if( eNCSErr != NCS_SUCCESS )
910 : {
911 : CPLError( CE_Failure, CPLE_AppDefined,
912 0 : "%s", NCSGetErrorText(eNCSErr) );
913 :
914 0 : return CE_Failure;
915 : }
916 :
917 : /* -------------------------------------------------------------------- */
918 : /* Setup working scanline, and the pointers into it. */
919 : /* -------------------------------------------------------------------- */
920 1 : int nDataTypeSize = (GDALGetDataTypeSize(eRasterDataType) / 8);
921 : GByte *pabyBILScanline = (GByte *) CPLMalloc(nBufXSize * nDataTypeSize *
922 1 : nBandCount);
923 1 : GByte **papabyBIL = (GByte **) CPLMalloc(nBandCount * sizeof(void*));
924 :
925 4 : for( i = 0; i < nBandCount; i++ )
926 3 : papabyBIL[i] = pabyBILScanline + i * nBufXSize * nDataTypeSize;
927 :
928 : /* -------------------------------------------------------------------- */
929 : /* Read back all the data for the requested view. */
930 : /* -------------------------------------------------------------------- */
931 41 : for( int iScanline = 0; iScanline < nBufYSize; iScanline++ )
932 : {
933 : NCSEcwReadStatus eRStatus;
934 :
935 : eRStatus = poFileView->ReadLineBIL( eNCSRequestDataType, nBandCount,
936 40 : (void **) papabyBIL );
937 40 : if( eRStatus != NCSECW_READ_OK )
938 : {
939 0 : CPLFree( papabyBIL );
940 0 : CPLFree( pabyBILScanline );
941 : CPLError( CE_Failure, CPLE_AppDefined,
942 0 : "NCScbmReadViewLineBIL failed." );
943 0 : return CE_Failure;
944 : }
945 :
946 160 : for( i = 0; i < nBandCount; i++ )
947 : {
948 : GDALCopyWords(
949 : pabyBILScanline + i * nDataTypeSize * nBufXSize,
950 : eRasterDataType, nDataTypeSize,
951 : ((GByte *) pData) + nLineSpace * iScanline + nBandSpace * i,
952 : eBufType, nPixelSpace,
953 120 : nBufXSize );
954 : }
955 : }
956 :
957 1 : CPLFree( pabyBILScanline );
958 1 : CPLFree( papabyBIL );
959 :
960 1 : return CE_None;
961 : }
962 :
963 : /************************************************************************/
964 : /* IdentifyJPEG2000() */
965 : /* */
966 : /* Open method that only supports JPEG2000 files. */
967 : /************************************************************************/
968 :
969 8674 : int ECWDataset::IdentifyJPEG2000( GDALOpenInfo * poOpenInfo )
970 :
971 : {
972 8674 : if( EQUALN(poOpenInfo->pszFilename,"J2K_SUBFILE:",12) )
973 8 : return TRUE;
974 :
975 8666 : else if( poOpenInfo->nHeaderBytes >= 16
976 : && (memcmp( poOpenInfo->pabyHeader, jpc_header,
977 : sizeof(jpc_header) ) == 0
978 : || memcmp( poOpenInfo->pabyHeader, jp2_header,
979 : sizeof(jp2_header) ) == 0) )
980 37 : return TRUE;
981 :
982 : else
983 8629 : return FALSE;
984 : }
985 :
986 : /************************************************************************/
987 : /* OpenJPEG2000() */
988 : /* */
989 : /* Open method that only supports JPEG2000 files. */
990 : /************************************************************************/
991 :
992 956 : GDALDataset *ECWDataset::OpenJPEG2000( GDALOpenInfo * poOpenInfo )
993 :
994 : {
995 956 : if (!IdentifyJPEG2000(poOpenInfo))
996 914 : return NULL;
997 :
998 42 : return Open( poOpenInfo, TRUE );
999 : }
1000 :
1001 : /************************************************************************/
1002 : /* IdentifyECW() */
1003 : /* */
1004 : /* Identify method that only supports ECW files. */
1005 : /************************************************************************/
1006 :
1007 8986 : int ECWDataset::IdentifyECW( GDALOpenInfo * poOpenInfo )
1008 :
1009 : {
1010 : /* -------------------------------------------------------------------- */
1011 : /* This has to either be a file on disk ending in .ecw or a */
1012 : /* ecwp: protocol url. */
1013 : /* -------------------------------------------------------------------- */
1014 8986 : if( (!EQUAL(CPLGetExtension(poOpenInfo->pszFilename),"ecw")
1015 : || poOpenInfo->nHeaderBytes == 0)
1016 : && !EQUALN(poOpenInfo->pszFilename,"ecwp:",5) )
1017 8982 : return FALSE;
1018 :
1019 4 : return TRUE;
1020 : }
1021 :
1022 : /************************************************************************/
1023 : /* OpenECW() */
1024 : /* */
1025 : /* Open method that only supports ECW files. */
1026 : /************************************************************************/
1027 :
1028 1257 : GDALDataset *ECWDataset::OpenECW( GDALOpenInfo * poOpenInfo )
1029 :
1030 : {
1031 1257 : if (!IdentifyECW(poOpenInfo))
1032 1253 : return NULL;
1033 :
1034 4 : return Open( poOpenInfo, FALSE );
1035 : }
1036 :
1037 : /************************************************************************/
1038 : /* Open() */
1039 : /************************************************************************/
1040 :
1041 46 : GDALDataset *ECWDataset::Open( GDALOpenInfo * poOpenInfo, int bIsJPEG2000 )
1042 :
1043 : {
1044 46 : CNCSJP2FileView *poFileView = NULL;
1045 : NCSError eErr;
1046 46 : CNCSError oErr;
1047 : int i;
1048 46 : FILE *fpVSIL = NULL;
1049 46 : VSIIOStream *poIOStream = NULL;
1050 46 : int bUsingCustomStream = FALSE;
1051 :
1052 46 : ECWInitialize();
1053 :
1054 : /* -------------------------------------------------------------------- */
1055 : /* This will disable automatic conversion of YCbCr to RGB by */
1056 : /* the toolkit. */
1057 : /* -------------------------------------------------------------------- */
1058 46 : if( !CSLTestBoolean( CPLGetConfigOption("CONVERT_YCBCR_TO_RGB","YES") ) )
1059 0 : NCSecwSetConfig(NCSCFG_JP2_MANAGE_ICC, FALSE);
1060 :
1061 : /* -------------------------------------------------------------------- */
1062 : /* Handle special case of a JPEG2000 data stream in another file. */
1063 : /* -------------------------------------------------------------------- */
1064 46 : int bIsVirtualFile = FALSE;
1065 : try_again:
1066 68 : if( EQUALN(poOpenInfo->pszFilename,"J2K_SUBFILE:",12) ||
1067 : bIsVirtualFile )
1068 : {
1069 15 : GIntBig subfile_offset=-1, subfile_size=-1;
1070 15 : const char *real_filename = NULL;
1071 :
1072 15 : if (EQUALN(poOpenInfo->pszFilename,"J2K_SUBFILE:",12))
1073 : {
1074 5 : char** papszTokens = CSLTokenizeString2(poOpenInfo->pszFilename + 12, ",", 0);
1075 5 : if (CSLCount(papszTokens) >= 2)
1076 : {
1077 5 : subfile_offset = CPLScanUIntBig(papszTokens[0], strlen(papszTokens[0]));
1078 5 : subfile_size = CPLScanUIntBig(papszTokens[1], strlen(papszTokens[1]));
1079 : }
1080 : else
1081 : {
1082 : CPLError( CE_Failure, CPLE_OpenFailed,
1083 0 : "Failed to parse J2K_SUBFILE specification." );
1084 0 : CSLDestroy(papszTokens);
1085 0 : return NULL;
1086 : }
1087 5 : CSLDestroy(papszTokens);
1088 :
1089 5 : real_filename = strstr(poOpenInfo->pszFilename,",");
1090 5 : if( real_filename != NULL )
1091 5 : real_filename = strstr(real_filename+1,",");
1092 5 : if( real_filename != NULL )
1093 5 : real_filename++;
1094 : else
1095 : {
1096 : CPLError( CE_Failure, CPLE_OpenFailed,
1097 0 : "Failed to parse J2K_SUBFILE specification." );
1098 0 : return NULL;
1099 : }
1100 :
1101 : }
1102 : else
1103 : {
1104 10 : real_filename = poOpenInfo->pszFilename;
1105 10 : subfile_offset = 0;
1106 : }
1107 :
1108 15 : fpVSIL = VSIFOpenL( real_filename, "rb" );
1109 15 : if( fpVSIL == NULL )
1110 : {
1111 : CPLError( CE_Failure, CPLE_OpenFailed,
1112 0 : "Failed to open %s.", real_filename );
1113 0 : return NULL;
1114 : }
1115 :
1116 15 : if( hECWDatasetMutex == NULL )
1117 : {
1118 0 : hECWDatasetMutex = CPLCreateMutex();
1119 : }
1120 15 : else if( !CPLAcquireMutex( hECWDatasetMutex, 60.0 ) )
1121 : {
1122 0 : CPLDebug( "ECW", "Failed to acquire mutex in 60s." );
1123 : }
1124 : else
1125 : {
1126 15 : CPLDebug( "ECW", "Got mutex." );
1127 : }
1128 15 : poIOStream = new VSIIOStream();
1129 : poIOStream->Access( fpVSIL, FALSE, real_filename,
1130 30 : subfile_offset, subfile_size );
1131 :
1132 15 : poFileView = new CNCSJP2FileView();
1133 30 : oErr = poFileView->Open( poIOStream, false );
1134 :
1135 : // The CNCSJP2FileView (poFileView) object may not use the iostream
1136 : // (poIOStream) passed to the CNCSJP2FileView::Open() method if an
1137 : // iostream is already available to the ECW JPEG 2000 SDK for a given
1138 : // file. Consequently, if the iostream passed to
1139 : // CNCSJP2FileView::Open() does not become the underlying iostream
1140 : // of the CNCSJP2FileView object, then it should be deleted.
1141 : //
1142 : // In addition, the underlying iostream of the CNCSJP2FileView object
1143 : // should not be deleted until all CNCSJP2FileView objects using the
1144 : // underlying iostream are deleted. Consequently, each time a
1145 : // CNCSJP2FileView object is created, the nFileViewCount attribute
1146 : // of the underlying VSIIOStream object must be incremented for use
1147 : // in the ECWDataset destructor.
1148 :
1149 : VSIIOStream * poUnderlyingIOStream =
1150 15 : ((VSIIOStream *)(poFileView->GetStream()));
1151 :
1152 15 : if ( poUnderlyingIOStream )
1153 12 : poUnderlyingIOStream->nFileViewCount++;
1154 :
1155 15 : if ( poIOStream != poUnderlyingIOStream )
1156 : {
1157 3 : delete poIOStream;
1158 : }
1159 : else
1160 : {
1161 12 : bUsingCustomStream = TRUE;
1162 : }
1163 :
1164 15 : CPLReleaseMutex( hECWDatasetMutex );
1165 :
1166 15 : if( oErr.GetErrorNumber() != NCS_SUCCESS )
1167 : {
1168 3 : if (poFileView)
1169 3 : delete poFileView;
1170 :
1171 3 : char* pszErrorMessage = oErr.GetErrorMessage();
1172 : CPLError( CE_Failure, CPLE_AppDefined,
1173 3 : "%s", pszErrorMessage );
1174 3 : NCSFree(pszErrorMessage);
1175 :
1176 3 : return NULL;
1177 : }
1178 : }
1179 :
1180 : /* -------------------------------------------------------------------- */
1181 : /* This has to either be a file on disk ending in .ecw or a */
1182 : /* ecwp: protocol url. */
1183 : /* -------------------------------------------------------------------- */
1184 41 : else if( poOpenInfo->nHeaderBytes >= 16
1185 : && (memcmp( poOpenInfo->pabyHeader, jpc_header,
1186 : sizeof(jpc_header) ) == 0
1187 : || memcmp( poOpenInfo->pabyHeader, jp2_header,
1188 : sizeof(jp2_header) ) == 0) )
1189 : {
1190 : /* accept JPEG2000 files */
1191 : }
1192 4 : else if( (!EQUAL(CPLGetExtension(poOpenInfo->pszFilename),"ecw")
1193 : || poOpenInfo->nHeaderBytes == 0)
1194 : && !EQUALN(poOpenInfo->pszFilename,"ecwp:",5) )
1195 0 : return( NULL );
1196 :
1197 : /* -------------------------------------------------------------------- */
1198 : /* Open the client interface. */
1199 : /* -------------------------------------------------------------------- */
1200 53 : if( poFileView == NULL )
1201 : {
1202 41 : poFileView = new CNCSFile();
1203 82 : oErr = poFileView->Open( (char *) poOpenInfo->pszFilename, FALSE );
1204 41 : eErr = oErr.GetErrorNumber();
1205 : CPLDebug( "ECW", "NCScbmOpenFileView(%s): eErr = %d",
1206 41 : poOpenInfo->pszFilename, (int) eErr );
1207 41 : if( eErr != NCS_SUCCESS )
1208 : {
1209 10 : delete poFileView;
1210 :
1211 : /* If the file is not a 'real' file but recognized as a */
1212 : /* virtual file by the VSIL API, try again by using a */
1213 : /* VSIIOStream object, like in the J2K_SUBFILE case */
1214 : VSIStatBuf sBuf;
1215 : VSIStatBufL sBufL;
1216 10 : if (!bIsVirtualFile &&
1217 : VSIStat(poOpenInfo->pszFilename, &sBuf) != 0 &&
1218 : VSIStatL(poOpenInfo->pszFilename, &sBufL) == 0)
1219 : {
1220 10 : bIsVirtualFile = TRUE;
1221 10 : goto try_again;
1222 : }
1223 :
1224 : CPLError( CE_Failure, CPLE_AppDefined,
1225 0 : "%s", NCSGetErrorText(eErr) );
1226 0 : return NULL;
1227 : }
1228 : }
1229 :
1230 43 : if( poOpenInfo->eAccess == GA_Update )
1231 : {
1232 : CPLError( CE_Failure, CPLE_NotSupported,
1233 : "The DIMAP driver does not support update access to existing"
1234 0 : " datasets.\n" );
1235 0 : return NULL;
1236 : }
1237 :
1238 : /* -------------------------------------------------------------------- */
1239 : /* Create a corresponding GDALDataset. */
1240 : /* -------------------------------------------------------------------- */
1241 : ECWDataset *poDS;
1242 :
1243 43 : poDS = new ECWDataset(bIsJPEG2000);
1244 :
1245 43 : poDS->poFileView = poFileView;
1246 :
1247 43 : if( fpVSIL != NULL )
1248 12 : poDS->nPamFlags |= GPF_DISABLED;
1249 :
1250 43 : poDS->bUsingCustomStream = bUsingCustomStream;
1251 :
1252 : /* -------------------------------------------------------------------- */
1253 : /* Fetch general file information. */
1254 : /* -------------------------------------------------------------------- */
1255 43 : poDS->psFileInfo = poFileView->GetFileInfo();
1256 :
1257 : CPLDebug( "ECW", "FileInfo: SizeXY=%d,%d Bands=%d\n"
1258 : " OriginXY=%g,%g CellIncrementXY=%g,%g\n",
1259 : poDS->psFileInfo->nSizeX,
1260 : poDS->psFileInfo->nSizeY,
1261 : poDS->psFileInfo->nBands,
1262 : poDS->psFileInfo->fOriginX,
1263 : poDS->psFileInfo->fOriginY,
1264 : poDS->psFileInfo->fCellIncrementX,
1265 43 : poDS->psFileInfo->fCellIncrementY );
1266 :
1267 : /* -------------------------------------------------------------------- */
1268 : /* Establish raster info. */
1269 : /* -------------------------------------------------------------------- */
1270 43 : poDS->nRasterXSize = poDS->psFileInfo->nSizeX;
1271 43 : poDS->nRasterYSize = poDS->psFileInfo->nSizeY;
1272 :
1273 : /* -------------------------------------------------------------------- */
1274 : /* Establish the GDAL data type that corresponds. A few NCS */
1275 : /* data types have no direct corresponding value in GDAL so we */
1276 : /* will coerce to something sufficiently similar. */
1277 : /* -------------------------------------------------------------------- */
1278 43 : poDS->eNCSRequestDataType = poDS->psFileInfo->eCellType;
1279 43 : switch( poDS->psFileInfo->eCellType )
1280 : {
1281 : case NCSCT_UINT8:
1282 32 : poDS->eRasterDataType = GDT_Byte;
1283 32 : break;
1284 :
1285 : case NCSCT_UINT16:
1286 5 : poDS->eRasterDataType = GDT_UInt16;
1287 5 : break;
1288 :
1289 : case NCSCT_UINT32:
1290 : case NCSCT_UINT64:
1291 1 : poDS->eRasterDataType = GDT_UInt32;
1292 1 : poDS->eNCSRequestDataType = NCSCT_UINT32;
1293 1 : break;
1294 :
1295 : case NCSCT_INT8:
1296 : case NCSCT_INT16:
1297 3 : poDS->eRasterDataType = GDT_Int16;
1298 3 : poDS->eNCSRequestDataType = NCSCT_INT16;
1299 3 : break;
1300 :
1301 : case NCSCT_INT32:
1302 : case NCSCT_INT64:
1303 2 : poDS->eRasterDataType = GDT_Int32;
1304 2 : poDS->eNCSRequestDataType = NCSCT_INT32;
1305 2 : break;
1306 :
1307 : case NCSCT_IEEE4:
1308 0 : poDS->eRasterDataType = GDT_Float32;
1309 0 : break;
1310 :
1311 : case NCSCT_IEEE8:
1312 0 : poDS->eRasterDataType = GDT_Float64;
1313 : break;
1314 : }
1315 :
1316 : /* -------------------------------------------------------------------- */
1317 : /* Create band information objects. */
1318 : /* -------------------------------------------------------------------- */
1319 134 : for( i=0; i < poDS->psFileInfo->nBands; i++ )
1320 91 : poDS->SetBand( i+1, new ECWRasterBand( poDS, i+1 ) );
1321 :
1322 : /* -------------------------------------------------------------------- */
1323 : /* Look for supporting coordinate system information. */
1324 : /* -------------------------------------------------------------------- */
1325 43 : GDALJP2Metadata oJP2Geo;
1326 :
1327 43 : if( oJP2Geo.ReadAndParse( poOpenInfo->pszFilename ) )
1328 : {
1329 22 : poDS->pszProjection = CPLStrdup(oJP2Geo.pszProjection);
1330 22 : poDS->bGeoTransformValid = oJP2Geo.bHaveGeoTransform;
1331 : memcpy( poDS->adfGeoTransform, oJP2Geo.adfGeoTransform,
1332 22 : sizeof(double) * 6 );
1333 22 : poDS->nGCPCount = oJP2Geo.nGCPCount;
1334 22 : poDS->pasGCPList = oJP2Geo.pasGCPList;
1335 22 : oJP2Geo.pasGCPList = NULL;
1336 22 : oJP2Geo.nGCPCount = 0;
1337 : }
1338 : else
1339 : {
1340 21 : poDS->ECW2WKTProjection();
1341 : }
1342 :
1343 : /* -------------------------------------------------------------------- */
1344 : /* Check for world file for ecw files. */
1345 : /* -------------------------------------------------------------------- */
1346 43 : if( !poDS->bGeoTransformValid
1347 : && EQUAL(CPLGetExtension(poOpenInfo->pszFilename),"ecw") )
1348 : {
1349 : poDS->bGeoTransformValid |=
1350 : GDALReadWorldFile( poOpenInfo->pszFilename, ".eww",
1351 : poDS->adfGeoTransform )
1352 : || GDALReadWorldFile( poOpenInfo->pszFilename, ".ecww",
1353 : poDS->adfGeoTransform )
1354 : || GDALReadWorldFile( poOpenInfo->pszFilename, ".wld",
1355 0 : poDS->adfGeoTransform );
1356 : }
1357 :
1358 : /* -------------------------------------------------------------------- */
1359 : /* Initialize any PAM information. */
1360 : /* -------------------------------------------------------------------- */
1361 43 : poDS->SetDescription( poOpenInfo->pszFilename );
1362 43 : poDS->TryLoadXML();
1363 :
1364 : /* -------------------------------------------------------------------- */
1365 : /* Confirm the requested access is supported. */
1366 : /* -------------------------------------------------------------------- */
1367 43 : if( poOpenInfo->eAccess == GA_Update )
1368 : {
1369 0 : delete poDS;
1370 : CPLError( CE_Failure, CPLE_NotSupported,
1371 : "The ECW driver does not support update access to existing"
1372 0 : " datasets.\n" );
1373 0 : return NULL;
1374 : }
1375 :
1376 43 : return( poDS );
1377 : }
1378 :
1379 : /************************************************************************/
1380 : /* GetGCPCount() */
1381 : /************************************************************************/
1382 :
1383 6 : int ECWDataset::GetGCPCount()
1384 :
1385 : {
1386 6 : if( nGCPCount != 0 )
1387 3 : return nGCPCount;
1388 : else
1389 3 : return GDALPamDataset::GetGCPCount();
1390 : }
1391 :
1392 : /************************************************************************/
1393 : /* GetGCPProjection() */
1394 : /************************************************************************/
1395 :
1396 2 : const char *ECWDataset::GetGCPProjection()
1397 :
1398 : {
1399 2 : if( nGCPCount > 0 )
1400 2 : return pszProjection;
1401 : else
1402 0 : return GDALPamDataset::GetGCPProjection();
1403 : }
1404 :
1405 : /************************************************************************/
1406 : /* GetGCP() */
1407 : /************************************************************************/
1408 :
1409 3 : const GDAL_GCP *ECWDataset::GetGCPs()
1410 :
1411 : {
1412 3 : if( nGCPCount != 0 )
1413 2 : return pasGCPList;
1414 : else
1415 1 : return GDALPamDataset::GetGCPs();
1416 : }
1417 :
1418 : /************************************************************************/
1419 : /* GetProjectionRef() */
1420 : /* */
1421 : /* We let PAM coordinate system override the one stored inside */
1422 : /* our file. */
1423 : /************************************************************************/
1424 :
1425 60 : const char *ECWDataset::GetProjectionRef()
1426 :
1427 : {
1428 60 : const char* pszPamPrj = GDALPamDataset::GetProjectionRef();
1429 :
1430 60 : if( pszProjection != NULL && strlen(pszPamPrj) == 0 )
1431 40 : return pszProjection;
1432 : else
1433 20 : return pszPamPrj;
1434 : }
1435 :
1436 : /************************************************************************/
1437 : /* GetGeoTransform() */
1438 : /* */
1439 : /* Only return the native geotransform if we appear to be */
1440 : /* returning the native coordinate system, otherwise defer to */
1441 : /* the PAM geotransform. */
1442 : /************************************************************************/
1443 :
1444 22 : CPLErr ECWDataset::GetGeoTransform( double * padfTransform )
1445 :
1446 : {
1447 29 : if( (GetProjectionRef() != pszProjection
1448 7 : && strlen(GetProjectionRef()) > 0)
1449 : || !bGeoTransformValid )
1450 : {
1451 2 : return GDALPamDataset::GetGeoTransform( padfTransform );
1452 : }
1453 : else
1454 : {
1455 20 : memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 );
1456 20 : return( CE_None );
1457 : }
1458 : }
1459 :
1460 : /************************************************************************/
1461 : /* GetMetadata() */
1462 : /************************************************************************/
1463 :
1464 13 : char **ECWDataset::GetMetadata( const char *pszDomain )
1465 :
1466 : {
1467 13 : if( pszDomain == NULL || !EQUAL(pszDomain,"GML") )
1468 13 : return GDALPamDataset::GetMetadata( pszDomain );
1469 : else
1470 0 : return papszGMLMetadata;
1471 : }
1472 :
1473 : /************************************************************************/
1474 : /* ECW2WKTProjection() */
1475 : /* */
1476 : /* Set the dataset pszProjection string in OGC WKT format by */
1477 : /* looking up the ECW (GDT) coordinate system info in */
1478 : /* ecw_cs.dat support data file. */
1479 : /* */
1480 : /* This code is likely still broken in some circumstances. For */
1481 : /* instance, I haven't been careful about changing the linear */
1482 : /* projection parameters (false easting/northing) if the units */
1483 : /* is feet. Lots of cases missing here, and in ecw_cs.dat. */
1484 : /************************************************************************/
1485 :
1486 21 : void ECWDataset::ECW2WKTProjection()
1487 :
1488 : {
1489 21 : if( psFileInfo == NULL )
1490 0 : return;
1491 :
1492 : /* -------------------------------------------------------------------- */
1493 : /* Capture Geotransform. */
1494 : /* */
1495 : /* We will try to ignore the provided file information if it is */
1496 : /* origin (0,0) and pixel size (1,1). I think sometimes I have */
1497 : /* also seen pixel increments of 0 on invalid datasets. */
1498 : /* -------------------------------------------------------------------- */
1499 21 : if( psFileInfo->fOriginX != 0.0
1500 : || psFileInfo->fOriginY != 0.0
1501 : || (psFileInfo->fCellIncrementX != 0.0
1502 : && psFileInfo->fCellIncrementX != 1.0)
1503 : || (psFileInfo->fCellIncrementY != 0.0
1504 : && psFileInfo->fCellIncrementY != 1.0) )
1505 : {
1506 4 : bGeoTransformValid = TRUE;
1507 :
1508 4 : adfGeoTransform[0] = psFileInfo->fOriginX;
1509 4 : adfGeoTransform[1] = psFileInfo->fCellIncrementX;
1510 4 : adfGeoTransform[2] = 0.0;
1511 :
1512 4 : adfGeoTransform[3] = psFileInfo->fOriginY;
1513 4 : adfGeoTransform[4] = 0.0;
1514 4 : adfGeoTransform[5] = psFileInfo->fCellIncrementY;
1515 : }
1516 :
1517 : /* -------------------------------------------------------------------- */
1518 : /* do we have projection and datum? */
1519 : /* -------------------------------------------------------------------- */
1520 : CPLDebug( "ECW", "projection=%s, datum=%s",
1521 21 : psFileInfo->szProjection, psFileInfo->szDatum );
1522 :
1523 21 : if( EQUAL(psFileInfo->szProjection,"RAW") )
1524 21 : return;
1525 :
1526 : /* -------------------------------------------------------------------- */
1527 : /* Set projection if we have it. */
1528 : /* -------------------------------------------------------------------- */
1529 0 : OGRSpatialReference oSRS;
1530 0 : CPLString osUnits = "METERS";
1531 :
1532 0 : if( psFileInfo->eCellSizeUnits == ECW_CELL_UNITS_FEET )
1533 0 : osUnits = "FEET";
1534 :
1535 0 : if( oSRS.importFromERM( psFileInfo->szProjection,
1536 : psFileInfo->szDatum,
1537 : osUnits ) != OGRERR_NONE )
1538 : return;
1539 :
1540 0 : oSRS.exportToWkt( &pszProjection );
1541 : }
1542 :
1543 : #endif /* def FRMT_ecw */
1544 :
1545 : /************************************************************************/
1546 : /* ECWInitialize() */
1547 : /* */
1548 : /* Initialize NCS library. We try to defer this as late as */
1549 : /* possible since de-initializing it seems to be expensive/slow */
1550 : /* on some system. */
1551 : /************************************************************************/
1552 :
1553 79 : void ECWInitialize()
1554 :
1555 : {
1556 79 : CPLMutexHolder oHolder( &hECWDatasetMutex );
1557 :
1558 79 : if( bNCSInitialized )
1559 : return;
1560 :
1561 1 : NCSecwInit();
1562 1 : bNCSInitialized = TRUE;
1563 :
1564 : const char *pszEcwCacheSize =
1565 1 : CPLGetConfigOption("GDAL_ECW_CACHE_MAXMEM",NULL);
1566 :
1567 1 : if( pszEcwCacheSize != NULL )
1568 0 : NCSecwSetConfig(NCSCFG_CACHE_MAXMEM, atoi(pszEcwCacheSize) );
1569 : }
1570 :
1571 : /************************************************************************/
1572 : /* GDALDeregister_ECW() */
1573 : /************************************************************************/
1574 :
1575 325 : void GDALDeregister_ECW( GDALDriver * )
1576 :
1577 : {
1578 : /* For unknown reason, this cleanup can take up to 3 seconds (see #3134). */
1579 : /* Not worth it */
1580 : #ifdef notdef
1581 : if( bNCSInitialized )
1582 : {
1583 : bNCSInitialized = FALSE;
1584 : NCSecwShutdown();
1585 : }
1586 :
1587 : if( hECWDatasetMutex != NULL )
1588 : {
1589 : CPLDestroyMutex( hECWDatasetMutex );
1590 : hECWDatasetMutex = NULL;
1591 : }
1592 : #endif
1593 325 : }
1594 :
1595 : /************************************************************************/
1596 : /* GDALRegister_ECW() */
1597 : /************************************************************************/
1598 :
1599 338 : void GDALRegister_ECW()
1600 :
1601 : {
1602 : #ifdef FRMT_ecw
1603 : GDALDriver *poDriver;
1604 :
1605 338 : if (! GDAL_CHECK_VERSION("ECW driver"))
1606 0 : return;
1607 :
1608 338 : if( GDALGetDriverByName( "ECW" ) == NULL )
1609 : {
1610 336 : poDriver = new GDALDriver();
1611 :
1612 336 : poDriver->SetDescription( "ECW" );
1613 : poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
1614 336 : "ERMapper Compressed Wavelets" );
1615 : poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
1616 336 : "frmt_ecw.html" );
1617 336 : poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "ecw" );
1618 :
1619 336 : poDriver->pfnIdentify = ECWDataset::IdentifyECW;
1620 336 : poDriver->pfnOpen = ECWDataset::OpenECW;
1621 336 : poDriver->pfnUnloadDriver = GDALDeregister_ECW;
1622 : #ifdef HAVE_COMPRESS
1623 : // The create method seems not to work properly.
1624 : // poDriver->pfnCreate = ECWCreateECW;
1625 336 : poDriver->pfnCreateCopy = ECWCreateCopyECW;
1626 : poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES,
1627 336 : "Byte" );
1628 : poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST,
1629 : "<CreationOptionList>"
1630 : " <Option name='TARGET' type='float' description='Compression Percentage' />"
1631 : " <Option name='PROJ' type='string' description='ERMapper Projection Name'/>"
1632 : " <Option name='DATUM' type='string' description='ERMapper Datum Name' />"
1633 : " <Option name='LARGE_OK' type='boolean' description='Enable compressing 500+MB files'/>"
1634 336 : "</CreationOptionList>" );
1635 : #endif
1636 :
1637 336 : GetGDALDriverManager()->RegisterDriver( poDriver );
1638 : }
1639 : #endif /* def FRMT_ecw */
1640 : }
1641 :
1642 : /************************************************************************/
1643 : /* GDALRegister_ECW_JP2ECW() */
1644 : /* */
1645 : /* This function exists so that when built as a plugin, there */
1646 : /* is a function that will register both drivers. */
1647 : /************************************************************************/
1648 :
1649 0 : void GDALRegister_ECW_JP2ECW()
1650 :
1651 : {
1652 0 : GDALRegister_ECW();
1653 0 : GDALRegister_JP2ECW();
1654 0 : }
1655 :
1656 : /************************************************************************/
1657 : /* ECWDatasetOpenJPEG2000() */
1658 : /************************************************************************/
1659 14 : GDALDataset* ECWDatasetOpenJPEG2000(GDALOpenInfo* poOpenInfo)
1660 : {
1661 14 : return ECWDataset::OpenJPEG2000(poOpenInfo);
1662 : }
1663 :
1664 : /************************************************************************/
1665 : /* GDALRegister_JP2ECW() */
1666 : /************************************************************************/
1667 338 : void GDALRegister_JP2ECW()
1668 :
1669 : {
1670 : #ifdef FRMT_ecw
1671 : GDALDriver *poDriver;
1672 :
1673 338 : if (! GDAL_CHECK_VERSION("JP2ECW driver"))
1674 0 : return;
1675 :
1676 338 : if( GDALGetDriverByName( "JP2ECW" ) == NULL )
1677 : {
1678 336 : poDriver = new GDALDriver();
1679 :
1680 336 : poDriver->SetDescription( "JP2ECW" );
1681 : poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
1682 336 : "ERMapper JPEG2000" );
1683 : poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
1684 336 : "frmt_jp2ecw.html" );
1685 336 : poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "jp2" );
1686 :
1687 336 : poDriver->pfnIdentify = ECWDataset::IdentifyJPEG2000;
1688 336 : poDriver->pfnOpen = ECWDataset::OpenJPEG2000;
1689 : #ifdef HAVE_COMPRESS
1690 336 : poDriver->pfnCreate = ECWCreateJPEG2000;
1691 336 : poDriver->pfnCreateCopy = ECWCreateCopyJPEG2000;
1692 : poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES,
1693 336 : "Byte UInt16 Int16 UInt32 Int32 Float32 Float64" );
1694 : poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST,
1695 : "<CreationOptionList>"
1696 : " <Option name='TARGET' type='float' description='Compression Percentage' />"
1697 : " <Option name='PROJ' type='string' description='ERMapper Projection Name'/>"
1698 : " <Option name='DATUM' type='string' description='ERMapper Datum Name' />"
1699 : " <Option name='LARGE_OK' type='boolean' description='Enable compressing 500+MB files'/>"
1700 : " <Option name='GeoJP2' type='boolean' description='defaults to ON'/>"
1701 : " <Option name='GMLJP2' type='boolean' description='defaults to ON'/>"
1702 : " <Option name='PROFILE' type='string-select'>"
1703 : " <Value>BASELINE_0</Value>"
1704 : " <Value>BASELINE_1</Value>"
1705 : " <Value>BASELINE_2</Value>"
1706 : " <Value>NPJE</Value>"
1707 : " <Value>EPJE</Value>"
1708 : " </Option>"
1709 : " <Option name='PROGRESSION' type='string-select'>"
1710 : " <Value>LRCP</Value>"
1711 : " <Value>RLCP</Value>"
1712 : " <Value>RPCL</Value>"
1713 : " </Option>"
1714 : " <Option name='CODESTREAM_ONLY' type='boolean' description='No JP2 wrapper'/>"
1715 : " <Option name='LEVELS' type='int'/>"
1716 : " <Option name='LAYERS' type='int'/>"
1717 : " <Option name='PRECINCT_WIDTH' type='int'/>"
1718 : " <Option name='PRECINCT_HEIGHT' type='int'/>"
1719 : " <Option name='TILE_WIDTH' type='int'/>"
1720 : " <Option name='TILE_HEIGHT' type='int'/>"
1721 : " <Option name='INCLUDE_SOP' type='boolean'/>"
1722 : " <Option name='INCLUDE_EPH' type='boolean'/>"
1723 : " <Option name='DECOMPRESS_LAYERS' type='int'/>"
1724 : " <Option name='DECOMPRESS_RECONSTRUCTION_PARAMETER' type='float'/>"
1725 336 : "</CreationOptionList>" );
1726 : #endif
1727 :
1728 336 : GetGDALDriverManager()->RegisterDriver( poDriver );
1729 : }
1730 : #endif /* def FRMT_ecw */
1731 1140 : }
1732 :
1733 :
1734 :
1735 :
|