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 23 24 // MARKER(update_precomp.py): autogen include statement, do not remove 25 #include "precompiled_basegfx.hxx" 26 27 #include <basegfx/tools/gradienttools.hxx> 28 #include <basegfx/point/b2dpoint.hxx> 29 #include <basegfx/range/b2drange.hxx> 30 #include <basegfx/matrix/b2dhommatrixtools.hxx> 31 32 namespace basegfx 33 { operator ==(const ODFGradientInfo & rODFGradientInfo) const34 bool ODFGradientInfo::operator==(const ODFGradientInfo& rODFGradientInfo) const 35 { 36 return getTextureTransform() == rODFGradientInfo.getTextureTransform() 37 && getAspectRatio() == rODFGradientInfo.getAspectRatio() 38 && getSteps() == rODFGradientInfo.getSteps(); 39 } 40 getBackTextureTransform() const41 const B2DHomMatrix& ODFGradientInfo::getBackTextureTransform() const 42 { 43 if(maBackTextureTransform.isIdentity()) 44 { 45 const_cast< ODFGradientInfo* >(this)->maBackTextureTransform = getTextureTransform(); 46 const_cast< ODFGradientInfo* >(this)->maBackTextureTransform.invert(); 47 } 48 49 return maBackTextureTransform; 50 } 51 52 /** Most of the setup for linear & axial gradient is the same, except 53 for the border treatment. Factored out here. 54 */ init1DGradientInfo(const B2DRange & rTargetRange,sal_uInt32 nSteps,double fBorder,double fAngle,bool bAxial)55 ODFGradientInfo init1DGradientInfo( 56 const B2DRange& rTargetRange, 57 sal_uInt32 nSteps, 58 double fBorder, 59 double fAngle, 60 bool bAxial) 61 { 62 B2DHomMatrix aTextureTransform; 63 64 fAngle = -fAngle; 65 66 double fTargetSizeX(rTargetRange.getWidth()); 67 double fTargetSizeY(rTargetRange.getHeight()); 68 double fTargetOffsetX(rTargetRange.getMinX()); 69 double fTargetOffsetY(rTargetRange.getMinY()); 70 71 // add object expansion 72 const bool bAngleUsed(!fTools::equalZero(fAngle)); 73 74 if(bAngleUsed) 75 { 76 const double fAbsCos(fabs(cos(fAngle))); 77 const double fAbsSin(fabs(sin(fAngle))); 78 const double fNewX(fTargetSizeX * fAbsCos + fTargetSizeY * fAbsSin); 79 const double fNewY(fTargetSizeY * fAbsCos + fTargetSizeX * fAbsSin); 80 81 fTargetOffsetX -= (fNewX - fTargetSizeX) / 2.0; 82 fTargetOffsetY -= (fNewY - fTargetSizeY) / 2.0; 83 fTargetSizeX = fNewX; 84 fTargetSizeY = fNewY; 85 } 86 87 const double fSizeWithoutBorder(1.0 - fBorder); 88 89 if(bAxial) 90 { 91 aTextureTransform.scale(1.0, fSizeWithoutBorder * 0.5); 92 aTextureTransform.translate(0.0, 0.5); 93 } 94 else 95 { 96 if(!fTools::equal(fSizeWithoutBorder, 1.0)) 97 { 98 aTextureTransform.scale(1.0, fSizeWithoutBorder); 99 aTextureTransform.translate(0.0, fBorder); 100 } 101 } 102 103 aTextureTransform.scale(fTargetSizeX, fTargetSizeY); 104 105 // add texture rotate after scale to keep perpendicular angles 106 if(bAngleUsed) 107 { 108 const B2DPoint aCenter(0.5 * fTargetSizeX, 0.5 * fTargetSizeY); 109 110 aTextureTransform *= basegfx::tools::createRotateAroundPoint(aCenter, fAngle); 111 } 112 113 // add object translate 114 aTextureTransform.translate(fTargetOffsetX, fTargetOffsetY); 115 116 // prepare aspect for texture 117 const double fAspectRatio(fTools::equalZero(fTargetSizeY) ? 1.0 : fTargetSizeX / fTargetSizeY); 118 119 return ODFGradientInfo(aTextureTransform, fAspectRatio, nSteps); 120 } 121 122 /** Most of the setup for radial & ellipsoidal gradient is the same, 123 except for the border treatment. Factored out here. 124 */ initEllipticalGradientInfo(const B2DRange & rTargetRange,const B2DVector & rOffset,sal_uInt32 nSteps,double fBorder,double fAngle,bool bCircular)125 ODFGradientInfo initEllipticalGradientInfo( 126 const B2DRange& rTargetRange, 127 const B2DVector& rOffset, 128 sal_uInt32 nSteps, 129 double fBorder, 130 double fAngle, 131 bool bCircular) 132 { 133 B2DHomMatrix aTextureTransform; 134 135 fAngle = -fAngle; 136 137 double fTargetSizeX(rTargetRange.getWidth()); 138 double fTargetSizeY(rTargetRange.getHeight()); 139 double fTargetOffsetX(rTargetRange.getMinX()); 140 double fTargetOffsetY(rTargetRange.getMinY()); 141 142 // add object expansion 143 if(bCircular) 144 { 145 const double fOriginalDiag(sqrt((fTargetSizeX * fTargetSizeX) + (fTargetSizeY * fTargetSizeY))); 146 147 fTargetOffsetX -= (fOriginalDiag - fTargetSizeX) / 2.0; 148 fTargetOffsetY -= (fOriginalDiag - fTargetSizeY) / 2.0; 149 fTargetSizeX = fOriginalDiag; 150 fTargetSizeY = fOriginalDiag; 151 } 152 else 153 { 154 fTargetOffsetX -= (0.4142 / 2.0 ) * fTargetSizeX; 155 fTargetOffsetY -= (0.4142 / 2.0 ) * fTargetSizeY; 156 fTargetSizeX = 1.4142 * fTargetSizeX; 157 fTargetSizeY = 1.4142 * fTargetSizeY; 158 } 159 160 const double fHalfBorder((1.0 - fBorder) * 0.5); 161 162 aTextureTransform.scale(fHalfBorder, fHalfBorder); 163 aTextureTransform.translate(0.5, 0.5); 164 aTextureTransform.scale(fTargetSizeX, fTargetSizeY); 165 166 // add texture rotate after scale to keep perpendicular angles 167 if(!bCircular && !fTools::equalZero(fAngle)) 168 { 169 const B2DPoint aCenter(0.5 * fTargetSizeX, 0.5 * fTargetSizeY); 170 171 aTextureTransform *= basegfx::tools::createRotateAroundPoint(aCenter, fAngle); 172 } 173 174 // add defined offsets after rotation 175 if(!fTools::equal(0.5, rOffset.getX()) || !fTools::equal(0.5, rOffset.getY())) 176 { 177 // use original target size 178 fTargetOffsetX += (rOffset.getX() - 0.5) * rTargetRange.getWidth(); 179 fTargetOffsetY += (rOffset.getY() - 0.5) * rTargetRange.getHeight(); 180 } 181 182 // add object translate 183 aTextureTransform.translate(fTargetOffsetX, fTargetOffsetY); 184 185 // prepare aspect for texture 186 const double fAspectRatio((0.0 != fTargetSizeY) ? fTargetSizeX / fTargetSizeY : 1.0); 187 188 return ODFGradientInfo(aTextureTransform, fAspectRatio, nSteps); 189 } 190 191 /** Setup for rect & square gradient is exactly the same. Factored out 192 here. 193 */ initRectGradientInfo(const B2DRange & rTargetRange,const B2DVector & rOffset,sal_uInt32 nSteps,double fBorder,double fAngle,bool bSquare)194 ODFGradientInfo initRectGradientInfo( 195 const B2DRange& rTargetRange, 196 const B2DVector& rOffset, 197 sal_uInt32 nSteps, 198 double fBorder, 199 double fAngle, 200 bool bSquare) 201 { 202 B2DHomMatrix aTextureTransform; 203 204 fAngle = -fAngle; 205 206 double fTargetSizeX(rTargetRange.getWidth()); 207 double fTargetSizeY(rTargetRange.getHeight()); 208 double fTargetOffsetX(rTargetRange.getMinX()); 209 double fTargetOffsetY(rTargetRange.getMinY()); 210 211 // add object expansion 212 if(bSquare) 213 { 214 const double fSquareWidth(std::max(fTargetSizeX, fTargetSizeY)); 215 216 fTargetOffsetX -= (fSquareWidth - fTargetSizeX) / 2.0; 217 fTargetOffsetY -= (fSquareWidth - fTargetSizeY) / 2.0; 218 fTargetSizeX = fTargetSizeY = fSquareWidth; 219 } 220 221 // add object expansion 222 const bool bAngleUsed(!fTools::equalZero(fAngle)); 223 224 if(bAngleUsed) 225 { 226 const double fAbsCos(fabs(cos(fAngle))); 227 const double fAbsSin(fabs(sin(fAngle))); 228 const double fNewX(fTargetSizeX * fAbsCos + fTargetSizeY * fAbsSin); 229 const double fNewY(fTargetSizeY * fAbsCos + fTargetSizeX * fAbsSin); 230 231 fTargetOffsetX -= (fNewX - fTargetSizeX) / 2.0; 232 fTargetOffsetY -= (fNewY - fTargetSizeY) / 2.0; 233 fTargetSizeX = fNewX; 234 fTargetSizeY = fNewY; 235 } 236 237 const double fHalfBorder((1.0 - fBorder) * 0.5); 238 239 aTextureTransform.scale(fHalfBorder, fHalfBorder); 240 aTextureTransform.translate(0.5, 0.5); 241 aTextureTransform.scale(fTargetSizeX, fTargetSizeY); 242 243 // add texture rotate after scale to keep perpendicular angles 244 if(bAngleUsed) 245 { 246 const B2DPoint aCenter(0.5 * fTargetSizeX, 0.5 * fTargetSizeY); 247 248 aTextureTransform *= basegfx::tools::createRotateAroundPoint(aCenter, fAngle); 249 } 250 251 // add defined offsets after rotation 252 if(!fTools::equal(0.5, rOffset.getX()) || !fTools::equal(0.5, rOffset.getY())) 253 { 254 // use scaled target size 255 fTargetOffsetX += (rOffset.getX() - 0.5) * fTargetSizeX; 256 fTargetOffsetY += (rOffset.getY() - 0.5) * fTargetSizeY; 257 } 258 259 // add object translate 260 aTextureTransform.translate(fTargetOffsetX, fTargetOffsetY); 261 262 // prepare aspect for texture 263 const double fAspectRatio((0.0 != fTargetSizeY) ? fTargetSizeX / fTargetSizeY : 1.0); 264 265 return ODFGradientInfo(aTextureTransform, fAspectRatio, nSteps); 266 } 267 268 namespace tools 269 { createLinearODFGradientInfo(const B2DRange & rTargetArea,sal_uInt32 nSteps,double fBorder,double fAngle)270 ODFGradientInfo createLinearODFGradientInfo( 271 const B2DRange& rTargetArea, 272 sal_uInt32 nSteps, 273 double fBorder, 274 double fAngle) 275 { 276 return init1DGradientInfo( 277 rTargetArea, 278 nSteps, 279 fBorder, 280 fAngle, 281 false); 282 } 283 createAxialODFGradientInfo(const B2DRange & rTargetArea,sal_uInt32 nSteps,double fBorder,double fAngle)284 ODFGradientInfo createAxialODFGradientInfo( 285 const B2DRange& rTargetArea, 286 sal_uInt32 nSteps, 287 double fBorder, 288 double fAngle) 289 { 290 return init1DGradientInfo( 291 rTargetArea, 292 nSteps, 293 fBorder, 294 fAngle, 295 true); 296 } 297 createRadialODFGradientInfo(const B2DRange & rTargetArea,const B2DVector & rOffset,sal_uInt32 nSteps,double fBorder)298 ODFGradientInfo createRadialODFGradientInfo( 299 const B2DRange& rTargetArea, 300 const B2DVector& rOffset, 301 sal_uInt32 nSteps, 302 double fBorder) 303 { 304 return initEllipticalGradientInfo( 305 rTargetArea, 306 rOffset, 307 nSteps, 308 fBorder, 309 0.0, 310 true); 311 } 312 createEllipticalODFGradientInfo(const B2DRange & rTargetArea,const B2DVector & rOffset,sal_uInt32 nSteps,double fBorder,double fAngle)313 ODFGradientInfo createEllipticalODFGradientInfo( 314 const B2DRange& rTargetArea, 315 const B2DVector& rOffset, 316 sal_uInt32 nSteps, 317 double fBorder, 318 double fAngle) 319 { 320 return initEllipticalGradientInfo( 321 rTargetArea, 322 rOffset, 323 nSteps, 324 fBorder, 325 fAngle, 326 false); 327 } 328 createSquareODFGradientInfo(const B2DRange & rTargetArea,const B2DVector & rOffset,sal_uInt32 nSteps,double fBorder,double fAngle)329 ODFGradientInfo createSquareODFGradientInfo( 330 const B2DRange& rTargetArea, 331 const B2DVector& rOffset, 332 sal_uInt32 nSteps, 333 double fBorder, 334 double fAngle) 335 { 336 return initRectGradientInfo( 337 rTargetArea, 338 rOffset, 339 nSteps, 340 fBorder, 341 fAngle, 342 true); 343 } 344 createRectangularODFGradientInfo(const B2DRange & rTargetArea,const B2DVector & rOffset,sal_uInt32 nSteps,double fBorder,double fAngle)345 ODFGradientInfo createRectangularODFGradientInfo( 346 const B2DRange& rTargetArea, 347 const B2DVector& rOffset, 348 sal_uInt32 nSteps, 349 double fBorder, 350 double fAngle) 351 { 352 return initRectGradientInfo( 353 rTargetArea, 354 rOffset, 355 nSteps, 356 fBorder, 357 fAngle, 358 false); 359 } 360 getLinearGradientAlpha(const B2DPoint & rUV,const ODFGradientInfo & rGradInfo)361 double getLinearGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo) 362 { 363 const B2DPoint aCoor(rGradInfo.getBackTextureTransform() * rUV); 364 365 // Ignore Y, this is not needed at all for Y-Oriented gradients 366 // if(aCoor.getX() < 0.0 || aCoor.getX() > 1.0) 367 // { 368 // return 0.0; 369 // } 370 371 if(aCoor.getY() <= 0.0) 372 { 373 return 0.0; // start value for inside 374 } 375 376 if(aCoor.getY() >= 1.0) 377 { 378 return 1.0; // end value for outside 379 } 380 381 const sal_uInt32 nSteps(rGradInfo.getSteps()); 382 383 if(nSteps) 384 { 385 return floor(aCoor.getY() * nSteps) / double(nSteps - 1); 386 } 387 388 return aCoor.getY(); 389 } 390 getAxialGradientAlpha(const B2DPoint & rUV,const ODFGradientInfo & rGradInfo)391 double getAxialGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo) 392 { 393 const B2DPoint aCoor(rGradInfo.getBackTextureTransform() * rUV); 394 395 // Ignore Y, this is not needed at all for Y-Oriented gradients 396 //if(aCoor.getX() < 0.0 || aCoor.getX() > 1.0) 397 //{ 398 // return 0.0; 399 //} 400 401 const double fAbsY(fabs(aCoor.getY())); 402 403 if(fAbsY >= 1.0) 404 { 405 return 1.0; // use end value when outside in Y 406 } 407 408 const sal_uInt32 nSteps(rGradInfo.getSteps()); 409 410 if(nSteps) 411 { 412 return floor(fAbsY * nSteps) / double(nSteps - 1); 413 } 414 415 return fAbsY; 416 } 417 getRadialGradientAlpha(const B2DPoint & rUV,const ODFGradientInfo & rGradInfo)418 double getRadialGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo) 419 { 420 const B2DPoint aCoor(rGradInfo.getBackTextureTransform() * rUV); 421 422 if(aCoor.getX() < -1.0 || aCoor.getX() > 1.0 || aCoor.getY() < -1.0 || aCoor.getY() > 1.0) 423 { 424 return 0.0; 425 } 426 427 const double t(1.0 - sqrt(aCoor.getX() * aCoor.getX() + aCoor.getY() * aCoor.getY())); 428 const sal_uInt32 nSteps(rGradInfo.getSteps()); 429 430 if(nSteps && t < 1.0) 431 { 432 return floor(t * nSteps) / double(nSteps - 1); 433 } 434 435 return t; 436 } 437 getEllipticalGradientAlpha(const B2DPoint & rUV,const ODFGradientInfo & rGradInfo)438 double getEllipticalGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo) 439 { 440 return getRadialGradientAlpha(rUV, rGradInfo); // only matrix setup differs 441 } 442 getSquareGradientAlpha(const B2DPoint & rUV,const ODFGradientInfo & rGradInfo)443 double getSquareGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo) 444 { 445 const B2DPoint aCoor(rGradInfo.getBackTextureTransform() * rUV); 446 const double fAbsX(fabs(aCoor.getX())); 447 448 if(fAbsX >= 1.0) 449 { 450 return 0.0; 451 } 452 453 const double fAbsY(fabs(aCoor.getY())); 454 455 if(fAbsY >= 1.0) 456 { 457 return 0.0; 458 } 459 460 const double t(1.0 - std::max(fAbsX, fAbsY)); 461 const sal_uInt32 nSteps(rGradInfo.getSteps()); 462 463 if(nSteps && t < 1.0) 464 { 465 return floor(t * nSteps) / double(nSteps - 1); 466 } 467 468 return t; 469 } 470 getRectangularGradientAlpha(const B2DPoint & rUV,const ODFGradientInfo & rGradInfo)471 double getRectangularGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo) 472 { 473 return getSquareGradientAlpha(rUV, rGradInfo); // only matrix setup differs 474 } 475 } // namespace tools 476 } // namespace basegfx 477 478 // eof 479