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#[derive(Debug)]
12pub struct Image {
13 inner: ImageRC<*mut Fl_Image>,
14}
15
16#[repr(i32)]
18#[derive(Debug, Copy, Clone, PartialEq, Eq)]
19pub enum RgbScaling {
20 Nearest = 0,
22 Bilinear,
24}
25
26crate::macros::image::impl_image_ext!(Image, Fl_Image);
27
28impl Image {
29 pub fn set_scaling_algorithm(algorithm: RgbScaling) {
31 unsafe { Fl_Image_set_scaling_algorithm(algorithm as i32) }
32 }
33
34 pub fn scaling_algorithm() -> RgbScaling {
36 unsafe { mem::transmute(Fl_Image_scaling_algorithm()) }
37 }
38}
39
40#[cfg(feature = "use-images")]
41#[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 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 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#[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 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 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#[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 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 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#[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 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 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 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#[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 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 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#[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 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 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 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
477 pub struct AnimGifImageFlags: u16 {
478 const None = 0;
480 const DONT_START = 1;
485 const DONT_RESIZE_CANVAS = 2;
490 const DONT_SET_AS_IMAGE = 4;
495 const OPTIMIZE_MEMORY = 8;
502 const LOG_FLAG = 64;
504 const DEBUG_FLAG = 128;
506 }
507}
508
509#[cfg(feature = "use-images")]
510#[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 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 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 pub fn delay(&self, frame: i32) -> f64 {
601 unsafe { Fl_Anim_GIF_Image_delay(*self.inner, frame) }
602 }
603
604 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 pub fn is_animated(&self) -> bool {
611 unsafe { Fl_Anim_GIF_Image_is_animated(*self.inner) != 0 }
612 }
613
614 pub fn set_speed(&mut self, speed: f64) {
616 unsafe { Fl_Anim_GIF_Image_set_speed(*self.inner, speed) }
617 }
618
619 pub fn speed(&mut self) -> f64 {
621 unsafe { Fl_Anim_GIF_Image_speed(*self.inner) }
622 }
623
624 pub fn start(&mut self) -> bool {
626 unsafe { Fl_Anim_GIF_Image_start(*self.inner) != 0 }
627 }
628
629 pub fn stop(&mut self) -> bool {
631 unsafe { Fl_Anim_GIF_Image_stop(*self.inner) != 0 }
632 }
633
634 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 pub fn playing(&self) -> bool {
647 unsafe { Fl_Anim_GIF_Image_playing(*self.inner) != 0 }
648 }
649}
650
651#[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 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#[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 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#[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 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#[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 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#[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 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#[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 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 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 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 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 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 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 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 }
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 }
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 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 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 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 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 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 pub fn set_scaling_algorithm(algorithm: RgbScaling) {
1315 unsafe { Fl_RGB_Image_set_scaling_algorithm(algorithm as i32) }
1316 }
1317
1318 pub fn scaling_algorithm() -> RgbScaling {
1320 unsafe { mem::transmute(Fl_RGB_Image_scaling_algorithm()) }
1321 }
1322}
1323
1324#[cfg(feature = "use-images")]
1325#[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 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 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 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#[repr(C)]
1411#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1412pub struct IconDirEntry {
1413 b_width: c_int,
1415 b_height: c_int,
1417 b_color_count: c_int,
1419 b_reserve: c_int,
1421 w_planes: c_int,
1423 w_bit_count: c_int,
1425 dw_bytes_in_res: c_int,
1427 dw_image_offset: c_int,
1429}