mirror of https://github.com/libsdl-org/SDL
Added support for high-DPI cursors and icons
Fixes https://github.com/libsdl-org/SDL/issues/9838
This commit is contained in:
parent
94d9229ce2
commit
31ed3665ad
|
@ -413,6 +413,8 @@ extern SDL_DECLSPEC SDL_Cursor * SDLCALL SDL_CreateCursor(const Uint8 * data,
|
||||||
/**
|
/**
|
||||||
* Create a color cursor.
|
* Create a color cursor.
|
||||||
*
|
*
|
||||||
|
* If this function is passed a surface with alternate representations, the surface will be interpreted as the content to be used for 100% display scale, and the alternate representations will be used for high DPI situations. For example, if the original surface is 32x32, then on a 2x macOS display or 200% display scale on Windows, a 64x64 version of the image will be used, if available. If a matching version of the image isn't available, the closest size image will be scaled to the appropriate size and be used instead.
|
||||||
|
*
|
||||||
* \param surface an SDL_Surface structure representing the cursor image.
|
* \param surface an SDL_Surface structure representing the cursor image.
|
||||||
* \param hot_x the x position of the cursor hot spot.
|
* \param hot_x the x position of the cursor hot spot.
|
||||||
* \param hot_y the y position of the cursor hot spot.
|
* \param hot_y the y position of the cursor hot spot.
|
||||||
|
|
|
@ -1334,6 +1334,8 @@ extern SDL_DECLSPEC const char * SDLCALL SDL_GetWindowTitle(SDL_Window *window);
|
||||||
/**
|
/**
|
||||||
* Set the icon for a window.
|
* Set the icon for a window.
|
||||||
*
|
*
|
||||||
|
* If this function is passed a surface with alternate representations, the surface will be interpreted as the content to be used for 100% display scale, and the alternate representations will be used for high DPI situations. For example, if the original surface is 32x32, then on a 2x macOS display or 200% display scale on Windows, a 64x64 version of the image will be used, if available. If a matching version of the image isn't available, the closest size image will be scaled to the appropriate size and be used instead.
|
||||||
|
*
|
||||||
* \param window the window to change.
|
* \param window the window to change.
|
||||||
* \param icon an SDL_Surface structure containing the icon for the window.
|
* \param icon an SDL_Surface structure containing the icon for the window.
|
||||||
* \returns 0 on success or a negative error code on failure; call
|
* \returns 0 on success or a negative error code on failure; call
|
||||||
|
|
|
@ -250,43 +250,54 @@ SDL_SystemTheme Cocoa_GetSystemTheme(void)
|
||||||
/* This function assumes that it's called from within an autorelease pool */
|
/* This function assumes that it's called from within an autorelease pool */
|
||||||
NSImage *Cocoa_CreateImage(SDL_Surface *surface)
|
NSImage *Cocoa_CreateImage(SDL_Surface *surface)
|
||||||
{
|
{
|
||||||
SDL_Surface *converted;
|
|
||||||
NSBitmapImageRep *imgrep;
|
|
||||||
Uint8 *pixels;
|
|
||||||
NSImage *img;
|
NSImage *img;
|
||||||
|
|
||||||
converted = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_RGBA32);
|
|
||||||
if (!converted) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Premultiply the alpha channel */
|
|
||||||
SDL_PremultiplySurfaceAlpha(converted, SDL_FALSE);
|
|
||||||
|
|
||||||
imgrep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
|
|
||||||
pixelsWide:converted->w
|
|
||||||
pixelsHigh:converted->h
|
|
||||||
bitsPerSample:8
|
|
||||||
samplesPerPixel:4
|
|
||||||
hasAlpha:YES
|
|
||||||
isPlanar:NO
|
|
||||||
colorSpaceName:NSDeviceRGBColorSpace
|
|
||||||
bytesPerRow:converted->pitch
|
|
||||||
bitsPerPixel:SDL_BITSPERPIXEL(converted->format)];
|
|
||||||
if (imgrep == nil) {
|
|
||||||
SDL_DestroySurface(converted);
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Copy the pixels */
|
|
||||||
pixels = [imgrep bitmapData];
|
|
||||||
SDL_memcpy(pixels, converted->pixels, (size_t)converted->h * converted->pitch);
|
|
||||||
SDL_DestroySurface(converted);
|
|
||||||
|
|
||||||
img = [[NSImage alloc] initWithSize:NSMakeSize(surface->w, surface->h)];
|
img = [[NSImage alloc] initWithSize:NSMakeSize(surface->w, surface->h)];
|
||||||
if (img != nil) {
|
if (img == nil) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Surface **images = SDL_GetSurfaceImages(surface, NULL);
|
||||||
|
if (!images) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; images[i]; ++i) {
|
||||||
|
SDL_Surface *converted = SDL_ConvertSurface(images[i], SDL_PIXELFORMAT_RGBA32);
|
||||||
|
if (!converted) {
|
||||||
|
SDL_free(images);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Premultiply the alpha channel */
|
||||||
|
SDL_PremultiplySurfaceAlpha(converted, SDL_FALSE);
|
||||||
|
|
||||||
|
NSBitmapImageRep *imgrep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
|
||||||
|
pixelsWide:converted->w
|
||||||
|
pixelsHigh:converted->h
|
||||||
|
bitsPerSample:8
|
||||||
|
samplesPerPixel:4
|
||||||
|
hasAlpha:YES
|
||||||
|
isPlanar:NO
|
||||||
|
colorSpaceName:NSDeviceRGBColorSpace
|
||||||
|
bytesPerRow:converted->pitch
|
||||||
|
bitsPerPixel:SDL_BITSPERPIXEL(converted->format)];
|
||||||
|
if (imgrep == nil) {
|
||||||
|
SDL_free(images);
|
||||||
|
SDL_DestroySurface(converted);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy the pixels */
|
||||||
|
Uint8 *pixels = [imgrep bitmapData];
|
||||||
|
SDL_memcpy(pixels, converted->pixels, (size_t)converted->h * converted->pitch);
|
||||||
|
SDL_DestroySurface(converted);
|
||||||
|
|
||||||
|
/* Add the image representation */
|
||||||
[img addRepresentation:imgrep];
|
[img addRepresentation:imgrep];
|
||||||
}
|
}
|
||||||
|
SDL_free(images);
|
||||||
|
|
||||||
return img;
|
return img;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,12 +27,24 @@
|
||||||
#include "SDL_windowsrawinput.h"
|
#include "SDL_windowsrawinput.h"
|
||||||
|
|
||||||
#include "../SDL_video_c.h"
|
#include "../SDL_video_c.h"
|
||||||
|
#include "../SDL_blit.h"
|
||||||
#include "../../events/SDL_mouse_c.h"
|
#include "../../events/SDL_mouse_c.h"
|
||||||
#include "../../joystick/usb_ids.h"
|
#include "../../joystick/usb_ids.h"
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct CachedCursor
|
||||||
|
{
|
||||||
|
float scale;
|
||||||
|
HCURSOR cursor;
|
||||||
|
struct CachedCursor *next;
|
||||||
|
} CachedCursor;
|
||||||
|
|
||||||
struct SDL_CursorData
|
struct SDL_CursorData
|
||||||
{
|
{
|
||||||
|
SDL_Surface *surface;
|
||||||
|
int hot_x;
|
||||||
|
int hot_y;
|
||||||
|
CachedCursor *cache;
|
||||||
HCURSOR cursor;
|
HCURSOR cursor;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -189,6 +201,7 @@ static HCURSOR WIN_CreateHCursor(SDL_Surface *surface, int hot_x, int hot_y)
|
||||||
ii.hbmColor = is_monochrome ? NULL : CreateColorBitmap(surface);
|
ii.hbmColor = is_monochrome ? NULL : CreateColorBitmap(surface);
|
||||||
|
|
||||||
if (!ii.hbmMask || (!is_monochrome && !ii.hbmColor)) {
|
if (!ii.hbmMask || (!is_monochrome && !ii.hbmColor)) {
|
||||||
|
SDL_SetError("Couldn't create cursor bitmaps");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,11 +221,29 @@ static HCURSOR WIN_CreateHCursor(SDL_Surface *surface, int hot_x, int hot_y)
|
||||||
|
|
||||||
static SDL_Cursor *WIN_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y)
|
static SDL_Cursor *WIN_CreateCursor(SDL_Surface *surface, int hot_x, int hot_y)
|
||||||
{
|
{
|
||||||
HCURSOR hcursor = WIN_CreateHCursor(surface, hot_x, hot_y);
|
if (!SDL_SurfaceHasAlternateImages(surface)) {
|
||||||
if (!hcursor) {
|
HCURSOR hcursor = WIN_CreateHCursor(surface, hot_x, hot_y);
|
||||||
return NULL;
|
if (!hcursor) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return WIN_CreateCursorAndData(hcursor);
|
||||||
}
|
}
|
||||||
return WIN_CreateCursorAndData(hcursor);
|
|
||||||
|
// Dynamically generate cursors at the appropriate DPI
|
||||||
|
SDL_Cursor *cursor = (SDL_Cursor *)SDL_calloc(1, sizeof(*cursor));
|
||||||
|
if (cursor) {
|
||||||
|
SDL_CursorData *data = (SDL_CursorData *)SDL_calloc(1, sizeof(*data));
|
||||||
|
if (!data) {
|
||||||
|
SDL_free(cursor);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
data->hot_x = hot_x;
|
||||||
|
data->hot_y = hot_y;
|
||||||
|
data->surface = surface;
|
||||||
|
++surface->refcount;
|
||||||
|
cursor->internal = data;
|
||||||
|
}
|
||||||
|
return cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
static SDL_Cursor *WIN_CreateBlankCursor(void)
|
static SDL_Cursor *WIN_CreateBlankCursor(void)
|
||||||
|
@ -302,6 +333,15 @@ static void WIN_FreeCursor(SDL_Cursor *cursor)
|
||||||
{
|
{
|
||||||
SDL_CursorData *data = cursor->internal;
|
SDL_CursorData *data = cursor->internal;
|
||||||
|
|
||||||
|
if (data->surface) {
|
||||||
|
SDL_DestroySurface(data->surface);
|
||||||
|
}
|
||||||
|
while (data->cache) {
|
||||||
|
CachedCursor *entry = data->cache;
|
||||||
|
data->cache = entry->next;
|
||||||
|
DestroyCursor(entry->cursor);
|
||||||
|
SDL_free(entry);
|
||||||
|
}
|
||||||
if (data->cursor) {
|
if (data->cursor) {
|
||||||
DestroyCursor(data->cursor);
|
DestroyCursor(data->cursor);
|
||||||
}
|
}
|
||||||
|
@ -309,13 +349,74 @@ static void WIN_FreeCursor(SDL_Cursor *cursor)
|
||||||
SDL_free(cursor);
|
SDL_free(cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static HCURSOR GetCachedCursor(SDL_Cursor *cursor)
|
||||||
|
{
|
||||||
|
SDL_CursorData *data = cursor->internal;
|
||||||
|
|
||||||
|
SDL_Window *focus = SDL_GetMouseFocus();
|
||||||
|
if (!focus) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
float scale = SDL_GetDisplayContentScale(SDL_GetDisplayForWindow(focus));
|
||||||
|
for (CachedCursor *entry = data->cache; entry; entry = entry->next) {
|
||||||
|
if (scale == entry->scale) {
|
||||||
|
return entry->cursor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need to create a cursor for this content scale
|
||||||
|
SDL_Surface *surface = NULL;
|
||||||
|
HCURSOR hcursor = NULL;
|
||||||
|
CachedCursor *entry = NULL;
|
||||||
|
|
||||||
|
surface = SDL_GetSurfaceImage(data->surface, scale);
|
||||||
|
if (!surface) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
int hot_x = (int)SDL_round(data->hot_x * scale);
|
||||||
|
int hot_y = (int)SDL_round(data->hot_x * scale);
|
||||||
|
hcursor = WIN_CreateHCursor(surface, hot_x, hot_y);
|
||||||
|
if (!hcursor) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = (CachedCursor *)SDL_malloc(sizeof(*entry));
|
||||||
|
if (!entry) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
entry->cursor = hcursor;
|
||||||
|
entry->scale = scale;
|
||||||
|
entry->next = data->cache;
|
||||||
|
data->cache = entry;
|
||||||
|
|
||||||
|
SDL_DestroySurface(surface);
|
||||||
|
|
||||||
|
return hcursor;
|
||||||
|
|
||||||
|
error:
|
||||||
|
if (surface) {
|
||||||
|
SDL_DestroySurface(surface);
|
||||||
|
}
|
||||||
|
if (hcursor) {
|
||||||
|
DestroyCursor(hcursor);
|
||||||
|
}
|
||||||
|
SDL_free(entry);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static int WIN_ShowCursor(SDL_Cursor *cursor)
|
static int WIN_ShowCursor(SDL_Cursor *cursor)
|
||||||
{
|
{
|
||||||
if (!cursor) {
|
if (!cursor) {
|
||||||
cursor = SDL_blank_cursor;
|
cursor = SDL_blank_cursor;
|
||||||
}
|
}
|
||||||
if (cursor) {
|
if (cursor) {
|
||||||
SDL_cursor = cursor->internal->cursor;
|
if (cursor->internal->surface) {
|
||||||
|
SDL_cursor = GetCachedCursor(cursor);
|
||||||
|
} else {
|
||||||
|
SDL_cursor = cursor->internal->cursor;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
SDL_cursor = NULL;
|
SDL_cursor = NULL;
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
|
@ -109,10 +109,8 @@ static const char *cross[] = {
|
||||||
"0,0"
|
"0,0"
|
||||||
};
|
};
|
||||||
|
|
||||||
static SDL_Cursor *
|
static SDL_Surface *load_image_file(const char *file)
|
||||||
init_color_cursor(const char *file)
|
|
||||||
{
|
{
|
||||||
SDL_Cursor *cursor = NULL;
|
|
||||||
SDL_Surface *surface = SDL_LoadBMP(file);
|
SDL_Surface *surface = SDL_LoadBMP(file);
|
||||||
if (surface) {
|
if (surface) {
|
||||||
if (SDL_GetSurfacePalette(surface)) {
|
if (SDL_GetSurfacePalette(surface)) {
|
||||||
|
@ -138,14 +136,50 @@ init_color_cursor(const char *file)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SDL_Surface *load_image(const char *file)
|
||||||
|
{
|
||||||
|
SDL_Surface *surface = load_image_file(file);
|
||||||
|
if (surface) {
|
||||||
|
/* Add a 2x version of this image, if available */
|
||||||
|
SDL_Surface *surface2x = NULL;
|
||||||
|
const char *ext = SDL_strrchr(file, '.');
|
||||||
|
size_t len = SDL_strlen(file) + 2 + 1;
|
||||||
|
char *file2x = (char *)SDL_malloc(len);
|
||||||
|
if (file2x) {
|
||||||
|
SDL_strlcpy(file2x, file, len);
|
||||||
|
if (ext) {
|
||||||
|
SDL_memcpy(file2x + (ext - file), "2x", 3);
|
||||||
|
SDL_strlcat(file2x, ext, len);
|
||||||
|
} else {
|
||||||
|
SDL_strlcat(file2x, "2x", len);
|
||||||
|
}
|
||||||
|
surface2x = load_image_file(file2x);
|
||||||
|
SDL_free(file2x);
|
||||||
|
}
|
||||||
|
if (surface2x) {
|
||||||
|
SDL_AddSurfaceAlternateImage(surface, surface2x);
|
||||||
|
SDL_DestroySurface(surface2x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SDL_Cursor *init_color_cursor(const char *file)
|
||||||
|
{
|
||||||
|
SDL_Cursor *cursor = NULL;
|
||||||
|
SDL_Surface *surface = load_image(file);
|
||||||
|
if (surface) {
|
||||||
cursor = SDL_CreateColorCursor(surface, 0, 0);
|
cursor = SDL_CreateColorCursor(surface, 0, 0);
|
||||||
SDL_DestroySurface(surface);
|
SDL_DestroySurface(surface);
|
||||||
}
|
}
|
||||||
return cursor;
|
return cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
static SDL_Cursor *
|
static SDL_Cursor *init_system_cursor(const char *image[])
|
||||||
init_system_cursor(const char *image[])
|
|
||||||
{
|
{
|
||||||
int i, row, col;
|
int i, row, col;
|
||||||
Uint8 data[4 * 32];
|
Uint8 data[4 * 32];
|
||||||
|
@ -373,6 +407,14 @@ int main(int argc, char *argv[])
|
||||||
num_cursors = 0;
|
num_cursors = 0;
|
||||||
|
|
||||||
if (color_cursor) {
|
if (color_cursor) {
|
||||||
|
SDL_Surface *icon = load_image(color_cursor);
|
||||||
|
if (icon) {
|
||||||
|
for (i = 0; i < state->num_windows; ++i) {
|
||||||
|
SDL_SetWindowIcon(state->windows[i], icon);
|
||||||
|
}
|
||||||
|
SDL_DestroySurface(icon);
|
||||||
|
}
|
||||||
|
|
||||||
cursor = init_color_cursor(color_cursor);
|
cursor = init_color_cursor(color_cursor);
|
||||||
if (cursor) {
|
if (cursor) {
|
||||||
cursors[num_cursors] = cursor;
|
cursors[num_cursors] = cursor;
|
||||||
|
|
Loading…
Reference in New Issue