/* -*- 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) 2001 * 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 "nsCOMPtr.h" #include "nsSVGLibartPathGeometry.h" #include "nsISVGRendererPathGeometry.h" #include "nsISVGLibartCanvas.h" #include "nsIDOMSVGMatrix.h" #include "nsSVGLibartRegion.h" #include "nsISVGRendererRegion.h" #include "nsSVGLibartBPathBuilder.h" #include "nsSVGLibartGradient.h" #include "nsISVGRendererPathBuilder.h" #include "nsISVGPathGeometrySource.h" #include "nsISVGGradient.h" #include "nsSVGFill.h" #include "nsSVGStroke.h" #include "nsIServiceManager.h" #include "nsMemory.h" #include "prdtoa.h" #include "nsString.h" #include "nsIDOMSVGRect.h" #include "nsSVGTypeCIDs.h" #include "nsIComponentManager.h" #include "nsISVGPathFlatten.h" // comment from art_vpath_path.c: The Adobe PostScript reference // manual defines flatness as the maximum deviation between any // point on the vpath approximation and the corresponding point on the // "true" curve, and we follow this definition here. A value of 0.25 // should ensure high quality for aa rendering. #define SVG_BEZIER_FLATNESS 0.5 /** * \addtogroup libart_renderer Libart Rendering Engine * @{ */ //////////////////////////////////////////////////////////////////////// /** * Libart path geometry implementation */ class nsSVGLibartPathGeometry : public nsISVGRendererPathGeometry { protected: friend nsresult NS_NewSVGLibartPathGeometry(nsISVGRendererPathGeometry **result, nsISVGPathGeometrySource *src); nsSVGLibartPathGeometry(); ~nsSVGLibartPathGeometry(); nsresult Init(nsISVGPathGeometrySource* src); public: // nsISupports interface: NS_DECL_ISUPPORTS // nsISVGRendererPathGeometry interface: NS_DECL_NSISVGRENDERERPATHGEOMETRY protected: void ClearPath() { if (mVPath) { art_free(mVPath); mVPath=nsnull; } } void ClearFill() { mFill.Clear(); } void ClearStroke() { mStroke.Clear(); } void ClearCoveredRegion() { mCoveredRegion = nsnull; } ArtVpath *GetPath(); ArtSVP *GetFill(); ArtSVP *GetStroke(); ArtSVP *GetGradient(); private: nsCOMPtr mSource; nsCOMPtr mCoveredRegion; ArtVpath* mVPath; nsSVGFill mFill; nsSVGStroke mStroke; }; /** @} */ //---------------------------------------------------------------------- // implementation: nsSVGLibartPathGeometry::nsSVGLibartPathGeometry() : mVPath(nsnull) { } nsSVGLibartPathGeometry::~nsSVGLibartPathGeometry() { ClearPath(); } nsresult nsSVGLibartPathGeometry::Init(nsISVGPathGeometrySource* src) { mSource = src; return NS_OK; } nsresult NS_NewSVGLibartPathGeometry(nsISVGRendererPathGeometry **result, nsISVGPathGeometrySource *src) { nsSVGLibartPathGeometry* pg = new nsSVGLibartPathGeometry(); if (!pg) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(pg); nsresult rv = pg->Init(src); if (NS_FAILED(rv)) { NS_RELEASE(pg); return rv; } *result = pg; return rv; } //---------------------------------------------------------------------- // nsISupports methods: NS_IMPL_ADDREF(nsSVGLibartPathGeometry) NS_IMPL_RELEASE(nsSVGLibartPathGeometry) NS_INTERFACE_MAP_BEGIN(nsSVGLibartPathGeometry) NS_INTERFACE_MAP_ENTRY(nsISVGRendererPathGeometry) NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END //---------------------------------------------------------------------- ArtVpath* nsSVGLibartPathGeometry::GetPath() { if (mVPath) return mVPath; // 1. construct a bezier path: ArtBpath*bpath = nsnull; nsCOMPtr builder; NS_NewSVGLibartBPathBuilder(getter_AddRefs(builder), &bpath); mSource->ConstructPath(builder); builder->EndPath(); // 2. transform the bpath into global coords: double matrix[6]; { nsCOMPtr ctm; mSource->GetCanvasTM(getter_AddRefs(ctm)); NS_ASSERTION(ctm, "graphic source didn't have a ctm"); float val; ctm->GetA(&val); matrix[0] = val; ctm->GetB(&val); matrix[1] = val; ctm->GetC(&val); matrix[2] = val; ctm->GetD(&val); matrix[3] = val; ctm->GetE(&val); matrix[4] = val; ctm->GetF(&val); matrix[5] = val; } if ( bpath && ( matrix[0] != 1.0 || matrix[2] != 0.0 || matrix[4] != 0.0 || matrix[1] != 0.0 || matrix[3] != 1.0 || matrix[5] != 0.0 )) { ArtBpath* temp = bpath; bpath = art_bpath_affine_transform(bpath, matrix); art_free(temp); } // 3. convert the bpath into a vpath: if (bpath) mVPath = art_bez_path_to_vec(bpath, SVG_BEZIER_FLATNESS); return mVPath; } ArtSVP * nsSVGLibartPathGeometry::GetFill() { if (!mFill.IsEmpty() || !GetPath()) return mFill.GetSvp(); mFill.Build(GetPath(), mSource); return mFill.GetSvp(); } ArtSVP * nsSVGLibartPathGeometry::GetStroke() { if (!mStroke.IsEmpty() || !GetPath()) return mStroke.GetSvp(); mStroke.Build(GetPath(), mSource); return mStroke.GetSvp(); } //---------------------------------------------------------------------- // nsISVGRendererPathGeometry methods: /** Implements void render(in nsISVGRendererCanvas canvas); */ NS_IMETHODIMP nsSVGLibartPathGeometry::Render(nsISVGRendererCanvas *canvas) { nsCOMPtr libartCanvas = do_QueryInterface(canvas); NS_ASSERTION(libartCanvas, "wrong svg render context for geometry!"); if (!libartCanvas) return NS_ERROR_FAILURE; PRUint16 type; // paint fill: mSource->GetFillPaintType(&type); if (type == nsISVGGeometrySource::PAINT_TYPE_SOLID_COLOR && GetFill()) { nscolor rgb; mSource->GetFillPaint(&rgb); float opacity; mSource->GetFillOpacity(&opacity); ArtColor col; libartCanvas->GetArtColor(rgb, col); ArtRender* render = libartCanvas->NewRender(); NS_ASSERTION(render, "could not create render"); #ifdef DEBUG_scooter printf("nsSVGLibartPathGeometry::Render - opacity=%f\n",opacity); #endif art_render_mask_solid(render, (int)(0x10000 * opacity)); art_render_svp(render, GetFill()); art_render_image_solid(render, col); libartCanvas->InvokeRender(render); } else if (type == nsISVGGeometrySource::PAINT_TYPE_SERVER && GetFill() ) { // Handle gradients nsCOMPtr aGrad; mSource->GetFillGradient(getter_AddRefs(aGrad)); float opacity; mSource->GetFillOpacity(&opacity); ArtRender* render = libartCanvas->NewRender(); NS_ASSERTION(render, "could not create render"); art_render_mask_solid(render, (int)(0x10000 * opacity)); art_render_svp(render, GetFill()); // Now, get the appropriate gradient fill nsCOMPtr region; GetCoveredRegion(getter_AddRefs(region)); nsCOMPtr aLibartRegion = do_QueryInterface(region); nsCOMPtr ctm; mSource->GetCanvasTM(getter_AddRefs(ctm)); LibartGradient(render, ctm, aGrad, aLibartRegion, mSource); // And draw it libartCanvas->InvokeRender(render); } // paint stroke: mSource->GetStrokePaintType(&type); if (type == nsISVGGeometrySource::PAINT_TYPE_SOLID_COLOR && GetStroke()) { nscolor rgb; mSource->GetStrokePaint(&rgb); float opacity; mSource->GetStrokeOpacity(&opacity); ArtColor col; libartCanvas->GetArtColor(rgb, col); ArtRender* render = libartCanvas->NewRender(); NS_ASSERTION(render, "could not create render"); art_render_mask_solid(render, (int)(0x10000 * opacity)); art_render_svp(render, GetStroke()); art_render_image_solid(render, col); libartCanvas->InvokeRender(render); } else if (type == nsISVGGeometrySource::PAINT_TYPE_SERVER && GetStroke()) { // Handle gradients nsCOMPtr aGrad; mSource->GetStrokeGradient(getter_AddRefs(aGrad)); float opacity; mSource->GetStrokeOpacity(&opacity); ArtRender* render = libartCanvas->NewRender(); NS_ASSERTION(render, "could not create render"); art_render_mask_solid(render, (int)(0x10000 * opacity)); art_render_svp(render, GetStroke()); // Now, get the appropriate gradient fill nsCOMPtr region; GetCoveredRegion(getter_AddRefs(region)); nsCOMPtr aLibartRegion = do_QueryInterface(region); nsCOMPtr ctm; mSource->GetCanvasTM(getter_AddRefs(ctm)); LibartGradient(render, ctm, aGrad, aLibartRegion, mSource); // And draw it libartCanvas->InvokeRender(render); } return NS_OK; } /** Implements nsISVGRendererRegion update(in unsigned long updatemask); */ NS_IMETHODIMP nsSVGLibartPathGeometry::Update(PRUint32 updatemask, nsISVGRendererRegion **_retval) { *_retval = nsnull; const unsigned long pathmask = nsISVGPathGeometrySource::UPDATEMASK_PATH | nsISVGGeometrySource::UPDATEMASK_CANVAS_TM; const unsigned long fillmask = pathmask | nsISVGGeometrySource::UPDATEMASK_FILL_RULE; const unsigned long strokemask = pathmask | nsISVGGeometrySource::UPDATEMASK_STROKE_WIDTH | nsISVGGeometrySource::UPDATEMASK_STROKE_LINECAP | nsISVGGeometrySource::UPDATEMASK_STROKE_LINEJOIN | nsISVGGeometrySource::UPDATEMASK_STROKE_MITERLIMIT | nsISVGGeometrySource::UPDATEMASK_STROKE_DASH_ARRAY | nsISVGGeometrySource::UPDATEMASK_STROKE_DASHOFFSET; const unsigned long coveredregionmask = fillmask | strokemask | nsISVGGeometrySource::UPDATEMASK_FILL_PAINT_TYPE | nsISVGGeometrySource::UPDATEMASK_STROKE_PAINT_TYPE; nsCOMPtr before; // only obtain the 'before' region if we have built a path before: if (!mFill.IsEmpty() || !mStroke.IsEmpty()) GetCoveredRegion(getter_AddRefs(before)); if ((updatemask & pathmask)!=0){ ClearPath(); } if ((updatemask & fillmask)!=0) ClearFill(); if ((updatemask & strokemask)!=0) ClearStroke(); if ((updatemask & coveredregionmask)!=0) { ClearCoveredRegion(); nsCOMPtr after; GetCoveredRegion(getter_AddRefs(after)); if (after) after->Combine(before, _retval); } if (!*_retval) { *_retval = before; NS_IF_ADDREF(*_retval); } return NS_OK; } /** Implements nsISVGRendererRegion getCoveredRegion(); */ NS_IMETHODIMP nsSVGLibartPathGeometry::GetCoveredRegion(nsISVGRendererRegion **_retval) { *_retval = nsnull; if (mCoveredRegion) { *_retval = mCoveredRegion; NS_ADDREF(*_retval); return NS_OK; } PRUint16 type; mSource->GetFillPaintType(&type); bool hasCoveredFill = (type!=nsISVGGeometrySource::PAINT_TYPE_NONE) && GetFill(); mSource->GetStrokePaintType(&type); bool hasCoveredStroke = (type!=nsISVGGeometrySource::PAINT_TYPE_NONE) && GetStroke(); if (!hasCoveredFill && !hasCoveredStroke) return NS_OK; if (hasCoveredFill) { nsCOMPtr reg1; NS_NewSVGLibartSVPRegion(getter_AddRefs(reg1), GetFill()); if (hasCoveredStroke) { nsCOMPtr reg2; NS_NewSVGLibartSVPRegion(getter_AddRefs(reg2), GetStroke()); reg1->Combine(reg2, _retval); } else { *_retval = reg1; NS_ADDREF(*_retval); } } // covered stroke only else NS_NewSVGLibartSVPRegion(_retval, GetStroke()); mCoveredRegion = *_retval; return NS_OK; } /** Implements boolean containsPoint(in float x, in float y); */ NS_IMETHODIMP nsSVGLibartPathGeometry::ContainsPoint(float x, float y, PRBool *_retval) { *_retval = PR_FALSE; PRUint16 mask; mSource->GetHittestMask(&mask); if (mask & nsISVGPathGeometrySource::HITTEST_MASK_FILL && GetFill() && mFill.Contains(x,y)) { *_retval = PR_TRUE; return NS_OK; } if (mask & nsISVGPathGeometrySource::HITTEST_MASK_STROKE && GetStroke() && mStroke.Contains(x,y)) { *_retval = PR_TRUE; } return NS_OK; } NS_IMETHODIMP nsSVGLibartPathGeometry::GetBoundingBox(nsIDOMSVGRect * *aBoundingBox) { *aBoundingBox = nsnull; ArtSVP *path = GetFill(); if (!path) return NS_ERROR_FAILURE; nsCOMPtr rect = do_CreateInstance(NS_SVGRECT_CONTRACTID); NS_ASSERTION(rect, "could not create rect"); if (!rect) return NS_ERROR_FAILURE; ArtDRect bound; art_drect_svp(&bound, path); rect->SetX(bound.x0); rect->SetY(bound.y0); rect->SetWidth(bound.x1 - bound.x0); rect->SetHeight(bound.y1 - bound.y0); *aBoundingBox = rect; NS_ADDREF(*aBoundingBox); return NS_OK; } NS_IMETHODIMP nsSVGLibartPathGeometry::Flatten(nsSVGPathData **aData) { *aData = nsnull; return NS_ERROR_NOT_IMPLEMENTED; }