/******************************Module*Header*******************************\
* Module Name: xformgdi.cxx
*
* Contains all the mapping and coordinate functions.
*
* Created: 09-Nov-1990 16:49:36
* Author: Wendy Wu [wendywu]
*
* Copyright (c) 1990-1999 Microsoft Corporation
\**************************************************************************/

#include "precomp.hxx"

#define UNITS_PER_MILLIMETER_LOMETRIC     10    // .1 mm/unit
#define UNITS_PER_MILLIMETER_HIMETRIC    100    // .01 mm/unit
#define UNITS_PER_METER_LOENGLISH       3937    // (100 units/in) / (0.0254 m/in)
#define UNITS_PER_METER_HIENGLISH      39370    // (1000 units/in) / (0.0254 mm/in)
#define UNITS_PER_METER_TWIPS          56693    // (1440 units/in) / (0.0254 mm/in)

#if DBG
int giXformLevel = 0;
#endif

extern "C" BOOL
ProbeAndConvertXFORM(
      XFORML     *kpXform,
      XFORML     *pXform
      );



/******************************Public*Routine******************************\
* GreGetMapMode(hdc)
*
* Get the mapping mode of the specified dc.
*
* History:
*  09-Nov-1990 -by- Wendy Wu [wendywu]
* Wrote it.
\**************************************************************************/

int APIENTRY GreGetMapMode(
    HDC hdc)
{
    DWORD dw = 0;
    XDCOBJ dco( hdc );

    if(dco.bValid())
    {
        dw = dco.ulMapMode();
        dco.vUnlockFast();
    }
    else
    {
        WARNING("Invalid DC passed to GreGetMapMode\n");
    }

    return(dw);
}

/******************************Public*Routine******************************\
* GreGetViewportExt(hdc,pSize)
*
* Get the viewport extents of the specified dc.
*
* History:
*  09-Nov-1990 -by- Wendy Wu [wendywu]
* Wrote it.
\**************************************************************************/

BOOL APIENTRY GreGetViewportExt(
    HDC     hdc,
    PSIZE   pSize)
{
    return(GreGetDCPoint(hdc,DCPT_VPEXT,(PPOINTL)pSize));
}

/******************************Public*Routine******************************\
* GreGetViewportOrg(hdc,pPoint)
*
* Get the viewport origin of the specified dc.
*
* History:
*  09-Nov-1990 -by- Wendy Wu [wendywu]
* Wrote it.
\**************************************************************************/

BOOL APIENTRY GreGetViewportOrg(
    HDC     hdc,
    LPPOINT pPoint)
{
    return(GreGetDCPoint(hdc,DCPT_VPORG,(PPOINTL)pPoint));
}

/******************************Public*Routine******************************\
* GreGetWindowExt(hdc,pSize)
*
* Get the window extents of the specified dc.
*
* History:
*  09-Nov-1990 -by- Wendy Wu [wendywu]
* Wrote it.
\**************************************************************************/

BOOL APIENTRY GreGetWindowExt(
    HDC     hdc,
    PSIZE   pSize)
{
    return(GreGetDCPoint(hdc,DCPT_WNDEXT,(PPOINTL)pSize));
}

/******************************Public*Routine******************************\
* GreGetWindowOrg(hdc,pPoint)
*
* Get the window origin of the specified dc.
*
* History:
*  09-Nov-1990 -by- Wendy Wu [wendywu]
* Wrote it.
\**************************************************************************/

BOOL APIENTRY GreGetWindowOrg(
    HDC     hdc,
    LPPOINT pPoint)
{
    return(GreGetDCPoint(hdc,DCPT_WNDORG,(PPOINTL)pPoint));
}

/******************************Public*Routine******************************\
* GreSetViewportOrg(hdc,x,y,pPoint)
*
* Set the viewport origin of the specified dc.
*
*  15-Sep-1992 -by- Gerrit van Wingerden [gerritv]
* Modified since xforms have moved to client side and this routine is
* now only called by usersrv.
*
* History:
*  09-Nov-1990 -by- Wendy Wu [wendywu]
* Wrote it.
\**************************************************************************/

BOOL APIENTRY GreSetViewportOrg(
 HDC     hdc,
 int     x,
 int     y,
LPPOINT pPoint)
{


    DCOBJ    dcox(hdc);                 // lock the dc object

    if (!dcox.bValid())                 // check if lock is valid
        return(FALSE);

    if (MIRRORED_DC(dcox.pdc))
        x = -x;

    if (BLTOFXOK(x) && BLTOFXOK(y))
    {
        if (!dcox.pdc->bValidPtlCurrent())
        {
            ASSERTGDI(dcox.pdc->bValidPtfxCurrent(), "Both CPs invalid?");

            EXFORMOBJ exoDtoW(dcox, DEVICE_TO_WORLD);

            if (exoDtoW.bValid())
                exoDtoW.bXform(&dcox.ptfxCurrent(), &dcox.ptlCurrent(), 1);

            dcox.pdc->vValidatePtlCurrent();
        }

    // After the transform, the device space CP will be invalid:

        dcox.pdc->vInvalidatePtfxCurrent();

    // x, y of viewport origin can fit into 28 bits.

    // If we get here it means we've been called by USER and Viewport
    // and Window extents should be (1,1) and Window Orgs should be zero,
    // and the world transform shouldn't be set either.

        DONTUSE(pPoint);    // pPoint is now ignored

        EXFORMOBJ xoWtoD(dcox, WORLD_TO_DEVICE);
        EFLOATEXT efDx((LONG) x);
        EFLOATEXT efDy((LONG) y);

        efDx.vTimes16();
        efDy.vTimes16();
        
        ASSERTGDI(xoWtoD.efM11().bIs16() ||
                  (MIRRORED_DC(dcox.pdc) && xoWtoD.efM11().bIsNeg16()),
                  "efM11 not 16 in GreSetViewportOrg or -16 in Mirroring Mode" );
        ASSERTGDI( xoWtoD.efM22().bIs16(),
                   "efM22 not 16 in GreSetViewportOrg" );

        dcox.pdc->flSet_flXform( DEVICE_TO_WORLD_INVALID | PAGE_XLATE_CHANGED);
        //xoWtoD.vSetTranslations( efDx, efDy );
        dcox.pdc->lViewportOrgX(x);
        dcox.pdc->lViewportOrgY(y);

        xoWtoD.vInit(dcox, DEVICE_TO_WORLD);

        return(TRUE);

    }
    else
        return(FALSE);
}


/******************************Public*Routine******************************\
* GreSetWindowOrg(hdc,x,y,pPoint)
*
* Set the window origin of the specified dc.
*
* History:
*  09-Nov-1990 -by- Wendy Wu [wendywu]
* Wrote it.
\**************************************************************************/

BOOL APIENTRY GreSetWindowOrg(
 HDC     hdc,
 int     x,
 int     y,
LPPOINT pPoint)
{


    DCOBJ    dcox(hdc);                 // lock the dc object

    if (!dcox.bValid())                 // check if lock is valid
        return(FALSE);

// If we get here it means we've been called by USER and Viewport
// and Window extents should be (1,1) and Viewport Orgs should be zero,
// and the world transform shouldn't be set either.

    DONTUSE(pPoint);    // pPoint is now ignored

    EXFORMOBJ xoWtoD(dcox, WORLD_TO_DEVICE);
    EFLOATEXT efDx((LONG) -x);
    EFLOATEXT efDy((LONG) -y);

    efDx.vTimes16();
    efDy.vTimes16();

    ASSERTGDI( xoWtoD.efM11().bIs16() ||
               (MIRRORED_DC(dcox.pdc) && xoWtoD.efM11().bIsNeg16()),
               "efM11 not 16 in GreSetViewportOrg" );

    ASSERTGDI( xoWtoD.efM22().bIs16(),
               "efM22 not 16 in GreSetViewportOrg" );

    if (!dcox.pdc->bValidPtlCurrent())
    {
        ASSERTGDI(dcox.pdc->bValidPtfxCurrent(), "Both CPs invalid?");

        EXFORMOBJ exoDtoW(dcox, DEVICE_TO_WORLD);

        if (exoDtoW.bValid())
            exoDtoW.bXform(&dcox.ptfxCurrent(), &dcox.ptlCurrent(), 1);

        dcox.pdc->vValidatePtlCurrent();
    }

// After the transform, the device space CP will be invalid:

    dcox.pdc->vInvalidatePtfxCurrent();

    dcox.pdc->flSet_flXform( DEVICE_TO_WORLD_INVALID | PAGE_XLATE_CHANGED);
    //xoWtoD.vSetTranslations( efDx, efDy );

    //
    // save in DC for USER. Caution: these valuse must be restored before app
    // uses DC again
    //

    dcox.pdc->lWindowOrgX(x);
    dcox.pdc->lWindowOrgY(y);
    dcox.pdc->SetWindowOrgAndMirror(x);

    xoWtoD.vInit(dcox, DEVICE_TO_WORLD);

    return( TRUE );

}


/******************************Public*Routine******************************\
* NtGdiConvertMetafileRect:
*
*   Transform a RECT from inclusive-exclusive to inclusive-inclusive for
*   a rectangle recorded in a metafile.
*
* Arguments:
*
*   hdc   - current device context
*   rect  - rtectangle to convert
*
* Return Value:
*
*   MRI_OK,MRI_NULLBOX,MRI_ERROR
*
* History:
*
*    9-Apr-1996 -by- Mark Enstrom [marke]
*
\**************************************************************************/

LONG APIENTRY
NtGdiConvertMetafileRect(
    HDC    hdc,
    PRECTL prect
    )
{
    LONG lResult = MRI_ERROR;
    RECTL rclMeta;

    //
    // copy in rect structure
    //

    __try
    {
        rclMeta = ProbeAndReadStructure(prect,RECTL);
        lResult = MRI_OK;
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        WARNINGX(22);
    }

    if (lResult == MRI_OK)
    {
        //
        //  attempt to lock DC
        //

        DCOBJ  dco(hdc);

        if (dco.bValid())
        {
            ERECTFX rectFX;
            BOOL bResult;

            //
            // DC must be in compatible mode
            //

            ASSERTGDI(dco.pdc->iGraphicsMode() == GM_COMPATIBLE,
                        "NtGdiConvertMetafileRect: Map Mode is not GM_COMPATIBLE");

            //
            // transform rectangle points to device FX
            //

            {
                EXFORMOBJ xo(dco,XFORM_WORLD_TO_DEVICE);

                bResult = xo.bXform((PPOINTL)&rclMeta,(PPOINTFIX)&rectFX,2);
            }

            if (bResult)
            {
                //
                // order device point rectangle
                //

                rectFX.vOrder();

                //
                // adjust lower and right points for exclusive to inclusive
                //

                rectFX.xRight  -= 16;
                rectFX.yBottom -= 16;

                //
                // check for empty rectFX
                //

                if ((rectFX.xRight  < rectFX.xLeft) ||
                    (rectFX.yBottom < rectFX.yTop))
                {
                    lResult = MRI_NULLBOX;
                }

                //
                // convert back to logical space
                //

                EXFORMOBJ xoDev(dco,XFORM_DEVICE_TO_WORLD);

                bResult = xoDev.bXform((PPOINTFIX)&rectFX,(PPOINTL)&rclMeta,2);

                //
                // Write results to caller's buffer
                //

                if (bResult)
                {
                    __try
                    {
                        ProbeAndWriteStructure(prect,rclMeta,RECT);
                    }
                    __except(EXCEPTION_EXECUTE_HANDLER)
                    {
                        WARNINGX(8);
                        lResult = MRI_ERROR;
                    }
                }
                else
                {
                    lResult = MRI_ERROR;
                }
            }
            else
            {
                lResult = MRI_ERROR;
            }
        }
        else
        {
            lResult = MRI_ERROR;
        }
    }

    if (lResult == MRI_ERROR)
    {
        SAVE_ERROR_CODE(ERROR_INVALID_PARAMETER);
    }

    return(lResult);
}

/******************************Public*Routine******************************\
*
*
* History:
*  02-Dec-1994 -by-  Eric Kutter [erick]
* Wrote it.
\**************************************************************************/

BOOL APIENTRY GreTransformPoints(
    HDC    hdc,
    PPOINT pptIn,
    PPOINT pptOut,
    int    c,
    int    iMode
    )
{
    XFORMPRINT(1,"GreTransformPoints, iMode = %ld\n",iMode);

    BOOL bResult = FALSE;

    DCOBJ  dco(hdc);                    // lock the dc object

    if (dco.bValid())                  // check if lock is valid
    {
        if (c <= 0)                    // check if there are points to convert
        {
            bResult = TRUE;
        }
        else
        {
            EXFORMOBJ xo(dco, (iMode == XFP_DPTOLP) ? XFORM_DEVICE_TO_WORLD : XFORM_WORLD_TO_DEVICE);

            if (xo.bValid())
            {
                switch (iMode)
                {
                case XFP_DPTOLP:
                case XFP_LPTODP:
                    bResult = xo.bXform((PPOINTL)pptIn, (PPOINTL)pptOut, c);
                    break;

                case XFP_LPTODPFX:
                    bResult = xo.bXform((PPOINTL)pptIn, (PPOINTFIX)pptOut, c);
                    break;

                default:
                    WARNING("Invalid mode passed to GreTranformPoints\n");
                    break;
                }
            }
        }
    }
    return(bResult);
}

/******************************Public*Routine******************************\
* GreDPtoLP(hdc,ppt,nCount)
*
* Convert the given device points into logical points.
*
* History:
*  09-Nov-1990 -by- Wendy Wu [wendywu]
* Wrote it.
\**************************************************************************/

BOOL APIENTRY GreDPtoLP(
HDC     hdc,
LPPOINT ppt,
int     nCount)
{
    return(GreTransformPoints(hdc,ppt,ppt,nCount,XFP_DPTOLP));
}

/******************************Public*Routine******************************\
* GreLPtoDP(hdc,ppt,nCount)
*
* Convert the given logical points into device points.
*
* History:
*  09-Nov-1990 -by- Wendy Wu [wendywu]
* Wrote it.
\**************************************************************************/

BOOL APIENTRY GreLPtoDP(
HDC     hdc,
LPPOINT ppt,
int     nCount)
{
    return(GreTransformPoints(hdc,ppt,ppt,nCount,XFP_LPTODP));
}

/******************************Private*Routine*****************************\
* bWorldMatrixInRange(pmx)
*
* See if the coefficients of the world transform matrix are within
* our minimum and maximum range.
*
* History:
*  27-Nov-1990 -by- Wendy Wu [wendywu]
* Wrote it.
\**************************************************************************/

BOOL bWorldMatrixInRange(PMATRIX pmx)
{

    BOOL bRet;

#if defined(_AMD64_) || defined(_IA64_) || defined(BUILD_WOW6432)
/*
    EFLOAT ef = pmx->efM11;
    ef.vAbs();
    if ((ef < (FLOAT)MIN_WORLD_XFORM) || (ef > (FLOAT)MIN_WORLD_XFORM))
        return(FALSE);

    ef = pmx->efM12;
    ef.vAbs();
    if ((ef < (FLOAT)MIN_WORLD_XFORM) || (ef > (FLOAT)MIN_WORLD_XFORM))
        return(FALSE);

    ef = pmx->efM21;
    ef.vAbs();
    if ((ef < (FLOAT)MIN_WORLD_XFORM) || (ef > (FLOAT)MIN_WORLD_XFORM))
        return(FALSE);

    ef = pmx->efM22;
    ef.vAbs();
    if ((ef < (FLOAT)MIN_WORLD_XFORM) || (ef > (FLOAT)MIN_WORLD_XFORM))
        return(FALSE);

    ef = pmx->efDx;
    ef.vAbs();
    if ((ef < (FLOAT)MIN_WORLD_XFORM) || (ef > (FLOAT)MIN_WORLD_XFORM))
        return(FALSE);

    ef = pmx->efDy;
    ef.vAbs();
    if ((ef < (FLOAT)MIN_WORLD_XFORM) || (ef > (FLOAT)MIN_WORLD_XFORM))
        return(FALSE);
*/

    bRet = TRUE;

#else


    bRet =
    ((pmx->efM11.bExpLessThan(MAX_WORLD_XFORM_EXP)) &&
     (pmx->efM12.bExpLessThan(MAX_WORLD_XFORM_EXP)) &&
     (pmx->efM21.bExpLessThan(MAX_WORLD_XFORM_EXP)) &&
     (pmx->efM22.bExpLessThan(MAX_WORLD_XFORM_EXP)) &&
     (pmx->efDx.bExpLessThan(MAX_WORLD_XFORM_EXP)) &&
     (pmx->efDy.bExpLessThan(MAX_WORLD_XFORM_EXP)))
     ;

#endif

    if (bRet)
    {
    // at this point bRet == TRUE. We have to figure out the cases
    // when the determinant is zero and set bRet to FALSE;

    // We do what we can to avoid multiplications in common cases
    // when figuring out if this is a singular trasform

        if (pmx->efM12.bIsZero() && pmx->efM21.bIsZero())
        {
            if (pmx->efM11.bIsZero() || pmx->efM22.bIsZero())
                bRet = FALSE;
        }
        else if (pmx->efM11.bIsZero() && pmx->efM22.bIsZero())
        {
            if (pmx->efM12.bIsZero() || pmx->efM21.bIsZero())
                bRet = FALSE;
        }
        else // general case, have to do multiplications
        {
            EFLOAT ef = pmx->efM11;
            ef *= pmx->efM22;

            EFLOAT ef1 = pmx->efM12;
            ef1 *= pmx->efM21;

            ef -= ef1; // determinant.

            if (ef.bIsZero())
            {
                bRet = FALSE;
            }
        }
    }

    return bRet;
}


/******************************Public*Routine******************************\
* GreGetDeviceWidth(hdc)
*
* Get the device surface width of the specified dc.
*
* History:
*  26-Jan-1998 -by- Mohamed Hassanin [mhamid]
* Wrote it.
\**************************************************************************/
LONG  APIENTRY
GreGetDeviceWidth(HDC hdc)
{
    DCOBJ dco(hdc);

    if (dco.bValid() != FALSE)
    {
        return (dco.pdc->GetDeviceWidth());
    }
    else
    {
        WARNING("Invalid DC passed to GreGetDeviceWidth\n");
    }

    return (GDI_ERROR);
}

/******************************Public*Routine******************************\
* GreMirrorWindowOrg(hdc)
*
* Mirror the Window X Org. By calling MirrorWindowOrg
*
* History:
*  26-Jan-1998 -by- Mohamed Hassanin [mhamid]
* Wrote it.
\**************************************************************************/
BOOL  APIENTRY
GreMirrorWindowOrg(HDC hdc)
{
    DCOBJ dco(hdc);

    if (dco.bValid() != FALSE)
    {
        dco.pdc->MirrorWindowOrg();
        return (TRUE);
    }

    return (FALSE);
}
/******************************Public*Routine******************************\
* GreGetLayout
*
*
* History:
*  Fri 12-Sep-1991 11:29    -by- Mohamed Hassanin [MHamid]
* Wrote it.
\**************************************************************************/
DWORD APIENTRY
GreGetLayout(HDC hdc)
{
    DWORD dwRet = GDI_ERROR;

    DCOBJ dco(hdc);

    if (dco.bValid() != FALSE)
    {
        dwRet = dco.pdc->dwLayout();
    }

    return(dwRet);
}

/******************************Public*Routine******************************\
* GreSetLayout
*
*
* History:
*  Fri 12-Sep-1991 11:29    -by- Mohamed Hassanin [MHamid]
* Wrote it.
\**************************************************************************/
DWORD APIENTRY
GreSetLayout
( HDC    hdc,
  LONG   wox,
  DWORD  dwLayout)
{
    DCOBJ dco( hdc );
    if( !dco.bValid() )
    {
       WARNING("Xform update invalid hdc\n");
       return(GDI_ERROR);
    }
    return dco.pdc->dwSetLayout(wox, dwLayout);
}

/******************************Public*Routine******************************\
* GreXformUpdate
*
* Updates the server's copy of the WtoD transform, transform related flags,
* and viewport and window extents.
*
* History:
*  6-Aug-1992 -by- Gerrit van Wingerden [gerritv]
* Wrote it.
\**************************************************************************/


BOOL GreXformUpdate
( HDC hdc,
  FLONG flXform,
  LONG wex,
  LONG wey,
  LONG vex,
  LONG vey,
  LONG mapMode,
  PVOID pvMatrix
)
{
    DCOBJ dco( hdc );

    if( !dco.bValid() )
    {
       WARNING("Xform update invalid hdc\n");
       return(FALSE);
    }

// Copy window and viewport extents
    dco.pdc->vSet_szlWindowExt( wex, wey );
    dco.pdc->vSet_szlViewportExt( vex, vey );
    dco.pdc->ulMapMode( mapMode );

// Save current position

    ASSERTGDI(dco.bValid(), "DC not valid");

    if (!dco.pdc->bValidPtlCurrent())
    {
        ASSERTGDI(dco.pdc->bValidPtfxCurrent(), "Both CPs invalid?");

        EXFORMOBJ exoDtoW(dco, DEVICE_TO_WORLD);

        if (exoDtoW.bValid())
            exoDtoW.bXform(&dco.ptfxCurrent(), &dco.ptlCurrent(), 1);

        dco.pdc->vValidatePtlCurrent();
    }

// Set the flags

    dco.pdc->flResetflXform( flXform );
    dco.pdc->flSet_flXform( DEVICE_TO_WORLD_INVALID );

// Set the new world transform

    RtlCopyMemory( (PVOID) &dco.pdc->mxWorldToDevice(), pvMatrix, sizeof( MATRIX ));
    RtlCopyMemory( (PVOID) &dco.pdc->mxUserWorldToDevice(), pvMatrix, sizeof( MATRIX ));

// After the transform, the device space CP will be invalid:

    dco.pdc->vInvalidatePtfxCurrent();

    if( flXform & INVALIDATE_ATTRIBUTES )
    {
        EXFORMOBJ exo(dco, WORLD_TO_DEVICE);

        dco.pdc->vRealizeLineAttrs(exo);
        dco.pdc->vXformChange(TRUE);
    }

    return (TRUE);
}

/******************************Member*Function*****************************\
* vConvertXformToMatrix
*
* Convert a xform structure into a matrix struct.
*
* History:
*  27-Mar-1991 -by- Wendy Wu [wendywu]
* Wrote it.
\**************************************************************************/

VOID vConvertXformToMatrix(CONST XFORML *pxf, PMATRIX pmx)
{
    pmx->efM11 = pxf->eM11;         // overloading operator = which covert
    pmx->efM12 = pxf->eM12;         // IEEE float to our internal EFLOAT
    pmx->efM21 = pxf->eM21;
    pmx->efM22 = pxf->eM22;
    pmx->efDx = pxf->eDx;
    pmx->efDy = pxf->eDy;
#if DBG
    if (!pmx->efDx.bEfToL(pmx->fxDx))
        WARNING("vConvertXformToMatrix:translation dx overflowed\n");
    if (!pmx->efDy.bEfToL(pmx->fxDy))
        WARNING("vConvertXformToMatrix:translation dy overflowed\n");
#else
    pmx->efDx.bEfToL(pmx->fxDx);
    pmx->efDy.bEfToL(pmx->fxDy);
#endif
    pmx->flAccel = XFORM_FORMAT_LTOL;

    if ((pmx->efDx == pmx->efDy) && pmx->efDy.bIsZero())
        pmx->flAccel |= XFORM_NO_TRANSLATION;

    if (pmx->efM12.bIsZero() && pmx->efM21.bIsZero())
    {
        pmx->flAccel |= XFORM_SCALE;
        if (pmx->efM11.bIs1() && pmx->efM22.bIs1())
            pmx->flAccel |= XFORM_UNITY;
    }

}

/******************************Private*Routine*****************************\
* vMakeIso()
*
* Shrink viewport extents in one direction to match the aspect ratio of
* the window.
*
* History:
*
*  05-Dec-1994 -by-  Eric Kutter [erick]
* Moved back to the server
*
*  6-Aug-1992 -by- Gerrit van Wingerden [gerritv]
* Modified for client side use.
*
*  09-Nov-1990 -by- Wendy Wu [wendywu]
* Wrote it.
\**************************************************************************/

VOID DC::vMakeIso()
{
    LONG    lVpExt;
    EFLOAT  efRes, efTemp, efTemp1;

// Calculate the pixel aspect ratio efRes = ASPECTY / ASPECTX.

    if(lVirtualDevicePixelCx() != 0)
    {
        //
        // if lVirtualDeviceCx/Cy are set, use these, they are in micrometers
        // Otherwise use their millimeters counter part
        //
        if ((lVirtualDeviceCx()) != 0 && (lVirtualDeviceCy() != 0))
        {
           efTemp = lVirtualDevicePixelCx();
           efTemp1 = lVirtualDeviceCy();

           LONG lTemp = EngMulDiv(lVirtualDevicePixelCy(), lVirtualDeviceCx(), lVirtualDevicePixelCx());
           efRes = lTemp;
           efRes /= efTemp1;
        }
        else
        {
           efRes = lVirtualDevicePixelCy() * lVirtualDeviceMmCx();
           efTemp = lVirtualDevicePixelCx();
           efTemp1 = lVirtualDeviceMmCy();

           efRes /= efTemp;
           efRes /= efTemp1;
        }
    }
    else
    {
        PDEVOBJ po(hdev());
        ASSERTGDI(po.bValid(), "Invalid PDEV\n");

        efRes  = (LONG)po.ulLogPixelsY();
        efTemp = (LONG)po.ulLogPixelsX();
        efRes /= efTemp;
    }

// Our goal is to make the following formula true
// VpExt.cy / VpExt.cx = (WdExt.cy / WdExt.cx) * (ASPECTY / ASPECTX)

// Let's calculate VpExt.cy assuming VpExt.cx is the limiting factor.
// VpExt.cy = (WdExt.cy * VpExt.cx) / WdExt.cx * efRes

    EFLOATEXT efVpExt = lWindowExtCy();
    efTemp   = lViewportExtCx();
    efTemp1  = lWindowExtCx();
    efVpExt *= efTemp;
    efVpExt /= efTemp1;
    efVpExt *= efRes;
    efVpExt.bEfToL(lVpExt);             // convert efloat to long

    lVpExt = ABS(lVpExt);

// Shrink y if the |original VpExt.cy| > the |calculated VpExt.cy|
// The original signs of the extents are preserved.

    if (lViewportExtCy() > 0)
    {
        if (lViewportExtCy() >= lVpExt)
        {
            lViewportExtCy(lVpExt);
            return;
        }
    }
    else
    {
        if (-lViewportExtCy() >= lVpExt)
        {
            lViewportExtCy(-lVpExt);
            return;
        }
    }

// We know VpExt.cy is the real limiting factor.  Let's calculate the correct
// VpExt.cx.
// VpExt.cx = (WdExt.cx * VpExt.cy) / WdExt.cy / Res

    efVpExt  = lWindowExtCx();
    efTemp   = lViewportExtCy();
    efTemp1  = lWindowExtCy();
    efVpExt *= efTemp;
    efVpExt /= efTemp1;
    efVpExt /= efRes;
    efVpExt.bEfToL(lVpExt);

    lVpExt = ABS(lVpExt);

    if(lViewportExtCx() > 0 )
        lViewportExtCx(lVpExt);
    else
        lViewportExtCx(-lVpExt);

}


/**************************************************************************\
* NtGdiScaleViewportExtEx()
*
* History:
*  07-Jun-1995 -by-  Andre Vachon [andreva]
* Wrote it.
\**************************************************************************/

BOOL
NtGdiScaleViewportExtEx(
    HDC    hdc,
    int    xNum,
    int    xDenom,
    int    yNum,
    int    yDenom,
    LPSIZE pszOut
    )
{
    BOOL bRet = FALSE;

    DCOBJ dcox(hdc);

    if (dcox.bValid())
    {
        BOOL bNoExcept = TRUE;

        //
        // NOTE: NT 3.51 compatibility.
        // Even if the API failed (returned FALSE), the viewport was returned
        // properly - stay compatible with this.
        //

        if (pszOut)
        {
            __try
            {
                ProbeForWrite(pszOut,sizeof(SIZE), sizeof(DWORD));
                *pszOut = dcox.pdc->szlViewportExt();
            }
            __except(EXCEPTION_EXECUTE_HANDLER)
            {
                // SetLastError(GetExceptionCode());

                bNoExcept = FALSE;
            }
        }

        if (bNoExcept == TRUE)
        {
            // can't change extent if fixed scale

            if (dcox.ulMapMode() <= MM_MAX_FIXEDSCALE)
            {
                bRet = TRUE;
            }
            else
            {
                LONG lx, ly;
                if ((xDenom != 0) &&
                    (yDenom != 0) &&
                    ((lx = (dcox.pdc->lViewportExtCx() * xNum) / xDenom) != 0) &&
                    ((ly = (dcox.pdc->lViewportExtCy() * yNum) / yDenom) != 0))
                {
                    dcox.pdc->lViewportExtCx(lx);
                    dcox.pdc->lViewportExtCy(ly);
                    dcox.pdc->MirrorWindowOrg();
                    dcox.pdc->vPageExtentsChanged();
                    bRet = TRUE;
                }
            }
        }
    }

    return(bRet);
}


/******************************Public*Routine******************************\
* GreScaleWindowExtEx()
*
* History:
*  02-Dec-1994 -by-  Eric Kutter [erick]
* Wrote it.
\**************************************************************************/

BOOL GreScaleWindowExtEx(
    HDC hdc,
    int xNum,
    int xDenom,
    int yNum,
    int yDenom,
    PSIZE psizl)
{
    BOOL bRet = FALSE;

    DCOBJ dcox(hdc);                   // lock the dc object

    if (dcox.bValid())                 // check if lock is valid
    {
        if (psizl != (LPSIZE)NULL)
        {
            *psizl = dcox.pdc->szlWindowExt();      // fetch old extent
            if (MIRRORED_DC(dcox.pdc))
                psizl->cx = -psizl->cx;
        }

    // can't change extent if fixed scale

        if (dcox.ulMapMode() <= MM_MAX_FIXEDSCALE)
        {
            bRet = TRUE;
        }
        else
        {
            LONG lx, ly;
            if ((xDenom != 0) &&
                (yDenom != 0) &&
                ((lx = (dcox.pdc->lWindowExtCx() * xNum) / xDenom) != 0) &&
                ((ly = (dcox.pdc->lWindowExtCy() * yNum) / yDenom) != 0))
            {
                dcox.pdc->lWindowExtCx(lx);
                dcox.pdc->lWindowExtCy(ly);
                dcox.pdc->MirrorWindowOrg();
                dcox.pdc->vPageExtentsChanged();
                bRet = TRUE;
            }
        }
    }
    return(bRet);
}

/******************************Private*Routine*****************************\
* vComputePageXform
*
* Compute the page to device scaling factors.
*
* History:
*  05-Dec-1994 -by-  Eric Kutter [erick]
* Moved back to the server
*
*
*  15-Dec-1992 -by- Wendy Wu [wendywu]
* Wrote it.
\**************************************************************************/

VOID DC::vComputePageXform()
{
    EFLOAT ef;
    EFLOATEXT efTemp;

    ef     = LTOFX(lViewportExtCx());
    efTemp = lWindowExtCx();
    ef    /= efTemp;
    efM11PtoD(ef);

    ef     = LTOFX(lViewportExtCy());
    efTemp = lWindowExtCy();
    ef    /= efTemp;
    efM22PtoD(ef);
}

/******************************Public*Routine******************************\
*
* int EngMulDiv
* I am surprised that there is no system service routine to do this.
* anyway I am just fixing the old routine
*
* History:
*  15-Dec-1995 -by- Bodin Dresevic [BodinD]
* Wrote it.
\**************************************************************************/

int EngMulDiv(
    int a,
    int b,
    int c)
{
    LONGLONG ll;
    int iSign = 1;

    if (a < 0)
    {
        iSign = -iSign;
        a = -a;
    }
    if (b < 0)
    {
        iSign = -iSign;
        b = -b;
    }

    if (c != 0)
    {
        if (c < 0)
        {
            iSign = -iSign;
            c = -c;
        }

        ll = (LONGLONG)a;
        ll *= b;
        ll += (c/2); // used to add (c+1)/2 which is wrong
        ll /= c;

    // at this point ll is guaranteed to be > 0. Thus we will do
    // unsigned compare in the next step which generates two less instructions
    // on x86 [bodind]

        if ((ULONGLONG)ll > (ULONG)INT_MAX) // check for overflow:
        {
            if (iSign > 0)
                return INT_MAX;
            else
                return INT_MIN;
        }
        else
        {
            if (iSign > 0)
                return ((int)ll);
            else
                return (-(int)ll);
        }
    }
    else
    {
        ASSERTGDI(c, "EngMulDiv - c == 0\n");
        ASSERTGDI(a | b, "EngMulDiv - a|b == 0\n");

        if (iSign > 0)
            return INT_MAX;
        else
            return INT_MIN;
    }
}
/******************************Public*Routine******************************\
* dwSetLayout
*
* Mirror the dc, by offsetting the window origin. If wox == -1, then the
* window origin becomes mirrored by the DC window width as follows :
* -((Device_Surface_Width * WindowExtX) / ViewportExtX) + LogicalWindowOrgX
*
* Otherwise mirroring is done by the specified wox amount :
* (wox - Current Window X-Origin)
*
* The function also changes windowExt.cx to be -1 so that positive x will
* go from right to left.
*
* History:
*  09-Dec-1997 -by- Mohammed Abdel-Hamid  [mhamid]
* Wrote it.
\**************************************************************************/

DWORD DC::dwSetLayout(LONG   wox, DWORD dwDefLayout)
{

    POINTL ptWOrg, ptVOrg;
    SIZEL  SzWExt, SzVExt;
    DWORD  dwOldLayout;

    dwOldLayout = dwLayout();
    dwLayout(dwDefLayout);
    if ((dwOldLayout & LAYOUT_ORIENTATIONMASK) == (dwDefLayout & LAYOUT_ORIENTATIONMASK)) {
        return dwOldLayout;
    }

    vGet_szlWindowExt(&SzWExt);
    vGet_ptlViewportOrg(&ptVOrg);

    if (dwDefLayout & LAYOUT_RTL) {
        //Set the rtl layout
        ulMapMode(MM_ANISOTROPIC);
        ASSERTGDI((SzWExt.cx > 0), "GreSetLayout WExt.cx < 0 Check it");
    } else {
        ASSERTGDI((SzWExt.cx < 0), "GreSetLayout WExt.cx > 0 Check it");
    }

    SzWExt.cx = -SzWExt.cx;
    vSet_szlWindowExt(&SzWExt);

    ptVOrg.x = -ptVOrg.x;
    vSet_ptlViewportOrg(&ptVOrg);

    if (wox == -1) {
        MirrorWindowOrg();
    } else {
        vGet_ptlWindowOrg(&ptWOrg);
        ptWOrg.x = wox - ptWOrg.x;
        vSet_ptlWindowOrg(&ptWOrg);
    }

    //
    // TA_CENTER equals 6 (0110 Bin) and TA_RIGHT equals 2 (0010 Binary) numerically.
    // so be careful not to do 'flTextAlign() & TA_CENTER'
    // since this will succeed for RIGHT aligned DCs
    // and as a result, the TA_RIGHT bit won't get cleared. [samera]
    //
    if ((flTextAlign()&TA_CENTER) != TA_CENTER) {
        flTextAlign(flTextAlign() ^ (TA_RIGHT));
    }
    if (bClockwise()) {
        vClearClockwise();
    } else {
        vSetClockwise();
    }
    vPageExtentsChanged();
    return dwOldLayout;
}

EFLOAT ef16 = {EFLOAT_16};

/******************************Public*Routine******************************\
* DC::iSetMapMode()
*
* History:
*  07-Dec-1994 -by-  Eric Kutter [erick]
* Wrote it.
\**************************************************************************/

int DC::iSetMapMode(
    int iMode)
{
    int iOldMode;
    DWORD dwOldLayout = 0;

    // If the new map mode is not MM_ANISOTROPIC
    // And the DC in a mirrored mode
    // Then Turn off the mirroring and turn it on back after
    // Setting the new mode.

    if (iMode != MM_ANISOTROPIC) {
        dwOldLayout = dwLayout();
        if (dwOldLayout & LAYOUT_ORIENTATIONMASK) {
            dwSetLayout(-1 , 0);
        }
    }

// If set to the old map mode, don't bother setting it again except
// with MM_ISOTROPIC in which the extents might have been changed.

    iOldMode = ulMapMode();

    if ((iMode != iOldMode) || (iMode == MM_ISOTROPIC))
    {
        if (iMode == MM_TEXT)
        {
            lWindowExtCx(1);
            lWindowExtCy(1);
            lViewportExtCx(1);
            lViewportExtCy(1);
            ulMapMode(iMode);

        // We don't want to recalculate M11 and M22 in vUpdateWtoDXform().
        // Set them correctly here so we can just recalculate translations
        // in vUpdateWtoDXform().

            efM11PtoD(ef16);
            efM22PtoD(ef16);
            mxWorldToDevice().efM11 = ef16;
            mxWorldToDevice().efM22 = ef16;
            mxWorldToDevice().flAccel = XFORM_FORMAT_LTOFX | XFORM_UNITY | XFORM_SCALE;
            RtlCopyMemory(
                  (PVOID) &mxUserWorldToDevice(),
                  (PVOID) &mxWorldToDevice(),
                  sizeof( MATRIX ));

            vSetFlagsMM_TEXT();
        }
        else if (iMode == MM_ANISOTROPIC)
        {
            ulMapMode(iMode);
            vSetFlagsMM_ISO_OR_ANISO();
        }
        else if ((iMode < MM_MIN) || (iMode > MM_MAX))
        {
            return(0);
        }
        else if (lVirtualDevicePixelCx() == 0)
        {
            PDEVOBJ po(hdev());
            ASSERTGDI(po.bValid(), "Invalid PDEV\n");

        // Protect against dynamic mode changes while we compute values
        // using ulHorzSize and ulVertSize:

            DEVLOCKOBJ dlo(po);

        // Get the size of the surface

            lViewportExtCx(po.GdiInfo()->ulHorzRes);
            lViewportExtCy(-(LONG)po.GdiInfo()->ulVertRes);

        // Get the size of the device

            switch (iMode)
            {
            case MM_LOMETRIC:
            //
            // n um. * (1 mm. / 1000 um.) * (10 LoMetric units/1 mm.) = y LoMet
            //
                lWindowExtCx((po.GdiInfo()->ulHorzSize + 50)/100);
                lWindowExtCy((po.GdiInfo()->ulVertSize + 50)/100);
                vSetFlagsMM_FIXED();
                break;

            case MM_HIMETRIC:
            //
            // n um. * (1 mm. / 1000 um.) * (100 HiMetric units/1 mm.) = y HiMet
            //
                lWindowExtCx((po.GdiInfo()->ulHorzSize + 5)/10);
                lWindowExtCy((po.GdiInfo()->ulVertSize + 5)/10);
                vSetFlagsMM_FIXED();
                break;

            case MM_LOENGLISH:
            //
            // n um. * (1 in. / 25400 um.) * (100 LoEng units/1 in.) = y LoEng
            //
                lWindowExtCx((po.GdiInfo()->ulHorzSize + 127)/254);
                lWindowExtCy((po.GdiInfo()->ulVertSize + 127)/254);
                vSetFlagsMM_FIXED();
                break;

            case MM_HIENGLISH:
            //
            // n um. * (1 in. / 25400 um.) * (1000 HiEng units/1 in.) = m HiEng
            //
                lWindowExtCx(EngMulDiv(po.GdiInfo()->ulHorzSize, 10, 254));
                lWindowExtCy(EngMulDiv(po.GdiInfo()->ulVertSize, 10, 254));
                vSetFlagsMM_FIXED();
                break;

            case MM_TWIPS:
            //
            // n um. * (1 in. / 25400 um.) * (1440 Twips/1 in.) = m Twips
            //
                lWindowExtCx(EngMulDiv(po.GdiInfo()->ulHorzSize, 144, 2540));
                lWindowExtCy(EngMulDiv(po.GdiInfo()->ulVertSize, 144, 2540));

            // If it's cached earlier, use it.

            #if defined(_AMD64_) || defined(_IA64_) || defined(BUILD_WOW6432)
                if (efM11_TWIPS().e == (FLOAT)0)
            #else
                if (efM11_TWIPS().i.lMant == 0)
            #endif
                {
                    vComputePageXform();
                    efM11_TWIPS(efM11PtoD());
                    efM22_TWIPS(efM22PtoD());
                }
                ulMapMode(MM_TWIPS);

            // We don't want to recalculate M11 and M22 in vUpdateWtoDXform().
            // Set them correctly here so we can just recalculate translations
            // in vUpdateWtoDXform().

                efM11PtoD(efM11_TWIPS());
                efM22PtoD(efM22_TWIPS());
                mxWorldToDevice().efM11 = efM11_TWIPS();
                mxWorldToDevice().efM22 = efM22_TWIPS();
                mxWorldToDevice().flAccel = XFORM_FORMAT_LTOFX | XFORM_SCALE;
                   RtlCopyMemory(
                         (PVOID) &mxUserWorldToDevice(),
                         (PVOID) &mxWorldToDevice(),
                         sizeof( MATRIX ));

                vSetFlagsMM_FIXED_CACHED();
                goto JUST_RETURN;
                //
                // we need to pass thru mirroring code if
                // it is enabled so the followingis commented for
                // the above line
                // return(iOldMode);
                //

            case MM_ISOTROPIC:
                lWindowExtCx((po.GdiInfo()->ulHorzSize + 50)/100);
                lWindowExtCy((po.GdiInfo()->ulVertSize + 50)/100);
                vSetFlagsMM_ISO_OR_ANISO();
                break;

            default:
                return(0);
            }

            ulMapMode(iMode);
            vPageExtentsChanged();
        }
        else
        {
        // Get the size of the virtual surface

            lViewportExtCx(lVirtualDevicePixelCx());
            lViewportExtCy(-lVirtualDevicePixelCy());

        // Get the size of the virtual device

            switch (iMode)
            {
            case MM_LOMETRIC:
            //
            // n mm. * (10 LoMetric units/1 mm.) = y LoMet
            //
                lWindowExtCx(10 * lVirtualDeviceMmCx());
                lWindowExtCy(10 * lVirtualDeviceMmCy());
                vSetFlagsMM_FIXED();
                break;

            case MM_HIMETRIC:
            //
            // n mm. * (100 HiMetric units/1 mm.) = y HiMet
            //
                lWindowExtCx(100 * lVirtualDeviceMmCx());
                lWindowExtCy(100 * lVirtualDeviceMmCy());
                vSetFlagsMM_FIXED();
                break;

            case MM_LOENGLISH:
            //
            // n mm. * (10 in./254 mm.) * (100 LoEng/1 in.) = y LoEng
            //
                lWindowExtCx(EngMulDiv(lVirtualDeviceMmCx(),1000, 254));
                lWindowExtCy(EngMulDiv(lVirtualDeviceMmCy(),1000, 254));
                vSetFlagsMM_FIXED();
                break;

            case MM_HIENGLISH:
            //
            // n mm. * (10 in./254 mm.) * (1000 LoEng/1 in.) = y LoEng
            //
                lWindowExtCx(EngMulDiv(lVirtualDeviceMmCx(),10000, 254));
                lWindowExtCy(EngMulDiv(lVirtualDeviceMmCy(),10000, 254));
                vSetFlagsMM_FIXED();
                break;

            case MM_TWIPS:
            //
            // n mm. * (10 in./254 mm.) * (1440 Twips/1 in.) = y Twips
            //
                lWindowExtCx(EngMulDiv(lVirtualDeviceMmCx(),14400, 254));
                lWindowExtCy(EngMulDiv(lVirtualDeviceMmCy(),14400, 254));
                vSetFlagsMM_FIXED();
                break;

            case MM_ISOTROPIC:
            //
            // n mm. * (10 LoMetric units/1 mm.) = y LoMet
            //
                lWindowExtCx(10 * lVirtualDeviceMmCx());
                lWindowExtCy(10 * lVirtualDeviceMmCy());
                vSetFlagsMM_ISO_OR_ANISO();
                break;

            default:
                return(0);
            }

            ulMapMode(iMode);
            vPageExtentsChanged();

        }
JUST_RETURN:
        // If turned the mirroring off then turn it on back.
        if (dwOldLayout & LAYOUT_ORIENTATIONMASK) {
            //And then set it again.
            dwSetLayout(-1 , dwOldLayout);
        }
    }

    return(iOldMode);
}

/******************************Public*Routine******************************\
* NtGdiSetVirtualResolution()
*
* Set the virtual resolution of the specified dc.
* The virtual resolution is used to compute transform matrix only.
* If the virtual units are all zeros, the default physical units are used.
* Otherwise, non of the units can be zero.
*
* Currently used by metafile component only.
*
* History:
*
*  05-Dec-1994 -by-  Eric Kutter [erick]
* Moved back to the server
*
*  6-Aug-1992 -by- Gerrit van Wingerden [gerritv]
* Modified for client side use.
*
*  Tue Aug 27 13:04:11 1991     -by-    Hock San Lee    [hockl]
* Wrote it.
\**************************************************************************/

BOOL APIENTRY NtGdiSetVirtualResolution(
    HDC    hdc,
    int    cxVirtualDevicePixel,    // Width of the virtual device in pels
    int    cyVirtualDevicePixel,    // Height of the virtual device in pels
    int    cxVirtualDeviceMm,       // Width of the virtual device in millimeters
    int    cyVirtualDeviceMm)       // Height of the virtual device in millimeters
{
    XFORMPRINT(1,"GreSetVirtualResolution\n",0);

    BOOL bRet = FALSE;

// The units must be all zeros or all non-zeros.

    if ((cxVirtualDevicePixel != 0 && cyVirtualDevicePixel != 0 &&
         cxVirtualDeviceMm    != 0 && cyVirtualDeviceMm != 0)
        ||
        (cxVirtualDevicePixel == 0 && cyVirtualDevicePixel == 0 &&
         cxVirtualDeviceMm    == 0 && cyVirtualDeviceMm    == 0))
    {
    // now lock down the DC

        DCOBJ dcox(hdc);                   // lock the dc object

        if (dcox.bValid())                 // check if lock is valid
        {

            dcox.pdc->lVirtualDevicePixelCx(cxVirtualDevicePixel);
            dcox.pdc->lVirtualDevicePixelCy(cyVirtualDevicePixel);

            dcox.pdc->lVirtualDeviceMmCx(cxVirtualDeviceMm);
            dcox.pdc->lVirtualDeviceMmCy(cyVirtualDeviceMm);

            bRet = TRUE;
        }
    }

    return(bRet);
}

/******************************Public*Routine******************************\
* NtGdiSetSizeDevice()
*
* This is to compensate insufficient precision in SetVirtualResolution
*
* Modified for client side use.
*
*  5/17/99 -by- Lingyun Wang [lingyunw]
* Wrote it.
\**************************************************************************/
BOOL APIENTRY NtGdiSetSizeDevice(
    HDC    hdc,
    int    cxVirtualDevice,       // Width of the virtual device in micrometers
    int    cyVirtualDevice)       // Height of the virtual device in micrometers
{
    BOOL bRet = FALSE;

// The units must be all zeros or all non-zeros.

    if ((cxVirtualDevice != 0) && (cyVirtualDevice != 0))
    {
    // now lock down the DC

        DCOBJ dcox(hdc);                   // lock the dc object

        if (dcox.bValid())                 // check if lock is valid
        {

            dcox.pdc->lVirtualDeviceCx(cxVirtualDevice);
            dcox.pdc->lVirtualDeviceCy(cyVirtualDevice);

            bRet = TRUE;
        }
    }

    return(bRet);
}



/******************************Public*Routine******************************\
* GreGetTransform()
*
* History:
*  05-Dec-1994 -by-  Eric Kutter [erick]
* Wrote it.
\**************************************************************************/

BOOL GreGetTransform(
    HDC     hdc,
    DWORD   iXform,
    XFORML *pxf)
{
    XFORMPRINT(1,"GreGetTransform - iXform = %ld\n",iXform);

    BOOL bRet = FALSE;

    // now lock down the DC

    DCOBJ dcox(hdc);

    if (dcox.bValid())
    {

        EXFORMOBJ xo(dcox,iXform);
        MATRIX mx;

        if (!xo.bValid() && (iXform == XFORM_PAGE_TO_DEVICE))
        {
            xo.vInitPageToDevice(dcox,&mx);
        }

        if (xo.bValid())
        {
            xo.vGetCoefficient(pxf);

            bRet = TRUE;
        }
    }

    return(bRet);
}


/******************************Public*Routine******************************\
* XformUpdate
*
*  Sends update transform information to the server.
*
* History:
*  6-Aug-1992 -by- Gerrit van Wingerden [gerritv]
* Wrote it.
\**************************************************************************/

VOID EXFORMOBJ::vInit(
    XDCOBJ& dco,
    ULONG iXform)
{
    XFORMPRINT(1,"EXFORMOBJ::vInit - iXform = %lx\n",iXform);

    if (dco.pdc->bDirtyXform())
    {
    // Save current position

        if (!dco.pdc->bValidPtlCurrent())
        {
            ASSERTGDI(dco.pdc->bValidPtfxCurrent(), "Both CPs invalid?");

            EXFORMOBJ exoDtoW(dco.pdc->mxDeviceToWorld());

            if (exoDtoW.bValid())
                exoDtoW.bXform(&dco.ptfxCurrent(), &dco.ptlCurrent(), 1);

            dco.pdc->vValidatePtlCurrent();
        }

    // update the transforms

        dco.pdc->vUpdateWtoDXform();

    // After the transform, the device space CP will be invalid:

        dco.pdc->vInvalidatePtfxCurrent();

        if( dco.pdc->flXform() & INVALIDATE_ATTRIBUTES)
        {

            EXFORMOBJ exoWtoD(dco.pdc->mxWorldToDevice());

            dco.pdc->vRealizeLineAttrs(exoWtoD);
            dco.pdc->vXformChange(TRUE);
            dco.pdc->flClr_flXform(INVALIDATE_ATTRIBUTES);
        }

        dco.pdc->flSet_flXform(DEVICE_TO_WORLD_INVALID);
    }

    switch (iXform)
    {
    case XFORM_WORLD_TO_DEVICE:
        pmx = &dco.pdc->mxWorldToDevice();
        XFORMPRINT(2,"EXFORM::vInit - WtoD, pmx = %p\n",pmx);
        break;

    case XFORM_DEVICE_TO_WORLD:
        pmx = &dco.pdc->mxDeviceToWorld();

        if (dco.pdc->flXform() & DEVICE_TO_WORLD_INVALID)
        {
            if (bInverse(dco.pdc->mxWorldToDevice()))
            {
               dco.pdc->flClr_flXform(DEVICE_TO_WORLD_INVALID);
               RtlCopyMemory(
                              (PVOID)&dco.pdc->mxUserDeviceToWorld(),
                              (PVOID)pmx,
                              sizeof( MATRIX )
                            );

            }
            else
            {
               pmx = (PMATRIX)NULL;
            }
            XFORMPRINT(2,"EXFORM::vInit - DtoW was dirty, pmx = %p\n",pmx);
        }
        break;

    case XFORM_WORLD_TO_PAGE:
        XFORMPRINT(2,"EXFORM::vInit - WtoP, pmx = %p\n",pmx);
        pmx = &dco.pdc->mxWorldToPage();
        break;

    default:
        XFORMPRINT(2,"EXFORM::vInit - NULL",pmx);
        pmx = NULL;
        break;
    }
}

/******************************Public*Routine******************************\
* EXFORMOBJ::vInitPageToDevice()
*
* History:
*  05-Dec-1994 -by-  Eric Kutter [erick]
* Wrote it.
\**************************************************************************/

VOID EXFORMOBJ::vInitPageToDevice(
    XDCOBJ& dco,
    PMATRIX pmx_)
{
    pmx = pmx_;

    pmx->efM11 = dco.pdc->efM11PtoD();
    pmx->efM12.vSetToZero();
    pmx->efM21.vSetToZero();
    pmx->efM22 = dco.pdc->efM22PtoD();
    pmx->efDx  = dco.pdc->efDxPtoD();
    pmx->efDy  = dco.pdc->efDyPtoD();
    pmx->efDx.bEfToL(pmx->fxDx);
    pmx->efDy.bEfToL(pmx->fxDy);

    vComputeWtoDAccelFlags();
}

/******************************Private*Routine*****************************\
* vUpdateWtoDXform
*
* Update the world to device transform.
*
* History:
*
*  15-Dec-1992 -by- Wendy Wu [wendywu]
* Wrote it.
\**************************************************************************/

VOID DC::vUpdateWtoDXform()
{
    PMATRIX pmx = &mxWorldToDevice();

    if (bDirtyXlateOrExt())
    {
        if (bPageExtentsChanged())
        {
        // Recalculate the scaling factors for the page to device xform.

        // M11 = ViewportExt.cx / WindowExt.cx
        // M22 = ViewportExt.cy / WindowExt.cy

            if (ulMapMode() == MM_ISOTROPIC)
                vMakeIso();

            if ((lWindowExtCx() == lViewportExtCx()) &&
                (lWindowExtCy() == lViewportExtCy()))
            {
                efM11PtoD(ef16);
                efM22PtoD(ef16);

                pmx->flAccel = XFORM_FORMAT_LTOFX | XFORM_UNITY | XFORM_SCALE;

                flSet_flXform(PAGE_TO_DEVICE_SCALE_IDENTITY);
            }
            else
            {
                EFLOATEXT ef1;
                EFLOATEXT ef2;
                ef1 = LTOFX(lViewportExtCx());
                ef2 = lWindowExtCx();
                ef1 /= ef2;
                efM11PtoD(ef1);

                ef1 = LTOFX(lViewportExtCy());
                ef2 = lWindowExtCy();
                ef1 /= ef2;
                efM22PtoD(ef1);

                pmx->flAccel = XFORM_FORMAT_LTOFX | XFORM_SCALE;
                flClr_flXform(PAGE_TO_DEVICE_SCALE_IDENTITY | PAGE_TO_DEVICE_IDENTITY);
            }

            if (efM11PtoD().bIsNegative())
                flSet_flXform(PTOD_EFM11_NEGATIVE);
            else
                flClr_flXform(PTOD_EFM11_NEGATIVE);

            if (efM22PtoD().bIsNegative())
                flSet_flXform(PTOD_EFM22_NEGATIVE);
            else
                flClr_flXform(PTOD_EFM22_NEGATIVE);
        }

    // Recalculate the translations for the page to device xform.

    // Dx = ViewportOrg.x - (ViewportExt.cx / WindowExt.cx) * WindowOrg.x
    // (ViewportExt.cx / WindowExt.cx) = efM11

    // Dy = ViewportOrg.y - (ViewportExt.cy / WindowExt.cy) * WindowOrg.cy
    // (ViewportExt.cy / WindowExt.cy) = efM22

        if ((lWindowOrgX() == 0) &&
            (lWindowOrgY() == 0))
        {
            if ((lViewportOrgX() == 0) &&
                (lViewportOrgY() == 0))
            {
                EFLOAT efZ;
                efZ.vSetToZero();

                efDxPtoD(efZ);
                efDyPtoD(efZ);
                pmx->fxDx = 0;
                pmx->fxDy = 0;
                pmx->flAccel |= XFORM_NO_TRANSLATION;

                if (bPageToDeviceScaleIdentity())
                    flSet_flXform(PAGE_TO_DEVICE_IDENTITY);
            }
            else
            {
                efDxPtoD(LTOFX(lViewportOrgX()));
                efDyPtoD(LTOFX(lViewportOrgY()));

                pmx->fxDx = LTOFX(lViewportOrgX());
                pmx->fxDy = LTOFX(lViewportOrgY());

                pmx->flAccel &= ~XFORM_NO_TRANSLATION;
                flClr_flXform(PAGE_TO_DEVICE_IDENTITY);
            }
        }
        else
        {
            flClr_flXform(PAGE_TO_DEVICE_IDENTITY);
            pmx->flAccel &= ~XFORM_NO_TRANSLATION;
            if (bPageToDeviceScaleIdentity())
            {
                efDxPtoD(LTOFX(-lWindowOrgX()));
                efDyPtoD(LTOFX(-lWindowOrgY()));

                if ((lViewportOrgX() != 0) ||
                    (lViewportOrgY() != 0))
                {
                    goto ADD_VIEWPORT_ORG;
                }

                pmx->fxDx = LTOFX(-lWindowOrgX());
                pmx->fxDy = LTOFX(-lWindowOrgY());
            }
            else
            {
                {
                    EFLOATEXT ef;
                    ef = -lWindowOrgX();
                    ef *= efrM11PtoD();
                    efDxPtoD(ef);

                    ef = -lWindowOrgY();
                    ef *= efrM22PtoD();
                    efDyPtoD(ef);
                }

                if ((lViewportOrgX()!= 0) ||
                    (lViewportOrgY() != 0))
                {
                ADD_VIEWPORT_ORG:

                    EFLOATEXT efXVO(LTOFX(lViewportOrgX()));
                    efXVO += efrDxPtoD();
                    efDxPtoD(efXVO);

                    EFLOATEXT efYVO(LTOFX(lViewportOrgY()));
                    efYVO += efrDyPtoD();
                    efDyPtoD(efYVO);
                }

                efDxPtoD().bEfToL(pmx->fxDx);
                efDyPtoD().bEfToL(pmx->fxDy);
            }
        }

        if (bWorldToPageIdentity())
        {
        // Copy the PAGE_TO_DEVICE xform to WORLD_TO_DEVICE.
        // pmx->fxDx, fxDy and flAccel has been set earlier in this routine.

            pmx->efM11 = efM11PtoD();
            pmx->efM22 = efM22PtoD();
            pmx->efM12.vSetToZero();
            pmx->efM21.vSetToZero();
            pmx->efDx  = efDxPtoD();
            pmx->efDy  = efDyPtoD();

            if (bPageToDeviceIdentity())
                pmx->flAccel = XFORM_FORMAT_LTOFX | XFORM_SCALE | XFORM_UNITY |
                               XFORM_NO_TRANSLATION;
            else if (bPageToDeviceScaleIdentity())
                pmx->flAccel = XFORM_FORMAT_LTOFX | XFORM_SCALE | XFORM_UNITY;
            else
                pmx->flAccel = XFORM_FORMAT_LTOFX | XFORM_SCALE;

            flClr_flXform(PAGE_XLATE_CHANGED | PAGE_EXTENTS_CHANGED |
                          WORLD_XFORM_CHANGED);


            RtlCopyMemory( (PVOID) &mxUserWorldToDevice(), pmx, sizeof( MATRIX ));

            return;
        }
    }
    else
    {
        if (bWorldToPageIdentity())
        {
        // World transform has changed to identity.

            pmx->efM11 = efM11PtoD();
            pmx->efM22 = efM22PtoD();
            pmx->efM12.vSetToZero();
            pmx->efM21.vSetToZero();
            pmx->efDx  = efDxPtoD();
            pmx->efDy  = efDyPtoD();

            efDxPtoD().bEfToL(pmx->fxDx);
            efDyPtoD().bEfToL(pmx->fxDy);

            if (bPageToDeviceIdentity())
                pmx->flAccel = XFORM_FORMAT_LTOFX | XFORM_SCALE | XFORM_UNITY |
                               XFORM_NO_TRANSLATION;
            else if (bPageToDeviceScaleIdentity())
                pmx->flAccel = XFORM_FORMAT_LTOFX | XFORM_SCALE | XFORM_UNITY;
            else
                pmx->flAccel = XFORM_FORMAT_LTOFX | XFORM_SCALE;

            flClr_flXform(PAGE_XLATE_CHANGED | PAGE_EXTENTS_CHANGED |
                          WORLD_XFORM_CHANGED);

            RtlCopyMemory( (PVOID) &mxUserWorldToDevice(), pmx, sizeof( MATRIX ));
            return;
        }
    }

// Multiply the world to page and page to device xform together.

    PMATRIX pmxWtoP = &mxWorldToPage();

    if (bPageToDeviceScaleIdentity())
    {
        RtlCopyMemory(pmx, pmxWtoP, offsetof(MATRIX, flAccel));
        pmx->efM11.vTimes16();
        pmx->efM12.vTimes16();
        pmx->efM21.vTimes16();
        pmx->efM22.vTimes16();
        pmx->efDx.vTimes16();
        pmx->efDy.vTimes16();
    }
    else
    {
        pmx->efM11.eqMul(pmxWtoP->efM11,efM11PtoD());
        pmx->efM21.eqMul(pmxWtoP->efM21,efM11PtoD());
        pmx->efM12.eqMul(pmxWtoP->efM12,efM22PtoD());
        pmx->efM22.eqMul(pmxWtoP->efM22,efM22PtoD());

        pmx->efDx.eqMul(pmxWtoP->efDx,efM11PtoD());
        pmx->efDy.eqMul(pmxWtoP->efDy,efM22PtoD());
    }

    pmx->efDx += efrDxPtoD();
    pmx->efDx.bEfToL(pmx->fxDx);

    pmx->efDy += efrDyPtoD();
    pmx->efDy.bEfToL(pmx->fxDy);

    if (pmx->efM12.bIsZero() && pmx->efM21.bIsZero())
    {
        if (pmx->efM11.bIs16() && pmx->efM22.bIs16())
            pmx->flAccel = XFORM_FORMAT_LTOFX | XFORM_SCALE | XFORM_UNITY;
        else
            pmx->flAccel = XFORM_FORMAT_LTOFX | XFORM_SCALE;
    }
    else
    {
        pmx->flAccel = XFORM_FORMAT_LTOFX;
    }

    if ((pmx->fxDx == 0) && (pmx->fxDy == 0))
        pmx->flAccel |= XFORM_NO_TRANSLATION;

    flClr_flXform(PAGE_XLATE_CHANGED | PAGE_EXTENTS_CHANGED |
                 WORLD_XFORM_CHANGED);

    RtlCopyMemory((PVOID)&mxUserWorldToDevice(),pmx,sizeof(MATRIX));

}


/******************************Private*Routine*****************************\
* bWordXformIdentity
*
* See is a world transform matrix is identity.
*
* History:
*
*  6-Aug-1992 -by- Gerrit van Wingerden [gerritv]
* Modified for client side use.
*
*  26-Dec-1990 -by- Wendy Wu [wendywu]
* Wrote it.
\**************************************************************************/

BOOL bWorldXformIdentity(CONST XFORML *pxf)
{
    return((pxf->eM11 == IEEE_1_0F) && (pxf->eM12 == IEEE_0_0F) &&
           (pxf->eM21 == IEEE_0_0F) && (pxf->eM22 == IEEE_1_0F) &&
           (pxf->eDx  == IEEE_0_0F) && (pxf->eDy  == IEEE_0_0F));
}


/******************************Public*Routine******************************\
* NtGdiModifyWorldTransform()
*
* History:
*  01-Nov-1994 -by-  Eric Kutter [erick]
* Wrote it.
\**************************************************************************/

BOOL
APIENTRY
NtGdiModifyWorldTransform(
    HDC     hdc,
    LPXFORM pxf,
    DWORD   iXform
    )
{
    XFORMPRINT(1,"GreModifyWorldTransform - iXform = %ld\n",iXform);

    ASSERTGDI(sizeof(XFORM) == sizeof(XFORML),"sizeof(XFORM) != sizeof(XFORML)\n");

    BOOL bRet = FALSE;

    DCOBJ dcox(hdc);

    if (dcox.bValid())
    {
        XFORML xf;

        if (pxf)
        {
            bRet = ProbeAndConvertXFORM((XFORML *)pxf, &xf);
        }
        else
        {
            // must be identity to allow pxf == NULL

            bRet = (iXform == MWT_IDENTITY);
        }

        if (bRet)
        {
            bRet = dcox.bModifyWorldTransform(&xf,iXform);
        }
    }

    return(bRet);
}



/******************************Public*Routine******************************\
* XDCOBJ::bModifyWorldTransform()
*
* History:
*  07-Dec-1994 -by-  Eric Kutter [erick]
* Wrote it.
\**************************************************************************/

BOOL XDCOBJ::bModifyWorldTransform(
    CONST XFORML *pxf,
    ULONG         iMode)
{
    BOOL bRet = FALSE;
    MATRIX  mx;

    switch (iMode)
    {
    case MWT_SET:
        if (!bWorldXformIdentity(pxf))
        {
            vConvertXformToMatrix(pxf, &mx);

            if (bWorldMatrixInRange(&mx))       // check if the new world xform is
            {                                   // within the min, max range.
                RtlCopyMemory(&pdc->mxWorldToPage(), &mx, offsetof(MATRIX, flAccel));
                RtlCopyMemory(&pdc->mxUserWorldToPage(), &mx, offsetof(MATRIX, flAccel));
                pdc->vClrWorldXformIdentity();
                bRet = TRUE;
            }
            break;
        }
    //MWT_IDENTITY must follow MWT_SET.  This will fall through if it is identity

    case MWT_IDENTITY:
        if (!pdc->bWorldToPageIdentity())
        {
            RtlCopyMemory(&pdc->mxWorldToPage(), &gmxIdentity_LToL, offsetof(MATRIX, flAccel));
            RtlCopyMemory(&pdc->mxUserWorldToPage(), &gmxIdentity_LToL, offsetof(MATRIX, flAccel));
            pdc->vSetWorldXformIdentity();
        }
        bRet = TRUE;
        break;

    case MWT_LEFTMULTIPLY:
    case MWT_RIGHTMULTIPLY:
        vConvertXformToMatrix(pxf,&mx);

        if (!pdc->bWorldToPageIdentity())
        {
            EXFORMOBJ xoWtoP(*this,XFORM_WORLD_TO_PAGE);

            if (!xoWtoP.bMultToWorld(&mx, iMode))
                break;
        }

        if (!bWorldMatrixInRange(&mx))      // check if the new world xform is
            break;                          // within the min, max range.

        RtlCopyMemory( &pdc->mxWorldToPage(), &mx, offsetof(MATRIX, flAccel));
        RtlCopyMemory( &pdc->mxUserWorldToPage(), &mx, offsetof(MATRIX, flAccel));

    // Check if the resultant matrix is identity.

        if (memcmp(&mx, &gmxIdentity_LToL, offsetof(MATRIX, flAccel)))
        {
            pdc->vClrWorldXformIdentity();
        }
        else
        {
            pdc->vSetWorldXformIdentity();
        }
        bRet = TRUE;
        break;

    default:
        WARNING("invalid mode passed to GreModifyWorldTransform\n");
        break;
    }

    return(bRet);
}

/******************************Public*Routine******************************\
* GreCombineTransform
*
*  Concatenate two transforms together by (*pxfSrc1) x (*pxfSrc2).
*
* History:
*
*  6-Aug-1992 -by- Gerrit van Wingerden [gerritv]
* Modified for client side use.
*
*  24-Jan-1992 -by- Wendy Wu [wendywu]
* Wrote it.
\**************************************************************************/

BOOL GreCombineTransform(
    XFORML *pxfDst,
    XFORML *pxfSrc1,
    XFORML *pxfSrc2)
{
    MATRIX  mx1,mx2,mxDst;

    vConvertXformToMatrix(pxfSrc1, &mx1);
    vConvertXformToMatrix(pxfSrc2, &mx2);

    EXFORMOBJ xoDst(mxDst);

    if (!xoDst.bMultiply(&mx1, &mx2))
        return(FALSE);

    xoDst.flAccel(XFORM_FORMAT_LTOL);

    xoDst.vGetCoefficient(pxfDst);
    return(TRUE);
}

/******************************Public*Routine******************************\
* NtGdiUpdateTransform
*
*     This routine flushes the current transform
*
* History:
* 7/2/98 -by- Lingyun Wang [lingyunw]
\**************************************************************************/

BOOL NtGdiUpdateTransform(HDC hdc)
{
    BOOL bRet = TRUE;

    // update the transforms
    XDCOBJ dco(hdc);

    if (dco.bValid())
    {
       dco.pdc->vUpdateWtoDXform();

       dco.vUnlock();
    }
    else
    {
        bRet = FALSE;
    }

    return (bRet);

}