A tiny, header-only “VG_Q1” vector renderer for e-paper / embedded displays.
- Converts simple SVG files into a compact, 2-color vector bytecode format (“VG_Q1”).
- Decodes VG_Q1 byte streams and renders them onto any display class with Adafruit_GFX-like primitives.
Include the headers:
#include "eink_vg.h" //for drawVG_Q1
#include "vg_assets.h" //for vg_assets[] and vg_asset_t
Generated assets are emitted as:
vg_asset_t vg_assets[](name + pointer + length), and/or- a static byte array named
/*name*/_vg
vg_asset_t is defined as: typedef struct { const char* name; const uint8_t* data; size_t len; } vg_asset_t;
Put SVG icons in assets/svg/.
During the build, CMake runs a Python tool that generates:
<build>/.../generated/vg_assets.h
In CMakeLists.txt the converter is called with:
--box 64x64
To change that, call:
idf.py -DEINK_VG_BOX_W=128 -DEINK_VG_BOX_H=296 reconfigure- The bytecode renderer is 2-color (palette0/palette1). The converter maps SVG colors to index 0/1 by luma.
- Each SVG element is rendered with a single
colorIndex(fill and stroke share the same index for that element). - Keep SVGs simple (icons/logos). Text is ignored.
template <class DisplayT>
bool drawVG_Q1(
DisplayT& d,
const uint8_t* prog,
int16_t dx, int16_t dy, int16_t dw, int16_t dh,
uint16_t palette0, uint16_t palette1
);prog: pointer to a VG_Q1 bytecode program.dx,dy,dw,dh: destination rectangle on your display.palette0 / palette1: the two colors used by the program (colorIndex 0 maps topalette0, 1 maps topalette1).- Returns
trueif the program decoded successfully,falseon invalid/unknown opcodes or malformed header.
DisplayT must provide these Adafruit_GFX-style drawing primitives:
drawLine(x0,y0,x1,y1,color)drawFastHLine(x,y,w,color)fillRect(x,y,w,h,color)anddrawRect(x,y,w,h,color)fillRoundRect(x,y,w,h,r,color)anddrawRoundRect(x,y,w,h,r,color)fillCircle(x,y,r,color)anddrawCircle(x,y,r,color)
A VG_Q1 program is a stream of opcodes:
- Starts with
OP_VIEWBOXfollowed byvbW,vbH(uint16each) - Followed by any number of drawing ops
- Ends with
OP_END
Coordinates are int16 in “viewBox units” and are scaled into the destination rectangle using
preserveAspectRatio = xMidYMid meet (uniform scale + centered).
| Opcode | Payload | Meaning |
|---|---|---|
OP_VIEWBOX |
u16 vbW, u16 vbH |
Declares the source coordinate system |
OP_STYLE |
u8 flags, u8 strokeW, u8 colorIndex |
Enables fill/stroke + stroke width + palette index |
OP_LINE |
s16 x0,y0,x1,y1 |
Draw line (stroke-only behavior) |
OP_RECT |
s16 x,y,w,h,rx |
Rect / round-rect (rx = corner radius) |
OP_CIRCLE |
s16 cx,cy,r |
Circle |
OP_POLY |
u8 n, u8 pflags, n*(s16 x,y) |
Polyline / polygon (POLY_CLOSED => polygon) |
OP_END |
– | End of stream |
STYLE_FILL(0x01): fill shapesSTYLE_STROKE(0x02): stroke outlinesstrokeW: thickness (1..255), implemented by drawing multiple offset lines.colorIndex: 0 usespalette0, 1 usespalette1.
OP_POLYfill uses a scanline filler with a fixed internal cap of 128 points.- The style carries only one
colorIndexfor both fill and stroke (per element). Use separate SVG elements if you need multiple colors. - This repo includes a build-time SVG→VG_Q1 converter and a working ESP-IDF example — see
examples/.