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
use std::rc::Rc;
use std::cell::RefCell;
use std::collections::{HashMap, VecDeque};
use web_sys::{HtmlImageElement};
use crate::{Error, NikoError, Event, event, graphics::TextureId};
use glow::HasContext;

#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Image {
    pub(crate) id: u32,
}

impl Image {
    pub(crate) fn new(id: u32) -> Self {
        Self {
            id,
        }
    }
}

pub struct Images {
    images: HashMap<u32, HtmlImageElement>,
    textures: HashMap<u32, TextureId>,
    sizes: HashMap<u32, (u32, u32)>,
    next_id: u32,
}

impl Images {
    pub fn new() -> Self {
        Self {
            images: HashMap::new(),
            textures: HashMap::new(),
            sizes: HashMap::new(),
            next_id: 0,
        }
    }

    pub(crate) fn create_image_from_url(&mut self, url: &str, event_queue_handle: Rc<RefCell<VecDeque<Event>>>) -> Result<Image, Error> {
        let id = self.next_id;
        self.next_id += 1;

        let html_image = HtmlImageElement::new().map_err(|_| NikoError::PlatformError("could not create a new HtmlImageElement".to_string()))?;
        html_image.set_src(url);

        let closure = event(&html_image, "load", move |_event: web_sys::Event| {
            event_queue_handle.borrow_mut().push_back(Event::ImageLoaded(id));
        }).map_err(|_| NikoError::PlatformError("could not create a loading closure for an image".to_string()))?;
        closure.forget();

        self.images.insert(id, html_image);

        Ok(Image::new(id))
    }

    #[cfg(target_arch = "wasm32")]
    pub(crate) fn finish_loading(&mut self, id: u32, gl: &glow::Context) -> Result<(), Error> {
        let image = self.images.get(&id).unwrap();
        let width = image.width();
        let height = image.height();
        self.sizes.insert(id, (width, height));

        let texture = unsafe {
            let texture = gl.create_texture()
                .map_err(|error| NikoError::PlatformError(error))?;

            gl.bind_texture(glow::TEXTURE_2D, Some(texture));
            gl.tex_image_2d_with_html_image(
                glow::TEXTURE_2D,
                0,
                glow::RGBA as i32,
                glow::RGBA,
                glow::UNSIGNED_BYTE,
                &image,
            );

            gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_WRAP_S, glow::CLAMP_TO_EDGE as i32);
            gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_WRAP_T, glow::CLAMP_TO_EDGE as i32);
            gl.tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_MIN_FILTER, glow::LINEAR as i32);

            texture
        };

        self.textures.insert(id, texture);

        Ok(())
    }

    #[cfg(not(target_arch = "wasm32"))]
    pub(crate) fn finish_loading(&mut self, id: u32, gl: &glow::Context) -> Result<(), Error> {
        unimplemented!()
    }

    pub fn find_texture(&self, image: Image) -> Option<TextureId> {
        match self.textures.get(&image.id) {
            Some(texture) => Some(*texture),
            None => None,
        }
    }

    pub fn find_size(&self, image: Image) -> Option<(u32, u32)> {
        match self.sizes.get(&image.id) {
            Some((width, height)) => Some((*width, *height)),
            None => None,
        }
    }
}