ctx is a small footprint vector graphics protocol and rasterizer based on the HTML5 Canvas 2D context. It can drive resolution independent antialiased interfaces on microcontrollers as well as provide color and CMYK processing capabilities as well as floating point processing needed for content authoring.

It provides both a C API and an efficient text based serialization of the canvas drawing model. Almost the full W3C rendering API is covered, currently missing is ellipse(), correct handling of path instead of using bounding box in in_fill().

Render targets handled natively are 8bit sRGBA RGBA8, floating point scRGB RGBAF 8bit and floating point grayscale with alpha. GRAYA8 and GRAYAF and floating point CMYK CMYKAF. Integration points are catered for in API and protocol for color management, which will be done with babl. The formats RGB332, RGB565, RGB565_BYTESWAPPED, CMYKA8, RGB8, BGRA8, GRAY1, GRAY2, GRAY4, GRAY8 and GRAYF are handled by converting processed scanlines back and forth to one of the supported targets.

SVG and PDF output can be generated with the cairo renderer, which becomes available if cairo.h is included before ctx.h

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.

license and funding

ctx is currently developed under LGPLv3+ you can encourage continued development of ctx and dissimilar technologies by supporting its developer, if income through patreon and similar is above 4000USD per month for a year, or a similar amount in shorter time, I will relicense ctx under the ISC license.

standards

ctx uses the HTML5 2d context is the primary reference, The compositing is according to Compositing and Blending Level 2; though ctx permits any combination of compositing and blending mode. The core ideas for a 2d vector vocabulary is however shared with PDF, cairo, OpenVG, directdraw and others all with a basis in postscript.

protocol reference

This is a terse technical reference on the command entries stores internally when serializing drawing commands, it maps to the the C API if you ruen camelCase into snake_case, and prefixing each command with ctx_.

The ctx text format is a direct serialization of the API.

When a keyword 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.

graygraysets gray source, and color model
grayagray alphasets gray alpha source, and color model
rgbr g bsets rgb source
rgbar g b argb alpha color source
cmykc m y kcmyk color source
cmykac m y k acmyk alpha color source
drgbr g bsets rgb source, in device space
drgbar g b argb alpha color source, in device space
dcmykc m y kcmyk color source, in device space
dcmykac m y k acmyk alpha color source, in device space
a relArcTo5
b clip
c relCurveTo6
g save
e translatex y
f linearGradientx0 y0 x1 y1
h relHorLinex
ka globalAlphaalpha
kb textBaselinealphabetic | top | bottom | middle | hanging | ideographic
kB blendModenormal | multiply | screen | overlay | darken | lighten | colorDodge | colorBurn | hardLight | softLight | difference | exclusion | hue | saturation | color | luminosity | divide | addition | subtract
kc lineCap capnone | round | square
kC shadowColorcolor componentsworks like gradientAddStop
kf fontSizefont_size
kj lineJoin joinbevel | round | miter0 1 or 2 are also valid values
kl miterLimitmiter_limit
km compositingModesourceOver | copy | clear | sourceIn | sourceOut | sourceAtop | destinationOver | destination | destinationIn | destination_out | destinationAtop | xor
ks shadowBlurblur_radius
kt textAlignstart | end | center | left | right
kw lineWidthline_width
kx shadowOffsetXhorizontal_offset
ky shadowOffsetYvertical_offset
l relLineTox y
m relMoveTox y
n font"font name"
o radialGradient6
p gradientAddStop addStoppos R G B Aarguments depend on current color model, you can change the color model without setting a color by specifying a color without arguments.
q relQuadTocx cy x y
r rectangle rectx y width height
s relSmoothTocx cy x y
t relSmoothQuadTox y
u strokeText"utf8-string"
v relVerLiney
w glyphunichardraws 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" | kerningInterspersed with utf8 sequences of text numbers can appear that shift the horizontal position.
y identity
z closePath
A arcTox1 y1 x2 y2 radius
B arcx y radius angle1 angle2 direction
C curveTocx1 cy1 cx2 cy2 x y
E stroke
F fill
G restore
H horLineTox
J rotateradians
L lineTox y
M moveTox y
N beginPath
O scalescale_x scale_y
Q quadTocx cy x y
S smoothTocx cy x y
T smoothQuadTox y
U reset
V verLineToy
X exit
X done
Z closePath
W transforma b c d e fapplies the transformation matrix
{ startGroupstarts a compositing group
} endGroupends a compositing group, and blends and composites it with the content before the group using the now current compositing mode, blending mode and global alpha.

Detailed syntax description

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 sequenes 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 %. 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 code

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.

Files

The singleheader library ctx.
ctx.h580Kbrowse
ctx-font-ascii.h72KbrowseExample font to use with ctx, containing only ASCII
ctx-font-regular.h128KbrowseExample font to use with ctx
ctx.elf168Kcard10 l0dable
hello.c3KAnimated terminal test.
test-size.c3Kminimal example

is ctx fast yet?

This table contains fills of 1024px with various fill sources for all of ctx' supported targets, along with cairo included as a frame of reference. Small changes, like changing between inlining, forced inlinging and no inlining can lead to performance fluctuations, the numbers show that performance is decent in contrast with cairo.

Ctx implements all combinations of compositing and blending modes, but normal with source over has received the most attentions, both with optional AVX256 acceleration and not.

format color a=1.0 color a=0.75 lgrad rgrad sAtop a=1.0 sAtop a=0.75 sAtop lgrad sAtop rgrad overlay a=1.0 overlay a=0.75 overlay lgrad overlay rgrad aTop overlay..lgrad..rgrad
RGBA8 AVX2380919962811811451269292100885656103975656
RGBA8799765217167141122908711510256561171035756
cairo258511481156664456011569
GRAYA810526518454264258785418717863521881786352
GRAYAF5065067473594596737222922955552312295454
RGBAF51367121416050753717614812912652501191175048
CMYKAF5324054254379381505814514436391481483639
CMYKA8133131384412912740448180303280823033
GRAY12682405845157154554512612246431261224643

some observations on the above

To know how this compares with yet other renderers, compare with the numbers in blend2d's benchmarks..

When targeting a microcontroller that has a small enough framebuffer that it is possible to keep RGBA8 instead of RGB565, one should use RGBA8 and convert on-the-fly when copying out, for best performance.

For floating point compilers do a better job of autovectorizing code, SIMD implementations for normal + source-over does however still make sense.

optimization vs binary size

ctx is designed from the beginning to act as a GPU in software for modern micontrontrollers - powerful enough to have hundres of megahertz and possibly multiple cores. Different optimization settings of the compiler give a wide range for the performance/binary size tradeoff. But the small footprint in RAM and ability to use a shared read-only display list makes the ctx rasterizer core also useful in multi-threaded rendering.

font data size:    15291 bytes (A sans font subsetted to only ASCII)
RGBA8 rasterizer:  38869 bytes (-Os ~38kb - with many features disabled ~29kb
                                -O0  90-631kb
                                -O2  57-114kb
                                -Ofast/-O3  76-181kb, size bump due to
                                SIMD tree-vectorizer)

ctx parser:        20512 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.)

Even more agressive optimization with exponential compile time has been tested by forcing inlining and separate compilation of all combinations of blend, composite and image source modes - building ctx then takes 5-6minutes and the resulting binary is close to a megabyte, performance gains exist but are near neglible.

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.

semi-ordered roadmap / wishlist

  • reduce size and improve performance
  • Document/clean up network transparent UI protocol using ctx syntax. (test, test)
  • swap uses of cairo in GEGL with ctx
  • 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.)
  • glyph fallback
  • faster 1d-gaussian blur for shadow blur
  • gradients: use both circle centers
  • bindings to a javascript engine, for running canvas js content as apps.
  • stroking
    • reimplement as a stroke_to_path
    • miter limit (unless round joins are used, all corners are mitered now)
    • dash pattern
  • SVG/HTML parser renderer (in progress, from mrg)
  • PDF/SVG generation (can already be done through cairo integration)
  • Add API for integrating device-N with RGB/CMYK through coloritto
  • 32bit float coverage from rasterizer - right now we support 32f images; but composite them with 8bit alpha masks.
  • More SIMD code (the common RGBA8 cases for AVX2 are the only bit covered thus far, for floating point formats making the existing code more autovectorizer friendly is a priority.)

minimal example code


#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)
{
  char *utf8 = "hello\nctx\n";
  Ctx *ctx = ctx_new_for_framebuffer (
    pixels, WIDTH, HEIGHT, WIDTH*4,
    CTX_FORMAT_RGBA8);

  ctx_rgba        (ctx, 0.5, 0.5, 0.5, 1);
  ctx_rectangle   (ctx, 0, 0, 80, 24);
  ctx_fill        (ctx);
  ctx_move_to     (ctx, 10, 9);
  ctx_font_size   (ctx, 12);
  ctx_line_width  (ctx, 2);
  ctx_rgba        (ctx, 0, 0, 0, 1);
  ctx_text_stroke (ctx, utf8);
  ctx_rgba        (ctx, 1,0, 1.0, 1.0, 1.0);
  ctx_move_to     (ctx, 10, 9);
  ctx_text        (ctx, utf8);
  ctx_free (ctx);

  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");
  }

  return 0;
}
which outputs (the rightmost column of pixels is a bug):
~/src/ctx/tests$ ./test-size 
▓▓▓▓▓▓▓▓▓▓▓█▓▓▓█▓▓▓▓▓▓▓▓▓▓▓▓▓▓██▓▓▓▓▓▓▓▓▓▓▓▓▓▓█▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█
▓▓▓▓▓▓▓▓▓▓██▓▓█ █▓▓▓▓▓▓▓▓▓▓▓▓███▓▓▓▓▓▓▓▓▓▓▓▓▓███▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█
▓▓▓▓▓▓▓▓▓██ ██████████▓██▓▓▓██▓▓██▓████▓▓█████▒███▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█
▓▓▓▓▓▓▓▓▓█░  ░█ ██░░ ░██ █▓█ █   ▓█▒  ███   ▓▓   █▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█
▓▓▓▓▓▓▓▓▓██ ███ ██ ▓█░██░███░█▓▓██▓░██░██░████▒███▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█
▓▓▓▓▓▓▓▓▓▓█ █▓█ ██░██▓▓█▓▒█▒▓█▓▒██░▓██░▓█ ▓███▒██▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█
▓▓▓▓▓▓▓▓▓▓█ █▓█ ██░██▓▓██ █ ██▓▓██░▒▓▓▓███▒░▒█▒█▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█
▓▓▓▓▓▓▓▓▓▓█ ███ ██░██▓▓██▒░▒██▓▓██▒▒████████░█▒███▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█
▓▓▓▓▓▓▓▓▓▓█▒░▒█ ██░██▓▓██▓ █▓██░░██░░▒░█▓░▒░▓██░░█▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█
▓▓▓▓▓▓▓▓▓▓▓█████▓██▓▓██▓██ █▓▓████▓██████████▓████▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█▓ █▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓████▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓███▓▓▓▓▓▓▓▓▓▓▓▓▓▓███▓▓▓▓▓▓▓▓▓▓▓▓▓██▓▓▓▓▓▓▓▓▓▓▓▓▓▓█
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█ █▓▓▓▓▓▓▓▓▓▓▓▓▓██░░█▓▓▓▓▓▓▓▓▓▓▓█▒▓█▓▓▓▓▓▓▓▓▓▓▓▓▓█
▓▓▓▓▓▓▓▓▓▓▓████▓▓▓█████ ███▓▓▓████▓▓▓▓█░███████▓▓████▓█▒▓███▓▓████▓▓████
▓▓▓▓▓▓▓▓▓▓██▒▒▓███   ▓█ ▒ ░█▓█▒  ██▓▓█▒  ░█▓  ████  ▒██▒░  ███   ▓██   █
▓▓▓▓▓▓▓▓▓██░██▒██▒▓████ ▓█▒▓█▓▒██░█▓▓██░███░██░██░██▒▓█▒░██░█████░██████
▓▓▓▓▓▓▓▓▓█▓▒██▓▒█ █▓▓▓█ ███▒█░███░█▓▓▓█░██▓▓██▓▓█ ███░█▒▓██▒███▓▓░▓███░█
▓▓▓▓▓▓▓▓▓█▓▒█████ █▓▓▓█ ███▒█░█▓█░█▓▓▓█░██▒▓██▓▓█░███░█▒▓██▒▓█ ██▒▓██░▓█
▓▓▓▓▓▓▓▓▓█▓░█████░█████ ███▒█░▓██ █▓▓▓█░██▓░██░██ ██▓░█▒▒██░██░██░▓█▒▒██
▓▓▓▓▓▓▓▓▓▓█▓░▒░▓██ ▒░██ ███▒██░░ ▓█▓▓▓█░█▓█▒  ▒██▓ ░░██▒░▒░▒██░░░▒▓█ ▒▒█
▓▓▓▓▓▓▓▓▓▓▓█████▓███████▓▓██▓▓████▓▓▓▓▓██▓▓████▓▓████▓██████▓▓██████████
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█

To fetch, build, and run ctx, and an example similar to the above run the following:

mkdir foo;cd foo;wget http://ctx.graphics/{ctx.h,ctx-font-regular.h,hello.c};make hello && ./hello