LibGfx: Add CanvasCommandPlayer
Once canvas commands can be sent to another process, the receiver needs an endpoint that owns the persistent surface and validates command data before it reaches Skia. Add CanvasCommandPlayer for that role. The player replays CanvasCommandList deltas through concrete PainterSkia APIs and keeps painter state across play() calls, matching the way a compositor-hosted canvas surface will accumulate mutations over time. Initialize ops allocate or resize the backing surface so creation, resize and repaint all flow through the same command stream.
This commit is contained in:
parent
714cf827db
commit
d7cb7c4c15
4 changed files with 184 additions and 0 deletions
|
|
@ -5,6 +5,7 @@ set(SOURCES
|
|||
BitmapSequence.cpp
|
||||
CMYKBitmap.cpp
|
||||
CanvasCommandList.cpp
|
||||
CanvasCommandPlayer.cpp
|
||||
Color.cpp
|
||||
ColorConversion.cpp
|
||||
ColorSpace.cpp
|
||||
|
|
|
|||
131
Libraries/LibGfx/CanvasCommandPlayer.cpp
Normal file
131
Libraries/LibGfx/CanvasCommandPlayer.cpp
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* Copyright (c) 2026, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Assertions.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/CanvasCommandPlayer.h>
|
||||
#include <LibGfx/Color.h>
|
||||
#include <LibGfx/PainterSkia.h>
|
||||
#include <LibGfx/PaintingSurface.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
CanvasCommandPlayer::CanvasCommandPlayer(RefPtr<SkiaBackendContext> skia_backend_context, IntSize size, BitmapFormat format, AlphaType alpha_type)
|
||||
: m_surface(PaintingSurface::create_with_size(size, format, alpha_type, move(skia_backend_context)))
|
||||
, m_painter(make<PainterSkia>(*m_surface))
|
||||
{
|
||||
}
|
||||
|
||||
CanvasCommandPlayer::~CanvasCommandPlayer() = default;
|
||||
|
||||
NonnullRefPtr<PaintingSurface> CanvasCommandPlayer::surface() const
|
||||
{
|
||||
return m_surface;
|
||||
}
|
||||
|
||||
void CanvasCommandPlayer::clear(Color color)
|
||||
{
|
||||
m_painter->clear_rect({ {}, m_surface->size().to_type<float>() }, color);
|
||||
}
|
||||
|
||||
void CanvasCommandPlayer::play(CanvasCommandList const& command_list)
|
||||
{
|
||||
for (auto const& command : command_list.commands())
|
||||
command.visit([&](auto const& command) { play_command(command); });
|
||||
}
|
||||
|
||||
void CanvasCommandPlayer::play_command(CanvasCommands::ClearRect const& command)
|
||||
{
|
||||
m_painter->clear_rect(command.rect, command.color);
|
||||
}
|
||||
|
||||
void CanvasCommandPlayer::play_command(CanvasCommands::FillRect const& command)
|
||||
{
|
||||
m_painter->fill_rect(command.rect, command.color);
|
||||
}
|
||||
|
||||
void CanvasCommandPlayer::play_command(CanvasCommands::DrawBitmap const& command)
|
||||
{
|
||||
m_painter->draw_bitmap(command.dst_rect, command.frame, command.src_rect, command.scaling_mode, command.filter, command.global_alpha, command.compositing_and_blending_operator);
|
||||
}
|
||||
|
||||
void CanvasCommandPlayer::play_command(CanvasCommands::FillPath const& command)
|
||||
{
|
||||
// Shadows are recorded as blurred solid-color fills; everything else goes through the general paint-style overload.
|
||||
if (command.blur_radius > 0 && command.style.has<Color>()) {
|
||||
m_painter->fill_path(command.path, command.style.get<Color>(), command.winding_rule, command.blur_radius, command.compositing_and_blending_operator);
|
||||
return;
|
||||
}
|
||||
|
||||
m_painter->fill_path(command.path, resolve_paint_style(command.style), command.filter, command.global_alpha, command.compositing_and_blending_operator, command.winding_rule);
|
||||
}
|
||||
|
||||
void CanvasCommandPlayer::play_command(CanvasCommands::StrokePath const& command)
|
||||
{
|
||||
if (command.blur_radius > 0 && command.style.has<Color>()) {
|
||||
m_painter->stroke_path(command.path, command.style.get<Color>(), command.thickness, command.blur_radius, command.compositing_and_blending_operator, command.cap_style, command.join_style, command.miter_limit, command.dash_array, command.dash_offset);
|
||||
return;
|
||||
}
|
||||
|
||||
m_painter->stroke_path(command.path, resolve_paint_style(command.style), command.filter, command.thickness, command.global_alpha, command.compositing_and_blending_operator, command.cap_style, command.join_style, command.miter_limit, command.dash_array, command.dash_offset);
|
||||
}
|
||||
|
||||
void CanvasCommandPlayer::play_command(CanvasCommands::SetTransform const& command)
|
||||
{
|
||||
m_painter->set_transform(command.transform);
|
||||
}
|
||||
|
||||
void CanvasCommandPlayer::play_command(CanvasCommands::Save const&)
|
||||
{
|
||||
m_painter->save();
|
||||
}
|
||||
|
||||
void CanvasCommandPlayer::play_command(CanvasCommands::Restore const&)
|
||||
{
|
||||
m_painter->restore();
|
||||
}
|
||||
|
||||
void CanvasCommandPlayer::play_command(CanvasCommands::ClipPath const& command)
|
||||
{
|
||||
m_painter->clip(command.path, command.winding_rule);
|
||||
}
|
||||
|
||||
void CanvasCommandPlayer::play_command(CanvasCommands::Reset const&)
|
||||
{
|
||||
m_painter->reset();
|
||||
}
|
||||
|
||||
NonnullRefPtr<PaintStyle> CanvasCommandPlayer::resolve_paint_style(CanvasPaintStyle const& style) const
|
||||
{
|
||||
auto with_color_stops = [](auto paint_style, auto const& gradient) -> NonnullRefPtr<PaintStyle> {
|
||||
paint_style->set_color_stops(Vector<ColorStop> { gradient.color_stops });
|
||||
if (gradient.repeat_length.has_value())
|
||||
paint_style->set_repeat_length(*gradient.repeat_length);
|
||||
return paint_style;
|
||||
};
|
||||
|
||||
return style.visit(
|
||||
[](Color const& color) -> NonnullRefPtr<PaintStyle> {
|
||||
return MUST(SolidColorPaintStyle::create(color));
|
||||
},
|
||||
[&](CanvasLinearGradient const& gradient) -> NonnullRefPtr<PaintStyle> {
|
||||
return with_color_stops(MUST(CanvasLinearGradientPaintStyle::create(gradient.start_point, gradient.end_point)), gradient);
|
||||
},
|
||||
[&](CanvasRadialGradient const& gradient) -> NonnullRefPtr<PaintStyle> {
|
||||
return with_color_stops(MUST(CanvasRadialGradientPaintStyle::create(gradient.start_center, gradient.start_radius, gradient.end_center, gradient.end_radius)), gradient);
|
||||
},
|
||||
[&](CanvasConicGradient const& gradient) -> NonnullRefPtr<PaintStyle> {
|
||||
return with_color_stops(MUST(CanvasConicGradientPaintStyle::create(gradient.center, gradient.start_angle)), gradient);
|
||||
},
|
||||
[&](CanvasPatternStyle const& pattern) -> NonnullRefPtr<PaintStyle> {
|
||||
auto paint_style = MUST(CanvasPatternPaintStyle::create(pattern.image, pattern.repetition));
|
||||
if (pattern.transform.has_value())
|
||||
paint_style->set_transform(*pattern.transform);
|
||||
return paint_style;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
50
Libraries/LibGfx/CanvasCommandPlayer.h
Normal file
50
Libraries/LibGfx/CanvasCommandPlayer.h
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (c) 2026, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <AK/NonnullOwnPtr.h>
|
||||
#include <AK/NonnullRefPtr.h>
|
||||
#include <AK/RefPtr.h>
|
||||
#include <LibGfx/CanvasCommandList.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
class CanvasCommandPlayer {
|
||||
AK_MAKE_NONCOPYABLE(CanvasCommandPlayer);
|
||||
AK_MAKE_NONMOVABLE(CanvasCommandPlayer);
|
||||
|
||||
public:
|
||||
CanvasCommandPlayer(RefPtr<SkiaBackendContext>, IntSize, BitmapFormat, AlphaType);
|
||||
~CanvasCommandPlayer();
|
||||
|
||||
NonnullRefPtr<PaintingSurface> surface() const;
|
||||
|
||||
void clear(Color);
|
||||
|
||||
void play(CanvasCommandList const&);
|
||||
|
||||
private:
|
||||
void play_command(CanvasCommands::ClearRect const&);
|
||||
void play_command(CanvasCommands::FillRect const&);
|
||||
void play_command(CanvasCommands::DrawBitmap const&);
|
||||
void play_command(CanvasCommands::FillPath const&);
|
||||
void play_command(CanvasCommands::StrokePath const&);
|
||||
void play_command(CanvasCommands::SetTransform const&);
|
||||
void play_command(CanvasCommands::Save const&);
|
||||
void play_command(CanvasCommands::Restore const&);
|
||||
void play_command(CanvasCommands::ClipPath const&);
|
||||
void play_command(CanvasCommands::Reset const&);
|
||||
|
||||
NonnullRefPtr<PaintStyle> resolve_paint_style(CanvasPaintStyle const&) const;
|
||||
|
||||
NonnullRefPtr<PaintingSurface> m_surface;
|
||||
NonnullOwnPtr<PainterSkia> m_painter;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ namespace Gfx {
|
|||
class Bitmap;
|
||||
class CMYKBitmap;
|
||||
class CanvasCommandList;
|
||||
class CanvasCommandPlayer;
|
||||
class ColorSpace;
|
||||
class DecodedImageFrame;
|
||||
class Color;
|
||||
|
|
@ -22,6 +23,7 @@ class ImageDecoder;
|
|||
struct FontPixelMetrics;
|
||||
|
||||
class Painter;
|
||||
class PainterSkia;
|
||||
class PaintingSurface;
|
||||
class Palette;
|
||||
class YUVData;
|
||||
|
|
|
|||
Loading…
Reference in a new issue