#include "precomp.h"


//
// OD.CPP
// Order Decoding
//
// Copyright(c) Microsoft 1997-
//

#define MLZ_FILE_ZONE  ZONE_ORDER




//
// OD_ViewStarting()
//
// Sets up the odLast... vars
//
BOOL ASShare::OD_ViewStarting(ASPerson * pasPerson)
{
    BOOL            rc = FALSE;
    TSHR_COLOR      colorWhite = {0xFF,0xFF,0xFF};
    BYTE            brushExtra[7] = {0,0,0,0,0,0,0};

    DebugEntry(ASShare::OD_ViewStarting);

    ValidateView(pasPerson);

    //
    // Invalidate OD results
    //
    pasPerson->m_pView->m_odInvalRgnTotal = CreateRectRgn(0, 0, 0, 0);
    if (pasPerson->m_pView->m_odInvalRgnTotal == NULL)
    {
        ERROR_OUT(("OD_PartyStartingHosting: Couldn't create total invalid OD region"));
        DC_QUIT;
    }

    pasPerson->m_pView->m_odInvalRgnOrder = CreateRectRgn(0, 0, 0, 0);
    if (pasPerson->m_pView->m_odInvalRgnOrder == NULL)
    {
        ERROR_OUT(("OD_PartyStartingHosting: Couldn't create order invalid OD region"));
        DC_QUIT;
    }

    //
    // Back color.
    //
    pasPerson->m_pView->m_odLastBkColor = 0;
    ODUseBkColor(pasPerson, TRUE, colorWhite);

    //
    // Text color.
    //
    pasPerson->m_pView->m_odLastTextColor = 0;
    ODUseTextColor(pasPerson, TRUE, colorWhite);

    //
    // Background mode.
    //
    pasPerson->m_pView->m_odLastBkMode = TRANSPARENT;
    ODUseBkMode(pasPerson, OPAQUE);

    //
    // ROP2.
    //
    pasPerson->m_pView->m_odLastROP2 = R2_BLACK;
    ODUseROP2(pasPerson, R2_COPYPEN);

    //
    // Fill Mode.  It's zero, we don't need to do anything since 0 isn't
    // a valid mode, so we'll change it the first order we get that uses
    // one.
    //
    ASSERT(pasPerson->m_pView->m_odLastFillMode == 0);

    //
    // Arc Direction.  It's zero, we don't need to do anything since 0
    // isn't a valid dir, so we'll change it the first order we get that
    // uses one.
    //
    ASSERT(pasPerson->m_pView->m_odLastArcDirection == 0);

    //
    // Pen.
    //
    pasPerson->m_pView->m_odLastPenStyle = PS_DASH;
    pasPerson->m_pView->m_odLastPenWidth = 2;
    pasPerson->m_pView->m_odLastPenColor = 0;
    ODUsePen(pasPerson, TRUE, PS_SOLID, 1, colorWhite);

    //
    // Brush.
    //
    pasPerson->m_pView->m_odLastBrushOrgX = 1;
    pasPerson->m_pView->m_odLastBrushOrgY = 1;
    pasPerson->m_pView->m_odLastBrushBkColor = 0;
    pasPerson->m_pView->m_odLastBrushTextColor = 0;
    pasPerson->m_pView->m_odLastLogBrushStyle = BS_NULL;
    pasPerson->m_pView->m_odLastLogBrushHatch = HS_VERTICAL;
    pasPerson->m_pView->m_odLastLogBrushColor.red = 0;
    pasPerson->m_pView->m_odLastLogBrushColor.green = 0;
    pasPerson->m_pView->m_odLastLogBrushColor.blue = 0;
    ODUseBrush(pasPerson, TRUE, 0, 0, BS_SOLID, HS_HORIZONTAL,
        colorWhite, brushExtra);

    //
    // Char extra.
    //
    pasPerson->m_pView->m_odLastCharExtra = 1;
    ODUseTextCharacterExtra(pasPerson, 0);

    //
    // Text justification.
    //
    pasPerson->m_pView->m_odLastJustExtra = 1;
    pasPerson->m_pView->m_odLastJustCount = 1;
    ODUseTextJustification(pasPerson, 0, 0);

    // odLastBaselineOffset.  This is zero, which is the default in the DC
    // right now so need to change anything.

    //
    // Font.
    //
    // We don't call ODUseFont because we know that the following values
    // are invalid.  The first valid font to arrive will be selected.
    //
    ASSERT(pasPerson->m_pView->m_odLastFontID == NULL);
    pasPerson->m_pView->m_odLastFontCodePage = 0;
    pasPerson->m_pView->m_odLastFontWidth    = 0;
    pasPerson->m_pView->m_odLastFontHeight   = 0;
    pasPerson->m_pView->m_odLastFontWeight   = 0;
    pasPerson->m_pView->m_odLastFontFlags    = 0;
    pasPerson->m_pView->m_odLastFontFaceLen  = 0;
    ZeroMemory(pasPerson->m_pView->m_odLastFaceName, sizeof(pasPerson->m_pView->m_odLastFaceName));

    //
    // These next 4 variables which describe the current clip rectangle are
    // only valid if fRectReset is FALSE.  If fRectReset is true then no
    // clipping is in force.
    //
    pasPerson->m_pView->m_odRectReset  = TRUE;
    pasPerson->m_pView->m_odLastLeft   = 0x12345678;
    pasPerson->m_pView->m_odLastTop    = 0x12345678;
    pasPerson->m_pView->m_odLastRight  = 0x12345678;
    pasPerson->m_pView->m_odLastBottom = 0x12345678;

    // odLastVGAColor?
    // odLastVGAResult?

    rc = TRUE;

DC_EXIT_POINT:
    DebugExitBOOL(ASShare::OD_ViewStarting, rc);
    return(rc);
}


//
// OD_ViewEnded()
// Cleans up any created objects
//
void ASShare::OD_ViewEnded(ASPerson * pasPerson)
{
    DebugEntry(ASShare::OD_ViewEnded);

    ValidateView(pasPerson);

    //
    // We may create and select in a font and a pen for our drawing decode.
    // Select them out and delete them.  Since we can't delete stock objects,
    // if we didn't actually create one, there's no harm in it.
    //
    if (pasPerson->m_pView->m_usrDC != NULL)
    {
        DeleteBrush(SelectBrush(pasPerson->m_pView->m_usrDC, (HBRUSH)GetStockObject(BLACK_BRUSH)));
        DeletePen(SelectPen(pasPerson->m_pView->m_usrDC, (HPEN)GetStockObject(BLACK_PEN)));
    }

    //
    // Destroy the brush patern
    //
    if (pasPerson->m_pView->m_odLastBrushPattern != NULL)
    {
        DeleteBitmap(pasPerson->m_pView->m_odLastBrushPattern);
        pasPerson->m_pView->m_odLastBrushPattern = NULL;
    }

    //
    // Destroy the font -- but in this case we don't know that our font is
    // actually the one in the DC.  od2 also selects in fonts.
    //
    if (pasPerson->m_pView->m_odLastFontID != NULL)
    {
        // Make sure this isn't selected in to usrDC
        SelectFont(pasPerson->m_pView->m_usrDC, (HFONT)GetStockObject(SYSTEM_FONT));
        DeleteFont(pasPerson->m_pView->m_odLastFontID);
        pasPerson->m_pView->m_odLastFontID = NULL;
    }

    if (pasPerson->m_pView->m_odInvalRgnTotal != NULL)
    {
        DeleteRgn(pasPerson->m_pView->m_odInvalRgnTotal);
        pasPerson->m_pView->m_odInvalRgnTotal = NULL;
    }

    if (pasPerson->m_pView->m_odInvalRgnOrder != NULL)
    {
        DeleteRgn(pasPerson->m_pView->m_odInvalRgnOrder);
        pasPerson->m_pView->m_odInvalRgnOrder = NULL;
    }

    DebugExitVOID(ASShare::OD_ViewEnded);
}



//
// OD_ReceivedPacket()
//
// Handles incoming orders packet from a host.  Replays the drawing orders
// into the screen bitmap for the host, then repaints the view with the
// results.
//
void  ASShare::OD_ReceivedPacket
(
    ASPerson *      pasPerson,
    PS20DATAPACKET  pPacket
)
{
    PORDPACKET      pOrders;
    HPALETTE        hOldPalette;
    HPALETTE        hOldSavePalette;
    UINT            cOrders;
    UINT            cUpdates;
    UINT            i;
    LPCOM_ORDER_UA  pOrder;
    UINT            decodedLength;
    LPBYTE          pEncodedOrder;
    TSHR_INT32      xOrigin;
    TSHR_INT32      yOrigin;
    BOOL            fPalRGB;

    DebugEntry(ASShare::OD_ReceivedPacket);

    ValidateView(pasPerson);

    pOrders = (PORDPACKET)pPacket;

    //
    // The color type is RGB if we or they are < 256 colors
    // Else it's PALETTE if they are old, or new and not sending 24bpp
    //
    fPalRGB = TRUE;

    if ((g_usrScreenBPP < 8) || (pasPerson->cpcCaps.screen.capsBPP < 8))
    {
        TRACE_OUT(("OD_ReceivedPacket: no PALRGB"));
        fPalRGB = FALSE;
    }
    else
    {
        // At 24bpp, no palette matching for RGB values unless we're <= 8
        if ((g_usrScreenBPP > 8) && (pOrders->sendBPP > 8))
        {
            TRACE_OUT(("OD_ReceivedPacket: no PALRGB"));
            fPalRGB = FALSE;
        }
    }


    if (g_usrPalettized)
    {
        //
        // Select and realize the current remote palette into the device
        // context.
        //
        hOldPalette = SelectPalette(pasPerson->m_pView->m_usrDC, pasPerson->pmPalette, FALSE);
        RealizePalette(pasPerson->m_pView->m_usrDC);

        //
        // We must select the same palette into the Save Bitmap DC so that
        // no color conversion occurs during save and restore operations.
        //
        if (pasPerson->m_pView->m_ssiDC != NULL)
        {
            hOldSavePalette = SelectPalette(pasPerson->m_pView->m_ssiDC,
                pasPerson->pmPalette, FALSE);
            RealizePalette(pasPerson->m_pView->m_ssiDC);
        }
    }

    //
    // Extract the number of orders supplied.
    //
    cOrders = pOrders->cOrders;

    if (m_oefOE2EncodingOn)
    {
        pEncodedOrder = (LPBYTE)(&pOrders->data);
        pOrder = NULL;
    }
    else
    {
        pOrder = (LPCOM_ORDER_UA)(&pOrders->data);
        pEncodedOrder = NULL;
    }

    //
    // Get the desktop origin for this person.
    //
    TRACE_OUT(( "Begin replaying %u orders ((", cOrders));

    //
    // This should be empty, we should have reset it when we invalidated
    // the view of the host the last time we got a packet.
    //
#ifdef _DEBUG
    {
        RECT    rcBounds;

        ASSERT(pasPerson->m_pView->m_odInvalTotal == 0);
        GetRgnBox(pasPerson->m_pView->m_odInvalRgnTotal, &rcBounds);
        ASSERT(IsRectEmpty(&rcBounds));
    }
#endif // _DEBUG

    //
    // Repeat for each of the received orders.
    //
    for (i = 0; i < cOrders; i++)
    {
        if (m_oefOE2EncodingOn)
        {
            //
            // Decode the first order. The pOrder returned by
            // OD2_DecodeOrder should have all fields in local byte order
            //
            pOrder = OD2_DecodeOrder( (PDCEO2ORDER)pEncodedOrder,
                                      &decodedLength,
                                      pasPerson );

            if (pOrder == NULL)
            {
                ERROR_OUT(( "Failed to decode order from pasPerson %u", pasPerson));
                DC_QUIT;
            }
        }
        else
        {
            //
            // Convert any font ids to be local ids.
            //

            //
            // BOGUS LAURABU
            // pOrder is unaligned, FH_Convert... takes an aligned order
            //
            FH_ConvertAnyFontIDToLocal((LPCOM_ORDER)pOrder, pasPerson);
            decodedLength = pOrder->OrderHeader.cbOrderDataLength +
                                                    sizeof(COM_ORDER_HEADER);
        }

        //
        // If the order is a Private Order then it is dealt with by
        // the Bitmap Cache Controller.
        //
        if (EXTRACT_TSHR_UINT16_UA(&(pOrder->OrderHeader.fOrderFlags)) &
            OF_PRIVATE)
        {
            RBC_ProcessCacheOrder(pasPerson, pOrder);
        }
        else if (  EXTRACT_TSHR_UINT16_UA(
                 &(((LPPATBLT_ORDER)pOrder->abOrderData)->type)) ==
                                                LOWORD(ORD_DESKSCROLL))
        {
            ERROR_OUT(("Received DESKSCROLL order, obsolete, from 3.0 node [%d]",
                pasPerson->mcsID));
        }
        else
        {
            //
            // Replay the received order.  This will also add the
            // bounds to the invalidate region.
            //
            //
            OD_ReplayOrder(pasPerson, (LPCOM_ORDER)pOrder, fPalRGB);
        }

        if (m_oefOE2EncodingOn)
        {
            pEncodedOrder += decodedLength;
        }
        else
        {
            pOrder = (LPCOM_ORDER_UA)((LPBYTE)pOrder + decodedLength);
        }
    }
    TRACE_OUT(( "End replaying orders ))"));

    //
    // Pass the Update Region to the Shadow Window Presenter.
    //
    OD_UpdateView(pasPerson);

DC_EXIT_POINT:
    if (g_usrPalettized)
    {
        //
        // Reinstate the old palette(s).
        //
        SelectPalette(pasPerson->m_pView->m_usrDC, hOldPalette, FALSE);
        if (pasPerson->m_pView->m_ssiDC != NULL)
        {
            SelectPalette(pasPerson->m_pView->m_ssiDC, hOldSavePalette, FALSE);
        }
    }

    DebugExitVOID(ASShare::OD_ReceivedPacket);
}

//
// OD_UpdateView()
//
// This is called after we've processed an order packet and replayed the
// drawing into our bitmap for the host.
//
// Replaying the drawing keeps a running tally of the area changed.  This
// function invalidates the changed area in the view of the host, so it
// will repaint and show the updates.
//
void  ASShare::OD_UpdateView(ASPerson * pasHost)
{
    RECT        rcBounds;

    DebugEntry(ASShare::OD_UpdateView);

    ValidateView(pasHost);

    //
    // Do nothing if there are no updates.
    //
    if (pasHost->m_pView->m_odInvalTotal == 0)
    {
        // Nothing got played back, nothing to repaint
    }
    else if (pasHost->m_pView->m_odInvalTotal <= MAX_UPDATE_REGION_ORDERS)
    {
        VIEW_InvalidateRgn(pasHost, pasHost->m_pView->m_odInvalRgnTotal);
    }
    else
    {
        //
        // Rather than invalidating a very complex region, which will
        // chew up a lot of memory, just invalidate the bounding box.
        //
        GetRgnBox(pasHost->m_pView->m_odInvalRgnTotal, &rcBounds);
        TRACE_OUT(("OD_UpdateView: Update region too complex; use bounds {%04d, %04d, %04d, %04d}",
            rcBounds.left, rcBounds.top, rcBounds.right, rcBounds.bottom));

        //
        // BOGUS LAURABU!
        // This code used to add one to the right & bottom, which was
        // bogus EXCLUSIVE coordinate confusion.  I fixed this--the bound
        // box is the right area.
        //
        SetRectRgn(pasHost->m_pView->m_odInvalRgnTotal, rcBounds.left, rcBounds.top,
            rcBounds.right, rcBounds.bottom);
        VIEW_InvalidateRgn(pasHost, pasHost->m_pView->m_odInvalRgnTotal);
    }

    // Now reset the update region to empty
    SetRectRgn(pasHost->m_pView->m_odInvalRgnTotal, 0, 0, 0, 0);
    pasHost->m_pView->m_odInvalTotal = 0;

    DebugExitVOID(ASShare::OD_UpdateView);
}


//
// OD_ReplayOrder()
//
// Replays one drawing operation, the next one, in the packet of orders
// we received from a host.
//
void  ASShare::OD_ReplayOrder
(
    ASPerson *      pasPerson,
    LPCOM_ORDER     pOrder,
    BOOL            fPalRGB
)
{
    LPPATBLT_ORDER  pDrawing;
    LPSTR           faceName;
    UINT            faceNameLength;
    UINT            trueFontWidth;
    UINT            maxFontHeight;
    TSHR_UINT16     nFontFlags;
    TSHR_UINT16     nCodePage;
    UINT            i;
    RECT            rcDst;

    DebugEntry(ASShare::OD_ReplayOrder);

    ValidateView(pasPerson);

    pDrawing = (LPPATBLT_ORDER)pOrder->abOrderData;

    //
    // These are VD coords.
    // WHEN 2.X INTEROP IS GONE, GET RID OF m_pView->m_dsScreenOrigin
    //
    RECT_FROM_TSHR_RECT16(&rcDst, pOrder->OrderHeader.rcsDst);

    //
    // The host bitmap is in screen, not VD, coords
    //
    if (pOrder->OrderHeader.fOrderFlags & OF_NOTCLIPPED)
    {
        //
        // The rectangle associated with this order is the bounding
        // rectangle of the order and does not clip it.  We optimise this
        // case by passing in a large rectangle that will not result in
        // clipping to ODUseRectRegion.  ODUseRectRegion will spot if this
        // is the same as the last clip region we set and take a fast exit
        // path. This improves performance substantially.
        //
        ODUseRectRegion(pasPerson, 0, 0, 10000, 10000);
    }
    else
    {
        ODUseRectRegion(pasPerson, rcDst.left, rcDst.top, rcDst.right, rcDst.bottom);
    }

    switch (pDrawing->type)
    {
        case ORD_DSTBLT_TYPE:
            ODReplayDSTBLT(pasPerson, (LPDSTBLT_ORDER)pDrawing, fPalRGB);
            break;

        case ORD_PATBLT_TYPE:
            ODReplayPATBLT(pasPerson, (LPPATBLT_ORDER)pDrawing, fPalRGB);
            break;

        case ORD_SCRBLT_TYPE:
            ODReplaySCRBLT(pasPerson, (LPSCRBLT_ORDER)pDrawing, fPalRGB);
            break;

        case ORD_MEMBLT_TYPE:
        case ORD_MEMBLT_R2_TYPE:
            ODReplayMEMBLT(pasPerson, (LPMEMBLT_ORDER)pDrawing, fPalRGB);
            break;

        case ORD_MEM3BLT_TYPE:
        case ORD_MEM3BLT_R2_TYPE:
            ODReplayMEM3BLT(pasPerson, (LPMEM3BLT_ORDER)pDrawing, fPalRGB);
            break;

        case ORD_RECTANGLE_TYPE:
            ODReplayRECTANGLE(pasPerson, (LPRECTANGLE_ORDER)pDrawing, fPalRGB);
            break;

        case ORD_POLYGON_TYPE:
            ODReplayPOLYGON(pasPerson, (LPPOLYGON_ORDER)pDrawing, fPalRGB);
            break;

        case ORD_PIE_TYPE:
            ODReplayPIE(pasPerson, (LPPIE_ORDER)pDrawing, fPalRGB);
            break;

        case ORD_ELLIPSE_TYPE:
            ODReplayELLIPSE(pasPerson, (LPELLIPSE_ORDER)pDrawing, fPalRGB);
            break;

        case ORD_ARC_TYPE:
            ODReplayARC(pasPerson, (LPARC_ORDER)pDrawing, fPalRGB);
            break;

        case ORD_CHORD_TYPE:
            ODReplayCHORD(pasPerson, (LPCHORD_ORDER)pDrawing, fPalRGB);
            break;

        case ORD_POLYBEZIER_TYPE:
            ODReplayPOLYBEZIER(pasPerson, (LPPOLYBEZIER_ORDER)pDrawing, fPalRGB);
            break;

        case ORD_ROUNDRECT_TYPE:
            ODReplayROUNDRECT(pasPerson, (LPROUNDRECT_ORDER)pDrawing, fPalRGB);
            break;

        case ORD_LINETO_TYPE:
            ODReplayLINETO(pasPerson, (LPLINETO_ORDER)pDrawing, fPalRGB);
            break;

        case ORD_EXTTEXTOUT_TYPE:
            ODReplayEXTTEXTOUT(pasPerson, (LPEXTTEXTOUT_ORDER)pDrawing, fPalRGB);
            break;

        case ORD_TEXTOUT_TYPE:
            ODReplayTEXTOUT(pasPerson, (LPTEXTOUT_ORDER)pDrawing, fPalRGB);
            break;

        case ORD_OPAQUERECT_TYPE:
            ODReplayOPAQUERECT(pasPerson, (LPOPAQUERECT_ORDER)pDrawing, fPalRGB);
            break;

        case ORD_SAVEBITMAP_TYPE:
            SSI_SaveBitmap(pasPerson, (LPSAVEBITMAP_ORDER)pDrawing);
            break;

        default:
            ERROR_OUT(( "ORDER: Unrecognised order %d from [%d]",
                         (int)pDrawing->type, pasPerson->mcsID));
            break;
    }

    //
    // rcDst is INCLUSIVE coords still
    //
    if ((rcDst.left <= rcDst.right) && (rcDst.top <= rcDst.bottom))
    {
        SetRectRgn(pasPerson->m_pView->m_odInvalRgnOrder, rcDst.left, rcDst.top,
            rcDst.right+1, rcDst.bottom+1);

        //
        // Combine the rectangle region with the update region.
        //
        if (UnionRgn(pasPerson->m_pView->m_odInvalRgnTotal, pasPerson->m_pView->m_odInvalRgnTotal, pasPerson->m_pView->m_odInvalRgnOrder) <= ERROR)
        {
            RECT    rcCur;

            //
            // Union failed; so simplyify the current region
            //
            WARNING_OUT(("OD_ReplayOrder: UnionRgn failed"));

            //
            // BOGUS LAURABU!
            // This code used to add one to the right & bottom, which is
            // bogus exclusive coord confusion.  The bound box is the right
            // area.
            //
            GetRgnBox(pasPerson->m_pView->m_odInvalRgnTotal, &rcCur);
            SetRectRgn(pasPerson->m_pView->m_odInvalRgnTotal, rcCur.left, rcCur.top, rcCur.right,
                rcCur.bottom);

            //
            // Reset odInvalTotal count -- this is really a # of bounds rects
            // count, and now we have just one.
            //
            pasPerson->m_pView->m_odInvalTotal = 1;

            if (UnionRgn(pasPerson->m_pView->m_odInvalRgnTotal, pasPerson->m_pView->m_odInvalRgnTotal, pasPerson->m_pView->m_odInvalRgnOrder) <= ERROR)
            {
                ERROR_OUT(("OD_ReplayOrder: UnionRgn failed after simplification"));
            }
        }

        pasPerson->m_pView->m_odInvalTotal++;
    }

    DebugExitVOID(ASShare::OD_ReplayOrder);
}



//
// ODReplayDSTBLT()
// Replays a DSTBLT order
//
void ASShare::ODReplayDSTBLT
(
    ASPerson *      pasPerson,
    LPDSTBLT_ORDER  pDstBlt,
    BOOL            fPalRGB
)
{
    DebugEntry(ASShare::ODReplayDSTBLT);

    TRACE_OUT(("ORDER: DstBlt X %hd Y %hd w %hd h %hd rop %08lX",
                         pDstBlt->nLeftRect,
                         pDstBlt->nTopRect,
                         pDstBlt->nWidth,
                         pDstBlt->nHeight,
                         (UINT)ODConvertToWindowsROP(pDstBlt->bRop)));

    //
    // Apply DS origin offset ourselves (do not use transform)
    //
    PatBlt(pasPerson->m_pView->m_usrDC,
        pDstBlt->nLeftRect - pasPerson->m_pView->m_dsScreenOrigin.x,
        pDstBlt->nTopRect - pasPerson->m_pView->m_dsScreenOrigin.y,
        pDstBlt->nWidth,
        pDstBlt->nHeight,
        ODConvertToWindowsROP(pDstBlt->bRop));

    DebugExitVOID(ASShare::ODReplayDSTBLT);
}



//
// ASShare::ODReplayPATBLT()
// Replays a PATBLT order
//
void ASShare::ODReplayPATBLT
(
    ASPerson *      pasPerson,
    LPPATBLT_ORDER  pPatblt,
    BOOL            fPalRGB
)
{
    TSHR_COLOR      BackColor;
    TSHR_COLOR      ForeColor;

    DebugEntry(ASShare::ODReplayPATBLT);

    TRACE_OUT(("ORDER: PatBlt BC %08lX FC %08lX Brush %02X %02X X %d Y %d w %d h %d rop %08lX",
                        pPatblt->BackColor,
                        pPatblt->ForeColor,
                        pPatblt->BrushStyle,
                        pPatblt->BrushHatch,
                        pPatblt->nLeftRect,
                        pPatblt->nTopRect,
                        pPatblt->nWidth,
                        pPatblt->nHeight,
                        ODConvertToWindowsROP(pPatblt->bRop) ));

    ODAdjustColor(pasPerson, &(pPatblt->BackColor), &BackColor, OD_BACK_COLOR);
    ODAdjustColor(pasPerson, &(pPatblt->ForeColor), &ForeColor, OD_FORE_COLOR);

    ODUseBkColor(pasPerson, fPalRGB, BackColor);
    ODUseTextColor(pasPerson, fPalRGB, ForeColor);

    ODUseBrush(pasPerson, fPalRGB, pPatblt->BrushOrgX, pPatblt->BrushOrgY,
        pPatblt->BrushStyle, pPatblt->BrushHatch, ForeColor, pPatblt->BrushExtra);

    //
    // Apply DS origin offset ourselves (do not use transform)
    //
    PatBlt(pasPerson->m_pView->m_usrDC,
        pPatblt->nLeftRect - pasPerson->m_pView->m_dsScreenOrigin.x,
        pPatblt->nTopRect  - pasPerson->m_pView->m_dsScreenOrigin.y,
        pPatblt->nWidth,
        pPatblt->nHeight,
        ODConvertToWindowsROP(pPatblt->bRop));

    DebugExitVOID(ASShare::ODReplayPATBLT);
}



//
// ASShare::ODReplaySCRBLT()
// Replays SCRBLT order
//
void ASShare::ODReplaySCRBLT
(
    ASPerson *      pasPerson,
    LPSCRBLT_ORDER  pScrBlt,
    BOOL            fPalRGB
)
{
    DebugEntry(ASShare::ODReplaySCRBLT);

    TRACE_OUT(("ORDER: ScrBlt dx %d dy %d w %d h %d sx %d sy %d rop %08lX",
        pScrBlt->nLeftRect,
        pScrBlt->nTopRect,
        pScrBlt->nWidth,
        pScrBlt->nHeight,
        pScrBlt->nXSrc,
        pScrBlt->nYSrc,
        ODConvertToWindowsROP(pScrBlt->bRop)));

    //
    // Apply DS origin offset ourselves (do not use transform)
    //
    BitBlt(pasPerson->m_pView->m_usrDC,
        pScrBlt->nLeftRect - pasPerson->m_pView->m_dsScreenOrigin.x,
        pScrBlt->nTopRect - pasPerson->m_pView->m_dsScreenOrigin.y,
        pScrBlt->nWidth,
        pScrBlt->nHeight,
        pasPerson->m_pView->m_usrDC,
        pScrBlt->nXSrc - pasPerson->m_pView->m_dsScreenOrigin.x,
        pScrBlt->nYSrc - pasPerson->m_pView->m_dsScreenOrigin.y,
        ODConvertToWindowsROP(pScrBlt->bRop));

    DebugExitVOID(ASShare::ODReplaySCRBLT);
}



//
// ASShare::ODReplayMEMBLT()
// Replays MEMBLT and MEMBLT_R2 orders
//
void ASShare::ODReplayMEMBLT
(
    ASPerson *      pasPerson,
    LPMEMBLT_ORDER  pMemBlt,
    BOOL            fPalRGB
)
{
    HPALETTE        hpalOld;
    HPALETTE        hpalOld2;
    TSHR_UINT16     cacheIndex;
    UINT            nXSrc;
    HBITMAP         cacheBitmap;
    HBITMAP         hOldBitmap;
    COLORREF        clrBk;
    COLORREF        clrText;

    DebugEntry(ASShare::ODReplayMEMBLT);

    ValidateView(pasPerson);

    TRACE_OUT(("MEMBLT nXSrc %d",pMemBlt->nXSrc));

    hpalOld = SelectPalette(pasPerson->m_pView->m_usrWorkDC, pasPerson->pmPalette, FALSE);
    RealizePalette(pasPerson->m_pView->m_usrWorkDC);

    hpalOld2 = SelectPalette( pasPerson->m_pView->m_usrDC, pasPerson->pmPalette, FALSE );
    RealizePalette(pasPerson->m_pView->m_usrDC);

    //
    // Now get the source bitmap.  The cache is defined by
    // hBitmap.  For R1 protocols the cache index is indicated
    // by the source offset on the order.  For R2 it is
    // indicated by a separate field in the order.
    // The color table index is in the high order of hBitmap
    //
    cacheIndex = ((LPMEMBLT_R2_ORDER)pMemBlt)->cacheIndex;
    nXSrc = pMemBlt->nXSrc;

    TRACE_OUT(( "MEMBLT color %d cache %d:%d",
        MEMBLT_COLORINDEX(pMemBlt),
        MEMBLT_CACHETABLE(pMemBlt),
        cacheIndex));

    cacheBitmap = RBC_MapCacheIDToBitmapHandle(pasPerson,
        MEMBLT_CACHETABLE(pMemBlt), cacheIndex, MEMBLT_COLORINDEX(pMemBlt));

    hOldBitmap = SelectBitmap(pasPerson->m_pView->m_usrWorkDC, cacheBitmap);

    TRACE_OUT(("ORDER: MemBlt dx %d dy %d w %d h %d sx %d sy %d rop %08lX",
        pMemBlt->nLeftRect,
        pMemBlt->nTopRect,
        pMemBlt->nWidth,
        pMemBlt->nHeight,
        nXSrc,
        pMemBlt->nYSrc,
        ODConvertToWindowsROP(pMemBlt->bRop)));

    //
    // ALWAYS set back/fore color to white/black in case of rops like
    // SRCAND or SRCINVERT which will use their values.
    //
    clrBk = SetBkColor(pasPerson->m_pView->m_usrDC, RGB(255, 255, 255));
    clrText = SetTextColor(pasPerson->m_pView->m_usrDC, RGB(0, 0, 0));

    //
    // Apply DS origin offset ourselves (do not use transform)
    //
    BitBlt(pasPerson->m_pView->m_usrDC,
        pMemBlt->nLeftRect- pasPerson->m_pView->m_dsScreenOrigin.x,
        pMemBlt->nTopRect - pasPerson->m_pView->m_dsScreenOrigin.y,
        pMemBlt->nWidth,
        pMemBlt->nHeight,
        pasPerson->m_pView->m_usrWorkDC,
        nXSrc,
        pMemBlt->nYSrc,
        ODConvertToWindowsROP(pMemBlt->bRop));

    //
    // If the relevant property is set hatch the area in blue.
    //
    if (m_usrHatchBitmaps)
    {
        SDP_DrawHatchedRect(pasPerson->m_pView->m_usrDC,
            pMemBlt->nLeftRect - pasPerson->m_pView->m_dsScreenOrigin.x,
            pMemBlt->nTopRect  - pasPerson->m_pView->m_dsScreenOrigin.y,
            pMemBlt->nWidth,
            pMemBlt->nHeight,
            USR_HATCH_COLOR_BLUE);
    }

    //
    // Restore back, text colors
    //
    SetTextColor(pasPerson->m_pView->m_usrDC, clrText);
    SetBkColor(pasPerson->m_pView->m_usrDC, clrBk);

    //
    // Deselect the bitmap from the DC.
    //
    SelectBitmap(pasPerson->m_pView->m_usrWorkDC, hOldBitmap);

    SelectPalette(pasPerson->m_pView->m_usrWorkDC, hpalOld, FALSE);
    SelectPalette(pasPerson->m_pView->m_usrDC, hpalOld2, FALSE);

    DebugExitVOID(ASShare::ODReplayMEMBLT);
}


//
// ASShare::ODReplayMEM3BLT()
// Replays MEM3BLT and MEM3BLT_R2 orders
//
void ASShare::ODReplayMEM3BLT
(
    ASPerson *      pasPerson,
    LPMEM3BLT_ORDER pMem3Blt,
    BOOL            fPalRGB
)
{
    HPALETTE        hpalOld;
    HPALETTE        hpalOld2;
    TSHR_UINT16     cacheIndex;
    int             nXSrc;
    HBITMAP         cacheBitmap;
    HBITMAP         hOldBitmap;
    TSHR_COLOR      BackColor;
    TSHR_COLOR      ForeColor;

    DebugEntry(ASShare::ODReplayMEM3BLT);

    ValidateView(pasPerson);

    TRACE_OUT(("MEM3BLT nXSrc %d",pMem3Blt->nXSrc));
    TRACE_OUT(("ORDER: Mem3Blt brush %04lX %04lX dx %d dy %d "\
            "w %d h %d sx %d sy %d rop %08lX",
        pMem3Blt->BrushStyle,
        pMem3Blt->BrushHatch,
        pMem3Blt->nLeftRect,
        pMem3Blt->nTopRect,
        pMem3Blt->nWidth,
        pMem3Blt->nHeight,
        pMem3Blt->nXSrc,
        pMem3Blt->nYSrc,
        (UINT)ODConvertToWindowsROP(pMem3Blt->bRop)));


    hpalOld = SelectPalette(pasPerson->m_pView->m_usrWorkDC, pasPerson->pmPalette, FALSE);
    RealizePalette(pasPerson->m_pView->m_usrWorkDC);

    hpalOld2 = SelectPalette( pasPerson->m_pView->m_usrDC, pasPerson->pmPalette, FALSE);
    RealizePalette(pasPerson->m_pView->m_usrDC);

    //
    // Now get the source bitmap.  The cache is defined by
    // hBitmap.  For R1 protocols the cache index is indicated
    // by the source offset on the order.  For R2 it is
    // indicated by a separate field in the order.
    // The color table index is in the high order of hBitmap
    //
    cacheIndex = ((LPMEM3BLT_R2_ORDER)pMem3Blt)->cacheIndex;
    nXSrc = pMem3Blt->nXSrc;

    TRACE_OUT(("MEM3BLT color %d cache %d:%d",
        MEMBLT_COLORINDEX(pMem3Blt),
        MEMBLT_CACHETABLE(pMem3Blt),
        cacheIndex));

    cacheBitmap = RBC_MapCacheIDToBitmapHandle(pasPerson,
        MEMBLT_CACHETABLE(pMem3Blt), cacheIndex, MEMBLT_COLORINDEX(pMem3Blt));

    hOldBitmap = SelectBitmap(pasPerson->m_pView->m_usrWorkDC, cacheBitmap);

    ODAdjustColor(pasPerson, &(pMem3Blt->BackColor), &BackColor, OD_BACK_COLOR);
    ODAdjustColor(pasPerson, &(pMem3Blt->ForeColor), &ForeColor, OD_FORE_COLOR);

    ODUseBkColor(pasPerson, fPalRGB, BackColor);
    ODUseTextColor(pasPerson, fPalRGB, ForeColor);

    ODUseBrush(pasPerson, fPalRGB, pMem3Blt->BrushOrgX, pMem3Blt->BrushOrgY,
        pMem3Blt->BrushStyle, pMem3Blt->BrushHatch, ForeColor,
        pMem3Blt->BrushExtra);

    //
    // Apply DS origin offset ourselves (do not use transform)
    //
    BitBlt(pasPerson->m_pView->m_usrDC,
        pMem3Blt->nLeftRect - pasPerson->m_pView->m_dsScreenOrigin.x,
        pMem3Blt->nTopRect - pasPerson->m_pView->m_dsScreenOrigin.y,
        pMem3Blt->nWidth,
        pMem3Blt->nHeight,
        pasPerson->m_pView->m_usrWorkDC,
        nXSrc,
        pMem3Blt->nYSrc,
        ODConvertToWindowsROP(pMem3Blt->bRop));

    //
    // If the relevant property is set hatch the area in blue.
    //
    if (m_usrHatchBitmaps)
    {
        SDP_DrawHatchedRect(pasPerson->m_pView->m_usrDC,
            pMem3Blt->nLeftRect - pasPerson->m_pView->m_dsScreenOrigin.x,
            pMem3Blt->nTopRect  - pasPerson->m_pView->m_dsScreenOrigin.y,
            pMem3Blt->nWidth,
            pMem3Blt->nHeight,
            USR_HATCH_COLOR_BLUE);
    }

    //
    // Deselect the bitmap from the DC.
    //
    SelectBitmap(pasPerson->m_pView->m_usrWorkDC, hOldBitmap);

    SelectPalette(pasPerson->m_pView->m_usrWorkDC, hpalOld, FALSE);
    SelectPalette(pasPerson->m_pView->m_usrDC, hpalOld2, FALSE);

    DebugExitVOID(ASShare::ODReplayMEM3BLT);
}



//
// ASShare::ODReplayRECTANGLE()
// Replays RECTANGLE order
//
void ASShare::ODReplayRECTANGLE
(
    ASPerson *          pasPerson,
    LPRECTANGLE_ORDER   pRectangle,
    BOOL                fPalRGB
)
{
    TSHR_COLOR          BackColor;
    TSHR_COLOR          ForeColor;
    TSHR_COLOR          PenColor;

    DebugEntry(ASShare::ODReplayRECTANGLE);

    TRACE_OUT(("ORDER: Rectangle BC %08lX FC %08lX BM %04hX brush %02hX " \
            "%02hX rop2 %04hX pen %04hX %04hX %08lX rect %d %d %d %d",
        pRectangle->BackColor,
        pRectangle->ForeColor,
        (TSHR_UINT16)pRectangle->BackMode,
        (TSHR_UINT16)pRectangle->BrushStyle,
        (TSHR_UINT16)pRectangle->BrushHatch,
        (TSHR_UINT16)pRectangle->ROP2,
        (TSHR_UINT16)pRectangle->PenStyle,
        (TSHR_UINT16)pRectangle->PenWidth,
        pRectangle->PenColor,
        (int)pRectangle->nLeftRect,
        (int)pRectangle->nTopRect,
        (int)pRectangle->nRightRect + 1,
        (int)pRectangle->nBottomRect + 1));

    ODAdjustColor(pasPerson, &(pRectangle->BackColor), &BackColor, OD_BACK_COLOR);
    ODAdjustColor(pasPerson, &(pRectangle->ForeColor), &ForeColor, OD_FORE_COLOR);
    ODAdjustColor(pasPerson, &(pRectangle->PenColor), &PenColor, OD_PEN_COLOR);

    ODUseBkColor(pasPerson, fPalRGB, BackColor);
    ODUseTextColor(pasPerson, fPalRGB, ForeColor);

    ODUseBkMode(pasPerson, pRectangle->BackMode);

    ODUseBrush(pasPerson, fPalRGB, pRectangle->BrushOrgX, pRectangle->BrushOrgY,
        pRectangle->BrushStyle, pRectangle->BrushHatch, ForeColor,
        pRectangle->BrushExtra);

    ODUseROP2(pasPerson, pRectangle->ROP2);

    ODUsePen(pasPerson, fPalRGB, pRectangle->PenStyle, pRectangle->PenWidth,
        PenColor);

    //
    // The rectangle in the order is inclusive but Windows works
    // with exclusive rectangles.
    //
    // Apply DS origin offset ourselves (do not use transform)
    //
    Rectangle(pasPerson->m_pView->m_usrDC,
        pRectangle->nLeftRect  - pasPerson->m_pView->m_dsScreenOrigin.x,
        pRectangle->nTopRect   - pasPerson->m_pView->m_dsScreenOrigin.y,
        pRectangle->nRightRect - pasPerson->m_pView->m_dsScreenOrigin.x + 1,
        pRectangle->nBottomRect- pasPerson->m_pView->m_dsScreenOrigin.y + 1);

    DebugExitVOID(ASShare::ODReplayRECTANGLE);
}



//
// ASShare::ODReplayPOLYGON()
// Replays POLYGON order
//
void ASShare::ODReplayPOLYGON
(
    ASPerson *      pasPerson,
    LPPOLYGON_ORDER pPolygon,
    BOOL            fPalRGB
)
{
    POINT           aP[ORD_MAX_POLYGON_POINTS];
    UINT            i;
    UINT            cPoints;
    TSHR_COLOR      BackColor;
    TSHR_COLOR      ForeColor;
    TSHR_COLOR      PenColor;

    DebugEntry(ASShare::ODReplayPOLYGON);

    cPoints = pPolygon->variablePoints.len /
            sizeof(pPolygon->variablePoints.aPoints[0]);

    TRACE_OUT(("ORDER: Polygon BC %08lX FC %08lX BM %04hX brush %02hX %02hX "
            "%02hX %02hX rop2 %04hX pen %04hX %04hX %08lX points %d",
        pPolygon->BackColor,
        pPolygon->ForeColor,
        (TSHR_UINT16)pPolygon->BackMode,
        (TSHR_UINT16)pPolygon->BrushStyle,
        (TSHR_UINT16)pPolygon->BrushHatch,
        (TSHR_UINT16)pPolygon->ROP2,
        (TSHR_UINT16)pPolygon->PenStyle,
        (TSHR_UINT16)pPolygon->PenWidth,
        pPolygon->PenColor,
        cPoints));

    //
    // Apply DS origin offset ourselves (do not use transform)
    // while copying to native size point array.
    //
    for (i = 0; i < cPoints; i++)
    {
        TRACE_OUT(( "aPoints[%u]: %d,%d", i,
            (int)(pPolygon->variablePoints.aPoints[i].x),
            (int)(pPolygon->variablePoints.aPoints[i].y)));

        aP[i].x = pPolygon->variablePoints.aPoints[i].x -
                  pasPerson->m_pView->m_dsScreenOrigin.x;
        aP[i].y = pPolygon->variablePoints.aPoints[i].y -
                  pasPerson->m_pView->m_dsScreenOrigin.y;
    }

    ODAdjustColor(pasPerson, &(pPolygon->BackColor), &BackColor, OD_BACK_COLOR);
    ODAdjustColor(pasPerson, &(pPolygon->ForeColor), &ForeColor, OD_FORE_COLOR);
    ODAdjustColor(pasPerson, &(pPolygon->PenColor), &PenColor, OD_PEN_COLOR);

    ODUseBkColor(pasPerson, fPalRGB, BackColor);
    ODUseTextColor(pasPerson, fPalRGB, ForeColor);

    ODUseBkMode(pasPerson, pPolygon->BackMode);

    ODUseBrush(pasPerson, fPalRGB, pPolygon->BrushOrgX, pPolygon->BrushOrgY,
        pPolygon->BrushStyle, pPolygon->BrushHatch, ForeColor,
        pPolygon->BrushExtra);

    ODUseROP2(pasPerson, pPolygon->ROP2);

    ODUsePen(pasPerson, fPalRGB, pPolygon->PenStyle, pPolygon->PenWidth,
        PenColor);

    ODUseFillMode(pasPerson, pPolygon->FillMode);


    Polygon(pasPerson->m_pView->m_usrDC, aP, cPoints);


    DebugExitVOID(ASShare::ODReplayPOLYGON);
}


//
// ASShare::ODReplayPIE()
// Replays PIE order
//
void ASShare::ODReplayPIE
(
    ASPerson *      pasPerson,
    LPPIE_ORDER     pPie,
    BOOL            fPalRGB
)
{
    TSHR_COLOR      BackColor;
    TSHR_COLOR      ForeColor;
    TSHR_COLOR      PenColor;

    DebugEntry(ASShare::ODReplayPIE);

    TRACE_OUT(("ORDER: Pie BC %08lX FC %08lX BM %04hX brush %02hX "
            " %02hX rop2 %04hX pen %04hX %04hX %08lX rect %d %d %d %d",
        pPie->BackColor,
        pPie->ForeColor,
        (TSHR_UINT16)pPie->BackMode,
        (TSHR_UINT16)pPie->BrushStyle,
        (TSHR_UINT16)pPie->BrushHatch,
        (TSHR_UINT16)pPie->ROP2,
        (TSHR_UINT16)pPie->PenStyle,
        (TSHR_UINT16)pPie->PenWidth,
        pPie->PenColor,
        (int)pPie->nLeftRect,
        (int)pPie->nTopRect,
        (int)pPie->nRightRect + 1,
        (int)pPie->nBottomRect + 1));

    ODAdjustColor(pasPerson, &(pPie->BackColor), &BackColor, OD_BACK_COLOR);
    ODAdjustColor(pasPerson, &(pPie->ForeColor), &ForeColor, OD_FORE_COLOR);
    ODAdjustColor(pasPerson, &(pPie->PenColor), &PenColor, OD_PEN_COLOR);

    ODUseBkColor(pasPerson, fPalRGB, BackColor);
    ODUseTextColor(pasPerson, fPalRGB, ForeColor);

    ODUseBkMode(pasPerson, pPie->BackMode);

    ODUseBrush(pasPerson, fPalRGB, pPie->BrushOrgX, pPie->BrushOrgY,
        pPie->BrushStyle, pPie->BrushHatch, ForeColor, pPie->BrushExtra);

    ODUseROP2(pasPerson, pPie->ROP2);

    ODUsePen(pasPerson, fPalRGB, pPie->PenStyle, pPie->PenWidth,
        PenColor);

    ODUseArcDirection(pasPerson, (int)pPie->ArcDirection);


    Pie(pasPerson->m_pView->m_usrDC,
        pPie->nLeftRect   - pasPerson->m_pView->m_dsScreenOrigin.x,
        pPie->nTopRect    - pasPerson->m_pView->m_dsScreenOrigin.y,
        pPie->nRightRect  - pasPerson->m_pView->m_dsScreenOrigin.x + 1,
        pPie->nBottomRect - pasPerson->m_pView->m_dsScreenOrigin.y + 1,
        pPie->nXStart     - pasPerson->m_pView->m_dsScreenOrigin.x,
        pPie->nYStart     - pasPerson->m_pView->m_dsScreenOrigin.y,
        pPie->nXEnd       - pasPerson->m_pView->m_dsScreenOrigin.x,
        pPie->nYEnd       - pasPerson->m_pView->m_dsScreenOrigin.y);


    DebugExitVOID(ASShare::ODReplayPIE);
}



//
// ASShare::ODReplayELLIPSE()
// Replays ELLIPSE order
//
void ASShare::ODReplayELLIPSE
(
    ASPerson *      pasPerson,
    LPELLIPSE_ORDER pEllipse,
    BOOL            fPalRGB
)
{
    TSHR_COLOR      BackColor;
    TSHR_COLOR      ForeColor;
    TSHR_COLOR      PenColor;

    DebugEntry(ASShare::ODReplayELLIPSE);

    TRACE_OUT(("ORDER: Ellipse BC %08lX FC %08lX BM %04hX brush %02hX %02hX "
            "rop2 %04hX pen %04hX %04hX %08lX rect %d %d %d %d",
        pEllipse->BackColor,
        pEllipse->ForeColor,
        (TSHR_UINT16)pEllipse->BackMode,
        (TSHR_UINT16)pEllipse->BrushStyle,
        (TSHR_UINT16)pEllipse->BrushHatch,
        (TSHR_UINT16)pEllipse->ROP2,
        (TSHR_UINT16)pEllipse->PenStyle,
        (TSHR_UINT16)pEllipse->PenWidth,
        pEllipse->PenColor,
        (int)pEllipse->nLeftRect,
        (int)pEllipse->nTopRect,
        (int)pEllipse->nRightRect + 1,
        (int)pEllipse->nBottomRect + 1));

    ODAdjustColor(pasPerson, &(pEllipse->BackColor), &BackColor, OD_BACK_COLOR);
    ODAdjustColor(pasPerson, &(pEllipse->ForeColor), &ForeColor, OD_FORE_COLOR);
    ODAdjustColor(pasPerson, &(pEllipse->PenColor), &PenColor, OD_PEN_COLOR);

    ODUseBkColor(pasPerson, fPalRGB, BackColor);
    ODUseTextColor(pasPerson, fPalRGB, ForeColor);

    ODUseBkMode(pasPerson, pEllipse->BackMode);

    ODUseBrush(pasPerson, fPalRGB, pEllipse->BrushOrgX, pEllipse->BrushOrgY,
        pEllipse->BrushStyle, pEllipse->BrushHatch, ForeColor,
        pEllipse->BrushExtra);

    ODUseROP2(pasPerson, pEllipse->ROP2);

    ODUsePen(pasPerson, fPalRGB, pEllipse->PenStyle, pEllipse->PenWidth,
        PenColor);


    Ellipse(pasPerson->m_pView->m_usrDC,
        pEllipse->nLeftRect   - pasPerson->m_pView->m_dsScreenOrigin.x,
        pEllipse->nTopRect    - pasPerson->m_pView->m_dsScreenOrigin.y,
        pEllipse->nRightRect  - pasPerson->m_pView->m_dsScreenOrigin.x + 1,
        pEllipse->nBottomRect - pasPerson->m_pView->m_dsScreenOrigin.y + 1);


    DebugExitVOID(ASShare::ODReplayELLIPSE);
}



//
// ASShare::ODReplayARC()
// Replays ARC order
//
void ASShare::ODReplayARC
(
    ASPerson *      pasPerson,
    LPARC_ORDER     pArc,
    BOOL            fPalRGB
)
{
    TSHR_COLOR      BackColor;
    TSHR_COLOR      PenColor;

    DebugEntry(ASShare::ODReplayARC);

    TRACE_OUT(("ORDER: Arc BC %08lX BM %04hX rop2 %04hX pen %04hX "
            "%04hX %08lX rect %d %d %d %d",
        pArc->BackColor,
        (TSHR_UINT16)pArc->BackMode,
        (TSHR_UINT16)pArc->ROP2,
        (TSHR_UINT16)pArc->PenStyle,
        (TSHR_UINT16)pArc->PenWidth,
        pArc->PenColor,
        (int)pArc->nLeftRect,
        (int)pArc->nTopRect,
        (int)pArc->nRightRect + 1,
        (int)pArc->nBottomRect + 1));

    ODAdjustColor(pasPerson, &(pArc->BackColor), &BackColor, OD_BACK_COLOR);
    ODAdjustColor(pasPerson, &(pArc->PenColor), &PenColor, OD_PEN_COLOR);

    ODUseBkColor(pasPerson, fPalRGB, BackColor);
    ODUseBkMode(pasPerson, pArc->BackMode);

    ODUseROP2(pasPerson, pArc->ROP2);

    ODUsePen(pasPerson, fPalRGB, pArc->PenStyle, pArc->PenWidth,
        PenColor);

    ODUseArcDirection(pasPerson, pArc->ArcDirection);


    Arc(pasPerson->m_pView->m_usrDC,
        pArc->nLeftRect   - pasPerson->m_pView->m_dsScreenOrigin.x,
        pArc->nTopRect    - pasPerson->m_pView->m_dsScreenOrigin.y,
        pArc->nRightRect  - pasPerson->m_pView->m_dsScreenOrigin.x + 1,
        pArc->nBottomRect - pasPerson->m_pView->m_dsScreenOrigin.y + 1,
        pArc->nXStart     - pasPerson->m_pView->m_dsScreenOrigin.x,
        pArc->nYStart     - pasPerson->m_pView->m_dsScreenOrigin.y,
        pArc->nXEnd       - pasPerson->m_pView->m_dsScreenOrigin.x,
        pArc->nYEnd       - pasPerson->m_pView->m_dsScreenOrigin.y);


    DebugExitVOID(ASShare::ODReplayARC);
}



//
// ASShare::ODReplayCHORD()
// Replays CHORD order
//
void ASShare::ODReplayCHORD
(
    ASPerson *      pasPerson,
    LPCHORD_ORDER   pChord,
    BOOL            fPalRGB
)
{
    TSHR_COLOR      BackColor;
    TSHR_COLOR      ForeColor;
    TSHR_COLOR      PenColor;

    DebugEntry(ASShare::ODReplayCHORD);

    TRACE_OUT(("ORDER: Chord BC %08lX FC %08lX BM %04hX brush "
            "%02hX %02hX rop2 %04hX pen %04hX %04hX %08lX rect "
            "%d %d %d %d",
        pChord->BackColor,
        pChord->ForeColor,
        (TSHR_UINT16)pChord->BackMode,
        (TSHR_UINT16)pChord->BrushStyle,
        (TSHR_UINT16)pChord->BrushHatch,
        (TSHR_UINT16)pChord->ROP2,
        (TSHR_UINT16)pChord->PenStyle,
        (TSHR_UINT16)pChord->PenWidth,
        pChord->PenColor,
        (int)pChord->nLeftRect,
        (int)pChord->nTopRect,
        (int)pChord->nRightRect + 1,
        (int)pChord->nBottomRect + 1));


    ODAdjustColor(pasPerson, &(pChord->BackColor), &BackColor, OD_BACK_COLOR);
    ODAdjustColor(pasPerson, &(pChord->ForeColor), &ForeColor, OD_FORE_COLOR);
    ODAdjustColor(pasPerson, &(pChord->PenColor), &PenColor, OD_PEN_COLOR);

    ODUseBkColor(pasPerson, fPalRGB, BackColor);
    ODUseTextColor(pasPerson, fPalRGB, ForeColor);

    ODUseBkMode(pasPerson, pChord->BackMode);

    ODUseBrush(pasPerson, fPalRGB, pChord->BrushOrgX, pChord->BrushOrgY,
        pChord->BrushStyle, pChord->BrushHatch, ForeColor,
        pChord->BrushExtra);

    ODUseROP2(pasPerson, pChord->ROP2);

    ODUsePen(pasPerson, fPalRGB, pChord->PenStyle, pChord->PenWidth,
        PenColor);

    ODUseArcDirection(pasPerson, pChord->ArcDirection);


    Chord(pasPerson->m_pView->m_usrDC,
        pChord->nLeftRect   - pasPerson->m_pView->m_dsScreenOrigin.x,
        pChord->nTopRect    - pasPerson->m_pView->m_dsScreenOrigin.y,
        pChord->nRightRect  - pasPerson->m_pView->m_dsScreenOrigin.x + 1,
        pChord->nBottomRect - pasPerson->m_pView->m_dsScreenOrigin.y + 1,
        pChord->nXStart     - pasPerson->m_pView->m_dsScreenOrigin.x,
        pChord->nYStart     - pasPerson->m_pView->m_dsScreenOrigin.y,
        pChord->nXEnd       - pasPerson->m_pView->m_dsScreenOrigin.x,
        pChord->nYEnd       - pasPerson->m_pView->m_dsScreenOrigin.y);


    DebugExitVOID(ASShare::ODReplayCHORD);
}



//
// ASShare::ODReplayPOLYBEZIER()
// Replays POLYBEZIER order
//
void ASShare::ODReplayPOLYBEZIER
(
    ASPerson *          pasPerson,
    LPPOLYBEZIER_ORDER  pPolyBezier,
    BOOL                fPalRGB
)
{
    POINT               aP[ORD_MAX_POLYBEZIER_POINTS];
    UINT                i;
    UINT                cPoints;
    TSHR_COLOR          BackColor;
    TSHR_COLOR          ForeColor;
    TSHR_COLOR          PenColor;

    DebugEntry(ASShare::ODReplayPOLYBEZIER);

    cPoints = pPolyBezier->variablePoints.len /
        sizeof(pPolyBezier->variablePoints.aPoints[0]);

    TRACE_OUT(("ORDER: PolyBezier BC %08lX FC %08lX BM %04hX rop2 "
            "%04hX pen %04hX %04hX %08lX points %d",
        pPolyBezier->BackColor,
        pPolyBezier->ForeColor,
        (TSHR_UINT16)pPolyBezier->BackMode,
        (TSHR_UINT16)pPolyBezier->ROP2,
        (TSHR_UINT16)pPolyBezier->PenStyle,
        (TSHR_UINT16)pPolyBezier->PenWidth,
        pPolyBezier->PenColor,
        (int)cPoints));

    //
    // Apply DS origin offset ourselves (do not use transform)
    // while copying to native size point array.
    //
    for (i = 0; i < cPoints; i++)
    {
        TRACE_OUT(("aPoints[%u]: %d,%d",(UINT)i,
            (int)(pPolyBezier->variablePoints.aPoints[i].x),
            (int)(pPolyBezier->variablePoints.aPoints[i].y)));

        aP[i].x = pPolyBezier->variablePoints.aPoints[i].x -
           pasPerson->m_pView->m_dsScreenOrigin.x;
        aP[i].y = pPolyBezier->variablePoints.aPoints[i].y -
           pasPerson->m_pView->m_dsScreenOrigin.y;
    }

    ODAdjustColor(pasPerson, &(pPolyBezier->BackColor), &BackColor, OD_BACK_COLOR);
    ODAdjustColor(pasPerson, &(pPolyBezier->ForeColor), &ForeColor, OD_FORE_COLOR);
    ODAdjustColor(pasPerson, &(pPolyBezier->PenColor), &PenColor, OD_PEN_COLOR);

    ODUseBkColor(pasPerson, fPalRGB, BackColor);
    ODUseTextColor(pasPerson, fPalRGB, ForeColor);

    ODUseBkMode(pasPerson, pPolyBezier->BackMode);

    ODUseROP2(pasPerson, pPolyBezier->ROP2);

    ODUsePen(pasPerson, fPalRGB, pPolyBezier->PenStyle, pPolyBezier->PenWidth,
        PenColor);


    PolyBezier(pasPerson->m_pView->m_usrDC, aP, cPoints);


    DebugExitVOID(ASShare::ODReplayPOLYBEZIER);
}



//
// ASShare::ODReplayROUNDRECT()
//
void ASShare::ODReplayROUNDRECT
(
    ASPerson *          pasPerson,
    LPROUNDRECT_ORDER   pRoundRect,
    BOOL                fPalRGB
)
{
    TSHR_COLOR          BackColor;
    TSHR_COLOR          ForeColor;
    TSHR_COLOR          PenColor;

    DebugEntry(ASShare::ODReplayROUNDRECT);

    TRACE_OUT(("ORDER: RoundRect BC %08lX FC %08lX BM %04hX " \
            "brush %02hX %02hX rop2 %04hX pen %04hX %04hX " \
            "%08lX rect %d %d %d %d ellipse %d %d",
        pRoundRect->BackColor,
        pRoundRect->ForeColor,
        (TSHR_UINT16)pRoundRect->BackMode,
        (TSHR_UINT16)pRoundRect->BrushStyle,
        (TSHR_UINT16)pRoundRect->BrushHatch,
        (TSHR_UINT16)pRoundRect->ROP2,
        (TSHR_UINT16)pRoundRect->PenStyle,
        (TSHR_UINT16)pRoundRect->PenWidth,
        pRoundRect->PenColor,
        (int)pRoundRect->nLeftRect,
        (int)pRoundRect->nTopRect,
        (int)pRoundRect->nRightRect,
        (int)pRoundRect->nBottomRect,
        (int)pRoundRect->nEllipseWidth,
        (int)pRoundRect->nEllipseHeight));

    ODAdjustColor(pasPerson, &(pRoundRect->BackColor), &BackColor, OD_BACK_COLOR);
    ODAdjustColor(pasPerson, &(pRoundRect->ForeColor), &ForeColor, OD_FORE_COLOR);
    ODAdjustColor(pasPerson, &(pRoundRect->PenColor), &PenColor, OD_PEN_COLOR);

    ODUseBkColor(pasPerson, fPalRGB, BackColor);
    ODUseTextColor(pasPerson, fPalRGB, ForeColor);

    ODUseBkMode(pasPerson, pRoundRect->BackMode);

    ODUseBrush(pasPerson, fPalRGB, pRoundRect->BrushOrgX, pRoundRect->BrushOrgY,
        pRoundRect->BrushStyle, pRoundRect->BrushHatch, ForeColor,
        pRoundRect->BrushExtra);

    ODUseROP2(pasPerson, pRoundRect->ROP2);

    ODUsePen(pasPerson, fPalRGB, pRoundRect->PenStyle, pRoundRect->PenWidth,
        PenColor);


    //
    // Apply DS origin offset ourselves (do not use transform).
    //
    RoundRect(pasPerson->m_pView->m_usrDC,
        pRoundRect->nLeftRect  - pasPerson->m_pView->m_dsScreenOrigin.x,
        pRoundRect->nTopRect   - pasPerson->m_pView->m_dsScreenOrigin.y,
        pRoundRect->nRightRect - pasPerson->m_pView->m_dsScreenOrigin.x + 1,
        pRoundRect->nBottomRect- pasPerson->m_pView->m_dsScreenOrigin.y + 1,
        pRoundRect->nEllipseWidth,
        pRoundRect->nEllipseHeight);


    DebugExitVOID(ASShare::ODReplayROUNDRECT);
}



//
// ASShare::ODReplayLINETO()
// Replays LINETO order
//
void ASShare::ODReplayLINETO
(
    ASPerson *      pasPerson,
    LPLINETO_ORDER  pLineTo,
    BOOL            fPalRGB
)
{
    TSHR_COLOR      BackColor;
    TSHR_COLOR      PenColor;

    DebugEntry(ASShare::ODReplayLINETO);

    TRACE_OUT(("ORDER: LineTo BC %08lX BM %04X rop2 %04X pen " \
            "%04X %04X %08lX x1 %d y1 %d x2 %d y2 %d",
        pLineTo->BackColor,
        pLineTo->BackMode,
        pLineTo->ROP2,
        pLineTo->PenStyle,
        pLineTo->PenWidth,
        pLineTo->PenColor,
        pLineTo->nXStart,
        pLineTo->nYStart,
        pLineTo->nXEnd,
        pLineTo->nYEnd));

    ODAdjustColor(pasPerson, &(pLineTo->BackColor), &BackColor, OD_BACK_COLOR);
    ODAdjustColor(pasPerson, &(pLineTo->PenColor), &PenColor, OD_PEN_COLOR);

    ODUseBkColor(pasPerson, fPalRGB, BackColor);
    ODUseBkMode(pasPerson, pLineTo->BackMode);

    ODUseROP2(pasPerson, pLineTo->ROP2);
    ODUsePen(pasPerson, fPalRGB, pLineTo->PenStyle, pLineTo->PenWidth,
        PenColor);


    //
    // Apply DS origin offset ourselves (do not use transform)
    //
    MoveToEx(pasPerson->m_pView->m_usrDC,
        pLineTo->nXStart - pasPerson->m_pView->m_dsScreenOrigin.x,
        pLineTo->nYStart - pasPerson->m_pView->m_dsScreenOrigin.y,
        NULL);
    LineTo(pasPerson->m_pView->m_usrDC,
        pLineTo->nXEnd - pasPerson->m_pView->m_dsScreenOrigin.x,
        pLineTo->nYEnd - pasPerson->m_pView->m_dsScreenOrigin.y);


    DebugExitVOID(ASShare::ODReplayLINETO);
}



//
// ASShare::ODReplayEXTTEXTOUT()
// Replays EXTTEXTOUT order
//
void ASShare::ODReplayEXTTEXTOUT
(
    ASPerson *          pasPerson,
    LPEXTTEXTOUT_ORDER  pExtTextOut,
    BOOL                fPalRGB
)
{
    LPINT               lpDx;
    RECT                rect;

    DebugEntry(ASShare::ODReplayEXTTEXTOUT);

    ValidateView(pasPerson);

    //
    // Convert from TSHR_RECT32 to RECT we can manipulate
    // And convert to screen coords
    //
    rect.left = pExtTextOut->rectangle.left;
    rect.top  = pExtTextOut->rectangle.top;
    rect.right = pExtTextOut->rectangle.right;
    rect.bottom = pExtTextOut->rectangle.bottom;
    OffsetRect(&rect, -pasPerson->m_pView->m_dsScreenOrigin.x, -pasPerson->m_pView->m_dsScreenOrigin.y);

    //
    // Get pointers to the optional/variable parameters.
    //
    if (pExtTextOut->fuOptions & ETO_WINDOWS)
    {
        //
        // Make the rectangle exclusive for Windows to use.
        //
        rect.right++;
        rect.bottom++;
    }

    if (pExtTextOut->fuOptions & ETO_LPDX)
    {
        //
        // if OE2 encoding is in use, the 'variable' string is
        // in fact fixed at its maximum possible value, hence
        // deltaX is always in the same place.
        //
        if (m_oefOE2EncodingOn)
        {
            lpDx = (LPINT)(pExtTextOut->variableDeltaX.deltaX);
        }
        else
        {
            //
            // If OE2 encoding is not in use, the variable string is
            // truly variable, hence the position of deltaX depends
            // on the length of the string.
            //
            lpDx = (LPINT)( ((LPBYTE)pExtTextOut) +
                  FIELD_OFFSET(EXTTEXTOUT_ORDER, variableString.string) +
                  pExtTextOut->variableString.len +
                  sizeof(pExtTextOut->variableDeltaX.len) );
        }

        //
        // Note that deltaLen contains the number of bytes used
        // for the deltas, NOT the number of deltas.
        //

        //
        // THERE IS A BUG IN THE ORDER ENCODING - THE DELTA
        // LENGTH FIELD IS NOT ALWAYS SET UP CORRECTLY.  USE
        // THE STRING LENGTH INSTEAD.
        //
    }
    else
    {
        lpDx = NULL;
    }

    TRACE_OUT(( "ORDER: ExtTextOut %u %s",
        pExtTextOut->variableString.len,
        pExtTextOut->variableString.string));

    //
    // Call our internal routine to draw the text
    //
    ODDrawTextOrder(pasPerson,
        TRUE,           // ExtTextOut
        fPalRGB,
        &pExtTextOut->common,
        pExtTextOut->variableString.string,
        pExtTextOut->variableString.len,
        &rect,
        pExtTextOut->fuOptions,
        lpDx);


    DebugExitVOID(ASShare::ODReplayEXTTEXTOUT);
}



//
// ASShare::ODReplayTEXTOUT()
// Replays TEXTOUT order
//
void ASShare::ODReplayTEXTOUT
(
    ASPerson *          pasPerson,
    LPTEXTOUT_ORDER     pTextOut,
    BOOL                fPalRGB
)
{
    DebugEntry(ASShare::ODReplayTEXTOUT);

    TRACE_OUT(("ORDER: TextOut len %hu '%s' flags %04hx bc %08lX " \
            "fc %08lX bm %04hx",
        (TSHR_UINT16)(pTextOut->variableString.len),
        pTextOut->variableString.string,
        pTextOut->common.FontFlags,
        pTextOut->common.BackColor,
        pTextOut->common.ForeColor,
        pTextOut->common.BackMode));

    //
    // Call our internal routine to draw the text
    //
    ODDrawTextOrder(pasPerson,
        FALSE,          // Not ExtTextOut
        fPalRGB,
        &pTextOut->common,
        pTextOut->variableString.string,
        pTextOut->variableString.len,
        NULL,           // ExtTextOut specific
        0,              // ExtTextOut specific
        NULL);          // ExtTextOut specific


    DebugExitVOID(ASShare::ODReplayTEXTOUT);
}



//
// ASShare::ODReplayOPAQUERECT()
// Replays OPAQUERECT order
//
void ASShare::ODReplayOPAQUERECT
(
    ASPerson *          pasPerson,
    LPOPAQUERECT_ORDER  pOpaqueRect,
    BOOL                fPalRGB
)
{
    RECT                rect;
    TSHR_COLOR          ForeColor;

    DebugEntry(ASShare::ODReplayOPAQUERECT);

    TRACE_OUT(( "ORDER: OpaqueRect BC %08lX x %d y %d w %x h %d",
        pOpaqueRect->Color,
        (int)pOpaqueRect->nLeftRect,
        (int)pOpaqueRect->nTopRect,
        (int)pOpaqueRect->nWidth,
        (int)pOpaqueRect->nHeight));

    ODAdjustColor(pasPerson, &(pOpaqueRect->Color), &ForeColor, OD_FORE_COLOR);

    ODUseBkColor(pasPerson, fPalRGB, ForeColor);

    //
    // Apply DS origin offset ourselves (do not use transform)
    //
    rect.left   = pOpaqueRect->nLeftRect- pasPerson->m_pView->m_dsScreenOrigin.x;
    rect.top    = pOpaqueRect->nTopRect - pasPerson->m_pView->m_dsScreenOrigin.y;
    rect.right  = rect.left + pOpaqueRect->nWidth;
    rect.bottom = rect.top  + pOpaqueRect->nHeight;


    ExtTextOut(pasPerson->m_pView->m_usrDC, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);


    DebugExitVOID(ASShare::ODReplayOPAQUERECT);
}



//
// OD_ResetRectRegion()
//
void  ASShare::OD_ResetRectRegion(ASPerson * pasPerson)
{
    DebugEntry(ASShare::OD_ResetRectRegion);

    ValidateView(pasPerson);

    if (!pasPerson->m_pView->m_odRectReset)
    {
        SelectClipRgn(pasPerson->m_pView->m_usrDC, NULL);

        //
        // Indicate that the region is currently reset.
        //
        pasPerson->m_pView->m_odRectReset = TRUE;
    }

    DebugExitVOID(ASShare::OD_ResetRectRegion);
}



//
// ODUseFont()
//
void  ASShare::ODUseFont
(
    ASPerson *  pasPerson,
    LPSTR       pName,
    UINT        facelength,
    UINT        CodePage,
    UINT        MaxHeight,
    UINT        Height,
    UINT        Width,
    UINT        Weight,
    UINT        flags
)
{
    BOOL          rc  = TRUE;
    TEXTMETRIC*   pfm = NULL;
    UINT          textAlign;

    DebugEntry(ASShare::ODUseFont);

    ValidateView(pasPerson);

    //
    // If the baseline alignment flag has been set or cleared, change the
    // alignment in our surface (do this now before we reset the
    // odLastFontFlags variable).
    //
    if ((flags & NF_BASELINE) != (pasPerson->m_pView->m_odLastFontFlags & NF_BASELINE))
    {
        textAlign = GetTextAlign(pasPerson->m_pView->m_usrDC);
        if ((flags & NF_BASELINE) != 0)
        {
            //
            // We are setting the baseline alignment flag.  We have to
            // clear the top alignment flag and set the baseline flag (they
            // are mutually exclusive).
            //
            textAlign &= ~TA_TOP;
            textAlign |= TA_BASELINE;
        }
        else
        {
            //
            // We are clearing the baseline alignment flag.  We have to set
            // the top alignment flag and clear the baseline flag (they are
            // mutually exclusive).
            //
            textAlign |= TA_TOP;
            textAlign &= ~TA_BASELINE;
        }
        SetTextAlign(pasPerson->m_pView->m_usrDC, textAlign);
    }

    //
    // The font face string is NOT null terminated in the order data so we
    // must use strncmp.
    //
    if ((pasPerson->m_pView->m_odLastFontFaceLen != facelength                        ) ||
        (memcmp((LPSTR)pasPerson->m_pView->m_odLastFaceName,pName,facelength) != 0 ) ||
        (pasPerson->m_pView->m_odLastFontCodePage != CodePage   ) ||
        (pasPerson->m_pView->m_odLastFontHeight   != Height     ) ||
        (pasPerson->m_pView->m_odLastFontWidth    != Width      ) ||
        (pasPerson->m_pView->m_odLastFontWeight   != Weight     ) ||
        (pasPerson->m_pView->m_odLastFontFlags    != flags      ))
    {
        TRACE_OUT((
                 "Change font from %s (CodePage %d height %d width %d "    \
                     "weight %d flags %04X) to %s (CodePage %d height %d " \
                     "width %d weight %u flags %04X)",
                 pasPerson->m_pView->m_odLastFaceName,
                 pasPerson->m_pView->m_odLastFontCodePage,
                 pasPerson->m_pView->m_odLastFontHeight,
                 pasPerson->m_pView->m_odLastFontWidth,
                 pasPerson->m_pView->m_odLastFontWeight,
                 pasPerson->m_pView->m_odLastFontFlags,
                 pName,
                 CodePage,
                 Height,
                 Width,
                 Weight,
                 flags));

        memcpy(pasPerson->m_pView->m_odLastFaceName, pName, facelength);
        pasPerson->m_pView->m_odLastFontFaceLen          = facelength;
        pasPerson->m_pView->m_odLastFaceName[facelength] = '\0';
        pasPerson->m_pView->m_odLastFontCodePage         = CodePage;
        pasPerson->m_pView->m_odLastFontHeight           = Height;
        pasPerson->m_pView->m_odLastFontWidth            = Width;
        pasPerson->m_pView->m_odLastFontWeight           = Weight;
        pasPerson->m_pView->m_odLastFontFlags            = flags;

        rc = USR_UseFont(pasPerson->m_pView->m_usrDC, &pasPerson->m_pView->m_odLastFontID,
                pfm, (LPSTR)pasPerson->m_pView->m_odLastFaceName, CodePage, MaxHeight,
                Height, Width, Weight, flags);
    }
    else
    {
        //
        // The font hasn't changed.  But we must still select it in since
        // both OD2 and OD code select in fonts.
        //
        ASSERT(pasPerson->m_pView->m_odLastFontID != NULL);
        SelectFont(pasPerson->m_pView->m_usrDC, pasPerson->m_pView->m_odLastFontID);
    }

    DebugExitVOID(ASShare::ODUseFont);
}

//
// FUNCTION: ASShare::ODUseRectRegion
//
// DESCRIPTION:
//
// Set the clipping rectangle in the ScreenBitmap to the given rectangle.
// The values passed are inclusive.
//
// PARAMETERS:
//
void  ASShare::ODUseRectRegion
(
    ASPerson *  pasPerson,
    int         left,
    int         top,
    int         right,
    int         bottom
)
{
    POINT   aPoints[2];
    HRGN    hrgnRect;

    DebugEntry(ASShare::ODUseRectRegion);

    ValidateView(pasPerson);

    // Adjust for 2.x desktop scrolling
    left   -= pasPerson->m_pView->m_dsScreenOrigin.x;
    top    -= pasPerson->m_pView->m_dsScreenOrigin.y;
    right  -= pasPerson->m_pView->m_dsScreenOrigin.x;
    bottom -= pasPerson->m_pView->m_dsScreenOrigin.y;

    if ((pasPerson->m_pView->m_odRectReset)            ||
        (left   != pasPerson->m_pView->m_odLastLeft)   ||
        (top    != pasPerson->m_pView->m_odLastTop)    ||
        (right  != pasPerson->m_pView->m_odLastRight)  ||
        (bottom != pasPerson->m_pView->m_odLastBottom))
    {
        //
        // The region clip rectangle has changed, so we change the region
        // in the screen bitmap DC.
        //
        aPoints[0].x = left;
        aPoints[0].y = top;
        aPoints[1].x = right;
        aPoints[1].y = bottom;

        //
        // Windows requires that the coordinates are in DEVICE values for
        // its SelectClipRgn call.
        //
        LPtoDP(pasPerson->m_pView->m_usrDC, aPoints, 2);

        if ((left > right) || (top > bottom))
        {
            //
            // We get this for SaveScreenBitmap orders.  SFR5292
            //
            TRACE_OUT(( "Null bounds of region rect"));
            hrgnRect = CreateRectRgn(0, 0, 0, 0);
        }
        else
        {
            // We must add one to right & bottom since coords were inclusive
            hrgnRect = CreateRectRgn( aPoints[0].x,
                               aPoints[0].y,
                               aPoints[1].x+1,
                               aPoints[1].y+1);

        }
        SelectClipRgn(pasPerson->m_pView->m_usrDC, hrgnRect);

        pasPerson->m_pView->m_odLastLeft   = left;
        pasPerson->m_pView->m_odLastTop    = top;
        pasPerson->m_pView->m_odLastRight  = right;
        pasPerson->m_pView->m_odLastBottom = bottom;
        pasPerson->m_pView->m_odRectReset = FALSE;

        if (hrgnRect != NULL)
        {
            DeleteRgn(hrgnRect);
        }
    }

    DebugExitVOID(ASShare::ODUseRectRegion);
}


//
// ODUseBrush creates the correct brush to use.  NB.  We rely on
// UseTextColor and UseBKColor being called before this routine to set up
// pasPerson->m_pView->m_odLastTextColor and pasPerson->m_pView->m_odLastBkColor correctly.
//
void  ASShare::ODUseBrush
(
    ASPerson *      pasPerson,
    BOOL            fPalRGB,
    int             x,
    int             y,
    UINT            Style,
    UINT            Hatch,
    TSHR_COLOR      Color,
    BYTE            Extra[7]
)
{
    HBRUSH hBrushNew = NULL;

    DebugEntry(ASShare::ODUseBrush);

    // Reset the origin
    if ((x != pasPerson->m_pView->m_odLastBrushOrgX) ||
        (y != pasPerson->m_pView->m_odLastBrushOrgY))
    {
        SetBrushOrgEx(pasPerson->m_pView->m_usrDC, x, y, NULL);

        // Update saved brush org
        pasPerson->m_pView->m_odLastBrushOrgX = x;
        pasPerson->m_pView->m_odLastBrushOrgY = y;
    }

    if ((Style != pasPerson->m_pView->m_odLastLogBrushStyle)               ||
        (Hatch != pasPerson->m_pView->m_odLastLogBrushHatch)               ||
        (memcmp(&Color, &pasPerson->m_pView->m_odLastLogBrushColor, sizeof(Color))) ||
        (memcmp(Extra,pasPerson->m_pView->m_odLastLogBrushExtra,sizeof(pasPerson->m_pView->m_odLastLogBrushExtra))) ||
        ((pasPerson->m_pView->m_odLastLogBrushStyle == BS_PATTERN)      &&
           ((pasPerson->m_pView->m_odLastTextColor != pasPerson->m_pView->m_odLastBrushTextColor) ||
            (pasPerson->m_pView->m_odLastBkColor   != pasPerson->m_pView->m_odLastBrushBkColor))))
    {
        pasPerson->m_pView->m_odLastLogBrushStyle = Style;
        pasPerson->m_pView->m_odLastLogBrushHatch = Hatch;
        pasPerson->m_pView->m_odLastLogBrushColor = Color;
        memcpy(pasPerson->m_pView->m_odLastLogBrushExtra, Extra, sizeof(pasPerson->m_pView->m_odLastLogBrushExtra));

        if (pasPerson->m_pView->m_odLastLogBrushStyle == BS_PATTERN)
        {
            //
            // A pattern from a bitmap is required.
            //
            if (pasPerson->m_pView->m_odLastBrushPattern == NULL)
            {
                TRACE_OUT(( "Creating bitmap to use for brush setup"));

                pasPerson->m_pView->m_odLastBrushPattern = CreateBitmap(8,8,1,1,NULL);
            }

            if (pasPerson->m_pView->m_odLastBrushPattern != NULL)
            {
                char      lpBits[16];

                //
                // Place the bitmap bits into an array of bytes in the
                // correct form for SetBitmapBits which uses 16 bits per
                // scanline.
                //
                lpBits[14] = (char)Hatch;
                lpBits[12] = Extra[0];
                lpBits[10] = Extra[1];
                lpBits[8]  = Extra[2];
                lpBits[6]  = Extra[3];
                lpBits[4]  = Extra[4];
                lpBits[2]  = Extra[5];
                lpBits[0]  = Extra[6];

                SetBitmapBits(pasPerson->m_pView->m_odLastBrushPattern,8*2,lpBits);

                hBrushNew = CreatePatternBrush(pasPerson->m_pView->m_odLastBrushPattern);
                if (hBrushNew == NULL)
                {
                    ERROR_OUT(( "Failed to create pattern brush"));
                }
                else
                {
                    pasPerson->m_pView->m_odLastBrushTextColor = pasPerson->m_pView->m_odLastTextColor;
                    pasPerson->m_pView->m_odLastBrushBkColor   = pasPerson->m_pView->m_odLastBkColor;
                }
            }
        }
        else
        {
            LOGBRUSH        logBrush;

            logBrush.lbStyle = pasPerson->m_pView->m_odLastLogBrushStyle;
            logBrush.lbHatch = pasPerson->m_pView->m_odLastLogBrushHatch;
            logBrush.lbColor = ODCustomRGB(pasPerson->m_pView->m_odLastLogBrushColor.red,
                                           pasPerson->m_pView->m_odLastLogBrushColor.green,
                                           pasPerson->m_pView->m_odLastLogBrushColor.blue,
                                           fPalRGB);
            hBrushNew = CreateBrushIndirect(&logBrush);
        }

        if (hBrushNew == NULL)
        {
            ERROR_OUT(( "Failed to create brush"));
        }
        else
        {
            TRACE_OUT(( "Selecting new brush 0x%08x", hBrushNew));
            DeleteBrush(SelectBrush(pasPerson->m_pView->m_usrDC, hBrushNew));
        }
    }

    DebugExitVOID(ASShare::ODUseBrush);
}



//
// ODDrawTextOrder()
// Common text order playback code for EXTTEXTOUT and TEXTOUT
//
void ASShare::ODDrawTextOrder
(
    ASPerson *          pasPerson,
    BOOL                isExtTextOut,
    BOOL                fPalRGB,
    LPCOMMON_TEXTORDER  pCommon,
    LPSTR               pText,
    UINT                textLength,
    LPRECT              pExtRect,
    UINT                extOptions,
    LPINT               pExtDx
)
{
    LPSTR               faceName;
    UINT                faceNameLength;
    UINT                maxFontHeight;
    TSHR_UINT16         nFontFlags;
    TSHR_UINT16         nCodePage;
    TSHR_COLOR          BackColor;
    TSHR_COLOR          ForeColor;

    DebugEntry(ASShare::ODDrawTextOrder);

    ODAdjustColor(pasPerson, &(pCommon->BackColor), &BackColor, OD_BACK_COLOR);
    ODAdjustColor(pasPerson, &(pCommon->ForeColor), &ForeColor, OD_FORE_COLOR);

    ODUseTextBkColor(pasPerson, fPalRGB, BackColor);
    ODUseTextColor(pasPerson, fPalRGB, ForeColor);

    ODUseBkMode(pasPerson, pCommon->BackMode);

    ODUseTextCharacterExtra(pasPerson, pCommon->CharExtra);
    ODUseTextJustification(pasPerson, pCommon->BreakExtra, pCommon->BreakCount);

    faceName = FH_GetFaceNameFromLocalHandle(pCommon->FontIndex,
                                             &faceNameLength);

    maxFontHeight = FH_GetMaxHeightFromLocalHandle(pCommon->FontIndex);

    //
    // Get the local font flags for the font, so that we can merge in any
    // specific local flag information when setting up the font.  The prime
    // example of this is whether the local font we matched is TrueType or
    // not, which information is not sent over the wire, but does need to
    // be used when setting up the font - or else we may draw using a local
    // fixed font of the same facename.
    //
    nFontFlags = (TSHR_UINT16)FH_GetFontFlagsFromLocalHandle(pCommon->FontIndex);

    //
    // Get the local CodePage for the font.
    //
    nCodePage = (TSHR_UINT16)FH_GetCodePageFromLocalHandle(pCommon->FontIndex);

    ODUseFont(pasPerson, faceName, faceNameLength, nCodePage,
        maxFontHeight, pCommon->FontHeight, pCommon->FontWidth,
        pCommon->FontWeight, pCommon->FontFlags | (nFontFlags & NF_LOCAL));

    //
    // Make the call.
    //
    if (isExtTextOut)
    {
        //
        // Apply DS origin offset ourselves (do not use transform)
        //
        ExtTextOut(pasPerson->m_pView->m_usrDC,
                  pCommon->nXStart - pasPerson->m_pView->m_dsScreenOrigin.x,
                  pCommon->nYStart - pasPerson->m_pView->m_dsScreenOrigin.y,
                  extOptions & ETO_WINDOWS,
                  pExtRect,
                  pText,
                  textLength,
                  pExtDx);
    }
    else
    {
        //
        // Apply DS origin offset ourselves (do not use transform)
        //
        TextOut(pasPerson->m_pView->m_usrDC,
                pCommon->nXStart - pasPerson->m_pView->m_dsScreenOrigin.x,
                pCommon->nYStart - pasPerson->m_pView->m_dsScreenOrigin.y,
                pText,
                textLength);
    }


    DebugExitVOID(ASShare::ODDrawTextOrder);
}



//
// ODAdjustColor()
//
// Used for playback on 4bpp devices.  We convert colors that are 'close'
// to VGA to their VGA equivalents.
//
// This function tries to find a close match in the VGA color set for a
// given input color.  Close is defined as follows: each color element
// (red, green, blue) must be within 7 of the corresponding element in a
// VGA color, without wrapping.  For example
//
// - 0xc7b8c6 is 'close' to 0xc0c0c0
//
// - 0xf8f8f8 is 'close' to 0xffffff
//
// - 0xff0102 is not 'close' to 0x000000, but is 'close' to 0xff0000
//
// Closeness is determined as follows:
//
// - for each entry in the table s_odVGAColors
//   - ADD the addMask to the color
//   - AND the result with the andMask
//   - if the result equals the testMask, this VGA color is close match
//
// Think about it.  It works.
//
//
void ASShare::ODAdjustColor
(
    ASPerson *          pasPerson,
    const TSHR_COLOR *  pColorIn,
    LPTSHR_COLOR        pColorOut,
    int                 type
)
{
    int         i;
    COLORREF    color;
    COLORREF    work;

    DebugEntry(ASShare::ODAdjustColor);

    *pColorOut = *pColorIn;

    if (g_usrScreenBPP > 4)
    {
        // Nothing to convert; bail out
        DC_QUIT;
    }

    //
    // Convert the color to a single integer
    //
    color = (pColorOut->red << 16) + (pColorOut->green << 8) + pColorOut->blue;

    //
    // See if this is the same as the last call of this type
    //
    if (color == pasPerson->m_pView->m_odLastVGAColor[type])
    {
        *pColorOut = pasPerson->m_pView->m_odLastVGAResult[type];
        TRACE_OUT(("Same as last %s color",
                (type == OD_BACK_COLOR ? "background" :
                type == OD_FORE_COLOR ? "foreground" : "pen")));
        DC_QUIT;
    }


    //
    // Scan the table for a close match.
    //
    for (i = 0; i < 16; i++)
    {
        //
        // Check for a close match.  Don't bother to look for an exact
        // match, as that is caught by this code.  The trade off is between
        // - an additional test and jump in non-exact cases
        // - an 'add' and an 'and' in the exact case.
        //
        work = color;
        work += s_odVGAColors[i].addMask;
        work &= s_odVGAColors[i].andMask;
        if (work == s_odVGAColors[i].testMask)
        {
            TRACE_OUT(( "%#6.6lx is close match for %#6.6lx (%s)",
                s_odVGAColors[i].color, color,
                type == OD_BACK_COLOR ? "background" :
                type == OD_FORE_COLOR ? "foreground" : "pen"));
            *pColorOut = s_odVGAColors[i].result;
            break;
        }
    }

    if (i == 16)
    {
        TRACE_OUT(( "No close VGA match found for %#6.6lx (%s)",
            color,
            type == OD_BACK_COLOR ? "background" :
            type == OD_FORE_COLOR ? "foreground" : "pen"));
    }

    //
    // Save the result for next time.
    //
    pasPerson->m_pView->m_odLastVGAColor[type] = color;
    pasPerson->m_pView->m_odLastVGAResult[type] = *pColorOut;

DC_EXIT_POINT:
    DebugExitVOID(ASShare::ODAdjustColor);
}


//
// LITTLE ASShare::ODUse() functions
//

//
// ASShare::ODUseTextBkColor()
//
void ASShare::ODUseTextBkColor
(
    ASPerson *  pasPerson,
    BOOL        fPalRGB,
    TSHR_COLOR  color
)
{
    COLORREF    rgb;

    ValidateView(pasPerson);

    rgb = ODCustomRGB(color.red, color.green, color.blue, fPalRGB);
    SetBkColor(pasPerson->m_pView->m_usrDC, rgb);

    // Update BK COLOR cache
    pasPerson->m_pView->m_odLastBkColor = rgb;
}


//
// ASShare::ODUseBkColor()
//
void ASShare::ODUseBkColor
(
    ASPerson *  pasPerson,
    BOOL        fPalRGB,
    TSHR_COLOR  color
)
{
    COLORREF    rgb;

    ValidateView(pasPerson);

    rgb = ODCustomRGB(color.red, color.green, color.blue, fPalRGB);
    if (rgb != pasPerson->m_pView->m_odLastBkColor)
    {
        SetBkColor(pasPerson->m_pView->m_usrDC, rgb);

        // Update BK COLOR cache
        pasPerson->m_pView->m_odLastBkColor = rgb;
    }
}


//
// ASShare::ODUseTextColor()
//
void ASShare::ODUseTextColor
(
    ASPerson *  pasPerson,
    BOOL        fPalRGB,
    TSHR_COLOR  color
)
{
    COLORREF    rgb;

    ValidateView(pasPerson);

    rgb = ODCustomRGB(color.red, color.green, color.blue, fPalRGB);
    if (rgb != pasPerson->m_pView->m_odLastTextColor)
    {
        SetTextColor(pasPerson->m_pView->m_usrDC, rgb);

        // Update TEXT COLOR cache
        pasPerson->m_pView->m_odLastTextColor = rgb;
    }
}


//
// ASShare::ODUseBkMode()
//
void ASShare::ODUseBkMode(ASPerson * pasPerson, int mode)
{
    if (mode != pasPerson->m_pView->m_odLastBkMode)
    {
        SetBkMode(pasPerson->m_pView->m_usrDC, mode);

        // Update BK MODE cache
        pasPerson->m_pView->m_odLastBkMode = mode;
    }
}



//
// ASShare::ODUsePen()
//
void ASShare::ODUsePen
(
    ASPerson *      pasPerson,
    BOOL            fPalRGB,
    UINT            style,
    UINT            width,
    TSHR_COLOR      color
)
{
    HPEN            hPenNew;
    COLORREF        rgb;

    ValidateView(pasPerson);

    rgb = ODCustomRGB(color.red, color.green, color.blue, fPalRGB);

    if ((style != pasPerson->m_pView->m_odLastPenStyle)   ||
        (rgb   != pasPerson->m_pView->m_odLastPenColor)   ||
        (width != pasPerson->m_pView->m_odLastPenWidth))
    {
        hPenNew = CreatePen(style, width, rgb);

        DeletePen(SelectPen(pasPerson->m_pView->m_usrDC, hPenNew));

        // Update PEN cache
        pasPerson->m_pView->m_odLastPenStyle = style;
        pasPerson->m_pView->m_odLastPenColor = rgb;
        pasPerson->m_pView->m_odLastPenWidth = width;
    }
}


//
// ASShare::ODUseROP2()
//
void ASShare::ODUseROP2(ASPerson * pasPerson, int rop2)
{
    if (rop2 != pasPerson->m_pView->m_odLastROP2)
    {
        SetROP2(pasPerson->m_pView->m_usrDC, rop2);

        // Update ROP2 cache
        pasPerson->m_pView->m_odLastROP2 = rop2;
    }
}


//
// ASShare::ODUseTextCharacterExtra()
//
void ASShare::ODUseTextCharacterExtra(ASPerson * pasPerson, int extra)
{
    if (extra != pasPerson->m_pView->m_odLastCharExtra)
    {
        SetTextCharacterExtra(pasPerson->m_pView->m_usrDC, extra);

        // Update TEXT EXTRA cache
        pasPerson->m_pView->m_odLastCharExtra = extra;
    }
}



//
// ASShare::ODUseTextJustification()
//
void ASShare::ODUseTextJustification(ASPerson * pasPerson, int extra, int count)
{
    if ((extra != pasPerson->m_pView->m_odLastJustExtra) ||
        (count != pasPerson->m_pView->m_odLastJustCount))
    {
        SetTextJustification(pasPerson->m_pView->m_usrDC, extra, count);

        // Update TEXT JUST cache
        pasPerson->m_pView->m_odLastJustExtra = extra;
        pasPerson->m_pView->m_odLastJustCount = count;
    }
}


//
// ASShare::ODUseFillMode()
//
void ASShare::ODUseFillMode(ASPerson * pasPerson, UINT mode)
{
    if (mode != pasPerson->m_pView->m_odLastFillMode)
    {
        SetPolyFillMode(pasPerson->m_pView->m_usrDC, (mode == ORD_FILLMODE_WINDING) ?
            WINDING : ALTERNATE);

        // Update FILL MODE cache
        pasPerson->m_pView->m_odLastFillMode = mode;
    }
}


//
// ASShare::ODUseArcDirection()
//
void ASShare::ODUseArcDirection(ASPerson * pasPerson, UINT dir)
{
    if (dir != pasPerson->m_pView->m_odLastArcDirection)
    {
        SetArcDirection(pasPerson->m_pView->m_usrDC, (dir == ORD_ARC_CLOCKWISE) ?
            AD_CLOCKWISE : AD_COUNTERCLOCKWISE);

        // Update ARC DIR cache
        pasPerson->m_pView->m_odLastArcDirection = dir;
    }
}

