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