diff options
Diffstat (limited to 'canvas/source/tools')
22 files changed, 5912 insertions, 0 deletions
diff --git a/canvas/source/tools/cachedprimitivebase.cxx b/canvas/source/tools/cachedprimitivebase.cxx new file mode 100644 index 000000000000..fcdcffbc924c --- /dev/null +++ b/canvas/source/tools/cachedprimitivebase.cxx @@ -0,0 +1,115 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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_canvas.hxx" + +#include <canvas/debug.hxx> +#include <canvas/base/cachedprimitivebase.hxx> + +#include <com/sun/star/rendering/RepaintResult.hpp> + +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/tools/canvastools.hxx> + + +using namespace ::com::sun::star; + +#define IMPLEMENTATION_NAME "canvas::CachedPrimitiveBase" +#define SERVICE_NAME "com.sun.star.rendering.CachedBitmap" + +namespace canvas +{ + CachedPrimitiveBase::CachedPrimitiveBase( const rendering::ViewState& rUsedViewState, + const uno::Reference< rendering::XCanvas >& rTarget, + bool bFailForChangedViewTransform ) : + CachedPrimitiveBase_Base( m_aMutex ), + maUsedViewState( rUsedViewState ), + mxTarget( rTarget ), + mbFailForChangedViewTransform( bFailForChangedViewTransform ) + { + } + + CachedPrimitiveBase::~CachedPrimitiveBase() + { + } + + void SAL_CALL CachedPrimitiveBase::disposing() + { + ::osl::MutexGuard aGuard( m_aMutex ); + + maUsedViewState.Clip.clear(); + mxTarget.clear(); + } + + sal_Int8 SAL_CALL CachedPrimitiveBase::redraw( const rendering::ViewState& aState ) throw (lang::IllegalArgumentException, uno::RuntimeException) + { + ::basegfx::B2DHomMatrix aUsedTransformation; + ::basegfx::B2DHomMatrix aNewTransformation; + + ::basegfx::unotools::homMatrixFromAffineMatrix( aUsedTransformation, + maUsedViewState.AffineTransform ); + ::basegfx::unotools::homMatrixFromAffineMatrix( aNewTransformation, + aState.AffineTransform ); + + const bool bSameViewTransforms( aUsedTransformation == aNewTransformation ); + + if( mbFailForChangedViewTransform && + !bSameViewTransforms ) + { + // differing transformations, don't try to draft the + // output, just plain fail here. + return rendering::RepaintResult::FAILED; + } + + return doRedraw( aState, + maUsedViewState, + mxTarget, + bSameViewTransforms ); + } + + ::rtl::OUString SAL_CALL CachedPrimitiveBase::getImplementationName( ) throw (uno::RuntimeException) + { + return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( IMPLEMENTATION_NAME ) ); + } + + sal_Bool SAL_CALL CachedPrimitiveBase::supportsService( const ::rtl::OUString& ServiceName ) throw (uno::RuntimeException) + { + return ServiceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM ( SERVICE_NAME ) ); + } + + uno::Sequence< ::rtl::OUString > SAL_CALL CachedPrimitiveBase::getSupportedServiceNames( ) throw (uno::RuntimeException) + { + uno::Sequence< ::rtl::OUString > aRet(1); + aRet[0] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM ( SERVICE_NAME ) ); + + return aRet; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/tools/canvascustomspritehelper.cxx b/canvas/source/tools/canvascustomspritehelper.cxx new file mode 100644 index 000000000000..0c7a3e55a2d7 --- /dev/null +++ b/canvas/source/tools/canvascustomspritehelper.cxx @@ -0,0 +1,499 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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_canvas.hxx" + +#include <canvas/debug.hxx> +#include <tools/diagnose_ex.h> +#include <canvas/verbosetrace.hxx> +#include <canvas/canvastools.hxx> + +#include <rtl/math.hxx> + +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/point/b2dpoint.hxx> +#include <basegfx/tools/canvastools.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <basegfx/numeric/ftools.hxx> + +#include <canvas/base/canvascustomspritehelper.hxx> + +using namespace ::com::sun::star; + + +namespace canvas +{ + bool CanvasCustomSpriteHelper::updateClipState( const Sprite::Reference& rSprite ) + { + if( !mxClipPoly.is() ) + { + // empty clip polygon -> everything is visible now + maCurrClipBounds.reset(); + mbIsCurrClipRectangle = true; + } + else + { + const sal_Int32 nNumClipPolygons( mxClipPoly->getNumberOfPolygons() ); + + // clip is not empty - determine actual update area + ::basegfx::B2DPolyPolygon aClipPath( + polyPolygonFromXPolyPolygon2D( mxClipPoly ) ); + + // apply sprite transformation also to clip! + aClipPath.transform( maTransform ); + + // clip which is about to be set, expressed as a + // b2drectangle + const ::basegfx::B2DRectangle& rClipBounds( + ::basegfx::tools::getRange( aClipPath ) ); + + const ::basegfx::B2DRectangle aBounds( 0.0, 0.0, + maSize.getX(), + maSize.getY() ); + + // rectangular area which is actually covered by the sprite. + // coordinates are relative to the sprite origin. + ::basegfx::B2DRectangle aSpriteRectPixel; + ::canvas::tools::calcTransformedRectBounds( aSpriteRectPixel, + aBounds, + maTransform ); + + // aClipBoundsA = new clip bound rect, intersected + // with sprite area + ::basegfx::B2DRectangle aClipBoundsA(rClipBounds); + aClipBoundsA.intersect( aSpriteRectPixel ); + + if( nNumClipPolygons != 1 ) + { + // clip cannot be a single rectangle -> cannot + // optimize update + mbIsCurrClipRectangle = false; + maCurrClipBounds = aClipBoundsA; + } + else + { + // new clip could be a single rectangle - check + // that now: + const bool bNewClipIsRect( + ::basegfx::tools::isRectangle( aClipPath.getB2DPolygon(0) ) ); + + // both new and old clip are truly rectangles + // - can now take the optimized path + const bool bUseOptimizedUpdate( bNewClipIsRect && + mbIsCurrClipRectangle ); + + const ::basegfx::B2DRectangle aOldBounds( maCurrClipBounds ); + + // store new current clip type + maCurrClipBounds = aClipBoundsA; + mbIsCurrClipRectangle = bNewClipIsRect; + + if( mbActive && + bUseOptimizedUpdate ) + { + // aClipBoundsB = maCurrClipBounds, i.e. last + // clip, intersected with sprite area + typedef ::std::vector< ::basegfx::B2DRectangle > VectorOfRects; + VectorOfRects aClipDifferences; + + // get all rectangles covered by exactly one + // of the polygons (aka XOR) + ::basegfx::computeSetDifference(aClipDifferences, + aClipBoundsA, + aOldBounds); + + // aClipDifferences now contains the final + // update areas, coordinates are still relative + // to the sprite origin. before submitting + // this area to 'updateSprite()' we need to + // translate this area to the final position, + // coordinates need to be relative to the + // spritecanvas. + VectorOfRects::const_iterator aCurr( aClipDifferences.begin() ); + const VectorOfRects::const_iterator aEnd( aClipDifferences.end() ); + while( aCurr != aEnd ) + { + mpSpriteCanvas->updateSprite( + rSprite, + maPosition, + ::basegfx::B2DRectangle( + maPosition + aCurr->getMinimum(), + maPosition + aCurr->getMaximum() ) ); + ++aCurr; + } + + // update calls all done + return true; + } + } + } + + // caller needs to perform update calls + return false; + } + + CanvasCustomSpriteHelper::CanvasCustomSpriteHelper() : + mpSpriteCanvas(), + maCurrClipBounds(), + maPosition(), + maSize(), + maTransform(), + mxClipPoly(), + mfPriority(0.0), + mfAlpha(0.0), + mbActive(false), + mbIsCurrClipRectangle(true), + mbIsContentFullyOpaque( false ), + mbAlphaDirty( true ), + mbPositionDirty( true ), + mbTransformDirty( true ), + mbClipDirty( true ), + mbPrioDirty( true ), + mbVisibilityDirty( true ) + { + } + + void CanvasCustomSpriteHelper::init( const geometry::RealSize2D& rSpriteSize, + const SpriteSurface::Reference& rOwningSpriteCanvas ) + { + ENSURE_OR_THROW( rOwningSpriteCanvas.get(), + "CanvasCustomSpriteHelper::init(): Invalid owning sprite canvas" ); + + mpSpriteCanvas = rOwningSpriteCanvas; + maSize.setX( ::std::max( 1.0, + ceil( rSpriteSize.Width ) ) ); // round up to nearest int, + // enforce sprite to have at + // least (1,1) pixel size + maSize.setY( ::std::max( 1.0, + ceil( rSpriteSize.Height ) ) ); + } + + void CanvasCustomSpriteHelper::disposing() + { + mpSpriteCanvas.clear(); + } + + void CanvasCustomSpriteHelper::clearingContent( const Sprite::Reference& /*rSprite*/ ) + { + // about to clear content to fully transparent + mbIsContentFullyOpaque = false; + } + + void CanvasCustomSpriteHelper::checkDrawBitmap( const Sprite::Reference& rSprite, + const uno::Reference< rendering::XBitmap >& xBitmap, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) + { + // check whether bitmap is non-alpha, and whether its + // transformed size covers the whole sprite. + if( !xBitmap->hasAlpha() ) + { + const geometry::IntegerSize2D& rInputSize( + xBitmap->getSize() ); + const ::basegfx::B2DSize& rOurSize( + rSprite->getSizePixel() ); + + ::basegfx::B2DHomMatrix aTransform; + if( tools::isInside( + ::basegfx::B2DRectangle( 0.0,0.0, + rOurSize.getX(), + rOurSize.getY() ), + ::basegfx::B2DRectangle( 0.0,0.0, + rInputSize.Width, + rInputSize.Height ), + ::canvas::tools::mergeViewAndRenderTransform(aTransform, + viewState, + renderState) ) ) + { + // bitmap is opaque and will fully cover the sprite, + // set flag appropriately + mbIsContentFullyOpaque = true; + } + } + } + + void CanvasCustomSpriteHelper::setAlpha( const Sprite::Reference& rSprite, + double alpha ) + { + if( !mpSpriteCanvas.get() ) + return; // we're disposed + + if( alpha != mfAlpha ) + { + mfAlpha = alpha; + + if( mbActive ) + { + mpSpriteCanvas->updateSprite( rSprite, + maPosition, + getUpdateArea() ); + } + + mbAlphaDirty = true; + } + } + + void CanvasCustomSpriteHelper::move( const Sprite::Reference& rSprite, + const geometry::RealPoint2D& aNewPos, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) + { + if( !mpSpriteCanvas.get() ) + return; // we're disposed + + ::basegfx::B2DHomMatrix aTransform; + ::canvas::tools::mergeViewAndRenderTransform(aTransform, + viewState, + renderState); + + // convert position to device pixel + ::basegfx::B2DPoint aPoint( + ::basegfx::unotools::b2DPointFromRealPoint2D(aNewPos) ); + aPoint *= aTransform; + + if( aPoint != maPosition ) + { + const ::basegfx::B2DRectangle& rBounds( getFullSpriteRect() ); + + if( mbActive ) + { + mpSpriteCanvas->moveSprite( rSprite, + rBounds.getMinimum(), + rBounds.getMinimum() - maPosition + aPoint, + rBounds.getRange() ); + } + + maPosition = aPoint; + mbPositionDirty = true; + } + } + + void CanvasCustomSpriteHelper::transform( const Sprite::Reference& rSprite, + const geometry::AffineMatrix2D& aTransformation ) + { + ::basegfx::B2DHomMatrix aMatrix; + ::basegfx::unotools::homMatrixFromAffineMatrix(aMatrix, + aTransformation); + + if( maTransform != aMatrix ) + { + // retrieve bounds before and after transformation change. + const ::basegfx::B2DRectangle& rPrevBounds( getUpdateArea() ); + + maTransform = aMatrix; + + if( !updateClipState( rSprite ) && + mbActive ) + { + mpSpriteCanvas->updateSprite( rSprite, + maPosition, + rPrevBounds ); + mpSpriteCanvas->updateSprite( rSprite, + maPosition, + getUpdateArea() ); + } + + mbTransformDirty = true; + } + } + + void CanvasCustomSpriteHelper::clip( const Sprite::Reference& rSprite, + const uno::Reference< rendering::XPolyPolygon2D >& xClip ) + { + // NULL xClip explicitely allowed here (to clear clipping) + + // retrieve bounds before and after clip change. + const ::basegfx::B2DRectangle& rPrevBounds( getUpdateArea() ); + + mxClipPoly = xClip; + + if( !updateClipState( rSprite ) && + mbActive ) + { + mpSpriteCanvas->updateSprite( rSprite, + maPosition, + rPrevBounds ); + mpSpriteCanvas->updateSprite( rSprite, + maPosition, + getUpdateArea() ); + } + + mbClipDirty = true; + } + + void CanvasCustomSpriteHelper::setPriority( const Sprite::Reference& rSprite, + double nPriority ) + { + if( !mpSpriteCanvas.get() ) + return; // we're disposed + + if( nPriority != mfPriority ) + { + mfPriority = nPriority; + + if( mbActive ) + { + mpSpriteCanvas->updateSprite( rSprite, + maPosition, + getUpdateArea() ); + } + + mbPrioDirty = true; + } + } + + void CanvasCustomSpriteHelper::show( const Sprite::Reference& rSprite ) + { + if( !mpSpriteCanvas.get() ) + return; // we're disposed + + if( !mbActive ) + { + mpSpriteCanvas->showSprite( rSprite ); + mbActive = true; + + // TODO(P1): if clip is the NULL clip (nothing visible), + // also save us the update call. + + if( mfAlpha != 0.0 ) + { + mpSpriteCanvas->updateSprite( rSprite, + maPosition, + getUpdateArea() ); + } + + mbVisibilityDirty = true; + } + } + + void CanvasCustomSpriteHelper::hide( const Sprite::Reference& rSprite ) + { + if( !mpSpriteCanvas.get() ) + return; // we're disposed + + if( mbActive ) + { + mpSpriteCanvas->hideSprite( rSprite ); + mbActive = false; + + // TODO(P1): if clip is the NULL clip (nothing visible), + // also save us the update call. + + if( mfAlpha != 0.0 ) + { + mpSpriteCanvas->updateSprite( rSprite, + maPosition, + getUpdateArea() ); + } + + mbVisibilityDirty = true; + } + } + + // Sprite interface + bool CanvasCustomSpriteHelper::isAreaUpdateOpaque( const ::basegfx::B2DRange& rUpdateArea ) const + { + if( !mbIsCurrClipRectangle || + !mbIsContentFullyOpaque || + !::rtl::math::approxEqual(mfAlpha, 1.0) ) + { + // sprite either transparent, or clip rect does not + // represent exact bounds -> update might not be fully + // opaque + return false; + } + else + { + // make sure sprite rect fully covers update area - + // although the update area originates from the sprite, + // it's by no means guaranteed that it's limited to this + // sprite's update area - after all, other sprites might + // have been merged, or this sprite is moving. + return getUpdateArea().isInside( rUpdateArea ); + } + } + + ::basegfx::B2DPoint CanvasCustomSpriteHelper::getPosPixel() const + { + return maPosition; + } + + ::basegfx::B2DVector CanvasCustomSpriteHelper::getSizePixel() const + { + return maSize; + } + + ::basegfx::B2DRange CanvasCustomSpriteHelper::getUpdateArea( const ::basegfx::B2DRange& rBounds ) const + { + // Internal! Only call with locked object mutex! + ::basegfx::B2DHomMatrix aTransform( maTransform ); + aTransform.translate( maPosition.getX(), + maPosition.getY() ); + + // transform bounds at origin, as the sprite transformation is + // formulated that way + ::basegfx::B2DRectangle aTransformedBounds; + return ::canvas::tools::calcTransformedRectBounds( aTransformedBounds, + rBounds, + aTransform ); + } + + ::basegfx::B2DRange CanvasCustomSpriteHelper::getUpdateArea() const + { + // Internal! Only call with locked object mutex! + + // return effective sprite rect, i.e. take active clip into + // account + if( maCurrClipBounds.isEmpty() ) + return getUpdateArea( ::basegfx::B2DRectangle( 0.0, 0.0, + maSize.getX(), + maSize.getY() ) ); + else + return ::basegfx::B2DRectangle( + maPosition + maCurrClipBounds.getMinimum(), + maPosition + maCurrClipBounds.getMaximum() ); + } + + double CanvasCustomSpriteHelper::getPriority() const + { + return mfPriority; + } + + ::basegfx::B2DRange CanvasCustomSpriteHelper::getFullSpriteRect() const + { + // Internal! Only call with locked object mutex! + return getUpdateArea( ::basegfx::B2DRectangle( 0.0, 0.0, + maSize.getX(), + maSize.getY() ) ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/tools/canvastools.cxx b/canvas/source/tools/canvastools.cxx new file mode 100644 index 000000000000..c5571b41b82a --- /dev/null +++ b/canvas/source/tools/canvastools.cxx @@ -0,0 +1,1047 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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_canvas.hxx" + +#include <canvas/debug.hxx> +#include <tools/diagnose_ex.h> + +#include <com/sun/star/geometry/AffineMatrix2D.hpp> +#include <com/sun/star/geometry/Matrix2D.hpp> +#include <com/sun/star/awt/Rectangle.hpp> +#include <com/sun/star/util/Endianness.hpp> +#include <com/sun/star/rendering/XIntegerBitmapColorSpace.hpp> +#include <com/sun/star/rendering/IntegerBitmapLayout.hpp> +#include <com/sun/star/rendering/ColorSpaceType.hpp> +#include <com/sun/star/rendering/ColorComponentTag.hpp> +#include <com/sun/star/rendering/RenderingIntent.hpp> +#include <com/sun/star/rendering/RenderState.hpp> +#include <com/sun/star/rendering/ViewState.hpp> +#include <com/sun/star/rendering/XCanvas.hpp> +#include <com/sun/star/rendering/XColorSpace.hpp> +#include <com/sun/star/rendering/CompositeOperation.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> + +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/range/b2drange.hxx> +#include <basegfx/range/b2irange.hxx> +#include <basegfx/range/b2drectangle.hxx> +#include <basegfx/point/b2dpoint.hxx> +#include <basegfx/point/b2ipoint.hxx> +#include <basegfx/vector/b2ivector.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <basegfx/tools/canvastools.hxx> +#include <basegfx/numeric/ftools.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> + +#include <cppuhelper/compbase1.hxx> +#include <rtl/instance.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <vcl/window.hxx> +#include <vcl/canvastools.hxx> + +#include <canvas/canvastools.hxx> + +#include <limits> + + +using namespace ::com::sun::star; + +namespace com { namespace sun { namespace star { namespace rendering +{ + bool operator==( const RenderState& renderState1, + const RenderState& renderState2 ) + { + if( renderState1.Clip != renderState2.Clip ) + return false; + + if( renderState1.DeviceColor != renderState2.DeviceColor ) + return false; + + if( renderState1.CompositeOperation != renderState2.CompositeOperation ) + return false; + + ::basegfx::B2DHomMatrix mat1, mat2; + ::canvas::tools::getRenderStateTransform( mat1, renderState1 ); + ::canvas::tools::getRenderStateTransform( mat2, renderState2 ); + if( mat1 != mat2 ) + return false; + + return true; + } + + bool operator==( const ViewState& viewState1, + const ViewState& viewState2 ) + { + if( viewState1.Clip != viewState2.Clip ) + return false; + + ::basegfx::B2DHomMatrix mat1, mat2; + ::canvas::tools::getViewStateTransform( mat1, viewState1 ); + ::canvas::tools::getViewStateTransform( mat2, viewState2 ); + if( mat1 != mat2 ) + return false; + + return true; + } +}}}} + +namespace canvas +{ + namespace tools + { + geometry::RealSize2D createInfiniteSize2D() + { + return geometry::RealSize2D( + ::std::numeric_limits<double>::infinity(), + ::std::numeric_limits<double>::infinity() ); + } + + rendering::RenderState& initRenderState( rendering::RenderState& renderState ) + { + // setup identity transform + setIdentityAffineMatrix2D( renderState.AffineTransform ); + renderState.Clip = uno::Reference< rendering::XPolyPolygon2D >(); + renderState.DeviceColor = uno::Sequence< double >(); + renderState.CompositeOperation = rendering::CompositeOperation::OVER; + + return renderState; + } + + rendering::ViewState& initViewState( rendering::ViewState& viewState ) + { + // setup identity transform + setIdentityAffineMatrix2D( viewState.AffineTransform ); + viewState.Clip = uno::Reference< rendering::XPolyPolygon2D >(); + + return viewState; + } + + ::basegfx::B2DHomMatrix& getViewStateTransform( ::basegfx::B2DHomMatrix& transform, + const rendering::ViewState& viewState ) + { + return ::basegfx::unotools::homMatrixFromAffineMatrix( transform, viewState.AffineTransform ); + } + + rendering::ViewState& setViewStateTransform( rendering::ViewState& viewState, + const ::basegfx::B2DHomMatrix& transform ) + { + ::basegfx::unotools::affineMatrixFromHomMatrix( viewState.AffineTransform, transform ); + + return viewState; + } + + ::basegfx::B2DHomMatrix& getRenderStateTransform( ::basegfx::B2DHomMatrix& transform, + const rendering::RenderState& renderState ) + { + return ::basegfx::unotools::homMatrixFromAffineMatrix( transform, renderState.AffineTransform ); + } + + rendering::RenderState& setRenderStateTransform( rendering::RenderState& renderState, + const ::basegfx::B2DHomMatrix& transform ) + { + ::basegfx::unotools::affineMatrixFromHomMatrix( renderState.AffineTransform, transform ); + + return renderState; + } + + rendering::RenderState& appendToRenderState( rendering::RenderState& renderState, + const ::basegfx::B2DHomMatrix& rTransform ) + { + ::basegfx::B2DHomMatrix transform; + + getRenderStateTransform( transform, renderState ); + return setRenderStateTransform( renderState, transform * rTransform ); + } + + rendering::ViewState& appendToViewState( rendering::ViewState& viewState, + const ::basegfx::B2DHomMatrix& rTransform ) + { + ::basegfx::B2DHomMatrix transform; + + getViewStateTransform( transform, viewState ); + return setViewStateTransform( viewState, transform * rTransform ); + } + + rendering::RenderState& prependToRenderState( rendering::RenderState& renderState, + const ::basegfx::B2DHomMatrix& rTransform ) + { + ::basegfx::B2DHomMatrix transform; + + getRenderStateTransform( transform, renderState ); + return setRenderStateTransform( renderState, rTransform * transform ); + } + + rendering::ViewState& prependToViewState( rendering::ViewState& viewState, + const ::basegfx::B2DHomMatrix& rTransform ) + { + ::basegfx::B2DHomMatrix transform; + + getViewStateTransform( transform, viewState ); + return setViewStateTransform( viewState, rTransform * transform ); + } + + ::basegfx::B2DHomMatrix& mergeViewAndRenderTransform( ::basegfx::B2DHomMatrix& combinedTransform, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) + { + ::basegfx::B2DHomMatrix viewTransform; + + ::basegfx::unotools::homMatrixFromAffineMatrix( combinedTransform, renderState.AffineTransform ); + ::basegfx::unotools::homMatrixFromAffineMatrix( viewTransform, viewState.AffineTransform ); + + // this statement performs combinedTransform = viewTransform * combinedTransform + combinedTransform *= viewTransform; + + return combinedTransform; + } + + rendering::ViewState& mergeViewAndRenderState( rendering::ViewState& resultViewState, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState, + const uno::Reference< rendering::XCanvas >& /*xCanvas*/ ) + { + ::basegfx::B2DHomMatrix aTmpMatrix; + geometry::AffineMatrix2D convertedMatrix; + + resultViewState.Clip = NULL; // TODO(F2): intersect clippings + + return setViewStateTransform( + resultViewState, + mergeViewAndRenderTransform( aTmpMatrix, + viewState, + renderState ) ); + } + + geometry::AffineMatrix2D& setIdentityAffineMatrix2D( geometry::AffineMatrix2D& matrix ) + { + matrix.m00 = 1.0; + matrix.m01 = 0.0; + matrix.m02 = 0.0; + matrix.m10 = 0.0; + matrix.m11 = 1.0; + matrix.m12 = 0.0; + + return matrix; + } + + geometry::Matrix2D& setIdentityMatrix2D( geometry::Matrix2D& matrix ) + { + matrix.m00 = 1.0; + matrix.m01 = 0.0; + matrix.m10 = 0.0; + matrix.m11 = 1.0; + + return matrix; + } + + namespace + { + class StandardColorSpace : public cppu::WeakImplHelper1< com::sun::star::rendering::XIntegerBitmapColorSpace > + { + private: + uno::Sequence< sal_Int8 > maComponentTags; + uno::Sequence< sal_Int32 > maBitCounts; + + virtual ::sal_Int8 SAL_CALL getType( ) throw (uno::RuntimeException) + { + return rendering::ColorSpaceType::RGB; + } + virtual uno::Sequence< ::sal_Int8 > SAL_CALL getComponentTags( ) throw (uno::RuntimeException) + { + return maComponentTags; + } + virtual ::sal_Int8 SAL_CALL getRenderingIntent( ) throw (uno::RuntimeException) + { + return rendering::RenderingIntent::PERCEPTUAL; + } + virtual uno::Sequence< beans::PropertyValue > SAL_CALL getProperties( ) throw (uno::RuntimeException) + { + return uno::Sequence< beans::PropertyValue >(); + } + virtual uno::Sequence< double > SAL_CALL convertColorSpace( const uno::Sequence< double >& deviceColor, + const uno::Reference< rendering::XColorSpace >& targetColorSpace ) throw (lang::IllegalArgumentException, + uno::RuntimeException) + { + // TODO(P3): if we know anything about target + // colorspace, this can be greatly sped up + uno::Sequence<rendering::ARGBColor> aIntermediate( + convertToARGB(deviceColor)); + return targetColorSpace->convertFromARGB(aIntermediate); + } + virtual uno::Sequence< rendering::RGBColor > SAL_CALL convertToRGB( const uno::Sequence< double >& deviceColor ) throw (lang::IllegalArgumentException, uno::RuntimeException) + { + const double* pIn( deviceColor.getConstArray() ); + const sal_Size nLen( deviceColor.getLength() ); + ENSURE_ARG_OR_THROW2(nLen%4==0, + "number of channels no multiple of 4", + static_cast<rendering::XColorSpace*>(this), 0); + + uno::Sequence< rendering::RGBColor > aRes(nLen/4); + rendering::RGBColor* pOut( aRes.getArray() ); + for( sal_Size i=0; i<nLen; i+=4 ) + { + *pOut++ = rendering::RGBColor(pIn[0],pIn[1],pIn[2]); + pIn += 4; + } + return aRes; + } + virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertToARGB( const uno::Sequence< double >& deviceColor ) throw (lang::IllegalArgumentException, uno::RuntimeException) + { + const double* pIn( deviceColor.getConstArray() ); + const sal_Size nLen( deviceColor.getLength() ); + ENSURE_ARG_OR_THROW2(nLen%4==0, + "number of channels no multiple of 4", + static_cast<rendering::XColorSpace*>(this), 0); + + uno::Sequence< rendering::ARGBColor > aRes(nLen/4); + rendering::ARGBColor* pOut( aRes.getArray() ); + for( sal_Size i=0; i<nLen; i+=4 ) + { + *pOut++ = rendering::ARGBColor(pIn[3],pIn[0],pIn[1],pIn[2]); + pIn += 4; + } + return aRes; + } + virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertToPARGB( const uno::Sequence< double >& deviceColor ) throw (lang::IllegalArgumentException, uno::RuntimeException) + { + const double* pIn( deviceColor.getConstArray() ); + const sal_Size nLen( deviceColor.getLength() ); + ENSURE_ARG_OR_THROW2(nLen%4==0, + "number of channels no multiple of 4", + static_cast<rendering::XColorSpace*>(this), 0); + + uno::Sequence< rendering::ARGBColor > aRes(nLen/4); + rendering::ARGBColor* pOut( aRes.getArray() ); + for( sal_Size i=0; i<nLen; i+=4 ) + { + *pOut++ = rendering::ARGBColor(pIn[3],pIn[3]*pIn[0],pIn[3]*pIn[1],pIn[3]*pIn[2]); + pIn += 4; + } + return aRes; + } + virtual uno::Sequence< double > SAL_CALL convertFromRGB( const uno::Sequence< rendering::RGBColor >& rgbColor ) throw (lang::IllegalArgumentException, uno::RuntimeException) + { + const rendering::RGBColor* pIn( rgbColor.getConstArray() ); + const sal_Size nLen( rgbColor.getLength() ); + + uno::Sequence< double > aRes(nLen*4); + double* pColors=aRes.getArray(); + for( sal_Size i=0; i<nLen; ++i ) + { + *pColors++ = pIn->Red; + *pColors++ = pIn->Green; + *pColors++ = pIn->Blue; + *pColors++ = 1.0; + ++pIn; + } + return aRes; + } + virtual uno::Sequence< double > SAL_CALL convertFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) throw (lang::IllegalArgumentException, uno::RuntimeException) + { + const rendering::ARGBColor* pIn( rgbColor.getConstArray() ); + const sal_Size nLen( rgbColor.getLength() ); + + uno::Sequence< double > aRes(nLen*4); + double* pColors=aRes.getArray(); + for( sal_Size i=0; i<nLen; ++i ) + { + *pColors++ = pIn->Red; + *pColors++ = pIn->Green; + *pColors++ = pIn->Blue; + *pColors++ = pIn->Alpha; + ++pIn; + } + return aRes; + } + virtual uno::Sequence< double > SAL_CALL convertFromPARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) throw (lang::IllegalArgumentException, uno::RuntimeException) + { + const rendering::ARGBColor* pIn( rgbColor.getConstArray() ); + const sal_Size nLen( rgbColor.getLength() ); + + uno::Sequence< double > aRes(nLen*4); + double* pColors=aRes.getArray(); + for( sal_Size i=0; i<nLen; ++i ) + { + *pColors++ = pIn->Red/pIn->Alpha; + *pColors++ = pIn->Green/pIn->Alpha; + *pColors++ = pIn->Blue/pIn->Alpha; + *pColors++ = pIn->Alpha; + ++pIn; + } + return aRes; + } + + // XIntegerBitmapColorSpace + virtual ::sal_Int32 SAL_CALL getBitsPerPixel( ) throw (uno::RuntimeException) + { + return 32; + } + virtual uno::Sequence< ::sal_Int32 > SAL_CALL getComponentBitCounts( ) throw (uno::RuntimeException) + { + return maBitCounts; + } + virtual ::sal_Int8 SAL_CALL getEndianness( ) throw (uno::RuntimeException) + { + return util::Endianness::LITTLE; + } + virtual uno::Sequence<double> SAL_CALL convertFromIntegerColorSpace( const uno::Sequence< ::sal_Int8 >& deviceColor, + const uno::Reference< rendering::XColorSpace >& targetColorSpace ) throw (lang::IllegalArgumentException, + uno::RuntimeException) + { + if( dynamic_cast<StandardColorSpace*>(targetColorSpace.get()) ) + { + const sal_Int8* pIn( deviceColor.getConstArray() ); + const sal_Size nLen( deviceColor.getLength() ); + ENSURE_ARG_OR_THROW2(nLen%4==0, + "number of channels no multiple of 4", + static_cast<rendering::XColorSpace*>(this), 0); + + uno::Sequence<double> aRes(nLen); + double* pOut( aRes.getArray() ); + for( sal_Size i=0; i<nLen; i+=4 ) + { + *pOut++ = vcl::unotools::toDoubleColor(*pIn++); + *pOut++ = vcl::unotools::toDoubleColor(*pIn++); + *pOut++ = vcl::unotools::toDoubleColor(*pIn++); + *pOut++ = vcl::unotools::toDoubleColor(255-*pIn++); + } + return aRes; + } + else + { + // TODO(P3): if we know anything about target + // colorspace, this can be greatly sped up + uno::Sequence<rendering::ARGBColor> aIntermediate( + convertIntegerToARGB(deviceColor)); + return targetColorSpace->convertFromARGB(aIntermediate); + } + } + virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertToIntegerColorSpace( const uno::Sequence< ::sal_Int8 >& deviceColor, + const uno::Reference< rendering::XIntegerBitmapColorSpace >& targetColorSpace ) throw (lang::IllegalArgumentException, + uno::RuntimeException) + { + if( dynamic_cast<StandardColorSpace*>(targetColorSpace.get()) ) + { + // it's us, so simply pass-through the data + return deviceColor; + } + else + { + // TODO(P3): if we know anything about target + // colorspace, this can be greatly sped up + uno::Sequence<rendering::ARGBColor> aIntermediate( + convertIntegerToARGB(deviceColor)); + return targetColorSpace->convertIntegerFromARGB(aIntermediate); + } + } + virtual uno::Sequence< rendering::RGBColor > SAL_CALL convertIntegerToRGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) throw (lang::IllegalArgumentException, uno::RuntimeException) + { + const sal_Int8* pIn( deviceColor.getConstArray() ); + const sal_Size nLen( deviceColor.getLength() ); + ENSURE_ARG_OR_THROW2(nLen%4==0, + "number of channels no multiple of 4", + static_cast<rendering::XColorSpace*>(this), 0); + + uno::Sequence< rendering::RGBColor > aRes(nLen/4); + rendering::RGBColor* pOut( aRes.getArray() ); + for( sal_Size i=0; i<nLen; i+=4 ) + { + *pOut++ = rendering::RGBColor( + vcl::unotools::toDoubleColor(pIn[0]), + vcl::unotools::toDoubleColor(pIn[1]), + vcl::unotools::toDoubleColor(pIn[2])); + pIn += 4; + } + return aRes; + } + + virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertIntegerToARGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) throw (lang::IllegalArgumentException, uno::RuntimeException) + { + const sal_Int8* pIn( deviceColor.getConstArray() ); + const sal_Size nLen( deviceColor.getLength() ); + ENSURE_ARG_OR_THROW2(nLen%4==0, + "number of channels no multiple of 4", + static_cast<rendering::XColorSpace*>(this), 0); + + uno::Sequence< rendering::ARGBColor > aRes(nLen/4); + rendering::ARGBColor* pOut( aRes.getArray() ); + for( sal_Size i=0; i<nLen; i+=4 ) + { + *pOut++ = rendering::ARGBColor( + vcl::unotools::toDoubleColor(255-pIn[3]), + vcl::unotools::toDoubleColor(pIn[0]), + vcl::unotools::toDoubleColor(pIn[1]), + vcl::unotools::toDoubleColor(pIn[2])); + pIn += 4; + } + return aRes; + } + + virtual uno::Sequence< rendering::ARGBColor > SAL_CALL convertIntegerToPARGB( const uno::Sequence< ::sal_Int8 >& deviceColor ) throw (lang::IllegalArgumentException, uno::RuntimeException) + { + const sal_Int8* pIn( deviceColor.getConstArray() ); + const sal_Size nLen( deviceColor.getLength() ); + ENSURE_ARG_OR_THROW2(nLen%4==0, + "number of channels no multiple of 4", + static_cast<rendering::XColorSpace*>(this), 0); + + uno::Sequence< rendering::ARGBColor > aRes(nLen/4); + rendering::ARGBColor* pOut( aRes.getArray() ); + for( sal_Size i=0; i<nLen; i+=4 ) + { + const sal_Int8 nAlpha( 255-pIn[3] ); + *pOut++ = rendering::ARGBColor( + vcl::unotools::toDoubleColor(nAlpha), + vcl::unotools::toDoubleColor(nAlpha*pIn[0]), + vcl::unotools::toDoubleColor(nAlpha*pIn[1]), + vcl::unotools::toDoubleColor(nAlpha*pIn[2])); + pIn += 4; + } + return aRes; + } + + virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromRGB( const uno::Sequence< rendering::RGBColor >& rgbColor ) throw (lang::IllegalArgumentException, uno::RuntimeException) + { + const rendering::RGBColor* pIn( rgbColor.getConstArray() ); + const sal_Size nLen( rgbColor.getLength() ); + + uno::Sequence< sal_Int8 > aRes(nLen*4); + sal_Int8* pColors=aRes.getArray(); + for( sal_Size i=0; i<nLen; ++i ) + { + *pColors++ = vcl::unotools::toByteColor(pIn->Red); + *pColors++ = vcl::unotools::toByteColor(pIn->Green); + *pColors++ = vcl::unotools::toByteColor(pIn->Blue); + *pColors++ = 0; + ++pIn; + } + return aRes; + } + + virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) throw (lang::IllegalArgumentException, uno::RuntimeException) + { + const rendering::ARGBColor* pIn( rgbColor.getConstArray() ); + const sal_Size nLen( rgbColor.getLength() ); + + uno::Sequence< sal_Int8 > aRes(nLen*4); + sal_Int8* pColors=aRes.getArray(); + for( sal_Size i=0; i<nLen; ++i ) + { + *pColors++ = vcl::unotools::toByteColor(pIn->Red); + *pColors++ = vcl::unotools::toByteColor(pIn->Green); + *pColors++ = vcl::unotools::toByteColor(pIn->Blue); + *pColors++ = 255-vcl::unotools::toByteColor(pIn->Alpha); + ++pIn; + } + return aRes; + } + + virtual uno::Sequence< ::sal_Int8 > SAL_CALL convertIntegerFromPARGB( const uno::Sequence< rendering::ARGBColor >& rgbColor ) throw (lang::IllegalArgumentException, uno::RuntimeException) + { + const rendering::ARGBColor* pIn( rgbColor.getConstArray() ); + const sal_Size nLen( rgbColor.getLength() ); + + uno::Sequence< sal_Int8 > aRes(nLen*4); + sal_Int8* pColors=aRes.getArray(); + for( sal_Size i=0; i<nLen; ++i ) + { + *pColors++ = vcl::unotools::toByteColor(pIn->Red/pIn->Alpha); + *pColors++ = vcl::unotools::toByteColor(pIn->Green/pIn->Alpha); + *pColors++ = vcl::unotools::toByteColor(pIn->Blue/pIn->Alpha); + *pColors++ = 255-vcl::unotools::toByteColor(pIn->Alpha); + ++pIn; + } + return aRes; + } + + public: + StandardColorSpace() : + maComponentTags(4), + maBitCounts(4) + { + sal_Int8* pTags = maComponentTags.getArray(); + sal_Int32* pBitCounts = maBitCounts.getArray(); + pTags[0] = rendering::ColorComponentTag::RGB_RED; + pTags[1] = rendering::ColorComponentTag::RGB_GREEN; + pTags[2] = rendering::ColorComponentTag::RGB_BLUE; + pTags[3] = rendering::ColorComponentTag::ALPHA; + + pBitCounts[0] = + pBitCounts[1] = + pBitCounts[2] = + pBitCounts[3] = 8; + } + }; + + struct StandardColorSpaceHolder : public rtl::StaticWithInit<uno::Reference<rendering::XIntegerBitmapColorSpace>, + StandardColorSpaceHolder> + { + uno::Reference<rendering::XIntegerBitmapColorSpace> operator()() + { + return new StandardColorSpace(); + } + }; + } + + uno::Reference<rendering::XIntegerBitmapColorSpace> getStdColorSpace() + { + return StandardColorSpaceHolder::get(); + } + + rendering::IntegerBitmapLayout getStdMemoryLayout( const geometry::IntegerSize2D& rBmpSize ) + { + rendering::IntegerBitmapLayout aLayout; + + aLayout.ScanLines = rBmpSize.Height; + aLayout.ScanLineBytes = rBmpSize.Width*4; + aLayout.ScanLineStride = aLayout.ScanLineBytes; + aLayout.PlaneStride = 0; + aLayout.ColorSpace = getStdColorSpace(); + aLayout.Palette.clear(); + aLayout.IsMsbFirst = sal_False; + + return aLayout; + } + + ::Color stdIntSequenceToColor( const uno::Sequence<sal_Int8>& rColor ) + { +#ifdef OSL_BIGENDIAN + const sal_Int8* pCols( rColor.getConstArray() ); + return ::Color( pCols[3], pCols[0], pCols[1], pCols[2] ); +#else + return ::Color( *reinterpret_cast< const ::ColorData* >(rColor.getConstArray()) ); +#endif + } + + uno::Sequence<sal_Int8> colorToStdIntSequence( const ::Color& rColor ) + { + uno::Sequence<sal_Int8> aRet(4); + sal_Int8* pCols( aRet.getArray() ); +#ifdef OSL_BIGENDIAN + pCols[0] = rColor.GetRed(); + pCols[1] = rColor.GetGreen(); + pCols[2] = rColor.GetBlue(); + pCols[3] = 255-rColor.GetTransparency(); +#else + *reinterpret_cast<sal_Int32*>(pCols) = rColor.GetColor(); +#endif + return aRet; + } + + // Create a corrected view transformation out of the give one, + // which ensures that the rectangle given by (0,0) and + // rSpriteSize is mapped with its left,top corner to (0,0) + // again. This is required to properly render sprite + // animations to buffer bitmaps. + ::basegfx::B2DHomMatrix& calcRectToOriginTransform( ::basegfx::B2DHomMatrix& o_transform, + const ::basegfx::B2DRange& i_srcRect, + const ::basegfx::B2DHomMatrix& i_transformation ) + { + if( i_srcRect.isEmpty() ) + return o_transform=i_transformation; + + // transform by given transformation + ::basegfx::B2DRectangle aTransformedRect; + + calcTransformedRectBounds( aTransformedRect, + i_srcRect, + i_transformation ); + + // now move resulting left,top point of bounds to (0,0) + const basegfx::B2DHomMatrix aCorrectedTransform(basegfx::tools::createTranslateB2DHomMatrix( + -aTransformedRect.getMinX(), -aTransformedRect.getMinY())); + + // prepend to original transformation + o_transform = aCorrectedTransform * i_transformation; + + return o_transform; + } + + ::basegfx::B2DRange& calcTransformedRectBounds( ::basegfx::B2DRange& outRect, + const ::basegfx::B2DRange& inRect, + const ::basegfx::B2DHomMatrix& transformation ) + { + outRect.reset(); + + if( inRect.isEmpty() ) + return outRect; + + // transform all four extremal points of the rectangle, + // take bounding rect of those. + + // transform left-top point + outRect.expand( transformation * inRect.getMinimum() ); + + // transform bottom-right point + outRect.expand( transformation * inRect.getMaximum() ); + + ::basegfx::B2DPoint aPoint; + + // transform top-right point + aPoint.setX( inRect.getMaxX() ); + aPoint.setY( inRect.getMinY() ); + + aPoint *= transformation; + outRect.expand( aPoint ); + + // transform bottom-left point + aPoint.setX( inRect.getMinX() ); + aPoint.setY( inRect.getMaxY() ); + + aPoint *= transformation; + outRect.expand( aPoint ); + + // over and out. + return outRect; + } + + ::basegfx::B2DHomMatrix& calcRectToRectTransform( ::basegfx::B2DHomMatrix& o_transform, + const ::basegfx::B2DRange& destRect, + const ::basegfx::B2DRange& srcRect, + const ::basegfx::B2DHomMatrix& transformation ) + { + if( srcRect.isEmpty() || + destRect.isEmpty() ) + { + return o_transform=transformation; + } + + // transform inputRect by transformation + ::basegfx::B2DRectangle aTransformedRect; + calcTransformedRectBounds( aTransformedRect, + srcRect, + transformation ); + + // now move resulting left,top point of bounds to (0,0) + basegfx::B2DHomMatrix aCorrectedTransform(basegfx::tools::createTranslateB2DHomMatrix( + -aTransformedRect.getMinX(), -aTransformedRect.getMinY())); + + // scale to match outRect + const double xDenom( aTransformedRect.getWidth() ); + const double yDenom( aTransformedRect.getHeight() ); + if( xDenom != 0.0 && yDenom != 0.0 ) + aCorrectedTransform.scale( destRect.getWidth() / xDenom, + destRect.getHeight() / yDenom ); + // TODO(E2): error handling + + // translate to final position + aCorrectedTransform.translate( destRect.getMinX(), + destRect.getMinY() ); + + ::basegfx::B2DHomMatrix transform( transformation ); + o_transform = aCorrectedTransform * transform; + + return o_transform; + } + + bool isInside( const ::basegfx::B2DRange& rContainedRect, + const ::basegfx::B2DRange& rTransformRect, + const ::basegfx::B2DHomMatrix& rTransformation ) + { + if( rContainedRect.isEmpty() || rTransformRect.isEmpty() ) + return false; + + ::basegfx::B2DPolygon aPoly( + ::basegfx::tools::createPolygonFromRect( rTransformRect ) ); + aPoly.transform( rTransformation ); + + return ::basegfx::tools::isInside( aPoly, + ::basegfx::tools::createPolygonFromRect( + rContainedRect ), + true ); + } + + namespace + { + bool clipAreaImpl( ::basegfx::B2IRange* o_pDestArea, + ::basegfx::B2IRange& io_rSourceArea, + ::basegfx::B2IPoint& io_rDestPoint, + const ::basegfx::B2IRange& rSourceBounds, + const ::basegfx::B2IRange& rDestBounds ) + { + const ::basegfx::B2IPoint aSourceTopLeft( + io_rSourceArea.getMinimum() ); + + ::basegfx::B2IRange aLocalSourceArea( io_rSourceArea ); + + // clip source area (which must be inside rSourceBounds) + aLocalSourceArea.intersect( rSourceBounds ); + + if( aLocalSourceArea.isEmpty() ) + return false; + + // calc relative new source area points (relative to orig + // source area) + const ::basegfx::B2IVector aUpperLeftOffset( + aLocalSourceArea.getMinimum()-aSourceTopLeft ); + const ::basegfx::B2IVector aLowerRightOffset( + aLocalSourceArea.getMaximum()-aSourceTopLeft ); + + ::basegfx::B2IRange aLocalDestArea( io_rDestPoint + aUpperLeftOffset, + io_rDestPoint + aLowerRightOffset ); + + // clip dest area (which must be inside rDestBounds) + aLocalDestArea.intersect( rDestBounds ); + + if( aLocalDestArea.isEmpty() ) + return false; + + // calc relative new dest area points (relative to orig + // source area) + const ::basegfx::B2IVector aDestUpperLeftOffset( + aLocalDestArea.getMinimum()-io_rDestPoint ); + const ::basegfx::B2IVector aDestLowerRightOffset( + aLocalDestArea.getMaximum()-io_rDestPoint ); + + io_rSourceArea = ::basegfx::B2IRange( aSourceTopLeft + aDestUpperLeftOffset, + aSourceTopLeft + aDestLowerRightOffset ); + io_rDestPoint = aLocalDestArea.getMinimum(); + + if( o_pDestArea ) + *o_pDestArea = aLocalDestArea; + + return true; + } + } + + bool clipScrollArea( ::basegfx::B2IRange& io_rSourceArea, + ::basegfx::B2IPoint& io_rDestPoint, + ::std::vector< ::basegfx::B2IRange >& o_ClippedAreas, + const ::basegfx::B2IRange& rBounds ) + { + ::basegfx::B2IRange aResultingDestArea; + + // compute full destination area (to determine uninitialized + // areas below) + const ::basegfx::B2I64Tuple& rRange( io_rSourceArea.getRange() ); + ::basegfx::B2IRange aInputDestArea( io_rDestPoint.getX(), + io_rDestPoint.getY(), + (io_rDestPoint.getX() + + static_cast<sal_Int32>(rRange.getX())), + (io_rDestPoint.getY() + + static_cast<sal_Int32>(rRange.getY())) ); + // limit to output area (no point updating outside of it) + aInputDestArea.intersect( rBounds ); + + // clip to rBounds + if( !clipAreaImpl( &aResultingDestArea, + io_rSourceArea, + io_rDestPoint, + rBounds, + rBounds ) ) + return false; + + // finally, compute all areas clipped off the total + // destination area. + ::basegfx::computeSetDifference( o_ClippedAreas, + aInputDestArea, + aResultingDestArea ); + + return true; + } + + bool clipBlit( ::basegfx::B2IRange& io_rSourceArea, + ::basegfx::B2IPoint& io_rDestPoint, + const ::basegfx::B2IRange& rSourceBounds, + const ::basegfx::B2IRange& rDestBounds ) + { + return clipAreaImpl( NULL, + io_rSourceArea, + io_rDestPoint, + rSourceBounds, + rDestBounds ); + } + + ::basegfx::B2IRange spritePixelAreaFromB2DRange( const ::basegfx::B2DRange& rRange ) + { + if( rRange.isEmpty() ) + return ::basegfx::B2IRange(); + + const ::basegfx::B2IPoint aTopLeft( ::basegfx::fround( rRange.getMinX() ), + ::basegfx::fround( rRange.getMinY() ) ); + return ::basegfx::B2IRange( aTopLeft, + aTopLeft + ::basegfx::B2IPoint( + ::basegfx::fround( rRange.getWidth() ), + ::basegfx::fround( rRange.getHeight() ) ) ); + } + + uno::Sequence< uno::Any >& getDeviceInfo( const uno::Reference< rendering::XCanvas >& i_rxCanvas, + uno::Sequence< uno::Any >& o_rxParams ) + { + o_rxParams.realloc( 0 ); + + if( i_rxCanvas.is() ) + { + try + { + uno::Reference< rendering::XGraphicDevice > xDevice( i_rxCanvas->getDevice(), + uno::UNO_QUERY_THROW ); + + uno::Reference< lang::XServiceInfo > xServiceInfo( xDevice, + uno::UNO_QUERY_THROW ); + uno::Reference< beans::XPropertySet > xPropSet( xDevice, + uno::UNO_QUERY_THROW ); + + o_rxParams.realloc( 2 ); + + o_rxParams[ 0 ] = uno::makeAny( xServiceInfo->getImplementationName() ); + o_rxParams[ 1 ] = uno::makeAny( xPropSet->getPropertyValue( + ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("DeviceHandle") ) ) ); + } + catch( uno::Exception& ) + { + // ignore, but return empty sequence + } + } + + return o_rxParams; + } + + awt::Rectangle getAbsoluteWindowRect( const awt::Rectangle& rRect, + const uno::Reference< awt::XWindow2 >& xWin ) + { + awt::Rectangle aRetVal( rRect ); + + ::Window* pWindow = VCLUnoHelper::GetWindow(xWin); + if( pWindow ) + { + ::Point aPoint( aRetVal.X, + aRetVal.Y ); + + aPoint = pWindow->OutputToScreenPixel( aPoint ); + + aRetVal.X = aPoint.X(); + aRetVal.Y = aPoint.Y(); + } + + return aRetVal; + } + + ::basegfx::B2DPolyPolygon getBoundMarksPolyPolygon( const ::basegfx::B2DRange& rRange ) + { + ::basegfx::B2DPolyPolygon aPolyPoly; + ::basegfx::B2DPolygon aPoly; + + const double nX0( rRange.getMinX() ); + const double nY0( rRange.getMinY() ); + const double nX1( rRange.getMaxX() ); + const double nY1( rRange.getMaxY() ); + + aPoly.append( ::basegfx::B2DPoint( nX0+4, + nY0 ) ); + aPoly.append( ::basegfx::B2DPoint( nX0, + nY0 ) ); + aPoly.append( ::basegfx::B2DPoint( nX0, + nY0+4 ) ); + aPolyPoly.append( aPoly ); aPoly.clear(); + + aPoly.append( ::basegfx::B2DPoint( nX1-4, + nY0 ) ); + aPoly.append( ::basegfx::B2DPoint( nX1, + nY0 ) ); + aPoly.append( ::basegfx::B2DPoint( nX1, + nY0+4 ) ); + aPolyPoly.append( aPoly ); aPoly.clear(); + + aPoly.append( ::basegfx::B2DPoint( nX0+4, + nY1 ) ); + aPoly.append( ::basegfx::B2DPoint( nX0, + nY1 ) ); + aPoly.append( ::basegfx::B2DPoint( nX0, + nY1-4 ) ); + aPolyPoly.append( aPoly ); aPoly.clear(); + + aPoly.append( ::basegfx::B2DPoint( nX1-4, + nY1 ) ); + aPoly.append( ::basegfx::B2DPoint( nX1, + nY1 ) ); + aPoly.append( ::basegfx::B2DPoint( nX1, + nY1-4 ) ); + aPolyPoly.append( aPoly ); + + return aPolyPoly; + } + + int calcGradientStepCount( ::basegfx::B2DHomMatrix& rTotalTransform, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState, + const rendering::Texture& texture, + int nColorSteps ) + { + // calculate overall texture transformation (directly from + // texture to device space). + ::basegfx::B2DHomMatrix aMatrix; + + rTotalTransform.identity(); + ::basegfx::unotools::homMatrixFromAffineMatrix( rTotalTransform, + texture.AffineTransform ); + ::canvas::tools::mergeViewAndRenderTransform(aMatrix, + viewState, + renderState); + rTotalTransform *= aMatrix; // prepend total view/render transformation + + // determine size of gradient in device coordinate system + // (to e.g. determine sensible number of gradient steps) + ::basegfx::B2DPoint aLeftTop( 0.0, 0.0 ); + ::basegfx::B2DPoint aLeftBottom( 0.0, 1.0 ); + ::basegfx::B2DPoint aRightTop( 1.0, 0.0 ); + ::basegfx::B2DPoint aRightBottom( 1.0, 1.0 ); + + aLeftTop *= rTotalTransform; + aLeftBottom *= rTotalTransform; + aRightTop *= rTotalTransform; + aRightBottom*= rTotalTransform; + + // longest line in gradient bound rect + const int nGradientSize( + static_cast<int>( + ::std::max( + ::basegfx::B2DVector(aRightBottom-aLeftTop).getLength(), + ::basegfx::B2DVector(aRightTop-aLeftBottom).getLength() ) + 1.0 ) ); + + // typical number for pixel of the same color (strip size) + const int nStripSize( nGradientSize < 50 ? 2 : 4 ); + + // use at least three steps, and at utmost the number of color + // steps + return ::std::max( 3, + ::std::min( + nGradientSize / nStripSize, + nColorSteps ) ); + } + + } // namespace tools + +} // namespace canvas + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/tools/canvastools.flt b/canvas/source/tools/canvastools.flt new file mode 100644 index 000000000000..67e713474bf1 --- /dev/null +++ b/canvas/source/tools/canvastools.flt @@ -0,0 +1,3 @@ +__CT +__real +internal diff --git a/canvas/source/tools/elapsedtime.cxx b/canvas/source/tools/elapsedtime.cxx new file mode 100644 index 000000000000..f4f6e5db8a67 --- /dev/null +++ b/canvas/source/tools/elapsedtime.cxx @@ -0,0 +1,227 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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_canvas.hxx" + +#include "osl/time.h" +#include "osl/diagnose.h" +#include "canvas/elapsedtime.hxx" + +#if defined(WIN) || defined(WNT) + +#if defined _MSC_VER +#pragma warning(push,1) +#endif + +// TEMP!!! +// Awaiting corresponding functionality in OSL +// +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <winbase.h> +#include <mmsystem.h> +#endif + +#if defined _MSC_VER +#pragma warning(pop) +#endif + +#include <algorithm> +#include <limits> + +namespace canvas { +namespace tools { + + +#if defined(WIN) || defined(WNT) +// TODO(Q2): is 0 okay for the failure case here? +double ElapsedTime::getSystemTime() +{ + // TEMP!!! + // Awaiting corresponding functionality in OSL + // + + // is there a performance counter available? + static bool bTimeSetupDone( false ); + static bool bPerfTimerAvailable( false ); + static LONGLONG nPerfCountFreq; + + // TODO(F1): This _might_ cause problems, as it prevents correct + // time handling for very long lifetimes of this class's + // surrounding component in memory. When the difference between + // current sys time and nInitialCount exceeds IEEE double's + // mantissa, time will start to run jerky. + static LONGLONG nInitialCount; + + if( !bTimeSetupDone ) + { + if( QueryPerformanceFrequency( + reinterpret_cast<LARGE_INTEGER *>(&nPerfCountFreq) ) ) + { + // read initial time: + QueryPerformanceCounter( + reinterpret_cast<LARGE_INTEGER *>(&nInitialCount) ); + bPerfTimerAvailable = true; + } + bTimeSetupDone = true; + } + + if( bPerfTimerAvailable ) + { + LONGLONG nCurrCount; + QueryPerformanceCounter( + reinterpret_cast<LARGE_INTEGER *>(&nCurrCount) ); + nCurrCount -= nInitialCount; + return double(nCurrCount) / nPerfCountFreq; + } + else + { + LONGLONG nCurrTime = timeGetTime(); + return double(nCurrTime) / 1000.0; + } +} + +#else // ! WNT + +// TODO(Q2): is 0 okay for the failure case here? +double ElapsedTime::getSystemTime() +{ + TimeValue aTimeVal; + if( osl_getSystemTime( &aTimeVal ) ) + return ((aTimeVal.Nanosec * 10e-10) + aTimeVal.Seconds); + else + return 0.0; +} + +#endif + +ElapsedTime::ElapsedTime() + : m_pTimeBase(), + m_fLastQueriedTime( 0.0 ), + m_fStartTime( getSystemTime() ), + m_fFrozenTime( 0.0 ), + m_bInPauseMode( false ), + m_bInHoldMode( false ) +{ +} + +ElapsedTime::ElapsedTime( + boost::shared_ptr<ElapsedTime> const & pTimeBase ) + : m_pTimeBase( pTimeBase ), + m_fLastQueriedTime( 0.0 ), + m_fStartTime( getCurrentTime() ), + m_fFrozenTime( 0.0 ), + m_bInPauseMode( false ), + m_bInHoldMode( false ) +{ +} + +boost::shared_ptr<ElapsedTime> const & ElapsedTime::getTimeBase() const +{ + return m_pTimeBase; +} + +void ElapsedTime::reset() +{ + m_fLastQueriedTime = 0.0; + m_fStartTime = getCurrentTime(); + m_fFrozenTime = 0.0; + m_bInPauseMode = false; + m_bInHoldMode = false; +} + +void ElapsedTime::adjustTimer( double fOffset, bool /*bLimitToLastQueriedTime*/ ) +{ + // to make getElapsedTime() become _larger_, have to reduce + // m_fStartTime. + m_fStartTime -= fOffset; + + // also adjust frozen time, this method must _always_ affect the + // value returned by getElapsedTime()! + if (m_bInHoldMode || m_bInPauseMode) + m_fFrozenTime += fOffset; +} + +double ElapsedTime::getCurrentTime() const +{ + return m_pTimeBase.get() == 0 + ? getSystemTime() : m_pTimeBase->getElapsedTimeImpl(); +} + +double ElapsedTime::getElapsedTime() const +{ + m_fLastQueriedTime = getElapsedTimeImpl(); + return m_fLastQueriedTime; +} + +double ElapsedTime::getElapsedTimeImpl() const +{ + if (m_bInHoldMode || m_bInPauseMode) + return m_fFrozenTime; + + return getCurrentTime() - m_fStartTime; +} + +void ElapsedTime::pauseTimer() +{ + m_fFrozenTime = getElapsedTimeImpl(); + m_bInPauseMode = true; +} + +void ElapsedTime::continueTimer() +{ + m_bInPauseMode = false; + + // stop pausing, time runs again. Note that + // getElapsedTimeImpl() honors hold mode, i.e. a + // continueTimer() in hold mode will preserve the latter + const double fPauseDuration( getElapsedTimeImpl() - m_fFrozenTime ); + + // adjust start time, such that subsequent getElapsedTime() calls + // will virtually start from m_fFrozenTime. + m_fStartTime += fPauseDuration; +} + +void ElapsedTime::holdTimer() +{ + // when called during hold mode (e.g. more than once per time + // object), the original hold time will be maintained. + m_fFrozenTime = getElapsedTimeImpl(); + m_bInHoldMode = true; +} + +void ElapsedTime::releaseTimer() +{ + m_bInHoldMode = false; +} + +} // namespace tools +} // namespace canvas + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/tools/imagecachedprimitive.hxx b/canvas/source/tools/imagecachedprimitive.hxx new file mode 100644 index 000000000000..23cab7ce83f8 --- /dev/null +++ b/canvas/source/tools/imagecachedprimitive.hxx @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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. + * + ************************************************************************/ + +#ifndef INCLUDED_CANVAS_IMAGECACHEDPRIMITIVE_HXX +#define INCLUDED_CANVAS_IMAGECACHEDPRIMITIVE_HXX + +#include <canvas/rendering/icachedprimitive.hxx> + +#include <boost/shared_ptr.hpp> + + +namespace canvas +{ + /** Objects with this interface are returned from every Image + render operation. + + These objects can be used to implement the + rendering::XCachedPrimitive interface, which in turn caches + render state and objects to facilitate quick redraws. + + Derived from ICachedPrimitive, to add the setImage() method + (which, strictly speaking, is a technicality, because Image + cannot create objects with a shared_ptr to itself). + */ + struct ImageCachedPrimitive : public ICachedPrimitive + { + virtual void setImage( const ::boost::shared_ptr< class Image >& rTargetImage ) = 0; + }; + + typedef ::boost::shared_ptr< ImageCachedPrimitive > ImageCachedPrimitiveSharedPtr; +} + +#endif /* INCLUDED_CANVAS_IMAGECACHEDPRIMITIVE_HXX */ + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/tools/makefile.mk b/canvas/source/tools/makefile.mk new file mode 100644 index 000000000000..d51053d1d491 --- /dev/null +++ b/canvas/source/tools/makefile.mk @@ -0,0 +1,91 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2000, 2010 Oracle and/or its affiliates. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# 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. +# +#************************************************************************* + +PRJ=..$/.. + +PRJNAME=canvas +TARGET=canvastools +ENABLE_EXCEPTIONS=TRUE + +# --- Settings ----------------------------------------------------------- + +.INCLUDE : settings.mk + +# --- Common ---------------------------------------------------------- + +.IF "$(verbose)"!="" || "$(VERBOSE)"!="" +CDEFS+= -DVERBOSE +.ENDIF +.IF "$(profiler)"!="" || "$(PROFILER)"!="" +CDEFS+= -DPROFILER +.ENDIF + +#CFLAGS +:= /Ox /Ot # THIS IS IMPORTANT + + +.IF "$(L10N_framework)"=="" +SLOFILES = \ + $(SLO)$/cachedprimitivebase.obj \ + $(SLO)$/canvascustomspritehelper.obj \ + $(SLO)$/canvastools.obj \ + $(SLO)$/elapsedtime.obj \ + $(SLO)$/parametricpolypolygon.obj \ + $(SLO)$/prioritybooster.obj \ + $(SLO)$/propertysethelper.obj \ + $(SLO)$/spriteredrawmanager.obj \ + $(SLO)$/surface.obj \ + $(SLO)$/surfaceproxy.obj \ + $(SLO)$/surfaceproxymanager.obj \ + $(SLO)$/pagemanager.obj \ + $(SLO)$/page.obj \ + $(SLO)$/verifyinput.obj + +SHL1TARGET= $(TARGET)$(DLLPOSTFIX) +SHL1IMPLIB= i$(TARGET) +SHL1STDLIBS= $(SALLIB) $(CPPULIB) $(BASEGFXLIB) $(CPPUHELPERLIB) $(COMPHELPERLIB) $(VCLLIB) $(TKLIB) $(TOOLSLIB) + +SHL1LIBS= $(SLB)$/$(TARGET).lib + +SHL1DEF= $(MISC)$/$(SHL1TARGET).def +DEF1NAME =$(SHL1TARGET) +DEF1DEPN =$(MISC)$/$(SHL1TARGET).flt \ + $(LIB1TARGET) + +DEF1DES =Canvastools +DEFLIB1NAME =$(TARGET) + +.IF "$(GUI)" == "WNT" +SHL1STDLIBS += $(WINMMLIB) $(KERNEL32LIB) +.ENDIF +.ENDIF + +# ========================================================================== + +.INCLUDE : target.mk + +$(MISC)$/$(SHL1TARGET).flt : makefile.mk $(TARGET).flt + @$(TYPE) $(TARGET).flt > $@ diff --git a/canvas/source/tools/page.cxx b/canvas/source/tools/page.cxx new file mode 100644 index 000000000000..7d85e419782a --- /dev/null +++ b/canvas/source/tools/page.cxx @@ -0,0 +1,155 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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_canvas.hxx" + +#include <boost/bind.hpp> +#include "page.hxx" + +namespace canvas +{ + Page::Page( const IRenderModuleSharedPtr &rRenderModule ) : + mpRenderModule(rRenderModule), + mpSurface(rRenderModule->createSurface(::basegfx::B2ISize())) + { + } + + void Page::validate() + { + if(!(isValid())) + { + ::std::for_each( mpFragments.begin(), + mpFragments.end(), + ::boost::mem_fn(&PageFragment::refresh)); + } + } + + bool Page::isValid() const + { + return mpSurface && mpSurface->isValid(); + } + + FragmentSharedPtr Page::allocateSpace( const ::basegfx::B2ISize& rSize ) + { + SurfaceRect rect(rSize); + if(insert(rect)) + { + FragmentSharedPtr pFragment(new PageFragment(rect,this)); + mpFragments.push_back(pFragment); + return pFragment; + } + + return FragmentSharedPtr(); + } + + bool Page::nakedFragment( const FragmentSharedPtr& pFragment ) + { + SurfaceRect rect(pFragment->getSize()); + if(insert(rect)) + { + pFragment->setPage(this); + mpFragments.push_back(pFragment); + return true; + } + + return false; + } + + void Page::free( const FragmentSharedPtr& pFragment ) + { + // the fragment passes as argument is no longer + // dedicated to this page. either it is about to + // be relocated to some other page or it will + // currently be deleted. in either case, simply + // remove the reference from our internal storage. + FragmentContainer_t::iterator it( + std::remove( + mpFragments.begin(),mpFragments.end(),pFragment)); + mpFragments.erase(it,mpFragments.end()); + } + + bool Page::insert( SurfaceRect& r ) + { + const FragmentContainer_t::const_iterator aEnd(mpFragments.end()); + FragmentContainer_t::const_iterator it(mpFragments.begin()); + while(it != aEnd) + { + const SurfaceRect &rect = (*it)->getRect(); + const sal_Int32 x = rect.maPos.getX(); + const sal_Int32 y = rect.maPos.getY(); + // to avoid interpolation artifacts from other textures, + // one pixel gap between them + const sal_Int32 w = rect.maSize.getX()+1; + const sal_Int32 h = rect.maSize.getY()+1; + + // probe location to the right + r.maPos.setX(x+w); + r.maPos.setY(y); + if(isValidLocation(r)) + return true; + + // probe location at bottom + r.maPos.setX(x); + r.maPos.setY(y+h); + if(isValidLocation(r)) + return true; + + ++it; + } + + r.maPos.setX(0); + r.maPos.setY(0); + + return isValidLocation(r); + } + + bool Page::isValidLocation( const SurfaceRect& r ) const + { + // the rectangle passed as argument has a valid + // location if and only if there's no intersection + // with existing areas. + SurfaceRect aBoundary(mpRenderModule->getPageSize()-basegfx::B2IVector(1,1)); + if( !r.inside(aBoundary) ) + return false; + + const FragmentContainer_t::const_iterator aEnd(mpFragments.end()); + FragmentContainer_t::const_iterator it(mpFragments.begin()); + while(it != aEnd) + { + if(r.intersection((*it)->getRect())) + return false; + + ++it; + } + + return true; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/tools/page.hxx b/canvas/source/tools/page.hxx new file mode 100644 index 000000000000..bef9684da594 --- /dev/null +++ b/canvas/source/tools/page.hxx @@ -0,0 +1,160 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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. + * + ************************************************************************/ + +#ifndef INCLUDED_CANVAS_PAGE_HXX +#define INCLUDED_CANVAS_PAGE_HXX + +#include <basegfx/vector/b2isize.hxx> +#include <basegfx/range/b2irectangle.hxx> +#include <canvas/rendering/icolorbuffer.hxx> +#include <canvas/rendering/irendermodule.hxx> +#include <canvas/rendering/isurface.hxx> + +#include <list> +#include <vector> +#include <boost/shared_ptr.hpp> +#include "surfacerect.hxx" + +namespace canvas +{ + class PageFragment; + + typedef ::boost::shared_ptr< PageFragment > FragmentSharedPtr; + + /** One page of IRenderModule-provided texture space + */ + class Page + { + public: + Page( const IRenderModuleSharedPtr& rRenderModule ); + + FragmentSharedPtr allocateSpace( const ::basegfx::B2ISize& rSize ); + bool nakedFragment( const FragmentSharedPtr& pFragment ); + void free( const FragmentSharedPtr& pFragment ); + const ISurfaceSharedPtr& getSurface() const { return mpSurface; } + bool isValid() const; + void validate(); + + private: + typedef std::list<FragmentSharedPtr> FragmentContainer_t; + + IRenderModuleSharedPtr mpRenderModule; + ISurfaceSharedPtr mpSurface; + FragmentContainer_t mpFragments; + + bool insert( SurfaceRect& r ); + bool isValidLocation( const SurfaceRect& r ) const; + }; + + typedef ::boost::shared_ptr< Page > PageSharedPtr; + + + /** A part of a page, which gets allocated to a surface + */ + class PageFragment + { + public: + PageFragment( const SurfaceRect& r, + Page* pPage ) : + mpPage(pPage), + maRect(r), + mpBuffer(), + maSourceOffset() + { + } + + /// Creates a 'naked' fragment. + PageFragment( const ::basegfx::B2ISize& rSize ) : + mpPage(NULL), + maRect(rSize), + mpBuffer(), + maSourceOffset() + { + } + + bool isNaked() const { return (mpPage == NULL); } + const SurfaceRect& getRect() const { return maRect; } + const ::basegfx::B2IPoint& getPos() const { return maRect.maPos; } + const ::basegfx::B2ISize& getSize() const { return maRect.maSize; } + void setColorBuffer( const IColorBufferSharedPtr& pColorBuffer ) { mpBuffer=pColorBuffer; } + void setSourceOffset( const ::basegfx::B2IPoint& rOffset ) { maSourceOffset=rOffset; } + void setPage( Page* pPage ) { mpPage=pPage; } + + void free( const FragmentSharedPtr& pFragment ) + { + if(mpPage) + mpPage->free(pFragment); + + mpPage=NULL; + } + + bool select( bool bRefresh ) + { + // request was made to select this fragment, + // but this fragment has not been located on any + // of the available pages, we need to hurry now. + if(!(mpPage)) + return false; + + ISurfaceSharedPtr pSurface(mpPage->getSurface()); + + // select this surface before wiping the contents + // since a specific implementation could trigger + // a rendering operation here... + if(!(pSurface->selectTexture())) + return false; + + // call refresh() if requested, otherwise we're up to date... + return bRefresh ? refresh() : true; + } + + bool refresh() + { + if(!(mpPage)) + return false; + + ISurfaceSharedPtr pSurface(mpPage->getSurface()); + + return pSurface->update( maRect.maPos, + ::basegfx::B2IRectangle( + maSourceOffset, + maSourceOffset + maRect.maSize ), + *mpBuffer ); + } + + private: + Page* mpPage; + SurfaceRect maRect; + IColorBufferSharedPtr mpBuffer; + ::basegfx::B2IPoint maSourceOffset; + }; +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/tools/pagemanager.cxx b/canvas/source/tools/pagemanager.cxx new file mode 100644 index 000000000000..b37c578e19ab --- /dev/null +++ b/canvas/source/tools/pagemanager.cxx @@ -0,0 +1,225 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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_canvas.hxx" + +#include <boost/bind.hpp> +#include "pagemanager.hxx" + +namespace canvas +{ + + ////////////////////////////////////////////////////////////////////////////////// + // PageManager + ////////////////////////////////////////////////////////////////////////////////// + + ////////////////////////////////////////////////////////////////////////////////// + // PageManager::allocateSpace + ////////////////////////////////////////////////////////////////////////////////// + + FragmentSharedPtr PageManager::allocateSpace( const ::basegfx::B2ISize& rSize ) + { + // we are asked to find a location for the requested size. + // first we try to satisfy the request from the + // remaining space in the existing pages. + const PageContainer_t::iterator aEnd(maPages.end()); + PageContainer_t::iterator it(maPages.begin()); + while(it != aEnd) + { + FragmentSharedPtr pFragment((*it)->allocateSpace(rSize)); + if(pFragment) + { + // the page created a new fragment, since we maybe want + // to consolidate sparse pages we keep a reference to + // the fragment. + maFragments.push_back(pFragment); + return pFragment; + } + + ++it; + } + + // otherwise try to create a new page and allocate space there... + PageSharedPtr pPage(new Page(mpRenderModule)); + if(pPage->isValid()) + { + maPages.push_back(pPage); + FragmentSharedPtr pFragment(pPage->allocateSpace(rSize)); + if (pFragment) + maFragments.push_back(pFragment); + return pFragment; + } + + // the rendermodule failed to create a new page [maybe out + // of videomemory], and all other pages could not take + // the new request. we decide to create a 'naked' fragment + // which will receive its location later. + FragmentSharedPtr pFragment(new PageFragment(rSize)); + maFragments.push_back(pFragment); + return pFragment; + } + + ////////////////////////////////////////////////////////////////////////////////// + // PageManager::free + ////////////////////////////////////////////////////////////////////////////////// + + void PageManager::free( const FragmentSharedPtr& pFragment ) + { + // erase the reference to the given fragment from our + // internal container. + FragmentContainer_t::iterator it( + std::remove( + maFragments.begin(),maFragments.end(),pFragment)); + maFragments.erase(it,maFragments.end()); + + // let the fragment itself know about it... + // we need to pass 'this' as argument since the fragment + // needs to pass this to the page and can't create + // shared_ptr from itself... + pFragment->free(pFragment); + } + + ////////////////////////////////////////////////////////////////////////////////// + // PageManager::nakedFragment + ////////////////////////////////////////////////////////////////////////////////// + + void PageManager::nakedFragment( const FragmentSharedPtr& pFragment ) + { + if(maPages.empty()) + return; + + // okay, one last chance is left, we try all available + // pages again. maybe some other fragment was deleted + // and we can exploit the space. + while(!(relocate(pFragment))) + { + // no way, we need to free up some space... + // TODO(F1): this is a heuristic, could + // be designed as a policy. + const FragmentContainer_t::const_iterator aEnd(maFragments.end()); + FragmentContainer_t::const_iterator candidate(maFragments.begin()); + while(candidate != aEnd) + { + if(*candidate && !((*candidate)->isNaked())) + break; + ++candidate; + } + + if (candidate != aEnd) + { + const ::basegfx::B2ISize& rSize((*candidate)->getSize()); + sal_uInt32 nMaxArea(rSize.getX()*rSize.getY()); + + FragmentContainer_t::const_iterator it(candidate); + while(it != aEnd) + { + if (*it && !((*it)->isNaked())) + { + const ::basegfx::B2ISize& rCandidateSize((*it)->getSize()); + const sal_uInt32 nArea(rCandidateSize.getX()*rCandidateSize.getY()); + if(nArea > nMaxArea) + { + candidate=it; + nMaxArea=nArea; + } + } + + ++it; + } + + // this does not erase the candidate, + // but makes it 'naked'... + (*candidate)->free(*candidate); + } + else + break; + } + } + + ////////////////////////////////////////////////////////////////////////////////// + // PageManager::relocate + ////////////////////////////////////////////////////////////////////////////////// + + bool PageManager::relocate( const FragmentSharedPtr& pFragment ) + { + // the fragment passed as argument is assumed to + // be naked, that is it is not located on any page. + // we try all available pages again, maybe some + // other fragment was deleted and we can exploit the space. + const PageContainer_t::iterator aEnd(maPages.end()); + PageContainer_t::iterator it(maPages.begin()); + while(it != aEnd) + { + // if the page at hand takes the fragment, we immediatelly + // call select() to pull the information from the associated + // image to the hardware surface. + if((*it)->nakedFragment(pFragment)) + { + // dirty, since newly allocated. + pFragment->select(true); + return true; + } + + ++it; + } + + return false; + } + + ////////////////////////////////////////////////////////////////////////////////// + // PageManager::validatePages + ////////////////////////////////////////////////////////////////////////////////// + + void PageManager::validatePages() + { + ::std::for_each( maPages.begin(), + maPages.end(), + ::boost::mem_fn(&Page::validate)); + } + + ////////////////////////////////////////////////////////////////////////////////// + // PageManager::getPageSize + ////////////////////////////////////////////////////////////////////////////////// + + ::basegfx::B2ISize PageManager::getPageSize() const + { + return mpRenderModule->getPageSize(); + } + + ////////////////////////////////////////////////////////////////////////////////// + // PageManager::getRenderModule + ////////////////////////////////////////////////////////////////////////////////// + + canvas::IRenderModuleSharedPtr PageManager::getRenderModule() const + { + return mpRenderModule; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/tools/pagemanager.hxx b/canvas/source/tools/pagemanager.hxx new file mode 100644 index 000000000000..f82a8e339a0c --- /dev/null +++ b/canvas/source/tools/pagemanager.hxx @@ -0,0 +1,97 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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. + * + ************************************************************************/ + +#ifndef INCLUDED_CANVAS_PAGEMANAGER_HXX +#define INCLUDED_CANVAS_PAGEMANAGER_HXX + +#include <basegfx/vector/b2isize.hxx> +#include <canvas/rendering/irendermodule.hxx> +#include <canvas/rendering/isurface.hxx> + +#include "page.hxx" + +namespace canvas +{ + + ////////////////////////////////////////////////////////////////////////////////// + // PageManager + ////////////////////////////////////////////////////////////////////////////////// + + class PageManager + { + public: + PageManager( const canvas::IRenderModuleSharedPtr pRenderModule ) : + mpRenderModule(pRenderModule) + { + } + + // returns the maximum size of a hardware + // accelerated page, e.g. OpenGL texture. + ::basegfx::B2ISize getPageSize() const; + + canvas::IRenderModuleSharedPtr getRenderModule() const; + + FragmentSharedPtr allocateSpace( const ::basegfx::B2ISize& rSize ); + void free( const FragmentSharedPtr& pFragment ); + + void nakedFragment( const FragmentSharedPtr& pFragment ); + + void validatePages(); + + private: + // the pagemanager needs access to the rendermodule + // since we query for system resources from it. + canvas::IRenderModuleSharedPtr mpRenderModule; + + // here we collect all fragments that will be created + // since we need them for relocation purposes. + typedef std::list<FragmentSharedPtr> FragmentContainer_t; + FragmentContainer_t maFragments; + + // this is the container holding all created pages, + // behind the scenes these are real hardware surfaces. + typedef std::list<PageSharedPtr> PageContainer_t; + PageContainer_t maPages; + + bool relocate( const FragmentSharedPtr& pFragment ); + }; + + ////////////////////////////////////////////////////////////////////////////////// + // PageManagerSharedPtr + ////////////////////////////////////////////////////////////////////////////////// + + typedef ::boost::shared_ptr< PageManager > PageManagerSharedPtr; + + ////////////////////////////////////////////////////////////////////////////////// + // End of file + ////////////////////////////////////////////////////////////////////////////////// +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/tools/parametricpolypolygon.cxx b/canvas/source/tools/parametricpolypolygon.cxx new file mode 100644 index 000000000000..8775c1eb3cc2 --- /dev/null +++ b/canvas/source/tools/parametricpolypolygon.cxx @@ -0,0 +1,293 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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_canvas.hxx" + +#include <canvas/debug.hxx> +#include <canvas/canvastools.hxx> + +#include <rtl/math.hxx> + +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/point/b2dpoint.hxx> +#include <basegfx/range/b2drectangle.hxx> +#include <basegfx/tools/canvastools.hxx> +#include <basegfx/numeric/ftools.hxx> +#include <basegfx/tools/tools.hxx> + +#include <limits> + +#include <canvas/parametricpolypolygon.hxx> + + +using namespace ::com::sun::star; + +namespace canvas +{ + uno::Sequence<rtl::OUString> ParametricPolyPolygon::getAvailableServiceNames() + { + uno::Sequence<rtl::OUString> aRet(3); + aRet[0] = rtl::OUString::createFromAscii("LinearGradient"); + aRet[1] = rtl::OUString::createFromAscii("EllipticalGradient"); + aRet[2] = rtl::OUString::createFromAscii("RectangularGradient"); + + return aRet; + } + + ParametricPolyPolygon* ParametricPolyPolygon::create( + const uno::Reference< rendering::XGraphicDevice >& rDevice, + const ::rtl::OUString& rServiceName, + const uno::Sequence< uno::Any >& rArgs ) + { + uno::Sequence< uno::Sequence< double > > colorSequence(2); + uno::Sequence< double > colorStops(2); + double fAspectRatio=1.0; + + // defaults + uno::Sequence< rendering::RGBColor > rgbColors(1); + rgbColors[0] = rendering::RGBColor(0,0,0); + colorSequence[0] = rDevice->getDeviceColorSpace()->convertFromRGB(rgbColors); + rgbColors[0] = rendering::RGBColor(1,1,1); + colorSequence[1] = rDevice->getDeviceColorSpace()->convertFromRGB(rgbColors); + colorStops[0] = 0; + colorStops[1] = 1; + + // extract args + for( sal_Int32 i=0; i<rArgs.getLength(); ++i ) + { + beans::PropertyValue aProp; + if( (rArgs[i] >>= aProp) ) + { + if( aProp.Name.equalsAscii("Colors") ) + { + aProp.Value >>= colorSequence; + } + else if( aProp.Name.equalsAscii("Stops") ) + { + aProp.Value >>= colorStops; + } + else if( aProp.Name.equalsAscii("AspectRatio") ) + { + aProp.Value >>= fAspectRatio; + } + } + } + + if( rServiceName.equalsAscii("LinearGradient") ) + { + return createLinearHorizontalGradient(rDevice, colorSequence, colorStops); + } + else if( rServiceName.equalsAscii("EllipticalGradient") ) + { + return createEllipticalGradient(rDevice, colorSequence, colorStops, fAspectRatio); + } + else if( rServiceName.equalsAscii("RectangularGradient") ) + { + return createRectangularGradient(rDevice, colorSequence, colorStops, fAspectRatio); + } + else if( rServiceName.equalsAscii("VerticalLineHatch") ) + { + // TODO: NYI + } + else if( rServiceName.equalsAscii("OrthogonalLinesHatch") ) + { + // TODO: NYI + } + else if( rServiceName.equalsAscii("ThreeCrossingLinesHatch") ) + { + // TODO: NYI + } + else if( rServiceName.equalsAscii("FourCrossingLinesHatch") ) + { + // TODO: NYI + } + + return NULL; + } + + ParametricPolyPolygon* ParametricPolyPolygon::createLinearHorizontalGradient( + const uno::Reference< rendering::XGraphicDevice >& rDevice, + const uno::Sequence< uno::Sequence< double > >& colors, + const uno::Sequence< double >& stops ) + { + // TODO(P2): hold gradient brush statically, and only setup + // the colors + return new ParametricPolyPolygon( rDevice, GRADIENT_LINEAR, colors, stops ); + } + + ParametricPolyPolygon* ParametricPolyPolygon::createEllipticalGradient( + const uno::Reference< rendering::XGraphicDevice >& rDevice, + const uno::Sequence< uno::Sequence< double > >& colors, + const uno::Sequence< double >& stops, + double fAspectRatio ) + { + // TODO(P2): hold gradient polygon statically, and only setup + // the colors + return new ParametricPolyPolygon( + rDevice, + ::basegfx::tools::createPolygonFromCircle( + ::basegfx::B2DPoint(0,0), 1 ), + GRADIENT_ELLIPTICAL, + colors, stops, fAspectRatio ); + } + + ParametricPolyPolygon* ParametricPolyPolygon::createRectangularGradient( const uno::Reference< rendering::XGraphicDevice >& rDevice, + const uno::Sequence< uno::Sequence< double > >& colors, + const uno::Sequence< double >& stops, + double fAspectRatio ) + { + // TODO(P2): hold gradient polygon statically, and only setup + // the colors + return new ParametricPolyPolygon( + rDevice, + ::basegfx::tools::createPolygonFromRect( + ::basegfx::B2DRectangle( -1, -1, 1, 1 ) ), + GRADIENT_RECTANGULAR, + colors, stops, fAspectRatio ); + } + + void SAL_CALL ParametricPolyPolygon::disposing() + { + ::osl::MutexGuard aGuard( m_aMutex ); + + mxDevice.clear(); + } + + uno::Reference< rendering::XPolyPolygon2D > SAL_CALL ParametricPolyPolygon::getOutline( double /*t*/ ) throw (lang::IllegalArgumentException, uno::RuntimeException) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + // TODO(F1): outline NYI + return uno::Reference< rendering::XPolyPolygon2D >(); + } + + uno::Sequence< double > SAL_CALL ParametricPolyPolygon::getColor( double /*t*/ ) throw (lang::IllegalArgumentException, uno::RuntimeException) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + // TODO(F1): color NYI + return uno::Sequence< double >(); + } + + uno::Sequence< double > SAL_CALL ParametricPolyPolygon::getPointColor( const geometry::RealPoint2D& /*point*/ ) throw (lang::IllegalArgumentException, uno::RuntimeException) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + // TODO(F1): point color NYI + return uno::Sequence< double >(); + } + + uno::Reference< rendering::XColorSpace > SAL_CALL ParametricPolyPolygon::getColorSpace() throw (uno::RuntimeException) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + return mxDevice.is() ? mxDevice->getDeviceColorSpace() : uno::Reference< rendering::XColorSpace >(); + } + +#define IMPLEMENTATION_NAME "Canvas::ParametricPolyPolygon" +#define SERVICE_NAME "com.sun.star.rendering.ParametricPolyPolygon" + + ::rtl::OUString SAL_CALL ParametricPolyPolygon::getImplementationName( ) throw (uno::RuntimeException) + { + return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( IMPLEMENTATION_NAME ) ); + } + + sal_Bool SAL_CALL ParametricPolyPolygon::supportsService( const ::rtl::OUString& ServiceName ) throw (uno::RuntimeException) + { + return ServiceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM ( SERVICE_NAME ) ); + } + + uno::Sequence< ::rtl::OUString > SAL_CALL ParametricPolyPolygon::getSupportedServiceNames( ) throw (uno::RuntimeException) + { + uno::Sequence< ::rtl::OUString > aRet(1); + aRet[0] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM ( SERVICE_NAME ) ); + + return aRet; + } + + ParametricPolyPolygon::~ParametricPolyPolygon() + { + } + + ParametricPolyPolygon::ParametricPolyPolygon( const uno::Reference< rendering::XGraphicDevice >& rDevice, + const ::basegfx::B2DPolygon& rGradientPoly, + GradientType eType, + const uno::Sequence< uno::Sequence< double > >& rColors, + const uno::Sequence< double >& rStops ) : + ParametricPolyPolygon_Base( m_aMutex ), + mxDevice( rDevice ), + maValues( rGradientPoly, + rColors, + rStops, + 1.0, + eType ) + { + } + + ParametricPolyPolygon::ParametricPolyPolygon( const uno::Reference< rendering::XGraphicDevice >& rDevice, + const ::basegfx::B2DPolygon& rGradientPoly, + GradientType eType, + const uno::Sequence< uno::Sequence< double > >& rColors, + const uno::Sequence< double >& rStops, + double nAspectRatio ) : + ParametricPolyPolygon_Base( m_aMutex ), + mxDevice( rDevice ), + maValues( rGradientPoly, + rColors, + rStops, + nAspectRatio, + eType ) + { + } + + ParametricPolyPolygon::ParametricPolyPolygon( const uno::Reference< rendering::XGraphicDevice >& rDevice, + GradientType eType, + const uno::Sequence< uno::Sequence< double > >& rColors, + const uno::Sequence< double >& rStops ) : + ParametricPolyPolygon_Base( m_aMutex ), + mxDevice( rDevice ), + maValues( ::basegfx::B2DPolygon(), + rColors, + rStops, + 1.0, + eType ) + { + } + + ParametricPolyPolygon::Values ParametricPolyPolygon::getValues() const + { + ::osl::MutexGuard aGuard( m_aMutex ); + + return maValues; + } + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/tools/prioritybooster.cxx b/canvas/source/tools/prioritybooster.cxx new file mode 100644 index 000000000000..4256965627f7 --- /dev/null +++ b/canvas/source/tools/prioritybooster.cxx @@ -0,0 +1,86 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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_canvas.hxx" + + +#ifdef WNT +# if defined _MSC_VER +# pragma warning(push,1) +# endif + +# include <windows.h> + +# if defined _MSC_VER +# pragma warning(pop) +# endif +#endif + +#include "osl/diagnose.h" +#include "canvas/prioritybooster.hxx" + + +namespace canvas +{ + namespace tools + { + struct PriorityBooster_Impl + { + int mnOldPriority; + }; + + PriorityBooster::PriorityBooster( sal_Int32 nDelta ) : + mpImpl( new PriorityBooster_Impl ) + { +#ifdef WNT + HANDLE aCurrThread = GetCurrentThread(); + mpImpl->mnOldPriority = GetThreadPriority( aCurrThread ); + + if ( 0 == SetThreadPriority( aCurrThread, mpImpl->mnOldPriority + nDelta ) ) + { + OSL_ENSURE( false, + "PriorityBooster::PriorityBooster(): Was not able to modify thread priority" ); + } +#else + nDelta = 0; // #i55991# placate gcc warning +#endif + } + + PriorityBooster::~PriorityBooster() + { +#ifdef WNT + SetThreadPriority( GetCurrentThread(), + mpImpl->mnOldPriority ); +#endif + } + } // namespace tools + +} // namespace canvas + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/tools/propertysethelper.cxx b/canvas/source/tools/propertysethelper.cxx new file mode 100644 index 000000000000..d48f1922d689 --- /dev/null +++ b/canvas/source/tools/propertysethelper.cxx @@ -0,0 +1,190 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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_canvas.hxx" + +#include <canvas/propertysethelper.hxx> + +using namespace ::com::sun::star; + +namespace canvas +{ + namespace + { + void throwUnknown( const ::rtl::OUString& aPropertyName ) + { + throw beans::UnknownPropertyException( + ::rtl::OUString::createFromAscii("PropertySetHelper: property ") + + aPropertyName + + ::rtl::OUString::createFromAscii(" not found."), + uno::Reference< uno::XInterface >() + ); + } + + void throwVeto( const ::rtl::OUString& aPropertyName ) + { + throw beans::PropertyVetoException( + ::rtl::OUString::createFromAscii("PropertySetHelper: property ") + + aPropertyName + + ::rtl::OUString::createFromAscii(" access was vetoed."), + uno::Reference< uno::XInterface >() ); + } + + struct EntryComparator + { + bool operator()( const PropertySetHelper::MapType::MapEntry& rLHS, + const PropertySetHelper::MapType::MapEntry& rRHS ) + { + return strcmp( rLHS.maKey, + rRHS.maKey ) < 0; + } + }; + } + + PropertySetHelper::PropertySetHelper() : + mpMap(), + maMapEntries() + { + } + + PropertySetHelper::PropertySetHelper( const InputMap& rMap ) : + mpMap(), + maMapEntries() + { + initProperties(rMap); + } + + void PropertySetHelper::initProperties( const InputMap& rMap ) + { + mpMap.reset(); + maMapEntries = rMap; + + std::sort( maMapEntries.begin(), + maMapEntries.end(), + EntryComparator() ); + + if( !maMapEntries.empty() ) + mpMap.reset( new MapType(&maMapEntries[0], + maMapEntries.size(), + true) ); + } + + void PropertySetHelper::addProperties( const InputMap& rMap ) + { + InputMap aMerged( getPropertyMap() ); + aMerged.insert( aMerged.end(), + rMap.begin(), + rMap.end() ); + + initProperties( aMerged ); + } + + bool PropertySetHelper::isPropertyName( const ::rtl::OUString& aPropertyName ) const + { + if( !mpMap.get() ) + return false; + + Callbacks aDummy; + return mpMap->lookup( aPropertyName, + aDummy ); + } + + uno::Reference< beans::XPropertySetInfo > PropertySetHelper::getPropertySetInfo() const + { + // we're a stealth property set + return uno::Reference< beans::XPropertySetInfo >(); + } + + void PropertySetHelper::setPropertyValue( const ::rtl::OUString& aPropertyName, + const uno::Any& aValue ) + { + Callbacks aCallbacks; + if( !mpMap.get() || + !mpMap->lookup( aPropertyName, + aCallbacks ) ) + { + throwUnknown( aPropertyName ); + } + + if( aCallbacks.setter.empty() ) + throwVeto( aPropertyName ); + + aCallbacks.setter(aValue); + } + + uno::Any PropertySetHelper::getPropertyValue( const ::rtl::OUString& aPropertyName ) const + { + Callbacks aCallbacks; + if( !mpMap.get() || + !mpMap->lookup( aPropertyName, + aCallbacks ) ) + { + throwUnknown( aPropertyName ); + } + + if( !aCallbacks.getter.empty() ) + return aCallbacks.getter(); + + // TODO(Q1): subtlety, empty getter method silently returns + // the empty any + return uno::Any(); + } + + void PropertySetHelper::addPropertyChangeListener( const ::rtl::OUString& aPropertyName, + const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/ ) + { + // check validity of property, but otherwise ignore the + // request + if( !isPropertyName( aPropertyName ) ) + throwUnknown( aPropertyName ); + } + + void PropertySetHelper::removePropertyChangeListener( const ::rtl::OUString& /*aPropertyName*/, + const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/ ) + { + // ignore request, no listener added in the first place + } + + void PropertySetHelper::addVetoableChangeListener( const ::rtl::OUString& aPropertyName, + const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/ ) + { + // check validity of property, but otherwise ignore the + // request + if( !isPropertyName( aPropertyName ) ) + throwUnknown( aPropertyName ); + } + + void PropertySetHelper::removeVetoableChangeListener( const ::rtl::OUString& /*aPropertyName*/, + const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/ ) + { + // ignore request, no listener added in the first place + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/tools/spriteredrawmanager.cxx b/canvas/source/tools/spriteredrawmanager.cxx new file mode 100644 index 000000000000..9fce1be93a5b --- /dev/null +++ b/canvas/source/tools/spriteredrawmanager.cxx @@ -0,0 +1,523 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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_canvas.hxx" + +#include <canvas/debug.hxx> +#include <tools/diagnose_ex.h> +#include <canvas/spriteredrawmanager.hxx> + +#include <basegfx/range/b2drectangle.hxx> +#include <basegfx/tools/canvastools.hxx> +#include <basegfx/vector/b2dsize.hxx> +#include <basegfx/range/rangeexpander.hxx> + +#include <algorithm> +#include <functional> +#include <boost/bind.hpp> + + +namespace canvas +{ + namespace + { + /** Helper class to condense sprite updates into a single action + + This class tracks the sprite changes over the recorded + change list, and generates a single update action from + that (note that per screen update, several moves, + visibility changes and content updates might happen) + */ + class SpriteTracer + { + public: + SpriteTracer( const Sprite::Reference& rAffectedSprite ) : + mpAffectedSprite(rAffectedSprite), + maMoveStartArea(), + maMoveEndArea(), + mbIsMove( false ), + mbIsGenericUpdate( false ) + { + } + + void operator()( const SpriteRedrawManager::SpriteChangeRecord& rSpriteRecord ) + { + // only deal with change events from the currently + // affected sprite + if( rSpriteRecord.mpAffectedSprite == mpAffectedSprite ) + { + switch( rSpriteRecord.meChangeType ) + { + case SpriteRedrawManager::SpriteChangeRecord::move: + if( !mbIsMove ) + { + // no move yet - this must be the first one + maMoveStartArea = ::basegfx::B2DRectangle( + rSpriteRecord.maOldPos, + rSpriteRecord.maOldPos + rSpriteRecord.maUpdateArea.getRange() ); + mbIsMove = true; + } + + maMoveEndArea = rSpriteRecord.maUpdateArea; + break; + + case SpriteRedrawManager::SpriteChangeRecord::update: + // update end update area of the + // sprite. Thus, every update() action + // _after_ the last move will correctly + // update the final repaint area. And this + // does not interfere with subsequent + // moves, because moves always perform a + // hard set of maMoveEndArea to their + // stored value + maMoveEndArea.expand( rSpriteRecord.maUpdateArea ); + mbIsGenericUpdate = true; + break; + + default: + ENSURE_OR_THROW( false, + "Unexpected case in SpriteUpdater::operator()" ); + break; + } + } + } + + void commit( SpriteRedrawManager::SpriteConnectedRanges& rUpdateCollector ) const + { + if( mbIsMove ) + { + if( !maMoveStartArea.isEmpty() || + !maMoveEndArea.isEmpty() ) + { + // if mbIsGenericUpdate is false, this is a + // pure move (i.e. no other update + // operations). Pass that information on to + // the SpriteInfo + const bool bIsPureMove( !mbIsGenericUpdate ); + + // ignore the case that start and end update + // area overlap - the b2dconnectedranges + // handle that, anyway. doing it this way + // ensures that we have both old and new area + // stored + + // round all given range up to enclosing + // integer rectangle - since the whole thing + // here is about + + // first, draw the new sprite position + rUpdateCollector.addRange( + ::basegfx::unotools::b2DSurroundingIntegerRangeFromB2DRange( maMoveEndArea ), + SpriteRedrawManager::SpriteInfo( + mpAffectedSprite, + maMoveEndArea, + true, + bIsPureMove ) ); + + // then, clear the old place (looks smoother + // this way) + rUpdateCollector.addRange( + ::basegfx::unotools::b2DSurroundingIntegerRangeFromB2DRange( maMoveStartArea ), + SpriteRedrawManager::SpriteInfo( + Sprite::Reference(), + maMoveStartArea, + true, + bIsPureMove ) ); + } + } + else if( mbIsGenericUpdate && + !maMoveEndArea.isEmpty() ) + { + rUpdateCollector.addRange( + ::basegfx::unotools::b2DSurroundingIntegerRangeFromB2DRange( maMoveEndArea ), + SpriteRedrawManager::SpriteInfo( + mpAffectedSprite, + maMoveEndArea, + true ) ); + } + } + + private: + Sprite::Reference mpAffectedSprite; + ::basegfx::B2DRectangle maMoveStartArea; + ::basegfx::B2DRectangle maMoveEndArea; + + /// True, if at least one move was encountered + bool mbIsMove; + + /// True, if at least one generic update was encountered + bool mbIsGenericUpdate; + }; + + + /** SpriteChecker functor, which for every sprite checks the + given update vector for necessary screen updates + */ + class SpriteUpdater + { + public: + /** Generate update area list + + @param rUpdater + Reference to an updater object, which will receive the + update areas. + + @param rChangeContainer + Container with all sprite change requests + + */ + SpriteUpdater( SpriteRedrawManager::SpriteConnectedRanges& rUpdater, + const SpriteRedrawManager::VectorOfChangeRecords& rChangeContainer ) : + mrUpdater( rUpdater ), + mrChangeContainer( rChangeContainer ) + { + } + + /** Call this method for every sprite on your screen + + This method scans the change container, collecting all + update info for the given sprite into one or two + update operations, which in turn are inserted into the + connected ranges processor. + + @param rSprite + Current sprite to collect update info for. + */ + void operator()( const Sprite::Reference& rSprite ) + { + const SpriteTracer aSpriteTracer( + ::std::for_each( mrChangeContainer.begin(), + mrChangeContainer.end(), + SpriteTracer( rSprite ) ) ); + + aSpriteTracer.commit( mrUpdater ); + } + + private: + SpriteRedrawManager::SpriteConnectedRanges& mrUpdater; + const SpriteRedrawManager::VectorOfChangeRecords& mrChangeContainer; + }; + } + + void SpriteRedrawManager::setupUpdateAreas( SpriteConnectedRanges& rUpdateAreas ) const + { + // TODO(T3): This is NOT thread safe at all. This only works + // under the assumption that NOBODY changes ANYTHING + // concurrently, while this method is on the stack. We should + // really rework the canvas::Sprite interface, in such a way + // that it dumps ALL its state with a single, atomic + // call. Then, we store that state locally. This prolly goes + // in line with the problem of having sprite state available + // for the frame before the last frame; plus, it avoids + // frequent locks of the object mutices + SpriteComparator aSpriteComparator; + + // put all sprites that have changed content into update areas + ListOfSprites::const_iterator aCurrSprite( maSprites.begin() ); + const ListOfSprites::const_iterator aEndSprite ( maSprites.end() ); + while( aCurrSprite != aEndSprite ) + { + if( (*aCurrSprite)->isContentChanged() ) + const_cast<SpriteRedrawManager*>(this)->updateSprite( *aCurrSprite, + (*aCurrSprite)->getPosPixel(), + (*aCurrSprite)->getUpdateArea() ); + ++aCurrSprite; + } + + // sort sprites after prio + VectorOfSprites aSortedSpriteVector; + ::std::copy( maSprites.begin(), + maSprites.end(), + ::std::back_insert_iterator< VectorOfSprites >(aSortedSpriteVector) ); + ::std::sort( aSortedSpriteVector.begin(), + aSortedSpriteVector.end(), + aSpriteComparator ); + + // extract all referenced sprites from the maChangeRecords + // (copy sprites, make the list unique, regarding the + // sprite pointer). This assumes that, until this scope + // ends, nobody changes the maChangeRecords vector! + VectorOfSprites aUpdatableSprites; + VectorOfChangeRecords::const_iterator aCurrRecord( maChangeRecords.begin() ); + const VectorOfChangeRecords::const_iterator aEndRecords( maChangeRecords.end() ); + while( aCurrRecord != aEndRecords ) + { + const Sprite::Reference& rSprite( aCurrRecord->getSprite() ); + if( rSprite.is() ) + aUpdatableSprites.push_back( rSprite ); + ++aCurrRecord; + } + + VectorOfSprites::iterator aBegin( aUpdatableSprites.begin() ); + VectorOfSprites::iterator aEnd ( aUpdatableSprites.end() ); + ::std::sort( aBegin, + aEnd, + aSpriteComparator ); + + aEnd = ::std::unique( aBegin, aEnd ); + + // for each unique sprite, check the change event vector, + // calculate the update operation from that, and add the + // result to the aUpdateArea. + ::std::for_each( aBegin, + aEnd, + SpriteUpdater( rUpdateAreas, + maChangeRecords) ); + + // TODO(P2): Implement your own output iterator adapter, to + // avoid that totally superfluous temp aUnchangedSprites + // vector. + + // add all sprites to rUpdateAreas, that are _not_ already + // contained in the uniquified vector of changed ones + // (i.e. the difference between aSortedSpriteVector and + // aUpdatableSprites). + VectorOfSprites aUnchangedSprites; + ::std::set_difference( aSortedSpriteVector.begin(), + aSortedSpriteVector.end(), + aBegin, aEnd, + ::std::back_insert_iterator< VectorOfSprites >(aUnchangedSprites) ); + + // add each remaining unchanged sprite to connected ranges, + // marked as "don't need update" + VectorOfSprites::const_iterator aCurr( aUnchangedSprites.begin() ); + const VectorOfSprites::const_iterator aEnd2( aUnchangedSprites.end() ); + while( aCurr != aEnd2 ) + { + const ::basegfx::B2DRange& rUpdateArea( (*aCurr)->getUpdateArea() ); + rUpdateAreas.addRange( + ::basegfx::unotools::b2DSurroundingIntegerRangeFromB2DRange( rUpdateArea ), + SpriteInfo(*aCurr, + rUpdateArea, + false) ); + ++aCurr; + } + } + +#if OSL_DEBUG_LEVEL > 0 + bool impIsEqualB2DRange(const basegfx::B2DRange& rRangeA, const basegfx::B2DRange& rRangeB, double fSmallValue) + { + return fabs(rRangeB.getMinX() - rRangeA.getMinX()) <= fSmallValue + && fabs(rRangeB.getMinY() - rRangeA.getMinY()) <= fSmallValue + && fabs(rRangeB.getMaxX() - rRangeA.getMaxX()) <= fSmallValue + && fabs(rRangeB.getMaxY() - rRangeA.getMaxY()) <= fSmallValue; + } + + bool impIsEqualB2DVector(const basegfx::B2DVector& rVecA, const basegfx::B2DVector& rVecB, double fSmallValue) + { + return fabs(rVecB.getX() - rVecA.getX()) <= fSmallValue + && fabs(rVecB.getY() - rVecA.getY()) <= fSmallValue; + } +#endif + + bool SpriteRedrawManager::isAreaUpdateScroll( ::basegfx::B2DRectangle& o_rMoveStart, + ::basegfx::B2DRectangle& o_rMoveEnd, + const UpdateArea& rUpdateArea, + ::std::size_t nNumSprites ) const + { + // check for a solitary move, which consists of exactly two + // pure-move entries, the first with valid, the second with + // invalid sprite (see SpriteTracer::commit()). Note that we + // cannot simply store some flag in SpriteTracer::commit() + // above and just check that here, since during the connected + // range calculations, other sprites might get merged into the + // same region (thus spoiling the scrolling move + // optimization). + if( nNumSprites != 2 ) + return false; + + const SpriteConnectedRanges::ComponentListType::const_iterator aFirst( + rUpdateArea.maComponentList.begin() ); + SpriteConnectedRanges::ComponentListType::const_iterator aSecond( + aFirst ); ++aSecond; + + if( !aFirst->second.isPureMove() || + !aSecond->second.isPureMove() || + !aFirst->second.getSprite().is() || + // use _true_ update area, not the rounded version + !aFirst->second.getSprite()->isAreaUpdateOpaque( aFirst->second.getUpdateArea() ) || + aSecond->second.getSprite().is() ) + { + // either no move update, or incorrect sprite, or sprite + // content not fully opaque over update region. + return false; + } + + o_rMoveStart = aSecond->second.getUpdateArea(); + o_rMoveEnd = aFirst->second.getUpdateArea(); + +#if OSL_DEBUG_LEVEL > 0 + ::basegfx::B2DRectangle aTotalBounds( o_rMoveStart ); + aTotalBounds.expand( o_rMoveEnd ); + + OSL_POSTCOND(impIsEqualB2DRange(rUpdateArea.maTotalBounds, basegfx::unotools::b2DSurroundingIntegerRangeFromB2DRange(aTotalBounds), 0.5), + "SpriteRedrawManager::isAreaUpdateScroll(): sprite area and total area mismatch"); + OSL_POSTCOND(impIsEqualB2DVector(o_rMoveStart.getRange(), o_rMoveEnd.getRange(), 0.5), + "SpriteRedrawManager::isAreaUpdateScroll(): scroll start and end area have mismatching size"); +#endif + + return true; + } + + bool SpriteRedrawManager::isAreaUpdateNotOpaque( const ::basegfx::B2DRectangle& rUpdateRect, + const AreaComponent& rComponent ) const + { + const Sprite::Reference& pAffectedSprite( rComponent.second.getSprite() ); + + if( !pAffectedSprite.is() ) + return true; // no sprite, no opaque update! + + return !pAffectedSprite->isAreaUpdateOpaque( rUpdateRect ); + } + + bool SpriteRedrawManager::isAreaUpdateOpaque( const UpdateArea& rUpdateArea, + ::std::size_t nNumSprites ) const + { + // check whether the sprites in the update area's list will + // fully cover the given area _and_ do that in an opaque way + // (i.e. no alpha, no non-rectangular sprite content). + + // TODO(P1): Come up with a smarter early-exit criterion here + // (though, I think, the case that _lots_ of sprites _fully_ + // cover a rectangular area _without_ any holes is extremely + // improbable) + + // avoid checking large number of sprites (and probably fail, + // anyway). Note: the case nNumSprites < 1 should normally not + // happen, as handleArea() calls backgroundPaint() then. + if( nNumSprites > 3 || nNumSprites < 1 ) + return false; + + const SpriteConnectedRanges::ComponentListType::const_iterator aBegin( + rUpdateArea.maComponentList.begin() ); + const SpriteConnectedRanges::ComponentListType::const_iterator aEnd( + rUpdateArea.maComponentList.end() ); + + // now, calc the _true_ update area, by merging all sprite's + // true update areas into one rectangle + ::basegfx::B2DRange aTrueArea( aBegin->second.getUpdateArea() ); + ::std::for_each( aBegin, + aEnd, + ::boost::bind( ::basegfx::B2DRangeExpander(aTrueArea), + ::boost::bind( &SpriteInfo::getUpdateArea, + ::boost::bind( ::std::select2nd<AreaComponent>(), + _1 ) ) ) ); + + // and check whether _any_ of the sprites tells that its area + // update will not be opaque. + return (::std::find_if( aBegin, + aEnd, + ::boost::bind( &SpriteRedrawManager::isAreaUpdateNotOpaque, + this, + ::boost::cref(aTrueArea), + _1 ) ) == aEnd ); + } + + bool SpriteRedrawManager::areSpritesChanged( const UpdateArea& rUpdateArea ) const + { + // check whether SpriteInfo::needsUpdate returns false for + // all elements of this area's contained sprites + // + // if not a single changed sprite found - just ignore this + // component (return false) + const SpriteConnectedRanges::ComponentListType::const_iterator aEnd( + rUpdateArea.maComponentList.end() ); + return (::std::find_if( rUpdateArea.maComponentList.begin(), + aEnd, + ::boost::bind( &SpriteInfo::needsUpdate, + ::boost::bind( + ::std::select2nd<SpriteConnectedRanges::ComponentType>(), + _1 ) ) ) != aEnd ); + } + + SpriteRedrawManager::SpriteRedrawManager() : + maSprites(), + maChangeRecords() + { + } + + void SpriteRedrawManager::disposing() + { + // drop all references + maChangeRecords.clear(); + + // dispose all sprites - the spritecanvas, and by delegation, + // this object, is the owner of the sprites. After all, a + // sprite without a canvas to render into makes not terribly + // much sense. + + // TODO(Q3): Once boost 1.33 is in, change back to for_each + // with ::boost::mem_fn. For the time being, explicit loop due + // to cdecl declaration of all UNO methods. + ListOfSprites::reverse_iterator aCurr( maSprites.rbegin() ); + ListOfSprites::reverse_iterator aEnd( maSprites.rend() ); + while( aCurr != aEnd ) + (*aCurr++)->dispose(); + + maSprites.clear(); + } + + void SpriteRedrawManager::clearChangeRecords() + { + maChangeRecords.clear(); + } + + void SpriteRedrawManager::showSprite( const Sprite::Reference& rSprite ) + { + maSprites.push_back( rSprite ); + } + + void SpriteRedrawManager::hideSprite( const Sprite::Reference& rSprite ) + { + maSprites.remove( rSprite ); + } + + void SpriteRedrawManager::moveSprite( const Sprite::Reference& rSprite, + const ::basegfx::B2DPoint& rOldPos, + const ::basegfx::B2DPoint& rNewPos, + const ::basegfx::B2DVector& rSpriteSize ) + { + maChangeRecords.push_back( SpriteChangeRecord( rSprite, + rOldPos, + rNewPos, + rSpriteSize ) ); + } + + void SpriteRedrawManager::updateSprite( const Sprite::Reference& rSprite, + const ::basegfx::B2DPoint& rPos, + const ::basegfx::B2DRange& rUpdateArea ) + { + maChangeRecords.push_back( SpriteChangeRecord( rSprite, + rPos, + rUpdateArea ) ); + } + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/tools/surface.cxx b/canvas/source/tools/surface.cxx new file mode 100644 index 000000000000..4d23c40f7b27 --- /dev/null +++ b/canvas/source/tools/surface.cxx @@ -0,0 +1,498 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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_canvas.hxx" + +#include "surface.hxx" +#include <basegfx/polygon/b2dpolygonclipper.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <comphelper/scopeguard.hxx> +#include <boost/bind.hpp> + +namespace canvas +{ + + ////////////////////////////////////////////////////////////////////////////////// + // Surface::Surface + ////////////////////////////////////////////////////////////////////////////////// + + Surface::Surface( const PageManagerSharedPtr& rPageManager, + const IColorBufferSharedPtr& rColorBuffer, + const ::basegfx::B2IPoint& rPos, + const ::basegfx::B2ISize& rSize ) : + mpColorBuffer(rColorBuffer), + mpPageManager(rPageManager), + mpFragment(), + maSourceOffset(rPos), + maSize(rSize), + mbIsDirty(true) + { + } + + ////////////////////////////////////////////////////////////////////////////////// + // Surface::~Surface + ////////////////////////////////////////////////////////////////////////////////// + + Surface::~Surface() + { + if(mpFragment) + mpPageManager->free(mpFragment); + } + + ////////////////////////////////////////////////////////////////////////////////// + // Surface::getUVCoords + ////////////////////////////////////////////////////////////////////////////////// + + void Surface::setColorBufferDirty() + { + mbIsDirty=true; + } + + ////////////////////////////////////////////////////////////////////////////////// + // Surface::getUVCoords + ////////////////////////////////////////////////////////////////////////////////// + + basegfx::B2DRectangle Surface::getUVCoords() const + { + ::basegfx::B2ISize aPageSize(mpPageManager->getPageSize()); + ::basegfx::B2IPoint aDestOffset; + if( mpFragment ) + aDestOffset = mpFragment->getPos(); + + const double pw( aPageSize.getX() ); + const double ph( aPageSize.getY() ); + const double ox( aDestOffset.getX() ); + const double oy( aDestOffset.getY() ); + const double sx( maSize.getX() ); + const double sy( maSize.getY() ); + + return ::basegfx::B2DRectangle( ox/pw, + oy/ph, + (ox+sx)/pw, + (oy+sy)/ph ); + } + + ////////////////////////////////////////////////////////////////////////////////// + // Surface::getUVCoords + ////////////////////////////////////////////////////////////////////////////////// + + basegfx::B2DRectangle Surface::getUVCoords( const ::basegfx::B2IPoint& rPos, + const ::basegfx::B2ISize& rSize ) const + { + ::basegfx::B2ISize aPageSize(mpPageManager->getPageSize()); + + const double pw( aPageSize.getX() ); + const double ph( aPageSize.getY() ); + const double ox( rPos.getX() ); + const double oy( rPos.getY() ); + const double sx( rSize.getX() ); + const double sy( rSize.getY() ); + + return ::basegfx::B2DRectangle( ox/pw, + oy/ph, + (ox+sx)/pw, + (oy+sy)/ph ); + } + + ////////////////////////////////////////////////////////////////////////////////// + // Surface::draw + ////////////////////////////////////////////////////////////////////////////////// + + bool Surface::draw( double fAlpha, + const ::basegfx::B2DPoint& rPos, + const ::basegfx::B2DHomMatrix& rTransform ) + { + IRenderModuleSharedPtr pRenderModule(mpPageManager->getRenderModule()); + + RenderModuleGuard aGuard( pRenderModule ); + + prepareRendering(); + + // convert size to normalized device coordinates + const ::basegfx::B2DRectangle& rUV( getUVCoords() ); + + const double u1(rUV.getMinX()); + const double v1(rUV.getMinY()); + const double u2(rUV.getMaxX()); + const double v2(rUV.getMaxY()); + + // concat transforms + // 1) offset of surface subarea + // 2) surface transform + // 3) translation to output position [rPos] + // 4) scale to normalized device coordinates + // 5) flip y-axis + // 6) translate to account for viewport transform + basegfx::B2DHomMatrix aTransform(basegfx::tools::createTranslateB2DHomMatrix( + maSourceOffset.getX(), maSourceOffset.getY())); + aTransform = aTransform * rTransform; + aTransform.translate(::basegfx::fround(rPos.getX()), + ::basegfx::fround(rPos.getY())); + + /* + ###################################### + ###################################### + ###################################### + + Y + ^+1 + | + 2 | 3 + x------------x + | | | + | | | + ------|-----O------|------>X + -1 | | | +1 + | | | + x------------x + 1 | 0 + | + |-1 + + ###################################### + ###################################### + ###################################### + */ + + const ::basegfx::B2DPoint& p0(aTransform * ::basegfx::B2DPoint(maSize.getX(),maSize.getY())); + const ::basegfx::B2DPoint& p1(aTransform * ::basegfx::B2DPoint(0.0,maSize.getY())); + const ::basegfx::B2DPoint& p2(aTransform * ::basegfx::B2DPoint(0.0,0.0)); + const ::basegfx::B2DPoint& p3(aTransform * ::basegfx::B2DPoint(maSize.getX(),0.0)); + + canvas::Vertex vertex; + vertex.r = 1.0f; + vertex.g = 1.0f; + vertex.b = 1.0f; + vertex.a = static_cast<float>(fAlpha); + vertex.z = 0.0f; + + { + pRenderModule->beginPrimitive( canvas::IRenderModule::PRIMITIVE_TYPE_QUAD ); + + // issue an endPrimitive() when leaving the scope + const ::comphelper::ScopeGuard aScopeGuard( + boost::bind( &::canvas::IRenderModule::endPrimitive, + ::boost::ref(pRenderModule) ) ); + + vertex.u=static_cast<float>(u2); vertex.v=static_cast<float>(v2); + vertex.x=static_cast<float>(p0.getX()); vertex.y=static_cast<float>(p0.getY()); + pRenderModule->pushVertex(vertex); + + vertex.u=static_cast<float>(u1); vertex.v=static_cast<float>(v2); + vertex.x=static_cast<float>(p1.getX()); vertex.y=static_cast<float>(p1.getY()); + pRenderModule->pushVertex(vertex); + + vertex.u=static_cast<float>(u1); vertex.v=static_cast<float>(v1); + vertex.x=static_cast<float>(p2.getX()); vertex.y=static_cast<float>(p2.getY()); + pRenderModule->pushVertex(vertex); + + vertex.u=static_cast<float>(u2); vertex.v=static_cast<float>(v1); + vertex.x=static_cast<float>(p3.getX()); vertex.y=static_cast<float>(p3.getY()); + pRenderModule->pushVertex(vertex); + } + + return !(pRenderModule->isError()); + } + + ////////////////////////////////////////////////////////////////////////////////// + // Surface::drawRectangularArea + ////////////////////////////////////////////////////////////////////////////////// + + bool Surface::drawRectangularArea( + double fAlpha, + const ::basegfx::B2DPoint& rPos, + const ::basegfx::B2DRectangle& rArea, + const ::basegfx::B2DHomMatrix& rTransform ) + { + if( rArea.isEmpty() ) + return true; // immediate exit for empty area + + IRenderModuleSharedPtr pRenderModule(mpPageManager->getRenderModule()); + + RenderModuleGuard aGuard( pRenderModule ); + + prepareRendering(); + + // these positions are relative to the texture + ::basegfx::B2IPoint aPos1( + ::basegfx::fround(rArea.getMinimum().getX()), + ::basegfx::fround(rArea.getMinimum().getY())); + ::basegfx::B2IPoint aPos2( + ::basegfx::fround(rArea.getMaximum().getX()), + ::basegfx::fround(rArea.getMaximum().getY()) ); + + // clip the positions to the area this surface covers + aPos1.setX(::std::max(aPos1.getX(),maSourceOffset.getX())); + aPos1.setY(::std::max(aPos1.getY(),maSourceOffset.getY())); + aPos2.setX(::std::min(aPos2.getX(),maSourceOffset.getX()+maSize.getX())); + aPos2.setY(::std::min(aPos2.getY(),maSourceOffset.getY()+maSize.getY())); + + // if the resulting area is empty, return immediately + ::basegfx::B2IVector aSize(aPos2 - aPos1); + if(aSize.getX() <= 0 || aSize.getY() <= 0) + return true; + + ::basegfx::B2IPoint aDestOffset; + if( mpFragment ) + aDestOffset = mpFragment->getPos(); + + // convert size to normalized device coordinates + const ::basegfx::B2DRectangle& rUV( + getUVCoords(aPos1 - maSourceOffset + aDestOffset, + aSize) ); + const double u1(rUV.getMinX()); + const double v1(rUV.getMinY()); + const double u2(rUV.getMaxX()); + const double v2(rUV.getMaxY()); + + // concatenate transforms + // 1) offset of surface subarea + // 2) surface transform + // 3) translation to output position [rPos] + basegfx::B2DHomMatrix aTransform(basegfx::tools::createTranslateB2DHomMatrix(aPos1.getX(), aPos1.getY())); + aTransform = aTransform * rTransform; + aTransform.translate(::basegfx::fround(rPos.getX()), + ::basegfx::fround(rPos.getY())); + + + /* + ###################################### + ###################################### + ###################################### + + Y + ^+1 + | + 2 | 3 + x------------x + | | | + | | | + ------|-----O------|------>X + -1 | | | +1 + | | | + x------------x + 1 | 0 + | + |-1 + + ###################################### + ###################################### + ###################################### + */ + + const ::basegfx::B2DPoint& p0(aTransform * ::basegfx::B2DPoint(aSize.getX(),aSize.getY())); + const ::basegfx::B2DPoint& p1(aTransform * ::basegfx::B2DPoint(0.0, aSize.getY())); + const ::basegfx::B2DPoint& p2(aTransform * ::basegfx::B2DPoint(0.0, 0.0)); + const ::basegfx::B2DPoint& p3(aTransform * ::basegfx::B2DPoint(aSize.getX(),0.0)); + + canvas::Vertex vertex; + vertex.r = 1.0f; + vertex.g = 1.0f; + vertex.b = 1.0f; + vertex.a = static_cast<float>(fAlpha); + vertex.z = 0.0f; + + { + pRenderModule->beginPrimitive( canvas::IRenderModule::PRIMITIVE_TYPE_QUAD ); + + // issue an endPrimitive() when leaving the scope + const ::comphelper::ScopeGuard aScopeGuard( + boost::bind( &::canvas::IRenderModule::endPrimitive, + ::boost::ref(pRenderModule) ) ); + + vertex.u=static_cast<float>(u2); vertex.v=static_cast<float>(v2); + vertex.x=static_cast<float>(p0.getX()); vertex.y=static_cast<float>(p0.getY()); + pRenderModule->pushVertex(vertex); + + vertex.u=static_cast<float>(u1); vertex.v=static_cast<float>(v2); + vertex.x=static_cast<float>(p1.getX()); vertex.y=static_cast<float>(p1.getY()); + pRenderModule->pushVertex(vertex); + + vertex.u=static_cast<float>(u1); vertex.v=static_cast<float>(v1); + vertex.x=static_cast<float>(p2.getX()); vertex.y=static_cast<float>(p2.getY()); + pRenderModule->pushVertex(vertex); + + vertex.u=static_cast<float>(u2); vertex.v=static_cast<float>(v1); + vertex.x=static_cast<float>(p3.getX()); vertex.y=static_cast<float>(p3.getY()); + pRenderModule->pushVertex(vertex); + } + + return !(pRenderModule->isError()); + } + + ////////////////////////////////////////////////////////////////////////////////// + // Surface::drawWithClip + ////////////////////////////////////////////////////////////////////////////////// + + bool Surface::drawWithClip( double fAlpha, + const ::basegfx::B2DPoint& rPos, + const ::basegfx::B2DPolygon& rClipPoly, + const ::basegfx::B2DHomMatrix& rTransform ) + { + IRenderModuleSharedPtr pRenderModule(mpPageManager->getRenderModule()); + + RenderModuleGuard aGuard( pRenderModule ); + + prepareRendering(); + + // untransformed surface rectangle, relative to the whole + // image (note: this surface might actually only be a tile of + // the whole image, with non-zero maSourceOffset) + const double x1(maSourceOffset.getX()); + const double y1(maSourceOffset.getY()); + const double w(maSize.getX()); + const double h(maSize.getY()); + const double x2(x1+w); + const double y2(y1+h); + const ::basegfx::B2DRectangle aSurfaceClipRect(x1,y1,x2,y2); + + // concatenate transforms + // we use 'fround' here to avoid rounding errors. the vertices will + // be transformed by the overall transform and uv coordinates will + // be calculated from the result, and this is why we need to use + // integer coordinates here... + basegfx::B2DHomMatrix aTransform; + aTransform = aTransform * rTransform; + aTransform.translate(::basegfx::fround(rPos.getX()), + ::basegfx::fround(rPos.getY())); + + /* + ###################################### + ###################################### + ###################################### + + Y + ^+1 + | + 2 | 3 + x------------x + | | | + | | | + ------|-----O------|------>X + -1 | | | +1 + | | | + x------------x + 1 | 0 + | + |-1 + + ###################################### + ###################################### + ###################################### + */ + + // uv coordinates that map the surface rectangle + // to the destination rectangle. + const ::basegfx::B2DRectangle& rUV( getUVCoords() ); + + basegfx::B2DPolygon rTriangleList(basegfx::tools::clipTriangleListOnRange(rClipPoly, + aSurfaceClipRect)); + + // Push vertices to backend renderer + if(const sal_uInt32 nVertexCount = rTriangleList.count()) + { + canvas::Vertex vertex; + vertex.r = 1.0f; + vertex.g = 1.0f; + vertex.b = 1.0f; + vertex.a = static_cast<float>(fAlpha); + vertex.z = 0.0f; + +#if defined(TRIANGLE_LOG) && defined(DBG_UTIL) + OSL_TRACE( "Surface::draw(): numvertices %d numtriangles %d\n", + nVertexCount, + nVertexCount/3 ); +#endif + + pRenderModule->beginPrimitive( canvas::IRenderModule::PRIMITIVE_TYPE_TRIANGLE ); + + // issue an endPrimitive() when leaving the scope + const ::comphelper::ScopeGuard aScopeGuard( + boost::bind( &::canvas::IRenderModule::endPrimitive, + ::boost::ref(pRenderModule) ) ); + + for(sal_uInt32 nIndex=0; nIndex<nVertexCount; ++nIndex) + { + const basegfx::B2DPoint &aPoint = rTriangleList.getB2DPoint(nIndex); + basegfx::B2DPoint aTransformedPoint(aTransform * aPoint); + const double tu(((aPoint.getX()-aSurfaceClipRect.getMinX())*rUV.getWidth()/w)+rUV.getMinX()); + const double tv(((aPoint.getY()-aSurfaceClipRect.getMinY())*rUV.getHeight()/h)+rUV.getMinY()); + vertex.u=static_cast<float>(tu); + vertex.v=static_cast<float>(tv); + vertex.x=static_cast<float>(aTransformedPoint.getX()); + vertex.y=static_cast<float>(aTransformedPoint.getY()); + pRenderModule->pushVertex(vertex); + } + } + + return !(pRenderModule->isError()); + } + + ////////////////////////////////////////////////////////////////////////////////// + // Surface::prepareRendering + ////////////////////////////////////////////////////////////////////////////////// + + void Surface::prepareRendering() + { + mpPageManager->validatePages(); + + // clients requested to draw from this surface, therefore one + // of the above implemented concrete rendering operations + // was triggered. we therefore need to ask the pagemanager + // to allocate some space for the fragment we're dedicated to. + if(!(mpFragment)) + { + mpFragment = mpPageManager->allocateSpace(maSize); + if( mpFragment ) + { + mpFragment->setColorBuffer(mpColorBuffer); + mpFragment->setSourceOffset(maSourceOffset); + } + } + + if( mpFragment ) + { + // now we need to 'select' the fragment, which will in turn + // pull informations from the image on demand. + // in case this fragment is still not located on any of the + // available pages ['naked'], we force the page manager to + // do it now, no way to defer this any longer... + if(!(mpFragment->select(mbIsDirty))) + mpPageManager->nakedFragment(mpFragment); + + } + mbIsDirty=false; + } + + ////////////////////////////////////////////////////////////////////////////////// + // End of file + ////////////////////////////////////////////////////////////////////////////////// +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/tools/surface.hxx b/canvas/source/tools/surface.hxx new file mode 100644 index 000000000000..1238a97943ce --- /dev/null +++ b/canvas/source/tools/surface.hxx @@ -0,0 +1,165 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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. + * + ************************************************************************/ + +#ifndef INCLUDED_CANVAS_SURFACE_HXX +#define INCLUDED_CANVAS_SURFACE_HXX + +#include <basegfx/point/b2ipoint.hxx> +#include <basegfx/point/b2dpoint.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/range/b2drectangle.hxx> +#include <basegfx/vector/b2isize.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <canvas/rendering/irendermodule.hxx> +#include <canvas/rendering/icolorbuffer.hxx> +#include <canvas/rendering/isurface.hxx> + +#include "surfacerect.hxx" +#include "pagemanager.hxx" + +namespace canvas +{ + ////////////////////////////////////////////////////////////////////////////////// + // Surface + ////////////////////////////////////////////////////////////////////////////////// + + /** surfaces denote occupied areas withing pages. + + pages encapsulate the hardware buffers that + contain image data which can be used for texturing. + surfaces are areas within those pages. + */ + class Surface + { + public: + + Surface( const PageManagerSharedPtr& rPageManager, + const IColorBufferSharedPtr& rColorBuffer, + const ::basegfx::B2IPoint& rPos, + const ::basegfx::B2ISize& rSize ); + ~Surface(); + + void setColorBufferDirty(); + + /** Render the surface content to screen. + + @param fAlpha + Overall alpha for content + + @param rPos + Output position + + @param rTransform + Output transformation (does not affect output position) + */ + bool draw( double fAlpha, + const ::basegfx::B2DPoint& rPos, + const ::basegfx::B2DHomMatrix& rTransform ); + + /** Render the surface content to screen. + + @param fAlpha + Overall alpha for content + + @param rPos + Output position + + @param rArea + Subset of the surface to render. Coordinate system are + surface area pixel, given area will be clipped to the + surface bounds. + + @param rTransform + Output transformation (does not affect output position) + */ + bool drawRectangularArea( + double fAlpha, + const ::basegfx::B2DPoint& rPos, + const ::basegfx::B2DRange& rArea, + const ::basegfx::B2DHomMatrix& rTransform ); + + /** Render the surface content to screen. + + @param fAlpha + Overall alpha for content + + @param rPos + Output position + + @param rClipPoly + Clip polygon for the surface. The clip polygon is also + subject to the output transformation. + + @param rTransform + Output transformation (does not affect output position) + */ + bool drawWithClip( double fAlpha, + const ::basegfx::B2DPoint& rPos, + const ::basegfx::B2DPolygon& rClipPoly, + const ::basegfx::B2DHomMatrix& rTransform ); + + // private attributes + private: + IColorBufferSharedPtr mpColorBuffer; + + // invoking any of the above defined 'draw' methods + // will forward primitive commands to the rendermodule. + PageManagerSharedPtr mpPageManager; + + FragmentSharedPtr mpFragment; + + // the offset of this surface with regard to the source + // image. if the source image had to be tiled into multiple + // surfaces, this offset denotes the relative pixel distance + // from the source image's upper, left corner + ::basegfx::B2IPoint maSourceOffset; + + // the size in pixels of this surface. please note that + // this size is likely to be smaller than the size of + // the colorbuffer we're associated with since we + // maybe represent only a part of it. + ::basegfx::B2ISize maSize; + + bool mbIsDirty; + + // private methods + private: + bool refresh( canvas::IColorBuffer& rBuffer ) const; + void prepareRendering(); + + basegfx::B2DRectangle getUVCoords() const; + basegfx::B2DRectangle getUVCoords( const ::basegfx::B2IPoint& rPos, + const ::basegfx::B2ISize& rSize ) const; + }; + + typedef ::boost::shared_ptr< Surface > SurfaceSharedPtr; +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/tools/surfaceproxy.cxx b/canvas/source/tools/surfaceproxy.cxx new file mode 100644 index 000000000000..a73246cfe211 --- /dev/null +++ b/canvas/source/tools/surfaceproxy.cxx @@ -0,0 +1,185 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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_canvas.hxx" + +#include <boost/bind.hpp> +#include <basegfx/polygon/b2dpolygoncutandtouch.hxx> +#include <basegfx/polygon/b2dpolygontriangulator.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include "surfaceproxy.hxx" + +namespace canvas +{ + ////////////////////////////////////////////////////////////////////////////////// + // SurfaceProxy::SurfaceProxy + ////////////////////////////////////////////////////////////////////////////////// + + SurfaceProxy::SurfaceProxy( const canvas::IColorBufferSharedPtr& pBuffer, + const PageManagerSharedPtr& pPageManager ) : + mpPageManager( pPageManager ), + maSurfaceList(), + mpBuffer( pBuffer ) + { + const ::basegfx::B2ISize aImageSize(mpBuffer->getWidth(),mpBuffer->getHeight()); + const ::basegfx::B2ISize aPageSize(mpPageManager->getPageSize()); + const sal_Int32 aPageSizeX(aPageSize.getX()); + const sal_Int32 aPageSizeY(aPageSize.getY()); + const sal_Int32 aImageSizeX(aImageSize.getX()); + const sal_Int32 aImageSizeY(aImageSize.getY()); + + // see if the size of the colorbuffer is larger than the size + // of a single page. if this is the case we divide the + // colorbuffer into as many surfaces as we need to get the + // whole area distributed. otherwise (the colorbuffer is + // smaller than the size of a single page) we search for free + // pages or create a new one. + // the incoming image is too large to fit into a single + // page. strategy: we split the image into rectangular + // areas that are as large as the maximum page size + // dictates and follow the strategy for fitting images. + size_t dwNumSurfaces(0); + for(sal_Int32 y=0; y<aImageSizeY; y+=aPageSizeY) + for(sal_Int32 x=0; x<aImageSizeX; x+=aPageSizeX) + ++dwNumSurfaces; + maSurfaceList.reserve(dwNumSurfaces); + + for(sal_Int32 y=0; y<aImageSizeY; y+=aPageSizeY) + { + for(sal_Int32 x=0; x<aImageSizeX; x+=aPageSizeX) + { + // the current surface is located at the position [x,y] + // and has the size [min(restx,pagesizex),min(resty,pagesizey) + ::basegfx::B2IPoint aOffset(x,y); + ::basegfx::B2ISize aSize( ::std::min( aImageSize.getX()-x, + aPageSize.getX() ), + ::std::min( aImageSize.getY()-y, + aPageSize.getY() ) ); + + maSurfaceList.push_back( + SurfaceSharedPtr( + new Surface( + mpPageManager, + mpBuffer, + aOffset, + aSize))); + } + } + } + + ////////////////////////////////////////////////////////////////////////////////// + // SurfaceProxy::setColorBufferDirty + ////////////////////////////////////////////////////////////////////////////////// + + void SurfaceProxy::setColorBufferDirty() + { + ::std::for_each( maSurfaceList.begin(), + maSurfaceList.end(), + ::boost::mem_fn(&Surface::setColorBufferDirty)); + } + + ////////////////////////////////////////////////////////////////////////////////// + // SurfaceProxy::draw + ////////////////////////////////////////////////////////////////////////////////// + + bool SurfaceProxy::draw( double fAlpha, + const ::basegfx::B2DPoint& rPos, + const ::basegfx::B2DHomMatrix& rTransform ) + { + ::std::for_each( maSurfaceList.begin(), + maSurfaceList.end(), + ::boost::bind( &Surface::draw, + _1, + fAlpha, + ::boost::cref(rPos), + ::boost::cref(rTransform))); + + return true; + } + + ////////////////////////////////////////////////////////////////////////////////// + // SurfaceProxy::draw + ////////////////////////////////////////////////////////////////////////////////// + + bool SurfaceProxy::draw( double fAlpha, + const ::basegfx::B2DPoint& rPos, + const ::basegfx::B2DRange& rArea, + const ::basegfx::B2DHomMatrix& rTransform ) + { + ::std::for_each( maSurfaceList.begin(), + maSurfaceList.end(), + ::boost::bind(&Surface::drawRectangularArea, + _1, + fAlpha, + ::boost::cref(rPos), + ::boost::cref(rArea), + ::boost::cref(rTransform))); + + return true; + } + + ////////////////////////////////////////////////////////////////////////////////// + // SurfaceProxy::draw + ////////////////////////////////////////////////////////////////////////////////// + + bool SurfaceProxy::draw( double fAlpha, + const ::basegfx::B2DPoint& rPos, + const ::basegfx::B2DPolyPolygon& rClipPoly, + const ::basegfx::B2DHomMatrix& rTransform ) + { + const ::basegfx::B2DPolygon& rTriangulatedPolygon( + ::basegfx::triangulator::triangulate(rClipPoly)); + +#if defined(VERBOSE) && OSL_DEBUG_LEVEL > 0 + // dump polygons + OSL_TRACE( "Original clip polygon: %s\n" + "Triangulated polygon: %s\n", + rtl::OUStringToOString( + basegfx::tools::exportToSvgD( rClipPoly ), + RTL_TEXTENCODING_ASCII_US).getStr(), + rtl::OUStringToOString( + basegfx::tools::exportToSvgD( + basegfx::B2DPolyPolygon(rTriangulatedPolygon) ), + RTL_TEXTENCODING_ASCII_US).getStr() ); +#endif + + ::std::for_each( maSurfaceList.begin(), + maSurfaceList.end(), + ::boost::bind(&Surface::drawWithClip, + _1, + fAlpha, + ::boost::cref(rPos), + ::boost::cref(rTriangulatedPolygon), + ::boost::cref(rTransform))); + + return true; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/tools/surfaceproxy.hxx b/canvas/source/tools/surfaceproxy.hxx new file mode 100644 index 000000000000..a68a1ce46b1b --- /dev/null +++ b/canvas/source/tools/surfaceproxy.hxx @@ -0,0 +1,137 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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. + * + ************************************************************************/ + +#ifndef INCLUDED_CANVAS_SURFACEPROXY_HXX +#define INCLUDED_CANVAS_SURFACEPROXY_HXX + +#include <canvas/rendering/isurfaceproxy.hxx> +#include <canvas/rendering/icolorbuffer.hxx> + +#include "pagemanager.hxx" +#include "surface.hxx" + +namespace canvas +{ + + ////////////////////////////////////////////////////////////////////////////////// + // SurfaceProxy + ////////////////////////////////////////////////////////////////////////////////// + + /** Definition of the surface proxy class. + + Surface proxies are the connection between *one* source image + and *one or more* hardware surfaces (or textures). in a + logical structure surface proxies represent soley this + dependeny plus some simple cache management. + */ + class SurfaceProxy : public ISurfaceProxy + { + public: + + SurfaceProxy( const canvas::IColorBufferSharedPtr& pBuffer, + const PageManagerSharedPtr &pPageManager ); + + // ISurfaceProxy interface + virtual void setColorBufferDirty(); + + /** Render the surface content to screen. + + @param fAlpha + Overall alpha for content + + @param rPos + Output position + + @param rTransform + Output transformation (does not affect output position) + */ + virtual bool draw( double fAlpha, + const ::basegfx::B2DPoint& rPos, + const ::basegfx::B2DHomMatrix& rTransform ); + + /** Render the surface content to screen. + + @param fAlpha + Overall alpha for content + + @param rPos + Output position + + @param rArea + Subset of the surface to render. Coordinate system are + surface area pixel, given area will be clipped to the + surface bounds. + + @param rTransform + Output transformation (does not affect output position) + */ + virtual bool draw( double fAlpha, + const ::basegfx::B2DPoint& rPos, + const ::basegfx::B2DRange& rArea, + const ::basegfx::B2DHomMatrix& rTransform ); + + /** Render the surface content to screen. + + @param fAlpha + Overall alpha for content + + @param rPos + Output position + + @param rClipPoly + Clip polygon for the surface. The clip polygon is also + subject to the output transformation. + + @param rTransform + Output transformation (does not affect output position) + */ + virtual bool draw( double fAlpha, + const ::basegfx::B2DPoint& rPos, + const ::basegfx::B2DPolyPolygon& rClipPoly, + const ::basegfx::B2DHomMatrix& rTransform ); + + private: + PageManagerSharedPtr mpPageManager; + + // the pagemanager will distribute the image + // to one or more surfaces, this is why we + // need a list here. + std::vector<SurfaceSharedPtr> maSurfaceList; + + // pointer to the source of image data + // which always is stored in system memory, + // 32bit rgba and can have any size. + canvas::IColorBufferSharedPtr mpBuffer; + }; + + typedef ::boost::shared_ptr< SurfaceProxy > SurfaceProxySharedPtr; +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/tools/surfaceproxymanager.cxx b/canvas/source/tools/surfaceproxymanager.cxx new file mode 100644 index 000000000000..1b6e8fba8589 --- /dev/null +++ b/canvas/source/tools/surfaceproxymanager.cxx @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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_canvas.hxx" + +#include <canvas/rendering/isurfaceproxymanager.hxx> +#include <canvas/rendering/isurfaceproxy.hxx> +#include "surfaceproxy.hxx" + +namespace canvas +{ + + ////////////////////////////////////////////////////////////////////////////////// + // SurfaceProxyManager + ////////////////////////////////////////////////////////////////////////////////// + + class SurfaceProxyManager : public ISurfaceProxyManager + { + public: + + SurfaceProxyManager( const IRenderModuleSharedPtr pRenderModule ) : + mpPageManager( new PageManager(pRenderModule) ) + { + } + + /** the whole idea is build around the concept that you create + some arbitrary buffer which contains the image data and + tell the texture manager about it. from there on you can + draw this image using any kind of graphics api you want. + in the technical sense we allocate some space in local + videomemory or AGP memory which will be filled on demand, + which means if there exists any rendering operation that + needs to read from this memory location. this method + creates a logical hardware surface object which uses the + given color buffer as the image source. internally this + texture may be distributed to several real hardware + surfaces. + */ + virtual ISurfaceProxySharedPtr createSurfaceProxy( const IColorBufferSharedPtr& pBuffer ) const + { + // not much to do for now, simply allocate a new surface + // proxy from our internal pool and initialize this thing + // properly. we *don't* create a hardware surface for now. + return SurfaceProxySharedPtr(new SurfaceProxy(pBuffer,mpPageManager)); + } + + private: + PageManagerSharedPtr mpPageManager; + }; + + ////////////////////////////////////////////////////////////////////////////////// + // createSurfaceProxyManager + ////////////////////////////////////////////////////////////////////////////////// + + ISurfaceProxyManagerSharedPtr createSurfaceProxyManager( const IRenderModuleSharedPtr& rRenderModule ) + { + return ISurfaceProxyManagerSharedPtr( + new SurfaceProxyManager( + rRenderModule)); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/tools/surfacerect.hxx b/canvas/source/tools/surfacerect.hxx new file mode 100644 index 000000000000..67b5ea464fc0 --- /dev/null +++ b/canvas/source/tools/surfacerect.hxx @@ -0,0 +1,138 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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. + * + ************************************************************************/ + +#ifndef INCLUDED_CANVAS_SURFACERECT_HXX +#define INCLUDED_CANVAS_SURFACERECT_HXX + +#include <basegfx/point/b2ipoint.hxx> +#include <basegfx/vector/b2isize.hxx> + +namespace canvas +{ + ////////////////////////////////////////////////////////////////////////////////// + // SurfaceRect + ////////////////////////////////////////////////////////////////////////////////// + + struct SurfaceRect + { + ::basegfx::B2IPoint maPos; + ::basegfx::B2ISize maSize; + ::basegfx::B2IPoint maBackup; + bool bEnabled; + + explicit SurfaceRect( const ::basegfx::B2ISize &rSize ) : + maPos(), + maSize(rSize), + maBackup(), + bEnabled(true) + { + } + + // coordinates contained in this rectangle are + // constrained to the following rules: + // 1) p.x >= pos.x + // 2) p.x <= pos.x+size.x + // 3) p.y >= pos.y + // 4) p.y <= pos.y+size.y + // in other words, 'size' means the number of pixels + // this rectangle encloses plus one. for example with pos[0,0] + // and size[512,512], p[512,512] would return inside. + // a size of [0,0] therefore denotes a one-by-one rectangle. + bool pointInside( sal_Int32 px, sal_Int32 py ) const + { + const sal_Int32 x1(maPos.getX()); + const sal_Int32 y1(maPos.getY()); + const sal_Int32 x2(maPos.getX()+maSize.getX()); + const sal_Int32 y2(maPos.getY()+maSize.getY()); + if(px < x1) return false; + if(px >= x2) return false; + if(py < y1) return false; + if(py >= y2) return false; + return true; + } + + // returns true if the horizontal line intersects the rect. + bool hLineIntersect( sal_Int32 lx1, sal_Int32 lx2, sal_Int32 ly ) const + { + const sal_Int32 x1(maPos.getX()); + const sal_Int32 y1(maPos.getY()); + const sal_Int32 x2(maPos.getX()+maSize.getX()); + const sal_Int32 y2(maPos.getY()+maSize.getY()); + if(ly < y1) return false; + if(ly >= y2) return false; + if((lx1 < x1) && (lx2 < x1)) return false; + if((lx1 >= x2) && (lx2 >= x2)) return false; + return true; + } + + //! Returns true if the vertical line intersects the rect. + bool vLineIntersect( sal_Int32 lx, sal_Int32 ly1, sal_Int32 ly2 ) const + { + const sal_Int32 x1(maPos.getX()); + const sal_Int32 y1(maPos.getY()); + const sal_Int32 x2(maPos.getX()+maSize.getX()); + const sal_Int32 y2(maPos.getY()+maSize.getY()); + if(lx < x1) return false; + if(lx >= x2) return false; + if((ly1 < y1) && (ly2 < y1)) return false; + if((ly1 >= y2) && (ly2 >= y2)) return false; + return true; + } + + // returns true if the passed rect intersects this one. + bool intersection( const SurfaceRect& r ) const + { + const sal_Int32 x1(maPos.getX()); + const sal_Int32 y1(maPos.getY()); + const sal_Int32 x2(maPos.getX()+maSize.getX()); + const sal_Int32 y2(maPos.getY()+maSize.getY()); + if(r.hLineIntersect(x1,x2,y1)) return true; + if(r.hLineIntersect(x1,x2,y2)) return true; + if(r.vLineIntersect(x1,y1,y2)) return true; + if(r.vLineIntersect(x2,y1,y2)) return true; + return false; + } + + bool inside( const SurfaceRect& r ) const + { + const sal_Int32 x1(maPos.getX()); + const sal_Int32 y1(maPos.getY()); + const sal_Int32 x2(maPos.getX()+maSize.getX()); + const sal_Int32 y2(maPos.getY()+maSize.getY()); + if(!(r.pointInside(x1,y1))) return false; + if(!(r.pointInside(x2,y1))) return false; + if(!(r.pointInside(x2,y2))) return false; + if(!(r.pointInside(x1,y2))) return false; + return true; + } + }; +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/canvas/source/tools/verifyinput.cxx b/canvas/source/tools/verifyinput.cxx new file mode 100644 index 000000000000..48a3b9bb6531 --- /dev/null +++ b/canvas/source/tools/verifyinput.cxx @@ -0,0 +1,929 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * 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_canvas.hxx" + +#include <com/sun/star/geometry/AffineMatrix2D.hpp> +#include <com/sun/star/geometry/Matrix2D.hpp> +#include <com/sun/star/geometry/RealPoint2D.hpp> +#include <com/sun/star/geometry/RealSize2D.hpp> +#include <com/sun/star/geometry/IntegerPoint2D.hpp> +#include <com/sun/star/geometry/IntegerSize2D.hpp> +#include <com/sun/star/geometry/RealRectangle2D.hpp> +#include <com/sun/star/geometry/RealBezierSegment2D.hpp> +#include <com/sun/star/rendering/RenderState.hpp> +#include <com/sun/star/rendering/ViewState.hpp> +#include <com/sun/star/rendering/XCanvas.hpp> +#include <com/sun/star/rendering/CompositeOperation.hpp> +#include <com/sun/star/rendering/TexturingMode.hpp> +#include <com/sun/star/util/Endianness.hpp> +#include <com/sun/star/rendering/PathCapType.hpp> +#include <com/sun/star/rendering/PathJoinType.hpp> +#include <com/sun/star/rendering/IntegerBitmapLayout.hpp> +#include <com/sun/star/rendering/FloatingPointBitmapFormat.hpp> +#include <com/sun/star/rendering/FloatingPointBitmapLayout.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> + +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/range/b2drange.hxx> +#include <basegfx/range/b2irange.hxx> +#include <basegfx/range/b2drectangle.hxx> +#include <basegfx/point/b2dpoint.hxx> +#include <basegfx/tools/canvastools.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> + +#include <canvas/verifyinput.hxx> +#include <canvas/canvastools.hxx> + + +using namespace ::com::sun::star; + +namespace canvas +{ + namespace tools + { + void verifyInput( const geometry::RealPoint2D& rPoint, + const char* pStr, + const uno::Reference< uno::XInterface >& xIf, + ::sal_Int16 nArgPos ) + { + (void)pStr; (void)xIf; (void)nArgPos; + +#if OSL_DEBUG_LEVEL > 0 + if( !::rtl::math::isFinite( rPoint.X ) ) + { + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): point X value contains infinite or NAN"), + xIf, + nArgPos ); + } + + if( !::rtl::math::isFinite( rPoint.Y ) ) + { + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): point X value contains infinite or NAN"), + xIf, + nArgPos ); + } +#else + if( !::rtl::math::isFinite( rPoint.X ) || + !::rtl::math::isFinite( rPoint.Y ) ) + { + throw lang::IllegalArgumentException(); + } +#endif + } + + void verifyInput( const geometry::RealSize2D& rSize, + const char* pStr, + const uno::Reference< uno::XInterface >& xIf, + ::sal_Int16 nArgPos ) + { + (void)pStr; (void)xIf; (void)nArgPos; + +#if OSL_DEBUG_LEVEL > 0 + if( !::rtl::math::isFinite( rSize.Width ) ) + { + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): size.Width value contains infinite or NAN"), + xIf, + nArgPos ); + } + + if( !::rtl::math::isFinite( rSize.Height ) ) + { + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): size.Height value contains infinite or NAN"), + xIf, + nArgPos ); + } +#else + if( !::rtl::math::isFinite( rSize.Width ) || + !::rtl::math::isFinite( rSize.Height ) ) + { + throw lang::IllegalArgumentException(); + } +#endif + } + + void verifyInput( const geometry::RealBezierSegment2D& rSegment, + const char* pStr, + const uno::Reference< uno::XInterface >& xIf, + ::sal_Int16 nArgPos ) + { + (void)pStr; (void)xIf; (void)nArgPos; + +#if OSL_DEBUG_LEVEL > 0 + if( !::rtl::math::isFinite( rSegment.Px ) ) + { + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): bezier segment's Px value contains infinite or NAN"), + xIf, + nArgPos ); + } + + if( !::rtl::math::isFinite( rSegment.Py ) ) + { + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): bezier segment's Py value contains infinite or NAN"), + xIf, + nArgPos ); + } + + if( !::rtl::math::isFinite( rSegment.C1x ) ) + { + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): bezier segment's C1x value contains infinite or NAN"), + xIf, + nArgPos ); + } + + if( !::rtl::math::isFinite( rSegment.C1y ) ) + { + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): bezier segment's C1y value contains infinite or NAN"), + xIf, + nArgPos ); + } + + if( !::rtl::math::isFinite( rSegment.C2x ) ) + { + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): bezier segment's C2x value contains infinite or NAN"), + xIf, + nArgPos ); + } + + if( !::rtl::math::isFinite( rSegment.C2y ) ) + { + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): bezier segment's C2y value contains infinite or NAN"), + xIf, + nArgPos ); + } +#else + if( !::rtl::math::isFinite( rSegment.Px ) || + !::rtl::math::isFinite( rSegment.Py ) || + !::rtl::math::isFinite( rSegment.C1x ) || + !::rtl::math::isFinite( rSegment.C1y ) || + !::rtl::math::isFinite( rSegment.C2x ) || + !::rtl::math::isFinite( rSegment.C2y ) ) + { + throw lang::IllegalArgumentException(); + } +#endif + } + + void verifyInput( const geometry::RealRectangle2D& rRect, + const char* pStr, + const uno::Reference< uno::XInterface >& xIf, + ::sal_Int16 nArgPos ) + { + (void)pStr; (void)xIf; (void)nArgPos; + +#if OSL_DEBUG_LEVEL > 0 + if( !::rtl::math::isFinite( rRect.X1 ) ) + { + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): rectangle point X1 contains infinite or NAN"), + xIf, + nArgPos ); + } + + if( !::rtl::math::isFinite( rRect.Y1 ) ) + { + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): rectangle point Y1 contains infinite or NAN"), + xIf, + nArgPos ); + } + + if( !::rtl::math::isFinite( rRect.X2 ) ) + { + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): rectangle point X2 contains infinite or NAN"), + xIf, + nArgPos ); + } + + if( !::rtl::math::isFinite( rRect.Y2 ) ) + { + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): rectangle point Y2 contains infinite or NAN"), + xIf, + nArgPos ); + } +#else + if( !::rtl::math::isFinite( rRect.X1 ) || + !::rtl::math::isFinite( rRect.Y1 ) || + !::rtl::math::isFinite( rRect.X2 ) || + !::rtl::math::isFinite( rRect.Y2 ) ) + { + throw lang::IllegalArgumentException(); + } +#endif + } + + void verifyInput( const geometry::AffineMatrix2D& matrix, + const char* pStr, + const uno::Reference< uno::XInterface >& xIf, + ::sal_Int16 nArgPos ) + { + (void)pStr; (void)xIf; (void)nArgPos; + +#if OSL_DEBUG_LEVEL > 0 + const sal_Int32 nBinaryState( + 100000 * !::rtl::math::isFinite( matrix.m00 ) + + 10000 * !::rtl::math::isFinite( matrix.m01 ) + + 1000 * !::rtl::math::isFinite( matrix.m02 ) + + 100 * !::rtl::math::isFinite( matrix.m10 ) + + 10 * !::rtl::math::isFinite( matrix.m11 ) + + 1 * !::rtl::math::isFinite( matrix.m12 ) ); + + if( nBinaryState ) + { + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): AffineMatrix2D contains infinite or NAN value(s) at the following positions (m00-m12): ") + + ::rtl::OUString::valueOf(nBinaryState), + xIf, + nArgPos ); + } +#else + if( !::rtl::math::isFinite( matrix.m00 ) || + !::rtl::math::isFinite( matrix.m01 ) || + !::rtl::math::isFinite( matrix.m02 ) || + !::rtl::math::isFinite( matrix.m10 ) || + !::rtl::math::isFinite( matrix.m11 ) || + !::rtl::math::isFinite( matrix.m12 ) ) + { + throw lang::IllegalArgumentException(); + } +#endif + } + + void verifyInput( const geometry::Matrix2D& matrix, + const char* pStr, + const uno::Reference< uno::XInterface >& xIf, + ::sal_Int16 nArgPos ) + { + (void)pStr; (void)xIf; (void)nArgPos; + +#if OSL_DEBUG_LEVEL > 0 + const sal_Int32 nBinaryState( + 1000 * !::rtl::math::isFinite( matrix.m00 ) + + 100 * !::rtl::math::isFinite( matrix.m01 ) + + 10 * !::rtl::math::isFinite( matrix.m10 ) + + 1 * !::rtl::math::isFinite( matrix.m11 ) ); + + if( nBinaryState ) + { + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): Matrix2D contains infinite or NAN value(s) at the following positions (m00-m11): ") + + ::rtl::OUString::valueOf(nBinaryState), + xIf, + nArgPos ); + } +#else + if( !::rtl::math::isFinite( matrix.m00 ) || + !::rtl::math::isFinite( matrix.m01 ) || + !::rtl::math::isFinite( matrix.m10 ) || + !::rtl::math::isFinite( matrix.m11 ) ) + { + throw lang::IllegalArgumentException(); + } +#endif + } + + void verifyInput( const rendering::ViewState& viewState, + const char* pStr, + const uno::Reference< uno::XInterface >& xIf, + ::sal_Int16 nArgPos ) + { + verifyInput( viewState.AffineTransform, + pStr, xIf, nArgPos ); + } + + void verifyInput( const rendering::RenderState& renderState, + const char* pStr, + const uno::Reference< uno::XInterface >& xIf, + ::sal_Int16 nArgPos, + sal_Int32 nMinColorComponents ) + { + verifyInput( renderState.AffineTransform, + pStr, xIf, nArgPos ); + + if( renderState.DeviceColor.getLength() < nMinColorComponents ) + { +#if OSL_DEBUG_LEVEL > 0 + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): render state's device color has too few components (") + + ::rtl::OUString::valueOf(nMinColorComponents) + + ::rtl::OUString::createFromAscii(" expected, ") + + ::rtl::OUString::valueOf(renderState.DeviceColor.getLength()) + + ::rtl::OUString::createFromAscii(" provided)"), + xIf, + nArgPos ); +#else + throw lang::IllegalArgumentException(); +#endif + } + + if( renderState.CompositeOperation < rendering::CompositeOperation::CLEAR || + renderState.CompositeOperation > rendering::CompositeOperation::SATURATE ) + { +#if OSL_DEBUG_LEVEL > 0 + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): render state's CompositeOperation value out of range (") + + ::rtl::OUString::valueOf(sal::static_int_cast<sal_Int32>(renderState.CompositeOperation)) + + ::rtl::OUString::createFromAscii(" not known)"), + xIf, + nArgPos ); +#else + throw lang::IllegalArgumentException(); +#endif + } + } + + void verifyInput( const rendering::Texture& texture, + const char* pStr, + const uno::Reference< uno::XInterface >& xIf, + ::sal_Int16 nArgPos ) + { + verifyInput( texture.AffineTransform, + pStr, xIf, nArgPos ); + + if( !::rtl::math::isFinite( texture.Alpha ) || + texture.Alpha < 0.0 || + texture.Alpha > 1.0 ) + { +#if OSL_DEBUG_LEVEL > 0 + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): textures' alpha value out of range (is ") + + ::rtl::OUString::valueOf(texture.Alpha) + + ::rtl::OUString::createFromAscii(")"), + xIf, + nArgPos ); +#else + throw lang::IllegalArgumentException(); +#endif + } + + if( texture.NumberOfHatchPolygons < 0 ) + { +#if OSL_DEBUG_LEVEL > 0 + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): textures' NumberOfHatchPolygons is negative"), + xIf, + nArgPos ); +#else + throw lang::IllegalArgumentException(); +#endif + } + + if( texture.RepeatModeX < rendering::TexturingMode::NONE || + texture.RepeatModeX > rendering::TexturingMode::REPEAT ) + { +#if OSL_DEBUG_LEVEL > 0 + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): textures' RepeatModeX value is out of range (") + + ::rtl::OUString::valueOf(sal::static_int_cast<sal_Int32>(texture.RepeatModeX)) + + ::rtl::OUString::createFromAscii(" not known)"), + xIf, + nArgPos ); +#else + throw lang::IllegalArgumentException(); +#endif + } + + if( texture.RepeatModeY < rendering::TexturingMode::NONE || + texture.RepeatModeY > rendering::TexturingMode::REPEAT ) + { +#if OSL_DEBUG_LEVEL > 0 + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): textures' RepeatModeY value is out of range (") + + ::rtl::OUString::valueOf(sal::static_int_cast<sal_Int32>(texture.RepeatModeY)) + + ::rtl::OUString::createFromAscii(" not known)"), + xIf, + nArgPos ); +#else + throw lang::IllegalArgumentException(); +#endif + } + } + + namespace + { + struct VerifyDashValue + { + VerifyDashValue( const char* pStr, + const uno::Reference< uno::XInterface >& xIf, + ::sal_Int16 nArgPos ) : + mpStr( pStr ), + mrIf( xIf ), + mnArgPos( nArgPos ) + { + } + + void operator()( const double& rVal ) + { + if( !::rtl::math::isFinite( rVal ) || rVal < 0.0 ) + { +#if OSL_DEBUG_LEVEL > 0 + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(mpStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): one of stroke attributes' DashArray value out of range (is ") + + ::rtl::OUString::valueOf(rVal) + + ::rtl::OUString::createFromAscii(")"), + mrIf, + mnArgPos ); +#else + throw lang::IllegalArgumentException(); +#endif + } + } + + const char* mpStr; + const uno::Reference< uno::XInterface >& mrIf; + sal_Int16 mnArgPos; + }; + } + + void verifyInput( const rendering::StrokeAttributes& strokeAttributes, + const char* pStr, + const uno::Reference< uno::XInterface >& xIf, + ::sal_Int16 nArgPos ) + { + if( !::rtl::math::isFinite( strokeAttributes.StrokeWidth ) || + strokeAttributes.StrokeWidth < 0.0 ) + { +#if OSL_DEBUG_LEVEL > 0 + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): stroke attributes' StrokeWidth value out of range (is ") + + ::rtl::OUString::valueOf(strokeAttributes.StrokeWidth) + + ::rtl::OUString::createFromAscii(")"), + xIf, + nArgPos ); +#else + throw lang::IllegalArgumentException(); +#endif + } + + if( !::rtl::math::isFinite( strokeAttributes.MiterLimit ) || + strokeAttributes.MiterLimit < 0.0 ) + { +#if OSL_DEBUG_LEVEL > 0 + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): stroke attributes' MiterLimit value out of range (is ") + + ::rtl::OUString::valueOf(strokeAttributes.MiterLimit) + + ::rtl::OUString::createFromAscii(")"), + xIf, + nArgPos ); +#else + throw lang::IllegalArgumentException(); +#endif + } + + ::std::for_each( strokeAttributes.DashArray.getConstArray(), + strokeAttributes.DashArray.getConstArray() + strokeAttributes.DashArray.getLength(), + VerifyDashValue( pStr, xIf, nArgPos ) ); + + ::std::for_each( strokeAttributes.LineArray.getConstArray(), + strokeAttributes.LineArray.getConstArray() + strokeAttributes.LineArray.getLength(), + VerifyDashValue( pStr, xIf, nArgPos ) ); + + if( strokeAttributes.StartCapType < rendering::PathCapType::BUTT || + strokeAttributes.StartCapType > rendering::PathCapType::SQUARE ) + { +#if OSL_DEBUG_LEVEL > 0 + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): stroke attributes' StartCapType value is out of range (") + + ::rtl::OUString::valueOf(sal::static_int_cast<sal_Int32>(strokeAttributes.StartCapType)) + + ::rtl::OUString::createFromAscii(" not known)"), + xIf, + nArgPos ); +#else + throw lang::IllegalArgumentException(); +#endif + } + + if( strokeAttributes.EndCapType < rendering::PathCapType::BUTT || + strokeAttributes.EndCapType > rendering::PathCapType::SQUARE ) + { +#if OSL_DEBUG_LEVEL > 0 + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): stroke attributes' StartCapType value is out of range (") + + ::rtl::OUString::valueOf(sal::static_int_cast<sal_Int32>(strokeAttributes.EndCapType)) + + ::rtl::OUString::createFromAscii(" not known)"), + xIf, + nArgPos ); +#else + throw lang::IllegalArgumentException(); +#endif + } + + if( strokeAttributes.JoinType < rendering::PathJoinType::NONE || + strokeAttributes.JoinType > rendering::PathJoinType::BEVEL ) + { +#if OSL_DEBUG_LEVEL > 0 + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): stroke attributes' JoinType value is out of range (") + + ::rtl::OUString::valueOf(sal::static_int_cast<sal_Int32>(strokeAttributes.JoinType)) + + ::rtl::OUString::createFromAscii(" not known)"), + xIf, + nArgPos ); +#else + throw lang::IllegalArgumentException(); +#endif + } + } + + void verifyInput( const rendering::IntegerBitmapLayout& bitmapLayout, + const char* pStr, + const uno::Reference< uno::XInterface >& xIf, + ::sal_Int16 nArgPos ) + { + (void)pStr; (void)xIf; (void)nArgPos; + + if( bitmapLayout.ScanLines < 0 ) + { +#if OSL_DEBUG_LEVEL > 0 + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): bitmap layout's ScanLines is negative"), + xIf, + nArgPos ); +#else + throw lang::IllegalArgumentException(); +#endif + } + + if( bitmapLayout.ScanLineBytes < 0 ) + { +#if OSL_DEBUG_LEVEL > 0 + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): bitmap layout's ScanLineBytes is negative"), + xIf, + nArgPos ); +#else + throw lang::IllegalArgumentException(); +#endif + } + + if( !bitmapLayout.ColorSpace.is() ) + { +#if OSL_DEBUG_LEVEL > 0 + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): bitmap layout's ColorSpace is invalid"), + xIf, + nArgPos ); +#else + throw lang::IllegalArgumentException(); +#endif + } + else + { + if( bitmapLayout.ColorSpace->getBitsPerPixel() < 0 ) + { +#if OSL_DEBUG_LEVEL > 0 + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): bitmap layout's ColorSpace getBitsPerPixel() is negative"), + xIf, + nArgPos ); +#else + throw lang::IllegalArgumentException(); +#endif + } + + if( bitmapLayout.ColorSpace->getEndianness() < util::Endianness::LITTLE || + bitmapLayout.ColorSpace->getEndianness() > util::Endianness::BIG ) + { +#if OSL_DEBUG_LEVEL > 0 + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): bitmap layout's ColorSpace getEndianness() value is out of range (") + + ::rtl::OUString::valueOf(sal::static_int_cast<sal_Int32>(bitmapLayout.ColorSpace->getEndianness())) + + ::rtl::OUString::createFromAscii(" not known)"), + xIf, + nArgPos ); +#else + throw lang::IllegalArgumentException(); +#endif + } + } + } + + void verifyInput( const rendering::FloatingPointBitmapLayout& bitmapLayout, + const char* pStr, + const uno::Reference< uno::XInterface >& xIf, + ::sal_Int16 nArgPos ) + { + (void)pStr; (void)xIf; (void)nArgPos; + + if( bitmapLayout.ScanLines < 0 ) + { +#if OSL_DEBUG_LEVEL > 0 + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): bitmap layout's ScanLines is negative"), + xIf, + nArgPos ); +#else + throw lang::IllegalArgumentException(); +#endif + } + + if( bitmapLayout.ScanLineBytes < 0 ) + { +#if OSL_DEBUG_LEVEL > 0 + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): bitmap layout's ScanLineBytes is negative"), + xIf, + nArgPos ); +#else + throw lang::IllegalArgumentException(); +#endif + } + + if( !bitmapLayout.ColorSpace.is() ) + { +#if OSL_DEBUG_LEVEL > 0 + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): bitmap layout's ColorSpace is invalid"), + xIf, + nArgPos ); +#else + throw lang::IllegalArgumentException(); +#endif + } + + if( bitmapLayout.NumComponents < 0 ) + { +#if OSL_DEBUG_LEVEL > 0 + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): bitmap layout's NumComponents is negative"), + xIf, + nArgPos ); +#else + throw lang::IllegalArgumentException(); +#endif + } + + if( bitmapLayout.Endianness < util::Endianness::LITTLE || + bitmapLayout.Endianness > util::Endianness::BIG ) + { +#if OSL_DEBUG_LEVEL > 0 + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): bitmap layout's Endianness value is out of range (") + + ::rtl::OUString::valueOf(sal::static_int_cast<sal_Int32>(bitmapLayout.Endianness)) + + ::rtl::OUString::createFromAscii(" not known)"), + xIf, + nArgPos ); +#else + throw lang::IllegalArgumentException(); +#endif + } + + if( bitmapLayout.Format < rendering::FloatingPointBitmapFormat::HALFFLOAT || + bitmapLayout.Format > rendering::FloatingPointBitmapFormat::DOUBLE ) + { +#if OSL_DEBUG_LEVEL > 0 + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): bitmap layout's Format value is out of range (") + + ::rtl::OUString::valueOf(sal::static_int_cast<sal_Int32>(bitmapLayout.Format)) + + ::rtl::OUString::createFromAscii(" not known)"), + xIf, + nArgPos ); +#else + throw lang::IllegalArgumentException(); +#endif + } + } + + void verifyInput( const rendering::FontInfo& /*fontInfo*/, + const char* /*pStr*/, + const uno::Reference< uno::XInterface >& /*xIf*/, + ::sal_Int16 /*nArgPos*/ ) + { + // TODO(E3): Implement FontDescription checks, once the + // Panose stuff is ready. + } + + void verifyInput( const rendering::FontRequest& fontRequest, + const char* pStr, + const uno::Reference< uno::XInterface >& xIf, + ::sal_Int16 nArgPos ) + { + verifyInput( fontRequest.FontDescription, + pStr, xIf, nArgPos ); + + if( !::rtl::math::isFinite( fontRequest.CellSize ) ) + { +#if OSL_DEBUG_LEVEL > 0 + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): font request's CellSize value contains infinite or NAN"), + xIf, + nArgPos ); +#else + throw lang::IllegalArgumentException(); +#endif + } + + if( !::rtl::math::isFinite( fontRequest.ReferenceAdvancement ) ) + { +#if OSL_DEBUG_LEVEL > 0 + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): font request's ReferenceAdvancement value contains infinite or NAN"), + xIf, + nArgPos ); +#else + throw lang::IllegalArgumentException(); +#endif + } + + if( fontRequest.CellSize != 0.0 && + fontRequest.ReferenceAdvancement != 0.0 ) + { +#if OSL_DEBUG_LEVEL > 0 + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyInput(): font request's CellSize and ReferenceAdvancement are mutually exclusive, one of them must be 0.0"), + xIf, + nArgPos ); +#else + throw lang::IllegalArgumentException(); +#endif + } + } + + void verifyIndexRange( const geometry::IntegerRectangle2D& rect, + const geometry::IntegerSize2D& size ) + { + const ::basegfx::B2IRange aRect( + ::basegfx::unotools::b2IRectangleFromIntegerRectangle2D( + rect ) ); + + if( aRect.getMinX() < 0 || + aRect.getMaxX() > size.Width || + aRect.getMinY() < 0 || + aRect.getMaxY() > size.Height ) + { + throw ::com::sun::star::lang::IndexOutOfBoundsException(); + } + } + + void verifyIndexRange( const geometry::IntegerPoint2D& pos, + const geometry::IntegerSize2D& size ) + { + if( pos.X < 0 || + pos.X > size.Width || + pos.Y < 0 || + pos.Y > size.Height ) + { + throw ::com::sun::star::lang::IndexOutOfBoundsException(); + } + } + + void verifyBitmapSize( const geometry::IntegerSize2D& size, + const char* pStr, + const uno::Reference< uno::XInterface >& xIf ) + { + (void)pStr; (void)xIf; + + if( size.Width <= 0 ) + { +#if OSL_DEBUG_LEVEL > 0 + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyBitmapSize(): size has 0 or negative width (value: ") + + ::rtl::OUString::valueOf(size.Width) + + ::rtl::OUString::createFromAscii(")"), + xIf, + 0 ); +#else + throw lang::IllegalArgumentException(); +#endif + } + + if( size.Height <= 0 ) + { +#if OSL_DEBUG_LEVEL > 0 + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifyBitmapSize(): size has 0 or negative height (value: ") + + ::rtl::OUString::valueOf(size.Height) + + ::rtl::OUString::createFromAscii(")"), + xIf, + 0 ); +#else + throw lang::IllegalArgumentException(); +#endif + } + } + + void verifySpriteSize( const geometry::RealSize2D& size, + const char* pStr, + const uno::Reference< uno::XInterface >& xIf ) + { + (void)pStr; (void)xIf; + + if( size.Width <= 0.0 ) + { +#if OSL_DEBUG_LEVEL > 0 + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifySpriteSize(): size has 0 or negative width (value: ") + + ::rtl::OUString::valueOf(size.Width) + + ::rtl::OUString::createFromAscii(")"), + xIf, + 0 ); +#else + throw lang::IllegalArgumentException(); +#endif + } + + if( size.Height <= 0.0 ) + { +#if OSL_DEBUG_LEVEL > 0 + throw lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii(pStr) + + ::rtl::OUString::createFromAscii(": verifySpriteSize(): size has 0 or negative height (value: ") + + ::rtl::OUString::valueOf(size.Height) + + ::rtl::OUString::createFromAscii(")"), + xIf, + 0 ); +#else + throw lang::IllegalArgumentException(); +#endif + } + } + + + } // namespace tools + +} // namespace canvas + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |