28 #include <QApplication>
29 #include <QCoreApplication>
31 #include <QDomDocument>
32 #include <QDomElement>
38 #include <QNetworkReply>
39 #include <QNetworkRequest>
41 #include <QImageReader>
42 #include <QSvgRenderer>
46 QgsImageCacheEntry::QgsImageCacheEntry(
const QString &path, QSize size,
const bool keepAspectRatio,
const double opacity,
double dpi )
49 , keepAspectRatio( keepAspectRatio )
57 const QgsImageCacheEntry *otherImage =
dynamic_cast< const QgsImageCacheEntry *
>( other );
59 if ( !otherImage || otherImage->keepAspectRatio != keepAspectRatio || otherImage->size != size || ( !size.isValid() && otherImage->targetDpi != targetDpi ) || otherImage->opacity != opacity || otherImage->path != path )
65 int QgsImageCacheEntry::dataSize()
const
68 if ( !image.isNull() )
70 size += image.sizeInBytes();
75 void QgsImageCacheEntry::dump()
const
77 QgsDebugMsgLevel( QStringLiteral(
"path: %1, size %2x%3" ).arg( path ).arg( size.width() ).arg( size.height() ), 3 );
82 static const int DEFAULT_IMAGE_CACHE_MAX_BYTES = 104857600;
85 :
QgsAbstractContentCache< QgsImageCacheEntry >( parent, QObject::tr(
"Image" ), DEFAULT_IMAGE_CACHE_MAX_BYTES )
87 int bytes =
QgsSettings().
value( QStringLiteral(
"/qgis/maxImageCacheSize" ), 0 ).toInt();
93 mMissingSvg = QStringLiteral(
"<svg width='10' height='10'><text x='5' y='10' font-size='10' text-anchor='middle'>?</text></svg>" ).toLatin1();
96 if ( QFile::exists( downloadingSvgPath ) )
98 QFile file( downloadingSvgPath );
99 if ( file.open( QIODevice::ReadOnly ) )
101 mFetchingSvg = file.readAll();
105 if ( mFetchingSvg.isEmpty() )
107 mFetchingSvg = QStringLiteral(
"<svg width='10' height='10'><text x='5' y='10' font-size='10' text-anchor='middle'>?</text></svg>" ).toLatin1();
113 QImage
QgsImageCache::pathAsImage(
const QString &f,
const QSize size,
const bool keepAspectRatio,
const double opacity,
bool &fitsInCache,
bool blocking,
double targetDpi,
bool *isMissing )
115 const QString file = f.trimmed();
119 if ( file.isEmpty() )
122 const QMutexLocker locker( &
mMutex );
126 QgsImageCacheEntry *currentEntry =
findExistingEntry(
new QgsImageCacheEntry( file, size, keepAspectRatio, opacity, targetDpi ) );
133 if ( currentEntry->image.isNull() )
135 long cachedDataSize = 0;
136 bool isBroken =
false;
137 result = renderImage( file, size, keepAspectRatio, opacity, targetDpi, isBroken, blocking );
138 cachedDataSize += result.sizeInBytes();
142 currentEntry->image = QImage();
147 currentEntry->image = result;
151 *isMissing = isBroken;
152 currentEntry->isMissingImage = isBroken;
158 result = currentEntry->image;
160 *isMissing = currentEntry->isMissingImage;
168 if ( path.isEmpty() )
172 if ( !path.startsWith( QLatin1String(
"base64:" ) ) && QFile::exists( path ) )
174 const QImageReader reader( path );
175 if ( reader.size().isValid() )
176 return reader.size();
178 return QImage( path ).size();
182 QByteArray ba =
getContent( path, QByteArray(
"broken" ), QByteArray(
"fetching" ), blocking );
184 if ( ba !=
"broken" && ba !=
"fetching" )
186 QBuffer buffer( &ba );
187 buffer.open( QIODevice::ReadOnly );
189 QImageReader reader( &buffer );
192 const QSize s = reader.size();
195 const QImage im = reader.read();
196 return im.isNull() ? QSize() : im.size();
202 QImage QgsImageCache::renderImage(
const QString &path, QSize size,
const bool keepAspectRatio,
const double opacity,
double targetDpi,
bool &isBroken,
bool blocking )
const
208 if ( !path.startsWith( QLatin1String(
"base64:" ) ) && QFile::exists( path ) )
210 QImageReader reader( path );
211 reader.setAutoTransform(
true );
213 if ( reader.format() ==
"pdf" )
215 if ( !size.isEmpty() )
222 reader.setScaledSize( size );
227 const QSize sizeAt72Dpi = reader.size();
228 const QSize sizeAtTargetDpi = sizeAt72Dpi * targetDpi / 72;
229 reader.setScaledSize( sizeAtTargetDpi );
237 QByteArray ba =
getContent( path, QByteArray(
"broken" ), QByteArray(
"fetching" ), blocking );
239 if ( ba ==
"broken" )
244 if ( !size.isValid() )
248 if ( size.width() == 0 )
249 size.setWidth( size.height() );
250 if ( size.height() == 0 )
251 size.setHeight( size.width() );
253 im = QImage( size, QImage::Format_ARGB32_Premultiplied );
257 QSvgRenderer r( mMissingSvg );
259 QSizeF s( r.viewBox().size() );
260 s.scale( size.width(), size.height(), Qt::KeepAspectRatio );
261 const QRectF rect( ( size.width() - s.width() ) / 2, ( size.height() - s.height() ) / 2, s.width(), s.height() );
262 r.render( &p, rect );
264 else if ( ba ==
"fetching" )
267 if ( size.width() == 0 )
268 size.setWidth( size.height() );
269 if ( size.height() == 0 )
270 size.setHeight( size.width() );
273 im = QImage( size, QImage::Format_ARGB32_Premultiplied );
277 QSvgRenderer r( mFetchingSvg );
279 QSizeF s( r.viewBox().size() );
280 s.scale( size.width(), size.height(), Qt::KeepAspectRatio );
281 const QRectF rect( ( size.width() - s.width() ) / 2, ( size.height() - s.height() ) / 2, s.width(), s.height() );
282 r.render( &p, rect );
286 QBuffer buffer( &ba );
287 buffer.open( QIODevice::ReadOnly );
289 QImageReader reader( &buffer );
290 reader.setAutoTransform(
true );
292 if ( reader.format() ==
"pdf" )
294 if ( !size.isEmpty() )
301 reader.setScaledSize( size );
306 const QSize sizeAt72Dpi = reader.size();
307 const QSize sizeAtTargetDpi = sizeAt72Dpi * targetDpi / 72;
308 reader.setScaledSize( sizeAtTargetDpi );
316 if ( !im.hasAlphaChannel() )
317 im = im.convertToFormat( QImage::Format_ARGB32 );
323 if ( !size.isValid() || size.isNull() || im.size() == size )
326 else if ( keepAspectRatio && size.height() == 0 )
327 return im.scaledToWidth( size.width(), Qt::SmoothTransformation );
329 else if ( keepAspectRatio && size.width() == 0 )
330 return im.scaledToHeight( size.height(), Qt::SmoothTransformation );
332 return im.scaled( size, keepAspectRatio ? Qt::KeepAspectRatio : Qt::IgnoreAspectRatio, Qt::SmoothTransformation );
void remoteContentFetched(const QString &url)
Emitted when the cache has finished retrieving content from a remote url.
Base class for entries in a QgsAbstractContentCache.
Abstract base class for file content caches, such as SVG or raster image caches.
QgsImageCacheEntry * findExistingEntry(QgsImageCacheEntry *entryTemplate)
Returns the existing entry from the cache which matches entryTemplate (deleting entryTemplate when do...
long mMaxCacheSize
Maximum cache size.
QByteArray getContent(const QString &path, const QByteArray &missingContent, const QByteArray &fetchingContent, bool blocking=false) const
Gets the file content corresponding to the given path.
long mTotalSize
Estimated total size of all cached content.
void trimToMaximumSize()
Removes the least used cache entries until the maximum cache size is under the predefined size limit.
static QString defaultThemePath()
Returns the path to the default theme directory.
QSize originalSize(const QString &path, bool blocking=false) const
Returns the original size (in pixels) of the image at the specified path.
QgsImageCache(QObject *parent=nullptr)
Constructor for QgsImageCache, with the specified parent object.
void remoteImageFetched(const QString &url)
Emitted when the cache has finished retrieving an image file from a remote url.
QImage pathAsImage(const QString &path, const QSize size, const bool keepAspectRatio, const double opacity, bool &fitsInCache, bool blocking=false, double targetDpi=96, bool *isMissing=nullptr)
Returns the specified path rendered as an image.
static void multiplyOpacity(QImage &image, double factor, QgsFeedback *feedback=nullptr)
Multiplies opacity of image pixel values by a factor.
This class is a composition of two QSettings instances:
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
#define QgsDebugMsgLevel(str, level)