Files
nt-lite/code/user/personalities/win32/gdi32.c
T

168 lines
5.5 KiB
C

// gdi32.c
#include <stdint.h>
#include <stddef.h>
#include <ntlite/syscall.h>
#if defined(__SSE2__) || defined(_M_X64) || defined(_M_IX86)
#include <emmintrin.h>
#elif defined(__ARM_NEON) || defined(__ARM_NEON__) || defined(_M_ARM64)
#include <arm_neon.h>
#endif
// ---------------------------------------------------------------------------
// Win32 Base Types
// ---------------------------------------------------------------------------
typedef void* HDC;
typedef unsigned long DWORD;
typedef unsigned short WORD;
typedef unsigned int UINT;
typedef long LONG;
typedef int BOOL;
#define TRUE 1
#define FALSE 0
// StretchDIBits Constants
#define BI_RGB 0L
#define DIB_RGB_COLORS 0
#define SRCCOPY (DWORD)0x00CC0020
// ---------------------------------------------------------------------------
// GDI Structs
// ---------------------------------------------------------------------------
typedef struct tagBITMAPINFOHEADER {
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER;
typedef struct tagRGBQUAD {
unsigned char rgbBlue;
unsigned char rgbGreen;
unsigned char rgbRed;
unsigned char rgbReserved;
} RGBQUAD;
typedef struct tagBITMAPINFO {
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[1];
} BITMAPINFO;
// ---------------------------------------------------------------------------
// GDI Exports
// ---------------------------------------------------------------------------
// Static buffers to persist between function calls
static uint32_t* destBuffer = NULL;
static int* srcX_map = NULL;
static int currentDestCapacity = 0;
static int currentXMapCapacity = 0;
__declspec(dllexport) int StretchDIBits(
HDC hdc,
int xDest, int yDest, int DestWidth, int DestHeight,
int xSrc, int ySrc, int SrcWidth, int SrcHeight,
const void *lpBits, const BITMAPINFO *lpbmi,
UINT iUsage, DWORD rop)
{
(void)hdc; (void)lpbmi; (void)iUsage; (void)rop;
// Safety check to prevent division by zero
if (DestWidth <= 0 || DestHeight <= 0 || SrcWidth <= 0 || SrcHeight <= 0) {
return 0;
}
// Handle trivial case (1:1 copy) to bypass allocation and scaling overhead
if (DestWidth == SrcWidth && DestHeight == SrcHeight && xSrc == 0 && ySrc == 0) {
SysScreenBlit(xDest, yDest, DestWidth, DestHeight, (const uint32_t*)lpBits);
return DestHeight;
}
const uint32_t* srcPixels = (const uint32_t*)lpBits;
// Only reallocate if the new size is LARGER than what we already have
int requiredPixels = DestWidth * DestHeight;
if (requiredPixels > currentDestCapacity) {
SysFree(destBuffer);
destBuffer = (uint32_t*)SysAlloc(requiredPixels * sizeof(uint32_t));
currentDestCapacity = requiredPixels;
}
if (DestWidth > currentXMapCapacity) {
SysFree(srcX_map);
srcX_map = (int*)SysAlloc(DestWidth * sizeof(int));
currentXMapCapacity = DestWidth;
}
for (int dx = 0; dx < DestWidth; ++dx) {
// Nearest neighbor coordinate mapping
srcX_map[dx] = xSrc + (dx * SrcWidth) / DestWidth;
// Clamp to safe bounds
if (srcX_map[dx] < 0) srcX_map[dx] = 0;
if (srcX_map[dx] >= SrcWidth) srcX_map[dx] = SrcWidth - 1;
}
// Process the image row by row
for (int dy = 0; dy < DestHeight; ++dy) {
// Map destination Y to source Y
int sy = ySrc + (dy * SrcHeight) / DestHeight;
if (sy < 0) sy = 0;
if (sy >= SrcHeight) sy = SrcHeight - 1;
// Assumes stride == SrcWidth (tightly packed 32bpp)
const uint32_t* srcRow = srcPixels + (sy * SrcWidth);
uint32_t* dstRow = destBuffer + (dy * DestWidth);
int dx = 0;
#if defined(__SSE2__) || defined(_M_X64)
// Process 4 pixels (16 bytes) per loop iteration using SSE2
for (; dx <= DestWidth - 4; dx += 4) {
// Gather 4 source pixels using our precomputed X map.
// Note: _mm_set_epi32 takes arguments in reverse order (3, 2, 1, 0)
__m128i pixels = _mm_set_epi32(
srcRow[srcX_map[dx + 3]],
srcRow[srcX_map[dx + 2]],
srcRow[srcX_map[dx + 1]],
srcRow[srcX_map[dx + 0]]
);
// Issue a single 128-bit unaligned store
_mm_storeu_si128((__m128i*)&dstRow[dx], pixels);
}
#elif defined(__ARM_NEON) || defined(__ARM_NEON__) || defined(_M_ARM64)
// Process 4 pixels per loop iteration using ARM NEON
for (; dx <= DestWidth - 4; dx += 4) {
// Initialize a vector and populate lanes (avoids stack-memory hop)
uint32x4_t pixels = vdupq_n_u32(0);
pixels = vsetq_lane_u32(srcRow[srcX_map[dx + 0]], pixels, 0);
pixels = vsetq_lane_u32(srcRow[srcX_map[dx + 1]], pixels, 1);
pixels = vsetq_lane_u32(srcRow[srcX_map[dx + 2]], pixels, 2);
pixels = vsetq_lane_u32(srcRow[srcX_map[dx + 3]], pixels, 3);
// Issue a single 128-bit store
vst1q_u32(&dstRow[dx], pixels);
}
#endif
// Epilogue: Handle remaining pixels if DestWidth isn't a multiple of 4
for (; dx < DestWidth; ++dx) {
dstRow[dx] = srcRow[srcX_map[dx]];
}
}
// Direct mapping to kernel screen blit using the stretched buffer
SysScreenBlit(xDest, yDest, DestWidth, DestHeight, destBuffer);
return DestHeight;
}