Remove `draw_render_target()` in favor of the standard tile-based rendering

path
This commit is contained in:
Patrick Walton 2020-04-02 22:03:42 -07:00
parent cd37791d72
commit eb2b622615
12 changed files with 322 additions and 278 deletions

View File

@ -12,18 +12,18 @@
use pathfinder_color::ColorU; use pathfinder_color::ColorU;
use pathfinder_content::dash::OutlineDash; use pathfinder_content::dash::OutlineDash;
use pathfinder_content::effects::{BlendMode, BlurDirection, Effects, Filter}; use pathfinder_content::effects::{BlendMode, BlurDirection, PatternFilter};
use pathfinder_content::fill::FillRule; use pathfinder_content::fill::FillRule;
use pathfinder_content::gradient::Gradient; use pathfinder_content::gradient::Gradient;
use pathfinder_content::outline::{ArcDirection, Contour, Outline}; use pathfinder_content::outline::{ArcDirection, Contour, Outline};
use pathfinder_content::pattern::{Pattern, PatternFlags, PatternSource}; use pathfinder_content::pattern::Pattern;
use pathfinder_content::render_target::RenderTargetId; use pathfinder_content::render_target::RenderTargetId;
use pathfinder_content::stroke::{LineCap, LineJoin as StrokeLineJoin}; use pathfinder_content::stroke::{LineCap, LineJoin as StrokeLineJoin};
use pathfinder_content::stroke::{OutlineStrokeToFill, StrokeStyle}; use pathfinder_content::stroke::{OutlineStrokeToFill, StrokeStyle};
use pathfinder_geometry::line_segment::LineSegment2F; use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::rect::RectF; use pathfinder_geometry::rect::{RectF, RectI};
use pathfinder_geometry::transform2d::Transform2F; use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::vector::{IntoVector2F, Vector2F, vec2f}; use pathfinder_geometry::vector::{IntoVector2F, Vector2F, Vector2I, vec2f, vec2i};
use pathfinder_renderer::paint::{Paint, PaintId}; use pathfinder_renderer::paint::{Paint, PaintId};
use pathfinder_renderer::scene::{ClipPath, ClipPathId, DrawPath, RenderTarget, Scene}; use pathfinder_renderer::scene::{ClipPath, ClipPathId, DrawPath, RenderTarget, Scene};
use std::borrow::Cow; use std::borrow::Cow;
@ -101,6 +101,11 @@ impl Canvas {
font_context, font_context,
} }
} }
#[inline]
pub fn size(&self) -> Vector2I {
self.scene.view_box().size().ceil().to_i32()
}
} }
pub struct CanvasRenderingContext2D { pub struct CanvasRenderingContext2D {
@ -340,7 +345,7 @@ impl CanvasRenderingContext2D {
return None; return None;
} }
let render_target_size = self.canvas.scene.view_box().size().ceil().to_i32(); let render_target_size = self.canvas.size();
let render_target_a = RenderTarget::new(render_target_size, String::new()); let render_target_a = RenderTarget::new(render_target_size, String::new());
let render_target_id_a = self.canvas.scene.push_render_target(render_target_a); let render_target_id_a = self.canvas.scene.push_render_target(render_target_a);
let render_target_b = RenderTarget::new(render_target_size, String::new()); let render_target_b = RenderTarget::new(render_target_size, String::new());
@ -357,16 +362,24 @@ impl CanvasRenderingContext2D {
}; };
let sigma = self.current_state.shadow_blur * 0.5; let sigma = self.current_state.shadow_blur * 0.5;
let mut paint_x = Pattern::from_render_target(render_target_ids[1], self.canvas.size());
let mut paint_y = Pattern::from_render_target(render_target_ids[0], self.canvas.size());
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 = self.canvas.scene.push_paint(&Paint::Pattern(paint_x));
let paint_id_y = self.canvas.scene.push_paint(&Paint::Pattern(paint_y));
// TODO(pcwalton): Apply clip as necessary.
let outline = Outline::from_rect(RectI::new(vec2i(0, 0), self.canvas.size()).to_f32());
let path_x = DrawPath::new(outline.clone(), paint_id_x);
let path_y = DrawPath::new(outline.clone(), paint_id_y);
self.canvas.scene.pop_render_target(); self.canvas.scene.pop_render_target();
self.canvas.scene.draw_render_target(render_target_ids[1], Effects::new(Filter::Blur { self.canvas.scene.push_path(path_x);
direction: BlurDirection::X,
sigma,
}));
self.canvas.scene.pop_render_target(); self.canvas.scene.pop_render_target();
self.canvas.scene.draw_render_target(render_target_ids[0], Effects::new(Filter::Blur { self.canvas.scene.push_path(path_y);
direction: BlurDirection::Y,
sigma,
}));
} }
// Transformations // Transformations
@ -487,17 +500,16 @@ impl CanvasRenderingContext2D {
pub fn create_pattern_from_canvas(&mut self, canvas: Canvas, transform: Transform2F) pub fn create_pattern_from_canvas(&mut self, canvas: Canvas, transform: Transform2F)
-> Pattern { -> Pattern {
let subscene_size = canvas.size();
let subscene = canvas.into_scene(); let subscene = canvas.into_scene();
let subscene_size = subscene.view_box().size().ceil().to_i32();
let render_target = RenderTarget::new(subscene_size, String::new()); let render_target = RenderTarget::new(subscene_size, String::new());
let render_target_id = self.canvas.scene.push_render_target(render_target); let render_target_id = self.canvas.scene.push_render_target(render_target);
self.canvas.scene.append_scene(subscene); self.canvas.scene.append_scene(subscene);
self.canvas.scene.pop_render_target(); self.canvas.scene.pop_render_target();
let pattern_source = PatternSource::RenderTarget {
id: render_target_id, let mut pattern = Pattern::from_render_target(render_target_id, subscene_size);
size: subscene_size, pattern.apply_transform(transform);
}; pattern
Pattern::new(pattern_source, transform, PatternFlags::empty())
} }
} }
@ -557,8 +569,7 @@ impl State {
let mut must_copy = !self.transform.is_identity(); let mut must_copy = !self.transform.is_identity();
if !must_copy { if !must_copy {
if let Paint::Pattern(ref pattern) = *paint { if let Paint::Pattern(ref pattern) = *paint {
must_copy = !self.image_smoothing_enabled != must_copy = self.image_smoothing_enabled != pattern.smoothing_enabled()
pattern.flags.contains(PatternFlags::NO_SMOOTHING);
} }
} }
@ -569,7 +580,7 @@ impl State {
let mut paint = (*paint).clone(); let mut paint = (*paint).clone();
paint.apply_transform(&self.transform); paint.apply_transform(&self.transform);
if let Paint::Pattern(ref mut pattern) = paint { if let Paint::Pattern(ref mut pattern) = paint {
pattern.flags.set(PatternFlags::NO_SMOOTHING, !self.image_smoothing_enabled); pattern.set_smoothing_enabled(self.image_smoothing_enabled);
} }
Cow::Owned(paint) Cow::Owned(paint)
} }
@ -833,7 +844,7 @@ pub trait CanvasImageDestLocation {
impl CanvasImageSource for Pattern { impl CanvasImageSource for Pattern {
#[inline] #[inline]
fn to_pattern(mut self, _: &mut CanvasRenderingContext2D, transform: Transform2F) -> Pattern { fn to_pattern(mut self, _: &mut CanvasRenderingContext2D, transform: Transform2F) -> Pattern {
self.transform(transform); self.apply_transform(transform);
self self
} }
} }

View File

@ -31,13 +31,6 @@ pub const MAX_STEM_DARKENING_AMOUNT: [f32; 2] = [0.3, 0.3];
/// This value is a subjective cutoff. Above this ppem value, no stem darkening is performed. /// This value is a subjective cutoff. Above this ppem value, no stem darkening is performed.
pub const MAX_STEM_DARKENING_PIXELS_PER_EM: f32 = 72.0; pub const MAX_STEM_DARKENING_PIXELS_PER_EM: f32 = 72.0;
/// Effects that can be applied to a layer.
#[derive(Clone, Copy, Debug, PartialEq, Default)]
pub struct Effects {
/// The shader that should be used when compositing this layer onto its destination.
pub filter: Filter,
}
/// The shader that should be used when compositing this layer onto its destination. /// The shader that should be used when compositing this layer onto its destination.
#[derive(Clone, Copy, PartialEq, Debug)] #[derive(Clone, Copy, PartialEq, Debug)]
pub enum Filter { pub enum Filter {
@ -54,6 +47,12 @@ pub enum Filter {
uv_origin: Vector2F, uv_origin: Vector2F,
}, },
PatternFilter(PatternFilter),
}
/// Shaders applicable to patterns.
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum PatternFilter {
/// Performs postprocessing operations useful for monochrome text. /// Performs postprocessing operations useful for monochrome text.
Text { Text {
/// The foreground color of the text. /// The foreground color of the text.
@ -136,13 +135,6 @@ impl Default for Filter {
} }
} }
impl Effects {
#[inline]
pub fn new(filter: Filter) -> Effects {
Effects { filter }
}
}
impl BlendMode { impl BlendMode {
/// Whether the backdrop is irrelevant when applying this blend mode (i.e. destination blend /// Whether the backdrop is irrelevant when applying this blend mode (i.e. destination blend
/// factor is zero when source alpha is one). /// factor is zero when source alpha is one).

View File

@ -110,6 +110,13 @@ impl Outline {
outline outline
} }
#[inline]
pub fn from_rect(rect: RectF) -> Outline {
let mut outline = Outline::new();
outline.push_contour(Contour::from_rect(rect));
outline
}
#[inline] #[inline]
pub fn bounds(&self) -> RectF { pub fn bounds(&self) -> RectF {
self.bounds self.bounds
@ -266,6 +273,18 @@ impl Contour {
} }
} }
#[inline]
pub fn from_rect(rect: RectF) -> Contour {
let mut contour = Contour::new();
contour.push_point(rect.origin(), PointFlags::empty(), false);
contour.push_point(rect.upper_right(), PointFlags::empty(), false);
contour.push_point(rect.lower_right(), PointFlags::empty(), false);
contour.push_point(rect.lower_left(), PointFlags::empty(), false);
contour.close();
contour.bounds = rect;
contour
}
// Replaces this contour with a new one, with arrays preallocated to match `self`. // Replaces this contour with a new one, with arrays preallocated to match `self`.
#[inline] #[inline]
pub(crate) fn take(&mut self) -> Contour { pub(crate) fn take(&mut self) -> Contour {

View File

@ -10,6 +10,7 @@
//! Raster image patterns. //! Raster image patterns.
use crate::effects::PatternFilter;
use crate::render_target::RenderTargetId; use crate::render_target::RenderTargetId;
use crate::util; use crate::util;
use pathfinder_color::{self as color, ColorU}; use pathfinder_color::{self as color, ColorU};
@ -26,9 +27,10 @@ use image::RgbaImage;
/// A raster image pattern. /// A raster image pattern.
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, PartialEq, Debug)]
pub struct Pattern { pub struct Pattern {
pub source: PatternSource, source: PatternSource,
pub transform: Transform2F, transform: Transform2F,
pub flags: PatternFlags, filter: Option<PatternFilter>,
flags: PatternFlags,
} }
#[derive(Clone, PartialEq, Eq, Hash, Debug)] #[derive(Clone, PartialEq, Eq, Hash, Debug)]
@ -61,13 +63,33 @@ bitflags! {
impl Pattern { impl Pattern {
#[inline] #[inline]
pub fn new(source: PatternSource, transform: Transform2F, flags: PatternFlags) -> Pattern { fn from_source(source: PatternSource) -> Pattern {
Pattern { source, transform, flags } Pattern {
source,
transform: Transform2F::default(),
filter: None,
flags: PatternFlags::empty(),
}
} }
#[inline] #[inline]
pub fn transform(&mut self, transform: Transform2F) { pub fn from_image(image: Image) -> Pattern {
self.transform *= transform Pattern::from_source(PatternSource::Image(image))
}
#[inline]
pub fn from_render_target(id: RenderTargetId, size: Vector2I) -> Pattern {
Pattern::from_source(PatternSource::RenderTarget { id, size })
}
#[inline]
pub fn transform(&self) -> Transform2F {
self.transform
}
#[inline]
pub fn apply_transform(&mut self, transform: Transform2F) {
self.transform = transform * self.transform;
} }
#[inline] #[inline]
@ -77,6 +99,56 @@ impl Pattern {
PatternSource::RenderTarget { size, .. } => size, PatternSource::RenderTarget { size, .. } => size,
} }
} }
#[inline]
pub fn filter(&self) -> Option<PatternFilter> {
self.filter
}
#[inline]
pub fn set_filter(&mut self, filter: Option<PatternFilter>) {
self.filter = filter;
}
#[inline]
pub fn repeat_x(&self) -> bool {
self.flags.contains(PatternFlags::REPEAT_X)
}
#[inline]
pub fn set_repeat_x(&mut self, repeat_x: bool) {
self.flags.set(PatternFlags::REPEAT_X, repeat_x);
}
#[inline]
pub fn repeat_y(&self) -> bool {
self.flags.contains(PatternFlags::REPEAT_Y)
}
#[inline]
pub fn set_repeat_y(&mut self, repeat_y: bool) {
self.flags.set(PatternFlags::REPEAT_Y, repeat_y);
}
#[inline]
pub fn smoothing_enabled(&self) -> bool {
!self.flags.contains(PatternFlags::NO_SMOOTHING)
}
#[inline]
pub fn set_smoothing_enabled(&mut self, enable: bool) {
self.flags.set(PatternFlags::NO_SMOOTHING, !enable);
}
#[inline]
pub fn is_opaque(&self) -> bool {
self.source.is_opaque()
}
#[inline]
pub fn source(&self) -> &PatternSource {
&self.source
}
} }
impl Image { impl Image {

View File

@ -22,10 +22,14 @@ use crate::device::{GroundProgram, GroundVertexArray};
use crate::ui::{DemoUIModel, DemoUIPresenter, ScreenshotInfo, ScreenshotType, UIAction}; use crate::ui::{DemoUIModel, DemoUIPresenter, ScreenshotInfo, ScreenshotType, UIAction};
use crate::window::{Event, Keycode, SVGPath, Window, WindowSize}; use crate::window::{Event, Keycode, SVGPath, Window, WindowSize};
use clap::{App, Arg}; use clap::{App, Arg};
use pathfinder_content::effects::{DEFRINGING_KERNEL_CORE_GRAPHICS, Effects}; use pathfinder_content::effects::DEFRINGING_KERNEL_CORE_GRAPHICS;
use pathfinder_content::effects::{Filter, STEM_DARKENING_FACTORS}; use pathfinder_content::effects::PatternFilter;
use pathfinder_content::effects::STEM_DARKENING_FACTORS;
use pathfinder_content::outline::Outline;
use pathfinder_content::pattern::Pattern;
use pathfinder_content::render_target::RenderTargetId;
use pathfinder_export::{Export, FileFormat}; use pathfinder_export::{Export, FileFormat};
use pathfinder_geometry::rect::RectF; use pathfinder_geometry::rect::{RectF, RectI};
use pathfinder_geometry::transform2d::Transform2F; use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::transform3d::Transform4F; use pathfinder_geometry::transform3d::Transform4F;
use pathfinder_geometry::vector::{Vector2F, Vector2I, Vector4F, vec2f, vec2i}; use pathfinder_geometry::vector::{Vector2F, Vector2I, Vector4F, vec2f, vec2i};
@ -34,7 +38,8 @@ use pathfinder_renderer::concurrent::scene_proxy::{RenderCommandStream, ScenePro
use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererOptions}; use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererOptions};
use pathfinder_renderer::gpu::renderer::{RenderStats, RenderTime, Renderer}; use pathfinder_renderer::gpu::renderer::{RenderStats, RenderTime, Renderer};
use pathfinder_renderer::options::{BuildOptions, RenderTransform}; use pathfinder_renderer::options::{BuildOptions, RenderTransform};
use pathfinder_renderer::scene::{RenderTarget, Scene}; use pathfinder_renderer::paint::Paint;
use pathfinder_renderer::scene::{DrawPath, RenderTarget, Scene};
use pathfinder_resources::ResourceLoader; use pathfinder_resources::ResourceLoader;
use pathfinder_svg::BuiltSVG; use pathfinder_svg::BuiltSVG;
use pathfinder_ui::{MousePosition, UIEvent}; use pathfinder_ui::{MousePosition, UIEvent};
@ -136,13 +141,13 @@ impl<W> DemoApp<W> where W: Window {
let mut ui_model = DemoUIModel::new(&options); let mut ui_model = DemoUIModel::new(&options);
let render_options = RendererOptions { background_color: None }; let render_options = RendererOptions { background_color: None };
let effects = build_effects(&ui_model); let filter = build_filter(&ui_model);
let viewport = window.viewport(options.mode.view(0)); let viewport = window.viewport(options.mode.view(0));
let (mut built_svg, svg_tree) = load_scene(resources, let (mut built_svg, svg_tree) = load_scene(resources,
&options.input_path, &options.input_path,
viewport.size(), viewport.size(),
effects); filter);
let message = get_svg_building_message(&built_svg); let message = get_svg_building_message(&built_svg);
@ -420,11 +425,11 @@ impl<W> DemoApp<W> where W: Window {
Event::OpenSVG(ref svg_path) => { Event::OpenSVG(ref svg_path) => {
let viewport = self.window.viewport(self.ui_model.mode.view(0)); let viewport = self.window.viewport(self.ui_model.mode.view(0));
let effects = build_effects(&self.ui_model); let filter = build_filter(&self.ui_model);
let (mut built_svg, svg_tree) = load_scene(self.window.resource_loader(), let (mut built_svg, svg_tree) = load_scene(self.window.resource_loader(),
svg_path, svg_path,
viewport.size(), viewport.size(),
effects); filter);
self.ui_model.message = get_svg_building_message(&built_svg); self.ui_model.message = get_svg_building_message(&built_svg);
@ -578,8 +583,8 @@ impl<W> DemoApp<W> where W: Window {
UIAction::ModelChanged => self.dirty = true, UIAction::ModelChanged => self.dirty = true,
UIAction::EffectsChanged => { UIAction::EffectsChanged => {
let viewport_size = self.window.viewport(self.ui_model.mode.view(0)).size(); let viewport_size = self.window.viewport(self.ui_model.mode.view(0)).size();
let effects = build_effects(&self.ui_model); let filter = build_filter(&self.ui_model);
let mut built_svg = build_svg_tree(&self.svg_tree, viewport_size, effects); let mut built_svg = build_svg_tree(&self.svg_tree, viewport_size, filter);
self.scene_metadata = self.scene_metadata =
SceneMetadata::new_clipping_view_box(&mut built_svg.scene, viewport_size); SceneMetadata::new_clipping_view_box(&mut built_svg.scene, viewport_size);
self.scene_proxy.replace_scene(built_svg.scene); self.scene_proxy.replace_scene(built_svg.scene);
@ -737,7 +742,7 @@ pub enum UIVisibility {
fn load_scene(resource_loader: &dyn ResourceLoader, fn load_scene(resource_loader: &dyn ResourceLoader,
input_path: &SVGPath, input_path: &SVGPath,
viewport_size: Vector2I, viewport_size: Vector2I,
effects: Option<Effects>) filter: Option<PatternFilter>)
-> (BuiltSVG, Tree) { -> (BuiltSVG, Tree) {
let mut data; let mut data;
match *input_path { match *input_path {
@ -750,33 +755,47 @@ fn load_scene(resource_loader: &dyn ResourceLoader,
}; };
let tree = Tree::from_data(&data, &UsvgOptions::default()).expect("Failed to parse the SVG!"); let tree = Tree::from_data(&data, &UsvgOptions::default()).expect("Failed to parse the SVG!");
let built_svg = build_svg_tree(&tree, viewport_size, effects); let built_svg = build_svg_tree(&tree, viewport_size, filter);
(built_svg, tree) (built_svg, tree)
} }
fn build_svg_tree(tree: &Tree, viewport_size: Vector2I, effects: Option<Effects>) -> BuiltSVG { // FIXME(pcwalton): Rework how transforms work in the demo. The transform affects the final
// composite steps, breaking this approach.
fn build_svg_tree(tree: &Tree, viewport_size: Vector2I, filter: Option<PatternFilter>)
-> BuiltSVG {
let mut scene = Scene::new(); let mut scene = Scene::new();
let filter_info = filter.map(|filter| {
let render_target_id = match effects { let scale = match filter {
None => None, PatternFilter::Text { defringing_kernel: Some(_), .. } => vec2i(3, 1),
Some(effects) => { _ => vec2i(1, 1),
let scale = match effects.filter { };
Filter::Text { defringing_kernel: Some(_), .. } => vec2i(3, 1), let name = "Text".to_owned();
_ => vec2i(1, 1), let render_target_size = viewport_size * scale;
}; let render_target = RenderTarget::new(render_target_size, name);
let name = "Text".to_owned(); let render_target_id = scene.push_render_target(render_target);
let render_target = RenderTarget::new(viewport_size * scale, name); FilterInfo { filter, render_target_id, render_target_size }
Some(scene.push_render_target(render_target)) });
}
};
let mut built_svg = BuiltSVG::from_tree_and_scene(&tree, scene); let mut built_svg = BuiltSVG::from_tree_and_scene(&tree, scene);
if let Some(FilterInfo { filter, render_target_id, render_target_size }) = filter_info {
let mut paint = Pattern::from_render_target(render_target_id, render_target_size);
paint.set_filter(Some(filter));
let paint_id = built_svg.scene.push_paint(&Paint::Pattern(paint));
let outline = Outline::from_rect(RectI::new(vec2i(0, 0), viewport_size).to_f32());
let path = DrawPath::new(outline, paint_id);
if let (Some(render_target_id), Some(effects)) = (render_target_id, effects) {
built_svg.scene.pop_render_target(); built_svg.scene.pop_render_target();
built_svg.scene.draw_render_target(render_target_id, effects); built_svg.scene.push_path(path);
}
return built_svg;
struct FilterInfo {
filter: PatternFilter,
render_target_id: RenderTargetId,
render_target_size: Vector2I,
} }
built_svg
} }
fn center_of_window(window_size: &WindowSize) -> Vector2F { fn center_of_window(window_size: &WindowSize) -> Vector2F {
@ -863,22 +882,20 @@ impl SceneMetadata {
} }
} }
fn build_effects(ui_model: &DemoUIModel) -> Option<Effects> { fn build_filter(ui_model: &DemoUIModel) -> Option<PatternFilter> {
if !ui_model.gamma_correction_effect_enabled && !ui_model.subpixel_aa_effect_enabled { if !ui_model.gamma_correction_effect_enabled && !ui_model.subpixel_aa_effect_enabled {
return None; return None;
} }
Some(Effects { Some(PatternFilter::Text {
filter: Filter::Text { fg_color: ui_model.foreground_color().to_f32(),
fg_color: ui_model.foreground_color().to_f32(), bg_color: ui_model.background_color().to_f32(),
bg_color: ui_model.background_color().to_f32(), gamma_correction: ui_model.gamma_correction_effect_enabled,
gamma_correction: ui_model.gamma_correction_effect_enabled, defringing_kernel: if ui_model.subpixel_aa_effect_enabled {
defringing_kernel: if ui_model.subpixel_aa_effect_enabled { // TODO(pcwalton): Select FreeType defringing kernel as necessary.
// TODO(pcwalton): Select FreeType defringing kernel as necessary. Some(DEFRINGING_KERNEL_CORE_GRAPHICS)
Some(DEFRINGING_KERNEL_CORE_GRAPHICS) } else {
} else { None
None }
}
},
}) })
} }

View File

@ -18,7 +18,7 @@ use pathfinder_color::{ColorF, ColorU, rgbau, rgbf, rgbu};
use pathfinder_content::fill::FillRule; use pathfinder_content::fill::FillRule;
use pathfinder_content::gradient::Gradient; use pathfinder_content::gradient::Gradient;
use pathfinder_content::outline::ArcDirection; use pathfinder_content::outline::ArcDirection;
use pathfinder_content::pattern::{Image, Pattern, PatternFlags, PatternSource}; use pathfinder_content::pattern::{Image, Pattern};
use pathfinder_content::stroke::LineCap; use pathfinder_content::stroke::LineCap;
use pathfinder_geometry::angle; use pathfinder_geometry::angle;
use pathfinder_geometry::line_segment::LineSegment2F; use pathfinder_geometry::line_segment::LineSegment2F;
@ -537,21 +537,11 @@ fn draw_window(context: &mut CanvasRenderingContext2D, title: &str, rect: RectF)
// Draw window with shadow. // Draw window with shadow.
context.set_fill_style(rgbau(28, 30, 34, 192)); context.set_fill_style(rgbau(28, 30, 34, 192));
context.set_shadow_blur(10.0);
context.set_shadow_offset(vec2f(0.0, 2.0));
context.set_shadow_color(rgbau(0, 0, 0, 128));
context.fill_path(create_rounded_rect_path(rect, CORNER_RADIUS), FillRule::Winding); context.fill_path(create_rounded_rect_path(rect, CORNER_RADIUS), FillRule::Winding);
context.set_shadow_color(rgbau(0, 0, 0, 0));
// Draw window drop shadow.
let mut path =
create_rounded_rect_path(RectF::new(rect.origin() - 10.0, rect.size() + vec2f(20.0, 30.0)),
CORNER_RADIUS);
path.rect(rect);
fill_path_with_box_gradient(context,
path,
FillRule::EvenOdd,
rect + vec2f(0.0, 2.0),
CORNER_RADIUS * 2.0,
10.0,
rgbau(0, 0, 0, 128),
rgbau(0, 0, 0, 0));
// Header. // Header.
let mut header_gradient = let mut header_gradient =
@ -585,21 +575,22 @@ fn draw_window(context: &mut CanvasRenderingContext2D, title: &str, rect: RectF)
fn draw_search_box(context: &mut CanvasRenderingContext2D, text: &str, rect: RectF) { fn draw_search_box(context: &mut CanvasRenderingContext2D, text: &str, rect: RectF) {
let corner_radius = rect.height() * 0.5 - 1.0; let corner_radius = rect.height() * 0.5 - 1.0;
fill_path_with_box_gradient(context, let path = create_rounded_rect_path(rect, corner_radius);
create_rounded_rect_path(rect, corner_radius), context.save();
FillRule::Winding, context.clip_path(path.clone(), FillRule::Winding);
rect + vec2f(0.0, 1.5), context.set_shadow_offset(vec2f(0.0, 1.5));
rect.height() * 0.5, context.set_shadow_blur(5.0);
5.0, context.set_shadow_color(rgbau(0, 0, 0, 92));
rgbau(0, 0, 0, 16), context.set_fill_style(rgbau(0, 0, 0, 16));
rgbau(0, 0, 0, 92)); context.fill_path(path, FillRule::Winding);
context.restore();
context.set_font_size(rect.height() * 0.5); context.set_font_size(rect.height() * 0.5);
context.set_font(FONT_NAME_EMOJI); context.set_font(FONT_NAME_EMOJI);
context.set_fill_style(rgbau(255, 255, 255, 64)); context.set_fill_style(rgbau(255, 255, 255, 64));
context.set_text_align(TextAlign::Center); context.set_text_align(TextAlign::Center);
context.set_text_baseline(TextBaseline::Middle); context.set_text_baseline(TextBaseline::Middle);
context.fill_text("🔍", rect.origin() + (rect.height() * 0.55)); context.fill_text("🔍", rect.origin() + Vector2F::splat(rect.height() * 0.55));
context.set_font(FONT_NAME_REGULAR); context.set_font(FONT_NAME_REGULAR);
context.set_font_size(17.0); context.set_font_size(17.0);
@ -908,9 +899,8 @@ fn draw_thumbnails(context: &mut CanvasRenderingContext2D,
(image_index % IMAGES_ACROSS) as i32, (image_index % IMAGES_ACROSS) as i32,
(image_index / IMAGES_ACROSS) as i32).to_f32() * THUMB_HEIGHT) * (image_index / IMAGES_ACROSS) as i32).to_f32() * THUMB_HEIGHT) *
Transform2F::from_scale(0.5); Transform2F::from_scale(0.5);
let pattern = Pattern::new(PatternSource::Image((*image).clone()), let mut pattern = Pattern::from_image((*image).clone());
pattern_transform, pattern.apply_transform(pattern_transform);
PatternFlags::empty());
context.set_fill_style(pattern); context.set_fill_style(pattern);
context.set_global_alpha(alpha); context.set_global_alpha(alpha);
context.fill_path(image_path, FillRule::Winding); context.fill_path(image_path, FillRule::Winding);

View File

@ -15,13 +15,13 @@ use crate::gpu::renderer::{BlendModeExt, MASK_TILES_ACROSS, MASK_TILES_DOWN};
use crate::gpu_data::{FillBatchPrimitive, RenderCommand, TexturePageId, Tile, TileBatch}; use crate::gpu_data::{FillBatchPrimitive, RenderCommand, TexturePageId, Tile, TileBatch};
use crate::gpu_data::{TileBatchTexture, TileObjectPrimitive, TileVertex}; use crate::gpu_data::{TileBatchTexture, TileObjectPrimitive, TileVertex};
use crate::options::{PreparedBuildOptions, PreparedRenderTransform, RenderCommandListener}; use crate::options::{PreparedBuildOptions, PreparedRenderTransform, RenderCommandListener};
use crate::paint::{PaintInfo, PaintMetadata, RenderTargetMetadata}; use crate::paint::{PaintInfo, PaintMetadata};
use crate::scene::{DisplayItem, Scene}; use crate::scene::{DisplayItem, Scene};
use crate::tile_map::DenseTileMap; use crate::tile_map::DenseTileMap;
use crate::tiles::{self, DrawTilingPathInfo, PackedTile, TILE_HEIGHT, TILE_WIDTH}; use crate::tiles::{self, DrawTilingPathInfo, PackedTile, TILE_HEIGHT, TILE_WIDTH};
use crate::tiles::{Tiler, TilingPathInfo}; use crate::tiles::{Tiler, TilingPathInfo};
use crate::z_buffer::{DepthMetadata, ZBuffer}; use crate::z_buffer::{DepthMetadata, ZBuffer};
use pathfinder_content::effects::{BlendMode, Effects}; use pathfinder_content::effects::{BlendMode, Filter};
use pathfinder_content::fill::FillRule; use pathfinder_content::fill::FillRule;
use pathfinder_content::render_target::RenderTargetId; use pathfinder_content::render_target::RenderTargetId;
use pathfinder_geometry::line_segment::{LineSegment2F, LineSegmentU4, LineSegmentU8}; use pathfinder_geometry::line_segment::{LineSegment2F, LineSegmentU4, LineSegmentU8};
@ -38,9 +38,7 @@ use std::u16;
pub(crate) struct SceneBuilder<'a> { pub(crate) struct SceneBuilder<'a> {
scene: &'a Scene, scene: &'a Scene,
built_options: &'a PreparedBuildOptions, built_options: &'a PreparedBuildOptions,
next_alpha_tile_index: AtomicUsize, next_alpha_tile_index: AtomicUsize,
pub(crate) listener: Box<dyn RenderCommandListener>, pub(crate) listener: Box<dyn RenderCommandListener>,
} }
@ -55,7 +53,7 @@ pub(crate) struct ObjectBuilder {
struct BuiltDrawPath { struct BuiltDrawPath {
path: BuiltPath, path: BuiltPath,
blend_mode: BlendMode, blend_mode: BlendMode,
effects: Effects, filter: Filter,
color_texture_page_0: TexturePageId, color_texture_page_0: TexturePageId,
color_texture_page_1: TexturePageId, color_texture_page_1: TexturePageId,
sampling_flags_0: TextureSamplingFlags, sampling_flags_0: TextureSamplingFlags,
@ -126,9 +124,9 @@ impl<'a> SceneBuilder<'a> {
let PaintInfo { let PaintInfo {
render_commands, render_commands,
paint_metadata, paint_metadata,
render_target_metadata,
opacity_tile_page, opacity_tile_page,
opacity_tile_transform, opacity_tile_transform,
render_target_metadata: _
} = self.scene.build_paint_info(render_transform); } = self.scene.build_paint_info(render_transform);
for render_command in render_commands { for render_command in render_commands {
self.listener.send(render_command); self.listener.send(render_command);
@ -160,7 +158,7 @@ impl<'a> SceneBuilder<'a> {
}) })
}); });
self.finish_building(&paint_metadata, &render_target_metadata, built_draw_paths); self.finish_building(&paint_metadata, built_draw_paths);
let build_time = Instant::now() - start_time; let build_time = Instant::now() - start_time;
self.listener.send(RenderCommand::Finish { build_time }); self.listener.send(RenderCommand::Finish { build_time });
@ -220,7 +218,7 @@ impl<'a> SceneBuilder<'a> {
BuiltDrawPath { BuiltDrawPath {
path: tiler.object_builder.built_path, path: tiler.object_builder.built_path,
blend_mode: path_object.blend_mode(), blend_mode: path_object.blend_mode(),
effects: paint_metadata.effects(), filter: paint_metadata.filter(),
color_texture_page_0: paint_metadata.location.page, color_texture_page_0: paint_metadata.location.page,
sampling_flags_0: paint_metadata.sampling_flags, sampling_flags_0: paint_metadata.sampling_flags,
color_texture_page_1: opacity_tile_page, color_texture_page_1: opacity_tile_page,
@ -230,10 +228,7 @@ impl<'a> SceneBuilder<'a> {
} }
} }
fn cull_tiles(&self, fn cull_tiles(&self, paint_metadata: &[PaintMetadata], built_draw_paths: Vec<BuiltDrawPath>)
paint_metadata: &[PaintMetadata],
render_target_metadata: &[RenderTargetMetadata],
built_draw_paths: Vec<BuiltDrawPath>)
-> CulledTiles { -> CulledTiles {
let mut culled_tiles = CulledTiles { display_list: vec![] }; let mut culled_tiles = CulledTiles { display_list: vec![] };
@ -269,40 +264,6 @@ impl<'a> SceneBuilder<'a> {
layer_z_buffers_stack.pop(); layer_z_buffers_stack.pop();
} }
DisplayItem::DrawRenderTarget { render_target, effects } => {
let effective_view_box = self.scene.effective_view_box(self.built_options);
let tile_rect = tiles::round_rect_out_to_tile_bounds(effective_view_box);
let layer_z_buffer = layer_z_buffers_stack.last().unwrap();
let mut tiles = vec![];
let uv_scale = vec2f(1.0, 1.0) / tile_rect.lower_right().to_f32();
let metadata = &render_target_metadata[render_target.0 as usize];
for tile_y in tile_rect.min_y()..tile_rect.max_y() {
for tile_x in tile_rect.min_x()..tile_rect.max_x() {
let tile_coords = vec2i(tile_x, tile_y);
if !layer_z_buffer.test(tile_coords, current_depth) {
continue;
}
let uv_rect = RectI::new(tile_coords, vec2i(1, 1)).to_f32() * uv_scale;
tiles.push(Tile::new_solid_from_texture_rect(tile_coords, uv_rect));
}
}
let batch = TileBatch {
tiles,
color_texture_0: Some(TileBatchTexture {
page: metadata.location.page,
sampling_flags: TextureSamplingFlags::empty(),
}),
color_texture_1: None,
effects,
blend_mode: BlendMode::SrcOver,
mask_0_fill_rule: None,
mask_1_fill_rule: None,
};
culled_tiles.display_list.push(CulledDisplayItem::DrawTiles(batch));
current_depth += 1;
}
DisplayItem::DrawPaths { DisplayItem::DrawPaths {
start_index: start_draw_path_index, start_index: start_draw_path_index,
end_index: end_draw_path_index, end_index: end_draw_path_index,
@ -328,7 +289,7 @@ impl<'a> SceneBuilder<'a> {
None, None,
None, None,
built_draw_path.blend_mode, built_draw_path.blend_mode,
built_draw_path.effects, built_draw_path.filter,
None, None,
None); None);
@ -339,7 +300,7 @@ impl<'a> SceneBuilder<'a> {
color_texture_0, color_texture_0,
color_texture_1, color_texture_1,
built_draw_path.blend_mode, built_draw_path.blend_mode,
built_draw_path.effects, built_draw_path.filter,
Some(built_draw_path.mask_0_fill_rule), Some(built_draw_path.mask_0_fill_rule),
None); None);
@ -351,7 +312,7 @@ impl<'a> SceneBuilder<'a> {
color_texture_0, color_texture_0,
color_texture_1, color_texture_1,
built_draw_path.blend_mode, built_draw_path.blend_mode,
built_draw_path.effects, built_draw_path.filter,
Some(built_draw_path.mask_0_fill_rule), Some(built_draw_path.mask_0_fill_rule),
Some(mask_1_fill_rule)); Some(mask_1_fill_rule));
} }
@ -365,7 +326,7 @@ impl<'a> SceneBuilder<'a> {
color_texture_0, color_texture_0,
color_texture_1, color_texture_1,
built_draw_path.blend_mode, built_draw_path.blend_mode,
built_draw_path.effects, built_draw_path.filter,
None, None,
None); None);
} }
@ -416,10 +377,6 @@ impl<'a> SceneBuilder<'a> {
current_depth += 1; current_depth += 1;
} }
} }
DisplayItem::DrawRenderTarget { .. } => {
// FIXME(pcwalton): Not great that this doesn't participate in Z-buffering!
current_depth += 1;
}
} }
} }
debug_assert_eq!(z_buffer_index_stack.len(), 1); debug_assert_eq!(z_buffer_index_stack.len(), 1);
@ -435,7 +392,7 @@ impl<'a> SceneBuilder<'a> {
color_texture_0: Option<TileBatchTexture>, color_texture_0: Option<TileBatchTexture>,
color_texture_1: Option<TileBatchTexture>, color_texture_1: Option<TileBatchTexture>,
blend_mode: BlendMode, blend_mode: BlendMode,
effects: Effects, filter: Filter,
mask_0_fill_rule: Option<FillRule>, mask_0_fill_rule: Option<FillRule>,
mask_1_fill_rule: Option<FillRule>) { mask_1_fill_rule: Option<FillRule>) {
if alpha_tiles.is_empty() { if alpha_tiles.is_empty() {
@ -454,13 +411,13 @@ impl<'a> SceneBuilder<'a> {
color_texture_0: ref batch_color_texture_0, color_texture_0: ref batch_color_texture_0,
color_texture_1: ref batch_color_texture_1, color_texture_1: ref batch_color_texture_1,
blend_mode: batch_blend_mode, blend_mode: batch_blend_mode,
effects: batch_effects, filter: batch_filter,
mask_0_fill_rule: batch_mask_0_fill_rule, mask_0_fill_rule: batch_mask_0_fill_rule,
mask_1_fill_rule: batch_mask_1_fill_rule, mask_1_fill_rule: batch_mask_1_fill_rule,
})) if *batch_color_texture_0 == color_texture_0 && })) if *batch_color_texture_0 == color_texture_0 &&
*batch_color_texture_1 == color_texture_1 && *batch_color_texture_1 == color_texture_1 &&
batch_blend_mode == blend_mode && batch_blend_mode == blend_mode &&
batch_effects == effects && batch_filter == filter &&
batch_mask_0_fill_rule == mask_0_fill_rule && batch_mask_0_fill_rule == mask_0_fill_rule &&
batch_mask_1_fill_rule == mask_1_fill_rule && batch_mask_1_fill_rule == mask_1_fill_rule &&
!batch_blend_mode.needs_readable_framebuffer() => {} !batch_blend_mode.needs_readable_framebuffer() => {}
@ -470,7 +427,7 @@ impl<'a> SceneBuilder<'a> {
color_texture_0, color_texture_0,
color_texture_1, color_texture_1,
blend_mode, blend_mode,
effects, filter,
mask_0_fill_rule, mask_0_fill_rule,
mask_1_fill_rule, mask_1_fill_rule,
}; };
@ -512,12 +469,9 @@ impl<'a> SceneBuilder<'a> {
fn finish_building(&mut self, fn finish_building(&mut self,
paint_metadata: &[PaintMetadata], paint_metadata: &[PaintMetadata],
render_target_metadata: &[RenderTargetMetadata],
built_draw_paths: Vec<BuiltDrawPath>) { built_draw_paths: Vec<BuiltDrawPath>) {
self.listener.send(RenderCommand::FlushFills); self.listener.send(RenderCommand::FlushFills);
let culled_tiles = self.cull_tiles(paint_metadata, let culled_tiles = self.cull_tiles(paint_metadata, built_draw_paths);
render_target_metadata,
built_draw_paths);
self.pack_tiles(culled_tiles); self.pack_tiles(culled_tiles);
} }
@ -525,7 +479,6 @@ impl<'a> SceneBuilder<'a> {
let mut framebuffer_nesting = 0; let mut framebuffer_nesting = 0;
for display_item in &self.scene.display_list { for display_item in &self.scene.display_list {
match *display_item { match *display_item {
DisplayItem::DrawRenderTarget { .. } => {}
DisplayItem::PushRenderTarget(_) => framebuffer_nesting += 1, DisplayItem::PushRenderTarget(_) => framebuffer_nesting += 1,
DisplayItem::PopRenderTarget => framebuffer_nesting -= 1, DisplayItem::PopRenderTarget => framebuffer_nesting -= 1,
DisplayItem::DrawPaths { start_index, end_index } => { DisplayItem::DrawPaths { start_index, end_index } => {

View File

@ -19,7 +19,8 @@ use crate::gpu_data::{TexturePageId, Tile, TileBatchTexture};
use crate::options::BoundingQuad; use crate::options::BoundingQuad;
use crate::tiles::{TILE_HEIGHT, TILE_WIDTH}; use crate::tiles::{TILE_HEIGHT, TILE_WIDTH};
use pathfinder_color::{self as color, ColorF, ColorU}; use pathfinder_color::{self as color, ColorF, ColorU};
use pathfinder_content::effects::{BlendMode, BlurDirection, DefringingKernel, Effects, Filter}; use pathfinder_content::effects::{BlendMode, BlurDirection, DefringingKernel};
use pathfinder_content::effects::{Filter, PatternFilter};
use pathfinder_content::fill::FillRule; use pathfinder_content::fill::FillRule;
use pathfinder_content::render_target::RenderTargetId; use pathfinder_content::render_target::RenderTargetId;
use pathfinder_geometry::line_segment::LineSegment2F; use pathfinder_geometry::line_segment::LineSegment2F;
@ -313,7 +314,7 @@ where
batch.mask_0_fill_rule, batch.mask_0_fill_rule,
batch.mask_1_fill_rule, batch.mask_1_fill_rule,
batch.blend_mode, batch.blend_mode,
batch.effects) batch.filter)
} }
RenderCommand::Finish { .. } => {} RenderCommand::Finish { .. } => {}
} }
@ -583,7 +584,7 @@ where
mask_0_fill_rule: Option<FillRule>, mask_0_fill_rule: Option<FillRule>,
mask_1_fill_rule: Option<FillRule>, mask_1_fill_rule: Option<FillRule>,
blend_mode: BlendMode, blend_mode: BlendMode,
effects: Effects) { filter: Filter) {
// TODO(pcwalton): Disable blend for solid tiles. // TODO(pcwalton): Disable blend for solid tiles.
let needs_readable_framebuffer = blend_mode.needs_readable_framebuffer(); let needs_readable_framebuffer = blend_mode.needs_readable_framebuffer();
@ -658,13 +659,18 @@ where
ctrl |= blend_mode.to_composite_ctrl() << COMBINER_CTRL_COMPOSITE_SHIFT; ctrl |= blend_mode.to_composite_ctrl() << COMBINER_CTRL_COMPOSITE_SHIFT;
match effects.filter { match filter {
Filter::None => {} Filter::None => {}
Filter::RadialGradient { line, radii, uv_origin } => { Filter::RadialGradient { line, radii, uv_origin } => {
ctrl |= COMBINER_CTRL_FILTER_RADIAL_GRADIENT << COMBINER_CTRL_COLOR_0_FILTER_SHIFT; ctrl |= COMBINER_CTRL_FILTER_RADIAL_GRADIENT << COMBINER_CTRL_COLOR_0_FILTER_SHIFT;
self.set_uniforms_for_radial_gradient_filter(&mut uniforms, line, radii, uv_origin) self.set_uniforms_for_radial_gradient_filter(&mut uniforms, line, radii, uv_origin)
} }
Filter::Text { fg_color, bg_color, defringing_kernel, gamma_correction } => { Filter::PatternFilter(PatternFilter::Text {
fg_color,
bg_color,
defringing_kernel,
gamma_correction,
}) => {
ctrl |= COMBINER_CTRL_FILTER_TEXT << COMBINER_CTRL_COLOR_0_FILTER_SHIFT; ctrl |= COMBINER_CTRL_FILTER_TEXT << COMBINER_CTRL_COLOR_0_FILTER_SHIFT;
self.set_uniforms_for_text_filter(&mut textures, self.set_uniforms_for_text_filter(&mut textures,
&mut uniforms, &mut uniforms,
@ -673,7 +679,7 @@ where
defringing_kernel, defringing_kernel,
gamma_correction); gamma_correction);
} }
Filter::Blur { direction, sigma } => { Filter::PatternFilter(PatternFilter::Blur { direction, sigma }) => {
ctrl |= COMBINER_CTRL_FILTER_BLUR << COMBINER_CTRL_COLOR_0_FILTER_SHIFT; ctrl |= COMBINER_CTRL_FILTER_BLUR << COMBINER_CTRL_COLOR_0_FILTER_SHIFT;
self.set_uniforms_for_blur_filter(&mut uniforms, direction, sigma); self.set_uniforms_for_blur_filter(&mut uniforms, direction, sigma);
} }

View File

@ -12,7 +12,7 @@
use crate::options::BoundingQuad; use crate::options::BoundingQuad;
use pathfinder_color::ColorU; use pathfinder_color::ColorU;
use pathfinder_content::effects::{BlendMode, Effects}; use pathfinder_content::effects::{BlendMode, Filter};
use pathfinder_content::fill::FillRule; use pathfinder_content::fill::FillRule;
use pathfinder_content::render_target::RenderTargetId; use pathfinder_content::render_target::RenderTargetId;
use pathfinder_geometry::line_segment::{LineSegmentU4, LineSegmentU8}; use pathfinder_geometry::line_segment::{LineSegmentU4, LineSegmentU8};
@ -91,8 +91,8 @@ pub struct TileBatch {
pub color_texture_1: Option<TileBatchTexture>, pub color_texture_1: Option<TileBatchTexture>,
pub mask_0_fill_rule: Option<FillRule>, pub mask_0_fill_rule: Option<FillRule>,
pub mask_1_fill_rule: Option<FillRule>, pub mask_1_fill_rule: Option<FillRule>,
pub filter: Filter,
pub blend_mode: BlendMode, pub blend_mode: BlendMode,
pub effects: Effects,
} }
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]

View File

@ -14,9 +14,9 @@ use crate::scene::RenderTarget;
use crate::tiles::{TILE_HEIGHT, TILE_WIDTH}; use crate::tiles::{TILE_HEIGHT, TILE_WIDTH};
use hashbrown::HashMap; use hashbrown::HashMap;
use pathfinder_color::ColorU; use pathfinder_color::ColorU;
use pathfinder_content::effects::{Effects, Filter}; use pathfinder_content::effects::{Filter, PatternFilter};
use pathfinder_content::gradient::Gradient; use pathfinder_content::gradient::Gradient;
use pathfinder_content::pattern::{Pattern, PatternFlags, PatternSource}; use pathfinder_content::pattern::{Pattern, PatternSource};
use pathfinder_content::render_target::RenderTargetId; use pathfinder_content::render_target::RenderTargetId;
use pathfinder_geometry::line_segment::LineSegment2F; use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::rect::{RectF, RectI}; use pathfinder_geometry::rect::{RectF, RectI};
@ -93,7 +93,7 @@ impl Paint {
Paint::Gradient(ref gradient) => { Paint::Gradient(ref gradient) => {
gradient.stops().iter().all(|stop| stop.color.is_opaque()) gradient.stops().iter().all(|stop| stop.color.is_opaque())
} }
Paint::Pattern(ref pattern) => pattern.source.is_opaque(), Paint::Pattern(ref pattern) => pattern.is_opaque(),
} }
} }
@ -133,7 +133,7 @@ impl Paint {
0.5)))); 0.5))));
} }
} }
Paint::Pattern(ref mut pattern) => pattern.transform = *transform * pattern.transform, Paint::Pattern(ref mut pattern) => pattern.apply_transform(*transform),
} }
} }
} }
@ -165,8 +165,8 @@ pub struct PaintMetadata {
pub sampling_flags: TextureSamplingFlags, pub sampling_flags: TextureSamplingFlags,
/// True if this paint is fully opaque. /// True if this paint is fully opaque.
pub is_opaque: bool, pub is_opaque: bool,
/// The radial gradient for this paint, if applicable. /// The filter to be applied to this paint.
pub radial_gradient: Option<RadialGradientMetadata>, pub filter: PaintFilter,
} }
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
@ -183,6 +183,18 @@ pub struct RenderTargetMetadata {
pub location: TextureLocation, pub location: TextureLocation,
} }
#[derive(Debug)]
pub enum PaintFilter {
None,
RadialGradient {
/// The line segment that connects the two circles.
line: LineSegment2F,
/// The radii of the two circles.
radii: F32x2,
},
PatternFilter(PatternFilter),
}
impl Palette { impl Palette {
#[allow(clippy::trivially_copy_pass_by_ref)] #[allow(clippy::trivially_copy_pass_by_ref)]
pub fn push_paint(&mut self, paint: &Paint) -> PaintId { pub fn push_paint(&mut self, paint: &Paint) -> PaintId {
@ -219,23 +231,26 @@ impl Palette {
let mut gradient_tile_builder = GradientTileBuilder::new(); let mut gradient_tile_builder = GradientTileBuilder::new();
let mut image_texel_info = vec![]; let mut image_texel_info = vec![];
for paint in &self.paints { for paint in &self.paints {
let (texture_location, mut sampling_flags, radial_gradient); let (texture_location, mut sampling_flags, filter);
match paint { match paint {
Paint::Color(color) => { Paint::Color(color) => {
texture_location = solid_color_tile_builder.allocate(&mut allocator, *color); texture_location = solid_color_tile_builder.allocate(&mut allocator, *color);
sampling_flags = TextureSamplingFlags::empty(); sampling_flags = TextureSamplingFlags::empty();
radial_gradient = None; filter = PaintFilter::None;
} }
Paint::Gradient(ref gradient) => { Paint::Gradient(ref gradient) => {
// FIXME(pcwalton): The gradient size might not be big enough. Detect this. // FIXME(pcwalton): The gradient size might not be big enough. Detect this.
texture_location = gradient_tile_builder.allocate(&mut allocator, gradient); texture_location = gradient_tile_builder.allocate(&mut allocator, gradient);
sampling_flags = TextureSamplingFlags::empty(); sampling_flags = TextureSamplingFlags::empty();
radial_gradient = gradient.radii().map(|radii| { filter = match gradient.radii() {
RadialGradientMetadata { line: gradient.line(), radii } None => PaintFilter::None,
}); Some(radii) => {
PaintFilter::RadialGradient { line: gradient.line(), radii }
}
};
} }
Paint::Pattern(ref pattern) => { Paint::Pattern(ref pattern) => {
match pattern.source { match *pattern.source() {
PatternSource::RenderTarget { id: render_target_id, .. } => { PatternSource::RenderTarget { id: render_target_id, .. } => {
texture_location = texture_location =
render_target_metadata[render_target_id.0 as usize].location; render_target_metadata[render_target_id.0 as usize].location;
@ -253,18 +268,21 @@ impl Palette {
} }
sampling_flags = TextureSamplingFlags::empty(); sampling_flags = TextureSamplingFlags::empty();
if pattern.flags.contains(PatternFlags::REPEAT_X) { if pattern.repeat_x() {
sampling_flags.insert(TextureSamplingFlags::REPEAT_U); sampling_flags.insert(TextureSamplingFlags::REPEAT_U);
} }
if pattern.flags.contains(PatternFlags::REPEAT_Y) { if pattern.repeat_y() {
sampling_flags.insert(TextureSamplingFlags::REPEAT_V); sampling_flags.insert(TextureSamplingFlags::REPEAT_V);
} }
if pattern.flags.contains(PatternFlags::NO_SMOOTHING) { if !pattern.smoothing_enabled() {
sampling_flags.insert(TextureSamplingFlags::NEAREST_MIN | sampling_flags.insert(TextureSamplingFlags::NEAREST_MIN |
TextureSamplingFlags::NEAREST_MAG); TextureSamplingFlags::NEAREST_MAG);
} }
radial_gradient = None; filter = match pattern.filter() {
None => PaintFilter::None,
Some(pattern_filter) => PaintFilter::PatternFilter(pattern_filter),
};
} }
}; };
@ -273,7 +291,7 @@ impl Palette {
texture_transform: Transform2F::default(), texture_transform: Transform2F::default(),
sampling_flags, sampling_flags,
is_opaque: paint.is_opaque(), is_opaque: paint.is_opaque(),
radial_gradient, filter,
}); });
} }
@ -303,24 +321,24 @@ impl Palette {
vector: texture_origin_uv, vector: texture_origin_uv,
} * render_transform } * render_transform
} }
Paint::Pattern(Pattern { source: PatternSource::Image(_), transform, .. }) => { Paint::Pattern(pattern) => {
let texture_origin_uv = match pattern.source() {
rect_to_uv(metadata.location.rect, texture_scale).origin(); PatternSource::Image(_) => {
Transform2F::from_translation(texture_origin_uv) * let texture_origin_uv =
Transform2F::from_scale(texture_scale) * rect_to_uv(metadata.location.rect, texture_scale).origin();
transform.inverse() * render_transform Transform2F::from_translation(texture_origin_uv) *
} Transform2F::from_scale(texture_scale) *
Paint::Pattern(Pattern { pattern.transform().inverse() * render_transform
source: PatternSource::RenderTarget { .. }, }
transform, PatternSource::RenderTarget { .. } => {
.. // FIXME(pcwalton): Only do this in GL, not Metal!
}) => { let texture_origin_uv = rect_to_uv(metadata.location.rect,
// FIXME(pcwalton): Only do this in GL, not Metal! texture_scale).lower_left();
let texture_origin_uv = rect_to_uv(metadata.location.rect, Transform2F::from_translation(texture_origin_uv) *
texture_scale).lower_left(); Transform2F::from_scale(texture_scale * vec2f(1.0, -1.0)) *
Transform2F::from_translation(texture_origin_uv) * pattern.transform().inverse() * render_transform
Transform2F::from_scale(texture_scale * vec2f(1.0, -1.0)) * }
transform.inverse() * render_transform }
} }
} }
} }
@ -376,18 +394,14 @@ impl PaintMetadata {
tex_coords tex_coords
} }
pub(crate) fn effects(&self) -> Effects { pub(crate) fn filter(&self) -> Filter {
Effects { match self.filter {
filter: match self.radial_gradient { PaintFilter::None => Filter::None,
None => Filter::None, PaintFilter::RadialGradient { line, radii } => {
Some(gradient) => { let uv_origin = self.texture_transform.vector;
Filter::RadialGradient { Filter::RadialGradient { line, radii, uv_origin }
line: gradient.line, }
radii: gradient.radii, PaintFilter::PatternFilter(pattern_filter) => Filter::PatternFilter(pattern_filter),
uv_origin: self.texture_transform.vector,
}
}
},
} }
} }
} }

View File

@ -15,7 +15,7 @@ use crate::concurrent::executor::Executor;
use crate::options::{BuildOptions, PreparedBuildOptions}; use crate::options::{BuildOptions, PreparedBuildOptions};
use crate::options::{PreparedRenderTransform, RenderCommandListener}; use crate::options::{PreparedRenderTransform, RenderCommandListener};
use crate::paint::{Paint, PaintId, PaintInfo, Palette}; use crate::paint::{Paint, PaintId, PaintInfo, Palette};
use pathfinder_content::effects::{BlendMode, Effects}; use pathfinder_content::effects::BlendMode;
use pathfinder_content::fill::FillRule; use pathfinder_content::fill::FillRule;
use pathfinder_content::outline::Outline; use pathfinder_content::outline::Outline;
use pathfinder_content::pattern::{Pattern, PatternSource}; use pathfinder_content::pattern::{Pattern, PatternSource};
@ -87,10 +87,6 @@ impl Scene {
self.display_list.push(DisplayItem::PopRenderTarget); self.display_list.push(DisplayItem::PopRenderTarget);
} }
pub fn draw_render_target(&mut self, render_target: RenderTargetId, effects: Effects) {
self.display_list.push(DisplayItem::DrawRenderTarget { render_target, effects });
}
pub fn append_scene(&mut self, scene: Scene) { pub fn append_scene(&mut self, scene: Scene) {
// Merge render targets. // Merge render targets.
let mut render_target_mapping = HashMap::new(); let mut render_target_mapping = HashMap::new();
@ -108,19 +104,20 @@ impl Scene {
for (old_paint_index, old_paint) in scene.palette.paints.iter().enumerate() { for (old_paint_index, old_paint) in scene.palette.paints.iter().enumerate() {
let old_paint_id = PaintId(old_paint_index as u16); let old_paint_id = PaintId(old_paint_index as u16);
let new_paint_id = match old_paint { let new_paint_id = match old_paint {
Paint::Pattern(Pattern { Paint::Pattern(pattern) => {
source: PatternSource::RenderTarget { id: old_render_target_id, size }, match pattern.source() {
transform, PatternSource::RenderTarget { id: old_render_target_id, size } => {
flags let mut new_pattern =
}) => { Pattern::from_render_target(*old_render_target_id, *size);
self.palette.push_paint(&Paint::Pattern(Pattern { new_pattern.set_filter(pattern.filter());
source: PatternSource::RenderTarget { new_pattern.apply_transform(pattern.transform());
id: render_target_mapping[old_render_target_id], new_pattern.set_repeat_x(pattern.repeat_x());
size: *size, new_pattern.set_repeat_y(pattern.repeat_y());
}, new_pattern.set_smoothing_enabled(pattern.smoothing_enabled());
transform: *transform, self.palette.push_paint(&Paint::Pattern(new_pattern))
flags: *flags, }
})) _ => self.palette.push_paint(old_paint),
}
} }
paint => self.palette.push_paint(paint), paint => self.palette.push_paint(paint),
}; };
@ -154,13 +151,6 @@ impl Scene {
// Merge display items. // Merge display items.
for display_item in scene.display_list { for display_item in scene.display_list {
match display_item { match display_item {
DisplayItem::DrawRenderTarget {
render_target: old_render_target_id,
effects,
} => {
let new_render_target_id = render_target_mapping[&old_render_target_id];
self.draw_render_target(new_render_target_id, effects)
}
DisplayItem::PushRenderTarget(old_render_target_id) => { DisplayItem::PushRenderTarget(old_render_target_id) => {
let new_render_target_id = render_target_mapping[&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));
@ -349,13 +339,6 @@ pub enum DisplayItem {
/// Draws paths to the render target on top of the stack. /// Draws paths to the render target on top of the stack.
DrawPaths { start_index: u32, end_index: u32 }, DrawPaths { start_index: u32, end_index: u32 },
/// Draws an entire render target to the render target on top of the stack.
///
/// FIXME(pcwalton): This draws the entire render target, so it's inefficient. We should get
/// rid of this command and transition all uses to `DrawPaths`. The reason it exists is that we
/// don't have logic to create tiles for blur bounding regions yet.
DrawRenderTarget { render_target: RenderTargetId, effects: Effects },
/// Pushes a render target onto the top of the stack. /// Pushes a render target onto the top of the stack.
PushRenderTarget(RenderTargetId), PushRenderTarget(RenderTargetId),

View File

@ -15,7 +15,7 @@ use crate::gpu_data::{Tile, TileBatch, TileBatchTexture, TileVertex};
use crate::paint::{PaintId, PaintMetadata}; use crate::paint::{PaintId, PaintMetadata};
use crate::tile_map::DenseTileMap; use crate::tile_map::DenseTileMap;
use crate::tiles; use crate::tiles;
use pathfinder_content::effects::{BlendMode, Effects}; use pathfinder_content::effects::{BlendMode, Filter};
use pathfinder_geometry::rect::RectF; use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::vector::{Vector2F, Vector2I, vec2i}; use pathfinder_geometry::vector::{Vector2F, Vector2I, vec2i};
use vec_map::VecMap; use vec_map::VecMap;
@ -94,7 +94,7 @@ impl ZBuffer {
}), }),
color_texture_1: None, color_texture_1: None,
tiles: vec![], tiles: vec![],
effects: Effects::default(), filter: Filter::None,
blend_mode: BlendMode::default(), blend_mode: BlendMode::default(),
mask_0_fill_rule: None, mask_0_fill_rule: None,
mask_1_fill_rule: None, mask_1_fill_rule: None,
@ -124,19 +124,6 @@ impl Tile {
paint_metadata), paint_metadata),
} }
} }
pub(crate) fn new_solid_from_texture_rect(tile_position: Vector2I, texture_rect: RectF)
-> Tile {
Tile {
upper_left: TileVertex::new_solid_from_uv(tile_position, texture_rect.origin()),
upper_right: TileVertex::new_solid_from_uv(tile_position + vec2i(1, 0),
texture_rect.upper_right()),
lower_left: TileVertex::new_solid_from_uv(tile_position + vec2i(0, 1),
texture_rect.lower_left()),
lower_right: TileVertex::new_solid_from_uv(tile_position + vec2i(1, 1),
texture_rect.lower_right()),
}
}
} }
impl TileVertex { impl TileVertex {