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