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