#include "stdafx.h" #include "Starfield.h" #include "Random.h" #include #include #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); }