diff options
Diffstat (limited to 'canvas/source/tools/image.cxx')
-rw-r--r-- | canvas/source/tools/image.cxx | 2394 |
1 files changed, 2394 insertions, 0 deletions
diff --git a/canvas/source/tools/image.cxx b/canvas/source/tools/image.cxx new file mode 100644 index 000000000000..4b8a2a334873 --- /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 - (sal_uInt8)*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 - (sal_uInt8)*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 - (sal_uInt8)*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 + sal_uIntPtr nFormat = pReadAccess->GetScanlineFormat(); + sal_uInt8 *pBuffer = reinterpret_cast<sal_uInt8 *>(rBmpData.mpBitmapData); + + switch(nFormat) + { + case BMP_FORMAT_24BIT_TC_BGR: + + { + sal_Int32 height = pReadAccess->Height(); + for(sal_Int32 y=0; y<height; ++y) + { + sal_uInt8 *pScanline=pReadAccess->GetScanline(y); + sal_Int32 width = pReadAccess->Width(); + for(sal_Int32 x=0; x<width; ++x) + { + // BGR -> RGB + sal_uInt8 b(*pScanline++); + sal_uInt8 g(*pScanline++); + sal_uInt8 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) + { + sal_uInt8 *pScanline=pReadAccess->GetScanline(y); + sal_Int32 width = pReadAccess->Width(); + for(sal_Int32 x=0; x<width; ++x) + { + // RGB -> RGB + sal_uInt8 r(*pScanline++); + sal_uInt8 g(*pScanline++); + sal_uInt8 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) + { + sal_uInt8 *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) + { + sal_uInt8 r(*pSrc++); + sal_uInt8 g(*pSrc++); + sal_uInt8 b(*pSrc++); + sal_uInt8 Alpha(*pSrc++); + sal_uInt8 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) + { + sal_uInt8 r(*pSrc++); + sal_uInt8 g(*pSrc++); + sal_uInt8 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 ); +} + +} |