diff --git a/python/core/effects/qgsimageoperation.sip b/python/core/effects/qgsimageoperation.sip index 32fa8c68481..939e23f7024 100644 --- a/python/core/effects/qgsimageoperation.sip +++ b/python/core/effects/qgsimageoperation.sip @@ -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/; diff --git a/src/core/effects/qgsimageoperation.cpp b/src/core/effects/qgsimageoperation.cpp index be86ef007a7..4d0de241718 100644 --- a/src/core/effects/qgsimageoperation.cpp +++ b/src/core/effects/qgsimageoperation.cpp @@ -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; } diff --git a/src/core/effects/qgsimageoperation.h b/src/core/effects/qgsimageoperation.h index 0ecf11fd1fe..bd8ef75b19e 100644 --- a/src/core/effects/qgsimageoperation.h +++ b/src/core/effects/qgsimageoperation.h @@ -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 ); diff --git a/tests/src/core/testqgsimageoperation.cpp b/tests/src/core/testqgsimageoperation.cpp index bf71093ee04..c3a88859ed5 100644 --- a/tests/src/core/testqgsimageoperation.cpp +++ b/tests/src/core/testqgsimageoperation.cpp @@ -20,6 +20,7 @@ #include #include #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; diff --git a/tests/testdata/control_images/expected_imageop_gaussianblur/expected_imageop_gaussianblur.png b/tests/testdata/control_images/expected_imageop_gaussianblur/expected_imageop_gaussianblur.png index 67b7e03e919..ef9b60a1f1f 100644 Binary files a/tests/testdata/control_images/expected_imageop_gaussianblur/expected_imageop_gaussianblur.png and b/tests/testdata/control_images/expected_imageop_gaussianblur/expected_imageop_gaussianblur.png differ diff --git a/tests/testdata/control_images/expected_imageop_gaussianblur_small/expected_imageop_gaussianblur_small.png b/tests/testdata/control_images/expected_imageop_gaussianblur_small/expected_imageop_gaussianblur_small.png index 1e3eaa93d17..89dc684e259 100644 Binary files a/tests/testdata/control_images/expected_imageop_gaussianblur_small/expected_imageop_gaussianblur_small.png and b/tests/testdata/control_images/expected_imageop_gaussianblur_small/expected_imageop_gaussianblur_small.png differ diff --git a/tests/testdata/control_images/expected_imageop_stackblur/expected_imageop_stackblur.png b/tests/testdata/control_images/expected_imageop_stackblur/expected_imageop_stackblur.png index 635cad6ff6a..6294774b5b5 100644 Binary files a/tests/testdata/control_images/expected_imageop_stackblur/expected_imageop_stackblur.png and b/tests/testdata/control_images/expected_imageop_stackblur/expected_imageop_stackblur.png differ diff --git a/tests/testdata/control_images/expected_painteffect_blur/painteffect_blur.png b/tests/testdata/control_images/expected_painteffect_blur/painteffect_blur.png new file mode 100644 index 00000000000..c0d1c10c093 Binary files /dev/null and b/tests/testdata/control_images/expected_painteffect_blur/painteffect_blur.png differ diff --git a/tests/testdata/control_images/expected_painteffect_dropshadow/expected_painteffect_dropshadow.png b/tests/testdata/control_images/expected_painteffect_dropshadow/expected_painteffect_dropshadow.png new file mode 100644 index 00000000000..dea25366a88 Binary files /dev/null and b/tests/testdata/control_images/expected_painteffect_dropshadow/expected_painteffect_dropshadow.png differ