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