Skip to content

Fix noise anchor and flickering in NoiseStyle and NoiseFilter#1433

Open
zfw1234567 wants to merge 18 commits into
Tencent:feature/partyhuang_layer_noisefrom
zfw1234567:feature/fengweizou_layer_noise
Open

Fix noise anchor and flickering in NoiseStyle and NoiseFilter#1433
zfw1234567 wants to merge 18 commits into
Tencent:feature/partyhuang_layer_noisefrom
zfw1234567:feature/fengweizou_layer_noise

Conversation

@zfw1234567
Copy link
Copy Markdown
Collaborator

@zfw1234567 zfw1234567 commented May 15, 2026

问题描述

  1. 噪声锚点偏移

    • NoiseFilter:在 Layer 下使用时,如果 inputImage 发生裁切,噪声采样原点会随之偏移,导致噪声图案不一致。
    • NoiseStyle:在 Layer 下使用 path 模式时,噪声采样原点会随 path 的变化而偏移,导致噪声图案不稳定。
  2. 噪声闪烁AlphaThresholdFragmentProcessor 的 GLSL 着色器使用 step() 函数进行 alpha 阈值判断,产生硬边界,导致噪声在不同帧之间出现闪烁。

主要变更

  1. 为 LayerFilter 添加带几何参数的 filterImage() 重载,接受 width/height/originOffset,使 NoiseFilter 能将噪声锚定到内容边界中心,而非随 inputImage 裁切而偏移
  2. 为 LayerStyle 的 draw()/onDraw() 添加 contentOffset 参数,使 NoiseStyle 能通过采样矩阵偏移将噪声锚定到内容位置,避免 path 模式下的偏移问题,同时消除 drawLayerStyleDefault 中的类型检查分发
  3. 引入 LayerImageFilter 基类,用于可表示为单个 ImageFilter 的滤镜(Blur、DropShadow、InnerShadow、ColorMatrix、Blend),提供 ImageFilter 缓存
  4. 重构 NoiseFilter 继承关系,NoiseFilter 直接继承 LayerFilter(因其效果依赖输入图像几何信息),getComposeFilter() 替换为 getImageFilter(),简化缓存机制
  5. 修复噪声闪烁:将 GLSLAlphaThresholdFragmentProcessor 中的 step() 替换为 smoothstep(threshold - 0.01, threshold + 0.01, alpha),消除硬边界闪烁

Comment thread src/layers/OffscreenRenderer.cpp Outdated

auto imageFilter = args.excludeEffects
? nullptr
: layer->getImageFilter(contentMatrix.getMaxScale(), contentBounds.width(),
Copy link
Copy Markdown
Collaborator

@Hparty Hparty May 15, 2026

Choose a reason for hiding this comment

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

这里仍然走的是『先合成 ImageFilter → makeWithFilter』老路径,所以 NoiseFilter 不得不额外暴露 getComposeFilter(scale, w, h, oo) 把几何参数塞进 ImageFilter,并且在 NoiseFilter 内部维护一套只为这条路径存在的 composedFilter / composedDirty / composedScale 缓存。

而 Layer::createSubtreeCacheImage 那条路径已经迁到 applyFilters() 上,逐个 filter 调 filterImage() 把几何参数直接传进去,不需要 getComposeFilter。

建议这里也改成 applyFilters() 风格(把 ApplyImageFilterIfNeeded 里的 clipBounds 反向裁剪和 imageMatrix 偏移合并那段一起重构),配合给 LayerFilter::filterBounds 加 MapDirection 参数(默认 Forward,LayerImageFilter 转发到底层 ImageFilter,NoiseFilter 之类 identity 直接返回 srcRect),然后:

  • 删掉 Layer::getImageFilter 整个入口(Layer 不再需要拿底层 ImageFilter,bounds 反推也走 LayerFilter::filterBounds(rect, scale, Reverse))
  • 删掉 LayerFilter::getComposeFilter 这套虚函数
  • 删掉 NoiseFilter 的 composedFilter / composedDirty / composedScale 缓存

这样能消掉『两条渲染路径要保持几何同步』的隐患,也能去掉只为 NoiseFilter 一个类存在的兜底机制。如果这次 PR 改动面太大,可以拆个 follow-up,但 getComposeFilter 这个名字和 composedFilter 缓存看起来像是临时方案,容易被忘掉。

@zfw1234567 zfw1234567 changed the title Fix unstable noise pattern in NoiseFilter by anchoring to content bounds center Fix noise anchor and flickering in NoiseStyle and NoiseFilter May 21, 2026
* LayerFilter represents a filter that applies effects to a layer, such as blurs, shadows, or color
* adjustments. It creates a new offscreen image that replaces the original layer content.
* LayerFilters are mutable and can be changed at any time.
* LayerFilter is the abstract base class for filters that apply effects to a layer, such as blurs,
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.

注释内容面向使用者,不用说太多内部细节

class LayerFilter : public LayerProperty {
public:
/**
* Applies this filter to the given input image at the specified scale factor. The offset stores
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.

filterImage只留一个,不要留两个对外接口

* @param offset If non-null, receives the (x, y) translation of the filtered image.
* @return The filtered image, or nullptr on failure.
*/
std::shared_ptr<Image> filterImage(std::shared_ptr<Image> input, float scale, float width,
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.

width,height,originOffset看下能不能组装成一个Rect行为,有点太散了

* forwards to this method when subclasses do not override the 5-arg version.
*/
virtual std::shared_ptr<ImageFilter> onCreateImageFilter(float scale);
virtual std::shared_ptr<Image> onFilterImage(std::shared_ptr<Image> input, float, Point*) {
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.

onFilterImage也不需要两个

* grow or shrink the visible region. The default implementation returns nullptr.
*/
void invalidateFilter();
virtual std::shared_ptr<ImageFilter> getImageFilter(float scale);
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.

getImageFilter不在这个基类里暴露,放在LayerImageFilter里


protected:
std::shared_ptr<ImageFilter> onBuildNoiseImageFilter(float scale, int width, int height) override;
std::shared_ptr<ImageFilter> onBuildNoiseImageFilter(float scale, Point shift) override;
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.

const Point& shift

float contentScale, std::shared_ptr<Image> extraSource,
const Point& extraSourceOffset, float alpha,
BlendMode blendMode);
BlendMode blendMode, const Point& contentOffset);
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.

contentOffset是否可以放在canvas上处理,这里有extraSourceOffset+contentOffset两个,是否用1个就能解决

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

contentoffset是用于补偿因path导致的shader采样锚点的偏移,和cavas无关,extraSourceOffset和contentOffset这里是两个不同的东西

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