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
//! Functions and types relating to textures.

use std::path::Path;
use std::rc::Rc;

use glm::Vec3;
use image;

use error::{Result, TetraError};
use graphics::opengl::GLTexture;
use graphics::{self, DrawParams, Drawable, Rectangle};
use Context;

/// Texture data.
///
/// This type acts as a lightweight handle to the associated graphics hardware data,
/// and so can be cloned with little overhead.
#[derive(Clone, PartialEq)]
pub struct Texture {
    pub(crate) handle: Rc<GLTexture>,
}

impl Texture {
    /// Creates a new texture from the given file.
    pub fn new<P: AsRef<Path>>(ctx: &mut Context, path: P) -> Result<Texture> {
        let image = image::open(path).map_err(TetraError::Image)?.to_rgba();
        let (width, height) = image.dimensions();

        let texture = ctx.gl.new_texture(width as i32, height as i32);
        ctx.gl
            .set_texture_data(&texture, &image, 0, 0, width as i32, height as i32);

        Ok(Texture::from_handle(texture))
    }

    pub(crate) fn from_handle(handle: GLTexture) -> Texture {
        Texture {
            handle: Rc::new(handle),
        }
    }
}

impl Drawable for Texture {
    fn draw<T: Into<DrawParams>>(&self, ctx: &mut Context, params: T) {
        let params = params.into();

        let texture_width = self.handle.width() as f32;
        let texture_height = self.handle.height() as f32;
        let clip = params
            .clip
            .unwrap_or_else(|| Rectangle::new(0.0, 0.0, texture_width, texture_height));

        let transform = params.build_matrix();

        let pos1 = transform * Vec3::new(0.0, 0.0, 1.0);
        let pos2 = transform * Vec3::new(clip.width, clip.height, 1.0);

        let u1 = clip.x / texture_width;
        let v1 = clip.y / texture_height;
        let u2 = (clip.x + clip.width) / texture_width;
        let v2 = (clip.y + clip.height) / texture_height;

        graphics::set_texture(ctx, self);

        graphics::push_quad(
            ctx,
            pos1.x,
            pos1.y,
            pos2.x,
            pos2.y,
            u1,
            v1,
            u2,
            v2,
            params.color,
        );
    }
}