ladybird/Libraries/LibGfx/CornerRadii.cpp
Aliaksandr Kalenik 65962ba03d LibGfx: Add IPC serialization for CornerRadii
The compositor IPC boundary needs to transfer CSS corner radii without
keeping their wire shape in Web-specific glue. Declare the IPC
specializations on the LibGfx types and encode each corner radius pair
next to the CornerRadii implementation.

This is preparatory work required to add IPC between the main and
compositor threads.
2026-05-21 11:45:06 +01:00

90 lines
3.3 KiB
C++

/*
* Copyright (c) 2020, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2021-2023, Sam Atkins <atkinssj@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Math.h>
#include <LibGfx/CornerRadii.h>
#include <LibIPC/Decoder.h>
#include <LibIPC/Encoder.h>
namespace Gfx {
// https://drafts.csswg.org/css-backgrounds/#shadow-shape
static void add_spread_distance_to_border_radius(int& border_radius, int spread_distance)
{
if (border_radius == 0 || spread_distance == 0)
return;
// To preserve the box's shape when spread is applied, the corner radii of the shadow are also increased (decreased,
// for inner shadows) from the border-box (padding-box) radii by adding (subtracting) the spread distance (and flooring
// at zero). However, in order to create a sharper corner when the border radius is small (and thus ensure continuity
// between round and sharp corners), when the border radius is less than the spread distance (or in the case of an inner
// shadow, less than the absolute value of a negative spread distance), the spread distance is first multiplied by the
// proportion 1 + (r-1)^3, where r is the ratio of the border radius to the spread distance, in calculating the corner
// radii of the spread shadow shape.
if (border_radius > AK::abs(spread_distance)) {
border_radius += spread_distance;
} else {
auto r = static_cast<float>(border_radius) / AK::abs(spread_distance);
border_radius += spread_distance * (1 + AK::pow(r - 1, 3.0f));
}
}
void CornerRadii::adjust_corners_for_spread_distance(int spread_distance)
{
add_spread_distance_to_border_radius(top_left.horizontal_radius, spread_distance);
add_spread_distance_to_border_radius(top_left.vertical_radius, spread_distance);
add_spread_distance_to_border_radius(top_right.horizontal_radius, spread_distance);
add_spread_distance_to_border_radius(top_right.vertical_radius, spread_distance);
add_spread_distance_to_border_radius(bottom_right.horizontal_radius, spread_distance);
add_spread_distance_to_border_radius(bottom_right.vertical_radius, spread_distance);
add_spread_distance_to_border_radius(bottom_left.horizontal_radius, spread_distance);
add_spread_distance_to_border_radius(bottom_left.vertical_radius, spread_distance);
}
}
namespace IPC {
template<>
ErrorOr<void> encode(Encoder& encoder, Gfx::CornerRadius const& radius)
{
TRY(encoder.encode(radius.horizontal_radius));
TRY(encoder.encode(radius.vertical_radius));
return {};
}
template<>
ErrorOr<Gfx::CornerRadius> decode(Decoder& decoder)
{
return Gfx::CornerRadius {
.horizontal_radius = TRY(decoder.decode<int>()),
.vertical_radius = TRY(decoder.decode<int>()),
};
}
template<>
ErrorOr<void> encode(Encoder& encoder, Gfx::CornerRadii const& radii)
{
TRY(encoder.encode(radii.top_left));
TRY(encoder.encode(radii.top_right));
TRY(encoder.encode(radii.bottom_right));
TRY(encoder.encode(radii.bottom_left));
return {};
}
template<>
ErrorOr<Gfx::CornerRadii> decode(Decoder& decoder)
{
return Gfx::CornerRadii {
.top_left = TRY(decoder.decode<Gfx::CornerRadius>()),
.top_right = TRY(decoder.decode<Gfx::CornerRadius>()),
.bottom_right = TRY(decoder.decode<Gfx::CornerRadius>()),
.bottom_left = TRY(decoder.decode<Gfx::CornerRadius>()),
};
}
}