mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 10:18:41 +02:00 
			
		
		
		
	Depends on D170370 Differential Revision: https://phabricator.services.mozilla.com/D170371
		
			
				
	
	
		
			706 lines
		
	
	
	
		
			25 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			706 lines
		
	
	
	
		
			25 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/. */
 | 
						||
 | 
						||
// Main header first:
 | 
						||
#include "SVGPatternFrame.h"
 | 
						||
 | 
						||
// Keep others in (case-insensitive) order:
 | 
						||
#include "AutoReferenceChainGuard.h"
 | 
						||
#include "gfx2DGlue.h"
 | 
						||
#include "gfxContext.h"
 | 
						||
#include "gfxMatrix.h"
 | 
						||
#include "gfxPattern.h"
 | 
						||
#include "gfxPlatform.h"
 | 
						||
#include "mozilla/ComputedStyle.h"
 | 
						||
#include "mozilla/ISVGDisplayableFrame.h"
 | 
						||
#include "mozilla/PresShell.h"
 | 
						||
#include "mozilla/SVGContentUtils.h"
 | 
						||
#include "mozilla/SVGGeometryFrame.h"
 | 
						||
#include "mozilla/SVGObserverUtils.h"
 | 
						||
#include "mozilla/SVGUtils.h"
 | 
						||
#include "mozilla/dom/SVGPatternElement.h"
 | 
						||
#include "mozilla/dom/SVGUnitTypesBinding.h"
 | 
						||
#include "mozilla/gfx/2D.h"
 | 
						||
#include "nsGkAtoms.h"
 | 
						||
#include "nsIFrameInlines.h"
 | 
						||
#include "SVGAnimatedTransformList.h"
 | 
						||
 | 
						||
using namespace mozilla::dom;
 | 
						||
using namespace mozilla::dom::SVGUnitTypes_Binding;
 | 
						||
using namespace mozilla::gfx;
 | 
						||
using namespace mozilla::image;
 | 
						||
 | 
						||
namespace mozilla {
 | 
						||
 | 
						||
//----------------------------------------------------------------------
 | 
						||
// Implementation
 | 
						||
 | 
						||
SVGPatternFrame::SVGPatternFrame(ComputedStyle* aStyle,
 | 
						||
                                 nsPresContext* aPresContext)
 | 
						||
    : SVGPaintServerFrame(aStyle, aPresContext, kClassID),
 | 
						||
      mSource(nullptr),
 | 
						||
      mLoopFlag(false),
 | 
						||
      mNoHRefURI(false) {}
 | 
						||
 | 
						||
NS_IMPL_FRAMEARENA_HELPERS(SVGPatternFrame)
 | 
						||
 | 
						||
NS_QUERYFRAME_HEAD(SVGPatternFrame)
 | 
						||
  NS_QUERYFRAME_ENTRY(SVGPatternFrame)
 | 
						||
NS_QUERYFRAME_TAIL_INHERITING(SVGPaintServerFrame)
 | 
						||
 | 
						||
//----------------------------------------------------------------------
 | 
						||
// nsIFrame methods:
 | 
						||
 | 
						||
nsresult SVGPatternFrame::AttributeChanged(int32_t aNameSpaceID,
 | 
						||
                                           nsAtom* aAttribute,
 | 
						||
                                           int32_t aModType) {
 | 
						||
  if (aNameSpaceID == kNameSpaceID_None &&
 | 
						||
      (aAttribute == nsGkAtoms::patternUnits ||
 | 
						||
       aAttribute == nsGkAtoms::patternContentUnits ||
 | 
						||
       aAttribute == nsGkAtoms::patternTransform ||
 | 
						||
       aAttribute == nsGkAtoms::x || aAttribute == nsGkAtoms::y ||
 | 
						||
       aAttribute == nsGkAtoms::width || aAttribute == nsGkAtoms::height ||
 | 
						||
       aAttribute == nsGkAtoms::preserveAspectRatio ||
 | 
						||
       aAttribute == nsGkAtoms::viewBox)) {
 | 
						||
    SVGObserverUtils::InvalidateDirectRenderingObservers(this);
 | 
						||
  }
 | 
						||
 | 
						||
  if ((aNameSpaceID == kNameSpaceID_XLink ||
 | 
						||
       aNameSpaceID == kNameSpaceID_None) &&
 | 
						||
      aAttribute == nsGkAtoms::href) {
 | 
						||
    // Blow away our reference, if any
 | 
						||
    SVGObserverUtils::RemoveTemplateObserver(this);
 | 
						||
    mNoHRefURI = false;
 | 
						||
    // And update whoever references us
 | 
						||
    SVGObserverUtils::InvalidateDirectRenderingObservers(this);
 | 
						||
  }
 | 
						||
 | 
						||
  return SVGPaintServerFrame::AttributeChanged(aNameSpaceID, aAttribute,
 | 
						||
                                               aModType);
 | 
						||
}
 | 
						||
 | 
						||
#ifdef DEBUG
 | 
						||
void SVGPatternFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
 | 
						||
                           nsIFrame* aPrevInFlow) {
 | 
						||
  NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::pattern),
 | 
						||
               "Content is not an SVG pattern");
 | 
						||
 | 
						||
  SVGPaintServerFrame::Init(aContent, aParent, aPrevInFlow);
 | 
						||
}
 | 
						||
#endif /* DEBUG */
 | 
						||
 | 
						||
//----------------------------------------------------------------------
 | 
						||
// SVGContainerFrame methods:
 | 
						||
 | 
						||
// If our GetCanvasTM is getting called, we
 | 
						||
// need to return *our current* transformation
 | 
						||
// matrix, which depends on our units parameters
 | 
						||
// and X, Y, Width, and Height
 | 
						||
gfxMatrix SVGPatternFrame::GetCanvasTM() {
 | 
						||
  if (mCTM) {
 | 
						||
    return *mCTM;
 | 
						||
  }
 | 
						||
 | 
						||
  // Do we know our rendering parent?
 | 
						||
  if (mSource) {
 | 
						||
    // Yes, use it!
 | 
						||
    return mSource->GetCanvasTM();
 | 
						||
  }
 | 
						||
 | 
						||
  // We get here when geometry in the <pattern> container is updated
 | 
						||
  return gfxMatrix();
 | 
						||
}
 | 
						||
 | 
						||
// -------------------------------------------------------------------------
 | 
						||
// Helper functions
 | 
						||
// -------------------------------------------------------------------------
 | 
						||
 | 
						||
/** Calculate the maximum expansion of a matrix */
 | 
						||
static float MaxExpansion(const Matrix& aMatrix) {
 | 
						||
  // maximum expansion derivation from
 | 
						||
  // http://lists.cairographics.org/archives/cairo/2004-October/001980.html
 | 
						||
  // and also implemented in cairo_matrix_transformed_circle_major_axis
 | 
						||
  double a = aMatrix._11;
 | 
						||
  double b = aMatrix._12;
 | 
						||
  double c = aMatrix._21;
 | 
						||
  double d = aMatrix._22;
 | 
						||
  double f = (a * a + b * b + c * c + d * d) / 2;
 | 
						||
  double g = (a * a + b * b - c * c - d * d) / 2;
 | 
						||
  double h = a * c + b * d;
 | 
						||
  return sqrt(f + sqrt(g * g + h * h));
 | 
						||
}
 | 
						||
 | 
						||
// The SVG specification says that the 'patternContentUnits' attribute "has no
 | 
						||
// effect if attribute ‘viewBox’ is specified". We still need to include a bbox
 | 
						||
// scale if the viewBox is specified and _patternUnits_ is set to or defaults to
 | 
						||
// objectBoundingBox though, since in that case the viewBox is relative to the
 | 
						||
// bbox
 | 
						||
static bool IncludeBBoxScale(const SVGAnimatedViewBox& aViewBox,
 | 
						||
                             uint32_t aPatternContentUnits,
 | 
						||
                             uint32_t aPatternUnits) {
 | 
						||
  return (!aViewBox.IsExplicitlySet() &&
 | 
						||
          aPatternContentUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) ||
 | 
						||
         (aViewBox.IsExplicitlySet() &&
 | 
						||
          aPatternUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
 | 
						||
}
 | 
						||
 | 
						||
// Given the matrix for the pattern element's own transform, this returns a
 | 
						||
// combined matrix including the transforms applicable to its target.
 | 
						||
static Matrix GetPatternMatrix(uint16_t aPatternUnits,
 | 
						||
                               const Matrix& patternTransform,
 | 
						||
                               const gfxRect& bbox, const gfxRect& callerBBox,
 | 
						||
                               const Matrix& callerCTM) {
 | 
						||
  // We really want the pattern matrix to handle translations
 | 
						||
  gfxFloat minx = bbox.X();
 | 
						||
  gfxFloat miny = bbox.Y();
 | 
						||
 | 
						||
  if (aPatternUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
 | 
						||
    minx += callerBBox.X();
 | 
						||
    miny += callerBBox.Y();
 | 
						||
  }
 | 
						||
 | 
						||
  float scale = 1.0f / MaxExpansion(callerCTM);
 | 
						||
  Matrix patternMatrix = patternTransform;
 | 
						||
  patternMatrix.PreScale(scale, scale);
 | 
						||
  patternMatrix.PreTranslate(minx, miny);
 | 
						||
 | 
						||
  return patternMatrix;
 | 
						||
}
 | 
						||
 | 
						||
static nsresult GetTargetGeometry(gfxRect* aBBox,
 | 
						||
                                  const SVGAnimatedViewBox& aViewBox,
 | 
						||
                                  uint16_t aPatternContentUnits,
 | 
						||
                                  uint16_t aPatternUnits, nsIFrame* aTarget,
 | 
						||
                                  const Matrix& aContextMatrix,
 | 
						||
                                  const gfxRect* aOverrideBounds) {
 | 
						||
  *aBBox =
 | 
						||
      aOverrideBounds
 | 
						||
          ? *aOverrideBounds
 | 
						||
          : SVGUtils::GetBBox(aTarget, SVGUtils::eUseFrameBoundsForOuterSVG |
 | 
						||
                                           SVGUtils::eBBoxIncludeFillGeometry);
 | 
						||
 | 
						||
  // Sanity check
 | 
						||
  if (IncludeBBoxScale(aViewBox, aPatternContentUnits, aPatternUnits) &&
 | 
						||
      (aBBox->Width() <= 0 || aBBox->Height() <= 0)) {
 | 
						||
    return NS_ERROR_FAILURE;
 | 
						||
  }
 | 
						||
 | 
						||
  // OK, now fix up the bounding box to reflect user coordinates
 | 
						||
  // We handle device unit scaling in pattern matrix
 | 
						||
  float scale = MaxExpansion(aContextMatrix);
 | 
						||
  if (scale <= 0) {
 | 
						||
    return NS_ERROR_FAILURE;
 | 
						||
  }
 | 
						||
  aBBox->Scale(scale);
 | 
						||
  return NS_OK;
 | 
						||
}
 | 
						||
 | 
						||
already_AddRefed<SourceSurface> SVGPatternFrame::PaintPattern(
 | 
						||
    const DrawTarget* aDrawTarget, Matrix* patternMatrix,
 | 
						||
    const Matrix& aContextMatrix, nsIFrame* aSource,
 | 
						||
    StyleSVGPaint nsStyleSVG::*aFillOrStroke, float aGraphicOpacity,
 | 
						||
    const gfxRect* aOverrideBounds, imgDrawingParams& aImgParams) {
 | 
						||
  /*
 | 
						||
   * General approach:
 | 
						||
   *    Set the content geometry stuff
 | 
						||
   *    Calculate our bbox (using x,y,width,height & patternUnits &
 | 
						||
   *                        patternTransform)
 | 
						||
   *    Create the surface
 | 
						||
   *    Calculate the content transformation matrix
 | 
						||
   *    Get our children (we may need to get them from another Pattern)
 | 
						||
   *    Call SVGPaint on all of our children
 | 
						||
   *    Return
 | 
						||
   */
 | 
						||
 | 
						||
  SVGPatternFrame* patternWithChildren = GetPatternWithChildren();
 | 
						||
  if (!patternWithChildren) {
 | 
						||
    // Either no kids or a bad reference
 | 
						||
    return nullptr;
 | 
						||
  }
 | 
						||
  nsIFrame* firstKid = patternWithChildren->mFrames.FirstChild();
 | 
						||
 | 
						||
  const SVGAnimatedViewBox& viewBox = GetViewBox();
 | 
						||
 | 
						||
  uint16_t patternContentUnits =
 | 
						||
      GetEnumValue(SVGPatternElement::PATTERNCONTENTUNITS);
 | 
						||
  uint16_t patternUnits = GetEnumValue(SVGPatternElement::PATTERNUNITS);
 | 
						||
 | 
						||
  /*
 | 
						||
   * Get the content geometry information.  This is a little tricky --
 | 
						||
   * our parent is probably a <defs>, but we are rendering in the context
 | 
						||
   * of some geometry source.  Our content geometry information needs to
 | 
						||
   * come from our rendering parent as opposed to our content parent.  We
 | 
						||
   * get that information from aSource, which is passed to us from the
 | 
						||
   * backend renderer.
 | 
						||
   *
 | 
						||
   * There are three "geometries" that we need:
 | 
						||
   *   1) The bounding box for the pattern.  We use this to get the
 | 
						||
   *      width and height for the surface, and as the return to
 | 
						||
   *      GetBBox.
 | 
						||
   *   2) The transformation matrix for the pattern.  This is not *quite*
 | 
						||
   *      the same as the canvas transformation matrix that we will
 | 
						||
   *      provide to our rendering children since we "fudge" it a little
 | 
						||
   *      to get the renderer to handle the translations correctly for us.
 | 
						||
   *   3) The CTM that we return to our children who make up the pattern.
 | 
						||
   */
 | 
						||
 | 
						||
  // Get all of the information we need from our "caller" -- i.e.
 | 
						||
  // the geometry that is being rendered with a pattern
 | 
						||
  gfxRect callerBBox;
 | 
						||
  if (NS_FAILED(GetTargetGeometry(&callerBBox, viewBox, patternContentUnits,
 | 
						||
                                  patternUnits, aSource, aContextMatrix,
 | 
						||
                                  aOverrideBounds))) {
 | 
						||
    return nullptr;
 | 
						||
  }
 | 
						||
 | 
						||
  // Construct the CTM that we will provide to our children when we
 | 
						||
  // render them into the tile.
 | 
						||
  gfxMatrix ctm = ConstructCTM(viewBox, patternContentUnits, patternUnits,
 | 
						||
                               callerBBox, aContextMatrix, aSource);
 | 
						||
  if (ctm.IsSingular()) {
 | 
						||
    return nullptr;
 | 
						||
  }
 | 
						||
 | 
						||
  if (patternWithChildren->mCTM) {
 | 
						||
    *patternWithChildren->mCTM = ctm;
 | 
						||
  } else {
 | 
						||
    patternWithChildren->mCTM = MakeUnique<gfxMatrix>(ctm);
 | 
						||
  }
 | 
						||
 | 
						||
  // Get the bounding box of the pattern.  This will be used to determine
 | 
						||
  // the size of the surface, and will also be used to define the bounding
 | 
						||
  // box for the pattern tile.
 | 
						||
  gfxRect bbox =
 | 
						||
      GetPatternRect(patternUnits, callerBBox, aContextMatrix, aSource);
 | 
						||
  if (bbox.Width() <= 0.0 || bbox.Height() <= 0.0) {
 | 
						||
    return nullptr;
 | 
						||
  }
 | 
						||
 | 
						||
  // Get the pattern transform
 | 
						||
  Matrix patternTransform = ToMatrix(GetPatternTransform());
 | 
						||
 | 
						||
  // revert the vector effect transform so that the pattern appears unchanged
 | 
						||
  if (aFillOrStroke == &nsStyleSVG::mStroke) {
 | 
						||
    gfxMatrix userToOuterSVG;
 | 
						||
    if (SVGUtils::GetNonScalingStrokeTransform(aSource, &userToOuterSVG)) {
 | 
						||
      patternTransform *= ToMatrix(userToOuterSVG);
 | 
						||
      if (patternTransform.IsSingular()) {
 | 
						||
        NS_WARNING("Singular matrix painting non-scaling-stroke");
 | 
						||
        return nullptr;
 | 
						||
      }
 | 
						||
    }
 | 
						||
  }
 | 
						||
 | 
						||
  // Get the transformation matrix that we will hand to the renderer's pattern
 | 
						||
  // routine.
 | 
						||
  *patternMatrix = GetPatternMatrix(patternUnits, patternTransform, bbox,
 | 
						||
                                    callerBBox, aContextMatrix);
 | 
						||
  if (patternMatrix->IsSingular()) {
 | 
						||
    return nullptr;
 | 
						||
  }
 | 
						||
 | 
						||
  // Now that we have all of the necessary geometries, we can
 | 
						||
  // create our surface.
 | 
						||
  gfxRect transformedBBox =
 | 
						||
      ThebesRect(patternTransform.TransformBounds(ToRect(bbox)));
 | 
						||
 | 
						||
  bool resultOverflows;
 | 
						||
  IntSize surfaceSize =
 | 
						||
      SVGUtils::ConvertToSurfaceSize(transformedBBox.Size(), &resultOverflows);
 | 
						||
 | 
						||
  // 0 disables rendering, < 0 is an error
 | 
						||
  if (surfaceSize.width <= 0 || surfaceSize.height <= 0) {
 | 
						||
    return nullptr;
 | 
						||
  }
 | 
						||
 | 
						||
  gfxFloat patternWidth = bbox.Width();
 | 
						||
  gfxFloat patternHeight = bbox.Height();
 | 
						||
 | 
						||
  if (resultOverflows || patternWidth != surfaceSize.width ||
 | 
						||
      patternHeight != surfaceSize.height) {
 | 
						||
    // scale drawing to pattern surface size
 | 
						||
    gfxMatrix tempTM = gfxMatrix(surfaceSize.width / patternWidth, 0.0, 0.0,
 | 
						||
                                 surfaceSize.height / patternHeight, 0.0, 0.0);
 | 
						||
    patternWithChildren->mCTM->PreMultiply(tempTM);
 | 
						||
 | 
						||
    // and rescale pattern to compensate
 | 
						||
    patternMatrix->PreScale(patternWidth / surfaceSize.width,
 | 
						||
                            patternHeight / surfaceSize.height);
 | 
						||
  }
 | 
						||
 | 
						||
  RefPtr<DrawTarget> dt = aDrawTarget->CreateSimilarDrawTargetWithBacking(
 | 
						||
      surfaceSize, SurfaceFormat::B8G8R8A8);
 | 
						||
  if (!dt || !dt->IsValid()) {
 | 
						||
    return nullptr;
 | 
						||
  }
 | 
						||
  dt->ClearRect(Rect(0, 0, surfaceSize.width, surfaceSize.height));
 | 
						||
 | 
						||
  gfxContext ctx(dt);
 | 
						||
 | 
						||
  if (aGraphicOpacity != 1.0f) {
 | 
						||
    ctx.Save();
 | 
						||
    ctx.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, aGraphicOpacity);
 | 
						||
  }
 | 
						||
 | 
						||
  // OK, now render -- note that we use "firstKid", which
 | 
						||
  // we got at the beginning because it takes care of the
 | 
						||
  // referenced pattern situation for us
 | 
						||
 | 
						||
  if (aSource->IsSVGGeometryFrame()) {
 | 
						||
    // Set the geometrical parent of the pattern we are rendering
 | 
						||
    patternWithChildren->mSource = static_cast<SVGGeometryFrame*>(aSource);
 | 
						||
  }
 | 
						||
 | 
						||
  // Delay checking NS_FRAME_DRAWING_AS_PAINTSERVER bit until here so we can
 | 
						||
  // give back a clear surface if there's a loop
 | 
						||
  if (!patternWithChildren->HasAnyStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER)) {
 | 
						||
    AutoSetRestorePaintServerState paintServer(patternWithChildren);
 | 
						||
    for (nsIFrame* kid = firstKid; kid; kid = kid->GetNextSibling()) {
 | 
						||
      gfxMatrix tm = *(patternWithChildren->mCTM);
 | 
						||
 | 
						||
      // The CTM of each frame referencing us can be different
 | 
						||
      ISVGDisplayableFrame* SVGFrame = do_QueryFrame(kid);
 | 
						||
      if (SVGFrame) {
 | 
						||
        SVGFrame->NotifySVGChanged(ISVGDisplayableFrame::TRANSFORM_CHANGED);
 | 
						||
        tm = SVGUtils::GetTransformMatrixInUserSpace(kid) * tm;
 | 
						||
      }
 | 
						||
 | 
						||
      SVGUtils::PaintFrameWithEffects(kid, ctx, tm, aImgParams);
 | 
						||
    }
 | 
						||
  }
 | 
						||
 | 
						||
  patternWithChildren->mSource = nullptr;
 | 
						||
 | 
						||
  if (aGraphicOpacity != 1.0f) {
 | 
						||
    ctx.PopGroupAndBlend();
 | 
						||
    ctx.Restore();
 | 
						||
  }
 | 
						||
 | 
						||
  // caller now owns the surface
 | 
						||
  return dt->GetBackingSurface();
 | 
						||
}
 | 
						||
 | 
						||
/* Will probably need something like this... */
 | 
						||
// How do we handle the insertion of a new frame?
 | 
						||
// We really don't want to rerender this every time,
 | 
						||
// do we?
 | 
						||
SVGPatternFrame* SVGPatternFrame::GetPatternWithChildren() {
 | 
						||
  // Do we have any children ourselves?
 | 
						||
  if (!mFrames.IsEmpty()) {
 | 
						||
    return this;
 | 
						||
  }
 | 
						||
 | 
						||
  // No, see if we chain to someone who does
 | 
						||
 | 
						||
  // Before we recurse, make sure we'll break reference loops and over long
 | 
						||
  // reference chains:
 | 
						||
  static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
 | 
						||
  AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
 | 
						||
                                        &sRefChainLengthCounter);
 | 
						||
  if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
 | 
						||
    // Break reference chain
 | 
						||
    return nullptr;
 | 
						||
  }
 | 
						||
 | 
						||
  SVGPatternFrame* next = GetReferencedPattern();
 | 
						||
  if (!next) {
 | 
						||
    return nullptr;
 | 
						||
  }
 | 
						||
 | 
						||
  return next->GetPatternWithChildren();
 | 
						||
}
 | 
						||
 | 
						||
uint16_t SVGPatternFrame::GetEnumValue(uint32_t aIndex, nsIContent* aDefault) {
 | 
						||
  SVGAnimatedEnumeration& thisEnum =
 | 
						||
      static_cast<SVGPatternElement*>(GetContent())->mEnumAttributes[aIndex];
 | 
						||
 | 
						||
  if (thisEnum.IsExplicitlySet()) {
 | 
						||
    return thisEnum.GetAnimValue();
 | 
						||
  }
 | 
						||
 | 
						||
  // Before we recurse, make sure we'll break reference loops and over long
 | 
						||
  // reference chains:
 | 
						||
  static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
 | 
						||
  AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
 | 
						||
                                        &sRefChainLengthCounter);
 | 
						||
  if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
 | 
						||
    // Break reference chain
 | 
						||
    return static_cast<SVGPatternElement*>(aDefault)
 | 
						||
        ->mEnumAttributes[aIndex]
 | 
						||
        .GetAnimValue();
 | 
						||
  }
 | 
						||
 | 
						||
  SVGPatternFrame* next = GetReferencedPattern();
 | 
						||
  return next ? next->GetEnumValue(aIndex, aDefault)
 | 
						||
              : static_cast<SVGPatternElement*>(aDefault)
 | 
						||
                    ->mEnumAttributes[aIndex]
 | 
						||
                    .GetAnimValue();
 | 
						||
}
 | 
						||
 | 
						||
SVGAnimatedTransformList* SVGPatternFrame::GetPatternTransformList(
 | 
						||
    nsIContent* aDefault) {
 | 
						||
  SVGAnimatedTransformList* thisTransformList =
 | 
						||
      static_cast<SVGPatternElement*>(GetContent())->GetAnimatedTransformList();
 | 
						||
 | 
						||
  if (thisTransformList && thisTransformList->IsExplicitlySet())
 | 
						||
    return thisTransformList;
 | 
						||
 | 
						||
  // Before we recurse, make sure we'll break reference loops and over long
 | 
						||
  // reference chains:
 | 
						||
  static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
 | 
						||
  AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
 | 
						||
                                        &sRefChainLengthCounter);
 | 
						||
  if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
 | 
						||
    // Break reference chain
 | 
						||
    return static_cast<SVGPatternElement*>(aDefault)->mPatternTransform.get();
 | 
						||
  }
 | 
						||
 | 
						||
  SVGPatternFrame* next = GetReferencedPattern();
 | 
						||
  return next ? next->GetPatternTransformList(aDefault)
 | 
						||
              : static_cast<SVGPatternElement*>(aDefault)
 | 
						||
                    ->mPatternTransform.get();
 | 
						||
}
 | 
						||
 | 
						||
gfxMatrix SVGPatternFrame::GetPatternTransform() {
 | 
						||
  SVGAnimatedTransformList* animTransformList =
 | 
						||
      GetPatternTransformList(GetContent());
 | 
						||
  if (!animTransformList) {
 | 
						||
    return gfxMatrix();
 | 
						||
  }
 | 
						||
 | 
						||
  return animTransformList->GetAnimValue().GetConsolidationMatrix();
 | 
						||
}
 | 
						||
 | 
						||
const SVGAnimatedViewBox& SVGPatternFrame::GetViewBox(nsIContent* aDefault) {
 | 
						||
  const SVGAnimatedViewBox& thisViewBox =
 | 
						||
      static_cast<SVGPatternElement*>(GetContent())->mViewBox;
 | 
						||
 | 
						||
  if (thisViewBox.IsExplicitlySet()) {
 | 
						||
    return thisViewBox;
 | 
						||
  }
 | 
						||
 | 
						||
  // Before we recurse, make sure we'll break reference loops and over long
 | 
						||
  // reference chains:
 | 
						||
  static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
 | 
						||
  AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
 | 
						||
                                        &sRefChainLengthCounter);
 | 
						||
  if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
 | 
						||
    // Break reference chain
 | 
						||
    return static_cast<SVGPatternElement*>(aDefault)->mViewBox;
 | 
						||
  }
 | 
						||
 | 
						||
  SVGPatternFrame* next = GetReferencedPattern();
 | 
						||
  return next ? next->GetViewBox(aDefault)
 | 
						||
              : static_cast<SVGPatternElement*>(aDefault)->mViewBox;
 | 
						||
}
 | 
						||
 | 
						||
const SVGAnimatedPreserveAspectRatio& SVGPatternFrame::GetPreserveAspectRatio(
 | 
						||
    nsIContent* aDefault) {
 | 
						||
  const SVGAnimatedPreserveAspectRatio& thisPar =
 | 
						||
      static_cast<SVGPatternElement*>(GetContent())->mPreserveAspectRatio;
 | 
						||
 | 
						||
  if (thisPar.IsExplicitlySet()) {
 | 
						||
    return thisPar;
 | 
						||
  }
 | 
						||
 | 
						||
  // Before we recurse, make sure we'll break reference loops and over long
 | 
						||
  // reference chains:
 | 
						||
  static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
 | 
						||
  AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
 | 
						||
                                        &sRefChainLengthCounter);
 | 
						||
  if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
 | 
						||
    // Break reference chain
 | 
						||
    return static_cast<SVGPatternElement*>(aDefault)->mPreserveAspectRatio;
 | 
						||
  }
 | 
						||
 | 
						||
  SVGPatternFrame* next = GetReferencedPattern();
 | 
						||
  return next ? next->GetPreserveAspectRatio(aDefault)
 | 
						||
              : static_cast<SVGPatternElement*>(aDefault)->mPreserveAspectRatio;
 | 
						||
}
 | 
						||
 | 
						||
const SVGAnimatedLength* SVGPatternFrame::GetLengthValue(uint32_t aIndex,
 | 
						||
                                                         nsIContent* aDefault) {
 | 
						||
  const SVGAnimatedLength* thisLength =
 | 
						||
      &static_cast<SVGPatternElement*>(GetContent())->mLengthAttributes[aIndex];
 | 
						||
 | 
						||
  if (thisLength->IsExplicitlySet()) {
 | 
						||
    return thisLength;
 | 
						||
  }
 | 
						||
 | 
						||
  // Before we recurse, make sure we'll break reference loops and over long
 | 
						||
  // reference chains:
 | 
						||
  static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
 | 
						||
  AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
 | 
						||
                                        &sRefChainLengthCounter);
 | 
						||
  if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
 | 
						||
    // Break reference chain
 | 
						||
    return &static_cast<SVGPatternElement*>(aDefault)
 | 
						||
                ->mLengthAttributes[aIndex];
 | 
						||
  }
 | 
						||
 | 
						||
  SVGPatternFrame* next = GetReferencedPattern();
 | 
						||
  return next ? next->GetLengthValue(aIndex, aDefault)
 | 
						||
              : &static_cast<SVGPatternElement*>(aDefault)
 | 
						||
                     ->mLengthAttributes[aIndex];
 | 
						||
}
 | 
						||
 | 
						||
// Private (helper) methods
 | 
						||
 | 
						||
SVGPatternFrame* SVGPatternFrame::GetReferencedPattern() {
 | 
						||
  if (mNoHRefURI) {
 | 
						||
    return nullptr;
 | 
						||
  }
 | 
						||
 | 
						||
  auto GetHref = [this](nsAString& aHref) {
 | 
						||
    SVGPatternElement* pattern =
 | 
						||
        static_cast<SVGPatternElement*>(this->GetContent());
 | 
						||
    if (pattern->mStringAttributes[SVGPatternElement::HREF].IsExplicitlySet()) {
 | 
						||
      pattern->mStringAttributes[SVGPatternElement::HREF].GetAnimValue(aHref,
 | 
						||
                                                                       pattern);
 | 
						||
    } else {
 | 
						||
      pattern->mStringAttributes[SVGPatternElement::XLINK_HREF].GetAnimValue(
 | 
						||
          aHref, pattern);
 | 
						||
    }
 | 
						||
    this->mNoHRefURI = aHref.IsEmpty();
 | 
						||
  };
 | 
						||
 | 
						||
  // We don't call SVGObserverUtils::RemoveTemplateObserver and set
 | 
						||
  // `mNoHRefURI = false` on failure since we want to be invalidated if the ID
 | 
						||
  // specified by our href starts resolving to a different/valid element.
 | 
						||
 | 
						||
  return do_QueryFrame(SVGObserverUtils::GetAndObserveTemplate(this, GetHref));
 | 
						||
}
 | 
						||
 | 
						||
gfxRect SVGPatternFrame::GetPatternRect(uint16_t aPatternUnits,
 | 
						||
                                        const gfxRect& aTargetBBox,
 | 
						||
                                        const Matrix& aTargetCTM,
 | 
						||
                                        nsIFrame* aTarget) {
 | 
						||
  // We need to initialize our box
 | 
						||
  float x, y, width, height;
 | 
						||
 | 
						||
  // Get the pattern x,y,width, and height
 | 
						||
  const SVGAnimatedLength *tmpX, *tmpY, *tmpHeight, *tmpWidth;
 | 
						||
  tmpX = GetLengthValue(SVGPatternElement::ATTR_X);
 | 
						||
  tmpY = GetLengthValue(SVGPatternElement::ATTR_Y);
 | 
						||
  tmpHeight = GetLengthValue(SVGPatternElement::ATTR_HEIGHT);
 | 
						||
  tmpWidth = GetLengthValue(SVGPatternElement::ATTR_WIDTH);
 | 
						||
 | 
						||
  if (aPatternUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
 | 
						||
    x = SVGUtils::ObjectSpace(aTargetBBox, tmpX);
 | 
						||
    y = SVGUtils::ObjectSpace(aTargetBBox, tmpY);
 | 
						||
    width = SVGUtils::ObjectSpace(aTargetBBox, tmpWidth);
 | 
						||
    height = SVGUtils::ObjectSpace(aTargetBBox, tmpHeight);
 | 
						||
  } else {
 | 
						||
    float scale = MaxExpansion(aTargetCTM);
 | 
						||
    x = SVGUtils::UserSpace(aTarget, tmpX) * scale;
 | 
						||
    y = SVGUtils::UserSpace(aTarget, tmpY) * scale;
 | 
						||
    width = SVGUtils::UserSpace(aTarget, tmpWidth) * scale;
 | 
						||
    height = SVGUtils::UserSpace(aTarget, tmpHeight) * scale;
 | 
						||
  }
 | 
						||
 | 
						||
  return gfxRect(x, y, width, height);
 | 
						||
}
 | 
						||
 | 
						||
gfxMatrix SVGPatternFrame::ConstructCTM(const SVGAnimatedViewBox& aViewBox,
 | 
						||
                                        uint16_t aPatternContentUnits,
 | 
						||
                                        uint16_t aPatternUnits,
 | 
						||
                                        const gfxRect& callerBBox,
 | 
						||
                                        const Matrix& callerCTM,
 | 
						||
                                        nsIFrame* aTarget) {
 | 
						||
  SVGViewportElement* ctx = nullptr;
 | 
						||
  nsIContent* targetContent = aTarget->GetContent();
 | 
						||
  gfxFloat scaleX, scaleY;
 | 
						||
 | 
						||
  // The objectBoundingBox conversion must be handled in the CTM:
 | 
						||
  if (IncludeBBoxScale(aViewBox, aPatternContentUnits, aPatternUnits)) {
 | 
						||
    scaleX = callerBBox.Width();
 | 
						||
    scaleY = callerBBox.Height();
 | 
						||
  } else {
 | 
						||
    if (targetContent->IsSVGElement()) {
 | 
						||
      ctx = static_cast<SVGElement*>(targetContent)->GetCtx();
 | 
						||
    }
 | 
						||
    scaleX = scaleY = MaxExpansion(callerCTM);
 | 
						||
  }
 | 
						||
 | 
						||
  if (!aViewBox.IsExplicitlySet()) {
 | 
						||
    return gfxMatrix(scaleX, 0.0, 0.0, scaleY, 0.0, 0.0);
 | 
						||
  }
 | 
						||
  const SVGViewBox& viewBox = aViewBox.GetAnimValue();
 | 
						||
 | 
						||
  if (viewBox.height <= 0.0f || viewBox.width <= 0.0f) {
 | 
						||
    return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);  // singular
 | 
						||
  }
 | 
						||
 | 
						||
  float viewportWidth, viewportHeight;
 | 
						||
  if (targetContent->IsSVGElement()) {
 | 
						||
    // If we're dealing with an SVG target only retrieve the context once.
 | 
						||
    // Calling the nsIFrame* variant of GetAnimValue would look it up on
 | 
						||
    // every call.
 | 
						||
    viewportWidth =
 | 
						||
        GetLengthValue(SVGPatternElement::ATTR_WIDTH)->GetAnimValue(ctx);
 | 
						||
    viewportHeight =
 | 
						||
        GetLengthValue(SVGPatternElement::ATTR_HEIGHT)->GetAnimValue(ctx);
 | 
						||
  } else {
 | 
						||
    // No SVG target, call the nsIFrame* variant of GetAnimValue.
 | 
						||
    viewportWidth =
 | 
						||
        GetLengthValue(SVGPatternElement::ATTR_WIDTH)->GetAnimValue(aTarget);
 | 
						||
    viewportHeight =
 | 
						||
        GetLengthValue(SVGPatternElement::ATTR_HEIGHT)->GetAnimValue(aTarget);
 | 
						||
  }
 | 
						||
 | 
						||
  if (viewportWidth <= 0.0f || viewportHeight <= 0.0f) {
 | 
						||
    return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);  // singular
 | 
						||
  }
 | 
						||
 | 
						||
  Matrix tm = SVGContentUtils::GetViewBoxTransform(
 | 
						||
      viewportWidth * scaleX, viewportHeight * scaleY, viewBox.x, viewBox.y,
 | 
						||
      viewBox.width, viewBox.height, GetPreserveAspectRatio());
 | 
						||
 | 
						||
  return ThebesMatrix(tm);
 | 
						||
}
 | 
						||
 | 
						||
//----------------------------------------------------------------------
 | 
						||
// SVGPaintServerFrame methods:
 | 
						||
already_AddRefed<gfxPattern> SVGPatternFrame::GetPaintServerPattern(
 | 
						||
    nsIFrame* aSource, const DrawTarget* aDrawTarget,
 | 
						||
    const gfxMatrix& aContextMatrix, StyleSVGPaint nsStyleSVG::*aFillOrStroke,
 | 
						||
    float aGraphicOpacity, imgDrawingParams& aImgParams,
 | 
						||
    const gfxRect* aOverrideBounds) {
 | 
						||
  if (aGraphicOpacity == 0.0f) {
 | 
						||
    return do_AddRef(new gfxPattern(DeviceColor()));
 | 
						||
  }
 | 
						||
 | 
						||
  // Paint it!
 | 
						||
  Matrix pMatrix;
 | 
						||
  RefPtr<SourceSurface> surface =
 | 
						||
      PaintPattern(aDrawTarget, &pMatrix, ToMatrix(aContextMatrix), aSource,
 | 
						||
                   aFillOrStroke, aGraphicOpacity, aOverrideBounds, aImgParams);
 | 
						||
 | 
						||
  if (!surface) {
 | 
						||
    return nullptr;
 | 
						||
  }
 | 
						||
 | 
						||
  RefPtr<gfxPattern> pattern = new gfxPattern(surface, pMatrix);
 | 
						||
 | 
						||
  if (!pattern) {
 | 
						||
    return nullptr;
 | 
						||
  }
 | 
						||
 | 
						||
  pattern->SetExtend(ExtendMode::REPEAT);
 | 
						||
  return pattern.forget();
 | 
						||
}
 | 
						||
 | 
						||
}  // namespace mozilla
 | 
						||
 | 
						||
// -------------------------------------------------------------------------
 | 
						||
// Public functions
 | 
						||
// -------------------------------------------------------------------------
 | 
						||
 | 
						||
nsIFrame* NS_NewSVGPatternFrame(mozilla::PresShell* aPresShell,
 | 
						||
                                mozilla::ComputedStyle* aStyle) {
 | 
						||
  return new (aPresShell)
 | 
						||
      mozilla::SVGPatternFrame(aStyle, aPresShell->GetPresContext());
 | 
						||
}
 |