use rin::graphics::CoordinateOrigin;
use std::rc::Rc;
use std::cell::RefCell;
use ttf_iosevka_ss08;
use rin::events::*;
use rin::window;
use rin::window::Events;
use rin::graphics::{ttf, Ttf};
use rin::math::{Pnt2, pnt2, Vec2, vec2};
use rect_packer::DensePacker;
use image::{self, GenericImage};
use std::ops::Deref;
use std::path::Path;
use rin::util::{Result, Error};
pub fn rendered_stream<'a, 'b>(events: Stream<'a, window::Event>) -> (Sender<'b, ()>, Stream<'b, bool>) where 'a: 'b {
let (rendered_sender, rendered_stream) = stream();
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
enum Event{
Start,
Render,
Update,
}
let rendered_stream = rendered_stream.map(|_| Event::Render)
.merge(events.update().map(|_| Event::Update))
.scan(Event::Start, |prev_event, event| {
let rendered = !(*prev_event == Event::Update && event == Event::Update);
*prev_event = event;
Some(rendered)
})
.dedup();
(rendered_sender, rendered_stream)
}
pub fn shorten<'t>(text: &'t str, font: &Ttf, width: f32) -> &'t str{
let text_width = font.bounding_box(text, 0., 0., CoordinateOrigin::TopLeft).width;
if text_width > width {
let indices = (0..text.len()-1).collect::<Vec<_>>();
let c = indices.binary_search_by(|i| {
let w = font.bounding_box(&text[0 .. *i], 0., 0., CoordinateOrigin::TopLeft).width;
((w * 10000.) as isize).cmp(&((width * 10000.) as isize))
});
let i = match c{
Ok(i) => i,
Err(i) => i,
};
if i > 0 {
&text[0 .. i - 1]
}else{
""
}
}else{
text
}
}
thread_local!(static DEFAULT_FONT: Rc<Ttf> =
Rc::new(ttf::Builder::from_bytes(ttf_iosevka_ss08::REGULAR, 14.).create().unwrap()));
pub fn default_font() -> Rc<Ttf>{
DEFAULT_FONT.with(|font| font.clone())
}
struct TextureAtlas{
changed: bool,
atlas: image::ImageBuffer<image::Rgba<u8>, Vec<u8>>,
occupancy_map: DensePacker,
}
impl TextureAtlas{
fn find_free_space(&mut self, w: u32, h: u32) -> Option<Pnt2<u32>>{
self.occupancy_map.pack(w as i32, h as i32, false)
.map(|rect| pnt2(rect.x as u32, rect.y as u32))
}
}
thread_local!(static TEXTURE_CACHE: RefCell<TextureAtlas> = {
let texture = image::ImageBuffer::from_vec(1024, 1024, vec![0; 1024 * 1024 * 4]).unwrap();
let occupancy_map = DensePacker::new(1024, 1024);
RefCell::new(TextureAtlas{
changed: false,
atlas: texture,
occupancy_map,
})
});
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
pub struct TextureHandle{
x: u32,
y: u32,
w: u32,
h: u32,
}
impl TextureHandle{
pub fn transform_uv(&self, uv: &Vec2) -> Vec2{
let uv_img = uv.component_mul(&vec2(self.w as f32, self.h as f32)) + vec2(self.x as f32, self.y as f32);
uv_img.component_mul(&vec2(1. / 1024., 1. / 1024.))
}
}
pub fn allocate_texture<C: Deref<Target = [u8]>>(img: &image::ImageBuffer<image::Rgba<u8>, C>) -> Option<TextureHandle>{
TEXTURE_CACHE.with(|atlas| {
let mut atlas = atlas.borrow_mut();
let handle = atlas.find_free_space(img.width(), img.height())
.map(|pos| TextureHandle{
x: pos.x,
y: pos.y,
w: img.width(),
h: img.height()
});
if let Some(handle) = handle.as_ref() {
let mut subview = image::SubImage::new(&mut atlas.atlas, handle.x, handle.y, img.width(), img.height());
for ((_,_,dst), src) in subview.pixels_mut().zip(img.pixels()){
*dst = *src;
}
}
atlas.changed = true;
handle
})
}
pub fn load_texture<P: AsRef<Path>>(path: P) -> Result<TextureHandle>{
let img = image::open(path.as_ref())
.map_err(|err| Error::with_cause(&format!("Error loading image from {:?}", path.as_ref()), err))?;
allocate_texture(&img.to_rgba())
.ok_or(Error::new("Couldn't allocate enough space on texture atlas"))
}
pub fn load_texture_from_memory(data: &[u8]) -> Result<TextureHandle>{
let img = image::load_from_memory(data)
.map_err(|err| Error::with_cause("Error loading image from memory", err))?;
allocate_texture(&img.to_rgba())
.ok_or(Error::new("Couldn't allocate enough space on texture atlas"))
}
pub fn stencil_to_hex<C: Deref<Target = [u8]>>(img: &image::ImageBuffer<image::Rgba<u8>, C>) -> Vec<u8>{
let mut hex = vec![];
let mut shift = 0;
let mut cur = 0;
for (i, p) in img.pixels().enumerate(){
if p[3] > 0 {
cur |= 1 << shift;
}
let i = i + 1;
if i % 8 == 0 {
hex.push(cur);
cur = 0;
shift = 0;
}else{
shift += 1;
}
}
hex
}
pub fn stencil_from_hex(w: u32, h: u32, hex: &[u8]) -> image::GrayAlphaImage {
let mut img = image::GrayAlphaImage::new(w, h);
for (i, p) in img.pixels_mut().enumerate(){
let shift = i % 8;
let offset = i / 8;
let cur = (hex[offset] >> shift) & 1;
p[0] = 255;
p[1] = cur * 255;
}
img
}
#[cfg(any(feature = "gl", feature = "gles", feature="webgl"))]
pub mod gl{
use rin::gl;
use rin::graphics;
use std::cell::{RefCell, UnsafeCell};
use std::collections::HashMap;
use rin::color::consts::WHITE;
use rin::color::ToRgba;
use rin::gl::RenderSurface;
thread_local!(static FONT_MATERIAL_CACHE: UnsafeCell<HashMap<usize, gl::ttf::Material>> =
UnsafeCell::new(HashMap::new()));
pub fn font_material<'a, C: ToRgba, R: RenderSurface>(context: &gl::Renderer<R>, ttf: &graphics::Ttf, color: &C) -> &'a gl::ttf::Material{
FONT_MATERIAL_CACHE.with(|cache| {
let cache = unsafe{ &mut *cache.get() };
let material = (*cache)
.entry(context.id())
.or_insert_with(|| gl::Ttf::default_material_for(context, ttf, &WHITE).unwrap());
material.set_color(color);
material
})
}
thread_local!(static TEXTURE_CACHE: RefCell<Option<gl::Texture>> = RefCell::default());
fn update_texture_atlas<R: RenderSurface>(gl: &gl::Renderer<R>){
super::TEXTURE_CACHE.with(|img_atlas|{
let mut img_atlas = img_atlas.borrow_mut();
if img_atlas.changed {
TEXTURE_CACHE.with(|atlas|{
let mut atlas = atlas.borrow_mut();
if atlas.is_none() {
*atlas = Some(gl.new_texture().from_image(&img_atlas.atlas, gl::TEXTURE_2D).unwrap());
}else{
atlas.as_mut().unwrap().load_image(&img_atlas.atlas, 0).unwrap();
}
});
img_atlas.changed = false;
}
})
}
pub(crate) fn with_texture_atlas<F, R>(gl: &gl::Renderer<R>, f: F)
where F: Fn(Option<&gl::Texture>),
R: RenderSurface,
{
update_texture_atlas(gl);
TEXTURE_CACHE.with(|tex| f(tex.borrow().as_ref()))
}
}