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