ctx a 2d vector graphics protocol and rasterizer
New website http://ctx.graphics/
ctx is a vector graphics rendering protocol and renderer, for resolution independent graphics and user interfaces; from code and RAM constrained embedded/BIOS use to networked user interfaces.
compact
The whole engine including a subsetted font with a only RGBA8 rasterizer fits in less than 64kb, the raw numbers on x86_64 (i686 doesn't get much smaller either). Different optimization settings of the compiler give a wide range for the performance/binary size tradeoff.
font data size: 15291 bytes (A sans font subsetted to only ASCII) RGBA8 rasterizer: 38869 bytes (-Os ~38kb -O0 90-631kb -O2 57-114kb -Ofast/-O3 76-181kb) text data parser: 22232 bytes (not needed for direct use from C, but also on embedded this can be useful for ease of integration with other languages or directly using ctx+mictrocontroller+display as a serial display.)
The total RAM requirements are modest, with the most slim configuration <16kb of RAM is required when directly rendering using the default limits, if the maximum number of vertices in a path is reduced this can be below 10kb.
Rendering tests
A gallery of test cases; mostly written in ctx' own syntax, some use SVG/HTML as input and an in-progress port of Microraptor GUIs SVG/HTML parser.
microcontroller tests
In this video both microcontrollers, the arm-cortex-m4 at 97mhz in the card10 badge and on the TTGO ESP32 both with immediate rendering, the card10 has a 180x80 display with really high visual quality, while the TFT LCD on the ESP32 has slightly higher pixel resolution at 160x128.
january 2020, a demo running on an esp with 320x240 16bpp display, here the framebuffer is larger than the biggest single allocation we can afford and the deferred rendering is rendering the display in 4 chunks. With little geometry this is fine; with more the displays starts having very visible tearing on large content changes.
Development funding
The library is still under development in terms of performance and capabilities. ctx is possible due to my financial supporters. If you want to encourage continued development of the concepts involved in ctx – do consider becoming one.
features
- ctx renderstream a compact ASCII and binary serialization of the vector drawing API.
- Pluggable font engines.
- ttf
- through stb_truetype - optional
- ctx
- uses binary ctx renderstream as format - optional
subsetted scalable outline fonts
- optional use of stb_truetype for runtime parsing TTF/OTF fonts.
- Self contained math, does not need linking with -lm
- RGBA8
- like html5 canvas, a todo item is doing compositing in linear light temporarily in 16bit instead.
- RGB332
- 8bit RGB, 256 colors, 3bit red 3bit green 2bit blue, similar to RGB565 with much worse banding but each pixel is a single byte, gradients get dithered other colors are quantized.
- RGB565
- 16bit RGB, 65536 colors - gradients get dithered other colors are quantized.
- RGB565_BYTESWAPPED
- same as RGB565 but with the bytes swapped.
ctx syntax
ctx is also able to parse and serialize a text-based version of the renderstream.
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 1 or more characters in the ranges a-z and a-Z. Numbers are sequenes of 0-9. Numbers can be followed by the suffixes % or @, indicating that units of % canvas width/height should be used or a global em / cell-size. Strings are 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.
The parser has a list of predefined words, see table below. When a word is parsed the parser expects a series of commands of the same type, that will consume arguments until a command change. There are also short forms of the commands available, where applicable these match the SVG path data syntax thus making ctx a superset of SVG path data.
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 |
A arcTo | x1 y1 x2 y2 radius | |
B arc | x y radius angle1 angle2 direction | |
C curveTo | cx1 cy1 cx2 cy2 x y | |
E stroke | ||
F fill | ||
G restore | ||
H horLineTo | x | |
J rotate | radians | |
L lineTo | x y | |
M moveTo | x y | |
N beginPath | ||
O scale | scale_x scale_y | |
Q quadTo | cx cy x y | |
S smoothTo | cx cy x y | |
T smoothQuadTo | x y | |
U reset | ||
V verLineTo | y | |
X exit | ||
X done | ||
Z closePath | ||
W transform | a b c d e f | applies the transformation matrix |
a relArcTo | 5 | |
b clip | ||
c relCurveTo | 6 | |
g save | ||
e translate | x y | |
f linearGradient | x0 y0 x1 y1 | |
h relHorLine | x | |
l relLineTo | x y | |
m relMoveTo | x y | |
n font | "font name" | |
o radialGradient | 6 | |
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. |
q relQuadTo | cx cy x y | |
r rectangle | x y width height | |
rect | x y width height | |
s relSmoothTo | cx cy x y | |
t relSmoothQuadTo | x y | |
u strokeText | "utf8-string" | |
v relVerLine | y | |
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. |
x text | "utf8-string" | kerning | Interspersed with utf8 sequences of text numbers can appear that shift the horizontal position. |
y identity | ||
z closePath | ||
kw lineWidth | line_width | |
kj lineJoin join | bevel | round | miter | 0 1 or 2 are also valid values |
kc lineCap cap | none | round | square | |
kf fontSize | font_size | |
kt textAlign | start | end | center | left | right | |
kt textAlign | start | end | center | left | right | |
kb textBaseline | alphabetic | top | bottom | middle | hanging | ideographic | |
ka globalAlpha | alpha | |
ka compositingMode | sourceOver | copy | clear | sourceIn | sourceOut | sourceAtop | destinationOver | destination | destinationIn | destination_out | destinationAtop | xor | |
kB blendMode | normal | multiply | screen | overlay | darken | lighten | colorDodge | colorBurn | hardLight | softLight | difference | exclusion | hue | saturation | color | luminosity | divide | addition | subtract | |
ks shadowBlur | blur radius | |
kC shadowColor | color components | works like gradientAddStop |
kx shadowOffsetX | horizontal_offset | |
ky shadowOffsetY | vertical_offset |
ctx renderstream
ctx can render directly to a framebuffer or batch drawing commands in a list for later playback, in immediate-mode the rasterizer is handed commands as they are generated, and when recording to renderstream the same data is built up in a compact binary representation.
During rendering the renderstream memory is not touched by the rasterizer, this permits multiple threads to render different tiles/parts of a framebuffer without locking.
On one of the micro-controller tests this is instead done in serial, generating the output framebuffer in chunks when the microcontroller would be unable to fit a full copy in RAM.
The renderer uses by a ctx context can be overriden, for now ctx comes with backend for driving cairo - opening up for SVG and PDF output, as well as backends for generating ctx syntax to a FILE * and a backend that computes a grid of hashes, permitting improved re-rendering speed for mostly static UIs.
Downloads
It is early stages for ctx, more instructions on integration and use read the header file itself. A git repository with tests and examples will eventually appear.
Library header: ctx.h, ctx-font-regular.h font used in examples, card10 test l0dable: ctx.elf
semi-ordered roadmap
- reduce size and improve performance
- rasterizer glitches
- clipping path (currently the boundng box of specified path is used)
- point in fill/stroke, (current in_fill API uses path boundingbox)
- texture upload as part of ctx syntax
- ICC color management (provisions have been made in the API for this, but RGBtoRGB and CMYKtoRGB RGBtoCMYK conversions are currently done naively, instead of using babl.)
- compositing groups
- glyph fallback
- faster shadow blur
- gradients: use both circle centers
- stroking
- miter limit (unless round joins are used, all corners are mitered now)
- dash pattern
- reimplement as a stroke_to_path
- optional 32bit float implementation of rasterizer, continuing to produce 8bit alpha coverage.
- 32bit float covarge computation in rasterizer
- add manual SIMD (when compiled with -Ofast gcc already vectorizes quite a bit of the code, even better alignment hints/constraints could speed up the code generation for gcc on existing code).
test-size.c compiled size test-case
#include <stdint.h> #include "ctx-font-regular.h" #define CTX_IMPLEMENTATION #include "ctx.h" #define WIDTH 72 #define HEIGHT 24 uint8_t pixels[WIDTH*HEIGHT*4]; int main (int argc, char **argv) { Ctx *ctx = ctx_new_for_framebuffer ( pixels, WIDTH, HEIGHT, WIDTH*4, CTX_FORMAT_RGBA8); ctx_set_rgba (ctx, 0.5, 0.5, 0.5, 1); ctx_rectangle (ctx, 0, 0, 80, 24); ctx_fill (ctx); char *utf8 = "tinytest\necho foobaz\n"; ctx_move_to (ctx, 10, 9); ctx_set_font_size (ctx, 12); ctx_set_line_width (ctx, 2); ctx_set_rgba (ctx, 0, 0, 0, 1); ctx_text_stroke (ctx, utf8); ctx_set_rgba (ctx, 1,0, 1.0, 1.0, 1.0); ctx_move_to (ctx, 10, 9); ctx_text (ctx, utf8); static char *utf8_gray_scale[]={" ","░","▒","▓","█","█", NULL}; int no=0; for (int y= 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++, no+=4) printf ("%s", utf8_gray_scale[5-(int)CTX_CLAMP(pixels[no+1]/255.0*6.0, 0, 5)]); printf ("\n"); } ctx_free (ctx); return 0; }which outputs (the rightmost column of pixels is a bug):
~/src/ctx/tests$ ./test-size ▓▓▓▓▓▓▓▓▓▓▓█▓▓▓█▓▓▓▓▓▓▓▓▓▓▓▓▓▓██▓▓▓▓▓▓▓▓▓▓▓▓▓▓█▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█ ▓▓▓▓▓▓▓▓▓▓██▓▓█ █▓▓▓▓▓▓▓▓▓▓▓▓███▓▓▓▓▓▓▓▓▓▓▓▓▓███▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█ ▓▓▓▓▓▓▓▓▓██ ██████████▓██▓▓▓██▓▓██▓████▓▓█████▒███▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█ ▓▓▓▓▓▓▓▓▓█░ ░█ ██░░ ░██ █▓█ █ ▓█▒ ███ ▓▓ █▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█ ▓▓▓▓▓▓▓▓▓██ ███ ██ ▓█░██░███░█▓▓██▓░██░██░████▒███▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█ ▓▓▓▓▓▓▓▓▓▓█ █▓█ ██░██▓▓█▓▒█▒▓█▓▒██░▓██░▓█ ▓███▒██▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█ ▓▓▓▓▓▓▓▓▓▓█ █▓█ ██░██▓▓██ █ ██▓▓██░▒▓▓▓███▒░▒█▒█▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█ ▓▓▓▓▓▓▓▓▓▓█ ███ ██░██▓▓██▒░▒██▓▓██▒▒████████░█▒███▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█ ▓▓▓▓▓▓▓▓▓▓█▒░▒█ ██░██▓▓██▓ █▓██░░██░░▒░█▓░▒░▓██░░█▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█ ▓▓▓▓▓▓▓▓▓▓▓█████▓██▓▓██▓██ █▓▓████▓██████████▓████▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█▓ █▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓████▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓███▓▓▓▓▓▓▓▓▓▓▓▓▓▓███▓▓▓▓▓▓▓▓▓▓▓▓▓██▓▓▓▓▓▓▓▓▓▓▓▓▓▓█ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█ █▓▓▓▓▓▓▓▓▓▓▓▓▓██░░█▓▓▓▓▓▓▓▓▓▓▓█▒▓█▓▓▓▓▓▓▓▓▓▓▓▓▓█ ▓▓▓▓▓▓▓▓▓▓▓████▓▓▓█████ ███▓▓▓████▓▓▓▓█░███████▓▓████▓█▒▓███▓▓████▓▓████ ▓▓▓▓▓▓▓▓▓▓██▒▒▓███ ▓█ ▒ ░█▓█▒ ██▓▓█▒ ░█▓ ████ ▒██▒░ ███ ▓██ █ ▓▓▓▓▓▓▓▓▓██░██▒██▒▓████ ▓█▒▓█▓▒██░█▓▓██░███░██░██░██▒▓█▒░██░█████░██████ ▓▓▓▓▓▓▓▓▓█▓▒██▓▒█ █▓▓▓█ ███▒█░███░█▓▓▓█░██▓▓██▓▓█ ███░█▒▓██▒███▓▓░▓███░█ ▓▓▓▓▓▓▓▓▓█▓▒█████ █▓▓▓█ ███▒█░█▓█░█▓▓▓█░██▒▓██▓▓█░███░█▒▓██▒▓█ ██▒▓██░▓█ ▓▓▓▓▓▓▓▓▓█▓░█████░█████ ███▒█░▓██ █▓▓▓█░██▓░██░██ ██▓░█▒▒██░██░██░▓█▒▒██ ▓▓▓▓▓▓▓▓▓▓█▓░▒░▓██ ▒░██ ███▒██░░ ▓█▓▓▓█░█▓█▒ ▒██▓ ░░██▒░▒░▒██░░░▒▓█ ▒▒█ ▓▓▓▓▓▓▓▓▓▓▓█████▓███████▓▓██▓▓████▓▓▓▓▓██▓▓████▓▓████▓██████▓▓██████████ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█