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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
//! Utilities to load PNG images

extern crate png;
extern crate sdl2;

use sdl2::pixels::{Color, PixelFormatEnum};
use sdl2::render::{BlendMode, Canvas};
use sdl2::video::Window;

use self::png::{ColorType, Decoder, DecodingError};

use std::io::Read;

use drawable::{DrawSettings, Drawable, KnownSize, Position, State};

/// A PNG image. Currently only supports RGB and RGBA color types
#[derive(Clone)]
pub struct PngImage {
    /// The width of the image
    pub width: usize,
    /// The height of the image
    pub height: usize,
    /// The data in the image, stored in chunks of 4 per pixel, containing the image in RGBA order
    pub data: Vec<u8>,
}

impl PngImage {
    /// Load an image from a specified source.
    pub fn load_from_path<R: Read>(r: R) -> Result<Self, DecodingError> {
        PngImage::load_from_path_transform(r, |x| x)
    }

    /// Load an image and apply a function to each pixel. Mostly used by [`LatexObj`] to fix alpha
    ///
    /// [`LatexObj`]: ../latex/latex_obj/struct.LatexObj.html
    pub fn load_from_path_transform<R: Read, F: Fn(Color) -> Color>(
        r: R,
        transform: F,
    ) -> Result<Self, DecodingError> {
        let (info, mut reader) = Decoder::new(r).read_info()?;

        let (width, height) = (info.width as usize, info.height as usize);

        let mut data = vec![0; width * height * 4];

        for y in 0..height {
            if let Some(row) = reader.next_row()? {
                assert_eq!(row.len(), width * info.color_type.samples());

                for (x, col) in row.chunks(info.color_type.samples()).enumerate() {
                    let sdl_col = match info.color_type {
                        ColorType::RGB => Color::RGB(col[0], col[1], col[2]),
                        ColorType::RGBA => Color::RGBA(col[0], col[1], col[2], col[3]),
                        _ => unimplemented!(),
                    };

                    let sdl_col = transform(sdl_col);

                    data[(y * width + x) * 4] = sdl_col.r;
                    data[(y * width + x) * 4 + 1] = sdl_col.g;
                    data[(y * width + x) * 4 + 2] = sdl_col.b;
                    data[(y * width + x) * 4 + 3] = sdl_col.a;
                }
            }
        }

        Ok(PngImage {
            width,
            height,
            data,
        })
    }
}

impl Drawable for PngImage {
    fn content(&self) -> Vec<&dyn Drawable> {
        vec![]
    }
    fn content_mut(&mut self) -> Vec<&mut dyn Drawable> {
        vec![]
    }

    fn draw(&self, canvas: &mut Canvas<Window>, pos: &Position, _settings: DrawSettings) {
        let creator = canvas.texture_creator();
        let mut texture = creator
            .create_texture_target(
                // The pixels are stored in RGBA order in self.data, but using
                // PixelFormatEnum::RGBA8888 gives the wrong image for some odd reason.
                Some(PixelFormatEnum::ABGR8888),
                self.width as u32,
                self.height as u32,
            )
            .expect("Can't make texture");

        texture.set_blend_mode(BlendMode::Blend);

        texture
            .update(None, self.data.as_slice(), 4 * self.width)
            .expect("Can't update");

        let rect = pos.into_rect_with_size_unbounded(self.width as u32, self.height as u32);

        canvas.copy(&texture, None, rect).expect("Can't copy");
    }

    fn step(&mut self) {}
    fn state(&self) -> State {
        State::Final
    }
}

/// Something that can act as, or contains, an image.
pub trait ImageContainer: KnownSize + Sized {
    /// Retrieve the data in the image
    fn get_data(&self) -> &Vec<u8>;

    /// Retrieve the data in the image, mutably
    fn get_data_mut(&mut self) -> &mut Vec<u8>;

    /// Retrieve the data in the image, consuming the object
    fn into_data(self) -> Vec<u8>;

    /// Convert the object to a dynamic KnownSize object, as rust doesn't support calling KnownSize
    /// -methods directly on this object
    fn as_knownsize(&self) -> &dyn KnownSize {
        self
    }
}

impl KnownSize for PngImage {
    fn width(&self) -> usize {
        self.width
    }
    fn height(&self) -> usize {
        self.height
    }
}

impl ImageContainer for PngImage {
    fn get_data(&self) -> &Vec<u8> {
        &self.data
    }
    fn get_data_mut(&mut self) -> &mut Vec<u8> {
        &mut self.data
    }
    fn into_data(self) -> Vec<u8> {
        self.data
    }
}