From 6a81ec4893cc18c624858871a70e7d6b33388c72 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 20 Dec 2017 10:40:21 -0800 Subject: [PATCH] Make SVG stroke width more accurate, and switch to XCAA by default. Partially addresses #57. --- demo/client/html/svg-demo.html.hbs | 4 ++-- demo/client/src/svg-loader.ts | 22 ++++++++++++++++------ path-utils/src/stroke.rs | 8 +++++--- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/demo/client/html/svg-demo.html.hbs b/demo/client/html/svg-demo.html.hbs index ee7c431a..d019030c 100644 --- a/demo/client/html/svg-demo.html.hbs +++ b/demo/client/html/svg-demo.html.hbs @@ -56,10 +56,10 @@ diff --git a/demo/client/src/svg-loader.ts b/demo/client/src/svg-loader.ts index 5e8f0817..016f1b01 100644 --- a/demo/client/src/svg-loader.ts +++ b/demo/client/src/svg-loader.ts @@ -15,7 +15,7 @@ import * as _ from 'lodash'; import 'path-data-polyfill.js'; import {parseServerTiming, PathfinderMeshData} from "./meshes"; import {AlphaMaskCompositingOperation, RenderTask, RenderTaskType} from './render-task'; -import {panic, Range, unwrapNull, unwrapUndef} from "./utils"; +import {panic, Range, unwrapNull, unwrapUndef, lerp} from "./utils"; export const BUILTIN_SVG_URI: string = "/svg/demo"; @@ -23,9 +23,6 @@ const parseColor = require('parse-color'); const PARTITION_SVG_PATHS_ENDPOINT_URL: string = "/partition-svg-paths"; -/// The minimum size of a stroke. -const HAIRLINE_STROKE_WIDTH: number = 0.25; - declare class SVGPathSegment { type: string; values: number[]; @@ -62,7 +59,20 @@ export class SVGStroke extends SVGPath { super(element, 'stroke'); const style = window.getComputedStyle(element); - this.width = parseInt(style.strokeWidth!, 10); + const ctm = element.getCTM(); + + const strokeWidthString = unwrapNull(style.strokeWidth); + const matches = /^(\d+\.?\d*)(.*)$/.exec(strokeWidthString); + let strokeWidth; + if (matches == null) { + strokeWidth = 0.0; + } else { + strokeWidth = parseFloat(matches[1]); + if (matches[2] === 'px') + strokeWidth *= lerp(ctm.a, ctm.d, 0.5); + } + + this.width = strokeWidth; } } @@ -179,7 +189,7 @@ export class SVGLoader { this.pathBounds.push(pathBounds); } else if (instance instanceof SVGStroke) { this.paths.push({ - kind: { Stroke: Math.max(HAIRLINE_STROKE_WIDTH, instance.width) }, + kind: { Stroke: instance.width }, segments: segments, }); this.pathBounds.push(pathBounds); diff --git a/path-utils/src/stroke.rs b/path-utils/src/stroke.rs index fce2de99..59cf98ed 100644 --- a/path-utils/src/stroke.rs +++ b/path-utils/src/stroke.rs @@ -56,6 +56,8 @@ impl Stroke { /// TODO(pcwalton): Miter and round joins. fn offset_subpath(&self, output: &mut PathBuffer, input: &PathBuffer, subpath_index: u32) { + let radius = self.width * 0.5; + let subpath = &input.subpaths[subpath_index as usize]; let mut prev_position = None; @@ -65,7 +67,7 @@ impl Stroke { if let Some(ref prev_position) = prev_position { if endpoint.control_point_index == u32::MAX { - let offset_line = Line::new(&prev_position, position).offset(self.width); + let offset_line = Line::new(&prev_position, position).offset(radius); output.endpoints.extend_from_slice(&[ Endpoint { position: offset_line.endpoints[0], @@ -87,9 +89,9 @@ impl Stroke { let control_point_position = &input.control_points[endpoint.control_point_index as usize]; let offset_line_0 = - Line::new(&prev_position, control_point_position).offset(self.width); + Line::new(&prev_position, control_point_position).offset(radius); let offset_line_1 = - Line::new(control_point_position, position).offset(self.width); + Line::new(control_point_position, position).offset(radius); // FIXME(pcwalton): Can the `None` case ever happen? let offset_control_point =