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