Reimplement FreeTypeFont against FreeType directly.
This commit is contained in:
4
Makefile
4
Makefile
@@ -289,7 +289,8 @@ mods: mod_common mod_cnc mod_d2k
|
|||||||
all: dependencies core stylecheck
|
all: dependencies core stylecheck
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
@-$(RM_F) *.exe *.dll *.dylib *.dll.config ./OpenRA*/*.dll ./OpenRA*/*.mdb *.mdb mods/**/*.dll mods/**/*.mdb *.resources
|
@-$(RM_F) $(shell find . -maxdepth 1 -iname '*.dll.config' -a ! -iname 'OpenRA.Platforms.Default.dll.config')
|
||||||
|
@-$(RM_F) *.exe *.dll *.dylib ./OpenRA*/*.dll ./OpenRA*/*.mdb *.mdb mods/**/*.dll mods/**/*.mdb *.resources
|
||||||
@-$(RM_RF) ./*/bin ./*/obj
|
@-$(RM_RF) ./*/bin ./*/obj
|
||||||
@-$(RM_RF) ./thirdparty/download
|
@-$(RM_RF) ./thirdparty/download
|
||||||
|
|
||||||
@@ -340,6 +341,7 @@ install-engine:
|
|||||||
@-echo "Installing OpenRA engine to $(DATA_INSTALL_DIR)"
|
@-echo "Installing OpenRA engine to $(DATA_INSTALL_DIR)"
|
||||||
@$(INSTALL_DIR) "$(DATA_INSTALL_DIR)"
|
@$(INSTALL_DIR) "$(DATA_INSTALL_DIR)"
|
||||||
@$(INSTALL_PROGRAM) $(foreach prog,$(CORE),$($(prog)_TARGET)) "$(DATA_INSTALL_DIR)"
|
@$(INSTALL_PROGRAM) $(foreach prog,$(CORE),$($(prog)_TARGET)) "$(DATA_INSTALL_DIR)"
|
||||||
|
@$(CP) OpenRA.Platforms.Default.dll.config "$(DATA_INSTALL_DIR)"
|
||||||
|
|
||||||
@$(INSTALL_DATA) "GeoLite2-Country.mmdb.gz" "$(DATA_INSTALL_DIR)/GeoLite2-Country.mmdb.gz"
|
@$(INSTALL_DATA) "GeoLite2-Country.mmdb.gz" "$(DATA_INSTALL_DIR)/GeoLite2-Country.mmdb.gz"
|
||||||
@$(INSTALL_DATA) VERSION "$(DATA_INSTALL_DIR)/VERSION"
|
@$(INSTALL_DATA) VERSION "$(DATA_INSTALL_DIR)/VERSION"
|
||||||
|
|||||||
6
OpenRA.Platforms.Default.dll.config
Normal file
6
OpenRA.Platforms.Default.dll.config
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<configuration>
|
||||||
|
<dllmap dll="freetype6" os="linux" target="libfreetype.so.6" />
|
||||||
|
<dllmap dll="freetype6" os="osx" target="/Library/Frameworks/Mono.framework/Libraries/libfreetype.6.dylib" />
|
||||||
|
<dllmap dll="freetype6" os="freebsd" target="libfreetype.so.6" />
|
||||||
|
</configuration>
|
||||||
@@ -9,75 +9,140 @@
|
|||||||
*/
|
*/
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using OpenRA.Primitives;
|
using OpenRA.Primitives;
|
||||||
using SharpFont;
|
|
||||||
|
|
||||||
namespace OpenRA.Platforms.Default
|
namespace OpenRA.Platforms.Default
|
||||||
{
|
{
|
||||||
|
[SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules", "SA1307:AccessibleFieldsMustBeginWithUpperCaseLetter",
|
||||||
|
Justification = "C-style naming is kept for consistency with the underlying native API.")]
|
||||||
|
[SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules", "SA1310:FieldNamesMustNotContainUnderscore",
|
||||||
|
Justification = "C-style naming is kept for consistency with the underlying native API.")]
|
||||||
|
internal static class FreeType
|
||||||
|
{
|
||||||
|
internal const uint OK = 0x00;
|
||||||
|
internal const int FT_LOAD_RENDER = 0x04;
|
||||||
|
|
||||||
|
internal static readonly int FaceRecGlyphOffset = IntPtr.Size == 8 ? 152 : 84; // offsetof(FT_FaceRec, glyph)
|
||||||
|
internal static readonly int GlyphSlotMetricsOffset = IntPtr.Size == 8 ? 48 : 24; // offsetof(FT_GlyphSlotRec, metrics)
|
||||||
|
internal static readonly int GlyphSlotBitmapOffset = IntPtr.Size == 8 ? 152 : 76; // offsetof(FT_GlyphSlotRec, bitmap)
|
||||||
|
internal static readonly int GlyphSlotBitmapLeftOffset = IntPtr.Size == 8 ? 192 : 100; // offsetof(FT_GlyphSlotRec, bitmap_left)
|
||||||
|
internal static readonly int GlyphSlotBitmapTopOffset = IntPtr.Size == 8 ? 196 : 104; // offsetof(FT_GlyphSlotRec, bitmap_top)
|
||||||
|
internal static readonly int MetricsWidthOffset = 0; // offsetof(FT_Glyph_Metrics, width)
|
||||||
|
internal static readonly int MetricsHeightOffset = IntPtr.Size == 8 ? 8 : 4; // offsetof(FT_Glyph_Metrics, height)
|
||||||
|
internal static readonly int MetricsAdvanceOffset = IntPtr.Size == 8 ? 32 : 16; // offsetof(FT_Glyph_Metrics, horiAdvance)
|
||||||
|
internal static readonly int BitmapPitchOffset = 8; // offsetof(FT_Bitmap, pitch)
|
||||||
|
internal static readonly int BitmapBufferOffset = IntPtr.Size == 8 ? 16 : 12; // offsetof(FT_Bitmap, buffer)
|
||||||
|
|
||||||
|
[DllImport("freetype6", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
internal static extern uint FT_Init_FreeType(out IntPtr library);
|
||||||
|
|
||||||
|
[DllImport("freetype6", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
internal static extern uint FT_New_Memory_Face(IntPtr library, IntPtr file_base, int file_size, int face_index, out IntPtr aface);
|
||||||
|
|
||||||
|
[DllImport("freetype6", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
internal static extern uint FT_Done_Face(IntPtr face);
|
||||||
|
|
||||||
|
[DllImport("freetype6", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
internal static extern uint FT_Set_Pixel_Sizes(IntPtr face, uint pixel_width, uint pixel_height);
|
||||||
|
|
||||||
|
[DllImport("freetype6", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
internal static extern uint FT_Load_Char(IntPtr face, uint char_code, int load_flags);
|
||||||
|
}
|
||||||
|
|
||||||
public sealed class FreeTypeFont : IFont
|
public sealed class FreeTypeFont : IFont
|
||||||
{
|
{
|
||||||
static readonly Library Library = new Library();
|
static readonly FontGlyph EmptyGlyph = new FontGlyph
|
||||||
readonly Face face;
|
{
|
||||||
|
Offset = int2.Zero,
|
||||||
|
Size = new Size(0, 0),
|
||||||
|
Advance = 0,
|
||||||
|
Data = null
|
||||||
|
};
|
||||||
|
|
||||||
|
static IntPtr library = IntPtr.Zero;
|
||||||
|
readonly GCHandle faceHandle;
|
||||||
|
readonly IntPtr face;
|
||||||
|
bool disposed;
|
||||||
|
|
||||||
public FreeTypeFont(byte[] data)
|
public FreeTypeFont(byte[] data)
|
||||||
{
|
{
|
||||||
face = new Face(Library, data, 0);
|
if (library == IntPtr.Zero && FreeType.FT_Init_FreeType(out library) != FreeType.OK)
|
||||||
|
throw new InvalidOperationException("Failed to initialize FreeType");
|
||||||
|
|
||||||
|
faceHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
|
||||||
|
if (FreeType.FT_New_Memory_Face(library, faceHandle.AddrOfPinnedObject(), data.Length, 0, out face) != FreeType.OK)
|
||||||
|
throw new InvalidDataException("Failed to initialize font");
|
||||||
}
|
}
|
||||||
|
|
||||||
public FontGlyph CreateGlyph(char c, int size, float deviceScale)
|
public FontGlyph CreateGlyph(char c, int size, float deviceScale)
|
||||||
{
|
{
|
||||||
try
|
var scaledSize = (uint)(size * deviceScale);
|
||||||
|
if (FreeType.FT_Set_Pixel_Sizes(face, scaledSize, scaledSize) != FreeType.OK)
|
||||||
|
return EmptyGlyph;
|
||||||
|
|
||||||
|
if (FreeType.FT_Load_Char(face, c, FreeType.FT_LOAD_RENDER) != FreeType.OK)
|
||||||
|
return EmptyGlyph;
|
||||||
|
|
||||||
|
// Extract the glyph data we care about
|
||||||
|
// HACK: This uses raw pointer offsets to avoid defining structs and types that are 95% unnecessary
|
||||||
|
var glyph = Marshal.ReadIntPtr(IntPtr.Add(face, FreeType.FaceRecGlyphOffset)); // face->glyph
|
||||||
|
|
||||||
|
var metrics = IntPtr.Add(glyph, FreeType.GlyphSlotMetricsOffset); // face->glyph->metrics
|
||||||
|
var metricsWidth = Marshal.ReadIntPtr(IntPtr.Add(metrics, FreeType.MetricsWidthOffset)); // face->glyph->metrics.width
|
||||||
|
var metricsHeight = Marshal.ReadIntPtr(IntPtr.Add(metrics, FreeType.MetricsHeightOffset)); // face->glyph->metrics.width
|
||||||
|
var metricsAdvance = Marshal.ReadIntPtr(IntPtr.Add(metrics, FreeType.MetricsAdvanceOffset)); // face->glyph->metrics.horiAdvance
|
||||||
|
|
||||||
|
var bitmap = IntPtr.Add(glyph, FreeType.GlyphSlotBitmapOffset); // face->glyph->bitmap
|
||||||
|
var bitmapPitch = Marshal.ReadInt32(IntPtr.Add(bitmap, FreeType.BitmapPitchOffset)); // face->glyph->bitmap.pitch
|
||||||
|
var bitmapBuffer = Marshal.ReadIntPtr(IntPtr.Add(bitmap, FreeType.BitmapBufferOffset)); // face->glyph->bitmap.buffer
|
||||||
|
|
||||||
|
var bitmapLeft = Marshal.ReadInt32(IntPtr.Add(glyph, FreeType.GlyphSlotBitmapLeftOffset)); // face->glyph.bitmap_left
|
||||||
|
var bitmapTop = Marshal.ReadInt32(IntPtr.Add(glyph, FreeType.GlyphSlotBitmapTopOffset)); // face->glyph.bitmap_top
|
||||||
|
|
||||||
|
// Convert FreeType's 26.6 fixed point format to integers by discarding fractional bits
|
||||||
|
var glyphSize = new Size((int)metricsWidth >> 6, (int)metricsHeight >> 6);
|
||||||
|
var glyphAdvance = (int)metricsAdvance >> 6;
|
||||||
|
|
||||||
|
var g = new FontGlyph
|
||||||
{
|
{
|
||||||
var scaledSize = (uint) (size * deviceScale);
|
Advance = glyphAdvance,
|
||||||
face.SetPixelSizes(scaledSize, scaledSize);
|
Offset = new int2(bitmapLeft, -bitmapTop),
|
||||||
|
Size = glyphSize,
|
||||||
|
Data = new byte[glyphSize.Width * glyphSize.Height]
|
||||||
|
};
|
||||||
|
|
||||||
face.LoadChar(c, LoadFlags.Default, LoadTarget.Normal);
|
unsafe
|
||||||
face.Glyph.RenderGlyph(RenderMode.Normal);
|
{
|
||||||
|
var p = (byte*)bitmapBuffer;
|
||||||
var glyphSize = new Size((int)face.Glyph.Metrics.Width, (int)face.Glyph.Metrics.Height);
|
var k = 0;
|
||||||
|
for (var j = 0; j < glyphSize.Height; j++)
|
||||||
var g = new FontGlyph
|
|
||||||
{
|
{
|
||||||
Advance = (float)face.Glyph.Metrics.HorizontalAdvance,
|
for (var i = 0; i < glyphSize.Width; i++)
|
||||||
Offset = new int2(face.Glyph.BitmapLeft, -face.Glyph.BitmapTop),
|
g.Data[k++] = p[i];
|
||||||
Size = glyphSize,
|
|
||||||
Data = new byte[glyphSize.Width * glyphSize.Height]
|
|
||||||
};
|
|
||||||
|
|
||||||
// A new bitmap is generated each time this property is accessed, so we do need to dispose it.
|
p += bitmapPitch;
|
||||||
using (var bitmap = face.Glyph.Bitmap)
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
var p = (byte*)bitmap.Buffer;
|
|
||||||
var k = 0;
|
|
||||||
for (var j = 0; j < glyphSize.Height; j++)
|
|
||||||
{
|
|
||||||
for (var i = 0; i < glyphSize.Width; i++)
|
|
||||||
g.Data[k++] = p[i];
|
|
||||||
|
|
||||||
p += bitmap.Pitch;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return g;
|
return g;
|
||||||
}
|
|
||||||
catch (FreeTypeException)
|
|
||||||
{
|
|
||||||
return new FontGlyph
|
|
||||||
{
|
|
||||||
Offset = int2.Zero,
|
|
||||||
Size = new Size(0, 0),
|
|
||||||
Advance = 0,
|
|
||||||
Data = null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
face.Dispose();
|
if (!disposed)
|
||||||
|
{
|
||||||
|
if (faceHandle.IsAllocated)
|
||||||
|
{
|
||||||
|
FreeType.FT_Done_Face(face);
|
||||||
|
|
||||||
|
faceHandle.Free();
|
||||||
|
disposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user