Show a warning when unsupported SVG features are used
This commit is contained in:
parent
6062676d0e
commit
40209b6fe8
|
@ -518,6 +518,7 @@ dependencies = [
|
|||
"pathfinder_ui 0.1.0",
|
||||
"rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sdl2 0.32.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sdl2-sys 0.32.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"usvg 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
@ -580,6 +581,7 @@ version = "0.3.0"
|
|||
name = "pathfinder_svg"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pathfinder_geometry 0.3.0",
|
||||
"pathfinder_renderer 0.1.0",
|
||||
"usvg 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
|
|
@ -11,6 +11,7 @@ jemallocator = "0.1"
|
|||
nfd = "0.0.4"
|
||||
rayon = "1.0"
|
||||
sdl2 = "0.32"
|
||||
sdl2-sys = "0.32"
|
||||
usvg = "0.4"
|
||||
|
||||
[dependencies.image]
|
||||
|
|
|
@ -29,18 +29,20 @@ use pathfinder_renderer::gpu_data::BuiltScene;
|
|||
use pathfinder_renderer::post::{DEFRINGING_KERNEL_CORE_GRAPHICS, STEM_DARKENING_FACTORS};
|
||||
use pathfinder_renderer::scene::Scene;
|
||||
use pathfinder_renderer::z_buffer::ZBuffer;
|
||||
use pathfinder_svg::SceneExt;
|
||||
use pathfinder_svg::BuiltSVG;
|
||||
use pathfinder_ui::UIEvent;
|
||||
use rayon::ThreadPoolBuilder;
|
||||
use sdl2::{EventPump, Sdl, VideoSubsystem};
|
||||
use sdl2::EventPump;
|
||||
use sdl2::event::{Event, WindowEvent};
|
||||
use sdl2::keyboard::Keycode;
|
||||
use sdl2::video::{GLContext, GLProfile, Window};
|
||||
use sdl2_sys::{SDL_Event, SDL_UserEvent};
|
||||
use std::f32::consts::FRAC_PI_4;
|
||||
use std::mem;
|
||||
use std::panic;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process;
|
||||
use std::ptr;
|
||||
use std::sync::mpsc::{self, Receiver, Sender};
|
||||
use std::thread;
|
||||
use std::time::{Duration, Instant};
|
||||
|
@ -72,6 +74,8 @@ const GROUND_LINE_COLOR: ColorU = ColorU { r: 127, g: 127, b: 127, a: 255 };
|
|||
|
||||
const APPROX_FONT_SIZE: f32 = 16.0;
|
||||
|
||||
const MESSAGE_TIMEOUT_SECS: u64 = 5;
|
||||
|
||||
pub const GRIDLINE_COUNT: u8 = 10;
|
||||
|
||||
mod device;
|
||||
|
@ -79,10 +83,6 @@ mod ui;
|
|||
|
||||
pub struct DemoApp {
|
||||
window: Window,
|
||||
#[allow(dead_code)]
|
||||
sdl_context: Sdl,
|
||||
#[allow(dead_code)]
|
||||
sdl_video: VideoSubsystem,
|
||||
sdl_event_pump: EventPump,
|
||||
#[allow(dead_code)]
|
||||
gl_context: GLContext,
|
||||
|
@ -98,6 +98,8 @@ pub struct DemoApp {
|
|||
exit: bool,
|
||||
mouselook_enabled: bool,
|
||||
dirty: bool,
|
||||
expire_message_event_id: u32,
|
||||
message_epoch: u32,
|
||||
|
||||
ui: DemoUI<GLDevice>,
|
||||
scene_thread_proxy: SceneThreadProxy,
|
||||
|
@ -112,6 +114,7 @@ impl DemoApp {
|
|||
pub fn new() -> DemoApp {
|
||||
let sdl_context = sdl2::init().unwrap();
|
||||
let sdl_video = sdl_context.video().unwrap();
|
||||
let sdl_event = sdl_context.event().unwrap();
|
||||
|
||||
let gl_attributes = sdl_video.gl_attr();
|
||||
gl_attributes.set_context_profile(GLProfile::Core);
|
||||
|
@ -130,6 +133,8 @@ impl DemoApp {
|
|||
let gl_context = window.gl_create_context().unwrap();
|
||||
gl::load_with(|name| sdl_video.gl_get_proc_address(name) as *const _);
|
||||
|
||||
let expire_message_event_id = unsafe { sdl_event.register_event().unwrap() };
|
||||
|
||||
let sdl_event_pump = sdl_context.event_pump().unwrap();
|
||||
|
||||
let device = GLDevice::new();
|
||||
|
@ -140,10 +145,12 @@ impl DemoApp {
|
|||
let (drawable_width, drawable_height) = window.drawable_size();
|
||||
let drawable_size = Point2DI32::new(drawable_width as i32, drawable_height as i32);
|
||||
|
||||
let base_scene = load_scene(&options.input_path);
|
||||
let scene_view_box = base_scene.view_box;
|
||||
let built_svg = load_scene(&options.input_path);
|
||||
let message = get_svg_building_message(&built_svg);
|
||||
|
||||
let scene_view_box = built_svg.scene.view_box;
|
||||
let renderer = Renderer::new(device, &resources, drawable_size);
|
||||
let scene_thread_proxy = SceneThreadProxy::new(base_scene, options.clone());
|
||||
let scene_thread_proxy = SceneThreadProxy::new(built_svg.scene, options.clone());
|
||||
update_drawable_size(&window, &scene_thread_proxy);
|
||||
|
||||
let camera = if options.three_d {
|
||||
|
@ -160,12 +167,19 @@ impl DemoApp {
|
|||
let ground_line_vertex_array = GroundLineVertexArray::new(&renderer.device,
|
||||
&ground_program);
|
||||
|
||||
let ui = DemoUI::new(&renderer.device, &resources, options);
|
||||
let mut ui = DemoUI::new(&renderer.device, &resources, options);
|
||||
let mut message_epoch = 0;
|
||||
emit_message(&mut ui, &mut message_epoch, expire_message_event_id, message);
|
||||
|
||||
// Leak our SDL stuff. It'll last the entire duration of the process anyway, and it means
|
||||
// we don't have to deal with any nasty issues regarding synchronizing background threads
|
||||
// during shutdown.
|
||||
mem::forget(sdl_event);
|
||||
mem::forget(sdl_video);
|
||||
mem::forget(sdl_context);
|
||||
|
||||
DemoApp {
|
||||
window,
|
||||
sdl_context,
|
||||
sdl_video,
|
||||
sdl_event_pump,
|
||||
gl_context,
|
||||
|
||||
|
@ -180,6 +194,8 @@ impl DemoApp {
|
|||
exit: false,
|
||||
mouselook_enabled: false,
|
||||
dirty: true,
|
||||
expire_message_event_id,
|
||||
message_epoch,
|
||||
|
||||
ui,
|
||||
scene_thread_proxy,
|
||||
|
@ -333,6 +349,12 @@ impl DemoApp {
|
|||
self.dirty = true;
|
||||
}
|
||||
}
|
||||
Event::User { type_: event_id, code: expected_epoch, .. } if
|
||||
event_id == self.expire_message_event_id &&
|
||||
expected_epoch as u32 == self.message_epoch => {
|
||||
self.ui.message = String::new();
|
||||
self.dirty = true;
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
|
@ -497,19 +519,21 @@ impl DemoApp {
|
|||
UIAction::None => {}
|
||||
|
||||
UIAction::OpenFile(ref path) => {
|
||||
let scene = load_scene(&path);
|
||||
self.scene_view_box = scene.view_box;
|
||||
let built_svg = load_scene(&path);
|
||||
self.ui.message = get_svg_building_message(&built_svg);
|
||||
|
||||
self.scene_view_box = built_svg.scene.view_box;
|
||||
|
||||
update_drawable_size(&self.window, &self.scene_thread_proxy);
|
||||
let drawable_size = current_drawable_size(&self.window);
|
||||
|
||||
self.camera = if self.ui.three_d_enabled {
|
||||
Camera::new_3d(scene.view_box)
|
||||
Camera::new_3d(built_svg.scene.view_box)
|
||||
} else {
|
||||
Camera::new_2d(scene.view_box, drawable_size)
|
||||
Camera::new_2d(built_svg.scene.view_box, drawable_size)
|
||||
};
|
||||
|
||||
self.scene_thread_proxy.load_scene(scene);
|
||||
self.scene_thread_proxy.load_scene(built_svg.scene);
|
||||
self.dirty = true;
|
||||
}
|
||||
|
||||
|
@ -690,8 +714,8 @@ impl Options {
|
|||
}
|
||||
}
|
||||
|
||||
fn load_scene(input_path: &Path) -> Scene {
|
||||
Scene::from_tree(Tree::from_file(input_path, &UsvgOptions::default()).unwrap())
|
||||
fn load_scene(input_path: &Path) -> BuiltSVG {
|
||||
BuiltSVG::from_tree(Tree::from_file(input_path, &UsvgOptions::default()).unwrap())
|
||||
}
|
||||
|
||||
fn build_scene(scene: &Scene, build_options: BuildOptions, jobs: Option<usize>) -> BuiltScene {
|
||||
|
@ -835,3 +859,48 @@ fn get_mouse_position(sdl_event_pump: &EventPump, scale_factor: f32) -> Point2DF
|
|||
let mouse_state = sdl_event_pump.mouse_state();
|
||||
Point2DI32::new(mouse_state.x(), mouse_state.y()).to_f32().scale(scale_factor)
|
||||
}
|
||||
|
||||
fn get_svg_building_message(built_svg: &BuiltSVG) -> String {
|
||||
if built_svg.result_flags.is_empty() {
|
||||
return String::new();
|
||||
}
|
||||
format!("Warning: These features in the SVG are unsupported: {}.", built_svg.result_flags)
|
||||
}
|
||||
|
||||
fn emit_message(ui: &mut DemoUI<GLDevice>,
|
||||
message_epoch: &mut u32,
|
||||
expire_message_event_id: u32,
|
||||
message: String) {
|
||||
if message.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
ui.message = message;
|
||||
let expected_epoch = *message_epoch + 1;
|
||||
*message_epoch = expected_epoch;
|
||||
thread::spawn(move || {
|
||||
thread::sleep(Duration::from_secs(MESSAGE_TIMEOUT_SECS));
|
||||
push_sdl_user_event(SDL_UserEvent {
|
||||
timestamp: 0,
|
||||
windowID: 0,
|
||||
type_: expire_message_event_id,
|
||||
code: expected_epoch as i32,
|
||||
data1: ptr::null_mut(),
|
||||
data2: ptr::null_mut(),
|
||||
}).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
// Posts an event from any thread.
|
||||
//
|
||||
// TODO(pcwalton): The fact that this is necessary is really a `rust-sdl2` bug, filed at
|
||||
// https://github.com/Rust-SDL2/rust-sdl2/issues/747.
|
||||
fn push_sdl_user_event(mut event: SDL_UserEvent) -> Result<(), String> {
|
||||
unsafe {
|
||||
if sdl2_sys::SDL_PushEvent(&mut event as *mut SDL_UserEvent as *mut SDL_Event) == 1 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(sdl2::get_error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,8 +14,8 @@ use pathfinder_geometry::basic::point::Point2DI32;
|
|||
use pathfinder_geometry::basic::rect::RectI32;
|
||||
use pathfinder_gpu::{Device, Resources};
|
||||
use pathfinder_renderer::gpu::debug::DebugUI;
|
||||
use pathfinder_ui::{BUTTON_HEIGHT, BUTTON_TEXT_OFFSET, BUTTON_WIDTH, PADDING, SWITCH_SIZE};
|
||||
use pathfinder_ui::{TEXT_COLOR, WINDOW_COLOR};
|
||||
use pathfinder_ui::{BUTTON_HEIGHT, BUTTON_TEXT_OFFSET, BUTTON_WIDTH, FONT_ASCENT, PADDING};
|
||||
use pathfinder_ui::{SWITCH_SIZE, TEXT_COLOR, TOOLTIP_HEIGHT, WINDOW_COLOR};
|
||||
use std::f32::consts::PI;
|
||||
use std::path::PathBuf;
|
||||
|
||||
|
@ -54,12 +54,15 @@ pub struct DemoUI<D> where D: Device {
|
|||
effects_panel_visible: bool,
|
||||
rotate_panel_visible: bool,
|
||||
|
||||
// FIXME(pcwalton): Factor the below out into a model class.
|
||||
|
||||
pub three_d_enabled: bool,
|
||||
pub dark_background_enabled: bool,
|
||||
pub gamma_correction_effect_enabled: bool,
|
||||
pub stem_darkening_effect_enabled: bool,
|
||||
pub subpixel_aa_effect_enabled: bool,
|
||||
pub rotation: i32,
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
impl<D> DemoUI<D> where D: Device {
|
||||
|
@ -92,6 +95,7 @@ impl<D> DemoUI<D> where D: Device {
|
|||
stem_darkening_effect_enabled: false,
|
||||
subpixel_aa_effect_enabled: false,
|
||||
rotation: SLIDER_WIDTH / 2,
|
||||
message: String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -100,6 +104,12 @@ impl<D> DemoUI<D> where D: Device {
|
|||
}
|
||||
|
||||
pub fn update(&mut self, device: &D, debug_ui: &mut DebugUI<D>, action: &mut UIAction) {
|
||||
// Draw message text.
|
||||
|
||||
self.draw_message_text(device, debug_ui);
|
||||
|
||||
// Draw button strip.
|
||||
|
||||
let bottom = debug_ui.ui.framebuffer_size().y() - PADDING;
|
||||
let mut position = Point2DI32::new(PADDING, bottom - BUTTON_HEIGHT);
|
||||
|
||||
|
@ -181,6 +191,23 @@ impl<D> DemoUI<D> where D: Device {
|
|||
self.draw_rotate_panel(device, debug_ui, action);
|
||||
}
|
||||
|
||||
fn draw_message_text(&mut self, device: &D, debug_ui: &mut DebugUI<D>) {
|
||||
if self.message.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let message_size = debug_ui.ui.measure_text(&self.message);
|
||||
let window_origin = Point2DI32::new(PADDING, PADDING);
|
||||
let window_size = Point2DI32::new(PADDING * 2 + message_size, TOOLTIP_HEIGHT);
|
||||
debug_ui.ui.draw_solid_rounded_rect(device,
|
||||
RectI32::new(window_origin, window_size),
|
||||
WINDOW_COLOR);
|
||||
debug_ui.ui.draw_text(device,
|
||||
&self.message,
|
||||
window_origin + Point2DI32::new(PADDING, PADDING + FONT_ASCENT),
|
||||
false);
|
||||
}
|
||||
|
||||
fn draw_effects_panel(&mut self, device: &D, debug_ui: &mut DebugUI<D>) {
|
||||
if !self.effects_panel_visible {
|
||||
return;
|
||||
|
|
|
@ -5,6 +5,7 @@ edition = "2018"
|
|||
authors = ["Patrick Walton <pcwalton@mimiga.net>"]
|
||||
|
||||
[dependencies]
|
||||
bitflags = "1.0"
|
||||
usvg = "0.4"
|
||||
|
||||
[dependencies.pathfinder_geometry]
|
||||
|
|
167
svg/src/lib.rs
167
svg/src/lib.rs
|
@ -10,6 +10,9 @@
|
|||
|
||||
//! Converts a subset of SVG to a Pathfinder scene.
|
||||
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
|
||||
use pathfinder_geometry::basic::line_segment::LineSegmentF32;
|
||||
use pathfinder_geometry::basic::point::Point2DF32;
|
||||
use pathfinder_geometry::basic::rect::RectF32;
|
||||
|
@ -20,29 +23,57 @@ use pathfinder_geometry::segment::{Segment, SegmentFlags};
|
|||
use pathfinder_geometry::stroke::OutlineStrokeToFill;
|
||||
use pathfinder_renderer::paint::Paint;
|
||||
use pathfinder_renderer::scene::{PathObject, PathObjectKind, Scene};
|
||||
use std::fmt::{Display, Formatter, Result as FormatResult};
|
||||
use std::mem;
|
||||
use usvg::{Color as SvgColor, Node, NodeExt, NodeKind, Paint as UsvgPaint};
|
||||
use usvg::{PathSegment as UsvgPathSegment, Rect as UsvgRect, Transform as UsvgTransform, Tree};
|
||||
use usvg::{PathSegment as UsvgPathSegment, Rect as UsvgRect, Transform as UsvgTransform};
|
||||
use usvg::{Tree, Visibility};
|
||||
|
||||
const HAIRLINE_STROKE_WIDTH: f32 = 0.1;
|
||||
|
||||
pub trait SceneExt {
|
||||
fn from_tree(tree: Tree) -> Self;
|
||||
pub struct BuiltSVG {
|
||||
pub scene: Scene,
|
||||
pub result_flags: BuildResultFlags,
|
||||
}
|
||||
|
||||
impl SceneExt for Scene {
|
||||
bitflags! {
|
||||
// NB: If you change this, make sure to update the `Display`
|
||||
// implementation as well.
|
||||
pub struct BuildResultFlags: u16 {
|
||||
const UNSUPPORTED_CLIP_PATH_NODE = 0x0001;
|
||||
const UNSUPPORTED_DEFS_NODE = 0x0002;
|
||||
const UNSUPPORTED_FILTER_NODE = 0x0004;
|
||||
const UNSUPPORTED_IMAGE_NODE = 0x0008;
|
||||
const UNSUPPORTED_LINEAR_GRADIENT_NODE = 0x0010;
|
||||
const UNSUPPORTED_MASK_NODE = 0x0020;
|
||||
const UNSUPPORTED_PATTERN_NODE = 0x0040;
|
||||
const UNSUPPORTED_RADIAL_GRADIENT_NODE = 0x0080;
|
||||
const UNSUPPORTED_NESTED_SVG_NODE = 0x0100;
|
||||
const UNSUPPORTED_TEXT_NODE = 0x0200;
|
||||
const UNSUPPORTED_LINK_PAINT = 0x0400;
|
||||
const UNSUPPORTED_CLIP_PATH_ATTR = 0x0800;
|
||||
const UNSUPPORTED_FILTER_ATTR = 0x1000;
|
||||
const UNSUPPORTED_MASK_ATTR = 0x2000;
|
||||
const UNSUPPORTED_OPACITY_ATTR = 0x4000;
|
||||
}
|
||||
}
|
||||
|
||||
impl BuiltSVG {
|
||||
// TODO(pcwalton): Allow a global transform to be set.
|
||||
fn from_tree(tree: Tree) -> Scene {
|
||||
pub fn from_tree(tree: Tree) -> BuiltSVG {
|
||||
let global_transform = Transform2DF32::default();
|
||||
|
||||
let mut scene = Scene::new();
|
||||
let mut built_svg = BuiltSVG {
|
||||
scene: Scene::new(),
|
||||
result_flags: BuildResultFlags::empty(),
|
||||
};
|
||||
|
||||
let root = &tree.root();
|
||||
match *root.borrow() {
|
||||
NodeKind::Svg(ref svg) => {
|
||||
scene.view_box = usvg_rect_to_euclid_rect(&svg.view_box.rect);
|
||||
built_svg.scene.view_box = usvg_rect_to_euclid_rect(&svg.view_box.rect);
|
||||
for kid in root.children() {
|
||||
process_node(&mut scene, &kid, &global_transform);
|
||||
built_svg.process_node(&kid, &global_transform);
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
|
@ -52,30 +83,44 @@ impl SceneExt for Scene {
|
|||
// recursively dropping reference counts on very large SVGs. :(
|
||||
mem::forget(tree);
|
||||
|
||||
scene
|
||||
}
|
||||
built_svg
|
||||
}
|
||||
|
||||
fn process_node(scene: &mut Scene, node: &Node, transform: &Transform2DF32) {
|
||||
fn process_node(&mut self, node: &Node, transform: &Transform2DF32) {
|
||||
let node_transform = usvg_transform_to_transform_2d(&node.transform());
|
||||
let transform = transform.pre_mul(&node_transform);
|
||||
|
||||
match *node.borrow() {
|
||||
NodeKind::Group(_) => {
|
||||
NodeKind::Group(ref group) => {
|
||||
if group.clip_path.is_some() {
|
||||
self.result_flags.insert(BuildResultFlags::UNSUPPORTED_CLIP_PATH_ATTR);
|
||||
}
|
||||
if group.filter.is_some() {
|
||||
self.result_flags.insert(BuildResultFlags::UNSUPPORTED_FILTER_ATTR);
|
||||
}
|
||||
if group.mask.is_some() {
|
||||
self.result_flags.insert(BuildResultFlags::UNSUPPORTED_MASK_ATTR);
|
||||
}
|
||||
if group.opacity.is_some() {
|
||||
self.result_flags.insert(BuildResultFlags::UNSUPPORTED_OPACITY_ATTR);
|
||||
}
|
||||
|
||||
for kid in node.children() {
|
||||
process_node(scene, &kid, &transform)
|
||||
self.process_node(&kid, &transform)
|
||||
}
|
||||
}
|
||||
NodeKind::Path(ref path) => {
|
||||
NodeKind::Path(ref path) if path.visibility == Visibility::Visible => {
|
||||
if let Some(ref fill) = path.fill {
|
||||
let style = scene.push_paint(&Paint::from_svg_paint(&fill.paint));
|
||||
let style =
|
||||
self.scene.push_paint(&Paint::from_svg_paint(&fill.paint,
|
||||
&mut self.result_flags));
|
||||
|
||||
let path = UsvgPathToSegments::new(path.segments.iter().cloned());
|
||||
let path = Transform2DF32PathIter::new(path, &transform);
|
||||
let outline = Outline::from_segments(path);
|
||||
|
||||
scene.bounds = scene.bounds.union_rect(outline.bounds());
|
||||
scene.objects.push(PathObject::new(
|
||||
self.scene.bounds = self.scene.bounds.union_rect(outline.bounds());
|
||||
self.scene.objects.push(PathObject::new(
|
||||
outline,
|
||||
style,
|
||||
node.id().to_string(),
|
||||
|
@ -84,7 +129,9 @@ fn process_node(scene: &mut Scene, node: &Node, transform: &Transform2DF32) {
|
|||
}
|
||||
|
||||
if let Some(ref stroke) = path.stroke {
|
||||
let style = scene.push_paint(&Paint::from_svg_paint(&stroke.paint));
|
||||
let style =
|
||||
self.scene.push_paint(&Paint::from_svg_paint(&stroke.paint,
|
||||
&mut self.result_flags));
|
||||
let stroke_width =
|
||||
f32::max(stroke.width.value() as f32, HAIRLINE_STROKE_WIDTH);
|
||||
|
||||
|
@ -96,8 +143,8 @@ fn process_node(scene: &mut Scene, node: &Node, transform: &Transform2DF32) {
|
|||
stroke_to_fill.offset();
|
||||
let outline = stroke_to_fill.outline;
|
||||
|
||||
scene.bounds = scene.bounds.union_rect(outline.bounds());
|
||||
scene.objects.push(PathObject::new(
|
||||
self.scene.bounds = self.scene.bounds.union_rect(outline.bounds());
|
||||
self.scene.objects.push(PathObject::new(
|
||||
outline,
|
||||
style,
|
||||
node.id().to_string(),
|
||||
|
@ -105,24 +152,96 @@ fn process_node(scene: &mut Scene, node: &Node, transform: &Transform2DF32) {
|
|||
));
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// TODO(pcwalton): Handle these by punting to WebRender.
|
||||
NodeKind::Path(..) => {}
|
||||
NodeKind::ClipPath(..) => {
|
||||
self.result_flags.insert(BuildResultFlags::UNSUPPORTED_CLIP_PATH_NODE);
|
||||
}
|
||||
NodeKind::Defs { .. } => {
|
||||
self.result_flags.insert(BuildResultFlags::UNSUPPORTED_DEFS_NODE);
|
||||
}
|
||||
NodeKind::Filter(..) => {
|
||||
self.result_flags.insert(BuildResultFlags::UNSUPPORTED_FILTER_NODE);
|
||||
}
|
||||
NodeKind::Image(..) => {
|
||||
self.result_flags.insert(BuildResultFlags::UNSUPPORTED_IMAGE_NODE);
|
||||
}
|
||||
NodeKind::LinearGradient(..) => {
|
||||
self.result_flags.insert(BuildResultFlags::UNSUPPORTED_LINEAR_GRADIENT_NODE);
|
||||
}
|
||||
NodeKind::Mask(..) => {
|
||||
self.result_flags.insert(BuildResultFlags::UNSUPPORTED_MASK_NODE);
|
||||
}
|
||||
NodeKind::Pattern(..) => {
|
||||
self.result_flags.insert(BuildResultFlags::UNSUPPORTED_PATTERN_NODE);
|
||||
}
|
||||
NodeKind::RadialGradient(..) => {
|
||||
self.result_flags.insert(BuildResultFlags::UNSUPPORTED_RADIAL_GRADIENT_NODE);
|
||||
}
|
||||
NodeKind::Svg(..) => {
|
||||
self.result_flags.insert(BuildResultFlags::UNSUPPORTED_NESTED_SVG_NODE);
|
||||
}
|
||||
NodeKind::Text(..) => {
|
||||
self.result_flags.insert(BuildResultFlags::UNSUPPORTED_TEXT_NODE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for BuildResultFlags {
|
||||
fn fmt(&self, formatter: &mut Formatter) -> FormatResult {
|
||||
if self.is_empty() {
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
let mut first = true;
|
||||
for (bit, name) in NAMES.iter().enumerate() {
|
||||
if (self.bits() >> bit) & 1 == 0 {
|
||||
continue;
|
||||
}
|
||||
if !first {
|
||||
formatter.write_str(", ")?;
|
||||
} else {
|
||||
first = false;
|
||||
}
|
||||
formatter.write_str(name)?;
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
|
||||
// Must match the order in `BuildResultFlags`.
|
||||
static NAMES: &'static [&'static str] = &[
|
||||
"<clipPath>",
|
||||
"<defs>",
|
||||
"<filter>",
|
||||
"<image>",
|
||||
"<linearGradient>",
|
||||
"<mask>",
|
||||
"<pattern>",
|
||||
"<radialGradient>",
|
||||
"nested <svg>",
|
||||
"<text>",
|
||||
"paint server element",
|
||||
"clip-path attribute",
|
||||
"filter attribute",
|
||||
"mask attribute",
|
||||
"opacity attribute",
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
trait PaintExt {
|
||||
fn from_svg_paint(svg_paint: &UsvgPaint) -> Self;
|
||||
fn from_svg_paint(svg_paint: &UsvgPaint, result_flags: &mut BuildResultFlags) -> Self;
|
||||
}
|
||||
|
||||
impl PaintExt for Paint {
|
||||
#[inline]
|
||||
fn from_svg_paint(svg_paint: &UsvgPaint) -> Paint {
|
||||
fn from_svg_paint(svg_paint: &UsvgPaint, result_flags: &mut BuildResultFlags) -> Paint {
|
||||
Paint {
|
||||
color: match *svg_paint {
|
||||
UsvgPaint::Color(color) => ColorU::from_svg_color(color),
|
||||
UsvgPaint::Link(_) => {
|
||||
// TODO(pcwalton)
|
||||
result_flags.insert(BuildResultFlags::UNSUPPORTED_LINK_PAINT);
|
||||
ColorU::black()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue