diff options
Diffstat (limited to 'canvas/source/tools')
26 files changed, 9490 insertions, 0 deletions
diff --git a/canvas/source/tools/bitmap.cxx b/canvas/source/tools/bitmap.cxx new file mode 100644 index 000000000000..eb7732b9a8f6 --- /dev/null +++ b/canvas/source/tools/bitmap.cxx @@ -0,0 +1,838 @@ +/************************************************************************* + * + * 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/rendering/bitmap.hxx> +#include <canvas/rendering/isurfaceproxy.hxx> +#include <basegfx/vector/b2isize.hxx> +#include "image.hxx" +#include <algorithm> +#include <iterator> + + +using namespace ::com::sun::star; + +namespace canvas +{ + class ImplBitmap + { + public: + ImplBitmap( const ::basegfx::B2IVector& rSize, + const ISurfaceProxyManagerSharedPtr& rMgr, + bool bWithAlpha ) : + mpImage(), + mpSurfaceProxy(), + mbIsSurfaceDirty( true ) + { + ENSURE_OR_THROW( rMgr, + "Bitmap::Bitmap(): Invalid surface proxy manager" ); + + Image::Description desc; + + desc.eFormat = bWithAlpha ? Image::FMT_A8R8G8B8 : Image::FMT_R8G8B8; + desc.nWidth = rSize.getX(); + desc.nHeight = rSize.getY(); + desc.nStride = 0; + desc.pBuffer = NULL; + + mpImage.reset( new Image(desc) ); + + // clear the surface [fill with opaque black] + mpImage->clear(0,255,255,255); + + // create a (possibly hardware accelerated) mirror surface. + mpSurfaceProxy = rMgr->createSurfaceProxy( mpImage ); + } + + bool hasAlpha() const + { + if( !mpImage ) + return false; + + return (mpImage->getDescription().eFormat == Image::FMT_A8R8G8B8); + } + + ::basegfx::B2IVector getSize() const + { + return ::basegfx::B2IVector( mpImage->getWidth(), + mpImage->getHeight() ); + } + + ::com::sun::star::uno::Sequence< sal_Int8 > getData( ::com::sun::star::rendering::IntegerBitmapLayout& /*bitmapLayout*/, + const ::com::sun::star::geometry::IntegerRectangle2D& rect ) + { + const IColorBuffer::Format format = mpImage->getFormat(); + const sal_uInt32 dwNumBytesPerPixel = getNumBytes(format); + const sal_uInt32 dwPitch = mpImage->getWidth()*dwNumBytesPerPixel; + + if(!(dwNumBytesPerPixel)) + return uno::Sequence< sal_Int8 >(); + + const sal_uInt32 dwWidth = rect.X2-rect.X1; + const sal_uInt32 dwHeight = rect.Y2-rect.Y1; + + uno::Sequence< sal_Int8 > aRes(dwWidth*dwHeight*4); + sal_uInt8 *pDst = reinterpret_cast<sal_uInt8 *>(aRes.getArray()); + + const sal_uInt8 *pSrc = mpImage->lock()+(rect.Y1*dwPitch)+(rect.X1*dwNumBytesPerPixel); + const sal_uInt32 dwSpanSizeInBytes = dwNumBytesPerPixel*dwWidth; + for(sal_uInt32 y=0; y<dwHeight; ++y) + { + rtl_copyMemory(pDst,pSrc,dwSpanSizeInBytes); + pDst += dwSpanSizeInBytes; + pSrc += dwPitch; + } + mpImage->unlock(); + + return aRes; + } + + void setData( const ::com::sun::star::uno::Sequence< sal_Int8 >& data, + const ::com::sun::star::rendering::IntegerBitmapLayout& /*bitmapLayout*/, + const ::com::sun::star::geometry::IntegerRectangle2D& rect ) + { + const IColorBuffer::Format format = mpImage->getFormat(); + const sal_uInt32 dwNumBytesPerPixel = getNumBytes(format); + const sal_uInt32 dwPitch = mpImage->getWidth()*dwNumBytesPerPixel; + + if(!(dwNumBytesPerPixel)) + return; + + const sal_uInt32 dwWidth = rect.X2-rect.X1; + const sal_uInt32 dwHeight = rect.Y2-rect.Y1; + const sal_uInt8 *pSrc = reinterpret_cast<const sal_uInt8 *>(data.getConstArray()); + + sal_uInt8 *pDst = mpImage->lock()+(rect.Y1*dwPitch)+(rect.X1*dwNumBytesPerPixel); + const sal_uInt32 dwSpanSizeInBytes = dwNumBytesPerPixel*dwWidth; + for(sal_uInt32 y=0; y<dwHeight; ++y) + { + rtl_copyMemory(pDst,pSrc,dwSpanSizeInBytes); + pSrc += dwSpanSizeInBytes; + pDst += dwPitch; + } + mpImage->unlock(); + } + + void setPixel( const ::com::sun::star::uno::Sequence< sal_Int8 >& color, + const ::com::sun::star::rendering::IntegerBitmapLayout& /*bitmapLayout*/, + const ::com::sun::star::geometry::IntegerPoint2D& pos ) + { + struct ARGBColor + { + sal_uInt8 a; + sal_uInt8 r; + sal_uInt8 g; + sal_uInt8 b; + }; + + union ARGB + { + ARGBColor Color; + sal_uInt32 color; + + inline ARGB( sal_uInt32 _color ) : color(_color) {} + }; + + ARGB argb(0xFFFFFFFF); + + if(color.getLength() > 2) + { + argb.Color.r = static_cast<sal_uInt8>(color[0]); + argb.Color.g = static_cast<sal_uInt8>(color[1]); + argb.Color.b = static_cast<sal_uInt8>(color[2]); + if(color.getLength() > 3) + argb.Color.a = static_cast<sal_uInt8>(255.0f*color[3]); + } + + const IColorBuffer::Format format = mpImage->getFormat(); + const sal_uInt32 dwNumBytesPerPixel = getNumBytes(format); + const sal_uInt32 dwPitch = mpImage->getWidth()*dwNumBytesPerPixel; + + if(!(dwNumBytesPerPixel)) + return; + + sal_uInt8 *pDst = mpImage->lock()+(pos.Y*dwPitch)+(pos.X*dwNumBytesPerPixel); + + switch(format) + { + case IColorBuffer::FMT_R8G8B8: + pDst[0] = argb.Color.r; + pDst[1] = argb.Color.g; + pDst[2] = argb.Color.b; + break; + case IColorBuffer::FMT_A8R8G8B8: + case IColorBuffer::FMT_X8R8G8B8: + pDst[0] = argb.Color.a; + pDst[1] = argb.Color.r; + pDst[2] = argb.Color.g; + pDst[3] = argb.Color.b; + break; + default: + break; + } + + mpImage->unlock(); + } + + uno::Sequence< sal_Int8 > getPixel( ::com::sun::star::rendering::IntegerBitmapLayout& /*bitmapLayout*/, + const ::com::sun::star::geometry::IntegerPoint2D& pos ) + { + const IColorBuffer::Format format = mpImage->getFormat(); + const sal_uInt32 dwNumBytesPerPixel = getNumBytes(format); + const sal_uInt32 dwPitch = mpImage->getWidth()*dwNumBytesPerPixel; + + if(!(dwNumBytesPerPixel)) + return uno::Sequence< sal_Int8 >(); + + uno::Sequence< sal_Int8 > aRet(dwNumBytesPerPixel); + const sal_uInt8 *pSrc = mpImage->lock()+(pos.Y*dwPitch)+(pos.X*dwNumBytesPerPixel); + + switch(format) + { + case IColorBuffer::FMT_R8G8B8: + aRet[0] = pSrc[0]; + aRet[1] = pSrc[1]; + aRet[2] = pSrc[2]; + break; + case IColorBuffer::FMT_A8R8G8B8: + case IColorBuffer::FMT_X8R8G8B8: + aRet[0] = pSrc[1]; + aRet[1] = pSrc[2]; + aRet[2] = pSrc[3]; + aRet[3] = pSrc[0]; + break; + default: + break; + } + + mpImage->unlock(); + + return aRet; + } + + // the IColorBuffer interface + // ========================== + + bool draw( double fAlpha, + const ::basegfx::B2DPoint& rPos, + const ::basegfx::B2DHomMatrix& rTransform ) + { + if( mbIsSurfaceDirty ) + { + mpSurfaceProxy->setColorBufferDirty(); + mbIsSurfaceDirty = false; + } + + return mpSurfaceProxy->draw( fAlpha, rPos, rTransform ); + } + + bool draw( double fAlpha, + const ::basegfx::B2DPoint& rPos, + const ::basegfx::B2DRange& rArea, + const ::basegfx::B2DHomMatrix& rTransform ) + { + if( mbIsSurfaceDirty ) + { + mpSurfaceProxy->setColorBufferDirty(); + mbIsSurfaceDirty = false; + } + + return mpSurfaceProxy->draw( fAlpha, rPos, rArea, rTransform ); + } + + + bool draw( double fAlpha, + const ::basegfx::B2DPoint& rPos, + const ::basegfx::B2DPolyPolygon& rClipPoly, + const ::basegfx::B2DHomMatrix& rTransform ) + { + if( mbIsSurfaceDirty ) + { + mpSurfaceProxy->setColorBufferDirty(); + mbIsSurfaceDirty = false; + } + + return mpSurfaceProxy->draw( fAlpha, rPos, rClipPoly, rTransform ); + } + + void clear( const uno::Sequence< double >& color ) + { + if(color.getLength() > 2) + { + mbIsSurfaceDirty = true; + + if(color.getLength() > 3) + { + mpImage->clear( static_cast<sal_uInt8>(255.0f*color[0]), + static_cast<sal_uInt8>(255.0f*color[1]), + static_cast<sal_uInt8>(255.0f*color[2]), + static_cast<sal_uInt8>(255.0f*color[3]) ); + } + else + { + mpImage->clear( static_cast<sal_uInt8>(255.0f*color[0]), + static_cast<sal_uInt8>(255.0f*color[1]), + static_cast<sal_uInt8>(255.0f*color[2]), + 255 ); + } + } + } + + void fillB2DPolyPolygon( const ::basegfx::B2DPolyPolygon& rPolyPolygon, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) + { + mbIsSurfaceDirty = true; + + mpImage->fillB2DPolyPolygon( rPolyPolygon, + viewState, + renderState ); + } + + + // the XCanvas-render interface + // ============================ + + void drawPoint( const geometry::RealPoint2D& aPoint, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) + { + mbIsSurfaceDirty = true; + + mpImage->drawPoint( aPoint, viewState, renderState ); + } + + void drawLine( const geometry::RealPoint2D& aStartPoint, + const geometry::RealPoint2D& aEndPoint, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) + { + mbIsSurfaceDirty = true; + + mpImage->drawLine( aStartPoint, aEndPoint, viewState, renderState ); + } + + void drawBezier( const geometry::RealBezierSegment2D& aBezierSegment, + const geometry::RealPoint2D& aEndPoint, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) + { + mbIsSurfaceDirty = true; + + mpImage->drawBezier( aBezierSegment, aEndPoint, viewState, renderState ); + } + + ICachedPrimitiveSharedPtr drawPolyPolygon( + const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) + { + mbIsSurfaceDirty = true; + + return setupCachedPrimitive( + mpImage->drawPolyPolygon( xPolyPolygon, viewState, renderState ) ); + } + + ICachedPrimitiveSharedPtr strokePolyPolygon( + const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState, + const rendering::StrokeAttributes& strokeAttributes ) + { + mbIsSurfaceDirty = true; + + return setupCachedPrimitive( + mpImage->strokePolyPolygon( xPolyPolygon, + viewState, + renderState, + strokeAttributes ) ); + } + + ICachedPrimitiveSharedPtr strokeTexturedPolyPolygon( + const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState, + const uno::Sequence< rendering::Texture >& textures, + const ::std::vector< ::boost::shared_ptr<Bitmap> >& textureAnnotations, + const rendering::StrokeAttributes& strokeAttributes ) + { + mbIsSurfaceDirty = true; + + ::std::vector< ImageSharedPtr > aTextureAnnotations; + convertTextureAnnotations( aTextureAnnotations, + textureAnnotations ); + + return setupCachedPrimitive( + mpImage->strokeTexturedPolyPolygon( xPolyPolygon, + viewState, + renderState, + textures, + aTextureAnnotations, + strokeAttributes ) ); + } + + ICachedPrimitiveSharedPtr strokeTextureMappedPolyPolygon( + const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState, + const uno::Sequence< rendering::Texture >& textures, + const ::std::vector< ::boost::shared_ptr<Bitmap> >& textureAnnotations, + const uno::Reference< geometry::XMapping2D >& xMapping, + const rendering::StrokeAttributes& strokeAttributes ) + { + mbIsSurfaceDirty = true; + + ::std::vector< ImageSharedPtr > aTextureAnnotations; + convertTextureAnnotations( aTextureAnnotations, + textureAnnotations ); + + return setupCachedPrimitive( + mpImage->strokeTextureMappedPolyPolygon( xPolyPolygon, + viewState, + renderState, + textures, + aTextureAnnotations, + xMapping, + strokeAttributes ) ); + } + + + ICachedPrimitiveSharedPtr fillPolyPolygon( + const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) + { + mbIsSurfaceDirty = true; + + return setupCachedPrimitive( + mpImage->fillPolyPolygon( xPolyPolygon, + viewState, + renderState ) ); + } + + ICachedPrimitiveSharedPtr fillTexturedPolyPolygon( + const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState, + const uno::Sequence< rendering::Texture >& textures, + const ::std::vector< ::boost::shared_ptr<Bitmap> >& textureAnnotations ) + { + mbIsSurfaceDirty = true; + + ::std::vector< ImageSharedPtr > aTextureAnnotations; + convertTextureAnnotations( aTextureAnnotations, + textureAnnotations ); + + return setupCachedPrimitive( + mpImage->fillTexturedPolyPolygon( xPolyPolygon, + viewState, + renderState, + textures, + aTextureAnnotations ) ); + } + + + ICachedPrimitiveSharedPtr fillTextureMappedPolyPolygon( + const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState, + const uno::Sequence< rendering::Texture >& textures, + const ::std::vector< ::boost::shared_ptr<Bitmap> >& textureAnnotations, + const uno::Reference< geometry::XMapping2D >& xMapping ) + { + mbIsSurfaceDirty = true; + + ::std::vector< ImageSharedPtr > aTextureAnnotations; + convertTextureAnnotations( aTextureAnnotations, + textureAnnotations ); + + return setupCachedPrimitive( + mpImage->fillTextureMappedPolyPolygon( xPolyPolygon, + viewState, + renderState, + textures, + aTextureAnnotations, + xMapping ) ); + } + + ICachedPrimitiveSharedPtr drawBitmap( + const uno::Reference< rendering::XBitmap >& xBitmap, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) + { + mbIsSurfaceDirty = true; + + return setupCachedPrimitive( + mpImage->drawBitmap( xBitmap, + viewState, + renderState ) ); + } + + ICachedPrimitiveSharedPtr drawBitmap( + const ::boost::shared_ptr<Bitmap>& rImage, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) + { + mbIsSurfaceDirty = true; + + return setupCachedPrimitive( + mpImage->drawBitmap( rImage->mpImpl->mpImage, + viewState, + renderState ) ); + } + + ICachedPrimitiveSharedPtr drawBitmapModulated( + const uno::Reference< rendering::XBitmap >& xBitmap, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) + { + mbIsSurfaceDirty = true; + + return setupCachedPrimitive( + mpImage->drawBitmapModulated( xBitmap, + viewState, + renderState ) ); + } + + + ICachedPrimitiveSharedPtr drawBitmapModulated( + const ::boost::shared_ptr<Bitmap>& rImage, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) + { + mbIsSurfaceDirty = true; + + return setupCachedPrimitive( + mpImage->drawBitmapModulated( rImage->mpImpl->mpImage, + viewState, + renderState ) ); + } + + private: + ICachedPrimitiveSharedPtr setupCachedPrimitive( + const ImageCachedPrimitiveSharedPtr& rCachedPrimitive ) const + { + if( rCachedPrimitive ) + rCachedPrimitive->setImage( mpImage ); + + return rCachedPrimitive; + } + + void convertTextureAnnotations( ::std::vector< ImageSharedPtr >& o_rTextureAnnotations, + const ::std::vector< BitmapSharedPtr >& textureAnnotations ) + { + ::std::vector< BitmapSharedPtr >::const_iterator aCurr( textureAnnotations.begin() ); + const ::std::vector< BitmapSharedPtr >::const_iterator aEnd( textureAnnotations.end() ); + while( aCurr != aEnd ) + { + if( aCurr->get() != NULL ) + o_rTextureAnnotations.push_back( (*aCurr)->mpImpl->mpImage ); + else + o_rTextureAnnotations.push_back( ImageSharedPtr() ); + + ++aCurr; + } + } + + sal_uInt32 getNumBytes( IColorBuffer::Format format ) + { + switch(format) + { + case IColorBuffer::FMT_R8G8B8: + return 3; + case IColorBuffer::FMT_A8R8G8B8: + case IColorBuffer::FMT_X8R8G8B8: + return 4; + default: + return 0; + } + } + + ImageSharedPtr mpImage; + ISurfaceProxySharedPtr mpSurfaceProxy; + bool mbIsSurfaceDirty; + }; + + + ///////////////////////////////////////////////////////////////// + // Actual Bitmap pimpl forwarding functions + ///////////////////////////////////////////////////////////////// + + Bitmap::Bitmap( const ::basegfx::B2IVector& rSize, + const ISurfaceProxyManagerSharedPtr& rMgr, + bool bWithAlpha ) : + mpImpl( new ImplBitmap( rSize, + rMgr, + bWithAlpha ) ) + { + } + + Bitmap::~Bitmap() + { + // outline destructor is _necessary_, because ImplBitmap is an + // incomplete type outside this file. + } + + // forward all methods to ImplBitmap + // ================================================== + + bool Bitmap::hasAlpha() const + { + return mpImpl->hasAlpha(); + } + + ::basegfx::B2IVector Bitmap::getSize() const + { + return mpImpl->getSize(); + } + + ::com::sun::star::uno::Sequence< sal_Int8 > Bitmap::getData( + ::com::sun::star::rendering::IntegerBitmapLayout& bitmapLayout, + const ::com::sun::star::geometry::IntegerRectangle2D& rect ) + { + return mpImpl->getData(bitmapLayout,rect); + } + + void Bitmap::setData( + const ::com::sun::star::uno::Sequence< sal_Int8 >& data, + const ::com::sun::star::rendering::IntegerBitmapLayout& bitmapLayout, + const ::com::sun::star::geometry::IntegerRectangle2D& rect ) + { + mpImpl->setData(data,bitmapLayout,rect); + } + + void Bitmap::setPixel( + const ::com::sun::star::uno::Sequence< sal_Int8 >& color, + const ::com::sun::star::rendering::IntegerBitmapLayout& bitmapLayout, + const ::com::sun::star::geometry::IntegerPoint2D& pos ) + { + mpImpl->setPixel(color,bitmapLayout,pos); + } + + uno::Sequence< sal_Int8 > Bitmap::getPixel( + ::com::sun::star::rendering::IntegerBitmapLayout& bitmapLayout, + const ::com::sun::star::geometry::IntegerPoint2D& pos ) + { + return mpImpl->getPixel(bitmapLayout,pos); + } + + bool Bitmap::draw( double fAlpha, + const ::basegfx::B2DPoint& rPos, + const ::basegfx::B2DHomMatrix& rTransform ) + { + return mpImpl->draw( fAlpha, rPos, rTransform ); + } + + bool Bitmap::draw( double fAlpha, + const ::basegfx::B2DPoint& rPos, + const ::basegfx::B2DRange& rArea, + const ::basegfx::B2DHomMatrix& rTransform ) + { + return mpImpl->draw( fAlpha, rPos, rArea, rTransform ); + } + + + bool Bitmap::draw( double fAlpha, + const ::basegfx::B2DPoint& rPos, + const ::basegfx::B2DPolyPolygon& rClipPoly, + const ::basegfx::B2DHomMatrix& rTransform ) + { + return mpImpl->draw( fAlpha, rPos, rClipPoly, rTransform ); + } + + void Bitmap::clear( const uno::Sequence< double >& color ) + { + mpImpl->clear( color ); + } + + void Bitmap::fillB2DPolyPolygon( + const ::basegfx::B2DPolyPolygon& rPolyPolygon, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) + { + mpImpl->fillB2DPolyPolygon( rPolyPolygon, viewState, renderState ); + } + + void Bitmap::drawPoint( const geometry::RealPoint2D& aPoint, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) + { + return mpImpl->drawPoint( aPoint, viewState, renderState ); + } + + void Bitmap::drawLine( const geometry::RealPoint2D& aStartPoint, + const geometry::RealPoint2D& aEndPoint, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) + { + return mpImpl->drawLine( aStartPoint, aEndPoint, viewState, renderState ); + } + + void Bitmap::drawBezier( const geometry::RealBezierSegment2D& aBezierSegment, + const geometry::RealPoint2D& aEndPoint, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) + { + return mpImpl->drawBezier( aBezierSegment, aEndPoint, viewState, renderState ); + } + + ICachedPrimitiveSharedPtr Bitmap::drawPolyPolygon( + const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) + { + return mpImpl->drawPolyPolygon( xPolyPolygon, viewState, renderState ); + } + + ICachedPrimitiveSharedPtr Bitmap::strokePolyPolygon( + const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState, + const rendering::StrokeAttributes& strokeAttributes ) + { + return mpImpl->strokePolyPolygon( xPolyPolygon, viewState, renderState, strokeAttributes ); + } + + ICachedPrimitiveSharedPtr Bitmap::strokeTexturedPolyPolygon( + const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState, + const uno::Sequence< rendering::Texture >& textures, + const ::std::vector< ::boost::shared_ptr<Bitmap> >& textureAnnotations, + const rendering::StrokeAttributes& strokeAttributes ) + { + return mpImpl->strokeTexturedPolyPolygon( xPolyPolygon, + viewState, + renderState, + textures, + textureAnnotations, + strokeAttributes ); + } + + ICachedPrimitiveSharedPtr Bitmap::strokeTextureMappedPolyPolygon( + const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState, + const uno::Sequence< rendering::Texture >& textures, + const ::std::vector< ::boost::shared_ptr<Bitmap> >& textureAnnotations, + const uno::Reference< geometry::XMapping2D >& xMapping, + const rendering::StrokeAttributes& strokeAttributes ) + { + return mpImpl->strokeTextureMappedPolyPolygon( xPolyPolygon, + viewState, + renderState, + textures, + textureAnnotations, + xMapping, + strokeAttributes ); + } + + + ICachedPrimitiveSharedPtr Bitmap::fillPolyPolygon( + const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) + { + return mpImpl->fillPolyPolygon( xPolyPolygon, + viewState, + renderState ); + } + + ICachedPrimitiveSharedPtr Bitmap::fillTexturedPolyPolygon( + const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState, + const uno::Sequence< rendering::Texture >& textures, + const ::std::vector< ::boost::shared_ptr<Bitmap> >& textureAnnotations ) + { + return mpImpl->fillTexturedPolyPolygon( xPolyPolygon, + viewState, + renderState, + textures, + textureAnnotations ); + } + + ICachedPrimitiveSharedPtr Bitmap::fillTextureMappedPolyPolygon( + const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState, + const uno::Sequence< rendering::Texture >& textures, + const ::std::vector< ::boost::shared_ptr<Bitmap> >& textureAnnotations, + const uno::Reference< geometry::XMapping2D >& xMapping ) + { + return mpImpl->fillTextureMappedPolyPolygon( xPolyPolygon, + viewState, + renderState, + textures, + textureAnnotations, + xMapping ); + } + + ICachedPrimitiveSharedPtr Bitmap::drawBitmap( + const uno::Reference< rendering::XBitmap >& xBitmap, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) + { + return mpImpl->drawBitmap( xBitmap, + viewState, + renderState ); + } + + ICachedPrimitiveSharedPtr Bitmap::drawBitmap( + const ::boost::shared_ptr<Bitmap>& rImage, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) + { + return mpImpl->drawBitmap( rImage, + viewState, + renderState ); + } + + ICachedPrimitiveSharedPtr Bitmap::drawBitmapModulated( + const uno::Reference< rendering::XBitmap >& xBitmap, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) + { + return mpImpl->drawBitmapModulated( xBitmap, + viewState, + renderState ); + } + + ICachedPrimitiveSharedPtr Bitmap::drawBitmapModulated( + const ::boost::shared_ptr<Bitmap>& rImage, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) + { + return mpImpl->drawBitmapModulated( rImage, + viewState, + renderState ); + } +} diff --git a/canvas/source/tools/cachedprimitivebase.cxx b/canvas/source/tools/cachedprimitivebase.cxx new file mode 100644 index 000000000000..0d850b842007 --- /dev/null +++ b/canvas/source/tools/cachedprimitivebase.cxx @@ -0,0 +1,112 @@ +/************************************************************************* + * + * 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; + } +} diff --git a/canvas/source/tools/canvascustomspritehelper.cxx b/canvas/source/tools/canvascustomspritehelper.cxx new file mode 100644 index 000000000000..f9d4f186d7a7 --- /dev/null +++ b/canvas/source/tools/canvascustomspritehelper.cxx @@ -0,0 +1,496 @@ +/************************************************************************* + * + * 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() ) ); + } +} diff --git a/canvas/source/tools/canvastools.cxx b/canvas/source/tools/canvastools.cxx new file mode 100755 index 000000000000..ad833cc3ca40 --- /dev/null +++ b/canvas/source/tools/canvastools.cxx @@ -0,0 +1,1044 @@ +/************************************************************************* + * + * 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 diff --git a/canvas/source/tools/canvastools.flt b/canvas/source/tools/canvastools.flt new file mode 100644 index 000000000000..a230939309da --- /dev/null +++ b/canvas/source/tools/canvastools.flt @@ -0,0 +1,4 @@ +__CT +__real +internal +agg
\ No newline at end of file diff --git a/canvas/source/tools/elapsedtime.cxx b/canvas/source/tools/elapsedtime.cxx new file mode 100644 index 000000000000..6ca58a2bf1ee --- /dev/null +++ b/canvas/source/tools/elapsedtime.cxx @@ -0,0 +1,224 @@ +/************************************************************************* + * + * 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 diff --git a/canvas/source/tools/image.cxx b/canvas/source/tools/image.cxx new file mode 100644 index 000000000000..b6183e463e99 --- /dev/null +++ b/canvas/source/tools/image.cxx @@ -0,0 +1,2394 @@ +/************************************************************************* + * + * 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/canvastools.hxx> +#include <canvas/parametricpolypolygon.hxx> + +#include <com/sun/star/rendering/RepaintResult.hpp> +#include <com/sun/star/rendering/XIntegerReadOnlyBitmap.hpp> + +#include <vcl/canvastools.hxx> +#include <vcl/bitmapex.hxx> +#include <vcl/bmpacc.hxx> + +#include <basegfx/range/b2drange.hxx> +#include <basegfx/point/b2dpoint.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> +#include <basegfx/polygon/b2dpolypolygon.hxx> +#include <basegfx/polygon/b2dpolygoncutandtouch.hxx> +#include <basegfx/polygon/b2dpolygontriangulator.hxx> +#include <basegfx/polygon/b2dpolypolygontools.hxx> +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/polygon/b2dpolygonclipper.hxx> +#include <basegfx/tools/canvastools.hxx> +#include <basegfx/polygon/b2dpolypolygoncutter.hxx> +#include <basegfx/polygon/b2dpolygonclipper.hxx> + +#include "image.hxx" + +#define CANVAS_IMAGE_CXX +#include "image_sysprereq.h" + +////////////////////////////////////////////////////////////////////////////////// +// platform-dependend includes [wrapped into their own namepsaces] +////////////////////////////////////////////////////////////////////////////////// + +#if defined(WNT) +# if defined _MSC_VER +# pragma warning(push,1) +# endif + + namespace win32 + { + #undef DECLARE_HANDLE + #undef WB_LEFT + #undef WB_RIGHT + #undef APIENTRY + #define WIN32_LEAN_AND_MEAN + #define NOMINMAX + #include <windows.h> + } + +# if defined _MSC_VER +# pragma warning(pop) +# endif +#elif defined(OS2) + namespace os2 + { + #include <svpm.h> + } +#else +#if !defined(QUARTZ) + namespace unx + { + #include <X11/Xlib.h> + } +#endif +#endif + +#include <algorithm> + +using namespace ::com::sun::star; + +namespace canvas { namespace +{ + ////////////////////////////////////////////////////////////////////////////////// + // TransAffineFromAffineMatrix + ////////////////////////////////////////////////////////////////////////////////// + + ::agg::trans_affine transAffineFromAffineMatrix( const geometry::AffineMatrix2D& m ) + { + return agg::trans_affine(m.m00, + m.m10, + m.m01, + m.m11, + m.m02, + m.m12); + } + + ////////////////////////////////////////////////////////////////////////////////// + // TransAffineFromB2DHomMatrix + ////////////////////////////////////////////////////////////////////////////////// + + ::agg::trans_affine transAffineFromB2DHomMatrix( const ::basegfx::B2DHomMatrix& m ) + { + return agg::trans_affine(m.get(0,0), + m.get(1,0), + m.get(0,1), + m.get(1,1), + m.get(0,2), + m.get(1,2)); + } + + ////////////////////////////////////////////////////////////////////////////////// + // ARGB + ////////////////////////////////////////////////////////////////////////////////// + + struct ARGBColor + { + sal_uInt8 a; + sal_uInt8 r; + sal_uInt8 g; + sal_uInt8 b; + }; + + /// ARGB color + union ARGB + { + ARGBColor Color; + sal_uInt32 color; + + ARGB() : + color(0) + { + } + + explicit ARGB( sal_uInt32 _color ) : + color(_color) + { + } + + ARGB( sal_uInt8 _a, + sal_uInt8 _r, + sal_uInt8 _g, + sal_uInt8 _b ) + { + Color.a = _a; + Color.r = _r; + Color.g = _g; + Color.b = _b; + } + + ARGB( sal_uInt32 default_color, + const ::com::sun::star::uno::Sequence< double >& sequence ) : + color(default_color) + { + if(sequence.getLength() > 2) + { + Color.r = static_cast<sal_uInt8>(255.0f*sequence[0]); + Color.g = static_cast<sal_uInt8>(255.0f*sequence[1]); + Color.b = static_cast<sal_uInt8>(255.0f*sequence[2]); + if(sequence.getLength() > 3) + Color.a = static_cast<sal_uInt8>(255.0f*sequence[3]); + } + } + + ARGB( const ARGB& rhs ) : + color( rhs.color ) + { + } + + ARGB &operator=( const ARGB &rhs ) + { + color=rhs.color; + return *this; + } + }; + + ////////////////////////////////////////////////////////////////////////////////// + // setupState + ////////////////////////////////////////////////////////////////////////////////// + + /// Calc common output state from XCanvas parameters + void setupState( ::basegfx::B2DHomMatrix& o_rViewTransform, + ::basegfx::B2DHomMatrix& o_rRenderTransform, + ::std::auto_ptr< ::basegfx::B2DPolyPolygon >& o_rViewClip, + ::std::auto_ptr< ::basegfx::B2DPolyPolygon >& o_rRenderClip, + ARGB& o_rRenderColor, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) + { + ::basegfx::unotools::homMatrixFromAffineMatrix(o_rRenderTransform, + renderState.AffineTransform); + ::basegfx::unotools::homMatrixFromAffineMatrix(o_rViewTransform, + viewState.AffineTransform); + + o_rRenderColor = ARGB(0xFFFFFFFF, + renderState.DeviceColor); + + // TODO(F3): handle compositing modes + + if( viewState.Clip.is() ) + { + ::basegfx::B2DPolyPolygon aViewClip( + ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D( viewState.Clip )); + + if(aViewClip.areControlPointsUsed()) + aViewClip = ::basegfx::tools::adaptiveSubdivideByAngle(aViewClip); + + o_rViewClip.reset( new ::basegfx::B2DPolyPolygon( aViewClip ) ); + } + + if( renderState.Clip.is() ) + { + ::basegfx::B2DPolyPolygon aRenderClip( + ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D( viewState.Clip ) ); + + if(aRenderClip.areControlPointsUsed()) + aRenderClip = ::basegfx::tools::adaptiveSubdivideByAngle(aRenderClip); + + o_rRenderClip.reset( new ::basegfx::B2DPolyPolygon( aRenderClip ) ); + } + } + + ////////////////////////////////////////////////////////////////////////////////// + // clipAndTransformPolygon + ////////////////////////////////////////////////////////////////////////////////// + + /** Clip and transform given polygon + + @param io_rClippee + Polygon to clip + + @param bIsFilledPolyPolygon + When true, the polygon is clipped as if it was to be rendered + with fill, when false, the polygon is clipped as if it was to + be rendered with stroking. + */ + void clipAndTransformPolygon( ::basegfx::B2DPolyPolygon& io_rClippee, + bool bIsFilledPolyPolygon, + const ::basegfx::B2DHomMatrix& rViewTransform, + const ::basegfx::B2DHomMatrix& rRenderTransform, + const ::basegfx::B2DPolyPolygon* pViewClip, + const ::basegfx::B2DPolyPolygon* pRenderClip ) + { + ::basegfx::B2DPolyPolygon aPolyPolygon(io_rClippee); + io_rClippee.clear(); + + // clip contour against renderclip + if( pRenderClip ) + { + // AW: Simplified + aPolyPolygon = basegfx::tools::clipPolyPolygonOnPolyPolygon( + aPolyPolygon, *pRenderClip, true, !bIsFilledPolyPolygon); + } + + if( !aPolyPolygon.count() ) + return; + + // transform result into view space + aPolyPolygon.transform(rRenderTransform); + + // clip contour against viewclip + if( pViewClip ) + { + // AW: Simplified + aPolyPolygon = basegfx::tools::clipPolyPolygonOnPolyPolygon( + aPolyPolygon, *pViewClip, true, !bIsFilledPolyPolygon); + } + + if(!(aPolyPolygon.count())) + return; + + // transform result into device space + aPolyPolygon.transform(rViewTransform); + + io_rClippee = aPolyPolygon; + } + + ////////////////////////////////////////////////////////////////////////////////// + // setupPolyPolygon + ////////////////////////////////////////////////////////////////////////////////// + + void setupPolyPolygon( ::basegfx::B2DPolyPolygon& io_rClippee, + bool bIsFilledPolyPolygon, + ARGB& o_rRenderColor, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) + { + ::basegfx::B2DHomMatrix aViewTransform; + ::basegfx::B2DHomMatrix aRenderTransform; + ::std::auto_ptr< ::basegfx::B2DPolyPolygon > pViewClip; + ::std::auto_ptr< ::basegfx::B2DPolyPolygon > pRenderClip; + + setupState( aViewTransform, + aRenderTransform, + pViewClip, + pRenderClip, + o_rRenderColor, + viewState, + renderState ); + + clipAndTransformPolygon( io_rClippee, + bIsFilledPolyPolygon, + aViewTransform, + aRenderTransform, + pViewClip.get(), + pRenderClip.get() ); + } + + ////////////////////////////////////////////////////////////////////////////////// + // RawABGRBitmap + ////////////////////////////////////////////////////////////////////////////////// + + // Raw ABGR [AABBGGRR] 32bit continous + struct RawABGRBitmap + { + sal_Int32 mnWidth; + sal_Int32 mnHeight; + sal_uInt8* mpBitmapData; + }; + + ////////////////////////////////////////////////////////////////////////////////// + // vclBitmapEx2Raw + ////////////////////////////////////////////////////////////////////////////////// + + void vclBitmapEx2Raw( const ::BitmapEx& rBmpEx, RawABGRBitmap& rBmpData ) + { + Bitmap aBitmap( rBmpEx.GetBitmap() ); + + ScopedBitmapReadAccess pReadAccess( aBitmap.AcquireReadAccess(), + aBitmap ); + + const sal_Int32 nWidth( rBmpData.mnWidth ); + const sal_Int32 nHeight( rBmpData.mnHeight ); + + ENSURE_OR_THROW( pReadAccess.get() != NULL, + "vclBitmapEx2Raw(): " + "Unable to acquire read acces to bitmap" ); + + if( rBmpEx.IsTransparent()) + { + if( rBmpEx.IsAlpha() ) + { + // 8bit alpha mask + Bitmap aAlpha( rBmpEx.GetAlpha().GetBitmap() ); + + ScopedBitmapReadAccess pAlphaReadAccess( aAlpha.AcquireReadAccess(), + aAlpha ); + + // By convention, the access buffer always has + // one of the following formats: + // + // BMP_FORMAT_1BIT_MSB_PAL + // BMP_FORMAT_4BIT_MSN_PAL + // BMP_FORMAT_8BIT_PAL + // BMP_FORMAT_16BIT_TC_LSB_MASK + // BMP_FORMAT_24BIT_TC_BGR + // BMP_FORMAT_32BIT_TC_MASK + // + // and is always BMP_FORMAT_BOTTOM_UP + // + // This is the way + // WinSalBitmap::AcquireBuffer() sets up the + // buffer + + ENSURE_OR_THROW( pAlphaReadAccess.get() != NULL, + "vclBitmapEx2Raw(): " + "Unable to acquire read acces to alpha" ); + + ENSURE_OR_THROW( pAlphaReadAccess->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL || + pAlphaReadAccess->GetScanlineFormat() == BMP_FORMAT_8BIT_TC_MASK, + "vclBitmapEx2Raw(): " + "Unsupported alpha scanline format" ); + + BitmapColor aCol; + sal_uInt8* pCurrOutput( rBmpData.mpBitmapData ); + int x, y; + + for( y=0; y<nHeight; ++y ) + { + switch( pReadAccess->GetScanlineFormat() ) + { + case BMP_FORMAT_8BIT_PAL: + { + Scanline pScan = pReadAccess->GetScanline( y ); + Scanline pAScan = pAlphaReadAccess->GetScanline( y ); + + for( x=0; x<nWidth; ++x ) + { + aCol = pReadAccess->GetPaletteColor( *pScan++ ); + + *pCurrOutput++ = aCol.GetBlue(); + *pCurrOutput++ = aCol.GetGreen(); + *pCurrOutput++ = aCol.GetRed(); + + // out notion of alpha is + // different from the rest + // of the world's + *pCurrOutput++ = 255 - (BYTE)*pAScan++; + } + } + break; + + case BMP_FORMAT_24BIT_TC_BGR: + { + Scanline pScan = pReadAccess->GetScanline( y ); + Scanline pAScan = pAlphaReadAccess->GetScanline( y ); + + for( x=0; x<nWidth; ++x ) + { + // store as RGBA + *pCurrOutput++ = *pScan++; + *pCurrOutput++ = *pScan++; + *pCurrOutput++ = *pScan++; + + // out notion of alpha is + // different from the rest + // of the world's + *pCurrOutput++ = 255 - (BYTE)*pAScan++; + } + } + break; + + // TODO(P2): Might be advantageous + // to hand-formulate the following + // formats, too. + case BMP_FORMAT_1BIT_MSB_PAL: + // FALLTHROUGH intended + case BMP_FORMAT_4BIT_MSN_PAL: + // FALLTHROUGH intended + case BMP_FORMAT_16BIT_TC_LSB_MASK: + // FALLTHROUGH intended + case BMP_FORMAT_32BIT_TC_MASK: + { + Scanline pAScan = pAlphaReadAccess->GetScanline( y ); + + // using fallback for those + // seldom formats + for( x=0; x<nWidth; ++x ) + { + // yes. x and y are swapped on Get/SetPixel + aCol = pReadAccess->GetColor(y,x); + + *pCurrOutput++ = aCol.GetBlue(); + *pCurrOutput++ = aCol.GetGreen(); + *pCurrOutput++ = aCol.GetRed(); + + // out notion of alpha is + // different from the rest + // of the world's + *pCurrOutput++ = 255 - (BYTE)*pAScan++; + } + } + break; + + case BMP_FORMAT_1BIT_LSB_PAL: + // FALLTHROUGH intended + case BMP_FORMAT_4BIT_LSN_PAL: + // FALLTHROUGH intended + case BMP_FORMAT_8BIT_TC_MASK: + // FALLTHROUGH intended + case BMP_FORMAT_24BIT_TC_RGB: + // FALLTHROUGH intended + case BMP_FORMAT_24BIT_TC_MASK: + // FALLTHROUGH intended + case BMP_FORMAT_16BIT_TC_MSB_MASK: + // FALLTHROUGH intended + case BMP_FORMAT_32BIT_TC_ABGR: + // FALLTHROUGH intended + case BMP_FORMAT_32BIT_TC_ARGB: + // FALLTHROUGH intended + case BMP_FORMAT_32BIT_TC_BGRA: + // FALLTHROUGH intended + case BMP_FORMAT_32BIT_TC_RGBA: + // FALLTHROUGH intended + default: + ENSURE_OR_THROW( false, + "vclBitmapEx2Raw(): " + "Unexpected scanline format - has " + "WinSalBitmap::AcquireBuffer() changed?" ); + } + } + } + else + { + // 1bit alpha mask + Bitmap aMask( rBmpEx.GetMask() ); + + ScopedBitmapReadAccess pMaskReadAccess( aMask.AcquireReadAccess(), + aMask ); + + // By convention, the access buffer always has + // one of the following formats: + // + // BMP_FORMAT_1BIT_MSB_PAL + // BMP_FORMAT_4BIT_MSN_PAL + // BMP_FORMAT_8BIT_PAL + // BMP_FORMAT_16BIT_TC_LSB_MASK + // BMP_FORMAT_24BIT_TC_BGR + // BMP_FORMAT_32BIT_TC_MASK + // + // and is always BMP_FORMAT_BOTTOM_UP + // + // This is the way + // WinSalBitmap::AcquireBuffer() sets up the + // buffer + + ENSURE_OR_THROW( pMaskReadAccess.get() != NULL, + "vclBitmapEx2Raw(): " + "Unable to acquire read acces to mask" ); + + ENSURE_OR_THROW( pMaskReadAccess->GetScanlineFormat() == BMP_FORMAT_1BIT_MSB_PAL, + "vclBitmapEx2Raw(): " + "Unsupported mask scanline format" ); + + BitmapColor aCol; + int nCurrBit; + const int nMask( 1L ); + const int nInitialBit(7); + sal_uInt32 *pBuffer = reinterpret_cast<sal_uInt32 *>(rBmpData.mpBitmapData); + int x, y; + + // mapping table, to get from mask index color to + // alpha value (which depends on the mask's palette) + sal_uInt8 aColorMap[2]; + + const BitmapColor& rCol0( pMaskReadAccess->GetPaletteColor( 0 ) ); + const BitmapColor& rCol1( pMaskReadAccess->GetPaletteColor( 1 ) ); + + // shortcut for true luminance calculation + // (assumes that palette is grey-level). Note the + // swapped the indices here, to account for the + // fact that VCL's notion of alpha is inverted to + // the rest of the world's. + aColorMap[0] = rCol1.GetRed(); + aColorMap[1] = rCol0.GetRed(); + + for( y=0; y<nHeight; ++y ) + { + switch( pReadAccess->GetScanlineFormat() ) + { + case BMP_FORMAT_8BIT_PAL: + { + Scanline pScan = pReadAccess->GetScanline( y ); + Scanline pMScan = pMaskReadAccess->GetScanline( y ); + + for( x=0, nCurrBit=nInitialBit; x<nWidth; ++x ) + { + aCol = pReadAccess->GetPaletteColor( *pScan++ ); + + // RGB -> ABGR + unsigned int color = aCol.GetRed(); + color |= aCol.GetGreen()<<8; + color |= aCol.GetBlue()<<16; + color |= aColorMap[ (pMScan[ (x & ~7L) >> 3L ] >> nCurrBit ) & nMask ]<<24; + *pBuffer++ = color; + nCurrBit = ((nCurrBit - 1) % 8L) & 7L; + } + } + break; + + case BMP_FORMAT_24BIT_TC_BGR: + { + Scanline pScan = pReadAccess->GetScanline( y ); + Scanline pMScan = pMaskReadAccess->GetScanline( y ); + + for( x=0, nCurrBit=nInitialBit; x<nWidth; ++x ) + { + // BGR -> ABGR + unsigned int color = (*pScan++)<<16; + color |= (*pScan++)<<8; + color |= (*pScan++); + color |= (aColorMap[ (pMScan[ (x & ~7L) >> 3L ] >> nCurrBit ) & nMask ])<<24; + *pBuffer++ = color; + nCurrBit = ((nCurrBit - 1) % 8L) & 7L; + } + } + break; + + // TODO(P2): Might be advantageous + // to hand-formulate the following + // formats, too. + case BMP_FORMAT_1BIT_MSB_PAL: + // FALLTHROUGH intended + case BMP_FORMAT_4BIT_MSN_PAL: + // FALLTHROUGH intended + case BMP_FORMAT_16BIT_TC_LSB_MASK: + // FALLTHROUGH intended + case BMP_FORMAT_32BIT_TC_MASK: + { + Scanline pMScan = pMaskReadAccess->GetScanline( y ); + + // using fallback for those + // seldom formats + for( x=0, nCurrBit=nInitialBit; x<nWidth; ++x ) + { + // yes. x and y are swapped on Get/SetPixel + aCol = pReadAccess->GetColor(y,x); + + // -> ABGR + unsigned int color = aCol.GetBlue()<<16; + color |= aCol.GetGreen()<<8; + color |= aCol.GetRed(); + color |= (aColorMap[ (pMScan[ (x & ~7L) >> 3L ] >> nCurrBit ) & nMask ])<<24; + *pBuffer++ = color; + nCurrBit = ((nCurrBit - 1) % 8L) & 7L; + } + } + break; + + case BMP_FORMAT_1BIT_LSB_PAL: + // FALLTHROUGH intended + case BMP_FORMAT_4BIT_LSN_PAL: + // FALLTHROUGH intended + case BMP_FORMAT_8BIT_TC_MASK: + // FALLTHROUGH intended + case BMP_FORMAT_24BIT_TC_RGB: + // FALLTHROUGH intended + case BMP_FORMAT_24BIT_TC_MASK: + // FALLTHROUGH intended + case BMP_FORMAT_16BIT_TC_MSB_MASK: + // FALLTHROUGH intended + case BMP_FORMAT_32BIT_TC_ABGR: + // FALLTHROUGH intended + case BMP_FORMAT_32BIT_TC_ARGB: + // FALLTHROUGH intended + case BMP_FORMAT_32BIT_TC_BGRA: + // FALLTHROUGH intended + case BMP_FORMAT_32BIT_TC_RGBA: + // FALLTHROUGH intended + default: + ENSURE_OR_THROW( false, + "vclBitmapEx2Raw(): " + "Unexpected scanline format - has " + "WinSalBitmap::AcquireBuffer() changed?" ); + } + } + } + } + else + { + // *no* alpha mask + ULONG nFormat = pReadAccess->GetScanlineFormat(); + BYTE *pBuffer = reinterpret_cast<BYTE *>(rBmpData.mpBitmapData); + + switch(nFormat) + { + case BMP_FORMAT_24BIT_TC_BGR: + + { + sal_Int32 height = pReadAccess->Height(); + for(sal_Int32 y=0; y<height; ++y) + { + BYTE *pScanline=pReadAccess->GetScanline(y); + sal_Int32 width = pReadAccess->Width(); + for(sal_Int32 x=0; x<width; ++x) + { + // BGR -> RGB + BYTE b(*pScanline++); + BYTE g(*pScanline++); + BYTE r(*pScanline++); + *pBuffer++ = r; + *pBuffer++ = g; + *pBuffer++ = b; + } + } + } + break; + + case BMP_FORMAT_24BIT_TC_RGB: + + { + sal_Int32 height = pReadAccess->Height(); + for(sal_Int32 y=0; y<height; ++y) + { + BYTE *pScanline=pReadAccess->GetScanline(y); + sal_Int32 width = pReadAccess->Width(); + for(sal_Int32 x=0; x<width; ++x) + { + // RGB -> RGB + BYTE r(*pScanline++); + BYTE g(*pScanline++); + BYTE b(*pScanline++); + *pBuffer++ = r; + *pBuffer++ = g; + *pBuffer++ = b; + } + } + } + break; + + case BMP_FORMAT_1BIT_MSB_PAL: + case BMP_FORMAT_1BIT_LSB_PAL: + case BMP_FORMAT_4BIT_MSN_PAL: + case BMP_FORMAT_4BIT_LSN_PAL: + case BMP_FORMAT_8BIT_PAL: + + { + sal_Int32 height = pReadAccess->Height(); + for(sal_Int32 y=0; y<height; ++y) + { + BYTE *pScanline=pReadAccess->GetScanline(y); + sal_Int32 width = pReadAccess->Width(); + for(sal_Int32 x=0; x<width; ++x) + { + BitmapColor aCol(pReadAccess->GetPaletteColor(*pScanline++)); + + *pBuffer++ = aCol.GetRed(); + *pBuffer++ = aCol.GetGreen(); + *pBuffer++ = aCol.GetBlue(); + } + } + } + break; + } + } + } + + ////////////////////////////////////////////////////////////////////////////////// + // color_generator_linear + ////////////////////////////////////////////////////////////////////////////////// + + template<typename T> struct color_generator_linear + { + typedef typename T::value_type value_type; + + color_generator_linear( const T &c1, + const T &c2, + unsigned int aSteps ) : maSteps(aSteps), + maColor1(c1), + maColor2(c2) + { + } + + unsigned size() const { return maSteps; } + const T operator [] (unsigned v) const + { + const double w = double(v)/maSteps; + return T( static_cast<value_type>(maColor1.r+(maColor2.r-maColor1.r)*w), + static_cast<value_type>(maColor1.g+(maColor2.g-maColor1.g)*w), + static_cast<value_type>(maColor1.b+(maColor2.b-maColor1.b)*w), + static_cast<value_type>(maColor1.a+(maColor2.a-maColor1.a)*w)); + } + + unsigned int maSteps; + const T maColor1; + const T maColor2; + }; + + ////////////////////////////////////////////////////////////////////////////////// + // color_generator_axial + ////////////////////////////////////////////////////////////////////////////////// + + template<typename T> struct color_generator_axial + { + typedef typename T::value_type value_type; + + color_generator_axial( const T &c1, + const T &c2, + unsigned int aSteps ) : maSteps(aSteps), + maColor1(c1), + maColor2(c2) + { + } + + unsigned size() const { return maSteps; } + const T operator [] (unsigned v) const + { + const double aHalfSteps = maSteps/2.0; + const double w = (v >= aHalfSteps) ? + 1.0-((double(v)-aHalfSteps)/aHalfSteps) : + (double(v)*2.0)/maSteps; + return T( static_cast<value_type>(maColor1.r+(maColor2.r-maColor1.r)*w), + static_cast<value_type>(maColor1.g+(maColor2.g-maColor1.g)*w), + static_cast<value_type>(maColor1.b+(maColor2.b-maColor1.b)*w), + static_cast<value_type>(maColor1.a+(maColor2.a-maColor1.a)*w)); + } + + unsigned int maSteps; + const T maColor1; + const T maColor2; + }; + + ////////////////////////////////////////////////////////////////////////////////// + // color_generator_adaptor + ////////////////////////////////////////////////////////////////////////////////// + + template<typename T> struct color_generator_adaptor + { + color_generator_adaptor( const T &c1, + const T &c2, + unsigned int aSteps ) : linear_generator(c1,c2,aSteps), + axial_generator(c1,c2,aSteps), + mbLinear(true) {} + void set_linear( bool bLinear ) { mbLinear=bLinear; } + unsigned size() const { return mbLinear ? linear_generator.size() : axial_generator.size(); } + const T operator [] (unsigned v) const + { + return mbLinear ? + linear_generator.operator [] (v) : + axial_generator.operator [] (v); + } + + color_generator_linear<T> linear_generator; + color_generator_axial<T> axial_generator; + bool mbLinear; + }; + + ////////////////////////////////////////////////////////////////////////////////// + // gradient_polymorphic_wrapper_base + ////////////////////////////////////////////////////////////////////////////////// + + struct gradient_polymorphic_wrapper_base + { + virtual int calculate(int x, int y, int) const = 0; + }; + + ////////////////////////////////////////////////////////////////////////////////// + // gradient_polymorphic_wrapper + ////////////////////////////////////////////////////////////////////////////////// + + template<class GradientF> struct gradient_polymorphic_wrapper : + public gradient_polymorphic_wrapper_base + { + virtual int calculate(int x, int y, int d) const + { + return m_gradient.calculate(x, y, d); + } + GradientF m_gradient; + }; + + ////////////////////////////////////////////////////////////////////////////////// + // gradient_rect + ////////////////////////////////////////////////////////////////////////////////// + + class gradient_rect + { + public: + + int width; + int height; + + inline int calculate(int x, int y, int d) const + { + int ax = abs(x); + int ay = abs(y); + int clamp_x = height>width ? 0 : (width-height); + int clamp_y = height>width ? (height-width) : 0; + int value_x = (ax-clamp_x)*d/(width-clamp_x); + int value_y = (ay-clamp_y)*d/(height-clamp_y); + if(ax < (clamp_x)) + value_x = 0; + if(ay < (clamp_y)) + value_y = 0; + return value_x > value_y ? value_x : value_y; + } + }; + + sal_uInt32 getBytesPerPixel( IColorBuffer::Format eFormat ) + { + switch(eFormat) + { + default: + OSL_ENSURE(false, "Unexpected pixel format"); + // FALLTHROUGH intended + case IColorBuffer::FMT_R8G8B8: + return 3L; + case IColorBuffer::FMT_A8R8G8B8: + return 4L; + } + } +} + +////////////////////////////////////////////////////////////////////////////////// +// Image::drawLinePolyPolygon +////////////////////////////////////////////////////////////////////////////////// + +template<class pixel_format> +void Image::drawLinePolyPolygonImpl( const ::basegfx::B2DPolyPolygon& rPolyPolygon, + double fStrokeWidth, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) +{ + ::basegfx::B2DPolyPolygon aPolyPolygon( rPolyPolygon ); + ARGB aRenderColor; + + setupPolyPolygon( aPolyPolygon, false, aRenderColor, viewState, renderState ); + + if( !aPolyPolygon.count() ) + return; + + // Class template pixel_formats_rgb24 has full knowledge about this + // particular pixel format in memory. The only template parameter + // can be order_rgb24 or order_bgr24 that determines the order of color channels. + //typedef agg::pixfmt_rgba32 pixel_format; + pixel_format pixf(maRenderingBuffer); + + // There are two basic renderers with almost the same functionality: + // renderer_base and renderer_mclip. The first one is used most often + // and it performs low level clipping. + // This simply adds clipping to the graphics buffer, the clip rect + // will be initialized to the area of the framebuffer. + typedef agg::renderer_base<pixel_format> renderer_base; + agg::renderer_base<pixel_format> renb(pixf); + + // To draw Anti-Aliased primitives one shoud *rasterize* them first. + // The primary rasterization technique in AGG is scanline based. + // That is, a polygon is converted into a number of horizontal + // scanlines and then the scanlines are being rendered one by one. + // To transfer information from a rasterizer to the scanline renderer + // there scanline containers are used. A scanline consists of a + // number of horizontal, non-intersecting spans. All spans must be ordered by X. + // --> *packed* scanline container + agg::scanline_p8 sl; + + typedef agg::renderer_outline_aa<renderer_base> renderer_type; + typedef agg::rasterizer_outline_aa<renderer_type> rasterizer_type; + agg::line_profile_aa profile; + profile.width(fStrokeWidth); + renderer_type ren(renb, profile); + rasterizer_type ras(ren); + + const agg::rgba8 fillcolor(aRenderColor.Color.r, + aRenderColor.Color.g, + aRenderColor.Color.b, + aRenderColor.Color.a); + ren.color(fillcolor); + + agg::path_storage path; + agg::conv_curve<agg::path_storage> curve(path); + + for(sal_uInt32 nPolygon=0; nPolygon<aPolyPolygon.count(); ++nPolygon) + { + const basegfx::B2DPolygon aPolygon(aPolyPolygon.getB2DPolygon(nPolygon)); + const sal_uInt32 nPointCount(aPolygon.count()); + + if(nPointCount) + { + if(aPolygon.areControlPointsUsed()) + { + // prepare edge-based loop + basegfx::B2DPoint aCurrentPoint(aPolygon.getB2DPoint(0)); + const sal_uInt32 nEdgeCount(aPolygon.isClosed() ? nPointCount - 1 : nPointCount); + + // first vertex + path.move_to(aCurrentPoint.getX(), aCurrentPoint.getY()); + + for(sal_uInt32 a(0); a < nEdgeCount; a++) + { + // access next point + const sal_uInt32 nNextIndex((a + 1) % nPointCount); + const basegfx::B2DPoint aNextPoint(aPolygon.getB2DPoint(nNextIndex)); + + // get control points + const basegfx::B2DPoint aControlNext(aPolygon.getNextControlPoint(a)); + const basegfx::B2DPoint aControlPrev(aPolygon.getPrevControlPoint(nNextIndex)); + + // specify first cp, second cp, next vertex + path.curve4( + aControlNext.getX(), aControlNext.getY(), + aControlPrev.getX(), aControlPrev.getY(), + aNextPoint.getX(), aNextPoint.getY()); + + // prepare next step + aCurrentPoint = aNextPoint; + } + } + else + { + const basegfx::B2DPoint aStartPoint(aPolygon.getB2DPoint(0)); + ras.move_to_d(aStartPoint.getX(), aStartPoint.getY()); + + for(sal_uInt32 a(1); a < nPointCount; a++) + { + const basegfx::B2DPoint aVertexPoint(aPolygon.getB2DPoint(a)); + ras.line_to_d(aVertexPoint.getX(), aVertexPoint.getY()); + } + + ras.render(aPolygon.isClosed()); + } + } + } + + ras.add_path(curve); + ras.render(false); +} + +////////////////////////////////////////////////////////////////////////////////// +// Image::drawLinePolyPolygon +////////////////////////////////////////////////////////////////////////////////// + +void Image::drawLinePolyPolygon( const ::basegfx::B2DPolyPolygon& rPoly, + double fStrokeWidth, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) +{ + switch(maDesc.eFormat) + { + case FMT_R8G8B8: + drawLinePolyPolygonImpl<agg::pixfmt_rgb24>(rPoly,fStrokeWidth,viewState,renderState); + break; + case FMT_A8R8G8B8: + drawLinePolyPolygonImpl<agg::pixfmt_rgba32>(rPoly,fStrokeWidth,viewState,renderState); + break; + default: + OSL_ENSURE(false, "Unexpected pixel format"); + break; + } +} + +////////////////////////////////////////////////////////////////////////////////// +// Image::implDrawBitmap +////////////////////////////////////////////////////////////////////////////////// + +/** internal utility function to draw one image into another one. + the source image will be drawn with respect to the given + transform and clip settings. + */ +ImageCachedPrimitiveSharedPtr Image::implDrawBitmap( + const Image& rBitmap, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) +{ + ::basegfx::B2DPolyPolygon aPoly( + ::basegfx::tools::createPolygonFromRect( + ::basegfx::B2DRange(0.0, 0.0, + rBitmap.maDesc.nWidth, + rBitmap.maDesc.nHeight ) ) ); + ARGB aFillColor; + + setupPolyPolygon( aPoly, true, aFillColor, viewState, renderState ); + + if( !aPoly.count() ) + return ImageCachedPrimitiveSharedPtr(); + + ::basegfx::B2DHomMatrix aViewTransform; + ::basegfx::B2DHomMatrix aRenderTransform; + ::basegfx::B2DHomMatrix aTextureTransform; + + ::basegfx::unotools::homMatrixFromAffineMatrix(aRenderTransform, + renderState.AffineTransform); + ::basegfx::unotools::homMatrixFromAffineMatrix(aViewTransform, + viewState.AffineTransform); + aTextureTransform *= aRenderTransform; + + // TODO(F2): Fill in texture + rendering::Texture aTexture; + + return fillTexturedPolyPolygon( rBitmap, + aPoly, + aTextureTransform, + aViewTransform, + aTexture ); +} + +////////////////////////////////////////////////////////////////////////////////// +// cachedPrimitiveFTPP [cachedPrimitive for [F]ill[T]extured[P]oly[P]olygon] +////////////////////////////////////////////////////////////////////////////////// + +#if AGG_VERSION >= 2400 +template<class pixel_format_dst,class span_gen_type> +#else +template<class pixel_format,class span_gen_type> +#endif +class cachedPrimitiveFTPP : public ImageCachedPrimitive +{ + public: + + cachedPrimitiveFTPP( const ::basegfx::B2DHomMatrix &rTransform, + const ::basegfx::B2DHomMatrix &rViewTransform, + agg::rendering_buffer &dst, + const agg::rendering_buffer& src ) : + aTransform(rTransform), + inter(tm), + filter(filter_kernel), +#if AGG_VERSION >= 2400 + pixs(const_cast<agg::rendering_buffer&>(src)), + source(pixs), + sg(source,inter,filter), + pixd(dst), + rb(pixd), + ren(rb,sa,sg) +#else + sg(sa,src,inter,filter), + pixf(dst), + rb(pixf), + ren(rb,sg) +#endif + { + ::basegfx::B2DHomMatrix aFinalTransform(aTransform); + aFinalTransform *= rViewTransform; + tm = transAffineFromB2DHomMatrix(aFinalTransform); + tm.invert(); + } + + virtual void setImage( const ::boost::shared_ptr< class Image >& rTargetImage ) + { + pImage=rTargetImage; + } + + virtual sal_Int8 redraw( const ::com::sun::star::rendering::ViewState& aState ) const + { + ::basegfx::B2DHomMatrix aViewTransform; + ::basegfx::unotools::homMatrixFromAffineMatrix(aViewTransform,aState.AffineTransform); + ::basegfx::B2DHomMatrix aFinalTransform(aTransform); + aFinalTransform *= aViewTransform; + tm = transAffineFromB2DHomMatrix(aFinalTransform); + tm.invert(); + redraw(); + return ::com::sun::star::rendering::RepaintResult::REDRAWN; + } + + inline void redraw() const { agg::render_scanlines(ras, sl, ren); } + + mutable agg::rasterizer_scanline_aa<> ras; + + private: + + typedef agg::span_interpolator_linear<> interpolator_type; +#if AGG_VERSION >= 2400 + typedef agg::renderer_base<pixel_format_dst> renderer_base; + typedef agg::span_allocator< typename span_gen_type::color_type > span_alloc_type; + typedef agg::renderer_scanline_aa<renderer_base, span_alloc_type, span_gen_type> renderer_type; + typedef typename span_gen_type::source_type source_type; + typedef typename span_gen_type::source_type::pixfmt_type pixel_format_src; +#else + typedef agg::renderer_base<pixel_format> renderer_base; + typedef agg::renderer_scanline_aa<renderer_base, span_gen_type> renderer_type; +#endif + + ::basegfx::B2DHomMatrix aTransform; + interpolator_type inter; + agg::image_filter_bilinear filter_kernel; + agg::image_filter_lut filter; +#if AGG_VERSION >= 2400 + span_alloc_type sa; + pixel_format_src pixs; + source_type source; +#else + agg::span_allocator< typename span_gen_type::color_type > sa; +#endif + span_gen_type sg; +#if AGG_VERSION >= 2400 + pixel_format_dst pixd; +#else + pixel_format pixf; +#endif + renderer_base rb; + mutable renderer_type ren; + mutable agg::scanline_p8 sl; + mutable agg::trans_affine tm; + ImageSharedPtr pImage; +}; + +////////////////////////////////////////////////////////////////////////////////// +// Image::fillTexturedPolyPolygon +////////////////////////////////////////////////////////////////////////////////// + +template<class pixel_format,class span_gen_type> +ImageCachedPrimitiveSharedPtr Image::fillTexturedPolyPolygonImpl( + const Image& rTexture, + const ::basegfx::B2DPolyPolygon& rPolyPolygon, + const ::basegfx::B2DHomMatrix& rOverallTransform, + const ::basegfx::B2DHomMatrix& rViewTransform, + const rendering::Texture& ) +{ + // calculate final overall transform. + ::basegfx::B2DHomMatrix aOverallTransform(rOverallTransform); + aOverallTransform *= rViewTransform; + + // instead of always using the full-blown solution we + // first check to see if this is a simple rectangular + // 1-to-1 copy from source to destination image. + ::basegfx::B2DTuple aTranslate(aOverallTransform.get(0,2),aOverallTransform.get(1,2)); + ::basegfx::B2DTuple aSize(rTexture.maDesc.nWidth,rTexture.maDesc.nHeight); + ::basegfx::B2DRange aRange(aTranslate,aTranslate+aSize); + ::basegfx::B2DPolyPolygon aPolyPolygon(rPolyPolygon); + aPolyPolygon.transform(aOverallTransform); + if(::basegfx::tools::isPolyPolygonEqualRectangle(aPolyPolygon,aRange)) + { + // yes, we can take the shortcut. + // but we need to clip the destination rectangle + // against the boundary of the destination image. + sal_Int32 dwSrcX(0); + sal_Int32 dwSrcY(0); + sal_Int32 dwDstX(static_cast<sal_Int32>(aTranslate.getX())); + sal_Int32 dwDstY(static_cast<sal_Int32>(aTranslate.getY())); + sal_Int32 dwWidth(rTexture.maDesc.nWidth); + sal_Int32 dwHeight(rTexture.maDesc.nHeight); + + // prevent fast copy if destination position is not an + // integer coordinate. otherwise we would most probably + // introduce visual glitches while combining this with + // high-accuracy rendering stuff. + if( ::basegfx::fTools::equalZero(aTranslate.getX()-dwDstX) && + ::basegfx::fTools::equalZero(aTranslate.getY()-dwDstY)) + { + // clip against destination boundary. shrink size if + // necessary, modify destination position if we need to. + if(dwDstX < 0) { dwWidth-=dwDstX; dwSrcX=-dwDstX; dwDstX=0; } + if(dwDstY < 0) { dwHeight-=dwDstY; dwSrcY=-dwDstY; dwDstY=0; } + const sal_Int32 dwRight(dwDstX+dwWidth); + const sal_Int32 dwBottom(dwDstY+dwHeight); + if(dwRight > dwWidth) + dwWidth -= dwRight-dwWidth; + if(dwBottom > dwHeight) + dwHeight -= dwBottom-dwHeight; + + // calculate source buffer + const Description &srcDesc = rTexture.maDesc; + const sal_uInt32 dwSrcBytesPerPixel(getBytesPerPixel(srcDesc.eFormat)); + const sal_uInt32 dwSrcPitch(srcDesc.nWidth*dwSrcBytesPerPixel+srcDesc.nStride); + sal_uInt8 *pSrcBuffer = rTexture.maDesc.pBuffer+(dwSrcPitch*dwSrcX)+(dwSrcBytesPerPixel*dwSrcY); + + // calculate destination buffer + const Description &dstDesc = maDesc; + const sal_uInt32 dwDstBytesPerPixel(getBytesPerPixel(dstDesc.eFormat)); + const sal_uInt32 dwDstPitch(dstDesc.nWidth*dwDstBytesPerPixel+dstDesc.nStride); + sal_uInt8 *pDstBuffer = maDesc.pBuffer+(dwDstPitch*dwDstY)+(dwDstBytesPerPixel*dwDstX); + + // if source and destination format match, we can simply + // copy whole scanlines. + if(srcDesc.eFormat == dstDesc.eFormat) + { + const sal_Size dwNumBytesPerScanline(dwSrcBytesPerPixel*dwWidth); + for(sal_Int32 y=0; y<dwHeight; ++y) + { + rtl_copyMemory(pDstBuffer,pSrcBuffer,dwNumBytesPerScanline); + pSrcBuffer += dwSrcPitch; + pDstBuffer += dwDstPitch; + } + } + else + { + // otherwise [formats do not match], we need to copy + // each pixel one by one and convert from source to destination format. + if(srcDesc.eFormat == FMT_A8R8G8B8 && dstDesc.eFormat == FMT_R8G8B8) + { + for(sal_Int32 y=0; y<dwHeight; ++y) + { + sal_uInt8 *pSrc=pSrcBuffer; + sal_uInt8 *pDst=pDstBuffer; + for(sal_Int32 x=0; x<dwWidth; ++x) + { + BYTE r(*pSrc++); + BYTE g(*pSrc++); + BYTE b(*pSrc++); + BYTE Alpha(*pSrc++); + BYTE OneMinusAlpha(0xFF-Alpha); + *pDst=(((r*Alpha)+((*pDst)*OneMinusAlpha))/0xFF); + ++pDst; + *pDst=(((g*Alpha)+((*pDst)*OneMinusAlpha))/0xFF); + ++pDst; + *pDst=(((b*Alpha)+((*pDst)*OneMinusAlpha))/0xFF); + ++pDst; + } + pSrcBuffer += dwSrcPitch; + pDstBuffer += dwDstPitch; + } + } + else if(srcDesc.eFormat == FMT_R8G8B8 && dstDesc.eFormat == FMT_A8R8G8B8) + { + for(sal_Int32 y=0; y<dwHeight; ++y) + { + sal_uInt8 *pSrc=pSrcBuffer; + sal_uInt8 *pDst=pDstBuffer; + for(sal_Int32 x=0; x<dwWidth; ++x) + { + BYTE r(*pSrc++); + BYTE g(*pSrc++); + BYTE b(*pSrc++); + *pDst++=r; + *pDst++=g; + *pDst++=b; + *pDst++=0xFF; + } + pSrcBuffer += dwSrcPitch; + pDstBuffer += dwDstPitch; + } + } + } + + return ImageCachedPrimitiveSharedPtr(); + } + } + + typedef cachedPrimitiveFTPP<pixel_format,span_gen_type> cachedPrimitive_t; + cachedPrimitive_t *pPrimitive = new cachedPrimitive_t( rOverallTransform, + rViewTransform, + maRenderingBuffer, + rTexture.maRenderingBuffer); + + agg::path_storage path; + agg::conv_curve<agg::path_storage> curve(path); + + for(sal_uInt32 nPolygon(0); nPolygon < rPolyPolygon.count(); nPolygon++) + { + const basegfx::B2DPolygon aPolygon(rPolyPolygon.getB2DPolygon(nPolygon)); + const sal_uInt32 nPointCount(aPolygon.count()); + + if(nPointCount) + { + if(aPolygon.areControlPointsUsed()) + { + // prepare edge-based loop + basegfx::B2DPoint aCurrentPoint(aPolygon.getB2DPoint(0)); + const sal_uInt32 nEdgeCount(aPolygon.isClosed() ? nPointCount - 1 : nPointCount); + + // first vertex + path.move_to(aCurrentPoint.getX(), aCurrentPoint.getY()); + + for(sal_uInt32 a(0); a < nEdgeCount; a++) + { + // access next point + const sal_uInt32 nNextIndex((a + 1) % nPointCount); + const basegfx::B2DPoint aNextPoint(aPolygon.getB2DPoint(nNextIndex)); + + // get control points + const basegfx::B2DPoint aControlNext(aPolygon.getNextControlPoint(a)); + const basegfx::B2DPoint aControlPrev(aPolygon.getPrevControlPoint(nNextIndex)); + + // specify first cp, second cp, next vertex + path.curve4( + aControlNext.getX(), aControlNext.getY(), + aControlPrev.getX(), aControlPrev.getY(), + aNextPoint.getX(), aNextPoint.getY()); + + // prepare next step + aCurrentPoint = aNextPoint; + } + } + else + { + const basegfx::B2DPoint aPoint(aPolygon.getB2DPoint(0)); + pPrimitive->ras.move_to_d(aPoint.getX(), aPoint.getY()); + + for(sal_uInt32 a(1); a < nPointCount; a++) + { + const basegfx::B2DPoint aVertexPoint(aPolygon.getB2DPoint(a)); + pPrimitive->ras.line_to_d(aVertexPoint.getX(), aVertexPoint.getY()); + } + + if(aPolygon.isClosed()) + { + pPrimitive->ras.close_polygon(); + } + } + } + } + + pPrimitive->ras.add_path(curve); + pPrimitive->redraw(); + + return ImageCachedPrimitiveSharedPtr(pPrimitive); +} + +////////////////////////////////////////////////////////////////////////////////// +// Image::fillTexturedPolyPolygon +////////////////////////////////////////////////////////////////////////////////// + +ImageCachedPrimitiveSharedPtr Image::fillTexturedPolyPolygon( + const Image& rTexture, + const ::basegfx::B2DPolyPolygon& rPolyPolygon, + const ::basegfx::B2DHomMatrix& rOverallTransform, + const ::basegfx::B2DHomMatrix& rViewTransform, + const rendering::Texture& texture ) +{ + typedef agg::wrap_mode_repeat wrap_x_type; + typedef agg::wrap_mode_repeat wrap_y_type; + typedef agg::pixfmt_rgb24 pixfmt_rgb24; + typedef agg::pixfmt_rgba32 pixfmt_rgba32; +#if AGG_VERSION >= 2400 + typedef agg::image_accessor_wrap< pixfmt_rgba32, wrap_x_type, wrap_y_type > img_source_type_rgba; + typedef agg::image_accessor_wrap< pixfmt_rgb24, wrap_x_type, wrap_y_type > img_source_type_rgb; + + typedef agg::span_image_resample_rgba_affine< img_source_type_rgba > span_gen_type_rgba; + typedef agg::span_image_resample_rgb_affine< img_source_type_rgb > span_gen_type_rgb; +#else + typedef agg::span_pattern_resample_rgba_affine< pixfmt_rgba32::color_type, + pixfmt_rgba32::order_type, + wrap_x_type, + wrap_y_type> span_gen_type_rgba; + typedef agg::span_pattern_resample_rgb_affine< pixfmt_rgb24::color_type, + pixfmt_rgb24::order_type, + wrap_x_type, + wrap_y_type> span_gen_type_rgb; +#endif + + const Format nDest = maDesc.eFormat; + const Format nSource = rTexture.maDesc.eFormat; + + if(nDest == FMT_R8G8B8 && nSource == FMT_R8G8B8) + { + return fillTexturedPolyPolygonImpl< agg::pixfmt_rgb24, + span_gen_type_rgb >( + rTexture, + rPolyPolygon, + rOverallTransform, + rViewTransform, + texture ); + } + else if(nDest == FMT_R8G8B8 && nSource == FMT_A8R8G8B8) + { + return fillTexturedPolyPolygonImpl< agg::pixfmt_rgb24, + span_gen_type_rgba >( + rTexture, + rPolyPolygon, + rOverallTransform, + rViewTransform, + texture ); + } + else if(nDest == FMT_A8R8G8B8 && nSource == FMT_R8G8B8) + { + return fillTexturedPolyPolygonImpl< agg::pixfmt_rgba32, + span_gen_type_rgb >( + rTexture, + rPolyPolygon, + rOverallTransform, + rViewTransform, + texture ); + } + else if(nDest == FMT_A8R8G8B8 && nSource == FMT_A8R8G8B8) + { + return fillTexturedPolyPolygonImpl< agg::pixfmt_rgba32, + span_gen_type_rgba >( + rTexture, + rPolyPolygon, + rOverallTransform, + rViewTransform, + texture ); + } + else + { + OSL_ENSURE(false, "Unexpected pixel format"); + } + + return ImageCachedPrimitiveSharedPtr(); +} + +////////////////////////////////////////////////////////////////////////////////// +// Image::fillGradient +////////////////////////////////////////////////////////////////////////////////// + +template<class pixel_format> +void Image::fillGradientImpl( const ParametricPolyPolygon::Values& rValues, + const uno::Sequence< double >& rUnoColor1, + const uno::Sequence< double >& rUnoColor2, + const ::basegfx::B2DPolyPolygon& rPolyPolygon, + const ::basegfx::B2DHomMatrix& rOverallTransform, + const rendering::Texture& ) +{ + const ARGB aColor1(0xFFFFFFFF, + rUnoColor1); + const ARGB aColor2(0xFFFFFFFF, + rUnoColor2); + + // first of all we need to provide the framebuffer we want to render to. + // the properties of the framebuffer are + // 1) memory & layout [width, height, stride] + // 2) pixelformat + // 3) clipping + + // Class template pixel_formats_rgb24 has full knowledge about this + // particular pixel format in memory. The only template parameter + // can be order_rgb24 or order_bgr24 that determines the order of color channels. + pixel_format pixf(maRenderingBuffer); + + // There are two basic renderers with almost the same functionality: + // renderer_base and renderer_mclip. The first one is used most often + // and it performs low level clipping. + // This simply adds clipping to the graphics buffer, the clip rect + // will be initialized to the area of the framebuffer. + typedef agg::renderer_base<pixel_format> renderer_base; + renderer_base rb(pixf); + + // bounding rectangle of untransformed polypolygon + const ::basegfx::B2DRange& rBounds(::basegfx::tools::getRange(rPolyPolygon)); + + // the color generator produces a specific color from + // some given interpolation value. + // number of steps for color interpolation + typedef typename pixel_format::color_type color_type; + color_type color1(agg::rgba8(aColor1.Color.r, + aColor1.Color.g, + aColor1.Color.b, + 255)); + color_type color2(agg::rgba8(aColor2.Color.r, + aColor2.Color.g, + aColor2.Color.b, + 255)); + typedef color_generator_adaptor<color_type> color_generator_type; + unsigned int dwNumSteps = static_cast<unsigned int>(rBounds.getWidth()); + color_generator_type colors(color1,color2,dwNumSteps); + colors.set_linear(true); + + // color = f(x,y) + gradient_polymorphic_wrapper<agg::gradient_x> gf_x; + gradient_polymorphic_wrapper<agg::gradient_radial> gf_radial; + gradient_polymorphic_wrapper<gradient_rect> gf_rectangular; + gf_rectangular.m_gradient.width = static_cast<int>(rBounds.getWidth())<<4; + gf_rectangular.m_gradient.height = static_cast<int>(rBounds.getHeight())<<4; + const gradient_polymorphic_wrapper_base *gf[] = { &gf_x, // GRADIENT_LINEAR + &gf_x, // GRADIENT_AXIAL + &gf_radial, // GRADIENT_ELLIPTICAL + &gf_rectangular // GRADIENT_RECTANGULAR + }; + + // how do texture coordinates change when the pixel coordinate change? + typedef agg::span_interpolator_linear<> interpolator_type; + agg::trans_affine tm; + tm *= agg::trans_affine_scaling(1.0f/rBounds.getWidth(), + 1.0f/rBounds.getHeight()); + if(rValues.meType == ParametricPolyPolygon::GRADIENT_ELLIPTICAL || + rValues.meType == ParametricPolyPolygon::GRADIENT_RECTANGULAR) + { + //tm *= trans_affine_scaling(mnAspectRatio,+1.0f); + //const double fAspect = aBounds.getWidth()/aBounds.getHeight(); + //tm *= trans_affine_scaling(+0.5f,+0.5f*(1.0f/fAspect)); + //tm *= trans_affine_translation(+0.5f,+0.5f); + tm *= agg::trans_affine_scaling(+0.5f,+0.5f); + tm *= agg::trans_affine_translation(+0.5f,+0.5f); + } + tm *= transAffineFromB2DHomMatrix(rOverallTransform); + tm.invert(); + interpolator_type inter(tm); + + // spanline allocators reserve memory for the color values + // filled up by the spanline generators. + typedef agg::span_allocator<color_type> gradient_span_alloc; + gradient_span_alloc span_alloc; + + // scanline generators create the actual color values for + // some specific coordinate range of a scanline. + typedef agg::span_gradient<color_type, + interpolator_type, + gradient_polymorphic_wrapper_base, + color_generator_type > gradient_span_gen; +#if AGG_VERSION >= 2400 + gradient_span_gen span_gen(inter, + *gf[rValues.meType], + colors, + 0, + dwNumSteps); +#else + gradient_span_gen span_gen(span_alloc, + inter, + *gf[rValues.meType], + colors, + 0, + dwNumSteps); +#endif + + // To draw Anti-Aliased primitives one shoud *rasterize* them first. + // The primary rasterization technique in AGG is scanline based. + // That is, a polygon is converted into a number of horizontal + // scanlines and then the scanlines are being rendered one by one. + // To transfer information from a rasterizer to the scanline renderer + // there scanline containers are used. A scanline consists of a + // number of horizontal, non-intersecting spans. All spans must be ordered by X. + // --> packed scanline container + agg::scanline_p8 sl; + + // antialiased scanline renderer with pattern filling capability + // [in contrast to solid renderers, that is] + // the instance of this particular renderer combines the + // renderbuffer [i.e. destination] and the spanline generator [i.e. source] +#if AGG_VERSION >= 2400 + typedef agg::renderer_scanline_aa<renderer_base, gradient_span_alloc, gradient_span_gen> renderer_gradient; + renderer_gradient r1(rb, span_alloc, span_gen); +#else + typedef agg::renderer_scanline_aa<renderer_base, gradient_span_gen> renderer_gradient; + renderer_gradient r1(rb, span_gen); +#endif + + // instantiate the rasterizer and feed the incoming polypolygon. + agg::rasterizer_scanline_aa<> ras; + agg::path_storage path; + agg::conv_curve<agg::path_storage> curve(path); + + for(sal_uInt32 nPolygon(0); nPolygon < rPolyPolygon.count(); nPolygon++) + { + const basegfx::B2DPolygon aPolygon(rPolyPolygon.getB2DPolygon(nPolygon)); + const sal_uInt32 nPointCount(aPolygon.count()); + + if(nPointCount) + { + if(aPolygon.areControlPointsUsed()) + { + // prepare edge-based loop + basegfx::B2DPoint aCurrentPoint(aPolygon.getB2DPoint(0)); + const sal_uInt32 nEdgeCount(aPolygon.isClosed() ? nPointCount - 1 : nPointCount); + + // first vertex + path.move_to(aCurrentPoint.getX(), aCurrentPoint.getY()); + + for(sal_uInt32 a(0); a < nEdgeCount; a++) + { + // access next point + const sal_uInt32 nNextIndex((a + 1) % nPointCount); + const basegfx::B2DPoint aNextPoint(aPolygon.getB2DPoint(nNextIndex)); + + // get control points + const basegfx::B2DPoint aControlNext(aPolygon.getNextControlPoint(a)); + const basegfx::B2DPoint aControlPrev(aPolygon.getPrevControlPoint(nNextIndex)); + + // specify first cp, second cp, next vertex + path.curve4( + aControlNext.getX(), aControlNext.getY(), + aControlPrev.getX(), aControlPrev.getY(), + aNextPoint.getX(), aNextPoint.getY()); + + // prepare next step + aCurrentPoint = aNextPoint; + } + } + else + { + const basegfx::B2DPoint aPoint(aPolygon.getB2DPoint(0)); + ras.move_to_d(aPoint.getX(), aPoint.getY()); + + for(sal_uInt32 a(1); a < nPointCount; a++) + { + const basegfx::B2DPoint aVertexPoint(aPolygon.getB2DPoint(a)); + ras.line_to_d(aVertexPoint.getX(), aVertexPoint.getY()); + } + + if(aPolygon.isClosed()) + { + ras.close_polygon(); + } + } + } + } + + // everything is up and running, go... + ras.add_path(curve); + render_scanlines(ras,sl,r1); +} + +////////////////////////////////////////////////////////////////////////////////// +// Image::fillGradient +////////////////////////////////////////////////////////////////////////////////// + +void Image::fillGradient( const ParametricPolyPolygon::Values& rValues, + const uno::Sequence< double >& rUnoColor1, + const uno::Sequence< double >& rUnoColor2, + const ::basegfx::B2DPolyPolygon& rPolyPolygon, + const ::basegfx::B2DHomMatrix& rOverallTransform, + const rendering::Texture& texture ) +{ + switch(maDesc.eFormat) + { + case FMT_R8G8B8: + fillGradientImpl<agg::pixfmt_rgb24>(rValues,rUnoColor1,rUnoColor2,rPolyPolygon,rOverallTransform,texture); + break; + case FMT_A8R8G8B8: + fillGradientImpl<agg::pixfmt_rgba32>(rValues,rUnoColor1,rUnoColor2,rPolyPolygon,rOverallTransform,texture); + break; + default: + OSL_ENSURE(false, "Unexpected pixel format"); + break; + } +} + +////////////////////////////////////////////////////////////////////////////////// +// Image::fromVCLBitmap +////////////////////////////////////////////////////////////////////////////////// + +bool Image::fromVCLBitmap( ::BitmapEx& rBmpEx ) +{ + const ::Size aBmpSize( rBmpEx.GetSizePixel() ); + Image::Description desc; + desc.eFormat = rBmpEx.IsTransparent() ? FMT_A8R8G8B8 : FMT_R8G8B8; + desc.nWidth = aBmpSize.Width(); + desc.nHeight = aBmpSize.Height(); + desc.nStride = 0; + const sal_uInt32 nPitch(desc.nWidth*getBytesPerPixel(desc.eFormat)+desc.nStride); + desc.pBuffer = new sal_uInt8 [nPitch*desc.nHeight]; + maDesc = desc; + mbBufferHasUserOwnership = false; + maRenderingBuffer.attach(static_cast<agg::int8u *>(desc.pBuffer), + desc.nWidth, + desc.nHeight, + nPitch); + RawABGRBitmap aBmpData; + aBmpData.mnWidth = aBmpSize.Width(); + aBmpData.mnHeight = aBmpSize.Height(); + aBmpData.mpBitmapData = static_cast<sal_uInt8 *>(desc.pBuffer); + vclBitmapEx2Raw(rBmpEx,aBmpData); + + return true; +} + +////////////////////////////////////////////////////////////////////////////////// +// Image::Image +////////////////////////////////////////////////////////////////////////////////// + +Image::Image( const Description& rDesc ) : + maDesc( rDesc ), + maRenderingBuffer(), + mbBufferHasUserOwnership( rDesc.pBuffer != NULL ) +{ +#if defined(PROFILER) + for(int i=0; i<TIMER_MAX; ++i) + maElapsedTime[i]=0.0; +#endif + + // allocate own buffer memory, if not provided + sal_uInt8* pBuffer = maDesc.pBuffer; + const sal_uInt32 nWidth(maDesc.nWidth); + const sal_uInt32 nHeight(maDesc.nHeight); + const sal_uInt32 nStride(maDesc.nStride); + const sal_uInt32 nPitch(nWidth*getBytesPerPixel(maDesc.eFormat) + + nStride); + + if( !pBuffer ) + pBuffer = new sal_uInt8 [nPitch*nHeight]; + + maDesc.pBuffer = pBuffer; + + // attach graphics buffer + maRenderingBuffer.attach( + static_cast<agg::int8u *>(pBuffer), + nWidth, + nHeight, + nPitch ); +} + +////////////////////////////////////////////////////////////////////////////////// +// Image::Image +////////////////////////////////////////////////////////////////////////////////// + +Image::Image( const uno::Reference< rendering::XBitmap >& xBitmap ) : + maDesc(), + maRenderingBuffer(), + mbBufferHasUserOwnership( false ) +{ +#if defined(PROFILER) + for(int i=0; i<TIMER_MAX; ++i) + maElapsedTime[i]=0.0; +#endif + + // TODO(F1): Add support for floating point bitmap formats + uno::Reference<rendering::XIntegerReadOnlyBitmap> xIntBmp(xBitmap, + uno::UNO_QUERY_THROW); + ::BitmapEx aBmpEx = ::vcl::unotools::bitmapExFromXBitmap(xIntBmp); + if( !!aBmpEx ) + fromVCLBitmap(aBmpEx); + + // TODO(F2): Fallback to XIntegerBitmap interface for import + OSL_ENSURE(false, + "Image::Image(): Cannot retrieve bitmap data!" ); +} + +////////////////////////////////////////////////////////////////////////////////// +// Image::~Image +////////////////////////////////////////////////////////////////////////////////// + +Image::~Image() +{ +#if defined(PROFILER) + + double aAccumulatedTime(0.0); + for(int i=0; i<TIMER_MAX; ++i) + aAccumulatedTime += maElapsedTime[i]; + + OSL_TRACE("Image %d - %d %d %d %d %d\n",(int)(aAccumulatedTime*1000.0), + (int)(maElapsedTime[TIMER_FILLTEXTUREDPOLYPOLYGON]*1000.0), + (int)(maElapsedTime[TIMER_FILLB2DPOLYPOLYGON]*1000.0), + (int)(maElapsedTime[TIMER_DRAWPOLYPOLYGON]*1000.0), + (int)(maElapsedTime[TIMER_FILLPOLYPOLYGON]*1000.0), + (int)(maElapsedTime[TIMER_DRAWBITMAP]*1000.0)); + +#endif + + if( !mbBufferHasUserOwnership ) + delete [] maDesc.pBuffer; +} + +////////////////////////////////////////////////////////////////////////////////// +// Image::clear +////////////////////////////////////////////////////////////////////////////////// + +template<class pixel_format> +void Image::clearImpl( sal_uInt8 a, + sal_uInt8 r, + sal_uInt8 g, + sal_uInt8 b ) +{ + pixel_format pixf(maRenderingBuffer); + agg::renderer_base<pixel_format> renb(pixf); + + renb.clear(agg::rgba8(r,g,b,a)); +} + +////////////////////////////////////////////////////////////////////////////////// +// Image::clear +////////////////////////////////////////////////////////////////////////////////// + +void Image::clear( sal_uInt8 a, + sal_uInt8 r, + sal_uInt8 g, + sal_uInt8 b ) +{ + switch(maDesc.eFormat) + { + case FMT_R8G8B8: + return clearImpl<agg::pixfmt_rgb24>(a,r,g,b); + case FMT_A8R8G8B8: + return clearImpl<agg::pixfmt_rgba32>(a,r,g,b); + default: + OSL_ENSURE(false, "Unexpected pixel format"); + break; + } +} + +////////////////////////////////////////////////////////////////////////////////// +// Image::fillB2DPolyPolygon +////////////////////////////////////////////////////////////////////////////////// + +void Image::fillB2DPolyPolygon( + const ::basegfx::B2DPolyPolygon& rPolyPolygon, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) +{ +#if defined(PROFILER) + ScopeTimer aTimer(TIMER_FILLB2DPOLYPOLYGON,this); +#endif + + switch(maDesc.eFormat) + { + case FMT_R8G8B8: + fillPolyPolygonImpl<agg::pixfmt_rgb24>(rPolyPolygon,viewState,renderState); + break; + case FMT_A8R8G8B8: + fillPolyPolygonImpl<agg::pixfmt_rgba32>(rPolyPolygon,viewState,renderState); + break; + default: + OSL_ENSURE(false, "Unexpected pixel format"); + break; + } +} + +////////////////////////////////////////////////////////////////////////////////// +// Image::lock +////////////////////////////////////////////////////////////////////////////////// + +sal_uInt8* Image::lock() const +{ + return maDesc.pBuffer; +} + +////////////////////////////////////////////////////////////////////////////////// +// Image::unlock +////////////////////////////////////////////////////////////////////////////////// + +void Image::unlock() const +{ +} + +////////////////////////////////////////////////////////////////////////////////// +// Image::getWidth +////////////////////////////////////////////////////////////////////////////////// + +sal_uInt32 Image::getWidth() const +{ + return maDesc.nWidth; +} + +////////////////////////////////////////////////////////////////////////////////// +// Image::getHeight +////////////////////////////////////////////////////////////////////////////////// + +sal_uInt32 Image::getHeight() const +{ + return maDesc.nHeight; +} + +////////////////////////////////////////////////////////////////////////////////// +// Image::getStride +////////////////////////////////////////////////////////////////////////////////// + +sal_uInt32 Image::getStride() const +{ + return maDesc.nWidth*getBytesPerPixel(maDesc.eFormat)+maDesc.nStride; +} + +////////////////////////////////////////////////////////////////////////////////// +// Image::getFormat +////////////////////////////////////////////////////////////////////////////////// + +IColorBuffer::Format Image::getFormat() const +{ + return maDesc.eFormat; +} + +////////////////////////////////////////////////////////////////////////////////// +// Image::drawPoint +////////////////////////////////////////////////////////////////////////////////// + +void Image::drawPoint( const geometry::RealPoint2D& /*aPoint*/, + const rendering::ViewState& /*viewState*/, + const rendering::RenderState& /*renderState*/ ) +{ + OSL_ENSURE(false, + "Image::drawPoint(): NYI" ); +} + +////////////////////////////////////////////////////////////////////////////////// +// Image::drawLine +////////////////////////////////////////////////////////////////////////////////// + +void Image::drawLine( const geometry::RealPoint2D& aStartPoint, + const geometry::RealPoint2D& aEndPoint, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) +{ + ::basegfx::B2DPolygon aLinePoly; + aLinePoly.append( + ::basegfx::unotools::b2DPointFromRealPoint2D( aStartPoint ) ); + aLinePoly.append( + ::basegfx::unotools::b2DPointFromRealPoint2D( aEndPoint ) ); + + drawLinePolyPolygon( ::basegfx::B2DPolyPolygon( aLinePoly ), + 1.0, + viewState, + renderState ); +} + +////////////////////////////////////////////////////////////////////////////////// +// Image::drawBezier +////////////////////////////////////////////////////////////////////////////////// + +void Image::drawBezier( const geometry::RealBezierSegment2D& aBezierSegment, + const geometry::RealPoint2D& aEndPoint, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) +{ + basegfx::B2DPolygon aBezierPoly; + + aBezierPoly.append(basegfx::B2DPoint(aBezierSegment.Px, aBezierSegment.Py)); + aBezierPoly.appendBezierSegment( + basegfx::B2DPoint(aBezierSegment.C1x, aBezierSegment.C1y), + basegfx::B2DPoint(aBezierSegment.C2x, aBezierSegment.C2y), + basegfx::unotools::b2DPointFromRealPoint2D(aEndPoint)); + + drawLinePolyPolygon( ::basegfx::B2DPolyPolygon( aBezierPoly ), + 1.0, + viewState, + renderState ); +} + +////////////////////////////////////////////////////////////////////////////////// +// Image::drawPolyPolygon +////////////////////////////////////////////////////////////////////////////////// + +ImageCachedPrimitiveSharedPtr Image::drawPolyPolygon( + const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) +{ +#if defined(PROFILER) + ScopeTimer aTimer(TIMER_DRAWPOLYPOLYGON,this); +#endif + + if( !xPolyPolygon.is() ) + return ImageCachedPrimitiveSharedPtr(); + + drawLinePolyPolygon( ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D( xPolyPolygon ), + 1.0, + viewState, + renderState ); + + // TODO(F2): Implement sensible ImageCachedPrimitive + return ImageCachedPrimitiveSharedPtr(); +} + +////////////////////////////////////////////////////////////////////////////////// +// Image::strokePolyPolygon +////////////////////////////////////////////////////////////////////////////////// + +ImageCachedPrimitiveSharedPtr Image::strokePolyPolygon( + const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState, + const rendering::StrokeAttributes& strokeAttributes ) +{ + if( !xPolyPolygon.is() ) + return ImageCachedPrimitiveSharedPtr(); + + drawLinePolyPolygon( ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D( xPolyPolygon ), + strokeAttributes.StrokeWidth, + viewState, + renderState ); + + // TODO(F2): Implement sensible ImageCachedPrimitive + return ImageCachedPrimitiveSharedPtr(); +} + +////////////////////////////////////////////////////////////////////////////////// +// Image::strokeTexturedPolyPolygon +////////////////////////////////////////////////////////////////////////////////// + +ImageCachedPrimitiveSharedPtr Image::strokeTexturedPolyPolygon( + const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/, + const rendering::ViewState& /*viewState*/, + const rendering::RenderState& /*renderState*/, + const uno::Sequence< rendering::Texture >& /*textures*/, + const ::std::vector< ::boost::shared_ptr<Image> >& /*textureAnnotations*/, + const rendering::StrokeAttributes& /*strokeAttributes*/ ) +{ + OSL_ENSURE(false, + "Image::strokeTexturedPolyPolygon(): NYI" ); + + // TODO(F2): Implement sensible ImageCachedPrimitive + return ImageCachedPrimitiveSharedPtr(); +} + +////////////////////////////////////////////////////////////////////////////////// +// Image::strokeTextureMappedPolyPolygon +////////////////////////////////////////////////////////////////////////////////// + +ImageCachedPrimitiveSharedPtr Image::strokeTextureMappedPolyPolygon( + const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/, + const rendering::ViewState& /*viewState*/, + const rendering::RenderState& /*renderState*/, + const uno::Sequence< rendering::Texture >& /*textures*/, + const ::std::vector< ::boost::shared_ptr<Image> >& /*textureAnnotations*/, + const uno::Reference< geometry::XMapping2D >& /*xMapping*/, + const rendering::StrokeAttributes& /*strokeAttributes*/ ) +{ + OSL_ENSURE(false, + "Image::strokeTextureMappedPolyPolygon(): NYI" ); + + // TODO(F2): Implement sensible ImageCachedPrimitive + return ImageCachedPrimitiveSharedPtr(); +} + +////////////////////////////////////////////////////////////////////////////////// +// Image::fillPolyPolygon +////////////////////////////////////////////////////////////////////////////////// + +template<class pixel_format> +ImageCachedPrimitiveSharedPtr Image::fillPolyPolygonImpl( + const ::basegfx::B2DPolyPolygon& rPolyPolygon, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) +{ +#if defined(PROFILER) + ScopeTimer aTimer(TIMER_FILLPOLYPOLYGON,this); +#endif + + ARGB aFillColor; + + ::basegfx::B2DPolyPolygon aPolyPolygon(rPolyPolygon); + setupPolyPolygon( aPolyPolygon, true, aFillColor, viewState, renderState ); + + if( !aPolyPolygon.count() ) + return ImageCachedPrimitiveSharedPtr(); + + pixel_format pixf(maRenderingBuffer); + agg::renderer_base<pixel_format> renb(pixf); + + // Scanline renderer for solid filling. + agg::renderer_scanline_aa_solid<agg::renderer_base<pixel_format> > ren(renb); + + // Rasterizer & scanline + agg::rasterizer_scanline_aa<> ras; + agg::scanline_p8 sl; + + agg::path_storage path; + agg::conv_curve<agg::path_storage> curve(path); + + for(sal_uInt32 nPolygon(0); nPolygon < aPolyPolygon.count(); nPolygon++) + { + const basegfx::B2DPolygon aPolygon(aPolyPolygon.getB2DPolygon(nPolygon)); + const sal_uInt32 nPointCount(aPolygon.count()); + + if(nPointCount) + { + if(aPolygon.areControlPointsUsed()) + { + // prepare edge-based loop + basegfx::B2DPoint aCurrentPoint(aPolygon.getB2DPoint(0)); + const sal_uInt32 nEdgeCount(aPolygon.isClosed() ? nPointCount - 1 : nPointCount); + + // first vertex + path.move_to(aCurrentPoint.getX(), aCurrentPoint.getY()); + + for(sal_uInt32 a(0); a < nEdgeCount; a++) + { + // access next point + const sal_uInt32 nNextIndex((a + 1) % nPointCount); + const basegfx::B2DPoint aNextPoint(aPolygon.getB2DPoint(nNextIndex)); + + // get control points + const basegfx::B2DPoint aControlNext(aPolygon.getNextControlPoint(a)); + const basegfx::B2DPoint aControlPrev(aPolygon.getPrevControlPoint(nNextIndex)); + + // specify first cp, second cp, next vertex + path.curve4( + aControlNext.getX(), aControlNext.getY(), + aControlPrev.getX(), aControlPrev.getY(), + aNextPoint.getX(), aNextPoint.getY()); + + // prepare next step + aCurrentPoint = aNextPoint; + } + } + else + { + const basegfx::B2DPoint aPoint(aPolygon.getB2DPoint(0)); + ras.move_to_d(aPoint.getX(), aPoint.getY()); + + for(sal_uInt32 a(1); a < nPointCount; a++) + { + const basegfx::B2DPoint aVertexPoint(aPolygon.getB2DPoint(a)); + ras.line_to_d(aVertexPoint.getX(), aVertexPoint.getY()); + } + + if(aPolygon.isClosed()) + { + ras.close_polygon(); + } + } + } + } + + ras.add_path(curve); + agg::rgba8 fillcolor(aFillColor.Color.r,aFillColor.Color.g,aFillColor.Color.b,aFillColor.Color.a); + ren.color(fillcolor); + agg::render_scanlines(ras, sl, ren); + + // TODO(F2): Implement sensible ImageCachedPrimitive + return ImageCachedPrimitiveSharedPtr(); +} + +////////////////////////////////////////////////////////////////////////////////// +// Image::fillPolyPolygon +////////////////////////////////////////////////////////////////////////////////// + +ImageCachedPrimitiveSharedPtr Image::fillPolyPolygon( + const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) +{ + if( !xPolyPolygon.is() ) + return ImageCachedPrimitiveSharedPtr(); + + ::basegfx::B2DPolyPolygon aPoly( + ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D( xPolyPolygon ) ); + + switch(maDesc.eFormat) + { + case FMT_R8G8B8: + return fillPolyPolygonImpl<agg::pixfmt_rgb24>(aPoly,viewState,renderState); + case FMT_A8R8G8B8: + return fillPolyPolygonImpl<agg::pixfmt_rgba32>(aPoly,viewState,renderState); + default: + OSL_ENSURE(false, "Unexpected pixel format"); + break; + } + + return ImageCachedPrimitiveSharedPtr(); +} + +////////////////////////////////////////////////////////////////////////////////// +// Image::fillTexturedPolyPolygon +////////////////////////////////////////////////////////////////////////////////// + +ImageCachedPrimitiveSharedPtr Image::fillTexturedPolyPolygon( + const uno::Reference< rendering::XPolyPolygon2D >& xPolyPolygon, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState, + const uno::Sequence< rendering::Texture >& textures, + const ::std::vector< ::boost::shared_ptr<Image> >& textureAnnotations ) +{ +#if defined(PROFILER) + ScopeTimer aTimer(TIMER_FILLTEXTUREDPOLYPOLYGON,this); +#endif + + if( !xPolyPolygon.is() ) + return ImageCachedPrimitiveSharedPtr(); + + ::basegfx::B2DPolyPolygon aPoly( + ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D( xPolyPolygon ) ); + ARGB aFillColor; + + setupPolyPolygon( aPoly, true, aFillColor, viewState, renderState ); + + if( !aPoly.count() ) + return ImageCachedPrimitiveSharedPtr(); + + ::basegfx::B2DHomMatrix aViewTransform; + ::basegfx::B2DHomMatrix aRenderTransform; + ::basegfx::B2DHomMatrix aTextureTransform; + + ::basegfx::unotools::homMatrixFromAffineMatrix(aTextureTransform, + textures[0].AffineTransform); + ::basegfx::unotools::homMatrixFromAffineMatrix(aRenderTransform, + renderState.AffineTransform); + ::basegfx::unotools::homMatrixFromAffineMatrix(aViewTransform, + viewState.AffineTransform); + aTextureTransform *= aRenderTransform; + + // TODO(F1): Multi-texturing + if( textures[0].Gradient.is() ) + { + aTextureTransform *= aViewTransform; + + // try to cast XParametricPolyPolygon2D reference to + // our implementation class. + ::canvas::ParametricPolyPolygon* pGradient = + dynamic_cast< ::canvas::ParametricPolyPolygon* >( textures[0].Gradient.get() ); + + if( pGradient ) + { + const ParametricPolyPolygon::Values& rValues( + pGradient->getValues() ); + + // TODO: use all the colors and place them on given positions/stops + // TODO(E1): Return value + // TODO(F1): FillRule + fillGradient( rValues, + rValues.maColors [0], + rValues.maColors [rValues.maColors.getLength () - 1], + aPoly, + aTextureTransform, + textures[0] ); + } + } + else if( textures[0].Bitmap.is() ) + { + ImageSharedPtr pTexture; + + if( textureAnnotations[0].get() != NULL ) + pTexture = textureAnnotations[0]; + else + pTexture.reset( new Image( textures[0].Bitmap ) ); + + const sal_uInt32 nWidth(pTexture->maDesc.nWidth); + const sal_uInt32 nHeight(pTexture->maDesc.nHeight); + + // scale texture into one-by-one unit rect. + aTextureTransform.scale(1.0f/nWidth, + 1.0f/nHeight); + + // TODO(E1): Return value + // TODO(F1): FillRule + return fillTexturedPolyPolygon( *pTexture, + aPoly, + aTextureTransform, + aViewTransform, + textures[0] ); + } + + // TODO(F2): Implement sensible ImageCachedPrimitive + return ImageCachedPrimitiveSharedPtr(); +} + +////////////////////////////////////////////////////////////////////////////////// +// Image::fillTextureMappedPolyPolygon +////////////////////////////////////////////////////////////////////////////////// + +ImageCachedPrimitiveSharedPtr Image::fillTextureMappedPolyPolygon( + const uno::Reference< rendering::XPolyPolygon2D >& /*xPolyPolygon*/, + const rendering::ViewState& /*viewState*/, + const rendering::RenderState& /*renderState*/, + const uno::Sequence< rendering::Texture >& /*textures*/, + const ::std::vector< ::boost::shared_ptr<Image> >& /*textureAnnotations*/, + const uno::Reference< geometry::XMapping2D >& /*xMapping*/ ) +{ + OSL_ENSURE(false, + "Image::fillTextureMappedPolyPolygon(): NYI" ); + + // TODO(F2): Implement sensible ImageCachedPrimitive + return ImageCachedPrimitiveSharedPtr(); +} + +////////////////////////////////////////////////////////////////////////////////// +// Image::drawBitmap +////////////////////////////////////////////////////////////////////////////////// + +ImageCachedPrimitiveSharedPtr Image::drawBitmap( + const uno::Reference< rendering::XBitmap >& xBitmap, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) +{ +#if defined(PROFILER) + ScopeTimer aTimer(TIMER_DRAWBITMAP,this); +#endif + + // TODO(P3): Implement bitmap caching + if( !xBitmap.is() ) + return ImageCachedPrimitiveSharedPtr(); +/* + XBitmapAccessor accessor( xBitmap ); + if(accessor.isValid()) + { + Image aImage( accessor.getDesc() ); + + implDrawBitmap( aImage, + viewState, + renderState ); + + // TODO(F2): Implement sensible ImageCachedPrimitive + return ImageCachedPrimitiveSharedPtr(); + } +*/ + Image aImage( xBitmap ); + + return implDrawBitmap( aImage,viewState,renderState ); +} + +////////////////////////////////////////////////////////////////////////////////// +// Image::drawBitmap +////////////////////////////////////////////////////////////////////////////////// + +ImageCachedPrimitiveSharedPtr Image::drawBitmap( + const ImageSharedPtr& rImage, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) +{ +#if defined(PROFILER) + ScopeTimer aTimer(TIMER_DRAWBITMAP,this); +#endif + + // TODO(P3): Implement bitmap caching + if( !rImage ) + return ImageCachedPrimitiveSharedPtr(); + + return implDrawBitmap( *rImage, + viewState, + renderState ); +} + +////////////////////////////////////////////////////////////////////////////////// +// Image::drawBitmapModulated +////////////////////////////////////////////////////////////////////////////////// + +ImageCachedPrimitiveSharedPtr Image::drawBitmapModulated( + const uno::Reference< rendering::XBitmap >& xBitmap, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) +{ + // TODO(P3): Implement bitmap caching + if( !xBitmap.is() ) + return ImageCachedPrimitiveSharedPtr(); + + Image aImage( xBitmap ); + + // TODO(F2): Distinguish modulated and unmodulated bitmap output + return implDrawBitmap( aImage,viewState,renderState ); +} + +////////////////////////////////////////////////////////////////////////////////// +// Image::drawBitmapModulated +////////////////////////////////////////////////////////////////////////////////// + +ImageCachedPrimitiveSharedPtr Image::drawBitmapModulated( + const ImageSharedPtr& rImage, + const rendering::ViewState& viewState, + const rendering::RenderState& renderState ) +{ + // TODO(P3): Implement bitmap caching + if( !rImage ) + return ImageCachedPrimitiveSharedPtr(); + + // TODO(F2): Distinguish modulated and unmodulated bitmap output + return implDrawBitmap( *rImage,viewState,renderState ); +} + +} diff --git a/canvas/source/tools/image.hxx b/canvas/source/tools/image.hxx new file mode 100644 index 000000000000..1c62c7d6276b --- /dev/null +++ b/canvas/source/tools/image.hxx @@ -0,0 +1,298 @@ +/************************************************************************* + * + * 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_IMAGE_HXX +#define INCLUDED_CANVAS_IMAGE_HXX + +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/rendering/XCanvas.hpp> +#include <canvas/rendering/icolorbuffer.hxx> +#include <canvas/parametricpolypolygon.hxx> +#include "imagecachedprimitive.hxx" + +#include <canvas/elapsedtime.hxx> + +#include "image_sysprereq.h" + +struct BitmapSystemData; +class BitmapEx; + +namespace canvas +{ + class Image : public IColorBuffer + { + public: + /// The description of the image + struct Description + { + IColorBuffer::Format eFormat; + sal_uInt32 nWidth; + sal_uInt32 nHeight; + sal_uInt32 nStride; + sal_uInt8* pBuffer; + }; + + /** Create a new image with the attributes passed as argument. + */ + explicit Image( const Description& desc ); + + /** Create a new image from the XBitmap passed as argument + */ + explicit Image( const ::com::sun::star::uno::Reference< + ::com::sun::star::rendering::XBitmap >& xBitmap ); + + + virtual ~Image(); + + /** Retrieve desciption of image layout + */ + const Description& getDescription() const { return maDesc; } + + /** Clear image with uniform color + */ + void clear( sal_uInt8 a, + sal_uInt8 r, + sal_uInt8 g, + sal_uInt8 b ); + + void fillB2DPolyPolygon( + const ::basegfx::B2DPolyPolygon& rPolyPolygon, + const ::com::sun::star::rendering::ViewState& viewState, + const ::com::sun::star::rendering::RenderState& renderState ); + + + // IColorBuffer interface implementation + // ===================================== + + virtual sal_uInt8* lock() const; + virtual void unlock() const; + virtual sal_uInt32 getWidth() const; + virtual sal_uInt32 getHeight() const; + virtual sal_uInt32 getStride() const; + virtual Format getFormat() const; + + + // High-level drawing operations (from the XCanvas interface) + // ========================================================== + + void drawPoint( const ::com::sun::star::geometry::RealPoint2D& aPoint, + const ::com::sun::star::rendering::ViewState& viewState, + const ::com::sun::star::rendering::RenderState& renderState ); + void drawLine( const ::com::sun::star::geometry::RealPoint2D& aStartPoint, + const ::com::sun::star::geometry::RealPoint2D& aEndPoint, + const ::com::sun::star::rendering::ViewState& viewState, + const ::com::sun::star::rendering::RenderState& renderState ); + void drawBezier( const ::com::sun::star::geometry::RealBezierSegment2D& aBezierSegment, + const ::com::sun::star::geometry::RealPoint2D& aEndPoint, + const ::com::sun::star::rendering::ViewState& viewState, + const ::com::sun::star::rendering::RenderState& renderState ); + ImageCachedPrimitiveSharedPtr drawPolyPolygon( + const ::com::sun::star::uno::Reference< + ::com::sun::star::rendering::XPolyPolygon2D >& xPolyPolygon, + const ::com::sun::star::rendering::ViewState& viewState, + const ::com::sun::star::rendering::RenderState& renderState ); + + ImageCachedPrimitiveSharedPtr strokePolyPolygon( + const ::com::sun::star::uno::Reference< + ::com::sun::star::rendering::XPolyPolygon2D >& xPolyPolygon, + const ::com::sun::star::rendering::ViewState& viewState, + const ::com::sun::star::rendering::RenderState& renderState, + const ::com::sun::star::rendering::StrokeAttributes& strokeAttributes ); + ImageCachedPrimitiveSharedPtr strokeTexturedPolyPolygon( + const ::com::sun::star::uno::Reference< + ::com::sun::star::rendering::XPolyPolygon2D >& xPolyPolygon, + const ::com::sun::star::rendering::ViewState& viewState, + const ::com::sun::star::rendering::RenderState& renderState, + const ::com::sun::star::uno::Sequence< + ::com::sun::star::rendering::Texture >& textures, + const ::std::vector< ::boost::shared_ptr<Image> >& textureAnnotations, + const ::com::sun::star::rendering::StrokeAttributes& strokeAttributes ); + ImageCachedPrimitiveSharedPtr strokeTextureMappedPolyPolygon( + const ::com::sun::star::uno::Reference< + ::com::sun::star::rendering::XPolyPolygon2D >& xPolyPolygon, + const ::com::sun::star::rendering::ViewState& viewState, + const ::com::sun::star::rendering::RenderState& renderState, + const ::com::sun::star::uno::Sequence< + ::com::sun::star::rendering::Texture >& textures, + const ::std::vector< ::boost::shared_ptr<Image> >& textureAnnotations, + const ::com::sun::star::uno::Reference< + ::com::sun::star::geometry::XMapping2D >& xMapping, + const ::com::sun::star::rendering::StrokeAttributes& strokeAttributes ); + ImageCachedPrimitiveSharedPtr fillPolyPolygon( + const ::com::sun::star::uno::Reference< + ::com::sun::star::rendering::XPolyPolygon2D >& xPolyPolygon, + const ::com::sun::star::rendering::ViewState& viewState, + const ::com::sun::star::rendering::RenderState& renderState ); + ImageCachedPrimitiveSharedPtr fillTexturedPolyPolygon( + const ::com::sun::star::uno::Reference< + ::com::sun::star::rendering::XPolyPolygon2D >& xPolyPolygon, + const ::com::sun::star::rendering::ViewState& viewState, + const ::com::sun::star::rendering::RenderState& renderState, + const ::com::sun::star::uno::Sequence< + ::com::sun::star::rendering::Texture >& textures, + const ::std::vector< ::boost::shared_ptr<Image> >& textureAnnotations ); + ImageCachedPrimitiveSharedPtr fillTextureMappedPolyPolygon( + const ::com::sun::star::uno::Reference< + ::com::sun::star::rendering::XPolyPolygon2D >& xPolyPolygon, + const ::com::sun::star::rendering::ViewState& viewState, + const ::com::sun::star::rendering::RenderState& renderState, + const ::com::sun::star::uno::Sequence< + ::com::sun::star::rendering::Texture >& textures, + const ::std::vector< ::boost::shared_ptr<Image> >& textureAnnotations, + const ::com::sun::star::uno::Reference< + ::com::sun::star::geometry::XMapping2D >& xMapping ); + + ImageCachedPrimitiveSharedPtr drawBitmap( + const ::com::sun::star::uno::Reference< + ::com::sun::star::rendering::XBitmap >& xBitmap, + const ::com::sun::star::rendering::ViewState& viewState, + const ::com::sun::star::rendering::RenderState& renderState ); + ImageCachedPrimitiveSharedPtr drawBitmap( + const ::boost::shared_ptr<Image>& rImage, + const ::com::sun::star::rendering::ViewState& viewState, + const ::com::sun::star::rendering::RenderState& renderState ); + + ImageCachedPrimitiveSharedPtr drawBitmapModulated( + const ::com::sun::star::uno::Reference< + ::com::sun::star::rendering::XBitmap >& xBitmap, + const ::com::sun::star::rendering::ViewState& viewState, + const ::com::sun::star::rendering::RenderState& renderState ); + ImageCachedPrimitiveSharedPtr drawBitmapModulated( + const ::boost::shared_ptr<Image>& rImage, + const ::com::sun::star::rendering::ViewState& viewState, + const ::com::sun::star::rendering::RenderState& renderState ); + + private: + void drawLinePolyPolygon( const ::basegfx::B2DPolyPolygon& rPoly, + double fStrokeWidth, + const ::com::sun::star::rendering::ViewState& viewState, + const ::com::sun::star::rendering::RenderState& renderState ); + + ImageCachedPrimitiveSharedPtr implDrawBitmap( + const Image& rBitmap, + const ::com::sun::star::rendering::ViewState& viewState, + const ::com::sun::star::rendering::RenderState& renderState ); + + ImageCachedPrimitiveSharedPtr fillTexturedPolyPolygon( + const Image& rTexture, + const ::basegfx::B2DPolyPolygon& rPolyPolygon, + const ::basegfx::B2DHomMatrix& rOverallTransform, + const ::basegfx::B2DHomMatrix& rViewTransform, + const ::com::sun::star::rendering::Texture& texture ); + + void fillGradient( const ParametricPolyPolygon::Values& rValues, + const ::com::sun::star::uno::Sequence< double >& rColor1, + const ::com::sun::star::uno::Sequence< double >& rColor2, + const ::basegfx::B2DPolyPolygon& rPolyPolygon, + const ::basegfx::B2DHomMatrix& rOverallTransform, + const ::com::sun::star::rendering::Texture& texture ); + bool fromVCLBitmap( ::BitmapEx& rBmpEx ); + + template<class pixel_format> + void drawLinePolyPolygonImpl( const ::basegfx::B2DPolyPolygon& rPoly, + double fStrokeWidth, + const ::com::sun::star::rendering::ViewState& viewState, + const ::com::sun::star::rendering::RenderState& renderState ); + + template<class pixel_format,class span_gen_type> + ImageCachedPrimitiveSharedPtr fillTexturedPolyPolygonImpl( + const Image& rTexture, + const ::basegfx::B2DPolyPolygon& rPolyPolygon, + const ::basegfx::B2DHomMatrix& rOverallTransform, + const ::basegfx::B2DHomMatrix& rViewTransform, + const ::com::sun::star::rendering::Texture& texture ); + + template<class pixel_format> + void fillGradientImpl( const ParametricPolyPolygon::Values& rValues, + const ::com::sun::star::uno::Sequence< double >& rUnoColor1, + const ::com::sun::star::uno::Sequence< double >& rUnoColor2, + const ::basegfx::B2DPolyPolygon& rPolyPolygon, + const ::basegfx::B2DHomMatrix& rOverallTransform, + const ::com::sun::star::rendering::Texture& texture ); + + template<class pixel_format> + ImageCachedPrimitiveSharedPtr fillPolyPolygonImpl( + const ::basegfx::B2DPolyPolygon& rPolyPolygon, + const ::com::sun::star::rendering::ViewState& viewState, + const ::com::sun::star::rendering::RenderState& renderState ); + + template<class pixel_format> void clearImpl( sal_uInt8 a, + sal_uInt8 r, + sal_uInt8 g, + sal_uInt8 b ); + + /** Image description + */ + Description maDesc; + + /** the graphics buffer is a simple array + where each element points to the start + of a scanline in consecutive order. + */ + agg::rendering_buffer maRenderingBuffer; + + /// Whether maRenderingBuffer is owned by the client of this object + bool mbBufferHasUserOwnership; + +#if defined(PROFILER) + + enum constant + { + TIMER_FILLTEXTUREDPOLYPOLYGON, + TIMER_FILLB2DPOLYPOLYGON, + TIMER_DRAWPOLYPOLYGON, + TIMER_FILLPOLYPOLYGON, + TIMER_DRAWBITMAP, + TIMER_MAX + }; + + double maElapsedTime[TIMER_MAX]; + + struct ScopeTimer + { + ScopeTimer( constant aConstant, Image *pImage ) : + maConstant(aConstant),mpImage(pImage) + {} + + ~ScopeTimer() + { + mpImage->maElapsedTime[maConstant] += maTimer.getElapsedTime(); + } + + constant maConstant; + Image* mpImage; + ::canvas::tools::ElapsedTime maTimer; + }; + +#endif + }; + + typedef ::boost::shared_ptr< Image > ImageSharedPtr; + +} + +#endif /* INCLUDED_CANVAS_IMAGE_HXX */ diff --git a/canvas/source/tools/image_sysprereq.h b/canvas/source/tools/image_sysprereq.h new file mode 100644 index 000000000000..ad81259c75c7 --- /dev/null +++ b/canvas/source/tools/image_sysprereq.h @@ -0,0 +1,102 @@ +/************************************************************************* + * + * 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. + * + ************************************************************************/ + +#if defined __GNUC__ +#pragma GCC system_header +#elif defined __SUNPRO_CC +#pragma disable_warn +#elif defined _MSC_VER +#pragma warning(push, 1) +#endif + +#include <agg2/agg_rendering_buffer.h> + +#ifdef CANVAS_IMAGE_CXX + +////////////////////////////////////////////////////////////////////////////////// +// includes from antigrain [i would love to write this stuff by myself] +////////////////////////////////////////////////////////////////////////////////// + +#include <agg2/agg_rendering_buffer.h> +#include <agg2/agg_pixfmt_rgb.h> +#include <agg2/agg_pixfmt_rgba.h> +#include <agg2/agg_renderer_base.h> +#include <agg2/agg_color_rgba.h> +#include <agg2/agg_rasterizer_outline_aa.h> +#include <agg2/agg_rasterizer_scanline_aa.h> +#include <agg2/agg_scanline_p.h> +#include <agg2/agg_scanline_u.h> +#include <agg2/agg_renderer_scanline.h> +#include <agg2/agg_renderer_outline_aa.h> +#include <agg2/agg_renderer_primitives.h> +#include <agg2/agg_path_storage.h> +#if AGG_VERSION == 2300 +#include <agg2/agg_span_pattern.h> +#endif +#include <agg2/agg_span_pattern_rgba.h> +#if AGG_VERSION >= 2400 +#include <agg2/agg_span_image_filter_rgb.h> +#include <agg2/agg_span_image_filter_rgba.h> +#else +#include <agg2/agg_span_pattern_resample_rgb.h> +#include <agg2/agg_span_pattern_resample_rgba.h> +#endif +#include <agg2/agg_span_interpolator_linear.h> +#include <agg2/agg_span_gradient.h> +#if AGG_VERSION == 2300 +#include <agg2/agg_span_image_resample_rgb.h> +#include <agg2/agg_span_image_resample_rgba.h> +#endif +#if AGG_VERSION >= 2400 +#include <agg2/agg_span_allocator.h> +#endif +#include <agg2/agg_image_filters.h> +#if AGG_VERSION >= 2400 +#include <agg2/agg_image_accessors.h> +#endif +#include <agg2/agg_dda_line.h> +#include <agg2/agg_scanline_storage_aa.h> +#include <agg2/agg_scanline_storage_bin.h> +#include <agg2/agg_scanline_bin.h> +#include <agg2/agg_path_storage_integer.h> +#include <agg2/agg_conv_contour.h> +#include <agg2/agg_conv_curve.h> +#include <agg2/agg_conv_stroke.h> +#include <agg2/agg_conv_transform.h> +#include <agg2/agg_trans_affine.h> +#include <agg2/agg_font_cache_manager.h> +#include <agg2/agg_bitset_iterator.h> +#include <agg2/agg_path_storage.h> + +#endif // CANVAS_IMAGE_CXX + +#if defined __SUNPRO_CC +#pragma enable_warn +#elif defined _MSC_VER +#pragma warning(pop) +#endif + diff --git a/canvas/source/tools/imagecachedprimitive.hxx b/canvas/source/tools/imagecachedprimitive.hxx new file mode 100644 index 000000000000..dfcf2bc6a831 --- /dev/null +++ b/canvas/source/tools/imagecachedprimitive.hxx @@ -0,0 +1,57 @@ +/************************************************************************* + * + * 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 */ diff --git a/canvas/source/tools/makefile.mk b/canvas/source/tools/makefile.mk new file mode 100644 index 000000000000..9d9e5ba002bf --- /dev/null +++ b/canvas/source/tools/makefile.mk @@ -0,0 +1,101 @@ +#************************************************************************* +# +# 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) + +.IF "$(ENABLE_AGG)"=="YES" + SLOFILES += $(SLO)$/bitmap.obj \ + $(SLO)$/image.obj + + .IF "$(AGG_VERSION)"!="" + CDEFS += -DAGG_VERSION=$(AGG_VERSION) + .ENDIF + SHL1STDLIBS += $(AGGLIB) +.ENDIF + +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..5035c0e81916 --- /dev/null +++ b/canvas/source/tools/page.cxx @@ -0,0 +1,152 @@ +/************************************************************************* + * + * 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; + } +} diff --git a/canvas/source/tools/page.hxx b/canvas/source/tools/page.hxx new file mode 100644 index 000000000000..51b770ad4ade --- /dev/null +++ b/canvas/source/tools/page.hxx @@ -0,0 +1,157 @@ +/************************************************************************* + * + * 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 diff --git a/canvas/source/tools/pagemanager.cxx b/canvas/source/tools/pagemanager.cxx new file mode 100644 index 000000000000..b867b432857c --- /dev/null +++ b/canvas/source/tools/pagemanager.cxx @@ -0,0 +1,216 @@ +/************************************************************************* + * + * 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)); + 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)->isNaked())) + break; + ++candidate; + } + + const ::basegfx::B2ISize& rSize((*candidate)->getSize()); + sal_uInt32 nMaxArea(rSize.getX()*rSize.getY()); + + FragmentContainer_t::const_iterator it(candidate); + while(it != aEnd) + { + if(!((*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); + } + } + + ////////////////////////////////////////////////////////////////////////////////// + // 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; + } +} diff --git a/canvas/source/tools/pagemanager.hxx b/canvas/source/tools/pagemanager.hxx new file mode 100644 index 000000000000..bf20b1d61e43 --- /dev/null +++ b/canvas/source/tools/pagemanager.hxx @@ -0,0 +1,94 @@ +/************************************************************************* + * + * 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 diff --git a/canvas/source/tools/parametricpolypolygon.cxx b/canvas/source/tools/parametricpolypolygon.cxx new file mode 100644 index 000000000000..368f04a572e3 --- /dev/null +++ b/canvas/source/tools/parametricpolypolygon.cxx @@ -0,0 +1,290 @@ +/************************************************************************* + * + * 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; + } + +} diff --git a/canvas/source/tools/prioritybooster.cxx b/canvas/source/tools/prioritybooster.cxx new file mode 100644 index 000000000000..984003ebc984 --- /dev/null +++ b/canvas/source/tools/prioritybooster.cxx @@ -0,0 +1,83 @@ +/************************************************************************* + * + * 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 diff --git a/canvas/source/tools/propertysethelper.cxx b/canvas/source/tools/propertysethelper.cxx new file mode 100644 index 000000000000..c95eae045ee5 --- /dev/null +++ b/canvas/source/tools/propertysethelper.cxx @@ -0,0 +1,187 @@ +/************************************************************************* + * + * 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 + } +} diff --git a/canvas/source/tools/spriteredrawmanager.cxx b/canvas/source/tools/spriteredrawmanager.cxx new file mode 100644 index 000000000000..7027a9d263df --- /dev/null +++ b/canvas/source/tools/spriteredrawmanager.cxx @@ -0,0 +1,520 @@ +/************************************************************************* + * + * 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 ) ); + } + +} diff --git a/canvas/source/tools/surface.cxx b/canvas/source/tools/surface.cxx new file mode 100644 index 000000000000..aaef28ba489f --- /dev/null +++ b/canvas/source/tools/surface.cxx @@ -0,0 +1,496 @@ +/************************************************************************* + * + * 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 + ////////////////////////////////////////////////////////////////////////////////// +} + diff --git a/canvas/source/tools/surface.hxx b/canvas/source/tools/surface.hxx new file mode 100644 index 000000000000..9acda579803a --- /dev/null +++ b/canvas/source/tools/surface.hxx @@ -0,0 +1,162 @@ +/************************************************************************* + * + * 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 diff --git a/canvas/source/tools/surfaceproxy.cxx b/canvas/source/tools/surfaceproxy.cxx new file mode 100644 index 000000000000..f4c31d4a2dc6 --- /dev/null +++ b/canvas/source/tools/surfaceproxy.cxx @@ -0,0 +1,182 @@ +/************************************************************************* + * + * 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; + } +} diff --git a/canvas/source/tools/surfaceproxy.hxx b/canvas/source/tools/surfaceproxy.hxx new file mode 100644 index 000000000000..7e42096c541b --- /dev/null +++ b/canvas/source/tools/surfaceproxy.hxx @@ -0,0 +1,134 @@ +/************************************************************************* + * + * 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 diff --git a/canvas/source/tools/surfaceproxymanager.cxx b/canvas/source/tools/surfaceproxymanager.cxx new file mode 100644 index 000000000000..dcc2ff574b57 --- /dev/null +++ b/canvas/source/tools/surfaceproxymanager.cxx @@ -0,0 +1,86 @@ +/************************************************************************* + * + * 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)); + } +} diff --git a/canvas/source/tools/surfacerect.hxx b/canvas/source/tools/surfacerect.hxx new file mode 100644 index 000000000000..cbeaa5e144d7 --- /dev/null +++ b/canvas/source/tools/surfacerect.hxx @@ -0,0 +1,135 @@ +/************************************************************************* + * + * 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 diff --git a/canvas/source/tools/verifyinput.cxx b/canvas/source/tools/verifyinput.cxx new file mode 100644 index 000000000000..42ab1c7aee19 --- /dev/null +++ b/canvas/source/tools/verifyinput.cxx @@ -0,0 +1,926 @@ +/************************************************************************* + * + * 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 |