1use std::mem::MaybeUninit;
2use std::os::raw::c_int;
3use std::{ptr, slice};
4
5use libheif_sys as lh;
6
7use crate::{
8 Channel, ColorProfileNCLX, ColorProfileRaw, ColorProfileType, ColorSpace, HeifError, HeifErrorCode,
9 HeifErrorSubCode, Result,
10};
11
12const MAX_IMAGE_SIZE: u32 = i32::MAX as _;
13
14pub struct Plane<T> {
15 pub data: T,
16 pub width: u32,
17 pub height: u32,
18 pub stride: usize,
19 pub bits_per_pixel: u8,
20 pub storage_bits_per_pixel: u8,
21}
22
23pub struct Planes<T> {
24 pub y: Option<Plane<T>>,
25 pub cb: Option<Plane<T>>,
26 pub cr: Option<Plane<T>>,
27 pub r: Option<Plane<T>>,
28 pub g: Option<Plane<T>>,
29 pub b: Option<Plane<T>>,
30 pub a: Option<Plane<T>>,
31 pub interleaved: Option<Plane<T>>,
32}
33
34pub struct Image {
35 pub(crate) inner: *mut lh::heif_image,
36}
37
38pub struct ScalingOptions {}
39
40#[allow(unsafe_code)]
41impl Image {
42 #[allow(unsafe_code)]
46 pub fn new(width: u32, height: u32, color_space: ColorSpace) -> Result<Image> {
47 if width > MAX_IMAGE_SIZE || height > MAX_IMAGE_SIZE {
48 return Err(HeifError {
49 code: HeifErrorCode::UsageError,
50 sub_code: HeifErrorSubCode::InvalidBoxSize,
51 message: "width or height is greater than MAX_IMAGE_SIZE".to_string(),
52 });
53 }
54
55 let mut c_image = MaybeUninit::<_>::uninit();
56 let err = unsafe {
57 lh::heif_image_create(
58 width as _,
59 height as _,
60 color_space.heif_color_space(),
61 color_space.heif_chroma(),
62 c_image.as_mut_ptr(),
63 )
64 };
65 HeifError::from_heif_error(err)?;
66 Ok(Image {
67 inner: unsafe { c_image.assume_init() },
68 })
69 }
70
71 #[inline]
72 pub(crate) fn from_heif_image(image: *mut lh::heif_image) -> Image {
73 Image { inner: image }
74 }
75
76 #[allow(unsafe_code)]
78 pub fn width(&self) -> u32 {
79 unsafe { lh::heif_image_get_primary_width(self.inner).max(0) as u32 }
80 }
81
82 #[allow(unsafe_code)]
84 pub fn height(&self) -> u32 {
85 unsafe { lh::heif_image_get_primary_height(self.inner).max(0) as u32 }
86 }
87
88 #[allow(unsafe_code)]
90 pub fn channel_width(&self, channel: Channel) -> Option<u32> {
91 let value = unsafe { lh::heif_image_get_width(self.inner, channel as _) };
92 (value >= 0).then_some(value as _)
93 }
94
95 #[allow(unsafe_code)]
97 pub fn channel_height(&self, channel: Channel) -> Option<u32> {
98 let value = unsafe { lh::heif_image_get_height(self.inner, channel as _) };
99 (value >= 0).then_some(value as _)
100 }
101
102 #[allow(unsafe_code)]
110 pub fn storage_bits_per_pixel(&self, channel: Channel) -> Option<u8> {
111 let value = unsafe { lh::heif_image_get_bits_per_pixel(self.inner, channel as _) };
112 (value >= 0).then_some(value as _)
113 }
114
115 #[allow(unsafe_code)]
125 pub fn bits_per_pixel(&self, channel: Channel) -> Option<u8> {
126 let value = unsafe { lh::heif_image_get_bits_per_pixel_range(self.inner, channel as _) };
127 (value >= 0).then_some(value as _)
128 }
129
130 #[cfg(feature = "v1_20")]
131 #[allow(unsafe_code)]
132 fn plane_inner(&self, channel: Channel) -> (*const u8, usize) {
133 let mut stride: usize = 1;
134 let data = unsafe { lh::heif_image_get_plane_readonly2(self.inner, channel as _, &mut stride) };
135 (data, stride)
136 }
137
138 #[cfg(not(feature = "v1_20"))]
139 #[allow(unsafe_code)]
140 fn plane_inner(&self, channel: Channel) -> (*const u8, usize) {
141 let mut stride: i32 = 1;
142 let data = unsafe { lh::heif_image_get_plane_readonly(self.inner, channel as _, &mut stride) };
143 (data, stride as _)
144 }
145
146 #[allow(unsafe_code)]
147 fn plane(&self, channel: Channel) -> Option<Plane<&[u8]>> {
148 let (data, stride) = self.plane_inner(channel);
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;
158 let bytes = unsafe { slice::from_raw_parts(data, size) };
159 Some(Plane {
160 data: bytes,
161 bits_per_pixel,
162 storage_bits_per_pixel,
163 width,
164 height,
165 stride,
166 })
167 }
168
169 #[cfg(feature = "v1_20")]
170 #[allow(unsafe_code)]
171 fn plane_mut_inner(&self, channel: Channel) -> (*mut u8, usize) {
172 let mut stride: usize = 1;
173 let data = unsafe { lh::heif_image_get_plane2(self.inner, channel as _, &mut stride) };
174 (data, stride)
175 }
176
177 #[cfg(not(feature = "v1_20"))]
178 #[allow(unsafe_code)]
179 fn plane_mut_inner(&self, channel: Channel) -> (*mut u8, usize) {
180 let mut stride: i32 = 1;
181 let data = unsafe { lh::heif_image_get_plane(self.inner, channel as _, &mut stride) };
182 (data, stride as _)
183 }
184
185 #[allow(clippy::mut_from_ref)]
186 #[allow(unsafe_code)]
187 fn plane_mut(&self, channel: Channel) -> Option<Plane<&mut [u8]>> {
188 let (data, stride): (*mut u8, usize) = self.plane_mut_inner(channel);
189 if data.is_null() {
190 return None;
191 }
192
193 let width = self.channel_width(channel).unwrap_or_default();
194 let height = self.channel_height(channel).unwrap_or_default();
195 let bits_per_pixel = self.bits_per_pixel(channel).unwrap_or_default();
196 let storage_bits_per_pixel = self.storage_bits_per_pixel(channel).unwrap_or_default();
197 let size = height as usize * stride;
198 let bytes = unsafe { slice::from_raw_parts_mut(data, size) };
199 Some(Plane {
200 data: bytes,
201 bits_per_pixel,
202 storage_bits_per_pixel,
203 width,
204 height,
205 stride,
206 })
207 }
208
209 #[allow(unsafe_code)]
210 pub fn planes(&self) -> Planes<&[u8]> {
211 Planes {
212 y: self.plane(Channel::Y),
213 cb: self.plane(Channel::Cb),
214 cr: self.plane(Channel::Cr),
215 r: self.plane(Channel::R),
216 g: self.plane(Channel::G),
217 b: self.plane(Channel::B),
218 a: self.plane(Channel::Alpha),
219 interleaved: self.plane(Channel::Interleaved),
220 }
221 }
222
223 #[allow(unsafe_code)]
224 pub fn planes_mut(&mut self) -> Planes<&mut [u8]> {
225 Planes {
226 y: self.plane_mut(Channel::Y),
227 cb: self.plane_mut(Channel::Cb),
228 cr: self.plane_mut(Channel::Cr),
229 r: self.plane_mut(Channel::R),
230 g: self.plane_mut(Channel::G),
231 b: self.plane_mut(Channel::B),
232 a: self.plane_mut(Channel::Alpha),
233 interleaved: self.plane_mut(Channel::Interleaved),
234 }
235 }
236
237 #[allow(unsafe_code)]
238 pub fn has_channel(&self, channel: Channel) -> bool {
239 unsafe { lh::heif_image_has_channel(self.inner, channel as _) != 0 }
240 }
241
242 #[allow(unsafe_code)]
243 pub fn color_space(&self) -> Option<ColorSpace> {
244 unsafe {
245 ColorSpace::from_libheif(
246 lh::heif_image_get_colorspace(self.inner),
247 lh::heif_image_get_chroma_format(self.inner),
248 )
249 }
250 }
251
252 #[allow(unsafe_code)]
256 pub fn scale(&self, width: u32, height: u32, _scaling_options: Option<ScalingOptions>) -> Result<Image> {
257 let mut c_image = MaybeUninit::<_>::uninit();
258 let err = unsafe {
259 lh::heif_image_scale_image(self.inner, c_image.as_mut_ptr(), width as _, height as _, ptr::null())
260 };
261 HeifError::from_heif_error(err)?;
262 Ok(Image {
263 inner: unsafe { c_image.assume_init() },
264 })
265 }
266
267 #[allow(unsafe_code)]
273 pub fn create_plane(&mut self, channel: Channel, width: u32, height: u32, bit_depth: u8) -> Result<()> {
274 let err = unsafe {
275 lh::heif_image_add_plane(
276 self.inner,
277 channel as _,
278 width as _,
279 height as _,
280 c_int::from(bit_depth),
281 )
282 };
283 HeifError::from_heif_error(err)
284 }
285
286 #[allow(unsafe_code)]
287 pub fn set_premultiplied_alpha(&self, is_premultiplied_alpha: bool) {
288 unsafe { lh::heif_image_set_premultiplied_alpha(self.inner, is_premultiplied_alpha as _) };
289 }
290
291 #[allow(unsafe_code)]
292 pub fn is_premultiplied_alpha(&self) -> bool {
293 unsafe { lh::heif_image_is_premultiplied_alpha(self.inner) != 0 }
294 }
295
296 #[allow(unsafe_code)]
297 pub fn color_profile_raw(&self) -> Option<ColorProfileRaw> {
298 let size = unsafe { lh::heif_image_get_raw_color_profile_size(self.inner) };
299 if size == 0 {
300 return None;
301 }
302 let mut result: Vec<u8> = Vec::with_capacity(size);
303 let err = unsafe { lh::heif_image_get_raw_color_profile(self.inner, result.as_ptr() as _) };
304 if err.code != 0 {
305 return None;
306 }
307 unsafe {
308 result.set_len(size);
309 }
310 let c_profile_type = unsafe { lh::heif_image_get_color_profile_type(self.inner) };
311 let profile_type = ColorProfileType::from(c_profile_type as u32);
312
313 Some(ColorProfileRaw {
314 typ: profile_type,
315 data: result,
316 })
317 }
318
319 #[allow(unsafe_code)]
320 pub fn set_color_profile_raw(&mut self, profile: &ColorProfileRaw) -> Result<()> {
321 let err = unsafe {
322 let mut c_profile_type = [0u8; 5];
323 c_profile_type[0..4].copy_from_slice(&profile.typ.0);
324 lh::heif_image_set_raw_color_profile(
325 self.inner,
326 c_profile_type.as_ptr() as _,
327 profile.data.as_ptr() as _,
328 profile.data.len(),
329 )
330 };
331 HeifError::from_heif_error(err)
332 }
333
334 #[allow(unsafe_code)]
335 pub fn color_profile_nclx(&self) -> Option<ColorProfileNCLX> {
336 let mut profile_ptr = MaybeUninit::<_>::uninit();
337 let err = unsafe { lh::heif_image_get_nclx_color_profile(self.inner, profile_ptr.as_mut_ptr()) };
338 if err.code != 0 {
339 return None;
340 }
341 let profile_ptr = unsafe { profile_ptr.assume_init() };
342 Some(ColorProfileNCLX { inner: profile_ptr })
343 }
344
345 #[allow(unsafe_code)]
346 pub fn set_color_profile_nclx(&mut self, profile: &ColorProfileNCLX) -> Result<()> {
347 let err = unsafe { lh::heif_image_set_nclx_color_profile(self.inner, profile.inner) };
348 HeifError::from_heif_error(err)
349 }
350
351 #[allow(unsafe_code)]
352 pub fn pixel_aspect_ratio(&self) -> (u32, u32) {
353 let mut aspect_h = 0;
354 let mut aspect_v = 0;
355 unsafe {
356 lh::heif_image_get_pixel_aspect_ratio(self.inner, &mut aspect_h as _, &mut aspect_v as _);
357 }
358 (aspect_h, aspect_v)
359 }
360
361 #[allow(unsafe_code)]
362 pub fn set_pixel_aspect_ratio(&mut self, aspect_h: u32, aspect_v: u32) {
363 unsafe {
364 lh::heif_image_set_pixel_aspect_ratio(self.inner, aspect_h, aspect_v);
365 }
366 }
367
368 #[cfg(feature = "v1_20")]
369 #[allow(unsafe_code)]
370 pub fn duration(&self) -> u32 {
371 unsafe { lh::heif_image_get_duration(self.inner) }
372 }
373
374 #[cfg(feature = "v1_20")]
375 #[allow(unsafe_code)]
376 pub fn set_duration(&self, duration: u32) {
377 unsafe { lh::heif_image_set_duration(self.inner, duration) }
378 }
379}
380
381#[allow(unsafe_code)]
382impl Drop for Image {
383 #[allow(unsafe_code)]
384 fn drop(&mut self) {
385 unsafe { lh::heif_image_release(self.inner) };
386 }
387}
388
389#[allow(unsafe_code)]
390unsafe impl Send for Image {}