mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1049256, part 1 - Convert SVG hit-testing to act on an SVG user space point instead of calling nsSVGUtils::GetCanvasTM(). r=longsonr
This commit is contained in:
parent
37d322f441
commit
5b5a18e51d
@ -3113,8 +3113,12 @@ nsDisplaySVGText::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
|
||||
SVGTextFrame *frame = static_cast<SVGTextFrame*>(mFrame);
|
||||
nsPoint pointRelativeToReferenceFrame = aRect.Center();
|
||||
// ToReferenceFrame() includes frame->GetPosition(), our user space position.
|
||||
nsPoint userSpacePt = pointRelativeToReferenceFrame -
|
||||
(ToReferenceFrame() - frame->GetPosition());
|
||||
nsPoint userSpacePtInAppUnits = pointRelativeToReferenceFrame -
|
||||
(ToReferenceFrame() - frame->GetPosition());
|
||||
|
||||
gfxPoint userSpacePt =
|
||||
gfxPoint(userSpacePtInAppUnits.x, userSpacePtInAppUnits.y) /
|
||||
frame->PresContext()->AppUnitsPerCSSPixel();
|
||||
|
||||
nsIFrame* target = frame->GetFrameForPoint(userSpacePt);
|
||||
if (target) {
|
||||
@ -3690,7 +3694,7 @@ SVGTextFrame::PaintSVG(nsRenderingContext* aContext,
|
||||
}
|
||||
|
||||
nsIFrame*
|
||||
SVGTextFrame::GetFrameForPoint(const nsPoint& aPoint)
|
||||
SVGTextFrame::GetFrameForPoint(const gfxPoint& aPoint)
|
||||
{
|
||||
NS_ASSERTION(GetFirstPrincipalChild(), "must have a child frame");
|
||||
|
||||
@ -3713,9 +3717,6 @@ SVGTextFrame::GetFrameForPoint(const nsPoint& aPoint)
|
||||
|
||||
nsPresContext* presContext = PresContext();
|
||||
|
||||
gfxPoint pointInOuterSVGUserUnits =
|
||||
gfxPoint(aPoint.x, aPoint.y) / PresContext()->AppUnitsPerCSSPixel();
|
||||
|
||||
// Ideally we'd iterate backwards so that we can just return the first frame
|
||||
// that is under aPoint. In practice this will rarely matter though since it
|
||||
// is rare for text in/under an SVG <text> element to overlap (i.e. the first
|
||||
@ -3729,13 +3730,12 @@ SVGTextFrame::GetFrameForPoint(const nsPoint& aPoint)
|
||||
continue;
|
||||
}
|
||||
|
||||
gfxMatrix m = run.GetTransformFromRunUserSpaceToUserSpace(presContext) *
|
||||
GetCanvasTM(FOR_HIT_TESTING);
|
||||
gfxMatrix m = run.GetTransformFromRunUserSpaceToUserSpace(presContext);
|
||||
if (!m.Invert()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
gfxPoint pointInRunUserSpace = m.Transform(pointInOuterSVGUserUnits);
|
||||
gfxPoint pointInRunUserSpace = m.Transform(aPoint);
|
||||
gfxRect frameRect =
|
||||
run.GetRunUserSpaceRect(presContext, TextRenderedRun::eIncludeFill |
|
||||
TextRenderedRun::eIncludeStroke).ToThebesRect();
|
||||
|
@ -319,7 +319,7 @@ public:
|
||||
virtual nsresult PaintSVG(nsRenderingContext* aContext,
|
||||
const nsIntRect* aDirtyRect,
|
||||
nsIFrame* aTransformRoot = nullptr) MOZ_OVERRIDE;
|
||||
virtual nsIFrame* GetFrameForPoint(const nsPoint& aPoint) MOZ_OVERRIDE;
|
||||
virtual nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) MOZ_OVERRIDE;
|
||||
virtual void ReflowSVG() MOZ_OVERRIDE;
|
||||
virtual nsRect GetCoveredRegion() MOZ_OVERRIDE;
|
||||
virtual SVGBBox GetBBoxContribution(const Matrix& aToBBoxUserspace,
|
||||
|
@ -62,11 +62,13 @@ public:
|
||||
const nsIntRect *aDirtyRect,
|
||||
nsIFrame* aTransformRoot = nullptr) = 0;
|
||||
|
||||
// Check if this frame or children contain the given point,
|
||||
// specified in app units relative to the origin of the outer
|
||||
// svg frame (origin ill-defined in the case of borders - bug
|
||||
// 290770). See bug 290852 for foreignObject complications.
|
||||
virtual nsIFrame* GetFrameForPoint(const nsPoint &aPoint)=0;
|
||||
/**
|
||||
* Returns the frame that should handle pointer events at aPoint. aPoint is
|
||||
* expected to be in the SVG user space of the frame on which this method is
|
||||
* called. The frame returned may be the frame on which this method is
|
||||
* called, any of its descendants or else nullptr.
|
||||
*/
|
||||
virtual nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) = 0;
|
||||
|
||||
// Get bounds in our nsSVGOuterSVGFrame's coordinates space (in app units)
|
||||
virtual nsRect GetCoveredRegion()=0;
|
||||
|
@ -157,9 +157,8 @@ nsSVGClipPathFrame::ClipPaint(nsRenderingContext* aContext,
|
||||
}
|
||||
|
||||
bool
|
||||
nsSVGClipPathFrame::ClipHitTest(nsIFrame* aParent,
|
||||
const gfxMatrix &aMatrix,
|
||||
const nsPoint &aPoint)
|
||||
nsSVGClipPathFrame::PointIsInsideClipPath(nsIFrame* aClippedFrame,
|
||||
const gfxPoint &aPoint)
|
||||
{
|
||||
// If the flag is set when we get here, it means this clipPath frame
|
||||
// has already been used in hit testing against the current clip,
|
||||
@ -170,29 +169,40 @@ nsSVGClipPathFrame::ClipHitTest(nsIFrame* aParent,
|
||||
}
|
||||
AutoClipPathReferencer clipRef(this);
|
||||
|
||||
mClipParent = aParent;
|
||||
if (mClipParentMatrix) {
|
||||
*mClipParentMatrix = aMatrix;
|
||||
} else {
|
||||
mClipParentMatrix = new gfxMatrix(aMatrix);
|
||||
gfxMatrix matrix = GetClipPathTransform(aClippedFrame);
|
||||
if (!matrix.Invert()) {
|
||||
return false;
|
||||
}
|
||||
gfxPoint point = matrix.Transform(aPoint);
|
||||
|
||||
// clipPath elements can themselves be clipped by a different clip path. In
|
||||
// that case the other clip path further clips away the element that is being
|
||||
// clipped by the original clipPath. If this clipPath is being clipped by a
|
||||
// different clip path we need to check if it prevents the original element
|
||||
// from recieving events at aPoint:
|
||||
nsSVGClipPathFrame *clipPathFrame =
|
||||
nsSVGEffects::GetEffectProperties(this).GetClipPathFrame(nullptr);
|
||||
if (clipPathFrame && !clipPathFrame->ClipHitTest(aParent, aMatrix, aPoint))
|
||||
if (clipPathFrame &&
|
||||
!clipPathFrame->PointIsInsideClipPath(aClippedFrame, aPoint)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (nsIFrame* kid = mFrames.FirstChild(); kid;
|
||||
kid = kid->GetNextSibling()) {
|
||||
nsISVGChildFrame* SVGFrame = do_QueryFrame(kid);
|
||||
if (SVGFrame) {
|
||||
// Notify the child frame that we may be working with a
|
||||
// different transform, so it can update its covered region
|
||||
// (used to shortcut hit testing).
|
||||
SVGFrame->NotifySVGChanged(nsISVGChildFrame::TRANSFORM_CHANGED);
|
||||
|
||||
if (SVGFrame->GetFrameForPoint(aPoint))
|
||||
gfxPoint pointForChild = point;
|
||||
gfxMatrix m = static_cast<nsSVGElement*>(kid->GetContent())->
|
||||
PrependLocalTransformsTo(gfxMatrix(), nsSVGElement::eUserSpaceToParent);
|
||||
if (!m.IsIdentity()) {
|
||||
if (!m.Invert()) {
|
||||
return false;
|
||||
}
|
||||
pointForChild = m.Transform(point);
|
||||
}
|
||||
if (SVGFrame->GetFrameForPoint(pointForChild)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@ -326,6 +336,19 @@ nsSVGClipPathFrame::GetCanvasTM(uint32_t aFor, nsIFrame* aTransformRoot)
|
||||
mClipParent);
|
||||
}
|
||||
|
||||
gfxMatrix
|
||||
nsSVGClipPathFrame::GetClipPathTransform(nsIFrame* aClippedFrame)
|
||||
{
|
||||
SVGClipPathElement *content = static_cast<SVGClipPathElement*>(mContent);
|
||||
|
||||
gfxMatrix tm = content->PrependLocalTransformsTo(gfxMatrix());
|
||||
|
||||
nsSVGEnum* clipPathUnits =
|
||||
&content->mEnumAttributes[SVGClipPathElement::CLIPPATHUNITS];
|
||||
|
||||
return nsSVGUtils::AdjustMatrixForUnits(tm, clipPathUnits, aClippedFrame);
|
||||
}
|
||||
|
||||
SVGBBox
|
||||
nsSVGClipPathFrame::GetBBoxForClipPathFrame(const SVGBBox &aBBox,
|
||||
const gfxMatrix &aMatrix)
|
||||
|
@ -41,9 +41,10 @@ public:
|
||||
nsIFrame* aParent,
|
||||
const gfxMatrix &aMatrix);
|
||||
|
||||
bool ClipHitTest(nsIFrame* aParent,
|
||||
const gfxMatrix &aMatrix,
|
||||
const nsPoint &aPoint);
|
||||
/**
|
||||
* aPoint is expected to be in aClippedFrame's SVG user space.
|
||||
*/
|
||||
bool PointIsInsideClipPath(nsIFrame* aClippedFrame, const gfxPoint &aPoint);
|
||||
|
||||
// Check if this clipPath is made up of more than one geometry object.
|
||||
// If so, the clipping API in cairo isn't enough and we need to use
|
||||
@ -78,6 +79,14 @@ public:
|
||||
SVGBBox
|
||||
GetBBoxForClipPathFrame(const SVGBBox &aBBox, const gfxMatrix &aMatrix);
|
||||
|
||||
/**
|
||||
* If the clipPath element transforms its children due to
|
||||
* clipPathUnits="objectBoundingBox" being set on it and/or due to the
|
||||
* 'transform' attribute being set on it, this function returns the resulting
|
||||
* transform.
|
||||
*/
|
||||
gfxMatrix GetClipPathTransform(nsIFrame* aClippedFrame);
|
||||
|
||||
private:
|
||||
// A helper class to allow us to paint clip paths safely. The helper
|
||||
// automatically sets and clears the mInUse flag on the clip path frame
|
||||
|
@ -271,7 +271,7 @@ nsSVGDisplayContainerFrame::PaintSVG(nsRenderingContext* aContext,
|
||||
}
|
||||
|
||||
nsIFrame*
|
||||
nsSVGDisplayContainerFrame::GetFrameForPoint(const nsPoint &aPoint)
|
||||
nsSVGDisplayContainerFrame::GetFrameForPoint(const gfxPoint& aPoint)
|
||||
{
|
||||
NS_ASSERTION(!NS_SVGDisplayListHitTestingEnabled() ||
|
||||
(mState & NS_FRAME_IS_NONDISPLAY),
|
||||
|
@ -148,7 +148,7 @@ public:
|
||||
virtual nsresult PaintSVG(nsRenderingContext* aContext,
|
||||
const nsIntRect *aDirtyRect,
|
||||
nsIFrame* aTransformRoot = nullptr) MOZ_OVERRIDE;
|
||||
virtual nsIFrame* GetFrameForPoint(const nsPoint &aPoint) MOZ_OVERRIDE;
|
||||
virtual nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) MOZ_OVERRIDE;
|
||||
virtual nsRect GetCoveredRegion() MOZ_OVERRIDE;
|
||||
virtual void ReflowSVG() MOZ_OVERRIDE;
|
||||
virtual void NotifySVGChanged(uint32_t aFlags) MOZ_OVERRIDE;
|
||||
|
@ -280,7 +280,7 @@ nsSVGForeignObjectFrame::PaintSVG(nsRenderingContext *aContext,
|
||||
}
|
||||
|
||||
nsIFrame*
|
||||
nsSVGForeignObjectFrame::GetFrameForPoint(const nsPoint &aPoint)
|
||||
nsSVGForeignObjectFrame::GetFrameForPoint(const gfxPoint& aPoint)
|
||||
{
|
||||
NS_ASSERTION(!NS_SVGDisplayListHitTestingEnabled() ||
|
||||
(mState & NS_FRAME_IS_NONDISPLAY),
|
||||
@ -298,29 +298,18 @@ nsSVGForeignObjectFrame::GetFrameForPoint(const nsPoint &aPoint)
|
||||
static_cast<nsSVGElement*>(mContent)->
|
||||
GetAnimatedLengthValues(&x, &y, &width, &height, nullptr);
|
||||
|
||||
gfxMatrix tm = GetCanvasTM(FOR_HIT_TESTING);
|
||||
if (!tm.Invert()) {
|
||||
if (!gfxRect(x, y, width, height).Contains(aPoint) ||
|
||||
!nsSVGUtils::HitTestClip(this, aPoint)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Convert aPoint from app units in canvas space to user space:
|
||||
// Convert the point to app units relative to the top-left corner of the
|
||||
// viewport that's established by the foreignObject element:
|
||||
|
||||
gfxPoint pt = gfxPoint(aPoint.x, aPoint.y) / PresContext()->AppUnitsPerCSSPixel();
|
||||
pt = tm.Transform(pt);
|
||||
|
||||
if (!gfxRect(0.0f, 0.0f, width, height).Contains(pt))
|
||||
return nullptr;
|
||||
|
||||
// Convert pt to app units in *local* space:
|
||||
|
||||
pt = pt * nsPresContext::AppUnitsPerCSSPixel();
|
||||
gfxPoint pt = (aPoint + gfxPoint(x, y)) * nsPresContext::AppUnitsPerCSSPixel();
|
||||
nsPoint point = nsPoint(NSToIntRound(pt.x), NSToIntRound(pt.y));
|
||||
|
||||
nsIFrame *frame = nsLayoutUtils::GetFrameForPoint(kid, point);
|
||||
if (frame && nsSVGUtils::HitTestClip(this, aPoint))
|
||||
return frame;
|
||||
|
||||
return nullptr;
|
||||
return nsLayoutUtils::GetFrameForPoint(kid, point);
|
||||
}
|
||||
|
||||
nsRect
|
||||
|
@ -79,7 +79,7 @@ public:
|
||||
virtual nsresult PaintSVG(nsRenderingContext *aContext,
|
||||
const nsIntRect *aDirtyRect,
|
||||
nsIFrame* aTransformRoot = nullptr) MOZ_OVERRIDE;
|
||||
virtual nsIFrame* GetFrameForPoint(const nsPoint &aPoint) MOZ_OVERRIDE;
|
||||
virtual nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) MOZ_OVERRIDE;
|
||||
virtual nsRect GetCoveredRegion() MOZ_OVERRIDE;
|
||||
virtual void ReflowSVG() MOZ_OVERRIDE;
|
||||
virtual void NotifySVGChanged(uint32_t aFlags) MOZ_OVERRIDE;
|
||||
|
@ -65,7 +65,7 @@ public:
|
||||
virtual nsresult PaintSVG(nsRenderingContext *aContext,
|
||||
const nsIntRect *aDirtyRect,
|
||||
nsIFrame* aTransformRoot) MOZ_OVERRIDE;
|
||||
virtual nsIFrame* GetFrameForPoint(const nsPoint &aPoint) MOZ_OVERRIDE;
|
||||
virtual nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) MOZ_OVERRIDE;
|
||||
virtual void ReflowSVG() MOZ_OVERRIDE;
|
||||
|
||||
// nsSVGPathGeometryFrame methods:
|
||||
@ -418,13 +418,25 @@ nsSVGImageFrame::PaintSVG(nsRenderingContext *aContext,
|
||||
}
|
||||
|
||||
nsIFrame*
|
||||
nsSVGImageFrame::GetFrameForPoint(const nsPoint &aPoint)
|
||||
nsSVGImageFrame::GetFrameForPoint(const gfxPoint& aPoint)
|
||||
{
|
||||
Rect rect;
|
||||
SVGImageElement *element = static_cast<SVGImageElement*>(mContent);
|
||||
element->GetAnimatedLengthValues(&rect.x, &rect.y,
|
||||
&rect.width, &rect.height, nullptr);
|
||||
|
||||
if (!rect.Contains(ToPoint(aPoint))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Special case for raster images -- we only want to accept points that fall
|
||||
// in the underlying image's (transformed) native bounds. That region
|
||||
// doesn't necessarily map to our <image> element's [x,y,width,height]. So,
|
||||
// we have to look up the native image size & our image transform in order
|
||||
// to filter out points that fall outside that area.
|
||||
// in the underlying image's (scaled to fit) native bounds. That region
|
||||
// doesn't necessarily map to our <image> element's [x,y,width,height] if the
|
||||
// raster image's aspect ratio is being preserved. We have to look up the
|
||||
// native image size & our viewBox transform in order to filter out points
|
||||
// that fall outside that area. (This special case doesn't apply to vector
|
||||
// images because they don't limit their drawing to explicit "native
|
||||
// bounds" -- they have an infinite canvas on which to place content.)
|
||||
if (StyleDisplay()->IsScrollableOverflow() && mImageContainer) {
|
||||
if (mImageContainer->GetType() == imgIContainer::TYPE_RASTER) {
|
||||
int32_t nativeWidth, nativeHeight;
|
||||
@ -433,23 +445,19 @@ nsSVGImageFrame::GetFrameForPoint(const nsPoint &aPoint)
|
||||
nativeWidth == 0 || nativeHeight == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!nsSVGUtils::HitTestRect(
|
||||
GetRasterImageTransform(nativeWidth, nativeHeight,
|
||||
FOR_HIT_TESTING),
|
||||
0, 0, nativeWidth, nativeHeight,
|
||||
PresContext()->AppUnitsToFloatCSSPixels(aPoint.x),
|
||||
PresContext()->AppUnitsToFloatCSSPixels(aPoint.y))) {
|
||||
Matrix viewBoxTM =
|
||||
SVGContentUtils::GetViewBoxTransform(rect.width, rect.height,
|
||||
0, 0, nativeWidth, nativeHeight,
|
||||
element->mPreserveAspectRatio);
|
||||
if (!nsSVGUtils::HitTestRect(viewBoxTM,
|
||||
0, 0, nativeWidth, nativeHeight,
|
||||
aPoint.x - rect.x, aPoint.y - rect.y)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
// The special case above doesn't apply to vector images, because they
|
||||
// don't limit their drawing to explicit "native bounds" -- they have
|
||||
// an infinite canvas on which to place content. So it's reasonable to
|
||||
// just fall back on our <image> element's own bounds here.
|
||||
}
|
||||
|
||||
return nsSVGImageFrameBase::GetFrameForPoint(aPoint);
|
||||
return this;
|
||||
}
|
||||
|
||||
nsIAtom *
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "nsSVGInnerSVGFrame.h"
|
||||
|
||||
// Keep others in (case-insensitive) order:
|
||||
#include "gfx2DGlue.h"
|
||||
#include "gfxContext.h"
|
||||
#include "nsIFrame.h"
|
||||
#include "nsISVGChildFrame.h"
|
||||
@ -18,6 +19,7 @@
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
nsIFrame*
|
||||
NS_NewSVGInnerSVGFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
|
||||
@ -252,7 +254,7 @@ nsSVGInnerSVGFrame::AttributeChanged(int32_t aNameSpaceID,
|
||||
}
|
||||
|
||||
nsIFrame*
|
||||
nsSVGInnerSVGFrame::GetFrameForPoint(const nsPoint &aPoint)
|
||||
nsSVGInnerSVGFrame::GetFrameForPoint(const gfxPoint& aPoint)
|
||||
{
|
||||
NS_ASSERTION(!NS_SVGDisplayListHitTestingEnabled() ||
|
||||
(mState & NS_FRAME_IS_NONDISPLAY),
|
||||
@ -260,16 +262,11 @@ nsSVGInnerSVGFrame::GetFrameForPoint(const nsPoint &aPoint)
|
||||
"SVG should take this code path");
|
||||
|
||||
if (StyleDisplay()->IsScrollableOverflow()) {
|
||||
nsSVGElement *content = static_cast<nsSVGElement*>(mContent);
|
||||
nsSVGContainerFrame *parent = static_cast<nsSVGContainerFrame*>(GetParent());
|
||||
|
||||
float clipX, clipY, clipWidth, clipHeight;
|
||||
content->GetAnimatedLengthValues(&clipX, &clipY, &clipWidth, &clipHeight, nullptr);
|
||||
|
||||
if (!nsSVGUtils::HitTestRect(gfx::ToMatrix(parent->GetCanvasTM(FOR_HIT_TESTING)),
|
||||
clipX, clipY, clipWidth, clipHeight,
|
||||
PresContext()->AppUnitsToDevPixels(aPoint.x),
|
||||
PresContext()->AppUnitsToDevPixels(aPoint.y))) {
|
||||
Rect clip;
|
||||
static_cast<nsSVGElement*>(mContent)->
|
||||
GetAnimatedLengthValues(&clip.x, &clip.y,
|
||||
&clip.width, &clip.height, nullptr);
|
||||
if (!clip.Contains(ToPoint(aPoint))) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ public:
|
||||
virtual nsRect GetCoveredRegion() MOZ_OVERRIDE;
|
||||
virtual void ReflowSVG() MOZ_OVERRIDE;
|
||||
virtual void NotifySVGChanged(uint32_t aFlags) MOZ_OVERRIDE;
|
||||
virtual nsIFrame* GetFrameForPoint(const nsPoint &aPoint) MOZ_OVERRIDE;
|
||||
virtual nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) MOZ_OVERRIDE;
|
||||
|
||||
// nsSVGContainerFrame methods:
|
||||
virtual gfxMatrix GetCanvasTM(uint32_t aFor,
|
||||
|
@ -366,7 +366,9 @@ nsSVGIntegrationUtils::HitTestFrameForEffects(nsIFrame* aFrame, const nsPoint& a
|
||||
aFrame->GetOffsetTo(firstFrame) + GetOffsetToBoundingBox(firstFrame);
|
||||
}
|
||||
nsPoint pt = aPt + toUserSpace;
|
||||
return nsSVGUtils::HitTestClip(firstFrame, pt);
|
||||
gfxPoint userSpacePt =
|
||||
gfxPoint(pt.x, pt.y) / aFrame->PresContext()->AppUnitsPerCSSPixel();
|
||||
return nsSVGUtils::HitTestClip(firstFrame, userSpacePt);
|
||||
}
|
||||
|
||||
class RegularFramePaintCallback : public nsSVGFilterPaintCallback
|
||||
|
@ -521,20 +521,24 @@ nsDisplayOuterSVG::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
|
||||
HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
|
||||
{
|
||||
nsSVGOuterSVGFrame *outerSVGFrame = static_cast<nsSVGOuterSVGFrame*>(mFrame);
|
||||
nsRect rectAtOrigin = aRect - ToReferenceFrame();
|
||||
nsRect thisRect(nsPoint(0,0), outerSVGFrame->GetSize());
|
||||
if (!thisRect.Intersects(rectAtOrigin))
|
||||
return;
|
||||
|
||||
nsPoint rectCenter(rectAtOrigin.x + rectAtOrigin.width / 2,
|
||||
rectAtOrigin.y + rectAtOrigin.height / 2);
|
||||
nsPoint refFrameToContentBox =
|
||||
ToReferenceFrame() + outerSVGFrame->GetContentRectRelativeToSelf().TopLeft();
|
||||
|
||||
nsPoint pointRelativeToContentBox =
|
||||
nsPoint(aRect.x + aRect.width / 2, aRect.y + aRect.height / 2) -
|
||||
refFrameToContentBox;
|
||||
|
||||
gfxPoint svgViewportRelativePoint =
|
||||
gfxPoint(pointRelativeToContentBox.x, pointRelativeToContentBox.y) /
|
||||
outerSVGFrame->PresContext()->AppUnitsPerCSSPixel();
|
||||
|
||||
nsSVGOuterSVGAnonChildFrame *anonKid =
|
||||
static_cast<nsSVGOuterSVGAnonChildFrame*>(
|
||||
outerSVGFrame->GetFirstPrincipalChild());
|
||||
nsIFrame* frame = nsSVGUtils::HitTestChildren(
|
||||
anonKid, rectCenter + outerSVGFrame->GetPosition() -
|
||||
outerSVGFrame->GetContentRect().TopLeft());
|
||||
|
||||
nsIFrame* frame =
|
||||
nsSVGUtils::HitTestChildren(anonKid, svgViewportRelativePoint);
|
||||
if (frame) {
|
||||
aOutFrames->AppendElement(frame);
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsDisplayList.h"
|
||||
#include "nsGkAtoms.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
#include "nsRenderingContext.h"
|
||||
#include "nsSVGEffects.h"
|
||||
#include "nsSVGIntegrationUtils.h"
|
||||
@ -82,8 +83,11 @@ nsDisplaySVGPathGeometry::HitTest(nsDisplayListBuilder* aBuilder, const nsRect&
|
||||
nsSVGPathGeometryFrame *frame = static_cast<nsSVGPathGeometryFrame*>(mFrame);
|
||||
nsPoint pointRelativeToReferenceFrame = aRect.Center();
|
||||
// ToReferenceFrame() includes frame->GetPosition(), our user space position.
|
||||
nsPoint userSpacePt = pointRelativeToReferenceFrame -
|
||||
(ToReferenceFrame() - frame->GetPosition());
|
||||
nsPoint userSpacePtInAppUnits = pointRelativeToReferenceFrame -
|
||||
(ToReferenceFrame() - frame->GetPosition());
|
||||
gfxPoint userSpacePt =
|
||||
gfxPoint(userSpacePtInAppUnits.x, userSpacePtInAppUnits.y) /
|
||||
frame->PresContext()->AppUnitsPerCSSPixel();
|
||||
if (frame->GetFrameForPoint(userSpacePt)) {
|
||||
aOutFrames->AppendElement(frame);
|
||||
}
|
||||
@ -236,12 +240,8 @@ nsSVGPathGeometryFrame::PaintSVG(nsRenderingContext *aContext,
|
||||
}
|
||||
|
||||
nsIFrame*
|
||||
nsSVGPathGeometryFrame::GetFrameForPoint(const nsPoint &aPoint)
|
||||
nsSVGPathGeometryFrame::GetFrameForPoint(const gfxPoint& aPoint)
|
||||
{
|
||||
gfxMatrix hitTestingTM = GetCanvasTM(FOR_HIT_TESTING);
|
||||
if (hitTestingTM.IsSingular()) {
|
||||
return nullptr;
|
||||
}
|
||||
FillRule fillRule;
|
||||
uint16_t hitTestFlags;
|
||||
if (GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD) {
|
||||
@ -250,12 +250,16 @@ nsSVGPathGeometryFrame::GetFrameForPoint(const nsPoint &aPoint)
|
||||
? FillRule::FILL_WINDING : FillRule::FILL_EVEN_ODD;
|
||||
} else {
|
||||
hitTestFlags = GetHitTestFlags();
|
||||
nsPoint point =
|
||||
nsSVGUtils::TransformOuterSVGPointToChildFrame(aPoint, hitTestingTM, PresContext());
|
||||
if (!hitTestFlags || ((hitTestFlags & SVG_HIT_TEST_CHECK_MRECT) &&
|
||||
!mRect.Contains(point))) {
|
||||
if (!hitTestFlags) {
|
||||
return nullptr;
|
||||
}
|
||||
if (hitTestFlags & SVG_HIT_TEST_CHECK_MRECT) {
|
||||
gfxRect rect =
|
||||
nsLayoutUtils::RectToGfxRect(mRect, PresContext()->AppUnitsPerCSSPixel());
|
||||
if (!rect.Contains(aPoint)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
fillRule = StyleSVG()->mFillRule == NS_STYLE_FILL_RULE_NONZERO
|
||||
? FillRule::FILL_WINDING : FillRule::FILL_EVEN_ODD;
|
||||
}
|
||||
@ -277,26 +281,14 @@ nsSVGPathGeometryFrame::GetFrameForPoint(const nsPoint &aPoint)
|
||||
return nullptr; // no path, so we don't paint anything that can be hit
|
||||
}
|
||||
|
||||
if (!hitTestingTM.IsIdentity()) {
|
||||
// We'll only get here if we don't have a nsDisplayItem that has called us
|
||||
// (for example, if we're a NS_FRAME_IS_NONDISPLAY frame under a clipPath).
|
||||
RefPtr<PathBuilder> builder =
|
||||
path->TransformedCopyToBuilder(ToMatrix(hitTestingTM), fillRule);
|
||||
path = builder->Finish();
|
||||
}
|
||||
|
||||
int32_t appUnitsPerCSSPx = PresContext()->AppUnitsPerCSSPixel();
|
||||
Point userSpacePoint = Point(Float(aPoint.x) / appUnitsPerCSSPx,
|
||||
Float(aPoint.y) / appUnitsPerCSSPx);
|
||||
|
||||
if (hitTestFlags & SVG_HIT_TEST_FILL) {
|
||||
isHit = path->ContainsPoint(userSpacePoint, Matrix());
|
||||
isHit = path->ContainsPoint(ToPoint(aPoint), Matrix());
|
||||
}
|
||||
if (!isHit && (hitTestFlags & SVG_HIT_TEST_STROKE)) {
|
||||
Point point = ToPoint(aPoint);
|
||||
SVGContentUtils::AutoStrokeOptions stroke;
|
||||
SVGContentUtils::GetStrokeOptions(&stroke, content, StyleContext(), nullptr);
|
||||
Matrix nonScalingStrokeMatrix =
|
||||
ToMatrix(nsSVGUtils::GetStrokeTransform(this));
|
||||
Matrix nonScalingStrokeMatrix = ToMatrix(nsSVGUtils::GetStrokeTransform(this));
|
||||
if (!nonScalingStrokeMatrix.IsIdentity()) {
|
||||
// We need to transform the path back into the appropriate ancestor
|
||||
// coordinate system in order for non-scaled stroke to be correct.
|
||||
@ -305,12 +297,12 @@ nsSVGPathGeometryFrame::GetFrameForPoint(const nsPoint &aPoint)
|
||||
if (!nonScalingStrokeMatrix.Invert()) {
|
||||
return nullptr;
|
||||
}
|
||||
userSpacePoint = ToMatrix(hitTestingTM) * nonScalingStrokeMatrix * userSpacePoint;
|
||||
point = nonScalingStrokeMatrix * point;
|
||||
RefPtr<PathBuilder> builder =
|
||||
path->TransformedCopyToBuilder(nonScalingStrokeMatrix, fillRule);
|
||||
path = builder->Finish();
|
||||
}
|
||||
isHit = path->StrokeContainsPoint(stroke, userSpacePoint, Matrix());
|
||||
isHit = path->StrokeContainsPoint(stroke, point, Matrix());
|
||||
}
|
||||
|
||||
if (isHit && nsSVGUtils::HitTestClip(this, aPoint))
|
||||
|
@ -96,7 +96,7 @@ protected:
|
||||
virtual nsresult PaintSVG(nsRenderingContext *aContext,
|
||||
const nsIntRect *aDirtyRect,
|
||||
nsIFrame* aTransformRoot = nullptr) MOZ_OVERRIDE;
|
||||
virtual nsIFrame* GetFrameForPoint(const nsPoint &aPoint) MOZ_OVERRIDE;
|
||||
virtual nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) MOZ_OVERRIDE;
|
||||
virtual nsRect GetCoveredRegion() MOZ_OVERRIDE;
|
||||
virtual void ReflowSVG() MOZ_OVERRIDE;
|
||||
virtual void NotifySVGChanged(uint32_t aFlags) MOZ_OVERRIDE;
|
||||
|
@ -55,7 +55,7 @@ public:
|
||||
virtual nsresult PaintSVG(nsRenderingContext* aContext,
|
||||
const nsIntRect *aDirtyRect,
|
||||
nsIFrame* aTransformRoot) MOZ_OVERRIDE;
|
||||
nsIFrame* GetFrameForPoint(const nsPoint &aPoint) MOZ_OVERRIDE;
|
||||
nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) MOZ_OVERRIDE;
|
||||
nsRect GetCoveredRegion() MOZ_OVERRIDE;
|
||||
virtual void ReflowSVG() MOZ_OVERRIDE;
|
||||
virtual SVGBBox GetBBoxContribution(const Matrix &aToBBoxUserspace,
|
||||
@ -128,7 +128,7 @@ nsSVGSwitchFrame::PaintSVG(nsRenderingContext* aContext,
|
||||
|
||||
|
||||
nsIFrame*
|
||||
nsSVGSwitchFrame::GetFrameForPoint(const nsPoint &aPoint)
|
||||
nsSVGSwitchFrame::GetFrameForPoint(const gfxPoint& aPoint)
|
||||
{
|
||||
NS_ASSERTION(!NS_SVGDisplayListHitTestingEnabled() ||
|
||||
(mState & NS_FRAME_IS_NONDISPLAY),
|
||||
@ -138,7 +138,20 @@ nsSVGSwitchFrame::GetFrameForPoint(const nsPoint &aPoint)
|
||||
nsIFrame *kid = GetActiveChildFrame();
|
||||
nsISVGChildFrame* svgFrame = do_QueryFrame(kid);
|
||||
if (svgFrame) {
|
||||
return svgFrame->GetFrameForPoint(aPoint);
|
||||
// Transform the point from our SVG user space to our child's.
|
||||
gfxPoint point = aPoint;
|
||||
gfxMatrix m =
|
||||
static_cast<const nsSVGElement*>(mContent)->
|
||||
PrependLocalTransformsTo(gfxMatrix(), nsSVGElement::eChildToUserSpace);
|
||||
m = static_cast<const nsSVGElement*>(kid->GetContent())->
|
||||
PrependLocalTransformsTo(m, nsSVGElement::eUserSpaceToParent);
|
||||
if (!m.IsIdentity()) {
|
||||
if (!m.Invert()) {
|
||||
return nullptr;
|
||||
}
|
||||
point = m.Transform(point);
|
||||
}
|
||||
return svgFrame->GetFrameForPoint(point);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
|
@ -694,7 +694,7 @@ nsSVGUtils::PaintFrameWithEffects(nsRenderingContext *aContext,
|
||||
}
|
||||
|
||||
bool
|
||||
nsSVGUtils::HitTestClip(nsIFrame *aFrame, const nsPoint &aPoint)
|
||||
nsSVGUtils::HitTestClip(nsIFrame *aFrame, const gfxPoint &aPoint)
|
||||
{
|
||||
nsSVGEffects::EffectProperties props =
|
||||
nsSVGEffects::GetEffectProperties(aFrame);
|
||||
@ -713,13 +713,28 @@ nsSVGUtils::HitTestClip(nsIFrame *aFrame, const nsPoint &aPoint)
|
||||
return true;
|
||||
}
|
||||
|
||||
return clipPathFrame->ClipHitTest(aFrame, GetCanvasTM(aFrame,
|
||||
nsISVGChildFrame::FOR_HIT_TESTING), aPoint);
|
||||
return clipPathFrame->PointIsInsideClipPath(aFrame, aPoint);
|
||||
}
|
||||
|
||||
nsIFrame *
|
||||
nsSVGUtils::HitTestChildren(nsIFrame *aFrame, const nsPoint &aPoint)
|
||||
nsSVGUtils::HitTestChildren(nsSVGDisplayContainerFrame* aFrame,
|
||||
const gfxPoint& aPoint)
|
||||
{
|
||||
// First we transform aPoint into the coordinate space established by aFrame
|
||||
// for its children (e.g. take account of any 'viewBox' attribute):
|
||||
gfxPoint point = aPoint;
|
||||
if (aFrame->GetContent()->IsSVG()) { // must check before cast
|
||||
gfxMatrix m = static_cast<const nsSVGElement*>(aFrame->GetContent())->
|
||||
PrependLocalTransformsTo(gfxMatrix(),
|
||||
nsSVGElement::eChildToUserSpace);
|
||||
if (!m.IsIdentity()) {
|
||||
if (!m.Invert()) {
|
||||
return nullptr;
|
||||
}
|
||||
point = m.Transform(point);
|
||||
}
|
||||
}
|
||||
|
||||
// Traverse the list in reverse order, so that if we get a hit we know that's
|
||||
// the topmost frame that intersects the point; then we can just return it.
|
||||
nsIFrame* result = nullptr;
|
||||
@ -733,7 +748,21 @@ nsSVGUtils::HitTestChildren(nsIFrame *aFrame, const nsPoint &aPoint)
|
||||
!static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
|
||||
continue;
|
||||
}
|
||||
result = SVGFrame->GetFrameForPoint(aPoint);
|
||||
// GetFrameForPoint() expects a point in its frame's SVG user space, so
|
||||
// we need to convert to that space:
|
||||
gfxPoint p = point;
|
||||
if (content->IsSVG()) { // must check before cast
|
||||
gfxMatrix m = static_cast<const nsSVGElement*>(content)->
|
||||
PrependLocalTransformsTo(gfxMatrix(),
|
||||
nsSVGElement::eUserSpaceToParent);
|
||||
if (!m.IsIdentity()) {
|
||||
if (!m.Invert()) {
|
||||
continue;
|
||||
}
|
||||
p = m.Transform(p);
|
||||
}
|
||||
}
|
||||
result = SVGFrame->GetFrameForPoint(p);
|
||||
if (result)
|
||||
break;
|
||||
}
|
||||
|
@ -304,12 +304,15 @@ public:
|
||||
/* Hit testing - check if point hits the clipPath of indicated
|
||||
* frame. Returns true if no clipPath set. */
|
||||
static bool
|
||||
HitTestClip(nsIFrame *aFrame, const nsPoint &aPoint);
|
||||
HitTestClip(nsIFrame *aFrame, const gfxPoint &aPoint);
|
||||
|
||||
/* Hit testing - check if point hits any children of frame. */
|
||||
|
||||
/**
|
||||
* Hit testing - check if point hits any children of aFrame. aPoint is
|
||||
* expected to be in the coordinate space established by aFrame for its
|
||||
* children (e.g. the space established by the 'viewBox' attribute on <svg>).
|
||||
*/
|
||||
static nsIFrame *
|
||||
HitTestChildren(nsIFrame *aFrame, const nsPoint &aPoint);
|
||||
HitTestChildren(nsSVGDisplayContainerFrame *aFrame, const gfxPoint &aPoint);
|
||||
|
||||
/*
|
||||
* Returns the CanvasTM of the indicated frame, whether it's a
|
||||
|
Loading…
Reference in New Issue
Block a user