/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is the Mozilla SVG project. * * The Initial Developer of the Original Code is * Crocodile Clips Ltd.. * Portions created by the Initial Developer are Copyright (C) 2002 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Alex Fritze (original author) * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsSVGPathGeometryFrame.h" #include "nsIDOMSVGDocument.h" #include "nsIDOMElement.h" #include "nsIDocument.h" #include "nsISVGRenderer.h" #include "nsISVGRendererRegion.h" #include "nsISVGValueUtils.h" #include "nsIDOMSVGTransformable.h" #include "nsIDOMSVGAnimTransformList.h" #include "nsIDOMSVGTransformList.h" #include "nsISVGContainerFrame.h" #include "nsSVGGradientFrame.h" #include "nsReadableUtils.h" #include "nsUnicharUtils.h" #include "nsSVGAtoms.h" #include "nsCRT.h" #include "prdtoa.h" #include "nsSVGMarkerFrame.h" #include "nsISVGMarkable.h" #include "nsIViewManager.h" //////////////////////////////////////////////////////////////////////// // nsSVGPathGeometryFrame nsSVGPathGeometryFrame::nsSVGPathGeometryFrame() : mUpdateFlags(0) { #ifdef DEBUG // printf("nsSVGPathGeometryFrame %p CTOR\n", this); #endif } nsSVGPathGeometryFrame::~nsSVGPathGeometryFrame() { #ifdef DEBUG // printf("~nsSVGPathGeometryFrame %p\n", this); #endif nsCOMPtr transformable = do_QueryInterface(mContent); NS_ASSERTION(transformable, "wrong content element"); nsCOMPtr transforms; transformable->GetTransform(getter_AddRefs(transforms)); nsCOMPtr value = do_QueryInterface(transforms); NS_REMOVE_SVGVALUE_OBSERVER(transforms); } //---------------------------------------------------------------------- // nsISupports methods NS_INTERFACE_MAP_BEGIN(nsSVGPathGeometryFrame) NS_INTERFACE_MAP_ENTRY(nsISVGGeometrySource) NS_INTERFACE_MAP_ENTRY(nsISVGPathGeometrySource) NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) NS_INTERFACE_MAP_ENTRY(nsISVGValueObserver) NS_INTERFACE_MAP_ENTRY(nsISVGChildFrame) NS_INTERFACE_MAP_END_INHERITING(nsSVGPathGeometryFrameBase) //---------------------------------------------------------------------- // nsIFrame methods NS_IMETHODIMP nsSVGPathGeometryFrame::Init(nsPresContext* aPresContext, nsIContent* aContent, nsIFrame* aParent, nsStyleContext* aContext, nsIFrame* aPrevInFlow) { // rv = nsSVGPathGeometryFrameBase::Init(aPresContext, aContent, aParent, // aContext, aPrevInFlow); mContent = aContent; NS_IF_ADDREF(mContent); mParent = aParent; Init(); SetStyleContext(aPresContext, aContext); return NS_OK; } NS_IMETHODIMP nsSVGPathGeometryFrame::AttributeChanged(nsPresContext* aPresContext, nsIContent* aChild, PRInt32 aNameSpaceID, nsIAtom* aAttribute, PRInt32 aModType, PRInt32 aHint) { // we don't use this notification mechanism #ifdef DEBUG // printf("** nsSVGPathGeometryFrame::AttributeChanged("); // nsAutoString str; // aAttribute->ToString(str); // nsCAutoString cstr; // cstr.AssignWithConversion(str); // printf(cstr.get()); // printf(")\n"); #endif return NS_OK; } NS_IMETHODIMP nsSVGPathGeometryFrame::DidSetStyleContext(nsPresContext* aPresContext) { // XXX: we'd like to use the style_hint mechanism and the // ContentStateChanged/AttributeChanged functions for style changes // to get slightly finer granularity, but unfortunately the // style_hints don't map very well onto svg. Here seems to be the // best place to deal with style changes: UpdateGraphic(nsISVGGeometrySource::UPDATEMASK_ALL); return NS_OK; } //---------------------------------------------------------------------- // nsISVGChildFrame methods // marker helper void nsSVGPathGeometryFrame::GetMarkerFrames(nsSVGMarkerFrame **markerStart, nsSVGMarkerFrame **markerMid, nsSVGMarkerFrame **markerEnd) { nsIURI *aURI; *markerStart = *markerMid = *markerEnd = NULL; aURI = GetStyleSVG()->mMarkerEnd; if (aURI) NS_GetSVGMarkerFrame(markerEnd, aURI, mContent); aURI = GetStyleSVG()->mMarkerMid; if (aURI) NS_GetSVGMarkerFrame(markerMid, aURI, mContent); aURI = GetStyleSVG()->mMarkerStart; if (aURI) NS_GetSVGMarkerFrame(markerStart, aURI, mContent); } NS_IMETHODIMP nsSVGPathGeometryFrame::Paint(nsISVGRendererCanvas* canvas, const nsRect& dirtyRectTwips) { if (!GetStyleVisibility()->IsVisible()) return NS_OK; GetGeometry()->Render(canvas); nsISVGMarkable *markable; CallQueryInterface(this, &markable); if (markable) { nsSVGMarkerFrame *markerEnd, *markerMid, *markerStart; GetMarkerFrames(&markerStart, &markerMid, &markerEnd); if (!markerEnd && !markerMid && !markerStart) return NS_OK; float strokeWidth; GetStrokeWidth(&strokeWidth); nsVoidArray marks; markable->GetMarkPoints(&marks); PRUint32 num = marks.Count(); if (markerStart) markerStart->PaintMark(canvas, this, (nsSVGMark *)marks[0], strokeWidth); if (markerMid) for (PRUint32 i = 1; i < num - 1; i++) markerMid->PaintMark(canvas, this, (nsSVGMark *)marks[i], strokeWidth); if (markerEnd) markerEnd->PaintMark(canvas, this, (nsSVGMark *)marks[num-1], strokeWidth); } return NS_OK; } NS_IMETHODIMP nsSVGPathGeometryFrame::GetFrameForPoint(float x, float y, nsIFrame** hit) { #ifdef DEBUG //printf("nsSVGPathGeometryFrame(%p)::GetFrameForPoint\n", this); #endif // test for hit: *hit = nsnull; PRBool isHit; GetGeometry()->ContainsPoint(x, y, &isHit); if (isHit) *hit = this; return NS_OK; } NS_IMETHODIMP_(already_AddRefed) nsSVGPathGeometryFrame::GetCoveredRegion() { nsISVGRendererRegion *region = nsnull; GetGeometry()->GetCoveredRegion(®ion); nsISVGMarkable *markable; CallQueryInterface(this, &markable); if (markable) { nsSVGMarkerFrame *markerEnd, *markerMid, *markerStart; GetMarkerFrames(&markerStart, &markerMid, &markerEnd); if (!markerEnd && !markerMid && !markerStart) return region; float strokeWidth; GetStrokeWidth(&strokeWidth); nsVoidArray marks; markable->GetMarkPoints(&marks); PRUint32 num = marks.Count(); if (markerStart) { nsCOMPtr mark; mark = markerStart->RegionMark(this, (nsSVGMark *)marks[0], strokeWidth); nsCOMPtr tmp = dont_AddRef(region); mark->Combine(tmp, ®ion); } if (markerMid) for (PRUint32 i = 1; i < num - 1; i++) { nsCOMPtr mark; mark = markerMid->RegionMark(this, (nsSVGMark *)marks[i], strokeWidth); nsCOMPtr tmp = dont_AddRef(region); mark->Combine(tmp, ®ion); } if (markerEnd) { nsCOMPtr mark; mark = markerEnd->RegionMark(this, (nsSVGMark *)marks[num-1], strokeWidth); nsCOMPtr tmp = dont_AddRef(region); mark->Combine(tmp, ®ion); } } return region; } NS_IMETHODIMP nsSVGPathGeometryFrame::InitialUpdate() { UpdateGraphic(nsISVGGeometrySource::UPDATEMASK_ALL); return NS_OK; } NS_IMETHODIMP nsSVGPathGeometryFrame::NotifyCanvasTMChanged() { UpdateGraphic(nsISVGGeometrySource::UPDATEMASK_CANVAS_TM); return NS_OK; } NS_IMETHODIMP nsSVGPathGeometryFrame::NotifyRedrawSuspended() { // XXX should we cache the fact that redraw is suspended? return NS_OK; } NS_IMETHODIMP nsSVGPathGeometryFrame::NotifyRedrawUnsuspended() { if (mUpdateFlags != 0) { nsCOMPtr dirty_region; GetGeometry()->Update(mUpdateFlags, getter_AddRefs(dirty_region)); if (dirty_region) { nsISVGOuterSVGFrame* outerSVGFrame = GetOuterSVGFrame(); if (outerSVGFrame) outerSVGFrame->InvalidateRegion(dirty_region, PR_TRUE); } mUpdateFlags = 0; } return NS_OK; } NS_IMETHODIMP nsSVGPathGeometryFrame::GetBBox(nsIDOMSVGRect **_retval) { *_retval = nsnull; return NS_ERROR_FAILURE; } //---------------------------------------------------------------------- // nsISVGValueObserver methods: NS_IMETHODIMP nsSVGPathGeometryFrame::WillModifySVGObservable(nsISVGValue* observable) { return NS_OK; } NS_IMETHODIMP nsSVGPathGeometryFrame::DidModifySVGObservable (nsISVGValue* observable) { // the observables we're listening in on affect the canvastm by // default. We can specialize in the subclasses when needed. UpdateGraphic(nsISVGGeometrySource::UPDATEMASK_CANVAS_TM); return NS_OK; } //---------------------------------------------------------------------- // nsISVGGeometrySource methods: /* [noscript] readonly attribute nsPresContext presContext; */ NS_IMETHODIMP nsSVGPathGeometryFrame::GetPresContext(nsPresContext * *aPresContext) { // XXX gcc 3.2.2 requires the explicit 'nsSVGPathGeometryFrameBase::' qualification *aPresContext = nsSVGPathGeometryFrameBase::GetPresContext(); NS_ADDREF(*aPresContext); return NS_OK; } /* readonly attribute nsIDOMSVGMatrix canvasTM; */ NS_IMETHODIMP nsSVGPathGeometryFrame::GetCanvasTM(nsIDOMSVGMatrix * *aCTM) { *aCTM = nsnull; nsISVGContainerFrame *containerFrame; mParent->QueryInterface(NS_GET_IID(nsISVGContainerFrame), (void**)&containerFrame); if (!containerFrame) { NS_ERROR("invalid container"); return NS_ERROR_FAILURE; } nsCOMPtr parentTM = containerFrame->GetCanvasTM(); NS_ASSERTION(parentTM, "null TM"); // append our local transformations if we have any: nsCOMPtr localTM; { nsCOMPtr transformable = do_QueryInterface(mContent); NS_ASSERTION(transformable, "wrong content element"); nsCOMPtr atl; transformable->GetTransform(getter_AddRefs(atl)); NS_ASSERTION(atl, "null animated transform list"); nsCOMPtr transforms; atl->GetAnimVal(getter_AddRefs(transforms)); NS_ASSERTION(transforms, "null transform list"); PRUint32 numberOfItems; transforms->GetNumberOfItems(&numberOfItems); if (numberOfItems>0) transforms->GetConsolidationMatrix(getter_AddRefs(localTM)); } if (localTM) { return parentTM->Multiply(localTM, aCTM); } *aCTM = parentTM; NS_ADDREF(*aCTM); return NS_OK; } /* readonly attribute float strokeOpacity; */ NS_IMETHODIMP nsSVGPathGeometryFrame::GetStrokeOpacity(float *aStrokeOpacity) { *aStrokeOpacity = GetStyleSVG()->mStrokeOpacity * GetStyleDisplay()->mOpacity; return NS_OK; } /* readonly attribute float strokeWidth; */ NS_IMETHODIMP nsSVGPathGeometryFrame::GetStrokeWidth(float *aStrokeWidth) { *aStrokeWidth = GetStyleSVG()->mStrokeWidth; return NS_OK; } /* void getStrokeDashArray ([array, size_is (count)] out float arr, out unsigned long count); */ NS_IMETHODIMP nsSVGPathGeometryFrame::GetStrokeDashArray(float **arr, PRUint32 *count) { const nsStyleSVG *svg = GetStyleSVG(); *count = svg->mStrokeDasharrayLength; if (*count) { *arr = (float *) nsMemory::Alloc(*count * sizeof(float)); if (*arr) memcpy(*arr, svg->mStrokeDasharray, *count * sizeof(float)); else { *count = 0; return NS_ERROR_OUT_OF_MEMORY; } } return NS_OK; } /* readonly attribute float strokeDashoffset; */ NS_IMETHODIMP nsSVGPathGeometryFrame::GetStrokeDashoffset(float *aStrokeDashoffset) { *aStrokeDashoffset = GetStyleSVG()->mStrokeDashoffset; return NS_OK; } /* readonly attribute unsigned short strokeLinecap; */ NS_IMETHODIMP nsSVGPathGeometryFrame::GetStrokeLinecap(PRUint16 *aStrokeLinecap) { *aStrokeLinecap = GetStyleSVG()->mStrokeLinecap; return NS_OK; } /* readonly attribute unsigned short strokeLinejoin; */ NS_IMETHODIMP nsSVGPathGeometryFrame::GetStrokeLinejoin(PRUint16 *aStrokeLinejoin) { *aStrokeLinejoin = GetStyleSVG()->mStrokeLinejoin; return NS_OK; } /* readonly attribute float strokeMiterlimit; */ NS_IMETHODIMP nsSVGPathGeometryFrame::GetStrokeMiterlimit(float *aStrokeMiterlimit) { *aStrokeMiterlimit = GetStyleSVG()->mStrokeMiterlimit; return NS_OK; } /* readonly attribute float fillOpacity; */ NS_IMETHODIMP nsSVGPathGeometryFrame::GetFillOpacity(float *aFillOpacity) { *aFillOpacity = GetStyleSVG()->mFillOpacity * GetStyleDisplay()->mOpacity; return NS_OK; } /* readonly attribute unsigned short fillRule; */ NS_IMETHODIMP nsSVGPathGeometryFrame::GetFillRule(PRUint16 *aFillRule) { *aFillRule = GetStyleSVG()->mFillRule; return NS_OK; } /* readonly attribute unsigned short strokePaintType; */ NS_IMETHODIMP nsSVGPathGeometryFrame::GetStrokePaintType(PRUint16 *aStrokePaintType) { *aStrokePaintType = GetStyleSVG()->mStroke.mType; return NS_OK; } /* [noscript] readonly attribute nscolor strokePaint; */ NS_IMETHODIMP nsSVGPathGeometryFrame::GetStrokePaint(nscolor *aStrokePaint) { *aStrokePaint = GetStyleSVG()->mStroke.mPaint.mColor; return NS_OK; } /* [noscript] void GetStrokeGradient(nsISVGGradient **aGrad); */ NS_IMETHODIMP nsSVGPathGeometryFrame::GetStrokeGradient(nsISVGGradient **aGrad) { nsIURI *aServer; aServer = GetStyleSVG()->mStroke.mPaint.mPaintServer; if (aServer == nsnull) return NS_ERROR_FAILURE; // Now have the URI. Get the gradient return NS_GetSVGGradient(aGrad, aServer, mContent, nsSVGPathGeometryFrameBase::GetPresContext()->PresShell()); } /* readonly attribute unsigned short fillPaintType; */ NS_IMETHODIMP nsSVGPathGeometryFrame::GetFillPaintType(PRUint16 *aFillPaintType) { *aFillPaintType = GetStyleSVG()->mFill.mType; return NS_OK; } /* [noscript] readonly attribute nscolor fillPaint; */ NS_IMETHODIMP nsSVGPathGeometryFrame::GetFillPaint(nscolor *aFillPaint) { *aFillPaint = GetStyleSVG()->mFill.mPaint.mColor; return NS_OK; } /* [noscript] void GetFillGradient(nsISVGGradient **aGrad); */ NS_IMETHODIMP nsSVGPathGeometryFrame::GetFillGradient(nsISVGGradient **aGrad) { nsIURI *aServer; aServer = GetStyleSVG()->mFill.mPaint.mPaintServer; if (aServer == nsnull) return NS_ERROR_FAILURE; // Now have the URI. Get the gradient return NS_GetSVGGradient(aGrad, aServer, mContent, nsSVGPathGeometryFrameBase::GetPresContext()->PresShell()); } //---------------------------------------------------------------------- // nsISVGPathGeometrySource methods: NS_IMETHODIMP nsSVGPathGeometryFrame::GetHittestMask(PRUint16 *aHittestMask) { *aHittestMask=0; switch(GetStyleSVG()->mPointerEvents) { case NS_STYLE_POINTER_EVENTS_NONE: break; case NS_STYLE_POINTER_EVENTS_VISIBLEPAINTED: if (GetStyleVisibility()->IsVisible()) { if (GetStyleSVG()->mFill.mType != eStyleSVGPaintType_None) *aHittestMask |= HITTEST_MASK_FILL; if (GetStyleSVG()->mStroke.mType != eStyleSVGPaintType_None) *aHittestMask |= HITTEST_MASK_STROKE; } break; case NS_STYLE_POINTER_EVENTS_VISIBLEFILL: if (GetStyleVisibility()->IsVisible()) { *aHittestMask |= HITTEST_MASK_FILL; } break; case NS_STYLE_POINTER_EVENTS_VISIBLESTROKE: if (GetStyleVisibility()->IsVisible()) { *aHittestMask |= HITTEST_MASK_STROKE; } break; case NS_STYLE_POINTER_EVENTS_VISIBLE: if (GetStyleVisibility()->IsVisible()) { *aHittestMask |= HITTEST_MASK_FILL; *aHittestMask |= HITTEST_MASK_STROKE; } break; case NS_STYLE_POINTER_EVENTS_PAINTED: if (GetStyleSVG()->mFill.mType != eStyleSVGPaintType_None) *aHittestMask |= HITTEST_MASK_FILL; if (GetStyleSVG()->mStroke.mType != eStyleSVGPaintType_None) *aHittestMask |= HITTEST_MASK_STROKE; break; case NS_STYLE_POINTER_EVENTS_FILL: *aHittestMask |= HITTEST_MASK_FILL; break; case NS_STYLE_POINTER_EVENTS_STROKE: *aHittestMask |= HITTEST_MASK_STROKE; break; case NS_STYLE_POINTER_EVENTS_ALL: *aHittestMask |= HITTEST_MASK_FILL; *aHittestMask |= HITTEST_MASK_STROKE; break; default: NS_ERROR("not reached"); break; } return NS_OK; } /* readonly attribute unsigned short shapeRendering; */ NS_IMETHODIMP nsSVGPathGeometryFrame::GetShapeRendering(PRUint16 *aShapeRendering) { *aShapeRendering = GetStyleSVG()->mShapeRendering; return NS_OK; } //---------------------------------------------------------------------- nsresult nsSVGPathGeometryFrame::Init() { // nsresult rv = nsSVGPathGeometryFrameBase::Init(); // if (NS_FAILED(rv)) return rv; // all path geometry frames listen in on changes to their // corresponding content element's transform attribute: nsCOMPtr transformable = do_QueryInterface(mContent); NS_ASSERTION(transformable, "wrong content element"); nsCOMPtr transforms; transformable->GetTransform(getter_AddRefs(transforms)); NS_ADD_SVGVALUE_OBSERVER(transforms); // construct a pathgeometry object: nsISVGOuterSVGFrame* outerSVGFrame = GetOuterSVGFrame(); if (!outerSVGFrame) { NS_ERROR("Null outerSVGFrame"); return NS_ERROR_FAILURE; } nsCOMPtr renderer; outerSVGFrame->GetRenderer(getter_AddRefs(renderer)); renderer->CreatePathGeometry(this, getter_AddRefs(mGeometry)); if (!mGeometry) return NS_ERROR_FAILURE; return NS_OK; } nsISVGRendererPathGeometry * nsSVGPathGeometryFrame::GetGeometry() { #ifdef DEBUG NS_ASSERTION(mGeometry, "invalid geometry object"); #endif return mGeometry; } void nsSVGPathGeometryFrame::UpdateGraphic(PRUint32 flags) { mUpdateFlags |= flags; nsISVGOuterSVGFrame *outerSVGFrame = GetOuterSVGFrame(); if (!outerSVGFrame) { NS_ERROR("null outerSVGFrame"); return; } PRBool suspended; outerSVGFrame->IsRedrawSuspended(&suspended); if (!suspended) { nsCOMPtr dirty_region; GetGeometry()->Update(mUpdateFlags, getter_AddRefs(dirty_region)); if (dirty_region) { // if we're painting a marker, this will get called during paint // when the region already be invalidated as needed nsIView *view = GetClosestView(); if (!view) return; nsIViewManager *vm = view->GetViewManager(); PRBool painting; vm->IsPainting(painting); if (!painting) outerSVGFrame->InvalidateRegion(dirty_region, PR_TRUE); } mUpdateFlags = 0; } } nsISVGOuterSVGFrame * nsSVGPathGeometryFrame::GetOuterSVGFrame() { NS_ASSERTION(mParent, "null parent"); nsISVGContainerFrame *containerFrame; mParent->QueryInterface(NS_GET_IID(nsISVGContainerFrame), (void**)&containerFrame); if (!containerFrame) { NS_ERROR("invalid container"); return nsnull; } return containerFrame->GetOuterSVGFrame(); }