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>
25 #include <texture/texture.hxx>
26 #include <basegfx/numeric/ftools.hxx>
27 #include <basegfx/utils/gradienttools.hxx>
28 #include <basegfx/matrix/b2dhommatrixtools.hxx>
30 #include <comphelper/random.hxx>
32 namespace drawinglayer::texture
36 double getRandomColorRange()
38 return comphelper::rng::uniform_real_distribution(0.0, nextafter(1.0, DBL_MAX
));
42 GeoTexSvx::GeoTexSvx()
46 GeoTexSvx::~GeoTexSvx()
50 bool GeoTexSvx::operator==(const GeoTexSvx
& /*rGeoTexSvx*/) const
52 // default implementation says yes (no data -> no difference)
56 void GeoTexSvx::modifyBColor(const basegfx::B2DPoint
& /*rUV*/, basegfx::BColor
& rBColor
, double& /*rfOpacity*/) const
58 // base implementation creates random color (for testing only, may also be pure virtual)
59 rBColor
.setRed(getRandomColorRange());
60 rBColor
.setGreen(getRandomColorRange());
61 rBColor
.setBlue(getRandomColorRange());
64 void GeoTexSvx::modifyOpacity(const basegfx::B2DPoint
& rUV
, double& rfOpacity
) const
66 // base implementation uses inverse of luminance of solved color (for testing only, may also be pure virtual)
67 basegfx::BColor aBaseColor
;
68 modifyBColor(rUV
, aBaseColor
, rfOpacity
);
69 rfOpacity
= 1.0 - aBaseColor
.luminance();
73 GeoTexSvxGradient::GeoTexSvxGradient(
74 const basegfx::B2DRange
& rDefinitionRange
,
75 sal_uInt32 nRequestedSteps
,
76 const basegfx::BColorStops
& rColorStops
,
78 : maDefinitionRange(rDefinitionRange
)
79 , mnRequestedSteps(nRequestedSteps
)
80 , mnColorStops(rColorStops
)
82 , maLastColorStopRange()
86 GeoTexSvxGradient::~GeoTexSvxGradient()
90 bool GeoTexSvxGradient::operator==(const GeoTexSvx
& rGeoTexSvx
) const
92 const GeoTexSvxGradient
* pCompare
= dynamic_cast< const GeoTexSvxGradient
* >(&rGeoTexSvx
);
95 && maGradientInfo
== pCompare
->maGradientInfo
96 && maDefinitionRange
== pCompare
->maDefinitionRange
97 && mnRequestedSteps
== pCompare
->mnRequestedSteps
98 && mnColorStops
== pCompare
->mnColorStops
99 && mfBorder
== pCompare
->mfBorder
);
102 GeoTexSvxGradientLinear::GeoTexSvxGradientLinear(
103 const basegfx::B2DRange
& rDefinitionRange
,
104 const basegfx::B2DRange
& rOutputRange
,
105 sal_uInt32 nRequestedSteps
,
106 const basegfx::BColorStops
& rColorStops
,
109 : GeoTexSvxGradient(rDefinitionRange
, nRequestedSteps
, rColorStops
, fBorder
)
114 maGradientInfo
= basegfx::utils::createLinearODFGradientInfo(
120 if(rDefinitionRange
!= rOutputRange
)
122 basegfx::B2DRange
aInvOutputRange(rOutputRange
);
124 aInvOutputRange
.transform(maGradientInfo
.getBackTextureTransform());
125 mfUnitMinX
= aInvOutputRange
.getMinX();
126 mfUnitWidth
= aInvOutputRange
.getWidth();
127 mfUnitMaxY
= aInvOutputRange
.getMaxY();
131 GeoTexSvxGradientLinear::~GeoTexSvxGradientLinear()
135 void GeoTexSvxGradientLinear::appendTransformationsAndColors(
136 const std::function
<void(const basegfx::B2DHomMatrix
& rMatrix
, const basegfx::BColor
& rColor
)>& rCallback
)
138 // no color at all, done
139 if (mnColorStops
.empty())
142 // only one color, done
143 if (mnColorStops
.size() < 2)
146 // check if we need last-ColorStop-correction
147 const bool bPenultimateUsed(mnColorStops
.checkPenultimate());
149 if (bPenultimateUsed
)
151 // Here we need to temporarily add a ColorStop entry with the
152 // same color as the last entry to correctly 'close' the
153 // created gradient geometry.
154 // The simplest way is to temporarily add an entry to the local
155 // ColorStops for this at 1.0 (using same color)
156 mnColorStops
.emplace_back(1.0, mnColorStops
.back().getStopColor());
159 // prepare unit range transform
160 basegfx::B2DHomMatrix aPattern
;
162 // bring from unit circle [-1, -1, 1, 1] to unit range [0, 0, 1, 1]
163 aPattern
.scale(0.5, 0.5);
164 aPattern
.translate(0.5, 0.5);
166 // scale and translate in X
167 aPattern
.scale(mfUnitWidth
, 1.0);
168 aPattern
.translate(mfUnitMinX
, 0.0);
170 // outer loop over ColorStops, each is from cs_l to cs_r
171 for (auto cs_l(mnColorStops
.begin()), cs_r(cs_l
+ 1); cs_r
!= mnColorStops
.end(); cs_l
++, cs_r
++)
174 const double fOffsetStart(cs_l
->getStopOffset());
175 const double fOffsetEnd(cs_r
->getStopOffset());
177 // same offset, empty BColorStopRange, continue with next step
178 if (basegfx::fTools::equal(fOffsetStart
, fOffsetEnd
))
181 // get colors & calculate steps
182 const basegfx::BColor
aCStart(cs_l
->getStopColor());
183 const basegfx::BColor
aCEnd(cs_r
->getStopColor());
184 const sal_uInt32
nSteps(basegfx::utils::calculateNumberOfSteps(
185 maGradientInfo
.getRequestedSteps(), aCStart
, aCEnd
));
187 // calculate StripeWidth
188 // nSteps is >= 1, see getRequestedSteps, so no check needed here
189 const double fStripeWidth((fOffsetEnd
- fOffsetStart
) / nSteps
);
191 // for the 1st color range we do not need to create the 1st step
192 // since it will be equal to StartColor and thus OuterColor, so
193 // will be painted by the 1st, always-created background polygon
194 // colored using OuterColor.
195 // We *need* to create this though for all 'inner' color ranges
196 // to get a correct start
197 const sal_uInt32
nStartInnerLoop(cs_l
== mnColorStops
.begin() ? 1 : 0);
199 for (sal_uInt32
innerLoop(nStartInnerLoop
); innerLoop
< nSteps
; innerLoop
++)
201 // calculate pos in Y
202 const double fPos(fOffsetStart
+ (fStripeWidth
* innerLoop
));
204 // scale and translate in Y. For GradientLinear we always have
206 double fHeight(1.0 - fPos
);
208 if (mfUnitMaxY
> 1.0)
210 // extend when difference between definition and OutputRange exists
211 fHeight
+= mfUnitMaxY
- 1.0;
214 basegfx::B2DHomMatrix
aNew(aPattern
);
215 aNew
.scale(1.0, fHeight
);
216 aNew
.translate(0.0, fPos
);
218 // set and add at target
220 maGradientInfo
.getTextureTransform() * aNew
,
221 1 == nSteps
? aCStart
: basegfx::BColor(interpolate(aCStart
, aCEnd
, double(innerLoop
) / double(nSteps
- 1))));
225 if (bPenultimateUsed
)
227 // correct temporary change
228 mnColorStops
.pop_back();
232 void GeoTexSvxGradientLinear::modifyBColor(const basegfx::B2DPoint
& rUV
, basegfx::BColor
& rBColor
, double& /*rfOpacity*/) const
234 // no color at all, done
235 if (mnColorStops
.empty())
238 // just single color, done
239 if (mnColorStops
.size() < 2)
241 rBColor
= mnColorStops
.front().getStopColor();
245 // texture-back-transform X/Y -> t [0.0..1.0] and determine color
246 const double fScaler(basegfx::utils::getLinearGradientAlpha(rUV
, maGradientInfo
));
247 rBColor
= mnColorStops
.getInterpolatedBColor(fScaler
, mnRequestedSteps
, maLastColorStopRange
);
250 GeoTexSvxGradientAxial::GeoTexSvxGradientAxial(
251 const basegfx::B2DRange
& rDefinitionRange
,
252 const basegfx::B2DRange
& rOutputRange
,
253 sal_uInt32 nRequestedSteps
,
254 const basegfx::BColorStops
& rColorStops
,
257 : GeoTexSvxGradient(rDefinitionRange
, nRequestedSteps
, rColorStops
, fBorder
)
261 // ARGH! GradientAxial uses the ColorStops in reverse order compared
262 // with the other gradients. Either stay 'thinking reverse' for the
263 // rest of time or adapt it here and go in same order as the other five,
264 // so unifications/tooling will be possible
265 mnColorStops
.reverseColorStops();
267 maGradientInfo
= basegfx::utils::createAxialODFGradientInfo(
273 if(rDefinitionRange
!= rOutputRange
)
275 basegfx::B2DRange
aInvOutputRange(rOutputRange
);
277 aInvOutputRange
.transform(maGradientInfo
.getBackTextureTransform());
278 mfUnitMinX
= aInvOutputRange
.getMinX();
279 mfUnitWidth
= aInvOutputRange
.getWidth();
283 GeoTexSvxGradientAxial::~GeoTexSvxGradientAxial()
287 void GeoTexSvxGradientAxial::appendTransformationsAndColors(
288 const std::function
<void(const basegfx::B2DHomMatrix
& rMatrix
, const basegfx::BColor
& rColor
)>& rCallback
)
290 // no color at all, done
291 if (mnColorStops
.empty())
294 // only one color, done
295 if (mnColorStops
.size() < 2)
298 // check if we need last-ColorStop-correction
299 const bool bPenultimateUsed(mnColorStops
.checkPenultimate());
301 if (bPenultimateUsed
)
303 // temporarily add a ColorStop entry
304 mnColorStops
.emplace_back(1.0, mnColorStops
.back().getStopColor());
307 // prepare unit range transform
308 basegfx::B2DHomMatrix aPattern
;
310 // bring in X from unit circle [-1, -1, 1, 1] to unit range [0, 0, 1, 1]
311 aPattern
.scale(0.5, 1.0);
312 aPattern
.translate(0.5, 0.0);
314 // scale/translate in X
315 aPattern
.scale(mfUnitWidth
, 1.0);
316 aPattern
.translate(mfUnitMinX
, 0.0);
318 // outer loop over ColorStops, each is from cs_l to cs_r
319 for (auto cs_l(mnColorStops
.begin()), cs_r(cs_l
+ 1); cs_r
!= mnColorStops
.end(); cs_l
++, cs_r
++)
322 const double fOffsetStart(cs_l
->getStopOffset());
323 const double fOffsetEnd(cs_r
->getStopOffset());
325 // same offset, empty BColorStopRange, continue with next step
326 if (basegfx::fTools::equal(fOffsetStart
, fOffsetEnd
))
329 // get colors & calculate steps
330 const basegfx::BColor
aCStart(cs_l
->getStopColor());
331 const basegfx::BColor
aCEnd(cs_r
->getStopColor());
332 const sal_uInt32
nSteps(basegfx::utils::calculateNumberOfSteps(
333 maGradientInfo
.getRequestedSteps(), aCStart
, aCEnd
));
335 // calculate StripeWidth
336 // nSteps is >= 1, see getRequestedSteps, so no check needed here
337 const double fStripeWidth((fOffsetEnd
- fOffsetStart
) / nSteps
);
339 // for the 1st color range we do not need to create the 1st step, see above
340 const sal_uInt32
nStartInnerLoop(cs_l
== mnColorStops
.begin() ? 1 : 0);
342 for (sal_uInt32
innerLoop(nStartInnerLoop
); innerLoop
< nSteps
; innerLoop
++)
344 // calculate pos in Y
345 const double fPos(fOffsetStart
+ (fStripeWidth
* innerLoop
));
347 // already centered in Y on X-Axis, just scale in Y
348 basegfx::B2DHomMatrix
aNew(aPattern
);
349 aNew
.scale(1.0, 1.0 - fPos
);
351 // set and add at target
353 maGradientInfo
.getTextureTransform() * aNew
,
354 1 == nSteps
? aCStart
: basegfx::BColor(interpolate(aCStart
, aCEnd
, double(innerLoop
) / double(nSteps
- 1))));
358 if (bPenultimateUsed
)
360 // correct temporary change
361 mnColorStops
.pop_back();
365 void GeoTexSvxGradientAxial::modifyBColor(const basegfx::B2DPoint
& rUV
, basegfx::BColor
& rBColor
, double& /*rfOpacity*/) const
367 // no color at all, done
368 if (mnColorStops
.empty())
371 // just single color, done
372 if (mnColorStops
.size() < 2)
374 // we use the reverse ColorSteps here, so use front value
375 rBColor
= mnColorStops
.front().getStopColor();
379 // texture-back-transform X/Y -> t [0.0..1.0] and determine color
380 const double fScaler(basegfx::utils::getAxialGradientAlpha(rUV
, maGradientInfo
));
382 // we use the reverse ColorSteps here, so mirror scaler value
383 rBColor
= mnColorStops
.getInterpolatedBColor(1.0 - fScaler
, mnRequestedSteps
, maLastColorStopRange
);
387 GeoTexSvxGradientRadial::GeoTexSvxGradientRadial(
388 const basegfx::B2DRange
& rDefinitionRange
,
389 sal_uInt32 nRequestedSteps
,
390 const basegfx::BColorStops
& rColorStops
,
394 : GeoTexSvxGradient(rDefinitionRange
, nRequestedSteps
, rColorStops
, fBorder
)
396 maGradientInfo
= basegfx::utils::createRadialODFGradientInfo(
398 basegfx::B2DVector(fOffsetX
,fOffsetY
),
403 GeoTexSvxGradientRadial::~GeoTexSvxGradientRadial()
407 void GeoTexSvxGradientRadial::appendTransformationsAndColors(
408 const std::function
<void(const basegfx::B2DHomMatrix
& rMatrix
, const basegfx::BColor
& rColor
)>& rCallback
)
410 // no color at all, done
411 if (mnColorStops
.empty())
414 // only one color, done
415 if (mnColorStops
.size() < 2)
418 // check if we need last-ColorStop-correction
419 const bool bPenultimateUsed(mnColorStops
.checkPenultimate());
421 if (bPenultimateUsed
)
423 // temporarily add a ColorStop entry
424 mnColorStops
.emplace_back(1.0, mnColorStops
.back().getStopColor());
427 // outer loop over ColorStops, each is from cs_l to cs_r
428 for (auto cs_l(mnColorStops
.begin()), cs_r(cs_l
+ 1); cs_r
!= mnColorStops
.end(); cs_l
++, cs_r
++)
431 const double fOffsetStart(cs_l
->getStopOffset());
432 const double fOffsetEnd(cs_r
->getStopOffset());
434 // same offset, empty BColorStopRange, continue with next step
435 if (basegfx::fTools::equal(fOffsetStart
, fOffsetEnd
))
438 // get colors & calculate steps
439 const basegfx::BColor
aCStart(cs_l
->getStopColor());
440 const basegfx::BColor
aCEnd(cs_r
->getStopColor());
441 const sal_uInt32
nSteps(basegfx::utils::calculateNumberOfSteps(
442 maGradientInfo
.getRequestedSteps(), aCStart
, aCEnd
));
444 // calculate StripeWidth
445 const double fStripeWidth((fOffsetEnd
- fOffsetStart
) / nSteps
);
447 // get correct start for inner loop (see above)
448 const sal_uInt32
nStartInnerLoop(cs_l
== mnColorStops
.begin() ? 1 : 0);
450 for (sal_uInt32
innerLoop(nStartInnerLoop
); innerLoop
< nSteps
; innerLoop
++)
452 // calculate size/radius
453 const double fSize(1.0 - (fOffsetStart
+ (fStripeWidth
* innerLoop
)));
455 // set and add at target
457 maGradientInfo
.getTextureTransform() * basegfx::utils::createScaleB2DHomMatrix(fSize
, fSize
),
458 1 == nSteps
? aCStart
: basegfx::BColor(interpolate(aCStart
, aCEnd
, double(innerLoop
) / double(nSteps
- 1))));
462 if (bPenultimateUsed
)
464 // correct temporary change
465 mnColorStops
.pop_back();
469 void GeoTexSvxGradientRadial::modifyBColor(const basegfx::B2DPoint
& rUV
, basegfx::BColor
& rBColor
, double& /*rfOpacity*/) const
471 // no color at all, done
472 if (mnColorStops
.empty())
475 // just single color, done
476 if (mnColorStops
.size() < 2)
478 rBColor
= mnColorStops
.front().getStopColor();
482 // texture-back-transform X/Y -> t [0.0..1.0] and determine color
483 const double fScaler(basegfx::utils::getRadialGradientAlpha(rUV
, maGradientInfo
));
484 rBColor
= mnColorStops
.getInterpolatedBColor(fScaler
, mnRequestedSteps
, maLastColorStopRange
);
488 GeoTexSvxGradientElliptical::GeoTexSvxGradientElliptical(
489 const basegfx::B2DRange
& rDefinitionRange
,
490 sal_uInt32 nRequestedSteps
,
491 const basegfx::BColorStops
& rColorStops
,
496 : GeoTexSvxGradient(rDefinitionRange
, nRequestedSteps
, rColorStops
, fBorder
)
498 maGradientInfo
= basegfx::utils::createEllipticalODFGradientInfo(
500 basegfx::B2DVector(fOffsetX
,fOffsetY
),
506 GeoTexSvxGradientElliptical::~GeoTexSvxGradientElliptical()
510 void GeoTexSvxGradientElliptical::appendTransformationsAndColors(
511 const std::function
<void(const basegfx::B2DHomMatrix
& rMatrix
, const basegfx::BColor
& rColor
)>& rCallback
)
513 // no color at all, done
514 if (mnColorStops
.empty())
517 // only one color, done
518 if (mnColorStops
.size() < 2)
521 // check if we need last-ColorStop-correction
522 const bool bPenultimateUsed(mnColorStops
.checkPenultimate());
524 if (bPenultimateUsed
)
526 // temporarily add a ColorStop entry
527 mnColorStops
.emplace_back(1.0, mnColorStops
.back().getStopColor());
530 // prepare vars dependent on aspect ratio
531 const double fAR(maGradientInfo
.getAspectRatio());
532 const bool bMTO(fAR
> 1.0);
534 // outer loop over ColorStops, each is from cs_l to cs_r
535 for (auto cs_l(mnColorStops
.begin()), cs_r(cs_l
+ 1); cs_r
!= mnColorStops
.end(); cs_l
++, cs_r
++)
538 const double fOffsetStart(cs_l
->getStopOffset());
539 const double fOffsetEnd(cs_r
->getStopOffset());
541 // same offset, empty BColorStopRange, continue with next step
542 if (basegfx::fTools::equal(fOffsetStart
, fOffsetEnd
))
545 // get colors & calculate steps
546 const basegfx::BColor
aCStart(cs_l
->getStopColor());
547 const basegfx::BColor
aCEnd(cs_r
->getStopColor());
548 const sal_uInt32
nSteps(basegfx::utils::calculateNumberOfSteps(
549 maGradientInfo
.getRequestedSteps(), aCStart
, aCEnd
));
551 // calculate StripeWidth
552 const double fStripeWidth((fOffsetEnd
- fOffsetStart
) / nSteps
);
554 // get correct start for inner loop (see above)
555 const sal_uInt32
nStartInnerLoop(cs_l
== mnColorStops
.begin() ? 1 : 0);
557 for (sal_uInt32
innerLoop(nStartInnerLoop
); innerLoop
< nSteps
; innerLoop
++)
559 // calculate offset position for entry
560 const double fSize(fOffsetStart
+ (fStripeWidth
* innerLoop
));
562 // set and add at target
564 maGradientInfo
.getTextureTransform()
565 * basegfx::utils::createScaleB2DHomMatrix(
566 1.0 - (bMTO
? fSize
/ fAR
: fSize
),
567 1.0 - (bMTO
? fSize
: fSize
* fAR
)),
568 1 == nSteps
? aCStart
: basegfx::BColor(interpolate(aCStart
, aCEnd
, double(innerLoop
) / double(nSteps
- 1))));
572 if (bPenultimateUsed
)
574 // correct temporary change
575 mnColorStops
.pop_back();
579 void GeoTexSvxGradientElliptical::modifyBColor(const basegfx::B2DPoint
& rUV
, basegfx::BColor
& rBColor
, double& /*rfOpacity*/) const
581 // no color at all, done
582 if (mnColorStops
.empty())
585 // just single color, done
586 if (mnColorStops
.size() < 2)
588 rBColor
= mnColorStops
.front().getStopColor();
592 // texture-back-transform X/Y -> t [0.0..1.0] and determine color
593 const double fScaler(basegfx::utils::getEllipticalGradientAlpha(rUV
, maGradientInfo
));
594 rBColor
= mnColorStops
.getInterpolatedBColor(fScaler
, mnRequestedSteps
, maLastColorStopRange
);
598 GeoTexSvxGradientSquare::GeoTexSvxGradientSquare(
599 const basegfx::B2DRange
& rDefinitionRange
,
600 sal_uInt32 nRequestedSteps
,
601 const basegfx::BColorStops
& rColorStops
,
606 : GeoTexSvxGradient(rDefinitionRange
, nRequestedSteps
, rColorStops
, fBorder
)
608 maGradientInfo
= basegfx::utils::createSquareODFGradientInfo(
610 basegfx::B2DVector(fOffsetX
,fOffsetY
),
616 GeoTexSvxGradientSquare::~GeoTexSvxGradientSquare()
620 void GeoTexSvxGradientSquare::appendTransformationsAndColors(
621 const std::function
<void(const basegfx::B2DHomMatrix
& rMatrix
, const basegfx::BColor
& rColor
)>& rCallback
)
623 // no color at all, done
624 if (mnColorStops
.empty())
627 // only one color, done
628 if (mnColorStops
.size() < 2)
631 // check if we need last-ColorStop-correction
632 const bool bPenultimateUsed(mnColorStops
.checkPenultimate());
634 if (bPenultimateUsed
)
636 // temporarily add a ColorStop entry
637 mnColorStops
.emplace_back(1.0, mnColorStops
.back().getStopColor());
640 // outer loop over ColorStops, each is from cs_l to cs_r
641 for (auto cs_l(mnColorStops
.begin()), cs_r(cs_l
+ 1); cs_r
!= mnColorStops
.end(); cs_l
++, cs_r
++)
644 const double fOffsetStart(cs_l
->getStopOffset());
645 const double fOffsetEnd(cs_r
->getStopOffset());
647 // same offset, empty BColorStopRange, continue with next step
648 if (basegfx::fTools::equal(fOffsetStart
, fOffsetEnd
))
651 // get colors & calculate steps
652 const basegfx::BColor
aCStart(cs_l
->getStopColor());
653 const basegfx::BColor
aCEnd(cs_r
->getStopColor());
654 const sal_uInt32
nSteps(basegfx::utils::calculateNumberOfSteps(
655 maGradientInfo
.getRequestedSteps(), aCStart
, aCEnd
));
657 // calculate StripeWidth
658 const double fStripeWidth((fOffsetEnd
- fOffsetStart
) / nSteps
);
660 // get correct start for inner loop (see above)
661 const sal_uInt32
nStartInnerLoop(cs_l
== mnColorStops
.begin() ? 1 : 0);
663 for (sal_uInt32
innerLoop(nStartInnerLoop
); innerLoop
< nSteps
; innerLoop
++)
665 // calculate size/radius
666 const double fSize(1.0 - (fOffsetStart
+ (fStripeWidth
* innerLoop
)));
668 // set and add at target
670 maGradientInfo
.getTextureTransform() * basegfx::utils::createScaleB2DHomMatrix(fSize
, fSize
),
671 1 == nSteps
? aCStart
: basegfx::BColor(interpolate(aCStart
, aCEnd
, double(innerLoop
) / double(nSteps
- 1))));
675 if (bPenultimateUsed
)
677 // correct temporary change
678 mnColorStops
.pop_back();
682 void GeoTexSvxGradientSquare::modifyBColor(const basegfx::B2DPoint
& rUV
, basegfx::BColor
& rBColor
, double& /*rfOpacity*/) const
684 // no color at all, done
685 if (mnColorStops
.empty())
688 // just single color, done
689 if (mnColorStops
.size() < 2)
691 rBColor
= mnColorStops
.front().getStopColor();
695 // texture-back-transform X/Y -> t [0.0..1.0] and determine color
696 const double fScaler(basegfx::utils::getSquareGradientAlpha(rUV
, maGradientInfo
));
697 rBColor
= mnColorStops
.getInterpolatedBColor(fScaler
, mnRequestedSteps
, maLastColorStopRange
);
701 GeoTexSvxGradientRect::GeoTexSvxGradientRect(
702 const basegfx::B2DRange
& rDefinitionRange
,
703 sal_uInt32 nRequestedSteps
,
704 const basegfx::BColorStops
& rColorStops
,
709 : GeoTexSvxGradient(rDefinitionRange
, nRequestedSteps
, rColorStops
, fBorder
)
711 maGradientInfo
= basegfx::utils::createRectangularODFGradientInfo(
713 basegfx::B2DVector(fOffsetX
,fOffsetY
),
719 GeoTexSvxGradientRect::~GeoTexSvxGradientRect()
723 void GeoTexSvxGradientRect::appendTransformationsAndColors(
724 const std::function
<void(const basegfx::B2DHomMatrix
& rMatrix
, const basegfx::BColor
& rColor
)>& rCallback
)
726 // no color at all, done
727 if (mnColorStops
.empty())
730 // only one color, done
731 if (mnColorStops
.size() < 2)
734 // check if we need last-ColorStop-correction
735 const bool bPenultimateUsed(mnColorStops
.checkPenultimate());
737 if (bPenultimateUsed
)
739 // temporarily add a ColorStop entry
740 mnColorStops
.emplace_back(1.0, mnColorStops
.back().getStopColor());
743 // prepare vars dependent on aspect ratio
744 const double fAR(maGradientInfo
.getAspectRatio());
745 const bool bMTO(fAR
> 1.0);
747 // outer loop over ColorStops, each is from cs_l to cs_r
748 for (auto cs_l(mnColorStops
.begin()), cs_r(cs_l
+ 1); cs_r
!= mnColorStops
.end(); cs_l
++, cs_r
++)
751 const double fOffsetStart(cs_l
->getStopOffset());
752 const double fOffsetEnd(cs_r
->getStopOffset());
754 // same offset, empty BColorStopRange, continue with next step
755 if (basegfx::fTools::equal(fOffsetStart
, fOffsetEnd
))
758 // get colors & calculate steps
759 const basegfx::BColor
aCStart(cs_l
->getStopColor());
760 const basegfx::BColor
aCEnd(cs_r
->getStopColor());
761 const sal_uInt32
nSteps(basegfx::utils::calculateNumberOfSteps(
762 maGradientInfo
.getRequestedSteps(), aCStart
, aCEnd
));
764 // calculate StripeWidth
765 const double fStripeWidth((fOffsetEnd
- fOffsetStart
) / nSteps
);
767 // get correct start for inner loop (see above)
768 const sal_uInt32
nStartInnerLoop(cs_l
== mnColorStops
.begin() ? 1 : 0);
770 for (sal_uInt32
innerLoop(nStartInnerLoop
); innerLoop
< nSteps
; innerLoop
++)
772 // calculate offset position for entry
773 const double fSize(fOffsetStart
+ (fStripeWidth
* innerLoop
));
775 // set and add at target
777 maGradientInfo
.getTextureTransform()
778 * basegfx::utils::createScaleB2DHomMatrix(
779 1.0 - (bMTO
? fSize
/ fAR
: fSize
),
780 1.0 - (bMTO
? fSize
: fSize
* fAR
)),
781 1 == nSteps
? aCStart
: basegfx::BColor(interpolate(aCStart
, aCEnd
, double(innerLoop
) / double(nSteps
- 1))));
785 if (bPenultimateUsed
)
787 // correct temporary change
788 mnColorStops
.pop_back();
792 void GeoTexSvxGradientRect::modifyBColor(const basegfx::B2DPoint
& rUV
, basegfx::BColor
& rBColor
, double& /*rfOpacity*/) const
794 // no color at all, done
795 if (mnColorStops
.empty())
798 // just single color, done
799 if (mnColorStops
.size() < 2)
801 rBColor
= mnColorStops
.front().getStopColor();
805 // texture-back-transform X/Y -> t [0.0..1.0] and determine color
806 const double fScaler(basegfx::utils::getRectangularGradientAlpha(rUV
, maGradientInfo
));
807 rBColor
= mnColorStops
.getInterpolatedBColor(fScaler
, mnRequestedSteps
, maLastColorStopRange
);
811 GeoTexSvxHatch::GeoTexSvxHatch(
812 const basegfx::B2DRange
& rDefinitionRange
,
813 const basegfx::B2DRange
& rOutputRange
,
816 : maOutputRange(rOutputRange
),
820 mbDefinitionRangeEqualsOutputRange(rDefinitionRange
== rOutputRange
)
822 double fTargetSizeX(rDefinitionRange
.getWidth());
823 double fTargetSizeY(rDefinitionRange
.getHeight());
824 double fTargetOffsetX(rDefinitionRange
.getMinX());
825 double fTargetOffsetY(rDefinitionRange
.getMinY());
829 // add object expansion
832 const double fAbsCos(fabs(cos(fAngle
)));
833 const double fAbsSin(fabs(sin(fAngle
)));
834 const double fNewX(fTargetSizeX
* fAbsCos
+ fTargetSizeY
* fAbsSin
);
835 const double fNewY(fTargetSizeY
* fAbsCos
+ fTargetSizeX
* fAbsSin
);
836 fTargetOffsetX
-= (fNewX
- fTargetSizeX
) / 2.0;
837 fTargetOffsetY
-= (fNewY
- fTargetSizeY
) / 2.0;
838 fTargetSizeX
= fNewX
;
839 fTargetSizeY
= fNewY
;
842 // add object scale before rotate
843 maTextureTransform
.scale(fTargetSizeX
, fTargetSizeY
);
845 // add texture rotate after scale to keep perpendicular angles
848 basegfx::B2DPoint
aCenter(0.5, 0.5);
849 aCenter
*= maTextureTransform
;
851 maTextureTransform
= basegfx::utils::createRotateAroundPoint(aCenter
, fAngle
)
852 * maTextureTransform
;
855 // add object translate
856 maTextureTransform
.translate(fTargetOffsetX
, fTargetOffsetY
);
858 // prepare height for texture
859 const double fSteps((0.0 != fDistance
) ? fTargetSizeY
/ fDistance
: 10.0);
860 mnSteps
= basegfx::fround(fSteps
+ 0.5);
861 mfDistance
= 1.0 / fSteps
;
864 GeoTexSvxHatch::~GeoTexSvxHatch()
868 bool GeoTexSvxHatch::operator==(const GeoTexSvx
& rGeoTexSvx
) const
870 const GeoTexSvxHatch
* pCompare
= dynamic_cast< const GeoTexSvxHatch
* >(&rGeoTexSvx
);
872 && maOutputRange
== pCompare
->maOutputRange
873 && maTextureTransform
== pCompare
->maTextureTransform
874 && mfDistance
== pCompare
->mfDistance
875 && mfAngle
== pCompare
->mfAngle
876 && mnSteps
== pCompare
->mnSteps
);
879 void GeoTexSvxHatch::appendTransformations(std::vector
< basegfx::B2DHomMatrix
>& rMatrices
)
881 if(mbDefinitionRangeEqualsOutputRange
)
883 // simple hatch where the definition area equals the output area
884 for(sal_uInt32
a(1); a
< mnSteps
; a
++)
887 const double fOffset(mfDistance
* static_cast<double>(a
));
888 basegfx::B2DHomMatrix aNew
;
889 aNew
.set(1, 2, fOffset
);
890 rMatrices
.push_back(maTextureTransform
* aNew
);
895 // output area is different from definition area, back-transform to get
896 // the output area in unit coordinates and fill this with hatch lines
897 // using the settings derived from the definition area
898 basegfx::B2DRange
aBackUnitRange(maOutputRange
);
900 aBackUnitRange
.transform(getBackTextureTransform());
902 // calculate vertical start value and a security maximum integer value to avoid death loops
903 double fStart(basegfx::snapToNearestMultiple(aBackUnitRange
.getMinY(), mfDistance
));
904 const sal_uInt32
nNeededIntegerSteps(basegfx::fround((aBackUnitRange
.getHeight() / mfDistance
) + 0.5));
905 sal_uInt32
nMaxIntegerSteps(std::min(nNeededIntegerSteps
, sal_uInt32(10000)));
907 while(fStart
< aBackUnitRange
.getMaxY() && nMaxIntegerSteps
)
909 // create new transform for
910 basegfx::B2DHomMatrix aNew
;
912 // adapt x scale and position
913 //aNew.scale(aBackUnitRange.getWidth(), 1.0);
914 //aNew.translate(aBackUnitRange.getMinX(), 0.0);
915 aNew
.set(0, 0, aBackUnitRange
.getWidth());
916 aNew
.set(0, 2, aBackUnitRange
.getMinX());
918 // adapt y position to current step
919 aNew
.set(1, 2, fStart
);
920 //aNew.translate(0.0, fStart);
922 // add new transformation
923 rMatrices
.push_back(maTextureTransform
* aNew
);
926 fStart
+= mfDistance
;
932 double GeoTexSvxHatch::getDistanceToHatch(const basegfx::B2DPoint
& rUV
) const
934 // the below is an inlined and optimised version of
935 // const basegfx::B2DPoint aCoor(getBackTextureTransform() * rUV);
936 // return fmod(aCoor.getY(), mfDistance);
938 const basegfx::B2DHomMatrix
& rMat
= getBackTextureTransform();
939 double fX
= rUV
.getX();
940 double fY
= rUV
.getY();
943 rMat
.get(1, 0) * fX
+
944 rMat
.get(1, 1) * fY
+
947 return fmod(fTempY
, mfDistance
);
950 const basegfx::B2DHomMatrix
& GeoTexSvxHatch::getBackTextureTransform() const
952 if(maBackTextureTransform
.isIdentity())
954 const_cast< GeoTexSvxHatch
* >(this)->maBackTextureTransform
= maTextureTransform
;
955 const_cast< GeoTexSvxHatch
* >(this)->maBackTextureTransform
.invert();
958 return maBackTextureTransform
;
962 GeoTexSvxTiled::GeoTexSvxTiled(
963 const basegfx::B2DRange
& rRange
,
967 mfOffsetX(std::clamp(fOffsetX
, 0.0, 1.0)),
968 mfOffsetY(std::clamp(fOffsetY
, 0.0, 1.0))
970 if(!basegfx::fTools::equalZero(mfOffsetX
))
976 GeoTexSvxTiled::~GeoTexSvxTiled()
980 bool GeoTexSvxTiled::operator==(const GeoTexSvx
& rGeoTexSvx
) const
982 const GeoTexSvxTiled
* pCompare
= dynamic_cast< const GeoTexSvxTiled
* >(&rGeoTexSvx
);
985 && maRange
== pCompare
->maRange
986 && mfOffsetX
== pCompare
->mfOffsetX
987 && mfOffsetY
== pCompare
->mfOffsetY
);
990 sal_uInt32
GeoTexSvxTiled::getNumberOfTiles() const
992 sal_Int32 nTiles
= 0;
993 iterateTiles([&](double, double) { ++nTiles
; });
997 void GeoTexSvxTiled::appendTransformations(std::vector
< basegfx::B2DHomMatrix
>& rMatrices
) const
999 const double fWidth(maRange
.getWidth());
1000 const double fHeight(maRange
.getHeight());
1001 iterateTiles([&](double fPosX
, double fPosY
) {
1002 rMatrices
.push_back(basegfx::utils::createScaleTranslateB2DHomMatrix(
1010 void GeoTexSvxTiled::iterateTiles(std::function
<void(double fPosX
, double fPosY
)> aFunc
) const
1012 const double fWidth(maRange
.getWidth());
1014 if(basegfx::fTools::equalZero(fWidth
))
1017 const double fHeight(maRange
.getHeight());
1019 if(basegfx::fTools::equalZero(fHeight
))
1022 double fStartX(maRange
.getMinX());
1023 double fStartY(maRange
.getMinY());
1029 const sal_Int32
nDiff(static_cast<sal_Int32
>(floor(fStartX
/ fWidth
)) + 1);
1032 fStartX
-= nDiff
* fWidth
;
1035 if((fStartX
+ fWidth
) < 0.0)
1037 const sal_Int32
nDiff(static_cast<sal_Int32
>(floor(-fStartX
/ fWidth
)));
1040 fStartX
+= nDiff
* fWidth
;
1045 const sal_Int32
nDiff(static_cast<sal_Int32
>(floor(fStartY
/ fHeight
)) + 1);
1048 fStartY
-= nDiff
* fHeight
;
1051 if((fStartY
+ fHeight
) < 0.0)
1053 const sal_Int32
nDiff(static_cast<sal_Int32
>(floor(-fStartY
/ fHeight
)));
1056 fStartY
+= nDiff
* fHeight
;
1059 if(!basegfx::fTools::equalZero(mfOffsetY
))
1061 for(double fPosX(fStartX
); basegfx::fTools::less(fPosX
, 1.0); fPosX
+= fWidth
, nPosX
++)
1063 for(double fPosY((nPosX
% 2) ? fStartY
- fHeight
+ (mfOffsetY
* fHeight
) : fStartY
);
1064 basegfx::fTools::less(fPosY
, 1.0); fPosY
+= fHeight
)
1065 aFunc(fPosX
, fPosY
);
1070 for(double fPosY(fStartY
); basegfx::fTools::less(fPosY
, 1.0); fPosY
+= fHeight
, nPosY
++)
1072 for(double fPosX((nPosY
% 2) ? fStartX
- fWidth
+ (mfOffsetX
* fWidth
) : fStartX
);
1073 basegfx::fTools::less(fPosX
, 1.0); fPosX
+= fWidth
)
1074 aFunc(fPosX
, fPosY
);
1080 } // end of namespace
1082 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */