Back to index

lightning-sunbird  0.9+nobinonly
nsSVGLibartPathGeometry.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  *
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 "nsCOMPtr.h"
00040 #include "nsSVGLibartPathGeometry.h"
00041 #include "nsISVGRendererPathGeometry.h"
00042 #include "nsISVGLibartCanvas.h"
00043 #include "nsIDOMSVGMatrix.h"
00044 #include "nsSVGLibartRegion.h"
00045 #include "nsISVGRendererRegion.h"
00046 #include "nsSVGLibartBPathBuilder.h"
00047 #include "nsSVGLibartGradient.h"
00048 #include "nsISVGRendererPathBuilder.h"
00049 #include "nsISVGPathGeometrySource.h"
00050 #include "nsISVGGradient.h"
00051 #include "nsSVGFill.h"
00052 #include "nsSVGStroke.h"
00053 #include "nsIServiceManager.h"
00054 #include "nsMemory.h"
00055 #include "prdtoa.h"
00056 #include "nsString.h"
00057 #include "nsIDOMSVGRect.h"
00058 #include "nsSVGTypeCIDs.h"
00059 #include "nsIComponentManager.h"
00060 #include "nsISVGPathFlatten.h"
00061 
00062 // comment from art_vpath_path.c: The Adobe PostScript reference
00063 // manual defines flatness as the maximum deviation between any
00064 // point on the vpath approximation and the corresponding point on the
00065 // "true" curve, and we follow this definition here. A value of 0.25
00066 // should ensure high quality for aa rendering.
00067 #define SVG_BEZIER_FLATNESS 0.5
00068 
00073 
00074 
00077 class nsSVGLibartPathGeometry : public nsISVGRendererPathGeometry
00078 {
00079 protected:
00080   friend nsresult NS_NewSVGLibartPathGeometry(nsISVGRendererPathGeometry **result,
00081                                               nsISVGPathGeometrySource *src);
00082 
00083   nsSVGLibartPathGeometry();
00084   ~nsSVGLibartPathGeometry();
00085   nsresult Init(nsISVGPathGeometrySource* src);
00086 
00087 public:
00088   // nsISupports interface:
00089   NS_DECL_ISUPPORTS
00090   
00091   // nsISVGRendererPathGeometry interface:
00092   NS_DECL_NSISVGRENDERERPATHGEOMETRY
00093   
00094 protected:
00095   void ClearPath() { if (mVPath) { art_free(mVPath); mVPath=nsnull; } }
00096   void ClearFill() { mFill.Clear(); }
00097   void ClearStroke() { mStroke.Clear(); }
00098   void ClearCoveredRegion() { mCoveredRegion = nsnull; }
00099   ArtVpath *GetPath();
00100   ArtSVP *GetFill();
00101   ArtSVP *GetStroke();
00102   ArtSVP *GetGradient();
00103   
00104   private:
00105   nsCOMPtr<nsISVGPathGeometrySource> mSource;
00106   nsCOMPtr<nsISVGRendererRegion> mCoveredRegion;
00107 
00108   ArtVpath* mVPath;
00109   nsSVGFill mFill;
00110   nsSVGStroke mStroke;
00111 };
00112 
00115 //----------------------------------------------------------------------
00116 // implementation:
00117 
00118 nsSVGLibartPathGeometry::nsSVGLibartPathGeometry()
00119     : mVPath(nsnull)
00120 {
00121 }
00122 
00123 nsSVGLibartPathGeometry::~nsSVGLibartPathGeometry()
00124 {
00125   ClearPath();
00126 }
00127 
00128 nsresult nsSVGLibartPathGeometry::Init(nsISVGPathGeometrySource* src)
00129 {
00130   mSource = src;
00131   return NS_OK;
00132 }
00133 
00134 
00135 nsresult
00136 NS_NewSVGLibartPathGeometry(nsISVGRendererPathGeometry **result,
00137                             nsISVGPathGeometrySource *src)
00138 {
00139   nsSVGLibartPathGeometry* pg = new nsSVGLibartPathGeometry();
00140   if (!pg) return NS_ERROR_OUT_OF_MEMORY;
00141 
00142   NS_ADDREF(pg);
00143 
00144   nsresult rv = pg->Init(src);
00145 
00146   if (NS_FAILED(rv)) {
00147     NS_RELEASE(pg);
00148     return rv;
00149   }
00150   
00151   *result = pg;
00152   return rv;
00153 }
00154 
00155 //----------------------------------------------------------------------
00156 // nsISupports methods:
00157 
00158 NS_IMPL_ADDREF(nsSVGLibartPathGeometry)
00159 NS_IMPL_RELEASE(nsSVGLibartPathGeometry)
00160 
00161 NS_INTERFACE_MAP_BEGIN(nsSVGLibartPathGeometry)
00162   NS_INTERFACE_MAP_ENTRY(nsISVGRendererPathGeometry)
00163   NS_INTERFACE_MAP_ENTRY(nsISupports)
00164 NS_INTERFACE_MAP_END
00165 
00166 //----------------------------------------------------------------------
00167 
00168 ArtVpath*
00169 nsSVGLibartPathGeometry::GetPath()
00170 {
00171   if (mVPath) return mVPath;
00172 
00173   // 1. construct a bezier path:
00174   ArtBpath*bpath = nsnull;
00175   
00176   nsCOMPtr<nsISVGRendererPathBuilder> builder;
00177   NS_NewSVGLibartBPathBuilder(getter_AddRefs(builder), &bpath);
00178   mSource->ConstructPath(builder);
00179   builder->EndPath();
00180 
00181   // 2. transform the bpath into global coords:
00182   double matrix[6];
00183   {
00184     nsCOMPtr<nsIDOMSVGMatrix> ctm;
00185     mSource->GetCanvasTM(getter_AddRefs(ctm));
00186     NS_ASSERTION(ctm, "graphic source didn't have a ctm");
00187     
00188     float val;
00189     ctm->GetA(&val);
00190     matrix[0] = val;
00191     
00192     ctm->GetB(&val);
00193     matrix[1] = val;
00194     
00195     ctm->GetC(&val);  
00196     matrix[2] = val;  
00197     
00198     ctm->GetD(&val);  
00199     matrix[3] = val;  
00200     
00201     ctm->GetE(&val);
00202     matrix[4] = val;
00203     
00204     ctm->GetF(&val);
00205     matrix[5] = val;
00206   }
00207 
00208   if ( bpath &&
00209        ( matrix[0] != 1.0 || matrix[2] != 0.0 || matrix[4] != 0.0 ||
00210          matrix[1] != 0.0 || matrix[3] != 1.0 || matrix[5] != 0.0 ))
00211   {
00212     ArtBpath* temp = bpath;
00213     bpath = art_bpath_affine_transform(bpath, matrix);
00214     art_free(temp);
00215   }
00216 
00217   // 3. convert the bpath into a vpath:
00218   if (bpath)
00219     mVPath = art_bez_path_to_vec(bpath, SVG_BEZIER_FLATNESS);
00220 
00221   return mVPath;
00222 }
00223 
00224 ArtSVP *
00225 nsSVGLibartPathGeometry::GetFill()
00226 {
00227   if (!mFill.IsEmpty() || !GetPath()) return mFill.GetSvp();
00228   
00229   mFill.Build(GetPath(), mSource);
00230   
00231   return mFill.GetSvp();
00232 }
00233 
00234 ArtSVP *
00235 nsSVGLibartPathGeometry::GetStroke()
00236 {
00237   if (!mStroke.IsEmpty() || !GetPath()) return mStroke.GetSvp();
00238 
00239   mStroke.Build(GetPath(), mSource);
00240   
00241   return mStroke.GetSvp();
00242 }
00243 
00244 //----------------------------------------------------------------------
00245 // nsISVGRendererPathGeometry methods:
00246 
00248 NS_IMETHODIMP
00249 nsSVGLibartPathGeometry::Render(nsISVGRendererCanvas *canvas)
00250 {
00251   nsCOMPtr<nsISVGLibartCanvas> libartCanvas = do_QueryInterface(canvas);
00252   NS_ASSERTION(libartCanvas, "wrong svg render context for geometry!");
00253   if (!libartCanvas) return NS_ERROR_FAILURE;
00254 
00255   PRUint16 type;
00256   
00257   // paint fill:
00258   mSource->GetFillPaintType(&type);
00259 
00260   if (type == nsISVGGeometrySource::PAINT_TYPE_SOLID_COLOR && GetFill()) {
00261     nscolor rgb;
00262     mSource->GetFillPaint(&rgb);
00263     float opacity;
00264     mSource->GetFillOpacity(&opacity);
00265 
00266     ArtColor col;
00267     libartCanvas->GetArtColor(rgb, col);
00268     
00269     ArtRender* render = libartCanvas->NewRender();
00270     NS_ASSERTION(render, "could not create render");
00271 
00272 #ifdef DEBUG_scooter
00273     printf("nsSVGLibartPathGeometry::Render - opacity=%f\n",opacity);
00274 #endif
00275 
00276     art_render_mask_solid(render, (int)(0x10000 * opacity));
00277     art_render_svp(render, GetFill());
00278     art_render_image_solid(render, col);
00279     libartCanvas->InvokeRender(render);
00280   } else if (type == nsISVGGeometrySource::PAINT_TYPE_SERVER && GetFill() ) {
00281     // Handle gradients
00282     nsCOMPtr<nsISVGGradient> aGrad;
00283     mSource->GetFillGradient(getter_AddRefs(aGrad));
00284     float opacity;
00285     mSource->GetFillOpacity(&opacity);
00286     
00287     ArtRender* render = libartCanvas->NewRender();
00288     NS_ASSERTION(render, "could not create render");
00289 
00290     art_render_mask_solid(render, (int)(0x10000 * opacity));
00291     art_render_svp(render, GetFill());
00292 
00293     // Now, get the appropriate gradient fill
00294     nsCOMPtr<nsISVGRendererRegion> region;
00295     GetCoveredRegion(getter_AddRefs(region));
00296     nsCOMPtr<nsISVGLibartRegion> aLibartRegion = do_QueryInterface(region);
00297     nsCOMPtr<nsIDOMSVGMatrix> ctm;
00298     mSource->GetCanvasTM(getter_AddRefs(ctm));
00299     LibartGradient(render, ctm, aGrad, aLibartRegion, mSource);
00300 
00301     // And draw it
00302     libartCanvas->InvokeRender(render);
00303   }
00304 
00305   // paint stroke:
00306   mSource->GetStrokePaintType(&type);
00307   if (type == nsISVGGeometrySource::PAINT_TYPE_SOLID_COLOR && GetStroke()) {
00308     nscolor rgb;
00309     mSource->GetStrokePaint(&rgb);
00310     float opacity;
00311     mSource->GetStrokeOpacity(&opacity);
00312 
00313     ArtColor col;
00314     libartCanvas->GetArtColor(rgb, col);
00315     
00316     ArtRender* render = libartCanvas->NewRender();
00317     NS_ASSERTION(render, "could not create render");
00318 
00319     art_render_mask_solid(render, (int)(0x10000 * opacity));
00320     art_render_svp(render, GetStroke());
00321     art_render_image_solid(render, col);
00322     libartCanvas->InvokeRender(render);
00323   } else if (type == nsISVGGeometrySource::PAINT_TYPE_SERVER && GetStroke()) {
00324     // Handle gradients
00325     nsCOMPtr<nsISVGGradient> aGrad;
00326     mSource->GetStrokeGradient(getter_AddRefs(aGrad));
00327     float opacity;
00328     mSource->GetStrokeOpacity(&opacity);
00329     
00330     ArtRender* render = libartCanvas->NewRender();
00331     NS_ASSERTION(render, "could not create render");
00332 
00333     art_render_mask_solid(render, (int)(0x10000 * opacity));
00334     art_render_svp(render, GetStroke());
00335 
00336     // Now, get the appropriate gradient fill
00337     nsCOMPtr<nsISVGRendererRegion> region;
00338     GetCoveredRegion(getter_AddRefs(region));
00339     nsCOMPtr<nsISVGLibartRegion> aLibartRegion = do_QueryInterface(region);
00340     nsCOMPtr<nsIDOMSVGMatrix> ctm;
00341     mSource->GetCanvasTM(getter_AddRefs(ctm));
00342     LibartGradient(render, ctm, aGrad, aLibartRegion, mSource);
00343 
00344     // And draw it
00345     libartCanvas->InvokeRender(render);
00346   }
00347   
00348   return NS_OK;
00349 }
00350 
00352 NS_IMETHODIMP
00353 nsSVGLibartPathGeometry::Update(PRUint32 updatemask, nsISVGRendererRegion **_retval)
00354 {
00355   *_retval = nsnull;
00356 
00357   const unsigned long pathmask =
00358     nsISVGPathGeometrySource::UPDATEMASK_PATH |
00359     nsISVGGeometrySource::UPDATEMASK_CANVAS_TM;
00360 
00361   const unsigned long fillmask = 
00362     pathmask |
00363     nsISVGGeometrySource::UPDATEMASK_FILL_RULE;
00364 
00365   const unsigned long strokemask =
00366     pathmask |
00367     nsISVGGeometrySource::UPDATEMASK_STROKE_WIDTH       |
00368     nsISVGGeometrySource::UPDATEMASK_STROKE_LINECAP     |
00369     nsISVGGeometrySource::UPDATEMASK_STROKE_LINEJOIN    |
00370     nsISVGGeometrySource::UPDATEMASK_STROKE_MITERLIMIT  |
00371     nsISVGGeometrySource::UPDATEMASK_STROKE_DASH_ARRAY  |
00372     nsISVGGeometrySource::UPDATEMASK_STROKE_DASHOFFSET;
00373 
00374   const unsigned long coveredregionmask =
00375     fillmask                                            |
00376     strokemask                                          |
00377     nsISVGGeometrySource::UPDATEMASK_FILL_PAINT_TYPE    |
00378     nsISVGGeometrySource::UPDATEMASK_STROKE_PAINT_TYPE;
00379   
00380   nsCOMPtr<nsISVGRendererRegion> before;
00381   // only obtain the 'before' region if we have built a path before:
00382   if (!mFill.IsEmpty() || !mStroke.IsEmpty())
00383     GetCoveredRegion(getter_AddRefs(before));
00384 
00385   if ((updatemask & pathmask)!=0){
00386     ClearPath();
00387   }
00388   if ((updatemask & fillmask)!=0)
00389     ClearFill();
00390   if ((updatemask & strokemask)!=0)
00391     ClearStroke();
00392   if ((updatemask & coveredregionmask)!=0) {
00393     ClearCoveredRegion();
00394     nsCOMPtr<nsISVGRendererRegion> after;
00395     GetCoveredRegion(getter_AddRefs(after));
00396     if (after)
00397       after->Combine(before, _retval);
00398   }
00399 
00400   if (!*_retval) {
00401     *_retval = before;
00402     NS_IF_ADDREF(*_retval);
00403   }
00404 
00405   return NS_OK;
00406 }
00407 
00409 NS_IMETHODIMP
00410 nsSVGLibartPathGeometry::GetCoveredRegion(nsISVGRendererRegion **_retval)
00411 {
00412   *_retval = nsnull;
00413   
00414   if (mCoveredRegion) {
00415     *_retval = mCoveredRegion;
00416     NS_ADDREF(*_retval);
00417     return NS_OK;
00418   }
00419   
00420   PRUint16 type;  
00421   mSource->GetFillPaintType(&type);
00422   bool hasCoveredFill = (type!=nsISVGGeometrySource::PAINT_TYPE_NONE) && GetFill();
00423   
00424   mSource->GetStrokePaintType(&type);
00425   bool hasCoveredStroke = (type!=nsISVGGeometrySource::PAINT_TYPE_NONE) && GetStroke();
00426 
00427   if (!hasCoveredFill && !hasCoveredStroke) return NS_OK;
00428 
00429   if (hasCoveredFill) {
00430     nsCOMPtr<nsISVGRendererRegion> reg1;
00431     NS_NewSVGLibartSVPRegion(getter_AddRefs(reg1), GetFill());
00432     if (hasCoveredStroke) {
00433       nsCOMPtr<nsISVGRendererRegion> reg2;
00434       NS_NewSVGLibartSVPRegion(getter_AddRefs(reg2), GetStroke());
00435       reg1->Combine(reg2, _retval);
00436     }
00437     else {
00438       *_retval = reg1;
00439       NS_ADDREF(*_retval);
00440     }
00441   } // covered stroke only
00442   else
00443     NS_NewSVGLibartSVPRegion(_retval, GetStroke());
00444 
00445   mCoveredRegion = *_retval;
00446   return NS_OK;
00447 }
00448 
00450 NS_IMETHODIMP
00451 nsSVGLibartPathGeometry::ContainsPoint(float x, float y, PRBool *_retval)
00452 {
00453   *_retval = PR_FALSE;
00454 
00455   PRUint16 mask;
00456   mSource->GetHittestMask(&mask);
00457 
00458   if (mask & nsISVGPathGeometrySource::HITTEST_MASK_FILL &&
00459       GetFill() &&
00460       mFill.Contains(x,y)) {
00461     *_retval = PR_TRUE;
00462     return NS_OK;
00463   }
00464   if (mask & nsISVGPathGeometrySource::HITTEST_MASK_STROKE &&
00465       GetStroke() &&
00466       mStroke.Contains(x,y)) {
00467     *_retval = PR_TRUE;
00468   }
00469   
00470   return NS_OK;
00471 }
00472 
00473 NS_IMETHODIMP
00474 nsSVGLibartPathGeometry::GetBoundingBox(nsIDOMSVGRect * *aBoundingBox)
00475 {
00476   *aBoundingBox = nsnull;
00477 
00478   ArtSVP *path = GetFill();
00479   if (!path)
00480     return NS_ERROR_FAILURE;
00481 
00482   nsCOMPtr<nsIDOMSVGRect> rect = do_CreateInstance(NS_SVGRECT_CONTRACTID);
00483 
00484   NS_ASSERTION(rect, "could not create rect");
00485   if (!rect) return NS_ERROR_FAILURE;
00486 
00487   ArtDRect bound;
00488   art_drect_svp(&bound, path);
00489 
00490   rect->SetX(bound.x0);
00491   rect->SetY(bound.y0);
00492   rect->SetWidth(bound.x1 - bound.x0);
00493   rect->SetHeight(bound.y1 - bound.y0);
00494 
00495   *aBoundingBox = rect;
00496   NS_ADDREF(*aBoundingBox);
00497   
00498   return NS_OK;
00499 
00500 }
00501 
00502 NS_IMETHODIMP
00503 nsSVGLibartPathGeometry::Flatten(nsSVGPathData **aData)
00504 {
00505   *aData = nsnull;
00506 
00507   return NS_ERROR_NOT_IMPLEMENTED;
00508 }