856 lines
34 KiB
C++
856 lines
34 KiB
C++
/*************************************************************************
|
|
*
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* Copyright 2008 by Sun Microsystems, Inc.
|
|
*
|
|
* OpenOffice.org - a multi-platform office productivity suite
|
|
*
|
|
* $RCSfile: tools.cxx,v $
|
|
* $Revision: 1.14 $
|
|
*
|
|
* This file is part of OpenOffice.org.
|
|
*
|
|
* OpenOffice.org is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License version 3
|
|
* only, as published by the Free Software Foundation.
|
|
*
|
|
* OpenOffice.org is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License version 3 for more details
|
|
* (a copy is included in the LICENSE file that accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* version 3 along with OpenOffice.org. If not, see
|
|
* <http://www.openoffice.org/license.html>
|
|
* for a copy of the LGPLv3 License.
|
|
*
|
|
************************************************************************/
|
|
|
|
// MARKER(update_precomp.py): autogen include statement, do not remove
|
|
#include "precompiled_slideshow.hxx"
|
|
|
|
#include <canvas/debug.hxx>
|
|
#include <tools/diagnose_ex.h>
|
|
#include <canvas/canvastools.hxx>
|
|
|
|
#include <math.h>
|
|
|
|
#include <com/sun/star/beans/NamedValue.hpp>
|
|
#include <com/sun/star/awt/Rectangle.hpp>
|
|
#include <com/sun/star/animations/ValuePair.hpp>
|
|
#include <com/sun/star/drawing/FillStyle.hpp>
|
|
#include <com/sun/star/drawing/LineStyle.hpp>
|
|
#include <com/sun/star/awt/FontSlant.hpp>
|
|
|
|
#include <basegfx/polygon/b2dpolygon.hxx>
|
|
#include <basegfx/polygon/b2dpolygontools.hxx>
|
|
#include <basegfx/range/b2drange.hxx>
|
|
#include <basegfx/vector/b2dvector.hxx>
|
|
#include <basegfx/vector/b2ivector.hxx>
|
|
#include <basegfx/matrix/b2dhommatrix.hxx>
|
|
#include <basegfx/numeric/ftools.hxx>
|
|
#include <basegfx/tools/lerp.hxx>
|
|
#include <basegfx/matrix/b2dhommatrixtools.hxx>
|
|
|
|
#include <cppcanvas/basegfxfactory.hxx>
|
|
|
|
#include "unoview.hxx"
|
|
#include "smilfunctionparser.hxx"
|
|
#include "tools.hxx"
|
|
|
|
#include <limits>
|
|
|
|
|
|
using namespace ::com::sun::star;
|
|
|
|
namespace slideshow
|
|
{
|
|
namespace internal
|
|
{
|
|
namespace
|
|
{
|
|
class NamedValueStringComparator
|
|
{
|
|
public:
|
|
NamedValueStringComparator( const ::rtl::OUString& rSearchString ) :
|
|
mrSearchString( rSearchString )
|
|
{
|
|
}
|
|
|
|
bool operator()( const beans::NamedValue& rValue )
|
|
{
|
|
return rValue.Name == mrSearchString;
|
|
}
|
|
|
|
private:
|
|
const ::rtl::OUString& mrSearchString;
|
|
};
|
|
|
|
class NamedValueComparator
|
|
{
|
|
public:
|
|
NamedValueComparator( const beans::NamedValue& rKey ) :
|
|
mrKey( rKey )
|
|
{
|
|
}
|
|
|
|
bool operator()( const beans::NamedValue& rValue )
|
|
{
|
|
return rValue.Name == mrKey.Name && rValue.Value == mrKey.Value;
|
|
}
|
|
|
|
private:
|
|
const beans::NamedValue& mrKey;
|
|
};
|
|
|
|
::basegfx::B2DHomMatrix getAttributedShapeTransformation( const ::basegfx::B2DRectangle& rShapeBounds,
|
|
const ShapeAttributeLayerSharedPtr& pAttr )
|
|
{
|
|
::basegfx::B2DHomMatrix aTransform;
|
|
const ::basegfx::B2DSize& rSize( rShapeBounds.getRange() );
|
|
|
|
const double nShearX( pAttr->isShearXAngleValid() ?
|
|
pAttr->getShearXAngle() :
|
|
0.0 );
|
|
const double nShearY( pAttr->isShearYAngleValid() ?
|
|
pAttr->getShearYAngle() :
|
|
0.0 );
|
|
const double nRotation( pAttr->isRotationAngleValid() ?
|
|
pAttr->getRotationAngle()*M_PI/180.0 :
|
|
0.0 );
|
|
|
|
// scale, shear and rotation pivot point is the shape
|
|
// center - adapt origin accordingly
|
|
aTransform.translate( -0.5, -0.5 );
|
|
|
|
// ensure valid size (zero size will inevitably lead
|
|
// to a singular transformation matrix)
|
|
aTransform.scale( ::basegfx::pruneScaleValue(
|
|
rSize.getX() ),
|
|
::basegfx::pruneScaleValue(
|
|
rSize.getY() ) );
|
|
|
|
const bool bNeedShearX( !::basegfx::fTools::equalZero(nShearX) );
|
|
const bool bNeedShearY( !::basegfx::fTools::equalZero(nShearY) );
|
|
const bool bNeedRotation( !::basegfx::fTools::equalZero(nRotation) );
|
|
|
|
if( bNeedRotation || bNeedShearX || bNeedShearY )
|
|
{
|
|
if( bNeedShearX )
|
|
aTransform.shearX( nShearX );
|
|
|
|
if( bNeedShearY )
|
|
aTransform.shearY( nShearY );
|
|
|
|
if( bNeedRotation )
|
|
aTransform.rotate( nRotation );
|
|
}
|
|
|
|
// move left, top corner back to position of the
|
|
// shape. Since we've already translated the
|
|
// center of the shape to the origin (the
|
|
// translate( -0.5, -0.5 ) above), translate to
|
|
// center of final shape position here.
|
|
aTransform.translate( rShapeBounds.getCenterX(),
|
|
rShapeBounds.getCenterY() );
|
|
|
|
return aTransform;
|
|
}
|
|
}
|
|
|
|
// Value extraction from Any
|
|
// =========================
|
|
|
|
/// extract unary double value from Any
|
|
bool extractValue( double& o_rValue,
|
|
const uno::Any& rSourceAny,
|
|
const ShapeSharedPtr& rShape,
|
|
const ::basegfx::B2DVector& rSlideBounds )
|
|
{
|
|
// try to extract numeric value (double, or smaller POD, like float or int)
|
|
if( (rSourceAny >>= o_rValue) )
|
|
{
|
|
// succeeded
|
|
return true;
|
|
}
|
|
|
|
// try to extract string
|
|
::rtl::OUString aString;
|
|
if( !(rSourceAny >>= aString) )
|
|
return false; // nothing left to try
|
|
|
|
// parse the string into an ExpressionNode
|
|
try
|
|
{
|
|
// Parse string into ExpressionNode, eval node at time 0.0
|
|
o_rValue = (*SmilFunctionParser::parseSmilValue(
|
|
aString,
|
|
calcRelativeShapeBounds(rSlideBounds,
|
|
rShape->getBounds()) ))(0.0);
|
|
}
|
|
catch( ParseError& )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// extract enum/constant group value from Any
|
|
bool extractValue( sal_Int32& o_rValue,
|
|
const uno::Any& rSourceAny,
|
|
const ShapeSharedPtr& /*rShape*/,
|
|
const ::basegfx::B2DVector& /*rSlideBounds*/ )
|
|
{
|
|
// try to extract numeric value (int, or smaller POD, like byte)
|
|
if( (rSourceAny >>= o_rValue) )
|
|
{
|
|
// succeeded
|
|
return true;
|
|
}
|
|
|
|
// okay, no plain int. Maybe one of the domain-specific enums?
|
|
drawing::FillStyle eFillStyle;
|
|
if( (rSourceAny >>= eFillStyle) )
|
|
{
|
|
o_rValue = sal::static_int_cast<sal_Int16>(eFillStyle);
|
|
|
|
// succeeded
|
|
return true;
|
|
}
|
|
|
|
drawing::LineStyle eLineStyle;
|
|
if( (rSourceAny >>= eLineStyle) )
|
|
{
|
|
o_rValue = sal::static_int_cast<sal_Int16>(eLineStyle);
|
|
|
|
// succeeded
|
|
return true;
|
|
}
|
|
|
|
awt::FontSlant eFontSlant;
|
|
if( (rSourceAny >>= eFontSlant) )
|
|
{
|
|
o_rValue = sal::static_int_cast<sal_Int16>(eFontSlant);
|
|
|
|
// succeeded
|
|
return true;
|
|
}
|
|
|
|
// nothing left to try. Failure
|
|
return false;
|
|
}
|
|
|
|
/// extract enum/constant group value from Any
|
|
bool extractValue( sal_Int16& o_rValue,
|
|
const uno::Any& rSourceAny,
|
|
const ShapeSharedPtr& rShape,
|
|
const ::basegfx::B2DVector& rSlideBounds )
|
|
{
|
|
sal_Int32 aValue;
|
|
if( !extractValue(aValue,rSourceAny,rShape,rSlideBounds) )
|
|
return false;
|
|
|
|
if( std::numeric_limits<sal_Int16>::max() < aValue ||
|
|
std::numeric_limits<sal_Int16>::min() > aValue )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
o_rValue = static_cast<sal_Int16>(aValue);
|
|
|
|
return true;
|
|
}
|
|
|
|
/// extract color value from Any
|
|
bool extractValue( RGBColor& o_rValue,
|
|
const uno::Any& rSourceAny,
|
|
const ShapeSharedPtr& /*rShape*/,
|
|
const ::basegfx::B2DVector& /*rSlideBounds*/ )
|
|
{
|
|
// try to extract numeric value (double, or smaller POD, like float or int)
|
|
{
|
|
double nTmp = 0;
|
|
if( (rSourceAny >>= nTmp) )
|
|
{
|
|
sal_uInt32 aIntColor( static_cast< sal_uInt32 >(nTmp) );
|
|
|
|
// TODO(F2): Handle color values correctly, here
|
|
o_rValue = unoColor2RGBColor( aIntColor );
|
|
|
|
// succeeded
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// try double sequence
|
|
{
|
|
uno::Sequence< double > aTmp;
|
|
if( (rSourceAny >>= aTmp) )
|
|
{
|
|
ENSURE_OR_THROW( aTmp.getLength() == 3,
|
|
"extractValue(): inappropriate length for RGB color value" );
|
|
|
|
o_rValue = RGBColor( aTmp[0], aTmp[1], aTmp[2] );
|
|
|
|
// succeeded
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// try sal_Int32 sequence
|
|
{
|
|
uno::Sequence< sal_Int32 > aTmp;
|
|
if( (rSourceAny >>= aTmp) )
|
|
{
|
|
ENSURE_OR_THROW( aTmp.getLength() == 3,
|
|
"extractValue(): inappropriate length for RGB color value" );
|
|
|
|
// truncate to byte
|
|
o_rValue = RGBColor( ::cppcanvas::makeColor(
|
|
static_cast<sal_uInt8>(aTmp[0]),
|
|
static_cast<sal_uInt8>(aTmp[1]),
|
|
static_cast<sal_uInt8>(aTmp[2]),
|
|
255 ) );
|
|
|
|
// succeeded
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// try sal_Int8 sequence
|
|
{
|
|
uno::Sequence< sal_Int8 > aTmp;
|
|
if( (rSourceAny >>= aTmp) )
|
|
{
|
|
ENSURE_OR_THROW( aTmp.getLength() == 3,
|
|
"extractValue(): inappropriate length for RGB color value" );
|
|
|
|
o_rValue = RGBColor( ::cppcanvas::makeColor( aTmp[0], aTmp[1], aTmp[2], 255 ) );
|
|
|
|
// succeeded
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// try to extract string
|
|
::rtl::OUString aString;
|
|
if( !(rSourceAny >>= aString) )
|
|
return false; // nothing left to try
|
|
|
|
// TODO(F2): Provide symbolic color values here
|
|
o_rValue = RGBColor( 0.5, 0.5, 0.5 );
|
|
|
|
return true;
|
|
}
|
|
|
|
/// extract color value from Any
|
|
bool extractValue( HSLColor& o_rValue,
|
|
const uno::Any& rSourceAny,
|
|
const ShapeSharedPtr& /*rShape*/,
|
|
const ::basegfx::B2DVector& /*rSlideBounds*/ )
|
|
{
|
|
// try double sequence
|
|
{
|
|
uno::Sequence< double > aTmp;
|
|
if( (rSourceAny >>= aTmp) )
|
|
{
|
|
ENSURE_OR_THROW( aTmp.getLength() == 3,
|
|
"extractValue(): inappropriate length for HSL color value" );
|
|
|
|
o_rValue = HSLColor( aTmp[0], aTmp[1], aTmp[2] );
|
|
|
|
// succeeded
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// try sal_Int8 sequence
|
|
{
|
|
uno::Sequence< sal_Int8 > aTmp;
|
|
if( (rSourceAny >>= aTmp) )
|
|
{
|
|
ENSURE_OR_THROW( aTmp.getLength() == 3,
|
|
"extractValue(): inappropriate length for HSL color value" );
|
|
|
|
o_rValue = HSLColor( aTmp[0]*360.0/255.0, aTmp[1]/255.0, aTmp[2]/255.0 );
|
|
|
|
// succeeded
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false; // nothing left to try
|
|
}
|
|
|
|
/// extract plain string from Any
|
|
bool extractValue( ::rtl::OUString& o_rValue,
|
|
const uno::Any& rSourceAny,
|
|
const ShapeSharedPtr& /*rShape*/,
|
|
const ::basegfx::B2DVector& /*rSlideBounds*/ )
|
|
{
|
|
// try to extract string
|
|
if( !(rSourceAny >>= o_rValue) )
|
|
return false; // nothing left to try
|
|
|
|
return true;
|
|
}
|
|
|
|
/// extract bool value from Any
|
|
bool extractValue( bool& o_rValue,
|
|
const uno::Any& rSourceAny,
|
|
const ShapeSharedPtr& /*rShape*/,
|
|
const ::basegfx::B2DVector& /*rSlideBounds*/ )
|
|
{
|
|
sal_Bool nTmp = sal_Bool();
|
|
// try to extract bool value
|
|
if( (rSourceAny >>= nTmp) )
|
|
{
|
|
o_rValue = nTmp;
|
|
|
|
// succeeded
|
|
return true;
|
|
}
|
|
|
|
// try to extract string
|
|
::rtl::OUString aString;
|
|
if( !(rSourceAny >>= aString) )
|
|
return false; // nothing left to try
|
|
|
|
// we also take the strings "true" and "false",
|
|
// as well as "on" and "off" here
|
|
if( aString.equalsIgnoreAsciiCaseAscii("true") ||
|
|
aString.equalsIgnoreAsciiCaseAscii("on") )
|
|
{
|
|
o_rValue = true;
|
|
return true;
|
|
}
|
|
if( aString.equalsIgnoreAsciiCaseAscii("false") ||
|
|
aString.equalsIgnoreAsciiCaseAscii("off") )
|
|
{
|
|
o_rValue = false;
|
|
return true;
|
|
}
|
|
|
|
// ultimately failed.
|
|
return false;
|
|
}
|
|
|
|
/// extract double 2-tuple from Any
|
|
bool extractValue( ::basegfx::B2DTuple& o_rPair,
|
|
const uno::Any& rSourceAny,
|
|
const ShapeSharedPtr& rShape,
|
|
const ::basegfx::B2DVector& rSlideBounds )
|
|
{
|
|
animations::ValuePair aPair;
|
|
|
|
if( !(rSourceAny >>= aPair) )
|
|
return false;
|
|
|
|
double nFirst;
|
|
if( !extractValue( nFirst, aPair.First, rShape, rSlideBounds ) )
|
|
return false;
|
|
|
|
double nSecond;
|
|
if( !extractValue( nSecond, aPair.Second, rShape, rSlideBounds ) )
|
|
return false;
|
|
|
|
o_rPair.setX( nFirst );
|
|
o_rPair.setY( nSecond );
|
|
|
|
return true;
|
|
}
|
|
|
|
bool findNamedValue( uno::Sequence< beans::NamedValue > const& rSequence,
|
|
const beans::NamedValue& rSearchKey )
|
|
{
|
|
const beans::NamedValue* pArray = rSequence.getConstArray();
|
|
const size_t nLen( rSequence.getLength() );
|
|
|
|
if( nLen == 0 )
|
|
return false;
|
|
|
|
const beans::NamedValue* pFound = ::std::find_if( pArray,
|
|
pArray + nLen,
|
|
NamedValueComparator( rSearchKey ) );
|
|
|
|
if( pFound == pArray + nLen )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool findNamedValue( beans::NamedValue* o_pRet,
|
|
const uno::Sequence< beans::NamedValue >& rSequence,
|
|
const ::rtl::OUString& rSearchString )
|
|
{
|
|
const beans::NamedValue* pArray = rSequence.getConstArray();
|
|
const size_t nLen( rSequence.getLength() );
|
|
|
|
if( nLen == 0 )
|
|
return false;
|
|
|
|
const beans::NamedValue* pFound = ::std::find_if( pArray,
|
|
pArray + nLen,
|
|
NamedValueStringComparator( rSearchString ) );
|
|
if( pFound == pArray + nLen )
|
|
return false;
|
|
|
|
if( o_pRet )
|
|
*o_pRet = *pFound;
|
|
|
|
return true;
|
|
}
|
|
|
|
basegfx::B2DRange calcRelativeShapeBounds( const basegfx::B2DVector& rPageSize,
|
|
const basegfx::B2DRange& rShapeBounds )
|
|
{
|
|
return basegfx::B2DRange( rShapeBounds.getMinX() / rPageSize.getX(),
|
|
rShapeBounds.getMinY() / rPageSize.getY(),
|
|
rShapeBounds.getMaxX() / rPageSize.getX(),
|
|
rShapeBounds.getMaxY() / rPageSize.getY() );
|
|
}
|
|
|
|
// TODO(F2): Currently, the positional attributes DO NOT mirror the XShape properties.
|
|
// First and foremost, this is because we must operate with the shape boundrect,
|
|
// not position and size (the conversion between logic rect, snap rect and boundrect
|
|
// are non-trivial for draw shapes, and I won't duplicate them here). Thus, shapes
|
|
// rotated on the page will still have 0.0 rotation angle, as the metafile
|
|
// representation fetched over the API is our default zero case.
|
|
|
|
::basegfx::B2DHomMatrix getShapeTransformation( const ::basegfx::B2DRectangle& rShapeBounds,
|
|
const ShapeAttributeLayerSharedPtr& pAttr )
|
|
{
|
|
if( !pAttr )
|
|
{
|
|
const basegfx::B2DHomMatrix aTransform(basegfx::tools::createScaleTranslateB2DHomMatrix(
|
|
rShapeBounds.getWidth(), rShapeBounds.getHeight(),
|
|
rShapeBounds.getMinX(), rShapeBounds.getMinY()));
|
|
|
|
return aTransform;
|
|
}
|
|
else
|
|
{
|
|
return getAttributedShapeTransformation( rShapeBounds,
|
|
pAttr );
|
|
}
|
|
}
|
|
|
|
::basegfx::B2DHomMatrix getSpriteTransformation( const ::basegfx::B2DVector& rPixelSize,
|
|
const ::basegfx::B2DVector& rOrigSize,
|
|
const ShapeAttributeLayerSharedPtr& pAttr )
|
|
{
|
|
::basegfx::B2DHomMatrix aTransform;
|
|
|
|
if( pAttr )
|
|
{
|
|
const double nShearX( pAttr->isShearXAngleValid() ?
|
|
pAttr->getShearXAngle() :
|
|
0.0 );
|
|
const double nShearY( pAttr->isShearYAngleValid() ?
|
|
pAttr->getShearYAngle() :
|
|
0.0 );
|
|
const double nRotation( pAttr->isRotationAngleValid() ?
|
|
pAttr->getRotationAngle()*M_PI/180.0 :
|
|
0.0 );
|
|
|
|
// scale, shear and rotation pivot point is the
|
|
// sprite's pixel center - adapt origin accordingly
|
|
aTransform.translate( -0.5*rPixelSize.getX(),
|
|
-0.5*rPixelSize.getY() );
|
|
|
|
const ::basegfx::B2DSize aSize(
|
|
pAttr->isWidthValid() ? pAttr->getWidth() : rOrigSize.getX(),
|
|
pAttr->isHeightValid() ? pAttr->getHeight() : rOrigSize.getY() );
|
|
|
|
// ensure valid size (zero size will inevitably lead
|
|
// to a singular transformation matrix).
|
|
aTransform.scale( ::basegfx::pruneScaleValue(
|
|
aSize.getX() /
|
|
::basegfx::pruneScaleValue(
|
|
rOrigSize.getX() ) ),
|
|
::basegfx::pruneScaleValue(
|
|
aSize.getY() /
|
|
::basegfx::pruneScaleValue(
|
|
rOrigSize.getY() ) ) );
|
|
|
|
const bool bNeedShearX( !::basegfx::fTools::equalZero(nShearX) );
|
|
const bool bNeedShearY( !::basegfx::fTools::equalZero(nShearY) );
|
|
const bool bNeedRotation( !::basegfx::fTools::equalZero(nRotation) );
|
|
|
|
if( bNeedRotation || bNeedShearX || bNeedShearY )
|
|
{
|
|
if( bNeedShearX )
|
|
aTransform.shearX( nShearX );
|
|
|
|
if( bNeedShearY )
|
|
aTransform.shearY( nShearY );
|
|
|
|
if( bNeedRotation )
|
|
aTransform.rotate( nRotation );
|
|
}
|
|
|
|
// move left, top corner back to original position of
|
|
// the sprite (we've translated the center of the
|
|
// sprite to the origin above).
|
|
aTransform.translate( 0.5*rPixelSize.getX(),
|
|
0.5*rPixelSize.getY() );
|
|
}
|
|
|
|
// return identity transform for un-attributed
|
|
// shapes. This renders the sprite as-is, in it's
|
|
// document-supplied size.
|
|
return aTransform;
|
|
}
|
|
|
|
::basegfx::B2DRectangle getShapeUpdateArea( const ::basegfx::B2DRectangle& rUnitBounds,
|
|
const ::basegfx::B2DHomMatrix& rShapeTransform,
|
|
const ShapeAttributeLayerSharedPtr& pAttr )
|
|
{
|
|
::basegfx::B2DHomMatrix aTransform;
|
|
|
|
if( pAttr &&
|
|
pAttr->isCharScaleValid() &&
|
|
fabs(pAttr->getCharScale()) > 1.0 )
|
|
{
|
|
// enlarge shape bounds. Have to consider the worst
|
|
// case here (the text fully fills the shape)
|
|
|
|
const double nCharScale( pAttr->getCharScale() );
|
|
|
|
// center of scaling is the middle of the shape
|
|
aTransform.translate( -0.5, -0.5 );
|
|
aTransform.scale( nCharScale, nCharScale );
|
|
aTransform.translate( 0.5, 0.5 );
|
|
}
|
|
|
|
aTransform *= rShapeTransform;
|
|
|
|
::basegfx::B2DRectangle aRes;
|
|
|
|
// apply shape transformation to unit rect
|
|
return ::canvas::tools::calcTransformedRectBounds(
|
|
aRes,
|
|
rUnitBounds,
|
|
aTransform );
|
|
}
|
|
|
|
::basegfx::B2DRange getShapeUpdateArea( const ::basegfx::B2DRange& rUnitBounds,
|
|
const ::basegfx::B2DRange& rShapeBounds )
|
|
{
|
|
return ::basegfx::B2DRectangle(
|
|
basegfx::tools::lerp( rShapeBounds.getMinX(),
|
|
rShapeBounds.getMaxX(),
|
|
rUnitBounds.getMinX() ),
|
|
basegfx::tools::lerp( rShapeBounds.getMinY(),
|
|
rShapeBounds.getMaxY(),
|
|
rUnitBounds.getMinY() ),
|
|
basegfx::tools::lerp( rShapeBounds.getMinX(),
|
|
rShapeBounds.getMaxX(),
|
|
rUnitBounds.getMaxX() ),
|
|
basegfx::tools::lerp( rShapeBounds.getMinY(),
|
|
rShapeBounds.getMaxY(),
|
|
rUnitBounds.getMaxY() ) );
|
|
}
|
|
|
|
::basegfx::B2DRectangle getShapePosSize( const ::basegfx::B2DRectangle& rOrigBounds,
|
|
const ShapeAttributeLayerSharedPtr& pAttr )
|
|
{
|
|
// an already empty shape bound need no further
|
|
// treatment. In fact, any changes applied below would
|
|
// actually remove the special empty state, thus, don't
|
|
// change!
|
|
if( !pAttr ||
|
|
rOrigBounds.isEmpty() )
|
|
{
|
|
return rOrigBounds;
|
|
}
|
|
else
|
|
{
|
|
// cannot use maBounds anymore, attributes might have been
|
|
// changed by now.
|
|
// Have to use absolute values here, as negative sizes
|
|
// (aka mirrored shapes) _still_ have the same bounds,
|
|
// only with mirrored content.
|
|
::basegfx::B2DSize aSize;
|
|
aSize.setX( fabs( pAttr->isWidthValid() ?
|
|
pAttr->getWidth() :
|
|
rOrigBounds.getWidth() ) );
|
|
aSize.setY( fabs( pAttr->isHeightValid() ?
|
|
pAttr->getHeight() :
|
|
rOrigBounds.getHeight() ) );
|
|
|
|
::basegfx::B2DPoint aPos;
|
|
aPos.setX( pAttr->isPosXValid() ?
|
|
pAttr->getPosX() :
|
|
rOrigBounds.getCenterX() );
|
|
aPos.setY( pAttr->isPosYValid() ?
|
|
pAttr->getPosY() :
|
|
rOrigBounds.getCenterY() );
|
|
|
|
// the positional attribute retrieved from the
|
|
// ShapeAttributeLayer actually denotes the _middle_
|
|
// of the shape (do it as the PPTs do...)
|
|
return ::basegfx::B2DRectangle( aPos - 0.5*aSize,
|
|
aPos + 0.5*aSize );
|
|
}
|
|
}
|
|
|
|
RGBColor unoColor2RGBColor( sal_Int32 nColor )
|
|
{
|
|
return RGBColor(
|
|
::cppcanvas::makeColor(
|
|
// convert from API color to IntSRGBA color
|
|
// (0xAARRGGBB -> 0xRRGGBBAA)
|
|
static_cast< sal_uInt8 >( nColor >> 16U ),
|
|
static_cast< sal_uInt8 >( nColor >> 8U ),
|
|
static_cast< sal_uInt8 >( nColor ),
|
|
static_cast< sal_uInt8 >( nColor >> 24U ) ) );
|
|
}
|
|
|
|
sal_Int32 RGBAColor2UnoColor( ::cppcanvas::Color::IntSRGBA aColor )
|
|
{
|
|
return ::cppcanvas::makeColorARGB(
|
|
// convert from IntSRGBA color to API color
|
|
// (0xRRGGBBAA -> 0xAARRGGBB)
|
|
static_cast< sal_uInt8 >(0),
|
|
::cppcanvas::getRed(aColor),
|
|
::cppcanvas::getGreen(aColor),
|
|
::cppcanvas::getBlue(aColor));
|
|
}
|
|
|
|
/*sal_Int32 RGBAColor2UnoColor( ::cppcanvas::Color::IntSRGBA aColor )
|
|
{
|
|
return ::cppcanvas::unMakeColor(
|
|
// convert from IntSRGBA color to API color
|
|
// (0xRRGGBBAA -> 0xAARRGGBB)
|
|
static_cast< sal_uInt8 >(0),
|
|
::cppcanvas::getRed(aColor),
|
|
::cppcanvas::getGreen(aColor),
|
|
::cppcanvas::getBlue(aColor));
|
|
}*/
|
|
|
|
sal_Int8 unSignedToSigned(sal_Int8 nInt)
|
|
{
|
|
if(nInt < 0 ){
|
|
sal_Int8 nInt2 = nInt >> 1U;
|
|
return nInt2;
|
|
}else{
|
|
return nInt;
|
|
}
|
|
}
|
|
|
|
void fillRect( const ::cppcanvas::CanvasSharedPtr& rCanvas,
|
|
const ::basegfx::B2DRectangle& rRect,
|
|
::cppcanvas::Color::IntSRGBA aFillColor )
|
|
{
|
|
const ::basegfx::B2DPolygon aPoly(
|
|
::basegfx::tools::createPolygonFromRect( rRect ));
|
|
|
|
::cppcanvas::PolyPolygonSharedPtr pPolyPoly(
|
|
::cppcanvas::BaseGfxFactory::getInstance().createPolyPolygon( rCanvas,
|
|
aPoly ) );
|
|
|
|
if( pPolyPoly )
|
|
{
|
|
pPolyPoly->setRGBAFillColor( aFillColor );
|
|
pPolyPoly->draw();
|
|
}
|
|
}
|
|
|
|
void initSlideBackground( const ::cppcanvas::CanvasSharedPtr& rCanvas,
|
|
const ::basegfx::B2ISize& rSize )
|
|
{
|
|
::cppcanvas::CanvasSharedPtr pCanvas( rCanvas->clone() );
|
|
|
|
// set transformation to identitiy (->device pixel)
|
|
pCanvas->setTransformation( ::basegfx::B2DHomMatrix() );
|
|
|
|
// #i42440# Fill the _full_ background in
|
|
// black. Since we had to extend the bitmap by one
|
|
// pixel, and the bitmap is initialized white,
|
|
// depending on the slide content a one pixel wide
|
|
// line will show to the bottom and the right.
|
|
fillRect( pCanvas,
|
|
::basegfx::B2DRectangle( 0.0, 0.0,
|
|
rSize.getX(),
|
|
rSize.getY() ),
|
|
0x000000FFU );
|
|
|
|
// fill the bounds rectangle in white. Subtract one pixel
|
|
// from both width and height, because the slide size is
|
|
// chosen one pixel larger than given by the drawing
|
|
// layer. This is because shapes with line style, that
|
|
// have the size of the slide would otherwise be cut
|
|
// off. OTOH, every other slide background (solid fill,
|
|
// gradient, bitmap) render one pixel less, thus revealing
|
|
// ugly white pixel to the right and the bottom.
|
|
fillRect( pCanvas,
|
|
::basegfx::B2DRectangle( 0.0, 0.0,
|
|
rSize.getX()-1,
|
|
rSize.getY()-1 ),
|
|
0xFFFFFFFFU );
|
|
}
|
|
|
|
::basegfx::B2DRectangle getAPIShapeBounds( const uno::Reference< drawing::XShape >& xShape )
|
|
{
|
|
uno::Reference< beans::XPropertySet > xPropSet( xShape,
|
|
uno::UNO_QUERY_THROW );
|
|
// read bound rect
|
|
awt::Rectangle aTmpRect;
|
|
if( !(xPropSet->getPropertyValue(
|
|
::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("BoundRect") ) ) >>= aTmpRect) )
|
|
{
|
|
ENSURE_OR_THROW( false,
|
|
"getAPIShapeBounds(): Could not get \"BoundRect\" property from shape" );
|
|
}
|
|
|
|
return ::basegfx::B2DRectangle( aTmpRect.X,
|
|
aTmpRect.Y,
|
|
aTmpRect.X+aTmpRect.Width,
|
|
aTmpRect.Y+aTmpRect.Height );
|
|
}
|
|
|
|
double getAPIShapePrio( const uno::Reference< drawing::XShape >& xShape )
|
|
{
|
|
uno::Reference< beans::XPropertySet > xPropSet( xShape,
|
|
uno::UNO_QUERY_THROW );
|
|
// read prio
|
|
sal_Int32 nPrio(0);
|
|
if( !(xPropSet->getPropertyValue(
|
|
::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("ZOrder") ) ) >>= nPrio) )
|
|
{
|
|
ENSURE_OR_THROW( false,
|
|
"getAPIShapePrio(): Could not get \"ZOrder\" property from shape" );
|
|
}
|
|
|
|
// TODO(F2): Check and adapt the range of possible values here.
|
|
// Maybe we can also take the total number of shapes here
|
|
return nPrio / 65535.0;
|
|
}
|
|
|
|
basegfx::B2IVector getSlideSizePixel( const basegfx::B2DVector& rSlideSize,
|
|
const UnoViewSharedPtr& pView )
|
|
{
|
|
ENSURE_OR_THROW(pView, "getSlideSizePixel(): invalid view");
|
|
|
|
// determine transformed page bounds
|
|
const basegfx::B2DRange aRect( 0,0,
|
|
rSlideSize.getX(),
|
|
rSlideSize.getY() );
|
|
basegfx::B2DRange aTmpRect;
|
|
canvas::tools::calcTransformedRectBounds( aTmpRect,
|
|
aRect,
|
|
pView->getTransformation() );
|
|
|
|
// #i42440# Returned slide size is one pixel too small, as
|
|
// rendering happens one pixel to the right and below the
|
|
// actual bound rect.
|
|
return basegfx::B2IVector(
|
|
basegfx::fround( aTmpRect.getRange().getX() ) + 1,
|
|
basegfx::fround( aTmpRect.getRange().getY() ) + 1 );
|
|
}
|
|
}
|
|
}
|