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 basegfx::B2DRange aPolyRange(getPolyPolygon().getB2DRange()); 813 const double fPolyWidth(aPolyRange.getWidth()); 814 const double fPolyHeight(aPolyRange.getHeight()); 815 816 // create ObjectTransform based on polygon range 817 const basegfx::B2DHomMatrix aObjectTransform( 818 basegfx::tools::createScaleTranslateB2DHomMatrix( 819 fPolyWidth, fPolyHeight, 820 aPolyRange.getMinX(), aPolyRange.getMinY())); 821 822 // create unit transform from unit vector to given linear gradient vector 823 basegfx::B2DHomMatrix aUnitGradientToGradient; 824 825 aUnitGradientToGradient.scale(getRadius(), getRadius()); 826 aUnitGradientToGradient.translate(getStart().getX(), getStart().getY()); 827 828 // create full transform from unit gradient coordinates to object coordinates 829 // including the SvgGradient transformation 830 basegfx::B2DHomMatrix aUnitGradientToObject(aObjectTransform * aUnitGradientToGradient); 831 832 // create inverse from it 833 basegfx::B2DHomMatrix aObjectToUnitGradient(aUnitGradientToObject); 834 aObjectToUnitGradient.invert(); 835 836 // back-transform polygon to unit gradient coordinates and get 837 // UnitRage. This is the range the gradient has to cover 838 basegfx::B2DPolyPolygon aUnitPoly(getPolyPolygon()); 839 aUnitPoly.transform(aObjectToUnitGradient); 840 const basegfx::B2DRange aUnitRange(aUnitPoly.getB2DRange()); 841 842 // create range which the gradient has to cover to cover the whole given geometry. 843 // For circle, go from 0.0 to max radius in all directions (the corners) 844 double fMax(basegfx::B2DVector(aUnitRange.getMinimum()).getLength()); 845 fMax = std::max(fMax, basegfx::B2DVector(aUnitRange.getMaximum()).getLength()); 846 fMax = std::max(fMax, basegfx::B2DVector(aUnitRange.getMinX(), aUnitRange.getMaxY()).getLength()); 847 fMax = std::max(fMax, basegfx::B2DVector(aUnitRange.getMaxX(), aUnitRange.getMinY()).getLength()); 848 849 // prepare result vectors 850 Primitive2DVector aTargetColor; 851 Primitive2DVector aTargetOpacity; 852 853 if(0.0 < fMax) 854 { 855 // prepare maFocalVector 856 if(isFocalSet()) 857 { 858 const_cast< SvgRadialGradientPrimitive2D* >(this)->maFocalLength = fMax; 859 } 860 861 // create central run, may also already do all necessary when 862 // Spread_pad is set as SpreadMethod and/or the range is smaller 863 double fPos(createRun(aTargetColor, aTargetOpacity, 0.0, fMax, getGradientEntries(), 0)); 864 865 if(fPos < fMax) 866 { 867 // can only happen when SpreadMethod is Spread_reflect or Spread_repeat, 868 // else the start and end pads are already created and fPos == fMax. 869 // For radial there is no way to transform the already created 870 // central run, it needs to be created from 1.0 to fMax 871 sal_Int32 nOffset(1); 872 873 while(fPos < fMax) 874 { 875 const bool bMirror(Spread_reflect == getSpreadMethod() && (nOffset % 2)); 876 877 if(bMirror) 878 { 879 createRun(aTargetColor, aTargetOpacity, 0.0, fMax, getMirroredGradientEntries(), nOffset); 880 } 881 else 882 { 883 createRun(aTargetColor, aTargetOpacity, 0.0, fMax, getGradientEntries(), nOffset); 884 } 885 886 nOffset++; 887 fPos += 1.0; 888 } 889 } 890 } 891 892 xRetval = createResult(aTargetColor, aTargetOpacity, aUnitGradientToObject, true); 893 } 894 895 return xRetval; 896 } 897 898 SvgRadialGradientPrimitive2D::SvgRadialGradientPrimitive2D( 899 const basegfx::B2DPolyPolygon& rPolyPolygon, 900 const SvgGradientEntryVector& rGradientEntries, 901 const basegfx::B2DPoint& rStart, 902 double fRadius, 903 SpreadMethod aSpreadMethod, 904 const basegfx::B2DPoint* pFocal, 905 double fOverlapping) 906 : BufferedDecompositionPrimitive2D(), 907 SvgGradientHelper(rPolyPolygon, rGradientEntries, rStart, aSpreadMethod, fOverlapping), 908 mfRadius(fRadius), 909 maFocal(rStart), 910 maFocalVector(0.0, 0.0), 911 maFocalLength(0.0), 912 maMirroredGradientEntries(), 913 mbFocalSet(false) 914 { 915 if(pFocal) 916 { 917 maFocal = *pFocal; 918 maFocalVector = maFocal - getStart(); 919 mbFocalSet = true; 920 } 921 } 922 923 bool SvgRadialGradientPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const 924 { 925 const SvgGradientHelper* pSvgGradientHelper = dynamic_cast< const SvgGradientHelper* >(&rPrimitive); 926 927 if(pSvgGradientHelper && SvgGradientHelper::operator==(*pSvgGradientHelper)) 928 { 929 const SvgRadialGradientPrimitive2D& rCompare = static_cast< const SvgRadialGradientPrimitive2D& >(rPrimitive); 930 931 if(getRadius() == rCompare.getRadius()) 932 { 933 if(isFocalSet() == rCompare.isFocalSet()) 934 { 935 if(isFocalSet()) 936 { 937 return getFocal() == rCompare.getFocal(); 938 } 939 else 940 { 941 return true; 942 } 943 } 944 } 945 } 946 947 return false; 948 } 949 950 basegfx::B2DRange SvgRadialGradientPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const 951 { 952 // return ObjectRange 953 return getPolyPolygon().getB2DRange(); 954 } 955 956 // provide unique ID 957 ImplPrimitrive2DIDBlock(SvgRadialGradientPrimitive2D, PRIMITIVE2D_ID_SVGRADIALGRADIENTPRIMITIVE2D) 958 959 } // end of namespace primitive2d 960 } // end of namespace drawinglayer 961 962 ////////////////////////////////////////////////////////////////////////////// 963 // SvgLinearAtomPrimitive2D class 964 965 namespace drawinglayer 966 { 967 namespace primitive2d 968 { 969 Primitive2DSequence SvgLinearAtomPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const 970 { 971 Primitive2DSequence xRetval; 972 const double fDelta(getOffsetB() - getOffsetA()); 973 974 if(!basegfx::fTools::equalZero(fDelta)) 975 { 976 if(getColorA() == getColorB()) 977 { 978 const basegfx::B2DPolygon aPolygon( 979 basegfx::tools::createPolygonFromRect( 980 basegfx::B2DRange( 981 getOffsetA() - getOverlapping(), 982 0.0, 983 getOffsetB() + getOverlapping(), 984 1.0))); 985 986 xRetval.realloc(1); 987 xRetval[0] = new PolyPolygonColorPrimitive2D( 988 basegfx::B2DPolyPolygon(aPolygon), 989 getColorA()); 990 } 991 else 992 { 993 // calc discrete length to change color all 2.5 pixels 994 sal_uInt32 nSteps(basegfx::fround(fDelta / (getDiscreteUnit() * 2.5))); 995 996 // use color distance, assume to do every 3rd 997 const double fColorDistance(getColorA().getDistance(getColorB())); 998 const sal_uInt32 nColorSteps(basegfx::fround(fColorDistance * (255.0 * 0.3))); 999 nSteps = std::min(nSteps, nColorSteps); 1000 1001 // roughly cut when too big 1002 nSteps = std::min(nSteps, sal_uInt32(100)); 1003 nSteps = std::max(nSteps, sal_uInt32(1)); 1004 1005 // preapare iteration 1006 double fStart(0.0); 1007 double fStep(fDelta / nSteps); 1008 1009 xRetval.realloc(nSteps); 1010 1011 for(sal_uInt32 a(0); a < nSteps; a++, fStart += fStep) 1012 { 1013 const double fLeft(getOffsetA() + fStart); 1014 const double fRight(fLeft + fStep); 1015 const basegfx::B2DPolygon aPolygon( 1016 basegfx::tools::createPolygonFromRect( 1017 basegfx::B2DRange( 1018 fLeft - getOverlapping(), 1019 0.0, 1020 fRight + getOverlapping(), 1021 1.0))); 1022 1023 xRetval[a] = new PolyPolygonColorPrimitive2D( 1024 basegfx::B2DPolyPolygon(aPolygon), 1025 basegfx::interpolate(getColorA(), getColorB(), fStart/fDelta)); 1026 } 1027 } 1028 } 1029 1030 return xRetval; 1031 } 1032 1033 bool SvgLinearAtomPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const 1034 { 1035 if(DiscreteMetricDependentPrimitive2D::operator==(rPrimitive)) 1036 { 1037 const SvgLinearAtomPrimitive2D& rCompare = static_cast< const SvgLinearAtomPrimitive2D& >(rPrimitive); 1038 1039 return (getColorA() == rCompare.getColorA() 1040 && getColorB() == rCompare.getColorB() 1041 && getOffsetA() == rCompare.getOffsetA() 1042 && getOffsetB() == rCompare.getOffsetB() 1043 && getOverlapping() == rCompare.getOverlapping()); 1044 } 1045 1046 return false; 1047 } 1048 1049 // provide unique ID 1050 ImplPrimitrive2DIDBlock(SvgLinearAtomPrimitive2D, PRIMITIVE2D_ID_SVGLINEARATOMPRIMITIVE2D) 1051 1052 } // end of namespace primitive2d 1053 } // end of namespace drawinglayer 1054 1055 ////////////////////////////////////////////////////////////////////////////// 1056 // SvgRadialAtomPrimitive2D class 1057 1058 namespace drawinglayer 1059 { 1060 namespace primitive2d 1061 { 1062 Primitive2DSequence SvgRadialAtomPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const 1063 { 1064 Primitive2DSequence xRetval; 1065 const double fDeltaScale(getScaleB() - getScaleA()); 1066 1067 if(!basegfx::fTools::equalZero(fDeltaScale)) 1068 { 1069 if(getColorA() == getColorB()) 1070 { 1071 basegfx::B2DPolygon aPolygonA(basegfx::tools::createPolygonFromUnitCircle()); 1072 basegfx::B2DPolygon aPolygonB(basegfx::tools::createPolygonFromUnitCircle()); 1073 double fScaleA(getScaleA()); 1074 const double fScaleB(getScaleB()); 1075 1076 if(fScaleA > getOverlapping()) 1077 { 1078 fScaleA -= getOverlapping(); 1079 } 1080 1081 const bool bUseA(basegfx::fTools::equalZero(fScaleA)); 1082 1083 if(getTranslateSet()) 1084 { 1085 if(bUseA) 1086 { 1087 aPolygonA.transform( 1088 basegfx::tools::createScaleTranslateB2DHomMatrix( 1089 fScaleA, 1090 fScaleA, 1091 getTranslateA().getX(), 1092 getTranslateA().getY())); 1093 } 1094 1095 aPolygonB.transform( 1096 basegfx::tools::createScaleTranslateB2DHomMatrix( 1097 fScaleB, 1098 fScaleB, 1099 getTranslateB().getX(), 1100 getTranslateB().getY())); 1101 } 1102 else 1103 { 1104 if(bUseA) 1105 { 1106 aPolygonA.transform( 1107 basegfx::tools::createScaleB2DHomMatrix( 1108 fScaleA, 1109 fScaleA)); 1110 } 1111 1112 aPolygonB.transform( 1113 basegfx::tools::createScaleB2DHomMatrix( 1114 fScaleB, 1115 fScaleB)); 1116 } 1117 1118 basegfx::B2DPolyPolygon aPolyPolygon(aPolygonB); 1119 1120 if(bUseA) 1121 { 1122 aPolyPolygon.append(aPolygonA); 1123 } 1124 1125 xRetval.realloc(1); 1126 1127 xRetval[0] = new PolyPolygonColorPrimitive2D( 1128 aPolyPolygon, 1129 getColorA()); 1130 } 1131 else 1132 { 1133 // calc discrete length to change color all 2.5 pixels 1134 sal_uInt32 nSteps(basegfx::fround(fDeltaScale / (getDiscreteUnit() * 2.5))); 1135 1136 // use color distance, assume to do every 3rd 1137 const double fColorDistance(getColorA().getDistance(getColorB())); 1138 const sal_uInt32 nColorSteps(basegfx::fround(fColorDistance * (255.0 * 0.3))); 1139 nSteps = std::min(nSteps, nColorSteps); 1140 1141 // roughly cut when too big 1142 nSteps = std::min(nSteps, sal_uInt32(100)); 1143 nSteps = std::max(nSteps, sal_uInt32(1)); 1144 1145 // preapare iteration 1146 double fStartScale(0.0); 1147 double fStepScale(fDeltaScale / nSteps); 1148 1149 xRetval.realloc(nSteps); 1150 1151 for(sal_uInt32 a(0); a < nSteps; a++, fStartScale += fStepScale) 1152 { 1153 double fScaleA(getScaleA() + fStartScale); 1154 const double fScaleB(fScaleA + fStepScale); 1155 const double fUnitScale(fStartScale/fDeltaScale); 1156 basegfx::B2DPolygon aPolygonA(basegfx::tools::createPolygonFromUnitCircle()); 1157 basegfx::B2DPolygon aPolygonB(basegfx::tools::createPolygonFromUnitCircle()); 1158 1159 if(fScaleA > getOverlapping()) 1160 { 1161 fScaleA -= getOverlapping(); 1162 } 1163 1164 const bool bUseA(basegfx::fTools::equalZero(fScaleA)); 1165 1166 if(getTranslateSet()) 1167 { 1168 const double fUnitScaleEnd((fStartScale + fStepScale)/fDeltaScale); 1169 const basegfx::B2DVector aTranslateB(basegfx::interpolate(getTranslateA(), getTranslateB(), fUnitScaleEnd)); 1170 1171 if(bUseA) 1172 { 1173 const basegfx::B2DVector aTranslateA(basegfx::interpolate(getTranslateA(), getTranslateB(), fUnitScale)); 1174 1175 aPolygonA.transform( 1176 basegfx::tools::createScaleTranslateB2DHomMatrix( 1177 fScaleA, 1178 fScaleA, 1179 aTranslateA.getX(), 1180 aTranslateA.getY())); 1181 } 1182 1183 aPolygonB.transform( 1184 basegfx::tools::createScaleTranslateB2DHomMatrix( 1185 fScaleB, 1186 fScaleB, 1187 aTranslateB.getX(), 1188 aTranslateB.getY())); 1189 } 1190 else 1191 { 1192 if(bUseA) 1193 { 1194 aPolygonA.transform( 1195 basegfx::tools::createScaleB2DHomMatrix( 1196 fScaleA, 1197 fScaleA)); 1198 } 1199 1200 aPolygonB.transform( 1201 basegfx::tools::createScaleB2DHomMatrix( 1202 fScaleB, 1203 fScaleB)); 1204 } 1205 1206 basegfx::B2DPolyPolygon aPolyPolygon(aPolygonB); 1207 1208 if(bUseA) 1209 { 1210 aPolyPolygon.append(aPolygonA); 1211 } 1212 1213 xRetval[nSteps - 1 - a] = new PolyPolygonColorPrimitive2D( 1214 aPolyPolygon, 1215 basegfx::interpolate(getColorA(), getColorB(), fUnitScale)); 1216 } 1217 } 1218 } 1219 1220 return xRetval; 1221 } 1222 1223 bool SvgRadialAtomPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const 1224 { 1225 if(DiscreteMetricDependentPrimitive2D::operator==(rPrimitive)) 1226 { 1227 const SvgRadialAtomPrimitive2D& rCompare = static_cast< const SvgRadialAtomPrimitive2D& >(rPrimitive); 1228 1229 return (getColorA() == rCompare.getColorA() 1230 && getColorB() == rCompare.getColorB() 1231 && getScaleA() == rCompare.getScaleA() 1232 && getScaleB() == rCompare.getScaleB() 1233 && getTranslateA() == rCompare.getTranslateA() 1234 && getTranslateB() == rCompare.getTranslateB() 1235 && getOverlapping() == rCompare.getOverlapping()); 1236 } 1237 1238 return false; 1239 } 1240 1241 // provide unique ID 1242 ImplPrimitrive2DIDBlock(SvgRadialAtomPrimitive2D, PRIMITIVE2D_ID_SVGRADIALATOMPRIMITIVE2D) 1243 1244 } // end of namespace primitive2d 1245 } // end of namespace drawinglayer 1246 1247 ////////////////////////////////////////////////////////////////////////////// 1248 // eof 1249