2449 lines
96 KiB
C++
2449 lines
96 KiB
C++
/*************************************************************************
|
|
*
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* Copyright 2008 by Sun Microsystems, Inc.
|
|
*
|
|
* OpenOffice.org - a multi-platform office productivity suite
|
|
*
|
|
* $RCSfile: image.cxx,v $
|
|
* $Revision: 1.14 $
|
|
*
|
|
* This file is part of OpenOffice.org.
|
|
*
|
|
* OpenOffice.org is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License version 3
|
|
* only, as published by the Free Software Foundation.
|
|
*
|
|
* OpenOffice.org is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License version 3 for more details
|
|
* (a copy is included in the LICENSE file that accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* version 3 along with OpenOffice.org. If not, see
|
|
* <http://www.openoffice.org/license.html>
|
|
* for a copy of the LGPLv3 License.
|
|
*
|
|
************************************************************************/
|
|
|
|
// MARKER(update_precomp.py): autogen include statement, do not remove
|
|
#include "precompiled_canvas.hxx"
|
|
|
|
#include <com/sun/star/lang/XUnoTunnel.hpp>
|
|
#include <com/sun/star/rendering/RepaintResult.hpp>
|
|
|
|
#include <canvas/canvastools.hxx>
|
|
#include <canvas/parametricpolypolygon.hxx>
|
|
#include <canvas/debug.hxx>
|
|
|
|
#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 "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(
|
|
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; y<nHeight; ++y )
|
|
{
|
|
switch( pReadAccess->GetScanlineFormat() )
|
|
{
|
|
case BMP_FORMAT_8BIT_PAL:
|
|
{
|
|
Scanline pScan = pReadAccess->GetScanline( y );
|
|
Scanline pAScan = pAlphaReadAccess->GetScanline( y );
|
|
|
|
for( x=0; x<nWidth; ++x )
|
|
{
|
|
aCol = pReadAccess->GetPaletteColor( *pScan++ );
|
|
|
|
*pCurrOutput++ = aCol.GetBlue();
|
|
*pCurrOutput++ = aCol.GetGreen();
|
|
*pCurrOutput++ = aCol.GetRed();
|
|
|
|
// out notion of alpha is
|
|
// different from the rest
|
|
// of the world's
|
|
*pCurrOutput++ = 255 - (BYTE)*pAScan++;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case BMP_FORMAT_24BIT_TC_BGR:
|
|
{
|
|
Scanline pScan = pReadAccess->GetScanline( y );
|
|
Scanline pAScan = pAlphaReadAccess->GetScanline( y );
|
|
|
|
for( x=0; x<nWidth; ++x )
|
|
{
|
|
// store as RGBA
|
|
*pCurrOutput++ = *pScan++;
|
|
*pCurrOutput++ = *pScan++;
|
|
*pCurrOutput++ = *pScan++;
|
|
|
|
// out notion of alpha is
|
|
// different from the rest
|
|
// of the world's
|
|
*pCurrOutput++ = 255 - (BYTE)*pAScan++;
|
|
}
|
|
}
|
|
break;
|
|
|
|
// TODO(P2): Might be advantageous
|
|
// to hand-formulate the following
|
|
// formats, too.
|
|
case BMP_FORMAT_1BIT_MSB_PAL:
|
|
// FALLTHROUGH intended
|
|
case BMP_FORMAT_4BIT_MSN_PAL:
|
|
// FALLTHROUGH intended
|
|
case BMP_FORMAT_16BIT_TC_LSB_MASK:
|
|
// FALLTHROUGH intended
|
|
case BMP_FORMAT_32BIT_TC_MASK:
|
|
{
|
|
Scanline pAScan = pAlphaReadAccess->GetScanline( y );
|
|
|
|
// using fallback for those
|
|
// seldom formats
|
|
for( x=0; x<nWidth; ++x )
|
|
{
|
|
// yes. x and y are swapped on Get/SetPixel
|
|
aCol = pReadAccess->GetColor(y,x);
|
|
|
|
*pCurrOutput++ = aCol.GetBlue();
|
|
*pCurrOutput++ = aCol.GetGreen();
|
|
*pCurrOutput++ = aCol.GetRed();
|
|
|
|
// out notion of alpha is
|
|
// different from the rest
|
|
// of the world's
|
|
*pCurrOutput++ = 255 - (BYTE)*pAScan++;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case BMP_FORMAT_1BIT_LSB_PAL:
|
|
// FALLTHROUGH intended
|
|
case BMP_FORMAT_4BIT_LSN_PAL:
|
|
// FALLTHROUGH intended
|
|
case BMP_FORMAT_8BIT_TC_MASK:
|
|
// FALLTHROUGH intended
|
|
case BMP_FORMAT_24BIT_TC_RGB:
|
|
// FALLTHROUGH intended
|
|
case BMP_FORMAT_24BIT_TC_MASK:
|
|
// FALLTHROUGH intended
|
|
case BMP_FORMAT_16BIT_TC_MSB_MASK:
|
|
// FALLTHROUGH intended
|
|
case BMP_FORMAT_32BIT_TC_ABGR:
|
|
// FALLTHROUGH intended
|
|
case BMP_FORMAT_32BIT_TC_ARGB:
|
|
// FALLTHROUGH intended
|
|
case BMP_FORMAT_32BIT_TC_BGRA:
|
|
// FALLTHROUGH intended
|
|
case BMP_FORMAT_32BIT_TC_RGBA:
|
|
// FALLTHROUGH intended
|
|
default:
|
|
ENSURE_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<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_AND_THROW( false,
|
|
"vclBitmapEx2Raw(): "
|
|
"Unexpected scanline format - has "
|
|
"WinSalBitmap::AcquireBuffer() changed?" );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// *no* alpha mask
|
|
ULONG nFormat = pReadAccess->GetScanlineFormat();
|
|
BYTE *pBuffer = reinterpret_cast<BYTE *>(rBmpData.mpBitmapData);
|
|
|
|
switch(nFormat)
|
|
{
|
|
case BMP_FORMAT_24BIT_TC_BGR:
|
|
|
|
{
|
|
sal_Int32 height = pReadAccess->Height();
|
|
for(sal_Int32 y=0; y<height; ++y)
|
|
{
|
|
BYTE *pScanline=pReadAccess->GetScanline(y);
|
|
sal_Int32 width = pReadAccess->Width();
|
|
for(sal_Int32 x=0; x<width; ++x)
|
|
{
|
|
// BGR -> RGB
|
|
BYTE b(*pScanline++);
|
|
BYTE g(*pScanline++);
|
|
BYTE r(*pScanline++);
|
|
*pBuffer++ = r;
|
|
*pBuffer++ = g;
|
|
*pBuffer++ = b;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case BMP_FORMAT_24BIT_TC_RGB:
|
|
|
|
{
|
|
sal_Int32 height = pReadAccess->Height();
|
|
for(sal_Int32 y=0; y<height; ++y)
|
|
{
|
|
BYTE *pScanline=pReadAccess->GetScanline(y);
|
|
sal_Int32 width = pReadAccess->Width();
|
|
for(sal_Int32 x=0; x<width; ++x)
|
|
{
|
|
// RGB -> RGB
|
|
BYTE r(*pScanline++);
|
|
BYTE g(*pScanline++);
|
|
BYTE b(*pScanline++);
|
|
*pBuffer++ = r;
|
|
*pBuffer++ = g;
|
|
*pBuffer++ = b;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case BMP_FORMAT_1BIT_MSB_PAL:
|
|
case BMP_FORMAT_1BIT_LSB_PAL:
|
|
case BMP_FORMAT_4BIT_MSN_PAL:
|
|
case BMP_FORMAT_4BIT_LSN_PAL:
|
|
case BMP_FORMAT_8BIT_PAL:
|
|
|
|
{
|
|
sal_Int32 height = pReadAccess->Height();
|
|
for(sal_Int32 y=0; y<height; ++y)
|
|
{
|
|
BYTE *pScanline=pReadAccess->GetScanline(y);
|
|
sal_Int32 width = pReadAccess->Width();
|
|
for(sal_Int32 x=0; x<width; ++x)
|
|
{
|
|
BitmapColor aCol(pReadAccess->GetPaletteColor(*pScanline++));
|
|
|
|
*pBuffer++ = aCol.GetRed();
|
|
*pBuffer++ = aCol.GetGreen();
|
|
*pBuffer++ = aCol.GetBlue();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// color_generator_linear
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
template<typename T> struct color_generator_linear
|
|
{
|
|
typedef typename T::value_type value_type;
|
|
|
|
color_generator_linear( const T &c1,
|
|
const T &c2,
|
|
unsigned int aSteps ) : maSteps(aSteps),
|
|
maColor1(c1),
|
|
maColor2(c2)
|
|
{
|
|
}
|
|
|
|
unsigned size() const { return maSteps; }
|
|
const T operator [] (unsigned v) const
|
|
{
|
|
const double w = double(v)/maSteps;
|
|
return T( static_cast<value_type>(maColor1.r+(maColor2.r-maColor1.r)*w),
|
|
static_cast<value_type>(maColor1.g+(maColor2.g-maColor1.g)*w),
|
|
static_cast<value_type>(maColor1.b+(maColor2.b-maColor1.b)*w),
|
|
static_cast<value_type>(maColor1.a+(maColor2.a-maColor1.a)*w));
|
|
}
|
|
|
|
unsigned int maSteps;
|
|
const T maColor1;
|
|
const T maColor2;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// color_generator_axial
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
template<typename T> struct color_generator_axial
|
|
{
|
|
typedef typename T::value_type value_type;
|
|
|
|
color_generator_axial( const T &c1,
|
|
const T &c2,
|
|
unsigned int aSteps ) : maSteps(aSteps),
|
|
maColor1(c1),
|
|
maColor2(c2)
|
|
{
|
|
}
|
|
|
|
unsigned size() const { return maSteps; }
|
|
const T operator [] (unsigned v) const
|
|
{
|
|
const double aHalfSteps = maSteps/2.0;
|
|
const double w = (v >= aHalfSteps) ?
|
|
1.0-((double(v)-aHalfSteps)/aHalfSteps) :
|
|
(double(v)*2.0)/maSteps;
|
|
return T( static_cast<value_type>(maColor1.r+(maColor2.r-maColor1.r)*w),
|
|
static_cast<value_type>(maColor1.g+(maColor2.g-maColor1.g)*w),
|
|
static_cast<value_type>(maColor1.b+(maColor2.b-maColor1.b)*w),
|
|
static_cast<value_type>(maColor1.a+(maColor2.a-maColor1.a)*w));
|
|
}
|
|
|
|
unsigned int maSteps;
|
|
const T maColor1;
|
|
const T maColor2;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// color_generator_adaptor
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
template<typename T> struct color_generator_adaptor
|
|
{
|
|
color_generator_adaptor( const T &c1,
|
|
const T &c2,
|
|
unsigned int aSteps ) : linear_generator(c1,c2,aSteps),
|
|
axial_generator(c1,c2,aSteps),
|
|
mbLinear(true) {}
|
|
void set_linear( bool bLinear ) { mbLinear=bLinear; }
|
|
unsigned size() const { return mbLinear ? linear_generator.size() : axial_generator.size(); }
|
|
const T operator [] (unsigned v) const
|
|
{
|
|
return mbLinear ?
|
|
linear_generator.operator [] (v) :
|
|
axial_generator.operator [] (v);
|
|
}
|
|
|
|
color_generator_linear<T> linear_generator;
|
|
color_generator_axial<T> axial_generator;
|
|
bool mbLinear;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// gradient_polymorphic_wrapper_base
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
struct gradient_polymorphic_wrapper_base
|
|
{
|
|
virtual int calculate(int x, int y, int) const = 0;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// gradient_polymorphic_wrapper
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
template<class GradientF> struct gradient_polymorphic_wrapper :
|
|
public gradient_polymorphic_wrapper_base
|
|
{
|
|
virtual int calculate(int x, int y, int d) const
|
|
{
|
|
return m_gradient.calculate(x, y, d);
|
|
}
|
|
GradientF m_gradient;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// gradient_rect
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
class gradient_rect
|
|
{
|
|
public:
|
|
|
|
int width;
|
|
int height;
|
|
|
|
inline int calculate(int x, int y, int d) const
|
|
{
|
|
int ax = abs(x);
|
|
int ay = abs(y);
|
|
int clamp_x = height>width ? 0 : (width-height);
|
|
int clamp_y = height>width ? (height-width) : 0;
|
|
int value_x = (ax-clamp_x)*d/(width-clamp_x);
|
|
int value_y = (ay-clamp_y)*d/(height-clamp_y);
|
|
if(ax < (clamp_x))
|
|
value_x = 0;
|
|
if(ay < (clamp_y))
|
|
value_y = 0;
|
|
return value_x > value_y ? value_x : value_y;
|
|
}
|
|
};
|
|
|
|
sal_uInt32 getBytesPerPixel( IColorBuffer::Format eFormat )
|
|
{
|
|
switch(eFormat)
|
|
{
|
|
default:
|
|
OSL_ENSURE(false, "Unexpected pixel format");
|
|
// FALLTHROUGH intended
|
|
case IColorBuffer::FMT_R8G8B8:
|
|
return 3L;
|
|
case IColorBuffer::FMT_A8R8G8B8:
|
|
return 4L;
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// Image::drawLinePolyPolygon
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
template<class pixel_format>
|
|
void Image::drawLinePolyPolygonImpl( const ::basegfx::B2DPolyPolygon& rPolyPolygon,
|
|
double fStrokeWidth,
|
|
const rendering::ViewState& viewState,
|
|
const rendering::RenderState& renderState )
|
|
{
|
|
::basegfx::B2DPolyPolygon aPolyPolygon( rPolyPolygon );
|
|
ARGB aRenderColor;
|
|
|
|
setupPolyPolygon( aPolyPolygon, false, aRenderColor, viewState, renderState );
|
|
|
|
if( !aPolyPolygon.count() )
|
|
return;
|
|
|
|
// Class template pixel_formats_rgb24 has full knowledge about this
|
|
// particular pixel format in memory. The only template parameter
|
|
// can be order_rgb24 or order_bgr24 that determines the order of color channels.
|
|
//typedef agg::pixfmt_rgba32 pixel_format;
|
|
pixel_format pixf(maRenderingBuffer);
|
|
|
|
// There are two basic renderers with almost the same functionality:
|
|
// renderer_base and renderer_mclip. The first one is used most often
|
|
// and it performs low level clipping.
|
|
// This simply adds clipping to the graphics buffer, the clip rect
|
|
// will be initialized to the area of the framebuffer.
|
|
typedef agg::renderer_base<pixel_format> renderer_base;
|
|
agg::renderer_base<pixel_format> renb(pixf);
|
|
|
|
// To draw Anti-Aliased primitives one shoud *rasterize* them first.
|
|
// The primary rasterization technique in AGG is scanline based.
|
|
// That is, a polygon is converted into a number of horizontal
|
|
// scanlines and then the scanlines are being rendered one by one.
|
|
// To transfer information from a rasterizer to the scanline renderer
|
|
// there scanline containers are used. A scanline consists of a
|
|
// number of horizontal, non-intersecting spans. All spans must be ordered by X.
|
|
// --> *packed* scanline container
|
|
agg::scanline_p8 sl;
|
|
|
|
typedef agg::renderer_outline_aa<renderer_base> renderer_type;
|
|
typedef agg::rasterizer_outline_aa<renderer_type> rasterizer_type;
|
|
agg::line_profile_aa profile;
|
|
profile.width(fStrokeWidth);
|
|
renderer_type ren(renb, profile);
|
|
rasterizer_type ras(ren);
|
|
|
|
const agg::rgba8 fillcolor(aRenderColor.Color.r,
|
|
aRenderColor.Color.g,
|
|
aRenderColor.Color.b,
|
|
aRenderColor.Color.a);
|
|
ren.color(fillcolor);
|
|
|
|
agg::path_storage path;
|
|
agg::conv_curve<agg::path_storage> curve(path);
|
|
|
|
for(sal_uInt32 nPolygon=0; nPolygon<aPolyPolygon.count(); ++nPolygon)
|
|
{
|
|
const basegfx::B2DPolygon aPolygon(aPolyPolygon.getB2DPolygon(nPolygon));
|
|
const sal_uInt32 nPointCount(aPolygon.count());
|
|
|
|
if(nPointCount)
|
|
{
|
|
if(aPolygon.areControlPointsUsed())
|
|
{
|
|
// prepare edge-based loop
|
|
basegfx::B2DPoint aCurrentPoint(aPolygon.getB2DPoint(0));
|
|
const sal_uInt32 nEdgeCount(aPolygon.isClosed() ? nPointCount - 1 : nPointCount);
|
|
|
|
// first vertex
|
|
path.move_to(aCurrentPoint.getX(), aCurrentPoint.getY());
|
|
|
|
for(sal_uInt32 a(0); a < nEdgeCount; a++)
|
|
{
|
|
// access next point
|
|
const sal_uInt32 nNextIndex((a + 1) % nPointCount);
|
|
const basegfx::B2DPoint aNextPoint(aPolygon.getB2DPoint(nNextIndex));
|
|
|
|
// get control points
|
|
const basegfx::B2DPoint aControlNext(aPolygon.getNextControlPoint(a));
|
|
const basegfx::B2DPoint aControlPrev(aPolygon.getPrevControlPoint(nNextIndex));
|
|
|
|
// specify first cp, second cp, next vertex
|
|
path.curve4(
|
|
aControlNext.getX(), aControlNext.getY(),
|
|
aControlPrev.getX(), aControlPrev.getY(),
|
|
aNextPoint.getX(), aNextPoint.getY());
|
|
|
|
// prepare next step
|
|
aCurrentPoint = aNextPoint;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const basegfx::B2DPoint aStartPoint(aPolygon.getB2DPoint(0));
|
|
ras.move_to_d(aStartPoint.getX(), aStartPoint.getY());
|
|
|
|
for(sal_uInt32 a(1); a < nPointCount; a++)
|
|
{
|
|
const basegfx::B2DPoint aVertexPoint(aPolygon.getB2DPoint(a));
|
|
ras.line_to_d(aVertexPoint.getX(), aVertexPoint.getY());
|
|
}
|
|
|
|
ras.render(aPolygon.isClosed());
|
|
}
|
|
}
|
|
}
|
|
|
|
ras.add_path(curve);
|
|
ras.render(false);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// Image::drawLinePolyPolygon
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Image::drawLinePolyPolygon( const ::basegfx::B2DPolyPolygon& rPoly,
|
|
double fStrokeWidth,
|
|
const rendering::ViewState& viewState,
|
|
const rendering::RenderState& renderState )
|
|
{
|
|
switch(maDesc.eFormat)
|
|
{
|
|
case FMT_R8G8B8:
|
|
drawLinePolyPolygonImpl<agg::pixfmt_rgb24>(rPoly,fStrokeWidth,viewState,renderState);
|
|
break;
|
|
case FMT_A8R8G8B8:
|
|
drawLinePolyPolygonImpl<agg::pixfmt_rgba32>(rPoly,fStrokeWidth,viewState,renderState);
|
|
break;
|
|
default:
|
|
OSL_ENSURE(false, "Unexpected pixel format");
|
|
break;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// Image::implDrawBitmap
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/** internal utility function to draw one image into another one.
|
|
the source image will be drawn with respect to the given
|
|
transform and clip settings.
|
|
*/
|
|
ImageCachedPrimitiveSharedPtr Image::implDrawBitmap(
|
|
const Image& rBitmap,
|
|
const rendering::ViewState& viewState,
|
|
const rendering::RenderState& renderState )
|
|
{
|
|
::basegfx::B2DPolyPolygon aPoly(
|
|
::basegfx::tools::createPolygonFromRect(
|
|
::basegfx::B2DRange(0.0, 0.0,
|
|
rBitmap.maDesc.nWidth,
|
|
rBitmap.maDesc.nHeight ) ) );
|
|
ARGB aFillColor;
|
|
|
|
setupPolyPolygon( aPoly, true, aFillColor, viewState, renderState );
|
|
|
|
if( !aPoly.count() )
|
|
return ImageCachedPrimitiveSharedPtr();
|
|
|
|
::basegfx::B2DHomMatrix aViewTransform;
|
|
::basegfx::B2DHomMatrix aRenderTransform;
|
|
::basegfx::B2DHomMatrix aTextureTransform;
|
|
|
|
::basegfx::unotools::homMatrixFromAffineMatrix(aRenderTransform,
|
|
renderState.AffineTransform);
|
|
::basegfx::unotools::homMatrixFromAffineMatrix(aViewTransform,
|
|
viewState.AffineTransform);
|
|
aTextureTransform *= aRenderTransform;
|
|
|
|
// TODO(F2): Fill in texture
|
|
rendering::Texture aTexture;
|
|
|
|
return fillTexturedPolyPolygon( rBitmap,
|
|
aPoly,
|
|
aTextureTransform,
|
|
aViewTransform,
|
|
aTexture );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// cachedPrimitiveFTPP [cachedPrimitive for [F]ill[T]extured[P]oly[P]olygon]
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#if AGG_VERSION >= 2400
|
|
template<class pixel_format_dst,class span_gen_type>
|
|
#else
|
|
template<class pixel_format,class span_gen_type>
|
|
#endif
|
|
class cachedPrimitiveFTPP : public ImageCachedPrimitive
|
|
{
|
|
public:
|
|
|
|
cachedPrimitiveFTPP( const ::basegfx::B2DHomMatrix &rTransform,
|
|
const ::basegfx::B2DHomMatrix &rViewTransform,
|
|
agg::rendering_buffer &dst,
|
|
const agg::rendering_buffer& src ) :
|
|
aTransform(rTransform),
|
|
inter(tm),
|
|
filter(filter_kernel),
|
|
#if AGG_VERSION >= 2400
|
|
pixs(const_cast<agg::rendering_buffer&>(src)),
|
|
source(pixs),
|
|
sg(source,inter,filter),
|
|
pixd(dst),
|
|
rb(pixd),
|
|
ren(rb,sa,sg)
|
|
#else
|
|
sg(sa,src,inter,filter),
|
|
pixf(dst),
|
|
rb(pixf),
|
|
ren(rb,sg)
|
|
#endif
|
|
{
|
|
::basegfx::B2DHomMatrix aFinalTransform(aTransform);
|
|
aFinalTransform *= rViewTransform;
|
|
tm = transAffineFromB2DHomMatrix(aFinalTransform);
|
|
tm.invert();
|
|
}
|
|
|
|
virtual void setImage( const ::boost::shared_ptr< class Image >& rTargetImage )
|
|
{
|
|
pImage=rTargetImage;
|
|
}
|
|
|
|
virtual sal_Int8 redraw( const ::com::sun::star::rendering::ViewState& aState ) const
|
|
{
|
|
::basegfx::B2DHomMatrix aViewTransform;
|
|
::basegfx::unotools::homMatrixFromAffineMatrix(aViewTransform,aState.AffineTransform);
|
|
::basegfx::B2DHomMatrix aFinalTransform(aTransform);
|
|
aFinalTransform *= aViewTransform;
|
|
tm = transAffineFromB2DHomMatrix(aFinalTransform);
|
|
tm.invert();
|
|
redraw();
|
|
return ::com::sun::star::rendering::RepaintResult::REDRAWN;
|
|
}
|
|
|
|
inline void redraw() const { agg::render_scanlines(ras, sl, ren); }
|
|
|
|
mutable agg::rasterizer_scanline_aa<> ras;
|
|
|
|
private:
|
|
|
|
typedef agg::span_interpolator_linear<> interpolator_type;
|
|
#if AGG_VERSION >= 2400
|
|
typedef agg::renderer_base<pixel_format_dst> renderer_base;
|
|
typedef agg::span_allocator< typename span_gen_type::color_type > span_alloc_type;
|
|
typedef agg::renderer_scanline_aa<renderer_base, span_alloc_type, span_gen_type> renderer_type;
|
|
typedef typename span_gen_type::source_type source_type;
|
|
typedef typename span_gen_type::source_type::pixfmt_type pixel_format_src;
|
|
#else
|
|
typedef agg::renderer_base<pixel_format> renderer_base;
|
|
typedef agg::renderer_scanline_aa<renderer_base, span_gen_type> renderer_type;
|
|
#endif
|
|
|
|
::basegfx::B2DHomMatrix aTransform;
|
|
interpolator_type inter;
|
|
agg::image_filter_bilinear filter_kernel;
|
|
agg::image_filter_lut filter;
|
|
#if AGG_VERSION >= 2400
|
|
span_alloc_type sa;
|
|
pixel_format_src pixs;
|
|
source_type source;
|
|
#else
|
|
agg::span_allocator< typename span_gen_type::color_type > sa;
|
|
#endif
|
|
span_gen_type sg;
|
|
#if AGG_VERSION >= 2400
|
|
pixel_format_dst pixd;
|
|
#else
|
|
pixel_format pixf;
|
|
#endif
|
|
renderer_base rb;
|
|
mutable renderer_type ren;
|
|
mutable agg::scanline_p8 sl;
|
|
mutable agg::trans_affine tm;
|
|
ImageSharedPtr pImage;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// Image::fillTexturedPolyPolygon
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
template<class pixel_format,class span_gen_type>
|
|
ImageCachedPrimitiveSharedPtr Image::fillTexturedPolyPolygonImpl(
|
|
const Image& rTexture,
|
|
const ::basegfx::B2DPolyPolygon& rPolyPolygon,
|
|
const ::basegfx::B2DHomMatrix& rOverallTransform,
|
|
const ::basegfx::B2DHomMatrix& rViewTransform,
|
|
const rendering::Texture& )
|
|
{
|
|
// calculate final overall transform.
|
|
::basegfx::B2DHomMatrix aOverallTransform(rOverallTransform);
|
|
aOverallTransform *= rViewTransform;
|
|
|
|
// instead of always using the full-blown solution we
|
|
// first check to see if this is a simple rectangular
|
|
// 1-to-1 copy from source to destination image.
|
|
::basegfx::B2DTuple aTranslate(aOverallTransform.get(0,2),aOverallTransform.get(1,2));
|
|
::basegfx::B2DTuple aSize(rTexture.maDesc.nWidth,rTexture.maDesc.nHeight);
|
|
::basegfx::B2DRange aRange(aTranslate,aTranslate+aSize);
|
|
::basegfx::B2DPolyPolygon aPolyPolygon(rPolyPolygon);
|
|
aPolyPolygon.transform(aOverallTransform);
|
|
if(::basegfx::tools::isPolyPolygonEqualRectangle(aPolyPolygon,aRange))
|
|
{
|
|
// yes, we can take the shortcut.
|
|
// but we need to clip the destination rectangle
|
|
// against the boundary of the destination image.
|
|
sal_Int32 dwSrcX(0);
|
|
sal_Int32 dwSrcY(0);
|
|
sal_Int32 dwDstX(static_cast<sal_Int32>(aTranslate.getX()));
|
|
sal_Int32 dwDstY(static_cast<sal_Int32>(aTranslate.getY()));
|
|
sal_Int32 dwWidth(rTexture.maDesc.nWidth);
|
|
sal_Int32 dwHeight(rTexture.maDesc.nHeight);
|
|
|
|
// prevent fast copy if destination position is not an
|
|
// integer coordinate. otherwise we would most probably
|
|
// introduce visual glitches while combining this with
|
|
// high-accuracy rendering stuff.
|
|
if( ::basegfx::fTools::equalZero(aTranslate.getX()-dwDstX) &&
|
|
::basegfx::fTools::equalZero(aTranslate.getY()-dwDstY))
|
|
{
|
|
// clip against destination boundary. shrink size if
|
|
// necessary, modify destination position if we need to.
|
|
if(dwDstX < 0) { dwWidth-=dwDstX; dwSrcX=-dwDstX; dwDstX=0; }
|
|
if(dwDstY < 0) { dwHeight-=dwDstY; dwSrcY=-dwDstY; dwDstY=0; }
|
|
const sal_Int32 dwRight(dwDstX+dwWidth);
|
|
const sal_Int32 dwBottom(dwDstY+dwHeight);
|
|
if(dwRight > dwWidth)
|
|
dwWidth -= dwRight-dwWidth;
|
|
if(dwBottom > dwHeight)
|
|
dwHeight -= dwBottom-dwHeight;
|
|
|
|
// calculate source buffer
|
|
const Description &srcDesc = rTexture.maDesc;
|
|
const sal_uInt32 dwSrcBytesPerPixel(getBytesPerPixel(srcDesc.eFormat));
|
|
const sal_uInt32 dwSrcPitch(srcDesc.nWidth*dwSrcBytesPerPixel+srcDesc.nStride);
|
|
sal_uInt8 *pSrcBuffer = rTexture.maDesc.pBuffer+(dwSrcPitch*dwSrcX)+(dwSrcBytesPerPixel*dwSrcY);
|
|
|
|
// calculate destination buffer
|
|
const Description &dstDesc = maDesc;
|
|
const sal_uInt32 dwDstBytesPerPixel(getBytesPerPixel(dstDesc.eFormat));
|
|
const sal_uInt32 dwDstPitch(dstDesc.nWidth*dwDstBytesPerPixel+dstDesc.nStride);
|
|
sal_uInt8 *pDstBuffer = maDesc.pBuffer+(dwDstPitch*dwDstY)+(dwDstBytesPerPixel*dwDstX);
|
|
|
|
// if source and destination format match, we can simply
|
|
// copy whole scanlines.
|
|
if(srcDesc.eFormat == dstDesc.eFormat)
|
|
{
|
|
const sal_Size dwNumBytesPerScanline(dwSrcBytesPerPixel*dwWidth);
|
|
for(sal_Int32 y=0; y<dwHeight; ++y)
|
|
{
|
|
rtl_copyMemory(pDstBuffer,pSrcBuffer,dwNumBytesPerScanline);
|
|
pSrcBuffer += dwSrcPitch;
|
|
pDstBuffer += dwDstPitch;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// otherwise [formats do not match], we need to copy
|
|
// each pixel one by one and convert from source to destination format.
|
|
if(srcDesc.eFormat == FMT_A8R8G8B8 && dstDesc.eFormat == FMT_R8G8B8)
|
|
{
|
|
for(sal_Int32 y=0; y<dwHeight; ++y)
|
|
{
|
|
sal_uInt8 *pSrc=pSrcBuffer;
|
|
sal_uInt8 *pDst=pDstBuffer;
|
|
for(sal_Int32 x=0; x<dwWidth; ++x)
|
|
{
|
|
BYTE r(*pSrc++);
|
|
BYTE g(*pSrc++);
|
|
BYTE b(*pSrc++);
|
|
BYTE Alpha(*pSrc++);
|
|
BYTE OneMinusAlpha(0xFF-Alpha);
|
|
*pDst=(((r*Alpha)+((*pDst)*OneMinusAlpha))/0xFF);
|
|
++pDst;
|
|
*pDst=(((g*Alpha)+((*pDst)*OneMinusAlpha))/0xFF);
|
|
++pDst;
|
|
*pDst=(((b*Alpha)+((*pDst)*OneMinusAlpha))/0xFF);
|
|
++pDst;
|
|
}
|
|
pSrcBuffer += dwSrcPitch;
|
|
pDstBuffer += dwDstPitch;
|
|
}
|
|
}
|
|
else if(srcDesc.eFormat == FMT_R8G8B8 && dstDesc.eFormat == FMT_A8R8G8B8)
|
|
{
|
|
for(sal_Int32 y=0; y<dwHeight; ++y)
|
|
{
|
|
sal_uInt8 *pSrc=pSrcBuffer;
|
|
sal_uInt8 *pDst=pDstBuffer;
|
|
for(sal_Int32 x=0; x<dwWidth; ++x)
|
|
{
|
|
BYTE r(*pSrc++);
|
|
BYTE g(*pSrc++);
|
|
BYTE b(*pSrc++);
|
|
*pDst++=r;
|
|
*pDst++=g;
|
|
*pDst++=b;
|
|
*pDst++=0xFF;
|
|
}
|
|
pSrcBuffer += dwSrcPitch;
|
|
pDstBuffer += dwDstPitch;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ImageCachedPrimitiveSharedPtr();
|
|
}
|
|
}
|
|
|
|
typedef cachedPrimitiveFTPP<pixel_format,span_gen_type> cachedPrimitive_t;
|
|
cachedPrimitive_t *pPrimitive = new cachedPrimitive_t( rOverallTransform,
|
|
rViewTransform,
|
|
maRenderingBuffer,
|
|
rTexture.maRenderingBuffer);
|
|
|
|
agg::path_storage path;
|
|
agg::conv_curve<agg::path_storage> curve(path);
|
|
|
|
for(sal_uInt32 nPolygon(0); nPolygon < rPolyPolygon.count(); nPolygon++)
|
|
{
|
|
const basegfx::B2DPolygon aPolygon(rPolyPolygon.getB2DPolygon(nPolygon));
|
|
const sal_uInt32 nPointCount(aPolygon.count());
|
|
|
|
if(nPointCount)
|
|
{
|
|
if(aPolygon.areControlPointsUsed())
|
|
{
|
|
// prepare edge-based loop
|
|
basegfx::B2DPoint aCurrentPoint(aPolygon.getB2DPoint(0));
|
|
const sal_uInt32 nEdgeCount(aPolygon.isClosed() ? nPointCount - 1 : nPointCount);
|
|
|
|
// first vertex
|
|
path.move_to(aCurrentPoint.getX(), aCurrentPoint.getY());
|
|
|
|
for(sal_uInt32 a(0); a < nEdgeCount; a++)
|
|
{
|
|
// access next point
|
|
const sal_uInt32 nNextIndex((a + 1) % nPointCount);
|
|
const basegfx::B2DPoint aNextPoint(aPolygon.getB2DPoint(nNextIndex));
|
|
|
|
// get control points
|
|
const basegfx::B2DPoint aControlNext(aPolygon.getNextControlPoint(a));
|
|
const basegfx::B2DPoint aControlPrev(aPolygon.getPrevControlPoint(nNextIndex));
|
|
|
|
// specify first cp, second cp, next vertex
|
|
path.curve4(
|
|
aControlNext.getX(), aControlNext.getY(),
|
|
aControlPrev.getX(), aControlPrev.getY(),
|
|
aNextPoint.getX(), aNextPoint.getY());
|
|
|
|
// prepare next step
|
|
aCurrentPoint = aNextPoint;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const basegfx::B2DPoint aPoint(aPolygon.getB2DPoint(0));
|
|
pPrimitive->ras.move_to_d(aPoint.getX(), aPoint.getY());
|
|
|
|
for(sal_uInt32 a(1); a < nPointCount; a++)
|
|
{
|
|
const basegfx::B2DPoint aVertexPoint(aPolygon.getB2DPoint(a));
|
|
pPrimitive->ras.line_to_d(aVertexPoint.getX(), aVertexPoint.getY());
|
|
}
|
|
|
|
if(aPolygon.isClosed())
|
|
{
|
|
pPrimitive->ras.close_polygon();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pPrimitive->ras.add_path(curve);
|
|
pPrimitive->redraw();
|
|
|
|
return ImageCachedPrimitiveSharedPtr(pPrimitive);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// Image::fillTexturedPolyPolygon
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
ImageCachedPrimitiveSharedPtr Image::fillTexturedPolyPolygon(
|
|
const Image& rTexture,
|
|
const ::basegfx::B2DPolyPolygon& rPolyPolygon,
|
|
const ::basegfx::B2DHomMatrix& rOverallTransform,
|
|
const ::basegfx::B2DHomMatrix& rViewTransform,
|
|
const rendering::Texture& texture )
|
|
{
|
|
typedef agg::wrap_mode_repeat wrap_x_type;
|
|
typedef agg::wrap_mode_repeat wrap_y_type;
|
|
typedef agg::pixfmt_rgb24 pixfmt_rgb24;
|
|
typedef agg::pixfmt_rgba32 pixfmt_rgba32;
|
|
#if AGG_VERSION >= 2400
|
|
typedef agg::image_accessor_wrap< pixfmt_rgba32, wrap_x_type, wrap_y_type > img_source_type_rgba;
|
|
typedef agg::image_accessor_wrap< pixfmt_rgb24, wrap_x_type, wrap_y_type > img_source_type_rgb;
|
|
|
|
typedef agg::span_image_resample_rgba_affine< img_source_type_rgba > span_gen_type_rgba;
|
|
typedef agg::span_image_resample_rgb_affine< img_source_type_rgb > span_gen_type_rgb;
|
|
#else
|
|
typedef agg::span_pattern_resample_rgba_affine< pixfmt_rgba32::color_type,
|
|
pixfmt_rgba32::order_type,
|
|
wrap_x_type,
|
|
wrap_y_type> span_gen_type_rgba;
|
|
typedef agg::span_pattern_resample_rgb_affine< pixfmt_rgb24::color_type,
|
|
pixfmt_rgb24::order_type,
|
|
wrap_x_type,
|
|
wrap_y_type> span_gen_type_rgb;
|
|
#endif
|
|
|
|
const Format nDest = maDesc.eFormat;
|
|
const Format nSource = rTexture.maDesc.eFormat;
|
|
|
|
if(nDest == FMT_R8G8B8 && nSource == FMT_R8G8B8)
|
|
{
|
|
return fillTexturedPolyPolygonImpl< agg::pixfmt_rgb24,
|
|
span_gen_type_rgb >(
|
|
rTexture,
|
|
rPolyPolygon,
|
|
rOverallTransform,
|
|
rViewTransform,
|
|
texture );
|
|
}
|
|
else if(nDest == FMT_R8G8B8 && nSource == FMT_A8R8G8B8)
|
|
{
|
|
return fillTexturedPolyPolygonImpl< agg::pixfmt_rgb24,
|
|
span_gen_type_rgba >(
|
|
rTexture,
|
|
rPolyPolygon,
|
|
rOverallTransform,
|
|
rViewTransform,
|
|
texture );
|
|
}
|
|
else if(nDest == FMT_A8R8G8B8 && nSource == FMT_R8G8B8)
|
|
{
|
|
return fillTexturedPolyPolygonImpl< agg::pixfmt_rgba32,
|
|
span_gen_type_rgb >(
|
|
rTexture,
|
|
rPolyPolygon,
|
|
rOverallTransform,
|
|
rViewTransform,
|
|
texture );
|
|
}
|
|
else if(nDest == FMT_A8R8G8B8 && nSource == FMT_A8R8G8B8)
|
|
{
|
|
return fillTexturedPolyPolygonImpl< agg::pixfmt_rgba32,
|
|
span_gen_type_rgba >(
|
|
rTexture,
|
|
rPolyPolygon,
|
|
rOverallTransform,
|
|
rViewTransform,
|
|
texture );
|
|
}
|
|
else
|
|
{
|
|
OSL_ENSURE(false, "Unexpected pixel format");
|
|
}
|
|
|
|
return ImageCachedPrimitiveSharedPtr();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
// Image::fillGradient
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
template<class pixel_format>
|
|
void Image::fillGradientImpl( const ParametricPolyPolygon::Values& rValues,
|
|
const uno::Sequence< double >& rUnoColor1,
|
|
const uno::Sequence< double >& rUnoColor2,
|
|
const ::basegfx::B2DPolyPolygon& rPolyPolygon,
|
|
const ::basegfx::B2DHomMatrix& rOverallTransform,
|
|
const rendering::Texture& )
|
|
{
|
|
const ARGB aColor1(0xFFFFFFFF,
|
|
rUnoColor1);
|
|
const ARGB aColor2(0xFFFFFFFF,
|
|
rUnoColor2);
|
|
|
|
// first of all we need to provide the framebuffer we want to render to.
|
|
// the properties of the framebuffer are
|
|
// 1) memory & layout [width, height, stride]
|
|
// 2) pixelformat
|
|
// 3) clipping
|
|
|
|
// Class template pixel_formats_rgb24 has full knowledge about this
|
|
// particular pixel format in memory. The only template parameter
|
|
// can be order_rgb24 or order_bgr24 that determines the order of color channels.
|
|
pixel_format pixf(maRenderingBuffer);
|
|
|
|
// There are two basic renderers with almost the same functionality:
|
|
// renderer_base and renderer_mclip. The first one is used most often
|
|
// and it performs low level clipping.
|
|
// This simply adds clipping to the graphics buffer, the clip rect
|
|
// will be initialized to the area of the framebuffer.
|
|
typedef agg::renderer_base<pixel_format> renderer_base;
|
|
renderer_base rb(pixf);
|
|
|
|
// bounding rectangle of untransformed polypolygon
|
|
const ::basegfx::B2DRange& rBounds(::basegfx::tools::getRange(rPolyPolygon));
|
|
|
|
// the color generator produces a specific color from
|
|
// some given interpolation value.
|
|
// number of steps for color interpolation
|
|
typedef typename pixel_format::color_type color_type;
|
|
color_type color1(agg::rgba8(aColor1.Color.r,
|
|
aColor1.Color.g,
|
|
aColor1.Color.b,
|
|
255));
|
|
color_type color2(agg::rgba8(aColor2.Color.r,
|
|
aColor2.Color.g,
|
|
aColor2.Color.b,
|
|
255));
|
|
typedef color_generator_adaptor<color_type> color_generator_type;
|
|
unsigned int dwNumSteps = static_cast<unsigned int>(rBounds.getWidth());
|
|
color_generator_type colors(color1,color2,dwNumSteps);
|
|
colors.set_linear(rValues.meType != ParametricPolyPolygon::GRADIENT_AXIAL);
|
|
|
|
// 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
|
|
|
|
uno::Reference< lang::XUnoTunnel > 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<BitmapEx*>(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<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( 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<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(
|
|
tools::polyPolygonFromXPolyPolygon2D( 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(
|
|
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<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 );
|
|
}
|
|
|
|
}
|