/****************************************************************************** * Project: GDAL * Author: Raul Alonso Reyes * Author: Even Rouault, * Purpose: JPEG-2000 driver based on Lurawave library, driver developed by SatCen * ****************************************************************************** * Copyright (c) 2016, SatCen - European Union Satellite Centre * Copyright (c) 2016, Even Rouault * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. ****************************************************************************/ #include "jp2lurarasterband.h" #include "jp2luradataset.h" /************************************************************************/ /* JP2LuraRasterBand() */ /************************************************************************/ JP2LuraRasterBand::JP2LuraRasterBand(JP2LuraDataset *poDSIn, int nBandIn, GDALDataType eDataTypeIn, int nBits, int nBlockXSizeIn, int nBlockYSizeIn) { eDataType = eDataTypeIn; nBlockXSize = nBlockXSizeIn; nBlockYSize = nBlockYSizeIn; poDS = poDSIn; nRasterXSize = poDSIn->nRasterXSize; nRasterYSize = poDSIn->nRasterYSize; nBand = nBandIn; if (nRasterXSize == nBlockXSize && nRasterYSize == nBlockYSize) { /* -------------------------------------------------------------------- */ /* Use a 2048x128 "virtual" block size unless the file is small. */ /* -------------------------------------------------------------------- */ if (nRasterXSize >= 2048) { nBlockXSize = 2048; } else { nBlockXSize = nRasterXSize; } if (nRasterYSize >= 128) { nBlockYSize = 128; } else { nBlockYSize = nRasterYSize; } } if( (nBits % 8) != 0 ) { GDALRasterBand::SetMetadataItem("NBITS", CPLString().Printf("%d",nBits), "IMAGE_STRUCTURE" ); } GDALRasterBand::SetMetadataItem("COMPRESSION", "JPEG2000", "IMAGE_STRUCTURE" ); bForceCachedIO = FALSE; } /************************************************************************/ /* ~JP2OpenJPEGRasterBand() */ /************************************************************************/ JP2LuraRasterBand::~JP2LuraRasterBand() { } /************************************************************************/ /* IReadBlock() */ /************************************************************************/ CPLErr JP2LuraRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void * pImage) { JP2LuraDataset *poGDS = reinterpret_cast(poDS); #ifdef DEBUG_VERBOSE CPLDebug("JP2Lura", "IReadBlock(nBand=%d,nLevel=%d %d,%d)", nBand, poGDS->iLevel, nBlockXOff, nBlockYOff); #endif int nXOff = nBlockXOff * nBlockXSize; int nYOff = nBlockYOff * nBlockYSize; int nXSize = nBlockXSize; int nYSize = nBlockYSize; if( nXOff + nXSize > nRasterXSize ) nXSize = nRasterXSize - nXOff; if( nYOff + nYSize > nRasterYSize ) nYSize = nRasterYSize - nYOff; GDALRasterIOExtraArg sExtraArgs; INIT_RASTERIO_EXTRA_ARG(sExtraArgs); const int nDTSizeBytes = GDALGetDataTypeSizeBytes(eDataType); CPLErr eErr = IRasterIO(GF_Read, nXOff, nYOff, nXSize, nYSize, pImage, nXSize, nYSize, eDataType, nDTSizeBytes, nDTSizeBytes * nXSize, &sExtraArgs); // Unpack previously packed buffer if needed if( eErr == CE_None && nXSize < nBlockXSize ) { GByte* pabyData = reinterpret_cast(pImage); for( int j = nYSize - 1; j >= 0; --j ) { memmove( pabyData + j * nBlockXSize * nDTSizeBytes, pabyData + j * nXSize * nDTSizeBytes, nXSize * nDTSizeBytes ); } } // Caching other bands while we have the cached buffer valid for( int iBand = 1;eErr == CE_None && iBand <= poGDS->nBands; iBand++ ) { if( iBand == nBand ) continue; JP2LuraRasterBand* poOtherBand = reinterpret_cast(poGDS->GetRasterBand(iBand)); GDALRasterBlock* poBlock = poOtherBand-> TryGetLockedBlockRef(nBlockXOff,nBlockYOff); if (poBlock != nullptr) { poBlock->DropLock(); continue; } poBlock = poOtherBand-> GetLockedBlockRef(nBlockXOff,nBlockYOff, TRUE); if (poBlock == nullptr) { continue; } GByte* pabyData = reinterpret_cast(poBlock->GetDataRef()); eErr = poOtherBand->IRasterIO(GF_Read, nXOff, nYOff, nXSize, nYSize, pabyData, nXSize, nYSize, eDataType, nDTSizeBytes, nDTSizeBytes * nXSize, &sExtraArgs); // Unpack previously packed buffer if needed if( eErr == CE_None && nXSize < nBlockXSize ) { for( int j = nYSize - 1; j >= 0; --j ) { memmove( pabyData + j * nBlockXSize * nDTSizeBytes, pabyData + j * nXSize * nDTSizeBytes, nXSize * nDTSizeBytes ); } } poBlock->DropLock(); } return eErr; } /************************************************************************/ /* IRasterIO() */ /************************************************************************/ CPLErr JP2LuraRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize, void * pData, int nBufXSize, int nBufYSize, GDALDataType eBufType, GSpacing nPixelSpace, GSpacing nLineSpace, GDALRasterIOExtraArg* psExtraArg) { JP2LuraDataset *poGDS = reinterpret_cast(poDS); const int nBufTypeSize = GDALGetDataTypeSizeBytes(eBufType); if (eRWFlag != GF_Read) return CE_Failure; #ifdef DEBUG_VERBOSE CPLDebug("JP2Lura", "RasterIO(nBand=%d,nLevel=%d %d,%d,%dx%d -> %dx%d)", nBand, poGDS->iLevel, nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize); #endif if( eBufType != eDataType || nPixelSpace != nBufTypeSize || nLineSpace != nPixelSpace * nBufXSize ) { return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize, eBufType, nPixelSpace, nLineSpace, psExtraArg); } // Use cached data if( poGDS->sOutputData.nXOff == nXOff && poGDS->sOutputData.nYOff == nYOff && poGDS->sOutputData.nXSize == nXSize && poGDS->sOutputData.nYSize == nYSize && poGDS->sOutputData.nBufXSize == nBufXSize && poGDS->sOutputData.nBufYSize == nBufYSize && poGDS->sOutputData.eBufType == eBufType ) { if (poGDS->sOutputData.pDatacache[nBand - 1] != nullptr) { #ifdef DEBUG_VERBOSE CPLDebug("JP2Lura", "Using cached data"); #endif memcpy(pData, poGDS->sOutputData.pDatacache[nBand - 1], static_cast(nBufXSize)*nBufYSize*nBufTypeSize); return CE_None; } } /* ==================================================================== */ /* Do we have overviews that would be appropriate to satisfy */ /* this request? */ /* ==================================================================== */ if ((nBufXSize < nXSize || nBufYSize < nYSize) && GetOverviewCount() > 0 && eRWFlag == GF_Read) { int nOverview; GDALRasterIOExtraArg sExtraArg; GDALCopyRasterIOExtraArg(&sExtraArg, psExtraArg); nOverview = GDALBandGetBestOverviewLevel2( this, nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize, &sExtraArg); if (nOverview >= 0) { GDALRasterBand* poOverviewBand = GetOverview(nOverview); if (poOverviewBand == nullptr) return CE_Failure; return poOverviewBand->RasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize, eBufType, nPixelSpace, nLineSpace, &sExtraArg); } } if( nBufXSize != nXSize || nBufYSize != nYSize ) { return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize, eBufType, nPixelSpace, nLineSpace, psExtraArg); } JP2_Error error = 0; const short factor = 1 << poGDS->iLevel; JP2_Rect comp_region; comp_region.ulLeft = nXOff; comp_region.ulRight = nXOff + nXSize; comp_region.ulTop = nYOff; comp_region.ulBottom = nYOff + nYSize; error = JP2_Decompress_SetProp(poGDS->sOutputData.handle, cJP2_Prop_Scale_Down, factor); if( error ) { CPLError(CE_Failure, CPLE_AppDefined, "Internal library error (%s).", JP2LuraDataset::GetErrorMessage(error)); return CE_Failure; } poGDS->sOutputData.pimage = reinterpret_cast(pData); poGDS->sOutputData.nXOff = nXOff; poGDS->sOutputData.nYOff = nYOff; poGDS->sOutputData.nXSize = nXSize; poGDS->sOutputData.nYSize = nYSize; poGDS->sOutputData.nBufXSize = nBufXSize; poGDS->sOutputData.nBufYSize = nBufYSize; poGDS->sOutputData.eBufType = eBufType; poGDS->sOutputData.nBand = nBand; poGDS->sOutputData.nBands = poGDS->nBands; if( poGDS->sOutputData.pDatacache == nullptr ) { poGDS->sOutputData.pDatacache = reinterpret_cast (CPLCalloc( poGDS->nBands, sizeof(unsigned char*))); } for (int i = 0; i < poGDS->nBands; i++) { if (poGDS->sOutputData.pDatacache[i] != nullptr) { VSIFree(poGDS->sOutputData.pDatacache[i]); poGDS->sOutputData.pDatacache[i] = nullptr; } if (i == nBand-1) continue; poGDS->sOutputData.pDatacache[i] = reinterpret_cast(VSIMalloc( static_cast(nBufXSize)*nBufYSize*nBufTypeSize)); } /*++++++++++++++++++++++++++++++++++++++++++++++*/ /* Set the callback function and parameter */ /*++++++++++++++++++++++++++++++++++++++++++++++*/ error = JP2_Decompress_SetProp(poGDS->sOutputData.handle, cJP2_Prop_Output_Parameter, reinterpret_cast(&(poGDS->sOutputData))); if( error ) { CPLError(CE_Failure, CPLE_AppDefined, "Internal library error (%s).", JP2LuraDataset::GetErrorMessage(error)); return CE_Failure; } error = JP2_Decompress_SetProp(poGDS->sOutputData.handle, cJP2_Prop_Output_Function, reinterpret_cast( GDALJP2Lura_Callback_Decompress_Write)); if( error ) { CPLError(CE_Failure, CPLE_AppDefined, "Internal library error (%s).", JP2LuraDataset::GetErrorMessage(error)); return CE_Failure; } error = JP2_Decompress_Region(poGDS->sOutputData.handle, comp_region); if( error ) { CPLError(CE_Failure, CPLE_AppDefined, "Internal library error during decompress region (%s).", JP2LuraDataset::GetErrorMessage(error)); return CE_Failure; } return CE_None; } /************************************************************************/ /* GetOverviewCount() */ /************************************************************************/ int JP2LuraRasterBand::GetOverviewCount() { JP2LuraDataset *poGDS = reinterpret_cast(poDS); return poGDS->nOverviewCount; } /************************************************************************/ /* GetOverview() */ /************************************************************************/ GDALRasterBand* JP2LuraRasterBand::GetOverview(int iOvrLevel) { JP2LuraDataset *poGDS = reinterpret_cast(poDS); if (iOvrLevel < 0 || iOvrLevel >= poGDS->nOverviewCount) return nullptr; return poGDS->papoOverviewDS[iOvrLevel]->GetRasterBand(nBand); } /************************************************************************/ /* GetColorInterpretation() */ /************************************************************************/ GDALColorInterp JP2LuraRasterBand::GetColorInterpretation() { JP2LuraDataset *poGDS = reinterpret_cast(poDS); if( poGDS->poCT ) return GCI_PaletteIndex; if( nBand == poGDS->nAlphaIndex + 1 ) return GCI_AlphaBand; if (poGDS->nBands <= 2 && poGDS->eColorspace == cJP2_Colorspace_Gray) return GCI_GrayIndex; else if (poGDS->eColorspace == cJP2_Colorspace_RGBa) { if( nBand == poGDS->nRedIndex + 1 ) return GCI_RedBand; if( nBand == poGDS->nGreenIndex + 1 ) return GCI_GreenBand; if( nBand == poGDS->nBlueIndex + 1 ) return GCI_BlueBand; } return GCI_Undefined; } /************************************************************************/ /* GetColorTable() */ /************************************************************************/ GDALColorTable* JP2LuraRasterBand::GetColorTable() { JP2LuraDataset *poGDS = reinterpret_cast(poDS); return poGDS->poCT; }