void nsSVGPathGeometryFrame::GeneratePath(gfxContext* aContext, const gfxMatrix *aOverrideTransform) { gfxMatrix matrix; if (aOverrideTransform) { matrix = *aOverrideTransform; } else { matrix = GetCanvasTM(); } if (matrix.IsSingular()) { aContext->IdentityMatrix(); aContext->NewPath(); return; } aContext->Multiply(matrix); // Hack to let SVGPathData::ConstructPath know if we have square caps: const nsStyleSVG* style = GetStyleSVG(); if (style->mStrokeLinecap == NS_STYLE_STROKE_LINECAP_SQUARE) { aContext->SetLineCap(gfxContext::LINE_CAP_SQUARE); } aContext->NewPath(); static_cast<nsSVGPathGeometryElement*>(mContent)->ConstructPath(aContext); }
gfxMatrix nsSVGForeignObjectFrame::GetCanvasTMForChildren() { float cssPxPerDevPx = PresContext()-> AppUnitsToFloatCSSPixels(PresContext()->AppUnitsPerDevPixel()); return GetCanvasTM().Scale(cssPxPerDevPx, cssPxPerDevPx); }
void nsSVGForeignObjectFrame::UpdateBounds() { NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingUpdateBounds(this), "This call is probaby a wasteful mistake"); NS_ABORT_IF_FALSE(!(GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD), "UpdateBounds mechanism not designed for this"); if (!nsSVGUtils::NeedsUpdatedBounds(this)) { return; } // We update mRect before the DoReflow call so that DoReflow uses the // correct dimensions: float x, y, w, h; static_cast<nsSVGForeignObjectElement*>(mContent)-> GetAnimatedLengthValues(&x, &y, &w, &h, nsnull); // If mRect's width or height are negative, reflow blows up! We must clamp! if (w < 0.0f) w = 0.0f; if (h < 0.0f) h = 0.0f; // GetCanvasTM includes the x,y translation mRect = nsLayoutUtils::RoundGfxRectToAppRect( gfxRect(0.0, 0.0, w, h), PresContext()->AppUnitsPerCSSPixel()); mCoveredRegion = ToCanvasBounds(gfxRect(0.0, 0.0, w, h), GetCanvasTM(), PresContext()); // Since we'll invalidate our entire area at the end of this method, we // empty our cached dirty regions to prevent FlushDirtyRegion under DoReflow // from wasting time invalidating: mSameDocDirtyRegion.SetEmpty(); mSubDocDirtyRegion.SetEmpty(); // Fully mark our kid dirty so that it gets resized if necessary // (NS_FRAME_HAS_DIRTY_CHILDREN isn't enough in that case): nsIFrame* kid = GetFirstPrincipalChild(); kid->AddStateBits(NS_FRAME_IS_DIRTY); // Make sure to not allow interrupts if we're not being reflown as a root: nsPresContext::InterruptPreventer noInterrupts(PresContext()); DoReflow(); // Now unset the various reflow bits: mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN); if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW)) { // We only invalidate if our outer-<svg> has already had its // initial reflow (since if it hasn't, its entire area will be // invalidated when it gets that initial reflow): nsSVGUtils::InvalidateBounds(this, true); } }
nsresult nsSVGMarkerFrame::PaintMark(nsRenderingContext *aContext, nsSVGPathGeometryFrame *aMarkedFrame, nsSVGMark *aMark, float aStrokeWidth) { // If the flag is set when we get here, it means this marker frame // has already been used painting the current mark, and the document // has a marker reference loop. if (mInUse) return NS_OK; AutoMarkerReferencer markerRef(this, aMarkedFrame); nsSVGMarkerElement *marker = static_cast<nsSVGMarkerElement*>(mContent); const nsSVGViewBoxRect viewBox = marker->GetViewBoxRect(); if (viewBox.width <= 0.0f || viewBox.height <= 0.0f) { // We must disable rendering if the viewBox width or height are zero. return NS_OK; } mStrokeWidth = aStrokeWidth; mX = aMark->x; mY = aMark->y; mAutoAngle = aMark->angle; gfxContext *gfx = aContext->ThebesContext(); if (GetStyleDisplay()->IsScrollableOverflow()) { gfx->Save(); gfxRect clipRect = nsSVGUtils::GetClipRectForFrame(this, viewBox.x, viewBox.y, viewBox.width, viewBox.height); nsSVGUtils::SetClipRect(gfx, GetCanvasTM(nsISVGChildFrame::FOR_PAINTING), clipRect); } for (nsIFrame* kid = mFrames.FirstChild(); kid; kid = kid->GetNextSibling()) { nsISVGChildFrame* SVGFrame = do_QueryFrame(kid); if (SVGFrame) { // The CTM of each frame referencing us may be different. SVGFrame->NotifySVGChanged( nsISVGChildFrame::DO_NOT_NOTIFY_RENDERING_OBSERVERS | nsISVGChildFrame::TRANSFORM_CHANGED); nsSVGUtils::PaintFrameWithEffects(aContext, nsnull, kid); } } if (GetStyleDisplay()->IsScrollableOverflow()) gfx->Restore(); return NS_OK; }
nsSVGPathGeometryFrame::GetFrameForPoint(const nsPoint &aPoint) { PRUint16 fillRule, hitTestFlags; if (GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD) { hitTestFlags = SVG_HIT_TEST_FILL; fillRule = GetClipRule(); } else { hitTestFlags = GetHitTestFlags(); // XXX once bug 614732 is fixed, aPoint won't need any conversion in order // to compare it with mRect. gfxMatrix canvasTM = GetCanvasTM(); if (canvasTM.IsSingular()) { return nsnull; } nsPoint point = nsSVGUtils::TransformOuterSVGPointToChildFrame(aPoint, canvasTM, PresContext()); if (!hitTestFlags || ((hitTestFlags & SVG_HIT_TEST_CHECK_MRECT) && !mRect.Contains(point))) return nsnull; fillRule = GetStyleSVG()->mFillRule; } bool isHit = false; nsRefPtr<gfxContext> context = new gfxContext(gfxPlatform::GetPlatform()->ScreenReferenceSurface()); GeneratePath(context); gfxPoint userSpacePoint = context->DeviceToUser(gfxPoint(PresContext()->AppUnitsToGfxUnits(aPoint.x), PresContext()->AppUnitsToGfxUnits(aPoint.y))); if (fillRule == NS_STYLE_FILL_RULE_EVENODD) context->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD); else context->SetFillRule(gfxContext::FILL_RULE_WINDING); if (hitTestFlags & SVG_HIT_TEST_FILL) isHit = context->PointInFill(userSpacePoint); if (!isHit && (hitTestFlags & SVG_HIT_TEST_STROKE)) { SetupCairoStrokeHitGeometry(context); isHit = context->PointInStroke(userSpacePoint); } if (isHit && nsSVGUtils::HitTestClip(this, aPoint)) return this; return nsnull; }
NS_IMETHODIMP nsSVGPathGeometryFrame::UpdateCoveredRegion() { gfxRect extent = GetBBoxContribution(gfxMatrix(), nsSVGUtils::eBBoxIncludeFill | nsSVGUtils::eBBoxIgnoreFillIfNone | nsSVGUtils::eBBoxIncludeStroke | nsSVGUtils::eBBoxIgnoreStrokeIfNone | nsSVGUtils::eBBoxIncludeMarkers); mRect = nsLayoutUtils::RoundGfxRectToAppRect(extent, PresContext()->AppUnitsPerCSSPixel()); // See bug 614732 comment 32. mCoveredRegion = nsSVGUtils::TransformFrameRectToOuterSVG( mRect, GetCanvasTM(), PresContext()); return NS_OK; }
NS_IMETHODIMP nsSVGForeignObjectFrame::UpdateCoveredRegion() { if (GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD) return NS_ERROR_FAILURE; float x, y, w, h; static_cast<nsSVGForeignObjectElement*>(mContent)-> GetAnimatedLengthValues(&x, &y, &w, &h, nsnull); // If mRect's width or height are negative, reflow blows up! We must clamp! if (w < 0.0f) w = 0.0f; if (h < 0.0f) h = 0.0f; // GetCanvasTM includes the x,y translation mRect = ToCanvasBounds(gfxRect(0.0, 0.0, w, h), GetCanvasTM(), PresContext()); return NS_OK; }
void nsSVGPathGeometryFrame::GeneratePath(gfxContext* aContext, const gfxMatrix *aOverrideTransform) { gfxMatrix matrix; if (aOverrideTransform) { matrix = *aOverrideTransform; } else { matrix = GetCanvasTM(); } if (matrix.IsSingular()) { aContext->IdentityMatrix(); aContext->NewPath(); return; } aContext->Multiply(matrix); aContext->NewPath(); static_cast<nsSVGPathGeometryElement*>(mContent)->ConstructPath(aContext); }
void nsSVGForeignObjectFrame::InvalidateDirtyRect(nsSVGOuterSVGFrame* aOuter, const nsRect& aRect, PRUint32 aFlags) { if (aRect.IsEmpty()) return; // Don't invalidate areas outside our bounds: nsRect rect = aRect.Intersect(mRect); if (rect.IsEmpty()) return; // The areas dirtied by children are in app units, relative to this frame. // We need to convert the rect from app units in our userspace to app units // relative to our nsSVGOuterSVGFrame's content rect. gfxRect r(aRect.x, aRect.y, aRect.width, aRect.height); r.Scale(1.0 / nsPresContext::AppUnitsPerCSSPixel()); rect = ToCanvasBounds(r, GetCanvasTM(), PresContext()); rect = nsSVGUtils::FindFilterInvalidation(this, rect); aOuter->InvalidateWithFlags(rect, aFlags); }
nsSVGForeignObjectFrame::GetFrameForPoint(const nsPoint &aPoint) { if (IsDisabled() || (GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)) return nsnull; nsIFrame* kid = GetFirstPrincipalChild(); if (!kid) return nsnull; float x, y, width, height; static_cast<nsSVGElement*>(mContent)-> GetAnimatedLengthValues(&x, &y, &width, &height, nsnull); gfxMatrix tm = GetCanvasTM().Invert(); if (tm.IsSingular()) return nsnull; // Convert aPoint from app units in canvas space to user space: gfxPoint pt = gfxPoint(aPoint.x, aPoint.y) / PresContext()->AppUnitsPerDevPixel(); pt = tm.Transform(pt); if (!gfxRect(0.0f, 0.0f, width, height).Contains(pt)) return nsnull; // Convert pt to app units in *local* space: pt = pt * nsPresContext::AppUnitsPerCSSPixel(); nsPoint point = nsPoint(NSToIntRound(pt.x), NSToIntRound(pt.y)); nsIFrame *frame = nsLayoutUtils::GetFrameForPoint(kid, point); if (frame && nsSVGUtils::HitTestClip(this, aPoint)) return frame; return nsnull; }
NS_IMETHODIMP nsSVGForeignObjectFrame::PaintSVG(nsRenderingContext *aContext, const nsIntRect *aDirtyRect) { if (IsDisabled()) return NS_OK; nsIFrame* kid = GetFirstPrincipalChild(); if (!kid) return NS_OK; gfxMatrix matrixForChildren = GetCanvasTMForChildren(); gfxMatrix matrix = GetCanvasTM(); if (matrixForChildren.IsSingular()) { NS_WARNING("Can't render foreignObject element!"); return NS_ERROR_FAILURE; } nsRect kidDirtyRect = kid->GetVisualOverflowRect(); /* Check if we need to draw anything. */ if (aDirtyRect) { // Transform the dirty rect into app units in our userspace. gfxMatrix invmatrix = matrix; invmatrix.Invert(); NS_ASSERTION(!invmatrix.IsSingular(), "inverse of non-singular matrix should be non-singular"); gfxRect transDirtyRect = gfxRect(aDirtyRect->x, aDirtyRect->y, aDirtyRect->width, aDirtyRect->height); transDirtyRect = invmatrix.TransformBounds(transDirtyRect); kidDirtyRect.IntersectRect(kidDirtyRect, nsLayoutUtils::RoundGfxRectToAppRect(transDirtyRect, PresContext()->AppUnitsPerCSSPixel())); // XXX after bug 614732 is fixed, we will compare mRect with aDirtyRect, // not with kidDirtyRect. I.e. // PRInt32 appUnitsPerDevPx = PresContext()->AppUnitsPerDevPixel(); // mRect.ToOutsidePixels(appUnitsPerDevPx).Intersects(*aDirtyRect) if (kidDirtyRect.IsEmpty()) return NS_OK; } gfxContext *gfx = aContext->ThebesContext(); gfx->Save(); if (GetStyleDisplay()->IsScrollableOverflow()) { float x, y, width, height; static_cast<nsSVGElement*>(mContent)-> GetAnimatedLengthValues(&x, &y, &width, &height, nsnull); gfxRect clipRect = nsSVGUtils::GetClipRectForFrame(this, 0.0f, 0.0f, width, height); nsSVGUtils::SetClipRect(gfx, matrix, clipRect); } gfx->Multiply(matrixForChildren); PRUint32 flags = nsLayoutUtils::PAINT_IN_TRANSFORM; if (SVGAutoRenderState::IsPaintingToWindow(aContext)) { flags |= nsLayoutUtils::PAINT_TO_WINDOW; } nsresult rv = nsLayoutUtils::PaintFrame(aContext, kid, nsRegion(kidDirtyRect), NS_RGBA(0,0,0,0), flags); gfx->Restore(); return rv; }
NS_IMETHODIMP nsSVGForeignObjectFrame::PaintSVG(nsSVGRenderState *aContext, const nsIntRect *aDirtyRect) { if (IsDisabled()) return NS_OK; nsIFrame* kid = GetFirstChild(nsnull); if (!kid) return NS_OK; gfxMatrix matrixForChildren = GetCanvasTMForChildren(); gfxMatrix matrix = GetCanvasTM(); nsRenderingContext *ctx = aContext->GetRenderingContext(this); if (!ctx || matrixForChildren.IsSingular()) { NS_WARNING("Can't render foreignObject element!"); return NS_ERROR_FAILURE; } /* Check if we need to draw anything. */ if (aDirtyRect) { PRInt32 appUnitsPerDevPx = PresContext()->AppUnitsPerDevPixel(); if (!mRect.ToOutsidePixels(appUnitsPerDevPx).Intersects(*aDirtyRect)) return NS_OK; } gfxContext *gfx = aContext->GetGfxContext(); gfx->Save(); if (GetStyleDisplay()->IsScrollableOverflow()) { float x, y, width, height; static_cast<nsSVGElement*>(mContent)-> GetAnimatedLengthValues(&x, &y, &width, &height, nsnull); gfxRect clipRect = nsSVGUtils::GetClipRectForFrame(this, 0.0f, 0.0f, width, height); nsSVGUtils::SetClipRect(gfx, matrix, clipRect); } gfx->Multiply(matrixForChildren); // Transform the dirty rect into the rectangle containing the // transformed dirty rect. gfxMatrix invmatrix = matrix.Invert(); NS_ASSERTION(!invmatrix.IsSingular(), "inverse of non-singular matrix should be non-singular"); gfxRect transDirtyRect = gfxRect(aDirtyRect->x, aDirtyRect->y, aDirtyRect->width, aDirtyRect->height); transDirtyRect = invmatrix.TransformBounds(transDirtyRect); transDirtyRect.Scale(nsPresContext::AppUnitsPerCSSPixel()); nsPoint tl(NSToCoordFloor(transDirtyRect.X()), NSToCoordFloor(transDirtyRect.Y())); nsPoint br(NSToCoordCeil(transDirtyRect.XMost()), NSToCoordCeil(transDirtyRect.YMost())); nsRect kidDirtyRect(tl.x, tl.y, br.x - tl.x, br.y - tl.y); kidDirtyRect.IntersectRect(kidDirtyRect, kid->GetRect()); PRUint32 flags = nsLayoutUtils::PAINT_IN_TRANSFORM; if (aContext->IsPaintingToWindow()) { flags |= nsLayoutUtils::PAINT_TO_WINDOW; } nsresult rv = nsLayoutUtils::PaintFrame(ctx, kid, nsRegion(kidDirtyRect), NS_RGBA(0,0,0,0), flags); gfx->Restore(); return rv; }