libcg is a tiny C library for 2D computer graphics, inspired by the Cairo / HTML Canvas drawing model. It renders anti-aliased graphics into an RGBA32 pixel buffer via span-based rasterization.
- Architecture Overview
- Data Types
- Matrix Functions
- Surface Functions
- Paint Functions
- Path Functions
- Context Functions
- Complete Examples
libcg follows a stateful drawing model. The central object is cg_ctx_t (the drawing context), which holds:
- A reference to the target
cg_surface_tpixel buffer - The current
cg_path_tbeing constructed - A stack of
cg_state_tobjects (push/pop viacg_save/cg_restore)
Each state stores: current paint (solid, gradient, or texture), transformation matrix, stroke style (width, cap, join, miter limit, dash pattern), fill rule, compositing operator, global opacity, and the clip region.
Memory management: Surface, Paint, and Path objects use reference counting. Functions that accept these objects will increment the reference count as needed (e.g., cg_set_source / cg_paint_create_texture call cg_paint_reference and cg_surface_reference internally). You must pair every _create with the corresponding _destroy.
Default state values (when a context is created):
| Property | Default Value |
|---|---|
| Paint | NULL (interprets as state->color) |
| Color | {0, 0, 0, 1} (black) |
| Matrix | Identity |
| Line width | 1.0 |
| Line cap | CG_LINE_CAP_BUTT |
| Line join | CG_LINE_JOIN_MITER |
| Miter limit | 10.0 |
| Fill rule | CG_FILL_RULE_NON_ZERO |
| Operator | CG_OPERATOR_SRC_OVER |
| Opacity | 1.0 |
Color component values throughout the API are floats in [0.0, 1.0]. The library clamps values to this range internally.
Coordinate system: Origin at top-left, X increases to the right, Y increases downward. Angles are in radians, positive direction is clockwise.
struct cg_point_t {
float x;
float y;
};A 2D point.
struct cg_rect_t {
float x;
float y;
float w;
float h;
};An axis-aligned rectangle anchored at the top-left corner (x, y) with width w and height h.
struct cg_matrix_t {
float a; float b;
float c; float d;
float tx; float ty;
};A 2D affine transformation matrix mapping a point (x, y) to:
x' = a*x + c*y + tx
y' = b*x + d*y + ty
In homogeneous form:
| a c tx |
| b d ty |
| 0 0 1 |
struct cg_color_t {
float r;
float g;
float b;
float a;
};RGBA color. All components clamped to [0.0, 1.0] when constructing paints.
struct cg_gradient_stop_t {
float offset;
struct cg_color_t color;
};Defines a position-color pair in a gradient. offset must be in [0.0, 1.0]. Offsets are clamped and sorted internally — if a stop has an offset smaller than the previous stop, it is bumped up to that value.
Used internally to describe path elements:
| Value | Points | Description |
|---|---|---|
CG_PATH_COMMAND_MOVE_TO |
1 | Moves to a new point, starts a sub-path |
CG_PATH_COMMAND_LINE_TO |
1 | Line segment to a point |
CG_PATH_COMMAND_CUBIC_TO |
3 | Cubic Bezier curve (control point 1, control point 2, end point) |
CG_PATH_COMMAND_CLOSE |
1 | Closes the current sub-path |
Note: Quadratic Bezier curves are internally converted to cubic when added to a path.
Controls how gradient colors are sampled outside the [0, 1] range:
| Value | Description |
|---|---|
CG_SPREAD_METHOD_PAD |
Clamp to the nearest edge color |
CG_SPREAD_METHOD_REFLECT |
Mirror the gradient at each integer boundary |
CG_SPREAD_METHOD_REPEAT |
Repeat the gradient (wrap around) |
| Value | Description |
|---|---|
CG_GRADIENT_TYPE_LINEAR |
Linear gradient |
CG_GRADIENT_TYPE_RADIAL |
Radial gradient |
| Value | Description |
|---|---|
CG_TEXTURE_TYPE_PLAIN |
Single copy of the texture drawn once (regions outside are transparent) |
CG_TEXTURE_TYPE_TILED |
Texture repeats to fill the entire painted area |
Note: Transformed non-tiled textures, and tiled textures with rotation/shear, use bilinear filtering. Other combinations use nearest-neighbor sampling.
| Value | Description |
|---|---|
CG_LINE_CAP_BUTT |
Flat end, exactly at the endpoint (default) |
CG_LINE_CAP_ROUND |
Semicircular end extending beyond the endpoint |
CG_LINE_CAP_SQUARE |
Square end extending half the line width beyond the endpoint |
| Value | Description |
|---|---|
CG_LINE_JOIN_MITER |
Sharp corner, clipped by miter limit (default) |
CG_LINE_JOIN_ROUND |
Rounded corner |
CG_LINE_JOIN_BEVEL |
Flat (beveled) corner |
Determines which regions are "inside" a self-intersecting path:
| Value | Description |
|---|---|
CG_FILL_RULE_NON_ZERO |
Non-zero winding rule (default) |
CG_FILL_RULE_EVEN_ODD |
Even-odd rule |
Porter-Duff compositing operators. These control how new pixels are blended with existing content on the surface:
| Value | Formula (short) | Effect |
|---|---|---|
CG_OPERATOR_CLEAR |
0 | Clear destination |
CG_OPERATOR_SRC |
S | Copy source |
CG_OPERATOR_DST |
D | Leave destination unchanged* |
CG_OPERATOR_SRC_OVER |
S + D*(1 - Aₛ) | Source on top (default) |
CG_OPERATOR_DST_OVER |
D + S*(1 - Aᴅ) | Destination on top |
CG_OPERATOR_SRC_IN |
S * Aᴅ | Source where destination exists |
CG_OPERATOR_DST_IN |
D * Aₛ | Destination where source exists |
CG_OPERATOR_SRC_OUT |
S * (1 - Aᴅ) | Source where destination is absent |
CG_OPERATOR_DST_OUT |
D * (1 - Aₛ) | Destination where source is absent |
CG_OPERATOR_SRC_ATOP |
SAᴅ + D(1 - Aₛ) | Source on top of destination, trimmed |
CG_OPERATOR_DST_ATOP |
DAₛ + S(1 - Aᴅ) | Destination on top of source, trimmed |
CG_OPERATOR_XOR |
S*(1 - Aᴅ) + D*(1 - Aₛ) | Non-overlapping regions combined |
Where S=source, D=destination, Aₛ=source alpha, Aᴅ=destination alpha
When the paint is a solid color with alpha=255 and operator is SRC_OVER, the library optimizes by using the SRC operator internally.
| Value | Description |
|---|---|
CG_PAINT_TYPE_COLOR |
Solid color |
CG_PAINT_TYPE_GRADIENT |
Gradient |
CG_PAINT_TYPE_TEXTURE |
Texture/image |
struct cg_surface_t {
int refcnt;
int width;
int height;
int stride;
int owndata;
unsigned char * pixels;
};The pixel canvas. Pixels are stored in premultiplied RGBA32 format (row-major). stride equals width * 4. owndata is 1 when the surface allocated its own buffer (via cg_surface_create) and 0 when wrapping external data (via cg_surface_create_for_data).
struct cg_path_t {
int refcnt;
int num_points;
int num_contours;
int num_curves;
int sub_path;
struct cg_point_t start_point;
struct { union cg_path_element_t * data; int size; int capacity; } elements;
};A vector path composed of sub-paths (contours). Each sub-path begins with a MOVE_TO and optionally ends with CLOSE. Quadratic Beziers are stored as cubic. Quadratic-to-cubic conversion uses:
CP1 = 2/3 * Q1 + 1/3 * P0
CP2 = 2/3 * Q1 + 1/3 * P2
Paint is the "what to draw" — it can be a solid color, gradient, or texture. The cg_paint_t struct serves as a base with a type discriminator. Access the concrete type through the appropriate subtype pointer.
Gradient paint stores color stops inline (allocated in the same block as the struct). values[6] stores geometry:
- Linear:
{x1, y1, x2, y2} - Radial:
{cx0, cy0, r0, cx1, cy1, r1}(start circle, then end circle)
Texture paint references a surface and stores opacity, texture type, and an affine matrix.
Matrices transform coordinates from user space to surface space. All matrix functions operate in-place on the matrix pointed to by m.
void cg_matrix_init(struct cg_matrix_t * m, float a, float b, float c, float d, float tx, float ty);Initialize a matrix with explicit values.
void cg_matrix_init_identity(struct cg_matrix_t * m);Set matrix to identity: {1, 0, 0, 1, 0, 0}.
void cg_matrix_init_translate(struct cg_matrix_t * m, float tx, float ty);Initialize as a translation-only matrix: {1, 0, 0, 1, tx, ty}.
void cg_matrix_init_scale(struct cg_matrix_t * m, float sx, float sy);Initialize as a scale-only matrix: {sx, 0, 0, sy, 0, 0}.
void cg_matrix_init_rotate(struct cg_matrix_t * m, float r);Initialize as a rotation matrix by r radians: {cos(r), sin(r), -sin(r), cos(r), 0, 0}.
void cg_matrix_init_shear(struct cg_matrix_t * m, float shx, float shy);Initialize as a shear matrix: {1, shy, shx, 1, 0, 0}.
void cg_matrix_translate(struct cg_matrix_t * m, float tx, float ty);Post-multiply a translation onto the existing matrix:
tx' = tx + a*dx + c*dy
ty' = ty + b*dx + d*dy
void cg_matrix_scale(struct cg_matrix_t * m, float sx, float sy);Post-multiply a scale: a*=sx; b*=sx; c*=sy; d*=sy.
void cg_matrix_rotate(struct cg_matrix_t * m, float r);Post-multiply a rotation by r radians.
void cg_matrix_shear(struct cg_matrix_t * m, float shx, float shy);Post-multiply a shear: m = m * shear(shx, shy).
void cg_matrix_multiply(struct cg_matrix_t * m, struct cg_matrix_t * m1, struct cg_matrix_t * m2);Set m = m1 * m2. An optimization path is used when both matrices are axis-aligned (i.e., b == 0 and c == 0 for both).
int cg_matrix_invert(struct cg_matrix_t * m);Invert the matrix in place. Returns non-zero on success, 0 if the matrix is singular.
Special cases:
- If
b == 0andc == 0(axis-aligned): returns0ifa == 0ord == 0. - Otherwise: returns
0if|det| <= 1e-20.
struct cg_surface_t * cg_surface_create(int width, int height);Allocates a new surface of the given size. The pixel buffer is zero-initialized (fully transparent black). owndata is set to 1.
struct cg_surface_t * cg_surface_create_for_data(int width, int height, void * pixels);Creates a surface wrapping an existing pixel buffer. pixels must be at least width * height * 4 bytes in premultiplied RGBA32 format. owndata is set to 0, meaning calling cg_surface_destroy will not free the pixel data — the caller is responsible for its lifetime.
void cg_surface_destroy(struct cg_surface_t * surface);Decrements the reference count. When it reaches zero, frees the pixel buffer (if owndata == 1) and the surface struct. Safe to call with NULL.
struct cg_surface_t * cg_surface_reference(struct cg_surface_t * surface);Increments the reference count and returns the surface. Safe to call with NULL (returns NULL).
Paint objects are standalone — they can be created once and reused across multiple drawing operations or contexts. Use cg_paint_destroy when done.
struct cg_paint_t * cg_paint_create_rgb(float r, float g, float b);Creates a solid color paint with alpha = 1.0. Equivalent to cg_paint_create_rgba(r, g, b, 1.0f).
struct cg_paint_t * cg_paint_create_rgba(float r, float g, float b, float a);Creates a solid color paint. All components are clamped to [0.0, 1.0].
struct cg_paint_t * cg_paint_create_color(struct cg_color_t * color);Creates a solid color paint from a cg_color_t struct. Equivalent to cg_paint_create_rgba(color->r, color->g, color->b, color->a).
struct cg_paint_t * cg_paint_create_linear_gradient(
float x1, float y1,
float x2, float y2,
enum cg_spread_method_t spread,
struct cg_gradient_stop_t * stops,
int nstops,
struct cg_matrix_t * m);Creates a linear gradient paint. The gradient transitions from stops[0].color at (x1, y1) to stops[nstops-1].color at (x2, y2). Intermediate stops are interpolated between these endpoints.
| Parameter | Description |
|---|---|
x1, y1 |
Start point of the gradient line |
x2, y2 |
End point of the gradient line |
spread |
How to handle coordinates outside [0, 1] along the gradient |
stops |
Array of nstops color stops (offsets clamped & sorted) |
m |
Optional user-to-gradient transform matrix, or NULL for identity |
struct cg_paint_t * cg_paint_create_radial_gradient(
float cx0, float cy0, float r0,
float cx1, float cy1, float r1,
enum cg_spread_method_t spread,
struct cg_gradient_stop_t * stops,
int nstops,
struct cg_matrix_t * m);Creates a radial gradient paint transitioning from circle (cx0, cy0, r0) at offset 0 to circle (cx1, cy1, r1) at offset 1.
Note: The values array in the paint is stored as {cx0, cy0, r0, cx1, cy1, r1} (start circle first, then end circle).
struct cg_paint_t * cg_paint_create_texture(
struct cg_surface_t * surface,
enum cg_texture_type_t type,
float opacity,
struct cg_matrix_t * m);Creates a texture paint. The surface is referenced (refcount incremented) and will be released when the paint is destroyed. opacity is clamped to [0, 1] and multiplied with the context's global opacity.
The optional matrix m (or identity if NULL) defines a user-to-texture-space transform.
void cg_paint_destroy(struct cg_paint_t * paint);Decrements reference count. When zero:
- For texture paints: calls
cg_surface_destroyon the referenced surface. - For all paints: frees the memory.
struct cg_paint_t * cg_paint_reference(struct cg_paint_t * paint);Increments reference count and returns the paint. Safe with NULL.
Path objects can be built independently of a context and reused.
struct cg_path_t * cg_path_create(void);Creates an empty path with no sub-paths.
void cg_path_destroy(struct cg_path_t * path);Decrements reference count, frees path data when zero.
struct cg_path_t * cg_path_reference(struct cg_path_t * path);Increments reference count, returns the path.
void cg_path_reset(struct cg_path_t * path);Clears all elements, contour data, and curve count. Resets to an empty path.
void cg_path_sub_path(struct cg_path_t * path);Marks that the next drawing command should begin a new sub-path. If the next command is line_to, cubic_to, etc. without a preceding move_to, it will be promoted to a move_to.
void cg_path_close(struct cg_path_t * path);Closes the current sub-path by adding a CLOSE command referencing the sub-path's start point. Does nothing if the path is empty. Resets the sub_path flag.
void cg_path_transform(struct cg_path_t * path, struct cg_matrix_t * m);Transforms all points in the path by matrix m in place. Traverses all move, line, close (1 point) and cubic (3 points) commands and maps each point through the matrix.
void cg_path_get_current_point(struct cg_path_t * path, float * x, float * y);Returns the current position. If there are no points or the path is in "new sub-path" mode, returns (0, 0). Either x or y may be NULL if only one coordinate is needed.
Paths are built incrementally. The first drawing command implicitly creates a MOVE_TO to (0, 0) if the path is empty.
void cg_path_move_to(struct cg_path_t * path, float x, float y);Starts a new sub-path at (x, y). Increments the contour count. Resets the sub_path flag.
void cg_path_line_to(struct cg_path_t * path, float x, float y);Adds a straight line segment from the current point to (x, y).
- If
sub_pathis set, promotes tomove_to(x, y). - If the path is empty, first adds
move_to(0, 0).
void cg_path_quad_to(struct cg_path_t * path, float x1, float y1, float x2, float y2);Adds a quadratic Bezier curve. Internally converted to cubic:
CP1 = 2/3 * (x1, y1) + 1/3 * current_point
CP2 = 2/3 * (x1, y1) + 1/3 * (x2, y2)
If the path is empty, first adds move_to(0, 0).
void cg_path_cubic_to(struct cg_path_t * path,
float x1, float y1,
float x2, float y2,
float x3, float y3);Adds a cubic Bezier curve with two control points (x1, y1), (x2, y2) and end point (x3, y3). Increments the curve count.
If the path is empty or sub_path is set, first adds move_to(0, 0).
void cg_path_arc_to(struct cg_path_t * path,
float rx, float ry,
float angle,
int large,
int sweep,
float x, float y);Adds an elliptical arc from the current point to (x, y).
| Parameter | Description |
|---|---|
rx, ry |
Ellipse radii (negatives are treated as absolute values) |
angle |
Rotation of the ellipse in radians |
large |
Non-zero to take the larger arc (arc > 180°) |
sweep |
Non-zero for positive (clockwise) angle direction |
x, y |
End point of the arc |
Edge cases:
- If
rx == 0orry == 0, or if start equals end, falls back tocg_path_line_to. - If the radii are too small to reach the endpoint, they are scaled up proportionally.
void cg_path_add_rectangle(struct cg_path_t * path, float x, float y, float w, float h);Adds a closed rectangle as a new sub-path: move_to(x, y) → line_to(x+w, y) → line_to(x+w, y+h) → line_to(x, y+h) → line_to(x, y) → close.
void cg_path_add_round_rectangle(struct cg_path_t * path,
float x, float y,
float w, float h,
float rx, float ry);Adds a closed rounded rectangle. Corner radii are clamped to min(w/2, h/2). If both radii are zero, falls back to cg_path_add_rectangle. Uses cubic Beziers with control points at radius * 0.5522847498 (KAPPA) to approximate quarter-circles.
void cg_path_add_ellipse(struct cg_path_t * path, float cx, float cy, float rx, float ry);Adds a closed ellipse centered at (cx, cy) with radii (rx, ry). Uses 4 cubic Bezier curves per ellipse.
void cg_path_add_circle(struct cg_path_t * path, float cx, float cy, float r);Convenience wrapper: calls cg_path_add_ellipse(path, cx, cy, r, r).
void cg_path_add_arc(struct cg_path_t * path,
float cx, float cy, float r,
float a0, float a1,
int ccw);Adds a circular arc centered at (cx, cy) from angle a0 to a1.
| Parameter | Description |
|---|---|
a0 |
Start angle in radians |
a1 |
End angle in radians |
ccw |
Non-zero for counter-clockwise direction |
Behavior:
- If
|a1 - a0| > 2π, the difference is clamped to2π. - If direction mismatch (e.g.,
a0 < a1butccwis set), adds or subtracts2π. - If the path is empty or in
sub_pathmode, begins with amove_toto the start point; otherwise adds aline_to. - Each
π/2segment is subdivided into one cubic Bezier.
void cg_path_add_path(struct cg_path_t * path,
struct cg_path_t * source,
struct cg_matrix_t * m);Appends source to the current path. If m is NULL, the source elements are copied directly. If m is non-NULL, each point in source is transformed by m before being added.
float cg_path_extents(struct cg_path_t * path, struct cg_rect_t * extents, int tight);Computes the bounding box of the path.
| Parameter | Description |
|---|---|
extents |
Output rectangle, or NULL if not needed |
tight |
Non-zero: flattens Bezier curves for exact extents. 0: uses control points only |
Returns: The approximate arc length of the path (sum of segment lengths), not the area.
struct cg_ctx_t * cg_create(struct cg_surface_t * surface);Creates a drawing context bound to surface. The surface is referenced. Initializes a default state, an empty path, and sets the clip rectangle to cover the entire surface {0, 0, width, height}.
void cg_destroy(struct cg_ctx_t * ctx);Destroys the context. Frees all saved states, the freed-state pool, span buffers, the surface reference, and the path. Safe with NULL.
Drawing states are stored on a linked-list stack. cg_save pushes a copy of the current state; cg_restore pops it. States include: paint, color, matrix, stroke style (width/cap/join/miter/dash), fill rule, operator, opacity, and clip region.
The library maintains a freed-state pool to avoid repeated malloc/free during save/restore cycles.
void cg_save(struct cg_ctx_t * ctx);Pushes the current state onto the stack. All subsequent modifications to paint, matrix, stroke style, etc. are isolated until cg_restore is called.
void cg_restore(struct cg_ctx_t * ctx);Pops the top state from the stack and restores it. Does nothing if only one state remains (the initial state is never popped).
struct cg_paint_t * cg_get_source(struct cg_ctx_t * ctx, struct cg_color_t * color);Returns the current paint object (may be NULL if using simple color mode). If color is non-NULL and a paint exists, the color is filled from the state's stored color (which may differ from the paint's color — the state snapshot color tracks the last cg_set_source_* value).
Note: When you call cg_set_source_rgba, it sets state->color to the given RGBA and sets state->paint = NULL. So cg_get_source would return NULL but cg_get_source(ctx, &c) would fill c with the correct color.
struct cg_surface_t * cg_get_surface(struct cg_ctx_t * ctx);Returns the surface bound to this context.
struct cg_path_t * cg_get_path(struct cg_ctx_t * ctx);Returns the current path being constructed.
struct cg_matrix_t * cg_get_matrix(struct cg_ctx_t * ctx);Returns a pointer to the current transformation matrix (mutable — changes affect the context).
enum cg_operator_t cg_get_operator(struct cg_ctx_t * ctx);Returns the current compositing operator.
float cg_get_opacity(struct cg_ctx_t * ctx);Returns the current global opacity.
void cg_get_current_point(struct cg_ctx_t * ctx, float * x, float * y);Returns the current path position.
int cg_has_current_point(struct cg_ctx_t * ctx);Checks whether the current path has a current point (i.e., a sub-path has been started with cg_move_to and not closed with cg_close_path or cg_new_sub_path). Returns 1 if there is a current point, 0 otherwise.
These functions rasterize the current path to determine spatial relationships. They are potentially expensive if called repeatedly in a loop.
void cg_fill_extents(struct cg_ctx_t * ctx, struct cg_rect_t * extents);Rasterizes the current path with the current fill rule and writes the bounding box to extents.
void cg_stroke_extents(struct cg_ctx_t * ctx, struct cg_rect_t * extents);Rasterizes the current path with the current stroke style (width, cap, join, dashes) and writes the bounding box to extents.
void cg_clip_extents(struct cg_ctx_t * ctx, struct cg_rect_t * extents);Returns the bounding box of the current clip region. If clipping is active, returns the clip span extents. Otherwise, returns the initial clip rectangle.
int cg_in_fill(struct cg_ctx_t * ctx, float x, float y);Tests if the point (x, y) is inside the filled area of the current path.
int cg_in_stroke(struct cg_ctx_t * ctx, float x, float y);Tests if the point (x, y) is inside the stroked area of the current path.
int cg_in_clip(struct cg_ctx_t * ctx, float x, float y);Tests if the point (x, y) is inside the current clip region.
void cg_set_source(struct cg_ctx_t * ctx, struct cg_paint_t * paint);Sets the current paint. The paint is referenced. Passing NULL reverts to using the state's stored color.
void cg_set_source_rgb(struct cg_ctx_t * ctx, float r, float g, float b);Sets the paint to a solid RGB color (alpha = 1.0). Internally stores the color in the state and sets paint to NULL.
void cg_set_source_rgba(struct cg_ctx_t * ctx, float r, float g, float b, float a);Sets the paint to a solid RGBA color.
void cg_set_source_surface(struct cg_ctx_t * ctx, struct cg_surface_t * surface, float x, float y);Creates a plain (non-tiled) texture paint from surface with a translation offset of (x, y). The surface is referenced. Internally equivalent to:
cg_set_texture(ctx, surface, CG_TEXTURE_TYPE_PLAIN, 1.0, &translate_matrix);void cg_set_linear_gradient(struct cg_ctx_t * ctx,
float x1, float y1, float x2, float y2,
enum cg_spread_method_t spread,
struct cg_gradient_stop_t * stops, int nstops,
struct cg_matrix_t * m);Creates and sets a linear gradient paint. See cg_paint_create_linear_gradient for parameter details.
void cg_set_radial_gradient(struct cg_ctx_t * ctx,
float cx0, float cy0, float r0,
float cx1, float cy1, float r1,
enum cg_spread_method_t spread,
struct cg_gradient_stop_t * stops, int nstops,
struct cg_matrix_t * m);Creates and sets a radial gradient paint. See cg_paint_create_radial_gradient for parameter details.
void cg_set_texture(struct cg_ctx_t * ctx,
struct cg_surface_t * surface,
enum cg_texture_type_t type,
float opacity,
struct cg_matrix_t * m);Creates and sets a texture paint. See cg_paint_create_texture for parameter details.
void cg_set_operator(struct cg_ctx_t * ctx, enum cg_operator_t op);Sets the compositing operator used when blending onto the surface.
void cg_set_opacity(struct cg_ctx_t * ctx, float opacity);Sets the global opacity. Clamped to [0.0, 1.0]. This is multiplied with the paint's opacity and per-span coverage.
void cg_set_fill_rule(struct cg_ctx_t * ctx, enum cg_fill_rule_t rule);Sets the fill rule. Applies to both filling and clipping.
void cg_set_line_width(struct cg_ctx_t * ctx, float width);Sets the stroke line width. The width is affected by the current transformation matrix (scaling the CTM will change the apparent stroke width).
void cg_set_line_cap(struct cg_ctx_t * ctx, enum cg_line_cap_t cap);Sets how the ends of open sub-paths are drawn.
void cg_set_line_join(struct cg_ctx_t * ctx, enum cg_line_join_t join);Sets how corners between connected line segments are drawn.
void cg_set_miter_limit(struct cg_ctx_t * ctx, float limit);Sets the miter limit. When joining lines with CG_LINE_JOIN_MITER, if the miter would extend beyond limit * line_width / 2, the join is converted to a bevel.
void cg_set_dash(struct cg_ctx_t * ctx, float * dashes, int ndashes, float offset);Sets the dash pattern. dashes is an array of alternating dash/gap lengths (in user space). If the array has an odd number of elements, it is effectively doubled. offset specifies where in the pattern to start. Both the array and offset can be set independently with cg_set_dash_array and cg_set_dash_offset.
To disable dashing, set ndashes to 0.
void cg_set_dash_array(struct cg_ctx_t * ctx, float * dashes, int ndashes);Sets only the dash array, preserving the current offset.
void cg_set_dash_offset(struct cg_ctx_t * ctx, float offset);Sets only the dash offset, preserving the current array.
All transform functions post-multiply the given transform onto the current transformation matrix (CTM). This means transforms are applied in the order they are called — the last transform happens first.
void cg_translate(struct cg_ctx_t * ctx, float tx, float ty);Translates the CTM by (tx, ty).
void cg_scale(struct cg_ctx_t * ctx, float sx, float sy);Scales the CTM by (sx, sy).
void cg_shear(struct cg_ctx_t * ctx, float shx, float shy);Shears the CTM.
void cg_rotate(struct cg_ctx_t * ctx, float angle);Rotates the CTM by angle radians clockwise.
void cg_transform(struct cg_ctx_t * ctx, struct cg_matrix_t * m);Post-multiplies the CTM by m: CTM = m * CTM.
void cg_set_matrix(struct cg_ctx_t * ctx, struct cg_matrix_t * m);Replaces the CTM with m.
void cg_identity_matrix(struct cg_ctx_t * ctx);Resets the CTM to the identity matrix.
These functions build the current path. Coordinates are in user space and will be transformed by the CTM at render time.
void cg_move_to(struct cg_ctx_t * ctx, float x, float y);Begins a new sub-path at (x, y).
void cg_line_to(struct cg_ctx_t * ctx, float x, float y);Adds a line segment to (x, y).
void cg_quad_to(struct cg_ctx_t * ctx, float x1, float y1, float x2, float y2);Adds a quadratic Bezier curve with control point (x1, y1) and end point (x2, y2). Internally converted to cubic.
void cg_cubic_to(struct cg_ctx_t * ctx,
float x1, float y1,
float x2, float y2,
float x3, float y3);Adds a cubic Bezier curve.
void cg_arc_to(struct cg_ctx_t * ctx,
float rx, float ry, float angle,
int large, int sweep,
float x, float y);Adds an elliptical arc. See cg_path_arc_to for details.
These functions compute the target coordinates by adding deltas to the current point:
void cg_rel_move_to(struct cg_ctx_t * ctx, float dx, float dy);
void cg_rel_line_to(struct cg_ctx_t * ctx, float dx, float dy);
void cg_rel_quad_to(struct cg_ctx_t * ctx, float dx1, float dy1, float dx2, float dy2);
void cg_rel_cubic_to(struct cg_ctx_t * ctx, float dx1, float dy1, float dx2, float dy2, float dx3, float dy3);
void cg_rel_arc_to(struct cg_ctx_t * ctx, float rx, float ry, float angle, int large, int sweep, float dx, float dy);For example, cg_rel_line_to(ctx, 10, 0) draws a line 10 units to the right from the current point.
Each of these adds a complete closed sub-path:
void cg_rectangle(struct cg_ctx_t * ctx, float x, float y, float w, float h);
void cg_round_rectangle(struct cg_ctx_t * ctx, float x, float y, float w, float h, float rx, float ry);
void cg_ellipse(struct cg_ctx_t * ctx, float cx, float cy, float rx, float ry);
void cg_circle(struct cg_ctx_t * ctx, float cx, float cy, float r);
void cg_arc(struct cg_ctx_t * ctx, float cx, float cy, float r, float a0, float a1);
void cg_arc_negative(struct cg_ctx_t * ctx, float cx, float cy, float r, float a0, float a1);cg_arc draws in the positive (clockwise) direction; cg_arc_negative draws counter-clockwise.
void cg_new_path(struct cg_ctx_t * ctx);Clears all segments, resetting the path to empty.
void cg_new_sub_path(struct cg_ctx_t * ctx);Begins a new sub-path. The next drawing command will start at the given point.
void cg_close_path(struct cg_ctx_t * ctx);Closes the current sub-path with a straight line back to its starting point.
void cg_add_path(struct cg_ctx_t * ctx, struct cg_path_t * path);Appends an external path to the current path. The path elements are copied directly (no transformation).
void cg_clip(struct cg_ctx_t * ctx);Rasterizes the current path using the current fill rule and sets it as the clip region, replacing any previous clip. Then clears the path. Subsequent drawing is visible only where it overlaps the clip.
Internally: The clip region is stored as a span buffer. On the first clip call, the path is rasterized and stored. Subsequent clip calls intersect with the existing clip spans.
void cg_clip_preserve(struct cg_ctx_t * ctx);Same as cg_clip, but does not clear the path (useful for fill+stroke after clipping).
void cg_reset_clip(struct cg_ctx_t * ctx);Resets the clip region back to the entire surface, as if no cg_clip had been called. Clears all accumulated clip spans and disables clipping.
void cg_fill(struct cg_ctx_t * ctx);Rasterizes the current path with the current fill rule, then blends the paint onto the surface within the filled region (intersected with any active clip). Then clears the path.
Rendering pipeline: path → FT outline → rasterize to spans → intersect with clip spans (if any) → blend with active operator/opacity.
void cg_fill_preserve(struct cg_ctx_t * ctx);Same as cg_fill, but preserves the path for subsequent operations (e.g., fill then stroke).
void cg_mask(struct cg_ctx_t * ctx, struct cg_paint_t * paint);A drawing operator that paints the current source using the alpha channel of paint as a per-pixel mask within the filled area of the current path. Opaque areas of the mask paint are painted with the source, transparent areas are not painted. Clears the path after completion.
Internally, the mask paint is rendered to a temporary surface (respecting the current transformation matrix), and then blended pixel-by-pixel: the source is rendered to a temp surface, the mask temp surface's alpha is used as per-pixel coverage, combined with the current operator and opacity. cg_mask_surface is a convenience wrapper that creates a texture paint from a surface and calls cg_mask.
void cg_mask_surface(struct cg_ctx_t * ctx, struct cg_surface_t * mask, float x, float y);A convenience wrapper that wraps mask into a texture paint at user-space position (x, y) and calls cg_mask(ctx, paint). The x and y parameters specify the user-space coordinate where the mask surface origin (upper-left corner) should appear.
void cg_stroke(struct cg_ctx_t * ctx);Strokes the current path using the current stroke style (width, cap, join, miter limit, dash pattern). If dashes are set, the path is dashed first, then stroked using the underlying FreeType stroker. Clears the path.
void cg_stroke_preserve(struct cg_ctx_t * ctx);Same as cg_stroke, but preserves the path.
void cg_paint(struct cg_ctx_t * ctx);Fills the entire clip region (or the entire surface if no clip is set) with the current paint. Does not use the path. This is typically used to fill a surface with a texture or gradient covering the whole area.
void cg_paint_with_alpha(struct cg_ctx_t * ctx, float alpha);Fills the entire clip region with the current paint, using the specified alpha value. Equivalent to cg_save(ctx) + cg_set_opacity(ctx, alpha) + cg_paint(ctx) + cg_restore(ctx). The original opacity is preserved after the call.
#include <cg.h>
struct cg_surface_t * surface = cg_surface_create(400, 300);
struct cg_ctx_t * ctx = cg_create(surface);
cg_set_source_rgba(ctx, 1.0, 0.0, 0.0, 1.0);
cg_rectangle(ctx, 50, 50, 100, 80);
cg_fill(ctx);
cg_set_source_rgba(ctx, 0.0, 0.0, 1.0, 1.0);
cg_set_line_width(ctx, 3.0);
cg_rectangle(ctx, 50, 50, 100, 80);
cg_stroke(ctx);
cg_destroy(ctx);
cg_surface_destroy(surface);struct cg_surface_t * surface = cg_surface_create(256, 256);
struct cg_ctx_t * ctx = cg_create(surface);
cg_arc(ctx, 128, 128, 76.8, 0, 2 * M_PI);
cg_clip(ctx);
cg_rectangle(ctx, 0, 0, 256, 256);
cg_fill(ctx);
cg_set_source_rgb(ctx, 0, 1, 0);
cg_move_to(ctx, 0, 0);
cg_line_to(ctx, 256, 256);
cg_set_line_width(ctx, 10.0);
cg_stroke(ctx);
cg_destroy(ctx);
cg_surface_destroy(surface);struct cg_surface_t * surface = cg_surface_create(256, 256);
struct cg_ctx_t * ctx = cg_create(surface);
struct cg_gradient_stop_t stops[] = {
{ 0.0, { 1, 1, 1, 1 } },
{ 1.0, { 0, 0, 0, 1 } },
};
cg_rectangle(ctx, 0, 0, 256, 256);
cg_set_linear_gradient(ctx, 0, 0, 0, 256,
CG_SPREAD_METHOD_PAD, stops, 2, NULL);
cg_fill(ctx);
cg_destroy(ctx);
cg_surface_destroy(surface);struct cg_surface_t * surface = cg_surface_create(256, 256);
struct cg_ctx_t * ctx = cg_create(surface);
cg_set_source_rgb(ctx, 1, 0, 0);
cg_save(ctx);
cg_translate(ctx, 64, 64);
cg_rotate(ctx, M_PI / 4);
cg_rectangle(ctx, -30, -20, 60, 40);
cg_fill(ctx);
cg_restore(ctx);
cg_save(ctx);
cg_translate(ctx, 192, 192);
cg_scale(ctx, 2.0, 1.0);
cg_rectangle(ctx, -30, -20, 60, 40);
cg_fill(ctx);
cg_restore(ctx);
cg_destroy(ctx);
cg_surface_destroy(surface);struct cg_surface_t * surface = cg_surface_create(256, 256);
struct cg_ctx_t * ctx = cg_create(surface);
float dashes[] = { 50, 10, 10, 10 };
cg_set_dash(ctx, dashes, 4, -50);
cg_set_line_width(ctx, 10);
cg_set_line_cap(ctx, CG_LINE_CAP_ROUND);
cg_set_line_join(ctx, CG_LINE_JOIN_ROUND);
cg_move_to(ctx, 128, 25.6);
cg_line_to(ctx, 230.4, 230.4);
cg_rel_line_to(ctx, -102.4, 0);
cg_cubic_to(ctx, 51.2, 230.4, 51.2, 128, 128, 128);
cg_stroke(ctx);
cg_destroy(ctx);
cg_surface_destroy(surface);cg_set_operator(ctx, CG_OPERATOR_SRC_OVER);
cg_set_source_rgba(ctx, 1.0, 0.0, 0.0, 0.7);
cg_arc(ctx, cx, cy, r, 0, 2 * M_PI);
cg_fill(ctx);struct cg_surface_t * img = cg_surface_create_for_data(128, 128, pixels);
struct cg_surface_t * surface = cg_surface_create(256, 256);
struct cg_ctx_t * ctx = cg_create(surface);
cg_translate(ctx, 128, 128);
cg_rotate(ctx, -45 * M_PI / 180);
cg_scale(ctx, 0.8, 0.8);
cg_translate(ctx, -64, -64);
cg_set_texture(ctx, img, CG_TEXTURE_TYPE_TILED, 1.0, NULL);
cg_paint(ctx);
cg_destroy(ctx);
cg_surface_destroy(surface);
cg_surface_destroy(img);