From 7e1bcd5159d4c9f098dd2124a3ee1adf1c49c0c0 Mon Sep 17 00:00:00 2001 From: Ykkrosh Date: Thu, 12 Aug 2004 17:36:48 +0000 Subject: [PATCH] Slightly nicer cursor loading (using the resource system for caching) This was SVN commit r986. --- source/lib/res/cursor.cpp | 445 ++++++++++++++++++-------------------- 1 file changed, 210 insertions(+), 235 deletions(-) diff --git a/source/lib/res/cursor.cpp b/source/lib/res/cursor.cpp index 59ee44e257..8ab1887cac 100755 --- a/source/lib/res/cursor.cpp +++ b/source/lib/res/cursor.cpp @@ -3,10 +3,11 @@ #include #include +#define USE_WINDOWS_CURSOR #ifdef _WIN32 - #include "lib/sysdep/win/win_internal.h" +#endif #include "lib/res/tex.h" #include "lib/res/ogl_tex.h" @@ -14,180 +15,7 @@ #include "lib/ogl.h" #include "CVFSFile.h" - -// Slightly hacky resource-loading code, but it doesn't -// need to do anything particularly clever. -// Pass name==NULL to unset the cursor. Pass name=="xyz" -// to load "art/textures/cursors/xyz.txt" (containing e.g. "10 10" -// for hotspot position) and "art/textures/cursors/xyz.png". - -void cursor_draw(const char* name) -{ - static HICON last_cursor = NULL; - static char* last_name = NULL; - - if (name == NULL) - { - // Clean up and go back to the standard Windows cursor - - SetCursor(LoadCursor(NULL, IDC_ARROW)); - - if (last_cursor) - DestroyIcon(last_cursor); - last_cursor = NULL; - - delete[] last_name; - last_name = NULL; - - return; - } - - // Don't do much if it's the same as last time - if (last_name && !strcmp(name, last_name)) - { - if (GetCursor() != last_cursor) - SetCursor(last_cursor); - return; - } - - // Store the name, so that the code can tell when it's - // being called several times with the same cursor - delete[] last_name; - last_name = new char[strlen(name)+1]; - strcpy(last_name, name); - - char filename[VFS_MAX_PATH]; - - // Load the .txt file containing the pixel offset of - // the cursor's hotspot (the bit of it that's - // drawn at (mouse_x,mouse_y) ) - sprintf(filename, "art/textures/cursors/%s.txt", name); - - int hotspotx, hotspoty; - - { - CVFSFile file; - if (file.Load(filename) != PSRETURN_OK) - { - assert(! "Error loading cursor hotspot .txt file"); - return; - } - std::stringstream s; - s << file.GetAsString(); - s >> hotspotx >> hotspoty; - } - - sprintf(filename, "art/textures/cursors/%s.png", name); - - Handle tex = tex_load(filename); - if (tex <= 0) - { - // TODO: Handle errors - assert(! "Error loading cursor texture"); - return; - } - - // Convert the image data into a DIB - - int w, h, fmt, bpp; - u32* imgdata; - tex_info(tex, &w, &h, &fmt, &bpp, (void**)&imgdata); - u32* imgdata_bgra; - if (fmt == GL_BGRA) - { - // No conversion needed - imgdata_bgra = imgdata; - } - else if (fmt == GL_RGBA) - { - // Convert ABGR -> ARGB (little-endian) - imgdata_bgra = new u32[w*h]; - for (int i=0; i> 16) & 0x000000ff) // B - ); - } - } - else - { - // TODO: Handle errors - assert(! "Cursor texture not 32-bit RGBA/BGRA"); - - tex_free(tex); - - return; - } - - BITMAPINFOHEADER dibheader = { - sizeof(BITMAPINFOHEADER), // biSize - w, // biWidth - h, // biHeight (positive means bottom-up) - 1, // biPlanes - 32, // biBitCount - BI_RGB, // biCompression - 0, // biSizeImage (not needed for BI_RGB) - 0, // biXPelsPerMeter (I really don't care how many pixels are in a meter) - 0, // biYPelsPerMeter - 0, // biClrUser (0 == maximum for this biBitCount. I hope we're not going to run in paletted display modes.) - 0 // biClrImportant - }; - BITMAPINFO dibinfo = { - dibheader, // bmiHeader - NULL // bmiColors[] - }; - - HDC hDC = wglGetCurrentDC(); - HBITMAP iconbitmap = CreateDIBitmap(hDC, &dibheader, CBM_INIT, imgdata_bgra, &dibinfo, 0); - if (! iconbitmap) - { - // TODO: Handle errors - assert(! "Error creating icon DIB"); - - if (imgdata_bgra != imgdata) delete[] imgdata_bgra; - tex_free(tex); - - return; - } - - ICONINFO info = { FALSE, hotspotx, hotspoty, iconbitmap, iconbitmap }; - HICON cursor = CreateIconIndirect(&info); - DeleteObject(iconbitmap); - - if (! cursor) - { - // TODO: Handle errors - assert(! "Error creating cursor"); - - if (imgdata_bgra != imgdata) delete[] imgdata_bgra; - tex_free(tex); - - return; - } - - if (last_cursor) - DestroyIcon(last_cursor); - SetCursor(cursor); - last_cursor = cursor; - - if (imgdata_bgra != imgdata) delete[] imgdata_bgra; - tex_free(tex); -} - -/****************************************************************/ -#else // #ifdef _WIN32 - -#include "lib/res/tex.h" -#include "lib/res/ogl_tex.h" -#include "lib/res/vfs.h" -#include "lib/ogl.h" -#include "CVFSFile.h" - -extern int mouse_x, mouse_y; - -struct Cursor { +struct ogl_cursor { Handle tex; int hotspotx, hotspoty; int w, h; @@ -201,52 +29,73 @@ struct Cursor { glColor4f(1.0f, 1.0f, 1.0f, 1.0f); glBegin(GL_QUADS); - glTexCoord2i(0, 1); glVertex2i( x-hotspotx, y+hotspoty ); - glTexCoord2i(1, 1); glVertex2i( x-hotspotx+w, y+hotspoty ); - glTexCoord2i(1, 0); glVertex2i( x-hotspotx+w, y+hotspoty-h ); - glTexCoord2i(0, 0); glVertex2i( x-hotspotx, y+hotspoty-h ); + glTexCoord2i(0, 1); glVertex2i( x-hotspotx, y+hotspoty ); + glTexCoord2i(1, 1); glVertex2i( x-hotspotx+w, y+hotspoty ); + glTexCoord2i(1, 0); glVertex2i( x-hotspotx+w, y+hotspoty-h ); + glTexCoord2i(0, 0); glVertex2i( x-hotspotx, y+hotspoty-h ); glEnd(); } }; -extern int g_yres; +extern int mouse_x, mouse_y; -// TODO: Remove duplication with Windows cursor code, handle errors, rewrite. -void cursor_draw(const char* name) +#ifdef _WIN32 +// On Windows, allow runtime choice between Windows cursors and OpenGL +// cursors (Windows = more responsive, OpenGL = more consistent with what +// the game sees) +struct Cursor { - static struct Cursor* last_cursor = NULL; - static char* last_name = NULL; + union { + HICON wincursor; // Windows handle + ogl_cursor* cursor; // font texture + }; + char type; // 0 for OpenGL cursor, 1 for Windows cursor +}; +#else // #ifdef _WIN32 +struct Cursor +{ + ogl_cursor* cursor; // font texture +}; +#endif // #ifdef _WIN32 / #else - if (name == NULL) +H_TYPE_DEFINE(Cursor); + +static void Cursor_init(Cursor* c, va_list) +{ +#ifdef _WIN32 +# ifdef USE_WINDOWS_CURSOR + c->type = 1; +# else + c->type = 0; +# endif + c->wincursor = NULL; +#endif + + c->cursor = NULL; +} + +static void Cursor_dtor(Cursor* c) +{ +#ifdef _WIN32 + if (c->type == 1) { - // Clean up - - if (last_cursor) + if (c->wincursor) + DestroyIcon(c->wincursor); + } + else +#endif // _WIN32 + { + if (c->cursor) { - tex_free(last_cursor->tex); - delete last_cursor; + tex_free(c->cursor->tex); + delete c->cursor; + c->cursor = NULL; } - last_cursor = NULL; - - delete[] last_name; - last_name = NULL; - - return; } +} - // Don't do much if it's the same as last time - if (last_name && !strcmp(name, last_name)) - { - last_cursor->draw(mouse_x, g_yres-mouse_y); - return; - } - - // Store the name, so that the code can tell when it's - // being called several times with the same cursor - delete[] last_name; - last_name = new char[strlen(name)+1]; - strcpy(last_name, name); - +static int Cursor_reload(Cursor* c, const char* name, Handle) +{ char filename[VFS_MAX_PATH]; // Load the .txt file containing the pixel offset of @@ -261,7 +110,7 @@ void cursor_draw(const char* name) if (file.Load(filename) != PSRETURN_OK) { assert(! "Error loading cursor hotspot .txt file"); - return; + return -1; } std::stringstream s; s << file.GetAsString(); @@ -275,36 +124,162 @@ void cursor_draw(const char* name) { // TODO: Handle errors assert(! "Error loading cursor texture"); - return; + return -1; } - int err = tex_upload(tex); - if (err < 0) +#ifdef _WIN32 + if (c->type == 1) { - // TODO: Handle errors - assert(! "Error uploading cursor texture"); - return; + // Convert the image data into a DIB + + int w, h, fmt, bpp; + u32* imgdata; + tex_info(tex, &w, &h, &fmt, &bpp, (void**)&imgdata); + u32* imgdata_bgra; + if (fmt == GL_BGRA) + { + // No conversion needed + imgdata_bgra = imgdata; + } + else if (fmt == GL_RGBA) + { + // Convert ABGR -> ARGB (little-endian) + imgdata_bgra = new u32[w*h]; + for (int i=0; i> 16) & 0x000000ff) // B + ); + } + } + else + { + // TODO: Handle errors + assert(! "Cursor texture not 32-bit RGBA/BGRA"); + + tex_free(tex); + + return -1; + } + + BITMAPINFOHEADER dibheader = { + sizeof(BITMAPINFOHEADER), // biSize + w, // biWidth + h, // biHeight (positive means bottom-up) + 1, // biPlanes + 32, // biBitCount + BI_RGB, // biCompression + 0, // biSizeImage (not needed for BI_RGB) + 0, // biXPelsPerMeter (I really don't care how many pixels are in a meter) + 0, // biYPelsPerMeter + 0, // biClrUser (0 == maximum for this biBitCount. I hope we're not going to run in paletted display modes.) + 0 // biClrImportant + }; + BITMAPINFO dibinfo = { + dibheader, // bmiHeader + NULL // bmiColors[] + }; + + HDC hDC = wglGetCurrentDC(); + HBITMAP iconbitmap = CreateDIBitmap(hDC, &dibheader, CBM_INIT, imgdata_bgra, &dibinfo, 0); + if (! iconbitmap) + { + // TODO: Handle errors + assert(! "Error creating icon DIB"); + + if (imgdata_bgra != imgdata) delete[] imgdata_bgra; + tex_free(tex); + + return -1; + } + + ICONINFO info = { FALSE, hotspotx, hotspoty, iconbitmap, iconbitmap }; + HICON cursor = CreateIconIndirect(&info); + DeleteObject(iconbitmap); + + if (! cursor) + { + // TODO: Handle errors + assert(! "Error creating cursor"); + + if (imgdata_bgra != imgdata) delete[] imgdata_bgra; + tex_free(tex); + + return -1; + } + + if (imgdata_bgra != imgdata) delete[] imgdata_bgra; + tex_free(tex); + + c->wincursor = cursor; } - - // Create the cursor object: - - int w, h; - tex_info(tex, &w, &h, NULL, NULL, NULL); - - struct Cursor* cursor = new struct Cursor; - cursor->tex = tex; - cursor->hotspotx = hotspotx; - cursor->hotspoty = hotspoty; - cursor->w = w; - cursor->h = h; - - if (last_cursor) + else +#endif // _WIN32 { - tex_free(last_cursor->tex); - delete last_cursor; + + int err = tex_upload(tex); + if (err < 0) + { + // TODO: Handle errors + assert(! "Error uploading cursor texture"); + return -1; + } + + c->cursor = new struct ogl_cursor; + + c->cursor->tex = tex; + c->cursor->hotspotx = hotspotx; + c->cursor->hotspoty = hotspoty; + // Get the width/height + tex_info(c->cursor->tex, &c->cursor->w, &c->cursor->h, NULL, NULL, NULL); } - cursor->draw(mouse_x, mouse_y); - last_cursor = cursor; + + return 0; } -#endif // #ifdef _WIN32 / #else +Handle cursor_load(const char* name) +{ + return h_alloc(H_Cursor, name, 0); +} + +int cursor_free(Handle& h) +{ + return h_free(h, H_Cursor); +} + +extern int g_yres; // from main.cpp. Required because GL's (0,0) is in the bottom-left + +void cursor_draw(const char* name) +{ + // Use 'null' to disable the cursor + if (!name) + { +#ifdef _WIN32 + SetCursor(LoadCursor(NULL, IDC_ARROW)); +#endif + return; + } + + Handle h = cursor_load(name); + if (h <= 0) + return; + + Cursor* c = (Cursor*) h_user_data(h, H_Cursor); + if (!c) + return; + +#ifdef _WIN32 + if (c->type == 1) + { + SetCursor(c->wincursor); + } + else +#endif // _WIN32 + { + c->cursor->draw(mouse_x, g_yres - mouse_y); + } + + cursor_free(h); +}