1use std::sync::atomic::{AtomicUsize, Ordering};
2
3use image::{Rgba, RgbaImage};
4#[cfg(not(target_arch = "wasm32"))]
5use rayon::prelude::*;
6
7use crate::{
8 error::{Error, Result},
9 input::{Input, Inputs},
10 mapping::Mapping,
11 pixer::{Pixer, sample_linear_opaque, sample_linear_premultiplied},
12};
13
14static RENDER_COUNT: AtomicUsize = AtomicUsize::new(0);
15
16const RR: f64 = 2048.0; #[derive(Debug, Clone, Copy, PartialEq, Eq)]
19#[repr(u8)]
20pub enum RenderQuality {
21 None,
22 Simple,
23 Sampled,
24}
25
26impl Default for RenderQuality {
27 fn default() -> Self {
28 RenderQuality::Sampled
29 }
30}
31
32#[derive(Debug, Clone)]
33pub struct CloudPoint {
34 pub layer: u8,
35 pub x: f64,
36 pub y: f64,
37}
38
39pub struct Render {
40 mapping: Option<Mapping>,
41 out: RgbaImage,
42 out_scaled: Option<RgbaImage>,
43 quality: RenderQuality,
44}
45
46impl Render {
47 pub fn new(quality: RenderQuality) -> Self {
48 Render { mapping: None, out: RgbaImage::new(1, 1), out_scaled: None, quality }
49 }
50
51 pub fn with_default_quality() -> Self {
52 Self::new(RenderQuality::Sampled)
53 }
54
55 pub fn attach_mapping(&mut self, mapping: Mapping) {
56 self.mapping = Some(mapping);
57 }
58
59 fn check(&self) -> Result<&Mapping> {
60 self.mapping.as_ref().ok_or(Error::NoMapping)
61 }
62
63 fn pre(&mut self) -> Result<()> {
64 let mapping = self.check()?;
65 self.out = mapping.neutral.clone();
66 Ok(())
67 }
68
69 fn add(&mut self, input: &Input) -> Result<()> {
71 let mapping = self.mapping.as_ref().ok_or(Error::NoMapping)?;
72
73 let active_scale = input.in_scale as f64 / 2.0;
74 let w = mapping.light.width() as i32;
75 let h = mapping.light.height() as i32;
76
77 if (mapping.map1.width() as i32) < w {
78 return Ok(());
79 }
80
81 let off = ((mapping.map2.height() as i32 - h) / 2) as i32;
82
83 if mapping.map1.width() != mapping.neutral.width() {
84 return Ok(());
85 }
86 if mapping.map2.width() != mapping.neutral.width() {
87 return Ok(());
88 }
89
90 let out_w = self.out.width() as usize;
91 let _out_h = self.out.height() as usize;
92 let input_img = input.get();
93 let input_opaque = input.is_opaque();
94
95 let light_raw = mapping.light.as_raw();
96 let dark_raw = mapping.dark.as_raw();
97 let map1_raw = mapping.map1.as_raw();
98 let map2_raw = mapping.map2.as_raw();
99
100 let row_stride = out_w * 4;
101 let map_stride = mapping.map1.width() as usize * 4;
102 let out_raw = self.out.as_mut();
103
104 #[cfg(not(target_arch = "wasm32"))]
105 let iter = out_raw.par_chunks_mut(row_stride).enumerate();
106 #[cfg(target_arch = "wasm32")]
107 let iter = out_raw.chunks_mut(row_stride).enumerate();
108
109 iter.for_each(|(y, row)| {
110 let map_y_base = (y as i32 + off) as u32;
111 if map_y_base >= mapping.map1.height() || map_y_base >= mapping.map2.height() {
112 return;
113 }
114
115 for x in 0..out_w {
116 let idx = x * 4;
117 let light_idx = y * row_stride + idx;
118 let map_idx = map_y_base as usize * map_stride + idx;
119
120 let light_pixel = &light_raw[light_idx..light_idx + 4];
121 let dark_pixel = &dark_raw[light_idx..light_idx + 4];
122 let map_pixel = &map1_raw[map_idx..map_idx + 4];
123 let sel_pixel = &map2_raw[map_idx..map_idx + 4];
124
125 if sel_pixel[0] != input.layer {
126 continue;
127 }
128
129 let act = map_pixel[3] as i32;
130 if act <= 25 {
131 continue;
132 }
133
134 let b_val = map_pixel[2] as i32;
135 let ymod = b_val / 16;
136 let xmod = b_val % 16;
137 let x1 = map_pixel[0] as f64 + 256.0 * xmod as f64 - RR;
138 let y1 = map_pixel[1] as f64 + 256.0 * ymod as f64 - RR;
139
140 let mut x12 = x1;
141 let mut y12 = y1;
142 let mut x13 = x1;
143 let mut y13 = y1;
144
145 if (x as i32) < w - 1 && (y as i32) < h - 1 {
146 let mdx_idx = map_idx + 4;
147 let mdy_idx = map_idx + map_stride;
148 let mdx = &map1_raw[mdx_idx..mdx_idx + 4];
149 let mdy = &map1_raw[mdy_idx..mdy_idx + 4];
150
151 if mdx[3] > 127 && mdy[3] > 127 {
152 let idx2 = &map2_raw[mdx_idx..mdx_idx + 4];
153 let idx3 = &map2_raw[mdy_idx..mdy_idx + 4];
154
155 if idx2[0] == input.layer && idx3[0] == input.layer {
156 let mod2 = mdx[2] as i32;
157 let ymod2 = mod2 / 16;
158 let xmod2 = mod2 % 16;
159 x12 = mdx[0] as f64 + 256.0 * xmod2 as f64 - RR;
160 y12 = mdx[1] as f64 + 256.0 * ymod2 as f64 - RR;
161
162 let mod3 = mdy[2] as i32;
163 let ymod3 = mod3 / 16;
164 let xmod3 = mod3 % 16;
165 x13 = mdy[0] as f64 + 256.0 * xmod3 as f64 - RR;
166 y13 = mdy[1] as f64 + 256.0 * ymod3 as f64 - RR;
167 }
168
169 let dax = x1 - x12;
173 let day = y1 - y12;
174 let dbx = x1 - x13;
175 let dby = y1 - y13;
176 if dax * dax + day * day > 160_000.0 || dbx * dbx + dby * dby > 160_000.0 {
177 x12 = x1;
178 y12 = y1;
179 x13 = x1;
180 y13 = y1;
181 }
182 }
183 }
184
185 let x1s = x1 * input.xs;
186 let y1s = y1 * input.ys;
187 let xx_rot = input.xa * x1s + input.ya * y1s;
188 let yy_rot = -input.ya * x1s + input.xa * y1s;
189 let xx = input.in_x0 + active_scale * (xx_rot + RR + input.xo) / RR;
190 let yy = input.in_y0 + active_scale * (yy_rot + RR + input.yo) / RR;
191
192 let x12s = x12 * input.xs;
193 let y12s = y12 * input.ys;
194 let xxa_rot = input.xa * x12s + input.ya * y12s;
195 let yya_rot = -input.ya * x12s + input.xa * y12s;
196 let xxa = input.in_x0 + active_scale * (xxa_rot + RR + input.xo) / RR - xx;
197 let yya = input.in_y0 + active_scale * (yya_rot + RR + input.yo) / RR - yy;
198
199 let x13s = x13 * input.xs;
200 let y13s = y13 * input.ys;
201 let xxb_rot = input.xa * x13s + input.ya * y13s;
202 let yyb_rot = -input.ya * x13s + input.xa * y13s;
203 let xxb = input.in_x0 + active_scale * (xxb_rot + RR + input.xo) / RR - xx;
204 let yyb = input.in_y0 + active_scale * (yyb_rot + RR + input.yo) / RR - yy;
205
206 let (mo, m2, m3, m4, m5, m2b, m3b, m4b, m5b) = if input_opaque {
207 (
208 sample_linear_opaque(input_img, xx, yy),
209 sample_linear_opaque(input_img, xx + xxa / 2.0, yy + yya / 2.0),
210 sample_linear_opaque(input_img, xx - xxa / 2.0, yy - yya / 2.0),
211 sample_linear_opaque(input_img, xx + xxb / 2.0, yy + yyb / 2.0),
212 sample_linear_opaque(input_img, xx - xxb / 2.0, yy - yyb / 2.0),
213 sample_linear_opaque(
214 input_img,
215 xx + (xxa + xxb) / 2.0,
216 yy + (yya + yyb) / 2.0,
217 ),
218 sample_linear_opaque(
219 input_img,
220 xx + (xxa - xxb) / 2.0,
221 yy + (yya - yyb) / 2.0,
222 ),
223 sample_linear_opaque(
224 input_img,
225 xx - (xxa + xxb) / 2.0,
226 yy - (yya + yyb) / 2.0,
227 ),
228 sample_linear_opaque(
229 input_img,
230 xx - (xxa - xxb) / 2.0,
231 yy - (yya - yyb) / 2.0,
232 ),
233 )
234 } else {
235 (
236 sample_linear_premultiplied(input_img, xx, yy),
237 sample_linear_premultiplied(input_img, xx + xxa / 2.0, yy + yya / 2.0),
238 sample_linear_premultiplied(input_img, xx - xxa / 2.0, yy - yya / 2.0),
239 sample_linear_premultiplied(input_img, xx + xxb / 2.0, yy + yyb / 2.0),
240 sample_linear_premultiplied(input_img, xx - xxb / 2.0, yy - yyb / 2.0),
241 sample_linear_premultiplied(
242 input_img,
243 xx + (xxa + xxb) / 2.0,
244 yy + (yya + yyb) / 2.0,
245 ),
246 sample_linear_premultiplied(
247 input_img,
248 xx + (xxa - xxb) / 2.0,
249 yy + (yya - yyb) / 2.0,
250 ),
251 sample_linear_premultiplied(
252 input_img,
253 xx - (xxa + xxb) / 2.0,
254 yy - (yya + yyb) / 2.0,
255 ),
256 sample_linear_premultiplied(
257 input_img,
258 xx - (xxa - xxb) / 2.0,
259 yy - (yya - yyb) / 2.0,
260 ),
261 )
262 };
263
264 let sc = (mo.a * 4.0
265 + (m2.a + m3.a + m4.a + m5.a) * 2.0
266 + (m2b.a + m3b.a + m4b.a + m5b.a))
267 / 16.0;
268
269 let mut mo =
270 (mo * 4.0 + (m2 + m3 + m4 + m5) * 2.0 + (m2b + m3b + m4b + m5b)) / 16.0;
271
272 if sc > 0.0001 {
273 mo.postblend(sc);
274 } else {
275 mo.r = 0.0;
276 mo.g = 0.0;
277 mo.b = 0.0;
278 mo.a = 0.0;
279 }
280
281 let m_r = mo.r as i32;
282 let m_g = mo.g as i32;
283 let m_b = mo.b as i32;
284 let m_a = mo.a as i32;
285
286 let result_r = dark_pixel[0] as i32
287 + ((light_pixel[0] as i32 - dark_pixel[0] as i32) * m_r) / 255;
288 let result_g = dark_pixel[1] as i32
289 + ((light_pixel[1] as i32 - dark_pixel[1] as i32) * m_g) / 255;
290 let result_b = dark_pixel[2] as i32
291 + ((light_pixel[2] as i32 - dark_pixel[2] as i32) * m_b) / 255;
292 let mut result_a = m_a;
293
294 if (dark_pixel[3] as i32) < result_a {
295 result_a = dark_pixel[3] as i32;
296 }
297
298 if result_a > 0 {
299 let idx = x * 4;
300 if result_a > 250 {
301 row[idx] = result_r.clamp(0, 255) as u8;
302 row[idx + 1] = result_g.clamp(0, 255) as u8;
303 row[idx + 2] = result_b.clamp(0, 255) as u8;
304 } else {
305 row[idx] = (row[idx] as i32
306 + ((result_r - row[idx] as i32) * result_a) / 255)
307 .clamp(0, 255) as u8;
308 row[idx + 1] = (row[idx + 1] as i32
309 + ((result_g - row[idx + 1] as i32) * result_a) / 255)
310 .clamp(0, 255) as u8;
311 row[idx + 2] = (row[idx + 2] as i32
312 + ((result_b - row[idx + 2] as i32) * result_a) / 255)
313 .clamp(0, 255) as u8;
314 }
315 }
316 }
317 });
318
319 Ok(())
320 }
321
322 fn add_simple(&mut self, input: &Input) -> Result<()> {
323 let mapping = self.mapping.as_ref().ok_or(Error::NoMapping)?;
324
325 let active_scale = input.in_scale as f64 / 2.0;
326 let off = ((mapping.map2.height() as i32 - mapping.light.height() as i32) / 2) as i32;
327
328 if (mapping.map1.width() as i32) < (mapping.light.width() as i32) {
329 return Ok(());
330 }
331
332 let out_w = self.out.width();
333 let out_h = self.out.height();
334
335 for y in 0..out_h {
336 for x in 0..out_w {
337 let light_pixel = mapping.light.get_pixel(x, y);
338 let dark_pixel = mapping.dark.get_pixel(x, y);
339
340 let map_y = (y as i32 + off) as u32;
341 if map_y >= mapping.map1.height() || map_y >= mapping.map2.height() {
342 continue;
343 }
344
345 let map_pixel = mapping.map1.get_pixel(x, map_y);
346 let sel_pixel = mapping.map2.get_pixel(x, map_y);
347
348 let b_val = map_pixel[2] as i32;
349 let ymod = b_val / 16;
350 let xmod = b_val % 16;
351 let act = map_pixel[3] as i32;
352 let x1 = (map_pixel[0] as f64 + 256.0 * xmod as f64 - RR) * input.xs;
353 let y1 = (map_pixel[1] as f64 + 256.0 * ymod as f64 - RR) * input.ys;
354
355 let xx_rot = input.xa * x1 + input.ya * y1;
356 let yy_rot = -input.ya * x1 + input.xa * y1;
357 let xx = input.in_x0 + active_scale * (xx_rot + RR + input.xo) / RR;
358 let yy = input.in_y0 + active_scale * (yy_rot + RR + input.yo) / RR;
359
360 let m = input.safe_pixel(xx as i32, yy as i32);
361
362 if sel_pixel[0] == input.layer && act > 25 {
363 let result_r = dark_pixel[0] as i32
364 + ((light_pixel[0] as i32 - dark_pixel[0] as i32) * m[0] as i32) / 255;
365 let result_g = dark_pixel[1] as i32
366 + ((light_pixel[1] as i32 - dark_pixel[1] as i32) * m[1] as i32) / 255;
367 let result_b = dark_pixel[2] as i32
368 + ((light_pixel[2] as i32 - dark_pixel[2] as i32) * m[2] as i32) / 255;
369 let mut result_a = m[3] as i32;
370
371 if (dark_pixel[3] as i32) < result_a {
372 result_a = dark_pixel[3] as i32;
373 }
374
375 if result_a > 0 {
376 let out_pixel = self.out.get_pixel_mut(x, y);
377 if result_a > 250 {
378 out_pixel[0] = result_r.clamp(0, 255) as u8;
379 out_pixel[1] = result_g.clamp(0, 255) as u8;
380 out_pixel[2] = result_b.clamp(0, 255) as u8;
381 } else {
382 out_pixel[0] = (out_pixel[0] as i32
383 + ((result_r - out_pixel[0] as i32) * result_a) / 255)
384 .clamp(0, 255) as u8;
385 out_pixel[1] = (out_pixel[1] as i32
386 + ((result_g - out_pixel[1] as i32) * result_a) / 255)
387 .clamp(0, 255) as u8;
388 out_pixel[2] = (out_pixel[2] as i32
389 + ((result_b - out_pixel[2] as i32) * result_a) / 255)
390 .clamp(0, 255) as u8;
391 }
392 }
393 }
394 }
395 }
396
397 Ok(())
398 }
399
400 fn post(&mut self) -> Result<()> {
402 RENDER_COUNT.fetch_add(1, Ordering::SeqCst);
403
404 let mapping = self.mapping.as_ref().ok_or(Error::NoMapping)?;
405 let w = self.out.width() as i32;
406 let h = self.out.height() as i32;
407
408 if (mapping.map1.width() as i32) < (mapping.light.width() as i32) {
409 return Ok(());
410 }
411
412 if mapping.map1.width() != mapping.neutral.width() {
413 return Ok(());
414 }
415 if mapping.map2.width() != mapping.neutral.width() {
416 return Ok(());
417 }
418
419 if !mapping.has_nonzero_smoothing() {
426 return Ok(());
427 }
428
429 let pre = self.out.clone();
430
431 for y in 0..h {
432 for x in 0..w {
433 let xu = x as u32;
434 let yu = y as u32;
435 let sel_pixel = mapping.map2.get_pixel(xu, yu);
436
437 if sel_pixel[2] > 0 {
438 if x > 0 && y > 0 && x < w - 1 && y < h - 1 {
439 let back1 = pre.get_pixel((x - 1) as u32, yu);
440 let idx1 = mapping.map2.get_pixel((x - 1) as u32, yu);
441
442 let back2 = pre.get_pixel((x + 1) as u32, yu);
443 let idx2 = mapping.map2.get_pixel((x + 1) as u32, yu);
444
445 let back3 = pre.get_pixel(xu, (y - 1) as u32);
446 let idx3 = mapping.map2.get_pixel(xu, (y - 1) as u32);
447
448 let back4 = pre.get_pixel(xu, (y + 1) as u32);
449 let idx4 = mapping.map2.get_pixel(xu, (y + 1) as u32);
450
451 let mut total = Pixer::new();
452 let mut ct = 0.0;
453
454 if idx1[2] < 127 {
455 total.add_rgba(back1);
456 ct += 1.0;
457 }
458 if idx2[2] < 127 {
459 total.add_rgba(back2);
460 ct += 1.0;
461 }
462 if idx3[2] < 127 {
463 total.add_rgba(back3);
464 ct += 1.0;
465 }
466 if idx4[2] < 127 {
467 total.add_rgba(back4);
468 ct += 1.0;
469 }
470
471 if ct > 0.5 {
472 total.div(ct);
473 let out_pixel = self.out.get_pixel_mut(xu, yu);
474 out_pixel[0] = total.r.clamp(0.0, 255.0) as u8;
475 out_pixel[1] = total.g.clamp(0.0, 255.0) as u8;
476 out_pixel[2] = total.b.clamp(0.0, 255.0) as u8;
477 }
478 }
479 }
480 }
481 }
482
483 Ok(())
484 }
485
486 pub fn apply(&mut self, inputs: &Inputs) -> Result<()> {
487 self.apply_scaled(inputs, -1, -1)
488 }
489
490 pub fn apply_scaled(&mut self, inputs: &Inputs, w: i32, h: i32) -> Result<()> {
491 self.pre()?;
492
493 match self.quality {
494 RenderQuality::None => {}
495 RenderQuality::Simple => {
496 for input in inputs.iter() {
497 self.add_simple(input)?;
498 }
499 }
500 RenderQuality::Sampled => {
501 for input in inputs.iter() {
502 self.add(input)?;
503 }
504 }
505 }
506
507 self.post()?;
508
509 if w > 0 && h > 0 && (w != self.out.width() as i32 || h != self.out.height() as i32) {
510 let wi = self.out.width() as i32;
511 let hi = self.out.height() as i32;
512
513 let fi = wi as f64 / hi as f64;
514 let f = w as f64 / h as f64;
515
516 let (xo, yo, wo, ho) = if fi > f + 0.001 {
517 let wo = w;
518 let ho = (wo as f64 / fi) as i32;
519 let yo = (h - ho) / 2;
520 (0, yo, wo, ho)
521 } else if fi < f - 0.001 {
522 let ho = h;
523 let wo = (h as f64 * fi) as i32;
524 let xo = (w - wo) / 2;
525 (xo, 0, wo, ho)
526 } else {
527 (0, 0, w, h)
528 };
529
530 let mut scaled = RgbaImage::from_pixel(w as u32, h as u32, Rgba([255, 255, 255, 0]));
531
532 for dy in 0..ho {
533 for dx in 0..wo {
534 let sx = (dx as f64 * wi as f64 / wo as f64) as u32;
535 let sy = (dy as f64 * hi as f64 / ho as f64) as u32;
536 let sx = sx.min(self.out.width() - 1);
537 let sy = sy.min(self.out.height() - 1);
538 let pixel = *self.out.get_pixel(sx, sy);
539 scaled.put_pixel((xo + dx) as u32, (yo + dy) as u32, pixel);
540 }
541 }
542
543 for pixel in scaled.pixels_mut() {
544 pixel[3] = 255;
545 }
546
547 self.out_scaled = Some(scaled);
548 self.out = RgbaImage::new(1, 1);
549 }
550
551 Ok(())
552 }
553
554 pub fn get(&self) -> &RgbaImage {
555 self.out_scaled.as_ref().unwrap_or(&self.out)
556 }
557
558 pub fn get_mut(&mut self) -> &mut RgbaImage {
559 if self.out_scaled.is_some() { self.out_scaled.as_mut().unwrap() } else { &mut self.out }
560 }
561
562 pub fn save<P: AsRef<std::path::Path>>(&self, path: P) -> Result<()> {
563 self.get().save(path)?;
564 Ok(())
565 }
566
567 pub fn render_count() -> usize {
568 RENDER_COUNT.load(Ordering::SeqCst)
569 }
570
571 pub fn get_cloud(&self, input: &Input) -> Result<Vec<CloudPoint>> {
572 let mapping = self.mapping.as_ref().ok_or(Error::NoMapping)?;
573
574 let active_scale = input.in_scale as f64 / 2.0;
575 let off = ((mapping.map2.height() as i32 - mapping.light.height() as i32) / 2) as i32;
576
577 if (mapping.map1.width() as i32) < (mapping.light.width() as i32) {
578 return Ok(Vec::new());
579 }
580
581 let mut cloud = Vec::new();
582 let w = mapping.light.width();
583 let h = mapping.light.height();
584
585 for y in 0..h {
586 for x in 0..w {
587 let light_pixel = mapping.light.get_pixel(x, y);
588 let dark_pixel = mapping.dark.get_pixel(x, y);
589
590 let map_y = (y as i32 + off) as u32;
591 if map_y >= mapping.map1.height() || map_y >= mapping.map2.height() {
592 continue;
593 }
594
595 let map_pixel = mapping.map1.get_pixel(x, map_y);
596 let sel_pixel = mapping.map2.get_pixel(x, map_y);
597
598 let b_val = map_pixel[2] as i32;
599 let ymod = b_val / 16;
600 let xmod = b_val % 16;
601 let act = map_pixel[3] as i32;
602 let x1 = (map_pixel[0] as f64 + 256.0 * xmod as f64 - RR) * input.xs;
603 let y1 = (map_pixel[1] as f64 + 256.0 * ymod as f64 - RR) * input.ys;
604
605 let xx_rot = input.xa * x1 + input.ya * y1;
606 let yy_rot = -input.ya * x1 + input.xa * y1;
607 let xx = input.in_x0 + active_scale * (xx_rot + RR + input.xo) / RR;
608 let yy = input.in_y0 + active_scale * (yy_rot + RR + input.yo) / RR;
609
610 if sel_pixel[0] != 0 && act > 25 {
611 let del = 5i32;
612 let r_diff = (light_pixel[0] as i32 - dark_pixel[0] as i32).abs();
613 let g_diff = (light_pixel[1] as i32 - dark_pixel[1] as i32).abs();
614 let b_diff = (light_pixel[2] as i32 - dark_pixel[2] as i32).abs();
615
616 if (r_diff > del || g_diff > del || b_diff > del)
617 && dark_pixel[3] > 100
618 && light_pixel[3] > 100
619 {
620 cloud.push(CloudPoint { layer: sel_pixel[0], x: xx, y: yy });
621 }
622 }
623 }
624 }
625
626 Ok(cloud)
627 }
628
629 pub fn auto_zoom_input(&self, input: &mut Input) -> Result<bool> {
630 let mapping = self.mapping.as_ref().ok_or(Error::NoMapping)?;
631
632 let active_scale = input.in_scale as f64 / 2.0;
633 let off = ((mapping.map2.height() as i32 - mapping.light.height() as i32) / 2) as i32;
634
635 if (mapping.map1.width() as i32) < (mapping.light.width() as i32) {
636 return Ok(false);
637 }
638
639 let mut x_min = input.width() as f64;
640 let mut x_max = 0.0f64;
641 let mut y_min = input.height() as f64;
642 let mut y_max = 0.0f64;
643
644 let w = mapping.light.width();
645 let h = mapping.light.height();
646
647 for y in 0..h {
648 for x in 0..w {
649 let map_y = (y as i32 + off) as u32;
650 if map_y >= mapping.map1.height() || map_y >= mapping.map2.height() {
651 continue;
652 }
653
654 let map_pixel = mapping.map1.get_pixel(x, map_y);
655 let sel_pixel = mapping.map2.get_pixel(x, map_y);
656
657 let b_val = map_pixel[2] as i32;
658 let ymod = b_val / 16;
659 let xmod = b_val % 16;
660 let act = map_pixel[3] as i32;
661 let x1 = (map_pixel[0] as f64 + 256.0 * xmod as f64 - RR) * input.xs;
662 let y1 = (map_pixel[1] as f64 + 256.0 * ymod as f64 - RR) * input.ys;
663
664 let xx_rot = input.xa * x1 + input.ya * y1;
665 let yy_rot = -input.ya * x1 + input.xa * y1;
666 let xx = input.in_x0 + active_scale * (xx_rot + RR + input.xo) / RR;
667 let yy = input.in_y0 + active_scale * (yy_rot + RR + input.yo) / RR;
668
669 if sel_pixel[0] == 1 && act > 25 {
672 if xx < x_min {
673 x_min = xx;
674 }
675 if xx > x_max {
676 x_max = xx;
677 }
678 if yy < y_min {
679 y_min = yy;
680 }
681 if yy > y_max {
682 y_max = yy;
683 }
684 }
685 }
686 }
687
688 let hh = input.height() as f64;
689 if y_max - y_min < hh * 0.75 {
690 input.xs *= 2.0;
691 input.ys *= 2.0;
692 return Ok(true);
693 }
694
695 Ok(false)
696 }
697
698 pub fn auto_zoom(&self, inputs: &mut Inputs) -> Result<bool> {
699 let mut changed = false;
700 for input in inputs.iter_mut() {
701 if self.auto_zoom_input(input)? {
702 changed = true;
703 }
704 }
705 Ok(changed)
706 }
707}