keygendr32/Keygender32/Starfield.c
2024-03-20 23:09:10 -07:00

308 lines
6.8 KiB
C

#include "stdafx.h"
#include "Starfield.h"
#include "Random.h"
#include <math.h>
#include <time.h>
#define WMU_FRAMERATE_NAME _T("SetFramerate_{6CFC2304-CE89-40c4-881C-988D36FBB978}")
#define DENSITY 3
#define LAYERS 5
const COLORREF crTransColors[] = {
RGB(0x5b, 0xce, 0xfa),
RGB(0xf5, 0xa9, 0xb8),
RGB(0xff, 0xff, 0xff),
};
typedef struct _StarLayer {
HBITMAP hbmFrame;
DOUBLE x;
BYTE alpha;
} StarLayer;
typedef struct _StarState {
StarLayer layers[LAYERS];
HBITMAP hbmColors[3]; // for each stripe
HDC hMemDC; // blits to screen
HDC hBlitDC; // blits to hMemDC
} StarState;
static void
DeleteState(StarState *state)
{
if (state) {
int i;
if (state->hBlitDC) DeleteDC(state->hBlitDC);
if (state->hMemDC) DeleteDC(state->hMemDC);
for (i = 0; i < LAYERS; i++) {
if (state->layers[i].hbmFrame) {
DeleteObject(state->layers[i].hbmFrame);
}
}
for (i = 0; i < 3; i++) {
if (state->hbmColors[i]) DeleteObject(state->hbmColors[i]);
}
// The state is usually stored in the stack, so zero out any
// values that might cause issues in case of reuse
ZeroMemory(state, sizeof(StarState));
}
}
// Initializes the StarState based on the given client area RECT
static void
InitState(HDC hDC,
StarState *state,
LPRECT rcArea)
{
UINT i, // iterators
iStripeSplit; // height per stripe
HBITMAP hbmColors[3];
DWORD dwStarCount = rcArea->bottom * DENSITY;
iStripeSplit = rcArea->bottom / 5;
// init DCs
state->hMemDC = CreateCompatibleDC(hDC);
state->hBlitDC = CreateCompatibleDC(hDC);
// init layer bitmaps
for (i = 0; i < LAYERS; i++) {
HBITMAP hbmOrig;
state->layers[i].hbmFrame = CreateCompatibleBitmap(hDC,
rcArea->right,
rcArea->bottom);
state->layers[i].x = 0;
state->layers[i].alpha = 159 + (i * 24);
// Before stars, fill the frame with the void of space
hbmOrig = SelectObject(
state->hBlitDC,
state->layers[i].hbmFrame);
BitBlt(state->hBlitDC,
0, 0,
rcArea->right, rcArea->bottom,
NULL,
0, 0,
BLACKNESS);
SelectObject(
state->hBlitDC,
hbmOrig);
}
// we only need to init the stripes, thanks to AlphaBlend
for (i = 0; i < 3; i++) {
HBITMAP hbmOrig;
hbmColors[i] = CreateCompatibleBitmap(hDC, 1, 1);
hbmOrig = SelectObject(state->hBlitDC,
hbmColors[i]);
SetPixel(state->hBlitDC,
0, 0, crTransColors[i]);
SelectObject(state->hBlitDC, hbmOrig);
}
// arm the RNG
xorsrand((unsigned)time(NULL));
// create each star, AlphaBlending as we go for ideal effect
for (i = 0; i < dwStarCount; i++) {
int layer = (WORD)(xorrand() >> 48) % LAYERS,
x = (int)((WORD)xorrand()) % (rcArea->right - 4),
y = i / DENSITY,
stripe = y / iStripeSplit,
width = max((int)(((float)layer + 3.0f) / 2.0f), 1);
HBITMAP hbmColor = hbmColors[(stripe < 3) ? stripe :
(stripe == 3) ? 1 : 0],
hbmOrig,
hbmBlitOrig;
BLENDFUNCTION bf = {
AC_SRC_OVER,
0,
state->layers[layer].alpha,
0
};
hbmOrig = SelectObject(state->hMemDC, state->layers[layer].hbmFrame);
hbmBlitOrig = SelectObject(state->hBlitDC, hbmColor);
AlphaBlend(state->hMemDC,
x - (width / 2), y - (width / 2),
width, width,
state->hBlitDC,
0, 0,
1, 1,
bf);
if (x + width > rcArea->right) {
// loop around to make the square look complete
AlphaBlend(state->hMemDC,
0, y,
(x + width) - rcArea->right, width,
state->hBlitDC,
0, 0,
1, 1,
bf);
}
SelectObject(state->hBlitDC, hbmBlitOrig);
SelectObject(state->hMemDC, hbmOrig);
}
for (i = 0; i < 3; i++) {
DeleteObject(hbmColors[i]);
}
return;
}
static void
DrawStars(HDC hDC,
StarState *state,
LPRECT rcArea)
{
// Logic taken from https://github.com/lunasorcery/keygender/blob/main/script.js
UINT i;
HBITMAP hbmOrig,
hbmFrame = CreateCompatibleBitmap(hDC,
rcArea->right,
rcArea->bottom);
static DWORD dwLastRun = 0;
DOUBLE dSpeedMult = 1.0;
hbmOrig = SelectObject(state->hMemDC, hbmFrame);
// Without this, Win98 starts blitting VRAM to our control
BitBlt(state->hMemDC,
0, 0,
rcArea->right, rcArea->bottom,
NULL,
0, 0,
BLACKNESS);
// try to keep a consistent speed across frames
if (dwLastRun != 0) {
DWORD dwElapsed = GetTickCount() - dwLastRun;
dSpeedMult = max(1.0, (dwElapsed / 17.0));
}
dwLastRun = GetTickCount();
for (i = 0; i < LAYERS; i++) {
HBITMAP hbmBlitOrig = NULL;
// split each layer into two pieces, and stitch them together
int widths[2];
widths[0] = (int)state->layers[i].x + rcArea->right;
widths[1] = rcArea->right - widths[0];
hbmBlitOrig = SelectObject(state->hBlitDC, state->layers[i].hbmFrame);
// Left side of the split
TransparentBlt(state->hMemDC,
0, 0,
widths[0], rcArea->bottom,
state->hBlitDC,
widths[1], 0,
widths[0], rcArea->bottom,
0);
// Right side of the split
TransparentBlt(state->hMemDC,
widths[0], 0,
widths[1], rcArea->bottom,
state->hBlitDC,
0, 0,
widths[1], rcArea->bottom,
0);
SelectObject(state->hBlitDC, hbmBlitOrig);
// Update frame location
state->layers[i].x -= ((DOUBLE)i + 1.5) * dSpeedMult;
if (state->layers[i].x + rcArea->right < 0) {
state->layers[i].x += rcArea->right;
}
}
BitBlt(hDC,
rcArea->left,
rcArea->top,
rcArea->right,
rcArea->bottom,
state->hMemDC,
0, 0,
SRCCOPY);
SelectObject(state->hMemDC, hbmOrig);
DeleteObject(hbmFrame);
return;
}
static LRESULT CALLBACK
StarfieldWndProc(HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
static RECT rcClient = {0};
static StarState state = {0};
static HBITMAP hbmColors[3] = {0};
static HDC hDC = NULL;
// Default to 60fps
static DOUBLE fFrameRate = (1.0 / 60) *
1000.0;
switch (uMsg) {
case WM_CREATE:
hDC = GetDC(hWnd);
GetClientRect(hWnd, &rcClient);
InitState(hDC, &state, &rcClient);
/* PASSTHRU */
case WM_TIMER:
// Frame tick!
DrawStars(hDC, &state, &rcClient);
SetTimer(hWnd, 0x69, fFrameRate, NULL);
break;
case WM_DESTROY:
DeleteState(&state);
DeleteDC(hDC);
hDC = NULL;
break;
default:
if (uMsg == WMU_FRAMERATE) {
// Update our frame rate
fFrameRate = (1.0 / (DOUBLE)(LONG)wParam)
* 1000.0;
return 0;
} else {
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
}
return 0;
}
void
InitStarfieldControl(HINSTANCE hInst)
{
WNDCLASSEX wcex = {0};
wcex.cbSize = sizeof(wcex);
wcex.lpfnWndProc = StarfieldWndProc;
wcex.hInstance = hInst;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.lpszClassName = STARFIELD_CLASS;
RegisterClassEx(&wcex);
WMU_FRAMERATE = RegisterWindowMessage(WMU_FRAMERATE_NAME);
}
void
DeinitStarfieldControl(HINSTANCE hInst)
{
UnregisterClass(STARFIELD_CLASS, hInst);
}