michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: // Main header first: michael@0: #include "nsSVGPatternFrame.h" michael@0: michael@0: // Keep others in (case-insensitive) order: michael@0: #include "gfx2DGlue.h" michael@0: #include "gfxContext.h" michael@0: #include "gfxMatrix.h" michael@0: #include "gfxPattern.h" michael@0: #include "gfxPlatform.h" michael@0: #include "mozilla/gfx/2D.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsISVGChildFrame.h" michael@0: #include "nsRenderingContext.h" michael@0: #include "nsStyleContext.h" michael@0: #include "nsSVGEffects.h" michael@0: #include "nsSVGPathGeometryFrame.h" michael@0: #include "mozilla/dom/SVGPatternElement.h" michael@0: #include "nsSVGUtils.h" michael@0: #include "nsSVGAnimatedTransformList.h" michael@0: #include "SVGContentUtils.h" michael@0: #include "gfxColor.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: using namespace mozilla::gfx; michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // Helper classes michael@0: michael@0: class MOZ_STACK_CLASS nsSVGPatternFrame::AutoPatternReferencer michael@0: { michael@0: public: michael@0: AutoPatternReferencer(nsSVGPatternFrame *aFrame michael@0: MOZ_GUARD_OBJECT_NOTIFIER_PARAM) michael@0: : mFrame(aFrame) michael@0: { michael@0: MOZ_GUARD_OBJECT_NOTIFIER_INIT; michael@0: // Reference loops should normally be detected in advance and handled, so michael@0: // we're not expecting to encounter them here michael@0: NS_ABORT_IF_FALSE(!mFrame->mLoopFlag, "Undetected reference loop!"); michael@0: mFrame->mLoopFlag = true; michael@0: } michael@0: ~AutoPatternReferencer() { michael@0: mFrame->mLoopFlag = false; michael@0: } michael@0: private: michael@0: nsSVGPatternFrame *mFrame; michael@0: MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER michael@0: }; michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // Implementation michael@0: michael@0: nsSVGPatternFrame::nsSVGPatternFrame(nsStyleContext* aContext) : michael@0: nsSVGPatternFrameBase(aContext), michael@0: mLoopFlag(false), michael@0: mNoHRefURI(false) michael@0: { michael@0: } michael@0: michael@0: NS_IMPL_FRAMEARENA_HELPERS(nsSVGPatternFrame) michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // nsIFrame methods: michael@0: michael@0: nsresult michael@0: nsSVGPatternFrame::AttributeChanged(int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute, michael@0: int32_t aModType) michael@0: { michael@0: if (aNameSpaceID == kNameSpaceID_None && michael@0: (aAttribute == nsGkAtoms::patternUnits || michael@0: aAttribute == nsGkAtoms::patternContentUnits || michael@0: aAttribute == nsGkAtoms::patternTransform || michael@0: aAttribute == nsGkAtoms::x || michael@0: aAttribute == nsGkAtoms::y || michael@0: aAttribute == nsGkAtoms::width || michael@0: aAttribute == nsGkAtoms::height || michael@0: aAttribute == nsGkAtoms::preserveAspectRatio || michael@0: aAttribute == nsGkAtoms::viewBox)) { michael@0: nsSVGEffects::InvalidateDirectRenderingObservers(this); michael@0: } michael@0: michael@0: if (aNameSpaceID == kNameSpaceID_XLink && michael@0: aAttribute == nsGkAtoms::href) { michael@0: // Blow away our reference, if any michael@0: Properties().Delete(nsSVGEffects::HrefProperty()); michael@0: mNoHRefURI = false; michael@0: // And update whoever references us michael@0: nsSVGEffects::InvalidateDirectRenderingObservers(this); michael@0: } michael@0: michael@0: return nsSVGPatternFrameBase::AttributeChanged(aNameSpaceID, michael@0: aAttribute, aModType); michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: void michael@0: nsSVGPatternFrame::Init(nsIContent* aContent, michael@0: nsIFrame* aParent, michael@0: nsIFrame* aPrevInFlow) michael@0: { michael@0: NS_ASSERTION(aContent->IsSVG(nsGkAtoms::pattern), "Content is not an SVG pattern"); michael@0: michael@0: nsSVGPatternFrameBase::Init(aContent, aParent, aPrevInFlow); michael@0: } michael@0: #endif /* DEBUG */ michael@0: michael@0: nsIAtom* michael@0: nsSVGPatternFrame::GetType() const michael@0: { michael@0: return nsGkAtoms::svgPatternFrame; michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // nsSVGContainerFrame methods: michael@0: michael@0: // If our GetCanvasTM is getting called, we michael@0: // need to return *our current* transformation michael@0: // matrix, which depends on our units parameters michael@0: // and X, Y, Width, and Height michael@0: gfxMatrix michael@0: nsSVGPatternFrame::GetCanvasTM(uint32_t aFor, nsIFrame* aTransformRoot) michael@0: { michael@0: if (mCTM) { michael@0: return *mCTM; michael@0: } michael@0: michael@0: // Do we know our rendering parent? michael@0: if (mSource) { michael@0: // Yes, use it! michael@0: return mSource->GetCanvasTM(aFor, aTransformRoot); michael@0: } michael@0: michael@0: // We get here when geometry in the container is updated michael@0: return gfxMatrix(); michael@0: } michael@0: michael@0: // ------------------------------------------------------------------------- michael@0: // Helper functions michael@0: // ------------------------------------------------------------------------- michael@0: michael@0: /** Calculate the maximum expansion of a matrix */ michael@0: static float michael@0: MaxExpansion(const Matrix &aMatrix) michael@0: { michael@0: // maximum expansion derivation from michael@0: // http://lists.cairographics.org/archives/cairo/2004-October/001980.html michael@0: // and also implemented in cairo_matrix_transformed_circle_major_axis michael@0: double a = aMatrix._11; michael@0: double b = aMatrix._12; michael@0: double c = aMatrix._21; michael@0: double d = aMatrix._22; michael@0: double f = (a * a + b * b + c * c + d * d) / 2; michael@0: double g = (a * a + b * b - c * c - d * d) / 2; michael@0: double h = a * c + b * d; michael@0: return sqrt(f + sqrt(g * g + h * h)); michael@0: } michael@0: michael@0: // The SVG specification says that the 'patternContentUnits' attribute "has no effect if michael@0: // attribute ‘viewBox’ is specified". We still need to include a bbox scale michael@0: // if the viewBox is specified and _patternUnits_ is set to or defaults to michael@0: // objectBoundingBox though, since in that case the viewBox is relative to the bbox michael@0: static bool michael@0: IncludeBBoxScale(const nsSVGViewBox& aViewBox, michael@0: uint32_t aPatternContentUnits, uint32_t aPatternUnits) michael@0: { michael@0: return (!aViewBox.IsExplicitlySet() && michael@0: aPatternContentUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) || michael@0: (aViewBox.IsExplicitlySet() && michael@0: aPatternUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX); michael@0: } michael@0: michael@0: // Given the matrix for the pattern element's own transform, this returns a michael@0: // combined matrix including the transforms applicable to its target. michael@0: static gfxMatrix michael@0: GetPatternMatrix(uint16_t aPatternUnits, michael@0: const gfxMatrix &patternTransform, michael@0: const gfxRect &bbox, michael@0: const gfxRect &callerBBox, michael@0: const Matrix &callerCTM) michael@0: { michael@0: // We really want the pattern matrix to handle translations michael@0: gfxFloat minx = bbox.X(); michael@0: gfxFloat miny = bbox.Y(); michael@0: michael@0: if (aPatternUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { michael@0: minx += callerBBox.X(); michael@0: miny += callerBBox.Y(); michael@0: } michael@0: michael@0: float scale = 1.0f / MaxExpansion(callerCTM); michael@0: gfxMatrix patternMatrix = patternTransform; michael@0: patternMatrix.Scale(scale, scale); michael@0: patternMatrix.Translate(gfxPoint(minx, miny)); michael@0: michael@0: return patternMatrix; michael@0: } michael@0: michael@0: static nsresult michael@0: GetTargetGeometry(gfxRect *aBBox, michael@0: const nsSVGViewBox &aViewBox, michael@0: uint16_t aPatternContentUnits, michael@0: uint16_t aPatternUnits, michael@0: nsIFrame *aTarget, michael@0: const Matrix &aContextMatrix, michael@0: const gfxRect *aOverrideBounds) michael@0: { michael@0: *aBBox = aOverrideBounds ? *aOverrideBounds : nsSVGUtils::GetBBox(aTarget); michael@0: michael@0: // Sanity check michael@0: if (IncludeBBoxScale(aViewBox, aPatternContentUnits, aPatternUnits) && michael@0: (aBBox->Width() <= 0 || aBBox->Height() <= 0)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // OK, now fix up the bounding box to reflect user coordinates michael@0: // We handle device unit scaling in pattern matrix michael@0: float scale = MaxExpansion(aContextMatrix); michael@0: if (scale <= 0) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: aBBox->Scale(scale); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsSVGPatternFrame::PaintPattern(gfxASurface** surface, michael@0: gfxMatrix* patternMatrix, michael@0: const gfxMatrix &aContextMatrix, michael@0: nsIFrame *aSource, michael@0: nsStyleSVGPaint nsStyleSVG::*aFillOrStroke, michael@0: float aGraphicOpacity, michael@0: const gfxRect *aOverrideBounds) michael@0: { michael@0: /* michael@0: * General approach: michael@0: * Set the content geometry stuff michael@0: * Calculate our bbox (using x,y,width,height & patternUnits & michael@0: * patternTransform) michael@0: * Create the surface michael@0: * Calculate the content transformation matrix michael@0: * Get our children (we may need to get them from another Pattern) michael@0: * Call SVGPaint on all of our children michael@0: * Return michael@0: */ michael@0: *surface = nullptr; michael@0: michael@0: // Get the first child of the pattern data we will render michael@0: nsIFrame* firstKid = GetPatternFirstChild(); michael@0: if (!firstKid) michael@0: return NS_ERROR_FAILURE; // Either no kids or a bad reference michael@0: michael@0: const nsSVGViewBox& viewBox = GetViewBox(); michael@0: michael@0: uint16_t patternContentUnits = michael@0: GetEnumValue(SVGPatternElement::PATTERNCONTENTUNITS); michael@0: uint16_t patternUnits = michael@0: GetEnumValue(SVGPatternElement::PATTERNUNITS); michael@0: michael@0: /* michael@0: * Get the content geometry information. This is a little tricky -- michael@0: * our parent is probably a , but we are rendering in the context michael@0: * of some geometry source. Our content geometry information needs to michael@0: * come from our rendering parent as opposed to our content parent. We michael@0: * get that information from aSource, which is passed to us from the michael@0: * backend renderer. michael@0: * michael@0: * There are three "geometries" that we need: michael@0: * 1) The bounding box for the pattern. We use this to get the michael@0: * width and height for the surface, and as the return to michael@0: * GetBBox. michael@0: * 2) The transformation matrix for the pattern. This is not *quite* michael@0: * the same as the canvas transformation matrix that we will michael@0: * provide to our rendering children since we "fudge" it a little michael@0: * to get the renderer to handle the translations correctly for us. michael@0: * 3) The CTM that we return to our children who make up the pattern. michael@0: */ michael@0: michael@0: // Get all of the information we need from our "caller" -- i.e. michael@0: // the geometry that is being rendered with a pattern michael@0: gfxRect callerBBox; michael@0: if (NS_FAILED(GetTargetGeometry(&callerBBox, michael@0: viewBox, michael@0: patternContentUnits, patternUnits, michael@0: aSource, michael@0: ToMatrix(aContextMatrix), michael@0: aOverrideBounds))) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: // Construct the CTM that we will provide to our children when we michael@0: // render them into the tile. michael@0: gfxMatrix ctm = ConstructCTM(viewBox, patternContentUnits, patternUnits, michael@0: callerBBox, ToMatrix(aContextMatrix), aSource); michael@0: if (ctm.IsSingular()) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // Get the pattern we are going to render michael@0: nsSVGPatternFrame *patternFrame = michael@0: static_cast(firstKid->GetParent()); michael@0: if (patternFrame->mCTM) { michael@0: *patternFrame->mCTM = ctm; michael@0: } else { michael@0: patternFrame->mCTM = new gfxMatrix(ctm); michael@0: } michael@0: michael@0: // Get the bounding box of the pattern. This will be used to determine michael@0: // the size of the surface, and will also be used to define the bounding michael@0: // box for the pattern tile. michael@0: gfxRect bbox = GetPatternRect(patternUnits, callerBBox, ToMatrix(aContextMatrix), aSource); michael@0: if (bbox.Width() <= 0.0 || bbox.Height() <= 0.0) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // Get the pattern transform michael@0: gfxMatrix patternTransform = GetPatternTransform(); michael@0: michael@0: // revert the vector effect transform so that the pattern appears unchanged michael@0: if (aFillOrStroke == &nsStyleSVG::mStroke) { michael@0: patternTransform.Multiply(nsSVGUtils::GetStrokeTransform(aSource).Invert()); michael@0: } michael@0: michael@0: // Get the transformation matrix that we will hand to the renderer's pattern michael@0: // routine. michael@0: *patternMatrix = GetPatternMatrix(patternUnits, patternTransform, michael@0: bbox, callerBBox, ToMatrix(aContextMatrix)); michael@0: if (patternMatrix->IsSingular()) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // Now that we have all of the necessary geometries, we can michael@0: // create our surface. michael@0: gfxRect transformedBBox = patternTransform.TransformBounds(bbox); michael@0: michael@0: bool resultOverflows; michael@0: IntSize surfaceSize = michael@0: nsSVGUtils::ConvertToSurfaceSize( michael@0: transformedBBox.Size(), &resultOverflows).ToIntSize(); michael@0: michael@0: // 0 disables rendering, < 0 is an error michael@0: if (surfaceSize.width <= 0 || surfaceSize.height <= 0) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: gfxFloat patternWidth = bbox.Width(); michael@0: gfxFloat patternHeight = bbox.Height(); michael@0: michael@0: if (resultOverflows || michael@0: patternWidth != surfaceSize.width || michael@0: patternHeight != surfaceSize.height) { michael@0: // scale drawing to pattern surface size michael@0: gfxMatrix tempTM = michael@0: gfxMatrix(surfaceSize.width / patternWidth, 0.0f, michael@0: 0.0f, surfaceSize.height / patternHeight, michael@0: 0.0f, 0.0f); michael@0: patternFrame->mCTM->PreMultiply(tempTM); michael@0: michael@0: // and rescale pattern to compensate michael@0: patternMatrix->Scale(patternWidth / surfaceSize.width, michael@0: patternHeight / surfaceSize.height); michael@0: } michael@0: michael@0: nsRefPtr tmpSurface = michael@0: gfxPlatform::GetPlatform()->CreateOffscreenSurface(surfaceSize, michael@0: gfxContentType::COLOR_ALPHA); michael@0: if (!tmpSurface || tmpSurface->CairoStatus()) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsRefPtr context(new nsRenderingContext()); michael@0: context->Init(aSource->PresContext()->DeviceContext(), tmpSurface); michael@0: gfxContext* gfx = context->ThebesContext(); michael@0: michael@0: // Fill with transparent black michael@0: gfx->SetOperator(gfxContext::OPERATOR_CLEAR); michael@0: gfx->Paint(); michael@0: gfx->SetOperator(gfxContext::OPERATOR_OVER); michael@0: michael@0: if (aGraphicOpacity != 1.0f) { michael@0: gfx->Save(); michael@0: gfx->PushGroup(gfxContentType::COLOR_ALPHA); michael@0: } michael@0: michael@0: // OK, now render -- note that we use "firstKid", which michael@0: // we got at the beginning because it takes care of the michael@0: // referenced pattern situation for us michael@0: michael@0: if (aSource->IsFrameOfType(nsIFrame::eSVGGeometry)) { michael@0: // Set the geometrical parent of the pattern we are rendering michael@0: patternFrame->mSource = static_cast(aSource); michael@0: } michael@0: michael@0: // Delay checking NS_FRAME_DRAWING_AS_PAINTSERVER bit until here so we can michael@0: // give back a clear surface if there's a loop michael@0: if (!(patternFrame->GetStateBits() & NS_FRAME_DRAWING_AS_PAINTSERVER)) { michael@0: patternFrame->AddStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER); michael@0: for (nsIFrame* kid = firstKid; kid; michael@0: kid = kid->GetNextSibling()) { michael@0: // The CTM of each frame referencing us can be different michael@0: nsISVGChildFrame* SVGFrame = do_QueryFrame(kid); michael@0: if (SVGFrame) { michael@0: SVGFrame->NotifySVGChanged(nsISVGChildFrame::TRANSFORM_CHANGED); michael@0: } michael@0: nsSVGUtils::PaintFrameWithEffects(context, nullptr, kid); michael@0: } michael@0: patternFrame->RemoveStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER); michael@0: } michael@0: michael@0: patternFrame->mSource = nullptr; michael@0: michael@0: if (aGraphicOpacity != 1.0f) { michael@0: gfx->PopGroupToSource(); michael@0: gfx->Paint(aGraphicOpacity); michael@0: gfx->Restore(); michael@0: } michael@0: michael@0: // caller now owns the surface michael@0: tmpSurface.forget(surface); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* Will probably need something like this... */ michael@0: // How do we handle the insertion of a new frame? michael@0: // We really don't want to rerender this every time, michael@0: // do we? michael@0: nsIFrame* michael@0: nsSVGPatternFrame::GetPatternFirstChild() michael@0: { michael@0: // Do we have any children ourselves? michael@0: nsIFrame* kid = mFrames.FirstChild(); michael@0: if (kid) michael@0: return kid; michael@0: michael@0: // No, see if we chain to someone who does michael@0: AutoPatternReferencer patternRef(this); michael@0: michael@0: nsSVGPatternFrame* next = GetReferencedPatternIfNotInUse(); michael@0: if (!next) michael@0: return nullptr; michael@0: michael@0: return next->GetPatternFirstChild(); michael@0: } michael@0: michael@0: uint16_t michael@0: nsSVGPatternFrame::GetEnumValue(uint32_t aIndex, nsIContent *aDefault) michael@0: { michael@0: nsSVGEnum& thisEnum = michael@0: static_cast(mContent)->mEnumAttributes[aIndex]; michael@0: michael@0: if (thisEnum.IsExplicitlySet()) michael@0: return thisEnum.GetAnimValue(); michael@0: michael@0: AutoPatternReferencer patternRef(this); michael@0: michael@0: nsSVGPatternFrame *next = GetReferencedPatternIfNotInUse(); michael@0: return next ? next->GetEnumValue(aIndex, aDefault) : michael@0: static_cast(aDefault)-> michael@0: mEnumAttributes[aIndex].GetAnimValue(); michael@0: } michael@0: michael@0: nsSVGAnimatedTransformList* michael@0: nsSVGPatternFrame::GetPatternTransformList(nsIContent* aDefault) michael@0: { michael@0: nsSVGAnimatedTransformList *thisTransformList = michael@0: static_cast(mContent)->GetAnimatedTransformList(); michael@0: michael@0: if (thisTransformList && thisTransformList->IsExplicitlySet()) michael@0: return thisTransformList; michael@0: michael@0: AutoPatternReferencer patternRef(this); michael@0: michael@0: nsSVGPatternFrame *next = GetReferencedPatternIfNotInUse(); michael@0: return next ? next->GetPatternTransformList(aDefault) : michael@0: static_cast(aDefault)->mPatternTransform.get(); michael@0: } michael@0: michael@0: gfxMatrix michael@0: nsSVGPatternFrame::GetPatternTransform() michael@0: { michael@0: nsSVGAnimatedTransformList* animTransformList = michael@0: GetPatternTransformList(mContent); michael@0: if (!animTransformList) michael@0: return gfxMatrix(); michael@0: michael@0: return animTransformList->GetAnimValue().GetConsolidationMatrix(); michael@0: } michael@0: michael@0: const nsSVGViewBox & michael@0: nsSVGPatternFrame::GetViewBox(nsIContent* aDefault) michael@0: { michael@0: const nsSVGViewBox &thisViewBox = michael@0: static_cast(mContent)->mViewBox; michael@0: michael@0: if (thisViewBox.IsExplicitlySet()) michael@0: return thisViewBox; michael@0: michael@0: AutoPatternReferencer patternRef(this); michael@0: michael@0: nsSVGPatternFrame *next = GetReferencedPatternIfNotInUse(); michael@0: return next ? next->GetViewBox(aDefault) : michael@0: static_cast(aDefault)->mViewBox; michael@0: } michael@0: michael@0: const SVGAnimatedPreserveAspectRatio & michael@0: nsSVGPatternFrame::GetPreserveAspectRatio(nsIContent *aDefault) michael@0: { michael@0: const SVGAnimatedPreserveAspectRatio &thisPar = michael@0: static_cast(mContent)->mPreserveAspectRatio; michael@0: michael@0: if (thisPar.IsExplicitlySet()) michael@0: return thisPar; michael@0: michael@0: AutoPatternReferencer patternRef(this); michael@0: michael@0: nsSVGPatternFrame *next = GetReferencedPatternIfNotInUse(); michael@0: return next ? next->GetPreserveAspectRatio(aDefault) : michael@0: static_cast(aDefault)->mPreserveAspectRatio; michael@0: } michael@0: michael@0: const nsSVGLength2 * michael@0: nsSVGPatternFrame::GetLengthValue(uint32_t aIndex, nsIContent *aDefault) michael@0: { michael@0: const nsSVGLength2 *thisLength = michael@0: &static_cast(mContent)->mLengthAttributes[aIndex]; michael@0: michael@0: if (thisLength->IsExplicitlySet()) michael@0: return thisLength; michael@0: michael@0: AutoPatternReferencer patternRef(this); michael@0: michael@0: nsSVGPatternFrame *next = GetReferencedPatternIfNotInUse(); michael@0: return next ? next->GetLengthValue(aIndex, aDefault) : michael@0: &static_cast(aDefault)->mLengthAttributes[aIndex]; michael@0: } michael@0: michael@0: // Private (helper) methods michael@0: nsSVGPatternFrame * michael@0: nsSVGPatternFrame::GetReferencedPattern() michael@0: { michael@0: if (mNoHRefURI) michael@0: return nullptr; michael@0: michael@0: nsSVGPaintingProperty *property = static_cast michael@0: (Properties().Get(nsSVGEffects::HrefProperty())); michael@0: michael@0: if (!property) { michael@0: // Fetch our pattern element's xlink:href attribute michael@0: SVGPatternElement *pattern = static_cast(mContent); michael@0: nsAutoString href; michael@0: pattern->mStringAttributes[SVGPatternElement::HREF].GetAnimValue(href, pattern); michael@0: if (href.IsEmpty()) { michael@0: mNoHRefURI = true; michael@0: return nullptr; // no URL michael@0: } michael@0: michael@0: // Convert href to an nsIURI michael@0: nsCOMPtr targetURI; michael@0: nsCOMPtr base = mContent->GetBaseURI(); michael@0: nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href, michael@0: mContent->GetCurrentDoc(), base); michael@0: michael@0: property = michael@0: nsSVGEffects::GetPaintingProperty(targetURI, this, nsSVGEffects::HrefProperty()); michael@0: if (!property) michael@0: return nullptr; michael@0: } michael@0: michael@0: nsIFrame *result = property->GetReferencedFrame(); michael@0: if (!result) michael@0: return nullptr; michael@0: michael@0: nsIAtom* frameType = result->GetType(); michael@0: if (frameType != nsGkAtoms::svgPatternFrame) michael@0: return nullptr; michael@0: michael@0: return static_cast(result); michael@0: } michael@0: michael@0: nsSVGPatternFrame * michael@0: nsSVGPatternFrame::GetReferencedPatternIfNotInUse() michael@0: { michael@0: nsSVGPatternFrame *referenced = GetReferencedPattern(); michael@0: if (!referenced) michael@0: return nullptr; michael@0: michael@0: if (referenced->mLoopFlag) { michael@0: // XXXjwatt: we should really send an error to the JavaScript Console here: michael@0: NS_WARNING("pattern reference loop detected while inheriting attribute!"); michael@0: return nullptr; michael@0: } michael@0: michael@0: return referenced; michael@0: } michael@0: michael@0: gfxRect michael@0: nsSVGPatternFrame::GetPatternRect(uint16_t aPatternUnits, michael@0: const gfxRect &aTargetBBox, michael@0: const Matrix &aTargetCTM, michael@0: nsIFrame *aTarget) michael@0: { michael@0: // We need to initialize our box michael@0: float x,y,width,height; michael@0: michael@0: // Get the pattern x,y,width, and height michael@0: const nsSVGLength2 *tmpX, *tmpY, *tmpHeight, *tmpWidth; michael@0: tmpX = GetLengthValue(SVGPatternElement::ATTR_X); michael@0: tmpY = GetLengthValue(SVGPatternElement::ATTR_Y); michael@0: tmpHeight = GetLengthValue(SVGPatternElement::ATTR_HEIGHT); michael@0: tmpWidth = GetLengthValue(SVGPatternElement::ATTR_WIDTH); michael@0: michael@0: if (aPatternUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { michael@0: x = nsSVGUtils::ObjectSpace(aTargetBBox, tmpX); michael@0: y = nsSVGUtils::ObjectSpace(aTargetBBox, tmpY); michael@0: width = nsSVGUtils::ObjectSpace(aTargetBBox, tmpWidth); michael@0: height = nsSVGUtils::ObjectSpace(aTargetBBox, tmpHeight); michael@0: } else { michael@0: float scale = MaxExpansion(aTargetCTM); michael@0: x = nsSVGUtils::UserSpace(aTarget, tmpX) * scale; michael@0: y = nsSVGUtils::UserSpace(aTarget, tmpY) * scale; michael@0: width = nsSVGUtils::UserSpace(aTarget, tmpWidth) * scale; michael@0: height = nsSVGUtils::UserSpace(aTarget, tmpHeight) * scale; michael@0: } michael@0: michael@0: return gfxRect(x, y, width, height); michael@0: } michael@0: michael@0: gfxMatrix michael@0: nsSVGPatternFrame::ConstructCTM(const nsSVGViewBox& aViewBox, michael@0: uint16_t aPatternContentUnits, michael@0: uint16_t aPatternUnits, michael@0: const gfxRect &callerBBox, michael@0: const Matrix &callerCTM, michael@0: nsIFrame *aTarget) michael@0: { michael@0: gfxMatrix tCTM; michael@0: SVGSVGElement *ctx = nullptr; michael@0: nsIContent* targetContent = aTarget->GetContent(); michael@0: michael@0: // The objectBoundingBox conversion must be handled in the CTM: michael@0: if (IncludeBBoxScale(aViewBox, aPatternContentUnits, aPatternUnits)) { michael@0: tCTM.Scale(callerBBox.Width(), callerBBox.Height()); michael@0: } else { michael@0: if (targetContent->IsSVG()) { michael@0: ctx = static_cast(targetContent)->GetCtx(); michael@0: } michael@0: float scale = MaxExpansion(callerCTM); michael@0: tCTM.Scale(scale, scale); michael@0: } michael@0: michael@0: if (!aViewBox.IsExplicitlySet()) { michael@0: return tCTM; michael@0: } michael@0: const nsSVGViewBoxRect viewBoxRect = aViewBox.GetAnimValue(); michael@0: michael@0: if (viewBoxRect.height <= 0.0f || viewBoxRect.width <= 0.0f) { michael@0: return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular michael@0: } michael@0: michael@0: float viewportWidth, viewportHeight; michael@0: if (targetContent->IsSVG()) { michael@0: // If we're dealing with an SVG target only retrieve the context once. michael@0: // Calling the nsIFrame* variant of GetAnimValue would look it up on michael@0: // every call. michael@0: viewportWidth = michael@0: GetLengthValue(SVGPatternElement::ATTR_WIDTH)->GetAnimValue(ctx); michael@0: viewportHeight = michael@0: GetLengthValue(SVGPatternElement::ATTR_HEIGHT)->GetAnimValue(ctx); michael@0: } else { michael@0: // No SVG target, call the nsIFrame* variant of GetAnimValue. michael@0: viewportWidth = michael@0: GetLengthValue(SVGPatternElement::ATTR_WIDTH)->GetAnimValue(aTarget); michael@0: viewportHeight = michael@0: GetLengthValue(SVGPatternElement::ATTR_HEIGHT)->GetAnimValue(aTarget); michael@0: } michael@0: michael@0: if (viewportWidth <= 0.0f || viewportHeight <= 0.0f) { michael@0: return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular michael@0: } michael@0: michael@0: Matrix tm = SVGContentUtils::GetViewBoxTransform( michael@0: viewportWidth, viewportHeight, michael@0: viewBoxRect.x, viewBoxRect.y, michael@0: viewBoxRect.width, viewBoxRect.height, michael@0: GetPreserveAspectRatio()); michael@0: michael@0: return ThebesMatrix(tm) * tCTM; michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // nsSVGPaintServerFrame methods: michael@0: michael@0: already_AddRefed michael@0: nsSVGPatternFrame::GetPaintServerPattern(nsIFrame *aSource, michael@0: const gfxMatrix& aContextMatrix, michael@0: nsStyleSVGPaint nsStyleSVG::*aFillOrStroke, michael@0: float aGraphicOpacity, michael@0: const gfxRect *aOverrideBounds) michael@0: { michael@0: if (aGraphicOpacity == 0.0f) { michael@0: nsRefPtr pattern = new gfxPattern(gfxRGBA(0, 0, 0, 0)); michael@0: return pattern.forget(); michael@0: } michael@0: michael@0: // Paint it! michael@0: nsRefPtr surface; michael@0: gfxMatrix pMatrix; michael@0: nsresult rv = PaintPattern(getter_AddRefs(surface), &pMatrix, aContextMatrix, michael@0: aSource, aFillOrStroke, aGraphicOpacity, aOverrideBounds); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: pMatrix.Invert(); michael@0: michael@0: nsRefPtr pattern = new gfxPattern(surface); michael@0: michael@0: if (!pattern || pattern->CairoStatus()) michael@0: return nullptr; michael@0: michael@0: pattern->SetMatrix(pMatrix); michael@0: pattern->SetExtend(gfxPattern::EXTEND_REPEAT); michael@0: return pattern.forget(); michael@0: } michael@0: michael@0: // ------------------------------------------------------------------------- michael@0: // Public functions michael@0: // ------------------------------------------------------------------------- michael@0: michael@0: nsIFrame* NS_NewSVGPatternFrame(nsIPresShell* aPresShell, michael@0: nsStyleContext* aContext) michael@0: { michael@0: return new (aPresShell) nsSVGPatternFrame(aContext); michael@0: } michael@0: