1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
24 #include <texture/texture.hxx>
25 #include <basegfx/numeric/ftools.hxx>
26 #include <basegfx/utils/gradienttools.hxx>
27 #include <basegfx/matrix/b2dhommatrixtools.hxx>
29 #include <comphelper/random.hxx>
31 namespace drawinglayer::texture
35 double getRandomColorRange()
37 return comphelper::rng::uniform_real_distribution(0.0, nextafter(1.0, DBL_MAX
));
41 GeoTexSvx::GeoTexSvx()
45 GeoTexSvx::~GeoTexSvx()
49 bool GeoTexSvx::operator==(const GeoTexSvx
& /*rGeoTexSvx*/) const
51 // default implementation says yes (no data -> no difference)
55 void GeoTexSvx::modifyBColor(const basegfx::B2DPoint
& /*rUV*/, basegfx::BColor
& rBColor
, double& /*rfOpacity*/) const
57 // base implementation creates random color (for testing only, may also be pure virtual)
58 rBColor
.setRed(getRandomColorRange());
59 rBColor
.setGreen(getRandomColorRange());
60 rBColor
.setBlue(getRandomColorRange());
63 void GeoTexSvx::modifyOpacity(const basegfx::B2DPoint
& rUV
, double& rfOpacity
) const
65 // base implementation uses inverse of luminance of solved color (for testing only, may also be pure virtual)
66 basegfx::BColor aBaseColor
;
67 modifyBColor(rUV
, aBaseColor
, rfOpacity
);
68 rfOpacity
= 1.0 - aBaseColor
.luminance();
72 GeoTexSvxGradient::GeoTexSvxGradient(
73 const basegfx::B2DRange
& rDefinitionRange
,
74 sal_uInt32 nRequestedSteps
,
75 const basegfx::BColorStops
& rColorStops
,
77 : maDefinitionRange(rDefinitionRange
)
78 , mnRequestedSteps(nRequestedSteps
)
79 , mnColorStops(rColorStops
)
81 , maLastColorStopRange()
85 GeoTexSvxGradient::~GeoTexSvxGradient()
89 bool GeoTexSvxGradient::operator==(const GeoTexSvx
& rGeoTexSvx
) const
91 const GeoTexSvxGradient
* pCompare
= dynamic_cast< const GeoTexSvxGradient
* >(&rGeoTexSvx
);
94 && maGradientInfo
== pCompare
->maGradientInfo
95 && maDefinitionRange
== pCompare
->maDefinitionRange
96 && mnRequestedSteps
== pCompare
->mnRequestedSteps
97 && mnColorStops
== pCompare
->mnColorStops
98 && mfBorder
== pCompare
->mfBorder
);
101 GeoTexSvxGradientLinear::GeoTexSvxGradientLinear(
102 const basegfx::B2DRange
& rDefinitionRange
,
103 const basegfx::B2DRange
& rOutputRange
,
104 sal_uInt32 nRequestedSteps
,
105 const basegfx::BColorStops
& rColorStops
,
108 : GeoTexSvxGradient(rDefinitionRange
, nRequestedSteps
, rColorStops
, fBorder
)
113 maGradientInfo
= basegfx::utils::createLinearODFGradientInfo(
119 if(rDefinitionRange
!= rOutputRange
)
121 basegfx::B2DRange
aInvOutputRange(rOutputRange
);
123 aInvOutputRange
.transform(maGradientInfo
.getBackTextureTransform());
124 mfUnitMinX
= aInvOutputRange
.getMinX();
125 mfUnitWidth
= aInvOutputRange
.getWidth();
126 mfUnitMaxY
= aInvOutputRange
.getMaxY();
130 GeoTexSvxGradientLinear::~GeoTexSvxGradientLinear()
134 void GeoTexSvxGradientLinear::appendTransformationsAndColors(
135 std::function
<void(const basegfx::B2DHomMatrix
& rMatrix
, const basegfx::BColor
& rColor
)> aCallback
)
137 // no color at all, done
138 if (mnColorStops
.empty())
141 // only one color, done
142 if (mnColorStops
.size() < 2)
145 // check if we need last-ColorStop-correction
146 const bool bPenultimateUsed(mnColorStops
.checkPenultimate());
148 if (bPenultimateUsed
)
150 // Here we need to temporarily add a ColorStop entry with the
151 // same color as the last entry to correctly 'close' the
152 // created gradient geometry.
153 // The simplest way is to temporarily add an entry to the local
154 // ColorStops for this at 1.0 (using same color)
155 mnColorStops
.emplace_back(1.0, mnColorStops
.back().getStopColor());
158 // prepare unit range transform
159 basegfx::B2DHomMatrix aPattern
;
161 // bring from unit circle [-1, -1, 1, 1] to unit range [0, 0, 1, 1]
162 aPattern
.scale(0.5, 0.5);
163 aPattern
.translate(0.5, 0.5);
165 // scale and translate in X
166 aPattern
.scale(mfUnitWidth
, 1.0);
167 aPattern
.translate(mfUnitMinX
, 0.0);
169 // outer loop over ColorStops, each is from cs_l to cs_r
170 for (auto cs_l(mnColorStops
.begin()), cs_r(cs_l
+ 1); cs_r
!= mnColorStops
.end(); cs_l
++, cs_r
++)
173 const double fOffsetStart(cs_l
->getStopOffset());
174 const double fOffsetEnd(cs_r
->getStopOffset());
176 // same offset, empty BColorStopRange, continue with next step
177 if (basegfx::fTools::equal(fOffsetStart
, fOffsetEnd
))
180 // get colors & calculate steps
181 const basegfx::BColor
aCStart(cs_l
->getStopColor());
182 const basegfx::BColor
aCEnd(cs_r
->getStopColor());
183 const sal_uInt32
nSteps(basegfx::utils::calculateNumberOfSteps(
184 maGradientInfo
.getRequestedSteps(), aCStart
, aCEnd
));
186 // calculate StripeWidth
187 // nSteps is >= 1, see getRequestedSteps, so no check needed here
188 const double fStripeWidth((fOffsetEnd
- fOffsetStart
) / nSteps
);
190 // for the 1st color range we do not need to create the 1st step
191 // since it will be equal to StartColor and thus OuterColor, so
192 // will be painted by the 1st, always-created background polygon
193 // colored using OuterColor.
194 // We *need* to create this though for all 'inner' color ranges
195 // to get a correct start
196 const sal_uInt32
nStartInnerLoop(cs_l
== mnColorStops
.begin() ? 1 : 0);
198 for (sal_uInt32
innerLoop(nStartInnerLoop
); innerLoop
< nSteps
; innerLoop
++)
200 // calculate pos in Y
201 const double fPos(fOffsetStart
+ (fStripeWidth
* innerLoop
));
203 // scale and translate in Y. For GradientLinear we always have
205 double fHeight(1.0 - fPos
);
207 if (mfUnitMaxY
> 1.0)
209 // extend when difference between definition and OutputRange exists
210 fHeight
+= mfUnitMaxY
- 1.0;
213 basegfx::B2DHomMatrix
aNew(aPattern
);
214 aNew
.scale(1.0, fHeight
);
215 aNew
.translate(0.0, fPos
);
217 // set and add at target
219 maGradientInfo
.getTextureTransform() * aNew
,
220 1 == nSteps
? aCStart
: basegfx::BColor(interpolate(aCStart
, aCEnd
, double(innerLoop
) / double(nSteps
- 1))));
224 if (bPenultimateUsed
)
226 // correct temporary change
227 mnColorStops
.pop_back();
231 void GeoTexSvxGradientLinear::modifyBColor(const basegfx::B2DPoint
& rUV
, basegfx::BColor
& rBColor
, double& /*rfOpacity*/) const
233 // no color at all, done
234 if (mnColorStops
.empty())
237 // just single color, done
238 if (mnColorStops
.size() < 2)
240 rBColor
= mnColorStops
.front().getStopColor();
244 // texture-back-transform X/Y -> t [0.0..1.0] and determine color
245 const double fScaler(basegfx::utils::getLinearGradientAlpha(rUV
, maGradientInfo
));
246 rBColor
= mnColorStops
.getInterpolatedBColor(fScaler
, mnRequestedSteps
, maLastColorStopRange
);
249 GeoTexSvxGradientAxial::GeoTexSvxGradientAxial(
250 const basegfx::B2DRange
& rDefinitionRange
,
251 const basegfx::B2DRange
& rOutputRange
,
252 sal_uInt32 nRequestedSteps
,
253 const basegfx::BColorStops
& rColorStops
,
256 : GeoTexSvxGradient(rDefinitionRange
, nRequestedSteps
, rColorStops
, fBorder
)
260 // ARGH! GradientAxial uses the ColorStops in reverse order compared
261 // with the other gradients. Either stay 'thinking reverse' for the
262 // rest of time or adapt it here and go in same order as the other five,
263 // so unifications/tooling will be possible
264 mnColorStops
.reverseColorStops();
266 maGradientInfo
= basegfx::utils::createAxialODFGradientInfo(
272 if(rDefinitionRange
!= rOutputRange
)
274 basegfx::B2DRange
aInvOutputRange(rOutputRange
);
276 aInvOutputRange
.transform(maGradientInfo
.getBackTextureTransform());
277 mfUnitMinX
= aInvOutputRange
.getMinX();
278 mfUnitWidth
= aInvOutputRange
.getWidth();
282 GeoTexSvxGradientAxial::~GeoTexSvxGradientAxial()
286 void GeoTexSvxGradientAxial::appendTransformationsAndColors(
287 std::function
<void(const basegfx::B2DHomMatrix
& rMatrix
, const basegfx::BColor
& rColor
)> aCallback
)
289 // no color at all, done
290 if (mnColorStops
.empty())
293 // only one color, done
294 if (mnColorStops
.size() < 2)
297 // check if we need last-ColorStop-correction
298 const bool bPenultimateUsed(mnColorStops
.checkPenultimate());
300 if (bPenultimateUsed
)
302 // temporarily add a ColorStop entry
303 mnColorStops
.emplace_back(1.0, mnColorStops
.back().getStopColor());
306 // prepare unit range transform
307 basegfx::B2DHomMatrix aPattern
;
309 // bring in X from unit circle [-1, -1, 1, 1] to unit range [0, 0, 1, 1]
310 aPattern
.scale(0.5, 1.0);
311 aPattern
.translate(0.5, 0.0);
313 // scale/translate in X
314 aPattern
.scale(mfUnitWidth
, 1.0);
315 aPattern
.translate(mfUnitMinX
, 0.0);
317 // outer loop over ColorStops, each is from cs_l to cs_r
318 for (auto cs_l(mnColorStops
.begin()), cs_r(cs_l
+ 1); cs_r
!= mnColorStops
.end(); cs_l
++, cs_r
++)
321 const double fOffsetStart(cs_l
->getStopOffset());
322 const double fOffsetEnd(cs_r
->getStopOffset());
324 // same offset, empty BColorStopRange, continue with next step
325 if (basegfx::fTools::equal(fOffsetStart
, fOffsetEnd
))
328 // get colors & calculate steps
329 const basegfx::BColor
aCStart(cs_l
->getStopColor());
330 const basegfx::BColor
aCEnd(cs_r
->getStopColor());
331 const sal_uInt32
nSteps(basegfx::utils::calculateNumberOfSteps(
332 maGradientInfo
.getRequestedSteps(), aCStart
, aCEnd
));
334 // calculate StripeWidth
335 // nSteps is >= 1, see getRequestedSteps, so no check needed here
336 const double fStripeWidth((fOffsetEnd
- fOffsetStart
) / nSteps
);
338 // for the 1st color range we do not need to create the 1st step, see above
339 const sal_uInt32
nStartInnerLoop(cs_l
== mnColorStops
.begin() ? 1 : 0);
341 for (sal_uInt32
innerLoop(nStartInnerLoop
); innerLoop
< nSteps
; innerLoop
++)
343 // calculate pos in Y
344 const double fPos(fOffsetStart
+ (fStripeWidth
* innerLoop
));
346 // already centered in Y on X-Axis, just scale in Y
347 basegfx::B2DHomMatrix
aNew(aPattern
);
348 aNew
.scale(1.0, 1.0 - fPos
);
350 // set and add at target
352 maGradientInfo
.getTextureTransform() * aNew
,
353 1 == nSteps
? aCStart
: basegfx::BColor(interpolate(aCStart
, aCEnd
, double(innerLoop
) / double(nSteps
- 1))));
357 if (bPenultimateUsed
)
359 // correct temporary change
360 mnColorStops
.pop_back();
364 void GeoTexSvxGradientAxial::modifyBColor(const basegfx::B2DPoint
& rUV
, basegfx::BColor
& rBColor
, double& /*rfOpacity*/) const
366 // no color at all, done
367 if (mnColorStops
.empty())
370 // just single color, done
371 if (mnColorStops
.size() < 2)
373 // we use the reverse ColorSteps here, so use front value
374 rBColor
= mnColorStops
.front().getStopColor();
378 // texture-back-transform X/Y -> t [0.0..1.0] and determine color
379 const double fScaler(basegfx::utils::getAxialGradientAlpha(rUV
, maGradientInfo
));
381 // we use the reverse ColorSteps here, so mirror scaler value
382 rBColor
= mnColorStops
.getInterpolatedBColor(1.0 - fScaler
, mnRequestedSteps
, maLastColorStopRange
);
386 GeoTexSvxGradientRadial::GeoTexSvxGradientRadial(
387 const basegfx::B2DRange
& rDefinitionRange
,
388 sal_uInt32 nRequestedSteps
,
389 const basegfx::BColorStops
& rColorStops
,
393 : GeoTexSvxGradient(rDefinitionRange
, nRequestedSteps
, rColorStops
, fBorder
)
395 maGradientInfo
= basegfx::utils::createRadialODFGradientInfo(
397 basegfx::B2DVector(fOffsetX
,fOffsetY
),
402 GeoTexSvxGradientRadial::~GeoTexSvxGradientRadial()
406 void GeoTexSvxGradientRadial::appendTransformationsAndColors(
407 std::function
<void(const basegfx::B2DHomMatrix
& rMatrix
, const basegfx::BColor
& rColor
)> aCallback
)
409 // no color at all, done
410 if (mnColorStops
.empty())
413 // only one color, done
414 if (mnColorStops
.size() < 2)
417 // check if we need last-ColorStop-correction
418 const bool bPenultimateUsed(mnColorStops
.checkPenultimate());
420 if (bPenultimateUsed
)
422 // temporarily add a ColorStop entry
423 mnColorStops
.emplace_back(1.0, mnColorStops
.back().getStopColor());
426 // outer loop over ColorStops, each is from cs_l to cs_r
427 for (auto cs_l(mnColorStops
.begin()), cs_r(cs_l
+ 1); cs_r
!= mnColorStops
.end(); cs_l
++, cs_r
++)
430 const double fOffsetStart(cs_l
->getStopOffset());
431 const double fOffsetEnd(cs_r
->getStopOffset());
433 // same offset, empty BColorStopRange, continue with next step
434 if (basegfx::fTools::equal(fOffsetStart
, fOffsetEnd
))
437 // get colors & calculate steps
438 const basegfx::BColor
aCStart(cs_l
->getStopColor());
439 const basegfx::BColor
aCEnd(cs_r
->getStopColor());
440 const sal_uInt32
nSteps(basegfx::utils::calculateNumberOfSteps(
441 maGradientInfo
.getRequestedSteps(), aCStart
, aCEnd
));
443 // calculate StripeWidth
444 const double fStripeWidth((fOffsetEnd
- fOffsetStart
) / nSteps
);
446 // get correct start for inner loop (see above)
447 const sal_uInt32
nStartInnerLoop(cs_l
== mnColorStops
.begin() ? 1 : 0);
449 for (sal_uInt32
innerLoop(nStartInnerLoop
); innerLoop
< nSteps
; innerLoop
++)
451 // calculate size/radius
452 const double fSize(1.0 - (fOffsetStart
+ (fStripeWidth
* innerLoop
)));
454 // set and add at target
456 maGradientInfo
.getTextureTransform() * basegfx::utils::createScaleB2DHomMatrix(fSize
, fSize
),
457 1 == nSteps
? aCStart
: basegfx::BColor(interpolate(aCStart
, aCEnd
, double(innerLoop
) / double(nSteps
- 1))));
461 if (bPenultimateUsed
)
463 // correct temporary change
464 mnColorStops
.pop_back();
468 void GeoTexSvxGradientRadial::modifyBColor(const basegfx::B2DPoint
& rUV
, basegfx::BColor
& rBColor
, double& /*rfOpacity*/) const
470 // no color at all, done
471 if (mnColorStops
.empty())
474 // just single color, done
475 if (mnColorStops
.size() < 2)
477 rBColor
= mnColorStops
.front().getStopColor();
481 // texture-back-transform X/Y -> t [0.0..1.0] and determine color
482 const double fScaler(basegfx::utils::getRadialGradientAlpha(rUV
, maGradientInfo
));
483 rBColor
= mnColorStops
.getInterpolatedBColor(fScaler
, mnRequestedSteps
, maLastColorStopRange
);
487 GeoTexSvxGradientElliptical::GeoTexSvxGradientElliptical(
488 const basegfx::B2DRange
& rDefinitionRange
,
489 sal_uInt32 nRequestedSteps
,
490 const basegfx::BColorStops
& rColorStops
,
495 : GeoTexSvxGradient(rDefinitionRange
, nRequestedSteps
, rColorStops
, fBorder
)
497 maGradientInfo
= basegfx::utils::createEllipticalODFGradientInfo(
499 basegfx::B2DVector(fOffsetX
,fOffsetY
),
505 GeoTexSvxGradientElliptical::~GeoTexSvxGradientElliptical()
509 void GeoTexSvxGradientElliptical::appendTransformationsAndColors(
510 std::function
<void(const basegfx::B2DHomMatrix
& rMatrix
, const basegfx::BColor
& rColor
)> aCallback
)
512 // no color at all, done
513 if (mnColorStops
.empty())
516 // only one color, done
517 if (mnColorStops
.size() < 2)
520 // check if we need last-ColorStop-correction
521 const bool bPenultimateUsed(mnColorStops
.checkPenultimate());
523 if (bPenultimateUsed
)
525 // temporarily add a ColorStop entry
526 mnColorStops
.emplace_back(1.0, mnColorStops
.back().getStopColor());
529 // prepare vars dependent on aspect ratio
530 const double fAR(maGradientInfo
.getAspectRatio());
531 const bool bMTO(fAR
> 1.0);
533 // outer loop over ColorStops, each is from cs_l to cs_r
534 for (auto cs_l(mnColorStops
.begin()), cs_r(cs_l
+ 1); cs_r
!= mnColorStops
.end(); cs_l
++, cs_r
++)
537 const double fOffsetStart(cs_l
->getStopOffset());
538 const double fOffsetEnd(cs_r
->getStopOffset());
540 // same offset, empty BColorStopRange, continue with next step
541 if (basegfx::fTools::equal(fOffsetStart
, fOffsetEnd
))
544 // get colors & calculate steps
545 const basegfx::BColor
aCStart(cs_l
->getStopColor());
546 const basegfx::BColor
aCEnd(cs_r
->getStopColor());
547 const sal_uInt32
nSteps(basegfx::utils::calculateNumberOfSteps(
548 maGradientInfo
.getRequestedSteps(), aCStart
, aCEnd
));
550 // calculate StripeWidth
551 const double fStripeWidth((fOffsetEnd
- fOffsetStart
) / nSteps
);
553 // get correct start for inner loop (see above)
554 const sal_uInt32
nStartInnerLoop(cs_l
== mnColorStops
.begin() ? 1 : 0);
556 for (sal_uInt32
innerLoop(nStartInnerLoop
); innerLoop
< nSteps
; innerLoop
++)
558 // calculate offset position for entry
559 const double fSize(fOffsetStart
+ (fStripeWidth
* innerLoop
));
561 // set and add at target
563 maGradientInfo
.getTextureTransform()
564 * basegfx::utils::createScaleB2DHomMatrix(
565 1.0 - (bMTO
? fSize
/ fAR
: fSize
),
566 1.0 - (bMTO
? fSize
: fSize
* fAR
)),
567 1 == nSteps
? aCStart
: basegfx::BColor(interpolate(aCStart
, aCEnd
, double(innerLoop
) / double(nSteps
- 1))));
571 if (bPenultimateUsed
)
573 // correct temporary change
574 mnColorStops
.pop_back();
578 void GeoTexSvxGradientElliptical::modifyBColor(const basegfx::B2DPoint
& rUV
, basegfx::BColor
& rBColor
, double& /*rfOpacity*/) const
580 // no color at all, done
581 if (mnColorStops
.empty())
584 // just single color, done
585 if (mnColorStops
.size() < 2)
587 rBColor
= mnColorStops
.front().getStopColor();
591 // texture-back-transform X/Y -> t [0.0..1.0] and determine color
592 const double fScaler(basegfx::utils::getEllipticalGradientAlpha(rUV
, maGradientInfo
));
593 rBColor
= mnColorStops
.getInterpolatedBColor(fScaler
, mnRequestedSteps
, maLastColorStopRange
);
597 GeoTexSvxGradientSquare::GeoTexSvxGradientSquare(
598 const basegfx::B2DRange
& rDefinitionRange
,
599 sal_uInt32 nRequestedSteps
,
600 const basegfx::BColorStops
& rColorStops
,
605 : GeoTexSvxGradient(rDefinitionRange
, nRequestedSteps
, rColorStops
, fBorder
)
607 maGradientInfo
= basegfx::utils::createSquareODFGradientInfo(
609 basegfx::B2DVector(fOffsetX
,fOffsetY
),
615 GeoTexSvxGradientSquare::~GeoTexSvxGradientSquare()
619 void GeoTexSvxGradientSquare::appendTransformationsAndColors(
620 std::function
<void(const basegfx::B2DHomMatrix
& rMatrix
, const basegfx::BColor
& rColor
)> aCallback
)
622 // no color at all, done
623 if (mnColorStops
.empty())
626 // only one color, done
627 if (mnColorStops
.size() < 2)
630 // check if we need last-ColorStop-correction
631 const bool bPenultimateUsed(mnColorStops
.checkPenultimate());
633 if (bPenultimateUsed
)
635 // temporarily add a ColorStop entry
636 mnColorStops
.emplace_back(1.0, mnColorStops
.back().getStopColor());
639 // outer loop over ColorStops, each is from cs_l to cs_r
640 for (auto cs_l(mnColorStops
.begin()), cs_r(cs_l
+ 1); cs_r
!= mnColorStops
.end(); cs_l
++, cs_r
++)
643 const double fOffsetStart(cs_l
->getStopOffset());
644 const double fOffsetEnd(cs_r
->getStopOffset());
646 // same offset, empty BColorStopRange, continue with next step
647 if (basegfx::fTools::equal(fOffsetStart
, fOffsetEnd
))
650 // get colors & calculate steps
651 const basegfx::BColor
aCStart(cs_l
->getStopColor());
652 const basegfx::BColor
aCEnd(cs_r
->getStopColor());
653 const sal_uInt32
nSteps(basegfx::utils::calculateNumberOfSteps(
654 maGradientInfo
.getRequestedSteps(), aCStart
, aCEnd
));
656 // calculate StripeWidth
657 const double fStripeWidth((fOffsetEnd
- fOffsetStart
) / nSteps
);
659 // get correct start for inner loop (see above)
660 const sal_uInt32
nStartInnerLoop(cs_l
== mnColorStops
.begin() ? 1 : 0);
662 for (sal_uInt32
innerLoop(nStartInnerLoop
); innerLoop
< nSteps
; innerLoop
++)
664 // calculate size/radius
665 const double fSize(1.0 - (fOffsetStart
+ (fStripeWidth
* innerLoop
)));
667 // set and add at target
669 maGradientInfo
.getTextureTransform() * basegfx::utils::createScaleB2DHomMatrix(fSize
, fSize
),
670 1 == nSteps
? aCStart
: basegfx::BColor(interpolate(aCStart
, aCEnd
, double(innerLoop
) / double(nSteps
- 1))));
674 if (bPenultimateUsed
)
676 // correct temporary change
677 mnColorStops
.pop_back();
681 void GeoTexSvxGradientSquare::modifyBColor(const basegfx::B2DPoint
& rUV
, basegfx::BColor
& rBColor
, double& /*rfOpacity*/) const
683 // no color at all, done
684 if (mnColorStops
.empty())
687 // just single color, done
688 if (mnColorStops
.size() < 2)
690 rBColor
= mnColorStops
.front().getStopColor();
694 // texture-back-transform X/Y -> t [0.0..1.0] and determine color
695 const double fScaler(basegfx::utils::getSquareGradientAlpha(rUV
, maGradientInfo
));
696 rBColor
= mnColorStops
.getInterpolatedBColor(fScaler
, mnRequestedSteps
, maLastColorStopRange
);
700 GeoTexSvxGradientRect::GeoTexSvxGradientRect(
701 const basegfx::B2DRange
& rDefinitionRange
,
702 sal_uInt32 nRequestedSteps
,
703 const basegfx::BColorStops
& rColorStops
,
708 : GeoTexSvxGradient(rDefinitionRange
, nRequestedSteps
, rColorStops
, fBorder
)
710 maGradientInfo
= basegfx::utils::createRectangularODFGradientInfo(
712 basegfx::B2DVector(fOffsetX
,fOffsetY
),
718 GeoTexSvxGradientRect::~GeoTexSvxGradientRect()
722 void GeoTexSvxGradientRect::appendTransformationsAndColors(
723 std::function
<void(const basegfx::B2DHomMatrix
& rMatrix
, const basegfx::BColor
& rColor
)> aCallback
)
725 // no color at all, done
726 if (mnColorStops
.empty())
729 // only one color, done
730 if (mnColorStops
.size() < 2)
733 // check if we need last-ColorStop-correction
734 const bool bPenultimateUsed(mnColorStops
.checkPenultimate());
736 if (bPenultimateUsed
)
738 // temporarily add a ColorStop entry
739 mnColorStops
.emplace_back(1.0, mnColorStops
.back().getStopColor());
742 // prepare vars dependent on aspect ratio
743 const double fAR(maGradientInfo
.getAspectRatio());
744 const bool bMTO(fAR
> 1.0);
746 // outer loop over ColorStops, each is from cs_l to cs_r
747 for (auto cs_l(mnColorStops
.begin()), cs_r(cs_l
+ 1); cs_r
!= mnColorStops
.end(); cs_l
++, cs_r
++)
750 const double fOffsetStart(cs_l
->getStopOffset());
751 const double fOffsetEnd(cs_r
->getStopOffset());
753 // same offset, empty BColorStopRange, continue with next step
754 if (basegfx::fTools::equal(fOffsetStart
, fOffsetEnd
))
757 // get colors & calculate steps
758 const basegfx::BColor
aCStart(cs_l
->getStopColor());
759 const basegfx::BColor
aCEnd(cs_r
->getStopColor());
760 const sal_uInt32
nSteps(basegfx::utils::calculateNumberOfSteps(
761 maGradientInfo
.getRequestedSteps(), aCStart
, aCEnd
));
763 // calculate StripeWidth
764 const double fStripeWidth((fOffsetEnd
- fOffsetStart
) / nSteps
);
766 // get correct start for inner loop (see above)
767 const sal_uInt32
nStartInnerLoop(cs_l
== mnColorStops
.begin() ? 1 : 0);
769 for (sal_uInt32
innerLoop(nStartInnerLoop
); innerLoop
< nSteps
; innerLoop
++)
771 // calculate offset position for entry
772 const double fSize(fOffsetStart
+ (fStripeWidth
* innerLoop
));
774 // set and add at target
776 maGradientInfo
.getTextureTransform()
777 * basegfx::utils::createScaleB2DHomMatrix(
778 1.0 - (bMTO
? fSize
/ fAR
: fSize
),
779 1.0 - (bMTO
? fSize
: fSize
* fAR
)),
780 1 == nSteps
? aCStart
: basegfx::BColor(interpolate(aCStart
, aCEnd
, double(innerLoop
) / double(nSteps
- 1))));
784 if (bPenultimateUsed
)
786 // correct temporary change
787 mnColorStops
.pop_back();
791 void GeoTexSvxGradientRect::modifyBColor(const basegfx::B2DPoint
& rUV
, basegfx::BColor
& rBColor
, double& /*rfOpacity*/) const
793 // no color at all, done
794 if (mnColorStops
.empty())
797 // just single color, done
798 if (mnColorStops
.size() < 2)
800 rBColor
= mnColorStops
.front().getStopColor();
804 // texture-back-transform X/Y -> t [0.0..1.0] and determine color
805 const double fScaler(basegfx::utils::getRectangularGradientAlpha(rUV
, maGradientInfo
));
806 rBColor
= mnColorStops
.getInterpolatedBColor(fScaler
, mnRequestedSteps
, maLastColorStopRange
);
810 GeoTexSvxHatch::GeoTexSvxHatch(
811 const basegfx::B2DRange
& rDefinitionRange
,
812 const basegfx::B2DRange
& rOutputRange
,
815 : maOutputRange(rOutputRange
),
819 mbDefinitionRangeEqualsOutputRange(rDefinitionRange
== rOutputRange
)
821 double fTargetSizeX(rDefinitionRange
.getWidth());
822 double fTargetSizeY(rDefinitionRange
.getHeight());
823 double fTargetOffsetX(rDefinitionRange
.getMinX());
824 double fTargetOffsetY(rDefinitionRange
.getMinY());
828 // add object expansion
831 const double fAbsCos(fabs(cos(fAngle
)));
832 const double fAbsSin(fabs(sin(fAngle
)));
833 const double fNewX(fTargetSizeX
* fAbsCos
+ fTargetSizeY
* fAbsSin
);
834 const double fNewY(fTargetSizeY
* fAbsCos
+ fTargetSizeX
* fAbsSin
);
835 fTargetOffsetX
-= (fNewX
- fTargetSizeX
) / 2.0;
836 fTargetOffsetY
-= (fNewY
- fTargetSizeY
) / 2.0;
837 fTargetSizeX
= fNewX
;
838 fTargetSizeY
= fNewY
;
841 // add object scale before rotate
842 maTextureTransform
.scale(fTargetSizeX
, fTargetSizeY
);
844 // add texture rotate after scale to keep perpendicular angles
847 basegfx::B2DPoint
aCenter(0.5, 0.5);
848 aCenter
*= maTextureTransform
;
850 maTextureTransform
= basegfx::utils::createRotateAroundPoint(aCenter
, fAngle
)
851 * maTextureTransform
;
854 // add object translate
855 maTextureTransform
.translate(fTargetOffsetX
, fTargetOffsetY
);
857 // prepare height for texture
858 const double fSteps((0.0 != fDistance
) ? fTargetSizeY
/ fDistance
: 10.0);
859 mnSteps
= basegfx::fround(fSteps
+ 0.5);
860 mfDistance
= 1.0 / fSteps
;
863 GeoTexSvxHatch::~GeoTexSvxHatch()
867 bool GeoTexSvxHatch::operator==(const GeoTexSvx
& rGeoTexSvx
) const
869 const GeoTexSvxHatch
* pCompare
= dynamic_cast< const GeoTexSvxHatch
* >(&rGeoTexSvx
);
871 && maOutputRange
== pCompare
->maOutputRange
872 && maTextureTransform
== pCompare
->maTextureTransform
873 && mfDistance
== pCompare
->mfDistance
874 && mfAngle
== pCompare
->mfAngle
875 && mnSteps
== pCompare
->mnSteps
);
878 void GeoTexSvxHatch::appendTransformations(std::vector
< basegfx::B2DHomMatrix
>& rMatrices
)
880 if(mbDefinitionRangeEqualsOutputRange
)
882 // simple hatch where the definition area equals the output area
883 for(sal_uInt32
a(1); a
< mnSteps
; a
++)
886 const double fOffset(mfDistance
* static_cast<double>(a
));
887 basegfx::B2DHomMatrix aNew
;
888 aNew
.set(1, 2, fOffset
);
889 rMatrices
.push_back(maTextureTransform
* aNew
);
894 // output area is different from definition area, back-transform to get
895 // the output area in unit coordinates and fill this with hatch lines
896 // using the settings derived from the definition area
897 basegfx::B2DRange
aBackUnitRange(maOutputRange
);
899 aBackUnitRange
.transform(getBackTextureTransform());
901 // calculate vertical start value and a security maximum integer value to avoid death loops
902 double fStart(basegfx::snapToNearestMultiple(aBackUnitRange
.getMinY(), mfDistance
));
903 const sal_uInt32
nNeededIntegerSteps(basegfx::fround((aBackUnitRange
.getHeight() / mfDistance
) + 0.5));
904 sal_uInt32
nMaxIntegerSteps(std::min(nNeededIntegerSteps
, sal_uInt32(10000)));
906 while(fStart
< aBackUnitRange
.getMaxY() && nMaxIntegerSteps
)
908 // create new transform for
909 basegfx::B2DHomMatrix aNew
;
911 // adapt x scale and position
912 //aNew.scale(aBackUnitRange.getWidth(), 1.0);
913 //aNew.translate(aBackUnitRange.getMinX(), 0.0);
914 aNew
.set(0, 0, aBackUnitRange
.getWidth());
915 aNew
.set(0, 2, aBackUnitRange
.getMinX());
917 // adapt y position to current step
918 aNew
.set(1, 2, fStart
);
919 //aNew.translate(0.0, fStart);
921 // add new transformation
922 rMatrices
.push_back(maTextureTransform
* aNew
);
925 fStart
+= mfDistance
;
931 double GeoTexSvxHatch::getDistanceToHatch(const basegfx::B2DPoint
& rUV
) const
933 // the below is an inlined and optimised version of
934 // const basegfx::B2DPoint aCoor(getBackTextureTransform() * rUV);
935 // return fmod(aCoor.getY(), mfDistance);
937 const basegfx::B2DHomMatrix
& rMat
= getBackTextureTransform();
938 double fX
= rUV
.getX();
939 double fY
= rUV
.getY();
942 rMat
.get(1, 0) * fX
+
943 rMat
.get(1, 1) * fY
+
946 if(!rMat
.isLastLineDefault())
948 const double fOne(1.0);
950 rMat
.get(2, 0) * fX
+
951 rMat
.get(2, 1) * fY
+
954 if(!basegfx::fTools::equalZero(fTempM
) && !basegfx::fTools::equal(fOne
, fTempM
))
960 return fmod(fTempY
, mfDistance
);
963 const basegfx::B2DHomMatrix
& GeoTexSvxHatch::getBackTextureTransform() const
965 if(maBackTextureTransform
.isIdentity())
967 const_cast< GeoTexSvxHatch
* >(this)->maBackTextureTransform
= maTextureTransform
;
968 const_cast< GeoTexSvxHatch
* >(this)->maBackTextureTransform
.invert();
971 return maBackTextureTransform
;
975 GeoTexSvxTiled::GeoTexSvxTiled(
976 const basegfx::B2DRange
& rRange
,
980 mfOffsetX(std::clamp(fOffsetX
, 0.0, 1.0)),
981 mfOffsetY(std::clamp(fOffsetY
, 0.0, 1.0))
983 if(!basegfx::fTools::equalZero(mfOffsetX
))
989 GeoTexSvxTiled::~GeoTexSvxTiled()
993 bool GeoTexSvxTiled::operator==(const GeoTexSvx
& rGeoTexSvx
) const
995 const GeoTexSvxTiled
* pCompare
= dynamic_cast< const GeoTexSvxTiled
* >(&rGeoTexSvx
);
998 && maRange
== pCompare
->maRange
999 && mfOffsetX
== pCompare
->mfOffsetX
1000 && mfOffsetY
== pCompare
->mfOffsetY
);
1003 sal_uInt32
GeoTexSvxTiled::getNumberOfTiles() const
1005 sal_Int32 nTiles
= 0;
1006 iterateTiles([&](double, double) { ++nTiles
; });
1010 void GeoTexSvxTiled::appendTransformations(std::vector
< basegfx::B2DHomMatrix
>& rMatrices
) const
1012 const double fWidth(maRange
.getWidth());
1013 const double fHeight(maRange
.getHeight());
1014 iterateTiles([&](double fPosX
, double fPosY
) {
1015 rMatrices
.push_back(basegfx::utils::createScaleTranslateB2DHomMatrix(
1023 void GeoTexSvxTiled::iterateTiles(std::function
<void(double fPosX
, double fPosY
)> aFunc
) const
1025 const double fWidth(maRange
.getWidth());
1027 if(basegfx::fTools::equalZero(fWidth
))
1030 const double fHeight(maRange
.getHeight());
1032 if(basegfx::fTools::equalZero(fHeight
))
1035 double fStartX(maRange
.getMinX());
1036 double fStartY(maRange
.getMinY());
1040 if(basegfx::fTools::more(fStartX
, 0.0))
1042 const sal_Int32
nDiff(static_cast<sal_Int32
>(floor(fStartX
/ fWidth
)) + 1);
1045 fStartX
-= nDiff
* fWidth
;
1048 if(basegfx::fTools::less(fStartX
+ fWidth
, 0.0))
1050 const sal_Int32
nDiff(static_cast<sal_Int32
>(floor(-fStartX
/ fWidth
)));
1053 fStartX
+= nDiff
* fWidth
;
1056 if(basegfx::fTools::more(fStartY
, 0.0))
1058 const sal_Int32
nDiff(static_cast<sal_Int32
>(floor(fStartY
/ fHeight
)) + 1);
1061 fStartY
-= nDiff
* fHeight
;
1064 if(basegfx::fTools::less(fStartY
+ fHeight
, 0.0))
1066 const sal_Int32
nDiff(static_cast<sal_Int32
>(floor(-fStartY
/ fHeight
)));
1069 fStartY
+= nDiff
* fHeight
;
1072 if(!basegfx::fTools::equalZero(mfOffsetY
))
1074 for(double fPosX(fStartX
); basegfx::fTools::less(fPosX
, 1.0); fPosX
+= fWidth
, nPosX
++)
1076 for(double fPosY((nPosX
% 2) ? fStartY
- fHeight
+ (mfOffsetY
* fHeight
) : fStartY
);
1077 basegfx::fTools::less(fPosY
, 1.0); fPosY
+= fHeight
)
1078 aFunc(fPosX
, fPosY
);
1083 for(double fPosY(fStartY
); basegfx::fTools::less(fPosY
, 1.0); fPosY
+= fHeight
, nPosY
++)
1085 for(double fPosX((nPosY
% 2) ? fStartX
- fWidth
+ (mfOffsetX
* fWidth
) : fStartX
);
1086 basegfx::fTools::less(fPosX
, 1.0); fPosX
+= fWidth
)
1087 aFunc(fPosX
, fPosY
);
1093 } // end of namespace
1095 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */