// ------------------------ WINFIRE.C ------------------------------ // Coded bye Jare of Iguana near Xmas of 1994 // Comments to a880104@zipi.fi.upm.es // My original ASM routine was 200 lines of plain ASM, this Windows // version goes up to near 500 lines!! // You can use this for anything you like, but you must credit me. #include #include #include #include // Fire array size. #define FIREW 128 #define FIREH 96 // Nice size for the window. Frame will take some bits apart, however. #define WINDOWW FIREW*4 #define WINDOWH FIREH*4 // Handy msg structure. typedef struct { HWND hwnd; UINT msg; WPARAM wParam; LPARAM lParam; } tMSG, *pMSG; // Low Level data types. typedef unsigned char byte; typedef unsigned short word; typedef unsigned long dword; typedef byte * Pbyte; typedef word * Pword; typedef dword * Pdword; typedef byte FAR * LPbyte; typedef word FAR * LPword; typedef dword FAR * LPdword; // =============================================================== // WinG DC handling functions. // The stock default bitmap of a WinGDC. static HBITMAP hBitmapMonochrome = 0; // Creates a top-down WinGDC with the specified rgb palette. // Returns the HDC and stores the bitmap pointer in ppSurfaceBits, // if not passed a NULL parameter. static HDC MakeWinGDC(RGBQUAD rgb[256], LPbyte *ppSurfaceBits) { HDC hWinGDC; HBITMAP hBitmapNew; int i; struct { BITMAPINFOHEADER Header; RGBQUAD ColorTable[256]; } Info; // Force top-down 8-bit bitmap of size FIREW*FIREH. Info.Header.biSize = sizeof(Info.Header); Info.Header.biPlanes = 1; Info.Header.biBitCount = 8; Info.Header.biCompression = BI_RGB; Info.Header.biSizeImage = 0; Info.Header.biClrUsed = 0; Info.Header.biClrImportant = 0; Info.Header.biWidth = FIREW; Info.Header.biHeight = -FIREH; // Minus for top-down. for (i = 0; i < 256; i++) Info.ColorTable[i] = rgb[i]; // Create a WinGDC and Bitmap, then select away DC's default // monochrome bitmap. hWinGDC = WinGCreateDC(); if (hWinGDC) { hBitmapNew = WinGCreateBitmap(hWinGDC, (LPBITMAPINFO)&Info, ppSurfaceBits); if (hBitmapNew) { hBitmapMonochrome = (HBITMAP)SelectObject(hWinGDC, hBitmapNew); } else { DeleteDC(hWinGDC); hWinGDC = 0; } } return hWinGDC; } // Finishes the WinGDC. Pretty straightforward. static void EndWinGDC(HDC hWinGDC) { HBITMAP hBitmapOld; if (hWinGDC && hBitmapMonochrome) { // Select the stock 1x1 monochrome bitmap back in hBitmapOld = (HBITMAP)SelectObject(hWinGDC, hBitmapMonochrome); hBitmapMonochrome = 0; DeleteObject(hBitmapOld); DeleteDC(hWinGDC); } } // Creates a palette from the specified colors, but requires // using the system colors. Updates 'rgb' to enable the use of an // identity palette i.e. on exit, rgb contains the effective // palette, that can in turn be used for creating the WinGDC. static HPALETTE CreateIdentityPalette(RGBQUAD aRGB[256], int nColors) { int i; static struct { WORD Version; WORD NumberOfEntries; PALETTEENTRY aEntries[256]; } pal = { 0x300, 256 }; HDC hdc; // For SYSPAL_STATIC, get the twenty static colors into // the array, then fill in the empty spaces with the // given color table hdc = GetDC(NULL); // Get the static colors GetSystemPaletteEntries(hdc, 0, 10, pal.aEntries); GetSystemPaletteEntries(hdc, 246, 10, pal.aEntries + 246); // Set the peFlags of the lower static colors to zero. // And copy the static colors to the user palette so // the caller knows exactly which palette we created. for (i = 0; i < 10; i++) { aRGB[i].rgbRed = pal.aEntries[i].peRed; aRGB[i].rgbGreen = pal.aEntries[i].peGreen; aRGB[i].rgbBlue = pal.aEntries[i].peBlue; pal.aEntries[i].peFlags = 0; } // Fill in the entries from the given color table for (; i < nColors+10; i++) { pal.aEntries[i].peRed = aRGB[i].rgbRed; pal.aEntries[i].peGreen = aRGB[i].rgbGreen; pal.aEntries[i].peBlue = aRGB[i].rgbBlue; pal.aEntries[i].peFlags = PC_RESERVED; } // Mark any empty entries as PC_RESERVED for (; i < 246; i++) { aRGB[i].rgbRed = pal.aEntries[i].peRed; aRGB[i].rgbGreen = pal.aEntries[i].peGreen; aRGB[i].rgbBlue = pal.aEntries[i].peBlue; pal.aEntries[i].peFlags = PC_RESERVED; } // Set the peFlags of the upper static colors to zero, and // copy static colors. for (; i < 256; i++) { aRGB[i].rgbRed = pal.aEntries[i].peRed; aRGB[i].rgbGreen = pal.aEntries[i].peGreen; aRGB[i].rgbBlue = pal.aEntries[i].peBlue; pal.aEntries[i].peFlags = 0; } ReleaseDC(NULL, hdc); // Create the palette return CreatePalette((LOGPALETTE *)&pal); } // =============================================================== // Fire algorithm. Pure C, ASM will do it much faster. static void DoFire(Pbyte to, Pbyte from) { int i, j; from += FIREW; to += 0; // All lines but the first and last. The first will disappear, // and the last must be taken with care for the limits of the // array. for (i = 1; i < FIREH-1; i++) { // Leftmost pixel. *to++ = (byte)(( (word)from[-1] + (word)from[1] + (word)from[-(int)FIREW] + (word)from[FIREW]) >> 2); from++; // Middle pixels. for (j = 1; j < FIREW-1; j++) { *to++ = (byte)(( (word)from[-1] + (word)from[1] + (word)from[-(int)FIREW-1] + (word)from[-(int)FIREW+1] + (word)from[+(int)FIREW-1] + (word)from[+(int)FIREW+1] + (word)from[-(int)FIREW] + (word)from[FIREW] ) >> 3); from++; } // Rightmost pixel. *to++ = (byte)(( (word)from[-1] + (word)from[1] + (word)from[-(int)FIREW] + (word)from[FIREW]) >> 2); from++; } // Bottom line. for (j = 0; j < FIREW-1; j++) { *to++ = (byte)(( (word)from[-1] + (word)from[1] + (word)from[-(int)FIREW] + (word)from[0]) >> 2); from++; } // Rightmost pixel of bottom line. *to = (byte)(((word)from[-1] + (word)from[-(int)FIREW]) >> 1); } // =============================================================== // Fire Window message handlers. // Global vars. static HINSTANCE AppInstance; // ditto. static HDC ScreenHDC; // WinGDC created. static LPbyte Screen; // Pointer to the surface bitmap. static UINT Timer; // Timer identifier. static RGBQUAD rgb[256]; // Palette used. static HPALETTE Palette; // Handle to it. static int WindowState; // Minimized, maximized or normal? static HICON FireIcon; // Handle to icon for QueryDragIcon. // Fire buffers. FAR so the are in their own data segment. static byte FireBuf1[FIREW*FIREH], FireBuf2[FIREW*FIREH]; static Pbyte Buf1 = FireBuf1, Buf2 = FireBuf2; // ---------- Message handlers. static LRESULT wmCreate(pMSG msg) { int i; FireIcon = LoadIcon(AppInstance, "FIREICON"); // Create a smooth palette. memset(rgb, 0, sizeof(rgb)); for (i = 10; i < 10+16; i++) rgb[i].rgbRed = (BYTE)((i-10)*16); for (; i < 10+48; i++) { rgb[i].rgbRed = 255; rgb[i].rgbGreen = (BYTE)((i-10-16)*8); } for (; i < 10+80; i++) { rgb[i].rgbRed = 255; rgb[i].rgbGreen = 255; rgb[i].rgbBlue = (BYTE)((i-10-48)*8); } for (; i < 10+236; i++) { rgb[i].rgbRed = 255; rgb[i].rgbGreen = 255; rgb[i].rgbBlue = 255; } // Clean up buffers. memset(FireBuf1, 0, sizeof(FireBuf1)); memset(FireBuf2, 0, sizeof(FireBuf2)); // Create stuff. Palette = CreateIdentityPalette(rgb, 236); ScreenHDC = MakeWinGDC(rgb, &Screen); WindowState = SC_RESTORE; Timer = SetTimer(msg->hwnd, 1, 1, NULL); return DefWindowProc(msg->hwnd, msg->msg, msg->wParam, msg->lParam); } static LRESULT wmDestroy(pMSG msg) { KillTimer(msg->hwnd, Timer); EndWinGDC(ScreenHDC); if (Palette != 0) DeleteObject(Palette); DestroyIcon(FireIcon); return DefWindowProc(msg->hwnd, msg->msg, msg->wParam, msg->lParam); } static LRESULT wmTimer(pMSG msg) { HDC hdc; int i; LPdword p; Pdword q; Pbyte s; RECT r; // Clean bottom line. memset(Buf2 + FIREW*(FIREH-1), 0, FIREW); // Set random hot spots and animate. for (i = 0; i < 20; i++) { int k = rand() % (FIREW-2); Buf2[FIREW*FIREH - k] = 235; } DoFire(Buf1, Buf2); // Copy buffer to the WinGDC bitmap, adding 10 for the colors. p = (LPdword)Screen; q = (Pdword)Buf1; for (i = 0; i < FIREH*FIREW/4; i++) *p++ = *q++ + 0x0a0a0a0a; // Swap buffers. s = Buf1; Buf1 = Buf2; Buf2 = s; // Dump bitmap. GetClientRect(msg->hwnd, &r); hdc = GetDC(msg->hwnd); SelectPalette(hdc, Palette, FALSE); RealizePalette(hdc); // If window is not minimized or maximized, do optimal stretch // by multiplying by 2^2, else stretch to whatever the size. // Vertical stretch can be anything, as it doesn't slow down // if it's not stretched by a power of two. if (WindowState == SC_MINIMIZE || WindowState == SC_MAXIMIZE) WinGStretchBlt(hdc, 0, 0, r.right-r.left, r.bottom-r.top, ScreenHDC, 0, 0, FIREW, FIREH); else WinGStretchBlt(hdc, 0, 0, WINDOWW, r.bottom-r.top, ScreenHDC, 0, 0, FIREW, FIREH); ReleaseDC(msg->hwnd, hdc); return DefWindowProc(msg->hwnd, msg->msg, msg->wParam, msg->lParam); } static LRESULT wmQueryNewPalette(pMSG msg) { HDC hdc; LRESULT f; hdc = GetDC(msg->hwnd); if (Palette) SelectPalette(hdc, Palette, FALSE); f = RealizePalette(hdc); ReleaseDC(msg->hwnd, hdc); return f; } static LRESULT wmSysCommand(pMSG msg) { if (msg->wParam == SC_MINIMIZE || msg->wParam == SC_MAXIMIZE || msg->wParam == SC_RESTORE) WindowState = msg->wParam; return DefWindowProc(msg->hwnd, msg->msg, msg->wParam, msg->lParam); } static LRESULT wmWindowPosChanging(pMSG msg) { LPWINDOWPOS lpwp; RECT rw, rc; int x, w; // If window is not maximized or minimized, align its client // area to a four-pixel boundary for optimal performance. if (WindowState == SC_RESTORE) { lpwp = (LPWINDOWPOS)msg->lParam; GetWindowRect(msg->hwnd, &rw); GetClientRect(msg->hwnd, &rc); w = rw.right - rw.left - (rc.right - rc.left); // Width of frame. x = lpwp->x + w; lpwp->x = (x & ~3) - w; // Align it. if (lpwp->cx - w > WINDOWW) lpwp->cx = w + WINDOWW; } return DefWindowProc(msg->hwnd, msg->msg, msg->wParam, msg->lParam); } // Window procedure. LRESULT CALLBACK FireWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { tMSG m; m.hwnd = hwnd; m.msg = msg; m.wParam = wParam; m.lParam = lParam; switch (msg) { case WM_CREATE: return wmCreate(&m); case WM_DESTROY: return wmDestroy(&m); case WM_QUERYDRAGICON: if (FireIcon != (HICON)NULL) return FireIcon; break; case WM_TIMER: return wmTimer(&m); case WM_PALETTECHANGED: if ((HWND)wParam != hwnd) // If we changed the palette. return wmQueryNewPalette(&m); break; case WM_QUERYNEWPALETTE: return wmQueryNewPalette(&m); case WM_WINDOWPOSCHANGING: return wmWindowPosChanging(&m); case WM_SYSCOMMAND: return wmSysCommand(&m); case WM_CLOSE: PostQuitMessage(0); break; } return DefWindowProc(hwnd, msg, wParam, lParam); } // =============================================================== // Main entry point. int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw) { static char ClassName[] = "WinFire"; static char AppName[] = "WinFire"; int w, h; MSG msg; HWND hwnd; if (hPrev == 0) { WNDCLASS c; c.hCursor = LoadCursor(NULL, IDC_WAIT); c.hIcon = NULL; c.lpszMenuName = NULL; c.lpszClassName = "WinFire"; c.hbrBackground = NULL; c.hInstance = hInst; c.style = CS_BYTEALIGNCLIENT | CS_VREDRAW | CS_HREDRAW; c.lpfnWndProc = (WNDPROC)FireWndProc; c.cbWndExtra = 0; c.cbClsExtra = 0; if (!RegisterClass(&c)) return 1; } AppInstance = hInst; w = GetSystemMetrics(SM_CXSCREEN); h = GetSystemMetrics(SM_CYSCREEN); hwnd = CreateWindow (ClassName, // Class name AppName, // Caption WS_OVERLAPPEDWINDOW, // Style bits (w-WINDOWW)/2, (h-WINDOWH)/2, // Position WINDOWW, WINDOWH, // Size (HWND)NULL, // Parent window (no parent) (HMENU)NULL, // no menu hInst, // handle to window instance (LPSTR)NULL // no params to pass on ); if (hwnd == (HWND)NULL) return 1; ShowWindow(hwnd, sw); while (GetMessage(&msg, (HWND)NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0; } // ------------------------ WINFIRE.C ------------------------------