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