tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

SVGUtils.h (24358B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef LAYOUT_SVG_SVGUTILS_H_
      8 #define LAYOUT_SVG_SVGUTILS_H_
      9 
     10 // include math.h to pick up definition of M_ maths defines e.g. M_PI
     11 #include <math.h>
     12 
     13 #include <algorithm>
     14 
     15 #include "DrawMode.h"
     16 #include "ImgDrawResult.h"
     17 #include "gfx2DGlue.h"
     18 #include "gfxMatrix.h"
     19 #include "gfxPoint.h"
     20 #include "gfxRect.h"
     21 #include "mozilla/ISVGDisplayableFrame.h"
     22 #include "mozilla/gfx/Rect.h"
     23 #include "nsCOMPtr.h"
     24 #include "nsChangeHint.h"
     25 #include "nsColor.h"
     26 #include "nsID.h"
     27 #include "nsIFrame.h"
     28 #include "nsISupports.h"
     29 #include "nsMathUtils.h"
     30 #include "nsStyleStruct.h"
     31 
     32 class gfxContext;
     33 class nsFrameList;
     34 class nsIContent;
     35 
     36 class nsPresContext;
     37 class nsTextFrame;
     38 
     39 struct nsStyleSVG;
     40 struct nsRect;
     41 
     42 namespace mozilla {
     43 class SVGAnimatedEnumeration;
     44 class SVGAnimatedLength;
     45 class SVGContextPaint;
     46 struct SVGContextPaintImpl;
     47 class SVGDisplayContainerFrame;
     48 class SVGGeometryFrame;
     49 class SVGOuterSVGFrame;
     50 namespace dom {
     51 class Element;
     52 class SVGElement;
     53 class UserSpaceMetrics;
     54 }  // namespace dom
     55 namespace gfx {
     56 class DrawTarget;
     57 class GeneralPattern;
     58 }  // namespace gfx
     59 }  // namespace mozilla
     60 
     61 #define SVG_HIT_TEST_FILL 0x01
     62 #define SVG_HIT_TEST_STROKE 0x02
     63 
     64 bool NS_SVGNewGetBBoxEnabled();
     65 
     66 namespace mozilla {
     67 
     68 /**
     69 * Sometimes we need to distinguish between an empty box and a box
     70 * that contains an element that has no size e.g. a point at the origin.
     71 */
     72 class SVGBBox final {
     73  using Rect = gfx::Rect;
     74 
     75 public:
     76  SVGBBox() : mIsEmpty(true) {}
     77 
     78  MOZ_IMPLICIT SVGBBox(const Rect& aRect) : mBBox(aRect), mIsEmpty(false) {}
     79 
     80  MOZ_IMPLICIT SVGBBox(const gfxRect& aRect)
     81      : mBBox(ToRect(aRect)), mIsEmpty(false) {}
     82 
     83  operator const Rect&() { return mBBox; }
     84 
     85  gfxRect ToThebesRect() const { return ThebesRect(mBBox); }
     86 
     87  bool IsEmpty() const { return mIsEmpty; }
     88 
     89  bool IsFinite() const { return mBBox.IsFinite(); }
     90 
     91  void Scale(float aScale) { mBBox.Scale(aScale); }
     92 
     93  void UnionEdges(const SVGBBox& aSVGBBox) {
     94    if (aSVGBBox.mIsEmpty) {
     95      return;
     96    }
     97    mBBox = mIsEmpty ? aSVGBBox.mBBox : mBBox.UnionEdges(aSVGBBox.mBBox);
     98    mIsEmpty = false;
     99  }
    100 
    101  void Intersect(const SVGBBox& aSVGBBox) {
    102    if (!mIsEmpty && !aSVGBBox.mIsEmpty) {
    103      mBBox = mBBox.Intersect(aSVGBBox.mBBox);
    104      if (mBBox.IsEmpty()) {
    105        mIsEmpty = true;
    106        mBBox = Rect(0, 0, 0, 0);
    107      }
    108    } else {
    109      mIsEmpty = true;
    110      mBBox = Rect(0, 0, 0, 0);
    111    }
    112  }
    113 
    114 private:
    115  Rect mBBox;
    116  bool mIsEmpty;
    117 };
    118 
    119 // GRRR WINDOWS HATE HATE HATE
    120 #undef CLIP_MASK
    121 
    122 class MOZ_RAII SVGAutoRenderState final {
    123  using DrawTarget = gfx::DrawTarget;
    124 
    125 public:
    126  explicit SVGAutoRenderState(DrawTarget* aDrawTarget);
    127  ~SVGAutoRenderState();
    128 
    129  void SetPaintingToWindow(bool aPaintingToWindow);
    130 
    131  static bool IsPaintingToWindow(DrawTarget* aDrawTarget);
    132 
    133 private:
    134  DrawTarget* mDrawTarget;
    135  void* mOriginalRenderState;
    136  bool mPaintingToWindow;
    137 };
    138 
    139 /**
    140 * General functions used by all of SVG layout and possibly content code.
    141 * If a method is used by content and depends only on other content methods
    142 * it should go in SVGContentUtils instead.
    143 */
    144 class SVGUtils final {
    145 public:
    146  using Element = dom::Element;
    147  using SVGElement = dom::SVGElement;
    148  using AntialiasMode = gfx::AntialiasMode;
    149  using DrawTarget = gfx::DrawTarget;
    150  using FillRule = gfx::FillRule;
    151  using GeneralPattern = gfx::GeneralPattern;
    152  using Size = gfx::Size;
    153  using imgDrawingParams = image::imgDrawingParams;
    154 
    155  NS_DECLARE_FRAME_PROPERTY_DELETABLE(ObjectBoundingBoxProperty, gfxRect)
    156 
    157  /**
    158   * Returns the frame's post-filter ink overflow rect when passed the
    159   * frame's pre-filter ink overflow rect. If the frame is not currently
    160   * being filtered, this function simply returns aUnfilteredRect.
    161   */
    162  static nsRect GetPostFilterInkOverflowRect(nsIFrame* aFrame,
    163                                             const nsRect& aPreFilterRect);
    164 
    165  /**
    166   * Schedules an update of the frame's bounds (which will in turn invalidate
    167   * the new area that the frame should paint to).
    168   *
    169   * This does nothing when passed an NS_FRAME_IS_NONDISPLAY frame.
    170   * In future we may want to allow ReflowSVG to be called on such frames,
    171   * but that would be better implemented as a ForceReflowSVG function to
    172   * be called synchronously while painting them without marking or paying
    173   * attention to dirty bits like this function.
    174   *
    175   * This is very similar to PresShell::FrameNeedsReflow. The main reason that
    176   * we have this function instead of using FrameNeedsReflow is because we need
    177   * to be able to call it under SVGOuterSVGFrame::NotifyViewportChange when
    178   * that function is called by SVGOuterSVGFrame::Reflow. FrameNeedsReflow
    179   * is not suitable for calling during reflow though, and it asserts as much.
    180   * The reason that we want to be callable under NotifyViewportChange is
    181   * because we want to synchronously notify and dirty the SVGOuterSVGFrame's
    182   * children so that when SVGOuterSVGFrame::DidReflow is called its children
    183   * will be updated for the new size as appropriate. Otherwise we'd have to
    184   * post an event to the event loop to mark dirty flags and request an update.
    185   *
    186   * Another reason that we don't currently want to call
    187   * PresShell::FrameNeedsReflow is because passing eRestyle to it to get it to
    188   * mark descendants dirty would cause it to descend through
    189   * SVGForeignObjectFrame frames to mark their children dirty, but we want to
    190   * handle SVGForeignObjectFrame specially. It would also do unnecessary work
    191   * descending into NS_FRAME_IS_NONDISPLAY frames.
    192   */
    193  static void ScheduleReflowSVG(nsIFrame* aFrame);
    194 
    195  /**
    196   * Returns true if the frame or any of its children need ReflowSVG
    197   * to be called on them.
    198   */
    199  static bool NeedsReflowSVG(const nsIFrame* aFrame);
    200 
    201  /**
    202   * Percentage lengths in SVG are resolved against the width/height of the
    203   * nearest viewport (or its viewBox, if set). This helper returns the size
    204   * of this "context" for the given frame so that percentage values can be
    205   * resolved.
    206   */
    207  static Size GetContextSize(const nsIFrame* aFrame);
    208 
    209  /* Computes the input length in terms of object space coordinates.
    210     Input: rect - bounding box
    211            length - length to be converted
    212  */
    213  static float ObjectSpace(const gfxRect& aRect,
    214                           const dom::UserSpaceMetrics& aMetrics,
    215                           const SVGAnimatedLength* aLength);
    216 
    217  /* Computes the input length in terms of user space coordinates.
    218     Input: content - object to be used for determining user space
    219     Input: length - length to be converted
    220  */
    221  static float UserSpace(nsIFrame* aNonSVGContext,
    222                         const SVGAnimatedLength* aLength);
    223  static float UserSpace(const dom::UserSpaceMetrics& aMetrics,
    224                         const SVGAnimatedLength* aLength);
    225 
    226  /* Find the outermost SVG frame of the passed frame */
    227  static SVGOuterSVGFrame* GetOuterSVGFrame(nsIFrame* aFrame);
    228 
    229  /**
    230   * Get the covered region for a frame. Return null if it's not an SVG frame.
    231   * @param aRect gets a rectangle in app units
    232   * @return the outer SVG frame which aRect is relative to
    233   */
    234  static nsIFrame* GetOuterSVGFrameAndCoveredRegion(nsIFrame* aFrame,
    235                                                    nsRect* aRect);
    236 
    237  /* Paint SVG frame with SVG effects
    238   */
    239  static void PaintFrameWithEffects(nsIFrame* aFrame, gfxContext& aContext,
    240                                    const gfxMatrix& aTransform,
    241                                    imgDrawingParams& aImgParams);
    242 
    243  /* Hit testing - check if point hits the clipPath of indicated
    244   * frame.  Returns true if no clipPath set. */
    245  static bool HitTestClip(nsIFrame* aFrame, const gfxPoint& aPoint);
    246 
    247  /*
    248   * Returns the CanvasTM of the indicated frame, whether it's a
    249   * child SVG frame, container SVG frame, or a regular frame.
    250   * For regular frames, we just return an identity matrix.
    251   */
    252  static gfxMatrix GetCanvasTM(nsIFrame* aFrame);
    253 
    254  /*
    255   * Returns whether the frame is transformed and what those transforms are.
    256   */
    257  static bool GetParentSVGTransforms(const nsIFrame* aFrame,
    258                                     gfx::Matrix* aFromParentTransform);
    259 
    260  /**
    261   * Notify the descendants of aFrame of a change to one of their ancestors
    262   * that might affect them.
    263   */
    264  static void NotifyChildrenOfSVGChange(
    265      nsIFrame* aFrame, ISVGDisplayableFrame::ChangeFlags aFlags);
    266 
    267  /*
    268   * Convert a surface size to an integer for use by thebes
    269   * possibly making it smaller in the process so the surface does not
    270   * use excessive memory.
    271   *
    272   * @param aSize the desired surface size
    273   * @param aResultOverflows true if the desired surface size is too big
    274   * @return the surface size to use
    275   */
    276  static gfx::IntSize ConvertToSurfaceSize(const gfxSize& aSize,
    277                                           bool* aResultOverflows);
    278 
    279  /*
    280   * Hit test a given rectangle/matrix.
    281   */
    282  static bool HitTestRect(const gfx::Matrix& aMatrix, float aRX, float aRY,
    283                          float aRWidth, float aRHeight, float aX, float aY);
    284 
    285  /**
    286   * Get the clip rect for the given frame, taking into account the CSS 'clip'
    287   * property. See:
    288   * http://www.w3.org/TR/SVG11/masking.html#OverflowAndClipProperties
    289   * The arguments for aX, aY, aWidth and aHeight should be the dimensions of
    290   * the viewport established by aFrame.
    291   */
    292  static gfxRect GetClipRectForFrame(const nsIFrame* aFrame, float aX, float aY,
    293                                     float aWidth, float aHeight);
    294 
    295  /* Using group opacity instead of fill or stroke opacity on a
    296   * geometry object seems to be a common authoring mistake.  If we're
    297   * not applying filters and not both stroking and filling, we can
    298   * generate the same result without going through the overhead of a
    299   * push/pop group. */
    300  static bool CanOptimizeOpacity(const nsIFrame* aFrame);
    301 
    302  /**
    303   * Take the CTM to userspace for an element, and adjust it to a CTM to its
    304   * object bounding box space if aUnits is SVG_UNIT_TYPE_OBJECTBOUNDINGBOX.
    305   * (I.e. so that [0,0] is at the top left of its bbox, and [1,1] is at the
    306   * bottom right of its bbox).
    307   *
    308   * If the bbox is empty, this will return a singular matrix.
    309   *
    310   * @param aFlags One or more of the BBoxFlags values defined below.
    311   */
    312  static gfxMatrix AdjustMatrixForUnits(const gfxMatrix& aMatrix,
    313                                        const SVGAnimatedEnumeration* aUnits,
    314                                        nsIFrame* aFrame, uint32_t aFlags);
    315 
    316  enum BBoxFlags {
    317    eBBoxIncludeFill = 1 << 0,
    318    // Include the geometry of the fill even when the fill does not
    319    // actually render (e.g. when fill="none" or fill-opacity="0")
    320    eBBoxIncludeFillGeometry = 1 << 1,
    321    eBBoxIncludeStroke = 1 << 2,
    322    // Include the geometry of the stroke even when the stroke does not
    323    // actually render (e.g. when stroke="none" or stroke-opacity="0")
    324    eBBoxIncludeStrokeGeometry = 1 << 3,
    325    eBBoxIncludeMarkers = 1 << 4,
    326    eBBoxIncludeClipped = 1 << 5,
    327    // Normally a getBBox call on outer-<svg> should only return the
    328    // bounds of the elements children. This flag will cause the
    329    // element's bounds to be returned instead.
    330    eUseFrameBoundsForOuterSVG = 1 << 6,
    331    // https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect
    332    eForGetClientRects = 1 << 7,
    333    // If the given frame is an HTML element, only include the region of the
    334    // given frame, instead of all continuations of it, while computing bbox if
    335    // this flag is set.
    336    eIncludeOnlyCurrentFrameForNonSVGElement = 1 << 8,
    337    // This flag is only has an effect when the target is a <use> element.
    338    // getBBox returns the bounds of the elements children in user space if
    339    // this flag is set; Otherwise, getBBox returns the union bounds in
    340    // the coordinate system formed by the <use> element.
    341    eUseUserSpaceOfUseElement = 1 << 9,
    342    // For a frame with a clip-path, if this flag is set then the result
    343    // will not be clipped to the bbox of the content inside the clip-path.
    344    eDoNotClipToBBoxOfContentInsideClipPath = 1 << 10,
    345    // For some cases, e.g. when using transform-box: stroke-box, we may have
    346    // the cyclical dependency if any of the elements in the subtree has
    347    // non-scaling-stroke. In this case, we should break it and use
    348    // transform-box:fill-box instead.
    349    // https://github.com/w3c/csswg-drafts/issues/9640
    350    eAvoidCycleIfNonScalingStroke = 1 << 11,
    351  };
    352  /**
    353   * This function in primarily for implementing the SVG DOM function getBBox()
    354   * and the SVG attribute value 'objectBoundingBox'.  However, it has been
    355   * extended with various extra parameters in order to become more of a
    356   * general purpose getter of all sorts of bounds that we might need to obtain
    357   * for SVG elements, or even for other elements that have SVG effects applied
    358   * to them.
    359   *
    360   * @param aFrame The frame of the element for which the bounds are to be
    361   *   obtained.
    362   * @param aFlags One or more of the BBoxFlags values defined above.
    363   * @param aToBoundsSpace If not specified the returned rect is in aFrame's
    364   *   element's "user space". A matrix can optionally be pass to specify a
    365   *   transform from aFrame's user space to the bounds space of interest
    366   *   (typically this will be the ancestor SVGOuterSVGFrame, but it could be
    367   *   to any other coordinate space).
    368   */
    369  static gfxRect GetBBox(nsIFrame* aFrame,
    370                         // If the default arg changes, update the handling for
    371                         // ObjectBoundingBoxProperty() in the implementation.
    372                         uint32_t aFlags = eBBoxIncludeFillGeometry,
    373                         const gfxMatrix* aToBoundsSpace = nullptr);
    374 
    375  /*
    376   * "User space" is the space that the frame's BBox (as calculated by
    377   * SVGUtils::GetBBox) is in. "Frame space" is the space that has its origin
    378   * at the top left of the union of the frame's border-box rects over all
    379   * continuations.
    380   * This function returns the offset one needs to add to something in frame
    381   * space in order to get its coordinates in user space.
    382   */
    383  static gfxPoint FrameSpaceInCSSPxToUserSpaceOffset(const nsIFrame* aFrame);
    384 
    385  /**
    386   * Convert a userSpaceOnUse/objectBoundingBoxUnits rectangle that's specified
    387   * using four SVGAnimatedLength values into a user unit rectangle in user
    388   * space.
    389   *
    390   * @param aXYWH pointer to 4 consecutive SVGAnimatedLength objects containing
    391   * the x, y, width and height values in that order
    392   * @param aBBox the bounding box of the object the rect is relative to;
    393   * may be null if aUnits is not SVG_UNIT_TYPE_OBJECTBOUNDINGBOX
    394   * @param aFrame the object in which to interpret user-space units;
    395   * may be null if aUnits is SVG_UNIT_TYPE_OBJECTBOUNDINGBOX
    396   */
    397  static gfxRect GetRelativeRect(uint16_t aUnits,
    398                                 const SVGAnimatedLength* aXYWH,
    399                                 const gfxRect& aBBox, nsIFrame* aFrame);
    400 
    401  static gfxRect GetRelativeRect(uint16_t aUnits,
    402                                 const SVGAnimatedLength* aXYWH,
    403                                 const gfxRect& aBBox,
    404                                 const SVGElement* aElement,
    405                                 const dom::UserSpaceMetrics& aMetrics);
    406 
    407  static bool OuterSVGIsCallingReflowSVG(nsIFrame* aFrame);
    408  static bool AnyOuterSVGIsCallingReflowSVG(nsIFrame* aFrame);
    409 
    410  /**
    411   * See https://svgwg.org/svg2-draft/painting.html#NonScalingStroke
    412   *
    413   * If the computed value of the 'vector-effect' property on aFrame is
    414   * 'non-scaling-stroke', then this function will set aUserToOuterSVG to the
    415   * transform from aFrame's SVG user space to the initial coordinate system
    416   * established by the viewport of aFrame's outer-<svg>'s (the coordinate
    417   * system in which the stroke is fixed).  If aUserToOuterSVG is set to a
    418   * non-identity matrix this function returns true, else it returns false.
    419   */
    420  static bool GetNonScalingStrokeTransform(const nsIFrame* aFrame,
    421                                           gfxMatrix* aUserToOuterSVG);
    422 
    423  /**
    424   * We need to track whether content has non-scaling-stroke because we can't
    425   * asynchronously animate it with a scaling transform.
    426   */
    427  static void UpdateNonScalingStrokeStateBit(nsIFrame* aFrame);
    428 
    429  /**
    430   * Compute the maximum possible device space stroke extents of a path given
    431   * the path's device space path extents, its stroke style and its ctm.
    432   *
    433   * This is a workaround for the lack of suitable cairo API for getting the
    434   * tight device space stroke extents of a path. This basically gives us the
    435   * tightest extents that we can guarantee fully enclose the inked stroke
    436   * without doing the calculations for the actual tight extents. We exploit
    437   * the fact that cairo does have an API for getting the tight device space
    438   * fill/path extents.
    439   *
    440   * This should die once bug 478152 is fixed.
    441   */
    442  static gfxRect PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
    443                                               const nsTextFrame* aFrame,
    444                                               const gfxMatrix& aMatrix);
    445  static gfxRect PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
    446                                               const SVGGeometryFrame* aFrame,
    447                                               const gfxMatrix& aMatrix);
    448 
    449  /**
    450   * Convert a floating-point value to a 32-bit integer value, clamping to
    451   * the range of valid integers.
    452   */
    453  static int32_t ClampToInt(double aVal) {
    454    return NS_lround(std::clamp(aVal, double(INT32_MIN), double(INT32_MAX)));
    455  }
    456 
    457  /**
    458   * Convert a floating-point value to a 64-bit integer value, clamping to
    459   * the lowest and highest integers that can be safely compared to a double.
    460   */
    461  static int64_t ClampToInt64(double aVal) {
    462    return static_cast<int64_t>(
    463        std::clamp<double>(aVal, INT64_MIN, std::nexttoward(INT64_MAX, 0)));
    464  }
    465 
    466  static nscolor GetFallbackOrPaintColor(
    467      const ComputedStyle&, StyleSVGPaint nsStyleSVG::* aFillOrStroke,
    468      nscolor aDefaultContextFallbackColor);
    469 
    470  static void MakeFillPatternFor(nsIFrame* aFrame, gfxContext* aContext,
    471                                 GeneralPattern* aOutPattern,
    472                                 imgDrawingParams& aImgParams,
    473                                 SVGContextPaint* aContextPaint = nullptr);
    474 
    475  static void MakeStrokePatternFor(nsIFrame* aFrame, gfxContext* aContext,
    476                                   GeneralPattern* aOutPattern,
    477                                   imgDrawingParams& aImgParams,
    478                                   SVGContextPaint* aContextPaint = nullptr);
    479 
    480  static float GetOpacity(const StyleSVGOpacity&, const SVGContextPaint*);
    481 
    482  /*
    483   * @return false if there is no stroke
    484   */
    485  static bool HasStroke(const nsIFrame* aFrame,
    486                        const SVGContextPaint* aContextPaint = nullptr);
    487 
    488  static float GetStrokeWidth(const nsIFrame* aFrame,
    489                              const SVGContextPaint* aContextPaint = nullptr);
    490 
    491  /*
    492   * Set up a context for a stroked path (including any dashing that applies).
    493   */
    494  static void SetupStrokeGeometry(nsIFrame* aFrame, gfxContext* aContext,
    495                                  SVGContextPaint* aContextPaint = nullptr);
    496 
    497  /**
    498   * This function returns a set of bit flags indicating which parts of the
    499   * element (fill, stroke, bounds) should intercept pointer events. It takes
    500   * into account the type of element and the value of the 'pointer-events'
    501   * property on the element.
    502   */
    503  static uint16_t GetGeometryHitTestFlags(const nsIFrame* aFrame);
    504 
    505  static FillRule ToFillRule(StyleFillRule aFillRule) {
    506    return aFillRule == StyleFillRule::Evenodd ? FillRule::FILL_EVEN_ODD
    507                                               : FillRule::FILL_WINDING;
    508  }
    509 
    510  static AntialiasMode ToAntialiasMode(StyleTextRendering aTextRendering) {
    511    return aTextRendering == StyleTextRendering::Optimizespeed
    512               ? AntialiasMode::NONE
    513               : AntialiasMode::SUBPIXEL;
    514  }
    515 
    516  static AntialiasMode ToAntialiasMode(StyleShapeRendering aShapeRendering) {
    517    return (aShapeRendering == StyleShapeRendering::Optimizespeed ||
    518            aShapeRendering == StyleShapeRendering::Crispedges)
    519               ? AntialiasMode::NONE
    520               : AntialiasMode::SUBPIXEL;
    521  }
    522 
    523  /**
    524   * Render a SVG glyph.
    525   * @param aElement the SVG glyph element to render
    526   * @param aContext the thebes aContext to draw to
    527   * @return true if rendering succeeded
    528   */
    529  static void PaintSVGGlyph(Element* aElement, gfxContext* aContext);
    530 
    531  /**
    532   * Get the extents of a SVG glyph.
    533   * @param aElement the SVG glyph element
    534   * @param aSVGToAppSpace the matrix mapping the SVG glyph space to the
    535   *   target context space
    536   * @param aResult the result (valid when true is returned)
    537   * @return true if calculating the extents succeeded
    538   */
    539  static bool GetSVGGlyphExtents(const Element* aElement,
    540                                 const gfxMatrix& aSVGToAppSpace,
    541                                 gfxRect* aResult);
    542 
    543  /**
    544   * Returns the app unit canvas bounds of a userspace rect.
    545   *
    546   * @param aToCanvas Transform from userspace to canvas device space.
    547   */
    548  static nsRect ToCanvasBounds(const gfxRect& aUserspaceRect,
    549                               const gfxMatrix& aToCanvas,
    550                               const nsPresContext* presContext);
    551 
    552  struct MaskUsage;
    553  static MaskUsage DetermineMaskUsage(const nsIFrame* aFrame,
    554                                      bool aHandleOpacity);
    555 
    556  struct MOZ_STACK_CLASS MaskUsage {
    557    friend MaskUsage SVGUtils::DetermineMaskUsage(const nsIFrame* aFrame,
    558                                                  bool aHandleOpacity);
    559 
    560    bool ShouldGenerateMaskLayer() const { return mShouldGenerateMaskLayer; }
    561 
    562    bool ShouldGenerateClipMaskLayer() const {
    563      return mShouldGenerateClipMaskLayer;
    564    }
    565 
    566    bool ShouldGenerateLayer() const {
    567      return mShouldGenerateMaskLayer || mShouldGenerateClipMaskLayer;
    568    }
    569 
    570    bool ShouldGenerateMask() const {
    571      return mShouldGenerateMaskLayer || mShouldGenerateClipMaskLayer ||
    572             !IsOpaque();
    573    }
    574 
    575    bool ShouldApplyClipPath() const { return mShouldApplyClipPath; }
    576 
    577    bool HasSVGClip() const {
    578      return mShouldGenerateClipMaskLayer || mShouldApplyClipPath;
    579    }
    580 
    581    bool ShouldApplyBasicShapeOrPath() const {
    582      return mShouldApplyBasicShapeOrPath;
    583    }
    584 
    585    bool IsSimpleClipShape() const { return mIsSimpleClipShape; }
    586 
    587    bool IsOpaque() const { return mOpacity == 1.0f; }
    588 
    589    bool IsTransparent() const { return mOpacity == 0.0f; }
    590 
    591    float Opacity() const { return mOpacity; }
    592 
    593    bool UsingMaskOrClipPath() const {
    594      return mShouldGenerateMaskLayer || mShouldGenerateClipMaskLayer ||
    595             mShouldApplyClipPath || mShouldApplyBasicShapeOrPath;
    596    }
    597 
    598    bool ShouldDoSomething() const {
    599      return mShouldGenerateMaskLayer || mShouldGenerateClipMaskLayer ||
    600             mShouldApplyClipPath || mShouldApplyBasicShapeOrPath ||
    601             mOpacity != 1.0f;
    602    }
    603 
    604   private:
    605    MaskUsage() = default;
    606 
    607    float mOpacity = 0.0f;
    608    bool mShouldGenerateMaskLayer = false;
    609    bool mShouldGenerateClipMaskLayer = false;
    610    bool mShouldApplyClipPath = false;
    611    bool mShouldApplyBasicShapeOrPath = false;
    612    bool mIsSimpleClipShape = false;
    613  };
    614 
    615  static float ComputeOpacity(const nsIFrame* aFrame, bool aHandleOpacity);
    616 
    617  /**
    618   * SVG frames expect to paint in SVG user units, which are equal to CSS px
    619   * units. This method provides a transform matrix to multiply onto a
    620   * gfxContext's current transform to convert the context's current units from
    621   * its usual dev pixels to SVG user units/CSS px to keep the SVG code happy.
    622   */
    623  static gfxMatrix GetCSSPxToDevPxMatrix(const nsIFrame* aNonSVGFrame);
    624  static gfxMatrix GetTransformMatrixInUserSpace(const nsIFrame* aFrame);
    625 };
    626 
    627 }  // namespace mozilla
    628 
    629 #endif  // LAYOUT_SVG_SVGUTILS_H_