Skip to content

Commit

Permalink
Merge pull request #57433 from nyalldawson/faster_render_checker
Browse files Browse the repository at this point in the history
Speed up QgsRenderChecker
  • Loading branch information
troopa81 committed May 15, 2024
2 parents 04b3cbd + 9818fec commit 40110a0
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 40 deletions.
85 changes: 45 additions & 40 deletions src/core/qgsrenderchecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <QColor>
#include <QPainter>
#include <QImage>
#include <QImageReader>
#include <QCryptographicHash>
#include <QByteArray>
#include <QDebug>
Expand Down Expand Up @@ -282,8 +283,8 @@ bool QgsRenderChecker::runTest( const QString &testName,
//
// Load the expected result pixmap
//
const QImage myExpectedImage( mExpectedImageFile );
if ( myExpectedImage.isNull() )
const QImageReader expectedImageReader( mExpectedImageFile );
if ( !expectedImageReader.canRead() )
{
qDebug() << "QgsRenderChecker::runTest failed - Could not load expected image from " << mExpectedImageFile;
mReport = "<table>"
Expand All @@ -294,13 +295,15 @@ bool QgsRenderChecker::runTest( const QString &testName,
performPostTestActions( flags );
return mResult;
}
mMatchTarget = myExpectedImage.width() * myExpectedImage.height();

const QSize expectedSize = expectedImageReader.size();
mMatchTarget = expectedSize.width() * expectedSize.height();
//
// Now render our layers onto a pixmap
//
mMapSettings.setBackgroundColor( qRgb( 152, 219, 249 ) );
mMapSettings.setFlag( Qgis::MapSettingsFlag::Antialiasing );
mMapSettings.setOutputSize( QSize( myExpectedImage.width(), myExpectedImage.height() ) / mMapSettings.devicePixelRatio() );
mMapSettings.setOutputSize( expectedSize / mMapSettings.devicePixelRatio() );

QElapsedTimer myTime;
myTime.start();
Expand All @@ -311,46 +314,45 @@ bool QgsRenderChecker::runTest( const QString &testName,

mElapsedTime = myTime.elapsed();

QImage myImage = job.renderedImage();
Q_ASSERT( myImage.devicePixelRatioF() == mMapSettings.devicePixelRatio() );

//
// Save the pixmap to disk so the user can make a
// visual assessment if needed
//
mRenderedImageFile = QDir::tempPath() + '/' + testName + "_result.png";

myImage.setDotsPerMeterX( myExpectedImage.dotsPerMeterX() );
myImage.setDotsPerMeterY( myExpectedImage.dotsPerMeterY() );
if ( ! myImage.save( mRenderedImageFile, "PNG", 100 ) )
{
qDebug() << "QgsRenderChecker::runTest failed - Could not save rendered image to " << mRenderedImageFile;
mReport = "<table>"
"<tr><td>Test Result:</td><td>Expected Result:</td></tr>\n"
"<tr><td>Nothing rendered</td>\n<td>Failed because Rendered "
"Image File could not be saved.</td></tr></table>\n";
mMarkdownReport = QStringLiteral( "Failed because rendered image file could not be saved to %1\n" ).arg( mRenderedImageFile );
mRenderedImage = job.renderedImage();
Q_ASSERT( mRenderedImage.devicePixelRatioF() == mMapSettings.devicePixelRatio() );
const bool res = compareImages( testName, mismatchCount, QString(), flags );

performPostTestActions( flags );
return mResult;
}
if ( ! res )
{
// If test failed, save the pixmap to disk so the user can make a
// visual assessment
if ( ! mRenderedImage.save( mRenderedImageFile, "PNG", 100 ) )
{
qDebug() << "QgsRenderChecker::runTest failed - Could not save rendered image to " << mRenderedImageFile;
mReport = "<table>"
"<tr><td>Test Result:</td><td>Expected Result:</td></tr>\n"
"<tr><td>Nothing rendered</td>\n<td>Failed because Rendered "
"Image File could not be saved.</td></tr></table>\n";
mMarkdownReport = QStringLiteral( "Failed because rendered image file could not be saved to %1\n" ).arg( mRenderedImageFile );

//create a world file to go with the image...
performPostTestActions( flags );
return mResult;
}

QFile wldFile( QDir::tempPath() + '/' + testName + "_result.wld" );
if ( wldFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
{
const QgsRectangle r = mMapSettings.extent();

QTextStream stream( &wldFile );
stream << QStringLiteral( "%1\r\n0 \r\n0 \r\n%2\r\n%3\r\n%4\r\n" )
.arg( qgsDoubleToString( mMapSettings.mapUnitsPerPixel() ),
qgsDoubleToString( -mMapSettings.mapUnitsPerPixel() ),
qgsDoubleToString( r.xMinimum() + mMapSettings.mapUnitsPerPixel() / 2.0 ),
qgsDoubleToString( r.yMaximum() - mMapSettings.mapUnitsPerPixel() / 2.0 ) );
//create a world file to go with the image...
QFile wldFile( QDir::tempPath() + '/' + testName + "_result.wld" );
if ( wldFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
{
const QgsRectangle r = mMapSettings.extent();

QTextStream stream( &wldFile );
stream << QStringLiteral( "%1\r\n0 \r\n0 \r\n%2\r\n%3\r\n%4\r\n" )
.arg( qgsDoubleToString( mMapSettings.mapUnitsPerPixel() ),
qgsDoubleToString( -mMapSettings.mapUnitsPerPixel() ),
qgsDoubleToString( r.xMinimum() + mMapSettings.mapUnitsPerPixel() / 2.0 ),
qgsDoubleToString( r.yMaximum() - mMapSettings.mapUnitsPerPixel() / 2.0 ) );
}
}

return compareImages( testName, mismatchCount, QString(), flags );
return res;
}


Expand Down Expand Up @@ -423,7 +425,7 @@ bool QgsRenderChecker::compareImages( const QString &testName, const QString &re
return string.left( firstNonTagIndex ) + string.at( firstNonTagIndex ).toUpper() + string.mid( firstNonTagIndex + 1 );
};

QImage myResultImage( mRenderedImageFile );
QImage myResultImage = mRenderedImage.isNull() ? QImage( mRenderedImageFile ) : mRenderedImage;
if ( myResultImage.isNull() )
{
qDebug() << "QgsRenderChecker::runTest failed - Could not load rendered image from " << mRenderedImageFile;
Expand Down Expand Up @@ -631,6 +633,8 @@ bool QgsRenderChecker::compareImages( const QString &testName, const QString &re
const int maxHeight = std::min( expectedImage.height(), myResultImage.height() );
const int maxWidth = std::min( expectedImage.width(), myResultImage.width() );

const int maskWidth = maskImage.width();

mMismatchCount = 0;
const int colorTolerance = static_cast< int >( mColorTolerance );
for ( int y = 0; y < maxHeight; ++y )
Expand All @@ -642,8 +646,9 @@ bool QgsRenderChecker::compareImages( const QString &testName, const QString &re

for ( int x = 0; x < maxWidth; ++x )
{
const int maskTolerance = ( maskScanline && maskImage.width() > x ) ? qRed( maskScanline[ x ] ) : 0;
const int pixelTolerance = std::max( colorTolerance, maskTolerance );
const int pixelTolerance = maskScanline
? std::max( colorTolerance, ( maskWidth > x ) ? qRed( maskScanline[ x ] ) : 0 )
: colorTolerance;
if ( pixelTolerance == 255 )
{
//skip pixel
Expand Down
1 change: 1 addition & 0 deletions src/core/qgsrenderchecker.h
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ class CORE_EXPORT QgsRenderChecker
QString mMarkdownReport;
unsigned int mMatchTarget = 0;
int mElapsedTime = 0;
QImage mRenderedImage;
QString mRenderedImageFile;
QString mExpectedImageFile;

Expand Down

0 comments on commit 40110a0

Please sign in to comment.