extern crate window as pistoncore_window;
extern crate graphics as piston_graphics;
extern crate gfx;
extern crate gfx_core;
extern crate gfx_device_gl;
extern crate gfx_graphics;
extern crate shader_version;
extern crate texture;
use self::shader_version::OpenGL;
use self::gfx_graphics::{Gfx2d, GfxGraphics};
use self::gfx_core::factory::Typed;
use self::gfx::Device;
use self::pistoncore_window::{OpenGLWindow, Size};
use piston_input::RenderArgs;
use super::draw;
use image;
use render;
use text;
pub use self::piston_graphics::{Context, DrawState, Graphics, ImageSize, Transformed};
pub use self::gfx_graphics::{GlyphError, Texture, TextureSettings, Flip};
pub type GfxEncoder = gfx::Encoder<gfx_device_gl::Resources, gfx_device_gl::CommandBuffer>;
pub type Glyphs = gfx_graphics::GlyphCache<gfx_device_gl::Resources, gfx_device_gl::Factory>;
pub type G2d<'a> = GfxGraphics<'a, gfx_device_gl::Resources, gfx_device_gl::CommandBuffer>;
pub type G2dTexture<'a> = Texture<gfx_device_gl::Resources>;
pub struct GfxContext {
pub encoder: GfxEncoder,
pub device: gfx_device_gl::Device,
pub output_color: gfx::handle::RenderTargetView<
gfx_device_gl::Resources, gfx::format::Srgba8>,
pub output_stencil: gfx::handle::DepthStencilView<
gfx_device_gl::Resources, gfx::format::DepthStencil>,
pub g2d: Gfx2d<gfx_device_gl::Resources>,
pub factory: gfx_device_gl::Factory,
}
fn create_main_targets(dim: gfx::tex::Dimensions) ->
(gfx::handle::RenderTargetView<gfx_device_gl::Resources, gfx::format::Srgba8>,
gfx::handle::DepthStencilView<gfx_device_gl::Resources, gfx::format::DepthStencil>)
{
use self::gfx_core::factory::Typed;
use self::gfx::format::{DepthStencil, Format, Formatted, Srgba8};
let color_format: Format = <Srgba8 as Formatted>::get_format();
let depth_format: Format = <DepthStencil as Formatted>::get_format();
let (output_color, output_stencil) =
gfx_device_gl::create_main_targets_raw(dim,
color_format.0,
depth_format.0);
let output_color = Typed::new(output_color);
let output_stencil = Typed::new(output_stencil);
(output_color, output_stencil)
}
impl GfxContext {
pub fn new<W>(window: &mut W, opengl: OpenGL, samples: u8) -> Self
where W: OpenGLWindow
{
let (device, mut factory) = gfx_device_gl::create(|s| window.get_proc_address(s) as *const _);
let draw_size = window.draw_size();
let (output_color, output_stencil) = {
let aa = samples as gfx::tex::NumSamples;
let dim = (draw_size.width as u16, draw_size.height as u16,
1, aa.into());
create_main_targets(dim)
};
let g2d = Gfx2d::new(opengl, &mut factory);
let encoder = factory.create_command_buffer().into();
GfxContext {
encoder: encoder,
device: device,
output_color: output_color,
output_stencil: output_stencil,
g2d: g2d,
factory: factory,
}
}
pub fn draw_2d<F, U>(&mut self, f: F, args: RenderArgs) -> U where
F: FnOnce(draw::Context, &mut G2d) -> U
{
let res = self.g2d.draw(
&mut self.encoder,
&self.output_color,
&self.output_stencil,
args.viewport(),
f
);
self.encoder.flush(&mut self.device);
res
}
pub fn after_render(&mut self) {
self.device.cleanup();
}
pub fn check_resize(&mut self, draw_size: Size) {
let dim = self.output_color.raw().get_dimensions();
let (w, h) = (dim.0, dim.1);
if w != draw_size.width as u16 || h != draw_size.height as u16 {
let dim = (draw_size.width as u16,
draw_size.height as u16,
dim.2, dim.3);
let (output_color, output_stencil) = create_main_targets(dim);
self.output_color = output_color;
self.output_stencil = output_stencil;
}
}
}
pub struct GlyphCache {
cache: text::GlyphCache,
texture: G2dTexture<'static>,
vertex_data: Vec<u8>,
}
impl GlyphCache {
pub fn new_from_factory(factory: &mut gfx_device_gl::Factory, width: u32, height: u32) -> GlyphCache {
const SCALE_TOLERANCE: f32 = 0.1;
const POSITION_TOLERANCE: f32 = 0.1;
let cache = text::GlyphCache::new(width, height, SCALE_TOLERANCE, POSITION_TOLERANCE);
let buffer_len = width as usize * height as usize;
let init = vec![128; buffer_len];
let settings = TextureSettings::new();
let texture = G2dTexture::from_memory_alpha(factory, &init, width, height, &settings).unwrap();
GlyphCache {
cache: cache,
texture: texture,
vertex_data: Vec::new(),
}
}
}
pub fn draw<'a, 'b, P, Img, F>(context: draw::Context,
graphics: &'a mut G2d<'b>,
primitives: P,
glyph_cache: &'a mut GlyphCache,
image_map: &'a image::Map<Img>,
texture_from_image: F)
where P: render::PrimitiveWalker,
F: FnMut(&Img) -> &G2dTexture<'static>,
{
let GlyphCache { ref mut texture, ref mut cache, ref mut vertex_data } = *glyph_cache;
let cache_queued_glyphs_fn = |graphics: &mut G2d,
cache: &mut G2dTexture<'static>,
rect: text::rt::Rect<u32>,
data: &[u8]|
{
cache_queued_glyphs(graphics, cache, rect, data, vertex_data);
};
draw::primitives(
primitives,
context,
graphics,
texture,
cache,
image_map,
cache_queued_glyphs_fn,
texture_from_image,
);
}
pub fn draw_primitive<'a, 'b, Img, F>(context: draw::Context,
graphics: &'a mut G2d<'b>,
primitive: render::Primitive,
glyph_cache: &'a mut GlyphCache,
image_map: &'a image::Map<Img>,
glyph_rectangles: &mut Vec<([f64; 4], [f64; 4])>,
texture_from_image: F)
where F: FnMut(&Img) -> &G2dTexture<'static>,
{
let GlyphCache { ref mut texture, ref mut cache, ref mut vertex_data } = *glyph_cache;
let cache_queued_glyphs_fn = |graphics: &mut G2d,
cache: &mut G2dTexture<'static>,
rect: text::rt::Rect<u32>,
data: &[u8]|
{
cache_queued_glyphs(graphics, cache, rect, data, vertex_data);
};
draw::primitive(
primitive,
context,
graphics,
texture,
cache,
image_map,
glyph_rectangles,
cache_queued_glyphs_fn,
texture_from_image,
);
}
fn cache_queued_glyphs(graphics: &mut G2d,
cache: &mut G2dTexture<'static>,
rect: text::rt::Rect<u32>,
data: &[u8],
vertex_data: &mut Vec<u8>)
{
use self::texture::UpdateTexture;
struct Bytes { b: u8, i: u8 }
impl Iterator for Bytes {
type Item = u8;
fn next(&mut self) -> Option<Self::Item> {
let b = match self.i {
0 => 255,
1 => 255,
2 => 255,
3 => self.b,
_ => return None,
};
self.i += 1;
Some(b)
}
}
let offset = [rect.min.x, rect.min.y];
let size = [rect.width(), rect.height()];
let format = self::texture::Format::Rgba8;
let encoder = &mut graphics.encoder;
vertex_data.clear();
vertex_data.extend(data.iter().flat_map(|&b| Bytes { b: b, i: 0 }));
UpdateTexture::update(cache, encoder, format, &vertex_data[..], offset, size)
.expect("Failed to update texture");
}