On this page
Server-side rasterizer that interprets dispatched MCP drawing commands and writes the resulting pixels directly into the target canvas frame buffer.
#server.src.pixelweaver.command_raster
#server.src.pixelweaver.command_raster
Server-side rasterizer for MCP drawing commands.
The collaboration server is the single source of truth for pixel data, but historically the drawing tools only recorded a command in command_history and relied on a connected frontend to rasterize it into pixels. In a headless MCP session (no frontend attached) those mutations were silently dropped: the server's authoritative pixel_data buffers stayed empty, GET /api/state/full returned a blank canvas, and save_project persisted empty PNGs.
This module closes that gap. apply_command interprets a dispatched drawing command (as produced by MCPCommandRegistry._make_command) and writes the resulting pixels directly into the target canvas frame, so the state the MCP pushes back actually carries the drawn pixels.
Geometry (line/rect/ellipse/diamond/flood-fill) mirrors the frontend plugins/builtin/drawing-utils.ts so the server and the UI agree on shapes. Write semantics are "replace" (overwrite RGBA), matching applyPixels / buffer.setPixel. Only RGBA drawing commands are handled; every other command type is a no-op here (role-plane and structural commands mutate state through their own paths).
#parse_hex_rgba
def parse_hex_rgba(color: str) -> tuple[int, int, int, int]Parse #rrggbb or #rrggbbaa into an (r, g, b, a) tuple.
A 6-digit hex has no alpha channel and is treated as fully opaque, matching the frontend hexToRgba.
#_Buffer
Thin mutable view over a canvas frame's RGBA byte buffer.
#in_bounds
def in_bounds(self, x: int, y: int) -> bool#get_pixel
def get_pixel(self, x: int, y: int) -> tuple[int, int, int, int]#set_pixel
def set_pixel(self, x: int, y: int, r: int, g: int, b: int, a: int) -> None#bresenham_line
def bresenham_line(x0: int, y0: int, x1: int, y1: int) -> list[tuple[int, int]]Standard Bresenham line, inclusive of both endpoints.
#rect_filled
def rect_filled(x: int, y: int, w: int, h: int) -> list[tuple[int, int]]Every pixel in the w x h rectangle at (x, y) (port of drawing-utils rectFilled).
#rect_outline
def rect_outline(x: int, y: int, w: int, h: int) -> list[tuple[int, int]]Border pixels of the w x h rectangle at (x, y) (port of drawing-utils rectOutline).
#ellipse_outline
def ellipse_outline(cx: int, cy: int, rx: int, ry: int) -> list[tuple[int, int]]Midpoint ellipse outline (port of drawing-utils ellipseOutline).
#ellipse_filled
def ellipse_filled(cx: int, cy: int, rx: int, ry: int) -> list[tuple[int, int]]Solid ellipse via scanline-fill of the outline (port of drawing-utils ellipseFilled).
#diamond_outline
def diamond_outline(cx: int, cy: int, rx: int, ry: int) -> list[tuple[int, int]]Diamond border through the four axis vertices (port of drawing-utils diamondOutline).
#diamond_filled
def diamond_filled(cx: int, cy: int, rx: int, ry: int) -> list[tuple[int, int]]Solid diamond of radius (rx, ry) at (cx, cy) (port of drawing-utils diamondFilled).
#flood_fill
def flood_fill(buffer: _Buffer, start_x: int, start_y: int, tolerance: int=0) -> list[tuple[int, int]]4-connected flood fill (port of drawing-utils floodFill).
#_srgb_to_linear
def _srgb_to_linear(c: float) -> floatsRGB channel (0..1) to linear light (port of culori convertRgbToLrgb).
#_rgb_to_oklab
def _rgb_to_oklab(r: int, g: int, b: int) -> tuple[float, float, float]8-bit sRGB to OKLab (port of culori convertRgbToLrgb + convertLrgbToOklab).
Matches colorDistance in src/lib/color/color-utils.ts (culori's differenceEuclidean('oklab')) so the tolerance fill selects the same region as the browser.
#oklab_distance
def oklab_distance(c1: tuple[int, int, int], c2: tuple[int, int, int]) -> floatPerceptual OKLab Euclidean distance in [0, 1] between two RGB triples.
Port of colorDistance (color-utils.ts): Euclidean distance in OKLab, clamped to 1. Operates on the RGB channels only (the client passes rgbToHex(r, g, b), dropping alpha).
#flood_fill_tolerance
def flood_fill_tolerance(buffer: _Buffer, start_x: int, start_y: int, tolerance: float) -> list[tuple[int, int]]4-connected flood fill with OKLab color tolerance.
Line-for-line port of floodFillTolerance in plugins/builtin/advanced-fill-tool.ts: transparent targets match only transparent neighbours; opaque targets match opaque neighbours whose OKLab distance from the start pixel is <= tolerance.
#scanline_fill_polygon
def scanline_fill_polygon(vertices: list[tuple[int, int]]) -> list[tuple[int, int]]Scanline fill of an arbitrary polygon (port of drawing-primitives scanlineFillPolygon). Vertices are (x, y) pairs.
#_resolve_frame
def _resolve_frame(canvas: CanvasState, params: dict[str, Any])Pick the frame this command targets (explicit frame_index else current).
#_resolve_layer_id
def _resolve_layer_id(canvas: CanvasState, params: dict[str, Any]) -> str | NonePick the target layer id (explicit layer_id else the first layer).
#apply_command
def apply_command(canvas: CanvasState, command: dict[str, Any]) -> boolRasterize a drawing command into canvas's pixel data.
Returns True if the command was a recognized drawing command that was applied, False otherwise (structural/role/unknown commands are no-ops). Bounds are clipped; out-of-canvas geometry is simply not written.
#_rasterize
def _rasterize(cmd_type: str, params: dict[str, Any], buffer: _Buffer) -> NoneDispatch a single drawing command onto the buffer.