Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
pulp::canvas::CoreGraphicsCanvas Class Reference

#include <cg_canvas.hpp>

Public Types

enum class  ImageSmoothingQuality { low , medium , high }
 Canvas2D image-smoothing quality enum. More...
enum class  PatternTileMode { repeat , no_repeat }
 Canvas2D ctx.createPattern. More...
enum class  BlendMode {
  normal = 0 , multiply , screen , overlay ,
  darken , lighten , color_dodge , color_burn ,
  hard_light , soft_light , difference , exclusion ,
  hue , saturation , color , luminosity ,
  source_over , destination_over , source_in , destination_in ,
  source_out , destination_out , source_atop , destination_atop ,
  xor_mode , copy , lighter
}
 Indices 0..15 match the existing W3C "advanced" composite ops and must stay stable — set_blend_mode in the JS bridge currently passes int_val 1/2/3 = multiply/screen/overlay. More...
enum class  TextAnchor { Baseline , GlyphTop , GlyphCenter , EmBoxTop }
 Explicit, typed vertical-anchor API. More...
enum class  SDFShape {
  rect , circle , rounded_rect , arc ,
  diamond , squircle , triangle , ring ,
  stadium , cross , flat_segment , rounded_segment ,
  flat_arc , quadratic_bezier
}
 Draw an SDF shape with anti-aliased edges via GPU shader. More...
enum class  TextDirection { ltr , rtl , inherit }
 Canvas2D ctx.direction. More...
enum class  ImageFit {
  fill , contain , cover , none ,
  scale_down
}
 Preserve-aspect fit modes for fit_image_rect / draw_image_fitted: fill — stretch to the destination box, ignoring aspect ratio. More...
using Point2D = pulp::canvas::Point2D
 Stroke a continuous polyline through an array of points.

Public Member Functions

RendererBackend renderer_backend () const noexcept override
 Identity of the renderer this canvas actually wraps.
float backing_scale () const noexcept override
 Effective device pixels per local canvas unit at the current transform.
 CoreGraphicsCanvas (CGContextRef ctx, float width, float height)
 ~CoreGraphicsCanvas () override
void save () override
void restore () override
int save_count () const override
 Return the current save-stack depth.
void restore_to_count (int target) override
 Pop the save stack down to target (typically captured earlier via save_count()).
void translate (float x, float y) override
void scale (float sx, float sy) override
void rotate (float radians) override
void concat_transform (float a, float b, float c, float d, float e, float f) override
 Concat (multiply) the supplied affine matrix onto the current transform — does NOT replace it.
void set_transform (float a, float b, float c, float d, float e, float f) override
 Replace the current transform with the affine matrix [ a c e ] [ b d f ] [ 0 0 1 ] Mirrors CanvasRenderingContext2D.setTransform(a, b, c, d, e, f).
void capture_paint_baseline_transform () override
 Snapshot the current device matrix as the baseline for subsequent set_transform() calls.
AffineTransform2x3 current_transform () const override
 Snapshot the current device matrix (CTM) without mutating canvas state.
void clip_rect (float x, float y, float w, float h) override
void clip (FillRule rule=FillRule::nonzero) override
 Intersect the current clip region with the current path.
void set_fill_color (Color c) override
void set_stroke_color (Color c) override
void set_line_width (float w) override
void set_line_cap (LineCap cap) override
void set_line_join (LineJoin join) override
void set_line_dash (const float *intervals, int count, float phase) override
 Set the line dash pattern for stroke operations.
void set_miter_limit (float limit) override
 Canvas2D ctx.miterLimit.
void set_font_smoothing (FontSmoothing smoothing) override
 antialiased turns CoreText font smoothing off, the switch Chromium flips on macOS.
void set_image_smoothing (bool enabled, ImageSmoothingQuality quality) override
 Canvas2D ctx.imageSmoothingEnabled + imageSmoothingQuality.
void set_fill_gradient_linear (float x0, float y0, float x1, float y1, const Color *colors, const float *positions, int count) override
 Set a linear gradient as the fill paint.
void set_fill_gradient_radial (float cx, float cy, float radius, const Color *colors, const float *positions, int count) override
 Set a radial gradient as the fill paint.
void set_fill_gradient_radial_two_circles (float x0, float y0, float r0, float x1, float y1, float r1, const Color *colors, const float *positions, int count) override
 True two-circle radial gradient via CGContextDrawRadialGradient(inner_center, inner_r, outer_center, outer_r).
void set_fill_gradient_conic_repeating (float cx, float cy, float start_angle, float sweep_turns, const Color *colors, const float *positions, int count) override
 Set a REPEATING conic gradient (CSS repeating-conic-gradient).
void set_fill_gradient_conic (float cx, float cy, float start_angle, const Color *colors, const float *positions, int count) override
 Set a conic (sweep) gradient as the fill paint.
void clear_fill_gradient () override
 Clear gradient, return to solid fill color.
void set_stroke_gradient_linear (float x0, float y0, float x1, float y1, const Color *colors, const float *positions, int count) override
void set_stroke_gradient_radial (float cx, float cy, float radius, const Color *colors, const float *positions, int count) override
void set_stroke_gradient_radial_two_circles (float x0, float y0, float r0, float x1, float y1, float r1, const Color *colors, const float *positions, int count) override
 Two-circle radial gradient on stroke (mirrors fill counterpart).
void set_stroke_gradient_conic (float cx, float cy, float start_angle, const Color *colors, const float *positions, int count) override
void clear_stroke_gradient () override
 Clear stroke gradient/pattern shader, restore solid stroke color.
void set_fill_pattern (const std::string &image_src, PatternTileMode tile_x, PatternTileMode tile_y) override
 Set an image pattern as the fill paint.
void set_stroke_pattern (const std::string &image_src, PatternTileMode tile_x, PatternTileMode tile_y) override
 Stroke counterpart.
void fill_rect (float x, float y, float w, float h) override
void clear_rect (float x, float y, float w, float h) override
 Clear a rectangular region to transparent black (rgba 0,0,0,0).
void stroke_rect (float x, float y, float w, float h) override
void fill_rounded_rect (float x, float y, float w, float h, float radius) override
void stroke_rounded_rect (float x, float y, float w, float h, float radius) override
void fill_circle (float cx, float cy, float radius) override
void stroke_circle (float cx, float cy, float radius) override
void stroke_arc (float cx, float cy, float radius, float start_angle, float end_angle) override
void stroke_line (float x0, float y0, float x1, float y1) override
void stroke_path (const Point2D *points, size_t count) override
 Stroke a continuous polyline — much smoother than individual stroke_line calls.
void fill_path (const Point2D *points, size_t count, FillRule rule=FillRule::nonzero) override
 Fill a closed polygon defined by points, honoring rule.
void fill_path (const Path &path, FillRule rule=FillRule::nonzero) override
 Fill path under rule.
void stroke_path (const Path &path, const StrokeStyle &style) override
 Stroke path with style.
void clip_path (const Path &path, FillRule rule=FillRule::nonzero) override
 Intersect the current clip with path. Caller owns save()/restore().
void draw_path_shadow (const Path &path, float dx, float dy, float blur, float spread, Color color) override
 Draw ONLY the drop shadow cast by path — not the path itself.
LayerHandle begin_layer (Rect2D bounds, bool cacheable=false) override
 Begin recording into an offscreen layer covering bounds (in current user space).
LayerHandle end_layer () override
 Seal the layer opened by the last begin_layer() and return its handle.
void draw_layer (LayerHandle layer, float alpha=1.0f, BlendMode mode=BlendMode::normal) override
 Composite a sealed layer at its recorded position.
void draw_layer_fitted (LayerHandle layer, Rect2D dest) override
 Composite a sealed layer scaled into dest.
void draw_layer_rotated (LayerHandle layer, float angle_rad) override
 Composite a sealed layer rotated about its own center.
bool layer_valid (LayerHandle layer) const override
 Is layer still backed by a live texture that can be drawn WITHOUT re-recording it?
void invalidate_layer (LayerHandle layer) override
 Drop layer's texture.
void begin_path () override
 Begin a new path.
void move_to (float x, float y) override
 Move the pen to (x, y) without drawing.
void line_to (float x, float y) override
 Add a line segment to (x, y).
void quad_to (float cpx, float cpy, float x, float y) override
 Add a quadratic bezier curve to (x, y) with control point (cpx, cpy).
void cubic_to (float cp1x, float cp1y, float cp2x, float cp2y, float x, float y) override
 Add a cubic bezier curve.
void close_path () override
 Close the current path subpath.
void fill_current_path (FillRule rule=FillRule::nonzero) override
 Fill the current path.
void stroke_current_path () override
 Stroke the current path.
void arc (float cx, float cy, float radius, float start_angle, float end_angle, bool anticlockwise) override
 Native arc subpath.
void arc_to (float x1, float y1, float x2, float y2, float radius) override
 Native arcTo.
void ellipse (float cx, float cy, float rx, float ry, float rotation, float start_angle, float end_angle, bool anticlockwise) override
 Native ellipse subpath.
void round_rect (float x, float y, float w, float h, float tl_x, float tl_y, float tr_x, float tr_y, float br_x, float br_y, float bl_x, float bl_y) override
 Native rounded-rectangle subpath with per-corner radii.
void set_opacity (float alpha) override
 Set global alpha for subsequent drawing operations (0.0-1.0).
void save_layer (float x, float y, float w, float h, float opacity, float blur_radius) override
 Save a compositing layer.
void set_blend_mode (BlendMode mode) override
 Set the compositing blend mode for subsequent draw operations.
void set_font (const std::string &family, float size) override
void set_font_full (const std::string &family, float size, int weight, int slant, float letter_spacing) override
 Richer font setter that propagates CSS font-weight (100..900), font-slant (0=upright, 1=italic), and letter-spacing (px between glyphs) through to the backend.
void set_font_features (std::vector< FontFeature > features) override
void clear_font_features () override
void set_text_align (TextAlign align) override
void fill_text (const std::string &text, float x, float y) override
void fill_text_with_max_width (const std::string &text, float x, float y, float max_width) override
 Canvas2D fillText(text, x, y, maxWidth) spec form.
void stroke_text (const std::string &text, float x, float y, float max_width=0.0f) override
 Canvas2D strokeText(text, x, y, maxWidth) spec form.
float measure_text (const std::string &text) override
TextMetrics measure_text_full (const std::string &text) override
 Measure text with full font metrics using current font settings.
float text_x_for_byte (const std::string &text, std::size_t byte_index) override
 Caret x (in text-local space, measured from the text's leading edge) for the glyph boundary at byte_index within text.
void set_shadow_color (Color color) override
 Sticky drop-shadow state that wraps subsequent draw operations, matching the CanvasRenderingContext2D shadowColor / shadowBlur / shadowOffsetX / shadowOffsetY properties.
void set_shadow_blur (float blur) override
void set_shadow_offset_x (float dx) override
void set_shadow_offset_y (float dy) override
bool supports (CanvasCapability cap) const override
 Report whether this backend renders cap's verb faithfully.
bool draw_image_from_data (const uint8_t *data, size_t size, float x, float y, float w, float h) override
 Draw an image from encoded data (PNG, JPEG, WebP) at the given rect.
bool draw_image_from_file (const std::string &path, float x, float y, float w, float h) override
 Draw an image from a file path at the given rect.
bool draw_image_from_data_rect (const uint8_t *data, size_t size, float sx, float sy, float sw, float sh, float dx, float dy, float dw, float dh) override
bool draw_image_from_file_rect (const std::string &path, float sx, float sy, float sw, float sh, float dx, float dy, float dw, float dh) override
bool measure_image_from_data (const uint8_t *data, size_t size, float &out_width, float &out_height) override
 Intrinsic pixel size of encoded image bytes, read from the image header without decoding pixels: the size draw_image_from_data would draw the full image at.
bool measure_image_from_file (const std::string &path, float &out_width, float &out_height) override
 Decoded-image intrinsic dimensions in pixels.
virtual void clip_path_svg (const std::string &svg_path_d)
 CSS clip-path: path("...") — intersect the current clip with an SVG-path-d string.
virtual void set_fill_gradient_radial_elliptical (float cx, float cy, float rx, float ry, const Color *colors, const float *positions, int count)
 Radial gradient with an independent radius per axis — the ending shape CSS calls an ellipse, which is radial-gradient's DEFAULT shape and the only one a two-value size (90% 70%) can express.
virtual void save_layer_with_bloom (float x, float y, float w, float h, float intensity, float threshold, float radius)
 Save a compositing layer that applies a real bloom/glow post-effect to the subtree painted into it.
virtual void draw_image (void *native_handle, float x, float y, float w, float h)
 Draw an image identified by an opaque, platform-specific handle into the rectangle (x, y, w, h).
virtual std::shared_ptr< SceneRecording > record_scene (float w, float h, const std::function< void(Canvas &)> &paint)
 Record paint into a replayable SceneRecording, capturing its draw commands ONCE so later frames can draw_scene() the result instead of re-running the (recursive, allocating) paint.
virtual bool draw_scene (const SceneRecording &rec)
 Replay a recording produced by THIS backend's record_scene() at the current transform/clip.
virtual void save_layer_with_blend (float x, float y, float w, float h, float opacity, float blur_radius, BlendMode mode)
 Save a compositing layer with an explicit blend mode.
virtual void save_layer_with_filters (float x, float y, float w, float h, float opacity, const FilterChainEntry *chain, int count)
virtual void save_layer_with_shader_effect (float x, float y, float w, float h, const std::string &effect_name, float intensity)
 Curated, named GPU post-effect layer save.
virtual void save_layer_with_mask (float x, float y, float w, float h, float opacity, const std::string &mask_image, const std::string &mask_size)
 CSS mask-image + mask-size paint slice.
virtual void fill_text_anchored (const std::string &text, float x, float y, TextAnchor anchor)
virtual void fill_text_sdf (const std::string &text, float x, float y, const class SdfAtlas &atlas)
 Render text using an SDF glyph atlas for resolution-independent rendering.
bool supports_image_draw () const
 Whether this backend can actually decode + draw a raster image (the draw_image_from_* verbs below).
virtual bool draw_svg (const std::string &svg_document, float x, float y, float w, float h)
 Render an SVG document (the literal <svg>…</svg> string) into the box [x,y,w,h], scaled to fit.
virtual bool read_pixels (int x, int y, int width, int height, uint8_t *out)
 Read RGBA pixels from the current surface into out (size must be at least width*height*4).
virtual bool write_pixels (const uint8_t *data, int width, int height, int dx, int dy)
 Write RGBA pixels (data size = width*height*4) to the current surface at destination (dx, dy).
virtual void draw_sdf_shape (SDFShape shape, float x, float y, float w, float h, const SDFStyle &style)
virtual bool draw_sdf_shape_with_shader (SDFShape shape, float x, float y, float w, float h, const SDFStyle &style, const std::string &sksl, const ShaderDrawOptions &options)
 Draw a shape through an author-supplied geometry chart shader.
virtual void draw_blurred_backdrop (float x, float y, float w, float h, float blur_radius, float corner_radius=0, Color tint=Color::rgba(0.0f, 0.0f, 0.0f, 0.314f))
 Save a blurred snapshot of the current canvas content as a backdrop.
virtual void save_backdrop_filter (float x, float y, float w, float h, float blur_radius)
 CSS backdrop-filter: blur(Npx) — push a compositing layer whose initial contents are the parent surface filtered through a Gaussian blur of blur_radius.
virtual void save_backdrop_filter_chain (float x, float y, float w, float h, const FilterChainEntry *chain, int count)
 CSS backdrop-filter with its full function list — saturate(0.2), brightness(2), invert(1), a grayscale(1) brightness(1.6) pair.
virtual void draw_box_shadow (float x, float y, float w, float h, float dx, float dy, float blur, float spread, Color color, bool inset=false, float corner_radius=0.0f)
 Draw a CSS-style box shadow around (or inside, when inset) a rounded rectangle anchored at (x, y, w, h).
virtual void set_direction (TextDirection direction)
virtual void set_filter (const std::string &filter)
 Canvas2D ctx.filter.
virtual void draw_waveform (const float *samples, size_t count, float x, float y, float width, float height, const WaveformStyle &style)
virtual bool draw_with_sksl (const std::string &sksl, float x, float y, float w, float h, const ShaderUniforms &uniforms)
 Draw a rectangle filled by a custom SkSL shader.
virtual bool draw_with_sksl (const std::string &sksl, float x, float y, float w, float h, const ShaderDrawOptions &options)
virtual bool save_layer_with_sksl_post_effect (float x, float y, float w, float h, const std::string &sksl, const ShaderUniforms &uniforms, float sample_radius=0.0f, const std::vector< NamedUniform > &extra_uniforms={}, BlendMode blend_mode=BlendMode::normal)
 Save a compositing layer whose ALREADY-PAINTED content is post-processed by a custom SkSL shader.
virtual void draw_image_transformed (void *native_handle, float w, float h, const AffineTransform &t)
 Draw an opaque image handle (see draw_image) under an arbitrary affine transform.
virtual void draw_image_fitted (void *native_handle, float iw, float ih, float dx, float dy, float dw, float dh, ImageFit fit, float align_x=0.5f, float align_y=0.5f)
 Draw an opaque image handle fitted into a destination box with a preserve-aspect fit mode and alignment.
virtual bool draw_image_file_fitted (const std::string &path, float dx, float dy, float dw, float dh, ImageFit fit, float align_x=0.5f, float align_y=0.5f)
 Draw a file-path image fitted into a destination box.
virtual void draw_image_tiled (void *native_handle, float tile_w, float tile_h, float dx, float dy, float dw, float dh, float offset_x=0.0f, float offset_y=0.0f)
 Fill the destination box (dx, dy, dw, dh) by tiling an opaque image handle at cells of size (tile_w x tile_h).
virtual void set_fill_gradient_linear_repeating (float x0, float y0, float x1, float y1, const Color *colors, const float *positions, int count)
 Set a REPEATING linear gradient (CSS repeating-linear-gradient).
virtual bool draw_image_from_static_data_rect (const uint8_t *data, size_t size, float sx, float sy, float sw, float sh, float dx, float dy, float dw, float dh)
 Source-rect draw for bytes with static storage duration and immutable contents.
virtual bool draw_image_from_keyed_data (const std::string &content_key, const uint8_t *data, size_t size, float x, float y, float w, float h)
 Draw encoded image bytes identified by content_key, the lowercase hex SHA-256 of exactly those bytes.
virtual bool draw_image_from_keyed_data_rect (const std::string &content_key, const uint8_t *data, size_t size, float sx, float sy, float sw, float sh, float dx, float dy, float dw, float dh)
 Source-rect form of draw_image_from_keyed_data, with the same key contract.
virtual void set_gradient_interpolation (GradientInterpolation m)
 Colour space every LATER gradient setter blends its stops in (CSS <color-interpolation-method>), until set again; save() does not keep it.
GradientInterpolation gradient_interpolation () const

Static Public Member Functions

static constexpr uint32_t make_font_feature_tag (const char(&s)[5])
 Builds a 4-byte big-endian OpenType tag from a 4-char string.
static std::string compile_sksl (const std::string &sksl)
 Validate and compile an SkSL shader without drawing.
static std::string compile_sdf_chart_sksl (SDFShape shape, const std::string &sksl)
 Compile an author chart shader against the geometry prelude used by draw_sdf_shape_with_shader.
static float sdf_feather_min_reach (const SDFStyle &style)
 The smallest draw-rect reach that holds style's feather: 3 sigma for the glow (1) and outer (3) modes, whose coverage lives outside the shape, and 0 for every other mode or a zero sigma.
static bool sksl_declares_uniform (const std::string &sksl, const std::string &uniform_name)
 Whether an SkSL shader declares a uniform of the given name.
static FitResult fit_image_rect (float iw, float ih, float dx, float dy, float dw, float dh, ImageFit fit, float align_x=0.5f, float align_y=0.5f)
 Pure geometry: place a source image of intrinsic size (iw x ih) into the destination box (dx, dy, dw, dh) per fit.

Member Typedef Documentation

◆ Point2D

Stroke a continuous polyline through an array of points.

Much smoother than individual stroke_line calls — produces proper anti-aliased curves with line joins between segments. Alias, not a second type. The canonical point lives at namespace scope in <pulp/canvas/affine_transform.hpp> so Path can use it; this name keeps every existing Canvas::Point2D call site working. (It is spelled Point2D rather than Point because Apple's MacTypes.h declares a global Point — see the note on pulp::canvas::Point2D.)

Member Enumeration Documentation

◆ BlendMode

enum class pulp::canvas::Canvas::BlendMode
stronginherited

Indices 0..15 match the existing W3C "advanced" composite ops and must stay stable — set_blend_mode in the JS bridge currently passes int_val 1/2/3 = multiply/screen/overlay.

Indices 16+ are Porter-Duff and "extra" CSS composite ops added so the JS bridge can support the full CanvasRenderingContext2D.globalCompositeOperation surface.

Enumerator
normal 
multiply 
screen 
overlay 
darken 
lighten 
color_dodge 
color_burn 
hard_light 
soft_light 
difference 
exclusion 
hue 
saturation 
color 
luminosity 
source_over 
destination_over 
source_in 
destination_in 
source_out 
destination_out 
source_atop 
destination_atop 
xor_mode 
copy 
lighter 

◆ ImageFit

enum class pulp::canvas::Canvas::ImageFit
stronginherited

Preserve-aspect fit modes for fit_image_rect / draw_image_fitted: fill — stretch to the destination box, ignoring aspect ratio.

contain — scale (preserving aspect) to the largest size that fits fully inside the box; may leave margins. cover — scale (preserving aspect) to the smallest size that fully covers the box; may crop (clipped to the box). none — natural pixel size, no scaling; positioned by alignment. scale_down — like none when the image already fits, otherwise like contain (never scales up).

Enumerator
fill 
contain 
cover 
none 
scale_down 

◆ ImageSmoothingQuality

enum class pulp::canvas::Canvas::ImageSmoothingQuality
stronginherited

Canvas2D image-smoothing quality enum.

Mirrors the spec's imageSmoothingQuality attribute. The bridge maps the JS string values ("low" | "medium" | "high") onto these.

Enumerator
low 
medium 
high 

◆ PatternTileMode

enum class pulp::canvas::Canvas::PatternTileMode
stronginherited

Canvas2D ctx.createPattern.

Tile mode per axis: repeat mirrors Skia's SkTileMode::kRepeat, no_repeat mirrors SkTileMode::kDecal. Spec values map as: "repeat" → (repeat, repeat) "repeat-x" → (repeat, no_repeat) "repeat-y" → (no_repeat, repeat) "no-repeat" → (no_repeat, no_repeat)

Enumerator
repeat 
no_repeat 

◆ SDFShape

enum class pulp::canvas::Canvas::SDFShape
stronginherited

Draw an SDF shape with anti-aliased edges via GPU shader.

Enumerator
rect 
circle 
rounded_rect 
arc 
diamond 
squircle 
triangle 
ring 
stadium 
cross 
flat_segment 
rounded_segment 
flat_arc 
quadratic_bezier 

◆ TextAnchor

enum class pulp::canvas::Canvas::TextAnchor
stronginherited

Explicit, typed vertical-anchor API.

The historical fill_text(text, x, y) follows the Canvas2D spec (web-compat baseline semantics) — y is the text baseline. Non-spec callers (widgets that mix labels with knobs / faders inside flex rows) need to opt for a different reference line so mixed-fontSize centered rows align on glyph reference, not box reference. Anchor modes:

Baseline — y is the baseline (Canvas2D web-spec default). GlyphTop — y is the top of the worst-case glyph bbox (SkFontMetrics::fTop). Useful when the caller computes top-of-text manually. GlyphCenter — y is the vertical CENTER of the glyph bbox. Use for vertical-align: middle semantics where you want the painted glyphs (not the measure box) centered on a target line. EmBoxTop — y is the top of the em-box (font-size based, font-independent). Use when laying out uniform-sized glyph grids.

Default implementation ignores anchor and treats y as the baseline, delegating straight to fill_text. Backends with font metrics override to translate y into a per-anchor baseline-y so glyphs land on the exact pixel the parity harness asserts.

Enumerator
Baseline 
GlyphTop 
GlyphCenter 
EmBoxTop 

◆ TextDirection

enum class pulp::canvas::Canvas::TextDirection
stronginherited

Canvas2D ctx.direction.

Sticky text-shaping direction that applies to subsequent fillText / strokeText calls. Spec values: ltr — left-to-right (default; matches SkShaper leftToRight=true) rtl — right-to-left (HarfBuzz buffer direction RTL) inherit — pulled from the canvas element / document writing direction. On backends without a per-View writing direction yet, treated as ltr (the most common case). Default no-op so backends without a real bidi/HarfBuzz path remain unaffected; SkiaCanvas overrides to wire through to the SkShaper invocation flag, RecordingCanvas captures one set_direction command per setter so canvas2d harness tests can assert flush order. Real bidi support (mixed-script paragraphs requiring the Bidi algorithm) tracks separately.

Enumerator
ltr 
rtl 
inherit 

Constructor & Destructor Documentation

◆ CoreGraphicsCanvas()

pulp::canvas::CoreGraphicsCanvas::CoreGraphicsCanvas ( CGContextRef ctx,
float width,
float height )
explicit

◆ ~CoreGraphicsCanvas()

pulp::canvas::CoreGraphicsCanvas::~CoreGraphicsCanvas ( )
override

Member Function Documentation

◆ arc()

void pulp::canvas::CoreGraphicsCanvas::arc ( float cx,
float cy,
float radius,
float start_angle,
float end_angle,
bool anticlockwise )
overridevirtual

Native arc subpath.

Maps to Canvas2D ctx.arc(cx, cy, r, startAngle, endAngle, anticlockwise) and to SkPath::arcTo(SkRect, startDeg, sweepDeg, false) on Skia. Replaces the JS shim's bezier approximation. The default fallback emits a quadrant-segmented cubic-bezier polyline so capture-only backends (RecordingCanvas) still produce a meaningful command stream when they choose not to override.

Reimplemented from pulp::canvas::Canvas.

◆ arc_to()

void pulp::canvas::CoreGraphicsCanvas::arc_to ( float x1,
float y1,
float x2,
float y2,
float radius )
overridevirtual

Native arcTo.

Maps to Canvas2D ctx.arcTo(x1, y1, x2, y2, radius) and to SkPath::arcTo(p1, p2, radius) on Skia (the 5-arg overload that computes the tangent arc between the current point, p1, and p2).

Reimplemented from pulp::canvas::Canvas.

◆ backing_scale()

float pulp::canvas::CoreGraphicsCanvas::backing_scale ( ) const
overridevirtualnoexcept

Effective device pixels per local canvas unit at the current transform.

Backends with an inspectable CTM override this when necessary; the default is correct for logical 1x recording/test canvases.

Reimplemented from pulp::canvas::Canvas.

◆ begin_layer()

LayerHandle pulp::canvas::CoreGraphicsCanvas::begin_layer ( Rect2D bounds,
bool cacheable = false )
overridevirtual

Begin recording into an offscreen layer covering bounds (in current user space).

Drawing between this and end_layer() goes to the layer, not the canvas.

cacheable asks the backend to KEEP the layer's texture after end_layer() so it can be redrawn on later frames without re-recording. A non-cacheable layer may be discarded as soon as it has been drawn once.

Backends without offscreen support return the null handle and draw through to the canvas directly. That degrades to "re-record every frame", which is correct — just not cached — because layer_valid() keeps returning false and the caller's if above keeps re-recording.

Reimplemented from pulp::canvas::Canvas.

◆ begin_path()

void pulp::canvas::CoreGraphicsCanvas::begin_path ( )
overridevirtual

Begin a new path.

Reimplemented from pulp::canvas::Canvas.

◆ capture_paint_baseline_transform()

void pulp::canvas::CoreGraphicsCanvas::capture_paint_baseline_transform ( )
overridevirtual

Snapshot the current device matrix as the baseline for subsequent set_transform() calls.

CanvasWidget::paint() calls this at entry so JS-supplied setTransform() composes onto the parent View transform rather than wiping it. Default no-op for non-Skia backends.

Reimplemented from pulp::canvas::Canvas.

◆ clear_fill_gradient()

void pulp::canvas::CoreGraphicsCanvas::clear_fill_gradient ( )
overridevirtual

Clear gradient, return to solid fill color.

Reimplemented from pulp::canvas::Canvas.

◆ clear_font_features()

void pulp::canvas::CoreGraphicsCanvas::clear_font_features ( )
overridevirtual

Reimplemented from pulp::canvas::Canvas.

◆ clear_rect()

void pulp::canvas::CoreGraphicsCanvas::clear_rect ( float x,
float y,
float w,
float h )
overridevirtual

Clear a rectangular region to transparent black (rgba 0,0,0,0).

Equivalent to CanvasRenderingContext2D.clearRect — replaces existing pixels rather than compositing over them. Pixel backends (SkiaCanvas) override with a kSrc/kClear paint so the underlying texels actually become transparent; the default falls back to a SrcOver fill with a transparent color (a no-op on most surfaces, but keeps recording / introspection backends inert). Without this replacement path, ctx.clearRect() on the canvas widget would not clear residual or parent-painted pixels.

Reimplemented from pulp::canvas::Canvas.

◆ clear_stroke_gradient()

void pulp::canvas::CoreGraphicsCanvas::clear_stroke_gradient ( )
overridevirtual

Clear stroke gradient/pattern shader, restore solid stroke color.

Reimplemented from pulp::canvas::Canvas.

◆ clip()

void pulp::canvas::CoreGraphicsCanvas::clip ( FillRule rule = FillRule::nonzero)
overridevirtual

Intersect the current clip region with the current path.

Mirrors CanvasRenderingContext2D.clip(). Default no-op so backends without a path builder remain unaffected. rule selects nonzero vs evenodd winding.

Reimplemented from pulp::canvas::Canvas.

◆ clip_path()

void pulp::canvas::CoreGraphicsCanvas::clip_path ( const Path & path,
FillRule rule = FillRule::nonzero )
overridevirtual

Intersect the current clip with path. Caller owns save()/restore().

Reimplemented from pulp::canvas::Canvas.

◆ clip_path_svg()

virtual void pulp::canvas::Canvas::clip_path_svg ( const std::string & svg_path_d)
inlinevirtualinherited

CSS clip-path: path("...") — intersect the current clip with an SVG-path-d string.

Skia maps to SkPath::FromSVGString + SkCanvas::clipPath. RecordingCanvas captures a clip_path_svg command for tests; backends without a path parser fall back to a no-op so callers can invoke it unconditionally without breaking save/restore bookkeeping. The caller is responsible for save()/restore() balancing — typical usage is save(); clip_path_svg("M..."); ...children...; restore();.

Reimplemented in pulp::canvas::RecordingCanvas.

◆ clip_rect()

void pulp::canvas::CoreGraphicsCanvas::clip_rect ( float x,
float y,
float w,
float h )
overridevirtual

Implements pulp::canvas::Canvas.

◆ close_path()

void pulp::canvas::CoreGraphicsCanvas::close_path ( )
overridevirtual

Close the current path subpath.

Reimplemented from pulp::canvas::Canvas.

◆ compile_sdf_chart_sksl()

std::string pulp::canvas::Canvas::compile_sdf_chart_sksl ( SDFShape shape,
const std::string & sksl )
staticinherited

Compile an author chart shader against the geometry prelude used by draw_sdf_shape_with_shader.

The author source must define shade(PulpChart).

◆ compile_sksl()

std::string pulp::canvas::Canvas::compile_sksl ( const std::string & sksl)
staticinherited

Validate and compile an SkSL shader without drawing.

Returns error string (empty = success). Static so it can be called without a Canvas instance. Non-Skia builds return a non-empty "Skia not available" error rather than reporting a false success — there is no compiler to ask.

◆ concat_transform()

void pulp::canvas::CoreGraphicsCanvas::concat_transform ( float a,
float b,
float c,
float d,
float e,
float f )
overridevirtual

Concat (multiply) the supplied affine matrix onto the current transform — does NOT replace it.

Used by View::paint_all() when a JS-supplied setTransform(id,a,b,c,d,e,f) is active on a View, so the View's transform composes with parent transforms (translate to bounds, outer transforms, etc.) rather than wiping them. Mirrors SkCanvas::concat. Matrix layout matches CanvasRenderingContext2D.setTransform: [ a c e ] [ b d f ] [ 0 0 1 ] Default no-op so non-Skia backends compile; Skia overrides to call SkCanvas::concat, RecordingCanvas overrides to capture the command.

Reimplemented from pulp::canvas::Canvas.

◆ cubic_to()

void pulp::canvas::CoreGraphicsCanvas::cubic_to ( float cp1x,
float cp1y,
float cp2x,
float cp2y,
float x,
float y )
overridevirtual

Add a cubic bezier curve.

Reimplemented from pulp::canvas::Canvas.

◆ current_transform()

AffineTransform2x3 pulp::canvas::CoreGraphicsCanvas::current_transform ( ) const
overridevirtual

Snapshot the current device matrix (CTM) without mutating canvas state.

Used by CanvasWidget::paint() diagnostics to log the inbound transform per paint when PULP_LOG_CANVAS_PAINT=1 is set, so we can confirm whether a missing canvas paint is caused by the CTM ending up off-window vs the widget never being painted at all. Default returns identity for backends without an introspectable matrix; SkiaCanvas (getTotalMatrix), CoreGraphicsCanvas (CGContextGetCTM), and RecordingCanvas (manual matrix tracking) override.

Reimplemented from pulp::canvas::Canvas.

◆ draw_blurred_backdrop()

virtual void pulp::canvas::Canvas::draw_blurred_backdrop ( float x,
float y,
float w,
float h,
float blur_radius,
float corner_radius = 0,
Color tint = Color::rgba(0.0f, 0.0f, 0.0f, 0.314f) )
inlinevirtualinherited

Save a blurred snapshot of the current canvas content as a backdrop.

Call before painting the overlay content.

◆ draw_box_shadow()

virtual void pulp::canvas::Canvas::draw_box_shadow ( float x,
float y,
float w,
float h,
float dx,
float dy,
float blur,
float spread,
Color color,
bool inset = false,
float corner_radius = 0.0f )
virtualinherited

Draw a CSS-style box shadow around (or inside, when inset) a rounded rectangle anchored at (x, y, w, h).

When inset is false this is a drop shadow rendered outside the box; when true it is an inner shadow rendered inside the box clipped to the box geometry. Default implementation is a CPU fallback that approximates the blur with stacked translucent rounded rects — Skia overrides with SkImageFilters::DropShadowOnly for a true Gaussian shadow. The out-of-line definition lives in core/canvas/src/recording_canvas.cpp so the CPU fallback is exercised by the canvas-level tests during coverage runs.

Reimplemented in pulp::canvas::RecordingCanvas.

◆ draw_image()

virtual void pulp::canvas::Canvas::draw_image ( void * native_handle,
float x,
float y,
float w,
float h )
inlinevirtualinherited

Draw an image identified by an opaque, platform-specific handle into the rectangle (x, y, w, h).

The handle is the native_handle published by pulp::view::ImageCache (see core/view/include/pulp/view/image_cache.hpp). Backends know how to interpret their own handles:

  • CoreGraphicsCanvas: CGImageRef
  • SkiaCanvas: SkImage* Canvases without an image pipeline are a no-op by default so widgets can call this unconditionally — the placeholder layer behind them keeps the UI non-blank.

Reimplemented in pulp::canvas::RecordingCanvas.

◆ draw_image_file_fitted()

virtual bool pulp::canvas::Canvas::draw_image_file_fitted ( const std::string & path,
float dx,
float dy,
float dw,
float dh,
ImageFit fit,
float align_x = 0.5f,
float align_y = 0.5f )
inlinevirtualinherited

Draw a file-path image fitted into a destination box.

Consults measure_image_from_file for the intrinsic size; if the backend cannot measure it, falls back to stretching the image to the box (the plain draw_image_from_file behavior). Returns whatever the underlying draw returns (false when the backend cannot draw the image).

◆ draw_image_fitted()

virtual void pulp::canvas::Canvas::draw_image_fitted ( void * native_handle,
float iw,
float ih,
float dx,
float dy,
float dw,
float dh,
ImageFit fit,
float align_x = 0.5f,
float align_y = 0.5f )
inlinevirtualinherited

Draw an opaque image handle fitted into a destination box with a preserve-aspect fit mode and alignment.

iw / ih are the image's intrinsic pixel dimensions (the handle is opaque, so the caller supplies them). The default clips to the destination box — so cover cannot paint outside it — and draws the fitted rectangle via draw_image.

◆ draw_image_from_data()

bool pulp::canvas::CoreGraphicsCanvas::draw_image_from_data ( const uint8_t * data,
size_t size,
float x,
float y,
float w,
float h )
overridevirtual

Draw an image from encoded data (PNG, JPEG, WebP) at the given rect.

Returns true if the image was decoded and drawn successfully.

Reimplemented from pulp::canvas::Canvas.

◆ draw_image_from_data_rect()

bool pulp::canvas::CoreGraphicsCanvas::draw_image_from_data_rect ( const uint8_t * data,
size_t size,
float sx,
float sy,
float sw,
float sh,
float dx,
float dy,
float dw,
float dh )
overridevirtual

Reimplemented from pulp::canvas::Canvas.

◆ draw_image_from_file()

bool pulp::canvas::CoreGraphicsCanvas::draw_image_from_file ( const std::string & path,
float x,
float y,
float w,
float h )
overridevirtual

Draw an image from a file path at the given rect.

Reimplemented from pulp::canvas::Canvas.

◆ draw_image_from_file_rect()

bool pulp::canvas::CoreGraphicsCanvas::draw_image_from_file_rect ( const std::string & path,
float sx,
float sy,
float sw,
float sh,
float dx,
float dy,
float dw,
float dh )
overridevirtual

Reimplemented from pulp::canvas::Canvas.

◆ draw_image_from_keyed_data()

virtual bool pulp::canvas::Canvas::draw_image_from_keyed_data ( const std::string & content_key,
const uint8_t * data,
size_t size,
float x,
float y,
float w,
float h )
inlinevirtualinherited

Draw encoded image bytes identified by content_key, the lowercase hex SHA-256 of exactly those bytes.

A backend may retain the decoded or GPU-uploaded image under that key across frames, so the key must change whenever the bytes do and must never be derived from the buffer address (a freed buffer's address is reused for different bytes). An empty key draws uncached. The default does not cache; it draws exactly as draw_image_from_data. Appended to preserve existing vtable slots.

◆ draw_image_from_keyed_data_rect()

virtual bool pulp::canvas::Canvas::draw_image_from_keyed_data_rect ( const std::string & content_key,
const uint8_t * data,
size_t size,
float sx,
float sy,
float sw,
float sh,
float dx,
float dy,
float dw,
float dh )
inlinevirtualinherited

Source-rect form of draw_image_from_keyed_data, with the same key contract.

The default draws exactly as draw_image_from_data_rect.

◆ draw_image_from_static_data_rect()

virtual bool pulp::canvas::Canvas::draw_image_from_static_data_rect ( const uint8_t * data,
size_t size,
float sx,
float sy,
float sw,
float sh,
float dx,
float dy,
float dw,
float dh )
inlinevirtualinherited

Source-rect draw for bytes with static storage duration and immutable contents.

GPU backends may retain a decoded/uploaded image across frames. Appended to preserve the slots of existing Canvas virtual methods. Never use this for a mutable or short-lived buffer.

◆ draw_image_tiled()

virtual void pulp::canvas::Canvas::draw_image_tiled ( void * native_handle,
float tile_w,
float tile_h,
float dx,
float dy,
float dw,
float dh,
float offset_x = 0.0f,
float offset_y = 0.0f )
inlinevirtualinherited

Fill the destination box (dx, dy, dw, dh) by tiling an opaque image handle at cells of size (tile_w x tile_h).

offset_x / offset_y shift the tile origin (for scrolling patterns). The default clips to the box and lays whole tiles via repeated draw_image; the trailing row / column overshoot the box but are clipped away. Pixel backends may override with a single tiled-shader fill; set_fill_pattern is the sticky shader-based alternative when a fill PATH (rather than a direct box) is what needs the tiling.

◆ draw_image_transformed()

virtual void pulp::canvas::Canvas::draw_image_transformed ( void * native_handle,
float w,
float h,
const AffineTransform & t )
inlinevirtualinherited

Draw an opaque image handle (see draw_image) under an arbitrary affine transform.

The image's natural box [0, 0, w, h] is painted through t, composed onto the current transform. Default composes concat_transform + draw_image inside a save / restore pair so the caller's transform state is preserved.

◆ draw_layer()

void pulp::canvas::CoreGraphicsCanvas::draw_layer ( LayerHandle layer,
float alpha = 1.0f,
BlendMode mode = BlendMode::normal )
overridevirtual

Composite a sealed layer at its recorded position.

Reimplemented from pulp::canvas::Canvas.

◆ draw_layer_fitted()

void pulp::canvas::CoreGraphicsCanvas::draw_layer_fitted ( LayerHandle layer,
Rect2D dest )
overridevirtual

Composite a sealed layer scaled into dest.

Reimplemented from pulp::canvas::Canvas.

◆ draw_layer_rotated()

void pulp::canvas::CoreGraphicsCanvas::draw_layer_rotated ( LayerHandle layer,
float angle_rad )
overridevirtual

Composite a sealed layer rotated about its own center.

Reimplemented from pulp::canvas::Canvas.

◆ draw_path_shadow()

void pulp::canvas::CoreGraphicsCanvas::draw_path_shadow ( const Path & path,
float dx,
float dy,
float blur,
float spread,
Color color )
overridevirtual

Draw ONLY the drop shadow cast by path — not the path itself.

This is a shadow-*rendering* primitive, deliberately separate from save_layer_with_filters's drop_shadow filter Kind (which shadows a whole painted subtree). The split exists because the common case — "shadow behind this icon, then the icon on top, in a different color" — needs the shadow WITHOUT the source, and a filter chain always composites the source too.

spread grows the shadow's silhouette before blurring, matching the CSS box-shadow spread term. The default fallback approximates it by scaling the path about its own center, which is exact for convex shapes and an approximation for concave ones (a true spread is a Minkowski sum / path outset, which is not something the immediate-mode API can express).

Reimplemented from pulp::canvas::Canvas.

◆ draw_scene()

virtual bool pulp::canvas::Canvas::draw_scene ( const SceneRecording & rec)
inlinevirtualinherited

Replay a recording produced by THIS backend's record_scene() at the current transform/clip.

Returns false when rec is not this backend's recording type (defensive — a foreign or base recording is never drawn), leaving the caller to fall back to a direct paint.

◆ draw_sdf_shape()

virtual void pulp::canvas::Canvas::draw_sdf_shape ( SDFShape shape,
float x,
float y,
float w,
float h,
const SDFStyle & style )
inlinevirtualinherited

◆ draw_sdf_shape_with_shader()

virtual bool pulp::canvas::Canvas::draw_sdf_shape_with_shader ( SDFShape shape,
float x,
float y,
float w,
float h,
const SDFStyle & style,
const std::string & sksl,
const ShaderDrawOptions & options )
inlinevirtualinherited

Draw a shape through an author-supplied geometry chart shader.

The shader must define half4 shade(PulpChart g).

Charted shapes are flat_arc, ring, flat_segment, rounded_segment and stadium; any other shape is refused, by name, rather than drawn. g.cross is the signed offset from the centre line, g.d the band's distance. g.valid is zero for degenerate geometry (no sweep, length or band); callers must not interpret chart fields when it is zero.

g.px is one device pixel expressed in the same units as g.d, taken from backing_scale(), so 2.0 * g.px is two device pixels at any backing scale rather than only at 1x.

The shader is evaluated over the w x h rect grown by options.reach on every side, so a feather or glow can paint past the shape's box. A glow or outer feather whose reach is below sdf_feather_min_reach(style) is refused (returns false, draws nothing) rather than drawn with its falloff cut off at the rect edge.

◆ draw_svg()

virtual bool pulp::canvas::Canvas::draw_svg ( const std::string & svg_document,
float x,
float y,
float w,
float h )
inlinevirtualinherited

Render an SVG document (the literal <svg>…</svg> string) into the box [x,y,w,h], scaled to fit.

Backed by Skia's SkSVGDOM, so gradients, filters (drop shadows), masks, opacities, and transforms render exactly as authored — the faithful-vector design-import lane draws a Figma node's own SVG export through here instead of approximating it. Returns false if the backend can't render SVG (non-Skia) or the document fails to parse.

◆ draw_waveform()

virtual void pulp::canvas::Canvas::draw_waveform ( const float * samples,
size_t count,
float x,
float y,
float width,
float height,
const WaveformStyle & style )
inlinevirtualinherited

◆ draw_with_sksl() [1/2]

virtual bool pulp::canvas::Canvas::draw_with_sksl ( const std::string & sksl,
float x,
float y,
float w,
float h,
const ShaderDrawOptions & options )
inlinevirtualinherited

◆ draw_with_sksl() [2/2]

virtual bool pulp::canvas::Canvas::draw_with_sksl ( const std::string & sksl,
float x,
float y,
float w,
float h,
const ShaderUniforms & uniforms )
inlinevirtualinherited

Draw a rectangle filled by a custom SkSL shader.

Only works on GPU backends (SkiaCanvas). CPU backends draw a fallback rect. The shader receives: uniform float2 resolution, float value, float time, layout(color) float4 accentColor/bgColor/trackColor/fillColor/thumbColor.

Reimplemented in pulp::canvas::RecordingCanvas.

◆ ellipse()

void pulp::canvas::CoreGraphicsCanvas::ellipse ( float cx,
float cy,
float rx,
float ry,
float rotation,
float start_angle,
float end_angle,
bool anticlockwise )
overridevirtual

Native ellipse subpath.

Maps to Canvas2D ctx.ellipse(cx, cy, rx, ry, rotation, startAngle, endAngle, anticlockwise). Honours rotation (rx/ry axis rotation in radians) by transforming the arc through SkMatrix on Skia.

Reimplemented from pulp::canvas::Canvas.

◆ end_layer()

LayerHandle pulp::canvas::CoreGraphicsCanvas::end_layer ( )
overridevirtual

Seal the layer opened by the last begin_layer() and return its handle.

After this, drawing goes back to the canvas (or the enclosing layer).

Reimplemented from pulp::canvas::Canvas.

◆ fill_circle()

void pulp::canvas::CoreGraphicsCanvas::fill_circle ( float cx,
float cy,
float radius )
overridevirtual

Implements pulp::canvas::Canvas.

◆ fill_current_path()

void pulp::canvas::CoreGraphicsCanvas::fill_current_path ( FillRule rule = FillRule::nonzero)
overridevirtual

Fill the current path.

rule selects nonzero vs evenodd winding, matching Canvas2D ctx.fill('evenodd').

Reimplemented from pulp::canvas::Canvas.

◆ fill_path() [1/2]

void pulp::canvas::CoreGraphicsCanvas::fill_path ( const Path & path,
FillRule rule = FillRule::nonzero )
overridevirtual

Fill path under rule.

Resets the current path, matching the Canvas2D convention that a shape-drawing call owns the path it builds.

Reimplemented from pulp::canvas::Canvas.

◆ fill_path() [2/2]

void pulp::canvas::CoreGraphicsCanvas::fill_path ( const Point2D * points,
size_t count,
FillRule rule = FillRule::nonzero )
overridevirtual

Fill a closed polygon defined by points, honoring rule.

rule selects nonzero vs even-odd winding, exactly as fill_current_path(rule) / Canvas2D ctx.fill('evenodd') do. Without it a compound point array (an outer contour followed by an inner one) could only ever fill as a solid disc — there was no way to punch the hole that makes it a ring.

The default is NOT a no-op. It synthesizes the polygon through the path API — begin_path / move_to / line_to / close_path / fill_current_path(rule) — which every backend already implements, so a Canvas subclass that does not override fill_path still draws the polygon instead of silently dropping it. Backends override only to skip the state round-trip (SkiaCanvas builds one SkPath; CoreGraphicsCanvas one CGPath).

Note the default resets the current path (begin_path), matching the Canvas2D convention that a shape-drawing call owns the path it builds. Degenerate input (null array, or fewer than three points — no enclosed area) draws nothing and never reads the array.

Reimplemented from pulp::canvas::Canvas.

◆ fill_rect()

void pulp::canvas::CoreGraphicsCanvas::fill_rect ( float x,
float y,
float w,
float h )
overridevirtual

Implements pulp::canvas::Canvas.

◆ fill_rounded_rect()

void pulp::canvas::CoreGraphicsCanvas::fill_rounded_rect ( float x,
float y,
float w,
float h,
float radius )
overridevirtual

Implements pulp::canvas::Canvas.

◆ fill_text()

void pulp::canvas::CoreGraphicsCanvas::fill_text ( const std::string & text,
float x,
float y )
overridevirtual

Implements pulp::canvas::Canvas.

◆ fill_text_anchored()

virtual void pulp::canvas::Canvas::fill_text_anchored ( const std::string & text,
float x,
float y,
TextAnchor anchor )
inlinevirtualinherited

◆ fill_text_sdf()

virtual void pulp::canvas::Canvas::fill_text_sdf ( const std::string & text,
float x,
float y,
const class SdfAtlas & atlas )
inlinevirtualinherited

Render text using an SDF glyph atlas for resolution-independent rendering.

The atlas must be pre-built via SdfAtlas::build(). Falls back to fill_text() if GPU is unavailable or the atlas doesn't contain the required glyphs.

◆ fill_text_with_max_width()

void pulp::canvas::CoreGraphicsCanvas::fill_text_with_max_width ( const std::string & text,
float x,
float y,
float max_width )
overridevirtual

Canvas2D fillText(text, x, y, maxWidth) spec form.

When max_width > 0 and the natural rendered advance exceeds it, the backend MUST scale the text horizontally so the resulting run is exactly max_width px wide (vertical metrics unchanged). The default implementation falls through to fill_text(text, x, y), preserving legacy behavior for backends that don't yet honor the constraint (RecordingCanvas, base mocks). Skia and CoreGraphics override this to apply an SkMatrix::Scale(maxWidth/measured, 1) (Skia) / CGContextScaleCTM(scale, 1) (CG) around the text origin. Glyph cluster boundaries are unaffected — HarfBuzz already shapes each cluster as an indivisible unit, so horizontal compression preserves cluster integrity by construction.

Reimplemented from pulp::canvas::Canvas.

◆ fit_image_rect()

FitResult pulp::canvas::Canvas::fit_image_rect ( float iw,
float ih,
float dx,
float dy,
float dw,
float dh,
ImageFit fit,
float align_x = 0.5f,
float align_y = 0.5f )
inlinestaticinherited

Pure geometry: place a source image of intrinsic size (iw x ih) into the destination box (dx, dy, dw, dh) per fit.

Alignment align_x / align_y are fractions in [0, 1]: 0 = left / top, 0.5 = center, 1 = right / bottom, positioning the scaled image inside (or, for cover, over) the box. Side-effect free, so it is unit-testable on its own without a backend.

◆ gradient_interpolation()

GradientInterpolation pulp::canvas::Canvas::gradient_interpolation ( ) const
inlineinherited

◆ invalidate_layer()

void pulp::canvas::CoreGraphicsCanvas::invalidate_layer ( LayerHandle layer)
overridevirtual

Drop layer's texture.

The next layer_valid() returns false, so the caller re-records it. Call this when the layer's contents change; backends may also perform the same retirement for context/scale changes.

Reimplemented from pulp::canvas::Canvas.

◆ layer_valid()

bool pulp::canvas::CoreGraphicsCanvas::layer_valid ( LayerHandle layer) const
overridevirtual

Is layer still backed by a live texture that can be drawn WITHOUT re-recording it?

Check every frame: explicit invalidation and backend context/backing-scale lifecycle changes may retire a handle.

Reimplemented from pulp::canvas::Canvas.

◆ line_to()

void pulp::canvas::CoreGraphicsCanvas::line_to ( float x,
float y )
overridevirtual

Add a line segment to (x, y).

Reimplemented from pulp::canvas::Canvas.

◆ make_font_feature_tag()

constexpr uint32_t pulp::canvas::Canvas::make_font_feature_tag ( const char(&) s[5])
inlinestaticconstexprinherited

Builds a 4-byte big-endian OpenType tag from a 4-char string.

Caller supplies exactly 4 ASCII chars (e.g. "tnum"); shorter strings are a compile error.

◆ measure_image_from_data()

bool pulp::canvas::CoreGraphicsCanvas::measure_image_from_data ( const uint8_t * data,
size_t size,
float & out_width,
float & out_height )
overridevirtual

Intrinsic pixel size of encoded image bytes, read from the image header without decoding pixels: the size draw_image_from_data would draw the full image at.

Returns false (sizes 0) when the bytes are not an image this backend can decode. The default reads a PNG IHDR only, so a backend with no decoder still sizes PNGs; Skia and CoreGraphics read every format their decoders accept. Appended after the existing virtuals.

Reimplemented from pulp::canvas::Canvas.

◆ measure_image_from_file()

bool pulp::canvas::CoreGraphicsCanvas::measure_image_from_file ( const std::string & path,
float & out_width,
float & out_height )
overridevirtual

Decoded-image intrinsic dimensions in pixels.

Returned via out-params so backends without image decode just leave them at 0 and callers short-circuit to "use destination bounds" (the previous behavior). Used by ImageView::paint to honor CSS object-fit / object-position and by canvas2d sprite sizing. Default returns false; Skia overrides to consult its decoder.

Reimplemented from pulp::canvas::Canvas.

◆ measure_text()

float pulp::canvas::CoreGraphicsCanvas::measure_text ( const std::string & text)
overridevirtual

Implements pulp::canvas::Canvas.

◆ measure_text_full()

TextMetrics pulp::canvas::CoreGraphicsCanvas::measure_text_full ( const std::string & text)
overridevirtual

Measure text with full font metrics using current font settings.

Reimplemented from pulp::canvas::Canvas.

◆ move_to()

void pulp::canvas::CoreGraphicsCanvas::move_to ( float x,
float y )
overridevirtual

Move the pen to (x, y) without drawing.

Reimplemented from pulp::canvas::Canvas.

◆ quad_to()

void pulp::canvas::CoreGraphicsCanvas::quad_to ( float cpx,
float cpy,
float x,
float y )
overridevirtual

Add a quadratic bezier curve to (x, y) with control point (cpx, cpy).

Reimplemented from pulp::canvas::Canvas.

◆ read_pixels()

virtual bool pulp::canvas::Canvas::read_pixels ( int x,
int y,
int width,
int height,
uint8_t * out )
inlinevirtualinherited

Read RGBA pixels from the current surface into out (size must be at least width*height*4).

Returns true on success. Default returns false (RecordingCanvas, CG fallback) — only Skia raster surfaces implement this end-to-end. Mirrors CanvasRenderingContext2D.getImageData().

◆ record_scene()

virtual std::shared_ptr< SceneRecording > pulp::canvas::Canvas::record_scene ( float w,
float h,
const std::function< void(Canvas &)> & paint )
inlinevirtualinherited

Record paint into a replayable SceneRecording, capturing its draw commands ONCE so later frames can draw_scene() the result instead of re-running the (recursive, allocating) paint.

w/h are the recorded content's logical size — the recording is resolution-INDEPENDENT (it stores commands in logical coordinates), so a DPI change replays the same recording with no re-record and no image re-upload.

Distinct from the begin_layer/draw_layer texture cache above: that snapshots a rasterised texture (Visage-style), this records the command stream (a lighter, resolution-free cache). Both are opt-in caches over the same "paint a subtree once, reuse it" need.

Returns nullptr when the backend cannot record (the base class, the CoreGraphics backend, and RecordingCanvas all do) — the caller MUST then paint directly every frame. supports(CanvasCapability::scene_cache) reports which backends record for real, so a caller can decide up front.

◆ renderer_backend()

RendererBackend pulp::canvas::CoreGraphicsCanvas::renderer_backend ( ) const
inlineoverridevirtualnoexcept

Identity of the renderer this canvas actually wraps.

Reimplemented from pulp::canvas::Canvas.

◆ restore()

void pulp::canvas::CoreGraphicsCanvas::restore ( )
overridevirtual

Implements pulp::canvas::Canvas.

◆ restore_to_count()

void pulp::canvas::CoreGraphicsCanvas::restore_to_count ( int target)
overridevirtual

Pop the save stack down to target (typically captured earlier via save_count()).

Backends that support it pop any leftover saves so an unbalanced JS draw script can't leak a ctx.save() into the parent View's paint scope. Default is a no-op so non-tracking backends keep their existing behavior; the CanvasWidget defense is meaningful only on backends that override both methods.

Reimplemented from pulp::canvas::Canvas.

◆ rotate()

void pulp::canvas::CoreGraphicsCanvas::rotate ( float radians)
overridevirtual

Implements pulp::canvas::Canvas.

◆ round_rect()

void pulp::canvas::CoreGraphicsCanvas::round_rect ( float x,
float y,
float w,
float h,
float tl_x,
float tl_y,
float tr_x,
float tr_y,
float br_x,
float br_y,
float bl_x,
float bl_y )
overridevirtual

Native rounded-rectangle subpath with per-corner radii.

Maps to Canvas2D ctx.roundRect(x, y, w, h, radii) and to SkRRect::MakeRectRadii on Skia. Radii layout matches the CSS 8-value form: top-left x/y, top-right x/y, bottom-right x/y, bottom-left x/y. The JS shim normalizes the 1/2/3/4-value forms to 8 floats before crossing the bridge.

Reimplemented from pulp::canvas::Canvas.

◆ save()

void pulp::canvas::CoreGraphicsCanvas::save ( )
overridevirtual

Implements pulp::canvas::Canvas.

◆ save_backdrop_filter()

virtual void pulp::canvas::Canvas::save_backdrop_filter ( float x,
float y,
float w,
float h,
float blur_radius )
inlinevirtualinherited

CSS backdrop-filter: blur(Npx) — push a compositing layer whose initial contents are the parent surface filtered through a Gaussian blur of blur_radius.

Subsequent draws into this layer composite over the blurred backdrop. Must be paired with restore().

Skia maps to SkCanvas::saveLayer(SaveLayerRec{ .fBackdrop = Blur }). CPU/recording fallback is a plain save() so the matching restore() stays balanced — visual fidelity downgrades to an unblurred overlay.

Reimplemented in pulp::canvas::RecordingCanvas.

◆ save_backdrop_filter_chain()

virtual void pulp::canvas::Canvas::save_backdrop_filter_chain ( float x,
float y,
float w,
float h,
const FilterChainEntry * chain,
int count )
inlinevirtualinherited

CSS backdrop-filter with its full function list — saturate(0.2), brightness(2), invert(1), a grayscale(1) brightness(1.6) pair.

Same layer contract as save_backdrop_filter: the layer's initial contents are the parent surface run through the composed filter, and it must be paired with restore().

The default keeps exactly the behaviour every backend already had — blur entries summed, everything else dropped — so a backend that has not implemented the chain renders an unfiltered (or merely blurred) backdrop rather than an unbalanced canvas stack. Query CanvasCapability::backdrop_filter_chain to find out which you got.

◆ save_count()

int pulp::canvas::CoreGraphicsCanvas::save_count ( ) const
inlineoverridevirtual

Return the current save-stack depth.

Used by CanvasWidget::paint() to defend against JS-driven ctx.save() / ctx.restore() imbalance: snapshot the depth at paint entry, replay the queued commands, then restore_to_count(initial_depth) to drop any leftover saves. Mirrors SkCanvas::getSaveCount / CGContext save-stack depth. Default returns 0 — backends without an introspectable stack rely on the default restore_to_count no-op below to leave behavior unchanged.

Reimplemented from pulp::canvas::Canvas.

◆ save_layer()

void pulp::canvas::CoreGraphicsCanvas::save_layer ( float x,
float y,
float w,
float h,
float opacity,
float blur_radius )
overridevirtual

Save a compositing layer.

All drawing until restore() is composited with the given opacity and optional blur. This is the correct way to implement CSS opacity and filter:blur() — the subtree paints into an offscreen buffer and is composited back as a single unit.

Reimplemented from pulp::canvas::Canvas.

◆ save_layer_with_blend()

virtual void pulp::canvas::Canvas::save_layer_with_blend ( float x,
float y,
float w,
float h,
float opacity,
float blur_radius,
BlendMode mode )
inlinevirtualinherited

Save a compositing layer with an explicit blend mode.

Used by CSS / RN mix-blend-mode: the subtree paints into the offscreen layer and the layer-paint composites back with the requested blend mode. Default BlendMode::normal is equivalent to the plain save_layer overload (kSrcOver). Subclasses that don't know how to honor the blend mode fall back to the plain overload.

Reimplemented in pulp::canvas::RecordingCanvas.

◆ save_layer_with_bloom()

virtual void pulp::canvas::Canvas::save_layer_with_bloom ( float x,
float y,
float w,
float h,
float intensity,
float threshold,
float radius )
inlinevirtualinherited

Save a compositing layer that applies a real bloom/glow post-effect to the subtree painted into it.

The layer content is thresholded (only pixels brighter than threshold contribute), blurred by radius, and additively composited (kPlus) back over the original — so bright regions bleed a glow beyond their edges. Restored by the matching restore().

Parameters
intensityglow gain applied to the thresholded highlights
thresholdbrightness cutoff in [0,1]; pixels below it don't glow
radiusblur radius (px) of the glow

The base implementation has no offscreen post-processing, so it degrades to a plain blurred layer (save_layer with radius * intensity blur) — the de-facto behavior effects had before a real bloom existed. Only the GPU backend (SkiaCanvas) implements the true threshold+blur+additive graph; RecordingCanvas records the bloom intent for headless assertions.

Reimplemented in pulp::canvas::RecordingCanvas.

◆ save_layer_with_filters()

virtual void pulp::canvas::Canvas::save_layer_with_filters ( float x,
float y,
float w,
float h,
float opacity,
const FilterChainEntry * chain,
int count )
inlinevirtualinherited

Reimplemented in pulp::canvas::RecordingCanvas.

◆ save_layer_with_mask()

virtual void pulp::canvas::Canvas::save_layer_with_mask ( float x,
float y,
float w,
float h,
float opacity,
const std::string & mask_image,
const std::string & mask_size )
inlinevirtualinherited

CSS mask-image + mask-size paint slice.

Composes the subtree with a mask shader applied via SkBlendMode:: kDstIn so the painted result keeps only the alpha that the mask permits. The Pulp pipeline: saveLayer(opacity) <- subtree paint target ... subtree paints into the layer ... saveLayer(SkBlendMode::kDstIn) <- mask composite layer drawShader(mask_image, scaled by mask_size) restore() <- composes the mask alpha onto the subtree restore() <- composes the masked subtree to the parent

mask_image is the CSS value verbatim — typically linear-gradient(...), radial-gradient(...), url(...), or none. mask_size is the CSS mask-size value (auto | cover | contain | <length>{1,2}).

The matching restore() call drives the mask composite (the canvas tracks pending masks on its save stack). Subclasses that can't honor the mask fall back to the plain save_layer overload (no mask applied — matches the existing behavior for backends without shader support). Unrecognized / unparseable mask_image values also fall back to the no-mask path so authors don't lose their content to a parser error.

Reimplemented in pulp::canvas::RecordingCanvas.

◆ save_layer_with_shader_effect()

virtual void pulp::canvas::Canvas::save_layer_with_shader_effect ( float x,
float y,
float w,
float h,
const std::string & effect_name,
float intensity )
inlinevirtualinherited

Curated, named GPU post-effect layer save.

This is the SAFE shader-effect entry for scripted / generated UIs: the subtree paints into an offscreen layer, and at restore() the whole layer is post-processed by ONE of a small vetted set of SkSL effects selected BY NAME — never arbitrary shader source. Known names (see core/canvas/src/named_shader_effects.hpp): crt, grain, vignette, noise, brushed, bloom. intensity is the single 0..1 strength knob (clamped by the backend).

Unknown names, degenerate sizes, or a Skia/GPU-unavailable backend all degrade gracefully to a plain save_layer (effect skipped) so a generated UI can request an effect unconditionally and headless CPU renders never crash. The default impl here IS that fallback; only SkiaCanvas builds the runtime-effect image filter.

◆ save_layer_with_sksl_post_effect()

virtual bool pulp::canvas::Canvas::save_layer_with_sksl_post_effect ( float x,
float y,
float w,
float h,
const std::string & sksl,
const ShaderUniforms & uniforms,
float sample_radius = 0.0f,
const std::vector< NamedUniform > & extra_uniforms = {},
BlendMode blend_mode = BlendMode::normal )
inlinevirtualinherited

Save a compositing layer whose ALREADY-PAINTED content is post-processed by a custom SkSL shader.

Unlike draw_with_sksl (which fills a fresh rect from a generative shader), this is a child-shader compositor: the subtree paints into the layer, then on restore() the shader runs over that content. The SkSL MUST declare uniform shader content; as its child — content.eval(xy) samples the subtree's rendered pixels — e.g. uniform shader content; half4 main(float2 xy) { return content.eval(xy).bgra; } // channel swap The same fixed uniform vocabulary as draw_with_sksl (resolution / value / time / accentColor / bgColor / trackColor / fillColor / thumbColor) is bound when declared, PLUS any arbitrary extra_uniforms (name → float/vec) the shader declares — guarded by findUniform, so unknown names are ignored.

blend_mode is how the finished post-effect layer composites back onto its parent (default source-over). BlendMode::lighter gives the additive audio-UI glow pattern.

sample_radius is the maximum distance (px) the shader may sample content.eval() away from the pixel being shaded — Skia needs this so a shader that reads neighboring texels (e.g. chromatic aberration's per-channel offset) gets valid samples near the layer edge instead of transparent black. Leave 0 for shaders that only read the current pixel.

Returns true when the shader was installed on the layer. Only the GPU backend (SkiaCanvas) implements it; the base falls back to a plain save_layer (still balanced — the subtree renders unfiltered) and returns false so callers can log the unsupported path once.

◆ scale()

void pulp::canvas::CoreGraphicsCanvas::scale ( float sx,
float sy )
overridevirtual

Implements pulp::canvas::Canvas.

◆ sdf_feather_min_reach()

float pulp::canvas::Canvas::sdf_feather_min_reach ( const SDFStyle & style)
inlinestaticinherited

The smallest draw-rect reach that holds style's feather: 3 sigma for the glow (1) and outer (3) modes, whose coverage lives outside the shape, and 0 for every other mode or a zero sigma.

Beyond 3 sigma the Gaussian falloff is under 0.15%, so a reach at or above this value never shows a hard edge where the draw rect ends.

◆ set_blend_mode()

void pulp::canvas::CoreGraphicsCanvas::set_blend_mode ( BlendMode mode)
overridevirtual

Set the compositing blend mode for subsequent draw operations.

Reimplemented from pulp::canvas::Canvas.

◆ set_direction()

virtual void pulp::canvas::Canvas::set_direction ( TextDirection direction)
inlinevirtualinherited

Reimplemented in pulp::canvas::RecordingCanvas.

◆ set_fill_color()

void pulp::canvas::CoreGraphicsCanvas::set_fill_color ( Color c)
overridevirtual

Implements pulp::canvas::Canvas.

◆ set_fill_gradient_conic()

void pulp::canvas::CoreGraphicsCanvas::set_fill_gradient_conic ( float cx,
float cy,
float start_angle,
const Color * colors,
const float * positions,
int count )
overridevirtual

Set a conic (sweep) gradient as the fill paint.

Reimplemented from pulp::canvas::Canvas.

◆ set_fill_gradient_conic_repeating()

void pulp::canvas::CoreGraphicsCanvas::set_fill_gradient_conic_repeating ( float cx,
float cy,
float start_angle,
float sweep_turns,
const Color * colors,
const float * positions,
int count )
overridevirtual

Set a REPEATING conic gradient (CSS repeating-conic-gradient).

The stop list spans sweep_turns of a full turn — positions are normalized to 0..1 across that band — and the band tiles around the circle.

Declared LAST, and as its own virtual rather than a wider set_fill_gradient_conic, on both of the usual grounds: a signature change would reorder this vtable under every prebuilt SDK consumer, and the Canvas2D command recorders would have to grow a field to carry the span. The default paints the band ONCE and clamps the rest, which is what a backend that cannot tile should show — recognisably the right colours in the wrong places, rather than nothing or a silent full-circle stretch.

Reimplemented from pulp::canvas::Canvas.

◆ set_fill_gradient_linear()

void pulp::canvas::CoreGraphicsCanvas::set_fill_gradient_linear ( float x0,
float y0,
float x1,
float y1,
const Color * colors,
const float * positions,
int count )
overridevirtual

Set a linear gradient as the fill paint.

Reimplemented from pulp::canvas::Canvas.

◆ set_fill_gradient_linear_repeating()

virtual void pulp::canvas::Canvas::set_fill_gradient_linear_repeating ( float x0,
float y0,
float x1,
float y1,
const Color * colors,
const float * positions,
int count )
inlinevirtualinherited

Set a REPEATING linear gradient (CSS repeating-linear-gradient).

The two points delimit ONE BAND rather than the whole box — positions are normalized to 0..1 across that band — and the band tiles along the gradient line in both directions.

Declared after the conic form above and for the same two reasons: a wider set_fill_gradient_linear would reorder this vtable under every prebuilt SDK consumer, and the Canvas2D command recorders would have to grow a field to carry the tile mode. The default paints the band ONCE and clamps the rest, which is what a backend that cannot tile should show — recognisably the right colours in the wrong places, rather than nothing or a silent stretch across the box.

◆ set_fill_gradient_radial()

void pulp::canvas::CoreGraphicsCanvas::set_fill_gradient_radial ( float cx,
float cy,
float radius,
const Color * colors,
const float * positions,
int count )
overridevirtual

Set a radial gradient as the fill paint.

Reimplemented from pulp::canvas::Canvas.

◆ set_fill_gradient_radial_elliptical()

virtual void pulp::canvas::Canvas::set_fill_gradient_radial_elliptical ( float cx,
float cy,
float rx,
float ry,
const Color * colors,
const float * positions,
int count )
inlinevirtualinherited

Radial gradient with an independent radius per axis — the ending shape CSS calls an ellipse, which is radial-gradient's DEFAULT shape and the only one a two-value size (90% 70%) can express.

Backends that can carry a local matrix on the shader override this; the default forwards to the circular overload using the horizontal radius, which is the pre-existing approximation rather than a blank fill.

◆ set_fill_gradient_radial_two_circles()

void pulp::canvas::CoreGraphicsCanvas::set_fill_gradient_radial_two_circles ( float x0,
float y0,
float r0,
float x1,
float y1,
float r1,
const Color * colors,
const float * positions,
int count )
overridevirtual

True two-circle radial gradient via CGContextDrawRadialGradient(inner_center, inner_r, outer_center, outer_r).

Unlike the single-circle override, this honours both endpoint circles.

Reimplemented from pulp::canvas::Canvas.

◆ set_fill_pattern()

void pulp::canvas::CoreGraphicsCanvas::set_fill_pattern ( const std::string & image_src,
PatternTileMode tile_x,
PatternTileMode tile_y )
overridevirtual

Set an image pattern as the fill paint.

image_src is a file path or data: URL — same identifier shape draw_image_from_file consumes, so backends share one decode path. Default is no-op so CPU-only / minimal canvases compile; SkiaCanvas overrides with a real SkShader::MakeImage. Empty image_src clears any active pattern (mirrors clear_fill_gradient's reset semantics).

Reimplemented from pulp::canvas::Canvas.

◆ set_filter()

virtual void pulp::canvas::Canvas::set_filter ( const std::string & filter)
inlinevirtualinherited

Canvas2D ctx.filter.

Sticky CSS <filter-function-list> string applied to subsequent fill / stroke / text / image draws. Spec supports: blur, brightness, contrast, drop-shadow, grayscale, hue-rotate, invert, opacity, saturate, sepia. The default is "none". SkiaCanvas parses the string into an SkImageFilter chain and applies via SkPaint::setImageFilter; CG and other backends can store the value but render unfiltered. RecordingCanvas captures the raw string. Distinct from CSS filter on a View: that filter applies to the View element, this one applies to the per-context Canvas2D paints inside it.

Reimplemented in pulp::canvas::RecordingCanvas.

◆ set_font()

void pulp::canvas::CoreGraphicsCanvas::set_font ( const std::string & family,
float size )
overridevirtual

Implements pulp::canvas::Canvas.

◆ set_font_features()

void pulp::canvas::CoreGraphicsCanvas::set_font_features ( std::vector< FontFeature > features)
overridevirtual

Reimplemented from pulp::canvas::Canvas.

◆ set_font_full()

void pulp::canvas::CoreGraphicsCanvas::set_font_full ( const std::string & family,
float size,
int weight,
int slant,
float letter_spacing )
overridevirtual

Richer font setter that propagates CSS font-weight (100..900), font-slant (0=upright, 1=italic), and letter-spacing (px between glyphs) through to the backend.

Default implementation forwards to the legacy set_font(family, size) so non-Label callers keep working unchanged. Backends that honor these properties (Skia, CoreText, RecordingCanvas) override this to capture or apply them. Label widget honors setFontFamily / setFontWeight / setLetterSpacing from JS.

Reimplemented from pulp::canvas::Canvas.

◆ set_font_smoothing()

void pulp::canvas::CoreGraphicsCanvas::set_font_smoothing ( FontSmoothing smoothing)
overridevirtual

antialiased turns CoreText font smoothing off, the switch Chromium flips on macOS.

It is graphics state, so save()/restore() scope it.

Reimplemented from pulp::canvas::Canvas.

◆ set_gradient_interpolation()

virtual void pulp::canvas::Canvas::set_gradient_interpolation ( GradientInterpolation m)
inlinevirtualinherited

Colour space every LATER gradient setter blends its stops in (CSS <color-interpolation-method>), until set again; save() does not keep it.

Appended for the vtable. Skia blends natively, CoreGraphics resamples.

Reimplemented in pulp::canvas::RecordingCanvas.

◆ set_image_smoothing()

void pulp::canvas::CoreGraphicsCanvas::set_image_smoothing ( bool enabled,
ImageSmoothingQuality quality )
overridevirtual

Canvas2D ctx.imageSmoothingEnabled + imageSmoothingQuality.

Sticky paint flag honored by subsequent drawImage calls. When enabled is false the backend uses nearest-neighbor sampling; when true the quality enum picks the resampler. Default no-op so backends without smoothing support compile; SkiaCanvas (SkSamplingOptions) and CoreGraphicsCanvas (CGContextSetInterpolationQuality) override. RecordingCanvas captures a set_image_smoothing command for tests.

Reimplemented from pulp::canvas::Canvas.

◆ set_line_cap()

void pulp::canvas::CoreGraphicsCanvas::set_line_cap ( LineCap cap)
overridevirtual

Implements pulp::canvas::Canvas.

◆ set_line_dash()

void pulp::canvas::CoreGraphicsCanvas::set_line_dash ( const float * intervals,
int count,
float phase )
overridevirtual

Set the line dash pattern for stroke operations.

Mirrors CanvasRenderingContext2D.setLineDash(). intervals alternate "on" / "off" lengths in pixels. An empty pattern (count == 0) clears the dash and reverts to solid strokes. HTML5 spec: an odd-length pattern is duplicated to even length; callers that want spec compliance must duplicate before invoking this method (the JS bridge does so). Default no-op for backends without dash support.

Reimplemented from pulp::canvas::Canvas.

◆ set_line_join()

void pulp::canvas::CoreGraphicsCanvas::set_line_join ( LineJoin join)
overridevirtual

Implements pulp::canvas::Canvas.

◆ set_line_width()

void pulp::canvas::CoreGraphicsCanvas::set_line_width ( float w)
overridevirtual

Implements pulp::canvas::Canvas.

◆ set_miter_limit()

void pulp::canvas::CoreGraphicsCanvas::set_miter_limit ( float limit)
overridevirtual

Canvas2D ctx.miterLimit.

Sticky stroke state — controls when a miter join collapses to a bevel (Skia / CG default = 10). Spec requires non-finite or non-positive values to be silently ignored; callers that want spec semantics should clamp at the JS bridge. Default no-op so backends without miter-limit support compile; SkiaCanvas (SkPaint::setStrokeMiter) and CoreGraphicsCanvas (CGContextSetMiterLimit) override. RecordingCanvas captures a set_miter_limit command for tests.

Reimplemented from pulp::canvas::Canvas.

◆ set_opacity()

void pulp::canvas::CoreGraphicsCanvas::set_opacity ( float alpha)
overridevirtual

Set global alpha for subsequent drawing operations (0.0-1.0).

Reimplemented from pulp::canvas::Canvas.

◆ set_shadow_blur()

void pulp::canvas::CoreGraphicsCanvas::set_shadow_blur ( float blur)
overridevirtual

Reimplemented from pulp::canvas::Canvas.

◆ set_shadow_color()

void pulp::canvas::CoreGraphicsCanvas::set_shadow_color ( Color color)
overridevirtual

Sticky drop-shadow state that wraps subsequent draw operations, matching the CanvasRenderingContext2D shadowColor / shadowBlur / shadowOffsetX / shadowOffsetY properties.

The shadow renders only when color.a > 0 AND (blur > 0 OR offset_x != 0 OR offset_y != 0) — same gate Chromium / WebKit use. Setting color to fully transparent (alpha == 0) effectively disables the shadow even if blur/offset are non-zero, mirroring spec semantics.

Backends that draw via SkPaint / CGContext layer the shadow onto the next draw call by configuring an image filter (Skia DropShadow) or CGContextSetShadowWithColor respectively. The default implementation here is a no-op so backends that haven't opted into shadow support compile unchanged; SkiaCanvas, CoreGraphicsCanvas, and RecordingCanvas override to actually honor (or capture) the state.

Reimplemented from pulp::canvas::Canvas.

◆ set_shadow_offset_x()

void pulp::canvas::CoreGraphicsCanvas::set_shadow_offset_x ( float dx)
overridevirtual

Reimplemented from pulp::canvas::Canvas.

◆ set_shadow_offset_y()

void pulp::canvas::CoreGraphicsCanvas::set_shadow_offset_y ( float dy)
overridevirtual

Reimplemented from pulp::canvas::Canvas.

◆ set_stroke_color()

void pulp::canvas::CoreGraphicsCanvas::set_stroke_color ( Color c)
overridevirtual

Implements pulp::canvas::Canvas.

◆ set_stroke_gradient_conic()

void pulp::canvas::CoreGraphicsCanvas::set_stroke_gradient_conic ( float cx,
float cy,
float start_angle,
const Color * colors,
const float * positions,
int count )
overridevirtual

Reimplemented from pulp::canvas::Canvas.

◆ set_stroke_gradient_linear()

void pulp::canvas::CoreGraphicsCanvas::set_stroke_gradient_linear ( float x0,
float y0,
float x1,
float y1,
const Color * colors,
const float * positions,
int count )
overridevirtual

Reimplemented from pulp::canvas::Canvas.

◆ set_stroke_gradient_radial()

void pulp::canvas::CoreGraphicsCanvas::set_stroke_gradient_radial ( float cx,
float cy,
float radius,
const Color * colors,
const float * positions,
int count )
overridevirtual

Reimplemented from pulp::canvas::Canvas.

◆ set_stroke_gradient_radial_two_circles()

void pulp::canvas::CoreGraphicsCanvas::set_stroke_gradient_radial_two_circles ( float x0,
float y0,
float r0,
float x1,
float y1,
float r1,
const Color * colors,
const float * positions,
int count )
overridevirtual

Two-circle radial gradient on stroke (mirrors fill counterpart).

Reimplemented from pulp::canvas::Canvas.

◆ set_stroke_pattern()

void pulp::canvas::CoreGraphicsCanvas::set_stroke_pattern ( const std::string & image_src,
PatternTileMode tile_x,
PatternTileMode tile_y )
overridevirtual

Stroke counterpart.

Rare in production code; default no-op. SkiaCanvas overrides via the same SkShader::MakeImage path applied to the stroke paint.

Reimplemented from pulp::canvas::Canvas.

◆ set_text_align()

void pulp::canvas::CoreGraphicsCanvas::set_text_align ( TextAlign align)
overridevirtual

Implements pulp::canvas::Canvas.

◆ set_transform()

void pulp::canvas::CoreGraphicsCanvas::set_transform ( float a,
float b,
float c,
float d,
float e,
float f )
overridevirtual

Replace the current transform with the affine matrix [ a c e ] [ b d f ] [ 0 0 1 ] Mirrors CanvasRenderingContext2D.setTransform(a, b, c, d, e, f).

Default implementation is a no-op so non-GPU backends still compile; SkiaCanvas overrides to compose the supplied matrix onto the paint baseline captured via capture_paint_baseline_transform().

Reimplemented from pulp::canvas::Canvas.

◆ sksl_declares_uniform()

bool pulp::canvas::Canvas::sksl_declares_uniform ( const std::string & sksl,
const std::string & uniform_name )
staticinherited

Whether an SkSL shader declares a uniform of the given name.

Compiles via the same process-lifetime cache as compile_sksl, so repeat queries are free; false if the source does not compile.

Used to decide whether a shader is time-driven and needs continuous repaint. Not a substring search: a timeline uniform must not pin the render loop, and a differently-spelled one must not silently freeze.

◆ stroke_arc()

void pulp::canvas::CoreGraphicsCanvas::stroke_arc ( float cx,
float cy,
float radius,
float start_angle,
float end_angle )
overridevirtual

Implements pulp::canvas::Canvas.

◆ stroke_circle()

void pulp::canvas::CoreGraphicsCanvas::stroke_circle ( float cx,
float cy,
float radius )
overridevirtual

Implements pulp::canvas::Canvas.

◆ stroke_current_path()

void pulp::canvas::CoreGraphicsCanvas::stroke_current_path ( )
overridevirtual

Stroke the current path.

Reimplemented from pulp::canvas::Canvas.

◆ stroke_line()

void pulp::canvas::CoreGraphicsCanvas::stroke_line ( float x0,
float y0,
float x1,
float y1 )
overridevirtual

Implements pulp::canvas::Canvas.

◆ stroke_path() [1/2]

void pulp::canvas::CoreGraphicsCanvas::stroke_path ( const Path & path,
const StrokeStyle & style )
overridevirtual

Stroke path with style.

Unlike the set_line_width / set_line_cap / set_line_join triplet, the whole stroke configuration arrives as one value and cannot be half-applied.

NOTE the default fallback mutates the canvas's stroke state (width / cap / join / dash) and does not restore it, because the immediate-mode API has no way to read the previous values back. Backends that override this — Skia and CoreGraphics both do — keep the style local to the one draw. Callers that care should save()/restore() around it.

Reimplemented from pulp::canvas::Canvas.

◆ stroke_path() [2/2]

void pulp::canvas::CoreGraphicsCanvas::stroke_path ( const Point2D * points,
size_t count )
overridevirtual

Stroke a continuous polyline — much smoother than individual stroke_line calls.

Reimplemented from pulp::canvas::Canvas.

◆ stroke_rect()

void pulp::canvas::CoreGraphicsCanvas::stroke_rect ( float x,
float y,
float w,
float h )
overridevirtual

Implements pulp::canvas::Canvas.

◆ stroke_rounded_rect()

void pulp::canvas::CoreGraphicsCanvas::stroke_rounded_rect ( float x,
float y,
float w,
float h,
float radius )
overridevirtual

Implements pulp::canvas::Canvas.

◆ stroke_text()

void pulp::canvas::CoreGraphicsCanvas::stroke_text ( const std::string & text,
float x,
float y,
float max_width = 0.0f )
overridevirtual

Canvas2D strokeText(text, x, y, maxWidth) spec form.

True outlined-glyph rendering: builds a stroked paint (SkPaint::kStroke_Style on Skia, CG text drawing mode kCTLineDrawingModeStroke on CoreGraphics) so the glyph outlines honor the current lineWidth / strokeStyle. The default implementation degrades to fill_text with the stroke color pre-set by the caller — visually approximate but spec-incompatible. Backends override to render real glyph outlines. max_width semantics match fill_text_with_max_width.

Reimplemented from pulp::canvas::Canvas.

◆ supports()

bool pulp::canvas::CoreGraphicsCanvas::supports ( CanvasCapability ) const
inlineoverridevirtual

Report whether this backend renders cap's verb faithfully.

Base returns false for everything: the base-class verb defaults are all degradations (silent no-op, color/mask/blur op dropped, filename placeholder). Concrete backends override to advertise the subset they actually implement. Callers branch on this to log-once and keep the documented fallback rather than silently emitting an unfaithful frame.

Reimplemented from pulp::canvas::Canvas.

◆ supports_image_draw()

bool pulp::canvas::Canvas::supports_image_draw ( ) const
inlineinherited

Whether this backend can actually decode + draw a raster image (the draw_image_from_* verbs below).

Thin alias for supports(CanvasCapability::images) — kept because it is public API used by headless tooling (ImageView placeholder path, CG image tests). Not virtual: backends express image support through supports(), and this forwards to it so there is one vocabulary and one source of truth.

◆ text_x_for_byte()

float pulp::canvas::CoreGraphicsCanvas::text_x_for_byte ( const std::string & text,
std::size_t byte_index )
overridevirtual

Caret x (in text-local space, measured from the text's leading edge) for the glyph boundary at byte_index within text.

This is the drift-proof companion to measure_text: it shapes the FULL run with the current font state (family / size / weight / slant / letter-spacing / direction / features) and returns the caret offset for the cluster boundary at byte_index, so a kerned pair like "AV" reports the same advance the painter actually draws — not the sum of isolated glyph widths.

The default implementation measures the prefix substring, which is correct for unkerned, unspaced text but drifts on kerned runs. The Skia backend overrides this to query the shaped paragraph directly.

Reimplemented from pulp::canvas::Canvas.

◆ translate()

void pulp::canvas::CoreGraphicsCanvas::translate ( float x,
float y )
overridevirtual

Implements pulp::canvas::Canvas.

◆ write_pixels()

virtual bool pulp::canvas::Canvas::write_pixels ( const uint8_t * data,
int width,
int height,
int dx,
int dy )
inlinevirtualinherited

Write RGBA pixels (data size = width*height*4) to the current surface at destination (dx, dy).

Returns true on success. Mirrors CanvasRenderingContext2D.putImageData().

Reimplemented in pulp::canvas::RecordingCanvas.


The documentation for this class was generated from the following file:
  • /home/runner/work/pulp/pulp/core/canvas/include/pulp/canvas/cg_canvas.hpp