#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 #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; iDrawPixel( 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(pStr - pBase), static_cast(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(*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(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(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(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(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(maFont).SetSize( Size( nNewWidth, aSize.Height() ) ); mbMap = FALSE; mbNewFont = TRUE; ImplNewFont(); // recurse once using stretched width mbMap = TRUE; const_cast(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(aPosAry.mnDestX / nScaleX); aPosAry.mnDestX = 0; } aPosAry.mnSrcWidth = static_cast(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(aPosAry.mnDestY / nScaleY); aPosAry.mnDestY = 0; } aPosAry.mnSrcHeight = static_cast(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 äåëàòü íå îáÿçàòåëüíî 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(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(nBase), static_cast(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(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(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 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 }