1use gfx::blendmode::bm_row;
21use rt::fx::{cvt, cvtd, cvttd, cvttd64, maxsd, minsd};
22
23use crate::perlin::{perlin_row, PerlinRow};
24use alloc::vec;
25use simd::{Kernel, Simd, Sse2};
26use crate::warp::{log, sincos};
27use crate::*;
28
29const NZ_SEG: usize = 256;
30const NZ_MAXOCT: usize = 12;
31const DEG2RAD: f64 = 0.017453292519943295769;
32
33static OFS: [[f64; 2]; 12] = [
34 [0.0, 0.0],
35 [0.6180339887498949, 0.75487766624669272],
36 [0.23606797749978981, 0.50975533249338545],
37 [0.85410196624968471, 0.26463299874007795],
38 [0.47213595499957961, 0.019510664986770898],
39 [0.090169943749474513, 0.77438833123346384],
40 [0.70820393249936942, 0.5292659974801559],
41 [0.32623792124926432, 0.28414366372684885],
42 [0.94427190999915922, 0.039021329973541796],
43 [0.56230589874905412, 0.79389899622023474],
44 [0.18033988749894903, 0.54877666246692769],
45 [0.79837387624884393, 0.30365432871361975],
46];
47
48#[inline]
50fn colvec(c: u32) -> [f32; 4] {
51 [(c & 255) as f32, (c >> 8 & 255) as f32, (c >> 16 & 255) as f32, (c >> 24) as f32]
52}
53
54#[inline]
56fn ramp(t: f64, c1: &[f32; 4], dc: &[f32; 4]) -> [i32; 4] {
57 let tf = t as f32;
58 core::array::from_fn(|c| cvt(tf * dc[c] + c1[c]))
59}
60
61#[inline]
62fn pack(v: &[i32; 4]) -> u32 {
63 let mut p = 0u32;
64 for (c, &x) in v.iter().enumerate() {
65 p |= (x.clamp(-32768, 32767).clamp(0, 255) as u32) << (8 * c);
66 }
67 p
68}
69
70fn noise_render(a: &FxArgs, dst: &mut Dst, turb: bool) -> i32 {
73 let p = a.params;
74 let mode = p.int(0, 0, 13) as u32 | 0xFF00;
75 let mut div = p.int(1, 2, 1000) as u32;
76 let power = p.dbl(2, 0.0, 1.0);
77 let (maxoct, ampmin, seed, cs) = if turb {
78 (p.int(3, 1, NZ_MAXOCT as i32) as usize, -1.0, p.0[4] as u32, 5)
79 } else {
80 (NZ_MAXOCT, 0.03, p.0[3] as u32, 4)
81 };
82 let mut inv = [0f64; NZ_MAXOCT];
83 let mut ampv = [0f64; NZ_MAXOCT];
84 let mut n = 0usize;
85 let mut amp = 1.0f64;
86 let mut sum = 0.0f64;
87 while n < maxoct && amp > ampmin && div != 0 {
88 inv[n] = 1.0 / div as f64;
89 ampv[n] = amp;
90 sum += amp;
91 amp *= power;
92 div >>= 1;
93 n += 1;
94 }
95 let norm = 2.0 / sum;
96 let c1 = colvec(p.0[cs] as u32);
97 let c2 = colvec(p.0[cs + 1] as u32);
98 let dc: [f32; 4] = core::array::from_fn(|c| c2[c] - c1[c]);
99 let (w, h) = (a.src.w, a.src.h);
100 let mut acc = [0f64; NZ_SEG];
101 let mut buf = [0u8; NZ_SEG * 4];
102 for y in a.y0..a.y1 {
103 if a.cancelled() {
104 return ERR_CANCEL;
105 }
106 let dy = (y + y - h) as f64;
107 let srow = a.src_row(y);
108 let drow = dst.row(y);
109 let mut x = a.x0;
110 while a.x1 - x > 0 {
111 let ns = ((a.x1 - x) as usize).min(NZ_SEG);
112 acc[..ns].fill(0.0);
113 for i in 0..n {
114 let rec = PerlinRow {
115 n: ns,
116 x0: x + x - w,
117 step: 2,
118 abs: turb,
119 seed,
120 stream: i as u32,
121 inv: inv[i],
122 ofsx: OFS[i][0],
123 y: inv[i] * dy + OFS[i][1],
124 amp: ampv[i],
125 };
126 perlin_row(&rec, &mut acc);
127 }
128 for j in 0..ns {
129 let v = acc[j];
130 let t = if turb { v * norm } else { (v + 1.0) * 0.5 };
131 let t = minsd(maxsd(t, 0.0), 1.0);
132 put(&mut buf, j, pack(&ramp(t, &c1, &dc)));
133 }
134 let o = (x - a.x0) as usize * 4;
135 bm_row(&mut drow[o..o + ns * 4], &srow[x as usize * 4..x as usize * 4 + ns * 4], &buf[..ns * 4], mode);
136 x += ns as i32;
137 }
138 }
139 0
140}
141
142fn clouds_render(a: &FxArgs, dst: &mut Dst) -> i32 {
143 noise_render(a, dst, false)
144}
145
146fn turb_render(a: &FxArgs, dst: &mut Dst) -> i32 {
147 noise_render(a, dst, true)
148}
149
150const FF_CHUNK: usize = 32; const FF_MAXCNT: usize = 26; const NREG: usize = 3; const MAXL: usize = 12;
156
157struct Lanes<'a> {
164 sx: &'a [f64],
165 sy: &'a [f64],
166 inside: &'a [bool],
167 nf: &'a mut [f64],
168 sf: &'a mut [f64],
169 mand: bool,
170 r2: f64,
171 kmax: i32,
172}
173
174struct F64s([u8; 8 * MAXL]);
177
178impl F64s {
179 #[inline(always)]
180 fn get(&self, c: usize) -> f64 {
181 f64::from_le_bytes(self.0[c * 8..c * 8 + 8].try_into().unwrap_or([0; 8]))
182 }
183 #[inline(always)]
184 fn set(&mut self, c: usize, v: f64) {
185 self.0[c * 8..c * 8 + 8].copy_from_slice(&v.to_le_bytes());
186 }
187 #[inline(always)]
189 fn ld<S: Simd>(&self, s: S, r: usize) -> S::V {
190 s.load(&self.0[r * S::BYTES..])
191 }
192 #[inline(always)]
193 fn st<S: Simd>(&mut self, s: S, r: usize, v: S::V) {
194 s.store(&mut self.0[r * S::BYTES..], v)
195 }
196}
197
198struct Src<'a> {
200 sx: &'a [f64],
201 sy: &'a [f64],
202 inside: &'a [bool],
203 mand: bool,
204 kmax: i32,
205}
206
207struct LaneSt {
209 zx: F64s,
210 zy: F64s,
211 cx: F64s,
212 cy: F64s,
213 idx: [i32; MAXL],
214 dl: [i32; MAXL],
215 next: usize,
216}
217
218impl LaneSt {
219 #[inline(always)]
222 fn take(&mut self, c: usize, lb: &Src, g: i32) {
223 let n = lb.sx.len();
224 while self.next < n && lb.inside[self.next] {
225 self.next += 1;
226 }
227 let j = self.next;
228 if j < n {
229 self.idx[c] = j as i32;
230 self.dl[c] = g + lb.kmax;
231 let (zx, zy, cx, cy) = if lb.mand { (0.0, 0.0, lb.sx[j], lb.sy[j]) } else { (lb.sx[j], lb.sy[j], 0.3125, 0.03) };
232 self.zx.set(c, zx);
233 self.zy.set(c, zy);
234 self.cx.set(c, cx);
235 self.cy.set(c, cy);
236 self.next += 1;
237 } else {
238 self.zx.set(c, 0.0);
239 self.zy.set(c, 0.0);
240 self.cx.set(c, 0.0);
241 self.cy.set(c, 0.0);
242 self.dl[c] = i32::MAX;
243 self.idx[c] = -1;
244 }
245 }
246
247 #[inline(always)]
249 fn record(&self, c: usize, m: f64, g: i32, kmax: i32, nf: &mut [f64], sf: &mut [f64]) {
250 let j = self.idx[c] as usize;
251 sf[j] = m;
252 nf[j] = (g - (self.dl[c] - kmax)) as f64;
253 }
254}
255
256impl Kernel for Lanes<'_> {
257 type Out = ();
258 #[inline(always)]
259 fn run<S: Simd>(self, s: S) {
260 let l = S::BYTES / 8;
261 let nl = NREG * l;
262 let Lanes { sx, sy, inside, nf, sf, mand, r2, kmax } = self;
263 let src = Src { sx, sy, inside, mand, kmax };
264 let mut ls = LaneSt {
265 zx: F64s([0; 8 * MAXL]),
266 zy: F64s([0; 8 * MAXL]),
267 cx: F64s([0; 8 * MAXL]),
268 cy: F64s([0; 8 * MAXL]),
269 idx: [-1; MAXL],
270 dl: [i32::MAX; MAXL],
271 next: 0,
272 };
273 let mut g = 0i32;
274 let full = if S::BYTES == 32 { u32::MAX } else { 0xFFFF };
275 let vr2 = s.splatd(r2);
276 for c in 0..nl {
278 ls.take(c, &src, g);
279 }
280 loop {
281 let mut mindl = i32::MAX;
282 for &d in &ls.dl[..nl] {
283 mindl = mindl.min(d);
284 }
285 if mindl == i32::MAX {
286 break;
287 }
288 let mut x = [s.zero(); NREG];
289 let mut y = [s.zero(); NREG];
290 let mut vcx = [s.zero(); NREG];
291 let mut vcy = [s.zero(); NREG];
292 for r in 0..NREG {
293 x[r] = ls.zx.ld(s, r);
294 y[r] = ls.zy.ld(s, r);
295 vcx[r] = ls.cx.ld(s, r);
296 vcy[r] = ls.cy.ld(s, r);
297 }
298 let mut esc = [full; NREG];
300 while g < mindl {
301 let mut x2 = [s.zero(); NREG];
302 let mut y2 = [s.zero(); NREG];
303 let mut all = full;
304 for r in 0..NREG {
305 x2[r] = s.muld(x[r], x[r]);
306 y2[r] = s.muld(y[r], y[r]);
307 esc[r] = s.movemask8(s.cmpltd(s.addd(x2[r], y2[r]), vr2));
308 all &= esc[r];
309 }
310 if all != full {
311 break;
312 }
313 for r in 0..NREG {
314 let nx = s.addd(s.subd(x2[r], y2[r]), vcx[r]);
315 y[r] = s.addd(s.muld(s.addd(x[r], x[r]), y[r]), vcy[r]);
316 x[r] = nx;
317 }
318 g += 1;
319 }
320 for r in 0..NREG {
321 ls.zx.st(s, r, x[r]);
322 ls.zy.st(s, r, y[r]);
323 }
324 if g < mindl {
325 for r in 0..NREG {
329 for k in 0..l {
330 let c = r * l + k;
331 if esc[r] >> (8 * k) & 1 == 0 && ls.idx[c] >= 0 {
332 let (zx, zy) = (ls.zx.get(c), ls.zy.get(c));
333 let m = zx * zx + zy * zy;
334 ls.record(c, m, g, kmax, nf, sf);
335 ls.take(c, &src, g);
336 }
337 }
338 }
339 } else {
340 for c in 0..nl {
342 if ls.idx[c] < 0 {
343 continue;
344 }
345 let (zx, zy) = (ls.zx.get(c), ls.zy.get(c));
346 let m = zx * zx + zy * zy;
347 if g >= ls.dl[c] || !(m < r2) {
348 ls.record(c, m, g, kmax, nf, sf);
349 ls.take(c, &src, g);
350 }
351 }
352 }
353 }
354 }
355}
356
357fn rainbow(c: f64, inside: bool) -> [i32; 4] {
360 if inside || c >= 1023.0 || c.is_nan() {
361 return [0, 0, 0, 255];
362 }
363 let hh = maxsd(c * 0.015625, -1.0e9);
364 let mut t = cvttd64(hh);
365 if t as f64 > hh {
366 t -= 1;
367 }
368 let mut f = (hh - t as f64) * 6.0;
369 let s = cvttd(f);
370 f -= s as f64;
371 let ff = cvtd(f * 255.0);
372 let d = 255 - ff;
373 let (r, g, b) = match s {
374 0 => (255, ff, 0),
375 1 => (d, 255, 0),
376 2 => (0, 255, ff),
377 3 => (0, d, 255),
378 4 => (ff, 0, 255),
379 _ => (255, 0, d),
380 };
381 [b, g, r, 255]
382}
383
384fn fractal_render(a: &FxArgs, dst: &mut Dst, mand: bool) -> i32 {
385 let p = a.params;
386 let (fact, q, invz, ang, scheme, cs, count, kmax, r2, invert, k1, k2);
387 if !mand {
388 fact = p.dbl(0, 1.0, 10.0);
389 q = p.int(1, 1, 5);
390 invz = 1.0 / p.dbl(2, 0.01, 50.0);
391 ang = p.dbl(3, -36000.0, 36000.0);
392 scheme = p.int(4, 0, 3);
393 cs = 5usize;
394 count = q * q;
395 kmax = 256;
396 r2 = 10000.0;
397 invert = false;
398 k1 = [768.0, 512.0];
399 k2 = [256.0, 0.0];
400 } else {
401 let f = p.int(0, 1, 10);
402 fact = f as f64;
403 kmax = (1023 / f as u32 + 1) as i32;
404 r2 = 100000.0;
405 q = p.int(1, 1, 5);
406 invz = 1.0 / (p.int(2, 0, 50) * 20 + 1) as f64;
407 invert = p.int(4, 0, 1) != 0;
408 scheme = p.int(5, 0, 3);
409 ang = p.dbl(3, -36000.0, 36000.0);
410 cs = 6;
411 count = q * q + 1;
412 k1 = [256.0, 512.0];
413 k2 = [768.0, 0.0];
414 }
415 let invq = 1.0 / q as f64;
416 let invcnt = 1.0 / count as f64;
417 let invh = 1.0 / a.src.h as f64;
418 let (sn, cn) = sincos(ang * DEG2RAD);
419 let (c1v, c2v) = if scheme == 1 { (0xFF00_0000, 0xFFFF_FFFF) } else { (p.0[cs] as u32, p.0[cs + 1] as u32) };
420 let c1 = colvec(c1v);
421 let c2 = colvec(c2v);
422 let dc: [f32; 4] = core::array::from_fn(|c| c2[c] - c1[c]);
423 let (w, h) = (a.src.w, a.src.h);
424 let ns = FF_CHUNK * FF_MAXCNT;
425 let (mut sx, mut sy, mut nf, mut sf) = (vec![0f64; ns], vec![0f64; ns], vec![0f64; ns], vec![0f64; ns]);
426 let mut ins = vec![false; ns];
427 let cnt = count as usize;
428 let f64x2 = |p: f64, q: f64| {
429 let mut b = [0u8; 16];
430 b[..8].copy_from_slice(&p.to_le_bytes());
431 b[8..].copy_from_slice(&q.to_le_bytes());
432 Sse2.load(&b)
433 };
434 let (vk1, vk2) = (f64x2(k1[0], k1[1]), f64x2(k2[0], k2[1]));
435 let v255 = Sse2.splatd(255.0);
436 let vcf = Sse2.splatd(count as f64);
437 let (vdc, vc1) = (Sse2.load_f32(&dc), Sse2.load_f32(&c1));
438 for y in a.y0..a.y1 {
439 if a.cancelled() {
440 return ERR_CANCEL;
441 }
442 let by = (y as f64 + y as f64) - h as f64;
443 let dx0 = dst.x0;
444 let drow = dst.row(y);
445 let mut xa = a.x0;
446 while a.x1 - xa > 0 {
447 let npx = ((a.x1 - xa) as usize).min(FF_CHUNK);
448 let mut j = 0usize;
450 for k in 0..npx {
451 let x = xa + k as i32;
452 let bx = (x as f64 + x as f64) - w as f64;
453 for i in 0..cnt {
454 let fi = i as i32 as f64;
455 let u = (fi * invcnt + bx) * invh;
456 let t = fi * invq;
457 let v = (t - cvttd(t) as f64 + by) * invh;
458 let ur = u * cn - v * sn;
459 let vr = u * sn + v * cn;
460 let (mut px, mut py) = (ur * invz, vr * invz);
461 ins[j] = false;
462 if mand {
463 px += -0.7453;
464 py += 0.1127;
465 let xq = px - 0.25;
466 let y2 = py * py;
467 let qq = xq * xq + y2;
468 if !((xq + qq) * qq > y2 * 0.25) {
469 ins[j] = true;
470 } else {
471 let x1 = px + 1.0;
472 ins[j] = !(x1 * x1 + py * py > 0.0625);
473 }
474 }
475 sx[j] = px;
476 sy[j] = py;
477 j += 1;
478 }
479 }
480 simd::dispatch(Lanes { sx: &sx[..j], sy: &sy[..j], inside: &ins[..j], nf: &mut nf, sf: &mut sf, mand, r2, kmax });
481 let x = Sse2;
485 let mut j = 0usize;
486 for k in 0..npx {
487 let mut sum = x.zero();
488 for _ in 0..cnt {
489 let inside = ins[j];
490 let val = if !mand {
491 (nf[j] - (2.0 - 18.420680743952367 / log(sf[j]))) * fact
492 } else if inside {
493 0.0
494 } else {
495 (nf[j] - log(sf[j]) * 0.086858896380650365) * fact + 64.0
496 };
497 let col = match scheme {
498 0 => {
499 if inside {
500 x.splat32(255)
501 } else {
502 let v = x.splatd(val);
503 let z = x.zero();
504 let lo = x.cvtt_f64_i32(x.mind(x.maxd(x.subd(v, vk1), z), v255));
505 let hi = x.cvtt_f64_i32(x.mind(x.maxd(x.subd(v, vk2), z), v255));
506 x.shufps::<0x44>(lo, hi)
507 }
508 }
509 3 => x.load_i32(&rainbow(val, inside)),
510 _ => {
511 let c = if inside { 1024.0 } else { val };
512 let t = minsd(maxsd(c * 0.0009765625, 0.0), 1.0);
513 x.cvtr_f32_i32(x.addf(x.mulf(x.splatf(t as f32), vdc), vc1))
514 }
515 };
516 sum = x.add32(sum, col);
517 j += 1;
518 }
519 let lo = x.cvtt_f64_i32(x.divd(x.cvt_i32_f64(sum), vcf));
520 let hi = x.cvtt_f64_i32(x.divd(x.cvt_i32_f64(x.shuf32::<0x0E>(sum)), vcf));
521 let q = x.packs32(x.shufps::<0x44>(lo, hi), x.zero());
522 let mut pxv = x.to_i32(x.packus16(q, q)) as u32;
523 if invert {
524 pxv ^= 0x00FF_FFFF;
525 }
526 put(drow, (xa + k as i32 - dx0) as usize, pxv);
527 }
528 xa += npx as i32;
529 }
530 }
531 0
532}
533
534fn julia_render(a: &FxArgs, dst: &mut Dst) -> i32 {
535 fractal_render(a, dst, false)
536}
537
538fn mandel_render(a: &FxArgs, dst: &mut Dst) -> i32 {
539 fractal_render(a, dst, true)
540}
541
542static BLENDS: [&str; 14] = [
545 "normal", "multiply", "additive", "color_burn", "color_dodge", "reflect", "glow", "overlay", "difference", "negation", "lighten",
546 "darken", "screen", "xor",
547];
548static SCHEMES: [&str; 4] = ["classic", "grayscale", "palette", "rainbow"];
549
550static P_CLOUDS: [FxProp; 6] = [
551 FxProp::choice(0, "blend_mode", 13.0, 0.0, &BLENDS),
552 FxProp::new(FP_INT, 0, 1, 0, "scale", 2.0, 1000.0, 250.0),
553 FxProp::new(FP_DEC, 2, 2, 0, "power", 0.0, 1.0, 0.5),
554 FxProp::new(FP_SEED, 0, 3, 0, "seed", 0.0, 0.0, 0.0),
555 FxProp::color(4, "primary_color", 0xFF00_0000),
556 FxProp::color(5, "secondary_color", 0xFFFF_FFFF),
557];
558static P_TURB: [FxProp; 7] = [
559 FxProp::choice(0, "blend_mode", 13.0, 0.0, &BLENDS),
560 FxProp::new(FP_INT, 0, 1, 0, "scale", 2.0, 1000.0, 250.0),
561 FxProp::new(FP_DEC, 2, 2, 0, "roughness", 0.0, 1.0, 0.5),
562 FxProp::new(FP_INT, 0, 3, 0, "detail", 1.0, 12.0, 8.0),
563 FxProp::new(FP_SEED, 0, 4, 0, "seed", 0.0, 0.0, 0.0),
564 FxProp::color(5, "primary_color", 0xFF00_0000),
565 FxProp::color(6, "secondary_color", 0xFFFF_FFFF),
566];
567static P_JULIA: [FxProp; 7] = [
568 FxProp::new(FP_DEC, 2, 0, 0, "factor", 1.0, 10.0, 4.0),
569 FxProp::new(FP_INT, 0, 1, 0, "quality", 1.0, 5.0, 2.0),
570 FxProp::new(FP_DEC, 2, 2, 0, "zoom", 0.0, 50.0, 1.0),
571 FxProp::new(FP_ANGLE, 2, 3, 0, "angle", -180.0, 180.0, 0.0),
572 FxProp::choice(4, "color_scheme", 3.0, 0.0, &SCHEMES),
573 FxProp::color(5, "primary_color", 0xFF00_0000),
574 FxProp::color(6, "secondary_color", 0xFFFF_FFFF),
575];
576static P_MANDEL: [FxProp; 8] = [
577 FxProp::new(FP_INT, 0, 0, 0, "factor", 1.0, 10.0, 1.0),
578 FxProp::new(FP_INT, 0, 1, 0, "quality", 1.0, 5.0, 2.0),
579 FxProp::new(FP_INT, 0, 2, 0, "zoom", 0.0, 50.0, 10.0),
580 FxProp::new(FP_ANGLE, 2, 3, 0, "angle", -180.0, 180.0, 0.0),
581 FxProp::new(FP_BOOL, 0, 4, 0, "invert_colors", 0.0, 1.0, 0.0),
582 FxProp::choice(5, "color_scheme", 3.0, 0.0, &SCHEMES),
583 FxProp::color(6, "primary_color", 0xFF00_0000),
584 FxProp::color(7, "secondary_color", 0xFFFF_FFFF),
585];
586
587const fn desc(id: u32, flags: u32, name: &'static str, render: RenderFn, props: &'static [FxProp]) -> FxDesc {
588 FxDesc { id, flags, name, prepare: None, render, props, category: FXC_RENDER, user: 0 }
589}
590
591pub static FXTAB: [FxDesc; 4] = [
593 desc(FX_CLOUDS, FXF_SEED, "clouds", clouds_render, &P_CLOUDS),
594 desc(FX_JULIA, FXF_SLOW, "julia_fractal", julia_render, &P_JULIA),
595 desc(FX_MANDELBROT, FXF_SLOW, "mandelbrot_fractal", mandel_render, &P_MANDEL),
596 desc(FX_TURBULENCE, FXF_SEED, "turbulence", turb_render, &P_TURB),
597];