1use std::mem::MaybeUninit;
2use std::os::raw::c_int;
3use std::ptr;
4use std::slice;
5
6use libheif_sys as lh;
7
8use crate::{
9 Channel, ColorProfileNCLX, ColorProfileRaw, ColorProfileType, ColorSpace, HeifError,
10 HeifErrorCode, HeifErrorSubCode, Result,
11};
12
13const MAX_IMAGE_SIZE: u32 = i32::MAX as _;
14
15pub struct Plane<T> {
16 pub data: T,
17 pub width: u32,
18 pub height: u32,
19 pub stride: usize,
20 pub bits_per_pixel: u8,
21 pub storage_bits_per_pixel: u8,
22}
23
24pub struct Planes<T> {
25 pub y: Option<Plane<T>>,
26 pub cb: Option<Plane<T>>,
27 pub cr: Option<Plane<T>>,
28 pub r: Option<Plane<T>>,
29 pub g: Option<Plane<T>>,
30 pub b: Option<Plane<T>>,
31 pub a: Option<Plane<T>>,
32 pub interleaved: Option<Plane<T>>,
33}
34
35pub struct Image {
36 pub(crate) inner: *mut lh::heif_image,
37}
38
39pub struct ScalingOptions {}
40
41impl Image {
42 pub fn new(width: u32, height: u32, color_space: ColorSpace) -> Result<Image> {
46 if width > MAX_IMAGE_SIZE || height > MAX_IMAGE_SIZE {
47 return Err(HeifError {
48 code: HeifErrorCode::UsageError,
49 sub_code: HeifErrorSubCode::InvalidBoxSize,
50 message: "width or height is greater than MAX_IMAGE_SIZE".to_string(),
51 });
52 }
53
54 let mut c_image = MaybeUninit::<_>::uninit();
55 let err = unsafe {
56 lh::heif_image_create(
57 width as _,
58 height as _,
59 color_space.heif_color_space(),
60 color_space.heif_chroma(),
61 c_image.as_mut_ptr(),
62 )
63 };
64 HeifError::from_heif_error(err)?;
65 Ok(Image {
66 inner: unsafe { c_image.assume_init() },
67 })
68 }
69
70 #[inline]
71 pub(crate) fn from_heif_image(image: *mut lh::heif_image) -> Image {
72 Image { inner: image }
73 }
74
75 pub fn width(&self) -> u32 {
77 unsafe { lh::heif_image_get_primary_width(self.inner).max(0) as u32 }
78 }
79
80 pub fn height(&self) -> u32 {
82 unsafe { lh::heif_image_get_primary_height(self.inner).max(0) as u32 }
83 }
84
85 pub fn channel_width(&self, channel: Channel) -> Option<u32> {
87 let value = unsafe { lh::heif_image_get_width(self.inner, channel as _) };
88 (value >= 0).then_some(value as _)
89 }
90
91 pub fn channel_height(&self, channel: Channel) -> Option<u32> {
93 let value = unsafe { lh::heif_image_get_height(self.inner, channel as _) };
94 (value >= 0).then_some(value as _)
95 }
96
97 pub fn storage_bits_per_pixel(&self, channel: Channel) -> Option<u8> {
105 let value = unsafe { lh::heif_image_get_bits_per_pixel(self.inner, channel as _) };
106 (value >= 0).then_some(value as _)
107 }
108
109 pub fn bits_per_pixel(&self, channel: Channel) -> Option<u8> {
119 let value = unsafe { lh::heif_image_get_bits_per_pixel_range(self.inner, channel as _) };
120 (value >= 0).then_some(value as _)
121 }
122
123 fn plane(&self, channel: Channel) -> Option<Plane<&[u8]>> {
124 let mut stride: i32 = 1;
125 let data = unsafe { lh::heif_image_get_plane(self.inner, channel as _, &mut stride) };
126 if data.is_null() {
127 return None;
128 }
129
130 let width = self.channel_width(channel).unwrap_or_default();
131 let height = self.channel_height(channel).unwrap_or_default();
132 let bits_per_pixel = self.bits_per_pixel(channel).unwrap_or_default();
133 let storage_bits_per_pixel = self.storage_bits_per_pixel(channel).unwrap_or_default();
134 let size = height as usize * stride as usize;
135 let bytes = unsafe { slice::from_raw_parts(data, size) };
136 Some(Plane {
137 data: bytes,
138 bits_per_pixel,
139 storage_bits_per_pixel,
140 width,
141 height,
142 stride: stride as _,
143 })
144 }
145
146 fn plane_mut(&self, channel: Channel) -> Option<Plane<&mut [u8]>> {
147 let mut stride: i32 = 1;
148 let data = unsafe { lh::heif_image_get_plane(self.inner, channel as _, &mut stride) };
149 if data.is_null() {
150 return None;
151 }
152
153 let width = self.channel_width(channel).unwrap_or_default();
154 let height = self.channel_height(channel).unwrap_or_default();
155 let bits_per_pixel = self.bits_per_pixel(channel).unwrap_or_default();
156 let storage_bits_per_pixel = self.storage_bits_per_pixel(channel).unwrap_or_default();
157 let size = height as usize * stride as usize;
158 let bytes = unsafe { slice::from_raw_parts_mut(data, size) };
159 Some(Plane {
160 data: bytes,
161 bits_per_pixel,
162 storage_bits_per_pixel,
163 width,
164 height,
165 stride: stride as _,
166 })
167 }
168
169 pub fn planes(&self) -> Planes<&[u8]> {
170 Planes {
171 y: self.plane(Channel::Y),
172 cb: self.plane(Channel::Cb),
173 cr: self.plane(Channel::Cr),
174 r: self.plane(Channel::R),
175 g: self.plane(Channel::G),
176 b: self.plane(Channel::B),
177 a: self.plane(Channel::Alpha),
178 interleaved: self.plane(Channel::Interleaved),
179 }
180 }
181
182 pub fn planes_mut(&mut self) -> Planes<&mut [u8]> {
183 Planes {
184 y: self.plane_mut(Channel::Y),
185 cb: self.plane_mut(Channel::Cb),
186 cr: self.plane_mut(Channel::Cr),
187 r: self.plane_mut(Channel::R),
188 g: self.plane_mut(Channel::G),
189 b: self.plane_mut(Channel::B),
190 a: self.plane_mut(Channel::Alpha),
191 interleaved: self.plane_mut(Channel::Interleaved),
192 }
193 }
194
195 pub fn has_channel(&self, channel: Channel) -> bool {
196 unsafe { lh::heif_image_has_channel(self.inner, channel as _) != 0 }
197 }
198
199 pub fn color_space(&self) -> Option<ColorSpace> {
210 unsafe {
211 ColorSpace::from_libheif(
212 lh::heif_image_get_colorspace(self.inner),
213 lh::heif_image_get_chroma_format(self.inner),
214 )
215 }
216 }
217
218 pub fn scale(
222 &self,
223 width: u32,
224 height: u32,
225 _scaling_options: Option<ScalingOptions>,
226 ) -> Result<Image> {
227 let mut c_image = MaybeUninit::<_>::uninit();
228 let err = unsafe {
229 lh::heif_image_scale_image(
230 self.inner,
231 c_image.as_mut_ptr(),
232 width as _,
233 height as _,
234 ptr::null(),
235 )
236 };
237 HeifError::from_heif_error(err)?;
238 Ok(Image {
239 inner: unsafe { c_image.assume_init() },
240 })
241 }
242
243 pub fn create_plane(
249 &mut self,
250 channel: Channel,
251 width: u32,
252 height: u32,
253 bit_depth: u8,
254 ) -> Result<()> {
255 let err = unsafe {
256 lh::heif_image_add_plane(
257 self.inner,
258 channel as _,
259 width as _,
260 height as _,
261 c_int::from(bit_depth),
262 )
263 };
264 HeifError::from_heif_error(err)
265 }
266
267 pub fn set_premultiplied_alpha(&self, is_premultiplied_alpha: bool) {
268 unsafe { lh::heif_image_set_premultiplied_alpha(self.inner, is_premultiplied_alpha as _) };
269 }
270
271 pub fn is_premultiplied_alpha(&self) -> bool {
272 unsafe { lh::heif_image_is_premultiplied_alpha(self.inner) != 0 }
273 }
274
275 pub fn color_profile_raw(&self) -> Option<ColorProfileRaw> {
276 let size = unsafe { lh::heif_image_get_raw_color_profile_size(self.inner) };
277 if size == 0 {
278 return None;
279 }
280 let mut result: Vec<u8> = Vec::with_capacity(size);
281 let err = unsafe { lh::heif_image_get_raw_color_profile(self.inner, result.as_ptr() as _) };
282 if err.code != 0 {
283 return None;
285 }
286 unsafe {
287 result.set_len(size);
288 }
289 let c_profile_type = unsafe { lh::heif_image_get_color_profile_type(self.inner) };
290 let profile_type = ColorProfileType::from(c_profile_type as u32);
292
293 Some(ColorProfileRaw {
294 typ: profile_type,
295 data: result,
296 })
297 }
298
299 pub fn set_color_profile_raw(&mut self, profile: &ColorProfileRaw) -> Result<()> {
300 let err = unsafe {
301 let mut c_profile_type = [0u8; 5];
302 c_profile_type[0..4].copy_from_slice(&profile.typ.0);
303 lh::heif_image_set_raw_color_profile(
304 self.inner,
305 c_profile_type.as_ptr() as _,
306 profile.data.as_ptr() as _,
307 profile.data.len(),
308 )
309 };
310 HeifError::from_heif_error(err)
311 }
312
313 pub fn color_profile_nclx(&self) -> Option<ColorProfileNCLX> {
314 let mut profile_ptr = MaybeUninit::<_>::uninit();
315 let err =
316 unsafe { lh::heif_image_get_nclx_color_profile(self.inner, profile_ptr.as_mut_ptr()) };
317 if err.code != 0 {
318 return None;
320 }
321 let profile_ptr = unsafe { profile_ptr.assume_init() };
322 Some(ColorProfileNCLX { inner: profile_ptr })
323 }
324
325 pub fn set_color_profile_nclx(&mut self, profile: &ColorProfileNCLX) -> Result<()> {
326 let err = unsafe { lh::heif_image_set_nclx_color_profile(self.inner, profile.inner) };
327 HeifError::from_heif_error(err)
328 }
329
330 pub fn pixel_aspect_ratio(&self) -> (u32, u32) {
331 let mut aspect_h = 0;
332 let mut aspect_v = 0;
333 unsafe {
334 lh::heif_image_get_pixel_aspect_ratio(
335 self.inner,
336 &mut aspect_h as _,
337 &mut aspect_v as _,
338 );
339 }
340 (aspect_h, aspect_v)
341 }
342
343 pub fn set_pixel_aspect_ratio(&mut self, aspect_h: u32, aspect_v: u32) {
344 unsafe {
345 lh::heif_image_set_pixel_aspect_ratio(self.inner, aspect_h, aspect_v);
346 }
347 }
348}
349
350impl Drop for Image {
351 fn drop(&mut self) {
352 unsafe { lh::heif_image_release(self.inner) };
353 }
354}
355
356unsafe impl Send for Image {}