Back to index

lightning-sunbird  0.9+nobinonly
nsSVGCairoPathGeometry.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 Cairo Renderer project.
00016  *
00017  * The Initial Developer of the Original Code is IBM Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 2004
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Parts of this file contain code derived from the following files(s)
00022  * of the Mozilla SVG project (these parts are Copyright (C) by their
00023  * respective copyright-holders):
00024  *    layout/svg/renderer/src/libart/nsSVGLibartPathGeometry.cpp
00025  *
00026  * Contributor(s):
00027  *   Alex Fritze <alex.fritze@crocodile-clips.com>
00028  *
00029  * Alternatively, the contents of this file may be used under the terms of
00030  * either of the GNU General Public License Version 2 or later (the "GPL"),
00031  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00032  * in which case the provisions of the GPL or the LGPL are applicable instead
00033  * of those above. If you wish to allow use of your version of this file only
00034  * under the terms of either the GPL or the LGPL, and not to allow others to
00035  * use your version of this file under the terms of the MPL, indicate your
00036  * decision by deleting the provisions above and replace them with the notice
00037  * and other provisions required by the GPL or the LGPL. If you do not delete
00038  * the provisions above, a recipient may use your version of this file under
00039  * the terms of any one of the MPL, the GPL or the LGPL.
00040  *
00041  * ***** END LICENSE BLOCK ***** */
00042 
00043 #include "nsCOMPtr.h"
00044 #include "nsSVGCairoPathGeometry.h"
00045 #include "nsISVGRendererPathGeometry.h"
00046 #include "nsISVGCairoCanvas.h"
00047 #include "nsISVGCairoRegion.h"
00048 #include "nsIDOMSVGMatrix.h"
00049 #include "nsISVGRendererRegion.h"
00050 #include "nsISVGPathGeometrySource.h"
00051 #include "nsISVGRendererPathBuilder.h"
00052 #include "nsSVGCairoPathBuilder.h"
00053 #include "nsMemory.h"
00054 #include <float.h>
00055 #include "cairo.h"
00056 #include "nsSVGCairoRegion.h"
00057 #include "nsISVGGradient.h"
00058 #include "nsSVGCairoGradient.h"
00059 #include "nsIDOMSVGRect.h"
00060 #include "nsSVGTypeCIDs.h"
00061 #include "nsIComponentManager.h"
00062 #include "nsISVGPathFlatten.h"
00063 #include "nsSVGUtils.h"
00064 
00065 extern cairo_surface_t *gSVGCairoDummySurface;
00066 
00071 
00072 
00075 class nsSVGCairoPathGeometry : public nsISVGRendererPathGeometry
00076 {
00077 protected:
00078   friend nsresult NS_NewSVGCairoPathGeometry(nsISVGRendererPathGeometry **result,
00079                                              nsISVGPathGeometrySource *src);
00080 
00081   nsSVGCairoPathGeometry();
00082   ~nsSVGCairoPathGeometry();
00083   nsresult Init(nsISVGPathGeometrySource* src);
00084 
00085 public:
00086   // nsISupports interface:
00087   NS_DECL_ISUPPORTS
00088   
00089   // nsISVGRendererPathGeometry interface:
00090   NS_DECL_NSISVGRENDERERPATHGEOMETRY
00091   
00092 private:
00093   nsCOMPtr<nsISVGPathGeometrySource> mSource;
00094   nsCOMPtr<nsISVGRendererRegion> mCoveredRegion;
00095 
00096   void GeneratePath(cairo_t *ctx, nsISVGCairoCanvas* aCanvas);
00097   void SetupStrokeGeometry(cairo_t *ctx);
00098   void SetupStrokeHitGeometry(cairo_t *ctx);
00099 };
00100 
00104 //----------------------------------------------------------------------
00105 // implementation:
00106 
00107 nsSVGCairoPathGeometry::nsSVGCairoPathGeometry()
00108 {
00109 }
00110 
00111 nsSVGCairoPathGeometry::~nsSVGCairoPathGeometry()
00112 {
00113 }
00114 
00115 nsresult nsSVGCairoPathGeometry::Init(nsISVGPathGeometrySource* src)
00116 {
00117   mSource = src;
00118   return NS_OK;
00119 }
00120 
00121 
00122 nsresult
00123 NS_NewSVGCairoPathGeometry(nsISVGRendererPathGeometry **result,
00124                            nsISVGPathGeometrySource *src)
00125 {
00126   nsSVGCairoPathGeometry* pg = new nsSVGCairoPathGeometry();
00127   if (!pg) return NS_ERROR_OUT_OF_MEMORY;
00128 
00129   NS_ADDREF(pg);
00130 
00131   nsresult rv = pg->Init(src);
00132 
00133   if (NS_FAILED(rv)) {
00134     NS_RELEASE(pg);
00135     return rv;
00136   }
00137   
00138   *result = pg;
00139   return rv;
00140 }
00141 
00142 //----------------------------------------------------------------------
00143 // nsISupports methods:
00144 
00145 NS_IMPL_ADDREF(nsSVGCairoPathGeometry)
00146 NS_IMPL_RELEASE(nsSVGCairoPathGeometry)
00147 
00148 NS_INTERFACE_MAP_BEGIN(nsSVGCairoPathGeometry)
00149 NS_INTERFACE_MAP_ENTRY(nsISVGRendererPathGeometry)
00150 NS_INTERFACE_MAP_ENTRY(nsISupports)
00151 NS_INTERFACE_MAP_END
00152 
00153 //----------------------------------------------------------------------
00154 
00155 void
00156 nsSVGCairoPathGeometry::GeneratePath(cairo_t *ctx, nsISVGCairoCanvas* aCanvas)
00157 {
00158   nsCOMPtr<nsIDOMSVGMatrix> ctm;
00159   mSource->GetCanvasTM(getter_AddRefs(ctm));
00160   NS_ASSERTION(ctm, "graphic source didn't specify a ctm");
00161 
00162   float m[6];
00163   float val;
00164   ctm->GetA(&val);
00165   m[0] = val;
00166     
00167   ctm->GetB(&val);
00168   m[1] = val;
00169     
00170   ctm->GetC(&val);  
00171   m[2] = val;  
00172     
00173   ctm->GetD(&val);  
00174   m[3] = val;  
00175   
00176   ctm->GetE(&val);
00177   m[4] = val;
00178   
00179   ctm->GetF(&val);
00180   m[5] = val;
00181 
00182   cairo_matrix_t matrix = { m[0], m[1], m[2], m[3], m[4], m[5] };
00183   if (aCanvas) {
00184     aCanvas->AdjustMatrixForInitialTransform(&matrix);
00185   }
00186 
00187   cairo_matrix_t inverse = matrix;
00188   if (cairo_matrix_invert(&inverse)) {
00189     cairo_identity_matrix(ctx);
00190     cairo_new_path(ctx);
00191     return;
00192   }
00193   cairo_set_matrix(ctx, &matrix);
00194 
00195   nsCOMPtr<nsISVGRendererPathBuilder> builder;
00196   NS_NewSVGCairoPathBuilder(getter_AddRefs(builder), ctx);
00197   mSource->ConstructPath(builder);
00198   builder->EndPath();
00199 }
00200 
00201 void
00202 nsSVGCairoPathGeometry::SetupStrokeGeometry(cairo_t *ctx)
00203 {
00204   float width;
00205   mSource->GetStrokeWidth(&width);
00206   cairo_set_line_width(ctx, double(width));
00207 
00208   PRUint16 capStyle;
00209   mSource->GetStrokeLinecap(&capStyle);
00210   switch (capStyle) {
00211     case nsISVGGeometrySource::STROKE_LINECAP_BUTT:
00212       cairo_set_line_cap(ctx, CAIRO_LINE_CAP_BUTT);
00213       break;
00214     case nsISVGGeometrySource::STROKE_LINECAP_ROUND:
00215       cairo_set_line_cap(ctx, CAIRO_LINE_CAP_ROUND);
00216       break;
00217     case nsISVGGeometrySource::STROKE_LINECAP_SQUARE:
00218       cairo_set_line_cap(ctx, CAIRO_LINE_CAP_SQUARE);
00219       break;
00220   }
00221 
00222   float miterlimit;
00223   mSource->GetStrokeMiterlimit(&miterlimit);
00224   cairo_set_miter_limit(ctx, double(miterlimit));
00225 
00226   PRUint16 joinStyle;
00227   mSource->GetStrokeLinejoin(&joinStyle);
00228   switch(joinStyle) {
00229     case nsISVGGeometrySource::STROKE_LINEJOIN_MITER:
00230       cairo_set_line_join(ctx, CAIRO_LINE_JOIN_MITER);
00231       break;
00232     case nsISVGGeometrySource::STROKE_LINEJOIN_ROUND:
00233       cairo_set_line_join(ctx, CAIRO_LINE_JOIN_ROUND);
00234       break;
00235     case nsISVGGeometrySource::STROKE_LINEJOIN_BEVEL:
00236       cairo_set_line_join(ctx, CAIRO_LINE_JOIN_BEVEL);
00237       break;
00238     }
00239 }
00240 
00241 void
00242 nsSVGCairoPathGeometry::SetupStrokeHitGeometry(cairo_t *ctx)
00243 {
00244   SetupStrokeGeometry(ctx);
00245 
00246   float *dashArray, offset;
00247   PRUint32 count;
00248   mSource->GetStrokeDashArray(&dashArray, &count);
00249   if (count > 0) {
00250     double *dashes = new double[count];
00251     for (unsigned i=0; i<count; i++)
00252       dashes[i] = dashArray[i];
00253     mSource->GetStrokeDashoffset(&offset);
00254     cairo_set_dash(ctx, dashes, count, double(offset));
00255     nsMemory::Free(dashArray);
00256     delete [] dashes;
00257   }
00258 }
00259 
00260 //----------------------------------------------------------------------
00261 // nsISVGRendererPathGeometry methods:
00262 
00264 NS_IMETHODIMP
00265 nsSVGCairoPathGeometry::Render(nsISVGRendererCanvas *canvas)
00266 {
00267   nsCOMPtr<nsISVGCairoCanvas> cairoCanvas = do_QueryInterface(canvas);
00268   NS_ASSERTION(cairoCanvas, "wrong svg render context for geometry!");
00269   if (!cairoCanvas) return NS_ERROR_FAILURE;
00270 
00271   cairo_t *ctx = cairoCanvas->GetContext();
00272 
00273   PRUint16 renderMode;
00274   canvas->GetRenderMode(&renderMode);
00275   cairo_matrix_t matrix;
00276 
00277   if (renderMode == nsISVGRendererCanvas::SVG_RENDER_MODE_NORMAL) {
00278     cairo_new_path(ctx);
00279 
00280     /* save/pop the state so we don't screw up the xform */
00281     cairo_save(ctx);
00282   } else {
00283     cairo_get_matrix(ctx, &matrix);
00284   }
00285 
00286   GeneratePath(ctx, cairoCanvas);
00287 
00288   PRUint16 rule;
00289   if (renderMode != nsISVGRendererCanvas::SVG_RENDER_MODE_NORMAL) {
00290     mSource->GetClipRule(&rule);
00291     cairo_set_matrix(ctx, &matrix);
00292   } else {
00293     mSource->GetFillRule(&rule);
00294   }
00295   if (rule == nsISVGGeometrySource::FILL_RULE_EVENODD)
00296     cairo_set_fill_rule(ctx, CAIRO_FILL_RULE_EVEN_ODD);
00297   else
00298     cairo_set_fill_rule(ctx, CAIRO_FILL_RULE_WINDING);
00299 
00300   if (renderMode != nsISVGRendererCanvas::SVG_RENDER_MODE_NORMAL) {
00301     return NS_OK;
00302   }
00303 
00304   PRUint16 shapeMode;
00305   mSource->GetShapeRendering(&shapeMode);
00306   switch (shapeMode) {
00307   case nsISVGPathGeometrySource::SHAPE_RENDERING_OPTIMIZESPEED:
00308   case nsISVGPathGeometrySource::SHAPE_RENDERING_CRISPEDGES:
00309     cairo_set_antialias(ctx, CAIRO_ANTIALIAS_NONE);
00310     break;
00311   default:
00312     cairo_set_antialias(ctx, CAIRO_ANTIALIAS_DEFAULT);
00313     break;
00314   }
00315 
00316   PRUint16 strokeType, fillType;
00317   PRUint16 strokeServerType = 0;
00318 
00319   PRBool bStroking = PR_FALSE;
00320   mSource->GetStrokePaintType(&strokeType);
00321   if (strokeType != nsISVGGeometrySource::PAINT_TYPE_NONE) {
00322     bStroking = PR_TRUE;
00323     if (strokeType == nsISVGGeometrySource::PAINT_TYPE_SERVER) {
00324       if (NS_FAILED(mSource->GetStrokePaintServerType(&strokeServerType)))
00325         // unknown type or missing frame
00326         bStroking = PR_FALSE;
00327     }
00328   }
00329 
00330   mSource->GetFillPaintType(&fillType);
00331   PRUint16 fillServerType = 0;
00332   if (fillType == nsISVGGeometrySource::PAINT_TYPE_SERVER) {
00333     if (NS_FAILED(mSource->GetFillPaintServerType(&fillServerType)))
00334       // unknown type or missing frame
00335       fillType = nsISVGGeometrySource::PAINT_TYPE_NONE;
00336   }
00337 
00338   if (fillType != nsISVGGeometrySource::PAINT_TYPE_NONE) {
00339     nscolor rgb;
00340     mSource->GetFillPaint(&rgb);
00341     float opacity;
00342     mSource->GetFillOpacity(&opacity);
00343 
00344     cairo_set_source_rgba(ctx,
00345                           NS_GET_R(rgb)/255.0,
00346                           NS_GET_G(rgb)/255.0,
00347                           NS_GET_B(rgb)/255.0,
00348                           opacity);
00349 
00350     if (fillType == nsISVGGeometrySource::PAINT_TYPE_SOLID_COLOR) {
00351       cairo_fill_preserve(ctx);
00352     } else if (fillType == nsISVGGeometrySource::PAINT_TYPE_SERVER) {
00353       if (fillServerType == nsISVGGeometrySource::PAINT_TYPE_GRADIENT) {
00354         nsCOMPtr<nsISVGGradient> aGrad;
00355         mSource->GetFillGradient(getter_AddRefs(aGrad));
00356 
00357         cairo_pattern_t *gradient = CairoGradient(ctx, aGrad, mSource, opacity);
00358         if (gradient) {
00359           cairo_set_source(ctx, gradient);
00360           cairo_fill_preserve(ctx);
00361           cairo_pattern_destroy(gradient);
00362         }
00363       } else {
00364         cairo_fill_preserve(ctx);
00365       }
00366     }
00367 
00368     if (!bStroking)
00369       cairo_new_path(ctx);
00370   }
00371 
00372   if (bStroking) {
00373     SetupStrokeHitGeometry(ctx);
00374 
00375     nscolor rgb;
00376     mSource->GetStrokePaint(&rgb);
00377     float opacity;
00378     mSource->GetStrokeOpacity(&opacity);
00379     cairo_set_source_rgba(ctx,
00380                           NS_GET_R(rgb)/255.0,
00381                           NS_GET_G(rgb)/255.0,
00382                           NS_GET_B(rgb)/255.0,
00383                           opacity);
00384 
00385     if (strokeType == nsISVGGeometrySource::PAINT_TYPE_SOLID_COLOR) {
00386       cairo_stroke(ctx);
00387     } else if (strokeType == nsISVGGeometrySource::PAINT_TYPE_SERVER) {
00388       PRUint16 serverType;
00389       mSource->GetStrokePaintServerType(&serverType);
00390       if (serverType == nsISVGGeometrySource::PAINT_TYPE_GRADIENT) {
00391         nsCOMPtr<nsISVGGradient> aGrad;
00392         mSource->GetStrokeGradient(getter_AddRefs(aGrad));
00393 
00394         cairo_pattern_t *gradient = CairoGradient(ctx, aGrad, mSource, opacity);
00395         if (gradient) {
00396           cairo_set_source(ctx, gradient);
00397           cairo_stroke(ctx);
00398           cairo_pattern_destroy(gradient);
00399         }
00400       } else {
00401         cairo_stroke(ctx);
00402       }
00403     }
00404   }
00405 
00406   cairo_restore(ctx);
00407 
00408   return NS_OK;
00409 }
00410 
00412 NS_IMETHODIMP
00413 nsSVGCairoPathGeometry::Update(PRUint32 updatemask, nsISVGRendererRegion **_retval)
00414 {
00415   *_retval = nsnull;
00416 
00417   const unsigned long pathmask =
00418     nsISVGPathGeometrySource::UPDATEMASK_PATH |
00419     nsISVGGeometrySource::UPDATEMASK_CANVAS_TM;
00420 
00421   const unsigned long fillmask = 
00422     pathmask |
00423     nsISVGGeometrySource::UPDATEMASK_FILL_RULE;
00424 
00425   const unsigned long strokemask =
00426     pathmask |
00427     nsISVGGeometrySource::UPDATEMASK_STROKE_WIDTH       |
00428     nsISVGGeometrySource::UPDATEMASK_STROKE_LINECAP     |
00429     nsISVGGeometrySource::UPDATEMASK_STROKE_LINEJOIN    |
00430     nsISVGGeometrySource::UPDATEMASK_STROKE_MITERLIMIT  |
00431     nsISVGGeometrySource::UPDATEMASK_STROKE_DASH_ARRAY  |
00432     nsISVGGeometrySource::UPDATEMASK_STROKE_DASHOFFSET;
00433 
00434   const unsigned long coveredregionmask =
00435     fillmask                                            |
00436     strokemask                                          |
00437     nsISVGGeometrySource::UPDATEMASK_FILL_PAINT_TYPE    |
00438     nsISVGGeometrySource::UPDATEMASK_STROKE_PAINT_TYPE;
00439 
00440   nsCOMPtr<nsISVGRendererRegion> before = mCoveredRegion;
00441 
00442   if (updatemask & coveredregionmask) {
00443     nsCOMPtr<nsISVGRendererRegion> after;
00444     GetCoveredRegion(getter_AddRefs(after));
00445 
00446     if (mCoveredRegion) {
00447       if (after)
00448         after->Combine(before, _retval);
00449     } else {
00450       *_retval = after;
00451       NS_IF_ADDREF(*_retval);
00452     }
00453     mCoveredRegion = after;
00454   }
00455 
00456   if (!*_retval) {
00457     *_retval = before;
00458     NS_IF_ADDREF(*_retval);
00459   }
00460 
00461   return NS_OK;
00462 }
00463 
00465 NS_IMETHODIMP
00466 nsSVGCairoPathGeometry::GetCoveredRegion(nsISVGRendererRegion **_retval)
00467 {
00468   *_retval = nsnull;
00469 
00470   float width;
00471   PRUint16 paintType;
00472   PRBool hasStroke = PR_FALSE, hasFill = PR_FALSE;
00473 
00474   mSource->GetStrokePaintType(&paintType);
00475   mSource->GetStrokeWidth(&width);
00476   if (paintType != nsISVGGeometrySource::PAINT_TYPE_NONE && width > 0)
00477     hasStroke = PR_TRUE;
00478 
00479   mSource->GetFillPaintType(&paintType);
00480   if (paintType != nsISVGGeometrySource::PAINT_TYPE_NONE)
00481     hasFill = PR_TRUE;
00482 
00483   if (!hasStroke && !hasFill)
00484     return NS_OK;
00485 
00486   cairo_t *ctx = cairo_create(gSVGCairoDummySurface);
00487 
00488   GeneratePath(ctx, nsnull);
00489   SetupStrokeGeometry(ctx);
00490 
00491   double xmin, ymin, xmax, ymax;
00492 
00493   if (hasStroke) {
00494     // need to use user_to_device instead of setting the identity_matrix
00495     // to make sure stroke-width is interpreted in the right coordinate space
00496     cairo_stroke_extents(ctx, &xmin, &ymin, &xmax, &ymax);
00497     nsSVGUtils::UserToDeviceBBox(ctx, &xmin, &ymin, &xmax, &ymax);
00498   } else {
00499     cairo_identity_matrix(ctx);
00500     cairo_fill_extents(ctx, &xmin, &ymin, &xmax, &ymax);
00501   }
00502 
00503   cairo_destroy(ctx);
00504 
00505   return NS_NewSVGCairoRectRegion(_retval, xmin, ymin, xmax-xmin, ymax-ymin);
00506 }
00507 
00509 NS_IMETHODIMP
00510 nsSVGCairoPathGeometry::ContainsPoint(float x, float y, PRBool *_retval)
00511 {
00512   *_retval = PR_FALSE;
00513 
00514   // early reject test
00515   if (mCoveredRegion) {
00516     nsCOMPtr<nsISVGCairoRegion> region = do_QueryInterface(mCoveredRegion);
00517     if (!region->Contains(x,y))
00518       return NS_OK;
00519   }
00520 
00521   cairo_t *ctx = cairo_create(gSVGCairoDummySurface);
00522   cairo_set_tolerance(ctx, 1.0);
00523 
00524   GeneratePath(ctx, nsnull);
00525   double xx = x, yy = y;
00526   cairo_device_to_user(ctx, &xx, &yy);
00527 
00528   PRUint16 rule;
00529   PRBool isClip;
00530   mSource->IsClipChild(&isClip);
00531   if (isClip)
00532     mSource->GetClipRule(&rule);
00533   else
00534     mSource->GetFillRule(&rule);
00535 
00536   if (rule == nsISVGGeometrySource::FILL_RULE_EVENODD)
00537     cairo_set_fill_rule(ctx, CAIRO_FILL_RULE_EVEN_ODD);
00538   else
00539     cairo_set_fill_rule(ctx, CAIRO_FILL_RULE_WINDING);
00540 
00541   PRUint16 mask = 0;
00542   mSource->GetHittestMask(&mask);
00543   if (mask & nsISVGPathGeometrySource::HITTEST_MASK_FILL)
00544     *_retval = cairo_in_fill(ctx, xx, yy);
00545   if (!*_retval && (mask & nsISVGPathGeometrySource::HITTEST_MASK_STROKE)) {
00546     SetupStrokeHitGeometry(ctx);
00547     *_retval = cairo_in_stroke(ctx, xx, yy);
00548   }
00549 
00550   cairo_destroy(ctx);
00551 
00552   return NS_OK;
00553 }
00554 
00555 NS_IMETHODIMP
00556 nsSVGCairoPathGeometry::GetBoundingBox(nsIDOMSVGRect * *aBoundingBox)
00557 {
00558   *aBoundingBox = nsnull;
00559 
00560   nsCOMPtr<nsIDOMSVGRect> rect = do_CreateInstance(NS_SVGRECT_CONTRACTID);
00561 
00562   NS_ASSERTION(rect, "could not create rect");
00563   if (!rect) return NS_ERROR_FAILURE;
00564 
00565   double xmin, ymin, xmax, ymax;
00566 
00567   cairo_t *ctx = cairo_create(gSVGCairoDummySurface);
00568   GeneratePath(ctx, nsnull);
00569 
00570   cairo_identity_matrix(ctx);
00571   cairo_fill_extents(ctx, &xmin, &ymin, &xmax, &ymax);
00572 
00573   /* cairo_fill_extents doesn't work on degenerate paths */
00574   if (xmin ==  32767 &&
00575       ymin ==  32767 &&
00576       xmax == -32768 &&
00577       ymax == -32768) {
00578     /* cairo_stroke_extents doesn't work with stroke width zero, fudge */
00579     cairo_set_line_width(ctx, 0.0001);
00580     cairo_stroke_extents(ctx, &xmin, &ymin, &xmax, &ymax);
00581   }
00582 
00583   cairo_destroy(ctx);
00584 
00585   rect->SetX(xmin);
00586   rect->SetY(ymin);
00587   rect->SetWidth(xmax - xmin);
00588   rect->SetHeight(ymax - ymin);
00589 
00590   *aBoundingBox = rect;
00591   NS_ADDREF(*aBoundingBox);
00592   
00593   return NS_OK;
00594 }
00595 
00596 NS_IMETHODIMP
00597 nsSVGCairoPathGeometry::Flatten(nsSVGPathData **aData)
00598 {
00599   cairo_t *ctx = cairo_create(gSVGCairoDummySurface);
00600   GeneratePath(ctx, nsnull);
00601   cairo_identity_matrix(ctx);
00602 
00603   *aData = new nsSVGPathData;
00604 
00605   cairo_path_t *path;
00606   cairo_path_data_t *data;
00607 
00608   path = cairo_copy_path_flat(ctx);
00609 
00610   for (PRInt32 i = 0; i < path->num_data; i += path->data[i].header.length) {
00611     data = &path->data[i];
00612     switch (data->header.type) {
00613     case CAIRO_PATH_MOVE_TO:
00614       (*aData)->AddPoint(data[1].point.x,
00615                          data[1].point.y,
00616                          NS_SVGPATHFLATTEN_MOVE);
00617       break;
00618     case CAIRO_PATH_LINE_TO:
00619       (*aData)->AddPoint(data[1].point.x,
00620                          data[1].point.y,
00621                          NS_SVGPATHFLATTEN_LINE);
00622       break;
00623     case CAIRO_PATH_CURVE_TO:
00624       /* should never happen with a flattened path */
00625       break;
00626     case CAIRO_PATH_CLOSE_PATH:
00627     {
00628       if (!(*aData)->count)
00629         break;
00630  
00631       /* find beginning of current subpath */
00632       for (PRUint32 k = (*aData)->count - 1; k >= 0; k--)
00633         if ((*aData)->type[k] == NS_SVGPATHFLATTEN_MOVE) {
00634           (*aData)->AddPoint((*aData)->x[k],
00635                              (*aData)->y[k],
00636                              NS_SVGPATHFLATTEN_LINE);
00637           break;
00638         }
00639     }
00640     }
00641   }
00642 
00643   cairo_path_destroy(path);
00644   cairo_destroy(ctx);
00645 
00646   return NS_OK;
00647 }