Skip to main content

apple_accelerate/
vimage.rs

1use crate::bridge;
2use crate::error::{Error, Result};
3use core::ffi::c_void;
4use core::marker::PhantomData;
5
6/// Common `vImage_Flags` values.
7pub 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
22/// Borrowed wrapper around a caller-owned image buffer.
23pub struct ImageBuffer<'a> {
24    data: *mut u8,
25    width: usize,
26    height: usize,
27    row_bytes: usize,
28    _marker: PhantomData<&'a mut [u8]>,
29}
30
31impl<'a> ImageBuffer<'a> {
32    pub fn from_argb8888(data: &'a mut [u8], width: usize, height: usize) -> Result<Self> {
33        let expected = width
34            .checked_mul(height)
35            .and_then(|pixels| pixels.checked_mul(4))
36            .ok_or(Error::OperationFailed("image dimensions overflowed"))?;
37        if data.len() < expected {
38            return Err(Error::InvalidLength {
39                expected,
40                actual: data.len(),
41            });
42        }
43
44        Ok(Self {
45            data: data.as_mut_ptr(),
46            width,
47            height,
48            row_bytes: width * 4,
49            _marker: PhantomData,
50        })
51    }
52
53    pub fn from_planar8(data: &'a mut [u8], width: usize, height: usize) -> Result<Self> {
54        let expected = width
55            .checked_mul(height)
56            .ok_or(Error::OperationFailed("image dimensions overflowed"))?;
57        if data.len() < expected {
58            return Err(Error::InvalidLength {
59                expected,
60                actual: data.len(),
61            });
62        }
63
64        Ok(Self {
65            data: data.as_mut_ptr(),
66            width,
67            height,
68            row_bytes: width,
69            _marker: PhantomData,
70        })
71    }
72
73    fn data_ptr(&self) -> *mut c_void {
74        self.data.cast()
75    }
76
77    fn width(&self) -> usize {
78        self.width
79    }
80
81    fn height(&self) -> usize {
82        self.height
83    }
84
85    fn row_bytes(&self) -> usize {
86        self.row_bytes
87    }
88}
89
90pub fn rotate_argb8888(
91    src: &ImageBuffer<'_>,
92    dst: &mut ImageBuffer<'_>,
93    angle_radians: f32,
94    background_color: [u8; 4],
95    flags: u32,
96) -> Result<()> {
97    // SAFETY: Source and destination buffers remain valid for the duration of the call.
98    let status = unsafe {
99        bridge::acc_vimage_rotate_argb8888(
100            src.data_ptr(),
101            src.width(),
102            src.height(),
103            src.row_bytes(),
104            dst.data_ptr(),
105            dst.width(),
106            dst.height(),
107            dst.row_bytes(),
108            angle_radians,
109            background_color.as_ptr(),
110            flags,
111        )
112    };
113    vimage_result(status)
114}
115
116pub fn box_convolve_argb8888(
117    src: &ImageBuffer<'_>,
118    dst: &mut ImageBuffer<'_>,
119    kernel_height: u32,
120    kernel_width: u32,
121    background_color: [u8; 4],
122    flags: u32,
123) -> Result<()> {
124    // SAFETY: Source and destination buffers remain valid for the duration of the call.
125    let status = unsafe {
126        bridge::acc_vimage_box_convolve_argb8888(
127            src.data_ptr(),
128            src.width(),
129            src.height(),
130            src.row_bytes(),
131            dst.data_ptr(),
132            dst.width(),
133            dst.height(),
134            dst.row_bytes(),
135            kernel_height,
136            kernel_width,
137            background_color.as_ptr(),
138            flags,
139        )
140    };
141    vimage_result(status)
142}
143
144pub fn scale_argb8888(src: &ImageBuffer<'_>, dst: &mut ImageBuffer<'_>, flags: u32) -> Result<()> {
145    // SAFETY: Source and destination buffers remain valid for the duration of the call.
146    let status = unsafe {
147        bridge::acc_vimage_scale_argb8888(
148            src.data_ptr(),
149            src.width(),
150            src.height(),
151            src.row_bytes(),
152            dst.data_ptr(),
153            dst.width(),
154            dst.height(),
155            dst.row_bytes(),
156            flags,
157        )
158    };
159    vimage_result(status)
160}
161
162pub fn contrast_stretch_planar8(
163    src: &ImageBuffer<'_>,
164    dst: &mut ImageBuffer<'_>,
165    flags: u32,
166) -> Result<()> {
167    // SAFETY: Source and destination buffers remain valid for the duration of the call.
168    let status = unsafe {
169        bridge::acc_vimage_contrast_stretch_planar8(
170            src.data_ptr(),
171            src.width(),
172            src.height(),
173            src.row_bytes(),
174            dst.data_ptr(),
175            dst.width(),
176            dst.height(),
177            dst.row_bytes(),
178            flags,
179        )
180    };
181    vimage_result(status)
182}