rapiddoc_core/
pillow_resize.rs1use crate::{Error, ErrorCode};
3
4pub fn fail(code: ErrorCode) -> Error {
5 let message = if code == ErrorCode::InvalidInput {
6 "公式图像字节长度或尺寸无效"
7 } else {
8 "公式图像尺寸或工作量超过预算"
9 };
10 Error {
11 code,
12 message: message.into(),
13 }
14}
15pub struct Rgb {
16 pub data: Vec<u8>,
17 pub w: usize,
18 pub h: usize,
19}
20pub fn size(w: usize, h: usize) -> Result<usize, Error> {
21 w.checked_mul(h)
22 .filter(|n| {
23 w > 0
24 && h > 0
25 && w <= crate::resources::limit(65_536)
26 && h <= crate::resources::limit(65_536)
27 && *n <= crate::resources::limit(16_000_000)
28 && n.checked_mul(3)
29 .is_some_and(|bytes| bytes <= isize::MAX as usize)
30 })
31 .ok_or_else(|| fail(ErrorCode::ResourceLimit))
32}
33pub struct Work(pub usize);
34impl Work {
35 fn add(&mut self, n: usize) -> Result<(), Error> {
36 self.0 = self
37 .0
38 .checked_add(n)
39 .filter(|n| *n <= crate::resources::limit(256_000_000))
40 .ok_or_else(|| fail(ErrorCode::ResourceLimit))?;
41 Ok(())
42 }
43}
44fn filter(x: f64, cubic: bool) -> f64 {
45 let x = x.abs();
46 if !cubic {
47 if x < 1. {
48 1. - x
49 } else {
50 0.
51 }
52 } else if x < 1. {
53 (1.5 * x - 2.5) * x * x + 1.
54 } else if x < 2. {
55 (((x - 5.) * x + 8.) * x - 4.) * -0.5
56 } else {
57 0.
58 }
59}
60fn coefficients(
61 input: usize,
62 end: f32,
63 output: usize,
64 cubic: bool,
65 work: &mut Work,
66) -> Result<Vec<(usize, Vec<i32>)>, Error> {
67 let scale = end as f64 / output as f64;
68 let fs = scale.max(1.);
69 let support = fs * if cubic { 2. } else { 1. };
70 let mut out = Vec::with_capacity(output);
71 for i in 0..output {
72 let center = (i as f64 + 0.5) * scale;
73 let start = ((center - support + 0.5) as isize).max(0) as usize;
74 let stop = ((center + support + 0.5) as usize).min(input);
75 let count = stop.saturating_sub(start);
76 work.add(count)?;
77 let mut weights: Vec<_> = (0..count)
78 .map(|j| filter((j as f64 + start as f64 - center + 0.5) * (1. / fs), cubic))
79 .collect();
80 let sum: f64 = weights.iter().sum();
81 for w in &mut weights {
82 if sum != 0. {
83 *w /= sum;
84 }
85 }
86 let fixed = weights
87 .into_iter()
88 .map(|w| ((w * (1u32 << 22) as f64) + if w < 0. { -0.5 } else { 0.5 }) as i32)
89 .collect();
90 out.push((start, fixed));
91 }
92 Ok(out)
93}
94fn axis(
95 im: Rgb,
96 output: usize,
97 end: f32,
98 vertical: bool,
99 cubic: bool,
100 work: &mut Work,
101) -> Result<Rgb, Error> {
102 let input = if vertical { im.h } else { im.w };
103 if output == input && end == input as f32 {
104 return Ok(im);
105 }
106 let (w, h) = if vertical {
107 (im.w, output)
108 } else {
109 (output, im.h)
110 };
111 let pixels = size(w, h)?;
112 let coeffs = coefficients(input, end, output, cubic, work)?;
113 let cross = if vertical { im.w } else { im.h };
114 let taps: usize = coeffs.iter().map(|(_, c)| c.len()).sum();
115 work.add(
116 taps.checked_mul(cross)
117 .and_then(|n| n.checked_mul(3))
118 .ok_or_else(|| fail(ErrorCode::ResourceLimit))?,
119 )?;
120 let mut data = vec![0; pixels * 3];
121 for row in 0..h {
124 for col in 0..w {
125 let (start, ks) = &coeffs[if vertical { row } else { col }];
126 let mut sums = [1i64 << 21; 3];
127 for (k, &weight) in ks.iter().enumerate() {
128 let pos = if vertical {
129 ((start + k) * im.w + col) * 3
130 } else {
131 (row * im.w + start + k) * 3
132 };
133 let weight = weight as i64;
134 sums[0] += im.data[pos] as i64 * weight;
135 sums[1] += im.data[pos + 1] as i64 * weight;
136 sums[2] += im.data[pos + 2] as i64 * weight;
137 }
138 let dest = (row * w + col) * 3;
139 for c in 0..3 {
140 data[dest + c] = (sums[c] >> 22).clamp(0, 255) as u8;
141 }
142 }
143 }
144 Ok(Rgb { data, w, h })
145}
146pub fn resize(
147 im: Rgb,
148 w: usize,
149 h: usize,
150 end: (f32, f32),
151 cubic: bool,
152 work: &mut Work,
153) -> Result<Rgb, Error> {
154 size(w, h)?;
155 if im.h > im.w.saturating_mul(100) && h < im.h {
157 let im = axis(im, h, end.1, true, cubic, work)?;
158 axis(im, w, end.0, false, cubic, work)
159 } else {
160 let im = axis(im, w, end.0, false, cubic, work)?;
161 axis(im, h, end.1, true, cubic, work)
162 }
163}
164pub fn reduce(im: Rgb, fx: usize, fy: usize, work: &mut Work) -> Result<Rgb, Error> {
165 let (w, h) = (im.w.div_ceil(fx), im.h.div_ceil(fy));
166 let mut data = vec![0; size(w, h)? * 3];
167 work.add(im.data.len())?;
168 for y in 0..h {
169 for x in 0..w {
170 let (nx, ny) = (fx.min(im.w - x * fx), fy.min(im.h - y * fy));
171 let n = nx * ny;
172 let multiplier = ((1u32 << 24) as f32 / n as f32) as u64;
174 for c in 0..3 {
175 let mut sum = n as u64 / 2;
176 for yy in 0..ny {
177 for xx in 0..nx {
178 sum += im.data[((y * fy + yy) * im.w + x * fx + xx) * 3 + c] as u64;
179 }
180 }
181 data[(y * w + x) * 3 + c] = ((sum * multiplier) >> 24) as u8;
182 }
183 }
184 }
185 Ok(Rgb { data, w, h })
186}