1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_basegfx.hxx"
30 
31 #include <basegfx/tools/gradienttools.hxx>
32 #include <basegfx/point/b2dpoint.hxx>
33 #include <basegfx/range/b2drange.hxx>
34 #include <basegfx/matrix/b2dhommatrixtools.hxx>
35 
36 namespace basegfx
37 {
38     /** Most of the setup for linear & axial gradient is the same, except
39         for the border treatment. Factored out here.
40     */
41     static void init1DGradientInfo(ODFGradientInfo& o_rGradientInfo,
42                                    const B2DRange&  rTargetRange,
43                                    sal_uInt32       nSteps,
44                                    double           fBorder,
45                                    double           fAngle,
46                                    bool             bAxial)
47     {
48         o_rGradientInfo.maTextureTransform.identity();
49         o_rGradientInfo.maBackTextureTransform.identity();
50         o_rGradientInfo.mnSteps = nSteps;
51 
52         fAngle = -fAngle;
53 
54         double fTargetSizeX(rTargetRange.getWidth());
55         double fTargetSizeY(rTargetRange.getHeight());
56         double fTargetOffsetX(rTargetRange.getMinX());
57         double fTargetOffsetY(rTargetRange.getMinY());
58 
59         // add object expansion
60         if(0.0 != fAngle)
61         {
62             const double fAbsCos(fabs(cos(fAngle)));
63             const double fAbsSin(fabs(sin(fAngle)));
64             const double fNewX(fTargetSizeX * fAbsCos + fTargetSizeY * fAbsSin);
65             const double fNewY(fTargetSizeY * fAbsCos + fTargetSizeX * fAbsSin);
66             fTargetOffsetX -= (fNewX - fTargetSizeX) / 2.0;
67             fTargetOffsetY -= (fNewY - fTargetSizeY) / 2.0;
68             fTargetSizeX = fNewX;
69             fTargetSizeY = fNewY;
70         }
71 
72         const double fSizeWithoutBorder=1.0 - fBorder;
73         if( bAxial )
74         {
75             o_rGradientInfo.maTextureTransform.scale(1.0, fSizeWithoutBorder * .5);
76             o_rGradientInfo.maTextureTransform.translate(0.0, 0.5);
77         }
78         else
79         {
80             if(!fTools::equal(fSizeWithoutBorder, 1.0))
81             {
82                 o_rGradientInfo.maTextureTransform.scale(1.0, fSizeWithoutBorder);
83                 o_rGradientInfo.maTextureTransform.translate(0.0, fBorder);
84             }
85         }
86 
87         o_rGradientInfo.maTextureTransform.scale(fTargetSizeX, fTargetSizeY);
88 
89         // add texture rotate after scale to keep perpendicular angles
90         if(0.0 != fAngle)
91         {
92             const B2DPoint aCenter(0.5*fTargetSizeX,
93                                    0.5*fTargetSizeY);
94             o_rGradientInfo.maTextureTransform *=
95                 basegfx::tools::createRotateAroundPoint(aCenter, fAngle);
96         }
97 
98         // add object translate
99         o_rGradientInfo.maTextureTransform.translate(fTargetOffsetX, fTargetOffsetY);
100 
101         // prepare aspect for texture
102         o_rGradientInfo.mfAspectRatio = (0.0 != fTargetSizeY) ?  fTargetSizeX / fTargetSizeY : 1.0;
103 
104         // build transform from u,v to [0.0 .. 1.0].
105         o_rGradientInfo.maBackTextureTransform = o_rGradientInfo.maTextureTransform;
106         o_rGradientInfo.maBackTextureTransform.invert();
107     }
108 
109     /** Most of the setup for radial & ellipsoidal gradient is the same,
110         except for the border treatment. Factored out here.
111     */
112     static void initEllipticalGradientInfo(ODFGradientInfo& o_rGradientInfo,
113                                            const B2DRange&  rTargetRange,
114                                            const B2DVector& rOffset,
115                                            sal_uInt32       nSteps,
116                                            double           fBorder,
117                                            double           fAngle,
118                                            bool             bCircular)
119     {
120         o_rGradientInfo.maTextureTransform.identity();
121         o_rGradientInfo.maBackTextureTransform.identity();
122         o_rGradientInfo.mnSteps = nSteps;
123 
124         fAngle = -fAngle;
125 
126         double fTargetSizeX(rTargetRange.getWidth());
127         double fTargetSizeY(rTargetRange.getHeight());
128         double fTargetOffsetX(rTargetRange.getMinX());
129         double fTargetOffsetY(rTargetRange.getMinY());
130 
131         // add object expansion
132         if( bCircular )
133         {
134             const double fOriginalDiag(sqrt((fTargetSizeX * fTargetSizeX) + (fTargetSizeY * fTargetSizeY)));
135             fTargetOffsetX -= (fOriginalDiag - fTargetSizeX) / 2.0;
136             fTargetOffsetY -= (fOriginalDiag - fTargetSizeY) / 2.0;
137             fTargetSizeX = fOriginalDiag;
138             fTargetSizeY = fOriginalDiag;
139         }
140         else
141         {
142             fTargetOffsetX -= (0.4142 / 2.0 ) * fTargetSizeX;
143             fTargetOffsetY -= (0.4142 / 2.0 ) * fTargetSizeY;
144             fTargetSizeX = 1.4142 * fTargetSizeX;
145             fTargetSizeY = 1.4142 * fTargetSizeY;
146         }
147 
148         const double fHalfBorder((1.0 - fBorder) * 0.5);
149         o_rGradientInfo.maTextureTransform.scale(fHalfBorder, fHalfBorder);
150 
151         o_rGradientInfo.maTextureTransform.translate(0.5, 0.5);
152         o_rGradientInfo.maTextureTransform.scale(fTargetSizeX, fTargetSizeY);
153 
154         // add texture rotate after scale to keep perpendicular angles
155         if( !bCircular && 0.0 != fAngle)
156         {
157             const B2DPoint aCenter(0.5*fTargetSizeX,
158                                    0.5*fTargetSizeY);
159             o_rGradientInfo.maTextureTransform *=
160                 basegfx::tools::createRotateAroundPoint(aCenter, fAngle);
161         }
162 
163         // add defined offsets after rotation
164         if(0.5 != rOffset.getX() || 0.5 != rOffset.getY())
165         {
166             // use original target size
167             fTargetOffsetX += (rOffset.getX() - 0.5) * rTargetRange.getWidth();
168             fTargetOffsetY += (rOffset.getY() - 0.5) * rTargetRange.getHeight();
169         }
170 
171         // add object translate
172         o_rGradientInfo.maTextureTransform.translate(fTargetOffsetX, fTargetOffsetY);
173 
174         // prepare aspect for texture
175         o_rGradientInfo.mfAspectRatio = (0.0 != fTargetSizeY) ?  fTargetSizeX / fTargetSizeY : 1.0;
176 
177         // build transform from u,v to [0.0 .. 1.0].
178         o_rGradientInfo.maBackTextureTransform = o_rGradientInfo.maTextureTransform;
179         o_rGradientInfo.maBackTextureTransform.invert();
180     }
181 
182     /** Setup for rect & square gradient is exactly the same. Factored out
183         here.
184     */
185     static void initRectGradientInfo(ODFGradientInfo& o_rGradientInfo,
186                                      const B2DRange&  rTargetRange,
187                                      const B2DVector& rOffset,
188                                      sal_uInt32       nSteps,
189                                      double           fBorder,
190                                      double           fAngle)
191     {
192         o_rGradientInfo.maTextureTransform.identity();
193         o_rGradientInfo.maBackTextureTransform.identity();
194         o_rGradientInfo.mnSteps = nSteps;
195 
196         fAngle = -fAngle;
197 
198         double fTargetSizeX(rTargetRange.getWidth());
199         double fTargetSizeY(rTargetRange.getHeight());
200         double fTargetOffsetX(rTargetRange.getMinX());
201         double fTargetOffsetY(rTargetRange.getMinY());
202 
203         // add object expansion
204         if(0.0 != fAngle)
205         {
206             const double fAbsCos(fabs(cos(fAngle)));
207             const double fAbsSin(fabs(sin(fAngle)));
208             const double fNewX(fTargetSizeX * fAbsCos + fTargetSizeY * fAbsSin);
209             const double fNewY(fTargetSizeY * fAbsCos + fTargetSizeX * fAbsSin);
210             fTargetOffsetX -= (fNewX - fTargetSizeX) / 2.0;
211             fTargetOffsetY -= (fNewY - fTargetSizeY) / 2.0;
212             fTargetSizeX = fNewX;
213             fTargetSizeY = fNewY;
214         }
215 
216         const double fHalfBorder((1.0 - fBorder) * 0.5);
217         o_rGradientInfo.maTextureTransform.scale(fHalfBorder, fHalfBorder);
218 
219         o_rGradientInfo.maTextureTransform.translate(0.5, 0.5);
220         o_rGradientInfo.maTextureTransform.scale(fTargetSizeX, fTargetSizeY);
221 
222         // add texture rotate after scale to keep perpendicular angles
223         if(0.0 != fAngle)
224         {
225             const B2DPoint aCenter(0.5*fTargetSizeX,
226                                    0.5*fTargetSizeY);
227             o_rGradientInfo.maTextureTransform *=
228                 basegfx::tools::createRotateAroundPoint(aCenter, fAngle);
229         }
230 
231         // add defined offsets after rotation
232         if(0.5 != rOffset.getX() || 0.5 != rOffset.getY())
233         {
234             // use scaled target size
235             fTargetOffsetX += (rOffset.getX() - 0.5) * fTargetSizeX;
236             fTargetOffsetY += (rOffset.getY() - 0.5) * fTargetSizeY;
237         }
238 
239         // add object translate
240         o_rGradientInfo.maTextureTransform.translate(fTargetOffsetX, fTargetOffsetY);
241 
242         // prepare aspect for texture
243         o_rGradientInfo.mfAspectRatio = (0.0 != fTargetSizeY) ?  fTargetSizeX / fTargetSizeY : 1.0;
244 
245         // build transform from u,v to [0.0 .. 1.0]. As base, use inverse texture transform
246         o_rGradientInfo.maBackTextureTransform = o_rGradientInfo.maTextureTransform;
247         o_rGradientInfo.maBackTextureTransform.invert();
248     }
249 
250     namespace tools
251     {
252 		ODFGradientInfo& createLinearODFGradientInfo(ODFGradientInfo& o_rGradientInfo,
253                                                      const B2DRange&  rTargetArea,
254                                                      sal_uInt32       nSteps,
255                                                      double           fBorder,
256                                                      double           fAngle)
257         {
258             init1DGradientInfo(o_rGradientInfo,
259                                rTargetArea,
260                                nSteps,
261                                fBorder,
262                                fAngle,
263                                false);
264             return o_rGradientInfo;
265         }
266 
267 		ODFGradientInfo& createAxialODFGradientInfo(ODFGradientInfo& o_rGradientInfo,
268                                                     const B2DRange&  rTargetArea,
269                                                     sal_uInt32       nSteps,
270                                                     double           fBorder,
271                                                     double           fAngle)
272         {
273             init1DGradientInfo(o_rGradientInfo,
274                                rTargetArea,
275                                nSteps,
276                                fBorder,
277                                fAngle,
278                                true);
279             return o_rGradientInfo;
280         }
281 
282 		ODFGradientInfo& createRadialODFGradientInfo(ODFGradientInfo& o_rGradientInfo,
283                                                      const B2DRange&  rTargetArea,
284                                                      const B2DVector& rOffset,
285                                                      sal_uInt32       nSteps,
286                                                      double           fBorder)
287         {
288             initEllipticalGradientInfo(o_rGradientInfo,
289                                        rTargetArea,
290                                        rOffset,
291                                        nSteps,
292                                        fBorder,
293                                        0.0,
294                                        true);
295             return o_rGradientInfo;
296         }
297 
298 		ODFGradientInfo& createEllipticalODFGradientInfo(ODFGradientInfo& o_rGradientInfo,
299                                                          const B2DRange&  rTargetArea,
300                                                          const B2DVector& rOffset,
301                                                          sal_uInt32       nSteps,
302                                                          double           fBorder,
303                                                          double           fAngle)
304         {
305             initEllipticalGradientInfo(o_rGradientInfo,
306                                        rTargetArea,
307                                        rOffset,
308                                        nSteps,
309                                        fBorder,
310                                        fAngle,
311                                        false);
312             return o_rGradientInfo;
313         }
314 
315 		ODFGradientInfo& createSquareODFGradientInfo(ODFGradientInfo& o_rGradientInfo,
316                                                      const B2DRange&  rTargetArea,
317                                                      const B2DVector& rOffset,
318                                                      sal_uInt32       nSteps,
319                                                      double           fBorder,
320                                                      double           fAngle)
321         {
322             initRectGradientInfo(o_rGradientInfo,
323                                  rTargetArea,
324                                  rOffset,
325                                  nSteps,
326                                  fBorder,
327                                  fAngle);
328             return o_rGradientInfo;
329         }
330 
331 		ODFGradientInfo& createRectangularODFGradientInfo(ODFGradientInfo& o_rGradientInfo,
332                                                           const B2DRange&  rTargetArea,
333                                                           const B2DVector& rOffset,
334                                                           sal_uInt32       nSteps,
335                                                           double           fBorder,
336                                                           double           fAngle)
337         {
338             initRectGradientInfo(o_rGradientInfo,
339                                  rTargetArea,
340                                  rOffset,
341                                  nSteps,
342                                  fBorder,
343                                  fAngle);
344             return o_rGradientInfo;
345         }
346 
347     } // namespace tools
348 
349 } // namespace basegfx
350