nsSVGPathGeometryFrame::GetCoveredRegion() { // XXX why are we adding in markers here each time someone gets the covered // region? Isn't UpdateCoveredRegion called whenever markers change? // And why are the answers to these questions not documented here??!! if (static_cast<nsSVGPathGeometryElement*>(mContent)->IsMarkable()) { MarkerProperties properties = GetMarkerProperties(this); if (!properties.MarkersExist()) return mRect; nsRect rect(mRect); float strokeWidth = GetStrokeWidth(); nsTArray<nsSVGMark> marks; static_cast<nsSVGPathGeometryElement*>(mContent)->GetMarkPoints(&marks); PRUint32 num = marks.Length(); if (num) { nsSVGMarkerFrame *frame = properties.GetMarkerStartFrame(); if (frame) { nsRect mark = frame->RegionMark(this, &marks[0], strokeWidth); rect.UnionRect(rect, mark); } frame = properties.GetMarkerMidFrame(); if (frame) { for (PRUint32 i = 1; i < num - 1; i++) { nsRect mark = frame->RegionMark(this, &marks[i], strokeWidth); rect.UnionRect(rect, mark); } } frame = properties.GetMarkerEndFrame(); if (frame) { nsRect mark = frame->RegionMark(this, &marks[num-1], strokeWidth); rect.UnionRect(rect, mark); } } return rect; } return mRect; }
NS_IMETHODIMP nsSVGPathGeometryFrame::PaintSVG(nsSVGRenderState *aContext, const nsIntRect *aDirtyRect) { if (!GetStyleVisibility()->IsVisible()) return NS_OK; /* render */ Render(aContext); if (static_cast<nsSVGPathGeometryElement*>(mContent)->IsMarkable()) { MarkerProperties properties = GetMarkerProperties(this); if (properties.MarkersExist()) { float strokeWidth = GetStrokeWidth(); nsTArray<nsSVGMark> marks; static_cast<nsSVGPathGeometryElement*> (mContent)->GetMarkPoints(&marks); PRUint32 num = marks.Length(); if (num) { nsSVGMarkerFrame *frame = properties.GetMarkerStartFrame(); if (frame) frame->PaintMark(aContext, this, &marks[0], strokeWidth); frame = properties.GetMarkerMidFrame(); if (frame) { for (PRUint32 i = 1; i < num - 1; i++) frame->PaintMark(aContext, this, &marks[i], strokeWidth); } frame = properties.GetMarkerEndFrame(); if (frame) frame->PaintMark(aContext, this, &marks[num-1], strokeWidth); } } } return NS_OK; }
gfxRect nsSVGPathGeometryFrame::GetBBoxContribution(const gfxMatrix &aToBBoxUserspace, PRUint32 aFlags) { if (aToBBoxUserspace.IsSingular()) { // XXX ReportToConsole return gfxRect(0.0, 0.0, 0.0, 0.0); } nsRefPtr<gfxContext> context = new gfxContext(gfxPlatform::GetPlatform()->ScreenReferenceSurface()); GeneratePath(context, &aToBBoxUserspace); context->IdentityMatrix(); gfxRect bbox; // Be careful when replacing the following logic to get the fill and stroke // extents independently (instead of computing the stroke extents from the // path extents). You may think that you can just use the stroke extents if // there is both a fill and a stroke. In reality it's necessary to calculate // both the fill and stroke extents, and take the union of the two. There are // two reasons for this: // // # Due to stroke dashing, in certain cases the fill extents could actually // extend outside the stroke extents. // # If the stroke is very thin, cairo won't paint any stroke, and so the // stroke bounds that it will return will be empty. gfxRect pathExtents = context->GetUserPathExtent(); // Account for fill: if ((aFlags & nsSVGUtils::eBBoxIncludeFill) != 0 && ((aFlags & nsSVGUtils::eBBoxIgnoreFillIfNone) == 0 || GetStyleSVG()->mFill.mType != eStyleSVGPaintType_None)) { bbox = pathExtents; } // Account for stroke: if ((aFlags & nsSVGUtils::eBBoxIncludeStroke) != 0 && ((aFlags & nsSVGUtils::eBBoxIgnoreStrokeIfNone) == 0 || HasStroke())) { // We can't use context->GetUserStrokeExtent() since it doesn't work for // device space extents. Instead we approximate the stroke extents from // pathExtents using PathExtentsToMaxStrokeExtents. if (pathExtents.Width() <= 0 && pathExtents.Height() <= 0) { // We have a zero length path, but it may still have non-empty stroke // bounds depending on the value of stroke-linecap. We need to fix up // pathExtents before it can be used with PathExtentsToMaxStrokeExtents // though, because if pathExtents is empty, its position will not have // been set. Happily we can use context->GetUserStrokeExtent() to find // the center point of the extents even though it gets the extents wrong. SetupCairoStrokeGeometry(context); pathExtents.MoveTo(context->GetUserStrokeExtent().Center()); pathExtents.SizeTo(0, 0); } bbox = bbox.Union(nsSVGUtils::PathExtentsToMaxStrokeExtents(pathExtents, this, aToBBoxUserspace)); } // Account for markers: if ((aFlags & nsSVGUtils::eBBoxIncludeMarkers) != 0 && static_cast<nsSVGPathGeometryElement*>(mContent)->IsMarkable()) { float strokeWidth = GetStrokeWidth(); MarkerProperties properties = GetMarkerProperties(this); if (properties.MarkersExist()) { nsTArray<nsSVGMark> marks; static_cast<nsSVGPathGeometryElement*>(mContent)->GetMarkPoints(&marks); PRUint32 num = marks.Length(); if (num) { nsSVGMarkerFrame *frame = properties.GetMarkerStartFrame(); if (frame) { gfxRect mbbox = frame->GetMarkBBoxContribution(aToBBoxUserspace, aFlags, this, &marks[0], strokeWidth); bbox.UnionRect(bbox, mbbox); } frame = properties.GetMarkerMidFrame(); if (frame) { for (PRUint32 i = 1; i < num - 1; i++) { gfxRect mbbox = frame->GetMarkBBoxContribution(aToBBoxUserspace, aFlags, this, &marks[i], strokeWidth); bbox.UnionRect(bbox, mbbox); } } frame = properties.GetMarkerEndFrame(); if (frame) { gfxRect mbbox = frame->GetMarkBBoxContribution(aToBBoxUserspace, aFlags, this, &marks[num-1], strokeWidth); bbox.UnionRect(bbox, mbbox); } } } } return bbox; }