LibWeb: Inline serialized filters in display list commands
Prep work for serializing display lists across the IPC boundary. Replace Gfx::Filter's Skia-specific backing with a portable Variant-based representation, add serialize_filter/deserialize_filter, and store filter data inline in ApplyEffects/ApplyBackdropFilter command payloads instead of in DisplayListResourceStorage. FilterResourceId is removed along with the per-storage filter map.
This commit is contained in:
parent
c1a69e6c9e
commit
3c9df3fccd
11 changed files with 986 additions and 292 deletions
|
|
@ -4,44 +4,42 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Atomic.h>
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <AK/NumericLimits.h>
|
||||
#include <LibGfx/DecodedImageFrame.h>
|
||||
#include <LibGfx/Filter.h>
|
||||
#include <LibGfx/FilterImpl.h>
|
||||
#include <LibGfx/SkiaUtils.h>
|
||||
#include <core/SkBlendMode.h>
|
||||
#include <core/SkColorFilter.h>
|
||||
#include <core/SkScalar.h>
|
||||
#include <effects/SkColorMatrix.h>
|
||||
#include <effects/SkImageFilters.h>
|
||||
#include <effects/SkPerlinNoiseShader.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
static Atomic<u64> s_next_id { 1 };
|
||||
|
||||
using Impl = FilterImpl;
|
||||
static ErrorOr<Optional<ByteBuffer>> copy_optional_color_table(Optional<ReadonlyBytes> bytes)
|
||||
{
|
||||
if (!bytes.has_value())
|
||||
return Optional<ByteBuffer> {};
|
||||
VERIFY(bytes->size() == 256);
|
||||
return Optional<ByteBuffer> { TRY(ByteBuffer::copy(*bytes)) };
|
||||
}
|
||||
|
||||
Filter::Filter(Filter const& other)
|
||||
: m_id(other.m_id)
|
||||
, m_impl(other.m_impl->clone())
|
||||
: m_impl(other.m_impl->clone())
|
||||
{
|
||||
}
|
||||
|
||||
Filter& Filter::operator=(Filter const& other)
|
||||
{
|
||||
if (this != &other) {
|
||||
m_id = other.m_id;
|
||||
if (this != &other)
|
||||
m_impl = other.m_impl->clone();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Filter::Filter(Filter&&) = default;
|
||||
|
||||
Filter& Filter::operator=(Filter&&) = default;
|
||||
|
||||
Filter::~Filter() = default;
|
||||
|
||||
Filter::Filter(NonnullOwnPtr<FilterImpl>&& impl)
|
||||
: m_id(s_next_id.fetch_add(1, AK::MemoryOrder::memory_order_relaxed))
|
||||
, m_impl(impl->clone())
|
||||
: m_impl(move(impl))
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -52,298 +50,541 @@ FilterImpl const& Filter::impl() const
|
|||
|
||||
Filter Filter::arithmetic(Optional<Filter const&> background, Optional<Filter const&> foreground, float k1, float k2, float k3, float k4)
|
||||
{
|
||||
sk_sp<SkImageFilter> background_skia = background.has_value() ? background->m_impl->filter : nullptr;
|
||||
sk_sp<SkImageFilter> foreground_skia = foreground.has_value() ? foreground->m_impl->filter : nullptr;
|
||||
|
||||
auto filter = SkImageFilters::Arithmetic(
|
||||
SkFloatToScalar(k1), SkFloatToScalar(k2), SkFloatToScalar(k3), SkFloatToScalar(k4), false, move(background_skia), move(foreground_skia));
|
||||
return Filter(Impl::create(filter));
|
||||
return Filter(FilterImpl::create(FilterImpl::Arithmetic {
|
||||
.background = background.copy(),
|
||||
.foreground = foreground.copy(),
|
||||
.k1 = k1,
|
||||
.k2 = k2,
|
||||
.k3 = k3,
|
||||
.k4 = k4,
|
||||
}));
|
||||
}
|
||||
|
||||
Filter Filter::compose(Filter const& outer, Filter const& inner)
|
||||
{
|
||||
auto inner_skia = inner.m_impl->filter;
|
||||
auto outer_skia = outer.m_impl->filter;
|
||||
|
||||
auto filter = SkImageFilters::Compose(outer_skia, inner_skia);
|
||||
return Filter(Impl::create(filter));
|
||||
return Filter(FilterImpl::create(FilterImpl::Compose {
|
||||
.outer = outer,
|
||||
.inner = inner,
|
||||
}));
|
||||
}
|
||||
|
||||
Filter Filter::blend(Optional<Filter const&> background, Optional<Filter const&> foreground, Gfx::CompositingAndBlendingOperator mode)
|
||||
{
|
||||
sk_sp<SkImageFilter> background_skia = background.has_value() ? background->m_impl->filter : nullptr;
|
||||
sk_sp<SkImageFilter> foreground_skia = foreground.has_value() ? foreground->m_impl->filter : nullptr;
|
||||
|
||||
auto filter = SkImageFilters::Blend(to_skia_blender(mode), background_skia, foreground_skia);
|
||||
return Filter(Impl::create(filter));
|
||||
return Filter(FilterImpl::create(FilterImpl::Blend {
|
||||
.background = background.copy(),
|
||||
.foreground = foreground.copy(),
|
||||
.mode = mode,
|
||||
}));
|
||||
}
|
||||
|
||||
Filter Filter::blur(float radius_x, float radius_y, Optional<Filter const&> input)
|
||||
{
|
||||
sk_sp<SkImageFilter> input_skia = input.has_value() ? input->m_impl->filter : nullptr;
|
||||
|
||||
auto filter = SkImageFilters::Blur(radius_x, radius_y, input_skia);
|
||||
return Filter(Impl::create(filter));
|
||||
return Filter(FilterImpl::create(FilterImpl::Blur {
|
||||
.radius_x = radius_x,
|
||||
.radius_y = radius_y,
|
||||
.input = input.copy(),
|
||||
}));
|
||||
}
|
||||
|
||||
Filter Filter::flood(Gfx::Color color, float opacity)
|
||||
{
|
||||
auto color_skia = to_skia_color(color);
|
||||
color_skia = SkColorSetA(color_skia, static_cast<u8>(opacity * 255));
|
||||
|
||||
return Filter(Impl::create(SkImageFilters::Shader(SkShaders::Color(color_skia))));
|
||||
return Filter(FilterImpl::create(FilterImpl::Flood {
|
||||
.color = color,
|
||||
.opacity = opacity,
|
||||
}));
|
||||
}
|
||||
|
||||
Filter Filter::displacement_map(Optional<Filter const&> color, Optional<Filter const&> displacement, float scale, ChannelSelector x_channel_selector, ChannelSelector y_channel_selector)
|
||||
{
|
||||
sk_sp<SkImageFilter> color_skia = color.has_value() ? color->m_impl->filter : nullptr;
|
||||
sk_sp<SkImageFilter> displacement_skia = displacement.has_value() ? displacement->m_impl->filter : nullptr;
|
||||
|
||||
auto convert_channel_selector = [](ChannelSelector channel_selector) {
|
||||
switch (channel_selector) {
|
||||
case ChannelSelector::Red:
|
||||
return SkColorChannel::kR;
|
||||
case ChannelSelector::Green:
|
||||
return SkColorChannel::kG;
|
||||
case ChannelSelector::Blue:
|
||||
return SkColorChannel::kB;
|
||||
case ChannelSelector::Alpha:
|
||||
return SkColorChannel::kA;
|
||||
}
|
||||
|
||||
VERIFY_NOT_REACHED();
|
||||
};
|
||||
|
||||
auto x_channel_selector_skia = convert_channel_selector(x_channel_selector);
|
||||
auto y_channel_selector_skia = convert_channel_selector(y_channel_selector);
|
||||
auto filter = SkImageFilters::DisplacementMap(x_channel_selector_skia, y_channel_selector_skia, scale, displacement_skia, color_skia);
|
||||
return Filter(Impl::create(filter));
|
||||
return Filter(FilterImpl::create(FilterImpl::DisplacementMap {
|
||||
.color = color.copy(),
|
||||
.displacement = displacement.copy(),
|
||||
.scale = scale,
|
||||
.x_channel_selector = x_channel_selector,
|
||||
.y_channel_selector = y_channel_selector,
|
||||
}));
|
||||
}
|
||||
|
||||
Filter Filter::drop_shadow(float offset_x, float offset_y, float radius, Gfx::Color color,
|
||||
Optional<Filter const&> input)
|
||||
Filter Filter::drop_shadow(float offset_x, float offset_y, float radius, Gfx::Color color, Optional<Filter const&> input)
|
||||
{
|
||||
sk_sp<SkImageFilter> input_skia = input.has_value() ? input->m_impl->filter : nullptr;
|
||||
auto shadow_color = to_skia_color(color);
|
||||
|
||||
auto filter = SkImageFilters::DropShadow(offset_x, offset_y, radius, radius, shadow_color, input_skia);
|
||||
return Filter(Impl::create(filter));
|
||||
return Filter(FilterImpl::create(FilterImpl::DropShadow {
|
||||
.offset_x = offset_x,
|
||||
.offset_y = offset_y,
|
||||
.radius = radius,
|
||||
.color = color,
|
||||
.input = input.copy(),
|
||||
}));
|
||||
}
|
||||
|
||||
Filter Filter::color(ColorFilterType type, float amount, Optional<Filter const&> input)
|
||||
{
|
||||
sk_sp<SkImageFilter> input_skia = input.has_value() ? input->m_impl->filter : nullptr;
|
||||
|
||||
sk_sp<SkColorFilter> color_filter;
|
||||
|
||||
// Matrices are taken from https://drafts.fxtf.org/filter-effects-1/#FilterPrimitiveRepresentation
|
||||
switch (type) {
|
||||
case ColorFilterType::Grayscale: {
|
||||
float matrix[20] = {
|
||||
0.2126f + 0.7874f * (1 - amount), 0.7152f - 0.7152f * (1 - amount),
|
||||
0.0722f - 0.0722f * (1 - amount), 0, 0,
|
||||
0.2126f - 0.2126f * (1 - amount), 0.7152f + 0.2848f * (1 - amount),
|
||||
0.0722f - 0.0722f * (1 - amount), 0, 0,
|
||||
0.2126f - 0.2126f * (1 - amount), 0.7152f - 0.7152f * (1 - amount),
|
||||
0.0722f + 0.9278f * (1 - amount), 0, 0,
|
||||
0, 0, 0, 1, 0
|
||||
};
|
||||
color_filter = SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kYes);
|
||||
break;
|
||||
}
|
||||
case Gfx::ColorFilterType::Brightness: {
|
||||
float matrix[20] = {
|
||||
amount, 0, 0, 0, 0,
|
||||
0, amount, 0, 0, 0,
|
||||
0, 0, amount, 0, 0,
|
||||
0, 0, 0, 1, 0
|
||||
};
|
||||
color_filter = SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kNo);
|
||||
break;
|
||||
}
|
||||
case Gfx::ColorFilterType::Contrast: {
|
||||
float intercept = -(0.5f * amount) + 0.5f;
|
||||
float matrix[20] = {
|
||||
amount, 0, 0, 0, intercept,
|
||||
0, amount, 0, 0, intercept,
|
||||
0, 0, amount, 0, intercept,
|
||||
0, 0, 0, 1, 0
|
||||
};
|
||||
color_filter = SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kNo);
|
||||
break;
|
||||
}
|
||||
case Gfx::ColorFilterType::Invert: {
|
||||
float matrix[20] = {
|
||||
1 - 2 * amount, 0, 0, 0, amount,
|
||||
0, 1 - 2 * amount, 0, 0, amount,
|
||||
0, 0, 1 - 2 * amount, 0, amount,
|
||||
0, 0, 0, 1, 0
|
||||
};
|
||||
color_filter = SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kYes);
|
||||
break;
|
||||
}
|
||||
case Gfx::ColorFilterType::Opacity: {
|
||||
float matrix[20] = {
|
||||
1, 0, 0, 0, 0,
|
||||
0, 1, 0, 0, 0,
|
||||
0, 0, 1, 0, 0,
|
||||
0, 0, 0, amount, 0
|
||||
};
|
||||
color_filter = SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kYes);
|
||||
break;
|
||||
}
|
||||
case Gfx::ColorFilterType::Sepia: {
|
||||
float matrix[20] = {
|
||||
0.393f + 0.607f * (1 - amount), 0.769f - 0.769f * (1 - amount), 0.189f - 0.189f * (1 - amount), 0,
|
||||
0,
|
||||
0.349f - 0.349f * (1 - amount), 0.686f + 0.314f * (1 - amount), 0.168f - 0.168f * (1 - amount), 0,
|
||||
0,
|
||||
0.272f - 0.272f * (1 - amount), 0.534f - 0.534f * (1 - amount), 0.131f + 0.869f * (1 - amount), 0,
|
||||
0,
|
||||
0, 0, 0, 1, 0
|
||||
};
|
||||
color_filter = SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kYes);
|
||||
break;
|
||||
}
|
||||
case Gfx::ColorFilterType::Saturate: {
|
||||
float matrix[20] = {
|
||||
0.213f + 0.787f * amount, 0.715f - 0.715f * amount, 0.072f - 0.072f * amount, 0, 0,
|
||||
0.213f - 0.213f * amount, 0.715f + 0.285f * amount, 0.072f - 0.072f * amount, 0, 0,
|
||||
0.213f - 0.213f * amount, 0.715f - 0.715f * amount, 0.072f + 0.928f * amount, 0, 0,
|
||||
0, 0, 0, 1, 0
|
||||
};
|
||||
color_filter = SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kNo);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
return Filter(Impl::create(SkImageFilters::ColorFilter(color_filter, input_skia)));
|
||||
return Filter(FilterImpl::create(FilterImpl::ColorFilter {
|
||||
.type = type,
|
||||
.amount = amount,
|
||||
.input = input.copy(),
|
||||
}));
|
||||
}
|
||||
|
||||
Filter Filter::color_matrix(float matrix[20], Optional<Filter const&> input)
|
||||
{
|
||||
sk_sp<SkImageFilter> input_skia = input.has_value() ? input->m_impl->filter : nullptr;
|
||||
|
||||
return Filter(Impl::create(SkImageFilters::ColorFilter(SkColorFilters::Matrix(matrix), input_skia)));
|
||||
Array<float, 20> matrix_values;
|
||||
for (size_t i = 0; i < matrix_values.size(); ++i)
|
||||
matrix_values[i] = matrix[i];
|
||||
return Filter(FilterImpl::create(FilterImpl::ColorMatrix {
|
||||
.matrix = matrix_values,
|
||||
.input = input.copy(),
|
||||
}));
|
||||
}
|
||||
|
||||
Filter Filter::color_table(Optional<ReadonlyBytes> a, Optional<ReadonlyBytes> r, Optional<ReadonlyBytes> g,
|
||||
Optional<ReadonlyBytes> b, Optional<Filter const&> input)
|
||||
Filter Filter::color_table(Optional<ReadonlyBytes> a, Optional<ReadonlyBytes> r, Optional<ReadonlyBytes> g, Optional<ReadonlyBytes> b, Optional<Filter const&> input)
|
||||
{
|
||||
VERIFY(!a.has_value() || a->size() == 256);
|
||||
VERIFY(!r.has_value() || r->size() == 256);
|
||||
VERIFY(!g.has_value() || g->size() == 256);
|
||||
VERIFY(!b.has_value() || b->size() == 256);
|
||||
|
||||
sk_sp<SkImageFilter> input_skia = input.has_value() ? input->m_impl->filter : nullptr;
|
||||
|
||||
auto* a_table = a.has_value() ? a->data() : nullptr;
|
||||
auto* r_table = r.has_value() ? r->data() : nullptr;
|
||||
auto* g_table = g.has_value() ? g->data() : nullptr;
|
||||
auto* b_table = b.has_value() ? b->data() : nullptr;
|
||||
|
||||
// Color tables are applied in linear space by default, so we need to convert twice.
|
||||
// FIXME: support sRGB space as well (i.e. don't perform these conversions).
|
||||
auto srgb_to_linear = SkImageFilters::ColorFilter(SkColorFilters::SRGBToLinearGamma(), input_skia);
|
||||
auto color_table = SkImageFilters::ColorFilter(SkColorFilters::TableARGB(a_table, r_table, g_table, b_table), srgb_to_linear);
|
||||
auto linear_to_srgb = SkImageFilters::ColorFilter(SkColorFilters::LinearToSRGBGamma(), color_table);
|
||||
return Filter(Impl::create(linear_to_srgb));
|
||||
return Filter(FilterImpl::create(FilterImpl::ColorTable {
|
||||
.a = MUST(copy_optional_color_table(a)),
|
||||
.r = MUST(copy_optional_color_table(r)),
|
||||
.g = MUST(copy_optional_color_table(g)),
|
||||
.b = MUST(copy_optional_color_table(b)),
|
||||
.input = input.copy(),
|
||||
}));
|
||||
}
|
||||
|
||||
Filter Filter::saturate(float value, Optional<Filter const&> input)
|
||||
{
|
||||
sk_sp<SkImageFilter> input_skia = input.has_value() ? input->m_impl->filter : nullptr;
|
||||
|
||||
SkColorMatrix matrix;
|
||||
matrix.setSaturation(value);
|
||||
|
||||
return Filter(Impl::create(SkImageFilters::ColorFilter(SkColorFilters::Matrix(matrix), input_skia)));
|
||||
return Filter(FilterImpl::create(FilterImpl::Saturate {
|
||||
.value = value,
|
||||
.input = input.copy(),
|
||||
}));
|
||||
}
|
||||
|
||||
Filter Filter::hue_rotate(float angle_degrees, Optional<Filter const&> input)
|
||||
{
|
||||
sk_sp<SkImageFilter> input_skia = input.has_value() ? input->m_impl->filter : nullptr;
|
||||
|
||||
float radians = AK::to_radians(angle_degrees);
|
||||
|
||||
auto cosA = cos(radians);
|
||||
auto sinA = sin(radians);
|
||||
|
||||
auto a00 = 0.213f + cosA * 0.787f - sinA * 0.213f;
|
||||
auto a01 = 0.715f - cosA * 0.715f - sinA * 0.715f;
|
||||
auto a02 = 0.072f - cosA * 0.072f + sinA * 0.928f;
|
||||
auto a10 = 0.213f - cosA * 0.213f + sinA * 0.143f;
|
||||
auto a11 = 0.715f + cosA * 0.285f + sinA * 0.140f;
|
||||
auto a12 = 0.072f - cosA * 0.072f - sinA * 0.283f;
|
||||
auto a20 = 0.213f - cosA * 0.213f - sinA * 0.787f;
|
||||
auto a21 = 0.715f - cosA * 0.715f + sinA * 0.715f;
|
||||
auto a22 = 0.072f + cosA * 0.928f + sinA * 0.072f;
|
||||
|
||||
float matrix[20] = {
|
||||
a00, a01, a02, 0, 0,
|
||||
a10, a11, a12, 0, 0,
|
||||
a20, a21, a22, 0, 0,
|
||||
0, 0, 0, 1, 0
|
||||
};
|
||||
|
||||
auto color_filter = SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kNo);
|
||||
return Filter(Impl::create(SkImageFilters::ColorFilter(color_filter, input_skia)));
|
||||
return Filter(FilterImpl::create(FilterImpl::HueRotate {
|
||||
.angle_degrees = angle_degrees,
|
||||
.input = input.copy(),
|
||||
}));
|
||||
}
|
||||
|
||||
Filter Filter::image(Gfx::DecodedImageFrame const& frame, Gfx::IntRect const& src_rect, Gfx::IntRect const& dest_rect, Gfx::ScalingMode scaling_mode)
|
||||
{
|
||||
auto skia_src_rect = to_skia_rect(src_rect);
|
||||
auto skia_dest_rect = to_skia_rect(dest_rect);
|
||||
auto sampling_options = to_skia_sampling_options(scaling_mode);
|
||||
|
||||
auto image = sk_image_from_bitmap(frame.bitmap(), frame.color_space());
|
||||
return Filter(Impl::create(SkImageFilters::Image(move(image), skia_src_rect, skia_dest_rect, sampling_options)));
|
||||
return Filter(FilterImpl::create(FilterImpl::Image {
|
||||
.frame = frame,
|
||||
.src_rect = src_rect,
|
||||
.dest_rect = dest_rect,
|
||||
.scaling_mode = scaling_mode,
|
||||
}));
|
||||
}
|
||||
|
||||
Filter Filter::merge(Vector<Optional<Filter>> const& inputs)
|
||||
{
|
||||
Vector<sk_sp<SkImageFilter>> skia_filters;
|
||||
skia_filters.ensure_capacity(inputs.size());
|
||||
for (auto& filter : inputs)
|
||||
skia_filters.unchecked_append(filter.has_value() ? filter->m_impl->filter : nullptr);
|
||||
|
||||
return Filter(Impl::create(SkImageFilters::Merge(skia_filters.data(), skia_filters.size())));
|
||||
return Filter(FilterImpl::create(FilterImpl::Merge {
|
||||
.inputs = inputs,
|
||||
}));
|
||||
}
|
||||
|
||||
Filter Filter::erode(float radius_x, float radius_y, Optional<Filter> const& input)
|
||||
{
|
||||
sk_sp<SkImageFilter> input_skia = input.has_value() ? input->m_impl->filter : nullptr;
|
||||
return Filter(Impl::create(SkImageFilters::Erode(radius_x, radius_y, input_skia)));
|
||||
return Filter(FilterImpl::create(FilterImpl::Erode {
|
||||
.radius_x = radius_x,
|
||||
.radius_y = radius_y,
|
||||
.input = input,
|
||||
}));
|
||||
}
|
||||
|
||||
Filter Filter::dilate(float radius_x, float radius_y, Optional<Filter> const& input)
|
||||
{
|
||||
sk_sp<SkImageFilter> input_skia = input.has_value() ? input->m_impl->filter : nullptr;
|
||||
return Filter(Impl::create(SkImageFilters::Dilate(radius_x, radius_y, input_skia)));
|
||||
return Filter(FilterImpl::create(FilterImpl::Dilate {
|
||||
.radius_x = radius_x,
|
||||
.radius_y = radius_y,
|
||||
.input = input,
|
||||
}));
|
||||
}
|
||||
|
||||
Filter Filter::offset(float dx, float dy, Optional<Filter const&> input)
|
||||
{
|
||||
sk_sp<SkImageFilter> input_skia = input.has_value() ? input->m_impl->filter : nullptr;
|
||||
return Filter(Impl::create(SkImageFilters::Offset(dx, dy, input_skia)));
|
||||
return Filter(FilterImpl::create(FilterImpl::Offset {
|
||||
.dx = dx,
|
||||
.dy = dy,
|
||||
.input = input.copy(),
|
||||
}));
|
||||
}
|
||||
|
||||
Filter Filter::turbulence(TurbulenceType turbulence_type, float base_frequency_x, float base_frequency_y, i32 num_octaves, float seed, Gfx::IntSize const& tile_stitch_size)
|
||||
{
|
||||
sk_sp<SkShader> turbulence_shader = [&] {
|
||||
auto skia_size = SkISize::Make(tile_stitch_size.width(), tile_stitch_size.height());
|
||||
switch (turbulence_type) {
|
||||
case TurbulenceType::Turbulence:
|
||||
return SkShaders::MakeTurbulence(base_frequency_x, base_frequency_y, num_octaves, seed, &skia_size);
|
||||
case TurbulenceType::FractalNoise:
|
||||
return SkShaders::MakeFractalNoise(base_frequency_x, base_frequency_y, num_octaves, seed, &skia_size);
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
}();
|
||||
return Filter(FilterImpl::create(FilterImpl::Turbulence {
|
||||
.turbulence_type = turbulence_type,
|
||||
.base_frequency_x = base_frequency_x,
|
||||
.base_frequency_y = base_frequency_y,
|
||||
.num_octaves = num_octaves,
|
||||
.seed = seed,
|
||||
.tile_stitch_size = tile_stitch_size,
|
||||
}));
|
||||
}
|
||||
|
||||
return Filter(Impl::create(SkImageFilters::Shader(move(turbulence_shader))));
|
||||
namespace {
|
||||
|
||||
using ImageEncoder = Function<u64(Gfx::DecodedImageFrame const&)>;
|
||||
using ImageDecoder = Function<Gfx::DecodedImageFrame(u64)>;
|
||||
|
||||
static void write_color(Stream& stream, Color color)
|
||||
{
|
||||
MUST(stream.write_value<u32>(color.value()));
|
||||
}
|
||||
|
||||
static Color read_color(Stream& stream)
|
||||
{
|
||||
return Color::from_bgra(MUST(stream.read_value<u32>()));
|
||||
}
|
||||
|
||||
static void write_int_rect(Stream& stream, Gfx::IntRect const& rect)
|
||||
{
|
||||
MUST(stream.write_value<i32>(rect.x()));
|
||||
MUST(stream.write_value<i32>(rect.y()));
|
||||
MUST(stream.write_value<i32>(rect.width()));
|
||||
MUST(stream.write_value<i32>(rect.height()));
|
||||
}
|
||||
|
||||
static Gfx::IntRect read_int_rect(Stream& stream)
|
||||
{
|
||||
auto x = MUST(stream.read_value<i32>());
|
||||
auto y = MUST(stream.read_value<i32>());
|
||||
auto width = MUST(stream.read_value<i32>());
|
||||
auto height = MUST(stream.read_value<i32>());
|
||||
return Gfx::IntRect { x, y, width, height };
|
||||
}
|
||||
|
||||
static void write_int_size(Stream& stream, Gfx::IntSize const& size)
|
||||
{
|
||||
MUST(stream.write_value<i32>(size.width()));
|
||||
MUST(stream.write_value<i32>(size.height()));
|
||||
}
|
||||
|
||||
static Gfx::IntSize read_int_size(Stream& stream)
|
||||
{
|
||||
auto width = MUST(stream.read_value<i32>());
|
||||
auto height = MUST(stream.read_value<i32>());
|
||||
return Gfx::IntSize { width, height };
|
||||
}
|
||||
|
||||
static void write_bytes(Stream& stream, ReadonlyBytes bytes)
|
||||
{
|
||||
VERIFY(bytes.size() <= NumericLimits<u32>::max());
|
||||
MUST(stream.write_value<u32>(bytes.size()));
|
||||
MUST(stream.write_until_depleted(bytes));
|
||||
}
|
||||
|
||||
static ByteBuffer read_bytes(Stream& stream)
|
||||
{
|
||||
auto size = MUST(stream.read_value<u32>());
|
||||
auto buffer = MUST(ByteBuffer::create_uninitialized(size));
|
||||
MUST(stream.read_until_filled(buffer));
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void encode_filter(Stream&, Filter const&, ImageEncoder const&);
|
||||
|
||||
template<typename T>
|
||||
static void encode_optional_filter(Stream& stream, Optional<T> const& filter, ImageEncoder const& encode_image)
|
||||
{
|
||||
MUST(stream.write_value<bool>(filter.has_value()));
|
||||
if (filter.has_value())
|
||||
encode_filter(stream, *filter, encode_image);
|
||||
}
|
||||
|
||||
static void encode_filter(Stream& stream, Filter const& filter, ImageEncoder const& encode_image)
|
||||
{
|
||||
filter.impl().operation.visit(
|
||||
[&](FilterImpl::Arithmetic const& op) {
|
||||
MUST(stream.write_value(FilterImpl::OperationType::Arithmetic));
|
||||
encode_optional_filter(stream, op.background, encode_image);
|
||||
encode_optional_filter(stream, op.foreground, encode_image);
|
||||
MUST(stream.write_value(op.k1));
|
||||
MUST(stream.write_value(op.k2));
|
||||
MUST(stream.write_value(op.k3));
|
||||
MUST(stream.write_value(op.k4));
|
||||
},
|
||||
[&](FilterImpl::Compose const& op) {
|
||||
MUST(stream.write_value(FilterImpl::OperationType::Compose));
|
||||
encode_filter(stream, op.outer, encode_image);
|
||||
encode_filter(stream, op.inner, encode_image);
|
||||
},
|
||||
[&](FilterImpl::Blend const& op) {
|
||||
MUST(stream.write_value(FilterImpl::OperationType::Blend));
|
||||
encode_optional_filter(stream, op.background, encode_image);
|
||||
encode_optional_filter(stream, op.foreground, encode_image);
|
||||
MUST(stream.write_value(op.mode));
|
||||
},
|
||||
[&](FilterImpl::Flood const& op) {
|
||||
MUST(stream.write_value(FilterImpl::OperationType::Flood));
|
||||
write_color(stream, op.color);
|
||||
MUST(stream.write_value(op.opacity));
|
||||
},
|
||||
[&](FilterImpl::DisplacementMap const& op) {
|
||||
MUST(stream.write_value(FilterImpl::OperationType::DisplacementMap));
|
||||
encode_optional_filter(stream, op.color, encode_image);
|
||||
encode_optional_filter(stream, op.displacement, encode_image);
|
||||
MUST(stream.write_value(op.scale));
|
||||
MUST(stream.write_value(op.x_channel_selector));
|
||||
MUST(stream.write_value(op.y_channel_selector));
|
||||
},
|
||||
[&](FilterImpl::DropShadow const& op) {
|
||||
MUST(stream.write_value(FilterImpl::OperationType::DropShadow));
|
||||
MUST(stream.write_value(op.offset_x));
|
||||
MUST(stream.write_value(op.offset_y));
|
||||
MUST(stream.write_value(op.radius));
|
||||
write_color(stream, op.color);
|
||||
encode_optional_filter(stream, op.input, encode_image);
|
||||
},
|
||||
[&](FilterImpl::Blur const& op) {
|
||||
MUST(stream.write_value(FilterImpl::OperationType::Blur));
|
||||
MUST(stream.write_value(op.radius_x));
|
||||
MUST(stream.write_value(op.radius_y));
|
||||
encode_optional_filter(stream, op.input, encode_image);
|
||||
},
|
||||
[&](FilterImpl::ColorFilter const& op) {
|
||||
MUST(stream.write_value(FilterImpl::OperationType::ColorFilter));
|
||||
MUST(stream.write_value(op.type));
|
||||
MUST(stream.write_value(op.amount));
|
||||
encode_optional_filter(stream, op.input, encode_image);
|
||||
},
|
||||
[&](FilterImpl::ColorMatrix const& op) {
|
||||
MUST(stream.write_value(FilterImpl::OperationType::ColorMatrix));
|
||||
for (auto value : op.matrix)
|
||||
MUST(stream.write_value(value));
|
||||
encode_optional_filter(stream, op.input, encode_image);
|
||||
},
|
||||
[&](FilterImpl::ColorTable const& op) {
|
||||
MUST(stream.write_value(FilterImpl::OperationType::ColorTable));
|
||||
auto encode_optional_color_table = [&](Optional<ByteBuffer> const& bytes) {
|
||||
MUST(stream.write_value<bool>(bytes.has_value()));
|
||||
if (bytes.has_value())
|
||||
write_bytes(stream, *bytes);
|
||||
};
|
||||
encode_optional_color_table(op.a);
|
||||
encode_optional_color_table(op.r);
|
||||
encode_optional_color_table(op.g);
|
||||
encode_optional_color_table(op.b);
|
||||
encode_optional_filter(stream, op.input, encode_image);
|
||||
},
|
||||
[&](FilterImpl::Saturate const& op) {
|
||||
MUST(stream.write_value(FilterImpl::OperationType::Saturate));
|
||||
MUST(stream.write_value(op.value));
|
||||
encode_optional_filter(stream, op.input, encode_image);
|
||||
},
|
||||
[&](FilterImpl::HueRotate const& op) {
|
||||
MUST(stream.write_value(FilterImpl::OperationType::HueRotate));
|
||||
MUST(stream.write_value(op.angle_degrees));
|
||||
encode_optional_filter(stream, op.input, encode_image);
|
||||
},
|
||||
[&](FilterImpl::Image const& op) {
|
||||
MUST(stream.write_value(FilterImpl::OperationType::Image));
|
||||
MUST(stream.write_value<u64>(encode_image(op.frame)));
|
||||
write_int_rect(stream, op.src_rect);
|
||||
write_int_rect(stream, op.dest_rect);
|
||||
MUST(stream.write_value(op.scaling_mode));
|
||||
},
|
||||
[&](FilterImpl::Merge const& op) {
|
||||
MUST(stream.write_value(FilterImpl::OperationType::Merge));
|
||||
VERIFY(op.inputs.size() <= NumericLimits<u32>::max());
|
||||
MUST(stream.write_value<u32>(op.inputs.size()));
|
||||
for (auto const& input : op.inputs)
|
||||
encode_optional_filter(stream, input, encode_image);
|
||||
},
|
||||
[&](FilterImpl::Offset const& op) {
|
||||
MUST(stream.write_value(FilterImpl::OperationType::Offset));
|
||||
MUST(stream.write_value(op.dx));
|
||||
MUST(stream.write_value(op.dy));
|
||||
encode_optional_filter(stream, op.input, encode_image);
|
||||
},
|
||||
[&](FilterImpl::Erode const& op) {
|
||||
MUST(stream.write_value(FilterImpl::OperationType::Erode));
|
||||
MUST(stream.write_value(op.radius_x));
|
||||
MUST(stream.write_value(op.radius_y));
|
||||
encode_optional_filter(stream, op.input, encode_image);
|
||||
},
|
||||
[&](FilterImpl::Dilate const& op) {
|
||||
MUST(stream.write_value(FilterImpl::OperationType::Dilate));
|
||||
MUST(stream.write_value(op.radius_x));
|
||||
MUST(stream.write_value(op.radius_y));
|
||||
encode_optional_filter(stream, op.input, encode_image);
|
||||
},
|
||||
[&](FilterImpl::Turbulence const& op) {
|
||||
MUST(stream.write_value(FilterImpl::OperationType::Turbulence));
|
||||
MUST(stream.write_value(op.turbulence_type));
|
||||
MUST(stream.write_value(op.base_frequency_x));
|
||||
MUST(stream.write_value(op.base_frequency_y));
|
||||
MUST(stream.write_value(op.num_octaves));
|
||||
MUST(stream.write_value(op.seed));
|
||||
write_int_size(stream, op.tile_stitch_size);
|
||||
});
|
||||
}
|
||||
|
||||
static Optional<Filter> decode_optional_filter(Stream&, ImageDecoder const&);
|
||||
|
||||
static Filter decode_filter(Stream& stream, ImageDecoder const& decode_image)
|
||||
{
|
||||
auto operation_type = MUST(stream.read_value<FilterImpl::OperationType>());
|
||||
switch (operation_type) {
|
||||
case FilterImpl::OperationType::Arithmetic: {
|
||||
auto background = decode_optional_filter(stream, decode_image);
|
||||
auto foreground = decode_optional_filter(stream, decode_image);
|
||||
auto k1 = MUST(stream.read_value<float>());
|
||||
auto k2 = MUST(stream.read_value<float>());
|
||||
auto k3 = MUST(stream.read_value<float>());
|
||||
auto k4 = MUST(stream.read_value<float>());
|
||||
return Filter::arithmetic(background, foreground, k1, k2, k3, k4);
|
||||
}
|
||||
case FilterImpl::OperationType::Compose: {
|
||||
auto outer = decode_filter(stream, decode_image);
|
||||
auto inner = decode_filter(stream, decode_image);
|
||||
return Filter::compose(outer, inner);
|
||||
}
|
||||
case FilterImpl::OperationType::Blend: {
|
||||
auto background = decode_optional_filter(stream, decode_image);
|
||||
auto foreground = decode_optional_filter(stream, decode_image);
|
||||
auto mode = MUST(stream.read_value<Gfx::CompositingAndBlendingOperator>());
|
||||
return Filter::blend(background, foreground, mode);
|
||||
}
|
||||
case FilterImpl::OperationType::Flood: {
|
||||
auto color = read_color(stream);
|
||||
auto opacity = MUST(stream.read_value<float>());
|
||||
return Filter::flood(color, opacity);
|
||||
}
|
||||
case FilterImpl::OperationType::DisplacementMap: {
|
||||
auto color = decode_optional_filter(stream, decode_image);
|
||||
auto displacement = decode_optional_filter(stream, decode_image);
|
||||
auto scale = MUST(stream.read_value<float>());
|
||||
auto x_channel_selector = MUST(stream.read_value<ChannelSelector>());
|
||||
auto y_channel_selector = MUST(stream.read_value<ChannelSelector>());
|
||||
return Filter::displacement_map(color, displacement, scale, x_channel_selector, y_channel_selector);
|
||||
}
|
||||
case FilterImpl::OperationType::DropShadow: {
|
||||
auto offset_x = MUST(stream.read_value<float>());
|
||||
auto offset_y = MUST(stream.read_value<float>());
|
||||
auto radius = MUST(stream.read_value<float>());
|
||||
auto color = read_color(stream);
|
||||
auto input = decode_optional_filter(stream, decode_image);
|
||||
return Filter::drop_shadow(offset_x, offset_y, radius, color, input);
|
||||
}
|
||||
case FilterImpl::OperationType::Blur: {
|
||||
auto radius_x = MUST(stream.read_value<float>());
|
||||
auto radius_y = MUST(stream.read_value<float>());
|
||||
auto input = decode_optional_filter(stream, decode_image);
|
||||
return Filter::blur(radius_x, radius_y, input);
|
||||
}
|
||||
case FilterImpl::OperationType::ColorFilter: {
|
||||
auto type = MUST(stream.read_value<ColorFilterType>());
|
||||
auto amount = MUST(stream.read_value<float>());
|
||||
auto input = decode_optional_filter(stream, decode_image);
|
||||
return Filter::color(type, amount, input);
|
||||
}
|
||||
case FilterImpl::OperationType::ColorMatrix: {
|
||||
Array<float, 20> matrix_values;
|
||||
for (auto& value : matrix_values)
|
||||
value = MUST(stream.read_value<float>());
|
||||
auto input = decode_optional_filter(stream, decode_image);
|
||||
return Filter::color_matrix(matrix_values.data(), input);
|
||||
}
|
||||
case FilterImpl::OperationType::ColorTable: {
|
||||
auto decode_optional_color_table = [&]() -> Optional<ByteBuffer> {
|
||||
auto has_value = MUST(stream.read_value<bool>());
|
||||
if (!has_value)
|
||||
return {};
|
||||
auto bytes = read_bytes(stream);
|
||||
VERIFY(bytes.size() == 256);
|
||||
return Optional<ByteBuffer> { move(bytes) };
|
||||
};
|
||||
auto a = decode_optional_color_table();
|
||||
auto r = decode_optional_color_table();
|
||||
auto g = decode_optional_color_table();
|
||||
auto b = decode_optional_color_table();
|
||||
auto input = decode_optional_filter(stream, decode_image);
|
||||
return Filter::color_table(a.has_value() ? Optional<ReadonlyBytes>(a->bytes()) : Optional<ReadonlyBytes> {},
|
||||
r.has_value() ? Optional<ReadonlyBytes>(r->bytes()) : Optional<ReadonlyBytes> {},
|
||||
g.has_value() ? Optional<ReadonlyBytes>(g->bytes()) : Optional<ReadonlyBytes> {},
|
||||
b.has_value() ? Optional<ReadonlyBytes>(b->bytes()) : Optional<ReadonlyBytes> {},
|
||||
input);
|
||||
}
|
||||
case FilterImpl::OperationType::Saturate: {
|
||||
auto value = MUST(stream.read_value<float>());
|
||||
auto input = decode_optional_filter(stream, decode_image);
|
||||
return Filter::saturate(value, input);
|
||||
}
|
||||
case FilterImpl::OperationType::HueRotate: {
|
||||
auto angle_degrees = MUST(stream.read_value<float>());
|
||||
auto input = decode_optional_filter(stream, decode_image);
|
||||
return Filter::hue_rotate(angle_degrees, input);
|
||||
}
|
||||
case FilterImpl::OperationType::Image: {
|
||||
auto image_id = MUST(stream.read_value<u64>());
|
||||
auto frame = decode_image(image_id);
|
||||
auto src_rect = read_int_rect(stream);
|
||||
auto dest_rect = read_int_rect(stream);
|
||||
auto scaling_mode = MUST(stream.read_value<Gfx::ScalingMode>());
|
||||
return Filter::image(frame, src_rect, dest_rect, scaling_mode);
|
||||
}
|
||||
case FilterImpl::OperationType::Merge: {
|
||||
Vector<Optional<Filter>> inputs;
|
||||
auto size = MUST(stream.read_value<u32>());
|
||||
inputs.ensure_capacity(size);
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
inputs.unchecked_append(decode_optional_filter(stream, decode_image));
|
||||
return Filter::merge(inputs);
|
||||
}
|
||||
case FilterImpl::OperationType::Offset: {
|
||||
auto dx = MUST(stream.read_value<float>());
|
||||
auto dy = MUST(stream.read_value<float>());
|
||||
auto input = decode_optional_filter(stream, decode_image);
|
||||
return Filter::offset(dx, dy, input);
|
||||
}
|
||||
case FilterImpl::OperationType::Erode: {
|
||||
auto radius_x = MUST(stream.read_value<float>());
|
||||
auto radius_y = MUST(stream.read_value<float>());
|
||||
auto input = decode_optional_filter(stream, decode_image);
|
||||
return Filter::erode(radius_x, radius_y, input);
|
||||
}
|
||||
case FilterImpl::OperationType::Dilate: {
|
||||
auto radius_x = MUST(stream.read_value<float>());
|
||||
auto radius_y = MUST(stream.read_value<float>());
|
||||
auto input = decode_optional_filter(stream, decode_image);
|
||||
return Filter::dilate(radius_x, radius_y, input);
|
||||
}
|
||||
case FilterImpl::OperationType::Turbulence: {
|
||||
auto turbulence_type = MUST(stream.read_value<TurbulenceType>());
|
||||
auto base_frequency_x = MUST(stream.read_value<float>());
|
||||
auto base_frequency_y = MUST(stream.read_value<float>());
|
||||
auto num_octaves = MUST(stream.read_value<i32>());
|
||||
auto seed = MUST(stream.read_value<float>());
|
||||
auto tile_stitch_size = read_int_size(stream);
|
||||
return Filter::turbulence(turbulence_type, base_frequency_x, base_frequency_y, num_octaves, seed, tile_stitch_size);
|
||||
}
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
static Optional<Filter> decode_optional_filter(Stream& stream, ImageDecoder const& decode_image)
|
||||
{
|
||||
auto has_value = MUST(stream.read_value<bool>());
|
||||
if (!has_value)
|
||||
return {};
|
||||
return decode_filter(stream, decode_image);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ByteBuffer serialize_filter(Filter const& filter, Function<u64(Gfx::DecodedImageFrame const&)> const& encode_image)
|
||||
{
|
||||
AllocatingMemoryStream stream;
|
||||
encode_filter(stream, filter, encode_image);
|
||||
auto buffer = MUST(ByteBuffer::create_uninitialized(stream.used_buffer_size()));
|
||||
MUST(stream.read_until_filled(buffer));
|
||||
return buffer;
|
||||
}
|
||||
|
||||
Filter deserialize_filter(ReadonlyBytes bytes, Function<Gfx::DecodedImageFrame(u64)> const& decode_image)
|
||||
{
|
||||
FixedMemoryStream stream { bytes };
|
||||
auto filter = decode_filter(stream, decode_image);
|
||||
VERIFY(stream.is_eof());
|
||||
return filter;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,14 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/Error.h>
|
||||
#include <AK/Function.h>
|
||||
#include <AK/NonnullOwnPtr.h>
|
||||
#include <AK/Types.h>
|
||||
#include <LibGfx/Color.h>
|
||||
#include <LibGfx/CompositingAndBlendingOperator.h>
|
||||
#include <LibGfx/DecodedImageFrame.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
#include <LibGfx/ScalingMode.h>
|
||||
|
|
@ -44,6 +48,8 @@ class Filter {
|
|||
public:
|
||||
Filter(Filter const&);
|
||||
Filter& operator=(Filter const&);
|
||||
Filter(Filter&&);
|
||||
Filter& operator=(Filter&&);
|
||||
|
||||
~Filter();
|
||||
|
||||
|
|
@ -67,12 +73,13 @@ public:
|
|||
static Filter turbulence(TurbulenceType turbulence_type, float base_frequency_x, float base_frequency_y, i32 num_octaves, float seed, Gfx::IntSize const& tile_stitch_size);
|
||||
|
||||
FilterImpl const& impl() const;
|
||||
u64 id() const { return m_id; }
|
||||
|
||||
private:
|
||||
Filter(NonnullOwnPtr<FilterImpl>&&);
|
||||
u64 m_id { 0 };
|
||||
NonnullOwnPtr<FilterImpl> m_impl;
|
||||
};
|
||||
|
||||
ByteBuffer serialize_filter(Filter const&, Function<u64(Gfx::DecodedImageFrame const&)> const& encode_image);
|
||||
Filter deserialize_filter(ReadonlyBytes, Function<Gfx::DecodedImageFrame(u64)> const& decode_image);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,23 +6,182 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Array.h>
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/NonnullOwnPtr.h>
|
||||
#include <core/SkColorFilter.h>
|
||||
#include <effects/SkImageFilters.h>
|
||||
#include <AK/Optional.h>
|
||||
#include <AK/Variant.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGfx/Filter.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
struct FilterImpl {
|
||||
sk_sp<SkImageFilter> filter;
|
||||
struct Arithmetic {
|
||||
Optional<Filter> background;
|
||||
Optional<Filter> foreground;
|
||||
float k1 { 0.0f };
|
||||
float k2 { 0.0f };
|
||||
float k3 { 0.0f };
|
||||
float k4 { 0.0f };
|
||||
};
|
||||
|
||||
static NonnullOwnPtr<FilterImpl> create(sk_sp<SkImageFilter> filter)
|
||||
struct Compose {
|
||||
Filter outer;
|
||||
Filter inner;
|
||||
};
|
||||
|
||||
struct Blend {
|
||||
Optional<Filter> background;
|
||||
Optional<Filter> foreground;
|
||||
Gfx::CompositingAndBlendingOperator mode { Gfx::CompositingAndBlendingOperator::Normal };
|
||||
};
|
||||
|
||||
struct Flood {
|
||||
Gfx::Color color;
|
||||
float opacity { 1.0f };
|
||||
};
|
||||
|
||||
struct DisplacementMap {
|
||||
Optional<Filter> color;
|
||||
Optional<Filter> displacement;
|
||||
float scale { 0.0f };
|
||||
ChannelSelector x_channel_selector { ChannelSelector::Alpha };
|
||||
ChannelSelector y_channel_selector { ChannelSelector::Alpha };
|
||||
};
|
||||
|
||||
struct DropShadow {
|
||||
float offset_x { 0.0f };
|
||||
float offset_y { 0.0f };
|
||||
float radius { 0.0f };
|
||||
Gfx::Color color;
|
||||
Optional<Filter> input;
|
||||
};
|
||||
|
||||
struct Blur {
|
||||
float radius_x { 0.0f };
|
||||
float radius_y { 0.0f };
|
||||
Optional<Filter> input;
|
||||
};
|
||||
|
||||
struct ColorFilter {
|
||||
ColorFilterType type { ColorFilterType::Brightness };
|
||||
float amount { 0.0f };
|
||||
Optional<Filter> input;
|
||||
};
|
||||
|
||||
struct ColorMatrix {
|
||||
Array<float, 20> matrix;
|
||||
Optional<Filter> input;
|
||||
};
|
||||
|
||||
struct ColorTable {
|
||||
Optional<ByteBuffer> a;
|
||||
Optional<ByteBuffer> r;
|
||||
Optional<ByteBuffer> g;
|
||||
Optional<ByteBuffer> b;
|
||||
Optional<Filter> input;
|
||||
};
|
||||
|
||||
struct Saturate {
|
||||
float value { 0.0f };
|
||||
Optional<Filter> input;
|
||||
};
|
||||
|
||||
struct HueRotate {
|
||||
float angle_degrees { 0.0f };
|
||||
Optional<Filter> input;
|
||||
};
|
||||
|
||||
struct Image {
|
||||
Gfx::DecodedImageFrame frame;
|
||||
Gfx::IntRect src_rect;
|
||||
Gfx::IntRect dest_rect;
|
||||
Gfx::ScalingMode scaling_mode { Gfx::ScalingMode::NearestNeighbor };
|
||||
};
|
||||
|
||||
struct Merge {
|
||||
Vector<Optional<Filter>> inputs;
|
||||
};
|
||||
|
||||
struct Offset {
|
||||
float dx { 0.0f };
|
||||
float dy { 0.0f };
|
||||
Optional<Filter> input;
|
||||
};
|
||||
|
||||
struct Erode {
|
||||
float radius_x { 0.0f };
|
||||
float radius_y { 0.0f };
|
||||
Optional<Filter> input;
|
||||
};
|
||||
|
||||
struct Dilate {
|
||||
float radius_x { 0.0f };
|
||||
float radius_y { 0.0f };
|
||||
Optional<Filter> input;
|
||||
};
|
||||
|
||||
struct Turbulence {
|
||||
TurbulenceType turbulence_type { TurbulenceType::Turbulence };
|
||||
float base_frequency_x { 0.0f };
|
||||
float base_frequency_y { 0.0f };
|
||||
i32 num_octaves { 0 };
|
||||
float seed { 0.0f };
|
||||
Gfx::IntSize tile_stitch_size;
|
||||
};
|
||||
|
||||
using Operation = Variant<
|
||||
Arithmetic,
|
||||
Compose,
|
||||
Blend,
|
||||
Flood,
|
||||
DisplacementMap,
|
||||
DropShadow,
|
||||
Blur,
|
||||
ColorFilter,
|
||||
ColorMatrix,
|
||||
ColorTable,
|
||||
Saturate,
|
||||
HueRotate,
|
||||
Image,
|
||||
Merge,
|
||||
Offset,
|
||||
Erode,
|
||||
Dilate,
|
||||
Turbulence>;
|
||||
|
||||
enum class OperationType : u8 {
|
||||
Arithmetic,
|
||||
Compose,
|
||||
Blend,
|
||||
Flood,
|
||||
DisplacementMap,
|
||||
DropShadow,
|
||||
Blur,
|
||||
ColorFilter,
|
||||
ColorMatrix,
|
||||
ColorTable,
|
||||
Saturate,
|
||||
HueRotate,
|
||||
Image,
|
||||
Merge,
|
||||
Offset,
|
||||
Erode,
|
||||
Dilate,
|
||||
Turbulence,
|
||||
};
|
||||
|
||||
Operation operation;
|
||||
|
||||
static NonnullOwnPtr<FilterImpl> create(Operation operation)
|
||||
{
|
||||
return adopt_own(*new FilterImpl(move(filter)));
|
||||
return adopt_own(*new FilterImpl(move(operation)));
|
||||
}
|
||||
|
||||
NonnullOwnPtr<FilterImpl> clone() const
|
||||
{
|
||||
return adopt_own(*new FilterImpl(filter));
|
||||
return adopt_own(*new FilterImpl(operation));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -5,17 +5,23 @@
|
|||
*/
|
||||
|
||||
#include <AK/Assertions.h>
|
||||
#include <AK/Math.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/ColorSpace.h>
|
||||
#include <LibGfx/DecodedImageFrame.h>
|
||||
#include <LibGfx/Filter.h>
|
||||
#include <LibGfx/FilterImpl.h>
|
||||
#include <LibGfx/SkiaUtils.h>
|
||||
#include <core/SkBitmap.h>
|
||||
#include <core/SkBlender.h>
|
||||
#include <core/SkColorFilter.h>
|
||||
#include <core/SkColorSpace.h>
|
||||
#include <core/SkImage.h>
|
||||
#include <core/SkImageFilter.h>
|
||||
#include <core/SkString.h>
|
||||
#include <effects/SkColorMatrix.h>
|
||||
#include <effects/SkImageFilters.h>
|
||||
#include <effects/SkPerlinNoiseShader.h>
|
||||
#include <effects/SkRuntimeEffect.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
|
@ -27,7 +33,253 @@ SkPath to_skia_path(Path const& path)
|
|||
|
||||
sk_sp<SkImageFilter> to_skia_image_filter(Gfx::Filter const& filter)
|
||||
{
|
||||
return filter.impl().filter;
|
||||
auto to_optional_skia_image_filter = [](Optional<Gfx::Filter> const& input) -> sk_sp<SkImageFilter> {
|
||||
if (!input.has_value())
|
||||
return nullptr;
|
||||
return to_skia_image_filter(input.value());
|
||||
};
|
||||
|
||||
return filter.impl().operation.visit(
|
||||
[&](FilterImpl::Arithmetic const& op) -> sk_sp<SkImageFilter> {
|
||||
auto background = to_optional_skia_image_filter(op.background);
|
||||
auto foreground = to_optional_skia_image_filter(op.foreground);
|
||||
return SkImageFilters::Arithmetic(
|
||||
SkFloatToScalar(op.k1),
|
||||
SkFloatToScalar(op.k2),
|
||||
SkFloatToScalar(op.k3),
|
||||
SkFloatToScalar(op.k4),
|
||||
false,
|
||||
move(background),
|
||||
move(foreground));
|
||||
},
|
||||
[&](FilterImpl::Compose const& op) -> sk_sp<SkImageFilter> {
|
||||
auto outer = to_skia_image_filter(op.outer);
|
||||
auto inner = to_skia_image_filter(op.inner);
|
||||
return SkImageFilters::Compose(outer, inner);
|
||||
},
|
||||
[&](FilterImpl::Blend const& op) -> sk_sp<SkImageFilter> {
|
||||
auto background = to_optional_skia_image_filter(op.background);
|
||||
auto foreground = to_optional_skia_image_filter(op.foreground);
|
||||
return SkImageFilters::Blend(to_skia_blender(op.mode), background, foreground);
|
||||
},
|
||||
[&](FilterImpl::Flood const& op) -> sk_sp<SkImageFilter> {
|
||||
auto color = to_skia_color(op.color);
|
||||
color = SkColorSetA(color, static_cast<u8>(op.opacity * 255));
|
||||
return SkImageFilters::Shader(SkShaders::Color(color));
|
||||
},
|
||||
[&](FilterImpl::DisplacementMap const& op) -> sk_sp<SkImageFilter> {
|
||||
auto color = to_optional_skia_image_filter(op.color);
|
||||
auto displacement = to_optional_skia_image_filter(op.displacement);
|
||||
auto convert_channel_selector = [](ChannelSelector channel_selector) {
|
||||
switch (channel_selector) {
|
||||
case ChannelSelector::Red:
|
||||
return SkColorChannel::kR;
|
||||
case ChannelSelector::Green:
|
||||
return SkColorChannel::kG;
|
||||
case ChannelSelector::Blue:
|
||||
return SkColorChannel::kB;
|
||||
case ChannelSelector::Alpha:
|
||||
return SkColorChannel::kA;
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
};
|
||||
return SkImageFilters::DisplacementMap(
|
||||
convert_channel_selector(op.x_channel_selector),
|
||||
convert_channel_selector(op.y_channel_selector),
|
||||
op.scale,
|
||||
displacement,
|
||||
color);
|
||||
},
|
||||
[&](FilterImpl::DropShadow const& op) -> sk_sp<SkImageFilter> {
|
||||
auto input = to_optional_skia_image_filter(op.input);
|
||||
return SkImageFilters::DropShadow(op.offset_x, op.offset_y, op.radius, op.radius, to_skia_color(op.color), input);
|
||||
},
|
||||
[&](FilterImpl::Blur const& op) -> sk_sp<SkImageFilter> {
|
||||
auto input = to_optional_skia_image_filter(op.input);
|
||||
return SkImageFilters::Blur(op.radius_x, op.radius_y, input);
|
||||
},
|
||||
[&](FilterImpl::ColorFilter const& op) -> sk_sp<SkImageFilter> {
|
||||
auto input = to_optional_skia_image_filter(op.input);
|
||||
sk_sp<SkColorFilter> color_filter;
|
||||
|
||||
// Matrices are taken from https://drafts.fxtf.org/filter-effects-1/#FilterPrimitiveRepresentation
|
||||
switch (op.type) {
|
||||
case ColorFilterType::Grayscale: {
|
||||
auto amount = op.amount;
|
||||
float matrix[20] = {
|
||||
0.2126f + 0.7874f * (1 - amount), 0.7152f - 0.7152f * (1 - amount),
|
||||
0.0722f - 0.0722f * (1 - amount), 0, 0,
|
||||
0.2126f - 0.2126f * (1 - amount), 0.7152f + 0.2848f * (1 - amount),
|
||||
0.0722f - 0.0722f * (1 - amount), 0, 0,
|
||||
0.2126f - 0.2126f * (1 - amount), 0.7152f - 0.7152f * (1 - amount),
|
||||
0.0722f + 0.9278f * (1 - amount), 0, 0,
|
||||
0, 0, 0, 1, 0
|
||||
};
|
||||
color_filter = SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kYes);
|
||||
break;
|
||||
}
|
||||
case Gfx::ColorFilterType::Brightness: {
|
||||
auto amount = op.amount;
|
||||
float matrix[20] = {
|
||||
amount, 0, 0, 0, 0,
|
||||
0, amount, 0, 0, 0,
|
||||
0, 0, amount, 0, 0,
|
||||
0, 0, 0, 1, 0
|
||||
};
|
||||
color_filter = SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kNo);
|
||||
break;
|
||||
}
|
||||
case Gfx::ColorFilterType::Contrast: {
|
||||
auto amount = op.amount;
|
||||
float intercept = -(0.5f * amount) + 0.5f;
|
||||
float matrix[20] = {
|
||||
amount, 0, 0, 0, intercept,
|
||||
0, amount, 0, 0, intercept,
|
||||
0, 0, amount, 0, intercept,
|
||||
0, 0, 0, 1, 0
|
||||
};
|
||||
color_filter = SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kNo);
|
||||
break;
|
||||
}
|
||||
case Gfx::ColorFilterType::Invert: {
|
||||
auto amount = op.amount;
|
||||
float matrix[20] = {
|
||||
1 - 2 * amount, 0, 0, 0, amount,
|
||||
0, 1 - 2 * amount, 0, 0, amount,
|
||||
0, 0, 1 - 2 * amount, 0, amount,
|
||||
0, 0, 0, 1, 0
|
||||
};
|
||||
color_filter = SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kYes);
|
||||
break;
|
||||
}
|
||||
case Gfx::ColorFilterType::Opacity: {
|
||||
auto amount = op.amount;
|
||||
float matrix[20] = {
|
||||
1, 0, 0, 0, 0,
|
||||
0, 1, 0, 0, 0,
|
||||
0, 0, 1, 0, 0,
|
||||
0, 0, 0, amount, 0
|
||||
};
|
||||
color_filter = SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kYes);
|
||||
break;
|
||||
}
|
||||
case Gfx::ColorFilterType::Sepia: {
|
||||
auto amount = op.amount;
|
||||
float matrix[20] = {
|
||||
0.393f + 0.607f * (1 - amount), 0.769f - 0.769f * (1 - amount), 0.189f - 0.189f * (1 - amount), 0,
|
||||
0,
|
||||
0.349f - 0.349f * (1 - amount), 0.686f + 0.314f * (1 - amount), 0.168f - 0.168f * (1 - amount), 0,
|
||||
0,
|
||||
0.272f - 0.272f * (1 - amount), 0.534f - 0.534f * (1 - amount), 0.131f + 0.869f * (1 - amount), 0,
|
||||
0,
|
||||
0, 0, 0, 1, 0
|
||||
};
|
||||
color_filter = SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kYes);
|
||||
break;
|
||||
}
|
||||
case Gfx::ColorFilterType::Saturate: {
|
||||
auto amount = op.amount;
|
||||
float matrix[20] = {
|
||||
0.213f + 0.787f * amount, 0.715f - 0.715f * amount, 0.072f - 0.072f * amount, 0, 0,
|
||||
0.213f - 0.213f * amount, 0.715f + 0.285f * amount, 0.072f - 0.072f * amount, 0, 0,
|
||||
0.213f - 0.213f * amount, 0.715f - 0.715f * amount, 0.072f + 0.928f * amount, 0, 0,
|
||||
0, 0, 0, 1, 0
|
||||
};
|
||||
color_filter = SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kNo);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
return SkImageFilters::ColorFilter(color_filter, input);
|
||||
},
|
||||
[&](FilterImpl::ColorMatrix const& op) -> sk_sp<SkImageFilter> {
|
||||
auto input = to_optional_skia_image_filter(op.input);
|
||||
return SkImageFilters::ColorFilter(SkColorFilters::Matrix(op.matrix.data()), input);
|
||||
},
|
||||
[&](FilterImpl::ColorTable const& op) -> sk_sp<SkImageFilter> {
|
||||
auto input = to_optional_skia_image_filter(op.input);
|
||||
auto* a_table = op.a.has_value() ? op.a->data() : nullptr;
|
||||
auto* r_table = op.r.has_value() ? op.r->data() : nullptr;
|
||||
auto* g_table = op.g.has_value() ? op.g->data() : nullptr;
|
||||
auto* b_table = op.b.has_value() ? op.b->data() : nullptr;
|
||||
|
||||
// Color tables are applied in linear space by default, so we need to convert twice.
|
||||
// FIXME: support sRGB space as well (i.e. don't perform these conversions).
|
||||
auto srgb_to_linear = SkImageFilters::ColorFilter(SkColorFilters::SRGBToLinearGamma(), input);
|
||||
auto color_table = SkImageFilters::ColorFilter(SkColorFilters::TableARGB(a_table, r_table, g_table, b_table), srgb_to_linear);
|
||||
return SkImageFilters::ColorFilter(SkColorFilters::LinearToSRGBGamma(), color_table);
|
||||
},
|
||||
[&](FilterImpl::Saturate const& op) -> sk_sp<SkImageFilter> {
|
||||
auto input = to_optional_skia_image_filter(op.input);
|
||||
SkColorMatrix matrix;
|
||||
matrix.setSaturation(op.value);
|
||||
return SkImageFilters::ColorFilter(SkColorFilters::Matrix(matrix), input);
|
||||
},
|
||||
[&](FilterImpl::HueRotate const& op) -> sk_sp<SkImageFilter> {
|
||||
auto input = to_optional_skia_image_filter(op.input);
|
||||
float radians = AK::to_radians(op.angle_degrees);
|
||||
auto cosA = cos(radians);
|
||||
auto sinA = sin(radians);
|
||||
|
||||
auto a00 = 0.213f + cosA * 0.787f - sinA * 0.213f;
|
||||
auto a01 = 0.715f - cosA * 0.715f - sinA * 0.715f;
|
||||
auto a02 = 0.072f - cosA * 0.072f + sinA * 0.928f;
|
||||
auto a10 = 0.213f - cosA * 0.213f + sinA * 0.143f;
|
||||
auto a11 = 0.715f + cosA * 0.285f + sinA * 0.140f;
|
||||
auto a12 = 0.072f - cosA * 0.072f - sinA * 0.283f;
|
||||
auto a20 = 0.213f - cosA * 0.213f - sinA * 0.787f;
|
||||
auto a21 = 0.715f - cosA * 0.715f + sinA * 0.715f;
|
||||
auto a22 = 0.072f + cosA * 0.928f + sinA * 0.072f;
|
||||
|
||||
float matrix[20] = {
|
||||
a00, a01, a02, 0, 0,
|
||||
a10, a11, a12, 0, 0,
|
||||
a20, a21, a22, 0, 0,
|
||||
0, 0, 0, 1, 0
|
||||
};
|
||||
return SkImageFilters::ColorFilter(SkColorFilters::Matrix(matrix, SkColorFilters::Clamp::kNo), input);
|
||||
},
|
||||
[&](FilterImpl::Image const& op) -> sk_sp<SkImageFilter> {
|
||||
auto skia_src_rect = to_skia_rect(op.src_rect);
|
||||
auto skia_dest_rect = to_skia_rect(op.dest_rect);
|
||||
auto sampling_options = to_skia_sampling_options(op.scaling_mode);
|
||||
auto image = sk_image_from_bitmap(op.frame.bitmap(), op.frame.color_space());
|
||||
return SkImageFilters::Image(move(image), skia_src_rect, skia_dest_rect, sampling_options);
|
||||
},
|
||||
[&](FilterImpl::Merge const& op) -> sk_sp<SkImageFilter> {
|
||||
Vector<sk_sp<SkImageFilter>> filters;
|
||||
filters.ensure_capacity(op.inputs.size());
|
||||
for (auto const& input : op.inputs)
|
||||
filters.unchecked_append(to_optional_skia_image_filter(input));
|
||||
return SkImageFilters::Merge(filters.data(), filters.size());
|
||||
},
|
||||
[&](FilterImpl::Offset const& op) -> sk_sp<SkImageFilter> {
|
||||
auto input = to_optional_skia_image_filter(op.input);
|
||||
return SkImageFilters::Offset(op.dx, op.dy, input);
|
||||
},
|
||||
[&](FilterImpl::Erode const& op) -> sk_sp<SkImageFilter> {
|
||||
auto input = to_optional_skia_image_filter(op.input);
|
||||
return SkImageFilters::Erode(op.radius_x, op.radius_y, input);
|
||||
},
|
||||
[&](FilterImpl::Dilate const& op) -> sk_sp<SkImageFilter> {
|
||||
auto input = to_optional_skia_image_filter(op.input);
|
||||
return SkImageFilters::Dilate(op.radius_x, op.radius_y, input);
|
||||
},
|
||||
[&](FilterImpl::Turbulence const& op) -> sk_sp<SkImageFilter> {
|
||||
sk_sp<SkShader> turbulence_shader = [&] {
|
||||
auto skia_size = SkISize::Make(op.tile_stitch_size.width(), op.tile_stitch_size.height());
|
||||
switch (op.turbulence_type) {
|
||||
case TurbulenceType::Turbulence:
|
||||
return SkShaders::MakeTurbulence(op.base_frequency_x, op.base_frequency_y, op.num_octaves, op.seed, &skia_size);
|
||||
case TurbulenceType::FractalNoise:
|
||||
return SkShaders::MakeFractalNoise(op.base_frequency_x, op.base_frequency_y, op.num_octaves, op.seed, &skia_size);
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
}();
|
||||
return SkImageFilters::Shader(move(turbulence_shader));
|
||||
});
|
||||
}
|
||||
|
||||
sk_sp<SkImage> sk_image_from_bitmap(Bitmap const& bitmap, ColorSpace const& color_space)
|
||||
|
|
|
|||
|
|
@ -167,7 +167,7 @@ void DisplayListPlayer::execute_impl(
|
|||
.opacity = effects.opacity,
|
||||
.compositing_and_blending_operator = effects.blend_mode,
|
||||
.has_filter = effects.gfx_filter.has_value(),
|
||||
.filter_id = {},
|
||||
.filter_data = {},
|
||||
},
|
||||
effects.gfx_filter.has_value() ? &effects.gfx_filter.value() : nullptr);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -436,7 +436,7 @@ struct ApplyBackdropFilter {
|
|||
Gfx::IntRect backdrop_region;
|
||||
CornerRadii corner_radii;
|
||||
bool has_backdrop_filter { false };
|
||||
FilterResourceId backdrop_filter_id;
|
||||
DisplayListDataSpan backdrop_filter_data;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return backdrop_region; }
|
||||
|
||||
|
|
@ -536,7 +536,7 @@ struct ApplyEffects {
|
|||
float opacity { 1.0f };
|
||||
Gfx::CompositingAndBlendingOperator compositing_and_blending_operator { Gfx::CompositingAndBlendingOperator::Normal };
|
||||
bool has_filter { false };
|
||||
FilterResourceId filter_id;
|
||||
DisplayListDataSpan filter_data;
|
||||
bool has_mask_kind { false };
|
||||
Gfx::MaskKind mask_kind {};
|
||||
|
||||
|
|
|
|||
|
|
@ -727,8 +727,10 @@ void DisplayListPlayerSkia::apply_backdrop_filter(ApplyBackdropFilter const& com
|
|||
ScopeGuard guard = [&] { canvas.restore(); };
|
||||
|
||||
if (command.has_backdrop_filter) {
|
||||
auto image_filter = to_skia_image_filter(
|
||||
active_display_list().resource_storage().filter(command.backdrop_filter_id));
|
||||
auto filter = Gfx::deserialize_filter(inline_data(command.backdrop_filter_data), [&](u64 image_id) {
|
||||
return active_display_list().resource_storage().image_frame(ImageFrameResourceId { image_id });
|
||||
});
|
||||
auto image_filter = to_skia_image_filter(filter);
|
||||
canvas.saveLayer(SkCanvas::SaveLayerRec(nullptr, nullptr, image_filter.get(), 0));
|
||||
canvas.restore();
|
||||
}
|
||||
|
|
@ -863,8 +865,16 @@ void DisplayListPlayerSkia::apply_effects(ApplyEffects const& command, Gfx::Filt
|
|||
if (command.compositing_and_blending_operator != Gfx::CompositingAndBlendingOperator::Normal)
|
||||
paint.setBlender(Gfx::to_skia_blender(command.compositing_and_blending_operator));
|
||||
|
||||
if (command.has_filter)
|
||||
paint.setImageFilter(to_skia_image_filter(filter ? *filter : active_display_list().resource_storage().filter(command.filter_id)));
|
||||
Optional<Gfx::Filter> deserialized_filter;
|
||||
if (command.has_filter) {
|
||||
if (!filter) {
|
||||
deserialized_filter = Gfx::deserialize_filter(inline_data(command.filter_data), [&](u64 image_id) {
|
||||
return active_display_list().resource_storage().image_frame(ImageFrameResourceId { image_id });
|
||||
});
|
||||
filter = &deserialized_filter.value();
|
||||
}
|
||||
paint.setImageFilter(to_skia_image_filter(*filter));
|
||||
}
|
||||
|
||||
if (command.has_mask_kind && command.mask_kind == Gfx::MaskKind::Luminance)
|
||||
paint.setColorFilter(SkLumaColorFilter::Make());
|
||||
|
|
|
|||
|
|
@ -198,6 +198,18 @@ static DisplayListDataSpan append_path_data(CommandPayloadBuilder<Command>& payl
|
|||
return payload_builder.append_data(path_data.span(), alignof(u32));
|
||||
}
|
||||
|
||||
template<DisplayListCommand Command>
|
||||
static DisplayListDataSpan append_filter_data(
|
||||
CommandPayloadBuilder<Command>& payload_builder,
|
||||
DisplayListResourceStorage& resource_storage,
|
||||
Gfx::Filter const& filter)
|
||||
{
|
||||
auto filter_data = Gfx::serialize_filter(filter, [&](Gfx::DecodedImageFrame const& frame) {
|
||||
return resource_storage.add_image_frame(frame).value();
|
||||
});
|
||||
return payload_builder.append_data(filter_data, alignof(u32));
|
||||
}
|
||||
|
||||
void DisplayListRecorder::replay_cached_commands(DisplayListCommandSequence const& commands)
|
||||
{
|
||||
commands.for_each_command_header([&](DisplayListCommandHeader const& header, ReadonlyBytes) {
|
||||
|
|
@ -555,12 +567,16 @@ void DisplayListRecorder::apply_backdrop_filter(Gfx::IntRect const& backdrop_reg
|
|||
{
|
||||
if (backdrop_region.is_empty())
|
||||
return;
|
||||
append_command(ApplyBackdropFilter {
|
||||
.backdrop_region = backdrop_region,
|
||||
.corner_radii = corner_radii,
|
||||
.has_backdrop_filter = true,
|
||||
.backdrop_filter_id = resource_storage().add_filter(backdrop_filter),
|
||||
});
|
||||
CommandPayloadBuilder<ApplyBackdropFilter> payload_builder(m_display_list);
|
||||
auto filter_data = append_filter_data(payload_builder, resource_storage(), backdrop_filter);
|
||||
append_command(
|
||||
ApplyBackdropFilter {
|
||||
.backdrop_region = backdrop_region,
|
||||
.corner_radii = corner_radii,
|
||||
.has_backdrop_filter = true,
|
||||
.backdrop_filter_data = filter_data,
|
||||
},
|
||||
payload_builder.inline_data());
|
||||
}
|
||||
|
||||
void DisplayListRecorder::paint_outer_box_shadow(PaintOuterBoxShadow outer_box_shadow)
|
||||
|
|
@ -637,13 +653,19 @@ void DisplayListRecorder::paint_scrollbar(ScrollFrameIndex scroll_frame_index, G
|
|||
|
||||
void DisplayListRecorder::apply_effects(float opacity, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator, Optional<Gfx::Filter> filter, Optional<Gfx::MaskKind> mask_kind)
|
||||
{
|
||||
append_command(ApplyEffects {
|
||||
.opacity = opacity,
|
||||
.compositing_and_blending_operator = compositing_and_blending_operator,
|
||||
.has_filter = filter.has_value(),
|
||||
.filter_id = filter.has_value() ? resource_storage().add_filter(filter.value()) : FilterResourceId {},
|
||||
.has_mask_kind = mask_kind.has_value(),
|
||||
.mask_kind = mask_kind.value_or({}) });
|
||||
CommandPayloadBuilder<ApplyEffects> payload_builder(m_display_list);
|
||||
auto filter_data = filter.has_value()
|
||||
? append_filter_data(payload_builder, resource_storage(), filter.value())
|
||||
: DisplayListDataSpan {};
|
||||
append_command(
|
||||
ApplyEffects {
|
||||
.opacity = opacity,
|
||||
.compositing_and_blending_operator = compositing_and_blending_operator,
|
||||
.has_filter = filter.has_value(),
|
||||
.filter_data = filter_data,
|
||||
.has_mask_kind = mask_kind.has_value(),
|
||||
.mask_kind = mask_kind.value_or({}) },
|
||||
payload_builder.inline_data());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ AK_TYPEDEF_DISTINCT_ORDERED_ID(u64, FontResourceId);
|
|||
AK_TYPEDEF_DISTINCT_ORDERED_ID(u64, ImageFrameResourceId);
|
||||
AK_TYPEDEF_DISTINCT_ORDERED_ID(u64, ExternalContentResourceId);
|
||||
AK_TYPEDEF_DISTINCT_ORDERED_ID(u64, VideoFrameResourceId);
|
||||
AK_TYPEDEF_DISTINCT_ORDERED_ID(u64, FilterResourceId);
|
||||
AK_TYPEDEF_DISTINCT_ORDERED_ID(u64, DisplayListResourceId);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibGfx/Filter.h>
|
||||
#include <LibGfx/Font/Font.h>
|
||||
#include <LibWeb/Painting/DisplayList.h>
|
||||
#include <LibWeb/Painting/DisplayListResourceStorage.h>
|
||||
|
|
@ -49,15 +50,6 @@ VideoFrameResourceId DisplayListResourceStorage::add_video_frame_source(NonnullR
|
|||
return { id };
|
||||
}
|
||||
|
||||
FilterResourceId DisplayListResourceStorage::add_filter(Gfx::Filter const& filter)
|
||||
{
|
||||
auto id = filter.id();
|
||||
m_filters.ensure(id, [&] {
|
||||
return filter;
|
||||
});
|
||||
return { id };
|
||||
}
|
||||
|
||||
DisplayListResourceId DisplayListResourceStorage::add_display_list(NonnullRefPtr<DisplayList const> display_list)
|
||||
{
|
||||
auto id = display_list->id();
|
||||
|
|
@ -67,6 +59,12 @@ DisplayListResourceId DisplayListResourceStorage::add_display_list(NonnullRefPtr
|
|||
return { id };
|
||||
}
|
||||
|
||||
static ReadonlyBytes inline_data(ReadonlyBytes payload, DisplayListDataSpan span)
|
||||
{
|
||||
VERIFY(static_cast<size_t>(span.offset) + span.size <= payload.size());
|
||||
return payload.slice(span.offset, span.size);
|
||||
}
|
||||
|
||||
void DisplayListResourceStorage::append_referenced_resources_from(
|
||||
DisplayListResourceStorage const& source,
|
||||
ReadonlyBytes command_bytes)
|
||||
|
|
@ -86,13 +84,23 @@ void DisplayListResourceStorage::append_referenced_resources_from(
|
|||
&& command.paint_style.type == DisplayListPaintStyleType::Pattern)
|
||||
add_display_list(source.display_list(command.paint_style.pattern_tile_display_list_id));
|
||||
}
|
||||
if constexpr (requires { command.backdrop_filter_id; }) {
|
||||
if (command.has_backdrop_filter)
|
||||
add_filter(source.filter(command.backdrop_filter_id));
|
||||
if constexpr (requires { command.backdrop_filter_data; }) {
|
||||
if (command.has_backdrop_filter) {
|
||||
Gfx::deserialize_filter(inline_data(payload, command.backdrop_filter_data), [&](u64 image_id) {
|
||||
auto const& frame = source.image_frame(ImageFrameResourceId { image_id });
|
||||
add_image_frame(frame);
|
||||
return frame;
|
||||
});
|
||||
}
|
||||
}
|
||||
if constexpr (requires { command.filter_id; }) {
|
||||
if (command.has_filter)
|
||||
add_filter(source.filter(command.filter_id));
|
||||
if constexpr (requires { command.filter_data; }) {
|
||||
if (command.has_filter) {
|
||||
Gfx::deserialize_filter(inline_data(payload, command.filter_data), [&](u64 image_id) {
|
||||
auto const& frame = source.image_frame(ImageFrameResourceId { image_id });
|
||||
add_image_frame(frame);
|
||||
return frame;
|
||||
});
|
||||
}
|
||||
}
|
||||
if constexpr (requires { command.display_list_id; })
|
||||
add_display_list(source.display_list(command.display_list_id));
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@
|
|||
#include <AK/RefPtr.h>
|
||||
#include <AK/Span.h>
|
||||
#include <LibGfx/DecodedImageFrame.h>
|
||||
#include <LibGfx/Filter.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
#include <LibWeb/Forward.h>
|
||||
#include <LibWeb/Painting/DisplayListResourceIds.h>
|
||||
|
|
@ -35,7 +34,6 @@ public:
|
|||
ImageFrameResourceId add_image_frame(Gfx::DecodedImageFrame const&);
|
||||
ExternalContentResourceId add_external_content_source(NonnullRefPtr<ExternalContentSource const>);
|
||||
VideoFrameResourceId add_video_frame_source(NonnullRefPtr<VideoFrameSource const>);
|
||||
FilterResourceId add_filter(Gfx::Filter const&);
|
||||
DisplayListResourceId add_display_list(NonnullRefPtr<DisplayList const>);
|
||||
void append_referenced_resources_from(DisplayListResourceStorage const& source, ReadonlyBytes command_bytes);
|
||||
|
||||
|
|
@ -43,7 +41,6 @@ public:
|
|||
Gfx::DecodedImageFrame const& image_frame(ImageFrameResourceId id) const { return m_image_frames.get(id.value()).value(); }
|
||||
ExternalContentSource const& external_content_source(ExternalContentResourceId id) const { return *m_external_content_sources.get(id.value()).value(); }
|
||||
VideoFrameSource const& video_frame_source(VideoFrameResourceId id) const { return *m_video_frame_sources.get(id.value()).value(); }
|
||||
Gfx::Filter const& filter(FilterResourceId id) const { return m_filters.get(id.value()).value(); }
|
||||
DisplayList const& display_list(DisplayListResourceId id) const { return *m_display_lists.get(id.value()).value(); }
|
||||
|
||||
private:
|
||||
|
|
@ -53,7 +50,6 @@ private:
|
|||
HashMap<u64, Gfx::DecodedImageFrame> m_image_frames;
|
||||
HashMap<u64, NonnullRefPtr<ExternalContentSource const>> m_external_content_sources;
|
||||
HashMap<u64, NonnullRefPtr<VideoFrameSource const>> m_video_frame_sources;
|
||||
HashMap<u64, Gfx::Filter> m_filters;
|
||||
HashMap<u64, NonnullRefPtr<DisplayList const>> m_display_lists;
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue