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