ctx is vector graphics protocol, library and rasterizer based on the HTML5 Canvas 2D context. It can drive interactive 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.

ctx 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().

Code

The direct links below correspond to the latest commit in git.

ctx.h (browse)580KThe singleheader library ctx.
ctx-font-ascii.h (browse)72KExample font to use with ctx, containing only ASCII
ctx-font-regular.h (browse)128KExample font to use with ctx
hello.c3KAnimated terminal test.
test-size.c3Kexample

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

license and funding

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

standards

The implementation of ctx uses the HTML5 2d context as 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 other API that have foundation in postscript.

optimization vs binary size

ctx is designed from the beginning to act as a software GPU for modern microcontrollers, some of which are more powerful than the PCs in the mid 90s. 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 in practice they 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, when doing deferred rendering the additional RAM required/reserved impacts the size of the display list and thus the complexity of the vector scene.

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.

semi-ordered roadmap / wishlist

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