/*************************************************************************
 *
 *  $RCSfile: outdev2.cxx,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: ka $ $Date: 2000-10-26 08:46:33 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#define _SV_OUTDEV2_CXX

#ifndef _SV_SVSYS_HXX
#include <svsys.h>
#endif
#ifndef _SV_SALBMP_HXX
#include <salbmp.hxx>
#endif
#ifndef _SV_SALGDI_HXX
#include <salgdi.hxx>
#endif
#ifndef _SV_IMPBMP_HXX
#include <impbmp.hxx>
#endif
#ifndef _DEBUG_HXX
#include <tools/debug.hxx>
#endif
#ifndef _SV_BITMAP_HXX
#include <bitmap.hxx>
#endif
#ifndef _SV_BITMAPEX_HXX
#include <bitmapex.hxx>
#endif
#ifndef _SV_WINDOW_HXX
#include <window.hxx>
#endif
#ifndef _SV_METAACT_HXX
#include <metaact.hxx>
#endif
#ifndef _SV_GDIMTF_HXX
#include <gdimtf.hxx>
#endif
#ifndef _SV_VIRDEV_HXX
#include <virdev.hxx>
#endif
#ifndef _SV_OUTDATA_HXX
#include <outdata.hxx>
#endif
#ifndef _SV_OUTDEV_H
#include <outdev.h>
#endif
#ifndef _SV_BMPACC_HXX
#include <bmpacc.hxx>
#endif
#ifndef _SV_REGION_H
#include <region.h>
#endif
#ifndef _SV_OUTDEV_HXX
#include <outdev.hxx>
#endif
#ifndef _SV_WINDOW_HXX
#include <window.hxx>
#endif
#ifdef REMOTE_APPSERVER
#include <rmoutdev.hxx>
#endif

#define BAND_MAX_SIZE 512000

// =======================================================================

DBG_NAMEEX( OutputDevice );

// =======================================================================

// -----------
// - Defines -
// -----------

#ifndef REMOTE_APPSERVER

#define OUTDEV_INIT()                       \
{                                           \
    if ( !IsDeviceOutputNecessary() )       \
        return;                             \
                                            \
    if ( !mpGraphics )                      \
        if ( !ImplGetGraphics() )           \
            return;                         \
                                            \
    if ( mbInitClipRegion )                 \
        ImplInitClipRegion();               \
                                            \
    if ( mbOutputClipped )                  \
        return;                             \
}

#else // !REMOTE_APPSERVER

#define OUTDEV_INIT()                                           \
{                                                               \
    if ( !IsDeviceOutputNecessary() )                           \
        return;                                                 \
    ImplServerGraphics* pGraphics = ImplGetServerGraphics();    \
    if ( !pGraphics )                                           \
        return;                                                 \
}

#endif // REMOTE_APPSERVER

#ifndef REMOTE_APPSERVER
#define TwoRect     SalTwoRect
#else
#define TwoRect     RemoteTwoRect
#endif

// -------------
// - externals -
// -------------

extern ULONG nVCLRLut[ 6 ];
extern ULONG nVCLGLut[ 6 ];
extern ULONG nVCLBLut[ 6 ];
extern ULONG nVCLDitherLut[ 256 ];
extern ULONG nVCLLut[ 256 ];

// =======================================================================

ULONG ImplAdjustTwoRect( TwoRect& 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;
}

// =======================================================================

void OutputDevice::ImplDrawOutDevDirect( const OutputDevice* pSrcDev, void* pVoidPosAry )
{
    TwoRect*            pPosAry = (TwoRect*)pVoidPosAry;
#ifndef REMOTE_APPSERVER
    SalGraphics*        pGraphics2;
#else
    ImplServerGraphics* pGraphics2;
#endif

    if ( pPosAry->mnSrcWidth && pPosAry->mnSrcHeight && pPosAry->mnDestWidth && pPosAry->mnDestHeight )
    {
        if ( this == pSrcDev )
            pGraphics2 = NULL;
        else
        {
            if ( (GetOutDevType() != pSrcDev->GetOutDevType()) ||
                 (GetOutDevType() != OUTDEV_WINDOW) )
            {
#ifndef REMOTE_APPSERVER
                if ( !pSrcDev->mpGraphics )
                {
                    if ( !((OutputDevice*)pSrcDev)->ImplGetGraphics() )
                        return;
                }
#endif
                pGraphics2 = pSrcDev->mpGraphics;
            }
            else
            {
                if ( ((Window*)this)->mpFrameWindow == ((Window*)pSrcDev)->mpFrameWindow )
                    pGraphics2 = NULL;
                else
                {
#ifndef REMOTE_APPSERVER
                    if ( !pSrcDev->mpGraphics )
                    {
                        if ( !((OutputDevice*)pSrcDev)->ImplGetGraphics() )
                            return;
                    }
#endif
                    pGraphics2 = pSrcDev->mpGraphics;

#ifndef REMOTE_APPSERVER
                    if ( !mpGraphics )
                    {
                        if ( !ImplGetGraphics() )
                            return;
                    }
                    DBG_ASSERT( mpGraphics && pSrcDev->mpGraphics,
                                "OutputDevice::DrawOutDev(): We need more than one Graphics" );
#endif
                }
            }
        }

        Rectangle   aSrcOutRect( Point( pSrcDev->mnOutOffX, pSrcDev->mnOutOffY ),
                                 Size( pSrcDev->mnOutWidth, pSrcDev->mnOutHeight ) );
        Rectangle   aSrcRect( Point( pPosAry->mnSrcX, pPosAry->mnSrcY ),
                              Size( pPosAry->mnSrcWidth, pPosAry->mnSrcHeight ) );
        const long  nOldRight = aSrcRect.Right();
        const long  nOldBottom = aSrcRect.Bottom();

        if ( !aSrcRect.Intersection( aSrcOutRect ).IsEmpty() )
        {
            if ( (pPosAry->mnSrcX+pPosAry->mnSrcWidth-1) > aSrcOutRect.Right() )
            {
                const long nOldWidth = pPosAry->mnSrcWidth;
                pPosAry->mnSrcWidth -= (nOldRight - aSrcRect.Right());
                pPosAry->mnDestWidth = pPosAry->mnDestWidth * pPosAry->mnSrcWidth / nOldWidth;
            }

            if ( (pPosAry->mnSrcY+pPosAry->mnSrcHeight-1) > aSrcOutRect.Bottom() )
            {
                const long nOldHeight = pPosAry->mnSrcHeight;
                pPosAry->mnSrcHeight -= (nOldBottom - aSrcRect.Bottom());
                pPosAry->mnDestHeight = pPosAry->mnDestHeight * pPosAry->mnSrcHeight / nOldHeight;
            }

            mpGraphics->CopyBits( pPosAry, pGraphics2 );
        }
    }
}

// ------------------------------------------------------------------

void OutputDevice::DrawOutDev( const Point& rDestPt, const Size& rDestSize,
                               const Point& rSrcPt,  const Size& rSrcSize )
{
    DBG_TRACE( "OutputDevice::DrawOutDev()" );
    DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
    DBG_ASSERT( meOutDevType != OUTDEV_PRINTER, "Don't use OutputDevice::DrawOutDev(...) with printer devices!" );

    if ( meOutDevType == OUTDEV_PRINTER )
        return;

    if ( ROP_INVERT == meRasterOp )
    {
        DrawRect( Rectangle( rDestPt, rDestSize ) );
        return;
    }

    if ( mpMetaFile )
    {
        const Bitmap aBmp( GetBitmap( rSrcPt, rSrcSize ) );
        mpMetaFile->AddAction( new MetaBmpScaleAction( rDestPt, rDestSize, aBmp ) );
    }

    OUTDEV_INIT();

    TwoRect aPosAry;
    aPosAry.mnSrcWidth   = ImplLogicWidthToDevicePixel( rSrcSize.Width() );
    aPosAry.mnSrcHeight  = ImplLogicHeightToDevicePixel( rSrcSize.Height() );
    aPosAry.mnDestWidth  = ImplLogicWidthToDevicePixel( rDestSize.Width() );
    aPosAry.mnDestHeight = ImplLogicHeightToDevicePixel( rDestSize.Height() );

    if ( aPosAry.mnSrcWidth && aPosAry.mnSrcHeight && aPosAry.mnDestWidth && aPosAry.mnDestHeight )
    {
        aPosAry.mnSrcX       = ImplLogicXToDevicePixel( rSrcPt.X() );
        aPosAry.mnSrcY       = ImplLogicYToDevicePixel( rSrcPt.Y() );
        aPosAry.mnDestX      = ImplLogicXToDevicePixel( rDestPt.X() );
        aPosAry.mnDestY      = ImplLogicYToDevicePixel( rDestPt.Y() );

        Rectangle   aSrcOutRect( Point( mnOutOffX, mnOutOffY ),
                                 Size( mnOutWidth, mnOutHeight ) );
        Rectangle   aSrcRect( Point( aPosAry.mnSrcX, aPosAry.mnSrcY ),
                              Size( aPosAry.mnSrcWidth, aPosAry.mnSrcHeight ) );
        long        nOldRight = aSrcRect.Right();
        long        nOldBottom = aSrcRect.Bottom();

        if ( !aSrcRect.Intersection( aSrcOutRect ).IsEmpty() )
        {
            if ( (aPosAry.mnSrcX+aPosAry.mnSrcWidth-1) > aSrcOutRect.Right() )
            {
                long nOldWidth = aPosAry.mnSrcWidth;
                aPosAry.mnSrcWidth -= nOldRight-aSrcRect.Right();
                aPosAry.mnDestWidth = aPosAry.mnDestWidth*aPosAry.mnSrcWidth/nOldWidth;
            }

            if ( (aPosAry.mnSrcY+aPosAry.mnSrcHeight-1) > aSrcOutRect.Bottom() )
            {
                long nOldHeight = aPosAry.mnSrcHeight;
                aPosAry.mnSrcHeight -= nOldBottom-aSrcRect.Bottom();
                aPosAry.mnDestHeight = aPosAry.mnDestHeight*aPosAry.mnSrcHeight/nOldHeight;
            }

            mpGraphics->CopyBits( &aPosAry, NULL );
        }
    }
}

// ------------------------------------------------------------------

void OutputDevice::DrawOutDev( const Point& rDestPt, const Size& rDestSize,
                               const Point& rSrcPt,  const Size& rSrcSize,
                               const OutputDevice& rOutDev )
{
    DBG_TRACE( "OutputDevice::DrawOutDev()" );
    DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
    DBG_CHKOBJ( &rOutDev, OutputDevice, ImplDbgCheckOutputDevice );
    DBG_ASSERT( meOutDevType != OUTDEV_PRINTER, "Don't use OutputDevice::DrawOutDev(...) with printer devices!" );
    DBG_ASSERT( rOutDev.meOutDevType != OUTDEV_PRINTER, "Don't use OutputDevice::DrawOutDev(...) with printer devices!" );

    if ( (meOutDevType == OUTDEV_PRINTER) || (rOutDev.meOutDevType == OUTDEV_PRINTER) )
        return;

    if ( ROP_INVERT == meRasterOp )
    {
        DrawRect( Rectangle( rDestPt, rDestSize ) );
        return;
    }

    if ( mpMetaFile )
    {
        const Bitmap aBmp( rOutDev.GetBitmap( rSrcPt, rSrcSize ) );
        mpMetaFile->AddAction( new MetaBmpScaleAction( rDestPt, rDestSize, aBmp ) );
    }

    OUTDEV_INIT();

    TwoRect aPosAry;
    aPosAry.mnSrcX       = rOutDev.ImplLogicXToDevicePixel( rSrcPt.X() );
    aPosAry.mnSrcY       = rOutDev.ImplLogicYToDevicePixel( rSrcPt.Y() );
    aPosAry.mnSrcWidth   = rOutDev.ImplLogicWidthToDevicePixel( rSrcSize.Width() );
    aPosAry.mnSrcHeight  = rOutDev.ImplLogicHeightToDevicePixel( rSrcSize.Height() );
    aPosAry.mnDestX      = ImplLogicXToDevicePixel( rDestPt.X() );
    aPosAry.mnDestY      = ImplLogicYToDevicePixel( rDestPt.Y() );
    aPosAry.mnDestWidth  = ImplLogicWidthToDevicePixel( rDestSize.Width() );
    aPosAry.mnDestHeight = ImplLogicHeightToDevicePixel( rDestSize.Height() );

    ImplDrawOutDevDirect( &rOutDev, &aPosAry );
}

// ------------------------------------------------------------------

void OutputDevice::CopyArea( const Point& rDestPt,
                             const Point& rSrcPt,  const Size& rSrcSize,
                             USHORT nFlags )
{
    DBG_TRACE( "OutputDevice::CopyArea()" );
    DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
    DBG_ASSERT( meOutDevType != OUTDEV_PRINTER, "Don't use OutputDevice::CopyArea(...) with printer devices!" );

    if ( meOutDevType == OUTDEV_PRINTER )
        return;

    RasterOp eOldRop = GetRasterOp();
    SetRasterOp( ROP_OVERPAINT );

    OUTDEV_INIT();

    TwoRect aPosAry;
    aPosAry.mnSrcWidth   = ImplLogicWidthToDevicePixel( rSrcSize.Width() );
    aPosAry.mnSrcHeight  = ImplLogicHeightToDevicePixel( rSrcSize.Height() );

    if ( aPosAry.mnSrcWidth && aPosAry.mnSrcHeight )
    {
        aPosAry.mnSrcX       = ImplLogicXToDevicePixel( rSrcPt.X() );
        aPosAry.mnSrcY       = ImplLogicYToDevicePixel( rSrcPt.Y() );
        aPosAry.mnDestX      = ImplLogicXToDevicePixel( rDestPt.X() );
        aPosAry.mnDestY      = ImplLogicYToDevicePixel( rDestPt.Y() );

        Rectangle   aSrcOutRect( Point( mnOutOffX, mnOutOffY ),
                                 Size( mnOutWidth, mnOutHeight ) );
        Rectangle   aSrcRect( Point( aPosAry.mnSrcX, aPosAry.mnSrcY ),
                              Size( aPosAry.mnSrcWidth, aPosAry.mnSrcHeight ) );
        long        nOldRight = aSrcRect.Right();
        long        nOldBottom = aSrcRect.Bottom();

        if ( !aSrcRect.Intersection( aSrcOutRect ).IsEmpty() )
        {
            if ( (aPosAry.mnSrcX+aPosAry.mnSrcWidth-1) > aSrcOutRect.Right() )
                aPosAry.mnSrcWidth -= nOldRight-aSrcRect.Right();

            if ( (aPosAry.mnSrcY+aPosAry.mnSrcHeight-1) > aSrcOutRect.Bottom() )
                aPosAry.mnSrcHeight -= nOldBottom-aSrcRect.Bottom();

            if ( (meOutDevType == OUTDEV_WINDOW) && (nFlags & COPYAREA_WINDOWINVALIDATE) )
            {
                ((Window*)this)->ImplMoveAllInvalidateRegions( aSrcRect,
                                                               aPosAry.mnDestX-aPosAry.mnSrcX,
                                                               aPosAry.mnDestY-aPosAry.mnSrcY,
                                                               FALSE );

#ifndef REMOTE_APPSERVER
                mpGraphics->CopyArea( aPosAry.mnDestX, aPosAry.mnDestY,
                                      aPosAry.mnSrcX, aPosAry.mnSrcY,
                                      aPosAry.mnSrcWidth, aPosAry.mnSrcHeight,
                                      SAL_COPYAREA_WINDOWINVALIDATE );
#else
                mpGraphics->CopyArea( aPosAry.mnDestX, aPosAry.mnDestY,
                                      aPosAry.mnSrcX, aPosAry.mnSrcY,
                                      aPosAry.mnSrcWidth, aPosAry.mnSrcHeight,
                                      COPYAREA_WINDOWINVALIDATE );
#endif
            }
            else
            {
                aPosAry.mnDestWidth  = aPosAry.mnSrcWidth;
                aPosAry.mnDestHeight = aPosAry.mnSrcHeight;
                mpGraphics->CopyBits( &aPosAry, NULL );
            }
        }
    }

    SetRasterOp( eOldRop );
}

// ------------------------------------------------------------------

void OutputDevice::ImplDrawFrameDev( const Point& rPt, const Point& rDevPt, const Size& rDevSize,
                                     const OutputDevice& rOutDev, const Region& rRegion )
{
    DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );

    GDIMetaFile*    pOldMetaFile = mpMetaFile;
    BOOL            bOldMap = mbMap;
    RasterOp        eOldROP = GetRasterOp();
    mpMetaFile = NULL;
    mbMap = FALSE;
    SetRasterOp( ROP_OVERPAINT );

#ifndef REMOTE_APPSERVER
    if ( !IsDeviceOutputNecessary() )
        return;

    if ( !mpGraphics )
    {
        if ( !ImplGetGraphics() )
            return;
    }
#else
    if ( !IsDeviceOutputNecessary() )
        return;

#endif

    // ClipRegion zuruecksetzen
#ifndef REMOTE_APPSERVER
    if ( rRegion.IsNull() )
        mpGraphics->ResetClipRegion();
    else
        ImplSelectClipRegion( mpGraphics, rRegion );
#else
    if ( rRegion.IsNull() )
        mpGraphics->SetClipRegion();
    else
        mpGraphics->SetClipRegion( rRegion );
#endif

    TwoRect aPosAry;
    aPosAry.mnSrcX       = rDevPt.X();
    aPosAry.mnSrcY       = rDevPt.Y();
    aPosAry.mnSrcWidth   = rDevSize.Width();
    aPosAry.mnSrcHeight  = rDevSize.Height();
    aPosAry.mnDestX      = rPt.X();
    aPosAry.mnDestY      = rPt.Y();
    aPosAry.mnDestWidth  = rDevSize.Width();
    aPosAry.mnDestHeight = rDevSize.Height();
    ImplDrawOutDevDirect( &rOutDev, &aPosAry );

    // Dafuer sorgen, das ClipRegion neu berechnet und gesetzt wird
    mbInitClipRegion = TRUE;

    SetRasterOp( eOldROP );
    mbMap = bOldMap;
    mpMetaFile = pOldMetaFile;
}

// ------------------------------------------------------------------

void OutputDevice::ImplGetFrameDev( const Point& rPt, const Point& rDevPt, const Size& rDevSize,
                                    OutputDevice& rDev )
{
    DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );

    BOOL bOldMap = mbMap;
    mbMap = FALSE;
    rDev.DrawOutDev( rDevPt, rDevSize, rPt, rDevSize, *this );
    mbMap = bOldMap;
}

// ------------------------------------------------------------------

void OutputDevice::DrawBitmap( const Point& rDestPt, const Bitmap& rBitmap )
{
    DBG_TRACE( "OutputDevice::DrawBitmap()" );
    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 )
{
    DBG_TRACE( "OutputDevice::DrawBitmap( Size )" );
    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 )
{
    DBG_TRACE( "OutputDevice::DrawBitmap( Point, Size )" );
    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 )
{
    DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );

    Bitmap aBmp( rBitmap );

    if ( ( mnDrawMode & DRAWMODE_NOBITMAP ) )
        return;
    else if ( ROP_INVERT == meRasterOp )
    {
        DrawRect( Rectangle( rDestPt, rDestSize ) );
        return;
    }
    else if ( mnDrawMode & ( DRAWMODE_BLACKBITMAP | DRAWMODE_WHITEBITMAP |
                             DRAWMODE_GRAYBITMAP | DRAWMODE_GHOSTEDBITMAP ) )
    {
        if ( mnDrawMode & ( DRAWMODE_BLACKBITMAP | DRAWMODE_WHITEBITMAP ) )
        {
            BYTE cCmpVal;

            if ( mnDrawMode & DRAWMODE_BLACKBITMAP )
                cCmpVal = ( mnDrawMode & DRAWMODE_GHOSTEDBITMAP ) ? 0x80 : 0;
            else
                cCmpVal = 255;

            Color aCol( cCmpVal, cCmpVal, cCmpVal );
            Push( PUSH_LINECOLOR | PUSH_FILLCOLOR );
            SetLineColor( aCol );
            SetFillColor( aCol );
            DrawRect( Rectangle( rDestPt, rDestSize ) );
            Pop();
            return;
        }
        else if( !!aBmp )
        {
            if ( mnDrawMode & DRAWMODE_GRAYBITMAP )
                aBmp.Convert( BMP_CONVERSION_8BIT_GREYS );

            if ( mnDrawMode & DRAWMODE_GHOSTEDBITMAP )
                aBmp.Convert( BMP_CONVERSION_GHOSTED );
        }
    }

    if ( mpMetaFile )
    {
        switch( nAction )
        {
            case( META_BMP_ACTION ):
                mpMetaFile->AddAction( new MetaBmpAction( rDestPt, aBmp ) );
            break;

            case( META_BMPSCALE_ACTION ):
                mpMetaFile->AddAction( new MetaBmpScaleAction( rDestPt, rDestSize, aBmp ) );
            break;

            case( META_BMPSCALEPART_ACTION ):
                mpMetaFile->AddAction( new MetaBmpScalePartAction(
                    rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel, aBmp ) );
            break;
        }
    }

    OUTDEV_INIT();

    if( ( OUTDEV_PRINTER == meOutDevType ) && mbClipRegion && ( REGION_COMPLEX == maRegion.GetType() ) )
    {
        Bitmap aMask;
        ImplPrintTransparent( aBmp, aMask, rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel );
        return;
    }

    if ( !( !aBmp ) )
    {
        TwoRect 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 );

#ifndef REMOTE_APPSERVER
            mpGraphics->DrawBitmap( &aPosAry, *aBmp.ImplGetImpBitmap()->ImplGetSalBitmap() );
#else
            aBmp.ImplDrawRemote( this,
                        Point( aPosAry.mnSrcX, aPosAry.mnSrcY ),
                        Size( aPosAry.mnSrcWidth, aPosAry.mnSrcHeight ),
                        Point( aPosAry.mnDestX, aPosAry.mnDestY ),
                        Size( aPosAry.mnDestWidth, aPosAry.mnDestHeight ) );
#endif
        }
    }
}

// ------------------------------------------------------------------

void OutputDevice::DrawBitmapEx( const Point& rDestPt,
                                 const BitmapEx& rBitmapEx )
{
    DBG_TRACE( "OutputDevice::DrawBitmapEx()" );

    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 )
{
    DBG_TRACE( "OutputDevice::DrawBitmapEx( Size )" );

    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 )
{
    DBG_TRACE( "OutputDevice::DrawBitmapEx( Point, Size )" );

    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 )
{
    DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );

    BitmapEx aBmpEx( rBitmapEx );

    if ( mnDrawMode & DRAWMODE_NOBITMAP )
        return;
    else if ( ROP_INVERT == meRasterOp )
    {
        DrawRect( Rectangle( rDestPt, rDestSize ) );
        return;
    }
    else if ( mnDrawMode & ( DRAWMODE_BLACKBITMAP | DRAWMODE_WHITEBITMAP |
                             DRAWMODE_GRAYBITMAP | DRAWMODE_GHOSTEDBITMAP ) )
    {
        if ( mnDrawMode & ( DRAWMODE_BLACKBITMAP | DRAWMODE_WHITEBITMAP ) )
        {
            Bitmap  aColorBmp( aBmpEx.GetSizePixel(), ( mnDrawMode & DRAWMODE_GHOSTEDBITMAP ) ? 4 : 1 );
            BYTE    cCmpVal;

            if ( mnDrawMode & DRAWMODE_BLACKBITMAP )
                cCmpVal = ( mnDrawMode & DRAWMODE_GHOSTEDBITMAP ) ? 0x80 : 0;
            else
                cCmpVal = 255;

            aColorBmp.Erase( Color( cCmpVal, cCmpVal, cCmpVal ) );

            if( aBmpEx.IsAlpha() )
                aBmpEx = BitmapEx( aColorBmp, aBmpEx.GetAlpha() );
            else
                aBmpEx = BitmapEx( aColorBmp, aBmpEx.GetMask() );
        }
        else if( !!aBmpEx )
        {
            if ( mnDrawMode & DRAWMODE_GRAYBITMAP )
                aBmpEx.Convert( BMP_CONVERSION_8BIT_GREYS );

            if ( mnDrawMode & DRAWMODE_GHOSTEDBITMAP )
                aBmpEx.Convert( BMP_CONVERSION_GHOSTED );
        }
    }

    if ( mpMetaFile )
    {
        switch( nAction )
        {
            case( META_BMPEX_ACTION ):
                mpMetaFile->AddAction( new MetaBmpExAction( rDestPt, aBmpEx ) );
            break;

            case( META_BMPEXSCALE_ACTION ):
                mpMetaFile->AddAction( new MetaBmpExScaleAction( rDestPt, rDestSize, aBmpEx ) );
            break;

            case( META_BMPEXSCALEPART_ACTION ):
                mpMetaFile->AddAction( new MetaBmpExScalePartAction( rDestPt, rDestSize,
                                                                     rSrcPtPixel, rSrcSizePixel, aBmpEx ) );
            break;
        }
    }

    OUTDEV_INIT();

    if( OUTDEV_PRINTER == meOutDevType )
    {
        Bitmap aBmp( aBmpEx.GetBitmap() ), aMask( aBmpEx.GetMask() );
        aBmp.Replace( aMask, Color( COL_WHITE ) );
        ImplPrintTransparent( aBmp, aMask, rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel );
        return;
    }
#ifndef REMOTE_APPSERVER
    else if( rBitmapEx.IsAlpha() )
    {
        ImplDrawAlpha( aBmpEx.GetBitmap(), aBmpEx.GetAlpha(), rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel );
        return;
    }
#endif

    if( !( !aBmpEx ) )
    {
        TwoRect 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 )
        {
#ifndef REMOTE_APPSERVER

            if( nMirrFlags )
                aBmpEx.Mirror( nMirrFlags );

            const ImpBitmap* pImpBmp = aBmpEx.ImplGetBitmapImpBitmap();
            const ImpBitmap* pMaskBmp = aBmpEx.ImplGetMaskImpBitmap();

            if ( pMaskBmp )
                mpGraphics->DrawBitmap( &aPosAry, *pImpBmp->ImplGetSalBitmap(), *pMaskBmp->ImplGetSalBitmap() );
            else
                mpGraphics->DrawBitmap( &aPosAry, *pImpBmp->ImplGetSalBitmap() );

#else

            if( nMirrFlags )
                aBmpEx.Mirror( nMirrFlags );

            aBmpEx.ImplDrawRemote( this,
                    Point( aPosAry.mnSrcX, aPosAry.mnSrcY ),
                    Size( aPosAry.mnSrcWidth, aPosAry.mnSrcHeight ),
                    Point( aPosAry.mnDestX, aPosAry.mnDestY ),
                    Size( aPosAry.mnDestWidth, aPosAry.mnDestHeight ) );

#endif
        }
    }
}

// ------------------------------------------------------------------

void OutputDevice::DrawMask( const Point& rDestPt,
                             const Bitmap& rBitmap, const Color& rMaskColor )
{
    DBG_TRACE( "OutputDevice::DrawMask()" );
    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 )
{
    DBG_TRACE( "OutputDevice::DrawMask( Size )" );
    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 )
{
    DBG_TRACE( "OutputDevice::DrawMask( Point, Size )" );
    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 )
{
    DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );

    if( ROP_INVERT == meRasterOp )
    {
        DrawRect( Rectangle( rDestPt, rDestSize ) );
        return;
    }

    if ( mpMetaFile )
    {
        switch( nAction )
        {
            case( META_MASK_ACTION ):
                mpMetaFile->AddAction( new MetaMaskAction( rDestPt,
                    rBitmap, rMaskColor ) );
            break;

            case( META_MASKSCALE_ACTION ):
                mpMetaFile->AddAction( new MetaMaskScaleAction( rDestPt,
                    rDestSize, rBitmap, rMaskColor ) );
            break;

            case( META_MASKSCALEPART_ACTION ):
                mpMetaFile->AddAction( new MetaMaskScalePartAction( rDestPt, rDestSize,
                    rSrcPtPixel, rSrcSizePixel, rBitmap, rMaskColor ) );
            break;
        }
    }

    OUTDEV_INIT();

#ifndef REMOTE_APPSERVER
    if ( OUTDEV_PRINTER == meOutDevType )
    {
        ImplPrintMask( rBitmap, rMaskColor, rDestPt, rDestSize, rSrcPtPixel, rSrcSizePixel );
        return;
    }
#endif

    const ImpBitmap* pImpBmp = rBitmap.ImplGetImpBitmap();

    if ( pImpBmp )
    {
        TwoRect 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 )
        {
#ifndef REMOTE_APPSERVER

            if( nMirrFlags )
            {
                Bitmap aTmp( rBitmap );
                aTmp.Mirror( nMirrFlags );
                mpGraphics->DrawMask( &aPosAry, *aTmp.ImplGetImpBitmap()->ImplGetSalBitmap(),
                                      ImplColorToSal( rMaskColor ) );
            }
            else
                mpGraphics->DrawMask( &aPosAry, *pImpBmp->ImplGetSalBitmap(),
                                      ImplColorToSal( rMaskColor ) );

#else

            if( nMirrFlags )
            {
                Bitmap aTmp( rBitmap );
                aTmp.Mirror( nMirrFlags );
                aTmp.ImplDrawRemoteMask( this,
                    Point( aPosAry.mnSrcX, aPosAry.mnSrcY ),
                    Size( aPosAry.mnSrcWidth, aPosAry.mnSrcHeight ),
                    Point( aPosAry.mnDestX, aPosAry.mnDestY ),
                    Size( aPosAry.mnDestWidth, aPosAry.mnDestHeight ),
                    rMaskColor );
            }
            else
                rBitmap.ImplDrawRemoteMask( this,
                    Point( aPosAry.mnSrcX, aPosAry.mnSrcY ),
                    Size( aPosAry.mnSrcWidth, aPosAry.mnSrcHeight ),
                    Point( aPosAry.mnDestX, aPosAry.mnDestY ),
                    Size( aPosAry.mnDestWidth, aPosAry.mnDestHeight ),
                    rMaskColor );

#endif
        }
    }
}

// ------------------------------------------------------------------

Bitmap OutputDevice::GetBitmap( const Point& rSrcPt, const Size& rSize ) const
{
    DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );

    Bitmap  aBmp;
    long    nX = ImplLogicXToDevicePixel( rSrcPt.X() );
    long    nY = ImplLogicYToDevicePixel( rSrcPt.Y() );
    long    nWidth = ImplLogicWidthToDevicePixel( rSize.Width() );
    long    nHeight = ImplLogicHeightToDevicePixel( rSize.Height() );

#ifndef REMOTE_APPSERVER
    if ( mpGraphics || ( (OutputDevice*) this )->ImplGetGraphics() )
#endif
    {
        if ( nWidth && nHeight )
        {
#ifndef REMOTE_APPSERVER
            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
                VirtualDevice aVDev( *this );

                if ( aVDev.SetOutputSizePixel( aRect.GetSize() ) )
                {
                    if ( ((OutputDevice*)&aVDev)->mpGraphics || ((OutputDevice*)&aVDev)->ImplGetGraphics() )
                    {
                        TwoRect 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 );

                        aBmp = aVDev.GetBitmap( Point(), aVDev.GetOutputSizePixel() );
                     }
                     else
                        bClipped = FALSE;
                }
                else
                    bClipped = FALSE;
            }

            if ( !bClipped )
            {
                SalBitmap* pSalBmp = mpGraphics->GetBitmap( nX, nY, nWidth, nHeight );

                if( pSalBmp )
                {
                    ImpBitmap* pImpBmp = new ImpBitmap;
                    pImpBmp->ImplSetSalBitmap( pSalBmp );
                    aBmp.ImplSetImpBitmap( pImpBmp );
                }
            }
#else
            aBmp.ImplGetRemoteBmp( (OutputDevice*) this, Point( nX, nY ), Size( nWidth, nHeight ) );
#endif
        }
    }

    return aBmp;
}

// ------------------------------------------------------------------

void OutputDevice::ImplGetFrameBitmap( const Point& rDestPt, const Size& rSize,
                                       Bitmap& rBitmap ) const
{
    DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );

    BOOL bOldMap = mbMap;
    ((OutputDevice*)this)->mbMap = FALSE;
    rBitmap = GetBitmap( rDestPt, rSize );
    ((OutputDevice*)this)->mbMap = bOldMap;
}

// ------------------------------------------------------------------

Color OutputDevice::GetPixel( const Point& rPt ) const
{
    DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );

    Color aColor;

#ifndef REMOTE_APPSERVER
    if ( mpGraphics || ((OutputDevice*)this)->ImplGetGraphics() )
    {
        if ( mbInitClipRegion )
            ((OutputDevice*)this)->ImplInitClipRegion();

        if ( !mbOutputClipped )
        {
            const long      nX = ImplLogicXToDevicePixel( rPt.X() );
            const long      nY = ImplLogicYToDevicePixel( rPt.Y() );
            const SalColor  aSalCol = mpGraphics->GetPixel( nX, nY );
            aColor.SetRed( SALCOLOR_RED( aSalCol ) );
            aColor.SetGreen( SALCOLOR_GREEN( aSalCol ) );
            aColor.SetBlue( SALCOLOR_BLUE( aSalCol ) );
        }
    }
#else // REMOTE_APPSERVER
    ImplServerGraphics* pGraphics = ( (OutputDevice*) this )->ImplGetServerGraphics();
    if( pGraphics )
    {
        const long nX = ImplLogicXToDevicePixel( rPt.X() );
        const long nY = ImplLogicYToDevicePixel( rPt.Y() );
        aColor = pGraphics->GetPixel( Point( nX, nY ) );
    }
#endif // REMOTE_APPSERVER

    return aColor;
}

// ------------------------------------------------------------------

Color* OutputDevice::GetPixel( const Polygon& rPts ) const
{
    DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );

    Color*          pColors = NULL;
    const USHORT    nSize = rPts.GetSize();

    if( nSize )
    {
#ifndef REMOTE_APPSERVER
        if ( mpGraphics || ((OutputDevice*)this)->ImplGetGraphics() )
        {
            if ( mbInitClipRegion )
                ((OutputDevice*)this)->ImplInitClipRegion();

            if ( !mbOutputClipped )
            {
                pColors = new Color[ nSize ];

                for( USHORT i = 0; i < nSize; i++ )
                {
                    Color&          rCol = pColors[ i ];
                    const Point&    rPt = rPts[ i ];
                    const SalColor  aSalCol( mpGraphics->GetPixel( ImplLogicXToDevicePixel( rPt.X() ),
                                                                   ImplLogicYToDevicePixel( rPt.Y() ) ) );

                    rCol.SetRed( SALCOLOR_RED( aSalCol ) );
                    rCol.SetGreen( SALCOLOR_GREEN( aSalCol ) );
                    rCol.SetBlue( SALCOLOR_BLUE( aSalCol ) );
                }
            }
        }
#else // REMOTE_APPSERVER
        ImplServerGraphics* pGraphics = ( (OutputDevice*) this )->ImplGetServerGraphics();
        if( pGraphics )
        {
            pColors = pGraphics->GetPixel( ImplLogicToDevicePixel( rPts ) );
        }
#endif // REMOTE_APPSERVER
    }

    return pColors;
}

// -----------------------------------------------------------------------

void OutputDevice::DrawPixel( const Point& rPt )
{
    DBG_TRACE( "OutputDevice::DrawPixel()" );
    DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );

    if ( mpMetaFile )
        mpMetaFile->AddAction( new MetaPointAction( rPt ) );

    if ( !IsDeviceOutputNecessary() || !mbLineColor )
        return;

#ifndef REMOTE_APPSERVER
    Point aPt = ImplLogicToDevicePixel( rPt );

    // we need a graphics
    if ( !mpGraphics )
    {
        if ( !ImplGetGraphics() )
            return;
    }

    if ( mbInitClipRegion )
        ImplInitClipRegion();
    if ( mbOutputClipped )
        return;

    if ( mbInitLineColor )
        ImplInitLineColor();

    mpGraphics->DrawPixel( aPt.X(), aPt.Y() );
#else
    ImplServerGraphics* pGraphics = ImplGetServerGraphics();
    if ( pGraphics )
    {
        if ( mbInitLineColor )
            ImplInitLineColor();
        pGraphics->DrawPixel( ImplLogicToDevicePixel( rPt ) );
    }
#endif
}

// -----------------------------------------------------------------------

void OutputDevice::DrawPixel( const Point& rPt, const Color& rColor )
{
    DBG_TRACE( "OutputDevice::DrawPixel()" );
    DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );

    Color aColor( rColor );

    if( mnDrawMode & ( DRAWMODE_BLACKLINE | DRAWMODE_WHITELINE |
                       DRAWMODE_GRAYLINE | DRAWMODE_GHOSTEDLINE ) )
    {
        if( !ImplIsColorTransparent( aColor ) )
        {
            if( mnDrawMode & DRAWMODE_BLACKLINE )
            {
                aColor = Color( COL_BLACK );
            }
            else if( mnDrawMode & DRAWMODE_WHITELINE )
            {
                aColor = Color( COL_WHITE );
            }
            else if( mnDrawMode & DRAWMODE_GRAYLINE )
            {
                const UINT8 cLum = aColor.GetLuminance();
                aColor = Color( cLum, cLum, cLum );
            }

            if( mnDrawMode & DRAWMODE_GHOSTEDLINE )
            {
                aColor = Color( ( aColor.GetRed() >> 1 ) | 0x80,
                                ( aColor.GetGreen() >> 1 ) | 0x80,
                                ( aColor.GetBlue() >> 1 ) | 0x80 );
            }
        }
    }

    if ( mpMetaFile )
        mpMetaFile->AddAction( new MetaPixelAction( rPt, aColor ) );

    if ( !IsDeviceOutputNecessary() || ImplIsColorTransparent( aColor ) )
        return;

#ifndef REMOTE_APPSERVER
    Point aPt = ImplLogicToDevicePixel( rPt );

    // we need a graphics
    if ( !mpGraphics )
    {
        if ( !ImplGetGraphics() )
            return;
    }

    if ( mbInitClipRegion )
        ImplInitClipRegion();
    if ( mbOutputClipped )
        return;

    mpGraphics->DrawPixel( aPt.X(), aPt.Y(), ImplColorToSal( aColor ) );
#else
    ImplServerGraphics* pGraphics = ImplGetServerGraphics();
    if ( pGraphics )
        pGraphics->DrawPixel( ImplLogicToDevicePixel( rPt ), aColor );
#endif
}

// -----------------------------------------------------------------------

void OutputDevice::DrawPixel( const Polygon& rPts, const Color* pColors )
{
    if ( !pColors )
        DrawPixel( rPts, GetLineColor() );
    else
    {
        DBG_TRACE( "OutputDevice::DrawPixel()" );
        DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
        DBG_ASSERT( pColors, "OutputDevice::DrawPixel: No color array specified" );

        const USHORT nSize = rPts.GetSize();

        if ( nSize )
        {
            if ( mpMetaFile )
                for ( USHORT i = 0; i < nSize; i++ )
                    mpMetaFile->AddAction( new MetaPixelAction( rPts[ i ], pColors[ i ] ) );

            if ( !IsDeviceOutputNecessary() )
                return;

#ifndef REMOTE_APPSERVER
            // we need a graphics
            if ( mpGraphics || ImplGetGraphics() )
            {
                if ( mbInitClipRegion )
                    ImplInitClipRegion();

                if ( mbOutputClipped )
                    return;

                for ( USHORT i = 0; i < nSize; i++ )
                {
                    const Point aPt( ImplLogicToDevicePixel( rPts[ i ] ) );
                    mpGraphics->DrawPixel( aPt.X(), aPt.Y(), ImplColorToSal( pColors[ i ] ) );
                }
            }
#else
            ImplServerGraphics* pGraphics = ImplGetServerGraphics();
            if ( pGraphics )
            {
                pGraphics->DrawPixel( ImplLogicToDevicePixel( rPts ), pColors );
            }
#endif
        }
    }
}

// -----------------------------------------------------------------------

void OutputDevice::DrawPixel( const Polygon& rPts, const Color& rColor )
{
    if( rColor != COL_TRANSPARENT )
    {
        const USHORT    nSize = rPts.GetSize();
        Color*          pColArray = new Color[ nSize ];

        for( USHORT i = 0; i < nSize; i++ )
            pColArray[ i ] = rColor;

        DrawPixel( rPts, pColArray );
        delete[] pColArray;
    }
}

// ------------------------------------------------------------------------

void OutputDevice::ImplDrawAlpha( const Bitmap& rBmp, const AlphaMask& rAlpha,
                                  const Point& rDestPt, const Size& rDestSize,
                                  const Point& rSrcPtPixel, const Size& rSrcSizePixel )
{
    Point       aPt;
    Point       aOutPt( LogicToPixel( rDestPt ) );
    Size        aOutSz( LogicToPixel( rDestSize ) );
    Rectangle   aDstRect( aPt, GetOutputSizePixel() );
    const BOOL  bHMirr = aOutSz.Width() < 0, bVMirr = aOutSz.Height() < 0;

    if( OUTDEV_WINDOW == meOutDevType )
    {
        const Region aPaintRgn( ( (Window*) this )->GetPaintRegion() );

        if( !aPaintRgn.IsNull() )
            aDstRect.Intersection( LogicToPixel( aPaintRgn.GetBoundRect() ) );
    }

    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() )
    {
        Rectangle aBmpRect( aPt, rBmp.GetSizePixel() );

        if( !aBmpRect.Intersection( Rectangle( rSrcPtPixel, rSrcSizePixel ) ).IsEmpty() )
        {
            GDIMetaFile*    pOldMetaFile = mpMetaFile; mpMetaFile = NULL;
            const BOOL      bOldMap = mbMap; mbMap = FALSE;
            Bitmap          aBmp( GetBitmap( aDstRect.TopLeft(), aDstRect.GetSize() ) );
            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, nMirrOffX, nMirrOffY;
            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 ];
            }

            if( GetBitCount() <= 8 )
            {
                Bitmap              aDither( aBmp.GetSizePixel(), 8 );
                BitmapColor         aIndex( 0 );
                BitmapReadAccess*   pP = ( (Bitmap&) rBmp ).AcquireReadAccess();
                BitmapReadAccess*   pA = ( (AlphaMask&) rAlpha ).AcquireReadAccess();
                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 );
                        }
                    }
                }

                ( (Bitmap&) rBmp ).ReleaseAccess( pP );
                ( (AlphaMask&) rAlpha ).ReleaseAccess( pA );
                aBmp.ReleaseAccess( pB );
                aDither.ReleaseAccess( pW );
                DrawBitmap( aDstRect.TopLeft(), aDither );
            }
            else
            {
                BitmapReadAccess*   pP = ( (Bitmap&) rBmp ).AcquireReadAccess();
                BitmapReadAccess*   pA = ( (AlphaMask&) rAlpha ).AcquireReadAccess();
                BitmapWriteAccess*  pB = aBmp.AcquireWriteAccess();

                if( pP && pA && pB )
                {
                    if( pA->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL )
                    {
                        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;
                        }
                    }
                    else
                    {
                        for( nY = 0; nY < nDstHeight; nY++ )
                        {
                            const long nMapY = pMapY[ nY ];

                            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 ),
                                                                     (BYTE) pA->GetPixel( nMapY, nMapX ) ) );
                            }
                        }
                    }
                }

                ( (Bitmap&) rBmp ).ReleaseAccess( pP );
                ( (AlphaMask&) rAlpha ).ReleaseAccess( pA );
                aBmp.ReleaseAccess( pB );
                DrawBitmap( aDstRect.TopLeft(), aBmp );
            }

            delete[] pMapX;
            delete[] pMapY;
            mbMap = bOldMap;
            mpMetaFile = pOldMetaFile;
        }
    }
}

// ------------------------------------------------------------------------

static Pair* ImplGetMap( long nFromSize, long nToSize )
{
    DBG_ASSERT( nFromSize && nToSize, "ImplGetMap(): Invalid size!" );

    Pair*           pMap = new Pair[ nFromSize ];
    const double    fSize = (double) nToSize / nFromSize;
    double          fRealSum = 0.0;
    const long      nLastToPos = nToSize - 1L;
    long            nErrSum = 0L, nPos = 0L, nSize = 0L;

    for( long i = 0L; i < nFromSize; i++ )
    {
        nPos = nPos + nSize;
        nSize = Max( FRound( fSize - ( nErrSum - fRealSum ) ), 0L );

        nErrSum += nSize;
        fRealSum += fSize;

        pMap[ i ].A() = nPos = Min( nPos, nLastToPos );
        pMap[ i ].B() = Min( nPos + Max( nSize, 1L ) - 1L, nLastToPos );
    }

    return pMap;
}

// ------------------------------------------------------------------------

static BOOL ImplCreateBandBitmaps( BitmapReadAccess* pPAcc, BitmapReadAccess* pMAcc,
                                   long* pMapX, long* pMapY,
                                   long nDstWidth, long nDstY1, long nDstY2,
                                   Bitmap& rPaint, Bitmap& rMask )
{
    const Size  aSz( nDstWidth, nDstY2 - nDstY1 + 1 );
    BOOL        bRet = FALSE;

    rPaint = Bitmap( aSz, pPAcc->GetBitCount(), pPAcc->HasPalette() ? &pPAcc->GetPalette() : NULL );
    rMask = Bitmap( aSz, pMAcc->GetBitCount(), pMAcc->HasPalette() ? &pMAcc->GetPalette() : NULL );

    BitmapWriteAccess* pWPAcc = rPaint.AcquireWriteAccess();
    BitmapWriteAccess* pWMAcc = rMask.AcquireWriteAccess();

    if( pWPAcc && pWMAcc )
    {
        const long  nWidth = pWPAcc->Width();
        const long  nHeight = pWPAcc->Width();
        const long  nPScanSize = pWPAcc->GetScanlineSize();
        const long  nMScanSize = pWMAcc->GetScanlineSize();
        long        nY = 0, nScanY = nDstY1;

        while( nScanY <= nDstY2 )
        {
            const long nMapY = pMapY[ nScanY ];

            for( long nX = 0L; nX < nWidth; nX++ )
            {
                const long nMapX = pMapX[ nX ];
                pWPAcc->SetPixel( nY, nX, pPAcc->GetPixel( nMapY, nMapX ) );
                pWMAcc->SetPixel( nY, nX, pMAcc->GetPixel( nMapY, nMapX ) );
            }

            while( ( nScanY < nDstY2 ) && ( pMapY[ nScanY + 1 ] == nMapY ) )
            {
                HMEMCPY( pWPAcc->GetScanline( nY + 1L ), pWPAcc->GetScanline( nY ), nPScanSize );
                HMEMCPY( pWMAcc->GetScanline( nY + 1L ), pWMAcc->GetScanline( nY ), nMScanSize );
                nY++, nScanY++;
            }

            nY++, nScanY++;
        }

        bRet = TRUE;
    }

    if( pWPAcc )
        rPaint.ReleaseAccess( pWPAcc );

    if( pWMAcc )
        rMask.ReleaseAccess( pWMAcc );

    return bRet;
}

// ------------------------------------------------------------------------

void OutputDevice::ImplPrintTransparent( const Bitmap& rBmp, const Bitmap& rMask,
                                         const Point& rDestPt, const Size& rDestSize,
                                         const Point& rSrcPtPixel, const Size& rSrcSizePixel )
{
    Point       aPt;
    Point       aDestPt( LogicToPixel( rDestPt ) );
    Size        aDestSz( LogicToPixel( rDestSize ) );
    Rectangle   aSrcRect( rSrcPtPixel, rSrcSizePixel );

    aSrcRect.Justify();

    if( !!rBmp && aSrcRect.GetWidth() && aSrcRect.GetHeight() && aDestSz.Width() && aDestSz.Height() )
    {
        ULONG   nMirrFlags = 0UL;
        Bitmap  aPaint( rBmp );
        Bitmap  aMask( rMask );
        Region  aDstRgn;
        BOOL    bMask = !!aMask;

        if( bMask && ( aMask.GetBitCount() > 1 ) )
            aMask.Convert( BMP_CONVERSION_1BIT_THRESHOLD );

        // mirrored horizontically
        if( aDestSz.Width() < 0L )
        {
            aDestSz.Width() = -aDestSz.Width();
            aDestPt.X() -= ( aDestSz.Width() - 1L );
            nMirrFlags |= BMP_MIRROR_HORZ;
        }

        // mirrored vertically
        if( aDestSz.Height() < 0L )
        {
            aDestSz.Height() = -aDestSz.Height();
            aDestPt.Y() -= ( aDestSz.Height() - 1L );
            nMirrFlags |= BMP_MIRROR_VERT;
        }

        // source cropped?
        if( aSrcRect != Rectangle( aPt, aPaint.GetSizePixel() ) )
        {
            aPaint.Crop( aSrcRect );
            if( bMask )
                aMask.Crop( aSrcRect );
        }

        // destination mirrored
        if( nMirrFlags )
        {
            aPaint.Mirror( nMirrFlags );
            if( bMask )
                aMask.Mirror( nMirrFlags );
        }

        const Rectangle aDstRect( aDestPt, aDestSz );

        // create destination region
        if( mbClipRegion && !maRegion.IsEmpty() )
        {
            aDstRgn = maRegion;
            aDstRgn.Intersect( aDstRect );
        }
        else
            aDstRgn = aDstRect;

        aDstRgn.Move( -aDstRect.Left(), -aDstRect.Top() );

        // we always want to have a mask
        if( !bMask )
        {
            aMask = Bitmap( aSrcRect.GetSize(), 1 );
            aMask.Erase( Color( COL_BLACK ) );
        }

        BitmapReadAccess* pPAcc = aPaint.AcquireReadAccess();
        BitmapReadAccess* pMAcc = aMask.AcquireReadAccess();

        if( pPAcc && pMAcc )
        {
            const long      nWidth = aDestSz.Width();
            const long      nHeight = aDestSz.Height();
            const long      nWidth1 = nWidth - 1;
            const long      nHeight1 = nHeight - 1;
            const long      nOldWidth1 = aSrcRect.GetWidth() - 1;
            const long      nOldHeight1 = aSrcRect.GetHeight() - 1;
            const long      nScanByteCount = Max( nWidth * aPaint.GetBitCount() / 8L, 1L );
            const long      nBandHeight = BAND_MAX_SIZE / nScanByteCount + 1;
            long*           pMapX = new long[ nWidth ];
            long*           pMapY = new long[ nHeight ];
            long            nX, nY;
            long            nBandY1, nBandY2;
            GDIMetaFile*    pOldMetaFile = mpMetaFile;
            const BOOL      bOldMap = mbMap;

            mpMetaFile = NULL;
            Push( PUSH_CLIPREGION );
            SetClipRegion();
            mbMap = FALSE;

            // create mapping tables
            for( nX = 0L; nX < nWidth; nX++ )
                pMapX[ nX ] = nWidth1 ? ( nX * nOldWidth1 / nWidth1 ) : 0;

            for( nY = 0L; nY < nHeight; nY++ )
                pMapY[ nY ] = nHeight1 ? ( nY * nOldHeight1 / nHeight1 ) : 0;

            // process bands
            for( nBandY1 = 0, nBandY2 = nBandHeight; nBandY1 < nHeight; nBandY1 += nBandHeight, nBandY2 += nBandHeight )
            {
                Bitmap aWorkPaint, aWorkMask;

                // don't walk over bounds
                if( nBandY2 > nHeight1 )
                    nBandY2 = nHeight1;

                if( ImplCreateBandBitmaps( pPAcc, pMAcc, pMapX, pMapY, nWidth, nBandY1, nBandY2, aWorkPaint, aWorkMask ) )
                {
                    Region aWorkRgn( aDstRgn );
                    aWorkRgn.Move( 0, -nBandY1 );
                    aWorkRgn.Intersect( aWorkMask.CreateRegion( COL_BLACK, Rectangle( aPt, aWorkMask.GetSizePixel() ) ) );

                    ImplRegionInfo  aInfo;
                    long            nWorkX, nWorkY, nWorkWidth, nWorkHeight;
                    BOOL            bRgnRect = aWorkRgn.ImplGetFirstRect( aInfo, nWorkX, nWorkY,
                                                                          nWorkWidth, nWorkHeight );

                    while( bRgnRect )
                    {
                        Bitmap      aCropBmp( aWorkPaint );
                        const Point aOutPt( nWorkX + aDestPt.X(), nWorkY + nBandY1 + aDestPt.Y() );
                        const Size  aOutSz( nWorkWidth, nWorkHeight );
                        const Size  aOutSz1( nWorkWidth + 1, nWorkHeight + 1 );

                        aCropBmp.Crop( Rectangle( Point( nWorkX, nWorkY ), aOutSz ) );
                        ImplDrawBitmap( aOutPt, aOutSz1, Point(), aOutSz, aCropBmp, META_BMPSCALE_ACTION );
                        bRgnRect = aWorkRgn.ImplGetNextRect( aInfo, nWorkX, nWorkY, nWorkWidth, nWorkHeight );
                    }
                }
            }

            delete[] pMapX;
            delete[] pMapY;
            mbMap = bOldMap;
            Pop();
            mpMetaFile = pOldMetaFile;
        }

        if( pPAcc )
            aPaint.ReleaseAccess( pPAcc );

        if( pMAcc )
            aMask.ReleaseAccess( pMAcc );
    }
}

// ------------------------------------------------------------------------

void OutputDevice::ImplPrintMask( const Bitmap& rMask, const Color& rMaskColor,
                                  const Point& rDestPt, const Size& rDestSize,
                                  const Point& rSrcPtPixel, const Size& rSrcSizePixel )
{
#ifndef REMOTE_APPSERVER

    Point       aPt;
    Point       aDestPt( LogicToPixel( rDestPt ) );
    Size        aDestSz( LogicToPixel( rDestSize ) );
    Rectangle   aSrcRect( rSrcPtPixel, rSrcSizePixel );

    aSrcRect.Justify();

    if( !!rMask &&
        aSrcRect.GetWidth() && aSrcRect.GetHeight() &&
        aDestSz.Width() && aDestSz.Height() )
    {
        ULONG   nMirrFlags = 0UL;
        Bitmap  aMask( rMask );
        Region  aRegion;

        if( aMask.GetBitCount() > 1 )
            aMask.Convert( BMP_CONVERSION_1BIT_THRESHOLD );

        if( aDestSz.Width() < 0L )
        {
            aDestSz.Width() = -aDestSz.Width();
            aDestPt.X() -= ( aDestSz.Width() - 1L );
            nMirrFlags |= BMP_MIRROR_HORZ;
        }

        if( aDestSz.Height() < 0L )
        {
            aDestSz.Height() = -aDestSz.Height();
            aDestPt.Y() -= ( aDestSz.Height() - 1L );
            nMirrFlags |= BMP_MIRROR_VERT;
        }

        // source cropped?
        if( aSrcRect != Rectangle( aPt, aMask.GetSizePixel() ) )
            aMask.Crop( aSrcRect );

        // destination mirrored
        if( nMirrFlags )
            aMask.Mirror( nMirrFlags );

        aRegion = aMask.CreateRegion( COL_BLACK, Rectangle( Point(), aMask.GetSizePixel() ) );

        ImplRegionInfo      aInfo;
        const Size          aSrcSz( aMask.GetSizePixel() );
        long                nSrcX, nSrcY, nSrcWidth, nSrcHeight;
        long                nDstX, nDstY, nDstWidth, nDstHeight;
        GDIMetaFile*        pOldMetaFile = mpMetaFile;
        Pair*               pMapX = ImplGetMap( aSrcSz.Width(), aDestSz.Width() );
        Pair*               pMapY = ImplGetMap( aSrcSz.Height(), aDestSz.Height() );
        BOOL                bOldMap = mbMap;
        BOOL                bRegionRect = aRegion.ImplGetFirstRect( aInfo, nSrcX, nSrcY, nSrcWidth, nSrcHeight );

        mpMetaFile = NULL;
        mbMap = FALSE;
        Push( PUSH_FILLCOLOR | PUSH_LINECOLOR );
        SetLineColor( rMaskColor );
        SetFillColor( rMaskColor );
        ImplInitLineColor();
        ImplInitFillColor();

        while( bRegionRect )
        {
            nDstX = pMapX[ nSrcX ].A();
            nDstY = pMapY[ nSrcY ].A();
            nDstWidth = pMapX[ nSrcX + nSrcWidth - 1L ].B() - nDstX + 1L;
            nDstHeight = pMapY[ nSrcY + nSrcHeight - 1L ].B() - nDstY + 1L;
            mpGraphics->DrawRect( nDstX + aDestPt.X(), nDstY + aDestPt.Y(), nDstWidth, nDstHeight );
            bRegionRect = aRegion.ImplGetNextRect( aInfo, nSrcX, nSrcY, nSrcWidth, nSrcHeight );
        }

        Pop();
        delete[] pMapX;
        delete[] pMapY;
        mbMap = bOldMap;
        mpMetaFile = pOldMetaFile;
    }

#endif
}