1#![no_std]
49#![forbid(unsafe_code)]
50
51extern crate alloc;
52
53pub mod adj;
54pub mod artistic;
55pub mod enhance;
56pub mod blur;
57pub mod color;
58pub mod conv;
59pub mod distort;
60pub mod hist;
61pub mod lochist;
62pub mod lut;
63pub mod noise;
64pub mod object;
65pub mod perlin;
66pub mod photo;
67pub mod render;
68pub mod stylize;
69pub mod warp;
70
71use alloc::boxed::Box;
72use alloc::vec::Vec;
73use core::any::Any;
74
75pub use gfx::tmask::TMask;
76pub use gfx::Surface;
77pub use plat::JobCtl;
78use rt::sync::SpinLock;
79
80pub const FX_NSLOTS: usize = 32; pub const FXF_ADJ: u32 = 1; pub const FXF_PREPARE: u32 = 2; pub const FXF_SEED: u32 = 4; pub const FXF_BANDS: u32 = 8; pub const FXF_WHOLE: u32 = 16; pub const FXF_NO_DIALOG: u32 = 32; pub const FXF_SLOW: u32 = 64; pub const FXC_ADJUST: u32 = 0;
93pub const FXC_ARTISTIC: u32 = 1;
94pub const FXC_BLUR: u32 = 2;
95pub const FXC_COLOR: u32 = 3;
96pub const FXC_DISTORT: u32 = 4;
97pub const FXC_NOISE: u32 = 5;
98pub const FXC_OBJECT: u32 = 6;
99pub const FXC_PHOTO: u32 = 7;
100pub const FXC_RENDER: u32 = 8;
101pub const FXC_STYLIZE: u32 = 9;
102
103pub const FP_INT: u8 = 1; pub const FP_DEC: u8 = 2; pub const FP_BOOL: u8 = 3; pub const FP_CHOICE: u8 = 4; pub const FP_ANGLE: u8 = 5; pub const FP_PAN: u8 = 6; pub const FP_COLOR: u8 = 7; pub const FP_SEED: u8 = 8; pub const FP_BLOB: u8 = 9; pub const FPF_LOG: u8 = 1; pub const FX_AUTO_LEVEL: u32 = 1; pub const FX_BLACK_WHITE: u32 = 2;
116pub const FX_BRIGHT_CONTRAST: u32 = 3;
117pub const FX_CURVES: u32 = 4;
118pub const FX_EXPOSURE: u32 = 5;
119pub const FX_HIGHLIGHTS_SHADOWS: u32 = 6;
120pub const FX_HUE_SATURATION: u32 = 7;
121pub const FX_INVERT_ALPHA: u32 = 8;
122pub const FX_INVERT_COLORS: u32 = 9;
123pub const FX_LEVELS: u32 = 10;
124pub const FX_POSTERIZE: u32 = 11;
125pub const FX_SEPIA: u32 = 12;
126pub const FX_TEMP_TINT: u32 = 13;
127pub const FX_INK_SKETCH: u32 = 20; pub const FX_OIL_PAINTING: u32 = 21;
129pub const FX_PENCIL_SKETCH: u32 = 22;
130pub const FX_BOKEH_BLUR: u32 = 30; pub const FX_FRAGMENT_BLUR: u32 = 31;
132pub const FX_GAUSSIAN_BLUR: u32 = 32;
133pub const FX_MEDIAN: u32 = 33;
134pub const FX_MOTION_BLUR: u32 = 34;
135pub const FX_RADIAL_BLUR: u32 = 35;
136pub const FX_SKETCH_BLUR: u32 = 36;
137pub const FX_SQUARE_BLUR: u32 = 37;
138pub const FX_SURFACE_BLUR: u32 = 38;
139pub const FX_UNFOCUS: u32 = 39;
140pub const FX_ZOOM_BLUR: u32 = 40;
141pub const FX_QUANTIZE: u32 = 50; pub const FX_BULGE: u32 = 60; pub const FX_CRYSTALIZE: u32 = 61;
144pub const FX_DENTS: u32 = 62;
145pub const FX_FROSTED_GLASS: u32 = 63;
146pub const FX_MORPHOLOGY: u32 = 64;
147pub const FX_PIXELATE: u32 = 65;
148pub const FX_POLAR_INVERSION: u32 = 66;
149pub const FX_TILE_REFLECTION: u32 = 67;
150pub const FX_TWIST: u32 = 68;
151pub const FX_ADD_NOISE: u32 = 70; pub const FX_REDUCE_NOISE: u32 = 71;
153pub const FX_DROP_SHADOW: u32 = 80; pub const FX_GLOW: u32 = 90; pub const FX_RED_EYE: u32 = 91;
156pub const FX_SHARPEN: u32 = 92;
157pub const FX_SOFTEN_PORTRAIT: u32 = 93;
158pub const FX_STRAIGHTEN: u32 = 94;
159pub const FX_VIGNETTE: u32 = 95;
160pub const FX_CLOUDS: u32 = 100; pub const FX_JULIA: u32 = 101;
162pub const FX_MANDELBROT: u32 = 102;
163pub const FX_TURBULENCE: u32 = 103;
164pub const FX_EDGE_DETECT: u32 = 110; pub const FX_EMBOSS: u32 = 111;
166pub const FX_OUTLINE: u32 = 112;
167pub const FX_RELIEF: u32 = 113;
168
169pub const ERR_NOMEM: i32 = -8;
170pub const ERR_DIMS: i32 = -12;
171pub const ERR_CANCEL: i32 = -14;
172pub const ERR_LIMIT: i32 = -15;
173
174const FXG_MAX: usize = 16; pub type Aux = Box<dyn Any + Send + Sync>;
180pub type PrepareFn = fn(&FxArgs) -> Result<Option<Aux>, i32>;
182pub type RenderFn = fn(&FxArgs, &mut Dst) -> i32;
184
185pub struct FxDesc {
187 pub id: u32,
188 pub flags: u32,
189 pub name: &'static str,
190 pub prepare: Option<PrepareFn>,
191 pub render: RenderFn,
192 pub props: &'static [FxProp],
193 pub category: u32,
194 pub user: usize,
196}
197
198pub struct FxProp {
201 pub typ: u8,
202 pub dec: u8,
203 pub slot: u8,
204 pub flags: u8,
205 pub name: &'static str,
206 pub min: f64,
207 pub max: f64,
208 pub def: f64,
210 pub def_y: f64,
212 pub choices: &'static [&'static str],
213}
214
215impl FxProp {
216 pub const fn new(typ: u8, dec: u8, slot: u8, flags: u8, name: &'static str, min: f64, max: f64, def: f64) -> FxProp {
218 FxProp { typ, dec, slot, flags, name, min, max, def, def_y: 0.0, choices: &[] }
219 }
220 pub const fn choice(slot: u8, name: &'static str, max: f64, def: f64, choices: &'static [&'static str]) -> FxProp {
222 FxProp { typ: FP_CHOICE, dec: 0, slot, flags: 0, name, min: 0.0, max, def, def_y: 0.0, choices }
223 }
224 pub const fn color(slot: u8, name: &'static str, argb: u32) -> FxProp {
226 FxProp { typ: FP_COLOR, dec: 0, slot, flags: 0, name, min: 0.0, max: 0.0, def: f64::from_bits(argb as u64), def_y: 0.0, choices: &[] }
227 }
228 pub const fn pan(slot: u8, name: &'static str, dec: u8, min: f64, max: f64, x: f64, y: f64) -> FxProp {
230 FxProp { typ: FP_PAN, dec, slot, flags: 0, name, min, max, def: x, def_y: y, choices: &[] }
231 }
232 pub fn def_color(&self) -> u32 {
234 self.def.to_bits() as u32
235 }
236}
237
238#[derive(Clone, Copy)]
240pub struct Params(pub [u64; FX_NSLOTS]);
241
242impl Default for Params {
243 fn default() -> Self {
244 Params([0; FX_NSLOTS])
245 }
246}
247
248impl Params {
249 #[inline]
251 pub fn i(&self, slot: usize) -> i64 {
252 self.0[slot] as i64
253 }
254 #[inline]
256 pub fn f(&self, slot: usize) -> f64 {
257 f64::from_bits(self.0[slot])
258 }
259 #[inline]
261 pub fn int(&self, slot: usize, lo: i32, hi: i32) -> i32 {
262 self.i(slot).max(lo as i64).min(hi as i64) as i32
263 }
264 #[inline]
266 pub fn dbl(&self, slot: usize, lo: f64, hi: f64) -> f64 {
267 let v = self.f(slot);
268 let v = if v.is_nan() { lo } else { v };
269 rt::fx::minsd(rt::fx::maxsd(v, lo), hi)
270 }
271 #[inline]
273 pub fn byte(&self, slot: usize, i: usize) -> u8 {
274 let k = slot * 8 + i;
275 (self.0[k / 8] >> ((k % 8) * 8)) as u8
276 }
277 pub fn set_i(&mut self, slot: usize, v: i64) {
278 self.0[slot] = v as u64;
279 }
280 pub fn set_f(&mut self, slot: usize, v: f64) {
281 self.0[slot] = v.to_bits();
282 }
283 pub fn set_byte(&mut self, slot: usize, i: usize, v: u8) {
284 let k = slot * 8 + i;
285 let sh = (k % 8) * 8;
286 self.0[k / 8] = self.0[k / 8] & !(0xFFu64 << sh) | (v as u64) << sh;
287 }
288}
289
290pub trait Cancel {
293 fn cancelled(&self) -> bool;
294}
295
296impl Cancel for JobCtl {
297 #[inline]
298 fn cancelled(&self) -> bool {
299 JobCtl::cancelled(self)
300 }
301}
302
303impl Cancel for core::sync::atomic::AtomicBool {
304 #[inline]
305 fn cancelled(&self) -> bool {
306 self.load(core::sync::atomic::Ordering::Relaxed)
307 }
308}
309
310impl Cancel for core::sync::atomic::AtomicU32 {
311 #[inline]
312 fn cancelled(&self) -> bool {
313 self.load(core::sync::atomic::Ordering::Relaxed) != 0
314 }
315}
316
317#[derive(Clone, Copy)]
319pub struct FxArgs<'a> {
320 pub src: &'a Surface,
321 pub x0: i32,
322 pub y0: i32,
323 pub x1: i32,
324 pub y1: i32,
325 pub params: &'a Params,
326 pub aux: Option<&'a (dyn Any + Send + Sync)>,
327 pub job: Option<&'a (dyn Cancel + Sync)>,
328 pub sel: Option<&'a TMask>,
329}
330
331impl<'a> FxArgs<'a> {
332 pub fn new(src: &'a Surface, roi: [i32; 4], params: &'a Params) -> FxArgs<'a> {
334 FxArgs { src, x0: roi[0], y0: roi[1], x1: roi[2], y1: roi[3], params, aux: None, job: None, sel: None }
335 }
336 #[inline]
338 pub fn cancelled(&self) -> bool {
339 match self.job {
340 Some(j) => j.cancelled(),
341 None => false,
342 }
343 }
344 #[inline]
346 pub fn aux<T: 'static>(&self) -> Option<&'a T> {
347 self.aux?.downcast_ref::<T>()
348 }
349 #[inline]
351 pub fn src_row(&self, y: i32) -> &'a [u8] {
352 row(self.src, y)
353 }
354}
355
356#[inline]
358pub fn row(sf: &Surface, y: i32) -> &[u8] {
359 let o = y as usize * sf.stride as usize;
360 &sf.px[o..o + sf.w as usize * 4]
361}
362
363#[inline(always)]
365pub fn px(b: &[u8], i: usize) -> u32 {
366 let q: [u8; 4] = b[4 * i..4 * i + 4].try_into().unwrap();
367 u32::from_le_bytes(q)
368}
369
370#[inline(always)]
372pub fn put(b: &mut [u8], i: usize, v: u32) {
373 b[4 * i..4 * i + 4].copy_from_slice(&v.to_le_bytes());
374}
375
376pub struct Dst<'a> {
378 rows: Rows<'a>,
379 pub x0: i32,
380 pub y0: i32,
381 pub x1: i32,
382 pub y1: i32,
383}
384
385enum Rows<'a> {
386 List(Vec<&'a mut [u8]>),
388 Table(&'a mut [&'a mut [u8]]),
390 Strided(&'a mut [u8], usize),
392}
393
394impl<'a> Dst<'a> {
395 pub fn new(roi: [i32; 4], rows: Vec<&'a mut [u8]>) -> Dst<'a> {
397 Dst { rows: Rows::List(rows), x0: roi[0], y0: roi[1], x1: roi[2], y1: roi[3] }
398 }
399
400 pub fn strided(roi: [i32; 4], buf: &'a mut [u8], stride: usize) -> Dst<'a> {
403 Dst { rows: Rows::Strided(buf, stride), x0: roi[0], y0: roi[1], x1: roi[2], y1: roi[3] }
404 }
405
406 pub fn of_surface(sf: &'a mut Surface, roi: [i32; 4]) -> Dst<'a> {
408 let (w, h) = (sf.w, sf.h);
409 let x0 = roi[0].clamp(0, w);
410 let y0 = roi[1].clamp(0, h);
411 let (x1, y1) = (roi[2].clamp(x0, w), roi[3].clamp(y0, h));
412 let stride = sf.stride as usize;
413 if x1 <= x0 || y1 <= y0 {
414 return Dst { rows: Rows::List(Vec::new()), x0, y0, x1, y1 };
415 }
416 let start = y0 as usize * stride + x0 as usize * 4;
417 let end = (y1 - 1) as usize * stride + x1 as usize * 4;
418 Dst { rows: Rows::Strided(&mut sf.px[start..end], stride), x0, y0, x1, y1 }
419 }
420
421 #[inline]
423 pub fn row(&mut self, y: i32) -> &mut [u8] {
424 let k = (y - self.y0) as usize;
425 match &mut self.rows {
426 Rows::List(v) => v[k],
427 Rows::Table(t) => t[k],
428 Rows::Strided(b, st) => {
429 let n = (self.x1 - self.x0) as usize * 4;
430 &mut b[k * *st..k * *st + n]
431 }
432 }
433 }
434}
435
436pub fn tiles<'a>(sf: &'a mut Surface, rects: &[[i32; 4]], store: &'a mut Vec<&'a mut [u8]>) -> Vec<Dst<'a>> {
442 let (w, h) = (sf.w, sf.h);
443 let stride = sf.stride as usize;
444 let cl: Vec<[i32; 4]> = rects
445 .iter()
446 .map(|r| {
447 let x0 = r[0].clamp(0, w);
448 let y0 = r[1].clamp(0, h);
449 [x0, y0, r[2].clamp(x0, w), r[3].clamp(y0, h)]
450 })
451 .collect();
452 let nrows = |r: &[i32; 4]| if r[2] > r[0] && r[3] > r[1] { (r[3] - r[1]) as usize } else { 0 };
454 let mut base = Vec::with_capacity(cl.len() + 1);
456 let mut total = 0usize;
457 for r in &cl {
458 base.push(total);
459 total += nrows(r);
460 }
461 base.push(total);
462 store.clear();
463 store.resize_with(total, Default::default);
464 let mut order: Vec<usize> = (0..cl.len()).filter(|&i| nrows(&cl[i]) > 0).collect();
466 if !order.is_empty() && stride != 0 {
467 order.sort_by_key(|&i| (cl[i][1], cl[i][0]));
468 let ymin = cl[order[0]][1];
469 let ymax = order.iter().map(|&i| cl[i][3]).max().unwrap_or(0);
470 let mut active: Vec<usize> = Vec::new();
471 let mut next = 0usize;
472 let lines = sf.px.chunks_mut(stride).take(ymax as usize).skip(ymin as usize);
473 for (k, line) in lines.enumerate() {
474 let y = ymin + k as i32;
475 active.retain(|&i| y < cl[i][3]);
476 while next < order.len() && cl[order[next]][1] == y {
477 let i = order[next];
478 let at = active.partition_point(|&j| cl[j][0] < cl[i][0]);
479 active.insert(at, i);
480 next += 1;
481 }
482 let (mut rest, _) = line.split_at_mut(w as usize * 4);
483 let mut at = 0i32; for &i in &active {
485 let (x0, x1) = (cl[i][0], cl[i][2]);
486 assert!(x0 >= at, "fx::tiles: overlapping rectangles");
487 let tmp = core::mem::take(&mut rest);
488 let (_, tail) = tmp.split_at_mut((x0 - at) as usize * 4);
489 let (mine, tail) = tail.split_at_mut((x1 - x0) as usize * 4);
490 store[base[i] + (y - cl[i][1]) as usize] = mine;
491 rest = tail;
492 at = x1;
493 }
494 }
495 }
496 let mut rest: &'a mut [&'a mut [u8]] = &mut store[..];
497 let mut out: Vec<Dst<'a>> = Vec::with_capacity(cl.len());
498 for (i, r) in cl.iter().enumerate() {
499 let (mine, tail) = core::mem::take(&mut rest).split_at_mut(base[i + 1] - base[i]);
500 rest = tail;
501 out.push(Dst { rows: Rows::Table(mine), x0: r[0], y0: r[1], x1: r[2], y1: r[3] });
502 }
503 out
504}
505
506#[inline]
509pub fn rows(a: &FxArgs, dst: &mut Dst, mut f: impl FnMut(&mut [u8], &[u8], i32, i32)) -> i32 {
510 if a.x1 - a.x0 <= 0 {
511 return 0;
512 }
513 let (b0, b1) = (a.x0 as usize * 4, a.x1 as usize * 4);
514 for y in a.y0..a.y1 {
515 if a.cancelled() {
516 return ERR_CANCEL;
517 }
518 let s = &a.src_row(y)[b0..b1];
519 f(dst.row(y), s, a.x0, y);
520 }
521 0
522}
523
524static REG: SpinLock<Vec<&'static [FxDesc]>> = SpinLock::new(Vec::new());
527
528pub fn register(group: &'static [FxDesc]) -> i32 {
530 let mut r = REG.lock();
531 if r.iter().any(|g| core::ptr::eq(*g, group)) {
532 return 0;
533 }
534 if r.len() >= FXG_MAX {
535 return ERR_LIMIT;
536 }
537 r.push(group);
538 0
539}
540
541pub fn use_all() {
543 for g in [
544 &adj::FXTAB as &'static [FxDesc],
545 &artistic::FXTAB,
546 &blur::FXTAB,
547 &color::FXTAB,
548 &distort::FXTAB,
549 &noise::FXTAB,
550 &object::FXTAB,
551 &photo::FXTAB,
552 &render::FXTAB,
553 &stylize::FXTAB,
554 &enhance::FXTAB,
555 ] {
556 register(g);
557 }
558}
559
560pub fn count() -> usize {
562 REG.lock().iter().map(|g| g.len()).sum()
563}
564
565pub fn get(i: usize) -> Option<&'static FxDesc> {
567 let r = REG.lock();
568 let mut i = i;
569 for g in r.iter() {
570 if i < g.len() {
571 return Some(&g[i]);
572 }
573 i -= g.len();
574 }
575 None
576}
577
578pub fn find(name: &str) -> Option<&'static FxDesc> {
580 (0..).map_while(get).find(|d| d.name == name)
581}
582
583pub fn find_id(id: u32) -> Option<&'static FxDesc> {
585 (0..).map_while(get).find(|d| d.id == id)
586}
587
588pub fn defaults(desc: &FxDesc, p: &mut Params) {
590 p.0 = [0; FX_NSLOTS];
591 for pr in desc.props {
592 let s = pr.slot as usize;
593 if s >= FX_NSLOTS {
594 continue;
595 }
596 match pr.typ {
597 FP_DEC | FP_ANGLE => p.0[s] = pr.def.to_bits(),
598 FP_PAN => {
599 p.0[s] = pr.def.to_bits();
600 if s + 1 < FX_NSLOTS {
601 p.0[s + 1] = pr.def_y.to_bits();
602 }
603 }
604 FP_COLOR => p.0[s] = pr.def.to_bits() & 0xFFFF_FFFF,
605 _ => p.0[s] = rt::fx::cvttd64(pr.def) as u64,
606 }
607 }
608}
609
610#[inline]
612pub fn hash32(x: u32, y: u32, seed: u32, stream: u32) -> u32 {
613 let mut h = x.wrapping_mul(0x8DA6B343)
614 ^ y.wrapping_mul(0xD8163841)
615 ^ seed.wrapping_mul(0xCB1AB31F)
616 ^ stream.wrapping_mul(0x165667B1);
617 h ^= h >> 16;
618 h = h.wrapping_mul(0x85EBCA6B);
619 h ^= h >> 13;
620 h = h.wrapping_mul(0xC2B2AE35);
621 h ^ (h >> 16)
622}