Skip to content

Add checkerboard grid background support for pagx-viewer#3520

Closed
jinwuwu001 wants to merge 5 commits into
mainfrom
feature/billyjin_GridBackground
Closed

Add checkerboard grid background support for pagx-viewer#3520
jinwuwu001 wants to merge 5 commits into
mainfrom
feature/billyjin_GridBackground

Conversation

@jinwuwu001

@jinwuwu001 jinwuwu001 commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

在 PAGScene 中新增 setBackgroundLayer 接口,支持在 surface 坐标系下绘制自定义背景层(不受 zoomScale/contentOffset 影响)。pagx-viewer 利用此接口,在未设置自定义背景色时默认绘制棋盘格背景,与设置背景色互斥切换。

@codecov-commenter

codecov-commenter commented Jun 18, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 81.06%. Comparing base (1151b28) to head (e18a4b6).
⚠️ Report is 3 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #3520   +/-   ##
=======================================
  Coverage   81.06%   81.06%           
=======================================
  Files         626      626           
  Lines       71226    71310   +84     
  Branches    20826    20837   +11     
=======================================
+ Hits        57737    57811   +74     
- Misses       9352     9353    +1     
- Partials     4137     4146    +9     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment thread src/pagx/PAGScene.cpp
canvas->clear();
}
_backgroundLayer->draw(canvas);
displayList->render(tgfxSurface.get(), false);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

autoClear=false 与 backgroundLayer 的语义冲突。

PAGScene::draw() 的契约(PAGScene.h:99-106)规定:autoClear=false 时"overlay the scene content on top of the existing surface content",即在已有内容之上叠加。

但本分支即使 autoClear=false 也会执行 _backgroundLayer->draw(canvas),背景层会盖在 surface 已有内容之上,再绘制 scene 内容。这与"叠加 scene 内容"的语义冲突——backgroundLayer 反倒会把用户已有的 surface 内容遮挡。

建议在 backgroundLayer 路径下,当 autoClear=false 时跳过背景层绘制;或在 setBackgroundLayer 的 API 注释里明确说明该 API 与 autoClear=false 的交互行为。

scene->getDisplayOptions()->setBackgroundColor({0, 0, 0, 0});
auto density = currentCanvasWidth > 0 ? static_cast<float>(lastSurfaceWidth) /
static_cast<float>(currentCanvasWidth)
: 1.0f;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

density 计算实际恒为 1.0,HiDPI 适配未生效。

第 327 行 syncSurfaceSize(currentCanvasWidth, currentCanvasHeight) 执行后,syncSurfaceSize 内部会执行 lastSurfaceWidth = canvasWidth(即把 currentCanvasWidth 赋给 lastSurfaceWidth)。因此这里 lastSurfaceWidth / currentCanvasWidth 永远是 1.0。

从命名推测原意应是 devicePixelRatio,但当前文件没有任何地方读取 DPR,emscripten_get_canvas_element_size 返回的本来就是 canvas 的像素分辨率而非 CSS 像素,分子分母同源。

结果是 GridBackgroundLayertileSize = 32 * density 始终等于 32 个物理像素,HiDPI 屏(DPR=2)下棋盘格视觉只有 16 CSS 像素,与"在不同屏幕看起来一致"的设计意图相悖。

建议从 JS 端通过 window.devicePixelRatio 把真实 DPR 传入,或在 GridBackgroundLayer 内部读取 DPR。

int bgHeight = lastSurfaceHeight;
if (!backgroundLayer || bgWidth != lastBackgroundWidth || bgHeight != lastBackgroundHeight ||
std::abs(density - lastBackgroundDensity) > 0.001f) {
backgroundLayer = GridBackgroundLayer::Make(bgWidth, bgHeight, density);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

GridBackgroundLayerdensity 参数设计意图与现状不一致。

GridBackground.cpptileSize = 32 * density 的注释明确说"Use fixed logical size (32px) so the grid looks the same on all screens",意图是按 DPR 缩放。但结合上面对 density 计算的评论(恒为 1.0),这个设计意图实际上从未生效。

建议和 density 计算的修复(见上面的评论)一起改:要么传入真实 DPR,要么干脆去掉 density 参数并由 Layer 内部读取 DPR。

Comment thread src/pagx/PAGScene.cpp

void PAGScene::setBackgroundLayer(std::shared_ptr<tgfx::Layer> layer) {
_backgroundLayer = std::move(layer);
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

新增公开 API 缺少单元测试覆盖。

Codecov 报告本 PR 行覆盖率仅 10%,test/src/ 下也没有 setBackgroundLayer 的任何用例。按项目 Test 规范,新增公开 API 应有 GTest 覆盖。

建议至少补充:

  1. 不设置 backgroundLayer 时 draw() 行为正常;
  2. 设置背景层后渲染正确(可用截图基准对比);
  3. 传 nullptr 能恢复无背景层状态;
  4. DisplayOptions::setBackgroundColor 的交互行为(与上面 请问能否在android或者Ios上面,导出成视频格式。 #1 的评论相关)。

Comment thread include/pagx/PAGScene.h
* in surface coordinates (not affected by zoomScale or contentOffset) after the surface is
* cleared and before the display list is rendered. Pass nullptr to remove a previously set
* background layer.
* @param layer the layer to draw as background, or nullptr to remove.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

API 注释建议补充 tgfx::Layer 的使用约束。

按 tgfx 文档(tgfx/layers/Layer.hdraw() 注释):

"The layer is drawn in its local space without applying its own matrix, alpha, blend mode, visible, scrollRect, or mask."

这意味着用户如果对传入的 background layer 设置了 alpha / 变换 / mask 等属性,这些属性都不会生效。这是个调用者很容易踩的坑(例如想给背景层加 alpha 让其半透明)。

建议在此注释里明确补充:"The layer is drawn directly in surface local space; its own matrix, alpha, blendMode, visible, scrollRect and mask properties have no effect — wrap them inside the layers content if needed."

Comment thread include/pagx/PAGScene.h
* background layer.
* @param layer the layer to draw as background, or nullptr to remove.
*/
void setBackgroundLayer(std::shared_ptr<tgfx::Layer> layer);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

setBackgroundLayerDisplayOptions::setBackgroundColor 的语义存在重叠且交互未定义,是一个潜在陷阱:

按 tgfx 文档(tgfx/layers/DisplayList.hbackgroundColor() 注释):DisplayList 的 background color 在 render() 路径下会用 SrcOver 绘制。当前 PAGScene::draw() 在背景层路径下仍调用 displayList->render(surface, false),所以如果用户同时调用了 setBackgroundLayer(myLayer)getDisplayOptions()->setBackgroundColor(opaqueColor),渲染顺序会变成:clear → myLayer → opaque color SrcOver(盖掉 myLayer) → 内容。

PAGXView 自己绕开了这个问题(背景层路径下显式设 {0,0,0,0}),但作为对外 API 这是隐式约束。建议二选一:

  1. 在此处注释里明确说明"使用 backgroundLayer 时应将 DisplayOptions 的背景色设为透明";
  2. 或在 PAGScene::draw() 中当 _backgroundLayer != nullptr 时,把 DisplayList 背景色临时置为 Transparent 后再 render,避免覆盖。

lastBackgroundDensity = density;
}
scene->setBackgroundLayer(backgroundLayer);
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

每帧重复设置 backgroundColor / backgroundLayer。

当前 draw() 每帧都会无条件调用 setBackgroundColor(...)setBackgroundLayer(...),即使状态没变化。这两个状态本质上只在 setBackgroundColor() / clearBackgroundColor() 时变更。

建议把状态注入操作收敛到那两个入口里执行,draw() 只负责绘制;或者加上"上一次状态"标记,只在变化时 set。属于代码整洁与微优化,非阻塞。

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.

3 participants