Back to index

lightning-sunbird  0.9+nobinonly
nsSVGCairoGradient.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/nsSVGCairoGradient.cpp
00025  *
00026  * Contributor(s):
00027  *
00028  * Alternatively, the contents of this file may be used under the terms of
00029  * either of the GNU General Public License Version 2 or later (the "GPL"),
00030  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00031  * in which case the provisions of the GPL or the LGPL are applicable instead
00032  * of those above. If you wish to allow use of your version of this file only
00033  * under the terms of either the GPL or the LGPL, and not to allow others to
00034  * use your version of this file under the terms of the MPL, indicate your
00035  * decision by deleting the provisions above and replace them with the notice
00036  * and other provisions required by the GPL or the LGPL. If you do not delete
00037  * the provisions above, a recipient may use your version of this file under
00038  * the terms of any one of the MPL, the GPL or the LGPL.
00039  *
00040  * ***** END LICENSE BLOCK ***** */
00041 
00042 #include <math.h>
00043 
00044 #include "nsCOMPtr.h"
00045 #include "nsIDOMSVGMatrix.h"
00046 #include "nsIDOMSVGGradientElement.h"
00047 #include "nsISVGPathGeometrySource.h"
00048 #include "nsSVGCairoGradient.h"
00049 
00050 
00051 static cairo_matrix_t SVGToMatrix(nsIDOMSVGMatrix *ctm)
00052 {
00053   float A, B, C, D, E, F;
00054   ctm->GetA(&A);
00055   ctm->GetB(&B);
00056   ctm->GetC(&C);
00057   ctm->GetD(&D);
00058   ctm->GetE(&E);
00059   ctm->GetF(&F);
00060   cairo_matrix_t matrix = { A, B, C, D, E, F };
00061   return matrix;
00062 }
00063 
00064 
00065 static void
00066 CairoSetStops(cairo_pattern_t *aPattern, nsISVGGradient *aGrad, float aOpacity)
00067 {
00068   PRUint32 nStops;
00069   float lastOffset = 0.0f;
00070 
00071   aGrad->GetStopCount(&nStops);
00072   for (PRUint32 i = 0; i < nStops; i++) {
00073     nscolor rgba;
00074     float offset;
00075     float opacity;
00076 
00077     aGrad->GetStopOffset(i, &offset);
00078     aGrad->GetStopColor(i, &rgba);
00079     aGrad->GetStopOpacity(i, &opacity);
00080 
00081     if (offset < lastOffset)
00082       offset = lastOffset;
00083     else
00084       lastOffset = offset;
00085 
00086     cairo_pattern_add_color_stop_rgba(aPattern, offset,
00087                                       NS_GET_R(rgba)/255.0,
00088                                       NS_GET_G(rgba)/255.0,
00089                                       NS_GET_B(rgba)/255.0,
00090                                       opacity * aOpacity);
00091   }
00092 }
00093 
00094 
00095 static cairo_pattern_t *
00096 CairoLinearGradient(cairo_t *ctx, nsISVGGradient *aGrad)
00097 {
00098   float fX1, fY1, fX2, fY2;
00099     
00100   nsCOMPtr<nsISVGLinearGradient>aLgrad = do_QueryInterface(aGrad);
00101   NS_ASSERTION(aLgrad, "error gradient did not provide a Linear Gradient interface");
00102   
00103   aLgrad->GetX1(&fX1);
00104   aLgrad->GetX2(&fX2);
00105   aLgrad->GetY1(&fY1);
00106   aLgrad->GetY2(&fY2);
00107   
00108   return cairo_pattern_create_linear(fX1, fY1, fX2, fY2);
00109 }
00110 
00111 
00112 static cairo_pattern_t *
00113 CairoRadialGradient(cairo_t *ctx, nsISVGGradient *aGrad)
00114 {
00115   float fCx, fCy, fR, fFx, fFy;
00116 
00117   // Get the Radial Gradient interface
00118   nsCOMPtr<nsISVGRadialGradient>aRgrad = do_QueryInterface(aGrad);
00119   NS_ASSERTION(aRgrad, "error gradient did not provide a Linear Gradient interface");
00120 
00121   aRgrad->GetCx(&fCx);
00122   aRgrad->GetCy(&fCy);
00123   aRgrad->GetR(&fR);
00124   aRgrad->GetFx(&fFx);
00125   aRgrad->GetFy(&fFy);
00126 
00127   if (fFx != fCx || fFy != fCy) {
00128     // The focal point (fFx and fFy) must be clamped to be *inside* - not on -
00129     // the circumference of the gradient or we'll get rendering anomalies. We
00130     // calculate the distance from the focal point to the gradient center and
00131     // make sure it is *less* than the gradient radius. 0.999 is used as the
00132     // factor of the radius because it's close enough to 1 that we won't get a
00133     // fringe at the edge of the gradient if we clamp, but not so close to 1
00134     // that rounding error will give us the same results as using fR itself.
00135     double dMax = 0.999 * fR;
00136     float dx = fFx - fCx;
00137     float dy = fFy - fCy;
00138     double d = sqrt((dx * dx) + (dy * dy));
00139     if (d > dMax) {
00140       double angle = atan2(dy, dx);
00141       fFx = (float)(dMax * cos(angle)) + fCx;
00142       fFy = (float)(dMax * sin(angle)) + fCy;
00143     }
00144   }
00145 
00146   return cairo_pattern_create_radial(fFx, fFy, 0, fCx, fCy, fR);
00147 }
00148 
00149 
00150 cairo_pattern_t *
00151 CairoGradient(cairo_t *ctx, nsISVGGradient *aGrad,
00152               nsISVGGeometrySource *aSource, float aOpacity)
00153 {
00154   NS_ASSERTION(aGrad, "Called CairoGradient without a gradient!");
00155   if (!aGrad)
00156     return nsnull;
00157 
00158   // Get the transform list (if there is one)
00159   nsCOMPtr<nsIDOMSVGMatrix> svgMatrix;
00160   aGrad->GetGradientTransform(getter_AddRefs(svgMatrix), aSource);
00161   NS_ASSERTION(svgMatrix, "CairoGradient: GetGradientTransform returns null");
00162 
00163   cairo_matrix_t patternMatrix = SVGToMatrix(svgMatrix);
00164   if (cairo_matrix_invert(&patternMatrix)) {
00165     return nsnull;
00166   }
00167 
00168   cairo_pattern_t *gradient;
00169 
00170   // Linear or Radial?
00171   PRUint32 type;
00172   aGrad->GetGradientType(&type);
00173   if (type == nsISVGGradient::SVG_LINEAR_GRADIENT)
00174     gradient = CairoLinearGradient(ctx, aGrad);
00175   else if (type == nsISVGGradient::SVG_RADIAL_GRADIENT)
00176     gradient = CairoRadialGradient(ctx, aGrad);
00177   else 
00178     return nsnull; // Shouldn't happen
00179 
00180   PRUint16 aSpread;
00181   aGrad->GetSpreadMethod(&aSpread);
00182   // We don't call cairo_pattern_set_extend for SVG_SPREADMETHOD_PAD because
00183   // pad is cairo's default, and the explicit argument used for pad changed
00184   // from CAIRO_EXTEND_NONE to CAIRO_EXTEND_PAD between cairo 1.0 and 1.2. See
00185   // bug 358930.
00186   if (aSpread == nsIDOMSVGGradientElement::SVG_SPREADMETHOD_REFLECT)
00187     cairo_pattern_set_extend(gradient, CAIRO_EXTEND_REFLECT);
00188   else if (aSpread == nsIDOMSVGGradientElement::SVG_SPREADMETHOD_REPEAT)
00189     cairo_pattern_set_extend(gradient, CAIRO_EXTEND_REPEAT);
00190   
00191   cairo_pattern_set_matrix(gradient, &patternMatrix);
00192 
00193   CairoSetStops(gradient, aGrad, aOpacity);
00194 
00195   return gradient;
00196 }