When working with custom cursors in SDL2, maintaining cursor sharpness during window scaling involves several considerations and potential solutions.
Custom cursors can become blurry for two main reasons:
Create a system that loads different cursor sizes based on the window scale:
#include <SDL.h>
#include <SDL_image.h>
#include <unordered_map>
#include <string>
class ScalableCursor {
public:
struct CursorSize {
int width;
int height;
SDL_Cursor* cursor;
};
ScalableCursor() {
// Load cursor variants
LoadCursorVariant("cursor_16.png", 16, 16);
LoadCursorVariant("cursor_32.png", 32, 32);
LoadCursorVariant("cursor_64.png", 64, 64);
}
void UpdateForScale(float windowScale) {
// Choose best cursor size for current scale
int targetSize = static_cast<int>(16.0f * windowScale);
SDL_Cursor* bestCursor{nullptr};
int smallestDiff{INT_MAX};
for (const auto& [size, cursorInfo] : Cursors) {
int diff{std::abs(size - targetSize)};
if (diff < smallestDiff) {
smallestDiff = diff;
bestCursor = cursorInfo.cursor;
}
}
if (bestCursor) {
SDL_SetCursor(bestCursor);
}
}
private:
void LoadCursorVariant(
const std::string& path,
int width,
int height
) {
SDL_Surface* surface{IMG_Load(path.c_str())};
if (!surface) {
return;
}
SDL_Cursor* cursor{SDL_CreateColorCursor(
surface, width/2, height/2)};
SDL_FreeSurface(surface);
if (cursor) {
Cursors[width] = {width, height, cursor};
}
}
std::unordered_map<int, CursorSize> Cursors;
};
If you're using custom cursor rendering, maintain pixel-perfect scaling:
class PixelPerfectCursor {
public:
void Render(
SDL_Renderer* renderer,
int mouseX,
int mouseY,
float scale
) {
// Calculate pixel-aligned position
int alignedX{static_cast<int>(
std::round(mouseX / scale) * scale)};
int alignedY{static_cast<int>(
std::round(mouseY / scale) * scale)};
// Set nearest-neighbor scaling
SDL_SetHint(
SDL_HINT_RENDER_SCALE_QUALITY, "0");
SDL_Rect destRect{
alignedX,
alignedY,
static_cast<int>(CursorWidth * scale),
static_cast<int>(CursorHeight * scale)
};
SDL_RenderCopy(
renderer,
CursorTexture,
nullptr,
&destRect
);
}
private:
SDL_Texture* CursorTexture;
int CursorWidth{16};
int CursorHeight{16};
};
Both solutions help maintain cursor sharpness during scaling. The first approach is better for traditional cursor implementation, while the second works well with custom rendering.
Remember to create your cursor images with clean, pixel-art style edges for best results when scaling.
Answers to questions are automatically generated and may not have been reviewed.
Learn how to control cursor visibility, switch between default system cursors, and create custom cursors