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_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::gradient::Gradient;
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::stroke::{LineCap, LineJoin as StrokeLineJoin};
use pathfinder_content::stroke::{OutlineStrokeToFill, StrokeStyle};
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::vector::{IntoVector2F, Vector2F, vec2f};
use pathfinder_geometry::vector::{IntoVector2F, Vector2F, Vector2I, vec2f, vec2i};
use pathfinder_renderer::paint::{Paint, PaintId};
use pathfinder_renderer::scene::{ClipPath, ClipPathId, DrawPath, RenderTarget, Scene};
use std::borrow::Cow;
@ -101,6 +101,11 @@ impl Canvas {
font_context,
}
}
#[inline]
pub fn size(&self) -> Vector2I {
self.scene.view_box().size().ceil().to_i32()
}
}
pub struct CanvasRenderingContext2D {
@ -340,7 +345,7 @@ impl CanvasRenderingContext2D {
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_id_a = self.canvas.scene.push_render_target(render_target_a);
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 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.draw_render_target(render_target_ids[1], Effects::new(Filter::Blur {
direction: BlurDirection::X,
sigma,
}));
self.canvas.scene.push_path(path_x);
self.canvas.scene.pop_render_target();
self.canvas.scene.draw_render_target(render_target_ids[0], Effects::new(Filter::Blur {
direction: BlurDirection::Y,
sigma,
}));
self.canvas.scene.push_path(path_y);
}
// Transformations
@ -487,17 +500,16 @@ impl CanvasRenderingContext2D {
pub fn create_pattern_from_canvas(&mut self, canvas: Canvas, transform: Transform2F)
-> Pattern {
let subscene_size = canvas.size();
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_id = self.canvas.scene.push_render_target(render_target);
self.canvas.scene.append_scene(subscene);
self.canvas.scene.pop_render_target();
let pattern_source = PatternSource::RenderTarget {
id: render_target_id,
size: subscene_size,
};
Pattern::new(pattern_source, transform, PatternFlags::empty())
let mut pattern = Pattern::from_render_target(render_target_id, subscene_size);
pattern.apply_transform(transform);
pattern
}
}
@ -557,8 +569,7 @@ impl State {
let mut must_copy = !self.transform.is_identity();
if !must_copy {
if let Paint::Pattern(ref pattern) = *paint {
must_copy = !self.image_smoothing_enabled !=
pattern.flags.contains(PatternFlags::NO_SMOOTHING);
must_copy = self.image_smoothing_enabled != pattern.smoothing_enabled()
}
}
@ -569,7 +580,7 @@ impl State {
let mut paint = (*paint).clone();
paint.apply_transform(&self.transform);
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)
}
@ -833,7 +844,7 @@ pub trait CanvasImageDestLocation {
impl CanvasImageSource for Pattern {
#[inline]
fn to_pattern(mut self, _: &mut CanvasRenderingContext2D, transform: Transform2F) -> Pattern {
self.transform(transform);
self.apply_transform(transform);
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.
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.
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum Filter {
@ -54,6 +47,12 @@ pub enum Filter {
uv_origin: Vector2F,
},
PatternFilter(PatternFilter),
}
/// Shaders applicable to patterns.
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum PatternFilter {
/// Performs postprocessing operations useful for monochrome text.
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 {
/// Whether the backdrop is irrelevant when applying this blend mode (i.e. destination blend
/// factor is zero when source alpha is one).

View File

@ -110,6 +110,13 @@ impl Outline {
outline
}
#[inline]
pub fn from_rect(rect: RectF) -> Outline {
let mut outline = Outline::new();
outline.push_contour(Contour::from_rect(rect));
outline
}
#[inline]
pub fn bounds(&self) -> RectF {
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`.
#[inline]
pub(crate) fn take(&mut self) -> Contour {

View File

@ -10,6 +10,7 @@
//! Raster image patterns.
use crate::effects::PatternFilter;
use crate::render_target::RenderTargetId;
use crate::util;
use pathfinder_color::{self as color, ColorU};
@ -26,9 +27,10 @@ use image::RgbaImage;
/// A raster image pattern.
#[derive(Clone, PartialEq, Debug)]
pub struct Pattern {
pub source: PatternSource,
pub transform: Transform2F,
pub flags: PatternFlags,
source: PatternSource,
transform: Transform2F,
filter: Option<PatternFilter>,
flags: PatternFlags,
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
@ -61,13 +63,33 @@ bitflags! {
impl Pattern {
#[inline]
pub fn new(source: PatternSource, transform: Transform2F, flags: PatternFlags) -> Pattern {
Pattern { source, transform, flags }
fn from_source(source: PatternSource) -> Pattern {
Pattern {
source,
transform: Transform2F::default(),
filter: None,
flags: PatternFlags::empty(),
}
}
#[inline]
pub fn transform(&mut self, transform: Transform2F) {
self.transform *= transform
pub fn from_image(image: Image) -> Pattern {
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]
@ -77,6 +99,56 @@ impl Pattern {
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 {

View File

@ -22,10 +22,14 @@ use crate::device::{GroundProgram, GroundVertexArray};
use crate::ui::{DemoUIModel, DemoUIPresenter, ScreenshotInfo, ScreenshotType, UIAction};
use crate::window::{Event, Keycode, SVGPath, Window, WindowSize};
use clap::{App, Arg};
use pathfinder_content::effects::{DEFRINGING_KERNEL_CORE_GRAPHICS, Effects};
use pathfinder_content::effects::{Filter, STEM_DARKENING_FACTORS};
use pathfinder_content::effects::DEFRINGING_KERNEL_CORE_GRAPHICS;
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_geometry::rect::RectF;
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};
@ -34,7 +38,8 @@ use pathfinder_renderer::concurrent::scene_proxy::{RenderCommandStream, ScenePro
use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererOptions};
use pathfinder_renderer::gpu::renderer::{RenderStats, RenderTime, Renderer};
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_svg::BuiltSVG;
use pathfinder_ui::{MousePosition, UIEvent};
@ -136,13 +141,13 @@ impl<W> DemoApp<W> where W: Window {
let mut ui_model = DemoUIModel::new(&options);
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 (mut built_svg, svg_tree) = load_scene(resources,
&options.input_path,
viewport.size(),
effects);
filter);
let message = get_svg_building_message(&built_svg);
@ -420,11 +425,11 @@ impl<W> DemoApp<W> where W: Window {
Event::OpenSVG(ref svg_path) => {
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(),
svg_path,
viewport.size(),
effects);
filter);
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::EffectsChanged => {
let viewport_size = self.window.viewport(self.ui_model.mode.view(0)).size();
let effects = build_effects(&self.ui_model);
let mut built_svg = build_svg_tree(&self.svg_tree, viewport_size, effects);
let filter = build_filter(&self.ui_model);
let mut built_svg = build_svg_tree(&self.svg_tree, viewport_size, filter);
self.scene_metadata =
SceneMetadata::new_clipping_view_box(&mut built_svg.scene, viewport_size);
self.scene_proxy.replace_scene(built_svg.scene);
@ -737,7 +742,7 @@ pub enum UIVisibility {
fn load_scene(resource_loader: &dyn ResourceLoader,
input_path: &SVGPath,
viewport_size: Vector2I,
effects: Option<Effects>)
filter: Option<PatternFilter>)
-> (BuiltSVG, Tree) {
let mut data;
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 built_svg = build_svg_tree(&tree, viewport_size, effects);
let built_svg = build_svg_tree(&tree, viewport_size, filter);
(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 render_target_id = match effects {
None => None,
Some(effects) => {
let scale = match effects.filter {
Filter::Text { defringing_kernel: Some(_), .. } => vec2i(3, 1),
let filter_info = filter.map(|filter| {
let scale = match filter {
PatternFilter::Text { defringing_kernel: Some(_), .. } => vec2i(3, 1),
_ => vec2i(1, 1),
};
let name = "Text".to_owned();
let render_target = RenderTarget::new(viewport_size * scale, name);
Some(scene.push_render_target(render_target))
}
};
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 }
});
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.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 {
@ -863,13 +882,12 @@ 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 {
return None;
}
Some(Effects {
filter: Filter::Text {
Some(PatternFilter::Text {
fg_color: ui_model.foreground_color().to_f32(),
bg_color: ui_model.background_color().to_f32(),
gamma_correction: ui_model.gamma_correction_effect_enabled,
@ -879,6 +897,5 @@ fn build_effects(ui_model: &DemoUIModel) -> Option<Effects> {
} else {
None
}
},
})
}

View File

@ -18,7 +18,7 @@ use pathfinder_color::{ColorF, ColorU, rgbau, rgbf, rgbu};
use pathfinder_content::fill::FillRule;
use pathfinder_content::gradient::Gradient;
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_geometry::angle;
use pathfinder_geometry::line_segment::LineSegment2F;
@ -537,21 +537,11 @@ fn draw_window(context: &mut CanvasRenderingContext2D, title: &str, rect: RectF)
// Draw window with shadow.
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);
// 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));
context.set_shadow_color(rgbau(0, 0, 0, 0));
// Header.
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) {
let corner_radius = rect.height() * 0.5 - 1.0;
fill_path_with_box_gradient(context,
create_rounded_rect_path(rect, corner_radius),
FillRule::Winding,
rect + vec2f(0.0, 1.5),
rect.height() * 0.5,
5.0,
rgbau(0, 0, 0, 16),
rgbau(0, 0, 0, 92));
let path = create_rounded_rect_path(rect, corner_radius);
context.save();
context.clip_path(path.clone(), FillRule::Winding);
context.set_shadow_offset(vec2f(0.0, 1.5));
context.set_shadow_blur(5.0);
context.set_shadow_color(rgbau(0, 0, 0, 92));
context.set_fill_style(rgbau(0, 0, 0, 16));
context.fill_path(path, FillRule::Winding);
context.restore();
context.set_font_size(rect.height() * 0.5);
context.set_font(FONT_NAME_EMOJI);
context.set_fill_style(rgbau(255, 255, 255, 64));
context.set_text_align(TextAlign::Center);
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_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).to_f32() * THUMB_HEIGHT) *
Transform2F::from_scale(0.5);
let pattern = Pattern::new(PatternSource::Image((*image).clone()),
pattern_transform,
PatternFlags::empty());
let mut pattern = Pattern::from_image((*image).clone());
pattern.apply_transform(pattern_transform);
context.set_fill_style(pattern);
context.set_global_alpha(alpha);
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::{TileBatchTexture, TileObjectPrimitive, TileVertex};
use crate::options::{PreparedBuildOptions, PreparedRenderTransform, RenderCommandListener};
use crate::paint::{PaintInfo, PaintMetadata, RenderTargetMetadata};
use crate::paint::{PaintInfo, PaintMetadata};
use crate::scene::{DisplayItem, Scene};
use crate::tile_map::DenseTileMap;
use crate::tiles::{self, DrawTilingPathInfo, PackedTile, TILE_HEIGHT, TILE_WIDTH};
use crate::tiles::{Tiler, TilingPathInfo};
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::render_target::RenderTargetId;
use pathfinder_geometry::line_segment::{LineSegment2F, LineSegmentU4, LineSegmentU8};
@ -38,9 +38,7 @@ use std::u16;
pub(crate) struct SceneBuilder<'a> {
scene: &'a Scene,
built_options: &'a PreparedBuildOptions,
next_alpha_tile_index: AtomicUsize,
pub(crate) listener: Box<dyn RenderCommandListener>,
}
@ -55,7 +53,7 @@ pub(crate) struct ObjectBuilder {
struct BuiltDrawPath {
path: BuiltPath,
blend_mode: BlendMode,
effects: Effects,
filter: Filter,
color_texture_page_0: TexturePageId,
color_texture_page_1: TexturePageId,
sampling_flags_0: TextureSamplingFlags,
@ -126,9 +124,9 @@ impl<'a> SceneBuilder<'a> {
let PaintInfo {
render_commands,
paint_metadata,
render_target_metadata,
opacity_tile_page,
opacity_tile_transform,
render_target_metadata: _
} = self.scene.build_paint_info(render_transform);
for render_command in render_commands {
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;
self.listener.send(RenderCommand::Finish { build_time });
@ -220,7 +218,7 @@ impl<'a> SceneBuilder<'a> {
BuiltDrawPath {
path: tiler.object_builder.built_path,
blend_mode: path_object.blend_mode(),
effects: paint_metadata.effects(),
filter: paint_metadata.filter(),
color_texture_page_0: paint_metadata.location.page,
sampling_flags_0: paint_metadata.sampling_flags,
color_texture_page_1: opacity_tile_page,
@ -230,10 +228,7 @@ impl<'a> SceneBuilder<'a> {
}
}
fn cull_tiles(&self,
paint_metadata: &[PaintMetadata],
render_target_metadata: &[RenderTargetMetadata],
built_draw_paths: Vec<BuiltDrawPath>)
fn cull_tiles(&self, paint_metadata: &[PaintMetadata], built_draw_paths: Vec<BuiltDrawPath>)
-> CulledTiles {
let mut culled_tiles = CulledTiles { display_list: vec![] };
@ -269,40 +264,6 @@ impl<'a> SceneBuilder<'a> {
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 {
start_index: start_draw_path_index,
end_index: end_draw_path_index,
@ -328,7 +289,7 @@ impl<'a> SceneBuilder<'a> {
None,
None,
built_draw_path.blend_mode,
built_draw_path.effects,
built_draw_path.filter,
None,
None);
@ -339,7 +300,7 @@ impl<'a> SceneBuilder<'a> {
color_texture_0,
color_texture_1,
built_draw_path.blend_mode,
built_draw_path.effects,
built_draw_path.filter,
Some(built_draw_path.mask_0_fill_rule),
None);
@ -351,7 +312,7 @@ impl<'a> SceneBuilder<'a> {
color_texture_0,
color_texture_1,
built_draw_path.blend_mode,
built_draw_path.effects,
built_draw_path.filter,
Some(built_draw_path.mask_0_fill_rule),
Some(mask_1_fill_rule));
}
@ -365,7 +326,7 @@ impl<'a> SceneBuilder<'a> {
color_texture_0,
color_texture_1,
built_draw_path.blend_mode,
built_draw_path.effects,
built_draw_path.filter,
None,
None);
}
@ -416,10 +377,6 @@ impl<'a> SceneBuilder<'a> {
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);
@ -435,7 +392,7 @@ impl<'a> SceneBuilder<'a> {
color_texture_0: Option<TileBatchTexture>,
color_texture_1: Option<TileBatchTexture>,
blend_mode: BlendMode,
effects: Effects,
filter: Filter,
mask_0_fill_rule: Option<FillRule>,
mask_1_fill_rule: Option<FillRule>) {
if alpha_tiles.is_empty() {
@ -454,13 +411,13 @@ impl<'a> SceneBuilder<'a> {
color_texture_0: ref batch_color_texture_0,
color_texture_1: ref batch_color_texture_1,
blend_mode: batch_blend_mode,
effects: batch_effects,
filter: batch_filter,
mask_0_fill_rule: batch_mask_0_fill_rule,
mask_1_fill_rule: batch_mask_1_fill_rule,
})) if *batch_color_texture_0 == color_texture_0 &&
*batch_color_texture_1 == color_texture_1 &&
batch_blend_mode == blend_mode &&
batch_effects == effects &&
batch_filter == filter &&
batch_mask_0_fill_rule == mask_0_fill_rule &&
batch_mask_1_fill_rule == mask_1_fill_rule &&
!batch_blend_mode.needs_readable_framebuffer() => {}
@ -470,7 +427,7 @@ impl<'a> SceneBuilder<'a> {
color_texture_0,
color_texture_1,
blend_mode,
effects,
filter,
mask_0_fill_rule,
mask_1_fill_rule,
};
@ -512,12 +469,9 @@ impl<'a> SceneBuilder<'a> {
fn finish_building(&mut self,
paint_metadata: &[PaintMetadata],
render_target_metadata: &[RenderTargetMetadata],
built_draw_paths: Vec<BuiltDrawPath>) {
self.listener.send(RenderCommand::FlushFills);
let culled_tiles = self.cull_tiles(paint_metadata,
render_target_metadata,
built_draw_paths);
let culled_tiles = self.cull_tiles(paint_metadata, built_draw_paths);
self.pack_tiles(culled_tiles);
}
@ -525,7 +479,6 @@ impl<'a> SceneBuilder<'a> {
let mut framebuffer_nesting = 0;
for display_item in &self.scene.display_list {
match *display_item {
DisplayItem::DrawRenderTarget { .. } => {}
DisplayItem::PushRenderTarget(_) => framebuffer_nesting += 1,
DisplayItem::PopRenderTarget => framebuffer_nesting -= 1,
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::tiles::{TILE_HEIGHT, TILE_WIDTH};
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::render_target::RenderTargetId;
use pathfinder_geometry::line_segment::LineSegment2F;
@ -313,7 +314,7 @@ where
batch.mask_0_fill_rule,
batch.mask_1_fill_rule,
batch.blend_mode,
batch.effects)
batch.filter)
}
RenderCommand::Finish { .. } => {}
}
@ -583,7 +584,7 @@ where
mask_0_fill_rule: Option<FillRule>,
mask_1_fill_rule: Option<FillRule>,
blend_mode: BlendMode,
effects: Effects) {
filter: Filter) {
// TODO(pcwalton): Disable blend for solid tiles.
let needs_readable_framebuffer = blend_mode.needs_readable_framebuffer();
@ -658,13 +659,18 @@ where
ctrl |= blend_mode.to_composite_ctrl() << COMBINER_CTRL_COMPOSITE_SHIFT;
match effects.filter {
match filter {
Filter::None => {}
Filter::RadialGradient { line, radii, uv_origin } => {
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)
}
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;
self.set_uniforms_for_text_filter(&mut textures,
&mut uniforms,
@ -673,7 +679,7 @@ where
defringing_kernel,
gamma_correction);
}
Filter::Blur { direction, sigma } => {
Filter::PatternFilter(PatternFilter::Blur { direction, sigma }) => {
ctrl |= COMBINER_CTRL_FILTER_BLUR << COMBINER_CTRL_COLOR_0_FILTER_SHIFT;
self.set_uniforms_for_blur_filter(&mut uniforms, direction, sigma);
}

View File

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

View File

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

View File

@ -15,7 +15,7 @@ use crate::concurrent::executor::Executor;
use crate::options::{BuildOptions, PreparedBuildOptions};
use crate::options::{PreparedRenderTransform, RenderCommandListener};
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::outline::Outline;
use pathfinder_content::pattern::{Pattern, PatternSource};
@ -87,10 +87,6 @@ impl Scene {
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) {
// Merge render targets.
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() {
let old_paint_id = PaintId(old_paint_index as u16);
let new_paint_id = match old_paint {
Paint::Pattern(Pattern {
source: PatternSource::RenderTarget { id: old_render_target_id, size },
transform,
flags
}) => {
self.palette.push_paint(&Paint::Pattern(Pattern {
source: PatternSource::RenderTarget {
id: render_target_mapping[old_render_target_id],
size: *size,
},
transform: *transform,
flags: *flags,
}))
Paint::Pattern(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.palette.push_paint(&Paint::Pattern(new_pattern))
}
_ => self.palette.push_paint(old_paint),
}
}
paint => self.palette.push_paint(paint),
};
@ -154,13 +151,6 @@ impl Scene {
// Merge display items.
for display_item in scene.display_list {
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) => {
let new_render_target_id = render_target_mapping[&old_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.
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.
PushRenderTarget(RenderTargetId),

View File

@ -15,7 +15,7 @@ use crate::gpu_data::{Tile, TileBatch, TileBatchTexture, TileVertex};
use crate::paint::{PaintId, PaintMetadata};
use crate::tile_map::DenseTileMap;
use crate::tiles;
use pathfinder_content::effects::{BlendMode, Effects};
use pathfinder_content::effects::{BlendMode, Filter};
use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::vector::{Vector2F, Vector2I, vec2i};
use vec_map::VecMap;
@ -94,7 +94,7 @@ impl ZBuffer {
}),
color_texture_1: None,
tiles: vec![],
effects: Effects::default(),
filter: Filter::None,
blend_mode: BlendMode::default(),
mask_0_fill_rule: None,
mask_1_fill_rule: None,
@ -124,19 +124,6 @@ impl Tile {
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 {