先来一句话,看了这么多GDAL的源代码,并不喜欢其C风格的烙印太重,还是更喜欢boost风格的简洁的现代C++风格。不过为了更好地应用GDAL,更深的定制它,还是需要将源代码看到底。因为GDAL毕竟是一个很好的图像处理的解决方案。复用它,可以省掉很多人年的工作。
GDALOpen函数代码:注释值得一看
/************************************************************************/ /* GDALOpen() */ /************************************************************************/ /** * \brief Open a raster file as a GDALDataset. * * This function will try to open the passed file, or virtual dataset * name by invoking the Open method of each registered GDALDriver in turn. * The first successful open will result in a returned dataset. If all * drivers fail then NULL is returned and an error is issued. * * Several recommandations : * <ul> * <li>If you open a dataset object with GA_Update access, it is not recommanded * to open a new dataset on the same underlying file.</li> * <li>The returned dataset should only be accessed by one thread at a time. If you * want to use it from different threads, you must add all necessary code (mutexes, etc.) * to avoid concurrent use of the object. (Some drivers, such as GeoTIFF, maintain internal * state variables that are updated each time a new block is read, thus preventing concurrent * use.) </li> * </ul> * * \sa GDALOpenShared() * * @param pszFilename the name of the file to access. In the case of * exotic drivers this may not refer to a physical file, but instead contain * information for the driver on how to access a dataset. It should be in UTF8 * encoding. * * @param eAccess the desired access, either GA_Update or GA_ReadOnly. Many * drivers support only read only access. * * @return A GDALDatasetH handle or NULL on failure. For C++ applications * this handle can be cast to a GDALDataset *. */ GDALDatasetH CPL_STDCALL GDALOpen( const char * pszFilename, GDALAccess eAccess ) { return GDALOpenInternal(pszFilename, eAccess, NULL); }
注释提示了几个地方:
1. 会依次调用每个已经注册的driver的open函数,第一个成功的会返回Dataset。这个依次应该是按照注册顺序,先注册的driver先被调用。
2. Dataset对象不是线程安全的,使用者自己注意维护多线程环境下的安全性。
3. 返回NULL代表打开失败
GDALDatasetH实际上是个void* , 又是C的玩法。逃过了编译器类型检查。
/** Opaque type used for the C bindings of the C++ GDALDataset class */ typedef void *GDALDatasetH;
实际上就是CDALDataset* 指针。
真正实现代码在下面的函数里面:
/* The drivers listed in papszAllowedDrivers can be in any order */ /* Only the order of registration will be taken into account */ GDALDatasetH GDALOpenInternal( const char * pszFilename, GDALAccess eAccess, const char* const * papszAllowedDrivers) { VALIDATE_POINTER1( pszFilename, "GDALOpen", NULL ); int iDriver; GDALDriverManager *poDM = GetGDALDriverManager(); GDALOpenInfo oOpenInfo( pszFilename, eAccess ); CPLLocaleC oLocaleForcer; CPLErrorReset(); CPLAssert( NULL != poDM ); for( iDriver = 0; iDriver < poDM->GetDriverCount(); iDriver++ ) { GDALDriver *poDriver = poDM->GetDriver( iDriver ); GDALDataset *poDS; if (papszAllowedDrivers != NULL && CSLFindString((char**)papszAllowedDrivers, GDALGetDriverShortName(poDriver)) == -1) continue; if ( poDriver->pfnOpen == NULL ) continue; poDS = poDriver->pfnOpen( &oOpenInfo ); if( poDS != NULL ) { if( strlen(poDS->GetDescription()) == 0 ) poDS->SetDescription( oOpenInfo.pszFilename ); if( poDS->poDriver == NULL ) poDS->poDriver = poDriver; if( CPLGetPID() != GDALGetResponsiblePIDForCurrentThread() ) CPLDebug( "GDAL", "GDALOpen(%s, this=%p) succeeds as %s (pid=%d, responsiblePID=%d).", pszFilename, poDS, poDriver->GetDescription(), (int)CPLGetPID(), (int)GDALGetResponsiblePIDForCurrentThread() ); else CPLDebug( "GDAL", "GDALOpen(%s, this=%p) succeeds as %s.", pszFilename, poDS, poDriver->GetDescription() ); return (GDALDatasetH) poDS; } if( CPLGetLastErrorNo() != 0 ) return NULL; } if( oOpenInfo.bStatOK ) CPLError( CE_Failure, CPLE_OpenFailed, "`%s' not recognised as a supported file format.\n", pszFilename ); else CPLError( CE_Failure, CPLE_OpenFailed, "`%s' does not exist in the file system,\n" "and is not recognised as a supported dataset name.\n", pszFilename ); return NULL; }
这段就和注释说的一样,遍历driver,依次尝试打开文件。在我的GeoTiff driver中,open方法会调用geotiff.cpp文件的Open方法:
/************************************************************************/ /* Open() */ /************************************************************************/ GDALDataset *GTiffDataset::Open( GDALOpenInfo * poOpenInfo ) { TIFF *hTIFF; int bAllowRGBAInterface = TRUE; const char *pszFilename = poOpenInfo->pszFilename; /* -------------------------------------------------------------------- */ /* Check if it looks like a TIFF file. */ /* -------------------------------------------------------------------- */ if (!Identify(poOpenInfo)) return NULL; if( EQUALN(pszFilename,"GTIFF_RAW:", strlen("GTIFF_RAW:")) ) { bAllowRGBAInterface = FALSE; pszFilename += strlen("GTIFF_RAW:"); } /* -------------------------------------------------------------------- */ /* We have a special hook for handling opening a specific */ /* directory of a TIFF file. */ /* -------------------------------------------------------------------- */ if( EQUALN(pszFilename,"GTIFF_DIR:",strlen("GTIFF_DIR:")) ) return OpenDir( poOpenInfo ); if (!GTiffOneTimeInit()) return NULL; /* -------------------------------------------------------------------- */ /* Try opening the dataset. */ /* -------------------------------------------------------------------- */ if( poOpenInfo->eAccess == GA_ReadOnly ) hTIFF = VSI_TIFFOpen( pszFilename, "r" ); else hTIFF = VSI_TIFFOpen( pszFilename, "r+" ); if( hTIFF == NULL ) return( NULL ); /* -------------------------------------------------------------------- */ /* Create a corresponding GDALDataset. */ /* -------------------------------------------------------------------- */ GTiffDataset *poDS; poDS = new GTiffDataset(); poDS->SetDescription( pszFilename ); poDS->osFilename = pszFilename; poDS->poActiveDS = poDS; if( poDS->OpenOffset( hTIFF, &(poDS->poActiveDS), TIFFCurrentDirOffset(hTIFF), TRUE, poOpenInfo->eAccess, bAllowRGBAInterface, TRUE, poOpenInfo->papszSiblingFiles) != CE_None ) { delete poDS; return NULL; } /* -------------------------------------------------------------------- */ /* Initialize any PAM information. */ /* -------------------------------------------------------------------- */ poDS->TryLoadXML(); poDS->ApplyPamInfo(); int i; for(i=1;i<=poDS->nBands;i++) { GTiffRasterBand* poBand = (GTiffRasterBand*) poDS->GetRasterBand(i); /* Load scale, offset and unittype from PAM if available */ if (!poBand->bHaveOffsetScale) { poBand->dfScale = poBand->GDALPamRasterBand::GetScale(&poBand->bHaveOffsetScale); poBand->dfOffset = poBand->GDALPamRasterBand::GetOffset(); } if (poBand->osUnitType.size() == 0) { const char* pszUnitType = poBand->GDALPamRasterBand::GetUnitType(); if (pszUnitType) poBand->osUnitType = pszUnitType; } } poDS->bMetadataChanged = FALSE; poDS->bGeoTIFFInfoChanged = FALSE; /* -------------------------------------------------------------------- */ /* Check for external overviews. */ /* -------------------------------------------------------------------- */ poDS->oOvManager.Initialize( poDS, pszFilename ); return poDS; }
这里主要看poDs->OpenOffset函数,它负责读取tiff文件的基本信息,以便后面快速读取。因为这么大的文件,显然不可能一次读进内存来。
/************************************************************************/ /* OpenOffset() */ /* */ /* Initialize the GTiffDataset based on a passed in file */ /* handle, and directory offset to utilize. This is called for */ /* full res, and overview pages. */ /************************************************************************/ CPLErr GTiffDataset::OpenOffset( TIFF *hTIFFIn, GTiffDataset **ppoActiveDSRef, toff_t nDirOffsetIn, int bBaseIn, GDALAccess eAccess, int bAllowRGBAInterface, int bReadGeoTransform, char** papszSiblingFiles )
该函数的定义在geotiff.cpp文件中,非常长,所以这里就不列代码了。
如果要完整的将GeoTiff整个加载过程分析透,需要更多的篇幅。我以后会不断的修改已经有的文章,使得其更准确,并增加新的文章,描述更多的细节。
欢迎讨论。