mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-04-28 18:47:15 +02:00
When executing display list commands, check if commands with effect contexts (opacity, filters, blend modes) are outside the viewport before applying the effect. Since effects don't affect clip state, would_be_fully_clipped_by_painter() returns the same result before and after applying effects. This avoids expensive saveLayer/restore cycles for off-screen commands with effects like blur, which is particularly beneficial for pages with many blurred decorative images (e.g., Discord's landing page has 70+ blurred star images). The optimization only applies when switching to a new effect context, not for consecutive commands with the same context, to preserve correct blend mode compositing behavior.
115 lines
2.9 KiB
C++
115 lines
2.9 KiB
C++
/*
|
|
* Copyright (c) 2026, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <AK/AtomicRefCounted.h>
|
|
#include <AK/Variant.h>
|
|
#include <LibGfx/CompositingAndBlendingOperator.h>
|
|
#include <LibGfx/Matrix4x4.h>
|
|
#include <LibGfx/Path.h>
|
|
#include <LibGfx/WindingRule.h>
|
|
#include <LibWeb/Painting/BorderRadiiData.h>
|
|
#include <LibWeb/Painting/ResolvedCSSFilter.h>
|
|
#include <LibWeb/Painting/ScrollState.h>
|
|
|
|
namespace Web::Painting {
|
|
|
|
struct ClipRect {
|
|
CSSPixelRect rect;
|
|
BorderRadiiData corner_radii;
|
|
};
|
|
|
|
struct ScrollData {
|
|
size_t scroll_frame_id;
|
|
bool is_sticky;
|
|
};
|
|
|
|
struct ClipData {
|
|
CSSPixelRect rect;
|
|
BorderRadiiData corner_radii;
|
|
|
|
explicit ClipData(ClipRect const& clip_rect)
|
|
: rect(clip_rect.rect)
|
|
, corner_radii(clip_rect.corner_radii)
|
|
{
|
|
}
|
|
|
|
ClipData(CSSPixelRect r, BorderRadiiData radii)
|
|
: rect(r)
|
|
, corner_radii(radii)
|
|
{
|
|
}
|
|
|
|
bool contains(CSSPixelPoint point) const;
|
|
};
|
|
|
|
struct TransformData {
|
|
Gfx::FloatMatrix4x4 matrix;
|
|
CSSPixelPoint origin;
|
|
};
|
|
|
|
struct PerspectiveData {
|
|
Gfx::FloatMatrix4x4 matrix;
|
|
};
|
|
|
|
struct ClipPathData {
|
|
Gfx::Path path;
|
|
CSSPixelRect bounding_rect;
|
|
Gfx::WindingRule fill_rule;
|
|
};
|
|
|
|
struct EffectsData {
|
|
float opacity { 1.0f };
|
|
Gfx::CompositingAndBlendingOperator blend_mode { Gfx::CompositingAndBlendingOperator::Normal };
|
|
ResolvedCSSFilter filter;
|
|
bool isolate { false };
|
|
|
|
bool needs_layer() const
|
|
{
|
|
return opacity < 1.0f
|
|
|| blend_mode != Gfx::CompositingAndBlendingOperator::Normal
|
|
|| filter.has_filters()
|
|
|| isolate;
|
|
}
|
|
};
|
|
|
|
using VisualContextData = Variant<ScrollData, ClipData, TransformData, PerspectiveData, ClipPathData, EffectsData>;
|
|
|
|
class AccumulatedVisualContext : public AtomicRefCounted<AccumulatedVisualContext> {
|
|
public:
|
|
static NonnullRefPtr<AccumulatedVisualContext> create(size_t id, VisualContextData data, RefPtr<AccumulatedVisualContext const> parent);
|
|
|
|
VisualContextData const& data() const { return m_data; }
|
|
RefPtr<AccumulatedVisualContext const> parent() const { return m_parent; }
|
|
|
|
bool is_effect() const { return m_data.has<EffectsData>(); }
|
|
|
|
size_t depth() const { return m_depth; }
|
|
size_t id() const { return m_id; }
|
|
|
|
void dump(StringBuilder&) const;
|
|
|
|
Optional<CSSPixelPoint> transform_point_for_hit_test(CSSPixelPoint screen_point, ScrollStateSnapshot const& scroll_state) const;
|
|
CSSPixelRect transform_rect_to_viewport(CSSPixelRect const&, ScrollStateSnapshot const&) const;
|
|
|
|
private:
|
|
AccumulatedVisualContext(size_t id, VisualContextData data, RefPtr<AccumulatedVisualContext const> parent)
|
|
: m_data(move(data))
|
|
, m_parent(move(parent))
|
|
, m_depth(m_parent ? m_parent->depth() + 1 : 1)
|
|
, m_id(id)
|
|
{
|
|
}
|
|
|
|
VisualContextData m_data;
|
|
RefPtr<AccumulatedVisualContext const> m_parent;
|
|
size_t m_depth;
|
|
size_t m_id;
|
|
};
|
|
|
|
}
|