fdo#74702 Refactor gradient clipping functions
There are two gradient clipping functions: one uses a normal intersection to get the symmetric difference to clip the gradient - this is used by OS X and when printing. The other uses XOR clipping, which is an elegant trick to implement complex clipping on graphics systems that have minimal capabilities. cf. http://www.openoffice.org/marketing/ooocon2008/programme/wednesday_1401.pdf Change-Id: Iab16258c8e758c41a29337525927ba780329e887 Reviewed-on: https://gerrit.libreoffice.org/8873 Tested-by: LibreOffice gerrit bot <gerrit@libreoffice.org> Reviewed-by: Chris Sherlock <chris.sherlock79@gmail.com> Tested-by: Chris Sherlock <chris.sherlock79@gmail.com>
This commit is contained in:
@@ -844,6 +844,12 @@ protected:
|
|||||||
virtual void EmulateDrawTransparent( const PolyPolygon& rPolyPoly, sal_uInt16 nTransparencePercent );
|
virtual void EmulateDrawTransparent( const PolyPolygon& rPolyPoly, sal_uInt16 nTransparencePercent );
|
||||||
void DrawInvisiblePolygon( const PolyPolygon& rPolyPoly );
|
void DrawInvisiblePolygon( const PolyPolygon& rPolyPoly );
|
||||||
|
|
||||||
|
virtual void ClipGradientToBounds( Gradient &rGradient, const PolyPolygon &rPolyPoly );
|
||||||
|
void ClipGradient( Gradient &rGradient, const PolyPolygon &rPolyPoly, const Rectangle &rBoundRect );
|
||||||
|
void XORClipGradient( Gradient &rGradient, const PolyPolygon &rPolyPoly, const Rectangle &rBoundRect );
|
||||||
|
|
||||||
|
virtual void ClipGradientMetafile ( const Gradient &rGradient, const PolyPolygon &rPolyPoly, const Rectangle &rBoundRect );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef void ( OutputDevice::* FontUpdateHandler_t )( bool );
|
typedef void ( OutputDevice::* FontUpdateHandler_t )( bool );
|
||||||
|
|
||||||
|
@@ -274,7 +274,10 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
long ImplGetGradientStepCount( long nMinRect ) SAL_OVERRIDE;
|
long ImplGetGradientStepCount( long nMinRect ) SAL_OVERRIDE;
|
||||||
|
virtual void ClipGradientToBounds( Gradient &rGradient, const PolyPolygon &rPolyPoly ) SAL_OVERRIDE;
|
||||||
|
virtual void ClipGradientMetafile ( const Gradient &rGradient, const PolyPolygon &rPolyPoly, const Rectangle &rBoundRect ) SAL_OVERRIDE;
|
||||||
virtual bool UsePolyPolygonForComplexGradient() SAL_OVERRIDE;
|
virtual bool UsePolyPolygonForComplexGradient() SAL_OVERRIDE;
|
||||||
|
|
||||||
void ScaleBitmap ( Bitmap&, SalTwoRect& ) SAL_OVERRIDE { };
|
void ScaleBitmap ( Bitmap&, SalTwoRect& ) SAL_OVERRIDE { };
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@@ -660,9 +660,9 @@ void OutputDevice::DrawGradient( const Rectangle& rRect,
|
|||||||
aGradient.SetSteps( GRADIENT_DEFAULT_STEPCOUNT );
|
aGradient.SetSteps( GRADIENT_DEFAULT_STEPCOUNT );
|
||||||
|
|
||||||
if( aGradient.GetStyle() == GradientStyle_LINEAR || aGradient.GetStyle() == GradientStyle_AXIAL )
|
if( aGradient.GetStyle() == GradientStyle_LINEAR || aGradient.GetStyle() == GradientStyle_AXIAL )
|
||||||
ImplDrawLinearGradient( aRect, aGradient, false, NULL );
|
ImplDrawLinearGradient( aRect, rGradient, false, NULL );
|
||||||
else
|
else
|
||||||
ImplDrawComplexGradient( aRect, aGradient, false, NULL );
|
ImplDrawComplexGradient( aRect, rGradient, false, NULL );
|
||||||
}
|
}
|
||||||
|
|
||||||
Pop();
|
Pop();
|
||||||
@@ -675,6 +675,129 @@ void OutputDevice::DrawGradient( const Rectangle& rRect,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OutputDevice::ClipGradientMetafile ( const Gradient &rGradient, const PolyPolygon &rPolyPoly, const Rectangle &rBoundRect )
|
||||||
|
{
|
||||||
|
const bool bOldOutput = IsOutputEnabled();
|
||||||
|
|
||||||
|
EnableOutput( false );
|
||||||
|
Push( PUSH_RASTEROP );
|
||||||
|
SetRasterOp( ROP_XOR );
|
||||||
|
DrawGradient( rBoundRect, rGradient );
|
||||||
|
SetFillColor( COL_BLACK );
|
||||||
|
SetRasterOp( ROP_0 );
|
||||||
|
DrawPolyPolygon( rPolyPoly );
|
||||||
|
SetRasterOp( ROP_XOR );
|
||||||
|
DrawGradient( rBoundRect, rGradient );
|
||||||
|
Pop();
|
||||||
|
EnableOutput( bOldOutput );
|
||||||
|
}
|
||||||
|
|
||||||
|
void OutputDevice::ClipGradientToBounds ( Gradient &rGradient, const PolyPolygon &rPolyPoly )
|
||||||
|
{
|
||||||
|
const Rectangle aBoundRect( rPolyPoly.GetBoundRect() );
|
||||||
|
|
||||||
|
if( ImplGetSVData()->maGDIData.mbNoXORClipping )
|
||||||
|
ClipGradient ( rGradient, rPolyPoly, aBoundRect );
|
||||||
|
else
|
||||||
|
XORClipGradient ( rGradient, rPolyPoly, aBoundRect );
|
||||||
|
}
|
||||||
|
|
||||||
|
void OutputDevice::ClipGradient ( Gradient &rGradient, const PolyPolygon &rPolyPoly, const Rectangle &rBoundRect )
|
||||||
|
{
|
||||||
|
if( !Rectangle( PixelToLogic( Point() ), GetOutputSize() ).IsEmpty() )
|
||||||
|
{
|
||||||
|
// convert rectangle to pixels
|
||||||
|
Rectangle aRect( ImplLogicToDevicePixel( rBoundRect ) );
|
||||||
|
aRect.Justify();
|
||||||
|
|
||||||
|
// do nothing if the rectangle is empty
|
||||||
|
if ( !aRect.IsEmpty() )
|
||||||
|
{
|
||||||
|
if( !mpGraphics && !ImplGetGraphics() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( mbInitClipRegion )
|
||||||
|
ImplInitClipRegion();
|
||||||
|
|
||||||
|
if( !mbOutputClipped )
|
||||||
|
{
|
||||||
|
PolyPolygon aClipPolyPoly( ImplLogicToDevicePixel( rPolyPoly ) );
|
||||||
|
|
||||||
|
// draw gradients without border
|
||||||
|
if( mbLineColor || mbInitLineColor )
|
||||||
|
{
|
||||||
|
mpGraphics->SetLineColor();
|
||||||
|
mbInitLineColor = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
mbInitFillColor = true;
|
||||||
|
|
||||||
|
// calculate step count if necessary
|
||||||
|
if ( !rGradient.GetSteps() )
|
||||||
|
rGradient.SetSteps( GRADIENT_DEFAULT_STEPCOUNT );
|
||||||
|
|
||||||
|
if( rGradient.GetStyle() == GradientStyle_LINEAR || rGradient.GetStyle() == GradientStyle_AXIAL )
|
||||||
|
ImplDrawLinearGradient( aRect, rGradient, false, &aClipPolyPoly );
|
||||||
|
else
|
||||||
|
ImplDrawComplexGradient( aRect, rGradient, false, &aClipPolyPoly );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OutputDevice::XORClipGradient ( Gradient &rGradient, const PolyPolygon &rPolyPoly, const Rectangle &rBoundRect )
|
||||||
|
{
|
||||||
|
const PolyPolygon aPolyPoly( LogicToPixel( rPolyPoly ) );
|
||||||
|
Point aPoint;
|
||||||
|
Rectangle aDstRect( aPoint, GetOutputSizePixel() );
|
||||||
|
|
||||||
|
aDstRect.Intersection( rBoundRect );
|
||||||
|
|
||||||
|
ClipToPaintRegion( aDstRect );
|
||||||
|
|
||||||
|
if( !aDstRect.IsEmpty() )
|
||||||
|
{
|
||||||
|
boost::scoped_ptr<VirtualDevice> pVDev;
|
||||||
|
const Size aDstSize( aDstRect.GetSize() );
|
||||||
|
|
||||||
|
if( HasAlpha() )
|
||||||
|
{
|
||||||
|
// #110958# Pay attention to alpha VDevs here, otherwise,
|
||||||
|
// background will be wrong: Temp VDev has to have alpha, too.
|
||||||
|
pVDev.reset(new VirtualDevice( *this, 0, GetAlphaBitCount() > 1 ? 0 : 1 ));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// nothing special here. Plain VDev
|
||||||
|
pVDev.reset(new VirtualDevice());
|
||||||
|
}
|
||||||
|
|
||||||
|
if( pVDev->SetOutputSizePixel( aDstSize) )
|
||||||
|
{
|
||||||
|
MapMode aVDevMap;
|
||||||
|
const bool bOldMap = mbMap;
|
||||||
|
|
||||||
|
EnableMapMode( false );
|
||||||
|
|
||||||
|
pVDev->DrawOutDev( Point(), aDstSize, aDstRect.TopLeft(), aDstSize, *this );
|
||||||
|
pVDev->SetRasterOp( ROP_XOR );
|
||||||
|
aVDevMap.SetOrigin( Point( -aDstRect.Left(), -aDstRect.Top() ) );
|
||||||
|
pVDev->SetMapMode( aVDevMap );
|
||||||
|
pVDev->DrawGradient( rBoundRect, rGradient );
|
||||||
|
pVDev->SetFillColor( COL_BLACK );
|
||||||
|
pVDev->SetRasterOp( ROP_0 );
|
||||||
|
pVDev->DrawPolyPolygon( aPolyPoly );
|
||||||
|
pVDev->SetRasterOp( ROP_XOR );
|
||||||
|
pVDev->DrawGradient( rBoundRect, rGradient );
|
||||||
|
aVDevMap.SetOrigin( Point() );
|
||||||
|
pVDev->SetMapMode( aVDevMap );
|
||||||
|
DrawOutDev( aDstRect.TopLeft(), aDstSize, Point(), aDstSize, *pVDev );
|
||||||
|
|
||||||
|
EnableMapMode( bOldMap );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void OutputDevice::DrawGradient( const PolyPolygon& rPolyPoly,
|
void OutputDevice::DrawGradient( const PolyPolygon& rPolyPoly,
|
||||||
const Gradient& rGradient )
|
const Gradient& rGradient )
|
||||||
{
|
{
|
||||||
@@ -719,34 +842,12 @@ void OutputDevice::DrawGradient( const PolyPolygon& rPolyPoly,
|
|||||||
|
|
||||||
if( mpMetaFile )
|
if( mpMetaFile )
|
||||||
{
|
{
|
||||||
const Rectangle aRect( rPolyPoly.GetBoundRect() );
|
const Rectangle aBoundRect( rPolyPoly.GetBoundRect() );
|
||||||
|
|
||||||
mpMetaFile->AddAction( new MetaCommentAction( "XGRAD_SEQ_BEGIN" ) );
|
mpMetaFile->AddAction( new MetaCommentAction( "XGRAD_SEQ_BEGIN" ) );
|
||||||
mpMetaFile->AddAction( new MetaGradientExAction( rPolyPoly, rGradient ) );
|
mpMetaFile->AddAction( new MetaGradientExAction( rPolyPoly, rGradient ) );
|
||||||
|
|
||||||
if( OUTDEV_PRINTER == meOutDevType )
|
ClipGradientMetafile ( rGradient, rPolyPoly, aBoundRect );
|
||||||
{
|
|
||||||
Push( PUSH_CLIPREGION );
|
|
||||||
IntersectClipRegion(Region(rPolyPoly));
|
|
||||||
DrawGradient( aRect, rGradient );
|
|
||||||
Pop();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const bool bOldOutput = IsOutputEnabled();
|
|
||||||
|
|
||||||
EnableOutput( false );
|
|
||||||
Push( PUSH_RASTEROP );
|
|
||||||
SetRasterOp( ROP_XOR );
|
|
||||||
DrawGradient( aRect, rGradient );
|
|
||||||
SetFillColor( COL_BLACK );
|
|
||||||
SetRasterOp( ROP_0 );
|
|
||||||
DrawPolyPolygon( rPolyPoly );
|
|
||||||
SetRasterOp( ROP_XOR );
|
|
||||||
DrawGradient( aRect, rGradient );
|
|
||||||
Pop();
|
|
||||||
EnableOutput( bOldOutput );
|
|
||||||
}
|
|
||||||
|
|
||||||
mpMetaFile->AddAction( new MetaCommentAction( "XGRAD_SEQ_END" ) );
|
mpMetaFile->AddAction( new MetaCommentAction( "XGRAD_SEQ_END" ) );
|
||||||
}
|
}
|
||||||
@@ -783,103 +884,7 @@ void OutputDevice::DrawGradient( const PolyPolygon& rPolyPoly,
|
|||||||
aGradient.SetEndColor( aEndCol );
|
aGradient.SetEndColor( aEndCol );
|
||||||
}
|
}
|
||||||
|
|
||||||
if( OUTDEV_PRINTER == meOutDevType || ImplGetSVData()->maGDIData.mbNoXORClipping )
|
ClipGradientToBounds ( aGradient, rPolyPoly );
|
||||||
{
|
|
||||||
const Rectangle aBoundRect( rPolyPoly.GetBoundRect() );
|
|
||||||
|
|
||||||
if( !Rectangle( PixelToLogic( Point() ), GetOutputSize() ).IsEmpty() )
|
|
||||||
{
|
|
||||||
// convert rectangle to pixels
|
|
||||||
Rectangle aRect( ImplLogicToDevicePixel( aBoundRect ) );
|
|
||||||
aRect.Justify();
|
|
||||||
|
|
||||||
// do nothing if the rectangle is empty
|
|
||||||
if ( !aRect.IsEmpty() )
|
|
||||||
{
|
|
||||||
if( !mpGraphics && !ImplGetGraphics() )
|
|
||||||
return;
|
|
||||||
|
|
||||||
if( mbInitClipRegion )
|
|
||||||
ImplInitClipRegion();
|
|
||||||
|
|
||||||
if( !mbOutputClipped )
|
|
||||||
{
|
|
||||||
PolyPolygon aClipPolyPoly( ImplLogicToDevicePixel( rPolyPoly ) );
|
|
||||||
|
|
||||||
// draw gradients without border
|
|
||||||
if( mbLineColor || mbInitLineColor )
|
|
||||||
{
|
|
||||||
mpGraphics->SetLineColor();
|
|
||||||
mbInitLineColor = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
mbInitFillColor = true;
|
|
||||||
|
|
||||||
// calculate step count if necessary
|
|
||||||
if ( !aGradient.GetSteps() )
|
|
||||||
aGradient.SetSteps( GRADIENT_DEFAULT_STEPCOUNT );
|
|
||||||
|
|
||||||
if( aGradient.GetStyle() == GradientStyle_LINEAR || aGradient.GetStyle() == GradientStyle_AXIAL )
|
|
||||||
ImplDrawLinearGradient( aRect, aGradient, false, &aClipPolyPoly );
|
|
||||||
else
|
|
||||||
ImplDrawComplexGradient( aRect, aGradient, false, &aClipPolyPoly );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const PolyPolygon aPolyPoly( LogicToPixel( rPolyPoly ) );
|
|
||||||
const Rectangle aBoundRect( aPolyPoly.GetBoundRect() );
|
|
||||||
Point aPoint;
|
|
||||||
Rectangle aDstRect( aPoint, GetOutputSizePixel() );
|
|
||||||
|
|
||||||
aDstRect.Intersection( aBoundRect );
|
|
||||||
|
|
||||||
ClipToPaintRegion( aDstRect );
|
|
||||||
|
|
||||||
if( !aDstRect.IsEmpty() )
|
|
||||||
{
|
|
||||||
boost::scoped_ptr<VirtualDevice> pVDev;
|
|
||||||
const Size aDstSize( aDstRect.GetSize() );
|
|
||||||
|
|
||||||
if( HasAlpha() )
|
|
||||||
{
|
|
||||||
// #110958# Pay attention to alpha VDevs here, otherwise,
|
|
||||||
// background will be wrong: Temp VDev has to have alpha, too.
|
|
||||||
pVDev.reset(new VirtualDevice( *this, 0, GetAlphaBitCount() > 1 ? 0 : 1 ));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// nothing special here. Plain VDev
|
|
||||||
pVDev.reset(new VirtualDevice());
|
|
||||||
}
|
|
||||||
|
|
||||||
if( pVDev->SetOutputSizePixel( aDstSize) )
|
|
||||||
{
|
|
||||||
MapMode aVDevMap;
|
|
||||||
const bool bOldMap = mbMap;
|
|
||||||
|
|
||||||
EnableMapMode( false );
|
|
||||||
|
|
||||||
pVDev->DrawOutDev( Point(), aDstSize, aDstRect.TopLeft(), aDstSize, *this );
|
|
||||||
pVDev->SetRasterOp( ROP_XOR );
|
|
||||||
aVDevMap.SetOrigin( Point( -aDstRect.Left(), -aDstRect.Top() ) );
|
|
||||||
pVDev->SetMapMode( aVDevMap );
|
|
||||||
pVDev->DrawGradient( aBoundRect, aGradient );
|
|
||||||
pVDev->SetFillColor( COL_BLACK );
|
|
||||||
pVDev->SetRasterOp( ROP_0 );
|
|
||||||
pVDev->DrawPolyPolygon( aPolyPoly );
|
|
||||||
pVDev->SetRasterOp( ROP_XOR );
|
|
||||||
pVDev->DrawGradient( aBoundRect, aGradient );
|
|
||||||
aVDevMap.SetOrigin( Point() );
|
|
||||||
pVDev->SetMapMode( aVDevMap );
|
|
||||||
DrawOutDev( aDstRect.TopLeft(), aDstSize, Point(), aDstSize, *pVDev );
|
|
||||||
|
|
||||||
EnableMapMode( bOldMap );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if( mpAlphaVDev )
|
if( mpAlphaVDev )
|
||||||
|
@@ -1812,4 +1812,19 @@ bool Printer::UsePolyPolygonForComplexGradient()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Printer::ClipGradientToBounds ( Gradient &rGradient, const PolyPolygon &rPolyPoly )
|
||||||
|
{
|
||||||
|
const Rectangle aBoundRect( rPolyPoly.GetBoundRect() );
|
||||||
|
|
||||||
|
ClipGradient ( rGradient, rPolyPoly, aBoundRect );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Printer::ClipGradientMetafile ( const Gradient &rGradient, const PolyPolygon &rPolyPoly, const Rectangle &rBoundRect )
|
||||||
|
{
|
||||||
|
Push( PUSH_CLIPREGION );
|
||||||
|
IntersectClipRegion(Region(rPolyPoly));
|
||||||
|
DrawGradient( rBoundRect, rGradient );
|
||||||
|
Pop();
|
||||||
|
}
|
||||||
|
|
||||||
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|
||||||
|
Reference in New Issue
Block a user