-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathGradientComponent.h
More file actions
296 lines (257 loc) · 8.93 KB
/
Copy pathGradientComponent.h
File metadata and controls
296 lines (257 loc) · 8.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
#pragma once
#include <stdint.h>
#include "deki-rendering/RendererComponent.h"
#include "Color.h"
/**
* @brief Gradient types for procedural generation
*/
enum class GradientType : uint8_t
{
Linear = 0, // Linear gradient with angle parameter
Radial = 1, // From center outward
Conical = 2 // Rotating around center
};
/**
* @brief Tiling modes for gradient rendering
*/
enum class GradientTileMode : uint8_t
{
None = 0, // No tiling - render gradient once
Horizontal = 1, // Tile horizontally
Vertical = 2, // Tile vertically
Both = 3, // Tile both horizontally and vertically
Mirror = 4 // Mirror tiling (gradient reverses each tile)
};
/**
* @brief Dithering modes for gradient rendering
*/
enum class GradientDitherMode : uint8_t
{
None = 0, // No dithering
Ordered2x2 = 1, // 2x2 Bayer matrix (fast, low quality)
Ordered4x4 = 2, // 4x4 Bayer matrix (balanced)
Ordered8x8 = 3, // 8x8 Bayer matrix (high quality, slower)
Ordered16x16 = 4 // 16x16 Bayer matrix (highest quality, slowest)
};
/**
* @brief Color stop for gradient definition
*/
struct GradientStop
{
float position; // Position along gradient (0.0 to 1.0)
deki::Color color; // Color at this position
GradientStop(float pos = 0.0f, const deki::Color& col = deki::Color::Black)
: position(pos), color(col)
{
}
// Legacy constructor for backward compatibility
GradientStop(float pos, uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha = 255)
: position(pos), color(red, green, blue, alpha)
{
}
};
/**
* @brief Gradient component for procedural gradient rendering
*
* Generates gradients in real-time with support for:
* - Multiple gradient types (linear, radial, conical)
* - Tiling modes for pattern repetition
* - Dithering for smooth color transitions on RGB565
* - Memory-efficient procedural generation
* - Performance optimized for embedded systems
*/
class GradientComponent : public RendererComponent
{
public:
DEKI_COMPONENT(GradientComponent, RendererComponent, "2D", "8d84cca3-7eb9-4b92-ba49-968ceec203c8", "DEKI_FEATURE_GRADIENT")
DEKI_DESCRIPTION("Draws a procedural gradient: linear, radial or conical.")
// Gradient properties
DEKI_EXPORT
GradientType gradient_type;
DEKI_EXPORT
GradientTileMode tile_mode;
DEKI_EXPORT
GradientDitherMode dither_mode;
/**
* @brief Scale of the dither pattern in pixels per Bayer cell.
*
* 1 (default) = native 1px dither (current behavior).
* 2/4/8/16 = chunkier blocks that make the pattern more visible —
* useful for stylized retro looks.
* Non power-of-2 values snap down to the nearest power of 2 (so 3→2, 7→4).
*/
DEKI_EXPORT
uint8_t dither_scale;
// Area to fill (meters)
DEKI_EXPORT
DEKI_UNIT(Distance)
float width;
DEKI_EXPORT
DEKI_UNIT(Distance)
float height;
// Gradient parameters
DEKI_EXPORT
DEKI_UNIT(Angle)
float angle; // Linear gradient angle. Stored in radians (0 = horizontal, π/2 = vertical); inspector displays degrees.
DEKI_EXPORT
float center_x; // For radial/conical gradients (0.0 to 1.0)
DEKI_EXPORT
float center_y; // For radial/conical gradients (0.0 to 1.0)
DEKI_EXPORT
float radius; // For radial gradients (0.0 to 1.0)
// Color stops (up to 4 stops for memory efficiency)
static constexpr uint8_t MAX_STOPS = 4;
GradientStop stops[MAX_STOPS];
DEKI_EXPORT
uint8_t stop_count;
// Individual color stop properties for editor serialization
// Stop 1 is always visible (minimum 1 stop required)
DEKI_EXPORT
float stop1_position;
DEKI_EXPORT
deki::Color stop1_color;
// Stop 2 visible when stop_count >= 2
DEKI_EXPORT
DEKI_VISIBLE_WHEN(stop_count, 2)
float stop2_position;
DEKI_EXPORT
DEKI_VISIBLE_WHEN(stop_count, 2)
deki::Color stop2_color;
// Stop 3 visible when stop_count >= 3
DEKI_EXPORT
DEKI_VISIBLE_WHEN(stop_count, 3)
float stop3_position;
DEKI_EXPORT
DEKI_VISIBLE_WHEN(stop_count, 3)
deki::Color stop3_color;
// Stop 4 visible when stop_count >= 4
DEKI_EXPORT
DEKI_VISIBLE_WHEN(stop_count, 4)
float stop4_position;
DEKI_EXPORT
DEKI_VISIBLE_WHEN(stop_count, 4)
deki::Color stop4_color;
// Tile properties (world meters; 0 = use component width/height)
DEKI_EXPORT
DEKI_UNIT(Distance)
float tile_width;
DEKI_EXPORT
DEKI_UNIT(Distance)
float tile_height;
GradientComponent(float w = 0.0f, float h = 0.0f);
~GradientComponent();
/**
* @brief Set gradient type and basic parameters
*/
void SetGradientType(GradientType type);
/**
* @brief Set linear gradient with angle
* @param angle_radians Angle in radians (0 = left to right, π/2 = top to bottom)
*/
void SetLinearGradient(float angle_radians = 0.0f);
/**
* @brief Set radial gradient with center and radius
* @param center_x Center X position (0.0 to 1.0)
* @param center_y Center Y position (0.0 to 1.0)
* @param radius Radius (0.0 to 1.0)
*/
void SetRadialGradient(float center_x = 0.5f, float center_y = 0.5f, float radius = 0.5f);
/**
* @brief Add a color stop to the gradient
* @param position Position along gradient (0.0 to 1.0)
* @param r Red component (0-255)
* @param g Green component (0-255)
* @param b Blue component (0-255)
*/
void AddColorStop(float position, uint8_t r, uint8_t g, uint8_t b);
/**
* @brief Clear all color stops
*/
void ClearColorStops();
/**
* @brief Sync color stops from individual property members
* Called before rendering to ensure stops array matches property values
*/
void SyncStopsFromProperties();
/**
* @brief Set simple two-color gradient
* @param start_r Start color red
* @param start_g Start color green
* @param start_b Start color blue
* @param end_r End color red
* @param end_g End color green
* @param end_b End color blue
*/
void SetSimpleGradient(
uint8_t start_r, uint8_t start_g, uint8_t start_b, uint8_t end_r, uint8_t end_g, uint8_t end_b);
/**
* @brief Set tiling mode
*/
void SetTiling(GradientTileMode mode, float tile_w = 0.0f, float tile_h = 0.0f);
/**
* @brief Set dithering mode for smooth gradients on RGB565
*/
void SetDithering(GradientDitherMode mode);
/**
* @brief Set the area to fill
*/
void SetArea(float w, float h);
/**
* @brief Render gradient to a local buffer at origin (0,0)
*
* Use this for editor preview or when you want to render to a texture
* that will be positioned separately. Buffer size should be width * height * 2 bytes (RGB565).
*
* @param buffer Output buffer (RGB565 format)
*/
void RenderToBuffer(uint8_t* buffer);
// Unified rendering via QuadBlit
bool RenderContent(const DekiObject* owner,
QuadBlit::Source& outSource,
float& outPivotX,
float& outPivotY,
uint8_t& outTintR,
uint8_t& outTintG,
uint8_t& outTintB,
uint8_t& outTintA) override;
private:
/**
* @brief Calculate gradient value at normalized position
* @param norm_x Normalized X position (0.0 to 1.0)
* @param norm_y Normalized Y position (0.0 to 1.0)
* @return Gradient position (0.0 to 1.0)
*/
float CalculateGradientPosition(float norm_x, float norm_y) const;
/**
* @brief Interpolate color at gradient position
* @param position Gradient position (0.0 to 1.0)
* @param r Output red component
* @param g Output green component
* @param b Output blue component
*/
void InterpolateColor(float position, uint8_t* r, uint8_t* g, uint8_t* b) const;
/**
* @brief Sample the Bayer threshold at (x, y) for the current dither_mode.
* @return Threshold in [0, 1).
*/
float SampleBayerThreshold(int32_t x, int32_t y) const;
/**
* @brief Pixelorama-style stipple pick: write one of the bracketing stop
* colors based on whether the local-t exceeds a Bayer threshold.
* No interpolation between stops — every output pixel is exactly
* one of the authored colors.
*/
void PickStopByThreshold(float position, float threshold,
uint8_t* r, uint8_t* g, uint8_t* b) const;
/**
* @brief Convert RGB to RGB565 format
*/
uint16_t ConvertToRGB565(uint8_t r, uint8_t g, uint8_t b) const;
/**
* @brief Render single pixel with bounds checking
*/
void RenderPixel(
int32_t x, int32_t y, uint16_t color, uint8_t* render_buffer, int screen_width, int screen_height) const;
};
// Generated property metadata (after class definition for offsetof)
#include "generated/GradientComponent.gen.h"