gecko-dev/layout/svg/nsSVGMarkerFrame.cpp
cku d85d3ab378 Bug 1351440 - Part 2. Encapsulate DrawResult and imgIContainer::FLAG_* into imgDrawingParams, and pass it to PaintSVG. r=jwatt
The DrawResult return was not in fact anything to do with the success or
failure of that method, but was actually passing out a very specific piece of information
about the success or failure of any imagelib drawing that may not have occurred
under the various PaintSVG calls.

The signature of PaintSVG is changed from
  DrawResult PaintSVG(...., uint32 flags);
to
  void PaintSVG(...., imgDrawingParams& aPackage);

imgDrawingParams wraps DrawResult and imgIContainer::FLAG_* as a pack, pass through
PaintSVG to imagelib draw calls under beneath.

MozReview-Commit-ID: IOq2evUAOQF
2017-05-18 22:03:41 +02:00

277 lines
8.5 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "nsSVGMarkerFrame.h"
// Keep others in (case-insensitive) order:
#include "gfxContext.h"
#include "nsSVGEffects.h"
#include "mozilla/dom/SVGMarkerElement.h"
#include "SVGGeometryElement.h"
#include "SVGGeometryFrame.h"
using namespace mozilla::dom;
using namespace mozilla::gfx;
using namespace mozilla::image;
nsContainerFrame*
NS_NewSVGMarkerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
{
return new (aPresShell) nsSVGMarkerFrame(aContext);
}
NS_IMPL_FRAMEARENA_HELPERS(nsSVGMarkerFrame)
//----------------------------------------------------------------------
// nsIFrame methods:
nsresult
nsSVGMarkerFrame::AttributeChanged(int32_t aNameSpaceID,
nsIAtom* 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)) {
nsSVGEffects::InvalidateDirectRenderingObservers(this);
}
return nsSVGContainerFrame::AttributeChanged(aNameSpaceID,
aAttribute, aModType);
}
#ifdef DEBUG
void
nsSVGMarkerFrame::Init(nsIContent* aContent,
nsContainerFrame* aParent,
nsIFrame* aPrevInFlow)
{
NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::marker), "Content is not an SVG marker");
nsSVGContainerFrame::Init(aContent, aParent, aPrevInFlow);
}
#endif /* DEBUG */
//----------------------------------------------------------------------
// nsSVGContainerFrame methods:
gfxMatrix
nsSVGMarkerFrame::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*>(mContent);
mInUse2 = true;
gfxMatrix markedTM = mMarkedFrame->GetCanvasTM();
mInUse2 = false;
Matrix markerTM = content->GetMarkerTransform(mStrokeWidth, mX, mY,
mAutoAngle, mIsStart);
Matrix viewBoxTM = content->GetViewBoxTransform();
return ThebesMatrix(viewBoxTM * markerTM) * 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
nsSVGMarkerFrame::PaintMark(gfxContext& aContext,
const gfxMatrix& aToMarkedFrameUserSpace,
SVGGeometryFrame *aMarkedFrame,
nsSVGMark *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*>(mContent);
if (!marker->HasValidDimensions()) {
return;
}
const nsSVGViewBoxRect viewBox = marker->GetViewBoxRect();
if (viewBox.width <= 0.0f || viewBox.height <= 0.0f) {
// We must disable rendering if the viewBox width or height are zero.
return;
}
mStrokeWidth = aStrokeWidth;
mX = aMark->x;
mY = aMark->y;
mAutoAngle = aMark->angle;
mIsStart = aMark->type == nsSVGMark::eStart;
Matrix viewBoxTM = marker->GetViewBoxTransform();
Matrix markerTM = marker->GetMarkerTransform(mStrokeWidth, mX, mY,
mAutoAngle, mIsStart);
gfxMatrix markTM = ThebesMatrix(viewBoxTM) * ThebesMatrix(markerTM) *
aToMarkedFrameUserSpace;
if (StyleDisplay()->IsScrollableOverflow()) {
aContext.Save();
gfxRect clipRect =
nsSVGUtils::GetClipRectForFrame(this, viewBox.x, viewBox.y,
viewBox.width, viewBox.height);
nsSVGUtils::SetClipRect(&aContext, markTM, clipRect);
}
nsIFrame* kid = GetAnonymousChildFrame(this);
nsSVGDisplayableFrame* SVGFrame = do_QueryFrame(kid);
// The CTM of each frame referencing us may be different.
SVGFrame->NotifySVGChanged(nsSVGDisplayableFrame::TRANSFORM_CHANGED);
nsSVGUtils::PaintFrameWithEffects(kid, aContext, markTM, aImgParams);
if (StyleDisplay()->IsScrollableOverflow())
aContext.Restore();
}
SVGBBox
nsSVGMarkerFrame::GetMarkBBoxContribution(const Matrix &aToBBoxUserspace,
uint32_t aFlags,
SVGGeometryFrame *aMarkedFrame,
const nsSVGMark *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*>(mContent);
if (!content->HasValidDimensions()) {
return bbox;
}
const nsSVGViewBoxRect viewBox = content->GetViewBoxRect();
if (viewBox.width <= 0.0f || viewBox.height <= 0.0f) {
return bbox;
}
mStrokeWidth = aStrokeWidth;
mX = aMark->x;
mY = aMark->y;
mAutoAngle = aMark->angle;
mIsStart = aMark->type == nsSVGMark::eStart;
Matrix markerTM =
content->GetMarkerTransform(mStrokeWidth, mX, mY, mAutoAngle, mIsStart);
Matrix viewBoxTM = content->GetViewBoxTransform();
Matrix tm = viewBoxTM * markerTM * aToBBoxUserspace;
nsSVGDisplayableFrame* 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
nsSVGMarkerFrame::SetParentCoordCtxProvider(SVGSVGElement *aContext)
{
SVGMarkerElement *marker = static_cast<SVGMarkerElement*>(mContent);
marker->SetParentCoordCtxProvider(aContext);
}
void
nsSVGMarkerFrame::DoUpdateStyleOfOwnedAnonBoxes(
mozilla::ServoStyleSet& aStyleSet,
nsStyleChangeList& aChangeList,
nsChangeHint aHintForThisFrame)
{
UpdateStyleOfChildAnonBox(GetAnonymousChildFrame(this), aStyleSet,
aChangeList, aHintForThisFrame);
}
//----------------------------------------------------------------------
// helper class
nsSVGMarkerFrame::AutoMarkerReferencer::AutoMarkerReferencer(
nsSVGMarkerFrame *aFrame,
SVGGeometryFrame *aMarkedFrame
MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
: mFrame(aFrame)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
mFrame->mInUse = true;
mFrame->mMarkedFrame = aMarkedFrame;
SVGSVGElement *ctx =
static_cast<nsSVGElement*>(aMarkedFrame->GetContent())->GetCtx();
mFrame->SetParentCoordCtxProvider(ctx);
}
nsSVGMarkerFrame::AutoMarkerReferencer::~AutoMarkerReferencer()
{
mFrame->SetParentCoordCtxProvider(nullptr);
mFrame->mMarkedFrame = nullptr;
mFrame->mInUse = false;
}
//----------------------------------------------------------------------
// Implementation of nsSVGMarkerAnonChildFrame
nsContainerFrame*
NS_NewSVGMarkerAnonChildFrame(nsIPresShell* aPresShell,
nsStyleContext* aContext)
{
return new (aPresShell) nsSVGMarkerAnonChildFrame(aContext);
}
NS_IMPL_FRAMEARENA_HELPERS(nsSVGMarkerAnonChildFrame)
#ifdef DEBUG
void
nsSVGMarkerAnonChildFrame::Init(nsIContent* aContent,
nsContainerFrame* aParent,
nsIFrame* aPrevInFlow)
{
MOZ_ASSERT(aParent->IsSVGMarkerFrame(), "Unexpected parent");
nsSVGDisplayContainerFrame::Init(aContent, aParent, aPrevInFlow);
}
#endif