<filename>windows/advcore/gdiplus/engine/flat/flatapi.cpp<gh_stars>10-100
/**************************************************************************\
*
* Copyright (c) 1998  Microsoft Corporation
*
* Module Name:
*
*   flatapi.cpp
*
* Abstract:
*
*   Flat GDI+ API wrappers
*
* Revision History:
*
*   12/13/1998 davidx
*       Created it.
*
\**************************************************************************/

#include "precomp.hpp"
#include "GdiplusFlat.h"
#include "gpverp.h"

#if DBG
#include <mmsystem.h>
#endif

extern "C" {


//--------------------------------------------------------------------------
//  CheckParameter(p)
//
//     If p evaluates to FALSE, then we currently assert.  In future,
//     we can simply return an invalid parameter status which throws
//     an exception.
//
//  CheckObjectBusy(p)
//
//     Not implemented.  Bails out if object is currently being used.
//
//--------------------------------------------------------------------------
//
// !!!: Only include NULL & IsValid checks in checked builds?
//
// !!!: Instead of deleting object, call a Dispose() method, so far
//       only Bitmap supports this.
//
// !!!: Lock Matrix objects, what about color?
//
// !! Better error checking.  For 'I' APIs which convert from Point to PointI,
//    etc.  Verify count & point are valid first.
//
// !! Change ObjectLock to mutable memory and GetObjectLock() to a const member.
//

#define CheckParameter(cond) \
            if (! (cond)) \
                return InvalidParameter;

#define CheckParameterValid(obj) \
            if (!(obj) || !((obj)->IsValid())) \
                return InvalidParameter;

#define CheckOptionalParameterValid(obj) \
            if ((obj) && (!(obj)->IsValid())) \
                return InvalidParameter;

#define CheckObjectBusy(obj) \
      GpLock lock##obj(obj->GetObjectLock()); \
      if (!(lock##obj).IsValid()) \
            return ObjectBusy;

#define CheckOptionalObjectBusy(obj) \
      GpLock lock##obj(obj ? obj->GetObjectLock() : NULL); \
      if (obj && (!(lock##obj).IsValid())) \
            return ObjectBusy;

// We should put an assert in here so that we stop in the debugger if anyone
// does this.

#define CheckObjectBusyForDelete(obj) \
      GpLock lock##obj(obj->GetObjectLock()); \
      if (!(lock##obj).IsValid())             \
      {                                       \
            WARNING(("Silent memory leak deleting object %p.", obj)); \
            WARNING(("Object lock held by another thread."));\
            WARNING(("Incorrect synchronization by the calling application."));\
            return ObjectBusy;                \
      }                                       \
      lock##obj.MakePermanentLock();

#define CheckColorParameter(color) \
            ;           

#define LazilyInitializeGdiplus

#define CheckGdiplusInitialized \
    { GdiplusStartupCriticalSection critsec; \
      if (Globals::LibraryInitRefCount <= 0) \
          return GdiplusNotInitialized; \
    }

#define CheckGdiplusInitialized_ReturnNULL \
    { GdiplusStartupCriticalSection critsec; \
      if (Globals::LibraryInitRefCount <= 0) \
          return NULL; \
    }

    
#define MaskPixelFormat(pixel) (pixel & 0xFFFFFF)
    

#if DBG

// This class asserts that GDI+ is in an initialized state, to be used in
// our entry-point functions in a debug build.
//
// We assert both at the start and the end of the call, and we make sure that
// we don't hold the critical section for the duration of the API (else this
// would serialize all our calls on debug builds.)

class InitAsserter
{
public:
    InitAsserter() {
        GdiplusStartupCriticalSection critsec;
        ASSERTMSG(Globals::LibraryInitRefCount > 0, ("GDI+ API called before GdiplusStartup/after GdiplusShutdown."));
    }
    ~InitAsserter() {
        GdiplusStartupCriticalSection critsec;
        ASSERTMSG(Globals::LibraryInitRefCount > 0, ("GDI+ API called before GdiplusStartup/after GdiplusShutdown."));
    }
};
#endif

#ifdef GP_ENABLE_MONITORS
#ifdef GP_RELEASE_BUILD
    #define DEFINE_MONITOR(a)
#else
    #define DEFINE_MONITOR(a) GpBlockMonitor blockMonitor(MONITOR(a));
#endif
#else
    #define DEFINE_MONITOR(a)
#endif    

// Define this to do something on each API entry point.
// This is a debugging aid for complex instruction streams (e.g. Office)
// [agodfrey] Well, it just became more than a mere debugging aid...
// [asecchia] Now it's definitely more than a debugging aid...
//            FPUStateSaver was being forgotten on many of our APIs so
//            it's now in here and therefore will be included in all of
//            our APIs (except Startup and Shutdown).

#if DBG
    #define API_ENTRY(a) \
        DEFINE_MONITOR(a) \
        LazilyInitializeGdiplus; \
        VERBOSE(("GDI+ API " #a)); \
        InitAsserter __initAsserter; \
        FPUStateSaver fps;

    #define API_ENTRY_NO_INITCHECK(a) \
        DEFINE_MONITOR(a) \
        VERBOSE(("GDI+ API ", #a));
#else

    #define API_ENTRY(a) \
        DEFINE_MONITOR(a) \
        LazilyInitializeGdiplus;\
        FPUStateSaver fps;

    #define API_ENTRY_NO_INITCHECK(a) \
        DEFINE_MONITOR(a)
#endif

GdiplusStartupOutput gStartupOutput = {
    NotificationStartup,
    NotificationShutdown
};

GpStatus WINAPI
GdiplusStartup(
    OUT ULONG_PTR *token,
    const GdiplusStartupInput *input,
    OUT GdiplusStartupOutput *output)
{
    API_ENTRY_NO_INITCHECK(GdiplusStartup);

    if (   (!token)
        || (!input)
        || (input->SuppressBackgroundThread && !output))
    {
        return InvalidParameter;
    }

    if (input->GdiplusVersion != 1)
    {
        return UnsupportedGdiplusVersion;
    }

    GdiplusStartupCriticalSection critsec;

    // Should never happen, because GdiplusShutdown won't decrement below zero.

    if (Globals::LibraryInitRefCount == 0)
    {
        // Note: We can't allocate anything before this point

        GpStatus ret = InternalGdiplusStartup(input);

        if (ret != Ok) return ret;

        Globals::LibraryInitToken = GenerateInitToken();
    }

    ASSERT(Globals::LibraryInitRefCount >= 0);

    *token = Globals::LibraryInitToken + Globals::LibraryInitRefCount;
    Globals::LibraryInitRefCount++;

    if (input->SuppressBackgroundThread)
    {
        *output = gStartupOutput;
    }
    return Ok;
}

VOID WINAPI
GdiplusShutdown(
    ULONG_PTR token)
{
    API_ENTRY_NO_INITCHECK(GdiplusShutdown);

    GdiplusStartupCriticalSection critsec;

    // Should never happen, because we won't decrement below zero.

    ASSERT(Globals::LibraryInitRefCount >= 0);

    if (Globals::LibraryInitRefCount == 0)
    {
        // Return - i.e. ignore the extra shutdown call

        RIP(("Too many calls to GdiplusShutdown"));
        return;
    }

    if (Globals::LibraryInitRefCount == 1)
    {
        // Shut down for real

        Globals::LibraryInitToken = 0;

        InternalGdiplusShutdown();

        // No allocation/deallocation can happen after this point
    }
    else if (token == Globals::LibraryInitToken)
    {
        // The first client to initialize is shutting down; we must clean up
        // after this one since it defines certain global behavior.

        Globals::UserDebugEventProc = NULL;
    }

    Globals::LibraryInitRefCount--;
}

GpStatus WINAPI
GdiplusNotificationHook(
    OUT ULONG_PTR *token)
{
    API_ENTRY(GdiplusNotificationHook);

    {
        GdiplusStartupCriticalSection critsec;

        // It's illegal to call this API if you're using a background thread.

        if (Globals::ThreadNotify)
        {
            return GenericError;
        }
    }

    return NotificationStartup(token);
}

VOID WINAPI
GdiplusNotificationUnhook(
    ULONG_PTR token)
{
    API_ENTRY(GdiplusNotificationUnhook);

    {
        GdiplusStartupCriticalSection critsec;

        // It's illegal to call this API if you're using a background thread.

        if (Globals::ThreadNotify)
        {
            return;
        }
    }

    NotificationShutdown(token);
}

GpStatus
WINGDIPAPI
GdipCreatePath(GpFillMode fillMode, GpPath **outPath)
{
    API_ENTRY(GdipCreatePath);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(outPath);

    GpPath * path;

    path = (GpPath *) InterlockedExchangePointer((PVOID *) &Globals::PathLookAside, NULL);

    if(path  != NULL)
    {
        path->GetObjectLock()->Reset();
        path->Reset(fillMode);
    }
    else
    {
        path = new (GpPathTag, TRUE) GpPath(fillMode);
    }


    if (CheckValid(path))
    {
        *outPath = path;
        return Ok;
    }
    else
        return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipCreatePath2(
    GDIPCONST GpPointF* points,
    GDIPCONST BYTE* types,
    INT count,
    GpFillMode fillMode,
    GpPath **path
    )
{
    API_ENTRY(GdipCreatePath2);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(path && points && types);

    *path = new (GpPathTag, TRUE) GpPath(points, types, count, fillMode);

    if (CheckValid(*path))
    {
        return Ok;
    }
    else
        return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipCreatePath2I(
    GDIPCONST GpPoint* points,
    GDIPCONST BYTE* types,
    INT count,
    GpFillMode fillMode,
    GpPath **path
    )
{
    API_ENTRY(GdipCreatePath2I);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(path && points && types);

    StackBuffer buffer;

    GpPointF* pointsF = (GpPointF*) buffer.GetBuffer(count*sizeof(GpPointF));

    if(!pointsF) return OutOfMemory;

    for (INT i=0; i<count; i++)
    {
        pointsF[i].X = TOREAL(points[i].X);
        pointsF[i].Y = TOREAL(points[i].Y);
    }

    *path = new (GpPathTag, TRUE) GpPath(pointsF, types, count, fillMode);

    if (CheckValid(*path))
    {
        return Ok;
    }
    else
        return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipClonePath(
    GpPath* path,
    GpPath** clonepath
    )
{
    API_ENTRY(GdipClonePath);
    CheckParameter(clonepath);
    CheckParameterValid(path);
    CheckObjectBusy(path);

    *clonepath = path->Clone();

    if (*clonepath)
    {
        // Make sure we tag this allocation as an API allocation
        GpTagMalloc(*clonepath, GpPathTag, TRUE);
        return Ok;
    }
    else
        return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipDeletePath(
    GpPath* path
    )
{
    API_ENTRY(GdipDeletePath);
    // NOTE: Do NOT call CheckParameterValid(), because we need to free
    // the object, even if it's not in a valid state.

    CheckParameter(path);
    CheckObjectBusyForDelete(path);

    path = (GpPath *) InterlockedExchangePointer((PVOID *) &Globals::PathLookAside, path);

    if(path != NULL)
    {
        delete path;
    }

    return Ok;
}

GpStatus
WINGDIPAPI
GdipResetPath(
    GpPath* path
    )
{
    API_ENTRY(GdipResetPath);
    CheckParameterValid(path);
    CheckObjectBusy(path);

    return path->Reset();
}

GpStatus
WINGDIPAPI
GdipGetPointCount(
    GpPath* path,
    INT *count
    )
{
    API_ENTRY(GdipGetPointCount);
    CheckParameter(count);
    CheckParameterValid(path);
    CheckObjectBusy(path);

    *count = path->GetPointCount();

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetPathTypes(
    GpPath* path,
    BYTE* types,
    INT count
    )
{
    API_ENTRY(GdipGetPathTypes);
    CheckParameter(types && count > 0);
    CheckParameterValid(path);
    CheckObjectBusy(path);

    INT pathCount;

    pathCount = path->GetPointCount();

    if (pathCount > count)
        return InsufficientBuffer;

    if (pathCount < 0)
        return GenericError;

    GpMemcpy(types, path->GetPathTypes(), pathCount);

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetPathPoints(
    GpPath* path,
    GpPointF* points,
    INT count
    )
{
    API_ENTRY(GdipGetPathPoints);
    // NOTE: Race condition between GetPointCount() & GetPathPoints()
    //       we need a manually invokable lock here.

    CheckParameter(points && count > 0);
    CheckParameterValid(path);
    CheckObjectBusy(path);

    INT pathCount;

    pathCount = path->GetPointCount();

    if (pathCount > count)
        return InsufficientBuffer;

    if (pathCount < 0)
        return GenericError;

    GpMemcpy(points, path->GetPathPoints(), pathCount*sizeof(GpPointF));

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetPathPointsI(
    GpPath* path,
    GpPoint* points,
    INT count
    )
{
    API_ENTRY(GdipGetPathPointsI);
    // NOTE: Race condition between GetPointCount() & GetPathPoints()
    //       we need a manually invokable lock here.

    CheckParameter(points && count > 0);
    CheckParameterValid(path);
    CheckObjectBusy(path);

    INT pathCount;

    pathCount = path->GetPointCount();

    if (pathCount > count)
        return InsufficientBuffer;

    if (pathCount < 0)
        return GenericError;


    GDIPCONST GpPointF *pointsF = path->GetPathPoints();

    for (INT i=0; i<count; i++)
    {
        points[i].X = GpRound(pointsF[i].X);
        points[i].Y = GpRound(pointsF[i].Y);
    }

    return Ok;
}


GpStatus
WINGDIPAPI
GdipGetPathFillMode(
    GpPath *path,
    GpFillMode *fillmode
    )
{
    API_ENTRY(GdipGetPathFillMode);
    CheckParameter(fillmode);
    CheckParameterValid(path);
    CheckObjectBusy(path);

    *fillmode = (GpFillMode)path->GetFillMode();

    return Ok;
}

GpStatus
WINGDIPAPI
GdipSetPathFillMode(
    GpPath *path,
    GpFillMode fillmode
    )
{
    API_ENTRY(GdipSetPathFillMode);
    CheckParameterValid(path);
    CheckObjectBusy(path);

    path->SetFillMode(fillmode);

    return Ok;
}

GpStatus
WINGDIPAPI
GdipSetPathData(
    GpPath *path,
    GpPathData* pathData
    )
{
    API_ENTRY(GdipSetPathData);
    CheckParameterValid(path);
    CheckObjectBusy(path);

    return path->SetPathData(pathData);
}

GpStatus
WINGDIPAPI
GdipGetPathData(
    GpPath *path,
    GpPathData* pathData
    )
{
    API_ENTRY(GdipGetPathData);
    CheckParameterValid(path);
    CheckObjectBusy(path);

    return path->GetPathData(pathData);
}

GpStatus
WINGDIPAPI
GdipStartPathFigure(
    GpPath *path
    )
{
    API_ENTRY(GdipStartPathFigure);
    CheckParameterValid(path);
    CheckObjectBusy(path);

    path->StartFigure();

    return Ok;
}

GpStatus
WINGDIPAPI
GdipClosePathFigure(
    GpPath *path
    )
{
    API_ENTRY(GdipClosePathFigure);
    CheckParameterValid(path);
    CheckObjectBusy(path);

    return path->CloseFigure();
}

GpStatus
WINGDIPAPI
GdipClosePathFigures(
    GpPath *path
    )
{
    API_ENTRY(GdipClosePathFigures);
    CheckParameterValid(path);
    CheckObjectBusy(path);

    return path->CloseFigures();
}

GpStatus
WINGDIPAPI
GdipSetPathMarker(
    GpPath *path
    )
{
    API_ENTRY(GdipSetPathMarker);
    CheckParameterValid(path);
    CheckObjectBusy(path);

    return path->SetMarker();
}

GpStatus
WINGDIPAPI
GdipClearPathMarkers(
    GpPath *path
    )
{
    API_ENTRY(GdipClearPathMarkers);
    CheckParameterValid(path);
    CheckObjectBusy(path);

    return path->ClearMarkers();
}

GpStatus
WINGDIPAPI
GdipReversePath(
    GpPath* path
    )
{
    API_ENTRY(GdipReversePath);
    CheckParameterValid(path);
    CheckObjectBusy(path);

    return path->Reverse();
}

GpStatus
WINGDIPAPI
GdipGetPathLastPoint(
    GpPath* path,
    GpPointF* lastPoint
    )
{
    API_ENTRY(GdipGetPathLastPoint);
    CheckParameter(lastPoint);
    CheckParameterValid(path);
    CheckObjectBusy(path);

    return path->GetLastPoint(lastPoint);
}

GpStatus
WINGDIPAPI
GdipAddPathLine(
    GpPath *path,
    REAL x1,
    REAL y1,
    REAL x2,
    REAL y2
    )
{
    API_ENTRY(GdipAddPathLine);
    CheckParameterValid(path);
    CheckObjectBusy(path);

    return path->AddLine(x1, y1, x2, y2);
}

GpStatus
WINGDIPAPI
GdipAddPathLineI(
    GpPath *path,
    INT x1,
    INT y1,
    INT x2,
    INT y2
    )
{
    API_ENTRY(GdipAddPathLineI);
    return GdipAddPathLine(path, TOREAL(x1), TOREAL(y1), TOREAL(x2), TOREAL(y2));
}

GpStatus
WINGDIPAPI
GdipAddPathLine2(
    GpPath *path,
    GDIPCONST GpPointF *points,
    INT count)
{
    API_ENTRY(GdipAddPathLine2);
    CheckParameter(points && count > 0);
    CheckParameterValid(path);
    CheckObjectBusy(path);

    return path->AddLines(points, count);
}

GpStatus
WINGDIPAPI
GdipAddPathLine2I(
    GpPath *path,
    GDIPCONST GpPoint *points,
    INT count)
{
    API_ENTRY(GdipAddPathLine2I);
    StackBuffer buffer;

    GpPointF* pointsF = (GpPointF*) buffer.GetBuffer(count*sizeof(GpPointF));

    if(!pointsF) return OutOfMemory;

    for (INT i=0; i<count; i++)
    {
        pointsF[i].X = TOREAL(points[i].X);
        pointsF[i].Y = TOREAL(points[i].Y);
    }

    GpStatus status = GdipAddPathLine2(path, pointsF, count);

    return status;
}

GpStatus
WINGDIPAPI
GdipAddPathArc(
    GpPath *path,
    REAL x,
    REAL y,
    REAL width,
    REAL height,
    REAL startAngle,
    REAL sweepAngle
    )
{
    API_ENTRY(GdipAddPathArc);
    CheckParameterValid(path);
    CheckObjectBusy(path);

    return path->AddArc(
        x,
        y,
        width,
        height,
        startAngle,
        sweepAngle
    );
}

GpStatus
WINGDIPAPI
GdipAddPathArcI(
    GpPath *path,
    INT x,
    INT y,
    INT width,
    INT height,
    REAL startAngle,
    REAL sweepAngle
    )
{
    API_ENTRY(GdipAddPathArcI);
   return GdipAddPathArc(path,
                         TOREAL(x),
                         TOREAL(y),
                         TOREAL(width),
                         TOREAL(height),
                         startAngle,
                         sweepAngle);
}

GpStatus
WINGDIPAPI
GdipAddPathBezier(
    GpPath *path,
    REAL x1,
    REAL y1,
    REAL x2,
    REAL y2,
    REAL x3,
    REAL y3,
    REAL x4,
    REAL y4
    )
{
    API_ENTRY(GdipAddPathBezier);
   CheckParameterValid(path);
   CheckObjectBusy(path);

   return path->AddBezier(x1,
                          y1,
                          x2,
                          y2,
                          x3,
                          y3,
                          x4,
                          y4);
}

GpStatus
WINGDIPAPI
GdipAddPathBezierI(
    GpPath *path,
    INT x1,
    INT y1,
    INT x2,
    INT y2,
    INT x3,
    INT y3,
    INT x4,
    INT y4
    )
{
    API_ENTRY(GdipAddPathBezierI);
   return GdipAddPathBezier(path,
                            TOREAL(x1),
                            TOREAL(y1),
                            TOREAL(x2),
                            TOREAL(y2),
                            TOREAL(x3),
                            TOREAL(y3),
                            TOREAL(x4),
                            TOREAL(y4));
}

GpStatus
WINGDIPAPI
GdipAddPathBeziers(
    GpPath *path,
    GDIPCONST GpPointF *points,
    INT count
    )
{
    API_ENTRY(GdipAddPathBeziers);
   CheckParameter(points && count > 0);
   CheckParameterValid(path);
   CheckObjectBusy(path);

   return path->AddBeziers(points, count);
}


GpStatus
WINGDIPAPI
GdipAddPathBeziersI(
    GpPath *path,
    GDIPCONST GpPoint *points,
    INT count
    )
{
    API_ENTRY(GdipAddPathBeziersI);
   StackBuffer buffer;

   GpPointF* pointsF = (GpPointF*) buffer.GetBuffer(count*sizeof(GpPointF));

   if(!pointsF) return OutOfMemory;

   for (INT i=0; i<count; i++)
   {
       pointsF[i].X = TOREAL(points[i].X);
       pointsF[i].Y = TOREAL(points[i].Y);
   }

   GpStatus status = GdipAddPathBeziers(path, pointsF, count);

   return status;
}

GpStatus
WINGDIPAPI
GdipAddPathCurve(
    GpPath *path,
    GDIPCONST GpPointF *points,
    INT count
    )
{
    API_ENTRY(GdipAddPathCurve);
   CheckParameter(points && count > 0);
   CheckParameterValid(path);
   CheckObjectBusy(path);

   return path->AddCurve(points, count);
}


GpStatus
WINGDIPAPI
GdipAddPathCurveI(
    GpPath *path,
    GDIPCONST GpPoint *points,
    INT count
    )
{
    API_ENTRY(GdipAddPathCurveI);
   CheckParameter(points && count > 0);

   StackBuffer buffer;

   GpPointF* pointsF = (GpPointF*) buffer.GetBuffer(count*sizeof(GpPointF));

   if(!pointsF) return OutOfMemory;

   for (INT i=0; i<count; i++)
   {
       pointsF[i].X = TOREAL(points[i].X);
       pointsF[i].Y = TOREAL(points[i].Y);
   }

   GpStatus status = GdipAddPathCurve(path, pointsF, count);

   return status;
}

GpStatus
WINGDIPAPI
GdipAddPathCurve2(
    GpPath *path,
    GDIPCONST GpPointF *points,
    INT count,
    REAL tension
    )
{
    API_ENTRY(GdipAddPathCurve2);
   CheckParameter(points && count > 0);
   CheckParameterValid(path);
   CheckObjectBusy(path);

   INT offset = 0;
   INT numOfSegments = count - 1;

   return path->AddCurve(points, count, tension, offset, numOfSegments);
}

GpStatus
WINGDIPAPI
GdipAddPathCurve2I(
    GpPath *path,
    GDIPCONST GpPoint *points,
    INT count,
    REAL tension
    )
{
    API_ENTRY(GdipAddPathCurve2I);
   CheckParameter(points && count > 0);

   StackBuffer buffer;

   GpPointF* pointsF = (GpPointF*) buffer.GetBuffer(count*sizeof(GpPointF));

   if(!pointsF) return OutOfMemory;

   for (INT i=0; i<count; i++)
   {
       pointsF[i].X = TOREAL(points[i].X);
       pointsF[i].Y = TOREAL(points[i].Y);
   }

   GpStatus status = GdipAddPathCurve2(path, pointsF, count, tension);

   return status;
}

GpStatus
WINGDIPAPI
GdipAddPathCurve3(
    GpPath *path,
    GDIPCONST GpPointF *points,
    INT count,
    INT offset,
    INT numberOfSegments,
    REAL tension
    )
{
    API_ENTRY(GdipAddPathCurve3);
   CheckParameter(points && count > 0);
   CheckParameterValid(path);
   CheckObjectBusy(path);

   return path->AddCurve(points, count, tension, offset, numberOfSegments);
}

GpStatus
WINGDIPAPI
GdipAddPathCurve3I(
    GpPath *path,
    GDIPCONST GpPoint *points,
    INT count,
    INT offset,
    INT numberOfSegments,
    REAL tension
    )
{
    API_ENTRY(GdipAddPathCurve3I);
   StackBuffer buffer;

   GpPointF* pointsF = (GpPointF*) buffer.GetBuffer(count*sizeof(GpPointF));

   if(!pointsF) return OutOfMemory;

   for (INT i=0; i<count; i++)
   {
       pointsF[i].X = TOREAL(points[i].X);
       pointsF[i].Y = TOREAL(points[i].Y);
   }

   GpStatus status = GdipAddPathCurve3(path, pointsF, count, offset, numberOfSegments, tension);

   return status;
}

GpStatus
WINGDIPAPI
GdipAddPathClosedCurve(
    GpPath *path,
    GDIPCONST GpPointF *points,
    INT count
    )
{
    API_ENTRY(GdipAddPathClosedCurve);
   CheckParameter(points && count > 0);
   CheckParameterValid(path);
   CheckObjectBusy(path);

   return path->AddClosedCurve(points, count);
}

GpStatus
WINGDIPAPI
GdipAddPathClosedCurveI(
    GpPath *path,
    GDIPCONST GpPoint *points,
    INT count
    )
{
    API_ENTRY(GdipAddPathClosedCurveI);
   StackBuffer buffer;

   GpPointF* pointsF = (GpPointF*) buffer.GetBuffer(count*sizeof(GpPointF));

   if(!pointsF) return OutOfMemory;

   for (INT i=0; i<count; i++)
   {
       pointsF[i].X = TOREAL(points[i].X);
       pointsF[i].Y = TOREAL(points[i].Y);
   }

   GpStatus status = GdipAddPathClosedCurve(path, pointsF, count);

   return status;
}

GpStatus
WINGDIPAPI
GdipAddPathClosedCurve2(
    GpPath *path,
    GDIPCONST GpPointF *points,
    INT count,
    REAL tension
    )
{
    API_ENTRY(GdipAddPathClosedCurve2);
   CheckParameter(points && count > 0);
   CheckParameterValid(path);
   CheckObjectBusy(path);

   return path->AddClosedCurve(points, count, tension);
}

GpStatus
WINGDIPAPI
GdipAddPathClosedCurve2I(
    GpPath *path,
    GDIPCONST GpPoint *points,
    INT count,
    REAL tension
    )
{
    API_ENTRY(GdipAddPathClosedCurve2I);
   StackBuffer buffer;

   GpPointF* pointsF = (GpPointF*) buffer.GetBuffer(count*sizeof(GpPointF));

   if(!pointsF) return OutOfMemory;

   for (INT i=0; i<count; i++)
   {
       pointsF[i].X = TOREAL(points[i].X);
       pointsF[i].Y = TOREAL(points[i].Y);
   }

   GpStatus status = GdipAddPathClosedCurve2(path, pointsF, count, tension);

   return status;
}

GpStatus
WINGDIPAPI
GdipAddPathRectangle(
    GpPath *path,
    REAL x,
    REAL y,
    REAL width,
    REAL height
    )
{
    API_ENTRY(GdipAddPathRectangle);
   CheckParameterValid(path);
   CheckObjectBusy(path);

   GpRectF rect(x, y, width, height);

   return path->AddRect(rect);
}

GpStatus
WINGDIPAPI
GdipAddPathRectangleI(
    GpPath *path,
    INT x,
    INT y,
    INT width,
    INT height
    )
{
    API_ENTRY(GdipAddPathRectangleI);
   return GdipAddPathRectangle(path, TOREAL(x), TOREAL(y), TOREAL(width), TOREAL(height));
}

GpStatus
WINGDIPAPI
GdipAddPathRectangles(
    GpPath *path,
    GDIPCONST GpRectF *rects,
    INT count
    )
{
    API_ENTRY(GdipAddPathRectangles);
   CheckParameter(rects && count > 0);
   CheckParameterValid(path);
   CheckObjectBusy(path);

   return path->AddRects(rects, count);
}

GpStatus
WINGDIPAPI
GdipAddPathRectanglesI(
    GpPath *path,
    GDIPCONST GpRect *rects,
    INT count
    )
{
    API_ENTRY(GdipAddPathRectanglesI);
   StackBuffer buffer;

   GpRectF *rectsF = (GpRectF*) buffer.GetBuffer(count*sizeof(GpRectF));

   if(!rectsF) return OutOfMemory;

   for (INT i=0; i<count; i++)
   {
       rectsF[i].X = TOREAL(rects[i].X);
       rectsF[i].Y = TOREAL(rects[i].Y);
       rectsF[i].Width = TOREAL(rects[i].Width);
       rectsF[i].Height = TOREAL(rects[i].Height);
   }

   GpStatus status = GdipAddPathRectangles(path, rectsF, count);

   return status;
}

GpStatus
WINGDIPAPI
GdipAddPathEllipse(
    GpPath* path,
    REAL x,
    REAL y,
    REAL width,
    REAL height
    )
{
    API_ENTRY(GdipAddPathEllipse);
    // Idea: What about just putting "GpRectF rect" as the parameter,
    //       no copying, the stack image should match the structure
    //       avoiding an additional copy.

    CheckParameterValid(path);
    CheckObjectBusy(path);

    GpRectF rect(x, y, width, height);

    return path->AddEllipse(rect);
}

GpStatus
WINGDIPAPI
GdipAddPathEllipseI(
    GpPath* path,
    INT x,
    INT y,
    INT width,
    INT height
    )
{
    API_ENTRY(GdipAddPathEllipseI);
    return GdipAddPathEllipse(path, TOREAL(x), TOREAL(y), TOREAL(width), TOREAL(height));
}

GpStatus
WINGDIPAPI
GdipAddPathPie(
    GpPath *path,
    REAL x,
    REAL y,
    REAL width,
    REAL height,
    REAL startAngle,
    REAL sweepAngle
    )
{
    API_ENTRY(GdipAddPathPie);
   CheckParameterValid(path);
   CheckObjectBusy(path);

   return path->AddPie(x, y, width, height, startAngle, sweepAngle);
}

GpStatus
WINGDIPAPI
GdipAddPathPieI(
    GpPath *path,
    INT x,
    INT y,
    INT width,
    INT height,
    REAL startAngle,
    REAL sweepAngle
    )
{
    API_ENTRY(GdipAddPathPieI);
   return GdipAddPathPie(path,
                         TOREAL(x),
                         TOREAL(y),
                         TOREAL(width),
                         TOREAL(height),
                         startAngle,
                         sweepAngle);
}

GpStatus
WINGDIPAPI
GdipAddPathPolygon(
    GpPath *path,
    GDIPCONST GpPointF *points,
    INT count
    )
{
    API_ENTRY(GdipAddPathPolygon);
   CheckParameter(points && count > 0);
   CheckParameterValid(path);
   CheckObjectBusy(path);

   return path->AddPolygon(points, count);
}

GpStatus
WINGDIPAPI
GdipAddPathPolygonI(
    GpPath *path,
    GDIPCONST GpPoint *points,
    INT count
    )
{
    API_ENTRY(GdipAddPathPolygonI);
   StackBuffer buffer;

   GpPointF* pointsF = (GpPointF*) buffer.GetBuffer(count*sizeof(GpPointF));

   if(!pointsF) return OutOfMemory;

   for (INT i=0; i<count; i++)
   {
       pointsF[i].X = TOREAL(points[i].X);
       pointsF[i].Y = TOREAL(points[i].Y);
   }

   GpStatus status = GdipAddPathPolygon(path, pointsF, count);

   return status;
}


GpStatus
WINGDIPAPI
GdipAddPathPath(
    GpPath *path,
    GDIPCONST GpPath* addingPath,
    BOOL connect
    )
{
    API_ENTRY(GdipAddPathPath);
   CheckParameterValid(path);
   CheckObjectBusy(path);

   return path->AddPath(addingPath, connect);
}

static inline
EmptyString(const WCHAR * string, INT length)
{
    return (length == 0 || length == -1 && string[0] == 0);
}

static inline
void SetEmptyRectF(RectF * boundingBox)
{
    boundingBox->X = boundingBox->Y = boundingBox->Width = boundingBox->Height = 0.0;
}

GpStatus
WINGDIPAPI
GdipAddPathString(
    GpPath                   *path,
    GDIPCONST WCHAR          *string,
    INT                       length,
    GDIPCONST GpFontFamily   *family,
    INT                       style,
    REAL                      emSize,
    GDIPCONST RectF          *layoutRect,
    GDIPCONST GpStringFormat *format
)
{
    API_ENTRY(GdipAddPathString);

    CheckParameter(string && layoutRect);
    CheckParameterValid(path);
    CheckObjectBusy(path);

    if (EmptyString(string, length))
    {
        return Ok;
    }


    GlobalTextLock lock;

    CheckParameterValid(family);
    CheckOptionalParameterValid(format);

    return path->AddString(
        string,
        length,
        family,
        style,
        emSize,
        layoutRect,
        format
    );
}

GpStatus
WINGDIPAPI
GdipAddPathStringI(
    GpPath                   *path,
    GDIPCONST WCHAR          *string,
    INT                       length,
    GDIPCONST GpFontFamily   *family,
    INT                       style,
    REAL                      emSize,
    GDIPCONST Rect           *layoutRect,
    GDIPCONST GpStringFormat *format
)
{
    API_ENTRY(GdipAddPathStringI);
    CheckParameter(layoutRect);

    // Not necessary to check all paramaters - they are validated
    // in the called function.

    GpRectF rectf(TOREAL(layoutRect->X), TOREAL(layoutRect->Y),
                  TOREAL(layoutRect->Width), TOREAL(layoutRect->Height));

    return GdipAddPathString(path, string, length, family, style, emSize,
                             &rectf, format);
}


GpStatus
WINGDIPAPI
GdipFlattenPath(
    GpPath *path,
    GpMatrix* matrix,
    REAL flatness
    )
{
    API_ENTRY(GdipFlattenPath);
    CheckParameterValid(path);
    CheckObjectBusy(path);
    CheckOptionalParameterValid(matrix);
    CheckOptionalObjectBusy(matrix);


    return path->Flatten(matrix, flatness);
}

GpStatus WINGDIPAPI
GdipWindingModeOutline(
    GpPath *path,
    GpMatrix *matrix,
    REAL flatness
)
{
    API_ENTRY(GdipWindingModeOutline);


    CheckParameterValid(path);
    CheckObjectBusy(path);
    CheckOptionalParameterValid(matrix);
    CheckOptionalObjectBusy(matrix);

    return path->ComputeWindingModeOutline(matrix, flatness);
}


GpStatus
WINGDIPAPI
GdipWidenPath(
    GpPath *path,
    GpPen *pen,
    GpMatrix *matrix,
    REAL flatness
)
{
    API_ENTRY(GdipWidenPath);


    CheckParameterValid(path);
    CheckParameterValid(pen);
    CheckObjectBusy(path);
    CheckObjectBusy(pen);
    CheckOptionalParameterValid(matrix);
    CheckOptionalObjectBusy(matrix);

    return path->Widen(pen, matrix, flatness);
}



GpStatus
WINGDIPAPI
GdipWarpPath(
    GpPath *path,
    GpMatrix* matrix,
    GDIPCONST GpPointF *points,
    INT count,
    REAL srcx,
    REAL srcy,
    REAL srcwidth,
    REAL srcheight,
    WarpMode warpMode,
    REAL flatness
    )
{
    API_ENTRY(GdipWarpPath);
    CheckParameterValid(path);
    CheckObjectBusy(path);
    CheckParameter(points && count > 0);
    CheckOptionalParameterValid(matrix);
    CheckOptionalObjectBusy(matrix);

    GpRectF srcRect(srcx, srcy, srcwidth, srcheight);


   // The flatness parameter is not implemented yet.

    return path->WarpAndFlattenSelf(matrix, points, count, srcRect, warpMode);
}

GpStatus
WINGDIPAPI
GdipTransformPath(
    GpPath *path,
    GpMatrix *matrix
    )
{
    API_ENTRY(GdipTransformPath);
    if(matrix == NULL)
        return Ok;  // No need to transform.

    CheckParameterValid(path);
    CheckParameterValid(matrix);
    CheckObjectBusy(path);
    CheckObjectBusy(matrix);

    path->Transform(matrix);

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetPathWorldBounds(
    GpPath* path,
    GpRectF* bounds,
    GDIPCONST GpMatrix *matrix,
    GDIPCONST GpPen *pen
    )
{
    API_ENTRY(GdipGetPathWorldBounds);
    CheckParameterValid(path);
    CheckObjectBusy(path);
    CheckParameter(bounds);
    CheckOptionalParameterValid(pen);
    CheckOptionalParameterValid(matrix);
    CheckOptionalObjectBusy(pen);
    CheckOptionalObjectBusy(matrix);


    // matrix and pen can be NULL.
    // So don't use CheckParameter for matrix and pen.

    GpStatus status = Ok;

    if(pen == NULL)
        status = path->GetBounds(bounds, const_cast<GpMatrix*>(matrix));
    else
        status = path->GetBounds(bounds,
                                 const_cast<GpMatrix*>(matrix),
                                 const_cast<GpPen*>(pen)->GetDevicePen());

    return status;
}

GpStatus
WINGDIPAPI
GdipGetPathWorldBoundsI(
    GpPath* path,
    GpRect* bounds,
    GDIPCONST GpMatrix *matrix,
    GDIPCONST GpPen *pen)
{
    API_ENTRY(GdipGetPathWorldBoundsI);
    CheckParameterValid(path);
    CheckObjectBusy(path);
    CheckParameter(bounds);
    CheckOptionalParameterValid(pen);
    CheckOptionalParameterValid(matrix);
    CheckOptionalObjectBusy(pen);
    CheckOptionalObjectBusy(matrix);

    // matrix and pen can be NULL.
    // So don't use CheckParameter for matrix and pen.

    GpStatus status = Ok;

    if(pen == NULL)
        status = path->GetBounds(bounds, const_cast<GpMatrix*>(matrix));
    else
        status = path->GetBounds(bounds,
                                 const_cast<GpMatrix*>(matrix),
                                 const_cast<GpPen*>(pen)->GetDevicePen());
    return status;
}

GpStatus
WINGDIPAPI
GdipIsVisiblePathPoint(
    GpPath* path,
    REAL x,
    REAL y,
    GpGraphics *graphics,
    BOOL *result
    )
{
    API_ENTRY(GdipIsVisiblePathPoint);
    CheckParameter(result);
    CheckParameterValid(path);
    CheckObjectBusy(path);

    GpPointF pt(x, y);
    GpMatrix worldToDevice;


    Status status = Ok;

    if(graphics)
    {
        CheckParameterValid(graphics);
        CheckObjectBusy(graphics);

        graphics->GetWorldToDeviceTransform(&worldToDevice);

        status = path->IsVisible(&pt, result, &worldToDevice);
    }
    else
        status = path->IsVisible(&pt, result);  // Use the default.

    return status;
}

GpStatus
WINGDIPAPI
GdipIsVisiblePathPointI(
    GpPath* path,
    INT x,
    INT y,
    GpGraphics *graphics,
    BOOL *result
    )
{
    API_ENTRY(GdipIsVisiblePathPointI);
    return GdipIsVisiblePathPoint(path,
                                  TOREAL(x),
                                  TOREAL(y),
                                  graphics,
                                  result);
}

GpStatus
WINGDIPAPI
GdipIsOutlineVisiblePathPoint(
    GpPath* path,
    REAL x,
    REAL y,
    GpPen *pen,
    GpGraphics *graphics,
    BOOL *result
    )
{
    API_ENTRY(GdipIsOutlineVisiblePathPoint);
    CheckParameter(result);
    CheckParameterValid(path);
    CheckObjectBusy(path);
    CheckParameterValid(pen);
    CheckObjectBusy(pen);

    GpPointF pt(x, y);
    GpMatrix worldToDevice;


    Status status = Ok;

    if(graphics)
    {
        CheckParameterValid(graphics);
        CheckObjectBusy(graphics);

        graphics->GetWorldToDeviceTransform(&worldToDevice);

        REAL dpiX = graphics->GetDpiX();
        REAL dpiY = graphics->GetDpiY();

        status = path->IsOutlineVisible(&pt, result, pen,
                &worldToDevice, dpiX, dpiY);
    }
    else
        status = path->IsOutlineVisible(&pt, result, pen);  // Use the default.

    return status;
}

GpStatus
WINGDIPAPI
GdipIsOutlineVisiblePathPointI(
    GpPath* path,
    INT x,
    INT y,
    GpPen *pen,
    GpGraphics *graphics,
    BOOL *result
    )
{
    API_ENTRY(GdipIsOutlineVisiblePathPointI);
    return GdipIsOutlineVisiblePathPoint(path,
                                         TOREAL(x),
                                         TOREAL(y),
                                         pen,
                                         graphics,
                                         result);
}

GpStatus WINGDIPAPI
GdipCreatePathIter(
    GpPathIterator **iterator,
    GpPath* path
    )
{
    API_ENTRY(GdipCreatePathIter);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(iterator);

    *iterator = new GpPathIterator(path);

    if (CheckValid(*iterator))
        return Ok;
    else
        return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipDeletePathIter(
    GpPathIterator *iterator
    )
{
    API_ENTRY(GdipDeletePathIter);
    // NOTE: Do NOT call CheckParameterValid(), because we need to free
    // the object, even if it's not in a valid state.
    CheckParameter(iterator);
    CheckObjectBusyForDelete(iterator);

    delete iterator;

    return Ok;
}

GpStatus WINGDIPAPI
GdipPathIterNextSubpath(
    GpPathIterator* iterator,
    INT* resultCount,
    INT* startIndex,
    INT* endIndex,
    BOOL* isClosed)
{
    API_ENTRY(GdipPathIterNextSubpath);
    CheckParameterValid(iterator);
    CheckObjectBusy(iterator);
    CheckParameter(resultCount);
    CheckParameter(startIndex);
    CheckParameter(endIndex);
    CheckParameter(isClosed);

    *resultCount = iterator->NextSubpath(startIndex, endIndex, isClosed);

    return Ok;
}

GpStatus WINGDIPAPI
GdipPathIterNextSubpathPath(
    GpPathIterator* iterator,
    INT* resultCount,
    GpPath* path,
    BOOL* isClosed)
{
    API_ENTRY(GdipPathIterNextSubpathPath);
    CheckParameterValid(iterator);
    CheckObjectBusy(iterator);
    CheckParameter(resultCount);
    CheckParameter(isClosed);

    *resultCount = iterator->NextSubpath(path, isClosed);

    return Ok;
}

GpStatus WINGDIPAPI
GdipPathIterNextPathType(
    GpPathIterator* iterator,
    INT* resultCount,
    BYTE* pathType,
    INT* startIndex,
    INT* endIndex
    )
{
    API_ENTRY(GdipPathIterNextPathType);
    CheckParameterValid(iterator);
    CheckObjectBusy(iterator);
    CheckParameter(resultCount);
    CheckParameter(pathType);
    CheckParameter(startIndex);
    CheckParameter(endIndex);

    *resultCount = iterator->NextPathType(pathType, startIndex, endIndex);

    return Ok;
}

GpStatus WINGDIPAPI
GdipPathIterNextMarker(
    GpPathIterator* iterator,
    INT* resultCount,
    INT* startIndex,
    INT* endIndex)
{
    API_ENTRY(GdipPathIterNextMarker);
    CheckParameterValid(iterator);
    CheckObjectBusy(iterator);
    CheckParameter(resultCount);
    CheckParameter(startIndex);
    CheckParameter(endIndex);

    *resultCount = iterator->NextMarker(startIndex, endIndex);

    return Ok;
}

GpStatus WINGDIPAPI
GdipPathIterNextMarkerPath(
    GpPathIterator* iterator,
    INT* resultCount,
    GpPath* path)
{
    API_ENTRY(GdipPathIterNextMarkerPath);
    CheckParameterValid(iterator);
    CheckObjectBusy(iterator);
    CheckParameter(resultCount);

    *resultCount = iterator->NextMarker(path);

    return Ok;
}

GpStatus WINGDIPAPI
GdipPathIterGetCount(
    GpPathIterator* iterator,
    INT* count
    )
{
    API_ENTRY(GdipPathIterGetCount);
    CheckParameterValid(iterator);
    CheckObjectBusy(iterator);
    CheckParameter(count);

    *count = iterator->GetCount();

     return Ok;
}

GpStatus WINGDIPAPI
GdipPathIterGetSubpathCount(
    GpPathIterator* iterator,
    INT* count
    )
{
    API_ENTRY(GdipPathIterGetSubpathCount);
    CheckParameterValid(iterator);
    CheckObjectBusy(iterator);
    CheckParameter(count);

    *count = iterator->GetSubpathCount();

    return Ok;
}

GpStatus WINGDIPAPI
GdipPathIterIsValid(
    GpPathIterator* iterator,
    BOOL* valid
    )
{
    API_ENTRY(GdipPathIterIsValid);
    CheckParameterValid(iterator);
    CheckObjectBusy(iterator);
    CheckParameter(valid);

    *valid = iterator->IsValid();

    return Ok;
}

GpStatus WINGDIPAPI
GdipPathIterHasCurve(
    GpPathIterator* iterator,
    BOOL* hasCurve
    )
{
    API_ENTRY(GdipPathIterHasCurve);
    CheckParameterValid(iterator);
    CheckObjectBusy(iterator);
    CheckParameter(hasCurve);

    *hasCurve = iterator->HasCurve();

    return Ok;
}

GpStatus WINGDIPAPI
GdipPathIterRewind(
    GpPathIterator* iterator
    )
{
    API_ENTRY(GdipPathIterRewind);
    CheckParameterValid(iterator);
    CheckObjectBusy(iterator);

    iterator->Rewind();

    return Ok;
}

GpStatus WINGDIPAPI
GdipPathIterEnumerate(
    GpPathIterator* iterator,
    INT* resultCount,
    GpPointF *points,
    BYTE *types,
    INT count)
{
    API_ENTRY(GdipPathIterEnumerate);
    CheckParameterValid(iterator);
    CheckObjectBusy(iterator);
    CheckParameter(resultCount);
    CheckParameter(points);
    CheckParameter(types);

    *resultCount = iterator->Enumerate(points, types, count);

    return Ok;
}

GpStatus WINGDIPAPI
GdipPathIterCopyData(
    GpPathIterator* iterator,
    INT* resultCount,
    GpPointF* points,
    BYTE* types,
    INT startIndex,
    INT endIndex)
{
    API_ENTRY(GdipPathIterCopyData);
    CheckParameterValid(iterator);
    CheckObjectBusy(iterator);
    CheckParameter(resultCount);
    CheckParameter(points);
    CheckParameter(types);

    *resultCount = iterator->CopyData(points, types, startIndex, endIndex);

    return Ok;
}

GpStatus
WINGDIPAPI
GdipCreateMatrix(GpMatrix **matrix)
{
    API_ENTRY(GdipCreateMatrix);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(matrix);

    *matrix = new GpMatrix();

    if (CheckValid(*matrix))
        return Ok;
    else
        return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipCreateMatrix2(
    REAL m11,
    REAL m12,
    REAL m21,
    REAL m22,
    REAL dx,
    REAL dy,
    GpMatrix ** outMatrix
    )
{
    API_ENTRY(GdipCreateMatrix2);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(outMatrix);

    GpMatrix * matrix;

    matrix = (GpMatrix *) InterlockedExchangePointer((PVOID *) &Globals::MatrixLookAside, NULL);

    if(matrix == NULL)
    {
        matrix = new GpMatrix(m11, m12, m21, m22, dx, dy);
    }
    else
    {
        matrix->GetObjectLock()->Reset();
        matrix->SetMatrix(m11, m12, m21, m22, dx, dy);
    }

    if (CheckValid(matrix))
    {
        *outMatrix = matrix;
        return Ok;
    }
    else
    {
        return OutOfMemory;
    }
}

GpStatus
WINGDIPAPI
GdipCreateMatrix3(
    GDIPCONST GpRectF *rect,
    GDIPCONST GpPointF *dstplg,
    GpMatrix **matrix
    )
{
    API_ENTRY(GdipCreateMatrix3);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(matrix && rect && dstplg);

    *matrix = new GpMatrix(dstplg, *rect);

    if (CheckValid(*matrix))
        return Ok;
    else
        return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipCreateMatrix3I(
    GDIPCONST GpRect *rect,
    GDIPCONST GpPoint *dstplg,
    GpMatrix **matrix
    )
{
    API_ENTRY(GdipCreateMatrix3I);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    GpRectF rectf(TOREAL(rect->X), TOREAL(rect->Y), TOREAL(rect->Width), TOREAL(rect->Height));
    GpPointF dstplgf[3] = { GpPointF(TOREAL(dstplg[0].X), TOREAL(dstplg[0].Y)),
                            GpPointF(TOREAL(dstplg[1].X), TOREAL(dstplg[1].Y)),
                            GpPointF(TOREAL(dstplg[2].X), TOREAL(dstplg[2].Y)) };

    return GdipCreateMatrix3(&rectf, dstplgf, matrix);
}

GpStatus
WINGDIPAPI
GdipCloneMatrix(
    GpMatrix *matrix,
    GpMatrix **clonematrix
    )
{
    API_ENTRY(GdipCloneMatrix);
    CheckParameter(clonematrix);
    CheckParameterValid(matrix);
    CheckObjectBusy(matrix);

    *clonematrix = matrix->Clone();

    if (CheckValid(*clonematrix))
        return Ok;
    else
        return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipDeleteMatrix(
    GpMatrix *matrix
    )
{
    API_ENTRY(GdipDeleteMatrix);
    // NOTE: Do NOT call CheckParameterValid(), because we need to free
    // the object, even if it's not in a valid state.
    CheckParameter(matrix);
    CheckObjectBusyForDelete(matrix);

    if(matrix != NULL)
    {

        matrix = (GpMatrix *) InterlockedExchangePointer((PVOID *) &Globals::MatrixLookAside, (PVOID) matrix);

        if(matrix != NULL)
        {
            delete matrix;
        }
    }

    return Ok;
}

GpStatus
WINGDIPAPI
GdipSetMatrixElements(
    GpMatrix *matrix,
    REAL m11,
    REAL m12,
    REAL m21,
    REAL m22,
    REAL dx,
    REAL dy
    )
{
    API_ENTRY(GdipSetMatrixElements);
   CheckParameterValid(matrix);
   CheckObjectBusy(matrix);

   REAL m[6] = { m11, m12, m21, m22, dx, dy };

   matrix->SetMatrix((REAL*) &m);

   return Ok;
}

GpStatus
WINGDIPAPI
GdipMultiplyMatrix(
    GpMatrix *matrix,
    GpMatrix *matrix2,
    GpMatrixOrder order
    )
{
    API_ENTRY(GdipMultiplyMatrix);
    CheckParameterValid(matrix);
    CheckObjectBusy(matrix);
    CheckParameterValid(matrix2);
    CheckObjectBusy(matrix2);
    CheckParameter(MatrixOrderIsValid(order));
    
    if (order == MatrixOrderPrepend)
        matrix->Prepend(*matrix2);
    else
        matrix->Append(*matrix2);

    return Ok;
}

GpStatus
WINGDIPAPI
GdipTranslateMatrix(
    GpMatrix *matrix,
    REAL offsetX,
    REAL offsetY,
    GpMatrixOrder order
    )
{
    API_ENTRY(GdipTranslateMatrix);
    CheckParameterValid(matrix);
    CheckObjectBusy(matrix);
    CheckParameter(MatrixOrderIsValid(order));

    matrix->Translate(offsetX, offsetY, order);

    return Ok;
}

GpStatus
WINGDIPAPI
GdipScaleMatrix(
    GpMatrix *matrix,
    REAL scaleX,
    REAL scaleY,
    GpMatrixOrder order
    )
{
    API_ENTRY(GdipScaleMatrix);
    CheckParameterValid(matrix);
    CheckObjectBusy(matrix);
    CheckParameter(MatrixOrderIsValid(order));

    matrix->Scale(scaleX, scaleY, order);

    return Ok;
}

GpStatus
WINGDIPAPI
GdipRotateMatrix(
    GpMatrix *matrix,
    REAL angle,
    GpMatrixOrder order
    )
{
    API_ENTRY(GdipRotateMatrix);
    CheckParameterValid(matrix);
    CheckObjectBusy(matrix);
    CheckParameter(MatrixOrderIsValid(order));

    matrix->Rotate(angle, order);

    return Ok;
}

GpStatus
WINGDIPAPI
GdipShearMatrix(
    GpMatrix *matrix,
    REAL shearX,
    REAL shearY,
    GpMatrixOrder order
    )
{
    API_ENTRY(GdipShearMatrix);
    CheckParameterValid(matrix);
    CheckObjectBusy(matrix);
    CheckParameter(MatrixOrderIsValid(order));

    matrix->Shear(shearX, shearY, order);

    return Ok;
}

GpStatus
WINGDIPAPI
GdipInvertMatrix(GpMatrix *matrix)
{
    API_ENTRY(GdipInvertMatrix);
   CheckParameterValid(matrix);
   CheckObjectBusy(matrix);

   return matrix->Invert();
}

GpStatus
WINGDIPAPI
GdipTransformMatrixPoints(
    GpMatrix *matrix,
    GpPointF *pts,
    INT count
    )
{
    API_ENTRY(GdipTransformMatrixPoints);
   CheckParameter(pts && count > 0);
   CheckParameterValid(matrix);
   CheckObjectBusy(matrix);

   matrix->Transform(pts, count);

   return Ok;
}

GpStatus
WINGDIPAPI
GdipTransformMatrixPointsI(
    GpMatrix *matrix,
    GpPoint *points,
    INT count
    )
{
    API_ENTRY(GdipTransformMatrixPointsI);
   StackBuffer buffer;

   GpPointF* pointsF = (GpPointF*) buffer.GetBuffer(count*sizeof(GpPointF));

   if(!pointsF) return OutOfMemory;

   for (INT i=0; i<count; i++)
   {
       pointsF[i].X = TOREAL(points[i].X);
       pointsF[i].Y = TOREAL(points[i].Y);
   }

   GpStatus status = GdipTransformMatrixPoints(matrix, pointsF, count);

   // convert back, rounding is a problem...

   if (status == Ok)
   {
       for (INT i=0; i<count; i++)
       {
           points[i].X = GpRound(pointsF[i].X);
           points[i].Y = GpRound(pointsF[i].Y);
       }
   }

   return status;
}

GpStatus
WINGDIPAPI
GdipVectorTransformMatrixPoints(
    GpMatrix *matrix,
    GpPointF *pointsF,
    INT count
    )
{
    API_ENTRY(GdipVectorTransformMatrixPoints);
   CheckParameter(pointsF && count > 0);
   CheckParameterValid(matrix);
   CheckObjectBusy(matrix);

   matrix->VectorTransform(pointsF, count);

   return Ok;
}

GpStatus
WINGDIPAPI
GdipVectorTransformMatrixPointsI(
    GpMatrix *matrix,
    GpPoint *points,
    INT count
    )
{
    API_ENTRY(GdipVectorTransformMatrixPointsI);
   CheckParameter(points && count > 0);
   CheckParameterValid(matrix);
   CheckObjectBusy(matrix);

   StackBuffer buffer;

   GpPointF* pointsF = (GpPointF*) buffer.GetBuffer(count*sizeof(GpPointF));

   if(!pointsF) return OutOfMemory;

   for (INT i=0; i<count; i++)
   {
       pointsF[i].X = TOREAL(points[i].X);
       pointsF[i].Y = TOREAL(points[i].Y);
   }

   matrix->VectorTransform(pointsF, count);

   // convert back, rounding is a problem...


   for (i=0; i<count; i++)
   {
        points[i].X = GpRound(pointsF[i].X);
        points[i].Y = GpRound(pointsF[i].Y);
   }

   return Ok;
}

GpStatus
WINGDIPAPI
GdipGetMatrixElements(
    GDIPCONST GpMatrix *matrix,
    REAL *m
   )
{
    API_ENTRY(GdipGetMatrixElements);
   CheckParameter(m);
   CheckParameterValid(matrix);
   CheckObjectBusy(matrix);

   matrix->GetMatrix(m);

   return Ok;
}

GpStatus
WINGDIPAPI
GdipIsMatrixInvertible(
    GDIPCONST GpMatrix *matrix,
    BOOL *result
    )
{
    API_ENTRY(GdipIsMatrixInvertible);
   CheckParameter(result);
   CheckParameterValid(matrix);
   CheckObjectBusy(matrix);

   *result = matrix->IsInvertible();

   return Ok;
}

GpStatus
WINGDIPAPI
GdipIsMatrixIdentity(
    GDIPCONST GpMatrix *matrix,
    BOOL *result
    )
{
    API_ENTRY(GdipIsMatrixIdentity);
   CheckParameter(result);
   CheckParameterValid(matrix);
   CheckObjectBusy(matrix);

   *result = matrix->IsIdentity();

   return Ok;
}

GpStatus
WINGDIPAPI
GdipIsMatrixEqual(
    GDIPCONST GpMatrix *matrix,
    GDIPCONST GpMatrix *matrix2,
    BOOL *result
    )
{
    API_ENTRY(GdipIsMatrixEqual);
    CheckParameter(result);
    CheckParameterValid(matrix);
    CheckObjectBusy(matrix);

    if (matrix != matrix2)
    {
        CheckParameterValid(matrix2);
        CheckObjectBusy(matrix2);

        *result = matrix->IsEqual(matrix2);
    }
    else
    {
        *result = TRUE;
    }

    return Ok;
}

GpStatus
WINGDIPAPI
GdipCreateRegion(
    GpRegion **region
    )
{
    API_ENTRY(GdipCreateRegion);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(region);

    *region = new GpRegion();

    if (CheckValid(*region))
        return Ok;
    else
        return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipCreateRegionRect(
    GDIPCONST GpRectF *rect,
    GpRegion **region
    )
{
    API_ENTRY(GdipCreateRegionRect);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(region && rect);

    *region = new GpRegion(rect);

    if (CheckValid(*region))
        return Ok;
    else
        return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipCreateRegionRectI(
    GDIPCONST GpRect *rect,
    GpRegion **region
    )
{
    API_ENTRY(GdipCreateRegionRectI);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    GpRectF rectf(TOREAL(rect->X),
                  TOREAL(rect->Y),
                  TOREAL(rect->Width),
                  TOREAL(rect->Height));

    return GdipCreateRegionRect(&rectf, region);
}

GpStatus
WINGDIPAPI
GdipCreateRegionPath(
    GpPath *path,
    GpRegion **region
    )
{
    API_ENTRY(GdipCreateRegionPath);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(region);
    CheckParameterValid(path);
    CheckObjectBusy(path);

    *region = new GpRegion(path);

    if (CheckValid(*region))
        return Ok;
    else
        return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipCreateRegionRgnData(
    GDIPCONST BYTE *regionData,
    INT size,
    GpRegion **region
    )
{
    API_ENTRY(GdipCreateRegionRgnData);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(regionData);
    CheckParameter(region);

    *region = new GpRegion(regionData, size);

    if (CheckValid(*region))
        return Ok;
    else
        return GenericError;
}

GpStatus
WINGDIPAPI
GdipCreateRegionHrgn(
    HRGN hRgn,
    GpRegion **region
    )
{
    API_ENTRY(GdipCreateRegionHrgn);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(region && hRgn && (GetObjectTypeInternal(hRgn) == OBJ_REGION));

    *region = new GpRegion(hRgn);

    if (CheckValid(*region))
        return Ok;
    else
        return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipCloneRegion(
    GpRegion *region,
    GpRegion **cloneregion
    )
{
    API_ENTRY(GdipCloneRegion);
    CheckParameter(cloneregion);
    CheckParameterValid(region);
    CheckObjectBusy(region);

    *cloneregion = new GpRegion(region);

    if (CheckValid(*cloneregion))
        return Ok;
    else
        return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipDeleteRegion(
    GpRegion *region
    )
{
    API_ENTRY(GdipDeleteRegion);
    // NOTE: Do NOT call CheckParameterValid(), because we need to free
    // the object, even if it's not in a valid state.
    CheckParameter(region);
    CheckObjectBusyForDelete(region);

    delete region;

    return Ok;
}

GpStatus
WINGDIPAPI
GdipSetInfinite(
    GpRegion *region
)
{
    API_ENTRY(GdipSetInfinite);
    CheckParameterValid(region);
    CheckObjectBusy(region);

    region->SetInfinite();
    return Ok;
}

GpStatus
WINGDIPAPI
GdipSetEmpty(
    GpRegion *region
)
{
    API_ENTRY(GdipSetEmpty);
    CheckParameterValid(region);
    CheckObjectBusy(region);

    region->SetEmpty();
    return Ok;
}

GpStatus
WINGDIPAPI
GdipCombineRegionRect(
    GpRegion *                region,
    GDIPCONST GpRectF *       rect,
    CombineMode               combineMode
    )
{
    API_ENTRY(GdipCombineRegionRect);
    CheckParameter(rect);
    CheckParameterValid(region);
    CheckObjectBusy(region);
    CheckParameter(CombineModeIsValid(combineMode));

    return region->Combine(rect, combineMode);
}

GpStatus
WINGDIPAPI
GdipCombineRegionRectI(
    GpRegion *                region,
    GDIPCONST GpRect *        rect,
    CombineMode               combineMode
    )
{
    API_ENTRY(GdipCombineRegionRectI);
    GpRectF rectf(TOREAL(rect->X),
                  TOREAL(rect->Y),
                  TOREAL(rect->Width),
                  TOREAL(rect->Height));

    return GdipCombineRegionRect(region, &rectf, combineMode);
}

GpStatus
WINGDIPAPI
GdipCombineRegionPath(
    GpRegion *      region,
    GpPath *        path,
    CombineMode     combineMode
    )
{
    API_ENTRY(GdipCombineRegionPath);
    CheckParameterValid(region);
    CheckParameterValid(path);
    CheckObjectBusy(region);
    CheckObjectBusy(path);
    CheckParameter(CombineModeIsValid(combineMode));

    return region->Combine(path, combineMode);
}

GpStatus
WINGDIPAPI
GdipCombineRegionRegion(
    GpRegion *      region,
    GpRegion *      region2,
    CombineMode     combineMode
    )
{
    API_ENTRY(GdipCombineRegionRegion);
    CheckParameterValid(region);
    CheckObjectBusy(region);
    CheckParameterValid(region2);
    CheckObjectBusy(region2);
    CheckParameter(CombineModeIsValid(combineMode));

    return region->Combine(region2, combineMode);
}

GpStatus
WINGDIPAPI
GdipTranslateRegion(
    GpRegion *region,
    REAL dx,
    REAL dy
    )
{
    API_ENTRY(GdipTranslateRegion);
   CheckParameter(region);
   CheckParameterValid(region);

   region->Offset(dx, dy);

   return Ok;
}

GpStatus
WINGDIPAPI
GdipTranslateRegionI(
    GpRegion *region,
    INT dx,
    INT dy
    )
{
    API_ENTRY(GdipTranslateRegionI);
   return GdipTranslateRegion(region, TOREAL(dx), TOREAL(dy));
}

GpStatus
WINGDIPAPI
GdipTransformRegion(
    GpRegion *region,
    GpMatrix *matrix
    )
{
    API_ENTRY(GdipTransformRegion);
   CheckParameterValid(matrix);
   CheckObjectBusy(matrix);
   CheckParameterValid(region);
   CheckObjectBusy(region);

   return region->Transform(matrix);
}

GpStatus
WINGDIPAPI
GdipGetRegionBounds(
    GpRegion *region,
    GpGraphics *graphics,
    GpRectF *rect
    )
{
    API_ENTRY(GdipGetRegionBounds);
    CheckParameter(rect);
    CheckParameterValid(region);
    CheckObjectBusy(region);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);


    return region->GetBounds(graphics, rect);
}

GpStatus
WINGDIPAPI
GdipGetRegionBoundsI(
    GpRegion *region,
    GpGraphics *graphics,
    GpRect *rect
    )
{
    API_ENTRY(GdipGetRegionBoundsI);
    CheckParameter(rect);
    CheckParameterValid(region);
    CheckObjectBusy(region);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);


    GpRectF     rectF;
    GpStatus    status = region->GetBounds(graphics, &rectF);

    if (status == Ok)
    {
        rect->X      = GpRound(rectF.X);
        rect->Y      = GpRound(rectF.Y);
        rect->Width  = GpRound(rectF.Width);
        rect->Height = GpRound(rectF.Height);
    }
    return status;
}

GpStatus
WINGDIPAPI
GdipGetRegionHRgn(
    GpRegion *region,
    GpGraphics *graphics,   // can be NULL
    HRGN *hRgn
    )
{
    API_ENTRY(GdipGetRegionHRgn);
    CheckParameter(hRgn);
    CheckParameterValid(region);
    CheckObjectBusy(region);


    if (graphics != NULL)
    {
        CheckParameterValid(graphics);
        CheckObjectBusy(graphics);
        return region->GetHRgn(graphics, hRgn);
    }
    return region->GetHRgn((GpGraphics*)NULL, hRgn);
}

GpStatus
WINGDIPAPI
GdipIsEmptyRegion(
    GpRegion *region,
    GpGraphics *graphics,
    BOOL *result
    )
{
    API_ENTRY(GdipIsEmptyRegion);
    CheckParameter(result);
    CheckParameterValid(region);
    CheckObjectBusy(region);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);

    GpMatrix worldToDevice;

    Status status;

    graphics->GetWorldToDeviceTransform(&worldToDevice);

    status = region->IsEmpty(&worldToDevice, result);

    return status;
}

GpStatus
WINGDIPAPI
GdipIsInfiniteRegion(
    GpRegion *region,
    GpGraphics *graphics,
    BOOL *result
    )
{
    API_ENTRY(GdipIsInfiniteRegion);
    CheckParameter(result);
    CheckParameterValid(region);
    CheckObjectBusy(region);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);

    GpMatrix worldToDevice;

    Status status;

    graphics->GetWorldToDeviceTransform(&worldToDevice);

    status = region->IsInfinite(&worldToDevice, result);

    return status;
}

GpStatus
WINGDIPAPI
GdipIsEqualRegion(
    GpRegion *region,
    GpRegion *region2,
    GpGraphics *graphics,
    BOOL *result
    )
{
    API_ENTRY(GdipIsEqualRegion);
    CheckParameter(result);
    CheckParameterValid(region);
    CheckObjectBusy(region);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);

    Status status;

    if (region != region2)
    {
        CheckParameterValid(region2);
        CheckObjectBusy(region2);


        GpMatrix worldToDevice;

        graphics->GetWorldToDeviceTransform(&worldToDevice);

        status = region->IsEqual(region2, &worldToDevice, result);
    }
    else
    {
        *result = TRUE;
        status = Ok;
    }

    return status;
}

GpStatus
WINGDIPAPI
GdipGetRegionDataSize(
    GpRegion *  region,
    UINT *       bufferSize
    )
{
    API_ENTRY(GdipGetRegionDataSize);
    CheckParameterValid(region);
    CheckObjectBusy(region);
    CheckParameter(bufferSize);


    if ((*bufferSize = region->GetExternalDataSize()) > 0)
    {
        return Ok;
    }
    return GenericError;
}

GpStatus
WINGDIPAPI
GdipGetRegionData(
    GpRegion *  region,
    BYTE *      buffer,
    UINT         bufferSize,
    UINT *       sizeFilled
    )
{
    API_ENTRY(GdipGetRegionData);
    CheckParameterValid(region);
    CheckObjectBusy(region);
    CheckParameter(buffer);
    CheckParameter(bufferSize > 0);


    UINT    filled = bufferSize;

    GpStatus status = region->GetExternalData(buffer, filled);

    ASSERT((INT)filled <= bufferSize);
    if (sizeFilled != NULL)
    {
        *sizeFilled = filled;
    }

    return status;
}

GpStatus
WINGDIPAPI
GdipIsVisibleRegionPoint(
    GpRegion *region,
    REAL x,
    REAL y,
    GpGraphics *graphics,
    BOOL *result
    )
{
    API_ENTRY(GdipIsVisibleRegionPoint);
    CheckParameter(result);
    CheckParameterValid(region);
    CheckObjectBusy(region);


    Status status;

    GpPointF pt(x, y);
    GpMatrix worldToDevice;

    if (graphics != NULL)
    {
        CheckParameterValid(graphics);
        CheckObjectBusy(graphics);

        graphics->GetWorldToDeviceTransform(&worldToDevice);
    }

    status = region->IsVisible(&pt, &worldToDevice, result);

    return status;
}

GpStatus
WINGDIPAPI
GdipIsVisibleRegionPointI(
    GpRegion *region,
    INT x,
    INT y,
    GpGraphics *graphics,
    BOOL *result
    )
{
    API_ENTRY(GdipIsVisibleRegionPointI);
    return GdipIsVisibleRegionPoint(region, TOREAL(x), TOREAL(y), graphics, result);
}

GpStatus
WINGDIPAPI
GdipIsVisibleRegionRect(
    GpRegion *region,
    REAL x,
    REAL y,
    REAL width,
    REAL height,
    GpGraphics *graphics,
    BOOL *result
    )
{
    API_ENTRY(GdipIsVisibleRegionRect);
    CheckParameter(result);
    CheckParameterValid(region);
    CheckObjectBusy(region);


    Status status;

    GpRectF rect(x, y, width, height);
    GpMatrix worldToDevice;

    if (graphics != NULL)
    {
        CheckParameterValid(graphics);
        CheckObjectBusy(graphics);

        graphics->GetWorldToDeviceTransform(&worldToDevice);
    }

    status = region->IsVisible(&rect, &worldToDevice, result);

    return status;
}


GpStatus
WINGDIPAPI
GdipIsVisibleRegionRectI(
    GpRegion *region,
    INT x,
    INT y,
    INT width,
    INT height,
    GpGraphics *graphics,
    BOOL *result
    )
{
    API_ENTRY(GdipIsVisibleRegionRectI);
    return GdipIsVisibleRegionRect(region,
                                   TOREAL(x),
                                   TOREAL(y),
                                   TOREAL(width),
                                   TOREAL(height),
                                   graphics,
                                   result);
}

GpStatus
WINGDIPAPI
GdipGetRegionScansCount(
    GpRegion *region,
    UINT *count,
    GpMatrix *matrix
    )
{
    API_ENTRY(GdipGetRegionScansCount);
    CheckParameterValid(region);
    CheckObjectBusy(region);
    CheckParameter(count);
    CheckParameterValid(matrix);
    CheckObjectBusy(matrix);


    // !! Rewrite this API to be more efficient
    return region->GetRegionScans((GpRect*)NULL,
                                  (INT*)count,
                                  matrix);
}

// If rects is NULL, return the count of rects in the region.
// Otherwise, assume rects is big enough to hold all the region rects
// and fill them in and return the number of rects filled in.
GpStatus
WINGDIPAPI
GdipGetRegionScans(
    GpRegion *region,
    GpRectF *rects,     // NULL to just get the count
    INT *count,
    GpMatrix *matrix
    )
{
    API_ENTRY(GdipGetRegionScans);
    CheckParameterValid(region);
    CheckObjectBusy(region);
    CheckParameter(count);
    CheckParameterValid(matrix);
    CheckObjectBusy(matrix);


    // !! Rewrite this API to verify IN count is sufficient
    return region->GetRegionScans(rects, count, matrix);
}

// If rects is NULL, return the count of rects in the region.
// Otherwise, assume rects is big enough to hold all the region rects
// and fill them in and return the number of rects filled in.
GpStatus
WINGDIPAPI
GdipGetRegionScansI(
    GpRegion *region,
    GpRect *rects,      // NULL to just get the count
    INT *count,
    GpMatrix *matrix
    )
{
    API_ENTRY(GdipGetRegionScansI);
    CheckParameterValid(region);
    CheckObjectBusy(region);
    CheckParameter(count);
    CheckParameterValid(matrix);
    CheckObjectBusy(matrix);


    return region->GetRegionScans(rects, count, matrix);
}

GpStatus
WINGDIPAPI
GdipCloneBrush(
    GpBrush *brush,
    GpBrush **clonebrush
    )
{
    API_ENTRY(GdipCloneBrush);
    CheckParameter(clonebrush);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    *clonebrush = brush->Clone();

    if (CheckValid(*clonebrush))
        return Ok;
    else
        return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipDeleteBrush(
    GpBrush *brush
    )
{
    API_ENTRY(GdipDeleteBrush);
    // NOTE: Do NOT call CheckParameterValid(), because we need to free
    // the object, even if it's not in a valid state.
    CheckParameter(brush);
    CheckObjectBusyForDelete(brush);

    delete brush;

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetBrushType(
    GpBrush *brush,
    GpBrushType *type
    )
{
    API_ENTRY(GdipGetBrushType);
    CheckParameter(type);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    *type = brush->GetBrushType();

    return Ok;
}

GpStatus
WINGDIPAPI
GdipCreateHatchBrush(
    GpHatchStyle hatchstyle,
    ARGB forecol,
    ARGB backcol,
    GpHatch **hatch
    )
{
    API_ENTRY(GdipCreateHatchBrush);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter((hatchstyle >= HatchStyleMin) && (hatchstyle <= HatchStyleMax));
    CheckParameter(hatch);
    CheckColorParameter(forecol);
    CheckColorParameter(backcol);

    GpColor fore(forecol);
    GpColor back(backcol);

    *hatch = new GpHatch(hatchstyle, fore, back);

    if (CheckValid(*hatch))
        return Ok;
    else
        return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipGetHatchStyle(
    GpHatch *brush,
    GpHatchStyle *hatchStyle
    )
{
    API_ENTRY(GdipGetHatchStyle);
    CheckParameter(hatchStyle);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    *hatchStyle = brush->GetHatchStyle();

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetHatchForegroundColor(
    GpHatch *brush,
    ARGB *argb
    )
{
    API_ENTRY(GdipGetHatchForegroundColor);
    CheckParameter(argb);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    GpColor foreColor;

    GpStatus status = brush->GetForegroundColor((GpColor*)&foreColor);

    *argb = foreColor.GetValue();

    return status;
}

GpStatus
WINGDIPAPI
GdipGetHatchBackgroundColor(
    GpHatch *brush,
    ARGB *argb
    )
{
    API_ENTRY(GdipGetHatchBackgroundColor);
    CheckParameter(argb);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    GpColor backColor;

    GpStatus status = brush->GetBackgroundColor((GpColor*)&backColor);

    *argb = backColor.GetValue();

    return status;
}

GpStatus
WINGDIPAPI
GdipCreateTexture(
    GpImage *image,
    GpWrapMode wrapmode,
    GpTexture **texture
    )
{
    API_ENTRY(GdipCreateTexture);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(texture);
    CheckParameterValid(image);
    CheckObjectBusy(image);


    *texture = new GpTexture(image, wrapmode);

    if (CheckValid(*texture))
        return Ok;
    else
        return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipCreateTexture2(
    GpImage *image,
    GpWrapMode wrapmode,
    REAL x,
    REAL y,
    REAL width,
    REAL height,
    GpTexture **texture
    )
{
    API_ENTRY(GdipCreateTexture2);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(texture);
    CheckParameterValid(image);
    CheckObjectBusy(image);

    GpRectF rect(x, y, width, height);


    *texture = new GpTexture(image, wrapmode, rect);

    if (CheckValid(*texture))
        return Ok;
    else
        return OutOfMemory;
}
GpStatus
WINGDIPAPI
GdipCreateTextureIA(
    GpImage *image,
    GDIPCONST GpImageAttributes *imageAttributes,
    REAL x,
    REAL y,
    REAL width,
    REAL height,
    GpTexture **texture
    )
{
    API_ENTRY(GdipCreateTextureIA);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(texture);
    CheckParameterValid(image);
    CheckObjectBusy(image);
    CheckOptionalParameterValid(imageAttributes);
    CheckOptionalObjectBusy(imageAttributes);

    GpRectF rect(x, y, width, height);


    *texture = new GpTexture(image, rect, imageAttributes);

    if (CheckValid(*texture))
        return Ok;
    else
        return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipCreateTexture2I(
    GpImage *image,
    GpWrapMode wrapmode,
    INT x,
    INT y,
    INT width,
    INT height,
    GpTexture **texture
    )
{
    API_ENTRY(GdipCreateTexture2I);
    CheckGdiplusInitialized; // We do this in all our object creation API's

   return GdipCreateTexture2(image,
                             wrapmode,
                             TOREAL(x),
                             TOREAL(y),
                             TOREAL(width),
                             TOREAL(height),
                             texture);
}

GpStatus
WINGDIPAPI
GdipCreateTextureIAI(
    GpImage *image,
    GDIPCONST GpImageAttributes *imageAttributes,
    INT x,
    INT y,
    INT width,
    INT height,
    GpTexture **texture
    )
{

    return GdipCreateTextureIA(image,
                               imageAttributes,
                               TOREAL(x),
                               TOREAL(y),
                               TOREAL(width),
                               TOREAL(height),
                               texture);
}

GpStatus
WINGDIPAPI
GdipSetTextureTransform(
    GpTexture *brush,
    GDIPCONST GpMatrix *matrix
    )
{
    API_ENTRY(GdipSetTextureTransform);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);
    CheckParameterValid(matrix);
    CheckObjectBusy(matrix);

    return brush->SetTransform(*matrix);
}

GpStatus
WINGDIPAPI
GdipGetTextureTransform(
    GpTexture *brush,
    GpMatrix *matrix
    )
{
    API_ENTRY(GdipGetTextureTransform);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);
    CheckParameterValid(matrix);
    CheckObjectBusy(matrix);

    return brush->GetTransform(matrix);
}

GpStatus
WINGDIPAPI
GdipResetTextureTransform(
    GpTexture* brush)
{
    API_ENTRY(GdipResetTextureTransform);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    return brush->ResetTransform();
}

GpStatus
WINGDIPAPI
GdipMultiplyTextureTransform(
    GpTexture* brush,
    GDIPCONST GpMatrix *matrix,
    GpMatrixOrder order)
{
    API_ENTRY(GdipMultiplyTextureTransform);
    if(matrix == NULL)
        return Ok;

    CheckParameterValid(brush);
    CheckObjectBusy(brush);
    CheckParameterValid(matrix);
    CheckObjectBusy(matrix);

    return brush->MultiplyTransform(*matrix, order);
}

GpStatus
WINGDIPAPI
GdipTranslateTextureTransform(
    GpTexture* brush,
    REAL dx,
    REAL dy,
    GpMatrixOrder order
    )
{
    API_ENTRY(GdipTranslateTextureTransform);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);
    CheckParameter(MatrixOrderIsValid(order));

    return brush->TranslateTransform(dx, dy, order);
}

GpStatus
WINGDIPAPI
GdipScaleTextureTransform(
    GpTexture* brush,
    REAL sx,
    REAL sy,
    GpMatrixOrder order
    )
{
    API_ENTRY(GdipScaleTextureTransform);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);
    CheckParameter(MatrixOrderIsValid(order));

    return brush->ScaleTransform(sx, sy, order);
}

GpStatus
WINGDIPAPI
GdipRotateTextureTransform(
    GpTexture* brush,
    REAL angle,
    GpMatrixOrder order
    )
{
    API_ENTRY(GdipRotateTextureTransform);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);
    CheckParameter(MatrixOrderIsValid(order));

    return brush->RotateTransform(angle, order);
}

GpStatus
WINGDIPAPI
GdipSetTextureWrapMode(
    GpTexture *brush,
    GpWrapMode wrapmode
    )
{
    API_ENTRY(GdipSetTextureWrapMode);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    brush->SetWrapMode(wrapmode);

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetTextureWrapMode(
    GpTexture *brush,
    GpWrapMode *wrapmode
    )
{
    API_ENTRY(GdipGetTextureWrapMode);
    CheckParameter(wrapmode);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    *wrapmode = (GpWrapMode)brush->GetWrapMode();

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetTextureImage(
    GpTexture *brush,
    GpImage **image
    )
{
    API_ENTRY(GdipGetTextureImage);
    // GetImage returns a pointer to the internal GpImage structure. Because
    // we have to create a new Image wrapper around it and give it to the caller
    // at the API level, we return a clone so they can throw it away without
    // destroying our internal brush structure.
    GpImage *imgtmp;
    imgtmp = brush->GetImage();
    *image = imgtmp->Clone();
    return Ok;
}

GpStatus
WINGDIPAPI
GdipCreateSolidFill(
    ARGB color,
    GpSolidFill **solidfill
    )
{
    API_ENTRY(GdipCreateSolidFill);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(solidfill);
    CheckColorParameter(color);

    *solidfill = new GpSolidFill(GpColor(color));

    if (CheckValid(*solidfill))
        return Ok;
    else
        return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipSetSolidFillColor(
    GpSolidFill *brush,
    ARGB color
    )
{
    API_ENTRY(GdipSetSolidFillColor);
    CheckColorParameter(color);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    brush->SetColor(GpColor(color));

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetSolidFillColor(
    GpSolidFill *brush,
    ARGB *color
    )
{
    API_ENTRY(GdipGetSolidFillColor);
    CheckParameter(color);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    *color = (brush->GetColor()).GetValue();

    return Ok;
}

GpStatus
WINGDIPAPI
GdipCreateLineBrush(
    GDIPCONST GpPointF* point1,
    GDIPCONST GpPointF* point2,
    ARGB color1,
    ARGB color2,
    GpWrapMode wrapmode,
    GpLineGradient **linegrad
    )
{
    API_ENTRY(GdipCreateLineBrush);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(linegrad && point1 && point2);
    CheckColorParameter(color1);
    CheckColorParameter(color2);

    if(wrapmode == WrapModeClamp)
    {
        return InvalidParameter;
    }

    GpColor c1(color1);
    GpColor c2(color2);

    *linegrad = new GpLineGradient(*point1, *point2, c1, c2, wrapmode);

    if (CheckValid(*linegrad))
        return Ok;
    else
        return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipCreateLineBrushI(
    GDIPCONST GpPoint* point1,
    GDIPCONST GpPoint* point2,
    ARGB color1,
    ARGB color2,
    GpWrapMode wrapmode,
    GpLineGradient **linegrad
    )
{
    API_ENTRY(GdipCreateLineBrushI);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckColorParameter(color1);
    CheckColorParameter(color2);
    CheckParameter(point1 && point2);

    if(wrapmode == WrapModeClamp)
    {
        return InvalidParameter;
    }

    GpPointF point1F(TOREAL(point1->X), TOREAL(point1->Y));
    GpPointF point2F(TOREAL(point2->X), TOREAL(point2->Y));

    return GdipCreateLineBrush(&point1F,
                               &point2F,
                               color1,
                               color2,
                               wrapmode,
                               linegrad);
}

GpStatus
WINGDIPAPI
GdipCreateLineBrushFromRect(
    GDIPCONST GpRectF* rect,
    ARGB color1,
    ARGB color2,
    LinearGradientMode mode,
    GpWrapMode wrapmode,
    GpLineGradient **linegrad)
{
    API_ENTRY(GdipCreateLineBrushFromRect);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckColorParameter(color1);
    CheckColorParameter(color2);
    CheckParameter(linegrad && rect);

    if(wrapmode == WrapModeClamp)
    {
        return InvalidParameter;
    }

    GpColor c1(color1);
    GpColor c2(color2);

    *linegrad = new GpLineGradient(*rect, c1, c2, mode, wrapmode);

    if (CheckValid(*linegrad))
        return Ok;
    else
        return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipCreateLineBrushFromRectI(
    GDIPCONST GpRect* rect,
    ARGB color1,
    ARGB color2,
    LinearGradientMode mode,
    GpWrapMode wrapmode,
    GpLineGradient **linegrad)
{
    API_ENTRY(GdipCreateLineBrushFromRectI);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckColorParameter(color1);
    CheckColorParameter(color2);
    CheckParameter(rect);

    if(wrapmode == WrapModeClamp)
    {
        return InvalidParameter;
    }

    GpRectF rectF(TOREAL(rect->X),
                  TOREAL(rect->Y),
                  TOREAL(rect->Width),
                  TOREAL(rect->Height));

    return GdipCreateLineBrushFromRect(&rectF,
                                       color1,
                                       color2,
                                       mode,
                                       wrapmode,
                                       linegrad);
}

GpStatus
WINGDIPAPI
GdipCreateLineBrushFromRectWithAngle(
    GDIPCONST GpRectF* rect,
    ARGB color1,
    ARGB color2,
    REAL angle,
    BOOL isAngleScalable,
    GpWrapMode wrapmode,
    GpLineGradient **linegrad)
{
    API_ENTRY(GdipCreateLineBrushFromRectWithAngle);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckColorParameter(color1);
    CheckColorParameter(color2);
    CheckParameter(linegrad && rect);

    if(wrapmode == WrapModeClamp)
    {
        return InvalidParameter;
    }

    GpColor c1(color1);
    GpColor c2(color2);

    *linegrad = new GpLineGradient(*rect, c1, c2, angle,
                        isAngleScalable, wrapmode);

    if (CheckValid(*linegrad))
        return Ok;
    else
        return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipCreateLineBrushFromRectWithAngleI(
    GDIPCONST GpRect* rect,
    ARGB color1,
    ARGB color2,
    REAL angle,
    BOOL isAngleScalable,
    GpWrapMode wrapmode,
    GpLineGradient **linegrad)
{
    API_ENTRY(GdipCreateLineBrushFromRectWithAngleI);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckColorParameter(color1);
    CheckColorParameter(color2);
    CheckParameter(rect);

    if(wrapmode == WrapModeClamp)
    {
        return InvalidParameter;
    }

    GpRectF rectF(TOREAL(rect->X),
                  TOREAL(rect->Y),
                  TOREAL(rect->Width),
                  TOREAL(rect->Height));

    return GdipCreateLineBrushFromRectWithAngle(&rectF,
                                       color1,
                                       color2,
                                       angle,
                                       isAngleScalable,
                                       wrapmode,
                                       linegrad);
}

GpStatus
WINGDIPAPI
GdipSetLineColors(
    GpLineGradient *brush,
    ARGB argb1,
    ARGB argb2
    )
{
    API_ENTRY(GdipSetLineColors);
    CheckColorParameter(argb1);
    CheckColorParameter(argb2);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    GpColor c1(argb1);
    GpColor c2(argb2);

    brush->SetLineColors(c1, c2);

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetLineColors(
    GpLineGradient *brush,
    ARGB *argb
    )
{
    API_ENTRY(GdipGetLineColors);
   CheckParameter(argb);
   CheckParameterValid(brush);
   CheckObjectBusy(brush);

   GpColor colors[2];
   brush->GetLineColors(&colors[0]);

   argb[0] = colors[0].GetValue();
   argb[1] = colors[1].GetValue();

   return Ok;
}

GpStatus
WINGDIPAPI
GdipGetLineRect(
    GpLineGradient *brush,
    GpRectF *rect
    )
{
    API_ENTRY(GdipGetLineRect);
   CheckParameter(rect);
   CheckParameterValid(brush);
   CheckObjectBusy(brush);

   brush->GetRect(*rect);

   return Ok;
}

GpStatus
WINGDIPAPI
GdipGetLineRectI(
    GpLineGradient *brush,
    GpRect *rect
    )
{
    API_ENTRY(GdipGetLineRectI);
    CheckParameter(rect);

    GpRectF rectF;

    GpStatus status = GdipGetLineRect(brush, &rectF);

    if (status == Ok)
    {

        rect->X = GpRound(rectF.X);
        rect->Y = GpRound(rectF.Y);
        rect->Width = GpRound(rectF.Width);
        rect->Height = GpRound(rectF.Height);
    }

    return status;
}

GpStatus
WINGDIPAPI
GdipSetLineGammaCorrection(
    GpLineGradient *brush,
    BOOL useGammaCorrection
    )
{
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    brush->SetGammaCorrection(useGammaCorrection);
    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetLineGammaCorrection(
    GpLineGradient *brush,
    BOOL *useGammaCorrection)
{
    CheckParameter(useGammaCorrection);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    *useGammaCorrection = brush->GetGammaCorrection();

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetLineBlendCount(
    GpLineGradient *brush,
    INT *count
    )
{
    API_ENTRY(GdipGetLineBlendCount);
   CheckParameter(count);
   CheckParameterValid(brush);
   CheckObjectBusy(brush);

   *count = brush->GetBlendCount();

   return Ok;
}

GpStatus
WINGDIPAPI
GdipGetLineBlend(
    GpLineGradient *brush,
    REAL *blend,
    REAL *positions,
    INT count
    )
{
    API_ENTRY(GdipGetLineBlend);
   CheckParameter(blend);
   CheckParameter(positions);
   CheckParameterValid(brush);
   CheckObjectBusy(brush);

   return brush->GetBlend(blend, positions, count);
}

GpStatus
WINGDIPAPI
GdipSetLineBlend(
    GpLineGradient *brush,
    GDIPCONST REAL *blend,
    GDIPCONST REAL *positions,
    INT count
    )
{
    API_ENTRY(GdipSetLineBlend);
   CheckParameter(blend);
   CheckParameter(positions);
   CheckParameterValid(brush);
   CheckObjectBusy(brush);

   return brush->SetBlend(blend, positions, count);
}

GpStatus
WINGDIPAPI
GdipGetLinePresetBlendCount(
    GpLineGradient *brush,
    INT *count
    )
{
    API_ENTRY(GdipGetLinePresetBlendCount);
    CheckParameter(count);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    *count = brush->GetPresetBlendCount();

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetLinePresetBlend(
    GpLineGradient *brush,
    ARGB *blend,
    REAL *blendPositions,
    INT count
    )
{
    API_ENTRY(GdipGetLinePresetBlend);
    CheckParameter(blend);
    CheckParameter(blendPositions);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    if (count <= 0)
    {
        return InvalidParameter;
    }

    if (count < brush->GetPresetBlendCount())
    {
        return InsufficientBuffer;
    }

    StackBuffer buffer;

    GpColor* gpcolors = (GpColor*) buffer.GetBuffer(count*sizeof(GpColor));

    if(!gpcolors) return OutOfMemory;

    if (gpcolors)
    {
        GpStatus status = brush->GetPresetBlend(gpcolors, blendPositions, count);

        for(INT i = 0; i < count; i++)
        {
            blend[i] = gpcolors[i].GetValue();
        }

        return status;
    }
    else
        return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipSetLinePresetBlend(
    GpLineGradient *brush,
    GDIPCONST ARGB *blend,
    GDIPCONST REAL *blendPositions,
    INT count
    )
{
    API_ENTRY(GdipSetLinePresetBlend);
    CheckParameter(blend);
    CheckParameter(blendPositions);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    CheckParameter(count > 0)

    // blend positions must start at 0.0 and end at 1.0
    if (REALABS(blendPositions[0]) > REAL_EPSILON ||
        REALABS(1.0f - blendPositions[count-1]) > REAL_EPSILON)
    {
        return InvalidParameter;
    }

    StackBuffer buffer;

    GpColor* gpcolors = (GpColor*) buffer.GetBuffer(count*sizeof(GpColor));

    if(!gpcolors) return OutOfMemory;

    if(gpcolors)
    {
        for(INT i = 0; i < count; i++)
        {
            gpcolors[i].SetColor(blend[i]);
        }

        GpStatus status = brush->SetPresetBlend(gpcolors, blendPositions, count);

        return status;
    }
    else
        return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipSetLineSigmaBlend(
    GpLineGradient *brush,
    REAL focus,
    REAL scale
    )
{
    API_ENTRY(GdipSetLineSigmaBlend);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    return brush->SetSigmaBlend(focus, scale);
}

GpStatus
WINGDIPAPI
GdipSetLineLinearBlend(
    GpLineGradient *brush,
    REAL focus,
    REAL scale
    )
{
    API_ENTRY(GdipSetLineLinearBlend);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    return brush->SetLinearBlend(focus, scale);
}

GpStatus
WINGDIPAPI
GdipSetLineWrapMode(
    GpLineGradient *brush,
    GpWrapMode wrapmode
    )
{
    API_ENTRY(GdipSetLineWrapMode);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);
    GpStatus status = Ok;

    if(wrapmode != WrapModeClamp)
        brush->SetWrapMode(wrapmode);
    else
        status = InvalidParameter;

    return status;
}

GpStatus
WINGDIPAPI
GdipGetLineWrapMode(
    GpLineGradient *brush,
    GpWrapMode *wrapmode
    )
{
    API_ENTRY(GdipGetLineWrapMode);
   CheckParameter(wrapmode);
   CheckParameterValid(brush);
   CheckObjectBusy(brush);

   *wrapmode = (GpWrapMode)brush->GetWrapMode();

   return Ok;
}

GpStatus
WINGDIPAPI
GdipSetLineTransform(
    GpLineGradient *brush,
    GDIPCONST GpMatrix *matrix
    )
{
    API_ENTRY(GdipSetLineTransform);
   CheckParameterValid(brush);
   CheckObjectBusy(brush);
   CheckParameterValid(matrix);
   CheckObjectBusy(matrix);

   return brush->SetTransform(*matrix);
}

GpStatus
WINGDIPAPI
GdipGetLineTransform(
    GpLineGradient *brush,
    GpMatrix *matrix
    )
{
    API_ENTRY(GdipGetLineTransform);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);
    CheckParameterValid(matrix);
    CheckObjectBusy(matrix);

    return brush->GetTransform(matrix);
}

GpStatus
WINGDIPAPI
GdipResetLineTransform(
    GpLineGradient* brush)
{
    API_ENTRY(GdipResetLineTransform);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    return brush->ResetTransform();
}

GpStatus
WINGDIPAPI
GdipMultiplyLineTransform(
    GpLineGradient* brush,
    GDIPCONST GpMatrix *matrix,
    GpMatrixOrder order)
{
    API_ENTRY(GdipMultiplyLineTransform);
    if(matrix == NULL)
        return Ok;

    CheckParameterValid(brush);
    CheckObjectBusy(brush);
    CheckParameterValid(matrix);
    CheckObjectBusy(matrix);

    return brush->MultiplyTransform(*matrix, order);
}

GpStatus
WINGDIPAPI
GdipTranslateLineTransform(
    GpLineGradient* brush,
    REAL dx,
    REAL dy,
    GpMatrixOrder order
    )
{
    API_ENTRY(GdipTranslateLineTransform);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);
    CheckParameter(MatrixOrderIsValid(order));

    return brush->TranslateTransform(dx, dy, order);
}

GpStatus
WINGDIPAPI
GdipScaleLineTransform(
    GpLineGradient* brush,
    REAL sx,
    REAL sy,
    GpMatrixOrder order
    )
{
    API_ENTRY(GdipScaleLineTransform);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);
    CheckParameter(MatrixOrderIsValid(order));

    return brush->ScaleTransform(sx, sy, order);
}

GpStatus
WINGDIPAPI
GdipRotateLineTransform(
    GpLineGradient* brush,
    REAL angle,
    GpMatrixOrder order
    )
{
    API_ENTRY(GdipRotateLineTransform);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);
    CheckParameter(MatrixOrderIsValid(order));

    return brush->RotateTransform(angle, order);
}

GpStatus
WINGDIPAPI
GdipCreatePathGradient(
    GDIPCONST GpPointF* points,
    INT count,
    GpWrapMode wrapMode,
    GpPathGradient **brush
    )
{
    API_ENTRY(GdipCreatePathGradient);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(brush);

    *brush = new GpPathGradient(points, count, wrapMode);

    if (CheckValid(*brush))
        return Ok;
    else
        return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipCreatePathGradientI(
    GDIPCONST GpPoint* points,
    INT count,
    GpWrapMode wrapMode,
    GpPathGradient **brush
    )
{
    API_ENTRY(GdipCreatePathGradientI);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(points);

    StackBuffer buffer;

    GpPointF* pointsF = (GpPointF*) buffer.GetBuffer(count*sizeof(GpPointF));

    if(!pointsF) return OutOfMemory;

    for (INT i=0; i<count; i++)
    {
         pointsF[i].X = TOREAL(points[i].X);
         pointsF[i].Y = TOREAL(points[i].Y);
    }

    GpStatus status = GdipCreatePathGradient(pointsF, count, wrapMode, brush);

    return status;
}

GpStatus
WINGDIPAPI
GdipCreatePathGradientFromPath(
    GDIPCONST GpPath* path,
    GpPathGradient **brush
    )
{
    API_ENTRY(GdipCreatePathGradientFromPath);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(brush);

    *brush = new GpPathGradient(path);

    if (CheckValid(*brush))
        return Ok;
    else
        return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipGetPathGradientCenterColor(
    GpPathGradient *brush,
    ARGB* color
    )
{
    API_ENTRY(GdipGetPathGradientCenterColor);
    CheckParameter(color);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    GpColor center;
    brush->GetCenterColor(&center);
    *color = center.GetValue();

    return Ok;
}

GpStatus
WINGDIPAPI
GdipSetPathGradientCenterColor(
    GpPathGradient *brush,
    ARGB color
    )
{
    API_ENTRY(GdipSetPathGradientCenterColor);
    CheckColorParameter(color);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    GpColor center(color);
    brush->SetCenterColor(center);

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetPathGradientSurroundColorsWithCount(
    GpPathGradient *brush,
    ARGB* colors,
    INT* count
    )
{
    API_ENTRY(GdipGetPathGradientSurroundColorsWithCount);
    CheckParameter(colors);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    INT count1 = brush->GetNumberOfPoints();

    if(*count < count1 || count1 <= 0)
        return InvalidParameter;

    count1 = min(*count, count1);

    ARGB lastValue, nextValue;

    INT n = 1;

    for(INT i = 0; i < count1; i++)
    {
        GpColor color;

        brush->GetSurroundColor(&color, i);
        nextValue = color.GetValue();
        colors[i] = nextValue;

        if(i > 0)
        {
            if(nextValue != lastValue)
                n = i + 1;
        }
        lastValue = nextValue;
    }

    *count = n;

    return Ok;
}

GpStatus
WINGDIPAPI
GdipSetPathGradientSurroundColorsWithCount(
    GpPathGradient *brush,
    GDIPCONST ARGB* colors,
    INT* count
    )
{
    API_ENTRY(GdipSetPathGradientSurroundColorsWithCount);
    CheckParameter(colors);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    INT count1 = brush->GetNumberOfPoints();

    INT minCount = min(*count, count1);

    if(*count > count1 || minCount < 1)
    {
        return InvalidParameter;
    }

    GpColor *gpColors = new GpColor[count1];

    if(NULL == gpColors)
    {
        return OutOfMemory;
    }

    INT i;
    for(i = 0; i < minCount; i++)
    {
        gpColors[i].SetColor(colors[i]);
    }

    if(minCount < count1)
    {
        for(i = minCount; i < count1; i++)
        {
            gpColors[i].SetColor(colors[minCount - 1]);
        }
    }

    *count = minCount;
    GpStatus status = brush->SetSurroundColors(gpColors);

    delete[] gpColors;

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetPathGradientPath(
    GpPathGradient *brush,
    GpPath *path
    )
{
    API_ENTRY(GdipGetPathGradientPath);
    return NotImplemented;
}

GpStatus
WINGDIPAPI
GdipSetPathGradientPath(
    GpPathGradient *brush,
    GDIPCONST GpPath *path
    )
{
    API_ENTRY(GdipSetPathGradientPath);
    return NotImplemented;
}

GpStatus
WINGDIPAPI
GdipGetPathGradientCenterPoint(
    GpPathGradient *brush,
    GpPointF* point
    )
{
    API_ENTRY(GdipGetPathGradientCenterPoint);
    CheckParameter(point);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    return brush->GetCenterPoint(point);
}

GpStatus
WINGDIPAPI
GdipGetPathGradientCenterPointI(
    GpPathGradient *brush,
    GpPoint* point
    )
{
    API_ENTRY(GdipGetPathGradientCenterPointI);
    CheckParameter(point);

    GpPointF pointF;

    GpStatus status = GdipGetPathGradientCenterPoint(brush, &pointF);

    if (status == Ok)
    {

         point->X = GpRound(pointF.X);
         point->Y = GpRound(pointF.Y);
    }

    return status;
}

GpStatus
WINGDIPAPI
GdipSetPathGradientCenterPoint(
    GpPathGradient *brush,
    GDIPCONST GpPointF* point
    )
{
    API_ENTRY(GdipSetPathGradientCenterPoint);
    CheckParameter(point);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    return brush->SetCenterPoint(*point);
}

GpStatus
WINGDIPAPI
GdipSetPathGradientCenterPointI(
    GpPathGradient *brush,
    GDIPCONST GpPoint* point
    )
{
    API_ENTRY(GdipSetPathGradientCenterPointI);
    CheckParameter(point);

    GpPointF pointF(TOREAL(point->X), TOREAL(point->Y));

    return GdipSetPathGradientCenterPoint(brush, &pointF);
}

GpStatus
WINGDIPAPI
GdipGetPathGradientPointCount(
    GpPathGradient *brush,
    INT* count)
{
    API_ENTRY(GdipGetPathGradientPointCount);
    CheckParameter(count);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    *count = brush->GetNumberOfPoints();

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetPathGradientSurroundColorCount(
    GpPathGradient *brush,
    INT* count)
{
    API_ENTRY(GdipGetPathGradientSurroundColorCount);
    CheckParameter(count);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    *count = brush->GetNumberOfColors();

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetPathGradientRect(
    GpPathGradient *brush,
    GpRectF *rect
    )
{
    API_ENTRY(GdipGetPathGradientRect);
    CheckParameter(rect);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    brush->GetRect(*rect);

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetPathGradientRectI(
    GpPathGradient *brush,
    GpRect *rect
    )
{
    API_ENTRY(GdipGetPathGradientRectI);
    CheckParameter(rect);

    GpRectF rectF;

    GpStatus status = GdipGetPathGradientRect(brush, &rectF);

    if (status == Ok)
    {

        rect->X = GpRound(rectF.X);
        rect->Y = GpRound(rectF.Y);
        rect->Width = GpRound(rectF.Width);
        rect->Height = GpRound(rectF.Height);
    }

    return status;
}

GpStatus
WINGDIPAPI
GdipSetPathGradientGammaCorrection(
    GpPathGradient *brush,
    BOOL useGammaCorrection
    )
{
    API_ENTRY(GdipSetPathGradientGammaCorrection);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    brush->SetGammaCorrection(useGammaCorrection);
    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetPathGradientGammaCorrection(
    GpPathGradient *brush,
    BOOL *useGammaCorrection)
{
    API_ENTRY(GdipGetPathGradientGammaCorrection);
    CheckParameter(useGammaCorrection);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    *useGammaCorrection = brush->GetGammaCorrection();

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetPathGradientBlendCount(
    GpPathGradient *brush,
    INT *count
    )
{
    API_ENTRY(GdipGetPathGradientBlendCount);
    CheckParameter(count);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    *count = brush->GetBlendCount();

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetPathGradientBlend(
    GpPathGradient *brush,
    REAL *blend,
    REAL *positions,
    INT count
    )
{
    API_ENTRY(GdipGetPathGradientBlend);
    CheckParameter(blend);
    CheckParameter(positions);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    return brush->GetBlend(blend, positions, count);
}

GpStatus
WINGDIPAPI
GdipSetPathGradientBlend(
    GpPathGradient *brush,
    GDIPCONST REAL *blend,
    GDIPCONST REAL *positions,
    INT count
    )
{
    API_ENTRY(GdipSetPathGradientBlend);
    CheckParameter(blend);
    CheckParameter(positions);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    return brush->SetBlend(blend, positions, count);
}

GpStatus
WINGDIPAPI
GdipGetPathGradientPresetBlendCount(
    GpPathGradient *brush,
    INT *count
    )
{
    API_ENTRY(GdipGetPathGradientPresetBlendCount);
   CheckParameter(count);
   CheckParameterValid(brush);
   CheckObjectBusy(brush);

   *count = brush->GetPresetBlendCount();

   return Ok;
}

GpStatus
WINGDIPAPI
GdipGetPathGradientPresetBlend(
    GpPathGradient *brush,
    ARGB *blend,
    REAL *blendPositions,
    INT count
    )
{
    API_ENTRY(GdipGetPathGradientPresetBlend);
    CheckParameter(blend);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    StackBuffer buffer;

    GpColor* gpcolors = (GpColor*) buffer.GetBuffer(count*sizeof(GpColor));

    if(!gpcolors) return OutOfMemory;

    if (gpcolors)
    {
        GpStatus status = brush->GetPresetBlend(gpcolors, blendPositions, count);

        for(INT i = 0; i < count; i++)
        {
            blend[i] = gpcolors[i].GetValue();
        }

        return status;
    }
    else
        return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipSetPathGradientPresetBlend(
    GpPathGradient *brush,
    GDIPCONST ARGB *blend,
    GDIPCONST REAL *blendPositions,
    INT count
    )
{
    API_ENTRY(GdipSetPathGradientPresetBlend);
    CheckParameter(blend);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    CheckParameter(count > 0);

    // blend positions must start at 0.0 and end at 1.0
    if (REALABS(blendPositions[0]) > REAL_EPSILON ||
        REALABS(1.0f - blendPositions[count-1]) > REAL_EPSILON)
    {
        return InvalidParameter;
    }

    StackBuffer buffer;

    GpColor* gpcolors = (GpColor*) buffer.GetBuffer(count*sizeof(GpColor));

    if(!gpcolors) return OutOfMemory;

    if(gpcolors)
    {
        for(INT i = 0; i < count; i++)
        {
            gpcolors[i].SetColor(blend[i]);
        }

        GpStatus status = brush->SetPresetBlend(gpcolors, blendPositions, count);

        return status;
    }
    else
        return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipSetPathGradientSigmaBlend(
    GpPathGradient *brush,
    REAL focus,
    REAL scale
    )
{
    API_ENTRY(GdipSetPathGradientSigmaBlend);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    return brush->SetSigmaBlend(focus, scale);
}

GpStatus
WINGDIPAPI
GdipSetPathGradientLinearBlend(
    GpPathGradient *brush,
    REAL focus,
    REAL scale
    )
{
    API_ENTRY(GdipSetPathGradientLinearBlend);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    return brush->SetLinearBlend(focus, scale);
}

GpStatus
WINGDIPAPI
GdipGetPathGradientWrapMode(
    GpPathGradient *brush,
    GpWrapMode *wrapmode
    )
{
    API_ENTRY(GdipGetPathGradientWrapMode);
    CheckParameter(wrapmode);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    *wrapmode = (GpWrapMode)brush->GetWrapMode();

    return Ok;
}

GpStatus
WINGDIPAPI
GdipSetPathGradientWrapMode(
    GpPathGradient *brush,
    GpWrapMode wrapmode
    )
{
    API_ENTRY(GdipSetPathGradientWrapMode);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    brush->SetWrapMode(wrapmode);

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetPathGradientTransform(
    GpPathGradient *brush,
    GpMatrix *matrix
    )
{
    API_ENTRY(GdipGetPathGradientTransform);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);
    CheckParameterValid(matrix);
    CheckObjectBusy(matrix);

    return brush->GetTransform(matrix);
}

GpStatus
WINGDIPAPI
GdipSetPathGradientTransform(
    GpPathGradient *brush,
    GpMatrix *matrix
    )
{
    API_ENTRY(GdipSetPathGradientTransform);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);
    CheckParameterValid(matrix);
    CheckObjectBusy(matrix);

    return brush->SetTransform(*matrix);
}

GpStatus
WINGDIPAPI
GdipResetPathGradientTransform(
    GpPathGradient* brush)
{
    API_ENTRY(GdipResetPathGradientTransform);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    return brush->ResetTransform();
}

GpStatus
WINGDIPAPI
GdipMultiplyPathGradientTransform(
    GpPathGradient* brush,
    GDIPCONST GpMatrix *matrix,
    GpMatrixOrder order)
{
    API_ENTRY(GdipMultiplyPathGradientTransform);
    if(matrix == NULL)
        return Ok;

    CheckParameterValid(brush);
    CheckObjectBusy(brush);
    CheckParameterValid(matrix);
    CheckObjectBusy(matrix);

    return brush->MultiplyTransform(*matrix, order);
}

GpStatus
WINGDIPAPI
GdipTranslatePathGradientTransform(
    GpPathGradient* brush,
    REAL dx,
    REAL dy,
    GpMatrixOrder order
    )
{
    API_ENTRY(GdipTranslatePathGradientTransform);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);
    CheckParameter(MatrixOrderIsValid(order));

    return brush->TranslateTransform(dx, dy, order);
}

GpStatus
WINGDIPAPI
GdipScalePathGradientTransform(
    GpPathGradient* brush,
    REAL sx,
    REAL sy,
    GpMatrixOrder order
    )
{
    API_ENTRY(GdipScalePathGradientTransform);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);
    CheckParameter(MatrixOrderIsValid(order));

    return brush->ScaleTransform(sx, sy, order);
}

GpStatus
WINGDIPAPI
GdipRotatePathGradientTransform(
    GpPathGradient* brush,
    REAL angle,
    GpMatrixOrder order
    )
{
    API_ENTRY(GdipRotatePathGradientTransform);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);
    CheckParameter(MatrixOrderIsValid(order));

    return brush->RotateTransform(angle, order);
}

GpStatus
WINGDIPAPI
GdipGetPathGradientFocusScales(
    GpPathGradient *brush,
    REAL* xScale,
    REAL* yScale
    )
{
    API_ENTRY(GdipGetPathGradientFocusScales);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);
    CheckParameter(xScale);
    CheckParameter(yScale);

    return brush->GetFocusScales(xScale, yScale);
}

GpStatus
WINGDIPAPI
GdipSetPathGradientFocusScales(
    GpPathGradient *brush,
    REAL xScale,
    REAL yScale
    )
{
    API_ENTRY(GdipSetPathGradientFocusScales);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    return brush->SetFocusScales(xScale, yScale);
}

GpStatus
WINGDIPAPI
GdipCreatePen1(
    ARGB color,
    REAL width,
    GpUnit unit,
    GpPen ** outPen
    )
{
    API_ENTRY(GdipCreatePen1);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckColorParameter(color);
    CheckParameter(outPen);

    // UnitDisplay is NOT valid; its only use is for Page Transforms
    CheckParameter((unit >= UnitWorld) && (unit <= UnitMillimeter) && (unit != UnitDisplay));

    GpPen * pen;

    pen = (GpPen *) InterlockedExchangePointer((PVOID *) &Globals::PenLookAside, NULL);

    if(pen == NULL)
    {
        pen = new GpPen(GpColor(color), width, unit);
    }
    else
    {
        pen->GetObjectLock()->Reset();
        pen->Set(GpColor(color), width, unit);
    }

    if (CheckValid(pen))
    {
        *outPen = pen;
        return Ok;
    }
    else
        return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipCreatePen2(
    GpBrush *brush,
    REAL width,
    GpUnit unit,
    GpPen **pen
    )
{
    API_ENTRY(GdipCreatePen2);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(pen);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    // UnitDisplay is NOT valid; its only use is for Page Transforms
    CheckParameter(
        (unit >= UnitWorld) &&
        (unit <= UnitMillimeter) &&
        (unit != UnitDisplay)
    );

    *pen = new GpPen(brush, width, unit);

    if (CheckValid(*pen))
        return Ok;
    else
        return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipClonePen(
    GpPen *pen,
    GpPen **clonepen
    )
{
    API_ENTRY(GdipClonePen);
    CheckParameter(clonepen);
    CheckParameterValid(pen);
    CheckObjectBusy(pen);

    *clonepen = pen->Clone();

    if (CheckValid(*clonepen))
        return Ok;
    else
        return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipDeletePen(
    GpPen *pen
    )
{
    API_ENTRY(GdipDeletePen);
    // NOTE: Do NOT call CheckParameterValid(), because we need to free
    // the object, even if it's not in a valid state.
    CheckParameter(pen);
    CheckObjectBusyForDelete(pen);

    // !!! [asecchia] only use the pen lookaside for solid pens.
    // it turns out that if we use pens containing TextureBrushes, we require
    // the stream to remain valid for the lifetime of the pen object in the
    // lookaside.

    if(pen->IsSolid())
    {
        pen = (GpPen *) InterlockedExchangePointer((PVOID *) &Globals::PenLookAside, pen);
    }

    if(pen != NULL)
    {
        delete pen;
    }

    return Ok;
}

GpStatus
WINGDIPAPI
GdipSetPenWidth(
    GpPen *pen,
    REAL width
    )
{
    API_ENTRY(GdipSetPenWidth);
   CheckParameterValid(pen);
   CheckObjectBusy(pen);

   pen->SetWidth(width);

   return Ok;
}

GpStatus
WINGDIPAPI
GdipGetPenWidth(
    GpPen *pen,
    REAL *width
    )
{
    API_ENTRY(GdipGetPenWidth);
   CheckParameter(width);
   CheckParameterValid(pen);
   CheckObjectBusy(pen);

   *width = pen->GetWidth();

   return Ok;
}

GpStatus
WINGDIPAPI
GdipSetPenUnit(
    GpPen *pen,
    GpUnit unit
    )
{
    API_ENTRY(GdipSetPenUnit);
    CheckParameterValid(pen);
    CheckObjectBusy(pen);

    // UnitDisplay is NOT valid; its only use is for Page Transforms
    CheckParameter((unit >= UnitWorld) && (unit <= UnitMillimeter) && (unit != UnitDisplay));

    pen->SetUnit(unit);

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetPenUnit(
    GpPen *pen,
    GpUnit* unit
    )
{
    API_ENTRY(GdipGetPenUnit);
   CheckParameter(unit);
   CheckParameterValid(pen);
   CheckObjectBusy(pen);

   *unit = pen->GetUnit();

   return Ok;
}

GpStatus
WINGDIPAPI
GdipSetPenLineCap197819(
    GpPen *pen,
    GpLineCap startCap,
    GpLineCap endCap,
    GpDashCap dashCap
    )
{
    API_ENTRY(GdipSetPenLineCap197819);
    CheckParameterValid(pen);
    CheckObjectBusy(pen);

    pen->SetLineCap(startCap, endCap, dashCap);

    return Ok;
}

GpStatus
WINGDIPAPI
GdipSetPenStartCap(
    GpPen *pen,
    GpLineCap startCap
    )
{
    API_ENTRY(GdipSetPenStartCap);
   CheckParameterValid(pen);
   CheckObjectBusy(pen);

   pen->SetStartCap(startCap);

   return Ok;
}

GpStatus
WINGDIPAPI
GdipSetPenEndCap(
    GpPen *pen,
    GpLineCap endCap
    )
{
    API_ENTRY(GdipSetPenEndCap);
   CheckParameterValid(pen);
   CheckObjectBusy(pen);

   pen->SetEndCap(endCap);

   return Ok;
}

GpStatus
WINGDIPAPI
GdipSetPenDashCap197819(
    GpPen *pen,
    GpDashCap dashCap
    )
{
    API_ENTRY(GdipSetPenDashCap197819);
   CheckParameterValid(pen);
   CheckObjectBusy(pen);

   pen->SetDashCap(dashCap);

   return Ok;
}

GpStatus
WINGDIPAPI
GdipGetPenStartCap(
    GpPen *pen,
    GpLineCap *startCap
    )
{
    API_ENTRY(GdipGetPenStartCap);
   CheckParameter(startCap);
   CheckParameterValid(pen);
   CheckObjectBusy(pen);

   *startCap = pen->GetStartCap();

   return Ok;
}

GpStatus
WINGDIPAPI
GdipGetPenEndCap(
    GpPen *pen,
    GpLineCap *endCap
    )
{
    API_ENTRY(GdipGetPenEndCap);
   CheckParameter(endCap);
   CheckParameterValid(pen);
   CheckObjectBusy(pen);

   *endCap = pen->GetEndCap();

   return Ok;
}

GpStatus
WINGDIPAPI
GdipGetPenDashCap197819(
    GpPen *pen,
    GpDashCap *dashCap
    )
{
   API_ENTRY(GdipGetPenDashCap197819);
   CheckParameter(dashCap);
   CheckParameterValid(pen);
   CheckObjectBusy(pen);

   *dashCap = pen->GetDashCap();

   return Ok;
}

GpStatus
WINGDIPAPI
GdipSetPenLineJoin(
    GpPen *pen,
    GpLineJoin lineJoin
    )
{
    API_ENTRY(GdipSetPenLineJoin);
   CheckParameterValid(pen);
   CheckObjectBusy(pen);

   pen->SetLineJoin(lineJoin);

   return Ok;
}

GpStatus
WINGDIPAPI
GdipGetPenLineJoin(
    GpPen *pen,
    GpLineJoin *lineJoin
    )
{
    API_ENTRY(GdipGetPenLineJoin);
   CheckParameter(lineJoin);
   CheckParameterValid(pen);
   CheckObjectBusy(pen);

   *lineJoin = pen->GetLineJoin();

   return Ok;
}

GpStatus
WINGDIPAPI
GdipSetPenCustomStartCap(
    GpPen *pen,
    GpCustomLineCap* customCap
    )
{
    API_ENTRY(GdipSetPenCustomStartCap);
   CheckParameterValid(customCap);
   CheckObjectBusy(customCap);
   CheckParameterValid(pen);
   CheckObjectBusy(pen);

   return pen->SetCustomStartCap(customCap);
}

GpStatus
WINGDIPAPI
GdipGetPenCustomStartCap(
    GpPen *pen,
    GpCustomLineCap** customCap
    )
{
    API_ENTRY(GdipGetPenCustomStartCap);
   CheckParameter(customCap);
   CheckParameterValid(pen);
   CheckObjectBusy(pen);

   return pen->GetCustomStartCap(customCap);
}

GpStatus
WINGDIPAPI
GdipSetPenCustomEndCap(
    GpPen *pen,
    GpCustomLineCap* customCap
    )
{
    API_ENTRY(GdipSetPenCustomEndCap);
   CheckParameterValid(customCap);
   CheckObjectBusy(customCap);
   CheckParameterValid(pen);
   CheckObjectBusy(pen);

   return pen->SetCustomEndCap(customCap);
}

GpStatus
WINGDIPAPI
GdipGetPenCustomEndCap(
    GpPen *pen,
    GpCustomLineCap** customCap
    )
{
    API_ENTRY(GdipGetPenCustomEndCap);
   CheckParameter(customCap);
   CheckParameterValid(pen);
   CheckObjectBusy(pen);

   return pen->GetCustomEndCap(customCap);
}

GpStatus
WINGDIPAPI
GdipSetPenMiterLimit(
    GpPen *pen,
    REAL miterLimit
    )
{
    API_ENTRY(GdipSetPenMiterLimit);
   CheckParameterValid(pen);
   CheckObjectBusy(pen);

   pen->SetMiterLimit(miterLimit);

   return Ok;
}

GpStatus
WINGDIPAPI
GdipGetPenMiterLimit(
    GpPen *pen,
    REAL *miterLimit
    )
{
    API_ENTRY(GdipGetPenMiterLimit);
   CheckParameter(miterLimit);
   CheckParameterValid(pen);
   CheckObjectBusy(pen);

   *miterLimit = pen->GetMiterLimit();

   return Ok;
}

GpStatus
WINGDIPAPI
GdipSetPenMode(
    GpPen *pen,
    GpPenAlignment penMode
    )
{
    API_ENTRY(GdipSetPenMode);
    CheckParameterValid(pen);
    CheckObjectBusy(pen);

    return pen->SetPenAlignment(penMode);
}

GpStatus
WINGDIPAPI
GdipGetPenMode(
    GpPen *pen,
    GpPenAlignment *penMode
    )
{
    API_ENTRY(GdipGetPenMode);
   CheckParameter(penMode);
   CheckParameterValid(pen);
   CheckObjectBusy(pen);

   *penMode = pen->GetPenAlignment();

   return Ok;
}


GpStatus
WINGDIPAPI
GdipSetPenTransform(
    GpPen *pen,
    GpMatrix *matrix
    )
{
    API_ENTRY(GdipSetPenTransform);
    CheckParameterValid(pen);
    CheckObjectBusy(pen);
    CheckParameterValid(matrix);
    CheckObjectBusy(matrix);
    
    return pen->SetTransform(*matrix);
}

GpStatus
WINGDIPAPI
GdipGetPenTransform(
    GpPen *pen,
    GpMatrix *matrix
    )
{
    API_ENTRY(GdipGetPenTransform);
    CheckParameterValid(pen);
    CheckObjectBusy(pen);
    CheckParameterValid(matrix);
    CheckObjectBusy(matrix);

    return pen->GetTransform(matrix);
}

GpStatus
WINGDIPAPI
GdipResetPenTransform(
    GpPen *pen)
{
    API_ENTRY(GdipResetPenTransform);
    CheckParameterValid(pen);
    CheckObjectBusy(pen);

    return pen->ResetTransform();
}

GpStatus
WINGDIPAPI
GdipMultiplyPenTransform(
    GpPen *pen,
    GDIPCONST GpMatrix *matrix,
    GpMatrixOrder order)
{
    API_ENTRY(GdipMultiplyPenTransform);
    if(matrix == NULL)
        return Ok;

    CheckParameterValid(pen);
    CheckObjectBusy(pen);
    CheckParameterValid(matrix);
    CheckObjectBusy(matrix);

    return pen->MultiplyTransform(*matrix, order);
}

GpStatus
WINGDIPAPI
GdipTranslatePenTransform(
    GpPen* pen,
    REAL dx,
    REAL dy,
    GpMatrixOrder order
    )
{
    API_ENTRY(GdipTranslatePenTransform);
    CheckParameterValid(pen);
    CheckObjectBusy(pen);
    CheckParameter(MatrixOrderIsValid(order));

    return pen->TranslateTransform(dx, dy, order);
}

GpStatus
WINGDIPAPI
GdipScalePenTransform(
    GpPen* pen,
    REAL sx,
    REAL sy,
    GpMatrixOrder order
    )
{
    API_ENTRY(GdipScalePenTransform);
    CheckParameterValid(pen);
    CheckObjectBusy(pen);
    CheckParameter(MatrixOrderIsValid(order));

    return pen->ScaleTransform(sx, sy, order);
}

GpStatus
WINGDIPAPI
GdipRotatePenTransform(
    GpPen* pen,
    REAL angle,
    GpMatrixOrder order
    )
{
    API_ENTRY(GdipRotatePenTransform);
    CheckParameterValid(pen);
    CheckObjectBusy(pen);
    CheckParameter(MatrixOrderIsValid(order));

    return pen->RotateTransform(angle, order);
}

GpStatus
WINGDIPAPI
GdipSetPenColor(
    GpPen *pen,
    ARGB argb
    )
{
    API_ENTRY(GdipSetPenColor);
    CheckColorParameter(argb);
    CheckParameterValid(pen);
    CheckObjectBusy(pen);

    GpColor color(argb);

    return pen->SetColor(&color);
}

GpStatus
WINGDIPAPI
GdipGetPenColor(
    GpPen *pen,
    ARGB *argb
    )
{
    API_ENTRY(GdipGetPenColor);
   CheckParameter(argb);
   CheckParameterValid(pen);
   CheckObjectBusy(pen);

   ARGB temp;
   Status status = pen->GetColor(&temp);
   *argb = temp;

   return status;
}

GpStatus
WINGDIPAPI
GdipSetPenBrushFill(
    GpPen *pen,
    GpBrush *brush
    )
{
    API_ENTRY(GdipSetPenBrushFill);
   CheckParameterValid(pen);
   CheckObjectBusy(pen);
   CheckParameterValid(brush);
   CheckObjectBusy(brush);

   return pen->SetBrush(brush);
}

GpStatus
WINGDIPAPI
GdipGetPenBrushFill(
    GpPen *pen,
    GpBrush **brush
    )
{
    API_ENTRY(GdipGetPenBrushFill);
   CheckParameterValid(pen);
   CheckObjectBusy(pen);
   CheckParameter(brush);

   *brush = pen->GetClonedBrush();

   return Ok;
}

GpStatus
WINGDIPAPI
GdipGetPenFillType(
    GpPen *pen,
    GpPenType* type
    )
{
    API_ENTRY(GdipGetPenFillType);
   CheckParameterValid(pen);
   CheckObjectBusy(pen);
   CheckParameter(type);

   *type = pen->GetPenType();

   return Ok;
}

GpStatus
WINGDIPAPI
GdipGetPenDashStyle(
    GpPen *pen,
    GpDashStyle *dashstyle
    )
{
    API_ENTRY(GdipGetPenDashStyle);
   CheckParameter(dashstyle);
   CheckParameterValid(pen);
   CheckObjectBusy(pen);

   *dashstyle = (GpDashStyle)pen->GetDashStyle();

   return Ok;
}

GpStatus
WINGDIPAPI
GdipSetPenDashStyle(
    GpPen *pen,
    GpDashStyle dashstyle
    )
{
    API_ENTRY(GdipSetPenDashStyle);
   CheckParameterValid(pen);
   CheckObjectBusy(pen);

   pen->SetDashStyle(dashstyle);

   return Ok;
}

GpStatus
WINGDIPAPI
GdipGetPenDashOffset(
    GpPen *pen,
    REAL *offset
    )
{
    API_ENTRY(GdipGetPenDashOffset);
    CheckParameter(offset);
    CheckParameterValid(pen);
    CheckObjectBusy(pen);

    *offset = pen->GetDashOffset();

    return Ok;
}

GpStatus
WINGDIPAPI
GdipSetPenDashOffset(
    GpPen *pen,
    REAL offset
    )
{
    API_ENTRY(GdipSetPenDashOffset);
    CheckParameterValid(pen);
    CheckObjectBusy(pen);

    pen->SetDashOffset(offset);

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetPenDashCount(
    GpPen *pen,
    INT *count
    )
{
    API_ENTRY(GdipGetPenDashCount);
    CheckParameter(count);
    CheckParameterValid(pen);
    CheckObjectBusy(pen);

    *count = pen->GetDashCount();

    return Ok;
}

GpStatus
WINGDIPAPI
GdipSetPenDashArray(
    GpPen *pen,
    GDIPCONST REAL *dash,
    INT count
    )
{
    API_ENTRY(GdipSetPenDashArray);
    CheckParameter(dash);
    CheckParameterValid(pen);
    CheckObjectBusy(pen);

    return pen->SetDashArray(dash, count);
}

GpStatus
WINGDIPAPI
GdipGetPenDashArray(
    GpPen *pen,
    REAL *dash,
    INT count
    )
{
    API_ENTRY(GdipGetPenDashArray);
    CheckParameter(dash);
    CheckParameterValid(pen);
    CheckObjectBusy(pen);

    return pen->GetDashArray(dash, count);
}


GpStatus
WINGDIPAPI
GdipGetPenCompoundCount(
    GpPen *pen,
    INT *count
    )
{
    API_ENTRY(GdipGetPenCompoundCount);
    CheckParameter(count);
    CheckParameterValid(pen);
    CheckObjectBusy(pen);

    *count = pen->GetCompoundCount();

    return Ok;
}

GpStatus
WINGDIPAPI
GdipSetPenCompoundArray(
    GpPen *pen,
    GDIPCONST REAL *compoundArray,
    INT count
    )
{
    API_ENTRY(GdipSetPenCompoundArray);
    CheckParameter(compoundArray);
    CheckParameterValid(pen);
    CheckObjectBusy(pen);

    return pen->SetCompoundArray(compoundArray, count);
}

GpStatus
WINGDIPAPI
GdipGetPenCompoundArray(
    GpPen *pen,
    REAL *compoundArray,
    INT count
    )
{
    API_ENTRY(GdipGetPenCompoundArray);
    CheckParameter(compoundArray);
    CheckParameterValid(pen);
    CheckObjectBusy(pen);

    return pen->GetCompoundArray(compoundArray, count);
}

GpStatus
WINGDIPAPI
GdipCreateCustomLineCap(
    GpPath* fillPath,
    GpPath* strokePath,
    GpLineCap baseCap,
    REAL baseInset,
    GpCustomLineCap **customCap
    )
{
    API_ENTRY(GdipCreateCustomLineCap);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    // fillPath or strokePath can be NULL.

    CheckParameter(customCap);
    CheckParameter((fillPath != NULL) || (strokePath != NULL));
    CheckOptionalParameterValid(fillPath);
    CheckOptionalObjectBusy(fillPath);
    CheckOptionalParameterValid(strokePath);
    CheckOptionalObjectBusy(strokePath);

    *customCap = new GpCustomLineCap(fillPath, strokePath, baseCap, baseInset);

    if (*customCap)
    {
        if((*customCap)->IsValid())
        {
            return Ok;
        }
        
        // This failed to create correctly. Retrieve the failure code.
        
        GpStatus status = (*customCap)->GetCreationStatus();
        delete *customCap;
        *customCap = NULL;
        return status;
    }
    else
    {
        return OutOfMemory;
    }
}

GpStatus
WINGDIPAPI
GdipCloneCustomLineCap(
    GpCustomLineCap *customCap,
    GpCustomLineCap **newCustomCap
    )
{
    API_ENTRY(GdipCloneCustomLineCap);
   CheckParameterValid(customCap);
   CheckObjectBusy(customCap);

   *newCustomCap = customCap->Clone();

    if (*newCustomCap)
    {
        if((*newCustomCap)->IsValid())
        {
            return Ok;
        }
        
        // This failed to create correctly. Retrieve the failure code.
        
        GpStatus status = (*newCustomCap)->GetCreationStatus();
        delete *newCustomCap;
        *newCustomCap = NULL;
        return status;
    }
    else
    {
        return OutOfMemory;
    }
}


GpStatus
WINGDIPAPI
GdipGetCustomLineCapType(
    GpCustomLineCap* customCap,
    CustomLineCapType* type
    )
{
    API_ENTRY(GdipGetCustomLineCapType);
   CheckParameter(type);
   CheckParameterValid(customCap);
   CheckObjectBusy(customCap);

   *type = customCap->GetType();

   return Ok;
}

GpStatus
WINGDIPAPI
GdipDeleteCustomLineCap(
    GpCustomLineCap* customCap
    )
{
    API_ENTRY(GdipDeleteCustomLineCap);
    // NOTE: Do NOT call CheckParameterValid(), because we need to free
    // the object, even if it's not in a valid state.
    CheckParameter(customCap);
    CheckObjectBusyForDelete(customCap);

    delete customCap;

    return Ok;
}

GpStatus
WINGDIPAPI
GdipSetCustomLineCapStrokeCaps(
    GpCustomLineCap* customCap,
    GpLineCap startCap,
    GpLineCap endCap)
{
    API_ENTRY(GdipSetCustomLineCapStrokeCaps);
    CheckParameterValid(customCap);
    CheckObjectBusy(customCap);

    return customCap->SetStrokeCaps(startCap, endCap);
}

GpStatus
WINGDIPAPI
GdipGetCustomLineCapStrokeCaps(
    GpCustomLineCap* customCap,
    GpLineCap* startCap,
    GpLineCap* endCap
    )
{
    API_ENTRY(GdipGetCustomLineCapStrokeCaps);
    CheckParameterValid(customCap);
    CheckObjectBusy(customCap);
    CheckParameter(startCap);
    CheckParameter(endCap);

    return customCap->GetStrokeCaps(startCap, endCap);
}

GpStatus
WINGDIPAPI
GdipSetCustomLineCapStrokeJoin(
    GpCustomLineCap* customCap,
    GpLineJoin lineJoin
    )
{
    API_ENTRY(GdipSetCustomLineCapStrokeJoin);
    CheckParameterValid(customCap);
    CheckObjectBusy(customCap);

    return customCap->SetStrokeJoin(lineJoin);
}

GpStatus
WINGDIPAPI
GdipGetCustomLineCapStrokeJoin(
    GpCustomLineCap* customCap,
    GpLineJoin* lineJoin
    )
{
    API_ENTRY(GdipGetCustomLineCapStrokeJoin);
    CheckParameterValid(customCap);
    CheckObjectBusy(customCap);
    CheckParameter(lineJoin);

    return customCap->GetStrokeJoin(lineJoin);
}

GpStatus
WINGDIPAPI
GdipSetCustomLineCapBaseCap(
    GpCustomLineCap* customCap,
    GpLineCap baseCap
    )
{
    API_ENTRY(GdipSetCustomLineCapBaseCap);
    CheckParameterValid(customCap);
    CheckObjectBusy(customCap);

    return customCap->SetBaseCap(baseCap);
}

GpStatus
WINGDIPAPI
GdipGetCustomLineCapBaseCap(
    GpCustomLineCap* customCap,
    GpLineCap* baseCap
    )
{
    API_ENTRY(GdipGetCustomLineCapBaseCap);
    CheckParameterValid(customCap);
    CheckObjectBusy(customCap);
    CheckParameter(baseCap);

    return customCap->GetBaseCap(baseCap);
}

GpStatus
WINGDIPAPI
GdipSetCustomLineCapBaseInset(
    GpCustomLineCap* customCap,
    REAL inset
    )
{
    API_ENTRY(GdipSetCustomLineCapBaseInset);
    CheckParameterValid(customCap);
    CheckObjectBusy(customCap);

    return customCap->SetBaseInset(inset);
}

GpStatus
WINGDIPAPI
GdipGetCustomLineCapBaseInset(
    GpCustomLineCap* customCap,
    REAL* inset
    )
{
    API_ENTRY(GdipGetCustomLineCapBaseInset);
    CheckParameterValid(customCap);
    CheckObjectBusy(customCap);
    CheckParameter(inset);

    return customCap->GetBaseInset(inset);
}

GpStatus
WINGDIPAPI
GdipSetCustomLineCapWidthScale(
    GpCustomLineCap* customCap,
    REAL widthScale
    )
{
    API_ENTRY(GdipSetCustomLineCapWidthScale);
    CheckParameterValid(customCap);
    CheckObjectBusy(customCap);

    return customCap->SetWidthScale(widthScale);
}

GpStatus WINGDIPAPI
GdipGetCustomLineCapWidthScale(
    GpCustomLineCap* customCap,
    REAL* widthScale
    )
{
    API_ENTRY(GdipGetCustomLineCapWidthScale);
    CheckParameterValid(customCap);
    CheckObjectBusy(customCap);
    CheckParameter(widthScale);

    return customCap->GetWidthScale(widthScale);
}

GpStatus
WINGDIPAPI
GdipCreateAdjustableArrowCap(
    REAL height,
    REAL width,
    BOOL isFilled,
    GpAdjustableArrowCap **cap)
{
    API_ENTRY(GdipCreateAdjustableArrowCap);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(cap);

    *cap = new GpAdjustableArrowCap(height, width, isFilled);

    if(*cap)
        return Ok;
    else
        return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipSetAdjustableArrowCapHeight(
    GpAdjustableArrowCap* cap,
    REAL height
    )
{
    API_ENTRY(GdipSetAdjustableArrowCapHeight);
    CheckParameterValid(cap);
    CheckObjectBusy(cap);

    return cap->SetHeight(height);
}

GpStatus
WINGDIPAPI
GdipGetAdjustableArrowCapHeight(
    GpAdjustableArrowCap* cap,
    REAL* height
    )
{
    API_ENTRY(GdipGetAdjustableArrowCapHeight);
    CheckParameterValid(cap);
    CheckObjectBusy(cap);
    CheckParameter(height);

    *height = cap->GetHeight();

    return Ok;
}

GpStatus
WINGDIPAPI
GdipSetAdjustableArrowCapWidth(
    GpAdjustableArrowCap* cap,
    REAL width
    )
{
    API_ENTRY(GdipSetAdjustableArrowCapWidth);
    CheckParameterValid(cap);
    CheckObjectBusy(cap);

    return cap->SetWidth(width);
}

GpStatus
WINGDIPAPI
GdipGetAdjustableArrowCapWidth(
    GpAdjustableArrowCap* cap,
    REAL* width
    )
{
    API_ENTRY(GdipGetAdjustableArrowCapWidth);
    CheckParameterValid(cap);
    CheckObjectBusy(cap);
    CheckParameter(width);

    *width = cap->GetWidth();

    return Ok;
}

GpStatus
WINGDIPAPI
GdipSetAdjustableArrowCapMiddleInset(
    GpAdjustableArrowCap* cap,
    REAL middleInset
    )
{
    API_ENTRY(GdipSetAdjustableArrowCapMiddleInset);
    CheckParameterValid(cap);
    CheckObjectBusy(cap);

    return cap->SetMiddleInset(middleInset);
}

GpStatus
WINGDIPAPI
GdipGetAdjustableArrowCapMiddleInset(
    GpAdjustableArrowCap* cap,
    REAL* middleInset
    )
{
    API_ENTRY(GdipGetAdjustableArrowCapMiddleInset);
    CheckParameterValid(cap);
    CheckObjectBusy(cap);
    CheckParameter(middleInset);

    *middleInset = cap->GetMiddleInset();

    return Ok;
}

GpStatus
WINGDIPAPI
GdipSetAdjustableArrowCapFillState(
    GpAdjustableArrowCap* cap,
    BOOL fillState
    )
{
    API_ENTRY(GdipSetAdjustableArrowCapFillState);
    CheckParameterValid(cap);
    CheckObjectBusy(cap);

    return cap->SetFillState(fillState);
}

GpStatus
WINGDIPAPI
GdipGetAdjustableArrowCapFillState(
    GpAdjustableArrowCap* cap,
    BOOL* fillState
    )
{
    API_ENTRY(GdipGetAdjustableArrowCapFillState);
    CheckParameterValid(cap);
    CheckObjectBusy(cap);
    CheckParameter(fillState);

    *fillState = cap->IsFilled();

    return Ok;
}

GpStatus
WINGDIPAPI
GdipLoadImageFromStream(
    IStream* stream,
    GpImage **image
    )
{
    API_ENTRY(GdipLoadImageFromStream);
    CheckParameter(image && stream);


    *image = GpImage::LoadImage(stream);

    // !!! Can't use CheckValid() since destructor is protected.

    if (*image)
    {
        if ((*image)->IsValid())
        {
            (*image)->SetICMConvert(FALSE);
            return Ok;
        }
        (*image)->Dispose();
        *image = NULL;
        return InvalidParameter;
    }
    return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipLoadImageFromFile(
    GDIPCONST WCHAR* filename,
    GpImage** image
    )
{
    API_ENTRY(GdipLoadImageFromFile);
    CheckParameter(image && filename);


    *image = GpImage::LoadImage(filename);

    if (*image)
    {
        if ((*image)->IsValid())
        {
            (*image)->SetICMConvert(FALSE);
            return Ok;
        }
        (*image)->Dispose();
        *image = NULL;
        return InvalidParameter;
    }
    return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipLoadImageFromStreamICM(
    IStream* stream,
    GpImage **image
    )
{
    API_ENTRY(GdipLoadImageFromStreamICM);
    CheckParameter(image && stream);


    *image = GpImage::LoadImage(stream);

    // !!! Can't use CheckValid() since destructor is protected.

    if (*image)
    {
        if ((*image)->IsValid())
        {
            (*image)->SetICMConvert(TRUE);
            return Ok;
        }
        (*image)->Dispose();
        *image = NULL;
        return InvalidParameter;
    }
    return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipLoadImageFromFileICM(
    GDIPCONST WCHAR* filename,
    GpImage** image
    )
{
    API_ENTRY(GdipLoadImageFromFileICM);
    CheckParameter(image && filename);


    *image = GpImage::LoadImage(filename);

    if (*image)
    {
        if ((*image)->IsValid())
        {
            (*image)->SetICMConvert(TRUE);
            return Ok;
        }
        (*image)->Dispose();
        *image = NULL;
        return InvalidParameter;
    }
    return OutOfMemory;
}


GpStatus
WINGDIPAPI
GdipGetEncoderParameterListSize(
    GpImage         *image,
    GDIPCONST CLSID *clsidEncoder,
    UINT            *size
    )
{
    API_ENTRY(GdipGetEncoderParameterListSize);
    CheckParameter(image && clsidEncoder);
    CheckObjectBusy(image);

    return image->GetEncoderParameterListSize(const_cast<CLSID*>(clsidEncoder), size);
}

GpStatus
WINGDIPAPI
GdipGetEncoderParameterList(
    GpImage           *image,
    GDIPCONST CLSID   *clsidEncoder,
    UINT               size,
    EncoderParameters *buffer
    )
{
    API_ENTRY(GdipGetEncoderParameterList);
    CheckParameter(image && clsidEncoder);
    CheckObjectBusy(image);

    return image->GetEncoderParameterList(const_cast<CLSID*>(clsidEncoder), size, buffer);
}

GpStatus
WINGDIPAPI
GdipSaveImageToStream(
    GpImage *image,
    IStream* stream,
    GDIPCONST CLSID* clsidEncoder,
    GDIPCONST EncoderParameters* encoderParams
    )
{
    API_ENTRY(GdipSaveImageToStream);
    CheckParameter(image && stream && clsidEncoder);
    CheckObjectBusy(image);

    return image->SaveToStream(stream,
                               const_cast<CLSID*>(clsidEncoder),
                               const_cast<EncoderParameters*>(encoderParams));
}

GpStatus
WINGDIPAPI
GdipSaveImageToFile(
    GpImage *image,
    GDIPCONST WCHAR* filename,
    GDIPCONST CLSID* clsidEncoder,
    GDIPCONST EncoderParameters* encoderParams
    )
{
    API_ENTRY(GdipSaveImageToFile);
    CheckParameter(image && filename && clsidEncoder);
    CheckObjectBusy(image);

    return image->SaveToFile(filename,
                             const_cast<CLSID*>(clsidEncoder),
                             const_cast<EncoderParameters*>(encoderParams));
}

GpStatus
WINGDIPAPI
GdipSaveAdd(
    GpImage *image,
    GDIPCONST EncoderParameters* encoderParams
    )
{
    API_ENTRY(GdipSaveAdd);
    CheckParameter(image && encoderParams);
    CheckObjectBusy(image);

    return image->SaveAdd(const_cast<EncoderParameters*>(encoderParams));
}

GpStatus
WINGDIPAPI
GdipSaveAddImage(
    GpImage*            image,
    GpImage*            newImage,
    GDIPCONST EncoderParameters*  encoderParams
    )
{
    API_ENTRY(GdipSaveAddImage);
    CheckParameter(image && newImage && encoderParams);
    CheckObjectBusy(image);

    return image->SaveAdd(newImage, const_cast<EncoderParameters*>(encoderParams));
}

GpStatus
WINGDIPAPI
GdipImageGetFrameDimensionsCount(
    GpImage*    image,
    UINT*       count
    )
{
    API_ENTRY(GdipImageGetFrameDimensionsCount);
    CheckParameter(image);
    CheckObjectBusy(image);
    return image->GetFrameDimensionsCount(count);
}

GpStatus
WINGDIPAPI
GdipImageGetFrameDimensionsList(
    GpImage*    image,
    GUID*       dimensionIDs,
    UINT        count
    )
{
    API_ENTRY(GdipImageGetFrameDimensionsList);
    CheckParameter(image);
    CheckObjectBusy(image);
    return image->GetFrameDimensionsList(dimensionIDs, count);
}

GpStatus
WINGDIPAPI
GdipImageGetFrameCount(
    GpImage*    image,
    GDIPCONST GUID* dimensionID,
    UINT*       count
    )
{
    API_ENTRY(GdipImageGetFrameCount);
    CheckParameter(image);
    CheckObjectBusy(image);
    return image->GetFrameCount(dimensionID, count);
}

GpStatus
WINGDIPAPI
GdipImageSelectActiveFrame(
    GpImage*    image,
    GDIPCONST GUID* dimensionID,
    UINT        frameIndex
    )
{
    API_ENTRY(GdipImageSelectActiveFrame);
    CheckParameter(image);
    CheckObjectBusy(image);
    return image->SelectActiveFrame(dimensionID, frameIndex);
}

GpStatus
WINGDIPAPI
GdipImageRotateFlip(
    GpImage*        image,
    RotateFlipType  rfType
    )
{
    API_ENTRY(GdipImageRotateFlip);
    CheckParameter(image);
    CheckObjectBusy(image);
    return image->RotateFlip(rfType);
}

GpStatus
WINGDIPAPI
GdipGetPropertyCount(
    GpImage*    image,
    UINT* numOfProperty
    )
{
    API_ENTRY(GdipGetPropertyCount);
    CheckParameter(image);
    CheckObjectBusy(image);
    return image->GetPropertyCount(numOfProperty);
}

GpStatus
WINGDIPAPI
GdipGetPropertyIdList(
    GpImage*    image,
    UINT        numOfProperty,
    PROPID*     list
    )
{
    API_ENTRY(GdipGetPropertyIdList);
    CheckParameter(image);
    CheckObjectBusy(image);
    return image->GetPropertyIdList(numOfProperty, list);
}

GpStatus
WINGDIPAPI
GdipGetPropertyItemSize(
    GpImage*    image,
    PROPID      propId,
    UINT*       size
    )
{
    API_ENTRY(GdipGetPropertyItemSize);
    CheckParameter(image);
    CheckObjectBusy(image);
    return image->GetPropertyItemSize(propId, size);
}

GpStatus
WINGDIPAPI
GdipGetPropertyItem(
    GpImage*        image,
    PROPID          propId,
    UINT            propSize,
    PropertyItem*   buffer
    )
{
    API_ENTRY(GdipGetPropertyItem);
    CheckParameter(image);
    CheckObjectBusy(image);
    return image->GetPropertyItem(propId, propSize, buffer);
}

GpStatus
WINGDIPAPI
GdipGetPropertySize(
    GpImage*    image,
    UINT*       totalBufferSize,
    UINT*       numProperties
    )
{
    API_ENTRY(GdipGetPropertySize);
    CheckParameter(image);
    CheckObjectBusy(image);
    return image->GetPropertySize(totalBufferSize, numProperties);
}

GpStatus
WINGDIPAPI
GdipGetAllPropertyItems(
    GpImage*    image,
    UINT        totalBufferSize,
    UINT        numProperties,
    PropertyItem* allItems
    )
{
    API_ENTRY(GdipGetAllPropertyItems);
    CheckParameter(image);
    CheckObjectBusy(image);
    return image->GetAllPropertyItems(totalBufferSize, numProperties, allItems);
}

GpStatus
WINGDIPAPI
GdipRemovePropertyItem(
    GpImage*    image,
    PROPID      propId
    )
{
    API_ENTRY(GdipRemovePropertyItem);
    CheckParameter(image);
    CheckObjectBusy(image);
    return image->RemovePropertyItem(propId);
}

GpStatus
WINGDIPAPI
GdipSetPropertyItem(
    GpImage*                   image,
    GDIPCONST PropertyItem*    item
    )
{
    API_ENTRY(GdipSetPropertyItem);
    CheckParameter(image && item);
    CheckObjectBusy(image);
    return image->SetPropertyItem(const_cast<PropertyItem*>(item));
}

GpStatus
WINGDIPAPI
GdipCloneImage(
    GpImage* image,
    GpImage** cloneimage
    )
{
    API_ENTRY(GdipCloneImage);
   CheckParameter(cloneimage);
   CheckParameterValid(image);
   CheckObjectBusy(image);

   *cloneimage = image->Clone();

   if (*cloneimage)
       return Ok;
   else
       return OutOfMemory;

   // !!!: There is no destructor to invoke.  Leave it to Clone to
   //       verify correct creation.
}

GpStatus
WINGDIPAPI
GdipDisposeImage(
    GpImage *image
    )
{
    API_ENTRY(GdipDisposeImage);
    // NOTE: Do NOT call CheckParameterValid(), because we need to free
    // the object, even if it's not in a valid state.
    CheckParameter(image);

    if (image->GetImageType() == ImageTypeMetafile)
    {
        // If the user still has the graphics associated with the metafile,
        // then we have to lock it, to prevent them from using it while
        // we're busy deleting the metafile object.  The metafile dispose
        // method will set the graphics to invalid, so the only thing
        // they can do with the graphics is to delete it after this call.

        GpMetafile *    metafile = (GpMetafile *)image;
        GpGraphics *    g = metafile->PrivateAPIForGettingMetafileGraphicsContext();
        CheckOptionalObjectBusy(g);
        CheckObjectBusyForDelete(metafile);
        metafile->Dispose();
        return Ok;
    }

    CheckObjectBusyForDelete(image);

    image->Dispose();

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetImageGraphicsContext(
    GpImage *image,
    GpGraphics **graphics
    )
{
    API_ENTRY(GdipGetImageGraphicsContext);
   CheckParameter(graphics);
   CheckParameterValid(image);
   CheckObjectBusy(image);

   *graphics = image->GetGraphicsContext();

   if (CheckValid(*graphics))
       return Ok;
   else
       return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipGetImageBounds(
    GpImage *image,
    GpRectF *srcRect,
    GpPageUnit *srcUnit
    )
{
    API_ENTRY(GdipGetImageBounds);
    CheckParameter(srcRect && srcUnit);
    CheckParameterValid(image);
    CheckObjectBusy(image);

    return image->GetBounds(srcRect, srcUnit);
}


GpStatus
WINGDIPAPI
GdipGetImageDimension(
    GpImage *image,
    REAL *width,
    REAL *height
    )
{
    API_ENTRY(GdipGetImageDimension);
    CheckParameter(width && height);
    CheckParameterValid(image);
    CheckObjectBusy(image);

    return image->GetPhysicalDimension(width, height);
}

GpStatus
WINGDIPAPI
GdipGetImageWidth(
    GpImage *image,
    UINT *width
    )
{
    API_ENTRY(GdipGetImageWidth);
    CheckParameter(width);
    CheckParameterValid(image);
    CheckObjectBusy(image);

    ImageInfo imageinfo;

    GpStatus status = image->GetImageInfo(&imageinfo);

    if (status == Ok)
    {
        *width = imageinfo.Width;
    }

    return status;
}

GpStatus
WINGDIPAPI
GdipGetImageHeight(
    GpImage *image,
    UINT *height
    )
{
    API_ENTRY(GdipGetImageHeight);
    CheckParameter(height);
    CheckParameterValid(image);
    CheckObjectBusy(image);

    ImageInfo imageinfo;

    GpStatus status = image->GetImageInfo(&imageinfo);

    if (status == Ok)
    {
        *height = imageinfo.Height;
    }

    return status;
}

GpStatus
WINGDIPAPI
GdipGetImageHorizontalResolution(
    GpImage *image,
    REAL *resolution
    )
{
    API_ENTRY(GdipGetImageHorizontalResolution);
    CheckParameter(resolution);
    CheckParameterValid(image);
    CheckObjectBusy(image);

    ImageInfo imageinfo;

    GpStatus status = image->GetImageInfo(&imageinfo);

    if (status == Ok)
    {
        *resolution = (REAL) imageinfo.Xdpi;
    }

    return status;
}

GpStatus
WINGDIPAPI
    GdipGetImageVerticalResolution(
    GpImage *image,
    REAL *resolution
    )
{
    API_ENTRY(GdipGetImageVerticalResolution);
    CheckParameter(resolution);
    CheckParameterValid(image);
    CheckObjectBusy(image);

    ImageInfo imageinfo;

    GpStatus status = image->GetImageInfo(&imageinfo);

    if (status == Ok)
    {
        *resolution = (REAL) imageinfo.Ydpi;
    }

    return status;
}

GpStatus
WINGDIPAPI
    GdipGetImageFlags(
    GpImage *image,
    UINT *flags
    )
{
    API_ENTRY(GdipGetImageFlags);
    CheckParameter(flags);
    CheckParameterValid(image);
    CheckObjectBusy(image);

    ImageInfo imageinfo;

    GpStatus status = image->GetImageInfo(&imageinfo);

    if (status == Ok)
    {
        *flags = imageinfo.Flags;
    }

    return status;
}

GpStatus
WINGDIPAPI
    GdipGetImageRawFormat(
    GpImage *image,
    GUID *format
    )
{
    API_ENTRY(GdipGetImageRawFormat);
    CheckParameter(format);
    CheckParameterValid(image);
    CheckObjectBusy(image);

    ImageInfo imageinfo;

    GpStatus status = image->GetImageInfo(&imageinfo);

    if (status == Ok)
    {
        *format = imageinfo.RawDataFormat;
    }

    return status;
}

GpStatus
WINGDIPAPI
    GdipGetImagePixelFormat(
    GpImage *image,
    PixelFormat *format
    )
{
    API_ENTRY(GdipGetImagePixelFormat);
    CheckParameter(format);
    CheckParameterValid(image);
    CheckObjectBusy(image);

    ImageInfo imageinfo;

    GpStatus status = image->GetImageInfo(&imageinfo);

    if (status == Ok)
    {
        //!!!need conversion to PixelFormat
        *format = (PixelFormat) MaskPixelFormat(imageinfo.PixelFormat);
    }

    return status;
}

GpStatus WINGDIPAPI
GdipGetImagePalette(GpImage *image, ColorPalette *palette, INT size)
{
    API_ENTRY(GdipGetImagePalette);
    CheckParameter(palette);
    CheckParameterValid(image);
    CheckObjectBusy(image);

    return image->GetPalette(palette, size);
}

GpStatus WINGDIPAPI
GdipSetImagePalette(GpImage *image, GDIPCONST ColorPalette *palette)
{
    API_ENTRY(GdipSetImagePalette);
    CheckParameter(palette);
    CheckParameterValid(image);
    CheckObjectBusy(image);

    return image->SetPalette(const_cast<ColorPalette*>(palette));
}

GpStatus WINGDIPAPI
GdipGetImagePaletteSize(GpImage *image, INT *size)
{
    API_ENTRY(GdipGetImagePaletteSize);

  CheckParameter(size);
  CheckParameterValid(image);
  CheckObjectBusy(image);

  *size = image->GetPaletteSize();

  // Note: image->GetPaletteSize() will return zero if and only if there is
  // something wrong in the whole pipeline, like a bad image, out of memory etc.

  if (*size == 0)
  {
      return GenericError;
  }
  else
  {
    return Ok;
  }
}




GpStatus
WINGDIPAPI
GdipGetImageType(
    GpImage* image,
    ImageType* type
    )
{
    API_ENTRY(GdipGetImageType);
    CheckParameter(type);
    CheckParameterValid(image);
    CheckObjectBusy(image);

    *type = (ImageType)image->GetImageType();

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetImageThumbnail(
    GpImage* image,
    UINT thumbWidth,
    UINT thumbHeight,
    GpImage** thumbImage,
    GetThumbnailImageAbort callback,
    VOID * callbackData
    )
{
    API_ENTRY(GdipGetImageThumbnail);
    CheckParameter(thumbImage);
    CheckParameterValid(image);
    CheckObjectBusy(image);

    *thumbImage = image->GetThumbnail(thumbWidth, thumbHeight, callback, callbackData);

    if (*thumbImage)
        return Ok;
    else
        return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipImageForceValidation(GpImage* image)
{
    API_ENTRY(GdipImageForceValidation);
    CheckParameterValid(image);
    CheckObjectBusy(image);

    // Metafiles don't need a force decode

    if (image->GetImageType() == ImageTypeBitmap)
        return (static_cast<GpBitmap*>(image))->ForceValidation();
    else
        return Ok;
}

GpStatus
WINGDIPAPI
GdipCreateBitmapFromStream(
    IStream* stream,
    GpBitmap** bitmap
    )
{
    API_ENTRY(GdipCreateBitmapFromStream);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(bitmap && stream);

    *bitmap = NULL;

    // See if the stream is a metafile


    GpMetafile* metafile = new GpMetafile(stream);

    if (metafile != NULL)
    {
        if (metafile->IsValid())
        {
            // If it is a {EMF, WMF} file, then we get a raster version of it
            // Note: GetBitmap() might return NULL if there is anything wrong
            // with the Metafile

            *bitmap = metafile->GetBitmap();
            metafile->Dispose();
            goto SkipRasterImage;
        }

        metafile->Dispose();
    }

    if ( NULL == *bitmap )
    {
        // it's not a valid metafile -- it must be a bitmap

        *bitmap = new GpBitmap(stream);
    }

SkipRasterImage:
    // !!! Can't use CheckValid() since destructor is protected.

    if (*bitmap)
    {
        if ((*bitmap)->IsValid())
        {
            (*bitmap)->SetICMConvert(FALSE);
            return Ok;
        }
        else
        {
            (*bitmap)->Dispose();
            *bitmap = NULL;
            return InvalidParameter;
        }
    }
    else
    {
        return OutOfMemory;
    }
}

GpStatus
WINGDIPAPI
GdipCreateBitmapFromFile(
    GDIPCONST WCHAR* filename,
    GpBitmap** bitmap
    )
{
    API_ENTRY(GdipCreateBitmapFromFile);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(bitmap && filename);

    *bitmap = NULL;


    // Try a metafile first always
    GpMetafile* metafile;
    metafile = new GpMetafile(filename);

    if (metafile)
    {
        if (metafile->IsValid())
        {
            *bitmap = metafile->GetBitmap();
            metafile->Dispose();
            goto SkipRasterImage;
        }

        metafile->Dispose();
    }

    // If it is not a metafile, then check if this is a raster image

    if ( NULL == *bitmap )
    {
        *bitmap = new GpBitmap(filename);
    }

    // !!! Can't use CheckValid() since destructor is protected.
SkipRasterImage:

    if (*bitmap)
    {
        if ((*bitmap)->IsValid())
        {
            (*bitmap)->SetICMConvert(FALSE);
            return Ok;
        }
        else
        {
            (*bitmap)->Dispose();
            *bitmap = NULL;
            return InvalidParameter;
        }
    }
    else
    {
        return OutOfMemory;
    }
}

GpStatus
WINGDIPAPI
GdipCreateBitmapFromStreamICM(
    IStream* stream,
    GpBitmap** bitmap
    )
{
    API_ENTRY(GdipCreateBitmapFromStreamICM);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(bitmap && stream);

    *bitmap = NULL;

    // See if the stream is a metafile


    GpMetafile* metafile = new GpMetafile(stream);

    if (metafile != NULL)
    {
        if (metafile->IsValid())
        {
            *bitmap = metafile->GetBitmap();
            metafile->Dispose();
            goto SkipRasterImage;
        }

        metafile->Dispose();
    }

    if ( NULL == *bitmap )
    {
        // it's not a valid metafile -- it must be a bitmap
        *bitmap = new GpBitmap(stream);
    }

    // !!! Can't use CheckValid() since destructor is protected.
SkipRasterImage:

    if (*bitmap)
    {
        if ((*bitmap)->IsValid())
        {
            (*bitmap)->SetICMConvert(TRUE);
            return Ok;
        }
        else
        {
            (*bitmap)->Dispose();
            *bitmap = NULL;
            return InvalidParameter;
        }
    }
    else
    {
        return OutOfMemory;
    }
}

GpStatus
WINGDIPAPI
GdipCreateBitmapFromFileICM(
    GDIPCONST WCHAR* filename,
    GpBitmap** bitmap
    )
{
    API_ENTRY(GdipCreateBitmapFromFileICM);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(bitmap && filename);

    // copied from GpImage::LoadImage

    *bitmap = NULL;


    // Try a metafile first always
    GpMetafile* metafile;
    metafile = new GpMetafile(filename);

    if (metafile)
    {
        if (metafile->IsValid())
        {
            *bitmap = metafile->GetBitmap();
            metafile->Dispose();
            goto SkipRasterImage;
        }

        metafile->Dispose();
    }

    // If it is not a metafile, then check if this is a raster image

    if ( NULL == *bitmap )
    {
        *bitmap = new GpBitmap(filename);
    }

    // !!! Can't use CheckValid() since destructor is protected.

SkipRasterImage:
    if (*bitmap)
    {
        if ((*bitmap)->IsValid())
        {
            (*bitmap)->SetICMConvert(TRUE);
            return Ok;
        }
        else
        {
            (*bitmap)->Dispose();
            *bitmap = NULL;
            return InvalidParameter;
        }
    }
    else
    {
        return OutOfMemory;
    }
}


GpStatus
WINGDIPAPI
GdipCreateBitmapFromScan0(
    INT width,
    INT height,
    INT stride,
    PixelFormatID format,
    BYTE* scan0,
    GpBitmap** bitmap
    )
{
    API_ENTRY(GdipCreateBitmapFromScan0);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(bitmap);
    format = MaskPixelFormat(format);


    if (scan0 == NULL)
    {
        *bitmap = new GpBitmap(width, height, format);
    }
    else
    {
        CheckParameter(stride);
        *bitmap = new GpBitmap(width, height, stride, format, scan0);
    }

    // !!! Can't use CheckValid() since destructor is protected.

    if (*bitmap)
    {
        if ((*bitmap)->IsValid())
            return Ok;
        else
        {
            (*bitmap)->Dispose();
            *bitmap = NULL;
            return InvalidParameter;
        }
    }
    else
    {
        return OutOfMemory;
    }
}

GpStatus
WINGDIPAPI
GdipCreateBitmapFromGraphics(
    INT width,
    INT height,
    GpGraphics* graphics,
    GpBitmap** bitmap
    )
{
    API_ENTRY(GdipCreateBitmapFromGraphics);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(bitmap);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);

    //!!!TODO: temp use 32bpp argb

    #ifdef NO_PREMULTIPLIED_ALPHA
    *bitmap = new GpBitmap(width, height, PIXFMT_32BPP_ARGB, graphics);
    #else
    *bitmap = new GpBitmap(width, height, PIXFMT_32BPP_PARGB, graphics);
    #endif

    // !!! Can't use CheckValid() since destructor is protected.

    if (*bitmap)
    {
        if ((*bitmap)->IsValid())
            return Ok;
        else
        {
            (*bitmap)->Dispose();
            *bitmap = NULL;
            return InvalidParameter;
        }
    }
    else
    {
        return OutOfMemory;
    }
}

GpStatus
WINGDIPAPI
GdipCreateBitmapFromDirectDrawSurface(
    IDirectDrawSurface7 * surface,
    GpBitmap** bitmap
    )
{
    API_ENTRY(GdipCreateBitmapFromDirectDrawSurface);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(bitmap);
    CheckParameter(surface);

    *bitmap = new GpBitmap(surface);

    // !!! Can't use CheckValid() since destructor is protected.

    if (*bitmap)
    {
        if ((*bitmap)->IsValid())
            return Ok;
        else
        {
            (*bitmap)->Dispose();
            *bitmap = NULL;

            return InvalidParameter;
        }
    }

    return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipCreateBitmapFromGdiDib(
    GDIPCONST BITMAPINFO* gdiBitmapInfo,
    VOID* gdiBitmapData,
    GpBitmap** bitmap
    )
{
    API_ENTRY(GdipCreateBitmapFromGdiDib);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(bitmap);
    CheckParameter(gdiBitmapInfo);
    CheckParameter(gdiBitmapData);

    *bitmap = new GpBitmap(const_cast<BITMAPINFO*>(gdiBitmapInfo),
                           gdiBitmapData,
                           FALSE);

    // !!! Can't use CheckValid() since destructor is protected.

    if (*bitmap)
    {
        if ((*bitmap)->IsValid())
            return Ok;
        else
        {
            (*bitmap)->Dispose();
            *bitmap = NULL;
            return InvalidParameter;
        }
    }
    else
    {
        return OutOfMemory;
    }
}

GpStatus
WINGDIPAPI
GdipCreateBitmapFromHBITMAP(
    HBITMAP hbm,
    HPALETTE hpal,
    GpBitmap** bitmap
    )
{
    API_ENTRY(GdipCreateBitmapFromHBITMAP);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(bitmap);

    return GpBitmap::CreateFromHBITMAP(hbm, hpal, bitmap);
}

GpStatus
WINGDIPAPI
GdipCreateHBITMAPFromBitmap(
    GpBitmap* bitmap,
    HBITMAP* hbmReturn,
    ARGB background
    )
{
    API_ENTRY(GdipCreateHBITMAPFromBitmap);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckColorParameter(background);
    CheckParameter(hbmReturn);
    CheckParameterValid(bitmap);
    CheckObjectBusy(bitmap);

    return bitmap->CreateHBITMAP(hbmReturn, background);
}

GpStatus
WINGDIPAPI
GdipCreateBitmapFromHICON(
    HICON hicon,
    GpBitmap** bitmap
    )
{
    API_ENTRY(GdipCreateBitmapFromHICON);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(bitmap);

    return GpBitmap::CreateFromHICON(hicon, bitmap);
}

GpStatus
WINGDIPAPI
GdipCreateBitmapFromResource(HINSTANCE hInstance,
                             GDIPCONST WCHAR* lpBitmapName,
                             GpBitmap** bitmap)
{
    API_ENTRY(GdipCreateBitmapFromResource);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(bitmap);

    return GpBitmap::CreateFromResource(hInstance, const_cast<WCHAR*>(lpBitmapName), bitmap);
}

GpStatus
WINGDIPAPI
GdipCreateHICONFromBitmap(
    GpBitmap* bitmap,
    HICON* hiconReturn)
{
    API_ENTRY(GdipCreateHICONFromBitmap);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(hiconReturn);
    CheckParameterValid(bitmap);
    CheckObjectBusy(bitmap);

    return bitmap->CreateHICON(hiconReturn);
}

GpStatus
WINGDIPAPI
GdipCloneBitmapArea(
    REAL x,
    REAL y,
    REAL width,
    REAL height,
    PixelFormatID format,
    GpBitmap *srcBitmap,
    GpBitmap **dstBitmap
    )
{
    API_ENTRY(GdipCloneBitmapArea);
    format = MaskPixelFormat(format);

   return GdipCloneBitmapAreaI(GpRound(x), GpRound(y),
                               GpRound(width), GpRound(height),
                               format, srcBitmap, dstBitmap);
}

GpStatus
WINGDIPAPI
GdipCloneBitmapAreaI(
    INT x,
    INT y,
    INT width,
    INT height,
    PixelFormatID format,
    GpBitmap *srcBitmap,
    GpBitmap **dstBitmap
    )
{
    API_ENTRY(GdipCloneBitmapAreaI);
   CheckParameter(dstBitmap);
   CheckParameterValid(srcBitmap);
   CheckObjectBusy(srcBitmap);

   format = MaskPixelFormat(format);

   GpRect rect(x, y, width, height);

   *dstBitmap = srcBitmap->Clone(&rect, format);

   if (*dstBitmap)
       return Ok;
   else
       return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipBitmapLockBits(
    GpBitmap* bitmap,
    GDIPCONST GpRect* rect,   // can be NULL
    UINT flags,
    PixelFormatID format,
    BitmapData* lockedBitmapData
)
{
    API_ENTRY(GdipBitmapLockBits);
    CheckParameter(lockedBitmapData);
    CheckParameterValid(bitmap);
    CheckObjectBusy(bitmap);

    format = MaskPixelFormat(format);

    return bitmap->LockBits(rect, flags, format, lockedBitmapData);
}

GpStatus
WINGDIPAPI
GdipBitmapUnlockBits(
    GpBitmap* bitmap,
    BitmapData* lockedBitmapData
)
{
    API_ENTRY(GdipBitmapUnlockBits);
    CheckParameter(lockedBitmapData);
    CheckParameterValid(bitmap);
    CheckObjectBusy(bitmap);

    return bitmap->UnlockBits(lockedBitmapData);
}

GpStatus
WINGDIPAPI
GdipBitmapGetPixel(
    GpBitmap* bitmap,
    INT x,
    INT y,
    ARGB *color
)
{
    API_ENTRY(GdipBitmapGetPixel);
    CheckParameter(color);
    CheckParameterValid(bitmap);
    CheckObjectBusy(bitmap);

    ARGB temp;
    Status status = bitmap->GetPixel(x, y, &temp);
    *color = temp;

    return status;
}

GpStatus
WINGDIPAPI
GdipBitmapSetPixel(
    GpBitmap* bitmap,
    INT x,
    INT y,
    ARGB color
)
{
    API_ENTRY(GdipBitmapSetPixel);
    CheckColorParameter(color);
    CheckParameterValid(bitmap);
    CheckObjectBusy(bitmap);

    return bitmap->SetPixel(x, y, color);
}

GpStatus
WINGDIPAPI
GdipBitmapSetResolution(
    GpBitmap* bitmap,
    REAL xdpi,
    REAL ydpi
)
{
    API_ENTRY(GdipBitmapSetResolution);
    CheckParameterValid(bitmap);
    CheckObjectBusy(bitmap);

    return bitmap->SetResolution(xdpi, ydpi);
}

GpStatus
WINGDIPAPI
GdipCreateImageAttributes(GpImageAttributes **imageattr)
{
    API_ENTRY(GdipCreateImageAttributes);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(imageattr);

    *imageattr = new GpImageAttributes();

    if (*imageattr)
    {
        if ((*imageattr)->IsValid())
        {
            return Ok;
        }
        else
        {
            (*imageattr)->Dispose();
            *imageattr = NULL;
            return OutOfMemory;
        }
    }
    else
    {
        return OutOfMemory;
    }
}

GpStatus
WINGDIPAPI
GdipCloneImageAttributes(
    GDIPCONST GpImageAttributes *imageattr,
    GpImageAttributes **cloneImageAttr
)
{
    API_ENTRY(GdipCloneImageAttributes);
    CheckParameter(cloneImageAttr);
    CheckParameterValid(imageattr);
    CheckObjectBusy(imageattr);

    *cloneImageAttr = imageattr->Clone();

    if (*cloneImageAttr)
        return Ok;
    else
        return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipDisposeImageAttributes(
    GpImageAttributes *imageattr
)
{
    API_ENTRY(GdipDisposeImageAttributes);
    // NOTE: Do NOT call CheckParameterValid(), because we need to free
    // the object, even if it's not in a valid state.
    CheckParameter(imageattr);
    CheckObjectBusyForDelete(imageattr);

    imageattr->Dispose();

    return Ok;
}

GpStatus
WINGDIPAPI
GdipSetImageAttributesToIdentity(
    GpImageAttributes *imageattr,
    ColorAdjustType type
)
{
    API_ENTRY(GdipSetImageAttributesToIdentity);
    CheckParameterValid(imageattr);
    CheckObjectBusy(imageattr);
    CheckParameter(ColorAdjustTypeIsValid(type));

    return imageattr->SetToIdentity(type);
}

GpStatus
WINGDIPAPI
GdipResetImageAttributes(
    GpImageAttributes *imageattr,
    ColorAdjustType type
)
{
    API_ENTRY(GdipResetImageAttributes);
    CheckParameterValid(imageattr);
    CheckObjectBusy(imageattr);
    CheckParameter(ColorAdjustTypeIsValid(type));

    return imageattr->Reset(type);
}

GpStatus
WINGDIPAPI
GdipSetImageAttributesColorMatrix(
    GpImageAttributes *imageattr,
    ColorAdjustType type,
    BOOL enableFlag,
    GDIPCONST ColorMatrix* colorMatrix,
    GDIPCONST ColorMatrix* grayMatrix,
    ColorMatrixFlags flags
)
{
    API_ENTRY(GdipSetImageAttributesColorMatrix);
    CheckParameterValid(imageattr);
    CheckObjectBusy(imageattr);
    CheckParameter(ColorAdjustTypeIsValid(type));

    // Note: GpImageAttributes::SetColorMatrix will validate
    //       colorMatrix since it may be valid to pass NULL
    //       (such as when enableFlag is FALSE).

    return imageattr->SetColorMatrix(type,
                                     enableFlag,
                                     const_cast<ColorMatrix*>(colorMatrix),
                                     const_cast<ColorMatrix*>(grayMatrix), flags);
}

GpStatus
WINGDIPAPI
GdipSetImageAttributesThreshold(
    GpImageAttributes *imageattr,
    ColorAdjustType type,
    BOOL enableFlag,
    REAL threshold
)
{
    API_ENTRY(GdipSetImageAttributesThreshold);
    CheckParameterValid(imageattr);
    CheckObjectBusy(imageattr);
    CheckParameter(ColorAdjustTypeIsValid(type));

    return imageattr->SetThreshold(type, enableFlag, threshold);
}

GpStatus
WINGDIPAPI
GdipSetImageAttributesGamma(
    GpImageAttributes *imageattr,
    ColorAdjustType type,
    BOOL enableFlag,
    REAL gamma
)
{
    API_ENTRY(GdipSetImageAttributesGamma);
    CheckParameterValid(imageattr);
    CheckObjectBusy(imageattr);
    CheckParameter(ColorAdjustTypeIsValid(type));

    return imageattr->SetGamma(type, enableFlag, gamma);
}

GpStatus
WINGDIPAPI
GdipSetImageAttributesNoOp(
    GpImageAttributes *imageattr,
    ColorAdjustType type,
    BOOL enableFlag
)
{
    API_ENTRY(GdipSetImageAttributesNoOp);
    CheckParameterValid(imageattr);
    CheckObjectBusy(imageattr);
    CheckParameter(ColorAdjustTypeIsValid(type));

    return imageattr->SetNoOp(type, enableFlag);
}

GpStatus
WINGDIPAPI
GdipSetImageAttributesColorKeys(
    GpImageAttributes *imageattr,
    ColorAdjustType type,
    BOOL enableFlag,
    ARGB argbLow,
    ARGB argbHigh
)
{
    API_ENTRY(GdipSetImageAttributesColorKeys);
    CheckColorParameter(argbLow);
    CheckColorParameter(argbHigh);
    CheckParameterValid(imageattr);
    CheckObjectBusy(imageattr);
    CheckParameter(ColorAdjustTypeIsValid(type));

    Color colorLow(argbLow);
    Color colorHigh(argbHigh);

    return imageattr->SetColorKeys(type, enableFlag, &colorLow, &colorHigh);
}

GpStatus
WINGDIPAPI
GdipSetImageAttributesOutputChannel(
    GpImageAttributes *imageattr,
    ColorAdjustType type,
    BOOL enableFlag,
    ColorChannelFlags channelFlags
)
{
    API_ENTRY(GdipSetImageAttributesOutputChannel);
    CheckParameterValid(imageattr);
    CheckObjectBusy(imageattr);
    CheckParameter(ColorAdjustTypeIsValid(type));

    return imageattr->SetOutputChannel(type, enableFlag, channelFlags);
}

GpStatus
WINGDIPAPI
GdipSetImageAttributesOutputChannelColorProfile(
    GpImageAttributes *imageattr,
    ColorAdjustType type,
    BOOL enableFlag,
    GDIPCONST WCHAR *colorProfileFilename)
{
    API_ENTRY(GdipSetImageAttributesOutputChannelColorProfile);
    CheckParameterValid(imageattr);
    CheckObjectBusy(imageattr);
    CheckParameter(ColorAdjustTypeIsValid(type));

    // Note: GpImageAttributes::SetOutputChannelProfile will validate
    //       colorProfileFilename since it may be valid to pass NULL
    //       (such as when enableFlag is FALSE).

    return imageattr->SetOutputChannelProfile(type, enableFlag,
                                              const_cast<WCHAR*>(colorProfileFilename));
}

GpStatus
WINGDIPAPI
GdipSetImageAttributesRemapTable(
    GpImageAttributes *imageattr,
    ColorAdjustType type,
    BOOL enableFlag,
    UINT mapSize,
    GDIPCONST ColorMap *map
)
{
    API_ENTRY(GdipSetImageAttributesRemapTable);
    CheckParameterValid(imageattr);
    CheckObjectBusy(imageattr);
    CheckParameter(ColorAdjustTypeIsValid(type));

    return imageattr->SetRemapTable(type, enableFlag, mapSize, const_cast<ColorMap*>(map));
}

GpStatus
WINGDIPAPI
GdipSetImageAttributesCachedBackground(
    GpImageAttributes *imageattr,
    BOOL enableFlag
)
{
    API_ENTRY(GdipSetImageAttributesCachedBackground);
    CheckParameterValid(imageattr);
    CheckObjectBusy(imageattr);

    return imageattr->SetCachedBackground(enableFlag);
}

GpStatus
WINGDIPAPI
GdipSetImageAttributesWrapMode(
    GpImageAttributes *imageAttr,
    WrapMode wrap,
    ARGB argb,
    BOOL clamp
)
{
    API_ENTRY(GdipSetImageAttributesWrapMode);
    CheckColorParameter(argb);
    CheckParameterValid(imageAttr);
    CheckObjectBusy(imageAttr);

    return imageAttr->SetWrapMode(wrap, argb, clamp);
}

GpStatus WINGDIPAPI
GdipGetImageAttributesAdjustedPalette(
    GpImageAttributes *imageAttr,
    ColorPalette * colorPalette,
    ColorAdjustType colorAdjustType
    )
{
    API_ENTRY(GdipGetImageAttributesAdjustedPalette);
    CheckParameterValid(imageAttr);
    CheckObjectBusy(imageAttr);
    CheckParameter((colorPalette != NULL) && (colorPalette->Count > 0));
    CheckParameter((colorAdjustType >= ColorAdjustTypeBitmap) &&
                   (colorAdjustType < ColorAdjustTypeCount));

    imageAttr->GetAdjustedPalette(colorPalette, colorAdjustType);

    return Ok;
}

GpStatus
WINGDIPAPI
GdipCreateFromHDC(
    HDC hdc,
    GpGraphics** graphics
    )
{
    API_ENTRY(GdipCreateFromHDC);
    CheckGdiplusInitialized; // We do this in all our object creation API's

   CheckParameter(graphics);


   *graphics = GpGraphics::GetFromHdc(hdc);

   if (CheckValid(*graphics))
       return Ok;
   else
       return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipCreateFromHDC2(
    HDC hdc,
    HANDLE hdevice,
    GpGraphics** graphics
    )
{
    API_ENTRY(GdipCreateFromHDC2);
    CheckGdiplusInitialized; // We do this in all our object creation API's

   CheckParameter(graphics);


   *graphics = GpGraphics::GetFromHdc(hdc, hdevice);

   if (CheckValid(*graphics))
       return Ok;
   else
       return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipCreateFromHWND(
    HWND hwnd,
    GpGraphics** graphics
    )
{
    API_ENTRY(GdipCreateFromHWND);
    CheckGdiplusInitialized; // We do this in all our object creation API's

   CheckParameter(graphics);

   *graphics = GpGraphics::GetFromHwnd(hwnd);

   if (CheckValid(*graphics))
       return Ok;
   else
       return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipCreateFromHWNDICM(
    HWND hwnd,
    GpGraphics** graphics
    )
{
    API_ENTRY(GdipCreateFromHWNDICM);
    CheckGdiplusInitialized; // We do this in all our object creation API's

   CheckParameter(graphics);

   *graphics = GpGraphics::GetFromHwnd(hwnd, IcmModeOn);

   if (CheckValid(*graphics))
       return Ok;
   else
       return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipDeleteGraphics(
    GpGraphics* graphics
    )
{
    API_ENTRY(GdipDeleteGraphics);
    // NOTE: Do NOT call CheckParameterValid(), because we need to free
    // the object, even if it's not in a valid state.
    CheckParameter(graphics);
    CheckObjectBusyForDelete(graphics);


    delete graphics;

    return Ok;
}

GpStatus
WINGDIPAPI
GdipFlush(
    GpGraphics* graphics,
    GpFlushIntention intention
    )
{
    API_ENTRY(GdipFlush);
    CheckParameter(graphics);
    CheckObjectBusy(graphics);


    graphics->Flush(intention);

    return Ok;
}


GpStatus
WINGDIPAPI
GdipSetRenderingOrigin(
    GpGraphics* graphics,
    INT x,
    INT y
    )
{
    API_ENTRY(GdipSetRenderingOrigin);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);

    graphics->SetRenderingOrigin(x, y);

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetRenderingOrigin(
    GpGraphics *graphics,
    INT *x,
    INT *y
    )
{
    API_ENTRY(GdipGetRenderingOrigin);
    CheckParameter(x);
    CheckParameter(y);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);

    graphics->GetRenderingOrigin(x, y);

    return Ok;
}



GpStatus
WINGDIPAPI
GdipSetCompositingMode(
    GpGraphics* graphics,
    CompositingMode newMode
    )
{
    API_ENTRY(GdipSetCompositingMode);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);

    graphics->SetCompositingMode(newMode);

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetCompositingMode(
    GpGraphics *graphics,
    CompositingMode *mode
    )
{
    API_ENTRY(GdipGetCompositingMode);
    CheckParameter(mode);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);

    *mode = graphics->GetCompositingMode();

    return Ok;
}

GpStatus
WINGDIPAPI
GdipSetCompositingQuality(
    GpGraphics* graphics,
    CompositingQuality newQuality
    )
{
    API_ENTRY(GdipSetCompositingQuality);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);

    graphics->SetCompositingQuality(newQuality);

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetCompositingQuality(
    GpGraphics *graphics,
    CompositingQuality *quality
    )
{
    API_ENTRY(GdipGetCompositingQuality);
    CheckParameter(quality);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);

    *quality = graphics->GetCompositingQuality();

    return Ok;
}

GpStatus
WINGDIPAPI
GdipSetSmoothingMode(
    GpGraphics* graphics,
    SmoothingMode smoothingMode
    )
{
    API_ENTRY(GdipSetSmoothingMode);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);

   // For now, have it so that a Rendering hint of 1
   // means antialiasing ON and a Rendering Hint of 0
   // means Off.

   switch(smoothingMode)
   {
   case SmoothingModeDefault:
   case SmoothingModeHighSpeed:
   case SmoothingModeNone:
       graphics->SetAntiAliasMode(FALSE);
       break;

   case SmoothingModeHighQuality:
   case SmoothingModeAntiAlias:
       graphics->SetAntiAliasMode(TRUE);
       break;

   default:
       // unknown rendering mode
       return InvalidParameter;
   }

   return Ok;
}

GpStatus
WINGDIPAPI
GdipGetSmoothingMode(
    GpGraphics *graphics,
    SmoothingMode *smoothingMode
    )
{
    API_ENTRY(GdipGetSmoothingMode);
   CheckParameter(smoothingMode);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);

   BOOL aaOn = graphics->GetAntiAliasMode();

   if (aaOn)
   {
       *smoothingMode = SmoothingModeAntiAlias;
   }
   else
   {
       *smoothingMode = SmoothingModeNone;
   }

   return Ok;
}

GpStatus
WINGDIPAPI
GdipSetPixelOffsetMode(
    GpGraphics* graphics,
    PixelOffsetMode newMode
    )
{
    API_ENTRY(GdipSetPixelOffsetMode);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);
    CheckParameter((newMode >= PixelOffsetModeDefault) &&
                   (newMode <= PixelOffsetModeHalf));

    graphics->SetPixelOffsetMode(newMode);
    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetPixelOffsetMode(
    GpGraphics *graphics,
    PixelOffsetMode *pixelOffsetMode
    )
{
    API_ENTRY(GdipGetPixelOffsetMode);
   CheckParameter(pixelOffsetMode);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);

   *pixelOffsetMode = graphics->GetPixelOffsetMode();
   return Ok;
}

GpStatus
WINGDIPAPI
GdipSetTextRenderingHint(
    GpGraphics* graphics,
    TextRenderingHint newMode
    )
{
    API_ENTRY(GdipSetTextRenderingHint);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);

   // For now, have it so that a Rendering hint of 1
   // means antialiasing ON and a Rendering Hint of 0
   // means Off.

   switch(newMode)
   {
   case TextRenderingHintSystemDefault:
   case TextRenderingHintSingleBitPerPixelGridFit:
   case TextRenderingHintSingleBitPerPixel:
   case TextRenderingHintAntiAlias:
   case TextRenderingHintAntiAliasGridFit:
   case TextRenderingHintClearTypeGridFit:
       graphics->SetTextRenderingHint(newMode);
       break;

   default:
       // unknown rendering mode
       return InvalidParameter;
   }

   return Ok;
}

GpStatus
WINGDIPAPI
GdipSetTextContrast(
    GpGraphics* graphics,
    UINT        contrast
    )
{
    API_ENTRY(GdipSetTextContrast);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);

   return graphics->SetTextContrast(contrast);
}

GpStatus
WINGDIPAPI
GdipGetTextContrast(
    GpGraphics* graphics,
    UINT      * contrast
    )
{
    API_ENTRY(GdipGetTextContrast);
   CheckParameter(contrast);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);

   *contrast = graphics->GetTextContrast();

   return Ok;
}


GpStatus
WINGDIPAPI
GdipGetTextRenderingHint(
    GpGraphics *graphics,
    TextRenderingHint *mode
    )
{
    API_ENTRY(GdipGetTextRenderingHint);
   CheckParameter(mode);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);

   *mode = graphics->GetTextRenderingHint();

   return Ok;
}

GpStatus
WINGDIPAPI
GdipSetInterpolationMode(
    GpGraphics* graphics,
    InterpolationMode interpolationMode
    )
{
    API_ENTRY(GdipSetInterpolationMode);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);
   CheckParameter((interpolationMode >= InterpolationModeDefault) &&
                  (interpolationMode <= InterpolationModeHighQualityBicubic));

   if(Globals::ForceBilinear)
   {
       if(interpolationMode != InterpolationModeNearestNeighbor)
       {
           interpolationMode = InterpolationModeBilinear;
       }
   }
   else
   {
       if(interpolationMode == InterpolationModeDefault ||
          interpolationMode == InterpolationModeLowQuality)
       {
           interpolationMode = InterpolationModeBilinear;
       }
       else if(interpolationMode == InterpolationModeHighQuality)
       {
           interpolationMode = InterpolationModeHighQualityBicubic;
       }
   }

   graphics->SetInterpolationMode(interpolationMode);

   return Ok;
}

GpStatus
WINGDIPAPI
GdipGetInterpolationMode(
    GpGraphics* graphics,
    InterpolationMode *interpolationMode
    )
{
    API_ENTRY(GdipGetInterpolationMode);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);

   *interpolationMode = graphics->GetInterpolationMode();

   return Ok;
}

GpStatus
WINGDIPAPI
GdipSetWorldTransform(
    GpGraphics *graphics,
    GpMatrix *matrix
    )
{
    API_ENTRY(GdipSetWorldTransform);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);
   CheckParameterValid(matrix);
   CheckObjectBusy(matrix);
    
   return graphics->SetWorldTransform(*matrix);
}

GpStatus
WINGDIPAPI
GdipResetWorldTransform(
    GpGraphics *graphics
    )
{
    API_ENTRY(GdipResetWorldTransform);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);

   return graphics->ResetWorldTransform();
}

GpStatus
WINGDIPAPI
GdipMultiplyWorldTransform(
    GpGraphics *graphics,
    GDIPCONST GpMatrix *matrix,
    GpMatrixOrder order
    )
{
    API_ENTRY(GdipMultiplyWorldTransform);
    if(matrix == NULL)
        return Ok;

    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);
    CheckParameterValid(matrix);
    CheckObjectBusy(matrix);
    CheckParameter(MatrixOrderIsValid(order));

    return graphics->MultiplyWorldTransform(*matrix, order);
}

GpStatus
WINGDIPAPI
GdipTranslateWorldTransform(
    GpGraphics *graphics,
    REAL dx,
    REAL dy,
    GpMatrixOrder order
    )
{
    API_ENTRY(GdipTranslateWorldTransform);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);
    CheckParameter(MatrixOrderIsValid(order));

    return graphics->TranslateWorldTransform(dx, dy, order);
}

GpStatus
WINGDIPAPI
GdipScaleWorldTransform(
    GpGraphics *graphics,
    REAL sx,
    REAL sy,
    GpMatrixOrder order
    )
{
    API_ENTRY(GdipScaleWorldTransform);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);
    CheckParameter(MatrixOrderIsValid(order));
    
    return graphics->ScaleWorldTransform(sx, sy, order);
}

GpStatus
WINGDIPAPI
GdipRotateWorldTransform(
    GpGraphics *graphics,
    REAL angle,
    GpMatrixOrder order
    )
{
    API_ENTRY(GdipRotateWorldTransform);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);
    CheckParameter(MatrixOrderIsValid(order));

    return graphics->RotateWorldTransform(angle, order);
}

GpStatus
WINGDIPAPI
GdipGetWorldTransform(
    GpGraphics *graphics,
    GpMatrix *matrix
    )
{
    API_ENTRY(GdipGetWorldTransform);
    CheckParameter(matrix);
    CheckObjectBusy(matrix);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);

    graphics->GetWorldTransform(*matrix);
    return Ok;
}

GpStatus
WINGDIPAPI
GdipResetPageTransform(
    GpGraphics *graphics
    )
{
    API_ENTRY(GdipResetPageTransform);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);

   return graphics->ResetPageTransform();
}

GpStatus
WINGDIPAPI
GdipGetPageUnit(
    GpGraphics* graphics,
    GpPageUnit* unit
    )
{
    API_ENTRY(GdipGetPageUnit);
   CheckParameter(unit);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);

   *unit = graphics->GetPageUnit();

   return Ok;
}

GpStatus
WINGDIPAPI
GdipSetPageUnit(
    GpGraphics* graphics,
    GpPageUnit unit
    )
{
    API_ENTRY(GdipSetPageUnit);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);

   // PageUnit can't be world
    CheckParameter((unit > UnitWorld) && (unit <= UnitMillimeter));

    return graphics->SetPageUnit(unit);
}

GpStatus
WINGDIPAPI
GdipGetPageScale(
    GpGraphics* graphics,
    REAL* scale
    )
{
    API_ENTRY(GdipGetPageScale);
   CheckParameter(scale);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);

   *scale = graphics->GetPageScale();

   return Ok;
}

GpStatus
WINGDIPAPI
GdipSetPageScale(
    GpGraphics* graphics,
    REAL scale
    )
{
    API_ENTRY(GdipSetPageScale);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);

   return graphics->SetPageScale(scale);
}

GpStatus
WINGDIPAPI
GdipGetDpiX(
    GpGraphics* graphics,
    REAL* dpi
    )
{
    API_ENTRY(GdipGetDpiX);
   CheckParameter(dpi);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);

   *dpi = graphics->GetDpiX();

   return Ok;
}

GpStatus
WINGDIPAPI
GdipGetDpiY(
    GpGraphics* graphics,
    REAL* dpi
    )
{
    API_ENTRY(GdipGetDpiY);
   CheckParameter(dpi);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);

   *dpi = graphics->GetDpiY();

   return Ok;
}

GpStatus
WINGDIPAPI
GdipTransformPoints(
    GpGraphics *graphics,
    GpCoordinateSpace destSpace,
    GpCoordinateSpace srcSpace,
    GpPointF *points,
    INT count
    )
{
    API_ENTRY(GdipTransformPoints);
   CheckParameter(points && count > 0);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);

   return graphics->TransformPoints(points, count, srcSpace, destSpace);
}

GpStatus
WINGDIPAPI
GdipTransformPointsI(
    GpGraphics *graphics,
    GpCoordinateSpace destSpace,
    GpCoordinateSpace srcSpace,
    GpPoint *points,
    INT count
    )
{
    API_ENTRY(GdipTransformPointsI);
   CheckParameter(points && count > 0);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);

   INT i;
   StackBuffer buffer;

   GpPointF* pointsF = (GpPointF*) buffer.GetBuffer(count*sizeof(GpPointF));

   if(!pointsF) return OutOfMemory;

   for (i=0; i<count; i++)
   {
       pointsF[i].X = TOREAL(points[i].X);
       pointsF[i].Y = TOREAL(points[i].Y);
   }

   GpStatus status = graphics->TransformPoints(pointsF, count, srcSpace, destSpace);

   for (i=0; i<count; i++)
   {
       points[i].X = GpRound(pointsF[i].X);
       points[i].Y = GpRound(pointsF[i].Y);
   }

   return status;
}

//------------------------------------------------------------------------
// GetNearestColor (for <= 8bpp surfaces)
//------------------------------------------------------------------------

GpStatus
WINGDIPAPI
GdipGetNearestColor(
    GpGraphics *graphics,
    ARGB *argb
    )
{
    API_ENTRY(GdipGetNearestColor);
    CheckParameter(argb);
    CheckColorParameter(*argb);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);

    ARGB temp = graphics->GetNearestColor(*argb);
    *argb = temp;
    return Ok;
}

// defined in Engine\render\Halftone.cpp
extern HPALETTE WINGDIPAPI GdipCreateHalftonePalette();

GpStatus
WINGDIPAPI
GdipDrawLine(
    GpGraphics *graphics,
    GpPen *pen,
    REAL x1,
    REAL y1,
    REAL x2,
    REAL y2
    )
{
    API_ENTRY(GdipDrawLine);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);
    CheckParameterValid(pen);
    CheckObjectBusy(pen);


    return graphics->DrawLine(pen, x1, y1, x2, y2);
}

GpStatus
WINGDIPAPI
GdipDrawLineI(
    GpGraphics *graphics,
    GpPen *pen,
    INT x1,
    INT y1,
    INT x2,
    INT y2
    )
{
    API_ENTRY(GdipDrawLineI);
    return GdipDrawLine(graphics, pen, TOREAL(x1), TOREAL(y1), TOREAL(x2), TOREAL(y2));
}

GpStatus
WINGDIPAPI
GdipDrawLines(
    GpGraphics *graphics,
    GpPen *pen,
    GDIPCONST GpPointF *points,
    INT count
    )
{
    API_ENTRY(GdipDrawLines);
   CheckParameter(points && count > 0);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);
   CheckParameterValid(pen);
   CheckObjectBusy(pen);


   return graphics->DrawLines(pen, points, count);
}

GpStatus
WINGDIPAPI
GdipDrawLinesI(
    GpGraphics *graphics,
    GpPen *pen,
    GDIPCONST GpPoint *points,
    INT count
    )
{
    API_ENTRY(GdipDrawLinesI);
   StackBuffer buffer;

   GpPointF* pointsF = (GpPointF*) buffer.GetBuffer(count*sizeof(GpPointF));

   if(!pointsF) return OutOfMemory;

   for (INT i=0; i<count; i++)
   {
       pointsF[i].X = TOREAL(points[i].X);
       pointsF[i].Y = TOREAL(points[i].Y);
   }

   GpStatus status = GdipDrawLines(graphics, pen, pointsF, count);

   return status;
}

GpStatus
WINGDIPAPI
GdipDrawArc(
    GpGraphics *graphics,
    GpPen *pen,
    REAL x,
    REAL y,
    REAL width,
    REAL height,
    REAL startAngle,
    REAL sweepAngle
    )
{
    API_ENTRY(GdipDrawArc);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);
   CheckParameterValid(pen);
   CheckObjectBusy(pen);


   return graphics->DrawArc(pen, x, y, width, height, startAngle, sweepAngle);
}

GpStatus
WINGDIPAPI
GdipDrawArcI(
    GpGraphics *graphics,
    GpPen *pen,
    INT x,
    INT y,
    INT width,
    INT height,
    REAL startAngle,
    REAL sweepAngle
    )
{
    API_ENTRY(GdipDrawArcI);
   return GdipDrawArc(graphics, pen, TOREAL(x), TOREAL(y),
                      TOREAL(width), TOREAL(height), startAngle, sweepAngle);
}

GpStatus
WINGDIPAPI
GdipDrawBezier(
    GpGraphics *graphics,
    GpPen *pen,
    REAL x1,
    REAL y1,
    REAL x2,
    REAL y2,
    REAL x3,
    REAL y3,
    REAL x4,
    REAL y4
    )
{
    API_ENTRY(GdipDrawBezier);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);
   CheckParameterValid(pen);
   CheckObjectBusy(pen);


   return graphics->DrawBezier(pen, x1, y1, x2, y2, x3, y3, x4, y4);
}

GpStatus
WINGDIPAPI
GdipDrawBezierI(
    GpGraphics *graphics,
    GpPen *pen,
    INT x1,
    INT y1,
    INT x2,
    INT y2,
    INT x3,
    INT y3,
    INT x4,
    INT y4
    )
{
    API_ENTRY(GdipDrawBezierI);
   return GdipDrawBezier(graphics, pen, TOREAL(x1), TOREAL(y1),
                         TOREAL(x2), TOREAL(y2), TOREAL(x3), TOREAL(y3),
                         TOREAL(x4), TOREAL(y4));
}

GpStatus
WINGDIPAPI
GdipDrawBeziers(
    GpGraphics *graphics,
    GpPen *pen,
    GDIPCONST GpPointF *points,
    INT count
    )
{
    API_ENTRY(GdipDrawBeziers);
   CheckParameter(points && count > 0);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);
   CheckParameterValid(pen);
   CheckObjectBusy(pen);


   return graphics->DrawBeziers(pen, points, count);
}

GpStatus
WINGDIPAPI
GdipDrawBeziersI(
    GpGraphics *graphics,
    GpPen *pen,
    GDIPCONST GpPoint *points,
    INT count
    )
{
    API_ENTRY(GdipDrawBeziersI);
    
    // Must check these parameters because we use them before calling into
    // GdipDrawBeziers
    
    CheckParameter(points && count > 0);
    
    StackBuffer buffer;

    GpPointF* pointsF = (GpPointF*) buffer.GetBuffer(count*sizeof(GpPointF));

    if(!pointsF) return OutOfMemory;

    for (INT i=0; i<count; i++)
    {
        pointsF[i].X = TOREAL(points[i].X);
        pointsF[i].Y = TOREAL(points[i].Y);
    }

    GpStatus status = GdipDrawBeziers(graphics, pen, pointsF, count);

    return status;
}

GpStatus
WINGDIPAPI
GdipDrawRectangle(
    GpGraphics *graphics,
    GpPen *pen,
    REAL x,
    REAL y,
    REAL width,
    REAL height)
{
    API_ENTRY(GdipDrawRectangle);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);
   CheckParameterValid(pen);
   CheckObjectBusy(pen);


   return graphics->DrawRect(pen, x, y, width, height);
}

GpStatus
WINGDIPAPI
GdipDrawRectangleI(
    GpGraphics *graphics,
    GpPen *pen,
    INT x,
    INT y,
    INT width,
    INT height)
{
    API_ENTRY(GdipDrawRectangleI);
   return GdipDrawRectangle(graphics, pen,
                            TOREAL(x), TOREAL(y),
                            TOREAL(width), TOREAL(height));
}

GpStatus
WINGDIPAPI
GdipDrawRectangles(
    GpGraphics *graphics,
    GpPen *pen,
    GDIPCONST GpRectF *rects,
    INT count
    )
{
    API_ENTRY(GdipDrawRectangles);
   CheckParameter(rects && count > 0);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);
   CheckParameterValid(pen);
   CheckObjectBusy(pen);


   return graphics->DrawRects(pen, rects, count);
}

GpStatus
WINGDIPAPI
GdipDrawRectanglesI(
    GpGraphics *graphics,
    GpPen *pen,
    GDIPCONST GpRect *rects,
    INT count
    )
{
    API_ENTRY(GdipDrawRectanglesI);
    CheckParameter(rects && count > 0);
   
   StackBuffer buffer;

   GpRectF *rectsF = (GpRectF*) buffer.GetBuffer(count*sizeof(GpRectF));

   if(!rectsF) return OutOfMemory;

   for (INT i=0; i<count; i++)
   {
       rectsF[i].X = TOREAL(rects[i].X);
       rectsF[i].Y = TOREAL(rects[i].Y);
       rectsF[i].Width = TOREAL(rects[i].Width);
       rectsF[i].Height = TOREAL(rects[i].Height);
   }

   GpStatus status = GdipDrawRectangles(graphics, pen, rectsF, count);

   return status;
}

GpStatus
WINGDIPAPI
GdipDrawEllipse(
    GpGraphics *graphics,
    GpPen *pen,
    REAL x,
    REAL y,
    REAL width,
    REAL height
    )
{
    API_ENTRY(GdipDrawEllipse);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);
   CheckParameterValid(pen);
   CheckObjectBusy(pen);


   return graphics->DrawEllipse(pen, x, y, width, height);
}

GpStatus
WINGDIPAPI
GdipDrawEllipseI(
    GpGraphics *graphics,
    GpPen *pen,
    INT x,
    INT y,
    INT width,
    INT height
    )
{
    API_ENTRY(GdipDrawEllipseI);
   return GdipDrawEllipse(graphics, pen,
                          TOREAL(x), TOREAL(y),
                          TOREAL(width), TOREAL(height));
}

GpStatus
WINGDIPAPI
GdipDrawPie(
    GpGraphics *graphics,
    GpPen *pen,
    REAL x,
    REAL y,
    REAL width,
    REAL height,
    REAL startAngle,
    REAL sweepAngle
    )
{
    API_ENTRY(GdipDrawPie);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);
   CheckParameterValid(pen);
   CheckObjectBusy(pen);


   return graphics->DrawPie(pen, x, y, width, height, startAngle, sweepAngle);
}

GpStatus
WINGDIPAPI
GdipDrawPieI(
    GpGraphics *graphics,
    GpPen *pen,
    INT x,
    INT y,
    INT width,
    INT height,
    REAL startAngle,
    REAL sweepAngle
    )
{
    API_ENTRY(GdipDrawPieI);
   return GdipDrawPie(graphics, pen,
                      TOREAL(x), TOREAL(y),
                      TOREAL(width), TOREAL(height),
                      startAngle, sweepAngle);
}

GpStatus
WINGDIPAPI
GdipDrawPolygon(
    GpGraphics *graphics,
    GpPen *pen,
    GDIPCONST GpPointF *points,
    INT count)
{
    API_ENTRY(GdipDrawPolygon);
   CheckParameter(points && count > 0);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);
   CheckParameterValid(pen);
   CheckObjectBusy(pen);


   return graphics->DrawPolygon(pen, points, count);
}

GpStatus
WINGDIPAPI
GdipDrawPolygonI(
    GpGraphics *graphics,
    GpPen *pen,
    GDIPCONST GpPoint *points,
    INT count)
{
    API_ENTRY(GdipDrawPolygonI);
   StackBuffer buffer;

   GpPointF *pointsF = (GpPointF*) buffer.GetBuffer(count*sizeof(GpPointF));

   if(!pointsF) return OutOfMemory;

   for (INT i=0; i<count; i++)
   {
       pointsF[i].X = TOREAL(points[i].X);
       pointsF[i].Y = TOREAL(points[i].Y);
   }

   GpStatus status = GdipDrawPolygon(graphics, pen, pointsF, count);

   return status;
}

GpStatus
WINGDIPAPI
GdipDrawPath(
    GpGraphics *graphics,
    GpPen *pen,
    GpPath *path)
{
    API_ENTRY(GdipDrawPath);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);
    CheckParameterValid(pen);
    CheckObjectBusy(pen);
    CheckParameterValid(path);
    CheckObjectBusy(path);


    return graphics->DrawPath(pen, path);
}

GpStatus
WINGDIPAPI
GdipDrawCurve(
    GpGraphics *graphics,
    GpPen *pen,
    GDIPCONST GpPointF *points,
    INT count
    )
{
    API_ENTRY(GdipDrawCurve);
   CheckParameter(points && count > 0);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);
   CheckParameterValid(pen);
   CheckObjectBusy(pen);


   return graphics->DrawCurve(pen, points, count);
}

GpStatus
WINGDIPAPI
GdipDrawCurveI(
    GpGraphics *graphics,
    GpPen *pen,
    GDIPCONST GpPoint *points,
    INT count
    )
{
    API_ENTRY(GdipDrawCurveI);
   StackBuffer buffer;

   GpPointF *pointsF = (GpPointF*) buffer.GetBuffer(count*sizeof(GpPointF));

   if(!pointsF) return OutOfMemory;

   for (INT i=0; i<count; i++)
   {
       pointsF[i].X = TOREAL(points[i].X);
       pointsF[i].Y = TOREAL(points[i].Y);
   }

   GpStatus status = GdipDrawCurve(graphics, pen, pointsF, count);

   return status;
}

GpStatus
WINGDIPAPI
GdipDrawCurve2(
    GpGraphics *graphics,
    GpPen *pen,
    GDIPCONST GpPointF *points,
    INT count,
    REAL tension
    )
{
    API_ENTRY(GdipDrawCurve2);
   CheckParameter(points && count > 0);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);
   CheckParameterValid(pen);
   CheckObjectBusy(pen);


   return graphics->DrawCurve(pen, points, count, tension, 0, count - 1);
}

GpStatus
WINGDIPAPI
GdipDrawCurve2I(
    GpGraphics *graphics,
    GpPen *pen,
    GDIPCONST GpPoint *points,
    INT count,
    REAL tension
    )
{
    API_ENTRY(GdipDrawCurve2I);
   StackBuffer buffer;

   GpPointF *pointsF = (GpPointF*) buffer.GetBuffer(count*sizeof(GpPointF));

   if(!pointsF) return OutOfMemory;

   for (INT i=0; i<count; i++)
   {
       pointsF[i].X = TOREAL(points[i].X);
       pointsF[i].Y = TOREAL(points[i].Y);
   }

   GpStatus status = GdipDrawCurve2(graphics, pen, pointsF, count, tension);

   return status;
}

GpStatus
WINGDIPAPI
GdipDrawCurve3(
    GpGraphics *graphics,
    GpPen *pen,
    GDIPCONST GpPointF *points,
    INT count,
    INT offset,
    INT numberOfSegments,
    REAL tension
    )
{
    API_ENTRY(GdipDrawCurve3);
   CheckParameter(points && count > 0);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);
   CheckParameterValid(pen);
   CheckObjectBusy(pen);


   return graphics->DrawCurve(pen, points, count, tension,
                              offset, numberOfSegments);
}

GpStatus
WINGDIPAPI
GdipDrawCurve3I(
    GpGraphics *graphics,
    GpPen *pen,
    GDIPCONST GpPoint *points,
    INT count,
    INT offset,
    INT numberOfSegments,
    REAL tension
    )
{
    API_ENTRY(GdipDrawCurve3I);
   StackBuffer buffer;

   GpPointF *pointsF = (GpPointF*) buffer.GetBuffer(count*sizeof(GpPointF));

   if(!pointsF) return OutOfMemory;

   for (INT i=0; i<count; i++)
   {
       pointsF[i].X = TOREAL(points[i].X);
       pointsF[i].Y = TOREAL(points[i].Y);
   }

   GpStatus status = GdipDrawCurve3(graphics, pen, pointsF, count,
                                    offset, numberOfSegments, tension);

   return status;
}

GpStatus
WINGDIPAPI
GdipDrawClosedCurve(
    GpGraphics *graphics,
    GpPen *pen,
    GDIPCONST GpPointF *points,
    INT count)
{
    API_ENTRY(GdipDrawClosedCurve);
   CheckParameter(points && count > 0);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);
   CheckParameterValid(pen);
   CheckObjectBusy(pen);


   return graphics->DrawClosedCurve(pen, points, count);
}

GpStatus
WINGDIPAPI
GdipDrawClosedCurveI(
    GpGraphics *graphics,
    GpPen *pen,
    GDIPCONST GpPoint *points,
    INT count)
{
    API_ENTRY(GdipDrawClosedCurveI);
   StackBuffer buffer;

   GpPointF *pointsF = (GpPointF*) buffer.GetBuffer(count*sizeof(GpPointF));

   if(!pointsF) return OutOfMemory;

   for (INT i=0; i<count; i++)
   {
       pointsF[i].X = TOREAL(points[i].X);
       pointsF[i].Y = TOREAL(points[i].Y);
   }

   GpStatus status = GdipDrawClosedCurve(graphics, pen, pointsF, count);

   return status;
}

GpStatus
WINGDIPAPI
GdipDrawClosedCurve2(
    GpGraphics *graphics,
    GpPen *pen,
    GDIPCONST GpPointF *points,
    INT count,
    REAL tension)
{
    API_ENTRY(GdipDrawClosedCurve2);
   CheckParameter(points && count > 0);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);
   CheckParameterValid(pen);
   CheckObjectBusy(pen);


   return graphics->DrawClosedCurve(pen, points, count, tension);
}

GpStatus
WINGDIPAPI
GdipDrawClosedCurve2I(
    GpGraphics *graphics,
    GpPen *pen,
    GDIPCONST GpPoint *points,
    INT count,
    REAL tension)
{
    API_ENTRY(GdipDrawClosedCurve2I);
   StackBuffer buffer;

   GpPointF *pointsF = (GpPointF*) buffer.GetBuffer(count*sizeof(GpPointF));

   if(!pointsF) return OutOfMemory;

   for (INT i=0; i<count; i++)
   {
       pointsF[i].X = TOREAL(points[i].X);
       pointsF[i].Y = TOREAL(points[i].Y);
   }

   GpStatus status = GdipDrawClosedCurve2(graphics, pen, pointsF, count, tension);

   return status;
}

GpStatus WINGDIPAPI
GdipGraphicsClear(
    GpGraphics *graphics,
    ARGB color)
{
    API_ENTRY(GdipGraphicsClear);
    CheckColorParameter(color);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);

    return graphics->Clear(GpColor(color));
}

GpStatus
WINGDIPAPI
GdipFillRectangle(
    GpGraphics *graphics,
    GpBrush *fill,
    REAL x,
    REAL y,
    REAL width,
    REAL height)
{
    API_ENTRY(GdipFillRectangle);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);
   CheckParameterValid(fill);
   CheckObjectBusy(fill);


   return graphics->FillRect(fill, x, y, width, height);
}

GpStatus
WINGDIPAPI
GdipFillRectangleI(
    GpGraphics *graphics,
    GpBrush *fill,
    INT x,
    INT y,
    INT width,
    INT height)
{
    API_ENTRY(GdipFillRectangleI);
   return GdipFillRectangle(graphics, fill, TOREAL(x), TOREAL(y), TOREAL(width), TOREAL(height));
}

GpStatus
WINGDIPAPI
GdipFillRectangles(
    GpGraphics *graphics,
    GpBrush *fill,
    GDIPCONST GpRectF *rects,
    INT count)
{
    API_ENTRY(GdipFillRectangles);
   CheckParameter(rects && count > 0);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);
   CheckParameterValid(fill);
   CheckObjectBusy(fill);


   return graphics->FillRects(fill, rects, count);
}

GpStatus
WINGDIPAPI
GdipFillRectanglesI(
    GpGraphics *graphics,
    GpBrush *fill,
    GDIPCONST GpRect *rects,
    INT count)
{
    API_ENTRY(GdipFillRectanglesI);
    StackBuffer buffer;

    GpRectF *rectsF = (GpRectF*) buffer.GetBuffer(count*sizeof(GpRectF));

    if(!rectsF) return OutOfMemory;

    for (INT i=0; i<count; i++)
    {
        rectsF[i].X = TOREAL(rects[i].X);
        rectsF[i].Y = TOREAL(rects[i].Y);
        rectsF[i].Width = TOREAL(rects[i].Width);
        rectsF[i].Height = TOREAL(rects[i].Height);
    }

    GpStatus status = GdipFillRectangles(graphics, fill, rectsF, count);

    return status;
}

GpStatus
WINGDIPAPI
GdipFillPolygon(
    GpGraphics *graphics,
    GpBrush *fill,
    GDIPCONST GpPointF *points,
    INT count,
    GpFillMode fillMode
    )
{
    API_ENTRY(GdipFillPolygon);
   CheckParameter(points && count > 0);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);
   CheckParameterValid(fill);
   CheckObjectBusy(fill);


   return graphics->FillPolygon(fill, points, count, fillMode);
}

GpStatus
WINGDIPAPI
GdipFillPolygonI(
    GpGraphics *graphics,
    GpBrush *fill,
    GDIPCONST GpPoint *points,
    INT count,
    GpFillMode fillMode
    )
{
    API_ENTRY(GdipFillPolygonI);
   StackBuffer buffer;

   GpPointF *pointsF = (GpPointF*) buffer.GetBuffer(count*sizeof(GpPointF));

   if(!pointsF) return OutOfMemory;

   for (INT i=0; i<count; i++)
   {
       pointsF[i].X = TOREAL(points[i].X);
       pointsF[i].Y = TOREAL(points[i].Y);
   }

   GpStatus status = GdipFillPolygon(graphics, fill, pointsF, count, fillMode);

   return status;
}

GpStatus
WINGDIPAPI
GdipFillPolygon2(
    GpGraphics *graphics,
    GpBrush *fill,
    GDIPCONST GpPointF *points,
    INT count
    )
{
    API_ENTRY(GdipFillPolygon2);
   CheckParameter(points && count > 0);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);
   CheckParameterValid(fill);
   CheckObjectBusy(fill);


   return graphics->FillPolygon(fill, points, count);
}

GpStatus
WINGDIPAPI
GdipFillPolygon2I(
    GpGraphics *graphics,
    GpBrush *fill,
    GDIPCONST GpPoint *points,
    INT count
    )
{
    API_ENTRY(GdipFillPolygon2I);
   StackBuffer buffer;

   GpPointF *pointsF = (GpPointF*) buffer.GetBuffer(count*sizeof(GpPointF));

   if(!pointsF) return OutOfMemory;

   for (INT i=0; i<count; i++)
   {
       pointsF[i].X = TOREAL(points[i].X);
       pointsF[i].Y = TOREAL(points[i].Y);
   }

   GpStatus status = GdipFillPolygon2(graphics, fill, pointsF, count);

   return status;
}

GpStatus
WINGDIPAPI
GdipFillEllipse(
    GpGraphics *graphics,
    GpBrush *fill,
    REAL x,
    REAL y,
    REAL width,
    REAL height
    )
{
    API_ENTRY(GdipFillEllipse);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);
   CheckParameterValid(fill);
   CheckObjectBusy(fill);


   return graphics->FillEllipse(fill, x, y, width, height);
}

GpStatus
WINGDIPAPI
GdipFillEllipseI(
    GpGraphics *graphics,
    GpBrush *fill,
    INT x,
    INT y,
    INT width,
    INT height
    )
{
    API_ENTRY(GdipFillEllipseI);
   return GdipFillEllipse(graphics, fill, TOREAL(x), TOREAL(y),
                          TOREAL(width), TOREAL(height));
}

GpStatus
WINGDIPAPI
GdipFillPie(
    GpGraphics *graphics,
    GpBrush *fill,
    REAL x,
    REAL y,
    REAL width,
    REAL height,
    REAL startAngle,
    REAL sweepAngle
    )
{
    API_ENTRY(GdipFillPie);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);
   CheckParameterValid(fill);
   CheckObjectBusy(fill);


   return graphics->FillPie(fill, x, y, width, height, startAngle, sweepAngle);
}

GpStatus
WINGDIPAPI
GdipFillPieI(
    GpGraphics *graphics,
    GpBrush *fill,
    INT x,
    INT y,
    INT width,
    INT height,
    REAL startAngle,
    REAL sweepAngle
    )
{
    API_ENTRY(GdipFillPieI);
   return GdipFillPie(graphics, fill, TOREAL(x), TOREAL(y),
                      TOREAL(width), TOREAL(height), startAngle, sweepAngle);
}

GpStatus
WINGDIPAPI
GdipFillPath(
    GpGraphics *graphics,
    GpBrush *fill,
    GpPath *path
    )
{
    API_ENTRY(GdipFillPath);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);
   CheckParameterValid(fill);
   CheckObjectBusy(fill);
   CheckParameterValid(path);
   CheckObjectBusy(path);


   return graphics->FillPath(fill, path);
}

GpStatus
WINGDIPAPI
GdipFillClosedCurve(
    GpGraphics *graphics,
    GpBrush *fill,
    GDIPCONST GpPointF *points,
    INT count
    )
{
    API_ENTRY(GdipFillClosedCurve);
   CheckParameter(points && count > 0);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);
   CheckParameterValid(fill);
   CheckObjectBusy(fill);


   return graphics->FillClosedCurve(fill, points, count);
}

GpStatus
WINGDIPAPI
GdipFillClosedCurveI(
    GpGraphics *graphics,
    GpBrush *fill,
    GDIPCONST GpPoint *points,
    INT count
    )
{
    API_ENTRY(GdipFillClosedCurveI);
   StackBuffer buffer;

   GpPointF *pointsF = (GpPointF*) buffer.GetBuffer(count*sizeof(GpPointF));

   if(!pointsF) return OutOfMemory;

   for (INT i=0; i<count; i++)
   {
       pointsF[i].X = TOREAL(points[i].X);
       pointsF[i].Y = TOREAL(points[i].Y);
   }

   GpStatus status = GdipFillClosedCurve(graphics, fill, pointsF, count);

   return status;
}

GpStatus
WINGDIPAPI
GdipFillClosedCurve2(
    GpGraphics *graphics,
    GpBrush *fill,
    GDIPCONST GpPointF *points,
    INT count,
    REAL tension,
    GpFillMode fillMode
    )
{
    API_ENTRY(GdipFillClosedCurve2);
   CheckParameter(points && count > 0);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);
   CheckParameterValid(fill);
   CheckObjectBusy(fill);


   return graphics->FillClosedCurve(fill, points, count, tension, fillMode);
}


GpStatus
WINGDIPAPI
GdipFillClosedCurve2I(
    GpGraphics *graphics,
    GpBrush *fill,
    GDIPCONST GpPoint *points,
    INT count,
    REAL tension,
    GpFillMode fillMode
    )
{
    API_ENTRY(GdipFillClosedCurve2I);
   StackBuffer buffer;

   GpPointF *pointsF = (GpPointF*) buffer.GetBuffer(count*sizeof(GpPointF));

   if(!pointsF) return OutOfMemory;

   for (INT i=0; i<count; i++)
   {
       pointsF[i].X = TOREAL(points[i].X);
       pointsF[i].Y = TOREAL(points[i].Y);
   }

   GpStatus status = GdipFillClosedCurve2(graphics, fill, pointsF, count, tension, fillMode);

   return status;
}

GpStatus
WINGDIPAPI
GdipFillRegion(
    GpGraphics *graphics,
    GpBrush *fill,
    GpRegion *region
    )
{
    API_ENTRY(GdipFillRegion);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);
   CheckParameterValid(fill);
   CheckObjectBusy(fill);
   CheckParameterValid(region);
   CheckObjectBusy(region);


   return graphics->FillRegion(fill, region);
}

GpStatus
WINGDIPAPI
GdipDrawString(
    GpGraphics               *graphics,
    GDIPCONST WCHAR          *string,
    INT                      length,
    GDIPCONST GpFont         *font,
    GDIPCONST RectF          *layoutRect,
    GDIPCONST GpStringFormat *stringFormat,
    GDIPCONST GpBrush        *brush
)
{
    API_ENTRY(GdipDrawString);
    CheckParameter(string && layoutRect);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    if (EmptyString(string, length))
    {
        return Ok;
    }


    GlobalTextLock lock;

    CheckParameterValid(font);
    CheckOptionalParameterValid(stringFormat);

    return graphics->DrawString(
        string,
        length,
        font,
        layoutRect,
        stringFormat,
        brush);
}

GpStatus
WINGDIPAPI
GdipMeasureString(
    GpGraphics               *graphics,
    GDIPCONST WCHAR          *string,
    INT                       length,
    GDIPCONST GpFont         *font,
    GDIPCONST RectF          *layoutRect,
    GDIPCONST GpStringFormat *stringFormat,
    RectF                    *boundingBox,
    INT                      *codepointsFitted,   // Optional parameter
    INT                      *linesFilled         // Optional parameter
)
{
    API_ENTRY(GdipMeasureString);
    CheckParameter(string && layoutRect && boundingBox);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);

    if (EmptyString(string, length))
    {
        SetEmptyRectF(boundingBox);
        if (codepointsFitted)
            *codepointsFitted = 0;
        if (linesFilled)
            *linesFilled = 0;
        return Ok;
    }


    GlobalTextLock lock;

    CheckParameterValid(font);
    CheckOptionalParameterValid(stringFormat);

    return graphics->MeasureString(
        string,
        length,
        font,
        layoutRect,
        stringFormat,
        boundingBox,
        codepointsFitted,
        linesFilled
    );
}

GpStatus
WINGDIPAPI
GdipMeasureCharacterRanges(
    GpGraphics               *graphics,
    GDIPCONST WCHAR          *string,
    INT                       length,
    GDIPCONST GpFont         *font,
    GDIPCONST RectF          &layoutRect,
    GDIPCONST GpStringFormat *stringFormat,
    INT                       regionCount,
    GpRegion                **regions
)
{
    API_ENTRY(GdipMeasureCharacterRanges);
    CheckParameter(string && (&layoutRect));
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);
    CheckParameterValid(font);
    CheckOptionalParameterValid(stringFormat);
    CheckParameter(regions);

    if (EmptyString(string, length))
    {
        return InvalidParameter;
    }


    GlobalTextLock lock;

    GpStatus status = graphics->MeasureCharacterRanges(
        string,
        length,
        font,
        layoutRect,
        stringFormat,
        regionCount,
        regions
    );

    return status;
}


GpStatus
WINGDIPAPI
GdipDrawDriverString(
    GpGraphics *graphics,
    GDIPCONST UINT16 *text,
    INT length,
    GDIPCONST GpFont *font,
    GDIPCONST GpBrush *brush,
    GDIPCONST PointF *positions,
    INT flags,
    GDIPCONST GpMatrix *matrix
)
{
    API_ENTRY(GdipDrawDriverString);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);
    CheckOptionalParameterValid(matrix);
    CheckOptionalObjectBusy(matrix);
    CheckParameterValid(brush);
    CheckObjectBusy(brush);

    CheckParameter(text && positions);

    if (length == -1 && !(flags & DriverStringOptionsCmapLookup))
    {
        return InvalidParameter;
    }
    if (EmptyString(text, length))
    {
        return Ok;
    }


    GlobalTextLock lock;

    CheckParameterValid(font);

    return graphics->DrawDriverString(
        text,
        length,
        font,
        brush,
        positions,
        flags,
        matrix
    );
}

GpStatus
WINGDIPAPI
GdipMeasureDriverString(
    GpGraphics *graphics,
    GDIPCONST UINT16 *text,
    INT length,
    GDIPCONST GpFont *font,
    GDIPCONST PointF *positions,
    INT flags,
    GDIPCONST GpMatrix *matrix,
    RectF *boundingBox
)
{
    API_ENTRY(GdipMeasureDriverString);
    GpStatus status;
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);
    CheckOptionalParameterValid(matrix);
    CheckOptionalObjectBusy(matrix);

    CheckParameter(text && positions && boundingBox);

    if (length == -1 && !(flags & DriverStringOptionsCmapLookup))
    {
        return InvalidParameter;
    }

    if (EmptyString(text, length))
    {
        SetEmptyRectF(boundingBox);
        return Ok;
    }


    GlobalTextLock lock;

    CheckParameterValid(font);

    return graphics->MeasureDriverString(
        text,
        length,
        font,
        positions,
        flags,
        matrix,
        boundingBox
    );
}

GpStatus
WINGDIPAPI
GdipGetFamilyName(
    GDIPCONST GpFontFamily *family,
    WCHAR                   name[LF_FACESIZE],
    LANGID                  language
)
{
    API_ENTRY(GdipGetFamilyName);

    GlobalTextLock lock;

    CheckParameterValid(family);

    return family->GetFamilyName(name, language);
}

/// end font/text stuff - move to end



GpStatus
WINGDIPAPI
GdipDrawImage(
    GpGraphics *graphics,
    GpImage *image,
    REAL x,
    REAL y
    )
{
    API_ENTRY(GdipDrawImage);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);
   CheckParameterValid(image);
   CheckObjectBusy(image);


   return graphics->DrawImage(image, x, y);
}


GpStatus
WINGDIPAPI
GdipDrawImageI(
    GpGraphics *graphics,
    GpImage *image,
    INT x,
    INT y
    )
{
    API_ENTRY(GdipDrawImageI);
   return GdipDrawImage(graphics, image, TOREAL(x), TOREAL(y));
}

GpStatus
WINGDIPAPI
GdipDrawImageRect(
    GpGraphics *graphics,
    GpImage *image,
    REAL x,
    REAL y,
    REAL width,
    REAL height
    )
{
    API_ENTRY(GdipDrawImageRect);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);
   CheckParameterValid(image);
   CheckObjectBusy(image);


   return graphics->DrawImage(image, x, y, width, height);
}


GpStatus
WINGDIPAPI
GdipDrawImageRectI(
    GpGraphics *graphics,
    GpImage *image,
    INT x,
    INT y,
    INT width,
    INT height
    )
{
    API_ENTRY(GdipDrawImageRectI);
   return GdipDrawImageRect(graphics, image, TOREAL(x), TOREAL(y), TOREAL(width), TOREAL(height));
}

GpStatus
WINGDIPAPI
GdipDrawImagePoints(
    GpGraphics *graphics,
    GpImage *image,
    GDIPCONST GpPointF *points,
    INT count
    )
{
    API_ENTRY(GdipDrawImagePoints);
   CheckParameter(points && count > 0);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);
   CheckParameterValid(image);
   CheckObjectBusy(image);


   return graphics->DrawImage(image, points, count);
}

GpStatus
WINGDIPAPI
GdipDrawImagePointsI(
    GpGraphics *graphics,
    GpImage *image,
    GDIPCONST GpPoint *points,
    INT count
    )
{
    API_ENTRY(GdipDrawImagePointsI);
    CheckParameter(points && count > 0);

    StackBuffer buffer;

    GpPointF *pointsF = (GpPointF*) buffer.GetBuffer(count*sizeof(GpPointF));

    if(!pointsF) return OutOfMemory;

    for (INT i=0; i<count; i++)
    {
        pointsF[i].X = TOREAL(points[i].X);
        pointsF[i].Y = TOREAL(points[i].Y);
    } 

    GpStatus status = GdipDrawImagePoints(graphics, image, pointsF, count);

    return status;
}

GpStatus
WINGDIPAPI
GdipDrawImagePointRect(
    GpGraphics *graphics,
    GpImage *image,
    REAL x,
    REAL y,
    REAL srcx,
    REAL srcy,
    REAL srcwidth,
    REAL srcheight,
    GpPageUnit srcUnit
    )
{
    API_ENTRY(GdipDrawImagePointRect);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);
    CheckParameterValid(image);
    CheckObjectBusy(image);
    CheckParameter(SrcUnitIsValid((Unit)srcUnit));
    
    // This additional check is required because we don't support the other 
    // source units in drawimage yet. RAID 311474
    
    if(UnitPixel != srcUnit)
    {
        return NotImplemented;
    }

    GpRectF srcRect(srcx, srcy, srcwidth, srcheight);


    return graphics->DrawImage(image, x, y, srcRect, srcUnit);
}

GpStatus
WINGDIPAPI
GdipDrawImagePointRectI(
    GpGraphics *graphics,
    GpImage *image,
    INT x,
    INT y,
    INT srcx,
    INT srcy,
    INT srcwidth,
    INT srcheight,
    GpPageUnit srcUnit
    )
{
    API_ENTRY(GdipDrawImagePointRectI);
   return GdipDrawImagePointRect(graphics, image, TOREAL(x), TOREAL(y), TOREAL(srcx),
                                 TOREAL(srcy), TOREAL(srcwidth), TOREAL(srcheight),
                                 srcUnit);
}

GpStatus
WINGDIPAPI
GdipDrawImageRectRect(
    GpGraphics *graphics,
    GpImage *image,
    REAL dstx,
    REAL dsty,
    REAL dstwidth,
    REAL dstheight,
    REAL srcx,
    REAL srcy,
    REAL srcwidth,
    REAL srcheight,
    GpPageUnit srcUnit,
    GDIPCONST GpImageAttributes* imageAttributes,
    DrawImageAbort callback,
    VOID * callbackData
    )
{
    API_ENTRY(GdipDrawImageRectRect);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);
    CheckParameterValid(image);
    CheckObjectBusy(image);
    CheckParameter(SrcUnitIsValid((Unit)srcUnit));
    
    // This additional check is required because we don't support the other 
    // source units in drawimage yet. RAID 311474
    
    if(UnitPixel != srcUnit)
    {
        return NotImplemented;
    }
    
    CheckOptionalParameterValid(imageAttributes);
    CheckOptionalObjectBusy(imageAttributes);

    GpRectF srcrect(srcx, srcy, srcwidth, srcheight);
    GpRectF destrect(dstx, dsty, dstwidth, dstheight);


    return graphics->DrawImage(image, destrect, srcrect, srcUnit,
                               imageAttributes, callback, callbackData);
}

GpStatus
WINGDIPAPI
GdipDrawImageRectRectI(
    GpGraphics *graphics,
    GpImage *image,
    INT dstx,
    INT dsty,
    INT dstwidth,
    INT dstheight,
    INT srcx,
    INT srcy,
    INT srcwidth,
    INT srcheight,
    GpPageUnit srcUnit,
    GDIPCONST GpImageAttributes* imageAttributes,
    DrawImageAbort callback,
    VOID * callbackData
    )
{
    API_ENTRY(GdipDrawImageRectRectI);
   return GdipDrawImageRectRect(graphics, image, TOREAL(dstx), TOREAL(dsty),
                                TOREAL(dstwidth), TOREAL(dstheight),
                                TOREAL(srcx), TOREAL(srcy), TOREAL(srcwidth),
                                TOREAL(srcheight), srcUnit,
                                imageAttributes, callback, callbackData);
}

GpStatus
WINGDIPAPI
GdipDrawImagePointsRect(
    GpGraphics *graphics,
    GpImage *image,
    GDIPCONST GpPointF *points,
    INT count,
    REAL srcx,
    REAL srcy,
    REAL srcwidth,
    REAL srcheight,
    GpPageUnit srcUnit,
    GDIPCONST GpImageAttributes* imageAttributes,
    DrawImageAbort callback,
    VOID * callbackData
    )
{
    API_ENTRY(GdipDrawImagePointsRect);
    CheckParameter(points && count > 0);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);
    CheckParameterValid(image);
    CheckObjectBusy(image);
    CheckParameter(SrcUnitIsValid((Unit)srcUnit));
    
    // This additional check is required because we don't support the other 
    // source units in drawimage yet. RAID 311474
    
    if(UnitPixel != srcUnit)
    {
        return NotImplemented;
    }
    
    CheckOptionalParameterValid(imageAttributes);
    CheckOptionalObjectBusy(imageAttributes);

    GpRectF srcrect(srcx, srcy, srcwidth, srcheight);


    return graphics->DrawImage(image, points, count, srcrect, srcUnit,
                               imageAttributes, callback, callbackData);
}

GpStatus
WINGDIPAPI
GdipDrawImagePointsRectI(
    GpGraphics *graphics,
    GpImage *image,
    GDIPCONST GpPoint *points,
    INT count,
    INT srcx,
    INT srcy,
    INT srcwidth,
    INT srcheight,
    GpPageUnit srcUnit,
    GDIPCONST GpImageAttributes* imageAttributes,
    DrawImageAbort callback,
    VOID * callbackData
    )
{
    API_ENTRY(GdipDrawImagePointsRectI);
    CheckParameter(points && count > 0);
   
    StackBuffer buffer;

    GpPointF *pointsF = (GpPointF*) buffer.GetBuffer(count*sizeof(GpPointF));

    if(!pointsF) return OutOfMemory;

    for (INT i=0; i<count; i++)
    {
        pointsF[i].X = TOREAL(points[i].X);
        pointsF[i].Y = TOREAL(points[i].Y);
    }

    GpStatus status = GdipDrawImagePointsRect(
        graphics, 
        image, 
        pointsF, 
        count,
        TOREAL(srcx), 
        TOREAL(srcy), 
        TOREAL(srcwidth),
        TOREAL(srcheight), 
        srcUnit,
        imageAttributes, 
        callback, 
        callbackData
    );

    return status;
}

GpStatus WINGDIPAPI
GdipEnumerateMetafileDestPoint(
    GpGraphics *            graphics,
    GDIPCONST GpMetafile *  metafile,
    GDIPCONST PointF &      destPoint,
    EnumerateMetafileProc   callback,
    VOID *                  callbackData,
    GDIPCONST GpImageAttributes *     imageAttributes
    )
{
    API_ENTRY(GdipEnumerateMetafileDestPoint);
    CheckParameter(callback);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);
    CheckParameterValid(metafile);
    CheckObjectBusy(metafile);
    CheckOptionalParameterValid(imageAttributes);
    CheckOptionalObjectBusy(imageAttributes);


    // Only the current thread can play back records from this enumeration
    metafile->SetThreadId(GetCurrentThreadId());
    return graphics->EnumerateMetafile(
                        metafile,
                        destPoint,
                        callback,
                        callbackData,
                        imageAttributes
                        );
    metafile->SetThreadId(0);
}

GpStatus WINGDIPAPI
GdipEnumerateMetafileDestPointI(
    GpGraphics *            graphics,
    GDIPCONST GpMetafile *  metafile,
    GDIPCONST Point &       destPoint,
    EnumerateMetafileProc   callback,
    VOID *                  callbackData,
    GDIPCONST GpImageAttributes *     imageAttributes
    )
{
    API_ENTRY(GdipEnumerateMetafileDestPointI);
    GpPointF    destPointF(TOREAL(destPoint.X), TOREAL(destPoint.Y));

    return GdipEnumerateMetafileDestPoint(
                graphics,
                metafile,
                destPointF,
                callback,
                callbackData,
                imageAttributes
                );
}

GpStatus WINGDIPAPI
GdipEnumerateMetafileDestRect(
    GpGraphics *            graphics,
    GDIPCONST GpMetafile *  metafile,
    GDIPCONST RectF &       destRect,
    EnumerateMetafileProc   callback,
    VOID *                  callbackData,
    GDIPCONST GpImageAttributes *     imageAttributes
    )
{
    API_ENTRY(GdipEnumerateMetafileDestRect);
    CheckParameter(callback);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);
    CheckParameterValid(metafile);
    CheckObjectBusy(metafile);
    CheckOptionalParameterValid(imageAttributes);
    CheckOptionalObjectBusy(imageAttributes);


    // Only the current thread can play back records from this enumeration
    metafile->SetThreadId(GetCurrentThreadId());
    return graphics->EnumerateMetafile(
                        metafile,
                        destRect,
                        callback,
                        callbackData,
                        imageAttributes
                        );
    metafile->SetThreadId(0);
}

GpStatus WINGDIPAPI
GdipEnumerateMetafileDestRectI(
    GpGraphics *            graphics,
    GDIPCONST GpMetafile *  metafile,
    GDIPCONST Rect &        destRect,
    EnumerateMetafileProc   callback,
    VOID *                  callbackData,
    GDIPCONST GpImageAttributes *     imageAttributes
    )
{
    API_ENTRY(GdipEnumerateMetafileDestRectI);
    GpRectF     destRectF(TOREAL(destRect.X), TOREAL(destRect.Y),
                          TOREAL(destRect.Width), TOREAL(destRect.Height));

    return GdipEnumerateMetafileDestRect(
                graphics,
                metafile,
                destRectF,
                callback,
                callbackData,
                imageAttributes
                );
}

GpStatus WINGDIPAPI
GdipEnumerateMetafileDestPoints(
    GpGraphics *            graphics,
    GDIPCONST GpMetafile *  metafile,
    GDIPCONST PointF *      destPoints,
    INT                     count,
    EnumerateMetafileProc   callback,
    VOID *                  callbackData,
    GDIPCONST GpImageAttributes *     imageAttributes
    )
{
    API_ENTRY(GdipEnumerateMetafileDestPoints);
    CheckParameter(callback);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);
    CheckParameterValid(metafile);
    CheckObjectBusy(metafile);
    CheckOptionalParameterValid(imageAttributes);
    CheckOptionalObjectBusy(imageAttributes);


    // Only the current thread can play back records from this enumeration
    metafile->SetThreadId(GetCurrentThreadId());
    return graphics->EnumerateMetafile(
                        metafile,
                        destPoints,
                        count,
                        callback,
                        callbackData,
                        imageAttributes
                        );
    metafile->SetThreadId(0);
}

GpStatus WINGDIPAPI
GdipEnumerateMetafileDestPointsI(
    GpGraphics *            graphics,
    GDIPCONST GpMetafile *      metafile,
    GDIPCONST Point *           destPoints,
    INT                     count,
    EnumerateMetafileProc   callback,
    VOID *                  callbackData,
    GDIPCONST GpImageAttributes *     imageAttributes
    )
{
    API_ENTRY(GdipEnumerateMetafileDestPointsI);
    CheckParameter(destPoints && (count > 0));

    StackBuffer buffer;

    GpPointF *destPointsF = (GpPointF*) buffer.GetBuffer(count*sizeof(GpPointF));

    if(!destPointsF) return OutOfMemory;

    for (INT i = 0; i < count; i++)
    {
        destPointsF[i].X = TOREAL(destPoints[i].X);
        destPointsF[i].Y = TOREAL(destPoints[i].Y);
    }

    return GdipEnumerateMetafileDestPoints(
                graphics,
                metafile,
                destPointsF,
                count,
                callback,
                callbackData,
                imageAttributes
                );
}

GpStatus WINGDIPAPI
GdipEnumerateMetafileSrcRectDestPoint(
    GpGraphics *            graphics,
    GDIPCONST GpMetafile *  metafile,
    GDIPCONST PointF &      destPoint,
    GDIPCONST RectF &       srcRect,
    Unit                    srcUnit,
    EnumerateMetafileProc   callback,
    VOID *                  callbackData,
    GDIPCONST GpImageAttributes *     imageAttributes
    )
{
    API_ENTRY(GdipEnumerateMetafileSrcRectDestPoint);
    CheckParameter(callback);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);
    CheckParameterValid(metafile);
    CheckObjectBusy(metafile);
    CheckParameter(SrcUnitIsValid((Unit)srcUnit));
    CheckOptionalParameterValid(imageAttributes);
    CheckOptionalObjectBusy(imageAttributes);


    // Only the current thread can play back records from this enumeration
    metafile->SetThreadId(GetCurrentThreadId());
    return graphics->EnumerateMetafile(
                        metafile,
                        destPoint,
                        srcRect,
                        srcUnit,
                        callback,
                        callbackData,
                        imageAttributes
                        );
    metafile->SetThreadId(0);
}

GpStatus WINGDIPAPI
GdipEnumerateMetafileSrcRectDestPointI(
    GpGraphics *            graphics,
    GDIPCONST GpMetafile *  metafile,
    GDIPCONST Point &       destPoint,
    GDIPCONST Rect &        srcRect,
    Unit                    srcUnit,
    EnumerateMetafileProc   callback,
    VOID *                  callbackData,
    GDIPCONST GpImageAttributes *     imageAttributes
    )
{
    API_ENTRY(GdipEnumerateMetafileSrcRectDestPointI);
    GpPointF    destPointF(TOREAL(destPoint.X), TOREAL(destPoint.Y));
    GpRectF     srcRectF(TOREAL(srcRect.X), TOREAL(srcRect.Y),
                         TOREAL(srcRect.Width), TOREAL(srcRect.Height));

    return GdipEnumerateMetafileSrcRectDestPoint(
                graphics,
                metafile,
                destPointF,
                srcRectF,
                srcUnit,
                callback,
                callbackData,
                imageAttributes
                );
}

GpStatus WINGDIPAPI
GdipEnumerateMetafileSrcRectDestRect(
    GpGraphics *            graphics,
    GDIPCONST GpMetafile *  metafile,
    GDIPCONST RectF &       destRect,
    GDIPCONST RectF &       srcRect,
    Unit                    srcUnit,
    EnumerateMetafileProc   callback,
    VOID *                  callbackData,
    GDIPCONST GpImageAttributes *     imageAttributes
    )
{
    API_ENTRY(GdipEnumerateMetafileSrcRectDestRect);
    CheckParameter(callback);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);
    CheckParameterValid(metafile);
    CheckObjectBusy(metafile);
    CheckParameter(SrcUnitIsValid((Unit)srcUnit));
    CheckOptionalParameterValid(imageAttributes);
    CheckOptionalObjectBusy(imageAttributes);


    // Only the current thread can play back records from this enumeration
    metafile->SetThreadId(GetCurrentThreadId());
    return graphics->EnumerateMetafile(
                        metafile,
                        destRect,
                        srcRect,
                        srcUnit,
                        callback,
                        callbackData,
                        imageAttributes
                        );
    metafile->SetThreadId(0);
}

GpStatus WINGDIPAPI
GdipEnumerateMetafileSrcRectDestRectI(
    GpGraphics *            graphics,
    GDIPCONST GpMetafile *  metafile,
    GDIPCONST Rect &        destRect,
    GDIPCONST Rect &        srcRect,
    Unit                    srcUnit,
    EnumerateMetafileProc   callback,
    VOID *                  callbackData,
    GDIPCONST GpImageAttributes *     imageAttributes
    )
{
    API_ENTRY(GdipEnumerateMetafileSrcRectDestRectI);
    GpRectF     destRectF(TOREAL(destRect.X), TOREAL(destRect.Y),
                          TOREAL(destRect.Width), TOREAL(destRect.Height));
    GpRectF     srcRectF(TOREAL(srcRect.X), TOREAL(srcRect.Y),
                         TOREAL(srcRect.Width), TOREAL(srcRect.Height));

    return GdipEnumerateMetafileSrcRectDestRect(
                graphics,
                metafile,
                destRectF,
                srcRectF,
                srcUnit,
                callback,
                callbackData,
                imageAttributes
                );
}

GpStatus WINGDIPAPI
GdipEnumerateMetafileSrcRectDestPoints(
    GpGraphics *            graphics,
    GDIPCONST GpMetafile *  metafile,
    GDIPCONST PointF *      destPoints,
    INT                     count,
    GDIPCONST RectF &       srcRect,
    Unit                    srcUnit,
    EnumerateMetafileProc   callback,
    VOID *                  callbackData,
    GDIPCONST GpImageAttributes *     imageAttributes
    )
{
    API_ENTRY(GdipEnumerateMetafileSrcRectDestPoints);
    CheckParameter(callback);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);
    CheckParameterValid(metafile);
    CheckObjectBusy(metafile);
    CheckParameter(SrcUnitIsValid((Unit)srcUnit));
    CheckOptionalParameterValid(imageAttributes);
    CheckOptionalObjectBusy(imageAttributes);


    // Only the current thread can play back records from this enumeration
    metafile->SetThreadId(GetCurrentThreadId());
    return graphics->EnumerateMetafile(
                        metafile,
                        destPoints,
                        count,
                        srcRect,
                        srcUnit,
                        callback,
                        callbackData,
                        imageAttributes
                        );
    metafile->SetThreadId(0);
}

GpStatus WINGDIPAPI
GdipEnumerateMetafileSrcRectDestPointsI(
    GpGraphics *            graphics,
    GDIPCONST GpMetafile *  metafile,
    GDIPCONST Point *       destPoints,
    INT                     count,
    GDIPCONST Rect &        srcRect,
    Unit                    srcUnit,
    EnumerateMetafileProc   callback,
    VOID *                  callbackData,
    GDIPCONST GpImageAttributes *     imageAttributes
    )
{
    API_ENTRY(GdipEnumerateMetafileSrcRectDestPointsI);
    CheckParameter(destPoints && (count > 0));

    GpRectF     srcRectF(TOREAL(srcRect.X), TOREAL(srcRect.Y),
                         TOREAL(srcRect.Width), TOREAL(srcRect.Height));

    StackBuffer buffer;

    GpPointF *destPointsF = (GpPointF*) buffer.GetBuffer(count*sizeof(GpPointF));

    if(!destPointsF) return OutOfMemory;

    for (INT i = 0; i < count; i++)
    {
        destPointsF[i].X = TOREAL(destPoints[i].X);
        destPointsF[i].Y = TOREAL(destPoints[i].Y);
    }

    return GdipEnumerateMetafileSrcRectDestPoints(
                graphics,
                metafile,
                destPointsF,
                count,
                srcRectF,
                srcUnit,
                callback,
                callbackData,
                imageAttributes
                );
}

GpStatus
WINGDIPAPI
GdipPlayMetafileRecord(
    GDIPCONST GpMetafile *  metafile,
    EmfPlusRecordType       recordType,
    UINT                    flags,
    UINT                    dataSize,
    GDIPCONST BYTE *        data
    )
{
    API_ENTRY(GdipPlayMetafileRecord);
    CheckParameterValid(metafile);
    CheckParameter(recordType);

    // The metafile must be already locked by the enumerator
    GpLock lockMetafile (metafile->GetObjectLock());
    if (lockMetafile.IsValid())
    {
        return InvalidParameter;
    }

    // Only the current thread can play back records from this enumeration
    if (GetCurrentThreadId() != metafile->GetThreadId())
    {
        return ObjectBusy;
    }


    return metafile->PlayRecord(recordType, flags, dataSize, data);
}

GpStatus
WINGDIPAPI
GdipSetClipGraphics(
    GpGraphics *    graphics,
    GpGraphics *    srcgraphics,
    CombineMode     combineMode
    )
{
    API_ENTRY(GdipSetClipGraphics);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);
    CheckParameterValid(srcgraphics);
    CheckObjectBusy(srcgraphics);
    CheckParameter(CombineModeIsValid(combineMode));


    return graphics->SetClip(srcgraphics, combineMode);
}

GpStatus
WINGDIPAPI
GdipSetClipRect(
    GpGraphics *    graphics,
    REAL            x,
    REAL            y,
    REAL            width,
    REAL            height,
    CombineMode     combineMode
    )
{
    API_ENTRY(GdipSetClipRect);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);
    CheckParameter(CombineModeIsValid(combineMode));

    GpRectF rect(x, y, width, height);


    return graphics->SetClip(rect, combineMode);
}

GpStatus
WINGDIPAPI
GdipSetClipRectI(
    GpGraphics *    graphics,
    INT             x,
    INT             y,
    INT             width,
    INT             height,
    CombineMode     combineMode
    )
{
    API_ENTRY(GdipSetClipRectI);
    return GdipSetClipRect(graphics, TOREAL(x), TOREAL(y),
                           TOREAL(width), TOREAL(height), combineMode);
}

GpStatus
WINGDIPAPI
GdipSetClipPath(
    GpGraphics *    graphics,
    GpPath *        path,
    CombineMode     combineMode
    )
{
    API_ENTRY(GdipSetClipPath);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);
    CheckParameterValid(path);
    CheckObjectBusy(path);
    CheckParameter(CombineModeIsValid(combineMode));


    return graphics->SetClip(path, combineMode);
}

GpStatus
WINGDIPAPI
GdipSetClipRegion(
    GpGraphics *    graphics,
    GpRegion *      region,
    CombineMode     combineMode
    )
{
    API_ENTRY(GdipSetClipRegion);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);
    CheckParameterValid(region);
    CheckObjectBusy(region);
    CheckParameter(CombineModeIsValid(combineMode));


    return graphics->SetClip(region, combineMode);
}

GpStatus
WINGDIPAPI
GdipSetClipHrgn(
    GpGraphics *    graphics,
    HRGN            hRgn,
    CombineMode     combineMode
    )
{
    API_ENTRY(GdipSetClipHrgn);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);
    CheckParameter(hRgn && (GetObjectTypeInternal(hRgn) == OBJ_REGION));
    CheckParameter(CombineModeIsValid(combineMode));


    return graphics->SetClip(hRgn, combineMode);
}

GpStatus
WINGDIPAPI
GdipResetClip(
    GpGraphics *graphics
    )
{
    API_ENTRY(GdipResetClip);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);

    return graphics->ResetClip();
}

GpStatus
WINGDIPAPI
GdipTranslateClip(
    GpGraphics *graphics,
    REAL dx,
    REAL dy
    )
{
    API_ENTRY(GdipTranslateClip);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);


    return graphics->OffsetClip(dx, dy);
}

GpStatus
WINGDIPAPI
GdipTranslateClipI(
    GpGraphics *graphics,
    INT dx,
    INT dy
    )
{
    API_ENTRY(GdipTranslateClipI);
    return GdipTranslateClip(graphics, TOREAL(dx), TOREAL(dy));
}

GpStatus
WINGDIPAPI
GdipGetClip(
    GpGraphics *graphics,
    GpRegion *region
    )
{
    API_ENTRY(GdipGetClip);
    CheckParameter(region);
    CheckObjectBusy(region);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);

    return graphics->GetClip(region);
}

GpStatus
WINGDIPAPI
GdipGetClipBounds(
    GpGraphics *graphics,
    GpRectF *rect
    )
{
    API_ENTRY(GdipGetClipBounds);
    CheckParameter(rect);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);

    graphics->GetClipBounds(*rect);

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetClipBoundsI(
    GpGraphics *graphics,
    GpRect *rect
    )
{
    API_ENTRY(GdipGetClipBoundsI);
    CheckParameter(rect);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);

    RectF rectf;

    graphics->GetClipBounds(rectf);

    rect->X = GpRound(rectf.X);
    rect->Y = GpRound(rectf.Y);
    rect->Width = GpRound(rectf.Width);
    rect->Height = GpRound(rectf.Height);

    return Ok;
}

GpStatus
WINGDIPAPI
GdipIsClipEmpty(
    GpGraphics *graphics,
    BOOL *result
    )
{
    API_ENTRY(GdipIsClipEmpty);
    CheckParameter(result);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);

    *result = graphics->IsClipEmpty();

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetVisibleClipBounds(
    GpGraphics *graphics,
    GpRectF *rect
    )
{
    API_ENTRY(GdipGetVisibleClipBounds);
    CheckParameter(rect);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);

    graphics->GetVisibleClipBounds(*rect);

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetVisibleClipBoundsI(
    GpGraphics *graphics,
    GpRect *rect
    )
{
    API_ENTRY(GdipGetVisibleClipBoundsI);
    CheckParameter(rect);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);

    RectF rectf;

    graphics->GetVisibleClipBounds(rectf);

    rect->X = GpRound(rectf.X);
    rect->Y = GpRound(rectf.Y);
    rect->Width = GpRound(rectf.Width);
    rect->Height = GpRound(rectf.Height);

    return Ok;
}

GpStatus
WINGDIPAPI
GdipIsVisibleClipEmpty(
    GpGraphics *graphics,
    BOOL *result
    )
{
    API_ENTRY(GdipIsVisibleClipEmpty);
    CheckParameter(result);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);

    *result = graphics->IsVisibleClipEmpty();

    return Ok;
}

GpStatus
WINGDIPAPI
GdipIsVisiblePoint(
    GpGraphics *graphics,
    REAL x,
    REAL y,
    BOOL *result
    )
{
    API_ENTRY(GdipIsVisiblePoint);
    CheckParameter(result);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);

    GpPointF pt(x,y);


    *result = graphics->IsVisible(pt);

    return Ok;
}

GpStatus
WINGDIPAPI
GdipIsVisiblePointI(
    GpGraphics *graphics,
    INT x,
    INT y,
    BOOL *result
    )
{
    API_ENTRY(GdipIsVisiblePointI);
    return GdipIsVisiblePoint(graphics, TOREAL(x), TOREAL(y), result);
}

GpStatus
WINGDIPAPI
GdipIsVisibleRect(
    GpGraphics *graphics,
    REAL x,
    REAL y,
    REAL width,
    REAL height,
    BOOL *result
    )
{
    API_ENTRY(GdipIsVisibleRect);
    CheckParameter(result);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);

    GpRectF rect(x, y, width, height);


    *result = graphics->IsVisible(rect);

    return Ok;
}

GpStatus
WINGDIPAPI
GdipIsVisibleRectI(
    GpGraphics *graphics,
    INT x,
    INT y,
    INT width,
    INT height,
    BOOL *result
    )
{
    API_ENTRY(GdipIsVisibleRectI);
    return GdipIsVisibleRect(graphics, TOREAL(x), TOREAL(y),
                             TOREAL(width), TOREAL(height), result);
}

GpStatus
WINGDIPAPI
GdipSaveGraphics(
    GpGraphics *graphics,
    GraphicsState *state
    )
{
    API_ENTRY(GdipSaveGraphics);
    CheckParameter(state);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);

    *state = (GraphicsState)graphics->Save();

    return Ok;
}

GpStatus
WINGDIPAPI
GdipRestoreGraphics(
    GpGraphics *graphics,
    GraphicsState state
    )
{
    API_ENTRY(GdipRestoreGraphics);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);

   graphics->Restore((INT)state);

   return Ok;
}


GpStatus
WINGDIPAPI
GdipBeginContainer(
    GpGraphics *graphics,
    GDIPCONST GpRectF *dstrect,
    GDIPCONST GpRectF *srcrect,
    GpPageUnit unit,
    GraphicsContainer *state
    )
{
    API_ENTRY(GdipBeginContainer);
    CheckParameter(state && dstrect && srcrect);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);
    CheckParameter(SrcUnitIsValid((Unit)unit));


    *state = (GraphicsContainer)graphics->BeginContainer(*dstrect, *srcrect, unit);

    return Ok;
}

GpStatus
WINGDIPAPI
GdipBeginContainer2(
    GpGraphics *graphics,
    GraphicsContainer *state
    )
{
    API_ENTRY(GdipBeginContainer2);
   CheckParameter(state);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);


   *state = (GraphicsContainer)graphics->BeginContainer();

   return Ok;
}

GpStatus
WINGDIPAPI
GdipBeginContainerI(
    GpGraphics *graphics,
    GDIPCONST GpRect *dstrect,
    GDIPCONST GpRect *srcrect,
    GpPageUnit unit,
    GraphicsContainer *state
    )
{
    API_ENTRY(GdipBeginContainerI);
   GpRectF dstrectF(TOREAL(dstrect->X), TOREAL(dstrect->Y), TOREAL(dstrect->Width), TOREAL(dstrect->Height));
   GpRectF srcrectF(TOREAL(srcrect->X), TOREAL(srcrect->Y), TOREAL(srcrect->Width), TOREAL(srcrect->Height));

   return GdipBeginContainer(graphics, &dstrectF, &srcrectF, unit, state);
}

GpStatus
WINGDIPAPI
GdipEndContainer(
    GpGraphics *graphics,
    GraphicsContainer state
    )
{
    API_ENTRY(GdipEndContainer);
   CheckParameterValid(graphics);
   CheckObjectBusy(graphics);

   graphics->EndContainer((INT)state);

   return Ok;
}

GpStatus
WINGDIPAPI
GdipGetMetafileHeaderFromWmf(
    HMETAFILE           hWmf,
    GDIPCONST WmfPlaceableFileHeader *     wmfPlaceableFileHeader,
    MetafileHeader *    header
    )
{
    API_ENTRY(GdipGetMetafileHeaderFromWmf);
    CheckParameter(hWmf && wmfPlaceableFileHeader);


    return GetMetafileHeader(hWmf, wmfPlaceableFileHeader, *header);
}

GpStatus
WINGDIPAPI
GdipGetMetafileHeaderFromEmf(
    HENHMETAFILE        hEmf,
    MetafileHeader *    header
    )
{
    API_ENTRY(GdipGetMetafileHeaderFromEmf);
    CheckParameter(hEmf);


    return GetMetafileHeader(hEmf, *header);
}

GpStatus
WINGDIPAPI
GdipGetMetafileHeaderFromFile(
    GDIPCONST WCHAR*        filename,
    MetafileHeader *    header
    )
{
    API_ENTRY(GdipGetMetafileHeaderFromFile);
    CheckParameter(filename);


    return GetMetafileHeader(filename, *header);
}

GpStatus
WINGDIPAPI
GdipGetMetafileHeaderFromStream(
    IStream *           stream,
    MetafileHeader *    header
    )
{
    API_ENTRY(GdipGetMetafileHeaderFromStream);
    CheckParameter(stream);


    return GetMetafileHeader(stream, *header);
}

GpStatus
WINGDIPAPI
GdipGetMetafileHeaderFromMetafile(
    GpMetafile *        metafile,
    MetafileHeader *    header
    )
{
    API_ENTRY(GdipGetMetafileHeaderFromMetafile);
    CheckParameterValid(metafile);
    CheckObjectBusy(metafile);


    metafile->GetHeader(*header);

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetHemfFromMetafile(
    GpMetafile *        metafile,
    HENHMETAFILE *      hEmf
    )
{
    API_ENTRY(GdipGetHemfFromMetafile);
    CheckParameter(hEmf);

    *hEmf = NULL;   // init to NULL in case the metafile is busy or invalid (Windows bug 216941)

    CheckParameterValid(metafile);
    CheckObjectBusy(metafile);

    return metafile->GetHemf(hEmf);
}

GpStatus
WINGDIPAPI
GdipCreateStreamOnFile(
    GDIPCONST WCHAR *   filename,
    UINT            access,     // GENERIC_READ and/or GENERIC_WRITE
    IStream **      stream
    )
{
    // We don't support this API anymore, simply return NotImplemented
    /*
    API_ENTRY(GdipCreateStreamOnFile);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(filename && stream);
    CheckParameter((access & (GENERIC_READ | GENERIC_WRITE)) != 0);

    if ((*stream = CreateStreamOnFile(filename, access)) != NULL)
    {
        return Ok;
    }
    return GenericError;
    */
    return NotImplemented;
}
GpStatus
WINGDIPAPI
GdipCreateMetafileFromWmf(
    HMETAFILE       hWmf,
    BOOL            deleteWmf,
    GDIPCONST WmfPlaceableFileHeader * wmfPlaceableFileHeader,  // can be NULL
    GpMetafile **   metafile
    )
{
    API_ENTRY(GdipCreateMetafileFromWmf);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(hWmf && metafile);


    *metafile = new GpMetafile(hWmf, wmfPlaceableFileHeader, deleteWmf);

    if (*metafile)
    {
        if ((*metafile)->IsValid())
        {
            return Ok;
        }
        (*metafile)->Dispose();
        *metafile = NULL;
    }
    return GenericError;
}

GpStatus
WINGDIPAPI
GdipCreateMetafileFromEmf(
    HENHMETAFILE    hEmf,
    BOOL            deleteEmf,
    GpMetafile **   metafile
    )
{
    API_ENTRY(GdipCreateMetafileFromEmf);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(hEmf && metafile);


    *metafile = new GpMetafile(hEmf, deleteEmf);

    if (*metafile)
    {
        if ((*metafile)->IsValid())
        {
            return Ok;
        }
        (*metafile)->Dispose();
        *metafile = NULL;
    }
    return GenericError;
}

GpStatus
WINGDIPAPI
GdipCreateMetafileFromFile(
    GDIPCONST WCHAR*    filename,
    GpMetafile **   metafile
    )
{
    API_ENTRY(GdipCreateMetafileFromFile);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(filename && metafile);


    *metafile = new GpMetafile(filename);

    if (*metafile)
    {
        if ((*metafile)->IsValid())
        {
            return Ok;
        }
        (*metafile)->Dispose();
        *metafile = NULL;
    }
    return GenericError;
}

GpStatus
WINGDIPAPI
GdipCreateMetafileFromWmfFile(
    GDIPCONST WCHAR*            filename,
    GDIPCONST WmfPlaceableFileHeader *   wmfPlaceableFileHeader,  // can be NULL
    GpMetafile **               metafile
    )
{
    API_ENTRY(GdipCreateMetafileFromWmfFile);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(filename && metafile);


    *metafile = new GpMetafile(filename, wmfPlaceableFileHeader);

    if (*metafile)
    {
        if ((*metafile)->IsValid())
        {
            return Ok;
        }
        (*metafile)->Dispose();
        *metafile = NULL;
    }
    return GenericError;
}

GpStatus
WINGDIPAPI
GdipCreateMetafileFromStream(
    IStream *       stream,
    GpMetafile **   metafile
    )
{
    API_ENTRY(GdipCreateMetafileFromStream);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(stream && metafile);


    *metafile = new GpMetafile(stream);

    if (*metafile)
    {
        if ((*metafile)->IsValid())
        {
            return Ok;
        }
        (*metafile)->Dispose();
        *metafile = NULL;
    }
    return GenericError;
}

GpStatus
WINGDIPAPI
GdipRecordMetafile(
    HDC                 referenceHdc,
    EmfType             type,
    GDIPCONST GpRectF *     frameRect,
    MetafileFrameUnit   frameUnit,
    GDIPCONST WCHAR *       description,
    GpMetafile **       metafile
    )
{
    API_ENTRY(GdipRecordMetafile);
    CheckParameter(referenceHdc && metafile);
    CheckParameter(EmfTypeIsValid(type));
    CheckParameter(MetafileFrameUnitIsValid(frameUnit));


    *metafile = new GpMetafile(referenceHdc, type, frameRect, frameUnit, description);

    if (*metafile)
    {
        if ((*metafile)->IsValid())
        {
            return Ok;
        }
        (*metafile)->Dispose();
        *metafile = NULL;
    }
    return GenericError;
}

GpStatus
WINGDIPAPI
GdipRecordMetafileI(
    HDC                 referenceHdc,
    EmfType             type,
    GDIPCONST GpRect *      frameRect,
    MetafileFrameUnit   frameUnit,
    GDIPCONST WCHAR *       description,
    GpMetafile **       metafile
    )
{
    API_ENTRY(GdipRecordMetafileI);
    CheckParameter(referenceHdc && frameRect);
    CheckParameter(EmfTypeIsValid(type));
    CheckParameter(MetafileFrameUnitIsValid(frameUnit));

    GpRectF frameRectF(TOREAL(frameRect->X),
                       TOREAL(frameRect->Y),
                       TOREAL(frameRect->Width),
                       TOREAL(frameRect->Height));

    return GdipRecordMetafile(referenceHdc,
                              type,
                              &frameRectF,
                              frameUnit,
                              description,
                              metafile);
}

GpStatus
WINGDIPAPI
GdipRecordMetafileFileName(
    GDIPCONST WCHAR*        fileName,
    HDC                 referenceHdc,
    EmfType             type,
    GDIPCONST GpRectF *     frameRect,
    MetafileFrameUnit   frameUnit,
    GDIPCONST WCHAR *       description,
    GpMetafile **       metafile
    )
{
    API_ENTRY(GdipRecordMetafileFileName);
    CheckParameter(fileName && referenceHdc && metafile);
    CheckParameter(EmfTypeIsValid(type));
    CheckParameter(MetafileFrameUnitIsValid(frameUnit));


    *metafile = new GpMetafile(fileName, referenceHdc, type, frameRect, frameUnit, description);

    if (*metafile)
    {
        if ((*metafile)->IsValid())
        {
            return Ok;
        }
        (*metafile)->Dispose();
        *metafile = NULL;
    }
    return GenericError;
}

GpStatus
WINGDIPAPI
GdipRecordMetafileFileNameI(
    GDIPCONST WCHAR*        fileName,
    HDC                 referenceHdc,
    EmfType             type,
    GDIPCONST GpRect *      frameRect,
    MetafileFrameUnit   frameUnit,
    GDIPCONST WCHAR *       description,
    GpMetafile **       metafile
    )
{
    API_ENTRY(GdipRecordMetafileFileNameI);
    CheckParameter(referenceHdc && frameRect);
    CheckParameter(EmfTypeIsValid(type));
    CheckParameter(MetafileFrameUnitIsValid(frameUnit));

    GpRectF frameRectF(TOREAL(frameRect->X),
                       TOREAL(frameRect->Y),
                       TOREAL(frameRect->Width),
                       TOREAL(frameRect->Height));

    return GdipRecordMetafileFileName(fileName,
                                      referenceHdc,
                                      type,
                                      &frameRectF,
                                      frameUnit,
                                      description,
                                      metafile);
}


GpStatus
WINGDIPAPI
GdipRecordMetafileStream(
    IStream *           stream,
    HDC                 referenceHdc,
    EmfType             type,
    GDIPCONST GpRectF *     frameRect,
    MetafileFrameUnit   frameUnit,
    GDIPCONST WCHAR *       description,
    GpMetafile **       metafile
    )
{
    API_ENTRY(GdipRecordMetafileStream);
    CheckParameter(stream && referenceHdc && metafile);
    CheckParameter(EmfTypeIsValid(type));
    CheckParameter(MetafileFrameUnitIsValid(frameUnit));


    *metafile = new GpMetafile(stream, referenceHdc, type, frameRect, frameUnit, description);

    if (*metafile)
    {
        if ((*metafile)->IsValid())
        {
            return Ok;
        }
        (*metafile)->Dispose();
        *metafile = NULL;
    }
    return GenericError;
}

GpStatus
WINGDIPAPI
GdipRecordMetafileStreamI(
    IStream *           stream,
    HDC                 referenceHdc,
    EmfType             type,
    GDIPCONST GpRect *      frameRect,
    MetafileFrameUnit   frameUnit,
    GDIPCONST WCHAR *       description,
    GpMetafile **       metafile
    )
{
    API_ENTRY(GdipRecordMetafileStreamI);
    CheckParameter(referenceHdc && frameRect);
    CheckParameter(EmfTypeIsValid(type));
    CheckParameter(MetafileFrameUnitIsValid(frameUnit));

    GpRectF frameRectF(TOREAL(frameRect->X),
                       TOREAL(frameRect->Y),
                       TOREAL(frameRect->Width),
                       TOREAL(frameRect->Height));

    return GdipRecordMetafileStream(stream,
                                    referenceHdc,
                                    type,
                                    &frameRectF,
                                    frameUnit,
                                    description,
                                    metafile);
}

GpStatus 
WINGDIPAPI
GdipSetMetafileDownLevelRasterizationLimit(
    GpMetafile *            metafile,
    UINT                    metafileRasterizationLimitDpi
    )
{
    API_ENTRY(GdipSetMetafileDownLevelRasterizationLimit);
    CheckParameterValid(metafile);
    CheckObjectBusy(metafile);

    // Since the rasterization limit is actually set in the graphics,
    // we check if the graphics is busy too.
    GpGraphics *    g = metafile->PrivateAPIForGettingMetafileGraphicsContext();
    CheckOptionalObjectBusy(g);
    
    return metafile->SetDownLevelRasterizationLimit(metafileRasterizationLimitDpi);
}

GpStatus WINGDIPAPI
GdipGetMetafileDownLevelRasterizationLimit(
    GDIPCONST GpMetafile *  metafile,
    UINT *                  metafileRasterizationLimitDpi
    )
{
    API_ENTRY(GdipGetMetafileDownLevelRasterizationLimit);
    CheckParameterValid(metafile);
    CheckObjectBusy(metafile);
    CheckParameter(metafileRasterizationLimitDpi);

    // Since the rasterization limit is actually set in the graphics,
    // we check if the graphics is busy too.
    GpGraphics *    g = metafile->PrivateAPIForGettingMetafileGraphicsContext();
    CheckOptionalObjectBusy(g);


    return metafile->GetDownLevelRasterizationLimit(metafileRasterizationLimitDpi);
}

// Codec management APIs

#define COPYCODECINFOSTR(_f)            \
        dst->_f = (GDIPCONST WCHAR*) buf;   \
        size = SizeofWSTR(cur->_f);     \
        memcpy(buf, cur->_f, size);     \
        buf += size

/**************************************************************************\
*
* Function Description:
*
*   Returns, via the OUT arguments, the number of installed decoders
*   and how much memory is needed to store the ImageCodecInfo for all the
*   decoders.  size tells how much memory the caller should allocate for
*   the call to GdipGetImageDecoders.
*
* Arguments:
*   numDecoders -- number of installed decoders
*   size        -- size (in bytes) of the ImageCodecInfo's of the decoders
*
* Return Value:
*
*   Status code
*
\**************************************************************************/

GpStatus
WINGDIPAPI
GdipGetImageDecodersSize(OUT UINT *numDecoders, OUT UINT *size)
{
    API_ENTRY(GdipGetImageDecodersSize);
    // Acquire global critical section

    ImagingCritSec critsec;

    ReloadCachedCodecInfo();

    CachedCodecInfo* cur;

    // Count the number of selected codecs
    // and figure the amount of memory we need to allocate

    *numDecoders = 0;
    *size = 0;

    for (cur = CachedCodecs; cur; cur = cur->next)
    {
        if (cur->Flags & IMGCODEC_DECODER)
        {
            (*numDecoders)++;
            *size += cur->structSize;
        }
    }
    // Global critical section is released in critsec destructor
    return Ok;
}


/**************************************************************************\
*
* Function Description:
*
*   Given the number of decoders (numDecoders), the size of
*   the incoming buffer (size), and a pointer to the buffer (decoders),
*   fill the buffer with the decoder information.
*
* Arguments:
*   numDecoders -- number of installed decoders
*   size        -- size (in bytes) of the ImageCodecInfo's of the decoders
*   decoders    -- pointer to a buffer to fill in the ImageCodecInfo
*
* Return Value:
*
*   Status code
*
\**************************************************************************/

GpStatus
WINGDIPAPI
GdipGetImageDecoders(UINT numDecoders,
                     UINT size,
                     ImageCodecInfo *decoders)
{
    API_ENTRY(GdipGetImageDecoders);
    GpStatus rv;
    HRESULT hResult;
    BYTE *buf;
    ImageCodecInfo* dst;
    CachedCodecInfo* cur;
    UINT numDecodersCheck = 0;
    UINT sizeCheck = 0;

    // Acquire global critical section

    ImagingCritSec critsec;

    if (decoders == NULL)
    {
        rv = GenericError;
        goto done;
    }

    GdipGetImageDecodersSize(&numDecodersCheck, &sizeCheck);
    // Check that the number of codecs (and size) now equals the number
    // that the user thinks there is.
    if ((numDecoders != numDecodersCheck) || (size != sizeCheck))
    {
        rv = GenericError;
        goto done;
    }
    // ASSERT: The result placed in sizeCheck is not used throughout
    // the rest of this function.

    buf = (BYTE *) decoders;

    // Copy codec information to the output buffer

    dst = decoders;
    buf += numDecoders * sizeof(ImageCodecInfo);

    for (cur = CachedCodecs; cur; cur = cur->next)
    {
        if ((cur->Flags & IMGCODEC_DECODER) == 0)
            continue;

        // First do a simple memory copy

        *dst = *static_cast<ImageCodecInfo *>(cur);

        // Then modify the pointer fields

        COPYCODECINFOSTR(CodecName);
        COPYCODECINFOSTR(FormatDescription);
        COPYCODECINFOSTR(FilenameExtension);
        COPYCODECINFOSTR(MimeType);

        if (size = cur->SigCount*cur->SigSize)
        {
            dst->SigPattern = buf;
            memcpy(buf, cur->SigPattern, size);
            buf += size;

            dst->SigMask = buf;
            memcpy(buf, cur->SigMask, size);
            buf += size;
        }

        dst++;
    }

    rv = Ok;

    // Global critical section is released in critsec destructor

done:
    return rv;
}


/**************************************************************************\
*
* Function Description:
*
*   Returns, via the OUT arguments, the number of installed encoders
*   and how much memory is needed to store the ImageCodecInfo for all the
*   encoders.  size tells how much memory the caller should allocate for
*   the call to GdipGetImageEncoders.
*
* Arguments:
*   numDecoders -- number of installed encoders
*   size        -- size (in bytes) of the ImageCodecInfo's of the encoders
*
* Return Value:
*
*   Status code
*
\**************************************************************************/

GpStatus
WINGDIPAPI
GdipGetImageEncodersSize(OUT UINT *numEncoders, OUT UINT *size)
{
    API_ENTRY(GdipGetImageEncodersSize);
    // Acquire global critical section

    ImagingCritSec critsec;

    ReloadCachedCodecInfo();

    CachedCodecInfo* cur;

    // Count the number of selected codecs
    // and figure the amount of memory we need to allocate

    *numEncoders = 0;
    *size = 0;

    for (cur = CachedCodecs; cur; cur = cur->next)
    {
        if (cur->Flags & IMGCODEC_ENCODER)
        {
            (*numEncoders)++;
            *size += cur->structSize;
        }
    }
    // Global critical section is released in critsec destructor
    return Ok;
}


/**************************************************************************\
*
* Function Description:
*
*   Given the number of encoders (numEncoders), the size of
*   the incoming buffer (size), and a pointer to the buffer (encoders),
*   fill the buffer with the encoder information.
*
* Arguments:
*   numEncoders -- number of installed encoders
*   size        -- size (in bytes) of the ImageCodecInfo's of the encoders
*   encoders    -- pointer to a buffer to fill in the ImageCodecInfo
*
* Return Value:
*
*   Status code
*
\**************************************************************************/

GpStatus
WINGDIPAPI
GdipGetImageEncoders(UINT numEncoders,
                     UINT size,
                     ImageCodecInfo *encoders)
{
    API_ENTRY(GdipGetImageEncoders);
    GpStatus rv;
    HRESULT hResult;
    BYTE *buf;
    ImageCodecInfo* dst;
    CachedCodecInfo* cur;
    UINT numEncodersCheck = 0;
    UINT sizeCheck = 0;

    // Acquire global critical section

    ImagingCritSec critsec;

    if (encoders == NULL)
    {
        rv = GenericError;
        goto done;
    }

    GdipGetImageEncodersSize(&numEncodersCheck, &sizeCheck);

    // Check that the number of codecs (and size) now equals the number
    // that the user thinks there is.
    if ((numEncoders != numEncodersCheck) || (size != sizeCheck))
    {
        rv = GenericError;
        goto done;
    }
    // ASSERT: The result placed in sizeCheck is not used throughout
    // the rest of this function.

    buf = (BYTE *) encoders;

    // Copy codec information to the output buffer

    dst = encoders;
    buf += numEncoders * sizeof(ImageCodecInfo);

    for (cur = CachedCodecs; cur; cur = cur->next)
    {
        if ((cur->Flags & IMGCODEC_ENCODER) == 0)
            continue;

        // First do a simple memory copy

        *dst = *static_cast<ImageCodecInfo*>(cur);

        // Then modify the pointer fields

        COPYCODECINFOSTR(CodecName);
        COPYCODECINFOSTR(FormatDescription);
        COPYCODECINFOSTR(FilenameExtension);
        COPYCODECINFOSTR(MimeType);

        if (size = cur->SigCount*cur->SigSize)
        {
            dst->SigPattern = buf;
            memcpy(buf, cur->SigPattern, size);
            buf += size;

            dst->SigMask = buf;
            memcpy(buf, cur->SigMask, size);
            buf += size;
        }

        dst++;
    }

    rv = Ok;

    // Global critical section is released in critsec destructor

done:
    return rv;
}

void*
WINGDIPAPI
GdipAlloc(
    size_t size
)
{
    API_ENTRY(GdipAlloc);
    CheckGdiplusInitialized_ReturnNULL;

    #if DBG
    return GpMallocAPI(size);
    #else
    return GpMalloc(size);
    #endif
}

void
WINGDIPAPI
GdipFree(
    void* ptr
)
{
    API_ENTRY(GdipFree);
    GpFree(ptr);
}

/// Out of place font/text stuff

GpStatus
WINGDIPAPI
GdipCreateFontFamilyFromName(GDIPCONST WCHAR *name,
                             GpFontCollection *fontCollection,
                             GpFontFamily **fontFamily)
{
    API_ENTRY(GdipCreateFontFamilyFromName);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(name && fontFamily);

    GlobalTextLock lock;

    CheckOptionalParameterValid(fontCollection);

    return GpFontFamily::CreateFontFamilyFromName(
                            name,
                            fontCollection,
                            fontFamily);
}


GpStatus
WINGDIPAPI
GdipGetGenericFontFamilySansSerif(GpFontFamily **nativeFamily)
{
    API_ENTRY(GdipGetGenericFontFamilySansSerif);
    CheckParameter(nativeFamily);
    return GpFontFamily::GetGenericFontFamilySansSerif(nativeFamily);
}

GpStatus
WINGDIPAPI
GdipGetGenericFontFamilySerif(GpFontFamily **nativeFamily)
{
    API_ENTRY(GdipGetGenericFontFamilySerif);
    CheckParameter(nativeFamily);
    return GpFontFamily::GetGenericFontFamilySerif(nativeFamily);
}

GpStatus
WINGDIPAPI
GdipGetGenericFontFamilyMonospace(GpFontFamily **nativeFamily)
{
    API_ENTRY(GdipGetGenericFontFamilyMonospace);
    CheckParameter(nativeFamily);
    return GpFontFamily::GetGenericFontFamilyMonospace(nativeFamily);
}


GpStatus
WINGDIPAPI
GdipCreateFont(
    GDIPCONST GpFontFamily *fontFamily,
    REAL                size,
    INT                 style,
    Unit                unit,
    GpFont            **font
)
{
    API_ENTRY(GdipCreateFont);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    GpStatus status = Ok;
    // check parameters
    CheckParameter(font);

    // UnitDisplay is NOT valid; its only use is for Page Transforms
    if (size <= 0 ||
//        (style < StyleRegular || style > StyleStrikeout) ||
        (unit < UnitWorld || unit > UnitMillimeter || unit == UnitDisplay))
    {
        return InvalidParameter;
    }

    GlobalTextLock lock;

    CheckParameterValid(fontFamily);

    if (*font = new GpFont(size, fontFamily, style, unit)) {
        if (!(*font)->GetFace()) {
           delete *font;
           *font = NULL;
           status = FontStyleNotFound;
        }
     }
    else
    {
        status = OutOfMemory;
    }

    return status;
}


GpStatus
WINGDIPAPI
GdipDeleteFontFamily(GpFontFamily *gpFontFamily)
{
    API_ENTRY(GdipDeleteFontFamily);
    // NOTE: Do NOT call CheckParameterValid(), because we need to free
    // the object, even if it's not in a valid state.
    CheckParameter(gpFontFamily);

    GlobalTextLock lock;

    gpFontFamily->DecFontFamilyRef();

    if (gpFontFamily->IsFileLoaded(FALSE))
    {
        // Try to delete the font file (RemoveFontFile) for each Face[].
        // This also will have the side effect of attempting to delete
        // any deletable GpFontFamily objects.
        UINT iFace = 0;
        UINT iLastFace = 0;
        GpFontFace *face = NULL;

        // Compute the index of the last non-NULL Face[] pointer.
        for (iFace = 0; iFace < NumFontFaces; iFace++)
        {
            face = gpFontFamily->GetFaceAbsolute(iFace);
            if (face != NULL)
            {
                iLastFace = iFace;
            }
        }

        for (iFace = 0; iFace <= iLastFace; iFace++)
        {
            face = gpFontFamily->GetFaceAbsolute(iFace);
            // Try to remove the font file if RemoveFontFile has
            // already been called on it.
            // Note that if iFace == iLastFace, then the call to
            // RemoveFontFile below might delete this gpFontFamily object,
            // which means the Face[] pointers would be garbage.  This is
            // why the for loop index goes up to iLastFace.
            if (face && face->pff->bRemoved)
            {
                GpFontCollection* actualFontCollection;
                actualFontCollection = gpFontFamily->GetFontCollection();
                actualFontCollection = actualFontCollection ?
                                        actualFontCollection :
                                        GpInstalledFontCollection::GetGpInstalledFontCollection();

                actualFontCollection->GetFontTable()->RemoveFontFile(face->pff->pwszPathname_);
            }
        }
    }

    return Ok;
}

GpStatus
WINGDIPAPI
GdipCloneFontFamily(GpFontFamily *gpFontFamily, GpFontFamily **gpClonedFontFamily)
{
    API_ENTRY(GdipCloneFontFamily);
    CheckParameter(gpClonedFontFamily);

    GlobalTextLock lock;

    CheckParameterValid(gpFontFamily);

    gpFontFamily->IncFontFamilyRef();
    *gpClonedFontFamily = gpFontFamily;

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetFamily(GpFont *font, GpFontFamily **family)
{
    API_ENTRY(GdipGetFamily);
    CheckParameter(family);

    GlobalTextLock lock;

    CheckParameterValid(font);

    *family = const_cast<GpFontFamily *>(font->GetFamily());

    return Ok;

}

GpStatus
WINGDIPAPI
GdipGetFontStyle(GpFont *font, INT *style)
{
    API_ENTRY(GdipGetFontStyle);
    CheckParameter(style);

    GlobalTextLock lock;

    CheckParameterValid(font);
    *style = font->GetStyle();

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetLogFontA(GpFont * font, GpGraphics *graphics, LOGFONTA * logfontA)
{
    API_ENTRY(GdipGetLogFontA);
    CheckParameter(logfontA);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);


    GlobalTextLock lock;

    CheckParameterValid(font);
    font->GetLogFontA(graphics, logfontA);

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetLogFontW(GpFont * font, GpGraphics *graphics, LOGFONTW * logfontW)
{
    API_ENTRY(GdipGetLogFontW);
    CheckParameter(logfontW);
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);


    GlobalTextLock lock;

    CheckParameterValid(font);
    font->GetLogFontW(graphics, logfontW);

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetFontSize(GpFont *font, REAL *size)
{
    API_ENTRY(GdipGetFontSize);
    CheckParameter(size);

    GlobalTextLock lock;

    CheckParameterValid(font);
    *size = font->GetEmSize();

    return Ok;
}


GpStatus
WINGDIPAPI
GdipGetFontUnit(GpFont *font, Unit *unit)
{
    API_ENTRY(GdipGetFontUnit);
    CheckParameter(unit);

    GlobalTextLock lock;

    CheckParameterValid(font);
    *unit = font->GetUnit();

    return Ok;
}


GpStatus
WINGDIPAPI
GdipGetFontHeight(GDIPCONST GpFont *font, GDIPCONST GpGraphics *graphics, REAL *height)
{
    API_ENTRY(GdipGetFontHeight);
    CheckParameter(height);
    CheckOptionalParameterValid(graphics);
    CheckOptionalObjectBusy(graphics);

    GlobalTextLock lock;

    CheckParameterValid(font);
    return font->GetHeight(graphics, height);
}


GpStatus
WINGDIPAPI
GdipGetFontHeightGivenDPI(GDIPCONST GpFont *font, REAL dpi, REAL *height)
{
    API_ENTRY(GdipGetFontHeight);
    CheckParameter(height);

    GlobalTextLock lock;

    CheckParameterValid(font);
    return font->GetHeight(dpi, height);
}


GpStatus
WINGDIPAPI
GdipCloneFont(
    GpFont* font,
    GpFont** cloneFont
    )
{
    API_ENTRY(GdipCloneFont);
    CheckParameter(cloneFont);

    GlobalTextLock lock;

    CheckParameterValid(font);
    *cloneFont = font->Clone();

    if (*cloneFont)
        return Ok;
    else
        return OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipDeleteFont(
    GpFont* font
    )
{
    API_ENTRY(GdipDeleteFont);
    // NOTE: Do NOT call CheckParameterValid(), because we need to free
    // the object, even if it's not in a valid state.
    CheckParameter(font);

    GlobalTextLock lock;

    delete font;

    return Ok;
}

/// End out of place font/text stuff

GpStatus
WINGDIPAPI
GdipGetDC(
    GpGraphics*     graphics,
    HDC *           hdc
    )
{
    API_ENTRY(GdipGetDC);
    CheckParameter(hdc);
    CheckParameterValid(graphics);

    // NOTE: We have to leave the graphics locked until the GdipReleaseDC call

    LONG *      lockCount = (graphics->GetObjectLock())->GetLockCount();
    LONG        result    = InterlockedIncrement(lockCount);

    if (result != 0)
    {
        InterlockedDecrement(lockCount);
        return ObjectBusy;
    }

    if ((*hdc = graphics->GetHdc()) == NULL)
    {
        InterlockedDecrement(lockCount);
        return InvalidParameter;
    }

    graphics->LockedByGetDC = -1;   // set graphics GetDC lock
    return Ok;
}

GpStatus
WINGDIPAPI
GdipReleaseDC(
    GpGraphics*     graphics,
    HDC             hdc
    )
{
    API_ENTRY(GdipReleaseDC);
    CheckParameter(hdc);
    CheckParameterValid(graphics);

    // NOTE: The graphics should already be locked from the GdipGetDC call

    LONG        result;

    if ((InterlockedIncrement(&(graphics->LockedByGetDC)) != 0) ||
        (!(graphics->GetObjectLock())->IsLocked()))
    {
        InterlockedDecrement(&(graphics->LockedByGetDC));
        return InvalidParameter;
    }

    graphics->ReleaseHdc(hdc);

    InterlockedDecrement((graphics->GetObjectLock())->GetLockCount());

    return Ok;
}

GpStatus
WINGDIPAPI
GdipComment(
    GpGraphics*     graphics,
    UINT            sizeData,
    GDIPCONST BYTE *    data
    )
{
    API_ENTRY(GdipComment);
    CheckParameter(data && (sizeData > 0));
    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);

    return graphics->Comment(sizeData, data);
}


// FontFamily

/// GdipCreateFontFamilyFromName should be moved here

/// GdipEnumerableFonts should be moved here

/// GdipEnumerateFonts should be moved here

/// GdipGetFamilyName should be moved here

GpStatus
WINGDIPAPI
GdipIsStyleAvailable(GDIPCONST GpFontFamily *family, INT style, BOOL * IsAvailable)
{
    API_ENTRY(GdipIsStyleAvailable);
    CheckParameter(IsAvailable);

    GlobalTextLock lock;

    CheckParameterValid(family);
    *IsAvailable = family->IsStyleAvailable(style);

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetEmHeight(GDIPCONST GpFontFamily *family, INT style, UINT16 * EmHeight)
{
    API_ENTRY(GdipGetEmHeight);
    CheckParameter(EmHeight);

    GlobalTextLock lock;

    CheckParameterValid(family);
    *EmHeight = family->GetDesignEmHeight(style);

    return Ok;

}

GpStatus
WINGDIPAPI
GdipGetCellAscent(GDIPCONST GpFontFamily *family, INT style, UINT16 * CellAscent)
{
    API_ENTRY(GdipGetCellAscent);
    CheckParameter(CellAscent);

    GlobalTextLock lock;

    CheckParameterValid(family);
    *CellAscent = family->GetDesignCellAscent(style);

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetCellDescent(GDIPCONST GpFontFamily *family, INT style, UINT16 * CellDescent)
{
    API_ENTRY(GdipGetCellDescent);
    CheckParameter(CellDescent);

    GlobalTextLock lock;

    CheckParameterValid(family);
    *CellDescent = family->GetDesignCellDescent(style);

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetLineSpacing(GDIPCONST GpFontFamily *family, INT style, UINT16 * LineSpacing)
{
    API_ENTRY(GdipGetLineSpacing);
    CheckParameter(LineSpacing);

    GlobalTextLock lock;

    CheckParameterValid(family);
    *LineSpacing = family->GetDesignLineSpacing(style);

    return Ok;
}

// Font

GpStatus
WINGDIPAPI
GdipCreateFontFromDC(
    HDC        hdc,
    GpFont   **font
)
{
    API_ENTRY(GdipCreateFontFromDC);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    GpStatus status = GenericError;
    CheckParameter(hdc);

    GlobalTextLock lock;

    if (*font = new GpFont(hdc))
    {
        // we should fail in case we don't have valid font family. (non-true type font selected in the hdc)
        if (!(*font)->IsValid())
        {
           delete *font;
           *font = NULL;
           status = NotTrueTypeFont;
        }
        else
            status = Ok;
    }

    return status;
}

GpStatus
WINGDIPAPI
GdipCreateFontFromLogfontA(
    HDC              hdc,
    GDIPCONST LOGFONTA  *logfont,
    GpFont         **font
)
{
    API_ENTRY(GdipCreateFontFromLogfontA);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    GpStatus status = GenericError;
    CheckParameter(hdc && logfont);

    GlobalTextLock lock;

    if (*font = new GpFont(hdc, const_cast<LOGFONTA*>(logfont)))
    {
        // we should fail in case we don't have valid font family. (non-true type font selected in the hdc)
        if (!(*font)->IsValid())
        {
           delete *font;
           *font = NULL;
           status = NotTrueTypeFont;
        }
        else
            status = Ok;
    }

    return status;
}

GpStatus
WINGDIPAPI
GdipCreateFontFromLogfontW(
    HDC             hdc,
    GDIPCONST LOGFONTW  *logfont,
    GpFont          **font
)
{
    API_ENTRY(GdipCreateFontFromLogfontW);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    GpStatus status = GenericError;
    CheckParameter(hdc && logfont);

    GlobalTextLock lock;

    if (*font = new GpFont(hdc, const_cast<LOGFONTW*>(logfont)))
    {
        // we should fail in case we don't have valid font family. (non-true type font selected in the hdc)
        if (!(*font)->IsValid())
        {
           delete *font;
           *font = NULL;
           status = NotTrueTypeFont;
        }
        else
            status = Ok;
    }

    return status;
}

/// GdipCreateFont should be moved here

/// GdipCloneFont should be moved here

/// GdipDeleteFont should be moved here

/// GdipGetFamily should be moved here

/// GdipGetFontStyle should be moved here

/// GdipGetFontSize should be moved here


GpStatus
WINGDIPAPI
GdipNewInstalledFontCollection(GpFontCollection** fontCollection)
{
    API_ENTRY(GdipNewInstalledFontCollection);
    CheckParameter (fontCollection);

    GlobalTextLock lock;

    *fontCollection = GpInstalledFontCollection::GetGpInstalledFontCollection();

    return (*fontCollection ? Ok : FileNotFound);
}

GpStatus
WINGDIPAPI
GdipNewPrivateFontCollection(GpFontCollection** fontCollection)
{
    API_ENTRY(GdipNewPrivateFontCollection);
    CheckParameter (fontCollection);

    GlobalTextLock lock;

    *fontCollection = new GpPrivateFontCollection;

    return (*fontCollection ? Ok : GenericError);
}

GpStatus
WINGDIPAPI
GdipDeletePrivateFontCollection(GpFontCollection** fontCollection)
{
    API_ENTRY(GdipDeletePrivateFontCollection);
    // NOTE: Do NOT call CheckParameterValid(), because we need to free
    // the object, even if it's not in a valid state.
    CheckParameter (fontCollection);

    GlobalTextLock lock;

    delete static_cast<GpPrivateFontCollection *>(*fontCollection);

    *fontCollection = NULL;

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetFontCollectionFamilyCount(
    GpFontCollection*   fontCollection,
    INT       *             numFound
    )
{
    API_ENTRY(GdipGetFontCollectionFamilyCount);
    CheckParameter (numFound);

    GlobalTextLock lock;

    CheckParameterValid(fontCollection);
    *numFound = fontCollection->GetFamilyCount();

    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetFontCollectionFamilyList(
    GpFontCollection* fontCollection,
    INT             numSought,
    GpFontFamily*   gpfamilies[],
    INT*            numFound
    )
{
    API_ENTRY(GdipGetFontCollectionFamilyList);
    GpStatus status;
    CheckParameter(gpfamilies);
    CheckParameter (numFound);

    GlobalTextLock lock;

    CheckParameterValid(fontCollection);
    return fontCollection->GetFamilies(
        numSought,
        gpfamilies,
        numFound);
}

GpStatus
WINGDIPAPI
GdipPrivateAddFontFile(
    GpFontCollection* fontCollection,
    GDIPCONST WCHAR* filename
    )
{
    API_ENTRY(GdipPrivateAddFontFile);
    CheckParameter (filename);

    // We can cast the fontCollection because we know that this
    // function is called only from PrivateFontCollection::AddFontFile,
    // and so we know that fontCollection really was constructed as
    // a PrivateFontCollection.
    GlobalTextLock lock;

    CheckParameterValid(fontCollection);
    return static_cast<GpPrivateFontCollection *>
        (fontCollection)->AddFontFile(filename);
}

GpStatus
WINGDIPAPI
GdipPrivateAddMemoryFont(
    GpFontCollection* fontCollection,
    GDIPCONST void* memory,
    INT length
    )
{
    API_ENTRY(GdipPrivateAddMemoryFont);
    CheckParameter (memory);

    // We can cast the fontCollection because we know that this
    // function is called only from PrivateFontCollection::AddMemoryFontFile,
    // and so we know that fontCollection really was constructed as
    // an PrivateFontCollection.
    GlobalTextLock lock;

    CheckParameterValid(fontCollection);
    return static_cast<GpPrivateFontCollection *>
        (fontCollection)->AddMemoryFont(memory, length);
}

GpStatus
WINGDIPAPI
GdipSetFontSize(GpFont *font, REAL size, Unit unit)
{
    API_ENTRY(GdipSetFontSize);

    GlobalTextLock lock;

    CheckParameterValid(font);

    // UnitDisplay is NOT valid; its only use is for Page Transforms
    if ((unit >= UnitWorld) && (unit <= UnitMillimeter) && (unit != UnitDisplay))
    {
        font->SetEmSize(size);
        font->SetUnit(unit);
        return Ok;
    }
    else
    {
        return GenericError;
    }
}


GpStatus
WINGDIPAPI
GdipCreateStringFormat(
    INT               formatAttributes,
    LANGID            language,
    GpStringFormat  **format
)
{
    API_ENTRY(GdipCreateStringFormat);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(format);

    GlobalTextLock lock;

    *format = new GpStringFormat(formatAttributes, language);

    return format ? Ok : OutOfMemory;
}

GpStatus
WINGDIPAPI
GdipStringFormatGetGenericDefault(GpStringFormat **format)
{
    API_ENTRY(GdipStringFormatGetGenericDefault);
    CheckParameter(format);

    GlobalTextLock lock;
    *format = GpStringFormat::GenericDefault();
    return Ok;
}

GpStatus
WINGDIPAPI
GdipStringFormatGetGenericTypographic(GpStringFormat **format)
{
    API_ENTRY(GdipStringFormatGetGenericTypographic);
    CheckParameter(format);
    GlobalTextLock lock;
    *format = GpStringFormat::GenericTypographic();
    return Ok;
}

GpStatus
WINGDIPAPI
GdipCloneStringFormat(
    GDIPCONST GpStringFormat *format,
    GpStringFormat **newFormat)
{
    API_ENTRY(GdipCloneStringFormat);
    CheckParameter(newFormat);

    GlobalTextLock lock;
    CheckParameterValid(format);

    *newFormat = format->Clone();

    return newFormat == NULL ? OutOfMemory : Ok;
}

GpStatus
WINGDIPAPI
GdipDeleteStringFormat(GpStringFormat *format)
{
    API_ENTRY(GdipDeleteStringFormat);
    // NOTE: Do NOT call CheckParameterValid(), because we need to free
    // the object, even if it's not in a valid state.
    CheckParameter(format);

    GlobalTextLock lock;

    if (!format->IsPermanent())
    {
        delete format;
    }

    return Ok;
}

GpStatus
WINGDIPAPI
GdipSetStringFormatFlags(GpStringFormat *format, INT flags)
{
    API_ENTRY(GdipSetStringFormatFlags);

    GlobalTextLock lock;
    CheckParameterValid(format);

    format->SetFormatFlags(flags);
    return Ok;
}

GpStatus
WINGDIPAPI
GdipGetStringFormatFlags(GDIPCONST GpStringFormat *format, INT *flags)
{
    API_ENTRY(GdipGetStringFormatFlags);
    CheckParameter(flags);

    GlobalTextLock lock;
    CheckParameterValid(format);
    *flags = format->GetFormatFlags();

    return Ok;
}

GpStatus
WINGDIPAPI
GdipSetStringFormatAlign(
    GpStringFormat   *format,
    StringAlignment   align)
{
    API_ENTRY(GdipSetStringFormatAlign);
    GlobalTextLock lock;
    CheckParameterValid(format);

    CheckParameter(    align >= StringAlignmentNear
                   &&  align <= StringAlignmentFar);

    return format->SetAlign(align);
}

GpStatus
WINGDIPAPI
GdipGetStringFormatAlign(
    GDIPCONST GpStringFormat *format,
    StringAlignment            *align)
{
    API_ENTRY(GdipGetStringFormatAlign);
    CheckParameter(align);
    GlobalTextLock lock;
    CheckParameterValid(format);

    return format->GetAlign(align);
}

GpStatus
WINGDIPAPI
GdipSetStringFormatLineAlign(
    GpStringFormat   *format,
    StringAlignment   align)
{
    API_ENTRY(GdipSetStringFormatLineAlign);

    GlobalTextLock lock;
    CheckParameterValid(format);

    CheckParameter(    align >= StringAlignmentNear
                   &&  align <= StringAlignmentFar);

    return format->SetLineAlign(align);
}

GpStatus
WINGDIPAPI
GdipGetStringFormatLineAlign(
    GDIPCONST GpStringFormat *format,
    StringAlignment          *align)
{
    API_ENTRY(GdipGetStringFormatLineAlign);
    CheckParameter(align);
    GlobalTextLock lock;
    CheckParameterValid(format);

    return format->GetLineAlign(align);
}


GpStatus
WINGDIPAPI
GdipSetStringFormatHotkeyPrefix(
    GpStringFormat *format,
    INT             hotkeyPrefix)
{
    API_ENTRY(GdipSetStringFormatHotkeyPrefix);
    CheckParameter(    hotkeyPrefix >= HotkeyPrefixNone
                   &&  hotkeyPrefix <= HotkeyPrefixHide);

    GlobalTextLock lock;
    CheckParameterValid(format);

    return format->SetHotkeyPrefix(hotkeyPrefix);
}

GpStatus
WINGDIPAPI
GdipGetStringFormatHotkeyPrefix(
    GDIPCONST GpStringFormat *format,
    INT                      *hotkeyPrefix)
{
    API_ENTRY(GdipGetStringFormatHotkeyPrefix);
    CheckParameter(hotkeyPrefix);
    GlobalTextLock lock;
    CheckParameterValid(format);

    return format->GetHotkeyPrefix(hotkeyPrefix);
}

GpStatus
WINGDIPAPI
GdipSetStringFormatTabStops(
    GpStringFormat  *format,
    REAL            firstTabOffset,
    INT             count,
    GDIPCONST REAL            *tabStops
)
{
    API_ENTRY(GdipSetStringFormatTabStops);
    CheckParameter(tabStops);
    GlobalTextLock lock;
    CheckParameterValid(format);

    return format->SetTabStops (
                firstTabOffset,
                count,
                tabStops
           );
}

GpStatus
WINGDIPAPI
GdipGetStringFormatTabStopCount(
    GDIPCONST GpStringFormat    *format,
    INT                     *count
)
{
    API_ENTRY(GdipGetStringFormatTabStopCount);
    CheckParameter(count);
    GlobalTextLock lock;
    CheckParameterValid(format);

    return format->GetTabStopCount (count);
}

GpStatus
WINGDIPAPI
GdipGetStringFormatTabStops(
    GDIPCONST GpStringFormat    *format,
    INT                     count,
    REAL                    *firstTabOffset,
    REAL                    *tabStops
)
{
    API_ENTRY(GdipGetStringFormatTabStops);
    CheckParameter(firstTabOffset && tabStops);
    GlobalTextLock lock;
    CheckParameterValid(format);

    return format->GetTabStops (
                firstTabOffset,
                count,
                tabStops
           );
}

GpStatus
WINGDIPAPI
GdipGetStringFormatMeasurableCharacterRangeCount(
    GDIPCONST GpStringFormat    *format,
    INT                         *count
)
{
    API_ENTRY(GdipGetStringFormatMeasurableCharacterRangeCount);
    CheckParameter(count);
    CheckParameterValid(format);

    *count = format->GetMeasurableCharacterRanges();
    return Ok;
}

GpStatus
WINGDIPAPI
GdipSetStringFormatMeasurableCharacterRanges(
    GpStringFormat              *format,
    INT                         rangeCount,
    GDIPCONST CharacterRange    *ranges
)
{
    API_ENTRY(GdipSetStringFormatMeasurableCharacterRanges);
    CheckParameter(ranges);
    CheckParameterValid(format);

    if (rangeCount > 32)
    {
        return ValueOverflow;
    }

    return format->SetMeasurableCharacterRanges(
        rangeCount,
        ranges
    );
    return Ok;
}

GpStatus
WINGDIPAPI
GdipSetStringFormatDigitSubstitution(
    GpStringFormat       *format,
    LANGID                language,
    StringDigitSubstitute substitute
)
{
    API_ENTRY(GdipSetStringFormatDigitSubstitution);
    GlobalTextLock lock;
    CheckParameterValid(format);

    return format->SetDigitSubstitution (
                language,
                substitute
           );
}

GpStatus
WINGDIPAPI
GdipGetStringFormatDigitSubstitution(
    GDIPCONST GpStringFormat        *format,
    LANGID                *language,
    StringDigitSubstitute *substitute
)
{
    API_ENTRY(GdipGetStringFormatDigitSubstitution);
    GlobalTextLock lock;
    CheckParameterValid(format);

    return format->GetDigitSubstitution (
                language,
                substitute
           );
}

GpStatus
WINGDIPAPI
GdipSetStringFormatTrimming(
    GpStringFormat  *format,
    StringTrimming   trimming
)
{
    API_ENTRY(GdipSetStringFormatTrimming);
    CheckParameter(    trimming >= StringTrimmingNone
                   &&  trimming <= StringTrimmingEllipsisPath);
    GlobalTextLock lock;
    CheckParameterValid(format);

    return format->SetTrimming(trimming);
}

GpStatus
WINGDIPAPI
GdipGetStringFormatTrimming(
    GDIPCONST GpStringFormat *format,
    StringTrimming       *trimming
)
{
    API_ENTRY(GdipGetStringFormatTrimming);
    CheckParameter(trimming);
    GlobalTextLock lock;
    CheckParameterValid(format);

    return format->GetTrimming(trimming);
}

GpStatus
WINGDIPAPI
GdipCreateCachedBitmap(
    GpBitmap *bitmap,
    GpGraphics *graphics,
    GpCachedBitmap **nativeCachedBitmap
)
{
    API_ENTRY(GdipCreateCachedBitmap);
    CheckGdiplusInitialized; // We do this in all our object creation API's

    CheckParameter(nativeCachedBitmap);

    // must have a bitmap and a graphics to create a CachedBitmap.
    // Also we must lock both objects for the duration of this call
    // because we can't have any other APIs modifying them.

    CheckParameterValid(graphics);
    CheckObjectBusy(graphics);
    CheckParameterValid(bitmap);
    CheckObjectBusy(bitmap);

    *nativeCachedBitmap = new GpCachedBitmap(bitmap, graphics);
    if(nativeCachedBitmap)
    {
        return Ok;
    }
    else
    {
        return OutOfMemory;
    }

}

GpStatus WINGDIPAPI
GdipDeleteCachedBitmap(GpCachedBitmap *nativeCachedBitmap)
{
    API_ENTRY(GdipDeleteCachedBitmap);

    // NOTE: Do NOT call CheckParameterValid(), because we need to free
    // the object, even if it's not in a valid state.
    CheckParameter(nativeCachedBitmap);

    // Grab the lock for the duration of the delete so that we bounce if
    // someone is busy rendering on it.

    // !!! [asecchia] note this will bounce if the object is locked and then
    // the deletion will fail. This will cause the app to leak memory because
    // there is no way to check the return status from delete.
    // This problem is common to all our deletion APIs

    CheckObjectBusyForDelete(nativeCachedBitmap);

    delete nativeCachedBitmap;

    return Ok;
}

GpStatus WINGDIPAPI
GdipDrawCachedBitmap(
    GpGraphics *nativeGraphics,
    GpCachedBitmap *cb,
    INT x,
    INT y
)
{
    API_ENTRY(GdipDrawCachedBitmap);
    // Check the input parameters for NULL

    CheckParameterValid(nativeGraphics);
    CheckParameterValid(cb);

    // Grab the lock to make sure nobody is currently trying to delete the
    // object under us.

    CheckObjectBusy(cb);

    // Grab the lock on the GpGraphics

    CheckObjectBusy(nativeGraphics);

    return (nativeGraphics->DrawCachedBitmap(cb, x, y));
}

// The palette must be freed and recreated whenever the Desktop colors change
HPALETTE
WINGDIPAPI
GdipCreateHalftonePalette()
{
    API_ENTRY(GdipCreateHalftonePalette);

    // !!! [agodfrey]: I bet we haven't documented the fact that the user
    //     has to call this any time the desktop colors change.
    //     Also, I don't know why we read 12 colors instead of 4 (I thought
    //     there were only 4 magic colors.)

    HPALETTE    hpalette;
    HDC         hdc = ::GetDC(NULL);  // Get a screen DC

    if (hdc != NULL)
    {
        // See if we need to get the desktop colors
        if ((::GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) &&
            (::GetSystemPaletteUse(hdc) == SYSPAL_STATIC) &&
            (::GetDeviceCaps(hdc, SIZEPALETTE) == 256))
        {
            // We have to get the desktop colors to guarantee
            // a XO_TRIVIAL translate in GDI.

            // First Entry is always Black
            ::GetSystemPaletteEntries(hdc, 1, 9,
                                      Win9xHalftonePalette.palPalEntry + 1);

            // We only can handle changes to the first 3 of the last 10
            ::GetSystemPaletteEntries(hdc, 246, 3,
                                      Win9xHalftonePalette.palPalEntry + 246);
        }
        ::ReleaseDC(NULL, hdc);
    }
    hpalette = ::CreatePalette((LOGPALETTE *)(&Win9xHalftonePalette));
    return hpalette;
}

GpStatus
WINGDIPAPI
GdipMonitorControl(GpMonitorControlEnum control, void * param)
{
    API_ENTRY(GdipMonitorControl);

    if(Globals::Monitors == NULL)
    {
        Globals::Monitors = new GpMonitors;

        if(Globals::Monitors == NULL)
            return OutOfMemory;
    }

    return Globals::Monitors->Control(control, param);
}

GpStatus
WINGDIPAPI
GdipTestControl(GpTestControlEnum control, void * param)
{
    API_ENTRY(GdipTestControl);

    GpStatus result = Ok;

    switch(control)
    {
    case TestControlForceBilinear:
        Globals::ForceBilinear = *((BOOL *) param);
        break;

    case TestControlNoICM:
        Globals::NoICM = *((BOOL *) param);
        break;

    case TestControlGetBuildNumber:
        *((INT32 *) param) = VER_PRODUCTBUILD;
        break;

    default:
        result = InvalidParameter;
        break;
    }

    return result;
}

UINT
WINGDIPAPI
GdipEmfToWmfBits(HENHMETAFILE hemf,
                 UINT         cbData16,
                 LPBYTE       pData16,
                 INT          iMapMode,
                 INT          eFlags)
{
    API_ENTRY(GdipEmfToWmfBits);
    return ConvertEmfToPlaceableWmf(
            hemf,
            cbData16,
            pData16,
            iMapMode,
            eFlags);
}

// Version information for debugging purposes
UINT32 GpBuildNumber = VER_PRODUCTBUILD;

} // end of extern "C"
