Branch data 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: */
|