On this page
Server-side rasterizer that materializes role-plane semantic-painting commands into per-pixel role bytes, serving both the WebSocket and MCP mutation paths.
#server.src.pixelweaver.role_raster
#server.src.pixelweaver.role_raster
Server-side rasterizer for role-plane (semantic painting) commands.
The RGBA rasterizer (command_raster) materializes classic pixel commands into frame.pixel_data. Role-kind layers instead store one ramp-role byte per pixel in frame.role_data (0xFF sentinel = transparent). Historically the role tools only recorded a command in command_history and relied on a connected frontend to rasterize -- so headless MCP sessions dropped every role edit and frame.role_data stayed empty.
This module closes that gap for role commands, mirroring the frontend role tools (plugins/builtin/*-tool.ts role twins + drawing-utils role helpers). The command parameter schema for role commands is identical between the MCP tools and the client-generated protocol (x0/y0/x1/y1, w/h, role, matrixSize), so this single rasterizer serves both the WebSocket and MCP mutation paths. Only the layer-id key differs (layer_id for MCP, layerId for the client) and both are accepted.
#bresenham_line_nodoubles
def bresenham_line_nodoubles(x0: int, y0: int, x1: int, y1: int) -> list[tuple[int, int]]Bresenham variant that steps diagonally instead of emitting doubles.
Port of drawing-utils.bresenhamLineNoDoubles: when both the major and minor axis would advance, take a single diagonal step so the line never contains an L-shaped "double" pixel.
#_RolePlane
Thin mutable view over a canvas frame's role-plane byte buffer.
#in_bounds
def in_bounds(self, x: int, y: int) -> bool#get_role
def get_role(self, x: int, y: int) -> int#set_role
def set_role(self, x: int, y: int, role: int) -> None#flood_fill_roles
def flood_fill_roles(plane: _RolePlane, start_x: int, start_y: int) -> list[tuple[int, int]]4-connected flood fill on the role plane (port of floodFillRoles).
#_resolve_role_layer_id
def _resolve_role_layer_id(canvas: CanvasState, params: dict[str, Any]) -> str | NonePick the target role layer id (explicit id else the first role layer).
#_validate_role_value
def _validate_role_value(value: Any) -> intA paint role must be an int in [0, 254]; the 0xFF sentinel is never a paint value (use erase_roles for transparency).
#apply_role_command
def apply_role_command(canvas: CanvasState, command: dict[str, Any]) -> boolRasterize a role command into canvas's role-plane data.
Returns True if the command was a recognized role command that was applied, False otherwise. Bounds are clipped. Raises ValueError on an illegal shade value or when no role layer can be resolved (hard error, never a silent no-op).