Index: src/core/raster/qgsrasterlayer.cpp =================================================================== --- src/core/raster/qgsrasterlayer.cpp (revision 12507) +++ src/core/raster/qgsrasterlayer.cpp (working copy) @@ -22,6 +22,7 @@ #include "qgsmaptopixel.h" #include "qgsproviderregistry.h" #include "qgsrasterbandstats.h" +#include "qgsrasterimagebuffer.h" #include "qgsrasterlayer.h" #include "qgsrasterpyramid.h" #include "qgsrectangle.h" @@ -2917,7 +2918,7 @@ // Only do this for the non-provider (hard-coded GDAL) scenario... // Maybe WMS can do this differently using QImage::numColors and QImage::color() - if ( mProviderKey.isEmpty() && hasBand( "Palette" ) && theBandNumber > 0 ) //dont tr() this its a gdal word! + if ( mProviderKey.isEmpty() && hasBand( "Palette" ) && theBandNumber > 0 ) //don't tr() this its a gdal word! { QgsDebugMsg( "....found paletted image" ); QgsColorRampShader myShader; @@ -2977,7 +2978,7 @@ GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, theBandNo ); QgsRasterBandStats myRasterBandStats = bandStatistics( theBandNo ); //calculate the histogram for this band - //we assume that it only needs to be calculated if the lenght of the histogram + //we assume that it only needs to be calculated if the length of the histogram //vector is not equal to the number of bins //i.e if the histogram has never previously been generated or the user has //selected a new number of bins. @@ -3786,7 +3787,7 @@ { QgsColorRampShader* myColorRampShader = ( QgsColorRampShader* ) mRasterShader->rasterShaderFunction(); - //TODO: Remove the customColorRampType check and following if() in v2.0, added for compatability with older ( bugged ) project files + //TODO: Remove the customColorRampType check and following if() in v2.0, added for compatibility with older ( bugged ) project files QDomNode customColorRampTypeNode = customColorRampNode.namedItem( "customColorRampType" ); QDomNode colorRampTypeNode = customColorRampNode.namedItem( "colorRampType" ); QString myRampType = ""; @@ -4505,6 +4506,61 @@ GDALRasterBandH myGdalBand = GDALGetRasterBand( mGdalDataset, theBandNo ); GDALDataType myDataType = GDALGetRasterDataType( myGdalBand ); + QgsRasterImageBuffer imageBuffer( myGdalBand, theQPainter, theRasterViewPort, theQgsMapToPixel, &mGeoTransform[0] ); + imageBuffer.reset(); + + QRgb* imageScanLine = 0; + void* rasterScanLine = 0; + + QRgb myDefaultColor = qRgba( 255, 255, 255, 0 ); + double myPixelValue = 0.0; + int myRedValue = 0; + int myGreenValue = 0; + int myBlueValue = 0; + int myAlphaValue = 0; + + while( imageBuffer.nextScanLine( &imageScanLine, &rasterScanLine ) ) + { + for( int i = 0; i < theRasterViewPort->drawableAreaXDim; ++i ) + { + myRedValue = 0; myGreenValue = 0; myBlueValue = 0; + myPixelValue = readValue( rasterScanLine, ( GDALDataType )myDataType, i); + + if ( mValidNoDataValue && ( fabs( myPixelValue - mNoDataValue ) <= TINY_VALUE || myPixelValue != myPixelValue ) ) + { + imageScanLine[ i ] = myDefaultColor; + continue; + } + + myAlphaValue = mRasterTransparency.alphaValue( myPixelValue, mTransparencyLevel ); + if ( 0 == myAlphaValue ) + { + imageScanLine[ i ] = myDefaultColor; + continue; + } + + if ( !mRasterShader->shade( myPixelValue, &myRedValue, &myGreenValue, &myBlueValue ) ) + { + imageScanLine[ i ] = myDefaultColor; + continue; + } + + if ( mInvertColor ) + { + //Invert flag, flip blue and read + imageScanLine[ i ] = qRgba( myBlueValue, myGreenValue, myRedValue, myAlphaValue ); + } + else + { + //Normal + imageScanLine[ i ] = qRgba( myRedValue, myGreenValue, myBlueValue, myAlphaValue ); + } + } + } + +#if 0 + + void *myGdalScanData = readData( myGdalBand, theRasterViewPort ); /* Check for out of memory error */ @@ -4568,6 +4624,7 @@ CPLFree( myGdalScanData ); paintImageToCanvas( theQPainter, theRasterViewPort, theQgsMapToPixel, &myQImage ); +#endif //0 } /** @@ -5314,7 +5371,7 @@ mRasterType = Multiband; } //TODO hasBand is really obsolete and only used in the Palette instance, change to new function hasPalette(int) - else if ( hasBand( "Palette" ) ) //dont tr() this its a gdal word! + else if ( hasBand( "Palette" ) ) //don't tr() this its a gdal word! { mRasterType = Palette; } Index: src/core/raster/qgsrasterimagebuffer.cpp =================================================================== --- src/core/raster/qgsrasterimagebuffer.cpp (revision 0) +++ src/core/raster/qgsrasterimagebuffer.cpp (revision 0) @@ -0,0 +1,163 @@ +/*************************************************************************** + qgsrasterimagebuffer.cpp + ------------------------ + begin : December 2009 + copyright : (C) 2009 by Marco Hugentobler + email : marco at hugis dot net + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsrasterimagebuffer.h" +#include "qgsmaptopixel.h" +#include "qgsrasterviewport.h" +#include +#include +#include +#include "cpl_conv.h" + +#ifndef Q_OS_MACX +#include +#else +#include +#endif + + +QgsRasterImageBuffer::QgsRasterImageBuffer(GDALRasterBandH rasterBand, QPainter* p, QgsRasterViewPort* viewPort, const QgsMapToPixel* mapToPixel, double* geoTransform): \ +mRasterBand(rasterBand), mPainter(p), mViewPort(viewPort), mMapToPixel(mapToPixel), mValid(false), mCurrentImage(0), mCurrentGDALData(0), mGeoTransform(geoTransform) +{ + +} + +QgsRasterImageBuffer::~QgsRasterImageBuffer() +{ + delete mCurrentImage; + CPLFree(mCurrentGDALData); +} + +void QgsRasterImageBuffer::reset() +{ + if( mRasterBand && mPainter && mViewPort && mMapToPixel ) + { + mValid = true; + } + else + { + mValid = false; + return; + } + + //decide on the partition of the image + + int pixels = mViewPort->drawableAreaXDim * mViewPort->drawableAreaYDim; + int mNumPartImages = std::max(pixels / 50000000.0, 1.0); + mNumRasterRowsPerPart = (double)mViewPort->clippedHeight / (double)mNumPartImages + 0.5; + + mCurrentPartRasterMin = -1; + mCurrentPartRasterMax = -1; + mCurrentPartImageRow = 0; + mNumCurrentImageRows = 0; + + createNextPartImage(); +} + +bool QgsRasterImageBuffer::nextScanLine(QRgb** imageScanLine, void** rasterScanLine) +{ + if(!mValid) + { + return false; + } + + if( !mCurrentGDALData || ! mCurrentImage ) + { + return false; + } + + if(mCurrentPartImageRow >= (mNumCurrentImageRows)) + { + if(!createNextPartImage()) + { + return false; + } + } + + *imageScanLine = ( QRgb* )(mCurrentImage->scanLine(mCurrentPartImageRow)); + GDALDataType type = GDALGetRasterDataType( mRasterBand ); + int size = GDALGetDataTypeSize( type ) / 8; + *rasterScanLine = mCurrentGDALData + mCurrentPartImageRow * mViewPort->drawableAreaXDim * size; + + ++mCurrentPartImageRow; + ++mCurrentRow; + return true; +} + +bool QgsRasterImageBuffer::createNextPartImage() +{ + //draw the last image if mCurrentImage if it exists + if(mCurrentImage) + { + double xLeft = mViewPort->topLeftPoint.x(); + double yTop = mViewPort->topLeftPoint.y() + fabs(mGeoTransform[5]) * mCurrentPartRasterMin / mMapToPixel->mapUnitsPerPixel(); + mPainter->drawImage(QPointF(xLeft, yTop), *mCurrentImage); + } + + delete mCurrentImage; mCurrentImage = 0; + CPLFree(mCurrentGDALData); mCurrentGDALData = 0; + + if(mCurrentPartRasterMax >= mViewPort->clippedHeight) + { + return false; //already at the end... + } + + mCurrentPartRasterMin = mCurrentPartRasterMax + 1; + mCurrentPartRasterMax = mCurrentPartRasterMin + mNumRasterRowsPerPart; + if(mCurrentPartRasterMax > mViewPort->clippedHeight) + { + mCurrentPartRasterMax = mViewPort->clippedHeight; + } + mCurrentRow = mCurrentPartRasterMin; + mCurrentPartImageRow = 0; + + //read GDAL image data + GDALDataType type = GDALGetRasterDataType( mRasterBand ); + int size = GDALGetDataTypeSize( type ) / 8; + int xSize = mViewPort->drawableAreaXDim; + + //make the raster tiles overlap at least 2 pixels to avoid white stripes + int overlapRows = 0; + overlapRows = mMapToPixel->mapUnitsPerPixel() / fabs(mGeoTransform[5]) + 2; + if(mCurrentPartRasterMax + overlapRows >= mViewPort->clippedHeight) + { + overlapRows = 0; + } + int rasterYSize = mCurrentPartRasterMax - mCurrentPartRasterMin + overlapRows; + + int ySize = fabs(( (rasterYSize) / mMapToPixel->mapUnitsPerPixel() * mGeoTransform[5] ) ) + 0.5; + if(ySize == 0) + { + return false; + } + mNumCurrentImageRows = ySize; + mCurrentGDALData = VSIMalloc(size * xSize * ySize); + CPLErr myErr = GDALRasterIO( mRasterBand, GF_Read, mViewPort->rectXOffset, \ + mViewPort->rectYOffset + mCurrentRow, mViewPort->clippedWidth, rasterYSize, \ + mCurrentGDALData, xSize, ySize, type, 0, 0); + + if ( myErr != CPLE_None ) + { + CPLFree(mCurrentGDALData); + mCurrentGDALData = 0; + return false; + } + + //create the QImage + mCurrentImage = new QImage( xSize, ySize, QImage::Format_ARGB32 ); + mCurrentImage->fill(qRgba(255, 255, 255, 0)); + return true; +} + Index: src/core/raster/qgsrasterimagebuffer.h =================================================================== --- src/core/raster/qgsrasterimagebuffer.h (revision 0) +++ src/core/raster/qgsrasterimagebuffer.h (revision 0) @@ -0,0 +1,65 @@ +/*************************************************************************** + qgsrasterimagebuffer.h + --------------------- + begin : December 2009 + copyright : (C) 2009 by Marco Hugentobler + email : marco at hugis dot net + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSRASTERIMAGEBUFFER_H +#define QGSRASTERIMAGEBUFFER_H + +#include + +typedef void* GDALRasterBandH; +class QgsMapToPixel; +struct QgsRasterViewPort; +class QImage; +class QPainter; + +/**A class encapsulates reading from a raster band and drawing the pixels to a painter. + The class allows sequential reading of the scan lines and setting the image scan line pixels. It automatically decides + on how much of the band / image should stay in virtual memory at a time*/ +class QgsRasterImageBuffer +{ + public: + QgsRasterImageBuffer(GDALRasterBandH rasterBand, QPainter* p, \ + QgsRasterViewPort* viewPort, const QgsMapToPixel* mapToPixel, double* mGeoTransform); + ~QgsRasterImageBuffer(); + void reset(); + /**Returns a pointer to the next scan line (or 0 if end)*/ + bool nextScanLine(QRgb** imageScanLine, void** rasterScanLine); + + private: + QgsRasterImageBuffer(); //forbidden + /**Creates next part image. Returns false if at end*/ + bool createNextPartImage(); + + GDALRasterBandH mRasterBand; //raster band + QPainter* mPainter; + QgsRasterViewPort* mViewPort; + const QgsMapToPixel* mMapToPixel; + double* mGeoTransform; + + bool mValid; + int mCurrentRow; + int mNumPartImages; //number of part images + int mNumRasterRowsPerPart; //number of (raster source) rows per part + int mCurrentPartRasterMin; //minimum (raster source) row of current image + int mCurrentPartRasterMax; //maximum (raster source) row of current image + int mCurrentPartImageRow; //current image row + int mNumCurrentImageRows; //number of image rows for the current part + + //current memory image and gdal scan data + QImage* mCurrentImage; + void* mCurrentGDALData; +}; + +#endif // QGSRASTERIMAGEBUFFER_H Index: src/core/CMakeLists.txt =================================================================== --- src/core/CMakeLists.txt (revision 12507) +++ src/core/CMakeLists.txt (working copy) @@ -110,6 +110,7 @@ raster/qgslinearminmaxenhancement.cpp raster/qgslinearminmaxenhancementwithclip.cpp raster/qgspseudocolorshader.cpp + raster/qgsrasterimagebuffer.cpp raster/qgsrasterlayer.cpp raster/qgsrastertransparency.cpp raster/qgsrastershader.cpp