1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
//! Renderable image buffers.

use widget_prelude::*;

use base::Downcast;

use std::mem;

/// An image buffer allocated by IUP.
///
/// ##Note: Not a Renderable Widget
/// While this type can be dereferenced and converted to `BaseWidget`, it is *not* a renderable
/// widget and adding it to a container will have no visual effect.
///
/// Instead, it should be set on another widget type that implements the `ImageContainer` trait,
/// which will handle the actual rendering.
///
/// ##Note: Memory Usage
/// This struct should be freed by calling `.destroy()` on it when it is no longer in use.
/// Otherwise, it will be freed when `kiss_ui::show_gui()` exits^([citation needed]).
/// 
/// ##Note: Cloning
/// Cloning this image does not duplicate its allocation. Thus, destroying one image cloned from
/// another will destroy them both.
pub struct Image(IUPPtr);

impl Image {
    /// Create a new RGB image buffer from a slice of 3-byte tuples, copying the data into a new
    /// allocation.
    ///
    /// See `transmute_buffer_rgb()` in this module.
    ///
    /// ##Panics
    /// If `width * height` is not equal to `pixels.len()`.
    pub fn new_rgb(width: u32, height: u32, pixels: &[(u8, u8, u8)]) -> Image {
        assert_eq!((width * height) as usize, pixels.len());
        unsafe { 
            let ptr = ::iup_sys::IupImageRGB(width as i32, height as i32, pixels.as_ptr() as *const u8); 
            Self::from_ptr(ptr)
        }
    }

    /// Create a new RGBA image buffer from a slice of 4-byte tuples, copying the data into a new
    /// allocation.
    ///
    /// See `transmute_buffer_rgba` in this module.
    ///
    /// ##Panics
    /// If `width * height` is not equal to `pixels.len()`.
    pub fn new_rgba(width: u32, height: u32, pixels: &[(u8, u8, u8, u8)]) -> Image {
        assert_eq!((width * height) as usize, pixels.len());
        unsafe { 
            let ptr = ::iup_sys::IupImageRGBA(width as i32, height as i32, pixels.as_ptr() as *const u8);
            Self::from_ptr(ptr)
        }
    } 
}

impl Destroy for Image {}

impl_widget! { Image, ["image", "imagergb", "imagergba"] }

/// Cast a slice of bytes to a slice of 3-byte tuples without copying.
///
/// Returns `None` if `buf.len()` is not evenly divisible by 3.
pub fn transmute_buffer_rgb(buf: &[u8]) -> Option<&[(u8, u8, u8)]> {
    if buf.len() % 3 == 0 {
        Some(unsafe { mem::transmute(buf) })
    } else {
        None
    }
}

/// Cast a slice of bytes to a slice of 4-byte tuples without copying.
///
/// Returns `None` if `buf.len()` is not evenly divisible by 4.
pub fn transmute_buffer_rgba(buf: &[u8]) -> Option<&[(u8, u8, u8, u8)]> {
    if buf.len() % 4 == 0 {
        Some(unsafe { mem::transmute(buf) })
    } else {
        None
    }
}

/// A trait describing an object that can render an image within itself.
pub trait ImageContainer: Widget {
    /// Set the image this widget is to render and return `self` for method chaining.
    fn set_image(self, image: Image) -> Self {
        self.set_attr_handle(::attrs::IMAGE, image);
        self
    }

    /// Get a copy of the image set on this widget, if any.
    fn get_image(&self) -> Option<Image> {
        use base::BaseWidget;

        self.get_attr_handle(::attrs::IMAGE)
            .map(BaseWidget::try_downcast::<Image>)
            .and_then(Result::ok)
    }
}