Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include <basegfx/tools/gradienttools.hxx>
21 : #include <basegfx/point/b2dpoint.hxx>
22 : #include <basegfx/range/b2drange.hxx>
23 : #include <basegfx/matrix/b2dhommatrixtools.hxx>
24 :
25 : namespace basegfx
26 : {
27 0 : bool ODFGradientInfo::operator==(const ODFGradientInfo& rODFGradientInfo) const
28 : {
29 0 : return getTextureTransform() == rODFGradientInfo.getTextureTransform()
30 0 : && getAspectRatio() == rODFGradientInfo.getAspectRatio()
31 0 : && getSteps() == rODFGradientInfo.getSteps();
32 : }
33 :
34 0 : const B2DHomMatrix& ODFGradientInfo::getBackTextureTransform() const
35 : {
36 0 : if(maBackTextureTransform.isIdentity())
37 : {
38 0 : const_cast< ODFGradientInfo* >(this)->maBackTextureTransform = getTextureTransform();
39 0 : const_cast< ODFGradientInfo* >(this)->maBackTextureTransform.invert();
40 : }
41 :
42 0 : return maBackTextureTransform;
43 : }
44 :
45 : /** Most of the setup for linear & axial gradient is the same, except
46 : for the border treatment. Factored out here.
47 : */
48 0 : ODFGradientInfo init1DGradientInfo(
49 : const B2DRange& rTargetRange,
50 : sal_uInt32 nSteps,
51 : double fBorder,
52 : double fAngle,
53 : bool bAxial)
54 : {
55 0 : B2DHomMatrix aTextureTransform;
56 :
57 0 : fAngle = -fAngle;
58 :
59 0 : double fTargetSizeX(rTargetRange.getWidth());
60 0 : double fTargetSizeY(rTargetRange.getHeight());
61 0 : double fTargetOffsetX(rTargetRange.getMinX());
62 0 : double fTargetOffsetY(rTargetRange.getMinY());
63 :
64 : // add object expansion
65 0 : const bool bAngleUsed(!fTools::equalZero(fAngle));
66 :
67 0 : if(bAngleUsed)
68 : {
69 0 : const double fAbsCos(fabs(cos(fAngle)));
70 0 : const double fAbsSin(fabs(sin(fAngle)));
71 0 : const double fNewX(fTargetSizeX * fAbsCos + fTargetSizeY * fAbsSin);
72 0 : const double fNewY(fTargetSizeY * fAbsCos + fTargetSizeX * fAbsSin);
73 :
74 0 : fTargetOffsetX -= (fNewX - fTargetSizeX) / 2.0;
75 0 : fTargetOffsetY -= (fNewY - fTargetSizeY) / 2.0;
76 0 : fTargetSizeX = fNewX;
77 0 : fTargetSizeY = fNewY;
78 : }
79 :
80 0 : const double fSizeWithoutBorder(1.0 - fBorder);
81 :
82 0 : if(bAxial)
83 : {
84 0 : aTextureTransform.scale(1.0, fSizeWithoutBorder * 0.5);
85 0 : aTextureTransform.translate(0.0, 0.5);
86 : }
87 : else
88 : {
89 0 : if(!fTools::equal(fSizeWithoutBorder, 1.0))
90 : {
91 0 : aTextureTransform.scale(1.0, fSizeWithoutBorder);
92 0 : aTextureTransform.translate(0.0, fBorder);
93 : }
94 : }
95 :
96 0 : aTextureTransform.scale(fTargetSizeX, fTargetSizeY);
97 :
98 : // add texture rotate after scale to keep perpendicular angles
99 0 : if(bAngleUsed)
100 : {
101 0 : const B2DPoint aCenter(0.5 * fTargetSizeX, 0.5 * fTargetSizeY);
102 :
103 0 : aTextureTransform *= basegfx::tools::createRotateAroundPoint(aCenter, fAngle);
104 : }
105 :
106 : // add object translate
107 0 : aTextureTransform.translate(fTargetOffsetX, fTargetOffsetY);
108 :
109 : // prepare aspect for texture
110 0 : const double fAspectRatio(fTools::equalZero(fTargetSizeY) ? 1.0 : fTargetSizeX / fTargetSizeY);
111 :
112 0 : return ODFGradientInfo(aTextureTransform, fAspectRatio, nSteps);
113 : }
114 :
115 : /** Most of the setup for radial & ellipsoidal gradient is the same,
116 : except for the border treatment. Factored out here.
117 : */
118 0 : ODFGradientInfo initEllipticalGradientInfo(
119 : const B2DRange& rTargetRange,
120 : const B2DVector& rOffset,
121 : sal_uInt32 nSteps,
122 : double fBorder,
123 : double fAngle,
124 : bool bCircular)
125 : {
126 0 : B2DHomMatrix aTextureTransform;
127 :
128 0 : fAngle = -fAngle;
129 :
130 0 : double fTargetSizeX(rTargetRange.getWidth());
131 0 : double fTargetSizeY(rTargetRange.getHeight());
132 0 : double fTargetOffsetX(rTargetRange.getMinX());
133 0 : double fTargetOffsetY(rTargetRange.getMinY());
134 :
135 : // add object expansion
136 0 : if(bCircular)
137 : {
138 0 : const double fOriginalDiag(sqrt((fTargetSizeX * fTargetSizeX) + (fTargetSizeY * fTargetSizeY)));
139 :
140 0 : fTargetOffsetX -= (fOriginalDiag - fTargetSizeX) / 2.0;
141 0 : fTargetOffsetY -= (fOriginalDiag - fTargetSizeY) / 2.0;
142 0 : fTargetSizeX = fOriginalDiag;
143 0 : fTargetSizeY = fOriginalDiag;
144 : }
145 : else
146 : {
147 0 : fTargetOffsetX -= (0.4142 / 2.0 ) * fTargetSizeX;
148 0 : fTargetOffsetY -= (0.4142 / 2.0 ) * fTargetSizeY;
149 0 : fTargetSizeX = 1.4142 * fTargetSizeX;
150 0 : fTargetSizeY = 1.4142 * fTargetSizeY;
151 : }
152 :
153 0 : const double fHalfBorder((1.0 - fBorder) * 0.5);
154 :
155 0 : aTextureTransform.scale(fHalfBorder, fHalfBorder);
156 0 : aTextureTransform.translate(0.5, 0.5);
157 0 : aTextureTransform.scale(fTargetSizeX, fTargetSizeY);
158 :
159 : // add texture rotate after scale to keep perpendicular angles
160 0 : if(!bCircular && !fTools::equalZero(fAngle))
161 : {
162 0 : const B2DPoint aCenter(0.5 * fTargetSizeX, 0.5 * fTargetSizeY);
163 :
164 0 : aTextureTransform *= basegfx::tools::createRotateAroundPoint(aCenter, fAngle);
165 : }
166 :
167 : // add defined offsets after rotation
168 0 : if(!fTools::equal(0.5, rOffset.getX()) || !fTools::equal(0.5, rOffset.getY()))
169 : {
170 : // use original target size
171 0 : fTargetOffsetX += (rOffset.getX() - 0.5) * rTargetRange.getWidth();
172 0 : fTargetOffsetY += (rOffset.getY() - 0.5) * rTargetRange.getHeight();
173 : }
174 :
175 : // add object translate
176 0 : aTextureTransform.translate(fTargetOffsetX, fTargetOffsetY);
177 :
178 : // prepare aspect for texture
179 0 : const double fAspectRatio((0.0 != fTargetSizeY) ? fTargetSizeX / fTargetSizeY : 1.0);
180 :
181 0 : return ODFGradientInfo(aTextureTransform, fAspectRatio, nSteps);
182 : }
183 :
184 : /** Setup for rect & square gradient is exactly the same. Factored out
185 : here.
186 : */
187 0 : ODFGradientInfo initRectGradientInfo(
188 : const B2DRange& rTargetRange,
189 : const B2DVector& rOffset,
190 : sal_uInt32 nSteps,
191 : double fBorder,
192 : double fAngle,
193 : bool bSquare)
194 : {
195 0 : B2DHomMatrix aTextureTransform;
196 :
197 0 : fAngle = -fAngle;
198 :
199 0 : double fTargetSizeX(rTargetRange.getWidth());
200 0 : double fTargetSizeY(rTargetRange.getHeight());
201 0 : double fTargetOffsetX(rTargetRange.getMinX());
202 0 : double fTargetOffsetY(rTargetRange.getMinY());
203 :
204 : // add object expansion
205 0 : if(bSquare)
206 : {
207 0 : const double fSquareWidth(std::max(fTargetSizeX, fTargetSizeY));
208 :
209 0 : fTargetOffsetX -= (fSquareWidth - fTargetSizeX) / 2.0;
210 0 : fTargetOffsetY -= (fSquareWidth - fTargetSizeY) / 2.0;
211 0 : fTargetSizeX = fTargetSizeY = fSquareWidth;
212 : }
213 :
214 : // add object expansion
215 0 : const bool bAngleUsed(!fTools::equalZero(fAngle));
216 :
217 0 : if(bAngleUsed)
218 : {
219 0 : const double fAbsCos(fabs(cos(fAngle)));
220 0 : const double fAbsSin(fabs(sin(fAngle)));
221 0 : const double fNewX(fTargetSizeX * fAbsCos + fTargetSizeY * fAbsSin);
222 0 : const double fNewY(fTargetSizeY * fAbsCos + fTargetSizeX * fAbsSin);
223 :
224 0 : fTargetOffsetX -= (fNewX - fTargetSizeX) / 2.0;
225 0 : fTargetOffsetY -= (fNewY - fTargetSizeY) / 2.0;
226 0 : fTargetSizeX = fNewX;
227 0 : fTargetSizeY = fNewY;
228 : }
229 :
230 0 : const double fHalfBorder((1.0 - fBorder) * 0.5);
231 :
232 0 : aTextureTransform.scale(fHalfBorder, fHalfBorder);
233 0 : aTextureTransform.translate(0.5, 0.5);
234 0 : aTextureTransform.scale(fTargetSizeX, fTargetSizeY);
235 :
236 : // add texture rotate after scale to keep perpendicular angles
237 0 : if(bAngleUsed)
238 : {
239 0 : const B2DPoint aCenter(0.5 * fTargetSizeX, 0.5 * fTargetSizeY);
240 :
241 0 : aTextureTransform *= basegfx::tools::createRotateAroundPoint(aCenter, fAngle);
242 : }
243 :
244 : // add defined offsets after rotation
245 0 : if(!fTools::equal(0.5, rOffset.getX()) || !fTools::equal(0.5, rOffset.getY()))
246 : {
247 : // use scaled target size
248 0 : fTargetOffsetX += (rOffset.getX() - 0.5) * fTargetSizeX;
249 0 : fTargetOffsetY += (rOffset.getY() - 0.5) * fTargetSizeY;
250 : }
251 :
252 : // add object translate
253 0 : aTextureTransform.translate(fTargetOffsetX, fTargetOffsetY);
254 :
255 : // prepare aspect for texture
256 0 : const double fAspectRatio((0.0 != fTargetSizeY) ? fTargetSizeX / fTargetSizeY : 1.0);
257 :
258 0 : return ODFGradientInfo(aTextureTransform, fAspectRatio, nSteps);
259 : }
260 :
261 : namespace tools
262 : {
263 0 : ODFGradientInfo createLinearODFGradientInfo(
264 : const B2DRange& rTargetArea,
265 : sal_uInt32 nSteps,
266 : double fBorder,
267 : double fAngle)
268 : {
269 : return init1DGradientInfo(
270 : rTargetArea,
271 : nSteps,
272 : fBorder,
273 : fAngle,
274 0 : false);
275 : }
276 :
277 0 : ODFGradientInfo createAxialODFGradientInfo(
278 : const B2DRange& rTargetArea,
279 : sal_uInt32 nSteps,
280 : double fBorder,
281 : double fAngle)
282 : {
283 : return init1DGradientInfo(
284 : rTargetArea,
285 : nSteps,
286 : fBorder,
287 : fAngle,
288 0 : true);
289 : }
290 :
291 0 : ODFGradientInfo createRadialODFGradientInfo(
292 : const B2DRange& rTargetArea,
293 : const B2DVector& rOffset,
294 : sal_uInt32 nSteps,
295 : double fBorder)
296 : {
297 : return initEllipticalGradientInfo(
298 : rTargetArea,
299 : rOffset,
300 : nSteps,
301 : fBorder,
302 : 0.0,
303 0 : true);
304 : }
305 :
306 0 : ODFGradientInfo createEllipticalODFGradientInfo(
307 : const B2DRange& rTargetArea,
308 : const B2DVector& rOffset,
309 : sal_uInt32 nSteps,
310 : double fBorder,
311 : double fAngle)
312 : {
313 : return initEllipticalGradientInfo(
314 : rTargetArea,
315 : rOffset,
316 : nSteps,
317 : fBorder,
318 : fAngle,
319 0 : false);
320 : }
321 :
322 0 : ODFGradientInfo createSquareODFGradientInfo(
323 : const B2DRange& rTargetArea,
324 : const B2DVector& rOffset,
325 : sal_uInt32 nSteps,
326 : double fBorder,
327 : double fAngle)
328 : {
329 : return initRectGradientInfo(
330 : rTargetArea,
331 : rOffset,
332 : nSteps,
333 : fBorder,
334 : fAngle,
335 0 : true);
336 : }
337 :
338 0 : ODFGradientInfo createRectangularODFGradientInfo(
339 : const B2DRange& rTargetArea,
340 : const B2DVector& rOffset,
341 : sal_uInt32 nSteps,
342 : double fBorder,
343 : double fAngle)
344 : {
345 : return initRectGradientInfo(
346 : rTargetArea,
347 : rOffset,
348 : nSteps,
349 : fBorder,
350 : fAngle,
351 0 : false);
352 : }
353 :
354 0 : double getLinearGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo)
355 : {
356 0 : const B2DPoint aCoor(rGradInfo.getBackTextureTransform() * rUV);
357 :
358 : // Ignore Y, this is not needed at all for Y-Oriented gradients
359 : // if(aCoor.getX() < 0.0 || aCoor.getX() > 1.0)
360 : // {
361 : // return 0.0;
362 : // }
363 :
364 0 : if(aCoor.getY() <= 0.0)
365 : {
366 0 : return 0.0; // start value for inside
367 : }
368 :
369 0 : if(aCoor.getY() >= 1.0)
370 : {
371 0 : return 1.0; // end value for outside
372 : }
373 :
374 0 : const sal_uInt32 nSteps(rGradInfo.getSteps());
375 :
376 0 : if(nSteps)
377 : {
378 0 : return floor(aCoor.getY() * nSteps) / double(nSteps - 1);
379 : }
380 :
381 0 : return aCoor.getY();
382 : }
383 :
384 0 : double getAxialGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo)
385 : {
386 0 : const B2DPoint aCoor(rGradInfo.getBackTextureTransform() * rUV);
387 :
388 : // Ignore Y, this is not needed at all for Y-Oriented gradients
389 : //if(aCoor.getX() < 0.0 || aCoor.getX() > 1.0)
390 : //{
391 : // return 0.0;
392 : //}
393 :
394 0 : const double fAbsY(fabs(aCoor.getY()));
395 :
396 0 : if(fAbsY >= 1.0)
397 : {
398 0 : return 1.0; // use end value when outside in Y
399 : }
400 :
401 0 : const sal_uInt32 nSteps(rGradInfo.getSteps());
402 :
403 0 : if(nSteps)
404 : {
405 0 : return floor(fAbsY * nSteps) / double(nSteps - 1);
406 : }
407 :
408 0 : return fAbsY;
409 : }
410 :
411 0 : double getRadialGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo)
412 : {
413 0 : const B2DPoint aCoor(rGradInfo.getBackTextureTransform() * rUV);
414 :
415 0 : if(aCoor.getX() < -1.0 || aCoor.getX() > 1.0 || aCoor.getY() < -1.0 || aCoor.getY() > 1.0)
416 : {
417 0 : return 0.0;
418 : }
419 :
420 0 : const double t(1.0 - sqrt(aCoor.getX() * aCoor.getX() + aCoor.getY() * aCoor.getY()));
421 0 : const sal_uInt32 nSteps(rGradInfo.getSteps());
422 :
423 0 : if(nSteps && t < 1.0)
424 : {
425 0 : return floor(t * nSteps) / double(nSteps - 1);
426 : }
427 :
428 0 : return t;
429 : }
430 :
431 0 : double getEllipticalGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo)
432 : {
433 0 : return getRadialGradientAlpha(rUV, rGradInfo); // only matrix setup differs
434 : }
435 :
436 0 : double getSquareGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo)
437 : {
438 0 : const B2DPoint aCoor(rGradInfo.getBackTextureTransform() * rUV);
439 0 : const double fAbsX(fabs(aCoor.getX()));
440 :
441 0 : if(fAbsX >= 1.0)
442 : {
443 0 : return 0.0;
444 : }
445 :
446 0 : const double fAbsY(fabs(aCoor.getY()));
447 :
448 0 : if(fAbsY >= 1.0)
449 : {
450 0 : return 0.0;
451 : }
452 :
453 0 : const double t(1.0 - std::max(fAbsX, fAbsY));
454 0 : const sal_uInt32 nSteps(rGradInfo.getSteps());
455 :
456 0 : if(nSteps && t < 1.0)
457 : {
458 0 : return floor(t * nSteps) / double(nSteps - 1);
459 : }
460 :
461 0 : return t;
462 : }
463 :
464 0 : double getRectangularGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo)
465 : {
466 0 : return getSquareGradientAlpha(rUV, rGradInfo); // only matrix setup differs
467 : }
468 : } // namespace tools
469 : } // namespace basegfx
470 :
471 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|