gecko-dev/dom/svg/SVGContentUtils.h

401 lines
14 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MOZILLA_SVGCONTENTUTILS_H
#define MOZILLA_SVGCONTENTUTILS_H
// include math.h to pick up definition of M_ maths defines e.g. M_PI
#include <math.h>
#include "mozilla/gfx/2D.h" // for StrokeOptions
#include "mozilla/gfx/Matrix.h"
#include "mozilla/RangedPtr.h"
#include "nsError.h"
#include "nsStringFwd.h"
#include "gfx2DGlue.h"
class gfxTextContextPaint;
class nsIContent;
class nsIDocument;
class nsIFrame;
class nsStyleContext;
class nsStyleCoord;
class nsSVGElement;
namespace mozilla {
class nsSVGAnimatedTransformList;
class SVGAnimatedPreserveAspectRatio;
class SVGPreserveAspectRatio;
namespace dom {
class Element;
class SVGSVGElement;
} // namespace dom
namespace gfx {
class Matrix;
} // namespace gfx
} // namespace mozilla
#define SVG_ZERO_LENGTH_PATH_FIX_FACTOR 512
/**
* SVGTransformTypes controls the transforms that PrependLocalTransformsTo
* applies.
*
* If aWhich is eAllTransforms, then all the transforms from the coordinate
* space established by this element for its children to the coordinate
* space established by this element's parent element for this element, are
* included.
*
* If aWhich is eUserSpaceToParent, then only the transforms from this
* element's userspace to the coordinate space established by its parent is
* included. This includes any transforms introduced by the 'transform'
* attribute, transform animations and animateMotion, but not any offsets
* due to e.g. 'x'/'y' attributes, or any transform due to a 'viewBox'
* attribute. (SVG userspace is defined to be the coordinate space in which
* coordinates on an element apply.)
*
* If aWhich is eChildToUserSpace, then only the transforms from the
* coordinate space established by this element for its childre to this
* elements userspace are included. This includes any offsets due to e.g.
* 'x'/'y' attributes, and any transform due to a 'viewBox' attribute, but
* does not include any transforms due to the 'transform' attribute.
*/
enum SVGTransformTypes {
eAllTransforms,
eUserSpaceToParent,
eChildToUserSpace
};
inline bool
IsSVGWhitespace(char aChar)
{
return aChar == '\x20' || aChar == '\x9' ||
aChar == '\xD' || aChar == '\xA';
}
inline bool
IsSVGWhitespace(char16_t aChar)
{
return aChar == char16_t('\x20') || aChar == char16_t('\x9') ||
aChar == char16_t('\xD') || aChar == char16_t('\xA');
}
/**
* Functions generally used by SVG Content classes. Functions here
* should not generally depend on layout methods/classes e.g. nsSVGUtils
*/
class SVGContentUtils
{
public:
typedef mozilla::gfx::Float Float;
typedef mozilla::gfx::Matrix Matrix;
typedef mozilla::gfx::Rect Rect;
typedef mozilla::gfx::StrokeOptions StrokeOptions;
typedef mozilla::SVGAnimatedPreserveAspectRatio SVGAnimatedPreserveAspectRatio;
typedef mozilla::SVGPreserveAspectRatio SVGPreserveAspectRatio;
/*
* Get the outer SVG element of an nsIContent
*/
static mozilla::dom::SVGSVGElement *GetOuterSVGElement(nsSVGElement *aSVGElement);
/**
* Activates the animation element aContent as a result of navigation to the
* fragment identifier that identifies aContent. aContent must be an instance
* of nsSVGAnimationElement.
*
* This is just a shim to allow nsSVGAnimationElement::ActivateByHyperlink to
* be called from layout/base without adding to that directory's include paths.
*/
static void ActivateByHyperlink(nsIContent *aContent);
/**
* Moz2D's StrokeOptions requires someone else to own its mDashPattern
* buffer, which is a pain when you want to initialize a StrokeOptions object
* in a helper function and pass it out. This sub-class owns the mDashPattern
* buffer so that consumers of such a helper function don't need to worry
* about creating it, passing it in, or deleting it. (An added benefit is
* that in the typical case when stroke-dasharray is short it will avoid
* allocating.)
*/
struct AutoStrokeOptions : public StrokeOptions {
AutoStrokeOptions()
{
MOZ_ASSERT(mDashLength == 0, "InitDashPattern() depends on this");
}
~AutoStrokeOptions() {
if (mDashPattern && mDashPattern != mSmallArray) {
delete [] mDashPattern;
}
}
/**
* Creates the buffer to store the stroke-dasharray, assuming out-of-memory
* does not occur. The buffer's address is assigned to mDashPattern and
* returned to the caller as a non-const pointer (so that the caller can
* initialize the values in the buffer, since mDashPattern is const).
*/
Float* InitDashPattern(size_t aDashCount) {
if (aDashCount <= MOZ_ARRAY_LENGTH(mSmallArray)) {
mDashPattern = mSmallArray;
return mSmallArray;
}
Float* nonConstArray = new (mozilla::fallible) Float[aDashCount];
mDashPattern = nonConstArray;
return nonConstArray;
}
void DiscardDashPattern() {
if (mDashPattern && mDashPattern != mSmallArray) {
delete [] mDashPattern;
}
mDashLength = 0;
mDashPattern = nullptr;
}
private:
// Most dasharrays will fit in this and save us allocating
Float mSmallArray[16];
};
enum StrokeOptionFlags {
eAllStrokeOptions,
eIgnoreStrokeDashing
};
/**
* Note: the linecap style returned in aStrokeOptions is not valid when
* ShapeTypeHasNoCorners(aElement) == true && aFlags == eIgnoreStrokeDashing,
* since when aElement has no corners the rendered linecap style depends on
* whether or not the stroke is dashed.
*/
static void GetStrokeOptions(AutoStrokeOptions* aStrokeOptions,
nsSVGElement* aElement,
nsStyleContext* aStyleContext,
gfxTextContextPaint *aContextPaint,
StrokeOptionFlags aFlags = eAllStrokeOptions);
/**
* Returns the current computed value of the CSS property 'stroke-width' for
* the given element. aStyleContext may be provided as an optimization.
* aContextPaint is also optional.
*
* Note that this function 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.
*/
static Float GetStrokeWidth(nsSVGElement* aElement,
nsStyleContext* aStyleContext,
gfxTextContextPaint *aContextPaint);
/*
* Get the number of CSS px (user units) per em (i.e. the em-height in user
* units) for an nsIContent
*
* XXX document the conditions under which these may fail, and what they
* return in those cases.
*/
static float GetFontSize(mozilla::dom::Element *aElement);
static float GetFontSize(nsIFrame *aFrame);
static float GetFontSize(nsStyleContext *aStyleContext);
/*
* Get the number of CSS px (user units) per ex (i.e. the x-height in user
* units) for an nsIContent
*
* XXX document the conditions under which these may fail, and what they
* return in those cases.
*/
static float GetFontXHeight(mozilla::dom::Element *aElement);
static float GetFontXHeight(nsIFrame *aFrame);
static float GetFontXHeight(nsStyleContext *aStyleContext);
/*
* Report a localized error message to the error console.
*/
static nsresult ReportToConsole(nsIDocument* doc,
const char* aWarning,
const char16_t **aParams,
uint32_t aParamsLength);
static Matrix GetCTM(nsSVGElement *aElement, bool aScreenCTM);
/**
* Gets the tight bounds-space stroke bounds of the non-scaling-stroked rect
* aRect.
* @param aToBoundsSpace transforms from source space to the space aBounds
* should be computed in. Must be rectilinear.
* @param aToNonScalingStrokeSpace transforms from source
* space to the space in which non-scaling stroke should be applied.
* Must be rectilinear.
*/
static void
RectilinearGetStrokeBounds(const Rect& aRect,
const Matrix& aToBoundsSpace,
const Matrix& aToNonScalingStrokeSpace,
float aStrokeWidth,
Rect* aBounds);
/**
* Check if this is one of the SVG elements that SVG 1.1 Full says
* establishes a viewport: svg, symbol, image or foreignObject.
*/
static bool EstablishesViewport(nsIContent *aContent);
static nsSVGElement*
GetNearestViewportElement(nsIContent *aContent);
/* enum for specifying coordinate direction for ObjectSpace/UserSpace */
enum ctxDirection { X, Y, XY };
/**
* Computes sqrt((aWidth^2 + aHeight^2)/2);
*/
static double ComputeNormalizedHypotenuse(double aWidth, double aHeight);
/* Returns the angle halfway between the two specified angles */
static float
AngleBisect(float a1, float a2);
/* Generate a viewbox to viewport tranformation matrix */
static Matrix
GetViewBoxTransform(float aViewportWidth, float aViewportHeight,
float aViewboxX, float aViewboxY,
float aViewboxWidth, float aViewboxHeight,
const SVGAnimatedPreserveAspectRatio &aPreserveAspectRatio);
static Matrix
GetViewBoxTransform(float aViewportWidth, float aViewportHeight,
float aViewboxX, float aViewboxY,
float aViewboxWidth, float aViewboxHeight,
const SVGPreserveAspectRatio &aPreserveAspectRatio);
static mozilla::RangedPtr<const char16_t>
GetStartRangedPtr(const nsAString& aString);
static mozilla::RangedPtr<const char16_t>
GetEndRangedPtr(const nsAString& aString);
/**
* True if 'aCh' is a decimal digit.
*/
static inline bool IsDigit(char16_t aCh)
{
return aCh >= '0' && aCh <= '9';
}
/**
* Assuming that 'aCh' is a decimal digit, return its numeric value.
*/
static inline uint32_t DecimalDigitValue(char16_t aCh)
{
MOZ_ASSERT(IsDigit(aCh), "Digit expected");
return aCh - '0';
}
/**
* Parses the sign (+ or -) of a number and moves aIter to the next
* character if a sign is found.
* @param aSignMultiplier [outparam] -1 if the sign is negative otherwise 1
* @return false if we hit the end of the string (i.e. if aIter is initially
* at aEnd, or if we reach aEnd right after the sign character).
*/
static inline bool
ParseOptionalSign(mozilla::RangedPtr<const char16_t>& aIter,
const mozilla::RangedPtr<const char16_t>& aEnd,
int32_t& aSignMultiplier)
{
if (aIter == aEnd) {
return false;
}
aSignMultiplier = *aIter == '-' ? -1 : 1;
mozilla::RangedPtr<const char16_t> iter(aIter);
if (*iter == '-' || *iter == '+') {
++iter;
if (iter == aEnd) {
return false;
}
}
aIter = iter;
return true;
}
/**
* Parse a number of the form:
* number ::= integer ([Ee] integer)? | [+-]? [0-9]* "." [0-9]+ ([Ee] integer)?
* Parsing fails if the number cannot be represented by a floatType.
* If parsing succeeds, aIter is updated so that it points to the character
* after the end of the number, otherwise it is left unchanged
*/
template<class floatType>
static bool
ParseNumber(mozilla::RangedPtr<const char16_t>& aIter,
const mozilla::RangedPtr<const char16_t>& aEnd,
floatType& aValue);
/**
* Parse a number of the form:
* number ::= integer ([Ee] integer)? | [+-]? [0-9]* "." [0-9]+ ([Ee] integer)?
* Parsing fails if there is anything left over after the number,
* or the number cannot be represented by a floatType.
*/
template<class floatType>
static bool
ParseNumber(const nsAString& aString, floatType& aValue);
/**
* Parse an integer of the form:
* integer ::= [+-]? [0-9]+
* The returned number is clamped to an int32_t if outside that range.
* If parsing succeeds, aIter is updated so that it points to the character
* after the end of the number, otherwise it is left unchanged
*/
static bool ParseInteger(mozilla::RangedPtr<const char16_t>& aIter,
const mozilla::RangedPtr<const char16_t>& aEnd,
int32_t& aValue);
/**
* Parse an integer of the form:
* integer ::= [+-]? [0-9]+
* The returned number is clamped to an int32_t if outside that range.
* Parsing fails if there is anything left over after the number.
*/
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(nsSVGElement *aContent,
const nsStyleCoord &aCoord);
/**
* Parse the SVG path string
* Returns a path
* string formatted as an SVG path
*/
static already_AddRefed<mozilla::gfx::Path>
GetPath(const nsAString& aPathString);
/**
* Returns true if aContent is one of the elements whose stroke is guaranteed
* to have no corners: circle or ellipse
*/
static bool ShapeTypeHasNoCorners(const nsIContent* aContent);
/**
* Prepends an element's local transforms to the transform matrix.
* This is a helper for nsSVGElement::PrependLocalTransformsTo.
* Any callers probably really want to call that method instead of this one.
*/
static gfxMatrix PrependLocalTransformsTo(
const gfxMatrix &aMatrix,
SVGTransformTypes aWhich,
const Matrix* aAnimateMotionTransform,
const mozilla::nsSVGAnimatedTransformList* aTransforms);
};
#endif