mirror of
https://github.com/vcmi/vcmi.git
synced 2024-11-28 08:48:48 +02:00
Merge pull request #4555 from IvanSavenko/xbrz_fonts
Improvements for fonts when xbrz is in use
This commit is contained in:
commit
624607caae
@ -16,15 +16,17 @@
|
||||
#include "../render/Colors.h"
|
||||
#include "../render/IScreenHandler.h"
|
||||
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
#include "../../lib/Rect.h"
|
||||
#include "../../lib/VCMI_Lib.h"
|
||||
#include "../../lib/filesystem/Filesystem.h"
|
||||
#include "../../lib/modding/CModHandler.h"
|
||||
#include "../../lib/texts/Languages.h"
|
||||
#include "../../lib/texts/TextOperations.h"
|
||||
#include "../../lib/vcmi_endian.h"
|
||||
#include "../../lib/VCMI_Lib.h"
|
||||
|
||||
#include <SDL_surface.h>
|
||||
#include <SDL_image.h>
|
||||
|
||||
struct AtlasLayout
|
||||
{
|
||||
@ -199,10 +201,20 @@ CBitmapFont::CBitmapFont(const std::string & filename):
|
||||
|
||||
if (GH.screenHandler().getScalingFactor() != 1)
|
||||
{
|
||||
auto scaledSurface = CSDL_Ext::scaleSurfaceIntegerFactor(atlasImage, GH.screenHandler().getScalingFactor());
|
||||
static const std::map<std::string, EScalingAlgorithm> filterNameToEnum = {
|
||||
{ "nearest", EScalingAlgorithm::NEAREST},
|
||||
{ "bilinear", EScalingAlgorithm::BILINEAR},
|
||||
{ "xbrz", EScalingAlgorithm::XBRZ}
|
||||
};
|
||||
|
||||
auto filterName = settings["video"]["fontUpscalingFilter"].String();
|
||||
EScalingAlgorithm algorithm = filterNameToEnum.at(filterName);
|
||||
auto scaledSurface = CSDL_Ext::scaleSurfaceIntegerFactor(atlasImage, GH.screenHandler().getScalingFactor(), algorithm);
|
||||
SDL_FreeSurface(atlasImage);
|
||||
atlasImage = scaledSurface;
|
||||
}
|
||||
|
||||
IMG_SavePNG(atlasImage, ("/home/ivan/font_" + filename).c_str());
|
||||
}
|
||||
|
||||
CBitmapFont::~CBitmapFont()
|
||||
|
@ -27,19 +27,25 @@ std::pair<std::unique_ptr<ui8[]>, ui64> CTrueTypeFont::loadData(const JsonNode &
|
||||
return CResourceHandler::get()->load(ResourcePath(filename, EResType::TTF_FONT))->readAll();
|
||||
}
|
||||
|
||||
int CTrueTypeFont::getPointSize(const JsonNode & config) const
|
||||
{
|
||||
int scalingFactor = getScalingFactor();
|
||||
|
||||
if (config.isNumber())
|
||||
return config.Integer() * scalingFactor;
|
||||
else
|
||||
return config[scalingFactor-1].Integer();
|
||||
}
|
||||
|
||||
TTF_Font * CTrueTypeFont::loadFont(const JsonNode &config)
|
||||
{
|
||||
int pointSizeBase = static_cast<int>(config["size"].Float());
|
||||
int scalingFactor = getScalingFactor();
|
||||
int pointSize = pointSizeBase * scalingFactor;
|
||||
|
||||
if(!TTF_WasInit() && TTF_Init()==-1)
|
||||
throw std::runtime_error(std::string("Failed to initialize true type support: ") + TTF_GetError() + "\n");
|
||||
|
||||
return TTF_OpenFontRW(SDL_RWFromConstMem(data.first.get(), (int)data.second), 1, pointSize);
|
||||
return TTF_OpenFontRW(SDL_RWFromConstMem(data.first.get(), data.second), 1, getPointSize(config["size"]));
|
||||
}
|
||||
|
||||
int CTrueTypeFont::getFontStyle(const JsonNode &config)
|
||||
int CTrueTypeFont::getFontStyle(const JsonNode &config) const
|
||||
{
|
||||
const JsonVector & names = config["style"].Vector();
|
||||
int ret = 0;
|
||||
|
@ -30,7 +30,8 @@ class CTrueTypeFont final : public IFont
|
||||
|
||||
std::pair<std::unique_ptr<ui8[]>, ui64> loadData(const JsonNode & config);
|
||||
TTF_Font * loadFont(const JsonNode & config);
|
||||
int getFontStyle(const JsonNode & config);
|
||||
int getPointSize(const JsonNode & config) const;
|
||||
int getFontStyle(const JsonNode & config) const;
|
||||
|
||||
void renderText(SDL_Surface * surface, const std::string & data, const ColorRGBA & color, const Point & pos) const override;
|
||||
public:
|
||||
|
@ -270,30 +270,10 @@ std::shared_ptr<ISharedImage> SDLImageShared::scaleInteger(int factor, SDL_Palet
|
||||
if (factor <= 0)
|
||||
throw std::runtime_error("Unable to scale by integer value of " + std::to_string(factor));
|
||||
|
||||
if (palette && surf->format->palette)
|
||||
if (palette && surf && surf->format->palette)
|
||||
SDL_SetSurfacePalette(surf, palette);
|
||||
|
||||
/// Convert current surface to ARGB format suitable for xBRZ
|
||||
/// TODO: skip its creation if this is format matches current surface (even if unlikely)
|
||||
SDL_Surface * intermediate = SDL_ConvertSurfaceFormat(surf, SDL_PIXELFORMAT_ARGB8888, 0);
|
||||
SDL_Surface * scaled = CSDL_Ext::newSurface(Point(surf->w * factor, surf->h * factor), intermediate);
|
||||
|
||||
assert(intermediate->pitch == intermediate->w * 4);
|
||||
assert(scaled->pitch == scaled->w * 4);
|
||||
|
||||
const uint32_t * srcPixels = static_cast<const uint32_t*>(intermediate->pixels);
|
||||
uint32_t * dstPixels = static_cast<uint32_t*>(scaled->pixels);
|
||||
|
||||
// avoid excessive granulation - xBRZ prefers at least 8-16 lines per task
|
||||
// TODO: compare performance and size of images, recheck values for potentially better parameters
|
||||
const int granulation = std::clamp(surf->h / 64 * 8, 8, 64);
|
||||
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, intermediate->h, granulation), [factor, srcPixels, dstPixels, intermediate](const tbb::blocked_range<size_t> & r)
|
||||
{
|
||||
xbrz::scale(factor, srcPixels, dstPixels, intermediate->w, intermediate->h, xbrz::ColorFormat::ARGB, {}, r.begin(), r.end());
|
||||
});
|
||||
|
||||
SDL_FreeSurface(intermediate);
|
||||
SDL_Surface * scaled = CSDL_Ext::scaleSurfaceIntegerFactor(surf, factor, EScalingAlgorithm::XBRZ);
|
||||
|
||||
auto ret = std::make_shared<SDLImageShared>(scaled);
|
||||
|
||||
@ -307,7 +287,7 @@ std::shared_ptr<ISharedImage> SDLImageShared::scaleInteger(int factor, SDL_Palet
|
||||
// erase our own reference
|
||||
SDL_FreeSurface(scaled);
|
||||
|
||||
if (surf->format->palette)
|
||||
if (surf && surf->format->palette)
|
||||
SDL_SetSurfacePalette(surf, originalPalette);
|
||||
|
||||
return ret;
|
||||
|
@ -638,8 +638,8 @@ SDL_Surface * CSDL_Ext::scaleSurface(SDL_Surface * surf, int width, int height)
|
||||
if(!surf || !width || !height)
|
||||
return nullptr;
|
||||
|
||||
if (surf->w * 2 == width && surf->h * 2 == height)
|
||||
return scaleSurfaceIntegerFactor(surf, 2);
|
||||
// TODO: use xBRZ if possible? E.g. when scaling to 150% do 100% -> 200% via xBRZ and then linear downscale 200% -> 150%?
|
||||
// Need to investigate which is optimal for performance and for visuals
|
||||
|
||||
SDL_Surface * intermediate = SDL_ConvertSurface(surf, screen->format, 0);
|
||||
SDL_Surface * ret = newSurface(Point(width, height), intermediate);
|
||||
@ -654,7 +654,7 @@ SDL_Surface * CSDL_Ext::scaleSurface(SDL_Surface * surf, int width, int height)
|
||||
return ret;
|
||||
}
|
||||
|
||||
SDL_Surface * CSDL_Ext::scaleSurfaceIntegerFactor(SDL_Surface * surf, int factor)
|
||||
SDL_Surface * CSDL_Ext::scaleSurfaceIntegerFactor(SDL_Surface * surf, int factor, EScalingAlgorithm algorithm)
|
||||
{
|
||||
if(surf == nullptr || factor == 0)
|
||||
return nullptr;
|
||||
@ -662,7 +662,7 @@ SDL_Surface * CSDL_Ext::scaleSurfaceIntegerFactor(SDL_Surface * surf, int factor
|
||||
int newWidth = surf->w * factor;
|
||||
int newHight = surf->h * factor;
|
||||
|
||||
SDL_Surface * intermediate = SDL_ConvertSurface(surf, screen->format, 0);
|
||||
SDL_Surface * intermediate = SDL_ConvertSurfaceFormat(surf, SDL_PIXELFORMAT_ARGB8888, 0);
|
||||
SDL_Surface * ret = newSurface(Point(newWidth, newHight), intermediate);
|
||||
|
||||
assert(intermediate->pitch == intermediate->w * 4);
|
||||
@ -675,10 +675,23 @@ SDL_Surface * CSDL_Ext::scaleSurfaceIntegerFactor(SDL_Surface * surf, int factor
|
||||
// TODO: compare performance and size of images, recheck values for potentially better parameters
|
||||
const int granulation = std::clamp(surf->h / 64 * 8, 8, 64);
|
||||
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, intermediate->h, granulation), [factor, srcPixels, dstPixels, intermediate](const tbb::blocked_range<size_t> & r)
|
||||
switch (algorithm)
|
||||
{
|
||||
xbrz::scale(factor, srcPixels, dstPixels, intermediate->w, intermediate->h, xbrz::ColorFormat::ARGB, {}, r.begin(), r.end());
|
||||
});
|
||||
case EScalingAlgorithm::NEAREST:
|
||||
xbrz::nearestNeighborScale(srcPixels, intermediate->w, intermediate->h, dstPixels, ret->w, ret->h);
|
||||
break;
|
||||
case EScalingAlgorithm::BILINEAR:
|
||||
xbrz::bilinearScale(srcPixels, intermediate->w, intermediate->h, dstPixels, ret->w, ret->h);
|
||||
break;
|
||||
case EScalingAlgorithm::XBRZ:
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, intermediate->h, granulation), [factor, srcPixels, dstPixels, intermediate](const tbb::blocked_range<size_t> & r)
|
||||
{
|
||||
xbrz::scale(factor, srcPixels, dstPixels, intermediate->w, intermediate->h, xbrz::ColorFormat::ARGB, {}, r.begin(), r.end());
|
||||
});
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("invalid scaling algorithm!");
|
||||
}
|
||||
|
||||
SDL_FreeSurface(intermediate);
|
||||
|
||||
|
@ -27,6 +27,13 @@ class Point;
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
enum class EScalingAlgorithm : int8_t
|
||||
{
|
||||
NEAREST,
|
||||
BILINEAR,
|
||||
XBRZ
|
||||
};
|
||||
|
||||
namespace CSDL_Ext
|
||||
{
|
||||
|
||||
@ -92,7 +99,7 @@ using TColorPutterAlpha = void (*)(uint8_t *&, const uint8_t &, const uint8_t &,
|
||||
|
||||
// bilinear filtering. Always returns rgba surface
|
||||
SDL_Surface * scaleSurface(SDL_Surface * surf, int width, int height);
|
||||
SDL_Surface * scaleSurfaceIntegerFactor(SDL_Surface * surf, int factor);
|
||||
SDL_Surface * scaleSurfaceIntegerFactor(SDL_Surface * surf, int factor, EScalingAlgorithm scaler);
|
||||
|
||||
template<int bpp>
|
||||
void convertToGrayscaleBpp(SDL_Surface * surf, const Rect & rect);
|
||||
|
@ -73,20 +73,19 @@ std::vector<std::string> CMessage::breakText(std::string text, size_t maxLineWid
|
||||
// each iteration generates one output line
|
||||
while(text.length())
|
||||
{
|
||||
ui32 lineWidth = 0; //in characters or given char metric
|
||||
ui32 wordBreak = -1; //last position for line break (last space character)
|
||||
ui32 currPos = 0; //current position in text
|
||||
bool opened = false; //set to true when opening brace is found
|
||||
std::string color; //color found
|
||||
|
||||
size_t symbolSize = 0; // width of character, in bytes
|
||||
size_t glyphWidth = 0; // width of printable glyph, pixels
|
||||
|
||||
std::string printableString;
|
||||
|
||||
// loops till line is full or end of text reached
|
||||
while(currPos < text.length() && text[currPos] != 0x0a && lineWidth < maxLineWidth)
|
||||
while(currPos < text.length() && text[currPos] != 0x0a)
|
||||
{
|
||||
symbolSize = TextOperations::getUnicodeCharacterSize(text[currPos]);
|
||||
glyphWidth = graphics->fonts[font]->getGlyphWidth(text.data() + currPos);
|
||||
|
||||
// candidate for line break
|
||||
if(ui8(text[currPos]) <= ui8(' '))
|
||||
@ -116,7 +115,14 @@ std::vector<std::string> CMessage::breakText(std::string text, size_t maxLineWid
|
||||
color = "";
|
||||
}
|
||||
else
|
||||
lineWidth += glyphWidth;
|
||||
{
|
||||
std::string newPrintableString = printableString;
|
||||
newPrintableString.append(text.data() + currPos, symbolSize);
|
||||
if (graphics->fonts[font]->getStringWidth(newPrintableString) < maxLineWidth)
|
||||
printableString.append(text.data() + currPos, symbolSize);
|
||||
else
|
||||
break;
|
||||
}
|
||||
currPos += symbolSize;
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,11 @@
|
||||
// Should be in format:
|
||||
// <replaced bitmap font name, case-sensetive> : <true type font description>
|
||||
// "file" - file to load font from, must be in data/ directory
|
||||
// "size" - point size of font
|
||||
// "size" - point size of font. Can be defined in two forms:
|
||||
// a) single number, e.g. 10. In this case, game will automatically multiply font size by upscaling factor when xBRZ is in use,
|
||||
// so xbrz 2x will use 20px, xbrz 3x will use 30px, etc
|
||||
// b) list of scaling factors for each scaling mode, e.g. [ 10, 16, 22, 26]. In this case game will select point size according to xBRZ scaling factor
|
||||
// so unscaled mode will use 10px, xbrz2 will use 16px, and xbrz3 will use 22
|
||||
// "style" - italic and\or bold, indicates font style
|
||||
// "blend" - if set to true, font will be antialiased
|
||||
"trueType":
|
||||
|
@ -167,6 +167,7 @@
|
||||
"targetfps",
|
||||
"vsync",
|
||||
"upscalingFilter",
|
||||
"fontUpscalingFilter",
|
||||
"downscalingFilter"
|
||||
],
|
||||
"properties" : {
|
||||
@ -231,6 +232,11 @@
|
||||
"type" : "boolean",
|
||||
"default" : true
|
||||
},
|
||||
"fontUpscalingFilter" : {
|
||||
"type" : "string",
|
||||
"enum" : [ "nearest", "bilinear", "xbrz" ],
|
||||
"default" : "nearest"
|
||||
},
|
||||
"upscalingFilter" : {
|
||||
"type" : "string",
|
||||
"enum" : [ "auto", "none", "xbrz2", "xbrz3", "xbrz4" ],
|
||||
|
Loading…
Reference in New Issue
Block a user