layout/svg/nsSVGUtils.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 // Main header first:
michael@0 7 // This is also necessary to ensure our definition of M_SQRT1_2 is picked up
michael@0 8 #include "nsSVGUtils.h"
michael@0 9 #include <algorithm>
michael@0 10
michael@0 11 // Keep others in (case-insensitive) order:
michael@0 12 #include "gfx2DGlue.h"
michael@0 13 #include "gfxContext.h"
michael@0 14 #include "gfxMatrix.h"
michael@0 15 #include "gfxPlatform.h"
michael@0 16 #include "gfxRect.h"
michael@0 17 #include "gfxUtils.h"
michael@0 18 #include "mozilla/gfx/2D.h"
michael@0 19 #include "mozilla/Preferences.h"
michael@0 20 #include "nsCSSFrameConstructor.h"
michael@0 21 #include "nsDisplayList.h"
michael@0 22 #include "nsFilterInstance.h"
michael@0 23 #include "nsFrameList.h"
michael@0 24 #include "nsGkAtoms.h"
michael@0 25 #include "nsIContent.h"
michael@0 26 #include "nsIDocument.h"
michael@0 27 #include "nsIFrame.h"
michael@0 28 #include "nsIPresShell.h"
michael@0 29 #include "nsISVGChildFrame.h"
michael@0 30 #include "nsPresContext.h"
michael@0 31 #include "nsRenderingContext.h"
michael@0 32 #include "nsStyleCoord.h"
michael@0 33 #include "nsStyleStruct.h"
michael@0 34 #include "nsSVGClipPathFrame.h"
michael@0 35 #include "nsSVGContainerFrame.h"
michael@0 36 #include "nsSVGEffects.h"
michael@0 37 #include "nsSVGFilterPaintCallback.h"
michael@0 38 #include "nsSVGForeignObjectFrame.h"
michael@0 39 #include "gfxSVGGlyphs.h"
michael@0 40 #include "nsSVGInnerSVGFrame.h"
michael@0 41 #include "nsSVGIntegrationUtils.h"
michael@0 42 #include "nsSVGLength2.h"
michael@0 43 #include "nsSVGMaskFrame.h"
michael@0 44 #include "nsSVGOuterSVGFrame.h"
michael@0 45 #include "mozilla/dom/SVGPathElement.h"
michael@0 46 #include "nsSVGPathGeometryElement.h"
michael@0 47 #include "nsSVGPathGeometryFrame.h"
michael@0 48 #include "nsSVGPaintServerFrame.h"
michael@0 49 #include "mozilla/dom/SVGSVGElement.h"
michael@0 50 #include "nsTextFrame.h"
michael@0 51 #include "SVGContentUtils.h"
michael@0 52 #include "mozilla/unused.h"
michael@0 53
michael@0 54 using namespace mozilla;
michael@0 55 using namespace mozilla::dom;
michael@0 56 using namespace mozilla::gfx;
michael@0 57
michael@0 58 static bool sSVGDisplayListHitTestingEnabled;
michael@0 59 static bool sSVGDisplayListPaintingEnabled;
michael@0 60
michael@0 61 bool
michael@0 62 NS_SVGDisplayListHitTestingEnabled()
michael@0 63 {
michael@0 64 return sSVGDisplayListHitTestingEnabled;
michael@0 65 }
michael@0 66
michael@0 67 bool
michael@0 68 NS_SVGDisplayListPaintingEnabled()
michael@0 69 {
michael@0 70 return sSVGDisplayListPaintingEnabled;
michael@0 71 }
michael@0 72
michael@0 73 // we only take the address of this:
michael@0 74 static mozilla::gfx::UserDataKey sSVGAutoRenderStateKey;
michael@0 75
michael@0 76 SVGAutoRenderState::SVGAutoRenderState(nsRenderingContext *aContext,
michael@0 77 RenderMode aMode
michael@0 78 MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
michael@0 79 : mContext(aContext)
michael@0 80 , mOriginalRenderState(nullptr)
michael@0 81 , mMode(aMode)
michael@0 82 , mPaintingToWindow(false)
michael@0 83 {
michael@0 84 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
michael@0 85 mOriginalRenderState = aContext->RemoveUserData(&sSVGAutoRenderStateKey);
michael@0 86 // We always remove ourselves from aContext before it dies, so
michael@0 87 // passing nullptr as the destroy function is okay.
michael@0 88 aContext->AddUserData(&sSVGAutoRenderStateKey, this, nullptr);
michael@0 89 }
michael@0 90
michael@0 91 SVGAutoRenderState::~SVGAutoRenderState()
michael@0 92 {
michael@0 93 mContext->RemoveUserData(&sSVGAutoRenderStateKey);
michael@0 94 if (mOriginalRenderState) {
michael@0 95 mContext->AddUserData(&sSVGAutoRenderStateKey, mOriginalRenderState, nullptr);
michael@0 96 }
michael@0 97 }
michael@0 98
michael@0 99 void
michael@0 100 SVGAutoRenderState::SetPaintingToWindow(bool aPaintingToWindow)
michael@0 101 {
michael@0 102 mPaintingToWindow = aPaintingToWindow;
michael@0 103 }
michael@0 104
michael@0 105 /* static */ SVGAutoRenderState::RenderMode
michael@0 106 SVGAutoRenderState::GetRenderMode(nsRenderingContext *aContext)
michael@0 107 {
michael@0 108 void *state = aContext->GetUserData(&sSVGAutoRenderStateKey);
michael@0 109 if (state) {
michael@0 110 return static_cast<SVGAutoRenderState*>(state)->mMode;
michael@0 111 }
michael@0 112 return NORMAL;
michael@0 113 }
michael@0 114
michael@0 115 /* static */ bool
michael@0 116 SVGAutoRenderState::IsPaintingToWindow(nsRenderingContext *aContext)
michael@0 117 {
michael@0 118 void *state = aContext->GetUserData(&sSVGAutoRenderStateKey);
michael@0 119 if (state) {
michael@0 120 return static_cast<SVGAutoRenderState*>(state)->mPaintingToWindow;
michael@0 121 }
michael@0 122 return false;
michael@0 123 }
michael@0 124
michael@0 125 void
michael@0 126 nsSVGUtils::Init()
michael@0 127 {
michael@0 128 Preferences::AddBoolVarCache(&sSVGDisplayListHitTestingEnabled,
michael@0 129 "svg.display-lists.hit-testing.enabled");
michael@0 130
michael@0 131 Preferences::AddBoolVarCache(&sSVGDisplayListPaintingEnabled,
michael@0 132 "svg.display-lists.painting.enabled");
michael@0 133 }
michael@0 134
michael@0 135 nsSVGDisplayContainerFrame*
michael@0 136 nsSVGUtils::GetNearestSVGViewport(nsIFrame *aFrame)
michael@0 137 {
michael@0 138 NS_ASSERTION(aFrame->IsFrameOfType(nsIFrame::eSVG), "SVG frame expected");
michael@0 139 if (aFrame->GetType() == nsGkAtoms::svgOuterSVGFrame) {
michael@0 140 return nullptr;
michael@0 141 }
michael@0 142 while ((aFrame = aFrame->GetParent())) {
michael@0 143 NS_ASSERTION(aFrame->IsFrameOfType(nsIFrame::eSVG), "SVG frame expected");
michael@0 144 if (aFrame->GetType() == nsGkAtoms::svgInnerSVGFrame ||
michael@0 145 aFrame->GetType() == nsGkAtoms::svgOuterSVGFrame) {
michael@0 146 return do_QueryFrame(aFrame);
michael@0 147 }
michael@0 148 }
michael@0 149 NS_NOTREACHED("This is not reached. It's only needed to compile.");
michael@0 150 return nullptr;
michael@0 151 }
michael@0 152
michael@0 153 nsRect
michael@0 154 nsSVGUtils::GetPostFilterVisualOverflowRect(nsIFrame *aFrame,
michael@0 155 const nsRect &aPreFilterRect)
michael@0 156 {
michael@0 157 NS_ABORT_IF_FALSE(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT,
michael@0 158 "Called on invalid frame type");
michael@0 159
michael@0 160 nsSVGFilterProperty *property = nsSVGEffects::GetFilterProperty(aFrame);
michael@0 161 if (!property || !property->ReferencesValidResources()) {
michael@0 162 return aPreFilterRect;
michael@0 163 }
michael@0 164
michael@0 165 return nsFilterInstance::GetPostFilterBounds(aFrame, nullptr, &aPreFilterRect);
michael@0 166 }
michael@0 167
michael@0 168 bool
michael@0 169 nsSVGUtils::OuterSVGIsCallingReflowSVG(nsIFrame *aFrame)
michael@0 170 {
michael@0 171 return GetOuterSVGFrame(aFrame)->IsCallingReflowSVG();
michael@0 172 }
michael@0 173
michael@0 174 bool
michael@0 175 nsSVGUtils::AnyOuterSVGIsCallingReflowSVG(nsIFrame* aFrame)
michael@0 176 {
michael@0 177 nsSVGOuterSVGFrame* outer = GetOuterSVGFrame(aFrame);
michael@0 178 do {
michael@0 179 if (outer->IsCallingReflowSVG()) {
michael@0 180 return true;
michael@0 181 }
michael@0 182 outer = GetOuterSVGFrame(outer->GetParent());
michael@0 183 } while (outer);
michael@0 184 return false;
michael@0 185 }
michael@0 186
michael@0 187 void
michael@0 188 nsSVGUtils::ScheduleReflowSVG(nsIFrame *aFrame)
michael@0 189 {
michael@0 190 NS_ABORT_IF_FALSE(aFrame->IsFrameOfType(nsIFrame::eSVG),
michael@0 191 "Passed bad frame!");
michael@0 192
michael@0 193 // If this is triggered, the callers should be fixed to call us before
michael@0 194 // ReflowSVG is called. If we try to mark dirty bits on frames while we're
michael@0 195 // in the process of removing them, things will get messed up.
michael@0 196 NS_ASSERTION(!OuterSVGIsCallingReflowSVG(aFrame),
michael@0 197 "Do not call under nsISVGChildFrame::ReflowSVG!");
michael@0 198
michael@0 199 // We don't call nsSVGEffects::InvalidateRenderingObservers here because
michael@0 200 // we should only be called under InvalidateAndScheduleReflowSVG (which
michael@0 201 // calls InvalidateBounds) or nsSVGDisplayContainerFrame::InsertFrames
michael@0 202 // (at which point the frame has no observers).
michael@0 203
michael@0 204 if (aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) {
michael@0 205 return;
michael@0 206 }
michael@0 207
michael@0 208 if (aFrame->GetStateBits() &
michael@0 209 (NS_FRAME_IS_DIRTY | NS_FRAME_FIRST_REFLOW)) {
michael@0 210 // Nothing to do if we're already dirty, or if the outer-<svg>
michael@0 211 // hasn't yet had its initial reflow.
michael@0 212 return;
michael@0 213 }
michael@0 214
michael@0 215 nsSVGOuterSVGFrame *outerSVGFrame = nullptr;
michael@0 216
michael@0 217 // We must not add dirty bits to the nsSVGOuterSVGFrame or else
michael@0 218 // PresShell::FrameNeedsReflow won't work when we pass it in below.
michael@0 219 if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG) {
michael@0 220 outerSVGFrame = static_cast<nsSVGOuterSVGFrame*>(aFrame);
michael@0 221 } else {
michael@0 222 aFrame->AddStateBits(NS_FRAME_IS_DIRTY);
michael@0 223
michael@0 224 nsIFrame *f = aFrame->GetParent();
michael@0 225 while (f && !(f->GetStateBits() & NS_STATE_IS_OUTER_SVG)) {
michael@0 226 if (f->GetStateBits() &
michael@0 227 (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN)) {
michael@0 228 return;
michael@0 229 }
michael@0 230 f->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
michael@0 231 f = f->GetParent();
michael@0 232 NS_ABORT_IF_FALSE(f->IsFrameOfType(nsIFrame::eSVG),
michael@0 233 "NS_STATE_IS_OUTER_SVG check above not valid!");
michael@0 234 }
michael@0 235
michael@0 236 outerSVGFrame = static_cast<nsSVGOuterSVGFrame*>(f);
michael@0 237
michael@0 238 NS_ABORT_IF_FALSE(outerSVGFrame &&
michael@0 239 outerSVGFrame->GetType() == nsGkAtoms::svgOuterSVGFrame,
michael@0 240 "Did not find nsSVGOuterSVGFrame!");
michael@0 241 }
michael@0 242
michael@0 243 if (outerSVGFrame->GetStateBits() & NS_FRAME_IN_REFLOW) {
michael@0 244 // We're currently under an nsSVGOuterSVGFrame::Reflow call so there is no
michael@0 245 // need to call PresShell::FrameNeedsReflow, since we have an
michael@0 246 // nsSVGOuterSVGFrame::DidReflow call pending.
michael@0 247 return;
michael@0 248 }
michael@0 249
michael@0 250 nsFrameState dirtyBit =
michael@0 251 (outerSVGFrame == aFrame ? NS_FRAME_IS_DIRTY : NS_FRAME_HAS_DIRTY_CHILDREN);
michael@0 252
michael@0 253 aFrame->PresContext()->PresShell()->FrameNeedsReflow(
michael@0 254 outerSVGFrame, nsIPresShell::eResize, dirtyBit);
michael@0 255 }
michael@0 256
michael@0 257 bool
michael@0 258 nsSVGUtils::NeedsReflowSVG(nsIFrame *aFrame)
michael@0 259 {
michael@0 260 NS_ABORT_IF_FALSE(aFrame->IsFrameOfType(nsIFrame::eSVG),
michael@0 261 "SVG uses bits differently!");
michael@0 262
michael@0 263 // The flags we test here may change, hence why we have this separate
michael@0 264 // function.
michael@0 265 return NS_SUBTREE_DIRTY(aFrame);
michael@0 266 }
michael@0 267
michael@0 268 void
michael@0 269 nsSVGUtils::NotifyAncestorsOfFilterRegionChange(nsIFrame *aFrame)
michael@0 270 {
michael@0 271 NS_ABORT_IF_FALSE(!(aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG),
michael@0 272 "Not expecting to be called on the outer SVG Frame");
michael@0 273
michael@0 274 aFrame = aFrame->GetParent();
michael@0 275
michael@0 276 while (aFrame) {
michael@0 277 if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG)
michael@0 278 return;
michael@0 279
michael@0 280 nsSVGFilterProperty *property = nsSVGEffects::GetFilterProperty(aFrame);
michael@0 281 if (property) {
michael@0 282 property->Invalidate();
michael@0 283 }
michael@0 284 aFrame = aFrame->GetParent();
michael@0 285 }
michael@0 286 }
michael@0 287
michael@0 288 float
michael@0 289 nsSVGUtils::ObjectSpace(const gfxRect &aRect, const nsSVGLength2 *aLength)
michael@0 290 {
michael@0 291 float axis;
michael@0 292
michael@0 293 switch (aLength->GetCtxType()) {
michael@0 294 case SVGContentUtils::X:
michael@0 295 axis = aRect.Width();
michael@0 296 break;
michael@0 297 case SVGContentUtils::Y:
michael@0 298 axis = aRect.Height();
michael@0 299 break;
michael@0 300 case SVGContentUtils::XY:
michael@0 301 axis = float(SVGContentUtils::ComputeNormalizedHypotenuse(
michael@0 302 aRect.Width(), aRect.Height()));
michael@0 303 break;
michael@0 304 default:
michael@0 305 NS_NOTREACHED("unexpected ctx type");
michael@0 306 axis = 0.0f;
michael@0 307 break;
michael@0 308 }
michael@0 309 if (aLength->IsPercentage()) {
michael@0 310 // Multiply first to avoid precision errors:
michael@0 311 return axis * aLength->GetAnimValInSpecifiedUnits() / 100;
michael@0 312 }
michael@0 313 return aLength->GetAnimValue(static_cast<SVGSVGElement*>(nullptr)) * axis;
michael@0 314 }
michael@0 315
michael@0 316 float
michael@0 317 nsSVGUtils::UserSpace(nsSVGElement *aSVGElement, const nsSVGLength2 *aLength)
michael@0 318 {
michael@0 319 return aLength->GetAnimValue(aSVGElement);
michael@0 320 }
michael@0 321
michael@0 322 float
michael@0 323 nsSVGUtils::UserSpace(nsIFrame *aNonSVGContext, const nsSVGLength2 *aLength)
michael@0 324 {
michael@0 325 return aLength->GetAnimValue(aNonSVGContext);
michael@0 326 }
michael@0 327
michael@0 328 nsSVGOuterSVGFrame *
michael@0 329 nsSVGUtils::GetOuterSVGFrame(nsIFrame *aFrame)
michael@0 330 {
michael@0 331 while (aFrame) {
michael@0 332 if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG) {
michael@0 333 return static_cast<nsSVGOuterSVGFrame*>(aFrame);
michael@0 334 }
michael@0 335 aFrame = aFrame->GetParent();
michael@0 336 }
michael@0 337
michael@0 338 return nullptr;
michael@0 339 }
michael@0 340
michael@0 341 nsIFrame*
michael@0 342 nsSVGUtils::GetOuterSVGFrameAndCoveredRegion(nsIFrame* aFrame, nsRect* aRect)
michael@0 343 {
michael@0 344 nsISVGChildFrame* svg = do_QueryFrame(aFrame);
michael@0 345 if (!svg)
michael@0 346 return nullptr;
michael@0 347 *aRect = (aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) ?
michael@0 348 nsRect(0, 0, 0, 0) : svg->GetCoveredRegion();
michael@0 349 return GetOuterSVGFrame(aFrame);
michael@0 350 }
michael@0 351
michael@0 352 gfxMatrix
michael@0 353 nsSVGUtils::GetCanvasTM(nsIFrame *aFrame, uint32_t aFor,
michael@0 354 nsIFrame* aTransformRoot)
michael@0 355 {
michael@0 356 // XXX yuck, we really need a common interface for GetCanvasTM
michael@0 357
michael@0 358 if (!aFrame->IsFrameOfType(nsIFrame::eSVG)) {
michael@0 359 return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aFrame);
michael@0 360 }
michael@0 361
michael@0 362 if (!(aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) &&
michael@0 363 !aTransformRoot) {
michael@0 364 if ((aFor == nsISVGChildFrame::FOR_PAINTING &&
michael@0 365 NS_SVGDisplayListPaintingEnabled()) ||
michael@0 366 (aFor == nsISVGChildFrame::FOR_HIT_TESTING &&
michael@0 367 NS_SVGDisplayListHitTestingEnabled())) {
michael@0 368 return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aFrame);
michael@0 369 }
michael@0 370 }
michael@0 371
michael@0 372 nsIAtom* type = aFrame->GetType();
michael@0 373 if (type == nsGkAtoms::svgForeignObjectFrame) {
michael@0 374 return static_cast<nsSVGForeignObjectFrame*>(aFrame)->
michael@0 375 GetCanvasTM(aFor, aTransformRoot);
michael@0 376 }
michael@0 377 if (type == nsGkAtoms::svgOuterSVGFrame) {
michael@0 378 return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aFrame);
michael@0 379 }
michael@0 380
michael@0 381 nsSVGContainerFrame *containerFrame = do_QueryFrame(aFrame);
michael@0 382 if (containerFrame) {
michael@0 383 return containerFrame->GetCanvasTM(aFor, aTransformRoot);
michael@0 384 }
michael@0 385
michael@0 386 return static_cast<nsSVGPathGeometryFrame*>(aFrame)->
michael@0 387 GetCanvasTM(aFor, aTransformRoot);
michael@0 388 }
michael@0 389
michael@0 390 gfxMatrix
michael@0 391 nsSVGUtils::GetUserToCanvasTM(nsIFrame *aFrame, uint32_t aFor)
michael@0 392 {
michael@0 393 NS_ASSERTION(aFor == nsISVGChildFrame::FOR_OUTERSVG_TM,
michael@0 394 "Unexpected aFor?");
michael@0 395
michael@0 396 nsISVGChildFrame* svgFrame = do_QueryFrame(aFrame);
michael@0 397 NS_ASSERTION(svgFrame, "bad frame");
michael@0 398
michael@0 399 gfxMatrix tm;
michael@0 400 if (svgFrame) {
michael@0 401 nsSVGElement *content = static_cast<nsSVGElement*>(aFrame->GetContent());
michael@0 402 tm = content->PrependLocalTransformsTo(
michael@0 403 GetCanvasTM(aFrame->GetParent(), aFor),
michael@0 404 nsSVGElement::eUserSpaceToParent);
michael@0 405 }
michael@0 406 return tm;
michael@0 407 }
michael@0 408
michael@0 409 void
michael@0 410 nsSVGUtils::NotifyChildrenOfSVGChange(nsIFrame *aFrame, uint32_t aFlags)
michael@0 411 {
michael@0 412 nsIFrame *kid = aFrame->GetFirstPrincipalChild();
michael@0 413
michael@0 414 while (kid) {
michael@0 415 nsISVGChildFrame* SVGFrame = do_QueryFrame(kid);
michael@0 416 if (SVGFrame) {
michael@0 417 SVGFrame->NotifySVGChanged(aFlags);
michael@0 418 } else {
michael@0 419 NS_ASSERTION(kid->IsFrameOfType(nsIFrame::eSVG) || kid->IsSVGText(),
michael@0 420 "SVG frame expected");
michael@0 421 // recurse into the children of container frames e.g. <clipPath>, <mask>
michael@0 422 // in case they have child frames with transformation matrices
michael@0 423 if (kid->IsFrameOfType(nsIFrame::eSVG)) {
michael@0 424 NotifyChildrenOfSVGChange(kid, aFlags);
michael@0 425 }
michael@0 426 }
michael@0 427 kid = kid->GetNextSibling();
michael@0 428 }
michael@0 429 }
michael@0 430
michael@0 431 // ************************************************************
michael@0 432
michael@0 433 class SVGPaintCallback : public nsSVGFilterPaintCallback
michael@0 434 {
michael@0 435 public:
michael@0 436 virtual void Paint(nsRenderingContext *aContext, nsIFrame *aTarget,
michael@0 437 const nsIntRect* aDirtyRect,
michael@0 438 nsIFrame* aTransformRoot) MOZ_OVERRIDE
michael@0 439 {
michael@0 440 nsISVGChildFrame *svgChildFrame = do_QueryFrame(aTarget);
michael@0 441 NS_ASSERTION(svgChildFrame, "Expected SVG frame here");
michael@0 442
michael@0 443 nsIntRect* dirtyRect = nullptr;
michael@0 444 nsIntRect tmpDirtyRect;
michael@0 445
michael@0 446 // aDirtyRect is in user-space pixels, we need to convert to
michael@0 447 // outer-SVG-frame-relative device pixels.
michael@0 448 if (aDirtyRect) {
michael@0 449 gfxMatrix userToDeviceSpace =
michael@0 450 nsSVGUtils::GetCanvasTM(aTarget, nsISVGChildFrame::FOR_PAINTING, aTransformRoot);
michael@0 451 if (userToDeviceSpace.IsSingular()) {
michael@0 452 return;
michael@0 453 }
michael@0 454 gfxRect dirtyBounds = userToDeviceSpace.TransformBounds(
michael@0 455 gfxRect(aDirtyRect->x, aDirtyRect->y, aDirtyRect->width, aDirtyRect->height));
michael@0 456 dirtyBounds.RoundOut();
michael@0 457 if (gfxUtils::GfxRectToIntRect(dirtyBounds, &tmpDirtyRect)) {
michael@0 458 dirtyRect = &tmpDirtyRect;
michael@0 459 }
michael@0 460 }
michael@0 461
michael@0 462 svgChildFrame->PaintSVG(aContext, dirtyRect, aTransformRoot);
michael@0 463 }
michael@0 464 };
michael@0 465
michael@0 466 void
michael@0 467 nsSVGUtils::PaintFrameWithEffects(nsRenderingContext *aContext,
michael@0 468 const nsIntRect *aDirtyRect,
michael@0 469 nsIFrame *aFrame,
michael@0 470 nsIFrame *aTransformRoot)
michael@0 471 {
michael@0 472 NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
michael@0 473 (aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) ||
michael@0 474 aFrame->PresContext()->IsGlyph(),
michael@0 475 "If display lists are enabled, only painting of non-display "
michael@0 476 "SVG should take this code path");
michael@0 477
michael@0 478 nsISVGChildFrame *svgChildFrame = do_QueryFrame(aFrame);
michael@0 479 if (!svgChildFrame)
michael@0 480 return;
michael@0 481
michael@0 482 float opacity = aFrame->StyleDisplay()->mOpacity;
michael@0 483 if (opacity == 0.0f)
michael@0 484 return;
michael@0 485
michael@0 486 const nsIContent* content = aFrame->GetContent();
michael@0 487 if (content->IsSVG() &&
michael@0 488 !static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
michael@0 489 return;
michael@0 490 }
michael@0 491
michael@0 492 /* Properties are added lazily and may have been removed by a restyle,
michael@0 493 so make sure all applicable ones are set again. */
michael@0 494
michael@0 495 nsSVGEffects::EffectProperties effectProperties =
michael@0 496 nsSVGEffects::GetEffectProperties(aFrame);
michael@0 497
michael@0 498 bool isOK = effectProperties.HasNoFilterOrHasValidFilter();
michael@0 499
michael@0 500 if (aDirtyRect &&
michael@0 501 !(aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
michael@0 502 // Here we convert aFrame's paint bounds to outer-<svg> device space,
michael@0 503 // compare it to aDirtyRect, and return early if they don't intersect.
michael@0 504 // We don't do this optimization for nondisplay SVG since nondisplay
michael@0 505 // SVG doesn't maintain bounds/overflow rects.
michael@0 506 nsRect overflowRect = aFrame->GetVisualOverflowRectRelativeToSelf();
michael@0 507 if (aFrame->IsFrameOfType(nsIFrame::eSVGGeometry) ||
michael@0 508 aFrame->IsSVGText()) {
michael@0 509 // Unlike containers, leaf frames do not include GetPosition() in
michael@0 510 // GetCanvasTM().
michael@0 511 overflowRect = overflowRect + aFrame->GetPosition();
michael@0 512 }
michael@0 513 int32_t appUnitsPerDevPx = aFrame->PresContext()->AppUnitsPerDevPixel();
michael@0 514 gfxMatrix tm = GetCanvasTM(aFrame, nsISVGChildFrame::FOR_PAINTING, aTransformRoot);
michael@0 515 if (aFrame->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer)) {
michael@0 516 gfx::Matrix childrenOnlyTM;
michael@0 517 if (static_cast<nsSVGContainerFrame*>(aFrame)->
michael@0 518 HasChildrenOnlyTransform(&childrenOnlyTM)) {
michael@0 519 // Undo the children-only transform:
michael@0 520 tm = ThebesMatrix(childrenOnlyTM).Invert() * tm;
michael@0 521 }
michael@0 522 }
michael@0 523 nsIntRect bounds = TransformFrameRectToOuterSVG(overflowRect,
michael@0 524 tm, aFrame->PresContext()).
michael@0 525 ToOutsidePixels(appUnitsPerDevPx);
michael@0 526 if (!aDirtyRect->Intersects(bounds)) {
michael@0 527 return;
michael@0 528 }
michael@0 529 }
michael@0 530
michael@0 531 /* SVG defines the following rendering model:
michael@0 532 *
michael@0 533 * 1. Render fill
michael@0 534 * 2. Render stroke
michael@0 535 * 3. Render markers
michael@0 536 * 4. Apply filter
michael@0 537 * 5. Apply clipping, masking, group opacity
michael@0 538 *
michael@0 539 * We follow this, but perform a couple of optimizations:
michael@0 540 *
michael@0 541 * + Use cairo's clipPath when representable natively (single object
michael@0 542 * clip region).
michael@0 543 *
michael@0 544 * + Merge opacity and masking if both used together.
michael@0 545 */
michael@0 546
michael@0 547 if (opacity != 1.0f && CanOptimizeOpacity(aFrame))
michael@0 548 opacity = 1.0f;
michael@0 549
michael@0 550 gfxContext *gfx = aContext->ThebesContext();
michael@0 551 bool complexEffects = false;
michael@0 552
michael@0 553 nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK);
michael@0 554 nsSVGMaskFrame *maskFrame = effectProperties.GetMaskFrame(&isOK);
michael@0 555
michael@0 556 bool isTrivialClip = clipPathFrame ? clipPathFrame->IsTrivial() : true;
michael@0 557
michael@0 558 if (!isOK) {
michael@0 559 // Some resource is invalid. We shouldn't paint anything.
michael@0 560 return;
michael@0 561 }
michael@0 562
michael@0 563 gfxMatrix matrix;
michael@0 564 if (clipPathFrame || maskFrame)
michael@0 565 matrix = GetCanvasTM(aFrame, nsISVGChildFrame::FOR_PAINTING, aTransformRoot);
michael@0 566
michael@0 567 /* Check if we need to do additional operations on this child's
michael@0 568 * rendering, which necessitates rendering into another surface. */
michael@0 569 if (opacity != 1.0f || maskFrame || (clipPathFrame && !isTrivialClip)
michael@0 570 || aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
michael@0 571 complexEffects = true;
michael@0 572 gfx->Save();
michael@0 573 if (!(aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
michael@0 574 // aFrame has a valid visual overflow rect, so clip to it before calling
michael@0 575 // PushGroup() to minimize the size of the surfaces we'll composite:
michael@0 576 gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(gfx);
michael@0 577 gfx->Multiply(GetCanvasTM(aFrame, nsISVGChildFrame::FOR_PAINTING, aTransformRoot));
michael@0 578 nsRect overflowRect = aFrame->GetVisualOverflowRectRelativeToSelf();
michael@0 579 if (aFrame->IsFrameOfType(nsIFrame::eSVGGeometry) ||
michael@0 580 aFrame->IsSVGText()) {
michael@0 581 // Unlike containers, leaf frames do not include GetPosition() in
michael@0 582 // GetCanvasTM().
michael@0 583 overflowRect = overflowRect + aFrame->GetPosition();
michael@0 584 }
michael@0 585 aContext->IntersectClip(overflowRect);
michael@0 586 }
michael@0 587 gfx->PushGroup(gfxContentType::COLOR_ALPHA);
michael@0 588 }
michael@0 589
michael@0 590 /* If this frame has only a trivial clipPath, set up cairo's clipping now so
michael@0 591 * we can just do normal painting and get it clipped appropriately.
michael@0 592 */
michael@0 593 if (clipPathFrame && isTrivialClip) {
michael@0 594 gfx->Save();
michael@0 595 clipPathFrame->ClipPaint(aContext, aFrame, matrix);
michael@0 596 }
michael@0 597
michael@0 598 /* Paint the child */
michael@0 599 if (effectProperties.HasValidFilter()) {
michael@0 600 nsRegion* dirtyRegion = nullptr;
michael@0 601 nsRegion tmpDirtyRegion;
michael@0 602 if (aDirtyRect) {
michael@0 603 // aDirtyRect is in outer-<svg> device pixels, but the filter code needs
michael@0 604 // it in frame space.
michael@0 605 gfxMatrix userToDeviceSpace =
michael@0 606 GetUserToCanvasTM(aFrame, nsISVGChildFrame::FOR_OUTERSVG_TM);
michael@0 607 if (userToDeviceSpace.IsSingular()) {
michael@0 608 return;
michael@0 609 }
michael@0 610 gfxMatrix deviceToUserSpace = userToDeviceSpace;
michael@0 611 deviceToUserSpace.Invert();
michael@0 612 gfxRect dirtyBounds = deviceToUserSpace.TransformBounds(
michael@0 613 gfxRect(aDirtyRect->x, aDirtyRect->y,
michael@0 614 aDirtyRect->width, aDirtyRect->height));
michael@0 615 tmpDirtyRegion =
michael@0 616 nsLayoutUtils::RoundGfxRectToAppRect(
michael@0 617 dirtyBounds, aFrame->PresContext()->AppUnitsPerCSSPixel()) -
michael@0 618 aFrame->GetPosition();
michael@0 619 dirtyRegion = &tmpDirtyRegion;
michael@0 620 }
michael@0 621 SVGPaintCallback paintCallback;
michael@0 622 nsFilterInstance::PaintFilteredFrame(aContext, aFrame, &paintCallback,
michael@0 623 dirtyRegion, aTransformRoot);
michael@0 624 } else {
michael@0 625 svgChildFrame->PaintSVG(aContext, aDirtyRect, aTransformRoot);
michael@0 626 }
michael@0 627
michael@0 628 if (clipPathFrame && isTrivialClip) {
michael@0 629 gfx->Restore();
michael@0 630 }
michael@0 631
michael@0 632 /* No more effects, we're done. */
michael@0 633 if (!complexEffects)
michael@0 634 return;
michael@0 635
michael@0 636 gfx->PopGroupToSource();
michael@0 637
michael@0 638 nsRefPtr<gfxPattern> maskSurface =
michael@0 639 maskFrame ? maskFrame->ComputeMaskAlpha(aContext, aFrame,
michael@0 640 matrix, opacity) : nullptr;
michael@0 641
michael@0 642 nsRefPtr<gfxPattern> clipMaskSurface;
michael@0 643 if (clipPathFrame && !isTrivialClip) {
michael@0 644 gfx->PushGroup(gfxContentType::COLOR_ALPHA);
michael@0 645
michael@0 646 nsresult rv = clipPathFrame->ClipPaint(aContext, aFrame, matrix);
michael@0 647 clipMaskSurface = gfx->PopGroup();
michael@0 648
michael@0 649 if (NS_SUCCEEDED(rv) && clipMaskSurface) {
michael@0 650 // Still more set after clipping, so clip to another surface
michael@0 651 if (maskSurface || opacity != 1.0f) {
michael@0 652 gfx->PushGroup(gfxContentType::COLOR_ALPHA);
michael@0 653 gfx->Mask(clipMaskSurface);
michael@0 654 gfx->PopGroupToSource();
michael@0 655 } else {
michael@0 656 gfx->Mask(clipMaskSurface);
michael@0 657 }
michael@0 658 }
michael@0 659 }
michael@0 660
michael@0 661 if (maskSurface) {
michael@0 662 gfx->Mask(maskSurface);
michael@0 663 } else if (opacity != 1.0f) {
michael@0 664 gfx->Paint(opacity);
michael@0 665 }
michael@0 666
michael@0 667 gfx->Restore();
michael@0 668 }
michael@0 669
michael@0 670 bool
michael@0 671 nsSVGUtils::HitTestClip(nsIFrame *aFrame, const nsPoint &aPoint)
michael@0 672 {
michael@0 673 nsSVGEffects::EffectProperties props =
michael@0 674 nsSVGEffects::GetEffectProperties(aFrame);
michael@0 675 if (!props.mClipPath)
michael@0 676 return true;
michael@0 677
michael@0 678 bool isOK = true;
michael@0 679 nsSVGClipPathFrame *clipPathFrame = props.GetClipPathFrame(&isOK);
michael@0 680 if (!isOK) {
michael@0 681 // clipPath is not a valid resource, so nothing gets painted, so
michael@0 682 // hit-testing must fail.
michael@0 683 return false;
michael@0 684 }
michael@0 685 if (!clipPathFrame) {
michael@0 686 // clipPath doesn't exist, ignore it.
michael@0 687 return true;
michael@0 688 }
michael@0 689
michael@0 690 return clipPathFrame->ClipHitTest(aFrame, GetCanvasTM(aFrame,
michael@0 691 nsISVGChildFrame::FOR_HIT_TESTING), aPoint);
michael@0 692 }
michael@0 693
michael@0 694 nsIFrame *
michael@0 695 nsSVGUtils::HitTestChildren(nsIFrame *aFrame, const nsPoint &aPoint)
michael@0 696 {
michael@0 697 // Traverse the list in reverse order, so that if we get a hit we know that's
michael@0 698 // the topmost frame that intersects the point; then we can just return it.
michael@0 699 nsIFrame* result = nullptr;
michael@0 700 for (nsIFrame* current = aFrame->PrincipalChildList().LastChild();
michael@0 701 current;
michael@0 702 current = current->GetPrevSibling()) {
michael@0 703 nsISVGChildFrame* SVGFrame = do_QueryFrame(current);
michael@0 704 if (SVGFrame) {
michael@0 705 const nsIContent* content = current->GetContent();
michael@0 706 if (content->IsSVG() &&
michael@0 707 !static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
michael@0 708 continue;
michael@0 709 }
michael@0 710 result = SVGFrame->GetFrameForPoint(aPoint);
michael@0 711 if (result)
michael@0 712 break;
michael@0 713 }
michael@0 714 }
michael@0 715
michael@0 716 if (result && !HitTestClip(aFrame, aPoint))
michael@0 717 result = nullptr;
michael@0 718
michael@0 719 return result;
michael@0 720 }
michael@0 721
michael@0 722 nsRect
michael@0 723 nsSVGUtils::GetCoveredRegion(const nsFrameList &aFrames)
michael@0 724 {
michael@0 725 nsRect rect;
michael@0 726
michael@0 727 for (nsIFrame* kid = aFrames.FirstChild();
michael@0 728 kid;
michael@0 729 kid = kid->GetNextSibling()) {
michael@0 730 nsISVGChildFrame* child = do_QueryFrame(kid);
michael@0 731 if (child) {
michael@0 732 nsRect childRect = child->GetCoveredRegion();
michael@0 733 rect.UnionRect(rect, childRect);
michael@0 734 }
michael@0 735 }
michael@0 736
michael@0 737 return rect;
michael@0 738 }
michael@0 739
michael@0 740 nsPoint
michael@0 741 nsSVGUtils::TransformOuterSVGPointToChildFrame(nsPoint aPoint,
michael@0 742 const gfxMatrix& aFrameToCanvasTM,
michael@0 743 nsPresContext* aPresContext)
michael@0 744 {
michael@0 745 NS_ABORT_IF_FALSE(!aFrameToCanvasTM.IsSingular(),
michael@0 746 "Callers must not pass a singular matrix");
michael@0 747 gfxMatrix canvasDevToFrameUserSpace = aFrameToCanvasTM;
michael@0 748 canvasDevToFrameUserSpace.Invert();
michael@0 749 gfxPoint devPt = gfxPoint(aPoint.x, aPoint.y) /
michael@0 750 aPresContext->AppUnitsPerDevPixel();
michael@0 751 gfxPoint userPt = canvasDevToFrameUserSpace.Transform(devPt);
michael@0 752 gfxPoint appPt = (userPt * aPresContext->AppUnitsPerCSSPixel()).Round();
michael@0 753 userPt.x = clamped(appPt.x, gfxFloat(nscoord_MIN), gfxFloat(nscoord_MAX));
michael@0 754 userPt.y = clamped(appPt.y, gfxFloat(nscoord_MIN), gfxFloat(nscoord_MAX));
michael@0 755 // now guaranteed to be safe:
michael@0 756 return nsPoint(nscoord(userPt.x), nscoord(userPt.y));
michael@0 757 }
michael@0 758
michael@0 759 nsRect
michael@0 760 nsSVGUtils::TransformFrameRectToOuterSVG(const nsRect& aRect,
michael@0 761 const gfxMatrix& aMatrix,
michael@0 762 nsPresContext* aPresContext)
michael@0 763 {
michael@0 764 gfxRect r(aRect.x, aRect.y, aRect.width, aRect.height);
michael@0 765 r.Scale(1.0 / nsPresContext::AppUnitsPerCSSPixel());
michael@0 766 return nsLayoutUtils::RoundGfxRectToAppRect(
michael@0 767 aMatrix.TransformBounds(r), aPresContext->AppUnitsPerDevPixel());
michael@0 768 }
michael@0 769
michael@0 770 gfxIntSize
michael@0 771 nsSVGUtils::ConvertToSurfaceSize(const gfxSize& aSize,
michael@0 772 bool *aResultOverflows)
michael@0 773 {
michael@0 774 gfxIntSize surfaceSize(ClampToInt(ceil(aSize.width)), ClampToInt(ceil(aSize.height)));
michael@0 775
michael@0 776 *aResultOverflows = surfaceSize.width != ceil(aSize.width) ||
michael@0 777 surfaceSize.height != ceil(aSize.height);
michael@0 778
michael@0 779 if (!gfxASurface::CheckSurfaceSize(surfaceSize)) {
michael@0 780 surfaceSize.width = std::min(NS_SVG_OFFSCREEN_MAX_DIMENSION,
michael@0 781 surfaceSize.width);
michael@0 782 surfaceSize.height = std::min(NS_SVG_OFFSCREEN_MAX_DIMENSION,
michael@0 783 surfaceSize.height);
michael@0 784 *aResultOverflows = true;
michael@0 785 }
michael@0 786
michael@0 787 return surfaceSize;
michael@0 788 }
michael@0 789
michael@0 790 bool
michael@0 791 nsSVGUtils::HitTestRect(const gfx::Matrix &aMatrix,
michael@0 792 float aRX, float aRY, float aRWidth, float aRHeight,
michael@0 793 float aX, float aY)
michael@0 794 {
michael@0 795 gfx::Rect rect(aRX, aRY, aRWidth, aRHeight);
michael@0 796 if (rect.IsEmpty() || aMatrix.IsSingular()) {
michael@0 797 return false;
michael@0 798 }
michael@0 799 gfx::Matrix toRectSpace = aMatrix;
michael@0 800 toRectSpace.Invert();
michael@0 801 gfx::Point p = toRectSpace * gfx::Point(aX, aY);
michael@0 802 return rect.x <= p.x && p.x <= rect.XMost() &&
michael@0 803 rect.y <= p.y && p.y <= rect.YMost();
michael@0 804 }
michael@0 805
michael@0 806 gfxRect
michael@0 807 nsSVGUtils::GetClipRectForFrame(nsIFrame *aFrame,
michael@0 808 float aX, float aY, float aWidth, float aHeight)
michael@0 809 {
michael@0 810 const nsStyleDisplay* disp = aFrame->StyleDisplay();
michael@0 811
michael@0 812 if (!(disp->mClipFlags & NS_STYLE_CLIP_RECT)) {
michael@0 813 NS_ASSERTION(disp->mClipFlags == NS_STYLE_CLIP_AUTO,
michael@0 814 "We don't know about this type of clip.");
michael@0 815 return gfxRect(aX, aY, aWidth, aHeight);
michael@0 816 }
michael@0 817
michael@0 818 if (disp->mOverflowX == NS_STYLE_OVERFLOW_HIDDEN ||
michael@0 819 disp->mOverflowY == NS_STYLE_OVERFLOW_HIDDEN) {
michael@0 820
michael@0 821 nsIntRect clipPxRect =
michael@0 822 disp->mClip.ToOutsidePixels(aFrame->PresContext()->AppUnitsPerDevPixel());
michael@0 823 gfxRect clipRect =
michael@0 824 gfxRect(clipPxRect.x, clipPxRect.y, clipPxRect.width, clipPxRect.height);
michael@0 825
michael@0 826 if (NS_STYLE_CLIP_RIGHT_AUTO & disp->mClipFlags) {
michael@0 827 clipRect.width = aWidth - clipRect.X();
michael@0 828 }
michael@0 829 if (NS_STYLE_CLIP_BOTTOM_AUTO & disp->mClipFlags) {
michael@0 830 clipRect.height = aHeight - clipRect.Y();
michael@0 831 }
michael@0 832
michael@0 833 if (disp->mOverflowX != NS_STYLE_OVERFLOW_HIDDEN) {
michael@0 834 clipRect.x = aX;
michael@0 835 clipRect.width = aWidth;
michael@0 836 }
michael@0 837 if (disp->mOverflowY != NS_STYLE_OVERFLOW_HIDDEN) {
michael@0 838 clipRect.y = aY;
michael@0 839 clipRect.height = aHeight;
michael@0 840 }
michael@0 841
michael@0 842 return clipRect;
michael@0 843 }
michael@0 844 return gfxRect(aX, aY, aWidth, aHeight);
michael@0 845 }
michael@0 846
michael@0 847 void
michael@0 848 nsSVGUtils::SetClipRect(gfxContext *aContext,
michael@0 849 const gfxMatrix &aCTM,
michael@0 850 const gfxRect &aRect)
michael@0 851 {
michael@0 852 if (aCTM.IsSingular())
michael@0 853 return;
michael@0 854
michael@0 855 gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(aContext);
michael@0 856 aContext->Multiply(aCTM);
michael@0 857 aContext->Clip(aRect);
michael@0 858 }
michael@0 859
michael@0 860 gfxRect
michael@0 861 nsSVGUtils::GetBBox(nsIFrame *aFrame, uint32_t aFlags)
michael@0 862 {
michael@0 863 if (aFrame->GetContent()->IsNodeOfType(nsINode::eTEXT)) {
michael@0 864 aFrame = aFrame->GetParent();
michael@0 865 }
michael@0 866 gfxRect bbox;
michael@0 867 nsISVGChildFrame *svg = do_QueryFrame(aFrame);
michael@0 868 if (svg || aFrame->IsSVGText()) {
michael@0 869 // It is possible to apply a gradient, pattern, clipping path, mask or
michael@0 870 // filter to text. When one of these facilities is applied to text
michael@0 871 // the bounding box is the entire text element in all
michael@0 872 // cases.
michael@0 873 if (aFrame->IsSVGText()) {
michael@0 874 nsIFrame* ancestor = GetFirstNonAAncestorFrame(aFrame);
michael@0 875 if (ancestor && ancestor->IsSVGText()) {
michael@0 876 while (ancestor->GetType() != nsGkAtoms::svgTextFrame) {
michael@0 877 ancestor = ancestor->GetParent();
michael@0 878 }
michael@0 879 }
michael@0 880 svg = do_QueryFrame(ancestor);
michael@0 881 }
michael@0 882 nsIContent* content = aFrame->GetContent();
michael@0 883 if (content->IsSVG() &&
michael@0 884 !static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
michael@0 885 return bbox;
michael@0 886 }
michael@0 887 gfxMatrix matrix;
michael@0 888 if (aFrame->GetType() == nsGkAtoms::svgForeignObjectFrame) {
michael@0 889 // The spec says getBBox "Returns the tight bounding box in *current user
michael@0 890 // space*". So we should really be doing this for all elements, but that
michael@0 891 // needs investigation to check that we won't break too much content.
michael@0 892 // NOTE: When changing this to apply to other frame types, make sure to
michael@0 893 // also update nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset.
michael@0 894 NS_ABORT_IF_FALSE(content->IsSVG(), "bad cast");
michael@0 895 nsSVGElement *element = static_cast<nsSVGElement*>(content);
michael@0 896 matrix = element->PrependLocalTransformsTo(matrix,
michael@0 897 nsSVGElement::eChildToUserSpace);
michael@0 898 }
michael@0 899 return svg->GetBBoxContribution(ToMatrix(matrix), aFlags).ToThebesRect();
michael@0 900 }
michael@0 901 return nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(aFrame);
michael@0 902 }
michael@0 903
michael@0 904 gfxPoint
michael@0 905 nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(nsIFrame *aFrame)
michael@0 906 {
michael@0 907 if (!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
michael@0 908 // The user space for non-SVG frames is defined as the bounding box of the
michael@0 909 // frame's border-box rects over all continuations.
michael@0 910 return gfxPoint();
michael@0 911 }
michael@0 912
michael@0 913 // Leaf frames apply their own offset inside their user space.
michael@0 914 if (aFrame->IsFrameOfType(nsIFrame::eSVGGeometry) ||
michael@0 915 aFrame->IsSVGText()) {
michael@0 916 return nsLayoutUtils::RectToGfxRect(aFrame->GetRect(),
michael@0 917 nsPresContext::AppUnitsPerCSSPixel()).TopLeft();
michael@0 918 }
michael@0 919
michael@0 920 // For foreignObject frames, nsSVGUtils::GetBBox applies their local
michael@0 921 // transform, so we need to do the same here.
michael@0 922 if (aFrame->GetType() == nsGkAtoms::svgForeignObjectFrame) {
michael@0 923 gfxMatrix transform = static_cast<nsSVGElement*>(aFrame->GetContent())->
michael@0 924 PrependLocalTransformsTo(gfxMatrix(),
michael@0 925 nsSVGElement::eChildToUserSpace);
michael@0 926 NS_ASSERTION(!transform.HasNonTranslation(), "we're relying on this being an offset-only transform");
michael@0 927 return transform.GetTranslation();
michael@0 928 }
michael@0 929
michael@0 930 return gfxPoint();
michael@0 931 }
michael@0 932
michael@0 933 gfxRect
michael@0 934 nsSVGUtils::GetRelativeRect(uint16_t aUnits, const nsSVGLength2 *aXYWH,
michael@0 935 const gfxRect &aBBox, nsIFrame *aFrame)
michael@0 936 {
michael@0 937 float x, y, width, height;
michael@0 938 if (aUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
michael@0 939 x = aBBox.X() + ObjectSpace(aBBox, &aXYWH[0]);
michael@0 940 y = aBBox.Y() + ObjectSpace(aBBox, &aXYWH[1]);
michael@0 941 width = ObjectSpace(aBBox, &aXYWH[2]);
michael@0 942 height = ObjectSpace(aBBox, &aXYWH[3]);
michael@0 943 } else {
michael@0 944 x = UserSpace(aFrame, &aXYWH[0]);
michael@0 945 y = UserSpace(aFrame, &aXYWH[1]);
michael@0 946 width = UserSpace(aFrame, &aXYWH[2]);
michael@0 947 height = UserSpace(aFrame, &aXYWH[3]);
michael@0 948 }
michael@0 949 return gfxRect(x, y, width, height);
michael@0 950 }
michael@0 951
michael@0 952 bool
michael@0 953 nsSVGUtils::CanOptimizeOpacity(nsIFrame *aFrame)
michael@0 954 {
michael@0 955 if (!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
michael@0 956 return false;
michael@0 957 }
michael@0 958 nsIAtom *type = aFrame->GetType();
michael@0 959 if (type != nsGkAtoms::svgImageFrame &&
michael@0 960 type != nsGkAtoms::svgPathGeometryFrame) {
michael@0 961 return false;
michael@0 962 }
michael@0 963 if (aFrame->StyleSVGReset()->HasFilters()) {
michael@0 964 return false;
michael@0 965 }
michael@0 966 // XXX The SVG WG is intending to allow fill, stroke and markers on <image>
michael@0 967 if (type == nsGkAtoms::svgImageFrame) {
michael@0 968 return true;
michael@0 969 }
michael@0 970 const nsStyleSVG *style = aFrame->StyleSVG();
michael@0 971 if (style->HasMarker()) {
michael@0 972 return false;
michael@0 973 }
michael@0 974 if (!style->HasFill() || !HasStroke(aFrame)) {
michael@0 975 return true;
michael@0 976 }
michael@0 977 return false;
michael@0 978 }
michael@0 979
michael@0 980 gfxMatrix
michael@0 981 nsSVGUtils::AdjustMatrixForUnits(const gfxMatrix &aMatrix,
michael@0 982 nsSVGEnum *aUnits,
michael@0 983 nsIFrame *aFrame)
michael@0 984 {
michael@0 985 if (aFrame &&
michael@0 986 aUnits->GetAnimValue() == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
michael@0 987 gfxRect bbox = GetBBox(aFrame);
michael@0 988 return gfxMatrix().Scale(bbox.Width(), bbox.Height()) *
michael@0 989 gfxMatrix().Translate(gfxPoint(bbox.X(), bbox.Y())) *
michael@0 990 aMatrix;
michael@0 991 }
michael@0 992 return aMatrix;
michael@0 993 }
michael@0 994
michael@0 995 nsIFrame*
michael@0 996 nsSVGUtils::GetFirstNonAAncestorFrame(nsIFrame* aStartFrame)
michael@0 997 {
michael@0 998 for (nsIFrame *ancestorFrame = aStartFrame; ancestorFrame;
michael@0 999 ancestorFrame = ancestorFrame->GetParent()) {
michael@0 1000 if (ancestorFrame->GetType() != nsGkAtoms::svgAFrame) {
michael@0 1001 return ancestorFrame;
michael@0 1002 }
michael@0 1003 }
michael@0 1004 return nullptr;
michael@0 1005 }
michael@0 1006
michael@0 1007 gfxMatrix
michael@0 1008 nsSVGUtils::GetStrokeTransform(nsIFrame *aFrame)
michael@0 1009 {
michael@0 1010 if (aFrame->GetContent()->IsNodeOfType(nsINode::eTEXT)) {
michael@0 1011 aFrame = aFrame->GetParent();
michael@0 1012 }
michael@0 1013
michael@0 1014 if (aFrame->StyleSVGReset()->mVectorEffect ==
michael@0 1015 NS_STYLE_VECTOR_EFFECT_NON_SCALING_STROKE) {
michael@0 1016
michael@0 1017 nsIContent *content = aFrame->GetContent();
michael@0 1018 NS_ABORT_IF_FALSE(content->IsSVG(), "bad cast");
michael@0 1019
michael@0 1020 // a non-scaling stroke is in the screen co-ordinate
michael@0 1021 // space rather so we need to invert the transform
michael@0 1022 // to the screen co-ordinate space to get there.
michael@0 1023 // See http://www.w3.org/TR/SVGTiny12/painting.html#NonScalingStroke
michael@0 1024 gfx::Matrix transform = SVGContentUtils::GetCTM(
michael@0 1025 static_cast<nsSVGElement*>(content), true);
michael@0 1026 if (!transform.IsSingular()) {
michael@0 1027 transform.Invert();
michael@0 1028 return ThebesMatrix(transform);
michael@0 1029 }
michael@0 1030 }
michael@0 1031 return gfxMatrix();
michael@0 1032 }
michael@0 1033
michael@0 1034 // The logic here comes from _cairo_stroke_style_max_distance_from_path
michael@0 1035 static gfxRect
michael@0 1036 PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
michael@0 1037 nsIFrame* aFrame,
michael@0 1038 double aStyleExpansionFactor,
michael@0 1039 const gfxMatrix& aMatrix)
michael@0 1040 {
michael@0 1041 double style_expansion =
michael@0 1042 aStyleExpansionFactor * nsSVGUtils::GetStrokeWidth(aFrame);
michael@0 1043
michael@0 1044 gfxMatrix matrix = aMatrix;
michael@0 1045 matrix.Multiply(nsSVGUtils::GetStrokeTransform(aFrame));
michael@0 1046
michael@0 1047 double dx = style_expansion * (fabs(matrix.xx) + fabs(matrix.xy));
michael@0 1048 double dy = style_expansion * (fabs(matrix.yy) + fabs(matrix.yx));
michael@0 1049
michael@0 1050 gfxRect strokeExtents = aPathExtents;
michael@0 1051 strokeExtents.Inflate(dx, dy);
michael@0 1052 return strokeExtents;
michael@0 1053 }
michael@0 1054
michael@0 1055 /*static*/ gfxRect
michael@0 1056 nsSVGUtils::PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
michael@0 1057 nsTextFrame* aFrame,
michael@0 1058 const gfxMatrix& aMatrix)
michael@0 1059 {
michael@0 1060 NS_ASSERTION(aFrame->IsSVGText(), "expected an nsTextFrame for SVG text");
michael@0 1061 return ::PathExtentsToMaxStrokeExtents(aPathExtents, aFrame, 0.5, aMatrix);
michael@0 1062 }
michael@0 1063
michael@0 1064 /*static*/ gfxRect
michael@0 1065 nsSVGUtils::PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
michael@0 1066 nsSVGPathGeometryFrame* aFrame,
michael@0 1067 const gfxMatrix& aMatrix)
michael@0 1068 {
michael@0 1069 double styleExpansionFactor = 0.5;
michael@0 1070
michael@0 1071 if (static_cast<nsSVGPathGeometryElement*>(aFrame->GetContent())->IsMarkable()) {
michael@0 1072 const nsStyleSVG* style = aFrame->StyleSVG();
michael@0 1073
michael@0 1074 if (style->mStrokeLinecap == NS_STYLE_STROKE_LINECAP_SQUARE) {
michael@0 1075 styleExpansionFactor = M_SQRT1_2;
michael@0 1076 }
michael@0 1077
michael@0 1078 if (style->mStrokeLinejoin == NS_STYLE_STROKE_LINEJOIN_MITER &&
michael@0 1079 styleExpansionFactor < style->mStrokeMiterlimit &&
michael@0 1080 aFrame->GetContent()->Tag() != nsGkAtoms::line) {
michael@0 1081 styleExpansionFactor = style->mStrokeMiterlimit;
michael@0 1082 }
michael@0 1083 }
michael@0 1084
michael@0 1085 return ::PathExtentsToMaxStrokeExtents(aPathExtents,
michael@0 1086 aFrame,
michael@0 1087 styleExpansionFactor,
michael@0 1088 aMatrix);
michael@0 1089 }
michael@0 1090
michael@0 1091 // ----------------------------------------------------------------------
michael@0 1092
michael@0 1093 /* static */ nscolor
michael@0 1094 nsSVGUtils::GetFallbackOrPaintColor(gfxContext *aContext, nsStyleContext *aStyleContext,
michael@0 1095 nsStyleSVGPaint nsStyleSVG::*aFillOrStroke)
michael@0 1096 {
michael@0 1097 const nsStyleSVGPaint &paint = aStyleContext->StyleSVG()->*aFillOrStroke;
michael@0 1098 nsStyleContext *styleIfVisited = aStyleContext->GetStyleIfVisited();
michael@0 1099 bool isServer = paint.mType == eStyleSVGPaintType_Server ||
michael@0 1100 paint.mType == eStyleSVGPaintType_ContextFill ||
michael@0 1101 paint.mType == eStyleSVGPaintType_ContextStroke;
michael@0 1102 nscolor color = isServer ? paint.mFallbackColor : paint.mPaint.mColor;
michael@0 1103 if (styleIfVisited) {
michael@0 1104 const nsStyleSVGPaint &paintIfVisited =
michael@0 1105 styleIfVisited->StyleSVG()->*aFillOrStroke;
michael@0 1106 // To prevent Web content from detecting if a user has visited a URL
michael@0 1107 // (via URL loading triggered by paint servers or performance
michael@0 1108 // differences between paint servers or between a paint server and a
michael@0 1109 // color), we do not allow whether links are visited to change which
michael@0 1110 // paint server is used or switch between paint servers and simple
michael@0 1111 // colors. A :visited style may only override a simple color with
michael@0 1112 // another simple color.
michael@0 1113 if (paintIfVisited.mType == eStyleSVGPaintType_Color &&
michael@0 1114 paint.mType == eStyleSVGPaintType_Color) {
michael@0 1115 nscolor colors[2] = { color, paintIfVisited.mPaint.mColor };
michael@0 1116 return nsStyleContext::CombineVisitedColors(
michael@0 1117 colors, aStyleContext->RelevantLinkVisited());
michael@0 1118 }
michael@0 1119 }
michael@0 1120 return color;
michael@0 1121 }
michael@0 1122
michael@0 1123 static void
michael@0 1124 SetupFallbackOrPaintColor(gfxContext *aContext, nsStyleContext *aStyleContext,
michael@0 1125 nsStyleSVGPaint nsStyleSVG::*aFillOrStroke,
michael@0 1126 float aOpacity)
michael@0 1127 {
michael@0 1128 nscolor color = nsSVGUtils::GetFallbackOrPaintColor(
michael@0 1129 aContext, aStyleContext, aFillOrStroke);
michael@0 1130
michael@0 1131 aContext->SetColor(gfxRGBA(NS_GET_R(color)/255.0,
michael@0 1132 NS_GET_G(color)/255.0,
michael@0 1133 NS_GET_B(color)/255.0,
michael@0 1134 NS_GET_A(color)/255.0 * aOpacity));
michael@0 1135 }
michael@0 1136
michael@0 1137 static float
michael@0 1138 MaybeOptimizeOpacity(nsIFrame *aFrame, float aFillOrStrokeOpacity)
michael@0 1139 {
michael@0 1140 float opacity = aFrame->StyleDisplay()->mOpacity;
michael@0 1141 if (opacity < 1 && nsSVGUtils::CanOptimizeOpacity(aFrame)) {
michael@0 1142 return aFillOrStrokeOpacity * opacity;
michael@0 1143 }
michael@0 1144 return aFillOrStrokeOpacity;
michael@0 1145 }
michael@0 1146
michael@0 1147 /* static */ bool
michael@0 1148 nsSVGUtils::SetupContextPaint(gfxContext *aContext,
michael@0 1149 gfxTextContextPaint *aContextPaint,
michael@0 1150 const nsStyleSVGPaint &aPaint,
michael@0 1151 float aOpacity)
michael@0 1152 {
michael@0 1153 nsRefPtr<gfxPattern> pattern;
michael@0 1154
michael@0 1155 if (!aContextPaint) {
michael@0 1156 return false;
michael@0 1157 }
michael@0 1158
michael@0 1159 switch (aPaint.mType) {
michael@0 1160 case eStyleSVGPaintType_ContextFill:
michael@0 1161 pattern = aContextPaint->GetFillPattern(aOpacity, aContext->CurrentMatrix());
michael@0 1162 break;
michael@0 1163 case eStyleSVGPaintType_ContextStroke:
michael@0 1164 pattern = aContextPaint->GetStrokePattern(aOpacity, aContext->CurrentMatrix());
michael@0 1165 break;
michael@0 1166 default:
michael@0 1167 return false;
michael@0 1168 }
michael@0 1169
michael@0 1170 if (!pattern) {
michael@0 1171 return false;
michael@0 1172 }
michael@0 1173
michael@0 1174 aContext->SetPattern(pattern);
michael@0 1175
michael@0 1176 return true;
michael@0 1177 }
michael@0 1178
michael@0 1179 bool
michael@0 1180 nsSVGUtils::SetupCairoFillPaint(nsIFrame *aFrame, gfxContext* aContext,
michael@0 1181 gfxTextContextPaint *aContextPaint)
michael@0 1182 {
michael@0 1183 const nsStyleSVG* style = aFrame->StyleSVG();
michael@0 1184 if (style->mFill.mType == eStyleSVGPaintType_None)
michael@0 1185 return false;
michael@0 1186
michael@0 1187 if (style->mFillRule == NS_STYLE_FILL_RULE_EVENODD)
michael@0 1188 aContext->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);
michael@0 1189 else
michael@0 1190 aContext->SetFillRule(gfxContext::FILL_RULE_WINDING);
michael@0 1191
michael@0 1192 float opacity = MaybeOptimizeOpacity(aFrame,
michael@0 1193 GetOpacity(style->mFillOpacitySource,
michael@0 1194 style->mFillOpacity,
michael@0 1195 aContextPaint));
michael@0 1196 nsSVGPaintServerFrame *ps =
michael@0 1197 nsSVGEffects::GetPaintServer(aFrame, &style->mFill, nsSVGEffects::FillProperty());
michael@0 1198 if (ps && ps->SetupPaintServer(aContext, aFrame, &nsStyleSVG::mFill, opacity))
michael@0 1199 return true;
michael@0 1200
michael@0 1201 if (SetupContextPaint(aContext, aContextPaint, style->mFill, opacity)) {
michael@0 1202 return true;
michael@0 1203 }
michael@0 1204
michael@0 1205 // On failure, use the fallback colour in case we have an
michael@0 1206 // objectBoundingBox where the width or height of the object is zero.
michael@0 1207 // See http://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBox
michael@0 1208 SetupFallbackOrPaintColor(aContext, aFrame->StyleContext(),
michael@0 1209 &nsStyleSVG::mFill, opacity);
michael@0 1210
michael@0 1211 return true;
michael@0 1212 }
michael@0 1213
michael@0 1214 bool
michael@0 1215 nsSVGUtils::SetupCairoStrokePaint(nsIFrame *aFrame, gfxContext* aContext,
michael@0 1216 gfxTextContextPaint *aContextPaint)
michael@0 1217 {
michael@0 1218 const nsStyleSVG* style = aFrame->StyleSVG();
michael@0 1219 if (style->mStroke.mType == eStyleSVGPaintType_None)
michael@0 1220 return false;
michael@0 1221
michael@0 1222 float opacity = MaybeOptimizeOpacity(aFrame,
michael@0 1223 GetOpacity(style->mStrokeOpacitySource,
michael@0 1224 style->mStrokeOpacity,
michael@0 1225 aContextPaint));
michael@0 1226
michael@0 1227 nsSVGPaintServerFrame *ps =
michael@0 1228 nsSVGEffects::GetPaintServer(aFrame, &style->mStroke, nsSVGEffects::StrokeProperty());
michael@0 1229 if (ps && ps->SetupPaintServer(aContext, aFrame, &nsStyleSVG::mStroke, opacity))
michael@0 1230 return true;
michael@0 1231
michael@0 1232 if (SetupContextPaint(aContext, aContextPaint, style->mStroke, opacity)) {
michael@0 1233 return true;
michael@0 1234 }
michael@0 1235
michael@0 1236 // On failure, use the fallback colour in case we have an
michael@0 1237 // objectBoundingBox where the width or height of the object is zero.
michael@0 1238 // See http://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBox
michael@0 1239 SetupFallbackOrPaintColor(aContext, aFrame->StyleContext(),
michael@0 1240 &nsStyleSVG::mStroke, opacity);
michael@0 1241
michael@0 1242 return true;
michael@0 1243 }
michael@0 1244
michael@0 1245 /* static */ float
michael@0 1246 nsSVGUtils::GetOpacity(nsStyleSVGOpacitySource aOpacityType,
michael@0 1247 const float& aOpacity,
michael@0 1248 gfxTextContextPaint *aOuterContextPaint)
michael@0 1249 {
michael@0 1250 float opacity = 1.0f;
michael@0 1251 switch (aOpacityType) {
michael@0 1252 case eStyleSVGOpacitySource_Normal:
michael@0 1253 opacity = aOpacity;
michael@0 1254 break;
michael@0 1255 case eStyleSVGOpacitySource_ContextFillOpacity:
michael@0 1256 if (aOuterContextPaint) {
michael@0 1257 opacity = aOuterContextPaint->GetFillOpacity();
michael@0 1258 } else {
michael@0 1259 NS_WARNING("context-fill-opacity used outside of an SVG glyph");
michael@0 1260 }
michael@0 1261 break;
michael@0 1262 case eStyleSVGOpacitySource_ContextStrokeOpacity:
michael@0 1263 if (aOuterContextPaint) {
michael@0 1264 opacity = aOuterContextPaint->GetStrokeOpacity();
michael@0 1265 } else {
michael@0 1266 NS_WARNING("context-stroke-opacity used outside of an SVG glyph");
michael@0 1267 }
michael@0 1268 break;
michael@0 1269 default:
michael@0 1270 NS_NOTREACHED("Unknown object opacity inheritance type for SVG glyph");
michael@0 1271 }
michael@0 1272 return opacity;
michael@0 1273 }
michael@0 1274
michael@0 1275 bool
michael@0 1276 nsSVGUtils::HasStroke(nsIFrame* aFrame, gfxTextContextPaint *aContextPaint)
michael@0 1277 {
michael@0 1278 const nsStyleSVG *style = aFrame->StyleSVG();
michael@0 1279 return style->HasStroke() && GetStrokeWidth(aFrame, aContextPaint) > 0;
michael@0 1280 }
michael@0 1281
michael@0 1282 float
michael@0 1283 nsSVGUtils::GetStrokeWidth(nsIFrame* aFrame, gfxTextContextPaint *aContextPaint)
michael@0 1284 {
michael@0 1285 const nsStyleSVG *style = aFrame->StyleSVG();
michael@0 1286 if (aContextPaint && style->mStrokeWidthFromObject) {
michael@0 1287 return aContextPaint->GetStrokeWidth();
michael@0 1288 }
michael@0 1289
michael@0 1290 nsIContent* content = aFrame->GetContent();
michael@0 1291 if (content->IsNodeOfType(nsINode::eTEXT)) {
michael@0 1292 content = content->GetParent();
michael@0 1293 }
michael@0 1294
michael@0 1295 nsSVGElement *ctx = static_cast<nsSVGElement*>(content);
michael@0 1296
michael@0 1297 return SVGContentUtils::CoordToFloat(aFrame->PresContext(), ctx,
michael@0 1298 style->mStrokeWidth);
michael@0 1299 }
michael@0 1300
michael@0 1301 void
michael@0 1302 nsSVGUtils::SetupCairoStrokeBBoxGeometry(nsIFrame* aFrame,
michael@0 1303 gfxContext *aContext,
michael@0 1304 gfxTextContextPaint *aContextPaint)
michael@0 1305 {
michael@0 1306 float width = GetStrokeWidth(aFrame, aContextPaint);
michael@0 1307 if (width <= 0)
michael@0 1308 return;
michael@0 1309 aContext->SetLineWidth(width);
michael@0 1310
michael@0 1311 // Apply any stroke-specific transform
michael@0 1312 gfxMatrix strokeTransform = GetStrokeTransform(aFrame);
michael@0 1313 if (!strokeTransform.IsIdentity()) {
michael@0 1314 aContext->Multiply(strokeTransform);
michael@0 1315 }
michael@0 1316
michael@0 1317 const nsStyleSVG* style = aFrame->StyleSVG();
michael@0 1318
michael@0 1319 switch (style->mStrokeLinecap) {
michael@0 1320 case NS_STYLE_STROKE_LINECAP_BUTT:
michael@0 1321 aContext->SetLineCap(gfxContext::LINE_CAP_BUTT);
michael@0 1322 break;
michael@0 1323 case NS_STYLE_STROKE_LINECAP_ROUND:
michael@0 1324 aContext->SetLineCap(gfxContext::LINE_CAP_ROUND);
michael@0 1325 break;
michael@0 1326 case NS_STYLE_STROKE_LINECAP_SQUARE:
michael@0 1327 aContext->SetLineCap(gfxContext::LINE_CAP_SQUARE);
michael@0 1328 break;
michael@0 1329 }
michael@0 1330
michael@0 1331 aContext->SetMiterLimit(style->mStrokeMiterlimit);
michael@0 1332
michael@0 1333 switch (style->mStrokeLinejoin) {
michael@0 1334 case NS_STYLE_STROKE_LINEJOIN_MITER:
michael@0 1335 aContext->SetLineJoin(gfxContext::LINE_JOIN_MITER);
michael@0 1336 break;
michael@0 1337 case NS_STYLE_STROKE_LINEJOIN_ROUND:
michael@0 1338 aContext->SetLineJoin(gfxContext::LINE_JOIN_ROUND);
michael@0 1339 break;
michael@0 1340 case NS_STYLE_STROKE_LINEJOIN_BEVEL:
michael@0 1341 aContext->SetLineJoin(gfxContext::LINE_JOIN_BEVEL);
michael@0 1342 break;
michael@0 1343 }
michael@0 1344 }
michael@0 1345
michael@0 1346 static bool
michael@0 1347 GetStrokeDashData(nsIFrame* aFrame,
michael@0 1348 FallibleTArray<gfxFloat>& aDashes,
michael@0 1349 gfxFloat* aDashOffset,
michael@0 1350 gfxTextContextPaint *aContextPaint)
michael@0 1351 {
michael@0 1352 const nsStyleSVG* style = aFrame->StyleSVG();
michael@0 1353 nsPresContext *presContext = aFrame->PresContext();
michael@0 1354 nsIContent *content = aFrame->GetContent();
michael@0 1355 nsSVGElement *ctx = static_cast<nsSVGElement*>
michael@0 1356 (content->IsNodeOfType(nsINode::eTEXT) ?
michael@0 1357 content->GetParent() : content);
michael@0 1358
michael@0 1359 gfxFloat totalLength = 0.0;
michael@0 1360 if (aContextPaint && style->mStrokeDasharrayFromObject) {
michael@0 1361 aDashes = aContextPaint->GetStrokeDashArray();
michael@0 1362
michael@0 1363 for (uint32_t i = 0; i < aDashes.Length(); i++) {
michael@0 1364 if (aDashes[i] < 0.0) {
michael@0 1365 return false;
michael@0 1366 }
michael@0 1367 totalLength += aDashes[i];
michael@0 1368 }
michael@0 1369
michael@0 1370 } else {
michael@0 1371 uint32_t count = style->mStrokeDasharrayLength;
michael@0 1372 if (!count || !aDashes.SetLength(count)) {
michael@0 1373 return false;
michael@0 1374 }
michael@0 1375
michael@0 1376 gfxFloat pathScale = 1.0;
michael@0 1377
michael@0 1378 if (content->Tag() == nsGkAtoms::path) {
michael@0 1379 pathScale = static_cast<SVGPathElement*>(content)->
michael@0 1380 GetPathLengthScale(SVGPathElement::eForStroking);
michael@0 1381 if (pathScale <= 0) {
michael@0 1382 return false;
michael@0 1383 }
michael@0 1384 }
michael@0 1385
michael@0 1386 const nsStyleCoord *dasharray = style->mStrokeDasharray;
michael@0 1387
michael@0 1388 for (uint32_t i = 0; i < count; i++) {
michael@0 1389 aDashes[i] = SVGContentUtils::CoordToFloat(presContext,
michael@0 1390 ctx,
michael@0 1391 dasharray[i]) * pathScale;
michael@0 1392 if (aDashes[i] < 0.0) {
michael@0 1393 return false;
michael@0 1394 }
michael@0 1395 totalLength += aDashes[i];
michael@0 1396 }
michael@0 1397 }
michael@0 1398
michael@0 1399 if (aContextPaint && style->mStrokeDashoffsetFromObject) {
michael@0 1400 *aDashOffset = aContextPaint->GetStrokeDashOffset();
michael@0 1401 } else {
michael@0 1402 *aDashOffset = SVGContentUtils::CoordToFloat(presContext,
michael@0 1403 ctx,
michael@0 1404 style->mStrokeDashoffset);
michael@0 1405 }
michael@0 1406
michael@0 1407 return (totalLength > 0.0);
michael@0 1408 }
michael@0 1409
michael@0 1410 void
michael@0 1411 nsSVGUtils::SetupCairoStrokeGeometry(nsIFrame* aFrame, gfxContext* aContext,
michael@0 1412 gfxTextContextPaint *aContextPaint)
michael@0 1413 {
michael@0 1414 SetupCairoStrokeBBoxGeometry(aFrame, aContext, aContextPaint);
michael@0 1415
michael@0 1416 AutoFallibleTArray<gfxFloat, 10> dashes;
michael@0 1417 gfxFloat dashOffset;
michael@0 1418 if (GetStrokeDashData(aFrame, dashes, &dashOffset, aContextPaint)) {
michael@0 1419 aContext->SetDash(dashes.Elements(), dashes.Length(), dashOffset);
michael@0 1420 }
michael@0 1421 }
michael@0 1422
michael@0 1423 uint16_t
michael@0 1424 nsSVGUtils::GetGeometryHitTestFlags(nsIFrame* aFrame)
michael@0 1425 {
michael@0 1426 uint16_t flags = 0;
michael@0 1427
michael@0 1428 switch(aFrame->StyleVisibility()->mPointerEvents) {
michael@0 1429 case NS_STYLE_POINTER_EVENTS_NONE:
michael@0 1430 break;
michael@0 1431 case NS_STYLE_POINTER_EVENTS_AUTO:
michael@0 1432 case NS_STYLE_POINTER_EVENTS_VISIBLEPAINTED:
michael@0 1433 if (aFrame->StyleVisibility()->IsVisible()) {
michael@0 1434 if (aFrame->StyleSVG()->mFill.mType != eStyleSVGPaintType_None)
michael@0 1435 flags |= SVG_HIT_TEST_FILL;
michael@0 1436 if (aFrame->StyleSVG()->mStroke.mType != eStyleSVGPaintType_None)
michael@0 1437 flags |= SVG_HIT_TEST_STROKE;
michael@0 1438 if (aFrame->StyleSVG()->mStrokeOpacity > 0)
michael@0 1439 flags |= SVG_HIT_TEST_CHECK_MRECT;
michael@0 1440 }
michael@0 1441 break;
michael@0 1442 case NS_STYLE_POINTER_EVENTS_VISIBLEFILL:
michael@0 1443 if (aFrame->StyleVisibility()->IsVisible()) {
michael@0 1444 flags |= SVG_HIT_TEST_FILL;
michael@0 1445 }
michael@0 1446 break;
michael@0 1447 case NS_STYLE_POINTER_EVENTS_VISIBLESTROKE:
michael@0 1448 if (aFrame->StyleVisibility()->IsVisible()) {
michael@0 1449 flags |= SVG_HIT_TEST_STROKE;
michael@0 1450 }
michael@0 1451 break;
michael@0 1452 case NS_STYLE_POINTER_EVENTS_VISIBLE:
michael@0 1453 if (aFrame->StyleVisibility()->IsVisible()) {
michael@0 1454 flags |= SVG_HIT_TEST_FILL | SVG_HIT_TEST_STROKE;
michael@0 1455 }
michael@0 1456 break;
michael@0 1457 case NS_STYLE_POINTER_EVENTS_PAINTED:
michael@0 1458 if (aFrame->StyleSVG()->mFill.mType != eStyleSVGPaintType_None)
michael@0 1459 flags |= SVG_HIT_TEST_FILL;
michael@0 1460 if (aFrame->StyleSVG()->mStroke.mType != eStyleSVGPaintType_None)
michael@0 1461 flags |= SVG_HIT_TEST_STROKE;
michael@0 1462 if (aFrame->StyleSVG()->mStrokeOpacity)
michael@0 1463 flags |= SVG_HIT_TEST_CHECK_MRECT;
michael@0 1464 break;
michael@0 1465 case NS_STYLE_POINTER_EVENTS_FILL:
michael@0 1466 flags |= SVG_HIT_TEST_FILL;
michael@0 1467 break;
michael@0 1468 case NS_STYLE_POINTER_EVENTS_STROKE:
michael@0 1469 flags |= SVG_HIT_TEST_STROKE;
michael@0 1470 break;
michael@0 1471 case NS_STYLE_POINTER_EVENTS_ALL:
michael@0 1472 flags |= SVG_HIT_TEST_FILL | SVG_HIT_TEST_STROKE;
michael@0 1473 break;
michael@0 1474 default:
michael@0 1475 NS_ERROR("not reached");
michael@0 1476 break;
michael@0 1477 }
michael@0 1478
michael@0 1479 return flags;
michael@0 1480 }
michael@0 1481
michael@0 1482 bool
michael@0 1483 nsSVGUtils::SetupCairoStroke(nsIFrame* aFrame, gfxContext* aContext,
michael@0 1484 gfxTextContextPaint *aContextPaint)
michael@0 1485 {
michael@0 1486 if (!HasStroke(aFrame, aContextPaint)) {
michael@0 1487 return false;
michael@0 1488 }
michael@0 1489 SetupCairoStrokeGeometry(aFrame, aContext, aContextPaint);
michael@0 1490
michael@0 1491 return SetupCairoStrokePaint(aFrame, aContext, aContextPaint);
michael@0 1492 }
michael@0 1493
michael@0 1494 bool
michael@0 1495 nsSVGUtils::PaintSVGGlyph(Element* aElement, gfxContext* aContext,
michael@0 1496 DrawMode aDrawMode,
michael@0 1497 gfxTextContextPaint* aContextPaint)
michael@0 1498 {
michael@0 1499 nsIFrame* frame = aElement->GetPrimaryFrame();
michael@0 1500 nsISVGChildFrame* svgFrame = do_QueryFrame(frame);
michael@0 1501 if (!svgFrame) {
michael@0 1502 return false;
michael@0 1503 }
michael@0 1504 nsRefPtr<nsRenderingContext> context(new nsRenderingContext());
michael@0 1505 context->Init(frame->PresContext()->DeviceContext(), aContext);
michael@0 1506 context->AddUserData(&gfxTextContextPaint::sUserDataKey, aContextPaint,
michael@0 1507 nullptr);
michael@0 1508 svgFrame->NotifySVGChanged(nsISVGChildFrame::TRANSFORM_CHANGED);
michael@0 1509 nsresult rv = svgFrame->PaintSVG(context, nullptr, frame);
michael@0 1510 return NS_SUCCEEDED(rv);
michael@0 1511 }
michael@0 1512
michael@0 1513 bool
michael@0 1514 nsSVGUtils::GetSVGGlyphExtents(Element* aElement,
michael@0 1515 const gfxMatrix& aSVGToAppSpace,
michael@0 1516 gfxRect* aResult)
michael@0 1517 {
michael@0 1518 nsIFrame* frame = aElement->GetPrimaryFrame();
michael@0 1519 nsISVGChildFrame* svgFrame = do_QueryFrame(frame);
michael@0 1520 if (!svgFrame) {
michael@0 1521 return false;
michael@0 1522 }
michael@0 1523
michael@0 1524 gfxMatrix transform(aSVGToAppSpace);
michael@0 1525 nsIContent* content = frame->GetContent();
michael@0 1526 if (content->IsSVG()) {
michael@0 1527 transform = static_cast<nsSVGElement*>(content)->
michael@0 1528 PrependLocalTransformsTo(aSVGToAppSpace);
michael@0 1529 }
michael@0 1530
michael@0 1531 *aResult = svgFrame->GetBBoxContribution(gfx::ToMatrix(transform),
michael@0 1532 nsSVGUtils::eBBoxIncludeFill | nsSVGUtils::eBBoxIncludeFillGeometry |
michael@0 1533 nsSVGUtils::eBBoxIncludeStroke | nsSVGUtils::eBBoxIncludeStrokeGeometry |
michael@0 1534 nsSVGUtils::eBBoxIncludeMarkers).ToThebesRect();
michael@0 1535 return true;
michael@0 1536 }
michael@0 1537
michael@0 1538 nsRect
michael@0 1539 nsSVGUtils::ToCanvasBounds(const gfxRect &aUserspaceRect,
michael@0 1540 const gfxMatrix &aToCanvas,
michael@0 1541 const nsPresContext *presContext)
michael@0 1542 {
michael@0 1543 return nsLayoutUtils::RoundGfxRectToAppRect(
michael@0 1544 aToCanvas.TransformBounds(aUserspaceRect),
michael@0 1545 presContext->AppUnitsPerDevPixel());
michael@0 1546 }

mercurial