fltk/
image.rs

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