mirror of
https://github.com/reactos/reactos.git
synced 2025-01-03 21:09:19 +00:00
1094 lines
33 KiB
C
1094 lines
33 KiB
C
/*
|
|
* ReactOS GDI lib
|
|
* Copyright (C) 2003 ReactOS Team
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
/*
|
|
* PROJECT: ReactOS gdi32.dll
|
|
* FILE: win32ss/gdi/gdi32/misc/misc.c
|
|
* PURPOSE: Miscellaneous functions
|
|
* PROGRAMMER: Thomas Weidenmueller <w3seek@reactos.com>
|
|
* UPDATE HISTORY:
|
|
* 2004/09/04 Created
|
|
*/
|
|
|
|
#include <precomp.h>
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
PGDI_TABLE_ENTRY GdiHandleTable = NULL;
|
|
PGDI_SHARED_HANDLE_TABLE GdiSharedHandleTable = NULL;
|
|
HANDLE CurrentProcessId = NULL;
|
|
DWORD GDI_BatchLimit = 1;
|
|
extern PGDIHANDLECACHE GdiHandleCache;
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
GdiFlush(VOID)
|
|
{
|
|
NtGdiFlush();
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
INT
|
|
WINAPI
|
|
Escape(
|
|
_In_ HDC hdc,
|
|
_In_ INT nEscape,
|
|
_In_ INT cbInput,
|
|
_In_ LPCSTR lpvInData,
|
|
_Out_ LPVOID lpvOutData)
|
|
{
|
|
INT retValue = SP_ERROR;
|
|
ULONG ulObjType;
|
|
|
|
ulObjType = GDI_HANDLE_GET_TYPE(hdc);
|
|
|
|
if (ulObjType == GDILoObjType_LO_METADC16_TYPE)
|
|
{
|
|
return METADC_ExtEscape(hdc, nEscape, cbInput, lpvInData, 0, lpvOutData);
|
|
}
|
|
|
|
switch (nEscape)
|
|
{
|
|
case ABORTDOC:
|
|
/* Note: Windows checks if the handle has any user data for the ABORTDOC command
|
|
* ReactOS copies this behavior to be compatible with windows 2003
|
|
*/
|
|
if (GdiGetDcAttr(hdc) == NULL)
|
|
{
|
|
GdiSetLastError(ERROR_INVALID_HANDLE);
|
|
retValue = FALSE;
|
|
}
|
|
else
|
|
{
|
|
retValue = AbortDoc(hdc);
|
|
}
|
|
break;
|
|
|
|
case DRAFTMODE:
|
|
case FLUSHOUTPUT:
|
|
case SETCOLORTABLE:
|
|
/* Note 1: DRAFTMODE, FLUSHOUTPUT, SETCOLORTABLE are outdated */
|
|
/* Note 2: Windows checks if the handle has any user data for the DRAFTMODE, FLUSHOUTPUT, SETCOLORTABLE commands
|
|
* ReactOS copies this behavior to be compatible with windows 2003
|
|
*/
|
|
if (GdiGetDcAttr(hdc) == NULL)
|
|
{
|
|
GdiSetLastError(ERROR_INVALID_HANDLE);
|
|
}
|
|
retValue = FALSE;
|
|
break;
|
|
|
|
case SETABORTPROC:
|
|
/* Note: Windows checks if the handle has any user data for the SETABORTPROC command
|
|
* ReactOS copies this behavior to be compatible with windows 2003
|
|
*/
|
|
if (GdiGetDcAttr(hdc) == NULL)
|
|
{
|
|
GdiSetLastError(ERROR_INVALID_HANDLE);
|
|
retValue = FALSE;
|
|
}
|
|
retValue = SetAbortProc(hdc, (ABORTPROC)lpvInData);
|
|
break;
|
|
|
|
case GETCOLORTABLE:
|
|
retValue = GetSystemPaletteEntries(hdc, (UINT)*lpvInData, 1, (LPPALETTEENTRY)lpvOutData);
|
|
if (!retValue)
|
|
{
|
|
retValue = SP_ERROR;
|
|
}
|
|
break;
|
|
|
|
case ENDDOC:
|
|
/* Note: Windows checks if the handle has any user data for the ENDDOC command
|
|
* ReactOS copies this behavior to be compatible with windows 2003
|
|
*/
|
|
if (GdiGetDcAttr(hdc) == NULL)
|
|
{
|
|
GdiSetLastError(ERROR_INVALID_HANDLE);
|
|
retValue = FALSE;
|
|
}
|
|
retValue = EndDoc(hdc);
|
|
break;
|
|
|
|
case GETSCALINGFACTOR:
|
|
/* Note GETSCALINGFACTOR is outdated have been replace by GetDeviceCaps */
|
|
if (ulObjType == GDI_OBJECT_TYPE_DC)
|
|
{
|
|
if (lpvOutData)
|
|
{
|
|
PPOINT ptr = (PPOINT)lpvOutData;
|
|
ptr->x = 0;
|
|
ptr->y = 0;
|
|
}
|
|
}
|
|
retValue = FALSE;
|
|
break;
|
|
|
|
case GETEXTENDEDTEXTMETRICS:
|
|
retValue = GetETM(hdc, (EXTTEXTMETRIC *)lpvOutData) != 0;
|
|
break;
|
|
|
|
case STARTDOC:
|
|
{
|
|
DOCINFOA di;
|
|
|
|
/* Note: Windows checks if the handle has any user data for the STARTDOC command
|
|
* ReactOS copies this behavior to be compatible with windows 2003
|
|
*/
|
|
if (GdiGetDcAttr(hdc) == NULL)
|
|
{
|
|
GdiSetLastError(ERROR_INVALID_HANDLE);
|
|
retValue = FALSE;
|
|
}
|
|
|
|
di.cbSize = sizeof(DOCINFOA);
|
|
di.lpszOutput = 0;
|
|
di.lpszDatatype = 0;
|
|
di.fwType = 0;
|
|
di.lpszDocName = lpvInData;
|
|
|
|
/* NOTE : doc for StartDocA/W at msdn http://msdn2.microsoft.com/en-us/library/ms535793(VS.85).aspx */
|
|
retValue = StartDocA(hdc, &di);
|
|
|
|
/* Check if StartDocA failed */
|
|
if (retValue < 0)
|
|
{
|
|
{
|
|
retValue = GetLastError();
|
|
|
|
/* Translate StartDocA error code to STARTDOC error code
|
|
* see msdn http://msdn2.microsoft.com/en-us/library/ms535472.aspx
|
|
*/
|
|
switch(retValue)
|
|
{
|
|
case ERROR_NOT_ENOUGH_MEMORY:
|
|
retValue = SP_OUTOFMEMORY;
|
|
break;
|
|
|
|
case ERROR_PRINT_CANCELLED:
|
|
retValue = SP_USERABORT;
|
|
break;
|
|
|
|
case ERROR_DISK_FULL:
|
|
retValue = SP_OUTOFDISK;
|
|
break;
|
|
|
|
default:
|
|
retValue = SP_ERROR;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
UNIMPLEMENTED;
|
|
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
}
|
|
|
|
return retValue;
|
|
}
|
|
|
|
INT
|
|
WINAPI
|
|
ExtEscape(HDC hDC,
|
|
int nEscape,
|
|
int cbInput,
|
|
LPCSTR lpszInData,
|
|
int cbOutput,
|
|
LPSTR lpszOutData)
|
|
{
|
|
return NtGdiExtEscape(hDC, NULL, 0, nEscape, cbInput, (LPSTR)lpszInData, cbOutput, lpszOutData);
|
|
}
|
|
|
|
INT
|
|
WINAPI
|
|
NamedEscape(HDC hdc,
|
|
PWCHAR pDriver,
|
|
INT iEsc,
|
|
INT cjIn,
|
|
LPSTR pjIn,
|
|
INT cjOut,
|
|
LPSTR pjOut)
|
|
{
|
|
/* FIXME metadc, metadc are done most in user mode, and we do not support it
|
|
* Windows 2000/XP/Vista ignore the current hdc, that are being pass and always set hdc to NULL
|
|
* when it calls to NtGdiExtEscape from NamedEscape
|
|
*/
|
|
return NtGdiExtEscape(NULL,pDriver,wcslen(pDriver),iEsc,cjIn,pjIn,cjOut,pjOut);
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
int
|
|
WINAPI
|
|
DrawEscape(HDC hDC,
|
|
INT nEscape,
|
|
INT cbInput,
|
|
LPCSTR lpszInData)
|
|
{
|
|
if (GDI_HANDLE_GET_TYPE(hDC) == GDI_OBJECT_TYPE_DC)
|
|
return NtGdiDrawEscape(hDC, nEscape, cbInput, (LPSTR) lpszInData);
|
|
|
|
if (GDI_HANDLE_GET_TYPE(hDC) != GDI_OBJECT_TYPE_METADC)
|
|
{
|
|
PLDC pLDC = GdiGetLDC(hDC);
|
|
if ( pLDC )
|
|
{
|
|
if (pLDC->Flags & LDC_META_PRINT)
|
|
{
|
|
// if (nEscape != QUERYESCSUPPORT)
|
|
// return EMFDRV_WriteEscape(hDC, nEscape, cbInput, lpszInData, EMR_DRAWESCAPE);
|
|
|
|
return NtGdiDrawEscape(hDC, nEscape, cbInput, (LPSTR) lpszInData);
|
|
}
|
|
}
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#define ALPHABLEND_NONE 0
|
|
#define ALPHABLEND_BINARY 1
|
|
#define ALPHABLEND_FULL 2
|
|
|
|
typedef struct _MARGINS {
|
|
int cxLeftWidth;
|
|
int cxRightWidth;
|
|
int cyTopHeight;
|
|
int cyBottomHeight;
|
|
} MARGINS, *PMARGINS;
|
|
|
|
enum SIZINGTYPE {
|
|
ST_TRUESIZE = 0,
|
|
ST_STRETCH = 1,
|
|
ST_TILE = 2,
|
|
};
|
|
|
|
#define TransparentBlt GdiTransparentBlt
|
|
#define AlphaBlend GdiAlphaBlend
|
|
|
|
/***********************************************************************
|
|
* UXTHEME_StretchBlt
|
|
*
|
|
* Pseudo TransparentBlt/StretchBlt
|
|
*/
|
|
static inline BOOL UXTHEME_StretchBlt(HDC hdcDst, int nXOriginDst, int nYOriginDst, int nWidthDst, int nHeightDst,
|
|
HDC hdcSrc, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc,
|
|
INT transparent, COLORREF transcolor)
|
|
{
|
|
static const BLENDFUNCTION blendFunc =
|
|
{
|
|
AC_SRC_OVER, /* BlendOp */
|
|
0, /* BlendFlag */
|
|
255, /* SourceConstantAlpha */
|
|
AC_SRC_ALPHA /* AlphaFormat */
|
|
};
|
|
|
|
BOOL ret = TRUE;
|
|
int old_stretch_mode;
|
|
POINT old_brush_org;
|
|
|
|
old_stretch_mode = SetStretchBltMode(hdcDst, HALFTONE);
|
|
SetBrushOrgEx(hdcDst, nXOriginDst, nYOriginDst, &old_brush_org);
|
|
|
|
if (transparent == ALPHABLEND_BINARY) {
|
|
/* Ensure we don't pass any negative values to TransparentBlt */
|
|
ret = TransparentBlt(hdcDst, nXOriginDst, nYOriginDst, abs(nWidthDst), abs(nHeightDst),
|
|
hdcSrc, nXOriginSrc, nYOriginSrc, abs(nWidthSrc), abs(nHeightSrc),
|
|
transcolor);
|
|
} else if ((transparent == ALPHABLEND_NONE) ||
|
|
!AlphaBlend(hdcDst, nXOriginDst, nYOriginDst, nWidthDst, nHeightDst,
|
|
hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc,
|
|
blendFunc))
|
|
{
|
|
ret = StretchBlt(hdcDst, nXOriginDst, nYOriginDst, nWidthDst, nHeightDst,
|
|
hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc,
|
|
SRCCOPY);
|
|
}
|
|
|
|
SetBrushOrgEx(hdcDst, old_brush_org.x, old_brush_org.y, NULL);
|
|
SetStretchBltMode(hdcDst, old_stretch_mode);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* UXTHEME_Blt
|
|
*
|
|
* Simplify sending same width/height for both source and dest
|
|
*/
|
|
static inline BOOL UXTHEME_Blt(HDC hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest,
|
|
HDC hdcSrc, int nXOriginSrc, int nYOriginSrc,
|
|
INT transparent, COLORREF transcolor)
|
|
{
|
|
return UXTHEME_StretchBlt(hdcDest, nXOriginDest, nYOriginDest, nWidthDest, nHeightDest,
|
|
hdcSrc, nXOriginSrc, nYOriginSrc, nWidthDest, nHeightDest,
|
|
transparent, transcolor);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* UXTHEME_SizedBlt
|
|
*
|
|
* Stretches or tiles, depending on sizingtype.
|
|
*/
|
|
static inline BOOL UXTHEME_SizedBlt (HDC hdcDst, int nXOriginDst, int nYOriginDst,
|
|
int nWidthDst, int nHeightDst,
|
|
HDC hdcSrc, int nXOriginSrc, int nYOriginSrc,
|
|
int nWidthSrc, int nHeightSrc,
|
|
int sizingtype,
|
|
INT transparent, COLORREF transcolor)
|
|
{
|
|
if (sizingtype == ST_TILE)
|
|
{
|
|
HDC hdcTemp;
|
|
BOOL result = FALSE;
|
|
|
|
if (!nWidthSrc || !nHeightSrc) return TRUE;
|
|
|
|
/* For destination width/height less than or equal to source
|
|
width/height, do not bother with memory bitmap optimization */
|
|
if (nWidthSrc >= nWidthDst && nHeightSrc >= nHeightDst)
|
|
{
|
|
int bltWidth = min (nWidthDst, nWidthSrc);
|
|
int bltHeight = min (nHeightDst, nHeightSrc);
|
|
|
|
return UXTHEME_Blt (hdcDst, nXOriginDst, nYOriginDst, bltWidth, bltHeight,
|
|
hdcSrc, nXOriginSrc, nYOriginSrc,
|
|
transparent, transcolor);
|
|
}
|
|
|
|
/* Create a DC with a bitmap consisting of a tiling of the source
|
|
bitmap, with standard GDI functions. This is faster than an
|
|
iteration with UXTHEME_Blt(). */
|
|
hdcTemp = CreateCompatibleDC(hdcSrc);
|
|
if (hdcTemp != 0)
|
|
{
|
|
HBITMAP bitmapTemp;
|
|
HBITMAP bitmapOrig;
|
|
int nWidthTemp, nHeightTemp;
|
|
int xOfs, xRemaining;
|
|
int yOfs, yRemaining;
|
|
int growSize;
|
|
|
|
/* Calculate temp dimensions of integer multiples of source dimensions */
|
|
nWidthTemp = ((nWidthDst + nWidthSrc - 1) / nWidthSrc) * nWidthSrc;
|
|
nHeightTemp = ((nHeightDst + nHeightSrc - 1) / nHeightSrc) * nHeightSrc;
|
|
bitmapTemp = CreateCompatibleBitmap(hdcSrc, nWidthTemp, nHeightTemp);
|
|
bitmapOrig = SelectObject(hdcTemp, bitmapTemp);
|
|
|
|
/* Initial copy of bitmap */
|
|
BitBlt(hdcTemp, 0, 0, nWidthSrc, nHeightSrc, hdcSrc, nXOriginSrc, nYOriginSrc, SRCCOPY);
|
|
|
|
/* Extend bitmap in the X direction. Growth of width is exponential */
|
|
xOfs = nWidthSrc;
|
|
xRemaining = nWidthTemp - nWidthSrc;
|
|
growSize = nWidthSrc;
|
|
while (xRemaining > 0)
|
|
{
|
|
growSize = min(growSize, xRemaining);
|
|
BitBlt(hdcTemp, xOfs, 0, growSize, nHeightSrc, hdcTemp, 0, 0, SRCCOPY);
|
|
xOfs += growSize;
|
|
xRemaining -= growSize;
|
|
growSize *= 2;
|
|
}
|
|
|
|
/* Extend bitmap in the Y direction. Growth of height is exponential */
|
|
yOfs = nHeightSrc;
|
|
yRemaining = nHeightTemp - nHeightSrc;
|
|
growSize = nHeightSrc;
|
|
while (yRemaining > 0)
|
|
{
|
|
growSize = min(growSize, yRemaining);
|
|
BitBlt(hdcTemp, 0, yOfs, nWidthTemp, growSize, hdcTemp, 0, 0, SRCCOPY);
|
|
yOfs += growSize;
|
|
yRemaining -= growSize;
|
|
growSize *= 2;
|
|
}
|
|
|
|
/* Use temporary hdc for source */
|
|
result = UXTHEME_Blt (hdcDst, nXOriginDst, nYOriginDst, nWidthDst, nHeightDst,
|
|
hdcTemp, 0, 0,
|
|
transparent, transcolor);
|
|
|
|
SelectObject(hdcTemp, bitmapOrig);
|
|
DeleteObject(bitmapTemp);
|
|
}
|
|
DeleteDC(hdcTemp);
|
|
return result;
|
|
}
|
|
else
|
|
{
|
|
return UXTHEME_StretchBlt (hdcDst, nXOriginDst, nYOriginDst, nWidthDst, nHeightDst,
|
|
hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc,
|
|
transparent, transcolor);
|
|
}
|
|
}
|
|
|
|
/***********************************************************************
|
|
* UXTHEME_DrawImageBackground
|
|
*
|
|
* Draw an imagefile background
|
|
*/
|
|
static HRESULT UXTHEME_DrawImageBackground(HDC hdc, HBITMAP bmpSrc, RECT *prcSrc, INT transparent,
|
|
COLORREF transparentcolor, BOOL borderonly, int sizingtype, MARGINS *psm, RECT *pRect)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
HBITMAP bmpSrcResized = NULL;
|
|
HGDIOBJ oldSrc;
|
|
HDC hdcSrc, hdcOrigSrc = NULL;
|
|
RECT rcDst;
|
|
POINT dstSize;
|
|
POINT srcSize;
|
|
RECT rcSrc;
|
|
MARGINS sm;
|
|
|
|
rcDst = *pRect;
|
|
rcSrc = *prcSrc;
|
|
sm = *psm;
|
|
|
|
hdcSrc = CreateCompatibleDC(hdc);
|
|
if(!hdcSrc) {
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
return hr;
|
|
}
|
|
oldSrc = SelectObject(hdcSrc, bmpSrc);
|
|
|
|
dstSize.x = rcDst.right-rcDst.left;
|
|
dstSize.y = rcDst.bottom-rcDst.top;
|
|
srcSize.x = rcSrc.right-rcSrc.left;
|
|
srcSize.y = rcSrc.bottom-rcSrc.top;
|
|
|
|
if(sizingtype == ST_TRUESIZE) {
|
|
if(!UXTHEME_StretchBlt(hdc, rcDst.left, rcDst.top, dstSize.x, dstSize.y,
|
|
hdcSrc, rcSrc.left, rcSrc.top, srcSize.x, srcSize.y,
|
|
transparent, transparentcolor))
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
else {
|
|
HDC hdcDst = NULL;
|
|
POINT org;
|
|
|
|
dstSize.x = abs(dstSize.x);
|
|
dstSize.y = abs(dstSize.y);
|
|
|
|
/* Resize source image if destination smaller than margins */
|
|
#ifndef __REACTOS__
|
|
/* Revert Wine Commit 2b650fa as it breaks themed Explorer Toolbar Separators
|
|
FIXME: Revisit this when the bug is fixed. CORE-9636 and Wine Bug #38538 */
|
|
if (sm.cyTopHeight + sm.cyBottomHeight > dstSize.y || sm.cxLeftWidth + sm.cxRightWidth > dstSize.x) {
|
|
if (sm.cyTopHeight + sm.cyBottomHeight > dstSize.y) {
|
|
sm.cyTopHeight = MulDiv(sm.cyTopHeight, dstSize.y, srcSize.y);
|
|
sm.cyBottomHeight = dstSize.y - sm.cyTopHeight;
|
|
srcSize.y = dstSize.y;
|
|
}
|
|
|
|
if (sm.cxLeftWidth + sm.cxRightWidth > dstSize.x) {
|
|
sm.cxLeftWidth = MulDiv(sm.cxLeftWidth, dstSize.x, srcSize.x);
|
|
sm.cxRightWidth = dstSize.x - sm.cxLeftWidth;
|
|
srcSize.x = dstSize.x;
|
|
}
|
|
|
|
hdcOrigSrc = hdcSrc;
|
|
hdcSrc = CreateCompatibleDC(NULL);
|
|
bmpSrcResized = CreateBitmap(srcSize.x, srcSize.y, 1, 32, NULL);
|
|
SelectObject(hdcSrc, bmpSrcResized);
|
|
|
|
UXTHEME_StretchBlt(hdcSrc, 0, 0, srcSize.x, srcSize.y, hdcOrigSrc, rcSrc.left, rcSrc.top,
|
|
rcSrc.right - rcSrc.left, rcSrc.bottom - rcSrc.top, transparent, transparentcolor);
|
|
|
|
rcSrc.left = 0;
|
|
rcSrc.top = 0;
|
|
rcSrc.right = srcSize.x;
|
|
rcSrc.bottom = srcSize.y;
|
|
}
|
|
#endif /* __REACTOS__ */
|
|
|
|
hdcDst = hdc;
|
|
OffsetViewportOrgEx(hdcDst, rcDst.left, rcDst.top, &org);
|
|
|
|
/* Upper left corner */
|
|
if(!UXTHEME_Blt(hdcDst, 0, 0, sm.cxLeftWidth, sm.cyTopHeight,
|
|
hdcSrc, rcSrc.left, rcSrc.top,
|
|
transparent, transparentcolor)) {
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto draw_error;
|
|
}
|
|
/* Upper right corner */
|
|
if(!UXTHEME_Blt (hdcDst, dstSize.x-sm.cxRightWidth, 0,
|
|
sm.cxRightWidth, sm.cyTopHeight,
|
|
hdcSrc, rcSrc.right-sm.cxRightWidth, rcSrc.top,
|
|
transparent, transparentcolor)) {
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto draw_error;
|
|
}
|
|
/* Lower left corner */
|
|
if(!UXTHEME_Blt (hdcDst, 0, dstSize.y-sm.cyBottomHeight,
|
|
sm.cxLeftWidth, sm.cyBottomHeight,
|
|
hdcSrc, rcSrc.left, rcSrc.bottom-sm.cyBottomHeight,
|
|
transparent, transparentcolor)) {
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto draw_error;
|
|
}
|
|
/* Lower right corner */
|
|
if(!UXTHEME_Blt (hdcDst, dstSize.x-sm.cxRightWidth, dstSize.y-sm.cyBottomHeight,
|
|
sm.cxRightWidth, sm.cyBottomHeight,
|
|
hdcSrc, rcSrc.right-sm.cxRightWidth, rcSrc.bottom-sm.cyBottomHeight,
|
|
transparent, transparentcolor)) {
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto draw_error;
|
|
}
|
|
|
|
if ((sizingtype == ST_STRETCH) || (sizingtype == ST_TILE)) {
|
|
int destCenterWidth = dstSize.x - (sm.cxLeftWidth + sm.cxRightWidth);
|
|
int srcCenterWidth = srcSize.x - (sm.cxLeftWidth + sm.cxRightWidth);
|
|
int destCenterHeight = dstSize.y - (sm.cyTopHeight + sm.cyBottomHeight);
|
|
int srcCenterHeight = srcSize.y - (sm.cyTopHeight + sm.cyBottomHeight);
|
|
|
|
if(destCenterWidth > 0) {
|
|
/* Center top */
|
|
if(!UXTHEME_SizedBlt (hdcDst, sm.cxLeftWidth, 0,
|
|
destCenterWidth, sm.cyTopHeight,
|
|
hdcSrc, rcSrc.left+sm.cxLeftWidth, rcSrc.top,
|
|
srcCenterWidth, sm.cyTopHeight,
|
|
sizingtype, transparent, transparentcolor)) {
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto draw_error;
|
|
}
|
|
/* Center bottom */
|
|
if(!UXTHEME_SizedBlt (hdcDst, sm.cxLeftWidth, dstSize.y-sm.cyBottomHeight,
|
|
destCenterWidth, sm.cyBottomHeight,
|
|
hdcSrc, rcSrc.left+sm.cxLeftWidth, rcSrc.bottom-sm.cyBottomHeight,
|
|
srcCenterWidth, sm.cyBottomHeight,
|
|
sizingtype, transparent, transparentcolor)) {
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto draw_error;
|
|
}
|
|
}
|
|
if(destCenterHeight > 0) {
|
|
/* Left center */
|
|
if(!UXTHEME_SizedBlt (hdcDst, 0, sm.cyTopHeight,
|
|
sm.cxLeftWidth, destCenterHeight,
|
|
hdcSrc, rcSrc.left, rcSrc.top+sm.cyTopHeight,
|
|
sm.cxLeftWidth, srcCenterHeight,
|
|
sizingtype,
|
|
transparent, transparentcolor)) {
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto draw_error;
|
|
}
|
|
/* Right center */
|
|
if(!UXTHEME_SizedBlt (hdcDst, dstSize.x-sm.cxRightWidth, sm.cyTopHeight,
|
|
sm.cxRightWidth, destCenterHeight,
|
|
hdcSrc, rcSrc.right-sm.cxRightWidth, rcSrc.top+sm.cyTopHeight,
|
|
sm.cxRightWidth, srcCenterHeight,
|
|
sizingtype, transparent, transparentcolor)) {
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto draw_error;
|
|
}
|
|
}
|
|
if(destCenterHeight > 0 && destCenterWidth > 0) {
|
|
if(!borderonly) {
|
|
/* Center */
|
|
if(!UXTHEME_SizedBlt (hdcDst, sm.cxLeftWidth, sm.cyTopHeight,
|
|
destCenterWidth, destCenterHeight,
|
|
hdcSrc, rcSrc.left+sm.cxLeftWidth, rcSrc.top+sm.cyTopHeight,
|
|
srcCenterWidth, srcCenterHeight,
|
|
sizingtype, transparent, transparentcolor)) {
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto draw_error;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
draw_error:
|
|
SetViewportOrgEx (hdcDst, org.x, org.y, NULL);
|
|
}
|
|
SelectObject(hdcSrc, oldSrc);
|
|
DeleteDC(hdcSrc);
|
|
if (bmpSrcResized) DeleteObject(bmpSrcResized);
|
|
if (hdcOrigSrc) DeleteDC(hdcOrigSrc);
|
|
*pRect = rcDst;
|
|
return hr;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
GdiDrawStream(HDC dc, ULONG l, PGDI_DRAW_STREAM pDS)
|
|
{
|
|
if (!pDS || l != sizeof(*pDS))
|
|
{
|
|
DPRINT1("GdiDrawStream: Invalid params\n");
|
|
return 0;
|
|
}
|
|
|
|
if (pDS->signature != 0x44727753 ||
|
|
pDS->reserved != 0 ||
|
|
pDS->unknown1 != 1 ||
|
|
pDS->unknown2 != 9)
|
|
{
|
|
DPRINT1("GdiDrawStream: Got unknown pDS data\n");
|
|
return 0;
|
|
}
|
|
|
|
{
|
|
MARGINS sm = {pDS->leftSizingMargin, pDS->rightSizingMargin, pDS->topSizingMargin, pDS->bottomSizingMargin};
|
|
INT transparent = 0;
|
|
int sizingtype;
|
|
|
|
if (pDS->drawOption & DS_TRANSPARENTALPHA)
|
|
transparent = ALPHABLEND_FULL;
|
|
else if (pDS->drawOption & DS_TRANSPARENTCLR)
|
|
transparent = ALPHABLEND_BINARY;
|
|
else
|
|
transparent = ALPHABLEND_NONE;
|
|
|
|
if (pDS->drawOption & DS_TILE)
|
|
sizingtype = ST_TILE;
|
|
else if (pDS->drawOption & DS_TRUESIZE)
|
|
sizingtype = ST_TRUESIZE;
|
|
else
|
|
sizingtype = ST_STRETCH;
|
|
|
|
if (pDS->rcDest.right < pDS->rcDest.left || pDS->rcDest.bottom < pDS->rcDest.top)
|
|
return 0;
|
|
|
|
if (sm.cxLeftWidth + sm.cxRightWidth > pDS->rcDest.right - pDS->rcDest.left)
|
|
{
|
|
sm.cxLeftWidth = sm.cxRightWidth = 0;
|
|
}
|
|
|
|
if (sm.cyTopHeight + sm.cyBottomHeight > pDS->rcDest.bottom - pDS->rcDest.top)
|
|
{
|
|
sm.cyTopHeight = sm.cyBottomHeight = 0;
|
|
}
|
|
|
|
UXTHEME_DrawImageBackground(pDS->hDC,
|
|
pDS->hImage,
|
|
&pDS->rcSrc,
|
|
transparent,
|
|
pDS->crTransparent,
|
|
FALSE,
|
|
sizingtype,
|
|
&sm,
|
|
&pDS->rcDest);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
GdiValidateHandle(HGDIOBJ hobj)
|
|
{
|
|
PGDI_TABLE_ENTRY Entry = GdiHandleTable + GDI_HANDLE_GET_INDEX(hobj);
|
|
if ( (Entry->Type & GDI_ENTRY_BASETYPE_MASK) != 0 &&
|
|
( (Entry->Type << GDI_ENTRY_UPPER_SHIFT) & GDI_HANDLE_TYPE_MASK ) ==
|
|
GDI_HANDLE_GET_TYPE(hobj) )
|
|
{
|
|
HANDLE pid = (HANDLE)((ULONG_PTR)Entry->ProcessId & ~0x1);
|
|
if(pid == NULL || pid == CurrentProcessId)
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
HGDIOBJ
|
|
WINAPI
|
|
GdiFixUpHandle(HGDIOBJ hGdiObj)
|
|
{
|
|
PGDI_TABLE_ENTRY Entry;
|
|
|
|
if (GDI_HANDLE_GET_UPPER(hGdiObj))
|
|
{
|
|
return hGdiObj;
|
|
}
|
|
|
|
/* FIXME is this right ?? */
|
|
|
|
Entry = GdiHandleTable + GDI_HANDLE_GET_INDEX(hGdiObj);
|
|
|
|
/* Rebuild handle for Object */
|
|
return (HGDIOBJ)(((ULONG_PTR)(hGdiObj)) | (Entry->Type << GDI_ENTRY_UPPER_SHIFT));
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
PVOID
|
|
WINAPI
|
|
GdiQueryTable(VOID)
|
|
{
|
|
return (PVOID)GdiHandleTable;
|
|
}
|
|
|
|
BOOL GdiGetHandleUserData(HGDIOBJ hGdiObj, DWORD ObjectType, PVOID *UserData)
|
|
{
|
|
PGDI_TABLE_ENTRY Entry = GdiHandleTable + GDI_HANDLE_GET_INDEX(hGdiObj);
|
|
|
|
/* Check if twe have the correct type */
|
|
if (GDI_HANDLE_GET_TYPE(hGdiObj) != ObjectType ||
|
|
((Entry->Type << GDI_ENTRY_UPPER_SHIFT) & GDI_HANDLE_TYPE_MASK) != ObjectType ||
|
|
(Entry->Type & GDI_ENTRY_BASETYPE_MASK) != (ObjectType & GDI_ENTRY_BASETYPE_MASK))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
/* Check if we are the owner */
|
|
if ((HANDLE)((ULONG_PTR)Entry->ProcessId & ~0x1) != CurrentProcessId)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
*UserData = Entry->UserData;
|
|
return TRUE;
|
|
}
|
|
|
|
PLDC
|
|
FASTCALL
|
|
GdiGetLDC(HDC hdc)
|
|
{
|
|
PDC_ATTR pdcattr;
|
|
|
|
/* Get the DC attribute */
|
|
pdcattr = GdiGetDcAttr(hdc);
|
|
if (pdcattr == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/* Return the LDC pointer */
|
|
return pdcattr->pvLDC;
|
|
}
|
|
|
|
BOOL
|
|
FASTCALL
|
|
GdiSetLDC(HDC hdc, PVOID pvLDC)
|
|
{
|
|
PDC_ATTR pdcattr;
|
|
|
|
/* Get the DC attribute */
|
|
pdcattr = GdiGetDcAttr(hdc);
|
|
if (pdcattr == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
/* Set the LDC pointer */
|
|
pdcattr->pvLDC = pvLDC;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID GdiSAPCallback(PLDC pldc)
|
|
{
|
|
DWORD Time, NewTime = GetTickCount();
|
|
|
|
Time = NewTime - pldc->CallBackTick;
|
|
|
|
if ( Time < SAPCALLBACKDELAY) return;
|
|
|
|
pldc->CallBackTick = NewTime;
|
|
|
|
if ( !pldc->pAbortProc(pldc->hDC, 0) )
|
|
{
|
|
CancelDC(pldc->hDC);
|
|
AbortDoc(pldc->hDC);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
DWORD
|
|
WINAPI
|
|
GdiSetBatchLimit(DWORD Limit)
|
|
{
|
|
DWORD OldLimit = GDI_BatchLimit;
|
|
|
|
if ( (!Limit) ||
|
|
(Limit >= GDI_BATCH_LIMIT))
|
|
{
|
|
return Limit;
|
|
}
|
|
|
|
GdiFlush();
|
|
GDI_BatchLimit = Limit;
|
|
return OldLimit;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
DWORD
|
|
WINAPI
|
|
GdiGetBatchLimit(VOID)
|
|
{
|
|
return GDI_BatchLimit;
|
|
}
|
|
|
|
|
|
/*
|
|
* @implemented
|
|
*/
|
|
VOID
|
|
WINAPI
|
|
GdiSetLastError(DWORD dwErrCode)
|
|
{
|
|
NtCurrentTeb()->LastErrorValue = (ULONG) dwErrCode;
|
|
}
|
|
|
|
HGDIOBJ
|
|
FASTCALL
|
|
hGetPEBHandle(HANDLECACHETYPE Type, COLORREF cr)
|
|
{
|
|
int Number, Offset, MaxNum, GdiType;
|
|
HANDLE Lock;
|
|
HGDIOBJ Handle = NULL;
|
|
|
|
Lock = InterlockedCompareExchangePointer( (PVOID*)&GdiHandleCache->ulLock,
|
|
NtCurrentTeb(),
|
|
NULL );
|
|
|
|
if (Lock) return Handle;
|
|
|
|
Number = GdiHandleCache->ulNumHandles[Type];
|
|
|
|
if (Type == hctBrushHandle)
|
|
{
|
|
Offset = 0;
|
|
MaxNum = CACHE_BRUSH_ENTRIES;
|
|
GdiType = GDILoObjType_LO_BRUSH_TYPE;
|
|
}
|
|
else if (Type == hctPenHandle)
|
|
{
|
|
Offset = CACHE_BRUSH_ENTRIES;
|
|
MaxNum = CACHE_PEN_ENTRIES;
|
|
GdiType = GDILoObjType_LO_PEN_TYPE;
|
|
}
|
|
else if (Type == hctRegionHandle)
|
|
{
|
|
Offset = CACHE_BRUSH_ENTRIES+CACHE_PEN_ENTRIES;
|
|
MaxNum = CACHE_REGION_ENTRIES;
|
|
GdiType = GDILoObjType_LO_REGION_TYPE;
|
|
}
|
|
else // Font is not supported here.
|
|
{
|
|
return Handle;
|
|
}
|
|
|
|
if ( Number && Number <= MaxNum )
|
|
{
|
|
PBRUSH_ATTR pBrush_Attr;
|
|
HGDIOBJ *hPtr;
|
|
hPtr = GdiHandleCache->Handle + Offset;
|
|
Handle = hPtr[Number - 1];
|
|
|
|
if (GdiGetHandleUserData( Handle, GdiType, (PVOID) &pBrush_Attr))
|
|
{
|
|
if (pBrush_Attr->AttrFlags & ATTR_CACHED)
|
|
{
|
|
DPRINT("Get Handle! Type %d Count %lu PEB 0x%p\n", Type, GdiHandleCache->ulNumHandles[Type], NtCurrentTeb()->ProcessEnvironmentBlock);
|
|
pBrush_Attr->AttrFlags &= ~ATTR_CACHED;
|
|
hPtr[Number - 1] = NULL;
|
|
GdiHandleCache->ulNumHandles[Type]--;
|
|
if ( Type == hctBrushHandle ) // Handle only brush.
|
|
{
|
|
if ( pBrush_Attr->lbColor != cr )
|
|
{
|
|
pBrush_Attr->lbColor = cr ;
|
|
pBrush_Attr->AttrFlags |= ATTR_NEW_COLOR;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Handle = NULL;
|
|
}
|
|
}
|
|
(void)InterlockedExchangePointer((PVOID*)&GdiHandleCache->ulLock, Lock);
|
|
return Handle;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
bMakePathNameW(LPWSTR lpBuffer,LPCWSTR lpFileName,LPWSTR *lpFilePart,DWORD unknown)
|
|
{
|
|
UNIMPLEMENTED;
|
|
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* @implemented
|
|
* Synchronized with WINE dlls/gdi32/driver.c
|
|
*/
|
|
DEVMODEW *
|
|
WINAPI
|
|
GdiConvertToDevmodeW(const DEVMODEA *dmA)
|
|
{
|
|
DEVMODEW *dmW;
|
|
WORD dmW_size, dmA_size;
|
|
|
|
dmA_size = dmA->dmSize;
|
|
|
|
/* this is the minimal dmSize that XP accepts */
|
|
if (dmA_size < FIELD_OFFSET(DEVMODEA, dmFields))
|
|
return NULL;
|
|
|
|
if (dmA_size > sizeof(DEVMODEA))
|
|
dmA_size = sizeof(DEVMODEA);
|
|
|
|
dmW_size = dmA_size + CCHDEVICENAME;
|
|
if (dmA_size >= FIELD_OFFSET(DEVMODEA, dmFormName) + CCHFORMNAME)
|
|
dmW_size += CCHFORMNAME;
|
|
|
|
dmW = HeapAlloc(GetProcessHeap(), 0, dmW_size + dmA->dmDriverExtra);
|
|
if (!dmW) return NULL;
|
|
|
|
MultiByteToWideChar(CP_ACP, 0, (const char*) dmA->dmDeviceName, -1,
|
|
dmW->dmDeviceName, CCHDEVICENAME);
|
|
/* copy slightly more, to avoid long computations */
|
|
memcpy(&dmW->dmSpecVersion, &dmA->dmSpecVersion, dmA_size - CCHDEVICENAME);
|
|
|
|
if (dmA_size >= FIELD_OFFSET(DEVMODEA, dmFormName) + CCHFORMNAME)
|
|
{
|
|
if (dmA->dmFields & DM_FORMNAME)
|
|
MultiByteToWideChar(CP_ACP, 0, (const char*) dmA->dmFormName, -1,
|
|
dmW->dmFormName, CCHFORMNAME);
|
|
else
|
|
dmW->dmFormName[0] = 0;
|
|
|
|
if (dmA_size > FIELD_OFFSET(DEVMODEA, dmLogPixels))
|
|
memcpy(&dmW->dmLogPixels, &dmA->dmLogPixels, dmA_size - FIELD_OFFSET(DEVMODEA, dmLogPixels));
|
|
}
|
|
|
|
if (dmA->dmDriverExtra)
|
|
memcpy((char *)dmW + dmW_size, (const char *)dmA + dmA_size, dmA->dmDriverExtra);
|
|
|
|
dmW->dmSize = dmW_size;
|
|
|
|
return dmW;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
GdiRealizationInfo(HDC hdc,
|
|
PREALIZATION_INFO pri)
|
|
{
|
|
// ATM we do not support local font data and Language Pack.
|
|
return NtGdiGetRealizationInfo(hdc, pri, (HFONT) NULL);
|
|
}
|
|
|
|
|
|
/*
|
|
* @halfplemented
|
|
*/
|
|
VOID WINAPI GdiInitializeLanguagePack(DWORD InitParam)
|
|
{
|
|
/* Lpk function pointers to be passed to user32 */
|
|
#if 0
|
|
FARPROC hookfuncs[4];
|
|
#endif
|
|
|
|
#ifdef LANGPACK
|
|
if (!LoadLPK(LPK_INIT)) // no lpk found!
|
|
#endif
|
|
return;
|
|
|
|
/* Call InitializeLpkHooks with 4 procedure addresses
|
|
loaded from lpk.dll but currently only one of them is currently implemented.
|
|
Then InitializeLpkHooks (in user32) uses these to replace certain internal functions
|
|
and ORs a DWORD being used also by ClientThreadSetup and calls
|
|
NtUserOneParam with parameter 54 which is ONEPARAM_ROUTINE_REGISTERLPK
|
|
which most likely changes the value of dwLpkEntryPoints in the
|
|
PROCESSINFO struct */
|
|
|
|
#if 0
|
|
hookfuncs[0] = GetProcAddress(hLpk, "LpkPSMTextOut");
|
|
InitializeLpkHooks(hookfuncs);
|
|
#endif
|
|
|
|
gbLpk = TRUE;
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
GdiAddGlsBounds(HDC hdc,LPRECT prc)
|
|
{
|
|
return NtGdiSetBoundsRect(hdc, prc, DCB_WINDOWMGR|DCB_ACCUMULATE ) ? TRUE : FALSE;
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
GetBoundsRectAlt(HDC hdc,LPRECT prc,UINT flags)
|
|
{
|
|
return NtGdiGetBoundsRect(hdc, prc, flags);
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
SetBoundsRectAlt(HDC hdc,LPRECT prc,UINT flags)
|
|
{
|
|
return NtGdiSetBoundsRect(hdc, prc, flags );
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
BOOL
|
|
WINAPI
|
|
GdiAddGlsRecord(HDC hdc,
|
|
DWORD unknown1,
|
|
LPCSTR unknown2,
|
|
LPRECT unknown3)
|
|
{
|
|
UNIMPLEMENTED;
|
|
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
|
return 0;
|
|
}
|
|
|