Files
abysmal-isle/Scripts/SubSystem/VitaNex/Core/Text/UOFont.cs
Unstable Kitsune b918192e4e Overwrite
Complete Overwrite of the Folder with the free shard. ServUO 57.3 has been added.
2023-11-28 23:20:26 -05:00

769 lines
15 KiB
C#

#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using Server;
using PF = System.Drawing.Imaging.PixelFormat;
#endregion
namespace VitaNex.Text
{
public enum UOEncoding : byte
{
Ascii,
Unicode
}
public enum UOFontStyle
{
Big = 0,
Normal = 1,
Small = 2
}
public sealed class UOFonts
{
private static readonly UOFont[] _Ascii;
private static readonly UOFont[] _Unicode;
private static readonly UOChar[][][] _Chars;
private static readonly byte[] _EmptyBuffer = new byte[0];
static UOFonts()
{
_Ascii = new UOFont[10];
_Unicode = new UOFont[13];
_Chars = new UOChar[2][][];
}
private static string FindFontFile(string file, params object[] args)
{
if (!args.IsNullOrEmpty())
{
file = String.Format(file, args);
}
var path = Path.Combine(Core.BaseDirectory, "Data", "Fonts", file);
if (!File.Exists(path))
{
path = Core.FindDataFile(file);
}
return path;
}
private static Bitmap NewEmptyImage()
{
return new Bitmap(UOFont.DefaultCharSize.Width, UOFont.DefaultCharSize.Height, UOFont.PixelFormat);
}
private static UOChar NewEmptyChar(UOEncoding enc)
{
return new UOChar(enc, 0, 0, NewEmptyImage());
}
private static UOFont Instantiate(UOEncoding enc, byte id)
{
int charsWidth = 0, charsHeight = 0;
var list = _Chars[(byte)enc][id];
var i = list.Length;
while (--i >= 0)
{
charsWidth = Math.Max(charsWidth, list[i].XOffset + list[i].Width);
charsHeight = Math.Max(charsHeight, list[i].YOffset + list[i].Height);
}
return new UOFont(enc, id, 1, 4, (byte)charsWidth, (byte)charsHeight, list);
}
private static UOFont LoadAscii(byte id)
{
if (id >= _Ascii.Length)
{
return null;
}
const UOEncoding enc = UOEncoding.Ascii;
var idx = (byte)enc;
if (_Chars.InBounds(idx, id) && _Chars[idx][id] != null)
{
return _Ascii[id] ?? (_Ascii[id] = Instantiate(enc, id));
}
var fonts = _Chars[idx] ?? (_Chars[idx] = new UOChar[_Ascii.Length][]);
var chars = fonts[id] ?? (fonts[id] = new UOChar[256]);
var path = FindFontFile("fonts.mul");
if (path == null || !File.Exists(path))
{
chars.SetAll(NewEmptyChar(enc));
return _Ascii[id] ?? (_Ascii[id] = Instantiate(enc, id));
}
using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
{
using (var bin = new BinaryReader(fs))
{
for (var i = 0; i <= id; i++)
{
bin.ReadByte(); // header
if (i == id)
{
for (var c = 0; c < 32; c++)
{
chars[c] = NewEmptyChar(enc);
}
}
for (var c = 32; c < chars.Length; c++)
{
var width = bin.ReadByte();
var height = bin.ReadByte();
bin.ReadByte(); // unk
if (i == id)
{
var buffer = _EmptyBuffer;
if (width * height > 0)
{
buffer = bin.ReadBytes((width * height) * 2);
}
chars[c] = new UOChar(enc, 0, 0, GetImage(width, height, buffer, enc));
}
else
{
bin.BaseStream.Seek((width * height) * 2, SeekOrigin.Current);
}
}
}
}
}
return _Ascii[id] ?? (_Ascii[id] = Instantiate(enc, id));
}
private static UOFont LoadUnicode(byte id)
{
if (id >= _Unicode.Length)
{
return null;
}
const UOEncoding enc = UOEncoding.Unicode;
var idx = (byte)enc;
if (_Chars.InBounds(idx, id) && _Chars[idx][id] != null)
{
return _Unicode[id] ?? (_Unicode[id] = Instantiate(enc, id));
}
var fonts = _Chars[idx] ?? (_Chars[idx] = new UOChar[_Unicode.Length][]);
var chars = fonts[id] ?? (fonts[id] = new UOChar[65536]);
var filePath = FindFontFile("unifont{0:#}.mul", id);
if (filePath == null)
{
chars.SetAll(NewEmptyChar(enc));
return _Unicode[id] ?? (_Unicode[id] = Instantiate(enc, id));
}
using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
using (var bin = new BinaryReader(fs))
{
for (int c = 0, o; c < chars.Length; c++)
{
fs.Seek(c * 4, SeekOrigin.Begin);
o = bin.ReadInt32();
if (o <= 0 || o >= fs.Length)
{
chars[c] = NewEmptyChar(enc);
continue;
}
fs.Seek(o, SeekOrigin.Begin);
var x = bin.ReadSByte(); // x-offset
var y = bin.ReadSByte(); // y-offset
var width = bin.ReadByte();
var height = bin.ReadByte();
var buffer = _EmptyBuffer;
if (width * height > 0)
{
buffer = bin.ReadBytes(height * (((width - 1) / 8) + 1));
}
chars[c] = new UOChar(enc, x, y, GetImage(width, height, buffer, enc));
}
}
}
return _Unicode[id] ?? (_Unicode[id] = Instantiate(enc, id));
}
private static unsafe Bitmap GetImage(int width, int height, byte[] buffer, UOEncoding enc)
{
if (width * height <= 0 || buffer.IsNullOrEmpty())
{
return NewEmptyImage();
}
var image = new Bitmap(width, height, UOFont.PixelFormat);
var bound = new Rectangle(0, 0, width, height);
var data = image.LockBits(bound, ImageLockMode.WriteOnly, UOFont.PixelFormat);
var index = 0;
var line = (ushort*)data.Scan0;
var delta = data.Stride >> 1;
int x, y;
ushort pixel;
for (y = 0; y < height; y++, line += delta)
{
var cur = line;
if (cur == null)
{
continue;
}
for (x = 0; x < width; x++)
{
pixel = 0;
if (enc > 0)
{
index = x / 8 + y * ((width + 7) / 8);
}
if (index < buffer.Length)
{
if (enc > 0)
{
pixel = buffer[index];
}
else
{
pixel = (ushort)(buffer[index++] | (buffer[index++] << 8));
}
}
if (enc > 0)
{
pixel &= (ushort)(1 << (7 - (x % 8)));
}
if (pixel == 0)
{
cur[x] = 0;
}
else if (enc > 0)
{
cur[x] = 0x8000;
}
else
{
cur[x] = (ushort)(pixel ^ 0x8000);
}
}
}
image.UnlockBits(data);
return image;
}
public static UOFont GetFont(UOEncoding enc, byte id)
{
switch (enc)
{
case UOEncoding.Ascii:
return LoadAscii(id);
case UOEncoding.Unicode:
return LoadUnicode(id);
}
return null;
}
public static UOFont GetAscii(byte id)
{
return GetFont(UOEncoding.Ascii, id);
}
public static UOFont GetUnicode(byte id)
{
return GetFont(UOEncoding.Unicode, id);
}
public static Bitmap GetImage(UOFont font, char c)
{
return font[c].GetImage();
}
public UOEncoding Encoding { get; private set; }
public UOFont this[int id] => GetFont(Encoding, (byte)id);
public int Count { get; private set; }
public byte DefaultID { get; private set; }
public UOFonts(UOEncoding enc)
{
Encoding = enc;
switch (Encoding)
{
case UOEncoding.Ascii:
{
Count = _Ascii.Length;
DefaultID = 3;
}
break;
case UOEncoding.Unicode:
{
Count = _Unicode.Length;
DefaultID = 1;
}
break;
}
}
public Bitmap GetImage(byte font, char c)
{
if (font >= Count)
{
font = DefaultID;
}
return GetImage(this[font], c);
}
public int GetWidth(byte font, string text)
{
if (font >= Count)
{
font = DefaultID;
}
return this[font].GetWidth(text);
}
public int GetHeight(byte font, string text)
{
if (font >= Count)
{
font = DefaultID;
}
return this[font].GetHeight(text);
}
public Size GetSize(byte font, string text)
{
if (font >= Count)
{
font = DefaultID;
}
return this[font].GetSize(text);
}
public int GetWidth(byte font, params string[] lines)
{
if (font >= Count)
{
font = DefaultID;
}
return this[font].GetWidth(lines);
}
public int GetHeight(byte font, params string[] lines)
{
if (font >= Count)
{
font = DefaultID;
}
return this[font].GetHeight(lines);
}
public Size GetSize(byte font, params string[] lines)
{
if (font >= Count)
{
font = DefaultID;
}
return this[font].GetSize(lines);
}
}
public sealed class UOFont
{
#if MONO
public const PF PixelFormat = PF.Format32bppArgb;
#else
public const PF PixelFormat = PF.Format16bppArgb1555;
#endif
public static Size DefaultCharSize = new Size(8, 10);
public static UOFonts Ascii { get; private set; }
public static UOFonts Unicode { get; private set; }
static UOFont()
{
Ascii = new UOFonts(UOEncoding.Ascii);
Unicode = new UOFonts(UOEncoding.Unicode);
for (var i = 0; i <= 1; i++)
{
VitaNexCore.ToConsole("[UOFont]: Preloaded {0}", Unicode[i]);
}
}
public static void Configure()
{ }
public static UOFont GetFont(UOEncoding enc, byte id)
{
return UOFonts.GetFont(enc, id);
}
public static UOFont GetAscii(byte id)
{
return UOFonts.GetFont(UOEncoding.Ascii, id);
}
public static UOFont GetUnicode(byte id)
{
return UOFonts.GetFont(UOEncoding.Unicode, id);
}
public static Bitmap GetImage(UOFont font, char c)
{
return UOFonts.GetImage(font, c);
}
public static Bitmap GetAsciiImage(byte font, char c)
{
return Ascii.GetImage(font, c);
}
public static Bitmap GetUnicodeImage(byte font, char c)
{
return Unicode.GetImage(font, c);
}
public static int GetAsciiWidth(byte font, string text)
{
return Ascii.GetWidth(font, text);
}
public static int GetAsciiHeight(byte font, string text)
{
return Ascii.GetHeight(font, text);
}
public static Size GetAsciiSize(byte font, string text)
{
return Ascii.GetSize(font, text);
}
public static int GetAsciiWidth(byte font, params string[] lines)
{
return Ascii.GetWidth(font, lines);
}
public static int GetAsciiHeight(byte font, params string[] lines)
{
return Ascii.GetHeight(font, lines);
}
public static Size GetAsciiSize(byte font, params string[] lines)
{
return Ascii.GetSize(font, lines);
}
public static int GetUnicodeWidth(byte font, string text)
{
return Unicode.GetWidth(font, text);
}
public static int GetUnicodeHeight(byte font, string text)
{
return Unicode.GetHeight(font, text);
}
public static Size GetUnicodeSize(byte font, string text)
{
return Unicode.GetSize(font, text);
}
public static int GetUnicodeWidth(byte font, params string[] lines)
{
return Unicode.GetWidth(font, lines);
}
public static int GetUnicodeHeight(byte font, params string[] lines)
{
return Unicode.GetHeight(font, lines);
}
public static Size GetUnicodeSize(byte font, params string[] lines)
{
return Unicode.GetSize(font, lines);
}
public UOEncoding Encoding { get; private set; }
public byte ID { get; private set; }
public byte MaxCharWidth { get; private set; }
public byte MaxCharHeight { get; private set; }
public byte CharSpacing { get; private set; }
public byte LineSpacing { get; private set; }
public byte LineHeight { get; private set; }
public UOChar[] Chars { get; private set; }
public int Length => Chars.Length;
public UOChar this[char c] => Chars[c % Length];
public UOChar this[int i] => Chars[i % Length];
public UOFont(
UOEncoding enc,
byte id,
byte charSpacing,
byte lineSpacing,
byte charsWidth,
byte charsHeight,
UOChar[] chars)
{
Encoding = enc;
ID = id;
CharSpacing = charSpacing;
LineSpacing = lineSpacing;
MaxCharWidth = charsWidth;
MaxCharHeight = charsHeight;
Chars = chars;
}
public int GetWidth(string value)
{
return GetSize(value).Width;
}
public int GetHeight(string value)
{
return GetSize(value).Height;
}
public Size GetSize(string value)
{
var lines = value.Split('\n');
if (lines.Length == 0)
{
lines = new[] { value };
}
return GetSize(lines);
}
public int GetWidth(params string[] lines)
{
return GetSize(lines).Width;
}
public int GetHeight(params string[] lines)
{
return GetSize(lines).Height;
}
public Size GetSize(params string[] lines)
{
var w = 0;
var h = 0;
var space = Chars[' '];
UOChar ci;
foreach (var line in lines.SelectMany(o => o.Contains('\n') ? o.Split('\n') : o.ToEnumerable()))
{
var lw = 0;
var lh = 0;
foreach (var c in line)
{
if (c == '\t')
{
lw += (CharSpacing + space.Width) * 4;
continue;
}
ci = this[c];
if (ci == null)
{
lw += (CharSpacing + space.Width);
continue;
}
lw += (CharSpacing + ci.XOffset + ci.Width);
lh = Math.Max(lh, ci.YOffset + ci.Height);
}
w = Math.Max(w, lw);
h += lh + LineSpacing;
}
return new Size(w, h);
}
public override string ToString()
{
return String.Format("({0}, {1}, {2})", Encoding, ID, Length);
}
}
public sealed class UOChar
{
public Bitmap Image { get; private set; }
public UOEncoding Encoding { get; private set; }
public sbyte XOffset { get; private set; }
public sbyte YOffset { get; private set; }
public byte Width { get; private set; }
public byte Height { get; private set; }
public UOChar(UOEncoding enc, sbyte ox, sbyte oy, Bitmap image)
{
Encoding = enc;
XOffset = ox;
YOffset = oy;
Image = image;
Width = (byte)Image.Width;
Height = (byte)Image.Height;
}
public Bitmap GetImage()
{
return GetImage(false);
}
public Bitmap GetImage(bool fill)
{
return GetImage(fill, Color555.White);
}
public Bitmap GetImage(bool fill, Color555 bgColor)
{
return GetImage(fill, bgColor, Color555.Black);
}
public unsafe Bitmap GetImage(bool fill, Color555 bgColor, Color555 textColor)
{
if (Width * Height <= 0)
{
return null;
}
var image = new Bitmap(Width, Height, UOFont.PixelFormat);
var bound = new Rectangle(0, 0, Width, Height);
var dataSrc = Image.LockBits(bound, ImageLockMode.ReadOnly, UOFont.PixelFormat);
var lineSrc = (ushort*)dataSrc.Scan0;
var deltaSrc = dataSrc.Stride >> 1;
var dataTrg = image.LockBits(bound, ImageLockMode.WriteOnly, UOFont.PixelFormat);
var lineTrg = (ushort*)dataTrg.Scan0;
var deltaTrg = dataTrg.Stride >> 1;
int x, y;
for (y = 0; y < Height; y++, lineSrc += deltaSrc, lineTrg += deltaTrg)
{
var source = lineSrc;
var target = lineTrg;
if (source == null || target == null)
{
continue;
}
for (x = 0; x < Width; x++)
{
if (source[x] != 0)
{
target[x] = textColor;
}
else if (fill)
{
target[x] = bgColor;
}
}
}
Image.UnlockBits(dataSrc);
image.UnlockBits(dataTrg);
return image;
}
}
}