Transition the existing postprocessing system to a layers system.
This is preparatory work for composite ops and blurs. Closes #261.
This commit is contained in:
parent
25f9346160
commit
d9e994e46d
|
@ -142,6 +142,11 @@ impl ColorF {
|
|||
ColorF::default()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn black() -> ColorF {
|
||||
ColorF(F32x4::new(0.0, 0.0, 0.0, 1.0))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn white() -> ColorF {
|
||||
ColorF(F32x4::splat(1.0))
|
||||
|
|
|
@ -22,7 +22,6 @@ 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_color::ColorU;
|
||||
use pathfinder_export::{Export, FileFormat};
|
||||
use pathfinder_geometry::rect::RectF;
|
||||
use pathfinder_geometry::transform2d::Transform2F;
|
||||
|
@ -32,9 +31,9 @@ use pathfinder_gpu::resources::ResourceLoader;
|
|||
use pathfinder_gpu::Device;
|
||||
use pathfinder_renderer::concurrent::scene_proxy::{RenderCommandStream, SceneProxy};
|
||||
use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererOptions};
|
||||
use pathfinder_renderer::gpu::renderer::{RenderStats, RenderTime, Renderer};
|
||||
use pathfinder_renderer::gpu::renderer::{PostprocessOptions, RenderStats, RenderTime, Renderer};
|
||||
use pathfinder_renderer::options::{BuildOptions, RenderTransform};
|
||||
use pathfinder_renderer::post::STEM_DARKENING_FACTORS;
|
||||
use pathfinder_renderer::post::{DEFRINGING_KERNEL_CORE_GRAPHICS, STEM_DARKENING_FACTORS};
|
||||
use pathfinder_renderer::scene::Scene;
|
||||
use pathfinder_svg::BuiltSVG;
|
||||
use pathfinder_ui::{MousePosition, UIEvent};
|
||||
|
@ -63,25 +62,6 @@ const CAMERA_ZOOM_AMOUNT_2D: f32 = 0.1;
|
|||
// Half of the eye separation distance.
|
||||
const DEFAULT_EYE_OFFSET: f32 = 0.025;
|
||||
|
||||
const LIGHT_BG_COLOR: ColorU = ColorU {
|
||||
r: 248,
|
||||
g: 248,
|
||||
b: 248,
|
||||
a: 255,
|
||||
};
|
||||
const DARK_BG_COLOR: ColorU = ColorU {
|
||||
r: 32,
|
||||
g: 32,
|
||||
b: 32,
|
||||
a: 255,
|
||||
};
|
||||
const TRANSPARENT_BG_COLOR: ColorU = ColorU {
|
||||
r: 0,
|
||||
g: 0,
|
||||
b: 0,
|
||||
a: 0,
|
||||
};
|
||||
|
||||
const APPROX_FONT_SIZE: f32 = 16.0;
|
||||
|
||||
const MESSAGE_TIMEOUT_SECS: u64 = 5;
|
||||
|
@ -101,6 +81,7 @@ pub struct DemoApp<W> where W: Window {
|
|||
|
||||
window_size: WindowSize,
|
||||
|
||||
svg_tree: Tree,
|
||||
scene_metadata: SceneMetadata,
|
||||
render_transform: Option<RenderTransform>,
|
||||
render_command_stream: Option<RenderCommandStream>,
|
||||
|
@ -151,7 +132,12 @@ impl<W> DemoApp<W> where W: Window {
|
|||
// Set up the executor.
|
||||
let executor = DemoExecutor::new(options.jobs);
|
||||
|
||||
let mut built_svg = load_scene(resources, &options.input_path);
|
||||
let mut ui_model = DemoUIModel::new(&options);
|
||||
let render_options = RendererOptions { background_color: None };
|
||||
|
||||
let effects = build_effects(&ui_model);
|
||||
|
||||
let (mut built_svg, svg_tree) = load_scene(resources, &options.input_path, effects);
|
||||
let message = get_svg_building_message(&built_svg);
|
||||
|
||||
let viewport = window.viewport(options.mode.view(0));
|
||||
|
@ -159,12 +145,9 @@ impl<W> DemoApp<W> where W: Window {
|
|||
viewport,
|
||||
window_size: window_size.device_size(),
|
||||
};
|
||||
// FIXME(pcwalton)
|
||||
let render_options = RendererOptions {
|
||||
background_color: None,
|
||||
};
|
||||
|
||||
let renderer = Renderer::new(device, resources, dest_framebuffer, render_options);
|
||||
|
||||
let scene_metadata = SceneMetadata::new_clipping_view_box(&mut built_svg.scene,
|
||||
viewport.size());
|
||||
let camera = Camera::new(options.mode, scene_metadata.view_box, viewport.size());
|
||||
|
@ -177,8 +160,6 @@ impl<W> DemoApp<W> where W: Window {
|
|||
&renderer.quad_vertex_positions_buffer(),
|
||||
&renderer.quad_vertex_indices_buffer());
|
||||
|
||||
let mut ui_model = DemoUIModel::new(&options);
|
||||
|
||||
let mut message_epoch = 0;
|
||||
emit_message::<W>(
|
||||
&mut ui_model,
|
||||
|
@ -196,6 +177,7 @@ impl<W> DemoApp<W> where W: Window {
|
|||
|
||||
window_size,
|
||||
|
||||
svg_tree,
|
||||
scene_metadata,
|
||||
render_transform: None,
|
||||
render_command_stream: None,
|
||||
|
@ -445,7 +427,11 @@ impl<W> DemoApp<W> where W: Window {
|
|||
}
|
||||
|
||||
Event::OpenSVG(ref svg_path) => {
|
||||
let mut built_svg = load_scene(self.window.resource_loader(), svg_path);
|
||||
let effects = build_effects(&self.ui_model);
|
||||
let (mut built_svg, svg_tree) = load_scene(self.window.resource_loader(),
|
||||
svg_path,
|
||||
effects);
|
||||
|
||||
self.ui_model.message = get_svg_building_message(&built_svg);
|
||||
|
||||
let viewport_size = self.window.viewport(self.ui_model.mode.view(0)).size();
|
||||
|
@ -456,6 +442,7 @@ impl<W> DemoApp<W> where W: Window {
|
|||
viewport_size);
|
||||
|
||||
self.scene_proxy.replace_scene(built_svg.scene);
|
||||
self.svg_tree = svg_tree;
|
||||
|
||||
self.dirty = true;
|
||||
}
|
||||
|
@ -499,8 +486,6 @@ impl<W> DemoApp<W> where W: Window {
|
|||
.to_f32()
|
||||
.scale(self.window_size.backing_scale_factor);
|
||||
|
||||
self.ui_presenter.set_show_text_effects(self.scene_metadata.monochrome_color.is_some());
|
||||
|
||||
let mut ui_action = UIAction::None;
|
||||
if self.options.ui == UIVisibility::All {
|
||||
self.ui_presenter.update(
|
||||
|
@ -599,6 +584,15 @@ impl<W> DemoApp<W> where W: Window {
|
|||
match ui_action {
|
||||
UIAction::None => {}
|
||||
UIAction::ModelChanged => self.dirty = true,
|
||||
UIAction::EffectsChanged => {
|
||||
let effects = build_effects(&self.ui_model);
|
||||
let mut built_svg = build_svg_tree(&self.svg_tree, effects);
|
||||
let viewport_size = self.window.viewport(self.ui_model.mode.view(0)).size();
|
||||
self.scene_metadata =
|
||||
SceneMetadata::new_clipping_view_box(&mut built_svg.scene, viewport_size);
|
||||
self.scene_proxy.replace_scene(built_svg.scene);
|
||||
self.dirty = true;
|
||||
}
|
||||
UIAction::TakeScreenshot(ref info) => {
|
||||
self.pending_screenshot_info = Some((*info).clone());
|
||||
self.dirty = true;
|
||||
|
@ -636,14 +630,6 @@ impl<W> DemoApp<W> where W: Window {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn background_color(&self) -> ColorU {
|
||||
match self.ui_model.background_color {
|
||||
BackgroundColor::Light => LIGHT_BG_COLOR,
|
||||
BackgroundColor::Dark => DARK_BG_COLOR,
|
||||
BackgroundColor::Transparent => TRANSPARENT_BG_COLOR,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -756,7 +742,10 @@ pub enum UIVisibility {
|
|||
All,
|
||||
}
|
||||
|
||||
fn load_scene(resource_loader: &dyn ResourceLoader, input_path: &SVGPath) -> BuiltSVG {
|
||||
fn load_scene(resource_loader: &dyn ResourceLoader,
|
||||
input_path: &SVGPath,
|
||||
effects: Option<PostprocessOptions>)
|
||||
-> (BuiltSVG, Tree) {
|
||||
let mut data;
|
||||
match *input_path {
|
||||
SVGPath::Default => data = resource_loader.slurp(DEFAULT_SVG_VIRTUAL_PATH).unwrap(),
|
||||
|
@ -767,7 +756,24 @@ fn load_scene(resource_loader: &dyn ResourceLoader, input_path: &SVGPath) -> Bui
|
|||
}
|
||||
};
|
||||
|
||||
BuiltSVG::from_tree(Tree::from_data(&data, &UsvgOptions::default()).unwrap())
|
||||
let tree = Tree::from_data(&data, &UsvgOptions::default()).expect("Failed to parse the SVG!");
|
||||
let built_svg = build_svg_tree(&tree, effects);
|
||||
(built_svg, tree)
|
||||
}
|
||||
|
||||
fn build_svg_tree(tree: &Tree, effects: Option<PostprocessOptions>) -> BuiltSVG {
|
||||
let mut scene = Scene::new();
|
||||
if let Some(effects) = effects {
|
||||
scene.push_layer(effects);
|
||||
}
|
||||
|
||||
let mut built_svg = BuiltSVG::from_tree_and_scene(&tree, scene);
|
||||
|
||||
if effects.is_some() {
|
||||
built_svg.scene.pop_layer();
|
||||
}
|
||||
|
||||
built_svg
|
||||
}
|
||||
|
||||
fn center_of_window(window_size: &WindowSize) -> Vector2F {
|
||||
|
@ -842,7 +848,6 @@ impl BackgroundColor {
|
|||
|
||||
struct SceneMetadata {
|
||||
view_box: RectF,
|
||||
monochrome_color: Option<ColorU>,
|
||||
}
|
||||
|
||||
impl SceneMetadata {
|
||||
|
@ -850,8 +855,25 @@ impl SceneMetadata {
|
|||
// Can we simplify this?
|
||||
fn new_clipping_view_box(scene: &mut Scene, viewport_size: Vector2I) -> SceneMetadata {
|
||||
let view_box = scene.view_box();
|
||||
let monochrome_color = scene.monochrome_color();
|
||||
scene.set_view_box(RectF::new(Vector2F::default(), viewport_size.to_f32()));
|
||||
SceneMetadata { view_box, monochrome_color }
|
||||
SceneMetadata { view_box }
|
||||
}
|
||||
}
|
||||
|
||||
fn build_effects(ui_model: &DemoUIModel) -> Option<PostprocessOptions> {
|
||||
if !ui_model.gamma_correction_effect_enabled && !ui_model.subpixel_aa_effect_enabled {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(PostprocessOptions {
|
||||
fg_color: ui_model.foreground_color().to_f32(),
|
||||
bg_color: ui_model.background_color().to_f32(),
|
||||
gamma_correction: ui_model.gamma_correction_effect_enabled,
|
||||
defringing_kernel: if ui_model.subpixel_aa_effect_enabled {
|
||||
// TODO(pcwalton): Select FreeType defringing kernel as necessary.
|
||||
Some(DEFRINGING_KERNEL_CORE_GRAPHICS)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
|
@ -21,10 +21,8 @@ use pathfinder_geometry::rect::RectI;
|
|||
use pathfinder_geometry::transform3d::Transform4F;
|
||||
use pathfinder_geometry::vector::{Vector2I, Vector4F};
|
||||
use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererOptions};
|
||||
use pathfinder_renderer::gpu::renderer::PostprocessOptions;
|
||||
use pathfinder_renderer::gpu_data::RenderCommand;
|
||||
use pathfinder_renderer::options::RenderTransform;
|
||||
use pathfinder_renderer::post::DEFRINGING_KERNEL_CORE_GRAPHICS;
|
||||
use std::path::PathBuf;
|
||||
|
||||
const GROUND_SOLID_COLOR: ColorU = ColorU {
|
||||
|
@ -88,7 +86,7 @@ impl<W> DemoApp<W> where W: Window {
|
|||
|
||||
// Clear to the appropriate color.
|
||||
let clear_color = match mode {
|
||||
Mode::TwoD => Some(self.background_color().to_f32()),
|
||||
Mode::TwoD => Some(self.ui_model.background_color().to_f32()),
|
||||
Mode::ThreeD => None,
|
||||
Mode::VR => Some(ColorF::transparent_black()),
|
||||
};
|
||||
|
@ -221,7 +219,7 @@ impl<W> DemoApp<W> where W: Window {
|
|||
|
||||
// Don't clear the first scene after drawing it.
|
||||
let clear_color = if render_scene_index == 0 {
|
||||
Some(self.background_color().to_f32())
|
||||
Some(self.ui_model.background_color().to_f32())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
@ -251,23 +249,6 @@ impl<W> DemoApp<W> where W: Window {
|
|||
}
|
||||
|
||||
fn render_vector_scene(&mut self) {
|
||||
match self.scene_metadata.monochrome_color {
|
||||
None => self.renderer.set_postprocess_options(None),
|
||||
Some(fg_color) => {
|
||||
self.renderer.set_postprocess_options(Some(PostprocessOptions {
|
||||
fg_color: fg_color.to_f32(),
|
||||
bg_color: self.background_color().to_f32(),
|
||||
gamma_correction: self.ui_model.gamma_correction_effect_enabled,
|
||||
defringing_kernel: if self.ui_model.subpixel_aa_effect_enabled {
|
||||
// TODO(pcwalton): Select FreeType defringing kernel as necessary.
|
||||
Some(DEFRINGING_KERNEL_CORE_GRAPHICS)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
if self.ui_model.mode == Mode::TwoD {
|
||||
self.renderer.disable_depth();
|
||||
} else {
|
||||
|
|
|
@ -11,8 +11,9 @@
|
|||
use crate::camera::Mode;
|
||||
use crate::window::Window;
|
||||
use crate::{BackgroundColor, Options};
|
||||
use pathfinder_geometry::vector::Vector2I;
|
||||
use pathfinder_color::ColorU;
|
||||
use pathfinder_geometry::rect::RectI;
|
||||
use pathfinder_geometry::vector::Vector2I;
|
||||
use pathfinder_gpu::resources::ResourceLoader;
|
||||
use pathfinder_gpu::Device;
|
||||
use pathfinder_renderer::gpu::debug::DebugUIPresenter;
|
||||
|
@ -39,6 +40,10 @@ const SCREENSHOT_PANEL_HEIGHT: i32 = BUTTON_HEIGHT * 2;
|
|||
const ROTATE_PANEL_WIDTH: i32 = SLIDER_WIDTH + PADDING * 2;
|
||||
const ROTATE_PANEL_HEIGHT: i32 = PADDING * 2 + SLIDER_HEIGHT;
|
||||
|
||||
const LIGHT_BG_COLOR: ColorU = ColorU { r: 248, g: 248, b: 248, a: 255, };
|
||||
const DARK_BG_COLOR: ColorU = ColorU { r: 32, g: 32, b: 32, a: 255, };
|
||||
const TRANSPARENT_BG_COLOR: ColorU = ColorU { r: 0, g: 0, b: 0, a: 0, };
|
||||
|
||||
static EFFECTS_PNG_NAME: &'static str = "demo-effects";
|
||||
static OPEN_PNG_NAME: &'static str = "demo-open";
|
||||
static ROTATE_PNG_NAME: &'static str = "demo-rotate";
|
||||
|
@ -74,6 +79,22 @@ impl DemoUIModel {
|
|||
fn rotation(&self) -> f32 {
|
||||
(self.rotation as f32 / SLIDER_WIDTH as f32 * 2.0 - 1.0) * PI
|
||||
}
|
||||
|
||||
// Only relevant if in monochrome mode.
|
||||
pub fn foreground_color(&self) -> ColorU {
|
||||
match self.background_color {
|
||||
BackgroundColor::Light | BackgroundColor::Transparent => ColorU::black(),
|
||||
BackgroundColor::Dark => ColorU::white(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn background_color(&self) -> ColorU {
|
||||
match self.background_color {
|
||||
BackgroundColor::Light => LIGHT_BG_COLOR,
|
||||
BackgroundColor::Dark => DARK_BG_COLOR,
|
||||
BackgroundColor::Transparent => TRANSPARENT_BG_COLOR,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DemoUIPresenter<D>
|
||||
|
@ -93,8 +114,6 @@ where
|
|||
background_panel_visible: bool,
|
||||
screenshot_panel_visible: bool,
|
||||
rotate_panel_visible: bool,
|
||||
|
||||
show_text_effects: bool,
|
||||
}
|
||||
|
||||
impl<D> DemoUIPresenter<D>
|
||||
|
@ -126,15 +145,9 @@ where
|
|||
background_panel_visible: false,
|
||||
screenshot_panel_visible: false,
|
||||
rotate_panel_visible: false,
|
||||
|
||||
show_text_effects: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_show_text_effects(&mut self, show_text_effects: bool) {
|
||||
self.show_text_effects = show_text_effects;
|
||||
}
|
||||
|
||||
pub fn update<W>(
|
||||
&mut self,
|
||||
device: &D,
|
||||
|
@ -156,22 +169,18 @@ where
|
|||
|
||||
let button_size = Vector2I::new(BUTTON_WIDTH, BUTTON_HEIGHT);
|
||||
|
||||
// Draw text effects button.
|
||||
if self.show_text_effects {
|
||||
if debug_ui_presenter.ui_presenter.draw_button(device,
|
||||
position,
|
||||
&self.effects_texture) {
|
||||
// Draw effects button.
|
||||
if debug_ui_presenter.ui_presenter.draw_button(device, position, &self.effects_texture) {
|
||||
self.effects_panel_visible = !self.effects_panel_visible;
|
||||
}
|
||||
if !self.effects_panel_visible {
|
||||
debug_ui_presenter.ui_presenter.draw_tooltip(
|
||||
device,
|
||||
"Text Effects",
|
||||
"Effects",
|
||||
RectI::new(position, button_size),
|
||||
);
|
||||
}
|
||||
position += Vector2I::new(button_size.x() + PADDING, 0);
|
||||
}
|
||||
|
||||
// Draw open button.
|
||||
if debug_ui_presenter.ui_presenter.draw_button(device, position, &self.open_texture) {
|
||||
|
@ -245,7 +254,7 @@ where
|
|||
position += Vector2I::new(button_size.x() + PADDING, 0);
|
||||
|
||||
// Draw effects panel, if necessary.
|
||||
self.draw_effects_panel(device, debug_ui_presenter, model);
|
||||
self.draw_effects_panel(device, debug_ui_presenter, model, action);
|
||||
|
||||
// Draw rotate and zoom buttons, if applicable.
|
||||
if model.mode != Mode::TwoD {
|
||||
|
@ -324,7 +333,8 @@ where
|
|||
fn draw_effects_panel(&mut self,
|
||||
device: &D,
|
||||
debug_ui_presenter: &mut DebugUIPresenter<D>,
|
||||
model: &mut DemoUIModel) {
|
||||
model: &mut DemoUIModel,
|
||||
action: &mut UIAction) {
|
||||
if !self.effects_panel_visible {
|
||||
return;
|
||||
}
|
||||
|
@ -340,30 +350,30 @@ where
|
|||
WINDOW_COLOR,
|
||||
);
|
||||
|
||||
model.gamma_correction_effect_enabled = self.draw_effects_switch(
|
||||
self.draw_effects_switch(
|
||||
device,
|
||||
action,
|
||||
debug_ui_presenter,
|
||||
"Gamma Correction",
|
||||
0,
|
||||
effects_panel_y,
|
||||
model.gamma_correction_effect_enabled,
|
||||
);
|
||||
model.stem_darkening_effect_enabled = self.draw_effects_switch(
|
||||
&mut model.gamma_correction_effect_enabled);
|
||||
self.draw_effects_switch(
|
||||
device,
|
||||
action,
|
||||
debug_ui_presenter,
|
||||
"Stem Darkening",
|
||||
1,
|
||||
effects_panel_y,
|
||||
model.stem_darkening_effect_enabled,
|
||||
);
|
||||
model.subpixel_aa_effect_enabled = self.draw_effects_switch(
|
||||
&mut model.stem_darkening_effect_enabled);
|
||||
self.draw_effects_switch(
|
||||
device,
|
||||
action,
|
||||
debug_ui_presenter,
|
||||
"Subpixel AA",
|
||||
2,
|
||||
effects_panel_y,
|
||||
model.subpixel_aa_effect_enabled,
|
||||
);
|
||||
&mut model.subpixel_aa_effect_enabled);
|
||||
}
|
||||
|
||||
fn draw_screenshot_panel<W>(
|
||||
|
@ -592,12 +602,12 @@ where
|
|||
fn draw_effects_switch(
|
||||
&self,
|
||||
device: &D,
|
||||
action: &mut UIAction,
|
||||
debug_ui_presenter: &mut DebugUIPresenter<D>,
|
||||
text: &str,
|
||||
index: i32,
|
||||
window_y: i32,
|
||||
value: bool,
|
||||
) -> bool {
|
||||
value: &mut bool) {
|
||||
let text_x = PADDING * 2;
|
||||
let text_y = window_y + PADDING + BUTTON_TEXT_OFFSET + (BUTTON_HEIGHT + PADDING) * index;
|
||||
debug_ui_presenter
|
||||
|
@ -608,10 +618,16 @@ where
|
|||
let switch_x = PADDING + EFFECTS_PANEL_WIDTH - (switch_width + PADDING);
|
||||
let switch_y = window_y + PADDING + (BUTTON_HEIGHT + PADDING) * index;
|
||||
let switch_position = Vector2I::new(switch_x, switch_y);
|
||||
|
||||
let new_value =
|
||||
debug_ui_presenter
|
||||
.ui_presenter
|
||||
.draw_text_switch(device, switch_position, &["Off", "On"], value as u8)
|
||||
!= 0
|
||||
.draw_text_switch(device, switch_position, &["Off", "On"], *value as u8) != 0;
|
||||
|
||||
if new_value != *value {
|
||||
*action = UIAction::EffectsChanged;
|
||||
*value = new_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -619,6 +635,7 @@ where
|
|||
pub enum UIAction {
|
||||
None,
|
||||
ModelChanged,
|
||||
EffectsChanged,
|
||||
TakeScreenshot(ScreenshotInfo),
|
||||
ZoomIn,
|
||||
ZoomActualSize,
|
||||
|
|
|
@ -478,6 +478,11 @@ impl Device for GLDevice {
|
|||
&framebuffer.texture
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn texture_format(&self, texture: &Self::Texture) -> TextureFormat {
|
||||
texture.format
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn texture_size(&self, texture: &Self::Texture) -> Vector2I {
|
||||
texture.size
|
||||
|
|
|
@ -70,6 +70,7 @@ pub trait Device: Sized {
|
|||
mode: BufferUploadMode,
|
||||
);
|
||||
fn framebuffer_texture<'f>(&self, framebuffer: &'f Self::Framebuffer) -> &'f Self::Texture;
|
||||
fn texture_format(&self, texture: &Self::Texture) -> TextureFormat;
|
||||
fn texture_size(&self, texture: &Self::Texture) -> Vector2I;
|
||||
fn upload_to_texture(&self, texture: &Self::Texture, rect: RectI, data: TextureDataRef);
|
||||
fn read_pixels(&self, target: &RenderTarget<Self>, viewport: RectI)
|
||||
|
|
|
@ -437,6 +437,17 @@ impl Device for MetalDevice {
|
|||
&framebuffer.0
|
||||
}
|
||||
|
||||
fn texture_format(&self, texture: &MetalTexture) -> TextureFormat {
|
||||
match texture.texture.pixel_format() {
|
||||
MTLPixelFormat::R8Unorm => TextureFormat::R8,
|
||||
MTLPixelFormat::R16Float => TextureFormat::R16F,
|
||||
MTLPixelFormat::RGBA8Unorm => TextureFormat::RGBA8,
|
||||
MTLPixelFormat::RGBA16Float => TextureFormat::RGBA16F,
|
||||
MTLPixelFormat::RGBA32Float => TextureFormat::RGBA32F,
|
||||
_ => panic!("Unexpected Metal texture format!"),
|
||||
}
|
||||
}
|
||||
|
||||
fn texture_size(&self, texture: &MetalTexture) -> Vector2I {
|
||||
Vector2I::new(texture.texture.width() as i32, texture.texture.height() as i32)
|
||||
}
|
||||
|
|
|
@ -11,12 +11,12 @@
|
|||
//! Packs data onto the GPU.
|
||||
|
||||
use crate::concurrent::executor::Executor;
|
||||
use crate::gpu::renderer::MASK_TILES_ACROSS;
|
||||
use crate::gpu::renderer::{MASK_TILES_ACROSS, PostprocessOptions};
|
||||
use crate::gpu_data::{AlphaTile, AlphaTileVertex, FillBatchPrimitive, MaskTile, MaskTileVertex};
|
||||
use crate::gpu_data::{RenderCommand, TileObjectPrimitive};
|
||||
use crate::gpu_data::{RenderCommand, SolidTileVertex, TileObjectPrimitive};
|
||||
use crate::options::{PreparedBuildOptions, RenderCommandListener};
|
||||
use crate::paint::{PaintInfo, PaintMetadata};
|
||||
use crate::scene::Scene;
|
||||
use crate::scene::{DisplayItem, Scene};
|
||||
use crate::tile_map::DenseTileMap;
|
||||
use crate::tiles::{self, TILE_HEIGHT, TILE_WIDTH, Tiler, TilingPathInfo};
|
||||
use crate::z_buffer::ZBuffer;
|
||||
|
@ -37,7 +37,6 @@ pub(crate) struct SceneBuilder<'a> {
|
|||
next_alpha_tile_index: AtomicUsize,
|
||||
next_mask_tile_index: AtomicUsize,
|
||||
|
||||
pub(crate) z_buffer: ZBuffer,
|
||||
pub(crate) listener: Box<dyn RenderCommandListener>,
|
||||
}
|
||||
|
||||
|
@ -52,17 +51,22 @@ pub(crate) struct ObjectBuilder {
|
|||
pub(crate) struct BuiltPath {
|
||||
pub mask_tiles: Vec<MaskTile>,
|
||||
pub alpha_tiles: Vec<AlphaTile>,
|
||||
pub solid_tiles: Vec<SolidTile>,
|
||||
pub tiles: DenseTileMap<TileObjectPrimitive>,
|
||||
pub fill_rule: FillRule,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub(crate) struct SolidTile {
|
||||
pub(crate) coords: Vector2I,
|
||||
}
|
||||
|
||||
impl<'a> SceneBuilder<'a> {
|
||||
pub(crate) fn new(
|
||||
scene: &'a Scene,
|
||||
built_options: &'a PreparedBuildOptions,
|
||||
listener: Box<dyn RenderCommandListener>,
|
||||
) -> SceneBuilder<'a> {
|
||||
let effective_view_box = scene.effective_view_box(built_options);
|
||||
SceneBuilder {
|
||||
scene,
|
||||
built_options,
|
||||
|
@ -70,7 +74,6 @@ impl<'a> SceneBuilder<'a> {
|
|||
next_alpha_tile_index: AtomicUsize::new(0),
|
||||
next_mask_tile_index: AtomicUsize::new(0),
|
||||
|
||||
z_buffer: ZBuffer::new(effective_view_box),
|
||||
listener,
|
||||
}
|
||||
}
|
||||
|
@ -167,26 +170,84 @@ impl<'a> SceneBuilder<'a> {
|
|||
tiler.object_builder.built_path
|
||||
}
|
||||
|
||||
fn cull_tiles(&self, built_clip_paths: Vec<BuiltPath>, built_draw_paths: Vec<BuiltPath>)
|
||||
fn cull_tiles(&self,
|
||||
paint_metadata: &[PaintMetadata],
|
||||
built_clip_paths: Vec<BuiltPath>,
|
||||
built_draw_paths: Vec<BuiltPath>)
|
||||
-> CulledTiles {
|
||||
let mut culled_tiles = CulledTiles {
|
||||
mask_winding_tiles: vec![],
|
||||
mask_evenodd_tiles: vec![],
|
||||
alpha_tiles: vec![],
|
||||
display_list: vec![],
|
||||
};
|
||||
|
||||
for built_clip_path in built_clip_paths {
|
||||
culled_tiles.push_mask_tiles(&built_clip_path);
|
||||
}
|
||||
|
||||
for built_draw_path in built_draw_paths {
|
||||
culled_tiles.push_mask_tiles(&built_draw_path);
|
||||
let mut remaining_layer_z_buffers = self.build_solid_tiles(&built_draw_paths);
|
||||
remaining_layer_z_buffers.reverse();
|
||||
|
||||
for alpha_tile in built_draw_path.alpha_tiles {
|
||||
// Process first Z-buffer.
|
||||
let first_z_buffer = remaining_layer_z_buffers.pop().unwrap();
|
||||
let first_solid_tiles = first_z_buffer.build_solid_tiles(&self.scene.paths,
|
||||
paint_metadata);
|
||||
if !first_solid_tiles.is_empty() {
|
||||
culled_tiles.display_list
|
||||
.push(CulledDisplayItem::DrawSolidTiles(first_solid_tiles));
|
||||
}
|
||||
|
||||
let mut layer_z_buffers_stack = vec![first_z_buffer];
|
||||
|
||||
for display_item in &self.scene.display_list {
|
||||
// Just pass through `PushLayer` and `PopLayer` commands.
|
||||
let (start_draw_path_index, end_draw_path_index) = match *display_item {
|
||||
DisplayItem::PushLayer { effects } => {
|
||||
culled_tiles.display_list.push(CulledDisplayItem::PushLayer { effects });
|
||||
|
||||
let z_buffer = remaining_layer_z_buffers.pop().unwrap();
|
||||
let solid_tiles = z_buffer.build_solid_tiles(&self.scene.paths,
|
||||
paint_metadata);
|
||||
if !solid_tiles.is_empty() {
|
||||
culled_tiles.display_list
|
||||
.push(CulledDisplayItem::DrawSolidTiles(solid_tiles));
|
||||
}
|
||||
layer_z_buffers_stack.push(z_buffer);
|
||||
continue;
|
||||
}
|
||||
DisplayItem::PopLayer => {
|
||||
culled_tiles.display_list.push(CulledDisplayItem::PopLayer);
|
||||
layer_z_buffers_stack.pop();
|
||||
continue;
|
||||
}
|
||||
DisplayItem::DrawPaths { start_index, end_index } => (start_index, end_index),
|
||||
};
|
||||
|
||||
for draw_path_index in start_draw_path_index..end_draw_path_index {
|
||||
let built_draw_path = &built_draw_paths[draw_path_index as usize];
|
||||
culled_tiles.push_mask_tiles(built_draw_path);
|
||||
|
||||
// Create a `DrawAlphaTiles` display item if necessary.
|
||||
match culled_tiles.display_list.last() {
|
||||
Some(&CulledDisplayItem::DrawAlphaTiles(_)) => {}
|
||||
_ => culled_tiles.display_list.push(CulledDisplayItem::DrawAlphaTiles(vec![])),
|
||||
}
|
||||
|
||||
// Fetch the destination alpha tiles buffer.
|
||||
let culled_alpha_tiles = match *culled_tiles.display_list.last_mut().unwrap() {
|
||||
CulledDisplayItem::DrawAlphaTiles(ref mut culled_alpha_tiles) => {
|
||||
culled_alpha_tiles
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let layer_z_buffer = layer_z_buffers_stack.last().unwrap();
|
||||
for alpha_tile in &built_draw_path.alpha_tiles {
|
||||
let alpha_tile_coords = alpha_tile.upper_left.tile_position();
|
||||
if self.z_buffer.test(alpha_tile_coords,
|
||||
if layer_z_buffer.test(alpha_tile_coords,
|
||||
alpha_tile.upper_left.object_index as u32) {
|
||||
culled_tiles.alpha_tiles.push(alpha_tile);
|
||||
culled_alpha_tiles.push(*alpha_tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -194,12 +255,37 @@ impl<'a> SceneBuilder<'a> {
|
|||
culled_tiles
|
||||
}
|
||||
|
||||
fn pack_tiles(&mut self, paint_metadata: &[PaintMetadata], culled_tiles: CulledTiles) {
|
||||
let path_count = self.scene.paths.len() as u32;
|
||||
let solid_tiles = self.z_buffer.build_solid_tiles(&self.scene.paths,
|
||||
paint_metadata,
|
||||
0..path_count);
|
||||
fn build_solid_tiles(&self, built_draw_paths: &[BuiltPath]) -> Vec<ZBuffer> {
|
||||
let effective_view_box = self.scene.effective_view_box(self.built_options);
|
||||
let mut z_buffers = vec![ZBuffer::new(effective_view_box)];
|
||||
let mut z_buffer_index_stack = vec![0];
|
||||
|
||||
// Create Z-buffers.
|
||||
for display_item in &self.scene.display_list {
|
||||
match *display_item {
|
||||
DisplayItem::PushLayer { .. } => {
|
||||
z_buffer_index_stack.push(z_buffers.len());
|
||||
z_buffers.push(ZBuffer::new(effective_view_box));
|
||||
}
|
||||
DisplayItem::PopLayer => {
|
||||
z_buffer_index_stack.pop();
|
||||
}
|
||||
DisplayItem::DrawPaths { start_index, end_index } => {
|
||||
let (start_index, end_index) = (start_index as usize, end_index as usize);
|
||||
let z_buffer = &mut z_buffers[*z_buffer_index_stack.last().unwrap()];
|
||||
for (path_index, built_draw_path) in
|
||||
built_draw_paths[start_index..end_index].iter().enumerate() {
|
||||
z_buffer.update(&built_draw_path.solid_tiles, path_index as u32);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
debug_assert_eq!(z_buffer_index_stack.len(), 1);
|
||||
|
||||
z_buffers
|
||||
}
|
||||
|
||||
fn pack_tiles(&mut self, culled_tiles: CulledTiles) {
|
||||
if !culled_tiles.mask_winding_tiles.is_empty() {
|
||||
self.listener.send(RenderCommand::RenderMaskTiles {
|
||||
tiles: culled_tiles.mask_winding_tiles,
|
||||
|
@ -213,11 +299,19 @@ impl<'a> SceneBuilder<'a> {
|
|||
});
|
||||
}
|
||||
|
||||
if !solid_tiles.is_empty() {
|
||||
self.listener.send(RenderCommand::DrawSolidTiles(solid_tiles));
|
||||
for display_item in culled_tiles.display_list {
|
||||
match display_item {
|
||||
CulledDisplayItem::DrawSolidTiles(tiles) => {
|
||||
self.listener.send(RenderCommand::DrawSolidTiles(tiles))
|
||||
}
|
||||
CulledDisplayItem::DrawAlphaTiles(tiles) => {
|
||||
self.listener.send(RenderCommand::DrawAlphaTiles(tiles))
|
||||
}
|
||||
CulledDisplayItem::PushLayer { effects } => {
|
||||
self.listener.send(RenderCommand::PushLayer { effects })
|
||||
}
|
||||
CulledDisplayItem::PopLayer => self.listener.send(RenderCommand::PopLayer),
|
||||
}
|
||||
if !culled_tiles.alpha_tiles.is_empty() {
|
||||
self.listener.send(RenderCommand::DrawAlphaTiles(culled_tiles.alpha_tiles));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -226,8 +320,8 @@ impl<'a> SceneBuilder<'a> {
|
|||
built_clip_paths: Vec<BuiltPath>,
|
||||
built_draw_paths: Vec<BuiltPath>) {
|
||||
self.listener.send(RenderCommand::FlushFills);
|
||||
let culled_tiles = self.cull_tiles(built_clip_paths, built_draw_paths);
|
||||
self.pack_tiles(paint_metadata, culled_tiles);
|
||||
let culled_tiles = self.cull_tiles(paint_metadata, built_clip_paths, built_draw_paths);
|
||||
self.pack_tiles(culled_tiles);
|
||||
}
|
||||
|
||||
pub(crate) fn allocate_mask_tile_index(&self) -> u16 {
|
||||
|
@ -236,10 +330,36 @@ impl<'a> SceneBuilder<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl BuiltPath {
|
||||
fn new(bounds: RectF, fill_rule: FillRule) -> BuiltPath {
|
||||
BuiltPath {
|
||||
mask_tiles: vec![],
|
||||
alpha_tiles: vec![],
|
||||
solid_tiles: vec![],
|
||||
tiles: DenseTileMap::new(tiles::round_rect_out_to_tile_bounds(bounds)),
|
||||
fill_rule,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SolidTile {
|
||||
#[inline]
|
||||
pub(crate) fn new(coords: Vector2I) -> SolidTile {
|
||||
SolidTile { coords }
|
||||
}
|
||||
}
|
||||
|
||||
struct CulledTiles {
|
||||
mask_winding_tiles: Vec<MaskTile>,
|
||||
mask_evenodd_tiles: Vec<MaskTile>,
|
||||
alpha_tiles: Vec<AlphaTile>,
|
||||
display_list: Vec<CulledDisplayItem>,
|
||||
}
|
||||
|
||||
enum CulledDisplayItem {
|
||||
DrawSolidTiles(Vec<SolidTileVertex>),
|
||||
DrawAlphaTiles(Vec<AlphaTile>),
|
||||
PushLayer { effects: PostprocessOptions },
|
||||
PopLayer,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
|
@ -252,13 +372,7 @@ pub struct TileStats {
|
|||
|
||||
impl ObjectBuilder {
|
||||
pub(crate) fn new(bounds: RectF, fill_rule: FillRule) -> ObjectBuilder {
|
||||
let tile_rect = tiles::round_rect_out_to_tile_bounds(bounds);
|
||||
let tiles = DenseTileMap::new(tile_rect);
|
||||
ObjectBuilder {
|
||||
built_path: BuiltPath { mask_tiles: vec![], alpha_tiles: vec![], tiles, fill_rule },
|
||||
bounds,
|
||||
fills: vec![],
|
||||
}
|
||||
ObjectBuilder { built_path: BuiltPath::new(bounds, fill_rule), bounds, fills: vec![] }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
@ -19,7 +19,7 @@ use crate::gpu_data::{AlphaTile, FillBatchPrimitive, MaskTile, PaintData};
|
|||
use crate::gpu_data::{RenderCommand, SolidTileVertex};
|
||||
use crate::post::DefringingKernel;
|
||||
use crate::tiles::{TILE_HEIGHT, TILE_WIDTH};
|
||||
use pathfinder_color::{self as color, ColorF, ColorU};
|
||||
use pathfinder_color::{self as color, ColorF};
|
||||
use pathfinder_content::fill::FillRule;
|
||||
use pathfinder_geometry::vector::{Vector2I, Vector4F};
|
||||
use pathfinder_geometry::rect::RectI;
|
||||
|
@ -43,6 +43,8 @@ static QUAD_VERTEX_INDICES: [u32; 6] = [0, 1, 3, 1, 2, 3];
|
|||
pub(crate) const MASK_TILES_ACROSS: u32 = 256;
|
||||
pub(crate) const MASK_TILES_DOWN: u32 = 256;
|
||||
|
||||
const FRAMEBUFFER_CACHE_SIZE: usize = 8;
|
||||
|
||||
// FIXME(pcwalton): Shrink this again!
|
||||
const MASK_FRAMEBUFFER_WIDTH: i32 = TILE_WIDTH as i32 * MASK_TILES_ACROSS as i32;
|
||||
const MASK_FRAMEBUFFER_HEIGHT: i32 = TILE_HEIGHT as i32 * MASK_TILES_DOWN as i32;
|
||||
|
@ -75,9 +77,9 @@ where
|
|||
fill_framebuffer: D::Framebuffer,
|
||||
mask_framebuffer: D::Framebuffer,
|
||||
paint_texture: Option<D::Texture>,
|
||||
layer_framebuffer_stack: Vec<LayerFramebufferInfo<D>>,
|
||||
|
||||
// Postprocessing shader
|
||||
postprocess_source_framebuffer: Option<D::Framebuffer>,
|
||||
postprocess_program: PostprocessProgram<D>,
|
||||
postprocess_vertex_array: PostprocessVertexArray<D>,
|
||||
gamma_lut_texture: D::Texture,
|
||||
|
@ -93,6 +95,7 @@ where
|
|||
// Rendering state
|
||||
framebuffer_flags: FramebufferFlags,
|
||||
buffered_fills: Vec<FillBatchPrimitive>,
|
||||
framebuffer_cache: FramebufferCache<D>,
|
||||
|
||||
// Debug
|
||||
pub stats: RenderStats,
|
||||
|
@ -102,7 +105,6 @@ where
|
|||
pub debug_ui_presenter: DebugUIPresenter<D>,
|
||||
|
||||
// Extra info
|
||||
postprocess_options: Option<PostprocessOptions>,
|
||||
use_depth: bool,
|
||||
}
|
||||
|
||||
|
@ -225,8 +227,8 @@ where
|
|||
fill_framebuffer,
|
||||
mask_framebuffer,
|
||||
paint_texture: None,
|
||||
layer_framebuffer_stack: vec![],
|
||||
|
||||
postprocess_source_framebuffer: None,
|
||||
postprocess_program,
|
||||
postprocess_vertex_array,
|
||||
gamma_lut_texture,
|
||||
|
@ -245,8 +247,8 @@ where
|
|||
|
||||
framebuffer_flags: FramebufferFlags::empty(),
|
||||
buffered_fills: vec![],
|
||||
framebuffer_cache: FramebufferCache::new(),
|
||||
|
||||
postprocess_options: None,
|
||||
use_depth: false,
|
||||
}
|
||||
}
|
||||
|
@ -254,7 +256,6 @@ where
|
|||
pub fn begin_scene(&mut self) {
|
||||
self.framebuffer_flags = FramebufferFlags::empty();
|
||||
self.device.begin_commands();
|
||||
self.init_postprocessing_framebuffer();
|
||||
self.stats = RenderStats::default();
|
||||
}
|
||||
|
||||
|
@ -277,6 +278,8 @@ where
|
|||
self.upload_mask_tiles(mask_tiles, fill_rule);
|
||||
self.draw_mask_tiles(count as u32, fill_rule);
|
||||
}
|
||||
RenderCommand::PushLayer { effects } => self.push_layer(effects),
|
||||
RenderCommand::PopLayer => self.pop_layer(),
|
||||
RenderCommand::DrawSolidTiles(ref solid_tile_vertices) => {
|
||||
let count = solid_tile_vertices.len() / 4;
|
||||
self.stats.solid_tile_count += count;
|
||||
|
@ -294,10 +297,6 @@ where
|
|||
}
|
||||
|
||||
pub fn end_scene(&mut self) {
|
||||
if self.postprocess_options.is_some() {
|
||||
self.postprocess();
|
||||
}
|
||||
|
||||
self.end_composite_timer_query();
|
||||
self.pending_timers.push_back(mem::replace(&mut self.current_timers, RenderTimers::new()));
|
||||
|
||||
|
@ -360,11 +359,6 @@ where
|
|||
self.debug_ui_presenter.ui_presenter.set_framebuffer_size(new_framebuffer_size);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_postprocess_options(&mut self, new_options: Option<PostprocessOptions>) {
|
||||
self.postprocess_options = new_options;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn disable_depth(&mut self) {
|
||||
self.use_depth = false;
|
||||
|
@ -386,17 +380,8 @@ where
|
|||
}
|
||||
|
||||
fn upload_paint_data(&mut self, paint_data: &PaintData) {
|
||||
// FIXME(pcwalton): This is a hack. We shouldn't be generating paint data at all on the
|
||||
// renderer side.
|
||||
let (paint_size, paint_texels): (Vector2I, &[ColorU]);
|
||||
let dummy_paint = [ColorU::white(); 1];
|
||||
if self.postprocess_options.is_some() {
|
||||
paint_size = Vector2I::splat(1);
|
||||
paint_texels = &dummy_paint;
|
||||
} else {
|
||||
paint_size = paint_data.size;
|
||||
paint_texels = &paint_data.texels;
|
||||
};
|
||||
let paint_size = paint_data.size;
|
||||
let paint_texels = &paint_data.texels;
|
||||
|
||||
match self.paint_texture {
|
||||
Some(ref paint_texture) if self.device.texture_size(paint_texture) == paint_size => {}
|
||||
|
@ -675,68 +660,6 @@ where
|
|||
self.preserve_draw_framebuffer();
|
||||
}
|
||||
|
||||
fn postprocess(&mut self) {
|
||||
let mut clear_color = None;
|
||||
if !self.framebuffer_flags
|
||||
.contains(FramebufferFlags::MUST_PRESERVE_DEST_FRAMEBUFFER_CONTENTS) {
|
||||
clear_color = self.options.background_color;
|
||||
}
|
||||
|
||||
let postprocess_options = match self.postprocess_options {
|
||||
None => return,
|
||||
Some(ref options) => options,
|
||||
};
|
||||
|
||||
let postprocess_source_framebuffer = self.postprocess_source_framebuffer.as_ref().unwrap();
|
||||
let source_texture = self
|
||||
.device
|
||||
.framebuffer_texture(postprocess_source_framebuffer);
|
||||
let source_texture_size = self.device.texture_size(source_texture);
|
||||
let main_viewport = self.main_viewport();
|
||||
|
||||
let mut uniforms = vec![
|
||||
(&self.postprocess_program.framebuffer_size_uniform,
|
||||
UniformData::Vec2(main_viewport.size().to_f32().0)),
|
||||
(&self.postprocess_program.source_uniform, UniformData::TextureUnit(0)),
|
||||
(&self.postprocess_program.source_size_uniform,
|
||||
UniformData::Vec2(source_texture_size.0.to_f32x2())),
|
||||
(&self.postprocess_program.gamma_lut_uniform, UniformData::TextureUnit(1)),
|
||||
(&self.postprocess_program.fg_color_uniform,
|
||||
UniformData::Vec4(postprocess_options.fg_color.0)),
|
||||
(&self.postprocess_program.bg_color_uniform,
|
||||
UniformData::Vec4(postprocess_options.bg_color.0)),
|
||||
(&self.postprocess_program.gamma_correction_enabled_uniform,
|
||||
UniformData::Int(postprocess_options.gamma_correction as i32)),
|
||||
];
|
||||
|
||||
match postprocess_options.defringing_kernel {
|
||||
Some(ref kernel) => {
|
||||
uniforms.push((&self.postprocess_program.kernel_uniform,
|
||||
UniformData::Vec4(F32x4::from_slice(&kernel.0))));
|
||||
}
|
||||
None => {
|
||||
uniforms.push((&self.postprocess_program.kernel_uniform,
|
||||
UniformData::Vec4(F32x4::default())));
|
||||
}
|
||||
}
|
||||
|
||||
self.device.draw_elements(6, &RenderState {
|
||||
target: &self.dest_render_target(),
|
||||
program: &self.postprocess_program.program,
|
||||
vertex_array: &self.postprocess_vertex_array.vertex_array,
|
||||
primitive: Primitive::Triangles,
|
||||
textures: &[&source_texture, &self.gamma_lut_texture],
|
||||
uniforms: &uniforms,
|
||||
viewport: main_viewport,
|
||||
options: RenderOptions {
|
||||
clear_ops: ClearOps { color: clear_color, ..ClearOps::default() },
|
||||
..RenderOptions::default()
|
||||
},
|
||||
});
|
||||
|
||||
self.framebuffer_flags.insert(FramebufferFlags::MUST_PRESERVE_DEST_FRAMEBUFFER_CONTENTS);
|
||||
}
|
||||
|
||||
fn draw_stencil(&mut self, quad_positions: &[Vector4F]) {
|
||||
self.device.allocate_buffer(
|
||||
&self.stencil_vertex_array.vertex_buffer,
|
||||
|
@ -819,52 +742,96 @@ where
|
|||
}
|
||||
|
||||
pub fn draw_render_target(&self) -> RenderTarget<D> {
|
||||
if self.postprocess_options.is_some() {
|
||||
RenderTarget::Framebuffer(self.postprocess_source_framebuffer.as_ref().unwrap())
|
||||
} else {
|
||||
self.dest_render_target()
|
||||
match self.layer_framebuffer_stack.last() {
|
||||
Some(ref layer_framebuffer_info) => {
|
||||
RenderTarget::Framebuffer(&layer_framebuffer_info.framebuffer)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dest_render_target(&self) -> RenderTarget<D> {
|
||||
None => {
|
||||
match self.dest_framebuffer {
|
||||
DestFramebuffer::Default { .. } => RenderTarget::Default,
|
||||
DestFramebuffer::Other(ref framebuffer) => RenderTarget::Framebuffer(framebuffer),
|
||||
DestFramebuffer::Other(ref framebuffer) => {
|
||||
RenderTarget::Framebuffer(framebuffer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn init_postprocessing_framebuffer(&mut self) {
|
||||
if !self.postprocess_options.is_some() {
|
||||
self.postprocess_source_framebuffer = None;
|
||||
return;
|
||||
}
|
||||
|
||||
let source_framebuffer_size = self.draw_viewport().size();
|
||||
match self.postprocess_source_framebuffer {
|
||||
Some(ref framebuffer)
|
||||
if self
|
||||
.device
|
||||
.texture_size(self.device.framebuffer_texture(framebuffer))
|
||||
== source_framebuffer_size => {}
|
||||
_ => {
|
||||
let texture = self
|
||||
.device
|
||||
.create_texture(TextureFormat::R8, source_framebuffer_size);
|
||||
self.postprocess_source_framebuffer =
|
||||
Some(self.device.create_framebuffer(texture));
|
||||
}
|
||||
fn push_layer(&mut self, effects: PostprocessOptions) {
|
||||
let main_framebuffer_size = self.main_viewport().size();
|
||||
let framebuffer_size = if effects.defringing_kernel.is_some() {
|
||||
main_framebuffer_size.scale_xy(Vector2I::new(3, 1))
|
||||
} else {
|
||||
main_framebuffer_size
|
||||
};
|
||||
|
||||
/*
|
||||
self.device.clear(&RenderTarget::Framebuffer(self.postprocess_source_framebuffer
|
||||
.as_ref()
|
||||
.unwrap()),
|
||||
RectI::new(Vector2I::default(), source_framebuffer_size),
|
||||
&ClearParams {
|
||||
color: Some(ColorF::transparent_black()),
|
||||
..ClearParams::default()
|
||||
let framebuffer = self.framebuffer_cache.create_framebuffer(&mut self.device,
|
||||
TextureFormat::RGBA8,
|
||||
framebuffer_size);
|
||||
self.layer_framebuffer_stack.push(LayerFramebufferInfo {
|
||||
framebuffer,
|
||||
effects,
|
||||
must_preserve_contents: false,
|
||||
});
|
||||
*/
|
||||
}
|
||||
|
||||
fn pop_layer(&mut self) {
|
||||
let layer_framebuffer_info = self.layer_framebuffer_stack
|
||||
.pop()
|
||||
.expect("Where's the layer?");
|
||||
|
||||
let clear_color = self.clear_color_for_draw_operation();
|
||||
|
||||
let postprocess_source_framebuffer = &layer_framebuffer_info.framebuffer;
|
||||
let source_texture = self
|
||||
.device
|
||||
.framebuffer_texture(postprocess_source_framebuffer);
|
||||
let source_texture_size = self.device.texture_size(source_texture);
|
||||
let main_viewport = self.main_viewport();
|
||||
|
||||
let mut uniforms = vec![
|
||||
(&self.postprocess_program.framebuffer_size_uniform,
|
||||
UniformData::Vec2(main_viewport.size().to_f32().0)),
|
||||
(&self.postprocess_program.source_uniform, UniformData::TextureUnit(0)),
|
||||
(&self.postprocess_program.source_size_uniform,
|
||||
UniformData::Vec2(source_texture_size.0.to_f32x2())),
|
||||
(&self.postprocess_program.gamma_lut_uniform, UniformData::TextureUnit(1)),
|
||||
(&self.postprocess_program.fg_color_uniform,
|
||||
UniformData::Vec4(layer_framebuffer_info.effects.fg_color.0)),
|
||||
(&self.postprocess_program.bg_color_uniform,
|
||||
UniformData::Vec4(layer_framebuffer_info.effects.bg_color.0)),
|
||||
(&self.postprocess_program.gamma_correction_enabled_uniform,
|
||||
UniformData::Int(layer_framebuffer_info.effects.gamma_correction as i32)),
|
||||
];
|
||||
|
||||
match layer_framebuffer_info.effects.defringing_kernel {
|
||||
Some(ref kernel) => {
|
||||
uniforms.push((&self.postprocess_program.kernel_uniform,
|
||||
UniformData::Vec4(F32x4::from_slice(&kernel.0))));
|
||||
}
|
||||
None => {
|
||||
uniforms.push((&self.postprocess_program.kernel_uniform,
|
||||
UniformData::Vec4(F32x4::default())));
|
||||
}
|
||||
}
|
||||
|
||||
self.device.draw_elements(6, &RenderState {
|
||||
target: &self.draw_render_target(),
|
||||
program: &self.postprocess_program.program,
|
||||
vertex_array: &self.postprocess_vertex_array.vertex_array,
|
||||
primitive: Primitive::Triangles,
|
||||
textures: &[&source_texture, &self.gamma_lut_texture],
|
||||
uniforms: &uniforms,
|
||||
viewport: main_viewport,
|
||||
options: RenderOptions {
|
||||
clear_ops: ClearOps { color: clear_color, ..ClearOps::default() },
|
||||
..RenderOptions::default()
|
||||
},
|
||||
});
|
||||
|
||||
self.preserve_draw_framebuffer();
|
||||
|
||||
self.framebuffer_cache.release_framebuffer(layer_framebuffer_info.framebuffer);
|
||||
}
|
||||
|
||||
fn stencil_state(&self) -> Option<StencilState> {
|
||||
|
@ -881,16 +848,17 @@ where
|
|||
}
|
||||
|
||||
fn clear_color_for_draw_operation(&mut self) -> Option<ColorF> {
|
||||
let postprocessing_needed = self.postprocess_options.is_some();
|
||||
let flag = if postprocessing_needed {
|
||||
FramebufferFlags::MUST_PRESERVE_POSTPROCESS_FRAMEBUFFER_CONTENTS
|
||||
} else {
|
||||
FramebufferFlags::MUST_PRESERVE_DEST_FRAMEBUFFER_CONTENTS
|
||||
let must_preserve_contents = match self.layer_framebuffer_stack.last() {
|
||||
Some(ref layer_framebuffer_info) => layer_framebuffer_info.must_preserve_contents,
|
||||
None => {
|
||||
self.framebuffer_flags
|
||||
.contains(FramebufferFlags::MUST_PRESERVE_DEST_FRAMEBUFFER_CONTENTS)
|
||||
}
|
||||
};
|
||||
|
||||
if self.framebuffer_flags.contains(flag) {
|
||||
if must_preserve_contents {
|
||||
None
|
||||
} else if !postprocessing_needed {
|
||||
} else if self.layer_framebuffer_stack.is_empty() {
|
||||
self.options.background_color
|
||||
} else {
|
||||
Some(ColorF::default())
|
||||
|
@ -898,21 +866,24 @@ where
|
|||
}
|
||||
|
||||
fn preserve_draw_framebuffer(&mut self) {
|
||||
let flag = if self.postprocess_options.is_some() {
|
||||
FramebufferFlags::MUST_PRESERVE_POSTPROCESS_FRAMEBUFFER_CONTENTS
|
||||
} else {
|
||||
FramebufferFlags::MUST_PRESERVE_DEST_FRAMEBUFFER_CONTENTS
|
||||
};
|
||||
self.framebuffer_flags.insert(flag);
|
||||
match self.layer_framebuffer_stack.last_mut() {
|
||||
Some(ref mut layer_framebuffer_info) => {
|
||||
layer_framebuffer_info.must_preserve_contents = true;
|
||||
}
|
||||
None => {
|
||||
self.framebuffer_flags
|
||||
.insert(FramebufferFlags::MUST_PRESERVE_DEST_FRAMEBUFFER_CONTENTS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw_viewport(&self) -> RectI {
|
||||
let main_viewport = self.main_viewport();
|
||||
match self.postprocess_options {
|
||||
Some(PostprocessOptions { defringing_kernel: Some(_), .. }) => {
|
||||
RectI::new(Vector2I::default(), main_viewport.size().scale_xy(Vector2I::new(3, 1)))
|
||||
match self.layer_framebuffer_stack.last() {
|
||||
Some(ref layer_framebuffer_info) => {
|
||||
let texture = self.device.framebuffer_texture(&layer_framebuffer_info.framebuffer);
|
||||
RectI::new(Vector2I::default(), self.device.texture_size(texture))
|
||||
}
|
||||
_ => main_viewport,
|
||||
None => self.main_viewport(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -953,7 +924,8 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default)]
|
||||
// FIXME(pcwalton): Rename to `Effects` and move to `pathfinder_content`, perhaps?
|
||||
#[derive(Clone, Copy, Default, Debug)]
|
||||
pub struct PostprocessOptions {
|
||||
pub fg_color: ColorF,
|
||||
pub bg_color: ColorF,
|
||||
|
@ -1035,7 +1007,46 @@ bitflags! {
|
|||
struct FramebufferFlags: u8 {
|
||||
const MUST_PRESERVE_FILL_FRAMEBUFFER_CONTENTS = 0x01;
|
||||
const MUST_PRESERVE_MASK_FRAMEBUFFER_CONTENTS = 0x02;
|
||||
const MUST_PRESERVE_POSTPROCESS_FRAMEBUFFER_CONTENTS = 0x04;
|
||||
const MUST_PRESERVE_DEST_FRAMEBUFFER_CONTENTS = 0x08;
|
||||
const MUST_PRESERVE_DEST_FRAMEBUFFER_CONTENTS = 0x04;
|
||||
}
|
||||
}
|
||||
|
||||
struct FramebufferCache<D> where D: Device {
|
||||
framebuffers: Vec<D::Framebuffer>,
|
||||
}
|
||||
|
||||
impl<D> FramebufferCache<D> where D: Device {
|
||||
fn new() -> FramebufferCache<D> {
|
||||
FramebufferCache { framebuffers: vec![] }
|
||||
}
|
||||
|
||||
fn create_framebuffer(&mut self, device: &mut D, format: TextureFormat, size: Vector2I)
|
||||
-> D::Framebuffer {
|
||||
for index in 0..self.framebuffers.len() {
|
||||
{
|
||||
let texture = device.framebuffer_texture(&self.framebuffers[index]);
|
||||
if device.texture_size(texture) != size ||
|
||||
device.texture_format(texture) != format {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return self.framebuffers.remove(index);
|
||||
}
|
||||
|
||||
let texture = device.create_texture(format, size);
|
||||
device.create_framebuffer(texture)
|
||||
}
|
||||
|
||||
fn release_framebuffer(&mut self, framebuffer: D::Framebuffer) {
|
||||
if self.framebuffers.len() == FRAMEBUFFER_CACHE_SIZE {
|
||||
self.framebuffers.pop();
|
||||
}
|
||||
self.framebuffers.insert(0, framebuffer);
|
||||
}
|
||||
}
|
||||
|
||||
struct LayerFramebufferInfo<D> where D: Device {
|
||||
framebuffer: D::Framebuffer,
|
||||
effects: PostprocessOptions,
|
||||
must_preserve_contents: bool,
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
//! Packed data ready to be sent to the GPU.
|
||||
|
||||
use crate::gpu::renderer::PostprocessOptions;
|
||||
use crate::options::BoundingQuad;
|
||||
use pathfinder_color::ColorU;
|
||||
use pathfinder_content::fill::FillRule;
|
||||
|
@ -24,6 +25,8 @@ pub enum RenderCommand {
|
|||
AddFills(Vec<FillBatchPrimitive>),
|
||||
FlushFills,
|
||||
RenderMaskTiles { tiles: Vec<MaskTile>, fill_rule: FillRule },
|
||||
PushLayer { effects: PostprocessOptions },
|
||||
PopLayer,
|
||||
DrawAlphaTiles(Vec<AlphaTile>),
|
||||
DrawSolidTiles(Vec<SolidTileVertex>),
|
||||
Finish { build_time: Duration },
|
||||
|
@ -125,6 +128,8 @@ impl Debug for RenderCommand {
|
|||
RenderCommand::RenderMaskTiles { ref tiles, fill_rule } => {
|
||||
write!(formatter, "RenderMaskTiles(x{}, {:?})", tiles.len(), fill_rule)
|
||||
}
|
||||
RenderCommand::PushLayer { .. } => write!(formatter, "PushLayer"),
|
||||
RenderCommand::PopLayer => write!(formatter, "PopLayer"),
|
||||
RenderCommand::DrawAlphaTiles(ref tiles) => {
|
||||
write!(formatter, "DrawAlphaTiles(x{})", tiles.len())
|
||||
}
|
||||
|
|
|
@ -12,10 +12,10 @@
|
|||
|
||||
use crate::builder::SceneBuilder;
|
||||
use crate::concurrent::executor::Executor;
|
||||
use crate::gpu::renderer::PostprocessOptions;
|
||||
use crate::options::{BuildOptions, PreparedBuildOptions};
|
||||
use crate::options::{PreparedRenderTransform, RenderCommandListener};
|
||||
use crate::paint::{Paint, PaintId, PaintInfo, Palette};
|
||||
use pathfinder_color::ColorU;
|
||||
use pathfinder_content::fill::FillRule;
|
||||
use pathfinder_geometry::vector::Vector2F;
|
||||
use pathfinder_geometry::rect::RectF;
|
||||
|
@ -24,6 +24,7 @@ use pathfinder_content::outline::Outline;
|
|||
|
||||
#[derive(Clone)]
|
||||
pub struct Scene {
|
||||
pub(crate) display_list: Vec<DisplayItem>,
|
||||
pub(crate) paths: Vec<DrawPath>,
|
||||
pub(crate) clip_paths: Vec<ClipPath>,
|
||||
palette: Palette,
|
||||
|
@ -35,6 +36,7 @@ impl Scene {
|
|||
#[inline]
|
||||
pub fn new() -> Scene {
|
||||
Scene {
|
||||
display_list: vec![],
|
||||
paths: vec![],
|
||||
clip_paths: vec![],
|
||||
palette: Palette::new(),
|
||||
|
@ -46,6 +48,19 @@ impl Scene {
|
|||
pub fn push_path(&mut self, path: DrawPath) {
|
||||
self.bounds = self.bounds.union_rect(path.outline.bounds());
|
||||
self.paths.push(path);
|
||||
|
||||
let new_path_count = self.paths.len() as u32;
|
||||
if let Some(DisplayItem::DrawPaths {
|
||||
start_index: _,
|
||||
ref mut end_index
|
||||
}) = self.display_list.last_mut() {
|
||||
*end_index = new_path_count;
|
||||
} else {
|
||||
self.display_list.push(DisplayItem::DrawPaths {
|
||||
start_index: new_path_count - 1,
|
||||
end_index: new_path_count,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_clip_path(&mut self, clip_path: ClipPath) -> ClipPathId {
|
||||
|
@ -55,6 +70,14 @@ impl Scene {
|
|||
clip_path_id
|
||||
}
|
||||
|
||||
pub fn push_layer(&mut self, effects: PostprocessOptions) {
|
||||
self.display_list.push(DisplayItem::PushLayer { effects });
|
||||
}
|
||||
|
||||
pub fn pop_layer(&mut self) {
|
||||
self.display_list.push(DisplayItem::PopLayer);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn build_paint_info(&self) -> PaintInfo {
|
||||
self.palette.build_paint_info(self.view_box.size().to_i32())
|
||||
|
@ -142,27 +165,6 @@ impl Scene {
|
|||
outline
|
||||
}
|
||||
|
||||
pub fn monochrome_color(&self) -> Option<ColorU> {
|
||||
if self.paths.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let first_paint_id = self.paths[0].paint;
|
||||
if self
|
||||
.paths
|
||||
.iter()
|
||||
.skip(1)
|
||||
.any(|path_object| path_object.paint != first_paint_id) {
|
||||
return None;
|
||||
}
|
||||
|
||||
match self.palette.paints[first_paint_id.0 as usize] {
|
||||
Paint::Color(color) => Some(color),
|
||||
Paint::Gradient(_) => None,
|
||||
Paint::Pattern(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn effective_view_box(&self, render_options: &PreparedBuildOptions) -> RectF {
|
||||
if render_options.subpixel_aa_enabled {
|
||||
|
@ -229,6 +231,13 @@ pub struct ClipPath {
|
|||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct ClipPathId(pub u32);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum DisplayItem {
|
||||
DrawPaths { start_index: u32, end_index: u32 },
|
||||
PushLayer { effects: PostprocessOptions },
|
||||
PopLayer,
|
||||
}
|
||||
|
||||
impl DrawPath {
|
||||
#[inline]
|
||||
pub fn new(outline: Outline,
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use crate::builder::{BuiltPath, ObjectBuilder, SceneBuilder};
|
||||
use crate::builder::{BuiltPath, ObjectBuilder, SceneBuilder, SolidTile};
|
||||
use crate::gpu_data::TileObjectPrimitive;
|
||||
use crate::paint::PaintMetadata;
|
||||
use pathfinder_content::fill::FillRule;
|
||||
|
@ -174,10 +174,9 @@ impl<'a> Tiler<'a> {
|
|||
(FillRule::EvenOdd, _) => {}
|
||||
}
|
||||
|
||||
// Next, if this is a solid tile, just poke it into the Z-buffer. We don't need
|
||||
// to do anything else here.
|
||||
// Next, if this is a solid tile, record that fact and stop here.
|
||||
if paint_metadata.is_opaque {
|
||||
self.scene_builder.z_buffer.update(tile_coords, self.object_index);
|
||||
self.object_builder.built_path.solid_tiles.push(SolidTile::new(tile_coords));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
//! Software occlusion culling.
|
||||
|
||||
use crate::builder::SolidTile;
|
||||
use crate::gpu_data::SolidTileVertex;
|
||||
use crate::paint::PaintMetadata;
|
||||
use crate::scene::DrawPath;
|
||||
|
@ -17,62 +18,43 @@ use crate::tile_map::DenseTileMap;
|
|||
use crate::tiles;
|
||||
use pathfinder_geometry::rect::RectF;
|
||||
use pathfinder_geometry::vector::Vector2I;
|
||||
use std::ops::Range;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
|
||||
|
||||
pub struct ZBuffer {
|
||||
buffer: DenseTileMap<AtomicUsize>,
|
||||
pub(crate) struct ZBuffer {
|
||||
buffer: DenseTileMap<u32>,
|
||||
}
|
||||
|
||||
impl ZBuffer {
|
||||
pub fn new(view_box: RectF) -> ZBuffer {
|
||||
pub(crate) fn new(view_box: RectF) -> ZBuffer {
|
||||
let tile_rect = tiles::round_rect_out_to_tile_bounds(view_box);
|
||||
ZBuffer {
|
||||
buffer: DenseTileMap::from_builder(|_| AtomicUsize::new(0), tile_rect),
|
||||
buffer: DenseTileMap::from_builder(|_| 0, tile_rect),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn test(&self, coords: Vector2I, object_index: u32) -> bool {
|
||||
pub(crate) fn test(&self, coords: Vector2I, object_index: u32) -> bool {
|
||||
let tile_index = self.buffer.coords_to_index_unchecked(coords);
|
||||
let existing_depth = self.buffer.data[tile_index as usize].load(AtomicOrdering::SeqCst);
|
||||
existing_depth < object_index as usize + 1
|
||||
self.buffer.data[tile_index as usize] < object_index + 1
|
||||
}
|
||||
|
||||
pub fn update(&self, coords: Vector2I, object_index: u16) {
|
||||
let tile_index = self.buffer.coords_to_index_unchecked(coords);
|
||||
let mut old_depth = self.buffer.data[tile_index].load(AtomicOrdering::SeqCst);
|
||||
let new_depth = (object_index + 1) as usize;
|
||||
while old_depth < new_depth {
|
||||
let prev_depth = self.buffer.data[tile_index].compare_and_swap(
|
||||
old_depth,
|
||||
new_depth,
|
||||
AtomicOrdering::SeqCst,
|
||||
);
|
||||
if prev_depth == old_depth {
|
||||
// Successfully written.
|
||||
return;
|
||||
}
|
||||
old_depth = prev_depth;
|
||||
pub(crate) fn update(&mut self, solid_tiles: &[SolidTile], object_index: u32) {
|
||||
for solid_tile in solid_tiles {
|
||||
let tile_index = self.buffer.coords_to_index_unchecked(solid_tile.coords);
|
||||
let z_dest = &mut self.buffer.data[tile_index as usize];
|
||||
*z_dest = u32::max(*z_dest, object_index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_solid_tiles(&self,
|
||||
paths: &[DrawPath],
|
||||
paint_metadata: &[PaintMetadata],
|
||||
object_range: Range<u32>)
|
||||
pub(crate) fn build_solid_tiles(&self, paths: &[DrawPath], paint_metadata: &[PaintMetadata])
|
||||
-> Vec<SolidTileVertex> {
|
||||
let mut solid_tiles = vec![];
|
||||
for tile_index in 0..self.buffer.data.len() {
|
||||
let depth = self.buffer.data[tile_index].load(AtomicOrdering::Relaxed);
|
||||
let depth = self.buffer.data[tile_index];
|
||||
if depth == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let tile_coords = self.buffer.index_to_coords(tile_index);
|
||||
let object_index = (depth - 1) as u32;
|
||||
if object_index < object_range.start || object_index >= object_range.end {
|
||||
continue;
|
||||
}
|
||||
|
||||
let paint_id = paths[object_index as usize].paint();
|
||||
let paint_metadata = &paint_metadata[paint_id.0 as usize];
|
||||
|
|
|
@ -27,7 +27,6 @@ use pathfinder_geometry::vector::Vector2F;
|
|||
use pathfinder_renderer::paint::Paint;
|
||||
use pathfinder_renderer::scene::{ClipPath, ClipPathId, DrawPath, Scene};
|
||||
use std::fmt::{Display, Formatter, Result as FormatResult};
|
||||
use std::mem;
|
||||
use usvg::{Color as SvgColor, FillRule as UsvgFillRule, LineCap as UsvgLineCap};
|
||||
use usvg::{LineJoin as UsvgLineJoin, Node, NodeExt, NodeKind, Opacity, Paint as UsvgPaint};
|
||||
use usvg::{PathSegment as UsvgPathSegment, Rect as UsvgRect, Transform as UsvgTransform};
|
||||
|
@ -65,11 +64,17 @@ bitflags! {
|
|||
|
||||
impl BuiltSVG {
|
||||
// TODO(pcwalton): Allow a global transform to be set.
|
||||
pub fn from_tree(tree: Tree) -> BuiltSVG {
|
||||
#[inline]
|
||||
pub fn from_tree(tree: &Tree) -> BuiltSVG {
|
||||
BuiltSVG::from_tree_and_scene(tree, Scene::new())
|
||||
}
|
||||
|
||||
// TODO(pcwalton): Allow a global transform to be set.
|
||||
pub fn from_tree_and_scene(tree: &Tree, scene: Scene) -> BuiltSVG {
|
||||
// TODO(pcwalton): Maybe have a `SVGBuilder` type to hold the clip path IDs and other
|
||||
// transient data separate from `BuiltSVG`?
|
||||
let mut built_svg = BuiltSVG {
|
||||
scene: Scene::new(),
|
||||
scene,
|
||||
result_flags: BuildResultFlags::empty(),
|
||||
clip_paths: HashMap::new(),
|
||||
};
|
||||
|
@ -83,11 +88,7 @@ impl BuiltSVG {
|
|||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
// FIXME(pcwalton): This is needed to avoid stack exhaustion in debug builds when
|
||||
// recursively dropping reference counts on very large SVGs. :(
|
||||
mem::forget(tree);
|
||||
}
|
||||
|
||||
built_svg
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue