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