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
11static IMAGES_REGISTERED: AtomicBool = AtomicBool::new(false);
13
14fn images_registered() -> bool {
16 IMAGES_REGISTERED.load(Ordering::Relaxed)
17}
18
19fn 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#[derive(Debug)]
32pub struct Image {
33 inner: ImageRC<*mut Fl_Image>,
34}
35
36#[repr(i32)]
38#[derive(Debug, Copy, Clone, PartialEq, Eq)]
39pub enum RgbScaling {
40 Nearest = 0,
42 Bilinear,
44}
45
46crate::macros::image::impl_image_ext!(Image, Fl_Image);
47
48impl Image {
49 pub fn set_scaling_algorithm(algorithm: RgbScaling) {
51 unsafe { Fl_Image_set_scaling_algorithm(algorithm as i32) }
52 }
53
54 pub fn scaling_algorithm() -> RgbScaling {
56 unsafe { mem::transmute(Fl_Image_scaling_algorithm()) }
57 }
58}
59
60#[cfg(feature = "use-images")]
61#[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 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 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#[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 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 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#[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 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 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#[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 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 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 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#[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 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 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#[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 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 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 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
497 pub struct AnimGifImageFlags: u16 {
498 const None = 0;
500 const DONT_START = 1;
505 const DONT_RESIZE_CANVAS = 2;
510 const DONT_SET_AS_IMAGE = 4;
515 const OPTIMIZE_MEMORY = 8;
522 const LOG_FLAG = 64;
524 const DEBUG_FLAG = 128;
526 }
527}
528
529#[cfg(feature = "use-images")]
530#[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 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 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 pub fn delay(&self, frame: i32) -> f64 {
621 unsafe { Fl_Anim_GIF_Image_delay(*self.inner, frame) }
622 }
623
624 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 pub fn is_animated(&self) -> bool {
631 unsafe { Fl_Anim_GIF_Image_is_animated(*self.inner) != 0 }
632 }
633
634 pub fn set_speed(&mut self, speed: f64) {
636 unsafe { Fl_Anim_GIF_Image_set_speed(*self.inner, speed) }
637 }
638
639 pub fn speed(&mut self) -> f64 {
641 unsafe { Fl_Anim_GIF_Image_speed(*self.inner) }
642 }
643
644 pub fn start(&mut self) -> bool {
646 unsafe { Fl_Anim_GIF_Image_start(*self.inner) != 0 }
647 }
648
649 pub fn stop(&mut self) -> bool {
651 unsafe { Fl_Anim_GIF_Image_stop(*self.inner) != 0 }
652 }
653
654 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 pub fn playing(&self) -> bool {
667 unsafe { Fl_Anim_GIF_Image_playing(*self.inner) != 0 }
668 }
669}
670
671#[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 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#[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 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#[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 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#[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 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#[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 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#[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 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 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 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 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 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 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 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 }
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 }
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 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 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 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 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 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 pub fn set_scaling_algorithm(algorithm: RgbScaling) {
1335 unsafe { Fl_RGB_Image_set_scaling_algorithm(algorithm as i32) }
1336 }
1337
1338 pub fn scaling_algorithm() -> RgbScaling {
1340 unsafe { mem::transmute(Fl_RGB_Image_scaling_algorithm()) }
1341 }
1342}
1343
1344#[cfg(feature = "use-images")]
1345#[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 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 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 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#[repr(C)]
1431#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1432pub struct IconDirEntry {
1433 b_width: c_int,
1435 b_height: c_int,
1437 b_color_count: c_int,
1439 b_reserve: c_int,
1441 w_planes: c_int,
1443 w_bit_count: c_int,
1445 dw_bytes_in_res: c_int,
1447 dw_image_offset: c_int,
1449}