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