25 return (
fl::u8)
fl::clamp((alpha * T(255.0f) + T(0.5f)).to_int(), 0, 255);
51 return (
fl::u8)
fl::clamp((alpha * c255 + half).to_int(), (fl::i32)0, (fl::i32)255);
57inline T
fromInt(
int n) {
return T(
static_cast<float>(n)); }
67inline T
fromFrac(
int p,
int q) {
return T(
static_cast<float>(p) /
static_cast<float>(q)); }
103 return static_cast<int>(val);
115 return static_cast<int>(val);
121template<
typename PixelT,
bool Overwrite>
123 int x,
int y,
const PixelT& color) {
128 pixels[
y *
width +
x] += color;
139template<
typename Coord,
typename =
void>
143template<
typename Coord>
149template<
typename Coord>
151 return num * inv_denom;
155template<
typename Coord>
165template<
typename Coord>
166inline Coord
aaRatio(Coord num, Coord denom, Coord inv_denom) {
179template<
typename Coord>
181 return static_cast<fl::i32
>(val.to_float() * 256.0f);
186 return static_cast<fl::i32
>(val * 256.0f);
191 return static_cast<fl::i32
>(val * 256.0);
196 return static_cast<fl::i32
>(
static_cast<fl::u32
>(val) << 8);
202 return static_cast<fl::i32
>(val.
raw() >> 8);
211 fl::u32 tmp =
static_cast<fl::u32
>(band);
212 while (tmp > 255u) { tmp >>= 1; ++sh; }
216 inv_out =
static_cast<fl::u16
>((65280u + (scaled >> 1)) / scaled);
222template<
typename Coord>
224 return static_cast<fl::i32
>(val.to_float() * 65536.0f);
229 return static_cast<fl::i32
>(val * 65536.0f);
234 return static_cast<fl::i32
>(val * 65536.0);
239 return static_cast<fl::i32
>(val) << 16;
251template<
typename PixelT>
264template<
typename PixelT>
277template<
typename PixelT,
bool Overwrite>
281 PixelT* ptr = &buf[py * w + f.
xmin];
286 while (px <= f.xmax && d2 >= f.
rout2) {
287 d2 += xd; xd += 131072; ++ptr; ++px;
290 while (px <= f.xmax && d2 >= f.
rin2 && d2 < f.
rout2) {
291 fl::u16 diff =
static_cast<fl::u16
>(
static_cast<fl::u32
>(f.
rout2 - d2) >> f.
band_shift);
293 PixelT c = f.
color; c.nscale8(br);
294 if (Overwrite) *ptr = c;
else *ptr += c;
295 d2 += xd; xd += 131072; ++ptr; ++px;
298 while (px <= f.
xmax && d2 < f.
rin2) {
299 if (Overwrite) *ptr = f.
color;
else *ptr += f.
color;
300 d2 += xd; xd += 131072; ++ptr; ++px;
304 fl::u16 diff =
static_cast<fl::u16
>(
static_cast<fl::u32
>(f.
rout2 - d2) >> f.
band_shift);
306 PixelT c = f.
color; c.nscale8(br);
307 if (Overwrite) *ptr = c;
else *ptr += c;
308 d2 += xd; xd += 131072; ++ptr; ++px;
316template<
typename PixelT,
bool Overwrite>
320 PixelT* ptr = &buf[py * w + g.
xmin];
325 while (px <= g.xmax && d2 >= g.
oo2) {
326 d2 += xd; xd += 131072; ++ptr; ++px;
329 while (px <= g.xmax && d2 >= g.
oi2 && d2 < g.
oo2) {
330 fl::u16 diff =
static_cast<fl::u16
>(
static_cast<fl::u32
>(g.
oo2 - d2) >> g.
outer_shift);
332 PixelT c = g.
color; c.nscale8(br);
333 if (Overwrite) *ptr = c;
else *ptr += c;
334 d2 += xd; xd += 131072; ++ptr; ++px;
337 while (px <= g.xmax && d2 >= g.
io2 && d2 < g.
oi2) {
338 if (Overwrite) *ptr = g.
color;
else *ptr += g.
color;
339 d2 += xd; xd += 131072; ++ptr; ++px;
342 while (px <= g.xmax && d2 >= g.
ii2 && d2 < g.
io2) {
343 fl::u16 diff =
static_cast<fl::u16
>(
static_cast<fl::u32
>(d2 - g.
ii2) >> g.
inner_shift);
345 PixelT c = g.
color; c.nscale8(br);
346 if (Overwrite) *ptr = c;
else *ptr += c;
347 d2 += xd; xd += 131072; ++ptr; ++px;
350 while (px <= g.
xmax && d2 < g.
ii2) {
351 d2 += xd; xd += 131072; ++ptr; ++px;
354 while (px <= g.xmax && d2 >= g.
ii2 && d2 < g.
io2) {
355 fl::u16 diff =
static_cast<fl::u16
>(
static_cast<fl::u32
>(d2 - g.
ii2) >> g.
inner_shift);
357 PixelT c = g.
color; c.nscale8(br);
358 if (Overwrite) *ptr = c;
else *ptr += c;
359 d2 += xd; xd += 131072; ++ptr; ++px;
362 while (px <= g.xmax && d2 >= g.
io2 && d2 < g.
oi2) {
363 if (Overwrite) *ptr = g.
color;
else *ptr += g.
color;
364 d2 += xd; xd += 131072; ++ptr; ++px;
367 while (px <= g.xmax && d2 >= g.
oi2 && d2 < g.
oo2) {
368 fl::u16 diff =
static_cast<fl::u16
>(
static_cast<fl::u32
>(g.
oo2 - d2) >> g.
outer_shift);
370 PixelT c = g.
color; c.nscale8(br);
371 if (Overwrite) *ptr = c;
else *ptr += c;
372 d2 += xd; xd += 131072; ++ptr; ++px;
380template<
typename PixelT>
400template<
typename PixelT,
bool Overwrite>
402 fl::i32 cross_start, fl::i32 dot_start,
404 PixelT* ptr = &buf[py * w + sc.
xmin];
405 fl::i32 cross = cross_start;
406 fl::i32 dot = dot_start;
409 const fl::i32 neg_thr = -thr;
412 while (px <= sc.
xmax && (cross >= thr || cross < neg_thr)) {
419 fl::i32 py8 =
static_cast<fl::i32
>(py) << 8;
420 fl::i32 ey8_y0 = py8 - sc.
y0_8;
421 fl::i32 ey8_y1 = py8 - sc.
y1_8;
424 while (px <= sc.
xmax && cross < thr && cross >= neg_thr) {
425 fl::i32 abs_cross = (cross < 0) ? -cross : cross;
426 bool on_segment = (dot >= 0 && dot <= sc.
len2_q);
430 fl::u16 shifted =
static_cast<fl::u16
>(
431 static_cast<fl::u32
>(abs_cross) >> sc.
aa_shift);
434 fl::u16 sq =
static_cast<fl::u16
>(linear_idx) * linear_idx;
435 fl::u8 sq_idx =
static_cast<fl::u8>((sq + 1 + (sq >> 8)) >> 8);
438 if (Overwrite) *ptr = c;
else *ptr += c;
439 }
else if (sc.
cap == LineCap::ROUND) {
444 ex8 = (
static_cast<fl::i32
>(px) << 8) - sc.
x0_8;
445 ey_s =
static_cast<fl::i16
>(ey8_y0);
447 ex8 = (
static_cast<fl::i32
>(px) << 8) - sc.
x1_8;
448 ey_s =
static_cast<fl::i16
>(ey8_y1);
450 fl::i16 ex_s =
static_cast<fl::i16
>(ex8);
452 fl::i32 ed2 =
static_cast<fl::i32
>(ex_s) * ex_s +
453 static_cast<fl::i32
>(ey_s) * ey_s;
455 fl::u16 shifted =
static_cast<fl::u16
>(
456 static_cast<fl::u32
>(ed2) >> sc.
cap_shift);
460 if (Overwrite) *ptr = c;
else *ptr += c;
462 }
else if (sc.
cap == LineCap::SQUARE) {
464 fl::u16 shifted =
static_cast<fl::u16
>(
465 static_cast<fl::u32
>(abs_cross) >> sc.
aa_shift);
467 fl::u16 sq =
static_cast<fl::u16
>(linear_idx) * linear_idx;
468 fl::u8 sq_idx =
static_cast<fl::u8>((sq + 1 + (sq >> 8)) >> 8);
471 if (Overwrite) *ptr = c;
else *ptr += c;
495template<
typename PixelT,
typename Coord>
497 Coord x0, Coord y0, Coord x1, Coord y1,
500template<
typename PixelT,
typename Coord>
502 Coord cx, Coord cy, Coord r,
505template<
typename PixelT,
typename Coord>
507 Coord cx, Coord cy, Coord r, Coord thickness,
510template<
typename PixelT,
typename Coord>
512 Coord x0, Coord y0, Coord x1, Coord y1, Coord thickness,
529template<
typename PixelT,
typename Coord,
bool Overwrite>
531 Coord x0, Coord y0, Coord x1, Coord y1) {
532 PixelT* pixels = canvas.
pixels;
546 fl::i32 dx8 = fx1 - fx0;
547 fl::i32 dy8 = fy1 - fy0;
550 fl::i32 gradient = (dx8 == 0) ? 0 : ((dy8 << 8) / dx8);
553 fl::i32 xend = (fx0 + 128) & ~0xFF;
554 fl::i32 yend = fy0 + ((gradient * (xend - fx0)) >> 8);
555 fl::i32 xgap = 256 - ((fx0 + 128) & 0xFF);
556 int xpxl1 = xend >> 8;
557 int ypxl1 = yend >> 8;
561 fl::i32 xend2 = (fx1 + 128) & ~0xFF;
562 fl::i32 yend2 = fy1 + ((gradient * (xend2 - fx1)) >> 8);
563 fl::i32 xgap2 = (fx1 + 128) & 0xFF;
564 int xpxl2 = xend2 >> 8;
565 int ypxl2 = yend2 >> 8;
570 fl::u8 b1 =
static_cast<fl::u8>(((255 - yfrac1) * xgap) >> 8);
573 PixelT c = color; c.nscale8(b1);
575 c = color; c.nscale8(b2);
578 PixelT c = color; c.nscale8(b1);
580 c = color; c.nscale8(b2);
587 fl::u8 b1 =
static_cast<fl::u8>(((255 - yfrac2) * xgap2) >> 8);
590 PixelT c = color; c.nscale8(b1);
592 c = color; c.nscale8(b2);
595 PixelT c = color; c.nscale8(b1);
597 c = color; c.nscale8(b2);
603 fl::i32 intery = yend + gradient;
605 for (
int x = xpxl1 + 1;
x < xpxl2; ++
x) {
608 PixelT c = color; c.nscale8(255 - frac);
610 c = color; c.nscale8(frac);
615 for (
int x = xpxl1 + 1;
x < xpxl2; ++
x) {
618 PixelT c = color; c.nscale8(255 - frac);
620 c = color; c.nscale8(frac);
627template<
typename PixelT,
typename Coord,
bool Overwrite>
629 Coord cx, Coord cy, Coord r);
631template<
typename PixelT,
typename Coord,
bool Overwrite>
633 Coord cx, Coord cy, Coord r, Coord thickness);
635template<
typename PixelT,
typename Coord,
bool Overwrite>
637 Coord x0, Coord y0, Coord x1, Coord y1,
642template<
typename PixelT,
typename Coord>
644 Coord x0, Coord y0, Coord x1, Coord y1,
655template<
typename PixelT,
typename Coord,
bool Overwrite>
657 Coord cx, Coord cy, Coord r) {
658 PixelT* pixels = canvas.
pixels;
666 fl::i32 rin8 = r8 - 128;
667 fl::i32 rout8 = r8 + 128;
669 fl::i32 rin2 = rin8 * rin8;
670 fl::i32 rout2 = rout8 * rout8;
671 fl::i32 band = rout2 - rin2;
672 if (band <= 0)
return;
674 int ri = (rout8 >> 8) + 1;
678 int xmin = cxi - ri;
if (xmin < 0) xmin = 0;
679 int xmax = cxi + ri;
if (xmax >=
width) xmax =
width - 1;
680 if (xmin > xmax)
return;
681 if (cyi + ri < 0 || cyi - ri >=
height)
return;
683 fl::i32 dx8 = (
static_cast<fl::i32
>(xmin) << 8) - cx8;
684 fl::i32 dx2 = dx8 * dx8;
687 fc.
xdelta0 = 512 * dx8 + 65536;
693 fl::i32 cyfrac = (
static_cast<fl::i32
>(cyi) << 8) - cy8;
694 fl::i32 d2c = dx2 + cyfrac * cyfrac;
696 if (cyi >= 0 && cyi <
height)
699 fl::i32 botd2 = d2c, botdelta = 512 * cyfrac + 65536;
700 fl::i32 topd2 = d2c, topdelta = -512 * cyfrac + 65536;
701 botd2 += botdelta; botdelta += 131072;
702 topd2 += topdelta; topdelta += 131072;
704 for (
int dy = 1; dy <= ri; ++dy) {
705 bool cbot = (botd2 - dx2 <= rout2);
706 bool ctop = (topd2 - dx2 <= rout2);
707 if (!cbot && !ctop)
break;
708 int pyb = cyi + dy, pyt = cyi - dy;
709 if (cbot && pyb >= 0 && pyb <
height)
711 if (ctop && pyt >= 0 && pyt <
height)
713 botd2 += botdelta; botdelta += 131072;
714 topd2 += topdelta; topdelta += 131072;
718template<
typename PixelT,
typename Coord>
720 Coord cx, Coord cy, Coord r,
731template<
typename PixelT,
typename Coord,
bool Overwrite>
733 Coord cx, Coord cy, Coord r, Coord thickness) {
734 PixelT* pixels = canvas.
pixels;
743 fl::i32 r_ii8 = r8 - 128;
744 fl::i32 r_io8 = r8 + 128;
745 fl::i32 r_oi8 = r8 + t8 - 128;
746 fl::i32 r_oo8 = r8 + t8 + 128;
748 if (r_ii8 < 0) r_ii8 = 0;
749 if (r_io8 < 0) r_io8 = 0;
751 fl::i32 ii2 = r_ii8 * r_ii8;
752 fl::i32 io2 = r_io8 * r_io8;
753 fl::i32 oi2 = r_oi8 * r_oi8;
754 fl::i32 oo2 = r_oo8 * r_oo8;
756 fl::i32 inner_band = io2 - ii2;
757 fl::i32 outer_band = oo2 - oi2;
759 int ri = (r_oo8 >> 8) + 1;
763 int xmin = cxi - ri;
if (xmin < 0) xmin = 0;
764 int xmax = cxi + ri;
if (xmax >=
width) xmax =
width - 1;
765 if (xmin > xmax)
return;
766 if (cyi + ri < 0 || cyi - ri >=
height)
return;
768 fl::i32 dx8 = (
static_cast<fl::i32
>(xmin) << 8) - cx8;
769 fl::i32 dx2 = dx8 * dx8;
772 gc.
xdelta0 = 512 * dx8 + 65536;
781 fl::i32 cyfrac = (
static_cast<fl::i32
>(cyi) << 8) - cy8;
782 fl::i32 d2c = dx2 + cyfrac * cyfrac;
784 if (cyi >= 0 && cyi <
height)
787 fl::i32 botd2 = d2c, botdelta = 512 * cyfrac + 65536;
788 fl::i32 topd2 = d2c, topdelta = -512 * cyfrac + 65536;
789 botd2 += botdelta; botdelta += 131072;
790 topd2 += topdelta; topdelta += 131072;
792 for (
int dy = 1; dy <= ri; ++dy) {
793 bool cbot = (botd2 - dx2 <= oo2);
794 bool ctop = (topd2 - dx2 <= oo2);
795 if (!cbot && !ctop)
break;
796 int pyb = cyi + dy, pyt = cyi - dy;
797 if (cbot && pyb >= 0 && pyb <
height)
799 if (ctop && pyt >= 0 && pyt <
height)
801 botd2 += botdelta; botdelta += 131072;
802 topd2 += topdelta; topdelta += 131072;
806template<
typename PixelT,
typename Coord>
808 Coord cx, Coord cy, Coord r, Coord thickness,
819template<
typename PixelT,
typename Coord,
bool Overwrite>
821 Coord x0, Coord y0, Coord x1, Coord y1,
822 Coord thickness,
LineCap cap) {
823 PixelT* pixels = canvas.
pixels;
829 fl::i32 dx_8 = x1_8 - x0_8;
830 fl::i32 dy_8 = y1_8 - y0_8;
831 if (dx_8 == 0 && dy_8 == 0)
return;
833 fl::i32 len2_16 = dx_8 * dx_8 + dy_8 * dy_8;
834 fl::i32 len_8 =
static_cast<fl::i32
>(
837 fl::i32 r_max_8 = thickness_8 >> 1;
838 fl::i32 threshold_q = r_max_8 * len_8;
840 if (threshold_q <= 0)
return;
842 int x0i = x0_8 >> 8, y0i = y0_8 >> 8;
843 int x1i = x1_8 >> 8, y1i = y1_8 >> 8;
844 int margin = ((r_max_8 + 255) >> 8) + 2;
845 int xmin = (x0i < x1i ? x0i : x1i) - margin;
846 int xmax = (x0i > x1i ? x0i : x1i) + margin;
847 int ymin = (y0i < y1i ? y0i : y1i) - margin;
848 int ymax = (y0i > y1i ? y0i : y1i) + margin;
850 if (xmin < 0) xmin = 0;
852 if (ymin < 0) ymin = 0;
854 if (xmin > xmax || ymin > ymax)
return;
856 fl::i32 dx_q = dx_8 << 8;
857 fl::i32 dy_q = dy_8 << 8;
859 fl::i32 rx_base_8 = (
static_cast<fl::i32
>(xmin) << 8) - x0_8;
860 fl::i32 ry_row_8 = (
static_cast<fl::i32
>(ymin) << 8) - y0_8;
861 fl::i32 cross_row_q = rx_base_8 * dy_8 - ry_row_8 * dx_8;
862 fl::i32 dot_row_q = rx_base_8 * dx_8 + ry_row_8 * dy_8;
864 fl::i32 len2_q = len_8 * len_8;
865 fl::i32 dot_ext_q = (cap == LineCap::SQUARE) ? threshold_q : 0;
878 if (cap == LineCap::ROUND && sc.
r_max2_8 > 0) {
885 for (
int py = ymin; py <= ymax; ++py) {
886 if (py >= 0 && py <
height) {
888 cross_row_q, dot_row_q, sc);
890 cross_row_q -= sc.
dx_q;
891 dot_row_q += sc.
dy_q;
895template<
typename PixelT,
typename Coord>
897 Coord x0, Coord y0, Coord x1, Coord y1, Coord thickness,
Canvas types for gfx primitives (implementation)
Generic canvas for any pixel type (e.g.
static constexpr i32 SCALE
static constexpr FASTLED_FORCE_INLINE s16x16 from_raw(i32 raw) FL_NOEXCEPT
constexpr i32 raw() const FL_NOEXCEPT
#define FL_PGM_READ_BYTE_NEAR(x)
Read a byte (8-bit) from PROGMEM memory.
Legacy compatibility header for 8-bit scaling functions.
Compile-time linker keep-alive hook for a single fl::Bus.
integral_constant< bool, B > bool_constant
void swap(T &a, T &b) FL_NOEXCEPT
integral_constant< bool, true > true_type
integral_constant< bool, false > false_type
void drawStrokeLineCore(Canvas< PixelT > &canvas, const PixelT &color, Coord x0, Coord y0, Coord x1, Coord y1, Coord thickness, LineCap cap)
Coord aaRatioDispatch(Coord num, Coord, Coord inv_denom, fl::false_type)
void drawLineCore(Canvas< PixelT > &canvas, const PixelT &color, Coord x0, Coord y0, Coord x1, Coord y1)
void renderStrokeRow(PixelT *buf, int w, int py, fl::i32 cross_start, fl::i32 dot_start, const StrokeCtx< PixelT > &sc)
Render one scanline of a stroke line using phase-based scanning.
void renderDiscRow(PixelT *buf, int w, int py, fl::i32 d2_row, const DiscCtx< PixelT > &f)
Render one scanline of a disc using incremental d².
void drawDiscCore(Canvas< PixelT > &canvas, const PixelT &color, Coord cx, Coord cy, Coord r)
fl::i32 toFixed8(Coord val)
Convert any Coord type to 8.8 fixed-point (fl::i32).
void renderRingRow(PixelT *buf, int w, int py, fl::i32 d2_row, const RingCtx< PixelT > &g)
Render one scanline of a ring using incremental d² with phase-based scanning.
void drawRingCore(Canvas< PixelT > &canvas, const PixelT &color, Coord cx, Coord cy, Coord r, Coord thickness)
void computeBandShift(fl::i32 band, fl::u8 &shift_out, fl::u16 &inv_out)
Precompute right-shift and reciprocal multiplier for AA division.
fl::i32 toQ16(Coord val)
Convert any Coord type to Q16.16 raw i32 for integer inner loops.
const fl::u8 distanceAA_LUT[256]
Distance antialiasing lookup table Maps normalized squared distance d²/r_max² (scaled 0....
============================================================================
Stroke line context: bundles per-line constants into a struct passed by reference.
Ring context: bundles all per-circle constants into a struct passed by reference.
Disc context: bundles per-circle constants into a struct passed by reference.
void drawLine(Canvas< PixelT > &canvas, const PixelT &color, Coord x0, Coord y0, Coord x1, Coord y1, fl::DrawMode mode=fl::DrawMode::DRAW_MODE_BLEND) FL_NOEXCEPT
============================================================================
Coord aaRatio(Coord num, Coord denom, Coord inv_denom)
Compute num/denom using the best strategy for the Coord type:
LineCap
Line cap styles for stroke operations.
fl::u8 coordToU8< float >(float alpha)
int toInt(const T &val)
Helper to convert any coordinate type to int Supports: s16x16 (via to_int()), float,...
int toInt< double >(const double &val)
float halfOf< float >(float val)
T fromInt(int n)
Convert an integer to Coord without float intermediate.
fl::s16x16 halfOf< fl::s16x16 >(fl::s16x16 val)
T halfOf(T val)
Divide Coord by 2 using shift (avoids expensive division on embedded).
int halfOf< int >(int val)
int toInt< int >(const int &val)
void addPixelToBuffer(PixelT *pixels, int width, int height, int x, int y, const PixelT &color)
Internal helper: add or set pixel to rectangular buffer with bounds checking Direct row-major indexin...
void drawDisc(Canvas< PixelT > &canvas, const PixelT &color, Coord cx, Coord cy, Coord r, fl::DrawMode mode=fl::DrawMode::DRAW_MODE_BLEND) FL_NOEXCEPT
fl::s16x16 fromFrac< fl::s16x16 >(int p, int q)
fl::u8 coordToU8< int >(int alpha)
void drawRing(Canvas< PixelT > &canvas, const PixelT &color, Coord cx, Coord cy, Coord r, Coord thickness, fl::DrawMode mode=fl::DrawMode::DRAW_MODE_BLEND) FL_NOEXCEPT
void drawStrokeLine(Canvas< PixelT > &canvas, const PixelT &color, Coord x0, Coord y0, Coord x1, Coord y1, Coord thickness, LineCap cap, fl::DrawMode mode=fl::DrawMode::DRAW_MODE_BLEND) FL_NOEXCEPT
fl::u8 coordToU8< fl::s16x16 >(fl::s16x16 alpha)
fl::u8 coordToU8< double >(double alpha)
fl::s16x16 fromInt< fl::s16x16 >(int n)
int toInt< float >(const float &val)
double halfOf< double >(double val)
T fromFrac(int p, int q)
Convert rational p/q to Coord without float.
fl::u8 coordToU8(T alpha)
sqrt is provided by fl::sqrt overloads in math.h for:
gfx::LineCap LineCap
Line cap style.
enable_if<!is_integral< T >::value, T >::type round(T value) FL_NOEXCEPT
FL_OPTIMIZE_FUNCTION constexpr u16 isqrt32(u32 x) FL_NOEXCEPT
constexpr enable_if< is_fixed_point< T >::value, T >::type abs(T x) FL_NOEXCEPT
constexpr enable_if< is_fixed_point< T >::value, T >::type clamp(T x, T lo, T hi) FL_NOEXCEPT
Base definition for an LED controller.
Simple rectangular canvas for graphics operations Combines a pixel buffer with dimensions for cache-o...