mirror of
https://github.com/libsdl-org/SDL.git
synced 2024-11-26 21:33:26 +08:00
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.
|
||||
*
|
||||
* 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 hot_x the x 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.
|
||||
*
|
||||
* 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 icon an SDL_Surface structure containing the icon for the window.
|
||||
* \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 */
|
||||
NSImage *Cocoa_CreateImage(SDL_Surface *surface)
|
||||
{
|
||||
SDL_Surface *converted;
|
||||
NSBitmapImageRep *imgrep;
|
||||
Uint8 *pixels;
|
||||
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)];
|
||||
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];
|
||||
}
|
||||
SDL_free(images);
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
|
@ -27,12 +27,24 @@
|
||||
#include "SDL_windowsrawinput.h"
|
||||
|
||||
#include "../SDL_video_c.h"
|
||||
#include "../SDL_blit.h"
|
||||
#include "../../events/SDL_mouse_c.h"
|
||||
#include "../../joystick/usb_ids.h"
|
||||
|
||||
|
||||
typedef struct CachedCursor
|
||||
{
|
||||
float scale;
|
||||
HCURSOR cursor;
|
||||
struct CachedCursor *next;
|
||||
} CachedCursor;
|
||||
|
||||
struct SDL_CursorData
|
||||
{
|
||||
SDL_Surface *surface;
|
||||
int hot_x;
|
||||
int hot_y;
|
||||
CachedCursor *cache;
|
||||
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);
|
||||
|
||||
if (!ii.hbmMask || (!is_monochrome && !ii.hbmColor)) {
|
||||
SDL_SetError("Couldn't create cursor bitmaps");
|
||||
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)
|
||||
{
|
||||
HCURSOR hcursor = WIN_CreateHCursor(surface, hot_x, hot_y);
|
||||
if (!hcursor) {
|
||||
return NULL;
|
||||
if (!SDL_SurfaceHasAlternateImages(surface)) {
|
||||
HCURSOR hcursor = WIN_CreateHCursor(surface, hot_x, hot_y);
|
||||
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)
|
||||
@ -302,6 +333,15 @@ static void WIN_FreeCursor(SDL_Cursor *cursor)
|
||||
{
|
||||
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) {
|
||||
DestroyCursor(data->cursor);
|
||||
}
|
||||
@ -309,13 +349,74 @@ static void WIN_FreeCursor(SDL_Cursor *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)
|
||||
{
|
||||
if (!cursor) {
|
||||
cursor = SDL_blank_cursor;
|
||||
}
|
||||
if (cursor) {
|
||||
SDL_cursor = cursor->internal->cursor;
|
||||
if (cursor->internal->surface) {
|
||||
SDL_cursor = GetCachedCursor(cursor);
|
||||
} else {
|
||||
SDL_cursor = cursor->internal->cursor;
|
||||
}
|
||||
} else {
|
||||
SDL_cursor = NULL;
|
||||
}
|
||||
|
BIN
test/icon2x.bmp
Normal file
BIN
test/icon2x.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
@ -109,10 +109,8 @@ static const char *cross[] = {
|
||||
"0,0"
|
||||
};
|
||||
|
||||
static SDL_Cursor *
|
||||
init_color_cursor(const char *file)
|
||||
static SDL_Surface *load_image_file(const char *file)
|
||||
{
|
||||
SDL_Cursor *cursor = NULL;
|
||||
SDL_Surface *surface = SDL_LoadBMP(file);
|
||||
if (surface) {
|
||||
if (SDL_GetSurfacePalette(surface)) {
|
||||
@ -138,14 +136,50 @@ init_color_cursor(const char *file)
|
||||
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);
|
||||
SDL_DestroySurface(surface);
|
||||
}
|
||||
return cursor;
|
||||
}
|
||||
|
||||
static SDL_Cursor *
|
||||
init_system_cursor(const char *image[])
|
||||
static SDL_Cursor *init_system_cursor(const char *image[])
|
||||
{
|
||||
int i, row, col;
|
||||
Uint8 data[4 * 32];
|
||||
@ -373,6 +407,14 @@ int main(int argc, char *argv[])
|
||||
num_cursors = 0;
|
||||
|
||||
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);
|
||||
if (cursor) {
|
||||
cursors[num_cursors] = cursor;
|
||||
|
Loading…
Reference in New Issue
Block a user