Move OutputDevice gradient funcs to vcl/source/gdi/outdev/gradient.cxx
Change-Id: I035e5abd948881313580726c33e66af30778f6a6
diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk
index 96bf383..61405b3 100644
--- a/vcl/Library_vcl.mk
+++ b/vcl/Library_vcl.mk
@@ -237,6 +237,7 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\
vcl/source/gdi/outdev/outdev2 \
vcl/source/gdi/outdev/outdev3 \
vcl/source/gdi/outdev/outdev4 \
vcl/source/gdi/outdev/gradient \
vcl/source/gdi/outdev/outdev5 \
vcl/source/gdi/outdev/outdev6 \
vcl/source/gdi/outdev/outdev \
diff --git a/vcl/source/gdi/outdev/gradient.cxx b/vcl/source/gdi/outdev/gradient.cxx
new file mode 100644
index 0000000..f9889a65
--- /dev/null
+++ b/vcl/source/gdi/outdev/gradient.cxx
@@ -0,0 +1,695 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <tools/debug.hxx>
#include <tools/line.hxx>
#include <tools/poly.hxx>
#include <vcl/gradient.hxx>
#include <vcl/metaact.hxx>
#include <vcl/gdimtf.hxx>
#include <vcl/salbtype.hxx>
#include <vcl/virdev.hxx>
#include <vcl/outdev.hxx>
#include <vcl/settings.hxx>
#include "window.h"
#include "salframe.hxx"
#include "salgdi.hxx"
#include "svdata.hxx"
#include "outdata.hxx"
#include <basegfx/polygon/b2dpolygon.hxx>
#include <basegfx/polygon/b2dpolypolygon.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx>
#include <boost/scoped_array.hpp>
#include <boost/scoped_ptr.hpp>
#define GRADIENT_DEFAULT_STEPCOUNT 0
void OutputDevice::DrawGradient( const Rectangle& rRect,
const Gradient& rGradient )
{
// Convert rectangle to a PolyPolygon by first converting to a Polygon
Polygon aPolygon ( rRect );
PolyPolygon aPolyPoly ( aPolygon );
DrawGradient ( aPolyPoly, rGradient );
}
void OutputDevice::DrawGradient( const PolyPolygon& rPolyPoly,
const Gradient& rGradient )
{
if ( mnDrawMode & DRAWMODE_NOGRADIENT )
return; // nothing to draw!
if ( mbInitClipRegion )
ImplInitClipRegion();
if ( mbOutputClipped )
return;
if ( rPolyPoly.Count() && rPolyPoly[ 0 ].GetSize() )
{
if ( mnDrawMode & ( DRAWMODE_BLACKGRADIENT | DRAWMODE_WHITEGRADIENT | DRAWMODE_SETTINGSGRADIENT) )
{
Color aColor = GetSingleColorGradientFill();
Push( PUSH_LINECOLOR | PUSH_FILLCOLOR );
SetLineColor( aColor );
SetFillColor( aColor );
DrawPolyPolygon( rPolyPoly );
Pop();
return;
}
Gradient aGradient( rGradient );
if ( mnDrawMode & ( DRAWMODE_GRAYGRADIENT | DRAWMODE_GHOSTEDGRADIENT ) )
{
SetGrayscaleColors( aGradient );
}
if( mpMetaFile )
{
const Rectangle aBoundRect( rPolyPoly.GetBoundRect() );
if ( rPolyPoly.IsRect() )
{
mpMetaFile->AddAction( new MetaGradientAction( aBoundRect, aGradient ) );
}
else
{
mpMetaFile->AddAction( new MetaCommentAction( "XGRAD_SEQ_BEGIN" ) );
mpMetaFile->AddAction( new MetaGradientExAction( rPolyPoly, rGradient ) );
Push( PUSH_CLIPREGION );
IntersectClipRegion(Region(rPolyPoly));
DrawGradient( aBoundRect, rGradient );
Pop();
mpMetaFile->AddAction( new MetaCommentAction( "XGRAD_SEQ_END" ) );
}
}
if( !IsDeviceOutputNecessary() || ImplIsRecordLayout() )
return;
// Clip and then draw the gradient
if( !Rectangle( PixelToLogic( Point() ), GetOutputSize() ).IsEmpty() )
{
const Rectangle aBoundRect( rPolyPoly.GetBoundRect() );
// convert rectangle to pixels
Rectangle aRect( ImplLogicToDevicePixel( aBoundRect ) );
aRect.Justify();
// do nothing if the rectangle is empty
if ( !aRect.IsEmpty() )
{
if( !mpGraphics && !ImplGetGraphics() )
return;
// secure clip region
Push( PUSH_CLIPREGION );
IntersectClipRegion( aBoundRect );
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 ( rPolyPoly.IsRect() )
{
// because we draw with no border line, we have to expand gradient
// rect to avoid missing lines on the right and bottom edge
aRect.Left()--;
aRect.Top()--;
aRect.Right()++;
aRect.Bottom()++;
}
// if the clipping polypolygon is a rectangle, then it's the same size as the bounding of the
// polypolygon, so pass in a NULL for the clipping parameter
if( aGradient.GetStyle() == GradientStyle_LINEAR || rGradient.GetStyle() == GradientStyle_AXIAL )
ImplDrawLinearGradient( aRect, aGradient, false, aClipPolyPoly.IsRect() ? NULL : &aClipPolyPoly );
else
ImplDrawComplexGradient( aRect, aGradient, false, aClipPolyPoly.IsRect() ? NULL : &aClipPolyPoly );
}
Pop();
}
}
}
if( mpAlphaVDev )
mpAlphaVDev->DrawPolyPolygon( rPolyPoly );
}
inline sal_uInt8 ImplGetGradientColorValue( long nValue )
{
if ( nValue < 0 )
return 0;
else if ( nValue > 0xFF )
return 0xFF;
else
return (sal_uInt8)nValue;
}
long OutputDevice::ImplGetGradientStepCount( long nMinRect )
{
long nInc = (nMinRect < 50) ? 2 : 4;
return nInc;
}
long OutputDevice::ImplGetGradientSteps( const Gradient& rGradient, const Rectangle& rRect, bool bMtf, bool bComplex )
{
// calculate step count
long nStepCount = rGradient.GetSteps();
long nMinRect;
// generate nStepCount, if not passed
if (bComplex)
nMinRect = std::min( rRect.GetWidth(), rRect.GetHeight() );
else
nMinRect = rRect.GetHeight();
if ( !nStepCount )
{
long nInc;
nInc = ImplGetGradientStepCount (nMinRect);
if ( !nInc || bMtf )
nInc = 1;
nStepCount = nMinRect / nInc;
}
return nStepCount;
}
void OutputDevice::ImplDrawLinearGradient( const Rectangle& rRect,
const Gradient& rGradient,
bool bMtf, const PolyPolygon* pClipPolyPoly )
{
// get BoundRect of rotated rectangle
Rectangle aRect;
Point aCenter;
sal_uInt16 nAngle = rGradient.GetAngle() % 3600;
rGradient.GetBoundRect( rRect, aRect, aCenter );
bool bLinear = (rGradient.GetStyle() == GradientStyle_LINEAR);
double fBorder = rGradient.GetBorder() * aRect.GetHeight() / 100.0;
if ( !bLinear )
{
fBorder /= 2.0;
}
Rectangle aMirrorRect = aRect; // used in style axial
aMirrorRect.Top() = ( aRect.Top() + aRect.Bottom() ) / 2;
if ( !bLinear )
{
aRect.Bottom() = aMirrorRect.Top();
}
// colour-intensities of start- and finish; change if needed
long nFactor;
Color aStartCol = rGradient.GetStartColor();
Color aEndCol = rGradient.GetEndColor();
long nStartRed = aStartCol.GetRed();
long nStartGreen = aStartCol.GetGreen();
long nStartBlue = aStartCol.GetBlue();
long nEndRed = aEndCol.GetRed();
long nEndGreen = aEndCol.GetGreen();
long nEndBlue = aEndCol.GetBlue();
nFactor = rGradient.GetStartIntensity();
nStartRed = (nStartRed * nFactor) / 100;
nStartGreen = (nStartGreen * nFactor) / 100;
nStartBlue = (nStartBlue * nFactor) / 100;
nFactor = rGradient.GetEndIntensity();
nEndRed = (nEndRed * nFactor) / 100;
nEndGreen = (nEndGreen * nFactor) / 100;
nEndBlue = (nEndBlue * nFactor) / 100;
// gradient style axial has exchanged start and end colors
if ( !bLinear)
{
long nTempColor = nStartRed;
nStartRed = nEndRed;
nEndRed = nTempColor;
nTempColor = nStartGreen;
nStartGreen = nEndGreen;
nEndGreen = nTempColor;
nTempColor = nStartBlue;
nStartBlue = nEndBlue;
nEndBlue = nTempColor;
}
sal_uInt8 nRed;
sal_uInt8 nGreen;
sal_uInt8 nBlue;
// Create border
Rectangle aBorderRect = aRect;
Polygon aPoly( 4 );
if (fBorder > 0.0)
{
nRed = (sal_uInt8)nStartRed;
nGreen = (sal_uInt8)nStartGreen;
nBlue = (sal_uInt8)nStartBlue;
if ( bMtf )
mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
else
mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
aBorderRect.Bottom() = (long)( aBorderRect.Top() + fBorder );
aRect.Top() = aBorderRect.Bottom();
aPoly[0] = aBorderRect.TopLeft();
aPoly[1] = aBorderRect.TopRight();
aPoly[2] = aBorderRect.BottomRight();
aPoly[3] = aBorderRect.BottomLeft();
aPoly.Rotate( aCenter, nAngle );
if ( bMtf )
mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
else
ImplDrawPolygon( aPoly, pClipPolyPoly );
if ( !bLinear)
{
aBorderRect = aMirrorRect;
aBorderRect.Top() = (long) ( aBorderRect.Bottom() - fBorder );
aMirrorRect.Bottom() = aBorderRect.Top();
aPoly[0] = aBorderRect.TopLeft();
aPoly[1] = aBorderRect.TopRight();
aPoly[2] = aBorderRect.BottomRight();
aPoly[3] = aBorderRect.BottomLeft();
aPoly.Rotate( aCenter, nAngle );
if ( bMtf )
mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
else
ImplDrawPolygon( aPoly, pClipPolyPoly );
}
}
// calculate step count
long nStepCount = ImplGetGradientSteps( rGradient, aRect, bMtf );
// minimal three steps and maximal as max color steps
long nAbsRedSteps = std::abs( nEndRed - nStartRed );
long nAbsGreenSteps = std::abs( nEndGreen - nStartGreen );
long nAbsBlueSteps = std::abs( nEndBlue - nStartBlue );
long nMaxColorSteps = std::max( nAbsRedSteps , nAbsGreenSteps );
nMaxColorSteps = std::max( nMaxColorSteps, nAbsBlueSteps );
long nSteps = std::min( nStepCount, nMaxColorSteps );
if ( nSteps < 3)
{
nSteps = 3;
}
double fScanInc = ((double)aRect.GetHeight()) / (double) nSteps;
double fGradientLine = (double)aRect.Top();
double fMirrorGradientLine = (double) aMirrorRect.Bottom();
double fAlpha = 0.0;
const double fStepsMinus1 = ((double)nSteps) - 1.0;
double fTempColor;
if ( !bLinear)
{
nSteps -= 1; // draw middle polygons as one polygon after loop to avoid gap
}
for ( long i = 0; i < nSteps; i++ )
{
// linear interpolation of color
fAlpha = ((double)i) / fStepsMinus1;
fTempColor = ((double)nStartRed) * (1.0-fAlpha) + ((double)nEndRed) * fAlpha;
nRed = ImplGetGradientColorValue((long)fTempColor);
fTempColor = ((double)nStartGreen) * (1.0-fAlpha) + ((double)nEndGreen) * fAlpha;
nGreen = ImplGetGradientColorValue((long)fTempColor);
fTempColor = ((double)nStartBlue) * (1.0-fAlpha) + ((double)nEndBlue) * fAlpha;
nBlue = ImplGetGradientColorValue((long)fTempColor);
if ( bMtf )
mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
else
mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
// Polygon for this color step
aRect.Top() = (long)( fGradientLine + ((double) i) * fScanInc );
aRect.Bottom() = (long)( fGradientLine + ( ((double) i) + 1.0 ) * fScanInc + fScanInc*.1 );
aPoly[0] = aRect.TopLeft();
aPoly[1] = aRect.TopRight();
aPoly[2] = aRect.BottomRight();
aPoly[3] = aRect.BottomLeft();
aPoly.Rotate( aCenter, nAngle );
if ( bMtf )
mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
else
ImplDrawPolygon( aPoly, pClipPolyPoly );
if ( !bLinear )
{
aMirrorRect.Bottom() = (long)( fMirrorGradientLine - ((double) i) * fScanInc );
aMirrorRect.Top() = (long)( fMirrorGradientLine - (((double) i) + 1.0)* fScanInc );
aPoly[0] = aMirrorRect.TopLeft();
aPoly[1] = aMirrorRect.TopRight();
aPoly[2] = aMirrorRect.BottomRight();
aPoly[3] = aMirrorRect.BottomLeft();
aPoly.Rotate( aCenter, nAngle );
if ( bMtf )
mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
else
ImplDrawPolygon( aPoly, pClipPolyPoly );
}
}
if ( !bLinear)
{
// draw middle polygon with end color
nRed = ImplGetGradientColorValue(nEndRed);
nGreen = ImplGetGradientColorValue(nEndGreen);
nBlue = ImplGetGradientColorValue(nEndBlue);
if ( bMtf )
mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
else
mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
aRect.Top() = (long)( fGradientLine + ((double)nSteps) * fScanInc );
aRect.Bottom() = (long)( fMirrorGradientLine - ((double) nSteps) * fScanInc );
aPoly[0] = aRect.TopLeft();
aPoly[1] = aRect.TopRight();
aPoly[2] = aRect.BottomRight();
aPoly[3] = aRect.BottomLeft();
aPoly.Rotate( aCenter, nAngle );
if ( bMtf )
mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
else
ImplDrawPolygon( aPoly, pClipPolyPoly );
}
}
void OutputDevice::ImplDrawComplexGradient( const Rectangle& rRect,
const Gradient& rGradient,
bool bMtf, const PolyPolygon* pClipPolyPoly )
{
// Determine if we output via Polygon or PolyPolygon
// For all rasteroperations other then Overpaint always use PolyPolygon,
// as we will get wrong results if we output multiple times on top of each other.
// Also for printers always use PolyPolygon, as not all printers
// can print polygons on top of each other.
boost::scoped_ptr<PolyPolygon> pPolyPoly;
Rectangle aRect;
Point aCenter;
Color aStartCol( rGradient.GetStartColor() );
Color aEndCol( rGradient.GetEndColor() );
long nStartRed = ( (long) aStartCol.GetRed() * rGradient.GetStartIntensity() ) / 100;
long nStartGreen = ( (long) aStartCol.GetGreen() * rGradient.GetStartIntensity() ) / 100;
long nStartBlue = ( (long) aStartCol.GetBlue() * rGradient.GetStartIntensity() ) / 100;
long nEndRed = ( (long) aEndCol.GetRed() * rGradient.GetEndIntensity() ) / 100;
long nEndGreen = ( (long) aEndCol.GetGreen() * rGradient.GetEndIntensity() ) / 100;
long nEndBlue = ( (long) aEndCol.GetBlue() * rGradient.GetEndIntensity() ) / 100;
long nRedSteps = nEndRed - nStartRed;
long nGreenSteps = nEndGreen - nStartGreen;
long nBlueSteps = nEndBlue - nStartBlue;
sal_uInt16 nAngle = rGradient.GetAngle() % 3600;
rGradient.GetBoundRect( rRect, aRect, aCenter );
if ( UsePolyPolygonForComplexGradient() || bMtf )
pPolyPoly.reset(new PolyPolygon( 2 ));
// last parameter - true if complex gradient, false if linear
long nStepCount = ImplGetGradientSteps( rGradient, rRect, bMtf, true );
// at least three steps and at most the number of colour differences
long nSteps = std::max( nStepCount, 2L );
long nCalcSteps = std::abs( nRedSteps );
long nTempSteps = std::abs( nGreenSteps );
if ( nTempSteps > nCalcSteps )
nCalcSteps = nTempSteps;
nTempSteps = std::abs( nBlueSteps );
if ( nTempSteps > nCalcSteps )
nCalcSteps = nTempSteps;
if ( nCalcSteps < nSteps )
nSteps = nCalcSteps;
if ( !nSteps )
nSteps = 1;
// determine output limits and stepsizes for all directions
Polygon aPoly;
double fScanLeft = aRect.Left();
double fScanTop = aRect.Top();
double fScanRight = aRect.Right();
double fScanBottom = aRect.Bottom();
double fScanIncX = (double) aRect.GetWidth() / (double) nSteps * 0.5;
double fScanIncY = (double) aRect.GetHeight() / (double) nSteps * 0.5;
// all gradients are rendered as nested rectangles which shrink
// equally in each dimension - except for 'square' gradients
// which shrink to a central vertex but are not per-se square.
if( rGradient.GetStyle() != GradientStyle_SQUARE )
{
fScanIncY = std::min( fScanIncY, fScanIncX );
fScanIncX = fScanIncY;
}
sal_uInt8 nRed = (sal_uInt8) nStartRed, nGreen = (sal_uInt8) nStartGreen, nBlue = (sal_uInt8) nStartBlue;
bool bPaintLastPolygon( false ); // #107349# Paint last polygon only if loop has generated any output
if( bMtf )
mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
else
mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
if( pPolyPoly )
{
pPolyPoly->Insert( aPoly = rRect );
pPolyPoly->Insert( aPoly );
}
else
{
// extend rect, to avoid missing bounding line
Rectangle aExtRect( rRect );
aExtRect.Left() -= 1;
aExtRect.Top() -= 1;
aExtRect.Right() += 1;
aExtRect.Bottom() += 1;
ImplDrawPolygon( aPoly = aExtRect, pClipPolyPoly );
}
// loop to output Polygone/PolyPolygone sequentially
for( long i = 1; i < nSteps; i++ )
{
// calculate new Polygon
aRect.Left() = (long)( fScanLeft += fScanIncX );
aRect.Top() = (long)( fScanTop += fScanIncY );
aRect.Right() = (long)( fScanRight -= fScanIncX );
aRect.Bottom() = (long)( fScanBottom -= fScanIncY );
if( ( aRect.GetWidth() < 2 ) || ( aRect.GetHeight() < 2 ) )
break;
if( rGradient.GetStyle() == GradientStyle_RADIAL || rGradient.GetStyle() == GradientStyle_ELLIPTICAL )
aPoly = Polygon( aRect.Center(), aRect.GetWidth() >> 1, aRect.GetHeight() >> 1 );
else
aPoly = Polygon( aRect );
aPoly.Rotate( aCenter, nAngle );
// adapt colour accordingly
const long nStepIndex = ( ( pPolyPoly ) ? i : ( i + 1 ) );
nRed = ImplGetGradientColorValue( nStartRed + ( ( nRedSteps * nStepIndex ) / nSteps ) );
nGreen = ImplGetGradientColorValue( nStartGreen + ( ( nGreenSteps * nStepIndex ) / nSteps ) );
nBlue = ImplGetGradientColorValue( nStartBlue + ( ( nBlueSteps * nStepIndex ) / nSteps ) );
// either slow PolyPolygon output or fast Polygon-Paiting
if( pPolyPoly )
{
bPaintLastPolygon = true; // #107349# Paint last polygon only if loop has generated any output
pPolyPoly->Replace( pPolyPoly->GetObject( 1 ), 0 );
pPolyPoly->Replace( aPoly, 1 );
if( bMtf )
mpMetaFile->AddAction( new MetaPolyPolygonAction( *pPolyPoly ) );
else
ImplDrawPolyPolygon( *pPolyPoly, pClipPolyPoly );
// #107349# Set fill color _after_ geometry painting:
// pPolyPoly's geometry is the band from last iteration's
// aPoly to current iteration's aPoly. The window outdev
// path (see else below), on the other hand, paints the
// full aPoly. Thus, here, we're painting the band before
// the one painted in the window outdev path below. To get
// matching colors, have to delay color setting here.
if( bMtf )
mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
else
mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
}
else
{
// #107349# Set fill color _before_ geometry painting
if( bMtf )
mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
else
mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
ImplDrawPolygon( aPoly, pClipPolyPoly );
}
}
// we should draw last inner Polygon if we output PolyPolygon
if( pPolyPoly )
{
const Polygon& rPoly = pPolyPoly->GetObject( 1 );
if( !rPoly.GetBoundRect().IsEmpty() )
{
// #107349# Paint last polygon with end color only if loop
// has generated output. Otherwise, the current
// (i.e. start) color is taken, to generate _any_ output.
if( bPaintLastPolygon )
{
nRed = ImplGetGradientColorValue( nEndRed );
nGreen = ImplGetGradientColorValue( nEndGreen );
nBlue = ImplGetGradientColorValue( nEndBlue );
}
if( bMtf )
{
mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
mpMetaFile->AddAction( new MetaPolygonAction( rPoly ) );
}
else
{
mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
ImplDrawPolygon( rPoly, pClipPolyPoly );
}
}
}
}
Color OutputDevice::GetSingleColorGradientFill()
{
Color aColor;
// we should never call on this function if any of these aren't set!
assert( mnDrawMode & ( DRAWMODE_BLACKGRADIENT | DRAWMODE_WHITEGRADIENT | DRAWMODE_SETTINGSGRADIENT) );
if ( mnDrawMode & DRAWMODE_BLACKGRADIENT )
aColor = Color( COL_BLACK );
else if ( mnDrawMode & DRAWMODE_WHITEGRADIENT )
aColor = Color( COL_WHITE );
else if ( mnDrawMode & DRAWMODE_SETTINGSGRADIENT )
aColor = GetSettings().GetStyleSettings().GetWindowColor();
if ( mnDrawMode & DRAWMODE_GHOSTEDGRADIENT )
{
aColor = Color( ( aColor.GetRed() >> 1 ) | 0x80,
( aColor.GetGreen() >> 1 ) | 0x80,
( aColor.GetBlue() >> 1 ) | 0x80 );
}
return aColor;
}
void OutputDevice::SetGrayscaleColors( Gradient &rGradient )
{
// this should only be called with the drawing mode is for grayscale or ghosted gradients
assert ( mnDrawMode & ( DRAWMODE_GRAYGRADIENT | DRAWMODE_GHOSTEDGRADIENT ) );
Color aStartCol( rGradient.GetStartColor() );
Color aEndCol( rGradient.GetEndColor() );
if ( mnDrawMode & DRAWMODE_GRAYGRADIENT )
{
sal_uInt8 cStartLum = aStartCol.GetLuminance(), cEndLum = aEndCol.GetLuminance();
aStartCol = Color( cStartLum, cStartLum, cStartLum );
aEndCol = Color( cEndLum, cEndLum, cEndLum );
}
if ( mnDrawMode & DRAWMODE_GHOSTEDGRADIENT )
{
aStartCol = Color( ( aStartCol.GetRed() >> 1 ) | 0x80,
( aStartCol.GetGreen() >> 1 ) | 0x80,
( aStartCol.GetBlue() >> 1 ) | 0x80 );
aEndCol = Color( ( aEndCol.GetRed() >> 1 ) | 0x80,
( aEndCol.GetGreen() >> 1 ) | 0x80,
( aEndCol.GetBlue() >> 1 ) | 0x80 );
}
rGradient.SetStartColor( aStartCol );
rGradient.SetEndColor( aEndCol );
}
void OutputDevice::AddGradientActions( const Rectangle& rRect, const Gradient& rGradient,
GDIMetaFile& rMtf )
{
Rectangle aRect( rRect );
aRect.Justify();
// do nothing if the rectangle is empty
if ( !aRect.IsEmpty() )
{
Gradient aGradient( rGradient );
GDIMetaFile* pOldMtf = mpMetaFile;
mpMetaFile = &rMtf;
mpMetaFile->AddAction( new MetaPushAction( PUSH_ALL ) );
mpMetaFile->AddAction( new MetaISectRectClipRegionAction( aRect ) );
mpMetaFile->AddAction( new MetaLineColorAction( Color(), false ) );
// because we draw with no border line, we have to expand gradient
// rect to avoid missing lines on the right and bottom edge
aRect.Left()--;
aRect.Top()--;
aRect.Right()++;
aRect.Bottom()++;
// 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, true, NULL );
else
ImplDrawComplexGradient( aRect, aGradient, true, NULL );
mpMetaFile->AddAction( new MetaPopAction() );
mpMetaFile = pOldMtf;
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/gdi/outdev/outdev4.cxx b/vcl/source/gdi/outdev/outdev4.cxx
index 31e816d..0df1990 100644
--- a/vcl/source/gdi/outdev/outdev4.cxx
+++ b/vcl/source/gdi/outdev/outdev4.cxx
@@ -46,7 +46,6 @@
#include <boost/scoped_ptr.hpp>
#define HATCH_MAXPOINTS 1024
#define GRADIENT_DEFAULT_STEPCOUNT 0
extern "C" int SAL_CALL ImplHatchCmpFnc( const void* p1, const void* p2 )
{
@@ -128,655 +127,6 @@ void OutputDevice::ImplDrawPolyPolygon( const PolyPolygon& rPolyPoly, const Poly
delete pPolyPoly;
}
void OutputDevice::DrawGradient( const Rectangle& rRect,
const Gradient& rGradient )
{
// Convert rectangle to a PolyPolygon by first converting to a Polygon
Polygon aPolygon ( rRect );
PolyPolygon aPolyPoly ( aPolygon );
DrawGradient ( aPolyPoly, rGradient );
}
void OutputDevice::DrawGradient( const PolyPolygon& rPolyPoly,
const Gradient& rGradient )
{
if ( mnDrawMode & DRAWMODE_NOGRADIENT )
return; // nothing to draw!
if ( mbInitClipRegion )
ImplInitClipRegion();
if ( mbOutputClipped )
return;
if ( rPolyPoly.Count() && rPolyPoly[ 0 ].GetSize() )
{
if ( mnDrawMode & ( DRAWMODE_BLACKGRADIENT | DRAWMODE_WHITEGRADIENT | DRAWMODE_SETTINGSGRADIENT) )
{
Color aColor = GetSingleColorGradientFill();
Push( PUSH_LINECOLOR | PUSH_FILLCOLOR );
SetLineColor( aColor );
SetFillColor( aColor );
DrawPolyPolygon( rPolyPoly );
Pop();
return;
}
Gradient aGradient( rGradient );
if ( mnDrawMode & ( DRAWMODE_GRAYGRADIENT | DRAWMODE_GHOSTEDGRADIENT ) )
{
SetGrayscaleColors( aGradient );
}
if( mpMetaFile )
{
const Rectangle aBoundRect( rPolyPoly.GetBoundRect() );
if ( rPolyPoly.IsRect() )
{
mpMetaFile->AddAction( new MetaGradientAction( aBoundRect, aGradient ) );
}
else
{
mpMetaFile->AddAction( new MetaCommentAction( "XGRAD_SEQ_BEGIN" ) );
mpMetaFile->AddAction( new MetaGradientExAction( rPolyPoly, rGradient ) );
Push( PUSH_CLIPREGION );
IntersectClipRegion(Region(rPolyPoly));
DrawGradient( aBoundRect, rGradient );
Pop();
mpMetaFile->AddAction( new MetaCommentAction( "XGRAD_SEQ_END" ) );
}
}
if( !IsDeviceOutputNecessary() || ImplIsRecordLayout() )
return;
// Clip and then draw the gradient
if( !Rectangle( PixelToLogic( Point() ), GetOutputSize() ).IsEmpty() )
{
const Rectangle aBoundRect( rPolyPoly.GetBoundRect() );
// convert rectangle to pixels
Rectangle aRect( ImplLogicToDevicePixel( aBoundRect ) );
aRect.Justify();
// do nothing if the rectangle is empty
if ( !aRect.IsEmpty() )
{
if( !mpGraphics && !ImplGetGraphics() )
return;
// secure clip region
Push( PUSH_CLIPREGION );
IntersectClipRegion( aBoundRect );
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 ( rPolyPoly.IsRect() )
{
// because we draw with no border line, we have to expand gradient
// rect to avoid missing lines on the right and bottom edge
aRect.Left()--;
aRect.Top()--;
aRect.Right()++;
aRect.Bottom()++;
}
// if the clipping polypolygon is a rectangle, then it's the same size as the bounding of the
// polypolygon, so pass in a NULL for the clipping parameter
if( aGradient.GetStyle() == GradientStyle_LINEAR || rGradient.GetStyle() == GradientStyle_AXIAL )
ImplDrawLinearGradient( aRect, aGradient, false, aClipPolyPoly.IsRect() ? NULL : &aClipPolyPoly );
else
ImplDrawComplexGradient( aRect, aGradient, false, aClipPolyPoly.IsRect() ? NULL : &aClipPolyPoly );
}
Pop();
}
}
}
if( mpAlphaVDev )
mpAlphaVDev->DrawPolyPolygon( rPolyPoly );
}
inline sal_uInt8 ImplGetGradientColorValue( long nValue )
{
if ( nValue < 0 )
return 0;
else if ( nValue > 0xFF )
return 0xFF;
else
return (sal_uInt8)nValue;
}
long OutputDevice::ImplGetGradientStepCount( long nMinRect )
{
long nInc = (nMinRect < 50) ? 2 : 4;
return nInc;
}
long OutputDevice::ImplGetGradientSteps( const Gradient& rGradient, const Rectangle& rRect, bool bMtf, bool bComplex )
{
// calculate step count
long nStepCount = rGradient.GetSteps();
long nMinRect;
// generate nStepCount, if not passed
if (bComplex)
nMinRect = std::min( rRect.GetWidth(), rRect.GetHeight() );
else
nMinRect = rRect.GetHeight();
if ( !nStepCount )
{
long nInc;
nInc = ImplGetGradientStepCount (nMinRect);
if ( !nInc || bMtf )
nInc = 1;
nStepCount = nMinRect / nInc;
}
return nStepCount;
}
void OutputDevice::ImplDrawLinearGradient( const Rectangle& rRect,
const Gradient& rGradient,
bool bMtf, const PolyPolygon* pClipPolyPoly )
{
// get BoundRect of rotated rectangle
Rectangle aRect;
Point aCenter;
sal_uInt16 nAngle = rGradient.GetAngle() % 3600;
rGradient.GetBoundRect( rRect, aRect, aCenter );
bool bLinear = (rGradient.GetStyle() == GradientStyle_LINEAR);
double fBorder = rGradient.GetBorder() * aRect.GetHeight() / 100.0;
if ( !bLinear )
{
fBorder /= 2.0;
}
Rectangle aMirrorRect = aRect; // used in style axial
aMirrorRect.Top() = ( aRect.Top() + aRect.Bottom() ) / 2;
if ( !bLinear )
{
aRect.Bottom() = aMirrorRect.Top();
}
// colour-intensities of start- and finish; change if needed
long nFactor;
Color aStartCol = rGradient.GetStartColor();
Color aEndCol = rGradient.GetEndColor();
long nStartRed = aStartCol.GetRed();
long nStartGreen = aStartCol.GetGreen();
long nStartBlue = aStartCol.GetBlue();
long nEndRed = aEndCol.GetRed();
long nEndGreen = aEndCol.GetGreen();
long nEndBlue = aEndCol.GetBlue();
nFactor = rGradient.GetStartIntensity();
nStartRed = (nStartRed * nFactor) / 100;
nStartGreen = (nStartGreen * nFactor) / 100;
nStartBlue = (nStartBlue * nFactor) / 100;
nFactor = rGradient.GetEndIntensity();
nEndRed = (nEndRed * nFactor) / 100;
nEndGreen = (nEndGreen * nFactor) / 100;
nEndBlue = (nEndBlue * nFactor) / 100;
// gradient style axial has exchanged start and end colors
if ( !bLinear)
{
long nTempColor = nStartRed;
nStartRed = nEndRed;
nEndRed = nTempColor;
nTempColor = nStartGreen;
nStartGreen = nEndGreen;
nEndGreen = nTempColor;
nTempColor = nStartBlue;
nStartBlue = nEndBlue;
nEndBlue = nTempColor;
}
sal_uInt8 nRed;
sal_uInt8 nGreen;
sal_uInt8 nBlue;
// Create border
Rectangle aBorderRect = aRect;
Polygon aPoly( 4 );
if (fBorder > 0.0)
{
nRed = (sal_uInt8)nStartRed;
nGreen = (sal_uInt8)nStartGreen;
nBlue = (sal_uInt8)nStartBlue;
if ( bMtf )
mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
else
mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
aBorderRect.Bottom() = (long)( aBorderRect.Top() + fBorder );
aRect.Top() = aBorderRect.Bottom();
aPoly[0] = aBorderRect.TopLeft();
aPoly[1] = aBorderRect.TopRight();
aPoly[2] = aBorderRect.BottomRight();
aPoly[3] = aBorderRect.BottomLeft();
aPoly.Rotate( aCenter, nAngle );
if ( bMtf )
mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
else
ImplDrawPolygon( aPoly, pClipPolyPoly );
if ( !bLinear)
{
aBorderRect = aMirrorRect;
aBorderRect.Top() = (long) ( aBorderRect.Bottom() - fBorder );
aMirrorRect.Bottom() = aBorderRect.Top();
aPoly[0] = aBorderRect.TopLeft();
aPoly[1] = aBorderRect.TopRight();
aPoly[2] = aBorderRect.BottomRight();
aPoly[3] = aBorderRect.BottomLeft();
aPoly.Rotate( aCenter, nAngle );
if ( bMtf )
mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
else
ImplDrawPolygon( aPoly, pClipPolyPoly );
}
}
// calculate step count
long nStepCount = ImplGetGradientSteps( rGradient, aRect, bMtf );
// minimal three steps and maximal as max color steps
long nAbsRedSteps = std::abs( nEndRed - nStartRed );
long nAbsGreenSteps = std::abs( nEndGreen - nStartGreen );
long nAbsBlueSteps = std::abs( nEndBlue - nStartBlue );
long nMaxColorSteps = std::max( nAbsRedSteps , nAbsGreenSteps );
nMaxColorSteps = std::max( nMaxColorSteps, nAbsBlueSteps );
long nSteps = std::min( nStepCount, nMaxColorSteps );
if ( nSteps < 3)
{
nSteps = 3;
}
double fScanInc = ((double)aRect.GetHeight()) / (double) nSteps;
double fGradientLine = (double)aRect.Top();
double fMirrorGradientLine = (double) aMirrorRect.Bottom();
double fAlpha = 0.0;
const double fStepsMinus1 = ((double)nSteps) - 1.0;
double fTempColor;
if ( !bLinear)
{
nSteps -= 1; // draw middle polygons as one polygon after loop to avoid gap
}
for ( long i = 0; i < nSteps; i++ )
{
// linear interpolation of color
fAlpha = ((double)i) / fStepsMinus1;
fTempColor = ((double)nStartRed) * (1.0-fAlpha) + ((double)nEndRed) * fAlpha;
nRed = ImplGetGradientColorValue((long)fTempColor);
fTempColor = ((double)nStartGreen) * (1.0-fAlpha) + ((double)nEndGreen) * fAlpha;
nGreen = ImplGetGradientColorValue((long)fTempColor);
fTempColor = ((double)nStartBlue) * (1.0-fAlpha) + ((double)nEndBlue) * fAlpha;
nBlue = ImplGetGradientColorValue((long)fTempColor);
if ( bMtf )
mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
else
mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
// Polygon for this color step
aRect.Top() = (long)( fGradientLine + ((double) i) * fScanInc );
aRect.Bottom() = (long)( fGradientLine + ( ((double) i) + 1.0 ) * fScanInc + fScanInc*.1 );
aPoly[0] = aRect.TopLeft();
aPoly[1] = aRect.TopRight();
aPoly[2] = aRect.BottomRight();
aPoly[3] = aRect.BottomLeft();
aPoly.Rotate( aCenter, nAngle );
if ( bMtf )
mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
else
ImplDrawPolygon( aPoly, pClipPolyPoly );
if ( !bLinear )
{
aMirrorRect.Bottom() = (long)( fMirrorGradientLine - ((double) i) * fScanInc );
aMirrorRect.Top() = (long)( fMirrorGradientLine - (((double) i) + 1.0)* fScanInc );
aPoly[0] = aMirrorRect.TopLeft();
aPoly[1] = aMirrorRect.TopRight();
aPoly[2] = aMirrorRect.BottomRight();
aPoly[3] = aMirrorRect.BottomLeft();
aPoly.Rotate( aCenter, nAngle );
if ( bMtf )
mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
else
ImplDrawPolygon( aPoly, pClipPolyPoly );
}
}
if ( !bLinear)
{
// draw middle polygon with end color
nRed = ImplGetGradientColorValue(nEndRed);
nGreen = ImplGetGradientColorValue(nEndGreen);
nBlue = ImplGetGradientColorValue(nEndBlue);
if ( bMtf )
mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
else
mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
aRect.Top() = (long)( fGradientLine + ((double)nSteps) * fScanInc );
aRect.Bottom() = (long)( fMirrorGradientLine - ((double) nSteps) * fScanInc );
aPoly[0] = aRect.TopLeft();
aPoly[1] = aRect.TopRight();
aPoly[2] = aRect.BottomRight();
aPoly[3] = aRect.BottomLeft();
aPoly.Rotate( aCenter, nAngle );
if ( bMtf )
mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
else
ImplDrawPolygon( aPoly, pClipPolyPoly );
}
}
void OutputDevice::ImplDrawComplexGradient( const Rectangle& rRect,
const Gradient& rGradient,
bool bMtf, const PolyPolygon* pClipPolyPoly )
{
// Determine if we output via Polygon or PolyPolygon
// For all rasteroperations other then Overpaint always use PolyPolygon,
// as we will get wrong results if we output multiple times on top of each other.
// Also for printers always use PolyPolygon, as not all printers
// can print polygons on top of each other.
boost::scoped_ptr<PolyPolygon> pPolyPoly;
Rectangle aRect;
Point aCenter;
Color aStartCol( rGradient.GetStartColor() );
Color aEndCol( rGradient.GetEndColor() );
long nStartRed = ( (long) aStartCol.GetRed() * rGradient.GetStartIntensity() ) / 100;
long nStartGreen = ( (long) aStartCol.GetGreen() * rGradient.GetStartIntensity() ) / 100;
long nStartBlue = ( (long) aStartCol.GetBlue() * rGradient.GetStartIntensity() ) / 100;
long nEndRed = ( (long) aEndCol.GetRed() * rGradient.GetEndIntensity() ) / 100;
long nEndGreen = ( (long) aEndCol.GetGreen() * rGradient.GetEndIntensity() ) / 100;
long nEndBlue = ( (long) aEndCol.GetBlue() * rGradient.GetEndIntensity() ) / 100;
long nRedSteps = nEndRed - nStartRed;
long nGreenSteps = nEndGreen - nStartGreen;
long nBlueSteps = nEndBlue - nStartBlue;
sal_uInt16 nAngle = rGradient.GetAngle() % 3600;
rGradient.GetBoundRect( rRect, aRect, aCenter );
if ( UsePolyPolygonForComplexGradient() || bMtf )
pPolyPoly.reset(new PolyPolygon( 2 ));
// last parameter - true if complex gradient, false if linear
long nStepCount = ImplGetGradientSteps( rGradient, rRect, bMtf, true );
// at least three steps and at most the number of colour differences
long nSteps = std::max( nStepCount, 2L );
long nCalcSteps = std::abs( nRedSteps );
long nTempSteps = std::abs( nGreenSteps );
if ( nTempSteps > nCalcSteps )
nCalcSteps = nTempSteps;
nTempSteps = std::abs( nBlueSteps );
if ( nTempSteps > nCalcSteps )
nCalcSteps = nTempSteps;
if ( nCalcSteps < nSteps )
nSteps = nCalcSteps;
if ( !nSteps )
nSteps = 1;
// determine output limits and stepsizes for all directions
Polygon aPoly;
double fScanLeft = aRect.Left();
double fScanTop = aRect.Top();
double fScanRight = aRect.Right();
double fScanBottom = aRect.Bottom();
double fScanIncX = (double) aRect.GetWidth() / (double) nSteps * 0.5;
double fScanIncY = (double) aRect.GetHeight() / (double) nSteps * 0.5;
// all gradients are rendered as nested rectangles which shrink
// equally in each dimension - except for 'square' gradients
// which shrink to a central vertex but are not per-se square.
if( rGradient.GetStyle() != GradientStyle_SQUARE )
{
fScanIncY = std::min( fScanIncY, fScanIncX );
fScanIncX = fScanIncY;
}
sal_uInt8 nRed = (sal_uInt8) nStartRed, nGreen = (sal_uInt8) nStartGreen, nBlue = (sal_uInt8) nStartBlue;
bool bPaintLastPolygon( false ); // #107349# Paint last polygon only if loop has generated any output
if( bMtf )
mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
else
mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
if( pPolyPoly )
{
pPolyPoly->Insert( aPoly = rRect );
pPolyPoly->Insert( aPoly );
}
else
{
// extend rect, to avoid missing bounding line
Rectangle aExtRect( rRect );
aExtRect.Left() -= 1;
aExtRect.Top() -= 1;
aExtRect.Right() += 1;
aExtRect.Bottom() += 1;
ImplDrawPolygon( aPoly = aExtRect, pClipPolyPoly );
}
// loop to output Polygone/PolyPolygone sequentially
for( long i = 1; i < nSteps; i++ )
{
// calculate new Polygon
aRect.Left() = (long)( fScanLeft += fScanIncX );
aRect.Top() = (long)( fScanTop += fScanIncY );
aRect.Right() = (long)( fScanRight -= fScanIncX );
aRect.Bottom() = (long)( fScanBottom -= fScanIncY );
if( ( aRect.GetWidth() < 2 ) || ( aRect.GetHeight() < 2 ) )
break;
if( rGradient.GetStyle() == GradientStyle_RADIAL || rGradient.GetStyle() == GradientStyle_ELLIPTICAL )
aPoly = Polygon( aRect.Center(), aRect.GetWidth() >> 1, aRect.GetHeight() >> 1 );
else
aPoly = Polygon( aRect );
aPoly.Rotate( aCenter, nAngle );
// adapt colour accordingly
const long nStepIndex = ( ( pPolyPoly ) ? i : ( i + 1 ) );
nRed = ImplGetGradientColorValue( nStartRed + ( ( nRedSteps * nStepIndex ) / nSteps ) );
nGreen = ImplGetGradientColorValue( nStartGreen + ( ( nGreenSteps * nStepIndex ) / nSteps ) );
nBlue = ImplGetGradientColorValue( nStartBlue + ( ( nBlueSteps * nStepIndex ) / nSteps ) );
// either slow PolyPolygon output or fast Polygon-Paiting
if( pPolyPoly )
{
bPaintLastPolygon = true; // #107349# Paint last polygon only if loop has generated any output
pPolyPoly->Replace( pPolyPoly->GetObject( 1 ), 0 );
pPolyPoly->Replace( aPoly, 1 );
if( bMtf )
mpMetaFile->AddAction( new MetaPolyPolygonAction( *pPolyPoly ) );
else
ImplDrawPolyPolygon( *pPolyPoly, pClipPolyPoly );
// #107349# Set fill color _after_ geometry painting:
// pPolyPoly's geometry is the band from last iteration's
// aPoly to current iteration's aPoly. The window outdev
// path (see else below), on the other hand, paints the
// full aPoly. Thus, here, we're painting the band before
// the one painted in the window outdev path below. To get
// matching colors, have to delay color setting here.
if( bMtf )
mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
else
mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
}
else
{
// #107349# Set fill color _before_ geometry painting
if( bMtf )
mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
else
mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
ImplDrawPolygon( aPoly, pClipPolyPoly );
}
}
// we should draw last inner Polygon if we output PolyPolygon
if( pPolyPoly )
{
const Polygon& rPoly = pPolyPoly->GetObject( 1 );
if( !rPoly.GetBoundRect().IsEmpty() )
{
// #107349# Paint last polygon with end color only if loop
// has generated output. Otherwise, the current
// (i.e. start) color is taken, to generate _any_ output.
if( bPaintLastPolygon )
{
nRed = ImplGetGradientColorValue( nEndRed );
nGreen = ImplGetGradientColorValue( nEndGreen );
nBlue = ImplGetGradientColorValue( nEndBlue );
}
if( bMtf )
{
mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) );
mpMetaFile->AddAction( new MetaPolygonAction( rPoly ) );
}
else
{
mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
ImplDrawPolygon( rPoly, pClipPolyPoly );
}
}
}
}
Color OutputDevice::GetSingleColorGradientFill()
{
Color aColor;
// we should never call on this function if any of these aren't set!
assert( mnDrawMode & ( DRAWMODE_BLACKGRADIENT | DRAWMODE_WHITEGRADIENT | DRAWMODE_SETTINGSGRADIENT) );
if ( mnDrawMode & DRAWMODE_BLACKGRADIENT )
aColor = Color( COL_BLACK );
else if ( mnDrawMode & DRAWMODE_WHITEGRADIENT )
aColor = Color( COL_WHITE );
else if ( mnDrawMode & DRAWMODE_SETTINGSGRADIENT )
aColor = GetSettings().GetStyleSettings().GetWindowColor();
if ( mnDrawMode & DRAWMODE_GHOSTEDGRADIENT )
{
aColor = Color( ( aColor.GetRed() >> 1 ) | 0x80,
( aColor.GetGreen() >> 1 ) | 0x80,
( aColor.GetBlue() >> 1 ) | 0x80 );
}
return aColor;
}
void OutputDevice::SetGrayscaleColors( Gradient &rGradient )
{
// this should only be called with the drawing mode is for grayscale or ghosted gradients
assert ( mnDrawMode & ( DRAWMODE_GRAYGRADIENT | DRAWMODE_GHOSTEDGRADIENT ) );
Color aStartCol( rGradient.GetStartColor() );
Color aEndCol( rGradient.GetEndColor() );
if ( mnDrawMode & DRAWMODE_GRAYGRADIENT )
{
sal_uInt8 cStartLum = aStartCol.GetLuminance(), cEndLum = aEndCol.GetLuminance();
aStartCol = Color( cStartLum, cStartLum, cStartLum );
aEndCol = Color( cEndLum, cEndLum, cEndLum );
}
if ( mnDrawMode & DRAWMODE_GHOSTEDGRADIENT )
{
aStartCol = Color( ( aStartCol.GetRed() >> 1 ) | 0x80,
( aStartCol.GetGreen() >> 1 ) | 0x80,
( aStartCol.GetBlue() >> 1 ) | 0x80 );
aEndCol = Color( ( aEndCol.GetRed() >> 1 ) | 0x80,
( aEndCol.GetGreen() >> 1 ) | 0x80,
( aEndCol.GetBlue() >> 1 ) | 0x80 );
}
rGradient.SetStartColor( aStartCol );
rGradient.SetEndColor( aEndCol );
}
void OutputDevice::AddGradientActions( const Rectangle& rRect, const Gradient& rGradient,
GDIMetaFile& rMtf )
{
Rectangle aRect( rRect );
aRect.Justify();
// do nothing if the rectangle is empty
if ( !aRect.IsEmpty() )
{
Gradient aGradient( rGradient );
GDIMetaFile* pOldMtf = mpMetaFile;
mpMetaFile = &rMtf;
mpMetaFile->AddAction( new MetaPushAction( PUSH_ALL ) );
mpMetaFile->AddAction( new MetaISectRectClipRegionAction( aRect ) );
mpMetaFile->AddAction( new MetaLineColorAction( Color(), false ) );
// because we draw with no border line, we have to expand gradient
// rect to avoid missing lines on the right and bottom edge
aRect.Left()--;
aRect.Top()--;
aRect.Right()++;
aRect.Bottom()++;
// 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, true, NULL );
else
ImplDrawComplexGradient( aRect, aGradient, true, NULL );
mpMetaFile->AddAction( new MetaPopAction() );
mpMetaFile = pOldMtf;
}
}
void OutputDevice::DrawHatch( const PolyPolygon& rPolyPoly, const Hatch& rHatch )
{