FastLED 3.10.8
Loading...
Searching...
No Matches
render_value_fp.h
Go to the documentation of this file.
1#pragma once
2
3// Generic fixed-point render_value_fp() — mirrors float render_value() in engine_core.h.
4// Converts polar coordinates to cartesian, applies Perlin noise, maps to [0, 255].
5// Uses sincos32() for trig, pnoise2d_raw() for noise.
6//
7// This is the core inner loop for all FP visualizer conversions.
8
12#include "fl/math/sin32.h"
13#include "fl/stl/noexcept.h"
14
15namespace fl {
16
17// Parameters for render_value_fp, all as s16x16 raw values.
18// Mirrors render_parameters from core_types.h.
20 fl::i32 angle_raw; // polar angle in radians (s16x16 raw)
21 fl::i32 dist_raw; // distance from center (s16x16 raw)
22 fl::i32 scale_x_raw; // x scale factor (s16x16 raw)
23 fl::i32 scale_y_raw; // y scale factor (s16x16 raw)
24 fl::i32 scale_z_raw; // z scale factor (s16x16 raw)
25 fl::i32 offset_x_raw; // x offset (s16x16 raw)
26 fl::i32 offset_y_raw; // y offset (s16x16 raw)
27 fl::i32 offset_z_raw; // z offset (s16x16 raw)
28 fl::i32 z_raw; // z value (s16x16 raw)
29 fl::i32 center_x_raw; // center x (s16x16 raw)
30 fl::i32 center_y_raw; // center y (s16x16 raw)
31 fl::i32 low_limit_raw; // noise low limit: 0 or -FP_ONE (s16x16 raw)
32 fl::i32 high_limit_raw; // noise high limit: FP_ONE (s16x16 raw)
33};
34
35// Convert s16x16 radians to A24 angle format for sincos32.
36// A24: 0 to 16777216 is a full circle (2*PI radians).
37// Conversion: angle_a24 = angle_rad * (16777216 / (2*PI))
38// = angle_rad * 2670177 (approximately)
39FASTLED_FORCE_INLINE fl::u32 radiansToA24_fp(fl::i32 angle_s16x16_raw) FL_NO_EXCEPT {
40 constexpr fl::i32 RAD_TO_A24 = 2670177; // 16777216 / (2*PI) in s16x16 sense
41 return static_cast<fl::u32>(
42 (static_cast<fl::i64>(angle_s16x16_raw) * RAD_TO_A24) >> fl::s16x16::FRAC_BITS);
43}
44
45// Fixed-point render_value: same algorithm as float render_value() in engine_core.h.
46// Returns clamped noise value in [0, 255] range.
47//
48// Algorithm:
49// newx = (offset_x + center_x - cos(angle) * dist) * scale_x
50// newy = (offset_y + center_y - sin(angle) * dist) * scale_y
51// noise = pnoise2d(newx, newy)
52// clamp noise to [low_limit, high_limit]
53// map to [0, 255]
55 const render_parameters_fp &p,
56 const fl::i32 *fade_lut,
57 const fl::u8 *perm) FL_NO_EXCEPT {
58
59 using FP = fl::s16x16;
60 constexpr fl::i32 FP_ONE = static_cast<fl::i32>(1) << FP::FRAC_BITS;
61
62 // sincos32 for angle
63 fl::u32 a24 = radiansToA24_fp(p.angle_raw);
64 SinCos32 sc = sincos32(a24);
65
66 // sincos32 output is in [-2147418112, 2147418112] (Q0.31 range).
67 // To multiply with s16x16 dist, we use:
68 // result_s16x16 = (sc_val * dist_raw) >> 31
69 // This gives us the product in s16x16 format.
70 fl::i32 cos_dist = static_cast<fl::i32>(
71 (static_cast<fl::i64>(sc.cos_val) * p.dist_raw) >> 31);
72 fl::i32 sin_dist = static_cast<fl::i32>(
73 (static_cast<fl::i64>(sc.sin_val) * p.dist_raw) >> 31);
74
75 // newx = (offset_x + center_x - cos*dist) * scale_x
76 // newy = (offset_y + center_y - sin*dist) * scale_y
77 fl::i32 pre_x = p.offset_x_raw + p.center_x_raw - cos_dist;
78 fl::i32 pre_y = p.offset_y_raw + p.center_y_raw - sin_dist;
79
80 fl::i32 nx = static_cast<fl::i32>(
81 (static_cast<fl::i64>(pre_x) * p.scale_x_raw) >> FP::FRAC_BITS);
82 fl::i32 ny = static_cast<fl::i32>(
83 (static_cast<fl::i64>(pre_y) * p.scale_y_raw) >> FP::FRAC_BITS);
84
85 // Compute z coordinate: newz = (offset_z + z) * scale_z
86 fl::i32 nz = static_cast<fl::i32>(
87 (static_cast<fl::i64>(p.offset_z_raw + p.z_raw) * p.scale_z_raw) >> FP::FRAC_BITS);
88
89 // Perlin noise: 2D when z==0, 3D otherwise
90 fl::i32 raw_noise;
91 if (nz == 0) {
92 raw_noise = perlin_s16x16::pnoise2d_raw(nx, ny, fade_lut, perm);
93 } else {
94 raw_noise = perlin_s16x16::pnoise3d_raw(nx, ny, nz, fade_lut, perm);
95 }
96
97 // Clamp to [low_limit, high_limit]
98 if (raw_noise < p.low_limit_raw) raw_noise = p.low_limit_raw;
99 if (raw_noise > p.high_limit_raw) raw_noise = p.high_limit_raw;
100
101 // Map from [low_limit, high_limit] to [0, 255]
102 // map_float(x, low, high, 0, 255) = (x - low) * 255 / (high - low)
103 fl::i32 range = p.high_limit_raw - p.low_limit_raw;
104 fl::i32 shifted = raw_noise - p.low_limit_raw;
105
106 // shifted is in [0, range], we want (shifted * 255) / range
107 // For the common case where low=0, high=1: range = FP_ONE
108 // result = (shifted * 255) >> FRAC_BITS (since shifted is in [0, FP_ONE])
109 // For low=-1, high=1: range = 2*FP_ONE
110 // result = (shifted * 255) / (2*FP_ONE)
111 fl::i32 result;
112 if (range == FP_ONE) {
113 // Optimized path: low_limit=0, high_limit=1
114 result = static_cast<fl::i32>(
115 (static_cast<fl::i64>(shifted) * 255) >> FP::FRAC_BITS);
116 } else if (range == 2 * FP_ONE) {
117 // Optimized path: low_limit=-1, high_limit=1
118 result = static_cast<fl::i32>(
119 (static_cast<fl::i64>(shifted) * 255) >> (FP::FRAC_BITS + 1));
120 } else {
121 // General path
122 result = static_cast<fl::i32>(
123 (static_cast<fl::i64>(shifted) * 255) / range);
124 }
125
126 // Final clamp to [0, 255]
127 if (result < 0) result = 0;
128 if (result > 255) result = 255;
129
130 return result;
131}
132
133// Color blend functions operating on [0, 255] integer values.
134// These mirror the float versions in engine_core.h.
135
136FASTLED_FORCE_INLINE fl::i32 multiply_fp(fl::i32 a, fl::i32 b) FL_NO_EXCEPT {
137 return (a * b) / 255;
138}
139
140FASTLED_FORCE_INLINE fl::i32 screen_fp(fl::i32 a, fl::i32 b) FL_NO_EXCEPT {
141 // screen(a,b) = 1 - (1-a/255)*(1-b/255) * 255
142 // = 255 - (255-a)*(255-b)/255
143 return 255 - ((255 - a) * (255 - b)) / 255;
144}
145
146FASTLED_FORCE_INLINE fl::i32 colordodge_fp(fl::i32 a, fl::i32 b) FL_NO_EXCEPT {
147 if (b >= 255) return 255;
148 fl::i32 result = (a * 255) / (255 - b);
149 return result > 255 ? 255 : result;
150}
151
152FASTLED_FORCE_INLINE fl::i32 colorburn_fp(fl::i32 a, fl::i32 b) FL_NO_EXCEPT {
153 if (b <= 0) return 0;
154 fl::i32 result = 255 - ((255 - a) * 255) / b;
155 return result < 0 ? 0 : result;
156}
157
158FASTLED_FORCE_INLINE fl::i32 subtract_fp(fl::i32 a, fl::i32 b) FL_NO_EXCEPT {
159 return a - b;
160}
161
162FASTLED_FORCE_INLINE fl::i32 add_fp(fl::i32 a, fl::i32 b) FL_NO_EXCEPT {
163 return a + b;
164}
165
166// Clamp RGB triple to [0, 255]
167FASTLED_FORCE_INLINE void rgb_sanity_check_fp(fl::i32 &r, fl::i32 &g, fl::i32 &b) FL_NO_EXCEPT {
168 if (r < 0) r = 0;
169 if (r > 255) r = 255;
170 if (g < 0) g = 0;
171 if (g > 255) g = 255;
172 if (b < 0) b = 0;
173 if (b > 255) b = 255;
174}
175
176// Hybrid helper: takes float render_parameters (same struct the float path uses),
177// converts to FP on-the-fly, and calls render_value_fp().
178// This enables a trivial conversion pattern: replace e->render_value(e->animation)
179// with render_value_fp_from_float(e->animation, fade_lut, perm).
180// The FP speedup comes from sincos32 + pnoise2d_raw replacing sinf/cosf + float pnoise.
182 const render_parameters &anim,
183 const fl::i32 *fade_lut,
184 const fl::u8 *perm) FL_NO_EXCEPT {
185 using FP = fl::s16x16;
187 p.angle_raw = FP(anim.angle).raw();
188 p.dist_raw = FP(anim.dist).raw();
189 p.scale_x_raw = FP(anim.scale_x).raw();
190 p.scale_y_raw = FP(anim.scale_y).raw();
191 p.scale_z_raw = FP(anim.scale_z).raw();
192 p.offset_x_raw = FP(anim.offset_x).raw();
193 p.offset_y_raw = FP(anim.offset_y).raw();
194 p.offset_z_raw = FP(anim.offset_z).raw();
195 p.z_raw = FP(anim.z).raw();
196 p.center_x_raw = FP(anim.center_x).raw();
197 p.center_y_raw = FP(anim.center_y).raw();
198 p.low_limit_raw = FP(anim.low_limit).raw();
199 p.high_limit_raw = FP(anim.high_limit).raw();
200 return render_value_fp(p, fade_lut, perm);
201}
202
203} // namespace fl
static constexpr int FRAC_BITS
Definition s16x16.h:24
constexpr i32 raw() const FL_NO_EXCEPT
Definition s16x16.h:62
#define FASTLED_FORCE_INLINE
unsigned char u8
Definition stdint.h:131
FASTLED_FORCE_INLINE fl::i32 render_value_fp(const render_parameters_fp &p, const fl::i32 *fade_lut, const fl::u8 *perm) FL_NO_EXCEPT
u8 u8 fract8 blur_amount FL_NO_EXCEPT
Definition blur.h:186
FASTLED_FORCE_INLINE fl::i32 render_value_fp_from_float(const render_parameters &anim, const fl::i32 *fade_lut, const fl::u8 *perm) FL_NO_EXCEPT
FASTLED_FORCE_INLINE fl::i32 colordodge_fp(fl::i32 a, fl::i32 b) FL_NO_EXCEPT
FASTLED_FORCE_INLINE void rgb_sanity_check_fp(fl::i32 &r, fl::i32 &g, fl::i32 &b) FL_NO_EXCEPT
static constexpr i32 FP_ONE
FASTLED_FORCE_INLINE fl::i32 screen_fp(fl::i32 a, fl::i32 b) FL_NO_EXCEPT
FASTLED_FORCE_INLINE fl::i32 add_fp(fl::i32 a, fl::i32 b) FL_NO_EXCEPT
FASTLED_FORCE_INLINE fl::u32 radiansToA24_fp(fl::i32 angle_s16x16_raw) FL_NO_EXCEPT
FASTLED_FORCE_INLINE SinCos32 sincos32(u32 angle) FL_NO_EXCEPT
Definition sin32.h:86
FASTLED_FORCE_INLINE fl::i32 colorburn_fp(fl::i32 a, fl::i32 b) FL_NO_EXCEPT
expected< T, E > result
Alias for expected (Rust-style naming)
Definition result.h:31
FASTLED_FORCE_INLINE fl::i32 subtract_fp(fl::i32 a, fl::i32 b) FL_NO_EXCEPT
InputGamut g
Definition rgbw.h:125
FASTLED_FORCE_INLINE fl::i32 multiply_fp(fl::i32 a, fl::i32 b) FL_NO_EXCEPT
Base definition for an LED controller.
Definition crgb.hpp:179
i32 sin_val
Definition sin32.h:23
i32 cos_val
Definition sin32.h:24
static fl::i32 pnoise3d_raw(fl::i32 fx_raw, fl::i32 fy_raw, fl::i32 fz_raw, const fl::i32 *fade_lut, const fl::u8 *perm) FL_NO_EXCEPT
static fl::i32 pnoise2d_raw(fl::i32 fx_raw, fl::i32 fy_raw, const fl::i32 *fade_lut, const fl::u8 *perm) FL_NO_EXCEPT