QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
qgsrenderchecker.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsrenderchecker.cpp
3  --------------------------------------
4  Date : 18 Jan 2008
5  Copyright : (C) 2008 by Tim Sutton
6  Email : tim @ linfiniti.com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgsrenderchecker.h"
17 
18 #include "qgis.h"
20 #include "qgsgeometry.h"
21 
22 #include <QColor>
23 #include <QPainter>
24 #include <QImage>
25 #include <QCryptographicHash>
26 #include <QByteArray>
27 #include <QDebug>
28 #include <QBuffer>
29 
30 
32 {
33  QString myDataDir( TEST_DATA_DIR ); //defined in CmakeLists.txt
34  QString myControlImageDir = myDataDir + "/control_images/" + mControlPathPrefix;
35  return myControlImageDir;
36 }
37 
38 void QgsRenderChecker::setControlName( const QString &name )
39 {
40  mControlName = name;
41  mExpectedImageFile = controlImagePath() + name + '/' + mControlPathSuffix + name + ".png";
42 }
43 
44 void QgsRenderChecker::setControlPathSuffix( const QString &name )
45 {
46  if ( !name.isEmpty() )
47  mControlPathSuffix = name + '/';
48  else
49  mControlPathSuffix.clear();
50 }
51 
52 QString QgsRenderChecker::imageToHash( const QString &imageFile )
53 {
54  QImage myImage;
55  myImage.load( imageFile );
56  QByteArray myByteArray;
57  QBuffer myBuffer( &myByteArray );
58  myImage.save( &myBuffer, "PNG" );
59  QString myImageString = QString::fromUtf8( myByteArray.toBase64().data() );
60  QCryptographicHash myHash( QCryptographicHash::Md5 );
61  myHash.addData( myImageString.toUtf8() );
62  return myHash.result().toHex().constData();
63 }
64 
66 {
67  mMapSettings = mapSettings;
68 }
69 
70 void QgsRenderChecker::drawBackground( QImage *image )
71 {
72  // create a 2x2 checker-board image
73  uchar pixDataRGB[] = { 255, 255, 255, 255,
74  127, 127, 127, 255,
75  127, 127, 127, 255,
76  255, 255, 255, 255
77  };
78 
79  QImage img( pixDataRGB, 2, 2, 8, QImage::Format_ARGB32 );
80  QPixmap pix = QPixmap::fromImage( img.scaled( 20, 20 ) );
81 
82  // fill image with texture
83  QBrush brush;
84  brush.setTexture( pix );
85  QPainter p( image );
86  p.setRenderHint( QPainter::Antialiasing, false );
87  p.fillRect( QRect( 0, 0, image->width(), image->height() ), brush );
88  p.end();
89 }
90 
91 bool QgsRenderChecker::isKnownAnomaly( const QString &diffImageFile )
92 {
93  QString myControlImageDir = controlImagePath() + mControlName + '/';
94  QDir myDirectory = QDir( myControlImageDir );
95  QStringList myList;
96  QString myFilename = QStringLiteral( "*" );
97  myList = myDirectory.entryList( QStringList( myFilename ),
98  QDir::Files | QDir::NoSymLinks );
99  //remove the control file from the list as the anomalies are
100  //all files except the control file
101  myList.removeAt( myList.indexOf( QFileInfo( mExpectedImageFile ).fileName() ) );
102 
103  QString myImageHash = imageToHash( diffImageFile );
104 
105 
106  for ( int i = 0; i < myList.size(); ++i )
107  {
108  QString myFile = myList.at( i );
109  mReport += "<tr><td colspan=3>"
110  "Checking if " + myFile + " is a known anomaly.";
111  mReport += QLatin1String( "</td></tr>" );
112  QString myAnomalyHash = imageToHash( controlImagePath() + mControlName + '/' + myFile );
113  QString myHashMessage = QStringLiteral(
114  "Checking if anomaly %1 (hash %2)<br>" )
115  .arg( myFile,
116  myAnomalyHash );
117  myHashMessage += QStringLiteral( "&nbsp; matches %1 (hash %2)" )
118  .arg( diffImageFile,
119  myImageHash );
120  //foo CDash
121  emitDashMessage( QStringLiteral( "Anomaly check" ), QgsDartMeasurement::Text, myHashMessage );
122 
123  mReport += "<tr><td colspan=3>" + myHashMessage + "</td></tr>";
124  if ( myImageHash == myAnomalyHash )
125  {
126  mReport += "<tr><td colspan=3>"
127  "Anomaly found! " + myFile;
128  mReport += QLatin1String( "</td></tr>" );
129  return true;
130  }
131  }
132  mReport += "<tr><td colspan=3>"
133  "No anomaly found! ";
134  mReport += QLatin1String( "</td></tr>" );
135  return false;
136 }
137 
138 void QgsRenderChecker::emitDashMessage( const QgsDartMeasurement &dashMessage )
139 {
140  if ( mBufferDashMessages )
141  mDashMessages << dashMessage;
142  else
143  dashMessage.send();
144 }
145 
146 void QgsRenderChecker::emitDashMessage( const QString &name, QgsDartMeasurement::Type type, const QString &value )
147 {
148  emitDashMessage( QgsDartMeasurement( name, type, value ) );
149 }
150 
151 bool QgsRenderChecker::runTest( const QString &testName,
152  unsigned int mismatchCount )
153 {
154  if ( mExpectedImageFile.isEmpty() )
155  {
156  qDebug( "QgsRenderChecker::runTest failed - Expected Image File not set." );
157  mReport = "<table>"
158  "<tr><td>Test Result:</td><td>Expected Result:</td></tr>\n"
159  "<tr><td>Nothing rendered</td>\n<td>Failed because Expected "
160  "Image File not set.</td></tr></table>\n";
161  return false;
162  }
163  //
164  // Load the expected result pixmap
165  //
166  QImage myExpectedImage( mExpectedImageFile );
167  if ( myExpectedImage.isNull() )
168  {
169  qDebug() << "QgsRenderChecker::runTest failed - Could not load expected image from " << mExpectedImageFile;
170  mReport = "<table>"
171  "<tr><td>Test Result:</td><td>Expected Result:</td></tr>\n"
172  "<tr><td>Nothing rendered</td>\n<td>Failed because Expected "
173  "Image File could not be loaded.</td></tr></table>\n";
174  return false;
175  }
176  mMatchTarget = myExpectedImage.width() * myExpectedImage.height();
177  //
178  // Now render our layers onto a pixmap
179  //
180  mMapSettings.setBackgroundColor( qRgb( 152, 219, 249 ) );
181  mMapSettings.setFlag( QgsMapSettings::Antialiasing );
182  mMapSettings.setOutputSize( QSize( myExpectedImage.width(), myExpectedImage.height() ) / mMapSettings.devicePixelRatio() );
183 
184  QElapsedTimer myTime;
185  myTime.start();
186 
187  QgsMapRendererSequentialJob job( mMapSettings );
188  job.start();
189  job.waitForFinished();
190 
191  mElapsedTime = myTime.elapsed();
192 
193  QImage myImage = job.renderedImage();
194  Q_ASSERT( myImage.devicePixelRatioF() == mMapSettings.devicePixelRatio() );
195 
196  //
197  // Save the pixmap to disk so the user can make a
198  // visual assessment if needed
199  //
200  mRenderedImageFile = QDir::tempPath() + '/' + testName + "_result.png";
201 
202  myImage.setDotsPerMeterX( myExpectedImage.dotsPerMeterX() );
203  myImage.setDotsPerMeterY( myExpectedImage.dotsPerMeterY() );
204  if ( ! myImage.save( mRenderedImageFile, "PNG", 100 ) )
205  {
206  qDebug() << "QgsRenderChecker::runTest failed - Could not save rendered image to " << mRenderedImageFile;
207  mReport = "<table>"
208  "<tr><td>Test Result:</td><td>Expected Result:</td></tr>\n"
209  "<tr><td>Nothing rendered</td>\n<td>Failed because Rendered "
210  "Image File could not be saved.</td></tr></table>\n";
211  return false;
212  }
213 
214  //create a world file to go with the image...
215 
216  QFile wldFile( QDir::tempPath() + '/' + testName + "_result.wld" );
217  if ( wldFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
218  {
219  QgsRectangle r = mMapSettings.extent();
220 
221  QTextStream stream( &wldFile );
222  stream << QStringLiteral( "%1\r\n0 \r\n0 \r\n%2\r\n%3\r\n%4\r\n" )
223  .arg( qgsDoubleToString( mMapSettings.mapUnitsPerPixel() ),
224  qgsDoubleToString( -mMapSettings.mapUnitsPerPixel() ),
225  qgsDoubleToString( r.xMinimum() + mMapSettings.mapUnitsPerPixel() / 2.0 ),
226  qgsDoubleToString( r.yMaximum() - mMapSettings.mapUnitsPerPixel() / 2.0 ) );
227  }
228 
229  return compareImages( testName, mismatchCount );
230 }
231 
232 
233 bool QgsRenderChecker::compareImages( const QString &testName,
234  unsigned int mismatchCount,
235  const QString &renderedImageFile )
236 {
237  if ( mExpectedImageFile.isEmpty() )
238  {
239  qDebug( "QgsRenderChecker::runTest failed - Expected Image (control) File not set." );
240  mReport = "<table>"
241  "<tr><td>Test Result:</td><td>Expected Result:</td></tr>\n"
242  "<tr><td>Nothing rendered</td>\n<td>Failed because Expected "
243  "Image File not set.</td></tr></table>\n";
244  return false;
245  }
246  if ( ! renderedImageFile.isEmpty() )
247  {
248  mRenderedImageFile = renderedImageFile;
249 #ifdef Q_OS_WIN
250  mRenderedImageFile = mRenderedImageFile.replace( '\\', '/' );
251 #endif
252  }
253 
254  if ( mRenderedImageFile.isEmpty() )
255  {
256  qDebug( "QgsRenderChecker::runTest failed - Rendered Image File not set." );
257  mReport = "<table>"
258  "<tr><td>Test Result:</td><td>Expected Result:</td></tr>\n"
259  "<tr><td>Nothing rendered</td>\n<td>Failed because Rendered "
260  "Image File not set.</td></tr></table>\n";
261  return false;
262  }
263 
264  //
265  // Load /create the images
266  //
267  QImage myExpectedImage( mExpectedImageFile );
268  QImage myResultImage( mRenderedImageFile );
269  if ( myResultImage.isNull() )
270  {
271  qDebug() << "QgsRenderChecker::runTest failed - Could not load rendered image from " << mRenderedImageFile;
272  mReport = "<table>"
273  "<tr><td>Test Result:</td><td>Expected Result:</td></tr>\n"
274  "<tr><td>Nothing rendered</td>\n<td>Failed because Rendered "
275  "Image File could not be loaded.</td></tr></table>\n";
276  return false;
277  }
278  QImage myDifferenceImage( myExpectedImage.width(),
279  myExpectedImage.height(),
280  QImage::Format_RGB32 );
281  QString myDiffImageFile = QDir::tempPath() + '/' + testName + "_result_diff.png";
282  myDifferenceImage.fill( qRgb( 152, 219, 249 ) );
283 
284  //check for mask
285  QString maskImagePath = mExpectedImageFile;
286  maskImagePath.chop( 4 ); //remove .png extension
287  maskImagePath += QLatin1String( "_mask.png" );
288  const QImage maskImage( maskImagePath );
289  const bool hasMask = !maskImage.isNull();
290 
291  //
292  // Set pixel count score and target
293  //
294  mMatchTarget = myExpectedImage.width() * myExpectedImage.height();
295  unsigned int myPixelCount = myResultImage.width() * myResultImage.height();
296  //
297  // Set the report with the result
298  //
299  mReport = QStringLiteral( "<script src=\"file://%1/../renderchecker.js\"></script>\n" ).arg( TEST_DATA_DIR );
300  mReport += QLatin1String( "<table>" );
301  mReport += QLatin1String( "<tr><td colspan=2>" );
302  mReport += QString( "<tr><td colspan=2>"
303  "Test image and result image for %1<br>"
304  "Expected size: %2 w x %3 h (%4 pixels)<br>"
305  "Actual size: %5 w x %6 h (%7 pixels)"
306  "</td></tr>" )
307  .arg( testName )
308  .arg( myExpectedImage.width() ).arg( myExpectedImage.height() ).arg( mMatchTarget )
309  .arg( myResultImage.width() ).arg( myResultImage.height() ).arg( myPixelCount );
310  mReport += QString( "<tr><td colspan=2>\n"
311  "Expected Duration : <= %1 (0 indicates not specified)<br>"
312  "Actual Duration : %2 ms<br></td></tr>" )
313  .arg( mElapsedTimeTarget )
314  .arg( mElapsedTime );
315 
316  // limit image size in page to something reasonable
317  int imgWidth = 420;
318  int imgHeight = 280;
319  if ( ! myExpectedImage.isNull() )
320  {
321  imgWidth = std::min( myExpectedImage.width(), imgWidth );
322  imgHeight = myExpectedImage.height() * imgWidth / myExpectedImage.width();
323  }
324 
325  QString myImagesString = QString(
326  "<tr>"
327  "<td colspan=2>Compare actual and expected result</td>"
328  "<td>Difference (all blue is good, any red is bad)</td>"
329  "</tr>\n<tr>"
330  "<td colspan=2 id=\"td-%1-%7\"></td>\n"
331  "<td align=center><img width=%5 height=%6 src=\"file://%2\"></td>\n"
332  "</tr>"
333  "</table>\n"
334  "<script>\naddComparison(\"td-%1-%7\",\"file://%3\",\"file://%4\",%5,%6);\n</script>\n" )
335  .arg( testName,
336  myDiffImageFile,
339  .arg( imgWidth ).arg( imgHeight )
340  .arg( QUuid::createUuid().toString().mid( 1, 6 ) );
341 
342  QString prefix;
343  if ( !mControlPathPrefix.isNull() )
344  {
345  prefix = QStringLiteral( " (prefix %1)" ).arg( mControlPathPrefix );
346  }
347  //
348  // To get the images into CDash
349  //
350  emitDashMessage( "Rendered Image " + testName + prefix, QgsDartMeasurement::ImagePng, mRenderedImageFile );
351  emitDashMessage( "Expected Image " + testName + prefix, QgsDartMeasurement::ImagePng, mExpectedImageFile );
352 
353  //
354  // Put the same info to debug too
355  //
356 
357  if ( myExpectedImage.width() != myResultImage.width() || myExpectedImage.height() != myResultImage.height() )
358  {
359  qDebug( "Expected size: %dw x %dh", myExpectedImage.width(), myExpectedImage.height() );
360  qDebug( "Actual size: %dw x %dh", myResultImage.width(), myResultImage.height() );
361  if ( hasMask )
362  qDebug( "Mask size: %dw x %dh", maskImage.width(), maskImage.height() );
363  }
364 
365  if ( mMatchTarget != myPixelCount )
366  {
367  qDebug( "Test image and result image for %s are different dimensions", testName.toLocal8Bit().constData() );
368 
369  if ( std::abs( myExpectedImage.width() - myResultImage.width() ) > mMaxSizeDifferenceX ||
370  std::abs( myExpectedImage.height() - myResultImage.height() ) > mMaxSizeDifferenceY )
371  {
372  mReport += QLatin1String( "<tr><td colspan=3>" );
373  mReport += "<font color=red>Expected image and result image for " + testName + " are different dimensions - FAILING!</font>";
374  mReport += QLatin1String( "</td></tr>" );
375  mReport += myImagesString;
376  return false;
377  }
378  else
379  {
380  mReport += QLatin1String( "<tr><td colspan=3>" );
381  mReport += "Expected image and result image for " + testName + " are different dimensions, but within tolerance";
382  mReport += QLatin1String( "</td></tr>" );
383  }
384  }
385 
386  if ( myExpectedImage.format() == QImage::Format_Indexed8 )
387  {
388  if ( myResultImage.format() != QImage::Format_Indexed8 )
389  {
390  qDebug() << "Expected image and result image for " << testName << " have different formats (8bit format is expected) - FAILING!";
391 
392  mReport += QLatin1String( "<tr><td colspan=3>" );
393  mReport += "<font color=red>Expected image and result image for " + testName + " have different formats (8bit format is expected) - FAILING!</font>";
394  mReport += QLatin1String( "</td></tr>" );
395  mReport += myImagesString;
396  return false;
397  }
398 
399  // When we compute the diff between the 2 images, we use constScanLine expecting a QRgb color
400  // but this method returns color table index for 8 bit image, not color.
401  // So we convert the 2 images in 32 bits so the diff works correctly
402  myResultImage = myResultImage.convertToFormat( QImage::Format_ARGB32 );
403  myExpectedImage = myExpectedImage.convertToFormat( QImage::Format_ARGB32 );
404  }
405 
406 
407  //
408  // Now iterate through them counting how many
409  // dissimilar pixel values there are
410  //
411 
412  int maxHeight = std::min( myExpectedImage.height(), myResultImage.height() );
413  int maxWidth = std::min( myExpectedImage.width(), myResultImage.width() );
414 
415  mMismatchCount = 0;
416  int colorTolerance = static_cast< int >( mColorTolerance );
417  for ( int y = 0; y < maxHeight; ++y )
418  {
419  const QRgb *expectedScanline = reinterpret_cast< const QRgb * >( myExpectedImage.constScanLine( y ) );
420  const QRgb *resultScanline = reinterpret_cast< const QRgb * >( myResultImage.constScanLine( y ) );
421  const QRgb *maskScanline = ( hasMask && maskImage.height() > y ) ? reinterpret_cast< const QRgb * >( maskImage.constScanLine( y ) ) : nullptr;
422  QRgb *diffScanline = reinterpret_cast< QRgb * >( myDifferenceImage.scanLine( y ) );
423 
424  for ( int x = 0; x < maxWidth; ++x )
425  {
426  int maskTolerance = ( maskScanline && maskImage.width() > x ) ? qRed( maskScanline[ x ] ) : 0;
427  int pixelTolerance = std::max( colorTolerance, maskTolerance );
428  if ( pixelTolerance == 255 )
429  {
430  //skip pixel
431  continue;
432  }
433 
434  QRgb myExpectedPixel = expectedScanline[x];
435  QRgb myActualPixel = resultScanline[x];
436  if ( pixelTolerance == 0 )
437  {
438  if ( myExpectedPixel != myActualPixel )
439  {
440  ++mMismatchCount;
441  diffScanline[ x ] = qRgb( 255, 0, 0 );
442  }
443  }
444  else
445  {
446  if ( std::abs( qRed( myExpectedPixel ) - qRed( myActualPixel ) ) > pixelTolerance ||
447  std::abs( qGreen( myExpectedPixel ) - qGreen( myActualPixel ) ) > pixelTolerance ||
448  std::abs( qBlue( myExpectedPixel ) - qBlue( myActualPixel ) ) > pixelTolerance ||
449  std::abs( qAlpha( myExpectedPixel ) - qAlpha( myActualPixel ) ) > pixelTolerance )
450  {
451  ++mMismatchCount;
452  diffScanline[ x ] = qRgb( 255, 0, 0 );
453  }
454  }
455  }
456  }
457  //
458  //save the diff image to disk
459  //
460  myDifferenceImage.save( myDiffImageFile );
461  emitDashMessage( "Difference Image " + testName + prefix, QgsDartMeasurement::ImagePng, myDiffImageFile );
462 
463  //
464  // Send match result to debug
465  //
466  if ( mMismatchCount > mismatchCount )
467  {
468  qDebug( "%d/%d pixels mismatched (%d allowed)", mMismatchCount, mMatchTarget, mismatchCount );
469  }
470 
471  //
472  // Send match result to report
473  //
474  mReport += QStringLiteral( "<tr><td colspan=3>%1/%2 pixels mismatched (allowed threshold: %3, allowed color component tolerance: %4)</td></tr>" )
475  .arg( mMismatchCount ).arg( mMatchTarget ).arg( mismatchCount ).arg( mColorTolerance );
476 
477  //
478  // And send it to CDash
479  //
480  emitDashMessage( QStringLiteral( "Mismatch Count" ), QgsDartMeasurement::Integer, QStringLiteral( "%1/%2" ).arg( mMismatchCount ).arg( mMatchTarget ) );
481 
482  if ( mMismatchCount <= mismatchCount )
483  {
484  mReport += QLatin1String( "<tr><td colspan = 3>\n" );
485  mReport += "Test image and result image for " + testName + " are matched<br>";
486  mReport += QLatin1String( "</td></tr>" );
487  if ( mElapsedTimeTarget != 0 && mElapsedTimeTarget < mElapsedTime )
488  {
489  //test failed because it took too long...
490  qDebug( "Test failed because render step took too long" );
491  mReport += QLatin1String( "<tr><td colspan = 3>\n" );
492  mReport += QLatin1String( "<font color=red>Test failed because render step took too long</font>" );
493  mReport += QLatin1String( "</td></tr>" );
494  mReport += myImagesString;
495  return false;
496  }
497  else
498  {
499  mReport += myImagesString;
500  return true;
501  }
502  }
503 
504  bool myAnomalyMatchFlag = isKnownAnomaly( myDiffImageFile );
505  if ( myAnomalyMatchFlag )
506  {
507  mReport += "<tr><td colspan=3>"
508  "Difference image matched a known anomaly - passing test! "
509  "</td></tr>";
510  return true;
511  }
512 
513  mReport += QLatin1String( "<tr><td colspan=3></td></tr>" );
514  emitDashMessage( QStringLiteral( "Image mismatch" ), QgsDartMeasurement::Text, "Difference image did not match any known anomaly or mask."
515  " If you feel the difference image should be considered an anomaly "
516  "you can do something like this\n"
517  "cp '" + myDiffImageFile + "' " + controlImagePath() + mControlName +
518  "/\nIf it should be included in the mask run\n"
519  "scripts/generate_test_mask_image.py '" + mExpectedImageFile + "' '" + mRenderedImageFile + "'\n" );
520 
521  mReport += QLatin1String( "<tr><td colspan = 3>\n" );
522  mReport += "<font color=red>Test image and result image for " + testName + " are mismatched</font><br>";
523  mReport += QLatin1String( "</td></tr>" );
524  mReport += myImagesString;
525  return false;
526 }
QgsRenderChecker::mExpectedImageFile
QString mExpectedImageFile
Definition: qgsrenderchecker.h:180
QgsRenderChecker::setMapSettings
void setMapSettings(const QgsMapSettings &mapSettings)
Definition: qgsrenderchecker.cpp:65
qgsrenderchecker.h
QgsMapSettings::setFlag
void setFlag(Flag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
Definition: qgsmapsettings.cpp:342
QgsRenderChecker::isKnownAnomaly
bool isKnownAnomaly(const QString &diffImageFile)
Gets a list of all the anomalies.
Definition: qgsrenderchecker.cpp:91
QgsRenderChecker::imageToHash
QString imageToHash(const QString &imageFile)
Gets an md5 hash that uniquely identifies an image.
Definition: qgsrenderchecker.cpp:52
QgsMapSettings::devicePixelRatio
float devicePixelRatio() const
Returns device pixel ratio Common values are 1 for normal-dpi displays and 2 for high-dpi "retina" di...
Definition: qgsmapsettings.cpp:247
QgsRenderChecker::setControlPathSuffix
void setControlPathSuffix(const QString &name)
Definition: qgsrenderchecker.cpp:44
QgsMapSettings::mapUnitsPerPixel
double mapUnitsPerPixel() const
Returns the distance in geographical coordinates that equals to one pixel in the map.
Definition: qgsmapsettings.cpp:391
qgis.h
QgsRenderChecker::compareImages
bool compareImages(const QString &testName, unsigned int mismatchCount=0, const QString &renderedImageFile=QString())
Test using two arbitrary images (map renderer will not be used)
Definition: qgsrenderchecker.cpp:233
QgsDartMeasurement::Text
@ Text
Definition: qgsdartmeasurement.h:32
QgsRectangle
A rectangle specified with double values.
Definition: qgsrectangle.h:42
qgsDoubleToString
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:275
QgsDartMeasurement::Integer
@ Integer
Definition: qgsdartmeasurement.h:34
QgsMapSettings::extent
QgsRectangle extent() const
Returns geographical coordinates of the rectangle that should be rendered.
Definition: qgsmapsettings.cpp:74
QgsRenderChecker::mReport
QString mReport
Definition: qgsrenderchecker.h:176
QgsMapSettings::setBackgroundColor
void setBackgroundColor(const QColor &color)
Sets the background color of the map.
Definition: qgsmapsettings.h:291
QgsRenderChecker::drawBackground
static void drawBackground(QImage *image)
Draws a checkboard pattern for image backgrounds, so that opacity is visible without requiring a tran...
Definition: qgsrenderchecker.cpp:70
QgsMapSettings::Antialiasing
@ Antialiasing
Enable anti-aliasing for map rendering.
Definition: qgsmapsettings.h:303
QgsRenderChecker::mElapsedTime
int mElapsedTime
Definition: qgsrenderchecker.h:178
QgsMapRendererSequentialJob
Job implementation that renders everything sequentially in one thread.
Definition: qgsmaprenderersequentialjob.h:34
QgsDartMeasurement::Type
Type
Definition: qgsdartmeasurement.h:31
QgsRectangle::xMinimum
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:167
QgsRenderChecker::controlImagePath
QString controlImagePath() const
Definition: qgsrenderchecker.cpp:31
QgsRenderChecker::mismatchCount
unsigned int mismatchCount()
Definition: qgsrenderchecker.h:56
qgsmaprenderersequentialjob.h
QgsDartMeasurement
Definition: qgsdartmeasurement.h:28
QgsRectangle::yMaximum
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:172
qgsgeometry.h
QgsMapRendererSequentialJob::waitForFinished
void waitForFinished() override
Block until the job has finished.
Definition: qgsmaprenderersequentialjob.cpp:95
QgsRenderChecker::mMatchTarget
unsigned int mMatchTarget
Definition: qgsrenderchecker.h:177
QgsDartMeasurement::send
void send() const
Definition: qgsdartmeasurement.cpp:43
QgsMapRendererSequentialJob::start
void start() override
Start the rendering job and immediately return.
Definition: qgsmaprenderersequentialjob.cpp:49
QgsRenderChecker::runTest
bool runTest(const QString &testName, unsigned int mismatchCount=0)
Test using renderer to generate the image to be compared.
Definition: qgsrenderchecker.cpp:151
QgsMapSettings::setOutputSize
void setOutputSize(QSize size)
Sets the size of the resulting map image.
Definition: qgsmapsettings.cpp:240
QgsMapSettings
The QgsMapSettings class contains configuration for rendering of the map.
Definition: qgsmapsettings.h:88
QgsDartMeasurement::ImagePng
@ ImagePng
Definition: qgsdartmeasurement.h:33
QgsRenderChecker::mRenderedImageFile
QString mRenderedImageFile
Definition: qgsrenderchecker.h:179
QgsMapRendererSequentialJob::renderedImage
QImage renderedImage() override
Gets a preview/resulting image.
Definition: qgsmaprenderersequentialjob.cpp:119
QgsRenderChecker::setControlName
void setControlName(const QString &name)
Base directory name for the control image (with control image path suffixed) the path to the image wi...
Definition: qgsrenderchecker.cpp:38