From 714cf827db0a3201781614ceb89762031e737edb Mon Sep 17 00:00:00 2001 From: Aliaksandr Kalenik Date: Thu, 11 Jun 2026 23:31:15 +0200 Subject: [PATCH] LibGfx: Add CanvasCommandList Moving 2D canvas rasterization into the Compositor process needs a wire format for canvas mutations that does not depend on LibWeb state. Add CanvasCommandList as an apply-once log of drawing operations whose operands are Gfx value types and whose IPC encoders can be shared by WebContent and the compositor side. --- Libraries/LibGfx/AffineTransform.cpp | 30 +++ Libraries/LibGfx/AffineTransform.h | 11 + Libraries/LibGfx/CMakeLists.txt | 3 + Libraries/LibGfx/CanvasCommandList.cpp | 355 +++++++++++++++++++++++++ Libraries/LibGfx/CanvasCommandList.h | 237 +++++++++++++++++ Libraries/LibGfx/DecodedImageFrame.cpp | 31 +++ Libraries/LibGfx/DecodedImageFrame.h | 11 + Libraries/LibGfx/Forward.h | 1 + Libraries/LibGfx/Gradients.cpp | 31 +++ Libraries/LibGfx/Gradients.h | 11 + Libraries/LibGfx/Rect.cpp | 20 ++ Libraries/LibGfx/Rect.h | 6 + 12 files changed, 747 insertions(+) create mode 100644 Libraries/LibGfx/CanvasCommandList.cpp create mode 100644 Libraries/LibGfx/CanvasCommandList.h create mode 100644 Libraries/LibGfx/DecodedImageFrame.cpp create mode 100644 Libraries/LibGfx/Gradients.cpp diff --git a/Libraries/LibGfx/AffineTransform.cpp b/Libraries/LibGfx/AffineTransform.cpp index c7b0fc4693..fb14ad6df0 100644 --- a/Libraries/LibGfx/AffineTransform.cpp +++ b/Libraries/LibGfx/AffineTransform.cpp @@ -9,6 +9,8 @@ #include #include #include +#include +#include namespace Gfx { @@ -259,3 +261,31 @@ Matrix<4, float> AffineTransform::to_matrix() const } } + +namespace IPC { + +template<> +ErrorOr encode(Encoder& encoder, Gfx::AffineTransform const& transform) +{ + TRY(encoder.encode(transform.a())); + TRY(encoder.encode(transform.b())); + TRY(encoder.encode(transform.c())); + TRY(encoder.encode(transform.d())); + TRY(encoder.encode(transform.e())); + TRY(encoder.encode(transform.f())); + return {}; +} + +template<> +ErrorOr decode(Decoder& decoder) +{ + auto a = TRY(decoder.decode()); + auto b = TRY(decoder.decode()); + auto c = TRY(decoder.decode()); + auto d = TRY(decoder.decode()); + auto e = TRY(decoder.decode()); + auto f = TRY(decoder.decode()); + return Gfx::AffineTransform { a, b, c, d, e, f }; +} + +} diff --git a/Libraries/LibGfx/AffineTransform.h b/Libraries/LibGfx/AffineTransform.h index e3b1668edb..84c7925a7f 100644 --- a/Libraries/LibGfx/AffineTransform.h +++ b/Libraries/LibGfx/AffineTransform.h @@ -11,6 +11,7 @@ #include #include #include +#include namespace Gfx { @@ -99,3 +100,13 @@ struct AK::Formatter : Formatter { return Formatter::format(builder, "[{} {} {} {} {} {}]"sv, value.a(), value.b(), value.c(), value.d(), value.e(), value.f()); } }; + +namespace IPC { + +template<> +ErrorOr encode(Encoder&, Gfx::AffineTransform const&); + +template<> +ErrorOr decode(Decoder&); + +} diff --git a/Libraries/LibGfx/CMakeLists.txt b/Libraries/LibGfx/CMakeLists.txt index 35aa60c547..42d3bacc9d 100644 --- a/Libraries/LibGfx/CMakeLists.txt +++ b/Libraries/LibGfx/CMakeLists.txt @@ -4,6 +4,7 @@ set(SOURCES BitmapExport.cpp BitmapSequence.cpp CMYKBitmap.cpp + CanvasCommandList.cpp Color.cpp ColorConversion.cpp ColorSpace.cpp @@ -21,7 +22,9 @@ set(SOURCES Font/WOFF2/Loader.cpp FontCascadeList.cpp GradientPainting.cpp + Gradients.cpp Matrix4x4.cpp + DecodedImageFrame.cpp ImageFormats/BMPWriter.cpp ImageFormats/JPEGWriter.cpp ImageFormats/PNGWriter.cpp diff --git a/Libraries/LibGfx/CanvasCommandList.cpp b/Libraries/LibGfx/CanvasCommandList.cpp new file mode 100644 index 0000000000..03a0199784 --- /dev/null +++ b/Libraries/LibGfx/CanvasCommandList.cpp @@ -0,0 +1,355 @@ +/* + * Copyright (c) 2026, Aliaksandr Kalenik + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include + +namespace Gfx { + +CanvasPaintStyle to_canvas_paint_style(PaintStyle const& paint_style) +{ + if (auto const* solid_color = as_if(paint_style)) + return solid_color->color(); + + if (auto const* linear_gradient = as_if(paint_style)) { + return CanvasLinearGradient { + .start_point = linear_gradient->start_point(), + .end_point = linear_gradient->end_point(), + .color_stops = Vector { linear_gradient->color_stops() }, + .repeat_length = linear_gradient->repeat_length(), + }; + } + + if (auto const* radial_gradient = as_if(paint_style)) { + return CanvasRadialGradient { + .start_center = radial_gradient->start_center(), + .start_radius = radial_gradient->start_radius(), + .end_center = radial_gradient->end_center(), + .end_radius = radial_gradient->end_radius(), + .color_stops = Vector { radial_gradient->color_stops() }, + .repeat_length = radial_gradient->repeat_length(), + }; + } + + if (auto const* conic_gradient = as_if(paint_style)) { + return CanvasConicGradient { + .center = conic_gradient->center(), + .start_angle = conic_gradient->start_angle(), + .color_stops = Vector { conic_gradient->color_stops() }, + .repeat_length = conic_gradient->repeat_length(), + }; + } + + if (auto const* pattern = as_if(paint_style)) { + return CanvasPatternStyle { + .image = pattern->image(), + .repetition = pattern->repetition(), + .transform = pattern->transform(), + }; + } + + VERIFY_NOT_REACHED(); +} + +} + +namespace IPC { + +template<> +ErrorOr encode(Encoder& encoder, Gfx::CanvasLinearGradient const& gradient) +{ + TRY(encoder.encode(gradient.start_point)); + TRY(encoder.encode(gradient.end_point)); + TRY(encoder.encode(gradient.color_stops)); + TRY(encoder.encode(gradient.repeat_length)); + return {}; +} + +template<> +ErrorOr decode(Decoder& decoder) +{ + return Gfx::CanvasLinearGradient { + .start_point = TRY(decoder.decode()), + .end_point = TRY(decoder.decode()), + .color_stops = TRY(decoder.decode>()), + .repeat_length = TRY(decoder.decode>()), + }; +} + +template<> +ErrorOr encode(Encoder& encoder, Gfx::CanvasRadialGradient const& gradient) +{ + TRY(encoder.encode(gradient.start_center)); + TRY(encoder.encode(gradient.start_radius)); + TRY(encoder.encode(gradient.end_center)); + TRY(encoder.encode(gradient.end_radius)); + TRY(encoder.encode(gradient.color_stops)); + TRY(encoder.encode(gradient.repeat_length)); + return {}; +} + +template<> +ErrorOr decode(Decoder& decoder) +{ + return Gfx::CanvasRadialGradient { + .start_center = TRY(decoder.decode()), + .start_radius = TRY(decoder.decode()), + .end_center = TRY(decoder.decode()), + .end_radius = TRY(decoder.decode()), + .color_stops = TRY(decoder.decode>()), + .repeat_length = TRY(decoder.decode>()), + }; +} + +template<> +ErrorOr encode(Encoder& encoder, Gfx::CanvasConicGradient const& gradient) +{ + TRY(encoder.encode(gradient.center)); + TRY(encoder.encode(gradient.start_angle)); + TRY(encoder.encode(gradient.color_stops)); + TRY(encoder.encode(gradient.repeat_length)); + return {}; +} + +template<> +ErrorOr decode(Decoder& decoder) +{ + return Gfx::CanvasConicGradient { + .center = TRY(decoder.decode()), + .start_angle = TRY(decoder.decode()), + .color_stops = TRY(decoder.decode>()), + .repeat_length = TRY(decoder.decode>()), + }; +} + +template<> +ErrorOr encode(Encoder& encoder, Gfx::CanvasPatternStyle const& pattern) +{ + TRY(encoder.encode(pattern.image)); + TRY(encoder.encode(pattern.repetition)); + TRY(encoder.encode(pattern.transform)); + return {}; +} + +template<> +ErrorOr decode(Decoder& decoder) +{ + return Gfx::CanvasPatternStyle { + .image = TRY(decoder.decode>()), + .repetition = TRY(decoder.decode()), + .transform = TRY(decoder.decode>()), + }; +} + +template<> +ErrorOr encode(Encoder& encoder, Gfx::CanvasCommands::ClearRect const& command) +{ + TRY(encoder.encode(command.rect)); + TRY(encoder.encode(command.color)); + return {}; +} + +template<> +ErrorOr decode(Decoder& decoder) +{ + return Gfx::CanvasCommands::ClearRect { + .rect = TRY(decoder.decode()), + .color = TRY(decoder.decode()), + }; +} + +template<> +ErrorOr encode(Encoder& encoder, Gfx::CanvasCommands::FillRect const& command) +{ + TRY(encoder.encode(command.rect)); + TRY(encoder.encode(command.color)); + return {}; +} + +template<> +ErrorOr decode(Decoder& decoder) +{ + return Gfx::CanvasCommands::FillRect { + .rect = TRY(decoder.decode()), + .color = TRY(decoder.decode()), + }; +} + +template<> +ErrorOr encode(Encoder& encoder, Gfx::CanvasCommands::DrawBitmap const& command) +{ + TRY(encoder.encode(command.frame)); + TRY(encoder.encode(command.dst_rect)); + TRY(encoder.encode(command.src_rect)); + TRY(encoder.encode(command.scaling_mode)); + TRY(encoder.encode(command.filter)); + TRY(encoder.encode(command.global_alpha)); + TRY(encoder.encode(command.compositing_and_blending_operator)); + return {}; +} + +template<> +ErrorOr decode(Decoder& decoder) +{ + return Gfx::CanvasCommands::DrawBitmap { + .frame = TRY(decoder.decode()), + .dst_rect = TRY(decoder.decode()), + .src_rect = TRY(decoder.decode()), + .scaling_mode = TRY(decoder.decode()), + .filter = TRY(decoder.decode>()), + .global_alpha = TRY(decoder.decode()), + .compositing_and_blending_operator = TRY(decoder.decode()), + }; +} + +template<> +ErrorOr encode(Encoder& encoder, Gfx::CanvasCommands::FillPath const& command) +{ + TRY(encoder.encode(command.path)); + TRY(encoder.encode(command.style)); + TRY(encoder.encode(command.winding_rule)); + TRY(encoder.encode(command.blur_radius)); + TRY(encoder.encode(command.filter)); + TRY(encoder.encode(command.global_alpha)); + TRY(encoder.encode(command.compositing_and_blending_operator)); + return {}; +} + +template<> +ErrorOr decode(Decoder& decoder) +{ + return Gfx::CanvasCommands::FillPath { + .path = TRY(decoder.decode()), + .style = TRY(decoder.decode()), + .winding_rule = TRY(decoder.decode()), + .blur_radius = TRY(decoder.decode()), + .filter = TRY(decoder.decode>()), + .global_alpha = TRY(decoder.decode()), + .compositing_and_blending_operator = TRY(decoder.decode()), + }; +} + +template<> +ErrorOr encode(Encoder& encoder, Gfx::CanvasCommands::StrokePath const& command) +{ + TRY(encoder.encode(command.path)); + TRY(encoder.encode(command.style)); + TRY(encoder.encode(command.thickness)); + TRY(encoder.encode(command.cap_style)); + TRY(encoder.encode(command.join_style)); + TRY(encoder.encode(command.miter_limit)); + TRY(encoder.encode(command.dash_array)); + TRY(encoder.encode(command.dash_offset)); + TRY(encoder.encode(command.blur_radius)); + TRY(encoder.encode(command.filter)); + TRY(encoder.encode(command.global_alpha)); + TRY(encoder.encode(command.compositing_and_blending_operator)); + return {}; +} + +template<> +ErrorOr decode(Decoder& decoder) +{ + return Gfx::CanvasCommands::StrokePath { + .path = TRY(decoder.decode()), + .style = TRY(decoder.decode()), + .thickness = TRY(decoder.decode()), + .cap_style = TRY(decoder.decode()), + .join_style = TRY(decoder.decode()), + .miter_limit = TRY(decoder.decode()), + .dash_array = TRY(decoder.decode>()), + .dash_offset = TRY(decoder.decode()), + .blur_radius = TRY(decoder.decode()), + .filter = TRY(decoder.decode>()), + .global_alpha = TRY(decoder.decode()), + .compositing_and_blending_operator = TRY(decoder.decode()), + }; +} + +template<> +ErrorOr encode(Encoder& encoder, Gfx::CanvasCommands::SetTransform const& command) +{ + TRY(encoder.encode(command.transform)); + return {}; +} + +template<> +ErrorOr decode(Decoder& decoder) +{ + return Gfx::CanvasCommands::SetTransform { + .transform = TRY(decoder.decode()), + }; +} + +template<> +ErrorOr encode(Encoder&, Gfx::CanvasCommands::Save const&) +{ + return {}; +} + +template<> +ErrorOr decode(Decoder&) +{ + return Gfx::CanvasCommands::Save {}; +} + +template<> +ErrorOr encode(Encoder&, Gfx::CanvasCommands::Restore const&) +{ + return {}; +} + +template<> +ErrorOr decode(Decoder&) +{ + return Gfx::CanvasCommands::Restore {}; +} + +template<> +ErrorOr encode(Encoder& encoder, Gfx::CanvasCommands::ClipPath const& command) +{ + TRY(encoder.encode(command.path)); + TRY(encoder.encode(command.winding_rule)); + return {}; +} + +template<> +ErrorOr decode(Decoder& decoder) +{ + return Gfx::CanvasCommands::ClipPath { + .path = TRY(decoder.decode()), + .winding_rule = TRY(decoder.decode()), + }; +} + +template<> +ErrorOr encode(Encoder&, Gfx::CanvasCommands::Reset const&) +{ + return {}; +} + +template<> +ErrorOr decode(Decoder&) +{ + return Gfx::CanvasCommands::Reset {}; +} + +template<> +ErrorOr encode(Encoder& encoder, Gfx::CanvasCommandList const& command_list) +{ + TRY(encoder.encode(command_list.commands())); + return {}; +} + +template<> +ErrorOr decode(Decoder& decoder) +{ + return Gfx::CanvasCommandList { TRY(decoder.decode>()) }; +} + +} diff --git a/Libraries/LibGfx/CanvasCommandList.h b/Libraries/LibGfx/CanvasCommandList.h new file mode 100644 index 0000000000..4d95840d41 --- /dev/null +++ b/Libraries/LibGfx/CanvasCommandList.h @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2026, Aliaksandr Kalenik + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Gfx { + +struct CanvasLinearGradient { + FloatPoint start_point; + FloatPoint end_point; + Vector color_stops; + Optional repeat_length; +}; + +struct CanvasRadialGradient { + FloatPoint start_center; + float start_radius { 0 }; + FloatPoint end_center; + float end_radius { 0 }; + Vector color_stops; + Optional repeat_length; +}; + +struct CanvasConicGradient { + FloatPoint center; + float start_angle { 0 }; + Vector color_stops; + Optional repeat_length; +}; + +struct CanvasPatternStyle { + Optional image; + CanvasPatternPaintStyle::Repetition repetition { CanvasPatternPaintStyle::Repetition::Repeat }; + Optional transform; +}; + +using CanvasPaintStyle = Variant; + +namespace CanvasCommands { + +struct ClearRect { + FloatRect rect; + Color color; +}; + +struct FillRect { + FloatRect rect; + Color color; +}; + +struct DrawBitmap { + DecodedImageFrame frame; + FloatRect dst_rect; + IntRect src_rect; + ScalingMode scaling_mode { ScalingMode::NearestNeighbor }; + Optional filter; + float global_alpha { 1 }; + CompositingAndBlendingOperator compositing_and_blending_operator { CompositingAndBlendingOperator::SourceOver }; +}; + +struct FillPath { + Path path; + CanvasPaintStyle style; + WindingRule winding_rule { WindingRule::Nonzero }; + float blur_radius { 0 }; + Optional filter; + float global_alpha { 1 }; + CompositingAndBlendingOperator compositing_and_blending_operator { CompositingAndBlendingOperator::SourceOver }; +}; + +struct StrokePath { + Path path; + CanvasPaintStyle style; + float thickness { 1 }; + Path::CapStyle cap_style { Path::CapStyle::Butt }; + Path::JoinStyle join_style { Path::JoinStyle::Miter }; + float miter_limit { 10 }; + Vector dash_array; + float dash_offset { 0 }; + float blur_radius { 0 }; + Optional filter; + float global_alpha { 1 }; + CompositingAndBlendingOperator compositing_and_blending_operator { CompositingAndBlendingOperator::SourceOver }; +}; + +struct SetTransform { + AffineTransform transform; +}; + +struct Save { }; + +struct Restore { }; + +struct ClipPath { + Path path; + WindingRule winding_rule { WindingRule::Nonzero }; +}; + +struct Reset { }; + +} + +using CanvasCommand = Variant< + CanvasCommands::ClearRect, + CanvasCommands::FillRect, + CanvasCommands::DrawBitmap, + CanvasCommands::FillPath, + CanvasCommands::StrokePath, + CanvasCommands::SetTransform, + CanvasCommands::Save, + CanvasCommands::Restore, + CanvasCommands::ClipPath, + CanvasCommands::Reset>; + +class CanvasCommandList { +public: + CanvasCommandList() = default; + explicit CanvasCommandList(Vector commands) + : m_commands(move(commands)) + { + } + + void append(CanvasCommand&& command) { m_commands.append(move(command)); } + + bool is_empty() const { return m_commands.is_empty(); } + size_t size() const { return m_commands.size(); } + + Vector const& commands() const { return m_commands; } + +private: + Vector m_commands; +}; + +CanvasPaintStyle to_canvas_paint_style(PaintStyle const&); + +} + +namespace IPC { + +template<> +ErrorOr encode(Encoder&, Gfx::CanvasLinearGradient const&); +template<> +ErrorOr decode(Decoder&); + +template<> +ErrorOr encode(Encoder&, Gfx::CanvasRadialGradient const&); +template<> +ErrorOr decode(Decoder&); + +template<> +ErrorOr encode(Encoder&, Gfx::CanvasConicGradient const&); +template<> +ErrorOr decode(Decoder&); + +template<> +ErrorOr encode(Encoder&, Gfx::CanvasPatternStyle const&); +template<> +ErrorOr decode(Decoder&); + +template<> +ErrorOr encode(Encoder&, Gfx::CanvasCommands::ClearRect const&); +template<> +ErrorOr decode(Decoder&); + +template<> +ErrorOr encode(Encoder&, Gfx::CanvasCommands::FillRect const&); +template<> +ErrorOr decode(Decoder&); + +template<> +ErrorOr encode(Encoder&, Gfx::CanvasCommands::DrawBitmap const&); +template<> +ErrorOr decode(Decoder&); + +template<> +ErrorOr encode(Encoder&, Gfx::CanvasCommands::FillPath const&); +template<> +ErrorOr decode(Decoder&); + +template<> +ErrorOr encode(Encoder&, Gfx::CanvasCommands::StrokePath const&); +template<> +ErrorOr decode(Decoder&); + +template<> +ErrorOr encode(Encoder&, Gfx::CanvasCommands::SetTransform const&); +template<> +ErrorOr decode(Decoder&); + +template<> +ErrorOr encode(Encoder&, Gfx::CanvasCommands::Save const&); +template<> +ErrorOr decode(Decoder&); + +template<> +ErrorOr encode(Encoder&, Gfx::CanvasCommands::Restore const&); +template<> +ErrorOr decode(Decoder&); + +template<> +ErrorOr encode(Encoder&, Gfx::CanvasCommands::ClipPath const&); +template<> +ErrorOr decode(Decoder&); + +template<> +ErrorOr encode(Encoder&, Gfx::CanvasCommands::Reset const&); +template<> +ErrorOr decode(Decoder&); + +template<> +ErrorOr encode(Encoder&, Gfx::CanvasCommandList const&); +template<> +ErrorOr decode(Decoder&); + +} diff --git a/Libraries/LibGfx/DecodedImageFrame.cpp b/Libraries/LibGfx/DecodedImageFrame.cpp new file mode 100644 index 0000000000..6c89ac2387 --- /dev/null +++ b/Libraries/LibGfx/DecodedImageFrame.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2026, Aliaksandr Kalenik + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include + +namespace IPC { + +template<> +ErrorOr encode(Encoder& encoder, Gfx::DecodedImageFrame const& frame) +{ + auto bitmap = frame.bitmap().to_shareable_bitmap(); + TRY(encoder.encode(bitmap)); + TRY(encoder.encode(frame.color_space())); + return {}; +} + +template<> +ErrorOr decode(Decoder& decoder) +{ + auto bitmap = TRY(decoder.decode()); + auto color_space = TRY(decoder.decode()); + return Gfx::DecodedImageFrame { *bitmap.bitmap(), move(color_space) }; +} + +} diff --git a/Libraries/LibGfx/DecodedImageFrame.h b/Libraries/LibGfx/DecodedImageFrame.h index 4972934f8f..6de35f200b 100644 --- a/Libraries/LibGfx/DecodedImageFrame.h +++ b/Libraries/LibGfx/DecodedImageFrame.h @@ -13,6 +13,7 @@ #include #include #include +#include namespace Gfx { @@ -67,3 +68,13 @@ private: }; } + +namespace IPC { + +template<> +ErrorOr encode(Encoder&, Gfx::DecodedImageFrame const&); + +template<> +ErrorOr decode(Decoder&); + +} diff --git a/Libraries/LibGfx/Forward.h b/Libraries/LibGfx/Forward.h index 9a33630c35..7c3b912724 100644 --- a/Libraries/LibGfx/Forward.h +++ b/Libraries/LibGfx/Forward.h @@ -10,6 +10,7 @@ namespace Gfx { class Bitmap; class CMYKBitmap; +class CanvasCommandList; class ColorSpace; class DecodedImageFrame; class Color; diff --git a/Libraries/LibGfx/Gradients.cpp b/Libraries/LibGfx/Gradients.cpp new file mode 100644 index 0000000000..25548a74bb --- /dev/null +++ b/Libraries/LibGfx/Gradients.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2026, Aliaksandr Kalenik + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +namespace IPC { + +template<> +ErrorOr encode(Encoder& encoder, Gfx::ColorStop const& color_stop) +{ + TRY(encoder.encode(color_stop.color)); + TRY(encoder.encode(color_stop.position)); + TRY(encoder.encode(color_stop.transition_hint)); + return {}; +} + +template<> +ErrorOr decode(Decoder& decoder) +{ + auto color = TRY(decoder.decode()); + auto position = TRY(decoder.decode()); + auto transition_hint = TRY(decoder.decode>()); + return Gfx::ColorStop { color, position, transition_hint }; +} + +} diff --git a/Libraries/LibGfx/Gradients.h b/Libraries/LibGfx/Gradients.h index e647cce648..a04796bb9d 100644 --- a/Libraries/LibGfx/Gradients.h +++ b/Libraries/LibGfx/Gradients.h @@ -9,6 +9,7 @@ #include #include #include +#include namespace Gfx { @@ -45,3 +46,13 @@ inline float calculate_gradient_length(Size gradient_size, float gradient_ang } } + +namespace IPC { + +template<> +ErrorOr encode(Encoder&, Gfx::ColorStop const&); + +template<> +ErrorOr decode(Decoder&); + +} diff --git a/Libraries/LibGfx/Rect.cpp b/Libraries/LibGfx/Rect.cpp index d7ed4a9145..71c67ced83 100644 --- a/Libraries/LibGfx/Rect.cpp +++ b/Libraries/LibGfx/Rect.cpp @@ -43,6 +43,26 @@ ErrorOr decode(Decoder& decoder) return Gfx::IntRect { point, size }; } +template<> +ErrorOr encode(Encoder& encoder, Gfx::FloatRect const& rect) +{ + TRY(encoder.encode(rect.x())); + TRY(encoder.encode(rect.y())); + TRY(encoder.encode(rect.width())); + TRY(encoder.encode(rect.height())); + return {}; +} + +template<> +ErrorOr decode(Decoder& decoder) +{ + auto x = TRY(decoder.decode()); + auto y = TRY(decoder.decode()); + auto width = TRY(decoder.decode()); + auto height = TRY(decoder.decode()); + return Gfx::FloatRect { x, y, width, height }; +} + } template class Gfx::Rect; diff --git a/Libraries/LibGfx/Rect.h b/Libraries/LibGfx/Rect.h index 70ef5d6db6..17486a2d13 100644 --- a/Libraries/LibGfx/Rect.h +++ b/Libraries/LibGfx/Rect.h @@ -620,4 +620,10 @@ ErrorOr encode(Encoder&, Gfx::IntRect const&); template<> ErrorOr decode(Decoder&); +template<> +ErrorOr encode(Encoder&, Gfx::FloatRect const&); + +template<> +ErrorOr decode(Decoder&); + }