1use super::cpu_text::{CpuText, Label};
2use super::effects::blur;
3use super::icons::Icons;
4use super::sdf::{self, Field};
5use super::sprites::SpriteImage;
6use super::style;
7use super::{
8 ADDITIVE, BLUR, DASHED, FILL_GRADIENT, FILL_IMAGE, FILL_MASK, FILL_VERTICAL, Fill, Fit,
9 FlatScene, Gradient, Instance, KIND_ARC, KIND_CIRCLE, KIND_ICON_FILL, KIND_ICON_LINE,
10 KIND_RECT, KIND_RING, KIND_SHADOW, Order, PATTERN_MASK, PATTERN_SHIFT, Shape, Step, UNPICKED,
11 pack_shadow, pack_shape, placed, positive, shadow,
12};
13
14pub struct Paint {
15 pub colour: [f32; 4],
16 pub footprint: f32,
17}
18
19pub(crate) fn field(instance: &Instance, icons: Option<&Icons>, p: [f32; 2]) -> Field {
20 let shape = instance.shape;
21 match instance.kind() {
22 KIND_RECT => sdf::rect(p, [shape[0], shape[1]], instance.corners),
23 KIND_CIRCLE => sdf::circle(p, shape[0]),
24 KIND_RING => sdf::ring(p, shape[0], shape[1]),
25 KIND_ARC => sdf::arc(p, shape[0], shape[1], shape[2], shape[3]),
26 _ => {
27 let icon = instance.icon;
28 let distance = icons.map_or(1.0e4, |icons| {
29 icons.sample([icon[0] + p[0] * icon[2], icon[1] + p[1] * icon[3]])
30 });
31 [distance, 0.0, 0.0]
32 }
33 }
34}
35
36fn mix(a: [f32; 4], b: [f32; 4], t: f32) -> [f32; 4] {
37 std::array::from_fn(|i| a[i] + (b[i] - a[i]) * t)
38}
39
40fn premultiply(c: [f32; 4], cover: f32) -> [f32; 4] {
41 [
42 c[0] * c[3] * cover,
43 c[1] * c[3] * cover,
44 c[2] * c[3] * cover,
45 c[3] * cover,
46 ]
47}
48
49pub fn paint(instance: &Instance, icons: Option<&Icons>, p: [f32; 2]) -> Paint {
50 paint_with(instance, icons, None, p)
51}
52
53pub fn paint_with(
54 instance: &Instance,
55 icons: Option<&Icons>,
56 gradient: Option<&Gradient>,
57 p: [f32; 2],
58) -> Paint {
59 paint_sprite(instance, icons, gradient, None, p)
60}
61
62pub fn paint_sprite(
63 instance: &Instance,
64 icons: Option<&Icons>,
65 gradient: Option<&Gradient>,
66 sprites: Option<&SpriteImage>,
67 p: [f32; 2],
68) -> Paint {
69 let flags = instance.info[0];
70 let mut paint = if flags & BLUR != 0 {
71 blur::paint(instance, icons, p)
72 } else {
73 plain(instance, icons, gradient, sprites, p)
74 };
75 if flags & ADDITIVE != 0 {
76 paint.colour[3] = 0.0;
77 }
78 paint
79}
80
81fn plain(
82 instance: &Instance,
83 icons: Option<&Icons>,
84 gradient: Option<&Gradient>,
85 sprites: Option<&SpriteImage>,
86 p: [f32; 2],
87) -> Paint {
88 let inverse = instance.inverse();
89 let kind = instance.kind();
90 if kind == KIND_SHADOW {
91 let centre = [p[0] - instance.icon[2], p[1] - instance.icon[3]];
92 let cover = shadow::rounded_box(
93 centre,
94 [instance.shape[0], instance.shape[1]],
95 instance.corners,
96 [instance.icon[0], instance.icon[1]],
97 );
98 return Paint {
99 colour: premultiply(instance.fill_top, cover),
100 footprint: 0.0,
101 };
102 }
103 let flags = instance.info[0];
104 let fill_mode = flags & FILL_MASK;
105 let opacity = instance.opacity();
106 let icon = kind == KIND_ICON_FILL || kind == KIND_ICON_LINE;
107 let field = field(instance, icons, p);
108 let mut fill = [0.0; 4];
109 let mut fill_cover = 0.0;
110 if fill_mode != 0 {
111 fill_cover = if icon {
112 sdf::coverage_even(field[0], inverse)
113 } else {
114 sdf::coverage(field, inverse)
115 };
116 let t = if fill_mode == FILL_VERTICAL {
117 ((p[1] - instance.origin[2]) / (instance.origin[3] - instance.origin[2]))
118 .clamp(0.0, 1.0)
119 } else {
120 0.0
121 };
122 let mut base = if fill_mode == FILL_GRADIENT {
123 gradient.map_or([0.0; 4], |gradient| gradient.paint(p))
124 } else if fill_mode == FILL_IMAGE {
125 let uv = [
126 instance.icon[0] + p[0] * instance.icon[2],
127 instance.icon[1] + p[1] * instance.icon[3],
128 ];
129 let texel = sprites.map_or([1.0; 4], |sprites| sprites.sample(uv));
130 premultiply(
131 std::array::from_fn(|k| texel[k] * instance.fill_top[k]),
132 1.0,
133 )
134 } else {
135 premultiply(mix(instance.fill_top, instance.fill_bottom, t), 1.0)
136 };
137 let pattern = (flags & PATTERN_MASK) >> PATTERN_SHIFT;
138 if pattern != 0 {
139 let packed = instance.info[2];
140 let colour = std::array::from_fn(|i| ((packed >> (8 * i)) & 255) as f32 / 255.0);
141 let cover = sdf::coverage(sdf::pattern(pattern, instance.icon, p), inverse);
142 let paint = premultiply(colour, cover);
143 base = std::array::from_fn(|i| paint[i] + base[i] * (1.0 - paint[3]));
144 }
145 fill = base.map(|value| value * fill_cover);
146 fill = style::bevel_over(instance, p, field, inverse, fill, fill_cover);
147 }
148 let mut stroke = [0.0; 4];
149 let mut stroke_cover = 0.0;
150 if instance.stroke[0] > 0.0 {
151 let s = if field[0] >= 0.0 { 1.0 } else { -1.0 };
152 let band = [
153 field[0].abs() - instance.stroke[0] * 0.5,
154 s * field[1],
155 s * field[2],
156 ];
157 stroke_cover = if icon {
158 sdf::coverage_even(band[0], inverse)
159 } else {
160 sdf::coverage(band, inverse)
161 };
162 if flags & DASHED != 0 {
163 let along = if kind == KIND_RECT {
164 sdf::rect_along(p, [instance.shape[0], instance.shape[1]], instance.corners)
165 } else {
166 sdf::circle_along(p, instance.shape[0])
167 };
168 stroke_cover *= sdf::coverage(
169 sdf::dash(
170 along,
171 instance.stroke[1],
172 instance.stroke[2],
173 instance.stroke[3],
174 ),
175 inverse,
176 );
177 }
178 stroke = premultiply(instance.stroke_colour, stroke_cover);
179 }
180 let colour = std::array::from_fn(|i| (stroke[i] + fill[i] * (1.0 - stroke[3])) * opacity);
181 let fill_alpha = if fill_mode == FILL_GRADIENT {
182 instance.fill_bottom[3]
183 } else {
184 instance.fill_top[3].max(instance.fill_bottom[3])
185 };
186 let fill_seen = if fill_mode != 0 && fill_alpha > 0.0 {
187 fill_cover
188 } else {
189 0.0
190 };
191 let stroke_seen = if instance.stroke_colour[3] > 0.0 {
192 stroke_cover
193 } else {
194 0.0
195 };
196 Paint {
197 colour,
198 footprint: if opacity > 0.0 {
199 fill_seen.max(stroke_seen)
200 } else {
201 0.0
202 },
203 }
204}
205
206fn over(dst: &mut [f32; 4], src: [f32; 4]) {
207 for i in 0..4 {
208 dst[i] = src[i] + dst[i] * (1.0 - src[3]);
209 }
210}
211
212fn blend(
213 image: &mut [[f32; 4]],
214 size: [u32; 2],
215 instance: &Instance,
216 icons: Option<&Icons>,
217 gradient: Option<&Gradient>,
218 sprites: Option<&SpriteImage>,
219) {
220 let [x0, y0, x1, y1] = instance.pixel_bounds();
221 let x0 = x0.floor().max(0.0) as u32;
222 let y0 = y0.floor().max(0.0) as u32;
223 let x1 = (x1.ceil().max(0.0) as u32).min(size[0]);
224 let y1 = (y1.ceil().max(0.0) as u32).min(size[1]);
225 for y in y0..y1 {
226 for x in x0..x1 {
227 let local = instance.to_local([x as f32 + 0.5, y as f32 + 0.5]);
228 let [cx, cy, hx, hy] = instance.bounds;
229 let margin = instance.margin();
230 if (local[0] - cx).abs() > hx + margin[0] || (local[1] - cy).abs() > hy + margin[1] {
231 continue;
232 }
233 let paint = paint_sprite(instance, icons, gradient, sprites, local);
234 over(&mut image[(y * size[0] + x) as usize], paint.colour);
235 }
236 }
237}
238
239pub fn render(
240 scene: &FlatScene<'_>,
241 icons: Option<&Icons>,
242 size: [u32; 2],
243) -> Result<Vec<[f32; 4]>, String> {
244 render_scene(scene, icons, None, size)
245}
246
247pub fn render_with_text(
248 scene: &FlatScene<'_>,
249 icons: Option<&Icons>,
250 texts: &[CpuText<'_>],
251 size: [u32; 2],
252) -> Result<Vec<[f32; 4]>, String> {
253 render_scene(scene, icons, Some(texts), size)
254}
255
256fn label<'a>(
257 texts: &[CpuText<'a>],
258 index: usize,
259 draw: &super::Draw,
260 fit: &Fit,
261 size: [u32; 2],
262) -> Result<Option<Label<'a>>, String> {
263 let text = texts
264 .get(index)
265 .ok_or_else(|| format!("a draw names text {index}, which is missing"))?;
266 if !positive(draw.opacity) || placed(&draw.transform, fit).is_none() {
267 return Ok(None);
268 }
269 Ok(Label::new(text, draw, fit, size))
270}
271
272fn render_scene(
273 scene: &FlatScene<'_>,
274 icons: Option<&Icons>,
275 texts: Option<&[CpuText<'_>]>,
276 size: [u32; 2],
277) -> Result<Vec<[f32; 4]>, String> {
278 let fit = Fit::new(scene.layout, size)?;
279 let sprites = scene.sprites.map(|sprites| &sprites.image);
280 let mut order = Order::default();
281 order.build(scene.draws, scene.groups.len())?;
282 let clear = scene
283 .clear
284 .map_or([0.0; 4], |colour| colour.premultiplied());
285 let pixels = (size[0] * size[1]) as usize;
286 let mut image = vec![clear; pixels];
287 let mut layer: Option<(Vec<[f32; 4]>, f32)> = None;
288 for step in &order.steps {
289 match *step {
290 Step::Layer(group) => {
291 layer = Some((
292 vec![[0.0; 4]; pixels],
293 scene.groups[usize::from(group)].opacity,
294 ));
295 }
296 Step::EndLayer(_) => {
297 if let Some((pixels, opacity)) = layer.take() {
298 for (dst, src) in image.iter_mut().zip(pixels) {
299 over(dst, src.map(|value| value * opacity));
300 }
301 }
302 }
303 Step::Draw(index) => {
304 let draw = &scene.draws[index];
305 let target = match layer.as_mut() {
306 Some((pixels, _)) => pixels,
307 None => &mut image,
308 };
309 if let Shape::Text(index) = draw.shape {
310 if let Some(texts) = texts
311 && let Some(label) = label(texts, index, draw, &fit, size)?
312 {
313 label.blend(target, size);
314 }
315 continue;
316 }
317 let gradient = match &draw.fill {
318 Fill::Gradient(gradient) => Some(gradient),
319 _ => None,
320 };
321 if let Some(instance) = pack_shadow(draw, &fit, scene.curve, &scene.light) {
322 blend(target, size, &instance, icons, None, None);
323 }
324 if let Some(instance) = pack_shape(draw, &fit, icons) {
325 blend(target, size, &instance, icons, gradient, sprites);
326 }
327 }
328 }
329 }
330 Ok(image)
331}
332
333pub fn pick(
334 scene: &FlatScene<'_>,
335 icons: Option<&Icons>,
336 size: [u32; 2],
337 pixel: [u32; 2],
338) -> Result<u32, String> {
339 pick_scene(scene, icons, None, size, pixel)
340}
341
342pub fn pick_with_text(
343 scene: &FlatScene<'_>,
344 icons: Option<&Icons>,
345 texts: &[CpuText<'_>],
346 size: [u32; 2],
347 pixel: [u32; 2],
348) -> Result<u32, String> {
349 pick_scene(scene, icons, Some(texts), size, pixel)
350}
351
352fn pick_scene(
353 scene: &FlatScene<'_>,
354 icons: Option<&Icons>,
355 texts: Option<&[CpuText<'_>]>,
356 size: [u32; 2],
357 pixel: [u32; 2],
358) -> Result<u32, String> {
359 let fit = Fit::new(scene.layout, size)?;
360 let mut order = Order::default();
361 order.build(scene.draws, scene.groups.len())?;
362 let centre = [pixel[0] as f32 + 0.5, pixel[1] as f32 + 0.5];
363 let mut id = 0;
364 for step in &order.steps {
365 if let Step::Draw(index) = *step
366 && let Shape::Text(at) = scene.draws[index].shape
367 {
368 let draw = &scene.draws[index];
369 if draw.id != 0
370 && let Some(texts) = texts
371 && let Some(label) = label(texts, at, draw, &fit, size)?
372 && label.picked(pixel)
373 {
374 id = draw.id;
375 }
376 continue;
377 }
378 if let Step::Draw(index) = *step
379 && let Some(instance) = pack_shape(&scene.draws[index], &fit, icons)
380 && instance.info[0] & UNPICKED == 0
381 {
382 let [x0, y0, x1, y1] = instance.pixel_bounds();
383 if centre[0] < x0 || centre[0] > x1 || centre[1] < y0 || centre[1] > y1 {
384 continue;
385 }
386 if plain(&instance, icons, None, None, instance.to_local(centre)).footprint >= 0.5 {
387 id = instance.info[1];
388 }
389 }
390 }
391 Ok(id)
392}