FastLED 3.9.15
Loading...
Searching...
No Matches

◆ renderStrokeRow()

template<typename PixelT, bool Overwrite>
void fl::gfx::detail::renderStrokeRow ( PixelT * buf,
int w,
int py,
fl::i32 cross_start,
fl::i32 dot_start,
const StrokeCtx< PixelT > & sc )
inline

Render one scanline of a stroke line using phase-based scanning.

Templated on Overwrite for compile-time dispatch (no per-pixel branch). AA uses precomputed shift+reciprocal — zero Coord multiplies.

Definition at line 401 of file primitives.h.

403 {
404 PixelT* ptr = &buf[py * w + sc.xmin];
405 fl::i32 cross = cross_start;
406 fl::i32 dot = dot_start;
407 int px = sc.xmin;
408 const fl::i32 thr = sc.threshold_q;
409 const fl::i32 neg_thr = -thr;
410
411 // Phase 1: Skip pixels outside the band
412 while (px <= sc.xmax && (cross >= thr || cross < neg_thr)) {
413 cross += sc.dy_q;
414 dot += sc.dx_q;
415 ++ptr; ++px;
416 }
417
418 // Hoist per-row endcap ey values (py is constant within a row)
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;
422
423 // Phase 2: Process visible pixels (|cross| < threshold)
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);
427
428 if (on_segment) {
429 // Integer AA: shift+multiply (no Coord multiply)
430 fl::u16 shifted = static_cast<fl::u16>(
431 static_cast<fl::u32>(abs_cross) >> sc.aa_shift);
432 fl::u8 linear_idx = static_cast<fl::u8>((shifted * sc.aa_inv) >> 8);
433 // Fast approximate x/255 ≈ (x + 1 + (x >> 8)) >> 8
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);
436 PixelT c = sc.color;
437 c.nscale8(FL_PGM_READ_BYTE_NEAR(&distanceAA_LUT[sq_idx]));
438 if (Overwrite) *ptr = c; else *ptr += c;
439 } else if (sc.cap == LineCap::ROUND) {
440 // Endpoint distance in 8.8 fixed-point
441 fl::i32 ex8;
442 fl::i16 ey_s;
443 if (dot < 0) {
444 ex8 = (static_cast<fl::i32>(px) << 8) - sc.x0_8;
445 ey_s = static_cast<fl::i16>(ey8_y0);
446 } else {
447 ex8 = (static_cast<fl::i32>(px) << 8) - sc.x1_8;
448 ey_s = static_cast<fl::i16>(ey8_y1);
449 }
450 fl::i16 ex_s = static_cast<fl::i16>(ex8);
451 // 16×16→32 multiply (4 MUL on AVR vs ~70 for 32×32)
452 fl::i32 ed2 = static_cast<fl::i32>(ex_s) * ex_s +
453 static_cast<fl::i32>(ey_s) * ey_s;
454 if (ed2 < sc.r_max2_8) {
455 fl::u16 shifted = static_cast<fl::u16>(
456 static_cast<fl::u32>(ed2) >> sc.cap_shift);
457 fl::u8 idx = static_cast<fl::u8>((shifted * sc.cap_inv) >> 8);
458 PixelT c = sc.color;
459 c.nscale8(FL_PGM_READ_BYTE_NEAR(&distanceAA_LUT[idx]));
460 if (Overwrite) *ptr = c; else *ptr += c;
461 }
462 } else if (sc.cap == LineCap::SQUARE) {
463 if (dot >= -sc.dot_ext_q && dot <= sc.len2_q + sc.dot_ext_q) {
464 fl::u16 shifted = static_cast<fl::u16>(
465 static_cast<fl::u32>(abs_cross) >> sc.aa_shift);
466 fl::u8 linear_idx = static_cast<fl::u8>((shifted * sc.aa_inv) >> 8);
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);
469 PixelT c = sc.color;
470 c.nscale8(FL_PGM_READ_BYTE_NEAR(&distanceAA_LUT[sq_idx]));
471 if (Overwrite) *ptr = c; else *ptr += c;
472 }
473 }
474 // FLAT: pixels outside [0, len2] are silently dropped
475
476 cross += sc.dy_q;
477 dot += sc.dx_q;
478 ++ptr; ++px;
479 }
480 // Phase 3: remaining pixels are outside the band — nothing to do
481}
#define FL_PGM_READ_BYTE_NEAR(x)
Read a byte (8-bit) from PROGMEM memory.
unsigned char u8
Definition s16x16x4.h:132
const fl::u8 distanceAA_LUT[256]
Distance antialiasing lookup table Maps normalized squared distance d²/r_max² (scaled 0....

References fl::gfx::detail::StrokeCtx< PixelT >::aa_inv, fl::gfx::detail::StrokeCtx< PixelT >::aa_shift, fl::gfx::detail::StrokeCtx< PixelT >::cap, fl::gfx::detail::StrokeCtx< PixelT >::cap_inv, fl::gfx::detail::StrokeCtx< PixelT >::cap_shift, fl::gfx::detail::StrokeCtx< PixelT >::color, distanceAA_LUT, fl::gfx::detail::StrokeCtx< PixelT >::dot_ext_q, fl::gfx::detail::StrokeCtx< PixelT >::dx_q, fl::gfx::detail::StrokeCtx< PixelT >::dy_q, FL_PGM_READ_BYTE_NEAR, fl::gfx::detail::StrokeCtx< PixelT >::len2_q, fl::gfx::detail::StrokeCtx< PixelT >::r_max2_8, renderStrokeRow(), fl::gfx::detail::StrokeCtx< PixelT >::threshold_q, fl::gfx::detail::StrokeCtx< PixelT >::x0_8, fl::gfx::detail::StrokeCtx< PixelT >::x1_8, fl::gfx::detail::StrokeCtx< PixelT >::xmax, fl::gfx::detail::StrokeCtx< PixelT >::xmin, fl::gfx::detail::StrokeCtx< PixelT >::y0_8, and fl::gfx::detail::StrokeCtx< PixelT >::y1_8.

Referenced by drawStrokeLineCore(), and renderStrokeRow().

+ Here is the call graph for this function:
+ Here is the caller graph for this function: