summaryrefslogtreecommitdiffstats
path: root/layout/svg/SVGMarkerFrame.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'layout/svg/SVGMarkerFrame.cpp')
-rw-r--r--layout/svg/SVGMarkerFrame.cpp245
1 files changed, 245 insertions, 0 deletions
diff --git a/layout/svg/SVGMarkerFrame.cpp b/layout/svg/SVGMarkerFrame.cpp
new file mode 100644
index 0000000000..4a8733369b
--- /dev/null
+++ b/layout/svg/SVGMarkerFrame.cpp
@@ -0,0 +1,245 @@
+/* -*- 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<SVGMarkerElement*>(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<SVGMarkerElement*>(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<SVGContextPaintImpl> 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<SVGMarkerElement*>(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<SVGMarkerElement*>(GetContent());
+ marker->SetParentCoordCtxProvider(aContext);
+}
+
+void SVGMarkerFrame::AppendDirectlyOwnedAnonBoxes(
+ nsTArray<OwnedAnonBox>& 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<SVGElement*>(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