std1.0.0-beta-7 SafeC docs

Source gui/gui_canvas.h

View in the repository

#pragma once
// SafeC Standard Library — GUI: the anti-aliased 2D vector canvas.
//
// A software rasterizer with the drawing model of the HTML canvas / Skia /
// Cairo family, drawing into a GuiWindow's pixel buffer or a GuiImage:
//
//   paths      move/line/quadratic/cubic/arc/close (gui_path.h), filled by
//              the nonzero or even-odd rule, stroked with any width, miter/
//              round/bevel joins, butt/round/square caps and dash patterns
//   paint      solid colors, linear / radial (two-circle) / sweep
//              gradients with up to 16 stops and pad/repeat/reflect spread,
//              and image patterns (any transform, clamp/repeat/none,
//              nearest or bilinear sampling)
//   state      an affine transform, global alpha, blend mode, rectangular
//              and path clipping, all saved/restored with save()/restore()
//   compositing premultiplied alpha; 12 Porter-Duff operators and 12
//              separable blend modes (multiply, screen, overlay, ...)
//   images     drawn scaled/rotated/cropped with filtering
//   text       TrueType glyphs from gui_text.h, anti-aliased and cached
//
// Anti-aliasing is exact in x and 16x supersampled in y: each pixel row
// is cut by 16 sample lines and every edge crossing contributes its exact
// horizontal coverage, accumulated in a difference buffer and integrated
// once per row — so a pixel's coverage is the true area inside the shape
// to within 1/16 of a pixel vertically, for either fill rule, with
// overlapping and self-intersecting paths. Compositing runs four channels
// at a time on SIMD vectors (vec<float, 4>, std/simd's f32x4); solid opaque
// spans are filled with plain 32-bit stores. Everything is deterministic:
// the same calls produce the same bytes on every platform.
//
//   struct GuiCanvas c = GuiCanvas::for_window(&win);
//   c.clear(GuiColor::hex(0xFFFFFFU));
//   c.set_fill_color(GuiColor::hex(0x3366CCU));
//   c.fill_rounded_rect(20.0f, 20.0f, 120.0f, 40.0f, 8.0f);
//   c.set_stroke_color(GuiColor::hex(0x000000U));
//   c.set_line_width(2.0f);
//   c.begin_path(); c.move_to(20.0f, 80.0f); c.line_to(140.0f, 120.0f); c.stroke();
//   c.set_font(GuiTypeface::fallback(), 16.0f);
//   c.fill_text("Hello", 20.0f, 150.0f);
//   c.free();
//   win.present();
//
// A canvas holds no copy of the pixels, only a pointer: after the window
// is resized (its buffer reallocated), point the canvas at it again with
// retarget_window().

#include <std/gui/gui.h>
#include <std/gui/gui_path.h>
#include <std/gui/gui_png.h>
#include <std/gui/gui_text.h>
#include <std/collections/vec.h>

// GuiPaint.kind
#define GUI_PAINT_SOLID  0
#define GUI_PAINT_LINEAR 1
#define GUI_PAINT_RADIAL 2
#define GUI_PAINT_SWEEP  3
#define GUI_PAINT_IMAGE  4

// GuiPaint.spread: what gradients do past their ends, and image patterns
// outside the image.
#define GUI_SPREAD_PAD     0   // extend the end colors / clamp to the edge pixels
#define GUI_SPREAD_REPEAT  1
#define GUI_SPREAD_REFLECT 2
#define GUI_SPREAD_NONE    3   // transparent (image patterns)

#define GUI_GRADIENT_MAX_STOPS 16
#define GUI_DASH_MAX 16

// Fill rules
#define GUI_FILL_NONZERO 0
#define GUI_FILL_EVENODD 1

// Line caps / joins
#define GUI_CAP_BUTT    0
#define GUI_CAP_ROUND   1
#define GUI_CAP_SQUARE  2
#define GUI_JOIN_MITER  0
#define GUI_JOIN_ROUND  1
#define GUI_JOIN_BEVEL  2

// Image filtering
#define GUI_FILTER_NEAREST  0
#define GUI_FILTER_BILINEAR 1

// Text baseline (GuiCanvas::set_text_baseline): what fill_text's y means.
#define GUI_BASELINE_ALPHABETIC 0
#define GUI_BASELINE_TOP        1
#define GUI_BASELINE_MIDDLE     2
#define GUI_BASELINE_BOTTOM     3

// Blend modes (GuiCanvas::set_blend_mode). Porter-Duff operators:
#define GUI_BLEND_SRC_OVER   0   // the default: source over destination
#define GUI_BLEND_SRC        1   // replace
#define GUI_BLEND_DST        2
#define GUI_BLEND_DST_OVER   3
#define GUI_BLEND_SRC_IN     4
#define GUI_BLEND_DST_IN     5
#define GUI_BLEND_SRC_OUT    6
#define GUI_BLEND_DST_OUT    7
#define GUI_BLEND_SRC_ATOP   8
#define GUI_BLEND_DST_ATOP   9
#define GUI_BLEND_XOR        10
#define GUI_BLEND_CLEAR      11
#define GUI_BLEND_PLUS       12  // additive, clamped
// Separable blend modes (W3C Compositing and Blending), composited source-over:
#define GUI_BLEND_MULTIPLY    13
#define GUI_BLEND_SCREEN      14
#define GUI_BLEND_OVERLAY     15
#define GUI_BLEND_DARKEN      16
#define GUI_BLEND_LIGHTEN     17
#define GUI_BLEND_COLOR_DODGE 18
#define GUI_BLEND_COLOR_BURN  19
#define GUI_BLEND_HARD_LIGHT  20
#define GUI_BLEND_SOFT_LIGHT  21
#define GUI_BLEND_DIFFERENCE  22
#define GUI_BLEND_EXCLUSION   23
#define GUI_BLEND_COUNT       24

namespace std {

struct GuiColorStop {
    float offset;            // 0..1
    struct GuiColor color;
};

// What to fill or stroke with. A value type: build one, add stops, hand
// it to set_fill_paint()/set_stroke_paint() (which copy it).
struct GuiPaint {
    int kind;                    // GUI_PAINT_*
    struct GuiColor color;       // SOLID
    // LINEAR: from (x0,y0) to (x1,y1).
    // RADIAL: from the circle (x0,y0,r0) to the circle (x1,y1,r1).
    // SWEEP:  around (x0,y0), starting at angle r0 (radians, clockwise).
    float x0; float y0; float r0;
    float x1; float y1; float r1;
    int nstops;
    struct GuiColorStop stops[GUI_GRADIENT_MAX_STOPS];
    int spread;                  // GUI_SPREAD_*
    const ?&GuiImage image;      // IMAGE: the pattern (must outlive the drawing)
    struct GuiMatrix matrix;     // paint space -> user space (pattern/gradient transform)
    int filter;                  // GUI_FILTER_* (IMAGE)
    float opacity;               // multiplies the paint's alpha (default 1)

    static struct GuiPaint solid(struct GuiColor c);
    static struct GuiPaint linear(float x0, float y0, float x1, float y1);
    static struct GuiPaint radial(float cx, float cy, float r);
    // Two-circle radial gradient, like the HTML canvas' createRadialGradient.
    static struct GuiPaint radial2(float x0, float y0, float r0, float x1, float y1, float r1);
    static struct GuiPaint sweep(float cx, float cy, float startAngle);
    // An image pattern, its pixel (0,0) at user-space (0,0), one image pixel
    // per user unit (change with set_transform()). Spread: GUI_SPREAD_REPEAT.
    static struct GuiPaint pattern(const &GuiImage img);

    // Adds a color stop (stops may be added in any order; beyond
    // GUI_GRADIENT_MAX_STOPS they're ignored).
    void add_stop(float offset, struct GuiColor c);
    void set_transform(struct GuiMatrix m);
    void set_spread(int spread);
    void set_filter(int filter);
    void set_opacity(float opacity);
};

// Everything save()/restore() saves.
struct GuiCanvasState {
    struct GuiMatrix m;
    struct GuiPaint fill;
    struct GuiPaint stroke;
    float lineWidth;
    float miterLimit;
    int lineCap;
    int lineJoin;
    float dash[GUI_DASH_MAX];
    int ndash;
    float dashOffset;
    float alpha;
    int blend;
    int fillRule;
    int filter;            // default image filter for draw_image*
    int antiAlias;
    // device-space clip: a rectangle, and optionally a coverage mask
    int clipX0; int clipY0; int clipX1; int clipY1;
    ?&heap unsigned char mask;     // width*height bytes, or null
    int ownsMask;
    const ?&GuiTypeface font;      // null: GuiTypeface::fallback()
    float fontSize;
    int textAlign;                 // GUI_TEXT_LEFT/CENTER/RIGHT
    int textBaseline;              // GUI_BASELINE_*
};

// A line segment of a flattened path, in device space (internal).
struct GuiEdge {
    float x0; float y0;            // y0 < y1
    float x1; float y1;
    float dxdy;
    int dir;                       // +1 downward in the original path, -1 upward
};

struct GuiCanvas {
    ?&unsigned char px;            // target pixels: premultiplied RGBA8888
    int width;
    int height;
    int stride;                    // bytes per row
    struct GuiCanvasState st;
    struct Vec<struct GuiCanvasState> saved;   // the save() stack
    struct GuiPath path;           // the current path (begin_path/move_to/...)
    // scratch buffers, kept between calls to avoid reallocating
    struct Vec<struct GuiEdge> edges;
    struct Vec<float> acc;
    struct Vec<float> xs;
    struct Vec<int> sorted;
    struct Vec<int> active;
    struct Vec<float> fpts;        // flattened polylines: x,y pairs ...
    struct Vec<int> fcont;         // ... and (first point, point count, closed) triples
    struct Vec<float> dpts;        // dashed polylines
    struct Vec<int> dcont;
    struct Vec<float> spts;        // stroke outline polygons
    struct Vec<int> scont;
    struct Vec<float> row;         // one row of coverage
    struct Vec<float> lut;         // gradient colors (256 premultiplied RGBA)
    struct GuiPath scratchPath;

    // ── targets ──────────────────────────────────────────────────────────
    static struct GuiCanvas for_window(&GuiWindow win);
    // Draws into an image (premultiplying its pixels first if they're
    // straight-alpha, see GuiImage.premultiplied).
    static struct GuiCanvas for_image(&GuiImage img);
    // Any premultiplied RGBA8888 buffer.
    static struct GuiCanvas for_pixels(&unsigned char px, int width, int height, int stride);
    // Point at the window's (possibly reallocated) buffer again. Resets
    // the clip if the size changed.
    void retarget_window(&GuiWindow win);
    // Back to the initial state (identity transform, black fill, no clip,
    // empty save stack and path).
    void reset();
    void drop();
    void free() consume;   // = drop()

    // ── state ────────────────────────────────────────────────────────────
    void save();
    void restore();
    void translate(float tx, float ty);
    void scale(float sx, float sy);
    void rotate(float radians);
    // Pre-multiplies the current transform by m (m applies first).
    void transform(struct GuiMatrix m);
    void set_transform(struct GuiMatrix m);
    void reset_transform();
    struct GuiMatrix get_transform() const;

    void set_fill_color(struct GuiColor c);
    void set_fill_paint(const &GuiPaint p);
    void set_stroke_color(struct GuiColor c);
    void set_stroke_paint(const &GuiPaint p);
    void set_line_width(float w);
    void set_line_cap(int lineCap);
    void set_line_join(int lineJoin);
    void set_miter_limit(float limit);
    // Alternating on/off lengths (n <= GUI_DASH_MAX; an odd count repeats
    // once more, as in SVG); n = 0 turns dashing off.
    void set_dash([]const float lengths, float offset);
    // internal: the (reference, length) core of set_dash()
    void set_dash_n_(const &float lengths, int n, float offset);
    void set_global_alpha(float a);
    void set_blend_mode(int mode);
    void set_fill_rule(int rule);
    void set_image_filter(int filter);
    // 0 = aliased (hard pixel edges: coverage rounded to 0 or 1).
    void set_anti_alias(int on);

    // ── clipping (intersects with the current clip; restore() undoes) ────
    void clip_rect(float x, float y, float w, float h);
    void clip_path(const &GuiPath p);
    void clip();                   // with the current path
    void reset_clip();
    // The clip's device-space bounds.
    struct GuiRect clip_bounds() const;

    // ── the current path ─────────────────────────────────────────────────
    void begin_path();
    void move_to(float x, float y);
    void line_to(float x, float y);
    void quad_to(float cx, float cy, float x, float y);
    void cubic_to(float c1x, float c1y, float c2x, float c2y, float x, float y);
    void arc(float cx, float cy, float r, float a0, float a1, int ccw);
    void arc_to(float x1, float y1, float x2, float y2, float r);
    void rect(float x, float y, float w, float h);
    void rounded_rect(float x, float y, float w, float h, float r);
    void ellipse(float cx, float cy, float rx, float ry);
    void close_path();
    void fill();
    void stroke();

    // ── paths and shapes ─────────────────────────────────────────────────
    void fill_path(const &GuiPath p);
    void stroke_path(const &GuiPath p);
    void fill_rect(float x, float y, float w, float h);
    void stroke_rect(float x, float y, float w, float h);
    // Makes the rectangle transparent (within the clip).
    void clear_rect(float x, float y, float w, float h);
    // Fills the whole target with 'c', ignoring transform, clip and blend.
    void clear(struct GuiColor c);
    void fill_rounded_rect(float x, float y, float w, float h, float r);
    void stroke_rounded_rect(float x, float y, float w, float h, float r);
    void fill_circle(float cx, float cy, float r);
    void stroke_circle(float cx, float cy, float r);
    void fill_ellipse(float cx, float cy, float rx, float ry);
    void draw_line(float x0, float y0, float x1, float y1);
    // The filled outline of stroking 'p' with the current line settings,
    // in user space — e.g. to hit-test or clip to a stroke.
    void stroke_to_path(const &GuiPath p, &GuiPath out);

    // ── images ───────────────────────────────────────────────────────────
    void draw_image(const &GuiImage img, float x, float y);
    void draw_image_scaled(const &GuiImage img, float x, float y, float w, float h);
    // The (sx,sy,sw,sh) part of the image into (dx,dy,dw,dh).
    void draw_image_rect(const &GuiImage img, float sx, float sy, float sw, float sh,
                         float dx, float dy, float dw, float dh);

    // ── text ─────────────────────────────────────────────────────────────
    void set_font(const &GuiTypeface face, float sizePx);
    void set_font_size(float sizePx);
    void set_text_align(int align);
    void set_text_baseline(int baseline);
    // Draws one line of UTF-8 text with the fill paint at (x, y) (see
    // set_text_baseline/set_text_align). Returns its advance width.
    float fill_text(const &char text, float x, float y);
    float fill_text_n([]const char text, float x, float y);
    // internal: the (reference, length) core of fill_text_n()
    float fill_text_n_n_(const &char text, unsigned long len, float x, float y);
    float measure_text(const &char text) const;
    // Draws a built layout with its top-left at (x, y).
    void fill_text_layout(const &GuiTextLayout layout, float x, float y);

    // ── reading back ─────────────────────────────────────────────────────
    // The straight-alpha color at device pixel (x, y) (transparent if out
    // of bounds).
    struct GuiColor get_pixel(int x, int y) const;
};

} // namespace std