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 bool bUseUnitCoordinates, 292 SpreadMethod aSpreadMethod) 293 : maPolyPolygon(rPolyPolygon), 294 maGradientEntries(rGradientEntries), 295 maStart(rStart), 296 maSpreadMethod(aSpreadMethod), 297 mbPreconditionsChecked(false), 298 mbCreatesContent(false), 299 mbSingleEntry(false), 300 mbFullyOpaque(true), 301 mbUseUnitCoordinates(bUseUnitCoordinates) 302 { 303 } 304 305 bool SvgGradientHelper::operator==(const SvgGradientHelper& rSvgGradientHelper) const 306 { 307 const SvgGradientHelper& rCompare = static_cast< const SvgGradientHelper& >(rSvgGradientHelper); 308 309 return (getPolyPolygon() == rCompare.getPolyPolygon() 310 && getGradientEntries() == rCompare.getGradientEntries() 311 && getStart() == rCompare.getStart() 312 && getUseUnitCoordinates() == rCompare.getUseUnitCoordinates() 313 && getSpreadMethod() == rCompare.getSpreadMethod()); 314 } 315 316 } // end of namespace primitive2d 317 } // end of namespace drawinglayer 318 319 ////////////////////////////////////////////////////////////////////////////// 320 321 namespace drawinglayer 322 { 323 namespace primitive2d 324 { 325 void SvgLinearGradientPrimitive2D::checkPreconditions() 326 { 327 // call parent 328 SvgGradientHelper::checkPreconditions(); 329 330 if(getCreatesContent()) 331 { 332 // Check Vector 333 const basegfx::B2DVector aVector(getEnd() - getStart()); 334 335 if(basegfx::fTools::equalZero(aVector.getX()) && basegfx::fTools::equalZero(aVector.getY())) 336 { 337 // fill with single color using last stop color 338 setSingleEntry(); 339 } 340 } 341 } 342 343 void SvgLinearGradientPrimitive2D::createAtom( 344 Primitive2DVector& rTargetColor, 345 Primitive2DVector& rTargetOpacity, 346 const SvgGradientEntry& rFrom, 347 const SvgGradientEntry& rTo, 348 sal_Int32 nOffset) const 349 { 350 // create gradient atom [rFrom.getOffset() .. rTo.getOffset()] with (rFrom.getOffset() > rTo.getOffset()) 351 if(rFrom.getOffset() == rTo.getOffset()) 352 { 353 OSL_ENSURE(false, "SvgGradient Atom creation with no step width (!)"); 354 } 355 else 356 { 357 rTargetColor.push_back( 358 new SvgLinearAtomPrimitive2D( 359 rFrom.getColor(), rFrom.getOffset() + nOffset, 360 rTo.getColor(), rTo.getOffset() + nOffset)); 361 362 if(!getFullyOpaque()) 363 { 364 const double fTransFrom(1.0 - rFrom.getOpacity()); 365 const double fTransTo(1.0 - rTo.getOpacity()); 366 const basegfx::BColor aColorFrom(fTransFrom, fTransFrom, fTransFrom); 367 const basegfx::BColor aColorTo(fTransTo, fTransTo, fTransTo); 368 369 rTargetOpacity.push_back( 370 new SvgLinearAtomPrimitive2D( 371 aColorFrom, rFrom.getOffset() + nOffset, 372 aColorTo, rTo.getOffset() + nOffset)); 373 } 374 } 375 } 376 377 Primitive2DSequence SvgLinearGradientPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const 378 { 379 Primitive2DSequence xRetval; 380 381 if(!getPreconditionsChecked()) 382 { 383 const_cast< SvgLinearGradientPrimitive2D* >(this)->checkPreconditions(); 384 } 385 386 if(getSingleEntry()) 387 { 388 // fill with last existing color 389 xRetval = createSingleGradientEntryFill(); 390 } 391 else if(getCreatesContent()) 392 { 393 // at least two color stops in range [0.0 .. 1.0], sorted, non-null vector, not completely 394 // invisible, width and height to fill are not empty 395 const basegfx::B2DRange aPolyRange(getPolyPolygon().getB2DRange()); 396 const double fPolyWidth(aPolyRange.getWidth()); 397 const double fPolyHeight(aPolyRange.getHeight()); 398 399 // create ObjectTransform based on polygon range 400 const basegfx::B2DHomMatrix aObjectTransform( 401 basegfx::tools::createScaleTranslateB2DHomMatrix( 402 fPolyWidth, fPolyHeight, 403 aPolyRange.getMinX(), aPolyRange.getMinY())); 404 basegfx::B2DHomMatrix aUnitGradientToObject; 405 static bool bInterpretAbsolute(true); 406 407 if(getUseUnitCoordinates()) 408 { 409 // interpret in unit coordinate system -> object aspect ratio will scale result 410 // create unit transform from unit vector [0.0 .. 1.0] along the X-Axis to given 411 // gradient vector defined by Start,End 412 const basegfx::B2DVector aVector(getEnd() - getStart()); 413 const double fVectorLength(aVector.getLength()); 414 basegfx::B2DHomMatrix aUnitGradientToGradient; 415 416 aUnitGradientToGradient.scale(fVectorLength, 1.0); 417 aUnitGradientToGradient.rotate(atan2(aVector.getY(), aVector.getX())); 418 aUnitGradientToGradient.translate(getStart().getX(), getStart().getY()); 419 420 // create full transform from unit gradient coordinates to object coordinates 421 // including the SvgGradient transformation 422 aUnitGradientToObject = aObjectTransform * aUnitGradientToGradient; 423 } 424 else 425 { 426 // interpret in object coordinate system -> object aspect ratio will not scale result 427 const basegfx::B2DPoint aStart(aObjectTransform * getStart()); 428 const basegfx::B2DPoint aEnd(aObjectTransform * getEnd()); 429 const basegfx::B2DVector aVector(aEnd - aStart); 430 431 aUnitGradientToObject.scale(aVector.getLength(), 1.0); 432 aUnitGradientToObject.rotate(atan2(aVector.getY(), aVector.getX())); 433 aUnitGradientToObject.translate(aStart.getX(), aStart.getY()); 434 } 435 436 // create inverse from it 437 basegfx::B2DHomMatrix aObjectToUnitGradient(aUnitGradientToObject); 438 aObjectToUnitGradient.invert(); 439 440 // back-transform polygon to unit gradient coordinates and get 441 // UnitRage. This is the range the gradient has to cover 442 basegfx::B2DPolyPolygon aUnitPoly(getPolyPolygon()); 443 aUnitPoly.transform(aObjectToUnitGradient); 444 const basegfx::B2DRange aUnitRange(aUnitPoly.getB2DRange()); 445 446 // prepare result vectors 447 Primitive2DVector aTargetColor; 448 Primitive2DVector aTargetOpacity; 449 450 if(basegfx::fTools::more(aUnitRange.getWidth(), 0.0)) 451 { 452 // add a pre-multiply to aUnitGradientToObject to allow 453 // multiplication of the polygon(xl, 0.0, xr, 1.0) 454 const basegfx::B2DHomMatrix aPreMultiply( 455 basegfx::tools::createScaleTranslateB2DHomMatrix( 456 1.0, aUnitRange.getHeight(), 0.0, aUnitRange.getMinY())); 457 aUnitGradientToObject = aUnitGradientToObject * aPreMultiply; 458 459 // create central run, may also already do all necessary when 460 // Spread_pad is set as SpreadMethod and/or the range is smaller 461 double fPos(createRun(aTargetColor, aTargetOpacity, aUnitRange.getMinX(), aUnitRange.getMaxX(), getGradientEntries(), 0)); 462 463 if(fPos < aUnitRange.getMaxX()) 464 { 465 // can only happen when SpreadMethod is Spread_reflect or Spread_repeat, 466 // else the start and end pads are already created and fPos == aUnitRange.getMaxX(). 467 // Its possible to express the repeated linear gradient by adding the 468 // transformed central run. Crete it this way 469 Primitive2DSequence aTargetColorEntries(Primitive2DVectorToPrimitive2DSequence(aTargetColor)); 470 Primitive2DSequence aTargetOpacityEntries(Primitive2DVectorToPrimitive2DSequence(aTargetOpacity)); 471 aTargetColor.clear(); 472 aTargetOpacity.clear(); 473 474 if(aTargetColorEntries.hasElements()) 475 { 476 // add original central run as group primitive 477 aTargetColor.push_back(new GroupPrimitive2D(aTargetColorEntries)); 478 479 if(aTargetOpacityEntries.hasElements()) 480 { 481 aTargetOpacity.push_back(new GroupPrimitive2D(aTargetOpacityEntries)); 482 } 483 484 // add negative runs 485 fPos = 0.0; 486 sal_Int32 nOffset(0); 487 488 while(fPos > aUnitRange.getMinX()) 489 { 490 fPos -= 1.0; 491 nOffset++; 492 493 basegfx::B2DHomMatrix aTransform; 494 const bool bMirror(Spread_reflect == getSpreadMethod() && (nOffset % 2)); 495 496 if(bMirror) 497 { 498 aTransform.scale(-1.0, 1.0); 499 aTransform.translate(fPos + 1.0, 0.0); 500 } 501 else 502 { 503 aTransform.translate(fPos, 0.0); 504 } 505 506 aTargetColor.push_back(new TransformPrimitive2D(aTransform, aTargetColorEntries)); 507 508 if(aTargetOpacityEntries.hasElements()) 509 { 510 aTargetOpacity.push_back(new TransformPrimitive2D(aTransform, aTargetOpacityEntries)); 511 } 512 } 513 514 // add positive runs 515 fPos = 1.0; 516 nOffset = 1; 517 518 while(fPos < aUnitRange.getMaxX()) 519 { 520 basegfx::B2DHomMatrix aTransform; 521 const bool bMirror(Spread_reflect == getSpreadMethod() && (nOffset % 2)); 522 523 if(bMirror) 524 { 525 aTransform.scale(-1.0, 1.0); 526 aTransform.translate(fPos + 1.0, 0.0); 527 } 528 else 529 { 530 aTransform.translate(fPos, 0.0); 531 } 532 533 aTargetColor.push_back(new TransformPrimitive2D(aTransform, aTargetColorEntries)); 534 535 if(aTargetOpacityEntries.hasElements()) 536 { 537 aTargetOpacity.push_back(new TransformPrimitive2D(aTransform, aTargetOpacityEntries)); 538 } 539 540 fPos += 1.0; 541 nOffset++; 542 } 543 } 544 } 545 } 546 547 xRetval = createResult(aTargetColor, aTargetOpacity, aUnitGradientToObject); 548 } 549 550 return xRetval; 551 } 552 553 SvgLinearGradientPrimitive2D::SvgLinearGradientPrimitive2D( 554 const basegfx::B2DPolyPolygon& rPolyPolygon, 555 const SvgGradientEntryVector& rGradientEntries, 556 const basegfx::B2DPoint& rStart, 557 const basegfx::B2DPoint& rEnd, 558 bool bUseUnitCoordinates, 559 SpreadMethod aSpreadMethod) 560 : BufferedDecompositionPrimitive2D(), 561 SvgGradientHelper(rPolyPolygon, rGradientEntries, rStart, bUseUnitCoordinates, aSpreadMethod), 562 maEnd(rEnd) 563 { 564 } 565 566 SvgLinearGradientPrimitive2D::~SvgLinearGradientPrimitive2D() 567 { 568 } 569 570 bool SvgLinearGradientPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const 571 { 572 const SvgGradientHelper* pSvgGradientHelper = dynamic_cast< const SvgGradientHelper* >(&rPrimitive); 573 574 if(pSvgGradientHelper && SvgGradientHelper::operator==(*pSvgGradientHelper)) 575 { 576 const SvgLinearGradientPrimitive2D& rCompare = static_cast< const SvgLinearGradientPrimitive2D& >(rPrimitive); 577 578 return (getEnd() == rCompare.getEnd()); 579 } 580 581 return false; 582 } 583 584 basegfx::B2DRange SvgLinearGradientPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const 585 { 586 // return ObjectRange 587 return getPolyPolygon().getB2DRange(); 588 } 589 590 // provide unique ID 591 ImplPrimitrive2DIDBlock(SvgLinearGradientPrimitive2D, PRIMITIVE2D_ID_SVGLINEARGRADIENTPRIMITIVE2D) 592 593 } // end of namespace primitive2d 594 } // end of namespace drawinglayer 595 596 ////////////////////////////////////////////////////////////////////////////// 597 598 namespace drawinglayer 599 { 600 namespace primitive2d 601 { 602 void SvgRadialGradientPrimitive2D::checkPreconditions() 603 { 604 // call parent 605 SvgGradientHelper::checkPreconditions(); 606 607 if(getCreatesContent()) 608 { 609 // Check Radius 610 if(basegfx::fTools::equalZero(getRadius())) 611 { 612 // fill with single color using last stop color 613 setSingleEntry(); 614 } 615 } 616 } 617 618 void SvgRadialGradientPrimitive2D::createAtom( 619 Primitive2DVector& rTargetColor, 620 Primitive2DVector& rTargetOpacity, 621 const SvgGradientEntry& rFrom, 622 const SvgGradientEntry& rTo, 623 sal_Int32 nOffset) const 624 { 625 // create gradient atom [rFrom.getOffset() .. rTo.getOffset()] with (rFrom.getOffset() > rTo.getOffset()) 626 if(rFrom.getOffset() == rTo.getOffset()) 627 { 628 OSL_ENSURE(false, "SvgGradient Atom creation with no step width (!)"); 629 } 630 else 631 { 632 const double fScaleFrom(rFrom.getOffset() + nOffset); 633 const double fScaleTo(rTo.getOffset() + nOffset); 634 635 if(isFocalSet()) 636 { 637 const basegfx::B2DVector aTranslateFrom(maFocalVector * (maFocalLength - fScaleFrom)); 638 const basegfx::B2DVector aTranslateTo(maFocalVector * (maFocalLength - fScaleTo)); 639 640 rTargetColor.push_back( 641 new SvgRadialAtomPrimitive2D( 642 rFrom.getColor(), fScaleFrom, aTranslateFrom, 643 rTo.getColor(), fScaleTo, aTranslateTo)); 644 } 645 else 646 { 647 rTargetColor.push_back( 648 new SvgRadialAtomPrimitive2D( 649 rFrom.getColor(), fScaleFrom, 650 rTo.getColor(), fScaleTo)); 651 } 652 653 if(!getFullyOpaque()) 654 { 655 const double fTransFrom(1.0 - rFrom.getOpacity()); 656 const double fTransTo(1.0 - rTo.getOpacity()); 657 const basegfx::BColor aColorFrom(fTransFrom, fTransFrom, fTransFrom); 658 const basegfx::BColor aColorTo(fTransTo, fTransTo, fTransTo); 659 660 if(isFocalSet()) 661 { 662 const basegfx::B2DVector aTranslateFrom(maFocalVector * (maFocalLength - fScaleFrom)); 663 const basegfx::B2DVector aTranslateTo(maFocalVector * (maFocalLength - fScaleTo)); 664 665 rTargetOpacity.push_back( 666 new SvgRadialAtomPrimitive2D( 667 aColorFrom, fScaleFrom, aTranslateFrom, 668 aColorTo, fScaleTo, aTranslateTo)); 669 } 670 else 671 { 672 rTargetOpacity.push_back( 673 new SvgRadialAtomPrimitive2D( 674 aColorFrom, fScaleFrom, 675 aColorTo, fScaleTo)); 676 } 677 } 678 } 679 } 680 681 const SvgGradientEntryVector& SvgRadialGradientPrimitive2D::getMirroredGradientEntries() const 682 { 683 if(maMirroredGradientEntries.empty() && !getGradientEntries().empty()) 684 { 685 const_cast< SvgRadialGradientPrimitive2D* >(this)->createMirroredGradientEntries(); 686 } 687 688 return maMirroredGradientEntries; 689 } 690 691 void SvgRadialGradientPrimitive2D::createMirroredGradientEntries() 692 { 693 if(maMirroredGradientEntries.empty() && !getGradientEntries().empty()) 694 { 695 const sal_uInt32 nCount(getGradientEntries().size()); 696 maMirroredGradientEntries.clear(); 697 maMirroredGradientEntries.reserve(nCount); 698 699 for(sal_uInt32 a(0); a < nCount; a++) 700 { 701 const SvgGradientEntry& rCandidate = getGradientEntries()[nCount - 1 - a]; 702 703 maMirroredGradientEntries.push_back( 704 SvgGradientEntry( 705 1.0 - rCandidate.getOffset(), 706 rCandidate.getColor(), 707 rCandidate.getOpacity())); 708 } 709 } 710 } 711 712 Primitive2DSequence SvgRadialGradientPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const 713 { 714 Primitive2DSequence xRetval; 715 716 if(!getPreconditionsChecked()) 717 { 718 const_cast< SvgRadialGradientPrimitive2D* >(this)->checkPreconditions(); 719 } 720 721 if(getSingleEntry()) 722 { 723 // fill with last existing color 724 xRetval = createSingleGradientEntryFill(); 725 } 726 else if(getCreatesContent()) 727 { 728 // at least two color stops in range [0.0 .. 1.0], sorted, non-null vector, not completely 729 // invisible, width and height to fill are not empty 730 const basegfx::B2DRange aPolyRange(getPolyPolygon().getB2DRange()); 731 const double fPolyWidth(aPolyRange.getWidth()); 732 const double fPolyHeight(aPolyRange.getHeight()); 733 734 // create ObjectTransform based on polygon range 735 const basegfx::B2DHomMatrix aObjectTransform( 736 basegfx::tools::createScaleTranslateB2DHomMatrix( 737 fPolyWidth, fPolyHeight, 738 aPolyRange.getMinX(), aPolyRange.getMinY())); 739 basegfx::B2DHomMatrix aUnitGradientToObject; 740 static bool bInterpretAbsolute(true); 741 742 if(getUseUnitCoordinates()) 743 { 744 // interpret in unit coordinate system -> object aspect ratio will scale result 745 // create unit transform from unit vector to given linear gradient vector 746 basegfx::B2DHomMatrix aUnitGradientToGradient; 747 748 aUnitGradientToGradient.scale(getRadius(), getRadius()); 749 aUnitGradientToGradient.translate(getStart().getX(), getStart().getY()); 750 751 // create full transform from unit gradient coordinates to object coordinates 752 // including the SvgGradient transformation 753 aUnitGradientToObject = aObjectTransform * aUnitGradientToGradient; 754 } 755 else 756 { 757 // interpret in object coordinate system -> object aspect ratio will not scale result 758 const double fRadius((aObjectTransform * basegfx::B2DVector(getRadius(), 0.0)).getLength()); 759 const basegfx::B2DPoint aStart(aObjectTransform * getStart()); 760 761 aUnitGradientToObject.scale(fRadius, fRadius); 762 aUnitGradientToObject.translate(aStart.getX(), aStart.getY()); 763 } 764 765 // create inverse from it 766 basegfx::B2DHomMatrix aObjectToUnitGradient(aUnitGradientToObject); 767 aObjectToUnitGradient.invert(); 768 769 // back-transform polygon to unit gradient coordinates and get 770 // UnitRage. This is the range the gradient has to cover 771 basegfx::B2DPolyPolygon aUnitPoly(getPolyPolygon()); 772 aUnitPoly.transform(aObjectToUnitGradient); 773 const basegfx::B2DRange aUnitRange(aUnitPoly.getB2DRange()); 774 775 // create range which the gradient has to cover to cover the whole given geometry. 776 // For circle, go from 0.0 to max radius in all directions (the corners) 777 double fMax(basegfx::B2DVector(aUnitRange.getMinimum()).getLength()); 778 fMax = std::max(fMax, basegfx::B2DVector(aUnitRange.getMaximum()).getLength()); 779 fMax = std::max(fMax, basegfx::B2DVector(aUnitRange.getMinX(), aUnitRange.getMaxY()).getLength()); 780 fMax = std::max(fMax, basegfx::B2DVector(aUnitRange.getMaxX(), aUnitRange.getMinY()).getLength()); 781 782 // prepare result vectors 783 Primitive2DVector aTargetColor; 784 Primitive2DVector aTargetOpacity; 785 786 if(0.0 < fMax) 787 { 788 // prepare maFocalVector 789 if(isFocalSet()) 790 { 791 const_cast< SvgRadialGradientPrimitive2D* >(this)->maFocalLength = fMax; 792 } 793 794 // create central run, may also already do all necessary when 795 // Spread_pad is set as SpreadMethod and/or the range is smaller 796 double fPos(createRun(aTargetColor, aTargetOpacity, 0.0, fMax, getGradientEntries(), 0)); 797 798 if(fPos < fMax) 799 { 800 // can only happen when SpreadMethod is Spread_reflect or Spread_repeat, 801 // else the start and end pads are already created and fPos == fMax. 802 // For radial there is no way to transform the already created 803 // central run, it needs to be created from 1.0 to fMax 804 sal_Int32 nOffset(1); 805 806 while(fPos < fMax) 807 { 808 const bool bMirror(Spread_reflect == getSpreadMethod() && (nOffset % 2)); 809 810 if(bMirror) 811 { 812 createRun(aTargetColor, aTargetOpacity, 0.0, fMax, getMirroredGradientEntries(), nOffset); 813 } 814 else 815 { 816 createRun(aTargetColor, aTargetOpacity, 0.0, fMax, getGradientEntries(), nOffset); 817 } 818 819 nOffset++; 820 fPos += 1.0; 821 } 822 } 823 } 824 825 xRetval = createResult(aTargetColor, aTargetOpacity, aUnitGradientToObject, true); 826 } 827 828 return xRetval; 829 } 830 831 SvgRadialGradientPrimitive2D::SvgRadialGradientPrimitive2D( 832 const basegfx::B2DPolyPolygon& rPolyPolygon, 833 const SvgGradientEntryVector& rGradientEntries, 834 const basegfx::B2DPoint& rStart, 835 double fRadius, 836 bool bUseUnitCoordinates, 837 SpreadMethod aSpreadMethod, 838 const basegfx::B2DPoint* pFocal) 839 : BufferedDecompositionPrimitive2D(), 840 SvgGradientHelper(rPolyPolygon, rGradientEntries, rStart, bUseUnitCoordinates, aSpreadMethod), 841 mfRadius(fRadius), 842 maFocal(rStart), 843 maFocalVector(0.0, 0.0), 844 maFocalLength(0.0), 845 maMirroredGradientEntries(), 846 mbFocalSet(false) 847 { 848 if(pFocal && !pFocal->equal(getStart())) 849 { 850 maFocal = *pFocal; 851 maFocalVector = maFocal - getStart(); 852 mbFocalSet = true; 853 } 854 } 855 856 SvgRadialGradientPrimitive2D::~SvgRadialGradientPrimitive2D() 857 { 858 } 859 860 bool SvgRadialGradientPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const 861 { 862 const SvgGradientHelper* pSvgGradientHelper = dynamic_cast< const SvgGradientHelper* >(&rPrimitive); 863 864 if(pSvgGradientHelper && SvgGradientHelper::operator==(*pSvgGradientHelper)) 865 { 866 const SvgRadialGradientPrimitive2D& rCompare = static_cast< const SvgRadialGradientPrimitive2D& >(rPrimitive); 867 868 if(getRadius() == rCompare.getRadius()) 869 { 870 if(isFocalSet() == rCompare.isFocalSet()) 871 { 872 if(isFocalSet()) 873 { 874 return getFocal() == rCompare.getFocal(); 875 } 876 else 877 { 878 return true; 879 } 880 } 881 } 882 } 883 884 return false; 885 } 886 887 basegfx::B2DRange SvgRadialGradientPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const 888 { 889 // return ObjectRange 890 return getPolyPolygon().getB2DRange(); 891 } 892 893 // provide unique ID 894 ImplPrimitrive2DIDBlock(SvgRadialGradientPrimitive2D, PRIMITIVE2D_ID_SVGRADIALGRADIENTPRIMITIVE2D) 895 896 } // end of namespace primitive2d 897 } // end of namespace drawinglayer 898 899 ////////////////////////////////////////////////////////////////////////////// 900 // SvgLinearAtomPrimitive2D class 901 902 namespace drawinglayer 903 { 904 namespace primitive2d 905 { 906 Primitive2DSequence SvgLinearAtomPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const 907 { 908 Primitive2DSequence xRetval; 909 const double fDelta(getOffsetB() - getOffsetA()); 910 911 if(!basegfx::fTools::equalZero(fDelta)) 912 { 913 // use one discrete unit for overlap (one pixel) 914 const double fDiscreteUnit(getDiscreteUnit()); 915 916 // use color distance and discrete lengths to calculate step count 917 const sal_uInt32 nSteps(calculateStepsForSvgGradient(getColorA(), getColorB(), fDelta, fDiscreteUnit)); 918 919 // prepare polygon in needed width at start position (with discrete overlap) 920 const basegfx::B2DPolygon aPolygon( 921 basegfx::tools::createPolygonFromRect( 922 basegfx::B2DRange( 923 getOffsetA() - fDiscreteUnit, 924 0.0, 925 getOffsetA() + (fDelta / nSteps) + fDiscreteUnit, 926 1.0))); 927 928 // prepare loop (inside to outside, [0.0 .. 1.0[) 929 double fUnitScale(0.0); 930 const double fUnitStep(1.0 / nSteps); 931 932 // prepare result set (known size) 933 xRetval.realloc(nSteps); 934 935 for(sal_uInt32 a(0); a < nSteps; a++, fUnitScale += fUnitStep) 936 { 937 basegfx::B2DPolygon aNew(aPolygon); 938 939 aNew.transform(basegfx::tools::createTranslateB2DHomMatrix(fDelta * fUnitScale, 0.0)); 940 xRetval[a] = new PolyPolygonColorPrimitive2D( 941 basegfx::B2DPolyPolygon(aNew), 942 basegfx::interpolate(getColorA(), getColorB(), fUnitScale)); 943 } 944 } 945 946 return xRetval; 947 } 948 949 SvgLinearAtomPrimitive2D::SvgLinearAtomPrimitive2D( 950 const basegfx::BColor& aColorA, double fOffsetA, 951 const basegfx::BColor& aColorB, double fOffsetB) 952 : DiscreteMetricDependentPrimitive2D(), 953 maColorA(aColorA), 954 maColorB(aColorB), 955 mfOffsetA(fOffsetA), 956 mfOffsetB(fOffsetB) 957 { 958 if(mfOffsetA > mfOffsetB) 959 { 960 OSL_ENSURE(false, "Wrong offset order (!)"); 961 ::std::swap(mfOffsetA, mfOffsetB); 962 } 963 } 964 965 bool SvgLinearAtomPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const 966 { 967 if(DiscreteMetricDependentPrimitive2D::operator==(rPrimitive)) 968 { 969 const SvgLinearAtomPrimitive2D& rCompare = static_cast< const SvgLinearAtomPrimitive2D& >(rPrimitive); 970 971 return (getColorA() == rCompare.getColorA() 972 && getColorB() == rCompare.getColorB() 973 && getOffsetA() == rCompare.getOffsetA() 974 && getOffsetB() == rCompare.getOffsetB()); 975 } 976 977 return false; 978 } 979 980 // provide unique ID 981 ImplPrimitrive2DIDBlock(SvgLinearAtomPrimitive2D, PRIMITIVE2D_ID_SVGLINEARATOMPRIMITIVE2D) 982 983 } // end of namespace primitive2d 984 } // end of namespace drawinglayer 985 986 ////////////////////////////////////////////////////////////////////////////// 987 // SvgRadialAtomPrimitive2D class 988 989 namespace drawinglayer 990 { 991 namespace primitive2d 992 { 993 Primitive2DSequence SvgRadialAtomPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const 994 { 995 Primitive2DSequence xRetval; 996 const double fDeltaScale(getScaleB() - getScaleA()); 997 998 if(!basegfx::fTools::equalZero(fDeltaScale)) 999 { 1000 // use one discrete unit for overlap (one pixel) 1001 const double fDiscreteUnit(getDiscreteUnit()); 1002 1003 // use color distance and discrete lengths to calculate step count 1004 const sal_uInt32 nSteps(calculateStepsForSvgGradient(getColorA(), getColorB(), fDeltaScale, fDiscreteUnit)); 1005 1006 // prepare loop ([0.0 .. 1.0[, full polygons, no polypolygons with holes) 1007 double fUnitScale(0.0); 1008 const double fUnitStep(1.0 / nSteps); 1009 1010 // prepare result set (known size) 1011 xRetval.realloc(nSteps); 1012 1013 for(sal_uInt32 a(0); a < nSteps; a++, fUnitScale += fUnitStep) 1014 { 1015 basegfx::B2DHomMatrix aTransform; 1016 const double fEndScale(getScaleB() - (fDeltaScale * fUnitScale)); 1017 1018 if(isTranslateSet()) 1019 { 1020 const basegfx::B2DVector aTranslate( 1021 basegfx::interpolate( 1022 getTranslateB(), 1023 getTranslateA(), 1024 fUnitScale)); 1025 1026 aTransform = basegfx::tools::createScaleTranslateB2DHomMatrix( 1027 fEndScale, 1028 fEndScale, 1029 aTranslate.getX(), 1030 aTranslate.getY()); 1031 } 1032 else 1033 { 1034 aTransform = basegfx::tools::createScaleB2DHomMatrix( 1035 fEndScale, 1036 fEndScale); 1037 } 1038 1039 basegfx::B2DPolygon aNew(basegfx::tools::createPolygonFromUnitCircle()); 1040 1041 aNew.transform(aTransform); 1042 xRetval[a] = new PolyPolygonColorPrimitive2D( 1043 basegfx::B2DPolyPolygon(aNew), 1044 basegfx::interpolate(getColorB(), getColorA(), fUnitScale)); 1045 } 1046 } 1047 1048 return xRetval; 1049 } 1050 1051 SvgRadialAtomPrimitive2D::SvgRadialAtomPrimitive2D( 1052 const basegfx::BColor& aColorA, double fScaleA, const basegfx::B2DVector& rTranslateA, 1053 const basegfx::BColor& aColorB, double fScaleB, const basegfx::B2DVector& rTranslateB) 1054 : DiscreteMetricDependentPrimitive2D(), 1055 maColorA(aColorA), 1056 maColorB(aColorB), 1057 mfScaleA(fScaleA), 1058 mfScaleB(fScaleB), 1059 mpTranslate(0) 1060 { 1061 // check and evtl. set translations 1062 if(!rTranslateA.equal(rTranslateB)) 1063 { 1064 mpTranslate = new VectorPair(rTranslateA, rTranslateB); 1065 } 1066 1067 // scale A and B have to be positive 1068 mfScaleA = ::std::max(mfScaleA, 0.0); 1069 mfScaleB = ::std::max(mfScaleB, 0.0); 1070 1071 // scale B has to be bigger than scale A; swap if different 1072 if(mfScaleA > mfScaleB) 1073 { 1074 OSL_ENSURE(false, "Wrong offset order (!)"); 1075 ::std::swap(mfScaleA, mfScaleB); 1076 1077 if(mpTranslate) 1078 { 1079 ::std::swap(mpTranslate->maTranslateA, mpTranslate->maTranslateB); 1080 } 1081 } 1082 } 1083 1084 SvgRadialAtomPrimitive2D::SvgRadialAtomPrimitive2D( 1085 const basegfx::BColor& aColorA, double fScaleA, 1086 const basegfx::BColor& aColorB, double fScaleB) 1087 : DiscreteMetricDependentPrimitive2D(), 1088 maColorA(aColorA), 1089 maColorB(aColorB), 1090 mfScaleA(fScaleA), 1091 mfScaleB(fScaleB), 1092 mpTranslate(0) 1093 { 1094 // scale A and B have to be positive 1095 mfScaleA = ::std::max(mfScaleA, 0.0); 1096 mfScaleB = ::std::max(mfScaleB, 0.0); 1097 1098 // scale B has to be bigger than scale A; swap if different 1099 if(mfScaleA > mfScaleB) 1100 { 1101 OSL_ENSURE(false, "Wrong offset order (!)"); 1102 ::std::swap(mfScaleA, mfScaleB); 1103 } 1104 } 1105 1106 SvgRadialAtomPrimitive2D::~SvgRadialAtomPrimitive2D() 1107 { 1108 if(mpTranslate) 1109 { 1110 delete mpTranslate; 1111 mpTranslate = 0; 1112 } 1113 } 1114 1115 bool SvgRadialAtomPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const 1116 { 1117 if(DiscreteMetricDependentPrimitive2D::operator==(rPrimitive)) 1118 { 1119 const SvgRadialAtomPrimitive2D& rCompare = static_cast< const SvgRadialAtomPrimitive2D& >(rPrimitive); 1120 1121 if(getColorA() == rCompare.getColorA() 1122 && getColorB() == rCompare.getColorB() 1123 && getScaleA() == rCompare.getScaleA() 1124 && getScaleB() == rCompare.getScaleB()) 1125 { 1126 if(isTranslateSet() && rCompare.isTranslateSet()) 1127 { 1128 return (getTranslateA() == rCompare.getTranslateA() 1129 && getTranslateB() == rCompare.getTranslateB()); 1130 } 1131 else if(!isTranslateSet() && !rCompare.isTranslateSet()) 1132 { 1133 return true; 1134 } 1135 } 1136 } 1137 1138 return false; 1139 } 1140 1141 // provide unique ID 1142 ImplPrimitrive2DIDBlock(SvgRadialAtomPrimitive2D, PRIMITIVE2D_ID_SVGRADIALATOMPRIMITIVE2D) 1143 1144 } // end of namespace primitive2d 1145 } // end of namespace drawinglayer 1146 1147 ////////////////////////////////////////////////////////////////////////////// 1148 // eof 1149