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 13, 2024
1 parent 6baad09 commit 2063858
Show file tree
Hide file tree
Showing 11 changed files with 110 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 @@ -138,6 +138,12 @@ class Bitmap
return (GetColorDepth() + 7) / 8;
}

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

// Gets size of Bitmap's pixel data, in bytes
inline int GetDataSize() const
{
Expand Down
10 changes: 6 additions & 4 deletions Engine/ac/draw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1670,8 +1670,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 @@ -1725,7 +1725,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 @@ -2811,6 +2811,8 @@ void construct_game_screen_overlay(bool draw_mouse)
}
}

if(usetup.mouse_hardware_cursor) update_hardware_cursor_graphic();

// Full screen tint fx, covers everything except for fade fx(?) and engine overlay
if ((play.screen_tint >= 1) && (play.screen_is_faded_out == 0))
gfxDriver->SetScreenTint(play.screen_tint & 0xff, (play.screen_tint >> 8) & 0xff, (play.screen_tint >> 16) & 0xff);
Expand Down Expand Up @@ -2954,7 +2956,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 @@ -117,6 +117,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;
clear_cache_on_room_change = false;
load_latest_save = 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 @@ -97,6 +97,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
size_t SpriteCacheSize = DefSpriteCacheSize; // in KB
size_t TextureCacheSize = DefTexCacheSize; // in KB
Expand Down
59 changes: 59 additions & 0 deletions Engine/ac/mouse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,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 @@ -52,6 +54,15 @@ int lastmx=-1,lastmy=-1;

CursorGraphicState cursor_gstate;

struct HardwareCursor {
int 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;

Bitmap *CursorGraphicState::GetImage() const
{
Expand All @@ -60,6 +71,9 @@ Bitmap *CursorGraphicState::GetImage() const

void CursorGraphicState::SetImage(std::unique_ptr<Common::Bitmap> pic, bool has_alpha)
{
if(hcur.bitmap) hcur.bitmap->Destroy();
hcur.bitmap.reset(new Bitmap());
hcur.bitmap->CreateCopy(pic.get(), 32);
_genImage = std::move(pic);
_hasAlpha = has_alpha;
_sprnum = -1;
Expand All @@ -68,13 +82,17 @@ void CursorGraphicState::SetImage(std::unique_ptr<Common::Bitmap> pic, bool has_

void CursorGraphicState::SetSpriteNum(int sprnum)
{
if(hcur.bitmap) hcur.bitmap->Destroy();
hcur.bitmap.reset(new Bitmap());
hcur.bitmap->CreateCopy(spriteset[_sprnum], 32);
_sprnum = sprnum;
_genImage.reset();
_hasAlpha = (game.SpriteInfos[sprnum].Flags & SPF_ALPHACHANNEL) != 0;
MarkChanged();
}



// The Mouse:: functions are static so the script doesn't pass
// in an object parameter
void Mouse_SetVisible(int isOn) {
Expand Down Expand Up @@ -404,8 +422,49 @@ void update_inv_cursor(int invnum) {
}
}

void update_hardware_cursor_graphic() {
int width, height, hotspot_x, hotspot_y;
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.cur_spriteslot <= 0) {
SDL_ShowCursor(0);
hcur.last_spriteslot = -1;
return;
}

if(hcur.last_spriteslot == hcur.cur_spriteslot) return;
std::unique_ptr<Bitmap> cur_bitmap = std::move(hcur.bitmap);
hcur.last_spriteslot = hcur.cur_spriteslot;

bool alpha_blend_cursor = (hcur.cur_spriteslot >= 0) ?
((game.SpriteInfos[hcur.cur_spriteslot].Flags & SPF_ALPHACHANNEL) != 0) : false;

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);

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

Bitmap* res_bmp = transform_sprite(cur_bitmap.get(), 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.cur_spriteslot = spriteslot;
// It looks like spriteslot 0 can be used in games with version 2.72 and lower.
// The NULL check should ensure that the sprite is valid anyway.
if ((spriteslot < 1) && (loaded_game_file_version > kGameVersion_272))
Expand Down
1 change: 1 addition & 0 deletions Engine/ac/mouse.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ int GetMouseCursor();
void update_script_mouse_coords();
void update_inv_cursor(int invnum);
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 @@ -40,6 +42,9 @@ namespace GfxUtil
// Keeps mask pixels intact, only converting mask color value if necessary.
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 @@ -388,6 +388,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 @@ -155,6 +155,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"
//--------------------------------------------------------------------------------|
" --no-message-box Disable alerts as modal message boxes\n"
" --no-plugins Disable plugin loading\n"
Expand Down Expand Up @@ -317,6 +318,8 @@ static int main_process_cmdline(ConfigTree &cfg, int argc, char *argv[])
cfg["override"]["multitasking"] = "1";
else if (ags_stricmp(arg, "--no-plugins") == 0)
cfg["override"]["noplugins"] = "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 2063858

Please sign in to comment.