/************************************************************************* * * OpenOffice.org - a multi-platform office productivity suite * * $RCSfile: bitmapdevice.cxx,v $ * * $Revision: 1.11 $ * * last change: $Author: thb $ $Date: 2006-06-08 16:39:02 $ * * The Contents of this file are made available subject to * the terms of GNU Lesser General Public License Version 2.1. * * * GNU Lesser General Public License Version 2.1 * ============================================= * Copyright 2005 by Sun Microsystems, Inc. * 901 San Antonio Road, Palo Alto, CA 94303, USA * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License version 2.1, as published by the Free Software Foundation. * * This library 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 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * ************************************************************************/ #include "basebmp/bitmapdevice.hxx" #include "basebmp/packedpixeliterator.hxx" #include "basebmp/pixeliterator.hxx" #include "basebmp/paletteimageaccessor.hxx" #include "basebmp/color.hxx" #include "basebmp/accessor.hxx" #include "basebmp/accessoradapters.hxx" #include "basebmp/scanlineformats.hxx" #include "basebmp/linerenderer.hxx" #include "basebmp/compositeiterator.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace basebmp { namespace { // Common accessor and iterator types //------------------------------------------------------------------------ typedef PaletteImageAccessor PaletteAccessor; typedef PackedPixelIterator< sal_uInt8, 1, true > MaskIterator; typedef StandardAccessor< sal_uInt8 > MaskAccessor; typedef PixelIterator< sal_uInt8 > AlphaMaskIterator; typedef vigra::AccessorTraits< sal_uInt8 >::default_accessor AlphaMaskAccessor; typedef PixelIterator ThirtyTwoBitPixelIterator; typedef vigra::AccessorTraits::default_accessor ThirtyTwoBitAccessor; // metafunctions to retrieve correct POD from/to basebmp::Color //------------------------------------------------------------------------ /// type-safe conversion between color and pod template< typename DataType > struct GreyScaleFromColor { typedef DataType value_type; DataType operator()( Color c ) { return c.getGrayscale(); } }; /// type-safe conversion between pod and color template< typename DataType > struct ColorFromGreyScale { typedef DataType value_type; Color operator()( DataType c ) { return Color(c,c,c); } }; /// Get converter from color to given data type template< typename DataType > struct fromColorConverter; template<> struct fromColorConverter< sal_uInt8 > { typedef GreyScaleFromColor type; }; template<> struct fromColorConverter< Color > { typedef std::identity type; }; /// Get converter from given data type to Color template< typename DataType > struct toColorConverter; template<> struct toColorConverter< sal_uInt8 > { typedef ColorFromGreyScale type; }; template<> struct toColorConverter< Color > { typedef std::identity type; }; // Polygon scanline conversion //------------------------------------------------------------------------ template< class DestIterator, class DestAccessor > class Renderer : public basegfx::B2DPolyPolygonRasterConverter { private: basegfx::B2IRange bounds_; typename DestAccessor::value_type fillColor_; typename DestAccessor::value_type clearColor_; DestIterator begin_; DestAccessor accessor_; public: Renderer(const basegfx::B2DPolyPolygon& rPolyPolyRaster, typename DestAccessor::value_type fillColor, const basegfx::B2IRange& bounds, DestIterator begin, DestIterator end, DestAccessor accessor ) : B2DPolyPolygonRasterConverter(rPolyPolyRaster, basegfx::B2DRange(bounds) ), bounds_(bounds), fillColor_( fillColor ), begin_( begin ), accessor_( accessor ) { } virtual void span(const double& rfXLeft, const double& rfXRight, sal_Int32 nY, bool bOn ) { if( !bOn || nY < bounds_.getMinY() || nY >= bounds_.getMaxY() || rfXLeft >= bounds_.getMaxX() || rfXRight < bounds_.getMinX() ) { return; } // clip span to bitmap bounds const sal_Int32 nStartX( std::max( bounds_.getMinX(), std::min( bounds_.getMaxX()-1, basegfx::fround( rfXLeft )))); const sal_Int32 nEndX ( std::max( bounds_.getMinX(), std::min( bounds_.getMaxX(), basegfx::fround( rfXRight )))); DestIterator currIter( begin_ + vigra::Diff2D(0,nY) ); typename DestIterator::row_iterator rowIter( currIter.rowIterator() + nStartX); typename DestIterator::row_iterator rowEnd( currIter.rowIterator() + nEndX ); // TODO(P2): Provide specialized span fill methods on the // iterator/accessor while( rowIter != rowEnd ) accessor_.set(fillColor_, rowIter++); } }; template< class DestIterator, class DestAccessor > std::auto_ptr< Renderer< DestIterator, DestAccessor > > makeRenderer( const basegfx::B2DPolyPolygon& rPolyPolyRaster, typename DestAccessor::value_type fillColor, const basegfx::B2IRange& outRange, vigra::triple dest ) { basegfx::B2IRange aBmpRange(0,0, dest.second.x - dest.first.x, dest.second.y - dest.first.y ); aBmpRange.intersect( outRange ); return std::auto_ptr< Renderer< DestIterator, DestAccessor > >( new Renderer< DestIterator, DestAccessor >(rPolyPolyRaster, fillColor, aBmpRange, dest.first, dest.second, dest.third)); } // Actual BitmapDevice implementation (templatized by accessor and iterator) //-------------------------------------------------------------------------- template< class DestIterator, class DestAccessor > class BitmapRenderer : public BitmapDevice { public: typedef BitmapRenderer MaskBitmap; typedef BitmapRenderer AlphaMaskBitmap; typedef typename colorLookup::type ColorLookupFunctor; typedef typename fromColorConverter< typename DestAccessor::value_type>::type FromColorFunctor; typedef typename toColorConverter< typename DestAccessor::value_type>::type ToColorFunctor; typedef typename rawAccessor::type RawAccessor; typedef typename xorAccessor::type XorAccessor; typedef typename xorAccessor::type RawXorAccessor; typedef typename maskedAccessor::type MaskedAccessor; typedef typename maskedAccessor::type RawMaskedAccessor; typedef typename maskedAccessor::type MaskedXorAccessor; typedef typename maskedAccessor::type RawMaskedXorAccessor; typedef DestIterator dest_iterator; typedef DestAccessor dest_accessor; typedef CompositeIterator2D< DestIterator, MaskIterator > composite_iterator_type; DestIterator maBegin; DestIterator maEnd; ColorLookupFunctor maColorLookup; FromColorFunctor maFromColorConverter; ToColorFunctor maToColorConverter; DestAccessor maAccessor; RawAccessor maRawAccessor; XorAccessor maXorAccessor; RawXorAccessor maRawXorAccessor; MaskedAccessor maMaskedAccessor; MaskedXorAccessor maMaskedXorAccessor; RawMaskedAccessor maRawMaskedAccessor; RawMaskedXorAccessor maRawMaskedXorAccessor; int mnWidth; int mnHeight; BitmapRenderer( const basegfx::B2IVector& rSize, bool bTopDown, sal_Int32 nScanlineFormat, sal_Int32 nScanlineStride, DestIterator begin, DestIterator end, DestAccessor accessor, const RawMemorySharedArray& rMem, const PaletteMemorySharedVector& rPalette ) : BitmapDevice( rSize, bTopDown, nScanlineFormat, nScanlineStride, rMem, rPalette ), maBegin( begin ), maEnd( end ), maColorLookup(), maFromColorConverter(), maToColorConverter(), maAccessor( accessor ), maRawAccessor( accessor ), maXorAccessor( accessor ), maRawXorAccessor( maRawAccessor ), maMaskedAccessor( accessor ), maMaskedXorAccessor( maXorAccessor ), maRawMaskedAccessor( maRawAccessor ), maRawMaskedXorAccessor( maRawXorAccessor ), mnWidth( maEnd.x - maBegin.x ), mnHeight( maEnd.y - maBegin.y ) {} private: boost::shared_ptr getCompatibleBitmap( const BitmapDeviceSharedPtr& bmp ) const { return boost::dynamic_pointer_cast< BitmapRenderer >( bmp ); } virtual bool isCompatibleBitmap( const BitmapDeviceSharedPtr& bmp ) const { // TODO(P1): dynamic_cast usually called twice for // compatible formats return getCompatibleBitmap(bmp).get() != NULL; } boost::shared_ptr getCompatibleMask( const BitmapDeviceSharedPtr& bmp ) const { return boost::dynamic_pointer_cast( bmp ); } virtual bool isCompatibleClipMask( const BitmapDeviceSharedPtr& bmp ) const { // TODO(P1): dynamic_cast usually called twice for // compatible formats return getCompatibleMask( bmp ).get() != NULL; } boost::shared_ptr getCompatibleAlphaMask( const BitmapDeviceSharedPtr& bmp ) const { return boost::dynamic_pointer_cast( bmp ); } virtual bool isCompatibleAlphaMask( const BitmapDeviceSharedPtr& bmp ) const { // TODO(P1): dynamic_cast usually called twice for // compatible formats return getCompatibleAlphaMask( bmp ).get() != NULL; } virtual void clear_i( Color fillColor ) { const typename dest_iterator::value_type colorIndex( maColorLookup( maAccessor, maFromColorConverter( fillColor))); DestIterator currIter( maBegin ); const DestIterator endIter ( maBegin + vigra::Diff2D(0,mnHeight) ); while( currIter != endIter ) { typename DestIterator::row_iterator rowIter( currIter.rowIterator() ); const typename DestIterator::row_iterator rowEnd( rowIter + mnWidth ); // TODO(P2): Provide specialized span fill methods on the // iterator/accessor while( rowIter != rowEnd ) maRawAccessor.set(colorIndex, rowIter++); ++currIter.y; } } virtual void setPixel_i( const basegfx::B2IPoint& rPt, Color lineColor, DrawMode drawMode ) { const DestIterator pixel( maBegin + vigra::Diff2D(rPt.getX(), rPt.getY()) ); if( drawMode == DrawMode_XOR ) maXorAccessor.set( maFromColorConverter( lineColor), pixel ); else maAccessor.set( maFromColorConverter( lineColor), pixel ); } virtual void setPixel_i( const basegfx::B2IPoint& rPt, Color lineColor, DrawMode drawMode, const BitmapDeviceSharedPtr& rClip ) { boost::shared_ptr pMask( getCompatibleMask(rClip) ); OSL_ASSERT( pMask ); const vigra::Diff2D offset(rPt.getX(), rPt.getY()); const composite_iterator_type aIter( maBegin + offset, pMask->maBegin + offset ); if( drawMode == DrawMode_XOR ) maMaskedXorAccessor.set( maFromColorConverter( lineColor), aIter ); else maMaskedAccessor.set( maFromColorConverter( lineColor), aIter ); } virtual Color getPixel_i(const basegfx::B2IPoint& rPt ) { const DestIterator pixel( maBegin + vigra::Diff2D(rPt.getX(), rPt.getY()) ); return maToColorConverter(maAccessor(pixel)); } virtual sal_uInt32 getPixelData_i( const basegfx::B2IPoint& rPt ) { const DestIterator pixel( maBegin + vigra::Diff2D(rPt.getX(), rPt.getY()) ); // xxx TODO return 0; // return maRawAccessor(pixel); } template< typename Range, typename Col, typename RawAccessor > void implRenderLine2( const basegfx::B2DPoint& rPt1, const basegfx::B2DPoint& rPt2, Col col, const Range& range, const RawAccessor& rawAcc ) { renderLine( basegfx::fround(rPt1), basegfx::fround(rPt2), col, range.first, range.second, rawAcc ); } template< typename Range, typename Accessor, typename RawAccessor > void implRenderLine( const basegfx::B2DPoint& rPt1, const basegfx::B2DPoint& rPt2, Color col, const Range& range, const Accessor& acc, const RawAccessor& rawAcc ) { implRenderLine2( rPt1,rPt2, maColorLookup( acc, maFromColorConverter( col)), range, rawAcc ); } template< typename Range, typename RawAccessor, typename XorAccessor > void implDrawLine( const basegfx::B2DPoint& rPt1, const basegfx::B2DPoint& rPt2, Color col, const Range& range, const RawAccessor& rawAcc, const XorAccessor& xorAcc, DrawMode drawMode ) { if( drawMode == DrawMode_XOR ) implRenderLine( rPt1, rPt2, col, range, maAccessor, xorAcc ); else implRenderLine( rPt1, rPt2, col, range, maAccessor, rawAcc ); } virtual void drawLine_i(const basegfx::B2DPoint& rPt1, const basegfx::B2DPoint& rPt2, Color lineColor, DrawMode drawMode ) { implDrawLine(rPt1,rPt2,lineColor, std::make_pair(maBegin,maEnd), maRawAccessor,maRawXorAccessor,drawMode); } vigra::pair getMaskedRange( const BitmapDeviceSharedPtr& rClip ) const { boost::shared_ptr pMask( getCompatibleMask(rClip) ); OSL_ASSERT( pMask ); return std::make_pair( composite_iterator_type( maBegin, pMask->maBegin ), composite_iterator_type( maEnd, pMask->maEnd )); } virtual void drawLine_i(const basegfx::B2DPoint& rPt1, const basegfx::B2DPoint& rPt2, Color lineColor, DrawMode drawMode, const BitmapDeviceSharedPtr& rClip ) { implDrawLine(rPt1,rPt2,lineColor, getMaskedRange(rClip), maRawMaskedAccessor,maRawMaskedXorAccessor,drawMode); } template< typename Range, typename RawAccessor > void implDrawPolygon( const basegfx::B2DPolygon& rPoly, Color col, const Range& range, const RawAccessor& acc ) { basegfx::B2DPolygon aPoly( rPoly ); if( rPoly.areControlVectorsUsed() ) aPoly = basegfx::tools::adaptiveSubdivideByCount( rPoly ); const typename dest_iterator::value_type colorIndex( maColorLookup( maAccessor, maFromColorConverter( col))); const sal_uInt32 nVertices( aPoly.count() ); for( sal_uInt32 i=1; i 1 && aPoly.isClosed() ) implRenderLine2( aPoly.getB2DPoint(nVertices-1), aPoly.getB2DPoint(0), colorIndex, range, acc ); } virtual void drawPolygon_i(const basegfx::B2DPolygon& rPoly, Color lineColor, DrawMode drawMode ) { if( drawMode == DrawMode_XOR ) implDrawPolygon( rPoly, lineColor, std::make_pair(maBegin, maEnd), maRawXorAccessor ); else implDrawPolygon( rPoly, lineColor, std::make_pair(maBegin, maEnd), maRawAccessor ); } virtual void drawPolygon_i(const basegfx::B2DPolygon& rPoly, Color lineColor, DrawMode drawMode, const BitmapDeviceSharedPtr& rClip ) { if( drawMode == DrawMode_XOR ) implDrawPolygon( rPoly, lineColor, getMaskedRange(rClip), maRawMaskedXorAccessor ); else implDrawPolygon( rPoly, lineColor, getMaskedRange(rClip), maRawMaskedAccessor ); } template< typename Range, typename RawAccessor > void implFillPolyPolygon( const basegfx::B2DPolyPolygon& rPoly, Color col, const Range& range, const RawAccessor& acc, const basegfx::B2IRange& rBounds ) { basegfx::B2DPolyPolygon aPoly( rPoly ); if( rPoly.areControlVectorsUsed() ) aPoly = basegfx::tools::adaptiveSubdivideByCount( rPoly ); makeRenderer( aPoly, maColorLookup( maAccessor, maFromColorConverter( col)), rBounds, vigra::make_triple( range.first, range.second, acc) )->rasterConvert( basegfx::FillRule_NONZERO_WINDING_NUMBER ); } virtual void fillPolyPolygon_i(const basegfx::B2DPolyPolygon& rPoly, Color fillColor, DrawMode drawMode, const basegfx::B2IRange& rBounds ) { if( drawMode == DrawMode_XOR ) implFillPolyPolygon( rPoly, fillColor, std::make_pair(maBegin,maEnd), maRawXorAccessor, rBounds ); else implFillPolyPolygon( rPoly, fillColor, std::make_pair(maBegin,maEnd), maRawAccessor, rBounds ); } virtual void fillPolyPolygon_i(const basegfx::B2DPolyPolygon& rPoly, Color fillColor, DrawMode drawMode, const basegfx::B2IRange& rBounds, const BitmapDeviceSharedPtr& rClip ) { if( drawMode == DrawMode_XOR ) implFillPolyPolygon( rPoly, fillColor, getMaskedRange(rClip), maRawMaskedXorAccessor, rBounds ); else implFillPolyPolygon( rPoly, fillColor, getMaskedRange(rClip), maRawMaskedAccessor, rBounds ); } template< typename Range, typename RawAccessor > void implDrawBitmap(const BitmapDeviceSharedPtr& rSrcBitmap, const basegfx::B2IRange& rSrcRect, const basegfx::B2IRange& rDstRect, const Range& range, const RawAccessor& acc) { boost::shared_ptr pSrcBmp( getCompatibleBitmap(rSrcBitmap) ); OSL_ASSERT( pSrcBmp ); // since resizeImageNoInterpolation() internally copyies // to a temporary buffer, also works with *this == rSrcBitmap vigra::resizeImageNoInterpolation( vigra::make_triple( pSrcBmp->maBegin + vigra::Diff2D(rSrcRect.getMinX(), rSrcRect.getMinY()), pSrcBmp->maBegin + vigra::Diff2D(rSrcRect.getMaxX(), rSrcRect.getMaxY()), pSrcBmp->maRawAccessor), vigra::make_triple( range.first + vigra::Diff2D(rDstRect.getMinX(), rDstRect.getMinY()), range.first + vigra::Diff2D(rDstRect.getMaxX(), rDstRect.getMaxY()), acc)); } virtual void drawBitmap_i(const BitmapDeviceSharedPtr& rSrcBitmap, const basegfx::B2IRange& rSrcRect, const basegfx::B2IRange& rDstRect, DrawMode drawMode ) { if( drawMode == DrawMode_XOR ) implDrawBitmap(rSrcBitmap, rSrcRect, rDstRect, std::make_pair(maBegin,maEnd), maRawXorAccessor); else implDrawBitmap(rSrcBitmap, rSrcRect, rDstRect, std::make_pair(maBegin,maEnd), maRawAccessor); } virtual void drawBitmap_i(const BitmapDeviceSharedPtr& rSrcBitmap, const basegfx::B2IRange& rSrcRect, const basegfx::B2IRange& rDstRect, DrawMode drawMode, const BitmapDeviceSharedPtr& rClip ) { if( drawMode == DrawMode_XOR ) implDrawBitmap(rSrcBitmap, rSrcRect, rDstRect, getMaskedRange(rClip), maRawMaskedXorAccessor); else implDrawBitmap(rSrcBitmap, rSrcRect, rDstRect, getMaskedRange(rClip), maRawMaskedAccessor); } template< typename Range, typename Accessor > void implDrawMaskedColor(Color rSrcColor, const BitmapDeviceSharedPtr& rAlphaMask, const basegfx::B2IRange& rSrcRect, const basegfx::B2IPoint& rDstPoint, const Range& range, const Accessor& acc) { boost::shared_ptr pAlpha( getCompatibleAlphaMask(rAlphaMask) ); OSL_ASSERT( pAlpha ); vigra::copyImage( pAlpha->maBegin + vigra::Diff2D(rSrcRect.getMinX(), rSrcRect.getMinY()), pAlpha->maBegin + vigra::Diff2D(rSrcRect.getMaxX(), rSrcRect.getMaxY()), pAlpha->maAccessor, range.first + vigra::Diff2D(rDstPoint.getX(), rDstPoint.getY()), ConstantColorBlendAccessorAdapter< Accessor, typename DestAccessor::value_type>( acc, maFromColorConverter(rSrcColor)) ); } virtual void drawMaskedColor_i(Color aSrcColor, const BitmapDeviceSharedPtr& rAlphaMask, const basegfx::B2IRange& rSrcRect, const basegfx::B2IPoint& rDstPoint ) { implDrawMaskedColor(aSrcColor, rAlphaMask, rSrcRect, rDstPoint, std::make_pair(maBegin,maEnd), maAccessor); } virtual void drawMaskedColor_i(Color aSrcColor, const BitmapDeviceSharedPtr& rAlphaMask, const basegfx::B2IRange& rSrcRect, const basegfx::B2IPoint& rDstPoint, const BitmapDeviceSharedPtr& rClip ) { implDrawMaskedColor(aSrcColor, rAlphaMask, rSrcRect, rDstPoint, getMaskedRange(rClip), maMaskedAccessor); } // must work with *this == rSrcBitmap! virtual void drawMaskedBitmap_i(const BitmapDeviceSharedPtr& rSrcBitmap, const BitmapDeviceSharedPtr& rMask, const basegfx::B2IRange& rSrcRect, const basegfx::B2IRange& rDstRect, DrawMode drawMode ) { OSL_ENSURE( false, "Method not yet implemented!" ); } virtual void drawMaskedBitmap_i(const BitmapDeviceSharedPtr& rSrcBitmap, const BitmapDeviceSharedPtr& rMask, const basegfx::B2IRange& rSrcRect, const basegfx::B2IRange& rDstRect, DrawMode drawMode, const BitmapDeviceSharedPtr& rClip ) { OSL_ENSURE( false, "Method not yet implemented!" ); } }; } // namespace struct ImplBitmapDevice { /// Bitmap memory plus deleter RawMemorySharedArray mpMem; /// Palette memory plus deleter (might be NULL) PaletteMemorySharedVector mpPalette; basegfx::B2IRange maBounds; basegfx::B2DRange maFloatBounds; sal_Int32 mnScanlineFormat; sal_Int32 mnScanlineStride; }; BitmapDevice::BitmapDevice( const basegfx::B2IVector& rSize, bool bTopDown, sal_Int32 nScanlineFormat, sal_Int32 nScanlineStride, const RawMemorySharedArray& rMem, const PaletteMemorySharedVector& rPalette ) : mpImpl( new ImplBitmapDevice ) { mpImpl->mpMem = rMem; mpImpl->mpPalette = rPalette; mpImpl->maBounds = basegfx::B2IRange( 0,0,rSize.getX(),rSize.getY() ); mpImpl->maFloatBounds = basegfx::B2DRange( 0,0,rSize.getX(),rSize.getY() ); mpImpl->mnScanlineFormat = nScanlineFormat; mpImpl->mnScanlineStride = bTopDown ? nScanlineStride : -nScanlineStride; } BitmapDevice::~BitmapDevice() { // outline, because of internal ImplBitmapDevice } basegfx::B2IVector BitmapDevice::getSize() const { return basegfx::B2IVector( mpImpl->maBounds.getWidth(), mpImpl->maBounds.getHeight() ); } bool BitmapDevice::isTopDown() const { return mpImpl->mnScanlineStride >= 0; } sal_Int32 BitmapDevice::getScanlineFormat() const { return mpImpl->mnScanlineFormat; } sal_Int32 BitmapDevice::getScanlineStride() const { return mpImpl->mnScanlineStride; } RawMemorySharedArray BitmapDevice::getBuffer() const { return mpImpl->mpMem; } PaletteMemorySharedVector BitmapDevice::getPalette() const { return mpImpl->mpPalette; } const sal_Int32 BitmapDevice::getPaletteEntryCount() const { return mpImpl->mpPalette ? mpImpl->mpPalette->size() : 0; } void BitmapDevice::clear( Color fillColor ) { clear_i( fillColor ); } void BitmapDevice::setPixel( const basegfx::B2IPoint& rPt, Color lineColor, DrawMode drawMode ) { if( mpImpl->maBounds.isInside(rPt) ) setPixel_i(rPt,lineColor,drawMode); } void BitmapDevice::setPixel( const basegfx::B2IPoint& rPt, Color lineColor, DrawMode drawMode, const BitmapDeviceSharedPtr& rClip ) { if( !rClip ) { setPixel(rPt,lineColor,drawMode); return; } if( mpImpl->maBounds.isInside(rPt) ) { if( isCompatibleClipMask( rClip ) ) setPixel_i(rPt,lineColor,drawMode,rClip); else OSL_ENSURE( false, "Generic output not yet implemented!" ); } } Color BitmapDevice::getPixel( const basegfx::B2IPoint& rPt ) { if( mpImpl->maBounds.isInside(rPt) ) return getPixel_i(rPt); return Color(); } sal_uInt32 BitmapDevice::getPixelData( const basegfx::B2IPoint& rPt ) { if( mpImpl->maBounds.isInside(rPt) ) return getPixelData_i(rPt); return 0; } void BitmapDevice::drawLine( const basegfx::B2IPoint& rPt1, const basegfx::B2IPoint& rPt2, Color lineColor, DrawMode drawMode ) { basegfx::B2DPoint aPt1( rPt1 ); basegfx::B2DPoint aPt2( rPt2 ); if( basegfx::tools::liangBarskyClip2D(aPt1,aPt2,mpImpl->maFloatBounds) ) { drawLine_i( aPt1, aPt2, lineColor, drawMode ); } } void BitmapDevice::drawLine( const basegfx::B2IPoint& rPt1, const basegfx::B2IPoint& rPt2, Color lineColor, DrawMode drawMode, const BitmapDeviceSharedPtr& rClip ) { if( !rClip ) { drawLine(rPt1,rPt2,lineColor,drawMode); return; } basegfx::B2DPoint aPt1( rPt1 ); basegfx::B2DPoint aPt2( rPt2 ); if( basegfx::tools::liangBarskyClip2D(aPt1,aPt2,mpImpl->maFloatBounds) ) { if( isCompatibleClipMask( rClip ) ) drawLine_i( aPt1, aPt2, lineColor, drawMode, rClip ); else OSL_ENSURE( false, "Generic output not yet implemented!" ); } } void BitmapDevice::drawPolygon( const basegfx::B2DPolygon& rPoly, Color lineColor, DrawMode drawMode ) { basegfx::B2DPolyPolygon aPoly( basegfx::tools::clipPolygonOnRange( rPoly, mpImpl->maFloatBounds, true, true )); const sal_uInt32 numPolies( aPoly.count() ); for( sal_uInt32 i=0; imaFloatBounds, true, true )); const sal_uInt32 numPolies( aPoly.count() ); for( sal_uInt32 i=0; imaBounds ); } void BitmapDevice::fillPolyPolygon( const basegfx::B2DPolyPolygon& rPoly, Color fillColor, DrawMode drawMode, const BitmapDeviceSharedPtr& rClip ) { if( !rClip ) { fillPolyPolygon(rPoly,fillColor,drawMode); return; } if( isCompatibleClipMask( rClip ) ) fillPolyPolygon_i( rPoly, fillColor, drawMode, mpImpl->maBounds, rClip ); else OSL_ENSURE( false, "Generic output not yet implemented!" ); } namespace { void assertImagePoint( const basegfx::B2IPoint& rPt, const basegfx::B2IRange& rPermittedRange ) { OSL_ASSERT( rPermittedRange.isInside(rPt) ); } void assertImageRange( const basegfx::B2IRange& rRange, const basegfx::B2IRange& rPermittedRange ) { #if OSL_DEBUG_LEVEL > 0 basegfx::B2IRange aRange( rRange ); aRange.intersect( rPermittedRange ); OSL_ASSERT( aRange == rRange ); #endif } // TODO(Q3): Move canvas/canvastools.hxx clipBlit() down // to basegfx, and use here! bool clipAreaImpl( ::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(); return true; } // TODO(Q3): Move canvas/canvastools.hxx clipBlit() down // to basegfx, and use here! bool clipAreaImpl( ::basegfx::B2IRange& io_rDestArea, ::basegfx::B2IRange& io_rSourceArea, const ::basegfx::B2IRange& rDestBounds, const ::basegfx::B2IRange& rSourceBounds ) { // extract inherent scale const double nScaleX( io_rDestArea.getWidth() / (double)io_rSourceArea.getWidth() ); const double nScaleY( io_rDestArea.getHeight() / (double)io_rSourceArea.getHeight() ); // extract range origins const basegfx::B2IPoint aDestTopLeft( io_rDestArea.getMinimum() ); 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( basegfx::fround(aDestTopLeft.getX() + nScaleX*aUpperLeftOffset.getX()), basegfx::fround(aDestTopLeft.getY() + nScaleY*aUpperLeftOffset.getY()), basegfx::fround(aDestTopLeft.getX() + nScaleX*aLowerRightOffset.getX()), basegfx::fround(aDestTopLeft.getY() + nScaleY*aLowerRightOffset.getY()) ); // 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()-aDestTopLeft ); const ::basegfx::B2IVector aDestLowerRightOffset( aLocalDestArea.getMaximum()-aDestTopLeft ); io_rSourceArea = ::basegfx::B2IRange( basegfx::fround(aSourceTopLeft.getX() + aDestUpperLeftOffset.getX()/nScaleX), basegfx::fround(aSourceTopLeft.getY() + aDestUpperLeftOffset.getY()/nScaleY), basegfx::fround(aSourceTopLeft.getX() + aDestLowerRightOffset.getX()/nScaleX), basegfx::fround(aSourceTopLeft.getY() + aDestLowerRightOffset.getY()/nScaleY) ); io_rDestArea = aLocalDestArea; // final source area clip (chopping round-offs) io_rSourceArea.intersect( rSourceBounds ); if( io_rSourceArea.isEmpty() ) return false; return true; } } void BitmapDevice::drawBitmap( const BitmapDeviceSharedPtr& rSrcBitmap, const basegfx::B2IRange& rSrcRect, const basegfx::B2IRange& rDstRect, DrawMode drawMode ) { const basegfx::B2IVector& rSrcSize( rSrcBitmap->getSize() ); const basegfx::B2IRange aSrcBounds( 0,0,rSrcSize.getX(),rSrcSize.getY() ); basegfx::B2IRange aSrcRange( rSrcRect ); basegfx::B2IRange aDestRange( rDstRect ); if( clipAreaImpl( aDestRange, aSrcRange, mpImpl->maBounds, aSrcBounds )) { assertImageRange(aDestRange,mpImpl->maBounds); assertImageRange(aSrcRange,aSrcBounds); if( isCompatibleBitmap( rSrcBitmap ) ) drawBitmap_i( rSrcBitmap, aSrcRange, aDestRange, drawMode ); else OSL_ENSURE( false, "Generic output not yet implemented!" ); } } void BitmapDevice::drawBitmap( const BitmapDeviceSharedPtr& rSrcBitmap, const basegfx::B2IRange& rSrcRect, const basegfx::B2IRange& rDstRect, DrawMode drawMode, const BitmapDeviceSharedPtr& rClip ) { if( !rClip ) { drawBitmap(rSrcBitmap,rSrcRect,rDstRect,drawMode); return; } const basegfx::B2IVector& rSrcSize( rSrcBitmap->getSize() ); const basegfx::B2IRange aSrcBounds( 0,0,rSrcSize.getX(),rSrcSize.getY() ); basegfx::B2IRange aSrcRange( rSrcRect ); basegfx::B2IRange aDestRange( rDstRect ); if( clipAreaImpl( aDestRange, aSrcRange, mpImpl->maBounds, aSrcBounds )) { assertImageRange(aDestRange,mpImpl->maBounds); assertImageRange(aSrcRange,aSrcBounds); if( isCompatibleBitmap( rSrcBitmap ) && isCompatibleClipMask( rClip ) ) { drawBitmap_i( rSrcBitmap, aSrcRange, aDestRange, drawMode, rClip ); } else { OSL_ENSURE( false, "Generic output not yet implemented!" ); } } } void BitmapDevice::drawMaskedColor( Color rSrcColor, const BitmapDeviceSharedPtr& rAlphaMask, const basegfx::B2IRange& rSrcRect, const basegfx::B2IPoint& rDstPoint ) { const basegfx::B2IVector& rSrcSize( rAlphaMask->getSize() ); const basegfx::B2IRange aSrcBounds( 0,0,rSrcSize.getX(),rSrcSize.getY() ); basegfx::B2IRange aSrcRange( rSrcRect ); basegfx::B2IPoint aDestPoint( rDstPoint ); if( clipAreaImpl( aSrcRange, aDestPoint, aSrcBounds, mpImpl->maBounds )) { assertImagePoint(aDestPoint,mpImpl->maBounds); assertImageRange(aSrcRange,aSrcBounds); if( isCompatibleAlphaMask( rAlphaMask ) ) drawMaskedColor_i( rSrcColor, rAlphaMask, aSrcRange, aDestPoint ); else OSL_ENSURE( false, "Generic output not yet implemented!" ); } } void BitmapDevice::drawMaskedColor( Color aSrcColor, const BitmapDeviceSharedPtr& rAlphaMask, const basegfx::B2IRange& rSrcRect, const basegfx::B2IPoint& rDstPoint, const BitmapDeviceSharedPtr& rClip ) { if( !rClip ) { drawMaskedColor(aSrcColor,rAlphaMask,rSrcRect,rDstPoint); return; } const basegfx::B2IVector& rSrcSize( rAlphaMask->getSize() ); const basegfx::B2IRange aSrcBounds( 0,0,rSrcSize.getX(),rSrcSize.getY() ); basegfx::B2IRange aSrcRange( rSrcRect ); basegfx::B2IPoint aDestPoint( rDstPoint ); if( clipAreaImpl( aSrcRange, aDestPoint, aSrcBounds, mpImpl->maBounds )) { assertImagePoint(aDestPoint,mpImpl->maBounds); assertImageRange(aSrcRange,aSrcBounds); if( isCompatibleAlphaMask( rAlphaMask ) && isCompatibleClipMask( rClip ) ) { drawMaskedColor_i( aSrcColor, rAlphaMask, aSrcRange, aDestPoint, rClip ); } else { OSL_ENSURE( false, "Generic output not yet implemented!" ); } } } void BitmapDevice::drawMaskedBitmap( const BitmapDeviceSharedPtr& rSrcBitmap, const BitmapDeviceSharedPtr& rMask, const basegfx::B2IRange& rSrcRect, const basegfx::B2IRange& rDstRect, DrawMode drawMode ) { OSL_ASSERT( rMask->getSize() == rSrcBitmap->getSize() ); const basegfx::B2IVector& rSrcSize( rSrcBitmap->getSize() ); const basegfx::B2IRange aSrcBounds( 0,0,rSrcSize.getX(),rSrcSize.getY() ); basegfx::B2IRange aSrcRange( rSrcRect ); basegfx::B2IRange aDestRange( rDstRect ); if( clipAreaImpl( aDestRange, aSrcRange, mpImpl->maBounds, aSrcBounds )) { assertImageRange(aDestRange,mpImpl->maBounds); assertImageRange(aSrcRange,aSrcBounds); if( isCompatibleBitmap( rSrcBitmap ) && isCompatibleClipMask( rMask ) ) { drawMaskedBitmap_i( rSrcBitmap, rMask, aSrcRange, aDestRange, drawMode ); } else { OSL_ENSURE( false, "Generic output not yet implemented!" ); } } } void BitmapDevice::drawMaskedBitmap( const BitmapDeviceSharedPtr& rSrcBitmap, const BitmapDeviceSharedPtr& rMask, const basegfx::B2IRange& rSrcRect, const basegfx::B2IRange& rDstRect, DrawMode drawMode, const BitmapDeviceSharedPtr& rClip ) { if( !rClip ) { drawMaskedBitmap(rSrcBitmap,rMask,rSrcRect,rDstRect,drawMode); return; } OSL_ASSERT( rMask->getSize() == rSrcBitmap->getSize() ); const basegfx::B2IVector& rSrcSize( rSrcBitmap->getSize() ); const basegfx::B2IRange aSrcBounds( 0,0,rSrcSize.getX(),rSrcSize.getY() ); basegfx::B2IRange aSrcRange( rSrcRect ); basegfx::B2IRange aDestRange( rDstRect ); if( clipAreaImpl( aDestRange, aSrcRange, mpImpl->maBounds, aSrcBounds )) { assertImageRange(aDestRange,mpImpl->maBounds); assertImageRange(aSrcRange,aSrcBounds); if( isCompatibleBitmap( rSrcBitmap ) && isCompatibleClipMask( rMask ) && isCompatibleClipMask( rClip ) ) { drawMaskedBitmap_i( rSrcBitmap, rMask, aSrcRange, aDestRange, drawMode, rClip ); } else { OSL_ENSURE( false, "Generic output not yet implemented!" ); } } } //---------------------------------------------------------------------------------- typedef BitmapRenderer OneBitMsbMaskRenderer; typedef BitmapRenderer OneBitMsbPaletteRenderer; typedef BitmapRenderer EightBitGrayRenderer; typedef BitmapRenderer ThirtyTwoBitTrueColorRenderer; namespace { BitmapDeviceSharedPtr createBitmapDeviceImpl( const basegfx::B2IVector& rSize, bool bTopDown, sal_Int32 nScanlineFormat, boost::shared_array< sal_uInt8 > pMem, PaletteMemorySharedVector pPal ) { sal_Int32 nScanlineStride(0); // HACK: 1bpp and 32bpp only, currently if( nScanlineFormat == Format::ONE_BIT_MSB_PAL || nScanlineFormat == Format::ONE_BIT_MSB_GRAY ) nScanlineStride = (rSize.getX() + 7) >> 3; else if( nScanlineFormat == Format::EIGHT_BIT_GRAY ) nScanlineStride = rSize.getX(); else if( nScanlineFormat == Format::THIRTYTWO_BIT_TC_MASK ) nScanlineStride = 4*rSize.getX(); nScanlineStride *= bTopDown ? 1 : -1; const std::size_t nMemSize( (nScanlineStride < 0 ? -nScanlineStride : nScanlineStride)*rSize.getY() ); if( !pMem ) { pMem.reset( reinterpret_cast(rtl_allocateMemory( nMemSize )), &rtl_freeMemory ); rtl_zeroMemory(pMem.get(),nMemSize); } sal_uInt8* pFirstScanline = nScanlineStride < 0 ? pMem.get() + nMemSize : pMem.get(); switch( nScanlineFormat ) { case Format::ONE_BIT_MSB_GRAY: { return BitmapDeviceSharedPtr( new OneBitMsbMaskRenderer( rSize, bTopDown, nScanlineFormat, nScanlineStride, MaskIterator(pFirstScanline, nScanlineStride), MaskIterator(pFirstScanline, nScanlineStride) + vigra::Diff2D(rSize.getX(), rSize.getY()), MaskAccessor(), pMem, pPal )); } case Format::ONE_BIT_MSB_PAL: { if( !pPal ) { boost::shared_ptr< std::vector > pLocalPal( new std::vector(2) ); pLocalPal->at(0) = Color(0x00000000); pLocalPal->at(1) = Color(0xFFFFFFFF); pPal = pLocalPal; } return BitmapDeviceSharedPtr( new OneBitMsbPaletteRenderer( rSize, bTopDown, nScanlineFormat, nScanlineStride, MaskIterator(pFirstScanline, nScanlineStride), MaskIterator(pFirstScanline, nScanlineStride) + vigra::Diff2D(rSize.getX(), rSize.getY()), PaletteAccessor( &pPal->at(0), pPal->size() ), pMem, pPal )); } case Format::EIGHT_BIT_GRAY: { return BitmapDeviceSharedPtr( new EightBitGrayRenderer( rSize, bTopDown, nScanlineFormat, nScanlineStride, AlphaMaskIterator(pFirstScanline, nScanlineStride), AlphaMaskIterator(pFirstScanline, nScanlineStride) + vigra::Diff2D(rSize.getX(), rSize.getY()), AlphaMaskAccessor(), pMem, pPal )); } case Format::THIRTYTWO_BIT_TC_MASK: { return BitmapDeviceSharedPtr( new ThirtyTwoBitTrueColorRenderer( rSize, bTopDown, nScanlineFormat, nScanlineStride, ThirtyTwoBitPixelIterator(reinterpret_cast(pFirstScanline), nScanlineStride), ThirtyTwoBitPixelIterator(reinterpret_cast(pFirstScanline), nScanlineStride) + vigra::Diff2D(rSize.getX(), rSize.getY()), ThirtyTwoBitAccessor(), pMem, pPal )); } default: // TODO(F3): other formats not yet implemented return BitmapDeviceSharedPtr(); } } } // namespace BitmapDeviceSharedPtr createBitmapDevice( const basegfx::B2IVector& rSize, bool bTopDown, sal_Int32 nScanlineFormat ) { return createBitmapDeviceImpl( rSize, bTopDown, nScanlineFormat, boost::shared_array< sal_uInt8 >(), PaletteMemorySharedVector() ); } BitmapDeviceSharedPtr createBitmapDevice( const basegfx::B2IVector& rSize, bool bTopDown, sal_Int32 nScanlineFormat, const RawMemorySharedArray& rMem, const PaletteMemorySharedVector& rPalette ) { return createBitmapDeviceImpl( rSize, bTopDown, nScanlineFormat, rMem, rPalette ); } BitmapDeviceSharedPtr cloneBitmapDevice( const basegfx::B2IVector& rSize, const BitmapDeviceSharedPtr& rProto ) { return createBitmapDeviceImpl( rSize, rProto->isTopDown(), rProto->getScanlineFormat(), boost::shared_array< sal_uInt8 >(), rProto->getPalette() ); } } // namespace basebmp