reactos/subsystems/win32/win32k/objects/brush.c

723 lines
18 KiB
C
Raw Normal View History

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS win32 subsystem
* PURPOSE: Functions for brushes
* FILE: subsystem/win32/win32k/objects/brush.c
* PROGRAMER:
1999-07-17 23:10:31 +00:00
*/
#include <win32k.h>
#define NDEBUG
#include <debug.h>
#define GDIOBJATTRFREE 170
typedef struct _GDI_OBJ_ATTR_FREELIST
{
LIST_ENTRY Entry;
DWORD nEntries;
PVOID AttrList[GDIOBJATTRFREE];
} GDI_OBJ_ATTR_FREELIST, *PGDI_OBJ_ATTR_FREELIST;
typedef struct _GDI_OBJ_ATTR_ENTRY
{
RGN_ATTR Attr[GDIOBJATTRFREE];
} GDI_OBJ_ATTR_ENTRY, *PGDI_OBJ_ATTR_ENTRY;
static const USHORT HatchBrushes[NB_HATCH_STYLES][8] =
{
XLATEOBJ rewrite. The new XLATEOBJ is not allocated from paged pool anymore, but instead allocated on the stack and Initialized. Only when we habe more than a color table with more than 6 entries, we need to allocate an additional buffer. The new interface: EXLATEOBJ_vInitialize is the main init function. It takes a source and destination palette and back and fore colors for monochome surfaces. EXLATEOBJ_vInitXlateFromDCs takes the source and dest DC and is for color translation between 2 surfaces represented by 2 DCs. EXLATEOBJ_vInitBrushXlate initializes an XLATEOBJ for a pattern brush. Finally EXLATEOBJ_vCleanup needs to be called when the XLATEOBJ is not needed anymore. Implement individual iXlate functions for certain cases and store a function pointer in the EXLATEOBJ structure for quick access. Change the usage of the PALETTE.Mode member to be a flag instead of an enum, add usage of PAL_MONOCHOME, PAL_RGB16_555 and PAL_RGB16_565. Add gpalMono, which *should* be used as palette for 1bpp DDBs. Currently there's a hack in the XLATEOBJ init code, to hack around the fact that this is missing. Fix the Hatch brush patterns, as they were inverted. Implement PALETTE_ulGetNearestBitFieldsIndex and PALETTE_ulGetNearestIndex. Get rid of the XLATEOBJ for the mouse pointer instead realize the pointer before usage. Get rid of logicalToSystem PALETTE member. NtGdiGetDIBitsInternal: Don't create a DIBBrush from the BITMAPINFO, when pvBits is NULL, as the function might be uninitualized. This fixes a crash of gdi_regtest. The whole function is quite ugly and needs to be rewritten (like probably the rest of the DIB code). This fixes the problem of artifacts in the selected desktop icons and some color problems. svn path=/trunk/; revision=42391
2009-08-04 20:37:10 +00:00
{0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF}, /* HS_HORIZONTAL */
{0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7}, /* HS_VERTICAL */
{0xFE, 0xFD, 0xFB, 0xF7, 0xEF, 0xDF, 0xBF, 0x7F}, /* HS_FDIAGONAL */
{0x7F, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD, 0xFE}, /* HS_BDIAGONAL */
{0xF7, 0xF7, 0xF7, 0xF7, 0x00, 0xF7, 0xF7, 0xF7}, /* HS_CROSS */
{0x7E, 0xBD, 0xDB, 0xE7, 0xE7, 0xDB, 0xBD, 0x7E} /* HS_DIAGCROSS */
};
PVOID
FASTCALL
AllocateObjectAttr(VOID)
{
PTHREADINFO pti;
PPROCESSINFO ppi;
PVOID pAttr;
PGDI_OBJ_ATTR_FREELIST pGdiObjAttrFreeList;
PGDI_OBJ_ATTR_ENTRY pGdiObjAttrEntry;
int i;
pti = PsGetCurrentThreadWin32Thread();
if (pti->pgdiBrushAttr)
{
pAttr = pti->pgdiBrushAttr; // Get the free one.
pti->pgdiBrushAttr = NULL;
return pAttr;
}
ppi = PsGetCurrentProcessWin32Process();
if (!ppi->pBrushAttrList) // If set point is null, allocate new group.
{
pGdiObjAttrEntry = EngAllocUserMem(sizeof(GDI_OBJ_ATTR_ENTRY), 0);
if (!pGdiObjAttrEntry)
{
DPRINT1("Attr Failed User Allocation!\n");
return NULL;
}
DPRINT("AllocObjectAttr User 0x%x\n",pGdiObjAttrEntry);
pGdiObjAttrFreeList = ExAllocatePoolWithTag( PagedPool,
sizeof(GDI_OBJ_ATTR_FREELIST),
GDITAG_BRUSH_FREELIST);
if ( !pGdiObjAttrFreeList )
{
EngFreeUserMem(pGdiObjAttrEntry);
return NULL;
}
RtlZeroMemory(pGdiObjAttrFreeList, sizeof(GDI_OBJ_ATTR_FREELIST));
DPRINT("AllocObjectAttr Ex 0x%x\n",pGdiObjAttrFreeList);
InsertHeadList( &ppi->GDIBrushAttrFreeList, &pGdiObjAttrFreeList->Entry);
pGdiObjAttrFreeList->nEntries = GDIOBJATTRFREE;
// Start at the bottom up and set end of free list point.
ppi->pBrushAttrList = &pGdiObjAttrEntry->Attr[GDIOBJATTRFREE-1];
// Build the free attr list.
for ( i = 0; i < GDIOBJATTRFREE; i++)
{
pGdiObjAttrFreeList->AttrList[i] = &pGdiObjAttrEntry->Attr[i];
}
}
pAttr = ppi->pBrushAttrList;
pGdiObjAttrFreeList = (PGDI_OBJ_ATTR_FREELIST)ppi->GDIBrushAttrFreeList.Flink;
// Free the list when it is full!
if ( pGdiObjAttrFreeList->nEntries-- == 1)
{ // No more free entries, so yank the list.
RemoveEntryList( &pGdiObjAttrFreeList->Entry );
ExFreePoolWithTag( pGdiObjAttrFreeList, GDITAG_BRUSH_FREELIST );
if ( IsListEmpty( &ppi->GDIBrushAttrFreeList ) )
{
ppi->pBrushAttrList = NULL;
return pAttr;
}
pGdiObjAttrFreeList = (PGDI_OBJ_ATTR_FREELIST)ppi->GDIBrushAttrFreeList.Flink;
}
ppi->pBrushAttrList = pGdiObjAttrFreeList->AttrList[pGdiObjAttrFreeList->nEntries-1];
return pAttr;
}
VOID
FASTCALL
FreeObjectAttr(PVOID pAttr)
{
PTHREADINFO pti;
PPROCESSINFO ppi;
PGDI_OBJ_ATTR_FREELIST pGdiObjAttrFreeList;
pti = PsGetCurrentThreadWin32Thread();
if (!pti) return;
if (!pti->pgdiBrushAttr)
{ // If it is null, just cache it for the next time.
pti->pgdiBrushAttr = pAttr;
return;
}
ppi = PsGetCurrentProcessWin32Process();
pGdiObjAttrFreeList = (PGDI_OBJ_ATTR_FREELIST)ppi->GDIBrushAttrFreeList.Flink;
// We add to the list of free entries, so this will grows!
if ( IsListEmpty(&ppi->GDIBrushAttrFreeList) ||
pGdiObjAttrFreeList->nEntries == GDIOBJATTRFREE )
{
pGdiObjAttrFreeList = ExAllocatePoolWithTag( PagedPool,
sizeof(GDI_OBJ_ATTR_FREELIST),
GDITAG_BRUSH_FREELIST);
if ( !pGdiObjAttrFreeList )
{
return;
}
InsertHeadList( &ppi->GDIBrushAttrFreeList, &pGdiObjAttrFreeList->Entry);
pGdiObjAttrFreeList->nEntries = 0;
}
// Up count, save the entry and set end of free list point.
++pGdiObjAttrFreeList->nEntries; // Top Down...
pGdiObjAttrFreeList->AttrList[pGdiObjAttrFreeList->nEntries-1] = pAttr;
ppi->pBrushAttrList = pAttr;
return;
}
BOOL
INTERNAL_CALL
BRUSH_Cleanup(PVOID ObjectBody)
{
PBRUSH pbrush = (PBRUSH)ObjectBody;
if (pbrush->flAttrs & (GDIBRUSH_IS_HATCH | GDIBRUSH_IS_BITMAP))
{
ASSERT(pbrush->hbmPattern);
GDIOBJ_SetOwnership(pbrush->hbmPattern, PsGetCurrentProcess());
GreDeleteObject(pbrush->hbmPattern);
}
/* Free the kmode styles array of EXTPENS */
if (pbrush->pStyle)
{
ExFreePool(pbrush->pStyle);
}
return TRUE;
}
INT
FASTCALL
BRUSH_GetObject(PBRUSH pbrush, INT Count, LPLOGBRUSH Buffer)
{
if (Buffer == NULL) return sizeof(LOGBRUSH);
if (Count == 0) return 0;
/* Set colour */
Buffer->lbColor = pbrush->BrushAttr.lbColor;
/* Set Hatch */
if ((pbrush->flAttrs & GDIBRUSH_IS_HATCH)!=0)
{
/* FIXME : this is not the right value */
Buffer->lbHatch = (LONG)pbrush->hbmPattern;
}
else
{
Buffer->lbHatch = 0;
}
Buffer->lbStyle = 0;
/* Get the type of style */
if ((pbrush->flAttrs & GDIBRUSH_IS_SOLID)!=0)
{
Buffer->lbStyle = BS_SOLID;
}
else if ((pbrush->flAttrs & GDIBRUSH_IS_NULL)!=0)
{
Buffer->lbStyle = BS_NULL; // BS_HOLLOW
}
else if ((pbrush->flAttrs & GDIBRUSH_IS_HATCH)!=0)
{
Buffer->lbStyle = BS_HATCHED;
}
else if ((pbrush->flAttrs & GDIBRUSH_IS_BITMAP)!=0)
{
Buffer->lbStyle = BS_PATTERN;
}
else if ((pbrush->flAttrs & GDIBRUSH_IS_DIB)!=0)
{
Buffer->lbStyle = BS_DIBPATTERN;
}
/* FIXME
else if ((pbrush->flAttrs & )!=0)
{
Buffer->lbStyle = BS_INDEXED;
}
else if ((pbrush->flAttrs & )!=0)
{
Buffer->lbStyle = BS_DIBPATTERNPT;
}
*/
/* FIXME */
return sizeof(LOGBRUSH);
}
/**
* @name CalculateColorTableSize
*
* Internal routine to calculate the number of color table entries.
*
* @param BitmapInfoHeader
* Input bitmap information header, can be any version of
* BITMAPINFOHEADER or BITMAPCOREHEADER.
*
* @param ColorSpec
* Pointer to variable which specifiing the color mode (DIB_RGB_COLORS
* or DIB_RGB_COLORS). On successful return this value is normalized
* according to the bitmap info.
*
* @param ColorTableSize
* On successful return this variable is filled with number of
* entries in color table for the image with specified parameters.
*
* @return
* TRUE if the input values together form a valid image, FALSE otherwise.
*/
BOOL
APIENTRY
CalculateColorTableSize(
CONST BITMAPINFOHEADER *BitmapInfoHeader,
UINT *ColorSpec,
UINT *ColorTableSize)
{
WORD BitCount;
DWORD ClrUsed;
DWORD Compression;
/*
* At first get some basic parameters from the passed BitmapInfoHeader
* structure. It can have one of the following formats:
* - BITMAPCOREHEADER (the oldest one with totally different layout
* from the others)
* - BITMAPINFOHEADER (the standard and most common header)
* - BITMAPV4HEADER (extension of BITMAPINFOHEADER)
* - BITMAPV5HEADER (extension of BITMAPV4HEADER)
*/
if (BitmapInfoHeader->biSize == sizeof(BITMAPCOREHEADER))
{
BitCount = ((LPBITMAPCOREHEADER)BitmapInfoHeader)->bcBitCount;
ClrUsed = 0;
Compression = BI_RGB;
}
else
{
BitCount = BitmapInfoHeader->biBitCount;
ClrUsed = BitmapInfoHeader->biClrUsed;
Compression = BitmapInfoHeader->biCompression;
}
switch (Compression)
{
case BI_BITFIELDS:
if (*ColorSpec == DIB_PAL_COLORS)
*ColorSpec = DIB_RGB_COLORS;
if (BitCount != 16 && BitCount != 32)
return FALSE;
/* For BITMAPV4HEADER/BITMAPV5HEADER the masks are included in
* the structure itself (bV4RedMask, bV4GreenMask, and bV4BlueMask).
* For BITMAPINFOHEADER the color masks are stored in the palette. */
if (BitmapInfoHeader->biSize > sizeof(BITMAPINFOHEADER))
*ColorTableSize = 0;
else
*ColorTableSize = 3;
return TRUE;
case BI_RGB:
switch (BitCount)
{
case 1:
*ColorTableSize = ClrUsed ? min(ClrUsed, 2) : 2;
return TRUE;
case 4:
*ColorTableSize = ClrUsed ? min(ClrUsed, 16) : 16;
return TRUE;
case 8:
*ColorTableSize = ClrUsed ? min(ClrUsed, 256) : 256;
return TRUE;
default:
if (*ColorSpec == DIB_PAL_COLORS)
*ColorSpec = DIB_RGB_COLORS;
if (BitCount != 16 && BitCount != 24 && BitCount != 32)
return FALSE;
*ColorTableSize = ClrUsed;
return TRUE;
}
case BI_RLE4:
if (BitCount == 4)
{
*ColorTableSize = ClrUsed ? min(ClrUsed, 16) : 16;
return TRUE;
}
return FALSE;
case BI_RLE8:
if (BitCount == 8)
{
*ColorTableSize = ClrUsed ? min(ClrUsed, 256) : 256;
return TRUE;
}
return FALSE;
case BI_JPEG:
case BI_PNG:
*ColorTableSize = ClrUsed;
return TRUE;
default:
return FALSE;
}
}
HBRUSH
APIENTRY
IntGdiCreateDIBBrush(
CONST BITMAPINFO *BitmapInfo,
UINT ColorSpec,
UINT BitmapInfoSize,
CONST VOID *PackedDIB)
{
HBRUSH hBrush;
PBRUSH pbrush;
HBITMAP hPattern;
ULONG_PTR DataPtr;
UINT PaletteEntryCount;
PSURFACE psurfPattern;
INT PaletteType;
HPALETTE hpal ;
if (BitmapInfo->bmiHeader.biSize < sizeof(BITMAPINFOHEADER))
{
SetLastWin32Error(ERROR_INVALID_PARAMETER);
return NULL;
}
if (!CalculateColorTableSize(&BitmapInfo->bmiHeader,
&ColorSpec,
&PaletteEntryCount))
{
SetLastWin32Error(ERROR_INVALID_PARAMETER);
return NULL;
}
// FIXME: What about BI_BITFIELDS
DataPtr = (ULONG_PTR)BitmapInfo + BitmapInfo->bmiHeader.biSize;
if (ColorSpec == DIB_RGB_COLORS)
DataPtr += PaletteEntryCount * sizeof(RGBQUAD);
else
DataPtr += PaletteEntryCount * sizeof(USHORT);
hPattern = IntGdiCreateBitmap(BitmapInfo->bmiHeader.biWidth,
BitmapInfo->bmiHeader.biHeight,
BitmapInfo->bmiHeader.biPlanes,
BitmapInfo->bmiHeader.biBitCount,
(PVOID)DataPtr);
if (hPattern == NULL)
{
SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
return NULL;
}
psurfPattern = SURFACE_LockSurface(hPattern);
ASSERT(psurfPattern != NULL);
hpal = BuildDIBPalette(BitmapInfo, &PaletteType);
psurfPattern->ppal = PALETTE_ShareLockPalette(hpal);
/* Lazy delete palette, it will be freed when its shared reference is zeroed */
GreDeleteObject(hpal);
SURFACE_UnlockSurface(psurfPattern);
pbrush = BRUSH_AllocBrushWithHandle();
if (pbrush == NULL)
{
GreDeleteObject(hPattern);
SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
return NULL;
}
hBrush = pbrush->BaseObject.hHmgr;
pbrush->flAttrs |= GDIBRUSH_IS_BITMAP | GDIBRUSH_IS_DIB;
pbrush->hbmPattern = hPattern;
/* FIXME: Fill in the rest of fields!!! */
GDIOBJ_SetOwnership(hPattern, NULL);
BRUSH_UnlockBrush(pbrush);
return hBrush;
}
HBRUSH
APIENTRY
IntGdiCreateHatchBrush(
INT Style,
COLORREF Color)
{
HBRUSH hBrush;
PBRUSH pbrush;
HBITMAP hPattern;
if (Style < 0 || Style >= NB_HATCH_STYLES)
{
return 0;
}
hPattern = IntGdiCreateBitmap(8, 8, 1, 1, (LPBYTE)HatchBrushes[Style]);
if (hPattern == NULL)
{
SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
return NULL;
}
pbrush = BRUSH_AllocBrushWithHandle();
if (pbrush == NULL)
{
GreDeleteObject(hPattern);
SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
return NULL;
}
hBrush = pbrush->BaseObject.hHmgr;
pbrush->flAttrs |= GDIBRUSH_IS_HATCH;
pbrush->hbmPattern = hPattern;
pbrush->BrushAttr.lbColor = Color & 0xFFFFFF;
GDIOBJ_SetOwnership(hPattern, NULL);
BRUSH_UnlockBrush(pbrush);
return hBrush;
}
HBRUSH
APIENTRY
IntGdiCreatePatternBrush(
HBITMAP hBitmap)
{
HBRUSH hBrush;
PBRUSH pbrush;
HBITMAP hPattern;
hPattern = BITMAP_CopyBitmap(hBitmap);
if (hPattern == NULL)
{
SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
return NULL;
}
pbrush = BRUSH_AllocBrushWithHandle();
if (pbrush == NULL)
{
GreDeleteObject(hPattern);
SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
return NULL;
}
hBrush = pbrush->BaseObject.hHmgr;
pbrush->flAttrs |= GDIBRUSH_IS_BITMAP;
pbrush->hbmPattern = hPattern;
/* FIXME: Fill in the rest of fields!!! */
GDIOBJ_SetOwnership(hPattern, NULL);
BRUSH_UnlockBrush(pbrush);
return hBrush;
}
HBRUSH
APIENTRY
IntGdiCreateSolidBrush(
COLORREF Color)
{
HBRUSH hBrush;
PBRUSH pbrush;
pbrush = BRUSH_AllocBrushWithHandle();
if (pbrush == NULL)
{
SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
return NULL;
}
hBrush = pbrush->BaseObject.hHmgr;
pbrush->flAttrs |= GDIBRUSH_IS_SOLID;
pbrush->BrushAttr.lbColor = Color;
/* FIXME: Fill in the rest of fields!!! */
BRUSH_UnlockBrush(pbrush);
return hBrush;
}
HBRUSH
APIENTRY
IntGdiCreateNullBrush(VOID)
{
HBRUSH hBrush;
PBRUSH pbrush;
pbrush = BRUSH_AllocBrushWithHandle();
if (pbrush == NULL)
{
SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
return NULL;
}
hBrush = pbrush->BaseObject.hHmgr;
pbrush->flAttrs |= GDIBRUSH_IS_NULL;
BRUSH_UnlockBrush(pbrush);
return hBrush;
}
VOID
FASTCALL
IntGdiSetSolidBrushColor(HBRUSH hBrush, COLORREF Color)
{
PBRUSH pbrush;
pbrush = BRUSH_LockBrush(hBrush);
if (pbrush->flAttrs & GDIBRUSH_IS_SOLID)
{
pbrush->BrushAttr.lbColor = Color & 0xFFFFFF;
}
BRUSH_UnlockBrush(pbrush);
}
/* PUBLIC FUNCTIONS ***********************************************************/
HBRUSH
APIENTRY
NtGdiCreateDIBBrush(
IN PVOID BitmapInfoAndData,
IN FLONG ColorSpec,
IN UINT BitmapInfoSize,
IN BOOL b8X8,
IN BOOL bPen,
IN PVOID PackedDIB)
{
BITMAPINFO *SafeBitmapInfoAndData;
NTSTATUS Status = STATUS_SUCCESS;
HBRUSH hBrush;
SafeBitmapInfoAndData = EngAllocMem(FL_ZERO_MEMORY, BitmapInfoSize, TAG_DIB);
if (SafeBitmapInfoAndData == NULL)
{
SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
return NULL;
}
_SEH2_TRY
{
ProbeForRead(BitmapInfoAndData, BitmapInfoSize, 1);
RtlCopyMemory(SafeBitmapInfoAndData, BitmapInfoAndData, BitmapInfoSize);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
if (!NT_SUCCESS(Status))
{
EngFreeMem(SafeBitmapInfoAndData);
SetLastNtError(Status);
return 0;
}
hBrush = IntGdiCreateDIBBrush(SafeBitmapInfoAndData,
ColorSpec,
BitmapInfoSize,
PackedDIB);
EngFreeMem(SafeBitmapInfoAndData);
return hBrush;
}
HBRUSH
APIENTRY
NtGdiCreateHatchBrushInternal(
ULONG Style,
COLORREF Color,
BOOL bPen)
{
return IntGdiCreateHatchBrush(Style, Color);
}
HBRUSH
APIENTRY
NtGdiCreatePatternBrushInternal(
HBITMAP hBitmap,
BOOL bPen,
BOOL b8x8)
{
return IntGdiCreatePatternBrush(hBitmap);
}
HBRUSH
APIENTRY
NtGdiCreateSolidBrush(COLORREF Color,
IN OPTIONAL HBRUSH hbr)
{
return IntGdiCreateSolidBrush(Color);
}
/**
* \name NtGdiSetBrushOrg
*
* \brief Sets the brush origin that GDI assigns to
* the next brush an application selects into the specified device context.
*
* @implemented
*/
BOOL
APIENTRY
NtGdiSetBrushOrg(HDC hDC, INT XOrg, INT YOrg, LPPOINT Point)
{
PDC dc;
PDC_ATTR pdcattr;
dc = DC_LockDc(hDC);
if (dc == NULL)
{
SetLastWin32Error(ERROR_INVALID_HANDLE);
return FALSE;
}
pdcattr = dc->pdcattr;
if (Point != NULL)
{
NTSTATUS Status = STATUS_SUCCESS;
POINT SafePoint;
SafePoint.x = pdcattr->ptlBrushOrigin.x;
SafePoint.y = pdcattr->ptlBrushOrigin.y;
_SEH2_TRY
{
ProbeForWrite(Point, sizeof(POINT), 1);
*Point = SafePoint;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
if (!NT_SUCCESS(Status))
{
DC_UnlockDc(dc);
SetLastNtError(Status);
return FALSE;
}
}
pdcattr->ptlBrushOrigin.x = XOrg;
pdcattr->ptlBrushOrigin.y = YOrg;
IntptlBrushOrigin(dc, XOrg, YOrg );
DC_UnlockDc(dc);
return TRUE;
}
/* EOF */