Files
core/ASCImageStudio3/ASCGraphics/OfficeSvmFile/ASC/ASCOutputDevice.cpp

8223 lines
273 KiB
C++
Raw Blame History

#include "ASCOutputDevice.h"
#include "tools/bigint.hxx"
#include "vcl/salbtype.hxx"
#include "vcl/alpha.hxx"
#include "implncvt.hxx"
#include "vcl/wall2.hxx"
#include "vcl/bmpacc.hxx"
#include "vcl/impbmp.hxx"
#include "vcl/salgtype.hxx"
#include "vcl/salgdi.hxx"
#include <math.h>
#include "outmap.cxx"
using namespace SVMCore;
namespace SVMCore{
#define TEXT_DRAW_ELLIPSIS (TEXT_DRAW_ENDELLIPSIS | TEXT_DRAW_PATHELLIPSIS | TEXT_DRAW_NEWSELLIPSIS)
// =======================================================================
#define UNDERLINE_LAST UNDERLINE_BOLDWAVE
#define STRIKEOUT_LAST STRIKEOUT_X
#define OUTDEV_POLYPOLY_STACKBUF 32
// =======================================================================
static void ImplRotatePos( long nOriginX, long nOriginY, long& rX, long& rY,
int nOrientation )
{
if ( (nOrientation >= 0) && !(nOrientation % 900) )
{
if ( (nOrientation >= 3600) )
nOrientation %= 3600;
if ( nOrientation )
{
rX -= nOriginX;
rY -= nOriginY;
if ( nOrientation == 900 )
{
long nTemp = rX;
rX = rY;
rY = -nTemp;
}
else if ( nOrientation == 1800 )
{
rX = -rX;
rY = -rY;
}
else /* ( nOrientation == 2700 ) */
{
long nTemp = rX;
rX = -rY;
rY = nTemp;
}
rX += nOriginX;
rY += nOriginY;
}
}
else
{
double nRealOrientation = nOrientation*F_PI1800;
double nCos = cos( nRealOrientation );
double nSin = sin( nRealOrientation );
// Translation...
long nX = rX-nOriginX;
long nY = rY-nOriginY;
// Rotation...
rX = +((long)(nCos*nX + nSin*nY)) + nOriginX;
rY = -((long)(nSin*nX - nCos*nY)) + nOriginY;
}
}
// =======================================================================
struct ImplObjStack
{
ImplObjStack* mpPrev;
MapMode* mpMapMode;
Region* mpClipRegion;
Color* mpLineColor;
Color* mpFillColor;
Font* mpFont;
Color* mpTextColor;
Color* mpTextFillColor;
Color* mpTextLineColor;
Color* mpOverlineColor;
Point* mpRefPoint;
TextAlign meTextAlign;
RasterOp meRasterOp;
ULONG mnTextLayoutMode;
LanguageType meTextLanguage;
USHORT mnFlags;
ImplObjStack()
{
mpPrev = NULL;
mpMapMode = NULL;
mpClipRegion = NULL;
mpLineColor = NULL;
mpFillColor = NULL;
mpFont = NULL;
mpTextColor = NULL;
mpTextFillColor = NULL;
mpTextLineColor = NULL;
mpOverlineColor = NULL;
mpRefPoint = NULL;
}
};
// -----------------------------------------------------------------------
static void ImplDeleteObjStack( ImplObjStack* pObjStack )
{
if ( pObjStack->mnFlags & PUSH_LINECOLOR )
{
if ( pObjStack->mpLineColor )
delete pObjStack->mpLineColor;
}
if ( pObjStack->mnFlags & PUSH_FILLCOLOR )
{
if ( pObjStack->mpFillColor )
delete pObjStack->mpFillColor;
}
if ( pObjStack->mnFlags & PUSH_FONT )
delete pObjStack->mpFont;
if ( pObjStack->mnFlags & PUSH_TEXTCOLOR )
delete pObjStack->mpTextColor;
if ( pObjStack->mnFlags & PUSH_TEXTFILLCOLOR )
{
if ( pObjStack->mpTextFillColor )
delete pObjStack->mpTextFillColor;
}
if ( pObjStack->mnFlags & PUSH_TEXTLINECOLOR )
{
if ( pObjStack->mpTextLineColor )
delete pObjStack->mpTextLineColor;
}
if ( pObjStack->mnFlags & PUSH_OVERLINECOLOR )
{
if ( pObjStack->mpOverlineColor )
delete pObjStack->mpOverlineColor;
}
if ( pObjStack->mnFlags & PUSH_MAPMODE )
{
if ( pObjStack->mpMapMode )
delete pObjStack->mpMapMode;
}
if ( pObjStack->mnFlags & PUSH_CLIPREGION )
{
if ( pObjStack->mpClipRegion )
delete pObjStack->mpClipRegion;
}
if ( pObjStack->mnFlags & PUSH_REFPOINT )
{
if ( pObjStack->mpRefPoint )
delete pObjStack->mpRefPoint;
}
delete pObjStack;
}
inline UINT8 ImplGetGradientColorValue( long nValue )
{
if ( nValue < 0 )
return 0;
else if ( nValue > 0xFF )
return 0xFF;
else
return (UINT8)nValue;
}
// ----------------
// - Cmp-Function -
// ----------------
int ImplHatchCmpFnc( const void* p1, const void* p2 )
{
const long nX1 = ( (Point*) p1 )->X();
const long nX2 = ( (Point*) p2 )->X();
const long nY1 = ( (Point*) p1 )->Y();
const long nY2 = ( (Point*) p2 )->Y();
return ( nX1 > nX2 ? 1 : nX1 == nX2 ? nY1 > nY2 ? 1: nY1 == nY2 ? 0 : -1 : -1 );
}
// =======================================================================
ULONG ImplAdjustTwoRect( SalTwoRect& rTwoRect, const Size& rSizePix )
{
ULONG nMirrFlags = 0;
if ( rTwoRect.mnDestWidth < 0 )
{
rTwoRect.mnSrcX = rSizePix.Width() - rTwoRect.mnSrcX - rTwoRect.mnSrcWidth;
rTwoRect.mnDestWidth = -rTwoRect.mnDestWidth;
rTwoRect.mnDestX -= rTwoRect.mnDestWidth-1;
nMirrFlags |= BMP_MIRROR_HORZ;
}
if ( rTwoRect.mnDestHeight < 0 )
{
rTwoRect.mnSrcY = rSizePix.Height() - rTwoRect.mnSrcY - rTwoRect.mnSrcHeight;
rTwoRect.mnDestHeight = -rTwoRect.mnDestHeight;
rTwoRect.mnDestY -= rTwoRect.mnDestHeight-1;
nMirrFlags |= BMP_MIRROR_VERT;
}
if( ( rTwoRect.mnSrcX < 0 ) || ( rTwoRect.mnSrcX >= rSizePix.Width() ) ||
( rTwoRect.mnSrcY < 0 ) || ( rTwoRect.mnSrcY >= rSizePix.Height() ) ||
( ( rTwoRect.mnSrcX + rTwoRect.mnSrcWidth ) > rSizePix.Width() ) ||
( ( rTwoRect.mnSrcY + rTwoRect.mnSrcHeight ) > rSizePix.Height() ) )
{
const Rectangle aSourceRect( Point( rTwoRect.mnSrcX, rTwoRect.mnSrcY ),
Size( rTwoRect.mnSrcWidth, rTwoRect.mnSrcHeight ) );
Rectangle aCropRect( aSourceRect );
aCropRect.Intersection( Rectangle( Point(), rSizePix ) );
if( aCropRect.IsEmpty() )
rTwoRect.mnSrcWidth = rTwoRect.mnSrcHeight = rTwoRect.mnDestWidth = rTwoRect.mnDestHeight = 0;
else
{
const double fFactorX = ( rTwoRect.mnSrcWidth > 1 ) ? (double) ( rTwoRect.mnDestWidth - 1 ) / ( rTwoRect.mnSrcWidth - 1 ) : 0.0;
const double fFactorY = ( rTwoRect.mnSrcHeight > 1 ) ? (double) ( rTwoRect.mnDestHeight - 1 ) / ( rTwoRect.mnSrcHeight - 1 ) : 0.0;
const long nDstX1 = rTwoRect.mnDestX + FRound( fFactorX * ( aCropRect.Left() - rTwoRect.mnSrcX ) );
const long nDstY1 = rTwoRect.mnDestY + FRound( fFactorY * ( aCropRect.Top() - rTwoRect.mnSrcY ) );
const long nDstX2 = rTwoRect.mnDestX + FRound( fFactorX * ( aCropRect.Right() - rTwoRect.mnSrcX ) );
const long nDstY2 = rTwoRect.mnDestY + FRound( fFactorY * ( aCropRect.Bottom() - rTwoRect.mnSrcY ) );
rTwoRect.mnSrcX = aCropRect.Left();
rTwoRect.mnSrcY = aCropRect.Top();
rTwoRect.mnSrcWidth = aCropRect.GetWidth();
rTwoRect.mnSrcHeight = aCropRect.GetHeight();
rTwoRect.mnDestX = nDstX1;
rTwoRect.mnDestY = nDstY1;
rTwoRect.mnDestWidth = nDstX2 - nDstX1 + 1;
rTwoRect.mnDestHeight = nDstY2 - nDstY1 + 1;
}
}
return nMirrFlags;
}
// -----------------------------------------------------------------------
#define OUTDEV_INIT() \
{ \
if ( !mpGraphics ) \
return; \
\
if ( mbInitClipRegion ) \
ImplInitClipRegion(); \
\
if ( mbOutputClipped ) \
return; \
}
// -----------
// - Defines -
// -----------
#define HATCH_MAXPOINTS 1024
#define GRADIENT_DEFAULT_STEPCOUNT 0
// -------------
// - externals -
// -------------
extern ULONG nVCLRLut[ 6 ];
extern ULONG nVCLGLut[ 6 ];
extern ULONG nVCLBLut[ 6 ];
extern ULONG nVCLDitherLut[ 256 ];
extern ULONG nVCLLut[ 256 ];
}//SVMCore
//int OutputDevice::ImplGetGraphics()
//{
// if ( mpGraphics )
// return TRUE;
//
// mbInitLineColor = TRUE;
// mbInitFillColor = TRUE;
// mbInitFont = TRUE;
// mbInitTextColor = TRUE;
// mbInitClipRegion = TRUE;
//
// if ( meOutDevType == OUTDEV_VIRDEV )
// ;//mpGraphics = new WinSalGraphics();
//
// if ( mpGraphics )
// {
// mpGraphics->SetXORMode( (ROP_INVERT == meRasterOp) || (ROP_XOR == meRasterOp), ROP_INVERT == meRasterOp );
// //mpGraphics->setAntiAliasB2DDraw(mnAntialiasing & ANTIALIASING_ENABLE_B2DDRAW);
// return TRUE;
// }
// return FALSE;
//}
void VirtualDevice::ImplInitVirDev( long nDX, long nDY, USHORT nBitCount )
{
if ( nDX < 1 )
nDX = 1;
if ( nDY < 1 )
nDY = 1;
mnBitCount = ( nBitCount ? nBitCount : GetBitCount() );
mnOutWidth = nDX;
mnOutHeight = nDY;
//mbScreenComp = TRUE;
//mnAlphaDepth = -1;
meOutDevType = OUTDEV_VIRDEV;
//mbDevOutput = TRUE;
mpFontList = new ImplDevFontList();
mpFontCache = new ImplFontCache( false );
mpGraphics = GetWinGraphics();
//mnDPIX = pOutDev->mnDPIX;
//mnDPIY = pOutDev->mnDPIY;
//maFont = pOutDev->maFont;
// Virtuelle Devices haben defaultmaessig einen weissen Hintergrund
SetBackground( Wallpaper( Color( COL_WHITE ) ) );
}
OutputDevice::OutputDevice() :
maRegion( REGION_NULL ),
maFillColor( COL_WHITE ),
maTextLineColor( COL_TRANSPARENT )//,
//maSettings( Application::GetSettings() )
{
mpGraphics = NULL;
//mpUnoGraphicsList = NULL;
//mpPrevGraphics = NULL;
//mpNextGraphics = NULL;
//mpMetaFile = NULL;
mpFontEntry = NULL;
mpFontCache = NULL;
mpFontList = NULL;
//mpGetDevFontList = NULL;
//mpGetDevSizeList = NULL;
mpObjStack = NULL;
//mpOutDevData = NULL;
//mpPDFWriter = NULL;
//mpAlphaVDev = NULL;
//mpExtOutDevData = NULL;
mnOutOffX = 0;
mnOutOffY = 0;
mnOutWidth = 0;
mnOutHeight = 0;
mnDPIX = 96;//todo
mnDPIY = 96;//todo
mnTextOffX = 0;
mnTextOffY = 0;
mnOutOffOrigX = 0;
mnOutOffLogicX = 0;
mnOutOffOrigY = 0;
mnOutOffLogicY = 0;
mnEmphasisAscent = 0;
mnEmphasisDescent = 0;
//mnDrawMode = 0;
mnTextLayoutMode = TEXT_LAYOUT_DEFAULT;
#ifdef AVS
if( Application::GetSettings().GetLayoutRTL() ) //#i84553# tip BiDi preference to RTL
mnTextLayoutMode = TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_TEXTORIGIN_LEFT;
#endif
meOutDevType = OUTDEV_VIRDEV;
//meOutDevViewType = OUTDEV_VIEWTYPE_DONTKNOW;
mbMap = FALSE;
//mbMapIsDefault = TRUE;
mbClipRegion = FALSE;
mbBackground = FALSE;
//mbOutput = TRUE;
//mbDevOutput = FALSE;
mbOutputClipped = FALSE;
maTextColor = Color( COL_BLACK );
maOverlineColor = Color( COL_TRANSPARENT );
//meTextAlign = maFont.GetAlign();
meRasterOp = ROP_OVERPAINT;
mnAntialiasing = 0;
meTextLanguage = 0; // TODO: get default from configuration?
mbLineColor = TRUE;
mbFillColor = TRUE;
mbInitLineColor = TRUE;
mbInitFillColor = TRUE;
mbInitFont = TRUE;
mbInitTextColor = TRUE;
mbInitClipRegion = TRUE;
mbClipRegionSet = FALSE;
mbKerning = FALSE;
mbNewFont = TRUE;
mbTextLines = FALSE;
mbTextSpecial = FALSE;
mbRefPoint = FALSE;
mbEnableRTL = FALSE; // mirroring must be explicitly allowed (typically for windows only)
// struct ImplMapRes
maMapRes.mnMapOfsX = 0;
maMapRes.mnMapOfsY = 0;
maMapRes.mnMapScNumX = 1;
maMapRes.mnMapScNumY = 1;
maMapRes.mnMapScDenomX = 1;
maMapRes.mnMapScDenomY = 1;
// struct ImplThresholdRes
maThresRes.mnThresLogToPixX = 0;
maThresRes.mnThresLogToPixY = 0;
maThresRes.mnThresPixToLogX = 0;
maThresRes.mnThresPixToLogY = 0;
ImplInitVirDev( 1, 1, 0 );
}
// -----------------------------------------------------------------------
OutputDevice::~OutputDevice()
{
#ifdef AVS
if ( GetUnoGraphicsList() )
{
UnoWrapperBase* pWrapper = Application::GetUnoWrapper( FALSE );
if ( pWrapper )
pWrapper->ReleaseAllGraphics( this );
delete mpUnoGraphicsList;
mpUnoGraphicsList = NULL;
}
if ( mpOutDevData )
ImplDeInitOutDevData();
#endif
ImplObjStack* pData = mpObjStack;
if ( pData )
{
while ( pData )
{
ImplObjStack* pTemp = pData;
pData = pData->mpPrev;
ImplDeleteObjStack( pTemp );
}
}
#ifdef AVS
// release the active font instance
if( mpFontEntry )
mpFontCache->Release( mpFontEntry );
// remove cached results of GetDevFontList/GetDevSizeList
// TODO: use smart pointers for them
if( mpGetDevFontList )
delete mpGetDevFontList;
if( mpGetDevSizeList )
delete mpGetDevSizeList;
// release ImplFontCache specific to this OutputDevice
// TODO: refcount ImplFontCache
if( mpFontCache
&& (mpFontCache != ImplGetSVData()->maGDIData.mpScreenFontCache)
&& (ImplGetSVData()->maGDIData.mpScreenFontCache != NULL) )
{
delete mpFontCache;
mpFontCache = NULL;
}
// release ImplFontList specific to this OutputDevice
// TODO: refcount ImplFontList
if( mpFontList
&& (mpFontList != ImplGetSVData()->maGDIData.mpScreenFontList)
&& (ImplGetSVData()->maGDIData.mpScreenFontList != NULL) )
{
mpFontList->Clear();
delete mpFontList;
mpFontList = NULL;
}
delete mpAlphaVDev;
#endif
#ifndef AVS_INSERT
delete mpFontList;
delete mpFontCache;
delete mpGraphics;
#endif
}
void OutputDevice::ImplInitClipRegion()
{
if ( mbClipRegion )
{
if ( maRegion.IsEmpty() )
mbOutputClipped = TRUE;
else
{
mbOutputClipped = FALSE;
ImplSelectClipRegion(
// #102532# Respect output offset also for clip region
ImplPixelToDevicePixel( maRegion ) );
}
mbClipRegionSet = TRUE;
}
else
{
if ( mbClipRegionSet )
{
mpGraphics->ResetClipRegion();
mbClipRegionSet = FALSE;
}
mbOutputClipped = FALSE;
}
mbInitClipRegion = FALSE;
}
bool OutputDevice::ImplSelectClipRegion( const Region& rRegion, SalGraphics* pGraphics )
{
// TODO(Q3): Change from static to plain method - everybody's
// calling it with pOutDev=this!
// => done, but only with minimal changes for now => TODO
OutputDevice* const pOutDev = this;
if( !pGraphics )
{
if( !mpGraphics )
return false;
pGraphics = mpGraphics;
}
long nX;
long nY;
long nWidth;
long nHeight;
ULONG nRectCount;
ImplRegionInfo aInfo;
BOOL bRegionRect;
BOOL bClipRegion = TRUE;
const BOOL bClipDeviceBounds = TRUE;//( !pOutDev->GetPDFWriter() && pOutDev->GetOutDevType() != OUTDEV_PRINTER );
nRectCount = rRegion.GetRectCount();
pGraphics->BeginSetClipRegion( nRectCount );
bRegionRect = rRegion.ImplGetFirstRect( aInfo, nX, nY, nWidth, nHeight );
if( bClipDeviceBounds )
{
// #b6520266# Perform actual rect clip against outdev
// dimensions, to generate empty clips whenever one of the
// values is completely off the device.
const long nOffX( pOutDev->mnOutOffX );
const long nOffY( pOutDev->mnOutOffY );
const long nDeviceWidth( pOutDev->GetOutputWidthPixel() );
const long nDeviceHeight( pOutDev->GetOutputHeightPixel() );
Rectangle aDeviceBounds( nOffX, nOffY,
nOffX+nDeviceWidth-1,
nOffY+nDeviceHeight-1 );
while ( bRegionRect )
{
// #i59315# Limit coordinates passed to sal layer to actual
// outdev dimensions - everything else bears the risk of
// overflowing internal coordinates (e.g. the 16 bit wire
// format of X11).
Rectangle aTmpRect(nX,nY,nX+nWidth-1,nY+nHeight-1);
aTmpRect.Intersection(aDeviceBounds);
if( !aTmpRect.IsEmpty() )
{
if ( !pGraphics->UnionClipRegion( aTmpRect.Left(),
aTmpRect.Top(),
aTmpRect.GetWidth(),
aTmpRect.GetHeight(),
pOutDev ) )
{
bClipRegion = FALSE;
}
}
else
{
//#i79850# Fake off-screen clip
if ( !pGraphics->UnionClipRegion( nDeviceWidth+1,
nDeviceHeight+1,
1, 1,
pOutDev ) )
{
bClipRegion = FALSE;
}
}
//DBG_ASSERT( bClipRegion, "OutputDevice::ImplSelectClipRegion() - can't create region" );
bRegionRect = rRegion.ImplGetNextRect( aInfo, nX, nY, nWidth, nHeight );
}
}
else
{
// #i65720# Actually, _don't_ clip anything on printer or PDF
// export, since output might be visible outside the specified
// device boundaries.
while ( bRegionRect )
{
if ( !pGraphics->UnionClipRegion( nX, nY, nWidth, nHeight, pOutDev ) )
bClipRegion = FALSE;
//DBG_ASSERT( bClipRegion, "OutputDevice::ImplSelectClipRegion() - can't cerate region" );
bRegionRect = rRegion.ImplGetNextRect( aInfo, nX, nY, nWidth, nHeight );
}
}
pGraphics->EndSetClipRegion();
return bClipRegion;
}
void OutputDevice::ImplInitLineColor()
{
//DBG_TESTSOLARMUTEX();
if( mbLineColor )
{
if( ROP_0 == meRasterOp )
mpGraphics->SetROPLineColor( SAL_ROP_0 );
else if( ROP_1 == meRasterOp )
mpGraphics->SetROPLineColor( SAL_ROP_1 );
else if( ROP_INVERT == meRasterOp )
mpGraphics->SetROPLineColor( SAL_ROP_INVERT );
else
mpGraphics->SetLineColor( ImplColorToSal( maLineColor ) );
}
else
mpGraphics->SetLineColor();
mbInitLineColor = FALSE;
}
void OutputDevice::ImplInitFillColor()
{
if( mbFillColor )
{
if( ROP_0 == meRasterOp )
mpGraphics->SetROPFillColor( SAL_ROP_0 );
else if( ROP_1 == meRasterOp )
mpGraphics->SetROPFillColor( SAL_ROP_1 );
else if( ROP_INVERT == meRasterOp )
mpGraphics->SetROPFillColor( SAL_ROP_INVERT );
else
mpGraphics->SetFillColor( ImplColorToSal( maFillColor ) );
}
else
mpGraphics->SetFillColor();
mbInitFillColor = FALSE;
}
Polygon OutputDevice::ImplSubdivideBezier( const Polygon& rPoly )
{
Polygon aPoly;
// #100127# Use adaptive subdivide instead of fixed 25 segments
rPoly.AdaptiveSubdivide( aPoly );
return aPoly;
}
// =======================================================================
PolyPolygon OutputDevice::ImplSubdivideBezier( const PolyPolygon& rPolyPoly )
{
USHORT i, nPolys = rPolyPoly.Count();
PolyPolygon aPolyPoly( nPolys );
for( i=0; i<nPolys; ++i )
aPolyPoly.Insert( ImplSubdivideBezier( rPolyPoly.GetObject(i) ) );
return aPolyPoly;
}
void OutputDevice::DrawPixel( const Point& rPt, const Color& rColor )
{
Color aColor( rColor );
Point aPt = ImplLogicToDevicePixel( rPt );
// we need a graphics
if ( !mpGraphics )
return;
if ( mbInitClipRegion )
ImplInitClipRegion();
if ( mbOutputClipped )
return;
mpGraphics->DrawPixel( aPt.X(), aPt.Y(), ImplColorToSal( rColor ), this );
}
void OutputDevice::DrawPixel( const Point& rPt )
{
Point aPt = ImplLogicToDevicePixel( rPt );
// we need a graphics
if ( !mpGraphics )
return;
if ( mbInitClipRegion )
ImplInitClipRegion();
if ( mbOutputClipped )
return;
if ( mbInitLineColor )
ImplInitLineColor();
mpGraphics->DrawPixel( aPt.X(), aPt.Y(), this );
}
void OutputDevice::DrawLine( const Point& rStartPt, const Point& rEndPt )
{
if ( !mbLineColor )
return;
if ( !mpGraphics )
return;
if ( mbInitClipRegion )
ImplInitClipRegion();
if ( mbOutputClipped )
return;
if ( mbInitLineColor )
ImplInitLineColor();
Point aStartPt = ImplLogicToDevicePixel( rStartPt );
Point aEndPt = ImplLogicToDevicePixel( rEndPt );
mpGraphics->DrawLine( aStartPt.X(), aStartPt.Y(), aEndPt.X(), aEndPt.Y(), this );
}
void OutputDevice::DrawLine( const Point& rStartPt, const Point& rEndPt, const LineInfo& rLineInfo )
{
if ( rLineInfo.IsDefault() )
{
DrawLine( rStartPt, rEndPt );
return;
}
if ( !mbLineColor || ( LINE_NONE == rLineInfo.GetStyle() ) )
return;
if( !mpGraphics )
return;
if ( mbInitClipRegion )
ImplInitClipRegion();
if ( mbOutputClipped )
return;
const LineInfo aInfo( ImplLogicToDevicePixel( rLineInfo ) );
if( ( aInfo.GetWidth() > 1L ) || ( LINE_DASH == aInfo.GetStyle() ) )
{
Polygon aPoly( 2 ); aPoly[ 0 ] = rStartPt; aPoly[ 1 ] = rEndPt;
ImplLineConverter aLineCvt( ImplLogicToDevicePixel( aPoly ), aInfo, ( mbRefPoint ) ? &maRefPoint : NULL );
if ( aInfo.GetWidth() > 1 )
{
const Color aOldLineColor( maLineColor );
const Color aOldFillColor( maFillColor );
SetLineColor();
ImplInitLineColor();
SetFillColor( aOldLineColor );
ImplInitFillColor();
for( const Polygon* pPoly = aLineCvt.ImplGetFirst(); pPoly; pPoly = aLineCvt.ImplGetNext() )
mpGraphics->DrawPolygon( pPoly->GetSize(), (const SalPoint*) pPoly->GetConstPointAry(), this );
SetFillColor( aOldFillColor );
SetLineColor( aOldLineColor );
}
else
{
if ( mbInitLineColor )
ImplInitLineColor();
for ( const Polygon* pPoly = aLineCvt.ImplGetFirst(); pPoly; pPoly = aLineCvt.ImplGetNext() )
mpGraphics->DrawLine( (*pPoly)[ 0 ].X(), (*pPoly)[ 0 ].Y(), (*pPoly)[ 1 ].X(), (*pPoly)[ 1 ].Y(), this );
}
}
else
{
const Point aStartPt( ImplLogicToDevicePixel( rStartPt ) );
const Point aEndPt( ImplLogicToDevicePixel( rEndPt ) );
if ( mbInitLineColor )
ImplInitLineColor();
mpGraphics->DrawLine( aStartPt.X(), aStartPt.Y(), aEndPt.X(), aEndPt.Y(), this );
}
}
void OutputDevice::DrawRect( const Rectangle& rRect )
{
if ( (!mbLineColor && !mbFillColor) )
return;
Rectangle aRect( ImplLogicToDevicePixel( rRect ) );
if ( aRect.IsEmpty() )
return;
aRect.Justify();
if ( !mpGraphics )
return;
if ( mbInitClipRegion )
ImplInitClipRegion();
if ( mbOutputClipped )
return;
if ( mbInitLineColor )
ImplInitLineColor();
if ( mbInitFillColor )
ImplInitFillColor();
mpGraphics->DrawRect( aRect.Left(), aRect.Top(), aRect.GetWidth(), aRect.GetHeight(), this );
}
void OutputDevice::DrawRect( const Rectangle& rRect, ULONG nHorzRound, ULONG nVertRound )
{
if ( !mbLineColor && !mbFillColor )
return;
const Rectangle aRect( ImplLogicToDevicePixel( rRect ) );
if ( aRect.IsEmpty() )
return;
nHorzRound = ImplLogicWidthToDevicePixel( nHorzRound );
nVertRound = ImplLogicHeightToDevicePixel( nVertRound );
// we need a graphics
if ( !mpGraphics )
return;
if ( mbInitClipRegion )
ImplInitClipRegion();
if ( mbOutputClipped )
return;
if ( mbInitLineColor )
ImplInitLineColor();
if ( mbInitFillColor )
ImplInitFillColor();
if ( !nHorzRound && !nVertRound )
mpGraphics->DrawRect( aRect.Left(), aRect.Top(), aRect.GetWidth(), aRect.GetHeight(), this );
else
{
const Polygon aRoundRectPoly( aRect, nHorzRound, nVertRound );
if ( aRoundRectPoly.GetSize() >= 2 )
{
const SalPoint* pPtAry = (const SalPoint*)aRoundRectPoly.GetConstPointAry();
if ( !mbFillColor )
mpGraphics->DrawPolyLine( aRoundRectPoly.GetSize(), pPtAry, this );
else
mpGraphics->DrawPolygon( aRoundRectPoly.GetSize(), pPtAry, this );
}
}
}
void OutputDevice::DrawEllipse( const Rectangle& rRect )
{
if ( !mbLineColor && !mbFillColor )
return;
Rectangle aRect( ImplLogicToDevicePixel( rRect ) );
if ( aRect.IsEmpty() )
return;
// we need a graphics
if ( !mpGraphics )
return;
if ( mbInitClipRegion )
ImplInitClipRegion();
if ( mbOutputClipped )
return;
if ( mbInitLineColor )
ImplInitLineColor();
Polygon aRectPoly( aRect.Center(), aRect.GetWidth() >> 1, aRect.GetHeight() >> 1 );
if ( aRectPoly.GetSize() >= 2 )
{
const SalPoint* pPtAry = (const SalPoint*)aRectPoly.GetConstPointAry();
if ( !mbFillColor )
mpGraphics->DrawPolyLine( aRectPoly.GetSize(), pPtAry, this );
else
{
if ( mbInitFillColor )
ImplInitFillColor();
mpGraphics->DrawPolygon( aRectPoly.GetSize(), pPtAry, this );
}
}
}
void OutputDevice::DrawArc( const Rectangle& rRect, const Point& rStartPt, const Point& rEndPt )
{
if ( !mbLineColor )
return;
Rectangle aRect( ImplLogicToDevicePixel( rRect ) );
if ( aRect.IsEmpty() )
return;
// we need a graphics
if ( !mpGraphics )
return;
if ( mbInitClipRegion )
ImplInitClipRegion();
if ( mbOutputClipped )
return;
if ( mbInitLineColor )
ImplInitLineColor();
const Point aStart( ImplLogicToDevicePixel( rStartPt ) );
const Point aEnd( ImplLogicToDevicePixel( rEndPt ) );
Polygon aArcPoly( aRect, aStart, aEnd, POLY_ARC );
if ( aArcPoly.GetSize() >= 2 )
{
const SalPoint* pPtAry = (const SalPoint*)aArcPoly.GetConstPointAry();
mpGraphics->DrawPolyLine( aArcPoly.GetSize(), pPtAry, this );
}
}
void OutputDevice::DrawPie( const Rectangle& rRect, const Point& rStartPt, const Point& rEndPt )
{
if ( !mbLineColor && !mbFillColor )
return;
Rectangle aRect( ImplLogicToDevicePixel( rRect ) );
if ( aRect.IsEmpty() )
return;
// we need a graphics
if ( !mpGraphics )
return;
if ( mbInitClipRegion )
ImplInitClipRegion();
if ( mbOutputClipped )
return;
if ( mbInitLineColor )
ImplInitLineColor();
const Point aStart( ImplLogicToDevicePixel( rStartPt ) );
const Point aEnd( ImplLogicToDevicePixel( rEndPt ) );
Polygon aPiePoly( aRect, aStart, aEnd, POLY_PIE );
if ( aPiePoly.GetSize() >= 2 )
{
const SalPoint* pPtAry = (const SalPoint*)aPiePoly.GetConstPointAry();
if ( !mbFillColor )
mpGraphics->DrawPolyLine( aPiePoly.GetSize(), pPtAry, this );
else
{
if ( mbInitFillColor )
ImplInitFillColor();
mpGraphics->DrawPolygon( aPiePoly.GetSize(), pPtAry, this );
}
}
}
void OutputDevice::DrawChord( const Rectangle& rRect, const Point& rStartPt, const Point& rEndPt )
{
if ( !mbLineColor && !mbFillColor )
return;
Rectangle aRect( ImplLogicToDevicePixel( rRect ) );
if ( aRect.IsEmpty() )
return;
// we need a graphics
if ( !mpGraphics )
return;
if ( mbInitClipRegion )
ImplInitClipRegion();
if ( mbOutputClipped )
return;
if ( mbInitLineColor )
ImplInitLineColor();
const Point aStart( ImplLogicToDevicePixel( rStartPt ) );
const Point aEnd( ImplLogicToDevicePixel( rEndPt ) );
Polygon aChordPoly( aRect, aStart, aEnd, POLY_CHORD );
if ( aChordPoly.GetSize() >= 2 )
{
const SalPoint* pPtAry = (const SalPoint*)aChordPoly.GetConstPointAry();
if ( !mbFillColor )
mpGraphics->DrawPolyLine( aChordPoly.GetSize(), pPtAry, this );
else
{
if ( mbInitFillColor )
ImplInitFillColor();
mpGraphics->DrawPolygon( aChordPoly.GetSize(), pPtAry, this );
}
}
}
void OutputDevice::DrawPolyLine( const Polygon& rPoly )
{
USHORT nPoints = rPoly.GetSize();
if ( !mbLineColor || (nPoints < 2) )
return;
// we need a graphics
if ( !mpGraphics )
return;
if ( mbInitClipRegion )
ImplInitClipRegion();
if ( mbOutputClipped )
return;
if ( mbInitLineColor )
ImplInitLineColor();
Polygon aPoly = ImplLogicToDevicePixel( rPoly );
const SalPoint* pPtAry = (const SalPoint*)aPoly.GetConstPointAry();
// #100127# Forward beziers to sal, if any
if( aPoly.HasFlags() )
{
const BYTE* pFlgAry = aPoly.GetConstFlagAry();
if( !mpGraphics->DrawPolyLineBezier( nPoints, pPtAry, pFlgAry, this ) )
{
aPoly = ImplSubdivideBezier(aPoly);
pPtAry = (const SalPoint*)aPoly.GetConstPointAry();
mpGraphics->DrawPolyLine( aPoly.GetSize(), pPtAry, this );
}
}
else
{
mpGraphics->DrawPolyLine( nPoints, pPtAry, this );
}
}
void OutputDevice::DrawPolyLine( const Polygon& rPoly, const LineInfo& rLineInfo )
{
if ( rLineInfo.IsDefault() )
{
DrawPolyLine( rPoly );
return;
}
#ifdef AVS
// #i101491#
// Try direct Fallback to B2D-Version of DrawPolyLine
if((mnAntialiasing & ANTIALIASING_ENABLE_B2DDRAW)
&& LINE_SOLID == rLineInfo.GetStyle())
{
DrawPolyLine(rPoly.getB2DPolygon(), (double)rLineInfo.GetWidth(), basegfx::B2DLINEJOIN_ROUND);
return;
}
#endif
ImpDrawPolyLineWithLineInfo(rPoly, rLineInfo);
}
void OutputDevice::ImpDrawPolyLineWithLineInfo(const Polygon& rPoly, const LineInfo& rLineInfo)
{
USHORT nPoints = rPoly.GetSize();
if ( !mbLineColor || ( nPoints < 2 ) || ( LINE_NONE == rLineInfo.GetStyle() ) )
return;
Polygon aPoly = ImplLogicToDevicePixel( rPoly );
// #100127# LineInfo is not curve-safe, subdivide always
if( aPoly.HasFlags() )
{
aPoly = ImplSubdivideBezier( aPoly );
nPoints = aPoly.GetSize();
}
// we need a graphics
if ( !mpGraphics )
return;
if ( mbInitClipRegion )
ImplInitClipRegion();
if ( mbOutputClipped )
return;
const LineInfo aInfo( ImplLogicToDevicePixel( rLineInfo ) );
if( aInfo.GetWidth() > 1L )
{
const Color aOldLineColor( maLineColor );
const Color aOldFillColor( maFillColor );
ImplLineConverter aLineCvt( aPoly, aInfo, ( mbRefPoint ) ? &maRefPoint : NULL );
SetLineColor();
ImplInitLineColor();
SetFillColor( aOldLineColor );
ImplInitFillColor();
bool bDone(false);
if(!bDone)
{
for( const Polygon* pPoly = aLineCvt.ImplGetFirst(); pPoly; pPoly = aLineCvt.ImplGetNext() )
{
mpGraphics->DrawPolygon( pPoly->GetSize(), (const SalPoint*) pPoly->GetConstPointAry(), this );
}
}
SetLineColor( aOldLineColor );
SetFillColor( aOldFillColor );
}
else
{
if ( mbInitLineColor )
ImplInitLineColor();
if ( LINE_DASH == aInfo.GetStyle() )
{
ImplLineConverter aLineCvt( aPoly, aInfo, ( mbRefPoint ) ? &maRefPoint : NULL );
for( const Polygon* pPoly = aLineCvt.ImplGetFirst(); pPoly; pPoly = aLineCvt.ImplGetNext() )
mpGraphics->DrawPolyLine( pPoly->GetSize(), (const SalPoint*)pPoly->GetConstPointAry(), this );
}
else
mpGraphics->DrawPolyLine( nPoints, (const SalPoint*) aPoly.GetConstPointAry(), this );
}
}
void OutputDevice::DrawPolygon( const Polygon& rPoly )
{
USHORT nPoints = rPoly.GetSize();
if ( (!mbLineColor && !mbFillColor) || (nPoints < 2) )
return;
// we need a graphics
if ( !mpGraphics )
return;
if ( mbInitClipRegion )
ImplInitClipRegion();
if ( mbOutputClipped )
return;
if ( mbInitLineColor )
ImplInitLineColor();
if ( mbInitFillColor )
ImplInitFillColor();
Polygon aPoly = ImplLogicToDevicePixel( rPoly );
const SalPoint* pPtAry = (const SalPoint*)aPoly.GetConstPointAry();
// #100127# Forward beziers to sal, if any
if( aPoly.HasFlags() )
{
const BYTE* pFlgAry = aPoly.GetConstFlagAry();
if( !mpGraphics->DrawPolygonBezier( nPoints, pPtAry, pFlgAry, this ) )
{
aPoly = ImplSubdivideBezier(aPoly);
pPtAry = (const SalPoint*)aPoly.GetConstPointAry();
mpGraphics->DrawPolygon( aPoly.GetSize(), pPtAry, this );
}
}
else
{
mpGraphics->DrawPolygon( nPoints, pPtAry, this );
}
}
// =======================================================================
void OutputDevice::ImplDrawPolygon( const Polygon& rPoly, const PolyPolygon* pClipPolyPoly )
{
if( pClipPolyPoly )
ImplDrawPolyPolygon( rPoly, pClipPolyPoly );
else
{
USHORT nPoints = rPoly.GetSize();
if ( nPoints < 2 )
return;
const SalPoint* pPtAry = (const SalPoint*)rPoly.GetConstPointAry();
mpGraphics->DrawPolygon( nPoints, pPtAry, this );
}
}
void OutputDevice::ImplDrawPolyPolygon( const PolyPolygon& rPolyPoly, const PolyPolygon* pClipPolyPoly )
{
PolyPolygon* pPolyPoly;
if( pClipPolyPoly )
{
pPolyPoly = new PolyPolygon;
rPolyPoly.GetIntersection( *pClipPolyPoly, *pPolyPoly );
}
else
pPolyPoly = (PolyPolygon*) &rPolyPoly;
if( pPolyPoly->Count() == 1 )
{
const Polygon rPoly = pPolyPoly->GetObject( 0 );
USHORT nSize = rPoly.GetSize();
if( nSize >= 2 )
{
const SalPoint* pPtAry = (const SalPoint*)rPoly.GetConstPointAry();
mpGraphics->DrawPolygon( nSize, pPtAry, this );
}
}
else if( pPolyPoly->Count() )
{
USHORT nCount = pPolyPoly->Count();
sal_uInt32* pPointAry = new sal_uInt32[nCount];
PCONSTSALPOINT* pPointAryAry = new PCONSTSALPOINT[nCount];
USHORT i = 0;
do
{
const Polygon& rPoly = pPolyPoly->GetObject( i );
USHORT nSize = rPoly.GetSize();
if ( nSize )
{
pPointAry[i] = nSize;
pPointAryAry[i] = (PCONSTSALPOINT)rPoly.GetConstPointAry();
i++;
}
else
nCount--;
}
while( i < nCount );
if( nCount == 1 )
mpGraphics->DrawPolygon( *pPointAry, *pPointAryAry, this );
else
mpGraphics->DrawPolyPolygon( nCount, pPointAry, pPointAryAry, this );
delete[] pPointAry;
delete[] pPointAryAry;
}
if( pClipPolyPoly )
delete pPolyPoly;
}
void OutputDevice::DrawPolyPolygon( const PolyPolygon& rPolyPoly )
{
USHORT nPoly = rPolyPoly.Count();
if ( (!mbLineColor && !mbFillColor) || !nPoly )
return;
// we need a graphics
if ( !mpGraphics )
return;
if ( mbInitClipRegion )
ImplInitClipRegion();
if ( mbOutputClipped )
return;
if ( mbInitLineColor )
ImplInitLineColor();
if ( mbInitFillColor )
ImplInitFillColor();
if ( nPoly == 1 )
{
// #100127# Map to DrawPolygon
Polygon aPoly = rPolyPoly.GetObject( 0 );
if( aPoly.GetSize() >= 2 )
DrawPolygon( aPoly );
}
else
{
// #100127# moved real PolyPolygon draw to separate method,
// have to call recursively, avoiding duplicate
// ImplLogicToDevicePixel calls
ImplDrawPolyPolygon( nPoly, ImplLogicToDevicePixel( rPolyPoly ) );
}
}
// #100127# Extracted from OutputDevice::DrawPolyPolygon()
void OutputDevice::ImplDrawPolyPolygon( USHORT nPoly, const PolyPolygon& rPolyPoly )
{
// AW: This crashes on empty PolyPolygons, avoid that
if(!nPoly)
return;
sal_uInt32 aStackAry1[OUTDEV_POLYPOLY_STACKBUF];
PCONSTSALPOINT aStackAry2[OUTDEV_POLYPOLY_STACKBUF];
BYTE* aStackAry3[OUTDEV_POLYPOLY_STACKBUF];
sal_uInt32* pPointAry;
PCONSTSALPOINT* pPointAryAry;
const BYTE** pFlagAryAry;
USHORT i = 0, j = 0, last = 0;
BOOL bHaveBezier = sal_False;
if ( nPoly > OUTDEV_POLYPOLY_STACKBUF )
{
pPointAry = new sal_uInt32[nPoly];
pPointAryAry = new PCONSTSALPOINT[nPoly];
pFlagAryAry = new const BYTE*[nPoly];
}
else
{
pPointAry = aStackAry1;
pPointAryAry = aStackAry2;
pFlagAryAry = (const BYTE**)aStackAry3;
}
do
{
const Polygon& rPoly = rPolyPoly.GetObject( i );
USHORT nSize = rPoly.GetSize();
if ( nSize )
{
pPointAry[j] = nSize;
pPointAryAry[j] = (PCONSTSALPOINT)rPoly.GetConstPointAry();
pFlagAryAry[j] = rPoly.GetConstFlagAry();
last = i;
if( pFlagAryAry[j] )
bHaveBezier = sal_True;
++j;
}
++i;
}
while ( i < nPoly );
if ( j == 1 )
{
// #100127# Forward beziers to sal, if any
if( bHaveBezier )
{
if( !mpGraphics->DrawPolygonBezier( *pPointAry, *pPointAryAry, *pFlagAryAry, this ) )
{
Polygon aPoly = ImplSubdivideBezier( rPolyPoly.GetObject( last ) );
mpGraphics->DrawPolygon( aPoly.GetSize(), (const SalPoint*)aPoly.GetConstPointAry(), this );
}
}
else
{
mpGraphics->DrawPolygon( *pPointAry, *pPointAryAry, this );
}
}
else
{
// #100127# Forward beziers to sal, if any
if( bHaveBezier )
{
if( !mpGraphics->DrawPolyPolygonBezier( j, pPointAry, pPointAryAry, pFlagAryAry, this ) )
{
PolyPolygon aPolyPoly = ImplSubdivideBezier( rPolyPoly );
ImplDrawPolyPolygon( aPolyPoly.Count(), aPolyPoly );
}
}
else
{
mpGraphics->DrawPolyPolygon( j, pPointAry, pPointAryAry, this );
}
}
if ( pPointAry != aStackAry1 )
{
delete[] pPointAry;
delete[] pPointAryAry;
delete[] pFlagAryAry;
}
}
// -----------------------------------------------------------------------
void OutputDevice::ImplDrawText( const Rectangle& rRect,
const String& rOrigStr, USHORT nStyle,
MetricVector* pVector, String* pDisplayText )
{
Color aOldTextColor;
Color aOldTextFillColor;
BOOL bRestoreFillColor = false;
#ifdef AVS
if ( (nStyle & TEXT_DRAW_DISABLE) && ! pVector )
{
BOOL bHighContrastBlack = FALSE;
BOOL bHighContrastWhite = FALSE;
Color aCol;
if( IsBackground() )
aCol = GetBackground().GetColor();
else
// best guess is the face color here
// but it may be totally wrong. the background color
// was typically already reset
aCol = GetSettings().GetStyleSettings().GetFaceColor();
bHighContrastBlack = aCol.IsDark();
bHighContrastWhite = aCol.IsBright() && GetSettings().GetStyleSettings().GetHighContrastMode();
aOldTextColor = GetTextColor();
if ( IsTextFillColor() )
{
bRestoreFillColor = TRUE;
aOldTextFillColor = GetTextFillColor();
}
else
bRestoreFillColor = FALSE;
if( bHighContrastBlack )
SetTextColor( COL_GREEN );
else if( bHighContrastWhite )
SetTextColor( COL_LIGHTGREEN );
else
{
// draw disabled text always without shadow
// as it fits better with native look
/*
SetTextColor( GetSettings().GetStyleSettings().GetLightColor() );
Rectangle aRect = rRect;
aRect.Move( 1, 1 );
DrawText( aRect, rOrigStr, nStyle & ~TEXT_DRAW_DISABLE );
*/
SetTextColor( GetSettings().GetStyleSettings().GetShadowColor() );
}
}
#endif
long nWidth = rRect.GetWidth();
long nHeight = rRect.GetHeight();
if ( ((nWidth <= 0) || (nHeight <= 0)) && (nStyle & TEXT_DRAW_CLIP) )
return;
Point aPos = rRect.TopLeft();
long nTextHeight = GetTextHeight();
TextAlign eAlign = GetTextAlign();
xub_StrLen nMnemonicPos = STRING_NOTFOUND;
String aStr = rOrigStr;
if ( nStyle & TEXT_DRAW_MNEMONIC )
aStr = GetNonMnemonicString( aStr, nMnemonicPos );
#ifdef AVS
// Mehrzeiligen Text behandeln wir anders
if ( nStyle & TEXT_DRAW_MULTILINE )
{
XubString aLastLine;
ImplMultiTextLineInfo aMultiLineInfo;
ImplTextLineInfo* pLineInfo;
long nMaxTextWidth;
xub_StrLen i;
xub_StrLen nLines;
xub_StrLen nFormatLines;
if ( nTextHeight )
{
nMaxTextWidth = ImplGetTextLines( aMultiLineInfo, nWidth, aStr, nStyle );
nLines = (xub_StrLen)(nHeight/nTextHeight);
nFormatLines = aMultiLineInfo.Count();
if ( !nLines )
nLines = 1;
if ( nFormatLines > nLines )
{
if ( nStyle & TEXT_DRAW_ENDELLIPSIS )
{
// Letzte Zeile zusammenbauen und kuerzen
nFormatLines = nLines-1;
pLineInfo = aMultiLineInfo.GetLine( nFormatLines );
aLastLine = aStr.Copy( pLineInfo->GetIndex() );
aLastLine.ConvertLineEnd( LINEEND_LF );
// Alle LineFeed's durch Spaces ersetzen
xub_StrLen nLastLineLen = aLastLine.Len();
for ( i = 0; i < nLastLineLen; i++ )
{
if ( aLastLine.GetChar( i ) == _LF )
aLastLine.SetChar( i, ' ' );
}
aLastLine = GetEllipsisString( aLastLine, nWidth, nStyle );
nStyle &= ~(TEXT_DRAW_VCENTER | TEXT_DRAW_BOTTOM);
nStyle |= TEXT_DRAW_TOP;
}
}
else
{
if ( nMaxTextWidth <= nWidth )
nStyle &= ~TEXT_DRAW_CLIP;
}
// Muss in der Hoehe geclippt werden?
if ( nFormatLines*nTextHeight > nHeight )
nStyle |= TEXT_DRAW_CLIP;
// Clipping setzen
if ( nStyle & TEXT_DRAW_CLIP )
{
Push( PUSH_CLIPREGION );
IntersectClipRegion( rRect );
}
// Vertikales Alignment
if ( nStyle & TEXT_DRAW_BOTTOM )
aPos.Y() += nHeight-(nFormatLines*nTextHeight);
else if ( nStyle & TEXT_DRAW_VCENTER )
aPos.Y() += (nHeight-(nFormatLines*nTextHeight))/2;
// Font Alignment
if ( eAlign == ALIGN_BOTTOM )
aPos.Y() += nTextHeight;
else if ( eAlign == ALIGN_BASELINE )
aPos.Y() += GetFontMetric().GetAscent();
// Alle Zeilen ausgeben, bis auf die letzte
for ( i = 0; i < nFormatLines; i++ )
{
pLineInfo = aMultiLineInfo.GetLine( i );
if ( nStyle & TEXT_DRAW_RIGHT )
aPos.X() += nWidth-pLineInfo->GetWidth();
else if ( nStyle & TEXT_DRAW_CENTER )
aPos.X() += (nWidth-pLineInfo->GetWidth())/2;
xub_StrLen nIndex = pLineInfo->GetIndex();
xub_StrLen nLineLen = pLineInfo->GetLen();
DrawText( aPos, aStr, nIndex, nLineLen, pVector, pDisplayText );
if ( !(GetSettings().GetStyleSettings().GetOptions() & STYLE_OPTION_NOMNEMONICS) && !pVector )
{
if ( (nMnemonicPos >= nIndex) && (nMnemonicPos < nIndex+nLineLen) )
{
long nMnemonicX;
long nMnemonicY;
long nMnemonicWidth;
sal_Int32* pCaretXArray = (sal_Int32*) alloca( 2 * sizeof(sal_Int32) * nLineLen );
/*BOOL bRet =*/ GetCaretPositions( aStr, pCaretXArray,
nIndex, nLineLen);
long lc_x1 = pCaretXArray[2*(nMnemonicPos - nIndex)];
long lc_x2 = pCaretXArray[2*(nMnemonicPos - nIndex)+1];
nMnemonicWidth = ::abs((int)(lc_x1 - lc_x2));
Point aTempPos = LogicToPixel( aPos );
nMnemonicX = mnOutOffX + aTempPos.X() + ImplLogicWidthToDevicePixel( Min( lc_x1, lc_x2 ) );
nMnemonicY = mnOutOffY + aTempPos.Y() + ImplLogicWidthToDevicePixel( GetFontMetric().GetAscent() );
ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
}
}
aPos.Y() += nTextHeight;
aPos.X() = rRect.Left();
}
// Gibt es noch eine letzte Zeile, dann diese linksbuendig ausgeben,
// da die Zeile gekuerzt wurde
if ( aLastLine.Len() )
DrawText( aPos, aLastLine, 0, STRING_LEN, pVector, pDisplayText );
// Clipping zuruecksetzen
if ( nStyle & TEXT_DRAW_CLIP )
Pop();
}
}
else
#endif
{
long nTextWidth = GetTextWidth( aStr );
#ifdef AVS
// Evt. Text kuerzen
if ( nTextWidth > nWidth )
{
if ( nStyle & TEXT_DRAW_ELLIPSIS )
{
aStr = GetEllipsisString( aStr, nWidth, nStyle );
nStyle &= ~(TEXT_DRAW_CENTER | TEXT_DRAW_RIGHT);
nStyle |= TEXT_DRAW_LEFT;
nTextWidth = GetTextWidth( aStr );
}
}
else
{
if ( nTextHeight <= nHeight )
nStyle &= ~TEXT_DRAW_CLIP;
}
// horizontal text alignment
if ( nStyle & TEXT_DRAW_RIGHT )
aPos.X() += nWidth-nTextWidth;
else if ( nStyle & TEXT_DRAW_CENTER )
aPos.X() += (nWidth-nTextWidth)/2;
// vertical font alignment
if ( eAlign == ALIGN_BOTTOM )
aPos.Y() += nTextHeight;
else if ( eAlign == ALIGN_BASELINE )
aPos.Y() += GetFontMetric().GetAscent();
if ( nStyle & TEXT_DRAW_BOTTOM )
aPos.Y() += nHeight-nTextHeight;
else if ( nStyle & TEXT_DRAW_VCENTER )
aPos.Y() += (nHeight-nTextHeight)/2;
long nMnemonicX = 0;
long nMnemonicY = 0;
long nMnemonicWidth = 0;
if ( nMnemonicPos != STRING_NOTFOUND )
{
sal_Int32* pCaretXArray = (sal_Int32*) alloca( 2 * sizeof(sal_Int32) * aStr.Len() );
/*BOOL bRet =*/ GetCaretPositions( aStr, pCaretXArray, 0, aStr.Len() );
long lc_x1 = pCaretXArray[2*(nMnemonicPos)];
long lc_x2 = pCaretXArray[2*(nMnemonicPos)+1];
nMnemonicWidth = ::abs((int)(lc_x1 - lc_x2));
Point aTempPos = LogicToPixel( aPos );
nMnemonicX = mnOutOffX + aTempPos.X() + ImplLogicWidthToDevicePixel( Min(lc_x1, lc_x2) );
nMnemonicY = mnOutOffY + aTempPos.Y() + ImplLogicWidthToDevicePixel( GetFontMetric().GetAscent() );
}
#endif
if ( nStyle & TEXT_DRAW_CLIP )
{
Push( PUSH_CLIPREGION );
IntersectClipRegion( rRect );
DrawText( aPos, aStr, 0, STRING_LEN, pVector, pDisplayText );
#ifdef AVS
if ( !(GetSettings().GetStyleSettings().GetOptions() & STYLE_OPTION_NOMNEMONICS) && !pVector )
{
if ( nMnemonicPos != STRING_NOTFOUND )
ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
}
#endif
Pop();
}
else
{
DrawText( aPos, aStr, 0, STRING_LEN, pVector, pDisplayText );
#ifdef AVS
if ( !(GetSettings().GetStyleSettings().GetOptions() & STYLE_OPTION_NOMNEMONICS) && !pVector )
{
if ( nMnemonicPos != STRING_NOTFOUND )
ImplDrawMnemonicLine( nMnemonicX, nMnemonicY, nMnemonicWidth );
}
#endif
}
}
if ( nStyle & TEXT_DRAW_DISABLE && !pVector )
{
SetTextColor( aOldTextColor );
if ( bRestoreFillColor )
SetTextFillColor( aOldTextFillColor );
}
}
// -----------------------------------------------------------------------
void OutputDevice::ImplDrawTextRect( long nBaseX, long nBaseY,
long nX, long nY, long nWidth, long nHeight )
{
short nOrientation = mpFontEntry->mnOrientation;
if ( nOrientation )
{
// Rotate rect without rounding problems for 90 degree rotations
if ( !(nOrientation % 900) )
{
nX -= nBaseX;
nY -= nBaseY;
if ( nOrientation == 900 )
{
long nTemp = nX;
nX = nY;
nY = -nTemp;
nTemp = nWidth;
nWidth = nHeight;
nHeight = nTemp;
nY -= nHeight;
}
else if ( nOrientation == 1800 )
{
nX = -nX;
nY = -nY;
nX -= nWidth;
nY -= nHeight;
}
else /* ( nOrientation == 2700 ) */
{
long nTemp = nX;
nX = -nY;
nY = nTemp;
nTemp = nWidth;
nWidth = nHeight;
nHeight = nTemp;
nX -= nWidth;
}
nX += nBaseX;
nY += nBaseY;
}
else
{
// inflate because polygons are drawn smaller
Rectangle aRect( Point( nX, nY ), Size( nWidth+1, nHeight+1 ) );
Polygon aPoly( aRect );
aPoly.Rotate( Point( nBaseX, nBaseY ), mpFontEntry->mnOrientation );
ImplDrawPolygon( aPoly );
return;
}
}
mpGraphics->DrawRect( nX, nY, nWidth, nHeight, this );
}
// -----------------------------------------------------------------------
long OutputDevice::GetTextWidth( const String& rStr,
xub_StrLen nIndex, xub_StrLen nLen ) const
{
//DBG_TRACE( "OutputDevice::GetTextWidth()" );
//DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
long nWidth = GetTextArray( rStr, NULL, nIndex, nLen );
return nWidth;
}
// -----------------------------------------------------------------------
long OutputDevice::GetTextArray( const String& rStr, sal_Int32* pDXAry,
xub_StrLen nIndex, xub_StrLen nLen ) const
{
//DBG_TRACE( "OutputDevice::GetTextArray()" );
//DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
if( nIndex >= rStr.Len() )
return 0;
if( (ULONG)nIndex+nLen >= rStr.Len() )
nLen = rStr.Len() - nIndex;
// do layout
SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen );
if( !pSalLayout )
return 0;
long nWidth = pSalLayout->FillDXArray( pDXAry );
int nWidthFactor = pSalLayout->GetUnitsPerPixel();
pSalLayout->Release();
// convert virtual char widths to virtual absolute positions
if( pDXAry )
for( int i = 1; i < nLen; ++i )
pDXAry[ i ] += pDXAry[ i-1 ];
// convert from font units to logical units
if( mbMap )
{
if( pDXAry )
for( int i = 0; i < nLen; ++i )
pDXAry[i] = ImplDevicePixelToLogicWidth( pDXAry[i] );
nWidth = ImplDevicePixelToLogicWidth( nWidth );
}
if( nWidthFactor > 1 )
{
if( pDXAry )
for( int i = 0; i < nLen; ++i )
pDXAry[i] /= nWidthFactor;
nWidth /= nWidthFactor;
}
return nWidth;
}
long OutputDevice::GetTextHeight() const
{
//DBG_TRACE( "OutputDevice::GetTextHeight()" );
//DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
if( mbNewFont )
if( !ImplNewFont() )
return 0;
if( mbInitFont )
ImplInitFont();
#ifdef AVS
if( !ImplNewFont() )
return 0;
#endif
long nHeight = mpFontEntry->mnLineHeight + mnEmphasisAscent + mnEmphasisDescent;
if ( mbMap )
nHeight = ImplDevicePixelToLogicHeight( nHeight );
return nHeight;
}
// -----------------------------------------------------------------------
String OutputDevice::GetNonMnemonicString( const String& rStr, xub_StrLen& rMnemonicPos )
{
String aStr = rStr;
xub_StrLen nLen = aStr.Len();
xub_StrLen i = 0;
rMnemonicPos = STRING_NOTFOUND;
while ( i < nLen )
{
if ( aStr.GetChar( i ) == '~' )
{
if ( aStr.GetChar( i+1 ) != '~' )
{
if ( rMnemonicPos == STRING_NOTFOUND )
rMnemonicPos = i;
aStr.Erase( i, 1 );
nLen--;
}
else
{
aStr.Erase( i, 1 );
nLen--;
i++;
}
}
else
i++;
}
return aStr;
}
void OutputDevice::DrawText( const Point& rStartPt, const XubString& rStr,
xub_StrLen nIndex , xub_StrLen nLen ,
MetricVector* pVector, String* pDisplayText )
{
if( pVector )
{
Region aClip( GetClipRegion() );
if( meOutDevType == OUTDEV_WINDOW )
aClip.Intersect( Rectangle( Point(), GetOutputSize() ) );
if( ! aClip.IsNull() )
{
MetricVector aTmp;
GetGlyphBoundRects( rStartPt, rStr, nIndex, nLen, nIndex, aTmp );
bool bInserted = false;
for( MetricVector::const_iterator it = aTmp.begin(); it != aTmp.end(); ++it, nIndex++ )
{
bool bAppend = false;
if( aClip.IsOver( *it ) )
bAppend = true;
else if( rStr.GetChar( nIndex ) == ' ' && bInserted )
{
MetricVector::const_iterator next = it;
++next;
if( next != aTmp.end() && aClip.IsOver( *next ) )
bAppend = true;
}
if( bAppend )
{
pVector->push_back( *it );
if( pDisplayText )
pDisplayText->Append( rStr.GetChar( nIndex ) );
bInserted = true;
}
}
}
else
{
GetGlyphBoundRects( rStartPt, rStr, nIndex, nLen, nIndex, *pVector );
if( pDisplayText )
pDisplayText->Append( rStr.Copy( nIndex, nLen ) );
}
}
if ( pVector )
return;
SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen, rStartPt, 0, NULL, true );
if( pSalLayout )
{
ImplDrawText( *pSalLayout );
pSalLayout->Release();
}
}
// -----------------------------------------------------------------------
void OutputDevice::ImplDrawText( SalLayout& rSalLayout )
{
if( mbInitClipRegion )
ImplInitClipRegion();
if( mbOutputClipped )
return;
if( mbInitTextColor )
ImplInitTextColor();
rSalLayout.DrawBase() += Point( mnTextOffX, mnTextOffY );
if( IsTextFillColor() )
ImplDrawTextBackground( rSalLayout );
if( mbTextSpecial )
ImplDrawSpecialText( rSalLayout );
else
ImplDrawTextDirect( rSalLayout, mbTextLines );
}
// -----------------------------------------------------------------------
void OutputDevice::ImplDrawTextBackground( const SalLayout& rSalLayout )
{
const long nWidth = rSalLayout.GetTextWidth() / rSalLayout.GetUnitsPerPixel();
const Point aBase = rSalLayout.DrawBase();
const long nX = aBase.X();
const long nY = aBase.Y();
if ( mbLineColor || mbInitLineColor )
{
mpGraphics->SetLineColor();
mbInitLineColor = TRUE;
}
mpGraphics->SetFillColor( ImplColorToSal( GetTextFillColor() ) );
mbInitFillColor = TRUE;
ImplDrawTextRect( nX, nY, nX, nY-mpFontEntry->maMetric.mnAscent-mnEmphasisAscent,
nWidth,
mpFontEntry->mnLineHeight+mnEmphasisAscent+mnEmphasisDescent );
}
// -----------------------------------------------------------------------
void OutputDevice::ImplDrawTextDirect( SalLayout& rSalLayout, BOOL bTextLines )
{
if( mpFontEntry->mnOwnOrientation )
if( ImplDrawRotateText( rSalLayout ) )
return;
long nOldX = rSalLayout.DrawBase().X();
#ifdef AVS
if( ! (mpPDFWriter && mpPDFWriter->isBuiltinFont(mpFontEntry->maFontSelData.mpFontData) ) )
{
if( ImplHasMirroredGraphics() )
{
long w = meOutDevType == OUTDEV_VIRDEV ? mnOutWidth : mpGraphics->GetGraphicsWidth();
long x = rSalLayout.DrawBase().X();
rSalLayout.DrawBase().X() = w - 1 - x;
if( !IsRTLEnabled() )
{
OutputDevice *pOutDevRef = (OutputDevice *)this;
// mirror this window back
long devX = w-pOutDevRef->mnOutWidth-pOutDevRef->mnOutOffX; // re-mirrored mnOutOffX
rSalLayout.DrawBase().X() = devX + ( pOutDevRef->mnOutWidth - 1 - (rSalLayout.DrawBase().X() - devX) ) ;
}
}
else if( IsRTLEnabled() )
{
//long w = meOutDevType == OUTDEV_VIRDEV ? mnOutWidth : mpGraphics->GetGraphicsWidth();
//long x = rSalLayout.DrawBase().X();
OutputDevice *pOutDevRef = (OutputDevice *)this;
// mirror this window back
long devX = pOutDevRef->mnOutOffX; // re-mirrored mnOutOffX
rSalLayout.DrawBase().X() = pOutDevRef->mnOutWidth - 1 - (rSalLayout.DrawBase().X() - devX) + devX;
}
#endif
rSalLayout.DrawText( *mpGraphics );
#ifdef AVS
}
#endif
rSalLayout.DrawBase().X() = nOldX;
if( bTextLines )
ImplDrawTextLines( rSalLayout,
maFont.GetStrikeout(), maFont.GetUnderline(), maFont.GetOverline(),
maFont.IsWordLineMode(), ImplIsUnderlineAbove( maFont ) );
#ifdef QWE
// emphasis marks
if( maFont.GetEmphasisMark() & EMPHASISMARK_STYLE )
ImplDrawEmphasisMarks( rSalLayout );
#endif
}
// -----------------------------------------------------------------------
void OutputDevice::ImplDrawEmphasisMarks( SalLayout& rSalLayout )
{
#ifdef QWE
Color aOldColor = GetTextColor();
Color aOldLineColor = GetLineColor();
Color aOldFillColor = GetFillColor();
BOOL bOldMap = mbMap;
//GDIMetaFile* pOldMetaFile = mpMetaFile;
//mpMetaFile = NULL;
EnableMapMode( FALSE );
FontEmphasisMark nEmphasisMark = ImplGetEmphasisMarkStyle( maFont );
PolyPolygon aPolyPoly;
Rectangle aRect1;
Rectangle aRect2;
long nEmphasisYOff;
long nEmphasisWidth;
long nEmphasisHeight;
BOOL bPolyLine;
if ( nEmphasisMark & EMPHASISMARK_POS_BELOW )
nEmphasisHeight = mnEmphasisDescent;
else
nEmphasisHeight = mnEmphasisAscent;
ImplGetEmphasisMark( aPolyPoly, bPolyLine,
aRect1, aRect2,
nEmphasisYOff, nEmphasisWidth,
nEmphasisMark,
nEmphasisHeight, mpFontEntry->mnOrientation );
if ( bPolyLine )
{
SetLineColor( GetTextColor() );
SetFillColor();
}
else
{
SetLineColor();
SetFillColor( GetTextColor() );
}
Point aOffset = Point(0,0);
if ( nEmphasisMark & EMPHASISMARK_POS_BELOW )
aOffset.Y() += mpFontEntry->maMetric.mnDescent + nEmphasisYOff;
else
aOffset.Y() -= mpFontEntry->maMetric.mnAscent + nEmphasisYOff;
long nEmphasisWidth2 = nEmphasisWidth / 2;
long nEmphasisHeight2 = nEmphasisHeight / 2;
aOffset += Point( nEmphasisWidth2, nEmphasisHeight2 );
Point aOutPoint;
Rectangle aRectangle;
for( int nStart = 0;;)
{
sal_GlyphId nGlyphIndex;
if( !rSalLayout.GetNextGlyphs( 1, &nGlyphIndex, aOutPoint, nStart ) )
break;
if( !mpGraphics->GetGlyphBoundRect( nGlyphIndex, aRectangle ) )
continue;
if( !rSalLayout.IsSpacingGlyph( nGlyphIndex ) )
{
Point aAdjPoint = aOffset;
aAdjPoint.X() += aRectangle.Left() + (aRectangle.GetWidth() - nEmphasisWidth) / 2;
if ( mpFontEntry->mnOrientation )
ImplRotatePos( 0, 0, aAdjPoint.X(), aAdjPoint.Y(), mpFontEntry->mnOrientation );
aOutPoint += aAdjPoint;
aOutPoint -= Point( nEmphasisWidth2, nEmphasisHeight2 );
ImplDrawEmphasisMark( rSalLayout.DrawBase().X(),
aOutPoint.X(), aOutPoint.Y(),
aPolyPoly, bPolyLine, aRect1, aRect2 );
}
}
SetLineColor( aOldLineColor );
SetFillColor( aOldFillColor );
EnableMapMode( bOldMap );
//mpMetaFile = pOldMetaFile;
#endif
}
// -----------------------------------------------------------------------
void OutputDevice::ImplDrawTextLines( SalLayout& rSalLayout,
FontStrikeout eStrikeout, FontUnderline eUnderline, FontUnderline eOverline, BOOL bWordLine, BOOL bUnderlineAbove )
{
if( bWordLine )
{
Point aPos, aStartPt;
sal_Int32 nWidth = 0, nAdvance=0;
for( int nStart = 0;;)
{
sal_GlyphId nGlyphIndex;
if( !rSalLayout.GetNextGlyphs( 1, &nGlyphIndex, aPos, nStart, &nAdvance ) )
break;
if( !rSalLayout.IsSpacingGlyph( nGlyphIndex ) )
{
if( !nWidth )
{
aStartPt = aPos;//rSalLayout.DrawBase() - (aPos - rSalLayout.DrawOffset());
}
nWidth += nAdvance;
}
else if( nWidth > 0 )
{
ImplDrawTextLine( rSalLayout.DrawBase().X(), aStartPt.X(), aStartPt.Y(), nWidth,
eStrikeout, eUnderline, eOverline, bUnderlineAbove );
nWidth = 0;
}
}
if( nWidth > 0 )
{
ImplDrawTextLine( rSalLayout.DrawBase().X(), aStartPt.X(), aStartPt.Y(), nWidth,
eStrikeout, eUnderline, eOverline, bUnderlineAbove );
}
}
else
{
Point aStartPt = rSalLayout.GetDrawPosition();
int nWidth = rSalLayout.GetTextWidth() / rSalLayout.GetUnitsPerPixel();
ImplDrawTextLine( rSalLayout.DrawBase().X(), aStartPt.X(), aStartPt.Y(), nWidth,
eStrikeout, eUnderline, eOverline, bUnderlineAbove );
}
}
// -----------------------------------------------------------------------
void OutputDevice::ImplDrawTextLine( long nBaseX,
long nX, long nY, long nWidth,
FontStrikeout eStrikeout,
FontUnderline eUnderline,
FontUnderline eOverline,
BOOL bUnderlineAbove )
{
if ( !nWidth )
return;
Color aStrikeoutColor = GetTextColor();
Color aUnderlineColor = GetTextLineColor();
Color aOverlineColor = GetOverlineColor();
BOOL bStrikeoutDone = FALSE;
BOOL bUnderlineDone = FALSE;
BOOL bOverlineDone = FALSE;
// TODO: fix rotated text
if ( IsRTLEnabled() )
// --- RTL --- mirror at basex
nX = nBaseX - nWidth - (nX - nBaseX - 1);
if ( !IsTextLineColor() )
aUnderlineColor = GetTextColor();
if ( !IsOverlineColor() )
aOverlineColor = GetTextColor();
if ( (eUnderline == UNDERLINE_SMALLWAVE) ||
(eUnderline == UNDERLINE_WAVE) ||
(eUnderline == UNDERLINE_DOUBLEWAVE) ||
(eUnderline == UNDERLINE_BOLDWAVE) )
{
ImplDrawWaveTextLine( nBaseX, nY, nX, nY, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
bUnderlineDone = TRUE;
}
if ( (eOverline == UNDERLINE_SMALLWAVE) ||
(eOverline == UNDERLINE_WAVE) ||
(eOverline == UNDERLINE_DOUBLEWAVE) ||
(eOverline == UNDERLINE_BOLDWAVE) )
{
ImplDrawWaveTextLine( nBaseX, nY, nX, nY, nWidth, eOverline, aOverlineColor, TRUE );
bOverlineDone = TRUE;
}
if ( (eStrikeout == STRIKEOUT_SLASH) ||
(eStrikeout == STRIKEOUT_X) )
{
ImplDrawStrikeoutChar( nBaseX, nY, nX, nY, nWidth, eStrikeout, aStrikeoutColor );
bStrikeoutDone = TRUE;
}
if ( !bUnderlineDone )
ImplDrawStraightTextLine( nBaseX, nY, nX, nY, nWidth, eUnderline, aUnderlineColor, bUnderlineAbove );
if ( !bOverlineDone )
ImplDrawStraightTextLine( nBaseX, nY, nX, nY, nWidth, eOverline, aOverlineColor, TRUE );
if ( !bStrikeoutDone )
ImplDrawStrikeoutLine( nBaseX, nY, nX, nY, nWidth, eStrikeout, aStrikeoutColor );
}
// -----------------------------------------------------------------------
void OutputDevice::ImplDrawStraightTextLine( long nBaseX, long nBaseY,
long nX, long nY, long nWidth,
FontUnderline eTextLine,
Color aColor,
BOOL bIsAbove )
{
ImplFontEntry* pFontEntry = mpFontEntry;
long nLineHeight = 0;
long nLinePos = 0;
long nLinePos2 = 0;
if ( eTextLine > UNDERLINE_LAST )
eTextLine = UNDERLINE_SINGLE;
switch ( eTextLine )
{
case UNDERLINE_SINGLE:
case UNDERLINE_DOTTED:
case UNDERLINE_DASH:
case UNDERLINE_LONGDASH:
case UNDERLINE_DASHDOT:
case UNDERLINE_DASHDOTDOT:
if ( bIsAbove )
{
nLineHeight = pFontEntry->maMetric.mnAboveUnderlineSize;
nLinePos = nY + pFontEntry->maMetric.mnAboveUnderlineOffset;
}
else
{
nLineHeight = pFontEntry->maMetric.mnUnderlineSize;
nLinePos = nY + pFontEntry->maMetric.mnUnderlineOffset;
}
break;
case UNDERLINE_BOLD:
case UNDERLINE_BOLDDOTTED:
case UNDERLINE_BOLDDASH:
case UNDERLINE_BOLDLONGDASH:
case UNDERLINE_BOLDDASHDOT:
case UNDERLINE_BOLDDASHDOTDOT:
if ( bIsAbove )
{
nLineHeight = pFontEntry->maMetric.mnAboveBUnderlineSize;
nLinePos = nY + pFontEntry->maMetric.mnAboveBUnderlineOffset;
}
else
{
nLineHeight = pFontEntry->maMetric.mnBUnderlineSize;
nLinePos = nY + pFontEntry->maMetric.mnBUnderlineOffset;
}
break;
case UNDERLINE_DOUBLE:
if ( bIsAbove )
{
nLineHeight = pFontEntry->maMetric.mnAboveDUnderlineSize;
nLinePos = nY + pFontEntry->maMetric.mnAboveDUnderlineOffset1;
nLinePos2 = nY + pFontEntry->maMetric.mnAboveDUnderlineOffset2;
}
else
{
nLineHeight = pFontEntry->maMetric.mnDUnderlineSize;
nLinePos = nY + pFontEntry->maMetric.mnDUnderlineOffset1;
nLinePos2 = nY + pFontEntry->maMetric.mnDUnderlineOffset2;
}
break;
default:
break;
}
if ( nLineHeight )
{
if ( mbLineColor || mbInitLineColor )
{
mpGraphics->SetLineColor();
mbInitLineColor = TRUE;
}
mpGraphics->SetFillColor( ImplColorToSal( aColor ) );
mbInitFillColor = TRUE;
long nLeft = nX;
switch ( eTextLine )
{
case UNDERLINE_SINGLE:
case UNDERLINE_BOLD:
ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight );
break;
case UNDERLINE_DOUBLE:
ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight );
ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos2, nWidth, nLineHeight );
break;
case UNDERLINE_DOTTED:
case UNDERLINE_BOLDDOTTED:
{
long nDotWidth = nLineHeight*mnDPIY;
nDotWidth += mnDPIY/2;
nDotWidth /= mnDPIY;
long nTempWidth = nDotWidth;
long nEnd = nLeft+nWidth;
while ( nLeft < nEnd )
{
if ( nLeft+nTempWidth > nEnd )
nTempWidth = nEnd-nLeft;
ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempWidth, nLineHeight );
nLeft += nDotWidth*2;
}
}
break;
case UNDERLINE_DASH:
case UNDERLINE_LONGDASH:
case UNDERLINE_BOLDDASH:
case UNDERLINE_BOLDLONGDASH:
{
long nDotWidth = nLineHeight*mnDPIY;
nDotWidth += mnDPIY/2;
nDotWidth /= mnDPIY;
long nMinDashWidth;
long nMinSpaceWidth;
long nSpaceWidth;
long nDashWidth;
if ( (eTextLine == UNDERLINE_LONGDASH) ||
(eTextLine == UNDERLINE_BOLDLONGDASH) )
{
nMinDashWidth = nDotWidth*6;
nMinSpaceWidth = nDotWidth*2;
nDashWidth = 200;
nSpaceWidth = 100;
}
else
{
nMinDashWidth = nDotWidth*4;
nMinSpaceWidth = (nDotWidth*150)/100;
nDashWidth = 100;
nSpaceWidth = 50;
}
nDashWidth = ((nDashWidth*mnDPIX)+1270)/2540;
nSpaceWidth = ((nSpaceWidth*mnDPIX)+1270)/2540;
// DashWidth wird gegebenenfalls verbreitert, wenn
// die dicke der Linie im Verhaeltnis zur Laenge
// zu dick wird
if ( nDashWidth < nMinDashWidth )
nDashWidth = nMinDashWidth;
if ( nSpaceWidth < nMinSpaceWidth )
nSpaceWidth = nMinSpaceWidth;
long nTempWidth = nDashWidth;
long nEnd = nLeft+nWidth;
while ( nLeft < nEnd )
{
if ( nLeft+nTempWidth > nEnd )
nTempWidth = nEnd-nLeft;
ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempWidth, nLineHeight );
nLeft += nDashWidth+nSpaceWidth;
}
}
break;
case UNDERLINE_DASHDOT:
case UNDERLINE_BOLDDASHDOT:
{
long nDotWidth = nLineHeight*mnDPIY;
nDotWidth += mnDPIY/2;
nDotWidth /= mnDPIY;
long nDashWidth = ((100*mnDPIX)+1270)/2540;
long nMinDashWidth = nDotWidth*4;
// DashWidth wird gegebenenfalls verbreitert, wenn
// die dicke der Linie im Verhaeltnis zur Laenge
// zu dick wird
if ( nDashWidth < nMinDashWidth )
nDashWidth = nMinDashWidth;
long nTempDotWidth = nDotWidth;
long nTempDashWidth = nDashWidth;
long nEnd = nLeft+nWidth;
while ( nLeft < nEnd )
{
if ( nLeft+nTempDotWidth > nEnd )
nTempDotWidth = nEnd-nLeft;
ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDotWidth, nLineHeight );
nLeft += nDotWidth*2;
if ( nLeft > nEnd )
break;
if ( nLeft+nTempDashWidth > nEnd )
nTempDashWidth = nEnd-nLeft;
ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDashWidth, nLineHeight );
nLeft += nDashWidth+nDotWidth;
}
}
break;
case UNDERLINE_DASHDOTDOT:
case UNDERLINE_BOLDDASHDOTDOT:
{
long nDotWidth = nLineHeight*mnDPIY;
nDotWidth += mnDPIY/2;
nDotWidth /= mnDPIY;
long nDashWidth = ((100*mnDPIX)+1270)/2540;
long nMinDashWidth = nDotWidth*4;
// DashWidth wird gegebenenfalls verbreitert, wenn
// die dicke der Linie im Verhaeltnis zur Laenge
// zu dick wird
if ( nDashWidth < nMinDashWidth )
nDashWidth = nMinDashWidth;
long nTempDotWidth = nDotWidth;
long nTempDashWidth = nDashWidth;
long nEnd = nLeft+nWidth;
while ( nLeft < nEnd )
{
if ( nLeft+nTempDotWidth > nEnd )
nTempDotWidth = nEnd-nLeft;
ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDotWidth, nLineHeight );
nLeft += nDotWidth*2;
if ( nLeft > nEnd )
break;
if ( nLeft+nTempDotWidth > nEnd )
nTempDotWidth = nEnd-nLeft;
ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDotWidth, nLineHeight );
nLeft += nDotWidth*2;
if ( nLeft > nEnd )
break;
if ( nLeft+nTempDashWidth > nEnd )
nTempDashWidth = nEnd-nLeft;
ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nTempDashWidth, nLineHeight );
nLeft += nDashWidth+nDotWidth;
}
}
break;
default:
break;
}
}
}
// -----------------------------------------------------------------------
void OutputDevice::ImplDrawStrikeoutChar( long nBaseX, long nBaseY,
long nX, long nY, long nWidth,
FontStrikeout eStrikeout,
Color aColor )
{
BOOL bOldMap = IsMapModeEnabled();
EnableMapMode( FALSE );
Color aOldColor = GetTextColor();
SetTextColor( aColor );
ImplInitTextColor();
xub_Unicode pChars[5];
if ( eStrikeout == STRIKEOUT_SLASH )
pChars[0] = '/';
else // ( eStrikeout == STRIKEOUT_X )
pChars[0] = 'X';
pChars[3]=pChars[2]=pChars[1]=pChars[0];
// calculate approximation of strikeout atom size
long nStrikeoutWidth = nWidth;
String aStrikeoutTest( pChars, 4 );
SalLayout* pLayout = ImplLayout( aStrikeoutTest, 0, 4 );
if ( pLayout )
{
nStrikeoutWidth = (pLayout->GetTextWidth() + 2) / 4;
pLayout->Release();
if ( nStrikeoutWidth <= 0 ) // sanity check
nStrikeoutWidth = 1;
}
// calculate acceptable strikeout length
// allow the strikeout to be one pixel larger than the text it strikes out
long nMaxWidth = nStrikeoutWidth/2;
if ( nMaxWidth < 2 )
nMaxWidth = 2;
nMaxWidth += nWidth + 1;
// build strikeout string
long nFullStrikeoutWidth = 0;
String aStrikeoutText( pChars, 0 );
while ( (nFullStrikeoutWidth+=nStrikeoutWidth) < nMaxWidth+1 )
aStrikeoutText += pChars[0];
// if the text width is smaller than the strikeout text, then do not
// strike out at all. This case requires user interaction, e.g. adding
// a space to the text
if ( (aStrikeoutText.Len() > 0) )
#ifdef AVS
&& !(mpPDFWriter && mpPDFWriter->isBuiltinFont(mpFontEntry->maFontSelData.mpFontData) ) )
#endif
{
if ( mpFontEntry->mnOrientation )
ImplRotatePos( nBaseX, nBaseY, nX, nY, mpFontEntry->mnOrientation );
// strikeout text has to be left aligned
ULONG nOrigTLM = mnTextLayoutMode;
mnTextLayoutMode = TEXT_LAYOUT_BIDI_STRONG | TEXT_LAYOUT_COMPLEX_DISABLED;
SalLayout* pSalLayout = ImplLayout( aStrikeoutText, 0, STRING_LEN );
mnTextLayoutMode = nOrigTLM;
if ( pSalLayout )
{
pSalLayout->DrawBase() = Point( nX+mnTextOffX, nY+mnTextOffY );
pSalLayout->DrawText( *mpGraphics );
pSalLayout->Release();
}
}
SetTextColor( aOldColor );
ImplInitTextColor();
EnableMapMode( bOldMap );
}
// -----------------------------------------------------------------------
void OutputDevice::ImplDrawStrikeoutLine( long nBaseX, long nBaseY,
long nX, long nY, long nWidth,
FontStrikeout eStrikeout,
Color aColor )
{
ImplFontEntry* pFontEntry = mpFontEntry;
long nLineHeight = 0;
long nLinePos = 0;
long nLinePos2 = 0;
if ( eStrikeout > STRIKEOUT_LAST )
eStrikeout = STRIKEOUT_SINGLE;
switch ( eStrikeout )
{
case STRIKEOUT_SINGLE:
nLineHeight = pFontEntry->maMetric.mnStrikeoutSize;
nLinePos = nY + pFontEntry->maMetric.mnStrikeoutOffset;
break;
case STRIKEOUT_BOLD:
nLineHeight = pFontEntry->maMetric.mnBStrikeoutSize;
nLinePos = nY + pFontEntry->maMetric.mnBStrikeoutOffset;
break;
case STRIKEOUT_DOUBLE:
nLineHeight = pFontEntry->maMetric.mnDStrikeoutSize;
nLinePos = nY + pFontEntry->maMetric.mnDStrikeoutOffset1;
nLinePos2 = nY + pFontEntry->maMetric.mnDStrikeoutOffset2;
break;
default:
break;
}
if ( nLineHeight )
{
if ( mbLineColor || mbInitLineColor )
{
mpGraphics->SetLineColor();
mbInitLineColor = TRUE;
}
mpGraphics->SetFillColor( ImplColorToSal( aColor ) );
mbInitFillColor = TRUE;
long nLeft = nX;
switch ( eStrikeout )
{
case STRIKEOUT_SINGLE:
case STRIKEOUT_BOLD:
ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight );
break;
case STRIKEOUT_DOUBLE:
ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos, nWidth, nLineHeight );
ImplDrawTextRect( nBaseX, nBaseY, nLeft, nLinePos2, nWidth, nLineHeight );
break;
default:
break;
}
}
}
// -----------------------------------------------------------------------
void OutputDevice::ImplDrawWaveTextLine( long nBaseX, long nBaseY,
long nX, long nY, long nWidth,
FontUnderline eTextLine,
Color aColor,
BOOL bIsAbove )
{
ImplFontEntry* pFontEntry = mpFontEntry;
long nLineHeight;
long nLinePos;
if ( bIsAbove )
{
nLineHeight = pFontEntry->maMetric.mnAboveWUnderlineSize;
nLinePos = pFontEntry->maMetric.mnAboveWUnderlineOffset;
}
else
{
nLineHeight = pFontEntry->maMetric.mnWUnderlineSize;
nLinePos = pFontEntry->maMetric.mnWUnderlineOffset;
}
if ( (eTextLine == UNDERLINE_SMALLWAVE) && (nLineHeight > 3) )
nLineHeight = 3;
long nLineWidth = (mnDPIX/300);
if ( !nLineWidth )
nLineWidth = 1;
if ( eTextLine == UNDERLINE_BOLDWAVE )
nLineWidth *= 2;
nLinePos += nY - (nLineHeight / 2);
long nLineWidthHeight = ((nLineWidth*mnDPIX)+(mnDPIY/2))/mnDPIY;
if ( eTextLine == UNDERLINE_DOUBLEWAVE )
{
long nOrgLineHeight = nLineHeight;
nLineHeight /= 3;
if ( nLineHeight < 2 )
{
if ( nOrgLineHeight > 1 )
nLineHeight = 2;
else
nLineHeight = 1;
}
long nLineDY = nOrgLineHeight-(nLineHeight*2);
if ( nLineDY < nLineWidthHeight )
nLineDY = nLineWidthHeight;
long nLineDY2 = nLineDY/2;
if ( !nLineDY2 )
nLineDY2 = 1;
nLinePos -= nLineWidthHeight-nLineDY2;
ImplDrawWaveLine( nBaseX, nBaseY, nX, nLinePos, nWidth, nLineHeight,
nLineWidth, mpFontEntry->mnOrientation, aColor );
nLinePos += nLineWidthHeight+nLineDY;
ImplDrawWaveLine( nBaseX, nBaseY, nX, nLinePos, nWidth, nLineHeight,
nLineWidth, mpFontEntry->mnOrientation, aColor );
}
else
{
nLinePos -= nLineWidthHeight/2;
ImplDrawWaveLine( nBaseX, nBaseY, nX, nLinePos, nWidth, nLineHeight,
nLineWidth, mpFontEntry->mnOrientation, aColor );
}
}
// -----------------------------------------------------------------------
static void ImplDrawWavePixel( long nOriginX, long nOriginY,
long nCurX, long nCurY,
short nOrientation,
SalGraphics* pGraphics,
OutputDevice* pOutDev,
BOOL bDrawPixAsRect,
long nPixWidth, long nPixHeight )
{
if ( nOrientation )
ImplRotatePos( nOriginX, nOriginY, nCurX, nCurY, nOrientation );
if ( bDrawPixAsRect )
{
pGraphics->DrawRect( nCurX, nCurY, nPixWidth, nPixHeight, pOutDev );
}
else
{
pGraphics->DrawPixel( nCurX, nCurY, pOutDev );
}
}
// -----------------------------------------------------------------------
void OutputDevice::ImplDrawWaveLine( long nBaseX, long nBaseY,
long nStartX, long nStartY,
long nWidth, long nHeight,
long nLineWidth, short nOrientation,
const Color& rColor )
{
if ( !nHeight )
return;
// Bei Hoehe von 1 Pixel reicht es, eine Linie auszugeben
if ( (nLineWidth == 1) && (nHeight == 1) )
{
mpGraphics->SetLineColor( ImplColorToSal( rColor ) );
mbInitLineColor = TRUE;
long nEndX = nStartX+nWidth;
long nEndY = nStartY;
if ( nOrientation )
{
ImplRotatePos( nBaseX, nBaseY, nStartX, nStartY, nOrientation );
ImplRotatePos( nBaseX, nBaseY, nEndX, nEndY, nOrientation );
}
mpGraphics->DrawLine( nStartX, nStartY, nEndX, nEndY, this );
}
else
{
long nCurX = nStartX;
long nCurY = nStartY;
long nDiffX = 2;
long nDiffY = nHeight-1;
long nCount = nWidth;
long nOffY = -1;
long nFreq;
long i;
long nPixWidth;
long nPixHeight;
BOOL bDrawPixAsRect;
// Auf Druckern die Pixel per DrawRect() ausgeben
if ( (GetOutDevType() == OUTDEV_PRINTER) || (nLineWidth > 1) )
{
if ( mbLineColor || mbInitLineColor )
{
mpGraphics->SetLineColor();
mbInitLineColor = TRUE;
}
mpGraphics->SetFillColor( ImplColorToSal( rColor ) );
mbInitFillColor = TRUE;
bDrawPixAsRect = TRUE;
nPixWidth = nLineWidth;
nPixHeight = ((nLineWidth*mnDPIX)+(mnDPIY/2))/mnDPIY;
}
else
{
mpGraphics->SetLineColor( ImplColorToSal( rColor ) );
mbInitLineColor = TRUE;
nPixWidth = 1;
nPixHeight = 1;
bDrawPixAsRect = FALSE;
}
if ( !nDiffY )
{
while ( nWidth )
{
ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation,
mpGraphics, this,
bDrawPixAsRect, nPixWidth, nPixHeight );
nCurX++;
nWidth--;
}
}
else
{
nCurY += nDiffY;
nFreq = nCount / (nDiffX+nDiffY);
while ( nFreq-- )
{
for( i = nDiffY; i; --i )
{
ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation,
mpGraphics, this,
bDrawPixAsRect, nPixWidth, nPixHeight );
nCurX++;
nCurY += nOffY;
}
for( i = nDiffX; i; --i )
{
ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation,
mpGraphics, this,
bDrawPixAsRect, nPixWidth, nPixHeight );
nCurX++;
}
nOffY = -nOffY;
}
nFreq = nCount % (nDiffX+nDiffY);
if ( nFreq )
{
for( i = nDiffY; i && nFreq; --i, --nFreq )
{
ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation,
mpGraphics, this,
bDrawPixAsRect, nPixWidth, nPixHeight );
nCurX++;
nCurY += nOffY;
}
for( i = nDiffX; i && nFreq; --i, --nFreq )
{
ImplDrawWavePixel( nBaseX, nBaseY, nCurX, nCurY, nOrientation,
mpGraphics, this,
bDrawPixAsRect, nPixWidth, nPixHeight );
nCurX++;
}
}
}
}
}
// -----------------------------------------------------------------------
BOOL OutputDevice::ImplIsUnderlineAbove( const Font& rFont )
{
if ( !rFont.IsVertical() )
return FALSE;
if( (LANGUAGE_JAPANESE == rFont.GetLanguage())
|| (LANGUAGE_JAPANESE == rFont.GetCJKContextLanguage()) )
// the underline is right for Japanese only
return TRUE;
return FALSE;
}
// -----------------------------------------------------------------------
bool OutputDevice::ImplDrawRotateText( SalLayout& rSalLayout )
{
#ifdef QWE
int nX = rSalLayout.DrawBase().X();
int nY = rSalLayout.DrawBase().Y();
Rectangle aBoundRect;
rSalLayout.DrawBase() = Point( 0, 0 );
rSalLayout.DrawOffset() = Point( 0, 0 );
if( !rSalLayout.GetBoundRect( *mpGraphics, aBoundRect ) )
{
// guess vertical text extents if GetBoundRect failed
int nRight = rSalLayout.GetTextWidth();
int nTop = mpFontEntry->maMetric.mnAscent + mnEmphasisAscent;
long nHeight = mpFontEntry->mnLineHeight + mnEmphasisAscent + mnEmphasisDescent;
aBoundRect = Rectangle( 0, -nTop, nRight, nHeight - nTop );
}
#ifdef AVS
// cache virtual device for rotation
if ( !mpOutDevData )
ImplInitOutDevData();
if ( !mpOutDevData->mpRotateDev )
mpOutDevData->mpRotateDev = new VirtualDevice( *this, 1 );
VirtualDevice* pVDev = mpOutDevData->mpRotateDev;
#endif
VirtualDevice* pVDev = new VirtualDevice( *this, 1 );
// size it accordingly
if( !pVDev->SetOutputSizePixel( aBoundRect.GetSize() ) )
return false;
Font aFont( GetFont() );
aFont.SetOrientation( 0 );
aFont.SetSize( Size( mpFontEntry->maFontSelData.mnWidth, mpFontEntry->maFontSelData.mnHeight ) );
pVDev->SetFont( aFont );
pVDev->SetTextColor( Color( COL_BLACK ) );
pVDev->SetTextFillColor();
pVDev->ImplNewFont();
pVDev->ImplInitFont();
pVDev->ImplInitTextColor();
// draw text into upper left corner
rSalLayout.DrawBase() -= aBoundRect.TopLeft();
rSalLayout.DrawText( *((OutputDevice*)pVDev)->mpGraphics );
Bitmap aBmp = pVDev->GetBitmap( Point(), aBoundRect.GetSize() );
if ( !aBmp || !aBmp.Rotate( mpFontEntry->mnOwnOrientation, COL_WHITE ) )
return false;
// calculate rotation offset
Polygon aPoly( aBoundRect );
aPoly.Rotate( Point(), mpFontEntry->mnOwnOrientation );
Point aPoint = aPoly.GetBoundRect().TopLeft();
aPoint += Point( nX, nY );
#ifdef AVS
// mask output with text colored bitmap
GDIMetaFile* pOldMetaFile = mpMetaFile;
#endif
long nOldOffX = mnOutOffX;
long nOldOffY = mnOutOffY;
BOOL bOldMap = mbMap;
mnOutOffX = 0L;
mnOutOffY = 0L;
#ifdef AVS
mpMetaFile = NULL;
#endif
EnableMapMode( FALSE );
DrawMask( aPoint, aBmp, GetTextColor() );
EnableMapMode( bOldMap );
mnOutOffX = nOldOffX;
mnOutOffY = nOldOffY;
#ifdef AVS
mpMetaFile = pOldMetaFile;
#endif
#endif
return true;
}
// -----------------------------------------------------------------------
void OutputDevice::ImplDrawSpecialText( SalLayout& rSalLayout )
{
#ifdef QWE
Color aOldColor = GetTextColor();
Color aOldTextLineColor = GetTextLineColor();
Color aOldOverlineColor = GetOverlineColor();
FontRelief eRelief = maFont.GetRelief();
Point aOrigPos = rSalLayout.DrawBase();
if ( eRelief != RELIEF_NONE )
{
Color aReliefColor( COL_LIGHTGRAY );
Color aTextColor( aOldColor );
Color aTextLineColor( aOldTextLineColor );
Color aOverlineColor( aOldOverlineColor );
// we don't have a automatic color, so black is always drawn on white
if ( aTextColor.GetColor() == COL_BLACK )
aTextColor = Color( COL_WHITE );
if ( aTextLineColor.GetColor() == COL_BLACK )
aTextLineColor = Color( COL_WHITE );
if ( aOverlineColor.GetColor() == COL_BLACK )
aOverlineColor = Color( COL_WHITE );
// relief-color is black for white text, in all other cases
// we set this to LightGray
if ( aTextColor.GetColor() == COL_WHITE )
aReliefColor = Color( COL_BLACK );
SetTextLineColor( aReliefColor );
SetOverlineColor( aReliefColor );
SetTextColor( aReliefColor );
ImplInitTextColor();
// calculate offset - for high resolution printers the offset
// should be greater so that the effect is visible
long nOff = 1;
nOff += mnDPIX/300;
if ( eRelief == RELIEF_ENGRAVED )
nOff = -nOff;
rSalLayout.DrawOffset() += Point( nOff, nOff);
ImplDrawTextDirect( rSalLayout, mbTextLines );
rSalLayout.DrawOffset() -= Point( nOff, nOff);
SetTextLineColor( aTextLineColor );
SetOverlineColor( aOverlineColor );
SetTextColor( aTextColor );
ImplInitTextColor();
ImplDrawTextDirect( rSalLayout, mbTextLines );
SetTextLineColor( aOldTextLineColor );
SetOverlineColor( aOldOverlineColor );
if ( aTextColor != aOldColor )
{
SetTextColor( aOldColor );
ImplInitTextColor();
}
}
else
{
if ( maFont.IsShadow() )
{
long nOff = 1 + ((mpFontEntry->mnLineHeight-24)/24);
if ( maFont.IsOutline() )
nOff++;
SetTextLineColor();
SetOverlineColor();
if ( (GetTextColor().GetColor() == COL_BLACK)
|| (GetTextColor().GetLuminance() < 8) )
SetTextColor( Color( COL_LIGHTGRAY ) );
else
SetTextColor( Color( COL_BLACK ) );
ImplInitTextColor();
rSalLayout.DrawBase() += Point( nOff, nOff );
ImplDrawTextDirect( rSalLayout, mbTextLines );
rSalLayout.DrawBase() -= Point( nOff, nOff );
SetTextColor( aOldColor );
SetTextLineColor( aOldTextLineColor );
SetOverlineColor( aOldOverlineColor );
ImplInitTextColor();
if ( !maFont.IsOutline() )
ImplDrawTextDirect( rSalLayout, mbTextLines );
}
if ( maFont.IsOutline() )
{
rSalLayout.DrawBase() = aOrigPos + Point(-1,-1);
ImplDrawTextDirect( rSalLayout, mbTextLines );
rSalLayout.DrawBase() = aOrigPos + Point(+1,+1);
ImplDrawTextDirect( rSalLayout, mbTextLines );
rSalLayout.DrawBase() = aOrigPos + Point(-1,+0);
ImplDrawTextDirect( rSalLayout, mbTextLines );
rSalLayout.DrawBase() = aOrigPos + Point(-1,+1);
ImplDrawTextDirect( rSalLayout, mbTextLines );
rSalLayout.DrawBase() = aOrigPos + Point(+0,+1);
ImplDrawTextDirect( rSalLayout, mbTextLines );
rSalLayout.DrawBase() = aOrigPos + Point(+0,-1);
ImplDrawTextDirect( rSalLayout, mbTextLines );
rSalLayout.DrawBase() = aOrigPos + Point(+1,-1);
ImplDrawTextDirect( rSalLayout, mbTextLines );
rSalLayout.DrawBase() = aOrigPos + Point(+1,+0);
ImplDrawTextDirect( rSalLayout, mbTextLines );
rSalLayout.DrawBase() = aOrigPos;
SetTextColor( Color( COL_WHITE ) );
SetTextLineColor( Color( COL_WHITE ) );
SetOverlineColor( Color( COL_WHITE ) );
ImplInitTextColor();
ImplDrawTextDirect( rSalLayout, mbTextLines );
SetTextColor( aOldColor );
SetTextLineColor( aOldTextLineColor );
SetOverlineColor( aOldOverlineColor );
ImplInitTextColor();
}
}
#endif
}
// -----------------------------------------------------------------------
void OutputDevice::ImplInitTextColor()
{
if ( mbInitTextColor )
{
mpGraphics->SetTextColor( ImplColorToSal( GetTextColor() ) );
mbInitTextColor = FALSE;
}
}
SalLayout* OutputDevice::ImplLayout( const String& rOrigStr,
xub_StrLen nMinIndex,
xub_StrLen nLen,
const Point& rLogicalPos,
long nLogicalWidth,
const sal_Int32* pDXArray,
bool bFilter ) const
{
// we need a graphics
if( !mpGraphics )
return NULL;
// initialize font if needed
if( mbNewFont )
if( !ImplNewFont() )
return NULL;
if( mbInitFont )
ImplInitFont();
// check string index and length
String aStr = rOrigStr;
if( (ULONG)nMinIndex + nLen >= aStr.Len() )
if( nMinIndex < aStr.Len() )
nLen = aStr.Len() - nMinIndex;
else
return NULL;
// filter out special markers
if( bFilter )
{
xub_StrLen nCutStart, nCutStop, nOrgLen = nLen;
bool bFiltered = mpGraphics->filterText( rOrigStr, aStr, nMinIndex, nLen, nCutStart, nCutStop );
if( !nLen )
return NULL;
if( bFiltered && nCutStop != nCutStart && pDXArray )
{
if( !nLen )
pDXArray = NULL;
else
{
sal_Int32* pAry = (sal_Int32*)alloca(sizeof(sal_Int32)*nLen);
if( nCutStart > nMinIndex )
memcpy( pAry, pDXArray, sizeof(sal_Int32)*(nCutStart-nMinIndex) );
// note: nCutStart will never be smaller than nMinIndex
memcpy( pAry+nCutStart-nMinIndex,
pDXArray + nOrgLen - (nCutStop-nMinIndex),
sizeof(sal_Int32)*(nLen - (nCutStart-nMinIndex)) );
pDXArray = pAry;
}
}
}
// convert from logical units to physical units
// recode string if needed
if( mpFontEntry->mpConversion )
ImplRecodeString( mpFontEntry->mpConversion, aStr, 0, aStr.Len() );
long nPixelWidth = nLogicalWidth;
if( nLogicalWidth && mbMap )
nPixelWidth = ImplLogicWidthToDevicePixel( nLogicalWidth );
if( pDXArray && mbMap )
{
// convert from logical units to font units using a temporary array
sal_Int32* pTempDXAry = (sal_Int32*)alloca( nLen * sizeof(sal_Int32) );
// using base position for better rounding a.k.a. "dancing characters"
int nPixelXOfs = ImplLogicWidthToDevicePixel( rLogicalPos.X() );
for( int i = 0; i < nLen; ++i )
pTempDXAry[i] = ImplLogicWidthToDevicePixel( rLogicalPos.X() + pDXArray[i] ) - nPixelXOfs;
pDXArray = pTempDXAry;
}
ImplLayoutArgs aLayoutArgs = ImplPrepareLayoutArgs( aStr, nMinIndex, nLen, nPixelWidth, pDXArray );
// get matching layout object for base font
SalLayout* pSalLayout = NULL;
#ifdef AVS
if( mpPDFWriter )
pSalLayout = mpPDFWriter->GetTextLayout( aLayoutArgs, &mpFontEntry->maFontSelData );
#endif
if( !pSalLayout )
pSalLayout = mpGraphics->GetTextLayout( aLayoutArgs, 0 );
// layout text
if( pSalLayout && !pSalLayout->LayoutText( aLayoutArgs ) )
{
pSalLayout->Release();
pSalLayout = NULL;
}
if( !pSalLayout )
return NULL;
#ifdef QWE
// do glyph fallback if needed
// #105768# avoid fallback for very small font sizes
if( aLayoutArgs.NeedFallback() )
if( mpFontEntry && (mpFontEntry->maFontSelData.mnHeight >= 3) )
pSalLayout = ImplGlyphFallbackLayout( pSalLayout, aLayoutArgs );
#endif
// position, justify, etc. the layout
pSalLayout->AdjustLayout( aLayoutArgs );
pSalLayout->DrawBase() = ImplLogicToDevicePixel( rLogicalPos );
// adjust to right alignment if necessary
if( aLayoutArgs.mnFlags & SAL_LAYOUT_RIGHT_ALIGN )
{
long nRTLOffset;
if( pDXArray )
nRTLOffset = pDXArray[ nLen - 1 ];
else if( nPixelWidth )
nRTLOffset = nPixelWidth;
else
nRTLOffset = pSalLayout->GetTextWidth() / pSalLayout->GetUnitsPerPixel();
pSalLayout->DrawOffset().X() = 1 - nRTLOffset;
}
return pSalLayout;
}
#ifdef QWE
// -----------------------------------------------------------------------
SalLayout* OutputDevice::ImplGlyphFallbackLayout( SalLayout* pSalLayout, ImplLayoutArgs& rLayoutArgs ) const
{
// prepare multi level glyph fallback
MultiSalLayout* pMultiSalLayout = NULL;
ImplLayoutRuns aLayoutRuns = rLayoutArgs.maRuns;
rLayoutArgs.PrepareFallback();
rLayoutArgs.mnFlags |= SAL_LAYOUT_FOR_FALLBACK;
#if defined(HDU_DEBUG)
{
int nCharPos = -1;
bool bRTL = false;
fprintf(stderr,"OD:ImplLayout Glyph Fallback for");
for( int i=0; i<8 && rLayoutArgs.GetNextPos( &nCharPos, &bRTL); ++i )
fprintf(stderr," U+%04X", rLayoutArgs.mpStr[ nCharPos ] );
fprintf(stderr,"\n");
rLayoutArgs.ResetPos();
}
#endif
// get list of unicodes that need glyph fallback
int nCharPos = -1;
bool bRTL = false;
rtl::OUStringBuffer aMissingCodeBuf;
while( rLayoutArgs.GetNextPos( &nCharPos, &bRTL) )
aMissingCodeBuf.append( rLayoutArgs.mpStr[ nCharPos ] );
rLayoutArgs.ResetPos();
rtl::OUString aMissingCodes = aMissingCodeBuf.makeStringAndClear();
ImplFontSelectData aFontSelData = mpFontEntry->maFontSelData;
// when device specific font substitution may have been performed for
// the originally selected font then make sure that a fallback to that
// font is performed first
int nDevSpecificFallback = 0;
if( mpOutDevData && !mpOutDevData->maDevFontSubst.Empty() )
nDevSpecificFallback = 1;
// try if fallback fonts support the missing unicodes
for( int nFallbackLevel = 1; nFallbackLevel < MAX_FALLBACK; ++nFallbackLevel )
{
// find a font family suited for glyph fallback
#ifndef FONTFALLBACK_HOOKS_DISABLED
// GetGlyphFallbackFont() needs a valid aFontSelData.mpFontEntry
// if the system-specific glyph fallback is active
aFontSelData.mpFontEntry = mpFontEntry; // reset the fontentry to base-level
#endif
ImplFontEntry* pFallbackFont = mpFontCache->GetGlyphFallbackFont( mpFontList,
aFontSelData, nFallbackLevel-nDevSpecificFallback, aMissingCodes );
if( !pFallbackFont )
break;
aFontSelData.mpFontEntry = pFallbackFont;
aFontSelData.mpFontData = pFallbackFont->maFontSelData.mpFontData;
if( mpFontEntry && nFallbackLevel < MAX_FALLBACK-1)
{
// ignore fallback font if it is the same as the original font
if( mpFontEntry->maFontSelData.mpFontData == aFontSelData.mpFontData )
{
mpFontCache->Release( pFallbackFont );
continue;
}
}
#if defined(HDU_DEBUG)
{
ByteString aOrigFontName( maFont.GetName(), RTL_TEXTENCODING_UTF8);
ByteString aFallbackName( aFontSelData.mpFontData->GetFamilyName(),
RTL_TEXTENCODING_UTF8);
fprintf(stderr,"\tGlyphFallback[lvl=%d] \"%s\" -> \"%s\" (q=%d)\n",
nFallbackLevel, aOrigFontName.GetBuffer(), aFallbackName.GetBuffer(),
aFontSelData.mpFontData->GetQuality());
}
#endif
pFallbackFont->mnSetFontFlags = mpGraphics->SetFont( &aFontSelData, nFallbackLevel );
// create and add glyph fallback layout to multilayout
rLayoutArgs.ResetPos();
SalLayout* pFallback = mpGraphics->GetTextLayout( rLayoutArgs, nFallbackLevel );
if( pFallback )
{
if( pFallback->LayoutText( rLayoutArgs ) )
{
if( !pMultiSalLayout )
pMultiSalLayout = new MultiSalLayout( *pSalLayout );
pMultiSalLayout->AddFallback( *pFallback,
rLayoutArgs.maRuns, aFontSelData.mpFontData );
if (nFallbackLevel == MAX_FALLBACK-1)
pMultiSalLayout->SetInComplete();
}
else
{
// there is no need for a font that couldn't resolve anything
pFallback->Release();
}
}
mpFontCache->Release( pFallbackFont );
// break when this fallback was sufficient
if( !rLayoutArgs.PrepareFallback() )
break;
}
if( pMultiSalLayout && pMultiSalLayout->LayoutText( rLayoutArgs ) )
pSalLayout = pMultiSalLayout;
// restore orig font settings
pSalLayout->InitFont();
rLayoutArgs.maRuns = aLayoutRuns;
return pSalLayout;
}
#endif
// -----------------------------------------------------------------------
ImplLayoutArgs OutputDevice::ImplPrepareLayoutArgs( String& rStr,
xub_StrLen nMinIndex, xub_StrLen nLen,
long nPixelWidth, const sal_Int32* pDXArray ) const
{
// get string length for calculating extents
xub_StrLen nEndIndex = rStr.Len();
if( (ULONG)nMinIndex + nLen < nEndIndex )
nEndIndex = nMinIndex + nLen;
// don't bother if there is nothing to do
if( nEndIndex < nMinIndex )
nEndIndex = nMinIndex;
int nLayoutFlags = 0;
if( mnTextLayoutMode & TEXT_LAYOUT_BIDI_RTL )
nLayoutFlags |= SAL_LAYOUT_BIDI_RTL;
if( mnTextLayoutMode & TEXT_LAYOUT_BIDI_STRONG )
nLayoutFlags |= SAL_LAYOUT_BIDI_STRONG;
else if( 0 == (mnTextLayoutMode & TEXT_LAYOUT_BIDI_RTL) )
{
// disable Bidi if no RTL hint and no RTL codes used
const xub_Unicode* pStr = rStr.GetBuffer() + nMinIndex;
const xub_Unicode* pEnd = rStr.GetBuffer() + nEndIndex;
for( ; pStr < pEnd; ++pStr )
if( ((*pStr >= 0x0580) && (*pStr < 0x0800)) // middle eastern scripts
|| ((*pStr >= 0xFB18) && (*pStr < 0xFE00)) // hebrew + arabic A presentation forms
|| ((*pStr >= 0xFE70) && (*pStr < 0xFEFF)) ) // arabic presentation forms B
break;
if( pStr >= pEnd )
nLayoutFlags |= SAL_LAYOUT_BIDI_STRONG;
}
if( mbKerning )
nLayoutFlags |= SAL_LAYOUT_KERNING_PAIRS;
if( maFont.GetKerning() & KERNING_ASIAN )
nLayoutFlags |= SAL_LAYOUT_KERNING_ASIAN;
if( maFont.IsVertical() )
nLayoutFlags |= SAL_LAYOUT_VERTICAL;
if( mnTextLayoutMode & TEXT_LAYOUT_ENABLE_LIGATURES )
nLayoutFlags |= SAL_LAYOUT_ENABLE_LIGATURES;
else if( mnTextLayoutMode & TEXT_LAYOUT_COMPLEX_DISABLED )
nLayoutFlags |= SAL_LAYOUT_COMPLEX_DISABLED;
else
{
// disable CTL for non-CTL text
const sal_Unicode* pStr = rStr.GetBuffer() + nMinIndex;
const sal_Unicode* pEnd = rStr.GetBuffer() + nEndIndex;
for( ; pStr < pEnd; ++pStr )
if( ((*pStr >= 0x0300) && (*pStr < 0x0370)) // diacritical marks
|| ((*pStr >= 0x0590) && (*pStr < 0x10A0)) // many CTL scripts
|| ((*pStr >= 0x1100) && (*pStr < 0x1200)) // hangul jamo
|| ((*pStr >= 0x1700) && (*pStr < 0x1900)) // many CTL scripts
|| ((*pStr >= 0xFB1D) && (*pStr < 0xFE00)) // middle east presentation
|| ((*pStr >= 0xFE70) && (*pStr < 0xFEFF)) ) // arabic presentation B
break;
if( pStr >= pEnd )
nLayoutFlags |= SAL_LAYOUT_COMPLEX_DISABLED;
}
if( meTextLanguage ) //TODO: (mnTextLayoutMode & TEXT_LAYOUT_SUBSTITUTE_DIGITS)
{
// disable character localization when no digits used
const sal_Unicode* pBase = rStr.GetBuffer();
const sal_Unicode* pStr = pBase + nMinIndex;
const sal_Unicode* pEnd = pBase + nEndIndex;
for( ; pStr < pEnd; ++pStr )
{
// TODO: are there non-digit localizations?
if( (*pStr >= '0') && (*pStr <= '9') )
{
// translate characters to local preference
sal_UCS4 cChar = GetLocalizedChar( *pStr, meTextLanguage );
if( cChar != *pStr )
// TODO: are the localized digit surrogates?
rStr.SetChar( static_cast<USHORT>(pStr - pBase),
static_cast<sal_Unicode>(cChar) );
}
}
}
// right align for RTL text, DRAWPOS_REVERSED, RTL window style
bool bRightAlign = ((mnTextLayoutMode & TEXT_LAYOUT_BIDI_RTL) != 0);
if( mnTextLayoutMode & TEXT_LAYOUT_TEXTORIGIN_LEFT )
bRightAlign = false;
else if ( mnTextLayoutMode & TEXT_LAYOUT_TEXTORIGIN_RIGHT )
bRightAlign = true;
// SSA: hack for western office, ie text get right aligned
// for debugging purposes of mirrored UI
//static const char* pEnv = getenv( "SAL_RTL_MIRRORTEXT" );
bool bRTLWindow = IsRTLEnabled();
bRightAlign ^= bRTLWindow;
if( bRightAlign )
nLayoutFlags |= SAL_LAYOUT_RIGHT_ALIGN;
// set layout options
ImplLayoutArgs aLayoutArgs( rStr.GetBuffer(), rStr.Len(), nMinIndex, nEndIndex, nLayoutFlags );
int nOrientation = mpFontEntry ? mpFontEntry->mnOrientation : 0;
aLayoutArgs.SetOrientation( nOrientation );
aLayoutArgs.SetLayoutWidth( nPixelWidth );
aLayoutArgs.SetDXArray( pDXArray );
return aLayoutArgs;
}
// =======================================================================
void OutputDevice::ImplInitFont() const
{
//DBG_TESTSOLARMUTEX();
if ( mbInitFont )
{
if ( meOutDevType != OUTDEV_PRINTER )
{
// decide if antialiasing is appropriate
bool bNonAntialiased = (GetAntialiasing() & ANTIALIASING_DISABLE_TEXT) != 0;
#ifdef AVS
const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
bNonAntialiased |= ((rStyleSettings.GetDisplayOptions() & DISPLAY_OPTION_AA_DISABLE) != 0);
bNonAntialiased |= (int(rStyleSettings.GetAntialiasingMinPixelHeight()) > mpFontEntry->maFontSelData.mnHeight);
#endif
mpFontEntry->maFontSelData.mbNonAntialiased = bNonAntialiased;
}
#ifdef AVS
if( !mpPDFWriter || !mpPDFWriter->isBuiltinFont( mpFontEntry->maFontSelData.mpFontData ) )
{
#endif
// select font in the device layers
mpFontEntry->mnSetFontFlags = mpGraphics->SetFont( &(mpFontEntry->maFontSelData), 0 );
#ifdef AVS
}
#endif
mbInitFont = false;
}
}
// -----------------------------------------------------------------------
bool OutputDevice::ImplNewFont() const
{
//DBG_TESTSOLARMUTEX();
#ifdef AVS
// get correct font list on the PDF writer if necessary
if( mpPDFWriter )
{
const ImplSVData* pSVData = ImplGetSVData();
if( mpFontList == pSVData->maGDIData.mpScreenFontList
|| mpFontCache == pSVData->maGDIData.mpScreenFontCache )
const_cast<OutputDevice&>(*this).ImplUpdateFontData( true );
}
#endif
if ( !mbNewFont )
return true;
// we need a graphics
if ( !mpGraphics )
return false;
SalGraphics* pGraphics = mpGraphics;
ImplInitFontList();
// convert to pixel height
// TODO: replace integer based aSize completely with subpixel accurate type
float fExactHeight = ImplFloatLogicHeightToDevicePixel( static_cast<float>(maFont.GetHeight()) );
Size aSize = ImplLogicToDevicePixel( maFont.GetSize() );
if ( !aSize.Height() )
{
// use default pixel height only when logical height is zero
if ( maFont.GetSize().Height() )
aSize.Height() = 1;
else
aSize.Height() = (12*mnDPIY)/72;
fExactHeight = static_cast<float>(aSize.Height());
}
// select the default width only when logical width is zero
if( (0 == aSize.Width()) && (0 != maFont.GetSize().Width()) )
aSize.Width() = 1;
// get font entry
ImplDirectFontSubstitution* pDevSpecificSubst = NULL;
#ifdef AVS
if( mpOutDevData )
pDevSpecificSubst = &mpOutDevData->maDevFontSubst;
#endif
ImplFontEntry* pOldEntry = mpFontEntry;
mpFontEntry = mpFontCache->GetFontEntry( mpFontList, maFont, aSize, fExactHeight, pDevSpecificSubst );
if( pOldEntry )
mpFontCache->Release( pOldEntry );
ImplFontEntry* pFontEntry = mpFontEntry;
// mark when lower layers need to get involved
mbNewFont = FALSE;
if( pFontEntry != pOldEntry )
mbInitFont = TRUE;
// select font when it has not been initialized yet
if ( !pFontEntry->mbInit )
{
ImplInitFont();
// get metric data from device layers
if ( pGraphics )
{
pFontEntry->mbInit = true;
pFontEntry->maMetric.mnOrientation = static_cast<short>(pFontEntry->maFontSelData.mnOrientation);
#ifdef AVS
if( mpPDFWriter && mpPDFWriter->isBuiltinFont( pFontEntry->maFontSelData.mpFontData ) )
mpPDFWriter->getFontMetric( &pFontEntry->maFontSelData, &(pFontEntry->maMetric) );
else
#endif
pGraphics->GetFontMetric( &(pFontEntry->maMetric) );
pFontEntry->maMetric.ImplInitTextLineSize( this );
pFontEntry->maMetric.ImplInitAboveTextLineSize();
pFontEntry->mnLineHeight = pFontEntry->maMetric.mnAscent + pFontEntry->maMetric.mnDescent;
if( pFontEntry->maFontSelData.mnOrientation
&& !pFontEntry->maMetric.mnOrientation
&& (meOutDevType != OUTDEV_PRINTER) )
{
pFontEntry->mnOwnOrientation = static_cast<short>(pFontEntry->maFontSelData.mnOrientation);
pFontEntry->mnOrientation = pFontEntry->mnOwnOrientation;
}
else
pFontEntry->mnOrientation = pFontEntry->maMetric.mnOrientation;
}
}
// enable kerning array if requested
if ( maFont.GetKerning() & KERNING_FONTSPECIFIC )
{
// TODO: test if physical font supports kerning and disable if not
if( pFontEntry->maMetric.mbKernableFont )
mbKerning = true;
}
else
mbKerning = false;
if ( maFont.GetKerning() & KERNING_ASIAN )
mbKerning = true;
// calculate EmphasisArea
mnEmphasisAscent = 0;
mnEmphasisDescent = 0;
if ( maFont.GetEmphasisMark() & EMPHASISMARK_STYLE )
{
FontEmphasisMark nEmphasisMark = ImplGetEmphasisMarkStyle( maFont );
long nEmphasisHeight = (pFontEntry->mnLineHeight*250)/1000;
if ( nEmphasisHeight < 1 )
nEmphasisHeight = 1;
if ( nEmphasisMark & EMPHASISMARK_POS_BELOW )
mnEmphasisDescent = nEmphasisHeight;
else
mnEmphasisAscent = nEmphasisHeight;
}
// calculate text offset depending on TextAlignment
TextAlign eAlign = maFont.GetAlign();
if ( eAlign == ALIGN_BASELINE )
{
mnTextOffX = 0;
mnTextOffY = 0;
}
else if ( eAlign == ALIGN_TOP )
{
mnTextOffX = 0;
mnTextOffY = +pFontEntry->maMetric.mnAscent + mnEmphasisAscent;
if ( pFontEntry->mnOrientation )
ImplRotatePos( 0, 0, mnTextOffX, mnTextOffY, pFontEntry->mnOrientation );
}
else // eAlign == ALIGN_BOTTOM
{
mnTextOffX = 0;
mnTextOffY = -pFontEntry->maMetric.mnDescent + mnEmphasisDescent;
if ( pFontEntry->mnOrientation )
ImplRotatePos( 0, 0, mnTextOffX, mnTextOffY, pFontEntry->mnOrientation );
}
mbTextLines = ((maFont.GetUnderline() != UNDERLINE_NONE) && (maFont.GetUnderline() != UNDERLINE_DONTKNOW)) ||
((maFont.GetOverline() != UNDERLINE_NONE) && (maFont.GetOverline() != UNDERLINE_DONTKNOW)) ||
((maFont.GetStrikeout() != STRIKEOUT_NONE) && (maFont.GetStrikeout() != STRIKEOUT_DONTKNOW));
mbTextSpecial = maFont.IsShadow() || maFont.IsOutline() ||
(maFont.GetRelief() != RELIEF_NONE);
// #95414# fix for OLE objects which use scale factors very creatively
if( mbMap && !aSize.Width() )
{
int nOrigWidth = pFontEntry->maMetric.mnWidth;
float fStretch = (float)maMapRes.mnMapScNumX * maMapRes.mnMapScDenomY;
fStretch /= (float)maMapRes.mnMapScNumY * maMapRes.mnMapScDenomX;
int nNewWidth = (int)(nOrigWidth * fStretch + 0.5);
if( (nNewWidth != nOrigWidth) && (nNewWidth != 0) )
{
Size aOrigSize = maFont.GetSize();
const_cast<Font&>(maFont).SetSize( Size( nNewWidth, aSize.Height() ) );
mbMap = FALSE;
mbNewFont = TRUE;
ImplNewFont(); // recurse once using stretched width
mbMap = TRUE;
const_cast<Font&>(maFont).SetSize( aOrigSize );
}
}
return true;
}
// =======================================================================
FontEmphasisMark OutputDevice::ImplGetEmphasisMarkStyle( const Font& rFont ) const
{
FontEmphasisMark nEmphasisMark = rFont.GetEmphasisMark();
// If no Position is set, then calculate the default position, which
// depends on the language
if ( !(nEmphasisMark & (EMPHASISMARK_POS_ABOVE | EMPHASISMARK_POS_BELOW)) )
{
LanguageType eLang = rFont.GetLanguage();
// In Chinese Simplified the EmphasisMarks are below/left
if ( (eLang == LANGUAGE_CHINESE_SIMPLIFIED) ||
(eLang == LANGUAGE_CHINESE_SINGAPORE) )
nEmphasisMark |= EMPHASISMARK_POS_BELOW;
else
{
eLang = rFont.GetCJKContextLanguage();
// In Chinese Simplified the EmphasisMarks are below/left
if ( (eLang == LANGUAGE_CHINESE_SIMPLIFIED) ||
(eLang == LANGUAGE_CHINESE_SINGAPORE) )
nEmphasisMark |= EMPHASISMARK_POS_BELOW;
else
nEmphasisMark |= EMPHASISMARK_POS_ABOVE;
}
}
return nEmphasisMark;
}
// =======================================================================
void OutputDevice::ImplInitFontList() const
{
if( ! mpFontList->Count() )
{
if( mpGraphics )
{
//RTL_LOGFILE_CONTEXT( aLog, "OutputDevice::ImplInitFontList()" );
mpGraphics->GetDevFontList( mpFontList );
}
}
}
void OutputDevice::DrawTextArray( const Point& rStartPt, const XubString& rStr,
const sal_Int32* pDXAry,
xub_StrLen nIndex,
xub_StrLen nLen )
{
SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen, rStartPt, 0, pDXAry, true );
if( pSalLayout )
{
ImplDrawText( *pSalLayout );
pSalLayout->Release();
}
}
void OutputDevice::DrawStretchText( const Point& rStartPt, ULONG nWidth,
const XubString& rStr,
xub_StrLen nIndex , xub_StrLen nLen )
{
SalLayout* pSalLayout = ImplLayout( rStr, nIndex, nLen, rStartPt, nWidth, NULL, true );
if( pSalLayout )
{
ImplDrawText( *pSalLayout );
pSalLayout->Release();
}
}
void OutputDevice::DrawText( const Rectangle& rRect,
const XubString& rOrigStr, USHORT nStyle,
MetricVector* pVector , String* pDisplayText )
{
if ( ! pVector || !rOrigStr.Len() || rRect.IsEmpty() )
return;
// we need a graphics
if( !mpGraphics )
return;
if( mbInitClipRegion )
ImplInitClipRegion();
if( mbOutputClipped )
return;
// #i47157# Factored out to ImplDrawTextRect(), to be used also
// from AddTextRectActions()
ImplDrawText( rRect, rOrigStr, nStyle, pVector, pDisplayText );
}
void OutputDevice::DrawTextLine( const Point& rPos, long nWidth,
FontStrikeout eStrikeout,
FontUnderline eUnderline,
FontUnderline eOverline,
BOOL bUnderlineAbove )
{
if ( ((eUnderline == UNDERLINE_NONE) || (eUnderline == UNDERLINE_DONTKNOW)) &&
((eOverline == UNDERLINE_NONE) || (eOverline == UNDERLINE_DONTKNOW)) &&
((eStrikeout == STRIKEOUT_NONE) || (eStrikeout == STRIKEOUT_DONTKNOW)) )
return;
// we need a graphics
if( !mpGraphics )
return;
if( mbInitClipRegion )
ImplInitClipRegion();
if( mbOutputClipped )
return;
// initialize font if needed to get text offsets
// TODO: only needed for mnTextOff!=(0,0)
if( mbNewFont )
if( !ImplNewFont() )
return;
if( mbInitFont )
ImplInitFont();
Point aPos = ImplLogicToDevicePixel( rPos );
nWidth = ImplLogicWidthToDevicePixel( nWidth );
aPos += Point( mnTextOffX, mnTextOffY );
ImplDrawTextLine( aPos.X(), aPos.X(), aPos.Y(), nWidth, eStrikeout, eUnderline, eOverline, bUnderlineAbove );
}
void OutputDevice::DrawBitmap( const Point& rDestPt,
const Bitmap& rBitmap )
{
const Size aSizePix( rBitmap.GetSizePixel() );
ImplDrawBitmap( rDestPt, PixelToLogic( aSizePix ), Point(), aSizePix, rBitmap, META_BMP_ACTION );
}
void OutputDevice::DrawBitmap( const Point& rDestPt, const Size& rDestSize,
const Bitmap& rBitmap )
{
ImplDrawBitmap( rDestPt, rDestSize, Point(), rBitmap.GetSizePixel(), rBitmap, META_BMPSCALE_ACTION );
}
void OutputDevice::DrawBitmap( const Point& rDestPt, const Size& rDestSize,
const Point& rSrcPtPixel, const Size& rSrcSizePixel,
const Bitmap& rBitmap )
{
ImplDrawBitmap( rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, rBitmap, META_BMPSCALEPART_ACTION );
}
void OutputDevice::ImplDrawBitmap( const Point& rDestPt, const Size& rDestSize,
const Point& rSrcPtPixel, const Size& rSrcSizePixel,
const Bitmap& rBitmap, const ULONG nAction )
{
Bitmap aBmp( rBitmap );
if ( ROP_INVERT == meRasterOp )
{
DrawRect( Rectangle( rDestPt, rDestSize ) );
return;
}
OUTDEV_INIT();
if( !aBmp.IsEmpty() )
{
SalTwoRect aPosAry;
aPosAry.mnSrcX = rSrcPtPixel.X();
aPosAry.mnSrcY = rSrcPtPixel.Y();
aPosAry.mnSrcWidth = rSrcSizePixel.Width();
aPosAry.mnSrcHeight = rSrcSizePixel.Height();
aPosAry.mnDestX = ImplLogicXToDevicePixel( rDestPt.X() );
aPosAry.mnDestY = ImplLogicYToDevicePixel( rDestPt.Y() );
aPosAry.mnDestWidth = ImplLogicWidthToDevicePixel( rDestSize.Width() );
aPosAry.mnDestHeight = ImplLogicHeightToDevicePixel( rDestSize.Height() );
const ULONG nMirrFlags = ImplAdjustTwoRect( aPosAry, aBmp.GetSizePixel() );
if ( aPosAry.mnSrcWidth && aPosAry.mnSrcHeight && aPosAry.mnDestWidth && aPosAry.mnDestHeight )
{
if ( nMirrFlags )
aBmp.Mirror( nMirrFlags );
/* #i75264# (corrected with #i81576#)
* sometimes a bitmap is scaled to a ridiculous size and drawn
* to a quite normal VDev, so only a very small part of
* the scaled bitmap will be visible. However actually scaling
* the bitmap will use so much memory that we end with a crash.
* Workaround: since only a small part of the scaled bitmap will
* be actually drawn anyway (because of clipping on the device
* boundary), limit the destination and source rectangles so
* that the destination rectangle will overlap the device but only
* be reasonably (say factor 2) larger than the device itself.
*/
if( aPosAry.mnDestWidth > 2048 || aPosAry.mnDestHeight > 2048 )
{
// #i81576# do the following trick only if there is overlap at all
// else the formulae don't work
// theoretically in this case we wouldn't need to draw the bitmap at all
// however there are some esoteric case where that is needed
if( aPosAry.mnDestX + aPosAry.mnDestWidth >= 0
&& aPosAry.mnDestX < mnOutWidth
&& aPosAry.mnDestY + aPosAry.mnDestHeight >= 0
&& aPosAry.mnDestY < mnOutHeight )
{
// reduce scaling to something reasonable taking into account the output size
if( aPosAry.mnDestWidth > 3*mnOutWidth && aPosAry.mnSrcWidth )
{
const double nScaleX = aPosAry.mnDestWidth/double(aPosAry.mnSrcWidth);
if( aPosAry.mnDestX + aPosAry.mnDestWidth > mnOutWidth )
{
aPosAry.mnDestWidth = Max(long(0),mnOutWidth-aPosAry.mnDestX);
}
if( aPosAry.mnDestX < 0 )
{
aPosAry.mnDestWidth += aPosAry.mnDestX;
aPosAry.mnSrcX -= static_cast<long>(aPosAry.mnDestX / nScaleX);
aPosAry.mnDestX = 0;
}
aPosAry.mnSrcWidth = static_cast<long>(aPosAry.mnDestWidth / nScaleX);
}
if( aPosAry.mnDestHeight > 3*mnOutHeight && aPosAry.mnSrcHeight != 0 )
{
const double nScaleY = aPosAry.mnDestHeight/double(aPosAry.mnSrcHeight);
if( aPosAry.mnDestY + aPosAry.mnDestHeight > mnOutHeight )
{
aPosAry.mnDestHeight = Max(long(0),mnOutHeight-aPosAry.mnDestY);
}
if( aPosAry.mnDestY < 0 )
{
aPosAry.mnDestHeight += aPosAry.mnDestY;
aPosAry.mnSrcY -= static_cast<long>(aPosAry.mnDestY / nScaleY);
aPosAry.mnDestY = 0;
}
aPosAry.mnSrcHeight = static_cast<long>(aPosAry.mnDestHeight / nScaleY);
}
}
}
if ( aPosAry.mnSrcWidth && aPosAry.mnSrcHeight && aPosAry.mnDestWidth && aPosAry.mnDestHeight )
mpGraphics->DrawBitmap( &aPosAry, *aBmp.ImplGetImpBitmap()->ImplGetSalBitmap(), this );
}
}
}
void OutputDevice::DrawBitmapEx( const Point& rDestPt,
const BitmapEx& rBitmapEx )
{
if( TRANSPARENT_NONE == rBitmapEx.GetTransparentType() )
DrawBitmap( rDestPt, rBitmapEx.GetBitmap() );
else
{
const Size aSizePix( rBitmapEx.GetSizePixel() );
ImplDrawBitmapEx( rDestPt, PixelToLogic( aSizePix ), Point(), aSizePix, rBitmapEx, META_BMPEX_ACTION );
}
}
void OutputDevice::DrawBitmapEx( const Point& rDestPt, const Size& rDestSize,
const BitmapEx& rBitmapEx )
{
if ( TRANSPARENT_NONE == rBitmapEx.GetTransparentType() )
DrawBitmap( rDestPt, rDestSize, rBitmapEx.GetBitmap() );
else
ImplDrawBitmapEx( rDestPt, rDestSize, Point(), rBitmapEx.GetSizePixel(), rBitmapEx, META_BMPEXSCALE_ACTION );
}
void OutputDevice::DrawBitmapEx( const Point& rDestPt, const Size& rDestSize,
const Point& rSrcPtPixel, const Size& rSrcSizePixel,
const BitmapEx& rBitmapEx )
{
if( TRANSPARENT_NONE == rBitmapEx.GetTransparentType() )
DrawBitmap( rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, rBitmapEx.GetBitmap() );
else
ImplDrawBitmapEx( rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, rBitmapEx, META_BMPEXSCALEPART_ACTION );
}
void OutputDevice::ImplDrawBitmapEx( const Point& rDestPt, const Size& rDestSize,
const Point& rSrcPtPixel, const Size& rSrcSizePixel,
const BitmapEx& rBitmapEx, const ULONG nAction )
{
BitmapEx aBmpEx( rBitmapEx );
if ( ROP_INVERT == meRasterOp )
{
DrawRect( Rectangle( rDestPt, rDestSize ) );
return;
}
OUTDEV_INIT();
if( aBmpEx.IsAlpha() )
{
ImplDrawAlpha( aBmpEx.GetBitmap(), aBmpEx.GetAlpha(), rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel );
return;
}
if( !( !aBmpEx ) )
{
SalTwoRect aPosAry;
aPosAry.mnSrcX = rSrcPtPixel.X();
aPosAry.mnSrcY = rSrcPtPixel.Y();
aPosAry.mnSrcWidth = rSrcSizePixel.Width();
aPosAry.mnSrcHeight = rSrcSizePixel.Height();
aPosAry.mnDestX = ImplLogicXToDevicePixel( rDestPt.X() );
aPosAry.mnDestY = ImplLogicYToDevicePixel( rDestPt.Y() );
aPosAry.mnDestWidth = ImplLogicWidthToDevicePixel( rDestSize.Width() );
aPosAry.mnDestHeight = ImplLogicHeightToDevicePixel( rDestSize.Height() );
const ULONG nMirrFlags = ImplAdjustTwoRect( aPosAry, aBmpEx.GetSizePixel() );
if( aPosAry.mnSrcWidth && aPosAry.mnSrcHeight && aPosAry.mnDestWidth && aPosAry.mnDestHeight )
{
if( nMirrFlags )
aBmpEx.Mirror( nMirrFlags );
const ImpBitmap* pImpBmp = aBmpEx.ImplGetBitmapImpBitmap();
const ImpBitmap* pMaskBmp = aBmpEx.ImplGetMaskImpBitmap();
if ( pMaskBmp )
{
// #4919452# reduce operation area to bounds of
// cliprect. since masked transparency involves
// creation of a large vdev and copying the screen
// content into that (slooow read from framebuffer),
// that should considerably increase performance for
// large bitmaps and small clippings.
// Note that this optimisation is a workaround for a
// Writer peculiarity, namely, to decompose background
// graphics into myriads of disjunct, tiny
// rectangles. That otherwise kills us here, since for
// transparent output, SAL always prepares the whole
// bitmap, if aPosAry contains the whole bitmap (and
// it's _not_ to blame for that).
// Note the call to ImplPixelToDevicePixel(), since
// aPosAry already contains the mnOutOff-offsets, they
// also have to be applied to the region
Rectangle aClipRegionBounds( ImplPixelToDevicePixel(maRegion).GetBoundRect() );
// TODO: Also respect scaling (that's a bit tricky,
// since the source points have to move fractional
// amounts (which is not possible, thus has to be
// emulated by increases copy area)
// const double nScaleX( aPosAry.mnDestWidth / aPosAry.mnSrcWidth );
// const double nScaleY( aPosAry.mnDestHeight / aPosAry.mnSrcHeight );
// for now, only identity scales allowed
if( !aClipRegionBounds.IsEmpty() &&
aPosAry.mnDestWidth == aPosAry.mnSrcWidth &&
aPosAry.mnDestHeight == aPosAry.mnSrcHeight )
{
// now intersect dest rect with clip region
aClipRegionBounds.Intersection( Rectangle( aPosAry.mnDestX,
aPosAry.mnDestY,
aPosAry.mnDestX + aPosAry.mnDestWidth - 1,
aPosAry.mnDestY + aPosAry.mnDestHeight - 1 ) );
// Note: I could theoretically optimize away the
// DrawBitmap below, if the region is empty
// here. Unfortunately, cannot rule out that
// somebody relies on the side effects.
if( !aClipRegionBounds.IsEmpty() )
{
aPosAry.mnSrcX += aClipRegionBounds.Left() - aPosAry.mnDestX;
aPosAry.mnSrcY += aClipRegionBounds.Top() - aPosAry.mnDestY;
aPosAry.mnSrcWidth = aClipRegionBounds.GetWidth();
aPosAry.mnSrcHeight = aClipRegionBounds.GetHeight();
aPosAry.mnDestX = aClipRegionBounds.Left();
aPosAry.mnDestY = aClipRegionBounds.Top();
aPosAry.mnDestWidth = aClipRegionBounds.GetWidth();
aPosAry.mnDestHeight = aClipRegionBounds.GetHeight();
}
}
mpGraphics->DrawBitmap( &aPosAry, *pImpBmp->ImplGetSalBitmap(),
*pMaskBmp->ImplGetSalBitmap(),
this );
}
else
{
mpGraphics->DrawBitmap( &aPosAry, *pImpBmp->ImplGetSalBitmap(), this );
}
}
}
}
void OutputDevice::ImplDrawAlpha( const Bitmap& rBmp, const AlphaMask& rAlpha,
const Point& rDestPt, const Size& rDestSize,
const Point& rSrcPtPixel, const Size& rSrcSizePixel )
{
const Point aNullPt;
Point aOutPt( LogicToPixel( rDestPt ) );
Size aOutSz( LogicToPixel( rDestSize ) );
Rectangle aDstRect( aNullPt, GetOutputSizePixel() );
const BOOL bHMirr = aOutSz.Width() < 0, bVMirr = aOutSz.Height() < 0;
#ifdef AVS
if( OUTDEV_WINDOW == meOutDevType )
{
const Region aPaintRgn( ( (Window*) this )->GetPaintRegion() );
if( !aPaintRgn.IsNull() )
aDstRect.Intersection( LogicToPixel( aPaintRgn.GetBoundRect() ) );
}
#endif
if( bHMirr )
{
aOutSz.Width() = -aOutSz.Width();
aOutPt.X() -= ( aOutSz.Width() - 1L );
}
if( bVMirr )
{
aOutSz.Height() = -aOutSz.Height();
aOutPt.Y() -= ( aOutSz.Height() - 1L );
}
if( !aDstRect.Intersection( Rectangle( aOutPt, aOutSz ) ).IsEmpty() )
{
bool bNativeAlpha = false;
static const char* pDisableNative = getenv( "SAL_DISABLE_NATIVE_ALPHA");
// #i83087# Naturally, system alpha blending cannot work with
// separate alpha VDev
if( !pDisableNative && !bHMirr && !bVMirr )
{
Point aRelPt = aOutPt + Point( mnOutOffX, mnOutOffY );
SalTwoRect aTR = {
rSrcPtPixel.X(), rSrcPtPixel.Y(),
rSrcSizePixel.Width(), rSrcSizePixel.Height(),
aRelPt.X(), aRelPt.Y(),
aOutSz.Width(), aOutSz.Height()
};
SalBitmap* pSalSrcBmp = rBmp.ImplGetImpBitmap()->ImplGetSalBitmap();
SalBitmap* pSalAlphaBmp = rAlpha.ImplGetImpBitmap()->ImplGetSalBitmap();
bNativeAlpha = mpGraphics->DrawAlphaBitmap( aTR, *pSalSrcBmp, *pSalAlphaBmp, this );
}
//VirtualDevice* pOldVDev = mpAlphaVDev;
Rectangle aBmpRect( aNullPt, rBmp.GetSizePixel() );
if( !bNativeAlpha
&& !aBmpRect.Intersection( Rectangle( rSrcPtPixel, rSrcSizePixel ) ).IsEmpty() )
{
//GDIMetaFile* pOldMetaFile = mpMetaFile; mpMetaFile = NULL;
const BOOL bOldMap = mbMap; mbMap = FALSE;
Bitmap aBmp( GetBitmap( aDstRect.TopLeft(), aDstRect.GetSize() ) );
// #109044# The generated bitmap need not necessarily be
// of aDstRect dimensions, it's internally clipped to
// window bounds. Thus, we correct the dest size here,
// since we later use it (in nDstWidth/Height) for pixel
// access)
// #i38887# reading from screen may sometimes fail
if( aBmp.ImplGetImpBitmap() )
aDstRect.SetSize( aBmp.GetSizePixel() );
BitmapColor aDstCol;
const long nSrcWidth = aBmpRect.GetWidth(), nSrcHeight = aBmpRect.GetHeight();
const long nDstWidth = aDstRect.GetWidth(), nDstHeight = aDstRect.GetHeight();
const long nOutWidth = aOutSz.Width(), nOutHeight = aOutSz.Height();
const long nOffX = aDstRect.Left() - aOutPt.X(), nOffY = aDstRect.Top() - aOutPt.Y();
long nX, nOutX, nY, nOutY;
long nMirrOffX = 0;
long nMirrOffY = 0;
long* pMapX = new long[ nDstWidth ];
long* pMapY = new long[ nDstHeight ];
// create horizontal mapping table
if( bHMirr )
nMirrOffX = ( aBmpRect.Left() << 1 ) + nSrcWidth - 1;
for( nX = 0L, nOutX = nOffX; nX < nDstWidth; nX++, nOutX++ )
{
pMapX[ nX ] = aBmpRect.Left() + nOutX * nSrcWidth / nOutWidth;
if( bHMirr )
pMapX[ nX ] = nMirrOffX - pMapX[ nX ];
}
// create vertical mapping table
if( bVMirr )
nMirrOffY = ( aBmpRect.Top() << 1 ) + nSrcHeight - 1;
for( nY = 0L, nOutY = nOffY; nY < nDstHeight; nY++, nOutY++ )
{
pMapY[ nY ] = aBmpRect.Top() + nOutY * nSrcHeight / nOutHeight;
if( bVMirr )
pMapY[ nY ] = nMirrOffY - pMapY[ nY ];
}
BitmapReadAccess* pP = ( (Bitmap&) rBmp ).AcquireReadAccess();
BitmapReadAccess* pA = ( (AlphaMask&) rAlpha ).AcquireReadAccess();
//DBG_ASSERT( pA->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL ||
//pA->GetScanlineFormat() == BMP_FORMAT_8BIT_TC_MASK,
//"OutputDevice::ImplDrawAlpha(): non-8bit alpha no longer supported!" );
// #i38887# reading from screen may sometimes fail
if( aBmp.ImplGetImpBitmap() )
{
Bitmap aTmp;
#ifdef AVS
if( mpAlphaVDev )
{
#endif
aTmp = ImplBlendWithAlpha(
aBmp,pP,pA,
aDstRect,
nOffY,nDstHeight,
nOffX,nDstWidth,
pMapX,pMapY );
#ifdef AVS
}
else
{
aTmp = ImplBlend(
aBmp,pP,pA,
nOffY,nDstHeight,
nOffX,nDstWidth,
aBmpRect,aOutSz,
bHMirr,bVMirr,
pMapX,pMapY );
}
// #110958# Disable alpha VDev, we're doing the necessary
// stuff explicitely furher below
if( mpAlphaVDev )
mpAlphaVDev = NULL;
#endif
DrawBitmap( aDstRect.TopLeft(),
aTmp );
#ifdef AVS
// #110958# Enable alpha VDev again
mpAlphaVDev = pOldVDev;
#endif
}
( (Bitmap&) rBmp ).ReleaseAccess( pP );
( (AlphaMask&) rAlpha ).ReleaseAccess( pA );
delete[] pMapX;
delete[] pMapY;
mbMap = bOldMap;
//mpMetaFile = pOldMetaFile;
}
}
}
// ------------------------------------------------------------------------
Bitmap OutputDevice::ImplBlendWithAlpha( Bitmap aBmp,
BitmapReadAccess* pP,
BitmapReadAccess* pA,
const Rectangle& aDstRect,
const sal_Int32 nOffY,
const sal_Int32 nDstHeight,
const sal_Int32 nOffX,
const sal_Int32 nDstWidth,
const long* pMapX,
const long* pMapY )
{
BitmapColor aDstCol,aSrcCol;
BYTE nSrcAlpha, nDstAlpha;
Bitmap res;
int nX, nOutX, nY, nOutY;
#ifdef QWE
OSL_ENSURE(mpAlphaVDev,
"ImplBlendWithAlpha(): call me only with valid alpha VDev!" );
BOOL bOldMapMode( mpAlphaVDev->IsMapModeEnabled() );
mpAlphaVDev->EnableMapMode(FALSE);
Bitmap aAlphaBitmap( mpAlphaVDev->GetBitmap( aDstRect.TopLeft(), aDstRect.GetSize() ) );
BitmapWriteAccess* pAlphaW = aAlphaBitmap.AcquireWriteAccess();
#endif
if( GetBitCount() <= 8 )
{
Bitmap aDither( aBmp.GetSizePixel(), 8 );
BitmapColor aIndex( 0 );
BitmapReadAccess* pB = aBmp.AcquireReadAccess();
BitmapWriteAccess* pW = aDither.AcquireWriteAccess();
if( pB && pP && pA && pW /*&& pAlphaW*/ )
{
for( nY = 0, nOutY = nOffY; nY < nDstHeight; nY++, nOutY++ )
{
const long nMapY = pMapY[ nY ];
const long nModY = ( nOutY & 0x0FL ) << 4L;
for( nX = 0, nOutX = nOffX; nX < nDstWidth; nX++, nOutX++ )
{
const long nMapX = pMapX[ nX ];
const ULONG nD = nVCLDitherLut[ nModY | ( nOutX & 0x0FL ) ];
aSrcCol = pP->GetColor( nMapY, nMapX );
aDstCol = pB->GetColor( nY, nX );
nSrcAlpha = 255 - pA->GetPixel( nMapY, nMapX ).GetBlueOrIndex();
nDstAlpha = 0;//255 - pAlphaW->GetPixel( nY, nX ).GetBlueOrIndex();
if( nSrcAlpha + nDstAlpha == 0 )
{
// #i70653# zero alpha -> zero color values
aIndex.SetIndex( (BYTE) ( nVCLRLut[ ( nVCLLut[ 0 ] + nD ) >> 16UL ] +
nVCLGLut[ ( nVCLLut[ 0 ] + nD ) >> 16UL ] +
nVCLBLut[ ( nVCLLut[ 0 ] + nD ) >> 16UL ] ) );
}
else
{
aDstCol.SetRed( (BYTE)(((int)(aSrcCol.GetRed())*nSrcAlpha + (int)(aDstCol.GetRed())*nDstAlpha) /
(nSrcAlpha+nDstAlpha)) );
aDstCol.SetGreen( (BYTE)(((int)(aSrcCol.GetGreen())*nSrcAlpha + (int)(aDstCol.GetGreen())*nDstAlpha) /
(nSrcAlpha+nDstAlpha)) );
aDstCol.SetBlue( (BYTE)(((int)(aSrcCol.GetBlue())*nSrcAlpha + (int)(aDstCol.GetBlue())*nDstAlpha) /
(nSrcAlpha+nDstAlpha)) );
aIndex.SetIndex( (BYTE) ( nVCLRLut[ ( nVCLLut[ aDstCol.GetRed() ] + nD ) >> 16UL ] +
nVCLGLut[ ( nVCLLut[ aDstCol.GetGreen() ] + nD ) >> 16UL ] +
nVCLBLut[ ( nVCLLut[ aDstCol.GetBlue() ] + nD ) >> 16UL ] ) );
}
pW->SetPixel( nY, nX, aIndex );
// Have to perform the compositing 'algebra' in
// the inverse alpha space (with 255 meaning
// opaque), otherwise, transitivity is not
// achieved.
nSrcAlpha = 255-COLOR_CHANNEL_MERGE( 255, (BYTE)nDstAlpha, nSrcAlpha );
aIndex.SetIndex( (BYTE) ( nVCLRLut[ ( nVCLLut[ nSrcAlpha ] + nD ) >> 16UL ] +
nVCLGLut[ ( nVCLLut[ nSrcAlpha ] + nD ) >> 16UL ] +
nVCLBLut[ ( nVCLLut[ nSrcAlpha ] + nD ) >> 16UL ] ) );
#ifdef AVS
pAlphaW->SetPixel( nY, nX, aIndex );
#endif
}
}
}
aBmp.ReleaseAccess( pB );
aDither.ReleaseAccess( pW );
res = aDither;
}
else
{
BitmapWriteAccess* pB = aBmp.AcquireWriteAccess();
if( pP && pA && pB )
{
for( nY = 0; nY < nDstHeight; nY++ )
{
const long nMapY = pMapY[ nY ];
for( nX = 0; nX < nDstWidth; nX++ )
{
const long nMapX = pMapX[ nX ];
aSrcCol = pP->GetColor( nMapY, nMapX );
aDstCol = pB->GetColor( nY, nX );
nSrcAlpha = 255 - pA->GetPixel( nMapY, nMapX ).GetBlueOrIndex();
nDstAlpha = 255;//255 - pAlphaW->GetPixel( nY, nX ).GetBlueOrIndex();
if( nSrcAlpha + nDstAlpha == 0 )
{
// #i70653# zero alpha -> zero color values
aDstCol.SetRed(0);
aDstCol.SetGreen(0);
aDstCol.SetBlue(0);
}
else
{
aDstCol.SetRed( (BYTE)(((int)(aSrcCol.GetRed())*nSrcAlpha + (int)(aDstCol.GetRed())*nDstAlpha) /
(nSrcAlpha+nDstAlpha)) );
aDstCol.SetGreen( (BYTE)(((int)(aSrcCol.GetGreen())*nSrcAlpha + (int)(aDstCol.GetGreen())*nDstAlpha) /
(nSrcAlpha+nDstAlpha)) );
aDstCol.SetBlue( (BYTE)(((int)(aSrcCol.GetBlue())*nSrcAlpha + (int)(aDstCol.GetBlue())*nDstAlpha) /
(nSrcAlpha+nDstAlpha)) );
}
pB->SetPixel( nY, nX, aDstCol );
// Have to perform the compositing 'algebra' in
// the inverse alpha space (with 255 meaning
// opaque), otherwise, transitivity is not
// achieved.
nSrcAlpha = 255-COLOR_CHANNEL_MERGE( 255, (BYTE)nDstAlpha, nSrcAlpha );
#ifdef AVS
pAlphaW->SetPixel( nY, nX, Color(nSrcAlpha, nSrcAlpha, nSrcAlpha) );
#endif
}
}
}
aBmp.ReleaseAccess( pB );
res = aBmp;
}
#ifdef AVS
aAlphaBitmap.ReleaseAccess( pAlphaW );
mpAlphaVDev->DrawBitmap( aDstRect.TopLeft(), aAlphaBitmap );
mpAlphaVDev->EnableMapMode( bOldMapMode );
#endif
return res;
}
// ------------------------------------------------------------------------
Bitmap OutputDevice::ImplBlend( Bitmap aBmp,
BitmapReadAccess* pP,
BitmapReadAccess* pA,
const sal_Int32 nOffY,
const sal_Int32 nDstHeight,
const sal_Int32 nOffX,
const sal_Int32 nDstWidth,
const Rectangle& aBmpRect,
const Size& aOutSz,
const bool bHMirr,
const bool bVMirr,
const long* pMapX,
const long* pMapY )
{
BitmapColor aDstCol;
Bitmap res;
int nX, nOutX, nY, nOutY;
if( GetBitCount() <= 8 )
{
Bitmap aDither( aBmp.GetSizePixel(), 8 );
BitmapColor aIndex( 0 );
BitmapReadAccess* pB = aBmp.AcquireReadAccess();
BitmapWriteAccess* pW = aDither.AcquireWriteAccess();
if( pB && pP && pA && pW )
{
for( nY = 0, nOutY = nOffY; nY < nDstHeight; nY++, nOutY++ )
{
const long nMapY = pMapY[ nY ];
const long nModY = ( nOutY & 0x0FL ) << 4L;
for( nX = 0, nOutX = nOffX; nX < nDstWidth; nX++, nOutX++ )
{
const long nMapX = pMapX[ nX ];
const ULONG nD = nVCLDitherLut[ nModY | ( nOutX & 0x0FL ) ];
aDstCol = pB->GetColor( nY, nX );
aDstCol.Merge( pP->GetColor( nMapY, nMapX ), (BYTE) pA->GetPixel( nMapY, nMapX ) );
aIndex.SetIndex( (BYTE) ( nVCLRLut[ ( nVCLLut[ aDstCol.GetRed() ] + nD ) >> 16UL ] +
nVCLGLut[ ( nVCLLut[ aDstCol.GetGreen() ] + nD ) >> 16UL ] +
nVCLBLut[ ( nVCLLut[ aDstCol.GetBlue() ] + nD ) >> 16UL ] ) );
pW->SetPixel( nY, nX, aIndex );
}
}
}
aBmp.ReleaseAccess( pB );
aDither.ReleaseAccess( pW );
res = aDither;
}
else
{
BitmapWriteAccess* pB = aBmp.AcquireWriteAccess();
bool bFastBlend = false;
#ifdef AVS //fast <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
if( pP && pA && pB )
{
SalTwoRect aTR;
aTR.mnSrcX = aBmpRect.Left();
aTR.mnSrcY = aBmpRect.Top();
aTR.mnSrcWidth = aBmpRect.GetWidth();
aTR.mnSrcHeight = aBmpRect.GetHeight();
aTR.mnDestX = nOffX;
aTR.mnDestY = nOffY;
aTR.mnDestWidth = aOutSz.Width();
aTR.mnDestHeight= aOutSz.Height();
if( !bHMirr || !bVMirr )
bFastBlend = ImplFastBitmapBlending( *pB,*pP,*pA, aTR );
}
#endif
if( pP && pA && pB && !bFastBlend )
{
switch( pP->GetScanlineFormat() )
{
case( BMP_FORMAT_8BIT_PAL ):
{
for( nY = 0; nY < nDstHeight; nY++ )
{
const long nMapY = pMapY[ nY ];
Scanline pPScan = pP->GetScanline( nMapY );
Scanline pAScan = pA->GetScanline( nMapY );
for( nX = 0; nX < nDstWidth; nX++ )
{
const long nMapX = pMapX[ nX ];
aDstCol = pB->GetPixel( nY, nX );
pB->SetPixel( nY, nX, aDstCol.Merge( pP->GetPaletteColor( pPScan[ nMapX ] ),
pAScan[ nMapX ] ) );
}
}
}
break;
case( BMP_FORMAT_24BIT_TC_BGR ):
{
for( nY = 0; nY < nDstHeight; nY++ )
{
const long nMapY = pMapY[ nY ];
Scanline pPScan = pP->GetScanline( nMapY );
Scanline pAScan = pA->GetScanline( nMapY );
for( nX = 0; nX < nDstWidth; nX++ )
{
const long nMapX = pMapX[ nX ];
Scanline pTmp = pPScan + nMapX * 3;
aDstCol = pB->GetPixel( nY, nX );
pB->SetPixel( nY, nX, aDstCol.Merge( pTmp[ 2 ], pTmp[ 1 ], pTmp[ 0 ],
pAScan[ nMapX ] ) );
}
}
}
break;
case( BMP_FORMAT_24BIT_TC_RGB ):
{
for( nY = 0; nY < nDstHeight; nY++ )
{
const long nMapY = pMapY[ nY ];
Scanline pPScan = pP->GetScanline( nMapY );
Scanline pAScan = pA->GetScanline( nMapY );
for( nX = 0; nX < nDstWidth; nX++ )
{
const long nMapX = pMapX[ nX ];
Scanline pTmp = pPScan + nMapX * 3;
aDstCol = pB->GetPixel( nY, nX );
pB->SetPixel( nY, nX, aDstCol.Merge( pTmp[ 0 ], pTmp[ 1 ], pTmp[ 2 ],
pAScan[ nMapX ] ) );
}
}
}
break;
default:
{
for( nY = 0; nY < nDstHeight; nY++ )
{
const long nMapY = pMapY[ nY ];
Scanline pAScan = pA->GetScanline( nMapY );
for( nX = 0; nX < nDstWidth; nX++ )
{
const long nMapX = pMapX[ nX ];
aDstCol = pB->GetPixel( nY, nX );
pB->SetPixel( nY, nX, aDstCol.Merge( pP->GetColor( nMapY, nMapX ),
pAScan[ nMapX ] ) );
}
}
}
break;
}
}
aBmp.ReleaseAccess( pB );
res = aBmp;
}
return res;
}
void OutputDevice::DrawMask( const Point& rDestPt,
const Bitmap& rBitmap, const Color& rMaskColor )
{
const Size aSizePix( rBitmap.GetSizePixel() );
ImplDrawMask( rDestPt, PixelToLogic( aSizePix ), Point(), aSizePix, rBitmap, rMaskColor, META_MASK_ACTION );
}
void OutputDevice::DrawMask( const Point& rDestPt, const Size& rDestSize,
const Bitmap& rBitmap, const Color& rMaskColor )
{
ImplDrawMask( rDestPt, rDestSize, Point(), rBitmap.GetSizePixel(), rBitmap, rMaskColor, META_MASKSCALE_ACTION );
}
void OutputDevice::DrawMask( const Point& rDestPt, const Size& rDestSize,
const Point& rSrcPtPixel, const Size& rSrcSizePixel,
const Bitmap& rBitmap, const Color& rMaskColor )
{
ImplDrawMask( rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, rBitmap, rMaskColor, META_MASKSCALEPART_ACTION );
}
// ------------------------------------------------------------------
void OutputDevice::ImplDrawMask( const Point& rDestPt, const Size& rDestSize,
const Point& rSrcPtPixel, const Size& rSrcSizePixel,
const Bitmap& rBitmap, const Color& rMaskColor,
const ULONG nAction )
{
if( ROP_INVERT == meRasterOp )
{
DrawRect( Rectangle( rDestPt, rDestSize ) );
return;
}
OUTDEV_INIT();
const ImpBitmap* pImpBmp = rBitmap.ImplGetImpBitmap();
if ( pImpBmp )
{
SalTwoRect aPosAry;
aPosAry.mnSrcX = rSrcPtPixel.X();
aPosAry.mnSrcY = rSrcPtPixel.Y();
aPosAry.mnSrcWidth = rSrcSizePixel.Width();
aPosAry.mnSrcHeight = rSrcSizePixel.Height();
aPosAry.mnDestX = ImplLogicXToDevicePixel( rDestPt.X() );
aPosAry.mnDestY = ImplLogicYToDevicePixel( rDestPt.Y() );
aPosAry.mnDestWidth = ImplLogicWidthToDevicePixel( rDestSize.Width() );
aPosAry.mnDestHeight = ImplLogicHeightToDevicePixel( rDestSize.Height() );
// spiegeln via Koordinaten wollen wir nicht
const ULONG nMirrFlags = ImplAdjustTwoRect( aPosAry, pImpBmp->ImplGetSize() );
// check if output is necessary
if( aPosAry.mnSrcWidth && aPosAry.mnSrcHeight && aPosAry.mnDestWidth && aPosAry.mnDestHeight )
{
if( nMirrFlags )
{
Bitmap aTmp( rBitmap );
aTmp.Mirror( nMirrFlags );
mpGraphics->DrawMask( &aPosAry, *aTmp.ImplGetImpBitmap()->ImplGetSalBitmap(),
ImplColorToSal( rMaskColor ) , this);
}
else
mpGraphics->DrawMask( &aPosAry, *pImpBmp->ImplGetSalBitmap(),
ImplColorToSal( rMaskColor ), this );
}
}
}
void OutputDevice::DrawGradient( const Rectangle& rRect, const Gradient& rGradient )
{
Gradient aGradient( rGradient );
// Rechteck in Pixel umrechnen
Rectangle aRect( ImplLogicToDevicePixel( rRect ) );
aRect.Justify();
// Wenn Rechteck leer ist, brauchen wir nichts machen
if ( !aRect.IsEmpty() )
{
// Clip Region sichern
Push( PUSH_CLIPREGION );
IntersectClipRegion( rRect );
// 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()++;
// we need a graphics
if ( !mpGraphics )
return;
if ( mbInitClipRegion )
ImplInitClipRegion();
if ( !mbOutputClipped )
{
// Gradienten werden ohne Umrandung gezeichnet
if ( mbLineColor || mbInitLineColor )
{
mpGraphics->SetLineColor();
mbInitLineColor = TRUE;
}
mbInitFillColor = TRUE;
// calculate step count if neccessary
if ( !aGradient.GetSteps() )
aGradient.SetSteps( GRADIENT_DEFAULT_STEPCOUNT );
if( aGradient.GetStyle() == GRADIENT_LINEAR || aGradient.GetStyle() == GRADIENT_AXIAL )
ImplDrawLinearGradient( aRect, aGradient, FALSE, NULL );
else
ImplDrawComplexGradient( aRect, aGradient, FALSE, NULL );
}
Pop();
}
}
void OutputDevice::DrawGradient( const PolyPolygon& rPolyPoly, const Gradient& rGradient )
{
#ifdef AVS
if( mbInitClipRegion )
ImplInitClipRegion();
if( mbOutputClipped )
return;
if( !mpGraphics )
return;
if( rPolyPoly.Count() && rPolyPoly[ 0 ].GetSize() )
{
const Rectangle aRect( rPolyPoly.GetBoundRect() );
Gradient aGradient( rGradient );
{
const PolyPolygon aPolyPoly( LogicToPixel( rPolyPoly ) );
const Rectangle aBoundRect( aPolyPoly.GetBoundRect() );
Point aPoint;
Rectangle aDstRect( aPoint, GetOutputSizePixel() );
aDstRect.Intersection( aBoundRect );
if( !aDstRect.IsEmpty() )
{
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 = new VirtualDevice( *this, 0, GetAlphaBitCount() > 1 ? 0 : 1 );
}
else
{
// nothing special here. Plain VDev
pVDev = 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 );
}
delete pVDev;
}
}
}
#endif
}
void OutputDevice::ImplDrawLinearGradient( const Rectangle& rRect,
const Gradient& rGradient,
BOOL bMtf, const PolyPolygon* pClipPolyPoly )
{
// rotiertes BoundRect ausrechnen
Rectangle aRect = rRect;
aRect.Left()--;
aRect.Top()--;
aRect.Right()++;
aRect.Bottom()++;
USHORT nAngle = rGradient.GetAngle() % 3600;
double fAngle = nAngle * F_PI1800;
double fWidth = aRect.GetWidth();
double fHeight = aRect.GetHeight();
double fDX = fWidth * fabs( cos( fAngle ) ) +
fHeight * fabs( sin( fAngle ) );
double fDY = fHeight * fabs( cos( fAngle ) ) +
fWidth * fabs( sin( fAngle ) );
fDX = (fDX - fWidth) * 0.5 + 0.5;
fDY = (fDY - fHeight) * 0.5 + 0.5;
aRect.Left() -= (long)fDX;
aRect.Right() += (long)fDX;
aRect.Top() -= (long)fDY;
aRect.Bottom() += (long)fDY;
// Rand berechnen und Rechteck neu setzen
Point aCenter = rRect.Center();
Rectangle aFullRect = aRect;
long nBorder = (long)rGradient.GetBorder() * aRect.GetHeight() / 100;
BOOL bLinear;
// Rand berechnen und Rechteck neu setzen fuer linearen Farbverlauf
if ( rGradient.GetStyle() == GRADIENT_LINEAR )
{
bLinear = TRUE;
aRect.Top() += nBorder;
}
// Rand berechnen und Rechteck neu setzen fuer axiale Farbverlauf
else
{
bLinear = FALSE;
nBorder >>= 1;
aRect.Top() += nBorder;
aRect.Bottom() -= nBorder;
}
// Top darf nicht groesser als Bottom sein
aRect.Top() = Min( aRect.Top(), (long)(aRect.Bottom() - 1) );
long nMinRect = aRect.GetHeight();
// Intensitaeten von Start- und Endfarbe ggf. aendern und
// Farbschrittweiten berechnen
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;
long nRedSteps = nEndRed - nStartRed;
long nGreenSteps = nEndGreen - nStartGreen;
long nBlueSteps = nEndBlue - nStartBlue;
long nStepCount = rGradient.GetSteps();
// Bei nicht linearen Farbverlaeufen haben wir nur die halben Steps
// pro Farbe
if ( !bLinear )
{
nRedSteps <<= 1;
nGreenSteps <<= 1;
nBlueSteps <<= 1;
}
// Anzahl der Schritte berechnen, falls nichts uebergeben wurde
if ( !nStepCount )
{
long nInc;
if ( !bMtf )
{
nInc = (nMinRect < 50) ? 2 : 4;
}
else
{
// #105998# Use display-equivalent step size calculation
nInc = (nMinRect < 800) ? 10 : 20;
}
if ( !nInc )
nInc = 1;
nStepCount = nMinRect / nInc;
}
// minimal drei Schritte und maximal die Anzahl der Farbunterschiede
long nSteps = Max( nStepCount, 2L );
long nCalcSteps = Abs( nRedSteps );
long nTempSteps = Abs( nGreenSteps );
if ( nTempSteps > nCalcSteps )
nCalcSteps = nTempSteps;
nTempSteps = Abs( nBlueSteps );
if ( nTempSteps > nCalcSteps )
nCalcSteps = nTempSteps;
if ( nCalcSteps < nSteps )
nSteps = nCalcSteps;
if ( !nSteps )
nSteps = 1;
// Falls axialer Farbverlauf, muss die Schrittanzahl ungerade sein
if ( !bLinear && !(nSteps & 1) )
nSteps++;
// Berechnung ueber Double-Addition wegen Genauigkeit
double fScanLine = aRect.Top();
double fScanInc = (double)aRect.GetHeight() / (double)nSteps;
// Startfarbe berechnen und setzen
UINT8 nRed;
UINT8 nGreen;
UINT8 nBlue;
long nSteps2;
long nStepsHalf = 0;
if ( bLinear )
{
// Um 1 erhoeht, um die Border innerhalb der Schleife
// zeichnen zu koennen
nSteps2 = nSteps + 1;
nRed = (UINT8)nStartRed;
nGreen = (UINT8)nStartGreen;
nBlue = (UINT8)nStartBlue;
}
else
{
// Um 2 erhoeht, um die Border innerhalb der Schleife
// zeichnen zu koennen
nSteps2 = nSteps + 2;
nRed = (UINT8)nEndRed;
nGreen = (UINT8)nEndGreen;
nBlue = (UINT8)nEndBlue;
nStepsHalf = nSteps >> 1;
}
if ( bMtf )
;//mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), TRUE ) );
else
mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
// Startpolygon erzeugen (== Borderpolygon)
Polygon aPoly( 4 );
Polygon aTempPoly( 2 );
aPoly[0] = aFullRect.TopLeft();
aPoly[1] = aFullRect.TopRight();
aPoly[2] = aRect.TopRight();
aPoly[3] = aRect.TopLeft();
aPoly.Rotate( aCenter, nAngle );
// Schleife, um rotierten Verlauf zu fuellen
for ( long i = 0; i < nSteps2; i++ )
{
// berechnetesPolygon ausgeben
if ( bMtf )
;//mpMetaFile->AddAction( new MetaPolygonAction( aPoly ) );
else
ImplDrawPolygon( aPoly, pClipPolyPoly );
// neues Polygon berechnen
aRect.Top() = (long)(fScanLine += fScanInc);
// unteren Rand komplett fuellen
if ( i == nSteps )
{
aTempPoly[0] = aFullRect.BottomLeft();
aTempPoly[1] = aFullRect.BottomRight();
}
else
{
aTempPoly[0] = aRect.TopLeft();
aTempPoly[1] = aRect.TopRight();
}
aTempPoly.Rotate( aCenter, nAngle );
aPoly[0] = aPoly[3];
aPoly[1] = aPoly[2];
aPoly[2] = aTempPoly[1];
aPoly[3] = aTempPoly[0];
// Farbintensitaeten aendern...
// fuer lineare FV
if ( bLinear )
{
nRed = ImplGetGradientColorValue( nStartRed+((nRedSteps*i)/nSteps2) );
nGreen = ImplGetGradientColorValue( nStartGreen+((nGreenSteps*i)/nSteps2) );
nBlue = ImplGetGradientColorValue( nStartBlue+((nBlueSteps*i)/nSteps2) );
}
// fuer radiale FV
else
{
// fuer axiale FV muss die letzte Farbe der ersten
// Farbe entsprechen
// #107350# Setting end color one step earlier, as the
// last time we get here, we drop out of the loop later
// on.
if ( i >= nSteps )
{
nRed = (UINT8)nEndRed;
nGreen = (UINT8)nEndGreen;
nBlue = (UINT8)nEndBlue;
}
else
{
if ( i <= nStepsHalf )
{
nRed = ImplGetGradientColorValue( nEndRed-((nRedSteps*i)/nSteps2) );
nGreen = ImplGetGradientColorValue( nEndGreen-((nGreenSteps*i)/nSteps2) );
nBlue = ImplGetGradientColorValue( nEndBlue-((nBlueSteps*i)/nSteps2) );
}
// genau die Mitte und hoeher
else
{
long i2 = i - nStepsHalf;
nRed = ImplGetGradientColorValue( nStartRed+((nRedSteps*i2)/nSteps2) );
nGreen = ImplGetGradientColorValue( nStartGreen+((nGreenSteps*i2)/nSteps2) );
nBlue = ImplGetGradientColorValue( nStartBlue+((nBlueSteps*i2)/nSteps2) );
}
}
}
if ( bMtf )
;//mpMetaFile->AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), TRUE ) );
else
mpGraphics->SetFillColor( MAKE_SALCOLOR( nRed, nGreen, nBlue ) );
}
}
// -----------------------------------------------------------------------
void OutputDevice::ImplDrawComplexGradient( const Rectangle& rRect,
const Gradient& rGradient,
BOOL bMtf, const PolyPolygon* pClipPolyPoly )
{
// Feststellen ob Ausgabe ueber Polygon oder PolyPolygon
// Bei Rasteroperationen ungleich Overpaint immer PolyPolygone,
// da es zu falschen Ergebnissen kommt, wenn man mehrfach uebereinander
// ausgibt
// Bei Druckern auch immer PolyPolygone, da nicht alle Drucker
// das Uebereinanderdrucken von Polygonen koennen
// Virtuelle Device werden auch ausgeklammert, da einige Treiber
// ansonsten zu langsam sind
PolyPolygon* pPolyPoly;
Rectangle aRect( rRect );
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;
long nStepCount = rGradient.GetSteps();
USHORT nAngle = rGradient.GetAngle() % 3600;
if( (meRasterOp != ROP_OVERPAINT) || bMtf )
pPolyPoly = new PolyPolygon( 2 );
else
pPolyPoly = NULL;
if( rGradient.GetStyle() == GRADIENT_SQUARE || rGradient.GetStyle() == GRADIENT_RECT )
{
const double fAngle = nAngle * F_PI1800;
const double fWidth = aRect.GetWidth();
const double fHeight = aRect.GetHeight();
double fDX = fWidth * fabs( cos( fAngle ) ) + fHeight * fabs( sin( fAngle ) );
double fDY = fHeight * fabs( cos( fAngle ) ) + fWidth * fabs( sin( fAngle ) );
fDX = ( fDX - fWidth ) * 0.5 + 0.5;
fDY = ( fDY - fHeight ) * 0.5 + 0.5;
aRect.Left() -= (long) fDX;
aRect.Right() += (long) fDX;
aRect.Top() -= (long) fDY;
aRect.Bottom() += (long) fDY;
}
Size aSize( aRect.GetSize() );
if( rGradient.GetStyle() == GRADIENT_RADIAL )
{
// Radien-Berechnung fuer Kreis
aSize.Width() = (long)(0.5 + sqrt((double)aSize.Width()*(double)aSize.Width() + (double)aSize.Height()*(double)aSize.Height()));
aSize.Height() = aSize.Width();
}
else if( rGradient.GetStyle() == GRADIENT_ELLIPTICAL )
{
// Radien-Berechnung fuer Ellipse
aSize.Width() = (long)( 0.5 + (double) aSize.Width() * 1.4142 );
aSize.Height() = (long)( 0.5 + (double) aSize.Height() * 1.4142 );
}
else if( rGradient.GetStyle() == GRADIENT_SQUARE )
{
if ( aSize.Width() > aSize.Height() )
aSize.Height() = aSize.Width();
else
aSize.Width() = aSize.Height();
}
// neue Mittelpunkte berechnen
long nZWidth = aRect.GetWidth() * (long) rGradient.GetOfsX() / 100;
long nZHeight = aRect.GetHeight() * (long) rGradient.GetOfsY() / 100;
long nBorderX = (long) rGradient.GetBorder() * aSize.Width() / 100;
long nBorderY = (long) rGradient.GetBorder() * aSize.Height() / 100;
Point aCenter( aRect.Left() + nZWidth, aRect.Top() + nZHeight );
// Rand beruecksichtigen
aSize.Width() -= nBorderX;
aSize.Height() -= nBorderY;
// Ausgaberechteck neu setzen
aRect.Left() = aCenter.X() - ( aSize.Width() >> 1 );
aRect.Top() = aCenter.Y() - ( aSize.Height() >> 1 );
aRect.SetSize( aSize );
long nMinRect = Min( aRect.GetWidth(), aRect.GetHeight() );
// Anzahl der Schritte berechnen, falls nichts uebergeben wurde
if( !nStepCount )
{
long nInc;
if ( !bMtf )
{
nInc = ( nMinRect < 50 ) ? 2 : 4;
}
else
{
// #105998# Use display-equivalent step size calculation
nInc = (nMinRect < 800) ? 10 : 20;
}
if( !nInc )
nInc = 1;
nStepCount = nMinRect / nInc;
}
// minimal drei Schritte und maximal die Anzahl der Farbunterschiede
long nSteps = Max( nStepCount, 2L );
long nCalcSteps = Abs( nRedSteps );
long nTempSteps = Abs( nGreenSteps );
if ( nTempSteps > nCalcSteps )
nCalcSteps = nTempSteps;
nTempSteps = Abs( nBlueSteps );
if ( nTempSteps > nCalcSteps )
nCalcSteps = nTempSteps;
if ( nCalcSteps < nSteps )
nSteps = nCalcSteps;
if ( !nSteps )
nSteps = 1;
// Ausgabebegrenzungen und Schrittweite fuer jede Richtung festlegen
Polygon aPoly;
double fScanLeft = aRect.Left();
double fScanTop = aRect.Top();
double fScanRight = aRect.Right();
double fScanBottom = aRect.Bottom();
double fScanInc = (double) nMinRect / (double) nSteps * 0.5;
UINT8 nRed = (UINT8) nStartRed, nGreen = (UINT8) nStartGreen, nBlue = (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 );
}
// Schleife, um nacheinander die Polygone/PolyPolygone auszugeben
for( long i = 1; i < nSteps; i++ )
{
// neues Polygon berechnen
aRect.Left() = (long)( fScanLeft += fScanInc );
aRect.Top() = (long)( fScanTop += fScanInc );
aRect.Right() = (long)( fScanRight -= fScanInc );
aRect.Bottom() = (long)( fScanBottom -= fScanInc );
if( ( aRect.GetWidth() < 2 ) || ( aRect.GetHeight() < 2 ) )
break;
if( rGradient.GetStyle() == GRADIENT_RADIAL || rGradient.GetStyle() == GRADIENT_ELLIPTICAL )
aPoly = Polygon( aRect.Center(), aRect.GetWidth() >> 1, aRect.GetHeight() >> 1 );
else
aPoly = Polygon( aRect );
aPoly.Rotate( aCenter, nAngle );
// Farbe entsprechend anpassen
const long nStepIndex = ( ( pPolyPoly != NULL ) ? i : ( i + 1 ) );
nRed = ImplGetGradientColorValue( nStartRed + ( ( nRedSteps * nStepIndex ) / nSteps ) );
nGreen = ImplGetGradientColorValue( nStartGreen + ( ( nGreenSteps * nStepIndex ) / nSteps ) );
nBlue = ImplGetGradientColorValue( nStartBlue + ( ( nBlueSteps * nStepIndex ) / nSteps ) );
// entweder langsame PolyPolygon-Ausgaben oder schnelles Polygon-Painting
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 );
}
}
// Falls PolyPolygon-Ausgabe, muessen wir noch ein letztes inneres Polygon zeichnen
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 );
}
}
delete pPolyPoly;
}
}
void OutputDevice::DrawHatch( const PolyPolygon& rPolyPoly, const ::Hatch& rHatch )
{
Hatch aHatch( rHatch );
if( !mpGraphics )
return;
if( mbInitClipRegion )
ImplInitClipRegion();
if( mbOutputClipped )
return;
if( rPolyPoly.Count() )
{
PolyPolygon aPolyPoly( LogicToPixel( rPolyPoly ) );
BOOL bOldMap = mbMap;
aPolyPoly.Optimize( POLY_OPTIMIZE_NO_SAME );
aHatch.SetDistance( ImplLogicWidthToDevicePixel( aHatch.GetDistance() ) );
EnableMapMode( FALSE );
Push( PUSH_LINECOLOR );
SetLineColor( aHatch.GetColor() );
ImplInitLineColor();
ImplDrawHatch( aPolyPoly, aHatch, FALSE );
Pop();
EnableMapMode( bOldMap );
}
}
void OutputDevice::ImplDrawHatch( const PolyPolygon& rPolyPoly, const Hatch& rHatch, BOOL bMtf )
{
Rectangle aRect( rPolyPoly.GetBoundRect() );
const long nLogPixelWidth = ImplDevicePixelToLogicWidth( 1 );
const long nWidth = ImplDevicePixelToLogicWidth( Max( ImplLogicWidthToDevicePixel( rHatch.GetDistance() ), 3L ) );
Point* pPtBuffer = new Point[ HATCH_MAXPOINTS ];
Point aPt1, aPt2, aEndPt1;
Size aInc;
// Single hatch
aRect.Left() -= nLogPixelWidth; aRect.Top() -= nLogPixelWidth; aRect.Right() += nLogPixelWidth; aRect.Bottom() += nLogPixelWidth;
ImplCalcHatchValues( aRect, nWidth, rHatch.GetAngle(), aPt1, aPt2, aInc, aEndPt1 );
do
{
ImplDrawHatchLine( Line( aPt1, aPt2 ), rPolyPoly, pPtBuffer, bMtf );
aPt1.X() += aInc.Width(); aPt1.Y() += aInc.Height();
aPt2.X() += aInc.Width(); aPt2.Y() += aInc.Height();
}
while( ( aPt1.X() <= aEndPt1.X() ) && ( aPt1.Y() <= aEndPt1.Y() ) );
if( ( rHatch.GetStyle() == HATCH_DOUBLE ) || ( rHatch.GetStyle() == HATCH_TRIPLE ) )
{
// Double hatch
ImplCalcHatchValues( aRect, nWidth, rHatch.GetAngle() + 900, aPt1, aPt2, aInc, aEndPt1 );
do
{
ImplDrawHatchLine( Line( aPt1, aPt2 ), rPolyPoly, pPtBuffer, bMtf );
aPt1.X() += aInc.Width(); aPt1.Y() += aInc.Height();
aPt2.X() += aInc.Width(); aPt2.Y() += aInc.Height();
}
while( ( aPt1.X() <= aEndPt1.X() ) && ( aPt1.Y() <= aEndPt1.Y() ) );
if( rHatch.GetStyle() == HATCH_TRIPLE )
{
// Triple hatch
ImplCalcHatchValues( aRect, nWidth, rHatch.GetAngle() + 450, aPt1, aPt2, aInc, aEndPt1 );
do
{
ImplDrawHatchLine( Line( aPt1, aPt2 ), rPolyPoly, pPtBuffer, bMtf );
aPt1.X() += aInc.Width(); aPt1.Y() += aInc.Height();
aPt2.X() += aInc.Width(); aPt2.Y() += aInc.Height();
}
while( ( aPt1.X() <= aEndPt1.X() ) && ( aPt1.Y() <= aEndPt1.Y() ) );
}
}
delete[] pPtBuffer;
}
// -----------------------------------------------------------------------
void OutputDevice::ImplCalcHatchValues( const Rectangle& rRect, long nDist, USHORT nAngle10,
Point& rPt1, Point& rPt2, Size& rInc, Point& rEndPt1 )
{
Point aRef;
long nAngle = nAngle10 % 1800;
long nOffset = 0;
if( nAngle > 900 )
nAngle -= 1800;
aRef = ( !IsRefPoint() ? rRect.TopLeft() : GetRefPoint() );
if( 0 == nAngle )
{
rInc = Size( 0, nDist );
rPt1 = rRect.TopLeft();
rPt2 = rRect.TopRight();
rEndPt1 = rRect.BottomLeft();
if( aRef.Y() <= rRect.Top() )
nOffset = ( ( rRect.Top() - aRef.Y() ) % nDist );
else
nOffset = ( nDist - ( ( aRef.Y() - rRect.Top() ) % nDist ) );
rPt1.Y() -= nOffset;
rPt2.Y() -= nOffset;
}
else if( 900 == nAngle )
{
rInc = Size( nDist, 0 );
rPt1 = rRect.TopLeft();
rPt2 = rRect.BottomLeft();
rEndPt1 = rRect.TopRight();
if( aRef.X() <= rRect.Left() )
nOffset = ( rRect.Left() - aRef.X() ) % nDist;
else
nOffset = nDist - ( ( aRef.X() - rRect.Left() ) % nDist );
rPt1.X() -= nOffset;
rPt2.X() -= nOffset;
}
else if( nAngle >= -450 && nAngle <= 450 )
{
const double fAngle = F_PI1800 * labs( nAngle );
const double fTan = tan( fAngle );
const long nYOff = FRound( ( rRect.Right() - rRect.Left() ) * fTan );
long nPY;
rInc = Size( 0, nDist = FRound( nDist / cos( fAngle ) ) );
if( nAngle > 0 )
{
rPt1 = rRect.TopLeft();
rPt2 = Point( rRect.Right(), rRect.Top() - nYOff );
rEndPt1 = Point( rRect.Left(), rRect.Bottom() + nYOff );
nPY = FRound( aRef.Y() - ( ( rPt1.X() - aRef.X() ) * fTan ) );
}
else
{
rPt1 = rRect.TopRight();
rPt2 = Point( rRect.Left(), rRect.Top() - nYOff );
rEndPt1 = Point( rRect.Right(), rRect.Bottom() + nYOff );
nPY = FRound( aRef.Y() + ( ( rPt1.X() - aRef.X() ) * fTan ) );
}
if( nPY <= rPt1.Y() )
nOffset = ( rPt1.Y() - nPY ) % nDist;
else
nOffset = nDist - ( ( nPY - rPt1.Y() ) % nDist );
rPt1.Y() -= nOffset;
rPt2.Y() -= nOffset;
}
else
{
const double fAngle = F_PI1800 * labs( nAngle );
const double fTan = tan( fAngle );
const long nXOff = FRound( ( rRect.Bottom() - rRect.Top() ) / fTan );
long nPX;
rInc = Size( nDist = FRound( nDist / sin( fAngle ) ), 0 );
if( nAngle > 0 )
{
rPt1 = rRect.TopLeft();
rPt2 = Point( rRect.Left() - nXOff, rRect.Bottom() );
rEndPt1 = Point( rRect.Right() + nXOff, rRect.Top() );
nPX = FRound( aRef.X() - ( ( rPt1.Y() - aRef.Y() ) / fTan ) );
}
else
{
rPt1 = rRect.BottomLeft();
rPt2 = Point( rRect.Left() - nXOff, rRect.Top() );
rEndPt1 = Point( rRect.Right() + nXOff, rRect.Bottom() );
nPX = FRound( aRef.X() + ( ( rPt1.Y() - aRef.Y() ) / fTan ) );
}
if( nPX <= rPt1.X() )
nOffset = ( rPt1.X() - nPX ) % nDist;
else
nOffset = nDist - ( ( nPX - rPt1.X() ) % nDist );
rPt1.X() -= nOffset;
rPt2.X() -= nOffset;
}
}
// ------------------------------------------------------------------------
void OutputDevice::ImplDrawHatchLine( const Line& rLine, const PolyPolygon& rPolyPoly,
Point* pPtBuffer, BOOL bMtf )
{
double fX, fY;
long nAdd, nPCounter = 0;
for( long nPoly = 0, nPolyCount = rPolyPoly.Count(); nPoly < nPolyCount; nPoly++ )
{
const Polygon& rPoly = rPolyPoly[ (USHORT) nPoly ];
if( rPoly.GetSize() > 1 )
{
Line aCurSegment( rPoly[ 0 ], Point() );
for( long i = 1, nCount = rPoly.GetSize(); i <= nCount; i++ )
{
aCurSegment.SetEnd( rPoly[ (USHORT)( i % nCount ) ] );
nAdd = 0;
if( rLine.Intersection( aCurSegment, fX, fY ) )
{
if( ( fabs( fX - aCurSegment.GetStart().X() ) <= 0.0000001 ) &&
( fabs( fY - aCurSegment.GetStart().Y() ) <= 0.0000001 ) )
{
const Line aPrevSegment( rPoly[ (USHORT)( ( i > 1 ) ? ( i - 2 ) : ( nCount - 1 ) ) ], aCurSegment.GetStart() );
const double fPrevDistance = rLine.GetDistance( aPrevSegment.GetStart() );
const double fCurDistance = rLine.GetDistance( aCurSegment.GetEnd() );
if( ( fPrevDistance <= 0.0 && fCurDistance > 0.0 ) ||
( fPrevDistance > 0.0 && fCurDistance < 0.0 ) )
{
nAdd = 1;
}
}
else if( ( fabs( fX - aCurSegment.GetEnd().X() ) <= 0.0000001 ) &&
( fabs( fY - aCurSegment.GetEnd().Y() ) <= 0.0000001 ) )
{
const Line aNextSegment( aCurSegment.GetEnd(), rPoly[ (USHORT)( ( i + 1 ) % nCount ) ] );
if( ( fabs( rLine.GetDistance( aNextSegment.GetEnd() ) ) <= 0.0000001 ) &&
( rLine.GetDistance( aCurSegment.GetStart() ) > 0.0 ) )
{
nAdd = 1;
}
}
else
nAdd = 1;
if( nAdd )
pPtBuffer[ nPCounter++ ] = Point( FRound( fX ), FRound( fY ) );
}
aCurSegment.SetStart( aCurSegment.GetEnd() );
}
}
}
if( nPCounter > 1 )
{
qsort( pPtBuffer, nPCounter, sizeof( Point ), ImplHatchCmpFnc );
if( nPCounter & 1 )
nPCounter--;
if( bMtf )
{
#ifdef AVS
for( long i = 0; i < nPCounter; i += 2 )
mpMetaFile->AddAction( new MetaLineAction( pPtBuffer[ i ], pPtBuffer[ i + 1 ] ) );
#endif
}
else
{
for( long i = 0; i < nPCounter; i += 2 )
{
#ifdef AVS
if( mpPDFWriter )
{
mpPDFWriter->drawLine( pPtBuffer[ i ], pPtBuffer[ i+1 ] );
}
else
{
#endif
const Point aPt1( ImplLogicToDevicePixel( pPtBuffer[ i ] ) );
const Point aPt2( ImplLogicToDevicePixel( pPtBuffer[ i + 1 ] ) );
mpGraphics->DrawLine( aPt1.X(), aPt1.Y(), aPt2.X(), aPt2.Y(), this );
#ifdef AVS
}
#endif
}
}
}
}
void OutputDevice::DrawWallpaper( const Rectangle& rRect, const Wallpaper& rWallpaper )
{
if ( rWallpaper.GetStyle() != WALLPAPER_NULL )
{
Rectangle aRect = LogicToPixel( rRect );
aRect.Justify();
if ( !aRect.IsEmpty() )
{
ImplDrawWallpaper( aRect.Left(), aRect.Top(), aRect.GetWidth(), aRect.GetHeight(),
rWallpaper );
}
}
}
void OutputDevice::ImplDrawWallpaper( long nX, long nY,
long nWidth, long nHeight,
const Wallpaper& rWallpaper )
{
if( rWallpaper.IsBitmap() )
ImplDrawBitmapWallpaper( nX, nY, nWidth, nHeight, rWallpaper );
else if( rWallpaper.IsGradient() )
ImplDrawGradientWallpaper( nX, nY, nWidth, nHeight, rWallpaper );
else
ImplDrawColorWallpaper( nX, nY, nWidth, nHeight, rWallpaper );
}
void OutputDevice::ImplDrawBitmapWallpaper( long nX, long nY,
long nWidth, long nHeight,
const Wallpaper& rWallpaper )
{
BitmapEx aBmpEx;
const BitmapEx* pCached = rWallpaper.ImplGetImpWallpaper()->ImplGetCachedBitmap();
Point aPos;
Size aSize;
const WallpaperStyle eStyle = rWallpaper.GetStyle();
const BOOL bOldMap = mbMap;
BOOL bDrawn = FALSE;
BOOL bDrawGradientBackground = FALSE;
BOOL bDrawColorBackground = FALSE;
if( pCached )
aBmpEx = *pCached;
else
aBmpEx = rWallpaper.GetBitmap();
const long nBmpWidth = aBmpEx.GetSizePixel().Width();
const long nBmpHeight = aBmpEx.GetSizePixel().Height();
const BOOL bTransparent = aBmpEx.IsTransparent();
// draw background
if( bTransparent )
{
if( rWallpaper.IsGradient() )
bDrawGradientBackground = TRUE;
else
{
if( !pCached && !rWallpaper.GetColor().GetTransparency() )
{
VirtualDevice aVDev( *this );
aVDev.SetBackground( rWallpaper.GetColor() );
aVDev.SetOutputSizePixel( Size( nBmpWidth, nBmpHeight ) );
aVDev.DrawBitmapEx( Point(), aBmpEx );
aBmpEx = aVDev.GetBitmap( Point(), aVDev.GetOutputSizePixel() );
}
bDrawColorBackground = TRUE;
}
}
else if( eStyle != WALLPAPER_TILE && eStyle != WALLPAPER_SCALE )
{
if( rWallpaper.IsGradient() )
bDrawGradientBackground = TRUE;
else
bDrawColorBackground = TRUE;
}
// background of bitmap?
if( bDrawGradientBackground )
ImplDrawGradientWallpaper( nX, nY, nWidth, nHeight, rWallpaper );
else if( bDrawColorBackground && bTransparent )
{
ImplDrawColorWallpaper( nX, nY, nWidth, nHeight, rWallpaper );
bDrawColorBackground = FALSE;
}
// calc pos and size
if( rWallpaper.IsRect() )
{
const Rectangle aBound( LogicToPixel( rWallpaper.GetRect() ) );
aPos = aBound.TopLeft();
aSize = aBound.GetSize();
}
else
{
aPos = Point( nX, nY );
aSize = Size( nWidth, nHeight );
}
EnableMapMode( FALSE );
Push( PUSH_CLIPREGION );
IntersectClipRegion( Rectangle( Point( nX, nY ), Size( nWidth, nHeight ) ) );
switch( eStyle )
{
case( WALLPAPER_SCALE ):
{
if( !pCached || ( pCached->GetSizePixel() != aSize ) )
{
if( pCached )
rWallpaper.ImplGetImpWallpaper()->ImplReleaseCachedBitmap();
aBmpEx = rWallpaper.GetBitmap();
aBmpEx.Scale( aSize );
aBmpEx = BitmapEx( aBmpEx.GetBitmap().CreateDisplayBitmap( this ), aBmpEx.GetMask() );
}
}
break;
case( WALLPAPER_TOPLEFT ):
break;
case( WALLPAPER_TOP ):
aPos.X() += ( aSize.Width() - nBmpWidth ) >> 1;
break;
case( WALLPAPER_TOPRIGHT ):
aPos.X() += ( aSize.Width() - nBmpWidth );
break;
case( WALLPAPER_LEFT ):
aPos.Y() += ( aSize.Height() - nBmpHeight ) >> 1;
break;
case( WALLPAPER_CENTER ):
{
aPos.X() += ( aSize.Width() - nBmpWidth ) >> 1;
aPos.Y() += ( aSize.Height() - nBmpHeight ) >> 1;
}
break;
case( WALLPAPER_RIGHT ):
{
aPos.X() += ( aSize.Width() - nBmpWidth );
aPos.Y() += ( aSize.Height() - nBmpHeight ) >> 1;
}
break;
case( WALLPAPER_BOTTOMLEFT ):
aPos.Y() += ( aSize.Height() - nBmpHeight );
break;
case( WALLPAPER_BOTTOM ):
{
aPos.X() += ( aSize.Width() - nBmpWidth ) >> 1;
aPos.Y() += ( aSize.Height() - nBmpHeight );
}
break;
case( WALLPAPER_BOTTOMRIGHT ):
{
aPos.X() += ( aSize.Width() - nBmpWidth );
aPos.Y() += ( aSize.Height() - nBmpHeight );
}
break;
default:
{
const long nRight = nX + nWidth - 1L;
const long nBottom = nY + nHeight - 1L;
long nFirstX;
long nFirstY;
if( eStyle == WALLPAPER_TILE )
{
nFirstX = aPos.X();
nFirstY = aPos.Y();
}
else
{
nFirstX = aPos.X() + ( ( aSize.Width() - nBmpWidth ) >> 1 );
nFirstY = aPos.Y() + ( ( aSize.Height() - nBmpHeight ) >> 1 );
}
const long nOffX = ( nFirstX - nX ) % nBmpWidth;
const long nOffY = ( nFirstY - nY ) % nBmpHeight;
long nStartX = nX + nOffX;
long nStartY = nY + nOffY;
if( nOffX > 0L )
nStartX -= nBmpWidth;
if( nOffY > 0L )
nStartY -= nBmpHeight;
for( long nBmpY = nStartY; nBmpY <= nBottom; nBmpY += nBmpHeight )
for( long nBmpX = nStartX; nBmpX <= nRight; nBmpX += nBmpWidth )
DrawBitmapEx( Point( nBmpX, nBmpY ), aBmpEx );
bDrawn = TRUE;
}
break;
}
if( !bDrawn )
{
// optimized for non-transparent bitmaps
if( bDrawColorBackground )
{
const Size aBmpSize( aBmpEx.GetSizePixel() );
const Point aTmpPoint;
const Rectangle aOutRect( aTmpPoint, GetOutputSizePixel() );
const Rectangle aColRect( Point( nX, nY ), Size( nWidth, nHeight ) );
Rectangle aWorkRect;
aWorkRect = Rectangle( 0, 0, aOutRect.Right(), aPos.Y() - 1L );
aWorkRect.Justify();
aWorkRect.Intersection( aColRect );
if( !aWorkRect.IsEmpty() )
{
ImplDrawColorWallpaper( aWorkRect.Left(), aWorkRect.Top(),
aWorkRect.GetWidth(), aWorkRect.GetHeight(),
rWallpaper );
}
aWorkRect = Rectangle( 0, aPos.Y(), aPos.X() - 1L, aPos.Y() + aBmpSize.Height() - 1L );
aWorkRect.Justify();
aWorkRect.Intersection( aColRect );
if( !aWorkRect.IsEmpty() )
{
ImplDrawColorWallpaper( aWorkRect.Left(), aWorkRect.Top(),
aWorkRect.GetWidth(), aWorkRect.GetHeight(),
rWallpaper );
}
aWorkRect = Rectangle( aPos.X() + aBmpSize.Width(), aPos.Y(), aOutRect.Right(), aPos.Y() + aBmpSize.Height() - 1L );
aWorkRect.Justify();
aWorkRect.Intersection( aColRect );
if( !aWorkRect.IsEmpty() )
{
ImplDrawColorWallpaper( aWorkRect.Left(), aWorkRect.Top(),
aWorkRect.GetWidth(), aWorkRect.GetHeight(),
rWallpaper );
}
aWorkRect = Rectangle( 0, aPos.Y() + aBmpSize.Height(), aOutRect.Right(), aOutRect.Bottom() );
aWorkRect.Justify();
aWorkRect.Intersection( aColRect );
if( !aWorkRect.IsEmpty() )
{
ImplDrawColorWallpaper( aWorkRect.Left(), aWorkRect.Top(),
aWorkRect.GetWidth(), aWorkRect.GetHeight(),
rWallpaper );
}
}
DrawBitmapEx( aPos, aBmpEx );
}
rWallpaper.ImplGetImpWallpaper()->ImplSetCachedBitmap( aBmpEx );
Pop();
EnableMapMode( bOldMap );
}
void OutputDevice::ImplDrawGradientWallpaper( long nX, long nY,
long nWidth, long nHeight,
const Wallpaper& rWallpaper )
{
Rectangle aBound;
const BOOL bOldMap = mbMap;
BOOL bNeedGradient = TRUE;
if ( rWallpaper.IsRect() )
aBound = LogicToPixel( rWallpaper.GetRect() );
else
aBound = Rectangle( Point( nX, nY ), Size( nWidth, nHeight ) );
EnableMapMode( FALSE );
Push( PUSH_CLIPREGION );
IntersectClipRegion( Rectangle( Point( nX, nY ), Size( nWidth, nHeight ) ) );
if( bNeedGradient )
DrawGradient( aBound, rWallpaper.GetGradient() );
Pop();
EnableMapMode( bOldMap );
}
void OutputDevice::ImplDrawColorWallpaper( long nX, long nY,
long nWidth, long nHeight,
const Wallpaper& rWallpaper )
{
// Wallpaper ohne Umrandung zeichnen
Color aOldLineColor = GetLineColor();
Color aOldFillColor = GetFillColor();
SetLineColor();
SetFillColor( rWallpaper.GetColor() );
BOOL bMap = mbMap;
EnableMapMode( FALSE );
DrawRect( Rectangle( Point( nX, nY ), Size( nWidth, nHeight ) ) );
SetLineColor( aOldLineColor );
SetFillColor( aOldFillColor );
EnableMapMode( bMap );
}
void OutputDevice::DrawTransparent( const PolyPolygon& rPolyPoly, USHORT nTransparencePercent )
{
bool bDrawn = false;
// short circuit for drawing an opaque polygon
if( (nTransparencePercent < 1) )
{
DrawPolyPolygon( rPolyPoly );
return;
}
// short circut for drawing an invisible polygon
if( !mbFillColor || (nTransparencePercent >= 100) )
{
// short circuit if the polygon border is invisible too
if( !mbLineColor )
return;
// DrawTransparent() assumes that the border is NOT to be drawn transparently???
Push( PUSH_FILLCOLOR );
SetFillColor();
DrawPolyPolygon( rPolyPoly );
Pop();
return;
}
// get the device graphics as drawing target
if( !mpGraphics )
return;
// debug helper:
static const char* pDisableNative = getenv( "SAL_DISABLE_NATIVE_ALPHA");
if( 1 )
{
{
PolyPolygon aPolyPoly( LogicToPixel( rPolyPoly ) );
Rectangle aPolyRect( aPolyPoly.GetBoundRect() );
Point aPoint;
Rectangle aDstRect( aPoint, GetOutputSizePixel() );
aDstRect.Intersection( aPolyRect );
if( !aDstRect.IsEmpty() )
{
// #i66849# Added fast path for exactly rectangular
// polygons
// #i83087# Naturally, system alpha blending cannot
// work with separate alpha VDev
if( !pDisableNative && aPolyPoly.IsRect() )
{
// setup Graphics only here (other cases delegate
// to basic OutDev methods)
if( 1 )
{
if ( mbInitClipRegion )
ImplInitClipRegion();
if ( mbInitLineColor )
ImplInitLineColor();
if ( mbInitFillColor )
ImplInitFillColor();
Rectangle aLogicPolyRect( rPolyPoly.GetBoundRect() );
Rectangle aPixelRect( ImplLogicToDevicePixel( aLogicPolyRect ) );
if( !mbOutputClipped )
{
bDrawn = mpGraphics->DrawAlphaRect(
aPixelRect.Left(), aPixelRect.Top(),
// #i98405# use methods with small g, else one pixel too much will be painted.
// This is because the source is a polygon which when painted would not paint
// the rightmost and lowest pixel line(s), so use one pixel less for the
// rectangle, too.
aPixelRect.getWidth(), aPixelRect.getHeight(),
static_cast<sal_uInt8>(nTransparencePercent),
this );
}
else
bDrawn = true;
}
}
if( !bDrawn )
{
//VirtualDevice aVDev( *this, 1 );
VirtualDevice aVDev( *this );
const Size aDstSz( aDstRect.GetSize() );
const BYTE cTrans = (BYTE) MinMax( FRound( nTransparencePercent * 2.55 ), 0, 255 );
if( aDstRect.Left() || aDstRect.Top() )
aPolyPoly.Move( -aDstRect.Left(), -aDstRect.Top() );
if( aVDev.SetOutputSizePixel( aDstSz ) )
{
const BOOL bOldMap = mbMap;
EnableMapMode( FALSE );
aVDev.SetLineColor( COL_BLACK );
aVDev.SetFillColor( COL_BLACK );
aVDev.DrawPolyPolygon( aPolyPoly );
Bitmap aPaint( GetBitmap( aDstRect.TopLeft(), aDstSz ) );
Bitmap aPolyMask( aVDev.GetBitmap( Point(), aDstSz ) );
// #107766# check for non-empty bitmaps before accessing them
if( !!aPaint && !!aPolyMask )
{
BitmapWriteAccess* pW = aPaint.AcquireWriteAccess();
BitmapReadAccess* pR = aPolyMask.AcquireReadAccess();
if( pW && pR )
{
BitmapColor aPixCol;
const BitmapColor aFillCol( GetFillColor() );
const BitmapColor aWhite( pR->GetBestMatchingColor( Color( COL_WHITE ) ) );
const BitmapColor aBlack( pR->GetBestMatchingColor( Color( COL_BLACK ) ) );
const long nWidth = pW->Width(), nHeight = pW->Height();
const long nR = aFillCol.GetRed(), nG = aFillCol.GetGreen(), nB = aFillCol.GetBlue();
long nX, nY;
if( aPaint.GetBitCount() <= 8 )
{
const BitmapPalette& rPal = pW->GetPalette();
const USHORT nCount = rPal.GetEntryCount();
BitmapColor* pMap = (BitmapColor*) new BYTE[ nCount * sizeof( BitmapColor ) ];
for( USHORT i = 0; i < nCount; i++ )
{
BitmapColor aCol( rPal[ i ] );
pMap[ i ] = BitmapColor( (BYTE) rPal.GetBestIndex( aCol.Merge( aFillCol, cTrans ) ) );
}
if( pR->GetScanlineFormat() == BMP_FORMAT_1BIT_MSB_PAL &&
pW->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL )
{
const BYTE cBlack = aBlack.GetIndex();
for( nY = 0; nY < nHeight; nY++ )
{
Scanline pWScan = pW->GetScanline( nY );
Scanline pRScan = pR->GetScanline( nY );
BYTE cBit = 128;
for( nX = 0; nX < nWidth; nX++, cBit >>= 1, pWScan++ )
{
if( !cBit )
cBit = 128, pRScan++;
if( ( *pRScan & cBit ) == cBlack )
*pWScan = (BYTE) pMap[ *pWScan ].GetIndex();
}
}
}
else
{
for( nY = 0; nY < nHeight; nY++ )
for( nX = 0; nX < nWidth; nX++ )
if( pR->GetPixel( nY, nX ) == aBlack )
pW->SetPixel( nY, nX, pMap[ pW->GetPixel( nY, nX ).GetIndex() ] );
}
delete[] (BYTE*) pMap;
}
else
{
if( pR->GetScanlineFormat() == BMP_FORMAT_1BIT_MSB_PAL &&
pW->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_BGR )
{
const BYTE cBlack = aBlack.GetIndex();
for( nY = 0; nY < nHeight; nY++ )
{
Scanline pWScan = pW->GetScanline( nY );
Scanline pRScan = pR->GetScanline( nY );
BYTE cBit = 128;
for( nX = 0; nX < nWidth; nX++, cBit >>= 1, pWScan += 3 )
{
if( !cBit )
cBit = 128, pRScan++;
if( ( *pRScan & cBit ) == cBlack )
{
pWScan[ 0 ] = COLOR_CHANNEL_MERGE( pWScan[ 0 ], nB, cTrans );
pWScan[ 1 ] = COLOR_CHANNEL_MERGE( pWScan[ 1 ], nG, cTrans );
pWScan[ 2 ] = COLOR_CHANNEL_MERGE( pWScan[ 2 ], nR, cTrans );
}
}
}
}
else
{
for( nY = 0; nY < nHeight; nY++ )
{
for( nX = 0; nX < nWidth; nX++ )
{
if( pR->GetPixel( nY, nX ) == aBlack )
{
aPixCol = pW->GetColor( nY, nX );
pW->SetPixel( nY, nX, aPixCol.Merge( aFillCol, cTrans ) );
}
}
}
}
}
}
aPolyMask.ReleaseAccess( pR );
aPaint.ReleaseAccess( pW );
DrawBitmap( aDstRect.TopLeft(), aPaint );
EnableMapMode( bOldMap );
if( mbLineColor )
{
Push( PUSH_FILLCOLOR );
SetFillColor();
DrawPolyPolygon( rPolyPoly );
Pop();
}
}
}
else
DrawPolyPolygon( rPolyPoly );
}
}
}
}
}
void OutputDevice::DrawTransparent( const GDIMetaFile& rMtf, const Point& rPos, const Size& rSize, const Gradient& rTransparenceGradient )
{
const Color aBlack( COL_BLACK );
if( ( rTransparenceGradient.GetStartColor() == aBlack && rTransparenceGradient.GetEndColor() == aBlack ) )
{
( (GDIMetaFile&) rMtf ).WindStart();
( (GDIMetaFile&) rMtf ).Play( this, rPos, rSize );
( (GDIMetaFile&) rMtf ).WindStart();
}
else
{
Rectangle aOutRect( LogicToPixel( rPos ), LogicToPixel( rSize ) );
Point aPoint;
Rectangle aDstRect( aPoint, GetOutputSizePixel() );
aDstRect.Intersection( aOutRect );
if( !aDstRect.IsEmpty() )
{
VirtualDevice* pVDev = new VirtualDevice;
((OutputDevice*)pVDev)->mnDPIX = mnDPIX;
((OutputDevice*)pVDev)->mnDPIY = mnDPIY;
if( pVDev->SetOutputSizePixel( aDstRect.GetSize() ) )
{
Bitmap aPaint, aMask;
AlphaMask aAlpha;
MapMode aMap( GetMapMode() );
Point aOutPos( PixelToLogic( aDstRect.TopLeft() ) );
const BOOL bOldMap = mbMap;
aMap.SetOrigin( Point( -aOutPos.X(), -aOutPos.Y() ) );
pVDev->SetMapMode( aMap );
const BOOL bVDevOldMap = pVDev->IsMapModeEnabled();
// create paint bitmap
( (GDIMetaFile&) rMtf ).WindStart();
( (GDIMetaFile&) rMtf ).Play( pVDev, rPos, rSize );
( (GDIMetaFile&) rMtf ).WindStart();
pVDev->EnableMapMode( FALSE );
aPaint = pVDev->GetBitmap( Point(), pVDev->GetOutputSizePixel() );
pVDev->EnableMapMode( bVDevOldMap ); // #i35331#: MUST NOT use EnableMapMode( TRUE ) here!
// create mask bitmap
pVDev->SetLineColor( COL_BLACK );
pVDev->SetFillColor( COL_BLACK );
pVDev->DrawRect( Rectangle( pVDev->PixelToLogic( Point() ), pVDev->GetOutputSize() ) );
#ifdef QWE
pVDev->SetDrawMode( DRAWMODE_WHITELINE | DRAWMODE_WHITEFILL | DRAWMODE_WHITETEXT |
DRAWMODE_WHITEBITMAP | DRAWMODE_WHITEGRADIENT );
#endif
( (GDIMetaFile&) rMtf ).WindStart();
( (GDIMetaFile&) rMtf ).Play( pVDev, rPos, rSize );
( (GDIMetaFile&) rMtf ).WindStart();
pVDev->EnableMapMode( FALSE );
aMask = pVDev->GetBitmap( Point(), pVDev->GetOutputSizePixel() );
pVDev->EnableMapMode( bVDevOldMap ); // #i35331#: MUST NOT use EnableMapMode( TRUE ) here!
#ifdef QWE
// create alpha mask from gradient
pVDev->SetDrawMode( DRAWMODE_GRAYGRADIENT );
#endif
pVDev->DrawGradient( Rectangle( rPos, rSize ), rTransparenceGradient );
#ifdef QWE
pVDev->SetDrawMode( DRAWMODE_DEFAULT );
#endif
pVDev->EnableMapMode( FALSE );
pVDev->DrawMask( Point(), pVDev->GetOutputSizePixel(), aMask, Color( COL_WHITE ) );
aAlpha = pVDev->GetBitmap( Point(), pVDev->GetOutputSizePixel() );
delete pVDev;
EnableMapMode( FALSE );
DrawBitmapEx( aDstRect.TopLeft(), BitmapEx( aPaint, aAlpha ) );
EnableMapMode( bOldMap );
}
else
delete pVDev;
}
}
}
void OutputDevice::DrawEPS( const Point& rPoint, const Size& rSize,
const GfxLink& rGfxLink, GDIMetaFile* pSubst )
{
if( mbOutputClipped )
return;
Rectangle aRect( ImplLogicToDevicePixel( Rectangle( rPoint, rSize ) ) );
if( !aRect.IsEmpty() )
{
// else draw the substitution graphics
if( pSubst )
{
//Graphic( *pSubst ).Draw( this, rPoint, rSize );
Gradient rTransparenceGradient;
rTransparenceGradient.SetStartColor( Color( COL_BLACK ) );
rTransparenceGradient.SetEndColor( Color( COL_BLACK ) );
DrawTransparent( *pSubst, rPoint, rSize, rTransparenceGradient );
}
}
}
// -----------------------------------------------------------------------
bool OutputDevice::ImplIsAntiparallel() const
{
bool bRet = false;
if( mpGraphics )
{
if( ( (mpGraphics->GetLayout() & SAL_LAYOUT_BIDI_RTL) && ! IsRTLEnabled() ) ||
( ! (mpGraphics->GetLayout() & SAL_LAYOUT_BIDI_RTL) && IsRTLEnabled() ) )
{
bRet = true;
}
}
return bRet;
}
void OutputDevice::ImplSetClipRegion( const Region* pRegion )
{
if ( !pRegion )
{
if ( mbClipRegion )
{
maRegion = Region( REGION_NULL );
mbClipRegion = FALSE;
mbInitClipRegion = TRUE;
}
}
else
{
maRegion = *pRegion;
mbClipRegion = TRUE;
mbInitClipRegion = TRUE;
}
}
void OutputDevice::SetClipRegion()
{
ImplSetClipRegion( NULL );
}
void OutputDevice::SetClipRegion( const Region& rRegion )
{
if ( rRegion.GetType() == REGION_NULL )
ImplSetClipRegion( NULL );
else
{
Region aRegion = LogicToPixel( rRegion );
ImplSetClipRegion( &aRegion );
}
}
void OutputDevice::MoveClipRegion( long nHorzMove, long nVertMove )
{
if ( mbClipRegion )
{
maRegion.Move( ImplLogicWidthToDevicePixel( nHorzMove ),
ImplLogicHeightToDevicePixel( nVertMove ) );
mbInitClipRegion = TRUE;
}
}
void OutputDevice::IntersectClipRegion( const Rectangle& rRect )
{
Rectangle aRect = LogicToPixel( rRect );
maRegion.Intersect( aRect );
mbClipRegion = TRUE;
mbInitClipRegion = TRUE;
}
void OutputDevice::IntersectClipRegion( const Region& rRegion )
{
RegionType eType = rRegion.GetType();
if ( eType != REGION_NULL )
{
Region aRegion = LogicToPixel( rRegion );
maRegion.Intersect( aRegion );
mbClipRegion = TRUE;
mbInitClipRegion = TRUE;
}
}
// -----------------------------------------------------------------------
Region OutputDevice::GetClipRegion() const
{
return PixelToLogic( maRegion );
}
void OutputDevice::SetLineColor()
{
if ( mbLineColor )
{
mbInitLineColor = TRUE;
mbLineColor = FALSE;
maLineColor = Color( COL_TRANSPARENT );
}
}
void OutputDevice::SetLineColor( const Color& rColor )
{
if( rColor.GetTransparency() )
{
if ( mbLineColor )
{
mbInitLineColor = TRUE;
mbLineColor = FALSE;
maLineColor = Color( COL_TRANSPARENT );
}
}
else
{
if( maLineColor != rColor )
{
mbInitLineColor = TRUE;
mbLineColor = TRUE;
maLineColor = rColor;
}
}
}
void OutputDevice::SetFillColor()
{
if ( mbFillColor )
{
mbInitFillColor = TRUE;
mbFillColor = FALSE;
maFillColor = Color( COL_TRANSPARENT );
}
}
void OutputDevice::SetFillColor( const Color& rColor )
{
if ( rColor.GetTransparency() )
{
if ( mbFillColor )
{
mbInitFillColor = TRUE;
mbFillColor = FALSE;
maFillColor = Color( COL_TRANSPARENT );
}
}
else
{
if ( maFillColor != rColor )
{
mbInitFillColor = TRUE;
mbFillColor = TRUE;
maFillColor = rColor;
}
}
}
void OutputDevice::SetTextColor( const Color& rColor )
{
if ( maTextColor != rColor )
{
maTextColor = rColor;
mbInitTextColor = TRUE;
}
}
void OutputDevice::SetTextFillColor()
{
if ( maFont.GetColor() != Color( COL_TRANSPARENT ) )
maFont.SetFillColor( Color( COL_TRANSPARENT ) );
if ( !maFont.IsTransparent() )
maFont.SetTransparent( TRUE );
}
void OutputDevice::SetTextFillColor( const Color& rColor )
{
BOOL bTransFill = rColor.GetTransparency() ? TRUE : FALSE;
if ( maFont.GetFillColor() != rColor )
maFont.SetFillColor( rColor );
if ( maFont.IsTransparent() != bTransFill )
maFont.SetTransparent( bTransFill );
}
Color OutputDevice::GetTextFillColor() const
{
if ( maFont.IsTransparent() )
return Color( COL_TRANSPARENT );
else
return maFont.GetFillColor();
}
void OutputDevice::SetTextLineColor()
{
maTextLineColor = Color( COL_TRANSPARENT );
}
void OutputDevice::SetTextLineColor( const Color& rColor )
{
maTextLineColor = rColor;
}
void OutputDevice::SetOverlineColor()
{
}
void OutputDevice::SetOverlineColor( const Color& rColor )
{
}
void OutputDevice::SetTextAlign( TextAlign eAlign )
{
if ( maFont.GetAlign() != eAlign )
{
maFont.SetAlign( eAlign );
mbNewFont = TRUE;
}
}
// -----------------------------------------------------------------------
void OutputDevice::SetBackground()
{
maBackground = Wallpaper();
mbBackground = FALSE;
}
// -----------------------------------------------------------------------
void OutputDevice::SetBackground( const Wallpaper& rBackground )
{
maBackground = rBackground;
if( rBackground.GetStyle() == WALLPAPER_NULL )
mbBackground = FALSE;
else
mbBackground = TRUE;
}
USHORT OutputDevice::GetBitCount() const
{
#ifdef AVS
if ( meOutDevType == OUTDEV_VIRDEV )
#endif
return ((OutputDevice*)this)->mnBitCount;
#ifdef AVS
// we need a graphics
if ( !mpGraphics )
return 0;
return (USHORT)mpGraphics->GetBitCount();
#endif
}
void OutputDevice::SetDigitLanguage( LanguageType eLang )
{
meTextLanguage = eLang;
}
void OutputDevice::SetLayoutMode( ULONG nTextLayoutMode )
{
mnTextLayoutMode = nTextLayoutMode;
}
void OutputDevice::SetRefPoint()
{
mbRefPoint = FALSE;
maRefPoint.X() = maRefPoint.Y() = 0L;
}
// -----------------------------------------------------------------------
void OutputDevice::SetRefPoint( const Point& rRefPoint )
{
mbRefPoint = TRUE;
maRefPoint = rRefPoint;
}
void OutputDevice::Push( USHORT nFlags )
{
ImplObjStack* pData = new ImplObjStack;
pData->mpPrev = mpObjStack;
mpObjStack = pData;
pData->mnFlags = nFlags;
if ( nFlags & PUSH_LINECOLOR )
{
if ( mbLineColor )
pData->mpLineColor = new Color( maLineColor );
else
pData->mpLineColor = NULL;
}
if ( nFlags & PUSH_FILLCOLOR )
{
if ( mbFillColor )
pData->mpFillColor = new Color( maFillColor );
else
pData->mpFillColor = NULL;
}
if ( nFlags & PUSH_FONT )
pData->mpFont = new Font( maFont );
if ( nFlags & PUSH_TEXTCOLOR )
pData->mpTextColor = new Color( GetTextColor() );
if ( nFlags & PUSH_TEXTFILLCOLOR )
{
if ( IsTextFillColor() )
pData->mpTextFillColor = new Color( GetTextFillColor() );
else
pData->mpTextFillColor = NULL;
}
if ( nFlags & PUSH_TEXTLINECOLOR )
{
if ( IsTextLineColor() )
pData->mpTextLineColor = new Color( GetTextLineColor() );
else
pData->mpTextLineColor = NULL;
}
if ( nFlags & PUSH_OVERLINECOLOR )
{
if ( IsOverlineColor() )
pData->mpOverlineColor = new Color( GetOverlineColor() );
else
pData->mpOverlineColor = NULL;
}
if ( nFlags & PUSH_TEXTALIGN )
pData->meTextAlign = GetTextAlign();
if( nFlags & PUSH_TEXTLAYOUTMODE )
pData->mnTextLayoutMode = GetLayoutMode();
if( nFlags & PUSH_TEXTLANGUAGE )
pData->meTextLanguage = GetDigitLanguage();
if ( nFlags & PUSH_RASTEROP )
pData->meRasterOp = GetRasterOp();
if ( nFlags & PUSH_MAPMODE )
{
if ( mbMap )
pData->mpMapMode = new MapMode( maMapMode );
else
pData->mpMapMode = NULL;
}
if ( nFlags & PUSH_CLIPREGION )
{
if ( mbClipRegion )
pData->mpClipRegion = new Region( maRegion );
else
pData->mpClipRegion = NULL;
}
if ( nFlags & PUSH_REFPOINT )
{
if ( mbRefPoint )
pData->mpRefPoint = new Point( maRefPoint );
else
pData->mpRefPoint = NULL;
}
}
// -----------------------------------------------------------------------
void OutputDevice::Pop()
{
ImplObjStack* pData = mpObjStack;
if ( !pData )
{
//DBG_ERRORFILE( "OutputDevice::Pop() without OutputDevice::Push()" );
return;
}
mpObjStack = pData->mpPrev;
if ( pData->mnFlags & PUSH_LINECOLOR )
{
if ( pData->mpLineColor )
SetLineColor( *pData->mpLineColor );
else
SetLineColor();
}
if ( pData->mnFlags & PUSH_FILLCOLOR )
{
if ( pData->mpFillColor )
SetFillColor( *pData->mpFillColor );
else
SetFillColor();
}
if ( pData->mnFlags & PUSH_FONT )
SetFont( *pData->mpFont );
if ( pData->mnFlags & PUSH_TEXTCOLOR )
SetTextColor( *pData->mpTextColor );
if ( pData->mnFlags & PUSH_TEXTFILLCOLOR )
{
if ( pData->mpTextFillColor )
SetTextFillColor( *pData->mpTextFillColor );
else
SetTextFillColor();
}
if ( pData->mnFlags & PUSH_TEXTLINECOLOR )
{
if ( pData->mpTextLineColor )
SetTextLineColor( *pData->mpTextLineColor );
else
SetTextLineColor();
}
if ( pData->mnFlags & PUSH_OVERLINECOLOR )
{
if ( pData->mpOverlineColor )
SetOverlineColor( *pData->mpOverlineColor );
else
SetOverlineColor();
}
if ( pData->mnFlags & PUSH_TEXTALIGN )
SetTextAlign( pData->meTextAlign );
if( pData->mnFlags & PUSH_TEXTLAYOUTMODE )
SetLayoutMode( pData->mnTextLayoutMode );
if( pData->mnFlags & PUSH_TEXTLANGUAGE )
SetDigitLanguage( pData->meTextLanguage );
if ( pData->mnFlags & PUSH_RASTEROP )
SetRasterOp( pData->meRasterOp );
if ( pData->mnFlags & PUSH_MAPMODE )
{
if ( pData->mpMapMode )
SetMapMode( *pData->mpMapMode );
else
SetMapMode();
}
if ( pData->mnFlags & PUSH_CLIPREGION )
ImplSetClipRegion( pData->mpClipRegion );
if ( pData->mnFlags & PUSH_REFPOINT )
{
if ( pData->mpRefPoint )
SetRefPoint( *pData->mpRefPoint );
else
SetRefPoint();
}
ImplDeleteObjStack( pData );
}
void OutputDevice::SetFont( const Font& rNewFont )
{
Font aFont( rNewFont );
if ( !maFont.IsSameInstance( aFont ) )
{
// Optimization MT/HDU: COL_TRANSPARENT means SetFont should ignore the font color,
// because SetTextColor() is used for this.
// #i28759# maTextColor might have been changed behind our back, commit then, too.
if( aFont.GetColor() != COL_TRANSPARENT
&& (aFont.GetColor() != maFont.GetColor() || aFont.GetColor() != maTextColor ) )
{
maTextColor = aFont.GetColor();
mbInitTextColor = TRUE;
}
maFont = aFont;
mbNewFont = TRUE;
}
}
#ifdef QWE
// -----------------------------------------------------------------------
void OutputDevice::SetDrawMode( ULONG nDrawMode )
{
mnDrawMode = nDrawMode;
}
#endif
void OutputDevice::SetRasterOp( RasterOp eRasterOp )
{
if ( meRasterOp != eRasterOp )
{
meRasterOp = eRasterOp;
mbInitLineColor = mbInitFillColor = TRUE;
if( mpGraphics )
mpGraphics->SetXORMode( (ROP_INVERT == meRasterOp) || (ROP_XOR == meRasterOp), ROP_INVERT == meRasterOp );
}
}
BOOL OutputDevice::SetOutputSizePixel( const Size& rNewSize, BOOL bErase )
{
return ImplSetOutputSizePixel(rNewSize, bErase);
}
BOOL OutputDevice::ImplSetOutputSizePixel( const Size& rNewSize, BOOL bErase )
{
#ifdef AVS
DBG_TRACE3( "VirtualDevice::ImplSetOutputSizePixel( %ld, %ld, %d )", rNewSize.Width(), rNewSize.Height(), (int)bErase );
if ( !mpVirDev )
return FALSE;
else
#endif
if ( rNewSize == GetOutputSizePixel() )
{
if ( bErase )
Erase();
return TRUE;
}
BOOL bRet;
long nNewWidth = rNewSize.Width(), nNewHeight = rNewSize.Height();
if ( nNewWidth < 1 )
nNewWidth = 1;
if ( nNewHeight < 1 )
nNewHeight = 1;
#ifdef AVS
if ( bErase )
{
bRet = mpVirDev->SetSize( nNewWidth, nNewHeight );
if ( bRet )
{
#endif
mnOutWidth = rNewSize.Width();
mnOutHeight = rNewSize.Height();
mpGraphics->SetOutputSizePixel( rNewSize );
Erase();
#ifdef AVS
}
}
else
{
SalVirtualDevice* pNewVirDev;
ImplSVData* pSVData = ImplGetSVData();
// we need a graphics
if ( !mpGraphics )
return FALSE;
pNewVirDev = pSVData->mpDefInst->CreateVirtualDevice( mpGraphics, nNewWidth, nNewHeight, mnBitCount );
if ( pNewVirDev )
{
SalGraphics* pGraphics = pNewVirDev->GetGraphics();
if ( pGraphics )
{
SalTwoRect aPosAry;
long nWidth;
long nHeight;
if ( mnOutWidth < nNewWidth )
nWidth = mnOutWidth;
else
nWidth = nNewWidth;
if ( mnOutHeight < nNewHeight )
nHeight = mnOutHeight;
else
nHeight = nNewHeight;
aPosAry.mnSrcX = 0;
aPosAry.mnSrcY = 0;
aPosAry.mnSrcWidth = nWidth;
aPosAry.mnSrcHeight = nHeight;
aPosAry.mnDestX = 0;
aPosAry.mnDestY = 0;
aPosAry.mnDestWidth = nWidth;
aPosAry.mnDestHeight = nHeight;
pGraphics->CopyBits( &aPosAry, mpGraphics, this, this );
pNewVirDev->ReleaseGraphics( pGraphics );
ImplReleaseGraphics();
pSVData->mpDefInst->DestroyVirtualDevice( mpVirDev );
mpVirDev = pNewVirDev;
mnOutWidth = rNewSize.Width();
mnOutHeight = rNewSize.Height();
bRet = TRUE;
}
else
{
bRet = FALSE;
pSVData->mpDefInst->DestroyVirtualDevice( pNewVirDev );
}
}
else
bRet = FALSE;
}
return bRet;
#endif
return TRUE;
}
Bitmap OutputDevice::GetBitmap( const Point& rSrcPt, const Size& rSize ) const
{
Bitmap aBmp;
long nX = ImplLogicXToDevicePixel( rSrcPt.X() );
long nY = ImplLogicYToDevicePixel( rSrcPt.Y() );
long nWidth = ImplLogicWidthToDevicePixel( rSize.Width() );
long nHeight = ImplLogicHeightToDevicePixel( rSize.Height() );
if ( mpGraphics )
{
if ( nWidth && nHeight )
{
Rectangle aRect( Point( nX, nY ), Size( nWidth, nHeight ) );
BOOL bClipped = FALSE;
// X-Koordinate ausserhalb des Bereichs?
if ( nX < mnOutOffX )
{
nWidth -= ( mnOutOffX - nX );
nX = mnOutOffX;
bClipped = TRUE;
}
// Y-Koordinate ausserhalb des Bereichs?
if ( nY < mnOutOffY )
{
nHeight -= ( mnOutOffY - nY );
nY = mnOutOffY;
bClipped = TRUE;
}
// Breite ausserhalb des Bereichs?
if ( (nWidth + nX) > (mnOutWidth + mnOutOffX) )
{
nWidth = mnOutOffX + mnOutWidth - nX;
bClipped = TRUE;
}
// Hoehe ausserhalb des Bereichs?
if ( (nHeight + nY) > (mnOutHeight + mnOutOffY) )
{
nHeight = mnOutOffY + mnOutHeight - nY;
bClipped = TRUE;
}
if ( bClipped )
{
// Falls auf den sichtbaren Bereich geclipped wurde,
// muessen wir eine Bitmap in der rchtigen Groesse
// erzeugen, in die die geclippte Bitmap an die angepasste
// Position kopiert wird
OutputDevice aVDev( *this );
if ( aVDev.SetOutputSizePixel( aRect.GetSize() ) )
{
if ( ((OutputDevice*)&aVDev)->mpGraphics )
{
SalTwoRect aPosAry;
aPosAry.mnSrcX = nX;
aPosAry.mnSrcY = nY;
aPosAry.mnSrcWidth = nWidth;
aPosAry.mnSrcHeight = nHeight;
aPosAry.mnDestX = ( aRect.Left() < mnOutOffX ) ? ( mnOutOffX - aRect.Left() ) : 0L;
aPosAry.mnDestY = ( aRect.Top() < mnOutOffY ) ? ( mnOutOffY - aRect.Top() ) : 0L;
aPosAry.mnDestWidth = nWidth;
aPosAry.mnDestHeight = nHeight;
if ( (nWidth > 0) && (nHeight > 0) )
(((OutputDevice*)&aVDev)->mpGraphics)->CopyBits( &aPosAry, mpGraphics, this, this );
aBmp = aVDev.GetBitmap( Point(), aVDev.GetOutputSizePixel() );
}
else
bClipped = FALSE;
}
else
bClipped = FALSE;
}
if ( !bClipped )
{
SalBitmap* pSalBmp = mpGraphics->GetBitmap( nX, nY, nWidth, nHeight, this );
if( pSalBmp )
{
ImpBitmap* pImpBmp = new ImpBitmap;
pImpBmp->ImplSetSalBitmap( pSalBmp );
aBmp.ImplSetImpBitmap( pImpBmp );
}
}
}
}
return aBmp;
}
void OutputDevice::Erase()
{
if ( mbBackground )
{
RasterOp eRasterOp = GetRasterOp();
if ( eRasterOp != ROP_OVERPAINT )
SetRasterOp( ROP_OVERPAINT );
ImplDrawWallpaper( 0, 0, mnOutWidth, mnOutHeight, maBackground );
if ( eRasterOp != ROP_OVERPAINT )
SetRasterOp( eRasterOp );
}
}
BOOL OutputDevice::GetGlyphBoundRects( const Point& rOrigin, const String& rStr,
int nIndex, int nLen, int nBase, MetricVector& rVector )
{
rVector.clear();
if( nLen == STRING_LEN )
nLen = rStr.Len() - nIndex;
Rectangle aRect;
for( int i = 0; i < nLen; i++ )
{
if( !GetTextBoundRect( aRect, rStr, static_cast<xub_StrLen>(nBase), static_cast<xub_StrLen>(nIndex+i), 1 ) )
break;
aRect.Move( rOrigin.X(), rOrigin.Y() );
rVector.push_back( aRect );
}
return (nLen == (int)rVector.size());
}
BOOL OutputDevice::GetTextBoundRect( Rectangle& rRect,
const String& rStr, xub_StrLen nBase, xub_StrLen nIndex,
xub_StrLen nLen ) const
{
//DBG_TRACE( "OutputDevice::GetTextBoundRect()" );
//DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
BOOL bRet = FALSE;
rRect.SetEmpty();
SalLayout* pSalLayout = NULL;
// calculate offset when nBase!=nIndex
long nXOffset = 0;
if( nBase != nIndex )
{
xub_StrLen nStart = Min( nBase, nIndex );
xub_StrLen nOfsLen = Max( nBase, nIndex ) - nStart;
pSalLayout = ImplLayout( rStr, nStart, nOfsLen );
if( pSalLayout )
{
nXOffset = pSalLayout->GetTextWidth();
nXOffset /= pSalLayout->GetUnitsPerPixel();
pSalLayout->Release();
// TODO: fix offset calculation for Bidi case
if( nBase < nIndex)
nXOffset = -nXOffset;
}
}
pSalLayout = ImplLayout( rStr, nIndex, nLen );
Rectangle aPixelRect;
if( pSalLayout )
{
bRet = pSalLayout->GetBoundRect( *mpGraphics, aPixelRect );
if( bRet )
{
int nWidthFactor = pSalLayout->GetUnitsPerPixel();
if( nWidthFactor > 1 )
{
double fFactor = 1.0 / nWidthFactor;
aPixelRect.Left()
= static_cast< long >(aPixelRect.Left() * fFactor);
aPixelRect.Right()
= static_cast< long >(aPixelRect.Right() * fFactor);
aPixelRect.Top()
= static_cast< long >(aPixelRect.Top() * fFactor);
aPixelRect.Bottom()
= static_cast< long >(aPixelRect.Bottom() * fFactor);
}
Point aRotatedOfs( mnTextOffX, mnTextOffY );
aRotatedOfs -= pSalLayout->GetDrawPosition( Point( nXOffset, 0 ) );
aPixelRect += aRotatedOfs;
rRect = PixelToLogic( aPixelRect );
if( mbMap )
rRect += Point( maMapRes.mnMapOfsX, maMapRes.mnMapOfsY );
}
pSalLayout->Release();
}
return TRUE;
#ifdef AVS
if( bRet || (OUTDEV_PRINTER == meOutDevType) || !mpFontEntry )
return bRet;
// fall back to bitmap method to get the bounding rectangle,
// so we need a monochrome virtual device with matching font
VirtualDevice aVDev( 1 );
Font aFont( GetFont() );
aFont.SetShadow( FALSE );
aFont.SetOutline( FALSE );
aFont.SetRelief( RELIEF_NONE );
aFont.SetOrientation( 0 );
aFont.SetSize( Size( mpFontEntry->maFontSelData.mnWidth, mpFontEntry->maFontSelData.mnHeight ) );
aVDev.SetFont( aFont );
aVDev.SetTextAlign( ALIGN_TOP );
// layout the text on the virtual device
pSalLayout = aVDev.ImplLayout( rStr, nIndex, nLen );
if( !pSalLayout )
return false;
// make the bitmap big enough
// TODO: use factors when it would get too big
long nWidth = pSalLayout->GetTextWidth();
long nHeight = mpFontEntry->mnLineHeight + mnEmphasisAscent + mnEmphasisDescent;
Point aOffset( nWidth/2, 8 );
Size aOutSize( nWidth + 2*aOffset.X(), nHeight + 2*aOffset.Y() );
if( !nWidth || !aVDev.SetOutputSizePixel( aOutSize ) )
return false;
// draw text in black
pSalLayout->DrawBase() = aOffset;
aVDev.SetTextColor( Color( COL_BLACK ) );
aVDev.SetTextFillColor();
aVDev.ImplInitTextColor();
aVDev.ImplDrawText( *pSalLayout );
pSalLayout->Release();
// find extents using the bitmap
Bitmap aBmp = aVDev.GetBitmap( Point(), aOutSize );
BitmapReadAccess* pAcc = aBmp.AcquireReadAccess();
if( !pAcc )
return FALSE;
const BitmapColor aBlack( pAcc->GetBestMatchingColor( Color( COL_BLACK ) ) );
const long nW = pAcc->Width();
const long nH = pAcc->Height();
long nLeft = 0;
long nRight = 0;
// find top left point
long nTop = 0;
for(; nTop < nH; ++nTop )
{
for( nLeft = 0; nLeft < nW; ++nLeft )
if( pAcc->GetPixel( nTop, nLeft ) == aBlack )
break;
if( nLeft < nW )
break;
}
// find bottom right point
long nBottom = nH;
while( --nBottom >= nTop )
{
for( nRight = nW; --nRight >= 0; )
if( pAcc->GetPixel( nBottom, nRight ) == aBlack )
break;
if( nRight >= 0 )
break;
}
if( nRight < nLeft )
{
long nX = nRight;
nRight = nLeft;
nLeft = nX;
}
for( long nY = nTop; nY <= nBottom; ++nY )
{
// find leftmost point
long nX;
for( nX = 0; nX < nLeft; ++nX )
if( pAcc->GetPixel( nY, nX ) == aBlack )
break;
nLeft = nX;
// find rightmost point
for( nX = nW; --nX > nRight; )
if( pAcc->GetPixel( nY, nX ) == aBlack )
break;
nRight = nX;
}
aBmp.ReleaseAccess( pAcc );
if( nTop <= nBottom )
{
Size aSize( nRight - nLeft + 1, nBottom - nTop + 1 );
Point aTopLeft( nLeft, nTop );
aTopLeft -= aOffset;
// adjust to text alignment
aTopLeft.Y()+= mnTextOffY - (mpFontEntry->maMetric.mnAscent + mnEmphasisAscent);
// convert to logical coordinates
aSize = PixelToLogic( aSize );
aTopLeft.X() = ImplDevicePixelToLogicWidth( aTopLeft.X() );
aTopLeft.Y() = ImplDevicePixelToLogicHeight( aTopLeft.Y() );
rRect = Rectangle( aTopLeft, aSize );
return TRUE;
}
return FALSE;
#endif
}
// =======================================================================
ImplFontEntry::ImplFontEntry( const ImplFontSelectData& rFontSelData )
: maFontSelData( rFontSelData ),
maMetric( rFontSelData ),
mpConversion( NULL ),
mnRefCount( 1 ),
mnSetFontFlags( 0 ),
mnOwnOrientation( 0 ),
mnOrientation( 0 ),
mbInit( false )//,
//mpUnicodeFallbackList( NULL )
{
maFontSelData.mpFontEntry = this;
}
// -----------------------------------------------------------------------
ImplFontEntry::~ImplFontEntry()
{
//delete mpUnicodeFallbackList;
}
// -----------------------------------------------------------------------
static String GetNextFontToken( const String& rTokenStr, xub_StrLen& rIndex )
{
// check for valid start index
int nStringLen = rTokenStr.Len();
if( rIndex >= nStringLen )
{
rIndex = STRING_NOTFOUND;
return String();
}
// find the next token delimiter and return the token substring
const sal_Unicode* pStr = rTokenStr.GetBuffer() + rIndex;
const sal_Unicode* pEnd = rTokenStr.GetBuffer() + nStringLen;
for(; pStr < pEnd; ++pStr )
if( (*pStr == ';') || (*pStr == ',') )
break;
xub_StrLen nTokenStart = rIndex;
xub_StrLen nTokenLen;
if( pStr < pEnd )
{
rIndex = static_cast<xub_StrLen>(pStr - rTokenStr.GetBuffer());
nTokenLen = rIndex - nTokenStart;
++rIndex; // skip over token separator
}
else
{
// no token delimiter found => handle last token
rIndex = STRING_NOTFOUND;
nTokenLen = STRING_LEN;
// optimize if the token string consists of just one token
if( !nTokenStart )
return rTokenStr;
}
return String( rTokenStr, nTokenStart, nTokenLen );
}
ImplFontMetricData::ImplFontMetricData( const ImplFontSelectData& rFontSelData )
: ImplFontAttributes( rFontSelData )
{
// initialize the members provided by the font request
mnWidth = rFontSelData.mnWidth;
mnOrientation = static_cast<short>(rFontSelData.mnOrientation);
// intialize the used font name
if( rFontSelData.mpFontData )
{
maName = rFontSelData.mpFontData->maName;
maStyleName= rFontSelData.mpFontData->maStyleName;
mbDevice = rFontSelData.mpFontData->mbDevice;
mbKernableFont = true;
}
else
{
xub_StrLen nTokenPos = 0;
maName = GetNextFontToken( rFontSelData.maName, nTokenPos );
maStyleName= rFontSelData.maStyleName;
mbDevice = false;
mbKernableFont = false;
}
// reset metrics that are usually measured for the font instance
mnAscent = 0;
mnDescent = 0;
mnIntLeading = 0;
mnExtLeading = 0;
mnSlant = 0;
mnMinKashida = 0;
// reset metrics that are usually derived from the measurements
mnUnderlineSize = 0;
mnUnderlineOffset = 0;
mnBUnderlineSize = 0;
mnBUnderlineOffset = 0;
mnDUnderlineSize = 0;
mnDUnderlineOffset1 = 0;
mnDUnderlineOffset2 = 0;
mnWUnderlineSize = 0;
mnWUnderlineOffset = 0;
mnAboveUnderlineSize = 0;
mnAboveUnderlineOffset = 0;
mnAboveBUnderlineSize = 0;
mnAboveBUnderlineOffset = 0;
mnAboveDUnderlineSize = 0;
mnAboveDUnderlineOffset1 = 0;
mnAboveDUnderlineOffset2 = 0;
mnAboveWUnderlineSize = 0;
mnAboveWUnderlineOffset = 0;
mnStrikeoutSize = 0;
mnStrikeoutOffset = 0;
mnBStrikeoutSize = 0;
mnBStrikeoutOffset = 0;
mnDStrikeoutSize = 0;
mnDStrikeoutOffset1 = 0;
mnDStrikeoutOffset2 = 0;
}
// -----------------------------------------------------------------------
void ImplFontMetricData::ImplInitTextLineSize( const OutputDevice* pDev )
{
long nDescent = mnDescent;
if ( nDescent <= 0 )
{
nDescent = mnAscent / 10;
if ( !nDescent )
nDescent = 1;
}
// #i55341# for some fonts it is not a good idea to calculate
// their text line metrics from the real font descent
// => work around this problem just for these fonts
if( 3*nDescent > mnAscent )
nDescent = mnAscent / 3;
long nLineHeight = ((nDescent*25)+50) / 100;
if ( !nLineHeight )
nLineHeight = 1;
long nLineHeight2 = nLineHeight / 2;
if ( !nLineHeight2 )
nLineHeight2 = 1;
long nBLineHeight = ((nDescent*50)+50) / 100;
if ( nBLineHeight == nLineHeight )
nBLineHeight++;
long nBLineHeight2 = nBLineHeight/2;
if ( !nBLineHeight2 )
nBLineHeight2 = 1;
long n2LineHeight = ((nDescent*16)+50) / 100;
if ( !n2LineHeight )
n2LineHeight = 1;
long n2LineDY = n2LineHeight;
/* #117909#
* add some pixels to minimum double line distance on higher resolution devices
*/
long nMin2LineDY = 1 + pDev->ImplGetDPIY()/150;
if ( n2LineDY < nMin2LineDY )
n2LineDY = nMin2LineDY;
long n2LineDY2 = n2LineDY/2;
if ( !n2LineDY2 )
n2LineDY2 = 1;
long nUnderlineOffset = mnDescent/2 + 1;
long nStrikeoutOffset = -((mnAscent - mnIntLeading) / 3);
mnUnderlineSize = nLineHeight;
mnUnderlineOffset = nUnderlineOffset - nLineHeight2;
mnBUnderlineSize = nBLineHeight;
mnBUnderlineOffset = nUnderlineOffset - nBLineHeight2;
mnDUnderlineSize = n2LineHeight;
mnDUnderlineOffset1 = nUnderlineOffset - n2LineDY2 - n2LineHeight;
mnDUnderlineOffset2 = mnDUnderlineOffset1 + n2LineDY + n2LineHeight;
long nWCalcSize = mnDescent;
if ( nWCalcSize < 6 )
{
if ( (nWCalcSize == 1) || (nWCalcSize == 2) )
mnWUnderlineSize = nWCalcSize;
else
mnWUnderlineSize = 3;
}
else
mnWUnderlineSize = ((nWCalcSize*50)+50) / 100;
// #109280# the following line assures that wavelnes are never placed below the descent, however
// for most fonts the waveline then is drawn into the text, so we better keep the old solution
// pFontEntry->maMetric.mnWUnderlineOffset = pFontEntry->maMetric.mnDescent + 1 - pFontEntry->maMetric.mnWUnderlineSize;
mnWUnderlineOffset = nUnderlineOffset;
mnStrikeoutSize = nLineHeight;
mnStrikeoutOffset = nStrikeoutOffset - nLineHeight2;
mnBStrikeoutSize = nBLineHeight;
mnBStrikeoutOffset = nStrikeoutOffset - nBLineHeight2;
mnDStrikeoutSize = n2LineHeight;
mnDStrikeoutOffset1 = nStrikeoutOffset - n2LineDY2 - n2LineHeight;
mnDStrikeoutOffset2 = mnDStrikeoutOffset1 + n2LineDY + n2LineHeight;
}
// -----------------------------------------------------------------------
void ImplFontMetricData::ImplInitAboveTextLineSize()
{
long nIntLeading = mnIntLeading;
// TODO: assess usage of nLeading below (changed in extleading CWS)
// if no leading is available, we assume 15% of the ascent
if ( nIntLeading <= 0 )
{
nIntLeading = mnAscent*15/100;
if ( !nIntLeading )
nIntLeading = 1;
}
long nLineHeight = ((nIntLeading*25)+50) / 100;
if ( !nLineHeight )
nLineHeight = 1;
long nBLineHeight = ((nIntLeading*50)+50) / 100;
if ( nBLineHeight == nLineHeight )
nBLineHeight++;
long n2LineHeight = ((nIntLeading*16)+50) / 100;
if ( !n2LineHeight )
n2LineHeight = 1;
long nCeiling = -mnAscent;
mnAboveUnderlineSize = nLineHeight;
mnAboveUnderlineOffset = nCeiling + (nIntLeading - nLineHeight + 1) / 2;
mnAboveBUnderlineSize = nBLineHeight;
mnAboveBUnderlineOffset = nCeiling + (nIntLeading - nBLineHeight + 1) / 2;
mnAboveDUnderlineSize = n2LineHeight;
mnAboveDUnderlineOffset1 = nCeiling + (nIntLeading - 3*n2LineHeight + 1) / 2;
mnAboveDUnderlineOffset2 = nCeiling + (nIntLeading + n2LineHeight + 1) / 2;
long nWCalcSize = nIntLeading;
if ( nWCalcSize < 6 )
{
if ( (nWCalcSize == 1) || (nWCalcSize == 2) )
mnAboveWUnderlineSize = nWCalcSize;
else
mnAboveWUnderlineSize = 3;
}
else
mnAboveWUnderlineSize = ((nWCalcSize*50)+50) / 100;
mnAboveWUnderlineOffset = nCeiling + (nIntLeading + 1) / 2;
}
// =======================================================================
ImplDevFontList::ImplDevFontList()
: mbMatchData( false )
, mbMapNames( false )
, mpPreMatchHook( NULL )
, mpFallbackHook( NULL )
, mpFallbackList( NULL )
, mnFallbackCount( -1 )
{}
// -----------------------------------------------------------------------
ImplDevFontList::~ImplDevFontList()
{
Clear();
}
// -----------------------------------------------------------------------
void ImplDevFontList::Clear()
{
// remove fallback lists
delete[] mpFallbackList;
mpFallbackList = NULL;
mnFallbackCount = -1;
// clear all entries in the device font list
DevFontList::iterator it = maDevFontList.begin();
for(; it != maDevFontList.end(); ++it )
{
ImplDevFontListData* pEntry = (*it).second;
delete pEntry;
}
maDevFontList.clear();
// match data must be recalculated too
mbMatchData = false;
}
// -----------------------------------------------------------------------
void ImplDevFontList::Add( ImplFontData* pNewData )
{
int nAliasQuality = pNewData->mnQuality - 100;
String aMapNames = pNewData->maMapNames;
pNewData->maMapNames = String();
bool bKeepNewData = false;
for( xub_StrLen nMapNameIndex = 0; nMapNameIndex != STRING_NOTFOUND; )
{
String aSearchName = pNewData->maName;
#ifdef AVS
ImplGetEnglishSearchFontName( aSearchName );
#endif
DevFontList::const_iterator it = maDevFontList.find( aSearchName );
ImplDevFontListData* pFoundData = NULL;
if( it != maDevFontList.end() )
pFoundData = (*it).second;
if( !pFoundData )
{
pFoundData = new ImplDevFontListData( aSearchName );
maDevFontList[ aSearchName ] = pFoundData;
}
bKeepNewData = pFoundData->AddFontFace( pNewData );
// add font alias if available
// a font alias should never win against an original font with similar quality
if( aMapNames.Len() >= nMapNameIndex )
break;
if( bKeepNewData ) // try to recycle obsoleted object
pNewData = pNewData->CreateAlias();
bKeepNewData = false;
pNewData->mnQuality = nAliasQuality;
pNewData->maName = GetNextFontToken( aMapNames, nMapNameIndex );
}
if( !bKeepNewData )
delete pNewData;
}
namespace SVMCore{
// -----------------------------------------------------------------------
void ImplGetEnglishSearchFontName( String& rName )
{
bool bNeedTranslation = false;
xub_StrLen nLen = rName.Len();
// Remove trailing whitespaces
xub_StrLen i = nLen;
while ( i && (rName.GetChar( i-1 ) < 32) )
i--;
if ( i != nLen )
rName.Erase( i );
// Remove Script at the end
// Scriptname must be the last part of the fontname and
// looks like "fontname (scriptname)". So there can only be a
// script name at the and of the fontname, when the last char is ')'
if ( (nLen >= 3) && rName.GetChar( nLen-1 ) == ')' )
{
int nOpen = 1;
xub_StrLen nTempLen = nLen-2;
while ( nTempLen )
{
if ( rName.GetChar( nTempLen ) == '(' )
{
nOpen--;
if ( !nOpen )
{
// Remove Space at the end
if ( nTempLen && (rName.GetChar( nTempLen-1 ) == ' ') )
nTempLen--;
rName.Erase( nTempLen );
nLen = nTempLen;
break;
}
}
if ( rName.GetChar( nTempLen ) == ')' )
nOpen++;
nTempLen--;
}
}
// remove all whitespaces and converts to lower case ASCII
// TODO: better transliteration to ASCII e.g. all digits
i = 0;
while ( i < nLen )
{
sal_Unicode c = rName.GetChar( i );
if ( c > 127 )
{
// Translate to Lowercase-ASCII
// FullWidth-ASCII to half ASCII
if ( (c >= 0xFF00) && (c <= 0xFF5E) )
{
c -= 0xFF00-0x0020;
// Upper to Lower
if ( (c >= 'A') && (c <= 'Z') )
c += 'a' - 'A';
rName.SetChar( i, c );
}
else
{
// Only Fontnames with None-Ascii-Characters must be translated
bNeedTranslation = true;
}
}
// not lowercase Ascii
else if ( !((c >= 'a') && (c <= 'z')) )
{
// To Lowercase-Ascii
if ( (c >= 'A') && (c <= 'Z') )
{
c += 'a' - 'A';
rName.SetChar( i, c );
}
else if( ((c < '0') || (c > '9')) && (c != ';') ) // not 0-9 or semicolon
{
// Remove white spaces and special characters
rName.Erase( i, 1 );
nLen--;
continue;
}
}
i++;
}
#ifdef AVS
// translate normalized localized name to its normalized English ASCII name
if( bNeedTranslation )
{
typedef std::hash_map<const String, const char*,FontNameHash> FontNameDictionary;
static FontNameDictionary aDictionary( sizeof(aImplLocalizedNamesList) / sizeof(*aImplLocalizedNamesList) );
// the font name dictionary needs to be intialized once
if( aDictionary.empty() )
{
// TODO: check if all dictionary entries are already normalized?
const ImplLocalizedFontName* pList = aImplLocalizedNamesList;
for(; pList->mpEnglishName; ++pList )
aDictionary[ pList->mpLocalizedNames ] = pList->mpEnglishName;
}
FontNameDictionary::const_iterator it = aDictionary.find( rName );
if( it != aDictionary.end() )
rName.AssignAscii( it->second );
}
#endif
}
}//SVMCore
// -----------------------------------------------------------------------
ImplFontCache::ImplFontCache( bool bPrinter )
: mpFirstEntry( NULL ),
mnRef0Count( 0 ),
mbPrinter( bPrinter )
{}
// -----------------------------------------------------------------------
ImplFontCache::~ImplFontCache()
{
#ifdef AVS
FontInstanceList::iterator it = maFontInstanceList.begin();
for(; it != maFontInstanceList.end(); ++it )
{
ImplFontEntry* pEntry = (*it).second;
delete pEntry;
}
#endif
}
// -----------------------------------------------------------------------
void ImplFontCache::Release( ImplFontEntry* pEntry )
{
static const int FONTCACHE_MAX = 50;
// DBG_ASSERT( (pEntry->mnRefCount > 0), "ImplFontCache::Release() - font refcount underflow" );
if( --pEntry->mnRefCount > 0 )
return;
if( ++mnRef0Count < FONTCACHE_MAX )
return;
#ifdef AVS
// remove unused entries from font instance cache
FontInstanceList::iterator it_next = maFontInstanceList.begin();
while( it_next != maFontInstanceList.end() )
{
FontInstanceList::iterator it = it_next++;
ImplFontEntry* pFontEntry = (*it).second;
if( pFontEntry->mnRefCount > 0 )
continue;
maFontInstanceList.erase( it );
delete pFontEntry;
--mnRef0Count;
DBG_ASSERT( (mnRef0Count>=0), "ImplFontCache::Release() - refcount0 underflow" );
if( mpFirstEntry == pFontEntry )
mpFirstEntry = NULL;
}
#endif
//DBG_ASSERT( (mnRef0Count==0), "ImplFontCache::Release() - refcount0 mismatch" );
}
// -----------------------------------------------------------------------
ImplFontEntry* ImplFontCache::GetFontEntry( ImplDevFontList* pFontList,
const Font& rFont, const Size& rSize, float fExactHeight, ImplDirectFontSubstitution* pDevSpecific )
{
String aSearchName = rFont.GetName();
#ifdef QWE
// TODO: also add device specific name caching
if( !pDevSpecific )
{
// check if the requested font name is already known
// if it is already known get its normalized search name
FontNameList::const_iterator it_name = maFontNameList.find( aSearchName );
if( it_name != maFontNameList.end() )
if( !(*it_name).second.EqualsAscii( "hg", 0, 2) )
aSearchName = (*it_name).second;
}
#endif
// initialize internal font request object
ImplFontSelectData aFontSelData( rFont, aSearchName, rSize, fExactHeight );
return GetFontEntry( pFontList, aFontSelData, pDevSpecific );
}
// -----------------------------------------------------------------------
ImplFontEntry* ImplFontCache::GetFontEntry( ImplDevFontList* pFontList,
ImplFontSelectData& aFontSelData, ImplDirectFontSubstitution* pDevSpecific )
{
// check if a directly matching logical font instance is already cached,
// the most recently used font usually has a hit rate of >50%
ImplFontEntry *pEntry = NULL;
ImplDevFontListData* pFontFamily = NULL;
#ifdef AVS
IFSD_Equal aIFSD_Equal;
if( mpFirstEntry && aIFSD_Equal( aFontSelData, mpFirstEntry->maFontSelData ) )
pEntry = mpFirstEntry;
else
{
FontInstanceList::iterator it = maFontInstanceList.find( aFontSelData );
if( it != maFontInstanceList.end() )
pEntry = (*it).second;
}
if( !pEntry ) // no direct cache hit
{
#endif
int nCount = pFontList->Count();
// find the best matching logical font family and update font selector accordingly
pFontFamily = pFontList->ImplFindByFont( aFontSelData, mbPrinter, pDevSpecific );
//DBG_ASSERT( (pFontFamily != NULL), "ImplFontCache::Get() No logical font found!" );
if( pFontFamily )
aFontSelData.maSearchName = pFontFamily->GetSearchName();
#ifdef AVS
// check if an indirectly matching logical font instance is already cached
FontInstanceList::iterator it = maFontInstanceList.find( aFontSelData );
if( it != maFontInstanceList.end() )
{
// we have an indirect cache hit
pEntry = (*it).second;
// cache the requested and the selected font names
// => next time there is a good chance for a direct cache hit
// don't allow the cache to grow too big
// TODO: implement some fancy LRU caching?
if( maFontNameList.size() >= 4000 )
maFontNameList.clear();
// TODO: also add device specific name caching
if( !pDevSpecific )
if( aFontSelData.maName != aFontSelData.maSearchName )
maFontNameList[ aFontSelData.maName ] = aFontSelData.maSearchName;
}
}
if( pEntry ) // cache hit => use existing font instance
{
// increase the font instance's reference count
if( !pEntry->mnRefCount++ )
--mnRef0Count;
}
else // no cache hit => create a new font instance
{
#endif
// find the best matching physical font face
ImplFontData* pFontData = pFontFamily->FindBestFontFace( aFontSelData );
aFontSelData.mpFontData = pFontData;
// create a new logical font instance from this physical font face
pEntry = pFontData->CreateFontInstance( aFontSelData );
#ifdef QWE
// if we found a different symbol font we need a symbol conversion table
if( pFontData->IsSymbolFont() )
if( aFontSelData.maTargetName != aFontSelData.maSearchName )
pEntry->mpConversion = ImplGetRecodeData( aFontSelData.maTargetName, aFontSelData.maSearchName );
// add the new entry to the cache
maFontInstanceList[ aFontSelData ] = pEntry;
}
#endif
mpFirstEntry = pEntry;
return pEntry;
}
struct FontMatchStatus
{
public:
int mnFaceMatch;
int mnHeightMatch;
int mnWidthMatch;
const xub_Unicode* mpTargetStyleName;
};
// -----------------------------------------------------------------------
ImplDevFontListData* ImplDevFontList::ImplFindByFont( ImplFontSelectData& rFSD,
bool bPrinter, ImplDirectFontSubstitution* pDevSpecific ) const
{
// give up if no fonts are available
if( !Count() )
return NULL;
// test if a font in the token list is available
// substitute the font if this was requested
USHORT nSubstFlags = FONT_SUBSTITUTE_ALWAYS;
if ( bPrinter )
nSubstFlags |= FONT_SUBSTITUTE_SCREENONLY;
bool bMultiToken = false;
xub_StrLen nTokenPos = 0;
String& aSearchName = rFSD.maSearchName; // TODO: get rid of reference
for(;;)
{
rFSD.maTargetName = GetNextFontToken( rFSD.maName, nTokenPos );
aSearchName = rFSD.maTargetName;
#ifdef AVS
ImplGetEnglishSearchFontName( aSearchName );
ImplFontSubstitute( aSearchName, nSubstFlags, pDevSpecific );
// #114999# special emboldening for Ricoh fonts
// TODO: smarter check for special cases by using PreMatch infrastructure?
if( (rFSD.meWeight > WEIGHT_MEDIUM)
&& aSearchName.EqualsAscii( "hg", 0, 2) )
{
String aBoldName;
if( aSearchName.EqualsAscii( "hggothicb", 0, 9) )
aBoldName = String(RTL_CONSTASCII_USTRINGPARAM("hggothice"));
else if( aSearchName.EqualsAscii( "hgpgothicb", 0, 10) )
aBoldName = String(RTL_CONSTASCII_USTRINGPARAM("hgpgothice"));
else if( aSearchName.EqualsAscii( "hgminchol", 0, 9) )
aBoldName = String(RTL_CONSTASCII_USTRINGPARAM("hgminchob"));
else if( aSearchName.EqualsAscii( "hgpminchol", 0, 10) )
aBoldName = String(RTL_CONSTASCII_USTRINGPARAM("hgpminchob"));
else if( aSearchName.EqualsAscii( "hgminchob" ) )
aBoldName = String(RTL_CONSTASCII_USTRINGPARAM("hgminchoe"));
else if( aSearchName.EqualsAscii( "hgpminchob" ) )
aBoldName = String(RTL_CONSTASCII_USTRINGPARAM("hgpminchoe"));
if( aBoldName.Len() && ImplFindBySearchName( aBoldName ) )
{
// the other font is available => use it
aSearchName = aBoldName;
// prevent synthetic emboldening of bold version
rFSD.meWeight = WEIGHT_DONTKNOW;
}
}
#endif
// check if the current font name token or its substitute is valid
ImplDevFontListData* pFoundData = ImplFindBySearchName( aSearchName );
if( pFoundData )
return pFoundData;
#ifdef AVS
// some systems provide special customization
// e.g. they suggest "serif" as UI-font, but this name cannot be used directly
// because the system wants to map it to another font first, e.g. "Helvetica"
if( mpPreMatchHook )
{
if( mpPreMatchHook->FindFontSubstitute( rFSD ) )
{
ImplGetEnglishSearchFontName( aSearchName );
pFoundData = ImplFindBySearchName( aSearchName );
if( pFoundData )
return pFoundData;
}
}
#endif
// break after last font name token was checked unsuccessfully
if( nTokenPos == STRING_NOTFOUND)
break;
bMultiToken = true;
}
#ifdef AVS
// if the first font was not available find the next available font in
// the semicolon separated list of font names. A font is also considered
// available when there is a matching entry in the Tools->Options->Fonts
// dialog witho neither ALWAYS nor SCREENONLY flags set and the substitution
// font is available
for( nTokenPos = 0; nTokenPos != STRING_NOTFOUND; )
{
if( bMultiToken )
{
rFSD.maTargetName = GetNextFontToken( rFSD.maName, nTokenPos );
aSearchName = rFSD.maTargetName;
ImplGetEnglishSearchFontName( aSearchName );
}
else
nTokenPos = STRING_NOTFOUND;
if( mpPreMatchHook )
if( mpPreMatchHook->FindFontSubstitute( rFSD ) )
ImplGetEnglishSearchFontName( aSearchName );
ImplFontSubstitute( aSearchName, nSubstFlags, pDevSpecific );
ImplDevFontListData* pFoundData = ImplFindBySearchName( aSearchName );
if( pFoundData )
return pFoundData;
}
// if no font with a directly matching name is available use the
// first font name token and get its attributes to find a replacement
if ( bMultiToken )
{
nTokenPos = 0;
rFSD.maTargetName = GetNextFontToken( rFSD.maName, nTokenPos );
aSearchName = rFSD.maTargetName;
ImplGetEnglishSearchFontName( aSearchName );
}
String aSearchShortName;
String aSearchFamilyName;
FontWeight eSearchWeight = rFSD.meWeight;
FontWidth eSearchWidth = rFSD.meWidthType;
ULONG nSearchType = 0;
FontSubstConfiguration::getMapName( aSearchName, aSearchShortName, aSearchFamilyName,
eSearchWeight, eSearchWidth, nSearchType );
// note: the search name was already translated to english (if possible)
// use the font's shortened name if needed
if ( aSearchShortName != aSearchName )
{
ImplDevFontListData* pFoundData = ImplFindBySearchName( aSearchShortName );
if( pFoundData )
{
#ifdef UNX
/* #96738# don't use mincho as an replacement for "MS Mincho" on X11: Mincho is
a korean bitmap font that is not suitable here. Use the font replacement table,
that automatically leads to the desired "HG Mincho Light J". Same story for
MS Gothic, there are thai and korean "Gothic" fonts, so we even prefer Andale */
static String aMS_Mincho( RTL_CONSTASCII_USTRINGPARAM("msmincho") );
static String aMS_Gothic( RTL_CONSTASCII_USTRINGPARAM("msgothic") );
if ((aSearchName != aMS_Mincho) && (aSearchName != aMS_Gothic))
// TODO: add heuristic to only throw out the fake ms* fonts
#endif
{
return pFoundData;
}
}
}
// use font fallback
const FontNameAttr* pFontAttr = NULL;
if( aSearchName.Len() )
{
// get fallback info using FontSubstConfiguration and
// the target name, it's shortened name and family name in that order
const FontSubstConfiguration& rFontSubst = *FontSubstConfiguration::get();
pFontAttr = rFontSubst.getSubstInfo( aSearchName );
if ( !pFontAttr && (aSearchShortName != aSearchName) )
pFontAttr = rFontSubst.getSubstInfo( aSearchShortName );
if ( !pFontAttr && (aSearchFamilyName != aSearchShortName) )
pFontAttr = rFontSubst.getSubstInfo( aSearchFamilyName );
// try the font substitutions suggested by the fallback info
if( pFontAttr )
{
ImplDevFontListData* pFoundData = ImplFindBySubstFontAttr( *pFontAttr );
if( pFoundData )
return pFoundData;
}
}
// if a target symbol font is not available use a default symbol font
if( rFSD.IsSymbolFont() )
{
com::sun::star::lang::Locale aDefaultLocale( OUString( RTL_CONSTASCII_USTRINGPARAM("en") ), OUString(), OUString() );
aSearchName = DefaultFontConfiguration::get()->getDefaultFont( aDefaultLocale, DEFAULTFONT_SYMBOL );
ImplDevFontListData* pFoundData = ImplFindByTokenNames( aSearchName );
if( pFoundData )
return pFoundData;
}
// now try the other font name tokens
while( nTokenPos != STRING_NOTFOUND )
{
rFSD.maTargetName = GetNextFontToken( rFSD.maName, nTokenPos );
if( !rFSD.maTargetName.Len() )
continue;
aSearchName = rFSD.maTargetName;
ImplGetEnglishSearchFontName( aSearchName );
String aTempShortName;
String aTempFamilyName;
ULONG nTempType = 0;
FontWeight eTempWeight = rFSD.meWeight;
FontWidth eTempWidth = WIDTH_DONTKNOW;
FontSubstConfiguration::getMapName( aSearchName, aTempShortName, aTempFamilyName,
eTempWeight, eTempWidth, nTempType );
// use a shortend token name if available
if( aTempShortName != aSearchName )
{
ImplDevFontListData* pFoundData = ImplFindBySearchName( aTempShortName );
if( pFoundData )
return pFoundData;
}
// use a font name from font fallback list to determine font attributes
// get fallback info using FontSubstConfiguration and
// the target name, it's shortened name and family name in that order
const FontSubstConfiguration& rFontSubst = *FontSubstConfiguration::get();
const FontNameAttr* pTempFontAttr = rFontSubst.getSubstInfo( aSearchName );
if ( !pTempFontAttr && (aTempShortName != aSearchName) )
pTempFontAttr = rFontSubst.getSubstInfo( aTempShortName );
if ( !pTempFontAttr && (aTempFamilyName != aTempShortName) )
pTempFontAttr = rFontSubst.getSubstInfo( aTempFamilyName );
// try the font substitutions suggested by the fallback info
if( pTempFontAttr )
{
ImplDevFontListData* pFoundData = ImplFindBySubstFontAttr( *pTempFontAttr );
if( pFoundData )
return pFoundData;
if( !pFontAttr )
pFontAttr = pTempFontAttr;
}
}
// if still needed use the alias names of the installed fonts
if( mbMapNames )
{
ImplDevFontListData* pFoundData = ImplFindByAliasName( rFSD.maTargetName, aSearchShortName );
if( pFoundData )
return pFoundData;
}
// if still needed use the font request's attributes to find a good match
switch( rFSD.meLanguage )
{
case LANGUAGE_CHINESE:
case LANGUAGE_CHINESE_SIMPLIFIED:
case LANGUAGE_CHINESE_SINGAPORE:
nSearchType |= IMPL_FONT_ATTR_CJK | IMPL_FONT_ATTR_CJK_SC;
break;
case LANGUAGE_CHINESE_TRADITIONAL:
case LANGUAGE_CHINESE_HONGKONG:
case LANGUAGE_CHINESE_MACAU:
nSearchType |= IMPL_FONT_ATTR_CJK | IMPL_FONT_ATTR_CJK_TC;
break;
case LANGUAGE_KOREAN:
case LANGUAGE_KOREAN_JOHAB:
nSearchType |= IMPL_FONT_ATTR_CJK | IMPL_FONT_ATTR_CJK_KR;
break;
case LANGUAGE_JAPANESE:
nSearchType |= IMPL_FONT_ATTR_CJK | IMPL_FONT_ATTR_CJK_JP;
break;
default:
nSearchType |= ImplIsCJKFont( rFSD.maName );
if( rFSD.IsSymbolFont() )
nSearchType |= IMPL_FONT_ATTR_SYMBOL;
break;
}
ImplCalcType( nSearchType, eSearchWeight, eSearchWidth, rFSD.meFamily, pFontAttr );
ImplDevFontListData* pFoundData = ImplFindByAttributes( nSearchType,
eSearchWeight, eSearchWidth, rFSD.meFamily, rFSD.meItalic, aSearchFamilyName );
if( pFoundData )
{
// overwrite font selection attributes using info from the typeface flags
if( (eSearchWeight >= WEIGHT_BOLD)
&& (eSearchWeight > rFSD.meWeight)
&& (pFoundData->mnTypeFaces & IMPL_DEVFONT_BOLD) )
rFSD.meWeight = eSearchWeight;
else if( (eSearchWeight < WEIGHT_NORMAL)
&& (eSearchWeight < rFSD.meWeight)
&& (eSearchWeight != WEIGHT_DONTKNOW)
&& (pFoundData->mnTypeFaces & IMPL_DEVFONT_LIGHT) )
rFSD.meWeight = eSearchWeight;
if( (nSearchType & IMPL_FONT_ATTR_ITALIC)
&& ((rFSD.meItalic == ITALIC_DONTKNOW) || (rFSD.meItalic == ITALIC_NONE))
&& (pFoundData->mnTypeFaces & IMPL_DEVFONT_ITALIC) )
rFSD.meItalic = ITALIC_NORMAL;
}
else
{
// if still needed fall back to default fonts
pFoundData = FindDefaultFont();
}
return pFoundData;
#endif
return FindDefaultFont();
}
// -----------------------------------------------------------------------
ImplDevFontListData* ImplDevFontList::FindDefaultFont() const
{
#ifdef AVS
// try to find one of the default fonts of the
// UNICODE, SANSSERIF, SERIF or FIXED default font lists
const DefaultFontConfiguration& rDefaults = *DefaultFontConfiguration::get();
com::sun::star::lang::Locale aLocale( OUString( RTL_CONSTASCII_USTRINGPARAM("en") ), OUString(), OUString() );
String aFontname = rDefaults.getDefaultFont( aLocale, DEFAULTFONT_SANS_UNICODE );
ImplDevFontListData* pFoundData = ImplFindByTokenNames( aFontname );
if( pFoundData )
return pFoundData;
aFontname = rDefaults.getDefaultFont( aLocale, DEFAULTFONT_SANS );
pFoundData = ImplFindByTokenNames( aFontname );
if( pFoundData )
return pFoundData;
aFontname = rDefaults.getDefaultFont( aLocale, DEFAULTFONT_SERIF );
pFoundData = ImplFindByTokenNames( aFontname );
if( pFoundData )
return pFoundData;
aFontname = rDefaults.getDefaultFont( aLocale, DEFAULTFONT_FIXED );
pFoundData = ImplFindByTokenNames( aFontname );
if( pFoundData )
return pFoundData;
// now try to find a reasonable non-symbol font
InitMatchData();
DevFontList::const_iterator it = maDevFontList.begin();
for(; it != maDevFontList.end(); ++it )
{
ImplDevFontListData* pData = (*it).second;
if( pData->mnMatchType & IMPL_FONT_ATTR_SYMBOL )
continue;
pFoundData = pData;
if( pData->mnMatchType & (IMPL_FONT_ATTR_DEFAULT|IMPL_FONT_ATTR_STANDARD) )
break;
}
if( pFoundData )
return pFoundData;
#endif
ImplDevFontListData* pFoundData = NULL;
DevFontList::const_iterator it = maDevFontList.find( String(L"Arial") );
if( it != maDevFontList.end() )
pFoundData = (*it).second;
if( NULL == pFoundData )
{
DevFontList::const_iterator it = maDevFontList.begin();
// finding any font is better than finding no font at all
it = maDevFontList.begin();
if( it != maDevFontList.end() )
pFoundData = (*it).second;
}
return pFoundData;
}
// find the font from the normalized font family name
ImplDevFontListData* ImplDevFontList::ImplFindBySearchName( const String& rSearchName ) const
{
#ifdef AVS
#ifdef DEBUG
String aTempName = rSearchName;
ImplGetEnglishSearchFontName( aTempName );
DBG_ASSERT( aTempName == rSearchName, "ImplDevFontList::ImplFindBySearchName() called with non-normalized name" );
#endif
#endif
DevFontList::const_iterator it = maDevFontList.find( rSearchName );
if( it == maDevFontList.end() )
return NULL;
ImplDevFontListData* pFoundData = (*it).second;
return pFoundData;
return NULL;
}
// =======================================================================
ImplFontData::ImplFontData( const ImplDevFontAttributes& rDFA, int nMagic )
: ImplDevFontAttributes( rDFA ),
mnWidth(0),
mnHeight(0),
mnMagic( nMagic ),
mpNext( NULL )
{
// StarSymbol is a unicode font, but it still deserves the symbol flag
if( !mbSymbolFlag )
if( 0 == GetFamilyName().CompareIgnoreCaseToAscii( "starsymbol", 10)
|| 0 == GetFamilyName().CompareIgnoreCaseToAscii( "opensymbol", 10) )
mbSymbolFlag = true;
}
bool ImplFontData::IsBetterMatch( const ImplFontSelectData& rFSD, FontMatchStatus& rStatus ) const
{
int nMatch = 0;
const String& rFontName = rFSD.maTargetName;
if( (rFontName == maName) || rFontName.EqualsIgnoreCaseAscii( maName ) )
nMatch += 240000;
if( rStatus.mpTargetStyleName
&& maStyleName.EqualsIgnoreCaseAscii( rStatus.mpTargetStyleName ) )
nMatch += 120000;
if( (rFSD.mePitch != PITCH_DONTKNOW) && (rFSD.mePitch == mePitch) )
nMatch += 20000;
// prefer NORMAL font width
// TODO: change when the upper layers can tell their width preference
if( meWidthType == WIDTH_NORMAL )
nMatch += 400;
else if( (meWidthType == WIDTH_SEMI_EXPANDED) || (meWidthType == WIDTH_SEMI_CONDENSED) )
nMatch += 300;
if( rFSD.meWeight != WEIGHT_DONTKNOW )
{
// if not bold prefer light fonts to bold fonts
int nReqWeight = (int)rFSD.meWeight;
if ( rFSD.meWeight > WEIGHT_MEDIUM )
nReqWeight += 100;
int nGivenWeight = (int)meWeight;
if( meWeight > WEIGHT_MEDIUM )
nGivenWeight += 100;
int nWeightDiff = nReqWeight - nGivenWeight;
if ( nWeightDiff == 0 )
nMatch += 1000;
else if ( nWeightDiff == +1 || nWeightDiff == -1 )
nMatch += 700;
else if ( nWeightDiff < +50 && nWeightDiff > -50)
nMatch += 200;
}
else // requested weight == WEIGHT_DONTKNOW
{
// prefer NORMAL font weight
// TODO: change when the upper layers can tell their weight preference
if( meWeight == WEIGHT_NORMAL )
nMatch += 450;
else if( meWeight == WEIGHT_MEDIUM )
nMatch += 350;
else if( (meWeight == WEIGHT_SEMILIGHT) || (meWeight == WEIGHT_SEMIBOLD) )
nMatch += 200;
else if( meWeight == WEIGHT_LIGHT )
nMatch += 150;
}
if ( rFSD.meItalic == ITALIC_NONE )
{
if( meItalic == ITALIC_NONE )
nMatch += 900;
}
else
{
if( rFSD.meItalic == meItalic )
nMatch += 900;
else if( meItalic != ITALIC_NONE )
nMatch += 600;
}
if( mbDevice )
nMatch += 1;
int nHeightMatch = 0;
int nWidthMatch = 0;
if( IsScalable() )
{
if( rFSD.mnOrientation != 0 )
nMatch += 80;
else if( rFSD.mnWidth != 0 )
nMatch += 25;
else
nMatch += 5;
}
else
{
if( rFSD.mnHeight == mnHeight )
{
nMatch += 20;
if( rFSD.mnWidth == mnWidth )
nMatch += 10;
}
else
{
// for non-scalable fonts the size difference is very important
// prefer the smaller font face because of clipping/overlapping issues
int nHeightDiff = (rFSD.mnHeight - mnHeight) * 1000;
nHeightMatch = (nHeightDiff >= 0) ? -nHeightDiff : 100+nHeightDiff;
if( rFSD.mnHeight )
nHeightMatch /= rFSD.mnHeight;
if( (rFSD.mnWidth != 0) && (mnWidth != 0) && (rFSD.mnWidth != mnWidth) )
{
int nWidthDiff = (rFSD.mnWidth - mnWidth) * 100;
nWidthMatch = (nWidthDiff >= 0) ? -nWidthDiff : +nWidthDiff;
}
}
}
if( rStatus.mnFaceMatch > nMatch )
return false;
else if( rStatus.mnFaceMatch < nMatch )
{
rStatus.mnFaceMatch = nMatch;
rStatus.mnHeightMatch = nHeightMatch;
rStatus.mnWidthMatch = nWidthMatch;
return true;
}
// when two fonts are still competing prefer the
// one with the best matching height
if( rStatus.mnHeightMatch > nHeightMatch )
return false;
else if( rStatus.mnHeightMatch < nHeightMatch )
{
rStatus.mnHeightMatch = nHeightMatch;
rStatus.mnWidthMatch = nWidthMatch;
return true;
}
if( rStatus.mnWidthMatch > nWidthMatch )
return false;
rStatus.mnWidthMatch = nWidthMatch;
return true;
}
// -----------------------------------------------------------------------
StringCompare ImplFontData::CompareIgnoreSize( const ImplFontData& rOther ) const
{
// compare their width, weight, italic and style name
if( meWidthType < rOther.meWidthType )
return COMPARE_LESS;
else if( meWidthType > rOther.meWidthType )
return COMPARE_GREATER;
if( meWeight < rOther.meWeight )
return COMPARE_LESS;
else if( meWeight > rOther.meWeight )
return COMPARE_GREATER;
if( meItalic < rOther.meItalic )
return COMPARE_LESS;
else if( meItalic > rOther.meItalic )
return COMPARE_GREATER;
StringCompare eCompare = maName.CompareTo( rOther.maName );
return eCompare;
}
// -----------------------------------------------------------------------
StringCompare ImplFontData::CompareWithSize( const ImplFontData& rOther ) const
{
StringCompare eCompare = CompareIgnoreSize( rOther );
if( eCompare != COMPARE_EQUAL )
return eCompare;
if( mnHeight < rOther.mnHeight )
return COMPARE_LESS;
else if( mnHeight > rOther.mnHeight )
return COMPARE_GREATER;
if( mnWidth < rOther.mnWidth )
return COMPARE_LESS;
else if( mnWidth > rOther.mnWidth )
return COMPARE_GREATER;
return COMPARE_EQUAL;
}
// =======================================================================
ImplDevFontListData::ImplDevFontListData( const String& rSearchName )
: mpFirst( NULL ),
maSearchName( rSearchName ),
mnTypeFaces( 0 ),
mnMatchType( 0 ),
meMatchWeight( WEIGHT_DONTKNOW ),
meMatchWidth( WIDTH_DONTKNOW ),
meFamily( FAMILY_DONTKNOW ),
mePitch( PITCH_DONTKNOW ),
mnMinQuality( -1 )
{}
// -----------------------------------------------------------------------
ImplDevFontListData::~ImplDevFontListData()
{
// release all physical font faces
while( mpFirst )
{
ImplFontData* pFace = mpFirst;
mpFirst = pFace->GetNextFace();
delete pFace;
}
}
// -----------------------------------------------------------------------
bool ImplDevFontListData::AddFontFace( ImplFontData* pNewData )
{
pNewData->mpNext = NULL;
if( !mpFirst )
{
maName = pNewData->maName;
maMapNames = pNewData->maMapNames;
meFamily = pNewData->meFamily;
mePitch = pNewData->mePitch;
mnMinQuality = pNewData->mnQuality;
}
else
{
if( meFamily == FAMILY_DONTKNOW )
meFamily = pNewData->meFamily;
if( mePitch == PITCH_DONTKNOW )
mePitch = pNewData->mePitch;
if( mnMinQuality > pNewData->mnQuality )
mnMinQuality = pNewData->mnQuality;
}
// set attributes for attribute based font matching
if( pNewData->IsScalable() )
mnTypeFaces |= IMPL_DEVFONT_SCALABLE;
if( pNewData->IsSymbolFont() )
mnTypeFaces |= IMPL_DEVFONT_SYMBOL;
else
mnTypeFaces |= IMPL_DEVFONT_NONESYMBOL;
if( pNewData->meWeight != WEIGHT_DONTKNOW )
{
if( pNewData->meWeight >= WEIGHT_SEMIBOLD )
mnTypeFaces |= IMPL_DEVFONT_BOLD;
else if( pNewData->meWeight <= WEIGHT_SEMILIGHT )
mnTypeFaces |= IMPL_DEVFONT_LIGHT;
else
mnTypeFaces |= IMPL_DEVFONT_NORMAL;
}
if( pNewData->meItalic == ITALIC_NONE )
mnTypeFaces |= IMPL_DEVFONT_NONEITALIC;
else if( (pNewData->meItalic == ITALIC_NORMAL)
|| (pNewData->meItalic == ITALIC_OBLIQUE) )
mnTypeFaces |= IMPL_DEVFONT_ITALIC;
if( (meMatchWeight == WEIGHT_DONTKNOW)
|| (meMatchWidth == WIDTH_DONTKNOW)
|| (mnMatchType == 0) )
{
// TODO: is it cheaper to calc matching attributes now or on demand?
// calc matching attributes if other entries are already initialized
// MT: Perform05: Do lazy, quite expensive, not needed in start-up!
// const FontSubstConfiguration& rFontSubst = *FontSubstConfiguration::get();
// InitMatchData( rFontSubst, maSearchName );
// mbMatchData=true; // Somewhere else???
}
// reassign name (sharing saves memory)
if( pNewData->maName == maName )
pNewData->maName = maName;
// insert new physical font face into linked list
// TODO: get rid of linear search?
ImplFontData* pData;
ImplFontData** ppHere = &mpFirst;
for(; (pData=*ppHere) != NULL; ppHere=&pData->mpNext )
{
StringCompare eComp = pNewData->CompareWithSize( *pData );
if( eComp == COMPARE_GREATER )
continue;
if( eComp == COMPARE_LESS )
break;
// ignore duplicate if its quality is worse
if( pNewData->mnQuality < pData->mnQuality )
return false;
// keep the device font if its quality is good enough
if( (pNewData->mnQuality == pData->mnQuality)
&& (pData->mbDevice || !pNewData->mbDevice) )
return false;
// replace existing font face with a better one
pNewData->mpNext = pData->mpNext;
*ppHere = pNewData;
delete pData;
return true;
}
// insert into or append to list of physical font faces
pNewData->mpNext = pData;
*ppHere = pNewData;
return true;
}
// -----------------------------------------------------------------------
ImplFontData* ImplDevFontListData::FindBestFontFace( const ImplFontSelectData& rFSD ) const
{
if( !mpFirst )
return NULL;
if( !mpFirst->GetNextFace() )
return mpFirst;
// FontName+StyleName should map to FamilyName+StyleName
const String& rSearchName = rFSD.maTargetName;
const xub_Unicode* pTargetStyleName = NULL;
if( (rSearchName.Len() > maSearchName.Len())
&& rSearchName.Equals( maSearchName, 0, maSearchName.Len() ) )
pTargetStyleName = rSearchName.GetBuffer() + maSearchName.Len() + 1;
// linear search, TODO: improve?
ImplFontData* pFontFace = mpFirst;
ImplFontData* pBestFontFace = pFontFace;
FontMatchStatus aFontMatchStatus = {0,0,0, pTargetStyleName};
for(; pFontFace; pFontFace = pFontFace->GetNextFace() )
if( pFontFace->IsBetterMatch( rFSD, aFontMatchStatus ) )
pBestFontFace = pFontFace;
return pBestFontFace;
}
// =======================================================================
ImplFontSelectData::ImplFontSelectData( const Font& rFont,
const String& rSearchName, const Size& rSize, float fExactHeight)
: maSearchName( rSearchName ),
mnWidth( rSize.Width() ),
mnHeight( rSize.Height() ),
mfExactHeight( fExactHeight),
mnOrientation( rFont.GetOrientation() ),
meLanguage( rFont.GetLanguage() ),
mbVertical( rFont.IsVertical() ),
mbNonAntialiased( false ),
mpFontData( NULL ),
mpFontEntry( NULL )
{
maTargetName = maName;
rFont.GetFontAttributes( *this );
// normalize orientation between 0 and 3600
if( 3600 <= (unsigned)mnOrientation )
{
if( mnOrientation >= 0 )
mnOrientation %= 3600;
else
mnOrientation = 3600 - (-mnOrientation % 3600);
}
// normalize width and height
if( mnHeight < 0 )
mnHeight = -mnHeight;
if( mnWidth < 0 )
mnWidth = -mnWidth;
}
// -----------------------------------------------------------------------
ImplFontSelectData::ImplFontSelectData( const ImplFontData& rFontData,
const Size& rSize, float fExactHeight, int nOrientation, bool bVertical )
: ImplFontAttributes( rFontData ),
mnWidth( rSize.Width() ),
mnHeight( rSize.Height() ),
mfExactHeight( fExactHeight ),
mnOrientation( nOrientation ),
meLanguage( 0 ),
mbVertical( bVertical ),
mbNonAntialiased( false ),
mpFontData( &rFontData ),
mpFontEntry( NULL )
{
maTargetName = maSearchName = maName;
// NOTE: no normalization for width/height/orientation
}