Skip to content

Commit

Permalink
Engine: add support for hardware cursors
Browse files Browse the repository at this point in the history
  • Loading branch information
ericoporto committed Sep 1, 2023
1 parent 88c8261 commit fe07da1
Show file tree
Hide file tree
Showing 11 changed files with 105 additions and 4 deletions.
6 changes: 6 additions & 0 deletions Common/gfx/allegrobitmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,12 @@ class Bitmap
return (GetColorDepth() + 7) / 8;
}

// Length of the scanline in bytes
inline int GetPitch() const
{
return GetBPP() * GetWidth();
}

// CHECKME: probably should not be exposed, see comment to GetData()
inline int GetDataSize() const
{
Expand Down
9 changes: 5 additions & 4 deletions Engine/ac/draw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1634,8 +1634,8 @@ static void apply_tint_or_light(ObjTexture &actsp, int light_level,
// * if transformation is necessary - writes into dst and returns dst;
// * if no transformation is necessary - simply returns src;
// Used for software render mode only.
static Bitmap *transform_sprite(Bitmap *src, bool src_has_alpha, std::unique_ptr<Bitmap> &dst,
const Size dst_sz, GraphicFlip flip = Common::kFlip_None)
Common::Bitmap *transform_sprite(Common::Bitmap *src, bool src_has_alpha, std::unique_ptr<Common::Bitmap> &dst,
Size dst_sz, Common::GraphicFlip flip)
{
if ((src->GetSize() == dst_sz) && (flip == kFlip_None))
return src; // No transform: return source image
Expand Down Expand Up @@ -1689,7 +1689,7 @@ static Bitmap *transform_sprite(Bitmap *src, bool src_has_alpha, std::unique_ptr
static bool scale_and_flip_sprite(ObjTexture &actsp, int sppic, int width, int height, bool hmirror)
{
Bitmap *src = spriteset[sppic];
Bitmap *result = transform_sprite(src, (game.SpriteInfos[sppic].Flags & SPF_ALPHACHANNEL) != 0,
Bitmap const *result = transform_sprite(src, (game.SpriteInfos[sppic].Flags & SPF_ALPHACHANNEL) != 0,
actsp.Bmp, Size(width, height), hmirror ? kFlip_Horizontal : kFlip_None);
return result != src;
}
Expand Down Expand Up @@ -2720,6 +2720,7 @@ void construct_game_screen_overlay(bool draw_mouse)
{
gfxDriver->DrawSprite(AGSE_POSTSCREENDRAW, 0, nullptr);
}
if(usetup.mouse_hardware_cursor) update_hardware_cursor_graphic();

// Add mouse cursor pic, and global screen tint effect
if (play.screen_is_faded_out == 0)
Expand Down Expand Up @@ -2913,7 +2914,7 @@ void render_graphics(IDriverDependantBitmap *extraBitmap, int extraX, int extraY
gfxDriver->DrawSprite(extraX, extraY, extraBitmap);
gfxDriver->EndSpriteBatch();
}
construct_game_screen_overlay(true);
construct_game_screen_overlay(!usetup.mouse_hardware_cursor);
render_to_screen();

if (!play.screen_is_faded_out) {
Expand Down
4 changes: 4 additions & 0 deletions Engine/ac/draw.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ void invalidate_rect(int x1, int y1, int x2, int y2, bool in_room);

void mark_current_background_dirty();

// Generates a transformed sprite, using src image and parameters
Common::Bitmap *transform_sprite(Common::Bitmap *src, bool src_has_alpha, std::unique_ptr<Common::Bitmap> &dst,
Size dst_sz, Common::GraphicFlip flip = Common::kFlip_None);

// Avoid freeing and reallocating the memory if possible
Common::Bitmap *recycle_bitmap(Common::Bitmap *bimp, int coldep, int wid, int hit, bool make_transparent = false);
void recycle_bitmap(std::unique_ptr<Common::Bitmap> &bimp, int coldep, int wid, int hit, bool make_transparent = false);
Expand Down
1 change: 1 addition & 0 deletions Engine/ac/gamesetup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ GameSetup::GameSetup()
mouse_speed_def = kMouseSpeed_CurrentDisplay;
touch_emulate_mouse = kTouchMouse_OneFingerDrag;
touch_motion_relative = false;
mouse_hardware_cursor = false;
RenderAtScreenRes = false;
Supersampling = 1;
clear_cache_on_room_change = false;
Expand Down
1 change: 1 addition & 0 deletions Engine/ac/gamesetup.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ struct GameSetup
// touch control abs/relative mode
bool touch_motion_relative;
//
bool mouse_hardware_cursor;
bool RenderAtScreenRes; // render sprites at screen resolution, as opposed to native one
int Supersampling;
size_t SpriteCacheSize = DefSpriteCacheSize; // in KB
Expand Down
55 changes: 55 additions & 0 deletions Engine/ac/mouse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@
#include "ac/spritecache.h"
#include "gfx/graphicsdriver.h"
#include "gfx/gfxfilter.h"
#include "gfx/gfx_util.h"
#include "platform/base/agsplatformdriver.h"
#include <SDL_mouse.h>

using namespace AGS::Common;
using namespace AGS::Engine;
Expand All @@ -54,6 +56,18 @@ Bitmap *dotted_mouse_cursor = nullptr;
IDriverDependantBitmap *mouseCursor = nullptr;
Bitmap *blank_mouse_cursor = nullptr;

eAGSMouseButton simulatedClick = kMouseNone;

struct HardwareCursor {
int prev_cur_spriteslot = -1;
int last_spriteslot = -1;
SDL_Surface *sdl_surface = nullptr;
std::unique_ptr<Bitmap> bitmap = nullptr;
SDL_Cursor *sdl_cursor = nullptr;
const Point base_point = Point(32, 32);
Point prev_point = Point(1, 1);
} hcur;

// The Mouse:: functions are static so the script doesn't pass
// in an object parameter
void Mouse_SetVisible(int isOn) {
Expand Down Expand Up @@ -389,7 +403,48 @@ void update_cached_mouse_cursor()
mouseCursor = gfxDriver->CreateDDBFromBitmap(mousecurs[0], alpha_blend_cursor != 0);
}

void update_hardware_cursor_graphic() {
int width, height, hotspot_x, hotspot_y;
Bitmap* cur_bitmap;
Size sz;
if(hcur.prev_point != GameScaling.Scale(hcur.base_point)) {
hcur.prev_point = GameScaling.Scale(hcur.base_point);
hcur.last_spriteslot = -1;
}

if(play.mouse_cursor_hidden || hcur.prev_cur_spriteslot <= 0) {
SDL_ShowCursor(0);
hcur.last_spriteslot = -1;
return;
}

if(hcur.last_spriteslot == hcur.prev_cur_spriteslot) return;
hcur.last_spriteslot = hcur.prev_cur_spriteslot;

cur_bitmap = mousecurs[0];
width = GameScaling.X.ScaleDistance(cur_bitmap->GetWidth());
height = GameScaling.Y.ScaleDistance(cur_bitmap->GetHeight());
hotspot_x = GameScaling.X.ScaleDistance(game.mcurs[cur_cursor].hotx);
hotspot_y = GameScaling.Y.ScaleDistance(game.mcurs[cur_cursor].hoty);
sz = Size(width, height);

if(hcur.bitmap) hcur.bitmap->Destroy();

hcur.bitmap.reset (BitmapHelper::CreateBitmap(width, height, cur_bitmap->GetColorDepth()));

Bitmap* res_bmp = transform_sprite(cur_bitmap, alpha_blend_cursor != 0, hcur.bitmap, sz, GraphicFlip::kFlip_None);

if(hcur.sdl_cursor) SDL_FreeCursor(hcur.sdl_cursor);
if(hcur.sdl_surface) SDL_FreeSurface(hcur.sdl_surface);

hcur.sdl_surface = GfxUtil::CreateSDL_SurfaceFromBitmap(res_bmp);
hcur.sdl_cursor = SDL_CreateColorCursor(hcur.sdl_surface, hotspot_x, hotspot_y);
SDL_SetCursor(hcur.sdl_cursor);
SDL_ShowCursor(1);
}

void set_new_cursor_graphic (int spriteslot) {
hcur.prev_cur_spriteslot = spriteslot;
mousecurs[0] = spriteset[spriteslot];

// It looks like spriteslot 0 can be used in games with version 2.72 and lower.
Expand Down
1 change: 1 addition & 0 deletions Engine/ac/mouse.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ void update_script_mouse_coords();
void update_inv_cursor(int invnum);
void update_cached_mouse_cursor();
void set_new_cursor_graphic (int spriteslot);
void update_hardware_cursor_graphic();
int find_next_enabled_cursor(int startwith);
int find_previous_enabled_cursor(int startwith);

Expand Down
23 changes: 23 additions & 0 deletions Engine/gfx/gfx_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "core/platform.h"
#include "gfx/gfx_util.h"
#include "gfx/blender.h"
#include "SDL_surface.h"

namespace AGS
{
Expand All @@ -41,6 +42,28 @@ Bitmap *ConvertBitmap(Bitmap *src, int dst_color_depth)
return src;
}

SDL_Surface* CreateSDL_SurfaceFromBitmap(Bitmap* src)
{
Uint32 rmask, gmask, bmask, amask;
if(src->GetColorDepth() == 32)
{
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
rmask = 0x0000ff00;
gmask = 0x00ff0000;
bmask = 0xff000000;
amask = 0x000000ff;
#else
rmask = 0x00ff0000;
gmask = 0x0000ff00;
bmask = 0x000000ff;
amask = 0xff000000;
#endif
}

SDL_Surface* surface = SDL_CreateRGBSurfaceFrom((void *) src->GetData(), src->GetWidth(), src->GetHeight(), src->GetColorDepth(), src->GetPitch(), rmask, gmask, bmask, amask);
return surface;
}


typedef BLENDER_FUNC PfnBlenderCb;

Expand Down
5 changes: 5 additions & 0 deletions Engine/gfx/gfx_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
#include "gfx/bitmap.h"
#include "gfx/gfx_def.h"

struct SDL_Surface;

namespace AGS
{
namespace Engine
Expand All @@ -39,6 +41,9 @@ namespace GfxUtil
// Creates a COPY of the source bitmap, converted to the given format.
Bitmap *ConvertBitmap(Bitmap *src, int dst_color_depth);

// Creates a COPY of the source bitmap, as an SDL Surface
SDL_Surface* CreateSDL_SurfaceFromBitmap(Bitmap* src);

// Considers the given information about source and destination surfaces,
// then draws a bimtap over another either using requested blending mode,
// or fallbacks to common "magic pink" transparency mode;
Expand Down
1 change: 1 addition & 0 deletions Engine/main/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,7 @@ void apply_config(const ConfigTree &cfg)
mouse_str = CfgReadString(cfg, "mouse", "speed_def", "current_display");
usetup.mouse_speed_def = StrUtil::ParseEnum<MouseSpeedDef>(
mouse_str, CstrArr<kNumMouseSpeedDefs>{ "absolute", "current_display" }, usetup.mouse_speed_def);
usetup.mouse_hardware_cursor = CfgReadBoolInt(cfg, "mouse", "hardware_cursor");

// Touch options
usetup.touch_emulate_mouse = StrUtil::ParseEnum<TouchMouseEmulation>(
Expand Down
3 changes: 3 additions & 0 deletions Engine/main/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ void main_print_help() {
" --log-stdout=+mgs:debug\n"
" --log-file=all:warn\n"
" --log-file-path=PATH Define custom path for the log file\n"
" --mouse-hardware-cursor Draw game cursor using hardware accelerated method\n"
//--------------------------------------------------------------------------------|
#if AGS_PLATFORM_OS_WINDOWS
" --no-message-box Disable alerts as modal message boxes\n"
Expand Down Expand Up @@ -318,6 +319,8 @@ static int main_process_cmdline(ConfigTree &cfg, int argc, char *argv[])
cfg["language"]["translation"] = "";
else if (ags_stricmp(arg, "--background") == 0)
cfg["override"]["multitasking"] = "1";
else if (ags_stricmp(arg, "--mouse-hardware-cursor") == 0)
cfg["mouse"]["hardware_cursor"] = "1";
else if (ags_stricmp(arg, "--fps") == 0)
cfg["misc"]["show_fps"] = "1";
else if (ags_stricmp(arg, "--test") == 0) debug_flags |= DBG_DEBUGMODE;
Expand Down

0 comments on commit fe07da1

Please sign in to comment.