Move the generic UI code in the renderer crate to a separate crate

This commit is contained in:
Patrick Walton 2019-03-05 15:13:55 -08:00
parent e09b447d0d
commit ad0691c146
13 changed files with 776 additions and 690 deletions

12
Cargo.lock generated
View File

@ -271,6 +271,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]] [[package]]
@ -514,6 +515,7 @@ dependencies = [
"pathfinder_gpu 0.1.0", "pathfinder_gpu 0.1.0",
"pathfinder_renderer 0.1.0", "pathfinder_renderer 0.1.0",
"pathfinder_svg 0.1.0", "pathfinder_svg 0.1.0",
"pathfinder_ui 0.1.0",
"rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"sdl2 0.32.1 (registry+https://github.com/rust-lang/crates.io-index)", "sdl2 0.32.1 (registry+https://github.com/rust-lang/crates.io-index)",
"usvg 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "usvg 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -562,6 +564,7 @@ dependencies = [
"pathfinder_geometry 0.3.0", "pathfinder_geometry 0.3.0",
"pathfinder_gpu 0.1.0", "pathfinder_gpu 0.1.0",
"pathfinder_simd 0.3.0", "pathfinder_simd 0.3.0",
"pathfinder_ui 0.1.0",
"quickcheck 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "quickcheck 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
@ -586,6 +589,15 @@ dependencies = [
[[package]] [[package]]
name = "pathfinder_ui" name = "pathfinder_ui"
version = "0.1.0" version = "0.1.0"
dependencies = [
"hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"pathfinder_geometry 0.3.0",
"pathfinder_gpu 0.1.0",
"pathfinder_simd 0.3.0",
"serde 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.88 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "phf" name = "phf"

View File

@ -32,3 +32,6 @@ path = "../../renderer"
[dependencies.pathfinder_svg] [dependencies.pathfinder_svg]
path = "../../svg" path = "../../svg"
[dependencies.pathfinder_ui]
path = "../../ui"

View File

@ -19,13 +19,13 @@ use pathfinder_geometry::basic::point::{Point2DF32, Point2DI32, Point3DF32};
use pathfinder_geometry::basic::rect::RectF32; use pathfinder_geometry::basic::rect::RectF32;
use pathfinder_geometry::basic::transform2d::Transform2DF32; use pathfinder_geometry::basic::transform2d::Transform2DF32;
use pathfinder_geometry::basic::transform3d::{Perspective, Transform3DF32}; use pathfinder_geometry::basic::transform3d::{Perspective, Transform3DF32};
use pathfinder_geometry::color::ColorU;
use pathfinder_gl::GLDevice; use pathfinder_gl::GLDevice;
use pathfinder_gpu::{DepthFunc, DepthState, Device, Primitive, RenderState, Resources}; use pathfinder_gpu::{DepthFunc, DepthState, Device, Primitive, RenderState, Resources};
use pathfinder_gpu::{StencilFunc, StencilState, UniformData}; use pathfinder_gpu::{StencilFunc, StencilState, UniformData};
use pathfinder_renderer::builder::{RenderOptions, RenderTransform, SceneBuilder}; use pathfinder_renderer::builder::{RenderOptions, RenderTransform, SceneBuilder};
use pathfinder_renderer::gpu::renderer::Renderer; use pathfinder_renderer::gpu::renderer::Renderer;
use pathfinder_renderer::gpu_data::BuiltScene; use pathfinder_renderer::gpu_data::BuiltScene;
use pathfinder_renderer::paint::ColorU;
use pathfinder_renderer::post::{DEFRINGING_KERNEL_CORE_GRAPHICS, STEM_DARKENING_FACTORS}; use pathfinder_renderer::post::{DEFRINGING_KERNEL_CORE_GRAPHICS, STEM_DARKENING_FACTORS};
use pathfinder_renderer::scene::Scene; use pathfinder_renderer::scene::Scene;
use pathfinder_renderer::z_buffer::ZBuffer; use pathfinder_renderer::z_buffer::ZBuffer;

View File

@ -12,9 +12,10 @@ use crate::Options;
use nfd::Response; use nfd::Response;
use pathfinder_geometry::basic::point::Point2DI32; use pathfinder_geometry::basic::point::Point2DI32;
use pathfinder_geometry::basic::rect::RectI32; use pathfinder_geometry::basic::rect::RectI32;
use pathfinder_geometry::color::ColorU;
use pathfinder_gpu::{Device, Resources}; use pathfinder_gpu::{Device, Resources};
use pathfinder_renderer::gpu::debug::{DebugUI, PADDING, TEXT_COLOR, WINDOW_COLOR}; use pathfinder_renderer::gpu::debug::DebugUI;
use pathfinder_renderer::paint::ColorU; use pathfinder_ui::{PADDING, TEXT_COLOR, WINDOW_COLOR};
use std::f32::consts::PI; use std::f32::consts::PI;
use std::path::PathBuf; use std::path::PathBuf;
@ -120,7 +121,7 @@ impl<D> DemoUI<D> where D: Device {
debug_ui: &mut DebugUI<D>, debug_ui: &mut DebugUI<D>,
event: &mut UIEvent, event: &mut UIEvent,
action: &mut UIAction) { action: &mut UIAction) {
let bottom = debug_ui.framebuffer_size().y() - PADDING; let bottom = debug_ui.ui.framebuffer_size().y() - PADDING;
// Draw effects button. // Draw effects button.
let effects_button_position = Point2DI32::new(PADDING, bottom - BUTTON_HEIGHT); let effects_button_position = Point2DI32::new(PADDING, bottom - BUTTON_HEIGHT);
@ -218,13 +219,13 @@ impl<D> DemoUI<D> where D: Device {
return; return;
} }
let bottom = debug_ui.framebuffer_size().y() - PADDING; let bottom = debug_ui.ui.framebuffer_size().y() - PADDING;
let effects_panel_y = bottom - (BUTTON_HEIGHT + PADDING + EFFECTS_PANEL_HEIGHT); let effects_panel_y = bottom - (BUTTON_HEIGHT + PADDING + EFFECTS_PANEL_HEIGHT);
debug_ui.draw_solid_rounded_rect(device, debug_ui.ui.draw_solid_rounded_rect(device,
RectI32::new(Point2DI32::new(PADDING, effects_panel_y), RectI32::new(Point2DI32::new(PADDING, effects_panel_y),
Point2DI32::new(EFFECTS_PANEL_WIDTH, Point2DI32::new(EFFECTS_PANEL_WIDTH,
EFFECTS_PANEL_HEIGHT)), EFFECTS_PANEL_HEIGHT)),
WINDOW_COLOR); WINDOW_COLOR);
self.gamma_correction_effect_enabled = self.gamma_correction_effect_enabled =
self.draw_effects_switch(device, self.draw_effects_switch(device,
@ -262,13 +263,13 @@ impl<D> DemoUI<D> where D: Device {
return; return;
} }
let bottom = debug_ui.framebuffer_size().y() - PADDING; let bottom = debug_ui.ui.framebuffer_size().y() - PADDING;
let rotate_panel_y = bottom - (BUTTON_HEIGHT + PADDING + ROTATE_PANEL_HEIGHT); let rotate_panel_y = bottom - (BUTTON_HEIGHT + PADDING + ROTATE_PANEL_HEIGHT);
let rotate_panel_origin = Point2DI32::new(ROTATE_PANEL_X, rotate_panel_y); let rotate_panel_origin = Point2DI32::new(ROTATE_PANEL_X, rotate_panel_y);
let rotate_panel_size = Point2DI32::new(ROTATE_PANEL_WIDTH, ROTATE_PANEL_HEIGHT); let rotate_panel_size = Point2DI32::new(ROTATE_PANEL_WIDTH, ROTATE_PANEL_HEIGHT);
debug_ui.draw_solid_rounded_rect(device, debug_ui.ui.draw_solid_rounded_rect(device,
RectI32::new(rotate_panel_origin, rotate_panel_size), RectI32::new(rotate_panel_origin, rotate_panel_size),
WINDOW_COLOR); WINDOW_COLOR);
let (widget_x, widget_y) = (ROTATE_PANEL_X + PADDING, rotate_panel_y + PADDING); let (widget_x, widget_y) = (ROTATE_PANEL_X + PADDING, rotate_panel_y + PADDING);
let widget_rect = RectI32::new(Point2DI32::new(widget_x, widget_y), let widget_rect = RectI32::new(Point2DI32::new(widget_x, widget_y),
@ -283,13 +284,13 @@ impl<D> DemoUI<D> where D: Device {
let slider_track_rect = let slider_track_rect =
RectI32::new(Point2DI32::new(widget_x, slider_track_y), RectI32::new(Point2DI32::new(widget_x, slider_track_y),
Point2DI32::new(SLIDER_WIDTH, SLIDER_TRACK_HEIGHT)); Point2DI32::new(SLIDER_WIDTH, SLIDER_TRACK_HEIGHT));
debug_ui.draw_rect_outline(device, slider_track_rect, TEXT_COLOR); debug_ui.ui.draw_rect_outline(device, slider_track_rect, TEXT_COLOR);
let slider_knob_x = widget_x + self.rotation - SLIDER_KNOB_WIDTH / 2; let slider_knob_x = widget_x + self.rotation - SLIDER_KNOB_WIDTH / 2;
let slider_knob_rect = let slider_knob_rect =
RectI32::new(Point2DI32::new(slider_knob_x, widget_y), RectI32::new(Point2DI32::new(slider_knob_x, widget_y),
Point2DI32::new(SLIDER_KNOB_WIDTH, SLIDER_KNOB_HEIGHT)); Point2DI32::new(SLIDER_KNOB_WIDTH, SLIDER_KNOB_HEIGHT));
debug_ui.draw_solid_rect(device, slider_knob_rect, TEXT_COLOR); debug_ui.ui.draw_solid_rect(device, slider_knob_rect, TEXT_COLOR);
} }
fn draw_button(&self, fn draw_button(&self,
@ -300,12 +301,12 @@ impl<D> DemoUI<D> where D: Device {
texture: &D::Texture) texture: &D::Texture)
-> bool { -> bool {
let button_rect = RectI32::new(origin, Point2DI32::new(BUTTON_WIDTH, BUTTON_HEIGHT)); let button_rect = RectI32::new(origin, Point2DI32::new(BUTTON_WIDTH, BUTTON_HEIGHT));
debug_ui.draw_solid_rounded_rect(device, button_rect, WINDOW_COLOR); debug_ui.ui.draw_solid_rounded_rect(device, button_rect, WINDOW_COLOR);
debug_ui.draw_rounded_rect_outline(device, button_rect, OUTLINE_COLOR); debug_ui.ui.draw_rounded_rect_outline(device, button_rect, OUTLINE_COLOR);
debug_ui.draw_texture(device, debug_ui.ui.draw_texture(device,
origin + Point2DI32::new(PADDING, PADDING), origin + Point2DI32::new(PADDING, PADDING),
texture, texture,
BUTTON_ICON_COLOR); BUTTON_ICON_COLOR);
event.handle_mouse_down_in_rect(button_rect).is_some() event.handle_mouse_down_in_rect(button_rect).is_some()
} }
@ -320,7 +321,7 @@ impl<D> DemoUI<D> where D: Device {
-> bool { -> bool {
let text_x = PADDING * 2; let text_x = PADDING * 2;
let text_y = window_y + PADDING + BUTTON_TEXT_OFFSET + (BUTTON_HEIGHT + PADDING) * index; let text_y = window_y + PADDING + BUTTON_TEXT_OFFSET + (BUTTON_HEIGHT + PADDING) * index;
debug_ui.draw_text(device, text, Point2DI32::new(text_x, text_y), false); debug_ui.ui.draw_text(device, text, Point2DI32::new(text_x, text_y), false);
let switch_x = PADDING + EFFECTS_PANEL_WIDTH - (SWITCH_SIZE + PADDING); let switch_x = PADDING + EFFECTS_PANEL_WIDTH - (SWITCH_SIZE + PADDING);
let switch_y = window_y + PADDING + (BUTTON_HEIGHT + PADDING) * index; let switch_y = window_y + PADDING + (BUTTON_HEIGHT + PADDING) * index;
@ -344,17 +345,20 @@ impl<D> DemoUI<D> where D: Device {
-> bool { -> bool {
value = self.draw_switch(device, debug_ui, event, origin, value); value = self.draw_switch(device, debug_ui, event, origin, value);
let off_size = debug_ui.measure_text(off_text); let off_size = debug_ui.ui.measure_text(off_text);
let on_size = debug_ui.measure_text(on_text); let on_size = debug_ui.ui.measure_text(on_text);
let off_offset = SWITCH_HALF_SIZE / 2 - off_size / 2; let off_offset = SWITCH_HALF_SIZE / 2 - off_size / 2;
let on_offset = SWITCH_HALF_SIZE + SWITCH_HALF_SIZE / 2 - on_size / 2; let on_offset = SWITCH_HALF_SIZE + SWITCH_HALF_SIZE / 2 - on_size / 2;
let text_top = BUTTON_TEXT_OFFSET; let text_top = BUTTON_TEXT_OFFSET;
debug_ui.draw_text(device, debug_ui.ui.draw_text(device,
off_text, off_text,
origin + Point2DI32::new(off_offset, text_top), origin + Point2DI32::new(off_offset, text_top),
!value); !value);
debug_ui.draw_text(device, on_text, origin + Point2DI32::new(on_offset, text_top), value); debug_ui.ui.draw_text(device,
on_text,
origin + Point2DI32::new(on_offset, text_top),
value);
value value
} }
@ -377,14 +381,14 @@ impl<D> DemoUI<D> where D: Device {
let off_color = if !value { WINDOW_COLOR } else { TEXT_COLOR }; let off_color = if !value { WINDOW_COLOR } else { TEXT_COLOR };
let on_color = if value { WINDOW_COLOR } else { TEXT_COLOR }; let on_color = if value { WINDOW_COLOR } else { TEXT_COLOR };
debug_ui.draw_texture(device, debug_ui.ui.draw_texture(device,
origin + Point2DI32::new(off_offset, PADDING), origin + Point2DI32::new(off_offset, PADDING),
off_texture, off_texture,
off_color); off_color);
debug_ui.draw_texture(device, debug_ui.ui.draw_texture(device,
origin + Point2DI32::new(on_offset, PADDING), origin + Point2DI32::new(on_offset, PADDING),
on_texture, on_texture,
on_color); on_color);
value value
} }
@ -401,20 +405,20 @@ impl<D> DemoUI<D> where D: Device {
value = !value; value = !value;
} }
debug_ui.draw_solid_rounded_rect(device, widget_rect, WINDOW_COLOR); debug_ui.ui.draw_solid_rounded_rect(device, widget_rect, WINDOW_COLOR);
debug_ui.draw_rounded_rect_outline(device, widget_rect, OUTLINE_COLOR); debug_ui.ui.draw_rounded_rect_outline(device, widget_rect, OUTLINE_COLOR);
let highlight_size = Point2DI32::new(SWITCH_HALF_SIZE, BUTTON_HEIGHT); let highlight_size = Point2DI32::new(SWITCH_HALF_SIZE, BUTTON_HEIGHT);
if !value { if !value {
debug_ui.draw_solid_rounded_rect(device, debug_ui.ui.draw_solid_rounded_rect(device,
RectI32::new(origin, highlight_size), RectI32::new(origin, highlight_size),
TEXT_COLOR); TEXT_COLOR);
} else { } else {
let x_offset = SWITCH_HALF_SIZE + 1; let x_offset = SWITCH_HALF_SIZE + 1;
debug_ui.draw_solid_rounded_rect(device, debug_ui.ui.draw_solid_rounded_rect(device,
RectI32::new(origin + Point2DI32::new(x_offset, 0), RectI32::new(origin + Point2DI32::new(x_offset, 0),
highlight_size), highlight_size),
TEXT_COLOR); TEXT_COLOR);
} }
value value

78
geometry/src/color.rs Normal file
View File

@ -0,0 +1,78 @@
// pathfinder/geometry/src/color.rs
//
// Copyright © 2019 The Pathfinder Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use pathfinder_simd::default::F32x4;
use std::fmt::{self, Debug, Formatter};
#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct ColorU {
pub r: u8,
pub g: u8,
pub b: u8,
pub a: u8,
}
impl ColorU {
#[inline]
pub fn black() -> ColorU {
ColorU {
r: 0,
g: 0,
b: 0,
a: 255,
}
}
#[inline]
pub fn to_f32(&self) -> ColorF {
let color = F32x4::new(self.r as f32, self.g as f32, self.b as f32, self.a as f32);
ColorF(color * F32x4::splat(1.0 / 255.0))
}
}
impl Debug for ColorU {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
if self.a == 255 {
write!(formatter, "#{:02x}{:02x}{:02x}", self.r, self.g, self.b)
} else {
write!(formatter,
"rgba({}, {}, {}, {})",
self.r,
self.g,
self.b,
self.a as f32 / 255.0)
}
}
}
#[derive(Clone, Copy)]
pub struct ColorF(pub F32x4);
impl ColorF {
#[inline]
pub fn r(&self) -> f32 {
self.0[0]
}
#[inline]
pub fn g(&self) -> f32 {
self.0[1]
}
#[inline]
pub fn b(&self) -> f32 {
self.0[2]
}
#[inline]
pub fn a(&self) -> f32 {
self.0[3]
}
}

View File

@ -17,6 +17,7 @@ extern crate bitflags;
pub mod basic; pub mod basic;
pub mod clip; pub mod clip;
pub mod color;
pub mod monotonic; pub mod monotonic;
pub mod orientation; pub mod orientation;
pub mod outline; pub mod outline;

View File

@ -23,5 +23,8 @@ path = "../gpu"
[dependencies.pathfinder_simd] [dependencies.pathfinder_simd]
path = "../simd" path = "../simd"
[dependencies.pathfinder_ui]
path = "../ui"
[dev-dependencies] [dev-dependencies]
quickcheck = "0.7" quickcheck = "0.7"

View File

@ -16,92 +16,23 @@
//! The debug font atlas was generated using: https://evanw.github.io/font-texture-generator/ //! The debug font atlas was generated using: https://evanw.github.io/font-texture-generator/
use crate::gpu_data::Stats; use crate::gpu_data::Stats;
use crate::paint::ColorU;
use pathfinder_geometry::basic::point::Point2DI32; use pathfinder_geometry::basic::point::Point2DI32;
use pathfinder_geometry::basic::rect::RectI32; use pathfinder_geometry::basic::rect::RectI32;
use pathfinder_gpu::{BlendState, BufferTarget, BufferUploadMode, Device, Primitive, RenderState}; use pathfinder_gpu::{Device, Resources};
use pathfinder_gpu::{Resources, UniformData, VertexAttrType}; use pathfinder_ui::{PADDING, UI, WINDOW_COLOR};
use pathfinder_simd::default::F32x4; use std::collections::VecDeque;
use serde_json;
use std::collections::{HashMap, VecDeque};
use std::fs::File;
use std::io::BufReader;
use std::ops::{Add, Div}; use std::ops::{Add, Div};
use std::time::Duration; use std::time::Duration;
const SAMPLE_BUFFER_SIZE: usize = 60; const SAMPLE_BUFFER_SIZE: usize = 60;
const DEBUG_TEXTURE_VERTEX_SIZE: usize = 8;
const DEBUG_SOLID_VERTEX_SIZE: usize = 4;
pub const PADDING: i32 = 12;
pub static TEXT_COLOR: ColorU = ColorU { r: 255, g: 255, b: 255, a: 255 };
pub static WINDOW_COLOR: ColorU = ColorU { r: 0, g: 0, b: 0, a: 255 - 90 };
static INVERTED_TEXT_COLOR: ColorU = ColorU { r: 0, g: 0, b: 0, a: 255 };
const PERF_WINDOW_WIDTH: i32 = 375; const PERF_WINDOW_WIDTH: i32 = 375;
const PERF_WINDOW_HEIGHT: i32 = LINE_HEIGHT * 6 + PADDING + 2; const PERF_WINDOW_HEIGHT: i32 = LINE_HEIGHT * 6 + PADDING + 2;
const FONT_ASCENT: i32 = 28; const FONT_ASCENT: i32 = 28;
const LINE_HEIGHT: i32 = 42; const LINE_HEIGHT: i32 = 42;
static FONT_JSON_FILENAME: &'static str = "debug-font.json";
static FONT_PNG_NAME: &'static str = "debug-font";
static CORNER_FILL_PNG_NAME: &'static str = "debug-corner-fill";
static CORNER_OUTLINE_PNG_NAME: &'static str = "debug-corner-outline";
static QUAD_INDICES: [u32; 6] = [0, 1, 3, 1, 2, 3];
static RECT_LINE_INDICES: [u32; 8] = [0, 1, 1, 2, 2, 3, 3, 0];
static OUTLINE_RECT_LINE_INDICES: [u32; 8] = [0, 1, 2, 3, 4, 5, 6, 7];
#[derive(Deserialize)]
#[allow(dead_code)]
pub struct DebugFont {
name: String,
size: i32,
bold: bool,
italic: bool,
width: u32,
height: u32,
characters: HashMap<char, DebugCharacter>,
}
#[derive(Deserialize)]
struct DebugCharacter {
x: i32,
y: i32,
width: i32,
height: i32,
#[serde(rename = "originX")]
origin_x: i32,
#[serde(rename = "originY")]
origin_y: i32,
advance: i32,
}
impl DebugFont {
fn load(resources: &Resources) -> DebugFont {
let mut path = resources.resources_directory.clone();
path.push(FONT_JSON_FILENAME);
serde_json::from_reader(BufReader::new(File::open(path).unwrap())).unwrap()
}
}
pub struct DebugUI<D> where D: Device { pub struct DebugUI<D> where D: Device {
framebuffer_size: Point2DI32, pub ui: UI<D>,
texture_program: DebugTextureProgram<D>,
texture_vertex_array: DebugTextureVertexArray<D>,
font: DebugFont,
solid_program: DebugSolidProgram<D>,
solid_vertex_array: DebugSolidVertexArray<D>,
font_texture: D::Texture,
corner_fill_texture: D::Texture,
corner_outline_texture: D::Texture,
cpu_samples: SampleBuffer<CPUSample>, cpu_samples: SampleBuffer<CPUSample>,
gpu_samples: SampleBuffer<GPUSample>, gpu_samples: SampleBuffer<GPUSample>,
@ -109,42 +40,8 @@ pub struct DebugUI<D> where D: Device {
impl<D> DebugUI<D> where D: Device { impl<D> DebugUI<D> where D: Device {
pub fn new(device: &D, resources: &Resources, framebuffer_size: Point2DI32) -> DebugUI<D> { pub fn new(device: &D, resources: &Resources, framebuffer_size: Point2DI32) -> DebugUI<D> {
let texture_program = DebugTextureProgram::new(device, resources); let ui = UI::new(device, resources, framebuffer_size);
let texture_vertex_array = DebugTextureVertexArray::new(device, &texture_program); DebugUI { ui, cpu_samples: SampleBuffer::new(), gpu_samples: SampleBuffer::new() }
let font = DebugFont::load(resources);
let solid_program = DebugSolidProgram::new(device, resources);
let solid_vertex_array = DebugSolidVertexArray::new(device, &solid_program);
let font_texture = device.create_texture_from_png(resources, FONT_PNG_NAME);
let corner_fill_texture = device.create_texture_from_png(resources, CORNER_FILL_PNG_NAME);
let corner_outline_texture = device.create_texture_from_png(resources,
CORNER_OUTLINE_PNG_NAME);
DebugUI {
framebuffer_size,
texture_program,
texture_vertex_array,
font,
solid_program,
solid_vertex_array,
font_texture,
corner_fill_texture,
corner_outline_texture,
cpu_samples: SampleBuffer::new(),
gpu_samples: SampleBuffer::new(),
}
}
pub fn framebuffer_size(&self) -> Point2DI32 {
self.framebuffer_size
}
pub fn set_framebuffer_size(&mut self, window_size: Point2DI32) {
self.framebuffer_size = window_size;
} }
pub fn add_sample(&mut self, pub fn add_sample(&mut self,
@ -159,460 +56,43 @@ impl<D> DebugUI<D> where D: Device {
pub fn draw(&self, device: &D) { pub fn draw(&self, device: &D) {
// Draw performance window. // Draw performance window.
let bottom = self.framebuffer_size.y() - PADDING; let framebuffer_size = self.ui.framebuffer_size();
let bottom = framebuffer_size.y() - PADDING;
let window_rect = RectI32::new( let window_rect = RectI32::new(
Point2DI32::new(self.framebuffer_size.x() - PADDING - PERF_WINDOW_WIDTH, Point2DI32::new(framebuffer_size.x() - PADDING - PERF_WINDOW_WIDTH,
bottom - PERF_WINDOW_HEIGHT), bottom - PERF_WINDOW_HEIGHT),
Point2DI32::new(PERF_WINDOW_WIDTH, PERF_WINDOW_HEIGHT)); Point2DI32::new(PERF_WINDOW_WIDTH, PERF_WINDOW_HEIGHT));
self.draw_solid_rounded_rect(device, window_rect, WINDOW_COLOR); self.ui.draw_solid_rounded_rect(device, window_rect, WINDOW_COLOR);
let origin = window_rect.origin() + Point2DI32::new(PADDING, PADDING + FONT_ASCENT); let origin = window_rect.origin() + Point2DI32::new(PADDING, PADDING + FONT_ASCENT);
let mean_cpu_sample = self.cpu_samples.mean(); let mean_cpu_sample = self.cpu_samples.mean();
self.draw_text(device, self.ui.draw_text(device,
&format!("Objects: {}", mean_cpu_sample.stats.object_count), &format!("Objects: {}", mean_cpu_sample.stats.object_count),
origin, origin,
false); false);
self.draw_text(device, self.ui.draw_text(device,
&format!("Solid Tiles: {}", mean_cpu_sample.stats.solid_tile_count), &format!("Solid Tiles: {}", mean_cpu_sample.stats.solid_tile_count),
origin + Point2DI32::new(0, LINE_HEIGHT * 1), origin + Point2DI32::new(0, LINE_HEIGHT * 1),
false); false);
self.draw_text(device, self.ui.draw_text(device,
&format!("Mask Tiles: {}", mean_cpu_sample.stats.mask_tile_count), &format!("Mask Tiles: {}", mean_cpu_sample.stats.mask_tile_count),
origin + Point2DI32::new(0, LINE_HEIGHT * 2), origin + Point2DI32::new(0, LINE_HEIGHT * 2),
false); false);
self.draw_text(device, self.ui.draw_text(device,
&format!("Fills: {}", mean_cpu_sample.stats.fill_count), &format!("Fills: {}", mean_cpu_sample.stats.fill_count),
origin + Point2DI32::new(0, LINE_HEIGHT * 3), origin + Point2DI32::new(0, LINE_HEIGHT * 3),
false); false);
self.draw_text(device, self.ui.draw_text(device,
&format!("CPU Time: {:.3} ms", duration_to_ms(mean_cpu_sample.elapsed)), &format!("CPU Time: {:.3} ms", duration_to_ms(mean_cpu_sample.elapsed)),
origin + Point2DI32::new(0, LINE_HEIGHT * 4), origin + Point2DI32::new(0, LINE_HEIGHT * 4),
false); false);
let mean_gpu_sample = self.gpu_samples.mean(); let mean_gpu_sample = self.gpu_samples.mean();
self.draw_text(device, self.ui.draw_text(device,
&format!("GPU Time: {:.3} ms", duration_to_ms(mean_gpu_sample.elapsed)), &format!("GPU Time: {:.3} ms", duration_to_ms(mean_gpu_sample.elapsed)),
origin + Point2DI32::new(0, LINE_HEIGHT * 5), origin + Point2DI32::new(0, LINE_HEIGHT * 5),
false); false);
}
pub fn draw_solid_rect(&self, device: &D, rect: RectI32, color: ColorU) {
self.draw_rect(device, rect, color, true);
}
pub fn draw_rect_outline(&self, device: &D, rect: RectI32, color: ColorU) {
self.draw_rect(device, rect, color, false);
}
fn draw_rect(&self, device: &D, rect: RectI32, color: ColorU, filled: bool) {
let vertex_data = [
DebugSolidVertex::new(rect.origin()),
DebugSolidVertex::new(rect.upper_right()),
DebugSolidVertex::new(rect.lower_right()),
DebugSolidVertex::new(rect.lower_left()),
];
if filled {
self.draw_solid_rects_with_vertex_data(device,
&vertex_data,
&QUAD_INDICES,
color,
true);
} else {
self.draw_solid_rects_with_vertex_data(device,
&vertex_data,
&RECT_LINE_INDICES,
color,
false);
}
}
fn draw_solid_rects_with_vertex_data(&self,
device: &D,
vertex_data: &[DebugSolidVertex],
index_data: &[u32],
color: ColorU,
filled: bool) {
device.bind_vertex_array(&self.solid_vertex_array.vertex_array);
device.upload_to_buffer(&self.solid_vertex_array.vertex_buffer,
vertex_data,
BufferTarget::Vertex,
BufferUploadMode::Dynamic);
device.upload_to_buffer(&self.solid_vertex_array.index_buffer,
index_data,
BufferTarget::Index,
BufferUploadMode::Dynamic);
device.use_program(&self.solid_program.program);
device.set_uniform(&self.solid_program.framebuffer_size_uniform,
UniformData::Vec2(self.framebuffer_size.0.to_f32x4()));
set_color_uniform(device, &self.solid_program.color_uniform, color);
let primitive = if filled { Primitive::Triangles } else { Primitive::Lines };
device.draw_elements(primitive, index_data.len() as u32, &RenderState {
blend: BlendState::RGBOneAlphaOneMinusSrcAlpha,
..RenderState::default()
});
}
pub fn draw_text(&self, device: &D, string: &str, origin: Point2DI32, invert: bool) {
let mut next = origin;
let char_count = string.chars().count();
let mut vertex_data = Vec::with_capacity(char_count * 4);
let mut index_data = Vec::with_capacity(char_count * 6);
for mut character in string.chars() {
if !self.font.characters.contains_key(&character) {
character = '?';
}
let info = &self.font.characters[&character];
let position_rect =
RectI32::new(Point2DI32::new(next.x() - info.origin_x, next.y() - info.origin_y),
Point2DI32::new(info.width as i32, info.height as i32));
let tex_coord_rect = RectI32::new(Point2DI32::new(info.x, info.y),
Point2DI32::new(info.width, info.height));
let first_vertex_index = vertex_data.len();
vertex_data.extend_from_slice(&[
DebugTextureVertex::new(position_rect.origin(), tex_coord_rect.origin()),
DebugTextureVertex::new(position_rect.upper_right(), tex_coord_rect.upper_right()),
DebugTextureVertex::new(position_rect.lower_right(), tex_coord_rect.lower_right()),
DebugTextureVertex::new(position_rect.lower_left(), tex_coord_rect.lower_left()),
]);
index_data.extend(QUAD_INDICES.iter().map(|&i| i + first_vertex_index as u32));
let next_x = next.x() + info.advance;
next.set_x(next_x);
}
let color = if invert { INVERTED_TEXT_COLOR } else { TEXT_COLOR };
self.draw_texture_with_vertex_data(device,
&vertex_data,
&index_data,
&self.font_texture,
color);
}
pub fn draw_texture(&self,
device: &D,
origin: Point2DI32,
texture: &D::Texture,
color: ColorU) {
let position_rect = RectI32::new(origin, device.texture_size(&texture));
let tex_coord_rect = RectI32::new(Point2DI32::default(), position_rect.size());
let vertex_data = [
DebugTextureVertex::new(position_rect.origin(), tex_coord_rect.origin()),
DebugTextureVertex::new(position_rect.upper_right(), tex_coord_rect.upper_right()),
DebugTextureVertex::new(position_rect.lower_right(), tex_coord_rect.lower_right()),
DebugTextureVertex::new(position_rect.lower_left(), tex_coord_rect.lower_left()),
];
self.draw_texture_with_vertex_data(device, &vertex_data, &QUAD_INDICES, texture, color);
}
pub fn measure_text(&self, string: &str) -> i32 {
let mut next = 0;
for mut character in string.chars() {
if !self.font.characters.contains_key(&character) {
character = '?';
}
let info = &self.font.characters[&character];
next += info.advance;
}
next
}
pub fn draw_solid_rounded_rect(&self, device: &D, rect: RectI32, color: ColorU) {
let corner_texture = self.corner_texture(true);
let corner_rects = CornerRects::new(device, rect, corner_texture);
self.draw_rounded_rect_corners(device, color, corner_texture, &corner_rects);
let solid_rect_mid = RectI32::from_points(corner_rects.upper_left.upper_right(),
corner_rects.lower_right.lower_left());
let solid_rect_left = RectI32::from_points(corner_rects.upper_left.lower_left(),
corner_rects.lower_left.upper_right());
let solid_rect_right = RectI32::from_points(corner_rects.upper_right.lower_left(),
corner_rects.lower_right.upper_right());
let vertex_data = vec![
DebugSolidVertex::new(solid_rect_mid.origin()),
DebugSolidVertex::new(solid_rect_mid.upper_right()),
DebugSolidVertex::new(solid_rect_mid.lower_right()),
DebugSolidVertex::new(solid_rect_mid.lower_left()),
DebugSolidVertex::new(solid_rect_left.origin()),
DebugSolidVertex::new(solid_rect_left.upper_right()),
DebugSolidVertex::new(solid_rect_left.lower_right()),
DebugSolidVertex::new(solid_rect_left.lower_left()),
DebugSolidVertex::new(solid_rect_right.origin()),
DebugSolidVertex::new(solid_rect_right.upper_right()),
DebugSolidVertex::new(solid_rect_right.lower_right()),
DebugSolidVertex::new(solid_rect_right.lower_left()),
];
let mut index_data = Vec::with_capacity(18);
index_data.extend(QUAD_INDICES.iter().map(|&index| index + 0));
index_data.extend(QUAD_INDICES.iter().map(|&index| index + 4));
index_data.extend(QUAD_INDICES.iter().map(|&index| index + 8));
self.draw_solid_rects_with_vertex_data(device,
&vertex_data,
&index_data[0..18],
color,
true);
}
pub fn draw_rounded_rect_outline(&self, device: &D, rect: RectI32, color: ColorU) {
let corner_texture = self.corner_texture(false);
let corner_rects = CornerRects::new(device, rect, corner_texture);
self.draw_rounded_rect_corners(device, color, corner_texture, &corner_rects);
let vertex_data = vec![
DebugSolidVertex::new(corner_rects.upper_left.upper_right()),
DebugSolidVertex::new(corner_rects.upper_right.origin()),
DebugSolidVertex::new(corner_rects.upper_right.lower_right()),
DebugSolidVertex::new(corner_rects.lower_right.upper_right()),
DebugSolidVertex::new(corner_rects.lower_left.lower_right()),
DebugSolidVertex::new(corner_rects.lower_right.lower_left()),
DebugSolidVertex::new(corner_rects.upper_left.lower_left()),
DebugSolidVertex::new(corner_rects.lower_left.origin()),
];
let index_data = &OUTLINE_RECT_LINE_INDICES;
self.draw_solid_rects_with_vertex_data(device, &vertex_data, index_data, color, false);
}
fn draw_rounded_rect_corners(&self,
device: &D,
color: ColorU,
texture: &D::Texture,
corner_rects: &CornerRects) {
let corner_size = device.texture_size(&texture);
let tex_coord_rect = RectI32::new(Point2DI32::default(), corner_size);
let vertex_data = vec![
DebugTextureVertex::new(
corner_rects.upper_left.origin(), tex_coord_rect.origin()),
DebugTextureVertex::new(
corner_rects.upper_left.upper_right(), tex_coord_rect.upper_right()),
DebugTextureVertex::new(
corner_rects.upper_left.lower_right(), tex_coord_rect.lower_right()),
DebugTextureVertex::new(
corner_rects.upper_left.lower_left(), tex_coord_rect.lower_left()),
DebugTextureVertex::new(
corner_rects.upper_right.origin(), tex_coord_rect.lower_left()),
DebugTextureVertex::new(
corner_rects.upper_right.upper_right(), tex_coord_rect.origin()),
DebugTextureVertex::new(
corner_rects.upper_right.lower_right(), tex_coord_rect.upper_right()),
DebugTextureVertex::new(
corner_rects.upper_right.lower_left(), tex_coord_rect.lower_right()),
DebugTextureVertex::new(
corner_rects.lower_left.origin(), tex_coord_rect.upper_right()),
DebugTextureVertex::new(
corner_rects.lower_left.upper_right(), tex_coord_rect.lower_right()),
DebugTextureVertex::new(
corner_rects.lower_left.lower_right(), tex_coord_rect.lower_left()),
DebugTextureVertex::new(
corner_rects.lower_left.lower_left(), tex_coord_rect.origin()),
DebugTextureVertex::new(
corner_rects.lower_right.origin(), tex_coord_rect.lower_right()),
DebugTextureVertex::new(
corner_rects.lower_right.upper_right(), tex_coord_rect.lower_left()),
DebugTextureVertex::new(
corner_rects.lower_right.lower_right(), tex_coord_rect.origin()),
DebugTextureVertex::new(
corner_rects.lower_right.lower_left(), tex_coord_rect.upper_right()),
];
let mut index_data = Vec::with_capacity(24);
index_data.extend(QUAD_INDICES.iter().map(|&index| index + 0));
index_data.extend(QUAD_INDICES.iter().map(|&index| index + 4));
index_data.extend(QUAD_INDICES.iter().map(|&index| index + 8));
index_data.extend(QUAD_INDICES.iter().map(|&index| index + 12));
self.draw_texture_with_vertex_data(device, &vertex_data, &index_data, texture, color);
}
fn corner_texture(&self, filled: bool) -> &D::Texture {
if filled { &self.corner_fill_texture } else { &self.corner_outline_texture }
}
fn draw_texture_with_vertex_data(&self,
device: &D,
vertex_data: &[DebugTextureVertex],
index_data: &[u32],
texture: &D::Texture,
color: ColorU) {
device.upload_to_buffer(&self.texture_vertex_array.vertex_buffer,
vertex_data,
BufferTarget::Vertex,
BufferUploadMode::Dynamic);
device.upload_to_buffer(&self.texture_vertex_array.index_buffer,
index_data,
BufferTarget::Index,
BufferUploadMode::Dynamic);
device.bind_vertex_array(&self.texture_vertex_array.vertex_array);
device.use_program(&self.texture_program.program);
device.set_uniform(&self.texture_program.framebuffer_size_uniform,
UniformData::Vec2(self.framebuffer_size.0.to_f32x4()));
device.set_uniform(&self.texture_program.texture_size_uniform,
UniformData::Vec2(device.texture_size(&texture).0.to_f32x4()));
set_color_uniform(device, &self.texture_program.color_uniform, color);
device.bind_texture(texture, 0);
device.set_uniform(&self.texture_program.texture_uniform, UniformData::TextureUnit(0));
device.draw_elements(Primitive::Triangles, index_data.len() as u32, &RenderState {
blend: BlendState::RGBOneAlphaOneMinusSrcAlpha,
..RenderState::default()
});
}
}
struct DebugTextureVertexArray<D> where D: Device {
vertex_array: D::VertexArray,
vertex_buffer: D::Buffer,
index_buffer: D::Buffer,
}
impl<D> DebugTextureVertexArray<D> where D: Device {
fn new(device: &D, debug_texture_program: &DebugTextureProgram<D>)
-> DebugTextureVertexArray<D> {
let (vertex_buffer, index_buffer) = (device.create_buffer(), device.create_buffer());
let vertex_array = device.create_vertex_array();
let position_attr = device.get_vertex_attr(&debug_texture_program.program, "Position");
let tex_coord_attr = device.get_vertex_attr(&debug_texture_program.program, "TexCoord");
device.bind_vertex_array(&vertex_array);
device.use_program(&debug_texture_program.program);
device.bind_buffer(&vertex_buffer, BufferTarget::Vertex);
device.bind_buffer(&index_buffer, BufferTarget::Index);
device.configure_float_vertex_attr(&position_attr,
2,
VertexAttrType::U16,
false,
DEBUG_TEXTURE_VERTEX_SIZE,
0,
0);
device.configure_float_vertex_attr(&tex_coord_attr,
2,
VertexAttrType::U16,
false,
DEBUG_TEXTURE_VERTEX_SIZE,
4,
0);
DebugTextureVertexArray { vertex_array, vertex_buffer, index_buffer }
}
}
struct DebugSolidVertexArray<D> where D: Device {
vertex_array: D::VertexArray,
vertex_buffer: D::Buffer,
index_buffer: D::Buffer,
}
impl<D> DebugSolidVertexArray<D> where D: Device {
fn new(device: &D, debug_solid_program: &DebugSolidProgram<D>) -> DebugSolidVertexArray<D> {
let (vertex_buffer, index_buffer) = (device.create_buffer(), device.create_buffer());
let vertex_array = device.create_vertex_array();
let position_attr = device.get_vertex_attr(&debug_solid_program.program, "Position");
device.bind_vertex_array(&vertex_array);
device.use_program(&debug_solid_program.program);
device.bind_buffer(&vertex_buffer, BufferTarget::Vertex);
device.bind_buffer(&index_buffer, BufferTarget::Index);
device.configure_float_vertex_attr(&position_attr,
2,
VertexAttrType::U16,
false,
DEBUG_SOLID_VERTEX_SIZE,
0,
0);
DebugSolidVertexArray { vertex_array, vertex_buffer, index_buffer }
}
}
struct DebugTextureProgram<D> where D: Device {
program: D::Program,
framebuffer_size_uniform: D::Uniform,
texture_size_uniform: D::Uniform,
texture_uniform: D::Uniform,
color_uniform: D::Uniform,
}
impl<D> DebugTextureProgram<D> where D: Device {
fn new(device: &D, resources: &Resources) -> DebugTextureProgram<D> {
let program = device.create_program(resources, "debug_texture");
let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize");
let texture_size_uniform = device.get_uniform(&program, "TextureSize");
let texture_uniform = device.get_uniform(&program, "Texture");
let color_uniform = device.get_uniform(&program, "Color");
DebugTextureProgram {
program,
framebuffer_size_uniform,
texture_size_uniform,
texture_uniform,
color_uniform,
}
}
}
struct DebugSolidProgram<D> where D: Device {
program: D::Program,
framebuffer_size_uniform: D::Uniform,
color_uniform: D::Uniform,
}
impl<D> DebugSolidProgram<D> where D: Device {
fn new(device: &D, resources: &Resources) -> DebugSolidProgram<D> {
let program = device.create_program(resources, "debug_solid");
let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize");
let color_uniform = device.get_uniform(&program, "Color");
DebugSolidProgram { program, framebuffer_size_uniform, color_uniform }
}
}
#[derive(Clone, Copy, Debug)]
#[allow(dead_code)]
#[repr(C)]
struct DebugTextureVertex {
position_x: i16,
position_y: i16,
tex_coord_x: u16,
tex_coord_y: u16,
}
impl DebugTextureVertex {
fn new(position: Point2DI32, tex_coord: Point2DI32) -> DebugTextureVertex {
DebugTextureVertex {
position_x: position.x() as i16,
position_y: position.y() as i16,
tex_coord_x: tex_coord.x() as u16,
tex_coord_y: tex_coord.y() as u16,
}
}
}
#[derive(Clone, Copy)]
#[allow(dead_code)]
#[repr(C)]
struct DebugSolidVertex {
position_x: i16,
position_y: i16,
}
impl DebugSolidVertex {
fn new(position: Point2DI32) -> DebugSolidVertex {
DebugSolidVertex { position_x: position.x() as i16, position_y: position.y() as i16 }
} }
} }
@ -646,11 +126,6 @@ impl<S> SampleBuffer<S> where S: Add<S, Output=S> + Div<u32, Output=S> + Clone +
} }
} }
fn set_color_uniform<D>(device: &D, uniform: &D::Uniform, color: ColorU) where D: Device {
let color = F32x4::new(color.r as f32, color.g as f32, color.b as f32, color.a as f32);
device.set_uniform(uniform, UniformData::Vec4(color * F32x4::splat(1.0 / 255.0)));
}
#[derive(Clone, Default)] #[derive(Clone, Default)]
struct CPUSample { struct CPUSample {
elapsed: Duration, elapsed: Duration,
@ -709,22 +184,3 @@ impl Div<u32> for GPUSample {
fn duration_to_ms(time: Duration) -> f64 { fn duration_to_ms(time: Duration) -> f64 {
time.as_secs() as f64 * 1000.0 + time.subsec_nanos() as f64 / 1000000.0 time.as_secs() as f64 * 1000.0 + time.subsec_nanos() as f64 / 1000000.0
} }
struct CornerRects {
upper_left: RectI32,
upper_right: RectI32,
lower_left: RectI32,
lower_right: RectI32,
}
impl CornerRects {
fn new<D>(device: &D, rect: RectI32, texture: &D::Texture) -> CornerRects where D: Device {
let size = device.texture_size(texture);
CornerRects {
upper_left: RectI32::new(rect.origin(), size),
upper_right: RectI32::new(rect.upper_right() - Point2DI32::new(size.x(), 0), size),
lower_left: RectI32::new(rect.lower_left() - Point2DI32::new(0, size.y()), size),
lower_right: RectI32::new(rect.lower_right() - size, size),
}
}
}

View File

@ -10,10 +10,11 @@
use crate::gpu::debug::DebugUI; use crate::gpu::debug::DebugUI;
use crate::gpu_data::{Batch, BuiltScene, SolidTileScenePrimitive}; use crate::gpu_data::{Batch, BuiltScene, SolidTileScenePrimitive};
use crate::paint::{ColorU, ObjectShader}; use crate::paint::ObjectShader;
use crate::post::DefringingKernel; use crate::post::DefringingKernel;
use crate::tiles::{TILE_HEIGHT, TILE_WIDTH}; use crate::tiles::{TILE_HEIGHT, TILE_WIDTH};
use pathfinder_geometry::basic::point::{Point2DI32, Point3DF32}; use pathfinder_geometry::basic::point::{Point2DI32, Point3DF32};
use pathfinder_geometry::color::ColorU;
use pathfinder_gpu::{BlendState, BufferTarget, BufferUploadMode, DepthFunc, DepthState, Device}; use pathfinder_gpu::{BlendState, BufferTarget, BufferUploadMode, DepthFunc, DepthState, Device};
use pathfinder_gpu::{Primitive, RenderState, Resources, StencilFunc, StencilState, TextureFormat}; use pathfinder_gpu::{Primitive, RenderState, Resources, StencilFunc, StencilState, TextureFormat};
use pathfinder_gpu::{UniformData, VertexAttrType}; use pathfinder_gpu::{UniformData, VertexAttrType};
@ -193,7 +194,7 @@ impl<D> Renderer<D> where D: Device {
#[inline] #[inline]
pub fn set_main_framebuffer_size(&mut self, new_framebuffer_size: Point2DI32) { pub fn set_main_framebuffer_size(&mut self, new_framebuffer_size: Point2DI32) {
self.main_framebuffer_size = new_framebuffer_size; self.main_framebuffer_size = new_framebuffer_size;
self.debug_ui.set_framebuffer_size(new_framebuffer_size); self.debug_ui.ui.set_framebuffer_size(new_framebuffer_size);
} }
#[inline] #[inline]

View File

@ -10,8 +10,7 @@
//! How a path is to be filled. //! How a path is to be filled.
use pathfinder_simd::default::F32x4; use pathfinder_geometry::color::ColorU;
use std::fmt::{self, Debug, Formatter};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Paint { pub struct Paint {
@ -21,72 +20,6 @@ pub struct Paint {
#[derive(Clone, Copy, PartialEq, Debug)] #[derive(Clone, Copy, PartialEq, Debug)]
pub struct PaintId(pub u16); pub struct PaintId(pub u16);
#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct ColorU {
pub r: u8,
pub g: u8,
pub b: u8,
pub a: u8,
}
impl ColorU {
#[inline]
pub fn black() -> ColorU {
ColorU {
r: 0,
g: 0,
b: 0,
a: 255,
}
}
#[inline]
pub fn to_f32(&self) -> ColorF {
let color = F32x4::new(self.r as f32, self.g as f32, self.b as f32, self.a as f32);
ColorF(color * F32x4::splat(1.0 / 255.0))
}
}
impl Debug for ColorU {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
if self.a == 255 {
write!(formatter, "#{:02x}{:02x}{:02x}", self.r, self.g, self.b)
} else {
write!(formatter,
"rgba({}, {}, {}, {})",
self.r,
self.g,
self.b,
self.a as f32 / 255.0)
}
}
}
#[derive(Clone, Copy)]
pub struct ColorF(pub F32x4);
impl ColorF {
#[inline]
pub fn r(&self) -> f32 {
self.0[0]
}
#[inline]
pub fn g(&self) -> f32 {
self.0[1]
}
#[inline]
pub fn b(&self) -> f32 {
self.0[2]
}
#[inline]
pub fn a(&self) -> f32 {
self.0[3]
}
}
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub struct ShaderId(pub u16); pub struct ShaderId(pub u16);

View File

@ -14,10 +14,11 @@ use pathfinder_geometry::basic::line_segment::LineSegmentF32;
use pathfinder_geometry::basic::point::Point2DF32; use pathfinder_geometry::basic::point::Point2DF32;
use pathfinder_geometry::basic::rect::RectF32; use pathfinder_geometry::basic::rect::RectF32;
use pathfinder_geometry::basic::transform2d::{Transform2DF32, Transform2DF32PathIter}; use pathfinder_geometry::basic::transform2d::{Transform2DF32, Transform2DF32PathIter};
use pathfinder_geometry::color::ColorU;
use pathfinder_geometry::outline::Outline; use pathfinder_geometry::outline::Outline;
use pathfinder_geometry::segment::{Segment, SegmentFlags}; use pathfinder_geometry::segment::{Segment, SegmentFlags};
use pathfinder_geometry::stroke::OutlineStrokeToFill; use pathfinder_geometry::stroke::OutlineStrokeToFill;
use pathfinder_renderer::paint::{ColorU, Paint}; use pathfinder_renderer::paint::Paint;
use pathfinder_renderer::scene::{PathObject, PathObjectKind, Scene}; use pathfinder_renderer::scene::{PathObject, PathObjectKind, Scene};
use std::mem; use std::mem;
use usvg::{Color as SvgColor, Node, NodeExt, NodeKind, Paint as UsvgPaint}; use usvg::{Color as SvgColor, Node, NodeExt, NodeKind, Paint as UsvgPaint};

View File

@ -3,3 +3,21 @@ name = "pathfinder_ui"
version = "0.1.0" version = "0.1.0"
authors = ["Patrick Walton <pcwalton@mimiga.net>"] authors = ["Patrick Walton <pcwalton@mimiga.net>"]
edition = "2018" edition = "2018"
[dependencies]
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
[dependencies.hashbrown]
version = "0.1"
features = ["serde"]
[dependencies.pathfinder_geometry]
path = "../geometry"
[dependencies.pathfinder_gpu]
path = "../gpu"
[dependencies.pathfinder_simd]
path = "../simd"

View File

@ -1 +1,577 @@
// pathfinder/ui/src/lib.rs
//
// Copyright © 2019 The Pathfinder Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! A minimal immediate mode UI, for debugging.
//!
//! This can be used in your own applications as an ultra-minimal lightweight
//! alternative to dear imgui, Conrod, etc.
#[macro_use]
extern crate serde_derive;
use hashbrown::HashMap;
use pathfinder_geometry::basic::point::Point2DI32;
use pathfinder_geometry::basic::rect::RectI32;
use pathfinder_geometry::color::ColorU;
use pathfinder_gpu::{BlendState, BufferTarget, BufferUploadMode, Device, Primitive, RenderState};
use pathfinder_gpu::{Resources, UniformData, VertexAttrType};
use pathfinder_simd::default::F32x4;
use serde_json;
use std::fs::File;
use std::io::BufReader;
const DEBUG_TEXTURE_VERTEX_SIZE: usize = 8;
const DEBUG_SOLID_VERTEX_SIZE: usize = 4;
pub const PADDING: i32 = 12;
pub static TEXT_COLOR: ColorU = ColorU { r: 255, g: 255, b: 255, a: 255 };
pub static WINDOW_COLOR: ColorU = ColorU { r: 0, g: 0, b: 0, a: 255 - 90 };
static INVERTED_TEXT_COLOR: ColorU = ColorU { r: 0, g: 0, b: 0, a: 255 };
static FONT_JSON_FILENAME: &'static str = "debug-font.json";
static FONT_PNG_NAME: &'static str = "debug-font";
static CORNER_FILL_PNG_NAME: &'static str = "debug-corner-fill";
static CORNER_OUTLINE_PNG_NAME: &'static str = "debug-corner-outline";
static QUAD_INDICES: [u32; 6] = [0, 1, 3, 1, 2, 3];
static RECT_LINE_INDICES: [u32; 8] = [0, 1, 1, 2, 2, 3, 3, 0];
static OUTLINE_RECT_LINE_INDICES: [u32; 8] = [0, 1, 2, 3, 4, 5, 6, 7];
#[derive(Deserialize)]
#[allow(dead_code)]
pub struct DebugFont {
name: String,
size: i32,
bold: bool,
italic: bool,
width: u32,
height: u32,
characters: HashMap<char, DebugCharacter>,
}
#[derive(Deserialize)]
struct DebugCharacter {
x: i32,
y: i32,
width: i32,
height: i32,
#[serde(rename = "originX")]
origin_x: i32,
#[serde(rename = "originY")]
origin_y: i32,
advance: i32,
}
impl DebugFont {
fn load(resources: &Resources) -> DebugFont {
let mut path = resources.resources_directory.clone();
path.push(FONT_JSON_FILENAME);
serde_json::from_reader(BufReader::new(File::open(path).unwrap())).unwrap()
}
}
pub struct UI<D> where D: Device {
framebuffer_size: Point2DI32,
texture_program: DebugTextureProgram<D>,
texture_vertex_array: DebugTextureVertexArray<D>,
solid_program: DebugSolidProgram<D>,
solid_vertex_array: DebugSolidVertexArray<D>,
font: DebugFont,
font_texture: D::Texture,
corner_fill_texture: D::Texture,
corner_outline_texture: D::Texture,
}
impl<D> UI<D> where D: Device {
pub fn new(device: &D, resources: &Resources, framebuffer_size: Point2DI32) -> UI<D> {
let texture_program = DebugTextureProgram::new(device, resources);
let texture_vertex_array = DebugTextureVertexArray::new(device, &texture_program);
let font = DebugFont::load(resources);
let solid_program = DebugSolidProgram::new(device, resources);
let solid_vertex_array = DebugSolidVertexArray::new(device, &solid_program);
let font_texture = device.create_texture_from_png(resources, FONT_PNG_NAME);
let corner_fill_texture = device.create_texture_from_png(resources, CORNER_FILL_PNG_NAME);
let corner_outline_texture = device.create_texture_from_png(resources,
CORNER_OUTLINE_PNG_NAME);
UI {
framebuffer_size,
texture_program,
texture_vertex_array,
font,
solid_program,
solid_vertex_array,
font_texture,
corner_fill_texture,
corner_outline_texture,
}
}
pub fn framebuffer_size(&self) -> Point2DI32 {
self.framebuffer_size
}
pub fn set_framebuffer_size(&mut self, window_size: Point2DI32) {
self.framebuffer_size = window_size;
}
pub fn draw_solid_rect(&self, device: &D, rect: RectI32, color: ColorU) {
self.draw_rect(device, rect, color, true);
}
pub fn draw_rect_outline(&self, device: &D, rect: RectI32, color: ColorU) {
self.draw_rect(device, rect, color, false);
}
fn draw_rect(&self, device: &D, rect: RectI32, color: ColorU, filled: bool) {
let vertex_data = [
DebugSolidVertex::new(rect.origin()),
DebugSolidVertex::new(rect.upper_right()),
DebugSolidVertex::new(rect.lower_right()),
DebugSolidVertex::new(rect.lower_left()),
];
if filled {
self.draw_solid_rects_with_vertex_data(device,
&vertex_data,
&QUAD_INDICES,
color,
true);
} else {
self.draw_solid_rects_with_vertex_data(device,
&vertex_data,
&RECT_LINE_INDICES,
color,
false);
}
}
fn draw_solid_rects_with_vertex_data(&self,
device: &D,
vertex_data: &[DebugSolidVertex],
index_data: &[u32],
color: ColorU,
filled: bool) {
device.bind_vertex_array(&self.solid_vertex_array.vertex_array);
device.upload_to_buffer(&self.solid_vertex_array.vertex_buffer,
vertex_data,
BufferTarget::Vertex,
BufferUploadMode::Dynamic);
device.upload_to_buffer(&self.solid_vertex_array.index_buffer,
index_data,
BufferTarget::Index,
BufferUploadMode::Dynamic);
device.use_program(&self.solid_program.program);
device.set_uniform(&self.solid_program.framebuffer_size_uniform,
UniformData::Vec2(self.framebuffer_size.0.to_f32x4()));
set_color_uniform(device, &self.solid_program.color_uniform, color);
let primitive = if filled { Primitive::Triangles } else { Primitive::Lines };
device.draw_elements(primitive, index_data.len() as u32, &RenderState {
blend: BlendState::RGBOneAlphaOneMinusSrcAlpha,
..RenderState::default()
});
}
pub fn draw_text(&self, device: &D, string: &str, origin: Point2DI32, invert: bool) {
let mut next = origin;
let char_count = string.chars().count();
let mut vertex_data = Vec::with_capacity(char_count * 4);
let mut index_data = Vec::with_capacity(char_count * 6);
for mut character in string.chars() {
if !self.font.characters.contains_key(&character) {
character = '?';
}
let info = &self.font.characters[&character];
let position_rect =
RectI32::new(Point2DI32::new(next.x() - info.origin_x, next.y() - info.origin_y),
Point2DI32::new(info.width as i32, info.height as i32));
let tex_coord_rect = RectI32::new(Point2DI32::new(info.x, info.y),
Point2DI32::new(info.width, info.height));
let first_vertex_index = vertex_data.len();
vertex_data.extend_from_slice(&[
DebugTextureVertex::new(position_rect.origin(), tex_coord_rect.origin()),
DebugTextureVertex::new(position_rect.upper_right(), tex_coord_rect.upper_right()),
DebugTextureVertex::new(position_rect.lower_right(), tex_coord_rect.lower_right()),
DebugTextureVertex::new(position_rect.lower_left(), tex_coord_rect.lower_left()),
]);
index_data.extend(QUAD_INDICES.iter().map(|&i| i + first_vertex_index as u32));
let next_x = next.x() + info.advance;
next.set_x(next_x);
}
let color = if invert { INVERTED_TEXT_COLOR } else { TEXT_COLOR };
self.draw_texture_with_vertex_data(device,
&vertex_data,
&index_data,
&self.font_texture,
color);
}
pub fn draw_texture(&self,
device: &D,
origin: Point2DI32,
texture: &D::Texture,
color: ColorU) {
let position_rect = RectI32::new(origin, device.texture_size(&texture));
let tex_coord_rect = RectI32::new(Point2DI32::default(), position_rect.size());
let vertex_data = [
DebugTextureVertex::new(position_rect.origin(), tex_coord_rect.origin()),
DebugTextureVertex::new(position_rect.upper_right(), tex_coord_rect.upper_right()),
DebugTextureVertex::new(position_rect.lower_right(), tex_coord_rect.lower_right()),
DebugTextureVertex::new(position_rect.lower_left(), tex_coord_rect.lower_left()),
];
self.draw_texture_with_vertex_data(device, &vertex_data, &QUAD_INDICES, texture, color);
}
pub fn measure_text(&self, string: &str) -> i32 {
let mut next = 0;
for mut character in string.chars() {
if !self.font.characters.contains_key(&character) {
character = '?';
}
let info = &self.font.characters[&character];
next += info.advance;
}
next
}
pub fn draw_solid_rounded_rect(&self, device: &D, rect: RectI32, color: ColorU) {
let corner_texture = self.corner_texture(true);
let corner_rects = CornerRects::new(device, rect, corner_texture);
self.draw_rounded_rect_corners(device, color, corner_texture, &corner_rects);
let solid_rect_mid = RectI32::from_points(corner_rects.upper_left.upper_right(),
corner_rects.lower_right.lower_left());
let solid_rect_left = RectI32::from_points(corner_rects.upper_left.lower_left(),
corner_rects.lower_left.upper_right());
let solid_rect_right = RectI32::from_points(corner_rects.upper_right.lower_left(),
corner_rects.lower_right.upper_right());
let vertex_data = vec![
DebugSolidVertex::new(solid_rect_mid.origin()),
DebugSolidVertex::new(solid_rect_mid.upper_right()),
DebugSolidVertex::new(solid_rect_mid.lower_right()),
DebugSolidVertex::new(solid_rect_mid.lower_left()),
DebugSolidVertex::new(solid_rect_left.origin()),
DebugSolidVertex::new(solid_rect_left.upper_right()),
DebugSolidVertex::new(solid_rect_left.lower_right()),
DebugSolidVertex::new(solid_rect_left.lower_left()),
DebugSolidVertex::new(solid_rect_right.origin()),
DebugSolidVertex::new(solid_rect_right.upper_right()),
DebugSolidVertex::new(solid_rect_right.lower_right()),
DebugSolidVertex::new(solid_rect_right.lower_left()),
];
let mut index_data = Vec::with_capacity(18);
index_data.extend(QUAD_INDICES.iter().map(|&index| index + 0));
index_data.extend(QUAD_INDICES.iter().map(|&index| index + 4));
index_data.extend(QUAD_INDICES.iter().map(|&index| index + 8));
self.draw_solid_rects_with_vertex_data(device,
&vertex_data,
&index_data[0..18],
color,
true);
}
pub fn draw_rounded_rect_outline(&self, device: &D, rect: RectI32, color: ColorU) {
let corner_texture = self.corner_texture(false);
let corner_rects = CornerRects::new(device, rect, corner_texture);
self.draw_rounded_rect_corners(device, color, corner_texture, &corner_rects);
let vertex_data = vec![
DebugSolidVertex::new(corner_rects.upper_left.upper_right()),
DebugSolidVertex::new(corner_rects.upper_right.origin()),
DebugSolidVertex::new(corner_rects.upper_right.lower_right()),
DebugSolidVertex::new(corner_rects.lower_right.upper_right()),
DebugSolidVertex::new(corner_rects.lower_left.lower_right()),
DebugSolidVertex::new(corner_rects.lower_right.lower_left()),
DebugSolidVertex::new(corner_rects.upper_left.lower_left()),
DebugSolidVertex::new(corner_rects.lower_left.origin()),
];
let index_data = &OUTLINE_RECT_LINE_INDICES;
self.draw_solid_rects_with_vertex_data(device, &vertex_data, index_data, color, false);
}
fn draw_rounded_rect_corners(&self,
device: &D,
color: ColorU,
texture: &D::Texture,
corner_rects: &CornerRects) {
let corner_size = device.texture_size(&texture);
let tex_coord_rect = RectI32::new(Point2DI32::default(), corner_size);
let vertex_data = vec![
DebugTextureVertex::new(
corner_rects.upper_left.origin(), tex_coord_rect.origin()),
DebugTextureVertex::new(
corner_rects.upper_left.upper_right(), tex_coord_rect.upper_right()),
DebugTextureVertex::new(
corner_rects.upper_left.lower_right(), tex_coord_rect.lower_right()),
DebugTextureVertex::new(
corner_rects.upper_left.lower_left(), tex_coord_rect.lower_left()),
DebugTextureVertex::new(
corner_rects.upper_right.origin(), tex_coord_rect.lower_left()),
DebugTextureVertex::new(
corner_rects.upper_right.upper_right(), tex_coord_rect.origin()),
DebugTextureVertex::new(
corner_rects.upper_right.lower_right(), tex_coord_rect.upper_right()),
DebugTextureVertex::new(
corner_rects.upper_right.lower_left(), tex_coord_rect.lower_right()),
DebugTextureVertex::new(
corner_rects.lower_left.origin(), tex_coord_rect.upper_right()),
DebugTextureVertex::new(
corner_rects.lower_left.upper_right(), tex_coord_rect.lower_right()),
DebugTextureVertex::new(
corner_rects.lower_left.lower_right(), tex_coord_rect.lower_left()),
DebugTextureVertex::new(
corner_rects.lower_left.lower_left(), tex_coord_rect.origin()),
DebugTextureVertex::new(
corner_rects.lower_right.origin(), tex_coord_rect.lower_right()),
DebugTextureVertex::new(
corner_rects.lower_right.upper_right(), tex_coord_rect.lower_left()),
DebugTextureVertex::new(
corner_rects.lower_right.lower_right(), tex_coord_rect.origin()),
DebugTextureVertex::new(
corner_rects.lower_right.lower_left(), tex_coord_rect.upper_right()),
];
let mut index_data = Vec::with_capacity(24);
index_data.extend(QUAD_INDICES.iter().map(|&index| index + 0));
index_data.extend(QUAD_INDICES.iter().map(|&index| index + 4));
index_data.extend(QUAD_INDICES.iter().map(|&index| index + 8));
index_data.extend(QUAD_INDICES.iter().map(|&index| index + 12));
self.draw_texture_with_vertex_data(device, &vertex_data, &index_data, texture, color);
}
fn corner_texture(&self, filled: bool) -> &D::Texture {
if filled { &self.corner_fill_texture } else { &self.corner_outline_texture }
}
fn draw_texture_with_vertex_data(&self,
device: &D,
vertex_data: &[DebugTextureVertex],
index_data: &[u32],
texture: &D::Texture,
color: ColorU) {
device.upload_to_buffer(&self.texture_vertex_array.vertex_buffer,
vertex_data,
BufferTarget::Vertex,
BufferUploadMode::Dynamic);
device.upload_to_buffer(&self.texture_vertex_array.index_buffer,
index_data,
BufferTarget::Index,
BufferUploadMode::Dynamic);
device.bind_vertex_array(&self.texture_vertex_array.vertex_array);
device.use_program(&self.texture_program.program);
device.set_uniform(&self.texture_program.framebuffer_size_uniform,
UniformData::Vec2(self.framebuffer_size.0.to_f32x4()));
device.set_uniform(&self.texture_program.texture_size_uniform,
UniformData::Vec2(device.texture_size(&texture).0.to_f32x4()));
set_color_uniform(device, &self.texture_program.color_uniform, color);
device.bind_texture(texture, 0);
device.set_uniform(&self.texture_program.texture_uniform, UniformData::TextureUnit(0));
device.draw_elements(Primitive::Triangles, index_data.len() as u32, &RenderState {
blend: BlendState::RGBOneAlphaOneMinusSrcAlpha,
..RenderState::default()
});
}
}
struct DebugTextureProgram<D> where D: Device {
program: D::Program,
framebuffer_size_uniform: D::Uniform,
texture_size_uniform: D::Uniform,
texture_uniform: D::Uniform,
color_uniform: D::Uniform,
}
impl<D> DebugTextureProgram<D> where D: Device {
fn new(device: &D, resources: &Resources) -> DebugTextureProgram<D> {
let program = device.create_program(resources, "debug_texture");
let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize");
let texture_size_uniform = device.get_uniform(&program, "TextureSize");
let texture_uniform = device.get_uniform(&program, "Texture");
let color_uniform = device.get_uniform(&program, "Color");
DebugTextureProgram {
program,
framebuffer_size_uniform,
texture_size_uniform,
texture_uniform,
color_uniform,
}
}
}
struct DebugTextureVertexArray<D> where D: Device {
vertex_array: D::VertexArray,
vertex_buffer: D::Buffer,
index_buffer: D::Buffer,
}
impl<D> DebugTextureVertexArray<D> where D: Device {
fn new(device: &D, debug_texture_program: &DebugTextureProgram<D>)
-> DebugTextureVertexArray<D> {
let (vertex_buffer, index_buffer) = (device.create_buffer(), device.create_buffer());
let vertex_array = device.create_vertex_array();
let position_attr = device.get_vertex_attr(&debug_texture_program.program, "Position");
let tex_coord_attr = device.get_vertex_attr(&debug_texture_program.program, "TexCoord");
device.bind_vertex_array(&vertex_array);
device.use_program(&debug_texture_program.program);
device.bind_buffer(&vertex_buffer, BufferTarget::Vertex);
device.bind_buffer(&index_buffer, BufferTarget::Index);
device.configure_float_vertex_attr(&position_attr,
2,
VertexAttrType::U16,
false,
DEBUG_TEXTURE_VERTEX_SIZE,
0,
0);
device.configure_float_vertex_attr(&tex_coord_attr,
2,
VertexAttrType::U16,
false,
DEBUG_TEXTURE_VERTEX_SIZE,
4,
0);
DebugTextureVertexArray { vertex_array, vertex_buffer, index_buffer }
}
}
struct DebugSolidVertexArray<D> where D: Device {
vertex_array: D::VertexArray,
vertex_buffer: D::Buffer,
index_buffer: D::Buffer,
}
impl<D> DebugSolidVertexArray<D> where D: Device {
fn new(device: &D, debug_solid_program: &DebugSolidProgram<D>) -> DebugSolidVertexArray<D> {
let (vertex_buffer, index_buffer) = (device.create_buffer(), device.create_buffer());
let vertex_array = device.create_vertex_array();
let position_attr = device.get_vertex_attr(&debug_solid_program.program, "Position");
device.bind_vertex_array(&vertex_array);
device.use_program(&debug_solid_program.program);
device.bind_buffer(&vertex_buffer, BufferTarget::Vertex);
device.bind_buffer(&index_buffer, BufferTarget::Index);
device.configure_float_vertex_attr(&position_attr,
2,
VertexAttrType::U16,
false,
DEBUG_SOLID_VERTEX_SIZE,
0,
0);
DebugSolidVertexArray { vertex_array, vertex_buffer, index_buffer }
}
}
struct DebugSolidProgram<D> where D: Device {
program: D::Program,
framebuffer_size_uniform: D::Uniform,
color_uniform: D::Uniform,
}
impl<D> DebugSolidProgram<D> where D: Device {
fn new(device: &D, resources: &Resources) -> DebugSolidProgram<D> {
let program = device.create_program(resources, "debug_solid");
let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize");
let color_uniform = device.get_uniform(&program, "Color");
DebugSolidProgram { program, framebuffer_size_uniform, color_uniform }
}
}
#[derive(Clone, Copy, Debug)]
#[allow(dead_code)]
#[repr(C)]
struct DebugTextureVertex {
position_x: i16,
position_y: i16,
tex_coord_x: u16,
tex_coord_y: u16,
}
impl DebugTextureVertex {
fn new(position: Point2DI32, tex_coord: Point2DI32) -> DebugTextureVertex {
DebugTextureVertex {
position_x: position.x() as i16,
position_y: position.y() as i16,
tex_coord_x: tex_coord.x() as u16,
tex_coord_y: tex_coord.y() as u16,
}
}
}
#[derive(Clone, Copy)]
#[allow(dead_code)]
#[repr(C)]
struct DebugSolidVertex {
position_x: i16,
position_y: i16,
}
impl DebugSolidVertex {
fn new(position: Point2DI32) -> DebugSolidVertex {
DebugSolidVertex { position_x: position.x() as i16, position_y: position.y() as i16 }
}
}
struct CornerRects {
upper_left: RectI32,
upper_right: RectI32,
lower_left: RectI32,
lower_right: RectI32,
}
impl CornerRects {
fn new<D>(device: &D, rect: RectI32, texture: &D::Texture) -> CornerRects where D: Device {
let size = device.texture_size(texture);
CornerRects {
upper_left: RectI32::new(rect.origin(), size),
upper_right: RectI32::new(rect.upper_right() - Point2DI32::new(size.x(), 0), size),
lower_left: RectI32::new(rect.lower_left() - Point2DI32::new(0, size.y()), size),
lower_right: RectI32::new(rect.lower_right() - size, size),
}
}
}
fn set_color_uniform<D>(device: &D, uniform: &D::Uniform, color: ColorU) where D: Device {
let color = F32x4::new(color.r as f32, color.g as f32, color.b as f32, color.a as f32);
device.set_uniform(uniform, UniformData::Vec4(color * F32x4::splat(1.0 / 255.0)));
}