Browse Source

Scale hardware cursor bilinearly

Make the hardware cursor blurry like the rest of the screen.
pull/2730/head
Gleb Mazovetskiy 5 years ago committed by Anders Jenbo
parent
commit
1d48dd095d
  1. 1
      CMakeLists.txt
  2. 12
      Source/hwcursor.cpp
  3. 101
      Source/utils/sdl_bilinear_scale.cpp
  4. 19
      Source/utils/sdl_bilinear_scale.hpp

1
CMakeLists.txt

@ -417,6 +417,7 @@ set(libdevilutionx_SRCS
Source/utils/file_util.cpp
Source/utils/language.cpp
Source/utils/paths.cpp
Source/utils/sdl_bilinear_scale.cpp
Source/utils/sdl_thread.cpp
Source/DiabloUI/art.cpp
Source/DiabloUI/art_draw.cpp

12
Source/hwcursor.cpp

@ -15,6 +15,7 @@
#include "cursor.h"
#include "engine.h"
#include "utils/display.h"
#include "utils/sdl_bilinear_scale.hpp"
#include "utils/sdl_wrap.h"
namespace devilution {
@ -61,6 +62,11 @@ Point GetHotpointPosition(const SDL_Surface &surface, HotpointPosition position)
app_fatal("Unhandled enum value");
}
bool ShouldUseBilinearScaling()
{
return sgOptions.Graphics.szScaleQuality[0] != '0';
}
bool SetHardwareCursor(SDL_Surface *surface, HotpointPosition hotpointPosition)
{
SDLCursorUniquePtr newCursor;
@ -75,7 +81,11 @@ bool SetHardwareCursor(SDL_Surface *surface, HotpointPosition hotpointPosition)
SDLSurfaceUniquePtr converted { SDL_ConvertSurfaceFormat(surface, SDL_PIXELFORMAT_ARGB8888, 0) };
SDLSurfaceUniquePtr scaledSurface = SDLWrap::CreateRGBSurfaceWithFormat(0, scaledSize.width, scaledSize.height, 32, SDL_PIXELFORMAT_ARGB8888);
SDL_BlitScaled(converted.get(), nullptr, scaledSurface.get(), nullptr);
if (ShouldUseBilinearScaling()) {
BilinearScale32(converted.get(), scaledSurface.get());
} else {
SDL_BlitScaled(converted.get(), nullptr, scaledSurface.get(), nullptr);
}
const Point hotpoint = GetHotpointPosition(*scaledSurface, hotpointPosition);
newCursor = SDLCursorUniquePtr { SDL_CreateColorCursor(scaledSurface.get(), hotpoint.x, hotpoint.y) };
}

101
Source/utils/sdl_bilinear_scale.cpp

@ -0,0 +1,101 @@
#include "utils/sdl_bilinear_scale.hpp"
#include <cstdint>
#include <memory>
// Performs bilinear scaling using fixed-width integer math.
namespace devilution {
namespace {
unsigned Frac(unsigned fixedPoint)
{
return fixedPoint & 0xffff;
}
unsigned ToInt(unsigned fixedPoint)
{
return fixedPoint >> 16;
}
std::unique_ptr<unsigned[]> CreateMixFactors(unsigned srcSize, unsigned dstSize)
{
std::unique_ptr<unsigned[]> result { new unsigned[dstSize + 1] };
const auto scale = static_cast<unsigned>(65536.0 * static_cast<float>(srcSize - 1) / dstSize);
unsigned mix = 0;
for (unsigned i = 0; i <= dstSize; ++i) {
result[i] = mix;
mix = Frac(mix) + scale;
}
return result;
};
std::uint8_t MixColors(std::uint8_t first, std::uint8_t second, unsigned ratio)
{
return ToInt((second - first) * ratio) + first;
}
} // namespace
void BilinearScale32(SDL_Surface *src, SDL_Surface *dst)
{
const std::unique_ptr<unsigned[]> mixXs = CreateMixFactors(src->w, dst->w);
const std::unique_ptr<unsigned[]> mixYs = CreateMixFactors(src->h, dst->h);
const unsigned dgap = dst->pitch - dst->w * 4;
auto *srcPixels = static_cast<std::uint8_t *>(src->pixels);
auto *dstPixels = static_cast<std::uint8_t *>(dst->pixels);
unsigned *curMixY = &mixYs[0];
unsigned srcY = 0;
for (unsigned y = 0; y < static_cast<unsigned>(dst->h); ++y) {
std::uint8_t *s[4] = {
srcPixels, // Self
srcPixels + 4, // Right
srcPixels + src->pitch, // Bottom
srcPixels + src->pitch + 4 // Bottom right
};
unsigned *curMixX = &mixXs[0];
unsigned srcX = 0;
for (unsigned x = 0; x < static_cast<unsigned>(dst->w); ++x) {
const unsigned mixX = Frac(*curMixX);
const unsigned mixY = Frac(*curMixY);
for (unsigned channel = 0; channel < 4; ++channel) {
dstPixels[channel] = MixColors(
MixColors(s[0][channel], s[1][channel], mixX),
MixColors(s[2][channel], s[3][channel], mixX),
mixY);
}
++curMixX;
if (*curMixX > 0) {
unsigned step = ToInt(*curMixX);
srcX += step;
if (srcX <= static_cast<unsigned>(src->w)) {
step *= 4;
for (auto &v : s) {
v += step;
}
}
}
dstPixels += 4;
}
++curMixY;
if (*curMixY > 0) {
const unsigned step = ToInt(*curMixY);
srcY += step;
if (srcY < static_cast<unsigned>(src->h)) {
srcPixels += step * src->pitch;
}
}
dstPixels += dgap;
}
}
} // namespace devilution

19
Source/utils/sdl_bilinear_scale.hpp

@ -0,0 +1,19 @@
#pragma once
#include <SDL_version.h>
#if SDL_VERSION_ATLEAST(2, 0, 0)
#include <SDL_surface.h>
#else
#include <SDL_video.h>
#endif
namespace devilution {
/**
* @brief Bilinear 32-bit scaling.
* Requires `src` and `dst` to have the same pixel format (ARGB8888 or RGBA8888).
*/
void BilinearScale32(SDL_Surface *src, SDL_Surface *dst);
} // namespace devilution
Loading…
Cancel
Save