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