Polyfill

Performance test

drawing.js

While working on my physics engine I realized that I would need a 2D graphics solution to draw the wide variety of shapes my engine would support. I also noticed that as I've moved between languages and platforms I needed something portable as well. Ideally the graphics library would be simple, capable of scaling to high resolutions, and support embedded fonts. The requirement for embedded fonts is important as most font libraries are fairly large - stb_truetype.h for instance is a single-file library of 194kb.

All of these requirements have led me to create drawing.js: a polygon based library modelled after javascript canvases. Polygons, circles, and even fonts are composed of just 2 things: lines and bezier curves.

Loading javascript...

SVG Paths

Defining even simple polygons in source can be exhausting, so most SVG engines allow for a shorthand notation: M move to, L line, C curve, Z close the path. Using this notation we can draw a simple cat:

M 0 0 L 250 250 L 750 250 L 1000 0 L 1000 700 L 500 1000 L 0 700 Z M 500 683 L 394 727 L 396 732 L 500 689 L 604 732 L 606 727 Z M 190 398 C 207 487 327 512 395 450 Z M 605 450 C 673 512 793 487 810 398 Z

Left shows the control points, right shows the final result.

Characters

In addition to geometric shapes we can represent letters, numbers, and symbols as SVG paths.

At 5 lines and 22 curves, this is the most complex character in drawing.js. As expected of a real g.

Curve Segmentation

The cubic Bezier curves we use are represented as a cubic polynomials. This makes determining what pixels they overlap computationally expensive. Instead, the curves are decomposed into lines immediately before rasterization.

4 lines 16 lines

A good segmentation algorithm will create the fewest lines possible while closely approximating the curve.

Pixel Blending

Pixels on the edge of the polygon might not be entirely covered by the polygon. When this happens, we don't want to completely replace the background pixel's color. Instead we want to blend it with the polygon's color.

There are several algorithms to blend pixels on the edge of the polygon with the background - some faster than others.

AlgorithmFirefox (ms)Chrome (ms) Accurate
Naive RGB #112888true
Naive RGB #213394true
32-bit #14847true
32-bit #24838true
32-bit #34733false
imul #15336false
imul #24334true

The winning algorithm, imul #2, splits the pixel into red/blue and green/alpha bytes and uses some integer tricks:

src = polygon color lh = (src&0x00ff00ff)>>>0; hh = (src&0xff00ff00)>>>0; hh2 = hh>>>8; a = alpha in [0,256] col = img[i]; col = (((Math.imul((col&0x00ff00ff)-lh,a)>>>8)+lh)&0x00ff00ff)+ ((Math.imul(((col&0xff00ff00)>>>8)-hh2,a)+hh)&0xff00ff00);