1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 // MARKER(update_precomp.py): autogen include statement, do not remove 23 #include "precompiled_drawinglayer.hxx" 24 25 #include <drawinglayer/primitive2d/svggradientprimitive2d.hxx> 26 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> 27 #include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx> 28 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> 29 #include <drawinglayer/primitive2d/polygonprimitive2d.hxx> 30 #include <basegfx/matrix/b2dhommatrixtools.hxx> 31 #include <basegfx/polygon/b2dpolygontools.hxx> 32 #include <basegfx/polygon/b2dpolygon.hxx> 33 #include <drawinglayer/primitive2d/transparenceprimitive2d.hxx> 34 #include <drawinglayer/primitive2d/transformprimitive2d.hxx> 35 #include <drawinglayer/primitive2d/maskprimitive2d.hxx> 36 #include <drawinglayer/geometry/viewinformation2d.hxx> 37 38 ////////////////////////////////////////////////////////////////////////////// 39 40 using namespace com::sun::star; 41 42 ////////////////////////////////////////////////////////////////////////////// 43 44 namespace 45 { 46 sal_uInt32 calculateStepsForSvgGradient(const basegfx::BColor& rColorA, const basegfx::BColor& rColorB, double fDelta, double fDiscreteUnit) 47 { 48 // use color distance, assume to do every color step (full quality) 49 sal_uInt32 nSteps(basegfx::fround(rColorA.getDistance(rColorB) * 255.0)); 50 51 if(nSteps) 52 { 53 // calc discrete length to change color all 1.5 disctete units (pixels) 54 const sal_uInt32 nDistSteps(basegfx::fround(fDelta / (fDiscreteUnit * 1.5))); 55 56 nSteps = std::min(nSteps, nDistSteps); 57 } 58 59 // roughly cut when too big or too small 60 nSteps = std::min(nSteps, sal_uInt32(255)); 61 nSteps = std::max(nSteps, sal_uInt32(1)); 62 63 return nSteps; 64 } 65 } // end of anonymous namespace 66 67 ////////////////////////////////////////////////////////////////////////////// 68 69 namespace drawinglayer 70 { 71 namespace primitive2d 72 { 73 Primitive2DSequence SvgGradientHelper::createSingleGradientEntryFill() const 74 { 75 const SvgGradientEntryVector& rEntries = getGradientEntries(); 76 const sal_uInt32 nCount(rEntries.size()); 77 Primitive2DSequence xRetval; 78 79 if(nCount) 80 { 81 const SvgGradientEntry& rSingleEntry = rEntries[nCount - 1]; 82 const double fOpacity(rSingleEntry.getOpacity()); 83 84 if(fOpacity > 0.0) 85 { 86 Primitive2DReference xRef( 87 new PolyPolygonColorPrimitive2D( 88 getPolyPolygon(), 89 rSingleEntry.getColor())); 90 91 if(fOpacity < 1.0) 92 { 93 const Primitive2DSequence aContent(&xRef, 1); 94 95 xRef = Primitive2DReference( 96 new UnifiedTransparencePrimitive2D( 97 aContent, 98 1.0 - fOpacity)); 99 } 100 101 xRetval = Primitive2DSequence(&xRef, 1); 102 } 103 } 104 else 105 { 106 OSL_ENSURE(false, "Single gradient entry construction without entry (!)"); 107 } 108 109 return xRetval; 110 } 111 112 void SvgGradientHelper::checkPreconditions() 113 { 114 mbPreconditionsChecked = true; 115 const SvgGradientEntryVector& rEntries = getGradientEntries(); 116 117 if(rEntries.empty()) 118 { 119 // no fill at all 120 } 121 else 122 { 123 const sal_uInt32 nCount(rEntries.size()); 124 125 if(1 == nCount) 126 { 127 // fill with single existing color 128 setSingleEntry(); 129 } 130 else 131 { 132 // sort maGradientEntries when more than one 133 std::sort(maGradientEntries.begin(), maGradientEntries.end()); 134 135 // gradient with at least two colors 136 bool bAllInvisible(true); 137 138 for(sal_uInt32 a(0); a < nCount; a++) 139 { 140 const SvgGradientEntry& rCandidate = rEntries[a]; 141 142 if(basegfx::fTools::equalZero(rCandidate.getOpacity())) 143 { 144 // invisible 145 mbFullyOpaque = false; 146 } 147 else if(basegfx::fTools::equal(rCandidate.getOpacity(), 1.0)) 148 { 149 // completely opaque 150 bAllInvisible = false; 151 } 152 else 153 { 154 // opacity 155 bAllInvisible = false; 156 mbFullyOpaque = false; 157 } 158 } 159 160 if(bAllInvisible) 161 { 162 // all invisible, nothing to do 163 } 164 else 165 { 166 const basegfx::B2DRange aPolyRange(getPolyPolygon().getB2DRange()); 167 168 if(aPolyRange.isEmpty()) 169 { 170 // no range to fill, nothing to do 171 } 172 else 173 { 174 const double fPolyWidth(aPolyRange.getWidth()); 175 const double fPolyHeight(aPolyRange.getHeight()); 176 177 if(basegfx::fTools::equalZero(fPolyWidth) || basegfx::fTools::equalZero(fPolyHeight)) 178 { 179 // no width/height to fill, nothing to do 180 } 181 else 182 { 183 mbCreatesContent = true; 184 } 185 } 186 } 187 } 188 } 189 } 190 191 double SvgGradientHelper::createRun( 192 Primitive2DVector& rTargetColor, 193 Primitive2DVector& rTargetOpacity, 194 double fPos, 195 double fMax, 196 const SvgGradientEntryVector& rEntries, 197 sal_Int32 nOffset) const 198 { 199 const sal_uInt32 nCount(rEntries.size()); 200 201 if(nCount) 202 { 203 const SvgGradientEntry& rStart = rEntries[0]; 204 const bool bCreateStartPad(fPos < 0.0 && Spread_pad == getSpreadMethod()); 205 const bool bCreateStartFill(rStart.getOffset() > 0.0); 206 sal_uInt32 nIndex(0); 207 208 if(bCreateStartPad || bCreateStartFill) 209 { 210 const SvgGradientEntry aTemp(bCreateStartPad ? fPos : 0.0, rStart.getColor(), rStart.getOpacity()); 211 212 createAtom(rTargetColor, rTargetOpacity, aTemp, rStart, nOffset); 213 fPos = rStart.getOffset(); 214 } 215 216 while(fPos < 1.0 && nIndex + 1 < nCount) 217 { 218 const SvgGradientEntry& rCandidateA = rEntries[nIndex++]; 219 const SvgGradientEntry& rCandidateB = rEntries[nIndex]; 220 221 createAtom(rTargetColor, rTargetOpacity, rCandidateA, rCandidateB, nOffset); 222 fPos = rCandidateB.getOffset(); 223 } 224 225 const SvgGradientEntry& rEnd = rEntries[nCount - 1]; 226 const bool bCreateEndPad(fPos < fMax && Spread_pad == getSpreadMethod()); 227 const bool bCreateEndFill(rEnd.getOffset() < 1.0); 228 229 if(bCreateEndPad || bCreateEndFill) 230 { 231 fPos = bCreateEndPad ? fMax : 1.0; 232 const SvgGradientEntry aTemp(fPos, rEnd.getColor(), rEnd.getOpacity()); 233 234 createAtom(rTargetColor, rTargetOpacity, rEnd, aTemp, nOffset); 235 } 236 } 237 else 238 { 239 OSL_ENSURE(false, "GradientAtom creation without ColorStops (!)"); 240 fPos = fMax; 241 } 242 243 return fPos; 244 } 245 246 Primitive2DSequence SvgGradientHelper::createResult( 247 const Primitive2DVector& rTargetColor, 248 const Primitive2DVector& rTargetOpacity, 249 const basegfx::B2DHomMatrix& rUnitGradientToObject, 250 bool bInvert) const 251 { 252 Primitive2DSequence xRetval; 253 const Primitive2DSequence aTargetColorEntries(Primitive2DVectorToPrimitive2DSequence(rTargetColor, bInvert)); 254 const Primitive2DSequence aTargetOpacityEntries(Primitive2DVectorToPrimitive2DSequence(rTargetOpacity, bInvert)); 255 256 if(aTargetColorEntries.hasElements()) 257 { 258 Primitive2DReference xRefContent; 259 260 if(aTargetOpacityEntries.hasElements()) 261 { 262 const Primitive2DReference xRefOpacity = new TransparencePrimitive2D( 263 aTargetColorEntries, 264 aTargetOpacityEntries); 265 266 xRefContent = new TransformPrimitive2D( 267 rUnitGradientToObject, 268 Primitive2DSequence(&xRefOpacity, 1)); 269 } 270 else 271 { 272 xRefContent = new TransformPrimitive2D( 273 rUnitGradientToObject, 274 aTargetColorEntries); 275 } 276 277 xRefContent = new MaskPrimitive2D( 278 getPolyPolygon(), 279 Primitive2DSequence(&xRefContent, 1)); 280 281 xRetval = Primitive2DSequence(&xRefContent, 1); 282 } 283 284 return xRetval; 285 } 286 287 SvgGradientHelper::SvgGradientHelper( 288 const basegfx::B2DPolyPolygon& rPolyPolygon, 289 const SvgGradientEntryVector& rGradientEntries, 290 const basegfx::B2DPoint& rStart, 291 SpreadMethod aSpreadMethod) 292 : maPolyPolygon(rPolyPolygon), 293 maGradientEntries(rGradientEntries), 294 maStart(rStart), 295 maSpreadMethod(aSpreadMethod), 296 mbPreconditionsChecked(false), 297 mbCreatesContent(false), 298 mbSingleEntry(false), 299 mbFullyOpaque(true) 300 { 301 } 302 303 bool SvgGradientHelper::operator==(const SvgGradientHelper& rSvgGradientHelper) const 304 { 305 const SvgGradientHelper& rCompare = static_cast< const SvgGradientHelper& >(rSvgGradientHelper); 306 307 return (getPolyPolygon() == rCompare.getPolyPolygon() 308 && getGradientEntries() == rCompare.getGradientEntries() 309 && getStart() == rCompare.getStart() 310 && getSpreadMethod() == rCompare.getSpreadMethod()); 311 } 312 313 } // end of namespace primitive2d 314 } // end of namespace drawinglayer 315 316 ////////////////////////////////////////////////////////////////////////////// 317 318 namespace drawinglayer 319 { 320 namespace primitive2d 321 { 322 void SvgLinearGradientPrimitive2D::checkPreconditions() 323 { 324 // call parent 325 SvgGradientHelper::checkPreconditions(); 326 327 if(getCreatesContent()) 328 { 329 // Check Vector 330 const basegfx::B2DVector aVector(getEnd() - getStart()); 331 332 if(basegfx::fTools::equalZero(aVector.getX()) && basegfx::fTools::equalZero(aVector.getY())) 333 { 334 // fill with single color using last stop color 335 setSingleEntry(); 336 } 337 } 338 } 339 340 void SvgLinearGradientPrimitive2D::createAtom( 341 Primitive2DVector& rTargetColor, 342 Primitive2DVector& rTargetOpacity, 343 const SvgGradientEntry& rFrom, 344 const SvgGradientEntry& rTo, 345 sal_Int32 nOffset) const 346 { 347 // create gradient atom [rFrom.getOffset() .. rTo.getOffset()] with (rFrom.getOffset() > rTo.getOffset()) 348 if(rFrom.getOffset() == rTo.getOffset()) 349 { 350 OSL_ENSURE(false, "SvgGradient Atom creation with no step width (!)"); 351 } 352 else 353 { 354 rTargetColor.push_back( 355 new SvgLinearAtomPrimitive2D( 356 rFrom.getColor(), rFrom.getOffset() + nOffset, 357 rTo.getColor(), rTo.getOffset() + nOffset)); 358 359 if(!getFullyOpaque()) 360 { 361 const double fTransFrom(1.0 - rFrom.getOpacity()); 362 const double fTransTo(1.0 - rTo.getOpacity()); 363 const basegfx::BColor aColorFrom(fTransFrom, fTransFrom, fTransFrom); 364 const basegfx::BColor aColorTo(fTransTo, fTransTo, fTransTo); 365 366 rTargetOpacity.push_back( 367 new SvgLinearAtomPrimitive2D( 368 aColorFrom, rFrom.getOffset() + nOffset, 369 aColorTo, rTo.getOffset() + nOffset)); 370 } 371 } 372 } 373 374 Primitive2DSequence SvgLinearGradientPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const 375 { 376 Primitive2DSequence xRetval; 377 378 if(!getPreconditionsChecked()) 379 { 380 const_cast< SvgLinearGradientPrimitive2D* >(this)->checkPreconditions(); 381 } 382 383 if(getSingleEntry()) 384 { 385 // fill with last existing color 386 xRetval = createSingleGradientEntryFill(); 387 } 388 else if(getCreatesContent()) 389 { 390 // at least two color stops in range [0.0 .. 1.0], sorted, non-null vector, not completely 391 // invisible, width and height to fill are not empty 392 const basegfx::B2DRange aPolyRange(getPolyPolygon().getB2DRange()); 393 const double fPolyWidth(aPolyRange.getWidth()); 394 const double fPolyHeight(aPolyRange.getHeight()); 395 396 // create ObjectTransform based on polygon range 397 const basegfx::B2DHomMatrix aObjectTransform( 398 basegfx::tools::createScaleTranslateB2DHomMatrix( 399 fPolyWidth, fPolyHeight, 400 aPolyRange.getMinX(), aPolyRange.getMinY())); 401 402 // get start, end in object coordinates 403 const basegfx::B2DPoint aStart(aObjectTransform * getStart()); 404 const basegfx::B2DPoint aEnd(aObjectTransform * getEnd()); 405 406 // create transform from unit vector [0.0 .. 1.0] along the X-Axis to given 407 // gradient vector in object coordinates defined by Start, End 408 const basegfx::B2DVector aVector(aEnd - aStart); 409 const double fVectorLength(aVector.getLength()); 410 basegfx::B2DHomMatrix aUnitGradientToObject; 411 412 aUnitGradientToObject.scale(fVectorLength, 1.0); 413 aUnitGradientToObject.rotate(atan2(aVector.getY(), aVector.getX())); 414 aUnitGradientToObject.translate(aStart.getX(), aStart.getY()); 415 416 // create inverse from it 417 basegfx::B2DHomMatrix aObjectToUnitGradient(aUnitGradientToObject); 418 aObjectToUnitGradient.invert(); 419 420 // back-transform polygon to unit gradient coordinates and get 421 // UnitRage. This is the range the gradient has to cover 422 basegfx::B2DPolyPolygon aUnitPoly(getPolyPolygon()); 423 aUnitPoly.transform(aObjectToUnitGradient); 424 const basegfx::B2DRange aUnitRange(aUnitPoly.getB2DRange()); 425 426 // prepare result vectors 427 Primitive2DVector aTargetColor; 428 Primitive2DVector aTargetOpacity; 429 430 if(basegfx::fTools::more(aUnitRange.getWidth(), 0.0)) 431 { 432 // add a pre-multiply to aUnitGradientToObject to allow 433 // multiplication of the polygon(xl, 0.0, xr, 1.0) 434 const basegfx::B2DHomMatrix aPreMultiply( 435 basegfx::tools::createScaleTranslateB2DHomMatrix( 436 1.0, aUnitRange.getHeight(), 0.0, aUnitRange.getMinY())); 437 aUnitGradientToObject = aUnitGradientToObject * aPreMultiply; 438 439 // create central run, may also already do all necessary when 440 // Spread_pad is set as SpreadMethod and/or the range is smaller 441 double fPos(createRun(aTargetColor, aTargetOpacity, aUnitRange.getMinX(), aUnitRange.getMaxX(), getGradientEntries(), 0)); 442 443 if(fPos < aUnitRange.getMaxX()) 444 { 445 // can only happen when SpreadMethod is Spread_reflect or Spread_repeat, 446 // else the start and end pads are already created and fPos == aUnitRange.getMaxX(). 447 // Its possible to express the repeated linear gradient by adding the 448 // transformed central run. Crete it this way 449 Primitive2DSequence aTargetColorEntries(Primitive2DVectorToPrimitive2DSequence(aTargetColor)); 450 Primitive2DSequence aTargetOpacityEntries(Primitive2DVectorToPrimitive2DSequence(aTargetOpacity)); 451 aTargetColor.clear(); 452 aTargetOpacity.clear(); 453 454 if(aTargetColorEntries.hasElements()) 455 { 456 // add original central run as group primitive 457 aTargetColor.push_back(new GroupPrimitive2D(aTargetColorEntries)); 458 459 if(aTargetOpacityEntries.hasElements()) 460 { 461 aTargetOpacity.push_back(new GroupPrimitive2D(aTargetOpacityEntries)); 462 } 463 464 // add negative runs 465 fPos = 0.0; 466 sal_Int32 nOffset(0); 467 468 while(fPos > aUnitRange.getMinX()) 469 { 470 fPos -= 1.0; 471 nOffset++; 472 473 basegfx::B2DHomMatrix aTransform; 474 const bool bMirror(Spread_reflect == getSpreadMethod() && (nOffset % 2)); 475 476 if(bMirror) 477 { 478 aTransform.scale(-1.0, 1.0); 479 aTransform.translate(fPos + 1.0, 0.0); 480 } 481 else 482 { 483 aTransform.translate(fPos, 0.0); 484 } 485 486 aTargetColor.push_back(new TransformPrimitive2D(aTransform, aTargetColorEntries)); 487 488 if(aTargetOpacityEntries.hasElements()) 489 { 490 aTargetOpacity.push_back(new TransformPrimitive2D(aTransform, aTargetOpacityEntries)); 491 } 492 } 493 494 // add positive runs 495 fPos = 1.0; 496 nOffset = 1; 497 498 while(fPos < aUnitRange.getMaxX()) 499 { 500 basegfx::B2DHomMatrix aTransform; 501 const bool bMirror(Spread_reflect == getSpreadMethod() && (nOffset % 2)); 502 503 if(bMirror) 504 { 505 aTransform.scale(-1.0, 1.0); 506 aTransform.translate(fPos + 1.0, 0.0); 507 } 508 else 509 { 510 aTransform.translate(fPos, 0.0); 511 } 512 513 aTargetColor.push_back(new TransformPrimitive2D(aTransform, aTargetColorEntries)); 514 515 if(aTargetOpacityEntries.hasElements()) 516 { 517 aTargetOpacity.push_back(new TransformPrimitive2D(aTransform, aTargetOpacityEntries)); 518 } 519 520 fPos += 1.0; 521 nOffset++; 522 } 523 } 524 } 525 } 526 527 xRetval = createResult(aTargetColor, aTargetOpacity, aUnitGradientToObject); 528 } 529 530 return xRetval; 531 } 532 533 SvgLinearGradientPrimitive2D::SvgLinearGradientPrimitive2D( 534 const basegfx::B2DPolyPolygon& rPolyPolygon, 535 const SvgGradientEntryVector& rGradientEntries, 536 const basegfx::B2DPoint& rStart, 537 const basegfx::B2DPoint& rEnd, 538 SpreadMethod aSpreadMethod) 539 : BufferedDecompositionPrimitive2D(), 540 SvgGradientHelper(rPolyPolygon, rGradientEntries, rStart, aSpreadMethod), 541 maEnd(rEnd) 542 { 543 } 544 545 SvgLinearGradientPrimitive2D::~SvgLinearGradientPrimitive2D() 546 { 547 } 548 549 bool SvgLinearGradientPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const 550 { 551 const SvgGradientHelper* pSvgGradientHelper = dynamic_cast< const SvgGradientHelper* >(&rPrimitive); 552 553 if(pSvgGradientHelper && SvgGradientHelper::operator==(*pSvgGradientHelper)) 554 { 555 const SvgLinearGradientPrimitive2D& rCompare = static_cast< const SvgLinearGradientPrimitive2D& >(rPrimitive); 556 557 return (getEnd() == rCompare.getEnd()); 558 } 559 560 return false; 561 } 562 563 basegfx::B2DRange SvgLinearGradientPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const 564 { 565 // return ObjectRange 566 return getPolyPolygon().getB2DRange(); 567 } 568 569 // provide unique ID 570 ImplPrimitrive2DIDBlock(SvgLinearGradientPrimitive2D, PRIMITIVE2D_ID_SVGLINEARGRADIENTPRIMITIVE2D) 571 572 } // end of namespace primitive2d 573 } // end of namespace drawinglayer 574 575 ////////////////////////////////////////////////////////////////////////////// 576 577 namespace drawinglayer 578 { 579 namespace primitive2d 580 { 581 void SvgRadialGradientPrimitive2D::checkPreconditions() 582 { 583 // call parent 584 SvgGradientHelper::checkPreconditions(); 585 586 if(getCreatesContent()) 587 { 588 // Check Radius 589 if(basegfx::fTools::equalZero(getRadius())) 590 { 591 // fill with single color using last stop color 592 setSingleEntry(); 593 } 594 } 595 } 596 597 void SvgRadialGradientPrimitive2D::createAtom( 598 Primitive2DVector& rTargetColor, 599 Primitive2DVector& rTargetOpacity, 600 const SvgGradientEntry& rFrom, 601 const SvgGradientEntry& rTo, 602 sal_Int32 nOffset) const 603 { 604 // create gradient atom [rFrom.getOffset() .. rTo.getOffset()] with (rFrom.getOffset() > rTo.getOffset()) 605 if(rFrom.getOffset() == rTo.getOffset()) 606 { 607 OSL_ENSURE(false, "SvgGradient Atom creation with no step width (!)"); 608 } 609 else 610 { 611 const double fScaleFrom(rFrom.getOffset() + nOffset); 612 const double fScaleTo(rTo.getOffset() + nOffset); 613 614 if(isFocalSet()) 615 { 616 const basegfx::B2DVector aTranslateFrom(maFocalVector * (maFocalLength - fScaleFrom)); 617 const basegfx::B2DVector aTranslateTo(maFocalVector * (maFocalLength - fScaleTo)); 618 619 rTargetColor.push_back( 620 new SvgRadialAtomPrimitive2D( 621 rFrom.getColor(), fScaleFrom, aTranslateFrom, 622 rTo.getColor(), fScaleTo, aTranslateTo)); 623 } 624 else 625 { 626 rTargetColor.push_back( 627 new SvgRadialAtomPrimitive2D( 628 rFrom.getColor(), fScaleFrom, 629 rTo.getColor(), fScaleTo)); 630 } 631 632 if(!getFullyOpaque()) 633 { 634 const double fTransFrom(1.0 - rFrom.getOpacity()); 635 const double fTransTo(1.0 - rTo.getOpacity()); 636 const basegfx::BColor aColorFrom(fTransFrom, fTransFrom, fTransFrom); 637 const basegfx::BColor aColorTo(fTransTo, fTransTo, fTransTo); 638 639 if(isFocalSet()) 640 { 641 const basegfx::B2DVector aTranslateFrom(maFocalVector * (maFocalLength - fScaleFrom)); 642 const basegfx::B2DVector aTranslateTo(maFocalVector * (maFocalLength - fScaleTo)); 643 644 rTargetOpacity.push_back( 645 new SvgRadialAtomPrimitive2D( 646 aColorFrom, fScaleFrom, aTranslateFrom, 647 aColorTo, fScaleTo, aTranslateTo)); 648 } 649 else 650 { 651 rTargetOpacity.push_back( 652 new SvgRadialAtomPrimitive2D( 653 aColorFrom, fScaleFrom, 654 aColorTo, fScaleTo)); 655 } 656 } 657 } 658 } 659 660 const SvgGradientEntryVector& SvgRadialGradientPrimitive2D::getMirroredGradientEntries() const 661 { 662 if(maMirroredGradientEntries.empty() && !getGradientEntries().empty()) 663 { 664 const_cast< SvgRadialGradientPrimitive2D* >(this)->createMirroredGradientEntries(); 665 } 666 667 return maMirroredGradientEntries; 668 } 669 670 void SvgRadialGradientPrimitive2D::createMirroredGradientEntries() 671 { 672 if(maMirroredGradientEntries.empty() && !getGradientEntries().empty()) 673 { 674 const sal_uInt32 nCount(getGradientEntries().size()); 675 maMirroredGradientEntries.clear(); 676 maMirroredGradientEntries.reserve(nCount); 677 678 for(sal_uInt32 a(0); a < nCount; a++) 679 { 680 const SvgGradientEntry& rCandidate = getGradientEntries()[nCount - 1 - a]; 681 682 maMirroredGradientEntries.push_back( 683 SvgGradientEntry( 684 1.0 - rCandidate.getOffset(), 685 rCandidate.getColor(), 686 rCandidate.getOpacity())); 687 } 688 } 689 } 690 691 Primitive2DSequence SvgRadialGradientPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const 692 { 693 Primitive2DSequence xRetval; 694 695 if(!getPreconditionsChecked()) 696 { 697 const_cast< SvgRadialGradientPrimitive2D* >(this)->checkPreconditions(); 698 } 699 700 if(getSingleEntry()) 701 { 702 // fill with last existing color 703 xRetval = createSingleGradientEntryFill(); 704 } 705 else if(getCreatesContent()) 706 { 707 // at least two color stops in range [0.0 .. 1.0], sorted, non-null vector, not completely 708 // invisible, width and height to fill are not empty 709 const basegfx::B2DRange aPolyRange(getPolyPolygon().getB2DRange()); 710 const double fPolyWidth(aPolyRange.getWidth()); 711 const double fPolyHeight(aPolyRange.getHeight()); 712 713 // create ObjectTransform based on polygon range 714 const basegfx::B2DHomMatrix aObjectTransform( 715 basegfx::tools::createScaleTranslateB2DHomMatrix( 716 fPolyWidth, fPolyHeight, 717 aPolyRange.getMinX(), aPolyRange.getMinY())); 718 719 // create unit transform from unit vector to given linear gradient vector 720 basegfx::B2DHomMatrix aUnitGradientToGradient; 721 722 aUnitGradientToGradient.scale(getRadius(), getRadius()); 723 aUnitGradientToGradient.translate(getStart().getX(), getStart().getY()); 724 725 // create full transform from unit gradient coordinates to object coordinates 726 // including the SvgGradient transformation 727 basegfx::B2DHomMatrix aUnitGradientToObject(aObjectTransform * aUnitGradientToGradient); 728 729 // create inverse from it 730 basegfx::B2DHomMatrix aObjectToUnitGradient(aUnitGradientToObject); 731 aObjectToUnitGradient.invert(); 732 733 // back-transform polygon to unit gradient coordinates and get 734 // UnitRage. This is the range the gradient has to cover 735 basegfx::B2DPolyPolygon aUnitPoly(getPolyPolygon()); 736 aUnitPoly.transform(aObjectToUnitGradient); 737 const basegfx::B2DRange aUnitRange(aUnitPoly.getB2DRange()); 738 739 // create range which the gradient has to cover to cover the whole given geometry. 740 // For circle, go from 0.0 to max radius in all directions (the corners) 741 double fMax(basegfx::B2DVector(aUnitRange.getMinimum()).getLength()); 742 fMax = std::max(fMax, basegfx::B2DVector(aUnitRange.getMaximum()).getLength()); 743 fMax = std::max(fMax, basegfx::B2DVector(aUnitRange.getMinX(), aUnitRange.getMaxY()).getLength()); 744 fMax = std::max(fMax, basegfx::B2DVector(aUnitRange.getMaxX(), aUnitRange.getMinY()).getLength()); 745 746 // prepare result vectors 747 Primitive2DVector aTargetColor; 748 Primitive2DVector aTargetOpacity; 749 750 if(0.0 < fMax) 751 { 752 // prepare maFocalVector 753 if(isFocalSet()) 754 { 755 const_cast< SvgRadialGradientPrimitive2D* >(this)->maFocalLength = fMax; 756 } 757 758 // create central run, may also already do all necessary when 759 // Spread_pad is set as SpreadMethod and/or the range is smaller 760 double fPos(createRun(aTargetColor, aTargetOpacity, 0.0, fMax, getGradientEntries(), 0)); 761 762 if(fPos < fMax) 763 { 764 // can only happen when SpreadMethod is Spread_reflect or Spread_repeat, 765 // else the start and end pads are already created and fPos == fMax. 766 // For radial there is no way to transform the already created 767 // central run, it needs to be created from 1.0 to fMax 768 sal_Int32 nOffset(1); 769 770 while(fPos < fMax) 771 { 772 const bool bMirror(Spread_reflect == getSpreadMethod() && (nOffset % 2)); 773 774 if(bMirror) 775 { 776 createRun(aTargetColor, aTargetOpacity, 0.0, fMax, getMirroredGradientEntries(), nOffset); 777 } 778 else 779 { 780 createRun(aTargetColor, aTargetOpacity, 0.0, fMax, getGradientEntries(), nOffset); 781 } 782 783 nOffset++; 784 fPos += 1.0; 785 } 786 } 787 } 788 789 xRetval = createResult(aTargetColor, aTargetOpacity, aUnitGradientToObject, true); 790 } 791 792 return xRetval; 793 } 794 795 SvgRadialGradientPrimitive2D::SvgRadialGradientPrimitive2D( 796 const basegfx::B2DPolyPolygon& rPolyPolygon, 797 const SvgGradientEntryVector& rGradientEntries, 798 const basegfx::B2DPoint& rStart, 799 double fRadius, 800 SpreadMethod aSpreadMethod, 801 const basegfx::B2DPoint* pFocal) 802 : BufferedDecompositionPrimitive2D(), 803 SvgGradientHelper(rPolyPolygon, rGradientEntries, rStart, aSpreadMethod), 804 mfRadius(fRadius), 805 maFocal(rStart), 806 maFocalVector(0.0, 0.0), 807 maFocalLength(0.0), 808 maMirroredGradientEntries(), 809 mbFocalSet(false) 810 { 811 if(pFocal && !pFocal->equal(getStart())) 812 { 813 maFocal = *pFocal; 814 maFocalVector = maFocal - getStart(); 815 mbFocalSet = true; 816 } 817 } 818 819 SvgRadialGradientPrimitive2D::~SvgRadialGradientPrimitive2D() 820 { 821 } 822 823 bool SvgRadialGradientPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const 824 { 825 const SvgGradientHelper* pSvgGradientHelper = dynamic_cast< const SvgGradientHelper* >(&rPrimitive); 826 827 if(pSvgGradientHelper && SvgGradientHelper::operator==(*pSvgGradientHelper)) 828 { 829 const SvgRadialGradientPrimitive2D& rCompare = static_cast< const SvgRadialGradientPrimitive2D& >(rPrimitive); 830 831 if(getRadius() == rCompare.getRadius()) 832 { 833 if(isFocalSet() == rCompare.isFocalSet()) 834 { 835 if(isFocalSet()) 836 { 837 return getFocal() == rCompare.getFocal(); 838 } 839 else 840 { 841 return true; 842 } 843 } 844 } 845 } 846 847 return false; 848 } 849 850 basegfx::B2DRange SvgRadialGradientPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const 851 { 852 // return ObjectRange 853 return getPolyPolygon().getB2DRange(); 854 } 855 856 // provide unique ID 857 ImplPrimitrive2DIDBlock(SvgRadialGradientPrimitive2D, PRIMITIVE2D_ID_SVGRADIALGRADIENTPRIMITIVE2D) 858 859 } // end of namespace primitive2d 860 } // end of namespace drawinglayer 861 862 ////////////////////////////////////////////////////////////////////////////// 863 // SvgLinearAtomPrimitive2D class 864 865 namespace drawinglayer 866 { 867 namespace primitive2d 868 { 869 Primitive2DSequence SvgLinearAtomPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const 870 { 871 Primitive2DSequence xRetval; 872 const double fDelta(getOffsetB() - getOffsetA()); 873 874 if(!basegfx::fTools::equalZero(fDelta)) 875 { 876 // use one discrete unit for overlap (one pixel) 877 const double fDiscreteUnit(getDiscreteUnit()); 878 879 // use color distance and discrete lengths to calculate step count 880 const sal_uInt32 nSteps(calculateStepsForSvgGradient(getColorA(), getColorB(), fDelta, fDiscreteUnit)); 881 882 // prepare polygon in needed width at start position (with discrete overlap) 883 const basegfx::B2DPolygon aPolygon( 884 basegfx::tools::createPolygonFromRect( 885 basegfx::B2DRange( 886 getOffsetA() - fDiscreteUnit, 887 0.0, 888 getOffsetA() + (fDelta / nSteps) + fDiscreteUnit, 889 1.0))); 890 891 // prepare loop (inside to outside, [0.0 .. 1.0[) 892 double fUnitScale(0.0); 893 const double fUnitStep(1.0 / nSteps); 894 895 // prepare result set (known size) 896 xRetval.realloc(nSteps); 897 898 for(sal_uInt32 a(0); a < nSteps; a++, fUnitScale += fUnitStep) 899 { 900 basegfx::B2DPolygon aNew(aPolygon); 901 902 aNew.transform(basegfx::tools::createTranslateB2DHomMatrix(fDelta * fUnitScale, 0.0)); 903 xRetval[a] = new PolyPolygonColorPrimitive2D( 904 basegfx::B2DPolyPolygon(aNew), 905 basegfx::interpolate(getColorA(), getColorB(), fUnitScale)); 906 } 907 } 908 909 return xRetval; 910 } 911 912 SvgLinearAtomPrimitive2D::SvgLinearAtomPrimitive2D( 913 const basegfx::BColor& aColorA, double fOffsetA, 914 const basegfx::BColor& aColorB, double fOffsetB) 915 : DiscreteMetricDependentPrimitive2D(), 916 maColorA(aColorA), 917 maColorB(aColorB), 918 mfOffsetA(fOffsetA), 919 mfOffsetB(fOffsetB) 920 { 921 if(mfOffsetA > mfOffsetB) 922 { 923 OSL_ENSURE(false, "Wrong offset order (!)"); 924 ::std::swap(mfOffsetA, mfOffsetB); 925 } 926 } 927 928 bool SvgLinearAtomPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const 929 { 930 if(DiscreteMetricDependentPrimitive2D::operator==(rPrimitive)) 931 { 932 const SvgLinearAtomPrimitive2D& rCompare = static_cast< const SvgLinearAtomPrimitive2D& >(rPrimitive); 933 934 return (getColorA() == rCompare.getColorA() 935 && getColorB() == rCompare.getColorB() 936 && getOffsetA() == rCompare.getOffsetA() 937 && getOffsetB() == rCompare.getOffsetB()); 938 } 939 940 return false; 941 } 942 943 // provide unique ID 944 ImplPrimitrive2DIDBlock(SvgLinearAtomPrimitive2D, PRIMITIVE2D_ID_SVGLINEARATOMPRIMITIVE2D) 945 946 } // end of namespace primitive2d 947 } // end of namespace drawinglayer 948 949 ////////////////////////////////////////////////////////////////////////////// 950 // SvgRadialAtomPrimitive2D class 951 952 namespace drawinglayer 953 { 954 namespace primitive2d 955 { 956 Primitive2DSequence SvgRadialAtomPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const 957 { 958 Primitive2DSequence xRetval; 959 const double fDeltaScale(getScaleB() - getScaleA()); 960 961 if(!basegfx::fTools::equalZero(fDeltaScale)) 962 { 963 // use one discrete unit for overlap (one pixel) 964 const double fDiscreteUnit(getDiscreteUnit()); 965 966 // use color distance and discrete lengths to calculate step count 967 const sal_uInt32 nSteps(calculateStepsForSvgGradient(getColorA(), getColorB(), fDeltaScale, fDiscreteUnit)); 968 969 // prepare loop ([0.0 .. 1.0[, full polygons, no polypolygons with holes) 970 double fUnitScale(0.0); 971 const double fUnitStep(1.0 / nSteps); 972 973 // prepare result set (known size) 974 xRetval.realloc(nSteps); 975 976 for(sal_uInt32 a(0); a < nSteps; a++, fUnitScale += fUnitStep) 977 { 978 basegfx::B2DHomMatrix aTransform; 979 const double fEndScale(getScaleB() - (fDeltaScale * fUnitScale)); 980 981 if(isTranslateSet()) 982 { 983 const basegfx::B2DVector aTranslate( 984 basegfx::interpolate( 985 getTranslateB(), 986 getTranslateA(), 987 fUnitScale)); 988 989 aTransform = basegfx::tools::createScaleTranslateB2DHomMatrix( 990 fEndScale, 991 fEndScale, 992 aTranslate.getX(), 993 aTranslate.getY()); 994 } 995 else 996 { 997 aTransform = basegfx::tools::createScaleB2DHomMatrix( 998 fEndScale, 999 fEndScale); 1000 } 1001 1002 basegfx::B2DPolygon aNew(basegfx::tools::createPolygonFromUnitCircle()); 1003 1004 aNew.transform(aTransform); 1005 xRetval[a] = new PolyPolygonColorPrimitive2D( 1006 basegfx::B2DPolyPolygon(aNew), 1007 basegfx::interpolate(getColorB(), getColorA(), fUnitScale)); 1008 } 1009 } 1010 1011 return xRetval; 1012 } 1013 1014 SvgRadialAtomPrimitive2D::SvgRadialAtomPrimitive2D( 1015 const basegfx::BColor& aColorA, double fScaleA, const basegfx::B2DVector& rTranslateA, 1016 const basegfx::BColor& aColorB, double fScaleB, const basegfx::B2DVector& rTranslateB) 1017 : DiscreteMetricDependentPrimitive2D(), 1018 maColorA(aColorA), 1019 maColorB(aColorB), 1020 mfScaleA(fScaleA), 1021 mfScaleB(fScaleB), 1022 mpTranslate(0) 1023 { 1024 // check and evtl. set translations 1025 if(!rTranslateA.equal(rTranslateB)) 1026 { 1027 mpTranslate = new VectorPair(rTranslateA, rTranslateB); 1028 } 1029 1030 // scale A and B have to be positive 1031 mfScaleA = ::std::max(mfScaleA, 0.0); 1032 mfScaleB = ::std::max(mfScaleB, 0.0); 1033 1034 // scale B has to be bigger than scale A; swap if different 1035 if(mfScaleA > mfScaleB) 1036 { 1037 OSL_ENSURE(false, "Wrong offset order (!)"); 1038 ::std::swap(mfScaleA, mfScaleB); 1039 1040 if(mpTranslate) 1041 { 1042 ::std::swap(mpTranslate->maTranslateA, mpTranslate->maTranslateB); 1043 } 1044 } 1045 } 1046 1047 SvgRadialAtomPrimitive2D::SvgRadialAtomPrimitive2D( 1048 const basegfx::BColor& aColorA, double fScaleA, 1049 const basegfx::BColor& aColorB, double fScaleB) 1050 : DiscreteMetricDependentPrimitive2D(), 1051 maColorA(aColorA), 1052 maColorB(aColorB), 1053 mfScaleA(fScaleA), 1054 mfScaleB(fScaleB), 1055 mpTranslate(0) 1056 { 1057 // scale A and B have to be positive 1058 mfScaleA = ::std::max(mfScaleA, 0.0); 1059 mfScaleB = ::std::max(mfScaleB, 0.0); 1060 1061 // scale B has to be bigger than scale A; swap if different 1062 if(mfScaleA > mfScaleB) 1063 { 1064 OSL_ENSURE(false, "Wrong offset order (!)"); 1065 ::std::swap(mfScaleA, mfScaleB); 1066 } 1067 } 1068 1069 SvgRadialAtomPrimitive2D::~SvgRadialAtomPrimitive2D() 1070 { 1071 if(mpTranslate) 1072 { 1073 delete mpTranslate; 1074 mpTranslate = 0; 1075 } 1076 } 1077 1078 bool SvgRadialAtomPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const 1079 { 1080 if(DiscreteMetricDependentPrimitive2D::operator==(rPrimitive)) 1081 { 1082 const SvgRadialAtomPrimitive2D& rCompare = static_cast< const SvgRadialAtomPrimitive2D& >(rPrimitive); 1083 1084 if(getColorA() == rCompare.getColorA() 1085 && getColorB() == rCompare.getColorB() 1086 && getScaleA() == rCompare.getScaleA() 1087 && getScaleB() == rCompare.getScaleB()) 1088 { 1089 if(isTranslateSet() && rCompare.isTranslateSet()) 1090 { 1091 return (getTranslateA() == rCompare.getTranslateA() 1092 && getTranslateB() == rCompare.getTranslateB()); 1093 } 1094 else if(!isTranslateSet() && !rCompare.isTranslateSet()) 1095 { 1096 return true; 1097 } 1098 } 1099 } 1100 1101 return false; 1102 } 1103 1104 // provide unique ID 1105 ImplPrimitrive2DIDBlock(SvgRadialAtomPrimitive2D, PRIMITIVE2D_ID_SVGRADIALATOMPRIMITIVE2D) 1106 1107 } // end of namespace primitive2d 1108 } // end of namespace drawinglayer 1109 1110 ////////////////////////////////////////////////////////////////////////////// 1111 // eof 1112