reactos/win32ss/gdi/eng/surface.c
Oleg Dubinskiy dc483bd700
[WIN32SS:ENG] EngCreateBitmap: mark created bitmaps as API bitmaps (#4146)
CORE-17561

Set the API_BITMAP flag for bitmaps created by EngCreateBitmap().

This avoids failure when created bitmap is passed to NtGdiSelectBitmap(), since it checks for this flag and fails if it isn't set.
Otherwise, if failure occurs, the bitmap is hot handled properly.
Setting this flag outside of that function is not an option at all, because it is a public (exported) function, and in many cases it's called directly by the caller (in case with MS DDraw stack, it can be called also by dxg.sys from dxgthk!EngCreateBitmap, which directly calls win32k!EngCreateBitmap).

In particular, it allows Windows XP/2003 DirectDraw stack (ddraw.dll & dxg.sys) to properly work in ReactOS (in software emulation mode), even on real hardware. So now, a lot of DirectX 1-7 apps and games (whose can work withoug hardware acceleation), are working properly with ddraw.dll + dxg.sys replacement! Also Justin Miller (DarkFire01) confirmed that even Direct3D software emulation now also works correctly on real hardware!
2021-12-29 16:51:23 +01:00

639 lines
15 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* PURPOSE: GDI Driver Surace Functions
* FILE: win32ss/gdi/eng/surface.c
* PROGRAMERS: Jason Filby
* Timo Kreuzer
* TESTING TO BE DONE:
* - Create a GDI bitmap with all formats, perform all drawing operations on them, render to VGA surface
* refer to \test\microwin\src\engine\devdraw.c for info on correct pixel plotting for various formats
*/
#include <win32k.h>
#define NDEBUG
#include <debug.h>
LONG giUniqueSurface = 0;
UCHAR
gajBitsPerFormat[11] =
{
0, /* 0: unused */
1, /* 1: BMF_1BPP */
4, /* 2: BMF_4BPP */
8, /* 3: BMF_8BPP */
16, /* 4: BMF_16BPP */
24, /* 5: BMF_24BPP */
32, /* 6: BMF_32BPP */
4, /* 7: BMF_4RLE */
8, /* 8: BMF_8RLE */
0, /* 9: BMF_JPEG */
0, /* 10: BMF_PNG */
};
ULONG
FASTCALL
BitmapFormat(ULONG cBits, ULONG iCompression)
{
switch (iCompression)
{
case BI_RGB:
/* Fall through */
case BI_BITFIELDS:
if (cBits <= 1) return BMF_1BPP;
if (cBits <= 4) return BMF_4BPP;
if (cBits <= 8) return BMF_8BPP;
if (cBits <= 16) return BMF_16BPP;
if (cBits <= 24) return BMF_24BPP;
if (cBits <= 32) return BMF_32BPP;
return 0;
case BI_RLE4:
return BMF_4RLE;
case BI_RLE8:
return BMF_8RLE;
default:
return 0;
}
}
VOID
NTAPI
SURFACE_vCleanup(PVOID ObjectBody)
{
PSURFACE psurf = (PSURFACE)ObjectBody;
PVOID pvBits = psurf->SurfObj.pvBits;
/* Check if the surface has bits */
if (pvBits)
{
/* Only bitmaps can have bits */
ASSERT(psurf->SurfObj.iType == STYPE_BITMAP);
/* Check if it is a DIB section */
if (psurf->hDIBSection)
{
/* Unmap the section view */
EngUnmapSectionView(pvBits, psurf->dwOffset, psurf->hSecure);
}
else if (psurf->SurfObj.fjBitmap & BMF_USERMEM)
{
/* Bitmap was allocated from usermode memory */
EngFreeUserMem(pvBits);
}
else if (psurf->SurfObj.fjBitmap & BMF_KMSECTION)
{
/* Bitmap was allocated from a kernel section */
if (!EngFreeSectionMem(NULL, pvBits))
{
DPRINT1("EngFreeSectionMem failed for %p!\n", pvBits);
// Should we BugCheck here?
ASSERT(FALSE);
}
}
else if (psurf->SurfObj.fjBitmap & BMF_POOLALLOC)
{
/* Free a pool allocation */
EngFreeMem(pvBits);
}
}
/* Free palette */
if(psurf->ppal)
{
PALETTE_ShareUnlockPalette(psurf->ppal);
}
}
PSURFACE
NTAPI
SURFACE_AllocSurface(
_In_ USHORT iType,
_In_ ULONG cx,
_In_ ULONG cy,
_In_ ULONG iFormat,
_In_ ULONG fjBitmap,
_In_opt_ ULONG cjWidth,
_In_opt_ ULONG cjBufSize,
_In_opt_ PVOID pvBits)
{
ULONG cBitsPixel, cjBits, cjObject;
PSURFACE psurf;
SURFOBJ *pso;
PVOID pvSection;
NT_ASSERT(!pvBits || (iType == STYPE_BITMAP));
NT_ASSERT((iFormat <= BMF_32BPP) || (cjBufSize != 0));
NT_ASSERT((LONG)cy > 0);
/* Verify format */
if ((iFormat < BMF_1BPP) || (iFormat > BMF_PNG))
{
DPRINT1("Invalid bitmap format: %lu\n", iFormat);
return NULL;
}
/* Get bits per pixel from the format */
cBitsPixel = gajBitsPerFormat[iFormat];
/* Are bits and a width in bytes given? */
if (pvBits && cjWidth)
{
/* Align the width (Windows compatibility, drivers expect that) */
cjWidth = WIDTH_BYTES_ALIGN32((cjWidth << 3) / cBitsPixel, cBitsPixel);
}
else
{
/* Calculate width from the bitmap width in pixels */
cjWidth = WIDTH_BYTES_ALIGN32(cx, cBitsPixel);
}
/* Is this an uncompressed format? */
if (iFormat <= BMF_32BPP)
{
/* Calculate the correct bitmap size in bytes */
if (!NT_SUCCESS(RtlULongMult(cjWidth, cy, &cjBits)))
{
DPRINT1("Overflow calculating size: cjWidth %lu, cy %lu\n",
cjWidth, cy);
return NULL;
}
/* Did we get a buffer and size? */
if ((pvBits != NULL) && (cjBufSize != 0))
{
/* Make sure the buffer is large enough */
if (cjBufSize < cjBits)
{
DPRINT1("Buffer is too small, required: %lu, got %lu\n",
cjBits, cjBufSize);
return NULL;
}
}
}
else
{
/* Compressed format, use the provided size */
NT_ASSERT(cjBufSize != 0);
cjBits = cjBufSize;
}
/* Check if we need an extra large object */
if ((iType == STYPE_BITMAP) && (pvBits == NULL) &&
!(fjBitmap & BMF_USERMEM) && !(fjBitmap & BMF_KMSECTION))
{
/* Allocate an object large enough to hold the bits */
cjObject = sizeof(SURFACE) + cjBits;
}
else
{
/* Otherwise just allocate the SURFACE structure */
cjObject = sizeof(SURFACE);
}
/* Check for arithmetic overflow */
if (cjObject < sizeof(SURFACE))
{
/* Fail! */
DPRINT1("Overflow calculating cjObject: cjBits %lu\n", cjBits);
return NULL;
}
/* Allocate a SURFACE object */
psurf = (PSURFACE)GDIOBJ_AllocObjWithHandle(GDI_OBJECT_TYPE_BITMAP, cjObject);
if (!psurf)
{
return NULL;
}
/* Initialize the basic fields */
pso = &psurf->SurfObj;
pso->hsurf = psurf->BaseObject.hHmgr;
pso->sizlBitmap.cx = cx;
pso->sizlBitmap.cy = cy;
pso->iBitmapFormat = iFormat;
pso->iType = iType;
pso->fjBitmap = (USHORT)fjBitmap;
pso->iUniq = InterlockedIncrement(&giUniqueSurface);
pso->cjBits = cjBits;
/* Check if we need a bitmap buffer */
if (iType == STYPE_BITMAP)
{
/* Check if we got one or if we need to allocate one */
if (pvBits != NULL)
{
/* Use the caller provided buffer */
pso->pvBits = pvBits;
}
else if (fjBitmap & BMF_USERMEM)
{
/* User mode memory was requested */
pso->pvBits = EngAllocUserMem(cjBits, 0);
/* Check for failure */
if (!pso->pvBits)
{
GDIOBJ_vDeleteObject(&psurf->BaseObject);
return NULL;
}
}
else if (fjBitmap & BMF_KMSECTION)
{
/* Use a kernel mode section */
pso->pvBits = EngAllocSectionMem(&pvSection,
(fjBitmap & BMF_NOZEROINIT) ?
0 : FL_ZERO_MEMORY,
cjBits, TAG_DIB);
/* Check for failure */
if (!pso->pvBits)
{
GDIOBJ_vDeleteObject(&psurf->BaseObject);
return NULL;
}
/* Free the section already, but keep the mapping */
EngFreeSectionMem(pvSection, NULL);
}
else
{
/* Buffer is after the object */
pso->pvBits = psurf + 1;
/* Zero the buffer, except requested otherwise */
if (!(fjBitmap & BMF_NOZEROINIT))
{
RtlZeroMemory(pso->pvBits, cjBits);
}
}
/* Set pvScan0 and lDelta */
if (fjBitmap & BMF_TOPDOWN)
{
/* Topdown is the normal way */
pso->pvScan0 = pso->pvBits;
pso->lDelta = cjWidth;
}
else
{
/* Inversed bitmap (bottom up) */
pso->pvScan0 = ((PCHAR)pso->pvBits + pso->cjBits - cjWidth);
pso->lDelta = -(LONG)cjWidth;
}
}
else
{
/* There are no bitmap bits */
pso->pvScan0 = pso->pvBits = NULL;
pso->lDelta = 0;
}
/* Assign a default palette and increment its reference count */
SURFACE_vSetPalette(psurf, appalSurfaceDefault[iFormat]);
return psurf;
}
HBITMAP
APIENTRY
EngCreateBitmap(
_In_ SIZEL sizl,
_In_ LONG lWidth,
_In_ ULONG iFormat,
_In_ ULONG fl,
_In_opt_ PVOID pvBits)
{
PSURFACE psurf;
HBITMAP hbmp;
/* Allocate a surface */
psurf = SURFACE_AllocSurface(STYPE_BITMAP,
sizl.cx,
sizl.cy,
iFormat,
fl,
lWidth,
0,
pvBits);
if (!psurf)
{
DPRINT1("SURFACE_AllocSurface failed. (STYPE_BITMAP, sizl.cx=%ld, sizl.cy=%ld, iFormat=%lu, fl=%lu, lWidth=%ld, pvBits=0x%p)\n",
sizl.cx, sizl.cy, iFormat, fl, lWidth, pvBits);
return NULL;
}
/* Get the handle for the bitmap */
hbmp = (HBITMAP)psurf->SurfObj.hsurf;
/* Mark as API bitmap */
psurf->flags = API_BITMAP;
/* Set public ownership */
GDIOBJ_vSetObjectOwner(&psurf->BaseObject, GDI_OBJ_HMGR_PUBLIC);
/* Unlock the surface and return */
SURFACE_UnlockSurface(psurf);
return hbmp;
}
/*
* @implemented
*/
HBITMAP
APIENTRY
EngCreateDeviceBitmap(
_In_ DHSURF dhsurf,
_In_ SIZEL sizl,
_In_ ULONG iFormat)
{
PSURFACE psurf;
HBITMAP hbmp;
/* Allocate a surface */
psurf = SURFACE_AllocSurface(STYPE_DEVBITMAP,
sizl.cx,
sizl.cy,
iFormat,
0,
0,
0,
NULL);
if (!psurf)
{
DPRINT1("SURFACE_AllocSurface failed. (STYPE_DEVBITMAP, sizl.cx=%ld, sizl.cy=%ld, iFormat=%lu)\n",
sizl.cx, sizl.cy, iFormat);
return NULL;
}
/* Set the device handle */
psurf->SurfObj.dhsurf = dhsurf;
/* Set public ownership */
GDIOBJ_vSetObjectOwner(&psurf->BaseObject, GDI_OBJ_HMGR_PUBLIC);
/* Get the handle for the bitmap */
hbmp = (HBITMAP)psurf->SurfObj.hsurf;
/* Unlock the surface and return */
SURFACE_UnlockSurface(psurf);
return hbmp;
}
HSURF
APIENTRY
EngCreateDeviceSurface(
_In_ DHSURF dhsurf,
_In_ SIZEL sizl,
_In_ ULONG iFormat)
{
PSURFACE psurf;
HSURF hsurf;
/* Allocate a surface */
psurf = SURFACE_AllocSurface(STYPE_DEVICE,
sizl.cx,
sizl.cy,
iFormat,
0,
0,
0,
NULL);
if (!psurf)
{
DPRINT1("SURFACE_AllocSurface failed. (STYPE_DEVICE, sizl.cx=%ld, sizl.cy=%ld, iFormat=%lu)\n",
sizl.cx, sizl.cy, iFormat);
return NULL;
}
/* Set the device handle */
psurf->SurfObj.dhsurf = dhsurf;
/* Set public ownership */
GDIOBJ_vSetObjectOwner(&psurf->BaseObject, GDI_OBJ_HMGR_PUBLIC);
/* Get the handle for the surface */
hsurf = psurf->SurfObj.hsurf;
/* Unlock the surface and return */
SURFACE_UnlockSurface(psurf);
return hsurf;
}
BOOL
APIENTRY
EngAssociateSurface(
_In_ HSURF hsurf,
_In_ HDEV hdev,
_In_ FLONG flHooks)
{
SURFOBJ *pso;
PSURFACE psurf;
PDEVOBJ* ppdev;
PPALETTE ppal;
ppdev = (PDEVOBJ*)hdev;
/* Lock the surface */
psurf = SURFACE_ShareLockSurface(hsurf);
if (!psurf)
{
return FALSE;
}
pso = &psurf->SurfObj;
/* Associate the hdev */
pso->hdev = hdev;
pso->dhpdev = ppdev->dhpdev;
/* Hook up specified functions */
psurf->flags &= ~HOOK_FLAGS;
psurf->flags |= (flHooks & HOOK_FLAGS);
/* Assign the PDEV's palette */
ppal = PALETTE_ShareLockPalette(ppdev->devinfo.hpalDefault);
SURFACE_vSetPalette(psurf, ppal);
PALETTE_ShareUnlockPalette(ppal);
SURFACE_ShareUnlockSurface(psurf);
return TRUE;
}
BOOL
APIENTRY
EngModifySurface(
_In_ HSURF hsurf,
_In_ HDEV hdev,
_In_ FLONG flHooks,
_In_ FLONG flSurface,
_In_ DHSURF dhsurf,
_In_ PVOID pvScan0,
_In_ LONG lDelta,
_Reserved_ PVOID pvReserved)
{
SURFOBJ *pso;
PSURFACE psurf;
PDEVOBJ* ppdev;
PPALETTE ppal;
/* Lock the surface */
psurf = SURFACE_ShareLockSurface(hsurf);
if (psurf == NULL)
{
DPRINT1("Failed to reference surface %p\n", hsurf);
return FALSE;
}
ppdev = (PDEVOBJ*)hdev;
pso = &psurf->SurfObj;
pso->dhsurf = dhsurf;
/* Associate the hdev */
pso->hdev = hdev;
pso->dhpdev = ppdev->dhpdev;
/* Hook up specified functions */
psurf->flags &= ~HOOK_FLAGS;
psurf->flags |= (flHooks & HOOK_FLAGS);
/* Assign the PDEV's palette */
ppal = PALETTE_ShareLockPalette(ppdev->devinfo.hpalDefault);
SURFACE_vSetPalette(psurf, ppal);
PALETTE_ShareUnlockPalette(ppal);
/* Update surface flags */
if (flSurface & MS_NOTSYSTEMMEMORY)
pso->fjBitmap |= BMF_NOTSYSMEM;
else
pso->fjBitmap &= ~BMF_NOTSYSMEM;
if (flSurface & MS_SHAREDACCESS)
psurf->flags |= SHAREACCESS_SURFACE;
else
psurf->flags &= ~SHAREACCESS_SURFACE;
/* Check if the caller passed bitmap bits */
if ((pvScan0 != NULL) && (lDelta != 0))
{
/* Update the fields */
pso->pvScan0 = pvScan0;
pso->lDelta = lDelta;
/* This is a bitmap now! */
pso->iType = STYPE_BITMAP;
/* Check memory layout */
if (lDelta > 0)
{
/* Topdown is the normal way */
pso->cjBits = lDelta * pso->sizlBitmap.cy;
pso->pvBits = pso->pvScan0;
pso->fjBitmap |= BMF_TOPDOWN;
}
else
{
/* Inversed bitmap (bottom up) */
pso->cjBits = (-lDelta) * pso->sizlBitmap.cy;
pso->pvBits = (PCHAR)pso->pvScan0 - pso->cjBits - lDelta;
pso->fjBitmap &= ~BMF_TOPDOWN;
}
}
else
{
/* Set bits to NULL */
pso->pvBits = NULL;
pso->pvScan0 = NULL;
pso->lDelta = 0;
/* Set appropriate surface type */
if (pso->iType != STYPE_DEVICE)
pso->iType = STYPE_DEVBITMAP;
}
SURFACE_ShareUnlockSurface(psurf);
return TRUE;
}
BOOL
APIENTRY
EngDeleteSurface(
_In_ _Post_ptr_invalid_ HSURF hsurf)
{
PSURFACE psurf;
psurf = SURFACE_ShareLockSurface(hsurf);
if (!psurf)
{
DPRINT1("Could not reference surface %p to delete\n", hsurf);
return FALSE;
}
GDIOBJ_vDeleteObject(&psurf->BaseObject);
return TRUE;
}
BOOL
APIENTRY
EngEraseSurface(
_In_ SURFOBJ *pso,
_In_ RECTL *prcl,
_In_ ULONG iColor)
{
ASSERT(pso);
ASSERT(prcl);
return FillSolid(pso, prcl, iColor);
}
/*
* @implemented
*/
SURFOBJ * APIENTRY
NtGdiEngLockSurface(IN HSURF hsurf)
{
return EngLockSurface(hsurf);
}
SURFOBJ *
APIENTRY
EngLockSurface(
_In_ HSURF hsurf)
{
SURFACE *psurf = SURFACE_ShareLockSurface(hsurf);
return psurf ? &psurf->SurfObj : NULL;
}
__kernel_entry
NTSTATUS
APIENTRY
NtGdiEngUnlockSurface(
_In_ SURFOBJ *pso)
{
UNIMPLEMENTED;
ASSERT(FALSE);
return STATUS_NOT_IMPLEMENTED;
}
VOID
APIENTRY
EngUnlockSurface(
_In_ _Post_ptr_invalid_ SURFOBJ *pso)
{
if (pso != NULL)
{
SURFACE *psurf = CONTAINING_RECORD(pso, SURFACE, SurfObj);
SURFACE_ShareUnlockSurface(psurf);
}
}
/* EOF */