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 : bool nTmp;
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 : return ::std::any_of( pArray,
443 : pArray + nLen,
444 0 : NamedValueComparator( rSearchKey ) );
445 : }
446 :
447 0 : basegfx::B2DRange calcRelativeShapeBounds( const basegfx::B2DVector& rPageSize,
448 : const basegfx::B2DRange& rShapeBounds )
449 : {
450 0 : return basegfx::B2DRange( rShapeBounds.getMinX() / rPageSize.getX(),
451 0 : rShapeBounds.getMinY() / rPageSize.getY(),
452 0 : rShapeBounds.getMaxX() / rPageSize.getX(),
453 0 : rShapeBounds.getMaxY() / rPageSize.getY() );
454 : }
455 :
456 : // TODO(F2): Currently, the positional attributes DO NOT mirror the XShape properties.
457 : // First and foremost, this is because we must operate with the shape boundrect,
458 : // not position and size (the conversion between logic rect, snap rect and boundrect
459 : // are non-trivial for draw shapes, and I won't duplicate them here). Thus, shapes
460 : // rotated on the page will still have 0.0 rotation angle, as the metafile
461 : // representation fetched over the API is our default zero case.
462 :
463 0 : ::basegfx::B2DHomMatrix getShapeTransformation( const ::basegfx::B2DRectangle& rShapeBounds,
464 : const ShapeAttributeLayerSharedPtr& pAttr )
465 : {
466 0 : if( !pAttr )
467 : {
468 : const basegfx::B2DHomMatrix aTransform(basegfx::tools::createScaleTranslateB2DHomMatrix(
469 : rShapeBounds.getWidth(), rShapeBounds.getHeight(),
470 0 : rShapeBounds.getMinX(), rShapeBounds.getMinY()));
471 :
472 0 : return aTransform;
473 : }
474 : else
475 : {
476 : return getAttributedShapeTransformation( rShapeBounds,
477 0 : pAttr );
478 : }
479 : }
480 :
481 0 : ::basegfx::B2DHomMatrix getSpriteTransformation( const ::basegfx::B2DVector& rPixelSize,
482 : const ::basegfx::B2DVector& rOrigSize,
483 : const ShapeAttributeLayerSharedPtr& pAttr )
484 : {
485 0 : ::basegfx::B2DHomMatrix aTransform;
486 :
487 0 : if( pAttr )
488 : {
489 0 : const double nShearX( pAttr->isShearXAngleValid() ?
490 0 : pAttr->getShearXAngle() :
491 0 : 0.0 );
492 0 : const double nShearY( pAttr->isShearYAngleValid() ?
493 0 : pAttr->getShearYAngle() :
494 0 : 0.0 );
495 0 : const double nRotation( pAttr->isRotationAngleValid() ?
496 0 : pAttr->getRotationAngle()*M_PI/180.0 :
497 0 : 0.0 );
498 :
499 : // scale, shear and rotation pivot point is the
500 : // sprite's pixel center - adapt origin accordingly
501 0 : aTransform.translate( -0.5*rPixelSize.getX(),
502 0 : -0.5*rPixelSize.getY() );
503 :
504 : const ::basegfx::B2DSize aSize(
505 0 : pAttr->isWidthValid() ? pAttr->getWidth() : rOrigSize.getX(),
506 0 : pAttr->isHeightValid() ? pAttr->getHeight() : rOrigSize.getY() );
507 :
508 : // ensure valid size (zero size will inevitably lead
509 : // to a singular transformation matrix).
510 : aTransform.scale( ::basegfx::pruneScaleValue(
511 0 : aSize.getX() /
512 : ::basegfx::pruneScaleValue(
513 0 : rOrigSize.getX() ) ),
514 : ::basegfx::pruneScaleValue(
515 0 : aSize.getY() /
516 : ::basegfx::pruneScaleValue(
517 0 : rOrigSize.getY() ) ) );
518 :
519 0 : const bool bNeedShearX( !::basegfx::fTools::equalZero(nShearX) );
520 0 : const bool bNeedShearY( !::basegfx::fTools::equalZero(nShearY) );
521 0 : const bool bNeedRotation( !::basegfx::fTools::equalZero(nRotation) );
522 :
523 0 : if( bNeedRotation || bNeedShearX || bNeedShearY )
524 : {
525 0 : if( bNeedShearX )
526 0 : aTransform.shearX( nShearX );
527 :
528 0 : if( bNeedShearY )
529 0 : aTransform.shearY( nShearY );
530 :
531 0 : if( bNeedRotation )
532 0 : aTransform.rotate( nRotation );
533 : }
534 :
535 : // move left, top corner back to original position of
536 : // the sprite (we've translated the center of the
537 : // sprite to the origin above).
538 0 : aTransform.translate( 0.5*rPixelSize.getX(),
539 0 : 0.5*rPixelSize.getY() );
540 : }
541 :
542 : // return identity transform for un-attributed
543 : // shapes. This renders the sprite as-is, in it's
544 : // document-supplied size.
545 0 : return aTransform;
546 : }
547 :
548 0 : ::basegfx::B2DRectangle getShapeUpdateArea( const ::basegfx::B2DRectangle& rUnitBounds,
549 : const ::basegfx::B2DHomMatrix& rShapeTransform,
550 : const ShapeAttributeLayerSharedPtr& pAttr )
551 : {
552 0 : ::basegfx::B2DHomMatrix aTransform;
553 :
554 0 : if( pAttr &&
555 0 : pAttr->isCharScaleValid() &&
556 0 : fabs(pAttr->getCharScale()) > 1.0 )
557 : {
558 : // enlarge shape bounds. Have to consider the worst
559 : // case here (the text fully fills the shape)
560 :
561 0 : const double nCharScale( pAttr->getCharScale() );
562 :
563 : // center of scaling is the middle of the shape
564 0 : aTransform.translate( -0.5, -0.5 );
565 0 : aTransform.scale( nCharScale, nCharScale );
566 0 : aTransform.translate( 0.5, 0.5 );
567 : }
568 :
569 0 : aTransform *= rShapeTransform;
570 :
571 0 : ::basegfx::B2DRectangle aRes;
572 :
573 : // apply shape transformation to unit rect
574 : return ::canvas::tools::calcTransformedRectBounds(
575 : aRes,
576 : rUnitBounds,
577 0 : aTransform );
578 : }
579 :
580 0 : ::basegfx::B2DRange getShapeUpdateArea( const ::basegfx::B2DRange& rUnitBounds,
581 : const ::basegfx::B2DRange& rShapeBounds )
582 : {
583 : return ::basegfx::B2DRectangle(
584 0 : basegfx::tools::lerp( rShapeBounds.getMinX(),
585 0 : rShapeBounds.getMaxX(),
586 : rUnitBounds.getMinX() ),
587 0 : basegfx::tools::lerp( rShapeBounds.getMinY(),
588 0 : rShapeBounds.getMaxY(),
589 : rUnitBounds.getMinY() ),
590 0 : basegfx::tools::lerp( rShapeBounds.getMinX(),
591 0 : rShapeBounds.getMaxX(),
592 : rUnitBounds.getMaxX() ),
593 0 : basegfx::tools::lerp( rShapeBounds.getMinY(),
594 0 : rShapeBounds.getMaxY(),
595 0 : rUnitBounds.getMaxY() ) );
596 : }
597 :
598 0 : ::basegfx::B2DRectangle getShapePosSize( const ::basegfx::B2DRectangle& rOrigBounds,
599 : const ShapeAttributeLayerSharedPtr& pAttr )
600 : {
601 : // an already empty shape bound need no further
602 : // treatment. In fact, any changes applied below would
603 : // actually remove the special empty state, thus, don't
604 : // change!
605 0 : if( !pAttr ||
606 0 : rOrigBounds.isEmpty() )
607 : {
608 0 : return rOrigBounds;
609 : }
610 : else
611 : {
612 : // cannot use maBounds anymore, attributes might have been
613 : // changed by now.
614 : // Have to use absolute values here, as negative sizes
615 : // (aka mirrored shapes) _still_ have the same bounds,
616 : // only with mirrored content.
617 0 : ::basegfx::B2DSize aSize;
618 0 : aSize.setX( fabs( pAttr->isWidthValid() ?
619 0 : pAttr->getWidth() :
620 0 : rOrigBounds.getWidth() ) );
621 0 : aSize.setY( fabs( pAttr->isHeightValid() ?
622 0 : pAttr->getHeight() :
623 0 : rOrigBounds.getHeight() ) );
624 :
625 0 : ::basegfx::B2DPoint aPos;
626 0 : aPos.setX( pAttr->isPosXValid() ?
627 0 : pAttr->getPosX() :
628 0 : rOrigBounds.getCenterX() );
629 0 : aPos.setY( pAttr->isPosYValid() ?
630 0 : pAttr->getPosY() :
631 0 : rOrigBounds.getCenterY() );
632 :
633 : // the positional attribute retrieved from the
634 : // ShapeAttributeLayer actually denotes the _middle_
635 : // of the shape (do it as the PPTs do...)
636 0 : return ::basegfx::B2DRectangle( aPos - 0.5*aSize,
637 0 : aPos + 0.5*aSize );
638 : }
639 : }
640 :
641 0 : RGBColor unoColor2RGBColor( sal_Int32 nColor )
642 : {
643 : return RGBColor(
644 : ::cppcanvas::makeColor(
645 : // convert from API color to IntSRGBA color
646 : // (0xAARRGGBB -> 0xRRGGBBAA)
647 0 : static_cast< sal_uInt8 >( nColor >> 16U ),
648 0 : static_cast< sal_uInt8 >( nColor >> 8U ),
649 : static_cast< sal_uInt8 >( nColor ),
650 0 : static_cast< sal_uInt8 >( nColor >> 24U ) ) );
651 : }
652 :
653 0 : sal_Int32 RGBAColor2UnoColor( ::cppcanvas::Color::IntSRGBA aColor )
654 : {
655 : return ::cppcanvas::makeColorARGB(
656 : // convert from IntSRGBA color to API color
657 : // (0xRRGGBBAA -> 0xAARRGGBB)
658 : static_cast< sal_uInt8 >(0),
659 0 : ::cppcanvas::getRed(aColor),
660 0 : ::cppcanvas::getGreen(aColor),
661 0 : ::cppcanvas::getBlue(aColor));
662 : }
663 :
664 0 : void fillRect( const ::cppcanvas::CanvasSharedPtr& rCanvas,
665 : const ::basegfx::B2DRectangle& rRect,
666 : ::cppcanvas::Color::IntSRGBA aFillColor )
667 : {
668 : const ::basegfx::B2DPolygon aPoly(
669 0 : ::basegfx::tools::createPolygonFromRect( rRect ));
670 :
671 : ::cppcanvas::PolyPolygonSharedPtr pPolyPoly(
672 0 : ::cppcanvas::BaseGfxFactory::createPolyPolygon( rCanvas, aPoly ) );
673 :
674 0 : if( pPolyPoly )
675 : {
676 0 : pPolyPoly->setRGBAFillColor( aFillColor );
677 0 : pPolyPoly->draw();
678 0 : }
679 0 : }
680 :
681 0 : void initSlideBackground( const ::cppcanvas::CanvasSharedPtr& rCanvas,
682 : const ::basegfx::B2ISize& rSize )
683 : {
684 0 : ::cppcanvas::CanvasSharedPtr pCanvas( rCanvas->clone() );
685 :
686 : // set transformation to identitiy (->device pixel)
687 0 : pCanvas->setTransformation( ::basegfx::B2DHomMatrix() );
688 :
689 : // #i42440# Fill the _full_ background in
690 : // black. Since we had to extend the bitmap by one
691 : // pixel, and the bitmap is initialized white,
692 : // depending on the slide content a one pixel wide
693 : // line will show to the bottom and the right.
694 : fillRect( pCanvas,
695 : ::basegfx::B2DRectangle( 0.0, 0.0,
696 0 : rSize.getX(),
697 0 : rSize.getY() ),
698 0 : 0x000000FFU );
699 :
700 : // fill the bounds rectangle in white. Subtract one pixel
701 : // from both width and height, because the slide size is
702 : // chosen one pixel larger than given by the drawing
703 : // layer. This is because shapes with line style, that
704 : // have the size of the slide would otherwise be cut
705 : // off. OTOH, every other slide background (solid fill,
706 : // gradient, bitmap) render one pixel less, thus revealing
707 : // ugly white pixel to the right and the bottom.
708 : fillRect( pCanvas,
709 : ::basegfx::B2DRectangle( 0.0, 0.0,
710 0 : rSize.getX()-1,
711 0 : rSize.getY()-1 ),
712 0 : 0xFFFFFFFFU );
713 0 : }
714 :
715 0 : ::basegfx::B2DRectangle getAPIShapeBounds( const uno::Reference< drawing::XShape >& xShape )
716 : {
717 : uno::Reference< beans::XPropertySet > xPropSet( xShape,
718 0 : uno::UNO_QUERY_THROW );
719 : // read bound rect
720 0 : awt::Rectangle aTmpRect;
721 0 : if( !(xPropSet->getPropertyValue(
722 0 : OUString("BoundRect") ) >>= aTmpRect) )
723 : {
724 0 : ENSURE_OR_THROW( false,
725 : "getAPIShapeBounds(): Could not get \"BoundRect\" property from shape" );
726 : }
727 :
728 : return ::basegfx::B2DRectangle( aTmpRect.X,
729 : aTmpRect.Y,
730 0 : aTmpRect.X+aTmpRect.Width,
731 0 : aTmpRect.Y+aTmpRect.Height );
732 : }
733 :
734 : /*
735 : TODO(F1): When ZOrder someday becomes usable enable this
736 :
737 : double getAPIShapePrio( const uno::Reference< drawing::XShape >& xShape )
738 : {
739 : uno::Reference< beans::XPropertySet > xPropSet( xShape,
740 : uno::UNO_QUERY_THROW );
741 : // read prio
742 : sal_Int32 nPrio(0);
743 : if( !(xPropSet->getPropertyValue(
744 : OUString("ZOrder") ) >>= nPrio) )
745 : {
746 : ENSURE_OR_THROW( false,
747 : "getAPIShapePrio(): Could not get \"ZOrder\" property from shape" );
748 : }
749 :
750 : // TODO(F2): Check and adapt the range of possible values here.
751 : // Maybe we can also take the total number of shapes here
752 : return nPrio / 65535.0;
753 : }
754 : */
755 :
756 0 : basegfx::B2IVector getSlideSizePixel( const basegfx::B2DVector& rSlideSize,
757 : const UnoViewSharedPtr& pView )
758 : {
759 0 : ENSURE_OR_THROW(pView, "getSlideSizePixel(): invalid view");
760 :
761 : // determine transformed page bounds
762 : const basegfx::B2DRange aRect( 0,0,
763 : rSlideSize.getX(),
764 0 : rSlideSize.getY() );
765 0 : basegfx::B2DRange aTmpRect;
766 : canvas::tools::calcTransformedRectBounds( aTmpRect,
767 : aRect,
768 0 : pView->getTransformation() );
769 :
770 : // #i42440# Returned slide size is one pixel too small, as
771 : // rendering happens one pixel to the right and below the
772 : // actual bound rect.
773 : return basegfx::B2IVector(
774 0 : basegfx::fround( aTmpRect.getRange().getX() ) + 1,
775 0 : basegfx::fround( aTmpRect.getRange().getY() ) + 1 );
776 : }
777 : }
778 : }
779 :
780 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|