Back to index

lightning-sunbird  0.9+nobinonly
nsSVGLibartGradient.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  *   Scooter Morris <scootermorris@comcast.net> (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 <math.h>
00040 
00041 #include "nsCOMPtr.h"
00042 #include "nsIDOMSVGMatrix.h"
00043 #include "nsIDOMSVGGradientElement.h"
00044 #include "nsIDOMSVGTransformList.h"
00045 #include "nsISVGPathGeometrySource.h"
00046 #include "nsISVGLibartRegion.h"
00047 #include "nsSVGLibartGradient.h"
00048 #include "prdtoa.h"
00049 #include "nsString.h"
00050 
00051 #define EPSILON 1e-6
00052 
00053 static void SVGToMatrix(nsIDOMSVGMatrix *ctm, double matrix[])
00054 {
00055   float val;
00056   ctm->GetA(&val);
00057   matrix[0] = val;
00058     
00059   ctm->GetB(&val);
00060   matrix[1] = val;
00061   
00062   ctm->GetC(&val);  
00063   matrix[2] = val;  
00064   
00065   ctm->GetD(&val);  
00066   matrix[3] = val;  
00067   
00068   ctm->GetE(&val);
00069   matrix[4] = val;
00070 
00071   ctm->GetF(&val);
00072   matrix[5] = val;
00073 }
00074 
00075 // Calculate the bounding box for this gradient
00076 static void GetBounds(nsISVGLibartRegion *aRegion, ArtIRect *rect)
00077 {
00078   ArtUta *aUta = aRegion->GetUta();
00079   int nRects = 0;
00080   ArtIRect *rectList = art_rect_list_from_uta(aUta, 200, 200, &nRects);
00081   rect->x0 = rectList[0].x0;
00082   rect->x1 = rectList[0].x1;
00083   rect->y0 = rectList[0].y0;
00084   rect->y1 = rectList[0].y1;
00085   for (int i = 1; i < nRects; i++) 
00086   {
00087     if (rectList[i].x0 < rect->x0)
00088       rect->x0 = rectList[i].x0;
00089     if (rectList[i].y0 < rect->y0)
00090       rect->y0 = rectList[i].y0;
00091     if (rectList[i].x1 > rect->x1)
00092       rect->x1 = rectList[i].x1;
00093     if (rectList[i].y1 > rect->y1)
00094       rect->y1 = rectList[i].y1;
00095   }
00096 
00097   art_free(rectList);
00098 }
00099 
00100 static ArtGradientStop *
00101 GetStops(nsISVGGradient *aGrad) {
00102   PRUint32 nStops;
00103   aGrad->GetStopCount(&nStops);
00104   ArtGradientStop *stops = art_new(ArtGradientStop, nStops);
00105   for (PRUint32 i = 0; i < nStops; i++)
00106   {
00107     // Get the stops into the stop array
00108     nscolor rgba;
00109     float offset;
00110 
00111     // ##stops[i].offset = rstops->stop[i].offset;
00112     aGrad->GetStopOffset(i, &offset);
00113     stops[i].offset = offset;
00114 
00115     aGrad->GetStopColor(i, &rgba);
00116 
00117     stops[i].color[0] = ART_PIX_MAX_FROM_8(NS_GET_R(rgba));
00118     stops[i].color[1] = ART_PIX_MAX_FROM_8(NS_GET_G(rgba));
00119     stops[i].color[2] = ART_PIX_MAX_FROM_8(NS_GET_B(rgba));
00120 
00121     // now get the opacity
00122     float opacity;
00123     aGrad->GetStopOpacity(i, &opacity);
00124     stops[i].color[3] = ART_PIX_MAX_FROM_8((int)(opacity*NS_GET_A(rgba)));
00125   }
00126   return stops;
00127 }
00128 
00129 static void
00130 LibartLinearGradient(ArtRender *render, nsISVGGradient *aGrad, double *affine)
00131 {
00132   ArtGradientLinear *agl = nsnull;
00133   double x1, y1, x2, y2;
00134   double dx, dy, scale;
00135   float fX1, fY1, fX2, fY2;
00136   PRUint32 nStops = 0;
00137 
00138   agl = art_new (ArtGradientLinear, 1);
00139   aGrad->GetStopCount(&nStops);
00140   NS_ASSERTION(nStops > 0, "no stops for gradient");
00141   if (nStops == 0)
00142     return;
00143   agl->n_stops = nStops;
00144   agl->stops = GetStops(aGrad);
00145 
00146 #ifdef DEBUG_scooter
00147   printf("In LibartLinearGradient\n");
00148   printf("Stop count: %d\n", agl->n_stops);
00149 #endif
00150 
00151   // Get the Linear Gradient interface
00152   nsCOMPtr<nsISVGLinearGradient>aLgrad = do_QueryInterface(aGrad);
00153   NS_ASSERTION(aLgrad, "error gradient did not provide a Linear Gradient interface");
00154 
00155   // Get the gradient vector
00156   aLgrad->GetX1(&fX1);
00157   aLgrad->GetX2(&fX2);
00158   aLgrad->GetY1(&fY1);
00159   aLgrad->GetY2(&fY2);
00160 
00161   // Convert to double
00162   x1 = fX1; y1 = fY1; x2 = fX2; y2 = fY2;
00163 
00164   // Convert to pixel space 
00165   float x = x1 * affine[0] + y1 * affine[2] + affine[4];
00166   float y = x1 * affine[1] + y1 * affine[3] + affine[5];
00167   x1 = x; y1 = y;
00168   x = x2 * affine[0] + y2 * affine[2] + affine[4];
00169   y = x2 * affine[1] + y2 * affine[3] + affine[5];
00170   x2 = x; y2 = y;
00171 
00172   // solve a, b, c so ax1 + by1 + c = 0 and ax2 + by2 + c = 1, maximum
00173   // gradient is in x1,y1 to x2,y2 dir
00174   dx = x2 - x1;
00175   dy = y2 - y1;
00176 
00177   // Protect against devide by 0
00178   if (fabs(dx) + fabs(dy) <= EPSILON )
00179     scale = 0.;
00180   else
00181     scale = 1.0 / (dx * dx + dy * dy);
00182 
00183   agl->a = dx * scale;
00184   agl->b = dy * scale;
00185   agl->c = -(x1 * agl->a + y1 * agl->b);
00186 
00187   // Get the spread method
00188   PRUint16 aSpread;
00189   aGrad->GetSpreadMethod(&aSpread);
00190   if (aSpread == nsIDOMSVGGradientElement::SVG_SPREADMETHOD_PAD)
00191     agl->spread = ART_GRADIENT_PAD;
00192   else if (aSpread == nsIDOMSVGGradientElement::SVG_SPREADMETHOD_REFLECT)
00193     agl->spread = ART_GRADIENT_REFLECT;
00194   else if (aSpread == nsIDOMSVGGradientElement::SVG_SPREADMETHOD_REPEAT)
00195     agl->spread = ART_GRADIENT_REPEAT;
00196 
00197   art_render_gradient_linear (render, agl, ART_FILTER_NEAREST);
00198   return;
00199 }
00200 
00201 static void
00202 LibartRadialGradient(ArtRender *render, nsISVGGradient *aGrad, double *affine)
00203 {
00204   ArtGradientRadial *agr = nsnull;
00205   double cx, cy, r, fx, fy;
00206   double aff1[6], aff2[6];
00207   float fCx, fCy, fR, fFx, fFy;
00208   PRUint32 nStops = 0;
00209 
00210 #ifdef DEBUG_scooter
00211   printf("In LibartRadialGradient\n");
00212 #endif
00213 
00214   agr = art_new (ArtGradientRadial, 1);
00215   aGrad->GetStopCount(&nStops);
00216   NS_ASSERTION(nStops > 0, "no stops for gradient");
00217   if (nStops == 0)
00218     return;
00219   agr->n_stops = nStops;
00220   agr->stops = GetStops(aGrad);
00221 
00222   // Get the Radial Gradient interface
00223   nsCOMPtr<nsISVGRadialGradient>aRgrad = do_QueryInterface(aGrad);
00224   NS_ASSERTION(aRgrad, "error gradient did not provide a Linear Gradient interface");
00225 
00226   // Get the gradient vector
00227   aRgrad->GetCx(&fCx);
00228   aRgrad->GetCy(&fCy);
00229   aRgrad->GetR(&fR);
00230   aRgrad->GetFx(&fFx);
00231   aRgrad->GetFy(&fFy);
00232 
00233   cx = fCx;
00234   cy = fCy;
00235   r = fR;
00236   fx = fFx;
00237   fy = fFy;
00238 
00239   art_affine_scale (aff1, r, r);
00240   art_affine_translate (aff2, cx, cy);
00241   art_affine_multiply (aff1, aff1, aff2);
00242   art_affine_multiply (aff1, aff1, affine);
00243   art_affine_invert (agr->affine, aff1);
00244 
00245   // libart doesn't support spreads on radial gradients
00246   agr->fx = (fx - cx) / r;
00247   agr->fy = (fy - cy) / r;
00248   art_render_gradient_radial (render, agr, ART_FILTER_NEAREST);
00249 }
00250 
00251 void
00252 LibartGradient(ArtRender *render, nsIDOMSVGMatrix *aMatrix, 
00253                nsISVGGradient *aGrad, nsISVGLibartRegion *aRegion,
00254                nsISVGGeometrySource *aSource)
00255 {
00256   double affine[6];
00257   NS_ASSERTION(aGrad, "Called LibartGradient without a gradient!");
00258   if (!aGrad) {
00259     return;
00260   }
00261 
00262   // Get the transform list (if there is one)
00263   nsCOMPtr<nsIDOMSVGMatrix> svgMatrix;
00264   aGrad->GetGradientTransform(getter_AddRefs(svgMatrix), aSource);
00265   NS_ASSERTION(svgMatrix, "LibartLinearGradient: GetGradientTransform returns null");
00266 
00267   double aTransMatrix[6];
00268   SVGToMatrix(svgMatrix, aTransMatrix);
00269 
00270   // Linear or Radial?
00271   PRUint32 type;
00272   aGrad->GetGradientType(&type);
00273   if (type == nsISVGGradient::SVG_LINEAR_GRADIENT)
00274     LibartLinearGradient(render, aGrad, aTransMatrix);
00275   else if (type == nsISVGGradient::SVG_RADIAL_GRADIENT)
00276     LibartRadialGradient(render, aGrad, aTransMatrix);
00277 
00278   return;
00279 }
00280 
00281