[WIN32K]
[reactos.git] / reactos / win32ss / gdi / ntgdi / brush.cpp
diff --git a/reactos/win32ss/gdi/ntgdi/brush.cpp b/reactos/win32ss/gdi/ntgdi/brush.cpp
new file mode 100644 (file)
index 0000000..1049684
--- /dev/null
@@ -0,0 +1,550 @@
+/*
+ * COPYRIGHT:         See COPYING in the top level directory
+ * PROJECT:           ReactOS win32 subsystem
+ * PURPOSE:           BRUSH class implementation
+ * PROGRAMER:         Timo Kreuzer (timo.kreuzer@reactos.org)
+ *
+ * REFERENCES:        http://support.microsoft.com/kb/kbview/108497
+ */
+
+#include "brush.hpp"
+
+DBG_DEFAULT_CHANNEL(GdiBrush);
+
+BRUSH::BRUSH(
+    _In_ FLONG flAttrs,
+    _In_ COLORREF crColor,
+    _In_ ULONG iHatch,
+    _In_opt_ HBITMAP hbmPattern,
+    _In_opt_ PVOID pvClient,
+    _In_ GDILOOBJTYPE loobjtype = GDILoObjType_LO_BRUSH_TYPE)
+    : BASEOBJECT(loobjtype)
+{
+    static ULONG ulGlobalBrushUnique = 0;
+
+    /* Get a unique value */
+    this->ulBrushUnique = InterlockedIncrementUL(&ulGlobalBrushUnique);
+
+    /* Start with kmode brush attribute */
+    this->pBrushAttr = &this->BrushAttr;
+
+    /* Set parameters */
+    this->flAttrs = flAttrs;
+    this->iHatch = iHatch;
+    this->hbmPattern = hbmPattern;
+    this->hbmClient = (HBITMAP)pvClient;
+    this->pBrushAttr->lbColor = crColor;
+
+    /* Initialize the other fields */
+    this->ptOrigin.x = 0;
+    this->ptOrigin.y = 0;
+    this->bCacheGrabbed = FALSE;
+    this->crBack = 0;
+    this->crFore = 0;
+    this->ulPalTime = 0;
+    this->ulSurfTime = 0;
+    this->pvRBrush = NULL;
+    this->hdev = NULL;
+}
+
+BRUSH::~BRUSH(
+    VOID)
+{
+    /* Check if we have a user mode brush attribute */
+    if (this->pBrushAttr != &this->BrushAttr)
+    {
+        /* Free memory to the process GDI pool */
+        GdiPoolFree(GetBrushAttrPool(), this->pBrushAttr);
+    }
+
+    /* Delete the pattern bitmap */
+    if (this->hbmPattern != NULL)
+    {
+        GreSetBitmapOwner(this->hbmPattern, BASEOBJECT::OWNER::POWNED);
+        GreDeleteObject(this->hbmPattern);
+    }
+}
+
+VOID
+BRUSH::vDeleteObject(
+    _In_ PVOID pvObject)
+{
+    PBRUSH pbr = static_cast<PBRUSH>(pvObject);
+    NT_ASSERT((GDI_HANDLE_GET_TYPE(pbr->hHmgr()) == GDILoObjType_LO_BRUSH_TYPE) ||
+              (GDI_HANDLE_GET_TYPE(pbr->hHmgr()) == GDILoObjType_LO_PEN_TYPE) ||
+              (GDI_HANDLE_GET_TYPE(pbr->hHmgr()) == GDILoObjType_LO_EXTPEN_TYPE));
+    delete pbr;
+}
+
+BOOL
+BRUSH::bAllocateBrushAttr(
+    VOID)
+{
+    PBRUSH_ATTR pBrushAttr;
+    NT_ASSERT(this->pBrushAttr == &this->BrushAttr);
+
+    /* Allocate a brush attribute from the pool */
+    pBrushAttr = static_cast<PBRUSH_ATTR>(GdiPoolAllocate(GetBrushAttrPool()));
+    if (pBrushAttr == NULL)
+    {
+        ERR("Could not allocate brush attr\n");
+        return FALSE;
+    }
+
+    /* Copy the content from the kernel mode brush attribute */
+    this->pBrushAttr = pBrushAttr;
+    *this->pBrushAttr = this->BrushAttr;
+
+    /* Set the object attribute in the handle table */
+    vSetObjectAttr(pBrushAttr);
+
+    return TRUE;
+}
+
+VOID
+BRUSH::vSetSolidColor(
+    _In_ COLORREF crColor)
+{
+    NT_ASSERT(this->flAttrs & BR_IS_SOLID);
+
+    /* Set new color and reset the pal times */
+    this->pBrushAttr->lbColor = crColor & 0xFFFFFF;
+    this->ulPalTime = -1;
+    this->ulSurfTime = -1;
+}
+
+HBITMAP
+BRUSH::hbmGetBitmapHandle(
+    _Out_ PUINT puUsage) const
+{
+    /* Return the color usage based on flags */
+    *puUsage = (this->flAttrs & BR_IS_DIBPALCOLORS) ? DIB_PAL_COLORS :
+               (this->flAttrs & BR_IS_DIBPALINDICES) ? DIB_PAL_INDICES :
+               DIB_RGB_COLORS;
+
+    return this->hbmPattern;
+}
+
+UINT
+BRUSH::cjGetObject(
+    _In_ UINT cjSize,
+    _Out_bytecap_(cjSize) PLOGBRUSH plb) const
+{
+    /* Check if only size is requested */
+    if (plb == NULL)
+        return sizeof(LOGBRUSH);
+
+    /* Check if size is ok */
+    if (cjSize == 0)
+        return 0;
+
+    /* Set color */
+    plb->lbColor = this->BrushAttr.lbColor;
+
+    /* Set style and hatch based on the attribute flags */
+    if (this->flAttrs & BR_IS_SOLID)
+    {
+        plb->lbStyle = BS_SOLID;
+        plb->lbHatch = 0;
+    }
+    else if (this->flAttrs & BR_IS_HATCH)
+    {
+        plb->lbStyle = BS_HATCHED;
+        plb->lbHatch = this->iHatch;
+    }
+    else if (this->flAttrs & BR_IS_DIB)
+    {
+        plb->lbStyle = BS_DIBPATTERN;
+        plb->lbHatch = (ULONG_PTR)this->hbmClient;
+    }
+    else if (this->flAttrs & BR_IS_BITMAP)
+    {
+        plb->lbStyle = BS_PATTERN;
+        plb->lbHatch = (ULONG_PTR)this->hbmClient;
+    }
+    else if (this->flAttrs & BR_IS_NULL)
+    {
+        plb->lbStyle = BS_NULL;
+        plb->lbHatch = 0;
+    }
+    else
+    {
+        NT_ASSERT(FALSE);
+    }
+
+    return sizeof(LOGBRUSH);
+}
+
+static
+HBRUSH
+CreateBrushInternal(
+    _In_ ULONG flAttrs,
+    _In_ COLORREF crColor,
+    _In_ ULONG iHatch,
+    _In_opt_ HBITMAP hbmPattern,
+    _In_opt_ PVOID pvClient)
+{
+    BASEOBJECT::OWNER owner;
+    PBRUSH pbr;
+    HBRUSH hbr;
+
+    NT_ASSERT(((flAttrs & BR_IS_BITMAP) == 0) || (hbmPattern != NULL));
+
+    /* Create the brush (brush takes ownership of the bitmap) */
+    pbr = new BRUSH(flAttrs, crColor, iHatch, hbmPattern, pvClient);
+    if (pbr == NULL)
+    {
+        ERR("Failed to allocate a brush\n");
+        GreSetBitmapOwner(hbmPattern, BASEOBJECT::OWNER::POWNED);
+        GreDeleteObject(hbmPattern);
+        return NULL;
+    }
+
+    /* Check if this is a global brush */
+    if (!(flAttrs & BR_IS_GLOBAL))
+    {
+        /* Not a global brush, so allocate a user mode brush attribute */
+        if (!pbr->bAllocateBrushAttr())
+        {
+            ERR("Failed to allocate brush attribute\n");
+            delete pbr;
+            return NULL;
+        }
+    }
+
+    /* Set the owner, either public or process owned */
+    owner = (flAttrs & BR_IS_GLOBAL) ? BASEOBJECT::OWNER::PUBLIC :
+                                       BASEOBJECT::OWNER::POWNED;
+
+    /* Insert the object into the GDI handle table */
+    hbr =  static_cast<HBRUSH>(pbr->hInsertObject(owner));
+    if (hbr == NULL)
+    {
+        ERR("Failed to insert brush\n");
+        delete pbr;
+        return NULL;
+    }
+
+    /* Unlock the brush */
+    pbr->vUnlock();
+
+    return hbr;
+}
+
+
+/* C interface ***************************************************************/
+
+extern "C" {
+
+VOID
+NTAPI
+BRUSH_vDeleteObject(
+    PVOID pvObject)
+{
+    BRUSH::vDeleteObject(pvObject);
+}
+
+INT
+FASTCALL
+BRUSH_GetObject(
+    PBRUSH pbr,
+    INT cjBuffer,
+    LPLOGBRUSH plbBuffer)
+{
+    return pbr->cjGetObject(cjBuffer, plbBuffer);
+}
+
+HBRUSH
+NTAPI
+IntGdiCreateNullBrush(
+    VOID)
+{
+    /* Call the internal function */
+    return CreateBrushInternal(BR_IS_NULL | BR_IS_GLOBAL, 0, 0, NULL, NULL);
+}
+
+HBRUSH
+APIENTRY
+IntGdiCreateSolidBrush(
+    COLORREF crColor)
+{
+    /* Call the internal function */
+    return CreateBrushInternal(BR_IS_SOLID | BR_IS_GLOBAL,
+                               crColor,
+                               0,
+                               NULL,
+                               NULL);
+}
+
+HBRUSH
+NTAPI
+IntGdiCreatePatternBrush(
+    HBITMAP hbmPattern)
+{
+    NT_ASSERT(hbmPattern != NULL);
+    GreSetBitmapOwner(hbmPattern, BASEOBJECT::OWNER::PUBLIC);
+    return CreateBrushInternal(BR_IS_BITMAP | BR_IS_GLOBAL,
+                               0,
+                               0,
+                               hbmPattern,
+                               NULL);
+}
+
+VOID
+NTAPI
+IntGdiSetSolidBrushColor(
+    _In_ HBRUSH hbr,
+    _In_ COLORREF crColor)
+{
+    PBRUSH pbr;
+
+    /* Lock the brush */
+    pbr = BRUSH::LockAny(hbr);
+    if (pbr == NULL)
+    {
+        ERR("Failed to lock brush %p\n", hbr);
+        return;
+    }
+
+    /* Call the member function */
+    pbr->vSetSolidColor(crColor);
+
+    /* Unlock the brush */
+    pbr->vUnlock();
+}
+
+__kernel_entry
+HBRUSH
+APIENTRY
+NtGdiCreateSolidBrush(
+    _In_ COLORREF crColor,
+    _In_opt_ HBRUSH hbr)
+{
+    if (hbr != NULL)
+    {
+        WARN("hbr is not supported, ignoring\n");
+    }
+
+    /* Call the internal function */
+    return CreateBrushInternal(BR_IS_SOLID, crColor, 0, NULL, NULL);
+}
+
+__kernel_entry
+HBRUSH
+APIENTRY
+NtGdiCreateHatchBrushInternal(
+    _In_ ULONG iHatch,
+    _In_ COLORREF crColor,
+    _In_ BOOL bPen)
+{
+    FLONG flAttr;
+
+    if (bPen)
+    {
+        WARN("bPen is not supported, ignoring\n");
+    }
+
+    /* Check what kind if hatch style this is */
+    if (iHatch < HS_DDI_MAX)
+    {
+        flAttr = BR_IS_HATCH;
+    }
+    else if (iHatch < HS_API_MAX)
+    {
+        flAttr = BR_IS_SOLID;
+    }
+    else
+    {
+        ERR("Invalid iHatch: %lu\n", iHatch);
+        return NULL;
+    }
+
+    /* Call the internal function */
+    return CreateBrushInternal(flAttr, crColor, iHatch, NULL, NULL);
+}
+
+__kernel_entry
+HBRUSH
+APIENTRY
+NtGdiCreatePatternBrushInternal(
+    _In_ HBITMAP hbmClient,
+    _In_ BOOL bPen,
+    _In_ BOOL b8X8)
+{
+    HBITMAP hbmPattern;
+
+    if (b8X8)
+    {
+        WARN("b8X8 is not supported, ignoring\n");
+    }
+
+    if (bPen)
+    {
+        WARN("bPen is not supported, ignoring\n");
+    }
+
+    /* Copy the bitmap */
+    hbmPattern = BITMAP_CopyBitmap(hbmClient);
+    if (hbmPattern == NULL)
+    {
+        ERR("Failed to copy the bitmap %p\n", hbmPattern);
+        return NULL;
+    }
+
+    /* Call the internal function (will delete hbmPattern on failure) */
+    return CreateBrushInternal(BR_IS_BITMAP, 0, 0, hbmPattern, hbmClient);
+}
+
+__kernel_entry
+HBRUSH
+APIENTRY
+NtGdiCreateDIBBrush(
+    _In_reads_bytes_(cj) PVOID pv,
+    _In_ FLONG uUsage,
+    _In_ UINT cj,
+    _In_ BOOL b8X8,
+    _In_ BOOL bPen,
+    _In_ PVOID pvClient)
+{
+    PVOID pvPackedDIB;
+    FLONG flAttrs;
+    HBITMAP hbm;
+    HBRUSH hbr = NULL;
+
+    if (b8X8)
+    {
+        WARN("b8X8 is not supported, ignoring\n");
+    }
+
+    if (bPen)
+    {
+        WARN("bPen is not supported, ignoring\n");
+    }
+
+    if (uUsage > DIB_PAL_INDICES)
+    {
+        ERR("Invalid uUsage value: %lu\n", uUsage);
+        EngSetLastError(ERROR_INVALID_PARAMETER);
+        return NULL;
+    }
+
+    /* Allocate a buffer for the packed DIB */
+    pvPackedDIB = ExAllocatePoolWithTag(PagedPool, cj, GDITAG_TEMP);
+    if (pvPackedDIB == NULL)
+    {
+        ERR("Failed to allocate temp buffer of %u bytes\n", cj);
+        return NULL;
+    }
+
+    /* Probe and copy the packed DIB */
+    _SEH2_TRY
+    {
+        ProbeForRead(pv, cj, 1);
+        RtlCopyMemory(pvPackedDIB, pv, cj);
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        ERR("Got exception, pv = %p, cj = %lu\n", pv, cj);
+        goto cleanup;
+    }
+    _SEH2_END;
+
+    flAttrs = BR_IS_BITMAP | BR_IS_DIB;
+
+    /* Check what kind of color table we have */
+    if (uUsage == DIB_PAL_COLORS)
+    {
+        /* Remember it and use DIB_PAL_BRUSHHACK to create a "special" palette */
+        flAttrs |= BR_IS_DIBPALCOLORS;
+        uUsage = DIB_PAL_BRUSHHACK;
+    }
+    else if (uUsage == DIB_PAL_INDICES)
+    {
+        /* No color table, bitmap contains device palette indices */
+        flAttrs |= BR_IS_DIBPALINDICES;
+
+        /* FIXME: This makes tests pass, but needs investigation. */
+        flAttrs |= BR_IS_NULL;
+    }
+
+    /* Create a bitmap from the DIB */
+    hbm = GreCreateDIBitmapFromPackedDIB(pvPackedDIB, cj, uUsage);
+    if (hbm == NULL)
+    {
+        ERR("Failed to create bitmap from DIB\n");
+        goto cleanup;
+    }
+
+    /* Call the internal function (will delete hbm on failure) */
+    hbr = CreateBrushInternal(flAttrs, 0, 0, hbm, pvClient);
+
+cleanup:
+
+    ExFreePoolWithTag(pvPackedDIB, GDITAG_TEMP);
+
+    return hbr;
+}
+
+__kernel_entry
+HBITMAP
+APIENTRY
+NtGdiGetObjectBitmapHandle(
+    _In_ HBRUSH hbr,
+    _Out_ UINT *piUsage)
+{
+    PBRUSH pbr;
+    HBITMAP hbm;
+    UINT uUsage;
+
+    /* Lock the brush */
+    pbr = BRUSH::LockForRead(hbr);
+    if (pbr == NULL)
+    {
+        ERR("Failed to lock brush %p\n", hbr);
+        return NULL;
+    }
+
+    /* Call the member function */
+    hbm = pbr->hbmGetBitmapHandle(&uUsage);
+
+    /* Unlock the brush */
+    pbr->vUnlock();
+
+    _SEH2_TRY
+    {
+        ProbeForWrite(piUsage, sizeof(*piUsage), 1);
+        *piUsage = uUsage;
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        ERR("Got exception! piUsage = %p\n", piUsage);
+        hbm = NULL;
+    }
+    _SEH2_END;
+
+    return hbm;
+}
+
+__kernel_entry
+HBRUSH
+APIENTRY
+NtGdiSetBrushAttributes(
+    _In_ HBRUSH hbr,
+    _In_ DWORD dwFlags)
+{
+    __debugbreak();
+    return NULL;
+}
+
+__kernel_entry
+HBRUSH
+APIENTRY
+NtGdiClearBrushAttributes(
+    _In_ HBRUSH hbr,
+    _In_ DWORD dwFlags)
+{
+    __debugbreak();
+    return NULL;
+}
+
+} /* extern "C" */