Ver Fonte

Bitmap fonts now use nearest neighbour scaling instead of xbrz

Ivan Savenko há 1 ano atrás
pai
commit
35467039a7

+ 6 - 1
client/renderSDL/CBitmapFont.cpp

@@ -25,6 +25,7 @@
 #include "../../lib/VCMI_Lib.h"
 
 #include <SDL_surface.h>
+#include <SDL_image.h>
 
 struct AtlasLayout
 {
@@ -199,10 +200,14 @@ CBitmapFont::CBitmapFont(const std::string & filename):
 
 	if (GH.screenHandler().getScalingFactor() != 1)
 	{
-		auto scaledSurface = CSDL_Ext::scaleSurfaceIntegerFactor(atlasImage, GH.screenHandler().getScalingFactor());
+		// scale using nearest neighbour - looks way better with H3 fonts than more high-quality xBRZ
+		// TODO: make configurable?
+		auto scaledSurface = CSDL_Ext::scaleSurfaceIntegerFactor(atlasImage, GH.screenHandler().getScalingFactor(), EScalingAlgorithm::NEAREST);
 		SDL_FreeSurface(atlasImage);
 		atlasImage = scaledSurface;
 	}
+
+	IMG_SavePNG(atlasImage, ("/home/ivan/font_" + filename).c_str());
 }
 
 CBitmapFont::~CBitmapFont()

+ 1 - 21
client/renderSDL/SDLImage.cpp

@@ -273,27 +273,7 @@ std::shared_ptr<ISharedImage> SDLImageShared::scaleInteger(int factor, SDL_Palet
 	if (palette && 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);
 

+ 20 - 7
client/renderSDL/SDL_Extensions.cpp

@@ -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);
 

+ 8 - 1
client/renderSDL/SDL_Extensions.h

@@ -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);