calc: on editing invalidation of view with different zoom is wrong
[LibreOffice.git] / drawinglayer / source / texture / texture.cxx
blob20572d091840441f671b16da6586598cbc2a0929
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>
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
33 namespace
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)
52 return true;
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,
76 double fBorder)
77 : maDefinitionRange(rDefinitionRange)
78 , mnRequestedSteps(nRequestedSteps)
79 , mnColorStops(rColorStops)
80 , mfBorder(fBorder)
81 , maLastColorStopRange()
85 GeoTexSvxGradient::~GeoTexSvxGradient()
89 bool GeoTexSvxGradient::operator==(const GeoTexSvx& rGeoTexSvx) const
91 const GeoTexSvxGradient* pCompare = dynamic_cast< const GeoTexSvxGradient* >(&rGeoTexSvx);
93 return (pCompare
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,
106 double fBorder,
107 double fAngle)
108 : GeoTexSvxGradient(rDefinitionRange, nRequestedSteps, rColorStops, fBorder)
109 , mfUnitMinX(0.0)
110 , mfUnitWidth(1.0)
111 , mfUnitMaxY(1.0)
113 maGradientInfo = basegfx::utils::createLinearODFGradientInfo(
114 rDefinitionRange,
115 nRequestedSteps,
116 fBorder,
117 fAngle);
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())
139 return;
141 // only one color, done
142 if (mnColorStops.size() < 2)
143 return;
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++)
172 // get offsets
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))
178 continue;
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
204 // the full height
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
218 aCallback(
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())
235 return;
237 // just single color, done
238 if (mnColorStops.size() < 2)
240 rBColor = mnColorStops.front().getStopColor();
241 return;
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,
254 double fBorder,
255 double fAngle)
256 : GeoTexSvxGradient(rDefinitionRange, nRequestedSteps, rColorStops, fBorder)
257 , mfUnitMinX(0.0)
258 , mfUnitWidth(1.0)
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(
267 rDefinitionRange,
268 nRequestedSteps,
269 fBorder,
270 fAngle);
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())
291 return;
293 // only one color, done
294 if (mnColorStops.size() < 2)
295 return;
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++)
320 // get offsets
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))
326 continue;
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
351 aCallback(
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())
368 return;
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();
375 return;
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,
390 double fBorder,
391 double fOffsetX,
392 double fOffsetY)
393 : GeoTexSvxGradient(rDefinitionRange, nRequestedSteps, rColorStops, fBorder)
395 maGradientInfo = basegfx::utils::createRadialODFGradientInfo(
396 rDefinitionRange,
397 basegfx::B2DVector(fOffsetX,fOffsetY),
398 nRequestedSteps,
399 fBorder);
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())
411 return;
413 // only one color, done
414 if (mnColorStops.size() < 2)
415 return;
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++)
429 // get offsets
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))
435 continue;
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
455 aCallback(
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())
472 return;
474 // just single color, done
475 if (mnColorStops.size() < 2)
477 rBColor = mnColorStops.front().getStopColor();
478 return;
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,
491 double fBorder,
492 double fOffsetX,
493 double fOffsetY,
494 double fAngle)
495 : GeoTexSvxGradient(rDefinitionRange, nRequestedSteps, rColorStops, fBorder)
497 maGradientInfo = basegfx::utils::createEllipticalODFGradientInfo(
498 rDefinitionRange,
499 basegfx::B2DVector(fOffsetX,fOffsetY),
500 nRequestedSteps,
501 fBorder,
502 fAngle);
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())
514 return;
516 // only one color, done
517 if (mnColorStops.size() < 2)
518 return;
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++)
536 // get offsets
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))
542 continue;
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
562 aCallback(
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())
582 return;
584 // just single color, done
585 if (mnColorStops.size() < 2)
587 rBColor = mnColorStops.front().getStopColor();
588 return;
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,
601 double fBorder,
602 double fOffsetX,
603 double fOffsetY,
604 double fAngle)
605 : GeoTexSvxGradient(rDefinitionRange, nRequestedSteps, rColorStops, fBorder)
607 maGradientInfo = basegfx::utils::createSquareODFGradientInfo(
608 rDefinitionRange,
609 basegfx::B2DVector(fOffsetX,fOffsetY),
610 nRequestedSteps,
611 fBorder,
612 fAngle);
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())
624 return;
626 // only one color, done
627 if (mnColorStops.size() < 2)
628 return;
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++)
642 // get offsets
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))
648 continue;
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
668 aCallback(
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())
685 return;
687 // just single color, done
688 if (mnColorStops.size() < 2)
690 rBColor = mnColorStops.front().getStopColor();
691 return;
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,
704 double fBorder,
705 double fOffsetX,
706 double fOffsetY,
707 double fAngle)
708 : GeoTexSvxGradient(rDefinitionRange, nRequestedSteps, rColorStops, fBorder)
710 maGradientInfo = basegfx::utils::createRectangularODFGradientInfo(
711 rDefinitionRange,
712 basegfx::B2DVector(fOffsetX,fOffsetY),
713 nRequestedSteps,
714 fBorder,
715 fAngle);
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())
727 return;
729 // only one color, done
730 if (mnColorStops.size() < 2)
731 return;
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++)
749 // get offsets
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))
755 continue;
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
775 aCallback(
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())
795 return;
797 // just single color, done
798 if (mnColorStops.size() < 2)
800 rBColor = mnColorStops.front().getStopColor();
801 return;
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,
813 double fDistance,
814 double fAngle)
815 : maOutputRange(rOutputRange),
816 mfDistance(0.1),
817 mfAngle(fAngle),
818 mnSteps(10),
819 mbDefinitionRangeEqualsOutputRange(rDefinitionRange == rOutputRange)
821 double fTargetSizeX(rDefinitionRange.getWidth());
822 double fTargetSizeY(rDefinitionRange.getHeight());
823 double fTargetOffsetX(rDefinitionRange.getMinX());
824 double fTargetOffsetY(rDefinitionRange.getMinY());
826 fAngle = -fAngle;
828 // add object expansion
829 if(0.0 != fAngle)
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
845 if(0.0 != fAngle)
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);
870 return (pCompare
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++)
885 // create matrix
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);
892 else
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);
924 // next step
925 fStart += mfDistance;
926 nMaxIntegerSteps--;
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();
941 double fTempY(
942 rMat.get(1, 0) * fX +
943 rMat.get(1, 1) * fY +
944 rMat.get(1, 2));
946 if(!rMat.isLastLineDefault())
948 const double fOne(1.0);
949 const double fTempM(
950 rMat.get(2, 0) * fX +
951 rMat.get(2, 1) * fY +
952 rMat.get(2, 2));
954 if(!basegfx::fTools::equalZero(fTempM) && !basegfx::fTools::equal(fOne, fTempM))
956 fTempY /= 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,
977 double fOffsetX,
978 double fOffsetY)
979 : maRange(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))
985 mfOffsetY = 0.0;
989 GeoTexSvxTiled::~GeoTexSvxTiled()
993 bool GeoTexSvxTiled::operator==(const GeoTexSvx& rGeoTexSvx) const
995 const GeoTexSvxTiled* pCompare = dynamic_cast< const GeoTexSvxTiled* >(&rGeoTexSvx);
997 return (pCompare
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; });
1007 return 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(
1016 fWidth,
1017 fHeight,
1018 fPosX,
1019 fPosY));
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))
1028 return;
1030 const double fHeight(maRange.getHeight());
1032 if(basegfx::fTools::equalZero(fHeight))
1033 return;
1035 double fStartX(maRange.getMinX());
1036 double fStartY(maRange.getMinY());
1037 sal_Int32 nPosX(0);
1038 sal_Int32 nPosY(0);
1040 if(basegfx::fTools::more(fStartX, 0.0))
1042 const sal_Int32 nDiff(static_cast<sal_Int32>(floor(fStartX / fWidth)) + 1);
1044 nPosX -= nDiff;
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)));
1052 nPosX += nDiff;
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);
1060 nPosY -= nDiff;
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)));
1068 nPosY += nDiff;
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);
1081 else
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: */