Preventing Cursor Blur When Scaling
Why does my custom cursor look blurry/pixelated when I scale my window? How can I make it stay sharp?
When working with custom cursors in SDL2, maintaining cursor sharpness during window scaling involves several considerations and potential solutions.
Understanding the Problem
Custom cursors can become blurry for two main reasons:
- Window scaling causing pixel interpolation
- Using cursor images at the wrong resolution for your display
Solution 1: Multi-Resolution Cursors
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;
};
Solution 2: Custom Rendering with Pixel-Perfect Scaling
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.
Customising Mouse Cursors
Learn how to control cursor visibility, switch between default system cursors, and create custom cursors