1 /***************************************************************************
2                          testqgsimageoperations.cpp
3                          --------------------------
4     begin                : January 2015
5     copyright            : (C) 2015 by Nyall Dawson
6     email                : nyall dot dawson at gmail dot com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 
18 #include "qgsimageoperation.h"
19 #include "qgscolorramp.h"
20 #include <QObject>
21 #include "qgstest.h"
22 #include "qgsrenderchecker.h"
23 #include "qgssymbollayerutils.h"
24 #include "qgsapplication.h"
25 
26 class TestQgsImageOperation : public QObject
27 {
28     Q_OBJECT
29 
30   private slots:
31     void initTestCase();// will be called before the first testfunction is executed.
32     void cleanupTestCase();// will be called after the last testfunction was executed.
33     void init();// will be called before each testfunction is executed.
34     void cleanup();// will be called after every testfunction.
35     void smallImageOp(); //test operation on small image (single threaded op)
36 
37     //grayscale
38     void grayscaleLightness();
39     void grayscaleLuminosity();
40     void grayscaleAverage();
41 
42     //brightness/contrast
43     void brightnessContrastNoChange();
44     void increaseBrightness();
45     void decreaseBrightness();
46     void increaseContrast();
47     void decreaseContrast();
48 
49     //hue/saturation
50     void hueSaturationNoChange();
51     void increaseSaturation();
52     void decreaseSaturation();
53     void colorizeFull();
54     void colorizePartial();
55 
56     //multiply opacity
57     void opacityNoChange();
58     void opacityIncrease();
59     void opacityDecrease();
60 
61     //overlay color
62     void overlayColor();
63 
64     //distance transform
65     void distanceTransformMaxDist();
66     void distanceTransformSetSpread();
67     void distanceTransformInterior();
68     void distanceTransformMisc();
69 
70     //stack blur
71     void stackBlur();
72     void stackBlurPremultiplied();
73     void alphaOnlyBlur();
74 
75     //gaussian blur
76     void gaussianBlur();
77     void gaussianBlurSmall();
78     void gaussianBlurNoChange();
79 
80     //flip
81     void flipHorizontal();
82     void flipVertical();
83 
84   private:
85 
86     QString mReport;
87     QString mSampleImage;
88     QString mTransparentSampleImage;
89 
90     bool imageCheck( const QString &testName, QImage &image, int mismatchCount );
91 };
92 
initTestCase()93 void TestQgsImageOperation::initTestCase()
94 {
95   QgsApplication::init();
96   QgsApplication::initQgis();
97 
98   mReport += QLatin1String( "<h1>Image Operation Tests</h1>\n" );
99   mSampleImage = QStringLiteral( TEST_DATA_DIR ) + "/sample_image.png";
100   mTransparentSampleImage = QStringLiteral( TEST_DATA_DIR ) + "/sample_alpha_image.png";
101 }
102 
cleanupTestCase()103 void TestQgsImageOperation::cleanupTestCase()
104 {
105   QString myReportFile = QDir::tempPath() + "/qgistest.html";
106   QFile myFile( myReportFile );
107   if ( myFile.open( QIODevice::WriteOnly | QIODevice::Append ) )
108   {
109     QTextStream myQTextStream( &myFile );
110     myQTextStream << mReport;
111     myFile.close();
112   }
113 }
114 
init()115 void TestQgsImageOperation::init()
116 {
117 }
118 
cleanup()119 void TestQgsImageOperation::cleanup()
120 {
121 
122 }
123 
smallImageOp()124 void TestQgsImageOperation::smallImageOp()
125 {
126   QImage image( QStringLiteral( TEST_DATA_DIR ) + "/small_sample_image.png" );
127   QgsImageOperation::convertToGrayscale( image, QgsImageOperation::GrayscaleLightness );
128 
129   bool result = imageCheck( QStringLiteral( "imageop_smallimage" ), image, 0 );
130   QVERIFY( result );
131 }
132 
grayscaleLightness()133 void TestQgsImageOperation::grayscaleLightness()
134 {
135   QImage image( mSampleImage );
136   QgsImageOperation::convertToGrayscale( image, QgsImageOperation::GrayscaleLightness );
137 
138   bool result = imageCheck( QStringLiteral( "imageop_graylightness" ), image, 0 );
139   QVERIFY( result );
140 }
141 
grayscaleLuminosity()142 void TestQgsImageOperation::grayscaleLuminosity()
143 {
144   QImage image( mSampleImage );
145   QgsImageOperation::convertToGrayscale( image, QgsImageOperation::GrayscaleLuminosity );
146 
147   bool result = imageCheck( QStringLiteral( "imageop_grayluminosity" ), image, 0 );
148   QVERIFY( result );
149 }
150 
grayscaleAverage()151 void TestQgsImageOperation::grayscaleAverage()
152 {
153   QImage image( mSampleImage );
154   QgsImageOperation::convertToGrayscale( image, QgsImageOperation::GrayscaleAverage );
155 
156   bool result = imageCheck( QStringLiteral( "imageop_grayaverage" ), image, 0 );
157   QVERIFY( result );
158 }
159 
brightnessContrastNoChange()160 void TestQgsImageOperation::brightnessContrastNoChange()
161 {
162   QImage image( mSampleImage );
163   QgsImageOperation::adjustBrightnessContrast( image, 0, 1.0 );
164 
165   bool result = imageCheck( QStringLiteral( "imageop_bcnochange" ), image, 0 );
166   QVERIFY( result );
167 }
168 
increaseBrightness()169 void TestQgsImageOperation::increaseBrightness()
170 {
171   QImage image( mSampleImage );
172   QgsImageOperation::adjustBrightnessContrast( image, 50, 1.0 );
173 
174   bool result = imageCheck( QStringLiteral( "imageop_increasebright" ), image, 0 );
175   QVERIFY( result );
176 }
177 
decreaseBrightness()178 void TestQgsImageOperation::decreaseBrightness()
179 {
180   QImage image( mSampleImage );
181   QgsImageOperation::adjustBrightnessContrast( image, -50, 1.0 );
182 
183   bool result = imageCheck( QStringLiteral( "imageop_decreasebright" ), image, 0 );
184   QVERIFY( result );
185 }
186 
increaseContrast()187 void TestQgsImageOperation::increaseContrast()
188 {
189   QImage image( mSampleImage );
190   QgsImageOperation::adjustBrightnessContrast( image, 0, 30.0 );
191 
192   bool result = imageCheck( QStringLiteral( "imageop_increasecontrast" ), image, 0 );
193   QVERIFY( result );
194 }
195 
decreaseContrast()196 void TestQgsImageOperation::decreaseContrast()
197 {
198   QImage image( mSampleImage );
199   QgsImageOperation::adjustBrightnessContrast( image, 0, 0.1 );
200 
201   bool result = imageCheck( QStringLiteral( "imageop_decreasecontrast" ), image, 0 );
202   QVERIFY( result );
203 }
204 
hueSaturationNoChange()205 void TestQgsImageOperation::hueSaturationNoChange()
206 {
207   QImage image( mSampleImage );
208   QgsImageOperation::adjustHueSaturation( image, 1.0 );
209 
210   bool result = imageCheck( QStringLiteral( "imageop_satnochange" ), image, 0 );
211   QVERIFY( result );
212 }
213 
increaseSaturation()214 void TestQgsImageOperation::increaseSaturation()
215 {
216   QImage image( mSampleImage );
217   QgsImageOperation::adjustHueSaturation( image, 5.0 );
218 
219   bool result = imageCheck( QStringLiteral( "imageop_increasesat" ), image, 0 );
220   QVERIFY( result );
221 }
222 
decreaseSaturation()223 void TestQgsImageOperation::decreaseSaturation()
224 {
225   QImage image( mSampleImage );
226   QgsImageOperation::adjustHueSaturation( image, 0.5 );
227 
228   bool result = imageCheck( QStringLiteral( "imageop_decreasesat" ), image, 0 );
229   QVERIFY( result );
230 }
231 
colorizeFull()232 void TestQgsImageOperation::colorizeFull()
233 {
234   QImage image( mSampleImage );
235   QgsImageOperation::adjustHueSaturation( image, 1.0, QColor( 255, 255, 0 ), 1.0 );
236 
237   bool result = imageCheck( QStringLiteral( "imageop_colorizefull" ), image, 0 );
238   QVERIFY( result );
239 }
240 
colorizePartial()241 void TestQgsImageOperation::colorizePartial()
242 {
243   QImage image( mSampleImage );
244   QgsImageOperation::adjustHueSaturation( image, 1.0, QColor( 255, 255, 0 ), 0.5 );
245 
246   bool result = imageCheck( QStringLiteral( "imageop_colorizepartial" ), image, 0 );
247   QVERIFY( result );
248 }
249 
opacityNoChange()250 void TestQgsImageOperation::opacityNoChange()
251 {
252   QImage image( mSampleImage );
253   QgsImageOperation::multiplyOpacity( image, 1.0 );
254 
255   bool result = imageCheck( QStringLiteral( "imageop_opacitynochange" ), image, 0 );
256   QVERIFY( result );
257 }
258 
opacityIncrease()259 void TestQgsImageOperation::opacityIncrease()
260 {
261   QImage image( mSampleImage );
262   QgsImageOperation::multiplyOpacity( image, 2.0 );
263 
264   bool result = imageCheck( QStringLiteral( "imageop_opacityincrease" ), image, 0 );
265   QVERIFY( result );
266 }
267 
opacityDecrease()268 void TestQgsImageOperation::opacityDecrease()
269 {
270   QImage image( mSampleImage );
271   QgsImageOperation::multiplyOpacity( image, 0.5 );
272 
273   bool result = imageCheck( QStringLiteral( "imageop_opacitydecrease" ), image, 0 );
274   QVERIFY( result );
275 }
276 
overlayColor()277 void TestQgsImageOperation::overlayColor()
278 {
279   QImage image( mSampleImage );
280   QgsImageOperation::overlayColor( image, QColor( 0, 255, 255 ) );
281 
282   bool result = imageCheck( QStringLiteral( "imageop_overlaycolor" ), image, 0 );
283   QVERIFY( result );
284 }
285 
distanceTransformMaxDist()286 void TestQgsImageOperation::distanceTransformMaxDist()
287 {
288   QImage image( mTransparentSampleImage );
289   QgsGradientColorRamp ramp;
290   QgsImageOperation::DistanceTransformProperties props;
291   props.useMaxDistance = true;
292   props.ramp = &ramp;
293   props.shadeExterior = true;
294 
295   QgsImageOperation::distanceTransform( image, props );
296 
297   bool result = imageCheck( QStringLiteral( "imageop_dt_max" ), image, 0 );
298   QVERIFY( result );
299 }
300 
distanceTransformSetSpread()301 void TestQgsImageOperation::distanceTransformSetSpread()
302 {
303   QImage image( mTransparentSampleImage );
304   QgsGradientColorRamp ramp;
305   QgsImageOperation::DistanceTransformProperties props;
306   props.useMaxDistance = false;
307   props.spread = 10;
308   props.ramp = &ramp;
309   props.shadeExterior = true;
310 
311   QgsImageOperation::distanceTransform( image, props );
312 
313   bool result = imageCheck( QStringLiteral( "imageop_dt_spread" ), image, 0 );
314   QVERIFY( result );
315 }
316 
distanceTransformInterior()317 void TestQgsImageOperation::distanceTransformInterior()
318 {
319   QImage image( mTransparentSampleImage );
320   QgsGradientColorRamp ramp;
321   QgsImageOperation::DistanceTransformProperties props;
322   props.useMaxDistance = true;
323   props.ramp = &ramp;
324   props.shadeExterior = false;
325 
326   QgsImageOperation::distanceTransform( image, props );
327 
328   bool result = imageCheck( QStringLiteral( "imageop_dt_interior" ), image, 0 );
329   QVERIFY( result );
330 }
331 
distanceTransformMisc()332 void TestQgsImageOperation::distanceTransformMisc()
333 {
334   //no ramp
335   QImage image( mSampleImage );
336   QgsImageOperation::DistanceTransformProperties props;
337   props.useMaxDistance = true;
338   props.ramp = nullptr;
339   props.shadeExterior = false;
340   QgsImageOperation::distanceTransform( image, props );
341   bool result = imageCheck( QStringLiteral( "imageop_nochange" ), image, 0 );
342   QVERIFY( result );
343 
344   //zero spread
345   QImage image2( mSampleImage );
346   QgsImageOperation::DistanceTransformProperties props2;
347   QgsGradientColorRamp ramp;
348   props2.useMaxDistance = false;
349   props2.spread = 0;
350   props2.ramp = &ramp;
351   props2.shadeExterior = false;
352   QgsImageOperation::distanceTransform( image2, props2 );
353   result = imageCheck( QStringLiteral( "imageop_zerospread" ), image2, 0 );
354   QVERIFY( result );
355 }
356 
stackBlur()357 void TestQgsImageOperation::stackBlur()
358 {
359   QImage image( mSampleImage );
360   QgsImageOperation::stackBlur( image, 10 );
361 
362   bool result = imageCheck( QStringLiteral( "imageop_stackblur" ), image, 0 );
363   QVERIFY( result );
364   QCOMPARE( image.format(), QImage::Format_ARGB32 );
365 }
366 
stackBlurPremultiplied()367 void TestQgsImageOperation::stackBlurPremultiplied()
368 {
369   QImage image( mSampleImage );
370   image = image.convertToFormat( QImage::Format_ARGB32_Premultiplied );
371   QgsImageOperation::stackBlur( image, 10 );
372 
373   bool result = imageCheck( QStringLiteral( "imageop_stackblur" ), image, 0 );
374   QVERIFY( result );
375   QCOMPARE( image.format(), QImage::Format_ARGB32_Premultiplied );
376 }
377 
alphaOnlyBlur()378 void TestQgsImageOperation::alphaOnlyBlur()
379 {
380   QImage image( QStringLiteral( TEST_DATA_DIR ) + "/small_sample_image.png" );
381   QgsImageOperation::stackBlur( image, 10, true );
382 
383   bool result = imageCheck( QStringLiteral( "imageop_stackblur_alphaonly" ), image, 0 );
384   QVERIFY( result );
385   QCOMPARE( image.format(), QImage::Format_ARGB32 );
386 
387   QImage premultImage( QStringLiteral( TEST_DATA_DIR ) + "/small_sample_image.png" );
388   premultImage = premultImage.convertToFormat( QImage::Format_ARGB32_Premultiplied );
389   QgsImageOperation::stackBlur( premultImage, 10, true );
390 
391   result = imageCheck( QStringLiteral( "imageop_stackblur_alphaonly" ), premultImage, 0 );
392   QVERIFY( result );
393   QCOMPARE( premultImage.format(), QImage::Format_ARGB32_Premultiplied );
394 }
395 
gaussianBlur()396 void TestQgsImageOperation::gaussianBlur()
397 {
398   QImage image( mSampleImage );
399   QImage *blurredImage = QgsImageOperation::gaussianBlur( image, 30 );
400 
401   bool result = imageCheck( QStringLiteral( "imageop_gaussianblur" ), *blurredImage, 0 );
402   QCOMPARE( blurredImage->format(), QImage::Format_ARGB32 );
403   delete blurredImage;
404   QVERIFY( result );
405 }
406 
407 //todo small, zero radius
gaussianBlurSmall()408 void TestQgsImageOperation::gaussianBlurSmall()
409 {
410   QImage image( QStringLiteral( TEST_DATA_DIR ) + "/small_sample_image.png" );
411   image = image.convertToFormat( QImage::Format_ARGB32_Premultiplied );
412 
413   QImage *blurredImage = QgsImageOperation::gaussianBlur( image, 10 );
414 
415   QCOMPARE( blurredImage->format(), QImage::Format_ARGB32_Premultiplied );
416   bool result = imageCheck( QStringLiteral( "imageop_gaussianblur_small" ), *blurredImage, 0 );
417   delete blurredImage;
418   QVERIFY( result );
419 }
420 
gaussianBlurNoChange()421 void TestQgsImageOperation::gaussianBlurNoChange()
422 {
423   QImage image( mSampleImage );
424   QImage *blurredImage = QgsImageOperation::gaussianBlur( image, 0 );
425 
426   bool result = imageCheck( QStringLiteral( "imageop_nochange" ), *blurredImage, 0 );
427   delete blurredImage;
428   QVERIFY( result );
429 }
430 
flipHorizontal()431 void TestQgsImageOperation::flipHorizontal()
432 {
433   QImage image( mSampleImage );
434   QgsImageOperation::flipImage( image, QgsImageOperation::FlipHorizontal );
435 
436   bool result = imageCheck( QStringLiteral( "imageop_fliphoz" ), image, 0 );
437   QVERIFY( result );
438 }
439 
flipVertical()440 void TestQgsImageOperation::flipVertical()
441 {
442   QImage image( mSampleImage );
443   QgsImageOperation::flipImage( image, QgsImageOperation::FlipVertical );
444 
445   bool result = imageCheck( QStringLiteral( "imageop_flipvert" ), image, 0 );
446   QVERIFY( result );
447 }
448 
449 //
450 // Private helper functions not called directly by CTest
451 //
452 
imageCheck(const QString & testName,QImage & image,int mismatchCount)453 bool TestQgsImageOperation::imageCheck( const QString &testName, QImage &image, int mismatchCount )
454 {
455   //draw background
456   QImage imageWithBackground( image.width(), image.height(), QImage::Format_RGB32 );
457   QgsRenderChecker::drawBackground( &imageWithBackground );
458   QPainter painter( &imageWithBackground );
459   painter.drawImage( 0, 0, image );
460   painter.end();
461 
462   mReport += "<h2>" + testName + "</h2>\n";
463   QString tempDir = QDir::tempPath() + '/';
464   QString fileName = tempDir + testName + ".png";
465   imageWithBackground.save( fileName, "PNG" );
466   QgsRenderChecker checker;
467   checker.setControlPathPrefix( QStringLiteral( "image_operations" ) );
468   checker.setControlName( "expected_" + testName );
469   checker.setRenderedImage( fileName );
470   checker.setColorTolerance( 2 );
471   bool resultFlag = checker.compareImages( testName, mismatchCount );
472   mReport += checker.report();
473   return resultFlag;
474 }
475 
476 QGSTEST_MAIN( TestQgsImageOperation )
477 #include "testqgsimageoperation.moc"
478