Bug 1903361 - Make SVG paths respect zoom. r=longsonr

Differential Revision: https://phabricator.services.mozilla.com/D214175
This commit is contained in:
Emilio Cobos Álvarez 2024-06-27 11:12:47 +00:00
Родитель 7a0a28135d
Коммит 3eadcfef57
8 изменённых файлов: 75 добавлений и 47 удалений

Просмотреть файл

@ -884,7 +884,9 @@ already_AddRefed<gfx::Path> SVGContentUtils::GetPath(
RefPtr<PathBuilder> builder =
drawTarget->CreatePathBuilder(FillRule::FILL_WINDING);
return pathData.BuildPath(builder, StyleStrokeLinecap::Butt, 1);
// This is called from canvas, so we don't need to get the effective zoom here
// or so.
return pathData.BuildPath(builder, StyleStrokeLinecap::Butt, 1, 1.0f);
}
bool SVGContentUtils::ShapeTypeHasNoCorners(const nsIContent* aContent) {

Просмотреть файл

@ -232,7 +232,7 @@ void SVGMotionSMILAnimationFunction::RebuildPathAndVerticesFromPathAttr() {
return;
}
mPath = path.BuildPathForMeasuring();
mPath = path.BuildPathForMeasuring(1.0f);
bool ok = path.GetDistancesFromOriginToEndsOfVisibleSegments(&mPathVertices);
if (!ok || !mPathVertices.Length()) {
mPath = nullptr;

Просмотреть файл

@ -162,13 +162,15 @@ static void ApproximateZeroLengthSubpathSquareCaps(PathBuilder* aPB,
already_AddRefed<Path> SVGPathData::BuildPath(PathBuilder* aBuilder,
StyleStrokeLinecap aStrokeLineCap,
Float aStrokeWidth) const {
return BuildPath(AsSpan(), aBuilder, aStrokeLineCap, aStrokeWidth);
Float aStrokeWidth,
float aZoom) const {
return BuildPath(AsSpan(), aBuilder, aStrokeLineCap, aStrokeWidth, {}, {},
aZoom);
}
#undef MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS_TO_DT
already_AddRefed<Path> SVGPathData::BuildPathForMeasuring() const {
already_AddRefed<Path> SVGPathData::BuildPathForMeasuring(float aZoom) const {
// Since the path that we return will not be used for painting it doesn't
// matter what we pass to CreatePathBuilder as aFillRule. Hawever, we do want
// to pass something other than NS_STYLE_STROKE_LINECAP_SQUARE as
@ -181,17 +183,17 @@ already_AddRefed<Path> SVGPathData::BuildPathForMeasuring() const {
gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
RefPtr<PathBuilder> builder =
drawTarget->CreatePathBuilder(FillRule::FILL_WINDING);
return BuildPath(builder, StyleStrokeLinecap::Butt, 0);
return BuildPath(builder, StyleStrokeLinecap::Butt, 0, aZoom);
}
/* static */
already_AddRefed<Path> SVGPathData::BuildPathForMeasuring(
Span<const StylePathCommand> aPath) {
Span<const StylePathCommand> aPath, float aZoom) {
RefPtr<DrawTarget> drawTarget =
gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
RefPtr<PathBuilder> builder =
drawTarget->CreatePathBuilder(FillRule::FILL_WINDING);
return BuildPath(aPath, builder, StyleStrokeLinecap::Butt, 0);
return BuildPath(aPath, builder, StyleStrokeLinecap::Butt, 0, {}, {}, aZoom);
}
static inline StyleCSSFloat GetRotate(const StyleCSSFloat& aAngle) {
@ -525,8 +527,9 @@ ComputeSegAnglesAndCorrectRadii(const Point& aSegStart, const Point& aSegEnd,
static_cast<float>(atan2(ty2, tx2))};
}
void SVGPathData::GetMarkerPositioningData(nsTArray<SVGMark>* aMarks) const {
return GetMarkerPositioningData(AsSpan(), aMarks);
void SVGPathData::GetMarkerPositioningData(float aZoom,
nsTArray<SVGMark>* aMarks) const {
return GetMarkerPositioningData(AsSpan(), aZoom, aMarks);
}
// Basically, this is identical to the above function, but replace |mData| with
@ -535,6 +538,7 @@ void SVGPathData::GetMarkerPositioningData(nsTArray<SVGMark>* aMarks) const {
// StylePathCommand for SVG d attribute in the future.
/* static */
void SVGPathData::GetMarkerPositioningData(Span<const StylePathCommand> aPath,
float aZoom,
nsTArray<SVGMark>* aMarks) {
if (aPath.IsEmpty()) {
return;
@ -564,7 +568,7 @@ void SVGPathData::GetMarkerPositioningData(Span<const StylePathCommand> aPath,
break;
case StylePathCommand::Tag::Move: {
const Point& p = cmd.move.point.ToGfxPoint();
const Point& p = cmd.move.point.ToGfxPoint() * aZoom;
pathStart = segEnd = cmd.move.by_to == StyleByTo::To ? p : segStart + p;
pathStartIndex = aMarks->Length();
// If authors are going to specify multiple consecutive moveto commands
@ -573,15 +577,15 @@ void SVGPathData::GetMarkerPositioningData(Span<const StylePathCommand> aPath,
break;
}
case StylePathCommand::Tag::Line: {
const Point& p = cmd.line.point.ToGfxPoint();
const Point& p = cmd.line.point.ToGfxPoint() * aZoom;
segEnd = cmd.line.by_to == StyleByTo::To ? p : segStart + p;
segStartAngle = segEndAngle = AngleOfVector(segEnd, segStart);
break;
}
case StylePathCommand::Tag::CubicCurve: {
Point cp1 = cmd.cubic_curve.control1.ToGfxPoint();
Point cp2 = cmd.cubic_curve.control2.ToGfxPoint();
segEnd = cmd.cubic_curve.point.ToGfxPoint();
Point cp1 = cmd.cubic_curve.control1.ToGfxPoint() * aZoom;
Point cp2 = cmd.cubic_curve.control2.ToGfxPoint() * aZoom;
segEnd = cmd.cubic_curve.point.ToGfxPoint() * aZoom;
if (cmd.cubic_curve.by_to == StyleByTo::By) {
cp1 += segStart;
@ -597,8 +601,8 @@ void SVGPathData::GetMarkerPositioningData(Span<const StylePathCommand> aPath,
break;
}
case StylePathCommand::Tag::QuadCurve: {
Point cp1 = cmd.quad_curve.control1.ToGfxPoint();
segEnd = cmd.quad_curve.point.ToGfxPoint();
Point cp1 = cmd.quad_curve.control1.ToGfxPoint() * aZoom;
segEnd = cmd.quad_curve.point.ToGfxPoint() * aZoom;
if (cmd.quad_curve.by_to == StyleByTo::By) {
cp1 += segStart;
@ -612,12 +616,12 @@ void SVGPathData::GetMarkerPositioningData(Span<const StylePathCommand> aPath,
}
case StylePathCommand::Tag::Arc: {
const auto& arc = cmd.arc;
float rx = arc.radii.x;
float ry = arc.radii.y;
float rx = arc.radii.x * aZoom;
float ry = arc.radii.y * aZoom;
float angle = arc.rotate;
bool largeArcFlag = arc.arc_size == StyleArcSize::Large;
bool sweepFlag = arc.arc_sweep == StyleArcSweep::Cw;
segEnd = arc.point.ToGfxPoint();
segEnd = arc.point.ToGfxPoint() * aZoom;
if (arc.by_to == StyleByTo::By) {
segEnd += segStart;
}
@ -654,18 +658,18 @@ void SVGPathData::GetMarkerPositioningData(Span<const StylePathCommand> aPath,
}
case StylePathCommand::Tag::HLine: {
if (cmd.h_line.by_to == StyleByTo::To) {
segEnd = Point(cmd.h_line.x, segStart.y);
segEnd = Point(cmd.h_line.x, segStart.y) * aZoom;
} else {
segEnd = segStart + Point(cmd.h_line.x, 0.0f);
segEnd = segStart + Point(cmd.h_line.x, 0.0f) * aZoom;
}
segStartAngle = segEndAngle = AngleOfVector(segEnd, segStart);
break;
}
case StylePathCommand::Tag::VLine: {
if (cmd.v_line.by_to == StyleByTo::To) {
segEnd = Point(segStart.x, cmd.v_line.y);
segEnd = Point(segStart.x, cmd.v_line.y) * aZoom;
} else {
segEnd = segStart + Point(0.0f, cmd.v_line.y);
segEnd = segStart + Point(0.0f, cmd.v_line.y) * aZoom;
}
segStartAngle = segEndAngle = AngleOfVector(segEnd, segStart);
break;
@ -674,8 +678,8 @@ void SVGPathData::GetMarkerPositioningData(Span<const StylePathCommand> aPath,
const Point& cp1 = prevSeg && prevSeg->IsCubicType()
? segStart * 2 - prevCP
: segStart;
Point cp2 = cmd.smooth_cubic.control2.ToGfxPoint();
segEnd = cmd.smooth_cubic.point.ToGfxPoint();
Point cp2 = cmd.smooth_cubic.control2.ToGfxPoint() * aZoom;
segEnd = cmd.smooth_cubic.point.ToGfxPoint() * aZoom;
if (cmd.smooth_cubic.by_to == StyleByTo::By) {
cp2 += segStart;
@ -694,8 +698,8 @@ void SVGPathData::GetMarkerPositioningData(Span<const StylePathCommand> aPath,
? segStart * 2 - prevCP
: segStart;
segEnd = cmd.smooth_quad.by_to == StyleByTo::To
? cmd.smooth_quad.point.ToGfxPoint()
: segStart + cmd.smooth_quad.point.ToGfxPoint();
? cmd.smooth_quad.point.ToGfxPoint() * aZoom
: segStart + cmd.smooth_quad.point.ToGfxPoint() * aZoom;
prevCP = cp1;
segStartAngle = AngleOfVector(cp1 == segStart ? segEnd : cp1, segStart);
@ -738,7 +742,7 @@ void SVGPathData::GetMarkerPositioningData(Span<const StylePathCommand> aPath,
prevSegEndAngle = segEndAngle;
}
if (aMarks->Length()) {
if (!aMarks->IsEmpty()) {
if (!(prevSeg && prevSeg->IsClose())) {
aMarks->LastElement().angle = prevSegEndAngle;
}

Просмотреть файл

@ -119,11 +119,10 @@ class SVGPathData {
static uint32_t GetPathSegAtLength(Span<const StylePathCommand> aPath,
float aDistance);
void GetMarkerPositioningData(nsTArray<SVGMark>* aMarks) const;
void GetMarkerPositioningData(float aZoom, nsTArray<SVGMark>* aMarks) const;
static void GetMarkerPositioningData(Span<const StylePathCommand> aPath,
nsTArray<SVGMark>* aMarks);
float aZoom, nsTArray<SVGMark>* aMarks);
/**
* Returns true, except on OOM, in which case returns false.
*/
@ -141,14 +140,14 @@ class SVGPathData {
* ApproximateZeroLengthSubpathSquareCaps can insert if we have square-caps.
* See the comment for that function for more info on that.
*/
already_AddRefed<Path> BuildPathForMeasuring() const;
already_AddRefed<Path> BuildPathForMeasuring(float aZoom) const;
already_AddRefed<Path> BuildPath(PathBuilder* aBuilder,
StyleStrokeLinecap aStrokeLineCap,
Float aStrokeWidth) const;
Float aStrokeWidth, float aZoom) const;
static already_AddRefed<Path> BuildPathForMeasuring(
Span<const StylePathCommand> aPath);
Span<const StylePathCommand> aPath, float aZoom);
/**
* This function tries to build the path from an array of GenericShapeCommand,
@ -157,11 +156,13 @@ class SVGPathData {
* Note: |StylePathCommand| doesn't accept percentage values, so its |aBasis|
* is empty by default.
*/
static already_AddRefed<Path> BuildPath(
Span<const StylePathCommand> aPath, PathBuilder* aBuilder,
StyleStrokeLinecap aStrokeLineCap, Float aStrokeWidth,
const CSSSize& aBasis = {}, const gfx::Point& aOffset = gfx::Point(),
float aZoomFactor = 1.0);
static already_AddRefed<Path> BuildPath(Span<const StylePathCommand> aPath,
PathBuilder* aBuilder,
StyleStrokeLinecap aStrokeLineCap,
Float aStrokeWidth,
const CSSSize& aBasis = {},
const gfx::Point& aOffset = {},
float aZoomFactor = 1.0);
static already_AddRefed<Path> BuildPath(
Span<const StyleShapeCommand> aShape, PathBuilder* aBuilder,
StyleStrokeLinecap aStrokeLineCap, Float aStrokeWidth,

Просмотреть файл

@ -88,9 +88,11 @@ already_AddRefed<Path> SVGPathElement::GetOrBuildPathForMeasuring() {
if (d.IsNone()) {
return;
}
path = SVGPathData::BuildPathForMeasuring(d.AsPath()._0.AsSpan());
path = SVGPathData::BuildPathForMeasuring(d.AsPath()._0.AsSpan(),
s->EffectiveZoom().ToFloat());
});
return success ? path.forget() : mD.GetAnimValue().BuildPathForMeasuring();
return success ? path.forget()
: mD.GetAnimValue().BuildPathForMeasuring(1.0f);
}
//----------------------------------------------------------------------
@ -108,7 +110,8 @@ void SVGPathElement::GetMarkPoints(nsTArray<SVGMark>* aMarks) {
if (styleSVGReset->mD.IsPath()) {
Span<const StylePathCommand> path =
styleSVGReset->mD.AsPath()._0.AsSpan();
SVGPathData::GetMarkerPositioningData(path, aMarks);
SVGPathData::GetMarkerPositioningData(path, s->EffectiveZoom().ToFloat(),
aMarks);
}
};
@ -116,7 +119,7 @@ void SVGPathElement::GetMarkPoints(nsTArray<SVGMark>* aMarks) {
return;
}
mD.GetAnimValue().GetMarkerPositioningData(aMarks);
mD.GetAnimValue().GetMarkerPositioningData(1.0f, aMarks);
}
void SVGPathElement::GetAsSimplePath(SimplePath* aSimplePath) {
@ -162,7 +165,8 @@ already_AddRefed<Path> SVGPathElement::BuildPath(PathBuilder* aBuilder) {
const auto& d = s->StyleSVGReset()->mD;
if (d.IsPath()) {
path = SVGPathData::BuildPath(d.AsPath()._0.AsSpan(), aBuilder,
strokeLineCap, strokeWidth);
strokeLineCap, strokeWidth, {}, {},
s->EffectiveZoom().ToFloat());
}
};
@ -172,7 +176,8 @@ already_AddRefed<Path> SVGPathElement::BuildPath(PathBuilder* aBuilder) {
}
// Fallback to use the d attribute if it exists.
return mD.GetAnimValue().BuildPath(aBuilder, strokeLineCap, strokeWidth);
return mD.GetAnimValue().BuildPath(aBuilder, strokeLineCap, strokeWidth,
1.0f);
}
bool SVGPathElement::GetDistancesFromOriginToEndsOfVisibleSegments(

Просмотреть файл

@ -4506,7 +4506,8 @@ already_AddRefed<Path> SVGTextFrame::GetTextPath(nsIFrame* aTextPathFrame) {
if (tp->mPath.IsRendered()) {
// This is just an attribute so there's no transform that can apply
// so we can just return the path directly.
return tp->mPath.GetAnimValue().BuildPathForMeasuring();
return tp->mPath.GetAnimValue().BuildPathForMeasuring(
aTextPathFrame->Style()->EffectiveZoom().ToFloat());
}
SVGGeometryElement* geomElement =

Просмотреть файл

@ -0,0 +1,6 @@
<!doctype html>
<meta charset="utf-8">
<svg width="320" height="200" style="background: #006AA7;">
<path d="M120 0 L 120 320Z" stroke="#FECC02" stroke-width="40"/>
<rect x="0" y="80" width="320" height="40" fill="#FECC02" />
</svg>

Просмотреть файл

@ -0,0 +1,9 @@
<!doctype html>
<meta charset="utf-8">
<link rel="help" href="https://drafts.csswg.org/css-viewport/#zoom-property">
<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1903361">
<link rel="match" href="svg-path-ref.html">
<svg width="160" height="100" style="zoom: 2; background: #006AA7;">
<path d="M60 0 L 60 160Z" stroke="#FECC02" stroke-width="20"/>
<rect x="0" y="40" width="160" height="20" fill="#FECC02" />
</svg>