Fix calculation of blur ops with non ARGB32_Premultiplied images

This commit is contained in:
Nyall Dawson 2015-01-26 14:11:28 +11:00
parent 2e8df945b4
commit f7f89b37b9
9 changed files with 77 additions and 17 deletions

View File

@ -10,10 +10,6 @@
* Operations are written to either modify an image in place or return a new image, depending
* on which is faster for the particular operation.
*
* \note These operations do not work using premultiplied ARGB32_Premultiplied images
* - please make sure the images are converted to standard ARGB32 images prior to calling
* these operations.
*
* \note Added in version 2.7
*/
class QgsImageOperation
@ -115,6 +111,8 @@ class QgsImageOperation
* @param image QImage to blur
* @param radius blur radius in pixels, maximum value of 16
* @param alphaOnly set to true to blur only the alpha component of the image
* @note for fastest operation, ensure the source image is ARGB32_Premultiplied if
* alphaOnly is set to false, or ARGB32 if alphaOnly is true
*/
static void stackBlur( QImage &image, const int radius, const bool alphaOnly = false );
@ -123,6 +121,7 @@ class QgsImageOperation
* @param image QImage to blur
* @param radius blur radius in pixels
* @returns blurred image
* @note for fastest operation, ensure the source image is ARGB32_Premultiplied
*/
static QImage* gaussianBlur( QImage &image, const int radius ) /Factory/;

View File

@ -533,20 +533,38 @@ void QgsImageOperation::stackBlur( QImage &image, const int radius, const bool a
int i1 = 0;
int i2 = 3;
if ( alphaOnly ) // this seems to only work right for a black color
//ensure correct source format.
QImage::Format originalFormat = image.format();
QImage* pImage = ℑ
if ( !alphaOnly && originalFormat != QImage::Format_ARGB32_Premultiplied )
{
pImage = new QImage( image.convertToFormat( QImage::Format_ARGB32_Premultiplied ) );
}
else if ( alphaOnly && originalFormat != QImage::Format_ARGB32 )
{
pImage = new QImage( image.convertToFormat( QImage::Format_ARGB32 ) );
}
if ( alphaOnly )
i1 = i2 = ( QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3 );
StackBlurLineOperation topToBottomBlur( alpha, QgsImageOperation::ByColumn, true, i1, i2 );
runLineOperation( image, topToBottomBlur );
runLineOperation( *pImage, topToBottomBlur );
StackBlurLineOperation leftToRightBlur( alpha, QgsImageOperation::ByRow, true, i1, i2 );
runLineOperation( image, leftToRightBlur );
runLineOperation( *pImage, leftToRightBlur );
StackBlurLineOperation bottomToTopBlur( alpha, QgsImageOperation::ByColumn, false, i1, i2 );
runLineOperation( image, bottomToTopBlur );
runLineOperation( *pImage, bottomToTopBlur );
StackBlurLineOperation rightToLeftBlur( alpha, QgsImageOperation::ByRow, false, i1, i2 );
runLineOperation( image, rightToLeftBlur );
runLineOperation( *pImage, rightToLeftBlur );
if ( pImage->format() != originalFormat )
{
image = pImage->convertToFormat( originalFormat );
delete pImage;
}
}
void QgsImageOperation::StackBlurLineOperation::operator()( QRgb* startRef, const int lineLength, const int bytesPerLine )
@ -591,17 +609,34 @@ QImage *QgsImageOperation::gaussianBlur( QImage &image, const int radius )
double* kernel = createGaussianKernel( radius );
//ensure correct source format.
QImage::Format originalFormat = image.format();
QImage* pImage = ℑ
if ( originalFormat != QImage::Format_ARGB32_Premultiplied )
{
pImage = new QImage( image.convertToFormat( QImage::Format_ARGB32_Premultiplied ) );
}
//blur along rows
QImage xBlurImage = QImage( width, height, QImage::Format_ARGB32 );
QImage xBlurImage = QImage( width, height, QImage::Format_ARGB32_Premultiplied );
GaussianBlurOperation rowBlur( radius, QgsImageOperation::ByRow, &xBlurImage, kernel );
runRectOperation( image, rowBlur );
runRectOperation( *pImage, rowBlur );
//blur along columns
QImage* yBlurImage = new QImage( width, height, QImage::Format_ARGB32 );
QImage* yBlurImage = new QImage( width, height, QImage::Format_ARGB32_Premultiplied );
GaussianBlurOperation colBlur( radius, QgsImageOperation::ByColumn, yBlurImage, kernel );
runRectOperation( xBlurImage, colBlur );
delete[] kernel;
if ( originalFormat != QImage::Format_ARGB32_Premultiplied )
{
QImage* convertedImage = new QImage( yBlurImage->convertToFormat( originalFormat ) );
delete yBlurImage;
delete pImage;
return convertedImage;
}
return yBlurImage;
}

View File

@ -36,10 +36,6 @@ class QgsVectorColorRampV2;
* Operations are written to either modify an image in place or return a new image, depending
* on which is faster for the particular operation.
*
* \note These operations do not work using premultiplied ARGB32_Premultiplied images
* - please make sure the images are converted to standard ARGB32 images prior to calling
* these operations.
*
* \note Added in version 2.7
*/
class CORE_EXPORT QgsImageOperation
@ -145,6 +141,8 @@ class CORE_EXPORT QgsImageOperation
* @param image QImage to blur
* @param radius blur radius in pixels, maximum value of 16
* @param alphaOnly set to true to blur only the alpha component of the image
* @note for fastest operation, ensure the source image is ARGB32_Premultiplied if
* alphaOnly is set to false, or ARGB32 if alphaOnly is true
*/
static void stackBlur( QImage &image, const int radius, const bool alphaOnly = false );
@ -153,6 +151,7 @@ class CORE_EXPORT QgsImageOperation
* @param image QImage to blur
* @param radius blur radius in pixels
* @returns blurred image
* @note for fastest operation, ensure the source image is ARGB32_Premultiplied
*/
static QImage* gaussianBlur( QImage &image, const int radius );

View File

@ -20,6 +20,7 @@
#include <QObject>
#include <QtTest/QtTest>
#include "qgsrenderchecker.h"
#include "qgssymbollayerv2utils.h"
class TestQgsImageOperation : public QObject
{
@ -66,6 +67,7 @@ class TestQgsImageOperation : public QObject
//stack blur
void stackBlur();
void stackBlurPremultiplied();
void alphaOnlyBlur();
//gaussian blur
@ -327,6 +329,18 @@ void TestQgsImageOperation::stackBlur()
bool result = imageCheck( QString( "imageop_stackblur" ), image, 0 );
QVERIFY( result );
QCOMPARE( image.format(), QImage::Format_ARGB32 );
}
void TestQgsImageOperation::stackBlurPremultiplied()
{
QImage image( mSampleImage );
image = image.convertToFormat( QImage::Format_ARGB32_Premultiplied );
QgsImageOperation::stackBlur( image, 10 );
bool result = imageCheck( QString( "imageop_stackblur" ), image, 0 );
QVERIFY( result );
QCOMPARE( image.format(), QImage::Format_ARGB32_Premultiplied );
}
void TestQgsImageOperation::alphaOnlyBlur()
@ -336,6 +350,15 @@ void TestQgsImageOperation::alphaOnlyBlur()
bool result = imageCheck( QString( "imageop_stackblur_alphaonly" ), image, 0 );
QVERIFY( result );
QCOMPARE( image.format(), QImage::Format_ARGB32 );
QImage premultImage( QString( TEST_DATA_DIR ) + QDir::separator() + "small_sample_image.png" );
premultImage = premultImage.convertToFormat( QImage::Format_ARGB32_Premultiplied );
QgsImageOperation::stackBlur( premultImage, 10, true );
result = imageCheck( QString( "imageop_stackblur_alphaonly" ), premultImage, 0 );
QVERIFY( result );
QCOMPARE( premultImage.format(), QImage::Format_ARGB32_Premultiplied );
}
void TestQgsImageOperation::gaussianBlur()
@ -344,6 +367,7 @@ void TestQgsImageOperation::gaussianBlur()
QImage* blurredImage = QgsImageOperation::gaussianBlur( image, 30 );
bool result = imageCheck( QString( "imageop_gaussianblur" ), *blurredImage, 0 );
QCOMPARE( blurredImage->format(), QImage::Format_ARGB32 );
delete blurredImage;
QVERIFY( result );
}
@ -352,8 +376,11 @@ void TestQgsImageOperation::gaussianBlur()
void TestQgsImageOperation::gaussianBlurSmall()
{
QImage image( QString( TEST_DATA_DIR ) + QDir::separator() + "small_sample_image.png" );
image = image.convertToFormat( QImage::Format_ARGB32_Premultiplied );
QImage* blurredImage = QgsImageOperation::gaussianBlur( image, 10 );
QCOMPARE( blurredImage->format(), QImage::Format_ARGB32_Premultiplied );
bool result = imageCheck( QString( "imageop_gaussianblur_small" ), *blurredImage, 0 );
delete blurredImage;
QVERIFY( result );
@ -397,7 +424,7 @@ bool TestQgsImageOperation::imageCheck( QString testName, QImage &image, int mis
QgsRenderChecker checker;
checker.setControlName( "expected_" + testName );
checker.setRenderedImage( fileName );
checker.setColorTolerance( 1 );
checker.setColorTolerance( 2 );
bool resultFlag = checker.compareImages( testName, mismatchCount );
mReport += checker.report();
return resultFlag;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 153 KiB

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 143 KiB

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB