1use crate::bridge;
2use crate::error::{Error, Result};
3use core::ffi::c_void;
4use core::marker::PhantomData;
5
6pub mod vimage_flags {
8 pub const NO_FLAGS: u32 = 0;
9 pub const BACKGROUND_COLOR_FILL: u32 = 4;
10 pub const EDGE_EXTEND: u32 = 8;
11 pub const HIGH_QUALITY_RESAMPLING: u32 = 32;
12}
13
14fn vimage_result(status: isize) -> Result<()> {
15 if status == 0 {
16 Ok(())
17 } else {
18 Err(Error::VImageError(status))
19 }
20}
21
22fn ensure_same_dimensions(lhs: &ImageBuffer<'_>, rhs: &ImageBuffer<'_>) -> Result<()> {
23 if lhs.width() == rhs.width() && lhs.height() == rhs.height() {
24 Ok(())
25 } else {
26 Err(Error::InvalidValue(
27 "vImage buffers must share the same width and height",
28 ))
29 }
30}
31
32pub struct ImageBuffer<'a> {
34 data: *mut u8,
35 width: usize,
36 height: usize,
37 row_bytes: usize,
38 _marker: PhantomData<&'a mut [u8]>,
39}
40
41impl<'a> ImageBuffer<'a> {
42 pub fn from_argb8888(data: &'a mut [u8], width: usize, height: usize) -> Result<Self> {
43 let expected = width
44 .checked_mul(height)
45 .and_then(|pixels| pixels.checked_mul(4))
46 .ok_or(Error::OperationFailed("image dimensions overflowed"))?;
47 if data.len() < expected {
48 return Err(Error::InvalidLength {
49 expected,
50 actual: data.len(),
51 });
52 }
53
54 Ok(Self {
55 data: data.as_mut_ptr(),
56 width,
57 height,
58 row_bytes: width * 4,
59 _marker: PhantomData,
60 })
61 }
62
63 pub fn from_planar8(data: &'a mut [u8], width: usize, height: usize) -> Result<Self> {
64 let expected = width
65 .checked_mul(height)
66 .ok_or(Error::OperationFailed("image dimensions overflowed"))?;
67 if data.len() < expected {
68 return Err(Error::InvalidLength {
69 expected,
70 actual: data.len(),
71 });
72 }
73
74 Ok(Self {
75 data: data.as_mut_ptr(),
76 width,
77 height,
78 row_bytes: width,
79 _marker: PhantomData,
80 })
81 }
82
83 fn data_ptr(&self) -> *mut c_void {
84 self.data.cast()
85 }
86
87 fn width(&self) -> usize {
88 self.width
89 }
90
91 fn height(&self) -> usize {
92 self.height
93 }
94
95 fn row_bytes(&self) -> usize {
96 self.row_bytes
97 }
98}
99
100pub fn rotate_argb8888(
101 src: &ImageBuffer<'_>,
102 dst: &mut ImageBuffer<'_>,
103 angle_radians: f32,
104 background_color: [u8; 4],
105 flags: u32,
106) -> Result<()> {
107 let status = unsafe {
109 bridge::acc_vimage_rotate_argb8888(
110 src.data_ptr(),
111 src.width(),
112 src.height(),
113 src.row_bytes(),
114 dst.data_ptr(),
115 dst.width(),
116 dst.height(),
117 dst.row_bytes(),
118 angle_radians,
119 background_color.as_ptr(),
120 flags,
121 )
122 };
123 vimage_result(status)
124}
125
126pub fn box_convolve_argb8888(
127 src: &ImageBuffer<'_>,
128 dst: &mut ImageBuffer<'_>,
129 kernel_height: u32,
130 kernel_width: u32,
131 background_color: [u8; 4],
132 flags: u32,
133) -> Result<()> {
134 let status = unsafe {
136 bridge::acc_vimage_box_convolve_argb8888(
137 src.data_ptr(),
138 src.width(),
139 src.height(),
140 src.row_bytes(),
141 dst.data_ptr(),
142 dst.width(),
143 dst.height(),
144 dst.row_bytes(),
145 kernel_height,
146 kernel_width,
147 background_color.as_ptr(),
148 flags,
149 )
150 };
151 vimage_result(status)
152}
153
154pub fn scale_argb8888(src: &ImageBuffer<'_>, dst: &mut ImageBuffer<'_>, flags: u32) -> Result<()> {
155 let status = unsafe {
157 bridge::acc_vimage_scale_argb8888(
158 src.data_ptr(),
159 src.width(),
160 src.height(),
161 src.row_bytes(),
162 dst.data_ptr(),
163 dst.width(),
164 dst.height(),
165 dst.row_bytes(),
166 flags,
167 )
168 };
169 vimage_result(status)
170}
171
172pub fn contrast_stretch_planar8(
173 src: &ImageBuffer<'_>,
174 dst: &mut ImageBuffer<'_>,
175 flags: u32,
176) -> Result<()> {
177 let status = unsafe {
179 bridge::acc_vimage_contrast_stretch_planar8(
180 src.data_ptr(),
181 src.width(),
182 src.height(),
183 src.row_bytes(),
184 dst.data_ptr(),
185 dst.width(),
186 dst.height(),
187 dst.row_bytes(),
188 flags,
189 )
190 };
191 vimage_result(status)
192}
193
194pub fn alpha_blend_argb8888(
195 src_top: &ImageBuffer<'_>,
196 src_bottom: &ImageBuffer<'_>,
197 dst: &mut ImageBuffer<'_>,
198 flags: u32,
199) -> Result<()> {
200 ensure_same_dimensions(src_top, src_bottom)?;
201 ensure_same_dimensions(src_top, dst)?;
202
203 let status = unsafe {
205 bridge::acc_vimage_alpha_blend_argb8888(
206 src_top.data_ptr(),
207 src_top.width(),
208 src_top.height(),
209 src_top.row_bytes(),
210 src_bottom.data_ptr(),
211 src_bottom.width(),
212 src_bottom.height(),
213 src_bottom.row_bytes(),
214 dst.data_ptr(),
215 dst.width(),
216 dst.height(),
217 dst.row_bytes(),
218 flags,
219 )
220 };
221 vimage_result(status)
222}
223
224pub fn clip_to_alpha_argb8888(
225 src: &ImageBuffer<'_>,
226 dst: &mut ImageBuffer<'_>,
227 flags: u32,
228) -> Result<()> {
229 ensure_same_dimensions(src, dst)?;
230
231 let status = unsafe {
233 bridge::acc_vimage_clip_to_alpha_argb8888(
234 src.data_ptr(),
235 src.width(),
236 src.height(),
237 src.row_bytes(),
238 dst.data_ptr(),
239 dst.width(),
240 dst.height(),
241 dst.row_bytes(),
242 flags,
243 )
244 };
245 vimage_result(status)
246}
247
248pub fn premultiply_argb8888(
249 src: &ImageBuffer<'_>,
250 dst: &mut ImageBuffer<'_>,
251 flags: u32,
252) -> Result<()> {
253 ensure_same_dimensions(src, dst)?;
254
255 let status = unsafe {
257 bridge::acc_vimage_premultiply_argb8888(
258 src.data_ptr(),
259 src.width(),
260 src.height(),
261 src.row_bytes(),
262 dst.data_ptr(),
263 dst.width(),
264 dst.height(),
265 dst.row_bytes(),
266 flags,
267 )
268 };
269 vimage_result(status)
270}
271
272pub fn unpremultiply_argb8888(
273 src: &ImageBuffer<'_>,
274 dst: &mut ImageBuffer<'_>,
275 flags: u32,
276) -> Result<()> {
277 ensure_same_dimensions(src, dst)?;
278
279 let status = unsafe {
281 bridge::acc_vimage_unpremultiply_argb8888(
282 src.data_ptr(),
283 src.width(),
284 src.height(),
285 src.row_bytes(),
286 dst.data_ptr(),
287 dst.width(),
288 dst.height(),
289 dst.row_bytes(),
290 flags,
291 )
292 };
293 vimage_result(status)
294}
295
296pub fn convert_planar8_to_argb8888(
297 src_alpha: &ImageBuffer<'_>,
298 src_red: &ImageBuffer<'_>,
299 src_green: &ImageBuffer<'_>,
300 src_blue: &ImageBuffer<'_>,
301 dst: &mut ImageBuffer<'_>,
302 flags: u32,
303) -> Result<()> {
304 ensure_same_dimensions(src_alpha, src_red)?;
305 ensure_same_dimensions(src_alpha, src_green)?;
306 ensure_same_dimensions(src_alpha, src_blue)?;
307 ensure_same_dimensions(src_alpha, dst)?;
308
309 let status = unsafe {
311 bridge::acc_vimage_convert_planar8_to_argb8888(
312 src_alpha.data_ptr(),
313 src_alpha.width(),
314 src_alpha.height(),
315 src_alpha.row_bytes(),
316 src_red.data_ptr(),
317 src_red.width(),
318 src_red.height(),
319 src_red.row_bytes(),
320 src_green.data_ptr(),
321 src_green.width(),
322 src_green.height(),
323 src_green.row_bytes(),
324 src_blue.data_ptr(),
325 src_blue.width(),
326 src_blue.height(),
327 src_blue.row_bytes(),
328 dst.data_ptr(),
329 dst.width(),
330 dst.height(),
331 dst.row_bytes(),
332 flags,
333 )
334 };
335 vimage_result(status)
336}
337
338pub fn convert_argb8888_to_planar8(
339 src: &ImageBuffer<'_>,
340 dst_alpha: &mut ImageBuffer<'_>,
341 dst_red: &mut ImageBuffer<'_>,
342 dst_green: &mut ImageBuffer<'_>,
343 dst_blue: &mut ImageBuffer<'_>,
344 flags: u32,
345) -> Result<()> {
346 ensure_same_dimensions(src, dst_alpha)?;
347 ensure_same_dimensions(src, dst_red)?;
348 ensure_same_dimensions(src, dst_green)?;
349 ensure_same_dimensions(src, dst_blue)?;
350
351 let status = unsafe {
353 bridge::acc_vimage_convert_argb8888_to_planar8(
354 src.data_ptr(),
355 src.width(),
356 src.height(),
357 src.row_bytes(),
358 dst_alpha.data_ptr(),
359 dst_alpha.width(),
360 dst_alpha.height(),
361 dst_alpha.row_bytes(),
362 dst_red.data_ptr(),
363 dst_red.width(),
364 dst_red.height(),
365 dst_red.row_bytes(),
366 dst_green.data_ptr(),
367 dst_green.width(),
368 dst_green.height(),
369 dst_green.row_bytes(),
370 dst_blue.data_ptr(),
371 dst_blue.width(),
372 dst_blue.height(),
373 dst_blue.row_bytes(),
374 flags,
375 )
376 };
377 vimage_result(status)
378}