1 | // Copyright 2020 the Resvg Authors |
2 | // SPDX-License-Identifier: Apache-2.0 OR MIT |
3 | |
4 | #![allow (clippy::needless_range_loop)] |
5 | |
6 | use super::{f32_bound, ImageRefMut}; |
7 | use usvg::ApproxZeroUlps; |
8 | |
9 | const RAND_M: i32 = 2147483647; // 2**31 - 1 |
10 | const RAND_A: i32 = 16807; // 7**5; primitive root of m |
11 | const RAND_Q: i32 = 127773; // m / a |
12 | const RAND_R: i32 = 2836; // m % a |
13 | const B_SIZE: usize = 0x100; |
14 | const B_SIZE_32: i32 = 0x100; |
15 | const B_LEN: usize = B_SIZE + B_SIZE + 2; |
16 | const BM: i32 = 0xff; |
17 | const PERLIN_N: i32 = 0x1000; |
18 | |
19 | #[derive (Clone, Copy)] |
20 | struct StitchInfo { |
21 | width: i32, // How much to subtract to wrap for stitching. |
22 | height: i32, |
23 | wrap_x: i32, // Minimum value to wrap. |
24 | wrap_y: i32, |
25 | } |
26 | |
27 | /// Applies a turbulence filter. |
28 | /// |
29 | /// `dest` image pixels will have an **unpremultiplied alpha**. |
30 | /// |
31 | /// - `offset_x` and `offset_y` indicate filter region offset. |
32 | /// - `sx` and `sy` indicate canvas scale. |
33 | pub fn apply( |
34 | offset_x: f64, |
35 | offset_y: f64, |
36 | sx: f64, |
37 | sy: f64, |
38 | base_frequency_x: f64, |
39 | base_frequency_y: f64, |
40 | num_octaves: u32, |
41 | seed: i32, |
42 | stitch_tiles: bool, |
43 | fractal_noise: bool, |
44 | dest: ImageRefMut, |
45 | ) { |
46 | let (lattice_selector, gradient) = init(seed); |
47 | let width = dest.width; |
48 | let height = dest.height; |
49 | let mut x = 0; |
50 | let mut y = 0; |
51 | for pixel in dest.data.iter_mut() { |
52 | let turb = |channel| { |
53 | let (tx, ty) = ((x as f64 + offset_x) / sx, (y as f64 + offset_y) / sy); |
54 | let n = turbulence( |
55 | channel, |
56 | tx, |
57 | ty, |
58 | x as f64, |
59 | y as f64, |
60 | width as f64, |
61 | height as f64, |
62 | base_frequency_x, |
63 | base_frequency_y, |
64 | num_octaves, |
65 | fractal_noise, |
66 | stitch_tiles, |
67 | &lattice_selector, |
68 | &gradient, |
69 | ); |
70 | |
71 | let n = if fractal_noise { |
72 | (n * 255.0 + 255.0) / 2.0 |
73 | } else { |
74 | n * 255.0 |
75 | }; |
76 | |
77 | (f32_bound(0.0, n as f32, 255.0) + 0.5) as u8 |
78 | }; |
79 | |
80 | pixel.r = turb(0); |
81 | pixel.g = turb(1); |
82 | pixel.b = turb(2); |
83 | pixel.a = turb(3); |
84 | |
85 | x += 1; |
86 | if x == dest.width { |
87 | x = 0; |
88 | y += 1; |
89 | } |
90 | } |
91 | } |
92 | |
93 | fn init(mut seed: i32) -> (Vec<usize>, Vec<Vec<Vec<f64>>>) { |
94 | let mut lattice_selector = vec![0; B_LEN]; |
95 | let mut gradient = vec![vec![vec![0.0; 2]; B_LEN]; 4]; |
96 | |
97 | if seed <= 0 { |
98 | seed = -seed % (RAND_M - 1) + 1; |
99 | } |
100 | |
101 | if seed > RAND_M - 1 { |
102 | seed = RAND_M - 1; |
103 | } |
104 | |
105 | for k in 0..4 { |
106 | for i in 0..B_SIZE { |
107 | lattice_selector[i] = i; |
108 | for j in 0..2 { |
109 | seed = random(seed); |
110 | gradient[k][i][j] = |
111 | ((seed % (B_SIZE_32 + B_SIZE_32)) - B_SIZE_32) as f64 / B_SIZE_32 as f64; |
112 | } |
113 | |
114 | let s = (gradient[k][i][0] * gradient[k][i][0] + gradient[k][i][1] * gradient[k][i][1]) |
115 | .sqrt(); |
116 | |
117 | gradient[k][i][0] /= s; |
118 | gradient[k][i][1] /= s; |
119 | } |
120 | } |
121 | |
122 | for i in (1..B_SIZE).rev() { |
123 | let k = lattice_selector[i]; |
124 | seed = random(seed); |
125 | let j = (seed % B_SIZE_32) as usize; |
126 | lattice_selector[i] = lattice_selector[j]; |
127 | lattice_selector[j] = k; |
128 | } |
129 | |
130 | for i in 0..B_SIZE + 2 { |
131 | lattice_selector[B_SIZE + i] = lattice_selector[i]; |
132 | for g in gradient.iter_mut().take(4) { |
133 | for j in 0..2 { |
134 | g[B_SIZE + i][j] = g[i][j]; |
135 | } |
136 | } |
137 | } |
138 | |
139 | (lattice_selector, gradient) |
140 | } |
141 | |
142 | fn turbulence( |
143 | color_channel: usize, |
144 | mut x: f64, |
145 | mut y: f64, |
146 | tile_x: f64, |
147 | tile_y: f64, |
148 | tile_width: f64, |
149 | tile_height: f64, |
150 | mut base_freq_x: f64, |
151 | mut base_freq_y: f64, |
152 | num_octaves: u32, |
153 | fractal_sum: bool, |
154 | do_stitching: bool, |
155 | lattice_selector: &[usize], |
156 | gradient: &[Vec<Vec<f64>>], |
157 | ) -> f64 { |
158 | // Adjust the base frequencies if necessary for stitching. |
159 | let mut stitch = if do_stitching { |
160 | // When stitching tiled turbulence, the frequencies must be adjusted |
161 | // so that the tile borders will be continuous. |
162 | if !base_freq_x.approx_zero_ulps(4) { |
163 | let lo_freq = (tile_width * base_freq_x).floor() / tile_width; |
164 | let hi_freq = (tile_width * base_freq_x).ceil() / tile_width; |
165 | if base_freq_x / lo_freq < hi_freq / base_freq_x { |
166 | base_freq_x = lo_freq; |
167 | } else { |
168 | base_freq_x = hi_freq; |
169 | } |
170 | } |
171 | |
172 | if !base_freq_y.approx_zero_ulps(4) { |
173 | let lo_freq = (tile_height * base_freq_y).floor() / tile_height; |
174 | let hi_freq = (tile_height * base_freq_y).ceil() / tile_height; |
175 | if base_freq_y / lo_freq < hi_freq / base_freq_y { |
176 | base_freq_y = lo_freq; |
177 | } else { |
178 | base_freq_y = hi_freq; |
179 | } |
180 | } |
181 | |
182 | // Set up initial stitch values. |
183 | let width = (tile_width * base_freq_x + 0.5) as i32; |
184 | let height = (tile_height * base_freq_y + 0.5) as i32; |
185 | let wrap_x = (tile_x * base_freq_x + PERLIN_N as f64 + width as f64) as i32; |
186 | let wrap_y = (tile_y * base_freq_y + PERLIN_N as f64 + height as f64) as i32; |
187 | Some(StitchInfo { |
188 | width, |
189 | height, |
190 | wrap_x, |
191 | wrap_y, |
192 | }) |
193 | } else { |
194 | None |
195 | }; |
196 | |
197 | let mut sum = 0.0; |
198 | x *= base_freq_x; |
199 | y *= base_freq_y; |
200 | let mut ratio = 1.0; |
201 | for _ in 0..num_octaves { |
202 | if fractal_sum { |
203 | sum += noise2(color_channel, x, y, lattice_selector, gradient, stitch) / ratio; |
204 | } else { |
205 | sum += noise2(color_channel, x, y, lattice_selector, gradient, stitch).abs() / ratio; |
206 | } |
207 | x *= 2.0; |
208 | y *= 2.0; |
209 | ratio *= 2.0; |
210 | |
211 | if let Some(ref mut stitch) = stitch { |
212 | // Update stitch values. Subtracting PerlinN before the multiplication and |
213 | // adding it afterward simplifies to subtracting it once. |
214 | stitch.width *= 2; |
215 | stitch.wrap_x = 2 * stitch.wrap_x - PERLIN_N; |
216 | stitch.height *= 2; |
217 | stitch.wrap_y = 2 * stitch.wrap_y - PERLIN_N; |
218 | } |
219 | } |
220 | |
221 | sum |
222 | } |
223 | |
224 | fn noise2( |
225 | color_channel: usize, |
226 | x: f64, |
227 | y: f64, |
228 | lattice_selector: &[usize], |
229 | gradient: &[Vec<Vec<f64>>], |
230 | stitch_info: Option<StitchInfo>, |
231 | ) -> f64 { |
232 | let t = x + PERLIN_N as f64; |
233 | let mut bx0 = t as i32; |
234 | let mut bx1 = bx0 + 1; |
235 | let rx0 = t - t as i64 as f64; |
236 | let rx1 = rx0 - 1.0; |
237 | let t = y + PERLIN_N as f64; |
238 | let mut by0 = t as i32; |
239 | let mut by1 = by0 + 1; |
240 | let ry0 = t - t as i64 as f64; |
241 | let ry1 = ry0 - 1.0; |
242 | |
243 | // If stitching, adjust lattice points accordingly. |
244 | if let Some(info) = stitch_info { |
245 | if bx0 >= info.wrap_x { |
246 | bx0 -= info.width; |
247 | } |
248 | |
249 | if bx1 >= info.wrap_x { |
250 | bx1 -= info.width; |
251 | } |
252 | |
253 | if by0 >= info.wrap_y { |
254 | by0 -= info.height; |
255 | } |
256 | |
257 | if by1 >= info.wrap_y { |
258 | by1 -= info.height; |
259 | } |
260 | } |
261 | |
262 | bx0 &= BM; |
263 | bx1 &= BM; |
264 | by0 &= BM; |
265 | by1 &= BM; |
266 | let i = lattice_selector[bx0 as usize]; |
267 | let j = lattice_selector[bx1 as usize]; |
268 | let b00 = lattice_selector[i + by0 as usize]; |
269 | let b10 = lattice_selector[j + by0 as usize]; |
270 | let b01 = lattice_selector[i + by1 as usize]; |
271 | let b11 = lattice_selector[j + by1 as usize]; |
272 | let sx = s_curve(rx0); |
273 | let sy = s_curve(ry0); |
274 | let q = &gradient[color_channel][b00]; |
275 | let u = rx0 * q[0] + ry0 * q[1]; |
276 | let q = &gradient[color_channel][b10]; |
277 | let v = rx1 * q[0] + ry0 * q[1]; |
278 | let a = lerp(sx, u, v); |
279 | let q = &gradient[color_channel][b01]; |
280 | let u = rx0 * q[0] + ry1 * q[1]; |
281 | let q = &gradient[color_channel][b11]; |
282 | let v = rx1 * q[0] + ry1 * q[1]; |
283 | let b = lerp(sx, u, v); |
284 | lerp(sy, a, b) |
285 | } |
286 | |
287 | fn random(seed: i32) -> i32 { |
288 | let mut result: i32 = RAND_A * (seed % RAND_Q) - RAND_R * (seed / RAND_Q); |
289 | if result <= 0 { |
290 | result += RAND_M; |
291 | } |
292 | |
293 | result |
294 | } |
295 | |
296 | #[inline ] |
297 | fn s_curve(t: f64) -> f64 { |
298 | t * t * (3.0 - 2.0 * t) |
299 | } |
300 | |
301 | #[inline ] |
302 | fn lerp(t: f64, a: f64, b: f64) -> f64 { |
303 | a + t * (b - a) |
304 | } |
305 | |