Back to index

lightning-sunbird  0.9+nobinonly
nsSVGGDIPlusPathBuilder.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) 2002
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include <windows.h>
00040 
00041 // unknwn.h is needed to build with WIN32_LEAN_AND_MEAN
00042 #include <unknwn.h>
00043 
00044 #include <Gdiplus.h>
00045 using namespace Gdiplus;
00046 
00047 #include "nsCOMPtr.h"
00048 #include "nsISVGPathGeometrySource.h"
00049 #include "nsISVGRendererPathBuilder.h"
00050 #include <math.h>
00051 
00056 
00057 
00062 class PointStack
00063 {
00064 public:
00065   PointStack();
00066   ~PointStack();
00067 
00068   struct PointData {
00069     float x;
00070     float y;
00071   };
00072   
00073   void PushPoint(float x, float y);
00074   const PointData *PopPoint();
00075 private:  
00076   void Grow();
00077   
00078   PointData mOrigin;
00079   PointData *mPoints;
00080   int mCapacity;
00081   int mSize;
00082 };
00083 
00086 PointStack::PointStack()
00087     : mPoints(0), mCapacity(0), mSize(0)
00088 {
00089   mOrigin.x = 0.0f;
00090   mOrigin.y = 0.0f;
00091 }
00092 
00093 PointStack::~PointStack()
00094 {
00095   if (mPoints) delete[] mPoints;
00096 }
00097 
00098 void PointStack::PushPoint(float x, float y)
00099 {
00100   if (mCapacity-mSize<1)
00101     Grow();
00102   NS_ASSERTION(mCapacity-mSize>0, "no space in array");
00103   mPoints[mSize].x = x;
00104   mPoints[mSize].y = y;
00105   ++mSize;
00106 }
00107 
00108 const PointStack::PointData *PointStack::PopPoint()
00109 {
00110   if (mSize == 0) return &mOrigin;
00111 
00112   return &mPoints[--mSize];
00113 }
00114 
00115 void PointStack::Grow()
00116 {
00117   // nested subpaths are not very common, so we'll just grow linearly
00118   PointData *oldPoints = mPoints;
00119   mPoints = new PointData[++mCapacity];
00120   if (oldPoints) {
00121     memcpy(mPoints, oldPoints, sizeof(PointData) * mSize);
00122     delete[] oldPoints;
00123   }
00124 }
00125 
00130 
00131 
00134 class nsSVGGDIPlusPathBuilder : public nsISVGRendererPathBuilder
00135 {
00136 protected:
00137   friend nsresult NS_NewSVGGDIPlusPathBuilder(nsISVGRendererPathBuilder **result,
00138                                               nsISVGPathGeometrySource *src,
00139                                               GraphicsPath* dest);
00140 
00141   nsSVGGDIPlusPathBuilder(nsISVGPathGeometrySource *src,
00142                           GraphicsPath* dest);
00143 
00144 public:
00145   // nsISupports interface:
00146   NS_DECL_ISUPPORTS
00147 
00148   // nsISVGRendererPathBuilder interface:
00149   NS_DECL_NSISVGRENDERERPATHBUILDER
00150 
00151 private:
00152   nsCOMPtr<nsISVGPathGeometrySource> mSource;
00153   GraphicsPath *mPath;
00154   PointStack mSubPathStack;
00155   PointF mCurrentPoint;
00156 };
00157 
00160 //----------------------------------------------------------------------
00161 // implementation:
00162 
00163 nsSVGGDIPlusPathBuilder::nsSVGGDIPlusPathBuilder(nsISVGPathGeometrySource *src,
00164                                                  GraphicsPath* dest)
00165     : mSource(src), mPath(dest)
00166 {
00167   PRUint16 fillrule;
00168   mSource->GetFillRule(&fillrule);
00169   mPath->SetFillMode(fillrule==nsISVGGeometrySource::FILL_RULE_NONZERO ?
00170                      FillModeWinding : FillModeAlternate);
00171 }
00172 
00173 nsresult
00174 NS_NewSVGGDIPlusPathBuilder(nsISVGRendererPathBuilder **result,
00175                             nsISVGPathGeometrySource *src,
00176                             GraphicsPath* dest)
00177 {
00178   *result = new nsSVGGDIPlusPathBuilder(src, dest);
00179   if (!result) return NS_ERROR_OUT_OF_MEMORY;
00180 
00181   NS_ADDREF(*result);
00182 
00183   return NS_OK;
00184 }
00185 
00186 //----------------------------------------------------------------------
00187 // nsISupports methods:
00188 
00189 NS_IMPL_ADDREF(nsSVGGDIPlusPathBuilder)
00190 NS_IMPL_RELEASE(nsSVGGDIPlusPathBuilder)
00191 
00192 NS_INTERFACE_MAP_BEGIN(nsSVGGDIPlusPathBuilder)
00193   NS_INTERFACE_MAP_ENTRY(nsISVGRendererPathBuilder)
00194   NS_INTERFACE_MAP_ENTRY(nsISupports)
00195 NS_INTERFACE_MAP_END
00196 
00197 //----------------------------------------------------------------------
00198 // nsISVGRendererPathBuilder methods:
00199 
00201 NS_IMETHODIMP
00202 nsSVGGDIPlusPathBuilder::Moveto(float x, float y)
00203 {
00204 //  mPath->CloseFigure();
00205   mPath->StartFigure();
00206   mCurrentPoint.X = x;
00207   mCurrentPoint.Y = y;
00208   mSubPathStack.PushPoint(x, y);
00209   return NS_OK;
00210 }
00211 
00213 NS_IMETHODIMP
00214 nsSVGGDIPlusPathBuilder::Lineto(float x, float y)
00215 {
00216   mPath->AddLine(mCurrentPoint.X, mCurrentPoint.Y, x, y);
00217   mCurrentPoint.X = x;
00218   mCurrentPoint.Y = y;
00219   return NS_OK;
00220 }
00221 
00223 NS_IMETHODIMP
00224 nsSVGGDIPlusPathBuilder::Curveto(float x, float y, float x1, float y1, float x2, float y2)
00225 {
00226   mPath->AddBezier(mCurrentPoint.X, mCurrentPoint.Y, // startpoint
00227                    x1, y1, x2, y2, // controlpoints
00228                    x, y); // endpoint
00229   mCurrentPoint.X = x;
00230   mCurrentPoint.Y = y;
00231   return NS_OK;
00232 }
00233 
00234 /* helper for Arcto : */
00235 static inline double CalcVectorAngle(double ux, double uy, double vx, double vy)
00236 {
00237   double ta = atan2(uy, ux);
00238        double tb = atan2(vy, vx);
00239        if (tb >= ta)
00240               return tb-ta;
00241        return 6.28318530718 - (ta-tb);
00242 }
00243 
00244 
00246 NS_IMETHODIMP
00247 nsSVGGDIPlusPathBuilder::Arcto(float x2, float y2, float rx, float ry, float angle, PRBool largeArcFlag, PRBool sweepFlag)
00248 {
00249   const double pi = 3.14159265359;
00250   const double radPerDeg = pi/180.0;
00251 
00252   float x1=mCurrentPoint.X, y1=mCurrentPoint.Y;
00253 
00254   // 1. Treat out-of-range parameters as described in
00255   // http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
00256   
00257   // If the endpoints (x1, y1) and (x2, y2) are identical, then this
00258   // is equivalent to omitting the elliptical arc segment entirely
00259   if (x1 == x2 && y1 == y2) return NS_OK;
00260 
00261   // If rX = 0 or rY = 0 then this arc is treated as a straight line
00262   // segment (a "lineto") joining the endpoints.
00263   if (rx == 0.0f || ry == 0.0f) {
00264     return Lineto(x2, y2);
00265   }
00266 
00267   // If rX or rY have negative signs, these are dropped; the absolute
00268   // value is used instead.
00269   if (rx<0.0) rx = -rx;
00270   if (ry<0.0) ry = -ry;
00271   
00272   // 2. convert to center parameterization as shown in
00273   // http://www.w3.org/TR/SVG/implnote.html
00274   double sinPhi = sin(angle*radPerDeg);
00275   double cosPhi = cos(angle*radPerDeg);
00276   
00277   double x1dash =  cosPhi * (x1-x2)/2.0 + sinPhi * (y1-y2)/2.0;
00278   double y1dash = -sinPhi * (x1-x2)/2.0 + cosPhi * (y1-y2)/2.0;
00279 
00280   double root;
00281   double numerator = rx*rx*ry*ry - rx*rx*y1dash*y1dash - ry*ry*x1dash*x1dash;
00282 
00283   if (numerator < 0.0) { 
00284     //  If rX , rY and are such that there is no solution (basically,
00285     //  the ellipse is not big enough to reach from (x1, y1) to (x2,
00286     //  y2)) then the ellipse is scaled up uniformly until there is
00287     //  exactly one solution (until the ellipse is just big enough).
00288 
00289     // -> find factor s, such that numerator' with rx'=s*rx and
00290     //    ry'=s*ry becomes 0 :
00291     float s = (float)sqrt(1.0 - numerator/(rx*rx*ry*ry));
00292 
00293     rx *= s;
00294     ry *= s;
00295     root = 0.0;
00296 
00297   }
00298   else {
00299     root = (largeArcFlag == sweepFlag ? -1.0 : 1.0) *
00300       sqrt( numerator/(rx*rx*y1dash*y1dash+ry*ry*x1dash*x1dash) );
00301   }
00302   
00303   double cxdash = root*rx*y1dash/ry;
00304   double cydash = -root*ry*x1dash/rx;
00305   
00306   double cx = cosPhi * cxdash - sinPhi * cydash + (x1+x2)/2.0;
00307   double cy = sinPhi * cxdash + cosPhi * cydash + (y1+y2)/2.0;
00308   double theta1 = CalcVectorAngle(1.0, 0.0, (x1dash-cxdash)/rx, (y1dash-cydash)/ry);
00309   double dtheta = CalcVectorAngle((x1dash-cxdash)/rx, (y1dash-cydash)/ry,
00310                                   (-x1dash-cxdash)/rx, (-y1dash-cydash)/ry);
00311   if (!sweepFlag && dtheta>0)
00312     dtheta -= 2.0*pi;
00313   else if (sweepFlag && dtheta<0)
00314     dtheta += 2.0*pi;
00315   
00316   // 3. convert into cubic bezier segments <= 90deg
00317   int segments = (int)ceil(fabs(dtheta/(pi/2.0)));
00318   double delta = dtheta/segments;
00319   double t = 8.0/3.0 * sin(delta/4.0) * sin(delta/4.0) / sin(delta/2.0);
00320   
00321   for (int i = 0; i < segments; ++i) {
00322     double cosTheta1 = cos(theta1);
00323     double sinTheta1 = sin(theta1);
00324     double theta2 = theta1 + delta;
00325     double cosTheta2 = cos(theta2);
00326     double sinTheta2 = sin(theta2);
00327     
00328     // a) calculate endpoint of the segment:
00329     double xe = cosPhi * rx*cosTheta2 - sinPhi * ry*sinTheta2 + cx;
00330     double ye = sinPhi * rx*cosTheta2 + cosPhi * ry*sinTheta2 + cy;
00331 
00332     // b) calculate gradients at start/end points of segment:
00333     double dx1 = t * ( - cosPhi * rx*sinTheta1 - sinPhi * ry*cosTheta1);
00334     double dy1 = t * ( - sinPhi * rx*sinTheta1 + cosPhi * ry*cosTheta1);
00335     
00336     double dxe = t * ( cosPhi * rx*sinTheta2 + sinPhi * ry*cosTheta2);
00337     double dye = t * ( sinPhi * rx*sinTheta2 - cosPhi * ry*cosTheta2);
00338 
00339     // c) draw the cubic bezier:
00340     Curveto((float)xe, (float)ye, (float)(x1+dx1), (float)(y1+dy1),
00341             (float)(xe+dxe), (float)(ye+dye));
00342 
00343     // do next segment
00344     theta1 = theta2;
00345     x1 = (float)xe;
00346     y1 = (float)ye;
00347   }
00348   return NS_OK;
00349 }
00350 
00352 NS_IMETHODIMP
00353 nsSVGGDIPlusPathBuilder::ClosePath(float *newX, float *newY)
00354 {
00355   mPath->CloseFigure();
00356   const PointStack::PointData *point = mSubPathStack.PopPoint();
00357 
00358   *newX = point->x;
00359   *newY = point->y;
00360   mCurrentPoint.X = point->x;
00361   mCurrentPoint.Y = point->y;
00362   
00363   return NS_OK;
00364 }
00365 
00367 NS_IMETHODIMP
00368 nsSVGGDIPlusPathBuilder::EndPath()
00369 {
00370   return NS_OK;
00371 }