FastLED 3.9.15
Loading...
Searching...
No Matches
gfx Directory Reference
+ Directory dependency graph for gfx:

Directories

 detail
 
 noise
 

Files

 _build.cpp.hpp
 Unity build for fl/gfx/ — graphics types, primitives, and simulators.
 
 blur.cpp.hpp
 
 blur.h
 
 canvas.h
 Canvas types for gfx primitives (implementation)
 
 clear.h
 
 colorutils.cpp.hpp
 
 colorutils.h
 Utility functions for color fill, palettes, blending, and more.
 
 colorutils_misc.h
 
 corkscrew.cpp.hpp
 
 corkscrew.h
 Corkscrew LED strip projection and rendering.
 
 crgb.h
 Defines the 8-bit red, green, and blue (RGB) pixel type in the fl namespace.
 
 crgb16.h
 
 crgb_extra.cpp.hpp
 
 downscale.cpp.hpp
 
 downscale.h
 
 draw_mode.h
 
 draw_visitor.h
 
 eorder.h
 
 fill.cpp.hpp
 
 fill.h
 
 five_bit_hd_gamma.cpp.hpp
 
 five_bit_hd_gamma.h
 Declares functions for five-bit gamma correction.
 
 gamma_lut.cpp.hpp
 Explicit instantiation of gamma LUT templates.
 
 gamma_lut.h
 
 gfx.h
 2D antialiased graphics for LED matrices
 
 gradient.cpp.hpp
 
 gradient.h
 
 hsv.h
 Defines the hue, saturation, and value (HSV) pixel struct.
 
 hsv16.cpp.hpp
 
 hsv16.h
 
 leds.cpp.hpp
 
 leds.h
 
 pixel_iterator.h
 
 pixel_iterator_any.h
 
 primitives.h
 
 raster.h
 
 raster_sparse.cpp.hpp
 
 raster_sparse.h
 
 rectangular_draw_buffer.cpp.hpp
 
 rectangular_draw_buffer.h
 
 rgbw.cpp.hpp
 
 rgbw.h
 Functions for red, green, blue, white (RGBW) output.
 
 rgbw_colorimetric.cpp.hpp
 Heavy implementations for the colorimetric RGBW solvers (issue #2545).
 
 rgbw_colorimetric.h
 Chromaticity-aware RGBW solvers — strict sub-gamut + wx_lp_legacy white extraction + boosted overdrive + LUT + RGBCCT (issue #2545).
 
 rgbw_pixel.h
 Simple RGBW pixel data structure for encoders.
 
 rgbww.cpp.hpp
 Dispatch + implementations for the 5-channel RGB->RGBWW path (issue #2558, Phase 3 of #2545).
 
 rgbww.h
 5-channel RGB + warm-W + cool-W (RGBWW / RGBCCT) configuration types (issue #2558, Phase 3 of #2545).
 
 rgbww_pixel.h
 Raw 5-channel RGBWW pixel data structure for encoders.
 
 sample.cpp.hpp
 
 sample.h
 2D grid sampling with bilinear and nearest-neighbor interpolation
 
 splat.cpp.hpp
 
 splat.h
 
 tile2x2.cpp.hpp
 
 tile2x2.h
 
 upscale.cpp.hpp
 
 upscale.h
 
 xypath.cpp.hpp
 
 xypath.h
 
 xypath_impls.cpp.hpp
 
 xypath_impls.h
 
 xypath_renderer.cpp.hpp
 
 xypath_renderer.h
 

Detailed Description

Antialiased lines, circles, and rings on rectangular pixel buffers. One header, four functions, any pixel type.

#include "fl/gfx/gfx.h"
2D antialiased graphics for LED matrices

1. Hello Canvas

A canvas wraps a pixel buffer you already have. It doesn't allocate.

CRGB leds[256]; // your buffer
fl::CanvasRGB canvas(leds, 16, 16); // 16 wide, 16 tall
canvas.drawDisc(CRGB::Red, 8.0f, 8.0f, 4.0f); // red circle at center
fl::CRGB leds[NUM_LEDS]
Convenience alias for CRGB canvas — use fl::CanvasRGB for no-template syntax.
Definition gfx.h:105
fl::CRGB CRGB
Definition crgb.h:25
@ Red
<div style='background:#FF0000;width:4em;height:4em;'></div>
Definition crgb.h:622

That's it. The pixels are written directly into leds[].


2. Drawing Shapes

Every function takes color first, then coordinates. Coordinates can be float, int, or fixed-point — see Section 5.

drawLine — thin line

canvas.drawLine(CRGB::White, x0, y0, x1, y1);
@ White
<div style='background:#FFFFFF;width:4em;height:4em;'></div>
Definition crgb.h:646

Wu-style antialiased line. Diagonal lines get smooth subpixel blending. Axis-aligned lines with integer coords produce crisp 1px strokes.

// crisp horizontal line
canvas.drawLine(CRGB::White, 0, 8, 15, 8);
// smooth diagonal with subpixel positioning
canvas.drawLine(CRGB::White, 0.0f, 0.0f, 15.0f, 8.3f);

drawDisc — filled circle

canvas.drawDisc(color, cx, cy, radius);

Solid disc with a soft 1px antialiased edge. Pixels fully inside the radius get full color. Pixels at the boundary get partial brightness.

canvas.drawDisc(CRGB::Red, 8.0f, 8.0f, 4.5f);

drawRing — hollow circle

canvas.drawRing(color, cx, cy, innerRadius, thickness);

An annulus (donut). The band runs from innerRadius to innerRadius + thickness. Both edges are antialiased. Pixels inside innerRadius are not touched — the "hole" stays transparent.

// ring centered at (8,8), inner radius 4, 2px wide band
canvas.drawRing(CRGB::Blue, 8.0f, 8.0f, 4.0f, 2.0f);
@ Blue
<div style='background:#0000FF;width:4em;height:4em;'></div>
Definition crgb.h:512

drawStrokeLine — thick line

canvas.drawStrokeLine(color, x0, y0, x1, y1, thickness);
canvas.drawStrokeLine(color, x0, y0, x1, y1, thickness, cap);

A line with width. The thickness is the full width (a thickness of 3 means 1.5px on each side of the centerline). Edges use distance-based falloff for smooth antialiasing.

// 3px wide line, default flat caps
canvas.drawStrokeLine(CRGB::Green, 2.0f, 8.0f, 14.0f, 8.0f, 3.0f);
// same line with round end caps
canvas.drawStrokeLine(CRGB::Green, 2.0f, 8.0f, 14.0f, 8.0f, 3.0f,
fl::LineCap::ROUND);
@ Green
<div style='background:#008000;width:4em;height:4em;'></div>
Definition crgb.h:558

End cap styles:

Style What it does
FLAT Ends exactly at the endpoints (default)
ROUND Semicircle caps extending past each endpoint
SQUARE Rectangle caps extending half-thickness past each endpoint

3. Working with the Canvas

Creating

// Most common — raw array
CRGB leds[256];
fl::CanvasRGB canvas(leds, 16, 16);
// Explicit span
fl::span<CRGB> buf(leds, 256);
fl::CanvasRGB canvas(buf, 16, 16);

CanvasRGB is a convenience alias for Canvas<CRGB>. For other pixel types, use Canvas<T> directly (see Section 7).

Reading and writing pixels

canvas.at(3, 7) = CRGB::Red; // set pixel at x=3, y=7
CRGB c = canvas.at(3, 7); // read it back

Bounds checking

if (canvas.has(x, y)) { ... } // true if 0<=x<width and 0<=y<height
int n = canvas.size(); // width * height
int y
Definition simple.h:93
int x
Definition simple.h:92

Clearing between frames

Drawing is additive — each draw call adds to what's already there. To start a fresh frame, zero the buffer yourself:

memset(leds, 0, sizeof(leds)); // clear to black
canvas.drawDisc(CRGB::Red, ...); // draw on clean slate

This is intentional. Additive blending lets you layer shapes naturally:

memset(leds, 0, sizeof(leds));
canvas.drawDisc(CRGB(255, 0, 0), 6.0f, 8.0f, 3.0f); // red circle
canvas.drawDisc(CRGB(0, 0, 255), 10.0f, 8.0f, 3.0f); // blue circle
// overlap region becomes purple

4. Putting It Together

A typical animation loop:

CRGB leds[256];
fl::CanvasRGB canvas(leds, 16, 16);
void loop() {
memset(leds, 0, sizeof(leds));
float t = millis() / 1000.0f;
// bouncing disc
float cx = 8.0f + 5.0f * sin(t);
float cy = 8.0f + 5.0f * cos(t * 0.7f);
canvas.drawDisc(CRGB::Red, cx, cy, 3.0f);
// crosshair
canvas.drawLine(CRGB(0, 80, 0), cx - 4.0f, cy, cx + 4.0f, cy);
canvas.drawLine(CRGB(0, 80, 0), cx, cy - 4.0f, cx, cy + 4.0f);
// pulsing ring
float r = 2.0f + sin(t * 3.0f);
canvas.drawRing(CRGB::Blue, 8.0f, 8.0f, r, 1.5f);
FastLED.show();
}
void loop()
FL_DISABLE_WARNING_PUSH FL_DISABLE_WARNING_GLOBAL_CONSTRUCTORS CFastLED FastLED
Global LED strip management instance.
static uint32_t t
Definition Luminova.h:55

5. Coordinate Types

All draw functions are templated on coordinate type. Use whatever fits:

Type When to use Example
float General purpose, smooth animation 8.0f, 8.3f
int Pixel-aligned geometry, no AA needed 0, 8, 15, 8
fl::s16x16 No FPU, high precision (16-bit fraction) fl::s16x16(8.5f)
fl::s8x8 No FPU, minimal RAM (8-bit fraction) fl::s8x8(8.5f)
// These all draw the same circle:
// float
canvas.drawDisc(CRGB::Red, 8.0f, 8.0f, 4.5f);
// int (snaps to pixel grid — no fractional positioning)
canvas.drawDisc(CRGB::Red, 8, 8, 4);
// fixed-point
fl::s16x16 cx(8.0f), cy(8.0f), r(4.5f);
canvas.drawDisc(CRGB::Red, cx, cy, r);

All coordinates in a single call must be the same type — you can't mix float and int in one call.


6. Fixed-Point Coordinates (s16x16, s8x8)

On 8-bit MCUs (AVR, ATtiny) there is no FPU — every float operation compiles to a slow software routine. fl::s16x16 is a signed 16.16 fixed-point type that does all math with integer shifts and multiplies, giving deterministic, FPU-free rendering at full subpixel precision.

Blazing fast fixed integer drawing - Courtesy of https://www.reddit.com/user/sutaburosu/

Reddit Video https://www.reddit.com/r/FastLED/comments/1rlzrob/antialiased_subpixel_positioned_2d_graphics

image

Quick start

#include "fl/gfx/gfx.h"
using fl::s16x16;
CRGB leds[256];
fl::CanvasRGB canvas(leds, 16, 16);
s16x16 cx(8.0f), cy(8.0f), r(4.5f);
canvas.drawDisc(CRGB::Red, cx, cy, r);

All draw functions accept s16x16 coordinates anywhere you would use float. The only rule: every coordinate in a single call must be the same type — don't mix float and s16x16.

Creating values

// From a float literal (converted at construction time)
s16x16 a(3.25f);
// From an integer (no fractional part)
s16x16 b(10.0f); // use float literal, not bare int
// From raw fixed-point representation (advanced)
s16x16 c = s16x16::from_raw(0x00048000); // 4.5 in 16.16

Always use float literals (3.25f, not 3.25 or 3) when constructing s16x16. The constructor is explicit, so implicit conversions from int or double won't compile.

Arithmetic

s16x16 supports the standard operators. All intermediate math stays in fixed-point — no float conversions in the hot path.

s16x16 a(2.5f), b(1.5f);
s16x16 sum = a + b; // 4.0
s16x16 diff = a - b; // 1.0
s16x16 prod = a * b; // 3.75
s16x16 quot = a / b; // 1.666...
s16x16 neg = -a; // -2.5
bool eq = (a == b); // false
bool lt = (a < b); // false

Converting back

s16x16 v(7.25f);
int i = v.to_int(); // 7 (truncates fractional part)
float f = v.to_float(); // 7.25
i32 raw = v.raw(); // underlying 16.16 representation

Drawing with fixed-point

Every canvas draw function works identically with s16x16:

s16x16 x0(1.0f), y0(2.0f), x1(14.0f), y1(12.5f);
s16x16 cx(8.0f), cy(8.0f), r(5.0f), thick(2.0f);
canvas.drawLine(CRGB::White, x0, y0, x1, y1);
canvas.drawDisc(CRGB::Red, cx, cy, r);
canvas.drawRing(CRGB::Blue, cx, cy, r, thick);
canvas.drawStrokeLine(CRGB::Green, x0, y0, x1, y1, thick);
canvas.drawStrokeLine(CRGB::Green, x0, y0, x1, y1, thick,
fl::LineCap::ROUND);

Antialiasing, distance calculations, and bounding-box iteration all run in pure integer arithmetic internally — the graphics engine has specialized code paths for s16x16 (e.g. coordToU8, fromInt, fromFrac) that avoid any float conversion.

s8x8 — compact fixed-point

fl::s8x8 is a signed 8.8 fixed-point type stored in a 16-bit integer. It has half the range and precision of s16x16 but uses half the RAM per value — useful on the most memory-constrained targets.

#include "fl/gfx/gfx.h"
using fl::s8x8;
s8x8 cx(8.0f), cy(8.0f), r(4.5f);
canvas.drawDisc(CRGB::Red, cx, cy, r);

The API is identical to s16x16 — same constructor, same operators, same to_int() / to_float() / raw() / from_raw() accessors.

Limits: integer part is -128..127, fractional resolution is 1/256. Coordinates or radii beyond ±127 will overflow. For larger canvases, use s16x16 instead.

When to use each type

float s16x16 s8x8
Best for ARM Cortex-M4+, ESP32, desktop AVR/ATtiny, high precision AVR/ATtiny, tight RAM
Storage 4 bytes 4 bytes 2 bytes
Integer range ±3.4×10³⁸ ±32767 ±127
Fractional bits ~23 (mantissa) 16 8
Speed (no FPU) Slow (software emulation) Fast (native integer ops) Fast (native integer ops)
Speed (with FPU) Fast (hardware) Comparable or slightly slower Comparable or slightly slower
Determinism Platform-dependent rounding Bit-exact across platforms Bit-exact across platforms

Rule of thumb: if your target has a hardware FPU, use float. On FPU-less MCUs, use s16x16 for general use or s8x8 when RAM is critical and your canvas fits within ±127 pixels.


7. Custom Pixel Types

Canvas<T> works with any pixel type that satisfies this contract:

struct MyPixel {
typedef SomeType fp; // channel precision type
MyPixel& nscale8(fl::u8 scale); // dim by scale/256
MyPixel& operator+=(const MyPixel& rhs); // saturating add
// must be copyable: MyPixel c = other;
};
fl::UISlider scale("Scale", 4,.1, 4,.1)
void nscale8(CRGB *leds, fl::u16 num_leds, fl::u8 scale)
unsigned char u8
Definition stdint.h:131

What each method does:

CRGB16 — 16-bit color

CRGB16 uses u8x8 (8.8 unsigned fixed-point) channels instead of u8. That's 256x finer brightness steps per channel — useful when you need smooth fades, HDR accumulation, or antialiased compositing without banding.

#include "fl/gfx/gfx.h"
#include "fl/gfx/crgb16.h"
using fl::CRGB16;
using fl::u8x8;
CRGB16 buffer[256];
fl::Canvas<CRGB16> canvas(fl::span<CRGB16>(buffer, 256), 16, 16);
CRGB16 red(u8x8(255.0f), u8x8(0.0f), u8x8(0.0f));
canvas.drawDisc(red, 8.0f, 8.0f, 4.5f);
canvas.drawLine(red, 0.0f, 0.0f, 15.0f, 8.0f);
canvas.drawRing(red, 8.0f, 8.0f, 5.0f, 2.0f);
canvas.drawStrokeLine(red, 2.0f, 8.0f, 14.0f, 8.0f, 3.0f);
Generic canvas for any pixel type (e.g.
Definition gfx.h:51

CRGB16 also has nscale(u8x8) for full-precision scaling without truncating to 8 bits.


8. Edge Cases

All primitives are safe with degenerate inputs — no crashes, no out-of-bounds writes:

Input Behavior
Zero radius or thickness Draws nothing
Zero-length line Draws nothing
Center off-screen Clips to canvas bounds
Shape partially off-screen Only visible pixels are written
Negative coordinates Clipped — no wrap-around

9. How It Works

Buffer layout. Pixels are stored row-major: buffer[y * width + x]. This gives sequential memory access when iterating rows — optimal for cache performance on LED matrices.

Antialiasing. All primitives use distance-based antialiasing with a half-pixel soft zone:

Performance. The primitives iterate only the bounding box of each shape (not the entire canvas). On a 32x32 canvas, 100 draw calls complete in under 100ms — roughly 10,000 calls/sec in release mode.


Files

File What's in it
gfx.h Main include — brings in everything below
canvas.h Canvas<T> struct and method declarations
primitives.h Drawing algorithm implementations
crgb16.h CRGB16 pixel type with u8x8 channels
detail/ Internal helpers (distance LUT, integer math)