From ecb76dc69f69ffe33a92327cf128d7ee47423eb5 Mon Sep 17 00:00:00 2001 From: zhangkun Date: Thu, 7 May 2026 10:11:21 +0800 Subject: [PATCH] fix(wallpapercache): use QImageReader scaled decoding to avoid OOM on large images MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Replace QImage::load() with QImageReader in pixmix effect processing, cap decoded resolution at 8K (7680px) to prevent excessive memory usage 2. Use QImageReader::setScaledSize() for scaleImage thread so decoding outputs directly at target size instead of full-size then downscale 3. Use QImageReader::imageFormat() static method for format detection to avoid instantiating a full reader when only the format is needed 4. Add proper error handling with canRead/isEmpty checks and error strings Log: Optimize wallpaper image decoding to use scaled reading and prevent OOM on large images fix(wallpapercache): 使用 QImageReader 缩放解码避免大图内存溢出 1. 将 pixmix 特效处理中的 QImage::load() 替换为 QImageReader, 限制解码分辨率上限为 8K (7680px),防止内存占用过大 2. 缩放线程使用 QImageReader::setScaledSize() 在解码时直接输出目标尺寸, 避免先全尺寸加载再缩放带来的额外内存开销 3. 格式检测改用 QImageReader::imageFormat() 静态方法, 避免实例化完整 reader 造成不必要的图片加载 4. 增加完善的错误检查,包含 canRead/isEmpty 校验及错误信息输出 Log: 优化壁纸图片解码流程,采用缩放读取方式防止超大图片导致内存溢出 PMS: BUG-359341 --- .../wallpapercache/imageeffectprocessor.cpp | 23 +++++++++-- .../wallpapercache/scaleimagethread.cpp | 39 +++++++++++++------ .../wallpapercache/wallpapercacheservice.cpp | 12 ++++-- 3 files changed, 56 insertions(+), 18 deletions(-) diff --git a/src/plugin-qt/wallpapercache/imageeffectprocessor.cpp b/src/plugin-qt/wallpapercache/imageeffectprocessor.cpp index 849511f2..46d1b9f5 100644 --- a/src/plugin-qt/wallpapercache/imageeffectprocessor.cpp +++ b/src/plugin-qt/wallpapercache/imageeffectprocessor.cpp @@ -17,6 +17,7 @@ #define PIXMIX_OPACITY 90 // Color opacity #define PIXMIX_SATURATION 50 // Saturation #define PIXMIX_BRIGHTNESS -60 // Brightness +#define PIXMIX_MAX_DIM 7680 // 8K UHD long side cap DGUI_USE_NAMESPACE @@ -67,9 +68,25 @@ ImageEffectProcessor::EffectType ImageEffectProcessor::effectTypeFromString(cons QImage ImageEffectProcessor::processPixmixEffect(const QString &imagePath) { - QImage originalImage; - if (!originalImage.load(imagePath)) { - qWarning() << "Failed to load image:" << imagePath; + QImageReader reader(imagePath); + if (!reader.canRead()) { + qWarning() << "Cannot read image:" << imagePath; + return QImage(); + } + + QSize originalSize = reader.size(); + if (!originalSize.isEmpty() + && (originalSize.width() > PIXMIX_MAX_DIM || originalSize.height() > PIXMIX_MAX_DIM)) { + QSize decodeSize = originalSize; + decodeSize.scale(PIXMIX_MAX_DIM, PIXMIX_MAX_DIM, Qt::KeepAspectRatio); + reader.setScaledSize(decodeSize); + qDebug() << "Decoding image at" << decodeSize + << "(original:" << originalSize << ")"; + } + + QImage originalImage = reader.read(); + if (originalImage.isNull()) { + qWarning() << "Failed to load image:" << imagePath << reader.errorString(); return QImage(); } diff --git a/src/plugin-qt/wallpapercache/scaleimagethread.cpp b/src/plugin-qt/wallpapercache/scaleimagethread.cpp index 5f3af0f9..f6089d6a 100644 --- a/src/plugin-qt/wallpapercache/scaleimagethread.cpp +++ b/src/plugin-qt/wallpapercache/scaleimagethread.cpp @@ -10,6 +10,7 @@ #include #include #include +#include ScaleImageThread::ScaleImageThread(QObject *parent) : QThread(parent) @@ -142,21 +143,37 @@ void ScaleImageThread::executeTask(const TaskData &task) QImage ScaleImageThread::scaleImage(const TaskData &task) { qDebug() << "scale image info:" << task.originalPath << " targetSize:" << task.targetSize; - QImage image; - image.load(task.originalPath); + + QImageReader reader(task.originalPath); + if (!reader.canRead()) { + qWarning() << "Cannot read image:" << task.originalPath; + return QImage(); + } + + QSize originalSize = reader.size(); + if (originalSize.isEmpty()) { + qWarning() << "Cannot get image size:" << task.originalPath; + return QImage(); + } + + QSize decodeSize = originalSize; + decodeSize.scale(task.targetSize, Qt::KeepAspectRatioByExpanding); + reader.setScaledSize(decodeSize); + + QImage image = reader.read(); if (image.isNull()) { - qWarning() << "load image failed:" << task.originalPath; + qWarning() << "load image failed:" << task.originalPath << reader.errorString(); return image; } - QSize size = task.targetSize; - image = image.scaled(size, Qt::KeepAspectRatioByExpanding, Qt::FastTransformation); - image = image.copy(QRect((image.width() - size.width()) / 2, - (image.height() - size.height()) / 2, - size.width(), - size.height())); - - return image; + const QSize &size = task.targetSize; + if (image.width() < size.width() || image.height() < size.height()) { + return image; + } + return image.copy(QRect((image.width() - size.width()) / 2, + (image.height() - size.height()) / 2, + size.width(), + size.height())); } QString ScaleImageThread::cacheImageToDisk(QImage &image, const TaskData &task, const QString &md5String) diff --git a/src/plugin-qt/wallpapercache/wallpapercacheservice.cpp b/src/plugin-qt/wallpapercache/wallpapercacheservice.cpp index 5178b0a1..67dd7f3d 100644 --- a/src/plugin-qt/wallpapercache/wallpapercacheservice.cpp +++ b/src/plugin-qt/wallpapercache/wallpapercacheservice.cpp @@ -201,10 +201,14 @@ QString WallpaperCacheService::saveImageFromFd(const QDBusUnixFileDescriptor &fd } originFile.close(); - // Detect format and rename with extension - QImageReader reader(originPath); - QString format = reader.format(); - QString destinationPath = originPath + QString(".%1").arg(format); + // Detect format using static method to avoid loading entire image + QByteArray format = QImageReader::imageFormat(originPath); + if (format.isEmpty()) { + qWarning() << "Failed to detect image format:" << originPath; + QFile::remove(originPath); + return QString(); + } + QString destinationPath = originPath + QString(".%1").arg(QString::fromLatin1(format)); QFile file(originPath); if (!file.rename(destinationPath)) { qWarning() << "originPath rename failed";