/************************************************************************* * * OpenOffice.org - a multi-platform office productivity suite * * $RCSfile: image.cxx,v $ * * $Revision: 1.5 $ * * last change: $Author: hr $ $Date: 2006-06-20 02:17:33 $ * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "image.hxx" #define CANVAS_IMAGE_CXX #include "image_sysprereq.h" ////////////////////////////////////////////////////////////////////////////////// // platform-dependend includes [wrapped into their own namepsaces] ////////////////////////////////////////////////////////////////////////////////// #if defined(WNT) namespace win32 { #undef DECLARE_HANDLE #undef WB_LEFT #undef WB_RIGHT #undef APIENTRY #define WIN32_LEAN_AND_MEAN #define NOMINMAX #include } #else namespace unx { #include } #endif #include 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(255.0f*sequence[0]); Color.g = static_cast(255.0f*sequence[1]); Color.b = static_cast(255.0f*sequence[2]); if(sequence.getLength() > 3) Color.a = static_cast(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); if( viewState.Clip.is() ) { ::basegfx::B2DPolyPolygon aViewClip( tools::polyPolygonFromXPolyPolygon2D( viewState.Clip ) ); if(aViewClip.areControlPointsUsed()) aViewClip = ::basegfx::tools::adaptiveSubdivideByAngle(aViewClip); o_rViewClip.reset( new ::basegfx::B2DPolyPolygon( aViewClip ) ); } if( renderState.Clip.is() ) { ::basegfx::B2DPolyPolygon aRenderClip( tools::polyPolygonFromXPolyPolygon2D( 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 ) { // TODO(F2): review subdivision algo, maybe use hybrid // approach from mcseem also in basegfx if(aPolyPolygon.areControlPointsUsed()) aPolyPolygon = ::basegfx::tools::adaptiveSubdivideByAngle(aPolyPolygon); if( bIsFilledPolyPolygon ) { ::basegfx::B2DPolyPolygon clip(*pRenderClip); aPolyPolygon = ::basegfx::tools::removeAllIntersections(aPolyPolygon); aPolyPolygon = ::basegfx::tools::removeNeutralPolygons(aPolyPolygon, sal_True); clip = ::basegfx::tools::removeAllIntersections(clip); clip = ::basegfx::tools::removeNeutralPolygons(clip, sal_True); aPolyPolygon.append(clip); aPolyPolygon = ::basegfx::tools::removeAllIntersections(aPolyPolygon); aPolyPolygon = ::basegfx::tools::removeNeutralPolygons(aPolyPolygon, sal_False); } else { // TODO(F3): add AW's addition to clipPolyPolygonOnPolyPolygon // regarding open/close state aPolyPolygon = basegfx::tools::clipPolyPolygonOnPolyPolygon(aPolyPolygon, *pRenderClip, true); } } if( !aPolyPolygon.count() ) return; // transform result into view space aPolyPolygon.transform(rRenderTransform); // clip contour against viewclip if( pViewClip ) { // TODO(F2): review subdivision algo, maybe use hybrid // approach from mcseem also in basegfx if(aPolyPolygon.areControlPointsUsed()) aPolyPolygon = ::basegfx::tools::adaptiveSubdivideByAngle(aPolyPolygon); if( bIsFilledPolyPolygon ) { ::basegfx::B2DPolyPolygon clip(*pViewClip); aPolyPolygon = ::basegfx::tools::removeAllIntersections(aPolyPolygon); aPolyPolygon = ::basegfx::tools::removeNeutralPolygons(aPolyPolygon, sal_True); clip = ::basegfx::tools::removeAllIntersections(clip); clip = ::basegfx::tools::removeNeutralPolygons(clip, sal_True); aPolyPolygon.append(clip); aPolyPolygon = ::basegfx::tools::removeAllIntersections(aPolyPolygon); aPolyPolygon = ::basegfx::tools::removeNeutralPolygons(aPolyPolygon, sal_False); } else { // TODO(F3): add AW's addition to clipPolyPolygonOnPolyPolygon // regarding open/close state aPolyPolygon = basegfx::tools::clipPolyPolygonOnPolyPolygon(aPolyPolygon, *pViewClip, true); } } 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_AND_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_AND_THROW( pAlphaReadAccess.get() != NULL, "vclBitmapEx2Raw(): " "Unable to acquire read acces to alpha" ); ENSURE_AND_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; yGetScanlineFormat() ) { case BMP_FORMAT_8BIT_PAL: { Scanline pScan = pReadAccess->GetScanline( y ); Scanline pAScan = pAlphaReadAccess->GetScanline( y ); for( x=0; xGetPaletteColor( *pScan++ ); *pCurrOutput++ = aCol.GetBlue(); *pCurrOutput++ = aCol.GetGreen(); *pCurrOutput++ = aCol.GetRed(); // out notion of alpha is // different from the rest // of the world's *pCurrOutput++ = 255 - (BYTE)*pAScan++; } } break; case BMP_FORMAT_24BIT_TC_BGR: { Scanline pScan = pReadAccess->GetScanline( y ); Scanline pAScan = pAlphaReadAccess->GetScanline( y ); for( x=0; xGetScanline( y ); // using fallback for those // seldom formats for( x=0; xGetColor(y,x); *pCurrOutput++ = aCol.GetBlue(); *pCurrOutput++ = aCol.GetGreen(); *pCurrOutput++ = aCol.GetRed(); // out notion of alpha is // different from the rest // of the world's *pCurrOutput++ = 255 - (BYTE)*pAScan++; } } break; case BMP_FORMAT_1BIT_LSB_PAL: // FALLTHROUGH intended case BMP_FORMAT_4BIT_LSN_PAL: // FALLTHROUGH intended case BMP_FORMAT_8BIT_TC_MASK: // FALLTHROUGH intended case BMP_FORMAT_24BIT_TC_RGB: // FALLTHROUGH intended case BMP_FORMAT_24BIT_TC_MASK: // FALLTHROUGH intended case BMP_FORMAT_16BIT_TC_MSB_MASK: // FALLTHROUGH intended case BMP_FORMAT_32BIT_TC_ABGR: // FALLTHROUGH intended case BMP_FORMAT_32BIT_TC_ARGB: // FALLTHROUGH intended case BMP_FORMAT_32BIT_TC_BGRA: // FALLTHROUGH intended case BMP_FORMAT_32BIT_TC_RGBA: // FALLTHROUGH intended default: ENSURE_AND_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_AND_THROW( pMaskReadAccess.get() != NULL, "vclBitmapEx2Raw(): " "Unable to acquire read acces to mask" ); ENSURE_AND_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(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; yGetScanlineFormat() ) { case BMP_FORMAT_8BIT_PAL: { Scanline pScan = pReadAccess->GetScanline( y ); Scanline pMScan = pMaskReadAccess->GetScanline( y ); for( x=0, nCurrBit=nInitialBit; xGetPaletteColor( *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 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; xGetColor(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_AND_THROW( false, "vclBitmapEx2Raw(): " "Unexpected scanline format - has " "WinSalBitmap::AcquireBuffer() changed?" ); } } } } else { // *no* alpha mask ULONG nFormat = pReadAccess->GetScanlineFormat(); BYTE *pBuffer = reinterpret_cast(rBmpData.mpBitmapData); switch(nFormat) { case BMP_FORMAT_24BIT_TC_BGR: { sal_Int32 height = pReadAccess->Height(); for(sal_Int32 y=0; yGetScanline(y); sal_Int32 width = pReadAccess->Width(); for(sal_Int32 x=0; x RGB BYTE b(*pScanline++); BYTE g(*pScanline++); BYTE r(*pScanline++); *pBuffer++ = r; *pBuffer++ = g; *pBuffer++ = b; } } } break; case BMP_FORMAT_24BIT_TC_RGB: { sal_Int32 height = pReadAccess->Height(); for(sal_Int32 y=0; yGetScanline(y); sal_Int32 width = pReadAccess->Width(); for(sal_Int32 x=0; x RGB BYTE r(*pScanline++); BYTE g(*pScanline++); BYTE b(*pScanline++); *pBuffer++ = r; *pBuffer++ = g; *pBuffer++ = b; } } } break; case BMP_FORMAT_1BIT_MSB_PAL: case BMP_FORMAT_1BIT_LSB_PAL: case BMP_FORMAT_4BIT_MSN_PAL: case BMP_FORMAT_4BIT_LSN_PAL: case BMP_FORMAT_8BIT_PAL: { sal_Int32 height = pReadAccess->Height(); for(sal_Int32 y=0; yGetScanline(y); sal_Int32 width = pReadAccess->Width(); for(sal_Int32 x=0; xGetPaletteColor(*pScanline++)); *pBuffer++ = aCol.GetRed(); *pBuffer++ = aCol.GetGreen(); *pBuffer++ = aCol.GetBlue(); } } } break; } } } ////////////////////////////////////////////////////////////////////////////////// // color_generator_linear ////////////////////////////////////////////////////////////////////////////////// template 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(maColor1.r+(maColor2.r-maColor1.r)*w), static_cast(maColor1.g+(maColor2.g-maColor1.g)*w), static_cast(maColor1.b+(maColor2.b-maColor1.b)*w), static_cast(maColor1.a+(maColor2.a-maColor1.a)*w)); } unsigned int maSteps; const T maColor1; const T maColor2; }; ////////////////////////////////////////////////////////////////////////////////// // color_generator_axial ////////////////////////////////////////////////////////////////////////////////// template 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(maColor1.r+(maColor2.r-maColor1.r)*w), static_cast(maColor1.g+(maColor2.g-maColor1.g)*w), static_cast(maColor1.b+(maColor2.b-maColor1.b)*w), static_cast(maColor1.a+(maColor2.a-maColor1.a)*w)); } unsigned int maSteps; const T maColor1; const T maColor2; }; ////////////////////////////////////////////////////////////////////////////////// // color_generator_adaptor ////////////////////////////////////////////////////////////////////////////////// template 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 linear_generator; color_generator_axial 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 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 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 renderer_base; agg::renderer_base 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_type; typedef agg::rasterizer_outline_aa 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 curve(path); for(sal_uInt32 nPolygon=0; nPolygon(rPoly,fStrokeWidth,viewState,renderState); break; case FMT_A8R8G8B8: drawLinePolyPolygonImpl(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] ////////////////////////////////////////////////////////////////////////////////// template 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), sg(sa,src,inter,filter), pixf(dst), rb(pixf), ren(rb,sg) { ::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; typedef agg::renderer_base renderer_base; typedef agg::renderer_scanline_aa renderer_type; ::basegfx::B2DHomMatrix aTransform; interpolator_type inter; agg::image_filter_bilinear filter_kernel; agg::image_filter_lut filter; agg::span_allocator< typename span_gen_type::color_type > sa; span_gen_type sg; pixel_format pixf; renderer_base rb; mutable renderer_type ren; mutable agg::scanline_p8 sl; mutable agg::trans_affine tm; ImageSharedPtr pImage; }; ////////////////////////////////////////////////////////////////////////////////// // Image::fillTexturedPolyPolygon ////////////////////////////////////////////////////////////////////////////////// template 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(aTranslate.getX())); sal_Int32 dwDstY(static_cast(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 cachedPrimitive_t; cachedPrimitive_t *pPrimitive = new cachedPrimitive_t( rOverallTransform, rViewTransform, maRenderingBuffer, rTexture.maRenderingBuffer); agg::path_storage path; agg::conv_curve curve(path); for(sal_uInt32 nPolygon=0; nPolygonras.move_to_d(rPoint.getX(),rPoint.getY()); for(sal_uInt32 nVertex=1; nVertexras.line_to_d(rVertexPoint.getX(),rVertexPoint.getY()); } if(rPoly.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; 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; 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 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 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_generator_type; unsigned int dwNumSteps = static_cast(rBounds.getWidth()); color_generator_type colors(color1,color2,dwNumSteps); colors.set_linear(rValues.meType != ParametricPolyPolygon::GRADIENT_AXIAL); // color = f(x,y) gradient_polymorphic_wrapper gf_x; gradient_polymorphic_wrapper gf_radial; gradient_polymorphic_wrapper gf_rectangular; gf_rectangular.m_gradient.width = static_cast(rBounds.getWidth())<<4; gf_rectangular.m_gradient.height = static_cast(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 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 gradient_span_gen; gradient_span_gen span_gen(span_alloc, inter, *gf[rValues.meType], colors, 0, dwNumSteps); // 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] typedef agg::renderer_scanline_aa renderer_gradient; renderer_gradient r1(rb, span_gen); // instantiate the rasterizer and feed the incoming polypolygon. agg::rasterizer_scanline_aa<> ras; agg::path_storage path; agg::conv_curve curve(path); for(sal_uInt32 nPolygon=0; nPolygon& 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(rValues,rUnoColor1,rUnoColor2,rPolyPolygon,rOverallTransform,texture); break; case FMT_A8R8G8B8: fillGradientImpl(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(desc.pBuffer), desc.nWidth, desc.nHeight, nPitch); RawABGRBitmap aBmpData; aBmpData.mnWidth = aBmpSize.Width(); aBmpData.mnHeight = aBmpSize.Height(); aBmpData.mpBitmapData = static_cast(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(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 xTunnel( xBitmap, uno::UNO_QUERY ); if( xTunnel.is() ) { sal_Int64 nPtr = xTunnel->getSomething( vcl::unotools::getTunnelIdentifier( vcl::unotools::Id_BitmapEx ) ); if(nPtr) { BitmapEx& rBmpEx = *reinterpret_cast(nPtr); fromVCLBitmap(rBmpEx); return; } } // TODO(F2): Fallback to XIntegerBitmap interface for import OSL_ENSURE(false, "Image::Image(): fallback to XBitmap interface import NYI!" ); } ////////////////////////////////////////////////////////////////////////////////// // Image::~Image ////////////////////////////////////////////////////////////////////////////////// Image::~Image() { #if defined(PROFILER) double aAccumulatedTime(0.0); for(int i=0; i void Image::clearImpl( sal_uInt8 a, sal_uInt8 r, sal_uInt8 g, sal_uInt8 b ) { pixel_format pixf(maRenderingBuffer); agg::renderer_base 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(a,r,g,b); case FMT_A8R8G8B8: return clearImpl(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(rPolyPolygon,viewState,renderState); break; case FMT_A8R8G8B8: fillPolyPolygonImpl(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.setControlPointA( 0, ::basegfx::B2DPoint( aBezierSegment.C1x, aBezierSegment.C1y ) ); aBezierPoly.setControlPointB( 0, ::basegfx::B2DPoint( aBezierSegment.C2x, aBezierSegment.C2y ) ); aBezierPoly.append( ::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( tools::polyPolygonFromXPolyPolygon2D( 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( tools::polyPolygonFromXPolyPolygon2D( 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 >& /*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 >& /*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 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 aPoly(rPolyPolygon); setupPolyPolygon( aPoly, true, aFillColor, viewState, renderState ); if( !aPoly.count() ) return ImageCachedPrimitiveSharedPtr(); pixel_format pixf(maRenderingBuffer); agg::renderer_base renb(pixf); // Scanline renderer for solid filling. agg::renderer_scanline_aa_solid > ren(renb); // Rasterizer & scanline agg::rasterizer_scanline_aa<> ras; agg::scanline_p8 sl; agg::path_storage path; agg::conv_curve curve(path); for(sal_uInt32 nPolygon=0; nPolygon& xPolyPolygon, const rendering::ViewState& viewState, const rendering::RenderState& renderState ) { if( !xPolyPolygon.is() ) return ImageCachedPrimitiveSharedPtr(); ::basegfx::B2DPolyPolygon aPoly( tools::polyPolygonFromXPolyPolygon2D( xPolyPolygon ) ); switch(maDesc.eFormat) { case FMT_R8G8B8: return fillPolyPolygonImpl(aPoly,viewState,renderState); case FMT_A8R8G8B8: return fillPolyPolygonImpl(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 >& textureAnnotations ) { #if defined(PROFILER) ScopeTimer aTimer(TIMER_FILLTEXTUREDPOLYPOLYGON,this); #endif if( !xPolyPolygon.is() ) return ImageCachedPrimitiveSharedPtr(); ::basegfx::B2DPolyPolygon aPoly( tools::polyPolygonFromXPolyPolygon2D( 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(E1): Return value // TODO(F1): FillRule fillGradient( rValues, rValues.maColor1, rValues.maColor2, 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 >& /*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 ); } }