GDALOpen 代码分析

来源:互联网 发布:矩阵行列式计算公式 编辑:程序博客网 时间:2024/05/18 22:46

先来一句话,看了这么多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整个加载过程分析透,需要更多的篇幅。我以后会不断的修改已经有的文章,使得其更准确,并增加新的文章,描述更多的细节。

欢迎讨论。