reactos/win32ss/gdi/ntgdi/dcobjs.c

794 lines
18 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Win32k subsystem
* PURPOSE: Functions for creation and destruction of DCs
* FILE: win32ss/gdi/ntgdi/dcobjs.c
* PROGRAMER: Timo Kreuzer (timo.kreuzer@rectos.org)
*/
#include <win32k.h>
#define NDEBUG
#include <debug.h>
VOID
FASTCALL
DC_vUpdateFillBrush(PDC pdc)
{
PDC_ATTR pdcattr = pdc->pdcattr;
PBRUSH pbrFill;
/* Check if the brush handle has changed */
if (pdcattr->hbrush != pdc->dclevel.pbrFill->BaseObject.hHmgr)
{
/* Try to lock the new brush */
pbrFill = BRUSH_ShareLockBrush(pdcattr->hbrush);
if (pbrFill)
{
/* Unlock old brush, set new brush */
BRUSH_ShareUnlockBrush(pdc->dclevel.pbrFill);
pdc->dclevel.pbrFill = pbrFill;
/* Mark eboFill as dirty */
pdcattr->ulDirty_ |= DIRTY_FILL;
}
else
{
/* Invalid brush handle, restore old one */
pdcattr->hbrush = pdc->dclevel.pbrFill->BaseObject.hHmgr;
}
}
/* Check if the EBRUSHOBJ needs update */
if (pdcattr->ulDirty_ & DIRTY_FILL)
{
/* Update eboFill */
EBRUSHOBJ_vUpdateFromDC(&pdc->eboFill, pdc->dclevel.pbrFill, pdc);
}
/* Check for DC brush */
if (pdcattr->hbrush == StockObjects[DC_BRUSH])
{
/* Update the eboFill's solid color */
EBRUSHOBJ_vSetSolidRGBColor(&pdc->eboFill, pdcattr->crBrushClr);
}
/* Clear flags */
pdcattr->ulDirty_ &= ~(DIRTY_FILL | DC_BRUSH_DIRTY);
}
VOID
FASTCALL
DC_vUpdateLineBrush(PDC pdc)
{
PDC_ATTR pdcattr = pdc->pdcattr;
PBRUSH pbrLine;
/* Check if the pen handle has changed */
if (pdcattr->hpen != pdc->dclevel.pbrLine->BaseObject.hHmgr)
{
/* Try to lock the new pen */
pbrLine = PEN_ShareLockPen(pdcattr->hpen);
if (pbrLine)
{
/* Unlock old brush, set new brush */
BRUSH_ShareUnlockBrush(pdc->dclevel.pbrLine);
pdc->dclevel.pbrLine = pbrLine;
/* Mark eboLine as dirty */
pdcattr->ulDirty_ |= DIRTY_LINE;
}
else
{
/* Invalid pen handle, restore old one */
pdcattr->hpen = pdc->dclevel.pbrLine->BaseObject.hHmgr;
}
}
/* Check if the EBRUSHOBJ needs update */
if (pdcattr->ulDirty_ & DIRTY_LINE)
{
/* Update eboLine */
EBRUSHOBJ_vUpdateFromDC(&pdc->eboLine, pdc->dclevel.pbrLine, pdc);
}
/* Check for DC pen */
if (pdcattr->hpen == StockObjects[DC_PEN])
{
/* Update the eboLine's solid color */
EBRUSHOBJ_vSetSolidRGBColor(&pdc->eboLine, pdcattr->crPenClr);
}
/* Clear flags */
pdcattr->ulDirty_ &= ~(DIRTY_LINE | DC_PEN_DIRTY);
}
VOID
FASTCALL
DC_vUpdateTextBrush(PDC pdc)
{
PDC_ATTR pdcattr = pdc->pdcattr;
/* Timo : The text brush should never be changed.
* Jérôme : Yeah, but its palette must be updated anyway! */
if(pdcattr->ulDirty_ & DIRTY_TEXT)
EBRUSHOBJ_vUpdateFromDC(&pdc->eboText, pbrDefaultBrush, pdc);
/* Update the eboText's solid color */
EBRUSHOBJ_vSetSolidRGBColor(&pdc->eboText, pdcattr->crForegroundClr);
/* Clear flag */
pdcattr->ulDirty_ &= ~DIRTY_TEXT;
}
VOID
FASTCALL
DC_vUpdateBackgroundBrush(PDC pdc)
{
PDC_ATTR pdcattr = pdc->pdcattr;
if(pdcattr->ulDirty_ & DIRTY_BACKGROUND)
EBRUSHOBJ_vUpdateFromDC(&pdc->eboBackground, pbrDefaultBrush, pdc);
/* Update the eboBackground's solid color */
EBRUSHOBJ_vSetSolidRGBColor(&pdc->eboBackground, pdcattr->crBackgroundClr);
/* Clear flag */
pdcattr->ulDirty_ &= ~DIRTY_BACKGROUND;
}
VOID
NTAPI
DC_vSetBrushOrigin(PDC pdc, LONG x, LONG y)
{
/* Set the brush origin */
pdc->dclevel.ptlBrushOrigin.x = x;
pdc->dclevel.ptlBrushOrigin.y = y;
/* Set the fill origin */
pdc->ptlFillOrigin.x = x + pdc->ptlDCOrig.x;
pdc->ptlFillOrigin.y = y + pdc->ptlDCOrig.y;
}
/**
* \name NtGdiSetBrushOrg
*
* \brief Sets the brush origin that GDI uses when drawing with pattern
* brushes. The brush origin is relative to the DC origin.
*
* @implemented
*/
_Success_(return!=FALSE)
__kernel_entry
BOOL
APIENTRY
NtGdiSetBrushOrg(
_In_ HDC hdc,
_In_ INT x,
_In_ INT y,
_Out_opt_ LPPOINT pptOut)
{
POINT ptOut;
/* Call the internal function */
BOOL Ret = GreSetBrushOrg( hdc, x, y, &ptOut);
if (Ret)
{
/* Check if the old origin was requested */
if (pptOut != NULL)
{
/* Enter SEH for buffer transfer */
_SEH2_TRY
{
/* Probe and copy the old origin */
ProbeForWrite(pptOut, sizeof(POINT), 1);
*pptOut = ptOut;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
_SEH2_YIELD(return FALSE);
}
_SEH2_END;
}
}
return Ret;
}
HPALETTE
NTAPI
GdiSelectPalette(
HDC hDC,
HPALETTE hpal,
BOOL ForceBackground)
{
PDC pdc;
HPALETTE oldPal = NULL;
PPALETTE ppal;
// FIXME: Mark the palette as a [fore\back]ground pal
pdc = DC_LockDc(hDC);
if (!pdc)
{
return NULL;
}
/* Check if this is a valid palette handle */
ppal = PALETTE_ShareLockPalette(hpal);
if (!ppal)
{
DC_UnlockDc(pdc);
return NULL;
}
/// FIXME: we shouldn't dereference pSurface when the PDEV is not locked
/* Is this a valid palette for this depth? */
if ((!pdc->dclevel.pSurface) ||
(BitsPerFormat(pdc->dclevel.pSurface->SurfObj.iBitmapFormat) <= 8
&& (ppal->flFlags & PAL_INDEXED)) ||
(BitsPerFormat(pdc->dclevel.pSurface->SurfObj.iBitmapFormat) > 8))
{
/* Get old palette, set new one */
oldPal = pdc->dclevel.hpal;
pdc->dclevel.hpal = hpal;
DC_vSelectPalette(pdc, ppal);
/* Mark the brushes invalid */
pdc->pdcattr->ulDirty_ |= DIRTY_FILL | DIRTY_LINE |
DIRTY_BACKGROUND | DIRTY_TEXT;
}
if(pdc->dctype == DCTYPE_MEMORY)
{
// This didn't work anyway
//IntGdiRealizePalette(hDC);
}
PALETTE_ShareUnlockPalette(ppal);
DC_UnlockDc(pdc);
return oldPal;
}
/*
* @implemented
*/
HBRUSH
APIENTRY
NtGdiSelectBrush(
IN HDC hDC,
IN HBRUSH hBrush)
{
PDC pDC;
HBRUSH hOrgBrush;
if (hDC == NULL || hBrush == NULL) return NULL;
pDC = DC_LockDc(hDC);
if (!pDC)
{
return NULL;
}
/* Simply return the user mode value, without checking */
hOrgBrush = pDC->pdcattr->hbrush;
pDC->pdcattr->hbrush = hBrush;
DC_vUpdateFillBrush(pDC);
DC_UnlockDc(pDC);
return hOrgBrush;
}
/*
* @implemented
*/
HPEN
APIENTRY
NtGdiSelectPen(
IN HDC hDC,
IN HPEN hPen)
{
PDC pDC;
HPEN hOrgPen;
if (hDC == NULL || hPen == NULL) return NULL;
pDC = DC_LockDc(hDC);
if (!pDC)
{
return NULL;
}
/* Simply return the user mode value, without checking */
hOrgPen = pDC->pdcattr->hpen;
pDC->pdcattr->hpen = hPen;
DC_vUpdateLineBrush(pDC);
DC_UnlockDc(pDC);
return hOrgPen;
}
BOOL
NTAPI
DC_bIsBitmapCompatible(PDC pdc, PSURFACE psurf)
{
ULONG cBitsPixel;
/* Must be an API bitmap */
if (!(psurf->flags & API_BITMAP)) return FALSE;
/* DIB sections are always compatible */
if (psurf->hSecure != NULL) return TRUE;
/* See if this is the same PDEV */
if (psurf->SurfObj.hdev == (HDEV)pdc->ppdev)
return TRUE;
/* Get the bit depth of the bitmap */
cBitsPixel = gajBitsPerFormat[psurf->SurfObj.iBitmapFormat];
/* 1 BPP is compatible */
if ((cBitsPixel == 1) || (cBitsPixel == pdc->ppdev->gdiinfo.cBitsPixel))
return TRUE;
return FALSE;
}
/*
* @implemented
*/
HBITMAP
APIENTRY
NtGdiSelectBitmap(
IN HDC hdc,
IN HBITMAP hbmp)
{
PDC pdc;
HBITMAP hbmpOld;
PSURFACE psurfNew, psurfOld;
HDC hdcOld;
ASSERT_NOGDILOCKS();
/* Verify parameters */
if (hdc == NULL || hbmp == NULL) return NULL;
/* First lock the DC */
pdc = DC_LockDc(hdc);
if (!pdc)
{
return NULL;
}
/* Must be a memory dc to select a bitmap */
if (pdc->dctype != DC_TYPE_MEMORY)
{
DC_UnlockDc(pdc);
return NULL;
}
/* Save the old bitmap */
psurfOld = pdc->dclevel.pSurface;
/* Check if there is a bitmap selected */
if (psurfOld)
{
/* Get the old bitmap's handle */
hbmpOld = psurfOld->BaseObject.hHmgr;
}
else
{
/* Use the default bitmap */
hbmpOld = StockObjects[DEFAULT_BITMAP];
}
/* Check if the new bitmap is already selected */
if (hbmp == hbmpOld)
{
/* Unlock the DC and return the old bitmap */
DC_UnlockDc(pdc);
return hbmpOld;
}
/* Check if the default bitmap was passed */
if (hbmp == StockObjects[DEFAULT_BITMAP])
{
psurfNew = NULL;
/* Default bitmap is 1x1 pixel */
pdc->dclevel.sizl.cx = 1;
pdc->dclevel.sizl.cy = 1;
}
else
{
/* Reference the new bitmap and check if it's valid */
psurfNew = SURFACE_ShareLockSurface(hbmp);
if (!psurfNew)
{
DC_UnlockDc(pdc);
return NULL;
}
/* Check if the bitmap is compatible with the dc */
if (!DC_bIsBitmapCompatible(pdc, psurfNew))
{
/* Dereference the bitmap, unlock the DC and fail. */
SURFACE_ShareUnlockSurface(psurfNew);
DC_UnlockDc(pdc);
return NULL;
}
/* Set the bitmap's hdc and check if it was set before */
hdcOld = InterlockedCompareExchangePointer((PVOID*)&psurfNew->hdc, hdc, 0);
if (hdcOld != NULL)
{
/* The bitmap is already selected into a different DC */
ASSERT(hdcOld != hdc);
/* Dereference the bitmap, unlock the DC and fail. */
SURFACE_ShareUnlockSurface(psurfNew);
DC_UnlockDc(pdc);
return NULL;
}
/* Copy the bitmap size */
pdc->dclevel.sizl = psurfNew->SurfObj.sizlBitmap;
/* Check if the bitmap is a dibsection */
if (psurfNew->hSecure)
{
/* Set DIBSECTION attribute */
pdc->pdcattr->ulDirty_ |= DC_DIBSECTION;
}
else
{
/* Remove DIBSECTION attribute */
pdc->pdcattr->ulDirty_ &= ~DC_DIBSECTION;
}
}
/* Select the new bitmap */
pdc->dclevel.pSurface = psurfNew;
/* Check if there was a bitmap selected before */
if (psurfOld)
{
/* Reset hdc of the old bitmap, it isn't selected anymore */
psurfOld->hdc = NULL;
/* Dereference the old bitmap */
SURFACE_ShareUnlockSurface(psurfOld);
}
/* Mark the DC brushes and the RAO region invalid */
pdc->pdcattr->ulDirty_ |= DIRTY_FILL | DIRTY_LINE;
pdc->fs |= DC_FLAG_DIRTY_RAO;
/* Update the system region */
REGION_SetRectRgn(pdc->prgnVis,
0,
0,
pdc->dclevel.sizl.cx,
pdc->dclevel.sizl.cy);
/* Unlock the DC */
DC_UnlockDc(pdc);
/* Return the old bitmap handle */
return hbmpOld;
}
BOOL
APIENTRY
NtGdiSelectClipPath(
HDC hDC,
int Mode)
{
PREGION RgnPath;
PPATH pPath, pNewPath;
BOOL success = FALSE;
PDC_ATTR pdcattr;
PDC pdc;
pdc = DC_LockDc(hDC);
if (!pdc)
{
EngSetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
pdcattr = pdc->pdcattr;
pPath = PATH_LockPath(pdc->dclevel.hPath);
if (!pPath)
{
DC_UnlockDc(pdc);
return FALSE;
}
/* Check that path is closed */
if (pPath->state != PATH_Closed)
{
EngSetLastError(ERROR_CAN_NOT_COMPLETE);
success = FALSE;
goto Exit;
}
/* Construct a region from the path */
RgnPath = IntSysCreateRectpRgn(0, 0, 0, 0);
if (!RgnPath)
{
EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
DC_UnlockDc(pdc);
return FALSE;
}
pNewPath = PATH_FlattenPath(pPath);
success = PATH_PathToRegion(pNewPath, pdcattr->jFillMode, RgnPath);
PATH_UnlockPath(pNewPath);
PATH_Delete(pNewPath->BaseObject.hHmgr);
if (success) success = IntGdiExtSelectClipRgn(pdc, RgnPath, Mode) != ERROR;
REGION_Delete(RgnPath);
Exit:
PATH_UnlockPath(pPath);
PATH_Delete(pdc->dclevel.hPath);
pdc->dclevel.flPath &= ~DCPATH_ACTIVE;
pdc->dclevel.hPath = NULL;
DC_UnlockDc(pdc);
return success;
}
HFONT
NTAPI
DC_hSelectFont(
_In_ PDC pdc,
_In_ HFONT hlfntNew)
{
PLFONT plfntNew;
HFONT hlfntOld;
// Legacy crap that will die with font engine rewrite
if (!NT_SUCCESS(TextIntRealizeFont(hlfntNew, NULL)))
{
return NULL;
}
/* Get the current selected font */
hlfntOld = pdc->dclevel.plfnt->BaseObject.hHmgr;
/* Check if a new font should be selected */
if (hlfntNew != hlfntOld)
{
/* Lock the new font */
plfntNew = LFONT_ShareLockFont(hlfntNew);
if (plfntNew)
{
/* Success, dereference the old font */
LFONT_ShareUnlockFont(pdc->dclevel.plfnt);
/* Select the new font */
pdc->dclevel.plfnt = plfntNew;
pdc->pdcattr->hlfntNew = hlfntNew;
/* Update dirty flags */
pdc->pdcattr->ulDirty_ |= DIRTY_CHARSET;
pdc->pdcattr->ulDirty_ &= ~SLOW_WIDTHS;
}
else
{
/* Failed, restore old, return NULL */
pdc->pdcattr->hlfntNew = hlfntOld;
hlfntOld = NULL;
}
}
return hlfntOld;
}
HFONT
APIENTRY
NtGdiSelectFont(
_In_ HDC hdc,
_In_ HFONT hfont)
{
HFONT hfontOld;
PDC pdc;
/* Check parameters */
if ((hdc == NULL) || (hfont == NULL))
{
return NULL;
}
/* Lock the DC */
pdc = DC_LockDc(hdc);
if (!pdc)
{
return NULL;
}
/* Call the internal function */
hfontOld = DC_hSelectFont(pdc, hfont);
/* Unlock the DC */
DC_UnlockDc(pdc);
/* Return the previously selected font */
return hfontOld;
}
HANDLE
APIENTRY
NtGdiGetDCObject(HDC hDC, INT ObjectType)
{
HGDIOBJ SelObject;
DC *pdc;
PDC_ATTR pdcattr;
/* From Wine: GetCurrentObject does not SetLastError() on a null object */
if(!hDC) return NULL;
if(!(pdc = DC_LockDc(hDC)))
{
return NULL;
}
pdcattr = pdc->pdcattr;
if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
DC_vUpdateFillBrush(pdc);
if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
DC_vUpdateLineBrush(pdc);
switch(ObjectType)
{
case GDI_OBJECT_TYPE_EXTPEN:
case GDI_OBJECT_TYPE_PEN:
SelObject = pdcattr->hpen;
break;
case GDI_OBJECT_TYPE_BRUSH:
SelObject = pdcattr->hbrush;
break;
case GDI_OBJECT_TYPE_PALETTE:
SelObject = pdc->dclevel.hpal;
break;
case GDI_OBJECT_TYPE_FONT:
SelObject = pdcattr->hlfntNew;
break;
case GDI_OBJECT_TYPE_BITMAP:
{
SURFACE *psurf = pdc->dclevel.pSurface;
SelObject = psurf ? psurf->BaseObject.hHmgr : StockObjects[DEFAULT_BITMAP];
break;
}
case GDI_OBJECT_TYPE_COLORSPACE:
DPRINT1("FIXME: NtGdiGetCurrentObject() ObjectType OBJ_COLORSPACE not supported yet!\n");
// SelObject = dc->dclevel.pColorSpace.BaseObject.hHmgr; ?
SelObject = NULL;
break;
default:
SelObject = NULL;
EngSetLastError(ERROR_INVALID_PARAMETER);
break;
}
DC_UnlockDc(pdc);
return SelObject;
}
/* See WINE, MSDN, OSR and Feng Yuan - Windows Graphics Programming Win32 GDI and DirectDraw
*
* 1st: http://www.codeproject.com/gdi/cliprgnguide.asp is wrong!
*
* The intersection of the clip with the meta region is not Rao it's API!
* Go back and read 7.2 Clipping pages 418-19:
* Rao = API & Vis:
* 1) The Rao region is the intersection of the API region and the system region,
* named after the Microsoft engineer who initially proposed it.
* 2) The Rao region can be calculated from the API region and the system region.
*
* API:
* API region is the intersection of the meta region and the clipping region,
* clearly named after the fact that it is controlled by GDI API calls.
*/
INT
APIENTRY
NtGdiGetRandomRgn(
HDC hdc,
HRGN hrgnDest,
INT iCode)
{
INT ret = 0;
PDC pdc;
PREGION prgnSrc = NULL;
pdc = DC_LockDc(hdc);
if (!pdc)
{
EngSetLastError(ERROR_INVALID_HANDLE);
return -1;
}
switch (iCode)
{
case CLIPRGN:
prgnSrc = pdc->dclevel.prgnClip;
break;
case METARGN:
prgnSrc = pdc->dclevel.prgnMeta;
break;
case APIRGN:
if (pdc->fs & DC_FLAG_DIRTY_RAO)
CLIPPING_UpdateGCRegion(pdc);
if (pdc->prgnAPI)
{
prgnSrc = pdc->prgnAPI;
}
else if (pdc->dclevel.prgnClip)
{
prgnSrc = pdc->dclevel.prgnClip;
}
else if (pdc->dclevel.prgnMeta)
{
prgnSrc = pdc->dclevel.prgnMeta;
}
break;
case SYSRGN:
prgnSrc = pdc->prgnVis;
break;
default:
break;
}
if (prgnSrc)
{
PREGION prgnDest = REGION_LockRgn(hrgnDest);
if (prgnDest)
{
ret = IntGdiCombineRgn(prgnDest, prgnSrc, 0, RGN_COPY) == ERROR ? -1 : 1;
if ((ret == 1) && (iCode == SYSRGN))
{
/// \todo FIXME This is not really correct, since we already modified the region
ret = REGION_bOffsetRgn(prgnDest, pdc->ptlDCOrig.x, pdc->ptlDCOrig.y);
}
REGION_UnlockRgn(prgnDest);
}
else
ret = -1;
}
DC_UnlockDc(pdc);
return ret;
}
ULONG
APIENTRY
NtGdiEnumObjects(
IN HDC hdc,
IN INT iObjectType,
IN ULONG cjBuf,
OUT OPTIONAL PVOID pvBuf)
{
UNIMPLEMENTED;
return 0;
}
/* EOF */