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