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