feat(DrawUtils): add drawInsetShadow for inner shadow rendering - #786
Conversation
Reviewer's GuideAdds DDrawUtils::drawInsetShadow to provide QWidget inner-shadow rendering compatible with QML BoxInsetShadow, using scaled offscreen image compositing, alpha blur, offset handling, and rounded-rectangle clipping. Sequence diagram for inset shadow renderingsequenceDiagram
participant Caller
participant DDrawUtils
participant QImage
participant QPainter
participant qt_blurImage
Caller->>DDrawUtils: drawInsetShadow(pa, rect, xRadius, yRadius, sc, radius, offset)
DDrawUtils->>QImage: Create opaque shadow_base
DDrawUtils->>QPainter: CompositionMode_Clear
QPainter->>QImage: Cut offset rounded-rect hole
DDrawUtils->>qt_blurImage: Blur shadow_base alpha
qt_blurImage-->>DDrawUtils: Return blurred image
DDrawUtils->>QPainter: CompositionMode_SourceIn
QPainter->>QImage: Apply shadow color sc
DDrawUtils->>QPainter: Clip rounded rect
QPainter->>Caller: Draw inset shadow image
Flow diagram for inset shadow compositingflowchart LR
A[Create opaque black image] --> B[Clear offset rounded-rect hole]
B --> C[Blur alpha with qt_blurImage]
C --> D[Replace black with shadow color]
D --> E[Clip to rounded rectangle]
E --> F[Draw image into target rectangle]
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
Hi @zqq-dora. Thanks for your PR. I'm waiting for a linuxdeepin member to verify that this patch is reasonable to test. If it is, they should reply with Once the patch is verified, the new status will be reflected by the I understand the commands that are listed here. DetailsInstructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. |
There was a problem hiding this comment.
Hey - I've found 2 issues
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="src/widgets/dstyle.cpp" line_range="411-446" />
<code_context>
+ QImage shadow_base(size, QImage::Format_ARGB32_Premultiplied);
</code_context>
<issue_to_address>
**issue (performance):** `drawInsetShadow` allocates two full-size images and runs `qt_blurImage` on every invocation, with no cache equivalent to the existing `drawShadow` implementation. Repainting a widget repeatedly, such as during hover or animation, therefore performs a full image allocation and Gaussian blur on every paint event.
**Triggers:** When a control using the new helper is repainted frequently or has a large rectangle.
**Suggested fix:** Cache the generated shadow by size, radii, color, blur radius, offset, and device-pixel ratio, or reuse a precomputed mask while only applying the current color.
</issue_to_address>
### Comment 2
<location path="src/widgets/dstyle.cpp" line_range="405" />
<code_context>
+ QSize size = rect.size() * scale;
+ xRadius *= scale;
+ yRadius *= scale;
+ radius *= scale;
+ QPoint scaledOffset(offset.x() * scale, offset.y() * scale);
+
+ // Build an opaque image, then cut out a rounded-rect hole shifted by
</code_context>
<issue_to_address>
**issue (bug_risk):** `scaledOffset` converts the scaled floating-point offset directly to `QPoint`, truncating fractional device-pixel values. On fractional device-pixel-ratio displays, an offset such as one logical pixel at 1.5x is rendered as one physical pixel instead of 1.5 physical pixels, producing a systematically incorrect inset-shadow offset.
**Triggers:** When the painter's device pixel ratio is fractional and `offset` is nonzero.
**Suggested fix:** Use a floating-point offset for the geometry or explicitly round the scaled values with the same policy used for other device-pixel geometry.
```suggestion
QPointF scaledOffset(offset.x() * scale, offset.y() * scale);
```
</issue_to_address>Log: 在 DDrawUtils 中新增 drawInsetShadow(),用于在 QWidget 控件上绘制内阴影,与 dtkdeclarative 的 BoxInsetShadow 视觉效果保持一致
cc7d18b to
fa86ca1
Compare
| pa->drawPixmap(shadow_rect, shadow); | ||
| } | ||
|
|
||
| void drawInsetShadow(QPainter *pa, const QRect &rect, qreal xRadius, qreal yRadius, const QColor &sc, qreal radius, const QPoint &offset) |
There was a problem hiding this comment.
可以仿照drawShadow的方式去实现,分开具体的insetShaodw的实现与导出的接口,添加缓存
There was a problem hiding this comment.
收到,按建议重构,计划如下:
- 拆分核心实现与导出接口:抽一个内部 static 函数负责生成内阴影图
(挖孔蒙版 + qt_blurImage + 颜色合成),drawInsetShadow 作为公开接口
只负责取图、按圆角裁剪、绘制。 - 加缓存:用 QPixmapCache 缓存生成的内阴影图,key 含尺寸/圆角/模糊半径/
offset/DPR。需要说明一点——内阴影与外阴影几何不同:外阴影圆角在 base
外四角,可用 borderImage 九宫格拉伸、缓存不含尺寸的小图;内阴影圆角在
"孔"的内四角、渐变也在孔边缘,九宫格拉伸会让圆角和渐变变形,故按整图
(带尺寸)缓存,hover/动画同尺寸即可命中。形式与 drawShadow 略有差异,
但同样满足"分层 + 缓存"。 - 顺手把 scaledOffset 由 QPoint 改为 QPointF,修正小数 DPR 下的偏移截断。
There was a problem hiding this comment.
已按建议重构完成(新提交 a4ebf02):
-
拆分实现与接口:抽出 static createInsetShadow() 负责生成内阴影图(挖孔蒙版 + qt_blurImage + 颜色合成),drawInsetShadow() 作为公开接口只做缓存查找、裁剪、绘制,与 drawShadow / dropShadow / borderImage 的分层方式一致。
-
添加缓存:用 QPixmapCache 缓存生成的内阴影图,key 含 尺寸/圆角/模糊半径/颜色+alpha/offset/DPR,hover、动画同尺寸重绘时命中缓存,不再每次 paint 重新分配和模糊。
补充一点:内阴影的圆角和内凹渐变都在"孔"的内边缘,无法像外阴影那样用 borderImage 九宫格拉伸复用小图(中间 tile 拉伸会变形),所以缓存按整图、key 带尺寸,与 drawShadow 缓存小 base 图的形式略有差异,但同样满足"分层 + 缓存"的思路。
- 顺带把 scaledOffset 从 QPoint 改为 QPointF,修正小数 DPR 下的偏移截断。
Separate the core image generation into static createInsetShadow(), keep drawInsetShadow() as the public interface doing cache lookup, clip, and draw - mirroring the drawShadow / dropShadow / borderImage layering. Cache the generated shadow with QPixmapCache keyed by size, radii, blur radius, color+alpha, offset and DPR, so hover/animation repaints of the same shape reuse the precomputed image instead of reallocating and re-blurring every paint. Also fix scaledOffset type from QPoint to QPointF to avoid truncating fractional device-pixel values on fractional-DPR displays.
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: 18202781743, zqq-dora The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
56f1571
into
linuxdeepin:v25-flowstyle
Summary
Add
DDrawUtils::drawInsetShadow()to dtkwidget, parallel to the existingdrawShadow(), for rendering inset (inner) shadows on QWidget controls. This mirrors the QMLBoxInsetShadowcomponent in dtkdeclarative, enabling consistent inset shadow effects across both QWidget and QML frameworks.The implementation uses
qt_blurImage(same asdrawShadow) to blur a hole-punched alpha mask, then composites the shadow color and clips to the rounded-rect shape.Implementation
offsetusingCompositionMode_Clear.qt_blurImage(same function asdrawShadow).CompositionMode_SourceIn.Mapping to QML BoxInsetShadow
cornerRadiusxRadius/yRadiusshadowColorscshadowOffsetY: 1(top highlight)offset=(0, 1)shadowOffsetY: -1(bottom dark edge)offset=(0, -1)shadowBlurradiusFiles
include/widgets/dstyle.hdrawInsetShadowdeclarationsrc/widgets/dstyle.cppdrawInsetShadowimplementation概述(中文)
在 dtkwidget 的
DDrawUtils命名空间中新增drawInsetShadow()函数,与现有的drawShadow()接口风格平行,用于在 QWidget 控件上绘制内阴影。与 dtkdeclarative 的 QMLBoxInsetShadow组件对应,使 QWidget 和 QML 两端视觉一致。实现使用
qt_blurImage(与drawShadow同一函数)对挖孔后的 alpha 蒙版做高斯模糊,再合成阴影颜色并裁剪到圆角矩形形状。实现原理
CompositionMode_Clear挖出按 offset 偏移的圆角矩形孔。qt_blurImage对 alpha 通道做高斯模糊(与drawShadow使用同一函数)。CompositionMode_SourceIn将占位黑色替换为实际阴影颜色。与 QML BoxInsetShadow 的对应
cornerRadiusxRadius/yRadiusshadowColorscshadowOffsetY: 1(顶部高光)offset=(0, 1)shadowOffsetY: -1(底部暗角)offset=(0, -1)shadowBlurradius修改文件
include/widgets/dstyle.hdrawInsetShadow声明src/widgets/dstyle.cppdrawInsetShadow实现Summary by Sourcery
Add a reusable inset-shadow drawing API for consistent inner shadow effects across QWidget controls.
New Features:
DDrawUtils::drawInsetShadow()for rendering cached, rounded-rectangle inner shadows in QWidget controls.Enhancements:
Chores: