Bug 1904891 - Support viewport and screen options for non-scaling-stroke r=emilio

Differential Revision: https://phabricator.services.mozilla.com/D215138
This commit is contained in:
longsonr 2024-07-03 17:42:10 +00:00
parent 36d5b49752
commit 9416db8add
17 changed files with 235 additions and 17 deletions

View File

@ -201,7 +201,9 @@ bool SVGGeometryElement::IsPointInStroke(const DOMPointInit& aPoint) {
SVGGeometryProperty::DoForComputedStyle(this, [&](const ComputedStyle* s) {
// Per spec, we should take vector-effect into account.
if (s->StyleSVGReset()->HasNonScalingStroke()) {
auto mat = SVGContentUtils::GetCTM(this);
auto mat = s->StyleSVGReset()->mVectorEffect.IsScreen()
? SVGContentUtils::GetScreenCTM(this)
: SVGContentUtils::GetCTM(this);
if (mat.HasNonTranslation()) {
// We have non-scaling-stroke as well as a non-translation transform.
// We should transform the path first then apply the stroke on the

View File

@ -174,7 +174,6 @@ rusty-enums = [
"mozilla::StyleMaskMode",
"mozilla::StyleScrollBehavior",
"mozilla::StyleColorInterpolation",
"mozilla::StyleVectorEffect",
"mozilla::StyleBackfaceVisibility",
"mozilla::StyleBlend",
"mozilla::StyleMaskComposite",
@ -588,6 +587,9 @@ cbindgen-types = [
{ gecko = "StylePageOrientation", servo = "crate::values::generics::page::PageOrientation" },
{ gecko = "StylePageSize", servo = "crate::values::computed::page::PageSize" },
{ gecko = "StyleDProperty", servo = "crate::values::specified::svg::DProperty" },
{ gecko = "StyleVectorEffectType", servo = "crate::values::specified::svg::VectorEffectType" },
{ gecko = "StyleCoordinateSpace", servo = "crate::values::specified::svg::CoordinateSpace" },
{ gecko = "StyleVectorEffect", servo = "crate::values::specified::svg::VectorEffect" },
{ gecko = "StyleImageRendering", servo = "crate::values::computed::ImageRendering" },
{ gecko = "StylePrintColorAdjust", servo = "crate::values::computed::PrintColorAdjust" },
{ gecko = "StyleForcedColorAdjust", servo = "crate::values::computed::ForcedColorAdjust" },

View File

@ -555,9 +555,6 @@ enum class StyleColorInterpolation : uint8_t {
Linearrgb = 2,
};
// vector-effect
enum class StyleVectorEffect : uint8_t { None = 0, NonScalingStroke = 1 };
// 3d Transforms - Backface visibility
enum class StyleBackfaceVisibility : uint8_t { Hidden = 0, Visible = 1 };

View File

@ -909,7 +909,6 @@ nsStyleSVGReset::nsStyleSVGReset()
mLightingColor(StyleColor::White()),
mStopOpacity(1.0f),
mFloodOpacity(1.0f),
mVectorEffect(StyleVectorEffect::None),
mMaskType(StyleMaskType::Luminance),
mD(StyleDProperty::None()) {
MOZ_COUNT_CTOR(nsStyleSVGReset);

View File

@ -1912,7 +1912,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleSVGReset {
bool HasMask() const;
bool HasNonScalingStroke() const {
return mVectorEffect == mozilla::StyleVectorEffect::NonScalingStroke;
return mVectorEffect.HasNonScalingStroke();
}
// geometry properties

View File

@ -9787,8 +9787,18 @@ var gCSSProperties = {
applies_to_first_letter: true,
applies_to_first_line: true,
initial_values: ["none"],
other_values: ["non-scaling-stroke"],
invalid_values: [],
other_values: [
"non-scaling-stroke",
"non-scaling-stroke viewport",
"non-scaling-stroke screen",
],
invalid_values: [
"none non-scaling-stroke",
"non-scaling-stroke viewport screen",
"none viewport",
"none screen",
"viewport non-scaling-stroke",
],
},
"-moz-window-dragging": {
domProp: "MozWindowDragging",

View File

@ -1078,8 +1078,11 @@ bool SVGUtils::GetNonScalingStrokeTransform(const nsIFrame* aFrame,
MOZ_ASSERT(aFrame->GetContent()->IsSVGElement(), "should be an SVG element");
*aUserToOuterSVG = ThebesMatrix(
SVGContentUtils::GetCTM(static_cast<SVGElement*>(aFrame->GetContent())));
SVGElement* content = static_cast<SVGElement*>(aFrame->GetContent());
*aUserToOuterSVG =
ThebesMatrix(aFrame->StyleSVGReset()->mVectorEffect.IsScreen()
? SVGContentUtils::GetScreenCTM(content)
: SVGContentUtils::GetCTM(content));
return aUserToOuterSVG->HasNonTranslation() && !aUserToOuterSVG->IsSingular();
}

View File

@ -617,6 +617,7 @@ class Longhand(Property):
"TouchAction",
"TransformStyle",
"UserSelect",
"VectorEffect",
"WordBreak",
"XSpan",
"XTextScale",

View File

@ -4,11 +4,11 @@
<%namespace name="helpers" file="/helpers.mako.rs" />
${helpers.single_keyword(
${helpers.predefined_type(
"vector-effect",
"none non-scaling-stroke",
"VectorEffect",
"computed::VectorEffect::none()",
engines="gecko",
gecko_enum_prefix="StyleVectorEffect",
animation_value_type="discrete",
spec="https://svgwg.org/svg2-draft/coords.html#VectorEffects",
affects="layout",

View File

@ -104,7 +104,7 @@ pub use self::rect::NonNegativeLengthOrNumberRect;
pub use self::resolution::Resolution;
pub use self::svg::{DProperty, MozContextProperties};
pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind};
pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth};
pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth, VectorEffect};
pub use self::text::HyphenateCharacter;
pub use self::text::TextUnderlinePosition;
pub use self::text::{InitialLetter, LetterSpacing, LineBreak, TextIndent};

View File

@ -10,7 +10,7 @@ use crate::values::computed::{LengthPercentage, NonNegativeLengthPercentage, Opa
use crate::values::generics::svg as generic;
use crate::Zero;
pub use crate::values::specified::{DProperty, MozContextProperties, SVGPaintOrder};
pub use crate::values::specified::{DProperty, MozContextProperties, SVGPaintOrder, VectorEffect};
/// Computed SVG Paint value
pub type SVGPaint = generic::GenericSVGPaint<Color, ComputedUrl>;

View File

@ -96,7 +96,7 @@ pub use self::rect::NonNegativeLengthOrNumberRect;
pub use self::resolution::Resolution;
pub use self::svg::{DProperty, MozContextProperties};
pub use self::svg::{SVGLength, SVGOpacity, SVGPaint};
pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth};
pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth, VectorEffect};
pub use self::svg_path::SVGPathData;
pub use self::text::HyphenateCharacter;
pub use self::text::RubyPosition;

View File

@ -402,3 +402,122 @@ impl Parse for DProperty {
Ok(DProperty::Path(path_data))
}
}
#[derive(
Clone,
Copy,
Debug,
Default,
Eq,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[css(bitflags(single = "none", mixed = "non-scaling-stroke"))]
#[repr(C)]
/// Values for vector-effect:
/// https://svgwg.org/svg2-draft/coords.html#VectorEffects
pub struct VectorEffectType(u8);
bitflags! {
impl VectorEffectType: u8 {
/// `none`
const NONE = 0;
/// `non-scaling-stroke`
const NON_SCALING_STROKE = 1 << 0;
}
}
#[allow(missing_docs)]
impl VectorEffectType {
pub fn is_none(&self) -> bool {
*self == VectorEffectType::NONE
}
}
#[derive(
Clone,
Copy,
Debug,
Default,
Eq,
MallocSizeOf,
Parse,
PartialEq,
SpecifiedValueInfo,
ToComputedValue,
ToCss,
ToResolvedValue,
ToShmem,
)]
#[repr(C)]
/// co-ordinate space for vector-effect:
/// https://svgwg.org/svg2-draft/coords.html#VectorEffects
pub enum CoordinateSpace {
#[default]
/// `viewport`
Viewport,
/// `screen`
Screen,
}
#[allow(missing_docs)]
impl CoordinateSpace {
pub fn is_viewport(&self) -> bool {
*self == Self::Viewport
}
}
#[derive(
Clone,
Copy,
Debug,
MallocSizeOf,
PartialEq,
SpecifiedValueInfo,
ToCss,
ToComputedValue,
ToResolvedValue,
ToShmem,
)]
#[repr(C)]
/// Specified value for the vector-effect property.
/// (The spec grammar gives
/// `none | [ non-scaling-stroke | non-scaling-size | non-rotation | fixed-position ]+ [ viewport | screen ]?`.)
/// https://svgwg.org/svg2-draft/coords.html#VectorEffects
pub struct VectorEffect {
/// none or non-scaling-stroke
pub effect_type: VectorEffectType,
/// screen or viewport
#[css(skip_if = "CoordinateSpace::is_viewport")]
pub coordinate_space: CoordinateSpace,
}
#[allow(missing_docs)]
impl VectorEffect {
#[inline]
pub fn none() -> Self {
Self {
effect_type: VectorEffectType::NONE,
coordinate_space: CoordinateSpace::Viewport,
}
}
}
impl Parse for VectorEffect {
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
let effect_type = VectorEffectType::parse(context, input)?;
if effect_type.is_none() {
return Ok(Self::none());
}
let coordinate_space = input.try_parse(CoordinateSpace::parse).unwrap_or(CoordinateSpace::Viewport);
Ok(Self { effect_type, coordinate_space })
}
}

View File

@ -307,6 +307,7 @@ include = [
"ComputedLinearStop",
"PiecewiseLinearFunction",
"BeforeFlag",
"VectorEffect",
"XTextScale",
"Zoom",
"TransitionProperty",
@ -555,6 +556,15 @@ renaming_overrides_prefixing = true
inline bool IsRight() const;
"""
"VectorEffect" = """
StyleVectorEffect()
: effect_type(StyleVectorEffectType::NONE),
coordinate_space(StyleCoordinateSpace::Viewport) {}
bool HasNonScalingStroke() const { return bool(effect_type & StyleVectorEffectType::NON_SCALING_STROKE); }
bool IsScreen() const { return coordinate_space == StyleCoordinateSpace::Screen; }
"""
# TODO(emilio): Add hooks to cbindgen to be able to generate [[nodiscard]]
# on the functions.
"Owned" = """

View File

@ -0,0 +1,33 @@
<!DOCTYPE html>
<html>
<title>non-scaling-stroke with screen transform</title>
<link rel="help" href="https://svgwg.org/svg2-draft/painting.html#PaintingVectorEffects" />
<link rel="match" href="green-100x100.svg" />
<body>
<style>
body {
border: none;
margin: 0;
width: 200px;
height: 200px;
transform: scale(0.5);
}
svg {
width: 100px;
height: 100px;
transform: scale(2) translate(-25px, -25px);
transform-origin: center;
transform-bxox: fill-box;
}
rect {
fill: red;
stroke: green;
stroke-width: 75px;
vector-effect: non-scaling-stroke screen;
}
</style>
<svg>
<rect width="75" height="75"/>
</svg>
</body>
</html>

View File

@ -0,0 +1,23 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>vector-effect test: parsing vector-effect with invalid values</title>
<link rel="help" href="https://www.w3.org/TR/SVG2/coords.html#VectorEffects">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/support/parsing-testcommon.js"></script>
</head>
<body>
<script>
test_invalid_value("vector-effect", "none none");
test_invalid_value("vector-effect", "none non-scaling-stroke");
test_invalid_value("vector-effect", "non-scaling-stroke viewport screen");
test_invalid_value("vector-effect", "none viewport");
test_invalid_value("vector-effect", "none screen");
test_invalid_value("vector-effect", "viewport");
test_invalid_value("vector-effect", "screen");
test_invalid_value("vector-effect", "screen non-scaling-stroke");
</script>
</body>
</html>

View File

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>vector-effect test: parsing vector-effect with invalid values</title>
<link rel="help" href="https://www.w3.org/TR/SVG2/coords.html#VectorEffects">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/css/support/parsing-testcommon.js"></script>
</head>
<body>
<script>
test_valid_value("vector-effect", "none");
test_valid_value("vector-effect", "non-scaling-stroke");
test_valid_value("vector-effect", "non-scaling-stroke viewport", "non-scaling-stroke");
test_valid_value("vector-effect", "non-scaling-stroke screen");
</script>
</body>
</html>