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                                      bool             bSquare)
188     {
189         o_rGradientInfo.maTextureTransform.identity();
190         o_rGradientInfo.maBackTextureTransform.identity();
191         o_rGradientInfo.mnSteps = nSteps;
192 
193         fAngle = -fAngle;
194 
195         double fTargetSizeX(rTargetRange.getWidth());
196         double fTargetSizeY(rTargetRange.getHeight());
197         double fTargetOffsetX(rTargetRange.getMinX());
198         double fTargetOffsetY(rTargetRange.getMinY());
199 
200         // add object expansion
201         if(bSquare)
202         {
203             const double fOriginalDiag(sqrt((fTargetSizeX * fTargetSizeX) + (fTargetSizeY * fTargetSizeY)));
204             fTargetOffsetX -= (fOriginalDiag - fTargetSizeX) / 2.0;
205             fTargetOffsetY -= (fOriginalDiag - fTargetSizeY) / 2.0;
206             fTargetSizeX = fOriginalDiag;
207             fTargetSizeY = fOriginalDiag;
208         }
209 
210         // add object expansion
211         if(0.0 != fAngle)
212         {
213             const double fAbsCos(fabs(cos(fAngle)));
214             const double fAbsSin(fabs(sin(fAngle)));
215             const double fNewX(fTargetSizeX * fAbsCos + fTargetSizeY * fAbsSin);
216             const double fNewY(fTargetSizeY * fAbsCos + fTargetSizeX * fAbsSin);
217             fTargetOffsetX -= (fNewX - fTargetSizeX) / 2.0;
218             fTargetOffsetY -= (fNewY - fTargetSizeY) / 2.0;
219             fTargetSizeX = fNewX;
220             fTargetSizeY = fNewY;
221         }
222 
223         const double fHalfBorder((1.0 - fBorder) * 0.5);
224         o_rGradientInfo.maTextureTransform.scale(fHalfBorder, fHalfBorder);
225 
226         o_rGradientInfo.maTextureTransform.translate(0.5, 0.5);
227         o_rGradientInfo.maTextureTransform.scale(fTargetSizeX, fTargetSizeY);
228 
229         // add texture rotate after scale to keep perpendicular angles
230         if(0.0 != fAngle)
231         {
232             const B2DPoint aCenter(0.5*fTargetSizeX,
233                                    0.5*fTargetSizeY);
234             o_rGradientInfo.maTextureTransform *=
235                 basegfx::tools::createRotateAroundPoint(aCenter, fAngle);
236         }
237 
238         // add defined offsets after rotation
239         if(0.5 != rOffset.getX() || 0.5 != rOffset.getY())
240         {
241             // use scaled target size
242             fTargetOffsetX += (rOffset.getX() - 0.5) * fTargetSizeX;
243             fTargetOffsetY += (rOffset.getY() - 0.5) * fTargetSizeY;
244         }
245 
246         // add object translate
247         o_rGradientInfo.maTextureTransform.translate(fTargetOffsetX, fTargetOffsetY);
248 
249         // prepare aspect for texture
250         o_rGradientInfo.mfAspectRatio = (0.0 != fTargetSizeY) ?  fTargetSizeX / fTargetSizeY : 1.0;
251 
252         // build transform from u,v to [0.0 .. 1.0]. As base, use inverse texture transform
253         o_rGradientInfo.maBackTextureTransform = o_rGradientInfo.maTextureTransform;
254         o_rGradientInfo.maBackTextureTransform.invert();
255     }
256 
257     namespace tools
258     {
259 		ODFGradientInfo& createLinearODFGradientInfo(ODFGradientInfo& o_rGradientInfo,
260                                                      const B2DRange&  rTargetArea,
261                                                      sal_uInt32       nSteps,
262                                                      double           fBorder,
263                                                      double           fAngle)
264         {
265             init1DGradientInfo(o_rGradientInfo,
266                                rTargetArea,
267                                nSteps,
268                                fBorder,
269                                fAngle,
270                                false);
271             return o_rGradientInfo;
272         }
273 
274 		ODFGradientInfo& createAxialODFGradientInfo(ODFGradientInfo& o_rGradientInfo,
275                                                     const B2DRange&  rTargetArea,
276                                                     sal_uInt32       nSteps,
277                                                     double           fBorder,
278                                                     double           fAngle)
279         {
280             init1DGradientInfo(o_rGradientInfo,
281                                rTargetArea,
282                                nSteps,
283                                fBorder,
284                                fAngle,
285                                true);
286             return o_rGradientInfo;
287         }
288 
289 		ODFGradientInfo& createRadialODFGradientInfo(ODFGradientInfo& o_rGradientInfo,
290                                                      const B2DRange&  rTargetArea,
291                                                      const B2DVector& rOffset,
292                                                      sal_uInt32       nSteps,
293                                                      double           fBorder)
294         {
295             initEllipticalGradientInfo(o_rGradientInfo,
296                                        rTargetArea,
297                                        rOffset,
298                                        nSteps,
299                                        fBorder,
300                                        0.0,
301                                        true);
302             return o_rGradientInfo;
303         }
304 
305 		ODFGradientInfo& createEllipticalODFGradientInfo(ODFGradientInfo& o_rGradientInfo,
306                                                          const B2DRange&  rTargetArea,
307                                                          const B2DVector& rOffset,
308                                                          sal_uInt32       nSteps,
309                                                          double           fBorder,
310                                                          double           fAngle)
311         {
312             initEllipticalGradientInfo(o_rGradientInfo,
313                                        rTargetArea,
314                                        rOffset,
315                                        nSteps,
316                                        fBorder,
317                                        fAngle,
318                                        false);
319             return o_rGradientInfo;
320         }
321 
322 		ODFGradientInfo& createSquareODFGradientInfo(ODFGradientInfo& o_rGradientInfo,
323                                                      const B2DRange&  rTargetArea,
324                                                      const B2DVector& rOffset,
325                                                      sal_uInt32       nSteps,
326                                                      double           fBorder,
327                                                      double           fAngle)
328         {
329             initRectGradientInfo(o_rGradientInfo,
330                                  rTargetArea,
331                                  rOffset,
332                                  nSteps,
333                                  fBorder,
334                                  fAngle,
335                                  true);
336             return o_rGradientInfo;
337         }
338 
339 		ODFGradientInfo& createRectangularODFGradientInfo(ODFGradientInfo& o_rGradientInfo,
340                                                           const B2DRange&  rTargetArea,
341                                                           const B2DVector& rOffset,
342                                                           sal_uInt32       nSteps,
343                                                           double           fBorder,
344                                                           double           fAngle)
345         {
346             initRectGradientInfo(o_rGradientInfo,
347                                  rTargetArea,
348                                  rOffset,
349                                  nSteps,
350                                  fBorder,
351                                  fAngle,
352                                  false);
353             return o_rGradientInfo;
354         }
355 
356     } // namespace tools
357 
358 } // namespace basegfx
359