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 { 34 bool ODFGradientInfo::operator==(const ODFGradientInfo& rODFGradientInfo) const 35 { 36 return getTextureTransform() == rODFGradientInfo.getTextureTransform() 37 && getAspectRatio() == rODFGradientInfo.getAspectRatio() 38 && getSteps() == rODFGradientInfo.getSteps(); 39 } 40 41 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 */ 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 */ 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 */ 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 { 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 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 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 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 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 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 361 double getLinearGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo) 362 { 363 const B2DPoint aCoor(rGradInfo.getBackTextureTransform() * rUV); 364 365 if(aCoor.getX() < 0.0 || aCoor.getX() > 1.0) 366 { 367 return 0.0; 368 } 369 370 if(aCoor.getY() <= 0.0) 371 { 372 return 0.0; 373 } 374 375 if(aCoor.getY() >= 1.0) 376 { 377 return 1.0; 378 } 379 380 const sal_uInt32 nSteps(rGradInfo.getSteps()); 381 382 if(nSteps) 383 { 384 return floor(aCoor.getY() * nSteps) / double(nSteps - 1); 385 } 386 387 return aCoor.getY(); 388 } 389 390 double getAxialGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo) 391 { 392 const B2DPoint aCoor(rGradInfo.getBackTextureTransform() * rUV); 393 394 if(aCoor.getX() < 0.0 || aCoor.getX() > 1.0) 395 { 396 return 0.0; 397 } 398 399 const double fAbsY(fabs(aCoor.getY())); 400 401 if(fAbsY >= 1.0) 402 { 403 return 0.0; 404 } 405 406 const sal_uInt32 nSteps(rGradInfo.getSteps()); 407 408 if(nSteps) 409 { 410 return floor(fAbsY * nSteps) / double(nSteps - 1); 411 } 412 413 return fAbsY; 414 } 415 416 double getRadialGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo) 417 { 418 const B2DPoint aCoor(rGradInfo.getBackTextureTransform() * rUV); 419 420 if(aCoor.getX() < -1.0 || aCoor.getX() > 1.0 || aCoor.getY() < -1.0 || aCoor.getY() > 1.0) 421 { 422 return 0.0; 423 } 424 425 const double t(1.0 - sqrt(aCoor.getX() * aCoor.getX() + aCoor.getY() * aCoor.getY())); 426 const sal_uInt32 nSteps(rGradInfo.getSteps()); 427 428 if(nSteps && t < 1.0) 429 { 430 return floor(t * nSteps) / double(nSteps - 1); 431 } 432 433 return t; 434 } 435 436 double getEllipticalGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo) 437 { 438 return getRadialGradientAlpha(rUV, rGradInfo); // only matrix setup differs 439 } 440 441 double getSquareGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo) 442 { 443 const B2DPoint aCoor(rGradInfo.getBackTextureTransform() * rUV); 444 const double fAbsX(fabs(aCoor.getX())); 445 446 if(fAbsX >= 1.0) 447 { 448 return 0.0; 449 } 450 451 const double fAbsY(fabs(aCoor.getY())); 452 453 if(fAbsY >= 1.0) 454 { 455 return 0.0; 456 } 457 458 const double t(1.0 - std::max(fAbsX, fAbsY)); 459 const sal_uInt32 nSteps(rGradInfo.getSteps()); 460 461 if(nSteps && t < 1.0) 462 { 463 return floor(t * nSteps) / double(nSteps - 1); 464 } 465 466 return t; 467 } 468 469 double getRectangularGradientAlpha(const B2DPoint& rUV, const ODFGradientInfo& rGradInfo) 470 { 471 return getSquareGradientAlpha(rUV, rGradInfo); // only matrix setup differs 472 } 473 } // namespace tools 474 } // namespace basegfx 475 476 // eof 477