fltk/
image.rs

1use crate::enums::ColorDepth;
2use crate::prelude::*;
3use crate::utils::FlString;
4use fltk_sys::image::*;
5use std::{
6    ffi::CString,
7    mem,
8    sync::atomic::{AtomicBool, Ordering},
9};
10
11/// Basically a check for image support
12static IMAGES_REGISTERED: AtomicBool = AtomicBool::new(false);
13
14/// Check if fltk-rs was initialized
15fn images_registered() -> bool {
16    IMAGES_REGISTERED.load(Ordering::Relaxed)
17}
18
19/// Registers all images supported by `SharedImage`
20fn register_images() {
21    #[cfg(feature = "use-images")]
22    unsafe {
23        fltk_sys::image::Fl_register_images();
24        fltk_sys::fl::Fl_load_system_icons();
25    }
26}
27
28type ImageRC<T> = std::rc::Rc<T>;
29
30/// Wrapper around `Fl_Image`, used to wrap other image types
31#[derive(Debug)]
32pub struct Image {
33    inner: ImageRC<*mut Fl_Image>,
34}
35
36/// The scaling algorithm to use for raster images
37#[repr(i32)]
38#[derive(Debug, Copy, Clone, PartialEq, Eq)]
39pub enum RgbScaling {
40    /// Default RGB image scaling algorithm
41    Nearest = 0,
42    /// More accurate, but slower RGB image scaling algorithm
43    Bilinear,
44}
45
46crate::macros::image::impl_image_ext!(Image, Fl_Image);
47
48impl Image {
49    /// Sets the scaling algorithm
50    pub fn set_scaling_algorithm(algorithm: RgbScaling) {
51        unsafe { Fl_Image_set_scaling_algorithm(algorithm as i32) }
52    }
53
54    /// Gets the scaling algorithm
55    pub fn scaling_algorithm() -> RgbScaling {
56        unsafe { mem::transmute(Fl_Image_scaling_algorithm()) }
57    }
58}
59
60#[cfg(feature = "use-images")]
61/// Creates a struct holding a shared image
62#[derive(Debug)]
63pub struct SharedImage {
64    inner: ImageRC<*mut Fl_Shared_Image>,
65}
66
67#[cfg(feature = "use-images")]
68crate::macros::image::impl_image_ext!(SharedImage, Fl_Shared_Image);
69
70#[cfg(feature = "use-images")]
71impl SharedImage {
72    /// Loads a `SharedImage` from a path
73    /// # Errors
74    /// Errors on non-existent path or invalid format, also errors if `app::App` was not initialized
75    pub fn load<P: AsRef<std::path::Path>>(path: P) -> Result<SharedImage, FltkError> {
76        Self::load_(path.as_ref())
77    }
78
79    fn load_(path: &std::path::Path) -> Result<SharedImage, FltkError> {
80        if !path.exists() {
81            return Err(FltkError::Internal(FltkErrorKind::ResourceNotFound));
82        }
83        unsafe {
84            let temp = path.to_str().ok_or_else(|| {
85                FltkError::Unknown(String::from("Failed to convert path to string"))
86            })?;
87            if !images_registered() {
88                register_images();
89            }
90            let temp = CString::safe_new(temp);
91            let x = Fl_Shared_Image_get(temp.as_ptr(), 0, 0);
92            if x.is_null() {
93                Err(FltkError::Internal(FltkErrorKind::ResourceNotFound))
94            } else {
95                if Fl_Shared_Image_fail(x) < 0 {
96                    return Err(FltkError::Internal(FltkErrorKind::ImageFormatError));
97                }
98                Ok(SharedImage {
99                    inner: ImageRC::from(x),
100                })
101            }
102        }
103    }
104
105    /// Loads a `SharedImage` from an image
106    /// # Errors
107    /// Errors on unsupported `SharedImage` types
108    pub fn from_image<I: ImageExt>(image: &I) -> Result<SharedImage, FltkError> {
109        unsafe {
110            assert!(!image.was_deleted());
111            let x = Fl_Shared_Image_from_rgb(image.as_image_ptr() as *mut Fl_RGB_Image, 1);
112            if x.is_null() {
113                Err(FltkError::Internal(FltkErrorKind::ResourceNotFound))
114            } else {
115                if Fl_Shared_Image_fail(x) < 0 {
116                    return Err(FltkError::Internal(FltkErrorKind::ImageFormatError));
117                }
118                Ok(SharedImage {
119                    inner: ImageRC::from(x),
120                })
121            }
122        }
123    }
124}
125
126#[cfg(feature = "use-images")]
127/// Creates a struct holding a Jpeg image
128#[derive(Debug)]
129pub struct JpegImage {
130    inner: ImageRC<*mut Fl_JPEG_Image>,
131}
132
133#[cfg(feature = "use-images")]
134crate::macros::image::impl_image_ext!(JpegImage, Fl_JPEG_Image);
135
136#[cfg(feature = "use-images")]
137impl JpegImage {
138    /// Loads the image from a filesystem path, doesn't check for the validity of the data
139    /// # Errors
140    /// Errors on non-existent path or invalid format
141    pub fn load<P: AsRef<std::path::Path>>(path: P) -> Result<JpegImage, FltkError> {
142        Self::load_(path.as_ref())
143    }
144
145    fn load_(path: &std::path::Path) -> Result<JpegImage, FltkError> {
146        if !path.exists() {
147            return Err(FltkError::Internal(FltkErrorKind::ResourceNotFound));
148        }
149        unsafe {
150            let temp = path.to_str().ok_or_else(|| {
151                FltkError::Unknown(String::from("Failed to convert path to string"))
152            })?;
153            if !images_registered() {
154                register_images();
155            }
156            let temp = CString::safe_new(temp);
157            let image_ptr = Fl_JPEG_Image_new(temp.as_ptr());
158            if image_ptr.is_null() {
159                Err(FltkError::Internal(FltkErrorKind::ResourceNotFound))
160            } else {
161                if Fl_JPEG_Image_fail(image_ptr) < 0 {
162                    return Err(FltkError::Internal(FltkErrorKind::ImageFormatError));
163                }
164                Ok(JpegImage {
165                    inner: ImageRC::from(image_ptr),
166                })
167            }
168        }
169    }
170
171    /// Loads the image from data/memory
172    /// # Errors
173    /// Errors on invalid format
174    pub fn from_data(data: &[u8]) -> Result<JpegImage, FltkError> {
175        unsafe {
176            if data.is_empty() {
177                Err(FltkError::Internal(FltkErrorKind::ResourceNotFound))
178            } else {
179                if !images_registered() {
180                    register_images();
181                }
182                let x = Fl_JPEG_Image_from(data.as_ptr());
183                if x.is_null() {
184                    Err(FltkError::Internal(FltkErrorKind::ResourceNotFound))
185                } else {
186                    if Fl_JPEG_Image_fail(x) < 0 {
187                        return Err(FltkError::Internal(FltkErrorKind::ImageFormatError));
188                    }
189                    Ok(JpegImage {
190                        inner: ImageRC::from(x),
191                    })
192                }
193            }
194        }
195    }
196}
197
198#[cfg(feature = "use-images")]
199/// Creates a struct holding a PNG image
200#[derive(Debug)]
201pub struct PngImage {
202    inner: ImageRC<*mut Fl_PNG_Image>,
203}
204
205#[cfg(feature = "use-images")]
206crate::macros::image::impl_image_ext!(PngImage, Fl_PNG_Image);
207
208#[cfg(feature = "use-images")]
209impl PngImage {
210    /// Loads the image from a filesystem path, doesn't check for the validity of the data
211    /// # Errors
212    /// Errors on non-existent path or invalid format
213    pub fn load<P: AsRef<std::path::Path>>(path: P) -> Result<PngImage, FltkError> {
214        Self::load_(path.as_ref())
215    }
216
217    fn load_(path: &std::path::Path) -> Result<PngImage, FltkError> {
218        if !path.exists() {
219            return Err(FltkError::Internal(FltkErrorKind::ResourceNotFound));
220        }
221        unsafe {
222            let temp = path.to_str().ok_or_else(|| {
223                FltkError::Unknown(String::from("Failed to convert path to string"))
224            })?;
225            if !images_registered() {
226                register_images();
227            }
228            let temp = CString::safe_new(temp);
229            let image_ptr = Fl_PNG_Image_new(temp.as_ptr());
230            if image_ptr.is_null() {
231                Err(FltkError::Internal(FltkErrorKind::ResourceNotFound))
232            } else {
233                if Fl_PNG_Image_fail(image_ptr) < 0 {
234                    return Err(FltkError::Internal(FltkErrorKind::ImageFormatError));
235                }
236                Ok(PngImage {
237                    inner: ImageRC::from(image_ptr),
238                })
239            }
240        }
241    }
242
243    /// Loads the image from data/memory
244    /// # Errors
245    /// Errors on invalid format
246    pub fn from_data(data: &[u8]) -> Result<PngImage, FltkError> {
247        unsafe {
248            if data.is_empty() {
249                Err(FltkError::Internal(FltkErrorKind::ResourceNotFound))
250            } else {
251                if !images_registered() {
252                    register_images();
253                }
254                let x = Fl_PNG_Image_from(data.as_ptr(), data.len() as i32);
255                if x.is_null() {
256                    Err(FltkError::Internal(FltkErrorKind::ResourceNotFound))
257                } else {
258                    if Fl_PNG_Image_fail(x) < 0 {
259                        return Err(FltkError::Internal(FltkErrorKind::ImageFormatError));
260                    }
261                    Ok(PngImage {
262                        inner: ImageRC::from(x),
263                    })
264                }
265            }
266        }
267    }
268}
269
270#[cfg(feature = "use-images")]
271/// Creates a struct holding an SVG image
272#[derive(Debug)]
273pub struct SvgImage {
274    inner: ImageRC<*mut Fl_SVG_Image>,
275}
276
277#[cfg(feature = "use-images")]
278crate::macros::image::impl_image_ext!(SvgImage, Fl_SVG_Image);
279
280#[cfg(feature = "use-images")]
281impl SvgImage {
282    /// Loads the image from a filesystem path, doesn't check for the validity of the data
283    /// # Errors
284    /// Errors on non-existent path or invalid format
285    pub fn load<P: AsRef<std::path::Path>>(path: P) -> Result<SvgImage, FltkError> {
286        Self::load_(path.as_ref())
287    }
288
289    fn load_(path: &std::path::Path) -> Result<SvgImage, FltkError> {
290        if !path.exists() {
291            return Err(FltkError::Internal(FltkErrorKind::ResourceNotFound));
292        }
293        unsafe {
294            let temp = path.to_str().ok_or_else(|| {
295                FltkError::Unknown(String::from("Failed to convert path to string"))
296            })?;
297            if !images_registered() {
298                register_images();
299            }
300            let temp = CString::safe_new(temp);
301            let image_ptr = Fl_SVG_Image_new(temp.as_ptr());
302            if image_ptr.is_null() {
303                Err(FltkError::Internal(FltkErrorKind::ResourceNotFound))
304            } else {
305                if Fl_SVG_Image_fail(image_ptr) < 0 {
306                    return Err(FltkError::Internal(FltkErrorKind::ImageFormatError));
307                }
308                Ok(SvgImage {
309                    inner: ImageRC::from(image_ptr),
310                })
311            }
312        }
313    }
314
315    /// Loads the image from data/memory
316    /// # Errors
317    /// Errors on invalid format
318    pub fn from_data(data: &str) -> Result<SvgImage, FltkError> {
319        if data.is_empty() {
320            Err(FltkError::Internal(FltkErrorKind::ResourceNotFound))
321        } else {
322            let data = CString::safe_new(data);
323            if !images_registered() {
324                register_images();
325            }
326            unsafe {
327                let x = Fl_SVG_Image_from(data.as_ptr());
328                if x.is_null() {
329                    Err(FltkError::Internal(FltkErrorKind::ResourceNotFound))
330                } else {
331                    if Fl_SVG_Image_fail(x) < 0 {
332                        return Err(FltkError::Internal(FltkErrorKind::ImageFormatError));
333                    }
334                    Ok(SvgImage {
335                        inner: ImageRC::from(x),
336                    })
337                }
338            }
339        }
340    }
341
342    /// Rasterize an `SvgImage`
343    pub fn normalize(&mut self) {
344        assert!(!self.was_deleted());
345        unsafe { Fl_SVG_Image_normalize(*self.inner) }
346    }
347}
348
349#[cfg(feature = "use-images")]
350/// Creates a struct holding a BMP image
351#[derive(Debug)]
352pub struct BmpImage {
353    inner: ImageRC<*mut Fl_BMP_Image>,
354}
355
356#[cfg(feature = "use-images")]
357crate::macros::image::impl_image_ext!(BmpImage, Fl_BMP_Image);
358
359#[cfg(feature = "use-images")]
360impl BmpImage {
361    /// Loads the image from a filesystem path, doesn't check for the validity of the data
362    /// # Errors
363    /// Errors on non-existent path or invalid format
364    pub fn load<P: AsRef<std::path::Path>>(path: P) -> Result<BmpImage, FltkError> {
365        Self::load_(path.as_ref())
366    }
367
368    fn load_(path: &std::path::Path) -> Result<BmpImage, FltkError> {
369        if !path.exists() {
370            return Err(FltkError::Internal(FltkErrorKind::ResourceNotFound));
371        }
372        unsafe {
373            let temp = path.to_str().ok_or_else(|| {
374                FltkError::Unknown(String::from("Failed to convert path to string"))
375            })?;
376            if !images_registered() {
377                register_images();
378            }
379            let temp = CString::safe_new(temp);
380            let image_ptr = Fl_BMP_Image_new(temp.as_ptr());
381            if image_ptr.is_null() {
382                Err(FltkError::Internal(FltkErrorKind::ResourceNotFound))
383            } else {
384                if Fl_BMP_Image_fail(image_ptr) < 0 {
385                    return Err(FltkError::Internal(FltkErrorKind::ImageFormatError));
386                }
387                Ok(BmpImage {
388                    inner: ImageRC::from(image_ptr),
389                })
390            }
391        }
392    }
393
394    /// Loads the image from data/memory
395    /// # Errors
396    /// Errors on invalid format
397    pub fn from_data(data: &[u8]) -> Result<BmpImage, FltkError> {
398        unsafe {
399            if data.is_empty() {
400                Err(FltkError::Internal(FltkErrorKind::ResourceNotFound))
401            } else {
402                if !images_registered() {
403                    register_images();
404                }
405                let x = Fl_BMP_Image_from(data.as_ptr(), data.len() as _);
406                if x.is_null() {
407                    Err(FltkError::Internal(FltkErrorKind::ResourceNotFound))
408                } else {
409                    if Fl_BMP_Image_fail(x) < 0 {
410                        return Err(FltkError::Internal(FltkErrorKind::ImageFormatError));
411                    }
412                    Ok(BmpImage {
413                        inner: ImageRC::from(x),
414                    })
415                }
416            }
417        }
418    }
419}
420
421#[cfg(feature = "use-images")]
422/// Creates a struct holding a GIF image
423#[derive(Debug)]
424pub struct GifImage {
425    inner: ImageRC<*mut Fl_GIF_Image>,
426}
427
428#[cfg(feature = "use-images")]
429crate::macros::image::impl_image_ext!(GifImage, Fl_GIF_Image);
430
431#[cfg(feature = "use-images")]
432impl GifImage {
433    /// Loads the image from a filesystem path, doesn't check for the validity of the data
434    /// # Errors
435    /// Errors on non-existent path or invalid format
436    pub fn load<P: AsRef<std::path::Path>>(path: P) -> Result<GifImage, FltkError> {
437        Self::load_(path.as_ref())
438    }
439
440    fn load_(path: &std::path::Path) -> Result<GifImage, FltkError> {
441        if !path.exists() {
442            return Err(FltkError::Internal(FltkErrorKind::ResourceNotFound));
443        }
444        unsafe {
445            let temp = path.to_str().ok_or_else(|| {
446                FltkError::Unknown(String::from("Failed to convert path to string"))
447            })?;
448            if !images_registered() {
449                register_images();
450            }
451            let temp = CString::safe_new(temp);
452            let image_ptr = Fl_GIF_Image_new(temp.as_ptr());
453            if image_ptr.is_null() {
454                Err(FltkError::Internal(FltkErrorKind::ResourceNotFound))
455            } else {
456                if Fl_GIF_Image_fail(image_ptr) < 0 {
457                    return Err(FltkError::Internal(FltkErrorKind::ImageFormatError));
458                }
459                Ok(GifImage {
460                    inner: ImageRC::from(image_ptr),
461                })
462            }
463        }
464    }
465
466    /// Loads the image from data/memory
467    /// # Errors
468    /// Errors on invalid format
469    pub fn from_data(data: &[u8]) -> Result<GifImage, FltkError> {
470        unsafe {
471            if data.is_empty() {
472                Err(FltkError::Internal(FltkErrorKind::ResourceNotFound))
473            } else {
474                if !images_registered() {
475                    register_images();
476                }
477                let x = Fl_GIF_Image_from(data.as_ptr(), data.len() as _);
478                if x.is_null() {
479                    Err(FltkError::Internal(FltkErrorKind::ResourceNotFound))
480                } else {
481                    if Fl_GIF_Image_fail(x) < 0 {
482                        return Err(FltkError::Internal(FltkErrorKind::ImageFormatError));
483                    }
484                    Ok(GifImage {
485                        inner: ImageRC::from(x),
486                    })
487                }
488            }
489        }
490    }
491}
492
493#[cfg(feature = "use-images")]
494bitflags::bitflags! {
495    /// Defines AnimGifImage flags
496    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
497    pub struct AnimGifImageFlags: u16 {
498        /// No Event
499        const None = 0;
500        /// This flag indicates to the loader that it should not start
501        /// the animation immediately after successful load, which is
502        /// the default.
503        /// It can be started later using the \ref start() method.
504        const DONT_START = 1;
505        /// This flag indicates to the loader that it should not
506        /// resize the canvas widget of the animation to the dimensions
507        /// of the animation, which is the default.
508        /// Needed for special use cases.
509        const DONT_RESIZE_CANVAS = 2;
510        /// This flag indicates to the loader that it should not
511        /// set the animation as \ref image() member of the canvas widget,
512        /// which is the default.
513        /// Needed for special use cases.
514        const DONT_SET_AS_IMAGE = 4;
515        /// Often frames change just a small area of the animation canvas.
516        /// This flag indicates to the loader to try using less memory
517        /// by storing frame data not as canvas-sized images but use the
518        /// sizes defined in the GIF file.
519        /// The drawbacks are higher cpu usage during playback and maybe
520        //// minor artefacts when resized.\
521        const OPTIMIZE_MEMORY = 8;
522        /// This flag can be used to print informations about the decoding process to the console.
523        const LOG_FLAG = 64;
524        /// This flag can be used to print even more informations about the decoding process to the console.
525        const DEBUG_FLAG = 128;
526    }
527}
528
529#[cfg(feature = "use-images")]
530/// Creates a struct holding an animated GIF image
531#[derive(Debug)]
532pub struct AnimGifImage {
533    inner: ImageRC<*mut Fl_Anim_GIF_Image>,
534}
535
536#[cfg(feature = "use-images")]
537crate::macros::image::impl_image_ext!(AnimGifImage, Fl_Anim_GIF_Image);
538
539#[cfg(feature = "use-images")]
540impl AnimGifImage {
541    /// Loads the image from a filesystem path, doesn't check for the validity of the data
542    /// # Errors
543    /// Errors on non-existent path or invalid format
544    pub fn load<P: AsRef<std::path::Path>, W: WidgetExt>(
545        path: P,
546        w: &mut W,
547        flags: AnimGifImageFlags,
548    ) -> Result<AnimGifImage, FltkError> {
549        Self::load_(path.as_ref(), w, flags)
550    }
551
552    fn load_<W: WidgetExt>(
553        path: &std::path::Path,
554        w: &mut W,
555        flags: AnimGifImageFlags,
556    ) -> Result<AnimGifImage, FltkError> {
557        if !path.exists() {
558            return Err(FltkError::Internal(FltkErrorKind::ResourceNotFound));
559        }
560        unsafe {
561            let temp = path.to_str().ok_or_else(|| {
562                FltkError::Unknown(String::from("Failed to convert path to string"))
563            })?;
564            if !images_registered() {
565                register_images();
566            }
567            let temp = CString::safe_new(temp);
568            let image_ptr =
569                Fl_Anim_GIF_Image_new(temp.as_ptr(), w.as_widget_ptr() as _, flags.bits());
570            if image_ptr.is_null() {
571                Err(FltkError::Internal(FltkErrorKind::ResourceNotFound))
572            } else {
573                if Fl_Anim_GIF_Image_fail(image_ptr) < 0 {
574                    return Err(FltkError::Internal(FltkErrorKind::ImageFormatError));
575                }
576                Ok(AnimGifImage {
577                    inner: ImageRC::from(image_ptr),
578                })
579            }
580        }
581    }
582
583    /// Loads the image from data/memory
584    /// # Errors
585    /// Errors on invalid format
586    pub fn from_data<W: WidgetExt>(
587        data: &[u8],
588        w: &mut W,
589        flags: AnimGifImageFlags,
590    ) -> Result<AnimGifImage, FltkError> {
591        unsafe {
592            if data.is_empty() {
593                Err(FltkError::Internal(FltkErrorKind::ResourceNotFound))
594            } else {
595                if !images_registered() {
596                    register_images();
597                }
598                let x = Fl_Anim_GIF_Image_from(
599                    std::ptr::null() as _,
600                    data.as_ptr(),
601                    data.len() as _,
602                    w.as_widget_ptr() as _,
603                    flags.bits(),
604                );
605                if x.is_null() {
606                    Err(FltkError::Internal(FltkErrorKind::ResourceNotFound))
607                } else {
608                    if Fl_Anim_GIF_Image_fail(x) < 0 {
609                        return Err(FltkError::Internal(FltkErrorKind::ImageFormatError));
610                    }
611                    Ok(AnimGifImage {
612                        inner: ImageRC::from(x),
613                    })
614                }
615            }
616        }
617    }
618
619    /// Returns the delay of the frame
620    pub fn delay(&self, frame: i32) -> f64 {
621        unsafe { Fl_Anim_GIF_Image_delay(*self.inner, frame) }
622    }
623
624    /// Sets the delay of the frame
625    pub fn set_delay(&mut self, frame: i32, delay: f64) {
626        unsafe { Fl_Anim_GIF_Image_set_delay(*self.inner, frame, delay) }
627    }
628
629    /// Returns whether the Gif is animated
630    pub fn is_animated(&self) -> bool {
631        unsafe { Fl_Anim_GIF_Image_is_animated(*self.inner) != 0 }
632    }
633
634    /// Sets the animation speed
635    pub fn set_speed(&mut self, speed: f64) {
636        unsafe { Fl_Anim_GIF_Image_set_speed(*self.inner, speed) }
637    }
638
639    /// Returns the animation speed
640    pub fn speed(&mut self) -> f64 {
641        unsafe { Fl_Anim_GIF_Image_speed(*self.inner) }
642    }
643
644    /// Starts the animation
645    pub fn start(&mut self) -> bool {
646        unsafe { Fl_Anim_GIF_Image_start(*self.inner) != 0 }
647    }
648
649    /// Stops the animation
650    pub fn stop(&mut self) -> bool {
651        unsafe { Fl_Anim_GIF_Image_stop(*self.inner) != 0 }
652    }
653
654    /// Show the next frame if the animation is stopped. Errors if the Gif has no more frames
655    pub fn next_frame(&mut self) -> Result<(), FltkError> {
656        unsafe {
657            if Fl_Anim_GIF_Image_next(*self.inner) != 0 {
658                Ok(())
659            } else {
660                Err(FltkError::Internal(FltkErrorKind::FailedOperation))
661            }
662        }
663    }
664
665    /// Returns whether the Gif is playing
666    pub fn playing(&self) -> bool {
667        unsafe { Fl_Anim_GIF_Image_playing(*self.inner) != 0 }
668    }
669}
670
671/// Creates a struct holding a XPM image
672#[derive(Debug)]
673pub struct XpmImage {
674    inner: ImageRC<*mut Fl_XPM_Image>,
675}
676
677crate::macros::image::impl_image_ext!(XpmImage, Fl_XPM_Image);
678
679impl XpmImage {
680    /// Loads the image from a filesystem path, doesn't check for the validity of the data
681    /// # Errors
682    /// Errors on non-existent path or invalid format
683    pub fn load<P: AsRef<std::path::Path>>(path: P) -> Result<XpmImage, FltkError> {
684        Self::load_(path.as_ref())
685    }
686
687    fn load_(path: &std::path::Path) -> Result<XpmImage, FltkError> {
688        if !path.exists() {
689            return Err(FltkError::Internal(FltkErrorKind::ResourceNotFound));
690        }
691        unsafe {
692            let temp = path.to_str().ok_or_else(|| {
693                FltkError::Unknown(String::from("Failed to convert path to string"))
694            })?;
695            let temp = CString::safe_new(temp);
696            let image_ptr = Fl_XPM_Image_new(temp.as_ptr());
697            if image_ptr.is_null() {
698                Err(FltkError::Internal(FltkErrorKind::ResourceNotFound))
699            } else {
700                if Fl_XPM_Image_fail(image_ptr) < 0 {
701                    return Err(FltkError::Internal(FltkErrorKind::ImageFormatError));
702                }
703                Ok(XpmImage {
704                    inner: ImageRC::from(image_ptr),
705                })
706            }
707        }
708    }
709}
710
711/// Creates a struct holding a XBM image
712#[derive(Debug)]
713pub struct XbmImage {
714    inner: ImageRC<*mut Fl_XBM_Image>,
715}
716
717crate::macros::image::impl_image_ext!(XbmImage, Fl_XBM_Image);
718
719impl XbmImage {
720    /// Loads the image from a filesystem path, doesn't check for the validity of the data
721    /// # Errors
722    /// Errors on non-existent path or invalid format
723    pub fn load<P: AsRef<std::path::Path>>(path: P) -> Result<XbmImage, FltkError> {
724        Self::load_(path.as_ref())
725    }
726
727    fn load_(path: &std::path::Path) -> Result<XbmImage, FltkError> {
728        if !path.exists() {
729            return Err(FltkError::Internal(FltkErrorKind::ResourceNotFound));
730        }
731        unsafe {
732            let temp = path.to_str().ok_or_else(|| {
733                FltkError::Unknown(String::from("Failed to convert path to string"))
734            })?;
735            let temp = CString::safe_new(temp);
736            let image_ptr = Fl_XBM_Image_new(temp.as_ptr());
737            if image_ptr.is_null() {
738                Err(FltkError::Internal(FltkErrorKind::ResourceNotFound))
739            } else {
740                if Fl_XBM_Image_fail(image_ptr) < 0 {
741                    return Err(FltkError::Internal(FltkErrorKind::ImageFormatError));
742                }
743                Ok(XbmImage {
744                    inner: ImageRC::from(image_ptr),
745                })
746            }
747        }
748    }
749}
750
751#[cfg(feature = "use-images")]
752/// Creates a struct holding a PNM image
753#[derive(Debug)]
754pub struct PnmImage {
755    inner: ImageRC<*mut Fl_PNM_Image>,
756}
757
758#[cfg(feature = "use-images")]
759crate::macros::image::impl_image_ext!(PnmImage, Fl_PNM_Image);
760
761#[cfg(feature = "use-images")]
762impl PnmImage {
763    /// Loads the image from a filesystem path, doesn't check for the validity of the data
764    /// # Errors
765    /// Errors on non-existent path or invalid format
766    pub fn load<P: AsRef<std::path::Path>>(path: P) -> Result<PnmImage, FltkError> {
767        Self::load_(path.as_ref())
768    }
769
770    fn load_(path: &std::path::Path) -> Result<PnmImage, FltkError> {
771        if !path.exists() {
772            return Err(FltkError::Internal(FltkErrorKind::ResourceNotFound));
773        }
774        unsafe {
775            let temp = path.to_str().ok_or_else(|| {
776                FltkError::Unknown(String::from("Failed to convert path to string"))
777            })?;
778            if !images_registered() {
779                register_images();
780            }
781            let temp = CString::safe_new(temp);
782            let image_ptr = Fl_PNM_Image_new(temp.as_ptr());
783            if image_ptr.is_null() {
784                Err(FltkError::Internal(FltkErrorKind::ResourceNotFound))
785            } else {
786                if Fl_PNM_Image_fail(image_ptr) < 0 {
787                    return Err(FltkError::Internal(FltkErrorKind::ImageFormatError));
788                }
789                Ok(PnmImage {
790                    inner: ImageRC::from(image_ptr),
791                })
792            }
793        }
794    }
795}
796
797/// Creates a struct holding a tiled image
798#[derive(Debug)]
799pub struct TiledImage {
800    inner: ImageRC<*mut Fl_Tiled_Image>,
801}
802
803crate::macros::image::impl_image_ext!(TiledImage, Fl_Tiled_Image);
804
805impl TiledImage {
806    /// Loads the image from a filesystem path, doesn't check for the validity of the data
807    pub fn new<Img: ImageExt>(img: &Img, w: i32, h: i32) -> TiledImage {
808        unsafe {
809            assert!(!img.was_deleted());
810            let ptr = Fl_Tiled_Image_new(img.as_image_ptr(), w, h);
811            assert!(!ptr.is_null());
812            TiledImage {
813                inner: ImageRC::from(ptr),
814            }
815        }
816    }
817}
818
819/// Creates a struct holding a pixmap image
820#[derive(Debug)]
821pub struct Pixmap {
822    inner: ImageRC<*mut Fl_Pixmap>,
823}
824
825crate::macros::image::impl_image_ext!(Pixmap, Fl_Pixmap);
826
827impl Pixmap {
828    /// Creates a new Pixmap image
829    /// # Errors
830    /// Errors on invalid or unsupported image format
831    pub fn new(data: &[&str]) -> Result<Pixmap, FltkError> {
832        if data.is_empty() {
833            Err(FltkError::Internal(FltkErrorKind::ResourceNotFound))
834        } else {
835            let data: Vec<*const std::ffi::c_char> = data
836                .iter()
837                .map(|x| CString::safe_new(x).into_raw() as *const std::ffi::c_char)
838                .collect();
839            unsafe {
840                let x = Fl_Pixmap_new(data.as_ptr() as _);
841                for x in &data {
842                    let _ = CString::from_raw(*x as _);
843                }
844                if x.is_null() {
845                    Err(FltkError::Internal(FltkErrorKind::ResourceNotFound))
846                } else {
847                    if Fl_Pixmap_fail(x) < 0 {
848                        return Err(FltkError::Internal(FltkErrorKind::ImageFormatError));
849                    }
850                    Ok(Pixmap {
851                        inner: ImageRC::from(x),
852                    })
853                }
854            }
855        }
856    }
857}
858
859/// Creates a struct holding a raw RGB image
860#[derive(Debug)]
861pub struct RgbImage {
862    pub(crate) inner: ImageRC<*mut Fl_RGB_Image>,
863}
864
865crate::macros::image::impl_image_ext!(RgbImage, Fl_RGB_Image);
866
867impl RgbImage {
868    /// Initializes a new raw `RgbImage`, copies the data and handles its lifetime.
869    /// If you need to work with RGB data,
870    /// # Errors
871    /// Errors on invalid or unsupported image format
872    pub fn new(data: &[u8], w: i32, h: i32, depth: ColorDepth) -> Result<RgbImage, FltkError> {
873        let sz = w * h * depth as i32;
874        if sz > data.len() as i32 {
875            return Err(FltkError::Internal(FltkErrorKind::ImageFormatError));
876        }
877        unsafe {
878            let img = Fl_RGB_Image_new(data.as_ptr(), w, h, depth as i32, 0);
879            if img.is_null() || Fl_RGB_Image_fail(img) < 0 {
880                Err(FltkError::Internal(FltkErrorKind::ImageFormatError))
881            } else {
882                Ok(RgbImage {
883                    inner: ImageRC::from(img),
884                })
885            }
886        }
887    }
888
889    /// Initializes a new raw `RgbImage` from shared data, doesn't handle the data's lifetime
890    /// # Safety
891    /// The data must be valid for the lifetime of the image
892    /// # Errors
893    /// Errors on invalid or unsupported image format
894    pub unsafe fn from_data(
895        data: &[u8],
896        w: i32,
897        h: i32,
898        depth: ColorDepth,
899    ) -> Result<RgbImage, FltkError> {
900        unsafe {
901            let sz = w * h * depth as i32;
902            if sz > data.len() as i32 {
903                return Err(FltkError::Internal(FltkErrorKind::ImageFormatError));
904            }
905            let img = Fl_RGB_Image_from_data(data.as_ptr(), w, h, depth as i32, 0);
906            if img.is_null() || Fl_RGB_Image_fail(img) < 0 {
907                Err(FltkError::Internal(FltkErrorKind::ImageFormatError))
908            } else {
909                Ok(RgbImage {
910                    inner: ImageRC::from(img),
911                })
912            }
913        }
914    }
915
916    /// Initializes a new raw `RgbImage`, copies the data and handles its lifetime.
917    /// If you need to work with RGB data,
918    /// # Errors
919    /// Errors on invalid or unsupported image format
920    /// # Safety
921    /// Passing wrong line data can read to over or underflow
922    pub unsafe fn new_ext(
923        data: &[u8],
924        w: i32,
925        h: i32,
926        depth: i32,
927        line_data: i32,
928    ) -> Result<RgbImage, FltkError> {
929        unsafe {
930            let img = Fl_RGB_Image_new(data.as_ptr(), w, h, depth, line_data);
931            if img.is_null() || Fl_RGB_Image_fail(img) < 0 {
932                Err(FltkError::Internal(FltkErrorKind::ImageFormatError))
933            } else {
934                Ok(RgbImage {
935                    inner: ImageRC::from(img),
936                })
937            }
938        }
939    }
940
941    /// Initializes a new raw `RgbImage` from shared data, doesn't handle the data's lifetime
942    /// # Safety
943    /// The data must be valid for the lifetime of the image
944    /// # Errors
945    /// Errors on invalid or unsupported image format
946    /// # Safety
947    /// Passing wrong line data can read to over or underflow
948    pub unsafe fn from_data_ext(
949        data: &[u8],
950        w: i32,
951        h: i32,
952        depth: i32,
953        line_data: i32,
954    ) -> Result<RgbImage, FltkError> {
955        unsafe {
956            let img = Fl_RGB_Image_from_data(data.as_ptr(), w, h, depth, line_data);
957            if img.is_null() || Fl_RGB_Image_fail(img) < 0 {
958                Err(FltkError::Internal(FltkErrorKind::ImageFormatError))
959            } else {
960                Ok(RgbImage {
961                    inner: ImageRC::from(img),
962                })
963            }
964        }
965    }
966
967    /// Creates an `RgbImage` from a pixmap
968    pub fn from_pixmap(image: &Pixmap) -> RgbImage {
969        unsafe { RgbImage::from_image_ptr(Fl_RGB_Image_from_pixmap(*image.inner as _) as _) }
970    }
971
972    /// Deconstructs a raw `RgbImage` into parts
973    /// # Safety
974    /// Destructures the image into its raw elements
975    pub unsafe fn into_parts(self) -> (Vec<u8>, i32, i32) {
976        let w = self.data_w();
977        let h = self.data_h();
978        (self.as_rgb_data(), w, h)
979    }
980
981    #[allow(clippy::too_many_lines)]
982    /// Convert from one `ColorDepth` to another `ColorDepth`
983    pub fn convert(&self, new_depth: ColorDepth) -> Result<RgbImage, FltkError> {
984        let depth = self.depth() as i32;
985        let new_depth = new_depth as i32;
986        if depth == new_depth {
987            Ok(self.copy())
988        } else {
989            let w = self.data_w();
990            let h = self.data_h();
991            let data = self.as_rgb_data();
992            let mut temp = Vec::new();
993            match depth {
994                1 => match new_depth {
995                    2 => {
996                        for i in data {
997                            temp.push(i);
998                            temp.push(255);
999                        }
1000                        assert!(temp.len() as i32 == w * h * 2);
1001                        RgbImage::new(&temp, w, h, ColorDepth::La8)
1002                    }
1003                    3 => {
1004                        for i in data {
1005                            temp.push(i);
1006                            temp.push(i);
1007                            temp.push(i);
1008                        }
1009                        assert!(temp.len() as i32 == w * h * 3);
1010                        RgbImage::new(&temp, w, h, ColorDepth::Rgb8)
1011                    }
1012                    4 => {
1013                        for i in data {
1014                            temp.push(i);
1015                            temp.push(i);
1016                            temp.push(i);
1017                            temp.push(255);
1018                        }
1019                        assert!(temp.len() as i32 == w * h * 4);
1020                        RgbImage::new(&temp, w, h, ColorDepth::Rgba8)
1021                    }
1022                    _ => unreachable!(),
1023                },
1024                2 => match new_depth {
1025                    1 => {
1026                        for (i, item) in data.iter().enumerate() {
1027                            if i % 2 == 0 {
1028                                temp.push(*item);
1029                            } else {
1030                                // skip
1031                            }
1032                        }
1033                        assert!(temp.len() as i32 == w * h);
1034                        RgbImage::new(&temp, w, h, ColorDepth::L8)
1035                    }
1036                    3 => {
1037                        for (i, item) in data.iter().enumerate() {
1038                            if i % 2 == 0 {
1039                                temp.push(*item);
1040                                temp.push(*item);
1041                                temp.push(*item);
1042                            } else {
1043                                // skip
1044                            }
1045                        }
1046                        assert!(temp.len() as i32 == w * h * 3);
1047                        RgbImage::new(&temp, w, h, ColorDepth::Rgb8)
1048                    }
1049                    4 => {
1050                        for (i, item) in data.iter().enumerate() {
1051                            temp.push(*item);
1052                            if i % 2 == 0 {
1053                                temp.push(*item);
1054                                temp.push(*item);
1055                            }
1056                        }
1057                        assert!(temp.len() as i32 == w * h * 4);
1058                        RgbImage::new(&temp, w, h, ColorDepth::Rgba8)
1059                    }
1060                    _ => unreachable!(),
1061                },
1062                3 => match new_depth {
1063                    1 => {
1064                        for pixel in data.chunks_exact(3) {
1065                            temp.push(
1066                                (f32::from(pixel[0]) * 0.299
1067                                    + f32::from(pixel[1]) * 0.587
1068                                    + f32::from(pixel[2]) * 0.114)
1069                                    as u8,
1070                            );
1071                        }
1072                        assert!(temp.len() as i32 == w * h);
1073                        RgbImage::new(&temp, w, h, ColorDepth::L8)
1074                    }
1075                    2 => {
1076                        for pixel in data.chunks_exact(3) {
1077                            temp.push(
1078                                (f32::from(pixel[0]) * 0.299
1079                                    + f32::from(pixel[1]) * 0.587
1080                                    + f32::from(pixel[2]) * 0.114)
1081                                    as u8,
1082                            );
1083                            temp.push(255);
1084                        }
1085                        assert!(temp.len() as i32 == w * h * 2);
1086                        RgbImage::new(&temp, w, h, ColorDepth::La8)
1087                    }
1088                    4 => {
1089                        for pixel in data.chunks_exact(3) {
1090                            temp.push(pixel[0]);
1091                            temp.push(pixel[1]);
1092                            temp.push(pixel[2]);
1093                            temp.push(255);
1094                        }
1095                        assert!(temp.len() as i32 == w * h * 4);
1096                        RgbImage::new(&temp, w, h, ColorDepth::Rgba8)
1097                    }
1098                    _ => unreachable!(),
1099                },
1100                4 => match new_depth {
1101                    1 => {
1102                        for pixel in data.chunks_exact(4) {
1103                            temp.push(
1104                                (f32::from(pixel[0]) * 0.299
1105                                    + f32::from(pixel[1]) * 0.587
1106                                    + f32::from(pixel[2]) * 0.114)
1107                                    as u8,
1108                            );
1109                        }
1110                        assert!(temp.len() as i32 == w * h);
1111                        RgbImage::new(&temp, w, h, ColorDepth::L8)
1112                    }
1113                    2 => {
1114                        for pixel in data.chunks_exact(4) {
1115                            temp.push(
1116                                (f32::from(pixel[0]) * 0.299
1117                                    + f32::from(pixel[1]) * 0.587
1118                                    + f32::from(pixel[2]) * 0.114)
1119                                    as u8,
1120                            );
1121                            temp.push(pixel[3]);
1122                        }
1123                        assert!(temp.len() as i32 == w * h * 2);
1124                        RgbImage::new(&temp, w, h, ColorDepth::La8)
1125                    }
1126                    3 => {
1127                        for pixel in data.chunks_exact(4) {
1128                            temp.push(pixel[0]);
1129                            temp.push(pixel[1]);
1130                            temp.push(pixel[2]);
1131                        }
1132                        assert!(temp.len() as i32 == w * h * 3);
1133                        RgbImage::new(&temp, w, h, ColorDepth::Rgb8)
1134                    }
1135                    _ => unreachable!(),
1136                },
1137                _ => unreachable!(),
1138            }
1139        }
1140    }
1141
1142    /// Convert black pixels to transparent
1143    pub fn convert_transparent(&self) -> Result<RgbImage, FltkError> {
1144        let depth = self.depth() as i32;
1145        if depth == 2 || depth == 4 {
1146            Ok(self.copy())
1147        } else {
1148            let w = self.w();
1149            let h = self.h();
1150            let data = self.as_rgb_data();
1151            let mut temp = Vec::new();
1152            match depth {
1153                1 => {
1154                    for i in data {
1155                        temp.push(i);
1156                        if i == 0 {
1157                            temp.push(0);
1158                        } else {
1159                            temp.push(255);
1160                        }
1161                    }
1162                    assert!(temp.len() as i32 == w * h * 2);
1163                    RgbImage::new(&temp, w, h, ColorDepth::La8)
1164                }
1165                3 => {
1166                    for pixel in data.chunks_exact(3) {
1167                        let r = pixel[0];
1168                        let g = pixel[1];
1169                        let b = pixel[2];
1170                        temp.push(r);
1171                        temp.push(g);
1172                        temp.push(b);
1173                        if r == 0 && g == 0 && b == 0 {
1174                            temp.push(0);
1175                        } else {
1176                            temp.push(255);
1177                        }
1178                    }
1179                    assert!(temp.len() as i32 == w * h * 4);
1180                    RgbImage::new(&temp, w, h, ColorDepth::Rgba8)
1181                }
1182                _ => unreachable!(),
1183            }
1184        }
1185    }
1186
1187    /// Blur the image
1188    /// Adapted from <https://www.cairographics.org/cookbook/blur.c/>
1189    pub fn blur(&self, radius: u32) -> Result<RgbImage, FltkError> {
1190        assert!(self.depth() == ColorDepth::Rgba8);
1191        let radius = radius as i32;
1192        let mut src = self.as_rgb_data();
1193        let width = self.w();
1194        let height = self.h();
1195        let depth = self.depth();
1196        let mut dst = vec![0u8; (width * height * depth as i32) as usize];
1197        let mut kernel = [0u8; 17];
1198        let size = kernel.len() as i32;
1199        let half = size / 2;
1200        let src_stride = width * depth as i32;
1201        let dst_stride = src_stride;
1202
1203        let mut x: u32;
1204        let mut y: u32;
1205        let mut z: u32;
1206        let mut w: u32;
1207        let mut p: u32;
1208
1209        let mut a: u32 = 0;
1210        for i in 0..size {
1211            let f = i - half;
1212            let f = f as f32;
1213            kernel[i as usize] = ((-f * f / 30.0).exp() * 80.0) as u8;
1214            a += u32::from(kernel[i as usize]);
1215        }
1216
1217        // Horizontally blur from surface -> temp
1218        for i in 0..height {
1219            let s: &[u32] = unsafe { src[(i * src_stride) as usize..].align_to::<u32>().1 };
1220            let d: &mut [u32] = unsafe { dst[(i * dst_stride) as usize..].align_to_mut::<u32>().1 };
1221            for j in 0..width {
1222                if radius < j && j < width - radius {
1223                    let j = j as usize;
1224                    d[j] = s[j];
1225                    continue;
1226                }
1227
1228                x = 0;
1229                y = 0;
1230                z = 0;
1231                w = 0;
1232                for k in 0..size {
1233                    if j - half + k < 0 || j - half + k >= width {
1234                        continue;
1235                    }
1236
1237                    p = s[(j - half + k) as usize];
1238                    let k = k as usize;
1239
1240                    x += ((p >> 24) & 0xff) * u32::from(kernel[k]);
1241                    y += ((p >> 16) & 0xff) * u32::from(kernel[k]);
1242                    z += ((p >> 8) & 0xff) * u32::from(kernel[k]);
1243                    w += (p & 0xff) * u32::from(kernel[k]);
1244                }
1245                d[j as usize] = ((x / a) << 24) | ((y / a) << 16) | ((z / a) << 8) | (w / a);
1246            }
1247        }
1248
1249        // Then vertically blur from tmp -> surface
1250        for i in 0..height {
1251            let mut s: &mut [u32] =
1252                unsafe { dst[(i * dst_stride) as usize..].align_to_mut::<u32>().1 };
1253            let d: &mut [u32] = unsafe { src[(i * src_stride) as usize..].align_to_mut::<u32>().1 };
1254            for j in 0..width {
1255                if radius < i && i < height - radius {
1256                    let j = j as usize;
1257                    d[j] = s[j];
1258                    continue;
1259                }
1260
1261                x = 0;
1262                y = 0;
1263                z = 0;
1264                w = 0;
1265                for k in 0..size {
1266                    if i - half + k < 0 || i - half + k >= height {
1267                        continue;
1268                    }
1269
1270                    s = unsafe {
1271                        dst[((i - half + k) * dst_stride) as usize..]
1272                            .align_to_mut::<u32>()
1273                            .1
1274                    };
1275                    p = s[j as usize];
1276                    let k = k as usize;
1277                    x += ((p >> 24) & 0xff) * u32::from(kernel[k]);
1278                    y += ((p >> 16) & 0xff) * u32::from(kernel[k]);
1279                    z += ((p >> 8) & 0xff) * u32::from(kernel[k]);
1280                    w += (p & 0xff) * u32::from(kernel[k]);
1281                }
1282                d[j as usize] = ((x / a) << 24) | ((y / a) << 16) | ((z / a) << 8) | (w / a);
1283            }
1284        }
1285        RgbImage::new(&dst, width, height, depth)
1286    }
1287
1288    /// Returns an SRGB image
1289    pub fn to_srgb_image(&self) -> Result<RgbImage, FltkError> {
1290        assert!(self.depth() as i32 >= 3);
1291        let depth = self.depth() as i32;
1292        let w = self.w();
1293        let h = self.h();
1294        fn correct_gamma(v: f32) -> f32 {
1295            if v <= 0.003_130_8 {
1296                v * 12.92
1297            } else {
1298                1.055 * v.powf(1.0 / 2.4) - 0.055
1299            }
1300        }
1301        let mut temp = vec![];
1302        let data = self.as_rgb_data();
1303        match depth {
1304            3 => {
1305                for pixel in data.chunks_exact(3) {
1306                    let r = (correct_gamma(f32::from(pixel[0]) / 255.0) * 255.0) as u8;
1307                    let g = (correct_gamma(f32::from(pixel[1]) / 255.0) * 255.0) as u8;
1308                    let b = (correct_gamma(f32::from(pixel[2]) / 255.0) * 255.0) as u8;
1309                    temp.push(r);
1310                    temp.push(g);
1311                    temp.push(b);
1312                }
1313                assert!(temp.len() as i32 == w * h * 3);
1314                RgbImage::new(&temp, w, h, ColorDepth::Rgb8)
1315            }
1316            4 => {
1317                for pixel in data.chunks_exact(4) {
1318                    let r = (correct_gamma(f32::from(pixel[0]) / 255.0) * 255.0) as u8;
1319                    let g = (correct_gamma(f32::from(pixel[1]) / 255.0) * 255.0) as u8;
1320                    let b = (correct_gamma(f32::from(pixel[2]) / 255.0) * 255.0) as u8;
1321                    temp.push(r);
1322                    temp.push(g);
1323                    temp.push(b);
1324                    temp.push(pixel[3]);
1325                }
1326                assert!(temp.len() as i32 == w * h * 4);
1327                RgbImage::new(&temp, w, h, ColorDepth::Rgba8)
1328            }
1329            _ => unreachable!(),
1330        }
1331    }
1332
1333    /// Sets the scaling algorithm
1334    pub fn set_scaling_algorithm(algorithm: RgbScaling) {
1335        unsafe { Fl_RGB_Image_set_scaling_algorithm(algorithm as i32) }
1336    }
1337
1338    /// Gets the scaling algorithm
1339    pub fn scaling_algorithm() -> RgbScaling {
1340        unsafe { mem::transmute(Fl_RGB_Image_scaling_algorithm()) }
1341    }
1342}
1343
1344#[cfg(feature = "use-images")]
1345/// Creates a struct holding a Windows icon (.ico) image
1346#[derive(Debug)]
1347pub struct IcoImage {
1348    inner: ImageRC<*mut Fl_ICO_Image>,
1349}
1350
1351#[cfg(feature = "use-images")]
1352crate::macros::image::impl_image_ext!(IcoImage, Fl_ICO_Image);
1353
1354#[cfg(feature = "use-images")]
1355impl IcoImage {
1356    /// Loads the image from a filesystem path, doesn't check for the validity of the data
1357    /// # Errors
1358    /// Errors on non-existent path or invalid format
1359    pub fn load<P: AsRef<std::path::Path>>(path: P) -> Result<IcoImage, FltkError> {
1360        Self::load_(path.as_ref())
1361    }
1362
1363    fn load_(path: &std::path::Path) -> Result<IcoImage, FltkError> {
1364        if !path.exists() {
1365            return Err(FltkError::Internal(FltkErrorKind::ResourceNotFound));
1366        }
1367        unsafe {
1368            let temp = path.to_str().ok_or_else(|| {
1369                FltkError::Unknown(String::from("Failed to convert path to string"))
1370            })?;
1371            if !images_registered() {
1372                register_images();
1373            }
1374            let temp = CString::safe_new(temp);
1375            let image_ptr = Fl_ICO_Image_new(temp.as_ptr(), -1);
1376            if image_ptr.is_null() {
1377                Err(FltkError::Internal(FltkErrorKind::ResourceNotFound))
1378            } else {
1379                if Fl_ICO_Image_fail(image_ptr) < 0 {
1380                    return Err(FltkError::Internal(FltkErrorKind::ImageFormatError));
1381                }
1382                Ok(IcoImage {
1383                    inner: ImageRC::from(image_ptr),
1384                })
1385            }
1386        }
1387    }
1388
1389    /// Loads the image from data/memory
1390    /// # Errors
1391    /// Errors on invalid format
1392    pub fn from_data(data: &[u8]) -> Result<IcoImage, FltkError> {
1393        unsafe {
1394            if data.is_empty() {
1395                Err(FltkError::Internal(FltkErrorKind::ResourceNotFound))
1396            } else {
1397                if !images_registered() {
1398                    register_images();
1399                }
1400                let x = Fl_ICO_Image_from_data(data.as_ptr(), data.len() as _, -1);
1401                if x.is_null() {
1402                    Err(FltkError::Internal(FltkErrorKind::ResourceNotFound))
1403                } else {
1404                    if Fl_ICO_Image_fail(x) < 0 {
1405                        return Err(FltkError::Internal(FltkErrorKind::ImageFormatError));
1406                    }
1407                    Ok(IcoImage {
1408                        inner: ImageRC::from(x),
1409                    })
1410                }
1411            }
1412        }
1413    }
1414
1415    /// Get the icon directory entry
1416    pub fn icon_dir_entry(&self) -> Vec<IconDirEntry> {
1417        unsafe {
1418            let mut size = 0;
1419            let ret = Fl_ICO_Image_icondirentry(*self.inner, &mut size) as *mut IconDirEntry;
1420            std::slice::from_raw_parts(ret, size as _).to_owned()
1421        }
1422    }
1423}
1424
1425#[cfg(feature = "use-images")]
1426use std::os::raw::c_int;
1427
1428#[cfg(feature = "use-images")]
1429/// Icon directory entry
1430#[repr(C)]
1431#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1432pub struct IconDirEntry {
1433    ///  Image width
1434    b_width: c_int,
1435    ///  Image height
1436    b_height: c_int,
1437    ///  Number of colors (0 if  ≥ 8bpp)
1438    b_color_count: c_int,
1439    ///  Reserved
1440    b_reserve: c_int,
1441    ///  Color Planes
1442    w_planes: c_int,
1443    ///  Bits per pixel
1444    w_bit_count: c_int,
1445    ///  Resource size in bytes
1446    dw_bytes_in_res: c_int,
1447    ///  Offset to the image
1448    dw_image_offset: c_int,
1449}