1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 /* 3 * This file is part of the LibreOffice project. 4 * 5 * This Source Code Form is subject to the terms of the Mozilla Public 6 * License, v. 2.0. If a copy of the MPL was not distributed with this 7 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 * 9 * This file incorporates work covered by the following license notice: 10 * 11 * Licensed to the Apache Software Foundation (ASF) under one or more 12 * contributor license agreements. See the NOTICE file distributed 13 * with this work for additional information regarding copyright 14 * ownership. The ASF licenses this file to you under the Apache 15 * License, Version 2.0 (the "License"); you may not use this file 16 * except in compliance with the License. You may obtain a copy of 17 * the License at http://www.apache.org/licenses/LICENSE-2.0 . 18 */ 19 20 21 #include <tools/diagnose_ex.h> 22 #include <canvas/canvastools.hxx> 23 24 #include <math.h> 25 26 #include <com/sun/star/beans/NamedValue.hpp> 27 #include <com/sun/star/awt/Rectangle.hpp> 28 #include <com/sun/star/animations/ValuePair.hpp> 29 #include <com/sun/star/drawing/FillStyle.hpp> 30 #include <com/sun/star/drawing/LineStyle.hpp> 31 #include <com/sun/star/awt/FontSlant.hpp> 32 33 #include <basegfx/polygon/b2dpolygon.hxx> 34 #include <basegfx/polygon/b2dpolygontools.hxx> 35 #include <basegfx/range/b2drange.hxx> 36 #include <basegfx/vector/b2dvector.hxx> 37 #include <basegfx/vector/b2ivector.hxx> 38 #include <basegfx/matrix/b2dhommatrix.hxx> 39 #include <basegfx/numeric/ftools.hxx> 40 #include <basegfx/utils/lerp.hxx> 41 #include <basegfx/matrix/b2dhommatrixtools.hxx> 42 43 #include <cppcanvas/basegfxfactory.hxx> 44 45 #include <unoview.hxx> 46 #include <slideshowexceptions.hxx> 47 #include <smilfunctionparser.hxx> 48 #include <tools.hxx> 49 50 #include <limits> 51 52 53 using namespace ::com::sun::star; 54 55 namespace slideshow::internal 56 { 57 namespace 58 { 59 class NamedValueComparator 60 { 61 public: NamedValueComparator(const beans::NamedValue & rKey)62 explicit NamedValueComparator( const beans::NamedValue& rKey ) : 63 mrKey( rKey ) 64 { 65 } 66 operator ()(const beans::NamedValue & rValue) const67 bool operator()( const beans::NamedValue& rValue ) const 68 { 69 return rValue.Name == mrKey.Name && rValue.Value == mrKey.Value; 70 } 71 72 private: 73 const beans::NamedValue& mrKey; 74 }; 75 getAttributedShapeTransformation(const::basegfx::B2DRectangle & rShapeBounds,const ShapeAttributeLayerSharedPtr & pAttr)76 ::basegfx::B2DHomMatrix getAttributedShapeTransformation( const ::basegfx::B2DRectangle& rShapeBounds, 77 const ShapeAttributeLayerSharedPtr& pAttr ) 78 { 79 ::basegfx::B2DHomMatrix aTransform; 80 const ::basegfx::B2DSize& rSize( rShapeBounds.getRange() ); 81 82 const double nShearX( pAttr->isShearXAngleValid() ? 83 pAttr->getShearXAngle() : 84 0.0 ); 85 const double nShearY( pAttr->isShearYAngleValid() ? 86 pAttr->getShearYAngle() : 87 0.0 ); 88 const double nRotation( pAttr->isRotationAngleValid() ? 89 basegfx::deg2rad(pAttr->getRotationAngle()) : 90 0.0 ); 91 92 // scale, shear and rotation pivot point is the shape 93 // center - adapt origin accordingly 94 aTransform.translate( -0.5, -0.5 ); 95 96 // ensure valid size (zero size will inevitably lead 97 // to a singular transformation matrix) 98 aTransform.scale( ::basegfx::pruneScaleValue( 99 rSize.getX() ), 100 ::basegfx::pruneScaleValue( 101 rSize.getY() ) ); 102 103 const bool bNeedShearX( !::basegfx::fTools::equalZero(nShearX) ); 104 const bool bNeedShearY( !::basegfx::fTools::equalZero(nShearY) ); 105 const bool bNeedRotation( !::basegfx::fTools::equalZero(nRotation) ); 106 107 if( bNeedRotation || bNeedShearX || bNeedShearY ) 108 { 109 if( bNeedShearX ) 110 aTransform.shearX( nShearX ); 111 112 if( bNeedShearY ) 113 aTransform.shearY( nShearY ); 114 115 if( bNeedRotation ) 116 aTransform.rotate( nRotation ); 117 } 118 119 // move left, top corner back to position of the 120 // shape. Since we've already translated the 121 // center of the shape to the origin (the 122 // translate( -0.5, -0.5 ) above), translate to 123 // center of final shape position here. 124 aTransform.translate( rShapeBounds.getCenterX(), 125 rShapeBounds.getCenterY() ); 126 127 return aTransform; 128 } 129 } 130 131 // Value extraction from Any 132 // ========================= 133 134 /// extract unary double value from Any extractValue(double & o_rValue,const uno::Any & rSourceAny,const ShapeSharedPtr & rShape,const::basegfx::B2DVector & rSlideBounds)135 bool extractValue( double& o_rValue, 136 const uno::Any& rSourceAny, 137 const ShapeSharedPtr& rShape, 138 const ::basegfx::B2DVector& rSlideBounds ) 139 { 140 // try to extract numeric value (double, or smaller POD, like float or int) 141 if( rSourceAny >>= o_rValue) 142 { 143 // succeeded 144 return true; 145 } 146 147 // try to extract string 148 OUString aString; 149 if( !(rSourceAny >>= aString) ) 150 return false; // nothing left to try 151 152 // parse the string into an ExpressionNode 153 try 154 { 155 // Parse string into ExpressionNode, eval node at time 0.0 156 o_rValue = (*SmilFunctionParser::parseSmilValue( 157 aString, 158 calcRelativeShapeBounds(rSlideBounds, 159 rShape->getBounds()) ))(0.0); 160 } 161 catch( ParseError& ) 162 { 163 return false; 164 } 165 166 return true; 167 } 168 169 /// extract enum/constant group value from Any extractValue(sal_Int32 & o_rValue,const uno::Any & rSourceAny,const ShapeSharedPtr &,const::basegfx::B2DVector &)170 bool extractValue( sal_Int32& o_rValue, 171 const uno::Any& rSourceAny, 172 const ShapeSharedPtr& /*rShape*/, 173 const ::basegfx::B2DVector& /*rSlideBounds*/ ) 174 { 175 // try to extract numeric value (int, or smaller POD, like byte) 176 if( rSourceAny >>= o_rValue) 177 { 178 // succeeded 179 return true; 180 } 181 182 // okay, no plain int. Maybe one of the domain-specific enums? 183 drawing::FillStyle eFillStyle; 184 if( rSourceAny >>= eFillStyle ) 185 { 186 o_rValue = sal::static_int_cast<sal_Int16>(eFillStyle); 187 188 // succeeded 189 return true; 190 } 191 192 drawing::LineStyle eLineStyle; 193 if( rSourceAny >>= eLineStyle ) 194 { 195 o_rValue = sal::static_int_cast<sal_Int16>(eLineStyle); 196 197 // succeeded 198 return true; 199 } 200 201 awt::FontSlant eFontSlant; 202 if( rSourceAny >>= eFontSlant ) 203 { 204 o_rValue = sal::static_int_cast<sal_Int16>(eFontSlant); 205 206 // succeeded 207 return true; 208 } 209 210 // nothing left to try. Failure 211 return false; 212 } 213 214 /// extract enum/constant group value from Any extractValue(sal_Int16 & o_rValue,const uno::Any & rSourceAny,const ShapeSharedPtr & rShape,const::basegfx::B2DVector & rSlideBounds)215 bool extractValue( sal_Int16& o_rValue, 216 const uno::Any& rSourceAny, 217 const ShapeSharedPtr& rShape, 218 const ::basegfx::B2DVector& rSlideBounds ) 219 { 220 sal_Int32 aValue; 221 if( !extractValue(aValue,rSourceAny,rShape,rSlideBounds) ) 222 return false; 223 224 if( std::numeric_limits<sal_Int16>::max() < aValue || 225 std::numeric_limits<sal_Int16>::min() > aValue ) 226 { 227 return false; 228 } 229 230 o_rValue = static_cast<sal_Int16>(aValue); 231 232 return true; 233 } 234 235 /// extract color value from Any extractValue(RGBColor & o_rValue,const uno::Any & rSourceAny,const ShapeSharedPtr &,const::basegfx::B2DVector &)236 bool extractValue( RGBColor& o_rValue, 237 const uno::Any& rSourceAny, 238 const ShapeSharedPtr& /*rShape*/, 239 const ::basegfx::B2DVector& /*rSlideBounds*/ ) 240 { 241 // try to extract numeric value (double, or smaller POD, like float or int) 242 { 243 double nTmp = 0; 244 if( rSourceAny >>= nTmp ) 245 { 246 sal_uInt32 aIntColor( static_cast< sal_uInt32 >(nTmp) ); 247 248 // TODO(F2): Handle color values correctly, here 249 o_rValue = unoColor2RGBColor( aIntColor ); 250 251 // succeeded 252 return true; 253 } 254 } 255 256 // try double sequence 257 { 258 uno::Sequence< double > aTmp; 259 if( rSourceAny >>= aTmp ) 260 { 261 ENSURE_OR_THROW( aTmp.getLength() == 3, 262 "extractValue(): inappropriate length for RGB color value" ); 263 264 o_rValue = RGBColor( aTmp[0], aTmp[1], aTmp[2] ); 265 266 // succeeded 267 return true; 268 } 269 } 270 271 // try sal_Int32 sequence 272 { 273 uno::Sequence< sal_Int32 > aTmp; 274 if( rSourceAny >>= aTmp ) 275 { 276 ENSURE_OR_THROW( aTmp.getLength() == 3, 277 "extractValue(): inappropriate length for RGB color value" ); 278 279 // truncate to byte 280 o_rValue = RGBColor( ::cppcanvas::makeColor( 281 static_cast<sal_uInt8>(aTmp[0]), 282 static_cast<sal_uInt8>(aTmp[1]), 283 static_cast<sal_uInt8>(aTmp[2]), 284 255 ) ); 285 286 // succeeded 287 return true; 288 } 289 } 290 291 // try sal_Int8 sequence 292 { 293 uno::Sequence< sal_Int8 > aTmp; 294 if( rSourceAny >>= aTmp ) 295 { 296 ENSURE_OR_THROW( aTmp.getLength() == 3, 297 "extractValue(): inappropriate length for RGB color value" ); 298 299 o_rValue = RGBColor( ::cppcanvas::makeColor( aTmp[0], aTmp[1], aTmp[2], 255 ) ); 300 301 // succeeded 302 return true; 303 } 304 } 305 306 // try to extract string 307 OUString aString; 308 if( !(rSourceAny >>= aString) ) 309 return false; // nothing left to try 310 311 // TODO(F2): Provide symbolic color values here 312 o_rValue = RGBColor( 0.5, 0.5, 0.5 ); 313 314 return true; 315 } 316 317 /// extract color value from Any extractValue(HSLColor & o_rValue,const uno::Any & rSourceAny,const ShapeSharedPtr &,const::basegfx::B2DVector &)318 bool extractValue( HSLColor& o_rValue, 319 const uno::Any& rSourceAny, 320 const ShapeSharedPtr& /*rShape*/, 321 const ::basegfx::B2DVector& /*rSlideBounds*/ ) 322 { 323 // try double sequence 324 { 325 uno::Sequence< double > aTmp; 326 if( rSourceAny >>= aTmp ) 327 { 328 ENSURE_OR_THROW( aTmp.getLength() == 3, 329 "extractValue(): inappropriate length for HSL color value" ); 330 331 o_rValue = HSLColor( aTmp[0], aTmp[1], aTmp[2] ); 332 333 // succeeded 334 return true; 335 } 336 } 337 338 // try sal_Int8 sequence 339 { 340 uno::Sequence< sal_Int8 > aTmp; 341 if( rSourceAny >>= aTmp ) 342 { 343 ENSURE_OR_THROW( aTmp.getLength() == 3, 344 "extractValue(): inappropriate length for HSL color value" ); 345 346 o_rValue = HSLColor( aTmp[0]*360.0/255.0, aTmp[1]/255.0, aTmp[2]/255.0 ); 347 348 // succeeded 349 return true; 350 } 351 } 352 353 return false; // nothing left to try 354 } 355 356 /// extract plain string from Any extractValue(OUString & o_rValue,const uno::Any & rSourceAny,const ShapeSharedPtr &,const::basegfx::B2DVector &)357 bool extractValue( OUString& o_rValue, 358 const uno::Any& rSourceAny, 359 const ShapeSharedPtr& /*rShape*/, 360 const ::basegfx::B2DVector& /*rSlideBounds*/ ) 361 { 362 // try to extract string 363 return rSourceAny >>= o_rValue; 364 } 365 366 /// extract bool value from Any extractValue(bool & o_rValue,const uno::Any & rSourceAny,const ShapeSharedPtr &,const::basegfx::B2DVector &)367 bool extractValue( bool& o_rValue, 368 const uno::Any& rSourceAny, 369 const ShapeSharedPtr& /*rShape*/, 370 const ::basegfx::B2DVector& /*rSlideBounds*/ ) 371 { 372 bool bTmp; 373 // try to extract bool value 374 if( rSourceAny >>= bTmp ) 375 { 376 o_rValue = bTmp; 377 378 // succeeded 379 return true; 380 } 381 382 // try to extract string 383 OUString aString; 384 if( !(rSourceAny >>= aString) ) 385 return false; // nothing left to try 386 387 // we also take the strings "true" and "false", 388 // as well as "on" and "off" here 389 if( aString.equalsIgnoreAsciiCase("true") || 390 aString.equalsIgnoreAsciiCase("on") ) 391 { 392 o_rValue = true; 393 return true; 394 } 395 if( aString.equalsIgnoreAsciiCase("false") || 396 aString.equalsIgnoreAsciiCase("off") ) 397 { 398 o_rValue = false; 399 return true; 400 } 401 402 // ultimately failed. 403 return false; 404 } 405 406 /// extract double 2-tuple from Any extractValue(::basegfx::B2DTuple & o_rPair,const uno::Any & rSourceAny,const ShapeSharedPtr & rShape,const::basegfx::B2DVector & rSlideBounds)407 bool extractValue( ::basegfx::B2DTuple& o_rPair, 408 const uno::Any& rSourceAny, 409 const ShapeSharedPtr& rShape, 410 const ::basegfx::B2DVector& rSlideBounds ) 411 { 412 animations::ValuePair aPair; 413 414 if( !(rSourceAny >>= aPair) ) 415 return false; 416 417 double nFirst; 418 if( !extractValue( nFirst, aPair.First, rShape, rSlideBounds ) ) 419 return false; 420 421 double nSecond; 422 if( !extractValue( nSecond, aPair.Second, rShape, rSlideBounds ) ) 423 return false; 424 425 o_rPair.setX( nFirst ); 426 o_rPair.setY( nSecond ); 427 428 return true; 429 } 430 findNamedValue(uno::Sequence<beans::NamedValue> const & rSequence,const beans::NamedValue & rSearchKey)431 bool findNamedValue( uno::Sequence< beans::NamedValue > const& rSequence, 432 const beans::NamedValue& rSearchKey ) 433 { 434 return ::std::any_of( rSequence.begin(), rSequence.end(), 435 NamedValueComparator( rSearchKey ) ); 436 } 437 calcRelativeShapeBounds(const basegfx::B2DVector & rPageSize,const basegfx::B2DRange & rShapeBounds)438 basegfx::B2DRange calcRelativeShapeBounds( const basegfx::B2DVector& rPageSize, 439 const basegfx::B2DRange& rShapeBounds ) 440 { 441 return basegfx::B2DRange( rShapeBounds.getMinX() / rPageSize.getX(), 442 rShapeBounds.getMinY() / rPageSize.getY(), 443 rShapeBounds.getMaxX() / rPageSize.getX(), 444 rShapeBounds.getMaxY() / rPageSize.getY() ); 445 } 446 447 // TODO(F2): Currently, the positional attributes DO NOT mirror the XShape properties. 448 // First and foremost, this is because we must operate with the shape boundrect, 449 // not position and size (the conversion between logic rect, snap rect and boundrect 450 // are non-trivial for draw shapes, and I won't duplicate them here). Thus, shapes 451 // rotated on the page will still have 0.0 rotation angle, as the metafile 452 // representation fetched over the API is our default zero case. 453 getShapeTransformation(const::basegfx::B2DRectangle & rShapeBounds,const ShapeAttributeLayerSharedPtr & pAttr)454 ::basegfx::B2DHomMatrix getShapeTransformation( const ::basegfx::B2DRectangle& rShapeBounds, 455 const ShapeAttributeLayerSharedPtr& pAttr ) 456 { 457 if( !pAttr ) 458 { 459 const basegfx::B2DHomMatrix aTransform(basegfx::utils::createScaleTranslateB2DHomMatrix( 460 rShapeBounds.getWidth(), rShapeBounds.getHeight(), 461 rShapeBounds.getMinX(), rShapeBounds.getMinY())); 462 463 return aTransform; 464 } 465 else 466 { 467 return getAttributedShapeTransformation( rShapeBounds, 468 pAttr ); 469 } 470 } 471 getSpriteTransformation(const::basegfx::B2DVector & rPixelSize,const::basegfx::B2DVector & rOrigSize,const ShapeAttributeLayerSharedPtr & pAttr)472 ::basegfx::B2DHomMatrix getSpriteTransformation( const ::basegfx::B2DVector& rPixelSize, 473 const ::basegfx::B2DVector& rOrigSize, 474 const ShapeAttributeLayerSharedPtr& pAttr ) 475 { 476 ::basegfx::B2DHomMatrix aTransform; 477 478 if( pAttr ) 479 { 480 const double nShearX( pAttr->isShearXAngleValid() ? 481 pAttr->getShearXAngle() : 482 0.0 ); 483 const double nShearY( pAttr->isShearYAngleValid() ? 484 pAttr->getShearYAngle() : 485 0.0 ); 486 const double nRotation( pAttr->isRotationAngleValid() ? 487 basegfx::deg2rad(pAttr->getRotationAngle()) : 488 0.0 ); 489 490 // scale, shear and rotation pivot point is the 491 // sprite's pixel center - adapt origin accordingly 492 aTransform.translate( -0.5*rPixelSize.getX(), 493 -0.5*rPixelSize.getY() ); 494 495 const ::basegfx::B2DSize aSize( 496 pAttr->isWidthValid() ? pAttr->getWidth() : rOrigSize.getX(), 497 pAttr->isHeightValid() ? pAttr->getHeight() : rOrigSize.getY() ); 498 499 // ensure valid size (zero size will inevitably lead 500 // to a singular transformation matrix). 501 aTransform.scale( ::basegfx::pruneScaleValue( 502 aSize.getX() / 503 ::basegfx::pruneScaleValue( 504 rOrigSize.getX() ) ), 505 ::basegfx::pruneScaleValue( 506 aSize.getY() / 507 ::basegfx::pruneScaleValue( 508 rOrigSize.getY() ) ) ); 509 510 const bool bNeedShearX( !::basegfx::fTools::equalZero(nShearX) ); 511 const bool bNeedShearY( !::basegfx::fTools::equalZero(nShearY) ); 512 const bool bNeedRotation( !::basegfx::fTools::equalZero(nRotation) ); 513 514 if( bNeedRotation || bNeedShearX || bNeedShearY ) 515 { 516 if( bNeedShearX ) 517 aTransform.shearX( nShearX ); 518 519 if( bNeedShearY ) 520 aTransform.shearY( nShearY ); 521 522 if( bNeedRotation ) 523 aTransform.rotate( nRotation ); 524 } 525 526 // move left, top corner back to original position of 527 // the sprite (we've translated the center of the 528 // sprite to the origin above). 529 aTransform.translate( 0.5*rPixelSize.getX(), 530 0.5*rPixelSize.getY() ); 531 } 532 533 // return identity transform for un-attributed 534 // shapes. This renders the sprite as-is, in its 535 // document-supplied size. 536 return aTransform; 537 } 538 getShapeUpdateArea(const::basegfx::B2DRectangle & rUnitBounds,const::basegfx::B2DHomMatrix & rShapeTransform,const ShapeAttributeLayerSharedPtr & pAttr)539 ::basegfx::B2DRectangle getShapeUpdateArea( const ::basegfx::B2DRectangle& rUnitBounds, 540 const ::basegfx::B2DHomMatrix& rShapeTransform, 541 const ShapeAttributeLayerSharedPtr& pAttr ) 542 { 543 ::basegfx::B2DHomMatrix aTransform; 544 545 if( pAttr && 546 pAttr->isCharScaleValid() && 547 fabs(pAttr->getCharScale()) > 1.0 ) 548 { 549 // enlarge shape bounds. Have to consider the worst 550 // case here (the text fully fills the shape) 551 552 const double nCharScale( pAttr->getCharScale() ); 553 554 // center of scaling is the middle of the shape 555 aTransform.translate( -0.5, -0.5 ); 556 aTransform.scale( nCharScale, nCharScale ); 557 aTransform.translate( 0.5, 0.5 ); 558 } 559 560 aTransform *= rShapeTransform; 561 562 ::basegfx::B2DRectangle aRes; 563 564 // apply shape transformation to unit rect 565 return ::canvas::tools::calcTransformedRectBounds( 566 aRes, 567 rUnitBounds, 568 aTransform ); 569 } 570 getShapeUpdateArea(const::basegfx::B2DRange & rUnitBounds,const::basegfx::B2DRange & rShapeBounds)571 ::basegfx::B2DRange getShapeUpdateArea( const ::basegfx::B2DRange& rUnitBounds, 572 const ::basegfx::B2DRange& rShapeBounds ) 573 { 574 return ::basegfx::B2DRectangle( 575 basegfx::utils::lerp( rShapeBounds.getMinX(), 576 rShapeBounds.getMaxX(), 577 rUnitBounds.getMinX() ), 578 basegfx::utils::lerp( rShapeBounds.getMinY(), 579 rShapeBounds.getMaxY(), 580 rUnitBounds.getMinY() ), 581 basegfx::utils::lerp( rShapeBounds.getMinX(), 582 rShapeBounds.getMaxX(), 583 rUnitBounds.getMaxX() ), 584 basegfx::utils::lerp( rShapeBounds.getMinY(), 585 rShapeBounds.getMaxY(), 586 rUnitBounds.getMaxY() ) ); 587 } 588 getShapePosSize(const::basegfx::B2DRectangle & rOrigBounds,const ShapeAttributeLayerSharedPtr & pAttr)589 ::basegfx::B2DRectangle getShapePosSize( const ::basegfx::B2DRectangle& rOrigBounds, 590 const ShapeAttributeLayerSharedPtr& pAttr ) 591 { 592 // an already empty shape bound need no further 593 // treatment. In fact, any changes applied below would 594 // actually remove the special empty state, thus, don't 595 // change! 596 if( !pAttr || 597 rOrigBounds.isEmpty() ) 598 { 599 return rOrigBounds; 600 } 601 else 602 { 603 // cannot use maBounds anymore, attributes might have been 604 // changed by now. 605 // Have to use absolute values here, as negative sizes 606 // (aka mirrored shapes) _still_ have the same bounds, 607 // only with mirrored content. 608 ::basegfx::B2DSize aSize; 609 aSize.setX( fabs( pAttr->isWidthValid() ? 610 pAttr->getWidth() : 611 rOrigBounds.getWidth() ) ); 612 aSize.setY( fabs( pAttr->isHeightValid() ? 613 pAttr->getHeight() : 614 rOrigBounds.getHeight() ) ); 615 616 ::basegfx::B2DPoint aPos; 617 aPos.setX( pAttr->isPosXValid() ? 618 pAttr->getPosX() : 619 rOrigBounds.getCenterX() ); 620 aPos.setY( pAttr->isPosYValid() ? 621 pAttr->getPosY() : 622 rOrigBounds.getCenterY() ); 623 624 // the positional attribute retrieved from the 625 // ShapeAttributeLayer actually denotes the _middle_ 626 // of the shape (do it as the PPTs do...) 627 return ::basegfx::B2DRectangle( aPos - 0.5*aSize, 628 aPos + 0.5*aSize ); 629 } 630 } 631 unoColor2RGBColor(sal_Int32 nColor)632 RGBColor unoColor2RGBColor( sal_Int32 nColor ) 633 { 634 return RGBColor( 635 ::cppcanvas::makeColor( 636 // convert from API color to IntSRGBA color 637 // (0xAARRGGBB -> 0xRRGGBBAA) 638 static_cast< sal_uInt8 >( nColor >> 16U ), 639 static_cast< sal_uInt8 >( nColor >> 8U ), 640 static_cast< sal_uInt8 >( nColor ), 641 static_cast< sal_uInt8 >( nColor >> 24U ) ) ); 642 } 643 RGBAColor2UnoColor(::cppcanvas::IntSRGBA aColor)644 sal_Int32 RGBAColor2UnoColor( ::cppcanvas::IntSRGBA aColor ) 645 { 646 return ::cppcanvas::makeColorARGB( 647 // convert from IntSRGBA color to API color 648 // (0xRRGGBBAA -> 0xAARRGGBB) 649 static_cast< sal_uInt8 >(0), 650 ::cppcanvas::getRed(aColor), 651 ::cppcanvas::getGreen(aColor), 652 ::cppcanvas::getBlue(aColor)); 653 } 654 fillRect(const::cppcanvas::CanvasSharedPtr & rCanvas,const::basegfx::B2DRectangle & rRect,::cppcanvas::IntSRGBA aFillColor)655 void fillRect( const ::cppcanvas::CanvasSharedPtr& rCanvas, 656 const ::basegfx::B2DRectangle& rRect, 657 ::cppcanvas::IntSRGBA aFillColor ) 658 { 659 const ::basegfx::B2DPolygon aPoly( 660 ::basegfx::utils::createPolygonFromRect( rRect )); 661 662 ::cppcanvas::PolyPolygonSharedPtr pPolyPoly( 663 ::cppcanvas::BaseGfxFactory::createPolyPolygon( rCanvas, aPoly ) ); 664 665 if( pPolyPoly ) 666 { 667 pPolyPoly->setRGBAFillColor( aFillColor ); 668 pPolyPoly->draw(); 669 } 670 } 671 initSlideBackground(const::cppcanvas::CanvasSharedPtr & rCanvas,const::basegfx::B2ISize & rSize)672 void initSlideBackground( const ::cppcanvas::CanvasSharedPtr& rCanvas, 673 const ::basegfx::B2ISize& rSize ) 674 { 675 ::cppcanvas::CanvasSharedPtr pCanvas( rCanvas->clone() ); 676 677 // set transformation to identity (->device pixel) 678 pCanvas->setTransformation( ::basegfx::B2DHomMatrix() ); 679 680 // #i42440# Fill the _full_ background in 681 // black. Since we had to extend the bitmap by one 682 // pixel, and the bitmap is initialized white, 683 // depending on the slide content a one pixel wide 684 // line will show to the bottom and the right. 685 fillRect( pCanvas, 686 ::basegfx::B2DRectangle( 0.0, 0.0, 687 rSize.getX(), 688 rSize.getY() ), 689 0x000000FFU ); 690 691 // fill the bounds rectangle in white. Subtract one pixel 692 // from both width and height, because the slide size is 693 // chosen one pixel larger than given by the drawing 694 // layer. This is because shapes with line style, that 695 // have the size of the slide would otherwise be cut 696 // off. OTOH, every other slide background (solid fill, 697 // gradient, bitmap) render one pixel less, thus revealing 698 // ugly white pixel to the right and the bottom. 699 fillRect( pCanvas, 700 ::basegfx::B2DRectangle( 0.0, 0.0, 701 rSize.getX()-1, 702 rSize.getY()-1 ), 703 0xFFFFFFFFU ); 704 } 705 getAPIShapeBounds(const uno::Reference<drawing::XShape> & xShape)706 ::basegfx::B2DRectangle getAPIShapeBounds( const uno::Reference< drawing::XShape >& xShape ) 707 { 708 uno::Reference< beans::XPropertySet > xPropSet( xShape, 709 uno::UNO_QUERY_THROW ); 710 // read bound rect 711 awt::Rectangle aTmpRect; 712 if( !(xPropSet->getPropertyValue("BoundRect") >>= aTmpRect) ) 713 { 714 ENSURE_OR_THROW( false, 715 "getAPIShapeBounds(): Could not get \"BoundRect\" property from shape" ); 716 } 717 718 return ::basegfx::B2DRectangle( aTmpRect.X, 719 aTmpRect.Y, 720 aTmpRect.X+aTmpRect.Width, 721 aTmpRect.Y+aTmpRect.Height ); 722 } 723 724 /* 725 TODO(F1): When ZOrder someday becomes usable enable this 726 727 double getAPIShapePrio( const uno::Reference< drawing::XShape >& xShape ) 728 { 729 uno::Reference< beans::XPropertySet > xPropSet( xShape, 730 uno::UNO_QUERY_THROW ); 731 // read prio 732 sal_Int32 nPrio(0); 733 if( !(xPropSet->getPropertyValue( 734 OUString("ZOrder") ) >>= nPrio) ) 735 { 736 ENSURE_OR_THROW( false, 737 "getAPIShapePrio(): Could not get \"ZOrder\" property from shape" ); 738 } 739 740 // TODO(F2): Check and adapt the range of possible values here. 741 // Maybe we can also take the total number of shapes here 742 return nPrio / 65535.0; 743 } 744 */ 745 getSlideSizePixel(const basegfx::B2DVector & rSlideSize,const UnoViewSharedPtr & pView)746 basegfx::B2IVector getSlideSizePixel( const basegfx::B2DVector& rSlideSize, 747 const UnoViewSharedPtr& pView ) 748 { 749 ENSURE_OR_THROW(pView, "getSlideSizePixel(): invalid view"); 750 751 // determine transformed page bounds 752 const basegfx::B2DRange aRect( 0,0, 753 rSlideSize.getX(), 754 rSlideSize.getY() ); 755 basegfx::B2DRange aTmpRect; 756 canvas::tools::calcTransformedRectBounds( aTmpRect, 757 aRect, 758 pView->getTransformation() ); 759 760 // #i42440# Returned slide size is one pixel too small, as 761 // rendering happens one pixel to the right and below the 762 // actual bound rect. 763 return basegfx::B2IVector( 764 basegfx::fround( aTmpRect.getRange().getX() ) + 1, 765 basegfx::fround( aTmpRect.getRange().getY() ) + 1 ); 766 } 767 } 768 769 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 770