Skip to content

fix: support scaling for local file icons#568

Open
qxp930712 wants to merge 1 commit intolinuxdeepin:masterfrom
qxp930712:master
Open

fix: support scaling for local file icons#568
qxp930712 wants to merge 1 commit intolinuxdeepin:masterfrom
qxp930712:master

Conversation

@qxp930712
Copy link

@qxp930712 qxp930712 commented Feb 6, 2026

  1. Add updateFileUrlImage method to handle FileUrl type icons
  2. Load local file image and apply manual scaling based on
    devicePixelRatio
  3. Use Qt::SmoothTransformation for quality scaling
  4. Modify setName to delegate FileUrl loading to maybeUpdateUrl logic
  5. This ensures local file icons scale correctly for high DPI displays

Influence:

  1. Test loading icons from local file paths on standard and high DPI
    screens
  2. Verify image rendering quality and sharpness after scaling
  3. Test behavior when sourceSize is explicitly specified vs default
  4. Verify that Base64 and Theme icons still render correctly
  5. Check for performance impact with large local image files

PMS: BUG-333731

Summary by Sourcery

Add dedicated handling for local PNG icon files to support high-quality, devicePixelRatio-aware scaling in DQuickIconImage.

New Features:

  • Introduce a FilePng icon type to distinguish local PNG files from generic file URLs.
  • Add a PNG-specific image update path that scales icons according to sourceSize and devicePixelRatio for smoother rendering.

Enhancements:

  • Route local .png paths through the unified maybeUpdateUrl flow instead of setting the source directly to ensure consistent scaling behavior.

@sourcery-ai
Copy link

sourcery-ai bot commented Feb 6, 2026

Reviewer's Guide

Adds explicit handling and high-DPI scaling for local PNG icon files by introducing a new FilePng icon type, routing PNG paths through a custom loader, and scaling images according to devicePixelRatio with smooth transformation before setting them on the QQuickImage.

Sequence diagram for PNG icon loading and high-DPI scaling

sequenceDiagram
    actor Client
    participant DQuickIconImage as DQuickIconImage
    participant DQuickIconImagePrivate as DQuickIconImagePrivate
    participant QQmlFile as QQmlFile
    participant QUrl as QUrl
    participant QImage as QImage

    Client->>DQuickIconImage: setName(name)
    DQuickIconImage->>QQmlFile: isLocalFile(name)
    QQmlFile-->>DQuickIconImage: bool
    alt name is local file
        DQuickIconImage->>QUrl: QUrl(name)
        QUrl-->>DQuickIconImage: url
        DQuickIconImage->>QUrl: toLocalFile()
        QUrl-->>DQuickIconImage: localFile
        DQuickIconImage->>DQuickIconImagePrivate: set iconType = FilePng (if localFile endsWith .png)
        DQuickIconImage->>DQuickIconImagePrivate: maybeUpdateUrl()
        DQuickIconImagePrivate->>DQuickIconImagePrivate: check iconType
        alt iconType is FilePng
            DQuickIconImagePrivate->>DQuickIconImagePrivate: updatePngImage()
            DQuickIconImagePrivate->>QUrl: QUrl(name)
            QUrl-->>DQuickIconImagePrivate: url
            DQuickIconImagePrivate->>QUrl: toLocalFile()
            QUrl-->>DQuickIconImagePrivate: localFile
            DQuickIconImagePrivate->>QImage: QImage(localFile)
            QImage-->>DQuickIconImagePrivate: image
            DQuickIconImagePrivate->>DQuickIconImage: sourceSize()
            DQuickIconImage-->>DQuickIconImagePrivate: iconSize
            DQuickIconImagePrivate->>DQuickIconImagePrivate: scale image using iconSize * devicePixelRatio with SmoothTransformation
            DQuickIconImagePrivate->>DQuickIconImagePrivate: setImage(image)
        end
    end
Loading

Class diagram for updated DQuickIconImage and DQuickIconImagePrivate PNG handling

classDiagram
    class QQuickImagePrivate

    class DQuickIconImagePrivate {
        +bool updateDevicePixelRatio(targetDevicePixelRatio qreal)
        +void updateBase64Image()
        +void updatePngImage()
        +static QImage requestImageFromBase64(name QString, requestedSize QSize, devicePixelRatio qreal)
        +IconType iconType
        +qreal devicePixelRatio
        +void setImage(image QImage)
    }

    class DQuickIconImage {
        +void setName(name QString)
        +QSize sourceSize()
        +void setSource(url QUrl)
    }

    class IconType {
        <<enumeration>>
        ThemeIconName
        Base64Data
        FileUrl
        FilePng
    }

    QQuickImagePrivate <|-- DQuickIconImagePrivate
    DQuickIconImagePrivate --> IconType
    DQuickIconImage --> DQuickIconImagePrivate
Loading

Flow diagram for setName local file PNG vs non-PNG handling

flowchart TD
    A["setName(name)"] --> B["QQmlFile::isLocalFile(name)?"]
    B -->|no| Z["Other name handling (unchanged)"]
    B -->|yes| C["url = QUrl(name)"]
    C --> D["url.isValid()?"]
    D -->|no| Z
    D -->|yes| E["localFile = url.toLocalFile()"]
    E --> F["localFile.endsWith(.png, CaseInsensitive)?"]
    F -->|yes| G["iconType = FilePng"]
    F -->|no| H["iconType = FileUrl"]
    H --> I["setSource(url)"]
    G --> J["maybeUpdateUrl() triggers updatePngImage for scaling"]
Loading

File-Level Changes

Change Details Files
Introduce a dedicated PNG icon type and update the icon type state machine to recognize local PNG files.
  • Extend the IconType enum with a FilePng variant to represent local PNG files requiring special scaling handling.
  • Adjust setName to detect local PNG file paths (by extension) and set iconType to FilePng instead of FileUrl.
  • Ensure non-PNG local files continue to use the existing FileUrl path and still call setSource directly.
src/private/dquickiconimage_p_p.h
src/private/dquickiconimage.cpp
Add a custom PNG loading and scaling path that applies devicePixelRatio-aware scaling and smooth transformation before displaying the icon.
  • Implement updatePngImage to load a local PNG via QUrl/toLocalFile, determine effective icon size from sourceSize or the image’s intrinsic size, and scale it by devicePixelRatio with Qt::SmoothTransformation.
  • Wire updatePngImage into maybeUpdateUrl so that FilePng icons are updated via the new code path instead of the generic URL handling.
  • Keep base64 handling intact by leaving updateBase64Image and its invocation unchanged while adding the PNG branch alongside it.
src/private/dquickiconimage.cpp

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • In updatePngImage(), iconSize * devicePixelRatio will either not compile or silently truncate the scale factor depending on types; consider explicitly constructing a scaled QSize (e.g. using qRound on width/height) so that high‑DPI scaling is accurate and type‑safe.
  • The setName branch for non‑PNG local files still calls setSource(url) directly, which contradicts the new comment about routing through maybeUpdateUrl for unified scaling; either route all local files through maybeUpdateUrl or adjust the comment/logic for consistency.
  • When creating the QUrl from a local path in updatePngImage(), using QUrl::fromLocalFile (or avoiding round‑tripping through QUrl at all) would be more robust than constructing QUrl(name) and then calling toLocalFile(), which can return an empty path for some valid local inputs.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In updatePngImage(), `iconSize * devicePixelRatio` will either not compile or silently truncate the scale factor depending on types; consider explicitly constructing a scaled QSize (e.g. using qRound on width/height) so that high‑DPI scaling is accurate and type‑safe.
- The `setName` branch for non‑PNG local files still calls `setSource(url)` directly, which contradicts the new comment about routing through `maybeUpdateUrl` for unified scaling; either route all local files through `maybeUpdateUrl` or adjust the comment/logic for consistency.
- When creating the QUrl from a local path in updatePngImage(), using `QUrl::fromLocalFile` (or avoiding round‑tripping through QUrl at all) would be more robust than constructing QUrl(name) and then calling `toLocalFile()`, which can return an empty path for some valid local inputs.

## Individual Comments

### Comment 1
<location> `src/private/dquickiconimage.cpp:43` </location>
<code_context>
     setImage(image);
 }

+void DQuickIconImagePrivate::updatePngImage()
+{
+    Q_ASSERT(iconType == FilePng);
</code_context>

<issue_to_address>
**issue (complexity):** Consider unifying all local-file handling into a single code path and helper instead of adding a PNG-specific icon type and branches.

You can keep the new PNG scaling behavior but simplify control flow by unifying local-file handling instead of introducing `FilePng` and PNG-specific branches.

### 1. Replace `FilePng`/`FileUrl` split with a single local-file path

Instead of deciding in `setName` whether something is `FilePng` vs `FileUrl`, treat all local files as a single icon type (e.g. `FileUrl` / `LocalFile`) and push the format-specific logic into one helper.

```cpp
// in setName
d->iconType = DQuickIconImagePrivate::ThemeIconName;

if (name.startsWith("data:image/")) {
    d->iconType = DQuickIconImagePrivate::Base64Data;
} else if (QQmlFile::isLocalFile(name)) {
    QUrl url(name);
    if (url.isValid()) {
        d->iconType = DQuickIconImagePrivate::FileUrl; // single local-file type
        // don't setSource here; let maybeUpdateUrl handle scaling / loading
    }
}

if (isComponentComplete()) {
    d->maybeUpdateUrl();
}
```

### 2. Centralize local-file loading (including PNG scaling) in one helper

You can fold `updatePngImage` into a more generic local-file helper and reuse it for all local files:

```cpp
void DQuickIconImagePrivate::updateLocalFileImage()
{
    Q_ASSERT(iconType == FileUrl);

    D_Q(DQuickIconImage);

    QUrl url(name);
    if (!url.isValid() || !url.isLocalFile())
        return;

    const QString localFile = url.toLocalFile();

    if (localFile.endsWith(".png", Qt::CaseInsensitive)) {
        QImage image(localFile);
        if (image.isNull())
            return;

        QSize iconSize = q->sourceSize();
        if (iconSize.isEmpty())
            iconSize = image.size();

        image = image.scaled(iconSize * devicePixelRatio,
                             Qt::KeepAspectRatio,
                             Qt::SmoothTransformation);
        setImage(image);
    } else {
        // non-PNG local files keep existing behavior
        q->setSource(url);
    }
}
```

### 3. Simplify `maybeUpdateUrl` branching

With the above, `maybeUpdateUrl` doesn’t need a `FilePng` case and stays stable as more formats are added:

```cpp
void DQuickIconImagePrivate::maybeUpdateUrl()
{
    D_Q(DQuickIconImage);

    // non-theme icons
    if (iconType != ThemeIconName) {
        if (iconType == Base64Data)
            updateBase64Image();
        else if (iconType == FileUrl)
            updateLocalFileImage();
        return;
    }

    // existing ThemeIconName handling...
}
```

This keeps all the new PNG scaling behavior, but:

- Removes the PNG-specific enum value and extension check from `setName`.
- Centralizes local-file behavior (including format-specific handling) in one place.
- Keeps `maybeUpdateUrl` from growing more `if/else` branches per file type.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

iconSize = image.size();
}
// 应用devicePixelRatio进行缩放
image = image.scaled(iconSize * devicePixelRatio, Qt::KeepAspectRatio, Qt::SmoothTransformation);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QImage image(icon_size * devicePixelRatio, QImage::Format_ARGB32);

是不是走的这里呀,

@deepin-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: qxp930712

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

1. Add updateFileUrlImage method to handle FileUrl type icons
2. Load local file image and apply manual scaling based on
devicePixelRatio
3. Use Qt::SmoothTransformation for quality scaling
4. Modify setName to delegate FileUrl loading to maybeUpdateUrl logic
5. This ensures local file icons scale correctly for high DPI displays

Influence:
1. Test loading icons from local file paths on standard and high DPI
screens
2. Verify image rendering quality and sharpness after scaling
3. Test behavior when sourceSize is explicitly specified vs default
4. Verify that Base64 and Theme icons still render correctly
5. Check for performance impact with large local image files

PMS: BUG-333731
@deepin-ci-robot
Copy link
Contributor

deepin pr auto review

这份代码 diff 主要实现了对本地文件 URL 类型图标的自定义加载逻辑,以便支持设备像素比(devicePixelRatio)的缩放处理,而不是直接使用 QQuickImage 的默认 source 加载机制。

以下是对这段代码的审查意见,包括语法逻辑、代码质量、代码性能和代码安全四个方面:

1. 语法逻辑

  • 通过性:代码语法正确,符合 Qt/C++ 编码规范,逻辑流程清晰。
  • Q_ASSERT 的使用
    • Q_ASSERT(iconType == FileUrl);updateFileUrlImage 中是合理的,因为调用方(maybeUpdateUrl)已经确保了类型匹配。
  • 逻辑一致性
    • setName 中,当 iconType 被设置为 FileUrl 后,移除了 setSource(url)。这符合注释中的意图,即通过 maybeUpdateUrl 统一处理。
    • maybeUpdateUrl 中增加了对 FileUrl 的分支调用 updateFileUrlImage,逻辑闭环完整。

2. 代码质量

  • 可读性:代码结构清晰,变量命名(如 iconSize, devicePixelRatio)准确表达了意图。注释说明了修改原因(支持缩放),这很好。
  • 错误处理与健壮性
    • 潜在问题:在 updateFileUrlImage 中,如果 url.isValid() 为真,但 QImage(url.toLocalFile()) 加载失败(例如文件损坏、格式不支持或路径实际上是网络路径而非本地路径),代码仅通过 if (!image.isNull()) 跳过设置。
      • 建议:虽然当前逻辑不会导致崩溃,但在加载失败时没有任何提示(如 qWarning),这可能会增加调试难度。建议在加载失败时输出日志。
  • 代码复用
    • updateBase64ImageupdateFileUrlImage 都有类似的逻辑:加载图片 -> 检查 sourceSize -> 缩放 -> setImage
    • 建议:虽然目前只有两个分支,但可以考虑抽取公共的缩放和设置逻辑,减少重复代码。不过考虑到 Base64 和 File 的加载方式差异较大,当前的写法也是可以接受的。

3. 代码性能

  • 同步 I/O 操作
    • QImage(url.toLocalFile()) 是一个同步文件读取操作。如果图片文件非常大,或者磁盘 I/O 繁忙,这可能会导致主线程(UI 线程)阻塞,造成界面卡顿。
    • 建议:Qt Quick 的 Image 组件通常建议使用异步加载。虽然这里为了控制缩放逻辑选择了手动加载,但需要注意不要在 UI 密集操作时频繁调用此函数加载大图。
  • 图片缩放
    • image.scaled(..., Qt::SmoothTransformation) 涉及到像素重采样,对于大图来说是一个 CPU 密集型操作。
    • 建议:确认 iconSize 是否合理。如果 sourceSize 未设置且原图非常大,缩放操作会很耗时。当前的逻辑 if (iconSize.isEmpty()) iconSize = image.size(); 意味着如果未指定大小,它会根据 devicePixelRatio 放大原图。例如原图 100x100,dpr=2,缩放到 200x200。这是正确的逻辑,但需注意性能消耗。

4. 代码安全

  • 路径安全
    • 代码使用了 url.toLocalFile()setName 中通过 QQmlFile::isLocalFile(name) 进行了前置检查,这能较好地过滤掉非本地文件路径,降低了注入非预期路径的风险。
  • 资源释放
    • QImage 处理得当,Qt 的隐式共享机制保证了这里的赋值和传参是安全的,不会造成内存泄漏或悬垂指针。
  • 空指针与崩溃风险
    • D_Q(DQuickIconImage) 宏用于获取 q 指针,这是 Qt P-Impl 模式的标准用法,是安全的。

改进建议总结

  1. 增加日志输出:在 updateFileUrlImage 中,当 image.isNull() 为真时,建议输出 qWarning() << "Failed to load icon image:" << url.toLocalFile();,以便排查文件加载问题。
  2. 性能考量:由于使用了同步加载,请确保此函数不被高频调用,或者确保加载的图片资源体积较小。如果可能,考虑是否可以在后台线程进行解码(虽然这会增加与 QML 线程交互的复杂度)。

改进后的代码示例

void DQuickIconImagePrivate::updateFileUrlImage()
{
    Q_ASSERT(iconType == FileUrl);

    D_Q(DQuickIconImage);
    
    // 处理本地PNG文件
    QUrl url(name);
    if (url.isValid()) {
        QImage image(url.toLocalFile());
        if (!image.isNull()) {
            QSize iconSize = q->sourceSize();
            if (iconSize.isEmpty()) {
                iconSize = image.size();
            }
            // 应用devicePixelRatio进行缩放
            image = image.scaled(iconSize * devicePixelRatio, Qt::KeepAspectRatio, Qt::SmoothTransformation);
            setImage(image);
        } else {
            // 增加错误日志,便于调试
            qWarning() << "DQuickIconImage: Failed to load image from url:" << url << "Local file:" << url.toLocalFile();
        }
    }
}

@18202781743 18202781743 changed the title feat: add scaling support for PNG icons fix: support scaling for local file icons Feb 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants