Skip to content

Commit df80fe3

Browse files
authored
Merge pull request #60604 from nyalldawson/fix_51273
Fix slow performance of raster image marker
2 parents 6525542 + 12cffa3 commit df80fe3

File tree

3 files changed

+144
-2
lines changed

3 files changed

+144
-2
lines changed

src/core/qgsimagecache.cpp

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,11 @@ QImage QgsImageCache::pathAsImagePrivate( const QString &f, const QSize size, co
215215
}
216216

217217
QSize QgsImageCache::originalSize( const QString &path, bool blocking ) const
218+
{
219+
return mImageSizeCache.originalSize( path, blocking );
220+
}
221+
222+
QSize QgsImageCache::originalSizePrivate( const QString &path, bool blocking ) const
218223
{
219224
if ( path.isEmpty() )
220225
return QSize();
@@ -527,4 +532,83 @@ QImage QgsImageCache::getFrameFromReader( QImageReader &reader, int frameNumber
527532
return reader.read();
528533
}
529534

535+
///@cond PRIVATE
530536
template class QgsAbstractContentCache<QgsImageCacheEntry>; // clazy:exclude=missing-qobject-macro
537+
538+
QgsImageSizeCacheEntry::QgsImageSizeCacheEntry( const QString &path )
539+
: QgsAbstractContentCacheEntry( path )
540+
{
541+
542+
}
543+
544+
int QgsImageSizeCacheEntry::dataSize() const
545+
{
546+
return sizeof( QSize );
547+
}
548+
549+
void QgsImageSizeCacheEntry::dump() const
550+
{
551+
QgsDebugMsgLevel( QStringLiteral( "path: %1" ).arg( path ), 3 );
552+
}
553+
554+
bool QgsImageSizeCacheEntry::isEqual( const QgsAbstractContentCacheEntry *other ) const
555+
{
556+
const QgsImageSizeCacheEntry *otherImage = dynamic_cast< const QgsImageSizeCacheEntry * >( other );
557+
if ( !otherImage
558+
|| otherImage->path != path )
559+
return false;
560+
561+
return true;
562+
}
563+
564+
template class QgsAbstractContentCache<QgsImageSizeCacheEntry>; // clazy:exclude=missing-qobject-macro
565+
566+
567+
//
568+
// QgsImageSizeCache
569+
//
570+
571+
QgsImageSizeCache::QgsImageSizeCache( QObject *parent )
572+
: QgsAbstractContentCache< QgsImageSizeCacheEntry >( parent, QObject::tr( "Image" ) )
573+
{
574+
mMaxCacheSize = 524288; // 500kb max cache size, we are only storing QSize objects here, so that should be heaps
575+
}
576+
577+
QgsImageSizeCache::~QgsImageSizeCache() = default;
578+
579+
QSize QgsImageSizeCache::originalSize( const QString &f, bool blocking )
580+
{
581+
QString file = f.trimmed();
582+
583+
if ( file.isEmpty() )
584+
return QSize();
585+
586+
const QMutexLocker locker( &mMutex );
587+
588+
QString base64String;
589+
QString mimeType;
590+
if ( parseBase64DataUrl( file, &mimeType, &base64String ) && mimeType.startsWith( QLatin1String( "image/" ) ) )
591+
{
592+
file = QStringLiteral( "base64:%1" ).arg( base64String );
593+
}
594+
595+
QgsImageSizeCacheEntry *currentEntry = findExistingEntry( new QgsImageSizeCacheEntry( file ) );
596+
597+
QSize result;
598+
599+
if ( !currentEntry->size.isValid() )
600+
{
601+
result = QgsApplication::imageCache()->originalSizePrivate( file, blocking );
602+
mTotalSize += currentEntry->dataSize();
603+
currentEntry->size = result;
604+
trimToMaximumSize();
605+
}
606+
else
607+
{
608+
result = currentEntry->size;
609+
}
610+
611+
return result;
612+
}
613+
614+
///@endcond

src/core/qgsimagecache.h

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,54 @@ class QTemporaryDir;
3232

3333
///@cond PRIVATE
3434

35+
/**
36+
* \ingroup core
37+
* \class QgsImageSizeCacheEntry
38+
* \brief An entry for a QgsImageSizeCache, representing the original size of a single raster image
39+
* \since QGIS 3.42
40+
*/
41+
class CORE_EXPORT QgsImageSizeCacheEntry : public QgsAbstractContentCacheEntry
42+
{
43+
public:
44+
45+
/**
46+
* Constructor for QgsImageSizeCacheEntry, corresponding to the specified image \a path.
47+
*/
48+
QgsImageSizeCacheEntry( const QString &path ) ;
49+
50+
//! Original image size
51+
QSize size;
52+
53+
int dataSize() const override;
54+
void dump() const override;
55+
bool isEqual( const QgsAbstractContentCacheEntry *other ) const override;
56+
57+
};
58+
59+
/**
60+
* \class QgsImageSizeCache
61+
* \ingroup core
62+
* \brief A cache for original image sizes.
63+
*
64+
* QgsImageSizeCache stores the original sizes of raster image files, allowing efficient
65+
* reuse without incurring the cost of re-opening and parsing the image on every render.
66+
*
67+
* This is a private class, used internally in QgsImageCache.
68+
*
69+
* \since QGIS 3.42
70+
*/
71+
class CORE_EXPORT QgsImageSizeCache : public QgsAbstractContentCache< QgsImageSizeCacheEntry >
72+
{
73+
Q_OBJECT
74+
75+
public:
76+
77+
QgsImageSizeCache( QObject *parent SIP_TRANSFERTHIS = nullptr );
78+
~QgsImageSizeCache() override;
79+
long maximumSize() const { return mMaxCacheSize; }
80+
QSize originalSize( const QString &path, bool blocking = false );
81+
};
82+
3583
/**
3684
* \ingroup core
3785
* \class QgsImageCacheEntry
@@ -256,6 +304,8 @@ class CORE_EXPORT QgsImageCache : public QgsAbstractContentCache< QgsImageCacheE
256304

257305
static QImage getFrameFromReader( QImageReader &reader, int frameNumber );
258306

307+
QSize originalSizePrivate( const QString &path, bool blocking = false ) const;
308+
259309
//! SVG content to be rendered if SVG file was not found.
260310
QByteArray mMissingSvg;
261311

@@ -266,6 +316,9 @@ class CORE_EXPORT QgsImageCache : public QgsAbstractContentCache< QgsImageCacheE
266316
QMap< QString, int > mTotalFrameCounts;
267317
QMap< QString, QVector< int > > mImageDelays;
268318

319+
mutable QgsImageSizeCache mImageSizeCache;
320+
321+
friend class QgsImageSizeCache;
269322
friend class TestQgsImageCache;
270323
};
271324

src/core/symbology/qgsmarkersymbollayer.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3131,7 +3131,7 @@ bool QgsRasterMarkerSymbolLayer::setPreservedAspectRatio( bool par )
31313131

31323132
double QgsRasterMarkerSymbolLayer::updateDefaultAspectRatio()
31333133
{
3134-
if ( mDefaultAspectRatio == 0.0 )
3134+
if ( mDefaultAspectRatio == 0.0 && !mPath.isEmpty() )
31353135
{
31363136
const QSize size = QgsApplication::imageCache()->originalSize( mPath );
31373137
mDefaultAspectRatio = ( !size.isNull() && size.isValid() && size.width() > 0 ) ? static_cast< double >( size.height() ) / static_cast< double >( size.width() ) : 0.0;
@@ -3380,7 +3380,12 @@ QVariantMap QgsRasterMarkerSymbolLayer::properties() const
33803380

33813381
QgsRasterMarkerSymbolLayer *QgsRasterMarkerSymbolLayer::clone() const
33823382
{
3383-
auto m = std::make_unique< QgsRasterMarkerSymbolLayer >( mPath, mSize, mAngle );
3383+
auto m = std::make_unique< QgsRasterMarkerSymbolLayer >();
3384+
m->mPath = mPath;
3385+
m->mDefaultAspectRatio = mDefaultAspectRatio;
3386+
m->mSize = mSize;
3387+
m->mAngle = mAngle;
3388+
// other members are copied by:
33843389
copyCommonProperties( m.get() );
33853390
return m.release();
33863391
}

0 commit comments

Comments
 (0)