layout/svg/nsSVGPathGeometryFrame.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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 #include "nsSVGPathGeometryFrame.h"
michael@0 8
michael@0 9 // Keep others in (case-insensitive) order:
michael@0 10 #include "gfxContext.h"
michael@0 11 #include "gfxPlatform.h"
michael@0 12 #include "gfxSVGGlyphs.h"
michael@0 13 #include "nsDisplayList.h"
michael@0 14 #include "nsGkAtoms.h"
michael@0 15 #include "nsRenderingContext.h"
michael@0 16 #include "nsSVGEffects.h"
michael@0 17 #include "nsSVGIntegrationUtils.h"
michael@0 18 #include "nsSVGMarkerFrame.h"
michael@0 19 #include "nsSVGPathGeometryElement.h"
michael@0 20 #include "nsSVGUtils.h"
michael@0 21 #include "mozilla/ArrayUtils.h"
michael@0 22 #include "SVGAnimatedTransformList.h"
michael@0 23 #include "SVGGraphicsElement.h"
michael@0 24
michael@0 25 using namespace mozilla;
michael@0 26 using namespace mozilla::gfx;
michael@0 27
michael@0 28 //----------------------------------------------------------------------
michael@0 29 // Implementation
michael@0 30
michael@0 31 nsIFrame*
michael@0 32 NS_NewSVGPathGeometryFrame(nsIPresShell* aPresShell,
michael@0 33 nsStyleContext* aContext)
michael@0 34 {
michael@0 35 return new (aPresShell) nsSVGPathGeometryFrame(aContext);
michael@0 36 }
michael@0 37
michael@0 38 NS_IMPL_FRAMEARENA_HELPERS(nsSVGPathGeometryFrame)
michael@0 39
michael@0 40 //----------------------------------------------------------------------
michael@0 41 // nsQueryFrame methods
michael@0 42
michael@0 43 NS_QUERYFRAME_HEAD(nsSVGPathGeometryFrame)
michael@0 44 NS_QUERYFRAME_ENTRY(nsISVGChildFrame)
michael@0 45 NS_QUERYFRAME_ENTRY(nsSVGPathGeometryFrame)
michael@0 46 NS_QUERYFRAME_TAIL_INHERITING(nsSVGPathGeometryFrameBase)
michael@0 47
michael@0 48 //----------------------------------------------------------------------
michael@0 49 // Display list item:
michael@0 50
michael@0 51 class nsDisplaySVGPathGeometry : public nsDisplayItem {
michael@0 52 public:
michael@0 53 nsDisplaySVGPathGeometry(nsDisplayListBuilder* aBuilder,
michael@0 54 nsSVGPathGeometryFrame* aFrame)
michael@0 55 : nsDisplayItem(aBuilder, aFrame)
michael@0 56 {
michael@0 57 MOZ_COUNT_CTOR(nsDisplaySVGPathGeometry);
michael@0 58 NS_ABORT_IF_FALSE(aFrame, "Must have a frame!");
michael@0 59 }
michael@0 60 #ifdef NS_BUILD_REFCNT_LOGGING
michael@0 61 virtual ~nsDisplaySVGPathGeometry() {
michael@0 62 MOZ_COUNT_DTOR(nsDisplaySVGPathGeometry);
michael@0 63 }
michael@0 64 #endif
michael@0 65
michael@0 66 NS_DISPLAY_DECL_NAME("nsDisplaySVGPathGeometry", TYPE_SVG_PATH_GEOMETRY)
michael@0 67
michael@0 68 virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
michael@0 69 HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames);
michael@0 70 virtual void Paint(nsDisplayListBuilder* aBuilder,
michael@0 71 nsRenderingContext* aCtx);
michael@0 72 };
michael@0 73
michael@0 74 void
michael@0 75 nsDisplaySVGPathGeometry::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
michael@0 76 HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
michael@0 77 {
michael@0 78 nsSVGPathGeometryFrame *frame = static_cast<nsSVGPathGeometryFrame*>(mFrame);
michael@0 79 nsPoint pointRelativeToReferenceFrame = aRect.Center();
michael@0 80 // ToReferenceFrame() includes frame->GetPosition(), our user space position.
michael@0 81 nsPoint userSpacePt = pointRelativeToReferenceFrame -
michael@0 82 (ToReferenceFrame() - frame->GetPosition());
michael@0 83 if (frame->GetFrameForPoint(userSpacePt)) {
michael@0 84 aOutFrames->AppendElement(frame);
michael@0 85 }
michael@0 86 }
michael@0 87
michael@0 88 void
michael@0 89 nsDisplaySVGPathGeometry::Paint(nsDisplayListBuilder* aBuilder,
michael@0 90 nsRenderingContext* aCtx)
michael@0 91 {
michael@0 92 // ToReferenceFrame includes our mRect offset, but painting takes
michael@0 93 // account of that too. To avoid double counting, we subtract that
michael@0 94 // here.
michael@0 95 nsPoint offset = ToReferenceFrame() - mFrame->GetPosition();
michael@0 96
michael@0 97 aCtx->PushState();
michael@0 98 aCtx->Translate(offset);
michael@0 99 static_cast<nsSVGPathGeometryFrame*>(mFrame)->PaintSVG(aCtx, nullptr);
michael@0 100 aCtx->PopState();
michael@0 101 }
michael@0 102
michael@0 103 //----------------------------------------------------------------------
michael@0 104 // nsIFrame methods
michael@0 105
michael@0 106 void
michael@0 107 nsSVGPathGeometryFrame::Init(nsIContent* aContent,
michael@0 108 nsIFrame* aParent,
michael@0 109 nsIFrame* aPrevInFlow)
michael@0 110 {
michael@0 111 AddStateBits(aParent->GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD);
michael@0 112 nsSVGPathGeometryFrameBase::Init(aContent, aParent, aPrevInFlow);
michael@0 113 }
michael@0 114
michael@0 115 nsresult
michael@0 116 nsSVGPathGeometryFrame::AttributeChanged(int32_t aNameSpaceID,
michael@0 117 nsIAtom* aAttribute,
michael@0 118 int32_t aModType)
michael@0 119 {
michael@0 120 // We don't invalidate for transform changes (the layers code does that).
michael@0 121 // Also note that SVGTransformableElement::GetAttributeChangeHint will
michael@0 122 // return nsChangeHint_UpdateOverflow for "transform" attribute changes
michael@0 123 // and cause DoApplyRenderingChangeToTree to make the SchedulePaint call.
michael@0 124
michael@0 125 if (aNameSpaceID == kNameSpaceID_None &&
michael@0 126 (static_cast<nsSVGPathGeometryElement*>
michael@0 127 (mContent)->AttributeDefinesGeometry(aAttribute))) {
michael@0 128 nsSVGEffects::InvalidateRenderingObservers(this);
michael@0 129 nsSVGUtils::ScheduleReflowSVG(this);
michael@0 130 }
michael@0 131 return NS_OK;
michael@0 132 }
michael@0 133
michael@0 134 /* virtual */ void
michael@0 135 nsSVGPathGeometryFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
michael@0 136 {
michael@0 137 nsSVGPathGeometryFrameBase::DidSetStyleContext(aOldStyleContext);
michael@0 138
michael@0 139 if (aOldStyleContext) {
michael@0 140 float oldOpacity = aOldStyleContext->PeekStyleDisplay()->mOpacity;
michael@0 141 float newOpacity = StyleDisplay()->mOpacity;
michael@0 142 if (newOpacity != oldOpacity &&
michael@0 143 nsSVGUtils::CanOptimizeOpacity(this)) {
michael@0 144 // nsIFrame::BuildDisplayListForStackingContext() is not going to create an
michael@0 145 // nsDisplayOpacity display list item, so DLBI won't invalidate for us.
michael@0 146 InvalidateFrame();
michael@0 147 }
michael@0 148 }
michael@0 149 }
michael@0 150
michael@0 151 nsIAtom *
michael@0 152 nsSVGPathGeometryFrame::GetType() const
michael@0 153 {
michael@0 154 return nsGkAtoms::svgPathGeometryFrame;
michael@0 155 }
michael@0 156
michael@0 157 bool
michael@0 158 nsSVGPathGeometryFrame::IsSVGTransformed(gfx::Matrix *aOwnTransform,
michael@0 159 gfx::Matrix *aFromParentTransform) const
michael@0 160 {
michael@0 161 bool foundTransform = false;
michael@0 162
michael@0 163 // Check if our parent has children-only transforms:
michael@0 164 nsIFrame *parent = GetParent();
michael@0 165 if (parent &&
michael@0 166 parent->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer)) {
michael@0 167 foundTransform = static_cast<nsSVGContainerFrame*>(parent)->
michael@0 168 HasChildrenOnlyTransform(aFromParentTransform);
michael@0 169 }
michael@0 170
michael@0 171 nsSVGElement *content = static_cast<nsSVGElement*>(mContent);
michael@0 172 nsSVGAnimatedTransformList* transformList =
michael@0 173 content->GetAnimatedTransformList();
michael@0 174 if ((transformList && transformList->HasTransform()) ||
michael@0 175 content->GetAnimateMotionTransform()) {
michael@0 176 if (aOwnTransform) {
michael@0 177 *aOwnTransform = gfx::ToMatrix(content->PrependLocalTransformsTo(gfxMatrix(),
michael@0 178 nsSVGElement::eUserSpaceToParent));
michael@0 179 }
michael@0 180 foundTransform = true;
michael@0 181 }
michael@0 182 return foundTransform;
michael@0 183 }
michael@0 184
michael@0 185 void
michael@0 186 nsSVGPathGeometryFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
michael@0 187 const nsRect& aDirtyRect,
michael@0 188 const nsDisplayListSet& aLists)
michael@0 189 {
michael@0 190 if (!static_cast<const nsSVGElement*>(mContent)->HasValidDimensions()) {
michael@0 191 return;
michael@0 192 }
michael@0 193 aLists.Content()->AppendNewToTop(
michael@0 194 new (aBuilder) nsDisplaySVGPathGeometry(aBuilder, this));
michael@0 195 }
michael@0 196
michael@0 197 //----------------------------------------------------------------------
michael@0 198 // nsISVGChildFrame methods
michael@0 199
michael@0 200 nsresult
michael@0 201 nsSVGPathGeometryFrame::PaintSVG(nsRenderingContext *aContext,
michael@0 202 const nsIntRect *aDirtyRect,
michael@0 203 nsIFrame* aTransformRoot)
michael@0 204 {
michael@0 205 if (!StyleVisibility()->IsVisible())
michael@0 206 return NS_OK;
michael@0 207
michael@0 208 uint32_t paintOrder = StyleSVG()->mPaintOrder;
michael@0 209 if (paintOrder == NS_STYLE_PAINT_ORDER_NORMAL) {
michael@0 210 Render(aContext, eRenderFill | eRenderStroke, aTransformRoot);
michael@0 211 PaintMarkers(aContext);
michael@0 212 } else {
michael@0 213 while (paintOrder) {
michael@0 214 uint32_t component =
michael@0 215 paintOrder & ((1 << NS_STYLE_PAINT_ORDER_BITWIDTH) - 1);
michael@0 216 switch (component) {
michael@0 217 case NS_STYLE_PAINT_ORDER_FILL:
michael@0 218 Render(aContext, eRenderFill, aTransformRoot);
michael@0 219 break;
michael@0 220 case NS_STYLE_PAINT_ORDER_STROKE:
michael@0 221 Render(aContext, eRenderStroke, aTransformRoot);
michael@0 222 break;
michael@0 223 case NS_STYLE_PAINT_ORDER_MARKERS:
michael@0 224 PaintMarkers(aContext);
michael@0 225 break;
michael@0 226 }
michael@0 227 paintOrder >>= NS_STYLE_PAINT_ORDER_BITWIDTH;
michael@0 228 }
michael@0 229 }
michael@0 230
michael@0 231 return NS_OK;
michael@0 232 }
michael@0 233
michael@0 234 nsIFrame*
michael@0 235 nsSVGPathGeometryFrame::GetFrameForPoint(const nsPoint &aPoint)
michael@0 236 {
michael@0 237 gfxMatrix canvasTM = GetCanvasTM(FOR_HIT_TESTING);
michael@0 238 if (canvasTM.IsSingular()) {
michael@0 239 return nullptr;
michael@0 240 }
michael@0 241 uint16_t fillRule, hitTestFlags;
michael@0 242 if (GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD) {
michael@0 243 hitTestFlags = SVG_HIT_TEST_FILL;
michael@0 244 fillRule = StyleSVG()->mClipRule;
michael@0 245 } else {
michael@0 246 hitTestFlags = GetHitTestFlags();
michael@0 247 // XXX once bug 614732 is fixed, aPoint won't need any conversion in order
michael@0 248 // to compare it with mRect.
michael@0 249 nsPoint point =
michael@0 250 nsSVGUtils::TransformOuterSVGPointToChildFrame(aPoint, canvasTM, PresContext());
michael@0 251 if (!hitTestFlags || ((hitTestFlags & SVG_HIT_TEST_CHECK_MRECT) &&
michael@0 252 !mRect.Contains(point)))
michael@0 253 return nullptr;
michael@0 254 fillRule = StyleSVG()->mFillRule;
michael@0 255 }
michael@0 256
michael@0 257 bool isHit = false;
michael@0 258
michael@0 259 nsRefPtr<gfxContext> tmpCtx =
michael@0 260 new gfxContext(gfxPlatform::GetPlatform()->ScreenReferenceSurface());
michael@0 261
michael@0 262 GeneratePath(tmpCtx, ToMatrix(canvasTM));
michael@0 263 gfxPoint userSpacePoint =
michael@0 264 tmpCtx->DeviceToUser(gfxPoint(PresContext()->AppUnitsToGfxUnits(aPoint.x),
michael@0 265 PresContext()->AppUnitsToGfxUnits(aPoint.y)));
michael@0 266
michael@0 267 if (fillRule == NS_STYLE_FILL_RULE_EVENODD)
michael@0 268 tmpCtx->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);
michael@0 269 else
michael@0 270 tmpCtx->SetFillRule(gfxContext::FILL_RULE_WINDING);
michael@0 271
michael@0 272 if (hitTestFlags & SVG_HIT_TEST_FILL)
michael@0 273 isHit = tmpCtx->PointInFill(userSpacePoint);
michael@0 274 if (!isHit && (hitTestFlags & SVG_HIT_TEST_STROKE)) {
michael@0 275 nsSVGUtils::SetupCairoStrokeGeometry(this, tmpCtx);
michael@0 276 // tmpCtx's matrix may have transformed by SetupCairoStrokeGeometry
michael@0 277 // if there is a non-scaling stroke. We need to transform userSpacePoint
michael@0 278 // so that everything is using the same co-ordinate system.
michael@0 279 userSpacePoint =
michael@0 280 nsSVGUtils::GetStrokeTransform(this).Invert().Transform(userSpacePoint);
michael@0 281 isHit = tmpCtx->PointInStroke(userSpacePoint);
michael@0 282 }
michael@0 283
michael@0 284 if (isHit && nsSVGUtils::HitTestClip(this, aPoint))
michael@0 285 return this;
michael@0 286
michael@0 287 return nullptr;
michael@0 288 }
michael@0 289
michael@0 290 nsRect
michael@0 291 nsSVGPathGeometryFrame::GetCoveredRegion()
michael@0 292 {
michael@0 293 return nsSVGUtils::TransformFrameRectToOuterSVG(
michael@0 294 mRect, GetCanvasTM(FOR_OUTERSVG_TM), PresContext());
michael@0 295 }
michael@0 296
michael@0 297 void
michael@0 298 nsSVGPathGeometryFrame::ReflowSVG()
michael@0 299 {
michael@0 300 NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingReflowSVG(this),
michael@0 301 "This call is probably a wasteful mistake");
michael@0 302
michael@0 303 NS_ABORT_IF_FALSE(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY),
michael@0 304 "ReflowSVG mechanism not designed for this");
michael@0 305
michael@0 306 if (!nsSVGUtils::NeedsReflowSVG(this)) {
michael@0 307 return;
michael@0 308 }
michael@0 309
michael@0 310 uint32_t flags = nsSVGUtils::eBBoxIncludeFill |
michael@0 311 nsSVGUtils::eBBoxIncludeStroke |
michael@0 312 nsSVGUtils::eBBoxIncludeMarkers;
michael@0 313 // Our "visual" overflow rect needs to be valid for building display lists
michael@0 314 // for hit testing, which means that for certain values of 'pointer-events'
michael@0 315 // it needs to include the geometry of the fill or stroke even when the fill/
michael@0 316 // stroke don't actually render (e.g. when stroke="none" or
michael@0 317 // stroke-opacity="0"). GetHitTestFlags() accounts for 'pointer-events'.
michael@0 318 uint16_t hitTestFlags = GetHitTestFlags();
michael@0 319 if ((hitTestFlags & SVG_HIT_TEST_FILL)) {
michael@0 320 flags |= nsSVGUtils::eBBoxIncludeFillGeometry;
michael@0 321 }
michael@0 322 if ((hitTestFlags & SVG_HIT_TEST_STROKE)) {
michael@0 323 flags |= nsSVGUtils::eBBoxIncludeStrokeGeometry;
michael@0 324 }
michael@0 325
michael@0 326 // We'd like to just pass the identity matrix to GetBBoxContribution, but if
michael@0 327 // this frame's user space size is _very_ large/small then the extents we
michael@0 328 // obtain below might have overflowed or otherwise be broken. This would
michael@0 329 // cause us to end up with a broken mRect and visual overflow rect and break
michael@0 330 // painting of this frame. This is particularly noticeable if the transforms
michael@0 331 // between us and our nsSVGOuterSVGFrame scale this frame to a reasonable
michael@0 332 // size. To avoid this we sadly have to do extra work to account for the
michael@0 333 // transforms between us and our nsSVGOuterSVGFrame, even though the
michael@0 334 // overwhelming number of SVGs will never have this problem.
michael@0 335 // XXX Will Azure eventually save us from having to do this?
michael@0 336 gfxSize scaleFactors = GetCanvasTM(FOR_OUTERSVG_TM).ScaleFactors(true);
michael@0 337 bool applyScaling = fabs(scaleFactors.width) >= 1e-6 &&
michael@0 338 fabs(scaleFactors.height) >= 1e-6;
michael@0 339 gfx::Matrix scaling;
michael@0 340 if (applyScaling) {
michael@0 341 scaling.Scale(scaleFactors.width, scaleFactors.height);
michael@0 342 }
michael@0 343 gfxRect extent = GetBBoxContribution(scaling, flags).ToThebesRect();
michael@0 344 if (applyScaling) {
michael@0 345 extent.Scale(1 / scaleFactors.width, 1 / scaleFactors.height);
michael@0 346 }
michael@0 347 mRect = nsLayoutUtils::RoundGfxRectToAppRect(extent,
michael@0 348 PresContext()->AppUnitsPerCSSPixel());
michael@0 349
michael@0 350 if (mState & NS_FRAME_FIRST_REFLOW) {
michael@0 351 // Make sure we have our filter property (if any) before calling
michael@0 352 // FinishAndStoreOverflow (subsequent filter changes are handled off
michael@0 353 // nsChangeHint_UpdateEffects):
michael@0 354 nsSVGEffects::UpdateEffects(this);
michael@0 355 }
michael@0 356
michael@0 357 nsRect overflow = nsRect(nsPoint(0,0), mRect.Size());
michael@0 358 nsOverflowAreas overflowAreas(overflow, overflow);
michael@0 359 FinishAndStoreOverflow(overflowAreas, mRect.Size());
michael@0 360
michael@0 361 mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
michael@0 362 NS_FRAME_HAS_DIRTY_CHILDREN);
michael@0 363
michael@0 364 // Invalidate, but only if this is not our first reflow (since if it is our
michael@0 365 // first reflow then we haven't had our first paint yet).
michael@0 366 if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
michael@0 367 InvalidateFrame();
michael@0 368 }
michael@0 369 }
michael@0 370
michael@0 371 void
michael@0 372 nsSVGPathGeometryFrame::NotifySVGChanged(uint32_t aFlags)
michael@0 373 {
michael@0 374 NS_ABORT_IF_FALSE(aFlags & (TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED),
michael@0 375 "Invalidation logic may need adjusting");
michael@0 376
michael@0 377 // Changes to our ancestors may affect how we render when we are rendered as
michael@0 378 // part of our ancestor (specifically, if our coordinate context changes size
michael@0 379 // and we have percentage lengths defining our geometry, then we need to be
michael@0 380 // reflowed). However, ancestor changes cannot affect how we render when we
michael@0 381 // are rendered as part of any rendering observers that we may have.
michael@0 382 // Therefore no need to notify rendering observers here.
michael@0 383
michael@0 384 // Don't try to be too smart trying to avoid the ScheduleReflowSVG calls
michael@0 385 // for the stroke properties examined below. Checking HasStroke() is not
michael@0 386 // enough, since what we care about is whether we include the stroke in our
michael@0 387 // overflow rects or not, and we sometimes deliberately include stroke
michael@0 388 // when it's not visible. See the complexities of GetBBoxContribution.
michael@0 389
michael@0 390 if (aFlags & COORD_CONTEXT_CHANGED) {
michael@0 391 // Stroke currently contributes to our mRect, which is why we have to take
michael@0 392 // account of stroke-width here. Note that we do not need to take account
michael@0 393 // of stroke-dashoffset since, although that can have a percentage value
michael@0 394 // that is resolved against our coordinate context, it does not affect our
michael@0 395 // mRect.
michael@0 396 if (static_cast<nsSVGPathGeometryElement*>(mContent)->GeometryDependsOnCoordCtx() ||
michael@0 397 StyleSVG()->mStrokeWidth.HasPercent()) {
michael@0 398 nsSVGUtils::ScheduleReflowSVG(this);
michael@0 399 }
michael@0 400 }
michael@0 401
michael@0 402 if ((aFlags & TRANSFORM_CHANGED) &&
michael@0 403 StyleSVGReset()->mVectorEffect ==
michael@0 404 NS_STYLE_VECTOR_EFFECT_NON_SCALING_STROKE) {
michael@0 405 // Stroke currently contributes to our mRect, and our stroke depends on
michael@0 406 // the transform to our outer-<svg> if |vector-effect:non-scaling-stroke|.
michael@0 407 nsSVGUtils::ScheduleReflowSVG(this);
michael@0 408 }
michael@0 409 }
michael@0 410
michael@0 411 SVGBBox
michael@0 412 nsSVGPathGeometryFrame::GetBBoxContribution(const Matrix &aToBBoxUserspace,
michael@0 413 uint32_t aFlags)
michael@0 414 {
michael@0 415 SVGBBox bbox;
michael@0 416
michael@0 417 if (aToBBoxUserspace.IsSingular()) {
michael@0 418 // XXX ReportToConsole
michael@0 419 return bbox;
michael@0 420 }
michael@0 421
michael@0 422 nsRefPtr<gfxContext> tmpCtx =
michael@0 423 new gfxContext(gfxPlatform::GetPlatform()->ScreenReferenceSurface());
michael@0 424
michael@0 425 GeneratePath(tmpCtx, aToBBoxUserspace);
michael@0 426 tmpCtx->IdentityMatrix();
michael@0 427
michael@0 428 // Be careful when replacing the following logic to get the fill and stroke
michael@0 429 // extents independently (instead of computing the stroke extents from the
michael@0 430 // path extents). You may think that you can just use the stroke extents if
michael@0 431 // there is both a fill and a stroke. In reality it's necessary to calculate
michael@0 432 // both the fill and stroke extents, and take the union of the two. There are
michael@0 433 // two reasons for this:
michael@0 434 //
michael@0 435 // # Due to stroke dashing, in certain cases the fill extents could actually
michael@0 436 // extend outside the stroke extents.
michael@0 437 // # If the stroke is very thin, cairo won't paint any stroke, and so the
michael@0 438 // stroke bounds that it will return will be empty.
michael@0 439
michael@0 440 gfxRect pathExtents = tmpCtx->GetUserPathExtent();
michael@0 441
michael@0 442 // Account for fill:
michael@0 443 if ((aFlags & nsSVGUtils::eBBoxIncludeFillGeometry) ||
michael@0 444 ((aFlags & nsSVGUtils::eBBoxIncludeFill) &&
michael@0 445 StyleSVG()->mFill.mType != eStyleSVGPaintType_None)) {
michael@0 446 bbox = pathExtents;
michael@0 447 }
michael@0 448
michael@0 449 // Account for stroke:
michael@0 450 if ((aFlags & nsSVGUtils::eBBoxIncludeStrokeGeometry) ||
michael@0 451 ((aFlags & nsSVGUtils::eBBoxIncludeStroke) &&
michael@0 452 nsSVGUtils::HasStroke(this))) {
michael@0 453 // We can't use tmpCtx->GetUserStrokeExtent() since it doesn't work for
michael@0 454 // device space extents. Instead we approximate the stroke extents from
michael@0 455 // pathExtents using PathExtentsToMaxStrokeExtents.
michael@0 456 if (pathExtents.Width() <= 0 && pathExtents.Height() <= 0) {
michael@0 457 // We have a zero length path, but it may still have non-empty stroke
michael@0 458 // bounds depending on the value of stroke-linecap. We need to fix up
michael@0 459 // pathExtents before it can be used with PathExtentsToMaxStrokeExtents
michael@0 460 // though, because if pathExtents is empty, its position will not have
michael@0 461 // been set. Happily we can use tmpCtx->GetUserStrokeExtent() to find
michael@0 462 // the center point of the extents even though it gets the extents wrong.
michael@0 463 nsSVGUtils::SetupCairoStrokeBBoxGeometry(this, tmpCtx);
michael@0 464 pathExtents.MoveTo(tmpCtx->GetUserStrokeExtent().Center());
michael@0 465 pathExtents.SizeTo(0, 0);
michael@0 466 }
michael@0 467 bbox.UnionEdges(nsSVGUtils::PathExtentsToMaxStrokeExtents(pathExtents,
michael@0 468 this,
michael@0 469 ThebesMatrix(aToBBoxUserspace)));
michael@0 470 }
michael@0 471
michael@0 472 // Account for markers:
michael@0 473 if ((aFlags & nsSVGUtils::eBBoxIncludeMarkers) != 0 &&
michael@0 474 static_cast<nsSVGPathGeometryElement*>(mContent)->IsMarkable()) {
michael@0 475
michael@0 476 float strokeWidth = nsSVGUtils::GetStrokeWidth(this);
michael@0 477 MarkerProperties properties = GetMarkerProperties(this);
michael@0 478
michael@0 479 if (properties.MarkersExist()) {
michael@0 480 nsTArray<nsSVGMark> marks;
michael@0 481 static_cast<nsSVGPathGeometryElement*>(mContent)->GetMarkPoints(&marks);
michael@0 482 uint32_t num = marks.Length();
michael@0 483
michael@0 484 // These are in the same order as the nsSVGMark::Type constants.
michael@0 485 nsSVGMarkerFrame* markerFrames[] = {
michael@0 486 properties.GetMarkerStartFrame(),
michael@0 487 properties.GetMarkerMidFrame(),
michael@0 488 properties.GetMarkerEndFrame(),
michael@0 489 };
michael@0 490 PR_STATIC_ASSERT(MOZ_ARRAY_LENGTH(markerFrames) == nsSVGMark::eTypeCount);
michael@0 491
michael@0 492 for (uint32_t i = 0; i < num; i++) {
michael@0 493 nsSVGMark& mark = marks[i];
michael@0 494 nsSVGMarkerFrame* frame = markerFrames[mark.type];
michael@0 495 if (frame) {
michael@0 496 SVGBBox mbbox =
michael@0 497 frame->GetMarkBBoxContribution(aToBBoxUserspace, aFlags, this,
michael@0 498 &marks[i], strokeWidth);
michael@0 499 bbox.UnionEdges(mbbox);
michael@0 500 }
michael@0 501 }
michael@0 502 }
michael@0 503 }
michael@0 504
michael@0 505 return bbox;
michael@0 506 }
michael@0 507
michael@0 508 //----------------------------------------------------------------------
michael@0 509 // nsSVGPathGeometryFrame methods:
michael@0 510
michael@0 511 gfxMatrix
michael@0 512 nsSVGPathGeometryFrame::GetCanvasTM(uint32_t aFor, nsIFrame* aTransformRoot)
michael@0 513 {
michael@0 514 if (!(GetStateBits() & NS_FRAME_IS_NONDISPLAY) && !aTransformRoot) {
michael@0 515 if ((aFor == FOR_PAINTING && NS_SVGDisplayListPaintingEnabled()) ||
michael@0 516 (aFor == FOR_HIT_TESTING && NS_SVGDisplayListHitTestingEnabled())) {
michael@0 517 return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(this);
michael@0 518 }
michael@0 519 }
michael@0 520
michael@0 521 NS_ASSERTION(mParent, "null parent");
michael@0 522
michael@0 523 nsSVGContainerFrame *parent = static_cast<nsSVGContainerFrame*>(mParent);
michael@0 524 dom::SVGGraphicsElement *content = static_cast<dom::SVGGraphicsElement*>(mContent);
michael@0 525
michael@0 526 return content->PrependLocalTransformsTo(
michael@0 527 this == aTransformRoot ? gfxMatrix() :
michael@0 528 parent->GetCanvasTM(aFor, aTransformRoot));
michael@0 529 }
michael@0 530
michael@0 531 nsSVGPathGeometryFrame::MarkerProperties
michael@0 532 nsSVGPathGeometryFrame::GetMarkerProperties(nsSVGPathGeometryFrame *aFrame)
michael@0 533 {
michael@0 534 NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame should be first continuation");
michael@0 535
michael@0 536 MarkerProperties result;
michael@0 537 const nsStyleSVG *style = aFrame->StyleSVG();
michael@0 538 result.mMarkerStart =
michael@0 539 nsSVGEffects::GetMarkerProperty(style->mMarkerStart, aFrame,
michael@0 540 nsSVGEffects::MarkerBeginProperty());
michael@0 541 result.mMarkerMid =
michael@0 542 nsSVGEffects::GetMarkerProperty(style->mMarkerMid, aFrame,
michael@0 543 nsSVGEffects::MarkerMiddleProperty());
michael@0 544 result.mMarkerEnd =
michael@0 545 nsSVGEffects::GetMarkerProperty(style->mMarkerEnd, aFrame,
michael@0 546 nsSVGEffects::MarkerEndProperty());
michael@0 547 return result;
michael@0 548 }
michael@0 549
michael@0 550 nsSVGMarkerFrame *
michael@0 551 nsSVGPathGeometryFrame::MarkerProperties::GetMarkerStartFrame()
michael@0 552 {
michael@0 553 if (!mMarkerStart)
michael@0 554 return nullptr;
michael@0 555 return static_cast<nsSVGMarkerFrame *>
michael@0 556 (mMarkerStart->GetReferencedFrame(nsGkAtoms::svgMarkerFrame, nullptr));
michael@0 557 }
michael@0 558
michael@0 559 nsSVGMarkerFrame *
michael@0 560 nsSVGPathGeometryFrame::MarkerProperties::GetMarkerMidFrame()
michael@0 561 {
michael@0 562 if (!mMarkerMid)
michael@0 563 return nullptr;
michael@0 564 return static_cast<nsSVGMarkerFrame *>
michael@0 565 (mMarkerMid->GetReferencedFrame(nsGkAtoms::svgMarkerFrame, nullptr));
michael@0 566 }
michael@0 567
michael@0 568 nsSVGMarkerFrame *
michael@0 569 nsSVGPathGeometryFrame::MarkerProperties::GetMarkerEndFrame()
michael@0 570 {
michael@0 571 if (!mMarkerEnd)
michael@0 572 return nullptr;
michael@0 573 return static_cast<nsSVGMarkerFrame *>
michael@0 574 (mMarkerEnd->GetReferencedFrame(nsGkAtoms::svgMarkerFrame, nullptr));
michael@0 575 }
michael@0 576
michael@0 577 void
michael@0 578 nsSVGPathGeometryFrame::Render(nsRenderingContext *aContext,
michael@0 579 uint32_t aRenderComponents,
michael@0 580 nsIFrame* aTransformRoot)
michael@0 581 {
michael@0 582 gfxContext *gfx = aContext->ThebesContext();
michael@0 583
michael@0 584 uint16_t renderMode = SVGAutoRenderState::GetRenderMode(aContext);
michael@0 585
michael@0 586 switch (StyleSVG()->mShapeRendering) {
michael@0 587 case NS_STYLE_SHAPE_RENDERING_OPTIMIZESPEED:
michael@0 588 case NS_STYLE_SHAPE_RENDERING_CRISPEDGES:
michael@0 589 gfx->SetAntialiasMode(gfxContext::MODE_ALIASED);
michael@0 590 break;
michael@0 591 default:
michael@0 592 gfx->SetAntialiasMode(gfxContext::MODE_COVERAGE);
michael@0 593 break;
michael@0 594 }
michael@0 595
michael@0 596 if (renderMode != SVGAutoRenderState::NORMAL) {
michael@0 597 NS_ABORT_IF_FALSE(renderMode == SVGAutoRenderState::CLIP ||
michael@0 598 renderMode == SVGAutoRenderState::CLIP_MASK,
michael@0 599 "Unknown render mode");
michael@0 600
michael@0 601 // In the case that |renderMode == SVGAutoRenderState::CLIP| then we don't
michael@0 602 // use the path we generate here until further up the call stack when
michael@0 603 // nsSVGClipPathFrame::Clip calls gfxContext::Clip. That's a problem for
michael@0 604 // Moz2D which emits paths in user space (unlike cairo which emits paths in
michael@0 605 // device space). gfxContext has hacks to deal with code changing the
michael@0 606 // transform then using the current path when it is backed by Moz2D, but
michael@0 607 // Moz2D itself does not since that would fundamentally go against its API.
michael@0 608 // Therefore we do not want to Save()/Restore() the gfxContext here in the
michael@0 609 // SVGAutoRenderState::CLIP case since that would block us from killing off
michael@0 610 // gfxContext and using Moz2D directly. Not bothering to Save()/Restore()
michael@0 611 // is actually okay, since we know that doesn't matter in the
michael@0 612 // SVGAutoRenderState::CLIP case (at least for the current implementation).
michael@0 613 gfxContextMatrixAutoSaveRestore autoSaveRestore;
michael@0 614 // For now revent back to doing the save even for CLIP to fix bug 959128.
michael@0 615 // Undo in bug 987193.
michael@0 616 //if (renderMode != SVGAutoRenderState::CLIP) {
michael@0 617 autoSaveRestore.SetContext(gfx);
michael@0 618 //}
michael@0 619
michael@0 620 GeneratePath(gfx, ToMatrix(GetCanvasTM(FOR_PAINTING, aTransformRoot)));
michael@0 621
michael@0 622 // We used to call gfx->Restore() here, since for the
michael@0 623 // SVGAutoRenderState::CLIP case it is important to leave the fill rule
michael@0 624 // that we set below untouched so that the value is still set when return
michael@0 625 // to gfxContext::Clip() further up the call stack. Since we no longer
michael@0 626 // call gfx->Save() in the SVGAutoRenderState::CLIP case we don't need to
michael@0 627 // worry that autoSaveRestore will delay the Restore() call for the
michael@0 628 // CLIP_MASK case until we exit this function.
michael@0 629
michael@0 630 gfxContext::FillRule oldFillRull = gfx->CurrentFillRule();
michael@0 631
michael@0 632 if (StyleSVG()->mClipRule == NS_STYLE_FILL_RULE_EVENODD)
michael@0 633 gfx->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);
michael@0 634 else
michael@0 635 gfx->SetFillRule(gfxContext::FILL_RULE_WINDING);
michael@0 636
michael@0 637 if (renderMode == SVGAutoRenderState::CLIP_MASK) {
michael@0 638 gfx->SetColor(gfxRGBA(1.0f, 1.0f, 1.0f, 1.0f));
michael@0 639 gfx->Fill();
michael@0 640 gfx->SetFillRule(oldFillRull); // restore, but only for CLIP_MASK
michael@0 641 gfx->NewPath();
michael@0 642 }
michael@0 643
michael@0 644 return;
michael@0 645 }
michael@0 646
michael@0 647 gfxContextAutoSaveRestore autoSaveRestore(gfx);
michael@0 648
michael@0 649 GeneratePath(gfx, ToMatrix(GetCanvasTM(FOR_PAINTING, aTransformRoot)));
michael@0 650
michael@0 651 gfxTextContextPaint *contextPaint =
michael@0 652 (gfxTextContextPaint*)aContext->GetUserData(&gfxTextContextPaint::sUserDataKey);
michael@0 653
michael@0 654 if ((aRenderComponents & eRenderFill) &&
michael@0 655 nsSVGUtils::SetupCairoFillPaint(this, gfx, contextPaint)) {
michael@0 656 gfx->Fill();
michael@0 657 }
michael@0 658
michael@0 659 if ((aRenderComponents & eRenderStroke) &&
michael@0 660 nsSVGUtils::SetupCairoStroke(this, gfx, contextPaint)) {
michael@0 661 gfx->Stroke();
michael@0 662 }
michael@0 663
michael@0 664 gfx->NewPath();
michael@0 665 }
michael@0 666
michael@0 667 void
michael@0 668 nsSVGPathGeometryFrame::GeneratePath(gfxContext* aContext,
michael@0 669 const Matrix &aTransform)
michael@0 670 {
michael@0 671 if (aTransform.IsSingular()) {
michael@0 672 aContext->IdentityMatrix();
michael@0 673 aContext->NewPath();
michael@0 674 return;
michael@0 675 }
michael@0 676
michael@0 677 aContext->MultiplyAndNudgeToIntegers(ThebesMatrix(aTransform));
michael@0 678
michael@0 679 // Hack to let SVGPathData::ConstructPath know if we have square caps:
michael@0 680 const nsStyleSVG* style = StyleSVG();
michael@0 681 if (style->mStrokeLinecap == NS_STYLE_STROKE_LINECAP_SQUARE) {
michael@0 682 aContext->SetLineCap(gfxContext::LINE_CAP_SQUARE);
michael@0 683 }
michael@0 684
michael@0 685 aContext->NewPath();
michael@0 686 static_cast<nsSVGPathGeometryElement*>(mContent)->ConstructPath(aContext);
michael@0 687 }
michael@0 688
michael@0 689 void
michael@0 690 nsSVGPathGeometryFrame::PaintMarkers(nsRenderingContext* aContext)
michael@0 691 {
michael@0 692 gfxTextContextPaint *contextPaint =
michael@0 693 (gfxTextContextPaint*)aContext->GetUserData(&gfxTextContextPaint::sUserDataKey);
michael@0 694
michael@0 695 if (static_cast<nsSVGPathGeometryElement*>(mContent)->IsMarkable()) {
michael@0 696 MarkerProperties properties = GetMarkerProperties(this);
michael@0 697
michael@0 698 if (properties.MarkersExist()) {
michael@0 699 float strokeWidth = nsSVGUtils::GetStrokeWidth(this, contextPaint);
michael@0 700
michael@0 701 nsTArray<nsSVGMark> marks;
michael@0 702 static_cast<nsSVGPathGeometryElement*>
michael@0 703 (mContent)->GetMarkPoints(&marks);
michael@0 704
michael@0 705 uint32_t num = marks.Length();
michael@0 706 if (num) {
michael@0 707 // These are in the same order as the nsSVGMark::Type constants.
michael@0 708 nsSVGMarkerFrame* markerFrames[] = {
michael@0 709 properties.GetMarkerStartFrame(),
michael@0 710 properties.GetMarkerMidFrame(),
michael@0 711 properties.GetMarkerEndFrame(),
michael@0 712 };
michael@0 713 PR_STATIC_ASSERT(MOZ_ARRAY_LENGTH(markerFrames) == nsSVGMark::eTypeCount);
michael@0 714
michael@0 715 for (uint32_t i = 0; i < num; i++) {
michael@0 716 nsSVGMark& mark = marks[i];
michael@0 717 nsSVGMarkerFrame* frame = markerFrames[mark.type];
michael@0 718 if (frame) {
michael@0 719 frame->PaintMark(aContext, this, &mark, strokeWidth);
michael@0 720 }
michael@0 721 }
michael@0 722 }
michael@0 723 }
michael@0 724 }
michael@0 725 }
michael@0 726
michael@0 727 uint16_t
michael@0 728 nsSVGPathGeometryFrame::GetHitTestFlags()
michael@0 729 {
michael@0 730 return nsSVGUtils::GetGeometryHitTestFlags(this);
michael@0 731 }

mercurial