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:
Chris Sherlock
2014-04-06 20:57:49 +10:00
parent 3b6dba357f
commit 8659d189ec
4 changed files with 152 additions and 123 deletions

View File

@@ -844,6 +844,12 @@ protected:
virtual void EmulateDrawTransparent( const PolyPolygon& rPolyPoly, sal_uInt16 nTransparencePercent );
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:
typedef void ( OutputDevice::* FontUpdateHandler_t )( bool );

View File

@@ -274,7 +274,10 @@ public:
protected:
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;
void ScaleBitmap ( Bitmap&, SalTwoRect& ) SAL_OVERRIDE { };
public:

View File

@@ -660,9 +660,9 @@ void OutputDevice::DrawGradient( const Rectangle& rRect,
aGradient.SetSteps( GRADIENT_DEFAULT_STEPCOUNT );
if( aGradient.GetStyle() == GradientStyle_LINEAR || aGradient.GetStyle() == GradientStyle_AXIAL )
ImplDrawLinearGradient( aRect, aGradient, false, NULL );
ImplDrawLinearGradient( aRect, rGradient, false, NULL );
else
ImplDrawComplexGradient( aRect, aGradient, false, NULL );
ImplDrawComplexGradient( aRect, rGradient, false, NULL );
}
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,
const Gradient& rGradient )
{
@@ -719,34 +842,12 @@ void OutputDevice::DrawGradient( const PolyPolygon& rPolyPoly,
if( mpMetaFile )
{
const Rectangle aRect( rPolyPoly.GetBoundRect() );
const Rectangle aBoundRect( rPolyPoly.GetBoundRect() );
mpMetaFile->AddAction( new MetaCommentAction( "XGRAD_SEQ_BEGIN" ) );
mpMetaFile->AddAction( new MetaGradientExAction( rPolyPoly, rGradient ) );
if( OUTDEV_PRINTER == meOutDevType )
{
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 );
}
ClipGradientMetafile ( rGradient, rPolyPoly, aBoundRect );
mpMetaFile->AddAction( new MetaCommentAction( "XGRAD_SEQ_END" ) );
}
@@ -783,103 +884,7 @@ void OutputDevice::DrawGradient( const PolyPolygon& rPolyPoly,
aGradient.SetEndColor( aEndCol );
}
if( OUTDEV_PRINTER == meOutDevType || ImplGetSVData()->maGDIData.mbNoXORClipping )
{
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 );
}
}
}
ClipGradientToBounds ( aGradient, rPolyPoly );
}
if( mpAlphaVDev )

View File

@@ -1812,4 +1812,19 @@ bool Printer::UsePolyPolygonForComplexGradient()
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: */