Back to index

lightning-sunbird  0.9+nobinonly
nsSVGPathFrame.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the Mozilla SVG project.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Crocodile Clips Ltd..
00019  * Portions created by the Initial Developer are Copyright (C) 2001
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
00024  *   Daniele Nicolodi <daniele@grinta.net>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include <math.h>
00041 
00042 #include "nsSVGPathGeometryFrame.h"
00043 #include "nsIDOMSVGAnimatedPathData.h"
00044 #include "nsIDOMSVGPathSegList.h"
00045 #include "nsIDOMSVGPathSeg.h"
00046 #include "nsIDOMSVGMatrix.h"
00047 #include "nsISVGRendererPathBuilder.h"
00048 #include "nsISVGMarkable.h"
00049 #include "nsSVGMarkerFrame.h"
00050 #include "nsISupports.h"
00051 #include "nsLayoutAtoms.h"
00052 #include "nsISVGPathFlatten.h"
00053 
00054 class nsSVGPathFrame : public nsSVGPathGeometryFrame,
00055                        public nsISVGMarkable,
00056                        public nsISVGPathFlatten
00057 {
00058 protected:
00059   friend nsresult
00060   NS_NewSVGPathFrame(nsIPresShell* aPresShell, nsIContent* aContent, nsIFrame** aNewFrame);
00061 
00062   ~nsSVGPathFrame();
00063   NS_IMETHOD InitSVG();
00064   
00065 public:
00066   // nsISVGValueObserver interface:
00067   NS_IMETHOD DidModifySVGObservable(nsISVGValue* observable,
00068                                     nsISVGValue::modificationType aModType);
00069 
00070   // nsISVGPathGeometrySource interface:
00071   NS_IMETHOD ConstructPath(nsISVGRendererPathBuilder *pathBuilder);
00072 
00073   // nsISVGMarkable interface
00074   void GetMarkPoints(nsVoidArray *aMarks);
00075 
00076   // nsISVGPathFlatten interface
00077   NS_IMETHOD GetFlattenedPath(nsSVGPathData **data, PRBool useLocalTransform);
00078 
00079    // nsISupports interface:
00080   NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr);
00081 
00087   virtual nsIAtom* GetType() const;
00088 
00089 #ifdef DEBUG
00090   NS_IMETHOD GetFrameName(nsAString& aResult) const
00091   {
00092     return MakeFrameName(NS_LITERAL_STRING("SVGPath"), aResult);
00093   }
00094 #endif
00095 
00096 private:
00097   NS_IMETHOD_(nsrefcnt) AddRef() { return NS_OK; }
00098   NS_IMETHOD_(nsrefcnt) Release() { return NS_OK; }  
00099 
00100   nsCOMPtr<nsIDOMSVGPathSegList> mSegments;
00101 };
00102 
00103 NS_INTERFACE_MAP_BEGIN(nsSVGPathFrame)
00104   NS_INTERFACE_MAP_ENTRY(nsISVGMarkable)
00105   NS_INTERFACE_MAP_ENTRY(nsISVGPathFlatten)
00106 NS_INTERFACE_MAP_END_INHERITING(nsSVGPathGeometryFrame)
00107 
00108 //----------------------------------------------------------------------
00109 // Implementation
00110 
00111 nsresult
00112 NS_NewSVGPathFrame(nsIPresShell* aPresShell, nsIContent* aContent, nsIFrame** aNewFrame)
00113 {
00114   *aNewFrame = nsnull;
00115   
00116   nsCOMPtr<nsIDOMSVGAnimatedPathData> anim_data = do_QueryInterface(aContent);
00117   if (!anim_data) {
00118 #ifdef DEBUG
00119     printf("warning: trying to construct an SVGPathFrame for a content element that doesn't support the right interfaces\n");
00120 #endif
00121     return NS_ERROR_FAILURE;
00122   }
00123   
00124   nsSVGPathFrame* it = new (aPresShell) nsSVGPathFrame;
00125   if (nsnull == it)
00126     return NS_ERROR_OUT_OF_MEMORY;
00127 
00128   *aNewFrame = it;
00129   return NS_OK;
00130 }
00131 
00132 nsSVGPathFrame::~nsSVGPathFrame()
00133 {
00134   nsCOMPtr<nsISVGValue> value;
00135   if (mSegments && (value = do_QueryInterface(mSegments)))
00136       value->RemoveObserver(this);
00137 }
00138 
00139 NS_IMETHODIMP
00140 nsSVGPathFrame::InitSVG()
00141 {
00142   nsresult rv = nsSVGPathGeometryFrame::InitSVG();
00143   if (NS_FAILED(rv)) return rv;
00144   
00145   nsCOMPtr<nsIDOMSVGAnimatedPathData> anim_data = do_QueryInterface(mContent);
00146   NS_ASSERTION(anim_data,"wrong content element");
00147   anim_data->GetAnimatedPathSegList(getter_AddRefs(mSegments));
00148   NS_ASSERTION(mSegments, "no pathseglist");
00149   if (!mSegments) return NS_ERROR_FAILURE;
00150   nsCOMPtr<nsISVGValue> value = do_QueryInterface(mSegments);
00151   if (value)
00152     value->AddObserver(this);
00153   
00154   return NS_OK;
00155 }  
00156 
00157 //----------------------------------------------------------------------
00158 // nsISVGValueObserver methods:
00159 
00160 NS_IMETHODIMP
00161 nsSVGPathFrame::DidModifySVGObservable(nsISVGValue* observable,
00162                                        nsISVGValue::modificationType aModType)
00163 {
00164   nsCOMPtr<nsIDOMSVGPathSegList> l = do_QueryInterface(observable);
00165   if (l && mSegments==l) {
00166     UpdateGraphic(nsISVGPathGeometrySource::UPDATEMASK_PATH);
00167     return NS_OK;
00168   }
00169   // else
00170   return nsSVGPathGeometryFrame::DidModifySVGObservable(observable, aModType);
00171 }
00172 
00173 //----------------------------------------------------------------------
00174 // nsISVGPathGeometrySource methods:
00175 
00176 /* void constructPath (in nsISVGRendererPathBuilder pathBuilder); */
00177 NS_IMETHODIMP nsSVGPathFrame::ConstructPath(nsISVGRendererPathBuilder* pathBuilder)
00178 {
00179   PRUint32 count;
00180   mSegments->GetNumberOfItems(&count);
00181   if (count == 0) return NS_OK;
00182   
00183   float cx = 0.0f; // current point
00184   float cy = 0.0f;
00185   
00186   float cx1 = 0.0f; // last controlpoint (for s,S,t,T)
00187   float cy1 = 0.0f;
00188 
00189   PRUint16 lastSegmentType = nsIDOMSVGPathSeg::PATHSEG_UNKNOWN;
00190   
00191   PRUint32 i;
00192   for (i = 0; i < count; ++i) {
00193     nsCOMPtr<nsIDOMSVGPathSeg> segment;
00194     mSegments->GetItem(i, getter_AddRefs(segment));
00195 
00196     PRUint16 type = nsIDOMSVGPathSeg::PATHSEG_UNKNOWN;
00197     segment->GetPathSegType(&type);
00198 
00199     PRBool absCoords = PR_FALSE;
00200     
00201     switch (type) {
00202       case nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH:
00203         pathBuilder->ClosePath(&cx,&cy);
00204         break;
00205         
00206       case nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS:
00207         absCoords = PR_TRUE;
00208       case nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL:
00209         {
00210           float x, y;
00211           if (!absCoords) {
00212             nsCOMPtr<nsIDOMSVGPathSegMovetoRel> moveseg = do_QueryInterface(segment);
00213             NS_ASSERTION(moveseg, "interface not implemented");
00214             moveseg->GetX(&x);
00215             moveseg->GetY(&y);
00216             x += cx;
00217             y += cy;
00218           } else {
00219             nsCOMPtr<nsIDOMSVGPathSegMovetoAbs> moveseg = do_QueryInterface(segment);
00220             NS_ASSERTION(moveseg, "interface not implemented");
00221             moveseg->GetX(&x);
00222             moveseg->GetY(&y);
00223           }            
00224           cx = x;
00225           cy = y;
00226           pathBuilder->Moveto(x,y);
00227         }
00228         break;
00229         
00230       case nsIDOMSVGPathSeg::PATHSEG_LINETO_ABS:
00231         absCoords = PR_TRUE;
00232       case nsIDOMSVGPathSeg::PATHSEG_LINETO_REL:
00233         {
00234           float x, y;
00235           if (!absCoords) {
00236             nsCOMPtr<nsIDOMSVGPathSegLinetoRel> lineseg = do_QueryInterface(segment);
00237             NS_ASSERTION(lineseg, "interface not implemented");
00238             lineseg->GetX(&x);
00239             lineseg->GetY(&y);
00240             x += cx;
00241             y += cy;
00242           } else {
00243             nsCOMPtr<nsIDOMSVGPathSegLinetoAbs> lineseg = do_QueryInterface(segment);
00244             NS_ASSERTION(lineseg, "interface not implemented");
00245             lineseg->GetX(&x);
00246             lineseg->GetY(&y);
00247           }            
00248           cx = x;
00249           cy = y;
00250           pathBuilder->Lineto(x,y);
00251         }
00252         break;        
00253 
00254       case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS:
00255         absCoords = PR_TRUE;
00256       case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_REL:
00257         {
00258           float x, y, x1, y1, x2, y2;
00259           if (!absCoords) {
00260             nsCOMPtr<nsIDOMSVGPathSegCurvetoCubicRel> curveseg = do_QueryInterface(segment);
00261             NS_ASSERTION(curveseg, "interface not implemented");
00262             curveseg->GetX(&x);
00263             curveseg->GetY(&y);
00264             curveseg->GetX1(&x1);
00265             curveseg->GetY1(&y1);
00266             curveseg->GetX2(&x2);
00267             curveseg->GetY2(&y2);
00268             x  += cx;
00269             y  += cy;
00270             x1 += cx;
00271             y1 += cy;
00272             x2 += cx;
00273             y2 += cy;
00274           } else {
00275             nsCOMPtr<nsIDOMSVGPathSegCurvetoCubicAbs> curveseg = do_QueryInterface(segment);
00276             NS_ASSERTION(curveseg, "interface not implemented");
00277             curveseg->GetX(&x);
00278             curveseg->GetY(&y);
00279             curveseg->GetX1(&x1);
00280             curveseg->GetY1(&y1);
00281             curveseg->GetX2(&x2);
00282             curveseg->GetY2(&y2);
00283           }            
00284           cx = x;
00285           cy = y;
00286           cx1 = x2;
00287           cy1 = y2;
00288           pathBuilder->Curveto(x, y, x1, y1, x2, y2);
00289         }
00290         break;
00291         
00292       case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS:
00293         absCoords = PR_TRUE;
00294       case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL:
00295         {
00296           float x, y, x1, y1, x31, y31, x32, y32;
00297           if (!absCoords) {
00298             nsCOMPtr<nsIDOMSVGPathSegCurvetoQuadraticRel> curveseg = do_QueryInterface(segment);
00299             NS_ASSERTION(curveseg, "interface not implemented");
00300             curveseg->GetX(&x);
00301             curveseg->GetY(&y);
00302             curveseg->GetX1(&x1);
00303             curveseg->GetY1(&y1);
00304             x  += cx;
00305             y  += cy;
00306             x1 += cx;
00307             y1 += cy;
00308           } else {
00309             nsCOMPtr<nsIDOMSVGPathSegCurvetoQuadraticAbs> curveseg = do_QueryInterface(segment);
00310             NS_ASSERTION(curveseg, "interface not implemented");
00311             curveseg->GetX(&x);
00312             curveseg->GetY(&y);
00313             curveseg->GetX1(&x1);
00314             curveseg->GetY1(&y1);
00315           }    
00316 
00317           // conversion of quadratic bezier curve to cubic bezier curve:
00318           x31 = cx + (x1 - cx) * 2 / 3;
00319           y31 = cy + (y1 - cy) * 2 / 3;
00320           x32 = x1 + (x - x1) / 3;
00321           y32 = y1 + (y - y1) / 3;
00322 
00323           cx  = x;
00324           cy  = y;
00325           cx1 = x1;
00326           cy1 = y1;
00327 
00328           pathBuilder->Curveto(x, y, x31, y31, x32, y32);
00329         }
00330         break;
00331 
00332       case nsIDOMSVGPathSeg::PATHSEG_ARC_ABS:
00333         absCoords = PR_TRUE;
00334       case nsIDOMSVGPathSeg::PATHSEG_ARC_REL:
00335         {
00336           float x0, y0, x, y, r1, r2, angle;
00337           PRBool largeArcFlag, sweepFlag;
00338 
00339           x0 = cx;
00340           y0 = cy;
00341           
00342           if (!absCoords) {
00343             nsCOMPtr<nsIDOMSVGPathSegArcRel> arcseg = do_QueryInterface(segment);
00344             NS_ASSERTION(arcseg, "interface not implemented");
00345             arcseg->GetX(&x);
00346             arcseg->GetY(&y);
00347             arcseg->GetR1(&r1);
00348             arcseg->GetR2(&r2);
00349             arcseg->GetAngle(&angle);
00350             arcseg->GetLargeArcFlag(&largeArcFlag);
00351             arcseg->GetSweepFlag(&sweepFlag);
00352 
00353             x  += cx;
00354             y  += cy;
00355           } else {
00356             nsCOMPtr<nsIDOMSVGPathSegArcAbs> arcseg = do_QueryInterface(segment);
00357             NS_ASSERTION(arcseg, "interface not implemented");
00358             arcseg->GetX(&x);
00359             arcseg->GetY(&y);
00360             arcseg->GetR1(&r1);
00361             arcseg->GetR2(&r2);
00362             arcseg->GetAngle(&angle);
00363             arcseg->GetLargeArcFlag(&largeArcFlag);
00364             arcseg->GetSweepFlag(&sweepFlag);
00365           }            
00366           cx = x;
00367           cy = y;
00368           
00369           pathBuilder->Arcto(x, y, r1, r2, angle, largeArcFlag, sweepFlag);
00370         }
00371         break;
00372 
00373       case nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_ABS:
00374         absCoords = PR_TRUE;
00375       case nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_REL:
00376         {
00377           float x;
00378           float y = cy;
00379           if (!absCoords) {
00380             nsCOMPtr<nsIDOMSVGPathSegLinetoHorizontalRel> lineseg = do_QueryInterface(segment);
00381             NS_ASSERTION(lineseg, "interface not implemented");
00382             lineseg->GetX(&x);
00383             x += cx;
00384           } else {
00385             nsCOMPtr<nsIDOMSVGPathSegLinetoHorizontalAbs> lineseg = do_QueryInterface(segment);
00386             NS_ASSERTION(lineseg, "interface not implemented");
00387             lineseg->GetX(&x);
00388           }
00389           cx = x;
00390           pathBuilder->Lineto(x,y);
00391         }
00392         break;        
00393 
00394       case nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_ABS:
00395         absCoords = PR_TRUE;
00396       case nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_REL:
00397         {
00398           float x = cx;
00399           float y;
00400           if (!absCoords) {
00401             nsCOMPtr<nsIDOMSVGPathSegLinetoVerticalRel> lineseg = do_QueryInterface(segment);
00402             NS_ASSERTION(lineseg, "interface not implemented");
00403             lineseg->GetY(&y);
00404             y += cy;
00405           } else {
00406             nsCOMPtr<nsIDOMSVGPathSegLinetoVerticalAbs> lineseg = do_QueryInterface(segment);
00407             NS_ASSERTION(lineseg, "interface not implemented");
00408             lineseg->GetY(&y);
00409           }
00410           cy = y;
00411           pathBuilder->Lineto(x,y);
00412         }
00413         break;
00414 
00415       case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
00416         absCoords = PR_TRUE;
00417       case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
00418         {
00419           float x, y, x1, y1, x2, y2;
00420 
00421           if (lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_REL        ||
00422               lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS        ||
00423               lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL ||
00424               lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS ) {
00425             // the first controlpoint is the reflection of the last one about the current point:
00426             x1 = 2*cx - cx1;
00427             y1 = 2*cy - cy1;
00428           }
00429           else {
00430             // the first controlpoint is equal to the current point:
00431             x1 = cx;
00432             y1 = cy;
00433           }
00434           
00435           if (!absCoords) {
00436             nsCOMPtr<nsIDOMSVGPathSegCurvetoCubicSmoothRel> curveseg = do_QueryInterface(segment);
00437             NS_ASSERTION(curveseg, "interface not implemented");
00438             curveseg->GetX(&x);
00439             curveseg->GetY(&y);
00440             curveseg->GetX2(&x2);
00441             curveseg->GetY2(&y2);
00442             x  += cx;
00443             y  += cy;
00444             x2 += cx;
00445             y2 += cy;
00446           } else {
00447             nsCOMPtr<nsIDOMSVGPathSegCurvetoCubicSmoothAbs> curveseg = do_QueryInterface(segment);
00448             NS_ASSERTION(curveseg, "interface not implemented");
00449             curveseg->GetX(&x);
00450             curveseg->GetY(&y);
00451             curveseg->GetX2(&x2);
00452             curveseg->GetY2(&y2);
00453           }            
00454           cx  = x;
00455           cy  = y;
00456           cx1 = x2;
00457           cy1 = y2;
00458           pathBuilder->Curveto(x, y, x1, y1, x2, y2);
00459         }
00460         break;
00461 
00462       case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
00463         absCoords = PR_TRUE;
00464       case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
00465         {
00466           float x, y, x1, y1, x31, y31, x32, y32;
00467 
00468           if (lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL        ||
00469               lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS        ||
00470               lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL ||
00471               lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS ) {
00472             // the first controlpoint is the reflection of the last one about the current point:
00473             x1 = 2*cx - cx1;
00474             y1 = 2*cy - cy1;
00475           }
00476           else {
00477             // the first controlpoint is equal to the current point:
00478             x1 = cx;
00479             y1 = cy;
00480           }
00481           
00482           if (!absCoords) {
00483             nsCOMPtr<nsIDOMSVGPathSegCurvetoQuadraticSmoothRel> curveseg = do_QueryInterface(segment);
00484             NS_ASSERTION(curveseg, "interface not implemented");
00485             curveseg->GetX(&x);
00486             curveseg->GetY(&y);
00487             x  += cx;
00488             y  += cy;
00489           } else {
00490             nsCOMPtr<nsIDOMSVGPathSegCurvetoQuadraticSmoothAbs> curveseg = do_QueryInterface(segment);
00491             NS_ASSERTION(curveseg, "interface not implemented");
00492             curveseg->GetX(&x);
00493             curveseg->GetY(&y);
00494           }            
00495 
00496           // conversion of quadratic bezier curve to cubic bezier curve:
00497           x31 = cx + (x1 - cx) * 2 / 3;
00498           y31 = cy + (y1 - cy) * 2 / 3;
00499           x32 = x1 + (x - x1) / 3;
00500           y32 = y1 + (y - y1) / 3;
00501 
00502           cx  = x;
00503           cy  = y;
00504           cx1 = x1;
00505           cy1 = y1;
00506 
00507           pathBuilder->Curveto(x, y, x31, y31, x32, y32);
00508         }
00509         break;
00510 
00511       default:
00512         NS_ASSERTION(1==0, "unknown path segment");
00513         break;
00514     }
00515     lastSegmentType = type;
00516   }
00517   
00518   return NS_OK;  
00519 }
00520 
00521 static float
00522 calcAngle(float ux, float uy, float vx, float vy)
00523 {
00524   float ta = atan2(uy, ux);
00525   float tb = atan2(vy, vx);
00526   if (tb >= ta)
00527     return tb-ta;
00528   return 2*M_PI - (ta-tb);
00529 }
00530 
00531 //----------------------------------------------------------------------
00532 // nsISVGMarkable methods:
00533 
00534 void
00535 nsSVGPathFrame::GetMarkPoints(nsVoidArray *aMarks) {
00536   PRUint32 count;
00537   mSegments->GetNumberOfItems(&count);
00538   nsCOMPtr<nsIDOMSVGPathSeg> segment;
00539   
00540   float cx = 0.0f; // current point
00541   float cy = 0.0f;
00542 
00543   float cx1 = 0.0f; // last controlpoint (for s,S,t,T)
00544   float cy1 = 0.0f;
00545 
00546   PRUint16 lastSegmentType = nsIDOMSVGPathSeg::PATHSEG_UNKNOWN;
00547 
00548   float px, py;    // subpath initial point
00549   float pathAngle, pathIndex;
00550 
00551   float prevAngle = 0, startAngle, endAngle;
00552   
00553   PRBool newSegment = PR_FALSE;
00554 
00555   PRUint32 i;
00556   for (i = 0; i < count; ++i) {
00557     nsCOMPtr<nsIDOMSVGPathSeg> segment;
00558     mSegments->GetItem(i, getter_AddRefs(segment));
00559 
00560     PRUint16 type = nsIDOMSVGPathSeg::PATHSEG_UNKNOWN;
00561     segment->GetPathSegType(&type);
00562 
00563     float x, y;
00564     PRBool absCoords = PR_FALSE;
00565     
00566     switch (type) {
00567     case nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH:
00568     {
00569       x = px;
00570       y = py;
00571       startAngle = endAngle = atan2(y - cy, x - cx);
00572     }
00573     break;
00574       
00575     case nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS:
00576       absCoords = PR_TRUE;
00577     case nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL:
00578     {
00579       if (!absCoords) {
00580         nsCOMPtr<nsIDOMSVGPathSegMovetoRel> moveseg = do_QueryInterface(segment);
00581         NS_ASSERTION(moveseg, "interface not implemented");
00582         moveseg->GetX(&x);
00583         moveseg->GetY(&y);
00584         x += cx;
00585         y += cy;
00586       } else {
00587         nsCOMPtr<nsIDOMSVGPathSegMovetoAbs> moveseg = do_QueryInterface(segment);
00588         NS_ASSERTION(moveseg, "interface not implemented");
00589         moveseg->GetX(&x);
00590         moveseg->GetY(&y);
00591       }
00592       px = x;
00593       py = y;
00594       startAngle = endAngle = prevAngle;
00595       newSegment = PR_TRUE;
00596     }
00597     break;
00598     
00599     case nsIDOMSVGPathSeg::PATHSEG_LINETO_ABS:
00600       absCoords = PR_TRUE;
00601     case nsIDOMSVGPathSeg::PATHSEG_LINETO_REL:
00602     {
00603       if (!absCoords) {
00604         nsCOMPtr<nsIDOMSVGPathSegLinetoRel> lineseg = do_QueryInterface(segment);
00605         NS_ASSERTION(lineseg, "interface not implemented");
00606         lineseg->GetX(&x);
00607         lineseg->GetY(&y);
00608         x += cx;
00609         y += cy;
00610       } else {
00611         nsCOMPtr<nsIDOMSVGPathSegLinetoAbs> lineseg = do_QueryInterface(segment);
00612         NS_ASSERTION(lineseg, "interface not implemented");
00613         lineseg->GetX(&x);
00614         lineseg->GetY(&y);
00615       }
00616       startAngle = endAngle = atan2(y - cy, x - cx);
00617     }
00618     break;        
00619     
00620     case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS:
00621       absCoords = PR_TRUE;
00622     case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_REL:
00623     {
00624       float x1, y1, x2, y2;
00625       if (!absCoords) {
00626         nsCOMPtr<nsIDOMSVGPathSegCurvetoCubicRel> curveseg = do_QueryInterface(segment);
00627         NS_ASSERTION(curveseg, "interface not implemented");
00628         curveseg->GetX(&x);
00629         curveseg->GetY(&y);
00630         curveseg->GetX1(&x1);
00631         curveseg->GetY1(&y1);
00632         curveseg->GetX2(&x2);
00633         curveseg->GetY2(&y2);
00634         x  += cx;
00635         y  += cy;
00636         x1 += cx;
00637         y1 += cy;
00638         x2 += cx;
00639         y2 += cy;
00640       } else {
00641         nsCOMPtr<nsIDOMSVGPathSegCurvetoCubicAbs> curveseg = do_QueryInterface(segment);
00642         NS_ASSERTION(curveseg, "interface not implemented");
00643         curveseg->GetX(&x);
00644         curveseg->GetY(&y);
00645         curveseg->GetX1(&x1);
00646         curveseg->GetY1(&y1);
00647         curveseg->GetX2(&x2);
00648         curveseg->GetY2(&y2);
00649       }
00650 
00651       cx1 = x2;
00652       cy1 = y2;
00653 
00654       if (x1 == cx && y1 == cy) {
00655         x1 = x2;
00656         y1 = y2;
00657       }
00658 
00659       if (x2 == x && y2 == y) {
00660         x2 = x1;
00661         y2 = y1;
00662       }
00663 
00664       startAngle = atan2(y1 - cy, x1 - cx);
00665       endAngle = atan2(y - y2, x - x2);
00666     }
00667     break;
00668     
00669     case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS:
00670       absCoords = PR_TRUE;
00671     case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL:
00672     {
00673       float x1, y1;
00674 
00675       if (!absCoords) {
00676         nsCOMPtr<nsIDOMSVGPathSegCurvetoQuadraticRel> curveseg = do_QueryInterface(segment);
00677         NS_ASSERTION(curveseg, "interface not implemented");
00678         curveseg->GetX(&x);
00679         curveseg->GetY(&y);
00680         curveseg->GetX1(&x1);
00681         curveseg->GetY1(&y1);
00682         x  += cx;
00683         y  += cy;
00684         x1 += cx;
00685         y1 += cy;
00686       } else {
00687         nsCOMPtr<nsIDOMSVGPathSegCurvetoQuadraticAbs> curveseg = do_QueryInterface(segment);
00688         NS_ASSERTION(curveseg, "interface not implemented");
00689         curveseg->GetX(&x);
00690         curveseg->GetY(&y);
00691         curveseg->GetX1(&x1);
00692         curveseg->GetY1(&y1);
00693       }    
00694 
00695       cx1 = x1;
00696       cy1 = y1;
00697 
00698       startAngle = atan2(y1 - cy, x1 - cx);
00699       endAngle = atan2(y - y1, x - x1);
00700     }
00701     break;
00702     
00703     case nsIDOMSVGPathSeg::PATHSEG_ARC_ABS:
00704       absCoords = PR_TRUE;
00705     case nsIDOMSVGPathSeg::PATHSEG_ARC_REL:
00706     {
00707       float r1, r2, angle;
00708       PRBool largeArcFlag, sweepFlag;
00709 
00710       if (!absCoords) {
00711         nsCOMPtr<nsIDOMSVGPathSegArcRel> arcseg = do_QueryInterface(segment);
00712         NS_ASSERTION(arcseg, "interface not implemented");
00713         arcseg->GetX(&x);
00714         arcseg->GetY(&y);
00715         arcseg->GetR1(&r1);
00716         arcseg->GetR2(&r2);
00717         arcseg->GetAngle(&angle);
00718         arcseg->GetLargeArcFlag(&largeArcFlag);
00719         arcseg->GetSweepFlag(&sweepFlag);
00720         
00721         x  += cx;
00722         y  += cy;
00723       } else {
00724         nsCOMPtr<nsIDOMSVGPathSegArcAbs> arcseg = do_QueryInterface(segment);
00725         NS_ASSERTION(arcseg, "interface not implemented");
00726         arcseg->GetX(&x);
00727         arcseg->GetY(&y);
00728         arcseg->GetR1(&r1);
00729         arcseg->GetR2(&r2);
00730         arcseg->GetAngle(&angle);
00731         arcseg->GetLargeArcFlag(&largeArcFlag);
00732         arcseg->GetSweepFlag(&sweepFlag);
00733       }
00734 
00735       /* check for degenerate ellipse */
00736       if (r1 == 0.0 || r2 == 0.0) {
00737         startAngle = endAngle = atan2(y - cy, x - cx);
00738         break;
00739       }
00740 
00741       r1 = fabs(r1);  r2 = fabs(r2);
00742 
00743       float xp, yp, cxp, cyp;
00744 
00745       /* slope fun&games ... see SVG spec, section F.6 */
00746       angle = angle*M_PI/180.0;
00747       xp = cos(angle)*(cx-x)/2.0 + sin(angle)*(cy-y)/2.0;
00748       yp = -sin(angle)*(cx-x)/2.0 + cos(angle)*(cy-y)/2.0;
00749 
00750       /* make sure radii are large enough */
00751       float root, numerator = r1*r1*r2*r2 - r1*r1*yp*yp - r2*r2*xp*xp;
00752       if (numerator < 0.0) {
00753         float s = sqrt(1.0 - numerator/(r1*r1*r2*r2));
00754         r1 *= s;
00755         r2 *= s;
00756         root = 0.0;
00757       } else {
00758         root = sqrt(numerator/(r1*r1*yp*yp + r2*r2*xp*xp));
00759         if (largeArcFlag == sweepFlag)
00760           root = -root;
00761       }
00762       cxp = root*r1*yp/r2;
00763       cyp = -root*r2*xp/r1;
00764 
00765       float theta, delta;
00766       theta = calcAngle(1.0, 0.0,  (xp-cxp)/r1, (yp-cyp)/r2);
00767       delta  = calcAngle((xp-cxp)/r1, (yp-cyp)/r2,  (-xp-cxp)/r1, (-yp-cyp)/r2);
00768       if (!sweepFlag && delta > 0)
00769         delta -= 2.0*M_PI;
00770       else if (sweepFlag && delta < 0)
00771         delta += 2.0*M_PI;
00772 
00773       float tx1, ty1, tx2, ty2;
00774       tx1 = -cos(angle)*r1*sin(theta) - sin(angle)*r2*cos(theta);
00775       ty1 = -sin(angle)*r1*sin(theta) + cos(angle)*r2*cos(theta);
00776       tx2 = -cos(angle)*r1*sin(theta+delta) - sin(angle)*r2*cos(theta+delta);
00777       ty2 = -sin(angle)*r1*sin(theta+delta) + cos(angle)*r2*cos(theta+delta);
00778 
00779       if (delta < 0.0f) {
00780         tx1 = -tx1;
00781         ty1 = -ty1;
00782         tx2 = -tx2;
00783         ty2 = -ty2;
00784       }
00785 
00786       startAngle = atan2(ty1, tx1);
00787       endAngle = atan2(ty2, tx2);
00788     }
00789     break;
00790     
00791     case nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_ABS:
00792       absCoords = PR_TRUE;
00793     case nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_REL:
00794     {
00795       y = cy;
00796       if (!absCoords) {
00797         nsCOMPtr<nsIDOMSVGPathSegLinetoHorizontalRel> lineseg = do_QueryInterface(segment);
00798         NS_ASSERTION(lineseg, "interface not implemented");
00799         lineseg->GetX(&x);
00800         x += cx;
00801       } else {
00802         nsCOMPtr<nsIDOMSVGPathSegLinetoHorizontalAbs> lineseg = do_QueryInterface(segment);
00803         NS_ASSERTION(lineseg, "interface not implemented");
00804         lineseg->GetX(&x);
00805       }
00806       startAngle = endAngle = atan2(0, x - cx);
00807     }
00808     break;        
00809     
00810     case nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_ABS:
00811       absCoords = PR_TRUE;
00812     case nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_REL:
00813     {
00814       x = cx;
00815       if (!absCoords) {
00816         nsCOMPtr<nsIDOMSVGPathSegLinetoVerticalRel> lineseg = do_QueryInterface(segment);
00817         NS_ASSERTION(lineseg, "interface not implemented");
00818         lineseg->GetY(&y);
00819         y += cy;
00820       } else {
00821         nsCOMPtr<nsIDOMSVGPathSegLinetoVerticalAbs> lineseg = do_QueryInterface(segment);
00822         NS_ASSERTION(lineseg, "interface not implemented");
00823         lineseg->GetY(&y);
00824       }
00825       startAngle = endAngle = atan2(y - cy, 0);
00826     }
00827     break;
00828     
00829     case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
00830       absCoords = PR_TRUE;
00831     case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
00832     {
00833       float x1, y1, x2, y2;
00834 
00835       if (lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_REL        ||
00836           lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS        ||
00837           lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL ||
00838           lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS ) {
00839         // the first controlpoint is the reflection of the last one about the current point:
00840         x1 = 2*cx - cx1;
00841         y1 = 2*cy - cy1;
00842       }
00843       else {
00844         // the first controlpoint is equal to the current point:
00845         x1 = cx;
00846         y1 = cy;
00847       }
00848       
00849       if (!absCoords) {
00850         nsCOMPtr<nsIDOMSVGPathSegCurvetoCubicSmoothRel> curveseg = do_QueryInterface(segment);
00851         NS_ASSERTION(curveseg, "interface not implemented");
00852         curveseg->GetX(&x);
00853         curveseg->GetY(&y);
00854         curveseg->GetX2(&x2);
00855         curveseg->GetY2(&y2);
00856         x  += cx;
00857         y  += cy;
00858         x2 += cx;
00859         y2 += cy;
00860       } else {
00861         nsCOMPtr<nsIDOMSVGPathSegCurvetoCubicSmoothAbs> curveseg = do_QueryInterface(segment);
00862         NS_ASSERTION(curveseg, "interface not implemented");
00863         curveseg->GetX(&x);
00864         curveseg->GetY(&y);
00865         curveseg->GetX2(&x2);
00866         curveseg->GetY2(&y2);
00867       }
00868 
00869       cx1 = x2;
00870       cy1 = y2;
00871 
00872       if (x1 == cx && y1 == cy) {
00873         x1 = x2;
00874         y1 = y2;
00875       }
00876 
00877       if (x2 == x && y2 == y) {
00878         x2 = x1;
00879         y2 = y1;
00880       }
00881 
00882       startAngle = atan2(y1 - cy, x1 - cx);
00883       endAngle = atan2(y - y2, x - x2);
00884     }
00885     break;
00886     
00887     case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
00888       absCoords = PR_TRUE;
00889     case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
00890       { 
00891         float x1, y1;
00892         
00893         if (lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL        ||
00894             lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS        ||
00895             lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL ||
00896             lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS ) {
00897           // the first controlpoint is the reflection of the last one about the current point:
00898           x1 = 2*cx - cx1;
00899           y1 = 2*cy - cy1;
00900         }
00901         else {
00902           // the first controlpoint is equal to the current point:
00903           x1 = cx;
00904           y1 = cy;
00905         }
00906         
00907         if (!absCoords) {
00908           nsCOMPtr<nsIDOMSVGPathSegCurvetoQuadraticSmoothRel> curveseg = do_QueryInterface(segment);
00909           NS_ASSERTION(curveseg, "interface not implemented");
00910           curveseg->GetX(&x);
00911           curveseg->GetY(&y);
00912           x  += cx;
00913           y  += cy;
00914         } else {
00915           nsCOMPtr<nsIDOMSVGPathSegCurvetoQuadraticSmoothAbs> curveseg = do_QueryInterface(segment);
00916           NS_ASSERTION(curveseg, "interface not implemented");
00917           curveseg->GetX(&x);
00918           curveseg->GetY(&y);
00919         }
00920 
00921         cx1 = x1;
00922         cy1 = y1;
00923         
00924         startAngle = atan2(y1 - cy, x1 - cx);
00925         endAngle = atan2(y - y1, x - x1);
00926       }
00927       break;
00928       
00929     default:
00930       NS_ASSERTION(1==0, "unknown path segment");
00931       break;
00932     }
00933     lastSegmentType = type;
00934 
00935     if (newSegment &&
00936         type != nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS &&
00937         type != nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL) {
00938       pathIndex = aMarks->Count() - 1;
00939       pathAngle = startAngle;
00940       ((nsSVGMark *)aMarks->ElementAt(aMarks->Count()-1))->angle = pathAngle;
00941       newSegment = PR_FALSE;
00942       prevAngle = endAngle;
00943     } else if (type == nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS ||
00944                type == nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL) {
00945       if (aMarks->Count())
00946         ((nsSVGMark *)aMarks->ElementAt(aMarks->Count()-1))->angle = prevAngle;
00947     } else {
00948       ((nsSVGMark *)aMarks->ElementAt(aMarks->Count()-1))->angle =
00949         nsSVGMarkerFrame::bisect(prevAngle, startAngle);
00950       prevAngle = endAngle;
00951     }
00952     
00953     nsSVGMark *mark = new nsSVGMark;
00954     mark->x = x;
00955     mark->y = y;
00956     aMarks->AppendElement(mark);
00957     
00958     if (type == nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH) {
00959       prevAngle = nsSVGMarkerFrame::bisect(endAngle, pathAngle);
00960       ((nsSVGMark *)aMarks->ElementAt(pathIndex))->angle = prevAngle;
00961     }
00962 
00963     cx = x;
00964     cy = y;
00965   }
00966 
00967   if (aMarks->Count())
00968     ((nsSVGMark *)aMarks->ElementAt(aMarks->Count()-1))->angle = prevAngle;
00969 }
00970 
00971 nsIAtom *
00972 nsSVGPathFrame::GetType() const
00973 {
00974   return nsLayoutAtoms::svgPathFrame;
00975 }
00976 
00977 //----------------------------------------------------------------------
00978 // nsISVGPathFlatten methods:
00979 
00980 NS_IMETHODIMP
00981 nsSVGPathFrame::GetFlattenedPath(nsSVGPathData **data,
00982                                  PRBool useLocalTransform)
00983 {
00984   nsISVGChildFrame *svgParent = nsnull;
00985   if (useLocalTransform) {
00986     CallQueryInterface(mParent, &svgParent);
00987     // svgParent==null is ok when the path is a child of nsSVGOuterSVGFrame
00988     if (!svgParent && mParent->GetType() != nsLayoutAtoms::svgOuterSVGFrame)
00989       return NS_ERROR_FAILURE;
00990   }
00991 
00992   nsISVGChildFrame* matrixFrame = useLocalTransform ? svgParent : this;
00993 
00994   if (matrixFrame) {
00995     matrixFrame->SetMatrixPropagation(PR_FALSE);
00996   }
00997 
00998   GetGeometry()->Flatten(data);
00999 
01000   if (matrixFrame) {
01001     matrixFrame->SetMatrixPropagation(PR_TRUE);
01002   }
01003 
01004   return NS_OK;
01005 }