extern crate graphics as piston_graphics;
use Rect;
use image;
use render;
use text;
#[doc(inline)]
pub use self::piston_graphics::{Context, DrawState, Graphics, ImageSize, Transformed};
pub fn primitives<'a, P, G, T, Img, C, F>(
mut primitives: P,
context: Context,
graphics: &'a mut G,
text_texture_cache: &'a mut T,
glyph_cache: &'a mut text::GlyphCache,
image_map: &'a image::Map<Img>,
mut cache_queued_glyphs: C,
mut texture_from_image: F,
)
where P: render::PrimitiveWalker,
G: Graphics<Texture=T>,
T: ImageSize,
C: FnMut(&mut G, &mut T, text::rt::Rect<u32>, &[u8]),
F: FnMut(&Img) -> &T,
{
let mut glyph_rectangles = Vec::new();
while let Some(prim) = render::PrimitiveWalker::next_primitive(&mut primitives) {
primitive(prim,
context,
graphics,
text_texture_cache,
glyph_cache,
image_map,
&mut glyph_rectangles,
&mut cache_queued_glyphs,
&mut texture_from_image);
}
}
pub fn primitive<'a, Img, G, T, C, F>(
primitive: render::Primitive,
context: Context,
graphics: &'a mut G,
text_texture_cache: &'a mut T,
glyph_cache: &'a mut text::GlyphCache,
image_map: &'a image::Map<Img>,
glyph_rectangles: &mut Vec<([f64; 4], [f64; 4])>,
mut cache_queued_glyphs: C,
mut texture_from_image: F,
)
where G: Graphics<Texture=T>,
T: ImageSize,
C: FnMut(&mut G, &mut T, text::rt::Rect<u32>, &[u8]),
F: FnMut(&Img) -> &T,
{
let render::Primitive { kind, scizzor, rect, .. } = primitive;
let view_size = context.get_view_size();
let context = context.trans(view_size[0] / 2.0, view_size[1] / 2.0).scale(1.0, -1.0);
let context = crop_context(context, scizzor);
match kind {
render::PrimitiveKind::Rectangle { color } => {
let (l, b, w, h) = rect.l_b_w_h();
let lbwh = [l, b, w, h];
let rectangle = piston_graphics::Rectangle::new(color.to_fsa());
rectangle.draw(lbwh, &context.draw_state, context.transform, graphics);
},
render::PrimitiveKind::Polygon { color, points } => {
let color = color.to_fsa();
let polygon = piston_graphics::Polygon::new(color);
polygon.draw(points, &context.draw_state, context.transform, graphics);
},
render::PrimitiveKind::Lines { color, cap, thickness, points } => {
use widget::primitive::line::Cap;
let color = color.to_fsa();
let mut points = points.iter();
if let Some(first) = points.next() {
let line = match cap {
Cap::Flat => piston_graphics::Line::new(color, thickness / 2.0),
Cap::Round => piston_graphics::Line::new_round(color, thickness / 2.0),
};
let mut start = first;
for end in points {
let coords = [start[0], start[1], end[0], end[1]];
line.draw(coords, &context.draw_state, context.transform, graphics);
start = end;
}
}
},
render::PrimitiveKind::Text { color, text, font_id } => {
let dpi_factor = context.viewport
.map(|v| v.window_size[0] as f32 / view_size[0] as f32)
.unwrap_or(1.0);
let positioned_glyphs = text.positioned_glyphs(dpi_factor);
let context = context.scale(1.0, -1.0).trans(-view_size[0] / 2.0, -view_size[1] / 2.0);
for glyph in positioned_glyphs.iter() {
glyph_cache.queue_glyph(font_id.index(), glyph.clone());
}
glyph_cache.cache_queued(|rect, data| {
cache_queued_glyphs(graphics, text_texture_cache, rect, data)
}).unwrap();
let cache_id = font_id.index();
let (tex_w, tex_h) = text_texture_cache.get_size();
let color = color.to_fsa();
let rectangles = positioned_glyphs.into_iter()
.filter_map(|g| glyph_cache.rect_for(cache_id, g).ok().unwrap_or(None))
.map(|(uv_rect, screen_rect)| {
let rectangle = {
let div_dpi_factor = |s| (s as f32 / dpi_factor as f32) as f64;
let left = div_dpi_factor(screen_rect.min.x);
let top = div_dpi_factor(screen_rect.min.y);
let right = div_dpi_factor(screen_rect.max.x);
let bottom = div_dpi_factor(screen_rect.max.y);
let w = right - left;
let h = bottom - top;
[left, top, w, h]
};
let source_rectangle = {
let x = (uv_rect.min.x * tex_w as f32) as f64;
let y = (uv_rect.min.y * tex_h as f32) as f64;
let w = ((uv_rect.max.x - uv_rect.min.x) * tex_w as f32) as f64;
let h = ((uv_rect.max.y - uv_rect.min.y) * tex_h as f32) as f64;
[x, y, w, h]
};
(rectangle, source_rectangle)
});
glyph_rectangles.clear();
glyph_rectangles.extend(rectangles);
piston_graphics::image::draw_many(&glyph_rectangles,
color,
text_texture_cache,
&context.draw_state,
context.transform,
graphics);
},
render::PrimitiveKind::Image { image_id, color, source_rect } => {
if let Some(img) = image_map.get(&image_id) {
let mut image = piston_graphics::image::Image::new();
image.color = color.map(|c| c.to_fsa());
if let Some(source_rect) = source_rect {
let (x, y, w, h) = source_rect.x_y_w_h();
image.source_rectangle = Some([x, y, w, h]);
}
let (left, top, w, h) = rect.l_t_w_h();
image.rectangle = Some([0.0, 0.0, w, h]);
let context = context.trans(left, top).scale(1.0, -1.0);
let transform = context.transform;
let draw_state = &context.draw_state;
let tex = texture_from_image(img);
image.draw(tex, draw_state, transform, graphics);
}
},
render::PrimitiveKind::Other(_widget) => {
},
}
}
fn crop_context(context: Context, rect: Rect) -> Context {
use utils::map_range;
let Context { draw_state, .. } = context;
let (x, y, w, h) = rect.x_y_w_h();
let view_dim = context.get_view_size();
let draw_dim = match context.viewport {
Some(viewport) => [viewport.draw_size[0] as f64, viewport.draw_size[1] as f64],
None => view_dim,
};
let left = -view_dim[0] / 2.0;
let right = view_dim[0] / 2.0;
let bottom = -view_dim[1] / 2.0;
let top = view_dim[1] / 2.0;
let left_x = x - w as f64 / 2.0;
let top_y = y - h as f64 / 2.0;
let x = map_range(left_x, left, right, 0, draw_dim[0] as i32);
let y = map_range(top_y, bottom, top, 0, draw_dim[1] as i32);
let w_scale = draw_dim[0] / view_dim[0];
let h_scale = draw_dim[1] / view_dim[1];
let w = w * w_scale;
let h = h * h_scale;
let x_neg = if x < 0 { x } else { 0 };
let y_neg = if y < 0 { y } else { 0 };
let mut x = ::std::cmp::max(0, x) as u32;
let mut y = ::std::cmp::max(0, y) as u32;
let mut w = ::std::cmp::max(0, (w as i32 + x_neg)) as u32;
let mut h = ::std::cmp::max(0, (h as i32 + y_neg)) as u32;
if let Some(rect) = draw_state.scissor {
let (r_x, r_y, r_w, r_h) = (rect[0], rect[1], rect[2], rect[3]);
if x + w < r_x || r_x + r_w < x || y + h < r_y || r_y + r_h < y {
w = 0;
h = 0;
} else {
let (a_l, a_r, a_b, a_t) = (x, x+w, y, y+h);
let (b_l, b_r, b_b, b_t) = (r_x, r_x+r_w, r_y, r_y+r_h);
let l = if a_l > b_l { a_l } else { b_l };
let r = if a_r < b_r { a_r } else { b_r };
let b = if a_b > b_b { a_b } else { b_b };
let t = if a_t < b_t { a_t } else { b_t };
x = l;
y = b;
w = r - l;
h = t - b;
}
}
Context { draw_state: draw_state.scissor([x, y, w, h]), ..context }
}