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