2017-10-27 20:33:53 +03:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
2012-05-21 15:12:37 +04:00
|
|
|
/* 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/. */
|
2005-04-05 01:42:26 +04:00
|
|
|
|
2012-03-26 15:58:59 +04:00
|
|
|
// Main header first:
|
|
|
|
// This is also necessary to ensure our definition of M_SQRT1_2 is picked up
|
2009-04-03 00:29:31 +04:00
|
|
|
#include "nsSVGUtils.h"
|
2013-01-15 16:22:03 +04:00
|
|
|
#include <algorithm>
|
2012-03-20 16:15:55 +04:00
|
|
|
|
2012-03-26 15:58:59 +04:00
|
|
|
// Keep others in (case-insensitive) order:
|
2013-11-18 18:29:51 +04:00
|
|
|
#include "gfx2DGlue.h"
|
2012-03-26 15:58:59 +04:00
|
|
|
#include "gfxContext.h"
|
|
|
|
#include "gfxMatrix.h"
|
|
|
|
#include "gfxPlatform.h"
|
|
|
|
#include "gfxRect.h"
|
|
|
|
#include "gfxUtils.h"
|
|
|
|
#include "mozilla/gfx/2D.h"
|
2014-09-29 17:26:15 +04:00
|
|
|
#include "mozilla/gfx/PatternHelpers.h"
|
2012-03-26 15:58:59 +04:00
|
|
|
#include "mozilla/Preferences.h"
|
2016-07-22 14:07:39 +03:00
|
|
|
#include "mozilla/SVGContextPaint.h"
|
2016-02-09 01:08:09 +03:00
|
|
|
#include "nsCSSClipPathInstance.h"
|
2012-05-17 08:05:09 +04:00
|
|
|
#include "nsCSSFrameConstructor.h"
|
2012-06-24 16:59:26 +04:00
|
|
|
#include "nsDisplayList.h"
|
2014-02-24 19:22:58 +04:00
|
|
|
#include "nsFilterInstance.h"
|
2012-03-26 15:58:59 +04:00
|
|
|
#include "nsFrameList.h"
|
|
|
|
#include "nsGkAtoms.h"
|
2005-04-05 01:42:26 +04:00
|
|
|
#include "nsIContent.h"
|
2005-08-05 00:32:02 +04:00
|
|
|
#include "nsIDocument.h"
|
|
|
|
#include "nsIFrame.h"
|
|
|
|
#include "nsIPresShell.h"
|
2017-02-09 21:24:31 +03:00
|
|
|
#include "nsSVGDisplayableFrame.h"
|
2014-10-20 13:55:48 +04:00
|
|
|
#include "nsLayoutUtils.h"
|
2012-03-26 15:58:59 +04:00
|
|
|
#include "nsPresContext.h"
|
|
|
|
#include "nsStyleCoord.h"
|
|
|
|
#include "nsStyleStruct.h"
|
2006-03-21 18:49:20 +03:00
|
|
|
#include "nsSVGClipPathFrame.h"
|
2006-06-01 19:31:15 +04:00
|
|
|
#include "nsSVGContainerFrame.h"
|
2017-08-30 16:14:46 +03:00
|
|
|
#include "SVGObserverUtils.h"
|
2008-09-11 04:24:16 +04:00
|
|
|
#include "nsSVGFilterPaintCallback.h"
|
2012-03-26 15:58:59 +04:00
|
|
|
#include "nsSVGForeignObjectFrame.h"
|
|
|
|
#include "nsSVGInnerSVGFrame.h"
|
|
|
|
#include "nsSVGIntegrationUtils.h"
|
|
|
|
#include "nsSVGLength2.h"
|
|
|
|
#include "nsSVGMaskFrame.h"
|
|
|
|
#include "nsSVGOuterSVGFrame.h"
|
2014-05-13 05:24:35 +04:00
|
|
|
#include "mozilla/dom/SVGClipPathElement.h"
|
2013-01-13 02:22:31 +04:00
|
|
|
#include "mozilla/dom/SVGPathElement.h"
|
2018-02-18 18:53:13 +03:00
|
|
|
#include "mozilla/dom/SVGUnitTypesBinding.h"
|
2016-12-18 14:11:47 +03:00
|
|
|
#include "SVGGeometryElement.h"
|
|
|
|
#include "SVGGeometryFrame.h"
|
2012-08-05 23:10:21 +04:00
|
|
|
#include "nsSVGPaintServerFrame.h"
|
2017-10-10 09:58:34 +03:00
|
|
|
#include "mozilla/dom/SVGViewportElement.h"
|
2012-08-10 15:13:44 +04:00
|
|
|
#include "nsTextFrame.h"
|
2012-09-22 23:26:05 +04:00
|
|
|
#include "SVGContentUtils.h"
|
2016-04-22 13:52:19 +03:00
|
|
|
#include "SVGTextFrame.h"
|
2016-08-23 07:09:32 +03:00
|
|
|
#include "mozilla/Unused.h"
|
2012-01-05 11:17:52 +04:00
|
|
|
|
2010-12-20 03:45:29 +03:00
|
|
|
using namespace mozilla;
|
2010-04-30 17:12:06 +04:00
|
|
|
using namespace mozilla::dom;
|
2018-06-26 00:20:54 +03:00
|
|
|
using namespace mozilla::dom::SVGUnitTypes_Binding;
|
2012-01-05 11:17:52 +04:00
|
|
|
using namespace mozilla::gfx;
|
2017-01-11 04:48:29 +03:00
|
|
|
using namespace mozilla::image;
|
2007-04-26 12:36:16 +04:00
|
|
|
|
2012-05-17 08:05:04 +04:00
|
|
|
static bool sSVGDisplayListHitTestingEnabled;
|
|
|
|
static bool sSVGDisplayListPaintingEnabled;
|
2014-05-13 05:24:35 +04:00
|
|
|
static bool sSVGNewGetBBoxEnabled;
|
2009-03-10 04:20:17 +03:00
|
|
|
|
2012-05-17 08:05:04 +04:00
|
|
|
bool NS_SVGDisplayListHitTestingEnabled() {
|
|
|
|
return sSVGDisplayListHitTestingEnabled;
|
|
|
|
}
|
2009-03-10 04:20:17 +03:00
|
|
|
|
2012-05-17 08:05:04 +04:00
|
|
|
bool NS_SVGDisplayListPaintingEnabled() {
|
|
|
|
return sSVGDisplayListPaintingEnabled;
|
2009-03-10 04:20:17 +03:00
|
|
|
}
|
|
|
|
|
2014-05-13 05:24:35 +04:00
|
|
|
bool NS_SVGNewGetBBoxEnabled() { return sSVGNewGetBBoxEnabled; }
|
|
|
|
|
2012-03-02 12:28:59 +04:00
|
|
|
// we only take the address of this:
|
|
|
|
static mozilla::gfx::UserDataKey sSVGAutoRenderStateKey;
|
|
|
|
|
2014-10-11 12:24:44 +04:00
|
|
|
SVGAutoRenderState::SVGAutoRenderState(
|
2013-12-26 22:49:49 +04:00
|
|
|
DrawTarget* aDrawTarget MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
|
2014-09-30 21:08:13 +04:00
|
|
|
: mDrawTarget(aDrawTarget),
|
2012-07-30 18:20:58 +04:00
|
|
|
mOriginalRenderState(nullptr),
|
2012-03-02 12:28:59 +04:00
|
|
|
mPaintingToWindow(false) {
|
2013-12-26 22:49:49 +04:00
|
|
|
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
2014-09-30 21:08:13 +04:00
|
|
|
mOriginalRenderState = aDrawTarget->RemoveUserData(&sSVGAutoRenderStateKey);
|
2012-03-02 12:28:59 +04:00
|
|
|
// We always remove ourselves from aContext before it dies, so
|
2012-07-30 18:20:58 +04:00
|
|
|
// passing nullptr as the destroy function is okay.
|
2014-09-30 21:08:13 +04:00
|
|
|
aDrawTarget->AddUserData(&sSVGAutoRenderStateKey, this, nullptr);
|
2012-03-02 12:28:59 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
SVGAutoRenderState::~SVGAutoRenderState() {
|
2014-09-30 21:08:13 +04:00
|
|
|
mDrawTarget->RemoveUserData(&sSVGAutoRenderStateKey);
|
2012-03-02 12:28:59 +04:00
|
|
|
if (mOriginalRenderState) {
|
2014-09-30 21:08:13 +04:00
|
|
|
mDrawTarget->AddUserData(&sSVGAutoRenderStateKey, mOriginalRenderState,
|
|
|
|
nullptr);
|
2012-03-02 12:28:59 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SVGAutoRenderState::SetPaintingToWindow(bool aPaintingToWindow) {
|
|
|
|
mPaintingToWindow = aPaintingToWindow;
|
|
|
|
}
|
|
|
|
|
2014-09-30 21:08:13 +04:00
|
|
|
/* static */ bool SVGAutoRenderState::IsPaintingToWindow(
|
|
|
|
DrawTarget* aDrawTarget) {
|
|
|
|
void* state = aDrawTarget->GetUserData(&sSVGAutoRenderStateKey);
|
2012-03-02 12:28:59 +04:00
|
|
|
if (state) {
|
|
|
|
return static_cast<SVGAutoRenderState*>(state)->mPaintingToWindow;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-05-17 08:05:04 +04:00
|
|
|
void nsSVGUtils::Init() {
|
|
|
|
Preferences::AddBoolVarCache(&sSVGDisplayListHitTestingEnabled,
|
|
|
|
"svg.display-lists.hit-testing.enabled");
|
|
|
|
|
|
|
|
Preferences::AddBoolVarCache(&sSVGDisplayListPaintingEnabled,
|
|
|
|
"svg.display-lists.painting.enabled");
|
2014-05-13 05:24:35 +04:00
|
|
|
|
|
|
|
Preferences::AddBoolVarCache(&sSVGNewGetBBoxEnabled,
|
|
|
|
"svg.new-getBBox.enabled");
|
2012-05-17 08:05:04 +04:00
|
|
|
}
|
|
|
|
|
2012-05-17 08:05:09 +04:00
|
|
|
nsRect nsSVGUtils::GetPostFilterVisualOverflowRect(
|
2012-06-17 00:23:48 +04:00
|
|
|
nsIFrame* aFrame, const nsRect& aPreFilterRect) {
|
2015-02-10 01:34:50 +03:00
|
|
|
MOZ_ASSERT(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT,
|
|
|
|
"Called on invalid frame type");
|
2012-05-17 08:05:09 +04:00
|
|
|
|
2018-08-29 21:11:43 +03:00
|
|
|
// Note: we do not return here for eHasNoRefs since we must still handle any
|
|
|
|
// CSS filter functions.
|
|
|
|
// TODO: We currently pass nullptr instead of an nsTArray* here, but we
|
|
|
|
// actually should get the filter frames and then pass them into
|
|
|
|
// GetPostFilterBounds below! See bug 1494263.
|
|
|
|
// TODO: we should really return an empty rect for eHasRefsSomeInvalid since
|
|
|
|
// in that case we disable painting of the element.
|
|
|
|
if (!aFrame->StyleEffects()->HasFilters() ||
|
|
|
|
SVGObserverUtils::GetAndObserveFilters(aFrame, nullptr) ==
|
|
|
|
SVGObserverUtils::eHasRefsSomeInvalid) {
|
2012-06-17 00:23:48 +04:00
|
|
|
return aPreFilterRect;
|
2012-05-17 08:05:09 +04:00
|
|
|
}
|
|
|
|
|
2014-02-24 19:22:58 +04:00
|
|
|
return nsFilterInstance::GetPostFilterBounds(aFrame, nullptr,
|
|
|
|
&aPreFilterRect);
|
2012-05-17 08:05:09 +04:00
|
|
|
}
|
|
|
|
|
2012-07-22 04:01:44 +04:00
|
|
|
bool nsSVGUtils::OuterSVGIsCallingReflowSVG(nsIFrame* aFrame) {
|
2012-09-22 23:26:05 +04:00
|
|
|
return GetOuterSVGFrame(aFrame)->IsCallingReflowSVG();
|
2012-03-20 16:15:53 +04:00
|
|
|
}
|
|
|
|
|
2013-06-03 18:15:29 +04:00
|
|
|
bool nsSVGUtils::AnyOuterSVGIsCallingReflowSVG(nsIFrame* aFrame) {
|
|
|
|
nsSVGOuterSVGFrame* outer = GetOuterSVGFrame(aFrame);
|
|
|
|
do {
|
|
|
|
if (outer->IsCallingReflowSVG()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
outer = GetOuterSVGFrame(outer->GetParent());
|
|
|
|
} while (outer);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-07-22 04:01:44 +04:00
|
|
|
void nsSVGUtils::ScheduleReflowSVG(nsIFrame* aFrame) {
|
2015-02-10 01:34:50 +03:00
|
|
|
MOZ_ASSERT(aFrame->IsFrameOfType(nsIFrame::eSVG), "Passed bad frame!");
|
2008-04-08 16:51:19 +04:00
|
|
|
|
2012-03-20 16:15:53 +04:00
|
|
|
// If this is triggered, the callers should be fixed to call us before
|
2012-07-22 04:01:44 +04:00
|
|
|
// ReflowSVG is called. If we try to mark dirty bits on frames while we're
|
2012-03-20 16:15:53 +04:00
|
|
|
// in the process of removing them, things will get messed up.
|
2012-07-22 04:01:44 +04:00
|
|
|
NS_ASSERTION(!OuterSVGIsCallingReflowSVG(aFrame),
|
2017-02-09 21:24:31 +03:00
|
|
|
"Do not call under nsSVGDisplayableFrame::ReflowSVG!");
|
2012-03-20 16:15:53 +04:00
|
|
|
|
2017-08-30 17:58:31 +03:00
|
|
|
// We don't call SVGObserverUtils::InvalidateRenderingObservers here because
|
2012-08-27 11:51:48 +04:00
|
|
|
// we should only be called under InvalidateAndScheduleReflowSVG (which
|
2012-03-20 16:15:53 +04:00
|
|
|
// calls InvalidateBounds) or nsSVGDisplayContainerFrame::InsertFrames
|
|
|
|
// (at which point the frame has no observers).
|
2008-10-01 04:51:05 +04:00
|
|
|
|
2013-07-12 11:13:07 +04:00
|
|
|
if (aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) {
|
2008-04-08 16:51:19 +04:00
|
|
|
return;
|
2012-02-04 17:58:46 +04:00
|
|
|
}
|
|
|
|
|
2012-03-20 16:15:53 +04:00
|
|
|
if (aFrame->GetStateBits() & (NS_FRAME_IS_DIRTY | NS_FRAME_FIRST_REFLOW)) {
|
|
|
|
// Nothing to do if we're already dirty, or if the outer-<svg>
|
|
|
|
// hasn't yet had its initial reflow.
|
2012-02-04 17:58:46 +04:00
|
|
|
return;
|
2012-03-20 16:15:53 +04:00
|
|
|
}
|
|
|
|
|
2012-07-30 18:20:58 +04:00
|
|
|
nsSVGOuterSVGFrame* outerSVGFrame = nullptr;
|
2012-03-20 16:15:53 +04:00
|
|
|
|
|
|
|
// We must not add dirty bits to the nsSVGOuterSVGFrame or else
|
|
|
|
// PresShell::FrameNeedsReflow won't work when we pass it in below.
|
|
|
|
if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG) {
|
|
|
|
outerSVGFrame = static_cast<nsSVGOuterSVGFrame*>(aFrame);
|
|
|
|
} else {
|
|
|
|
aFrame->AddStateBits(NS_FRAME_IS_DIRTY);
|
|
|
|
|
|
|
|
nsIFrame* f = aFrame->GetParent();
|
|
|
|
while (f && !(f->GetStateBits() & NS_STATE_IS_OUTER_SVG)) {
|
|
|
|
if (f->GetStateBits() &
|
|
|
|
(NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
f->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
|
|
|
|
f = f->GetParent();
|
2015-02-10 01:34:50 +03:00
|
|
|
MOZ_ASSERT(f->IsFrameOfType(nsIFrame::eSVG),
|
|
|
|
"NS_STATE_IS_OUTER_SVG check above not valid!");
|
2012-03-20 16:15:53 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
outerSVGFrame = static_cast<nsSVGOuterSVGFrame*>(f);
|
|
|
|
|
2017-04-30 18:30:08 +03:00
|
|
|
MOZ_ASSERT(outerSVGFrame && outerSVGFrame->IsSVGOuterSVGFrame(),
|
2015-02-10 01:34:50 +03:00
|
|
|
"Did not find nsSVGOuterSVGFrame!");
|
2012-03-20 16:15:53 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (outerSVGFrame->GetStateBits() & NS_FRAME_IN_REFLOW) {
|
|
|
|
// We're currently under an nsSVGOuterSVGFrame::Reflow call so there is no
|
|
|
|
// need to call PresShell::FrameNeedsReflow, since we have an
|
|
|
|
// nsSVGOuterSVGFrame::DidReflow call pending.
|
2008-04-08 16:51:19 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-03-20 16:15:53 +04:00
|
|
|
nsFrameState dirtyBit =
|
|
|
|
(outerSVGFrame == aFrame ? NS_FRAME_IS_DIRTY
|
|
|
|
: NS_FRAME_HAS_DIRTY_CHILDREN);
|
2008-04-08 16:51:19 +04:00
|
|
|
|
2017-11-09 05:00:48 +03:00
|
|
|
aFrame->PresShell()->FrameNeedsReflow(outerSVGFrame, nsIPresShell::eResize,
|
2012-03-20 16:15:53 +04:00
|
|
|
dirtyBit);
|
|
|
|
}
|
2012-02-04 17:58:46 +04:00
|
|
|
|
2012-07-22 04:01:44 +04:00
|
|
|
bool nsSVGUtils::NeedsReflowSVG(nsIFrame* aFrame) {
|
2015-02-10 01:34:50 +03:00
|
|
|
MOZ_ASSERT(aFrame->IsFrameOfType(nsIFrame::eSVG),
|
|
|
|
"SVG uses bits differently!");
|
2012-03-20 16:15:53 +04:00
|
|
|
|
|
|
|
// The flags we test here may change, hence why we have this separate
|
|
|
|
// function.
|
|
|
|
return NS_SUBTREE_DIRTY(aFrame);
|
2008-04-08 16:51:19 +04:00
|
|
|
}
|
|
|
|
|
2015-05-25 00:40:37 +03:00
|
|
|
Size nsSVGUtils::GetContextSize(const nsIFrame* aFrame) {
|
|
|
|
Size size;
|
|
|
|
|
|
|
|
MOZ_ASSERT(aFrame->GetContent()->IsSVGElement(), "bad cast");
|
|
|
|
const nsSVGElement* element =
|
|
|
|
static_cast<nsSVGElement*>(aFrame->GetContent());
|
|
|
|
|
2017-10-10 09:58:34 +03:00
|
|
|
SVGViewportElement* ctx = element->GetCtx();
|
2015-05-25 00:40:37 +03:00
|
|
|
if (ctx) {
|
|
|
|
size.width = ctx->GetLength(SVGContentUtils::X);
|
|
|
|
size.height = ctx->GetLength(SVGContentUtils::Y);
|
|
|
|
}
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2009-06-11 19:21:03 +04:00
|
|
|
float nsSVGUtils::ObjectSpace(const gfxRect& aRect,
|
|
|
|
const nsSVGLength2* aLength) {
|
2012-07-01 00:21:31 +04:00
|
|
|
float axis;
|
2005-09-07 02:30:40 +04:00
|
|
|
|
2006-04-14 19:09:39 +04:00
|
|
|
switch (aLength->GetCtxType()) {
|
2012-09-22 23:26:05 +04:00
|
|
|
case SVGContentUtils::X:
|
2009-06-11 19:21:03 +04:00
|
|
|
axis = aRect.Width();
|
2005-09-07 02:30:40 +04:00
|
|
|
break;
|
2012-09-22 23:26:05 +04:00
|
|
|
case SVGContentUtils::Y:
|
2009-06-11 19:21:03 +04:00
|
|
|
axis = aRect.Height();
|
2005-09-07 02:30:40 +04:00
|
|
|
break;
|
2012-09-22 23:26:05 +04:00
|
|
|
case SVGContentUtils::XY:
|
|
|
|
axis = float(SVGContentUtils::ComputeNormalizedHypotenuse(
|
|
|
|
aRect.Width(), aRect.Height()));
|
2009-12-13 09:27:30 +03:00
|
|
|
break;
|
|
|
|
default:
|
2018-06-18 08:43:11 +03:00
|
|
|
MOZ_ASSERT_UNREACHABLE("unexpected ctx type");
|
2009-12-13 09:27:30 +03:00
|
|
|
axis = 0.0f;
|
|
|
|
break;
|
2005-09-07 02:30:40 +04:00
|
|
|
}
|
2008-01-25 12:27:03 +03:00
|
|
|
if (aLength->IsPercentage()) {
|
2012-07-01 00:21:31 +04:00
|
|
|
// Multiply first to avoid precision errors:
|
|
|
|
return axis * aLength->GetAnimValInSpecifiedUnits() / 100;
|
2009-12-13 09:27:30 +03:00
|
|
|
}
|
2017-10-10 09:58:34 +03:00
|
|
|
return aLength->GetAnimValue(static_cast<SVGViewportElement*>(nullptr)) *
|
|
|
|
axis;
|
2005-09-07 02:30:40 +04:00
|
|
|
}
|
|
|
|
|
2008-09-11 04:24:16 +04:00
|
|
|
float nsSVGUtils::UserSpace(nsSVGElement* aSVGElement,
|
|
|
|
const nsSVGLength2* aLength) {
|
2006-04-14 19:09:39 +04:00
|
|
|
return aLength->GetAnimValue(aSVGElement);
|
2005-09-07 02:30:40 +04:00
|
|
|
}
|
|
|
|
|
2008-09-11 04:24:16 +04:00
|
|
|
float nsSVGUtils::UserSpace(nsIFrame* aNonSVGContext,
|
|
|
|
const nsSVGLength2* aLength) {
|
|
|
|
return aLength->GetAnimValue(aNonSVGContext);
|
|
|
|
}
|
|
|
|
|
2014-09-15 14:12:50 +04:00
|
|
|
float nsSVGUtils::UserSpace(const UserSpaceMetrics& aMetrics,
|
|
|
|
const nsSVGLength2* aLength) {
|
|
|
|
return aLength->GetAnimValue(aMetrics);
|
|
|
|
}
|
|
|
|
|
2006-02-14 00:22:41 +03:00
|
|
|
nsSVGOuterSVGFrame* nsSVGUtils::GetOuterSVGFrame(nsIFrame* aFrame) {
|
|
|
|
while (aFrame) {
|
|
|
|
if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG) {
|
2007-07-08 11:08:04 +04:00
|
|
|
return static_cast<nsSVGOuterSVGFrame*>(aFrame);
|
2006-02-14 00:22:41 +03:00
|
|
|
}
|
|
|
|
aFrame = aFrame->GetParent();
|
|
|
|
}
|
|
|
|
|
2012-07-30 18:20:58 +04:00
|
|
|
return nullptr;
|
2006-02-14 00:22:41 +03:00
|
|
|
}
|
2006-02-26 20:58:58 +03:00
|
|
|
|
2007-05-31 02:32:54 +04:00
|
|
|
nsIFrame* nsSVGUtils::GetOuterSVGFrameAndCoveredRegion(nsIFrame* aFrame,
|
|
|
|
nsRect* aRect) {
|
2017-02-09 21:24:31 +03:00
|
|
|
nsSVGDisplayableFrame* svg = do_QueryFrame(aFrame);
|
2012-07-30 18:20:58 +04:00
|
|
|
if (!svg) return nullptr;
|
2014-07-06 16:55:43 +04:00
|
|
|
nsSVGOuterSVGFrame* outer = GetOuterSVGFrame(aFrame);
|
|
|
|
if (outer == svg) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
2017-02-27 14:43:16 +03:00
|
|
|
|
|
|
|
if (aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) {
|
|
|
|
*aRect = nsRect(0, 0, 0, 0);
|
|
|
|
} else {
|
|
|
|
uint32_t flags =
|
|
|
|
nsSVGUtils::eForGetClientRects | nsSVGUtils::eBBoxIncludeFill |
|
|
|
|
nsSVGUtils::eBBoxIncludeStroke | nsSVGUtils::eBBoxIncludeMarkers;
|
|
|
|
gfxMatrix m = nsSVGUtils::GetUserToCanvasTM(aFrame);
|
|
|
|
SVGBBox bbox = nsSVGUtils::GetBBox(aFrame, flags, &m);
|
|
|
|
nsRect bounds = nsLayoutUtils::RoundGfxRectToAppRect(
|
|
|
|
bbox, aFrame->PresContext()->AppUnitsPerDevPixel());
|
|
|
|
nsMargin bp = outer->GetUsedBorderAndPadding();
|
|
|
|
*aRect = bounds + nsPoint(bp.left, bp.top);
|
|
|
|
}
|
|
|
|
|
2014-07-06 16:55:43 +04:00
|
|
|
return outer;
|
2007-05-31 02:32:54 +04:00
|
|
|
}
|
|
|
|
|
2014-09-08 15:28:50 +04:00
|
|
|
gfxMatrix nsSVGUtils::GetCanvasTM(nsIFrame* aFrame) {
|
2009-04-29 08:31:34 +04:00
|
|
|
// XXX yuck, we really need a common interface for GetCanvasTM
|
|
|
|
|
|
|
|
if (!aFrame->IsFrameOfType(nsIFrame::eSVG)) {
|
2017-01-24 18:22:43 +03:00
|
|
|
return GetCSSPxToDevPxMatrix(aFrame);
|
2009-04-29 08:31:34 +04:00
|
|
|
}
|
2008-09-11 04:24:16 +04:00
|
|
|
|
2017-05-01 20:32:52 +03:00
|
|
|
LayoutFrameType type = aFrame->Type();
|
|
|
|
if (type == LayoutFrameType::SVGForeignObject) {
|
2014-09-08 15:28:50 +04:00
|
|
|
return static_cast<nsSVGForeignObjectFrame*>(aFrame)->GetCanvasTM();
|
2009-04-29 08:31:34 +04:00
|
|
|
}
|
2017-05-01 20:32:52 +03:00
|
|
|
if (type == LayoutFrameType::SVGOuterSVG) {
|
2017-01-24 18:22:43 +03:00
|
|
|
return GetCSSPxToDevPxMatrix(aFrame);
|
2012-07-17 23:44:39 +04:00
|
|
|
}
|
2009-04-29 08:31:34 +04:00
|
|
|
|
|
|
|
nsSVGContainerFrame* containerFrame = do_QueryFrame(aFrame);
|
|
|
|
if (containerFrame) {
|
2014-09-08 15:28:50 +04:00
|
|
|
return containerFrame->GetCanvasTM();
|
2006-03-21 18:49:20 +03:00
|
|
|
}
|
|
|
|
|
2016-12-18 14:11:47 +03:00
|
|
|
return static_cast<SVGGeometryFrame*>(aFrame)->GetCanvasTM();
|
2006-03-21 18:49:20 +03:00
|
|
|
}
|
|
|
|
|
2014-09-08 15:28:50 +04:00
|
|
|
gfxMatrix nsSVGUtils::GetUserToCanvasTM(nsIFrame* aFrame) {
|
2017-02-09 21:24:31 +03:00
|
|
|
nsSVGDisplayableFrame* svgFrame = do_QueryFrame(aFrame);
|
2012-06-26 14:49:23 +04:00
|
|
|
NS_ASSERTION(svgFrame, "bad frame");
|
|
|
|
|
2013-12-27 00:13:57 +04:00
|
|
|
gfxMatrix tm;
|
2012-06-26 14:49:23 +04:00
|
|
|
if (svgFrame) {
|
|
|
|
nsSVGElement* content = static_cast<nsSVGElement*>(aFrame->GetContent());
|
2012-06-30 15:20:46 +04:00
|
|
|
tm = content->PrependLocalTransformsTo(GetCanvasTM(aFrame->GetParent()),
|
2015-12-03 01:36:23 +03:00
|
|
|
eUserSpaceToParent);
|
2012-06-26 14:49:23 +04:00
|
|
|
}
|
2013-12-27 00:13:57 +04:00
|
|
|
return tm;
|
2012-06-26 14:49:23 +04:00
|
|
|
}
|
|
|
|
|
2012-08-22 19:56:38 +04:00
|
|
|
void nsSVGUtils::NotifyChildrenOfSVGChange(nsIFrame* aFrame, uint32_t aFlags) {
|
2016-01-29 17:42:15 +03:00
|
|
|
for (nsIFrame* kid : aFrame->PrincipalChildList()) {
|
2017-02-09 21:24:31 +03:00
|
|
|
nsSVGDisplayableFrame* SVGFrame = do_QueryFrame(kid);
|
2007-12-20 17:26:34 +03:00
|
|
|
if (SVGFrame) {
|
2017-03-23 10:29:11 +03:00
|
|
|
SVGFrame->NotifySVGChanged(aFlags);
|
2007-12-20 17:26:34 +03:00
|
|
|
} else {
|
2017-03-23 10:29:11 +03:00
|
|
|
NS_ASSERTION(kid->IsFrameOfType(nsIFrame::eSVG) ||
|
|
|
|
nsSVGUtils::IsInSVGTextSubtree(kid),
|
2012-08-23 05:23:23 +04:00
|
|
|
"SVG frame expected");
|
2007-12-20 17:26:34 +03:00
|
|
|
// recurse into the children of container frames e.g. <clipPath>, <mask>
|
|
|
|
// in case they have child frames with transformation matrices
|
2012-08-23 05:23:23 +04:00
|
|
|
if (kid->IsFrameOfType(nsIFrame::eSVG)) {
|
|
|
|
NotifyChildrenOfSVGChange(kid, aFlags);
|
|
|
|
}
|
2007-12-20 17:26:34 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-03-21 18:49:20 +03:00
|
|
|
// ************************************************************
|
|
|
|
|
2008-09-11 04:24:16 +04:00
|
|
|
class SVGPaintCallback : public nsSVGFilterPaintCallback {
|
|
|
|
public:
|
2017-05-18 23:03:41 +03:00
|
|
|
virtual void Paint(gfxContext& aContext, nsIFrame* aTarget,
|
|
|
|
const gfxMatrix& aTransform, const nsIntRect* aDirtyRect,
|
|
|
|
imgDrawingParams& aImgParams) override {
|
2017-02-09 21:24:31 +03:00
|
|
|
nsSVGDisplayableFrame* svgFrame = do_QueryFrame(aTarget);
|
|
|
|
NS_ASSERTION(svgFrame, "Expected SVG frame here");
|
2008-10-16 11:55:10 +04:00
|
|
|
|
2012-07-30 18:20:58 +04:00
|
|
|
nsIntRect* dirtyRect = nullptr;
|
2008-10-16 11:55:10 +04:00
|
|
|
nsIntRect tmpDirtyRect;
|
|
|
|
|
|
|
|
// aDirtyRect is in user-space pixels, we need to convert to
|
|
|
|
// outer-SVG-frame-relative device pixels.
|
|
|
|
if (aDirtyRect) {
|
2014-08-29 23:42:07 +04:00
|
|
|
gfxMatrix userToDeviceSpace = aTransform;
|
2009-04-29 08:31:34 +04:00
|
|
|
if (userToDeviceSpace.IsSingular()) {
|
2017-05-18 23:03:41 +03:00
|
|
|
return;
|
2009-04-29 08:31:34 +04:00
|
|
|
}
|
2017-07-05 18:22:00 +03:00
|
|
|
gfxRect dirtyBounds = userToDeviceSpace.TransformBounds(gfxRect(
|
|
|
|
aDirtyRect->x, aDirtyRect->y, aDirtyRect->width, aDirtyRect->height));
|
2008-10-16 11:55:10 +04:00
|
|
|
dirtyBounds.RoundOut();
|
2010-11-24 12:35:21 +03:00
|
|
|
if (gfxUtils::GfxRectToIntRect(dirtyBounds, &tmpDirtyRect)) {
|
2008-10-16 11:55:10 +04:00
|
|
|
dirtyRect = &tmpDirtyRect;
|
|
|
|
}
|
2008-09-11 04:24:16 +04:00
|
|
|
}
|
|
|
|
|
2017-05-18 23:03:41 +03:00
|
|
|
svgFrame->PaintSVG(aContext, nsSVGUtils::GetCSSPxToDevPxMatrix(aTarget),
|
|
|
|
aImgParams, dirtyRect);
|
2008-09-11 04:24:16 +04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-11-03 06:08:06 +03:00
|
|
|
float nsSVGUtils::ComputeOpacity(nsIFrame* aFrame, bool aHandleOpacity) {
|
|
|
|
float opacity = aFrame->StyleEffects()->mOpacity;
|
|
|
|
|
|
|
|
if (opacity != 1.0f &&
|
|
|
|
(nsSVGUtils::CanOptimizeOpacity(aFrame) || !aHandleOpacity)) {
|
|
|
|
return 1.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
return opacity;
|
|
|
|
}
|
|
|
|
|
|
|
|
void nsSVGUtils::DetermineMaskUsage(nsIFrame* aFrame, bool aHandleOpacity,
|
|
|
|
MaskUsage& aUsage) {
|
|
|
|
aUsage.opacity = ComputeOpacity(aFrame, aHandleOpacity);
|
|
|
|
|
|
|
|
nsIFrame* firstFrame =
|
|
|
|
nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
|
|
|
|
|
|
|
|
const nsStyleSVGReset* svgReset = firstFrame->StyleSVGReset();
|
|
|
|
|
2018-09-19 17:54:27 +03:00
|
|
|
nsTArray<nsSVGMaskFrame*> maskFrames;
|
|
|
|
// XXX check return value?
|
|
|
|
SVGObserverUtils::GetAndObserveMasks(firstFrame, &maskFrames);
|
2016-11-23 08:51:36 +03:00
|
|
|
aUsage.shouldGenerateMaskLayer = (maskFrames.Length() > 0);
|
2016-11-03 06:08:06 +03:00
|
|
|
|
2018-09-05 20:12:44 +03:00
|
|
|
nsSVGClipPathFrame* clipPathFrame;
|
|
|
|
// XXX check return value?
|
|
|
|
SVGObserverUtils::GetAndObserveClipPath(firstFrame, &clipPathFrame);
|
2017-06-07 08:27:17 +03:00
|
|
|
MOZ_ASSERT(!clipPathFrame ||
|
|
|
|
svgReset->mClipPath.GetType() == StyleShapeSourceType::URL);
|
2016-11-03 06:08:06 +03:00
|
|
|
|
|
|
|
switch (svgReset->mClipPath.GetType()) {
|
|
|
|
case StyleShapeSourceType::URL:
|
|
|
|
if (clipPathFrame) {
|
|
|
|
if (clipPathFrame->IsTrivial()) {
|
|
|
|
aUsage.shouldApplyClipPath = true;
|
|
|
|
} else {
|
|
|
|
aUsage.shouldGenerateClipMaskLayer = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case StyleShapeSourceType::Shape:
|
|
|
|
case StyleShapeSourceType::Box:
|
2018-08-15 22:27:38 +03:00
|
|
|
case StyleShapeSourceType::Path:
|
2018-08-18 01:48:02 +03:00
|
|
|
aUsage.shouldApplyBasicShapeOrPath = true;
|
2016-11-03 06:08:06 +03:00
|
|
|
break;
|
|
|
|
case StyleShapeSourceType::None:
|
|
|
|
MOZ_ASSERT(!aUsage.shouldGenerateClipMaskLayer &&
|
2018-08-18 01:48:02 +03:00
|
|
|
!aUsage.shouldApplyClipPath &&
|
|
|
|
!aUsage.shouldApplyBasicShapeOrPath);
|
2016-11-03 06:08:06 +03:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
MOZ_ASSERT_UNREACHABLE("Unsupported clip-path type.");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-16 07:16:20 +03:00
|
|
|
class MixModeBlender {
|
|
|
|
public:
|
|
|
|
typedef mozilla::gfx::Factory Factory;
|
2016-11-03 07:17:20 +03:00
|
|
|
|
2016-11-16 07:16:20 +03:00
|
|
|
MixModeBlender(nsIFrame* aFrame, gfxContext* aContext)
|
|
|
|
: mFrame(aFrame), mSourceCtx(aContext) {
|
|
|
|
MOZ_ASSERT(mFrame && mSourceCtx);
|
|
|
|
}
|
2016-11-03 07:17:20 +03:00
|
|
|
|
2016-11-16 07:16:20 +03:00
|
|
|
bool ShouldCreateDrawTargetForBlend() const {
|
|
|
|
return mFrame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL;
|
|
|
|
}
|
2016-11-03 07:17:20 +03:00
|
|
|
|
2016-11-16 07:16:20 +03:00
|
|
|
gfxContext* CreateBlendTarget(const gfxMatrix& aTransform) {
|
|
|
|
MOZ_ASSERT(ShouldCreateDrawTargetForBlend());
|
|
|
|
|
|
|
|
// Create a temporary context to draw to so we can blend it back with
|
|
|
|
// another operator.
|
|
|
|
IntRect drawRect = ComputeClipExtsInDeviceSpace(aTransform);
|
|
|
|
|
|
|
|
RefPtr<DrawTarget> targetDT =
|
|
|
|
mSourceCtx->GetDrawTarget()->CreateSimilarDrawTarget(
|
|
|
|
drawRect.Size(), SurfaceFormat::B8G8R8A8);
|
|
|
|
if (!targetDT || !targetDT->IsValid()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
MOZ_ASSERT(!mTargetCtx,
|
|
|
|
"CreateBlendTarget is designed to be used once only.");
|
|
|
|
|
|
|
|
mTargetCtx = gfxContext::CreateOrNull(targetDT);
|
|
|
|
MOZ_ASSERT(mTargetCtx); // already checked the draw target above
|
|
|
|
mTargetCtx->SetMatrix(mSourceCtx->CurrentMatrix() *
|
2017-11-11 05:14:09 +03:00
|
|
|
Matrix::Translation(-drawRect.TopLeft()));
|
2016-11-16 07:16:20 +03:00
|
|
|
|
|
|
|
mTargetOffset = drawRect.TopLeft();
|
|
|
|
|
|
|
|
return mTargetCtx;
|
2016-11-16 07:16:20 +03:00
|
|
|
}
|
2016-11-03 07:17:20 +03:00
|
|
|
|
2016-11-16 07:16:20 +03:00
|
|
|
void BlendToTarget() {
|
|
|
|
MOZ_ASSERT(ShouldCreateDrawTargetForBlend());
|
|
|
|
MOZ_ASSERT(mTargetCtx,
|
|
|
|
"BlendToTarget should be used after CreateBlendTarget.");
|
2016-11-03 07:17:20 +03:00
|
|
|
|
2016-11-16 07:16:20 +03:00
|
|
|
RefPtr<SourceSurface> targetSurf = mTargetCtx->GetDrawTarget()->Snapshot();
|
2016-11-16 07:16:20 +03:00
|
|
|
|
2016-11-16 07:16:20 +03:00
|
|
|
gfxContextAutoSaveRestore save(mSourceCtx);
|
2017-11-11 05:14:09 +03:00
|
|
|
mSourceCtx->SetMatrix(Matrix()); // This will be restored right after.
|
2016-11-16 07:16:20 +03:00
|
|
|
RefPtr<gfxPattern> pattern = new gfxPattern(
|
|
|
|
targetSurf, Matrix::Translation(mTargetOffset.x, mTargetOffset.y));
|
|
|
|
mSourceCtx->SetPattern(pattern);
|
|
|
|
mSourceCtx->Paint();
|
|
|
|
}
|
2016-11-16 07:16:20 +03:00
|
|
|
|
2016-11-16 07:16:20 +03:00
|
|
|
private:
|
|
|
|
MixModeBlender() = delete;
|
2016-11-16 07:16:20 +03:00
|
|
|
|
2016-11-16 07:16:20 +03:00
|
|
|
IntRect ComputeClipExtsInDeviceSpace(const gfxMatrix& aTransform) {
|
|
|
|
// These are used if we require a temporary surface for a custom blend
|
|
|
|
// mode. Clip the source context first, so that we can generate a smaller
|
|
|
|
// temporary surface. (Since we will clip this context in
|
|
|
|
// SetupContextMatrix, a pair of save/restore is needed.)
|
|
|
|
gfxContextAutoSaveRestore saver(mSourceCtx);
|
|
|
|
|
|
|
|
if (!(mFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
|
|
|
|
// aFrame has a valid visual overflow rect, so clip to it before calling
|
|
|
|
// PushGroup() to minimize the size of the surfaces we'll composite:
|
|
|
|
gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(mSourceCtx);
|
|
|
|
mSourceCtx->Multiply(aTransform);
|
|
|
|
nsRect overflowRect = mFrame->GetVisualOverflowRectRelativeToSelf();
|
|
|
|
if (mFrame->IsFrameOfType(nsIFrame::eSVGGeometry) ||
|
2017-03-23 10:29:11 +03:00
|
|
|
nsSVGUtils::IsInSVGTextSubtree(mFrame)) {
|
2016-11-16 07:16:20 +03:00
|
|
|
// Unlike containers, leaf frames do not include GetPosition() in
|
|
|
|
// GetCanvasTM().
|
|
|
|
overflowRect = overflowRect + mFrame->GetPosition();
|
|
|
|
}
|
|
|
|
mSourceCtx->Clip(NSRectToSnappedRect(
|
|
|
|
overflowRect, mFrame->PresContext()->AppUnitsPerDevPixel(),
|
|
|
|
*mSourceCtx->GetDrawTarget()));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the clip extents in device space.
|
2017-09-04 09:04:17 +03:00
|
|
|
gfxRect clippedFrameSurfaceRect =
|
|
|
|
mSourceCtx->GetClipExtents(gfxContext::eDeviceSpace);
|
2016-11-16 07:16:20 +03:00
|
|
|
clippedFrameSurfaceRect.RoundOut();
|
|
|
|
|
|
|
|
IntRect result;
|
|
|
|
ToRect(clippedFrameSurfaceRect).ToIntRect(&result);
|
|
|
|
|
|
|
|
return Factory::CheckSurfaceSize(result.Size()) ? result : IntRect();
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIFrame* mFrame;
|
|
|
|
gfxContext* mSourceCtx;
|
|
|
|
RefPtr<gfxContext> mTargetCtx;
|
|
|
|
IntPoint mTargetOffset;
|
|
|
|
};
|
2016-11-03 07:17:20 +03:00
|
|
|
|
2014-08-29 23:42:07 +04:00
|
|
|
void nsSVGUtils::PaintFrameWithEffects(nsIFrame* aFrame, gfxContext& aContext,
|
|
|
|
const gfxMatrix& aTransform,
|
2017-05-18 23:03:41 +03:00
|
|
|
imgDrawingParams& aImgParams,
|
|
|
|
const nsIntRect* aDirtyRect) {
|
2012-07-20 22:12:29 +04:00
|
|
|
NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
|
2013-07-12 11:13:07 +04:00
|
|
|
(aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) ||
|
2018-01-15 17:33:25 +03:00
|
|
|
aFrame->PresContext()->Document()->IsSVGGlyphsDocument(),
|
2012-07-20 22:12:29 +04:00
|
|
|
"If display lists are enabled, only painting of non-display "
|
|
|
|
"SVG should take this code path");
|
|
|
|
|
2017-02-09 21:24:31 +03:00
|
|
|
nsSVGDisplayableFrame* svgFrame = do_QueryFrame(aFrame);
|
|
|
|
if (!svgFrame) return;
|
2006-03-21 18:49:20 +03:00
|
|
|
|
2016-11-03 06:58:15 +03:00
|
|
|
MaskUsage maskUsage;
|
|
|
|
DetermineMaskUsage(aFrame, true, maskUsage);
|
|
|
|
if (maskUsage.opacity == 0.0f) {
|
2017-05-18 23:03:41 +03:00
|
|
|
return;
|
2016-11-03 06:58:15 +03:00
|
|
|
}
|
2006-03-21 18:49:20 +03:00
|
|
|
|
2012-03-03 13:21:09 +04:00
|
|
|
const nsIContent* content = aFrame->GetContent();
|
2015-03-03 14:08:59 +03:00
|
|
|
if (content->IsSVGElement() &&
|
2012-03-03 13:21:09 +04:00
|
|
|
!static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
|
2017-05-18 23:03:41 +03:00
|
|
|
return;
|
2012-03-03 13:21:09 +04:00
|
|
|
}
|
|
|
|
|
2013-07-12 11:13:07 +04:00
|
|
|
if (aDirtyRect && !(aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
|
2012-05-17 08:05:09 +04:00
|
|
|
// Here we convert aFrame's paint bounds to outer-<svg> device space,
|
|
|
|
// compare it to aDirtyRect, and return early if they don't intersect.
|
|
|
|
// We don't do this optimization for nondisplay SVG since nondisplay
|
|
|
|
// SVG doesn't maintain bounds/overflow rects.
|
|
|
|
nsRect overflowRect = aFrame->GetVisualOverflowRectRelativeToSelf();
|
2013-02-11 10:22:17 +04:00
|
|
|
if (aFrame->IsFrameOfType(nsIFrame::eSVGGeometry) ||
|
2017-03-23 10:29:11 +03:00
|
|
|
nsSVGUtils::IsInSVGTextSubtree(aFrame)) {
|
2012-05-17 08:05:09 +04:00
|
|
|
// Unlike containers, leaf frames do not include GetPosition() in
|
|
|
|
// GetCanvasTM().
|
|
|
|
overflowRect = overflowRect + aFrame->GetPosition();
|
|
|
|
}
|
2013-02-20 01:46:27 +04:00
|
|
|
int32_t appUnitsPerDevPx = aFrame->PresContext()->AppUnitsPerDevPixel();
|
2014-08-29 23:42:07 +04:00
|
|
|
gfxMatrix tm = aTransform;
|
2012-05-17 08:05:09 +04:00
|
|
|
if (aFrame->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer)) {
|
2013-12-30 03:35:53 +04:00
|
|
|
gfx::Matrix childrenOnlyTM;
|
2012-05-17 08:05:09 +04:00
|
|
|
if (static_cast<nsSVGContainerFrame*>(aFrame)->HasChildrenOnlyTransform(
|
|
|
|
&childrenOnlyTM)) {
|
|
|
|
// Undo the children-only transform:
|
2014-07-11 11:06:39 +04:00
|
|
|
if (!childrenOnlyTM.Invert()) {
|
2017-05-18 23:03:41 +03:00
|
|
|
return;
|
2014-07-11 11:06:39 +04:00
|
|
|
}
|
|
|
|
tm = ThebesMatrix(childrenOnlyTM) * tm;
|
2012-05-17 08:05:09 +04:00
|
|
|
}
|
|
|
|
}
|
2012-09-22 23:26:05 +04:00
|
|
|
nsIntRect bounds =
|
|
|
|
TransformFrameRectToOuterSVG(overflowRect, tm, aFrame->PresContext())
|
2012-05-17 08:05:09 +04:00
|
|
|
.ToOutsidePixels(appUnitsPerDevPx);
|
|
|
|
if (!aDirtyRect->Intersects(bounds)) {
|
2017-05-18 23:03:41 +03:00
|
|
|
return;
|
2006-06-30 23:19:42 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-03-21 18:49:20 +03:00
|
|
|
/* SVG defines the following rendering model:
|
|
|
|
*
|
2009-10-26 20:17:49 +03:00
|
|
|
* 1. Render fill
|
|
|
|
* 2. Render stroke
|
|
|
|
* 3. Render markers
|
|
|
|
* 4. Apply filter
|
|
|
|
* 5. Apply clipping, masking, group opacity
|
2006-03-21 18:49:20 +03:00
|
|
|
*
|
2007-04-26 12:36:16 +04:00
|
|
|
* We follow this, but perform a couple of optimizations:
|
2006-03-21 18:49:20 +03:00
|
|
|
*
|
|
|
|
* + Use cairo's clipPath when representable natively (single object
|
|
|
|
* clip region).
|
2016-11-03 06:08:06 +03:00
|
|
|
*f
|
2006-03-21 18:49:20 +03:00
|
|
|
* + Merge opacity and masking if both used together.
|
|
|
|
*/
|
|
|
|
|
2016-11-03 06:58:15 +03:00
|
|
|
/* Properties are added lazily and may have been removed by a restyle,
|
|
|
|
so make sure all applicable ones are set again. */
|
2018-09-05 20:12:44 +03:00
|
|
|
nsSVGClipPathFrame* clipPathFrame;
|
2018-09-19 17:54:27 +03:00
|
|
|
nsTArray<nsSVGMaskFrame*> maskFrames;
|
2018-08-29 21:11:43 +03:00
|
|
|
// TODO: We currently pass nullptr instead of an nsTArray* here, but we
|
|
|
|
// actually should get the filter frames and then pass them into
|
|
|
|
// PaintFilteredFrame below! See bug 1494263.
|
2018-09-19 17:54:27 +03:00
|
|
|
if (SVGObserverUtils::GetAndObserveFilters(aFrame, nullptr) ==
|
2018-09-05 20:12:44 +03:00
|
|
|
SVGObserverUtils::eHasRefsSomeInvalid ||
|
|
|
|
SVGObserverUtils::GetAndObserveClipPath(aFrame, &clipPathFrame) ==
|
2018-09-19 17:54:27 +03:00
|
|
|
SVGObserverUtils::eHasRefsSomeInvalid ||
|
|
|
|
SVGObserverUtils::GetAndObserveMasks(aFrame, &maskFrames) ==
|
2018-08-29 21:11:43 +03:00
|
|
|
SVGObserverUtils::eHasRefsSomeInvalid) {
|
2010-12-19 20:47:01 +03:00
|
|
|
// Some resource is invalid. We shouldn't paint anything.
|
2017-05-18 23:03:41 +03:00
|
|
|
return;
|
2008-08-06 04:25:33 +04:00
|
|
|
}
|
2007-04-26 12:36:16 +04:00
|
|
|
|
2018-09-19 17:54:27 +03:00
|
|
|
nsSVGMaskFrame* maskFrame = maskFrames.IsEmpty() ? nullptr : maskFrames[0];
|
2016-12-07 05:12:36 +03:00
|
|
|
|
2016-11-16 07:16:20 +03:00
|
|
|
MixModeBlender blender(aFrame, &aContext);
|
|
|
|
gfxContext* target = blender.ShouldCreateDrawTargetForBlend()
|
|
|
|
? blender.CreateBlendTarget(aTransform)
|
|
|
|
: &aContext;
|
2016-11-03 07:38:36 +03:00
|
|
|
|
|
|
|
if (!target) {
|
2017-05-18 23:03:41 +03:00
|
|
|
return;
|
2016-11-03 07:38:36 +03:00
|
|
|
}
|
2015-11-11 18:15:39 +03:00
|
|
|
|
2006-03-21 18:49:20 +03:00
|
|
|
/* Check if we need to do additional operations on this child's
|
|
|
|
* rendering, which necessitates rendering into another surface. */
|
2016-11-03 06:58:15 +03:00
|
|
|
bool shouldGenerateMask =
|
|
|
|
(maskUsage.opacity != 1.0f || maskUsage.shouldGenerateClipMaskLayer ||
|
2016-11-16 07:16:20 +03:00
|
|
|
maskUsage.shouldGenerateMaskLayer);
|
2016-12-07 06:09:11 +03:00
|
|
|
bool shouldPushMask = false;
|
2015-11-11 18:15:39 +03:00
|
|
|
|
2016-11-03 06:58:15 +03:00
|
|
|
if (shouldGenerateMask) {
|
2015-11-11 18:15:39 +03:00
|
|
|
Matrix maskTransform;
|
2016-11-03 06:58:15 +03:00
|
|
|
RefPtr<SourceSurface> maskSurface;
|
2015-11-11 18:15:39 +03:00
|
|
|
|
2016-12-07 06:09:11 +03:00
|
|
|
// maskFrame can be nullptr even if maskUsage.shouldGenerateMaskLayer is
|
|
|
|
// true. That happens when a user gives an unresolvable mask-id, such as
|
|
|
|
// mask:url()
|
|
|
|
// mask:url(#id-which-does-not-exist)
|
|
|
|
// Since we only uses nsSVGUtils with SVG elements, not like mask on an
|
|
|
|
// HTML element, we should treat an unresolvable mask as no-mask here.
|
|
|
|
if (maskUsage.shouldGenerateMaskLayer && maskFrame) {
|
2016-11-16 07:59:52 +03:00
|
|
|
uint8_t maskMode = aFrame->StyleSVGReset()->mMask.mLayers[0].mMaskMode;
|
|
|
|
nsSVGMaskFrame::MaskParams params(&aContext, aFrame, aTransform,
|
|
|
|
maskUsage.opacity, &maskTransform,
|
2017-05-18 23:03:45 +03:00
|
|
|
maskMode, aImgParams);
|
|
|
|
maskSurface = maskFrame->GetMaskForMaskedFrame(params);
|
2016-11-03 06:58:15 +03:00
|
|
|
|
|
|
|
if (!maskSurface) {
|
2016-11-16 07:59:52 +03:00
|
|
|
// Either entire surface is clipped out, or gfx buffer allocation
|
|
|
|
// failure in nsSVGMaskFrame::GetMaskForMaskedFrame.
|
2017-05-18 23:03:41 +03:00
|
|
|
return;
|
2016-11-03 06:58:15 +03:00
|
|
|
}
|
2016-12-07 06:09:11 +03:00
|
|
|
shouldPushMask = true;
|
2015-11-11 18:15:39 +03:00
|
|
|
}
|
|
|
|
|
2016-11-03 06:58:15 +03:00
|
|
|
if (maskUsage.shouldGenerateClipMaskLayer) {
|
2015-11-11 18:15:39 +03:00
|
|
|
Matrix clippedMaskTransform;
|
2017-05-18 23:03:45 +03:00
|
|
|
RefPtr<SourceSurface> clipMaskSurface = clipPathFrame->GetClipMask(
|
2016-11-03 06:58:15 +03:00
|
|
|
aContext, aFrame, aTransform, &clippedMaskTransform, maskSurface,
|
2016-11-23 19:08:13 +03:00
|
|
|
maskTransform);
|
2015-11-11 18:15:39 +03:00
|
|
|
if (clipMaskSurface) {
|
|
|
|
maskSurface = clipMaskSurface;
|
|
|
|
maskTransform = clippedMaskTransform;
|
2016-11-16 07:59:52 +03:00
|
|
|
} else {
|
|
|
|
// Either entire surface is clipped out, or gfx buffer allocation
|
|
|
|
// failure in nsSVGClipPathFrame::GetClipMask.
|
2017-05-18 23:03:41 +03:00
|
|
|
return;
|
2015-11-11 18:15:39 +03:00
|
|
|
}
|
2016-12-07 06:09:11 +03:00
|
|
|
shouldPushMask = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!maskUsage.shouldGenerateClipMaskLayer &&
|
|
|
|
!maskUsage.shouldGenerateMaskLayer) {
|
|
|
|
shouldPushMask = true;
|
2015-11-11 18:15:39 +03:00
|
|
|
}
|
|
|
|
|
2016-11-03 06:58:15 +03:00
|
|
|
// SVG mask multiply opacity into maskSurface already, so we do not bother
|
|
|
|
// to apply opacity again.
|
2016-12-07 06:09:11 +03:00
|
|
|
if (shouldPushMask) {
|
|
|
|
target->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA,
|
|
|
|
maskFrame ? 1.0 : maskUsage.opacity,
|
|
|
|
maskSurface, maskTransform);
|
|
|
|
}
|
2006-03-21 18:49:20 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* If this frame has only a trivial clipPath, set up cairo's clipping now so
|
|
|
|
* we can just do normal painting and get it clipped appropriately.
|
|
|
|
*/
|
2018-08-18 01:48:02 +03:00
|
|
|
if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShapeOrPath) {
|
2016-11-03 06:58:15 +03:00
|
|
|
if (maskUsage.shouldApplyClipPath) {
|
|
|
|
clipPathFrame->ApplyClipPath(aContext, aFrame, aTransform);
|
|
|
|
} else {
|
2018-08-18 01:48:02 +03:00
|
|
|
nsCSSClipPathInstance::ApplyBasicShapeOrPathClip(aContext, aFrame);
|
2016-11-03 06:58:15 +03:00
|
|
|
}
|
2006-03-21 18:49:20 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Paint the child */
|
2018-08-29 21:11:43 +03:00
|
|
|
|
|
|
|
// We know we don't have eHasRefsSomeInvalid due to the check above. We
|
|
|
|
// don't test for eHasNoRefs here though since even if we have that we may
|
|
|
|
// still have CSS filter functions to handle. We have to check the style.
|
|
|
|
if (aFrame->StyleEffects()->HasFilters()) {
|
2014-04-24 12:25:17 +04:00
|
|
|
nsRegion* dirtyRegion = nullptr;
|
|
|
|
nsRegion tmpDirtyRegion;
|
2012-06-26 14:49:23 +04:00
|
|
|
if (aDirtyRect) {
|
|
|
|
// aDirtyRect is in outer-<svg> device pixels, but the filter code needs
|
|
|
|
// it in frame space.
|
2017-02-27 15:46:18 +03:00
|
|
|
gfxMatrix userToDeviceSpace = aTransform;
|
2012-06-26 14:49:23 +04:00
|
|
|
if (userToDeviceSpace.IsSingular()) {
|
2017-05-18 23:03:41 +03:00
|
|
|
return;
|
2012-06-26 14:49:23 +04:00
|
|
|
}
|
|
|
|
gfxMatrix deviceToUserSpace = userToDeviceSpace;
|
|
|
|
deviceToUserSpace.Invert();
|
2017-07-05 18:22:00 +03:00
|
|
|
gfxRect dirtyBounds = deviceToUserSpace.TransformBounds(gfxRect(
|
|
|
|
aDirtyRect->x, aDirtyRect->y, aDirtyRect->width, aDirtyRect->height));
|
2012-06-26 14:49:23 +04:00
|
|
|
tmpDirtyRegion = nsLayoutUtils::RoundGfxRectToAppRect(
|
2018-08-11 07:46:23 +03:00
|
|
|
dirtyBounds, AppUnitsPerCSSPixel()) -
|
2012-06-26 14:49:23 +04:00
|
|
|
aFrame->GetPosition();
|
2014-04-24 12:25:17 +04:00
|
|
|
dirtyRegion = &tmpDirtyRegion;
|
2012-06-26 14:49:23 +04:00
|
|
|
}
|
2017-03-19 20:30:32 +03:00
|
|
|
|
Bug 1385239 - Part 2. Remove aTransform parameter from PaintFilteredFrame. r=mstange
There are two callers of nsFilterInstance::PaintFilteredFrame:
1. nsSVGUtils::PaintFrameWithEffects at [1]
This function is used while painting a filtered element on a path which
display item painting is not supported yet, such as drawing elements inside a
indirect painted SVG object, such as a mask, a clipPath or a gradient object.
Let's say we have a masked element, which refers to an SVG mask, and there is
a filtered element inside that SVG mask.
Using nsFilterInstance::PaintFilteredFrame to paint that filtered frame in
the mask, we have to pass a gfxContext and a transform matrix to it. The
transform of the gfxContext 'target' that we pass in consists of a transform
from the referenced frame, of the masked frame, to the masked frame. We also
pass in a transform matrix 'aTransform', this matrix contains a transform
from the the masked frame to the filtered frame in *device units*, which
means it contains css-to-dev-px scaling factor.
2. nsSVGIntegrationUtils::PaintFilter at [2]
This function is used by normal display item painting.
The same, we pass a gfxContext 'context' and a transform matrix 'tm' into
nsFilterInstance::PaintFilteredFrame. The transform matrix of 'context'
consists of a transform from the referenced frame, of the filtered frame,
to this filtered frame, but the scale factor was taken out . The transform
matrix 'tm' we pass in contains scale transform from the referenced frame to
the filtered frame in *device unit*.
Inside nsFilterInstance::PaintFilteredFrame, we treat the transform matrix of
'aCtx' and 'aTransform' as parameters we pass in in #2 caller. So it can be
failed in #1. For example, if the filtered frame inside a masked frame has a
translation transform applied, since that translation was put in 'aTransfrom',
but we only use the scale factor of 'aTransform' in
nsFilterInstance::PaintFilteredFrame, translation factor disappears.
In this patch, I unified the definition of parameters of
nsFilterInstance::PaintFilteredFrame:
1. nsFilterInstance::PaintFilteredFrame(aCtx): the transform matrix of aCtx
should be a transform from the referenced frame to the filtered frame in
*css units*. Originally, the aCtx we passed in #1 is in device units, which
should be fixed; the aCtx we passed in #2 does not even include css scaling
factor, need be fixed too.
2. nsFilterInstance::PaintFilteredFrame(aTransform): this transform matrix
should contain only scaling factor in device units. And I removed it in the
end since I found we can get this value easily right inside the callee.
[1]
https://hg.mozilla.org/mozilla-central/file/ef585ac7c476/layout/svg/nsSVGUtils.cpp#l857
[2]
https://hg.mozilla.org/mozilla-central/file/ef585ac7c476/layout/svg/nsSVGIntegrationUtils.cpp#l1114
MozReview-Commit-ID: gRV128NyQv
--HG--
extra : rebase_source : 15d130babd07734c3c36e24e9ea8b5c4e5f7dbbf
2017-08-25 12:51:58 +03:00
|
|
|
gfxContextMatrixAutoSaveRestore autoSR(target);
|
|
|
|
|
|
|
|
// 'target' is currently scaled such that its user space units are CSS
|
|
|
|
// pixels (SVG user space units). But PaintFilteredFrame expects it to be
|
|
|
|
// scaled in such a way that its user space units are device pixels. So we
|
|
|
|
// have to adjust the scale.
|
|
|
|
gfxMatrix reverseScaleMatrix = nsSVGUtils::GetCSSPxToDevPxMatrix(aFrame);
|
|
|
|
DebugOnly<bool> invertible = reverseScaleMatrix.Invert();
|
2017-11-11 05:14:09 +03:00
|
|
|
target->SetMatrixDouble(reverseScaleMatrix * aTransform *
|
|
|
|
target->CurrentMatrixDouble());
|
Bug 1385239 - Part 2. Remove aTransform parameter from PaintFilteredFrame. r=mstange
There are two callers of nsFilterInstance::PaintFilteredFrame:
1. nsSVGUtils::PaintFrameWithEffects at [1]
This function is used while painting a filtered element on a path which
display item painting is not supported yet, such as drawing elements inside a
indirect painted SVG object, such as a mask, a clipPath or a gradient object.
Let's say we have a masked element, which refers to an SVG mask, and there is
a filtered element inside that SVG mask.
Using nsFilterInstance::PaintFilteredFrame to paint that filtered frame in
the mask, we have to pass a gfxContext and a transform matrix to it. The
transform of the gfxContext 'target' that we pass in consists of a transform
from the referenced frame, of the masked frame, to the masked frame. We also
pass in a transform matrix 'aTransform', this matrix contains a transform
from the the masked frame to the filtered frame in *device units*, which
means it contains css-to-dev-px scaling factor.
2. nsSVGIntegrationUtils::PaintFilter at [2]
This function is used by normal display item painting.
The same, we pass a gfxContext 'context' and a transform matrix 'tm' into
nsFilterInstance::PaintFilteredFrame. The transform matrix of 'context'
consists of a transform from the referenced frame, of the filtered frame,
to this filtered frame, but the scale factor was taken out . The transform
matrix 'tm' we pass in contains scale transform from the referenced frame to
the filtered frame in *device unit*.
Inside nsFilterInstance::PaintFilteredFrame, we treat the transform matrix of
'aCtx' and 'aTransform' as parameters we pass in in #2 caller. So it can be
failed in #1. For example, if the filtered frame inside a masked frame has a
translation transform applied, since that translation was put in 'aTransfrom',
but we only use the scale factor of 'aTransform' in
nsFilterInstance::PaintFilteredFrame, translation factor disappears.
In this patch, I unified the definition of parameters of
nsFilterInstance::PaintFilteredFrame:
1. nsFilterInstance::PaintFilteredFrame(aCtx): the transform matrix of aCtx
should be a transform from the referenced frame to the filtered frame in
*css units*. Originally, the aCtx we passed in #1 is in device units, which
should be fixed; the aCtx we passed in #2 does not even include css scaling
factor, need be fixed too.
2. nsFilterInstance::PaintFilteredFrame(aTransform): this transform matrix
should contain only scaling factor in device units. And I removed it in the
end since I found we can get this value easily right inside the callee.
[1]
https://hg.mozilla.org/mozilla-central/file/ef585ac7c476/layout/svg/nsSVGUtils.cpp#l857
[2]
https://hg.mozilla.org/mozilla-central/file/ef585ac7c476/layout/svg/nsSVGIntegrationUtils.cpp#l1114
MozReview-Commit-ID: gRV128NyQv
--HG--
extra : rebase_source : 15d130babd07734c3c36e24e9ea8b5c4e5f7dbbf
2017-08-25 12:51:58 +03:00
|
|
|
|
2017-05-18 23:03:41 +03:00
|
|
|
SVGPaintCallback paintCallback;
|
Bug 1385239 - Part 2. Remove aTransform parameter from PaintFilteredFrame. r=mstange
There are two callers of nsFilterInstance::PaintFilteredFrame:
1. nsSVGUtils::PaintFrameWithEffects at [1]
This function is used while painting a filtered element on a path which
display item painting is not supported yet, such as drawing elements inside a
indirect painted SVG object, such as a mask, a clipPath or a gradient object.
Let's say we have a masked element, which refers to an SVG mask, and there is
a filtered element inside that SVG mask.
Using nsFilterInstance::PaintFilteredFrame to paint that filtered frame in
the mask, we have to pass a gfxContext and a transform matrix to it. The
transform of the gfxContext 'target' that we pass in consists of a transform
from the referenced frame, of the masked frame, to the masked frame. We also
pass in a transform matrix 'aTransform', this matrix contains a transform
from the the masked frame to the filtered frame in *device units*, which
means it contains css-to-dev-px scaling factor.
2. nsSVGIntegrationUtils::PaintFilter at [2]
This function is used by normal display item painting.
The same, we pass a gfxContext 'context' and a transform matrix 'tm' into
nsFilterInstance::PaintFilteredFrame. The transform matrix of 'context'
consists of a transform from the referenced frame, of the filtered frame,
to this filtered frame, but the scale factor was taken out . The transform
matrix 'tm' we pass in contains scale transform from the referenced frame to
the filtered frame in *device unit*.
Inside nsFilterInstance::PaintFilteredFrame, we treat the transform matrix of
'aCtx' and 'aTransform' as parameters we pass in in #2 caller. So it can be
failed in #1. For example, if the filtered frame inside a masked frame has a
translation transform applied, since that translation was put in 'aTransfrom',
but we only use the scale factor of 'aTransform' in
nsFilterInstance::PaintFilteredFrame, translation factor disappears.
In this patch, I unified the definition of parameters of
nsFilterInstance::PaintFilteredFrame:
1. nsFilterInstance::PaintFilteredFrame(aCtx): the transform matrix of aCtx
should be a transform from the referenced frame to the filtered frame in
*css units*. Originally, the aCtx we passed in #1 is in device units, which
should be fixed; the aCtx we passed in #2 does not even include css scaling
factor, need be fixed too.
2. nsFilterInstance::PaintFilteredFrame(aTransform): this transform matrix
should contain only scaling factor in device units. And I removed it in the
end since I found we can get this value easily right inside the callee.
[1]
https://hg.mozilla.org/mozilla-central/file/ef585ac7c476/layout/svg/nsSVGUtils.cpp#l857
[2]
https://hg.mozilla.org/mozilla-central/file/ef585ac7c476/layout/svg/nsSVGIntegrationUtils.cpp#l1114
MozReview-Commit-ID: gRV128NyQv
--HG--
extra : rebase_source : 15d130babd07734c3c36e24e9ea8b5c4e5f7dbbf
2017-08-25 12:51:58 +03:00
|
|
|
nsFilterInstance::PaintFilteredFrame(aFrame, target, &paintCallback,
|
2017-05-18 23:03:41 +03:00
|
|
|
dirtyRegion, aImgParams);
|
2006-03-21 18:49:20 +03:00
|
|
|
} else {
|
2017-05-18 23:03:41 +03:00
|
|
|
svgFrame->PaintSVG(*target, aTransform, aImgParams, aDirtyRect);
|
2006-03-21 18:49:20 +03:00
|
|
|
}
|
2006-09-28 19:08:41 +04:00
|
|
|
|
2018-08-18 01:48:02 +03:00
|
|
|
if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShapeOrPath) {
|
2016-11-03 06:58:15 +03:00
|
|
|
aContext.PopClip();
|
2006-03-21 18:49:20 +03:00
|
|
|
}
|
|
|
|
|
2016-12-07 06:09:11 +03:00
|
|
|
if (shouldPushMask) {
|
2015-11-11 18:15:39 +03:00
|
|
|
target->PopGroupAndBlend();
|
2006-03-21 18:49:20 +03:00
|
|
|
}
|
|
|
|
|
2016-11-16 07:16:20 +03:00
|
|
|
if (blender.ShouldCreateDrawTargetForBlend()) {
|
2016-11-03 07:38:36 +03:00
|
|
|
MOZ_ASSERT(target != &aContext);
|
2016-11-16 07:16:20 +03:00
|
|
|
blender.BlendToTarget();
|
2006-03-21 18:49:20 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-07 11:09:31 +04:00
|
|
|
bool nsSVGUtils::HitTestClip(nsIFrame* aFrame, const gfxPoint& aPoint) {
|
2018-11-12 17:59:31 +03:00
|
|
|
// If the clip-path property references non-existent or invalid clipPath
|
|
|
|
// element(s) we ignore it.
|
2018-09-05 20:12:44 +03:00
|
|
|
nsSVGClipPathFrame* clipPathFrame;
|
2018-11-12 17:59:31 +03:00
|
|
|
SVGObserverUtils::GetAndObserveClipPath(aFrame, &clipPathFrame);
|
2018-09-05 20:12:44 +03:00
|
|
|
if (clipPathFrame) {
|
|
|
|
return clipPathFrame->PointIsInsideClipPath(aFrame, aPoint);
|
2006-03-21 18:49:20 +03:00
|
|
|
}
|
2018-09-05 20:12:44 +03:00
|
|
|
if (aFrame->StyleSVGReset()->HasClipPath()) {
|
|
|
|
return nsCSSClipPathInstance::HitTestBasicShapeOrPathClip(aFrame, aPoint);
|
2014-02-27 00:40:07 +04:00
|
|
|
}
|
2018-09-05 20:12:44 +03:00
|
|
|
return true;
|
2006-03-21 18:49:20 +03:00
|
|
|
}
|
|
|
|
|
2014-08-07 11:09:31 +04:00
|
|
|
nsIFrame* 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;
|
2015-03-03 14:08:59 +03:00
|
|
|
if (aFrame->GetContent()->IsSVGElement()) { // must check before cast
|
2014-08-07 11:09:31 +04:00
|
|
|
gfxMatrix m =
|
|
|
|
static_cast<const nsSVGElement*>(aFrame->GetContent())
|
|
|
|
->PrependLocalTransformsTo(gfxMatrix(), eChildToUserSpace);
|
|
|
|
if (!m.IsIdentity()) {
|
|
|
|
if (!m.Invert()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
2017-07-05 18:18:49 +03:00
|
|
|
point = m.TransformPoint(point);
|
2014-08-07 11:09:31 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-02 20:31:45 +04:00
|
|
|
// 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.
|
2012-07-30 18:20:58 +04:00
|
|
|
nsIFrame* result = nullptr;
|
2011-08-25 00:54:30 +04:00
|
|
|
for (nsIFrame* current = aFrame->PrincipalChildList().LastChild(); current;
|
2009-10-02 20:31:45 +04:00
|
|
|
current = current->GetPrevSibling()) {
|
2017-02-09 21:24:31 +03:00
|
|
|
nsSVGDisplayableFrame* SVGFrame = do_QueryFrame(current);
|
2006-03-21 18:49:20 +03:00
|
|
|
if (SVGFrame) {
|
2012-03-07 14:50:36 +04:00
|
|
|
const nsIContent* content = current->GetContent();
|
2015-03-03 14:08:59 +03:00
|
|
|
if (content->IsSVGElement() &&
|
2012-03-07 14:50:36 +04:00
|
|
|
!static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
|
|
|
|
continue;
|
|
|
|
}
|
2014-08-07 11:09:31 +04:00
|
|
|
// GetFrameForPoint() expects a point in its frame's SVG user space, so
|
|
|
|
// we need to convert to that space:
|
|
|
|
gfxPoint p = point;
|
2015-03-03 14:08:59 +03:00
|
|
|
if (content->IsSVGElement()) { // must check before cast
|
2014-08-07 11:09:31 +04:00
|
|
|
gfxMatrix m =
|
|
|
|
static_cast<const nsSVGElement*>(content)->PrependLocalTransformsTo(
|
2015-12-03 01:36:23 +03:00
|
|
|
gfxMatrix(), eUserSpaceToParent);
|
2014-08-07 11:09:31 +04:00
|
|
|
if (!m.IsIdentity()) {
|
|
|
|
if (!m.Invert()) {
|
|
|
|
continue;
|
|
|
|
}
|
2017-07-05 18:18:49 +03:00
|
|
|
p = m.TransformPoint(p);
|
2014-08-07 11:09:31 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
result = SVGFrame->GetFrameForPoint(p);
|
2012-03-03 13:21:09 +04:00
|
|
|
if (result) break;
|
2006-03-21 18:49:20 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-25 13:23:54 +04:00
|
|
|
if (result && !HitTestClip(aFrame, aPoint)) result = nullptr;
|
|
|
|
|
|
|
|
return result;
|
2006-03-21 18:49:20 +03:00
|
|
|
}
|
2006-04-14 19:09:39 +04:00
|
|
|
|
2012-02-10 16:33:46 +04:00
|
|
|
nsRect nsSVGUtils::TransformFrameRectToOuterSVG(const nsRect& aRect,
|
|
|
|
const gfxMatrix& aMatrix,
|
|
|
|
nsPresContext* aPresContext) {
|
|
|
|
gfxRect r(aRect.x, aRect.y, aRect.width, aRect.height);
|
2018-08-11 07:46:23 +03:00
|
|
|
r.Scale(1.0 / AppUnitsPerCSSPixel());
|
2017-07-05 18:22:00 +03:00
|
|
|
return nsLayoutUtils::RoundGfxRectToAppRect(
|
|
|
|
aMatrix.TransformBounds(r), aPresContext->AppUnitsPerDevPixel());
|
2012-02-10 16:33:46 +04:00
|
|
|
}
|
|
|
|
|
2011-04-21 23:36:46 +04:00
|
|
|
IntSize nsSVGUtils::ConvertToSurfaceSize(const gfxSize& aSize,
|
2011-09-29 10:19:26 +04:00
|
|
|
bool* aResultOverflows) {
|
2015-09-23 21:49:05 +03:00
|
|
|
IntSize surfaceSize(ClampToInt(ceil(aSize.width)),
|
|
|
|
ClampToInt(ceil(aSize.height)));
|
2011-04-21 23:36:46 +04:00
|
|
|
|
2011-10-26 12:52:11 +04:00
|
|
|
*aResultOverflows = surfaceSize.width != ceil(aSize.width) ||
|
|
|
|
surfaceSize.height != ceil(aSize.height);
|
2011-04-21 23:36:46 +04:00
|
|
|
|
2017-02-20 19:08:38 +03:00
|
|
|
if (!Factory::AllowedSurfaceSize(surfaceSize)) {
|
2013-01-15 16:22:03 +04:00
|
|
|
surfaceSize.width =
|
|
|
|
std::min(NS_SVG_OFFSCREEN_MAX_DIMENSION, surfaceSize.width);
|
2011-04-21 23:36:46 +04:00
|
|
|
surfaceSize.height =
|
2013-01-15 16:22:03 +04:00
|
|
|
std::min(NS_SVG_OFFSCREEN_MAX_DIMENSION, surfaceSize.height);
|
2011-10-17 18:59:28 +04:00
|
|
|
*aResultOverflows = true;
|
2011-04-21 23:36:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return surfaceSize;
|
|
|
|
}
|
|
|
|
|
2013-12-31 01:42:26 +04:00
|
|
|
bool nsSVGUtils::HitTestRect(const gfx::Matrix& aMatrix, float aRX, float aRY,
|
2006-09-19 20:35:29 +04:00
|
|
|
float aRWidth, float aRHeight, float aX,
|
|
|
|
float aY) {
|
2013-12-31 01:42:26 +04:00
|
|
|
gfx::Rect rect(aRX, aRY, aRWidth, aRHeight);
|
2012-06-15 07:02:27 +04:00
|
|
|
if (rect.IsEmpty() || aMatrix.IsSingular()) {
|
2011-10-17 18:59:28 +04:00
|
|
|
return false;
|
2006-09-19 20:35:29 +04:00
|
|
|
}
|
2013-12-31 01:42:26 +04:00
|
|
|
gfx::Matrix toRectSpace = aMatrix;
|
2012-06-15 07:02:27 +04:00
|
|
|
toRectSpace.Invert();
|
2016-09-08 19:26:03 +03:00
|
|
|
gfx::Point p = toRectSpace.TransformPoint(gfx::Point(aX, aY));
|
2012-06-15 07:02:27 +04:00
|
|
|
return rect.x <= p.x && p.x <= rect.XMost() && rect.y <= p.y &&
|
|
|
|
p.y <= rect.YMost();
|
2006-09-19 20:35:29 +04:00
|
|
|
}
|
2006-09-27 02:27:56 +04:00
|
|
|
|
2009-06-18 00:51:40 +04:00
|
|
|
gfxRect nsSVGUtils::GetClipRectForFrame(nsIFrame* aFrame, float aX, float aY,
|
|
|
|
float aWidth, float aHeight) {
|
2013-02-17 01:51:02 +04:00
|
|
|
const nsStyleDisplay* disp = aFrame->StyleDisplay();
|
2016-04-12 08:52:42 +03:00
|
|
|
const nsStyleEffects* effects = aFrame->StyleEffects();
|
2009-06-18 00:51:40 +04:00
|
|
|
|
2016-04-12 08:52:42 +03:00
|
|
|
if (!(effects->mClipFlags & NS_STYLE_CLIP_RECT)) {
|
|
|
|
NS_ASSERTION(effects->mClipFlags == NS_STYLE_CLIP_AUTO,
|
2009-06-18 00:51:40 +04:00
|
|
|
"We don't know about this type of clip.");
|
|
|
|
return gfxRect(aX, aY, aWidth, aHeight);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (disp->mOverflowX == NS_STYLE_OVERFLOW_HIDDEN ||
|
|
|
|
disp->mOverflowY == NS_STYLE_OVERFLOW_HIDDEN) {
|
2016-04-12 08:52:42 +03:00
|
|
|
nsIntRect clipPxRect = effects->mClip.ToOutsidePixels(
|
|
|
|
aFrame->PresContext()->AppUnitsPerDevPixel());
|
2009-06-18 00:51:40 +04:00
|
|
|
gfxRect clipRect = gfxRect(clipPxRect.x, clipPxRect.y, clipPxRect.width,
|
|
|
|
clipPxRect.height);
|
|
|
|
|
2016-04-12 08:52:42 +03:00
|
|
|
if (NS_STYLE_CLIP_RIGHT_AUTO & effects->mClipFlags) {
|
2011-04-19 07:07:21 +04:00
|
|
|
clipRect.width = aWidth - clipRect.X();
|
2009-06-18 00:51:40 +04:00
|
|
|
}
|
2016-04-12 08:52:42 +03:00
|
|
|
if (NS_STYLE_CLIP_BOTTOM_AUTO & effects->mClipFlags) {
|
2011-04-19 07:07:21 +04:00
|
|
|
clipRect.height = aHeight - clipRect.Y();
|
2009-06-18 00:51:40 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (disp->mOverflowX != NS_STYLE_OVERFLOW_HIDDEN) {
|
2011-04-19 07:07:21 +04:00
|
|
|
clipRect.x = aX;
|
|
|
|
clipRect.width = aWidth;
|
2009-06-18 00:51:40 +04:00
|
|
|
}
|
|
|
|
if (disp->mOverflowY != NS_STYLE_OVERFLOW_HIDDEN) {
|
2011-04-19 07:07:21 +04:00
|
|
|
clipRect.y = aY;
|
|
|
|
clipRect.height = aHeight;
|
2009-06-18 00:51:40 +04:00
|
|
|
}
|
2017-07-06 15:00:35 +03:00
|
|
|
|
2009-06-18 00:51:40 +04:00
|
|
|
return clipRect;
|
|
|
|
}
|
|
|
|
return gfxRect(aX, aY, aWidth, aHeight);
|
|
|
|
}
|
|
|
|
|
2006-11-27 20:30:57 +03:00
|
|
|
void nsSVGUtils::SetClipRect(gfxContext* aContext, const gfxMatrix& aCTM,
|
2009-06-18 00:51:40 +04:00
|
|
|
const gfxRect& aRect) {
|
2009-06-18 15:31:25 +04:00
|
|
|
if (aCTM.IsSingular()) return;
|
2006-11-27 20:30:57 +03:00
|
|
|
|
2012-06-15 07:02:27 +04:00
|
|
|
gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(aContext);
|
2009-06-18 15:31:25 +04:00
|
|
|
aContext->Multiply(aCTM);
|
2009-06-18 00:51:40 +04:00
|
|
|
aContext->Clip(aRect);
|
2006-11-27 20:30:57 +03:00
|
|
|
}
|
|
|
|
|
2017-02-27 14:43:16 +03:00
|
|
|
gfxRect nsSVGUtils::GetBBox(nsIFrame* aFrame, uint32_t aFlags,
|
|
|
|
const gfxMatrix* aToBoundsSpace) {
|
2018-04-13 01:41:00 +03:00
|
|
|
if (aFrame->GetContent()->IsText()) {
|
2010-08-07 13:38:11 +04:00
|
|
|
aFrame = aFrame->GetParent();
|
|
|
|
}
|
2017-03-22 13:56:53 +03:00
|
|
|
|
2017-03-23 10:29:11 +03:00
|
|
|
if (nsSVGUtils::IsInSVGTextSubtree(aFrame)) {
|
2010-08-07 13:38:11 +04:00
|
|
|
// It is possible to apply a gradient, pattern, clipping path, mask or
|
|
|
|
// filter to text. When one of these facilities is applied to text
|
2011-05-25 10:31:59 +04:00
|
|
|
// the bounding box is the entire text element in all
|
2010-08-07 13:38:11 +04:00
|
|
|
// cases.
|
2017-03-22 13:56:53 +03:00
|
|
|
nsIFrame* ancestor = GetFirstNonAAncestorFrame(aFrame);
|
2017-03-23 10:29:11 +03:00
|
|
|
if (ancestor && nsSVGUtils::IsInSVGTextSubtree(ancestor)) {
|
2017-04-30 18:30:08 +03:00
|
|
|
while (!ancestor->IsSVGTextFrame()) {
|
2017-03-22 13:56:53 +03:00
|
|
|
ancestor = ancestor->GetParent();
|
2012-08-10 15:13:44 +04:00
|
|
|
}
|
2012-03-03 13:21:09 +04:00
|
|
|
}
|
2017-03-22 13:56:53 +03:00
|
|
|
aFrame = ancestor;
|
|
|
|
}
|
2015-04-27 13:15:36 +03:00
|
|
|
|
2017-03-22 13:56:53 +03:00
|
|
|
nsSVGDisplayableFrame* svg = do_QueryFrame(aFrame);
|
|
|
|
const bool hasSVGLayout = aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT;
|
|
|
|
if (hasSVGLayout && !svg) {
|
|
|
|
// An SVG frame, but not one that can be displayed directly (for
|
|
|
|
// example, nsGradientFrame). These can't contribute to the bbox.
|
|
|
|
return gfxRect();
|
|
|
|
}
|
2015-04-27 13:15:36 +03:00
|
|
|
|
2017-03-22 13:56:53 +03:00
|
|
|
const bool isOuterSVG = svg && !hasSVGLayout;
|
2017-06-07 08:27:17 +03:00
|
|
|
MOZ_ASSERT(!isOuterSVG || aFrame->IsSVGOuterSVGFrame());
|
2017-03-22 13:56:53 +03:00
|
|
|
if (!svg || (isOuterSVG && (aFlags & eUseFrameBoundsForOuterSVG))) {
|
|
|
|
// An HTML element or an SVG outer frame.
|
|
|
|
MOZ_ASSERT(!hasSVGLayout);
|
2017-08-17 04:52:17 +03:00
|
|
|
bool onlyCurrentFrame = aFlags & eIncludeOnlyCurrentFrameForNonSVGElement;
|
|
|
|
return nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(
|
|
|
|
aFrame,
|
|
|
|
/* aUnionContinuations = */ !onlyCurrentFrame);
|
2017-03-22 13:56:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
MOZ_ASSERT(svg);
|
|
|
|
|
|
|
|
nsIContent* content = aFrame->GetContent();
|
|
|
|
if (content->IsSVGElement() &&
|
|
|
|
!static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
|
|
|
|
return gfxRect();
|
|
|
|
}
|
|
|
|
|
2017-08-31 07:21:39 +03:00
|
|
|
// Clean out flags which have no effects on returning bbox from now, so that
|
|
|
|
// we can cache and reuse ObjectBoundingBoxProperty() in the code below.
|
|
|
|
aFlags &= ~eIncludeOnlyCurrentFrameForNonSVGElement;
|
|
|
|
aFlags &= ~eUseFrameBoundsForOuterSVG;
|
|
|
|
if (!aFrame->IsSVGUseFrame()) {
|
|
|
|
aFlags &= ~eUseUserSpaceOfUseElement;
|
|
|
|
}
|
|
|
|
|
2017-02-27 14:43:16 +03:00
|
|
|
if (aFlags == eBBoxIncludeFillGeometry &&
|
|
|
|
// We only cache bbox in element's own user space
|
|
|
|
!aToBoundsSpace) {
|
2017-05-27 14:36:00 +03:00
|
|
|
gfxRect* prop = aFrame->GetProperty(ObjectBoundingBoxProperty());
|
2017-03-22 13:56:53 +03:00
|
|
|
if (prop) {
|
|
|
|
return *prop;
|
2012-02-17 12:12:47 +04:00
|
|
|
}
|
2017-03-22 13:56:53 +03:00
|
|
|
}
|
2016-12-07 10:28:47 +03:00
|
|
|
|
2017-03-22 13:56:53 +03:00
|
|
|
gfxMatrix matrix;
|
2017-02-27 14:43:16 +03:00
|
|
|
if (aToBoundsSpace) {
|
|
|
|
matrix = *aToBoundsSpace;
|
|
|
|
}
|
|
|
|
|
2017-08-29 15:41:45 +03:00
|
|
|
if (aFrame->IsSVGForeignObjectFrame() ||
|
2017-08-31 07:21:39 +03:00
|
|
|
aFlags & nsSVGUtils::eUseUserSpaceOfUseElement) {
|
2017-03-22 13:56:53 +03:00
|
|
|
// The spec says getBBox "Returns the tight bounding box in *current user
|
|
|
|
// space*". So we should really be doing this for all elements, but that
|
|
|
|
// needs investigation to check that we won't break too much content.
|
|
|
|
// NOTE: When changing this to apply to other frame types, make sure to
|
|
|
|
// also update nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset.
|
|
|
|
MOZ_ASSERT(content->IsSVGElement(), "bad cast");
|
|
|
|
nsSVGElement* element = static_cast<nsSVGElement*>(content);
|
|
|
|
matrix = element->PrependLocalTransformsTo(matrix, eChildToUserSpace);
|
|
|
|
}
|
|
|
|
gfxRect bbox =
|
|
|
|
svg->GetBBoxContribution(ToMatrix(matrix), aFlags).ToThebesRect();
|
|
|
|
// Account for 'clipped'.
|
|
|
|
if (aFlags & nsSVGUtils::eBBoxIncludeClipped) {
|
|
|
|
gfxRect clipRect(0, 0, 0, 0);
|
|
|
|
float x, y, width, height;
|
|
|
|
gfxMatrix tm;
|
|
|
|
gfxRect fillBBox =
|
|
|
|
svg->GetBBoxContribution(ToMatrix(tm), nsSVGUtils::eBBoxIncludeFill)
|
|
|
|
.ToThebesRect();
|
|
|
|
x = fillBBox.x;
|
|
|
|
y = fillBBox.y;
|
|
|
|
width = fillBBox.width;
|
|
|
|
height = fillBBox.height;
|
|
|
|
bool hasClip = aFrame->StyleDisplay()->IsScrollableOverflow();
|
|
|
|
if (hasClip) {
|
|
|
|
clipRect = nsSVGUtils::GetClipRectForFrame(aFrame, x, y, width, height);
|
2017-04-30 18:30:08 +03:00
|
|
|
if (aFrame->IsSVGForeignObjectFrame() || aFrame->IsSVGUseFrame()) {
|
2017-07-05 18:22:00 +03:00
|
|
|
clipRect = matrix.TransformBounds(clipRect);
|
2017-04-30 18:30:08 +03:00
|
|
|
}
|
2017-03-22 13:56:53 +03:00
|
|
|
}
|
2018-09-05 20:12:44 +03:00
|
|
|
nsSVGClipPathFrame* clipPathFrame;
|
|
|
|
if (SVGObserverUtils::GetAndObserveClipPath(aFrame, &clipPathFrame) ==
|
|
|
|
SVGObserverUtils::eHasRefsSomeInvalid) {
|
2017-03-22 13:56:53 +03:00
|
|
|
bbox = gfxRect(0, 0, 0, 0);
|
|
|
|
} else {
|
|
|
|
if (clipPathFrame) {
|
|
|
|
SVGClipPathElement* clipContent =
|
|
|
|
static_cast<SVGClipPathElement*>(clipPathFrame->GetContent());
|
2018-03-10 01:26:24 +03:00
|
|
|
if (clipContent->IsUnitsObjectBoundingBox()) {
|
2017-07-05 18:18:49 +03:00
|
|
|
matrix.PreTranslate(gfxPoint(x, y));
|
|
|
|
matrix.PreScale(width, height);
|
2017-04-30 18:30:08 +03:00
|
|
|
} else if (aFrame->IsSVGForeignObjectFrame()) {
|
2017-07-05 18:18:49 +03:00
|
|
|
matrix = gfxMatrix();
|
2014-05-13 05:24:35 +04:00
|
|
|
}
|
2017-11-16 01:33:41 +03:00
|
|
|
matrix =
|
|
|
|
clipContent->PrependLocalTransformsTo(matrix, eUserSpaceToParent);
|
2018-05-12 01:55:30 +03:00
|
|
|
bbox = clipPathFrame->GetBBoxForClipPathFrame(bbox, matrix, aFlags)
|
|
|
|
.ToThebesRect();
|
2017-03-22 13:56:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (hasClip) {
|
|
|
|
bbox = bbox.Intersect(clipRect);
|
2014-05-13 05:24:35 +04:00
|
|
|
}
|
2015-04-27 13:15:36 +03:00
|
|
|
|
2017-03-22 13:56:53 +03:00
|
|
|
if (bbox.IsEmpty()) {
|
|
|
|
bbox = gfxRect(0, 0, 0, 0);
|
|
|
|
}
|
2015-04-27 13:15:36 +03:00
|
|
|
}
|
2017-03-22 13:56:53 +03:00
|
|
|
}
|
2015-04-27 13:15:36 +03:00
|
|
|
|
2017-02-27 14:43:16 +03:00
|
|
|
if (aFlags == eBBoxIncludeFillGeometry &&
|
|
|
|
// We only cache bbox in element's own user space
|
|
|
|
!aToBoundsSpace) {
|
2017-03-22 13:56:53 +03:00
|
|
|
// Obtaining the bbox for objectBoundingBox calculations is common so we
|
|
|
|
// cache the result for future calls, since calculation can be expensive:
|
2017-05-27 14:36:00 +03:00
|
|
|
aFrame->SetProperty(ObjectBoundingBoxProperty(), new gfxRect(bbox));
|
2008-09-11 04:24:16 +04:00
|
|
|
}
|
2017-03-22 13:56:53 +03:00
|
|
|
|
|
|
|
return bbox;
|
2008-09-11 04:24:16 +04:00
|
|
|
}
|
|
|
|
|
2014-04-23 13:47:42 +04:00
|
|
|
gfxPoint nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(nsIFrame* aFrame) {
|
|
|
|
if (!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
|
|
|
|
// The user space for non-SVG frames is defined as the bounding box of the
|
|
|
|
// frame's border-box rects over all continuations.
|
|
|
|
return gfxPoint();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Leaf frames apply their own offset inside their user space.
|
|
|
|
if (aFrame->IsFrameOfType(nsIFrame::eSVGGeometry) ||
|
2017-03-23 10:29:11 +03:00
|
|
|
nsSVGUtils::IsInSVGTextSubtree(aFrame)) {
|
2014-04-23 13:47:42 +04:00
|
|
|
return nsLayoutUtils::RectToGfxRect(aFrame->GetRect(),
|
2018-08-11 07:46:23 +03:00
|
|
|
AppUnitsPerCSSPixel())
|
|
|
|
.TopLeft();
|
2014-04-23 13:47:42 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// For foreignObject frames, nsSVGUtils::GetBBox applies their local
|
|
|
|
// transform, so we need to do the same here.
|
2017-04-30 18:30:08 +03:00
|
|
|
if (aFrame->IsSVGForeignObjectFrame()) {
|
2014-04-23 13:47:42 +04:00
|
|
|
gfxMatrix transform =
|
|
|
|
static_cast<nsSVGElement*>(aFrame->GetContent())
|
2015-12-03 01:36:23 +03:00
|
|
|
->PrependLocalTransformsTo(gfxMatrix(), eChildToUserSpace);
|
2014-04-23 13:47:42 +04:00
|
|
|
NS_ASSERTION(!transform.HasNonTranslation(),
|
|
|
|
"we're relying on this being an offset-only transform");
|
|
|
|
return transform.GetTranslation();
|
|
|
|
}
|
|
|
|
|
|
|
|
return gfxPoint();
|
|
|
|
}
|
|
|
|
|
2014-09-15 14:12:50 +04:00
|
|
|
static gfxRect GetBoundingBoxRelativeRect(const nsSVGLength2* aXYWH,
|
|
|
|
const gfxRect& aBBox) {
|
|
|
|
return gfxRect(aBBox.x + nsSVGUtils::ObjectSpace(aBBox, &aXYWH[0]),
|
|
|
|
aBBox.y + nsSVGUtils::ObjectSpace(aBBox, &aXYWH[1]),
|
|
|
|
nsSVGUtils::ObjectSpace(aBBox, &aXYWH[2]),
|
|
|
|
nsSVGUtils::ObjectSpace(aBBox, &aXYWH[3]));
|
|
|
|
}
|
|
|
|
|
2012-08-22 19:56:38 +04:00
|
|
|
gfxRect nsSVGUtils::GetRelativeRect(uint16_t aUnits, const nsSVGLength2* aXYWH,
|
2014-09-15 14:12:50 +04:00
|
|
|
const gfxRect& aBBox,
|
|
|
|
const UserSpaceMetrics& aMetrics) {
|
2013-02-18 06:14:02 +04:00
|
|
|
if (aUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
|
2014-09-15 14:12:50 +04:00
|
|
|
return GetBoundingBoxRelativeRect(aXYWH, aBBox);
|
|
|
|
}
|
|
|
|
return gfxRect(UserSpace(aMetrics, &aXYWH[0]), UserSpace(aMetrics, &aXYWH[1]),
|
|
|
|
UserSpace(aMetrics, &aXYWH[2]),
|
|
|
|
UserSpace(aMetrics, &aXYWH[3]));
|
|
|
|
}
|
|
|
|
|
|
|
|
gfxRect nsSVGUtils::GetRelativeRect(uint16_t aUnits, const nsSVGLength2* aXYWH,
|
|
|
|
const gfxRect& aBBox, nsIFrame* aFrame) {
|
|
|
|
if (aUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
|
|
|
|
return GetBoundingBoxRelativeRect(aXYWH, aBBox);
|
|
|
|
}
|
|
|
|
nsIContent* content = aFrame->GetContent();
|
2015-03-03 14:08:59 +03:00
|
|
|
if (content->IsSVGElement()) {
|
2014-09-15 14:12:50 +04:00
|
|
|
nsSVGElement* svgElement = static_cast<nsSVGElement*>(content);
|
|
|
|
return GetRelativeRect(aUnits, aXYWH, aBBox, SVGElementMetrics(svgElement));
|
2008-09-11 04:24:16 +04:00
|
|
|
}
|
2014-09-15 14:12:50 +04:00
|
|
|
return GetRelativeRect(aUnits, aXYWH, aBBox,
|
|
|
|
NonSVGFrameUserSpaceMetrics(aFrame));
|
2008-09-11 04:24:16 +04:00
|
|
|
}
|
|
|
|
|
2007-01-04 18:05:39 +03:00
|
|
|
bool nsSVGUtils::CanOptimizeOpacity(nsIFrame* aFrame) {
|
2012-07-20 22:12:29 +04:00
|
|
|
if (!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
|
|
|
|
return false;
|
|
|
|
}
|
2017-05-01 20:32:52 +03:00
|
|
|
LayoutFrameType type = aFrame->Type();
|
|
|
|
if (type != LayoutFrameType::SVGImage &&
|
|
|
|
type != LayoutFrameType::SVGGeometry) {
|
2011-10-17 18:59:28 +04:00
|
|
|
return false;
|
2009-10-26 20:17:49 +03:00
|
|
|
}
|
2016-04-12 08:52:43 +03:00
|
|
|
if (aFrame->StyleEffects()->HasFilters()) {
|
2011-10-17 18:59:28 +04:00
|
|
|
return false;
|
2009-10-26 20:17:49 +03:00
|
|
|
}
|
|
|
|
// XXX The SVG WG is intending to allow fill, stroke and markers on <image>
|
2017-05-01 20:32:52 +03:00
|
|
|
if (type == LayoutFrameType::SVGImage) {
|
2011-10-17 18:59:28 +04:00
|
|
|
return true;
|
2009-10-26 20:17:49 +03:00
|
|
|
}
|
2013-02-17 01:51:02 +04:00
|
|
|
const nsStyleSVG* style = aFrame->StyleSVG();
|
2013-07-12 10:39:38 +04:00
|
|
|
if (style->HasMarker()) {
|
2011-10-17 18:59:28 +04:00
|
|
|
return false;
|
2009-10-26 20:17:49 +03:00
|
|
|
}
|
2017-09-17 01:28:12 +03:00
|
|
|
|
2018-06-18 00:16:07 +03:00
|
|
|
if (nsLayoutUtils::HasAnimationOfProperty(aFrame, eCSSProperty_opacity)) {
|
2017-09-17 01:28:12 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-11-23 17:32:21 +04:00
|
|
|
if (!style->HasFill() || !HasStroke(aFrame)) {
|
2011-10-17 18:59:28 +04:00
|
|
|
return true;
|
2007-01-04 18:05:39 +03:00
|
|
|
}
|
2011-10-17 18:59:28 +04:00
|
|
|
return false;
|
2007-01-04 18:05:39 +03:00
|
|
|
}
|
|
|
|
|
2009-07-23 12:35:59 +04:00
|
|
|
gfxMatrix nsSVGUtils::AdjustMatrixForUnits(const gfxMatrix& aMatrix,
|
2007-12-04 07:40:52 +03:00
|
|
|
nsSVGEnum* aUnits, nsIFrame* aFrame,
|
2017-08-17 04:52:17 +03:00
|
|
|
uint32_t aFlags) {
|
2013-02-18 06:14:02 +04:00
|
|
|
if (aFrame && aUnits->GetAnimValue() == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
|
2017-08-17 04:52:17 +03:00
|
|
|
gfxRect bbox = GetBBox(aFrame, aFlags);
|
2014-09-10 17:26:12 +04:00
|
|
|
gfxMatrix tm = aMatrix;
|
2017-07-05 18:18:49 +03:00
|
|
|
tm.PreTranslate(gfxPoint(bbox.X(), bbox.Y()));
|
|
|
|
tm.PreScale(bbox.Width(), bbox.Height());
|
2014-09-10 17:26:12 +04:00
|
|
|
return tm;
|
2007-12-04 07:40:52 +03:00
|
|
|
}
|
2009-07-23 12:35:59 +04:00
|
|
|
return aMatrix;
|
2007-12-04 07:40:52 +03:00
|
|
|
}
|
|
|
|
|
2009-01-19 21:31:34 +03:00
|
|
|
nsIFrame* nsSVGUtils::GetFirstNonAAncestorFrame(nsIFrame* aStartFrame) {
|
|
|
|
for (nsIFrame* ancestorFrame = aStartFrame; ancestorFrame;
|
|
|
|
ancestorFrame = ancestorFrame->GetParent()) {
|
2017-04-30 18:30:08 +03:00
|
|
|
if (!ancestorFrame->IsSVGAFrame()) {
|
2009-01-19 21:31:34 +03:00
|
|
|
return ancestorFrame;
|
|
|
|
}
|
|
|
|
}
|
2012-07-30 18:20:58 +04:00
|
|
|
return nullptr;
|
2009-01-19 21:31:34 +03:00
|
|
|
}
|
|
|
|
|
2014-09-30 21:08:14 +04:00
|
|
|
bool nsSVGUtils::GetNonScalingStrokeTransform(nsIFrame* aFrame,
|
|
|
|
gfxMatrix* aUserToOuterSVG) {
|
2018-04-13 01:41:00 +03:00
|
|
|
if (aFrame->GetContent()->IsText()) {
|
2013-06-16 15:05:39 +04:00
|
|
|
aFrame = aFrame->GetParent();
|
|
|
|
}
|
|
|
|
|
2014-12-17 16:58:31 +03:00
|
|
|
if (!aFrame->StyleSVGReset()->HasNonScalingStroke()) {
|
2014-09-30 21:08:14 +04:00
|
|
|
return false;
|
2012-05-18 12:34:25 +04:00
|
|
|
}
|
2014-09-30 21:08:14 +04:00
|
|
|
|
|
|
|
nsIContent* content = aFrame->GetContent();
|
2015-03-03 14:08:59 +03:00
|
|
|
MOZ_ASSERT(content->IsSVGElement(), "bad cast");
|
2014-09-30 21:08:14 +04:00
|
|
|
|
|
|
|
*aUserToOuterSVG = ThebesMatrix(
|
|
|
|
SVGContentUtils::GetCTM(static_cast<nsSVGElement*>(content), true));
|
|
|
|
|
|
|
|
return !aUserToOuterSVG->IsIdentity();
|
2012-05-18 12:34:25 +04:00
|
|
|
}
|
|
|
|
|
2011-09-05 21:53:34 +04:00
|
|
|
// The logic here comes from _cairo_stroke_style_max_distance_from_path
|
|
|
|
static gfxRect PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
|
2012-08-10 15:13:44 +04:00
|
|
|
nsIFrame* aFrame,
|
|
|
|
double aStyleExpansionFactor,
|
2012-02-10 16:33:39 +04:00
|
|
|
const gfxMatrix& aMatrix) {
|
2011-09-05 21:53:34 +04:00
|
|
|
double style_expansion =
|
2012-08-10 15:13:44 +04:00
|
|
|
aStyleExpansionFactor * nsSVGUtils::GetStrokeWidth(aFrame);
|
2009-03-31 16:19:39 +04:00
|
|
|
|
2014-09-30 21:08:14 +04:00
|
|
|
gfxMatrix matrix = aMatrix;
|
|
|
|
|
|
|
|
gfxMatrix outerSVGToUser;
|
|
|
|
if (nsSVGUtils::GetNonScalingStrokeTransform(aFrame, &outerSVGToUser)) {
|
|
|
|
outerSVGToUser.Invert();
|
2015-09-02 21:10:00 +03:00
|
|
|
matrix.PreMultiply(outerSVGToUser);
|
2014-09-30 21:08:14 +04:00
|
|
|
}
|
2012-05-18 12:34:25 +04:00
|
|
|
|
2014-06-17 21:35:51 +04:00
|
|
|
double dx = style_expansion * (fabs(matrix._11) + fabs(matrix._21));
|
|
|
|
double dy = style_expansion * (fabs(matrix._22) + fabs(matrix._12));
|
2009-03-31 16:19:39 +04:00
|
|
|
|
|
|
|
gfxRect strokeExtents = aPathExtents;
|
2011-04-19 07:07:51 +04:00
|
|
|
strokeExtents.Inflate(dx, dy);
|
2009-03-31 16:19:39 +04:00
|
|
|
return strokeExtents;
|
|
|
|
}
|
|
|
|
|
2012-08-10 15:13:44 +04:00
|
|
|
/*static*/ gfxRect nsSVGUtils::PathExtentsToMaxStrokeExtents(
|
|
|
|
const gfxRect& aPathExtents, nsTextFrame* aFrame,
|
|
|
|
const gfxMatrix& aMatrix) {
|
2017-03-23 10:29:11 +03:00
|
|
|
NS_ASSERTION(nsSVGUtils::IsInSVGTextSubtree(aFrame),
|
|
|
|
"expected an nsTextFrame for SVG text");
|
2012-08-10 15:13:44 +04:00
|
|
|
return ::PathExtentsToMaxStrokeExtents(aPathExtents, aFrame, 0.5, aMatrix);
|
|
|
|
}
|
|
|
|
|
2011-09-05 21:53:34 +04:00
|
|
|
/*static*/ gfxRect nsSVGUtils::PathExtentsToMaxStrokeExtents(
|
2016-12-18 14:11:47 +03:00
|
|
|
const gfxRect& aPathExtents, SVGGeometryFrame* aFrame,
|
2012-02-10 16:33:39 +04:00
|
|
|
const gfxMatrix& aMatrix) {
|
2015-03-03 14:09:00 +03:00
|
|
|
bool strokeMayHaveCorners =
|
2015-06-29 21:19:00 +03:00
|
|
|
!SVGContentUtils::ShapeTypeHasNoCorners(aFrame->GetContent());
|
2014-11-01 13:45:10 +03:00
|
|
|
|
|
|
|
// For a shape without corners the stroke can only extend half the stroke
|
|
|
|
// width from the path in the x/y-axis directions. For shapes with corners
|
|
|
|
// the stroke can extend by sqrt(1/2) (think 45 degree rotated rect, or line
|
|
|
|
// with stroke-linecaps="square").
|
|
|
|
double styleExpansionFactor = strokeMayHaveCorners ? M_SQRT1_2 : 0.5;
|
|
|
|
|
|
|
|
// The stroke can extend even further for paths that can be affected by
|
|
|
|
// stroke-miterlimit.
|
2015-03-03 14:09:00 +03:00
|
|
|
bool affectedByMiterlimit = aFrame->GetContent()->IsAnyOfSVGElements(
|
|
|
|
nsGkAtoms::path, nsGkAtoms::polyline, nsGkAtoms::polygon);
|
|
|
|
|
2014-11-01 13:45:10 +03:00
|
|
|
if (affectedByMiterlimit) {
|
2013-02-17 01:51:02 +04:00
|
|
|
const nsStyleSVG* style = aFrame->StyleSVG();
|
2011-09-05 21:53:34 +04:00
|
|
|
if (style->mStrokeLinejoin == NS_STYLE_STROKE_LINEJOIN_MITER &&
|
2014-11-01 13:45:10 +03:00
|
|
|
styleExpansionFactor < style->mStrokeMiterlimit / 2.0) {
|
|
|
|
styleExpansionFactor = style->mStrokeMiterlimit / 2.0;
|
2011-09-05 21:53:34 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ::PathExtentsToMaxStrokeExtents(aPathExtents, aFrame,
|
2012-02-10 16:33:39 +04:00
|
|
|
styleExpansionFactor, aMatrix);
|
2011-09-05 21:53:34 +04:00
|
|
|
}
|
|
|
|
|
2006-11-27 20:30:57 +03:00
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
|
2018-03-22 21:20:41 +03:00
|
|
|
/* static */ nscolor nsSVGUtils::GetFallbackOrPaintColor(
|
2012-08-05 23:10:21 +04:00
|
|
|
ComputedStyle* aComputedStyle, nsStyleSVGPaint nsStyleSVG::*aFillOrStroke) {
|
2018-03-22 21:20:41 +03:00
|
|
|
const nsStyleSVGPaint& paint = aComputedStyle->StyleSVG()->*aFillOrStroke;
|
|
|
|
ComputedStyle* styleIfVisited = aComputedStyle->GetStyleIfVisited();
|
2017-05-03 00:12:59 +03:00
|
|
|
nscolor color;
|
|
|
|
switch (paint.Type()) {
|
|
|
|
case eStyleSVGPaintType_Server:
|
|
|
|
case eStyleSVGPaintType_ContextStroke:
|
|
|
|
color = paint.GetFallbackType() == eStyleSVGFallbackType_Color
|
2018-06-27 10:07:37 +03:00
|
|
|
? paint.GetFallbackColor(aComputedStyle)
|
|
|
|
: NS_RGBA(0, 0, 0, 0);
|
2017-05-03 00:12:59 +03:00
|
|
|
break;
|
2017-05-04 00:00:40 +03:00
|
|
|
case eStyleSVGPaintType_ContextFill:
|
|
|
|
color = paint.GetFallbackType() == eStyleSVGFallbackType_Color
|
2018-06-27 10:07:37 +03:00
|
|
|
? paint.GetFallbackColor(aComputedStyle)
|
|
|
|
: NS_RGB(0, 0, 0);
|
2017-05-04 00:00:40 +03:00
|
|
|
break;
|
2017-05-03 00:12:59 +03:00
|
|
|
default:
|
2018-06-27 10:07:37 +03:00
|
|
|
color = paint.GetColor(aComputedStyle);
|
2017-05-03 00:12:59 +03:00
|
|
|
break;
|
|
|
|
}
|
2012-02-04 17:11:09 +04:00
|
|
|
if (styleIfVisited) {
|
|
|
|
const nsStyleSVGPaint& paintIfVisited =
|
2013-02-17 01:51:02 +04:00
|
|
|
styleIfVisited->StyleSVG()->*aFillOrStroke;
|
2012-02-04 17:11:09 +04:00
|
|
|
// To prevent Web content from detecting if a user has visited a URL
|
|
|
|
// (via URL loading triggered by paint servers or performance
|
|
|
|
// differences between paint servers or between a paint server and a
|
|
|
|
// color), we do not allow whether links are visited to change which
|
|
|
|
// paint server is used or switch between paint servers and simple
|
|
|
|
// colors. A :visited style may only override a simple color with
|
|
|
|
// another simple color.
|
2016-10-11 09:56:11 +03:00
|
|
|
if (paintIfVisited.Type() == eStyleSVGPaintType_Color &&
|
|
|
|
paint.Type() == eStyleSVGPaintType_Color) {
|
2018-06-27 10:07:37 +03:00
|
|
|
nscolor colors[2] = {color, paintIfVisited.GetColor(aComputedStyle)};
|
2018-03-22 21:20:41 +03:00
|
|
|
return ComputedStyle::CombineVisitedColors(
|
|
|
|
colors, aComputedStyle->RelevantLinkVisited());
|
2012-02-04 17:11:09 +04:00
|
|
|
}
|
|
|
|
}
|
2012-08-05 23:10:21 +04:00
|
|
|
return color;
|
|
|
|
}
|
|
|
|
|
2014-09-29 17:26:15 +04:00
|
|
|
/* static */ void nsSVGUtils::MakeFillPatternFor(
|
|
|
|
nsIFrame* aFrame, gfxContext* aContext, GeneralPattern* aOutPattern,
|
2017-05-18 23:03:41 +03:00
|
|
|
imgDrawingParams& aImgParams, SVGContextPaint* aContextPaint) {
|
2013-02-17 01:51:02 +04:00
|
|
|
const nsStyleSVG* style = aFrame->StyleSVG();
|
2016-10-11 09:56:11 +03:00
|
|
|
if (style->mFill.Type() == eStyleSVGPaintType_None) {
|
2017-05-18 23:03:41 +03:00
|
|
|
return;
|
2014-09-29 17:12:06 +04:00
|
|
|
}
|
2012-08-28 18:47:01 +04:00
|
|
|
|
2016-06-01 11:17:23 +03:00
|
|
|
const float opacity = aFrame->StyleEffects()->mOpacity;
|
|
|
|
|
|
|
|
float fillOpacity = GetOpacity(style->FillOpacitySource(),
|
|
|
|
style->mFillOpacity, aContextPaint);
|
|
|
|
if (opacity < 1.0f && nsSVGUtils::CanOptimizeOpacity(aFrame)) {
|
|
|
|
// Combine the group opacity into the fill opacity (we will have skipped
|
|
|
|
// creating an offscreen surface to apply the group opacity).
|
|
|
|
fillOpacity *= opacity;
|
|
|
|
}
|
|
|
|
|
2014-09-29 17:15:19 +04:00
|
|
|
const DrawTarget* dt = aContext->GetDrawTarget();
|
|
|
|
|
2012-08-05 23:10:21 +04:00
|
|
|
nsSVGPaintServerFrame* ps =
|
2018-09-25 23:16:49 +03:00
|
|
|
SVGObserverUtils::GetAndObservePaintServer(aFrame, &nsStyleSVG::mFill);
|
2017-05-18 23:03:41 +03:00
|
|
|
|
2014-09-29 17:12:06 +04:00
|
|
|
if (ps) {
|
2017-05-18 23:03:50 +03:00
|
|
|
RefPtr<gfxPattern> pattern =
|
2017-11-11 05:14:09 +03:00
|
|
|
ps->GetPaintServerPattern(aFrame, dt, aContext->CurrentMatrixDouble(),
|
2017-05-18 23:03:50 +03:00
|
|
|
&nsStyleSVG::mFill, fillOpacity, aImgParams);
|
2014-09-29 17:12:06 +04:00
|
|
|
if (pattern) {
|
2014-09-29 17:15:19 +04:00
|
|
|
pattern->CacheColorStops(dt);
|
2014-09-29 17:26:15 +04:00
|
|
|
aOutPattern->Init(*pattern->GetPattern(dt));
|
2017-05-18 23:03:41 +03:00
|
|
|
return;
|
2014-09-29 17:12:06 +04:00
|
|
|
}
|
|
|
|
}
|
2012-08-05 23:10:21 +04:00
|
|
|
|
2014-09-29 17:12:06 +04:00
|
|
|
if (aContextPaint) {
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<gfxPattern> pattern;
|
2016-10-11 09:56:11 +03:00
|
|
|
switch (style->mFill.Type()) {
|
2014-09-29 17:12:06 +04:00
|
|
|
case eStyleSVGPaintType_ContextFill:
|
2017-03-21 05:12:23 +03:00
|
|
|
pattern = aContextPaint->GetFillPattern(
|
2017-11-11 05:14:09 +03:00
|
|
|
dt, fillOpacity, aContext->CurrentMatrixDouble(), aImgParams);
|
2014-09-29 17:12:06 +04:00
|
|
|
break;
|
|
|
|
case eStyleSVGPaintType_ContextStroke:
|
2017-03-21 05:12:23 +03:00
|
|
|
pattern = aContextPaint->GetStrokePattern(
|
2017-11-11 05:14:09 +03:00
|
|
|
dt, fillOpacity, aContext->CurrentMatrixDouble(), aImgParams);
|
2014-09-29 17:12:06 +04:00
|
|
|
break;
|
|
|
|
default:;
|
|
|
|
}
|
|
|
|
if (pattern) {
|
2014-09-29 17:26:15 +04:00
|
|
|
aOutPattern->Init(*pattern->GetPattern(dt));
|
2017-05-18 23:03:41 +03:00
|
|
|
return;
|
2014-09-29 17:12:06 +04:00
|
|
|
}
|
2012-09-06 08:58:46 +04:00
|
|
|
}
|
|
|
|
|
2017-05-03 00:12:59 +03:00
|
|
|
if (style->mFill.GetFallbackType() == eStyleSVGFallbackType_None) {
|
2017-05-18 23:03:41 +03:00
|
|
|
return;
|
2017-05-03 00:12:59 +03:00
|
|
|
}
|
|
|
|
|
2012-08-05 23:10:21 +04:00
|
|
|
// On failure, use the fallback colour in case we have an
|
|
|
|
// objectBoundingBox where the width or height of the object is zero.
|
|
|
|
// See http://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBox
|
2018-03-22 21:20:41 +03:00
|
|
|
Color color(Color::FromABGR(
|
|
|
|
GetFallbackOrPaintColor(aFrame->Style(), &nsStyleSVG::mFill)));
|
2016-06-01 11:17:23 +03:00
|
|
|
color.a *= fillOpacity;
|
2014-10-17 15:53:16 +04:00
|
|
|
aOutPattern->InitColorPattern(ToDeviceColor(color));
|
2012-08-05 23:10:21 +04:00
|
|
|
}
|
|
|
|
|
2014-09-29 17:26:15 +04:00
|
|
|
/* static */ void nsSVGUtils::MakeStrokePatternFor(
|
|
|
|
nsIFrame* aFrame, gfxContext* aContext, GeneralPattern* aOutPattern,
|
2017-05-18 23:03:41 +03:00
|
|
|
imgDrawingParams& aImgParams, SVGContextPaint* aContextPaint) {
|
2013-02-17 01:51:02 +04:00
|
|
|
const nsStyleSVG* style = aFrame->StyleSVG();
|
2016-10-11 09:56:11 +03:00
|
|
|
if (style->mStroke.Type() == eStyleSVGPaintType_None) {
|
2017-05-18 23:03:41 +03:00
|
|
|
return;
|
2014-09-29 17:12:06 +04:00
|
|
|
}
|
2012-08-05 23:10:21 +04:00
|
|
|
|
2016-06-01 11:17:23 +03:00
|
|
|
const float opacity = aFrame->StyleEffects()->mOpacity;
|
|
|
|
|
|
|
|
float strokeOpacity = GetOpacity(style->StrokeOpacitySource(),
|
|
|
|
style->mStrokeOpacity, aContextPaint);
|
|
|
|
if (opacity < 1.0f && nsSVGUtils::CanOptimizeOpacity(aFrame)) {
|
|
|
|
// Combine the group opacity into the stroke opacity (we will have skipped
|
|
|
|
// creating an offscreen surface to apply the group opacity).
|
|
|
|
strokeOpacity *= opacity;
|
|
|
|
}
|
2014-09-29 17:15:19 +04:00
|
|
|
|
|
|
|
const DrawTarget* dt = aContext->GetDrawTarget();
|
|
|
|
|
2012-08-05 23:10:21 +04:00
|
|
|
nsSVGPaintServerFrame* ps =
|
2018-09-25 23:16:49 +03:00
|
|
|
SVGObserverUtils::GetAndObservePaintServer(aFrame, &nsStyleSVG::mStroke);
|
2017-05-18 23:03:41 +03:00
|
|
|
|
2014-09-29 17:12:06 +04:00
|
|
|
if (ps) {
|
2017-05-18 23:03:50 +03:00
|
|
|
RefPtr<gfxPattern> pattern = ps->GetPaintServerPattern(
|
2017-11-11 05:14:09 +03:00
|
|
|
aFrame, dt, aContext->CurrentMatrixDouble(), &nsStyleSVG::mStroke,
|
2017-05-18 23:03:50 +03:00
|
|
|
strokeOpacity, aImgParams);
|
2014-09-29 17:12:06 +04:00
|
|
|
if (pattern) {
|
2014-09-29 17:15:19 +04:00
|
|
|
pattern->CacheColorStops(dt);
|
2014-09-29 17:26:15 +04:00
|
|
|
aOutPattern->Init(*pattern->GetPattern(dt));
|
2017-05-18 23:03:41 +03:00
|
|
|
return;
|
2014-09-29 17:12:06 +04:00
|
|
|
}
|
|
|
|
}
|
2012-08-05 23:10:21 +04:00
|
|
|
|
2014-09-29 17:12:06 +04:00
|
|
|
if (aContextPaint) {
|
2015-10-18 08:24:48 +03:00
|
|
|
RefPtr<gfxPattern> pattern;
|
2016-10-11 09:56:11 +03:00
|
|
|
switch (style->mStroke.Type()) {
|
2014-09-29 17:12:06 +04:00
|
|
|
case eStyleSVGPaintType_ContextFill:
|
2017-03-21 05:12:23 +03:00
|
|
|
pattern = aContextPaint->GetFillPattern(
|
2017-11-11 05:14:09 +03:00
|
|
|
dt, strokeOpacity, aContext->CurrentMatrixDouble(), aImgParams);
|
2014-09-29 17:12:06 +04:00
|
|
|
break;
|
|
|
|
case eStyleSVGPaintType_ContextStroke:
|
2017-03-21 05:12:23 +03:00
|
|
|
pattern = aContextPaint->GetStrokePattern(
|
2017-11-11 05:14:09 +03:00
|
|
|
dt, strokeOpacity, aContext->CurrentMatrixDouble(), aImgParams);
|
2014-09-29 17:12:06 +04:00
|
|
|
break;
|
|
|
|
default:;
|
|
|
|
}
|
|
|
|
if (pattern) {
|
2014-09-29 17:26:15 +04:00
|
|
|
aOutPattern->Init(*pattern->GetPattern(dt));
|
2017-05-18 23:03:41 +03:00
|
|
|
return;
|
2014-09-29 17:12:06 +04:00
|
|
|
}
|
2012-09-06 08:58:46 +04:00
|
|
|
}
|
|
|
|
|
2017-05-03 00:12:59 +03:00
|
|
|
if (style->mStroke.GetFallbackType() == eStyleSVGFallbackType_None) {
|
2017-05-18 23:03:41 +03:00
|
|
|
return;
|
2017-05-03 00:12:59 +03:00
|
|
|
}
|
|
|
|
|
2012-08-05 23:10:21 +04:00
|
|
|
// On failure, use the fallback colour in case we have an
|
|
|
|
// objectBoundingBox where the width or height of the object is zero.
|
|
|
|
// See http://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBox
|
2018-03-22 21:20:41 +03:00
|
|
|
Color color(Color::FromABGR(
|
|
|
|
GetFallbackOrPaintColor(aFrame->Style(), &nsStyleSVG::mStroke)));
|
2016-06-01 11:17:23 +03:00
|
|
|
color.a *= strokeOpacity;
|
2014-10-17 15:53:16 +04:00
|
|
|
aOutPattern->InitColorPattern(ToDeviceColor(color));
|
2012-02-04 17:11:09 +04:00
|
|
|
}
|
2012-08-10 15:13:43 +04:00
|
|
|
|
2012-09-06 08:58:47 +04:00
|
|
|
/* static */ float nsSVGUtils::GetOpacity(nsStyleSVGOpacitySource aOpacityType,
|
|
|
|
const float& aOpacity,
|
2016-07-22 16:56:09 +03:00
|
|
|
SVGContextPaint* aContextPaint) {
|
2012-09-06 08:58:47 +04:00
|
|
|
float opacity = 1.0f;
|
|
|
|
switch (aOpacityType) {
|
|
|
|
case eStyleSVGOpacitySource_Normal:
|
|
|
|
opacity = aOpacity;
|
|
|
|
break;
|
2013-05-16 06:35:15 +04:00
|
|
|
case eStyleSVGOpacitySource_ContextFillOpacity:
|
2014-11-07 01:07:36 +03:00
|
|
|
if (aContextPaint) {
|
|
|
|
opacity = aContextPaint->GetFillOpacity();
|
2012-09-06 08:58:47 +04:00
|
|
|
} else {
|
2014-11-07 01:07:36 +03:00
|
|
|
NS_WARNING(
|
|
|
|
"Content used context-fill-opacity when not in a context element");
|
2012-09-06 08:58:47 +04:00
|
|
|
}
|
|
|
|
break;
|
2013-05-16 06:35:15 +04:00
|
|
|
case eStyleSVGOpacitySource_ContextStrokeOpacity:
|
2014-11-07 01:07:36 +03:00
|
|
|
if (aContextPaint) {
|
|
|
|
opacity = aContextPaint->GetStrokeOpacity();
|
2012-09-06 08:58:47 +04:00
|
|
|
} else {
|
2014-11-07 01:07:36 +03:00
|
|
|
NS_WARNING(
|
|
|
|
"Content used context-stroke-opacity when not in a context "
|
|
|
|
"element");
|
2012-09-06 08:58:47 +04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2018-06-18 08:43:11 +03:00
|
|
|
MOZ_ASSERT_UNREACHABLE(
|
|
|
|
"Unknown object opacity inheritance type for SVG "
|
|
|
|
"glyph");
|
2012-09-06 08:58:47 +04:00
|
|
|
}
|
|
|
|
return opacity;
|
|
|
|
}
|
|
|
|
|
2016-07-22 16:56:09 +03:00
|
|
|
bool nsSVGUtils::HasStroke(nsIFrame* aFrame, SVGContextPaint* aContextPaint) {
|
2013-02-17 01:51:02 +04:00
|
|
|
const nsStyleSVG* style = aFrame->StyleSVG();
|
2013-11-23 17:32:21 +04:00
|
|
|
return style->HasStroke() && GetStrokeWidth(aFrame, aContextPaint) > 0;
|
2012-08-10 15:13:43 +04:00
|
|
|
}
|
|
|
|
|
2016-07-22 16:56:09 +03:00
|
|
|
float nsSVGUtils::GetStrokeWidth(nsIFrame* aFrame,
|
|
|
|
SVGContextPaint* aContextPaint) {
|
2013-02-17 01:51:02 +04:00
|
|
|
const nsStyleSVG* style = aFrame->StyleSVG();
|
2016-06-03 10:16:39 +03:00
|
|
|
if (aContextPaint && style->StrokeWidthFromObject()) {
|
2013-05-16 06:35:12 +04:00
|
|
|
return aContextPaint->GetStrokeWidth();
|
2012-09-06 08:58:47 +04:00
|
|
|
}
|
|
|
|
|
2012-08-10 15:13:43 +04:00
|
|
|
nsIContent* content = aFrame->GetContent();
|
2018-04-13 01:41:00 +03:00
|
|
|
if (content->IsText()) {
|
2012-08-10 15:13:43 +04:00
|
|
|
content = content->GetParent();
|
|
|
|
}
|
|
|
|
|
|
|
|
nsSVGElement* ctx = static_cast<nsSVGElement*>(content);
|
|
|
|
|
2014-07-05 22:19:13 +04:00
|
|
|
return SVGContentUtils::CoordToFloat(ctx, style->mStrokeWidth);
|
2012-08-10 15:13:43 +04:00
|
|
|
}
|
|
|
|
|
2017-07-31 15:20:28 +03:00
|
|
|
void nsSVGUtils::SetupStrokeGeometry(nsIFrame* aFrame, gfxContext* aContext,
|
|
|
|
SVGContextPaint* aContextPaint) {
|
2018-03-11 19:11:24 +03:00
|
|
|
SVGContentUtils::AutoStrokeOptions strokeOptions;
|
|
|
|
SVGContentUtils::GetStrokeOptions(
|
|
|
|
&strokeOptions, static_cast<nsSVGElement*>(aFrame->GetContent()),
|
2018-03-22 21:20:41 +03:00
|
|
|
aFrame->Style(), aContextPaint);
|
2017-07-06 15:00:35 +03:00
|
|
|
|
2018-03-11 19:11:24 +03:00
|
|
|
if (strokeOptions.mLineWidth <= 0) {
|
|
|
|
return;
|
2014-09-29 17:12:06 +04:00
|
|
|
}
|
2012-08-10 15:13:43 +04:00
|
|
|
|
2018-03-11 19:11:24 +03:00
|
|
|
aContext->SetLineWidth(strokeOptions.mLineWidth);
|
|
|
|
aContext->SetLineCap(strokeOptions.mLineCap);
|
|
|
|
aContext->SetMiterLimit(strokeOptions.mMiterLimit);
|
|
|
|
aContext->SetLineJoin(strokeOptions.mLineJoin);
|
|
|
|
aContext->SetDash(strokeOptions.mDashPattern, strokeOptions.mDashLength,
|
|
|
|
strokeOptions.mDashOffset);
|
2012-08-10 15:13:43 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t nsSVGUtils::GetGeometryHitTestFlags(nsIFrame* aFrame) {
|
2012-08-22 19:56:38 +04:00
|
|
|
uint16_t flags = 0;
|
2012-08-10 15:13:43 +04:00
|
|
|
|
2018-08-14 11:37:37 +03:00
|
|
|
switch (aFrame->StyleUI()->mPointerEvents) {
|
2012-08-10 15:13:43 +04:00
|
|
|
case NS_STYLE_POINTER_EVENTS_NONE:
|
|
|
|
break;
|
|
|
|
case NS_STYLE_POINTER_EVENTS_AUTO:
|
|
|
|
case NS_STYLE_POINTER_EVENTS_VISIBLEPAINTED:
|
2013-02-17 01:51:02 +04:00
|
|
|
if (aFrame->StyleVisibility()->IsVisible()) {
|
2016-10-11 09:56:11 +03:00
|
|
|
if (aFrame->StyleSVG()->mFill.Type() != eStyleSVGPaintType_None)
|
2012-08-10 15:13:43 +04:00
|
|
|
flags |= SVG_HIT_TEST_FILL;
|
2016-10-11 09:56:11 +03:00
|
|
|
if (aFrame->StyleSVG()->mStroke.Type() != eStyleSVGPaintType_None)
|
2012-08-10 15:13:43 +04:00
|
|
|
flags |= SVG_HIT_TEST_STROKE;
|
2013-02-17 01:51:02 +04:00
|
|
|
if (aFrame->StyleSVG()->mStrokeOpacity > 0)
|
2012-08-10 15:13:43 +04:00
|
|
|
flags |= SVG_HIT_TEST_CHECK_MRECT;
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
|
|
|
break;
|
2012-08-10 15:13:43 +04:00
|
|
|
case NS_STYLE_POINTER_EVENTS_VISIBLEFILL:
|
2013-02-17 01:51:02 +04:00
|
|
|
if (aFrame->StyleVisibility()->IsVisible()) {
|
2012-08-10 15:13:43 +04:00
|
|
|
flags |= SVG_HIT_TEST_FILL;
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
|
|
|
break;
|
2012-08-10 15:13:43 +04:00
|
|
|
case NS_STYLE_POINTER_EVENTS_VISIBLESTROKE:
|
2013-02-17 01:51:02 +04:00
|
|
|
if (aFrame->StyleVisibility()->IsVisible()) {
|
2012-08-10 15:13:43 +04:00
|
|
|
flags |= SVG_HIT_TEST_STROKE;
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
|
|
|
break;
|
2012-08-10 15:13:43 +04:00
|
|
|
case NS_STYLE_POINTER_EVENTS_VISIBLE:
|
2013-02-17 01:51:02 +04:00
|
|
|
if (aFrame->StyleVisibility()->IsVisible()) {
|
2012-08-10 15:13:43 +04:00
|
|
|
flags |= SVG_HIT_TEST_FILL | SVG_HIT_TEST_STROKE;
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
|
|
|
break;
|
2012-08-10 15:13:43 +04:00
|
|
|
case NS_STYLE_POINTER_EVENTS_PAINTED:
|
2016-10-11 09:56:11 +03:00
|
|
|
if (aFrame->StyleSVG()->mFill.Type() != eStyleSVGPaintType_None)
|
2012-08-10 15:13:43 +04:00
|
|
|
flags |= SVG_HIT_TEST_FILL;
|
2016-10-11 09:56:11 +03:00
|
|
|
if (aFrame->StyleSVG()->mStroke.Type() != eStyleSVGPaintType_None)
|
2012-08-10 15:13:43 +04:00
|
|
|
flags |= SVG_HIT_TEST_STROKE;
|
2013-02-17 01:51:02 +04:00
|
|
|
if (aFrame->StyleSVG()->mStrokeOpacity) flags |= SVG_HIT_TEST_CHECK_MRECT;
|
2012-08-10 15:13:43 +04:00
|
|
|
break;
|
|
|
|
case NS_STYLE_POINTER_EVENTS_FILL:
|
|
|
|
flags |= SVG_HIT_TEST_FILL;
|
|
|
|
break;
|
|
|
|
case NS_STYLE_POINTER_EVENTS_STROKE:
|
|
|
|
flags |= SVG_HIT_TEST_STROKE;
|
|
|
|
break;
|
|
|
|
case NS_STYLE_POINTER_EVENTS_ALL:
|
|
|
|
flags |= SVG_HIT_TEST_FILL | SVG_HIT_TEST_STROKE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
NS_ERROR("not reached");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return flags;
|
|
|
|
}
|
|
|
|
|
2016-07-21 16:33:11 +03:00
|
|
|
void nsSVGUtils::PaintSVGGlyph(Element* aElement, gfxContext* aContext) {
|
2012-12-12 03:15:07 +04:00
|
|
|
nsIFrame* frame = aElement->GetPrimaryFrame();
|
2017-02-09 21:24:31 +03:00
|
|
|
nsSVGDisplayableFrame* svgFrame = do_QueryFrame(frame);
|
2013-08-19 17:08:43 +04:00
|
|
|
if (!svgFrame) {
|
2017-05-18 23:03:41 +03:00
|
|
|
return;
|
2012-12-12 03:15:07 +04:00
|
|
|
}
|
2014-08-29 23:42:07 +04:00
|
|
|
gfxMatrix m;
|
2015-03-03 14:08:59 +03:00
|
|
|
if (frame->GetContent()->IsSVGElement()) {
|
2014-08-29 23:42:07 +04:00
|
|
|
// PaintSVG() expects the passed transform to be the transform to its own
|
|
|
|
// SVG user space, so we need to account for any 'transform' attribute:
|
|
|
|
m = static_cast<nsSVGElement*>(frame->GetContent())
|
2015-12-03 01:36:23 +03:00
|
|
|
->PrependLocalTransformsTo(gfxMatrix(), eUserSpaceToParent);
|
2014-08-29 23:42:07 +04:00
|
|
|
}
|
2017-05-18 23:03:41 +03:00
|
|
|
|
2018-03-18 21:09:51 +03:00
|
|
|
// SVG-in-OpenType is not allowed to paint external resources, so we can
|
2017-05-18 23:03:41 +03:00
|
|
|
// just pass a dummy params into PatintSVG.
|
|
|
|
imgDrawingParams dummy;
|
|
|
|
svgFrame->PaintSVG(*aContext, m, dummy);
|
2012-12-12 03:15:07 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool nsSVGUtils::GetSVGGlyphExtents(Element* aElement,
|
|
|
|
const gfxMatrix& aSVGToAppSpace,
|
|
|
|
gfxRect* aResult) {
|
|
|
|
nsIFrame* frame = aElement->GetPrimaryFrame();
|
2017-02-09 21:24:31 +03:00
|
|
|
nsSVGDisplayableFrame* svgFrame = do_QueryFrame(frame);
|
2013-08-19 17:08:43 +04:00
|
|
|
if (!svgFrame) {
|
|
|
|
return false;
|
2012-12-12 03:15:07 +04:00
|
|
|
}
|
2013-10-25 12:09:30 +04:00
|
|
|
|
2013-12-27 00:13:57 +04:00
|
|
|
gfxMatrix transform(aSVGToAppSpace);
|
2013-10-25 12:09:30 +04:00
|
|
|
nsIContent* content = frame->GetContent();
|
2015-03-03 14:08:59 +03:00
|
|
|
if (content->IsSVGElement()) {
|
2013-12-27 00:13:57 +04:00
|
|
|
transform = static_cast<nsSVGElement*>(content)->PrependLocalTransformsTo(
|
|
|
|
aSVGToAppSpace);
|
2013-10-25 12:09:30 +04:00
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2013-12-30 10:50:07 +04:00
|
|
|
*aResult =
|
|
|
|
svgFrame
|
|
|
|
->GetBBoxContribution(gfx::ToMatrix(transform),
|
2013-08-19 17:08:43 +04:00
|
|
|
nsSVGUtils::eBBoxIncludeFill |
|
|
|
|
nsSVGUtils::eBBoxIncludeFillGeometry |
|
|
|
|
nsSVGUtils::eBBoxIncludeStroke |
|
|
|
|
nsSVGUtils::eBBoxIncludeStrokeGeometry |
|
2013-11-01 15:04:01 +04:00
|
|
|
nsSVGUtils::eBBoxIncludeMarkers)
|
|
|
|
.ToThebesRect();
|
2013-08-19 17:08:43 +04:00
|
|
|
return true;
|
2012-12-12 03:15:07 +04:00
|
|
|
}
|
2013-02-11 10:22:18 +04:00
|
|
|
|
|
|
|
nsRect nsSVGUtils::ToCanvasBounds(const gfxRect& aUserspaceRect,
|
|
|
|
const gfxMatrix& aToCanvas,
|
|
|
|
const nsPresContext* presContext) {
|
|
|
|
return nsLayoutUtils::RoundGfxRectToAppRect(
|
2017-07-05 18:22:00 +03:00
|
|
|
aToCanvas.TransformBounds(aUserspaceRect),
|
2013-02-11 10:22:18 +04:00
|
|
|
presContext->AppUnitsPerDevPixel());
|
|
|
|
}
|
2017-01-24 18:22:43 +03:00
|
|
|
|
|
|
|
gfxMatrix nsSVGUtils::GetCSSPxToDevPxMatrix(nsIFrame* aNonSVGFrame) {
|
|
|
|
int32_t appUnitsPerDevPixel =
|
|
|
|
aNonSVGFrame->PresContext()->AppUnitsPerDevPixel();
|
|
|
|
float devPxPerCSSPx =
|
|
|
|
1 / nsPresContext::AppUnitsToFloatCSSPixels(appUnitsPerDevPixel);
|
|
|
|
|
|
|
|
return gfxMatrix(devPxPerCSSPx, 0.0, 0.0, devPxPerCSSPx, 0.0, 0.0);
|
2017-02-22 19:58:50 +03:00
|
|
|
}
|