layout/svg/nsSVGPathGeometryFrame.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/layout/svg/nsSVGPathGeometryFrame.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,731 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +// Main header first:
    1.10 +#include "nsSVGPathGeometryFrame.h"
    1.11 +
    1.12 +// Keep others in (case-insensitive) order:
    1.13 +#include "gfxContext.h"
    1.14 +#include "gfxPlatform.h"
    1.15 +#include "gfxSVGGlyphs.h"
    1.16 +#include "nsDisplayList.h"
    1.17 +#include "nsGkAtoms.h"
    1.18 +#include "nsRenderingContext.h"
    1.19 +#include "nsSVGEffects.h"
    1.20 +#include "nsSVGIntegrationUtils.h"
    1.21 +#include "nsSVGMarkerFrame.h"
    1.22 +#include "nsSVGPathGeometryElement.h"
    1.23 +#include "nsSVGUtils.h"
    1.24 +#include "mozilla/ArrayUtils.h"
    1.25 +#include "SVGAnimatedTransformList.h"
    1.26 +#include "SVGGraphicsElement.h"
    1.27 +
    1.28 +using namespace mozilla;
    1.29 +using namespace mozilla::gfx;
    1.30 +
    1.31 +//----------------------------------------------------------------------
    1.32 +// Implementation
    1.33 +
    1.34 +nsIFrame*
    1.35 +NS_NewSVGPathGeometryFrame(nsIPresShell* aPresShell,
    1.36 +                           nsStyleContext* aContext)
    1.37 +{
    1.38 +  return new (aPresShell) nsSVGPathGeometryFrame(aContext);
    1.39 +}
    1.40 +
    1.41 +NS_IMPL_FRAMEARENA_HELPERS(nsSVGPathGeometryFrame)
    1.42 +
    1.43 +//----------------------------------------------------------------------
    1.44 +// nsQueryFrame methods
    1.45 +
    1.46 +NS_QUERYFRAME_HEAD(nsSVGPathGeometryFrame)
    1.47 +  NS_QUERYFRAME_ENTRY(nsISVGChildFrame)
    1.48 +  NS_QUERYFRAME_ENTRY(nsSVGPathGeometryFrame)
    1.49 +NS_QUERYFRAME_TAIL_INHERITING(nsSVGPathGeometryFrameBase)
    1.50 +
    1.51 +//----------------------------------------------------------------------
    1.52 +// Display list item:
    1.53 +
    1.54 +class nsDisplaySVGPathGeometry : public nsDisplayItem {
    1.55 +public:
    1.56 +  nsDisplaySVGPathGeometry(nsDisplayListBuilder* aBuilder,
    1.57 +                           nsSVGPathGeometryFrame* aFrame)
    1.58 +    : nsDisplayItem(aBuilder, aFrame)
    1.59 +  {
    1.60 +    MOZ_COUNT_CTOR(nsDisplaySVGPathGeometry);
    1.61 +    NS_ABORT_IF_FALSE(aFrame, "Must have a frame!");
    1.62 +  }
    1.63 +#ifdef NS_BUILD_REFCNT_LOGGING
    1.64 +  virtual ~nsDisplaySVGPathGeometry() {
    1.65 +    MOZ_COUNT_DTOR(nsDisplaySVGPathGeometry);
    1.66 +  }
    1.67 +#endif
    1.68 + 
    1.69 +  NS_DISPLAY_DECL_NAME("nsDisplaySVGPathGeometry", TYPE_SVG_PATH_GEOMETRY)
    1.70 +
    1.71 +  virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
    1.72 +                       HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames);
    1.73 +  virtual void Paint(nsDisplayListBuilder* aBuilder,
    1.74 +                     nsRenderingContext* aCtx);
    1.75 +};
    1.76 +
    1.77 +void
    1.78 +nsDisplaySVGPathGeometry::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
    1.79 +                                  HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
    1.80 +{
    1.81 +  nsSVGPathGeometryFrame *frame = static_cast<nsSVGPathGeometryFrame*>(mFrame);
    1.82 +  nsPoint pointRelativeToReferenceFrame = aRect.Center();
    1.83 +  // ToReferenceFrame() includes frame->GetPosition(), our user space position.
    1.84 +  nsPoint userSpacePt = pointRelativeToReferenceFrame -
    1.85 +                          (ToReferenceFrame() - frame->GetPosition());
    1.86 +  if (frame->GetFrameForPoint(userSpacePt)) {
    1.87 +    aOutFrames->AppendElement(frame);
    1.88 +  }
    1.89 +}
    1.90 +
    1.91 +void
    1.92 +nsDisplaySVGPathGeometry::Paint(nsDisplayListBuilder* aBuilder,
    1.93 +                                nsRenderingContext* aCtx)
    1.94 +{
    1.95 +  // ToReferenceFrame includes our mRect offset, but painting takes
    1.96 +  // account of that too. To avoid double counting, we subtract that
    1.97 +  // here.
    1.98 +  nsPoint offset = ToReferenceFrame() - mFrame->GetPosition();
    1.99 +
   1.100 +  aCtx->PushState();
   1.101 +  aCtx->Translate(offset);
   1.102 +  static_cast<nsSVGPathGeometryFrame*>(mFrame)->PaintSVG(aCtx, nullptr);
   1.103 +  aCtx->PopState();
   1.104 +}
   1.105 +
   1.106 +//----------------------------------------------------------------------
   1.107 +// nsIFrame methods
   1.108 +
   1.109 +void
   1.110 +nsSVGPathGeometryFrame::Init(nsIContent* aContent,
   1.111 +                             nsIFrame* aParent,
   1.112 +                             nsIFrame* aPrevInFlow)
   1.113 +{
   1.114 +  AddStateBits(aParent->GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD);
   1.115 +  nsSVGPathGeometryFrameBase::Init(aContent, aParent, aPrevInFlow);
   1.116 +}
   1.117 +
   1.118 +nsresult
   1.119 +nsSVGPathGeometryFrame::AttributeChanged(int32_t         aNameSpaceID,
   1.120 +                                         nsIAtom*        aAttribute,
   1.121 +                                         int32_t         aModType)
   1.122 +{
   1.123 +  // We don't invalidate for transform changes (the layers code does that).
   1.124 +  // Also note that SVGTransformableElement::GetAttributeChangeHint will
   1.125 +  // return nsChangeHint_UpdateOverflow for "transform" attribute changes
   1.126 +  // and cause DoApplyRenderingChangeToTree to make the SchedulePaint call.
   1.127 +
   1.128 +  if (aNameSpaceID == kNameSpaceID_None &&
   1.129 +      (static_cast<nsSVGPathGeometryElement*>
   1.130 +                  (mContent)->AttributeDefinesGeometry(aAttribute))) {
   1.131 +    nsSVGEffects::InvalidateRenderingObservers(this);
   1.132 +    nsSVGUtils::ScheduleReflowSVG(this);
   1.133 +  }
   1.134 +  return NS_OK;
   1.135 +}
   1.136 +
   1.137 +/* virtual */ void
   1.138 +nsSVGPathGeometryFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
   1.139 +{
   1.140 +  nsSVGPathGeometryFrameBase::DidSetStyleContext(aOldStyleContext);
   1.141 +
   1.142 +  if (aOldStyleContext) {
   1.143 +    float oldOpacity = aOldStyleContext->PeekStyleDisplay()->mOpacity;
   1.144 +    float newOpacity = StyleDisplay()->mOpacity;
   1.145 +    if (newOpacity != oldOpacity &&
   1.146 +        nsSVGUtils::CanOptimizeOpacity(this)) {
   1.147 +      // nsIFrame::BuildDisplayListForStackingContext() is not going to create an
   1.148 +      // nsDisplayOpacity display list item, so DLBI won't invalidate for us.
   1.149 +      InvalidateFrame();
   1.150 +    }
   1.151 +  }
   1.152 +}
   1.153 +
   1.154 +nsIAtom *
   1.155 +nsSVGPathGeometryFrame::GetType() const
   1.156 +{
   1.157 +  return nsGkAtoms::svgPathGeometryFrame;
   1.158 +}
   1.159 +
   1.160 +bool
   1.161 +nsSVGPathGeometryFrame::IsSVGTransformed(gfx::Matrix *aOwnTransform,
   1.162 +                                         gfx::Matrix *aFromParentTransform) const
   1.163 +{
   1.164 +  bool foundTransform = false;
   1.165 +
   1.166 +  // Check if our parent has children-only transforms:
   1.167 +  nsIFrame *parent = GetParent();
   1.168 +  if (parent &&
   1.169 +      parent->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer)) {
   1.170 +    foundTransform = static_cast<nsSVGContainerFrame*>(parent)->
   1.171 +                       HasChildrenOnlyTransform(aFromParentTransform);
   1.172 +  }
   1.173 +
   1.174 +  nsSVGElement *content = static_cast<nsSVGElement*>(mContent);
   1.175 +  nsSVGAnimatedTransformList* transformList =
   1.176 +    content->GetAnimatedTransformList();
   1.177 +  if ((transformList && transformList->HasTransform()) ||
   1.178 +      content->GetAnimateMotionTransform()) {
   1.179 +    if (aOwnTransform) {
   1.180 +      *aOwnTransform = gfx::ToMatrix(content->PrependLocalTransformsTo(gfxMatrix(),
   1.181 +                                  nsSVGElement::eUserSpaceToParent));
   1.182 +    }
   1.183 +    foundTransform = true;
   1.184 +  }
   1.185 +  return foundTransform;
   1.186 +}
   1.187 +
   1.188 +void
   1.189 +nsSVGPathGeometryFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
   1.190 +                                         const nsRect&           aDirtyRect,
   1.191 +                                         const nsDisplayListSet& aLists)
   1.192 +{
   1.193 +  if (!static_cast<const nsSVGElement*>(mContent)->HasValidDimensions()) {
   1.194 +    return;
   1.195 +  }
   1.196 +  aLists.Content()->AppendNewToTop(
   1.197 +    new (aBuilder) nsDisplaySVGPathGeometry(aBuilder, this));
   1.198 +}
   1.199 +
   1.200 +//----------------------------------------------------------------------
   1.201 +// nsISVGChildFrame methods
   1.202 +
   1.203 +nsresult
   1.204 +nsSVGPathGeometryFrame::PaintSVG(nsRenderingContext *aContext,
   1.205 +                                 const nsIntRect *aDirtyRect,
   1.206 +                                 nsIFrame* aTransformRoot)
   1.207 +{
   1.208 +  if (!StyleVisibility()->IsVisible())
   1.209 +    return NS_OK;
   1.210 +
   1.211 +  uint32_t paintOrder = StyleSVG()->mPaintOrder;
   1.212 +  if (paintOrder == NS_STYLE_PAINT_ORDER_NORMAL) {
   1.213 +    Render(aContext, eRenderFill | eRenderStroke, aTransformRoot);
   1.214 +    PaintMarkers(aContext);
   1.215 +  } else {
   1.216 +    while (paintOrder) {
   1.217 +      uint32_t component =
   1.218 +        paintOrder & ((1 << NS_STYLE_PAINT_ORDER_BITWIDTH) - 1);
   1.219 +      switch (component) {
   1.220 +        case NS_STYLE_PAINT_ORDER_FILL:
   1.221 +          Render(aContext, eRenderFill, aTransformRoot);
   1.222 +          break;
   1.223 +        case NS_STYLE_PAINT_ORDER_STROKE:
   1.224 +          Render(aContext, eRenderStroke, aTransformRoot);
   1.225 +          break;
   1.226 +        case NS_STYLE_PAINT_ORDER_MARKERS:
   1.227 +          PaintMarkers(aContext);
   1.228 +          break;
   1.229 +      }
   1.230 +      paintOrder >>= NS_STYLE_PAINT_ORDER_BITWIDTH;
   1.231 +    }
   1.232 +  }
   1.233 +
   1.234 +  return NS_OK;
   1.235 +}
   1.236 +
   1.237 +nsIFrame*
   1.238 +nsSVGPathGeometryFrame::GetFrameForPoint(const nsPoint &aPoint)
   1.239 +{
   1.240 +  gfxMatrix canvasTM = GetCanvasTM(FOR_HIT_TESTING);
   1.241 +  if (canvasTM.IsSingular()) {
   1.242 +    return nullptr;
   1.243 +  }
   1.244 +  uint16_t fillRule, hitTestFlags;
   1.245 +  if (GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD) {
   1.246 +    hitTestFlags = SVG_HIT_TEST_FILL;
   1.247 +    fillRule = StyleSVG()->mClipRule;
   1.248 +  } else {
   1.249 +    hitTestFlags = GetHitTestFlags();
   1.250 +    // XXX once bug 614732 is fixed, aPoint won't need any conversion in order
   1.251 +    // to compare it with mRect.
   1.252 +    nsPoint point =
   1.253 +      nsSVGUtils::TransformOuterSVGPointToChildFrame(aPoint, canvasTM, PresContext());
   1.254 +    if (!hitTestFlags || ((hitTestFlags & SVG_HIT_TEST_CHECK_MRECT) &&
   1.255 +                          !mRect.Contains(point)))
   1.256 +      return nullptr;
   1.257 +    fillRule = StyleSVG()->mFillRule;
   1.258 +  }
   1.259 +
   1.260 +  bool isHit = false;
   1.261 +
   1.262 +  nsRefPtr<gfxContext> tmpCtx =
   1.263 +    new gfxContext(gfxPlatform::GetPlatform()->ScreenReferenceSurface());
   1.264 +
   1.265 +  GeneratePath(tmpCtx, ToMatrix(canvasTM));
   1.266 +  gfxPoint userSpacePoint =
   1.267 +    tmpCtx->DeviceToUser(gfxPoint(PresContext()->AppUnitsToGfxUnits(aPoint.x),
   1.268 +                                  PresContext()->AppUnitsToGfxUnits(aPoint.y)));
   1.269 +
   1.270 +  if (fillRule == NS_STYLE_FILL_RULE_EVENODD)
   1.271 +    tmpCtx->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);
   1.272 +  else
   1.273 +    tmpCtx->SetFillRule(gfxContext::FILL_RULE_WINDING);
   1.274 +
   1.275 +  if (hitTestFlags & SVG_HIT_TEST_FILL)
   1.276 +    isHit = tmpCtx->PointInFill(userSpacePoint);
   1.277 +  if (!isHit && (hitTestFlags & SVG_HIT_TEST_STROKE)) {
   1.278 +    nsSVGUtils::SetupCairoStrokeGeometry(this, tmpCtx);
   1.279 +    // tmpCtx's matrix may have transformed by SetupCairoStrokeGeometry
   1.280 +    // if there is a non-scaling stroke. We need to transform userSpacePoint
   1.281 +    // so that everything is using the same co-ordinate system.
   1.282 +    userSpacePoint =
   1.283 +      nsSVGUtils::GetStrokeTransform(this).Invert().Transform(userSpacePoint);
   1.284 +    isHit = tmpCtx->PointInStroke(userSpacePoint);
   1.285 +  }
   1.286 +
   1.287 +  if (isHit && nsSVGUtils::HitTestClip(this, aPoint))
   1.288 +    return this;
   1.289 +
   1.290 +  return nullptr;
   1.291 +}
   1.292 +
   1.293 +nsRect
   1.294 +nsSVGPathGeometryFrame::GetCoveredRegion()
   1.295 +{
   1.296 +  return nsSVGUtils::TransformFrameRectToOuterSVG(
   1.297 +           mRect, GetCanvasTM(FOR_OUTERSVG_TM), PresContext());
   1.298 +}
   1.299 +
   1.300 +void
   1.301 +nsSVGPathGeometryFrame::ReflowSVG()
   1.302 +{
   1.303 +  NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingReflowSVG(this),
   1.304 +               "This call is probably a wasteful mistake");
   1.305 +
   1.306 +  NS_ABORT_IF_FALSE(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY),
   1.307 +                    "ReflowSVG mechanism not designed for this");
   1.308 +
   1.309 +  if (!nsSVGUtils::NeedsReflowSVG(this)) {
   1.310 +    return;
   1.311 +  }
   1.312 +
   1.313 +  uint32_t flags = nsSVGUtils::eBBoxIncludeFill |
   1.314 +                   nsSVGUtils::eBBoxIncludeStroke |
   1.315 +                   nsSVGUtils::eBBoxIncludeMarkers;
   1.316 +  // Our "visual" overflow rect needs to be valid for building display lists
   1.317 +  // for hit testing, which means that for certain values of 'pointer-events'
   1.318 +  // it needs to include the geometry of the fill or stroke even when the fill/
   1.319 +  // stroke don't actually render (e.g. when stroke="none" or
   1.320 +  // stroke-opacity="0"). GetHitTestFlags() accounts for 'pointer-events'.
   1.321 +  uint16_t hitTestFlags = GetHitTestFlags();
   1.322 +  if ((hitTestFlags & SVG_HIT_TEST_FILL)) {
   1.323 +   flags |= nsSVGUtils::eBBoxIncludeFillGeometry;
   1.324 +  }
   1.325 +  if ((hitTestFlags & SVG_HIT_TEST_STROKE)) {
   1.326 +   flags |= nsSVGUtils::eBBoxIncludeStrokeGeometry;
   1.327 +  }
   1.328 + 
   1.329 +  // We'd like to just pass the identity matrix to GetBBoxContribution, but if
   1.330 +  // this frame's user space size is _very_ large/small then the extents we
   1.331 +  // obtain below might have overflowed or otherwise be broken. This would
   1.332 +  // cause us to end up with a broken mRect and visual overflow rect and break
   1.333 +  // painting of this frame. This is particularly noticeable if the transforms
   1.334 +  // between us and our nsSVGOuterSVGFrame scale this frame to a reasonable
   1.335 +  // size. To avoid this we sadly have to do extra work to account for the
   1.336 +  // transforms between us and our nsSVGOuterSVGFrame, even though the
   1.337 +  // overwhelming number of SVGs will never have this problem.
   1.338 +  // XXX Will Azure eventually save us from having to do this?
   1.339 +  gfxSize scaleFactors = GetCanvasTM(FOR_OUTERSVG_TM).ScaleFactors(true);
   1.340 +  bool applyScaling = fabs(scaleFactors.width) >= 1e-6 &&
   1.341 +                      fabs(scaleFactors.height) >= 1e-6;
   1.342 +  gfx::Matrix scaling;
   1.343 +  if (applyScaling) {
   1.344 +    scaling.Scale(scaleFactors.width, scaleFactors.height);
   1.345 +  }
   1.346 +  gfxRect extent = GetBBoxContribution(scaling, flags).ToThebesRect();
   1.347 +  if (applyScaling) {
   1.348 +    extent.Scale(1 / scaleFactors.width, 1 / scaleFactors.height);
   1.349 +  }
   1.350 +  mRect = nsLayoutUtils::RoundGfxRectToAppRect(extent,
   1.351 +            PresContext()->AppUnitsPerCSSPixel());
   1.352 +
   1.353 +  if (mState & NS_FRAME_FIRST_REFLOW) {
   1.354 +    // Make sure we have our filter property (if any) before calling
   1.355 +    // FinishAndStoreOverflow (subsequent filter changes are handled off
   1.356 +    // nsChangeHint_UpdateEffects):
   1.357 +    nsSVGEffects::UpdateEffects(this);
   1.358 +  }
   1.359 +
   1.360 +  nsRect overflow = nsRect(nsPoint(0,0), mRect.Size());
   1.361 +  nsOverflowAreas overflowAreas(overflow, overflow);
   1.362 +  FinishAndStoreOverflow(overflowAreas, mRect.Size());
   1.363 +
   1.364 +  mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
   1.365 +              NS_FRAME_HAS_DIRTY_CHILDREN);
   1.366 +
   1.367 +  // Invalidate, but only if this is not our first reflow (since if it is our
   1.368 +  // first reflow then we haven't had our first paint yet).
   1.369 +  if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
   1.370 +    InvalidateFrame();
   1.371 +  }
   1.372 +}
   1.373 +
   1.374 +void
   1.375 +nsSVGPathGeometryFrame::NotifySVGChanged(uint32_t aFlags)
   1.376 +{
   1.377 +  NS_ABORT_IF_FALSE(aFlags & (TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED),
   1.378 +                    "Invalidation logic may need adjusting");
   1.379 +
   1.380 +  // Changes to our ancestors may affect how we render when we are rendered as
   1.381 +  // part of our ancestor (specifically, if our coordinate context changes size
   1.382 +  // and we have percentage lengths defining our geometry, then we need to be
   1.383 +  // reflowed). However, ancestor changes cannot affect how we render when we
   1.384 +  // are rendered as part of any rendering observers that we may have.
   1.385 +  // Therefore no need to notify rendering observers here.
   1.386 +
   1.387 +  // Don't try to be too smart trying to avoid the ScheduleReflowSVG calls
   1.388 +  // for the stroke properties examined below. Checking HasStroke() is not
   1.389 +  // enough, since what we care about is whether we include the stroke in our
   1.390 +  // overflow rects or not, and we sometimes deliberately include stroke
   1.391 +  // when it's not visible. See the complexities of GetBBoxContribution.
   1.392 +
   1.393 +  if (aFlags & COORD_CONTEXT_CHANGED) {
   1.394 +    // Stroke currently contributes to our mRect, which is why we have to take
   1.395 +    // account of stroke-width here. Note that we do not need to take account
   1.396 +    // of stroke-dashoffset since, although that can have a percentage value
   1.397 +    // that is resolved against our coordinate context, it does not affect our
   1.398 +    // mRect.
   1.399 +    if (static_cast<nsSVGPathGeometryElement*>(mContent)->GeometryDependsOnCoordCtx() ||
   1.400 +        StyleSVG()->mStrokeWidth.HasPercent()) {
   1.401 +      nsSVGUtils::ScheduleReflowSVG(this);
   1.402 +    }
   1.403 +  }
   1.404 +
   1.405 +  if ((aFlags & TRANSFORM_CHANGED) &&
   1.406 +      StyleSVGReset()->mVectorEffect ==
   1.407 +        NS_STYLE_VECTOR_EFFECT_NON_SCALING_STROKE) {
   1.408 +    // Stroke currently contributes to our mRect, and our stroke depends on
   1.409 +    // the transform to our outer-<svg> if |vector-effect:non-scaling-stroke|.
   1.410 +    nsSVGUtils::ScheduleReflowSVG(this);
   1.411 +  } 
   1.412 +}
   1.413 +
   1.414 +SVGBBox
   1.415 +nsSVGPathGeometryFrame::GetBBoxContribution(const Matrix &aToBBoxUserspace,
   1.416 +                                            uint32_t aFlags)
   1.417 +{
   1.418 +  SVGBBox bbox;
   1.419 +
   1.420 +  if (aToBBoxUserspace.IsSingular()) {
   1.421 +    // XXX ReportToConsole
   1.422 +    return bbox;
   1.423 +  }
   1.424 +
   1.425 +  nsRefPtr<gfxContext> tmpCtx =
   1.426 +    new gfxContext(gfxPlatform::GetPlatform()->ScreenReferenceSurface());
   1.427 +
   1.428 +  GeneratePath(tmpCtx, aToBBoxUserspace);
   1.429 +  tmpCtx->IdentityMatrix();
   1.430 +
   1.431 +  // Be careful when replacing the following logic to get the fill and stroke
   1.432 +  // extents independently (instead of computing the stroke extents from the
   1.433 +  // path extents). You may think that you can just use the stroke extents if
   1.434 +  // there is both a fill and a stroke. In reality it's necessary to calculate
   1.435 +  // both the fill and stroke extents, and take the union of the two. There are
   1.436 +  // two reasons for this:
   1.437 +  //
   1.438 +  // # Due to stroke dashing, in certain cases the fill extents could actually
   1.439 +  //   extend outside the stroke extents.
   1.440 +  // # If the stroke is very thin, cairo won't paint any stroke, and so the
   1.441 +  //   stroke bounds that it will return will be empty.
   1.442 +
   1.443 +  gfxRect pathExtents = tmpCtx->GetUserPathExtent();
   1.444 +
   1.445 +  // Account for fill:
   1.446 +  if ((aFlags & nsSVGUtils::eBBoxIncludeFillGeometry) ||
   1.447 +      ((aFlags & nsSVGUtils::eBBoxIncludeFill) &&
   1.448 +       StyleSVG()->mFill.mType != eStyleSVGPaintType_None)) {
   1.449 +    bbox = pathExtents;
   1.450 +  }
   1.451 +
   1.452 +  // Account for stroke:
   1.453 +  if ((aFlags & nsSVGUtils::eBBoxIncludeStrokeGeometry) ||
   1.454 +      ((aFlags & nsSVGUtils::eBBoxIncludeStroke) &&
   1.455 +       nsSVGUtils::HasStroke(this))) {
   1.456 +    // We can't use tmpCtx->GetUserStrokeExtent() since it doesn't work for
   1.457 +    // device space extents. Instead we approximate the stroke extents from
   1.458 +    // pathExtents using PathExtentsToMaxStrokeExtents.
   1.459 +    if (pathExtents.Width() <= 0 && pathExtents.Height() <= 0) {
   1.460 +      // We have a zero length path, but it may still have non-empty stroke
   1.461 +      // bounds depending on the value of stroke-linecap. We need to fix up
   1.462 +      // pathExtents before it can be used with PathExtentsToMaxStrokeExtents
   1.463 +      // though, because if pathExtents is empty, its position will not have
   1.464 +      // been set. Happily we can use tmpCtx->GetUserStrokeExtent() to find
   1.465 +      // the center point of the extents even though it gets the extents wrong.
   1.466 +      nsSVGUtils::SetupCairoStrokeBBoxGeometry(this, tmpCtx);
   1.467 +      pathExtents.MoveTo(tmpCtx->GetUserStrokeExtent().Center());
   1.468 +      pathExtents.SizeTo(0, 0);
   1.469 +    }
   1.470 +    bbox.UnionEdges(nsSVGUtils::PathExtentsToMaxStrokeExtents(pathExtents,
   1.471 +                                                              this,
   1.472 +                                                              ThebesMatrix(aToBBoxUserspace)));
   1.473 +  }
   1.474 +
   1.475 +  // Account for markers:
   1.476 +  if ((aFlags & nsSVGUtils::eBBoxIncludeMarkers) != 0 &&
   1.477 +      static_cast<nsSVGPathGeometryElement*>(mContent)->IsMarkable()) {
   1.478 +
   1.479 +    float strokeWidth = nsSVGUtils::GetStrokeWidth(this);
   1.480 +    MarkerProperties properties = GetMarkerProperties(this);
   1.481 +
   1.482 +    if (properties.MarkersExist()) {
   1.483 +      nsTArray<nsSVGMark> marks;
   1.484 +      static_cast<nsSVGPathGeometryElement*>(mContent)->GetMarkPoints(&marks);
   1.485 +      uint32_t num = marks.Length();
   1.486 +
   1.487 +      // These are in the same order as the nsSVGMark::Type constants.
   1.488 +      nsSVGMarkerFrame* markerFrames[] = {
   1.489 +        properties.GetMarkerStartFrame(),
   1.490 +        properties.GetMarkerMidFrame(),
   1.491 +        properties.GetMarkerEndFrame(),
   1.492 +      };
   1.493 +      PR_STATIC_ASSERT(MOZ_ARRAY_LENGTH(markerFrames) == nsSVGMark::eTypeCount);
   1.494 +
   1.495 +      for (uint32_t i = 0; i < num; i++) {
   1.496 +        nsSVGMark& mark = marks[i];
   1.497 +        nsSVGMarkerFrame* frame = markerFrames[mark.type];
   1.498 +        if (frame) {
   1.499 +          SVGBBox mbbox =
   1.500 +            frame->GetMarkBBoxContribution(aToBBoxUserspace, aFlags, this,
   1.501 +                                           &marks[i], strokeWidth);
   1.502 +          bbox.UnionEdges(mbbox);
   1.503 +        }
   1.504 +      }
   1.505 +    }
   1.506 +  }
   1.507 +
   1.508 +  return bbox;
   1.509 +}
   1.510 +
   1.511 +//----------------------------------------------------------------------
   1.512 +// nsSVGPathGeometryFrame methods:
   1.513 +
   1.514 +gfxMatrix
   1.515 +nsSVGPathGeometryFrame::GetCanvasTM(uint32_t aFor, nsIFrame* aTransformRoot)
   1.516 +{
   1.517 +  if (!(GetStateBits() & NS_FRAME_IS_NONDISPLAY) && !aTransformRoot) {
   1.518 +    if ((aFor == FOR_PAINTING && NS_SVGDisplayListPaintingEnabled()) ||
   1.519 +        (aFor == FOR_HIT_TESTING && NS_SVGDisplayListHitTestingEnabled())) {
   1.520 +      return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(this);
   1.521 +    }
   1.522 +  }
   1.523 +
   1.524 +  NS_ASSERTION(mParent, "null parent");
   1.525 +
   1.526 +  nsSVGContainerFrame *parent = static_cast<nsSVGContainerFrame*>(mParent);
   1.527 +  dom::SVGGraphicsElement *content = static_cast<dom::SVGGraphicsElement*>(mContent);
   1.528 +
   1.529 +  return content->PrependLocalTransformsTo(
   1.530 +      this == aTransformRoot ? gfxMatrix() :
   1.531 +                               parent->GetCanvasTM(aFor, aTransformRoot));
   1.532 +}
   1.533 +
   1.534 +nsSVGPathGeometryFrame::MarkerProperties
   1.535 +nsSVGPathGeometryFrame::GetMarkerProperties(nsSVGPathGeometryFrame *aFrame)
   1.536 +{
   1.537 +  NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame should be first continuation");
   1.538 +
   1.539 +  MarkerProperties result;
   1.540 +  const nsStyleSVG *style = aFrame->StyleSVG();
   1.541 +  result.mMarkerStart =
   1.542 +    nsSVGEffects::GetMarkerProperty(style->mMarkerStart, aFrame,
   1.543 +                                    nsSVGEffects::MarkerBeginProperty());
   1.544 +  result.mMarkerMid =
   1.545 +    nsSVGEffects::GetMarkerProperty(style->mMarkerMid, aFrame,
   1.546 +                                    nsSVGEffects::MarkerMiddleProperty());
   1.547 +  result.mMarkerEnd =
   1.548 +    nsSVGEffects::GetMarkerProperty(style->mMarkerEnd, aFrame,
   1.549 +                                    nsSVGEffects::MarkerEndProperty());
   1.550 +  return result;
   1.551 +}
   1.552 +
   1.553 +nsSVGMarkerFrame *
   1.554 +nsSVGPathGeometryFrame::MarkerProperties::GetMarkerStartFrame()
   1.555 +{
   1.556 +  if (!mMarkerStart)
   1.557 +    return nullptr;
   1.558 +  return static_cast<nsSVGMarkerFrame *>
   1.559 +    (mMarkerStart->GetReferencedFrame(nsGkAtoms::svgMarkerFrame, nullptr));
   1.560 +}
   1.561 +
   1.562 +nsSVGMarkerFrame *
   1.563 +nsSVGPathGeometryFrame::MarkerProperties::GetMarkerMidFrame()
   1.564 +{
   1.565 +  if (!mMarkerMid)
   1.566 +    return nullptr;
   1.567 +  return static_cast<nsSVGMarkerFrame *>
   1.568 +    (mMarkerMid->GetReferencedFrame(nsGkAtoms::svgMarkerFrame, nullptr));
   1.569 +}
   1.570 +
   1.571 +nsSVGMarkerFrame *
   1.572 +nsSVGPathGeometryFrame::MarkerProperties::GetMarkerEndFrame()
   1.573 +{
   1.574 +  if (!mMarkerEnd)
   1.575 +    return nullptr;
   1.576 +  return static_cast<nsSVGMarkerFrame *>
   1.577 +    (mMarkerEnd->GetReferencedFrame(nsGkAtoms::svgMarkerFrame, nullptr));
   1.578 +}
   1.579 +
   1.580 +void
   1.581 +nsSVGPathGeometryFrame::Render(nsRenderingContext *aContext,
   1.582 +                               uint32_t aRenderComponents,
   1.583 +                               nsIFrame* aTransformRoot)
   1.584 +{
   1.585 +  gfxContext *gfx = aContext->ThebesContext();
   1.586 +
   1.587 +  uint16_t renderMode = SVGAutoRenderState::GetRenderMode(aContext);
   1.588 +
   1.589 +  switch (StyleSVG()->mShapeRendering) {
   1.590 +  case NS_STYLE_SHAPE_RENDERING_OPTIMIZESPEED:
   1.591 +  case NS_STYLE_SHAPE_RENDERING_CRISPEDGES:
   1.592 +    gfx->SetAntialiasMode(gfxContext::MODE_ALIASED);
   1.593 +    break;
   1.594 +  default:
   1.595 +    gfx->SetAntialiasMode(gfxContext::MODE_COVERAGE);
   1.596 +    break;
   1.597 +  }
   1.598 +
   1.599 +  if (renderMode != SVGAutoRenderState::NORMAL) {
   1.600 +    NS_ABORT_IF_FALSE(renderMode == SVGAutoRenderState::CLIP ||
   1.601 +                      renderMode == SVGAutoRenderState::CLIP_MASK,
   1.602 +                      "Unknown render mode");
   1.603 +
   1.604 +    // In the case that |renderMode == SVGAutoRenderState::CLIP| then we don't
   1.605 +    // use the path we generate here until further up the call stack when
   1.606 +    // nsSVGClipPathFrame::Clip calls gfxContext::Clip. That's a problem for
   1.607 +    // Moz2D which emits paths in user space (unlike cairo which emits paths in
   1.608 +    // device space). gfxContext has hacks to deal with code changing the
   1.609 +    // transform then using the current path when it is backed by Moz2D, but
   1.610 +    // Moz2D itself does not since that would fundamentally go against its API.
   1.611 +    // Therefore we do not want to Save()/Restore() the gfxContext here in the
   1.612 +    // SVGAutoRenderState::CLIP case since that would block us from killing off
   1.613 +    // gfxContext and using Moz2D directly. Not bothering to Save()/Restore()
   1.614 +    // is actually okay, since we know that doesn't matter in the
   1.615 +    // SVGAutoRenderState::CLIP case (at least for the current implementation).
   1.616 +    gfxContextMatrixAutoSaveRestore autoSaveRestore;
   1.617 +    // For now revent back to doing the save even for CLIP to fix bug 959128.
   1.618 +    // Undo in bug 987193.
   1.619 +    //if (renderMode != SVGAutoRenderState::CLIP) {
   1.620 +      autoSaveRestore.SetContext(gfx);
   1.621 +    //}
   1.622 +
   1.623 +    GeneratePath(gfx, ToMatrix(GetCanvasTM(FOR_PAINTING, aTransformRoot)));
   1.624 +
   1.625 +    // We used to call gfx->Restore() here, since for the
   1.626 +    // SVGAutoRenderState::CLIP case it is important to leave the fill rule
   1.627 +    // that we set below untouched so that the value is still set when return
   1.628 +    // to gfxContext::Clip() further up the call stack. Since we no longer
   1.629 +    // call gfx->Save() in the SVGAutoRenderState::CLIP case we don't need to
   1.630 +    // worry that autoSaveRestore will delay the Restore() call for the
   1.631 +    // CLIP_MASK case until we exit this function.
   1.632 +
   1.633 +    gfxContext::FillRule oldFillRull = gfx->CurrentFillRule();
   1.634 +
   1.635 +    if (StyleSVG()->mClipRule == NS_STYLE_FILL_RULE_EVENODD)
   1.636 +      gfx->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);
   1.637 +    else
   1.638 +      gfx->SetFillRule(gfxContext::FILL_RULE_WINDING);
   1.639 +
   1.640 +    if (renderMode == SVGAutoRenderState::CLIP_MASK) {
   1.641 +      gfx->SetColor(gfxRGBA(1.0f, 1.0f, 1.0f, 1.0f));
   1.642 +      gfx->Fill();
   1.643 +      gfx->SetFillRule(oldFillRull); // restore, but only for CLIP_MASK
   1.644 +      gfx->NewPath();
   1.645 +    }
   1.646 +
   1.647 +    return;
   1.648 +  }
   1.649 +
   1.650 +  gfxContextAutoSaveRestore autoSaveRestore(gfx);
   1.651 +
   1.652 +  GeneratePath(gfx, ToMatrix(GetCanvasTM(FOR_PAINTING, aTransformRoot)));
   1.653 +
   1.654 +  gfxTextContextPaint *contextPaint =
   1.655 +    (gfxTextContextPaint*)aContext->GetUserData(&gfxTextContextPaint::sUserDataKey);
   1.656 +
   1.657 +  if ((aRenderComponents & eRenderFill) &&
   1.658 +      nsSVGUtils::SetupCairoFillPaint(this, gfx, contextPaint)) {
   1.659 +    gfx->Fill();
   1.660 +  }
   1.661 +
   1.662 +  if ((aRenderComponents & eRenderStroke) &&
   1.663 +       nsSVGUtils::SetupCairoStroke(this, gfx, contextPaint)) {
   1.664 +    gfx->Stroke();
   1.665 +  }
   1.666 +
   1.667 +  gfx->NewPath();
   1.668 +}
   1.669 +
   1.670 +void
   1.671 +nsSVGPathGeometryFrame::GeneratePath(gfxContext* aContext,
   1.672 +                                     const Matrix &aTransform)
   1.673 +{
   1.674 +  if (aTransform.IsSingular()) {
   1.675 +    aContext->IdentityMatrix();
   1.676 +    aContext->NewPath();
   1.677 +    return;
   1.678 +  }
   1.679 +
   1.680 +  aContext->MultiplyAndNudgeToIntegers(ThebesMatrix(aTransform));
   1.681 +
   1.682 +  // Hack to let SVGPathData::ConstructPath know if we have square caps:
   1.683 +  const nsStyleSVG* style = StyleSVG();
   1.684 +  if (style->mStrokeLinecap == NS_STYLE_STROKE_LINECAP_SQUARE) {
   1.685 +    aContext->SetLineCap(gfxContext::LINE_CAP_SQUARE);
   1.686 +  }
   1.687 +
   1.688 +  aContext->NewPath();
   1.689 +  static_cast<nsSVGPathGeometryElement*>(mContent)->ConstructPath(aContext);
   1.690 +}
   1.691 +
   1.692 +void
   1.693 +nsSVGPathGeometryFrame::PaintMarkers(nsRenderingContext* aContext)
   1.694 +{
   1.695 +  gfxTextContextPaint *contextPaint =
   1.696 +    (gfxTextContextPaint*)aContext->GetUserData(&gfxTextContextPaint::sUserDataKey);
   1.697 +
   1.698 +  if (static_cast<nsSVGPathGeometryElement*>(mContent)->IsMarkable()) {
   1.699 +    MarkerProperties properties = GetMarkerProperties(this);
   1.700 +
   1.701 +    if (properties.MarkersExist()) {
   1.702 +      float strokeWidth = nsSVGUtils::GetStrokeWidth(this, contextPaint);
   1.703 +
   1.704 +      nsTArray<nsSVGMark> marks;
   1.705 +      static_cast<nsSVGPathGeometryElement*>
   1.706 +                 (mContent)->GetMarkPoints(&marks);
   1.707 +
   1.708 +      uint32_t num = marks.Length();
   1.709 +      if (num) {
   1.710 +        // These are in the same order as the nsSVGMark::Type constants.
   1.711 +        nsSVGMarkerFrame* markerFrames[] = {
   1.712 +          properties.GetMarkerStartFrame(),
   1.713 +          properties.GetMarkerMidFrame(),
   1.714 +          properties.GetMarkerEndFrame(),
   1.715 +        };
   1.716 +        PR_STATIC_ASSERT(MOZ_ARRAY_LENGTH(markerFrames) == nsSVGMark::eTypeCount);
   1.717 +
   1.718 +        for (uint32_t i = 0; i < num; i++) {
   1.719 +          nsSVGMark& mark = marks[i];
   1.720 +          nsSVGMarkerFrame* frame = markerFrames[mark.type];
   1.721 +          if (frame) {
   1.722 +            frame->PaintMark(aContext, this, &mark, strokeWidth);
   1.723 +          }
   1.724 +        }
   1.725 +      }
   1.726 +    }
   1.727 +  }
   1.728 +}
   1.729 +
   1.730 +uint16_t
   1.731 +nsSVGPathGeometryFrame::GetHitTestFlags()
   1.732 +{
   1.733 +  return nsSVGUtils::GetGeometryHitTestFlags(this);
   1.734 +}

mercurial