summaryrefslogtreecommitdiff
path: root/canvas/source/vcl
diff options
context:
space:
mode:
authorthb <thb@openoffice.org>2009-10-16 00:43:16 +0200
committerthb <thb@openoffice.org>2009-10-16 00:43:16 +0200
commit1837a267a2cf82b0152631e416d8be66c2adef25 (patch)
treee9df5704754fc1ed559cdc01d7d5c41207af5374 /canvas/source/vcl
parent9b9d44ee50a38180c2451ca527bf7b9f6f46f0fe (diff)
#i105937# Much improved gradient support for canvas/basegfx/drawinglayer.
See http://blog.thebehrens.net/2009/07/28/hackweek-iv-canvas-convwatch/ for more background information
Diffstat (limited to 'canvas/source/vcl')
-rw-r--r--canvas/source/vcl/canvas.hxx3
-rw-r--r--canvas/source/vcl/canvashelper_texturefill.cxx286
-rw-r--r--canvas/source/vcl/spritecanvas.hxx3
3 files changed, 117 insertions, 175 deletions
diff --git a/canvas/source/vcl/canvas.hxx b/canvas/source/vcl/canvas.hxx
index 2279f817a406..c4e1a2853532 100644
--- a/canvas/source/vcl/canvas.hxx
+++ b/canvas/source/vcl/canvas.hxx
@@ -41,7 +41,6 @@
#include <com/sun/star/rendering/XIntegerBitmap.hpp>
#include <com/sun/star/rendering/XGraphicDevice.hpp>
#include <com/sun/star/rendering/XBufferController.hpp>
-#include <com/sun/star/rendering/XParametricPolyPolygon2DFactory.hpp>
#include <cppuhelper/compbase7.hxx>
#include <comphelper/uno3.hxx>
@@ -63,7 +62,7 @@ namespace vclcanvas
typedef ::cppu::WeakComponentImplHelper7< ::com::sun::star::rendering::XBitmapCanvas,
::com::sun::star::rendering::XIntegerBitmap,
::com::sun::star::rendering::XGraphicDevice,
- ::com::sun::star::rendering::XParametricPolyPolygon2DFactory,
+ ::com::sun::star::lang::XMultiServiceFactory,
::com::sun::star::util::XUpdatable,
::com::sun::star::beans::XPropertySet,
::com::sun::star::lang::XServiceName > GraphicDeviceBase_Base;
diff --git a/canvas/source/vcl/canvashelper_texturefill.cxx b/canvas/source/vcl/canvashelper_texturefill.cxx
index 571a8c4fc5a3..5219c6349186 100644
--- a/canvas/source/vcl/canvashelper_texturefill.cxx
+++ b/canvas/source/vcl/canvashelper_texturefill.cxx
@@ -57,6 +57,8 @@
#include <basegfx/polygon/b2dpolypolygontools.hxx>
#include <basegfx/polygon/b2dlinegeometry.hxx>
#include <basegfx/tools/tools.hxx>
+#include <basegfx/tools/lerp.hxx>
+#include <basegfx/tools/keystoplerp.hxx>
#include <basegfx/tools/canvastools.hxx>
#include <basegfx/numeric/ftools.hxx>
@@ -65,6 +67,9 @@
#include <canvas/canvastools.hxx>
#include <canvas/parametricpolypolygon.hxx>
+#include <boost/bind.hpp>
+#include <boost/tuple/tuple.hpp>
+
#include "spritecanvas.hxx"
#include "canvashelper.hxx"
#include "impltools.hxx"
@@ -118,17 +123,13 @@ namespace vclcanvas
Since most of the code for linear and axial gradients are
the same, we've a unified method here
*/
- void fillGeneralLinearGradient( OutputDevice& rOutDev,
- const ::basegfx::B2DHomMatrix& rTextureTransform,
- const ::Rectangle& rBounds,
- int nStepCount,
- const ::Color& rColor1,
- const ::Color& rColor2,
- bool bFillNonOverlapping,
- bool bAxialGradient )
+ void fillLinearGradient( OutputDevice& rOutDev,
+ const ::basegfx::B2DHomMatrix& rTextureTransform,
+ const ::Rectangle& rBounds,
+ unsigned int nStepCount,
+ const ::canvas::ParametricPolyPolygon::Values& rValues,
+ const std::vector< ::Color >& rColors )
{
- (void)bFillNonOverlapping;
-
// determine general position of gradient in relation to
// the bound rect
// =====================================================
@@ -207,36 +208,26 @@ namespace vclcanvas
// iteratively render all other strips
// -----------------------------------
- // ensure that nStepCount is odd, to have a well-defined
- // middle index for axial gradients.
- if( bAxialGradient && !(nStepCount % 2) )
+ // ensure that nStepCount matches color stop parity, to
+ // have a well-defined middle color e.g. for axial
+ // gradients.
+ if( (rColors.size() % 2) != (nStepCount % 2) )
++nStepCount;
- const int nStepCountHalved( nStepCount / 2 );
+ basegfx::tools::KeyStopLerp aLerper(rValues.maStops);
// only iterate nStepCount-1 steps, as the last strip is
// explicitely painted below
- for( int i=0; i<nStepCount-1; ++i )
+ for( unsigned int i=0; i<nStepCount-1; ++i )
{
- // lerp color
- if( bAxialGradient )
- {
- // axial gradient has a triangle-like interpolation function
- const int iPrime( i<=nStepCountHalved ? i : nStepCount-i-1);
+ std::ptrdiff_t nIndex;
+ double fAlpha;
+ boost::tuples::tie(nIndex,fAlpha)=aLerper.lerp(double(i)/nStepCount);
- rOutDev.SetFillColor(
- Color( (UINT8)(((nStepCountHalved - iPrime)*rColor1.GetRed() + iPrime*rColor2.GetRed())/nStepCountHalved),
- (UINT8)(((nStepCountHalved - iPrime)*rColor1.GetGreen() + iPrime*rColor2.GetGreen())/nStepCountHalved),
- (UINT8)(((nStepCountHalved - iPrime)*rColor1.GetBlue() + iPrime*rColor2.GetBlue())/nStepCountHalved) ) );
- }
- else
- {
- // linear gradient has a plain lerp between start and end color
- rOutDev.SetFillColor(
- Color( (UINT8)(((nStepCount - i)*rColor1.GetRed() + i*rColor2.GetRed())/nStepCount),
- (UINT8)(((nStepCount - i)*rColor1.GetGreen() + i*rColor2.GetGreen())/nStepCount),
- (UINT8)(((nStepCount - i)*rColor1.GetBlue() + i*rColor2.GetBlue())/nStepCount) ) );
- }
+ rOutDev.SetFillColor(
+ Color( (UINT8)(basegfx::tools::lerp(rColors[nIndex].GetRed(),rColors[nIndex+1].GetRed(),fAlpha)),
+ (UINT8)(basegfx::tools::lerp(rColors[nIndex].GetGreen(),rColors[nIndex+1].GetGreen(),fAlpha)),
+ (UINT8)(basegfx::tools::lerp(rColors[nIndex].GetBlue(),rColors[nIndex+1].GetBlue(),fAlpha)) ));
// copy right egde of polygon to left edge (and also
// copy the closing point)
@@ -283,59 +274,18 @@ namespace vclcanvas
aTempPoly[3] = ::Point( ::basegfx::fround( rPoint4.getX() ),
::basegfx::fround( rPoint4.getY() ) );
- if( bAxialGradient )
- rOutDev.SetFillColor( rColor1 );
- else
- rOutDev.SetFillColor( rColor2 );
+ rOutDev.SetFillColor( rColors.back() );
rOutDev.DrawPolygon( aTempPoly );
}
-
- inline void fillLinearGradient( OutputDevice& rOutDev,
- const ::Color& rColor1,
- const ::Color& rColor2,
- const ::basegfx::B2DHomMatrix& rTextureTransform,
- const ::Rectangle& rBounds,
- int nStepCount,
- bool bFillNonOverlapping )
- {
- fillGeneralLinearGradient( rOutDev,
- rTextureTransform,
- rBounds,
- nStepCount,
- rColor1,
- rColor2,
- bFillNonOverlapping,
- false );
- }
-
- inline void fillAxialGradient( OutputDevice& rOutDev,
- const ::Color& rColor1,
- const ::Color& rColor2,
- const ::basegfx::B2DHomMatrix& rTextureTransform,
- const ::Rectangle& rBounds,
- int nStepCount,
- bool bFillNonOverlapping )
- {
- fillGeneralLinearGradient( rOutDev,
- rTextureTransform,
- rBounds,
- nStepCount,
- rColor1,
- rColor2,
- bFillNonOverlapping,
- true );
- }
-
void fillPolygonalGradient( OutputDevice& rOutDev,
- const ::canvas::ParametricPolyPolygon::Values& rValues,
- const ::Color& rColor1,
- const ::Color& rColor2,
const ::basegfx::B2DHomMatrix& rTextureTransform,
const ::Rectangle& rBounds,
- int nStepCount,
- bool bFillNonOverlapping )
+ unsigned int nStepCount,
+ bool bFillNonOverlapping,
+ const ::canvas::ParametricPolyPolygon::Values& rValues,
+ const std::vector< ::Color >& rColors )
{
const ::basegfx::B2DPolygon& rGradientPoly( rValues.maGradientPoly );
@@ -369,9 +319,6 @@ namespace vclcanvas
// apply scaling (possibly anisotrophic) to inner polygon
// ------------------------------------------------------
- // move center of scaling to origin
- aInnerPolygonTransformMatrix.translate( -0.5, -0.5 );
-
// scale inner polygon according to aspect ratio: for
// wider-than-tall bounds (nAspectRatio > 1.0), the inner
// polygon, representing the gradient focus, must have
@@ -396,9 +343,6 @@ namespace vclcanvas
aInnerPolygonTransformMatrix.scale( 0.0, 0.0 );
}
- // move origin back to former center of polygon
- aInnerPolygonTransformMatrix.translate( 0.5, 0.5 );
-
// and finally, add texture transform to it.
aInnerPolygonTransformMatrix *= rTextureTransform;
@@ -406,8 +350,8 @@ namespace vclcanvas
aInnerPoly.transform( aInnerPolygonTransformMatrix );
- const sal_Int32 nNumPoints( aOuterPoly.count() );
- ::Polygon aTempPoly( static_cast<USHORT>(nNumPoints+1) );
+ const sal_uInt32 nNumPoints( aOuterPoly.count() );
+ ::Polygon aTempPoly( static_cast<USHORT>(nNumPoints+1) );
// increase number of steps by one: polygonal gradients have
// the outermost polygon rendered in rColor2, and the
@@ -425,37 +369,42 @@ namespace vclcanvas
// color).
++nStepCount;
+ basegfx::tools::KeyStopLerp aLerper(rValues.maStops);
+
if( !bFillNonOverlapping )
{
// fill background
- rOutDev.SetFillColor( rColor1 );
+ rOutDev.SetFillColor( rColors.front() );
rOutDev.DrawRect( rBounds );
// render polygon
// ==============
- for( int i=1,p; i<nStepCount; ++i )
+ for( unsigned int i=1,p; i<nStepCount; ++i )
{
+ const double fT( i/double(nStepCount) );
+
+ std::ptrdiff_t nIndex;
+ double fAlpha;
+ boost::tuples::tie(nIndex,fAlpha)=aLerper.lerp(fT);
+
// lerp color
rOutDev.SetFillColor(
- Color( (UINT8)(((nStepCount - i)*rColor1.GetRed() + i*rColor2.GetRed())/nStepCount),
- (UINT8)(((nStepCount - i)*rColor1.GetGreen() + i*rColor2.GetGreen())/nStepCount),
- (UINT8)(((nStepCount - i)*rColor1.GetBlue() + i*rColor2.GetBlue())/nStepCount) ) );
+ Color( (UINT8)(basegfx::tools::lerp(rColors[nIndex].GetRed(),rColors[nIndex+1].GetRed(),fAlpha)),
+ (UINT8)(basegfx::tools::lerp(rColors[nIndex].GetGreen(),rColors[nIndex+1].GetGreen(),fAlpha)),
+ (UINT8)(basegfx::tools::lerp(rColors[nIndex].GetBlue(),rColors[nIndex+1].GetBlue(),fAlpha)) ));
// scale and render polygon, by interpolating between
// outer and inner polygon.
- // calc interpolation parameter in [0,1] range
- const double nT( (nStepCount-i)/double(nStepCount) );
-
for( p=0; p<nNumPoints; ++p )
{
const ::basegfx::B2DPoint& rOuterPoint( aOuterPoly.getB2DPoint(p) );
const ::basegfx::B2DPoint& rInnerPoint( aInnerPoly.getB2DPoint(p) );
aTempPoly[(USHORT)p] = ::Point(
- basegfx::fround( (1.0-nT)*rInnerPoint.getX() + nT*rOuterPoint.getX() ),
- basegfx::fround( (1.0-nT)*rInnerPoint.getY() + nT*rOuterPoint.getY() ) );
+ basegfx::fround( fT*rInnerPoint.getX() + (1-fT)*rOuterPoint.getX() ),
+ basegfx::fround( fT*rInnerPoint.getY() + (1-fT)*rOuterPoint.getY() ) );
}
// close polygon explicitely
@@ -489,13 +438,19 @@ namespace vclcanvas
aTempPolyPoly.Insert( aTempPoly );
aTempPolyPoly.Insert( aTempPoly2 );
- for( int i=0,p; i<nStepCount; ++i )
+ for( unsigned int i=0,p; i<nStepCount; ++i )
{
+ const double fT( (i+1)/double(nStepCount) );
+
+ std::ptrdiff_t nIndex;
+ double fAlpha;
+ boost::tuples::tie(nIndex,fAlpha)=aLerper.lerp(fT);
+
// lerp color
rOutDev.SetFillColor(
- Color( (UINT8)(((nStepCount - i)*rColor1.GetRed() + i*rColor2.GetRed())/nStepCount),
- (UINT8)(((nStepCount - i)*rColor1.GetGreen() + i*rColor2.GetGreen())/nStepCount),
- (UINT8)(((nStepCount - i)*rColor1.GetBlue() + i*rColor2.GetBlue())/nStepCount) ) );
+ Color( (UINT8)(basegfx::tools::lerp(rColors[nIndex].GetRed(),rColors[nIndex+1].GetRed(),fAlpha)),
+ (UINT8)(basegfx::tools::lerp(rColors[nIndex].GetGreen(),rColors[nIndex+1].GetGreen(),fAlpha)),
+ (UINT8)(basegfx::tools::lerp(rColors[nIndex].GetBlue(),rColors[nIndex+1].GetBlue(),fAlpha)) ));
#if defined(VERBOSE) && OSL_DEBUG_LEVEL > 0
if( i && !(i % 10) )
@@ -506,17 +461,14 @@ namespace vclcanvas
// calculate the inner polygon, which is actually the
// start of the _next_ color strip. Thus, i+1
- // calc interpolation parameter in [0,1] range
- const double nT( (nStepCount-i-1)/double(nStepCount) );
-
for( p=0; p<nNumPoints; ++p )
{
const ::basegfx::B2DPoint& rOuterPoint( aOuterPoly.getB2DPoint(p) );
const ::basegfx::B2DPoint& rInnerPoint( aInnerPoly.getB2DPoint(p) );
aTempPoly[(USHORT)p] = ::Point(
- basegfx::fround( (1.0-nT)*rInnerPoint.getX() + nT*rOuterPoint.getX() ),
- basegfx::fround( (1.0-nT)*rInnerPoint.getY() + nT*rOuterPoint.getY() ) );
+ basegfx::fround( fT*rInnerPoint.getX() + (1-fT)*rOuterPoint.getX() ),
+ basegfx::fround( fT*rInnerPoint.getY() + (1-fT)*rOuterPoint.getY() ) );
}
// close polygon explicitely
@@ -549,46 +501,33 @@ namespace vclcanvas
void doGradientFill( OutputDevice& rOutDev,
const ::canvas::ParametricPolyPolygon::Values& rValues,
- const ::Color& rColor1,
- const ::Color& rColor2,
+ const std::vector< ::Color >& rColors,
const ::basegfx::B2DHomMatrix& rTextureTransform,
const ::Rectangle& rBounds,
- int nStepCount,
+ unsigned int nStepCount,
bool bFillNonOverlapping )
{
switch( rValues.meType )
{
case ::canvas::ParametricPolyPolygon::GRADIENT_LINEAR:
fillLinearGradient( rOutDev,
- rColor1,
- rColor2,
rTextureTransform,
rBounds,
nStepCount,
- bFillNonOverlapping );
- break;
-
- case ::canvas::ParametricPolyPolygon::GRADIENT_AXIAL:
- fillAxialGradient( rOutDev,
- rColor1,
- rColor2,
- rTextureTransform,
- rBounds,
- nStepCount,
- bFillNonOverlapping );
+ rValues,
+ rColors );
break;
case ::canvas::ParametricPolyPolygon::GRADIENT_ELLIPTICAL:
// FALLTHROUGH intended
case ::canvas::ParametricPolyPolygon::GRADIENT_RECTANGULAR:
fillPolygonalGradient( rOutDev,
- rValues,
- rColor1,
- rColor2,
rTextureTransform,
rBounds,
nStepCount,
- bFillNonOverlapping );
+ bFillNonOverlapping,
+ rValues,
+ rColors );
break;
default:
@@ -597,11 +536,19 @@ namespace vclcanvas
}
}
+ int numColorSteps( const ::Color& rColor1, const ::Color& rColor2 )
+ {
+ return ::std::max(
+ labs( rColor1.GetRed() - rColor2.GetRed() ),
+ ::std::max(
+ labs( rColor1.GetGreen() - rColor2.GetGreen() ),
+ labs( rColor1.GetBlue() - rColor2.GetBlue() ) ) );
+ }
+
bool gradientFill( OutputDevice& rOutDev,
OutputDevice* p2ndOutDev,
const ::canvas::ParametricPolyPolygon::Values& rValues,
- const ::Color& rColor1,
- const ::Color& rColor2,
+ const std::vector< ::Color >& rColors,
const PolyPolygon& rPoly,
const rendering::ViewState& viewState,
const rendering::RenderState& renderState,
@@ -646,12 +593,9 @@ namespace vclcanvas
// calc step size
// --------------
- const int nColorSteps(
- ::std::max(
- labs( rColor1.GetRed() - rColor2.GetRed() ),
- ::std::max(
- labs( rColor1.GetGreen() - rColor2.GetGreen() ),
- labs( rColor1.GetBlue() - rColor2.GetBlue() ) ) ) );
+ int nColorSteps = 0;
+ for( size_t i=0; i<rColors.size()-1; ++i )
+ nColorSteps += numColorSteps(rColors[i],rColors[i+1]);
// longest line in gradient bound rect
const int nGradientSize(
@@ -690,8 +634,7 @@ namespace vclcanvas
rOutDev.IntersectClipRegion( aPolygonDeviceRectOrig );
doGradientFill( rOutDev,
rValues,
- rColor1,
- rColor2,
+ rColors,
aTextureTransform,
aPolygonDeviceRectOrig,
nStepCount,
@@ -704,8 +647,7 @@ namespace vclcanvas
p2ndOutDev->IntersectClipRegion( aPolygonDeviceRectOrig );
doGradientFill( *p2ndOutDev,
rValues,
- rColor1,
- rColor2,
+ rColors,
aTextureTransform,
aPolygonDeviceRectOrig,
nStepCount,
@@ -723,8 +665,7 @@ namespace vclcanvas
doGradientFill( rOutDev,
rValues,
- rColor1,
- rColor2,
+ rColors,
aTextureTransform,
aPolygonDeviceRectOrig,
nStepCount,
@@ -737,8 +678,7 @@ namespace vclcanvas
p2ndOutDev->SetClipRegion( aPolyClipRegion );
doGradientFill( *p2ndOutDev,
rValues,
- rColor1,
- rColor2,
+ rColors,
aTextureTransform,
aPolygonDeviceRectOrig,
nStepCount,
@@ -753,8 +693,7 @@ namespace vclcanvas
rOutDev.SetRasterOp( ROP_XOR );
doGradientFill( rOutDev,
rValues,
- rColor1,
- rColor2,
+ rColors,
aTextureTransform,
aPolygonDeviceRectOrig,
nStepCount,
@@ -765,8 +704,7 @@ namespace vclcanvas
rOutDev.SetRasterOp( ROP_XOR );
doGradientFill( rOutDev,
rValues,
- rColor1,
- rColor2,
+ rColors,
aTextureTransform,
aPolygonDeviceRectOrig,
nStepCount,
@@ -779,8 +717,7 @@ namespace vclcanvas
p2ndOutDev->SetRasterOp( ROP_XOR );
doGradientFill( *p2ndOutDev,
rValues,
- rColor1,
- rColor2,
+ rColors,
aTextureTransform,
aPolygonDeviceRectOrig,
nStepCount,
@@ -791,8 +728,7 @@ namespace vclcanvas
p2ndOutDev->SetRasterOp( ROP_XOR );
doGradientFill( *p2ndOutDev,
rValues,
- rColor1,
- rColor2,
+ rColors,
aTextureTransform,
aPolygonDeviceRectOrig,
nStepCount,
@@ -855,33 +791,41 @@ namespace vclcanvas
::canvas::ParametricPolyPolygon* pGradient =
dynamic_cast< ::canvas::ParametricPolyPolygon* >( textures[0].Gradient.get() );
- if( pGradient )
+ if( pGradient && pGradient->getValues().maColors.getLength() )
{
// copy state from Gradient polypoly locally
// (given object might change!)
const ::canvas::ParametricPolyPolygon::Values& rValues(
pGradient->getValues() );
- // TODO: use all the colors and place them on given positions/stops
- const ::Color aColor1(
- ::vcl::unotools::stdColorSpaceSequenceToColor(
- rValues.maColors [0] ) );
- const ::Color aColor2(
- ::vcl::unotools::stdColorSpaceSequenceToColor(
- rValues.maColors [rValues.maColors.getLength () - 1] ) );
-
- // TODO(E1): Return value
- // TODO(F1): FillRule
- gradientFill( mpOutDev->getOutDev(),
- mp2ndOutDev.get() ? &mp2ndOutDev->getOutDev() : (OutputDevice*)NULL,
- rValues,
- aColor1,
- aColor2,
- aPolyPoly,
- viewState,
- renderState,
- textures[0],
- nTransparency );
+ if( rValues.maColors.getLength() < 2 )
+ {
+ rendering::RenderState aTempState=renderState;
+ aTempState.DeviceColor = rValues.maColors[0];
+ fillPolyPolygon(pCanvas, xPolyPolygon, viewState, aTempState);
+ }
+ else
+ {
+ std::vector< ::Color > aColors(rValues.maColors.getLength());
+ std::transform(&rValues.maColors[0],
+ &rValues.maColors[0]+rValues.maColors.getLength(),
+ aColors.begin(),
+ boost::bind(
+ &vcl::unotools::stdColorSpaceSequenceToColor,
+ _1));
+
+ // TODO(E1): Return value
+ // TODO(F1): FillRule
+ gradientFill( mpOutDev->getOutDev(),
+ mp2ndOutDev.get() ? &mp2ndOutDev->getOutDev() : (OutputDevice*)NULL,
+ rValues,
+ aColors,
+ aPolyPoly,
+ viewState,
+ renderState,
+ textures[0],
+ nTransparency );
+ }
}
else
{
diff --git a/canvas/source/vcl/spritecanvas.hxx b/canvas/source/vcl/spritecanvas.hxx
index 545eeeed4577..66762bcb65b7 100644
--- a/canvas/source/vcl/spritecanvas.hxx
+++ b/canvas/source/vcl/spritecanvas.hxx
@@ -42,7 +42,6 @@
#include <com/sun/star/rendering/XIntegerBitmap.hpp>
#include <com/sun/star/rendering/XGraphicDevice.hpp>
#include <com/sun/star/rendering/XBufferController.hpp>
-#include <com/sun/star/rendering/XParametricPolyPolygon2DFactory.hpp>
#include <cppuhelper/compbase9.hxx>
#include <comphelper/uno3.hxx>
@@ -65,7 +64,7 @@ namespace vclcanvas
typedef ::cppu::WeakComponentImplHelper9< ::com::sun::star::rendering::XSpriteCanvas,
::com::sun::star::rendering::XIntegerBitmap,
::com::sun::star::rendering::XGraphicDevice,
- ::com::sun::star::rendering::XParametricPolyPolygon2DFactory,
+ ::com::sun::star::lang::XMultiServiceFactory,
::com::sun::star::rendering::XBufferController,
::com::sun::star::awt::XWindowListener,
::com::sun::star::util::XUpdatable,