The ctx text format is a direct serialization of the ctx drawing API. This protocol permits sending rendering commands over serial, ptys and other forms of network - enabling remote and/or sandboxed rendering.
When used in the ctx terminal, the ctx rendering mode is entered by issuing the escape sequence \e[?200h this makes the terminal interpret commands for a ctx context created for the line the cursor currently is in. You should send a : first, to start the frame, and X + white space to end the sequence.
Starting point simple printing wrappers for bash-test.sh ctx.bash, test.nim ctx2d.nim and test.py ctx2d.py provide ways to experiment with terminal apps in these languages, the bash wrapper is most complete in that it also incorporates events, none of these do images/textures yet. The simulators for flow3r and tildagon badges have a more complete python wrappers as part of the simulator.
For just rendering these wrappers are sufficient but you do not get the full ability of the C api or bindings of it like with micropython that allows you to read back parts of the graphics rendering state like transforms, colors, font sizes, line parameters and more, as well as more complex event handling.
The bash example ctx-clock.sh, renders an analog clock without using any binding that scales with the terminal window, for more examples of the ctx syntax in practice see the rendering test gallery.
#!/bin/bash echo -ne "\e[?1049h" # alt-screen on echo -ne "\e[?25l" # text-cursor off function cleanup { echo -ne " X " # leave ctx mode; in case we're there echo -ne "\e[?1049l" # alt-screen off echo -ne "\e[?25h" # text-cursor on } trap cleanup EXIT # restore terminal state on ctrl+c and regular exit for a in b{1..1000}; do hour_radians=`bc <<<"scale=3;(($(date +%H|sed 's/^0//')+($(date +%M|sed s'/^6//')/60.0))/12.0+0.75)*3.14152*2"` minute_radians=`bc <<<"scale=3;($(date +%M|sed 's/^0//')/60.0+0.75)*3.14152*2"` second_radians=`bc <<<"scale=3;($(date +%S|sed 's/^0//')/60.0+0.75)*3.14152*2"` radius=45 echo -ne "\e[2J\e[H" # clear screen and go to top left corner echo -e "\e[?200h" # enter ctx mode _ rgba 1 1 1 0.5 arc 50% 50% $radius% 0.0 6.4 0 lineWidth $((radius/10))% stroke lineCap round moveTo 50% 50% arc 50% 50% $(($radius * 70 / 100))% $minute_radians $minute_radians 0 moveTo 50% 50% arc 50% 50% $(($radius * 50 / 100))% $hour_radians $hour_radians 0 stroke lineWidth $((radius/40))% moveTo 50% 50% arc 50% 50% $((radius * 90 / 100))% $second_radians $second_radians 0 _ rgba 1 0.1 0.1 1 stroke done "; sleep 0.1; done
This format maps to the C API if you turn camelCase into snake_case, and prefix each command with ctx_.
When a keyword is parsed the parser expects a series of commands of the same type, following arguments will be consumed in chunks as the number of arguments fill up, until a command change. Each command also has a short one-byte long name, where applicable these match the SVG path data format.
command | argument(s) | |
color | ||
---|---|---|
gray | gray | sets gray source, and color model |
graya | gray alpha | sets gray alpha source, and color model |
rgb | r g b | sets rgb source |
rgba | r g b a | rgb alpha color source |
cmyk | c m y k | cmyk color source |
cmyka | c m y k a | cmyk alpha color source |
drgb | r g b | sets rgb source, in device space |
drgba | r g b a | rgb alpha color source, in device space |
dcmyk | c m y k | cmyk color source, in device space |
dcmyka | c m y k a | cmyk alpha color source, in device space |
gradients | ||
f linearGradient | x0 y0 x1 y1 | start a linear gradient going from x0,y0 to x1,y1 initially it is black to white but this can be overriden by adding stops. |
o radialGradient | x0 y0 r0 x1 y1 r1 | Change the source to a radial gradient from a circle x0,y0 with radius r0 to an outher circle x1, y1 with radius r1. (NOTE: currently ctx is only using the second circles origin, both radiuses are in use.) |
U conicGradient | cx cy start_angle cycles | Change the source to a conic/conic gradient cenetered at cx,cy with gradient starting at angle start_angle. |
p gradientAddStop addStop | pos R G B A | arguments depend on current color model, you can change the color model without setting a color by specifying a color without arguments. |
textures | ||
I defineTexture | "eid" width height format ~a85encodedpixels~ | defines a texture, for now format is an integer that for 1-4 corresponds to GRAY8, GRAYA8, RGB8 and RGBA8, higher integer values correspond to the pixel formats used internally in ctx; but the protocol does as of yet not have a way to refer to pixelformat by a non-integer values. |
i texture | "eid" x y | sets active texture at offset x, y - can be used after defineTexture in and in frames immediately after other frames where the texture has been used. |
path construction | ||
N resetPath | removes all segments from the current path, this happens implicitly on clip,fill and stroke - unless preserve flag is set. | |
z closePath | special cased in tokenizer so that it does not need whitespace separting it from a following command word, this occurs in minimized SVG paths with subpaths folling close-path. | |
Z closePath | the note for z also applies to Z | |
M moveTo | x y | Set the current position, for adding path elements or drawing text. |
L lineTo | x y | Adds a straight line segment to the current path, x and y are new position. |
C curveTo | cx1 cy1 cx2 cy2 x y | Add a bezier curve segment ending at x, y with cx1,cy1 and cx2,cy2 as control points. |
Q quadTo | cx cy x y | Adds a quadratic bezier curve to x, y with control points cx, cy |
H horLineTo | x | draws a horizontal line |
V verLineTo | y | adds a vertical line segment anding at y |
S smoothTo | cx cy x y | Adds a smooth curve segment with only one control point cx,cy going to x, y |
T smoothQuadTo | x y | Adds a smooth curve segment for a curve going to x, y. |
A arcTo | xr yr rotation large sweep x1 y1 | adds a circular arc segment to current path centered at xr,yr going to x1,y1 with choice of possible solution chosen by other parameters. |
B arc | x y radius angle1 angle2 direction | Adds an arc segment starting at x,y with radius going from angle1 to angle2 in given direction. |
r rectangle rect | x y width height | adds a rectangle to the current path as a new sub path |
| roundRectangle | x y width height radius | Addds a rectangle to the current path as a subpath, with rounding of the corners of up to radius as the geometry permits. |
m relMoveTo | x y | moves the current position, starting a new subpath |
l relLineTo | x y | adds a straight line segment moving x and y relative to the current position. |
c relCurveTo | rcx0 rcy0 rcx1 rcy1 x y | Adds a quadratic bezier curve segment, with control points and destination x, y in coordinates relative to the current position. |
h relHorLine | distance | Add a line segment along the x axis the is distance long, negative for moving left. |
q relQuadTo | cx cy x y | Add a relative quadrativ bezier curve segment ending at x,y with cx, cy control points. |
s relSmoothTo | cx cy x y | Add a smooth curve to x, y with control point cx, cy with coordinates specified relative to current position. |
t relSmoothQuadTo | x y | Add a relative smooth quadratic stroke to x,y no control points needed. |
v relVerLine | distance | Add a straight line segment distance long from the current position to the current path. |
a relArcTo | xr yr rotation large sweep x y | Adds a circular arc segment with target x, y and xr,yr relative to current position. |
drawing commands | ||
D paint | fills the clipping path or full canvas. | |
F fill | fill the current path with the current color, gradient or pattern. | |
u stroke | strokes the current path using the currently set lineWidth, dashing, miter join and cap parameters. | |
x fillText | "utf8-string" | kerning | Interspersed with utf8 sequences of text numbers can appear that shift the horizontal position. |
w glyph | unichar | draws a single unicode character, the character no is currently passed in as decimal. (might be made internal), since a text string duplicates the API. |
transforms | ||
O scale | scale_x scale_y | Apply a scale factor to current x and y axes, values of 1.0, 1.0 does nothing. 2.0, 2.0 enlarges. |
Y translate | x y | shifts the origin by x, y |
J rotate | radians | rotates the interpretation of path command coordinates around the current origin, combine with translates to set the point of rotation. |
y identity | reset to identity transform (might be removed) | |
W transform | a b c d e f g h i | applies the transformation matrix |
state management | ||
g save | saves colors, font, font size, stroke options, clip, transforms, can be called multiple times, needs a matching restore | |
G restore | to be paired with save | |
: startFrame | the start of a new frame/drawing | |
X endFrame | end of frame corresponding to a previous startFrame | |
{ startGroup | starts a compositing group | |
} endGroup | ends a compositing group, | |
b clip | start clipping with the current path, clips are part of the save/restore state. | |
global parameters | ||
] colorSpace | userRGB|userCMYK ~a85encoded icc profile~ | sets the color space of a given space in rasterizer. |
_ strokeSource | The following source definition (color, gradient or texture) applies to stroking. | |
ka globalAlpha | alpha | sets a global opacity value applied on top of the alpha the source color, gradient or texture might already have. |
km compositingMode | sourceOver | copy | clear | sourceIn | sourceOut | sourceAtop | destinationOver | destination | destinationIn | destination_out | destinationAtop | xor | Set the used compositing operator. |
kB blendMode | normal | multiply | screen | overlay | darken | lighten | colorDodge | colorBurn | hardLight | softLight | difference | exclusion | hue | saturation | color | luminosity | divide | addition | subtract | Set the current blending mode used when painting, not the best verified part of ctx. |
stroke parameters | ||
kw lineWidth | line_width | Sets the linewidth used for stroking specified in user units. |
kj lineJoin join | bevel | round | miter | 0 1 or 2 are also valid values |
kc lineCap cap | none | round | square | specifies how the ends of lines, including dashes are drawn when stroked. |
kl miterLimit | miter_limit | set the miter limit used for stroking (not yet implemented in rasterizer) |
d lineDash | 1-N | specify stroke dashing pattern, empty to reset |
kD lineDashOffset | phase | floating point value in same units as dashes |
texture parameters | ||
kS imageSmoothing | 0|1 | Defaults to 1, when 0 nearest neighbor resampling is used. |
ke extend extendMode | none | pad | reflect | repeat | specifies how a gradient or texture repeats |
text parameters | ||
n font | "font name" | changes the current font to the named font, do not expect more than regular mono and bold to be available. |
kf fontSize | font_size | set the font size in user units. |
kt textAlign | start | end | center | left | right | specifies horizontal alignment of text in relation to current x position. |
kb textBaseline | alphabetic | top | bottom | middle | hanging | ideographic | sets the baseline, as in the current y position relative to the text. |
The language consists of a stream of words, numbers and strings, optionally separated by white space. The characters ,=(); are also treated as white-space. Words are substrings consisting of one or more characters in the ranges a-z and A-Z. Numbers are sequences of 0-9. Numbers can be followed by the suffixes % or @, indicating that units of percent canvas width/height should be used or a global em / cell-size. ^ is equivalent to CSS vh, and ~ vw, this can be used to override the automatic choice done by %, which favors height for dimensions that are independent of x/y like lineWidth and shadowBlur. Strings are raw binary UTF8 contained in ' or " pairs. "\n\r" etc from C are available for escaping newlines, though they can also be directly included in the string. # introduces a comment and the rest of the line is ignored.
Blobs are represented as strings using a85 ~~ as quotemarks.
Internally the core datastructure of ctx is a binary variant of this protocol. Stored in chunks of 9 bytes of code + data, the various backends consume chunks of this position independent commandstream.
The binary version is less stable, and depending on configuration options ctx might use different serialization.
Below is the start of one of the fonts in ctx in this binary form, annotated with comments.
#ifndef CTX_FONT_Roboto_Regular /* glyph index: !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghi jklmnopqrstuvwxyz{|}~ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔ ÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿŁπ“”•…€™−≈␣fffiflffifflſt */ static const struct __attribute__ ((packed)) {uint8_t code; uint32_t a; uint32_t b;} ctx_font_Roboto_Regular[]={ {15, 0x0000a008, 0x000012e5},/* length:4837 CTX_SUBDIV:8 CTX_BAKE_FONT_SIZE:160 */ {'(', 0x00000008, 0x00000001},/* Roboto*/ {32, 0x6f626f52, 0x00006f74}, {')', 0x00000008, 0x00000001}, {'(', 0x0000004b, 0x00000009},/* Apache Licence, Version 2.0 Copyright 2014 Christian Robertson - Apache 2*/ {32, 0x63617041, 0x4c206568}, {'i', 0x636e6563, 0x56202c65}, {'e', 0x6f697372, 0x2e32206e}, {'0', 0x706f430a, 0x67697279}, {'h', 0x30322074, 0x43203431}, {'h', 0x74736972, 0x206e6169}, {'R', 0x7265626f, 0x6e6f7374}, {32, 0x7041202d, 0x65686361}, {32, 0x00000032, 0x00000000}, {')', 0x0000004b, 0x00000009}, {'@', 0x00000020, 0x000021dd},/* x-advance: 33.863281 */ {'@', 0x00000021, 0x00002333},/* ! x-advance: 35.199219 */ {'M', 0x41c08889, 0xc2c22223}, {'l', 0xbf5ddde0, 0x428b5556}, {'4', 0x0000ffa7, 0xfdd3fff9}, {'6', 0x00000067, 0x02d6ff96}, {'8', 0xd80ee800, 0xf02bf00e}, {'8', 0x102b001c, 0x280f100f}, {'8', 0x27f11600, 0x10d510f2}, {'8', 0xf0d500e4, 0xd9f2f0f2}, {'@', 0x00000022, 0x00002bbb},/* " x-advance: 43.730469 */ {'M', 0x41944445, 0xc2baaaab}, {'l', 0xc0000000, 0x41be6664}, {'l', 0xc0ecccce, 0x00000000}, {'4', 0xfefa0000, 0x0000004b},