Demmer, Thomas wrote:
Hi all, as promised, here are the patch files for MS Windows to use Device Independent Bimaps to speed up.
Thanks a lot for your work Thomas.
- About the "get rid of GRXMain" part:
I will integrate it in the next pre-release. I have changed the vdriver a bit, I think is much cleaner. I attach it for review.
- About the DIB aproach:
Is very interesting and really fast!. But now than you have a pointer to a "24 bpp linear frame buffer" (the bitmap), why not use the standard GRX framebuffer with them?
The idea is to clone the fdrivers/lfb24.c in fdrivers/lfb24w32.c (by example) using a new driver name (like GR_frame24W32_LFB by example). I suspect the only things to add are the apropiate InvalidateRects.
Greetings, M.Alvarez
/** ** vd_win32.c ---- the standard Win32-API driver ** ** Author: Gernot Graeff ** E-mail: gernot.graeff@t-online.de ** Date: 13.11.98 ** ** This file is part of the GRX graphics library. ** ** The GRX graphics library is free software; you can redistribute it ** and/or modify it under some conditions; see the "copying.grx" file ** for details. ** ** This library is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** ** Changes by Josu Onandia (jonandia@fagorautomation.es) 21/02/2001 ** - The colors loaded in the ColorList are guaranteed to be actually used ** by Windows (GetNearestColor), for the GR_frameWin32 to work. ** - When the window is created, it gets the maximum size allowed by the ** current mode. Indeed this size is stored (maxWindowWidth/ ** maxWindowHeight). ** When the window is going to be resized (WM_GETMINMAXINFO) it's not ** allowed to grow bigger than this maximum size (it makes nosense). ** - Added some modes for 24bpp colors. ** - When changed to text-mode, the graphics window is hidden. If the ** application has a console (linked with -mconsole) it can use ** printf/scanf and the like. ** When changed again into graphics mode, the window reappears. ** - Inter-task synchronization. In some cases the two threads are ** manipulating at the same time the main window, and the on-memory ** bitmap. I guess this is causing trouble, so in some cases the main ** thread suspends the worker thread, make its operation, and then ** resumes it. ** - The window title is selectable with a define, at compile time. ** If not defined, it defaults to "GRX". ** ** Changes by M.Alvarez (malfer@telefonica.net) 02/01/2002 ** - Go to full screen if the framemode dimensions are equal to ** the screen dimensions (setting the client start area at 0,0). ** ** Changes by M.Alvarez (malfer@telefonica.net) 02/02/2002 ** - The w32 imput queue implemented as a circular queue. ** - All the input related code moved to w32inp.c ** - The main window is created in WinMain, so the grx program ** can use other videodrivers like the memory one. ** ** Changes by M.Alvarez (malfer@telefonica.net) 11/02/2002 ** - Now the GRX window is properly closed, so the previous app ** gets the focus. ** ** Changes by M.Alvarez (malfer@telefonica.net) 31/03/2002 ** - Accepts arbitrary (user defined) resolution. ** ** Changes by Thomas Demmer (TDemmer@krafteurope.com) ** - Instead of begin with WinMain and start a thread with the main ** GRX program, do it backward: begin in main and start a thread to ** handle the Windows window. With this change we get rid of the ** awful GRXMain special entry point. ** ** Changes by M.Alvarez (malfer@telefonica.net) 12/02/2003 ** - Sanitize the Thomas changes. **/
#include "libwin32.h" #include "libgrx.h" #include "grdriver.h" #include "arith.h"
#ifndef GRXWINDOW_TITLE #define GRXWINDOW_TITLE "GRX" #endif
HWND hGRXWnd = NULL; HDC hDCMem = NULL; HBITMAP hBitmapScreen = NULL;
CRITICAL_SECTION _csEventQueue; W32Event *_W32EventQueue = NULL; int _W32EventQueueSize = 0; int _W32EventQueueRead = 0; int _W32EventQueueWrite = 0; int _W32EventQueueLength = 0; SColorList *ColorList = NULL;
static int maxScreenWidth, maxScreenHeight; static int maxWindowWidth, maxWindowHeight;
static HANDLE windowThread = INVALID_HANDLE_VALUE; static HANDLE mainThread = INVALID_HANDLE_VALUE;
static volatile int isWindowThreadRunning = 0; static volatile int isMainWaitingTermination = 0;
static DWORD WINAPI WndThread(void *param); static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
/* NOTE: Adjusts the color to the real RGB used by Windows */ static void loadcolor(int c, int r, int g, int b) { SColorList *clTmp, *clPrev; HDC hdcW; COLORREF color;
hdcW = GetDC(hGRXWnd); color = RGB(r, g, b); color = GetNearestColor(hdcW, color); ReleaseDC(hGRXWnd, hdcW); if (ColorList == NULL) { clTmp = malloc(sizeof(SColorList)); clTmp->nIndex = c; clTmp->color = color; clTmp->pNext = NULL; ColorList = clTmp; return; } else { clTmp = ColorList; do { if (clTmp->nIndex == c) { clTmp->color = color; return; } clPrev = clTmp; clTmp = clTmp->pNext; } while (clTmp); clPrev->pNext = malloc(sizeof(SColorList)); clTmp = clPrev->pNext; clTmp->nIndex = c; clTmp->color = color; clTmp->pNext = NULL; return; } }
static void ColorList_destroy(void) { SColorList *clTmp;
while (ColorList != NULL) { clTmp = ColorList; ColorList = ColorList->pNext; free(clTmp); } }
static int setmode(GrVideoMode * mp, int noclear) { RECT Rect; HDC hDC; HBRUSH hBrush; int inipos;
if (mp->extinfo->mode != GR_frameText) { inipos = 50; if (mp->width == maxScreenWidth && mp->height == maxScreenHeight) inipos = 0; Rect.left = inipos; Rect.top = inipos; Rect.right = mp->width + inipos; Rect.bottom = mp->height + inipos; AdjustWindowRect(&Rect, WS_OVERLAPPEDWINDOW, FALSE); maxWindowWidth = Rect.right - Rect.left; maxWindowHeight = Rect.bottom - Rect.top; SetWindowPos(hGRXWnd, NULL, Rect.left, Rect.top, maxWindowWidth, maxWindowHeight, SWP_DRAWFRAME | SWP_NOZORDER | SWP_SHOWWINDOW);
if (hBitmapScreen) { DeleteObject(hBitmapScreen); hBitmapScreen = NULL; } hDC = GetDC(hGRXWnd); if (hDCMem == NULL) hDCMem = CreateCompatibleDC(hDC); hBitmapScreen = CreateCompatibleBitmap(hDC, mp->width, mp->height); SelectObject(hDCMem, hBitmapScreen); SetRect(&Rect, 0, 0, mp->width, mp->height); hBrush = CreateSolidBrush(RGB(0, 0, 0)); FillRect(hDCMem, &Rect, hBrush); FillRect(hDC, &Rect, hBrush); ReleaseDC(hGRXWnd, hDC); DeleteObject(hBrush); UpdateWindow(hGRXWnd); SetForegroundWindow(hGRXWnd); ColorList_destroy(); } else { /* If changing to text-mode, hide the graphics window. */ if (hGRXWnd != NULL) ShowWindow(hGRXWnd, SW_HIDE); } return (TRUE); }
static void setbank_dummy(int bk) { bk = bk; }
GrVideoModeExt grtextext = { GR_frameText, /* frame driver */ NULL, /* frame driver override */ NULL, /* frame buffer address */ {0, 0, 0}, /* color precisions */ {0, 0, 0}, /* color component bit positions */ 0, /* mode flag bits */ setmode, /* mode set */ NULL, /* virtual size set */ NULL, /* virtual scroll */ NULL, /* bank set function */ NULL, /* double bank set function */ NULL /* color loader */ };
static GrVideoModeExt grxwinext8 = { GR_frameWIN32_8, /* frame driver */ NULL, /* frame driver override */ NULL, /* frame buffer address */ {8, 8, 8}, /* color precisions */ {0, 8, 16}, /* color component bit positions */ 0, /* mode flag bits */ setmode, /* mode set */ NULL, /* virtual size set */ NULL, /* virtual scroll */ setbank_dummy, /* bank set function */ NULL, /* double bank set function */ loadcolor /* color loader */ };
static GrVideoModeExt grxwinext24 = { GR_frameWIN32_24, /* frame driver */ NULL, /* frame driver override */ NULL, /* frame buffer address */ {8, 8, 8}, /* color precisions */ {0, 8, 16}, /* color component bit positions */ 0, /* mode flag bits */ setmode, /* mode set */ NULL, /* virtual size set */ NULL, /* virtual scroll */ setbank_dummy, /* bank set function */ NULL, /* double bank set function */ NULL /* color loader */ };
static GrVideoMode modes[] = { /* pres. bpp wdt hgt BIOS scan priv. &ext */ {TRUE, 8, 80, 25, 0x00, 80, 1, &grtextext},
{TRUE, 8, 320, 240, 0x00, 320, 0, &grxwinext8}, {TRUE, 8, 640, 480, 0x00, 640, 0, &grxwinext8}, {TRUE, 8, 800, 600, 0x00, 800, 0, &grxwinext8}, {TRUE, 8, 1024, 768, 0x00, 1024, 0, &grxwinext8}, {TRUE, 8, 1280, 1024, 0x00, 1280, 0, &grxwinext8}, {TRUE, 8, 1600, 1200, 0x00, 1600, 0, &grxwinext8},
{TRUE, 24, 320, 240, 0x00, 0, 0, &grxwinext24}, {TRUE, 24, 640, 480, 0x00, 0, 0, &grxwinext24}, {TRUE, 24, 800, 600, 0x00, 0, 0, &grxwinext24}, {TRUE, 24, 1024, 768, 0x00, 0, 0, &grxwinext24}, {TRUE, 24, 1280, 1024, 0x00, 0, 0, &grxwinext24}, {TRUE, 24, 1600, 1200, 0x00, 0, 0, &grxwinext24},
{FALSE, 0, 9999, 9999, 0x00, 0, 0, NULL} };
static int detect(void) { static int inited = 0; WNDCLASSEX wndclass;
if (!inited) { wndclass.cbSize = sizeof(wndclass); wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = GetModuleHandle(NULL); wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); wndclass.lpszMenuName = NULL; wndclass.lpszClassName = "GRXCLASS"; wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION); if (RegisterClassEx(&wndclass)== 0) return FALSE; inited = 1; }
return TRUE; }
static int init(char *options) { int i; DWORD thread_id;
/* WARNING: mainThread can not be used in the windowThread */ mainThread = GetCurrentThread();
InitializeCriticalSection(&_csEventQueue);
/* The modes not compatible width the configuration */ /* of Windows are made 'non-present' */ maxScreenWidth = GetSystemMetrics(SM_CXSCREEN); for (i = 1; i < itemsof(modes); i++) { if (modes[i].width > maxScreenWidth) modes[i].present = FALSE; } maxScreenHeight = GetSystemMetrics(SM_CYSCREEN); for (i = 1; i < itemsof(modes); i++) { if (modes[i].height > maxScreenHeight) modes[i].present = FALSE; }
windowThread = CreateThread(NULL, 0, WndThread, NULL, 0, &thread_id);
/* Wait for thread creating the window. This is a busy */ /* waiting loop (bad), but we Sleep to yield (good) */ while (isWindowThreadRunning == 0) Sleep(1);
return TRUE; }
static void reset(void) { isMainWaitingTermination = 1; PostMessage(hGRXWnd, WM_CLOSE, 0, 0);
while (isWindowThreadRunning == 1) Sleep(1);
isMainWaitingTermination = 0; DeleteCriticalSection(&_csEventQueue); ColorList_destroy();
if (hBitmapScreen != NULL) { DeleteObject(hBitmapScreen); hBitmapScreen = NULL; } if (hDCMem != NULL) { DeleteDC(hDCMem); hDCMem = NULL; } }
static GrVideoMode * _w32_selectmode(GrVideoDriver * drv, int w, int h, int bpp, int txt, unsigned int * ep) { GrVideoMode *mp, *res;
if (txt) { res = _gr_selectmode(drv, w, h, bpp, txt, ep); goto done; } for (mp = &modes[1]; mp < &modes[itemsof(modes)-1]; mp++) { if (mp->present && mp->width == w && mp->height == h) { res = _gr_selectmode(drv, w, h, bpp, txt, ep); goto done; } } /* no predefined mode found. Create a new mode if we can*/ if (w <= maxScreenWidth && h <= maxScreenHeight) { mp->present = TRUE; mp->width = w; mp->height = h; if (bpp <= 8) { mp->bpp = 8; mp->extinfo = &grxwinext8; mp->lineoffset = mp->width; } else { mp->bpp = 24; mp->extinfo = &grxwinext24; mp->lineoffset = 0; } } res = _gr_selectmode(drv, w, h, bpp, txt, ep); done: return res; }
GrVideoDriver _GrVideoDriverWIN32 = { "win32", /* name */ GR_WIN32, /* adapter type */ NULL, /* inherit modes from this driver */ modes, /* mode table */ itemsof(modes), /* # of modes */ detect, /* detection routine */ init, /* initialization routine */ reset, /* reset routine */ _w32_selectmode, /* special mode select routine */ GR_DRIVERF_USER_RESOLUTION /* arbitrary resolution possible */ };
static DWORD WINAPI WndThread(void *param) { MSG msg;
hGRXWnd = CreateWindow("GRXCLASS", GRXWINDOW_TITLE, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX, 0, 0, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, GetModuleHandle(NULL), NULL); ShowWindow(hGRXWnd, SW_HIDE);
isWindowThreadRunning = 1;
while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); }
isWindowThreadRunning = 0;
ExitThread(0); return 0; }
static int convertwin32keystate(void) { int fkbState = 0;
if (GetKeyState(VK_SHIFT) < 0) fkbState |= GR_KB_SHIFT; if (GetKeyState(VK_CONTROL) < 0) fkbState |= GR_KB_CTRL; if (GetKeyState(VK_MENU) < 0) fkbState |= GR_KB_ALT; if (GetKeyState(VK_SCROLL) < 0) fkbState |= GR_KB_SCROLLOCK; if (GetKeyState(VK_NUMLOCK) < 0) fkbState |= GR_KB_NUMLOCK; if (GetKeyState(VK_CAPITAL) < 0) fkbState |= GR_KB_CAPSLOCK; if (GetKeyState(VK_INSERT) < 0) fkbState |= GR_KB_INSERT; return fkbState; }
static void EnqueueW32Event(UINT uMsg, WPARAM wParam, LPARAM lParam, int kbstat) { if (_W32EventQueue == NULL) return;
EnterCriticalSection(&_csEventQueue); _W32EventQueue[_W32EventQueueWrite].uMsg = uMsg; _W32EventQueue[_W32EventQueueWrite].wParam = wParam; _W32EventQueue[_W32EventQueueWrite].lParam = lParam; _W32EventQueue[_W32EventQueueWrite].kbstat = kbstat; if (++_W32EventQueueWrite == _W32EventQueueSize) _W32EventQueueWrite = 0; if (++_W32EventQueueLength > _W32EventQueueSize) { _W32EventQueueLength--; if (++_W32EventQueueRead == _W32EventQueueSize) _W32EventQueueRead = 0; } LeaveCriticalSection(&_csEventQueue); }
static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { static int cursorOn = 1; int kbstat; BOOL fInsert;
switch (uMsg) {
case WM_NCHITTEST: { LRESULT res = DefWindowProc(hWnd, uMsg, wParam, lParam); if (res == HTCLIENT) { if (cursorOn) { ShowCursor(FALSE); cursorOn = 0; } } else { if (!cursorOn) { ShowCursor(TRUE); cursorOn = 1; } } return res; }
case WM_CLOSE: if (!isMainWaitingTermination && MessageBox(hWnd, "This will abort the program\nare you sure?", "Abort", MB_APPLMODAL | MB_ICONQUESTION | MB_YESNO ) != IDYES) return 0; DestroyWindow(hWnd); if (!isMainWaitingTermination) ExitProcess(1); break;
case WM_DESTROY: PostQuitMessage(0); break;
case WM_GETMINMAXINFO: { LPMINMAXINFO lpmmi = (LPMINMAXINFO) lParam;
lpmmi->ptMaxSize.x = lpmmi->ptMaxTrackSize.x = maxWindowWidth; lpmmi->ptMaxSize.y = lpmmi->ptMaxTrackSize.y = maxWindowHeight; } return 0;
case WM_CHAR: kbstat = convertwin32keystate(); EnqueueW32Event(uMsg, wParam, lParam, kbstat); return 0;
case WM_SYSCHAR: fInsert = FALSE; kbstat = convertwin32keystate(); if (kbstat & GR_KB_ALT) { if (wParam >= 'a' && wParam <= 'z') fInsert = TRUE; if (wParam >= 'A' && wParam <= 'Z') fInsert = TRUE; if (wParam >= '0' && wParam <= '9') fInsert = TRUE; } if (!fInsert) break; EnqueueW32Event(uMsg, wParam, lParam, kbstat); return 0;
case WM_KEYDOWN: case WM_SYSKEYDOWN: kbstat = convertwin32keystate(); /* if (kbstat & GR_KB_ALT && wParam == VK_F4) PostMessage(hWnd, WM_CLOSE, 0, 0); */ EnqueueW32Event(uMsg, wParam, lParam, kbstat); return 0;
case WM_LBUTTONDOWN: case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: case WM_LBUTTONUP: case WM_MBUTTONUP: case WM_RBUTTONUP: kbstat = convertwin32keystate(); EnqueueW32Event(uMsg, wParam, lParam, kbstat); return 0;
case WM_MOUSEMOVE: kbstat = convertwin32keystate(); EnqueueW32Event(uMsg, wParam, lParam, kbstat); return 0;
case WM_PAINT: { RECT UpdateRect; HDC hDC; PAINTSTRUCT ps;
if (GetUpdateRect(hWnd, &UpdateRect, FALSE)) { BeginPaint(hWnd, &ps); hDC = GetDC(hWnd); BitBlt(hDC, UpdateRect.left, UpdateRect.top, UpdateRect.right - UpdateRect.left + 1, UpdateRect.bottom - UpdateRect.top + 1, hDCMem, UpdateRect.left, UpdateRect.top, SRCCOPY); ReleaseDC(hWnd, hDC); EndPaint(hWnd, &ps); } } return 0;
case WM_SYSCOMMAND: case WM_NCCREATE: case WM_NCPAINT: case WM_NCMOUSEMOVE: case WM_PALETTEISCHANGING: case WM_ACTIVATEAPP: case WM_NCCALCSIZE: case WM_ACTIVATE: case WM_NCACTIVATE: case WM_SHOWWINDOW: case WM_WINDOWPOSCHANGING: case WM_GETTEXT: case WM_SETFOCUS: case WM_KILLFOCUS: case WM_GETICON: case WM_ERASEBKGND: case WM_QUERYNEWPALETTE: case WM_WINDOWPOSCHANGED: case WM_GETDLGCODE: case WM_MOVE: case WM_SIZE: case WM_SETCURSOR: case WM_HELP: case WM_KEYUP: case WM_SYSKEYUP: break;
default: /* char szMsg[255]; sprintf(szMsg, "Msg %x, wParam %d, lParam %d", uMsg, wParam, lParam); MessageBox(NULL, szMsg, "Msg", MB_OK); */ break;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam); }