/* -*- 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 "SVGMarkerFrame.h" // Keep others in (case-insensitive) order: #include "gfxContext.h" #include "mozilla/PresShell.h" #include "mozilla/SVGContextPaint.h" #include "mozilla/SVGGeometryFrame.h" #include "mozilla/SVGObserverUtils.h" #include "mozilla/SVGUtils.h" #include "mozilla/dom/SVGGeometryElement.h" #include "mozilla/dom/SVGMarkerElement.h" using namespace mozilla::dom; using namespace mozilla::gfx; using namespace mozilla::image; nsContainerFrame* NS_NewSVGMarkerFrame(mozilla::PresShell* aPresShell, mozilla::ComputedStyle* aStyle) { return new (aPresShell) mozilla::SVGMarkerFrame(aStyle, aPresShell->GetPresContext()); } namespace mozilla { NS_IMPL_FRAMEARENA_HELPERS(SVGMarkerFrame) //---------------------------------------------------------------------- // nsIFrame methods: nsresult SVGMarkerFrame::AttributeChanged(int32_t aNameSpaceID, nsAtom* aAttribute, int32_t aModType) { if (aNameSpaceID == kNameSpaceID_None && (aAttribute == nsGkAtoms::markerUnits || aAttribute == nsGkAtoms::refX || aAttribute == nsGkAtoms::refY || aAttribute == nsGkAtoms::markerWidth || aAttribute == nsGkAtoms::markerHeight || aAttribute == nsGkAtoms::orient || aAttribute == nsGkAtoms::preserveAspectRatio || aAttribute == nsGkAtoms::viewBox)) { SVGObserverUtils::InvalidateRenderingObservers(this); } return SVGContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType); } #ifdef DEBUG void SVGMarkerFrame::Init(nsIContent* aContent, nsContainerFrame* aParent, nsIFrame* aPrevInFlow) { NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::marker), "Content is not an SVG marker"); SVGContainerFrame::Init(aContent, aParent, aPrevInFlow); } #endif /* DEBUG */ //---------------------------------------------------------------------- // SVGContainerFrame methods: gfxMatrix SVGMarkerFrame::GetCanvasTM() { NS_ASSERTION(mMarkedFrame, "null SVGGeometry frame"); if (mInUse2) { // We're going to be bailing drawing the marker, so return an identity. return gfxMatrix(); } SVGMarkerElement* content = static_cast(GetContent()); mInUse2 = true; gfxMatrix markedTM = mMarkedFrame->GetCanvasTM(); mInUse2 = false; Matrix viewBoxTM = content->GetViewBoxTransform(); return ThebesMatrix(viewBoxTM * mMarkerTM) * markedTM; } static nsIFrame* GetAnonymousChildFrame(nsIFrame* aFrame) { nsIFrame* kid = aFrame->PrincipalChildList().FirstChild(); MOZ_ASSERT(kid && kid->IsSVGMarkerAnonChildFrame(), "expected to find anonymous child of marker frame"); return kid; } void SVGMarkerFrame::PaintMark(gfxContext& aContext, const gfxMatrix& aToMarkedFrameUserSpace, SVGGeometryFrame* aMarkedFrame, const SVGMark& aMark, float aStrokeWidth, imgDrawingParams& aImgParams) { // If the flag is set when we get here, it means this marker frame // has already been used painting the current mark, and the document // has a marker reference loop. if (mInUse) { return; } AutoMarkerReferencer markerRef(this, aMarkedFrame); SVGMarkerElement* marker = static_cast(GetContent()); if (!marker->HasValidDimensions()) { return; } const SVGViewBox viewBox = marker->GetViewBox(); if (viewBox.width <= 0.0f || viewBox.height <= 0.0f) { // We must disable rendering if the viewBox width or height are zero. return; } Matrix viewBoxTM = marker->GetViewBoxTransform(); mMarkerTM = marker->GetMarkerTransform(aStrokeWidth, aMark); gfxMatrix markTM = ThebesMatrix(viewBoxTM) * ThebesMatrix(mMarkerTM) * aToMarkedFrameUserSpace; gfxClipAutoSaveRestore autoSaveClip(&aContext); if (StyleDisplay()->IsScrollableOverflow()) { gfxRect clipRect = SVGUtils::GetClipRectForFrame( this, viewBox.x, viewBox.y, viewBox.width, viewBox.height); autoSaveClip.TransformedClip(markTM, clipRect); } nsIFrame* kid = GetAnonymousChildFrame(this); ISVGDisplayableFrame* SVGFrame = do_QueryFrame(kid); // The CTM of each frame referencing us may be different. SVGFrame->NotifySVGChanged(ISVGDisplayableFrame::TRANSFORM_CHANGED); RefPtr contextPaint = new SVGContextPaintImpl(); contextPaint->Init(aContext.GetDrawTarget(), aContext.CurrentMatrixDouble(), aMarkedFrame, SVGContextPaint::GetContextPaint(marker), aImgParams); AutoSetRestoreSVGContextPaint autoSetRestore(contextPaint, marker->OwnerDoc()); SVGUtils::PaintFrameWithEffects(kid, aContext, markTM, aImgParams); } SVGBBox SVGMarkerFrame::GetMarkBBoxContribution(const Matrix& aToBBoxUserspace, uint32_t aFlags, SVGGeometryFrame* aMarkedFrame, const SVGMark& aMark, float aStrokeWidth) { SVGBBox bbox; // If the flag is set when we get here, it means this marker frame // has already been used in calculating the current mark bbox, and // the document has a marker reference loop. if (mInUse) { return bbox; } AutoMarkerReferencer markerRef(this, aMarkedFrame); SVGMarkerElement* content = static_cast(GetContent()); if (!content->HasValidDimensions()) { return bbox; } const SVGViewBox viewBox = content->GetViewBox(); if (viewBox.width <= 0.0f || viewBox.height <= 0.0f) { return bbox; } mMarkerTM = content->GetMarkerTransform(aStrokeWidth, aMark); Matrix viewBoxTM = content->GetViewBoxTransform(); Matrix tm = viewBoxTM * mMarkerTM * aToBBoxUserspace; ISVGDisplayableFrame* child = do_QueryFrame(GetAnonymousChildFrame(this)); // When we're being called to obtain the invalidation area, we need to // pass down all the flags so that stroke is included. However, once DOM // getBBox() accepts flags, maybe we should strip some of those here? // We need to include zero width/height vertical/horizontal lines, so we have // to use UnionEdges. bbox.UnionEdges(child->GetBBoxContribution(tm, aFlags)); return bbox; } void SVGMarkerFrame::SetParentCoordCtxProvider(SVGViewportElement* aContext) { SVGMarkerElement* marker = static_cast(GetContent()); marker->SetParentCoordCtxProvider(aContext); } void SVGMarkerFrame::AppendDirectlyOwnedAnonBoxes( nsTArray& aResult) { aResult.AppendElement(OwnedAnonBox(GetAnonymousChildFrame(this))); } //---------------------------------------------------------------------- // helper class SVGMarkerFrame::AutoMarkerReferencer::AutoMarkerReferencer( SVGMarkerFrame* aFrame, SVGGeometryFrame* aMarkedFrame) : mFrame(aFrame) { mFrame->mInUse = true; mFrame->mMarkedFrame = aMarkedFrame; SVGViewportElement* ctx = static_cast(aMarkedFrame->GetContent())->GetCtx(); mFrame->SetParentCoordCtxProvider(ctx); } SVGMarkerFrame::AutoMarkerReferencer::~AutoMarkerReferencer() { mFrame->SetParentCoordCtxProvider(nullptr); mFrame->mMarkedFrame = nullptr; mFrame->mInUse = false; } } // namespace mozilla //---------------------------------------------------------------------- // Implementation of SVGMarkerAnonChildFrame nsContainerFrame* NS_NewSVGMarkerAnonChildFrame( mozilla::PresShell* aPresShell, mozilla::ComputedStyle* aStyle) { return new (aPresShell) mozilla::SVGMarkerAnonChildFrame(aStyle, aPresShell->GetPresContext()); } namespace mozilla { NS_IMPL_FRAMEARENA_HELPERS(SVGMarkerAnonChildFrame) #ifdef DEBUG void SVGMarkerAnonChildFrame::Init(nsIContent* aContent, nsContainerFrame* aParent, nsIFrame* aPrevInFlow) { MOZ_ASSERT(aParent->IsSVGMarkerFrame(), "Unexpected parent"); SVGDisplayContainerFrame::Init(aContent, aParent, aPrevInFlow); } #endif } // namespace mozilla