Fix font rendering at non-integer display scales.
This commit is contained in:
@@ -13,7 +13,6 @@ using System;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using OpenRA.Primitives;
|
using OpenRA.Primitives;
|
||||||
using OpenRA.Support;
|
using OpenRA.Support;
|
||||||
using OpenRA.Widgets;
|
|
||||||
|
|
||||||
namespace OpenRA.Graphics
|
namespace OpenRA.Graphics
|
||||||
{
|
{
|
||||||
@@ -75,30 +74,31 @@ namespace OpenRA.Graphics
|
|||||||
// Offset from the baseline position to the top-left of the glyph for rendering
|
// Offset from the baseline position to the top-left of the glyph for rendering
|
||||||
location += new float2(0, size);
|
location += new float2(0, size);
|
||||||
|
|
||||||
|
// Calculate positions in screen pixel coordinates
|
||||||
var screenContrast = (int)(contrastOffset * deviceScale);
|
var screenContrast = (int)(contrastOffset * deviceScale);
|
||||||
var p = location;
|
var screen = new int2((int)(location.X * deviceScale + 0.5f), (int)(location.Y * deviceScale + 0.5f));
|
||||||
|
var contrastVector = new float2(screenContrast, screenContrast);
|
||||||
foreach (var s in text)
|
foreach (var s in text)
|
||||||
{
|
{
|
||||||
if (s == '\n')
|
if (s == '\n')
|
||||||
{
|
{
|
||||||
location += new float2(0, size);
|
location += new float2(0, size);
|
||||||
p = location;
|
screen = new int2((int)(location.X * deviceScale + 0.5f), (int)(location.Y * deviceScale + 0.5f));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Color of source glyph doesn't matter, so use black
|
|
||||||
var g = glyphs[Pair.New(s, Color.Black)];
|
var g = glyphs[Pair.New(s, Color.Black)];
|
||||||
|
|
||||||
|
// Convert screen coordinates back to UI coordinates for drawing
|
||||||
if (g.Sprite != null)
|
if (g.Sprite != null)
|
||||||
{
|
{
|
||||||
var contrastSprite = contrastGlyphs[Tuple.Create(s, contrastColor, screenContrast)];
|
var contrastSprite = contrastGlyphs[Tuple.Create(s, contrastColor, screenContrast)];
|
||||||
Game.Renderer.RgbaSpriteRenderer.DrawSprite(contrastSprite,
|
Game.Renderer.RgbaSpriteRenderer.DrawSprite(contrastSprite,
|
||||||
new float2(
|
(screen + g.Offset - contrastVector) / deviceScale,
|
||||||
(int)Math.Round(p.X * deviceScale + g.Offset.X - screenContrast, 0) / deviceScale,
|
|
||||||
p.Y + (g.Offset.Y - screenContrast) / deviceScale),
|
|
||||||
contrastSprite.Size / deviceScale);
|
contrastSprite.Size / deviceScale);
|
||||||
}
|
}
|
||||||
|
|
||||||
p += new float2(g.Advance / deviceScale, 0);
|
screen += new int2((int)(g.Advance + 0.5f), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,39 +107,40 @@ namespace OpenRA.Graphics
|
|||||||
// Offset from the baseline position to the top-left of the glyph for rendering
|
// Offset from the baseline position to the top-left of the glyph for rendering
|
||||||
location += new float2(0, size);
|
location += new float2(0, size);
|
||||||
|
|
||||||
var p = location;
|
// Calculate positions in screen pixel coordinates
|
||||||
|
var screen = new int2((int)(location.X * deviceScale + 0.5f), (int)(location.Y * deviceScale + 0.5f));
|
||||||
foreach (var s in text)
|
foreach (var s in text)
|
||||||
{
|
{
|
||||||
if (s == '\n')
|
if (s == '\n')
|
||||||
{
|
{
|
||||||
location += new float2(0, size);
|
location += new float2(0, size);
|
||||||
p = location;
|
screen = new int2((int)(location.X * deviceScale + 0.5f), (int)(location.Y * deviceScale + 0.5f));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var g = glyphs[Pair.New(s, c)];
|
var g = glyphs[Pair.New(s, c)];
|
||||||
|
|
||||||
|
// Convert screen coordinates back to UI coordinates for drawing
|
||||||
if (g.Sprite != null)
|
if (g.Sprite != null)
|
||||||
Game.Renderer.RgbaSpriteRenderer.DrawSprite(g.Sprite,
|
Game.Renderer.RgbaSpriteRenderer.DrawSprite(g.Sprite,
|
||||||
new float2(
|
(screen + g.Offset).ToFloat2() / deviceScale,
|
||||||
(int)Math.Round(p.X * deviceScale + g.Offset.X, 0) / deviceScale,
|
|
||||||
p.Y + g.Offset.Y / deviceScale),
|
|
||||||
g.Sprite.Size / deviceScale);
|
g.Sprite.Size / deviceScale);
|
||||||
|
|
||||||
p += new float2(g.Advance / deviceScale, 0);
|
screen += new int2((int)(g.Advance + 0.5f), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float3 Rotate(float3 v, float sina, float cosa, float2 offset)
|
float2 Rotate(float2 v, float sina, float cosa, float2 offset)
|
||||||
{
|
{
|
||||||
return new float3(
|
return new float2(
|
||||||
v.X * cosa - v.Y * sina + offset.X,
|
v.X * cosa - v.Y * sina + offset.X,
|
||||||
v.X * sina + v.Y * cosa + offset.Y,
|
v.X * sina + v.Y * cosa + offset.Y);
|
||||||
0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DrawText(string text, float2 location, Color c, float angle)
|
public void DrawText(string text, float2 location, Color c, float angle)
|
||||||
{
|
{
|
||||||
// Offset from the baseline position to the top-left of the glyph for rendering
|
// Offset from the baseline position to the top-left of the glyph for rendering
|
||||||
|
// All positions are calculated in UI coordinates
|
||||||
var offset = new float2(0, size);
|
var offset = new float2(0, size);
|
||||||
var cosa = (float)Math.Cos(-angle);
|
var cosa = (float)Math.Cos(-angle);
|
||||||
var sina = (float)Math.Sin(-angle);
|
var sina = (float)Math.Sin(-angle);
|
||||||
@@ -157,18 +158,25 @@ namespace OpenRA.Graphics
|
|||||||
var g = glyphs[Pair.New(s, c)];
|
var g = glyphs[Pair.New(s, c)];
|
||||||
if (g.Sprite != null)
|
if (g.Sprite != null)
|
||||||
{
|
{
|
||||||
var tl = new float3(
|
var tl = new float2(
|
||||||
(int)Math.Round(p.X * deviceScale + g.Offset.X, 0) / deviceScale,
|
p.X + g.Offset.X / deviceScale,
|
||||||
p.Y + g.Offset.Y / deviceScale, 0);
|
p.Y + g.Offset.Y / deviceScale);
|
||||||
var br = tl + g.Sprite.Size / deviceScale;
|
var br = tl + g.Sprite.Size.XY / deviceScale;
|
||||||
var tr = new float3(br.X, tl.Y, 0);
|
var tr = new float2(br.X, tl.Y);
|
||||||
var bl = new float3(tl.X, br.Y, 0);
|
var bl = new float2(tl.X, br.Y);
|
||||||
|
|
||||||
|
var ra = Rotate(tl, sina, cosa, location);
|
||||||
|
var rb = Rotate(tr, sina, cosa, location);
|
||||||
|
var rc = Rotate(br, sina, cosa, location);
|
||||||
|
var rd = Rotate(bl, sina, cosa, location);
|
||||||
|
|
||||||
|
// Offset rotated glyph to align the top-left corner with the screen pixel grid
|
||||||
|
var screenOffset = new float2((int)(ra.X * deviceScale + 0.5f), (int)(ra.Y * deviceScale + 0.5f)) / deviceScale - ra;
|
||||||
Game.Renderer.RgbaSpriteRenderer.DrawSprite(g.Sprite,
|
Game.Renderer.RgbaSpriteRenderer.DrawSprite(g.Sprite,
|
||||||
Rotate(tl, sina, cosa, location),
|
ra + screenOffset,
|
||||||
Rotate(tr, sina, cosa, location),
|
rb + screenOffset,
|
||||||
Rotate(br, sina, cosa, location),
|
rc + screenOffset,
|
||||||
Rotate(bl, sina, cosa, location));
|
rd + screenOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
p += new float2(g.Advance / deviceScale, 0);
|
p += new float2(g.Advance / deviceScale, 0);
|
||||||
@@ -191,7 +199,12 @@ namespace OpenRA.Graphics
|
|||||||
public void DrawTextWithShadow(string text, float2 location, Color fg, Color bg, int offset)
|
public void DrawTextWithShadow(string text, float2 location, Color fg, Color bg, int offset)
|
||||||
{
|
{
|
||||||
if (offset != 0)
|
if (offset != 0)
|
||||||
DrawText(text, location + new float2(offset, offset), bg);
|
{
|
||||||
|
// Shadow offsets are rounded to an integer number of screen pixels.
|
||||||
|
// This makes sure the shadow will be positioned consistently everywhere on the screen.
|
||||||
|
var screenOffset = (int)(offset * deviceScale) / deviceScale;
|
||||||
|
DrawText(text, location + new float2(screenOffset, screenOffset), bg);
|
||||||
|
}
|
||||||
|
|
||||||
DrawText(text, location, fg);
|
DrawText(text, location, fg);
|
||||||
}
|
}
|
||||||
@@ -204,7 +217,12 @@ namespace OpenRA.Graphics
|
|||||||
public void DrawTextWithShadow(string text, float2 location, Color fg, Color bg, int offset, float angle)
|
public void DrawTextWithShadow(string text, float2 location, Color fg, Color bg, int offset, float angle)
|
||||||
{
|
{
|
||||||
if (offset != 0)
|
if (offset != 0)
|
||||||
DrawText(text, location + new float2(offset, offset), bg, angle);
|
{
|
||||||
|
// Shadow offsets are rounded to an integer number of screen pixels.
|
||||||
|
// This makes sure the shadow will be positioned consistently everywhere on the screen.
|
||||||
|
var screenOffset = (int)(offset * deviceScale) / deviceScale;
|
||||||
|
DrawText(text, location + new float2(screenOffset, screenOffset), bg, angle);
|
||||||
|
}
|
||||||
|
|
||||||
DrawText(text, location, fg, angle);
|
DrawText(text, location, fg, angle);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user