LibreOffice Module drawinglayer (master) 1
texture.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#include <sal/config.h>
21
22#include <algorithm>
23#include <limits>
24
25#include <texture/texture.hxx>
29
30#include <comphelper/random.hxx>
31
33{
34 namespace
35 {
36 double getRandomColorRange()
37 {
38 return comphelper::rng::uniform_real_distribution(0.0, nextafter(1.0, DBL_MAX));
39 }
40 }
41
43 {
44 }
45
47 {
48 }
49
50 bool GeoTexSvx::operator==(const GeoTexSvx& /*rGeoTexSvx*/) const
51 {
52 // default implementation says yes (no data -> no difference)
53 return true;
54 }
55
56 void GeoTexSvx::modifyBColor(const basegfx::B2DPoint& /*rUV*/, basegfx::BColor& rBColor, double& /*rfOpacity*/) const
57 {
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());
62 }
63
64 void GeoTexSvx::modifyOpacity(const basegfx::B2DPoint& rUV, double& rfOpacity) const
65 {
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();
70 }
71
72
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()
83 {
84 }
85
87 {
88 }
89
90 bool GeoTexSvxGradient::operator==(const GeoTexSvx& rGeoTexSvx) const
91 {
92 const GeoTexSvxGradient* pCompare = dynamic_cast< const GeoTexSvxGradient* >(&rGeoTexSvx);
93
94 return (pCompare
95 && maGradientInfo == pCompare->maGradientInfo
98 && mnColorStops == pCompare->mnColorStops
99 && mfBorder == pCompare->mfBorder);
100 }
101
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)
113 {
115 rDefinitionRange,
116 nRequestedSteps,
117 fBorder,
118 fAngle);
119
120 if(rDefinitionRange != rOutputRange)
121 {
122 basegfx::B2DRange aInvOutputRange(rOutputRange);
123
124 aInvOutputRange.transform(maGradientInfo.getBackTextureTransform());
125 mfUnitMinX = aInvOutputRange.getMinX();
126 mfUnitWidth = aInvOutputRange.getWidth();
127 mfUnitMaxY = aInvOutputRange.getMaxY();
128 }
129 }
130
132 {
133 }
134
136 std::function<void(const basegfx::B2DHomMatrix& rMatrix, const basegfx::BColor& rColor)> aCallback)
137 {
138 // no color at all, done
139 if (mnColorStops.empty())
140 return;
141
142 // only one color, done
143 if (mnColorStops.size() < 2)
144 return;
145
146 // check if we need last-ColorStop-correction
147 const bool bPenultimateUsed(mnColorStops.checkPenultimate());
148
149 if (bPenultimateUsed)
150 {
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());
157 }
158
159 // prepare unit range transform
160 basegfx::B2DHomMatrix aPattern;
161
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);
165
166 // scale and translate in X
167 aPattern.scale(mfUnitWidth, 1.0);
168 aPattern.translate(mfUnitMinX, 0.0);
169
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++)
172 {
173 // get offsets
174 const double fOffsetStart(cs_l->getStopOffset());
175 const double fOffsetEnd(cs_r->getStopOffset());
176
177 // same offset, empty BColorStopRange, continue with next step
178 if (basegfx::fTools::equal(fOffsetStart, fOffsetEnd))
179 continue;
180
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));
186
187 // calculate StripeWidth
188 // nSteps is >= 1, see getRequestedSteps, so no check needed here
189 const double fStripeWidth((fOffsetEnd - fOffsetStart) / nSteps);
190
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);
198
199 for (sal_uInt32 innerLoop(nStartInnerLoop); innerLoop < nSteps; innerLoop++)
200 {
201 // calculate pos in Y
202 const double fPos(fOffsetStart + (fStripeWidth * innerLoop));
203
204 // scale and translate in Y. For GradientLinear we always have
205 // the full height
206 double fHeight(1.0 - fPos);
207
208 if (mfUnitMaxY > 1.0)
209 {
210 // extend when difference between definition and OutputRange exists
211 fHeight += mfUnitMaxY - 1.0;
212 }
213
214 basegfx::B2DHomMatrix aNew(aPattern);
215 aNew.scale(1.0, fHeight);
216 aNew.translate(0.0, fPos);
217
218 // set and add at target
219 aCallback(
220 maGradientInfo.getTextureTransform() * aNew,
221 1 == nSteps ? aCStart : basegfx::BColor(interpolate(aCStart, aCEnd, double(innerLoop) / double(nSteps - 1))));
222 }
223 }
224
225 if (bPenultimateUsed)
226 {
227 // correct temporary change
228 mnColorStops.pop_back();
229 }
230 }
231
232 void GeoTexSvxGradientLinear::modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& /*rfOpacity*/) const
233 {
234 // no color at all, done
235 if (mnColorStops.empty())
236 return;
237
238 // just single color, done
239 if (mnColorStops.size() < 2)
240 {
241 rBColor = mnColorStops.front().getStopColor();
242 return;
243 }
244
245 // texture-back-transform X/Y -> t [0.0..1.0] and determine color
246 const double fScaler(basegfx::utils::getLinearGradientAlpha(rUV, maGradientInfo));
248 }
249
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)
260 {
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
266
268 rDefinitionRange,
269 nRequestedSteps,
270 fBorder,
271 fAngle);
272
273 if(rDefinitionRange != rOutputRange)
274 {
275 basegfx::B2DRange aInvOutputRange(rOutputRange);
276
277 aInvOutputRange.transform(maGradientInfo.getBackTextureTransform());
278 mfUnitMinX = aInvOutputRange.getMinX();
279 mfUnitWidth = aInvOutputRange.getWidth();
280 }
281 }
282
284 {
285 }
286
288 std::function<void(const basegfx::B2DHomMatrix& rMatrix, const basegfx::BColor& rColor)> aCallback)
289 {
290 // no color at all, done
291 if (mnColorStops.empty())
292 return;
293
294 // only one color, done
295 if (mnColorStops.size() < 2)
296 return;
297
298 // check if we need last-ColorStop-correction
299 const bool bPenultimateUsed(mnColorStops.checkPenultimate());
300
301 if (bPenultimateUsed)
302 {
303 // temporarily add a ColorStop entry
304 mnColorStops.emplace_back(1.0, mnColorStops.back().getStopColor());
305 }
306
307 // prepare unit range transform
308 basegfx::B2DHomMatrix aPattern;
309
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);
313
314 // scale/translate in X
315 aPattern.scale(mfUnitWidth, 1.0);
316 aPattern.translate(mfUnitMinX, 0.0);
317
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++)
320 {
321 // get offsets
322 const double fOffsetStart(cs_l->getStopOffset());
323 const double fOffsetEnd(cs_r->getStopOffset());
324
325 // same offset, empty BColorStopRange, continue with next step
326 if (basegfx::fTools::equal(fOffsetStart, fOffsetEnd))
327 continue;
328
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));
334
335 // calculate StripeWidth
336 // nSteps is >= 1, see getRequestedSteps, so no check needed here
337 const double fStripeWidth((fOffsetEnd - fOffsetStart) / nSteps);
338
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);
341
342 for (sal_uInt32 innerLoop(nStartInnerLoop); innerLoop < nSteps; innerLoop++)
343 {
344 // calculate pos in Y
345 const double fPos(fOffsetStart + (fStripeWidth * innerLoop));
346
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);
350
351 // set and add at target
352 aCallback(
353 maGradientInfo.getTextureTransform() * aNew,
354 1 == nSteps ? aCStart : basegfx::BColor(interpolate(aCStart, aCEnd, double(innerLoop) / double(nSteps - 1))));
355 }
356 }
357
358 if (bPenultimateUsed)
359 {
360 // correct temporary change
361 mnColorStops.pop_back();
362 }
363 }
364
365 void GeoTexSvxGradientAxial::modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& /*rfOpacity*/) const
366 {
367 // no color at all, done
368 if (mnColorStops.empty())
369 return;
370
371 // just single color, done
372 if (mnColorStops.size() < 2)
373 {
374 // we use the reverse ColorSteps here, so use front value
375 rBColor = mnColorStops.front().getStopColor();
376 return;
377 }
378
379 // texture-back-transform X/Y -> t [0.0..1.0] and determine color
380 const double fScaler(basegfx::utils::getAxialGradientAlpha(rUV, maGradientInfo));
381
382 // we use the reverse ColorSteps here, so mirror scaler value
384 }
385
386
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)
395 {
397 rDefinitionRange,
398 basegfx::B2DVector(fOffsetX,fOffsetY),
399 nRequestedSteps,
400 fBorder);
401 }
402
404 {
405 }
406
408 std::function<void(const basegfx::B2DHomMatrix& rMatrix, const basegfx::BColor& rColor)> aCallback)
409 {
410 // no color at all, done
411 if (mnColorStops.empty())
412 return;
413
414 // only one color, done
415 if (mnColorStops.size() < 2)
416 return;
417
418 // check if we need last-ColorStop-correction
419 const bool bPenultimateUsed(mnColorStops.checkPenultimate());
420
421 if (bPenultimateUsed)
422 {
423 // temporarily add a ColorStop entry
424 mnColorStops.emplace_back(1.0, mnColorStops.back().getStopColor());
425 }
426
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++)
429 {
430 // get offsets
431 const double fOffsetStart(cs_l->getStopOffset());
432 const double fOffsetEnd(cs_r->getStopOffset());
433
434 // same offset, empty BColorStopRange, continue with next step
435 if (basegfx::fTools::equal(fOffsetStart, fOffsetEnd))
436 continue;
437
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));
443
444 // calculate StripeWidth
445 const double fStripeWidth((fOffsetEnd - fOffsetStart) / nSteps);
446
447 // get correct start for inner loop (see above)
448 const sal_uInt32 nStartInnerLoop(cs_l == mnColorStops.begin() ? 1 : 0);
449
450 for (sal_uInt32 innerLoop(nStartInnerLoop); innerLoop < nSteps; innerLoop++)
451 {
452 // calculate size/radius
453 const double fSize(1.0 - (fOffsetStart + (fStripeWidth * innerLoop)));
454
455 // set and add at target
456 aCallback(
457 maGradientInfo.getTextureTransform() * basegfx::utils::createScaleB2DHomMatrix(fSize, fSize),
458 1 == nSteps ? aCStart : basegfx::BColor(interpolate(aCStart, aCEnd, double(innerLoop) / double(nSteps - 1))));
459 }
460 }
461
462 if (bPenultimateUsed)
463 {
464 // correct temporary change
465 mnColorStops.pop_back();
466 }
467 }
468
469 void GeoTexSvxGradientRadial::modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& /*rfOpacity*/) const
470 {
471 // no color at all, done
472 if (mnColorStops.empty())
473 return;
474
475 // just single color, done
476 if (mnColorStops.size() < 2)
477 {
478 rBColor = mnColorStops.front().getStopColor();
479 return;
480 }
481
482 // texture-back-transform X/Y -> t [0.0..1.0] and determine color
483 const double fScaler(basegfx::utils::getRadialGradientAlpha(rUV, maGradientInfo));
485 }
486
487
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)
497 {
499 rDefinitionRange,
500 basegfx::B2DVector(fOffsetX,fOffsetY),
501 nRequestedSteps,
502 fBorder,
503 fAngle);
504 }
505
507 {
508 }
509
511 std::function<void(const basegfx::B2DHomMatrix& rMatrix, const basegfx::BColor& rColor)> aCallback)
512 {
513 // no color at all, done
514 if (mnColorStops.empty())
515 return;
516
517 // only one color, done
518 if (mnColorStops.size() < 2)
519 return;
520
521 // check if we need last-ColorStop-correction
522 const bool bPenultimateUsed(mnColorStops.checkPenultimate());
523
524 if (bPenultimateUsed)
525 {
526 // temporarily add a ColorStop entry
527 mnColorStops.emplace_back(1.0, mnColorStops.back().getStopColor());
528 }
529
530 // prepare vars dependent on aspect ratio
531 const double fAR(maGradientInfo.getAspectRatio());
532 const bool bMTO(fAR > 1.0);
533
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++)
536 {
537 // get offsets
538 const double fOffsetStart(cs_l->getStopOffset());
539 const double fOffsetEnd(cs_r->getStopOffset());
540
541 // same offset, empty BColorStopRange, continue with next step
542 if (basegfx::fTools::equal(fOffsetStart, fOffsetEnd))
543 continue;
544
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));
550
551 // calculate StripeWidth
552 const double fStripeWidth((fOffsetEnd - fOffsetStart) / nSteps);
553
554 // get correct start for inner loop (see above)
555 const sal_uInt32 nStartInnerLoop(cs_l == mnColorStops.begin() ? 1 : 0);
556
557 for (sal_uInt32 innerLoop(nStartInnerLoop); innerLoop < nSteps; innerLoop++)
558 {
559 // calculate offset position for entry
560 const double fSize(fOffsetStart + (fStripeWidth * innerLoop));
561
562 // set and add at target
563 aCallback(
564 maGradientInfo.getTextureTransform()
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))));
569 }
570 }
571
572 if (bPenultimateUsed)
573 {
574 // correct temporary change
575 mnColorStops.pop_back();
576 }
577 }
578
579 void GeoTexSvxGradientElliptical::modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& /*rfOpacity*/) const
580 {
581 // no color at all, done
582 if (mnColorStops.empty())
583 return;
584
585 // just single color, done
586 if (mnColorStops.size() < 2)
587 {
588 rBColor = mnColorStops.front().getStopColor();
589 return;
590 }
591
592 // texture-back-transform X/Y -> t [0.0..1.0] and determine color
595 }
596
597
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)
607 {
609 rDefinitionRange,
610 basegfx::B2DVector(fOffsetX,fOffsetY),
611 nRequestedSteps,
612 fBorder,
613 fAngle);
614 }
615
617 {
618 }
619
621 std::function<void(const basegfx::B2DHomMatrix& rMatrix, const basegfx::BColor& rColor)> aCallback)
622 {
623 // no color at all, done
624 if (mnColorStops.empty())
625 return;
626
627 // only one color, done
628 if (mnColorStops.size() < 2)
629 return;
630
631 // check if we need last-ColorStop-correction
632 const bool bPenultimateUsed(mnColorStops.checkPenultimate());
633
634 if (bPenultimateUsed)
635 {
636 // temporarily add a ColorStop entry
637 mnColorStops.emplace_back(1.0, mnColorStops.back().getStopColor());
638 }
639
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++)
642 {
643 // get offsets
644 const double fOffsetStart(cs_l->getStopOffset());
645 const double fOffsetEnd(cs_r->getStopOffset());
646
647 // same offset, empty BColorStopRange, continue with next step
648 if (basegfx::fTools::equal(fOffsetStart, fOffsetEnd))
649 continue;
650
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));
656
657 // calculate StripeWidth
658 const double fStripeWidth((fOffsetEnd - fOffsetStart) / nSteps);
659
660 // get correct start for inner loop (see above)
661 const sal_uInt32 nStartInnerLoop(cs_l == mnColorStops.begin() ? 1 : 0);
662
663 for (sal_uInt32 innerLoop(nStartInnerLoop); innerLoop < nSteps; innerLoop++)
664 {
665 // calculate size/radius
666 const double fSize(1.0 - (fOffsetStart + (fStripeWidth * innerLoop)));
667
668 // set and add at target
669 aCallback(
670 maGradientInfo.getTextureTransform() * basegfx::utils::createScaleB2DHomMatrix(fSize, fSize),
671 1 == nSteps ? aCStart : basegfx::BColor(interpolate(aCStart, aCEnd, double(innerLoop) / double(nSteps - 1))));
672 }
673 }
674
675 if (bPenultimateUsed)
676 {
677 // correct temporary change
678 mnColorStops.pop_back();
679 }
680 }
681
682 void GeoTexSvxGradientSquare::modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& /*rfOpacity*/) const
683 {
684 // no color at all, done
685 if (mnColorStops.empty())
686 return;
687
688 // just single color, done
689 if (mnColorStops.size() < 2)
690 {
691 rBColor = mnColorStops.front().getStopColor();
692 return;
693 }
694
695 // texture-back-transform X/Y -> t [0.0..1.0] and determine color
696 const double fScaler(basegfx::utils::getSquareGradientAlpha(rUV, maGradientInfo));
698 }
699
700
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)
710 {
712 rDefinitionRange,
713 basegfx::B2DVector(fOffsetX,fOffsetY),
714 nRequestedSteps,
715 fBorder,
716 fAngle);
717 }
718
720 {
721 }
722
724 std::function<void(const basegfx::B2DHomMatrix& rMatrix, const basegfx::BColor& rColor)> aCallback)
725 {
726 // no color at all, done
727 if (mnColorStops.empty())
728 return;
729
730 // only one color, done
731 if (mnColorStops.size() < 2)
732 return;
733
734 // check if we need last-ColorStop-correction
735 const bool bPenultimateUsed(mnColorStops.checkPenultimate());
736
737 if (bPenultimateUsed)
738 {
739 // temporarily add a ColorStop entry
740 mnColorStops.emplace_back(1.0, mnColorStops.back().getStopColor());
741 }
742
743 // prepare vars dependent on aspect ratio
744 const double fAR(maGradientInfo.getAspectRatio());
745 const bool bMTO(fAR > 1.0);
746
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++)
749 {
750 // get offsets
751 const double fOffsetStart(cs_l->getStopOffset());
752 const double fOffsetEnd(cs_r->getStopOffset());
753
754 // same offset, empty BColorStopRange, continue with next step
755 if (basegfx::fTools::equal(fOffsetStart, fOffsetEnd))
756 continue;
757
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));
763
764 // calculate StripeWidth
765 const double fStripeWidth((fOffsetEnd - fOffsetStart) / nSteps);
766
767 // get correct start for inner loop (see above)
768 const sal_uInt32 nStartInnerLoop(cs_l == mnColorStops.begin() ? 1 : 0);
769
770 for (sal_uInt32 innerLoop(nStartInnerLoop); innerLoop < nSteps; innerLoop++)
771 {
772 // calculate offset position for entry
773 const double fSize(fOffsetStart + (fStripeWidth * innerLoop));
774
775 // set and add at target
776 aCallback(
777 maGradientInfo.getTextureTransform()
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))));
782 }
783 }
784
785 if (bPenultimateUsed)
786 {
787 // correct temporary change
788 mnColorStops.pop_back();
789 }
790 }
791
792 void GeoTexSvxGradientRect::modifyBColor(const basegfx::B2DPoint& rUV, basegfx::BColor& rBColor, double& /*rfOpacity*/) const
793 {
794 // no color at all, done
795 if (mnColorStops.empty())
796 return;
797
798 // just single color, done
799 if (mnColorStops.size() < 2)
800 {
801 rBColor = mnColorStops.front().getStopColor();
802 return;
803 }
804
805 // texture-back-transform X/Y -> t [0.0..1.0] and determine color
808 }
809
810
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)
821 {
822 double fTargetSizeX(rDefinitionRange.getWidth());
823 double fTargetSizeY(rDefinitionRange.getHeight());
824 double fTargetOffsetX(rDefinitionRange.getMinX());
825 double fTargetOffsetY(rDefinitionRange.getMinY());
826
827 fAngle = -fAngle;
828
829 // add object expansion
830 if(0.0 != fAngle)
831 {
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;
840 }
841
842 // add object scale before rotate
843 maTextureTransform.scale(fTargetSizeX, fTargetSizeY);
844
845 // add texture rotate after scale to keep perpendicular angles
846 if(0.0 != fAngle)
847 {
848 basegfx::B2DPoint aCenter(0.5, 0.5);
849 aCenter *= maTextureTransform;
850
853 }
854
855 // add object translate
856 maTextureTransform.translate(fTargetOffsetX, fTargetOffsetY);
857
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;
862 }
863
865 {
866 }
867
868 bool GeoTexSvxHatch::operator==(const GeoTexSvx& rGeoTexSvx) const
869 {
870 const GeoTexSvxHatch* pCompare = dynamic_cast< const GeoTexSvxHatch* >(&rGeoTexSvx);
871 return (pCompare
872 && maOutputRange == pCompare->maOutputRange
874 && mfDistance == pCompare->mfDistance
875 && mfAngle == pCompare->mfAngle
876 && mnSteps == pCompare->mnSteps);
877 }
878
879 void GeoTexSvxHatch::appendTransformations(std::vector< basegfx::B2DHomMatrix >& rMatrices)
880 {
882 {
883 // simple hatch where the definition area equals the output area
884 for(sal_uInt32 a(1); a < mnSteps; a++)
885 {
886 // create matrix
887 const double fOffset(mfDistance * static_cast<double>(a));
889 aNew.set(1, 2, fOffset);
890 rMatrices.push_back(maTextureTransform * aNew);
891 }
892 }
893 else
894 {
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);
899
900 aBackUnitRange.transform(getBackTextureTransform());
901
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)));
906
907 while(fStart < aBackUnitRange.getMaxY() && nMaxIntegerSteps)
908 {
909 // create new transform for
911
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());
917
918 // adapt y position to current step
919 aNew.set(1, 2, fStart);
920 //aNew.translate(0.0, fStart);
921
922 // add new transformation
923 rMatrices.push_back(maTextureTransform * aNew);
924
925 // next step
926 fStart += mfDistance;
927 nMaxIntegerSteps--;
928 }
929 }
930 }
931
933 {
934 // the below is an inlined and optimised version of
935 // const basegfx::B2DPoint aCoor(getBackTextureTransform() * rUV);
936 // return fmod(aCoor.getY(), mfDistance);
937
939 double fX = rUV.getX();
940 double fY = rUV.getY();
941
942 double fTempY(
943 rMat.get(1, 0) * fX +
944 rMat.get(1, 1) * fY +
945 rMat.get(1, 2));
946
947 return fmod(fTempY, mfDistance);
948 }
949
951 {
953 {
955 const_cast< GeoTexSvxHatch* >(this)->maBackTextureTransform.invert();
956 }
957
959 }
960
961
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))
969 {
971 {
972 mfOffsetY = 0.0;
973 }
974 }
975
977 {
978 }
979
980 bool GeoTexSvxTiled::operator==(const GeoTexSvx& rGeoTexSvx) const
981 {
982 const GeoTexSvxTiled* pCompare = dynamic_cast< const GeoTexSvxTiled* >(&rGeoTexSvx);
983
984 return (pCompare
985 && maRange == pCompare->maRange
986 && mfOffsetX == pCompare->mfOffsetX
987 && mfOffsetY == pCompare->mfOffsetY);
988 }
989
991 {
992 sal_Int32 nTiles = 0;
993 iterateTiles([&](double, double) { ++nTiles; });
994 return nTiles;
995 }
996
997 void GeoTexSvxTiled::appendTransformations(std::vector< basegfx::B2DHomMatrix >& rMatrices) const
998 {
999 const double fWidth(maRange.getWidth());
1000 const double fHeight(maRange.getHeight());
1001 iterateTiles([&](double fPosX, double fPosY) {
1003 fWidth,
1004 fHeight,
1005 fPosX,
1006 fPosY));
1007 });
1008 }
1009
1010 void GeoTexSvxTiled::iterateTiles(std::function<void(double fPosX, double fPosY)> aFunc) const
1011 {
1012 const double fWidth(maRange.getWidth());
1013
1014 if(basegfx::fTools::equalZero(fWidth))
1015 return;
1016
1017 const double fHeight(maRange.getHeight());
1018
1019 if(basegfx::fTools::equalZero(fHeight))
1020 return;
1021
1022 double fStartX(maRange.getMinX());
1023 double fStartY(maRange.getMinY());
1024 sal_Int32 nPosX(0);
1025 sal_Int32 nPosY(0);
1026
1027 if(basegfx::fTools::more(fStartX, 0.0))
1028 {
1029 const sal_Int32 nDiff(static_cast<sal_Int32>(floor(fStartX / fWidth)) + 1);
1030
1031 nPosX -= nDiff;
1032 fStartX -= nDiff * fWidth;
1033 }
1034
1035 if(basegfx::fTools::less(fStartX + fWidth, 0.0))
1036 {
1037 const sal_Int32 nDiff(static_cast<sal_Int32>(floor(-fStartX / fWidth)));
1038
1039 nPosX += nDiff;
1040 fStartX += nDiff * fWidth;
1041 }
1042
1043 if(basegfx::fTools::more(fStartY, 0.0))
1044 {
1045 const sal_Int32 nDiff(static_cast<sal_Int32>(floor(fStartY / fHeight)) + 1);
1046
1047 nPosY -= nDiff;
1048 fStartY -= nDiff * fHeight;
1049 }
1050
1051 if(basegfx::fTools::less(fStartY + fHeight, 0.0))
1052 {
1053 const sal_Int32 nDiff(static_cast<sal_Int32>(floor(-fStartY / fHeight)));
1054
1055 nPosY += nDiff;
1056 fStartY += nDiff * fHeight;
1057 }
1058
1060 {
1061 for(double fPosX(fStartX); basegfx::fTools::less(fPosX, 1.0); fPosX += fWidth, nPosX++)
1062 {
1063 for(double fPosY((nPosX % 2) ? fStartY - fHeight + (mfOffsetY * fHeight) : fStartY);
1064 basegfx::fTools::less(fPosY, 1.0); fPosY += fHeight)
1065 aFunc(fPosX, fPosY);
1066 }
1067 }
1068 else
1069 {
1070 for(double fPosY(fStartY); basegfx::fTools::less(fPosY, 1.0); fPosY += fHeight, nPosY++)
1071 {
1072 for(double fPosX((nPosY % 2) ? fStartX - fWidth + (mfOffsetX * fWidth) : fStartX);
1073 basegfx::fTools::less(fPosX, 1.0); fPosX += fWidth)
1074 aFunc(fPosX, fPosY);
1075 }
1076 }
1077
1078 }
1079
1080} // end of namespace
1081
1082/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
B2DRange maRange
void set(sal_uInt16 nRow, sal_uInt16 nColumn, double fValue)
void translate(double fX, double fY)
double get(sal_uInt16 nRow, sal_uInt16 nColumn) const
void scale(double fX, double fY)
bool isIdentity() const
BASEGFX_DLLPUBLIC void transform(const B2DHomMatrix &rMatrix)
bool checkPenultimate() const
BColor getInterpolatedBColor(double fPosition, sal_uInt32 nRequestedSteps, BColorStopRange &rLastColorStopRange) const
void setBlue(double fNew)
double luminance() const
void setGreen(double fNew)
void setRed(double fNew)
TYPE getWidth() const
TYPE getMinX() const
TYPE getMinY() const
TYPE getMaxY() const
TYPE getHeight() const
TYPE getX() const
TYPE getY() const
GeoTexSvxGradientAxial(const basegfx::B2DRange &rDefinitionRange, const basegfx::B2DRange &rOutputRange, sal_uInt32 nRequestedSteps, const basegfx::BColorStops &rColorStops, double fBorder, double fAngle)
Definition: texture.cxx:250
virtual void modifyBColor(const basegfx::B2DPoint &rUV, basegfx::BColor &rBColor, double &rfOpacity) const override
Definition: texture.cxx:365
virtual void appendTransformationsAndColors(std::function< void(const basegfx::B2DHomMatrix &rMatrix, const basegfx::BColor &rColor)> aCallback) override
Definition: texture.cxx:287
GeoTexSvxGradientElliptical(const basegfx::B2DRange &rDefinitionRange, sal_uInt32 nRequestedSteps, const basegfx::BColorStops &rColorStops, double fBorder, double fOffsetX, double fOffsetY, double fAngle)
Definition: texture.cxx:488
virtual void modifyBColor(const basegfx::B2DPoint &rUV, basegfx::BColor &rBColor, double &rfOpacity) const override
Definition: texture.cxx:579
virtual void appendTransformationsAndColors(std::function< void(const basegfx::B2DHomMatrix &rMatrix, const basegfx::BColor &rColor)> aCallback) override
Definition: texture.cxx:510
virtual void appendTransformationsAndColors(std::function< void(const basegfx::B2DHomMatrix &rMatrix, const basegfx::BColor &rColor)> aCallback) override
Definition: texture.cxx:135
virtual void modifyBColor(const basegfx::B2DPoint &rUV, basegfx::BColor &rBColor, double &rfOpacity) const override
Definition: texture.cxx:232
GeoTexSvxGradientLinear(const basegfx::B2DRange &rDefinitionRange, const basegfx::B2DRange &rOutputRange, sal_uInt32 nRequestedSteps, const basegfx::BColorStops &rColorStops, double fBorder, double fAngle)
Definition: texture.cxx:102
virtual void appendTransformationsAndColors(std::function< void(const basegfx::B2DHomMatrix &rMatrix, const basegfx::BColor &rColor)> aCallback) override
Definition: texture.cxx:407
GeoTexSvxGradientRadial(const basegfx::B2DRange &rDefinitionRange, sal_uInt32 nRequestedSteps, const basegfx::BColorStops &rColorStops, double fBorder, double fOffsetX, double fOffsetY)
Definition: texture.cxx:387
virtual void modifyBColor(const basegfx::B2DPoint &rUV, basegfx::BColor &rBColor, double &rfOpacity) const override
Definition: texture.cxx:469
GeoTexSvxGradientRect(const basegfx::B2DRange &rDefinitionRange, sal_uInt32 nRequestedSteps, const basegfx::BColorStops &rColorStops, double fBorder, double fOffsetX, double fOffsetY, double fAngle)
Definition: texture.cxx:701
virtual void appendTransformationsAndColors(std::function< void(const basegfx::B2DHomMatrix &rMatrix, const basegfx::BColor &rColor)> aCallback) override
Definition: texture.cxx:723
virtual void modifyBColor(const basegfx::B2DPoint &rUV, basegfx::BColor &rBColor, double &rfOpacity) const override
Definition: texture.cxx:792
virtual void appendTransformationsAndColors(std::function< void(const basegfx::B2DHomMatrix &rMatrix, const basegfx::BColor &rColor)> aCallback) override
Definition: texture.cxx:620
virtual void modifyBColor(const basegfx::B2DPoint &rUV, basegfx::BColor &rBColor, double &rfOpacity) const override
Definition: texture.cxx:682
GeoTexSvxGradientSquare(const basegfx::B2DRange &rDefinitionRange, sal_uInt32 nRequestedSteps, const basegfx::BColorStops &rColorStops, double fBorder, double fOffsetX, double fOffsetY, double fAngle)
Definition: texture.cxx:598
basegfx::BColorStops::BColorStopRange maLastColorStopRange
Definition: texture.hxx:57
virtual bool operator==(const GeoTexSvx &rGeoTexSvx) const override
Definition: texture.cxx:90
basegfx::ODFGradientInfo maGradientInfo
Definition: texture.hxx:49
GeoTexSvxGradient(const basegfx::B2DRange &rDefinitionRange, sal_uInt32 nRequestedSteps, const basegfx::BColorStops &rColorStops, double fBorder)
Definition: texture.cxx:73
basegfx::B2DHomMatrix maBackTextureTransform
Definition: texture.hxx:191
virtual bool operator==(const GeoTexSvx &rGeoTexSvx) const override
Definition: texture.cxx:868
void appendTransformations(::std::vector< basegfx::B2DHomMatrix > &rMatrices)
Definition: texture.cxx:879
virtual ~GeoTexSvxHatch() override
Definition: texture.cxx:864
GeoTexSvxHatch(const basegfx::B2DRange &rDefinitionRange, const basegfx::B2DRange &rOutputRange, double fDistance, double fAngle)
Definition: texture.cxx:811
double getDistanceToHatch(const basegfx::B2DPoint &rUV) const
Definition: texture.cxx:932
basegfx::B2DHomMatrix maTextureTransform
Definition: texture.hxx:190
const basegfx::B2DHomMatrix & getBackTextureTransform() const
Definition: texture.cxx:950
sal_uInt32 getNumberOfTiles() const
Definition: texture.cxx:990
GeoTexSvxTiled(const basegfx::B2DRange &rRange, double fOffsetX=0.0, double fOffsetY=0.0)
Definition: texture.cxx:962
void appendTransformations(::std::vector< basegfx::B2DHomMatrix > &rMatrices) const
Definition: texture.cxx:997
virtual ~GeoTexSvxTiled() override
Definition: texture.cxx:976
virtual bool operator==(const GeoTexSvx &rGeoTexSvx) const override
Definition: texture.cxx:980
void iterateTiles(std::function< void(double fPosX, double fPosY)> aFunc) const
Definition: texture.cxx:1010
virtual bool operator==(const GeoTexSvx &rGeoTexSvx) const
Definition: texture.cxx:50
virtual void modifyOpacity(const basegfx::B2DPoint &rUV, double &rfOpacity) const
Definition: texture.cxx:64
virtual void modifyBColor(const basegfx::B2DPoint &rUV, basegfx::BColor &rBColor, double &rfOpacity) const
Definition: texture.cxx:56
uno_Any a
bool more(const T &rfValA, const T &rfValB)
bool equalZero(const T &rfVal)
bool equal(T const &rfValA, T const &rfValB)
bool less(const T &rfValA, const T &rfValB)
ODFGradientInfo createRadialODFGradientInfo(const B2DRange &rTargetArea, const B2DVector &rOffset, sal_uInt32 nRequestedSteps, double fBorder)
B2DHomMatrix createScaleTranslateB2DHomMatrix(double fScaleX, double fScaleY, double fTranslateX, double fTranslateY)
double getAxialGradientAlpha(const B2DPoint &rUV, const ODFGradientInfo &rGradInfo)
ODFGradientInfo createAxialODFGradientInfo(const B2DRange &rTargetArea, sal_uInt32 nRequestedSteps, double fBorder, double fAngle)
double getLinearGradientAlpha(const B2DPoint &rUV, const ODFGradientInfo &rGradInfo)
B2DHomMatrix createRotateAroundPoint(double fPointX, double fPointY, double fRadiant)
double getRectangularGradientAlpha(const B2DPoint &rUV, const ODFGradientInfo &rGradInfo)
double getEllipticalGradientAlpha(const B2DPoint &rUV, const ODFGradientInfo &rGradInfo)
double getSquareGradientAlpha(const B2DPoint &rUV, const ODFGradientInfo &rGradInfo)
B2DHomMatrix createScaleB2DHomMatrix(double fScaleX, double fScaleY)
ODFGradientInfo createLinearODFGradientInfo(const B2DRange &rTargetArea, sal_uInt32 nRequestedSteps, double fBorder, double fAngle)
double getRadialGradientAlpha(const B2DPoint &rUV, const ODFGradientInfo &rGradInfo)
ODFGradientInfo createSquareODFGradientInfo(const B2DRange &rTargetArea, const B2DVector &rOffset, sal_uInt32 nRequestedSteps, double fBorder, double fAngle)
ODFGradientInfo createEllipticalODFGradientInfo(const B2DRange &rTargetArea, const B2DVector &rOffset, sal_uInt32 nRequestedSteps, double fBorder, double fAngle)
ODFGradientInfo createRectangularODFGradientInfo(const B2DRange &rTargetArea, const B2DVector &rOffset, sal_uInt32 nRequestedSteps, double fBorder, double fAngle)
sal_uInt32 calculateNumberOfSteps(sal_uInt32 nRequestedSteps, const BColor &rStart, const BColor &rEnd)
double snapToNearestMultiple(double v, const double fStep)
B2IRange fround(const B2DRange &rRange)
double uniform_real_distribution(double a=0.0, double b=1.0)
HSLColor interpolate(const HSLColor &rFrom, const HSLColor &rTo, double t, bool bCCW)