tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / drawinglayer / source / texture / texture.cxx
blobd0210dbc7b24976f2b14a42c2b8add71c846c8d1
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
22 #include <algorithm>
23 #include <limits>
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
34 namespace
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)
53 return true;
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,
77 double fBorder)
78 : maDefinitionRange(rDefinitionRange)
79 , mnRequestedSteps(nRequestedSteps)
80 , mnColorStops(rColorStops)
81 , mfBorder(fBorder)
82 , maLastColorStopRange()
86 GeoTexSvxGradient::~GeoTexSvxGradient()
90 bool GeoTexSvxGradient::operator==(const GeoTexSvx& rGeoTexSvx) const
92 const GeoTexSvxGradient* pCompare = dynamic_cast< const GeoTexSvxGradient* >(&rGeoTexSvx);
94 return (pCompare
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,
107 double fBorder,
108 double fAngle)
109 : GeoTexSvxGradient(rDefinitionRange, nRequestedSteps, rColorStops, fBorder)
110 , mfUnitMinX(0.0)
111 , mfUnitWidth(1.0)
112 , mfUnitMaxY(1.0)
114 maGradientInfo = basegfx::utils::createLinearODFGradientInfo(
115 rDefinitionRange,
116 nRequestedSteps,
117 fBorder,
118 fAngle);
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())
140 return;
142 // only one color, done
143 if (mnColorStops.size() < 2)
144 return;
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++)
173 // get offsets
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))
179 continue;
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
205 // the full height
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
219 rCallback(
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())
236 return;
238 // just single color, done
239 if (mnColorStops.size() < 2)
241 rBColor = mnColorStops.front().getStopColor();
242 return;
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,
255 double fBorder,
256 double fAngle)
257 : GeoTexSvxGradient(rDefinitionRange, nRequestedSteps, rColorStops, fBorder)
258 , mfUnitMinX(0.0)
259 , mfUnitWidth(1.0)
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(
268 rDefinitionRange,
269 nRequestedSteps,
270 fBorder,
271 fAngle);
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())
292 return;
294 // only one color, done
295 if (mnColorStops.size() < 2)
296 return;
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++)
321 // get offsets
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))
327 continue;
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
352 rCallback(
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())
369 return;
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();
376 return;
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,
391 double fBorder,
392 double fOffsetX,
393 double fOffsetY)
394 : GeoTexSvxGradient(rDefinitionRange, nRequestedSteps, rColorStops, fBorder)
396 maGradientInfo = basegfx::utils::createRadialODFGradientInfo(
397 rDefinitionRange,
398 basegfx::B2DVector(fOffsetX,fOffsetY),
399 nRequestedSteps,
400 fBorder);
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())
412 return;
414 // only one color, done
415 if (mnColorStops.size() < 2)
416 return;
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++)
430 // get offsets
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))
436 continue;
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
456 rCallback(
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())
473 return;
475 // just single color, done
476 if (mnColorStops.size() < 2)
478 rBColor = mnColorStops.front().getStopColor();
479 return;
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,
492 double fBorder,
493 double fOffsetX,
494 double fOffsetY,
495 double fAngle)
496 : GeoTexSvxGradient(rDefinitionRange, nRequestedSteps, rColorStops, fBorder)
498 maGradientInfo = basegfx::utils::createEllipticalODFGradientInfo(
499 rDefinitionRange,
500 basegfx::B2DVector(fOffsetX,fOffsetY),
501 nRequestedSteps,
502 fBorder,
503 fAngle);
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())
515 return;
517 // only one color, done
518 if (mnColorStops.size() < 2)
519 return;
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++)
537 // get offsets
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))
543 continue;
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
563 rCallback(
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())
583 return;
585 // just single color, done
586 if (mnColorStops.size() < 2)
588 rBColor = mnColorStops.front().getStopColor();
589 return;
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,
602 double fBorder,
603 double fOffsetX,
604 double fOffsetY,
605 double fAngle)
606 : GeoTexSvxGradient(rDefinitionRange, nRequestedSteps, rColorStops, fBorder)
608 maGradientInfo = basegfx::utils::createSquareODFGradientInfo(
609 rDefinitionRange,
610 basegfx::B2DVector(fOffsetX,fOffsetY),
611 nRequestedSteps,
612 fBorder,
613 fAngle);
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())
625 return;
627 // only one color, done
628 if (mnColorStops.size() < 2)
629 return;
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++)
643 // get offsets
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))
649 continue;
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
669 rCallback(
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())
686 return;
688 // just single color, done
689 if (mnColorStops.size() < 2)
691 rBColor = mnColorStops.front().getStopColor();
692 return;
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,
705 double fBorder,
706 double fOffsetX,
707 double fOffsetY,
708 double fAngle)
709 : GeoTexSvxGradient(rDefinitionRange, nRequestedSteps, rColorStops, fBorder)
711 maGradientInfo = basegfx::utils::createRectangularODFGradientInfo(
712 rDefinitionRange,
713 basegfx::B2DVector(fOffsetX,fOffsetY),
714 nRequestedSteps,
715 fBorder,
716 fAngle);
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())
728 return;
730 // only one color, done
731 if (mnColorStops.size() < 2)
732 return;
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++)
750 // get offsets
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))
756 continue;
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
776 rCallback(
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())
796 return;
798 // just single color, done
799 if (mnColorStops.size() < 2)
801 rBColor = mnColorStops.front().getStopColor();
802 return;
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,
814 double fDistance,
815 double fAngle)
816 : maOutputRange(rOutputRange),
817 mfDistance(0.1),
818 mfAngle(fAngle),
819 mnSteps(10),
820 mbDefinitionRangeEqualsOutputRange(rDefinitionRange == rOutputRange)
822 double fTargetSizeX(rDefinitionRange.getWidth());
823 double fTargetSizeY(rDefinitionRange.getHeight());
824 double fTargetOffsetX(rDefinitionRange.getMinX());
825 double fTargetOffsetY(rDefinitionRange.getMinY());
827 fAngle = -fAngle;
829 // add object expansion
830 if(0.0 != fAngle)
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
846 if(0.0 != fAngle)
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);
871 return (pCompare
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++)
886 // create matrix
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);
893 else
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);
925 // next step
926 fStart += mfDistance;
927 nMaxIntegerSteps--;
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();
942 double fTempY(
943 rMat.get(1, 0) * fX +
944 rMat.get(1, 1) * fY +
945 rMat.get(1, 2));
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,
964 double fOffsetX,
965 double fOffsetY)
966 : maRange(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))
972 mfOffsetY = 0.0;
976 GeoTexSvxTiled::~GeoTexSvxTiled()
980 bool GeoTexSvxTiled::operator==(const GeoTexSvx& rGeoTexSvx) const
982 const GeoTexSvxTiled* pCompare = dynamic_cast< const GeoTexSvxTiled* >(&rGeoTexSvx);
984 return (pCompare
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; });
994 return 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(
1003 fWidth,
1004 fHeight,
1005 fPosX,
1006 fPosY));
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))
1015 return;
1017 const double fHeight(maRange.getHeight());
1019 if(basegfx::fTools::equalZero(fHeight))
1020 return;
1022 double fStartX(maRange.getMinX());
1023 double fStartY(maRange.getMinY());
1024 sal_Int32 nPosX(0);
1025 sal_Int32 nPosY(0);
1027 if(fStartX > 0.0)
1029 const sal_Int32 nDiff(static_cast<sal_Int32>(floor(fStartX / fWidth)) + 1);
1031 nPosX -= nDiff;
1032 fStartX -= nDiff * fWidth;
1035 if((fStartX + fWidth) < 0.0)
1037 const sal_Int32 nDiff(static_cast<sal_Int32>(floor(-fStartX / fWidth)));
1039 nPosX += nDiff;
1040 fStartX += nDiff * fWidth;
1043 if(fStartY > 0.0)
1045 const sal_Int32 nDiff(static_cast<sal_Int32>(floor(fStartY / fHeight)) + 1);
1047 nPosY -= nDiff;
1048 fStartY -= nDiff * fHeight;
1051 if((fStartY + fHeight) < 0.0)
1053 const sal_Int32 nDiff(static_cast<sal_Int32>(floor(-fStartY / fHeight)));
1055 nPosY += nDiff;
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);
1068 else
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: */