Improve C API

I ran `cargo fmt` on the project, which in hindsight was a poor choice.
I'm not correcting it right now.
This commit is contained in:
Michael Pfaff 2024-08-09 03:13:29 -04:00
parent ca750dda7b
commit 5e41738e4c
108 changed files with 10547 additions and 6871 deletions

View File

@ -68,8 +68,10 @@ default-members = [
]
[patch.crates-io]
font-kit = { git = "https://git.pfaff.dev/michael/font-kit" }
pathfinder_geometry = { path = "geometry" }
pathfinder_simd = { path = "simd" }
skribo = { git = "https://git.pfaff.dev/michael/skribo" }
[patch."https://github.com/servo/pathfinder"]
pathfinder_content = { path = "content" }

View File

@ -6,18 +6,18 @@ edition = "2018"
[features]
metal = ["dep:core-foundation", "dep:io-surface", "dep:metal", "dep:pathfinder_metal"]
svg = ["dep:pathfinder_svg", "dep:usvg"]
[lib]
crate-type = ["staticlib", "cdylib"]
name = "pathfinder"
[dependencies]
font-kit = "0.6"
font-kit = "0.13"
foreign-types = "0.3"
gl = "0.14"
libc = "0.2"
simple_logger = "4.3"
usvg = "0.9"
[dependencies.log]
version = "0.4"
@ -53,6 +53,11 @@ path = "../simd"
[dependencies.pathfinder_svg]
path = "../svg"
optional = true
[dependencies.usvg]
version = "0.10"
optional = true
[target.'cfg(target_os = "macos")'.dependencies.core-foundation]
version = "0.6"

File diff suppressed because it is too large Load Diff

View File

@ -13,7 +13,7 @@ keywords = ["pathfinder", "canvas", "vector", "graphics", "gpu"]
crate-type = ["rlib", "staticlib"]
[dependencies]
font-kit = { version = "0.6", optional = true }
font-kit = { version = "0.13", optional = true }
[dependencies.pathfinder_color]
path = "../color"

View File

@ -10,14 +10,14 @@
//! A simple API for Pathfinder that mirrors a subset of HTML canvas.
pub use pathfinder_color::{ColorF, ColorU, rgbaf, rgbau, rgbf, rgbu};
pub use pathfinder_color::{color_slice_to_u8_slice, u8_slice_to_color_slice, u8_vec_to_color_vec};
pub use pathfinder_color::{rgbaf, rgbau, rgbf, rgbu, ColorF, ColorU};
pub use pathfinder_content::fill::FillRule;
pub use pathfinder_content::stroke::LineCap;
pub use pathfinder_content::outline::ArcDirection;
pub use pathfinder_content::stroke::LineCap;
pub use pathfinder_geometry::rect::{RectF, RectI};
pub use pathfinder_geometry::transform2d::Transform2F;
pub use pathfinder_geometry::vector::{IntoVector2F, Vector2F, Vector2I, vec2f, vec2i};
pub use pathfinder_geometry::vector::{vec2f, vec2i, IntoVector2F, Vector2F, Vector2I};
use pathfinder_content::dash::OutlineDash;
use pathfinder_content::effects::{BlendMode, BlurDirection, PatternFilter};
@ -25,25 +25,25 @@ use pathfinder_content::gradient::Gradient;
use pathfinder_content::outline::{Contour, Outline};
use pathfinder_content::pattern::{Image, Pattern};
use pathfinder_content::render_target::RenderTargetId;
use pathfinder_content::stroke::{LineJoin as StrokeLineJoin};
use pathfinder_content::stroke::LineJoin as StrokeLineJoin;
use pathfinder_content::stroke::{OutlineStrokeToFill, StrokeStyle};
use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_renderer::paint::{Paint, PaintCompositeOp};
use pathfinder_renderer::scene::{ClipPath, ClipPathId, DrawPath, RenderTarget, Scene};
use std::borrow::Cow;
use std::default::Default;
use std::f32::consts::PI;
use std::f32;
use std::f32::consts::PI;
use std::fmt::{Debug, Error as FmtError, Formatter};
use std::mem;
use std::sync::Arc;
pub use text::CanvasFontContext;
#[cfg(feature = "pf-text")]
use skribo::FontCollection;
#[cfg(not(feature = "pf-text"))]
use crate::text::FontCollection;
#[cfg(feature = "pf-text")]
use skribo::FontCollection;
#[cfg(feature = "pf-text")]
pub use text::TextMetrics;
@ -108,11 +108,16 @@ impl Canvas {
self.scene
}
pub fn get_context_2d(self, canvas_font_context: CanvasFontContext)
-> CanvasRenderingContext2D {
pub fn get_context_2d(
self,
canvas_font_context: CanvasFontContext,
) -> CanvasRenderingContext2D {
#[cfg(feature = "pf-text")]
let default_font_collection =
canvas_font_context.0.borrow().default_font_collection.clone();
let default_font_collection = canvas_font_context
.0
.borrow()
.default_font_collection
.clone();
#[cfg(not(feature = "pf-text"))]
let default_font_collection = Arc::new(FontCollection);
CanvasRenderingContext2D {
@ -131,7 +136,8 @@ impl Canvas {
/// This resets the canvas' bounds. Do not call this mid-draw.
pub fn set_size(&mut self, new_size: Vector2F) {
self.scene.set_bounds(RectF::default());
self.scene.set_view_box(RectF::new(Vector2F::zero(), new_size));
self.scene
.set_view_box(RectF::new(Vector2F::zero(), new_size));
}
}
@ -272,12 +278,18 @@ impl CanvasRenderingContext2D {
// Fill and stroke styles
#[inline]
pub fn set_fill_style<FS>(&mut self, new_fill_style: FS) where FS: Into<FillStyle> {
pub fn set_fill_style<FS>(&mut self, new_fill_style: FS)
where
FS: Into<FillStyle>,
{
self.current_state.fill_paint = new_fill_style.into().into_paint();
}
#[inline]
pub fn set_stroke_style<FS>(&mut self, new_stroke_style: FS) where FS: Into<FillStyle> {
pub fn set_stroke_style<FS>(&mut self, new_stroke_style: FS)
where
FS: Into<FillStyle>,
{
self.current_state.stroke_paint = new_stroke_style.into().into_paint();
}
@ -336,9 +348,11 @@ impl CanvasRenderingContext2D {
let mut outline = path.into_outline();
if !self.current_state.line_dash.is_empty() {
let mut dash = OutlineDash::new(&outline,
&self.current_state.line_dash,
self.current_state.line_dash_offset);
let mut dash = OutlineDash::new(
&outline,
&self.current_state.line_dash,
self.current_state.line_dash_offset,
);
dash.dash();
outline = dash.into_outline();
}
@ -373,23 +387,29 @@ impl CanvasRenderingContext2D {
let transform = self.current_state.transform;
let clip_path = self.current_state.clip_path;
let blend_mode = self.current_state.global_composite_operation.to_blend_mode();
let blend_mode = self
.current_state
.global_composite_operation
.to_blend_mode();
outline.transform(&transform);
if !self.current_state.shadow_color.is_fully_transparent() {
let mut outline = outline.clone();
outline.transform(&Transform2F::from_translation(self.current_state.shadow_offset));
outline.transform(&Transform2F::from_translation(
self.current_state.shadow_offset,
));
let shadow_blur_info =
push_shadow_blur_render_targets_if_needed(&mut self.canvas.scene,
&self.current_state,
outline.bounds());
let shadow_blur_info = push_shadow_blur_render_targets_if_needed(
&mut self.canvas.scene,
&self.current_state,
outline.bounds(),
);
if let Some(ref shadow_blur_info) = shadow_blur_info {
outline.transform(&Transform2F::from_translation(-shadow_blur_info.bounds
.origin()
.to_f32()));
outline.transform(&Transform2F::from_translation(
-shadow_blur_info.bounds.origin().to_f32(),
));
}
// Per spec the shadow must respect the alpha of the shadowed path, but otherwise have
@ -412,9 +432,11 @@ impl CanvasRenderingContext2D {
path.set_blend_mode(blend_mode);
self.canvas.scene.push_draw_path(path);
composite_shadow_blur_render_targets_if_needed(&mut self.canvas.scene,
shadow_blur_info,
clip_path);
composite_shadow_blur_render_targets_if_needed(
&mut self.canvas.scene,
shadow_blur_info,
clip_path,
);
}
let mut path = DrawPath::new(outline, paint_id);
@ -423,10 +445,11 @@ impl CanvasRenderingContext2D {
path.set_blend_mode(blend_mode);
self.canvas.scene.push_draw_path(path);
fn push_shadow_blur_render_targets_if_needed(scene: &mut Scene,
current_state: &State,
outline_bounds: RectF)
-> Option<ShadowBlurRenderTargetInfo> {
fn push_shadow_blur_render_targets_if_needed(
scene: &mut Scene,
current_state: &State,
outline_bounds: RectF,
) -> Option<ShadowBlurRenderTargetInfo> {
if current_state.shadow_blur == 0.0 {
return None;
}
@ -447,9 +470,11 @@ impl CanvasRenderingContext2D {
})
}
fn composite_shadow_blur_render_targets_if_needed(scene: &mut Scene,
info: Option<ShadowBlurRenderTargetInfo>,
clip_path: Option<ClipPathId>) {
fn composite_shadow_blur_render_targets_if_needed(
scene: &mut Scene,
info: Option<ShadowBlurRenderTargetInfo>,
clip_path: Option<ClipPathId>,
) {
let info = match info {
None => return,
Some(info) => info,
@ -460,15 +485,21 @@ impl CanvasRenderingContext2D {
paint_y.apply_transform(Transform2F::from_translation(info.bounds.origin().to_f32()));
let sigma = info.sigma;
paint_x.set_filter(Some(PatternFilter::Blur { direction: BlurDirection::X, sigma }));
paint_y.set_filter(Some(PatternFilter::Blur { direction: BlurDirection::Y, sigma }));
paint_x.set_filter(Some(PatternFilter::Blur {
direction: BlurDirection::X,
sigma,
}));
paint_y.set_filter(Some(PatternFilter::Blur {
direction: BlurDirection::Y,
sigma,
}));
let paint_id_x = scene.push_paint(&Paint::from_pattern(paint_x));
let paint_id_y = scene.push_paint(&Paint::from_pattern(paint_y));
// TODO(pcwalton): Apply clip as necessary.
let outline_x = Outline::from_rect(RectF::new(vec2f(0.0, 0.0),
info.bounds.size().to_f32()));
let outline_x =
Outline::from_rect(RectF::new(vec2f(0.0, 0.0), info.bounds.size().to_f32()));
let path_x = DrawPath::new(outline_x, paint_id_x);
let outline_y = Outline::from_rect(info.bounds.to_f32());
let mut path_y = DrawPath::new(outline_y, paint_id_y);
@ -479,7 +510,6 @@ impl CanvasRenderingContext2D {
scene.pop_render_target();
scene.push_draw_path(path_y);
}
}
// Transformations
@ -490,7 +520,10 @@ impl CanvasRenderingContext2D {
}
#[inline]
pub fn scale<S>(&mut self, scale: S) where S: IntoVector2F {
pub fn scale<S>(&mut self, scale: S)
where
S: IntoVector2F,
{
self.current_state.transform *= Transform2F::from_scale(scale)
}
@ -540,14 +573,20 @@ impl CanvasRenderingContext2D {
#[inline]
pub fn draw_image<I, L>(&mut self, image: I, dest_location: L)
where I: CanvasImageSource, L: CanvasImageDestLocation {
where
I: CanvasImageSource,
L: CanvasImageDestLocation,
{
let pattern = image.to_pattern(self, Transform2F::default());
let src_rect = RectF::new(vec2f(0.0, 0.0), pattern.size().to_f32());
self.draw_subimage(pattern, src_rect, dest_location)
}
pub fn draw_subimage<I, L>(&mut self, image: I, src_location: RectF, dest_location: L)
where I: CanvasImageSource, L: CanvasImageDestLocation {
where
I: CanvasImageSource,
L: CanvasImageDestLocation,
{
let dest_size = dest_location.size().unwrap_or(src_location.size());
let scale = dest_size / src_location.size();
let offset = dest_location.origin() - src_location.origin() * scale;
@ -563,7 +602,9 @@ impl CanvasRenderingContext2D {
// Pixel manipulation
pub fn put_image_data<L>(&mut self, image_data: ImageData, dest_location: L)
where L: CanvasImageDestLocation {
where
L: CanvasImageDestLocation,
{
let origin = dest_location.origin();
let size = dest_location.size().unwrap_or(image_data.size.to_f32());
let pattern = Pattern::from_image(image_data.into_image());
@ -610,8 +651,11 @@ impl CanvasRenderingContext2D {
// Extensions
pub fn create_pattern_from_canvas(&mut self, canvas: Canvas, transform: Transform2F)
-> Pattern {
pub fn create_pattern_from_canvas(
&mut self,
canvas: Canvas,
transform: Transform2F,
) -> Pattern {
let subscene_size = canvas.size();
let subscene = canvas.into_scene();
let render_target = RenderTarget::new(subscene_size, String::new());
@ -724,7 +768,10 @@ pub struct Path2D {
impl Path2D {
#[inline]
pub fn new() -> Path2D {
Path2D { outline: Outline::new(), current_contour: Contour::new() }
Path2D {
outline: Outline::new(),
current_contour: Contour::new(),
}
}
#[inline]
@ -754,14 +801,17 @@ impl Path2D {
}
#[inline]
pub fn arc(&mut self,
center: Vector2F,
radius: f32,
start_angle: f32,
end_angle: f32,
direction: ArcDirection) {
pub fn arc(
&mut self,
center: Vector2F,
radius: f32,
start_angle: f32,
end_angle: f32,
direction: ArcDirection,
) {
let transform = Transform2F::from_scale(radius).translate(center);
self.current_contour.push_arc(&transform, start_angle, end_angle, direction);
self.current_contour
.push_arc(&transform, start_angle, end_angle, direction);
}
#[inline]
@ -775,10 +825,11 @@ impl Path2D {
let center = ctrl + bisector * (hypot / bisector.length());
let transform = Transform2F::from_scale(radius).translate(center);
let chord = LineSegment2F::new(vu0.yx() * vec2f(-1.0, 1.0), vu1.yx() * vec2f( 1.0, -1.0));
let chord = LineSegment2F::new(vu0.yx() * vec2f(-1.0, 1.0), vu1.yx() * vec2f(1.0, -1.0));
// FIXME(pcwalton): Is clockwise direction correct?
self.current_contour.push_arc_from_unit_chord(&transform, chord, ArcDirection::CW);
self.current_contour
.push_arc_from_unit_chord(&transform, chord, ArcDirection::CW);
}
pub fn rect(&mut self, rect: RectF) {
@ -790,17 +841,23 @@ impl Path2D {
self.current_contour.close();
}
pub fn ellipse<A>(&mut self,
center: Vector2F,
axes: A,
rotation: f32,
start_angle: f32,
end_angle: f32)
where A: IntoVector2F {
pub fn ellipse<A>(
&mut self,
center: Vector2F,
axes: A,
rotation: f32,
start_angle: f32,
end_angle: f32,
) where
A: IntoVector2F,
{
self.flush_current_contour();
let transform = Transform2F::from_scale(axes).rotate(rotation).translate(center);
self.current_contour.push_arc(&transform, start_angle, end_angle, ArcDirection::CW);
let transform = Transform2F::from_scale(axes)
.rotate(rotation)
.translate(center);
self.current_contour
.push_arc(&transform, start_angle, end_angle, ArcDirection::CW);
if end_angle - start_angle >= 2.0 * PI {
self.current_contour.close();
@ -826,7 +883,8 @@ impl Path2D {
fn flush_current_contour(&mut self) {
if !self.current_contour.is_empty() {
self.outline.push_contour(mem::replace(&mut self.current_contour, Contour::new()));
self.outline
.push_contour(mem::replace(&mut self.current_contour, Contour::new()));
}
}
}
@ -948,8 +1006,11 @@ pub enum ImageSmoothingQuality {
}
pub trait CanvasImageSource {
fn to_pattern(self, dest_context: &mut CanvasRenderingContext2D, transform: Transform2F)
-> Pattern;
fn to_pattern(
self,
dest_context: &mut CanvasRenderingContext2D,
transform: Transform2F,
) -> Pattern;
}
pub trait CanvasImageDestLocation {
@ -967,8 +1028,11 @@ impl CanvasImageSource for Pattern {
impl CanvasImageSource for Canvas {
#[inline]
fn to_pattern(self, dest_context: &mut CanvasRenderingContext2D, transform: Transform2F)
-> Pattern {
fn to_pattern(
self,
dest_context: &mut CanvasRenderingContext2D,
transform: Transform2F,
) -> Pattern {
dest_context.create_pattern_from_canvas(self, transform)
}
}
@ -1003,7 +1067,10 @@ pub struct ImageData {
impl ImageData {
#[inline]
pub fn new(size: Vector2I) -> ImageData {
ImageData { data: vec![ColorU::transparent_black(); size.area() as usize], size }
ImageData {
data: vec![ColorU::transparent_black(); size.area() as usize],
size,
}
}
#[inline]

View File

@ -3,8 +3,8 @@
// For this file only, any copyright is dedicated to the Public Domain.
// https://creativecommons.org/publicdomain/zero/1.0/
use pathfinder_geometry::vector::{Vector2F, vec2f};
use super::Path2D;
use pathfinder_geometry::vector::{vec2f, Vector2F};
#[test]
pub fn test_path2d_formatting() {

View File

@ -10,7 +10,7 @@
use crate::{CanvasRenderingContext2D, State, TextAlign, TextBaseline};
use font_kit::canvas::RasterizationOptions;
use font_kit::error::{GlyphLoadingError, FontLoadingError, SelectionError};
use font_kit::error::{FontLoadingError, GlyphLoadingError, SelectionError};
use font_kit::family_name::FamilyName;
use font_kit::handle::Handle;
use font_kit::hinting::HintingOptions;
@ -20,7 +20,7 @@ use font_kit::source::{Source, SystemSource};
use font_kit::sources::mem::MemSource;
use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::util;
use pathfinder_geometry::vector::{Vector2F, vec2f};
use pathfinder_geometry::vector::{vec2f, Vector2F};
use pathfinder_renderer::paint::PaintId;
use pathfinder_text::{FontContext, FontRenderOptions, TextRenderMode};
use skribo::{FontCollection, FontFamily, FontRef, Layout as SkriboLayout, TextStyle};
@ -36,8 +36,13 @@ impl CanvasRenderingContext2D {
/// fill the text that you passed into `measure_text()` with the layout-related style
/// properties set at the time you called that function. This allows Pathfinder to skip having
/// to lay out the text again.
pub fn fill_text<T>(&mut self, text: &T, position: Vector2F) -> Result<(), GlyphLoadingError> where T: ToTextLayout + ?Sized {
let paint = self.current_state.resolve_paint(&self.current_state.fill_paint);
pub fn fill_text<T>(&mut self, text: &T, position: Vector2F) -> Result<(), GlyphLoadingError>
where
T: ToTextLayout + ?Sized,
{
let paint = self
.current_state
.resolve_paint(&self.current_state.fill_paint);
let paint_id = self.canvas.scene.push_paint(&paint);
self.fill_or_stroke_text(text, position, paint_id, TextRenderMode::Fill)
}
@ -48,8 +53,13 @@ impl CanvasRenderingContext2D {
/// stroke the text that you passed into `measure_text()` with the layout-related style
/// properties set at the time you called that function. This allows Pathfinder to skip having
/// to lay out the text again.
pub fn stroke_text<T>(&mut self, text: &T, position: Vector2F) -> Result<(), GlyphLoadingError> where T: ToTextLayout + ?Sized {
let paint = self.current_state.resolve_paint(&self.current_state.stroke_paint);
pub fn stroke_text<T>(&mut self, text: &T, position: Vector2F) -> Result<(), GlyphLoadingError>
where
T: ToTextLayout + ?Sized,
{
let paint = self
.current_state
.resolve_paint(&self.current_state.stroke_paint);
let paint_id = self.canvas.scene.push_paint(&paint);
let render_mode = TextRenderMode::Stroke(self.current_state.resolve_stroke_style());
self.fill_or_stroke_text(text, position, paint_id, render_mode)
@ -60,39 +70,53 @@ impl CanvasRenderingContext2D {
/// As an extension, the returned `TextMetrics` object contains all the layout data for the
/// string and can be used in its place when calling `fill_text()` and `stroke_text()` to avoid
/// needlessly performing layout multiple times.
pub fn measure_text<T>(&self, text: &T) -> TextMetrics where T: ToTextLayout + ?Sized {
pub fn measure_text<T>(&self, text: &T) -> TextMetrics
where
T: ToTextLayout + ?Sized,
{
text.layout(CanvasState(&self.current_state)).into_owned()
}
fn fill_or_stroke_text<T>(&mut self,
text: &T,
mut position: Vector2F,
paint_id: PaintId,
render_mode: TextRenderMode) -> Result<(), GlyphLoadingError>
where T: ToTextLayout + ?Sized {
fn fill_or_stroke_text<T>(
&mut self,
text: &T,
mut position: Vector2F,
paint_id: PaintId,
render_mode: TextRenderMode,
) -> Result<(), GlyphLoadingError>
where
T: ToTextLayout + ?Sized,
{
let layout = text.layout(CanvasState(&self.current_state));
let clip_path = self.current_state.clip_path;
let blend_mode = self.current_state.global_composite_operation.to_blend_mode();
let blend_mode = self
.current_state
.global_composite_operation
.to_blend_mode();
position += layout.text_origin();
let transform = self.current_state.transform * Transform2F::from_translation(position);
self.canvas_font_context
.0
.borrow_mut()
.font_context
.push_layout(&mut self.canvas.scene,
&layout.skribo_layout,
&TextStyle { size: layout.font_size },
&FontRenderOptions {
transform,
render_mode,
hinting_options: HintingOptions::None,
clip_path,
blend_mode,
paint_id,
})?;
.0
.borrow_mut()
.font_context
.push_layout(
&mut self.canvas.scene,
&layout.skribo_layout,
&TextStyle {
size: layout.font_size,
},
&FontRenderOptions {
transform,
render_mode,
hinting_options: HintingOptions::None,
clip_path,
blend_mode,
paint_id,
},
)?;
Ok(())
}
@ -105,9 +129,12 @@ impl CanvasRenderingContext2D {
}
#[inline]
pub fn set_font<FC>(&mut self, font_collection: FC) -> Result<(), FontError> where FC: IntoFontCollection {
pub fn set_font<FC>(&mut self, font_collection: FC) -> Result<(), FontError>
where
FC: IntoFontCollection,
{
let font_collection = font_collection.into_font_collection(&self.canvas_font_context)?;
self.current_state.font_collection = font_collection;
self.current_state.font_collection = font_collection;
Ok(())
}
@ -155,7 +182,13 @@ pub trait ToTextLayout {
impl ToTextLayout for str {
fn layout(&self, state: CanvasState) -> Cow<TextMetrics> {
Cow::Owned(TextMetrics::layout(self, &state.0.font_collection, state.0.font_size, state.0.text_align, state.0.text_baseline))
Cow::Owned(TextMetrics::layout(
self,
&state.0.font_collection,
state.0.font_size,
state.0.text_align,
state.0.text_baseline,
))
}
}
@ -168,10 +201,12 @@ impl ToTextLayout for String {
impl ToTextLayout for Rc<SkriboLayout> {
fn layout(&self, state: CanvasState) -> Cow<TextMetrics> {
Cow::Owned(TextMetrics::new((*self).clone(),
state.0.font_size,
state.0.text_align,
state.0.text_baseline))
Cow::Owned(TextMetrics::new(
(*self).clone(),
state.0.font_size,
state.0.text_align,
state.0.text_baseline,
))
}
}
@ -203,8 +238,9 @@ pub(super) struct CanvasFontContextData {
impl CanvasFontContext {
pub fn new(font_source: Arc<dyn Source>) -> CanvasFontContext {
let mut default_font_collection = FontCollection::new();
if let Ok(default_font) = font_source.select_best_match(&[FamilyName::SansSerif],
&Properties::new()) {
if let Ok(default_font) =
font_source.select_best_match(&[FamilyName::SansSerif], &Properties::new())
{
if let Ok(default_font) = default_font.load() {
default_font_collection.add_family(FontFamily::new_from_font(default_font));
}
@ -224,7 +260,10 @@ impl CanvasFontContext {
}
/// A convenience method to create a font context with a set of in-memory fonts.
pub fn from_fonts<I>(fonts: I) -> CanvasFontContext where I: Iterator<Item = Handle> {
pub fn from_fonts<I>(fonts: I) -> CanvasFontContext
where
I: Iterator<Item = Handle>,
{
CanvasFontContext::new(Arc::new(MemSource::from_fonts(fonts).unwrap()))
}
@ -236,7 +275,8 @@ impl CanvasFontContext {
this.font_source
.select_by_postscript_name(postscript_name)
.map_err(FontError::NotFound)?
.load().map_err(FontError::LoadError)
.load()
.map_err(FontError::LoadError)
}
}
@ -298,11 +338,12 @@ struct VerticalMetrics {
}
impl TextMetrics {
pub fn new(skribo_layout: Rc<SkriboLayout>,
font_size: f32,
align: TextAlign,
baseline: TextBaseline)
-> TextMetrics {
pub fn new(
skribo_layout: Rc<SkriboLayout>,
font_size: f32,
align: TextAlign,
baseline: TextBaseline,
) -> TextMetrics {
TextMetrics {
skribo_layout,
font_size,
@ -317,10 +358,18 @@ impl TextMetrics {
}
}
pub fn layout(string: &str, font_collection: &FontCollection, font_size: f32, text_align: TextAlign, text_baseline: TextBaseline) -> TextMetrics {
let skribo_layout = Rc::new(skribo::layout(&TextStyle { size: font_size },
font_collection,
string));
pub fn layout(
string: &str,
font_collection: &FontCollection,
font_size: f32,
text_align: TextAlign,
text_baseline: TextBaseline,
) -> TextMetrics {
let skribo_layout = Rc::new(skribo::layout(
&TextStyle { size: font_size },
font_collection,
string,
));
TextMetrics::new(skribo_layout, font_size, text_align, text_baseline)
}
@ -342,11 +391,11 @@ impl TextMetrics {
self.text_y_offset.set(Some(match self.baseline {
TextBaseline::Alphabetic => 0.0,
TextBaseline::Top => vertical_metrics.em_height_ascent,
TextBaseline::Middle => {
util::lerp(vertical_metrics.em_height_ascent,
vertical_metrics.em_height_descent,
0.5)
}
TextBaseline::Middle => util::lerp(
vertical_metrics.em_height_ascent,
vertical_metrics.em_height_descent,
0.5,
),
TextBaseline::Bottom => vertical_metrics.em_height_descent,
TextBaseline::Ideographic => vertical_metrics.ideographic_baseline,
TextBaseline::Hanging => vertical_metrics.hanging_baseline,
@ -368,39 +417,56 @@ impl TextMetrics {
let font_metrics = last_glyph.font.font.metrics();
let scale_factor = self.skribo_layout.size / font_metrics.units_per_em as f32;
let glyph_rect = last_glyph.font.font.typographic_bounds(glyph_id).unwrap();
self.width.set(Some(last_glyph.offset.x() +
glyph_rect.max_x() * scale_factor));
self.width.set(Some(
last_glyph.offset.x() + glyph_rect.max_x() * scale_factor,
));
}
}
}
self.width.get().unwrap()
}
fn populate_vertical_metrics_if_necessary(&self) {
if self.vertical_metrics.get().is_none() {
self.vertical_metrics.set(Some(VerticalMetrics::measure(&self.skribo_layout)));
self.vertical_metrics
.set(Some(VerticalMetrics::measure(&self.skribo_layout)));
}
}
pub fn font_bounding_box_ascent(&self) -> f32 {
self.populate_vertical_metrics_if_necessary();
self.vertical_metrics.get().unwrap().font_bounding_box_ascent - self.text_y_offset()
self.vertical_metrics
.get()
.unwrap()
.font_bounding_box_ascent
- self.text_y_offset()
}
pub fn font_bounding_box_descent(&self) -> f32 {
self.populate_vertical_metrics_if_necessary();
self.vertical_metrics.get().unwrap().font_bounding_box_descent - self.text_y_offset()
self.vertical_metrics
.get()
.unwrap()
.font_bounding_box_descent
- self.text_y_offset()
}
pub fn actual_bounding_box_ascent(&self) -> f32 {
self.populate_vertical_metrics_if_necessary();
self.vertical_metrics.get().unwrap().actual_bounding_box_ascent - self.text_y_offset()
self.vertical_metrics
.get()
.unwrap()
.actual_bounding_box_ascent
- self.text_y_offset()
}
pub fn actual_bounding_box_descent(&self) -> f32 {
self.populate_vertical_metrics_if_necessary();
self.vertical_metrics.get().unwrap().actual_bounding_box_descent - self.text_y_offset()
self.vertical_metrics
.get()
.unwrap()
.actual_bounding_box_descent
- self.text_y_offset()
}
pub fn em_height_ascent(&self) -> f32 {
@ -421,14 +487,20 @@ impl TextMetrics {
let glyph_id = first_glyph.glyph_id;
let font_metrics = first_glyph.font.font.metrics();
let scale_factor = self.skribo_layout.size / font_metrics.units_per_em as f32;
let glyph_rect = first_glyph.font.font.raster_bounds(
glyph_id,
font_metrics.units_per_em as f32,
Transform2F::default(),
HintingOptions::None,
RasterizationOptions::GrayscaleAa).unwrap();
self.actual_left_extent.set(Some(first_glyph.offset.x() +
glyph_rect.min_x() as f32 * scale_factor));
let glyph_rect = first_glyph
.font
.font
.raster_bounds(
glyph_id,
font_metrics.units_per_em as f32,
Transform2F::default(),
HintingOptions::None,
RasterizationOptions::GrayscaleAa,
)
.unwrap();
self.actual_left_extent.set(Some(
first_glyph.offset.x() + glyph_rect.min_x() as f32 * scale_factor,
));
}
}
}
@ -443,14 +515,20 @@ impl TextMetrics {
let glyph_id = last_glyph.glyph_id;
let font_metrics = last_glyph.font.font.metrics();
let scale_factor = self.skribo_layout.size / font_metrics.units_per_em as f32;
let glyph_rect = last_glyph.font.font.raster_bounds(
glyph_id,
font_metrics.units_per_em as f32,
Transform2F::default(),
HintingOptions::None,
RasterizationOptions::GrayscaleAa).unwrap();
self.actual_right_extent.set(Some(last_glyph.offset.x() +
glyph_rect.max_x() as f32 * scale_factor));
let glyph_rect = last_glyph
.font
.font
.raster_bounds(
glyph_id,
font_metrics.units_per_em as f32,
Transform2F::default(),
HintingOptions::None,
RasterizationOptions::GrayscaleAa,
)
.unwrap();
self.actual_right_extent.set(Some(
last_glyph.offset.x() + glyph_rect.max_x() as f32 * scale_factor,
));
}
}
}
@ -471,7 +549,6 @@ impl TextMetrics {
self.populate_vertical_metrics_if_necessary();
self.vertical_metrics.get().unwrap().ideographic_baseline - self.text_y_offset()
}
}
impl VerticalMetrics {
@ -498,28 +575,30 @@ impl VerticalMetrics {
let font_metrics = font.metrics();
let scale_factor = skribo_layout.size / font_metrics.units_per_em as f32;
vertical_metrics.em_height_ascent =
(font_metrics.ascent *
scale_factor).max(vertical_metrics.em_height_ascent);
vertical_metrics.em_height_descent =
(font_metrics.descent *
scale_factor).min(vertical_metrics.em_height_descent);
vertical_metrics.font_bounding_box_ascent =
(font_metrics.bounding_box.max_y() *
scale_factor).max(vertical_metrics.font_bounding_box_ascent);
(font_metrics.ascent * scale_factor).max(vertical_metrics.em_height_ascent);
vertical_metrics.em_height_descent = (font_metrics.descent * scale_factor)
.min(vertical_metrics.em_height_descent);
vertical_metrics.font_bounding_box_ascent = (font_metrics.bounding_box.max_y()
* scale_factor)
.max(vertical_metrics.font_bounding_box_ascent);
vertical_metrics.font_bounding_box_descent =
(font_metrics.bounding_box.min_y() *
scale_factor).min(vertical_metrics.font_bounding_box_descent);
(font_metrics.bounding_box.min_y() * scale_factor)
.min(vertical_metrics.font_bounding_box_descent);
last_font = Some(font);
}
}
let font = last_font.as_ref().unwrap();
let glyph_rect = font.raster_bounds(glyph.glyph_id,
skribo_layout.size,
Transform2F::default(),
HintingOptions::None,
RasterizationOptions::GrayscaleAa).unwrap();
let glyph_rect = font
.raster_bounds(
glyph.glyph_id,
skribo_layout.size,
Transform2F::default(),
HintingOptions::None,
RasterizationOptions::GrayscaleAa,
)
.unwrap();
vertical_metrics.actual_bounding_box_ascent =
(glyph_rect.max_y() as f32).max(vertical_metrics.actual_bounding_box_ascent);
vertical_metrics.actual_bounding_box_descent =
@ -533,7 +612,10 @@ impl VerticalMetrics {
/// Various things that can be conveniently converted into font collections for use with
/// `CanvasRenderingContext2D::set_font()`.
pub trait IntoFontCollection {
fn into_font_collection(self, font_context: &CanvasFontContext) -> Result<Arc<FontCollection>, FontError>;
fn into_font_collection(
self,
font_context: &CanvasFontContext,
) -> Result<Arc<FontCollection>, FontError>;
}
impl IntoFontCollection for Arc<FontCollection> {
@ -565,14 +647,20 @@ impl IntoFontCollection for Vec<FontFamily> {
impl IntoFontCollection for Font {
#[inline]
fn into_font_collection(self, context: &CanvasFontContext) -> Result<Arc<FontCollection>, FontError> {
fn into_font_collection(
self,
context: &CanvasFontContext,
) -> Result<Arc<FontCollection>, FontError> {
Ok(FontFamily::new_from_font(self).into_font_collection(context)?)
}
}
impl<'a> IntoFontCollection for &'a [Font] {
#[inline]
fn into_font_collection(self, context: &CanvasFontContext) -> Result<Arc<FontCollection>, FontError> {
fn into_font_collection(
self,
context: &CanvasFontContext,
) -> Result<Arc<FontCollection>, FontError> {
let mut family = FontFamily::new();
for font in self {
family.add_font(FontRef::new((*font).clone()))
@ -583,14 +671,22 @@ impl<'a> IntoFontCollection for &'a [Font] {
impl<'a> IntoFontCollection for &'a str {
#[inline]
fn into_font_collection(self, context: &CanvasFontContext) -> Result<Arc<FontCollection>, FontError> {
context.get_font_by_postscript_name(self)?.into_font_collection(context)
fn into_font_collection(
self,
context: &CanvasFontContext,
) -> Result<Arc<FontCollection>, FontError> {
context
.get_font_by_postscript_name(self)?
.into_font_collection(context)
}
}
impl<'a, 'b> IntoFontCollection for &'a [&'b str] {
#[inline]
fn into_font_collection(self, context: &CanvasFontContext) -> Result<Arc<FontCollection>, FontError> {
fn into_font_collection(
self,
context: &CanvasFontContext,
) -> Result<Arc<FontCollection>, FontError> {
let mut font_collection = FontCollection::new();
for postscript_name in self {
let font = context.get_font_by_postscript_name(postscript_name)?;

View File

@ -127,13 +127,13 @@ impl ColorF {
let c = (1.0 - f32::abs(2.0 * l - 1.0)) * s;
let xc = F32x4::new(c * (1.0 - f32::abs(h % 2.0 - 1.0)), c, 0.0, a);
let rgba = match f32::ceil(h) as i32 {
1 => xc.yxzw(),
2 => xc.xyzw(),
3 => xc.zyxw(),
4 => xc.zxyw(),
5 => xc.xzyw(),
1 => xc.yxzw(),
2 => xc.xyzw(),
3 => xc.zyxw(),
4 => xc.zxyw(),
5 => xc.xzyw(),
0 | 6 => xc.yzxw(),
_ => xc.zzzw(),
_ => xc.zzzw(),
};
let m = l - 0.5 * c;
ColorF(rgba + F32x4::new(m, m, m, 0.0))
@ -162,7 +162,12 @@ impl ColorF {
#[inline]
pub fn to_u8(&self) -> ColorU {
let color = (self.0 * F32x4::splat(255.0)).to_i32x4();
ColorU { r: color[0] as u8, g: color[1] as u8, b: color[2] as u8, a: color[3] as u8 }
ColorU {
r: color[0] as u8,
g: color[1] as u8,
b: color[2] as u8,
a: color[3] as u8,
}
}
#[inline]
@ -226,9 +231,7 @@ impl Debug for ColorF {
#[inline]
pub fn color_slice_to_u8_slice(slice: &[ColorU]) -> &[u8] {
unsafe {
slice::from_raw_parts(slice.as_ptr() as *const u8, slice.len() * 4)
}
unsafe { slice::from_raw_parts(slice.as_ptr() as *const u8, slice.len() * 4) }
}
#[inline]

View File

@ -9,7 +9,7 @@
// except according to those terms.
use pathfinder_simd::default::F32x4;
use std::ops::{Add, Mul, Deref};
use std::ops::{Add, Deref, Mul};
/// ColorMatrix filter/transformation
///
@ -34,22 +34,22 @@ impl ColorMatrix {
/// See the `hueRotate` attribute of the `feColorMatrix` element in the SVG specification.
pub fn hue_rotate(angle: f32) -> ColorMatrix {
let a = ColorMatrix::from_rows([
[ 0.213, 0.715, 0.072, 0.0, 0.0],
[ 0.213, 0.715, 0.072, 0.0, 0.0],
[ 0.213, 0.715, 0.072, 0.0, 0.0],
[ 0.0, 0.0, 0.0, 1.0, 0.0],
[0.213, 0.715, 0.072, 0.0, 0.0],
[0.213, 0.715, 0.072, 0.0, 0.0],
[0.213, 0.715, 0.072, 0.0, 0.0],
[0.0, 0.0, 0.0, 1.0, 0.0],
]);
let b = ColorMatrix::from_rows([
[ 0.787, -0.715, -0.072, 0.0, 0.0],
[-0.213, 0.285, -0.072, 0.0, 0.0],
[-0.213, -0.715, 0.928, 0.0, 0.0],
[ 0.0, 0.0, 0.0, 0.0, 0.0],
[0.787, -0.715, -0.072, 0.0, 0.0],
[-0.213, 0.285, -0.072, 0.0, 0.0],
[-0.213, -0.715, 0.928, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0, 0.0],
]);
let c = ColorMatrix::from_rows([
[-0.213, -0.715, 0.928, 0.0, 0.0],
[ 0.143, 0.140, -0.283, 0.0, 0.0],
[-0.787, 0.715, 0.072, 0.0, 0.0],
[ 0.0, 0.0, 0.0, 0.0, 0.0],
[-0.213, -0.715, 0.928, 0.0, 0.0],
[0.143, 0.140, -0.283, 0.0, 0.0],
[-0.787, 0.715, 0.072, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0, 0.0],
]);
a + b * angle.cos() + c * angle.sin()
}
@ -59,16 +59,16 @@ impl ColorMatrix {
/// See the `saturate` attribute of the `feColorMatrix` element in the SVG specification.
pub fn saturate(saturation: f32) -> ColorMatrix {
let a = ColorMatrix::from_rows([
[ 0.213, 0.715, 0.072, 0.0, 0.0],
[ 0.213, 0.715, 0.072, 0.0, 0.0],
[ 0.213, 0.715, 0.072, 0.0, 0.0],
[ 0.0, 0.0, 0.0, 1.0, 0.0],
[0.213, 0.715, 0.072, 0.0, 0.0],
[0.213, 0.715, 0.072, 0.0, 0.0],
[0.213, 0.715, 0.072, 0.0, 0.0],
[0.0, 0.0, 0.0, 1.0, 0.0],
]);
let b = ColorMatrix::from_rows([
[ 0.787, -0.715, -0.072, 0.0, 0.0],
[-0.213, 0.285, -0.072, 0.0, 0.0],
[-0.213, -0.715, 0.928, 0.0, 0.0],
[ 0.0, 0.0, 0.0, 0.0, 0.0],
[0.787, -0.715, -0.072, 0.0, 0.0],
[-0.213, 0.285, -0.072, 0.0, 0.0],
[-0.213, -0.715, 0.928, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0, 0.0],
]);
a + b * saturation
}
@ -79,10 +79,10 @@ impl ColorMatrix {
/// specification.
pub fn luminance_to_alpha() -> ColorMatrix {
ColorMatrix::from_rows([
[ 0.0, 0.0, 0.0, 0.0, 0.0],
[ 0.0, 0.0, 0.0, 0.0, 0.0],
[ 0.0, 0.0, 0.0, 0.0, 0.0],
[ 0.2125, 0.7154, 0.0721, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0, 0.0],
[0.2125, 0.7154, 0.0721, 0.0, 0.0],
])
}
}

View File

@ -18,7 +18,7 @@ use arrayvec::ArrayVec;
use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::util::lerp;
use pathfinder_geometry::vector::{Vector2F, Vector4F, vec2f};
use pathfinder_geometry::vector::{vec2f, Vector2F, Vector4F};
use smallvec::SmallVec;
use std::fmt::Debug;
use std::mem;
@ -491,8 +491,10 @@ pub(crate) fn rect_is_inside_polygon(rect: RectF, polygon_points: &[Vector2F]) -
}
/// Clips a line segment to an axis-aligned rectangle using Cohen-Sutherland clipping.
pub fn clip_line_segment_to_rect(mut line_segment: LineSegment2F, rect: RectF)
-> Option<LineSegment2F> {
pub fn clip_line_segment_to_rect(
mut line_segment: LineSegment2F,
rect: RectF,
) -> Option<LineSegment2F> {
let mut outcode_from = compute_outcode(line_segment.from(), rect);
let mut outcode_to = compute_outcode(line_segment.to(), rect);
@ -512,29 +514,45 @@ pub fn clip_line_segment_to_rect(mut line_segment: LineSegment2F, rect: RectF)
};
if outcode.contains(Outcode::LEFT) {
point = vec2f(rect.min_x(),
lerp(line_segment.from_y(),
line_segment.to_y(),
(rect.min_x() - line_segment.from_x()) /
(line_segment.to_x() - line_segment.from_x())));
point = vec2f(
rect.min_x(),
lerp(
line_segment.from_y(),
line_segment.to_y(),
(rect.min_x() - line_segment.from_x())
/ (line_segment.to_x() - line_segment.from_x()),
),
);
} else if outcode.contains(Outcode::RIGHT) {
point = vec2f(rect.max_x(),
lerp(line_segment.from_y(),
line_segment.to_y(),
(rect.max_x() - line_segment.from_x()) /
(line_segment.to_x() - line_segment.from_x())));
point = vec2f(
rect.max_x(),
lerp(
line_segment.from_y(),
line_segment.to_y(),
(rect.max_x() - line_segment.from_x())
/ (line_segment.to_x() - line_segment.from_x()),
),
);
} else if outcode.contains(Outcode::TOP) {
point = vec2f(lerp(line_segment.from_x(),
line_segment.to_x(),
(rect.min_y() - line_segment.from_y()) /
(line_segment.to_y() - line_segment.from_y())),
rect.min_y());
point = vec2f(
lerp(
line_segment.from_x(),
line_segment.to_x(),
(rect.min_y() - line_segment.from_y())
/ (line_segment.to_y() - line_segment.from_y()),
),
rect.min_y(),
);
} else if outcode.contains(Outcode::BOTTOM) {
point = vec2f(lerp(line_segment.from_x(),
line_segment.to_x(),
(rect.max_y() - line_segment.from_y()) /
(line_segment.to_y() - line_segment.from_y())),
rect.max_y());
point = vec2f(
lerp(
line_segment.from_x(),
line_segment.to_x(),
(rect.max_y() - line_segment.from_y())
/ (line_segment.to_y() - line_segment.from_y()),
),
rect.max_y(),
);
}
if clip_from {

View File

@ -38,7 +38,11 @@ impl<'a> OutlineDash<'a> {
/// <https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineDashOffset>.
#[inline]
pub fn new(input: &'a Outline, dashes: &'a [f32], offset: f32) -> OutlineDash<'a> {
OutlineDash { input, output: Outline::new(), state: DashState::new(dashes, offset) }
OutlineDash {
input,
output: Outline::new(),
state: DashState::new(dashes, offset),
}
}
/// Performs the dashing operation.
@ -66,9 +70,16 @@ struct ContourDash<'a, 'b, 'c> {
}
impl<'a, 'b, 'c> ContourDash<'a, 'b, 'c> {
fn new(input: &'a Contour, output: &'b mut Outline, state: &'c mut DashState<'a>)
-> ContourDash<'a, 'b, 'c> {
ContourDash { input, output, state }
fn new(
input: &'a Contour,
output: &'b mut Outline,
state: &'c mut DashState<'a>,
) -> ContourDash<'a, 'b, 'c> {
ContourDash {
input,
output,
state,
}
}
fn dash(&mut self) {
@ -95,13 +106,16 @@ impl<'a, 'b, 'c> ContourDash<'a, 'b, 'c> {
}
if self.state.is_on() {
self.state.output.push_segment(&current_segment, PushSegmentFlags::empty());
self.state
.output
.push_segment(&current_segment, PushSegmentFlags::empty());
}
self.state.distance_left -= distance;
if self.state.distance_left < EPSILON {
if self.state.is_on() {
self.output.push_contour(mem::replace(&mut self.state.output, Contour::new()));
self.output
.push_contour(mem::replace(&mut self.state.output, Contour::new()));
}
self.state.current_dash_index += 1;

View File

@ -10,7 +10,7 @@
use crate::orientation::Orientation;
use crate::outline::Contour;
use pathfinder_geometry::vector::{Vector2F, vec2f};
use pathfinder_geometry::vector::{vec2f, Vector2F};
pub struct ContourDilator<'a> {
contour: &'a mut Contour,
@ -33,10 +33,11 @@ impl<'a> ContourDilator<'a> {
pub fn dilate(&mut self) {
// Determine orientation.
let scale = self.amount * (match self.orientation {
Orientation::Ccw => vec2f( 1.0, -1.0),
Orientation::Cw => vec2f(-1.0, 1.0),
});
let scale = self.amount
* (match self.orientation {
Orientation::Ccw => vec2f(1.0, -1.0),
Orientation::Cw => vec2f(-1.0, 1.0),
});
// Find the starting and previous positions.
let first_position = self.contour.position_of(0);

View File

@ -10,7 +10,7 @@
//! Special effects that can be applied to layers.
use pathfinder_color::{ColorF, matrix::ColorMatrix};
use pathfinder_color::{matrix::ColorMatrix, ColorF};
use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::vector::Vector2F;
use pathfinder_simd::default::F32x2;
@ -88,7 +88,7 @@ pub enum PatternFilter {
},
/// A color matrix multiplication.
///
///
/// The matrix is stored in 5 columns of `F32x4`. See the `feColorMatrix` element in the SVG
/// specification.
ColorMatrix(ColorMatrix),
@ -202,64 +202,64 @@ impl BlendMode {
pub fn occludes_backdrop(self) -> bool {
match self {
BlendMode::SrcOver | BlendMode::Clear => true,
BlendMode::DestOver |
BlendMode::DestOut |
BlendMode::SrcAtop |
BlendMode::Xor |
BlendMode::Lighter |
BlendMode::Lighten |
BlendMode::Darken |
BlendMode::Copy |
BlendMode::SrcIn |
BlendMode::DestIn |
BlendMode::SrcOut |
BlendMode::DestAtop |
BlendMode::Multiply |
BlendMode::Screen |
BlendMode::HardLight |
BlendMode::Overlay |
BlendMode::ColorDodge |
BlendMode::ColorBurn |
BlendMode::SoftLight |
BlendMode::Difference |
BlendMode::Exclusion |
BlendMode::Hue |
BlendMode::Saturation |
BlendMode::Color |
BlendMode::Luminosity => false,
BlendMode::DestOver
| BlendMode::DestOut
| BlendMode::SrcAtop
| BlendMode::Xor
| BlendMode::Lighter
| BlendMode::Lighten
| BlendMode::Darken
| BlendMode::Copy
| BlendMode::SrcIn
| BlendMode::DestIn
| BlendMode::SrcOut
| BlendMode::DestAtop
| BlendMode::Multiply
| BlendMode::Screen
| BlendMode::HardLight
| BlendMode::Overlay
| BlendMode::ColorDodge
| BlendMode::ColorBurn
| BlendMode::SoftLight
| BlendMode::Difference
| BlendMode::Exclusion
| BlendMode::Hue
| BlendMode::Saturation
| BlendMode::Color
| BlendMode::Luminosity => false,
}
}
/// True if this blend mode does not preserve destination areas outside the source.
pub fn is_destructive(self) -> bool {
match self {
BlendMode::Clear |
BlendMode::Copy |
BlendMode::SrcIn |
BlendMode::DestIn |
BlendMode::SrcOut |
BlendMode::DestAtop => true,
BlendMode::SrcOver |
BlendMode::DestOver |
BlendMode::DestOut |
BlendMode::SrcAtop |
BlendMode::Xor |
BlendMode::Lighter |
BlendMode::Lighten |
BlendMode::Darken |
BlendMode::Multiply |
BlendMode::Screen |
BlendMode::HardLight |
BlendMode::Overlay |
BlendMode::ColorDodge |
BlendMode::ColorBurn |
BlendMode::SoftLight |
BlendMode::Difference |
BlendMode::Exclusion |
BlendMode::Hue |
BlendMode::Saturation |
BlendMode::Color |
BlendMode::Luminosity => false,
BlendMode::Clear
| BlendMode::Copy
| BlendMode::SrcIn
| BlendMode::DestIn
| BlendMode::SrcOut
| BlendMode::DestAtop => true,
BlendMode::SrcOver
| BlendMode::DestOver
| BlendMode::DestOut
| BlendMode::SrcAtop
| BlendMode::Xor
| BlendMode::Lighter
| BlendMode::Lighten
| BlendMode::Darken
| BlendMode::Multiply
| BlendMode::Screen
| BlendMode::HardLight
| BlendMode::Overlay
| BlendMode::ColorDodge
| BlendMode::ColorBurn
| BlendMode::SoftLight
| BlendMode::Difference
| BlendMode::Exclusion
| BlendMode::Hue
| BlendMode::Saturation
| BlendMode::Color
| BlendMode::Luminosity => false,
}
}
}

View File

@ -14,8 +14,8 @@ use crate::util;
use pathfinder_color::ColorU;
use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::vector::Vector2F;
use pathfinder_geometry::util as geometry_util;
use pathfinder_geometry::vector::Vector2F;
use pathfinder_simd::default::F32x2;
use std::cmp::Ordering;
use std::convert;
@ -67,7 +67,7 @@ pub enum GradientGeometry {
/// Like `gradientTransform` in SVG. Note that this is the inverse of Cairo's gradient
/// transform.
transform: Transform2F,
}
},
}
/// What should be rendered outside the color stops.
@ -83,13 +83,20 @@ pub enum GradientWrap {
impl Eq for Gradient {}
impl Hash for Gradient {
fn hash<H>(&self, state: &mut H) where H: Hasher {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
match self.geometry {
GradientGeometry::Linear(line) => {
(0).hash(state);
util::hash_line_segment(line, state);
}
GradientGeometry::Radial { line, radii, transform } => {
GradientGeometry::Radial {
line,
radii,
transform,
} => {
(1).hash(state);
util::hash_line_segment(line, state);
util::hash_f32(radii.x(), state);
@ -109,7 +116,10 @@ impl Hash for Gradient {
impl Eq for ColorStop {}
impl Hash for ColorStop {
fn hash<H>(&self, state: &mut H) where H: Hasher {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
unsafe {
self.color.hash(state);
let offset = mem::transmute::<f32, u32>(self.offset);
@ -145,10 +155,17 @@ impl Gradient {
/// with two circles, pass a `LineSegment2F`. To start the gradient at the center of the
/// circle, pass zero for the first radius.
#[inline]
pub fn radial<L>(line: L, radii: F32x2) -> Gradient where L: RadialGradientLine {
pub fn radial<L>(line: L, radii: F32x2) -> Gradient
where
L: RadialGradientLine,
{
let transform = Transform2F::default();
Gradient {
geometry: GradientGeometry::Radial { line: line.to_line(), radii, transform },
geometry: GradientGeometry::Radial {
line: line.to_line(),
radii,
transform,
},
stops: Vec::new(),
wrap: GradientWrap::Clamp,
}
@ -157,9 +174,16 @@ impl Gradient {
/// Adds a new color stop to the radial gradient.
#[inline]
pub fn add(&mut self, stop: ColorStop) {
let index = self.stops.binary_search_by(|other| {
if other.offset <= stop.offset { Ordering::Less } else { Ordering::Greater }
}).unwrap_or_else(convert::identity);
let index = self
.stops
.binary_search_by(|other| {
if other.offset <= stop.offset {
Ordering::Less
} else {
Ordering::Greater
}
})
.unwrap_or_else(convert::identity);
self.stops.insert(index, stop);
}
@ -193,10 +217,22 @@ impl Gradient {
t = geometry_util::clamp(t, 0.0, 1.0);
let last_index = self.stops.len() - 1;
let upper_index = self.stops.binary_search_by(|stop| {
if stop.offset < t || stop.offset == 0.0 { Ordering::Less } else { Ordering::Greater }
}).unwrap_or_else(convert::identity).min(last_index);
let lower_index = if upper_index > 0 { upper_index - 1 } else { upper_index };
let upper_index = self
.stops
.binary_search_by(|stop| {
if stop.offset < t || stop.offset == 0.0 {
Ordering::Less
} else {
Ordering::Greater
}
})
.unwrap_or_else(convert::identity)
.min(last_index);
let lower_index = if upper_index > 0 {
upper_index - 1
} else {
upper_index
};
let lower_stop = &self.stops[lower_index];
let upper_stop = &self.stops[upper_index];
@ -207,7 +243,11 @@ impl Gradient {
}
let ratio = ((t - lower_stop.offset) / denom).min(1.0);
lower_stop.color.to_f32().lerp(upper_stop.color.to_f32(), ratio).to_u8()
lower_stop
.color
.to_f32()
.lerp(upper_stop.color.to_f32(), ratio)
.to_u8()
}
/// Returns true if all colors of all stops in this gradient are opaque (alpha is 1.0).
@ -220,7 +260,9 @@ impl Gradient {
/// 0.0).
#[inline]
pub fn is_fully_transparent(&self) -> bool {
self.stops.iter().all(|stop| stop.color.is_fully_transparent())
self.stops
.iter()
.all(|stop| stop.color.is_fully_transparent())
}
/// Applies the given affine transform to this gradient.
@ -234,9 +276,9 @@ impl Gradient {
match self.geometry {
GradientGeometry::Linear(ref mut line) => *line = new_transform * *line,
GradientGeometry::Radial { ref mut transform, .. } => {
*transform = new_transform * *transform
}
GradientGeometry::Radial {
ref mut transform, ..
} => *transform = new_transform * *transform,
}
}
}
@ -283,9 +325,10 @@ mod test {
}
// Check that it sorted stably
assert!(grad.stops.windows(2).all(|w| {
w[0].offset < w[1].offset || w[0].color.r < w[1].color.r
}));
assert!(grad
.stops
.windows(2)
.all(|w| { w[0].offset < w[1].offset || w[0].color.r < w[1].color.r }));
}
#[test]
@ -293,7 +336,10 @@ mod test {
let mut grad = Gradient::linear_from_points(Vector2F::default(), Vector2F::default());
for i in 0..110 {
let zero_width = (i == 0) || (11 <= i && i < 99) || (i == 109);
grad.add_color_stop(ColorU::new(if zero_width { 255 } else { 0 }, 0, 0, 1), (i % 11) as f32 / 10.0);
grad.add_color_stop(
ColorU::new(if zero_width { 255 } else { 0 }, 0, 0, 1),
(i % 11) as f32 / 10.0,
);
}
for i in 0..11 {

View File

@ -17,10 +17,10 @@ use crate::segment::{Segment, SegmentFlags, SegmentKind};
use crate::util::safe_sqrt;
use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::transform2d::{Transform2F, Matrix2x2F};
use pathfinder_geometry::transform2d::{Matrix2x2F, Transform2F};
use pathfinder_geometry::transform3d::Perspective;
use pathfinder_geometry::unit_vector::UnitVector;
use pathfinder_geometry::vector::{Vector2F, vec2f};
use pathfinder_geometry::vector::{vec2f, Vector2F};
use std::f32::consts::PI;
use std::fmt::{self, Debug, Formatter};
use std::mem;
@ -90,7 +90,10 @@ impl Outline {
/// Creates a new outline from a list of segments.
#[inline]
pub fn from_segments<I>(segments: I) -> Outline where I: Iterator<Item = Segment> {
pub fn from_segments<I>(segments: I) -> Outline
where
I: Iterator<Item = Segment>,
{
let mut outline = Outline::new();
let mut current_contour = Contour::new();
@ -380,7 +383,7 @@ impl Contour {
contour.push_cubic(
vec2f(p0.x(), p1.y()),
vec2f(p1.x(), p0.y()),
vec2f(p2.x(), p0.y())
vec2f(p2.x(), p0.y()),
);
}
@ -393,7 +396,7 @@ impl Contour {
contour.push_cubic(
vec2f(p1.x(), p0.y()),
vec2f(p0.x(), p1.y()),
vec2f(p0.x(), p2.y())
vec2f(p0.x(), p2.y()),
);
}
@ -406,7 +409,7 @@ impl Contour {
contour.push_cubic(
vec2f(p0.x(), p1.y()),
vec2f(p1.x(), p0.y()),
vec2f(p2.x(), p0.y())
vec2f(p2.x(), p0.y()),
);
}
@ -419,7 +422,7 @@ impl Contour {
contour.push_cubic(
vec2f(p1.x(), p0.y()),
vec2f(p0.x(), p1.y()),
vec2f(p0.x(), p2.y())
vec2f(p0.x(), p2.y()),
);
}
@ -557,10 +560,7 @@ impl Contour {
}
#[inline]
pub(crate) fn push_point(&mut self,
point: Vector2F,
flags: PointFlags,
update_bounds: bool) {
pub(crate) fn push_point(&mut self, point: Vector2F, flags: PointFlags, update_bounds: bool) {
debug_assert!(!point.x().is_nan() && !point.y().is_nan());
if update_bounds {
@ -612,26 +612,30 @@ impl Contour {
///
/// * `direction`: Whether the arc should be drawn clockwise or counterclockwise from the +x
/// axis.
pub fn push_arc(&mut self,
transform: &Transform2F,
start_angle: f32,
end_angle: f32,
direction: ArcDirection) {
pub fn push_arc(
&mut self,
transform: &Transform2F,
start_angle: f32,
end_angle: f32,
direction: ArcDirection,
) {
if end_angle - start_angle >= PI * 2.0 {
self.push_ellipse(transform);
} else {
let start = vec2f(start_angle.cos(), start_angle.sin());
let end = vec2f(end_angle.cos(), end_angle.sin());
let end = vec2f(end_angle.cos(), end_angle.sin());
self.push_arc_from_unit_chord(transform, LineSegment2F::new(start, end), direction);
}
}
/// Given the endpoints of a unit arc, adds Bézier curves to approximate that arc to the
/// current contour. The given transform is applied to the resulting arc.
pub fn push_arc_from_unit_chord(&mut self,
transform: &Transform2F,
mut chord: LineSegment2F,
direction: ArcDirection) {
pub fn push_arc_from_unit_chord(
&mut self,
transform: &Transform2F,
mut chord: LineSegment2F,
direction: ArcDirection,
) {
let mut direction_transform = Transform2F::default();
if direction == ArcDirection::CCW {
chord *= vec2f(1.0, -1.0);
@ -640,15 +644,17 @@ impl Contour {
let (mut vector, end_vector) = (UnitVector(chord.from()), UnitVector(chord.to()));
for segment_index in 0..4 {
debug!("push_arc_from_unit_chord(): loop segment index {}", segment_index);
debug!(
"push_arc_from_unit_chord(): loop segment index {}",
segment_index
);
let mut sweep_vector = end_vector.rev_rotate_by(vector);
let last = sweep_vector.0.x() >= -EPSILON && sweep_vector.0.y() >= -EPSILON;
debug!("... end_vector={:?} vector={:?} sweep_vector={:?} last={:?}",
end_vector,
vector,
sweep_vector,
last);
debug!(
"... end_vector={:?} vector={:?} sweep_vector={:?} last={:?}",
end_vector, vector, sweep_vector, last
);
let mut segment;
if !last {
@ -683,12 +689,14 @@ impl Contour {
/// Draws an ellipse section with radii given by `radius` rotated by `x_axis_rotation` in
/// radians to `to` in the given direction. If `large_arc` is true, draws an arc bigger than
/// π radians, otherwise smaller than π radians.
pub fn push_svg_arc(&mut self,
radius: Vector2F,
x_axis_rotation: f32,
large_arc: bool,
direction: ArcDirection,
to: Vector2F) {
pub fn push_svg_arc(
&mut self,
radius: Vector2F,
x_axis_rotation: f32,
large_arc: bool,
direction: ArcDirection,
to: Vector2F,
) {
let r = radius;
let p = to;
let last = self.last_position().unwrap_or_default();
@ -698,7 +706,7 @@ impl Contour {
let r_inv = r.recip();
let sign = match (large_arc, direction) {
(false, ArcDirection::CW) | (true, ArcDirection::CCW) => 1.0,
(false, ArcDirection::CCW) | (true, ArcDirection::CW) => -1.0
(false, ArcDirection::CCW) | (true, ArcDirection::CW) => -1.0,
};
let rot = Matrix2x2F::from_rotation(x_axis_rotation);
// x'
@ -716,10 +724,14 @@ impl Contour {
let rq2 = r2 * q2.yx(); // (r_x^2 q_y^2, r_y^2 q_x^2)
let rq2_sum = rq2.x() + rq2.y(); // r_x^2 q_y^2 + r_y^2 q_x^2
// c'
let s = vec2f(1., -1.) * r * (q * r_inv).yx() * safe_sqrt((r2_prod - rq2_sum) / rq2_sum) * sign;
// c'
let s = vec2f(1., -1.)
* r
* (q * r_inv).yx()
* safe_sqrt((r2_prod - rq2_sum) / rq2_sum)
* sign;
let c = rot * s + (last + p) * 0.5;
let a = (q - s) * r_inv;
let b = -(q + s) * r_inv;
(a, b, c)
@ -729,10 +741,10 @@ impl Contour {
let b = -a;
(a, b, c)
};
let transform = Transform2F {
matrix: rot,
vector: c
vector: c,
} * Transform2F::from_scale(r);
let chord = LineSegment2F::new(a, b);
self.push_arc_from_unit_chord(&transform, chord, direction);
@ -747,17 +759,25 @@ impl Contour {
pub fn push_ellipse(&mut self, transform: &Transform2F) {
let segment = Segment::quarter_circle_arc();
let mut rotation;
self.push_segment(&segment.transform(transform),
PushSegmentFlags::UPDATE_BOUNDS | PushSegmentFlags::INCLUDE_FROM_POINT);
rotation = Transform2F::from_rotation_vector(UnitVector(vec2f( 0.0, 1.0)));
self.push_segment(&segment.transform(&(*transform * rotation)),
PushSegmentFlags::UPDATE_BOUNDS);
rotation = Transform2F::from_rotation_vector(UnitVector(vec2f(-1.0, 0.0)));
self.push_segment(&segment.transform(&(*transform * rotation)),
PushSegmentFlags::UPDATE_BOUNDS);
rotation = Transform2F::from_rotation_vector(UnitVector(vec2f( 0.0, -1.0)));
self.push_segment(&segment.transform(&(*transform * rotation)),
PushSegmentFlags::UPDATE_BOUNDS);
self.push_segment(
&segment.transform(transform),
PushSegmentFlags::UPDATE_BOUNDS | PushSegmentFlags::INCLUDE_FROM_POINT,
);
rotation = Transform2F::from_rotation_vector(UnitVector(vec2f(0.0, 1.0)));
self.push_segment(
&segment.transform(&(*transform * rotation)),
PushSegmentFlags::UPDATE_BOUNDS,
);
rotation = Transform2F::from_rotation_vector(UnitVector(vec2f(-1.0, 0.0)));
self.push_segment(
&segment.transform(&(*transform * rotation)),
PushSegmentFlags::UPDATE_BOUNDS,
);
rotation = Transform2F::from_rotation_vector(UnitVector(vec2f(0.0, -1.0)));
self.push_segment(
&segment.transform(&(*transform * rotation)),
PushSegmentFlags::UPDATE_BOUNDS,
);
}
/// Returns the segment starting at the point with the given index.
@ -812,7 +832,7 @@ impl Contour {
#[inline]
pub fn point_is_endpoint(&self, point_index: u32) -> bool {
!self.flags[point_index as usize]
.intersects(PointFlags::CONTROL_POINT_0 | PointFlags::CONTROL_POINT_1)
.intersects(PointFlags::CONTROL_POINT_0 | PointFlags::CONTROL_POINT_1)
}
/// Returns `point_index + addend` modulo the number of points in this contour.
@ -928,8 +948,10 @@ impl Contour {
impl Debug for Contour {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
for (segment_index, segment) in self.iter(ContourIterFlags::IGNORE_CLOSE_SEGMENT)
.enumerate() {
for (segment_index, segment) in self
.iter(ContourIterFlags::IGNORE_CLOSE_SEGMENT)
.enumerate()
{
if segment_index == 0 {
write!(
formatter,
@ -1024,10 +1046,11 @@ impl<'a> Iterator for ContourIter<'a> {
fn next(&mut self) -> Option<Segment> {
let contour = self.contour;
let include_close_segment = self.contour.closed &&
!self.flags.contains(ContourIterFlags::IGNORE_CLOSE_SEGMENT);
if (self.index == contour.len() && !include_close_segment) ||
self.index == contour.len() + 1 {
let include_close_segment =
self.contour.closed && !self.flags.contains(ContourIterFlags::IGNORE_CLOSE_SEGMENT);
if (self.index == contour.len() && !include_close_segment)
|| self.index == contour.len() + 1
{
return None;
}
@ -1050,7 +1073,10 @@ impl<'a> Iterator for ContourIter<'a> {
let point2 = contour.position_of(point2_index);
self.index += 1;
if contour.point_is_endpoint(point2_index) {
return Some(Segment::quadratic(LineSegment2F::new(point0, point2), point1));
return Some(Segment::quadratic(
LineSegment2F::new(point0, point2),
point1,
));
}
let point3_index = self.index;

View File

@ -15,7 +15,7 @@ use crate::render_target::RenderTargetId;
use crate::util;
use pathfinder_color::{self as color, ColorU};
use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::vector::{Vector2I, vec2i};
use pathfinder_geometry::vector::{vec2i, Vector2I};
use std::collections::hash_map::DefaultHasher;
use std::fmt::{self, Debug, Formatter};
use std::hash::{Hash, Hasher};
@ -46,7 +46,7 @@ pub enum PatternSource {
id: RenderTargetId,
/// The device pixel size of the render target.
size: Vector2I,
}
},
}
/// A raster image, in 32-bit RGBA (8 bits per channel), non-premultiplied form.
@ -213,7 +213,12 @@ impl Image {
pixels.hash(&mut pixels_hasher);
let pixels_hash = pixels_hasher.finish();
Image { size, pixels, pixels_hash, is_opaque }
Image {
size,
pixels,
pixels_hash,
is_opaque,
}
}
/// A convenience function to create a new image with the given image from the `image` crate.
@ -280,7 +285,10 @@ impl Debug for Image {
}
impl Hash for Image {
fn hash<H>(&self, hasher: &mut H) where H: Hasher {
fn hash<H>(&self, hasher: &mut H)
where
H: Hasher,
{
self.size.hash(hasher);
self.pixels_hash.hash(hasher);
self.is_opaque.hash(hasher);
@ -290,7 +298,10 @@ impl Hash for Image {
impl Eq for Pattern {}
impl Hash for Pattern {
fn hash<H>(&self, state: &mut H) where H: Hasher {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
self.source.hash(state);
util::hash_transform2f(self.transform, state);
self.flags.hash(state);

View File

@ -13,7 +13,7 @@
use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::util::EPSILON;
use pathfinder_geometry::vector::{Vector2F, vec2f};
use pathfinder_geometry::vector::{vec2f, Vector2F};
use pathfinder_simd::default::F32x4;
use std::f32::consts::SQRT_2;
@ -100,8 +100,12 @@ impl Segment {
return Segment::line(LineSegment2F::new(vec2f(1.0, 0.0), vec2f(1.0, 0.0)));
}
let term = F32x4::new(cos_sweep_angle, -cos_sweep_angle,
cos_sweep_angle, -cos_sweep_angle);
let term = F32x4::new(
cos_sweep_angle,
-cos_sweep_angle,
cos_sweep_angle,
-cos_sweep_angle,
);
let signs = F32x4::new(1.0, -1.0, 1.0, 1.0);
let p3p0 = ((F32x4::splat(1.0) + term) * F32x4::splat(0.5)).sqrt() * signs;
let (p0x, p0y) = (p3p0.z(), p3p0.w());
@ -175,8 +179,9 @@ impl Segment {
let mut new_segment = *self;
let p1_2 = self.ctrl.from() + self.ctrl.from();
new_segment.ctrl = LineSegment2F::new(self.baseline.from() + p1_2,
p1_2 + self.baseline.to()) * (1.0 / 3.0);
new_segment.ctrl =
LineSegment2F::new(self.baseline.from() + p1_2, p1_2 + self.baseline.to())
* (1.0 / 3.0);
new_segment.kind = SegmentKind::Cubic;
new_segment
}

View File

@ -16,15 +16,15 @@ use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::util::EPSILON;
use pathfinder_geometry::vector::{Vector2F, vec2f};
use pathfinder_geometry::vector::{vec2f, Vector2F};
use std::f32;
const TOLERANCE: f32 = 0.01;
/// Strokes an outline with a stroke style to produce a new outline.
///
///
/// An example of use:
///
///
/// ```no_run
/// use pathfinder_content::stroke::OutlineStrokeToFill;
/// use pathfinder_content::stroke::StrokeStyle;
@ -86,7 +86,11 @@ impl<'a> OutlineStrokeToFill<'a> {
/// given stroke style.
#[inline]
pub fn new(input: &Outline, style: StrokeStyle) -> OutlineStrokeToFill {
OutlineStrokeToFill { input, output: Outline::new(), style }
OutlineStrokeToFill {
input,
output: Outline::new(),
style,
}
}
/// Performs the stroke operation.
@ -94,18 +98,22 @@ impl<'a> OutlineStrokeToFill<'a> {
let mut new_contours = vec![];
for input in &self.input.contours {
let closed = input.closed;
let mut stroker = ContourStrokeToFill::new(input,
Contour::new(),
self.style.line_width * 0.5,
self.style.line_join);
let mut stroker = ContourStrokeToFill::new(
input,
Contour::new(),
self.style.line_width * 0.5,
self.style.line_join,
);
stroker.offset_forward();
if closed {
self.push_stroked_contour(&mut new_contours, stroker, true);
stroker = ContourStrokeToFill::new(input,
Contour::new(),
self.style.line_width * 0.5,
self.style.line_join);
stroker = ContourStrokeToFill::new(
input,
Contour::new(),
self.style.line_width * 0.5,
self.style.line_join,
);
} else {
self.add_cap(&mut stroker.output);
}
@ -119,7 +127,9 @@ impl<'a> OutlineStrokeToFill<'a> {
}
let mut new_bounds = None;
new_contours.iter().for_each(|contour| contour.update_bounds(&mut new_bounds));
new_contours
.iter()
.for_each(|contour| contour.update_bounds(&mut new_bounds));
self.output.contours = new_contours;
self.output.bounds = new_bounds.unwrap_or_else(|| RectF::default());
@ -131,18 +141,22 @@ impl<'a> OutlineStrokeToFill<'a> {
self.output
}
fn push_stroked_contour(&mut self,
new_contours: &mut Vec<Contour>,
mut stroker: ContourStrokeToFill,
closed: bool) {
fn push_stroked_contour(
&mut self,
new_contours: &mut Vec<Contour>,
mut stroker: ContourStrokeToFill,
closed: bool,
) {
// Add join if necessary.
if closed && stroker.output.might_need_join(self.style.line_join) {
let (p1, p0) = (stroker.output.position_of(1), stroker.output.position_of(0));
let final_segment = LineSegment2F::new(p1, p0);
stroker.output.add_join(self.style.line_width * 0.5,
self.style.line_join,
stroker.input.position_of(0),
final_segment);
stroker.output.add_join(
self.style.line_width * 0.5,
self.style.line_join,
stroker.input.position_of(0),
final_segment,
);
}
stroker.output.closed = true;
@ -151,7 +165,7 @@ impl<'a> OutlineStrokeToFill<'a> {
fn add_cap(&mut self, contour: &mut Contour) {
if self.style.line_cap == LineCap::Butt || contour.len() < 2 {
return
return;
}
let width = self.style.line_width;
@ -209,14 +223,23 @@ struct ContourStrokeToFill<'a> {
impl<'a> ContourStrokeToFill<'a> {
#[inline]
fn new(input: &Contour, output: Contour, radius: f32, join: LineJoin) -> ContourStrokeToFill {
ContourStrokeToFill { input, output, radius, join }
ContourStrokeToFill {
input,
output,
radius,
join,
}
}
fn offset_forward(&mut self) {
for (segment_index, segment) in self.input.iter(ContourIterFlags::empty()).enumerate() {
// FIXME(pcwalton): We negate the radius here so that round end caps can be drawn
// clockwise. Of course, we should just implement anticlockwise arcs to begin with...
let join = if segment_index == 0 { LineJoin::Bevel } else { self.join };
let join = if segment_index == 0 {
LineJoin::Bevel
} else {
self.join
};
segment.offset(-self.radius, join, &mut self.output);
}
}
@ -231,7 +254,11 @@ impl<'a> ContourStrokeToFill<'a> {
for (segment_index, segment) in segments.iter().enumerate() {
// FIXME(pcwalton): We negate the radius here so that round end caps can be drawn
// clockwise. Of course, we should just implement anticlockwise arcs to begin with...
let join = if segment_index == 0 { LineJoin::Bevel } else { self.join };
let join = if segment_index == 0 {
LineJoin::Bevel
} else {
self.join
};
segment.offset(-self.radius, join, &mut self.output);
}
}
@ -239,11 +266,13 @@ impl<'a> ContourStrokeToFill<'a> {
trait Offset {
fn offset(&self, distance: f32, join: LineJoin, contour: &mut Contour);
fn add_to_contour(&self,
distance: f32,
join: LineJoin,
join_point: Vector2F,
contour: &mut Contour);
fn add_to_contour(
&self,
distance: f32,
join: LineJoin,
join_point: Vector2F,
contour: &mut Contour,
);
fn offset_once(&self, distance: f32) -> Self;
fn error_is_within_tolerance(&self, other: &Segment, distance: f32) -> bool;
}
@ -270,11 +299,13 @@ impl Offset for Segment {
after.offset(distance, join, contour);
}
fn add_to_contour(&self,
distance: f32,
join: LineJoin,
join_point: Vector2F,
contour: &mut Contour) {
fn add_to_contour(
&self,
distance: f32,
join: LineJoin,
join_point: Vector2F,
contour: &mut Contour,
) {
// Add join if necessary.
if contour.might_need_join(join) {
let p3 = self.baseline.from();
@ -404,11 +435,13 @@ impl Contour {
}
}
fn add_join(&mut self,
distance: f32,
join: LineJoin,
join_point: Vector2F,
next_tangent: LineSegment2F) {
fn add_join(
&mut self,
distance: f32,
join: LineJoin,
join_point: Vector2F,
next_tangent: LineSegment2F,
) {
let (p0, p1) = (self.position_of_last(2), self.position_of_last(1));
let prev_tangent = LineSegment2F::new(p0, p1);
@ -456,10 +489,14 @@ impl Default for StrokeStyle {
impl Default for LineCap {
#[inline]
fn default() -> LineCap { LineCap::Butt }
fn default() -> LineCap {
LineCap::Butt
}
}
impl Default for LineJoin {
#[inline]
fn default() -> LineJoin { LineJoin::Miter(10.0) }
fn default() -> LineJoin {
LineJoin::Miter(10.0)
}
}

View File

@ -36,8 +36,12 @@ where
// TODO(pcwalton): Can we go faster by transforming an entire line segment with SIMD?
let mut segment = self.iter.next()?;
if !segment.is_none() {
segment.baseline.set_from(self.transform * segment.baseline.from());
segment.baseline.set_to(self.transform * segment.baseline.to());
segment
.baseline
.set_from(self.transform * segment.baseline.from());
segment
.baseline
.set_to(self.transform * segment.baseline.to());
if !segment.is_line() {
segment.ctrl.set_from(self.transform * segment.ctrl.from());
if !segment.is_quadratic() {
@ -83,10 +87,16 @@ where
fn next(&mut self) -> Option<Segment> {
let mut segment = self.iter.next()?;
if !segment.is_none() {
segment.baseline.set_from(self.perspective * segment.baseline.from());
segment.baseline.set_to(self.perspective * segment.baseline.to());
segment
.baseline
.set_from(self.perspective * segment.baseline.from());
segment
.baseline
.set_to(self.perspective * segment.baseline.to());
if !segment.is_line() {
segment.ctrl.set_from(self.perspective * segment.ctrl.from());
segment
.ctrl
.set_from(self.perspective * segment.ctrl.from());
if !segment.is_quadratic() {
segment.ctrl.set_to(self.perspective * segment.ctrl.to());
}

View File

@ -16,30 +16,45 @@ use pathfinder_simd::default::{F32x2, F32x4};
use std::hash::{Hash, Hasher};
use std::mem;
pub(crate) fn hash_line_segment<H>(line_segment: LineSegment2F, state: &mut H) where H: Hasher {
pub(crate) fn hash_line_segment<H>(line_segment: LineSegment2F, state: &mut H)
where
H: Hasher,
{
hash_f32x4(line_segment.0, state);
}
pub(crate) fn hash_transform2f<H>(transform: Transform2F, state: &mut H) where H: Hasher {
pub(crate) fn hash_transform2f<H>(transform: Transform2F, state: &mut H)
where
H: Hasher,
{
hash_f32x4(transform.matrix.0, state);
hash_f32x2(transform.vector.0, state);
}
pub(crate) fn hash_f32<H>(value: f32, state: &mut H) where H: Hasher {
pub(crate) fn hash_f32<H>(value: f32, state: &mut H)
where
H: Hasher,
{
unsafe {
let data: u32 = mem::transmute::<f32, u32>(value);
data.hash(state);
}
}
pub(crate) fn hash_f32x2<H>(vector: F32x2, state: &mut H) where H: Hasher {
pub(crate) fn hash_f32x2<H>(vector: F32x2, state: &mut H)
where
H: Hasher,
{
unsafe {
let data: [u32; 2] = mem::transmute::<F32x2, [u32; 2]>(vector);
data.hash(state);
}
}
pub(crate) fn hash_f32x4<H>(vector: F32x4, state: &mut H) where H: Hasher {
pub(crate) fn hash_f32x4<H>(vector: F32x4, state: &mut H)
where
H: Hasher,
{
unsafe {
let data: [u32; 4] = mem::transmute::<F32x4, [u32; 4]>(vector);
data.hash(state);

View File

@ -16,8 +16,8 @@ use jni::{JNIEnv, JavaVM};
use pathfinder_demo::window::{Event, SVGPath, View, Window, WindowSize};
use pathfinder_demo::DemoApp;
use pathfinder_demo::Options;
use pathfinder_geometry::vector::{Vector2I, vec2i};
use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::vector::{vec2i, Vector2I};
use pathfinder_gl::GLVersion;
use pathfinder_resources::ResourceLoader;
use std::cell::RefCell;
@ -131,7 +131,10 @@ pub unsafe extern "system" fn Java_graphics_pathfinder_pathfinderdemo_Pathfinder
x: i32,
y: i32,
) {
EVENT_QUEUE.lock().unwrap().push(Event::MouseDown(vec2i(x, y)))
EVENT_QUEUE
.lock()
.unwrap()
.push(Event::MouseDown(vec2i(x, y)))
}
#[no_mangle]
@ -141,7 +144,10 @@ pub unsafe extern "system" fn Java_graphics_pathfinder_pathfinderdemo_Pathfinder
x: i32,
y: i32,
) {
EVENT_QUEUE.lock().unwrap().push(Event::MouseDragged(vec2i(x, y)))
EVENT_QUEUE
.lock()
.unwrap()
.push(Event::MouseDragged(vec2i(x, y)))
}
#[no_mangle]
@ -152,7 +158,10 @@ pub unsafe extern "system" fn Java_graphics_pathfinder_pathfinderdemo_Pathfinder
center_x: i32,
center_y: i32,
) {
EVENT_QUEUE.lock().unwrap().push(Event::Zoom(factor, vec2i(center_x, center_y)))
EVENT_QUEUE
.lock()
.unwrap()
.push(Event::Zoom(factor, vec2i(center_x, center_y)))
}
#[no_mangle]

View File

@ -11,7 +11,7 @@ pf-gl = []
clap = "2.32"
gl = "0.14"
rayon = "1.0"
usvg = "0.9"
usvg = "0.10"
[dependencies.image]
version = "0.23"

View File

@ -14,10 +14,10 @@
// proper.
use crate::window::{OcularTransform, View};
use pathfinder_geometry::vector::{Vector2I, Vector4F};
use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::transform3d::{Perspective, Transform4F};
use pathfinder_geometry::vector::{Vector2I, Vector4F};
use std::f32::consts::FRAC_PI_4;
const NEAR_CLIP_PLANE: f32 = 0.01;
@ -53,8 +53,8 @@ impl Camera {
}
fn new_2d(view_box: RectF, viewport_size: Vector2I) -> Camera {
let scale = i32::min(viewport_size.x(), viewport_size.y()) as f32 *
scale_factor_for_view_box(view_box);
let scale = i32::min(viewport_size.x(), viewport_size.y()) as f32
* scale_factor_for_view_box(view_box);
let origin = viewport_size.to_f32() * 0.5 - view_box.size() * (scale * 0.5);
Camera::TwoD(Transform2F::from_scale(scale).translate(origin))
}
@ -154,9 +154,10 @@ impl CameraTransform3D {
pub fn to_transform(&self) -> Transform4F {
let flip = Vector4F::new(1.0, -1.0, 1.0, 1.0);
Transform4F::from_scale(flip).translate(-self.position)
.uniform_scale(2.0 * self.scale)
.rotate(self.yaw, self.pitch, 0.0)
Transform4F::from_scale(flip)
.translate(-self.position)
.uniform_scale(2.0 * self.scale)
.rotate(self.yaw, self.pitch, 0.0)
}
}

View File

@ -35,7 +35,10 @@ impl DemoExecutor {
impl Executor for DemoExecutor {
fn build_vector<T, F>(&self, length: usize, builder: F) -> Vec<T>
where T: Send, F: Fn(usize) -> T + Send + Sync {
where
T: Send,
F: Fn(usize) -> T + Send + Sync,
{
if self.sequential_mode {
SequentialExecutor.build_vector(length, builder)
} else {

View File

@ -63,19 +63,33 @@ where
) -> GroundVertexArray<D> {
let vertex_array = device.create_vertex_array();
let position_attr = device.get_vertex_attr(&ground_program.program, "Position").unwrap();
let position_attr = device
.get_vertex_attr(&ground_program.program, "Position")
.unwrap();
device.bind_buffer(&vertex_array, quad_vertex_positions_buffer, BufferTarget::Vertex);
device.configure_vertex_attr(&vertex_array, &position_attr, &VertexAttrDescriptor {
size: 2,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I16,
stride: 4,
offset: 0,
divisor: 0,
buffer_index: 0,
});
device.bind_buffer(&vertex_array, quad_vertex_indices_buffer, BufferTarget::Index);
device.bind_buffer(
&vertex_array,
quad_vertex_positions_buffer,
BufferTarget::Vertex,
);
device.configure_vertex_attr(
&vertex_array,
&position_attr,
&VertexAttrDescriptor {
size: 2,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I16,
stride: 4,
offset: 0,
divisor: 0,
buffer_index: 0,
},
);
device.bind_buffer(
&vertex_array,
quad_vertex_indices_buffer,
BufferTarget::Index,
);
GroundVertexArray { vertex_array }
}

View File

@ -20,10 +20,10 @@ use crate::camera::Camera;
use crate::concurrent::DemoExecutor;
use crate::device::{GroundProgram, GroundVertexArray};
use crate::ui::{DemoUIModel, DemoUIPresenter, ScreenshotInfo, ScreenshotType, UIAction};
use crate::window::{Event, Keycode, DataPath, Window, WindowSize};
use crate::window::{DataPath, Event, Keycode, Window, WindowSize};
use clap::{App, Arg};
use pathfinder_content::effects::DEFRINGING_KERNEL_CORE_GRAPHICS;
use pathfinder_content::effects::PatternFilter;
use pathfinder_content::effects::DEFRINGING_KERNEL_CORE_GRAPHICS;
use pathfinder_content::effects::STEM_DARKENING_FACTORS;
use pathfinder_content::outline::Outline;
use pathfinder_content::pattern::Pattern;
@ -32,7 +32,7 @@ use pathfinder_export::{Export, FileFormat};
use pathfinder_geometry::rect::{RectF, RectI};
use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::transform3d::Transform4F;
use pathfinder_geometry::vector::{Vector2F, Vector2I, Vector4F, vec2f, vec2i};
use pathfinder_geometry::vector::{vec2f, vec2i, Vector2F, Vector2I, Vector4F};
use pathfinder_gpu::Device;
use pathfinder_renderer::concurrent::scene_proxy::SceneProxy;
use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererLevel};
@ -44,14 +44,14 @@ use pathfinder_renderer::scene::{DrawPath, RenderTarget, Scene};
use pathfinder_resources::ResourceLoader;
use pathfinder_svg::SVGScene;
use pathfinder_ui::{MousePosition, UIEvent};
use pdf::file::{CachedFile, FileOptions};
use pdf_render::{Cache as PdfRenderCache, SceneBackend};
use std::fs::File;
use std::io::BufWriter;
use std::path::PathBuf;
use std::thread;
use std::time::Duration;
use usvg::{Options as UsvgOptions, Tree as SvgTree};
use pdf::file::{CachedFile, FileOptions};
use pdf_render::{Cache as PdfRenderCache, SceneBackend};
#[cfg(any(not(target_os = "macos"), feature = "pf-gl"))]
use pathfinder_gl::GLDevice as DeviceImpl;
@ -88,11 +88,14 @@ enum Content {
Pdf {
file: CachedFile<Vec<u8>>,
cache: PdfRenderCache,
page_nr: u32
}
page_nr: u32,
},
}
pub struct DemoApp<W> where W: Window {
pub struct DemoApp<W>
where
W: Window,
{
pub window: W,
pub should_exit: bool,
pub options: Options,
@ -126,7 +129,10 @@ pub struct DemoApp<W> where W: Window {
ground_vertex_array: GroundVertexArray<DeviceImpl>,
}
impl<W> DemoApp<W> where W: Window {
impl<W> DemoApp<W>
where
W: Window,
{
pub fn new(window: W, window_size: WindowSize, options: Options) -> DemoApp<W> {
let expire_message_event_id = window.create_user_event_id();
@ -172,17 +178,18 @@ impl<W> DemoApp<W> where W: Window {
let renderer = Renderer::new(device, resources, render_mode, render_options);
let scene_metadata = SceneMetadata::new_clipping_view_box(&mut scene,
viewport.size());
let scene_metadata = SceneMetadata::new_clipping_view_box(&mut scene, viewport.size());
let camera = Camera::new(options.mode, scene_metadata.view_box, viewport.size());
let scene_proxy = SceneProxy::from_scene(scene, level, executor);
let ground_program = GroundProgram::new(renderer.device(), resources);
let ground_vertex_array = GroundVertexArray::new(renderer.device(),
&ground_program,
&renderer.quad_vertex_positions_buffer(),
&renderer.quad_vertex_indices_buffer());
let ground_vertex_array = GroundVertexArray::new(
renderer.device(),
&ground_program,
&renderer.quad_vertex_positions_buffer(),
&renderer.quad_vertex_indices_buffer(),
);
let mut message_epoch = 0;
emit_message::<W>(
@ -260,9 +267,9 @@ impl<W> DemoApp<W> where W: Window {
if modelview_transform.offset(*velocity) {
self.dirty = true;
}
let perspective = scene_transform.perspective *
scene_transform.modelview_to_eye *
modelview_transform.to_transform();
let perspective = scene_transform.perspective
* scene_transform.modelview_to_eye
* modelview_transform.to_transform();
Some(RenderTransform::Perspective(perspective))
}
Camera::TwoD(transform) => Some(RenderTransform::Transform2D(transform)),
@ -281,7 +288,7 @@ impl<W> DemoApp<W> where W: Window {
self.scene_proxy.build(build_options);
/*
self.render_command_stream =
self.render_command_stream =
Some(self.scene_proxy.build_with_stream(build_options, self.renderer.gpu_features()));
*/
}
@ -299,8 +306,8 @@ impl<W> DemoApp<W> where W: Window {
Event::WindowResized(new_size) => {
self.window_size = new_size;
let viewport = self.window.viewport(self.ui_model.mode.view(0));
self.scene_proxy.set_view_box(RectF::new(Vector2F::zero(),
viewport.size().to_f32()));
self.scene_proxy
.set_view_box(RectF::new(Vector2F::zero(), viewport.size().to_f32()));
self.renderer.options_mut().dest =
DestFramebuffer::full_window(self.window_size.device_size());
self.renderer.dest_framebuffer_size_changed();
@ -312,7 +319,11 @@ impl<W> DemoApp<W> where W: Window {
}
Event::MouseMoved(new_position) if self.mouselook_enabled => {
let mouse_position = self.process_mouse_position(new_position);
if let Camera::ThreeD { ref mut modelview_transform, .. } = self.camera {
if let Camera::ThreeD {
ref mut modelview_transform,
..
} = self.camera
{
let rotation = mouse_position.relative.to_f32() * MOUSELOOK_ROTATION_SPEED;
modelview_transform.yaw += rotation.x();
modelview_transform.pitch += rotation.y();
@ -329,13 +340,18 @@ impl<W> DemoApp<W> where W: Window {
let backing_scale_factor = self.window_size.backing_scale_factor;
let position = position.to_f32() * backing_scale_factor;
let scale_delta = 1.0 + d_dist * CAMERA_SCALE_SPEED_2D;
*transform = transform.translate(-position)
.scale(scale_delta)
.translate(position);
*transform = transform
.translate(-position)
.scale(scale_delta)
.translate(position);
}
}
Event::Look { pitch, yaw } => {
if let Camera::ThreeD { ref mut modelview_transform, .. } = self.camera {
if let Camera::ThreeD {
ref mut modelview_transform,
..
} = self.camera
{
modelview_transform.pitch += pitch;
modelview_transform.yaw += yaw;
}
@ -352,21 +368,20 @@ impl<W> DemoApp<W> where W: Window {
*scene_transform = eye_transforms[0];
for (index, eye_transform) in eye_transforms.iter().enumerate().skip(1) {
let weight = 1.0 / (index + 1) as f32;
scene_transform.perspective.transform =
scene_transform.perspective
.transform
.lerp(weight, &eye_transform.perspective.transform);
scene_transform.modelview_to_eye =
scene_transform.modelview_to_eye
.lerp(weight, &eye_transform.modelview_to_eye);
}
scene_transform.perspective.transform = scene_transform
.perspective
.transform
.lerp(weight, &eye_transform.perspective.transform);
scene_transform.modelview_to_eye = scene_transform
.modelview_to_eye
.lerp(weight, &eye_transform.modelview_to_eye);
}
// TODO: calculate the eye offset from the eye transforms?
let z_offset = -DEFAULT_EYE_OFFSET *
scene_transform.perspective.transform.c0.x();
let z_offset =
-DEFAULT_EYE_OFFSET * scene_transform.perspective.transform.c0.x();
let z_offset = Vector4F::new(0.0, 0.0, z_offset, 1.0);
scene_transform.modelview_to_eye =
Transform4F::from_translation(z_offset) *
scene_transform.modelview_to_eye;
scene_transform.modelview_to_eye = Transform4F::from_translation(z_offset)
* scene_transform.modelview_to_eye;
}
}
Event::KeyDown(Keycode::Alphanumeric(b'w')) => {
@ -452,9 +467,11 @@ impl<W> DemoApp<W> where W: Window {
let viewport_size = self.window.viewport(self.ui_model.mode.view(0)).size();
self.scene_metadata =
SceneMetadata::new_clipping_view_box(&mut scene, viewport_size);
self.camera = Camera::new(self.ui_model.mode,
self.scene_metadata.view_box,
viewport_size);
self.camera = Camera::new(
self.ui_model.mode,
self.scene_metadata.view_box,
viewport_size,
);
self.scene_proxy.replace_scene(scene);
@ -498,19 +515,28 @@ impl<W> DemoApp<W> where W: Window {
.push(*ui_event);
}
self.renderer.debug_ui_presenter_mut().debug_ui_presenter.ui_presenter.mouse_position =
self.renderer
.debug_ui_presenter_mut()
.debug_ui_presenter
.ui_presenter
.mouse_position =
self.last_mouse_position.to_f32() * self.window_size.backing_scale_factor;
let mut ui_action = UIAction::None;
if self.options.ui == UIVisibility::All {
let DebugUIPresenterInfo { device, allocator, debug_ui_presenter } =
self.renderer.debug_ui_presenter_mut();
self.ui_presenter.update(device,
allocator,
&mut self.window,
debug_ui_presenter,
&mut ui_action,
&mut self.ui_model);
let DebugUIPresenterInfo {
device,
allocator,
debug_ui_presenter,
} = self.renderer.debug_ui_presenter_mut();
self.ui_presenter.update(
device,
allocator,
&mut self.window,
debug_ui_presenter,
&mut ui_action,
&mut self.ui_model,
);
}
self.handle_ui_events(frame, &mut ui_action);
@ -524,24 +550,32 @@ impl<W> DemoApp<W> where W: Window {
fn maybe_take_screenshot(&mut self) {
match self.pending_screenshot_info.take() {
None => {}
Some(ScreenshotInfo { kind: ScreenshotType::PNG, path }) => {
self.take_raster_screenshot(path)
}
Some(ScreenshotInfo { kind: ScreenshotType::SVG, path }) => {
Some(ScreenshotInfo {
kind: ScreenshotType::PNG,
path,
}) => self.take_raster_screenshot(path),
Some(ScreenshotInfo {
kind: ScreenshotType::SVG,
path,
}) => {
// FIXME(pcwalton): This won't work on Android.
let mut writer = BufWriter::new(File::create(path).unwrap());
self.scene_proxy.copy_scene().export(&mut writer, FileFormat::SVG).unwrap();
self.scene_proxy
.copy_scene()
.export(&mut writer, FileFormat::SVG)
.unwrap();
}
}
}
fn handle_ui_events(&mut self, mut frame: Frame, ui_action: &mut UIAction) {
frame.ui_events = self.renderer
.debug_ui_presenter_mut()
.debug_ui_presenter
.ui_presenter
.event_queue
.drain();
frame.ui_events = self
.renderer
.debug_ui_presenter_mut()
.debug_ui_presenter
.ui_presenter
.event_queue
.drain();
self.handle_ui_action(ui_action);
@ -550,9 +584,11 @@ impl<W> DemoApp<W> where W: Window {
// FIXME(pcwalton): This should really be an MVC setup.
if self.camera.mode() != self.ui_model.mode {
let viewport_size = self.window.viewport(self.ui_model.mode.view(0)).size();
self.camera = Camera::new(self.ui_model.mode,
self.scene_metadata.view_box,
viewport_size);
self.camera = Camera::new(
self.ui_model.mode,
self.scene_metadata.view_box,
viewport_size,
);
}
for ui_event in frame.ui_events {
@ -614,9 +650,10 @@ impl<W> DemoApp<W> where W: Window {
if let Camera::TwoD(ref mut transform) = self.camera {
let old_rotation = transform.rotation();
let center = center_of_window(&self.window_size);
*transform = transform.translate(-center)
.rotate(*theta - old_rotation)
.translate(center);
*transform = transform
.translate(-center)
.rotate(*theta - old_rotation)
.translate(center);
}
}
}
@ -695,7 +732,7 @@ impl Options {
Arg::with_name("high-performance-gpu")
.short("g")
.long("high-performance-gpu")
.help("Use the high-performance (discrete) GPU, if available")
.help("Use the high-performance (discrete) GPU, if available"),
)
.arg(
Arg::with_name("level")
@ -703,7 +740,7 @@ impl Options {
.short("l")
.help("Set the renderer feature level as a Direct3D version equivalent")
.takes_value(true)
.possible_values(&["9", "11"])
.possible_values(&["9", "11"]),
)
.arg(
Arg::with_name("INPUT")
@ -764,36 +801,52 @@ pub enum UIVisibility {
}
impl Content {
fn render(&mut self, viewport_size: Vector2I, filter: Option<PatternFilter>) -> (Scene, String) {
fn render(
&mut self,
viewport_size: Vector2I,
filter: Option<PatternFilter>,
) -> (Scene, String) {
match *self {
Content::Svg(ref tree) => {
let built_svg = build_svg_tree(&tree, viewport_size, filter);
let message = get_svg_building_message(&built_svg);
(built_svg.scene, message)
}
Content::Pdf { ref file, ref mut cache, page_nr } => {
Content::Pdf {
ref file,
ref mut cache,
page_nr,
} => {
let page = file.get_page(page_nr).expect("no such page");
let mut backend = SceneBackend::new(cache);
pdf_render::render_page(&mut backend, &file.resolver(), &page, Transform2F::default()).unwrap();
pdf_render::render_page(
&mut backend,
&file.resolver(),
&page,
Transform2F::default(),
)
.unwrap();
(backend.finish(), String::new())
}
}
}
}
fn load_scene(resource_loader: &dyn ResourceLoader,
input_path: &DataPath,)
-> Content {
fn load_scene(resource_loader: &dyn ResourceLoader, input_path: &DataPath) -> Content {
let data = match *input_path {
DataPath::Default => resource_loader.slurp(DEFAULT_SVG_VIRTUAL_PATH).unwrap(),
DataPath::Resource(ref name) => resource_loader.slurp(name).unwrap(),
DataPath::Path(ref path) => std::fs::read(path).unwrap().into()
DataPath::Path(ref path) => std::fs::read(path).unwrap().into(),
};
if let Ok(tree) = SvgTree::from_data(&data, &UsvgOptions::default()) {
Content::Svg(tree)
} else if let Ok(file) = FileOptions::cached().load(data) {
Content::Pdf { file, cache: PdfRenderCache::new(), page_nr: 0 }
Content::Pdf {
file,
cache: PdfRenderCache::new(),
page_nr: 0,
}
} else {
panic!("can't load data");
}
@ -801,22 +854,38 @@ fn load_scene(resource_loader: &dyn ResourceLoader,
// FIXME(pcwalton): Rework how transforms work in the demo. The transform affects the final
// composite steps, breaking this approach.
fn build_svg_tree(tree: &SvgTree, viewport_size: Vector2I, filter: Option<PatternFilter>) -> SVGScene {
fn build_svg_tree(
tree: &SvgTree,
viewport_size: Vector2I,
filter: Option<PatternFilter>,
) -> SVGScene {
let mut scene = Scene::new();
let filter_info = filter.map(|filter| {
let scale = match filter {
PatternFilter::Text { defringing_kernel: Some(_), .. } => vec2i(3, 1),
PatternFilter::Text {
defringing_kernel: Some(_),
..
} => vec2i(3, 1),
_ => vec2i(1, 1),
};
let name = "Text".to_owned();
let render_target_size = viewport_size * scale;
let render_target = RenderTarget::new(render_target_size, name);
let render_target_id = scene.push_render_target(render_target);
FilterInfo { filter, render_target_id, render_target_size }
FilterInfo {
filter,
render_target_id,
render_target_size,
}
});
let mut built_svg = SVGScene::from_tree_and_scene(&tree, scene);
if let Some(FilterInfo { filter, render_target_id, render_target_size }) = filter_info {
if let Some(FilterInfo {
filter,
render_target_id,
render_target_size,
}) = filter_info
{
let mut pattern = Pattern::from_render_target(render_target_id, render_target_size);
pattern.set_filter(Some(filter));
let paint_id = built_svg.scene.push_paint(&Paint::from_pattern(pattern));
@ -879,7 +948,10 @@ struct Frame {
impl Frame {
fn new(transform: RenderTransform, ui_events: Vec<UIEvent>) -> Frame {
Frame { transform, ui_events }
Frame {
transform,
ui_events,
}
}
}
@ -928,6 +1000,6 @@ fn build_filter(ui_model: &DemoUIModel) -> Option<PatternFilter> {
Some(DEFRINGING_KERNEL_CORE_GRAPHICS)
} else {
None
}
},
})
}

View File

@ -15,11 +15,11 @@ use crate::window::{View, Window};
use crate::{BackgroundColor, DemoApp, UIVisibility};
use image::ColorType;
use pathfinder_color::{ColorF, ColorU};
use pathfinder_gpu::{ClearOps, DepthFunc, DepthState, Device, Primitive, RenderOptions};
use pathfinder_gpu::{RenderState, RenderTarget, TextureData, TextureFormat, UniformData};
use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::transform3d::Transform4F;
use pathfinder_geometry::vector::{Vector2I, Vector4F};
use pathfinder_gpu::{ClearOps, DepthFunc, DepthState, Device, Primitive, RenderOptions};
use pathfinder_gpu::{RenderState, RenderTarget, TextureData, TextureFormat, UniformData};
use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererOptions};
use pathfinder_renderer::options::RenderTransform;
use std::mem;
@ -41,7 +41,10 @@ const GROUND_LINE_COLOR: ColorU = ColorU {
const GRIDLINE_COUNT: i32 = 10;
impl<W> DemoApp<W> where W: Window {
impl<W> DemoApp<W>
where
W: Window,
{
pub fn prepare_frame_rendering(&mut self) -> u32 {
// Make the context current.
let view = self.ui_model.mode.view(0);
@ -62,9 +65,10 @@ impl<W> DemoApp<W> where W: Window {
let viewport = self.window.viewport(View::Stereo(0));
if self.scene_framebuffer.is_none()
|| self.renderer.device().texture_size(
&self.renderer.device().framebuffer_texture(self.scene_framebuffer
.as_ref()
.unwrap()),
&self
.renderer
.device()
.framebuffer_texture(self.scene_framebuffer.as_ref().unwrap()),
) != viewport.size()
{
let scene_texture = self
@ -120,9 +124,9 @@ impl<W> DemoApp<W> where W: Window {
},
..*self.renderer.options()
};
if let DestFramebuffer::Other(scene_framebuffer) = mem::replace(self.renderer
.options_mut(),
new_options).dest {
if let DestFramebuffer::Other(scene_framebuffer) =
mem::replace(self.renderer.options_mut(), new_options).dest
{
self.scene_framebuffer = Some(scene_framebuffer);
}
}
@ -165,22 +169,25 @@ impl<W> DemoApp<W> where W: Window {
self.draw_environment(render_scene_index);
let scene_framebuffer = self.scene_framebuffer.as_ref().unwrap();
let scene_texture = self.renderer.device().framebuffer_texture(scene_framebuffer);
let scene_texture = self
.renderer
.device()
.framebuffer_texture(scene_framebuffer);
let mut quad_scale = self.scene_metadata.view_box.size().to_4d();
quad_scale.set_z(1.0);
let quad_scale_transform = Transform4F::from_scale(quad_scale);
let scene_transform_matrix = scene_transform.perspective *
scene_transform.modelview_to_eye *
modelview_transform.to_transform() *
quad_scale_transform;
let scene_transform_matrix = scene_transform.perspective
* scene_transform.modelview_to_eye
* modelview_transform.to_transform()
* quad_scale_transform;
let eye_transform = &eye_transforms[render_scene_index as usize];
let eye_transform_matrix = eye_transform.perspective *
eye_transform.modelview_to_eye *
modelview_transform.to_transform() *
quad_scale_transform;
let eye_transform_matrix = eye_transform.perspective
* eye_transform.modelview_to_eye
* modelview_transform.to_transform()
* quad_scale_transform;
debug!(
"eye transform({}).modelview_to_eye={:?}",
@ -220,8 +227,8 @@ impl<W> DemoApp<W> where W: Window {
let base_transform = perspective.transform * Transform4F::from_translation(offset);
// Fill ground.
let transform = base_transform *
Transform4F::from_scale(Vector4F::new(ground_scale, 1.0, ground_scale, 1.0));
let transform = base_transform
* Transform4F::from_scale(Vector4F::new(ground_scale, 1.0, ground_scale, 1.0));
// Don't clear the first scene after drawing it.
let clear_color = if render_scene_index == 0 {
@ -230,30 +237,49 @@ impl<W> DemoApp<W> where W: Window {
None
};
self.renderer.device().draw_elements(6, &RenderState {
target: &self.renderer.draw_render_target(),
program: &self.ground_program.program,
vertex_array: &self.ground_vertex_array.vertex_array,
primitive: Primitive::Triangles,
textures: &[],
images: &[],
storage_buffers: &[],
uniforms: &[
(&self.ground_program.transform_uniform,
UniformData::from_transform_3d(&transform)),
(&self.ground_program.ground_color_uniform,
UniformData::Vec4(GROUND_SOLID_COLOR.to_f32().0)),
(&self.ground_program.gridline_color_uniform,
UniformData::Vec4(GROUND_LINE_COLOR.to_f32().0)),
(&self.ground_program.gridline_count_uniform, UniformData::Int(GRIDLINE_COUNT)),
],
viewport: self.renderer.draw_viewport(),
options: RenderOptions {
depth: Some(DepthState { func: DepthFunc::Less, write: true }),
clear_ops: ClearOps { color: clear_color, depth: Some(1.0), stencil: Some(0) },
..RenderOptions::default()
self.renderer.device().draw_elements(
6,
&RenderState {
target: &self.renderer.draw_render_target(),
program: &self.ground_program.program,
vertex_array: &self.ground_vertex_array.vertex_array,
primitive: Primitive::Triangles,
textures: &[],
images: &[],
storage_buffers: &[],
uniforms: &[
(
&self.ground_program.transform_uniform,
UniformData::from_transform_3d(&transform),
),
(
&self.ground_program.ground_color_uniform,
UniformData::Vec4(GROUND_SOLID_COLOR.to_f32().0),
),
(
&self.ground_program.gridline_color_uniform,
UniformData::Vec4(GROUND_LINE_COLOR.to_f32().0),
),
(
&self.ground_program.gridline_count_uniform,
UniformData::Int(GRIDLINE_COUNT),
),
],
viewport: self.renderer.draw_viewport(),
options: RenderOptions {
depth: Some(DepthState {
func: DepthFunc::Less,
write: true,
}),
clear_ops: ClearOps {
color: clear_color,
depth: Some(1.0),
stencil: Some(0),
},
..RenderOptions::default()
},
},
});
);
}
#[allow(deprecated)]
@ -271,9 +297,15 @@ impl<W> DemoApp<W> where W: Window {
pub fn take_raster_screenshot(&mut self, path: PathBuf) {
let drawable_size = self.window_size.device_size();
let viewport = RectI::new(Vector2I::default(), drawable_size);
let texture_data_receiver =
self.renderer.device().read_pixels(&RenderTarget::Default, viewport);
let pixels = match self.renderer.device().recv_texture_data(&texture_data_receiver) {
let texture_data_receiver = self
.renderer
.device()
.read_pixels(&RenderTarget::Default, viewport);
let pixels = match self
.renderer
.device()
.recv_texture_data(&texture_data_receiver)
{
TextureData::U8(pixels) => pixels,
_ => panic!("Unexpected pixel format for default framebuffer!"),
};

View File

@ -13,7 +13,7 @@ use crate::window::Window;
use crate::{BackgroundColor, Options};
use pathfinder_color::ColorU;
use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::vector::{Vector2I, vec2i};
use pathfinder_geometry::vector::{vec2i, Vector2I};
use pathfinder_gpu::allocator::GPUMemoryAllocator;
use pathfinder_gpu::{Device, TextureFormat};
use pathfinder_renderer::gpu::debug::DebugUIPresenter;
@ -41,9 +41,24 @@ const SCREENSHOT_PANEL_HEIGHT: i32 = BUTTON_HEIGHT * 2;
const ROTATE_PANEL_WIDTH: i32 = SLIDER_WIDTH + PADDING * 2;
const ROTATE_PANEL_HEIGHT: i32 = PADDING * 2 + SLIDER_HEIGHT;
const LIGHT_BG_COLOR: ColorU = ColorU { r: 248, g: 248, b: 248, a: 255, };
const DARK_BG_COLOR: ColorU = ColorU { r: 32, g: 32, b: 32, a: 255, };
const TRANSPARENT_BG_COLOR: ColorU = ColorU { r: 0, g: 0, b: 0, a: 0, };
const LIGHT_BG_COLOR: ColorU = ColorU {
r: 248,
g: 248,
b: 248,
a: 255,
};
const DARK_BG_COLOR: ColorU = ColorU {
r: 32,
g: 32,
b: 32,
a: 255,
};
const TRANSPARENT_BG_COLOR: ColorU = ColorU {
r: 0,
g: 0,
b: 0,
a: 0,
};
static EFFECTS_PNG_NAME: &'static str = "demo-effects";
static OPEN_PNG_NAME: &'static str = "demo-open";
@ -98,7 +113,10 @@ impl DemoUIModel {
}
}
pub struct DemoUIPresenter<D> where D: Device {
pub struct DemoUIPresenter<D>
where
D: Device,
{
effects_texture: D::Texture,
open_texture: D::Texture,
rotate_texture: D::Texture,
@ -114,34 +132,29 @@ pub struct DemoUIPresenter<D> where D: Device {
rotate_panel_visible: bool,
}
impl<D> DemoUIPresenter<D> where D: Device {
impl<D> DemoUIPresenter<D>
where
D: Device,
{
pub fn new(device: &D, resources: &dyn ResourceLoader) -> DemoUIPresenter<D> {
device.begin_commands();
let effects_texture = device.create_texture_from_png(resources,
EFFECTS_PNG_NAME,
TextureFormat::R8);
let open_texture = device.create_texture_from_png(resources,
OPEN_PNG_NAME,
TextureFormat::R8);
let rotate_texture = device.create_texture_from_png(resources,
ROTATE_PNG_NAME,
TextureFormat::R8);
let zoom_in_texture = device.create_texture_from_png(resources,
ZOOM_IN_PNG_NAME,
TextureFormat::R8);
let zoom_actual_size_texture = device.create_texture_from_png(resources,
ZOOM_ACTUAL_SIZE_PNG_NAME,
TextureFormat::R8);
let zoom_out_texture = device.create_texture_from_png(resources,
ZOOM_OUT_PNG_NAME,
TextureFormat::R8);
let background_texture = device.create_texture_from_png(resources,
BACKGROUND_PNG_NAME,
TextureFormat::R8);
let screenshot_texture = device.create_texture_from_png(resources,
SCREENSHOT_PNG_NAME,
TextureFormat::R8);
let effects_texture =
device.create_texture_from_png(resources, EFFECTS_PNG_NAME, TextureFormat::R8);
let open_texture =
device.create_texture_from_png(resources, OPEN_PNG_NAME, TextureFormat::R8);
let rotate_texture =
device.create_texture_from_png(resources, ROTATE_PNG_NAME, TextureFormat::R8);
let zoom_in_texture =
device.create_texture_from_png(resources, ZOOM_IN_PNG_NAME, TextureFormat::R8);
let zoom_actual_size_texture =
device.create_texture_from_png(resources, ZOOM_ACTUAL_SIZE_PNG_NAME, TextureFormat::R8);
let zoom_out_texture =
device.create_texture_from_png(resources, ZOOM_OUT_PNG_NAME, TextureFormat::R8);
let background_texture =
device.create_texture_from_png(resources, BACKGROUND_PNG_NAME, TextureFormat::R8);
let screenshot_texture =
device.create_texture_from_png(resources, SCREENSHOT_PNG_NAME, TextureFormat::R8);
device.end_commands();
@ -162,14 +175,17 @@ impl<D> DemoUIPresenter<D> where D: Device {
}
}
pub fn update<W>(&mut self,
device: &D,
allocator: &mut GPUMemoryAllocator<D>,
window: &mut W,
debug_ui_presenter: &mut DebugUIPresenter<D>,
action: &mut UIAction,
model: &mut DemoUIModel)
where W: Window {
pub fn update<W>(
&mut self,
device: &D,
allocator: &mut GPUMemoryAllocator<D>,
window: &mut W,
debug_ui_presenter: &mut DebugUIPresenter<D>,
action: &mut UIAction,
model: &mut DemoUIModel,
) where
W: Window,
{
// Draw message text.
self.draw_message_text(device, allocator, debug_ui_presenter, model);
@ -182,34 +198,50 @@ impl<D> DemoUIPresenter<D> where D: Device {
let button_size = vec2i(BUTTON_WIDTH, BUTTON_HEIGHT);
// Draw effects button.
if debug_ui_presenter.ui_presenter
.draw_button(device, allocator, position, &self.effects_texture) {
if debug_ui_presenter.ui_presenter.draw_button(
device,
allocator,
position,
&self.effects_texture,
) {
self.effects_panel_visible = !self.effects_panel_visible;
}
if !self.effects_panel_visible {
debug_ui_presenter.ui_presenter.draw_tooltip(device,
allocator,
"Effects",
RectI::new(position, button_size));
debug_ui_presenter.ui_presenter.draw_tooltip(
device,
allocator,
"Effects",
RectI::new(position, button_size),
);
}
position += vec2i(button_size.x() + PADDING, 0);
// Draw open button.
if debug_ui_presenter.ui_presenter
.draw_button(device, allocator, position, &self.open_texture) {
if debug_ui_presenter.ui_presenter.draw_button(
device,
allocator,
position,
&self.open_texture,
) {
// FIXME(pcwalton): This is not sufficient for Android, where we will need to take in
// the contents of the file.
window.present_open_svg_dialog();
}
debug_ui_presenter.ui_presenter.draw_tooltip(device,
allocator,
"Open SVG",
RectI::new(position, button_size));
debug_ui_presenter.ui_presenter.draw_tooltip(
device,
allocator,
"Open SVG",
RectI::new(position, button_size),
);
position += vec2i(BUTTON_WIDTH + PADDING, 0);
// Draw screenshot button.
if debug_ui_presenter.ui_presenter
.draw_button(device, allocator, position, &self.screenshot_texture) {
if debug_ui_presenter.ui_presenter.draw_button(
device,
allocator,
position,
&self.screenshot_texture,
) {
self.screenshot_panel_visible = !self.screenshot_panel_visible;
}
if !self.screenshot_panel_visible {
@ -222,20 +254,24 @@ impl<D> DemoUIPresenter<D> where D: Device {
}
// Draw screenshot panel, if necessary.
self.draw_screenshot_panel(device,
allocator,
window,
debug_ui_presenter,
position.x(),
action);
self.draw_screenshot_panel(
device,
allocator,
window,
debug_ui_presenter,
position.x(),
action,
);
position += vec2i(button_size.x() + PADDING, 0);
// Draw mode switch.
let new_mode = debug_ui_presenter.ui_presenter.draw_text_switch(device,
allocator,
position,
&["2D", "3D", "VR"],
model.mode as u8);
let new_mode = debug_ui_presenter.ui_presenter.draw_text_switch(
device,
allocator,
position,
&["2D", "3D", "VR"],
model.mode as u8,
);
if new_mode != model.mode as u8 {
model.mode = match new_mode {
0 => Mode::TwoD,
@ -256,10 +292,12 @@ impl<D> DemoUIPresenter<D> where D: Device {
position += vec2i(mode_switch_width + PADDING, 0);
// Draw background switch.
if debug_ui_presenter.ui_presenter.draw_button(device,
allocator,
position,
&self.background_texture) {
if debug_ui_presenter.ui_presenter.draw_button(
device,
allocator,
position,
&self.background_texture,
) {
self.background_panel_visible = !self.background_panel_visible;
}
if !self.background_panel_visible {
@ -272,12 +310,14 @@ impl<D> DemoUIPresenter<D> where D: Device {
}
// Draw background panel, if necessary.
self.draw_background_panel(device,
allocator,
debug_ui_presenter,
position.x(),
action,
model);
self.draw_background_panel(
device,
allocator,
debug_ui_presenter,
position.x(),
action,
model,
);
position += vec2i(button_size.x() + PADDING, 0);
// Draw effects panel, if necessary.
@ -288,49 +328,65 @@ impl<D> DemoUIPresenter<D> where D: Device {
return;
}
if debug_ui_presenter.ui_presenter.draw_button(device,
allocator,
position,
&self.rotate_texture) {
if debug_ui_presenter.ui_presenter.draw_button(
device,
allocator,
position,
&self.rotate_texture,
) {
self.rotate_panel_visible = !self.rotate_panel_visible;
}
if !self.rotate_panel_visible {
debug_ui_presenter.ui_presenter.draw_tooltip(device,
allocator,
"Rotate",
RectI::new(position, button_size));
debug_ui_presenter.ui_presenter.draw_tooltip(
device,
allocator,
"Rotate",
RectI::new(position, button_size),
);
}
self.draw_rotate_panel(device, allocator, debug_ui_presenter, position.x(), action, model);
self.draw_rotate_panel(
device,
allocator,
debug_ui_presenter,
position.x(),
action,
model,
);
position += vec2i(BUTTON_WIDTH + PADDING, 0);
// Draw zoom control.
self.draw_zoom_control(device, allocator, debug_ui_presenter, position, action);
}
fn draw_zoom_control(&mut self,
device: &D,
allocator: &mut GPUMemoryAllocator<D>,
debug_ui_presenter: &mut DebugUIPresenter<D>,
position: Vector2I,
action: &mut UIAction) {
fn draw_zoom_control(
&mut self,
device: &D,
allocator: &mut GPUMemoryAllocator<D>,
debug_ui_presenter: &mut DebugUIPresenter<D>,
position: Vector2I,
action: &mut UIAction,
) {
let zoom_segmented_control_width =
debug_ui_presenter.ui_presenter.measure_segmented_control(3);
let zoom_segmented_control_rect =
RectI::new(position, vec2i(zoom_segmented_control_width, BUTTON_HEIGHT));
debug_ui_presenter.ui_presenter
.draw_tooltip(device, allocator, "Zoom", zoom_segmented_control_rect);
debug_ui_presenter.ui_presenter.draw_tooltip(
device,
allocator,
"Zoom",
zoom_segmented_control_rect,
);
let zoom_textures = &[
&self.zoom_in_texture,
&self.zoom_actual_size_texture,
&self.zoom_out_texture
&self.zoom_out_texture,
];
match debug_ui_presenter.ui_presenter.draw_image_segmented_control(device,
allocator,
position,
zoom_textures,
None) {
match debug_ui_presenter
.ui_presenter
.draw_image_segmented_control(device, allocator, position, zoom_textures, None)
{
Some(0) => *action = UIAction::ZoomIn,
Some(1) => *action = UIAction::ZoomActualSize,
Some(2) => *action = UIAction::ZoomOut,
@ -338,11 +394,13 @@ impl<D> DemoUIPresenter<D> where D: Device {
}
}
fn draw_message_text(&mut self,
device: &D,
allocator: &mut GPUMemoryAllocator<D>,
debug_ui_presenter: &mut DebugUIPresenter<D>,
model: &mut DemoUIModel) {
fn draw_message_text(
&mut self,
device: &D,
allocator: &mut GPUMemoryAllocator<D>,
debug_ui_presenter: &mut DebugUIPresenter<D>,
model: &mut DemoUIModel,
) {
if model.message.is_empty() {
return;
}
@ -365,12 +423,14 @@ impl<D> DemoUIPresenter<D> where D: Device {
);
}
fn draw_effects_panel(&mut self,
device: &D,
allocator: &mut GPUMemoryAllocator<D>,
debug_ui_presenter: &mut DebugUIPresenter<D>,
model: &mut DemoUIModel,
action: &mut UIAction) {
fn draw_effects_panel(
&mut self,
device: &D,
allocator: &mut GPUMemoryAllocator<D>,
debug_ui_presenter: &mut DebugUIPresenter<D>,
model: &mut DemoUIModel,
action: &mut UIAction,
) {
if !self.effects_panel_visible {
return;
}
@ -380,44 +440,56 @@ impl<D> DemoUIPresenter<D> where D: Device {
debug_ui_presenter.ui_presenter.draw_solid_rounded_rect(
device,
allocator,
RectI::new(vec2i(PADDING, effects_panel_y),
vec2i(EFFECTS_PANEL_WIDTH, EFFECTS_PANEL_HEIGHT)),
WINDOW_COLOR);
RectI::new(
vec2i(PADDING, effects_panel_y),
vec2i(EFFECTS_PANEL_WIDTH, EFFECTS_PANEL_HEIGHT),
),
WINDOW_COLOR,
);
self.draw_effects_switch(device,
allocator,
action,
debug_ui_presenter,
"Gamma Correction",
0,
effects_panel_y,
&mut model.gamma_correction_effect_enabled);
self.draw_effects_switch(device,
allocator,
action,
debug_ui_presenter,
"Stem Darkening",
1,
effects_panel_y,
&mut model.stem_darkening_effect_enabled);
self.draw_effects_switch(device,
allocator,
action,
debug_ui_presenter,
"Subpixel AA",
2,
effects_panel_y,
&mut model.subpixel_aa_effect_enabled);
self.draw_effects_switch(
device,
allocator,
action,
debug_ui_presenter,
"Gamma Correction",
0,
effects_panel_y,
&mut model.gamma_correction_effect_enabled,
);
self.draw_effects_switch(
device,
allocator,
action,
debug_ui_presenter,
"Stem Darkening",
1,
effects_panel_y,
&mut model.stem_darkening_effect_enabled,
);
self.draw_effects_switch(
device,
allocator,
action,
debug_ui_presenter,
"Subpixel AA",
2,
effects_panel_y,
&mut model.subpixel_aa_effect_enabled,
);
}
fn draw_screenshot_panel<W>(&mut self,
device: &D,
allocator: &mut GPUMemoryAllocator<D>,
window: &mut W,
debug_ui_presenter: &mut DebugUIPresenter<D>,
panel_x: i32,
action: &mut UIAction)
where W: Window {
fn draw_screenshot_panel<W>(
&mut self,
device: &D,
allocator: &mut GPUMemoryAllocator<D>,
window: &mut W,
debug_ui_presenter: &mut DebugUIPresenter<D>,
panel_x: i32,
action: &mut UIAction,
) where
W: Window,
{
if !self.screenshot_panel_visible {
return;
}
@ -428,33 +500,42 @@ impl<D> DemoUIPresenter<D> where D: Device {
debug_ui_presenter.ui_presenter.draw_solid_rounded_rect(
device,
allocator,
RectI::new(panel_position, vec2i(SCREENSHOT_PANEL_WIDTH, SCREENSHOT_PANEL_HEIGHT)),
RectI::new(
panel_position,
vec2i(SCREENSHOT_PANEL_WIDTH, SCREENSHOT_PANEL_HEIGHT),
),
WINDOW_COLOR,
);
self.draw_screenshot_menu_item(device,
allocator,
window,
debug_ui_presenter,
ScreenshotType::PNG,
panel_position,
action);
self.draw_screenshot_menu_item(device,
allocator,
window,
debug_ui_presenter,
ScreenshotType::SVG,
panel_position,
action);
self.draw_screenshot_menu_item(
device,
allocator,
window,
debug_ui_presenter,
ScreenshotType::PNG,
panel_position,
action,
);
self.draw_screenshot_menu_item(
device,
allocator,
window,
debug_ui_presenter,
ScreenshotType::SVG,
panel_position,
action,
);
}
fn draw_background_panel(&mut self,
device: &D,
allocator: &mut GPUMemoryAllocator<D>,
debug_ui_presenter: &mut DebugUIPresenter<D>,
panel_x: i32,
action: &mut UIAction,
model: &mut DemoUIModel) {
fn draw_background_panel(
&mut self,
device: &D,
allocator: &mut GPUMemoryAllocator<D>,
debug_ui_presenter: &mut DebugUIPresenter<D>,
panel_x: i32,
action: &mut UIAction,
model: &mut DemoUIModel,
) {
if !self.background_panel_visible {
return;
}
@ -465,40 +546,51 @@ impl<D> DemoUIPresenter<D> where D: Device {
debug_ui_presenter.ui_presenter.draw_solid_rounded_rect(
device,
allocator,
RectI::new(panel_position, vec2i(BACKGROUND_PANEL_WIDTH, BACKGROUND_PANEL_HEIGHT)),
RectI::new(
panel_position,
vec2i(BACKGROUND_PANEL_WIDTH, BACKGROUND_PANEL_HEIGHT),
),
WINDOW_COLOR,
);
self.draw_background_menu_item(device,
allocator,
debug_ui_presenter,
BackgroundColor::Light,
panel_position,
action,
model);
self.draw_background_menu_item(device,
allocator,
debug_ui_presenter,
BackgroundColor::Dark,
panel_position,
action,
model);
self.draw_background_menu_item(device,
allocator,
debug_ui_presenter,
BackgroundColor::Transparent,
panel_position,
action,
model);
self.draw_background_menu_item(
device,
allocator,
debug_ui_presenter,
BackgroundColor::Light,
panel_position,
action,
model,
);
self.draw_background_menu_item(
device,
allocator,
debug_ui_presenter,
BackgroundColor::Dark,
panel_position,
action,
model,
);
self.draw_background_menu_item(
device,
allocator,
debug_ui_presenter,
BackgroundColor::Transparent,
panel_position,
action,
model,
);
}
fn draw_rotate_panel(&mut self,
device: &D,
allocator: &mut GPUMemoryAllocator<D>,
debug_ui_presenter: &mut DebugUIPresenter<D>,
rotate_panel_x: i32,
action: &mut UIAction,
model: &mut DemoUIModel) {
fn draw_rotate_panel(
&mut self,
device: &D,
allocator: &mut GPUMemoryAllocator<D>,
debug_ui_presenter: &mut DebugUIPresenter<D>,
rotate_panel_x: i32,
action: &mut UIAction,
model: &mut DemoUIModel,
) {
if !self.rotate_panel_visible {
return;
}
@ -511,42 +603,61 @@ impl<D> DemoUIPresenter<D> where D: Device {
device,
allocator,
RectI::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_rect = RectI::new(vec2i(widget_x, widget_y),
vec2i(SLIDER_WIDTH, SLIDER_KNOB_HEIGHT));
let widget_rect = RectI::new(
vec2i(widget_x, widget_y),
vec2i(SLIDER_WIDTH, SLIDER_KNOB_HEIGHT),
);
if let Some(position) = debug_ui_presenter
.ui_presenter
.event_queue
.handle_mouse_down_or_dragged_in_rect(widget_rect) {
.handle_mouse_down_or_dragged_in_rect(widget_rect)
{
model.rotation = position.x();
*action = UIAction::Rotate(model.rotation());
}
let slider_track_y =
rotate_panel_y + PADDING + SLIDER_KNOB_HEIGHT / 2 - SLIDER_TRACK_HEIGHT / 2;
let slider_track_rect = RectI::new(vec2i(widget_x, slider_track_y),
vec2i(SLIDER_WIDTH, SLIDER_TRACK_HEIGHT));
debug_ui_presenter.ui_presenter
.draw_rect_outline(device, allocator, slider_track_rect, TEXT_COLOR);
let slider_track_rect = RectI::new(
vec2i(widget_x, slider_track_y),
vec2i(SLIDER_WIDTH, SLIDER_TRACK_HEIGHT),
);
debug_ui_presenter.ui_presenter.draw_rect_outline(
device,
allocator,
slider_track_rect,
TEXT_COLOR,
);
let slider_knob_x = widget_x + model.rotation - SLIDER_KNOB_WIDTH / 2;
let slider_knob_rect = RectI::new(vec2i(slider_knob_x, widget_y),
vec2i(SLIDER_KNOB_WIDTH, SLIDER_KNOB_HEIGHT));
debug_ui_presenter.ui_presenter
.draw_solid_rect(device, allocator, slider_knob_rect, TEXT_COLOR);
let slider_knob_rect = RectI::new(
vec2i(slider_knob_x, widget_y),
vec2i(SLIDER_KNOB_WIDTH, SLIDER_KNOB_HEIGHT),
);
debug_ui_presenter.ui_presenter.draw_solid_rect(
device,
allocator,
slider_knob_rect,
TEXT_COLOR,
);
}
fn draw_screenshot_menu_item<W>(&mut self,
device: &D,
allocator: &mut GPUMemoryAllocator<D>,
window: &mut W,
debug_ui_presenter: &mut DebugUIPresenter<D>,
screenshot_type: ScreenshotType,
panel_position: Vector2I,
action: &mut UIAction)
where W: Window {
fn draw_screenshot_menu_item<W>(
&mut self,
device: &D,
allocator: &mut GPUMemoryAllocator<D>,
window: &mut W,
debug_ui_presenter: &mut DebugUIPresenter<D>,
screenshot_type: ScreenshotType,
panel_position: Vector2I,
action: &mut UIAction,
) where
W: Window,
{
let index = screenshot_type as i32;
let text = format!("Save as {}...", screenshot_type.as_str());
@ -554,29 +665,36 @@ impl<D> DemoUIPresenter<D> where D: Device {
let widget_origin = panel_position + vec2i(0, widget_size.y() * index);
let widget_rect = RectI::new(widget_origin, widget_size);
if self.draw_menu_item(device,
allocator,
debug_ui_presenter,
&text,
widget_rect,
false) {
if self.draw_menu_item(
device,
allocator,
debug_ui_presenter,
&text,
widget_rect,
false,
) {
// FIXME(pcwalton): This is not sufficient for Android, where we will need to take in
// the contents of the file.
if let Ok(path) = window.run_save_dialog(screenshot_type.extension()) {
self.screenshot_panel_visible = false;
*action = UIAction::TakeScreenshot(ScreenshotInfo { kind: screenshot_type, path });
*action = UIAction::TakeScreenshot(ScreenshotInfo {
kind: screenshot_type,
path,
});
}
}
}
fn draw_background_menu_item(&mut self,
device: &D,
allocator: &mut GPUMemoryAllocator<D>,
debug_ui_presenter: &mut DebugUIPresenter<D>,
color: BackgroundColor,
panel_position: Vector2I,
action: &mut UIAction,
model: &mut DemoUIModel) {
fn draw_background_menu_item(
&mut self,
device: &D,
allocator: &mut GPUMemoryAllocator<D>,
debug_ui_presenter: &mut DebugUIPresenter<D>,
color: BackgroundColor,
panel_position: Vector2I,
action: &mut UIAction,
model: &mut DemoUIModel,
) {
let (text, index) = (color.as_str(), color as i32);
let widget_size = vec2i(BACKGROUND_PANEL_WIDTH, BUTTON_HEIGHT);
@ -584,66 +702,83 @@ impl<D> DemoUIPresenter<D> where D: Device {
let widget_rect = RectI::new(widget_origin, widget_size);
let selected = color == model.background_color;
if self.draw_menu_item(device,
allocator,
debug_ui_presenter,
text,
widget_rect,
selected) {
if self.draw_menu_item(
device,
allocator,
debug_ui_presenter,
text,
widget_rect,
selected,
) {
model.background_color = color;
*action = UIAction::ModelChanged;
}
}
fn draw_menu_item(&self,
device: &D,
allocator: &mut GPUMemoryAllocator<D>,
debug_ui_presenter: &mut DebugUIPresenter<D>,
text: &str,
widget_rect: RectI,
selected: bool)
-> bool {
fn draw_menu_item(
&self,
device: &D,
allocator: &mut GPUMemoryAllocator<D>,
debug_ui_presenter: &mut DebugUIPresenter<D>,
text: &str,
widget_rect: RectI,
selected: bool,
) -> bool {
if selected {
debug_ui_presenter.ui_presenter
.draw_solid_rounded_rect(device, allocator, widget_rect, TEXT_COLOR);
debug_ui_presenter.ui_presenter.draw_solid_rounded_rect(
device,
allocator,
widget_rect,
TEXT_COLOR,
);
}
let (text_x, text_y) = (PADDING * 2, BUTTON_TEXT_OFFSET);
let text_position = widget_rect.origin() + vec2i(text_x, text_y);
debug_ui_presenter.ui_presenter
.draw_text(device, allocator, text, text_position, selected);
debug_ui_presenter
.ui_presenter
.draw_text(device, allocator, text, text_position, selected);
debug_ui_presenter.ui_presenter
.event_queue
.handle_mouse_down_in_rect(widget_rect)
.is_some()
debug_ui_presenter
.ui_presenter
.event_queue
.handle_mouse_down_in_rect(widget_rect)
.is_some()
}
fn draw_effects_switch(&self,
device: &D,
allocator: &mut GPUMemoryAllocator<D>,
action: &mut UIAction,
debug_ui_presenter: &mut DebugUIPresenter<D>,
text: &str,
index: i32,
window_y: i32,
value: &mut bool) {
fn draw_effects_switch(
&self,
device: &D,
allocator: &mut GPUMemoryAllocator<D>,
action: &mut UIAction,
debug_ui_presenter: &mut DebugUIPresenter<D>,
text: &str,
index: i32,
window_y: i32,
value: &mut bool,
) {
let text_x = PADDING * 2;
let text_y = window_y + PADDING + BUTTON_TEXT_OFFSET + (BUTTON_HEIGHT + PADDING) * index;
debug_ui_presenter.ui_presenter
.draw_text(device, allocator, text, vec2i(text_x, text_y), false);
debug_ui_presenter.ui_presenter.draw_text(
device,
allocator,
text,
vec2i(text_x, text_y),
false,
);
let switch_width = debug_ui_presenter.ui_presenter.measure_segmented_control(2);
let switch_x = PADDING + EFFECTS_PANEL_WIDTH - (switch_width + PADDING);
let switch_y = window_y + PADDING + (BUTTON_HEIGHT + PADDING) * index;
let switch_position = vec2i(switch_x, switch_y);
let new_value = debug_ui_presenter.ui_presenter
.draw_text_switch(device,
allocator,
switch_position,
&["Off", "On"],
*value as u8) != 0;
let new_value = debug_ui_presenter.ui_presenter.draw_text_switch(
device,
allocator,
switch_position,
&["Off", "On"],
*value as u8,
) != 0;
if new_value != *value {
*action = UIAction::EffectsChanged;

View File

@ -33,7 +33,9 @@ pub trait Window {
#[cfg(any(not(target_os = "macos"), feature = "pf-gl"))]
fn gl_version(&self) -> GLVersion;
#[cfg(any(not(target_os = "macos"), feature = "pf-gl"))]
fn gl_default_framebuffer(&self) -> GLuint { 0 }
fn gl_default_framebuffer(&self) -> GLuint {
0
}
#[cfg(any(not(target_os = "macos"), feature = "pf-gl"))]
fn present(&mut self, device: &mut GLDevice);

View File

@ -7,7 +7,7 @@ authors = ["Alan Jeffrey <ajeffrey@mozilla.com>"]
[dependencies]
gl = "0.14"
rayon = "1.0"
usvg = "0.9"
usvg = "0.10"
egl = "0.2"
log = "0.4"
smallvec = "1.2"

View File

@ -263,27 +263,51 @@ impl MLResult {
}
}
impl Error for MLResult {
}
impl Error for MLResult {}
// Functions from the MagicLeap C API
#[cfg(not(feature = "mocked"))]
extern "C" {
pub fn MLGraphicsCreateClientGL(options: *const MLGraphicsOptions, gl_context: MLHandle, graphics_client : &mut MLHandle) -> MLResult;
pub fn MLGraphicsCreateClientGL(
options: *const MLGraphicsOptions,
gl_context: MLHandle,
graphics_client: &mut MLHandle,
) -> MLResult;
pub fn MLGraphicsDestroyClient(graphics_client: *mut MLHandle) -> MLResult;
pub fn MLHeadTrackingCreate(tracker: *mut MLHandle) -> MLResult;
pub fn MLHeadTrackingGetStaticData(head_tracker: MLHandle, data: *mut MLHeadTrackingStaticData) -> MLResult;
pub fn MLHeadTrackingGetStaticData(
head_tracker: MLHandle,
data: *mut MLHeadTrackingStaticData,
) -> MLResult;
pub fn MLPerceptionGetSnapshot(snapshot: *mut MLSnapshotPtr) -> MLResult;
pub fn MLSnapshotGetTransform(snapshot: MLSnapshotPtr, id: *const MLCoordinateFrameUID, transform: *mut MLTransform) -> MLResult;
pub fn MLSnapshotGetTransform(
snapshot: MLSnapshotPtr,
id: *const MLCoordinateFrameUID,
transform: *mut MLTransform,
) -> MLResult;
pub fn MLPerceptionReleaseSnapshot(snapshot: MLSnapshotPtr) -> MLResult;
pub fn MLLifecycleSetReadyIndication() -> MLResult;
pub fn MLGraphicsGetClipExtents(graphics_client: MLHandle, array: *mut MLGraphicsClipExtentsInfoArray) -> MLResult;
pub fn MLGraphicsGetRenderTargets(graphics_client: MLHandle, targets: *mut MLGraphicsRenderTargetsInfo) -> MLResult;
pub fn MLGraphicsGetClipExtents(
graphics_client: MLHandle,
array: *mut MLGraphicsClipExtentsInfoArray,
) -> MLResult;
pub fn MLGraphicsGetRenderTargets(
graphics_client: MLHandle,
targets: *mut MLGraphicsRenderTargetsInfo,
) -> MLResult;
pub fn MLGraphicsInitFrameParams(params: *mut MLGraphicsFrameParams) -> MLResult;
pub fn MLGraphicsBeginFrame(graphics_client: MLHandle, params: *const MLGraphicsFrameParams, frame_handle: *mut MLHandle, virtual_camera_array: *mut MLGraphicsVirtualCameraInfoArray) -> MLResult;
pub fn MLGraphicsBeginFrame(
graphics_client: MLHandle,
params: *const MLGraphicsFrameParams,
frame_handle: *mut MLHandle,
virtual_camera_array: *mut MLGraphicsVirtualCameraInfoArray,
) -> MLResult;
pub fn MLGraphicsEndFrame(graphics_client: MLHandle, frame_handle: MLHandle) -> MLResult;
pub fn MLGraphicsSignalSyncObjectGL(graphics_client: MLHandle, sync_object: MLHandle) -> MLResult;
pub fn MLGraphicsSignalSyncObjectGL(
graphics_client: MLHandle,
sync_object: MLHandle,
) -> MLResult;
pub fn MLGetResultString(result_code: MLResult) -> *const c_char;
pub fn MLLoggingLogLevelIsEnabled(lvl: MLLogLevel) -> bool;
pub fn MLLoggingLog(lvl: MLLogLevel, tag: *const c_char, message: *const c_char);

View File

@ -22,31 +22,31 @@ use gl::types::GLuint;
use log::info;
use pathfinder_demo::DemoApp;
use pathfinder_demo::Options;
use pathfinder_demo::UIVisibility;
use pathfinder_demo::BackgroundColor;
use pathfinder_demo::Mode;
use pathfinder_color::ColorF;
use pathfinder_demo::window::Event;
use pathfinder_demo::window::SVGPath;
use pathfinder_demo::BackgroundColor;
use pathfinder_demo::DemoApp;
use pathfinder_demo::Mode;
use pathfinder_demo::Options;
use pathfinder_demo::UIVisibility;
use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::vector::vec2i;
use pathfinder_geometry::vector::Vector2F;
use pathfinder_geometry::vector::Vector2I;
use pathfinder_geometry::vector::vec2i;
use pathfinder_color::ColorF;
use pathfinder_gl::GLDevice;
use pathfinder_gl::GLVersion;
use pathfinder_gpu::ClearParams;
use pathfinder_gpu::Device;
use pathfinder_renderer::concurrent::executor::SequentialExecutor;
use pathfinder_renderer::concurrent::scene_proxy::SceneProxy;
use pathfinder_renderer::gpu::renderer::Renderer;
use pathfinder_renderer::gpu::renderer::DestFramebuffer;
use pathfinder_renderer::gpu::renderer::Renderer;
use pathfinder_renderer::options::RenderOptions;
use pathfinder_renderer::options::RenderTransform;
use pathfinder_resources::ResourceLoader;
use pathfinder_resources::fs::FilesystemResourceLoader;
use pathfinder_resources::ResourceLoader;
use pathfinder_simd::default::F32x4;
use pathfinder_svg::SVGScene;
@ -72,10 +72,17 @@ struct ImmersiveApp {
}
#[no_mangle]
pub extern "C" fn magicleap_pathfinder_demo_init(egl_display: EGLDisplay, egl_context: EGLContext) -> *mut c_void {
unsafe { c_api::MLLoggingLog(c_api::MLLogLevel::Info,
b"Pathfinder Demo\0".as_ptr() as *const _,
b"Initializing\0".as_ptr() as *const _) };
pub extern "C" fn magicleap_pathfinder_demo_init(
egl_display: EGLDisplay,
egl_context: EGLContext,
) -> *mut c_void {
unsafe {
c_api::MLLoggingLog(
c_api::MLLogLevel::Info,
b"Pathfinder Demo\0".as_ptr() as *const _,
b"Initializing\0".as_ptr() as *const _,
)
};
let tag = CString::new("Pathfinder Demo").unwrap();
let level = log::LevelFilter::Warn;
@ -97,7 +104,11 @@ pub extern "C" fn magicleap_pathfinder_demo_init(egl_display: EGLDisplay, egl_co
info!("Initialized app");
let (sender, receiver) = flume::unbounded();
Box::into_raw(Box::new(ImmersiveApp { sender, receiver, demo })) as *mut c_void
Box::into_raw(Box::new(ImmersiveApp {
sender,
receiver,
demo,
})) as *mut c_void
}
#[no_mangle]
@ -124,12 +135,17 @@ pub unsafe extern "C" fn magicleap_pathfinder_demo_run(app: *mut c_void) {
}
#[no_mangle]
pub unsafe extern "C" fn magicleap_pathfinder_demo_load(app: *mut c_void, svg_filename: *const c_char) {
pub unsafe extern "C" fn magicleap_pathfinder_demo_load(
app: *mut c_void,
svg_filename: *const c_char,
) {
let app = app as *mut ImmersiveApp;
if let Some(app) = app.as_mut() {
let svg_filename = CStr::from_ptr(svg_filename).to_string_lossy().into_owned();
info!("Loading {}.", svg_filename);
let _ = app.sender.send(Event::OpenSVG(SVGPath::Resource(svg_filename)));
let _ = app
.sender
.send(Event::OpenSVG(SVGPath::Resource(svg_filename)));
}
}
@ -150,9 +166,13 @@ pub struct MagicLeapPathfinderRenderOptions {
#[no_mangle]
pub extern "C" fn magicleap_pathfinder_init() -> *mut c_void {
unsafe { c_api::MLLoggingLog(c_api::MLLogLevel::Info,
b"Pathfinder Demo\0".as_ptr() as *const _,
b"Initializing\0".as_ptr() as *const _) };
unsafe {
c_api::MLLoggingLog(
c_api::MLLogLevel::Info,
b"Pathfinder Demo\0".as_ptr() as *const _,
b"Initializing\0".as_ptr() as *const _,
)
};
let tag = CString::new("Pathfinder Demo").unwrap();
let level = log::LevelFilter::Info;
@ -175,12 +195,17 @@ pub extern "C" fn magicleap_pathfinder_init() -> *mut c_void {
}
#[no_mangle]
pub unsafe extern "C" fn magicleap_pathfinder_render(pf: *mut c_void, options: *const MagicLeapPathfinderRenderOptions) {
pub unsafe extern "C" fn magicleap_pathfinder_render(
pf: *mut c_void,
options: *const MagicLeapPathfinderRenderOptions,
) {
let pf = pf as *mut MagicLeapPathfinder;
if let (Some(pf), Some(options)) = (pf.as_mut(), options.as_ref()) {
let resources = &pf.resources;
let svg_filename = CStr::from_ptr(options.svg_filename).to_string_lossy().into_owned();
let svg_filename = CStr::from_ptr(options.svg_filename)
.to_string_lossy()
.into_owned();
let svg = pf.svgs.entry(svg_filename).or_insert_with(|| {
let svg_filename = CStr::from_ptr(options.svg_filename).to_string_lossy();
let data = resources.slurp(&*svg_filename).unwrap();
@ -191,35 +216,56 @@ pub unsafe extern "C" fn magicleap_pathfinder_render(pf: *mut c_void, options: *
let mut width = 0;
let mut height = 0;
egl::query_surface(options.display, options.surface, egl::EGL_WIDTH, &mut width);
egl::query_surface(options.display, options.surface, egl::EGL_HEIGHT, &mut height);
egl::query_surface(
options.display,
options.surface,
egl::EGL_HEIGHT,
&mut height,
);
let size = vec2i(width, height);
let viewport_origin = vec2i(options.viewport[0] as i32, options.viewport[1] as i32);
let viewport_size = vec2i(options.viewport[2] as i32, options.viewport[3] as i32);
let viewport = RectI::new(viewport_origin, viewport_size);
let bg_color = ColorF(F32x4::new(options.bg_color[0], options.bg_color[1], options.bg_color[2], options.bg_color[3]));
let bg_color = ColorF(F32x4::new(
options.bg_color[0],
options.bg_color[1],
options.bg_color[2],
options.bg_color[3],
));
let renderer = pf.renderers.entry((options.display, options.surface)).or_insert_with(|| {
let mut fbo = 0;
gl::GetIntegerv(gl::DRAW_FRAMEBUFFER_BINDING, &mut fbo);
let device = GLDevice::new(GLVersion::GLES3, fbo as GLuint);
let dest_framebuffer = DestFramebuffer::Default { viewport, window_size: size };
Renderer::new(device, resources, dest_framebuffer)
});
let renderer = pf
.renderers
.entry((options.display, options.surface))
.or_insert_with(|| {
let mut fbo = 0;
gl::GetIntegerv(gl::DRAW_FRAMEBUFFER_BINDING, &mut fbo);
let device = GLDevice::new(GLVersion::GLES3, fbo as GLuint);
let dest_framebuffer = DestFramebuffer::Default {
viewport,
window_size: size,
};
Renderer::new(device, resources, dest_framebuffer)
});
renderer.set_main_framebuffer_size(size);
renderer.device.bind_default_framebuffer(viewport);
renderer.device.clear(&ClearParams { color: Some(bg_color), ..ClearParams::default() });
renderer.device.clear(&ClearParams {
color: Some(bg_color),
..ClearParams::default()
});
renderer.disable_depth();
svg.scene.set_view_box(viewport.to_f32());
let scale = i32::min(viewport_size.x(), viewport_size.y()) as f32 /
f32::max(svg.scene.bounds().size().x(), svg.scene.bounds().size().y());
let scale = i32::min(viewport_size.x(), viewport_size.y()) as f32
/ f32::max(svg.scene.bounds().size().x(), svg.scene.bounds().size().y());
let transform = Transform2F::from_translation(svg.scene.bounds().size().scale(-0.5))
.post_mul(&Transform2F::from_scale(scale))
.post_mul(&Transform2F::from_translation(viewport_size.to_f32().scale(0.5)));
.post_mul(&Transform2F::from_translation(
viewport_size.to_f32().scale(0.5),
));
let render_options = RenderOptions {
transform: RenderTransform::Transform2D(transform),

View File

@ -32,9 +32,9 @@ use crate::c_api::ML_RESULT_TIMEOUT;
use crate::c_api::ML_VIRTUAL_CAMERA_COUNT;
use egl;
use egl::EGL_NO_SURFACE;
use egl::EGLContext;
use egl::EGLDisplay;
use egl::EGL_NO_SURFACE;
use gl;
use gl::types::GLuint;
@ -53,12 +53,12 @@ use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::transform3d::Perspective;
use pathfinder_geometry::transform3d::Transform4F;
use pathfinder_geometry::util;
use pathfinder_geometry::vector::Vector2I;
use pathfinder_geometry::vector::Vector2F;
use pathfinder_geometry::vector::vec2i;
use pathfinder_geometry::vector::Vector2F;
use pathfinder_geometry::vector::Vector2I;
use pathfinder_gl::GLVersion;
use pathfinder_resources::ResourceLoader;
use pathfinder_resources::fs::FilesystemResourceLoader;
use pathfinder_resources::ResourceLoader;
use pathfinder_simd::default::F32x4;
use rayon::ThreadPoolBuilder;
@ -99,19 +99,20 @@ impl Window for MagicLeapWindow {
self.framebuffer_id
}
fn adjust_thread_pool_settings(&self, thread_pool_builder: ThreadPoolBuilder) -> ThreadPoolBuilder {
fn adjust_thread_pool_settings(
&self,
thread_pool_builder: ThreadPoolBuilder,
) -> ThreadPoolBuilder {
thread_pool_builder.start_handler(|id| unsafe { init_scene_thread(id) })
}
fn create_user_event_id (&self) -> u32 {
fn create_user_event_id(&self) -> u32 {
0
}
fn push_user_event(_: u32, _: u32) {
}
fn push_user_event(_: u32, _: u32) {}
fn present_open_svg_dialog(&mut self) {
}
fn present_open_svg_dialog(&mut self) {}
fn run_save_dialog(&self, _: &str) -> Result<PathBuf, ()> {
Err(())
@ -125,7 +126,10 @@ impl Window for MagicLeapWindow {
self.begin_frame();
let eye = match view {
View::Stereo(eye) if (eye as usize) < ML_VIRTUAL_CAMERA_COUNT => eye as usize,
_ => { debug!("Asked for unexpected view: {:?}", view); 0 }
_ => {
debug!("Asked for unexpected view: {:?}", view);
0
}
};
debug!("Making {} current.", eye);
let viewport = self.virtual_camera_array.viewport;
@ -135,9 +139,26 @@ impl Window for MagicLeapWindow {
let layer_id = virtual_camera.virtual_camera_name as i32;
unsafe {
gl::BindFramebuffer(gl::FRAMEBUFFER, self.framebuffer_id);
gl::FramebufferTextureLayer(gl::FRAMEBUFFER, gl::COLOR_ATTACHMENT0, color_id, 0, layer_id);
gl::FramebufferTextureLayer(gl::FRAMEBUFFER, gl::DEPTH_ATTACHMENT, depth_id, 0, layer_id);
gl::Viewport(viewport.x as i32, viewport.y as i32, viewport.w as i32, viewport.h as i32);
gl::FramebufferTextureLayer(
gl::FRAMEBUFFER,
gl::COLOR_ATTACHMENT0,
color_id,
0,
layer_id,
);
gl::FramebufferTextureLayer(
gl::FRAMEBUFFER,
gl::DEPTH_ATTACHMENT,
depth_id,
0,
layer_id,
);
gl::Viewport(
viewport.x as i32,
viewport.y as i32,
viewport.w as i32,
viewport.h as i32,
);
}
debug!("Made {} current.", eye);
}
@ -161,7 +182,7 @@ impl MagicLeapWindow {
debug!("Creating MagicLeapWindow");
let mut framebuffer_id = 0;
let graphics_options = MLGraphicsOptions::default();
let mut graphics_client = unsafe { mem::zeroed() };
let mut graphics_client = unsafe { mem::zeroed() };
let mut head_tracker = unsafe { mem::zeroed() };
let mut targets = unsafe { mem::zeroed() };
let virtual_camera_array = unsafe { mem::zeroed() };
@ -175,7 +196,10 @@ impl MagicLeapWindow {
MLHeadTrackingCreate(&mut head_tracker).unwrap();
MLGraphicsGetRenderTargets(graphics_client, &mut targets).unwrap();
}
let (max_width, max_height) = targets.buffers.iter().map(|buffer| buffer.color)
let (max_width, max_height) = targets
.buffers
.iter()
.map(|buffer| buffer.color)
.chain(targets.buffers.iter().map(|buffer| buffer.depth))
.map(|target| (target.width as i32, target.height as i32))
.max()
@ -218,7 +242,12 @@ impl MagicLeapWindow {
unsafe {
gl::BindFramebuffer(gl::FRAMEBUFFER, self.framebuffer_id);
MLGraphicsInitFrameParams(&mut params).unwrap();
let mut result = MLGraphicsBeginFrame(self.graphics_client, &params, &mut self.frame_handle, &mut self.virtual_camera_array);
let mut result = MLGraphicsBeginFrame(
self.graphics_client,
&params,
&mut self.frame_handle,
&mut self.virtual_camera_array,
);
if result == ML_RESULT_TIMEOUT {
info!("PF frame timeout");
let mut sleep = Duration::from_millis(1);
@ -227,7 +256,12 @@ impl MagicLeapWindow {
sleep = (sleep * 2).min(max_sleep);
info!("PF exponential backoff {}ms", sleep.as_millis());
thread::sleep(sleep);
result = MLGraphicsBeginFrame(self.graphics_client, &params, &mut self.frame_handle, &mut self.virtual_camera_array);
result = MLGraphicsBeginFrame(
self.graphics_client,
&params,
&mut self.frame_handle,
&mut self.virtual_camera_array,
);
}
info!("PF frame finished timeout");
}
@ -236,21 +270,26 @@ impl MagicLeapWindow {
let virtual_camera_array = &self.virtual_camera_array;
let initial_camera = self.initial_camera_transform.get_or_insert_with(|| {
let initial_offset = Transform4F::from_translation(0.0, 0.0, 1.0);
let mut camera = virtual_camera_array.virtual_cameras[0].transform;
for i in 1..virtual_camera_array.num_virtual_cameras {
let next = virtual_camera_array.virtual_cameras[i as usize].transform;
camera = camera.lerp(next, 1.0 / (i as f32 + 1.0));
}
Transform4F::from(camera).post_mul(&initial_offset)
let mut camera = virtual_camera_array.virtual_cameras[0].transform;
for i in 1..virtual_camera_array.num_virtual_cameras {
let next = virtual_camera_array.virtual_cameras[i as usize].transform;
camera = camera.lerp(next, 1.0 / (i as f32 + 1.0));
}
Transform4F::from(camera).post_mul(&initial_offset)
});
let camera_transforms = (0..virtual_camera_array.num_virtual_cameras)
.map(|i| {
let camera = &virtual_camera_array.virtual_cameras[i as usize];
let camera = &virtual_camera_array.virtual_cameras[i as usize];
let projection = Transform4F::from(camera.projection);
let size = RectI::from(virtual_camera_array.viewport).size();
let perspective = Perspective::new(&projection, size);
let modelview_to_eye = Transform4F::from(camera.transform).inverse().post_mul(initial_camera);
OcularTransform { perspective, modelview_to_eye }
let modelview_to_eye = Transform4F::from(camera.transform)
.inverse()
.post_mul(initial_camera);
OcularTransform {
perspective,
modelview_to_eye,
}
})
.collect();
self.in_frame = true;
@ -266,7 +305,8 @@ impl MagicLeapWindow {
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
for i in 0..self.virtual_camera_array.num_virtual_cameras {
let virtual_camera = &self.virtual_camera_array.virtual_cameras[i as usize];
MLGraphicsSignalSyncObjectGL(self.graphics_client, virtual_camera.sync_object).unwrap();
MLGraphicsSignalSyncObjectGL(self.graphics_client, virtual_camera.sync_object)
.unwrap();
}
MLGraphicsEndFrame(self.graphics_client, self.frame_handle).unwrap();
}
@ -358,8 +398,7 @@ impl MLTransform {
impl From<MLTransform> for Transform4F {
fn from(mat: MLTransform) -> Self {
Transform4F::from(mat.rotation)
.pre_mul(&Transform4F::from(mat.position))
Transform4F::from(mat.rotation).pre_mul(&Transform4F::from(mat.position))
}
}
@ -390,10 +429,9 @@ impl From<MLQuaternionf> for Transform4F {
impl From<MLMat4f> for Transform4F {
fn from(mat: MLMat4f) -> Self {
let a = mat.matrix_colmajor;
Transform4F::row_major(a[0], a[4], a[8], a[12],
a[1], a[5], a[9], a[13],
a[2], a[6], a[10], a[14],
a[3], a[7], a[11], a[15])
Transform4F::row_major(
a[0], a[4], a[8], a[12], a[1], a[5], a[9], a[13], a[2], a[6], a[10], a[14], a[3], a[7],
a[11], a[15],
)
}
}

View File

@ -28,7 +28,11 @@ use crate::c_api::MLSnapshotPtr;
use crate::c_api::MLTransform;
use std::os::raw::c_char;
pub unsafe fn MLGraphicsCreateClientGL(options: *const MLGraphicsOptions, gl_context: MLHandle, graphics_client : &mut MLHandle) -> MLResult {
pub unsafe fn MLGraphicsCreateClientGL(
options: *const MLGraphicsOptions,
gl_context: MLHandle,
graphics_client: &mut MLHandle,
) -> MLResult {
unimplemented!()
}
@ -40,7 +44,10 @@ pub unsafe fn MLHeadTrackingCreate(tracker: *mut MLHandle) -> MLResult {
unimplemented!()
}
pub unsafe fn MLHeadTrackingGetStaticData(head_tracker: MLHandle, data: *mut MLHeadTrackingStaticData) -> MLResult {
pub unsafe fn MLHeadTrackingGetStaticData(
head_tracker: MLHandle,
data: *mut MLHeadTrackingStaticData,
) -> MLResult {
unimplemented!()
}
@ -48,7 +55,11 @@ pub unsafe fn MLPerceptionGetSnapshot(snapshot: *mut MLSnapshotPtr) -> MLResult
unimplemented!()
}
pub unsafe fn MLSnapshotGetTransform(snapshot: MLSnapshotPtr, id: *const MLCoordinateFrameUID, transform: *mut MLTransform) -> MLResult {
pub unsafe fn MLSnapshotGetTransform(
snapshot: MLSnapshotPtr,
id: *const MLCoordinateFrameUID,
transform: *mut MLTransform,
) -> MLResult {
unimplemented!()
}
@ -60,11 +71,17 @@ pub unsafe fn MLLifecycleSetReadyIndication() -> MLResult {
unimplemented!()
}
pub unsafe fn MLGraphicsGetClipExtents(graphics_client: MLHandle, array: *mut MLGraphicsClipExtentsInfoArray) -> MLResult {
pub unsafe fn MLGraphicsGetClipExtents(
graphics_client: MLHandle,
array: *mut MLGraphicsClipExtentsInfoArray,
) -> MLResult {
unimplemented!()
}
pub unsafe fn MLGraphicsGetRenderTargets(graphics_client: MLHandle, targets: *mut MLGraphicsRenderTargetsInfo) -> MLResult {
pub unsafe fn MLGraphicsGetRenderTargets(
graphics_client: MLHandle,
targets: *mut MLGraphicsRenderTargetsInfo,
) -> MLResult {
unimplemented!()
}
@ -72,7 +89,12 @@ pub unsafe fn MLGraphicsInitFrameParams(params: *mut MLGraphicsFrameParams) -> M
unimplemented!()
}
pub unsafe fn MLGraphicsBeginFrame(graphics_client: MLHandle, params: *const MLGraphicsFrameParams, frame_handle: *mut MLHandle, virtual_camera_array: *mut MLGraphicsVirtualCameraInfoArray) -> MLResult {
pub unsafe fn MLGraphicsBeginFrame(
graphics_client: MLHandle,
params: *const MLGraphicsFrameParams,
frame_handle: *mut MLHandle,
virtual_camera_array: *mut MLGraphicsVirtualCameraInfoArray,
) -> MLResult {
unimplemented!()
}
@ -80,7 +102,10 @@ pub unsafe fn MLGraphicsEndFrame(graphics_client: MLHandle, frame_handle: MLHand
unimplemented!()
}
pub unsafe fn MLGraphicsSignalSyncObjectGL(graphics_client: MLHandle, sync_object: MLHandle) -> MLResult {
pub unsafe fn MLGraphicsSignalSyncObjectGL(
graphics_client: MLHandle,
sync_object: MLHandle,
) -> MLResult {
unimplemented!()
}
@ -95,4 +120,3 @@ pub unsafe fn MLLoggingLogLevelIsEnabled(lvl: MLLogLevel) -> bool {
pub unsafe fn MLLoggingLog(lvl: MLLogLevel, tag: *const c_char, message: *const c_char) {
unimplemented!()
}

View File

@ -18,33 +18,33 @@ extern crate objc;
use euclid::default::Size2D;
use nfd::Response;
use pathfinder_demo::window::{Event, Keycode, DataPath, View, Window, WindowSize};
use pathfinder_demo::window::{DataPath, Event, Keycode, View, Window, WindowSize};
use pathfinder_demo::{DemoApp, Options};
use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::vector::{Vector2I, vec2i};
use pathfinder_resources::ResourceLoader;
use pathfinder_geometry::vector::{vec2i, Vector2I};
use pathfinder_resources::fs::FilesystemResourceLoader;
use pathfinder_resources::ResourceLoader;
use std::cell::Cell;
use std::collections::VecDeque;
use std::path::PathBuf;
use std::sync::Mutex;
use surfman::{SurfaceAccess, SurfaceType, declare_surfman};
use surfman::{declare_surfman, SurfaceAccess, SurfaceType};
use winit::dpi::LogicalSize;
use winit::{ControlFlow, ElementState, Event as WinitEvent, EventsLoop, EventsLoopProxy};
use winit::{MouseButton, VirtualKeyCode, Window as WinitWindow, WindowBuilder, WindowEvent};
use winit::dpi::LogicalSize;
#[cfg(any(not(target_os = "macos"), feature = "pf-gl"))]
use gl::types::GLuint;
#[cfg(any(not(target_os = "macos"), feature = "pf-gl"))]
use gl;
#[cfg(any(not(target_os = "macos"), feature = "pf-gl"))]
use surfman::{Connection, Context, ContextAttributeFlags, ContextAttributes};
#[cfg(any(not(target_os = "macos"), feature = "pf-gl"))]
use surfman::{Device, GLVersion as SurfmanGLVersion};
use gl::types::GLuint;
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
use io_surface::IOSurfaceRef;
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
use pathfinder_metal::MetalDevice;
#[cfg(any(not(target_os = "macos"), feature = "pf-gl"))]
use surfman::{Connection, Context, ContextAttributeFlags, ContextAttributes};
#[cfg(any(not(target_os = "macos"), feature = "pf-gl"))]
use surfman::{Device, GLVersion as SurfmanGLVersion};
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
use surfman::{NativeDevice, SystemConnection, SystemDevice, SystemSurface};
@ -138,7 +138,10 @@ struct EventQueue {
#[derive(Clone)]
enum CustomEvent {
User { message_type: u32, message_data: u32 },
User {
message_type: u32,
message_data: u32,
},
OpenData(PathBuf),
}
@ -155,15 +158,17 @@ impl Window for WindowImpl {
#[cfg(any(not(target_os = "macos"), feature = "pf-gl"))]
fn gl_default_framebuffer(&self) -> GLuint {
self.device.context_surface_info(&self.context).unwrap().unwrap().framebuffer_object
self.device
.context_surface_info(&self.context)
.unwrap()
.unwrap()
.framebuffer_object
}
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
fn metal_device(&self) -> metal::Device {
// FIXME(pcwalton): Remove once `surfman` upgrades `metal-rs` version.
unsafe {
std::mem::transmute(self.metal_device.0.clone())
}
unsafe { std::mem::transmute(self.metal_device.0.clone()) }
}
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
@ -172,7 +177,10 @@ impl Window for WindowImpl {
}
fn viewport(&self, view: View) -> RectI {
let WindowSize { logical_size, backing_scale_factor } = self.size();
let WindowSize {
logical_size,
backing_scale_factor,
} = self.size();
let mut size = (logical_size.to_f32() * backing_scale_factor).to_i32();
let mut x_offset = 0;
if let View::Stereo(index) = view {
@ -192,17 +200,24 @@ impl Window for WindowImpl {
#[cfg(any(not(target_os = "macos"), feature = "pf-gl"))]
fn present(&mut self, _: &mut GLDevice) {
let mut surface = self.device
.unbind_surface_from_context(&mut self.context)
.unwrap()
.unwrap();
self.device.present_surface(&mut self.context, &mut surface).unwrap();
self.device.bind_surface_to_context(&mut self.context, surface).unwrap();
let mut surface = self
.device
.unbind_surface_from_context(&mut self.context)
.unwrap()
.unwrap();
self.device
.present_surface(&mut self.context, &mut surface)
.unwrap();
self.device
.bind_surface_to_context(&mut self.context, surface)
.unwrap();
}
#[cfg(all(target_os = "macos", not(feature = "pf-gl")))]
fn present(&mut self, metal_device: &mut MetalDevice) {
self.device.present_surface(&mut self.surface).expect("Failed to present surface!");
self.device
.present_surface(&mut self.surface)
.expect("Failed to present surface!");
metal_device.swap_texture(self.device.native_surface(&self.surface).0);
}
@ -214,7 +229,9 @@ impl Window for WindowImpl {
if let Ok(Response::Okay(path)) = nfd::open_file_dialog(Some("svg,pdf"), None) {
let mut event_queue = EVENT_QUEUE.lock().unwrap();
let event_queue = event_queue.as_mut().unwrap();
event_queue.pending_custom_events.push_back(CustomEvent::OpenData(PathBuf::from(path)));
event_queue
.pending_custom_events
.push_back(CustomEvent::OpenData(PathBuf::from(path)));
drop(event_queue.event_loop_proxy.wakeup());
}
}
@ -235,10 +252,12 @@ impl Window for WindowImpl {
fn push_user_event(message_type: u32, message_data: u32) {
let mut event_queue = EVENT_QUEUE.lock().unwrap();
let event_queue = event_queue.as_mut().unwrap();
event_queue.pending_custom_events.push_back(CustomEvent::User {
message_type,
message_data,
});
event_queue
.pending_custom_events
.push_back(CustomEvent::User {
message_type,
message_data,
});
drop(event_queue.event_loop_proxy.wakeup());
}
}
@ -249,14 +268,17 @@ impl WindowImpl {
let event_loop = EventsLoop::new();
let window_size = Size2D::new(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT);
let logical_size = LogicalSize::new(window_size.width as f64, window_size.height as f64);
let window = WindowBuilder::new().with_title("Pathfinder Demo")
.with_dimensions(logical_size)
.build(&event_loop)
.unwrap();
let window = WindowBuilder::new()
.with_title("Pathfinder Demo")
.with_dimensions(logical_size)
.build(&event_loop)
.unwrap();
window.show();
let connection = Connection::from_winit_window(&window).unwrap();
let native_widget = connection.create_native_widget_from_winit_window(&window).unwrap();
let native_widget = connection
.create_native_widget_from_winit_window(&window)
.unwrap();
let adapter = if options.high_performance_gpu {
connection.create_hardware_adapter().unwrap()
@ -270,13 +292,18 @@ impl WindowImpl {
version: SurfmanGLVersion::new(3, 0),
flags: ContextAttributeFlags::ALPHA,
};
let context_descriptor = device.create_context_descriptor(&context_attributes).unwrap();
let context_descriptor = device
.create_context_descriptor(&context_attributes)
.unwrap();
let surface_type = SurfaceType::Widget { native_widget };
let mut context = device.create_context(&context_descriptor).unwrap();
let surface = device.create_surface(&context, SurfaceAccess::GPUOnly, surface_type)
.unwrap();
device.bind_surface_to_context(&mut context, surface).unwrap();
let surface = device
.create_surface(&context, SurfaceAccess::GPUOnly, surface_type)
.unwrap();
device
.bind_surface_to_context(&mut context, surface)
.unwrap();
device.make_context_current(&context).unwrap();
gl::load_with(|symbol_name| device.get_proc_address(&context, symbol_name));
@ -307,14 +334,17 @@ impl WindowImpl {
let event_loop = EventsLoop::new();
let window_size = Size2D::new(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT);
let logical_size = LogicalSize::new(window_size.width as f64, window_size.height as f64);
let window = WindowBuilder::new().with_title("Pathfinder Demo")
.with_dimensions(logical_size)
.build(&event_loop)
.unwrap();
let window = WindowBuilder::new()
.with_title("Pathfinder Demo")
.with_dimensions(logical_size)
.build(&event_loop)
.unwrap();
window.show();
let connection = SystemConnection::from_winit_window(&window).unwrap();
let native_widget = connection.create_native_widget_from_winit_window(&window).unwrap();
let native_widget = connection
.create_native_widget_from_winit_window(&window)
.unwrap();
let adapter = if options.high_performance_gpu {
connection.create_hardware_adapter().unwrap()
@ -326,7 +356,9 @@ impl WindowImpl {
let native_device = device.native_device();
let surface_type = SurfaceType::Widget { native_widget };
let surface = device.create_surface(SurfaceAccess::GPUOnly, surface_type).unwrap();
let surface = device
.create_surface(SurfaceAccess::GPUOnly, surface_type)
.unwrap();
let resource_loader = FilesystemResourceLoader::locate();
@ -350,11 +382,16 @@ impl WindowImpl {
}
}
fn window(&self) -> &WinitWindow { &self.window }
fn window(&self) -> &WinitWindow {
&self.window
}
fn size(&self) -> WindowSize {
let window = self.window();
let (monitor, size) = (window.get_current_monitor(), window.get_inner_size().unwrap());
let (monitor, size) = (
window.get_current_monitor(),
window.get_inner_size().unwrap(),
);
WindowSize {
logical_size: vec2i(size.width as i32, size.height as i32),
@ -370,18 +407,13 @@ impl WindowImpl {
let pending_events = &mut self.pending_events;
self.event_loop.run_forever(|winit_event| {
//println!("blocking {:?}", winit_event);
match convert_winit_event(winit_event,
window,
mouse_position,
mouse_down) {
match convert_winit_event(winit_event, window, mouse_position, mouse_down) {
Some(event) => {
//println!("handled");
pending_events.push_back(event);
ControlFlow::Break
}
None => {
ControlFlow::Continue
}
None => ControlFlow::Continue,
}
});
}
@ -397,10 +429,9 @@ impl WindowImpl {
let pending_events = &mut self.pending_events;
self.event_loop.poll_events(|winit_event| {
//println!("nonblocking {:?}", winit_event);
if let Some(event) = convert_winit_event(winit_event,
window,
mouse_position,
mouse_down) {
if let Some(event) =
convert_winit_event(winit_event, window, mouse_position, mouse_down)
{
//println!("handled");
pending_events.push_back(event);
}
@ -410,87 +441,93 @@ impl WindowImpl {
}
}
fn convert_winit_event(winit_event: WinitEvent,
window: &WinitWindow,
mouse_position: &mut Vector2I,
mouse_down: &mut bool)
-> Option<Event> {
fn convert_winit_event(
winit_event: WinitEvent,
window: &WinitWindow,
mouse_position: &mut Vector2I,
mouse_down: &mut bool,
) -> Option<Event> {
match winit_event {
WinitEvent::Awakened => {
let mut event_queue = EVENT_QUEUE.lock().unwrap();
let event_queue = event_queue.as_mut().unwrap();
match event_queue.pending_custom_events
.pop_front()
.expect("`Awakened` with no pending custom event!") {
CustomEvent::OpenData(data_path) => Some(Event::OpenData(DataPath::Path(data_path))),
CustomEvent::User { message_data, message_type } => {
Some(Event::User { message_data, message_type })
match event_queue
.pending_custom_events
.pop_front()
.expect("`Awakened` with no pending custom event!")
{
CustomEvent::OpenData(data_path) => {
Some(Event::OpenData(DataPath::Path(data_path)))
}
CustomEvent::User {
message_data,
message_type,
} => Some(Event::User {
message_data,
message_type,
}),
}
}
WinitEvent::WindowEvent { event: window_event, .. } => {
match window_event {
WindowEvent::MouseInput {
state: ElementState::Pressed,
button: MouseButton::Left,
..
} => {
*mouse_down = true;
Some(Event::MouseDown(*mouse_position))
WinitEvent::WindowEvent {
event: window_event,
..
} => match window_event {
WindowEvent::MouseInput {
state: ElementState::Pressed,
button: MouseButton::Left,
..
} => {
*mouse_down = true;
Some(Event::MouseDown(*mouse_position))
}
WindowEvent::MouseInput {
state: ElementState::Released,
button: MouseButton::Left,
..
} => {
*mouse_down = false;
None
}
WindowEvent::CursorMoved { position, .. } => {
*mouse_position = vec2i(position.x as i32, position.y as i32);
if *mouse_down {
Some(Event::MouseDragged(*mouse_position))
} else {
Some(Event::MouseMoved(*mouse_position))
}
WindowEvent::MouseInput {
state: ElementState::Released,
button: MouseButton::Left,
..
} => {
*mouse_down = false;
None
}
WindowEvent::CursorMoved { position, .. } => {
*mouse_position = vec2i(position.x as i32, position.y as i32);
if *mouse_down {
Some(Event::MouseDragged(*mouse_position))
} else {
Some(Event::MouseMoved(*mouse_position))
}
WindowEvent::KeyboardInput { input, .. } => input
.virtual_keycode
.and_then(|virtual_keycode| match virtual_keycode {
VirtualKeyCode::Escape => Some(Keycode::Escape),
VirtualKeyCode::Tab => Some(Keycode::Tab),
virtual_keycode => {
let vk = virtual_keycode as u32;
let vk_a = VirtualKeyCode::A as u32;
let vk_z = VirtualKeyCode::Z as u32;
if vk >= vk_a && vk <= vk_z {
let character = ((vk - vk_a) + 'A' as u32) as u8;
Some(Keycode::Alphanumeric(character))
} else {
None
}
}
}
WindowEvent::KeyboardInput { input, .. } => {
input.virtual_keycode.and_then(|virtual_keycode| {
match virtual_keycode {
VirtualKeyCode::Escape => Some(Keycode::Escape),
VirtualKeyCode::Tab => Some(Keycode::Tab),
virtual_keycode => {
let vk = virtual_keycode as u32;
let vk_a = VirtualKeyCode::A as u32;
let vk_z = VirtualKeyCode::Z as u32;
if vk >= vk_a && vk <= vk_z {
let character = ((vk - vk_a) + 'A' as u32) as u8;
Some(Keycode::Alphanumeric(character))
} else {
None
}
}
}
}).map(|keycode| {
match input.state {
ElementState::Pressed => Event::KeyDown(keycode),
ElementState::Released => Event::KeyUp(keycode),
}
})
}
WindowEvent::CloseRequested => Some(Event::Quit),
WindowEvent::Resized(new_size) => {
let logical_size = vec2i(new_size.width as i32, new_size.height as i32);
let backing_scale_factor =
window.get_current_monitor().get_hidpi_factor() as f32;
Some(Event::WindowResized(WindowSize {
logical_size,
backing_scale_factor,
}))
}
_ => None,
})
.map(|keycode| match input.state {
ElementState::Pressed => Event::KeyDown(keycode),
ElementState::Released => Event::KeyUp(keycode),
}),
WindowEvent::CloseRequested => Some(Event::Quit),
WindowEvent::Resized(new_size) => {
let logical_size = vec2i(new_size.width as i32, new_size.height as i32);
let backing_scale_factor = window.get_current_monitor().get_hidpi_factor() as f32;
Some(Event::WindowResized(WindowSize {
logical_size,
backing_scale_factor,
}))
}
}
_ => None,
},
_ => None,
}
}
}

View File

@ -11,21 +11,21 @@
//! Demonstrates how to use the Pathfinder canvas API with `glutin`.
use glutin::dpi::PhysicalSize;
use glutin::{ContextBuilder, GlProfile, GlRequest};
use glutin::event_loop::{ControlFlow, EventLoop};
use glutin::event::{Event, KeyboardInput, VirtualKeyCode, WindowEvent};
use glutin::event_loop::{ControlFlow, EventLoop};
use glutin::window::WindowBuilder;
use glutin::{ContextBuilder, GlProfile, GlRequest};
use pathfinder_canvas::{Canvas, CanvasFontContext, Path2D};
use pathfinder_color::ColorF;
use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::vector::{vec2f, vec2i};
use pathfinder_gl::{GLDevice, GLVersion};
use pathfinder_resources::embedded::EmbeddedResourceLoader;
use pathfinder_renderer::concurrent::rayon::RayonExecutor;
use pathfinder_renderer::concurrent::scene_proxy::SceneProxy;
use pathfinder_renderer::gpu::renderer::Renderer;
use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererMode, RendererOptions};
use pathfinder_renderer::gpu::renderer::Renderer;
use pathfinder_renderer::options::BuildOptions;
use pathfinder_resources::embedded::EmbeddedResourceLoader;
fn main() {
// Calculate the right logical size of the window.
@ -34,14 +34,16 @@ fn main() {
let physical_window_size = PhysicalSize::new(window_size.x() as f64, window_size.y() as f64);
// Open a window.
let window_builder = WindowBuilder::new().with_title("Minimal example")
.with_inner_size(physical_window_size);
let window_builder = WindowBuilder::new()
.with_title("Minimal example")
.with_inner_size(physical_window_size);
// Create an OpenGL 3.x context for Pathfinder to use.
let gl_context = ContextBuilder::new().with_gl(GlRequest::Latest)
.with_gl_profile(GlProfile::Core)
.build_windowed(window_builder, &event_loop)
.unwrap();
let gl_context = ContextBuilder::new()
.with_gl(GlRequest::Latest)
.with_gl_profile(GlProfile::Core)
.build_windowed(window_builder, &event_loop)
.unwrap();
// Load OpenGL, and make the context current.
let gl_context = unsafe { gl_context.make_current().unwrap() };
@ -79,28 +81,38 @@ fn main() {
canvas.stroke_path(path);
// Render the canvas to screen.
let mut scene = SceneProxy::from_scene(canvas.into_canvas().into_scene(),
renderer.mode().level,
RayonExecutor);
let mut scene = SceneProxy::from_scene(
canvas.into_canvas().into_scene(),
renderer.mode().level,
RayonExecutor,
);
scene.build_and_render(&mut renderer, BuildOptions::default());
gl_context.swap_buffers().unwrap();
// Wait for a keypress.
event_loop.run(move |event, _, control_flow| {
match event {
Event::WindowEvent { event: WindowEvent::CloseRequested, .. } |
Event::WindowEvent {
event: WindowEvent::KeyboardInput {
input: KeyboardInput { virtual_keycode: Some(VirtualKeyCode::Escape), .. },
..
},
event: WindowEvent::CloseRequested,
..
}
| Event::WindowEvent {
event:
WindowEvent::KeyboardInput {
input:
KeyboardInput {
virtual_keycode: Some(VirtualKeyCode::Escape),
..
},
..
},
..
} => {
*control_flow = ControlFlow::Exit;
},
}
_ => {
*control_flow = ControlFlow::Wait;
},
}
};
})
}

View File

@ -12,8 +12,8 @@ use foreign_types::ForeignTypeRef;
use metal::{CAMetalLayer, CoreAnimationLayerRef};
use pathfinder_canvas::{Canvas, CanvasFontContext, Path2D};
use pathfinder_color::ColorF;
use pathfinder_geometry::vector::{vec2f, vec2i};
use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::vector::{vec2f, vec2i};
use pathfinder_metal::MetalDevice;
use pathfinder_renderer::concurrent::rayon::RayonExecutor;
use pathfinder_renderer::concurrent::scene_proxy::SceneProxy;
@ -34,10 +34,15 @@ fn main() {
// Open a window.
let window_size = vec2i(640, 480);
let window = video.window("Minimal example", window_size.x() as u32, window_size.y() as u32)
.opengl()
.build()
.unwrap();
let window = video
.window(
"Minimal example",
window_size.x() as u32,
window_size.y() as u32,
)
.opengl()
.build()
.unwrap();
// Create a Metal context.
let canvas = window.into_canvas().present_vsync().build().unwrap();
@ -48,9 +53,7 @@ fn main() {
let drawable = metal_layer.next_drawable().unwrap();
// Create a Pathfinder renderer.
let device = unsafe {
MetalDevice::new(metal_device, drawable.clone())
};
let device = unsafe { MetalDevice::new(metal_device, drawable.clone()) };
let mode = RendererMode::default_for_device(&device);
let options = RendererOptions {
dest: DestFramebuffer::full_window(window_size),
@ -81,9 +84,11 @@ fn main() {
canvas.stroke_path(path);
// Render the canvas to screen.
let mut scene = SceneProxy::from_scene(canvas.into_canvas().into_scene(),
renderer.mode().level,
RayonExecutor);
let mut scene = SceneProxy::from_scene(
canvas.into_canvas().into_scene(),
renderer.mode().level,
RayonExecutor,
);
scene.build_and_render(&mut renderer, BuildOptions::default());
renderer.device().present_drawable(drawable);
@ -91,7 +96,11 @@ fn main() {
let mut event_pump = sdl_context.event_pump().unwrap();
loop {
match event_pump.wait_event() {
Event::Quit {..} | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => return,
Event::Quit { .. }
| Event::KeyDown {
keycode: Some(Keycode::Escape),
..
} => return,
_ => {}
}
}

View File

@ -20,7 +20,9 @@ use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererMode, RendererO
use pathfinder_renderer::gpu::renderer::Renderer;
use pathfinder_renderer::options::BuildOptions;
use pathfinder_resources::embedded::EmbeddedResourceLoader;
use surfman::{Connection, ContextAttributeFlags, ContextAttributes, GLVersion as SurfmanGLVersion};
use surfman::{
Connection, ContextAttributeFlags, ContextAttributes, GLVersion as SurfmanGLVersion,
};
use surfman::{SurfaceAccess, SurfaceType};
use winit::dpi::LogicalSize;
use winit::{ControlFlow, Event, EventsLoop, WindowBuilder, WindowEvent};
@ -30,16 +32,19 @@ fn main() {
let mut event_loop = EventsLoop::new();
let window_size = Size2D::new(640, 480);
let logical_size = LogicalSize::new(window_size.width as f64, window_size.height as f64);
let window = WindowBuilder::new().with_title("Minimal example")
.with_dimensions(logical_size)
.build(&event_loop)
.unwrap();
let window = WindowBuilder::new()
.with_title("Minimal example")
.with_dimensions(logical_size)
.build(&event_loop)
.unwrap();
window.show();
// Create a `surfman` device. On a multi-GPU system, we'll request the low-power integrated
// GPU.
let connection = Connection::from_winit_window(&window).unwrap();
let native_widget = connection.create_native_widget_from_winit_window(&window).unwrap();
let native_widget = connection
.create_native_widget_from_winit_window(&window)
.unwrap();
let adapter = connection.create_low_power_adapter().unwrap();
let mut device = connection.create_device(&adapter).unwrap();
@ -48,14 +53,19 @@ fn main() {
version: SurfmanGLVersion::new(3, 0),
flags: ContextAttributeFlags::ALPHA,
};
let context_descriptor = device.create_context_descriptor(&context_attributes).unwrap();
let context_descriptor = device
.create_context_descriptor(&context_attributes)
.unwrap();
// Make the OpenGL context via `surfman`, and load OpenGL functions.
let surface_type = SurfaceType::Widget { native_widget };
let mut context = device.create_context(&context_descriptor).unwrap();
let surface = device.create_surface(&context, SurfaceAccess::GPUOnly, surface_type)
.unwrap();
device.bind_surface_to_context(&mut context, surface).unwrap();
let surface = device
.create_surface(&context, SurfaceAccess::GPUOnly, surface_type)
.unwrap();
device
.bind_surface_to_context(&mut context, surface)
.unwrap();
device.make_context_current(&context).unwrap();
gl::load_with(|symbol_name| device.get_proc_address(&context, symbol_name));
@ -65,10 +75,11 @@ fn main() {
let framebuffer_size = vec2i(physical_size.width as i32, physical_size.height as i32);
// Create a Pathfinder GL device.
let default_framebuffer = device.context_surface_info(&context)
.unwrap()
.unwrap()
.framebuffer_object;
let default_framebuffer = device
.context_surface_info(&context)
.unwrap()
.unwrap()
.framebuffer_object;
let pathfinder_device = GLDevice::new(GLVersion::GL3, default_framebuffer);
// Create a Pathfinder renderer.
@ -87,17 +98,27 @@ fn main() {
event_loop.run_forever(|event| {
let mut should_render = is_first_render;
match event {
Event::WindowEvent { event: WindowEvent::CloseRequested, .. } |
Event::WindowEvent { event: WindowEvent::KeyboardInput { .. }, .. } => return ControlFlow::Break,
Event::WindowEvent { event: WindowEvent::Refresh, .. } => {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
}
| Event::WindowEvent {
event: WindowEvent::KeyboardInput { .. },
..
} => return ControlFlow::Break,
Event::WindowEvent {
event: WindowEvent::Refresh,
..
} => {
should_render = true;
}
_ => {},
_ => {}
}
if should_render {
// Make a canvas. We're going to draw a house.
let mut canvas = Canvas::new(framebuffer_size.to_f32()).get_context_2d(font_context.clone());
let mut canvas =
Canvas::new(framebuffer_size.to_f32()).get_context_2d(font_context.clone());
// Set line width.
canvas.set_line_width(10.0);
@ -117,15 +138,22 @@ fn main() {
canvas.stroke_path(path);
// Render the canvas to screen.
let mut scene = SceneProxy::from_scene(canvas.into_canvas().into_scene(),
renderer.mode().level,
RayonExecutor);
let mut scene = SceneProxy::from_scene(
canvas.into_canvas().into_scene(),
renderer.mode().level,
RayonExecutor,
);
scene.build_and_render(&mut renderer, BuildOptions::default());
// Present the surface.
let mut surface = device.unbind_surface_from_context(&mut context).unwrap().unwrap();
let mut surface = device
.unbind_surface_from_context(&mut context)
.unwrap()
.unwrap();
device.present_surface(&mut context, &mut surface).unwrap();
device.bind_surface_to_context(&mut context, surface).unwrap();
device
.bind_surface_to_context(&mut context, surface)
.unwrap();
}
is_first_render = false;

View File

@ -11,7 +11,7 @@
use euclid::default::Size2D;
use pathfinder_canvas::{Canvas, CanvasFontContext, CanvasRenderingContext2D, FillStyle, Path2D};
use pathfinder_color::{ColorF, ColorU};
use pathfinder_geometry::vector::{Vector2F, Vector2I, vec2f, vec2i};
use pathfinder_geometry::vector::{vec2f, vec2i, Vector2F, Vector2I};
use pathfinder_gl::{GLDevice, GLVersion};
use pathfinder_renderer::concurrent::rayon::RayonExecutor;
use pathfinder_renderer::concurrent::scene_proxy::SceneProxy;
@ -19,9 +19,11 @@ use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererMode, RendererO
use pathfinder_renderer::gpu::renderer::Renderer;
use pathfinder_renderer::options::BuildOptions;
use pathfinder_resources::embedded::EmbeddedResourceLoader;
use std::f32::consts::PI;
use std::f32;
use surfman::{Connection, ContextAttributeFlags, ContextAttributes, GLVersion as SurfmanGLVersion};
use std::f32::consts::PI;
use surfman::{
Connection, ContextAttributeFlags, ContextAttributes, GLVersion as SurfmanGLVersion,
};
use surfman::{SurfaceAccess, SurfaceType};
use winit::dpi::LogicalSize;
use winit::{Event, EventsLoop, WindowBuilder, WindowEvent};
@ -43,16 +45,19 @@ fn main() {
let mut event_loop = EventsLoop::new();
let window_size = Size2D::new(1067, 800);
let logical_size = LogicalSize::new(window_size.width as f64, window_size.height as f64);
let window = WindowBuilder::new().with_title("Moire example")
.with_dimensions(logical_size)
.build(&event_loop)
.unwrap();
let window = WindowBuilder::new()
.with_title("Moire example")
.with_dimensions(logical_size)
.build(&event_loop)
.unwrap();
window.show();
// Create a `surfman` device. On a multi-GPU system, we'll request the low-power integrated
// GPU.
let connection = Connection::from_winit_window(&window).unwrap();
let native_widget = connection.create_native_widget_from_winit_window(&window).unwrap();
let native_widget = connection
.create_native_widget_from_winit_window(&window)
.unwrap();
let adapter = connection.create_low_power_adapter().unwrap();
let mut device = connection.create_device(&adapter).unwrap();
@ -61,14 +66,19 @@ fn main() {
version: SurfmanGLVersion::new(3, 0),
flags: ContextAttributeFlags::ALPHA,
};
let context_descriptor = device.create_context_descriptor(&context_attributes).unwrap();
let context_descriptor = device
.create_context_descriptor(&context_attributes)
.unwrap();
// Make the OpenGL context via `surfman`, and load OpenGL functions.
let surface_type = SurfaceType::Widget { native_widget };
let mut gl_context = device.create_context(&context_descriptor).unwrap();
let surface = device.create_surface(&gl_context, SurfaceAccess::GPUOnly, surface_type)
.unwrap();
device.bind_surface_to_context(&mut gl_context, surface).unwrap();
let surface = device
.create_surface(&gl_context, SurfaceAccess::GPUOnly, surface_type)
.unwrap();
device
.bind_surface_to_context(&mut gl_context, surface)
.unwrap();
device.make_context_current(&gl_context).unwrap();
gl::load_with(|symbol_name| device.get_proc_address(&gl_context, symbol_name));
@ -78,10 +88,11 @@ fn main() {
let framebuffer_size = vec2i(physical_size.width as i32, physical_size.height as i32);
// Create a Pathfinder GL device.
let default_framebuffer = device.context_surface_info(&gl_context)
.unwrap()
.unwrap()
.framebuffer_object;
let default_framebuffer = device
.context_surface_info(&gl_context)
.unwrap()
.unwrap()
.framebuffer_object;
let pathfinder_device = GLDevice::new(GLVersion::GL3, default_framebuffer);
// Create our renderers.
@ -101,16 +112,27 @@ fn main() {
moire_renderer.render();
// Present the rendered canvas via `surfman`.
let mut surface = device.unbind_surface_from_context(&mut gl_context).unwrap().unwrap();
device.present_surface(&mut gl_context, &mut surface).unwrap();
device.bind_surface_to_context(&mut gl_context, surface).unwrap();
let mut surface = device
.unbind_surface_from_context(&mut gl_context)
.unwrap()
.unwrap();
device
.present_surface(&mut gl_context, &mut surface)
.unwrap();
device
.bind_surface_to_context(&mut gl_context, surface)
.unwrap();
event_loop.poll_events(|event| {
match event {
Event::WindowEvent { event: WindowEvent::CloseRequested, .. } |
Event::WindowEvent { event: WindowEvent::KeyboardInput { .. }, .. } => exit = true,
_ => {}
event_loop.poll_events(|event| match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
}
| Event::WindowEvent {
event: WindowEvent::KeyboardInput { .. },
..
} => exit = true,
_ => {}
});
}
}
@ -127,8 +149,11 @@ struct MoireRenderer {
}
impl MoireRenderer {
fn new(renderer: Renderer<GLDevice>, window_size: Vector2I, drawable_size: Vector2I)
-> MoireRenderer {
fn new(
renderer: Renderer<GLDevice>,
window_size: Vector2I,
drawable_size: Vector2I,
) -> MoireRenderer {
let level = renderer.mode().level;
MoireRenderer {
renderer,
@ -159,7 +184,7 @@ impl MoireRenderer {
self.renderer.options_mut().background_color = Some(background_color);
// Make a canvas.
let mut canvas =
let mut canvas =
Canvas::new(self.drawable_size.to_f32()).get_context_2d(self.font_context.clone());
canvas.set_line_width(CIRCLE_THICKNESS * self.device_pixel_ratio);
canvas.set_stroke_style(FillStyle::Color(foreground_color.to_u8()));
@ -171,7 +196,8 @@ impl MoireRenderer {
// Build and render scene.
self.scene.replace_scene(canvas.into_canvas().into_scene());
self.scene.build_and_render(&mut self.renderer, BuildOptions::default());
self.scene
.build_and_render(&mut self.renderer, BuildOptions::default());
self.frame += 1;
}

View File

@ -6,7 +6,7 @@ edition = "2018"
[dependencies]
arrayvec = "0.5"
font-kit = "0.6"
font-kit = "0.13"
gl = "0.14"
[dependencies.euclid]

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,7 @@ authors = ["Patrick Walton <pcwalton@mimiga.net>"]
edition = "2018"
[dependencies]
font-kit = "0.6"
font-kit = "0.13"
gl = "0.14"
sdl2 = "0.33"
sdl2-sys = "0.33"

View File

@ -18,8 +18,8 @@ use pathfinder_renderer::concurrent::scene_proxy::SceneProxy;
use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererMode, RendererOptions};
use pathfinder_renderer::gpu::renderer::Renderer;
use pathfinder_renderer::options::BuildOptions;
use pathfinder_resources::ResourceLoader;
use pathfinder_resources::fs::FilesystemResourceLoader;
use pathfinder_resources::ResourceLoader;
use sdl2::event::{Event, WindowEvent};
use sdl2::keyboard::Keycode;
use sdl2::video::GLProfile;
@ -36,16 +36,19 @@ fn main() {
let gl_attributes = video.gl_attr();
gl_attributes.set_context_profile(GLProfile::Core);
gl_attributes.set_context_version(4, 1);
gl_attributes.set_context_flags()
.forward_compatible()
.set();
gl_attributes.set_context_flags().forward_compatible().set();
// Open a window.
let window_size = vec2i(640, 480);
let window = video.window("Text example", window_size.x() as u32, window_size.y() as u32)
.opengl()
.build()
.unwrap();
let window = video
.window(
"Text example",
window_size.x() as u32,
window_size.y() as u32,
)
.opengl()
.build()
.unwrap();
// Create the GL context, and make it current.
let gl_context = window.gl_create_context().unwrap();
@ -75,10 +78,21 @@ fn main() {
let mut fps_buf = String::new();
loop {
match event_pump.poll_event() {
Some(Event::Quit {..} | Event::KeyDown { keycode: Some(Keycode::Escape), .. }) => return,
Some(Event::Window { win_event: WindowEvent::Exposed, .. }) | None => {
Some(
Event::Quit { .. }
| Event::KeyDown {
keycode: Some(Keycode::Escape),
..
},
) => return,
Some(Event::Window {
win_event: WindowEvent::Exposed,
..
})
| None => {
// Make a canvas.
let mut canvas = Canvas::new(window_size.to_f32()).get_context_2d(font_context.clone());
let mut canvas =
Canvas::new(window_size.to_f32()).get_context_2d(font_context.clone());
// Draw the text.
canvas.set_font("Overpass-Regular").unwrap();
@ -87,7 +101,9 @@ fn main() {
let elapsed = now.duration_since(last_update);
if elapsed >= Duration::from_millis(50) {
last_update = now;
let fps = (n as f64) * ((Duration::from_secs(1).as_micros() as f64) / (elapsed.as_micros() as f64));
let fps = (n as f64)
* ((Duration::from_secs(1).as_micros() as f64)
/ (elapsed.as_micros() as f64));
fps_buf.clear();
fps_buf.push_str("FPS: ");
use std::fmt::Write;
@ -100,14 +116,16 @@ fn main() {
canvas.stroke_text("Goodbye Pathfinder!", vec2f(608.0, 464.0));
// Render the canvas to screen.
let mut scene = SceneProxy::from_scene(canvas.into_canvas().into_scene(),
renderer.mode().level,
RayonExecutor);
let mut scene = SceneProxy::from_scene(
canvas.into_canvas().into_scene(),
renderer.mode().level,
RayonExecutor,
);
scene.build_and_render(&mut renderer, BuildOptions::default());
window.gl_swap_window();
n += 1;
},
}
_ => {}
}
}

View File

@ -18,8 +18,8 @@ use pathfinder_renderer::gpu::renderer::Renderer;
use pathfinder_renderer::options::BuildOptions;
use pathfinder_resources::embedded::EmbeddedResourceLoader;
use pathfinder_webgl::WebGlDevice;
use wasm_bindgen::JsCast;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use web_sys::{self, HtmlCanvasElement, WebGl2RenderingContext};
mod utils;
@ -32,11 +32,12 @@ pub fn rust_main() {
let canvas = document.get_element_by_id("c").unwrap();
let canvas: HtmlCanvasElement = canvas.dyn_into::<HtmlCanvasElement>().unwrap();
let context = canvas.get_context("webgl2")
.unwrap()
.unwrap()
.dyn_into::<WebGl2RenderingContext>()
.unwrap();
let context = canvas
.get_context("webgl2")
.unwrap()
.unwrap()
.dyn_into::<WebGl2RenderingContext>()
.unwrap();
// Get the real size of the window, taking HiDPI into account.
let framebuffer_size = vec2i(canvas.width() as i32, canvas.height() as i32);

View File

@ -10,20 +10,20 @@
use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::vector::{Vector2F, vec2f, vec2i};
use pathfinder_geometry::vector::{vec2f, vec2i, Vector2F};
use pathfinder_gl::{GLDevice, GLVersion};
use pathfinder_renderer::concurrent::rayon::RayonExecutor;
use pathfinder_renderer::concurrent::scene_proxy::SceneProxy;
use pathfinder_renderer::gpu::renderer::Renderer;
use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererMode, RendererOptions};
use pathfinder_renderer::options::{RenderTransform, BuildOptions};
use pathfinder_resources::ResourceLoader;
use pathfinder_renderer::gpu::renderer::Renderer;
use pathfinder_renderer::options::{BuildOptions, RenderTransform};
use pathfinder_renderer::scene::Scene;
use pathfinder_resources::embedded::EmbeddedResourceLoader;
use pathfinder_resources::ResourceLoader;
use pathfinder_swf::{draw_paths_into_scene, process_swf_tags};
use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use sdl2::video::GLProfile;
use pathfinder_renderer::scene::Scene;
use pathfinder_swf::{draw_paths_into_scene, process_swf_tags};
use std::env;
use std::fs::read;
@ -35,8 +35,8 @@ fn main() {
match read(path) {
Ok(bytes) => {
swf_bytes = bytes;
},
Err(e) => panic!("{}", e)
}
Err(e) => panic!("{}", e),
}
} else {
// NOTE(jon): This is a version of the ghostscript tiger graphic flattened to a single
@ -83,13 +83,21 @@ fn main() {
// Open a window.
let window_size = vec2i(stage.width(), stage.height());
let window = video.window("Minimal example", window_size.x() as u32, window_size.y() as u32)
let window = video
.window(
"Minimal example",
window_size.x() as u32,
window_size.y() as u32,
)
.opengl()
.allow_highdpi()
.build()
.unwrap();
let pixel_size = vec2i(window.drawable_size().0 as i32, window.drawable_size().1 as i32);
let pixel_size = vec2i(
window.drawable_size().0 as i32,
window.drawable_size().1 as i32,
);
let device_pixel_ratio = pixel_size.x() as f32 / window_size.x() as f32;
// Create the GL context, and make it current.
@ -109,9 +117,10 @@ fn main() {
// Clear to swf stage background color.
let mut scene = Scene::new();
scene.set_view_box(RectF::new(Vector2F::zero(),
vec2f(stage.width() as f32,
stage.height() as f32) * device_pixel_ratio));
scene.set_view_box(RectF::new(
Vector2F::zero(),
vec2f(stage.width() as f32, stage.height() as f32) * device_pixel_ratio,
));
draw_paths_into_scene(&library, &mut scene);
// Render the canvas to screen.
@ -126,7 +135,11 @@ fn main() {
let mut event_pump = sdl_context.event_pump().unwrap();
loop {
match event_pump.wait_event() {
Event::Quit {..} | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => return,
Event::Quit { .. }
| Event::KeyDown {
keycode: Some(Keycode::Escape),
..
} => return,
_ => {}
}
}

View File

@ -10,7 +10,7 @@
use pathfinder_content::outline::ContourIterFlags;
use pathfinder_content::segment::SegmentKind;
use pathfinder_geometry::vector::{Vector2F, vec2f};
use pathfinder_geometry::vector::{vec2f, Vector2F};
use pathfinder_renderer::scene::{DrawPathId, Scene};
use std::fmt;
use std::io::{self, Write};
@ -39,7 +39,7 @@ impl Export for Scene {
match format {
FileFormat::SVG => export_svg(self, writer),
FileFormat::PDF => export_pdf(self, writer),
FileFormat::PS => export_ps(self, writer)
FileFormat::PS => export_ps(self, writer),
}
}
}
@ -63,7 +63,12 @@ fn export_svg<W: Write>(scene: &Scene, writer: &mut W) -> io::Result<()> {
if !draw_path.name.is_empty() {
write!(writer, " id=\"{}\"", draw_path.name)?;
}
writeln!(writer, " fill=\"{:?}\" d=\"{:?}\" />", paint.base_color(), draw_path.outline)?;
writeln!(
writer,
" fill=\"{:?}\" d=\"{:?}\" />",
paint.base_color(),
draw_path.outline
)?;
}
writeln!(writer, "</svg>")?;
Ok(())
@ -107,11 +112,11 @@ fn export_pdf<W: Write>(scene: &Scene, writer: &mut W) -> io::Result<()> {
let c2 = c * (2.0 / 3.0) + p * (1.0 / 3.0);
pdf.cubic_to(c1, c2, p);
}
SegmentKind::Cubic => {
pdf.cubic_to(tr(segment.ctrl.from()),
tr(segment.ctrl.to()),
tr(segment.baseline.to()))
}
SegmentKind::Cubic => pdf.cubic_to(
tr(segment.ctrl.from()),
tr(segment.ctrl.to()),
tr(segment.baseline.to()),
),
}
}
@ -136,11 +141,15 @@ fn export_ps<W: Write>(scene: &Scene, writer: &mut W) -> io::Result<()> {
let view_box = scene.view_box();
writeln!(writer, "%!PS-Adobe-3.0 EPSF-3.0")?;
writeln!(writer, "%%BoundingBox: {:.0} {:.0}",
writeln!(
writer,
"%%BoundingBox: {:.0} {:.0}",
P(view_box.origin()),
P(view_box.size()),
)?;
writeln!(writer, "%%HiResBoundingBox: {} {}",
writeln!(
writer,
"%%HiResBoundingBox: {} {}",
P(view_box.origin()),
P(view_box.size()),
)?;
@ -176,7 +185,9 @@ fn export_ps<W: Write>(scene: &Scene, writer: &mut W) -> io::Result<()> {
writeln!(writer, "{} {} {} curveto", P(c1), P(c2), P(p))?;
}
SegmentKind::Cubic => {
writeln!(writer, "{} {} {} curveto",
writeln!(
writer,
"{} {} {} curveto",
P(segment.ctrl.from()),
P(segment.ctrl.to()),
P(segment.baseline.to())
@ -202,5 +213,3 @@ fn export_ps<W: Write>(scene: &Scene, writer: &mut W) -> io::Result<()> {
writeln!(writer, "showpage")?;
Ok(())
}

View File

@ -18,14 +18,11 @@ use std::io::{self, Write};
struct Counter<T> {
inner: T,
count: u64
count: u64,
}
impl<T> Counter<T> {
pub fn new(inner: T) -> Counter<T> {
Counter {
inner,
count: 0
}
Counter { inner, count: 0 }
}
pub fn pos(&self) -> u64 {
self.count
@ -37,8 +34,8 @@ impl<W: Write> Write for Counter<W> {
Ok(n) => {
self.count += n as u64;
Ok(n)
},
Err(e) => Err(e)
}
Err(e) => Err(e),
}
}
fn flush(&mut self) -> io::Result<()> {
@ -95,7 +92,7 @@ impl Pdf {
},
],
page_size: None,
compression: Some(Compression::Fast)
compression: Some(Compression::Fast),
}
}
@ -113,11 +110,14 @@ impl Pdf {
#[inline]
pub fn set_fill_color(&mut self, color: ColorU) {
let norm = |color| f32::from(color) / 255.0;
writeln!(self.page_buffer, "{} {} {} rg",
writeln!(
self.page_buffer,
"{} {} {} rg",
norm(color.r),
norm(color.g),
norm(color.b)
).unwrap();
)
.unwrap();
}
/// Move to a new page in the PDF document
@ -134,7 +134,7 @@ impl Pdf {
self.page_size = Some(size);
}
pub fn move_to(&mut self, p: Vector2F) {
pub fn move_to(&mut self, p: Vector2F) {
writeln!(self.page_buffer, "{} {} m", p.x(), p.y()).unwrap();
}
@ -143,7 +143,17 @@ impl Pdf {
}
pub fn cubic_to(&mut self, c1: Vector2F, c2: Vector2F, p: Vector2F) {
writeln!(self.page_buffer, "{} {} {} {} {} {} c", c1.x(), c1.y(), c2.x(), c2.y(), p.x(), p.y()).unwrap();
writeln!(
self.page_buffer,
"{} {} {} {} {} {} c",
c1.x(),
c1.y(),
c2.x(),
c2.y(),
p.x(),
p.y()
)
.unwrap();
}
pub fn fill(&mut self) {
writeln!(self.page_buffer, "f").unwrap();
@ -156,7 +166,7 @@ impl Pdf {
fn end_page(&mut self) {
let size = match self.page_size.take() {
Some(size) => size,
None => return // no page started
None => return, // no page started
};
let page_stream = if let Some(level) = self.compression {
let compressed = deflate::deflate_bytes_zlib_conf(&self.page_buffer, level);
@ -185,22 +195,31 @@ impl Pdf {
/Resources <<\n"
.to_vec();
for (idx, _obj) in self.objects.iter().enumerate().filter(|&(_, o)| o.is_xobject) {
write!(page_object, "/XObject {} 0 R ", idx+1).unwrap();
for (idx, _obj) in self
.objects
.iter()
.enumerate()
.filter(|&(_, o)| o.is_xobject)
{
write!(page_object, "/XObject {} 0 R ", idx + 1).unwrap();
}
write!(page_object,
" >>\n \
/MediaBox [0 0 {} {}]\n \
/Contents {} 0 R\n\
>>\n",
size.x(), size.y(), stream_object_id
).unwrap();
write!(
page_object,
" >>\n /MediaBox [0 0 {} {}]\n /Contents {} 0 R\n>>\n",
size.x(),
size.y(),
stream_object_id
)
.unwrap();
self.add_object(page_object, true, false);
}
/// Write the in-memory PDF representation to disk
pub fn write_to<W>(&mut self, writer: W) -> io::Result<()> where W: Write {
pub fn write_to<W>(&mut self, writer: W) -> io::Result<()>
where
W: Write,
{
let mut out = Counter::new(writer);
out.write_all(b"%PDF-1.7\n%\xB5\xED\xAE\xFB\n")?;
@ -211,7 +230,7 @@ impl Pdf {
// Write out each object
for (idx, obj) in self.objects.iter_mut().enumerate().skip(2) {
obj.offset = Some(out.pos());
write!(out, "{} 0 obj\n", idx+1)?;
write!(out, "{} 0 obj\n", idx + 1)?;
out.write_all(&obj.contents)?;
out.write_all(b"endobj\n")?;
}
@ -220,12 +239,18 @@ impl Pdf {
self.objects[1].offset = Some(out.pos());
out.write_all(b"2 0 obj\n")?;
out.write_all(b"<< /Type /Pages\n")?;
write!(out,
write!(
out,
"/Count {}\n",
self.objects.iter().filter(|o| o.is_page).count()
)?;
out.write_all(b"/Kids [")?;
for (idx, _obj) in self.objects.iter().enumerate().filter(|&(_, obj)| obj.is_page) {
for (idx, _obj) in self
.objects
.iter()
.enumerate()
.filter(|&(_, obj)| obj.is_page)
{
write!(out, "{} 0 R ", idx + 1)?;
}
out.write_all(b"] >>\nendobj\n")?;

View File

@ -12,7 +12,7 @@
use crate::transform2d::Matrix2x2F;
use crate::util;
use crate::vector::{Vector2F, vec2f};
use crate::vector::{vec2f, Vector2F};
use pathfinder_simd::default::F32x4;
use std::ops::{Add, Mul, MulAssign, Sub};

View File

@ -91,13 +91,19 @@ impl RectF {
pub fn contains_point(self, point: Vector2F) -> bool {
// self.origin <= point && point <= self.lower_right
let point = point.0.to_f32x4();
self.0.concat_xy_xy(point).packed_le(point.concat_xy_zw(self.0)).all_true()
self.0
.concat_xy_xy(point)
.packed_le(point.concat_xy_zw(self.0))
.all_true()
}
#[inline]
pub fn contains_rect(self, other: RectF) -> bool {
// self.origin <= other.origin && other.lower_right <= self.lower_right
self.0.concat_xy_zw(other.0).packed_le(other.0.concat_xy_zw(self.0)).all_true()
self.0
.concat_xy_zw(other.0)
.packed_le(other.0.concat_xy_zw(self.0))
.all_true()
}
#[inline]
@ -121,7 +127,10 @@ impl RectF {
#[inline]
pub fn intersects(self, other: RectF) -> bool {
// self.origin < other.lower_right && other.origin < self.lower_right
self.0.concat_xy_xy(other.0).packed_lt(other.0.concat_zw_zw(self.0)).all_true()
self.0
.concat_xy_xy(other.0)
.packed_lt(other.0.concat_zw_zw(self.0))
.all_true()
}
#[inline]
@ -173,13 +182,19 @@ impl RectF {
}
#[inline]
pub fn dilate<A>(self, amount: A) -> RectF where A: IntoVector2F {
pub fn dilate<A>(self, amount: A) -> RectF
where
A: IntoVector2F,
{
let amount = amount.into_vector_2f();
RectF::from_points(self.origin() - amount, self.lower_right() + amount)
}
#[inline]
pub fn contract<A>(self, amount: A) -> RectF where A: IntoVector2F {
pub fn contract<A>(self, amount: A) -> RectF
where
A: IntoVector2F,
{
let amount = amount.into_vector_2f();
RectF::from_points(self.origin() + amount, self.lower_right() - amount)
}
@ -380,7 +395,10 @@ impl RectI {
#[inline]
pub fn intersects(self, other: RectI) -> bool {
// self.origin < other.lower_right && other.origin < self.lower_right
self.0.concat_xy_xy(other.0).packed_lt(other.0.concat_zw_zw(self.0)).all_true()
self.0
.concat_xy_xy(other.0)
.packed_lt(other.0.concat_zw_zw(self.0))
.all_true()
}
#[inline]

View File

@ -14,7 +14,7 @@ use crate::line_segment::LineSegment2F;
use crate::rect::RectF;
use crate::transform3d::Transform4F;
use crate::unit_vector::UnitVector;
use crate::vector::{IntoVector2F, Vector2F, vec2f};
use crate::vector::{vec2f, IntoVector2F, Vector2F};
use pathfinder_simd::default::F32x4;
use std::ops::{Mul, MulAssign, Sub};
@ -31,7 +31,10 @@ impl Default for Matrix2x2F {
impl Matrix2x2F {
#[inline]
pub fn from_scale<S>(scale: S) -> Matrix2x2F where S: IntoVector2F {
pub fn from_scale<S>(scale: S) -> Matrix2x2F
where
S: IntoVector2F,
{
let scale = scale.into_vector_2f();
Matrix2x2F(F32x4::new(scale.x(), 0.0, 0.0, scale.y()))
}
@ -145,7 +148,10 @@ impl Default for Transform2F {
impl Transform2F {
#[inline]
pub fn from_scale<S>(scale: S) -> Transform2F where S: IntoVector2F {
pub fn from_scale<S>(scale: S) -> Transform2F
where
S: IntoVector2F,
{
let scale = scale.into_vector_2f();
Transform2F {
matrix: Matrix2x2F::from_scale(scale),
@ -171,12 +177,21 @@ impl Transform2F {
#[inline]
pub fn from_translation(vector: Vector2F) -> Transform2F {
Transform2F { matrix: Matrix2x2F::default(), vector }
Transform2F {
matrix: Matrix2x2F::default(),
vector,
}
}
#[inline]
pub fn from_scale_rotation_translation<S>(scale: S, theta: f32, translation: Vector2F)
-> Transform2F where S: IntoVector2F {
pub fn from_scale_rotation_translation<S>(
scale: S,
theta: f32,
translation: Vector2F,
) -> Transform2F
where
S: IntoVector2F,
{
let scale = scale.into_vector_2f();
let rotation = Transform2F::from_rotation(theta);
let translation = Transform2F::from_translation(translation);
@ -261,7 +276,10 @@ impl Transform2F {
}
#[inline]
pub fn scale<S>(&self, scale: S) -> Transform2F where S: IntoVector2F {
pub fn scale<S>(&self, scale: S) -> Transform2F
where
S: IntoVector2F,
{
let scale = scale.into_vector_2f();
Transform2F::from_scale(scale) * *self
}
@ -294,7 +312,10 @@ impl Transform2F {
pub fn inverse(&self) -> Transform2F {
let matrix_inv = self.matrix.inverse();
let vector_inv = -(matrix_inv * self.vector);
Transform2F { matrix: matrix_inv, vector: vector_inv }
Transform2F {
matrix: matrix_inv,
vector: vector_inv,
}
}
}
@ -329,7 +350,7 @@ impl Mul<RectF> for Transform2F {
type Output = RectF;
#[inline]
fn mul(self, rect: RectF) -> RectF {
let (upper_left, upper_right) = (self * rect.origin(), self * rect.upper_right());
let (upper_left, upper_right) = (self * rect.origin(), self * rect.upper_right());
let (lower_left, lower_right) = (self * rect.lower_left(), self * rect.lower_right());
let min_point = upper_left.min(upper_right).min(lower_left).min(lower_right);
let max_point = upper_left.max(upper_right).max(lower_left).max(lower_right);

View File

@ -74,7 +74,7 @@ impl Transform4F {
c0: F32x4::new(scale.x(), 0.0, 0.0, 0.0),
c1: F32x4::new(0.0, scale.y(), 0.0, 0.0),
c2: F32x4::new(0.0, 0.0, scale.z(), 0.0),
c3: F32x4::new(0.0, 0.0, 0.0, 1.0),
c3: F32x4::new(0.0, 0.0, 0.0, 1.0),
}
}
@ -86,7 +86,10 @@ impl Transform4F {
#[inline]
pub fn from_translation(mut translation: Vector4F) -> Transform4F {
translation.set_w(1.0);
Transform4F { c3: translation.0, ..Transform4F::default() }
Transform4F {
c3: translation.0,
..Transform4F::default()
}
}
// TODO(pcwalton): Optimize.
@ -212,11 +215,24 @@ impl Transform4F {
// TODO(pcwalton): Use SIMD. This needs a matrix transpose:
// https://fgiesen.wordpress.com/2013/07/09/simd-transposes-1/
let transform = Transform4F::row_major(s.x(), s.y(), s.z(), 0.0,
u.x(), u.y(), u.z(), 0.0,
minus_f.x(), minus_f.y(), minus_f.z(), 0.0,
0.0, 0.0, 0.0, 1.0) *
Transform4F::from_translation((-eye).to_4d());
let transform = Transform4F::row_major(
s.x(),
s.y(),
s.z(),
0.0,
u.x(),
u.y(),
u.z(),
0.0,
minus_f.x(),
minus_f.y(),
minus_f.z(),
0.0,
0.0,
0.0,
0.0,
1.0,
) * Transform4F::from_translation((-eye).to_4d());
transform
}
@ -415,7 +431,7 @@ impl Mul<RectF> for Perspective {
type Output = RectF;
#[inline]
fn mul(self, rect: RectF) -> RectF {
let (upper_left, upper_right) = (self * rect.origin(), self * rect.upper_right());
let (upper_left, upper_right) = (self * rect.origin(), self * rect.upper_right());
let (lower_left, lower_right) = (self * rect.lower_left(), self * rect.lower_right());
let min_point = upper_left.min(upper_right).min(lower_left).min(lower_right);
let max_point = upper_left.max(upper_right).max(lower_left).max(lower_right);
@ -425,8 +441,8 @@ impl Mul<RectF> for Perspective {
#[cfg(test)]
mod test {
use crate::vector::Vector4F;
use crate::transform3d::Transform4F;
use crate::vector::Vector4F;
#[test]
fn test_post_mul() {

View File

@ -26,14 +26,20 @@ impl UnitVector {
#[inline]
pub fn rotate_by(&self, other: UnitVector) -> UnitVector {
let products = (self.0).0.to_f32x4().xyyx() * (other.0).0.to_f32x4().xyxy();
UnitVector(Vector2F::new(products[0] - products[1], products[2] + products[3]))
UnitVector(Vector2F::new(
products[0] - products[1],
products[2] + products[3],
))
}
/// Angle subtraction formula.
#[inline]
pub fn rev_rotate_by(&self, other: UnitVector) -> UnitVector {
let products = (self.0).0.to_f32x4().xyyx() * (other.0).0.to_f32x4().xyxy();
UnitVector(Vector2F::new(products[0] + products[1], products[2] - products[3]))
UnitVector(Vector2F::new(
products[0] + products[1],
products[2] - products[3],
))
}
/// Half angle formula.
@ -41,7 +47,10 @@ impl UnitVector {
pub fn halve_angle(&self) -> UnitVector {
let x = self.0.x();
let term = F32x2::new(x, -x);
UnitVector(Vector2F((F32x2::splat(0.5) * (F32x2::splat(1.0) + term)).max(F32x2::default())
.sqrt()))
UnitVector(Vector2F(
(F32x2::splat(0.5) * (F32x2::splat(1.0) + term))
.max(F32x2::default())
.sqrt(),
))
}
}

View File

@ -36,12 +36,20 @@ impl Vector2F {
#[inline]
pub fn to_3d(self) -> Vector3F {
Vector3F(self.0.to_f32x4().concat_xy_zw(F32x4::new(0.0, 0.0, 0.0, 0.0)))
Vector3F(
self.0
.to_f32x4()
.concat_xy_zw(F32x4::new(0.0, 0.0, 0.0, 0.0)),
)
}
#[inline]
pub fn to_4d(self) -> Vector4F {
Vector4F(self.0.to_f32x4().concat_xy_zw(F32x4::new(0.0, 0.0, 0.0, 1.0)))
Vector4F(
self.0
.to_f32x4()
.concat_xy_zw(F32x4::new(0.0, 0.0, 0.0, 1.0)),
)
}
#[inline]
@ -294,9 +302,9 @@ impl Neg for Vector2F {
}
/// Either a scalar or a `Vector2F`.
///
///
/// Scalars will be automatically splatted (i.e. `x` becomes `vec2f(x, x)`).
///
///
/// Be judicious with the use of this trait. Only use it if it will aid readability without the
/// potential to introduce bugs.
pub trait IntoVector2F {
@ -458,7 +466,10 @@ impl Eq for Vector2I {}
impl Hash for Vector2I {
#[inline]
fn hash<H>(&self, state: &mut H) where H: Hasher {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
self.x().hash(state);
self.y().hash(state);
}

File diff suppressed because it is too large Load Diff

View File

@ -11,8 +11,8 @@
//! GPU memory management.
use crate::{BufferData, BufferTarget, BufferUploadMode, Device, TextureFormat};
use instant::Instant;
use fxhash::FxHashMap;
use instant::Instant;
use pathfinder_geometry::vector::Vector2I;
use std::collections::VecDeque;
use std::default::Default;
@ -31,7 +31,10 @@ const DECAY_TIME: f32 = 0.250;
// This helps avoid stalls. This is admittedly a bit of a hack.
const REUSE_TIME: f32 = 0.015;
pub struct GPUMemoryAllocator<D> where D: Device {
pub struct GPUMemoryAllocator<D>
where
D: Device,
{
general_buffers_in_use: FxHashMap<GeneralBufferID, BufferAllocation<D>>,
index_buffers_in_use: FxHashMap<IndexBufferID, BufferAllocation<D>>,
textures_in_use: FxHashMap<TextureID, TextureAllocation<D>>,
@ -45,34 +48,61 @@ pub struct GPUMemoryAllocator<D> where D: Device {
bytes_allocated: u64,
}
struct BufferAllocation<D> where D: Device {
struct BufferAllocation<D>
where
D: Device,
{
buffer: D::Buffer,
size: u64,
tag: BufferTag,
}
struct TextureAllocation<D> where D: Device {
struct TextureAllocation<D>
where
D: Device,
{
texture: D::Texture,
descriptor: TextureDescriptor,
tag: TextureTag,
}
struct FramebufferAllocation<D> where D: Device {
struct FramebufferAllocation<D>
where
D: Device,
{
framebuffer: D::Framebuffer,
descriptor: TextureDescriptor,
tag: FramebufferTag,
}
struct FreeObject<D> where D: Device {
struct FreeObject<D>
where
D: Device,
{
timestamp: Instant,
kind: FreeObjectKind<D>,
}
enum FreeObjectKind<D> where D: Device {
GeneralBuffer { id: GeneralBufferID, allocation: BufferAllocation<D> },
IndexBuffer { id: IndexBufferID, allocation: BufferAllocation<D> },
Texture { id: TextureID, allocation: TextureAllocation<D> },
Framebuffer { id: FramebufferID, allocation: FramebufferAllocation<D> },
enum FreeObjectKind<D>
where
D: Device,
{
GeneralBuffer {
id: GeneralBufferID,
allocation: BufferAllocation<D>,
},
IndexBuffer {
id: IndexBufferID,
allocation: BufferAllocation<D>,
},
Texture {
id: TextureID,
allocation: TextureAllocation<D>,
},
Framebuffer {
id: FramebufferID,
allocation: FramebufferAllocation<D>,
},
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
@ -108,7 +138,10 @@ pub struct TextureTag(pub &'static str);
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct FramebufferTag(pub &'static str);
impl<D> GPUMemoryAllocator<D> where D: Device {
impl<D> GPUMemoryAllocator<D>
where
D: Device,
{
pub fn new() -> GPUMemoryAllocator<D> {
GPUMemoryAllocator {
general_buffers_in_use: FxHashMap::default(),
@ -125,8 +158,12 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
}
}
pub fn allocate_general_buffer<T>(&mut self, device: &D, size: u64, tag: BufferTag)
-> GeneralBufferID {
pub fn allocate_general_buffer<T>(
&mut self,
device: &D,
size: u64,
tag: BufferTag,
) -> GeneralBufferID {
let mut byte_size = size * mem::size_of::<T>() as u64;
if byte_size < MAX_BUFFER_SIZE_CLASS {
byte_size = byte_size.next_power_of_two();
@ -139,8 +176,8 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
FreeObject {
ref timestamp,
kind: FreeObjectKind::GeneralBuffer { ref allocation, .. },
} if allocation.size == byte_size &&
(now - *timestamp).as_secs_f32() >= REUSE_TIME => {}
} if allocation.size == byte_size
&& (now - *timestamp).as_secs_f32() >= REUSE_TIME => {}
_ => continue,
}
@ -148,9 +185,7 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
Some(FreeObject {
kind: FreeObjectKind::GeneralBuffer { id, allocation },
..
}) => {
(id, allocation)
}
}) => (id, allocation),
_ => unreachable!(),
};
@ -161,29 +196,44 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
}
let buffer = device.create_buffer(BufferUploadMode::Dynamic);
device.allocate_buffer::<u8>(&buffer,
BufferData::Uninitialized(byte_size as usize),
BufferTarget::Vertex);
device.allocate_buffer::<u8>(
&buffer,
BufferData::Uninitialized(byte_size as usize),
BufferTarget::Vertex,
);
let id = self.next_general_buffer_id;
self.next_general_buffer_id.0 += 1;
debug!("mapping general buffer: {:?} {} ({}x{}) {:?}",
id,
byte_size,
size,
mem::size_of::<T>(),
tag);
debug!(
"mapping general buffer: {:?} {} ({}x{}) {:?}",
id,
byte_size,
size,
mem::size_of::<T>(),
tag
);
self.general_buffers_in_use.insert(id, BufferAllocation { buffer, size: byte_size, tag });
self.general_buffers_in_use.insert(
id,
BufferAllocation {
buffer,
size: byte_size,
tag,
},
);
self.bytes_allocated += byte_size;
self.bytes_committed += byte_size;
id
}
pub fn allocate_index_buffer<T>(&mut self, device: &D, size: u64, tag: BufferTag)
-> IndexBufferID {
pub fn allocate_index_buffer<T>(
&mut self,
device: &D,
size: u64,
tag: BufferTag,
) -> IndexBufferID {
let mut byte_size = size * mem::size_of::<T>() as u64;
if byte_size < MAX_BUFFER_SIZE_CLASS {
byte_size = byte_size.next_power_of_two();
@ -196,15 +246,16 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
FreeObject {
ref timestamp,
kind: FreeObjectKind::IndexBuffer { ref allocation, .. },
} if allocation.size == byte_size &&
(now - *timestamp).as_secs_f32() >= REUSE_TIME => {}
} if allocation.size == byte_size
&& (now - *timestamp).as_secs_f32() >= REUSE_TIME => {}
_ => continue,
}
let (id, mut allocation) = match self.free_objects.remove(free_object_index) {
Some(FreeObject { kind: FreeObjectKind::IndexBuffer { id, allocation }, .. }) => {
(id, allocation)
}
Some(FreeObject {
kind: FreeObjectKind::IndexBuffer { id, allocation },
..
}) => (id, allocation),
_ => unreachable!(),
};
@ -215,33 +266,45 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
}
let buffer = device.create_buffer(BufferUploadMode::Dynamic);
device.allocate_buffer::<u8>(&buffer,
BufferData::Uninitialized(byte_size as usize),
BufferTarget::Index);
device.allocate_buffer::<u8>(
&buffer,
BufferData::Uninitialized(byte_size as usize),
BufferTarget::Index,
);
let id = self.next_index_buffer_id;
self.next_index_buffer_id.0 += 1;
debug!("mapping index buffer: {:?} {} ({}x{}) {:?}",
id,
byte_size,
size,
mem::size_of::<T>(),
tag);
debug!(
"mapping index buffer: {:?} {} ({}x{}) {:?}",
id,
byte_size,
size,
mem::size_of::<T>(),
tag
);
self.index_buffers_in_use.insert(id, BufferAllocation { buffer, size: byte_size, tag });
self.index_buffers_in_use.insert(
id,
BufferAllocation {
buffer,
size: byte_size,
tag,
},
);
self.bytes_allocated += byte_size;
self.bytes_committed += byte_size;
id
}
pub fn allocate_texture(&mut self,
device: &D,
size: Vector2I,
format: TextureFormat,
tag: TextureTag)
-> TextureID {
pub fn allocate_texture(
&mut self,
device: &D,
size: Vector2I,
format: TextureFormat,
tag: TextureTag,
) -> TextureID {
let descriptor = TextureDescriptor {
width: size.x() as u32,
height: size.y() as u32,
@ -251,15 +314,18 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
for free_object_index in 0..self.free_objects.len() {
match self.free_objects[free_object_index] {
FreeObject { kind: FreeObjectKind::Texture { ref allocation, .. }, .. } if
allocation.descriptor == descriptor => {}
FreeObject {
kind: FreeObjectKind::Texture { ref allocation, .. },
..
} if allocation.descriptor == descriptor => {}
_ => continue,
}
let (id, mut allocation) = match self.free_objects.remove(free_object_index) {
Some(FreeObject { kind: FreeObjectKind::Texture { id, allocation }, .. }) => {
(id, allocation)
}
Some(FreeObject {
kind: FreeObjectKind::Texture { id, allocation },
..
}) => (id, allocation),
_ => unreachable!(),
};
@ -275,7 +341,14 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
let id = self.next_texture_id;
self.next_texture_id.0 += 1;
self.textures_in_use.insert(id, TextureAllocation { texture, descriptor, tag });
self.textures_in_use.insert(
id,
TextureAllocation {
texture,
descriptor,
tag,
},
);
self.bytes_allocated += byte_size;
self.bytes_committed += byte_size;
@ -283,12 +356,13 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
id
}
pub fn allocate_framebuffer(&mut self,
device: &D,
size: Vector2I,
format: TextureFormat,
tag: FramebufferTag)
-> FramebufferID {
pub fn allocate_framebuffer(
&mut self,
device: &D,
size: Vector2I,
format: TextureFormat,
tag: FramebufferTag,
) -> FramebufferID {
let descriptor = TextureDescriptor {
width: size.x() as u32,
height: size.y() as u32,
@ -298,15 +372,16 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
for free_object_index in 0..self.free_objects.len() {
match self.free_objects[free_object_index].kind {
FreeObjectKind::Framebuffer { ref allocation, .. } if allocation.descriptor ==
descriptor => {}
FreeObjectKind::Framebuffer { ref allocation, .. }
if allocation.descriptor == descriptor => {}
_ => continue,
}
let (id, mut allocation) = match self.free_objects.remove(free_object_index) {
Some(FreeObject { kind: FreeObjectKind::Framebuffer { id, allocation }, .. }) => {
(id, allocation)
}
Some(FreeObject {
kind: FreeObjectKind::Framebuffer { id, allocation },
..
}) => (id, allocation),
_ => unreachable!(),
};
@ -323,11 +398,14 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
let id = self.next_framebuffer_id;
self.next_framebuffer_id.0 += 1;
self.framebuffers_in_use.insert(id, FramebufferAllocation {
framebuffer,
descriptor,
tag,
});
self.framebuffers_in_use.insert(
id,
FramebufferAllocation {
framebuffer,
descriptor,
tag,
},
);
self.bytes_allocated += byte_size;
self.bytes_committed += byte_size;
@ -339,8 +417,8 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
let now = Instant::now();
loop {
match self.free_objects.front() {
Some(FreeObject { timestamp, .. }) if (now - *timestamp).as_secs_f32() >=
DECAY_TIME => {}
Some(FreeObject { timestamp, .. })
if (now - *timestamp).as_secs_f32() >= DECAY_TIME => {}
_ => break,
}
match self.free_objects.pop_front() {
@ -352,15 +430,24 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
debug!("purging general buffer: {}", allocation.size);
self.bytes_allocated -= allocation.size;
}
Some(FreeObject { kind: FreeObjectKind::IndexBuffer { allocation, .. }, .. }) => {
Some(FreeObject {
kind: FreeObjectKind::IndexBuffer { allocation, .. },
..
}) => {
debug!("purging index buffer: {}", allocation.size);
self.bytes_allocated -= allocation.size;
}
Some(FreeObject { kind: FreeObjectKind::Texture { allocation, .. }, .. }) => {
Some(FreeObject {
kind: FreeObjectKind::Texture { allocation, .. },
..
}) => {
debug!("purging texture: {:?}", allocation.descriptor);
self.bytes_allocated -= allocation.descriptor.byte_size();
}
Some(FreeObject { kind: FreeObjectKind::Framebuffer { allocation, .. }, .. }) => {
Some(FreeObject {
kind: FreeObjectKind::Framebuffer { allocation, .. },
..
}) => {
debug!("purging framebuffer: {:?}", allocation.descriptor);
self.bytes_allocated -= allocation.descriptor.byte_size();
}
@ -369,9 +456,10 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
}
pub fn free_general_buffer(&mut self, id: GeneralBufferID) {
let allocation = self.general_buffers_in_use
.remove(&id)
.expect("Attempted to free unallocated general buffer!");
let allocation = self
.general_buffers_in_use
.remove(&id)
.expect("Attempted to free unallocated general buffer!");
self.bytes_committed -= allocation.size;
self.free_objects.push_back(FreeObject {
timestamp: Instant::now(),
@ -380,9 +468,10 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
}
pub fn free_index_buffer(&mut self, id: IndexBufferID) {
let allocation = self.index_buffers_in_use
.remove(&id)
.expect("Attempted to free unallocated index buffer!");
let allocation = self
.index_buffers_in_use
.remove(&id)
.expect("Attempted to free unallocated index buffer!");
self.bytes_committed -= allocation.size;
self.free_objects.push_back(FreeObject {
timestamp: Instant::now(),
@ -391,9 +480,10 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
}
pub fn free_texture(&mut self, id: TextureID) {
let allocation = self.textures_in_use
.remove(&id)
.expect("Attempted to free unallocated texture!");
let allocation = self
.textures_in_use
.remove(&id)
.expect("Attempted to free unallocated texture!");
let byte_size = allocation.descriptor.byte_size();
self.bytes_committed -= byte_size;
self.free_objects.push_back(FreeObject {
@ -403,9 +493,10 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
}
pub fn free_framebuffer(&mut self, id: FramebufferID) {
let allocation = self.framebuffers_in_use
.remove(&id)
.expect("Attempted to free unallocated framebuffer!");
let allocation = self
.framebuffers_in_use
.remove(&id)
.expect("Attempted to free unallocated framebuffer!");
let byte_size = allocation.descriptor.byte_size();
self.bytes_committed -= byte_size;
self.free_objects.push_back(FreeObject {
@ -450,7 +541,10 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
ids.sort();
for id in ids {
let allocation = &self.general_buffers_in_use[&id];
println!("id {:?}: {:?} ({:?} B)", id, allocation.tag, allocation.size);
println!(
"id {:?}: {:?} ({:?} B)",
id, allocation.tag, allocation.size
);
}
println!("Index buffers:");
@ -458,7 +552,10 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
ids.sort();
for id in ids {
let allocation = &self.index_buffers_in_use[&id];
println!("id {:?}: {:?} ({:?} B)", id, allocation.tag, allocation.size);
println!(
"id {:?}: {:?} ({:?} B)",
id, allocation.tag, allocation.size
);
}
println!("Textures:");
@ -466,13 +563,15 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
ids.sort();
for id in ids {
let allocation = &self.textures_in_use[&id];
println!("id {:?}: {:?} {:?}x{:?} {:?} ({:?} B)",
id,
allocation.tag,
allocation.descriptor.width,
allocation.descriptor.height,
allocation.descriptor.format,
allocation.descriptor.byte_size());
println!(
"id {:?}: {:?} {:?}x{:?} {:?} ({:?} B)",
id,
allocation.tag,
allocation.descriptor.width,
allocation.descriptor.height,
allocation.descriptor.format,
allocation.descriptor.byte_size()
);
}
println!("Framebuffers:");
@ -480,13 +579,15 @@ impl<D> GPUMemoryAllocator<D> where D: Device {
ids.sort();
for id in ids {
let allocation = &self.framebuffers_in_use[&id];
println!("id {:?}: {:?} {:?}x{:?} {:?} ({:?} B)",
id,
allocation.tag,
allocation.descriptor.width,
allocation.descriptor.height,
allocation.descriptor.format,
allocation.descriptor.byte_size());
println!(
"id {:?}: {:?} {:?}x{:?} {:?} ({:?} B)",
id,
allocation.tag,
allocation.descriptor.width,
allocation.descriptor.height,
allocation.descriptor.format,
allocation.descriptor.byte_size()
);
}
}
}

View File

@ -22,7 +22,7 @@ use image::ImageFormat;
use pathfinder_color::ColorF;
use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::transform3d::Transform4F;
use pathfinder_geometry::vector::{Vector2I, vec2i};
use pathfinder_geometry::vector::{vec2i, Vector2I};
use pathfinder_resources::ResourceLoader;
use pathfinder_simd::default::{F32x2, F32x4, I32x2};
use std::ops::Range;
@ -50,64 +50,95 @@ pub trait Device: Sized {
fn device_name(&self) -> String;
fn feature_level(&self) -> FeatureLevel;
fn create_texture(&self, format: TextureFormat, size: Vector2I) -> Self::Texture;
fn create_texture_from_data(&self, format: TextureFormat, size: Vector2I, data: TextureDataRef)
-> Self::Texture;
fn create_shader(&self, resources: &dyn ResourceLoader, name: &str, kind: ShaderKind)
-> Self::Shader;
fn create_shader_from_source(&self, name: &str, source: &[u8], kind: ShaderKind)
-> Self::Shader;
fn create_texture_from_data(
&self,
format: TextureFormat,
size: Vector2I,
data: TextureDataRef,
) -> Self::Texture;
fn create_shader(
&self,
resources: &dyn ResourceLoader,
name: &str,
kind: ShaderKind,
) -> Self::Shader;
fn create_shader_from_source(
&self,
name: &str,
source: &[u8],
kind: ShaderKind,
) -> Self::Shader;
fn create_vertex_array(&self) -> Self::VertexArray;
fn create_program_from_shaders(&self,
resources: &dyn ResourceLoader,
name: &str,
shaders: ProgramKind<Self::Shader>)
-> Self::Program;
fn set_compute_program_local_size(&self,
program: &mut Self::Program,
local_size: ComputeDimensions);
fn create_program_from_shaders(
&self,
resources: &dyn ResourceLoader,
name: &str,
shaders: ProgramKind<Self::Shader>,
) -> Self::Program;
fn set_compute_program_local_size(
&self,
program: &mut Self::Program,
local_size: ComputeDimensions,
);
fn get_vertex_attr(&self, program: &Self::Program, name: &str) -> Option<Self::VertexAttr>;
fn get_uniform(&self, program: &Self::Program, name: &str) -> Self::Uniform;
fn get_texture_parameter(&self, program: &Self::Program, name: &str) -> Self::TextureParameter;
fn get_image_parameter(&self, program: &Self::Program, name: &str) -> Self::ImageParameter;
fn get_storage_buffer(&self, program: &Self::Program, name: &str, binding: u32)
-> Self::StorageBuffer;
fn bind_buffer(&self,
vertex_array: &Self::VertexArray,
buffer: &Self::Buffer,
target: BufferTarget);
fn configure_vertex_attr(&self,
vertex_array: &Self::VertexArray,
attr: &Self::VertexAttr,
descriptor: &VertexAttrDescriptor);
fn get_storage_buffer(
&self,
program: &Self::Program,
name: &str,
binding: u32,
) -> Self::StorageBuffer;
fn bind_buffer(
&self,
vertex_array: &Self::VertexArray,
buffer: &Self::Buffer,
target: BufferTarget,
);
fn configure_vertex_attr(
&self,
vertex_array: &Self::VertexArray,
attr: &Self::VertexAttr,
descriptor: &VertexAttrDescriptor,
);
fn create_framebuffer(&self, texture: Self::Texture) -> Self::Framebuffer;
fn create_buffer(&self, mode: BufferUploadMode) -> Self::Buffer;
fn allocate_buffer<T>(&self,
buffer: &Self::Buffer,
data: BufferData<T>,
target: BufferTarget);
fn upload_to_buffer<T>(&self,
buffer: &Self::Buffer,
position: usize,
data: &[T],
target: BufferTarget);
fn allocate_buffer<T>(&self, buffer: &Self::Buffer, data: BufferData<T>, target: BufferTarget);
fn upload_to_buffer<T>(
&self,
buffer: &Self::Buffer,
position: usize,
data: &[T],
target: BufferTarget,
);
fn framebuffer_texture<'f>(&self, framebuffer: &'f Self::Framebuffer) -> &'f Self::Texture;
fn destroy_framebuffer(&self, framebuffer: Self::Framebuffer) -> Self::Texture;
fn texture_format(&self, texture: &Self::Texture) -> TextureFormat;
fn texture_size(&self, texture: &Self::Texture) -> Vector2I;
fn set_texture_sampling_mode(&self, texture: &Self::Texture, flags: TextureSamplingFlags);
fn upload_to_texture(&self, texture: &Self::Texture, rect: RectI, data: TextureDataRef);
fn read_pixels(&self, target: &RenderTarget<Self>, viewport: RectI)
-> Self::TextureDataReceiver;
fn read_buffer(&self, buffer: &Self::Buffer, target: BufferTarget, range: Range<usize>)
-> Self::BufferDataReceiver;
fn read_pixels(
&self,
target: &RenderTarget<Self>,
viewport: RectI,
) -> Self::TextureDataReceiver;
fn read_buffer(
&self,
buffer: &Self::Buffer,
target: BufferTarget,
range: Range<usize>,
) -> Self::BufferDataReceiver;
fn begin_commands(&self);
fn end_commands(&self);
fn draw_arrays(&self, index_count: u32, render_state: &RenderState<Self>);
fn draw_elements(&self, index_count: u32, render_state: &RenderState<Self>);
fn draw_elements_instanced(&self,
index_count: u32,
instance_count: u32,
render_state: &RenderState<Self>);
fn draw_elements_instanced(
&self,
index_count: u32,
instance_count: u32,
render_state: &RenderState<Self>,
);
fn dispatch_compute(&self, dimensions: ComputeDimensions, state: &ComputeState<Self>);
fn add_fence(&self) -> Self::Fence;
fn wait_for_fence(&self, fence: &Self::Fence);
@ -121,11 +152,12 @@ pub trait Device: Sized {
fn try_recv_buffer(&self, receiver: &Self::BufferDataReceiver) -> Option<Vec<u8>>;
fn recv_buffer(&self, receiver: &Self::BufferDataReceiver) -> Vec<u8>;
fn create_texture_from_png(&self,
resources: &dyn ResourceLoader,
name: &str,
format: TextureFormat)
-> Self::Texture {
fn create_texture_from_png(
&self,
resources: &dyn ResourceLoader,
name: &str,
format: TextureFormat,
) -> Self::Texture {
let data = resources.slurp(&format!("textures/{}.png", name)).unwrap();
let image = image::load_from_memory_with_format(&data, ImageFormat::Png).unwrap();
match format {
@ -143,11 +175,13 @@ pub trait Device: Sized {
}
}
fn upload_png_to_texture(&self,
resources: &dyn ResourceLoader,
name: &str,
texture: &Self::Texture,
format: TextureFormat) {
fn upload_png_to_texture(
&self,
resources: &dyn ResourceLoader,
name: &str,
texture: &Self::Texture,
format: TextureFormat,
) {
let data = resources.slurp(&format!("textures/{}.png", name)).unwrap();
let image = image::load_from_memory_with_format(&data, ImageFormat::Png).unwrap();
match format {
@ -174,12 +208,10 @@ pub trait Device: Sized {
shader_names: ProgramKind<&str>,
) -> Self::Program {
let shaders = match shader_names {
ProgramKind::Raster { vertex, fragment } => {
ProgramKind::Raster {
vertex: self.create_shader(resources, vertex, ShaderKind::Vertex),
fragment: self.create_shader(resources, fragment, ShaderKind::Fragment),
}
}
ProgramKind::Raster { vertex, fragment } => ProgramKind::Raster {
vertex: self.create_shader(resources, vertex, ShaderKind::Vertex),
fragment: self.create_shader(resources, fragment, ShaderKind::Fragment),
},
ProgramKind::Compute(compute) => {
ProgramKind::Compute(self.create_shader(resources, compute, ShaderKind::Compute))
}
@ -188,7 +220,10 @@ pub trait Device: Sized {
}
fn create_raster_program(&self, resources: &dyn ResourceLoader, name: &str) -> Self::Program {
let shaders = ProgramKind::Raster { vertex: name, fragment: name };
let shaders = ProgramKind::Raster {
vertex: name,
fragment: name,
};
self.create_program_from_shader_names(resources, name, shaders)
}
@ -253,10 +288,7 @@ pub enum ShaderKind {
#[derive(Clone, Copy, Debug)]
pub enum ProgramKind<T> {
Raster {
vertex: T,
fragment: T,
},
Raster { vertex: T, fragment: T },
Compute(T),
}
@ -287,7 +319,10 @@ pub enum Primitive {
}
#[derive(Clone)]
pub struct RenderState<'a, D> where D: Device {
pub struct RenderState<'a, D>
where
D: Device,
{
pub target: &'a RenderTarget<'a, D>,
pub program: &'a D::Program,
pub vertex_array: &'a D::VertexArray,
@ -301,7 +336,10 @@ pub struct RenderState<'a, D> where D: Device {
}
#[derive(Clone)]
pub struct ComputeState<'a, D> where D: Device {
pub struct ComputeState<'a, D>
where
D: Device,
{
pub program: &'a D::Program,
pub uniforms: &'a [UniformBinding<'a, D::Uniform>],
pub textures: &'a [TextureBinding<'a, D::TextureParameter, D::Texture>],
@ -332,7 +370,10 @@ pub struct ClearOps {
}
#[derive(Clone, Copy, Debug)]
pub enum RenderTarget<'a, D> where D: Device {
pub enum RenderTarget<'a, D>
where
D: Device,
{
Default,
Framebuffer(&'a D::Framebuffer),
}
@ -537,8 +578,11 @@ pub enum ImageAccess {
impl<'a> TextureDataRef<'a> {
#[doc(hidden)]
pub fn check_and_extract_data_ptr(self, minimum_size: Vector2I, format: TextureFormat)
-> *const c_void {
pub fn check_and_extract_data_ptr(
self,
minimum_size: Vector2I,
format: TextureFormat,
) -> *const c_void {
let channels = match (format, self) {
(TextureFormat::R8, TextureDataRef::U8(_)) => 1,
(TextureFormat::RGBA8, TextureDataRef::U8(_)) => 4,

View File

@ -307,7 +307,10 @@ pub struct ShapeKeyframeProperty {
impl Lottie {
#[inline]
pub fn from_reader<R>(reader: R) -> Result<Lottie, JSONError> where R: Read {
pub fn from_reader<R>(reader: R) -> Result<Lottie, JSONError>
where
R: Read,
{
serde_json::from_reader(reader)
}
}

File diff suppressed because it is too large Load Diff

View File

@ -12,7 +12,7 @@
use crate::gpu_data::{TextureLocation, TexturePageId};
use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::vector::{Vector2F, Vector2I, vec2f, vec2i};
use pathfinder_geometry::vector::{vec2f, vec2i, Vector2F, Vector2I};
const ATLAS_TEXTURE_LENGTH: u32 = 1024;
@ -64,9 +64,10 @@ impl TextureAllocator {
pub fn allocate(&mut self, requested_size: Vector2I, mode: AllocationMode) -> TextureLocation {
// If requested, or if the image is too big, use a separate page.
if mode == AllocationMode::OwnPage ||
requested_size.x() > ATLAS_TEXTURE_LENGTH as i32 ||
requested_size.y() > ATLAS_TEXTURE_LENGTH as i32 {
if mode == AllocationMode::OwnPage
|| requested_size.x() > ATLAS_TEXTURE_LENGTH as i32
|| requested_size.y() > ATLAS_TEXTURE_LENGTH as i32
{
return self.allocate_image(requested_size);
}
@ -74,19 +75,17 @@ impl TextureAllocator {
let mut first_free_page_index = self.pages.len();
for (page_index, page) in self.pages.iter_mut().enumerate() {
match *page {
Some(ref mut page) => {
match page.allocator {
TexturePageAllocator::Image { .. } => {}
TexturePageAllocator::Atlas(ref mut allocator) => {
if let Some(rect) = allocator.allocate(requested_size) {
return TextureLocation {
page: TexturePageId(page_index as u32),
rect
};
}
Some(ref mut page) => match page.allocator {
TexturePageAllocator::Image { .. } => {}
TexturePageAllocator::Atlas(ref mut allocator) => {
if let Some(rect) = allocator.allocate(requested_size) {
return TextureLocation {
page: TexturePageId(page_index as u32),
rect,
};
}
}
}
},
None => first_free_page_index = first_free_page_index.min(page_index),
}
}
@ -94,7 +93,9 @@ impl TextureAllocator {
// Add a new atlas.
let page = self.get_first_free_page_id();
let mut allocator = TextureAtlasAllocator::new();
let rect = allocator.allocate(requested_size).expect("Allocation failed!");
let rect = allocator
.allocate(requested_size)
.expect("Allocation failed!");
while (page.0 as usize) >= self.pages.len() {
self.pages.push(None);
}
@ -130,9 +131,10 @@ impl TextureAllocator {
pub fn free(&mut self, location: TextureLocation) {
//println!("free({:?})", location);
match self.pages[location.page.0 as usize]
.as_mut()
.expect("Texture page is not allocated!")
.allocator {
.as_mut()
.expect("Texture page is not allocated!")
.allocator
{
TexturePageAllocator::Image { size } => {
debug_assert_eq!(location.rect, RectI::new(Vector2I::default(), size));
}
@ -151,7 +153,11 @@ impl TextureAllocator {
}
pub fn page_size(&self, page_id: TexturePageId) -> Vector2I {
match self.pages[page_id.0 as usize].as_ref().expect("No such texture page!").allocator {
match self.pages[page_id.0 as usize]
.as_ref()
.expect("No such texture page!")
.allocator
{
TexturePageAllocator::Atlas(ref atlas) => Vector2I::splat(atlas.size as i32),
TexturePageAllocator::Image { size, .. } => size,
}
@ -162,7 +168,10 @@ impl TextureAllocator {
}
pub fn page_is_new(&self, page_id: TexturePageId) -> bool {
self.pages[page_id.0 as usize].as_ref().expect("No such texture page!").is_new
self.pages[page_id.0 as usize]
.as_ref()
.expect("No such texture page!")
.is_new
}
pub fn mark_all_pages_as_allocated(&mut self) {
@ -178,7 +187,10 @@ impl TextureAllocator {
while first_index < self.pages.len() && self.pages[first_index].is_none() {
first_index += 1;
}
TexturePageIter { allocator: self, next_index: first_index }
TexturePageIter {
allocator: self,
next_index: first_index,
}
}
}
@ -190,20 +202,29 @@ impl TextureAtlasAllocator {
#[inline]
fn with_length(length: u32) -> TextureAtlasAllocator {
TextureAtlasAllocator { root: TreeNode::EmptyLeaf, size: length }
TextureAtlasAllocator {
root: TreeNode::EmptyLeaf,
size: length,
}
}
#[inline]
fn allocate(&mut self, requested_size: Vector2I) -> Option<RectI> {
let requested_length =
(requested_size.x().max(requested_size.y()) as u32).next_power_of_two();
self.root.allocate(Vector2I::default(), self.size, requested_length)
self.root
.allocate(Vector2I::default(), self.size, requested_length)
}
#[inline]
fn free(&mut self, rect: RectI) {
let requested_length = rect.width() as u32;
self.root.free(Vector2I::default(), self.size, rect.origin(), requested_length)
self.root.free(
Vector2I::default(),
self.size,
rect.origin(),
requested_length,
)
}
#[inline]
@ -218,8 +239,12 @@ impl TextureAtlasAllocator {
impl TreeNode {
// Invariant: `requested_size` must be a power of two.
fn allocate(&mut self, this_origin: Vector2I, this_size: u32, requested_size: u32)
-> Option<RectI> {
fn allocate(
&mut self,
this_origin: Vector2I,
this_size: u32,
requested_size: u32,
) -> Option<RectI> {
if let TreeNode::FullLeaf = *self {
// No room here.
return None;
@ -253,19 +278,23 @@ impl TreeNode {
if let Some(origin) = kids[0].allocate(this_origin, kid_size, requested_size) {
return Some(origin);
}
if let Some(origin) = kids[1].allocate(this_origin + vec2i(kid_size as i32, 0),
kid_size,
requested_size) {
if let Some(origin) = kids[1].allocate(
this_origin + vec2i(kid_size as i32, 0),
kid_size,
requested_size,
) {
return Some(origin);
}
if let Some(origin) = kids[2].allocate(this_origin + vec2i(0, kid_size as i32),
kid_size,
requested_size) {
if let Some(origin) = kids[2].allocate(
this_origin + vec2i(0, kid_size as i32),
kid_size,
requested_size,
) {
return Some(origin);
}
if let Some(origin) = kids[3].allocate(this_origin + kid_size as i32,
kid_size,
requested_size) {
if let Some(origin) =
kids[3].allocate(this_origin + kid_size as i32, kid_size, requested_size)
{
return Some(origin);
}
@ -277,11 +306,13 @@ impl TreeNode {
}
#[allow(dead_code)]
fn free(&mut self,
this_origin: Vector2I,
this_size: u32,
requested_origin: Vector2I,
requested_size: u32) {
fn free(
&mut self,
this_origin: Vector2I,
this_size: u32,
requested_origin: Vector2I,
requested_size: u32,
) {
if this_size <= requested_size {
if this_size == requested_size && this_origin == requested_origin {
*self = TreeNode::EmptyLeaf;
@ -324,11 +355,9 @@ impl TreeNode {
fn merge_if_necessary(&mut self) {
match *self {
TreeNode::Parent(ref mut kids) => {
if kids.iter().all(|kid| {
match **kid {
TreeNode::EmptyLeaf => true,
_ => false,
}
if kids.iter().all(|kid| match **kid {
TreeNode::EmptyLeaf => true,
_ => false,
}) {
*self = TreeNode::EmptyLeaf;
}
@ -353,8 +382,9 @@ impl<'a> Iterator for TexturePageIter<'a> {
};
loop {
self.next_index += 1;
if self.next_index >= self.allocator.pages.len() ||
self.allocator.pages[self.next_index as usize].is_some() {
if self.next_index >= self.allocator.pages.len()
|| self.allocator.pages[self.next_index as usize].is_some()
{
break;
}
}
@ -372,8 +402,9 @@ mod test {
#[test]
fn test_allocation_and_freeing() {
quickcheck::quickcheck(prop_allocation_and_freeing_work as
fn(u32, Vec<(u32, u32)>) -> bool);
quickcheck::quickcheck(
prop_allocation_and_freeing_work as fn(u32, Vec<(u32, u32)>) -> bool,
);
fn prop_allocation_and_freeing_work(mut length: u32, mut sizes: Vec<(u32, u32)>) -> bool {
length = u32::next_power_of_two(length).max(1);

File diff suppressed because it is too large Load Diff

View File

@ -18,7 +18,9 @@ pub trait Executor {
/// (0..length).into_par_iter().map(builder).collect()
/// ```
fn build_vector<T, F>(&self, length: usize, builder: F) -> Vec<T>
where T: Send, F: Fn(usize) -> T + Send + Sync;
where
T: Send,
F: Fn(usize) -> T + Send + Sync;
}
/// An executor that simply executes tasks sequentially in the same thread.
@ -26,7 +28,10 @@ pub struct SequentialExecutor;
impl Executor for SequentialExecutor {
fn build_vector<T, F>(&self, length: usize, builder: F) -> Vec<T>
where T: Send, F: Fn(usize) -> T + Send + Sync {
where
T: Send,
F: Fn(usize) -> T + Send + Sync,
{
(0..length).into_iter().map(builder).collect()
}
}

View File

@ -18,7 +18,10 @@ pub struct RayonExecutor;
impl Executor for RayonExecutor {
fn build_vector<T, F>(&self, length: usize, builder: F) -> Vec<T>
where T: Send, F: Fn(usize) -> T + Send + Sync {
where
T: Send,
F: Fn(usize) -> T + Send + Sync,
{
(0..length).into_par_iter().map(builder).collect()
}
}

View File

@ -41,24 +41,27 @@ pub struct SceneProxy {
impl SceneProxy {
/// Creates a new scene proxy using the given renderer GPU API level and executor to execute
/// CPU tasks.
///
///
/// If you want to use multiple threads, you typically pass in a `RayonExecutor` here. If you
/// want to use a single thread (perhaps because you're in a WebAssembly environment), pass a
/// `SequentialExecutor`.
pub fn new<E>(renderer_level: RendererLevel, executor: E) -> SceneProxy
where E: Executor + Send + 'static {
where
E: Executor + Send + 'static,
{
SceneProxy::from_scene(Scene::new(), renderer_level, executor)
}
/// Wraps an existing scene in a scene proxy using the given renderer GPU API level an executor
/// to execute CPU tasks.
///
///
/// If you want to use multiple threads, you typically pass in a `RayonExecutor` here. If you
/// want to use a single thread (perhaps because you're in a WebAssembly environment), pass a
/// `SequentialExecutor`.
pub fn from_scene<E>(scene: Scene, renderer_level: RendererLevel, executor: E)
-> SceneProxy
where E: Executor + Send + 'static {
pub fn from_scene<E>(scene: Scene, renderer_level: RendererLevel, executor: E) -> SceneProxy
where
E: Executor + Send + 'static,
{
let (main_to_worker_sender, main_to_worker_receiver) =
flume::bounded(MAX_MESSAGES_IN_FLIGHT);
let (worker_to_main_sender, worker_to_main_receiver) =
@ -68,19 +71,26 @@ impl SceneProxy {
}));
let sink = SceneSink::new(listener, renderer_level);
thread::spawn(move || scene_thread(scene, executor, sink, main_to_worker_receiver));
SceneProxy { sender: main_to_worker_sender, receiver: worker_to_main_receiver }
SceneProxy {
sender: main_to_worker_sender,
receiver: worker_to_main_receiver,
}
}
/// Replaces the wrapped scene with a new one, discarding the old scene.
#[inline]
pub fn replace_scene(&self, new_scene: Scene) {
self.sender.send(MainToWorkerMsg::ReplaceScene(new_scene)).unwrap();
self.sender
.send(MainToWorkerMsg::ReplaceScene(new_scene))
.unwrap();
}
/// Sets the view box of the scene, which defines the visible rectangle.
#[inline]
pub fn set_view_box(&self, new_view_box: RectF) {
self.sender.send(MainToWorkerMsg::SetViewBox(new_view_box)).unwrap();
self.sender
.send(MainToWorkerMsg::SetViewBox(new_view_box))
.unwrap();
}
/// Constructs a scene and queues up the commands needed to render it.
@ -91,7 +101,10 @@ impl SceneProxy {
/// Sends all queued commands to the given renderer to render the wrapped scene.
#[inline]
pub fn render<D>(&mut self, renderer: &mut Renderer<D>) where D: Device {
pub fn render<D>(&mut self, renderer: &mut Renderer<D>)
where
D: Device,
{
renderer.begin_scene();
while let Ok(command) = self.receiver.recv() {
renderer.render_command(&command);
@ -114,7 +127,9 @@ impl SceneProxy {
/// ```
#[inline]
pub fn build_and_render<D>(&mut self, renderer: &mut Renderer<D>, build_options: BuildOptions)
where D: Device {
where
D: Device,
{
self.build(build_options);
self.render(renderer);
}
@ -123,16 +138,21 @@ impl SceneProxy {
#[inline]
pub fn copy_scene(&self) -> Scene {
let (sender, receiver) = flume::bounded(MAX_MESSAGES_IN_FLIGHT);
self.sender.send(MainToWorkerMsg::CopyScene(sender)).unwrap();
self.sender
.send(MainToWorkerMsg::CopyScene(sender))
.unwrap();
receiver.recv().unwrap()
}
}
fn scene_thread<E>(mut scene: Scene,
executor: E,
mut sink: SceneSink<'static>,
main_to_worker_receiver: Receiver<MainToWorkerMsg>)
where E: Executor {
fn scene_thread<E>(
mut scene: Scene,
executor: E,
mut sink: SceneSink<'static>,
main_to_worker_receiver: Receiver<MainToWorkerMsg>,
) where
E: Executor,
{
while let Ok(msg) = main_to_worker_receiver.recv() {
match msg {
MainToWorkerMsg::ReplaceScene(new_scene) => scene = new_scene,

View File

@ -15,25 +15,25 @@ use crate::paint::PaintCompositeOp;
use pathfinder_content::effects::BlendMode;
use pathfinder_gpu::{BlendFactor, BlendState};
const COMBINER_CTRL_COLOR_COMBINE_SRC_IN: i32 = 0x1;
const COMBINER_CTRL_COLOR_COMBINE_SRC_IN: i32 = 0x1;
const COMBINER_CTRL_COLOR_COMBINE_DEST_IN: i32 = 0x2;
const COMBINER_CTRL_COMPOSITE_NORMAL: i32 = 0x0;
const COMBINER_CTRL_COMPOSITE_MULTIPLY: i32 = 0x1;
const COMBINER_CTRL_COMPOSITE_SCREEN: i32 = 0x2;
const COMBINER_CTRL_COMPOSITE_OVERLAY: i32 = 0x3;
const COMBINER_CTRL_COMPOSITE_DARKEN: i32 = 0x4;
const COMBINER_CTRL_COMPOSITE_LIGHTEN: i32 = 0x5;
const COMBINER_CTRL_COMPOSITE_COLOR_DODGE: i32 = 0x6;
const COMBINER_CTRL_COMPOSITE_COLOR_BURN: i32 = 0x7;
const COMBINER_CTRL_COMPOSITE_HARD_LIGHT: i32 = 0x8;
const COMBINER_CTRL_COMPOSITE_SOFT_LIGHT: i32 = 0x9;
const COMBINER_CTRL_COMPOSITE_DIFFERENCE: i32 = 0xa;
const COMBINER_CTRL_COMPOSITE_EXCLUSION: i32 = 0xb;
const COMBINER_CTRL_COMPOSITE_HUE: i32 = 0xc;
const COMBINER_CTRL_COMPOSITE_SATURATION: i32 = 0xd;
const COMBINER_CTRL_COMPOSITE_COLOR: i32 = 0xe;
const COMBINER_CTRL_COMPOSITE_LUMINOSITY: i32 = 0xf;
const COMBINER_CTRL_COMPOSITE_NORMAL: i32 = 0x0;
const COMBINER_CTRL_COMPOSITE_MULTIPLY: i32 = 0x1;
const COMBINER_CTRL_COMPOSITE_SCREEN: i32 = 0x2;
const COMBINER_CTRL_COMPOSITE_OVERLAY: i32 = 0x3;
const COMBINER_CTRL_COMPOSITE_DARKEN: i32 = 0x4;
const COMBINER_CTRL_COMPOSITE_LIGHTEN: i32 = 0x5;
const COMBINER_CTRL_COMPOSITE_COLOR_DODGE: i32 = 0x6;
const COMBINER_CTRL_COMPOSITE_COLOR_BURN: i32 = 0x7;
const COMBINER_CTRL_COMPOSITE_HARD_LIGHT: i32 = 0x8;
const COMBINER_CTRL_COMPOSITE_SOFT_LIGHT: i32 = 0x9;
const COMBINER_CTRL_COMPOSITE_DIFFERENCE: i32 = 0xa;
const COMBINER_CTRL_COMPOSITE_EXCLUSION: i32 = 0xb;
const COMBINER_CTRL_COMPOSITE_HUE: i32 = 0xc;
const COMBINER_CTRL_COMPOSITE_SATURATION: i32 = 0xd;
const COMBINER_CTRL_COMPOSITE_COLOR: i32 = 0xe;
const COMBINER_CTRL_COMPOSITE_LUMINOSITY: i32 = 0xf;
pub(crate) trait ToBlendState {
fn to_blend_state(self) -> Option<BlendState>;
@ -42,121 +42,99 @@ pub(crate) trait ToBlendState {
impl ToBlendState for BlendMode {
fn to_blend_state(self) -> Option<BlendState> {
match self {
BlendMode::Clear => {
Some(BlendState {
src_rgb_factor: BlendFactor::Zero,
dest_rgb_factor: BlendFactor::Zero,
src_alpha_factor: BlendFactor::Zero,
dest_alpha_factor: BlendFactor::Zero,
..BlendState::default()
})
}
BlendMode::SrcOver => {
Some(BlendState {
src_rgb_factor: BlendFactor::One,
dest_rgb_factor: BlendFactor::OneMinusSrcAlpha,
src_alpha_factor: BlendFactor::One,
dest_alpha_factor: BlendFactor::OneMinusSrcAlpha,
..BlendState::default()
})
}
BlendMode::DestOver => {
Some(BlendState {
src_rgb_factor: BlendFactor::OneMinusDestAlpha,
dest_rgb_factor: BlendFactor::One,
src_alpha_factor: BlendFactor::OneMinusDestAlpha,
dest_alpha_factor: BlendFactor::One,
..BlendState::default()
})
}
BlendMode::SrcIn => {
Some(BlendState {
src_rgb_factor: BlendFactor::DestAlpha,
dest_rgb_factor: BlendFactor::Zero,
src_alpha_factor: BlendFactor::DestAlpha,
dest_alpha_factor: BlendFactor::Zero,
..BlendState::default()
})
}
BlendMode::DestIn => {
Some(BlendState {
src_rgb_factor: BlendFactor::Zero,
dest_rgb_factor: BlendFactor::SrcAlpha,
src_alpha_factor: BlendFactor::Zero,
dest_alpha_factor: BlendFactor::SrcAlpha,
..BlendState::default()
})
}
BlendMode::SrcOut => {
Some(BlendState {
src_rgb_factor: BlendFactor::OneMinusDestAlpha,
dest_rgb_factor: BlendFactor::Zero,
src_alpha_factor: BlendFactor::OneMinusDestAlpha,
dest_alpha_factor: BlendFactor::Zero,
..BlendState::default()
})
}
BlendMode::DestOut => {
Some(BlendState {
src_rgb_factor: BlendFactor::Zero,
dest_rgb_factor: BlendFactor::OneMinusSrcAlpha,
src_alpha_factor: BlendFactor::Zero,
dest_alpha_factor: BlendFactor::OneMinusSrcAlpha,
..BlendState::default()
})
}
BlendMode::SrcAtop => {
Some(BlendState {
src_rgb_factor: BlendFactor::DestAlpha,
dest_rgb_factor: BlendFactor::OneMinusSrcAlpha,
src_alpha_factor: BlendFactor::DestAlpha,
dest_alpha_factor: BlendFactor::OneMinusSrcAlpha,
..BlendState::default()
})
}
BlendMode::DestAtop => {
Some(BlendState {
src_rgb_factor: BlendFactor::OneMinusDestAlpha,
dest_rgb_factor: BlendFactor::SrcAlpha,
src_alpha_factor: BlendFactor::OneMinusDestAlpha,
dest_alpha_factor: BlendFactor::SrcAlpha,
..BlendState::default()
})
}
BlendMode::Xor => {
Some(BlendState {
src_rgb_factor: BlendFactor::OneMinusDestAlpha,
dest_rgb_factor: BlendFactor::OneMinusSrcAlpha,
src_alpha_factor: BlendFactor::OneMinusDestAlpha,
dest_alpha_factor: BlendFactor::OneMinusSrcAlpha,
..BlendState::default()
})
}
BlendMode::Lighter => {
Some(BlendState {
src_rgb_factor: BlendFactor::One,
dest_rgb_factor: BlendFactor::One,
src_alpha_factor: BlendFactor::One,
dest_alpha_factor: BlendFactor::One,
..BlendState::default()
})
}
BlendMode::Copy |
BlendMode::Darken |
BlendMode::Lighten |
BlendMode::Multiply |
BlendMode::Screen |
BlendMode::HardLight |
BlendMode::Overlay |
BlendMode::ColorDodge |
BlendMode::ColorBurn |
BlendMode::SoftLight |
BlendMode::Difference |
BlendMode::Exclusion |
BlendMode::Hue |
BlendMode::Saturation |
BlendMode::Color |
BlendMode::Luminosity => {
BlendMode::Clear => Some(BlendState {
src_rgb_factor: BlendFactor::Zero,
dest_rgb_factor: BlendFactor::Zero,
src_alpha_factor: BlendFactor::Zero,
dest_alpha_factor: BlendFactor::Zero,
..BlendState::default()
}),
BlendMode::SrcOver => Some(BlendState {
src_rgb_factor: BlendFactor::One,
dest_rgb_factor: BlendFactor::OneMinusSrcAlpha,
src_alpha_factor: BlendFactor::One,
dest_alpha_factor: BlendFactor::OneMinusSrcAlpha,
..BlendState::default()
}),
BlendMode::DestOver => Some(BlendState {
src_rgb_factor: BlendFactor::OneMinusDestAlpha,
dest_rgb_factor: BlendFactor::One,
src_alpha_factor: BlendFactor::OneMinusDestAlpha,
dest_alpha_factor: BlendFactor::One,
..BlendState::default()
}),
BlendMode::SrcIn => Some(BlendState {
src_rgb_factor: BlendFactor::DestAlpha,
dest_rgb_factor: BlendFactor::Zero,
src_alpha_factor: BlendFactor::DestAlpha,
dest_alpha_factor: BlendFactor::Zero,
..BlendState::default()
}),
BlendMode::DestIn => Some(BlendState {
src_rgb_factor: BlendFactor::Zero,
dest_rgb_factor: BlendFactor::SrcAlpha,
src_alpha_factor: BlendFactor::Zero,
dest_alpha_factor: BlendFactor::SrcAlpha,
..BlendState::default()
}),
BlendMode::SrcOut => Some(BlendState {
src_rgb_factor: BlendFactor::OneMinusDestAlpha,
dest_rgb_factor: BlendFactor::Zero,
src_alpha_factor: BlendFactor::OneMinusDestAlpha,
dest_alpha_factor: BlendFactor::Zero,
..BlendState::default()
}),
BlendMode::DestOut => Some(BlendState {
src_rgb_factor: BlendFactor::Zero,
dest_rgb_factor: BlendFactor::OneMinusSrcAlpha,
src_alpha_factor: BlendFactor::Zero,
dest_alpha_factor: BlendFactor::OneMinusSrcAlpha,
..BlendState::default()
}),
BlendMode::SrcAtop => Some(BlendState {
src_rgb_factor: BlendFactor::DestAlpha,
dest_rgb_factor: BlendFactor::OneMinusSrcAlpha,
src_alpha_factor: BlendFactor::DestAlpha,
dest_alpha_factor: BlendFactor::OneMinusSrcAlpha,
..BlendState::default()
}),
BlendMode::DestAtop => Some(BlendState {
src_rgb_factor: BlendFactor::OneMinusDestAlpha,
dest_rgb_factor: BlendFactor::SrcAlpha,
src_alpha_factor: BlendFactor::OneMinusDestAlpha,
dest_alpha_factor: BlendFactor::SrcAlpha,
..BlendState::default()
}),
BlendMode::Xor => Some(BlendState {
src_rgb_factor: BlendFactor::OneMinusDestAlpha,
dest_rgb_factor: BlendFactor::OneMinusSrcAlpha,
src_alpha_factor: BlendFactor::OneMinusDestAlpha,
dest_alpha_factor: BlendFactor::OneMinusSrcAlpha,
..BlendState::default()
}),
BlendMode::Lighter => Some(BlendState {
src_rgb_factor: BlendFactor::One,
dest_rgb_factor: BlendFactor::One,
src_alpha_factor: BlendFactor::One,
dest_alpha_factor: BlendFactor::One,
..BlendState::default()
}),
BlendMode::Copy
| BlendMode::Darken
| BlendMode::Lighten
| BlendMode::Multiply
| BlendMode::Screen
| BlendMode::HardLight
| BlendMode::Overlay
| BlendMode::ColorDodge
| BlendMode::ColorBurn
| BlendMode::SoftLight
| BlendMode::Difference
| BlendMode::Exclusion
| BlendMode::Hue
| BlendMode::Saturation
| BlendMode::Color
| BlendMode::Luminosity => {
// Blending is done manually in the shader.
None
}
@ -171,18 +149,18 @@ pub(crate) trait ToCompositeCtrl {
impl ToCompositeCtrl for BlendMode {
fn to_composite_ctrl(&self) -> i32 {
match *self {
BlendMode::SrcOver |
BlendMode::SrcAtop |
BlendMode::DestOver |
BlendMode::DestOut |
BlendMode::Xor |
BlendMode::Lighter |
BlendMode::Clear |
BlendMode::Copy |
BlendMode::SrcIn |
BlendMode::SrcOut |
BlendMode::DestIn |
BlendMode::DestAtop => COMBINER_CTRL_COMPOSITE_NORMAL,
BlendMode::SrcOver
| BlendMode::SrcAtop
| BlendMode::DestOver
| BlendMode::DestOut
| BlendMode::Xor
| BlendMode::Lighter
| BlendMode::Clear
| BlendMode::Copy
| BlendMode::SrcIn
| BlendMode::SrcOut
| BlendMode::DestIn
| BlendMode::DestAtop => COMBINER_CTRL_COMPOSITE_NORMAL,
BlendMode::Multiply => COMBINER_CTRL_COMPOSITE_MULTIPLY,
BlendMode::Darken => COMBINER_CTRL_COMPOSITE_DARKEN,
BlendMode::Lighten => COMBINER_CTRL_COMPOSITE_LIGHTEN,
@ -219,33 +197,33 @@ pub trait BlendModeExt {
impl BlendModeExt for BlendMode {
fn needs_readable_framebuffer(self) -> bool {
match self {
BlendMode::Clear |
BlendMode::SrcOver |
BlendMode::DestOver |
BlendMode::SrcIn |
BlendMode::DestIn |
BlendMode::SrcOut |
BlendMode::DestOut |
BlendMode::SrcAtop |
BlendMode::DestAtop |
BlendMode::Xor |
BlendMode::Lighter |
BlendMode::Copy => false,
BlendMode::Lighten |
BlendMode::Darken |
BlendMode::Multiply |
BlendMode::Screen |
BlendMode::HardLight |
BlendMode::Overlay |
BlendMode::ColorDodge |
BlendMode::ColorBurn |
BlendMode::SoftLight |
BlendMode::Difference |
BlendMode::Exclusion |
BlendMode::Hue |
BlendMode::Saturation |
BlendMode::Color |
BlendMode::Luminosity => true,
BlendMode::Clear
| BlendMode::SrcOver
| BlendMode::DestOver
| BlendMode::SrcIn
| BlendMode::DestIn
| BlendMode::SrcOut
| BlendMode::DestOut
| BlendMode::SrcAtop
| BlendMode::DestAtop
| BlendMode::Xor
| BlendMode::Lighter
| BlendMode::Copy => false,
BlendMode::Lighten
| BlendMode::Darken
| BlendMode::Multiply
| BlendMode::Screen
| BlendMode::HardLight
| BlendMode::Overlay
| BlendMode::ColorDodge
| BlendMode::ColorBurn
| BlendMode::SoftLight
| BlendMode::Difference
| BlendMode::Exclusion
| BlendMode::Hue
| BlendMode::Saturation
| BlendMode::Color
| BlendMode::Luminosity => true,
}
}
}

View File

@ -9,7 +9,7 @@
// except according to those terms.
//! A GPU compute-based renderer that uses functionality available in Direct3D 11.
//!
//!
//! This renderer supports OpenGL at least 4.3, OpenGL ES at least 3.1, and Metal of any version.
pub mod renderer;

File diff suppressed because it is too large Load Diff

View File

@ -21,7 +21,10 @@ pub(crate) const BIN_WORKGROUP_SIZE: u32 = 64;
pub(crate) const PROPAGATE_WORKGROUP_SIZE: u32 = 64;
pub(crate) const SORT_WORKGROUP_SIZE: u32 = 64;
pub(crate) struct ProgramsD3D11<D> where D: Device {
pub(crate) struct ProgramsD3D11<D>
where
D: Device,
{
pub(crate) bound_program: BoundProgramD3D11<D>,
pub(crate) dice_program: DiceProgramD3D11<D>,
pub(crate) bin_program: BinProgramD3D11<D>,
@ -31,7 +34,10 @@ pub(crate) struct ProgramsD3D11<D> where D: Device {
pub(crate) tile_program: TileProgramD3D11<D>,
}
impl<D> ProgramsD3D11<D> where D: Device {
impl<D> ProgramsD3D11<D>
where
D: Device,
{
pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> ProgramsD3D11<D> {
ProgramsD3D11 {
bound_program: BoundProgramD3D11::new(device, resources),
@ -45,7 +51,10 @@ impl<D> ProgramsD3D11<D> where D: Device {
}
}
pub(crate) struct PropagateProgramD3D11<D> where D: Device {
pub(crate) struct PropagateProgramD3D11<D>
where
D: Device,
{
pub(crate) program: D::Program,
pub(crate) framebuffer_tile_size_uniform: D::Uniform,
pub(crate) column_count_uniform: D::Uniform,
@ -60,10 +69,17 @@ pub(crate) struct PropagateProgramD3D11<D> where D: Device {
pub(crate) alpha_tiles_storage_buffer: D::StorageBuffer,
}
impl<D> PropagateProgramD3D11<D> where D: Device {
impl<D> PropagateProgramD3D11<D>
where
D: Device,
{
pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> PropagateProgramD3D11<D> {
let mut program = device.create_compute_program(resources, "d3d11/propagate");
let local_size = ComputeDimensions { x: PROPAGATE_WORKGROUP_SIZE, y: 1, z: 1 };
let local_size = ComputeDimensions {
x: PROPAGATE_WORKGROUP_SIZE,
y: 1,
z: 1,
};
device.set_compute_program_local_size(&mut program, local_size);
let framebuffer_tile_size_uniform = device.get_uniform(&program, "FramebufferTileSize");
@ -95,7 +111,10 @@ impl<D> PropagateProgramD3D11<D> where D: Device {
}
}
pub(crate) struct FillProgramD3D11<D> where D: Device {
pub(crate) struct FillProgramD3D11<D>
where
D: Device,
{
pub(crate) program: D::Program,
pub(crate) dest_image: D::ImageParameter,
pub(crate) area_lut_texture: D::TextureParameter,
@ -105,10 +124,17 @@ pub(crate) struct FillProgramD3D11<D> where D: Device {
pub(crate) alpha_tiles_storage_buffer: D::StorageBuffer,
}
impl<D> FillProgramD3D11<D> where D: Device {
impl<D> FillProgramD3D11<D>
where
D: Device,
{
pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> FillProgramD3D11<D> {
let mut program = device.create_compute_program(resources, "d3d11/fill");
let local_size = ComputeDimensions { x: TILE_WIDTH, y: TILE_HEIGHT / 4, z: 1 };
let local_size = ComputeDimensions {
x: TILE_WIDTH,
y: TILE_HEIGHT / 4,
z: 1,
};
device.set_compute_program_local_size(&mut program, local_size);
let dest_image = device.get_image_parameter(&program, "Dest");
@ -130,7 +156,10 @@ impl<D> FillProgramD3D11<D> where D: Device {
}
}
pub(crate) struct TileProgramD3D11<D> where D: Device {
pub(crate) struct TileProgramD3D11<D>
where
D: Device,
{
pub(crate) common: TileProgramCommon<D>,
pub(crate) load_action_uniform: D::Uniform,
pub(crate) clear_color_uniform: D::Uniform,
@ -140,11 +169,14 @@ pub(crate) struct TileProgramD3D11<D> where D: Device {
pub(crate) first_tile_map_storage_buffer: D::StorageBuffer,
}
impl<D> TileProgramD3D11<D> where D: Device {
impl<D> TileProgramD3D11<D>
where
D: Device,
{
fn new(device: &D, resources: &dyn ResourceLoader) -> TileProgramD3D11<D> {
let mut program = device.create_compute_program(resources, "d3d11/tile");
device.set_compute_program_local_size(&mut program,
ComputeDimensions { x: 16, y: 4, z: 1 });
device
.set_compute_program_local_size(&mut program, ComputeDimensions { x: 16, y: 4, z: 1 });
let load_action_uniform = device.get_uniform(&program, "LoadAction");
let clear_color_uniform = device.get_uniform(&program, "ClearColor");
@ -166,7 +198,10 @@ impl<D> TileProgramD3D11<D> where D: Device {
}
}
pub(crate) struct BinProgramD3D11<D> where D: Device {
pub(crate) struct BinProgramD3D11<D>
where
D: Device,
{
pub(crate) program: D::Program,
pub(crate) microline_count_uniform: D::Uniform,
pub(crate) max_fill_count_uniform: D::Uniform,
@ -178,10 +213,17 @@ pub(crate) struct BinProgramD3D11<D> where D: Device {
pub(crate) backdrops_storage_buffer: D::StorageBuffer,
}
impl<D> BinProgramD3D11<D> where D: Device {
impl<D> BinProgramD3D11<D>
where
D: Device,
{
pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> BinProgramD3D11<D> {
let mut program = device.create_compute_program(resources, "d3d11/bin");
let dimensions = ComputeDimensions { x: BIN_WORKGROUP_SIZE, y: 1, z: 1 };
let dimensions = ComputeDimensions {
x: BIN_WORKGROUP_SIZE,
y: 1,
z: 1,
};
device.set_compute_program_local_size(&mut program, dimensions);
let microline_count_uniform = device.get_uniform(&program, "MicrolineCount");
@ -209,7 +251,10 @@ impl<D> BinProgramD3D11<D> where D: Device {
}
}
pub(crate) struct DiceProgramD3D11<D> where D: Device {
pub(crate) struct DiceProgramD3D11<D>
where
D: Device,
{
pub(crate) program: D::Program,
pub(crate) transform_uniform: D::Uniform,
pub(crate) translation_uniform: D::Uniform,
@ -223,17 +268,24 @@ pub(crate) struct DiceProgramD3D11<D> where D: Device {
pub(crate) microlines_storage_buffer: D::StorageBuffer,
}
impl<D> DiceProgramD3D11<D> where D: Device {
impl<D> DiceProgramD3D11<D>
where
D: Device,
{
pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> DiceProgramD3D11<D> {
let mut program = device.create_compute_program(resources, "d3d11/dice");
let dimensions = ComputeDimensions { x: DICE_WORKGROUP_SIZE, y: 1, z: 1 };
let dimensions = ComputeDimensions {
x: DICE_WORKGROUP_SIZE,
y: 1,
z: 1,
};
device.set_compute_program_local_size(&mut program, dimensions);
let transform_uniform = device.get_uniform(&program, "Transform");
let translation_uniform = device.get_uniform(&program, "Translation");
let path_count_uniform = device.get_uniform(&program, "PathCount");
let last_batch_segment_index_uniform = device.get_uniform(&program,
"LastBatchSegmentIndex");
let last_batch_segment_index_uniform =
device.get_uniform(&program, "LastBatchSegmentIndex");
let max_microline_count_uniform = device.get_uniform(&program, "MaxMicrolineCount");
let compute_indirect_params_storage_buffer =
@ -259,7 +311,10 @@ impl<D> DiceProgramD3D11<D> where D: Device {
}
}
pub(crate) struct BoundProgramD3D11<D> where D: Device {
pub(crate) struct BoundProgramD3D11<D>
where
D: Device,
{
pub(crate) program: D::Program,
pub(crate) path_count_uniform: D::Uniform,
pub(crate) tile_count_uniform: D::Uniform,
@ -267,10 +322,17 @@ pub(crate) struct BoundProgramD3D11<D> where D: Device {
pub(crate) tiles_storage_buffer: D::StorageBuffer,
}
impl<D> BoundProgramD3D11<D> where D: Device {
impl<D> BoundProgramD3D11<D>
where
D: Device,
{
pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> BoundProgramD3D11<D> {
let mut program = device.create_compute_program(resources, "d3d11/bound");
let dimensions = ComputeDimensions { x: BOUND_WORKGROUP_SIZE, y: 1, z: 1 };
let dimensions = ComputeDimensions {
x: BOUND_WORKGROUP_SIZE,
y: 1,
z: 1,
};
device.set_compute_program_local_size(&mut program, dimensions);
let path_count_uniform = device.get_uniform(&program, "PathCount");
@ -289,7 +351,10 @@ impl<D> BoundProgramD3D11<D> where D: Device {
}
}
pub(crate) struct SortProgramD3D11<D> where D: Device {
pub(crate) struct SortProgramD3D11<D>
where
D: Device,
{
pub(crate) program: D::Program,
pub(crate) tile_count_uniform: D::Uniform,
pub(crate) tiles_storage_buffer: D::StorageBuffer,
@ -297,10 +362,17 @@ pub(crate) struct SortProgramD3D11<D> where D: Device {
pub(crate) z_buffer_storage_buffer: D::StorageBuffer,
}
impl<D> SortProgramD3D11<D> where D: Device {
impl<D> SortProgramD3D11<D>
where
D: Device,
{
pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> SortProgramD3D11<D> {
let mut program = device.create_compute_program(resources, "d3d11/sort");
let dimensions = ComputeDimensions { x: SORT_WORKGROUP_SIZE, y: 1, z: 1 };
let dimensions = ComputeDimensions {
x: SORT_WORKGROUP_SIZE,
y: 1,
z: 1,
};
device.set_compute_program_local_size(&mut program, dimensions);
let tile_count_uniform = device.get_uniform(&program, "TileCount");
@ -316,4 +388,4 @@ impl<D> SortProgramD3D11<D> where D: Device {
z_buffer_storage_buffer,
}
}
}
}

View File

@ -9,7 +9,7 @@
// except according to those terms.
//! A hybrid CPU-GPU renderer that only relies on functionality available in Direct3D 9.
//!
//!
//! This renderer supports OpenGL at least 3.0, OpenGL ES at least 3.0, Metal of any version, and
//! WebGL at least 2.0.

View File

@ -9,17 +9,17 @@
// except according to those terms.
//! A hybrid CPU-GPU renderer that only relies on functionality available in Direct3D 9.
//!
//!
//! This renderer supports OpenGL at least 3.0, OpenGL ES at least 3.0, Metal of any version, and
//! WebGL at least 2.0.
use crate::gpu::blend::{BlendModeExt, ToBlendState};
use crate::gpu::perf::TimeCategory;
use crate::gpu::renderer::{FramebufferFlags, MASK_FRAMEBUFFER_HEIGHT, MASK_FRAMEBUFFER_WIDTH};
use crate::gpu::renderer::{RendererCore, RendererFlags};
use crate::gpu::d3d9::shaders::{ClipTileCombineVertexArrayD3D9, ClipTileCopyVertexArrayD3D9};
use crate::gpu::d3d9::shaders::{CopyTileVertexArray, FillVertexArrayD3D9};
use crate::gpu::d3d9::shaders::{ProgramsD3D9, TileVertexArrayD3D9};
use crate::gpu::perf::TimeCategory;
use crate::gpu::renderer::{FramebufferFlags, MASK_FRAMEBUFFER_HEIGHT, MASK_FRAMEBUFFER_WIDTH};
use crate::gpu::renderer::{RendererCore, RendererFlags};
use crate::gpu_data::{Clip, DrawTileBatchD3D9, Fill, TileBatchTexture, TileObjectPrimitive};
use crate::tile_map::DenseTileMap;
use crate::tiles::{TILE_HEIGHT, TILE_WIDTH};
@ -28,7 +28,7 @@ use pathfinder_color::ColorF;
use pathfinder_content::effects::BlendMode;
use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::transform3d::Transform4F;
use pathfinder_geometry::vector::{Vector2I, Vector4F, vec2i};
use pathfinder_geometry::vector::{vec2i, Vector2I, Vector4F};
use pathfinder_gpu::allocator::{BufferTag, FramebufferID, FramebufferTag, GeneralBufferID};
use pathfinder_gpu::allocator::{IndexBufferID, TextureID, TextureTag};
use pathfinder_gpu::{BlendFactor, BlendState, BufferTarget, ClearOps, Device, Primitive};
@ -40,7 +40,10 @@ use std::u32;
const MAX_FILLS_PER_BATCH: usize = 0x10000;
pub(crate) struct RendererD3D9<D> where D: Device {
pub(crate) struct RendererD3D9<D>
where
D: Device,
{
// Basic data
programs: ProgramsD3D9<D>,
quads_vertex_indices_buffer_id: Option<IndexBufferID>,
@ -54,17 +57,23 @@ pub(crate) struct RendererD3D9<D> where D: Device {
dest_blend_framebuffer_id: FramebufferID,
}
impl<D> RendererD3D9<D> where D: Device {
pub(crate) fn new(core: &mut RendererCore<D>, resources: &dyn ResourceLoader)
-> RendererD3D9<D> {
impl<D> RendererD3D9<D>
where
D: Device,
{
pub(crate) fn new(
core: &mut RendererCore<D>,
resources: &dyn ResourceLoader,
) -> RendererD3D9<D> {
let programs = ProgramsD3D9::new(&core.device, resources);
let window_size = core.options.dest.window_size(&core.device);
let dest_blend_framebuffer_id =
core.allocator.allocate_framebuffer(&core.device,
window_size,
TextureFormat::RGBA8,
FramebufferTag("DestBlendD3D9"));
let dest_blend_framebuffer_id = core.allocator.allocate_framebuffer(
&core.device,
window_size,
TextureFormat::RGBA8,
FramebufferTag("DestBlendD3D9"),
);
RendererD3D9 {
programs,
@ -78,43 +87,57 @@ impl<D> RendererD3D9<D> where D: Device {
}
}
pub(crate) fn upload_and_draw_tiles(&mut self,
core: &mut RendererCore<D>,
batch: &DrawTileBatchD3D9) {
pub(crate) fn upload_and_draw_tiles(
&mut self,
core: &mut RendererCore<D>,
batch: &DrawTileBatchD3D9,
) {
if !batch.clips.is_empty() {
let clip_buffer_info = self.upload_clip_tiles(core, &batch.clips);
self.clip_tiles(core, &clip_buffer_info);
core.allocator.free_general_buffer(clip_buffer_info.clip_buffer_id);
core.allocator
.free_general_buffer(clip_buffer_info.clip_buffer_id);
}
let tile_buffer = self.upload_tiles(core, &batch.tiles);
let z_buffer_texture_id = self.upload_z_buffer(core, &batch.z_buffer_data);
self.draw_tiles(core,
batch.tiles.len() as u32,
tile_buffer.tile_vertex_buffer_id,
batch.color_texture,
batch.blend_mode,
z_buffer_texture_id);
self.draw_tiles(
core,
batch.tiles.len() as u32,
tile_buffer.tile_vertex_buffer_id,
batch.color_texture,
batch.blend_mode,
z_buffer_texture_id,
);
core.allocator.free_texture(z_buffer_texture_id);
core.allocator.free_general_buffer(tile_buffer.tile_vertex_buffer_id);
core.allocator
.free_general_buffer(tile_buffer.tile_vertex_buffer_id);
}
fn upload_tiles(&mut self, core: &mut RendererCore<D>, tiles: &[TileObjectPrimitive])
-> TileBufferD3D9 {
let tile_vertex_buffer_id =
core.allocator.allocate_general_buffer::<TileObjectPrimitive>(&core.device,
tiles.len() as u64,
BufferTag("TileD3D9"));
fn upload_tiles(
&mut self,
core: &mut RendererCore<D>,
tiles: &[TileObjectPrimitive],
) -> TileBufferD3D9 {
let tile_vertex_buffer_id = core
.allocator
.allocate_general_buffer::<TileObjectPrimitive>(
&core.device,
tiles.len() as u64,
BufferTag("TileD3D9"),
);
let tile_vertex_buffer = &core.allocator.get_general_buffer(tile_vertex_buffer_id);
core.device.upload_to_buffer(tile_vertex_buffer, 0, tiles, BufferTarget::Vertex);
core.device
.upload_to_buffer(tile_vertex_buffer, 0, tiles, BufferTarget::Vertex);
self.ensure_index_buffer(core, tiles.len());
TileBufferD3D9 { tile_vertex_buffer_id }
TileBufferD3D9 {
tile_vertex_buffer_id,
}
}
fn ensure_index_buffer(&mut self, core: &mut RendererCore<D>, mut length: usize) {
length = length.next_power_of_two();
if self.quads_vertex_indices_length >= length {
@ -125,24 +148,33 @@ impl<D> RendererD3D9<D> where D: Device {
let mut indices: Vec<u32> = Vec::with_capacity(length * 6);
for index in 0..(length as u32) {
indices.extend_from_slice(&[
index * 4 + 0, index * 4 + 1, index * 4 + 2,
index * 4 + 1, index * 4 + 3, index * 4 + 2,
index * 4 + 0,
index * 4 + 1,
index * 4 + 2,
index * 4 + 1,
index * 4 + 3,
index * 4 + 2,
]);
}
if let Some(quads_vertex_indices_buffer_id) = self.quads_vertex_indices_buffer_id.take() {
core.allocator.free_index_buffer(quads_vertex_indices_buffer_id);
core.allocator
.free_index_buffer(quads_vertex_indices_buffer_id);
}
let quads_vertex_indices_buffer_id =
core.allocator.allocate_index_buffer::<u32>(&core.device,
indices.len() as u64,
BufferTag("QuadsVertexIndicesD3D9"));
let quads_vertex_indices_buffer =
core.allocator.get_index_buffer(quads_vertex_indices_buffer_id);
core.device.upload_to_buffer(quads_vertex_indices_buffer,
0,
&indices,
BufferTarget::Index);
let quads_vertex_indices_buffer_id = core.allocator.allocate_index_buffer::<u32>(
&core.device,
indices.len() as u64,
BufferTag("QuadsVertexIndicesD3D9"),
);
let quads_vertex_indices_buffer = core
.allocator
.get_index_buffer(quads_vertex_indices_buffer_id);
core.device.upload_to_buffer(
quads_vertex_indices_buffer,
0,
&indices,
BufferTarget::Index,
);
self.quads_vertex_indices_buffer_id = Some(quads_vertex_indices_buffer_id);
self.quads_vertex_indices_length = length;
}
@ -179,221 +211,305 @@ impl<D> RendererD3D9<D> where D: Device {
}
let fill_storage_info = self.upload_buffered_fills(core);
self.draw_fills(core, fill_storage_info.fill_buffer_id, fill_storage_info.fill_count);
core.allocator.free_general_buffer(fill_storage_info.fill_buffer_id);
self.draw_fills(
core,
fill_storage_info.fill_buffer_id,
fill_storage_info.fill_count,
);
core.allocator
.free_general_buffer(fill_storage_info.fill_buffer_id);
}
fn upload_buffered_fills(&mut self, core: &mut RendererCore<D>) -> FillBufferInfoD3D9 {
let buffered_fills = &mut self.buffered_fills;
debug_assert!(!buffered_fills.is_empty());
let fill_buffer_id = core.allocator
.allocate_general_buffer::<Fill>(&core.device,
MAX_FILLS_PER_BATCH as u64,
BufferTag("Fill"));
let fill_buffer_id = core.allocator.allocate_general_buffer::<Fill>(
&core.device,
MAX_FILLS_PER_BATCH as u64,
BufferTag("Fill"),
);
let fill_vertex_buffer = core.allocator.get_general_buffer(fill_buffer_id);
debug_assert!(buffered_fills.len() <= u32::MAX as usize);
core.device.upload_to_buffer(fill_vertex_buffer, 0, &buffered_fills, BufferTarget::Vertex);
core.device
.upload_to_buffer(fill_vertex_buffer, 0, &buffered_fills, BufferTarget::Vertex);
let fill_count = buffered_fills.len() as u32;
buffered_fills.clear();
FillBufferInfoD3D9 { fill_buffer_id, fill_count }
FillBufferInfoD3D9 {
fill_buffer_id,
fill_count,
}
}
fn draw_fills(&mut self,
core: &mut RendererCore<D>,
fill_buffer_id: GeneralBufferID,
fill_count: u32) {
fn draw_fills(
&mut self,
core: &mut RendererCore<D>,
fill_buffer_id: GeneralBufferID,
fill_count: u32,
) {
let fill_raster_program = &self.programs.fill_program;
let fill_vertex_buffer = core.allocator.get_general_buffer(fill_buffer_id);
let quad_vertex_positions_buffer =
core.allocator.get_general_buffer(core.quad_vertex_positions_buffer_id);
let quad_vertex_indices_buffer = core.allocator
.get_index_buffer(core.quad_vertex_indices_buffer_id);
let quad_vertex_positions_buffer = core
.allocator
.get_general_buffer(core.quad_vertex_positions_buffer_id);
let quad_vertex_indices_buffer = core
.allocator
.get_index_buffer(core.quad_vertex_indices_buffer_id);
let area_lut_texture = core.allocator.get_texture(core.area_lut_texture_id);
let mask_viewport = self.mask_viewport(core);
let mask_storage = core.mask_storage.as_ref().expect("Where's the mask storage?");
let mask_storage = core
.mask_storage
.as_ref()
.expect("Where's the mask storage?");
let mask_framebuffer_id = mask_storage.framebuffer_id;
let mask_framebuffer = core.allocator.get_framebuffer(mask_framebuffer_id);
let fill_vertex_array = FillVertexArrayD3D9::new(&core.device,
fill_raster_program,
fill_vertex_buffer,
quad_vertex_positions_buffer,
quad_vertex_indices_buffer);
let fill_vertex_array = FillVertexArrayD3D9::new(
&core.device,
fill_raster_program,
fill_vertex_buffer,
quad_vertex_positions_buffer,
quad_vertex_indices_buffer,
);
let mut clear_color = None;
if !core.framebuffer_flags.contains(FramebufferFlags::MASK_FRAMEBUFFER_IS_DIRTY) {
if !core
.framebuffer_flags
.contains(FramebufferFlags::MASK_FRAMEBUFFER_IS_DIRTY)
{
clear_color = Some(ColorF::default());
};
let timer_query = core.timer_query_cache.start_timing_draw_call(&core.device,
&core.options);
let timer_query = core
.timer_query_cache
.start_timing_draw_call(&core.device, &core.options);
core.device.draw_elements_instanced(6, fill_count, &RenderState {
target: &RenderTarget::Framebuffer(mask_framebuffer),
program: &fill_raster_program.program,
vertex_array: &fill_vertex_array.vertex_array,
primitive: Primitive::Triangles,
textures: &[(&fill_raster_program.area_lut_texture, area_lut_texture)],
uniforms: &[
(&fill_raster_program.framebuffer_size_uniform,
UniformData::Vec2(mask_viewport.size().to_f32().0)),
(&fill_raster_program.tile_size_uniform,
UniformData::Vec2(F32x2::new(TILE_WIDTH as f32, TILE_HEIGHT as f32))),
],
images: &[],
storage_buffers: &[],
viewport: mask_viewport,
options: RenderOptions {
blend: Some(BlendState {
src_rgb_factor: BlendFactor::One,
src_alpha_factor: BlendFactor::One,
dest_rgb_factor: BlendFactor::One,
dest_alpha_factor: BlendFactor::One,
..BlendState::default()
}),
clear_ops: ClearOps { color: clear_color, ..ClearOps::default() },
..RenderOptions::default()
core.device.draw_elements_instanced(
6,
fill_count,
&RenderState {
target: &RenderTarget::Framebuffer(mask_framebuffer),
program: &fill_raster_program.program,
vertex_array: &fill_vertex_array.vertex_array,
primitive: Primitive::Triangles,
textures: &[(&fill_raster_program.area_lut_texture, area_lut_texture)],
uniforms: &[
(
&fill_raster_program.framebuffer_size_uniform,
UniformData::Vec2(mask_viewport.size().to_f32().0),
),
(
&fill_raster_program.tile_size_uniform,
UniformData::Vec2(F32x2::new(TILE_WIDTH as f32, TILE_HEIGHT as f32)),
),
],
images: &[],
storage_buffers: &[],
viewport: mask_viewport,
options: RenderOptions {
blend: Some(BlendState {
src_rgb_factor: BlendFactor::One,
src_alpha_factor: BlendFactor::One,
dest_rgb_factor: BlendFactor::One,
dest_alpha_factor: BlendFactor::One,
..BlendState::default()
}),
clear_ops: ClearOps {
color: clear_color,
..ClearOps::default()
},
..RenderOptions::default()
},
},
});
);
core.stats.drawcall_count += 1;
core.finish_timing_draw_call(&timer_query);
core.current_timer.as_mut().unwrap().push_query(TimeCategory::Fill, timer_query);
core.current_timer
.as_mut()
.unwrap()
.push_query(TimeCategory::Fill, timer_query);
core.framebuffer_flags.insert(FramebufferFlags::MASK_FRAMEBUFFER_IS_DIRTY);
core.framebuffer_flags
.insert(FramebufferFlags::MASK_FRAMEBUFFER_IS_DIRTY);
}
fn clip_tiles(&mut self, core: &mut RendererCore<D>, clip_buffer_info: &ClipBufferInfo) {
// Allocate temp mask framebuffer.
let mask_temp_framebuffer_id =
core.allocator.allocate_framebuffer(&core.device,
self.mask_viewport(core).size(),
core.mask_texture_format(),
FramebufferTag("TempClipMaskD3D9"));
let mask_temp_framebuffer_id = core.allocator.allocate_framebuffer(
&core.device,
self.mask_viewport(core).size(),
core.mask_texture_format(),
FramebufferTag("TempClipMaskD3D9"),
);
let mask_temp_framebuffer = core.allocator.get_framebuffer(mask_temp_framebuffer_id);
let mask_storage = core.mask_storage.as_ref().expect("Where's the mask storage?");
let mask_storage = core
.mask_storage
.as_ref()
.expect("Where's the mask storage?");
let mask_framebuffer_id = mask_storage.framebuffer_id;
let mask_framebuffer = core.allocator.get_framebuffer(mask_framebuffer_id);
let mask_texture = core.device.framebuffer_texture(mask_framebuffer);
let mask_texture_size = core.device.texture_size(&mask_texture);
let clip_vertex_buffer = core.allocator
.get_general_buffer(clip_buffer_info.clip_buffer_id);
let quad_vertex_positions_buffer =
core.allocator.get_general_buffer(core.quad_vertex_positions_buffer_id);
let quad_vertex_indices_buffer = core.allocator
.get_index_buffer(core.quad_vertex_indices_buffer_id);
let clip_vertex_buffer = core
.allocator
.get_general_buffer(clip_buffer_info.clip_buffer_id);
let quad_vertex_positions_buffer = core
.allocator
.get_general_buffer(core.quad_vertex_positions_buffer_id);
let quad_vertex_indices_buffer = core
.allocator
.get_index_buffer(core.quad_vertex_indices_buffer_id);
let tile_clip_copy_vertex_array =
ClipTileCopyVertexArrayD3D9::new(&core.device,
&self.programs.tile_clip_copy_program,
clip_vertex_buffer,
quad_vertex_positions_buffer,
quad_vertex_indices_buffer);
let tile_clip_combine_vertex_array =
ClipTileCombineVertexArrayD3D9::new(&core.device,
&self.programs.tile_clip_combine_program,
clip_vertex_buffer,
quad_vertex_positions_buffer,
quad_vertex_indices_buffer);
let tile_clip_copy_vertex_array = ClipTileCopyVertexArrayD3D9::new(
&core.device,
&self.programs.tile_clip_copy_program,
clip_vertex_buffer,
quad_vertex_positions_buffer,
quad_vertex_indices_buffer,
);
let tile_clip_combine_vertex_array = ClipTileCombineVertexArrayD3D9::new(
&core.device,
&self.programs.tile_clip_combine_program,
clip_vertex_buffer,
quad_vertex_positions_buffer,
quad_vertex_indices_buffer,
);
let timer_query = core.timer_query_cache.start_timing_draw_call(&core.device,
&core.options);
let timer_query = core
.timer_query_cache
.start_timing_draw_call(&core.device, &core.options);
// Copy out tiles.
//
// TODO(pcwalton): Don't do this on GL4.
core.device.draw_elements_instanced(6, clip_buffer_info.clip_count * 2, &RenderState {
target: &RenderTarget::Framebuffer(mask_temp_framebuffer),
program: &self.programs.tile_clip_copy_program.program,
vertex_array: &tile_clip_copy_vertex_array.vertex_array,
primitive: Primitive::Triangles,
textures: &[
(&self.programs.tile_clip_copy_program.src_texture,
core.device.framebuffer_texture(mask_framebuffer)),
],
images: &[],
uniforms: &[
(&self.programs.tile_clip_copy_program.framebuffer_size_uniform,
UniformData::Vec2(mask_texture_size.to_f32().0)),
],
storage_buffers: &[],
viewport: RectI::new(Vector2I::zero(), mask_texture_size),
options: RenderOptions::default(),
});
core.device.draw_elements_instanced(
6,
clip_buffer_info.clip_count * 2,
&RenderState {
target: &RenderTarget::Framebuffer(mask_temp_framebuffer),
program: &self.programs.tile_clip_copy_program.program,
vertex_array: &tile_clip_copy_vertex_array.vertex_array,
primitive: Primitive::Triangles,
textures: &[(
&self.programs.tile_clip_copy_program.src_texture,
core.device.framebuffer_texture(mask_framebuffer),
)],
images: &[],
uniforms: &[(
&self
.programs
.tile_clip_copy_program
.framebuffer_size_uniform,
UniformData::Vec2(mask_texture_size.to_f32().0),
)],
storage_buffers: &[],
viewport: RectI::new(Vector2I::zero(), mask_texture_size),
options: RenderOptions::default(),
},
);
core.stats.drawcall_count += 1;
core.finish_timing_draw_call(&timer_query);
core.current_timer.as_mut().unwrap().push_query(TimeCategory::Other, timer_query);
let timer_query = core.timer_query_cache.start_timing_draw_call(&core.device,
&core.options);
core.current_timer
.as_mut()
.unwrap()
.push_query(TimeCategory::Other, timer_query);
let timer_query = core
.timer_query_cache
.start_timing_draw_call(&core.device, &core.options);
// Combine clip tiles.
core.device.draw_elements_instanced(6, clip_buffer_info.clip_count, &RenderState {
target: &RenderTarget::Framebuffer(mask_framebuffer),
program: &self.programs.tile_clip_combine_program.program,
vertex_array: &tile_clip_combine_vertex_array.vertex_array,
primitive: Primitive::Triangles,
textures: &[
(&self.programs.tile_clip_combine_program.src_texture,
core.device.framebuffer_texture(&mask_temp_framebuffer)),
],
images: &[],
uniforms: &[
(&self.programs.tile_clip_combine_program.framebuffer_size_uniform,
UniformData::Vec2(mask_texture_size.to_f32().0)),
],
storage_buffers: &[],
viewport: RectI::new(Vector2I::zero(), mask_texture_size),
options: RenderOptions::default(),
});
core.device.draw_elements_instanced(
6,
clip_buffer_info.clip_count,
&RenderState {
target: &RenderTarget::Framebuffer(mask_framebuffer),
program: &self.programs.tile_clip_combine_program.program,
vertex_array: &tile_clip_combine_vertex_array.vertex_array,
primitive: Primitive::Triangles,
textures: &[(
&self.programs.tile_clip_combine_program.src_texture,
core.device.framebuffer_texture(&mask_temp_framebuffer),
)],
images: &[],
uniforms: &[(
&self
.programs
.tile_clip_combine_program
.framebuffer_size_uniform,
UniformData::Vec2(mask_texture_size.to_f32().0),
)],
storage_buffers: &[],
viewport: RectI::new(Vector2I::zero(), mask_texture_size),
options: RenderOptions::default(),
},
);
core.stats.drawcall_count += 1;
core.finish_timing_draw_call(&timer_query);
core.current_timer.as_mut().unwrap().push_query(TimeCategory::Other, timer_query);
core.current_timer
.as_mut()
.unwrap()
.push_query(TimeCategory::Other, timer_query);
core.allocator.free_framebuffer(mask_temp_framebuffer_id);
}
fn upload_z_buffer(&mut self, core: &mut RendererCore<D>, z_buffer_map: &DenseTileMap<i32>)
-> TextureID {
let z_buffer_texture_id = core.allocator.allocate_texture(&core.device,
z_buffer_map.rect.size(),
TextureFormat::RGBA8,
TextureTag("ZBufferD3D9"));
fn upload_z_buffer(
&mut self,
core: &mut RendererCore<D>,
z_buffer_map: &DenseTileMap<i32>,
) -> TextureID {
let z_buffer_texture_id = core.allocator.allocate_texture(
&core.device,
z_buffer_map.rect.size(),
TextureFormat::RGBA8,
TextureTag("ZBufferD3D9"),
);
let z_buffer_texture = core.allocator.get_texture(z_buffer_texture_id);
debug_assert_eq!(z_buffer_map.rect.origin(), Vector2I::default());
let z_data: &[u8] = z_buffer_map.data.as_byte_slice();
core.device.upload_to_texture(z_buffer_texture,
z_buffer_map.rect,
TextureDataRef::U8(&z_data));
core.device.upload_to_texture(
z_buffer_texture,
z_buffer_map.rect,
TextureDataRef::U8(&z_data),
);
z_buffer_texture_id
}
// Uploads clip tiles from CPU to GPU.
fn upload_clip_tiles(&mut self, core: &mut RendererCore<D>, clips: &[Clip]) -> ClipBufferInfo {
let clip_buffer_id = core.allocator.allocate_general_buffer::<Clip>(&core.device,
clips.len() as u64,
BufferTag("ClipD3D9"));
let clip_buffer_id = core.allocator.allocate_general_buffer::<Clip>(
&core.device,
clips.len() as u64,
BufferTag("ClipD3D9"),
);
let clip_buffer = core.allocator.get_general_buffer(clip_buffer_id);
core.device.upload_to_buffer(clip_buffer, 0, clips, BufferTarget::Vertex);
ClipBufferInfo { clip_buffer_id, clip_count: clips.len() as u32 }
core.device
.upload_to_buffer(clip_buffer, 0, clips, BufferTarget::Vertex);
ClipBufferInfo {
clip_buffer_id,
clip_count: clips.len() as u32,
}
}
fn draw_tiles(&mut self,
core: &mut RendererCore<D>,
tile_count: u32,
tile_vertex_buffer_id: GeneralBufferID,
color_texture_0: Option<TileBatchTexture>,
blend_mode: BlendMode,
z_buffer_texture_id: TextureID) {
fn draw_tiles(
&mut self,
core: &mut RendererCore<D>,
tile_count: u32,
tile_vertex_buffer_id: GeneralBufferID,
color_texture_0: Option<TileBatchTexture>,
blend_mode: BlendMode,
z_buffer_texture_id: TextureID,
) {
// TODO(pcwalton): Disable blend for solid tiles.
if tile_count == 0 {
@ -410,79 +526,112 @@ impl<D> RendererD3D9<D> where D: Device {
let clear_color = core.clear_color_for_draw_operation();
let draw_viewport = core.draw_viewport();
let timer_query = core.timer_query_cache.start_timing_draw_call(&core.device,
&core.options);
let timer_query = core
.timer_query_cache
.start_timing_draw_call(&core.device, &core.options);
let tile_raster_program = &self.programs.tile_program;
let tile_vertex_buffer = core.allocator.get_general_buffer(tile_vertex_buffer_id);
let quad_vertex_positions_buffer =
core.allocator.get_general_buffer(core.quad_vertex_positions_buffer_id);
let quad_vertex_indices_buffer = core.allocator
.get_index_buffer(core.quad_vertex_indices_buffer_id);
let dest_blend_framebuffer = core.allocator
.get_framebuffer(self.dest_blend_framebuffer_id);
let quad_vertex_positions_buffer = core
.allocator
.get_general_buffer(core.quad_vertex_positions_buffer_id);
let quad_vertex_indices_buffer = core
.allocator
.get_index_buffer(core.quad_vertex_indices_buffer_id);
let dest_blend_framebuffer = core
.allocator
.get_framebuffer(self.dest_blend_framebuffer_id);
let (mut textures, mut uniforms) = (vec![], vec![]);
core.set_uniforms_for_drawing_tiles(&tile_raster_program.common,
&mut textures,
&mut uniforms,
color_texture_0);
core.set_uniforms_for_drawing_tiles(
&tile_raster_program.common,
&mut textures,
&mut uniforms,
color_texture_0,
);
uniforms.push((&tile_raster_program.transform_uniform,
UniformData::Mat4(self.tile_transform(core).to_columns())));
textures.push((&tile_raster_program.dest_texture,
core.device.framebuffer_texture(dest_blend_framebuffer)));
uniforms.push((
&tile_raster_program.transform_uniform,
UniformData::Mat4(self.tile_transform(core).to_columns()),
));
textures.push((
&tile_raster_program.dest_texture,
core.device.framebuffer_texture(dest_blend_framebuffer),
));
let z_buffer_texture = core.allocator.get_texture(z_buffer_texture_id);
textures.push((&tile_raster_program.common.z_buffer_texture, z_buffer_texture));
uniforms.push((&tile_raster_program.common.z_buffer_texture_size_uniform,
UniformData::IVec2(core.device.texture_size(z_buffer_texture).0)));
textures.push((
&tile_raster_program.common.z_buffer_texture,
z_buffer_texture,
));
uniforms.push((
&tile_raster_program.common.z_buffer_texture_size_uniform,
UniformData::IVec2(core.device.texture_size(z_buffer_texture).0),
));
let tile_vertex_array = TileVertexArrayD3D9::new(&core.device,
&self.programs.tile_program,
tile_vertex_buffer,
quad_vertex_positions_buffer,
quad_vertex_indices_buffer);
let tile_vertex_array = TileVertexArrayD3D9::new(
&core.device,
&self.programs.tile_program,
tile_vertex_buffer,
quad_vertex_positions_buffer,
quad_vertex_indices_buffer,
);
core.device.draw_elements_instanced(6, tile_count, &RenderState {
target: &core.draw_render_target(),
program: &tile_raster_program.common.program,
vertex_array: &tile_vertex_array.vertex_array,
primitive: Primitive::Triangles,
textures: &textures,
images: &[],
storage_buffers: &[],
uniforms: &uniforms,
viewport: draw_viewport,
options: RenderOptions {
blend: blend_mode.to_blend_state(),
stencil: self.stencil_state(core),
clear_ops: ClearOps { color: clear_color, ..ClearOps::default() },
..RenderOptions::default()
core.device.draw_elements_instanced(
6,
tile_count,
&RenderState {
target: &core.draw_render_target(),
program: &tile_raster_program.common.program,
vertex_array: &tile_vertex_array.vertex_array,
primitive: Primitive::Triangles,
textures: &textures,
images: &[],
storage_buffers: &[],
uniforms: &uniforms,
viewport: draw_viewport,
options: RenderOptions {
blend: blend_mode.to_blend_state(),
stencil: self.stencil_state(core),
clear_ops: ClearOps {
color: clear_color,
..ClearOps::default()
},
..RenderOptions::default()
},
},
});
);
core.stats.drawcall_count += 1;
core.finish_timing_draw_call(&timer_query);
core.current_timer.as_mut().unwrap().push_query(TimeCategory::Composite, timer_query);
core.current_timer
.as_mut()
.unwrap()
.push_query(TimeCategory::Composite, timer_query);
core.preserve_draw_framebuffer();
}
fn copy_alpha_tiles_to_dest_blend_texture(&mut self,
core: &mut RendererCore<D>,
tile_count: u32,
vertex_buffer_id: GeneralBufferID) {
fn copy_alpha_tiles_to_dest_blend_texture(
&mut self,
core: &mut RendererCore<D>,
tile_count: u32,
vertex_buffer_id: GeneralBufferID,
) {
let draw_viewport = core.draw_viewport();
let mut textures = vec![];
let mut uniforms = vec![
(&self.programs.tile_copy_program.transform_uniform,
UniformData::Mat4(self.tile_transform(core).to_columns())),
(&self.programs.tile_copy_program.tile_size_uniform,
UniformData::Vec2(F32x2::new(TILE_WIDTH as f32, TILE_HEIGHT as f32))),
(
&self.programs.tile_copy_program.transform_uniform,
UniformData::Mat4(self.tile_transform(core).to_columns()),
),
(
&self.programs.tile_copy_program.tile_size_uniform,
UniformData::Vec2(F32x2::new(TILE_WIDTH as f32, TILE_HEIGHT as f32)),
),
];
let draw_framebuffer = match core.draw_render_target() {
@ -492,41 +641,51 @@ impl<D> RendererD3D9<D> where D: Device {
let draw_texture = core.device.framebuffer_texture(&draw_framebuffer);
textures.push((&self.programs.tile_copy_program.src_texture, draw_texture));
uniforms.push((&self.programs.tile_copy_program.framebuffer_size_uniform,
UniformData::Vec2(draw_viewport.size().to_f32().0)));
uniforms.push((
&self.programs.tile_copy_program.framebuffer_size_uniform,
UniformData::Vec2(draw_viewport.size().to_f32().0),
));
let quads_vertex_indices_buffer_id = self.quads_vertex_indices_buffer_id
.expect("Where's the quads vertex buffer?");
let quads_vertex_indices_buffer = core.allocator
.get_index_buffer(quads_vertex_indices_buffer_id);
let quads_vertex_indices_buffer_id = self
.quads_vertex_indices_buffer_id
.expect("Where's the quads vertex buffer?");
let quads_vertex_indices_buffer = core
.allocator
.get_index_buffer(quads_vertex_indices_buffer_id);
let vertex_buffer = core.allocator.get_general_buffer(vertex_buffer_id);
let tile_copy_vertex_array = CopyTileVertexArray::new(&core.device,
&self.programs.tile_copy_program,
vertex_buffer,
quads_vertex_indices_buffer);
let tile_copy_vertex_array = CopyTileVertexArray::new(
&core.device,
&self.programs.tile_copy_program,
vertex_buffer,
quads_vertex_indices_buffer,
);
let dest_blend_framebuffer = core.allocator
.get_framebuffer(self.dest_blend_framebuffer_id);
let dest_blend_framebuffer = core
.allocator
.get_framebuffer(self.dest_blend_framebuffer_id);
core.device.draw_elements(tile_count * 6, &RenderState {
target: &RenderTarget::Framebuffer(dest_blend_framebuffer),
program: &self.programs.tile_copy_program.program,
vertex_array: &tile_copy_vertex_array.vertex_array,
primitive: Primitive::Triangles,
textures: &textures,
images: &[],
storage_buffers: &[],
uniforms: &uniforms,
viewport: draw_viewport,
options: RenderOptions {
clear_ops: ClearOps {
color: Some(ColorF::new(1.0, 0.0, 0.0, 1.0)),
..ClearOps::default()
core.device.draw_elements(
tile_count * 6,
&RenderState {
target: &RenderTarget::Framebuffer(dest_blend_framebuffer),
program: &self.programs.tile_copy_program.program,
vertex_array: &tile_copy_vertex_array.vertex_array,
primitive: Primitive::Triangles,
textures: &textures,
images: &[],
storage_buffers: &[],
uniforms: &uniforms,
viewport: draw_viewport,
options: RenderOptions {
clear_ops: ClearOps {
color: Some(ColorF::new(1.0, 0.0, 0.0, 1.0)),
..ClearOps::default()
},
..RenderOptions::default()
},
..RenderOptions::default()
},
});
);
core.stats.drawcall_count += 1;
}

View File

@ -10,313 +10,478 @@
//! Shaders and vertex specifications for the Direct3D 9-level renderer.
use crate::gpu::shaders::{TILE_INSTANCE_SIZE, TileProgramCommon};
use crate::gpu::shaders::{TileProgramCommon, TILE_INSTANCE_SIZE};
use pathfinder_gpu::{BufferTarget, Device, VertexAttrClass, VertexAttrDescriptor, VertexAttrType};
use pathfinder_resources::ResourceLoader;
const FILL_INSTANCE_SIZE: usize = 12;
const CLIP_TILE_INSTANCE_SIZE: usize = 16;
pub(crate) struct FillVertexArrayD3D9<D> where D: Device {
pub(crate) struct FillVertexArrayD3D9<D>
where
D: Device,
{
pub(crate) vertex_array: D::VertexArray,
}
impl<D> FillVertexArrayD3D9<D> where D: Device {
pub(crate) fn new(device: &D,
fill_program: &FillProgramD3D9<D>,
vertex_buffer: &D::Buffer,
quad_vertex_positions_buffer: &D::Buffer,
quad_vertex_indices_buffer: &D::Buffer)
-> FillVertexArrayD3D9<D> {
impl<D> FillVertexArrayD3D9<D>
where
D: Device,
{
pub(crate) fn new(
device: &D,
fill_program: &FillProgramD3D9<D>,
vertex_buffer: &D::Buffer,
quad_vertex_positions_buffer: &D::Buffer,
quad_vertex_indices_buffer: &D::Buffer,
) -> FillVertexArrayD3D9<D> {
let vertex_array = device.create_vertex_array();
let tess_coord_attr = device.get_vertex_attr(&fill_program.program, "TessCoord").unwrap();
let line_segment_attr = device.get_vertex_attr(&fill_program.program, "LineSegment")
.unwrap();
let tile_index_attr = device.get_vertex_attr(&fill_program.program, "TileIndex").unwrap();
let tess_coord_attr = device
.get_vertex_attr(&fill_program.program, "TessCoord")
.unwrap();
let line_segment_attr = device
.get_vertex_attr(&fill_program.program, "LineSegment")
.unwrap();
let tile_index_attr = device
.get_vertex_attr(&fill_program.program, "TileIndex")
.unwrap();
device.bind_buffer(&vertex_array, quad_vertex_positions_buffer, BufferTarget::Vertex);
device.configure_vertex_attr(&vertex_array, &tess_coord_attr, &VertexAttrDescriptor {
size: 2,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::U16,
stride: 4,
offset: 0,
divisor: 0,
buffer_index: 0,
});
device.bind_buffer(
&vertex_array,
quad_vertex_positions_buffer,
BufferTarget::Vertex,
);
device.configure_vertex_attr(
&vertex_array,
&tess_coord_attr,
&VertexAttrDescriptor {
size: 2,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::U16,
stride: 4,
offset: 0,
divisor: 0,
buffer_index: 0,
},
);
device.bind_buffer(&vertex_array, &vertex_buffer, BufferTarget::Vertex);
device.configure_vertex_attr(&vertex_array, &line_segment_attr, &VertexAttrDescriptor {
size: 4,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::U16,
stride: FILL_INSTANCE_SIZE,
offset: 0,
divisor: 1,
buffer_index: 1,
});
device.configure_vertex_attr(&vertex_array, &tile_index_attr, &VertexAttrDescriptor {
size: 1,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I32,
stride: FILL_INSTANCE_SIZE,
offset: 8,
divisor: 1,
buffer_index: 1,
});
device.bind_buffer(&vertex_array, quad_vertex_indices_buffer, BufferTarget::Index);
device.configure_vertex_attr(
&vertex_array,
&line_segment_attr,
&VertexAttrDescriptor {
size: 4,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::U16,
stride: FILL_INSTANCE_SIZE,
offset: 0,
divisor: 1,
buffer_index: 1,
},
);
device.configure_vertex_attr(
&vertex_array,
&tile_index_attr,
&VertexAttrDescriptor {
size: 1,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I32,
stride: FILL_INSTANCE_SIZE,
offset: 8,
divisor: 1,
buffer_index: 1,
},
);
device.bind_buffer(
&vertex_array,
quad_vertex_indices_buffer,
BufferTarget::Index,
);
FillVertexArrayD3D9 { vertex_array }
}
}
pub(crate) struct TileVertexArrayD3D9<D> where D: Device {
pub(crate) struct TileVertexArrayD3D9<D>
where
D: Device,
{
pub(crate) vertex_array: D::VertexArray,
}
impl<D> TileVertexArrayD3D9<D> where D: Device {
pub(crate) fn new(device: &D,
tile_program: &TileProgramD3D9<D>,
tile_vertex_buffer: &D::Buffer,
quad_vertex_positions_buffer: &D::Buffer,
quad_vertex_indices_buffer: &D::Buffer)
-> TileVertexArrayD3D9<D> {
impl<D> TileVertexArrayD3D9<D>
where
D: Device,
{
pub(crate) fn new(
device: &D,
tile_program: &TileProgramD3D9<D>,
tile_vertex_buffer: &D::Buffer,
quad_vertex_positions_buffer: &D::Buffer,
quad_vertex_indices_buffer: &D::Buffer,
) -> TileVertexArrayD3D9<D> {
let vertex_array = device.create_vertex_array();
let tile_offset_attr =
device.get_vertex_attr(&tile_program.common.program, "TileOffset").unwrap();
let tile_origin_attr =
device.get_vertex_attr(&tile_program.common.program, "TileOrigin").unwrap();
let mask_0_tex_coord_attr =
device.get_vertex_attr(&tile_program.common.program, "MaskTexCoord0").unwrap();
let ctrl_backdrop_attr =
device.get_vertex_attr(&tile_program.common.program, "CtrlBackdrop").unwrap();
let color_attr = device.get_vertex_attr(&tile_program.common.program, "Color").unwrap();
let path_index_attr = device.get_vertex_attr(&tile_program.common.program, "PathIndex")
.unwrap();
let tile_offset_attr = device
.get_vertex_attr(&tile_program.common.program, "TileOffset")
.unwrap();
let tile_origin_attr = device
.get_vertex_attr(&tile_program.common.program, "TileOrigin")
.unwrap();
let mask_0_tex_coord_attr = device
.get_vertex_attr(&tile_program.common.program, "MaskTexCoord0")
.unwrap();
let ctrl_backdrop_attr = device
.get_vertex_attr(&tile_program.common.program, "CtrlBackdrop")
.unwrap();
let color_attr = device
.get_vertex_attr(&tile_program.common.program, "Color")
.unwrap();
let path_index_attr = device
.get_vertex_attr(&tile_program.common.program, "PathIndex")
.unwrap();
device.bind_buffer(&vertex_array, quad_vertex_positions_buffer, BufferTarget::Vertex);
device.configure_vertex_attr(&vertex_array, &tile_offset_attr, &VertexAttrDescriptor {
size: 2,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I16,
stride: 4,
offset: 0,
divisor: 0,
buffer_index: 0,
});
device.bind_buffer(
&vertex_array,
quad_vertex_positions_buffer,
BufferTarget::Vertex,
);
device.configure_vertex_attr(
&vertex_array,
&tile_offset_attr,
&VertexAttrDescriptor {
size: 2,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I16,
stride: 4,
offset: 0,
divisor: 0,
buffer_index: 0,
},
);
device.bind_buffer(&vertex_array, tile_vertex_buffer, BufferTarget::Vertex);
device.configure_vertex_attr(&vertex_array, &tile_origin_attr, &VertexAttrDescriptor {
size: 2,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I16,
stride: TILE_INSTANCE_SIZE,
offset: 0,
divisor: 1,
buffer_index: 1,
});
device.configure_vertex_attr(&vertex_array, &mask_0_tex_coord_attr, &VertexAttrDescriptor {
size: 4,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::U8,
stride: TILE_INSTANCE_SIZE,
offset: 4,
divisor: 1,
buffer_index: 1,
});
device.configure_vertex_attr(&vertex_array, &path_index_attr, &VertexAttrDescriptor {
size: 1,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I32,
stride: TILE_INSTANCE_SIZE,
offset: 8,
divisor: 1,
buffer_index: 1,
});
device.configure_vertex_attr(&vertex_array, &color_attr, &VertexAttrDescriptor {
size: 1,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I16,
stride: TILE_INSTANCE_SIZE,
offset: 12,
divisor: 1,
buffer_index: 1,
});
device.configure_vertex_attr(&vertex_array, &ctrl_backdrop_attr, &VertexAttrDescriptor {
size: 2,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I8,
stride: TILE_INSTANCE_SIZE,
offset: 14,
divisor: 1,
buffer_index: 1,
});
device.bind_buffer(&vertex_array, quad_vertex_indices_buffer, BufferTarget::Index);
device.configure_vertex_attr(
&vertex_array,
&tile_origin_attr,
&VertexAttrDescriptor {
size: 2,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I16,
stride: TILE_INSTANCE_SIZE,
offset: 0,
divisor: 1,
buffer_index: 1,
},
);
device.configure_vertex_attr(
&vertex_array,
&mask_0_tex_coord_attr,
&VertexAttrDescriptor {
size: 4,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::U8,
stride: TILE_INSTANCE_SIZE,
offset: 4,
divisor: 1,
buffer_index: 1,
},
);
device.configure_vertex_attr(
&vertex_array,
&path_index_attr,
&VertexAttrDescriptor {
size: 1,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I32,
stride: TILE_INSTANCE_SIZE,
offset: 8,
divisor: 1,
buffer_index: 1,
},
);
device.configure_vertex_attr(
&vertex_array,
&color_attr,
&VertexAttrDescriptor {
size: 1,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I16,
stride: TILE_INSTANCE_SIZE,
offset: 12,
divisor: 1,
buffer_index: 1,
},
);
device.configure_vertex_attr(
&vertex_array,
&ctrl_backdrop_attr,
&VertexAttrDescriptor {
size: 2,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I8,
stride: TILE_INSTANCE_SIZE,
offset: 14,
divisor: 1,
buffer_index: 1,
},
);
device.bind_buffer(
&vertex_array,
quad_vertex_indices_buffer,
BufferTarget::Index,
);
TileVertexArrayD3D9 { vertex_array }
}
}
pub(crate) struct ClipTileCopyVertexArrayD3D9<D> where D: Device {
pub(crate) struct ClipTileCopyVertexArrayD3D9<D>
where
D: Device,
{
pub(crate) vertex_array: D::VertexArray,
}
impl<D> ClipTileCopyVertexArrayD3D9<D> where D: Device {
pub(crate) fn new(device: &D,
clip_tile_copy_program: &ClipTileCopyProgramD3D9<D>,
vertex_buffer: &D::Buffer,
quad_vertex_positions_buffer: &D::Buffer,
quad_vertex_indices_buffer: &D::Buffer)
-> ClipTileCopyVertexArrayD3D9<D> {
impl<D> ClipTileCopyVertexArrayD3D9<D>
where
D: Device,
{
pub(crate) fn new(
device: &D,
clip_tile_copy_program: &ClipTileCopyProgramD3D9<D>,
vertex_buffer: &D::Buffer,
quad_vertex_positions_buffer: &D::Buffer,
quad_vertex_indices_buffer: &D::Buffer,
) -> ClipTileCopyVertexArrayD3D9<D> {
let vertex_array = device.create_vertex_array();
let tile_offset_attr =
device.get_vertex_attr(&clip_tile_copy_program.program, "TileOffset").unwrap();
let tile_index_attr =
device.get_vertex_attr(&clip_tile_copy_program.program, "TileIndex").unwrap();
let tile_offset_attr = device
.get_vertex_attr(&clip_tile_copy_program.program, "TileOffset")
.unwrap();
let tile_index_attr = device
.get_vertex_attr(&clip_tile_copy_program.program, "TileIndex")
.unwrap();
device.bind_buffer(&vertex_array, quad_vertex_positions_buffer, BufferTarget::Vertex);
device.configure_vertex_attr(&vertex_array, &tile_offset_attr, &VertexAttrDescriptor {
size: 2,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I16,
stride: 4,
offset: 0,
divisor: 0,
buffer_index: 0,
});
device.bind_buffer(
&vertex_array,
quad_vertex_positions_buffer,
BufferTarget::Vertex,
);
device.configure_vertex_attr(
&vertex_array,
&tile_offset_attr,
&VertexAttrDescriptor {
size: 2,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I16,
stride: 4,
offset: 0,
divisor: 0,
buffer_index: 0,
},
);
device.bind_buffer(&vertex_array, &vertex_buffer, BufferTarget::Vertex);
device.configure_vertex_attr(&vertex_array, &tile_index_attr, &VertexAttrDescriptor {
size: 1,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I32,
stride: CLIP_TILE_INSTANCE_SIZE / 2,
offset: 0,
divisor: 1,
buffer_index: 1,
});
device.bind_buffer(&vertex_array, quad_vertex_indices_buffer, BufferTarget::Index);
device.configure_vertex_attr(
&vertex_array,
&tile_index_attr,
&VertexAttrDescriptor {
size: 1,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I32,
stride: CLIP_TILE_INSTANCE_SIZE / 2,
offset: 0,
divisor: 1,
buffer_index: 1,
},
);
device.bind_buffer(
&vertex_array,
quad_vertex_indices_buffer,
BufferTarget::Index,
);
ClipTileCopyVertexArrayD3D9 { vertex_array }
}
}
pub(crate) struct ClipTileCombineVertexArrayD3D9<D> where D: Device {
pub(crate) struct ClipTileCombineVertexArrayD3D9<D>
where
D: Device,
{
pub(crate) vertex_array: D::VertexArray,
}
impl<D> ClipTileCombineVertexArrayD3D9<D> where D: Device {
pub(crate) fn new(device: &D,
clip_tile_combine_program: &ClipTileCombineProgramD3D9<D>,
vertex_buffer: &D::Buffer,
quad_vertex_positions_buffer: &D::Buffer,
quad_vertex_indices_buffer: &D::Buffer)
-> ClipTileCombineVertexArrayD3D9<D> {
impl<D> ClipTileCombineVertexArrayD3D9<D>
where
D: Device,
{
pub(crate) fn new(
device: &D,
clip_tile_combine_program: &ClipTileCombineProgramD3D9<D>,
vertex_buffer: &D::Buffer,
quad_vertex_positions_buffer: &D::Buffer,
quad_vertex_indices_buffer: &D::Buffer,
) -> ClipTileCombineVertexArrayD3D9<D> {
let vertex_array = device.create_vertex_array();
let tile_offset_attr =
device.get_vertex_attr(&clip_tile_combine_program.program, "TileOffset").unwrap();
let dest_tile_index_attr =
device.get_vertex_attr(&clip_tile_combine_program.program, "DestTileIndex").unwrap();
let dest_backdrop_attr =
device.get_vertex_attr(&clip_tile_combine_program.program, "DestBackdrop").unwrap();
let src_tile_index_attr =
device.get_vertex_attr(&clip_tile_combine_program.program, "SrcTileIndex").unwrap();
let src_backdrop_attr =
device.get_vertex_attr(&clip_tile_combine_program.program, "SrcBackdrop").unwrap();
let tile_offset_attr = device
.get_vertex_attr(&clip_tile_combine_program.program, "TileOffset")
.unwrap();
let dest_tile_index_attr = device
.get_vertex_attr(&clip_tile_combine_program.program, "DestTileIndex")
.unwrap();
let dest_backdrop_attr = device
.get_vertex_attr(&clip_tile_combine_program.program, "DestBackdrop")
.unwrap();
let src_tile_index_attr = device
.get_vertex_attr(&clip_tile_combine_program.program, "SrcTileIndex")
.unwrap();
let src_backdrop_attr = device
.get_vertex_attr(&clip_tile_combine_program.program, "SrcBackdrop")
.unwrap();
device.bind_buffer(&vertex_array, quad_vertex_positions_buffer, BufferTarget::Vertex);
device.configure_vertex_attr(&vertex_array, &tile_offset_attr, &VertexAttrDescriptor {
size: 2,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I16,
stride: 4,
offset: 0,
divisor: 0,
buffer_index: 0,
});
device.bind_buffer(
&vertex_array,
quad_vertex_positions_buffer,
BufferTarget::Vertex,
);
device.configure_vertex_attr(
&vertex_array,
&tile_offset_attr,
&VertexAttrDescriptor {
size: 2,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I16,
stride: 4,
offset: 0,
divisor: 0,
buffer_index: 0,
},
);
device.bind_buffer(&vertex_array, &vertex_buffer, BufferTarget::Vertex);
device.configure_vertex_attr(&vertex_array, &dest_tile_index_attr, &VertexAttrDescriptor {
size: 1,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I32,
stride: CLIP_TILE_INSTANCE_SIZE,
offset: 0,
divisor: 1,
buffer_index: 1,
});
device.configure_vertex_attr(&vertex_array, &dest_backdrop_attr, &VertexAttrDescriptor {
size: 1,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I32,
stride: CLIP_TILE_INSTANCE_SIZE,
offset: 4,
divisor: 1,
buffer_index: 1,
});
device.configure_vertex_attr(&vertex_array, &src_tile_index_attr, &VertexAttrDescriptor {
size: 1,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I32,
stride: CLIP_TILE_INSTANCE_SIZE,
offset: 8,
divisor: 1,
buffer_index: 1,
});
device.configure_vertex_attr(&vertex_array, &src_backdrop_attr, &VertexAttrDescriptor {
size: 1,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I32,
stride: CLIP_TILE_INSTANCE_SIZE,
offset: 12,
divisor: 1,
buffer_index: 1,
});
device.bind_buffer(&vertex_array, quad_vertex_indices_buffer, BufferTarget::Index);
device.configure_vertex_attr(
&vertex_array,
&dest_tile_index_attr,
&VertexAttrDescriptor {
size: 1,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I32,
stride: CLIP_TILE_INSTANCE_SIZE,
offset: 0,
divisor: 1,
buffer_index: 1,
},
);
device.configure_vertex_attr(
&vertex_array,
&dest_backdrop_attr,
&VertexAttrDescriptor {
size: 1,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I32,
stride: CLIP_TILE_INSTANCE_SIZE,
offset: 4,
divisor: 1,
buffer_index: 1,
},
);
device.configure_vertex_attr(
&vertex_array,
&src_tile_index_attr,
&VertexAttrDescriptor {
size: 1,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I32,
stride: CLIP_TILE_INSTANCE_SIZE,
offset: 8,
divisor: 1,
buffer_index: 1,
},
);
device.configure_vertex_attr(
&vertex_array,
&src_backdrop_attr,
&VertexAttrDescriptor {
size: 1,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I32,
stride: CLIP_TILE_INSTANCE_SIZE,
offset: 12,
divisor: 1,
buffer_index: 1,
},
);
device.bind_buffer(
&vertex_array,
quad_vertex_indices_buffer,
BufferTarget::Index,
);
ClipTileCombineVertexArrayD3D9 { vertex_array }
}
}
pub(crate) struct CopyTileVertexArray<D> where D: Device {
pub(crate) struct CopyTileVertexArray<D>
where
D: Device,
{
pub(crate) vertex_array: D::VertexArray,
}
impl<D> CopyTileVertexArray<D> where D: Device {
pub(crate) fn new(device: &D,
copy_tile_program: &CopyTileProgram<D>,
copy_tile_vertex_buffer: &D::Buffer,
quads_vertex_indices_buffer: &D::Buffer)
-> CopyTileVertexArray<D> {
impl<D> CopyTileVertexArray<D>
where
D: Device,
{
pub(crate) fn new(
device: &D,
copy_tile_program: &CopyTileProgram<D>,
copy_tile_vertex_buffer: &D::Buffer,
quads_vertex_indices_buffer: &D::Buffer,
) -> CopyTileVertexArray<D> {
let vertex_array = device.create_vertex_array();
let tile_position_attr =
device.get_vertex_attr(&copy_tile_program.program, "TilePosition").unwrap();
let tile_position_attr = device
.get_vertex_attr(&copy_tile_program.program, "TilePosition")
.unwrap();
device.bind_buffer(&vertex_array, copy_tile_vertex_buffer, BufferTarget::Vertex);
device.configure_vertex_attr(&vertex_array, &tile_position_attr, &VertexAttrDescriptor {
size: 2,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I16,
stride: TILE_INSTANCE_SIZE,
offset: 0,
divisor: 0,
buffer_index: 0,
});
device.bind_buffer(&vertex_array, quads_vertex_indices_buffer, BufferTarget::Index);
device.configure_vertex_attr(
&vertex_array,
&tile_position_attr,
&VertexAttrDescriptor {
size: 2,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I16,
stride: TILE_INSTANCE_SIZE,
offset: 0,
divisor: 0,
buffer_index: 0,
},
);
device.bind_buffer(
&vertex_array,
quads_vertex_indices_buffer,
BufferTarget::Index,
);
CopyTileVertexArray { vertex_array }
}
}
pub(crate) struct FillProgramD3D9<D> where D: Device {
pub(crate) struct FillProgramD3D9<D>
where
D: Device,
{
pub(crate) program: D::Program,
pub(crate) framebuffer_size_uniform: D::Uniform,
pub(crate) tile_size_uniform: D::Uniform,
pub(crate) area_lut_texture: D::TextureParameter,
}
impl<D> FillProgramD3D9<D> where D: Device {
impl<D> FillProgramD3D9<D>
where
D: Device,
{
fn new(device: &D, resources: &dyn ResourceLoader) -> FillProgramD3D9<D> {
let program = device.create_raster_program(resources, "d3d9/fill");
let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize");
@ -331,54 +496,86 @@ impl<D> FillProgramD3D9<D> where D: Device {
}
}
pub(crate) struct TileProgramD3D9<D> where D: Device {
pub(crate) struct TileProgramD3D9<D>
where
D: Device,
{
pub(crate) common: TileProgramCommon<D>,
pub(crate) dest_texture: D::TextureParameter,
pub(crate) transform_uniform: D::Uniform,
}
impl<D> TileProgramD3D9<D> where D: Device {
impl<D> TileProgramD3D9<D>
where
D: Device,
{
fn new(device: &D, resources: &dyn ResourceLoader) -> TileProgramD3D9<D> {
let program = device.create_raster_program(resources, "d3d9/tile");
let dest_texture = device.get_texture_parameter(&program, "DestTexture");
let transform_uniform = device.get_uniform(&program, "Transform");
let common = TileProgramCommon::new(device, program);
TileProgramD3D9 { common, dest_texture, transform_uniform }
TileProgramD3D9 {
common,
dest_texture,
transform_uniform,
}
}
}
pub(crate) struct ClipTileCombineProgramD3D9<D> where D: Device {
pub(crate) struct ClipTileCombineProgramD3D9<D>
where
D: Device,
{
pub(crate) program: D::Program,
pub(crate) src_texture: D::TextureParameter,
pub(crate) framebuffer_size_uniform: D::Uniform,
}
impl<D> ClipTileCombineProgramD3D9<D> where D: Device {
pub(crate) fn new(device: &D, resources: &dyn ResourceLoader)
-> ClipTileCombineProgramD3D9<D> {
impl<D> ClipTileCombineProgramD3D9<D>
where
D: Device,
{
pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> ClipTileCombineProgramD3D9<D> {
let program = device.create_raster_program(resources, "d3d9/tile_clip_combine");
let src_texture = device.get_texture_parameter(&program, "Src");
let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize");
ClipTileCombineProgramD3D9 { program, src_texture, framebuffer_size_uniform }
ClipTileCombineProgramD3D9 {
program,
src_texture,
framebuffer_size_uniform,
}
}
}
pub(crate) struct ClipTileCopyProgramD3D9<D> where D: Device {
pub(crate) struct ClipTileCopyProgramD3D9<D>
where
D: Device,
{
pub(crate) program: D::Program,
pub(crate) src_texture: D::TextureParameter,
pub(crate) framebuffer_size_uniform: D::Uniform,
}
impl<D> ClipTileCopyProgramD3D9<D> where D: Device {
impl<D> ClipTileCopyProgramD3D9<D>
where
D: Device,
{
pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> ClipTileCopyProgramD3D9<D> {
let program = device.create_raster_program(resources, "d3d9/tile_clip_copy");
let src_texture = device.get_texture_parameter(&program, "Src");
let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize");
ClipTileCopyProgramD3D9 { program, src_texture, framebuffer_size_uniform }
ClipTileCopyProgramD3D9 {
program,
src_texture,
framebuffer_size_uniform,
}
}
}
pub(crate) struct CopyTileProgram<D> where D: Device {
pub(crate) struct CopyTileProgram<D>
where
D: Device,
{
pub(crate) program: D::Program,
pub(crate) transform_uniform: D::Uniform,
pub(crate) tile_size_uniform: D::Uniform,
@ -386,7 +583,10 @@ pub(crate) struct CopyTileProgram<D> where D: Device {
pub(crate) src_texture: D::TextureParameter,
}
impl<D> CopyTileProgram<D> where D: Device {
impl<D> CopyTileProgram<D>
where
D: Device,
{
pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> CopyTileProgram<D> {
let program = device.create_raster_program(resources, "d3d9/tile_copy");
let transform_uniform = device.get_uniform(&program, "Transform");
@ -403,7 +603,10 @@ impl<D> CopyTileProgram<D> where D: Device {
}
}
pub(crate) struct ProgramsD3D9<D> where D: Device {
pub(crate) struct ProgramsD3D9<D>
where
D: Device,
{
pub(crate) fill_program: FillProgramD3D9<D>,
pub(crate) tile_program: TileProgramD3D9<D>,
pub(crate) tile_clip_copy_program: ClipTileCopyProgramD3D9<D>,
@ -411,7 +614,10 @@ pub(crate) struct ProgramsD3D9<D> where D: Device {
pub(crate) tile_copy_program: CopyTileProgram<D>,
}
impl<D> ProgramsD3D9<D> where D: Device {
impl<D> ProgramsD3D9<D>
where
D: Device,
{
pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> ProgramsD3D9<D> {
ProgramsD3D9 {
fill_program: FillProgramD3D9::new(device, resources),

View File

@ -18,11 +18,11 @@
use crate::gpu::options::RendererLevel;
use crate::gpu::perf::{RenderStats, RenderTime};
use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::vector::{Vector2I, vec2i};
use pathfinder_gpu::Device;
use pathfinder_geometry::vector::{vec2i, Vector2I};
use pathfinder_gpu::allocator::GPUMemoryAllocator;
use pathfinder_gpu::Device;
use pathfinder_resources::ResourceLoader;
use pathfinder_ui::{FONT_ASCENT, LINE_HEIGHT, PADDING, UIPresenter, WINDOW_COLOR};
use pathfinder_ui::{UIPresenter, FONT_ASCENT, LINE_HEIGHT, PADDING, WINDOW_COLOR};
use std::collections::VecDeque;
use std::ops::{Add, Div};
use std::time::Duration;
@ -40,9 +40,12 @@ const INFO_WINDOW_WIDTH: i32 = 425;
const INFO_WINDOW_HEIGHT: i32 = LINE_HEIGHT * 2 + PADDING + 2;
/// Manages the debug UI.
pub struct DebugUIPresenter<D> where D: Device {
pub struct DebugUIPresenter<D>
where
D: Device,
{
/// The general UI presenter object.
///
///
/// You can use this to draw your own application-specific debug widgets.
pub ui_presenter: UIPresenter<D>,
@ -53,12 +56,16 @@ pub struct DebugUIPresenter<D> where D: Device {
renderer_level: RendererLevel,
}
impl<D> DebugUIPresenter<D> where D: Device {
pub(crate) fn new(device: &D,
resources: &dyn ResourceLoader,
framebuffer_size: Vector2I,
renderer_level: RendererLevel)
-> DebugUIPresenter<D> {
impl<D> DebugUIPresenter<D>
where
D: Device,
{
pub(crate) fn new(
device: &D,
resources: &dyn ResourceLoader,
framebuffer_size: Vector2I,
renderer_level: RendererLevel,
) -> DebugUIPresenter<D> {
let ui_presenter = UIPresenter::new(device, resources, framebuffer_size);
DebugUIPresenter {
ui_presenter,
@ -90,29 +97,35 @@ impl<D> DebugUIPresenter<D> where D: Device {
let framebuffer_size = self.ui_presenter.framebuffer_size();
let bottom = framebuffer_size.y() - PADDING;
let window_rect = RectI::new(
vec2i(framebuffer_size.x() - PADDING - INFO_WINDOW_WIDTH,
bottom - INFO_WINDOW_HEIGHT),
vec2i(
framebuffer_size.x() - PADDING - INFO_WINDOW_WIDTH,
bottom - INFO_WINDOW_HEIGHT,
),
vec2i(INFO_WINDOW_WIDTH, INFO_WINDOW_HEIGHT),
);
self.ui_presenter.draw_solid_rounded_rect(device, allocator, window_rect, WINDOW_COLOR);
self.ui_presenter
.draw_solid_rounded_rect(device, allocator, window_rect, WINDOW_COLOR);
let origin = window_rect.origin() + vec2i(PADDING, PADDING + FONT_ASCENT);
let level = match self.renderer_level {
RendererLevel::D3D9 => "D3D9",
RendererLevel::D3D11 => "D3D11",
};
self.ui_presenter.draw_text(device,
allocator,
&format!("{} ({} level)", self.backend_name, level),
origin + vec2i(0, LINE_HEIGHT * 0),
false);
self.ui_presenter.draw_text(device,
allocator,
&self.device_name,
origin + vec2i(0, LINE_HEIGHT * 1),
false);
self.ui_presenter.draw_text(
device,
allocator,
&format!("{} ({} level)", self.backend_name, level),
origin + vec2i(0, LINE_HEIGHT * 0),
false,
);
self.ui_presenter.draw_text(
device,
allocator,
&self.device_name,
origin + vec2i(0, LINE_HEIGHT * 1),
false,
);
}
fn performance_window_size(&self) -> Vector2I {
@ -130,16 +143,20 @@ impl<D> DebugUIPresenter<D> where D: Device {
let framebuffer_size = self.ui_presenter.framebuffer_size();
let bottom = framebuffer_size.y() - PADDING;
let window_rect = RectI::new(
vec2i(framebuffer_size.x() - PADDING - STATS_WINDOW_WIDTH,
bottom -
PADDING -
INFO_WINDOW_HEIGHT -
performance_window_height -
PADDING -
STATS_WINDOW_HEIGHT),
vec2i(STATS_WINDOW_WIDTH, STATS_WINDOW_HEIGHT));
vec2i(
framebuffer_size.x() - PADDING - STATS_WINDOW_WIDTH,
bottom
- PADDING
- INFO_WINDOW_HEIGHT
- performance_window_height
- PADDING
- STATS_WINDOW_HEIGHT,
),
vec2i(STATS_WINDOW_WIDTH, STATS_WINDOW_HEIGHT),
);
self.ui_presenter.draw_solid_rounded_rect(device, allocator, window_rect, WINDOW_COLOR);
self.ui_presenter
.draw_solid_rounded_rect(device, allocator, window_rect, WINDOW_COLOR);
let mean_cpu_sample = self.cpu_samples.mean();
let origin = window_rect.origin() + vec2i(PADDING, PADDING + FONT_ASCENT);
@ -179,11 +196,15 @@ impl<D> DebugUIPresenter<D> where D: Device {
let framebuffer_size = self.ui_presenter.framebuffer_size();
let bottom = framebuffer_size.y() - PADDING;
let window_rect = RectI::new(
vec2i(framebuffer_size.x() - PADDING - performance_window_size.x(),
bottom - INFO_WINDOW_HEIGHT - PADDING - performance_window_size.y()),
performance_window_size);
vec2i(
framebuffer_size.x() - PADDING - performance_window_size.x(),
bottom - INFO_WINDOW_HEIGHT - PADDING - performance_window_size.y(),
),
performance_window_size,
);
self.ui_presenter.draw_solid_rounded_rect(device, allocator, window_rect, WINDOW_COLOR);
self.ui_presenter
.draw_solid_rounded_rect(device, allocator, window_rect, WINDOW_COLOR);
let mean_cpu_sample = self.cpu_samples.mean();
let mean_gpu_sample = self.gpu_samples.mean();
@ -201,8 +222,10 @@ impl<D> DebugUIPresenter<D> where D: Device {
self.ui_presenter.draw_text(
device,
allocator,
&format!("VRAM Alloc.: {:.1} MB",
mean_cpu_sample.gpu_bytes_allocated as f64 / (1024.0 * 1024.0)),
&format!(
"VRAM Alloc.: {:.1} MB",
mean_cpu_sample.gpu_bytes_allocated as f64 / (1024.0 * 1024.0)
),
origin + vec2i(0, current_y),
false,
);
@ -210,8 +233,10 @@ impl<D> DebugUIPresenter<D> where D: Device {
self.ui_presenter.draw_text(
device,
allocator,
&format!("VRAM Commit: {:.1} MB",
mean_cpu_sample.gpu_bytes_committed as f64 / (1024.0 * 1024.0)),
&format!(
"VRAM Commit: {:.1} MB",
mean_cpu_sample.gpu_bytes_committed as f64 / (1024.0 * 1024.0)
),
origin + vec2i(0, current_y),
false,
);
@ -220,7 +245,10 @@ impl<D> DebugUIPresenter<D> where D: Device {
self.ui_presenter.draw_text(
device,
allocator,
&format!("CPU: {:.3} ms", duration_to_ms(mean_cpu_sample.cpu_build_time)),
&format!(
"CPU: {:.3} ms",
duration_to_ms(mean_cpu_sample.cpu_build_time)
),
origin + vec2i(0, current_y),
false,
);
@ -231,7 +259,10 @@ impl<D> DebugUIPresenter<D> where D: Device {
self.ui_presenter.draw_text(
device,
allocator,
&format!("GPU Dice: {:.3} ms", duration_to_ms(mean_gpu_sample.dice_time)),
&format!(
"GPU Dice: {:.3} ms",
duration_to_ms(mean_gpu_sample.dice_time)
),
origin + vec2i(0, current_y),
false,
);
@ -239,7 +270,10 @@ impl<D> DebugUIPresenter<D> where D: Device {
self.ui_presenter.draw_text(
device,
allocator,
&format!("GPU Bin: {:.3} ms", duration_to_ms(mean_gpu_sample.bin_time)),
&format!(
"GPU Bin: {:.3} ms",
duration_to_ms(mean_gpu_sample.bin_time)
),
origin + vec2i(0, current_y),
false,
);
@ -250,7 +284,10 @@ impl<D> DebugUIPresenter<D> where D: Device {
self.ui_presenter.draw_text(
device,
allocator,
&format!("GPU Fill: {:.3} ms", duration_to_ms(mean_gpu_sample.fill_time)),
&format!(
"GPU Fill: {:.3} ms",
duration_to_ms(mean_gpu_sample.fill_time)
),
origin + vec2i(0, current_y),
false,
);
@ -258,7 +295,10 @@ impl<D> DebugUIPresenter<D> where D: Device {
self.ui_presenter.draw_text(
device,
allocator,
&format!("GPU Comp.: {:.3} ms", duration_to_ms(mean_gpu_sample.composite_time)),
&format!(
"GPU Comp.: {:.3} ms",
duration_to_ms(mean_gpu_sample.composite_time)
),
origin + vec2i(0, current_y),
false,
);
@ -266,7 +306,10 @@ impl<D> DebugUIPresenter<D> where D: Device {
self.ui_presenter.draw_text(
device,
allocator,
&format!("GPU Other: {:.3} ms", duration_to_ms(mean_gpu_sample.other_time)),
&format!(
"GPU Other: {:.3} ms",
duration_to_ms(mean_gpu_sample.other_time)
),
origin + vec2i(0, current_y),
false,
);
@ -274,18 +317,18 @@ impl<D> DebugUIPresenter<D> where D: Device {
let mut wallclock_time = match self.renderer_level {
RendererLevel::D3D11 => {
duration_to_ms(mean_cpu_sample.cpu_build_time) +
duration_to_ms(mean_gpu_sample.fill_time)
}
RendererLevel::D3D9 => {
f64::max(duration_to_ms(mean_cpu_sample.cpu_build_time),
duration_to_ms(mean_gpu_sample.fill_time))
duration_to_ms(mean_cpu_sample.cpu_build_time)
+ duration_to_ms(mean_gpu_sample.fill_time)
}
RendererLevel::D3D9 => f64::max(
duration_to_ms(mean_cpu_sample.cpu_build_time),
duration_to_ms(mean_gpu_sample.fill_time),
),
};
wallclock_time += duration_to_ms(mean_gpu_sample.composite_time) +
duration_to_ms(mean_gpu_sample.dice_time) +
duration_to_ms(mean_gpu_sample.bin_time) +
duration_to_ms(mean_gpu_sample.other_time);
wallclock_time += duration_to_ms(mean_gpu_sample.composite_time)
+ duration_to_ms(mean_gpu_sample.dice_time)
+ duration_to_ms(mean_gpu_sample.bin_time)
+ duration_to_ms(mean_gpu_sample.other_time);
self.ui_presenter.draw_text(
device,
allocator,
@ -294,7 +337,6 @@ impl<D> DebugUIPresenter<D> where D: Device {
false,
);
}
}
struct SampleBuffer<S>

View File

@ -10,8 +10,8 @@
//! The GPU renderer for Pathfinder 3.
pub mod d3d9;
pub mod d3d11;
pub mod d3d9;
pub mod debug;
pub mod options;
pub mod perf;

View File

@ -22,7 +22,10 @@ pub struct RendererMode {
}
/// Options that influence rendering that can be changed at runtime.
pub struct RendererOptions<D> where D: Device {
pub struct RendererOptions<D>
where
D: Device,
{
/// Where the rendering should go: either to the default framebuffer (i.e. screen) or to a
/// custom framebuffer.
pub dest: DestFramebuffer<D>,
@ -48,12 +51,20 @@ pub enum RendererLevel {
impl RendererMode {
/// Creates a new `RendererMode` with a suitable API level for the given GPU device.
#[inline]
pub fn default_for_device<D>(device: &D) -> RendererMode where D: Device {
RendererMode { level: RendererLevel::default_for_device(device) }
pub fn default_for_device<D>(device: &D) -> RendererMode
where
D: Device,
{
RendererMode {
level: RendererLevel::default_for_device(device),
}
}
}
impl<D> Default for RendererOptions<D> where D: Device {
impl<D> Default for RendererOptions<D>
where
D: Device,
{
#[inline]
fn default() -> RendererOptions<D> {
RendererOptions {
@ -66,7 +77,10 @@ impl<D> Default for RendererOptions<D> where D: Device {
impl RendererLevel {
/// Returns a suitable renderer level for the given device.
pub fn default_for_device<D>(device: &D) -> RendererLevel where D: Device {
pub fn default_for_device<D>(device: &D) -> RendererLevel
where
D: Device,
{
match device.feature_level() {
FeatureLevel::D3D10 => RendererLevel::D3D9,
FeatureLevel::D3D11 => RendererLevel::D3D11,
@ -76,7 +90,10 @@ impl RendererLevel {
/// Where the rendered content should go.
#[derive(Clone)]
pub enum DestFramebuffer<D> where D: Device {
pub enum DestFramebuffer<D>
where
D: Device,
{
/// The rendered content should go to the default framebuffer (e.g. the window in OpenGL).
Default {
/// The rectangle within the window to draw in, in device pixels.
@ -88,22 +105,34 @@ pub enum DestFramebuffer<D> where D: Device {
Other(D::Framebuffer),
}
impl<D> Default for DestFramebuffer<D> where D: Device {
impl<D> Default for DestFramebuffer<D>
where
D: Device,
{
#[inline]
fn default() -> DestFramebuffer<D> {
DestFramebuffer::Default { viewport: RectI::default(), window_size: Vector2I::default() }
DestFramebuffer::Default {
viewport: RectI::default(),
window_size: Vector2I::default(),
}
}
}
impl<D> DestFramebuffer<D> where D: Device {
impl<D> DestFramebuffer<D>
where
D: Device,
{
/// Returns a `DestFramebuffer` object that renders to the entire contents of the default
/// framebuffer.
///
///
/// The `window_size` parameter specifies the size of the window in device pixels.
#[inline]
pub fn full_window(window_size: Vector2I) -> DestFramebuffer<D> {
let viewport = RectI::new(Vector2I::default(), window_size);
DestFramebuffer::Default { viewport, window_size }
DestFramebuffer::Default {
viewport,
window_size,
}
}
/// Returns the size of the destination buffer, in device pixels.

View File

@ -22,7 +22,7 @@ pub struct RenderStats {
/// The total number of path objects in the scene.
pub path_count: usize,
/// The number of fill operations it took to render the scene.
///
///
/// A fill operation is a single edge in a 16x16 device pixel tile.
pub fill_count: usize,
/// The total number of 16x16 device pixel tile masks generated.
@ -35,7 +35,7 @@ pub struct RenderStats {
/// The number of GPU API draw calls it took to render the scene.
pub drawcall_count: u32,
/// The number of bytes of VRAM Pathfinder has allocated.
///
///
/// This may be higher than `gpu_bytes_committed` because Pathfinder caches some data for
/// faster reuse.
pub gpu_bytes_allocated: u64,
@ -75,11 +75,17 @@ impl Div<usize> for RenderStats {
}
}
pub(crate) struct TimerQueryCache<D> where D: Device {
pub(crate) struct TimerQueryCache<D>
where
D: Device,
{
free_queries: Vec<D::TimerQuery>,
}
pub(crate) struct PendingTimer<D> where D: Device {
pub(crate) struct PendingTimer<D>
where
D: Device,
{
pub(crate) dice_times: Vec<TimerFuture<D>>,
pub(crate) bin_times: Vec<TimerFuture<D>>,
pub(crate) fill_times: Vec<TimerFuture<D>>,
@ -87,7 +93,10 @@ pub(crate) struct PendingTimer<D> where D: Device {
pub(crate) other_times: Vec<TimerFuture<D>>,
}
pub(crate) enum TimerFuture<D> where D: Device {
pub(crate) enum TimerFuture<D>
where
D: Device,
{
Pending(D::TimerQuery),
Resolved(Duration),
}
@ -101,21 +110,31 @@ pub(crate) enum TimeCategory {
Other,
}
impl<D> TimerQueryCache<D> where D: Device {
impl<D> TimerQueryCache<D>
where
D: Device,
{
pub(crate) fn new() -> TimerQueryCache<D> {
TimerQueryCache { free_queries: vec![] }
TimerQueryCache {
free_queries: vec![],
}
}
pub(crate) fn alloc(&mut self, device: &D) -> D::TimerQuery {
self.free_queries.pop().unwrap_or_else(|| device.create_timer_query())
self.free_queries
.pop()
.unwrap_or_else(|| device.create_timer_query())
}
pub(crate) fn free(&mut self, old_query: D::TimerQuery) {
self.free_queries.push(old_query);
}
pub(crate) fn start_timing_draw_call(&mut self, device: &D, options: &RendererOptions<D>)
-> Option<D::TimerQuery> {
pub(crate) fn start_timing_draw_call(
&mut self,
device: &D,
options: &RendererOptions<D>,
) -> Option<D::TimerQuery> {
if !options.show_debug_ui {
return None;
}
@ -126,7 +145,10 @@ impl<D> TimerQueryCache<D> where D: Device {
}
}
impl<D> PendingTimer<D> where D: Device {
impl<D> PendingTimer<D>
where
D: Device,
{
pub(crate) fn new() -> PendingTimer<D> {
PendingTimer {
dice_times: vec![],
@ -139,10 +161,14 @@ impl<D> PendingTimer<D> where D: Device {
pub(crate) fn poll(&mut self, device: &D) -> Vec<D::TimerQuery> {
let mut old_queries = vec![];
for future in self.dice_times.iter_mut().chain(self.bin_times.iter_mut())
.chain(self.fill_times.iter_mut())
.chain(self.composite_times.iter_mut())
.chain(self.other_times.iter_mut()) {
for future in self
.dice_times
.iter_mut()
.chain(self.bin_times.iter_mut())
.chain(self.fill_times.iter_mut())
.chain(self.composite_times.iter_mut())
.chain(self.other_times.iter_mut())
{
if let Some(old_query) = future.poll(device) {
old_queries.push(old_query)
}
@ -157,20 +183,28 @@ impl<D> PendingTimer<D> where D: Device {
let composite_time = total_time_of_timer_futures(&self.composite_times);
let other_time = total_time_of_timer_futures(&self.other_times);
match (dice_time, bin_time, fill_time, composite_time, other_time) {
(Some(dice_time),
Some(bin_time),
Some(fill_time),
Some(composite_time),
Some(other_time)) => {
Some(RenderTime { dice_time, bin_time, fill_time, composite_time, other_time })
}
(
Some(dice_time),
Some(bin_time),
Some(fill_time),
Some(composite_time),
Some(other_time),
) => Some(RenderTime {
dice_time,
bin_time,
fill_time,
composite_time,
other_time,
}),
_ => None,
}
}
pub(crate) fn push_query(&mut self,
time_category: TimeCategory,
timer_query: Option<D::TimerQuery>) {
pub(crate) fn push_query(
&mut self,
time_category: TimeCategory,
timer_query: Option<D::TimerQuery>,
) {
let timer_future = match timer_query {
None => return,
Some(timer_query) => TimerFuture::new(timer_query),
@ -185,7 +219,10 @@ impl<D> PendingTimer<D> where D: Device {
}
}
impl<D> TimerFuture<D> where D: Device {
impl<D> TimerFuture<D>
where
D: Device,
{
pub(crate) fn new(query: D::TimerQuery) -> TimerFuture<D> {
TimerFuture::Pending(query)
}
@ -197,17 +234,18 @@ impl<D> TimerFuture<D> where D: Device {
};
match duration {
None => None,
Some(duration) => {
match mem::replace(self, TimerFuture::Resolved(duration)) {
TimerFuture::Resolved(_) => unreachable!(),
TimerFuture::Pending(old_query) => Some(old_query),
}
}
Some(duration) => match mem::replace(self, TimerFuture::Resolved(duration)) {
TimerFuture::Resolved(_) => unreachable!(),
TimerFuture::Pending(old_query) => Some(old_query),
},
}
}
}
fn total_time_of_timer_futures<D>(futures: &[TimerFuture<D>]) -> Option<Duration> where D: Device {
fn total_time_of_timer_futures<D>(futures: &[TimerFuture<D>]) -> Option<Duration>
where
D: Device,
{
let mut total = Duration::default();
for future in futures {
match *future {
@ -222,11 +260,11 @@ fn total_time_of_timer_futures<D>(futures: &[TimerFuture<D>]) -> Option<Duration
#[derive(Clone, Copy, Debug)]
pub struct RenderTime {
/// How much GPU time it took to divide all edges in the scene into small lines.
///
///
/// This will be zero in the D3D9-level backend, since in that backend dicing is done on CPU.
pub dice_time: Duration,
/// How much GPU time it took to assign those diced microlines to tiles.
///
///
/// This will be zero in the D3D9-level backend, since in that backend binning is done on CPU.
pub bin_time: Duration,
/// How much GPU time it took to draw fills (i.e. render edges) to masks.

File diff suppressed because it is too large Load Diff

View File

@ -15,105 +15,173 @@ use pathfinder_resources::ResourceLoader;
// TODO(pcwalton): Replace with `mem::size_of` calls?
pub(crate) const TILE_INSTANCE_SIZE: usize = 16;
pub(crate) struct BlitVertexArray<D> where D: Device {
pub(crate) struct BlitVertexArray<D>
where
D: Device,
{
pub(crate) vertex_array: D::VertexArray,
}
impl<D> BlitVertexArray<D> where D: Device {
pub(crate) fn new(device: &D,
blit_program: &BlitProgram<D>,
quad_vertex_positions_buffer: &D::Buffer,
quad_vertex_indices_buffer: &D::Buffer)
-> BlitVertexArray<D> {
impl<D> BlitVertexArray<D>
where
D: Device,
{
pub(crate) fn new(
device: &D,
blit_program: &BlitProgram<D>,
quad_vertex_positions_buffer: &D::Buffer,
quad_vertex_indices_buffer: &D::Buffer,
) -> BlitVertexArray<D> {
let vertex_array = device.create_vertex_array();
let position_attr = device.get_vertex_attr(&blit_program.program, "Position").unwrap();
let position_attr = device
.get_vertex_attr(&blit_program.program, "Position")
.unwrap();
device.bind_buffer(&vertex_array, quad_vertex_positions_buffer, BufferTarget::Vertex);
device.configure_vertex_attr(&vertex_array, &position_attr, &VertexAttrDescriptor {
size: 2,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I16,
stride: 4,
offset: 0,
divisor: 0,
buffer_index: 0,
});
device.bind_buffer(&vertex_array, quad_vertex_indices_buffer, BufferTarget::Index);
device.bind_buffer(
&vertex_array,
quad_vertex_positions_buffer,
BufferTarget::Vertex,
);
device.configure_vertex_attr(
&vertex_array,
&position_attr,
&VertexAttrDescriptor {
size: 2,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I16,
stride: 4,
offset: 0,
divisor: 0,
buffer_index: 0,
},
);
device.bind_buffer(
&vertex_array,
quad_vertex_indices_buffer,
BufferTarget::Index,
);
BlitVertexArray { vertex_array }
}
}
pub(crate) struct VertexArraysCore<D> where D: Device {
pub(crate) struct VertexArraysCore<D>
where
D: Device,
{
pub(crate) blit_vertex_array: BlitVertexArray<D>,
}
impl<D> VertexArraysCore<D> where D: Device {
pub(crate) fn new(device: &D,
programs: &ProgramsCore<D>,
quad_vertex_positions_buffer: &D::Buffer,
quad_vertex_indices_buffer: &D::Buffer)
-> VertexArraysCore<D> {
impl<D> VertexArraysCore<D>
where
D: Device,
{
pub(crate) fn new(
device: &D,
programs: &ProgramsCore<D>,
quad_vertex_positions_buffer: &D::Buffer,
quad_vertex_indices_buffer: &D::Buffer,
) -> VertexArraysCore<D> {
VertexArraysCore {
blit_vertex_array: BlitVertexArray::new(device,
&programs.blit_program,
quad_vertex_positions_buffer,
quad_vertex_indices_buffer),
blit_vertex_array: BlitVertexArray::new(
device,
&programs.blit_program,
quad_vertex_positions_buffer,
quad_vertex_indices_buffer,
),
}
}
}
pub(crate) struct ClearVertexArray<D> where D: Device {
pub(crate) struct ClearVertexArray<D>
where
D: Device,
{
pub(crate) vertex_array: D::VertexArray,
}
impl<D> ClearVertexArray<D> where D: Device {
pub(crate) fn new(device: &D,
clear_program: &ClearProgram<D>,
quad_vertex_positions_buffer: &D::Buffer,
quad_vertex_indices_buffer: &D::Buffer)
-> ClearVertexArray<D> {
impl<D> ClearVertexArray<D>
where
D: Device,
{
pub(crate) fn new(
device: &D,
clear_program: &ClearProgram<D>,
quad_vertex_positions_buffer: &D::Buffer,
quad_vertex_indices_buffer: &D::Buffer,
) -> ClearVertexArray<D> {
let vertex_array = device.create_vertex_array();
let position_attr = device.get_vertex_attr(&clear_program.program, "Position").unwrap();
let position_attr = device
.get_vertex_attr(&clear_program.program, "Position")
.unwrap();
device.bind_buffer(&vertex_array, quad_vertex_positions_buffer, BufferTarget::Vertex);
device.configure_vertex_attr(&vertex_array, &position_attr, &VertexAttrDescriptor {
size: 2,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I16,
stride: 4,
offset: 0,
divisor: 0,
buffer_index: 0,
});
device.bind_buffer(&vertex_array, quad_vertex_indices_buffer, BufferTarget::Index);
device.bind_buffer(
&vertex_array,
quad_vertex_positions_buffer,
BufferTarget::Vertex,
);
device.configure_vertex_attr(
&vertex_array,
&position_attr,
&VertexAttrDescriptor {
size: 2,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I16,
stride: 4,
offset: 0,
divisor: 0,
buffer_index: 0,
},
);
device.bind_buffer(
&vertex_array,
quad_vertex_indices_buffer,
BufferTarget::Index,
);
ClearVertexArray { vertex_array }
}
}
pub(crate) struct BlitProgram<D> where D: Device {
pub(crate) struct BlitProgram<D>
where
D: Device,
{
pub(crate) program: D::Program,
pub(crate) dest_rect_uniform: D::Uniform,
pub(crate) framebuffer_size_uniform: D::Uniform,
pub(crate) src_texture: D::TextureParameter,
}
impl<D> BlitProgram<D> where D: Device {
impl<D> BlitProgram<D>
where
D: Device,
{
pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> BlitProgram<D> {
let program = device.create_raster_program(resources, "blit");
let dest_rect_uniform = device.get_uniform(&program, "DestRect");
let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize");
let src_texture = device.get_texture_parameter(&program, "Src");
BlitProgram { program, dest_rect_uniform, framebuffer_size_uniform, src_texture }
BlitProgram {
program,
dest_rect_uniform,
framebuffer_size_uniform,
src_texture,
}
}
}
pub(crate) struct ProgramsCore<D> where D: Device {
pub(crate) struct ProgramsCore<D>
where
D: Device,
{
pub(crate) blit_program: BlitProgram<D>,
}
impl<D> ProgramsCore<D> where D: Device {
impl<D> ProgramsCore<D>
where
D: Device,
{
pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> ProgramsCore<D> {
ProgramsCore {
blit_program: BlitProgram::new(device, resources),
@ -121,24 +189,38 @@ impl<D> ProgramsCore<D> where D: Device {
}
}
pub(crate) struct ClearProgram<D> where D: Device {
pub(crate) struct ClearProgram<D>
where
D: Device,
{
pub(crate) program: D::Program,
pub(crate) rect_uniform: D::Uniform,
pub(crate) framebuffer_size_uniform: D::Uniform,
pub(crate) color_uniform: D::Uniform,
}
impl<D> ClearProgram<D> where D: Device {
impl<D> ClearProgram<D>
where
D: Device,
{
pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> ClearProgram<D> {
let program = device.create_raster_program(resources, "clear");
let rect_uniform = device.get_uniform(&program, "Rect");
let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize");
let color_uniform = device.get_uniform(&program, "Color");
ClearProgram { program, rect_uniform, framebuffer_size_uniform, color_uniform }
ClearProgram {
program,
rect_uniform,
framebuffer_size_uniform,
color_uniform,
}
}
}
pub(crate) struct TileProgramCommon<D> where D: Device {
pub(crate) struct TileProgramCommon<D>
where
D: Device,
{
pub(crate) program: D::Program,
pub(crate) tile_size_uniform: D::Uniform,
pub(crate) texture_metadata_texture: D::TextureParameter,
@ -153,7 +235,10 @@ pub(crate) struct TileProgramCommon<D> where D: Device {
pub(crate) framebuffer_size_uniform: D::Uniform,
}
impl<D> TileProgramCommon<D> where D: Device {
impl<D> TileProgramCommon<D>
where
D: Device,
{
pub(crate) fn new(device: &D, program: D::Program) -> TileProgramCommon<D> {
let tile_size_uniform = device.get_uniform(&program, "TileSize");
let texture_metadata_texture = device.get_texture_parameter(&program, "TextureMetadata");
@ -184,89 +269,142 @@ impl<D> TileProgramCommon<D> where D: Device {
}
}
pub(crate) struct StencilProgram<D> where D: Device {
pub(crate) struct StencilProgram<D>
where
D: Device,
{
pub(crate) program: D::Program,
}
impl<D> StencilProgram<D> where D: Device {
impl<D> StencilProgram<D>
where
D: Device,
{
pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> StencilProgram<D> {
let program = device.create_raster_program(resources, "stencil");
StencilProgram { program }
}
}
pub(crate) struct StencilVertexArray<D> where D: Device {
pub(crate) struct StencilVertexArray<D>
where
D: Device,
{
pub(crate) vertex_array: D::VertexArray,
pub(crate) vertex_buffer: D::Buffer,
pub(crate) index_buffer: D::Buffer,
}
impl<D> StencilVertexArray<D> where D: Device {
impl<D> StencilVertexArray<D>
where
D: Device,
{
pub(crate) fn new(device: &D, stencil_program: &StencilProgram<D>) -> StencilVertexArray<D> {
let vertex_array = device.create_vertex_array();
let vertex_buffer = device.create_buffer(BufferUploadMode::Static);
let index_buffer = device.create_buffer(BufferUploadMode::Static);
let position_attr = device.get_vertex_attr(&stencil_program.program, "Position").unwrap();
let position_attr = device
.get_vertex_attr(&stencil_program.program, "Position")
.unwrap();
device.bind_buffer(&vertex_array, &vertex_buffer, BufferTarget::Vertex);
device.configure_vertex_attr(&vertex_array, &position_attr, &VertexAttrDescriptor {
size: 3,
class: VertexAttrClass::Float,
attr_type: VertexAttrType::F32,
stride: 4 * 4,
offset: 0,
divisor: 0,
buffer_index: 0,
});
device.configure_vertex_attr(
&vertex_array,
&position_attr,
&VertexAttrDescriptor {
size: 3,
class: VertexAttrClass::Float,
attr_type: VertexAttrType::F32,
stride: 4 * 4,
offset: 0,
divisor: 0,
buffer_index: 0,
},
);
device.bind_buffer(&vertex_array, &index_buffer, BufferTarget::Index);
StencilVertexArray { vertex_array, vertex_buffer, index_buffer }
StencilVertexArray {
vertex_array,
vertex_buffer,
index_buffer,
}
}
}
pub(crate) struct ReprojectionProgram<D> where D: Device {
pub(crate) struct ReprojectionProgram<D>
where
D: Device,
{
pub(crate) program: D::Program,
pub(crate) old_transform_uniform: D::Uniform,
pub(crate) new_transform_uniform: D::Uniform,
pub(crate) texture: D::TextureParameter,
}
impl<D> ReprojectionProgram<D> where D: Device {
impl<D> ReprojectionProgram<D>
where
D: Device,
{
pub(crate) fn new(device: &D, resources: &dyn ResourceLoader) -> ReprojectionProgram<D> {
let program = device.create_raster_program(resources, "reproject");
let old_transform_uniform = device.get_uniform(&program, "OldTransform");
let new_transform_uniform = device.get_uniform(&program, "NewTransform");
let texture = device.get_texture_parameter(&program, "Texture");
ReprojectionProgram { program, old_transform_uniform, new_transform_uniform, texture }
ReprojectionProgram {
program,
old_transform_uniform,
new_transform_uniform,
texture,
}
}
}
pub(crate) struct ReprojectionVertexArray<D> where D: Device {
pub(crate) struct ReprojectionVertexArray<D>
where
D: Device,
{
pub(crate) vertex_array: D::VertexArray,
}
impl<D> ReprojectionVertexArray<D> where D: Device {
pub(crate) fn new(device: &D,
reprojection_program: &ReprojectionProgram<D>,
quad_vertex_positions_buffer: &D::Buffer,
quad_vertex_indices_buffer: &D::Buffer)
-> ReprojectionVertexArray<D> {
impl<D> ReprojectionVertexArray<D>
where
D: Device,
{
pub(crate) fn new(
device: &D,
reprojection_program: &ReprojectionProgram<D>,
quad_vertex_positions_buffer: &D::Buffer,
quad_vertex_indices_buffer: &D::Buffer,
) -> ReprojectionVertexArray<D> {
let vertex_array = device.create_vertex_array();
let position_attr = device.get_vertex_attr(&reprojection_program.program, "Position")
.unwrap();
let position_attr = device
.get_vertex_attr(&reprojection_program.program, "Position")
.unwrap();
device.bind_buffer(&vertex_array, quad_vertex_positions_buffer, BufferTarget::Vertex);
device.configure_vertex_attr(&vertex_array, &position_attr, &VertexAttrDescriptor {
size: 2,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I16,
stride: 4,
offset: 0,
divisor: 0,
buffer_index: 0,
});
device.bind_buffer(&vertex_array, quad_vertex_indices_buffer, BufferTarget::Index);
device.bind_buffer(
&vertex_array,
quad_vertex_positions_buffer,
BufferTarget::Vertex,
);
device.configure_vertex_attr(
&vertex_array,
&position_attr,
&VertexAttrDescriptor {
size: 2,
class: VertexAttrClass::Int,
attr_type: VertexAttrType::I16,
stride: 4,
offset: 0,
divisor: 0,
buffer_index: 0,
},
);
device.bind_buffer(
&vertex_array,
quad_vertex_indices_buffer,
BufferTarget::Index,
);
ReprojectionVertexArray { vertex_array }
}

View File

@ -24,15 +24,15 @@ use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::vector::{Vector2F, Vector2I};
use pathfinder_gpu::TextureSamplingFlags;
use std::fmt::{Debug, Formatter, Result as DebugResult};
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::time::Duration;
use std::u32;
pub const TILE_CTRL_MASK_WINDING: i32 = 0x1;
pub const TILE_CTRL_MASK_WINDING: i32 = 0x1;
pub const TILE_CTRL_MASK_EVEN_ODD: i32 = 0x2;
pub const TILE_CTRL_MASK_0_SHIFT: i32 = 0;
pub const TILE_CTRL_MASK_0_SHIFT: i32 = 0;
pub enum RenderCommand {
// Starts rendering a frame.
@ -51,15 +51,24 @@ pub enum RenderCommand {
},
// Allocates a texture page.
AllocateTexturePage { page_id: TexturePageId, descriptor: TexturePageDescriptor },
AllocateTexturePage {
page_id: TexturePageId,
descriptor: TexturePageDescriptor,
},
// Uploads data to a texture page.
UploadTexelData { texels: Arc<Vec<ColorU>>, location: TextureLocation },
UploadTexelData {
texels: Arc<Vec<ColorU>>,
location: TextureLocation,
},
// Associates a render target with a texture page.
//
// TODO(pcwalton): Add a rect to this so we can render to subrects of a page.
DeclareRenderTarget { id: RenderTargetId, location: TextureLocation },
DeclareRenderTarget {
id: RenderTargetId,
location: TextureLocation,
},
// Upload texture metadata.
UploadTextureMetadata(Vec<TextureMetadataEntry>),
@ -71,7 +80,7 @@ pub enum RenderCommand {
FlushFillsD3D9,
/// Upload a scene to GPU.
///
///
/// This will only be sent if dicing and binning is done on GPU.
UploadSceneD3D11 {
draw_segments: SegmentsD3D11,
@ -95,7 +104,9 @@ pub enum RenderCommand {
DrawTilesD3D11(DrawTileBatchD3D11),
// Presents a rendered frame.
Finish { cpu_build_time: Duration },
Finish {
cpu_build_time: Duration,
},
}
#[derive(Clone, Copy, PartialEq, Debug, Default)]
@ -116,7 +127,7 @@ pub struct TextureLocation {
#[derive(Clone, Debug)]
pub struct TileBatchDataD3D11 {
/// The ID of this batch.
///
///
/// The renderer should not assume that these values are consecutive.
pub batch_id: TileBatchId,
/// The number of paths in this batch.
@ -147,7 +158,7 @@ pub struct PrepareTilesInfoD3D11 {
pub backdrops: Vec<BackdropInfoD3D11>,
/// Mapping from path index to metadata needed to compute propagation on GPU.
///
///
/// This contains indices into the `tiles` vector.
pub propagate_metadata: Vec<PropagateMetadataD3D11>,
@ -184,7 +195,7 @@ pub struct ClippedPathInfo {
pub clipped_path_count: u32,
/// The maximum number of clipped tiles.
///
///
/// This is used to allocate vertex buffers.
pub max_clipped_tile_count: u32,
@ -193,7 +204,7 @@ pub struct ClippedPathInfo {
}
/// Together with the `TileBatchId`, uniquely identifies a path on the renderer side.
///
///
/// Generally, `PathIndex(!0)` represents no path.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct PathBatchIndex(pub u32);
@ -438,8 +449,10 @@ impl PathBatchIndex {
impl AlphaTileId {
#[inline]
pub fn new(next_alpha_tile_index: &[AtomicUsize; ALPHA_TILE_LEVEL_COUNT], level: usize)
-> AlphaTileId {
pub fn new(
next_alpha_tile_index: &[AtomicUsize; ALPHA_TILE_LEVEL_COUNT],
level: usize,
) -> AlphaTileId {
let alpha_tile_index = next_alpha_tile_index[level].fetch_add(1, Ordering::Relaxed);
debug_assert!(alpha_tile_index < ALPHA_TILES_PER_LEVEL);
AlphaTileId((level * ALPHA_TILES_PER_LEVEL + alpha_tile_index) as u32)
@ -470,11 +483,22 @@ impl Debug for RenderCommand {
fn fmt(&self, formatter: &mut Formatter) -> DebugResult {
match *self {
RenderCommand::Start { .. } => write!(formatter, "Start"),
RenderCommand::AllocateTexturePage { page_id, descriptor: _ } => {
RenderCommand::AllocateTexturePage {
page_id,
descriptor: _,
} => {
write!(formatter, "AllocateTexturePage({})", page_id.0)
}
RenderCommand::UploadTexelData { ref texels, location } => {
write!(formatter, "UploadTexelData(x{:?}, {:?})", texels.len(), location)
RenderCommand::UploadTexelData {
ref texels,
location,
} => {
write!(
formatter,
"UploadTexelData(x{:?}, {:?})",
texels.len(),
location
)
}
RenderCommand::DeclareRenderTarget { id, location } => {
write!(formatter, "DeclareRenderTarget({:?}, {:?})", id, location)
@ -486,23 +510,29 @@ impl Debug for RenderCommand {
write!(formatter, "AddFillsD3D9(x{})", fills.len())
}
RenderCommand::FlushFillsD3D9 => write!(formatter, "FlushFills"),
RenderCommand::UploadSceneD3D11 { ref draw_segments, ref clip_segments } => {
write!(formatter,
"UploadSceneD3D11(DP x{}, DI x{}, CP x{}, CI x{})",
draw_segments.points.len(),
draw_segments.indices.len(),
clip_segments.points.len(),
clip_segments.indices.len())
RenderCommand::UploadSceneD3D11 {
ref draw_segments,
ref clip_segments,
} => {
write!(
formatter,
"UploadSceneD3D11(DP x{}, DI x{}, CP x{}, CI x{})",
draw_segments.points.len(),
draw_segments.indices.len(),
clip_segments.points.len(),
clip_segments.indices.len()
)
}
RenderCommand::PrepareClipTilesD3D11(ref batch) => {
let clipped_path_count = match batch.clipped_path_info {
None => 0,
Some(ref clipped_path_info) => clipped_path_info.clipped_path_count,
};
write!(formatter,
"PrepareClipTilesD3D11({:?}, C {})",
batch.batch_id,
clipped_path_count)
write!(
formatter,
"PrepareClipTilesD3D11({:?}, C {})",
batch.batch_id, clipped_path_count
)
}
RenderCommand::PushRenderTarget(render_target_id) => {
write!(formatter, "PushRenderTarget({:?})", render_target_id)
@ -512,13 +542,18 @@ impl Debug for RenderCommand {
write!(formatter, "DrawTilesD3D9(x{:?})", batch.tiles.len())
}
RenderCommand::DrawTilesD3D11(ref batch) => {
write!(formatter,
"DrawTilesD3D11({:?}, C0 {:?})",
batch.tile_batch_data.batch_id,
batch.color_texture)
write!(
formatter,
"DrawTilesD3D11({:?}, C0 {:?})",
batch.tile_batch_data.batch_id, batch.color_texture
)
}
RenderCommand::Finish { cpu_build_time } => {
write!(formatter, "Finish({} ms)", cpu_build_time.as_secs_f64() * 1000.0)
write!(
formatter,
"Finish({} ms)",
cpu_build_time.as_secs_f64() * 1000.0
)
}
}
}

View File

@ -21,7 +21,7 @@ use pathfinder_geometry::vector::{Vector2F, Vector4F};
use pathfinder_content::clip::PolygonClipper3D;
/// A sink for the render commands that scenes build.
///
///
/// In single-threaded operation, this object typically buffers commands into an array and then,
/// once scene building is complete, commands are all sent to the output at once. In multithreaded
/// operation, on the other hand, commands are sent to the renderer on the fly as they're built.
@ -129,9 +129,10 @@ impl RenderTransform {
}
let inverse_transform = perspective.transform.inverse();
let clip_polygon = points.into_iter()
.map(|point| (inverse_transform * point).to_2d())
.collect();
let clip_polygon = points
.into_iter()
.map(|point| (inverse_transform * point).to_2d())
.collect();
return PreparedRenderTransform::Perspective {
perspective,
clip_polygon,
@ -166,17 +167,13 @@ impl PreparedBuildOptions {
pub(crate) fn to_prepare_mode(&self, renderer_level: RendererLevel) -> PrepareMode {
match renderer_level {
RendererLevel::D3D9 => PrepareMode::CPU,
RendererLevel::D3D11 => {
match self.transform {
PreparedRenderTransform::Perspective { .. } => PrepareMode::TransformCPUBinGPU,
PreparedRenderTransform::None => {
PrepareMode::GPU { transform: Transform2F::default() }
}
PreparedRenderTransform::Transform2D(transform) => {
PrepareMode::GPU { transform }
}
}
}
RendererLevel::D3D11 => match self.transform {
PreparedRenderTransform::Perspective { .. } => PrepareMode::TransformCPUBinGPU,
PreparedRenderTransform::None => PrepareMode::GPU {
transform: Transform2F::default(),
},
PreparedRenderTransform::Transform2D(transform) => PrepareMode::GPU { transform },
},
}
}
}

View File

@ -23,7 +23,7 @@ use pathfinder_content::render_target::RenderTargetId;
use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::rect::{RectF, RectI};
use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::vector::{Vector2F, Vector2I, vec2f, vec2i};
use pathfinder_geometry::vector::{vec2f, vec2i, Vector2F, Vector2I};
use pathfinder_gpu::TextureSamplingFlags;
use pathfinder_simd::default::{F32x2, F32x4};
use std::f32;
@ -118,7 +118,10 @@ impl Paint {
/// Creates a simple paint from a single base color.
#[inline]
pub fn from_color(color: ColorU) -> Paint {
Paint { base_color: color, overlay: None }
Paint {
base_color: color,
overlay: None,
}
}
/// Creates a paint from a gradient.
@ -167,12 +170,10 @@ impl Paint {
match self.overlay {
None => true,
Some(ref overlay) => {
match overlay.contents {
PaintContents::Gradient(ref gradient) => gradient.is_opaque(),
PaintContents::Pattern(ref pattern) => pattern.is_opaque(),
}
}
Some(ref overlay) => match overlay.contents {
PaintContents::Gradient(ref gradient) => gradient.is_opaque(),
PaintContents::Pattern(ref pattern) => pattern.is_opaque(),
},
}
}
@ -186,12 +187,10 @@ impl Paint {
match self.overlay {
None => true,
Some(ref overlay) => {
match overlay.contents {
PaintContents::Gradient(ref gradient) => gradient.is_fully_transparent(),
PaintContents::Pattern(_) => false,
}
}
Some(ref overlay) => match overlay.contents {
PaintContents::Gradient(ref gradient) => gradient.is_fully_transparent(),
PaintContents::Pattern(_) => false,
},
}
}
@ -251,12 +250,10 @@ impl Paint {
pub fn pattern(&self) -> Option<&Pattern> {
match self.overlay {
None => None,
Some(ref overlay) => {
match overlay.contents {
PaintContents::Pattern(ref pattern) => Some(pattern),
_ => None,
}
}
Some(ref overlay) => match overlay.contents {
PaintContents::Pattern(ref pattern) => Some(pattern),
_ => None,
},
}
}
@ -265,12 +262,10 @@ impl Paint {
pub fn pattern_mut(&mut self) -> Option<&mut Pattern> {
match self.overlay {
None => None,
Some(ref mut overlay) => {
match overlay.contents {
PaintContents::Pattern(ref mut pattern) => Some(pattern),
_ => None,
}
}
Some(ref mut overlay) => match overlay.contents {
PaintContents::Pattern(ref mut pattern) => Some(pattern),
_ => None,
},
}
}
@ -279,12 +274,10 @@ impl Paint {
pub fn gradient(&self) -> Option<&Gradient> {
match self.overlay {
None => None,
Some(ref overlay) => {
match overlay.contents {
PaintContents::Gradient(ref gradient) => Some(gradient),
_ => None,
}
}
Some(ref overlay) => match overlay.contents {
PaintContents::Gradient(ref gradient) => Some(gradient),
_ => None,
},
}
}
}
@ -392,13 +385,17 @@ impl Palette {
pub(crate) fn push_render_target(&mut self, render_target: RenderTarget) -> RenderTargetId {
let id = self.render_targets.len() as u32;
self.render_targets.push(render_target);
RenderTargetId { scene: self.scene_id.0, render_target: id }
RenderTargetId {
scene: self.scene_id.0,
render_target: id,
}
}
pub(crate) fn build_paint_info(&mut self,
texture_manager: &mut PaintTextureManager,
render_transform: Transform2F)
-> PaintInfo {
pub(crate) fn build_paint_info(
&mut self,
texture_manager: &mut PaintTextureManager,
render_transform: Transform2F,
) -> PaintInfo {
// Assign render target locations.
let mut transient_paint_locations = vec![];
let render_target_metadata =
@ -410,9 +407,11 @@ impl Palette {
gradient_tile_builder,
image_texel_info,
used_image_hashes,
} = self.assign_paint_locations(&render_target_metadata,
texture_manager,
&mut transient_paint_locations);
} = self.assign_paint_locations(
&render_target_metadata,
texture_manager,
&mut transient_paint_locations,
);
// Calculate texture transforms.
self.calculate_texture_transforms(&mut paint_metadata, texture_manager, render_transform);
@ -425,36 +424,45 @@ impl Palette {
self.allocate_textures(&mut render_commands, texture_manager);
// Create render commands.
self.create_render_commands(&mut render_commands,
render_target_metadata,
gradient_tile_builder,
image_texel_info);
self.create_render_commands(
&mut render_commands,
render_target_metadata,
gradient_tile_builder,
image_texel_info,
);
// Free transient locations and unused images, now that they're no longer needed.
self.free_transient_locations(texture_manager, transient_paint_locations);
self.free_unused_images(texture_manager, used_image_hashes);
PaintInfo { render_commands, paint_metadata }
PaintInfo {
render_commands,
paint_metadata,
}
}
fn assign_render_target_locations(&self,
texture_manager: &mut PaintTextureManager,
transient_paint_locations: &mut Vec<TextureLocation>)
-> Vec<RenderTargetMetadata> {
fn assign_render_target_locations(
&self,
texture_manager: &mut PaintTextureManager,
transient_paint_locations: &mut Vec<TextureLocation>,
) -> Vec<RenderTargetMetadata> {
let mut render_target_metadata = vec![];
for render_target in &self.render_targets {
let location = texture_manager.allocator.allocate_image(render_target.size());
let location = texture_manager
.allocator
.allocate_image(render_target.size());
render_target_metadata.push(RenderTargetMetadata { location });
transient_paint_locations.push(location);
}
render_target_metadata
}
fn assign_paint_locations(&self,
render_target_metadata: &[RenderTargetMetadata],
texture_manager: &mut PaintTextureManager,
transient_paint_locations: &mut Vec<TextureLocation>)
-> PaintLocationsInfo {
fn assign_paint_locations(
&self,
render_target_metadata: &[RenderTargetMetadata],
texture_manager: &mut PaintTextureManager,
transient_paint_locations: &mut Vec<TextureLocation>,
) -> PaintLocationsInfo {
let mut paint_metadata = vec![];
let mut gradient_tile_builder = GradientTileBuilder::new();
let mut image_texel_info = vec![];
@ -476,10 +484,11 @@ impl Palette {
// FIXME(pcwalton): The gradient size might not be big enough. Detect
// this.
let location =
gradient_tile_builder.allocate(allocator,
transient_paint_locations,
gradient);
let location = gradient_tile_builder.allocate(
allocator,
transient_paint_locations,
gradient,
);
Some(PaintColorTextureMetadata {
location,
page_scale: allocator.page_scale(location.page),
@ -496,12 +505,17 @@ impl Palette {
})
}
PaintContents::Pattern(ref pattern) => {
let border = vec2i(if pattern.repeat_x() { 0 } else { 1 },
if pattern.repeat_y() { 0 } else { 1 });
let border = vec2i(
if pattern.repeat_x() { 0 } else { 1 },
if pattern.repeat_y() { 0 } else { 1 },
);
let location;
match *pattern.source() {
PatternSource::RenderTarget { id: render_target_id, .. } => {
PatternSource::RenderTarget {
id: render_target_id,
..
} => {
let index = render_target_id.render_target as usize;
location = render_target_metadata[index].location;
}
@ -519,9 +533,11 @@ impl Palette {
let allocation_mode = AllocationMode::OwnPage;
location = allocator.allocate(
image.size() + border * 2,
allocation_mode);
texture_manager.cached_images.insert(image_hash,
location);
allocation_mode,
);
texture_manager
.cached_images
.insert(image_hash, location);
}
}
image_texel_info.push(ImageTexelInfo {
@ -542,8 +558,10 @@ impl Palette {
sampling_flags.insert(TextureSamplingFlags::REPEAT_V);
}
if !pattern.smoothing_enabled() {
sampling_flags.insert(TextureSamplingFlags::NEAREST_MIN |
TextureSamplingFlags::NEAREST_MAG);
sampling_flags.insert(
TextureSamplingFlags::NEAREST_MIN
| TextureSamplingFlags::NEAREST_MAG,
);
}
let filter = match pattern.filter() {
@ -582,24 +600,28 @@ impl Palette {
}
}
fn calculate_texture_transforms(&self,
paint_metadata: &mut [PaintMetadata],
texture_manager: &mut PaintTextureManager,
render_transform: Transform2F) {
fn calculate_texture_transforms(
&self,
paint_metadata: &mut [PaintMetadata],
texture_manager: &mut PaintTextureManager,
render_transform: Transform2F,
) {
for (paint, metadata) in self.paints.iter().zip(paint_metadata.iter_mut()) {
let color_texture_metadata = match metadata.color_texture_metadata {
None => continue,
Some(ref mut color_texture_metadata) => color_texture_metadata,
};
let texture_scale = texture_manager.allocator
.page_scale(color_texture_metadata.location.page);
let texture_scale = texture_manager
.allocator
.page_scale(color_texture_metadata.location.page);
let texture_rect = color_texture_metadata.location.rect;
color_texture_metadata.transform = match paint.overlay
.as_ref()
.expect("Why do we have color texture \
metadata but no overlay?")
.contents {
color_texture_metadata.transform = match paint
.overlay
.as_ref()
.expect("Why do we have color texture metadata but no overlay?")
.contents
{
PaintContents::Gradient(Gradient {
geometry: GradientGeometry::Linear(gradient_line),
..
@ -620,16 +642,16 @@ impl Palette {
PatternSource::Image(_) => {
let texture_origin_uv =
rect_to_uv(texture_rect, texture_scale).origin();
Transform2F::from_scale(texture_scale).translate(texture_origin_uv) *
pattern.transform().inverse()
Transform2F::from_scale(texture_scale).translate(texture_origin_uv)
* pattern.transform().inverse()
}
PatternSource::RenderTarget { .. } => {
// FIXME(pcwalton): Only do this in GL, not Metal!
let texture_origin_uv =
rect_to_uv(texture_rect, texture_scale).lower_left();
Transform2F::from_translation(texture_origin_uv) *
Transform2F::from_scale(texture_scale * vec2f(1.0, -1.0)) *
pattern.transform().inverse()
Transform2F::from_translation(texture_origin_uv)
* Transform2F::from_scale(texture_scale * vec2f(1.0, -1.0))
* pattern.transform().inverse()
}
}
}
@ -638,10 +660,13 @@ impl Palette {
}
}
fn create_texture_metadata(&self, paint_metadata: &[PaintMetadata])
-> Vec<TextureMetadataEntry> {
paint_metadata.iter().map(|paint_metadata| {
TextureMetadataEntry {
fn create_texture_metadata(
&self,
paint_metadata: &[PaintMetadata],
) -> Vec<TextureMetadataEntry> {
paint_metadata
.iter()
.map(|paint_metadata| TextureMetadataEntry {
color_0_transform: match paint_metadata.color_texture_metadata {
None => Transform2F::default(),
Some(ref color_texture_metadata) => color_texture_metadata.transform,
@ -654,31 +679,41 @@ impl Palette {
base_color: paint_metadata.base_color,
filter: paint_metadata.filter(),
blend_mode: paint_metadata.blend_mode,
}
}).collect()
})
.collect()
}
fn allocate_textures(&self,
render_commands: &mut Vec<RenderCommand>,
texture_manager: &mut PaintTextureManager) {
fn allocate_textures(
&self,
render_commands: &mut Vec<RenderCommand>,
texture_manager: &mut PaintTextureManager,
) {
for page_id in texture_manager.allocator.page_ids() {
let page_size = texture_manager.allocator.page_size(page_id);
let descriptor = TexturePageDescriptor { size: page_size };
if texture_manager.allocator.page_is_new(page_id) {
render_commands.push(RenderCommand::AllocateTexturePage { page_id, descriptor });
render_commands.push(RenderCommand::AllocateTexturePage {
page_id,
descriptor,
});
}
}
texture_manager.allocator.mark_all_pages_as_allocated();
}
fn create_render_commands(&self,
render_commands: &mut Vec<RenderCommand>,
render_target_metadata: Vec<RenderTargetMetadata>,
gradient_tile_builder: GradientTileBuilder,
image_texel_info: Vec<ImageTexelInfo>) {
fn create_render_commands(
&self,
render_commands: &mut Vec<RenderCommand>,
render_target_metadata: Vec<RenderTargetMetadata>,
gradient_tile_builder: GradientTileBuilder,
image_texel_info: Vec<ImageTexelInfo>,
) {
for (index, metadata) in render_target_metadata.iter().enumerate() {
let id = RenderTargetId { scene: self.scene_id.0, render_target: index as u32 };
let id = RenderTargetId {
scene: self.scene_id.0,
render_target: index as u32,
};
render_commands.push(RenderCommand::DeclareRenderTarget {
id,
location: metadata.location,
@ -693,18 +728,22 @@ impl Palette {
}
}
fn free_transient_locations(&self,
texture_manager: &mut PaintTextureManager,
transient_paint_locations: Vec<TextureLocation>) {
fn free_transient_locations(
&self,
texture_manager: &mut PaintTextureManager,
transient_paint_locations: Vec<TextureLocation>,
) {
for location in transient_paint_locations {
texture_manager.allocator.free(location);
}
}
// Frees images that are cached but not used this frame.
fn free_unused_images(&self,
texture_manager: &mut PaintTextureManager,
used_image_hashes: HashSet<ImageHash>) {
fn free_unused_images(
&self,
texture_manager: &mut PaintTextureManager,
used_image_hashes: HashSet<ImageHash>,
) {
let cached_images = &mut texture_manager.cached_images;
let allocator = &mut texture_manager.allocator;
cached_images.retain(|image_hash, location| {
@ -719,9 +758,9 @@ impl Palette {
pub(crate) fn append_palette(&mut self, palette: Palette) -> MergedPaletteInfo {
// Merge render targets.
let mut render_target_mapping = HashMap::new();
for (old_render_target_index, render_target) in palette.render_targets
.into_iter()
.enumerate() {
for (old_render_target_index, render_target) in
palette.render_targets.into_iter().enumerate()
{
let old_render_target_id = RenderTargetId {
scene: palette.scene_id.0,
render_target: old_render_target_index as u32,
@ -736,31 +775,33 @@ impl Palette {
let old_paint_id = PaintId(old_paint_index as u16);
let new_paint_id = match *old_paint.overlay() {
None => self.push_paint(old_paint),
Some(ref overlay) => {
match *overlay.contents() {
PaintContents::Pattern(ref pattern) => {
match pattern.source() {
PatternSource::RenderTarget { id: old_render_target_id, size } => {
let mut new_pattern =
Pattern::from_render_target(*old_render_target_id, *size);
new_pattern.set_filter(pattern.filter());
new_pattern.apply_transform(pattern.transform());
new_pattern.set_repeat_x(pattern.repeat_x());
new_pattern.set_repeat_y(pattern.repeat_y());
new_pattern.set_smoothing_enabled(pattern.smoothing_enabled());
self.push_paint(&Paint::from_pattern(new_pattern))
}
_ => self.push_paint(old_paint),
}
Some(ref overlay) => match *overlay.contents() {
PaintContents::Pattern(ref pattern) => match pattern.source() {
PatternSource::RenderTarget {
id: old_render_target_id,
size,
} => {
let mut new_pattern =
Pattern::from_render_target(*old_render_target_id, *size);
new_pattern.set_filter(pattern.filter());
new_pattern.apply_transform(pattern.transform());
new_pattern.set_repeat_x(pattern.repeat_x());
new_pattern.set_repeat_y(pattern.repeat_y());
new_pattern.set_smoothing_enabled(pattern.smoothing_enabled());
self.push_paint(&Paint::from_pattern(new_pattern))
}
_ => self.push_paint(old_paint),
}
}
},
_ => self.push_paint(old_paint),
},
};
paint_mapping.insert(old_paint_id, new_paint_id);
}
MergedPaletteInfo { render_target_mapping, paint_mapping }
MergedPaletteInfo {
render_target_mapping,
paint_mapping,
}
}
}
@ -782,25 +823,27 @@ impl PaintMetadata {
pub(crate) fn filter(&self) -> Filter {
match self.color_texture_metadata {
None => Filter::None,
Some(ref color_metadata) => {
match color_metadata.filter {
PaintFilter::None => Filter::None,
PaintFilter::RadialGradient { line, radii } => {
let uv_rect = rect_to_uv(color_metadata.location.rect,
color_metadata.page_scale).contract(
vec2f(0.0, color_metadata.page_scale.y() * 0.5));
Filter::RadialGradient { line, radii, uv_origin: uv_rect.origin() }
}
PaintFilter::PatternFilter(pattern_filter) => {
Filter::PatternFilter(pattern_filter)
Some(ref color_metadata) => match color_metadata.filter {
PaintFilter::None => Filter::None,
PaintFilter::RadialGradient { line, radii } => {
let uv_rect =
rect_to_uv(color_metadata.location.rect, color_metadata.page_scale)
.contract(vec2f(0.0, color_metadata.page_scale.y() * 0.5));
Filter::RadialGradient {
line,
radii,
uv_origin: uv_rect.origin(),
}
}
}
PaintFilter::PatternFilter(pattern_filter) => Filter::PatternFilter(pattern_filter),
},
}
}
pub(crate) fn tile_batch_texture(&self) -> Option<TileBatchTexture> {
self.color_texture_metadata.as_ref().map(PaintColorTextureMetadata::as_tile_batch_texture)
self.color_texture_metadata
.as_ref()
.map(PaintColorTextureMetadata::as_tile_batch_texture)
}
}
@ -825,13 +868,13 @@ impl GradientTileBuilder {
GradientTileBuilder { tiles: vec![] }
}
fn allocate(&mut self,
allocator: &mut TextureAllocator,
transient_paint_locations: &mut Vec<TextureLocation>,
gradient: &Gradient)
-> TextureLocation {
if self.tiles.is_empty() ||
self.tiles.last().unwrap().next_index == GRADIENT_TILE_LENGTH {
fn allocate(
&mut self,
allocator: &mut TextureAllocator,
transient_paint_locations: &mut Vec<TextureLocation>,
gradient: &Gradient,
) -> TextureLocation {
if self.tiles.is_empty() || self.tiles.last().unwrap().next_index == GRADIENT_TILE_LENGTH {
let size = Vector2I::splat(GRADIENT_TILE_LENGTH as i32);
let area = size.x() as usize * size.y() as usize;
let page_location = allocator.allocate(size, AllocationMode::OwnPage);
@ -846,8 +889,10 @@ impl GradientTileBuilder {
let data = self.tiles.last_mut().unwrap();
let location = TextureLocation {
page: data.page,
rect: RectI::new(vec2i(0, data.next_index as i32),
vec2i(GRADIENT_TILE_LENGTH as i32, 1)),
rect: RectI::new(
vec2i(0, data.next_index as i32),
vec2i(GRADIENT_TILE_LENGTH as i32, 1),
),
};
data.next_index += 1;

View File

@ -24,7 +24,7 @@ use pathfinder_content::outline::Outline;
use pathfinder_content::render_target::RenderTargetId;
use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::vector::{Vector2I, vec2f};
use pathfinder_geometry::vector::{vec2f, Vector2I};
use pathfinder_gpu::Device;
use std::mem;
use std::ops::Range;
@ -90,7 +90,9 @@ impl Scene {
let end_path_id = DrawPathId(draw_path_id.0 + 1);
match self.display_list.last_mut() {
Some(DisplayItem::DrawPaths(ref mut range)) => range.end = end_path_id,
_ => self.display_list.push(DisplayItem::DrawPaths(draw_path_id..end_path_id)),
_ => self
.display_list
.push(DisplayItem::DrawPaths(draw_path_id..end_path_id)),
}
self.epoch.next();
@ -111,7 +113,8 @@ impl Scene {
/// top of the stack.
pub fn push_render_target(&mut self, render_target: RenderTarget) -> RenderTargetId {
let render_target_id = self.palette.push_render_target(render_target);
self.display_list.push(DisplayItem::PushRenderTarget(render_target_id));
self.display_list
.push(DisplayItem::PushRenderTarget(render_target_id));
self.epoch.next();
render_target_id
}
@ -161,7 +164,8 @@ impl Scene {
match display_item {
DisplayItem::PushRenderTarget(old_render_target_id) => {
let new_render_target_id = render_target_mapping[&old_render_target_id];
self.display_list.push(DisplayItem::PushRenderTarget(new_render_target_id));
self.display_list
.push(DisplayItem::PushRenderTarget(new_render_target_id));
}
DisplayItem::PopRenderTarget => {
self.display_list.push(DisplayItem::PopRenderTarget);
@ -180,11 +184,13 @@ impl Scene {
}
#[inline]
pub(crate) fn build_paint_info(&mut self,
texture_manager: &mut PaintTextureManager,
render_transform: Transform2F)
-> PaintInfo {
self.palette.build_paint_info(texture_manager, render_transform)
pub(crate) fn build_paint_info(
&mut self,
texture_manager: &mut PaintTextureManager,
render_transform: Transform2F,
) -> PaintInfo {
self.palette
.build_paint_info(texture_manager, render_transform)
}
/// Defines a new paint, which specifies how paths are to be filled or stroked. Returns a paint
@ -227,10 +233,11 @@ impl Scene {
}
#[allow(deprecated)]
pub(crate) fn apply_render_options(&self,
original_outline: &Outline,
options: &PreparedBuildOptions)
-> Outline {
pub(crate) fn apply_render_options(
&self,
original_outline: &Outline,
options: &PreparedBuildOptions,
) -> Outline {
let mut outline;
match options.transform {
PreparedRenderTransform::Perspective {
@ -289,11 +296,14 @@ impl Scene {
/// `SequentialExecutor` to prepare commands on a single thread or `RayonExecutor` to prepare
/// commands in parallel across multiple threads.
#[inline]
pub fn build<'a, 'b, E>(&mut self,
options: BuildOptions,
sink: &'b mut SceneSink<'a>,
executor: &E)
where E: Executor {
pub fn build<'a, 'b, E>(
&mut self,
options: BuildOptions,
sink: &'b mut SceneSink<'a>,
executor: &E,
) where
E: Executor,
{
let prepared_options = options.prepare(self.bounds);
SceneBuilder::new(self, &prepared_options, sink).build(executor)
}
@ -334,7 +344,10 @@ impl Scene {
/// Returns the paint with the given ID.
#[inline]
pub fn get_paint(&self, paint_id: PaintId) -> &Paint {
self.palette.paints.get(paint_id.0 as usize).expect("No paint with that ID!")
self.palette
.paints
.get(paint_id.0 as usize)
.expect("No paint with that ID!")
}
/// Returns the globally-unique ID of the scene.
@ -349,12 +362,16 @@ impl Scene {
}
/// A convenience method to build a scene and accumulate commands into a vector.
pub fn build_into_vector<D, E>(&mut self,
renderer: &mut Renderer<D>,
build_options: BuildOptions,
executor: E)
-> Vec<RenderCommand>
where D: Device, E: Executor {
pub fn build_into_vector<D, E>(
&mut self,
renderer: &mut Renderer<D>,
build_options: BuildOptions,
executor: E,
) -> Vec<RenderCommand>
where
D: Device,
E: Executor,
{
let commands = Arc::new(Mutex::new(vec![]));
let commands_for_listener = commands.clone();
let listener = RenderCommandListener::new(Box::new(move |command| {
@ -368,11 +385,15 @@ impl Scene {
/// A convenience method to build a scene and send the resulting commands to the given
/// renderer.
pub fn build_and_render<D, E>(&mut self,
renderer: &mut Renderer<D>,
build_options: BuildOptions,
executor: E)
where D: Device, E: Executor + Send {
pub fn build_and_render<D, E>(
&mut self,
renderer: &mut Renderer<D>,
build_options: BuildOptions,
executor: E,
) where
D: Device,
E: Executor + Send,
{
std::thread::scope(move |scope| {
let (tx, rx) = flume::bounded(MAX_MESSAGES_IN_FLIGHT);
@ -380,9 +401,8 @@ impl Scene {
// TODO: avoid this auxiliary thread
scope.spawn(move || {
let listener = RenderCommandListener::new(Box::new(move |command| {
tx.send(command).unwrap()
}));
let listener =
RenderCommandListener::new(Box::new(move |command| tx.send(command).unwrap()));
let mut sink = SceneSink::new(listener, level);
self.build(build_options, &mut sink, &executor);
});
@ -430,9 +450,15 @@ impl SceneEpoch {
#[inline]
fn successor(&self) -> SceneEpoch {
if self.lo == u64::MAX {
SceneEpoch { hi: self.hi + 1, lo: 0 }
SceneEpoch {
hi: self.hi + 1,
lo: 0,
}
} else {
SceneEpoch { hi: self.hi, lo: self.lo + 1 }
SceneEpoch {
hi: self.hi,
lo: self.lo + 1,
}
}
}
@ -445,8 +471,10 @@ impl SceneEpoch {
impl<'a> SceneSink<'a> {
/// Creates a new scene sink from the given render command listener and level.
#[inline]
pub fn new(listener: RenderCommandListener<'a>, renderer_level: RendererLevel)
-> SceneSink<'a> {
pub fn new(
listener: RenderCommandListener<'a>,
renderer_level: RendererLevel,
) -> SceneSink<'a> {
SceneSink {
listener,
renderer_level,
@ -607,7 +635,12 @@ impl ClipPath {
/// has no name.
#[inline]
pub fn new(outline: Outline) -> ClipPath {
ClipPath { outline, clip_path: None, fill_rule: FillRule::Winding, name: String::new() }
ClipPath {
outline,
clip_path: None,
fill_rule: FillRule::Winding,
name: String::new(),
}
}
/// Returns the outline of this clip path, which defines its vector commands.

View File

@ -9,18 +9,26 @@
// except according to those terms.
use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::vector::{Vector2I, vec2i};
use pathfinder_geometry::vector::{vec2i, Vector2I};
#[derive(Clone, Debug)]
pub struct DenseTileMap<T> where T: Clone + Copy {
pub struct DenseTileMap<T>
where
T: Clone + Copy,
{
pub data: Vec<T>,
pub rect: RectI,
}
impl<T> DenseTileMap<T> where T: Clone + Copy {
impl<T> DenseTileMap<T>
where
T: Clone + Copy,
{
#[inline]
pub fn from_builder<F>(mut build: F, rect: RectI) -> DenseTileMap<T>
where F: FnMut(Vector2I) -> T {
where
F: FnMut(Vector2I) -> T,
{
let mut data = Vec::with_capacity(rect.size().x() as usize * rect.size().y() as usize);
for y in rect.min_y()..rect.max_y() {
for x in rect.min_x()..rect.max_x() {
@ -32,7 +40,8 @@ impl<T> DenseTileMap<T> where T: Clone + Copy {
#[inline]
pub fn get(&self, coords: Vector2I) -> Option<&T> {
self.coords_to_index(coords).and_then(|index| self.data.get(index))
self.coords_to_index(coords)
.and_then(|index| self.data.get(index))
}
#[inline]

View File

@ -16,14 +16,14 @@ use crate::gpu::options::RendererLevel;
use crate::gpu_data::AlphaTileId;
use crate::options::PrepareMode;
use crate::scene::{ClipPathId, PathId};
use crate::tiles::{TILE_HEIGHT, TILE_WIDTH, TilingPathInfo};
use crate::tiles::{TilingPathInfo, TILE_HEIGHT, TILE_WIDTH};
use pathfinder_content::clip;
use pathfinder_content::fill::FillRule;
use pathfinder_content::outline::{ContourIterFlags, Outline};
use pathfinder_content::segment::Segment;
use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::vector::{Vector2F, Vector2I, vec2f, vec2i};
use pathfinder_geometry::vector::{vec2f, vec2i, Vector2F, Vector2I};
use pathfinder_simd::default::{F32x2, U32x2};
use std::f32::NEG_INFINITY;
@ -37,32 +37,43 @@ pub(crate) struct Tiler<'a, 'b, 'c, 'd> {
}
impl<'a, 'b, 'c, 'd> Tiler<'a, 'b, 'c, 'd> {
pub(crate) fn new(scene_builder: &'a SceneBuilder<'b, 'a, 'c, 'd>,
path_id: PathId,
outline: &'a Outline,
fill_rule: FillRule,
view_box: RectF,
prepare_mode: &PrepareMode,
clip_path_id: Option<ClipPathId>,
built_clip_paths: &'a [BuiltPath],
path_info: TilingPathInfo)
-> Tiler<'a, 'b, 'c, 'd> {
let bounds = outline.bounds().intersection(view_box).unwrap_or(RectF::default());
pub(crate) fn new(
scene_builder: &'a SceneBuilder<'b, 'a, 'c, 'd>,
path_id: PathId,
outline: &'a Outline,
fill_rule: FillRule,
view_box: RectF,
prepare_mode: &PrepareMode,
clip_path_id: Option<ClipPathId>,
built_clip_paths: &'a [BuiltPath],
path_info: TilingPathInfo,
) -> Tiler<'a, 'b, 'c, 'd> {
let bounds = outline
.bounds()
.intersection(view_box)
.unwrap_or(RectF::default());
let clip_path = match clip_path_id {
Some(clip_path_id) => Some(&built_clip_paths[clip_path_id.0 as usize]),
_ => None,
};
let object_builder = ObjectBuilder::new(path_id,
bounds,
view_box,
fill_rule,
prepare_mode,
clip_path_id,
&path_info);
let object_builder = ObjectBuilder::new(
path_id,
bounds,
view_box,
fill_rule,
prepare_mode,
clip_path_id,
&path_info,
);
Tiler { scene_builder, object_builder, outline, clip_path }
Tiler {
scene_builder,
object_builder,
outline,
clip_path,
}
}
pub(crate) fn generate_tiles(&mut self) {
@ -93,9 +104,11 @@ impl<'a, 'b, 'c, 'd> Tiler<'a, 'b, 'c, 'd> {
fn prepare_tiles(&mut self) {
// Don't do this here if the GPU will do it.
let (backdrops, tiles, clips) = match self.object_builder.built_path.data {
BuiltPathData::CPU(ref mut tiled_data) => {
(&mut tiled_data.backdrops, &mut tiled_data.tiles, &mut tiled_data.clip_tiles)
}
BuiltPathData::CPU(ref mut tiled_data) => (
&mut tiled_data.backdrops,
&mut tiled_data.tiles,
&mut tiled_data.clip_tiles,
),
BuiltPathData::TransformCPUBinGPU(_) | BuiltPathData::GPU => {
panic!("We shouldn't be preparing tiles on CPU!")
}
@ -118,30 +131,34 @@ impl<'a, 'b, 'c, 'd> Tiler<'a, 'b, 'c, 'd> {
};
match clip_tiles.get(tile_coords) {
Some(clip_tile) => {
if clip_tile.alpha_tile_id != AlphaTileId(!0) &&
draw_alpha_tile_id != AlphaTileId(!0) {
if clip_tile.alpha_tile_id != AlphaTileId(!0)
&& draw_alpha_tile_id != AlphaTileId(!0)
{
// Hard case: We have an alpha tile and a clip tile with masks. Add a
// job to combine the two masks. Because the mask combining step
// applies the backdrops, zero out the backdrop in the draw tile itself
// so that we don't double-count it.
let clip = clips.as_mut()
.expect("Where are the clips?")
.get_mut(tile_coords)
.unwrap();
let clip = clips
.as_mut()
.expect("Where are the clips?")
.get_mut(tile_coords)
.unwrap();
clip.dest_tile_id = draw_tile.alpha_tile_id;
clip.dest_backdrop = draw_tile_backdrop as i32;
clip.src_tile_id = clip_tile.alpha_tile_id;
clip.src_backdrop = clip_tile.backdrop as i32;
draw_tile_backdrop = 0;
} else if clip_tile.alpha_tile_id != AlphaTileId(!0) &&
draw_alpha_tile_id == AlphaTileId(!0) &&
draw_tile_backdrop != 0 {
} else if clip_tile.alpha_tile_id != AlphaTileId(!0)
&& draw_alpha_tile_id == AlphaTileId(!0)
&& draw_tile_backdrop != 0
{
// This is a solid draw tile, but there's a clip applied. Replace it
// with an alpha tile pointing directly to the clip mask.
draw_alpha_tile_id = clip_tile.alpha_tile_id;
draw_tile_backdrop = clip_tile.backdrop;
} else if clip_tile.alpha_tile_id == AlphaTileId(!0) &&
clip_tile.backdrop == 0 {
} else if clip_tile.alpha_tile_id == AlphaTileId(!0)
&& clip_tile.backdrop == 0
{
// This is a blank clip tile. Cull the draw tile entirely.
draw_alpha_tile_id = AlphaTileId(!0);
draw_tile_backdrop = 0;
@ -163,17 +180,20 @@ impl<'a, 'b, 'c, 'd> Tiler<'a, 'b, 'c, 'd> {
}
}
fn process_segment(segment: &Segment,
scene_builder: &SceneBuilder,
object_builder: &mut ObjectBuilder) {
fn process_segment(
segment: &Segment,
scene_builder: &SceneBuilder,
object_builder: &mut ObjectBuilder,
) {
// TODO(pcwalton): Stop degree elevating.
if segment.is_quadratic() {
let cubic = segment.to_cubic();
return process_segment(&cubic, scene_builder, object_builder);
}
if segment.is_line() ||
(segment.is_cubic() && segment.as_cubic_segment().is_flat(FLATTENING_TOLERANCE)) {
if segment.is_line()
|| (segment.is_cubic() && segment.as_cubic_segment().is_flat(FLATTENING_TOLERANCE))
{
return process_line_segment(segment.baseline, scene_builder, object_builder);
}
@ -188,12 +208,16 @@ fn process_segment(segment: &Segment,
//
// The algorithm to step through tiles is Amanatides and Woo, "A Fast Voxel Traversal Algorithm for
// Ray Tracing" 1987: http://www.cse.yorku.ca/~amana/research/grid.pdf
fn process_line_segment(line_segment: LineSegment2F,
scene_builder: &SceneBuilder,
object_builder: &mut ObjectBuilder) {
fn process_line_segment(
line_segment: LineSegment2F,
scene_builder: &SceneBuilder,
object_builder: &mut ObjectBuilder,
) {
let view_box = scene_builder.scene.view_box();
let clip_box = RectF::from_points(vec2f(view_box.min_x(), NEG_INFINITY),
view_box.lower_right());
let clip_box = RectF::from_points(
vec2f(view_box.min_x(), NEG_INFINITY),
view_box.lower_right(),
);
let line_segment = match clip::clip_line_segment_to_rect(line_segment, clip_box) {
None => return,
Some(line_segment) => line_segment,
@ -202,8 +226,9 @@ fn process_line_segment(line_segment: LineSegment2F,
let tile_size = vec2f(TILE_WIDTH as f32, TILE_HEIGHT as f32);
let tile_size_recip = Vector2F::splat(1.0) / tile_size;
let tile_line_segment =
(line_segment.0 * tile_size_recip.0.concat_xy_xy(tile_size_recip.0)).floor().to_i32x4();
let tile_line_segment = (line_segment.0 * tile_size_recip.0.concat_xy_xy(tile_size_recip.0))
.floor()
.to_i32x4();
let from_tile_coords = Vector2I(tile_line_segment.xy());
let to_tile_coords = Vector2I(tile_line_segment.zw());
@ -216,8 +241,9 @@ fn process_line_segment(line_segment: LineSegment2F,
// Compute `first_tile_crossing = (from_tile_coords + vec2i(vector.x >= 0 ? 1 : 0,
// vector.y >= 0 ? 1 : 0)) * tile_size`.
let first_tile_crossing = (from_tile_coords +
Vector2I((!vector_is_negative & U32x2::splat(1)).to_i32x2())).to_f32() * tile_size;
let first_tile_crossing =
(from_tile_coords + Vector2I((!vector_is_negative & U32x2::splat(1)).to_i32x2())).to_f32()
* tile_size;
let mut t_max = (first_tile_crossing - line_segment.from()) / vector;
let t_delta = (tile_size / vector).abs();
@ -244,11 +270,19 @@ fn process_line_segment(line_segment: LineSegment2F,
//
// In that case we just need to step in the positive direction to move to the lower
// right tile.
if step.x() > 0 { StepDirection::X } else { StepDirection::Y }
if step.x() > 0 {
StepDirection::X
} else {
StepDirection::Y
}
};
let next_t =
(if next_step_direction == StepDirection::X { t_max.x() } else { t_max.y() }).min(1.0);
let next_t = (if next_step_direction == StepDirection::X {
t_max.x()
} else {
t_max.y()
})
.min(1.0);
// If we've reached the end tile, don't step at all.
let next_step_direction = if tile_coords == to_tile_coords {
@ -264,13 +298,15 @@ fn process_line_segment(line_segment: LineSegment2F,
// Add extra fills if necessary.
if step.y() < 0 && next_step_direction == Some(StepDirection::Y) {
// Leaves through top boundary.
let auxiliary_segment = LineSegment2F::new(clipped_line_segment.to(),
tile_coords.to_f32() * tile_size);
let auxiliary_segment =
LineSegment2F::new(clipped_line_segment.to(), tile_coords.to_f32() * tile_size);
object_builder.add_fill(scene_builder, auxiliary_segment, tile_coords);
} else if step.y() > 0 && last_step_direction == Some(StepDirection::Y) {
// Enters through top boundary.
let auxiliary_segment = LineSegment2F::new(tile_coords.to_f32() * tile_size,
clipped_line_segment.from());
let auxiliary_segment = LineSegment2F::new(
tile_coords.to_f32() * tile_size,
clipped_line_segment.from(),
);
object_builder.add_fill(scene_builder, auxiliary_segment, tile_coords);
}

View File

@ -8,8 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::gpu_data::{TileObjectPrimitive, TILE_CTRL_MASK_WINDING};
use crate::gpu_data::{TILE_CTRL_MASK_0_SHIFT, TILE_CTRL_MASK_EVEN_ODD};
use crate::gpu_data::{TILE_CTRL_MASK_WINDING, TileObjectPrimitive};
use crate::paint::PaintId;
use pathfinder_content::effects::BlendMode;
use pathfinder_content::fill::FillRule;
@ -62,10 +62,14 @@ impl TilingPathInfo {
}
pub fn round_rect_out_to_tile_bounds(rect: RectF) -> RectI {
(rect * vec2f(1.0 / TILE_WIDTH as f32, 1.0 / TILE_HEIGHT as f32)).round_out().to_i32()
(rect * vec2f(1.0 / TILE_WIDTH as f32, 1.0 / TILE_HEIGHT as f32))
.round_out()
.to_i32()
}
impl TileObjectPrimitive {
#[inline]
pub fn is_solid(&self) -> bool { !self.alpha_tile_id.is_valid() }
pub fn is_solid(&self) -> bool {
!self.alpha_tile_id.is_valid()
}
}

View File

@ -19,9 +19,16 @@ fn main() {
let mut dest = File::create(dest_path).unwrap();
let cwd = env::current_dir().unwrap();
writeln!(&mut dest, "// Generated by `pathfinder/resources/build.rs`. Do not edit!\n").unwrap();
writeln!(&mut dest,
"pub static RESOURCES: &'static [(&'static str, &'static [u8])] = &[").unwrap();
writeln!(
&mut dest,
"// Generated by `pathfinder/resources/build.rs`. Do not edit!\n"
)
.unwrap();
writeln!(
&mut dest,
"pub static RESOURCES: &'static [(&'static str, &'static [u8])] = &["
)
.unwrap();
let src = BufReader::new(File::open("MANIFEST").unwrap());
for line in src.lines() {
@ -36,10 +43,12 @@ fn main() {
full_path.push(line);
let escaped_full_path = full_path.to_str().unwrap().escape_default().to_string();
writeln!(&mut dest,
" (\"{}\", include_bytes!(\"{}\")),",
escaped_path,
escaped_full_path).unwrap();
writeln!(
&mut dest,
" (\"{}\", include_bytes!(\"{}\")),",
escaped_path, escaped_full_path
)
.unwrap();
println!("cargo:rerun-if-changed={}", line);
}

View File

@ -26,7 +26,11 @@ impl EmbeddedResourceLoader {
impl ResourceLoader for EmbeddedResourceLoader {
fn slurp(&self, virtual_path: &str) -> Result<Vec<u8>, IOError> {
match RESOURCES.iter().filter(|&(path, _)| *path == virtual_path).next() {
match RESOURCES
.iter()
.filter(|&(path, _)| *path == virtual_path)
.next()
{
Some((_, data)) => Ok(data.to_vec()),
None => Err(IOError::from(ErrorKind::NotFound)),
}

View File

@ -11,10 +11,10 @@
//! An abstraction for reading resources.
//!
//! This accomplishes two purposes over just using the filesystem to locate shaders and so forth:
//!
//!
//! 1. Downstream users of Pathfinder shouldn't be burdened with having to install the resources
//! alongside their binary.
//!
//!
//! 2. There may not be a traditional filesystem available, as for example is the case on Android.
use std::io::Error as IOError;

View File

@ -10,9 +10,9 @@
use std::arch::aarch64::{self, float32x2_t, float32x4_t, int32x2_t, int32x4_t};
use std::arch::aarch64::{uint32x2_t, uint32x4_t};
use std::intrinsics::simd::*;
use std::f32;
use std::fmt::{self, Debug, Formatter};
use std::intrinsics::simd::*;
use std::mem;
use std::ops::{Add, BitAnd, BitOr, Div, Index, IndexMut, Mul, Not, Shr, Sub};
@ -201,7 +201,6 @@ impl IndexMut<usize> for F32x2 {
}
}
impl Add<F32x2> for F32x2 {
type Output = F32x2;
#[inline]
@ -832,7 +831,6 @@ impl BitOr<U32x2> for U32x2 {
}
}
// Four 32-bit unsigned integers
#[derive(Clone, Copy)]

View File

@ -49,7 +49,10 @@ impl F32x2 {
#[inline]
pub fn approx_eq(self, other: F32x2, epsilon: f32) -> bool {
(self - other).abs().packed_gt(F32x2::splat(epsilon)).all_false()
(self - other)
.abs()
.packed_gt(F32x2::splat(epsilon))
.all_false()
}
}
@ -140,7 +143,10 @@ impl F32x4 {
#[inline]
pub fn approx_eq(self, other: F32x4, epsilon: f32) -> bool {
(self - other).abs().packed_gt(F32x4::splat(epsilon)).all_false()
(self - other)
.abs()
.packed_gt(F32x4::splat(epsilon))
.all_false()
}
}

View File

@ -10,7 +10,7 @@
use std::f32;
use std::fmt::{self, Debug, Formatter};
use std::ops::{Add, BitAnd, BitOr, Div, Index, IndexMut, Mul, Shr, Sub, Not};
use std::ops::{Add, BitAnd, BitOr, Div, Index, IndexMut, Mul, Not, Shr, Sub};
mod swizzle_f32x4;
mod swizzle_i32x4;
@ -485,18 +485,12 @@ impl I32x2 {
#[inline]
pub fn min(self, other: I32x2) -> I32x2 {
I32x2([
self[0].min(other[0]),
self[1].min(other[1]),
])
I32x2([self[0].min(other[0]), self[1].min(other[1])])
}
#[inline]
pub fn max(self, other: I32x2) -> I32x2 {
I32x2([
self[0].max(other[0]),
self[1].max(other[1]),
])
I32x2([self[0].max(other[0]), self[1].max(other[1])])
}
// Packed comparisons
@ -531,7 +525,7 @@ impl I32x2 {
if self[0] < other[0] { !0 } else { 0 },
if self[1] < other[1] { !0 } else { 0 },
])
}
}
// Conversions
@ -715,7 +709,12 @@ impl I32x4 {
/// FIXME(pcwalton): Should they? This will assert on overflow in debug.
#[inline]
pub fn to_u32x4(self) -> U32x4 {
U32x4([self[0] as u32, self[1] as u32, self[2] as u32, self[3] as u32])
U32x4([
self[0] as u32,
self[1] as u32,
self[2] as u32,
self[3] as u32,
])
}
}
@ -777,7 +776,12 @@ impl BitAnd<I32x4> for I32x4 {
type Output = I32x4;
#[inline]
fn bitand(self, other: I32x4) -> I32x4 {
I32x4([self[0] & other[0], self[1] & other[1], self[2] & other[2], self[3] & other[3]])
I32x4([
self[0] & other[0],
self[1] & other[1],
self[2] & other[2],
self[3] & other[3],
])
}
}
@ -785,7 +789,12 @@ impl BitOr<I32x4> for I32x4 {
type Output = I32x4;
#[inline]
fn bitor(self, other: I32x4) -> I32x4 {
I32x4([self[0] | other[0], self[1] | other[1], self[2] | other[2], self[3] | other[3]])
I32x4([
self[0] | other[0],
self[1] | other[1],
self[2] | other[2],
self[3] | other[3],
])
}
}
@ -840,7 +849,6 @@ impl U32x2 {
pub fn to_i32x2(self) -> I32x2 {
I32x2::new(self[0] as i32, self[1] as i32)
}
}
impl BitAnd<U32x2> for U32x2 {
@ -894,7 +902,12 @@ impl U32x4 {
/// FIXME(pcwalton): Should they? This will assert on overflow in debug.
#[inline]
pub fn to_i32x4(self) -> I32x4 {
I32x4([self[0] as i32, self[1] as i32, self[2] as i32, self[3] as i32])
I32x4([
self[0] as i32,
self[1] as i32,
self[2] as i32,
self[3] as i32,
])
}
// Basic operations
@ -930,6 +943,11 @@ impl Shr<u32> for U32x4 {
type Output = U32x4;
#[inline]
fn shr(self, amount: u32) -> U32x4 {
U32x4([self[0] >> amount, self[1] >> amount, self[2] >> amount, self[3] >> amount])
U32x4([
self[0] >> amount,
self[1] >> amount,
self[2] >> amount,
self[3] >> amount,
])
}
}

View File

@ -37,7 +37,10 @@ fn test_f32x4_accessors_and_mutators() {
fn test_f32x4_basic_ops() {
let a = F32x4::new(1.0, 3.0, 5.0, 7.0);
let b = F32x4::new(2.0, 2.0, 6.0, 6.0);
assert_eq!(a.approx_recip(), F32x4::new(0.99975586, 0.333313, 0.19995117, 0.14282227));
assert_eq!(
a.approx_recip(),
F32x4::new(0.99975586, 0.333313, 0.19995117, 0.14282227)
);
assert_eq!(a.min(b), F32x4::new(1.0, 2.0, 5.0, 6.0));
assert_eq!(a.max(b), F32x4::new(2.0, 3.0, 6.0, 7.0));
let c = F32x4::new(-1.0, 1.3, -20.0, 3.6);

View File

@ -13,14 +13,14 @@ use std::fmt::{self, Debug, Formatter};
use std::mem;
use std::ops::{Add, BitAnd, BitOr, BitXor, Div, Index, IndexMut, Mul, Not, Shr, Sub};
#[cfg(target_pointer_width = "32")]
use std::arch::x86::{__m128, __m128i};
#[cfg(target_pointer_width = "32")]
use std::arch::x86;
#[cfg(target_pointer_width = "64")]
use std::arch::x86_64::{__m128, __m128i};
#[cfg(target_pointer_width = "32")]
use std::arch::x86::{__m128, __m128i};
#[cfg(target_pointer_width = "64")]
use std::arch::x86_64 as x86;
#[cfg(target_pointer_width = "64")]
use std::arch::x86_64::{__m128, __m128i};
mod swizzle_f32x4;
mod swizzle_i32x4;
@ -285,20 +285,12 @@ impl F32x4 {
#[inline]
pub fn packed_eq(self, other: F32x4) -> U32x4 {
unsafe {
U32x4(x86::_mm_castps_si128(x86::_mm_cmpeq_ps(
self.0, other.0,
)))
}
unsafe { U32x4(x86::_mm_castps_si128(x86::_mm_cmpeq_ps(self.0, other.0))) }
}
#[inline]
pub fn packed_gt(self, other: F32x4) -> U32x4 {
unsafe {
U32x4(x86::_mm_castps_si128(x86::_mm_cmpgt_ps(
self.0, other.0,
)))
}
unsafe { U32x4(x86::_mm_castps_si128(x86::_mm_cmpgt_ps(self.0, other.0))) }
}
#[inline]

File diff suppressed because it is too large Load Diff

View File

@ -12,7 +12,7 @@ keywords = ["pathfinder", "svg", "vector", "graphics", "gpu"]
[dependencies]
bitflags = "1.0"
hashbrown = "0.7"
usvg = "0.9"
usvg = "0.10.0"
[dependencies.pathfinder_color]
path = "../color"

View File

@ -25,7 +25,7 @@ use pathfinder_content::transform::Transform2FPathIter;
use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::vector::{Vector2F, vec2f};
use pathfinder_geometry::vector::{vec2f, Vector2F};
use pathfinder_renderer::paint::Paint;
use pathfinder_renderer::scene::{ClipPath, ClipPathId, DrawPath, Scene};
use pathfinder_simd::default::F32x2;
@ -80,7 +80,9 @@ impl SVGScene {
let root = &tree.root();
match *root.borrow() {
NodeKind::Svg(ref svg) => {
built_svg.scene.set_view_box(usvg_rect_to_euclid_rect(&svg.view_box.rect));
built_svg
.scene
.set_view_box(usvg_rect_to_euclid_rect(&svg.view_box.rect));
for kid in root.children() {
built_svg.process_node(&kid, &State::new(), &mut None);
}
@ -91,24 +93,24 @@ impl SVGScene {
built_svg
}
fn process_node(&mut self,
node: &Node,
state: &State,
clip_outline: &mut Option<Outline>) {
fn process_node(&mut self, node: &Node, state: &State, clip_outline: &mut Option<Outline>) {
let mut state = (*state).clone();
let node_transform = usvg_transform_to_transform_2d(&node.transform());
state.transform = state.transform * node_transform;
match *node.borrow() {
NodeKind::Group(ref group) => {
if group.filter.is_some() {
self.result_flags.insert(BuildResultFlags::UNSUPPORTED_FILTER_ATTR);
self.result_flags
.insert(BuildResultFlags::UNSUPPORTED_FILTER_ATTR);
}
if group.mask.is_some() {
self.result_flags.insert(BuildResultFlags::UNSUPPORTED_MASK_ATTR);
self.result_flags
.insert(BuildResultFlags::UNSUPPORTED_MASK_ATTR);
}
if let Some(ref clip_path_name) = group.clip_path {
if let Some(clip_outline) = self.clip_paths.get(clip_path_name) {
let transformed_outline = clip_outline.clone().transformed(&state.transform);
let transformed_outline =
clip_outline.clone().transformed(&state.transform);
let mut clip_path = ClipPath::new(transformed_outline);
clip_path.set_clip_path(state.clip_path);
clip_path.set_name(format!("ClipPath({})", clip_path_name));
@ -126,31 +128,38 @@ impl SVGScene {
let path = UsvgPathToSegments::new(path.data.iter().cloned());
let path = Transform2FPathIter::new(path, &state.transform);
if clip_outline.is_some() {
self.result_flags.insert(BuildResultFlags::UNSUPPORTED_MULTIPLE_CLIP_PATHS);
self.result_flags
.insert(BuildResultFlags::UNSUPPORTED_MULTIPLE_CLIP_PATHS);
}
*clip_outline = Some(Outline::from_segments(path));
}
NodeKind::Path(ref path) if state.path_destination == PathDestination::Draw &&
path.visibility == Visibility::Visible => {
NodeKind::Path(ref path)
if state.path_destination == PathDestination::Draw
&& path.visibility == Visibility::Visible =>
{
if let Some(ref fill) = path.fill {
let path = UsvgPathToSegments::new(path.data.iter().cloned());
let outline = Outline::from_segments(path);
let name = format!("Fill({})", node.id());
self.push_draw_path(outline,
name,
&state,
&fill.paint,
fill.opacity,
fill.rule);
self.push_draw_path(
outline,
name,
&state,
&fill.paint,
fill.opacity,
fill.rule,
);
}
if let Some(ref stroke) = path.stroke {
let stroke_style = StrokeStyle {
line_width: f32::max(stroke.width.value() as f32, HAIRLINE_STROKE_WIDTH),
line_cap: LineCap::from_usvg_line_cap(stroke.linecap),
line_join: LineJoin::from_usvg_line_join(stroke.linejoin,
stroke.miterlimit.value() as f32),
line_join: LineJoin::from_usvg_line_join(
stroke.linejoin,
stroke.miterlimit.value() as f32,
),
};
let path = UsvgPathToSegments::new(path.data.iter().cloned());
@ -168,12 +177,14 @@ impl SVGScene {
let outline = stroke_to_fill.into_outline();
let name = format!("Stroke({})", node.id());
self.push_draw_path(outline,
name,
&state,
&stroke.paint,
stroke.opacity,
UsvgFillRule::NonZero);
self.push_draw_path(
outline,
name,
&state,
&stroke.paint,
stroke.opacity,
UsvgFillRule::NonZero,
);
}
}
NodeKind::Path(..) => {}
@ -184,7 +195,8 @@ impl SVGScene {
self.process_node(&kid, &state, &mut clip_outline);
}
self.clip_paths.insert(node.id().to_owned(), clip_outline.unwrap());
self.clip_paths
.insert(node.id().to_owned(), clip_outline.unwrap());
}
NodeKind::Defs => {
// FIXME(pcwalton): This is wrong.
@ -195,20 +207,24 @@ impl SVGScene {
}
NodeKind::LinearGradient(ref svg_linear_gradient) => {
let from = vec2f(svg_linear_gradient.x1 as f32, svg_linear_gradient.y1 as f32);
let to = vec2f(svg_linear_gradient.x2 as f32, svg_linear_gradient.y2 as f32);
let to = vec2f(svg_linear_gradient.x2 as f32, svg_linear_gradient.y2 as f32);
let gradient = Gradient::linear_from_points(from, to);
self.add_gradient(gradient,
svg_linear_gradient.id.clone(),
&svg_linear_gradient.base)
self.add_gradient(
gradient,
svg_linear_gradient.id.clone(),
&svg_linear_gradient.base,
)
}
NodeKind::RadialGradient(ref svg_radial_gradient) => {
let from = vec2f(svg_radial_gradient.fx as f32, svg_radial_gradient.fy as f32);
let to = vec2f(svg_radial_gradient.cx as f32, svg_radial_gradient.cy as f32);
let to = vec2f(svg_radial_gradient.cx as f32, svg_radial_gradient.cy as f32);
let radii = F32x2::new(0.0, svg_radial_gradient.r.value() as f32);
let gradient = Gradient::radial(LineSegment2F::new(from, to), radii);
self.add_gradient(gradient,
svg_radial_gradient.id.clone(),
&svg_radial_gradient.base)
self.add_gradient(
gradient,
svg_radial_gradient.id.clone(),
&svg_radial_gradient.base,
)
}
NodeKind::Filter(..) => {
self.result_flags
@ -230,10 +246,12 @@ impl SVGScene {
}
}
fn add_gradient(&mut self,
mut gradient: Gradient,
id: String,
usvg_base_gradient: &BaseGradient) {
fn add_gradient(
&mut self,
mut gradient: Gradient,
id: String,
usvg_base_gradient: &BaseGradient,
) {
for stop in &usvg_base_gradient.stops {
let mut stop = ColorStop::from_usvg_stop(stop);
if usvg_base_gradient.spread_method == SpreadMethod::Reflect {
@ -261,22 +279,32 @@ impl SVGScene {
let transform = usvg_transform_to_transform_2d(&usvg_base_gradient.transform);
// TODO(pcwalton): What should we do with `gradientUnits`?
self.gradients.insert(id, GradientInfo { gradient, transform });
self.gradients.insert(
id,
GradientInfo {
gradient,
transform,
},
);
}
fn push_draw_path(&mut self,
mut outline: Outline,
name: String,
state: &State,
paint: &UsvgPaint,
opacity: Opacity,
fill_rule: UsvgFillRule) {
fn push_draw_path(
&mut self,
mut outline: Outline,
name: String,
state: &State,
paint: &UsvgPaint,
opacity: Opacity,
fill_rule: UsvgFillRule,
) {
outline.transform(&state.transform);
let paint = Paint::from_svg_paint(paint,
&state.transform,
opacity,
&self.gradients,
&mut self.result_flags);
let paint = Paint::from_svg_paint(
paint,
&state.transform,
opacity,
&self.gradients,
&mut self.result_flags,
);
let style = self.scene.push_paint(&paint);
let fill_rule = FillRule::from_usvg_fill_rule(fill_rule);
let mut path = DrawPath::new(outline, style);
@ -323,22 +351,24 @@ impl Display for BuildResultFlags {
}
trait PaintExt {
fn from_svg_paint(svg_paint: &UsvgPaint,
transform: &Transform2F,
opacity: Opacity,
gradients: &HashMap<String, GradientInfo>,
result_flags: &mut BuildResultFlags)
-> Self;
fn from_svg_paint(
svg_paint: &UsvgPaint,
transform: &Transform2F,
opacity: Opacity,
gradients: &HashMap<String, GradientInfo>,
result_flags: &mut BuildResultFlags,
) -> Self;
}
impl PaintExt for Paint {
#[inline]
fn from_svg_paint(svg_paint: &UsvgPaint,
transform: &Transform2F,
opacity: Opacity,
gradients: &HashMap<String, GradientInfo>,
result_flags: &mut BuildResultFlags)
-> Paint {
fn from_svg_paint(
svg_paint: &UsvgPaint,
transform: &Transform2F,
opacity: Opacity,
gradients: &HashMap<String, GradientInfo>,
result_flags: &mut BuildResultFlags,
) -> Paint {
let mut paint;
match *svg_paint {
UsvgPaint::Color(color) => paint = Paint::from_color(ColorU::from_svg_color(color)),
@ -366,13 +396,21 @@ impl PaintExt for Paint {
}
fn usvg_rect_to_euclid_rect(rect: &UsvgRect) -> RectF {
RectF::new(vec2f(rect.x() as f32, rect.y() as f32),
vec2f(rect.width() as f32, rect.height() as f32))
RectF::new(
vec2f(rect.x() as f32, rect.y() as f32),
vec2f(rect.width() as f32, rect.height() as f32),
)
}
fn usvg_transform_to_transform_2d(transform: &UsvgTransform) -> Transform2F {
Transform2F::row_major(transform.a as f32, transform.c as f32, transform.e as f32,
transform.b as f32, transform.d as f32, transform.f as f32)
Transform2F::row_major(
transform.a as f32,
transform.c as f32,
transform.e as f32,
transform.b as f32,
transform.d as f32,
transform.f as f32,
)
}
struct UsvgPathToSegments<I>
@ -435,8 +473,10 @@ where
let ctrl0 = vec2f(x1 as f32, y1 as f32);
let ctrl1 = vec2f(x2 as f32, y2 as f32);
let to = vec2f(x as f32, y as f32);
let mut segment = Segment::cubic(LineSegment2F::new(self.last_subpath_point, to),
LineSegment2F::new(ctrl0, ctrl1));
let mut segment = Segment::cubic(
LineSegment2F::new(self.last_subpath_point, to),
LineSegment2F::new(ctrl0, ctrl1),
);
if self.just_moved {
segment.flags.insert(SegmentFlags::FIRST_IN_SUBPATH);
}
@ -465,7 +505,12 @@ trait ColorUExt {
impl ColorUExt for ColorU {
#[inline]
fn from_svg_color(svg_color: SvgColor) -> ColorU {
ColorU { r: svg_color.red, g: svg_color.green, b: svg_color.blue, a: !0 }
ColorU {
r: svg_color.red,
g: svg_color.green,
b: svg_color.blue,
a: !0,
}
}
}

View File

@ -8,16 +8,16 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::ops::Add;
use pathfinder_color::{ColorF, ColorU};
use pathfinder_content::fill::FillRule;
use pathfinder_content::outline::{Outline, Contour};
use pathfinder_content::outline::{Contour, Outline};
use pathfinder_content::stroke::{OutlineStrokeToFill, StrokeStyle};
use pathfinder_geometry::vector::vec2f;
use pathfinder_renderer::scene::{DrawPath, Scene};
use std::ops::Add;
use swf_types::tags::SetBackgroundColor;
use swf_types::{Tag, SRgb8, Movie};
use swf_types::{Movie, SRgb8, Tag};
use crate::shapes::{GraphicLayers, PaintOrLine};
@ -51,7 +51,7 @@ impl Add for Twips {
#[derive(Copy, Clone, Debug, PartialEq)]
struct Point2<T> {
x: T,
y: T
y: T,
}
impl Point2<Twips> {
@ -66,7 +66,10 @@ impl Point2<Twips> {
impl Add for Point2<Twips> {
type Output = Self;
fn add(self, rhs: Self) -> Self {
Point2 { x: self.x + rhs.x, y: self.y + rhs.y }
Point2 {
x: self.x + rhs.x,
y: self.y + rhs.y,
}
}
}
@ -98,11 +101,11 @@ impl Stage {
g: self.background_color.g,
b: self.background_color.b,
a: 255,
}.to_f32()
}
.to_f32()
}
}
pub struct SymbolLibrary(Vec<Symbol>);
impl SymbolLibrary {
@ -126,7 +129,7 @@ pub fn process_swf_tags(movie: &Movie) -> (SymbolLibrary, Stage) {
background_color: SRgb8 {
r: 255,
g: 255,
b: 255
b: 255,
},
width: stage_width.as_f32() as i32,
height: stage_height.as_f32() as i32,
@ -136,14 +139,14 @@ pub fn process_swf_tags(movie: &Movie) -> (SymbolLibrary, Stage) {
match tag {
Tag::SetBackgroundColor(SetBackgroundColor { color }) => {
stage.background_color = *color;
},
}
Tag::DefineShape(shape) => {
symbol_library.add_symbol(Symbol::Graphic(shapes::decode_shape(shape)));
// We will assume that symbol ids just go up, and are 1 based.
let symbol_id: SymbolId = shape.id;
debug_assert!(symbol_id as usize == symbol_library.0.len());
}
_ => ()
_ => (),
}
}
(symbol_library, stage)
@ -166,12 +169,15 @@ pub fn draw_paths_into_scene(library: &SymbolLibrary, scene: &mut Scene) {
let Point2 { x, y } = segment.to.as_f32();
match segment.ctrl {
Some(ctrl) => {
let Point2 { x: ctrl_x, y: ctrl_y } = ctrl.as_f32();
let Point2 {
x: ctrl_x,
y: ctrl_y,
} = ctrl.as_f32();
contour.push_quadratic(vec2f(ctrl_x, ctrl_y), vec2f(x, y));
}
None => {
contour.push_endpoint(vec2f(x, y));
},
}
}
}
if shape.is_closed() {
@ -183,11 +189,14 @@ pub fn draw_paths_into_scene(library: &SymbolLibrary, scene: &mut Scene) {
}
if let PaintOrLine::Line(line) = style_layer.kind() {
let mut stroke_to_fill = OutlineStrokeToFill::new(&path, StrokeStyle {
line_width: line.width.as_f32(),
line_cap: line.cap,
line_join: line.join,
});
let mut stroke_to_fill = OutlineStrokeToFill::new(
&path,
StrokeStyle {
line_width: line.width.as_f32(),
line_cap: line.cap,
line_join: line.join,
},
);
stroke_to_fill.offset();
path = stroke_to_fill.into_outline();
}

View File

@ -8,16 +8,16 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::{Twips, Point2};
use crate::{Point2, Twips};
use pathfinder_color::ColorU;
use pathfinder_content::stroke::{LineJoin, LineCap};
use pathfinder_content::stroke::{LineCap, LineJoin};
use pathfinder_renderer::paint::Paint;
use std::cmp::Ordering;
use std::mem;
use swf_types::tags::DefineShape;
use swf_types::{CapStyle, FillStyle, JoinStyle, LineStyle, ShapeRecord, StraightSRgba8, Vector2D};
use swf_types::{fill_styles, join_styles, shape_records};
use swf_types::{CapStyle, FillStyle, JoinStyle, LineStyle, ShapeRecord, StraightSRgba8, Vector2D};
#[derive(Clone, Copy, Debug)]
pub(crate) struct LineSegment {
@ -44,7 +44,7 @@ impl LineDirection {
fn reverse(&mut self) {
*self = match self {
LineDirection::Right => LineDirection::Left,
LineDirection::Left => LineDirection::Right
LineDirection::Left => LineDirection::Right,
};
}
}
@ -194,19 +194,16 @@ impl StyleLayer {
if self.is_fill() {
// I think sorting is only necessary when we want to have closed shapes,
// lines don't really need this?
self.shapes.sort_unstable_by(|a, b| {
match (a.is_closed(), b.is_closed()) {
self.shapes
.sort_unstable_by(|a, b| match (a.is_closed(), b.is_closed()) {
(true, true) | (false, false) => Ordering::Equal,
(true, false) => Ordering::Less,
(false, true) => Ordering::Greater,
}
});
});
}
// A cursor at the index of the first unclosed shape, if any.
let first_open_index = self.shapes
.iter()
.position(|frag| !frag.is_closed());
let first_open_index = self.shapes.iter().position(|frag| !frag.is_closed());
if let Some(first_open_index) = first_open_index {
if self.shapes.len() - first_open_index >= 2 {
@ -235,78 +232,75 @@ impl StyleLayer {
}
}
fn get_new_styles<'a>(
fills: &'a Vec<FillStyle>,
lines: &'a Vec<LineStyle>
) -> impl Iterator<Item=PaintOrLine> + 'a {
lines: &'a Vec<LineStyle>,
) -> impl Iterator<Item = PaintOrLine> + 'a {
// This enforces the order that fills and line groupings are added in.
// Fills always come first.
fills.iter().filter_map(|fill_style| {
match fill_style {
FillStyle::Solid(
fill_styles::Solid {
color: StraightSRgba8 {
r,
g,
b,
a
}
}
) => {
Some(PaintOrLine::Paint(Paint::from_color(ColorU { r: *r, g: *g, b: *b, a: *a })))
}
_ => unimplemented!("Unimplemented fill style")
}
}).chain(
lines.iter().filter_map(|LineStyle {
width,
fill,
join,
start_cap,
end_cap: _,
/*
TODO(jon): Handle these cases?
pub no_h_scale: bool,
pub no_v_scale: bool,
pub no_close: bool,
pub pixel_hinting: bool,
*/
..
}| {
if let FillStyle::Solid(fill_styles::Solid {
color: StraightSRgba8 {
r,
g,
b,
a
}
}) = fill {
// NOTE: PathFinder doesn't support different cap styles for start and end of
// strokes, so lets assume that they're always the same for the inputs we care about.
// Alternately, we split a line in two with a diff cap style for each.
// assert_eq!(start_cap, end_cap);
Some(PaintOrLine::Line(SwfLineStyle {
width: Twips(*width as i32),
color: Paint::from_color(ColorU { r: *r, g: *g, b: *b, a: *a }),
join: match join {
JoinStyle::Bevel => LineJoin::Bevel,
JoinStyle::Round => LineJoin::Round,
JoinStyle::Miter(join_styles::Miter { limit }) => {
LineJoin::Miter(*limit as f32)
},
},
cap: match start_cap {
CapStyle::None => LineCap::Butt,
CapStyle::Square => LineCap::Square,
CapStyle::Round => LineCap::Round,
},
}))
} else {
unimplemented!("unimplemented line fill style");
}
fills
.iter()
.filter_map(|fill_style| match fill_style {
FillStyle::Solid(fill_styles::Solid {
color: StraightSRgba8 { r, g, b, a },
}) => Some(PaintOrLine::Paint(Paint::from_color(ColorU {
r: *r,
g: *g,
b: *b,
a: *a,
}))),
_ => unimplemented!("Unimplemented fill style"),
})
)
.chain(lines.iter().filter_map(
|LineStyle {
width,
fill,
join,
start_cap,
end_cap: _,
/*
TODO(jon): Handle these cases?
pub no_h_scale: bool,
pub no_v_scale: bool,
pub no_close: bool,
pub pixel_hinting: bool,
*/
..
}| {
if let FillStyle::Solid(fill_styles::Solid {
color: StraightSRgba8 { r, g, b, a },
}) = fill
{
// NOTE: PathFinder doesn't support different cap styles for start and end of
// strokes, so lets assume that they're always the same for the inputs we care about.
// Alternately, we split a line in two with a diff cap style for each.
// assert_eq!(start_cap, end_cap);
Some(PaintOrLine::Line(SwfLineStyle {
width: Twips(*width as i32),
color: Paint::from_color(ColorU {
r: *r,
g: *g,
b: *b,
a: *a,
}),
join: match join {
JoinStyle::Bevel => LineJoin::Bevel,
JoinStyle::Round => LineJoin::Round,
JoinStyle::Miter(join_styles::Miter { limit }) => {
LineJoin::Miter(*limit as f32)
}
},
cap: match start_cap {
CapStyle::None => LineCap::Butt,
CapStyle::Square => LineCap::Square,
CapStyle::Round => LineCap::Round,
},
}))
} else {
unimplemented!("unimplemented line fill style");
}
},
))
}
pub(crate) fn decode_shape(shape: &DefineShape) -> GraphicLayers {
@ -339,15 +333,13 @@ pub(crate) fn decode_shape(shape: &DefineShape) -> GraphicLayers {
for record in &shape.records {
match record {
ShapeRecord::StyleChange(
shape_records::StyleChange {
move_to,
new_styles,
line_style,
left_fill,
right_fill,
}
) => {
ShapeRecord::StyleChange(shape_records::StyleChange {
move_to,
new_styles,
line_style,
left_fill,
right_fill,
}) => {
// Start a whole new style grouping.
if let Some(new_style) = new_styles {
// Consolidate current style grouping and begin a new one.
@ -406,7 +398,10 @@ pub(crate) fn decode_shape(shape: &DefineShape) -> GraphicLayers {
// Move to, start new shape fragments with the current styles.
if let Some(Vector2D { x, y }) = move_to {
let to: Point2<Twips> = Point2 { x: Twips(*x), y: Twips(*y) };
let to: Point2<Twips> = Point2 {
x: Twips(*x),
y: Twips(*y),
};
prev_pos = Some(to);
// If we didn't start a new shape for the current fill due to a fill
@ -433,34 +428,27 @@ pub(crate) fn decode_shape(shape: &DefineShape) -> GraphicLayers {
.push_new_shape(LineDirection::Right);
}
}
},
ShapeRecord::Edge(
shape_records::Edge {
delta,
control_delta,
}
) => {
}
ShapeRecord::Edge(shape_records::Edge {
delta,
control_delta,
}) => {
let from = prev_pos.unwrap();
let to = Point2 {
x: from.x + Twips(delta.x),
y: from.y + Twips(delta.y)
y: from.y + Twips(delta.y),
};
prev_pos = Some(to);
let new_segment = LineSegment {
from,
to,
ctrl: control_delta.map(|Vector2D { x, y }| {
Point2 {
x: from.x + Twips(x),
y: from.y + Twips(y),
}
ctrl: control_delta.map(|Vector2D { x, y }| Point2 {
x: from.x + Twips(x),
y: from.y + Twips(y),
}),
};
if some_fill_set && !both_fills_same {
for fill_id in [
current_right_fill,
current_left_fill
].iter() {
for fill_id in [current_right_fill, current_left_fill].iter() {
if let Some(fill_id) = fill_id {
graphic
.with_fill_style_mut(*fill_id)
@ -472,8 +460,10 @@ pub(crate) fn decode_shape(shape: &DefineShape) -> GraphicLayers {
} else if both_fills_set_and_same {
for (fill_id, direction) in [
(current_right_fill, LineDirection::Right),
(current_left_fill, LineDirection::Left)
].iter() {
(current_left_fill, LineDirection::Left),
]
.iter()
{
// NOTE: If both left and right fill are set the same,
// then we don't record the edge as part of the current shape;
// it's will just be an internal stroke inside an otherwise solid
@ -508,7 +498,7 @@ pub(crate) fn decode_shape(shape: &DefineShape) -> GraphicLayers {
fn find_matches(
mut first_open_index: usize,
shapes: &mut Vec<Shape>,
reverse: bool
reverse: bool,
) -> Option<Vec<Shape>> {
let mut dropped_pieces = None;
while first_open_index < shapes.len() {
@ -563,7 +553,11 @@ pub(crate) struct GraphicLayers {
impl GraphicLayers {
fn new() -> GraphicLayers {
GraphicLayers { style_layers: Vec::new(), stroke_layer_offset: None, base_layer_offset: 0 }
GraphicLayers {
style_layers: Vec::new(),
stroke_layer_offset: None,
base_layer_offset: 0,
}
}
fn begin_style_group(&mut self) {
@ -572,22 +566,30 @@ impl GraphicLayers {
}
fn begin_fill_style(&mut self, fill: Paint) {
self.style_layers.push(StyleLayer { fill: PaintOrLine::Paint(fill), shapes: Vec::new() })
self.style_layers.push(StyleLayer {
fill: PaintOrLine::Paint(fill),
shapes: Vec::new(),
})
}
fn begin_line_style(&mut self, line: SwfLineStyle) {
if self.stroke_layer_offset.is_none() {
self.stroke_layer_offset = Some(self.style_layers.len());
}
self.style_layers.push(StyleLayer { fill: PaintOrLine::Line(line), shapes: Vec::new() })
self.style_layers.push(StyleLayer {
fill: PaintOrLine::Line(line),
shapes: Vec::new(),
})
}
fn with_fill_style_mut(&mut self, fill_id: usize) -> Option<&mut StyleLayer> {
self.style_layers.get_mut(self.base_layer_offset + fill_id - 1)
self.style_layers
.get_mut(self.base_layer_offset + fill_id - 1)
}
fn with_line_style_mut(&mut self, line_id: usize) -> Option<&mut StyleLayer> {
self.style_layers.get_mut((self.stroke_layer_offset.unwrap() + line_id) - 1)
self.style_layers
.get_mut((self.stroke_layer_offset.unwrap() + line_id) - 1)
}
pub(crate) fn layers(&self) -> &Vec<StyleLayer> {
@ -606,4 +608,3 @@ impl GraphicLayers {
}
}
}

View File

@ -9,7 +9,7 @@ repository = "https://github.com/servo/pathfinder"
homepage = "https://github.com/servo/pathfinder"
[dependencies]
font-kit = "0.6"
font-kit = "0.13"
[dependencies.pathfinder_content]
path = "../content"

View File

@ -19,7 +19,7 @@ use pathfinder_content::outline::{Contour, Outline};
use pathfinder_content::stroke::{OutlineStrokeToFill, StrokeStyle};
use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::vector::{Vector2F, vec2f};
use pathfinder_geometry::vector::{vec2f, Vector2F};
use pathfinder_renderer::paint::PaintId;
use pathfinder_renderer::scene::{ClipPathId, DrawPath, Scene};
use skribo::{FontCollection, Layout, TextStyle};
@ -28,12 +28,18 @@ use std::mem;
use std::sync::Arc;
#[derive(Clone)]
pub struct FontContext<F> where F: Loader {
font_info: HashMap<String, FontInfo<F>>,
pub struct FontContext<F>
where
F: Loader,
{
font_info: HashMap<String, FontInfo<F>>,
}
#[derive(Clone)]
struct FontInfo<F> where F: Loader {
struct FontInfo<F>
where
F: Loader,
{
font: F,
metrics: Metrics,
outline_cache: HashMap<GlyphId, Outline>,
@ -63,7 +69,10 @@ impl Default for FontRenderOptions {
}
}
enum FontInfoRefMut<'a, F> where F: Loader {
enum FontInfoRefMut<'a, F>
where
F: Loader,
{
Ref(&'a mut FontInfo<F>),
Owned(FontInfo<F>),
}
@ -71,26 +80,33 @@ enum FontInfoRefMut<'a, F> where F: Loader {
#[derive(Clone, Copy, PartialEq, Debug, Eq, Hash)]
pub struct GlyphId(pub u32);
impl<F> FontContext<F> where F: Loader {
impl<F> FontContext<F>
where
F: Loader,
{
#[inline]
pub fn new() -> FontContext<F> {
FontContext { font_info: HashMap::new() }
FontContext {
font_info: HashMap::new(),
}
}
fn push_glyph(&mut self,
scene: &mut Scene,
font: &F,
font_key: Option<&str>,
glyph_id: GlyphId,
glyph_offset: Vector2F,
font_size: f32,
render_options: &FontRenderOptions)
-> Result<(), GlyphLoadingError> {
fn push_glyph(
&mut self,
scene: &mut Scene,
font: &F,
font_key: Option<&str>,
glyph_id: GlyphId,
glyph_offset: Vector2F,
font_size: f32,
render_options: &FontRenderOptions,
) -> Result<(), GlyphLoadingError> {
// Insert the font into the cache if needed.
let mut font_info = match font_key {
Some(font_key) => {
if !self.font_info.contains_key(&*font_key) {
self.font_info.insert(font_key.to_owned(), FontInfo::new((*font).clone()));
self.font_info
.insert(font_key.to_owned(), FontInfo::new((*font).clone()));
}
FontInfoRefMut::Ref(self.font_info.get_mut(&*font_key).unwrap())
}
@ -115,8 +131,8 @@ impl<F> FontContext<F> where F: Loader {
let metrics = &font_info.metrics;
let font_scale = font_size / metrics.units_per_em as f32;
let render_transform = render_options.transform *
Transform2F::from_scale(vec2f(font_scale, -font_scale)).translate(glyph_offset);
let render_transform = render_options.transform
* Transform2F::from_scale(vec2f(font_scale, -font_scale)).translate(glyph_offset);
let mut outline = match cached_outline {
Some(mut cached_outline) => {
@ -131,7 +147,11 @@ impl<F> FontContext<F> where F: Loader {
render_transform
};
let mut outline_builder = OutlinePathBuilder::new(&transform);
font.outline(glyph_id.0, render_options.hinting_options, &mut outline_builder)?;
font.outline(
glyph_id.0,
render_options.hinting_options,
&mut outline_builder,
)?;
let mut outline = outline_builder.build();
if can_cache_outline {
font_info.outline_cache.insert(glyph_id, outline.clone());
@ -159,22 +179,25 @@ impl<F> FontContext<F> where F: Loader {
/// Attempts to look up a font in the font cache.
#[inline]
pub fn get_cached_font(&self, postscript_name: &str) -> Option<&F> {
self.font_info.get(postscript_name).map(|font_info| &font_info.font)
self.font_info
.get(postscript_name)
.map(|font_info| &font_info.font)
}
}
impl FontContext<DefaultLoader> {
pub fn push_layout(&mut self,
scene: &mut Scene,
layout: &Layout,
style: &TextStyle,
render_options: &FontRenderOptions)
-> Result<(), GlyphLoadingError> {
pub fn push_layout(
&mut self,
scene: &mut Scene,
layout: &Layout,
style: &TextStyle,
render_options: &FontRenderOptions,
) -> Result<(), GlyphLoadingError> {
let mut cached_font_key: Option<CachedFontKey<DefaultLoader>> = None;
for glyph in &layout.glyphs {
match cached_font_key {
Some(ref cached_font_key) if Arc::ptr_eq(&cached_font_key.font,
&glyph.font.font) => {}
Some(ref cached_font_key)
if Arc::ptr_eq(&cached_font_key.font, &glyph.font.font) => {}
_ => {
cached_font_key = Some(CachedFontKey {
font: glyph.font.font.clone(),
@ -183,43 +206,59 @@ impl FontContext<DefaultLoader> {
}
}
let cached_font_key = cached_font_key.as_ref().unwrap();
self.push_glyph(scene,
&*cached_font_key.font,
cached_font_key.key.as_ref().map(|key| &**key),
GlyphId(glyph.glyph_id),
glyph.offset,
style.size,
&render_options)?;
self.push_glyph(
scene,
&*cached_font_key.font,
cached_font_key.key.as_ref().map(|key| &**key),
GlyphId(glyph.glyph_id),
glyph.offset,
style.size,
&render_options,
)?;
}
Ok(())
}
#[inline]
pub fn push_text(&mut self,
scene: &mut Scene,
text: &str,
style: &TextStyle,
collection: &FontCollection,
render_options: &FontRenderOptions)
-> Result<(), GlyphLoadingError> {
pub fn push_text(
&mut self,
scene: &mut Scene,
text: &str,
style: &TextStyle,
collection: &FontCollection,
render_options: &FontRenderOptions,
) -> Result<(), GlyphLoadingError> {
let layout = skribo::layout(style, collection, text);
self.push_layout(scene, &layout, style, render_options)
}
}
struct CachedFontKey<F> where F: Loader {
struct CachedFontKey<F>
where
F: Loader,
{
font: Arc<F>,
key: Option<String>,
}
impl<F> FontInfo<F> where F: Loader {
impl<F> FontInfo<F>
where
F: Loader,
{
fn new(font: F) -> FontInfo<F> {
let metrics = font.metrics();
FontInfo { font, metrics, outline_cache: HashMap::new() }
FontInfo {
font,
metrics,
outline_cache: HashMap::new(),
}
}
}
impl<'a, F> FontInfoRefMut<'a, F> where F: Loader {
impl<'a, F> FontInfoRefMut<'a, F>
where
F: Loader,
{
fn get_mut(&mut self) -> &mut FontInfo<F> {
match *self {
FontInfoRefMut::Ref(ref mut reference) => &mut **reference,
@ -251,7 +290,8 @@ impl OutlinePathBuilder {
fn flush_current_contour(&mut self) {
if !self.current_contour.is_empty() {
self.outline.push_contour(mem::replace(&mut self.current_contour, Contour::new()));
self.outline
.push_contour(mem::replace(&mut self.current_contour, Contour::new()));
}
}
@ -272,13 +312,16 @@ impl OutlineSink for OutlinePathBuilder {
}
fn quadratic_curve_to(&mut self, ctrl: Vector2F, to: Vector2F) {
self.current_contour.push_quadratic(self.transform * ctrl, self.transform * to);
self.current_contour
.push_quadratic(self.transform * ctrl, self.transform * to);
}
fn cubic_curve_to(&mut self, ctrl: LineSegment2F, to: Vector2F) {
self.current_contour.push_cubic(self.transform * ctrl.from(),
self.transform * ctrl.to(),
self.transform * to);
self.current_contour.push_cubic(
self.transform * ctrl.from(),
self.transform * ctrl.to(),
self.transform * to,
);
}
fn close(&mut self) {

File diff suppressed because it is too large Load Diff

View File

@ -69,17 +69,20 @@ fn main() {
.version("0.1")
.author("The Pathfinder Project Developers")
.about("Generates area lookup tables for use with Pathfinder")
.arg(Arg::with_name("OUTPUT-PATH").help("The `.png` image to produce")
.required(true)
.index(1));
.arg(
Arg::with_name("OUTPUT-PATH")
.help("The `.png` image to produce")
.required(true)
.index(1),
);
let matches = app.get_matches();
let image = ImageBuffer::from_fn(WIDTH, HEIGHT, |u, v| {
if u == 0 {
return Rgba([255, 255, 255, 255])
return Rgba([255, 255, 255, 255]);
}
if u == WIDTH - 1 {
return Rgba([0, 0, 0, 0])
return Rgba([0, 0, 0, 0]);
}
let y = ((u as f32) - (WIDTH / 2) as f32) / 16.0;

View File

@ -9,4 +9,4 @@ edition = "2018"
[dependencies]
pathfinder_export = { path = "../../export" }
pathfinder_svg = { path = "../../svg" }
usvg = "0.9"
usvg = "0.10"

View File

@ -1,10 +1,10 @@
use std::fs::File;
use std::io::{Read, BufWriter};
use std::error::Error;
use std::path::PathBuf;
use pathfinder_svg::SVGScene;
use pathfinder_export::{Export, FileFormat};
use usvg::{Tree, Options};
use pathfinder_svg::SVGScene;
use std::error::Error;
use std::fs::File;
use std::io::{BufWriter, Read};
use std::path::PathBuf;
use usvg::{Options, Tree};
fn main() -> Result<(), Box<dyn Error>> {
let mut args = std::env::args_os().skip(1);
@ -20,7 +20,7 @@ fn main() -> Result<(), Box<dyn Error>> {
let format = match output.extension().and_then(|s| s.to_str()) {
Some("pdf") => FileFormat::PDF,
Some("ps") => FileFormat::PS,
_ => return Err("output filename must have .ps or .pdf extension".into())
_ => return Err("output filename must have .ps or .pdf extension".into()),
};
scene.export(&mut writer, format).unwrap();
Ok(())

View File

@ -62,7 +62,7 @@ impl LuminanceColorSpace {
if luma <= 0.0031308 {
luma * 12.92
} else {
1.055 * luma.powf(1./2.4) - 0.055
1.055 * luma.powf(1. / 2.4) - 0.055
}
}
}
@ -70,7 +70,7 @@ impl LuminanceColorSpace {
}
//TODO: tests
fn round_to_u8(x : f32) -> u8 {
fn round_to_u8(x: f32) -> u8 {
let v = (x + 0.5).floor() as i32;
assert!(0 <= v && v < 0x100);
v as u8
@ -134,12 +134,7 @@ impl ColorLut for ColorU {
// Quantize to the smallest value that yields the same table index.
fn quantized_floor(&self) -> ColorU {
ColorU::new(
self.r & LUM_MASK,
self.g & LUM_MASK,
self.b & LUM_MASK,
255,
)
ColorU::new(self.r & LUM_MASK, self.g & LUM_MASK, self.b & LUM_MASK, 255)
}
// Quantize to the largest value that yields the same table index.
@ -176,10 +171,13 @@ fn apply_contrast(srca: f32, contrast: f32) -> f32 {
// The approach here is not necessarily the one with the lowest error
// See https://bel.fi/alankila/lcd/alpcor.html for a similar kind of thing
// that just search for the adjusted alpha value
pub fn build_gamma_correcting_lut(table: &mut [u8; 256], src: u8, contrast: f32,
src_space: LuminanceColorSpace,
dst_convert: LuminanceColorSpace) {
pub fn build_gamma_correcting_lut(
table: &mut [u8; 256],
src: u8,
contrast: f32,
src_space: LuminanceColorSpace,
dst_convert: LuminanceColorSpace,
) {
let src = src as f32 / 255.0;
let lin_src = src_space.to_luma(src);
// Guess at the dst. The perceptual inverse provides smaller visual
@ -195,7 +193,7 @@ pub fn build_gamma_correcting_lut(table: &mut [u8; 256], src: u8, contrast: f32,
// Remove discontinuity and instability when src is close to dst.
// The value 1/256 is arbitrary and appears to contain the instability.
if (src - dst).abs() < (1.0 / 256.0) {
let mut ii : f32 = 0.0;
let mut ii: f32 = 0.0;
for v in table.iter_mut() {
let raw_srca = ii / 255.0;
let srca = apply_contrast(raw_srca, adjusted_contrast);
@ -205,7 +203,7 @@ pub fn build_gamma_correcting_lut(table: &mut [u8; 256], src: u8, contrast: f32,
}
} else {
// Avoid slow int to float conversion.
let mut ii : f32 = 0.0;
let mut ii: f32 = 0.0;
for v in table.iter_mut() {
// 'raw_srca += 1.0f / 255.0f' and even
// 'raw_srca = i * (1.0f / 255.0f)' can add up to more than 1.0f.
@ -247,11 +245,13 @@ impl GammaLut {
for (i, entry) in self.tables.iter_mut().enumerate() {
let luminance = scale255(LUM_BITS, i as u8);
build_gamma_correcting_lut(entry,
luminance,
contrast,
paint_color_space,
device_color_space);
build_gamma_correcting_lut(
entry,
luminance,
contrast,
paint_color_space,
device_color_space,
);
}
}
@ -280,7 +280,11 @@ impl GammaLut {
let table_b = self.get_table(color.b);
for pixel in pixels.chunks_mut(4) {
let (b, g, r) = (table_b[pixel[0] as usize], table_g[pixel[1] as usize], table_r[pixel[2] as usize]);
let (b, g, r) = (
table_b[pixel[0] as usize],
table_g[pixel[1] as usize],
table_r[pixel[2] as usize],
);
pixel[0] = b;
pixel[1] = g;
pixel[2] = r;
@ -301,7 +305,6 @@ impl GammaLut {
pixel[3] = alpha;
}
}
} // end impl GammaLut
#[cfg(test)]
@ -309,16 +312,19 @@ mod tests {
use super::*;
fn over(dst: u32, src: u32, alpha: u32) -> u32 {
(src * alpha + dst * (255 - alpha))/255
(src * alpha + dst * (255 - alpha)) / 255
}
fn overf(dst: f32, src: f32, alpha: f32) -> f32 {
((src * alpha + dst * (255. - alpha))/255.) as f32
((src * alpha + dst * (255. - alpha)) / 255.) as f32
}
fn absdiff(a: u32, b: u32) -> u32 {
if a < b { b - a } else { a - b }
if a < b {
b - a
} else {
a - b
}
}
#[test]
@ -326,7 +332,7 @@ mod tests {
let mut table = [0u8; 256];
let g = 2.0;
let space = LuminanceColorSpace::Gamma(g);
let mut src : u32 = 131;
let mut src: u32 = 131;
while src < 256 {
build_gamma_correcting_lut(&mut table, src as u8, 0., space, space);
let mut max_diff = 0;
@ -338,7 +344,8 @@ mod tests {
let lin_src = (src as f32 / 255.).powf(g) * 255.;
let preblend_result = over(dst, src, preblend as u32);
let true_result = ((overf(lin_dst, lin_src, alpha as f32) / 255.).powf(1. / g) * 255.) as u32;
let true_result =
((overf(lin_dst, lin_src, alpha as f32) / 255.).powf(1. / g) * 255.) as u32;
let diff = absdiff(preblend_result, true_result);
//println!("{} -- {} {} = {}", alpha, preblend_result, true_result, diff);
max_diff = max(max_diff, diff);
@ -347,7 +354,6 @@ mod tests {
//println!("{} {} max {}", src, dst, max_diff);
assert!(max_diff <= 33);
dst += 1;
}
src += 1;
}

View File

@ -42,9 +42,12 @@ pub fn main() {
.version("0.1")
.author("The Pathfinder Project Developers")
.about("Generates gamma lookup tables for use with Pathfinder")
.arg(Arg::with_name("OUTPUT-PATH").help("The `.png` image to produce")
.required(true)
.index(1));
.arg(
Arg::with_name("OUTPUT-PATH")
.help("The `.png` image to produce")
.required(true)
.index(1),
);
let matches = app.get_matches();
let gamma_lut = GammaLut::new(CONTRAST, GAMMA, GAMMA);

View File

@ -5,4 +5,4 @@ authors = ["Patrick Walton <pcwalton@mimiga.net>"]
edition = "2018"
[dependencies]
usvg = "0.9"
usvg = "0.10"

View File

@ -49,9 +49,18 @@ fn process_node(node: &Node) {
match segment {
PathSegment::MoveTo { x, y } => println!(" path.moveTo({}, {});", x, y),
PathSegment::LineTo { x, y } => println!(" path.lineTo({}, {});", x, y),
PathSegment::CurveTo { x1, y1, x2, y2, x, y } => {
println!(" path.cubicTo({}, {}, {}, {}, {}, {});",
x1, y1, x2, y2, x, y);
PathSegment::CurveTo {
x1,
y1,
x2,
y2,
x,
y,
} => {
println!(
" path.cubicTo({}, {}, {}, {}, {}, {});",
x1, y1, x2, y2, x, y
);
}
PathSegment::ClosePath => println!(" path.close();"),
}
@ -78,10 +87,12 @@ fn process_node(node: &Node) {
fn set_color(paint: &Paint) {
if let Paint::Color(color) = *paint {
println!(" paint.setColor(0x{:x});",
((color.red as u32) << 16) |
((color.green as u32) << 8) |
((color.blue as u32) << 0) |
(0xff << 24));
println!(
" paint.setColor(0x{:x});",
((color.red as u32) << 16)
| ((color.green as u32) << 8)
| ((color.blue as u32) << 0)
| (0xff << 24)
);
}
}

View File

@ -23,8 +23,8 @@ use pathfinder_resources::embedded::EmbeddedResourceLoader;
use pathfinder_webgl::WebGlDevice;
use std::str::FromStr;
use std::sync::Arc;
use wasm_bindgen::JsCast;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use web_sys::{self, HtmlCanvasElement, WebGl2RenderingContext};
#[wasm_bindgen]
@ -45,11 +45,12 @@ struct WebCanvasState {
#[wasm_bindgen(js_name = "createContext")]
pub fn create_context(html_canvas: HtmlCanvasElement) -> PFCanvasRenderingContext2D {
let context = html_canvas.get_context("webgl2")
.unwrap()
.unwrap()
.dyn_into::<WebGl2RenderingContext>()
.unwrap();
let context = html_canvas
.get_context("webgl2")
.unwrap()
.unwrap()
.dyn_into::<WebGl2RenderingContext>()
.unwrap();
// Get the real size of the window, taking HiDPI into account.
let framebuffer_size = vec2i(html_canvas.width() as i32, html_canvas.height() as i32);
@ -89,15 +90,21 @@ impl PFCanvasRenderingContext2D {
#[wasm_bindgen(js_name = "pfFlush")]
pub fn pf_flush(&mut self) {
// Update framebuffer size.
let framebuffer_size = vec2i(self.html_canvas.width() as i32,
self.html_canvas.height() as i32);
let framebuffer_size = vec2i(
self.html_canvas.width() as i32,
self.html_canvas.height() as i32,
);
self.renderer.options_mut().dest = DestFramebuffer::full_window(framebuffer_size);
self.renderer.options_mut().background_color = None;
self.renderer.dest_framebuffer_size_changed();
// TODO(pcwalton): This is inefficient!
let mut scene = (*self.context.canvas_mut().scene()).clone();
scene.build_and_render(&mut self.renderer, BuildOptions::default(), SequentialExecutor);
scene.build_and_render(
&mut self.renderer,
BuildOptions::default(),
SequentialExecutor,
);
self.context.canvas_mut().set_size(framebuffer_size);
}
@ -116,17 +123,20 @@ impl PFCanvasRenderingContext2D {
#[wasm_bindgen(js_name = "clearRect")]
pub fn clear_rect(&mut self, x: f32, y: f32, width: f32, height: f32) {
self.context.clear_rect(RectF::new(vec2f(x, y), vec2f(width, height)));
self.context
.clear_rect(RectF::new(vec2f(x, y), vec2f(width, height)));
}
#[wasm_bindgen(js_name = "fillRect")]
pub fn fill_rect(&mut self, x: f32, y: f32, width: f32, height: f32) {
self.context.fill_rect(RectF::new(vec2f(x, y), vec2f(width, height)));
self.context
.fill_rect(RectF::new(vec2f(x, y), vec2f(width, height)));
}
#[wasm_bindgen(js_name = "strokeRect")]
pub fn stroke_rect(&mut self, x: f32, y: f32, width: f32, height: f32) {
self.context.stroke_rect(RectF::new(vec2f(x, y), vec2f(width, height)));
self.context
.stroke_rect(RectF::new(vec2f(x, y), vec2f(width, height)));
}
// TODO(pcwalton): Drawing text
@ -219,12 +229,14 @@ impl PFCanvasRenderingContext2D {
#[wasm_bindgen(js_name = "bezierCurveTo")]
pub fn bezier_curve_to(&mut self, cp1x: f32, cp1y: f32, cp2x: f32, cp2y: f32, x: f32, y: f32) {
self.default_path.bezier_curve_to(vec2f(cp1x, cp1y), vec2f(cp2x, cp2y), vec2f(x, y))
self.default_path
.bezier_curve_to(vec2f(cp1x, cp1y), vec2f(cp2x, cp2y), vec2f(x, y))
}
#[wasm_bindgen(js_name = "quadraticCurveTo")]
pub fn quadratic_curve_to(&mut self, cpx: f32, cpy: f32, x: f32, y: f32) {
self.default_path.quadratic_curve_to(vec2f(cpx, cpy), vec2f(x, y))
self.default_path
.quadratic_curve_to(vec2f(cpx, cpy), vec2f(x, y))
}
#[wasm_bindgen(js_name = "closePath")]
@ -260,9 +272,11 @@ fn parse_fill_or_stroke_style(string: &str) -> Option<FillStyle> {
Err(_) => return None,
Ok(css_color) => css_color,
};
let color = ColorU::new(css_color.r,
css_color.g,
css_color.b,
(css_color.a * 255.0).round() as u8);
let color = ColorU::new(
css_color.r,
css_color.g,
css_color.b,
(css_color.a * 255.0).round() as u8,
);
Some(FillStyle::Color(color))
}

View File

@ -13,7 +13,7 @@
#[macro_use]
extern crate log;
use js_sys::{Uint8Array, Uint16Array, Float32Array, Object};
use js_sys::{Float32Array, Object, Uint16Array, Uint8Array};
use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::vector::Vector2I;
use pathfinder_gpu::{BlendFactor, BlendOp, BufferData, BufferTarget, BufferUploadMode, ClearOps};
@ -186,12 +186,16 @@ impl WebGlDevice {
self.clear(&render_state.options.clear_ops);
}
self.context.use_program(Some(&render_state.program.gl_program));
self.context.bind_vertex_array(Some(&render_state.vertex_array.gl_vertex_array));
self.context
.use_program(Some(&render_state.program.gl_program));
self.context
.bind_vertex_array(Some(&render_state.vertex_array.gl_vertex_array));
self.bind_textures_and_images(&render_state.program,
&render_state.textures,
&render_state.images);
self.bind_textures_and_images(
&render_state.program,
&render_state.textures,
&render_state.images,
);
for (uniform, data) in render_state.uniforms {
self.set_uniform(uniform, data);
@ -200,17 +204,19 @@ impl WebGlDevice {
}
fn bind_textures_and_images(
&self,
program: &WebGlProgram,
texture_bindings: &[TextureBinding<WebGlTextureParameter, WebGlTexture>],
_: &[ImageBinding<(), WebGlTexture>]) {
&self,
program: &WebGlProgram,
texture_bindings: &[TextureBinding<WebGlTextureParameter, WebGlTexture>],
_: &[ImageBinding<(), WebGlTexture>],
) {
for &(texture_parameter, texture) in texture_bindings {
self.bind_texture(texture, texture_parameter.texture_unit);
}
let parameters = program.parameters.borrow();
for (texture_unit, uniform) in parameters.textures.iter().enumerate() {
self.context.uniform1i(uniform.location.as_ref(), texture_unit as i32);
self.context
.uniform1i(uniform.location.as_ref(), texture_unit as i32);
self.ck();
}
}
@ -486,9 +492,7 @@ impl Device for WebGlDevice {
size: Vector2I,
data_ref: TextureDataRef,
) -> WebGlTexture {
let data = unsafe {
check_and_extract_data(data_ref, size, format)
};
let data = unsafe { check_and_extract_data(data_ref, size, format) };
let texture = self.context.create_texture().unwrap();
let texture = WebGlTexture {
@ -568,7 +572,10 @@ impl Device for WebGlDevice {
.create_program()
.expect("unable to create program object");
match shaders {
ProgramKind::Raster { ref vertex, ref fragment } => {
ProgramKind::Raster {
ref vertex,
ref fragment,
} => {
self.context.attach_shader(&gl_program, &vertex.gl_shader);
self.context.attach_shader(&gl_program, &fragment.gl_shader);
}
@ -622,7 +629,9 @@ impl Device for WebGlDevice {
fn get_uniform(&self, program: &WebGlProgram, name: &str) -> WebGlUniform {
let name = format!("u{}", name);
let location = self.context.get_uniform_location(&program.gl_program, &name);
let location = self
.context
.get_uniform_location(&program.gl_program, &name);
self.ck();
WebGlUniform { location: location }
}
@ -638,7 +647,10 @@ impl Device for WebGlDevice {
index
}
};
WebGlTextureParameter { uniform, texture_unit: index as u32 }
WebGlTextureParameter {
uniform,
texture_unit: index as u32,
}
}
fn get_image_parameter(&self, _: &WebGlProgram, _: &str) {
@ -744,12 +756,7 @@ impl Device for WebGlDevice {
}
}
fn allocate_buffer<T>(
&self,
buffer: &WebGlBuffer,
data: BufferData<T>,
target: BufferTarget,
) {
fn allocate_buffer<T>(&self, buffer: &WebGlBuffer, data: BufferData<T>, target: BufferTarget) {
let target = match target {
BufferTarget::Vertex => WebGl::ARRAY_BUFFER,
BufferTarget::Index => WebGl::ELEMENT_ARRAY_BUFFER,
@ -770,16 +777,22 @@ impl Device for WebGlDevice {
}
}
fn upload_to_buffer<T>(&self,
buffer: &Self::Buffer,
position: usize,
data: &[T],
target: BufferTarget) {
fn upload_to_buffer<T>(
&self,
buffer: &Self::Buffer,
position: usize,
data: &[T],
target: BufferTarget,
) {
let target = target.to_gl_target();
self.context.bind_buffer(target, Some(&buffer.buffer)); self.ck();
self.context.buffer_sub_data_with_i32_and_u8_array(target,
position as i32,
slice_to_u8(data)); self.ck();
self.context.bind_buffer(target, Some(&buffer.buffer));
self.ck();
self.context.buffer_sub_data_with_i32_and_u8_array(
target,
position as i32,
slice_to_u8(data),
);
self.ck();
}
#[inline]
@ -794,44 +807,46 @@ impl Device for WebGlDevice {
fn set_texture_sampling_mode(&self, texture: &Self::Texture, flags: TextureSamplingFlags) {
self.bind_texture(texture, 0);
self.context
.tex_parameteri(WebGl::TEXTURE_2D,
WebGl::TEXTURE_MIN_FILTER,
if flags.contains(TextureSamplingFlags::NEAREST_MIN) {
WebGl::NEAREST as i32
} else {
WebGl::LINEAR as i32
});
self.context
.tex_parameteri(WebGl::TEXTURE_2D,
WebGl::TEXTURE_MAG_FILTER,
if flags.contains(TextureSamplingFlags::NEAREST_MAG) {
WebGl::NEAREST as i32
} else {
WebGl::LINEAR as i32
});
self.context
.tex_parameteri(WebGl::TEXTURE_2D,
WebGl::TEXTURE_WRAP_S,
if flags.contains(TextureSamplingFlags::REPEAT_U) {
WebGl::REPEAT as i32
} else {
WebGl::CLAMP_TO_EDGE as i32
});
self.context
.tex_parameteri(WebGl::TEXTURE_2D,
WebGl::TEXTURE_WRAP_T,
if flags.contains(TextureSamplingFlags::REPEAT_V) {
WebGl::REPEAT as i32
} else {
WebGl::CLAMP_TO_EDGE as i32
});
self.context.tex_parameteri(
WebGl::TEXTURE_2D,
WebGl::TEXTURE_MIN_FILTER,
if flags.contains(TextureSamplingFlags::NEAREST_MIN) {
WebGl::NEAREST as i32
} else {
WebGl::LINEAR as i32
},
);
self.context.tex_parameteri(
WebGl::TEXTURE_2D,
WebGl::TEXTURE_MAG_FILTER,
if flags.contains(TextureSamplingFlags::NEAREST_MAG) {
WebGl::NEAREST as i32
} else {
WebGl::LINEAR as i32
},
);
self.context.tex_parameteri(
WebGl::TEXTURE_2D,
WebGl::TEXTURE_WRAP_S,
if flags.contains(TextureSamplingFlags::REPEAT_U) {
WebGl::REPEAT as i32
} else {
WebGl::CLAMP_TO_EDGE as i32
},
);
self.context.tex_parameteri(
WebGl::TEXTURE_2D,
WebGl::TEXTURE_WRAP_T,
if flags.contains(TextureSamplingFlags::REPEAT_V) {
WebGl::REPEAT as i32
} else {
WebGl::CLAMP_TO_EDGE as i32
},
);
}
fn upload_to_texture(&self, texture: &WebGlTexture, rect: RectI, data_ref: TextureDataRef) {
let data = unsafe {
check_and_extract_data(data_ref, rect.size(), texture.format)
};
let data = unsafe { check_and_extract_data(data_ref, rect.size(), texture.format) };
assert!(rect.size().x() >= 0);
assert!(rect.size().y() >= 0);
assert!(rect.max_x() <= texture.size.x());