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