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 );
|
||||
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 );
|
||||
|
||||
|
@@ -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:
|
||||
|
@@ -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 )
|
||||
|
@@ -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: */
|
||||
|
Reference in New Issue
Block a user