зеркало из https://github.com/mozilla/gecko-dev.git
Bug 932761 - Implement Moz2D path builders for all of the SVG element types. r=dholbert
This commit is contained in:
Родитель
ac08cd69cc
Коммит
121dda43b7
|
@ -4,12 +4,15 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/dom/SVGCircleElement.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "nsGkAtoms.h"
|
||||
#include "gfxContext.h"
|
||||
#include "mozilla/dom/SVGCircleElementBinding.h"
|
||||
|
||||
NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Circle)
|
||||
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
|
@ -90,5 +93,20 @@ SVGCircleElement::ConstructPath(gfxContext *aCtx)
|
|||
aCtx->Arc(gfxPoint(x, y), r, 0, 2*M_PI);
|
||||
}
|
||||
|
||||
TemporaryRef<Path>
|
||||
SVGCircleElement::BuildPath()
|
||||
{
|
||||
RefPtr<PathBuilder> pathBuilder = CreatePathBuilder();
|
||||
|
||||
float x, y, r;
|
||||
GetAnimatedLengthValues(&x, &y, &r, nullptr);
|
||||
|
||||
if (r > 0.0f) {
|
||||
pathBuilder->Arc(Point(x, y), r, 0, Float(2*M_PI));
|
||||
}
|
||||
|
||||
return pathBuilder->Finish();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -32,6 +32,7 @@ public:
|
|||
|
||||
// nsSVGPathGeometryElement methods:
|
||||
virtual void ConstructPath(gfxContext *aCtx) MOZ_OVERRIDE;
|
||||
virtual TemporaryRef<Path> BuildPath() MOZ_OVERRIDE;
|
||||
|
||||
virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE;
|
||||
|
||||
|
|
|
@ -565,3 +565,25 @@ SVGContentUtils::ParseInteger(const nsAString& aString,
|
|||
int64_t(std::numeric_limits<int32_t>::max())));
|
||||
return true;
|
||||
}
|
||||
|
||||
float
|
||||
SVGContentUtils::CoordToFloat(nsPresContext *aPresContext,
|
||||
nsSVGElement *aContent,
|
||||
const nsStyleCoord &aCoord)
|
||||
{
|
||||
switch (aCoord.GetUnit()) {
|
||||
case eStyleUnit_Factor:
|
||||
// user units
|
||||
return aCoord.GetFactorValue();
|
||||
|
||||
case eStyleUnit_Coord:
|
||||
return nsPresContext::AppUnitsToFloatCSSPixels(aCoord.GetCoordValue());
|
||||
|
||||
case eStyleUnit_Percent: {
|
||||
SVGSVGElement* ctx = aContent->GetCtx();
|
||||
return ctx ? aCoord.GetPercentValue() * ctx->GetLength(SVGContentUtils::XY) : 0.0f;
|
||||
}
|
||||
default:
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,9 @@
|
|||
class nsIContent;
|
||||
class nsIDocument;
|
||||
class nsIFrame;
|
||||
class nsPresContext;
|
||||
class nsStyleContext;
|
||||
class nsStyleCoord;
|
||||
class nsSVGElement;
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -169,6 +171,15 @@ public:
|
|||
*/
|
||||
static bool
|
||||
ParseInteger(const nsAString& aString, int32_t& aValue);
|
||||
|
||||
/**
|
||||
* Converts an nsStyleCoord into a userspace value. Handles units
|
||||
* Factor (straight userspace), Coord (dimensioned), and Percent (of
|
||||
* aContent's SVG viewport)
|
||||
*/
|
||||
static float CoordToFloat(nsPresContext *aPresContext,
|
||||
nsSVGElement *aContent,
|
||||
const nsStyleCoord &aCoord);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -5,10 +5,14 @@
|
|||
|
||||
#include "mozilla/dom/SVGEllipseElement.h"
|
||||
#include "mozilla/dom/SVGEllipseElementBinding.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "mozilla/gfx/PathHelpers.h"
|
||||
#include "gfxContext.h"
|
||||
|
||||
NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Ellipse)
|
||||
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
|
@ -100,5 +104,20 @@ SVGEllipseElement::ConstructPath(gfxContext *aCtx)
|
|||
}
|
||||
}
|
||||
|
||||
TemporaryRef<Path>
|
||||
SVGEllipseElement::BuildPath()
|
||||
{
|
||||
RefPtr<PathBuilder> pathBuilder = CreatePathBuilder();
|
||||
|
||||
float x, y, rx, ry;
|
||||
GetAnimatedLengthValues(&x, &y, &rx, &ry, nullptr);
|
||||
|
||||
if (rx > 0.0f && ry > 0.0f) {
|
||||
AppendEllipseToPath(pathBuilder, Point(x, y), Size(2.0*rx, 2.0*ry));
|
||||
}
|
||||
|
||||
return pathBuilder->Finish();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -32,6 +32,7 @@ public:
|
|||
|
||||
// nsSVGPathGeometryElement methods:
|
||||
virtual void ConstructPath(gfxContext *aCtx) MOZ_OVERRIDE;
|
||||
virtual TemporaryRef<Path> BuildPath() MOZ_OVERRIDE;
|
||||
|
||||
virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE;
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "mozilla/Util.h"
|
||||
|
||||
#include "mozilla/dom/SVGImageElement.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsNetUtil.h"
|
||||
|
@ -16,6 +17,8 @@
|
|||
|
||||
NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Image)
|
||||
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
|
@ -236,6 +239,29 @@ SVGImageElement::ConstructPath(gfxContext *aCtx)
|
|||
aCtx->Rectangle(gfxRect(x, y, width, height));
|
||||
}
|
||||
|
||||
TemporaryRef<Path>
|
||||
SVGImageElement::BuildPath()
|
||||
{
|
||||
// We get called in order to get bounds for this element, and for
|
||||
// hit-testing against it. For that we just pretend to be a rectangle.
|
||||
|
||||
RefPtr<PathBuilder> pathBuilder = CreatePathBuilder();
|
||||
|
||||
float x, y, width, height;
|
||||
GetAnimatedLengthValues(&x, &y, &width, &height, nullptr);
|
||||
|
||||
if (width <= 0 || height <= 0) {
|
||||
Rect r(x, y, width, height);
|
||||
pathBuilder->MoveTo(r.TopLeft());
|
||||
pathBuilder->LineTo(r.TopRight());
|
||||
pathBuilder->LineTo(r.BottomRight());
|
||||
pathBuilder->LineTo(r.BottomLeft());
|
||||
pathBuilder->Close();
|
||||
}
|
||||
|
||||
return pathBuilder->Finish();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// nsSVGElement methods
|
||||
|
||||
|
|
|
@ -55,6 +55,7 @@ public:
|
|||
|
||||
// nsSVGPathGeometryElement methods:
|
||||
virtual void ConstructPath(gfxContext *aCtx) MOZ_OVERRIDE;
|
||||
virtual TemporaryRef<Path> BuildPath() MOZ_OVERRIDE;
|
||||
|
||||
// nsSVGSVGElement methods:
|
||||
virtual bool HasValidDimensions() const MOZ_OVERRIDE;
|
||||
|
|
|
@ -5,10 +5,13 @@
|
|||
|
||||
#include "mozilla/dom/SVGLineElement.h"
|
||||
#include "mozilla/dom/SVGLineElementBinding.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "gfxContext.h"
|
||||
|
||||
NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Line)
|
||||
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
|
@ -115,5 +118,19 @@ SVGLineElement::ConstructPath(gfxContext *aCtx)
|
|||
aCtx->LineTo(gfxPoint(x2, y2));
|
||||
}
|
||||
|
||||
TemporaryRef<Path>
|
||||
SVGLineElement::BuildPath()
|
||||
{
|
||||
RefPtr<PathBuilder> pathBuilder = CreatePathBuilder();
|
||||
|
||||
float x1, y1, x2, y2;
|
||||
GetAnimatedLengthValues(&x1, &y1, &x2, &y2, nullptr);
|
||||
|
||||
pathBuilder->MoveTo(Point(x1, y1));
|
||||
pathBuilder->LineTo(Point(x2, y2));
|
||||
|
||||
return pathBuilder->Finish();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -34,6 +34,7 @@ public:
|
|||
virtual bool IsMarkable() MOZ_OVERRIDE { return true; }
|
||||
virtual void GetMarkPoints(nsTArray<nsSVGMark> *aMarks) MOZ_OVERRIDE;
|
||||
virtual void ConstructPath(gfxContext *aCtx) MOZ_OVERRIDE;
|
||||
virtual TemporaryRef<Path> BuildPath() MOZ_OVERRIDE;
|
||||
|
||||
virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "nsSVGPathDataParser.h"
|
||||
#include "nsSVGPathGeometryElement.h" // for nsSVGMark
|
||||
#include <stdarg.h>
|
||||
#include "nsStyleConsts.h"
|
||||
#include "SVGContentUtils.h"
|
||||
#include "SVGPathSegUtils.h"
|
||||
#include "gfxContext.h"
|
||||
|
@ -217,18 +218,51 @@ SVGPathData::GetPathSegAtLength(float aDistance) const
|
|||
*
|
||||
* Cairo only does this for |stroke-linecap: round| and not for
|
||||
* |stroke-linecap: square| (since that's what Adobe Acrobat has always done).
|
||||
* Most likely the other backends that DrawTarget uses have the same behavior.
|
||||
*
|
||||
* To help us conform to the SVG spec we have this helper function to draw an
|
||||
* approximation of square caps for zero length subpaths. It does this by
|
||||
* inserting a subpath containing a single axis aligned straight line that is
|
||||
* as small as it can be without cairo throwing it away for being too small to
|
||||
* affect rendering. Cairo will then draw stroke caps for this axis aligned
|
||||
* line, creating an axis aligned rectangle (approximating the square that
|
||||
* would ideally be drawn).
|
||||
* inserting a subpath containing a single user space axis aligned straight
|
||||
* line that is as small as it can be while minimizing the risk of it being
|
||||
* thrown away by the DrawTarget's backend for being too small to affect
|
||||
* rendering. The idea is that we'll then get stroke caps drawn for this axis
|
||||
* aligned line, creating an axis aligned rectangle that approximates the
|
||||
* square that would ideally be drawn.
|
||||
*
|
||||
* Since we don't have any information about transforms from user space to
|
||||
* device space, we choose the length of the small line that we insert by
|
||||
* making it a small percentage of the stroke width of the path. This should
|
||||
* hopefully allow us to make the line as long as possible (to avoid rounding
|
||||
* issues in the backend resulting in the backend seeing it as having zero
|
||||
* length) while still avoiding the small rectangle being noticably different
|
||||
* from a square.
|
||||
*
|
||||
* Note that this function inserts a subpath into the current gfx path that
|
||||
* will be present during both fill and stroke operations.
|
||||
*/
|
||||
static void
|
||||
ApproximateZeroLengthSubpathSquareCaps(PathBuilder* aPB,
|
||||
const Point& aPoint,
|
||||
Float aStrokeWidth)
|
||||
{
|
||||
// Note that caps are proportional to stroke width, so if stroke width is
|
||||
// zero it's actually fine for |tinyLength| below to end up being zero.
|
||||
// However, it would be a waste to inserting a LineTo in that case, so better
|
||||
// not to.
|
||||
MOZ_ASSERT(aStrokeWidth > 0.0f,
|
||||
"Make the caller check for this, or check it here");
|
||||
|
||||
// The fraction of the stroke width that we choose for the length of the
|
||||
// line is rather arbitrary, other than being chosen to meet the requirements
|
||||
// described in the comment above.
|
||||
|
||||
Float tinyLength = aStrokeWidth / 32;
|
||||
|
||||
aPB->MoveTo(aPoint);
|
||||
aPB->LineTo(aPoint + Point(tinyLength, 0));
|
||||
aPB->MoveTo(aPoint);
|
||||
}
|
||||
|
||||
static void
|
||||
ApproximateZeroLengthSubpathSquareCaps(const gfxPoint &aPoint, gfxContext *aCtx)
|
||||
{
|
||||
|
@ -244,32 +278,12 @@ ApproximateZeroLengthSubpathSquareCaps(const gfxPoint &aPoint, gfxContext *aCtx)
|
|||
aCtx->MoveTo(aPoint);
|
||||
}
|
||||
|
||||
static void
|
||||
ApproximateZeroLengthSubpathSquareCaps(const Point& aPoint,
|
||||
DrawTarget* aDT,
|
||||
PathBuilder* aPB)
|
||||
{
|
||||
// Cairo's fixed point fractional part is 8 bits wide, so its device space
|
||||
// coordinate granularity is 1/256 pixels. However, to prevent user space
|
||||
// |aPoint| and |aPoint + tinyAdvance| being rounded to the same device
|
||||
// coordinates, we double this for |tinyAdvance|:
|
||||
|
||||
Matrix currentTransform = aDT->GetTransform();
|
||||
currentTransform.Invert();
|
||||
Size tinyAdvance = currentTransform * Size(2.0/256.0, 0.0);
|
||||
|
||||
aPB->MoveTo(aPoint);
|
||||
aPB->LineTo(aPoint + Point(tinyAdvance.width, tinyAdvance.height));
|
||||
aPB->MoveTo(aPoint);
|
||||
}
|
||||
|
||||
#define MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS_TO_DT \
|
||||
do { \
|
||||
if (capsAreSquare && !subpathHasLength && subpathContainsNonArc && \
|
||||
SVGPathSegUtils::IsValidType(prevSegType) && \
|
||||
(!IsMoveto(prevSegType) || \
|
||||
segType == PATHSEG_CLOSEPATH)) { \
|
||||
ApproximateZeroLengthSubpathSquareCaps(segStart, aDT, builder); \
|
||||
if (capsAreSquare && !subpathHasLength && aStrokeWidth > 0 && \
|
||||
subpathContainsNonArc && SVGPathSegUtils::IsValidType(prevSegType) && \
|
||||
(!IsMoveto(prevSegType) || segType == PATHSEG_CLOSEPATH)) { \
|
||||
ApproximateZeroLengthSubpathSquareCaps(builder, segStart, aStrokeWidth);\
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
@ -284,17 +298,23 @@ ApproximateZeroLengthSubpathSquareCaps(const Point& aPoint,
|
|||
} while(0)
|
||||
|
||||
TemporaryRef<Path>
|
||||
SVGPathData::ConstructPath(DrawTarget *aDT,
|
||||
FillRule aFillRule,
|
||||
CapStyle aCapStyle) const
|
||||
SVGPathData::BuildPath(FillRule aFillRule,
|
||||
uint8_t aStrokeLineCap,
|
||||
Float aStrokeWidth) const
|
||||
{
|
||||
if (mData.IsEmpty() || !IsMoveto(SVGPathSegUtils::DecodeType(mData[0]))) {
|
||||
return nullptr; // paths without an initial moveto are invalid
|
||||
}
|
||||
|
||||
RefPtr<PathBuilder> builder = aDT->CreatePathBuilder(aFillRule);
|
||||
RefPtr<DrawTarget> drawTarget =
|
||||
gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
|
||||
NS_ASSERTION(gfxPlatform::GetPlatform()->
|
||||
SupportsAzureContentForDrawTarget(drawTarget),
|
||||
"Should support Moz2D content drawing");
|
||||
|
||||
bool capsAreSquare = aCapStyle == CAP_SQUARE;
|
||||
RefPtr<PathBuilder> builder = drawTarget->CreatePathBuilder(aFillRule);
|
||||
|
||||
bool capsAreSquare = aStrokeLineCap == NS_STYLE_STROKE_LINECAP_SQUARE;
|
||||
bool subpathHasLength = false; // visual length
|
||||
bool subpathContainsNonArc = false;
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "nsINode.h"
|
||||
#include "nsIWeakReferenceUtils.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "mozilla/gfx/Types.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsSVGElement.h"
|
||||
#include "nsTArray.h"
|
||||
|
@ -85,6 +86,7 @@ class SVGPathData
|
|||
typedef gfx::DrawTarget DrawTarget;
|
||||
typedef gfx::Path Path;
|
||||
typedef gfx::FillRule FillRule;
|
||||
typedef gfx::Float Float;
|
||||
typedef gfx::CapStyle CapStyle;
|
||||
|
||||
public:
|
||||
|
@ -161,9 +163,9 @@ public:
|
|||
ToPath(const gfxMatrix& aMatrix) const;
|
||||
|
||||
void ConstructPath(gfxContext *aCtx) const;
|
||||
TemporaryRef<Path> ConstructPath(DrawTarget* aDT,
|
||||
FillRule aFillRule,
|
||||
CapStyle aCapStyle) const;
|
||||
TemporaryRef<Path> BuildPath(FillRule aFillRule,
|
||||
uint8_t aCapStyle,
|
||||
Float aStrokeWidth) const;
|
||||
|
||||
const_iterator begin() const { return mData.Elements(); }
|
||||
const_iterator end() const { return mData.Elements() + mData.Length(); }
|
||||
|
|
|
@ -13,12 +13,18 @@
|
|||
#include "gfxPath.h"
|
||||
#include "mozilla/dom/SVGPathElementBinding.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsComputedDOMStyle.h"
|
||||
#include "nsGkAtoms.h"
|
||||
#include "nsStyleConsts.h"
|
||||
#include "nsStyleStruct.h"
|
||||
#include "SVGContentUtils.h"
|
||||
|
||||
class gfxContext;
|
||||
|
||||
NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Path)
|
||||
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
|
@ -350,5 +356,43 @@ SVGPathElement::GetPathLengthScale(PathLengthScaleForType aFor)
|
|||
return 1.0;
|
||||
}
|
||||
|
||||
TemporaryRef<Path>
|
||||
SVGPathElement::BuildPath()
|
||||
{
|
||||
// The Moz2D PathBuilder that our SVGPathData will be using only cares about
|
||||
// the fill rule. However, in order to fulfill the requirements of the SVG
|
||||
// spec regarding zero length sub-paths when square line caps are in use,
|
||||
// SVGPathData needs to know our stroke-linecap style and, if "square", then
|
||||
// also our stroke width. See the comment for
|
||||
// ApproximateZeroLengthSubpathSquareCaps for more info.
|
||||
|
||||
uint8_t strokeLineCap = NS_STYLE_STROKE_LINECAP_BUTT;
|
||||
Float strokeWidth = 0;
|
||||
|
||||
nsRefPtr<nsStyleContext> styleContext =
|
||||
nsComputedDOMStyle::GetStyleContextForElementNoFlush(this, nullptr, nullptr);
|
||||
if (styleContext) {
|
||||
const nsStyleSVG* style = styleContext->StyleSVG();
|
||||
// Note: the path that we return may be used for hit-testing, and SVG
|
||||
// exposes hit-testing of strokes that are not actually painted. For that
|
||||
// reason we do not check for eStyleSVGPaintType_None or check the stroke
|
||||
// opacity here.
|
||||
if (style->mStrokeLinecap == NS_STYLE_STROKE_LINECAP_SQUARE) {
|
||||
strokeLineCap = style->mStrokeLinecap;
|
||||
strokeWidth = GetStrokeWidth();
|
||||
}
|
||||
}
|
||||
|
||||
// The fill rule that we pass must be the current
|
||||
// computed value of our CSS 'fill-rule' property if the path that we return
|
||||
// will be used for painting or hit-testing. For all other uses (bounds
|
||||
// calculatons, length measurement, position-at-offset calculations) the fill
|
||||
// rule that we pass doesn't matter. As a result we can just pass the current
|
||||
// computed value regardless of who's calling us, or what they're going to do
|
||||
// with the path that we return.
|
||||
|
||||
return mD.GetAnimValue().BuildPath(GetFillRule(), strokeLineCap, strokeWidth);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -47,6 +47,7 @@ public:
|
|||
virtual bool IsMarkable() MOZ_OVERRIDE;
|
||||
virtual void GetMarkPoints(nsTArray<nsSVGMark> *aMarks) MOZ_OVERRIDE;
|
||||
virtual void ConstructPath(gfxContext *aCtx) MOZ_OVERRIDE;
|
||||
virtual TemporaryRef<Path> BuildPath() MOZ_OVERRIDE;
|
||||
|
||||
virtual already_AddRefed<gfxPath> GetPath(const gfxMatrix &aMatrix) MOZ_OVERRIDE;
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "nsDebug.h"
|
||||
#include "gfxPoint.h"
|
||||
#include "mozilla/gfx/Point.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
@ -18,6 +19,8 @@ namespace mozilla {
|
|||
*/
|
||||
class SVGPoint
|
||||
{
|
||||
typedef mozilla::gfx::Point Point;
|
||||
|
||||
public:
|
||||
|
||||
SVGPoint()
|
||||
|
@ -57,6 +60,10 @@ public:
|
|||
return gfxPoint(mX, mY);
|
||||
}
|
||||
|
||||
operator Point() const {
|
||||
return Point(mX, mY);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
bool IsValid() const {
|
||||
return NS_finite(mX) && NS_finite(mY);
|
||||
|
|
|
@ -7,10 +7,14 @@
|
|||
#include "nsGkAtoms.h"
|
||||
#include "gfxContext.h"
|
||||
#include "mozilla/dom/SVGRectElementBinding.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "mozilla/gfx/PathHelpers.h"
|
||||
#include <algorithm>
|
||||
|
||||
NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Rect)
|
||||
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
|
@ -148,5 +152,51 @@ SVGRectElement::ConstructPath(gfxContext *aCtx)
|
|||
gfxCornerSizes(corner, corner, corner, corner));
|
||||
}
|
||||
|
||||
TemporaryRef<Path>
|
||||
SVGRectElement::BuildPath()
|
||||
{
|
||||
RefPtr<PathBuilder> pathBuilder = CreatePathBuilder();
|
||||
|
||||
float x, y, width, height, rx, ry;
|
||||
GetAnimatedLengthValues(&x, &y, &width, &height, &rx, &ry, nullptr);
|
||||
|
||||
if (width > 0 && height > 0) {
|
||||
rx = std::max(rx, 0.0f);
|
||||
ry = std::max(ry, 0.0f);
|
||||
|
||||
if (rx == 0 && ry == 0) {
|
||||
// Optimization for the no rounded corners case.
|
||||
Rect r(x, y, width, height);
|
||||
pathBuilder->MoveTo(r.TopLeft());
|
||||
pathBuilder->LineTo(r.TopRight());
|
||||
pathBuilder->LineTo(r.BottomRight());
|
||||
pathBuilder->LineTo(r.BottomLeft());
|
||||
pathBuilder->Close();
|
||||
} else {
|
||||
// If either the 'rx' or the 'ry' attribute isn't set, then we have to
|
||||
// set it to the value of the other:
|
||||
bool hasRx = mLengthAttributes[ATTR_RX].IsExplicitlySet();
|
||||
bool hasRy = mLengthAttributes[ATTR_RY].IsExplicitlySet();
|
||||
MOZ_ASSERT(hasRx || hasRy);
|
||||
|
||||
if (hasRx && !hasRy) {
|
||||
ry = rx;
|
||||
} else if (hasRy && !hasRx) {
|
||||
rx = ry;
|
||||
}
|
||||
|
||||
// Clamp rx and ry to half the rect's width and height respectively:
|
||||
rx = std::min(rx, width / 2);
|
||||
ry = std::min(ry, height / 2);
|
||||
|
||||
Size cornerRadii(rx, ry);
|
||||
Size radii[] = { cornerRadii, cornerRadii, cornerRadii, cornerRadii };
|
||||
AppendRoundedRectToPath(pathBuilder, Rect(x, y, width, height), radii);
|
||||
}
|
||||
}
|
||||
|
||||
return pathBuilder->Finish();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -32,6 +32,7 @@ public:
|
|||
|
||||
// nsSVGPathGeometryElement methods:
|
||||
virtual void ConstructPath(gfxContext *aCtx) MOZ_OVERRIDE;
|
||||
virtual TemporaryRef<Path> BuildPath() MOZ_OVERRIDE;
|
||||
|
||||
virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE;
|
||||
|
||||
|
|
|
@ -4,7 +4,15 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsSVGPathGeometryElement.h"
|
||||
|
||||
#include "gfxPlatform.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "nsComputedDOMStyle.h"
|
||||
#include "nsSVGLength2.h"
|
||||
#include "SVGContentUtils.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// Implementation
|
||||
|
@ -57,3 +65,61 @@ nsSVGPathGeometryElement::GetPath(const gfxMatrix &aMatrix)
|
|||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TemporaryRef<PathBuilder>
|
||||
nsSVGPathGeometryElement::CreatePathBuilder()
|
||||
{
|
||||
RefPtr<DrawTarget> drawTarget =
|
||||
gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
|
||||
NS_ASSERTION(gfxPlatform::GetPlatform()->
|
||||
SupportsAzureContentForDrawTarget(drawTarget),
|
||||
"Should support Moz2D content drawing");
|
||||
|
||||
// The fill rule that we pass to CreatePathBuilder must be the current
|
||||
// computed value of our CSS 'fill-rule' property if the path that we return
|
||||
// will be used for painting or hit-testing. For all other uses (bounds
|
||||
// calculatons, length measurement, position-at-offset calculations) the fill
|
||||
// rule that we pass doesn't matter. As a result we can just pass the current
|
||||
// computed value regardless of who's calling us, or what they're going to do
|
||||
// with the path that we return.
|
||||
|
||||
return drawTarget->CreatePathBuilder(GetFillRule());
|
||||
}
|
||||
|
||||
FillRule
|
||||
nsSVGPathGeometryElement::GetFillRule()
|
||||
{
|
||||
FillRule fillRule = FILL_WINDING; // Equivalent to NS_STYLE_FILL_RULE_NONZERO
|
||||
|
||||
nsRefPtr<nsStyleContext> styleContext =
|
||||
nsComputedDOMStyle::GetStyleContextForElementNoFlush(this, nullptr,
|
||||
nullptr);
|
||||
|
||||
if (styleContext) {
|
||||
MOZ_ASSERT(styleContext->StyleSVG()->mFillRule ==
|
||||
NS_STYLE_FILL_RULE_NONZERO ||
|
||||
styleContext->StyleSVG()->mFillRule ==
|
||||
NS_STYLE_FILL_RULE_EVENODD);
|
||||
|
||||
if (styleContext->StyleSVG()->mFillRule == NS_STYLE_FILL_RULE_EVENODD) {
|
||||
fillRule = FILL_EVEN_ODD;
|
||||
}
|
||||
} else {
|
||||
// ReportToConsole
|
||||
NS_WARNING("Couldn't get style context for content in GetFillRule");
|
||||
}
|
||||
|
||||
return fillRule;
|
||||
}
|
||||
|
||||
Float
|
||||
nsSVGPathGeometryElement::GetStrokeWidth()
|
||||
{
|
||||
nsRefPtr<nsStyleContext> styleContext =
|
||||
nsComputedDOMStyle::GetStyleContextForElementNoFlush(this, nullptr,
|
||||
nullptr);
|
||||
return styleContext ?
|
||||
SVGContentUtils::CoordToFloat(styleContext->PresContext(), this,
|
||||
styleContext->StyleSVG()->mStrokeWidth) :
|
||||
0.0f;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#ifndef __NS_SVGPATHGEOMETRYELEMENT_H__
|
||||
#define __NS_SVGPATHGEOMETRYELEMENT_H__
|
||||
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "SVGGraphicsElement.h"
|
||||
|
||||
class gfxPath;
|
||||
|
@ -33,6 +34,12 @@ typedef mozilla::dom::SVGGraphicsElement nsSVGPathGeometryElementBase;
|
|||
|
||||
class nsSVGPathGeometryElement : public nsSVGPathGeometryElementBase
|
||||
{
|
||||
protected:
|
||||
typedef mozilla::gfx::FillRule FillRule;
|
||||
typedef mozilla::gfx::Float Float;
|
||||
typedef mozilla::gfx::Path Path;
|
||||
typedef mozilla::gfx::PathBuilder PathBuilder;
|
||||
|
||||
public:
|
||||
nsSVGPathGeometryElement(already_AddRefed<nsINodeInfo> aNodeInfo);
|
||||
|
||||
|
@ -52,7 +59,34 @@ public:
|
|||
virtual bool IsMarkable();
|
||||
virtual void GetMarkPoints(nsTArray<nsSVGMark> *aMarks);
|
||||
virtual void ConstructPath(gfxContext *aCtx) = 0;
|
||||
|
||||
/**
|
||||
* Returns a Path that can be used to paint, hit-test or calculate bounds for
|
||||
* this element.
|
||||
*/
|
||||
virtual mozilla::TemporaryRef<Path> BuildPath() = 0;
|
||||
|
||||
virtual already_AddRefed<gfxPath> GetPath(const gfxMatrix &aMatrix);
|
||||
|
||||
/**
|
||||
* Returns a PathBuilder object created using the current computed value of
|
||||
* the CSS property 'fill-rule' for this element.
|
||||
*/
|
||||
mozilla::TemporaryRef<PathBuilder> CreatePathBuilder();
|
||||
|
||||
/**
|
||||
* Returns the current computed value of the CSS property 'fill-rule' for
|
||||
* this element.
|
||||
*/
|
||||
FillRule GetFillRule();
|
||||
|
||||
/**
|
||||
* Returns the current computed value of the CSS property 'stroke-width' for
|
||||
* this element. (I.e. this does NOT take account of the value of the
|
||||
* 'stroke' and 'stroke-opacity' properties to, say, return zero if they are
|
||||
* "none" or "0", respectively.)
|
||||
*/
|
||||
Float GetStrokeWidth();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -6,9 +6,11 @@
|
|||
#include "nsSVGPolyElement.h"
|
||||
#include "DOMSVGPointList.h"
|
||||
#include "gfxContext.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "SVGContentUtils.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// nsISupports methods
|
||||
|
@ -120,3 +122,19 @@ nsSVGPolyElement::ConstructPath(gfxContext *aCtx)
|
|||
}
|
||||
}
|
||||
|
||||
TemporaryRef<Path>
|
||||
nsSVGPolyElement::BuildPath()
|
||||
{
|
||||
RefPtr<PathBuilder> pathBuilder = CreatePathBuilder();
|
||||
|
||||
const SVGPointList &points = mPoints.GetAnimValue();
|
||||
|
||||
if (!points.IsEmpty()) {
|
||||
pathBuilder->MoveTo(points[0]);
|
||||
for (uint32_t i = 1; i < points.Length(); ++i) {
|
||||
pathBuilder->LineTo(points[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return pathBuilder->Finish();
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ public:
|
|||
virtual bool IsMarkable() MOZ_OVERRIDE { return true; }
|
||||
virtual void GetMarkPoints(nsTArray<nsSVGMark> *aMarks) MOZ_OVERRIDE;
|
||||
virtual void ConstructPath(gfxContext *aCtx) MOZ_OVERRIDE;
|
||||
virtual mozilla::TemporaryRef<Path> BuildPath() MOZ_OVERRIDE;
|
||||
|
||||
// WebIDL
|
||||
already_AddRefed<mozilla::DOMSVGPointList> Points();
|
||||
|
|
|
@ -5058,9 +5058,9 @@ nsSVGTextFrame2::ShouldRenderAsPath(nsRenderingContext* aContext,
|
|||
// Text has a stroke.
|
||||
if (!(style->mStroke.mType == eStyleSVGPaintType_None ||
|
||||
style->mStrokeOpacity == 0 ||
|
||||
nsSVGUtils::CoordToFloat(PresContext(),
|
||||
static_cast<nsSVGElement*>(mContent),
|
||||
style->mStrokeWidth) == 0)) {
|
||||
SVGContentUtils::CoordToFloat(PresContext(),
|
||||
static_cast<nsSVGElement*>(mContent),
|
||||
style->mStrokeWidth) == 0)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -391,28 +391,6 @@ nsSVGUtils::ComputeAlphaMask(uint8_t *aData,
|
|||
}
|
||||
}
|
||||
|
||||
float
|
||||
nsSVGUtils::CoordToFloat(nsPresContext *aPresContext,
|
||||
nsSVGElement *aContent,
|
||||
const nsStyleCoord &aCoord)
|
||||
{
|
||||
switch (aCoord.GetUnit()) {
|
||||
case eStyleUnit_Factor:
|
||||
// user units
|
||||
return aCoord.GetFactorValue();
|
||||
|
||||
case eStyleUnit_Coord:
|
||||
return nsPresContext::AppUnitsToFloatCSSPixels(aCoord.GetCoordValue());
|
||||
|
||||
case eStyleUnit_Percent: {
|
||||
SVGSVGElement* ctx = aContent->GetCtx();
|
||||
return ctx ? aCoord.GetPercentValue() * ctx->GetLength(SVGContentUtils::XY) : 0.0f;
|
||||
}
|
||||
default:
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
nsSVGDisplayContainerFrame*
|
||||
nsSVGUtils::GetNearestSVGViewport(nsIFrame *aFrame)
|
||||
{
|
||||
|
@ -1621,8 +1599,8 @@ nsSVGUtils::GetStrokeWidth(nsIFrame* aFrame, gfxTextContextPaint *aContextPaint)
|
|||
|
||||
nsSVGElement *ctx = static_cast<nsSVGElement*>(content);
|
||||
|
||||
return CoordToFloat(aFrame->PresContext(), ctx,
|
||||
style->mStrokeWidth);
|
||||
return SVGContentUtils::CoordToFloat(aFrame->PresContext(), ctx,
|
||||
style->mStrokeWidth);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1713,9 +1691,9 @@ GetStrokeDashData(nsIFrame* aFrame,
|
|||
const nsStyleCoord *dasharray = style->mStrokeDasharray;
|
||||
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
aDashes[i] = nsSVGUtils::CoordToFloat(presContext,
|
||||
ctx,
|
||||
dasharray[i]) * pathScale;
|
||||
aDashes[i] = SVGContentUtils::CoordToFloat(presContext,
|
||||
ctx,
|
||||
dasharray[i]) * pathScale;
|
||||
if (aDashes[i] < 0.0) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1726,9 +1704,9 @@ GetStrokeDashData(nsIFrame* aFrame,
|
|||
if (aContextPaint && style->mStrokeDashoffsetFromObject) {
|
||||
*aDashOffset = aContextPaint->GetStrokeDashOffset();
|
||||
} else {
|
||||
*aDashOffset = nsSVGUtils::CoordToFloat(presContext,
|
||||
ctx,
|
||||
style->mStrokeDashoffset);
|
||||
*aDashOffset = SVGContentUtils::CoordToFloat(presContext,
|
||||
ctx,
|
||||
style->mStrokeDashoffset);
|
||||
}
|
||||
|
||||
return (totalLength > 0.0);
|
||||
|
|
|
@ -292,15 +292,6 @@ public:
|
|||
const nsIntRect &aRect,
|
||||
float aOpacity);
|
||||
|
||||
/*
|
||||
* Converts a nsStyleCoord into a userspace value. Handles units
|
||||
* Factor (straight userspace), Coord (dimensioned), and Percent (of
|
||||
* the current SVG viewport)
|
||||
*/
|
||||
static float CoordToFloat(nsPresContext *aPresContext,
|
||||
nsSVGElement *aContent,
|
||||
const nsStyleCoord &aCoord);
|
||||
|
||||
/**
|
||||
* Gets the nearest nsSVGInnerSVGFrame or nsSVGOuterSVGFrame frame. aFrame
|
||||
* must be an SVG frame. If aFrame is of type nsGkAtoms::svgOuterSVGFrame,
|
||||
|
|
Загрузка…
Ссылка в новой задаче