feat: Initial commit
This commit is contained in:
16
Assets/Cryville.Common/Cryville.Common.asmdef
Normal file
16
Assets/Cryville.Common/Cryville.Common.asmdef
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "Cryville.Common",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"GUID:da293eebbcb9a4947a212534c52d1a32"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": false,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
7
Assets/Cryville.Common/Cryville.Common.asmdef.meta
Normal file
7
Assets/Cryville.Common/Cryville.Common.asmdef.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b92f9c7ac10b1c04e86fc48210f62ab1
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/Cryville.Common/Font.meta
Normal file
8
Assets/Cryville.Common/Font.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6e89e0a49233f9e48a97ca7751345a54
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
113
Assets/Cryville.Common/Font/FontFile.cs
Normal file
113
Assets/Cryville.Common/Font/FontFile.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
using Cryville.Common.IO;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace Cryville.Common.Font {
|
||||
public abstract class FontFile : IEnumerable<Typeface> {
|
||||
public abstract int Count { get; }
|
||||
public abstract Typeface this[int index] { get; }
|
||||
protected FileInfo File { get; private set; }
|
||||
protected BinaryReader Reader { get; private set; }
|
||||
public FontFile(FileInfo file) {
|
||||
File = file;
|
||||
Reader = new BinaryReaderBE(new FileStream(file.FullName, FileMode.Open, FileAccess.Read));
|
||||
}
|
||||
public void Close() { Reader.Close(); }
|
||||
|
||||
public static FontFile Create(FileInfo file) {
|
||||
switch (file.Extension) {
|
||||
case ".ttf": case ".otf": return new FontFileTTF(file);
|
||||
case ".ttc": case ".otc": return new FontFileTTC(file);
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Enumerator GetEnumerator() {
|
||||
return new Enumerator(this);
|
||||
}
|
||||
IEnumerator<Typeface> IEnumerable<Typeface>.GetEnumerator() {
|
||||
return GetEnumerator();
|
||||
}
|
||||
IEnumerator IEnumerable.GetEnumerator() {
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public struct Enumerator : IEnumerator<Typeface> {
|
||||
readonly FontFile _self;
|
||||
int _index;
|
||||
internal Enumerator(FontFile self) {
|
||||
_self = self;
|
||||
_index = -1;
|
||||
}
|
||||
|
||||
public Typeface Current {
|
||||
get {
|
||||
if (_index < 0)
|
||||
throw new InvalidOperationException(_index == -1 ? "Enum not started" : "Enum ended");
|
||||
return _self[_index];
|
||||
}
|
||||
}
|
||||
|
||||
object IEnumerator.Current { get { return Current; } }
|
||||
|
||||
public void Dispose() {
|
||||
_index = -2;
|
||||
}
|
||||
|
||||
public bool MoveNext() {
|
||||
if (_index == -2) return false;
|
||||
_index++;
|
||||
if (_index >= _self.Count) {
|
||||
_index = -2;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Reset() {
|
||||
_index = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
public class FontFileTTF : FontFile {
|
||||
public override int Count { get { return 1; } }
|
||||
public override Typeface this[int index] {
|
||||
get {
|
||||
if (index != 0) throw new ArgumentOutOfRangeException("index");
|
||||
try {
|
||||
return new TypefaceTTF(Reader, File, index);
|
||||
}
|
||||
catch (Exception) {
|
||||
throw new InvalidDataException("Invalid font");
|
||||
}
|
||||
}
|
||||
}
|
||||
public FontFileTTF(FileInfo file) : base(file) { }
|
||||
}
|
||||
public class FontFileTTC : FontFile {
|
||||
readonly IReadOnlyList<uint> _offsets;
|
||||
public override int Count { get { return _offsets.Count; } }
|
||||
public override Typeface this[int index] {
|
||||
get {
|
||||
if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException("index");
|
||||
Reader.BaseStream.Position = _offsets[index];
|
||||
try {
|
||||
return new TypefaceTTF(Reader, File, index);
|
||||
}
|
||||
catch (Exception) {
|
||||
throw new InvalidDataException("Invalid font");
|
||||
}
|
||||
}
|
||||
}
|
||||
public FontFileTTC(FileInfo file) : base(file) {
|
||||
try {
|
||||
_offsets = new TTCHeader(Reader, 0).GetItems();
|
||||
}
|
||||
catch (Exception) {
|
||||
throw new InvalidDataException("Invalid font");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Cryville.Common/Font/FontFile.cs.meta
Normal file
11
Assets/Cryville.Common/Font/FontFile.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c1804280aa04fb744a331a1d2dc0066b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
64
Assets/Cryville.Common/Font/FontManager.cs
Normal file
64
Assets/Cryville.Common/Font/FontManager.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Cryville.Common.Font {
|
||||
public abstract class FontManager {
|
||||
public IReadOnlyDictionary<string, Typeface> MapFullNameToTypeface { get; private set; }
|
||||
public IReadOnlyDictionary<string, IReadOnlyCollection<Typeface>> MapNameToTypefaces { get; private set; }
|
||||
public FontManager() {
|
||||
var map1 = new Dictionary<string, Typeface>();
|
||||
var map2 = new Dictionary<string, List<Typeface>>();
|
||||
foreach (var f in EnumerateAllTypefaces()) {
|
||||
if (!map1.ContainsKey(f.FullName)) {
|
||||
map1.Add(f.FullName, f);
|
||||
}
|
||||
else {
|
||||
continue;
|
||||
}
|
||||
List<Typeface> set2;
|
||||
if (!map2.TryGetValue(f.FamilyName, out set2)) {
|
||||
map2.Add(f.FamilyName, set2 = new List<Typeface>());
|
||||
}
|
||||
set2.Add(f);
|
||||
}
|
||||
MapFullNameToTypeface = map1;
|
||||
MapNameToTypefaces = map2.ToDictionary(i => i.Key, i => (IReadOnlyCollection<Typeface>)i.Value);
|
||||
}
|
||||
protected abstract IEnumerable<Typeface> EnumerateAllTypefaces();
|
||||
protected static IEnumerable<Typeface> ScanDirectoryForTypefaces(string dir) {
|
||||
foreach (var f in new DirectoryInfo(dir).EnumerateFiles()) {
|
||||
FontFile file;
|
||||
try {
|
||||
file = FontFile.Create(f);
|
||||
}
|
||||
catch (InvalidDataException) {
|
||||
continue;
|
||||
}
|
||||
if (file == null) continue;
|
||||
var enumerator = file.GetEnumerator();
|
||||
while (enumerator.MoveNext()) {
|
||||
Typeface ret;
|
||||
try {
|
||||
ret = enumerator.Current;
|
||||
}
|
||||
catch (InvalidDataException) {
|
||||
continue;
|
||||
}
|
||||
yield return ret;
|
||||
}
|
||||
file.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
public class FontManagerAndroid : FontManager {
|
||||
protected override IEnumerable<Typeface> EnumerateAllTypefaces() {
|
||||
return ScanDirectoryForTypefaces("/system/fonts");
|
||||
}
|
||||
}
|
||||
public class FontManagerWindows : FontManager {
|
||||
protected override IEnumerable<Typeface> EnumerateAllTypefaces() {
|
||||
return ScanDirectoryForTypefaces("C:/Windows/Fonts");
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Cryville.Common/Font/FontManager.cs.meta
Normal file
11
Assets/Cryville.Common/Font/FontManager.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9fd2cf4e5a96f5146a8b950c3647e4c9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
343
Assets/Cryville.Common/Font/FontMatcher.cs
Normal file
343
Assets/Cryville.Common/Font/FontMatcher.cs
Normal file
@@ -0,0 +1,343 @@
|
||||
using Cryville.Culture;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Cryville.Common.Font {
|
||||
public abstract class FontMatcher {
|
||||
protected FontManager Manager { get; private set; }
|
||||
public FontMatcher(FontManager manager) { Manager = manager; }
|
||||
public abstract IEnumerable<Typeface> MatchLanguage(LanguageId lang, bool distinctFamily = false);
|
||||
}
|
||||
public class FallbackListFontMatcher : FontMatcher {
|
||||
readonly LanguageMatching _matcher;
|
||||
static readonly string UltimateFallbackScript = "zzzz";
|
||||
public Dictionary<string, List<string>> MapScriptToTypefaces = new();
|
||||
public static Dictionary<string, List<string>> GetDefaultWindowsFallbackMap() {
|
||||
var map = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
|
||||
FillKeysWithScripts(map, () => new List<string>());
|
||||
// Reference: https://github.com/chromium/chromium/blob/main/third_party/blink/renderer/platform/fonts/win/font_fallback_win.cc
|
||||
map[UltimateFallbackScript].Insert(0, "SimSun"); // Custom
|
||||
map[UltimateFallbackScript].Insert(0, "SimHei"); // Custom
|
||||
map[UltimateFallbackScript].Insert(0, "Microsoft YaHei"); // Custom
|
||||
map[UltimateFallbackScript].Insert(0, "Arial");
|
||||
map[UltimateFallbackScript].Insert(0, "Times New Roman");
|
||||
map[UltimateFallbackScript].Insert(0, "Segoe UI"); // Custom
|
||||
map["arab"].Insert(0, "Tahoma");
|
||||
map["cyrl"].Insert(0, "Times New Roman");
|
||||
map["grek"].Insert(0, "Times New Roman");
|
||||
map["hebr"].Insert(0, "David");
|
||||
map["jpan"].Insert(0, "MS PGothic");
|
||||
map["latn"].Insert(0, "Times New Roman");
|
||||
map["hans"].Insert(0, "SimSun");
|
||||
map["hans"].Insert(0, "SimHei"); // Custom
|
||||
map["thai"].Insert(0, "Tahoma");
|
||||
map["hans"].Insert(0, "PMingLiU");
|
||||
// Reference: https://learn.microsoft.com/en-us/globalization/input/font-support
|
||||
var ver = Environment.OSVersion.Version;
|
||||
if (ver >= new Version(5, 0)) { // Windows 2000
|
||||
map["armn"].Insert(0, "Sylfaen");
|
||||
map["deva"].Insert(0, "Mangal");
|
||||
map["geor"].Insert(0, "Sylfaen");
|
||||
map["taml"].Insert(0, "Latha");
|
||||
}
|
||||
if (ver >= new Version(5, 1)) { // Windows XP
|
||||
map["gujr"].Insert(0, "Shruti");
|
||||
map["guru"].Insert(0, "Raavi");
|
||||
map["knda"].Insert(0, "Tunga");
|
||||
map["syrc"].Insert(0, "Estrangelo Edessa");
|
||||
map["telu"].Insert(0, "Gautami");
|
||||
map["thaa"].Insert(0, "MV Boli");
|
||||
// SP2
|
||||
map["beng"].Insert(0, "Vrinda");
|
||||
map["mlym"].Insert(0, "Kartika");
|
||||
}
|
||||
if (ver >= new Version(6, 0)) { // Windows Vista
|
||||
map["cans"].Insert(0, "Euphemia");
|
||||
map["cher"].Insert(0, "Plantagenet");
|
||||
map["ethi"].Insert(0, "Nyala");
|
||||
map["khmr"].Insert(0, "DaunPenh MoolBoran");
|
||||
map["kore"].Insert(0, "Malgun Gothic"); // Reference: https://en.wikipedia.org/wiki/List_of_typefaces_included_with_Microsoft_Windows
|
||||
map["laoo"].Insert(0, "DokChampa");
|
||||
map["mong"].Insert(0, "Mongolian Baiti");
|
||||
map["orya"].Insert(0, "Kalinga");
|
||||
map["sinh"].Insert(0, "Iskoola Pota");
|
||||
map["tibt"].Insert(0, "Microsoft Himalaya");
|
||||
map["yiii"].Insert(0, "Microsoft Yi Baiti");
|
||||
map["arab"].Insert(0, "Segoe UI");
|
||||
map["cyrl"].Insert(0, "Segoe UI");
|
||||
map["grek"].Insert(0, "Segoe UI");
|
||||
map["latn"].Insert(0, "Segoe UI");
|
||||
map["hans"].Add("SimSun-ExtB");
|
||||
map["hant"].Add("MingLiU-ExtB");
|
||||
map["hant"].Add("MingLiU_HKSCS-ExtB");
|
||||
map["arab"].Add("Microsoft Uighur");
|
||||
map["zmth"].Insert(0, "Cambria Math");
|
||||
// Reference: https://en.wikipedia.org/wiki/List_of_CJK_fonts
|
||||
map["jpan"].Insert(0, "Meiryo");
|
||||
map["hans"].Insert(0, "Microsoft YaHei");
|
||||
}
|
||||
if (ver >= new Version(6, 1)) { // Windows 7
|
||||
map["brai"].Insert(0, "Segoe UI Symbol");
|
||||
map["dsrt"].Insert(0, "Segoe UI Symbol");
|
||||
map["talu"].Insert(0, "Microsoft New Tai Lue");
|
||||
map["ogam"].Insert(0, "Segoe UI Symbol");
|
||||
map["osma"].Insert(0, "Ebrima");
|
||||
map["phag"].Insert(0, "Microsoft PhagsPa");
|
||||
map["runr"].Insert(0, "Segoe UI Symbol");
|
||||
map["zsym"].Insert(0, "Segoe UI Symbol");
|
||||
map["tale"].Insert(0, "Microsoft Tai Le");
|
||||
map["tfng"].Insert(0, "Ebrima");
|
||||
map["vaii"].Insert(0, "Ebrima");
|
||||
}
|
||||
if (ver >= new Version(6, 2)) { // Windows 8
|
||||
map["glag"].Insert(0, "Segoe UI Symbol");
|
||||
map["goth"].Insert(0, "Segoe UI Symbol");
|
||||
map["hang"].Add("Malgun Gothic");
|
||||
map["ital"].Insert(0, "Segoe UI Symbol");
|
||||
map["lisu"].Insert(0, "Segoe UI");
|
||||
map["mymr"].Insert(0, "Myanmar Text");
|
||||
map["nkoo"].Insert(0, "Ebrima");
|
||||
map["orkh"].Insert(0, "Segoe UI Symbol");
|
||||
map["ethi"].Insert(0, "Ebrima");
|
||||
map["cans"].Insert(0, "Gadugi");
|
||||
map["hant"].Insert(0, "Microsoft JhengHei UI");
|
||||
map["hans"].Insert(0, "Microsoft YaHei UI");
|
||||
map["beng"].Insert(0, "Nirmala UI");
|
||||
map["deva"].Insert(0, "Nirmala UI");
|
||||
map["gujr"].Insert(0, "Nirmala UI");
|
||||
map["guru"].Insert(0, "Nirmala UI"); // NOT DOCUMENTED, UNVERIFIED
|
||||
map["knda"].Insert(0, "Nirmala UI"); // NOT DOCUMENTED, UNVERIFIED
|
||||
map["mlym"].Insert(0, "Nirmala UI");
|
||||
map["orya"].Insert(0, "Nirmala UI");
|
||||
map["sinh"].Insert(0, "Nirmala UI"); // NOT DOCUMENTED, UNVERIFIED
|
||||
map["taml"].Insert(0, "Nirmala UI"); // NOT DOCUMENTED, UNVERIFIED
|
||||
map["telu"].Insert(0, "Nirmala UI");
|
||||
map["armn"].Insert(0, "Segoe UI");
|
||||
map["geor"].Insert(0, "Segoe UI");
|
||||
map["hebr"].Insert(0, "Segoe UI");
|
||||
}
|
||||
if (ver >= new Version(6, 3)) { // Windows 8.1
|
||||
map["bugi"].Insert(0, "Leelawadee UI");
|
||||
map["copt"].Insert(0, "Segoe UI Symbol");
|
||||
map["java"].Insert(0, "Javanese Text");
|
||||
map["merc"].Insert(0, "Segoe UI Symbol");
|
||||
map["olck"].Insert(0, "Nirmala UI");
|
||||
map["sora"].Insert(0, "Nirmala UI");
|
||||
map["khmr"].Insert(0, "Leelawadee UI");
|
||||
map["laoo"].Insert(0, "Leelawadee UI");
|
||||
map["thai"].Insert(0, "Leelawadee UI");
|
||||
map["zsye"].Insert(0, "Segoe UI Emoji");
|
||||
}
|
||||
if (ver >= new Version(10, 0)) { // Windows 10
|
||||
map["brah"].Insert(0, "Segoe UI Historic");
|
||||
map["cari"].Insert(0, "Segoe UI Historic");
|
||||
map["cprt"].Insert(0, "Segoe UI Historic");
|
||||
map["egyp"].Insert(0, "Segoe UI Historic");
|
||||
map["armi"].Insert(0, "Segoe UI Historic");
|
||||
map["phli"].Insert(0, "Segoe UI Historic");
|
||||
map["prti"].Insert(0, "Segoe UI Historic");
|
||||
map["khar"].Insert(0, "Segoe UI Historic");
|
||||
map["lyci"].Insert(0, "Segoe UI Historic");
|
||||
map["lydi"].Insert(0, "Segoe UI Historic");
|
||||
map["phnx"].Insert(0, "Segoe UI Historic");
|
||||
map["xpeo"].Insert(0, "Segoe UI Historic");
|
||||
map["sarb"].Insert(0, "Segoe UI Historic");
|
||||
map["shaw"].Insert(0, "Segoe UI Historic");
|
||||
map["xsux"].Insert(0, "Segoe UI Historic");
|
||||
map["ugar"].Insert(0, "Segoe UI Historic");
|
||||
// Segoe UI Symbol -> Segoe UI Historic
|
||||
map["glag"].Insert(0, "Segoe UI Historic");
|
||||
map["goth"].Insert(0, "Segoe UI Historic");
|
||||
map["merc"].Insert(0, "Segoe UI Historic");
|
||||
map["ogam"].Insert(0, "Segoe UI Historic");
|
||||
map["ital"].Insert(0, "Segoe UI Historic");
|
||||
map["orkh"].Insert(0, "Segoe UI Historic");
|
||||
map["runr"].Insert(0, "Segoe UI Historic");
|
||||
//
|
||||
map["jpan"].Insert(0, "Yu Gothic UI");
|
||||
map["zsym"].Add("Segoe MDL2 Assets");
|
||||
}
|
||||
return map;
|
||||
}
|
||||
public static Dictionary<string, List<string>> GetDefaultAndroidFallbackMap() {
|
||||
var map = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
|
||||
FillKeysWithScripts(map, () => new List<string>());
|
||||
map[UltimateFallbackScript].Insert(0, "Noto Sans CJK TC"); // TODO Modify default fallback
|
||||
map[UltimateFallbackScript].Insert(0, "Noto Sans CJK JP");
|
||||
map[UltimateFallbackScript].Insert(0, "Noto Sans CJK SC");
|
||||
map[UltimateFallbackScript].Insert(0, "Roboto");
|
||||
map["zsye"].Insert(0, "Noto Color Emoji");
|
||||
map["zsye"].Add("Noto Color Emoji Flags");
|
||||
map["arab"].Insert(0, "Noto Naskh Arabic");
|
||||
map["adlm"].Insert(0, "Noto Sans Adlam");
|
||||
map["ahom"].Insert(0, "Noto Sans Ahom");
|
||||
map["hluw"].Insert(0, "Noto Sans Anatolian Hieroglyphs");
|
||||
map["armn"].Insert(0, "Noto Sans Armenian");
|
||||
map["avst"].Insert(0, "Noto Sans Avestan");
|
||||
map["bali"].Insert(0, "Noto Sans Balinese");
|
||||
map["bamu"].Insert(0, "Noto Sans Bamum");
|
||||
map["bass"].Insert(0, "Noto Sans Bassa Vah");
|
||||
map["batk"].Insert(0, "Noto Sans Batak");
|
||||
map["beng"].Insert(0, "Noto Sans Bengali");
|
||||
map["bhks"].Insert(0, "Noto Sans Bhaiksuki");
|
||||
map["brah"].Insert(0, "Noto Sans Brahmi");
|
||||
map["bugi"].Insert(0, "Noto Sans Buginese");
|
||||
map["buhd"].Insert(0, "Noto Sans Buhid");
|
||||
map["jpan"].Insert(0, "Noto Sans CJK JP");
|
||||
map["kore"].Insert(0, "Noto Sans CJK KR");
|
||||
map["hans"].Insert(0, "Noto Sans CJK SC");
|
||||
map["hant"].Insert(0, "Noto Sans CJK TC");
|
||||
map["hant"].Add("Noto Sans CJK HK");
|
||||
map["cans"].Insert(0, "Noto Sans Canadian Aboriginal");
|
||||
map["cari"].Insert(0, "Noto Sans Carian");
|
||||
map["cakm"].Insert(0, "Noto Sans Chakma");
|
||||
map["cham"].Insert(0, "Noto Sans Cham");
|
||||
map["cher"].Insert(0, "Noto Sans Cherokee");
|
||||
map["copt"].Insert(0, "Noto Sans Coptic");
|
||||
map["xsux"].Insert(0, "Noto Sans Cuneiform");
|
||||
map["cprt"].Insert(0, "Noto Sans Cypriot");
|
||||
map["dsrt"].Insert(0, "Noto Sans Deseret");
|
||||
map["deva"].Insert(0, "Noto Sans Devanagari");
|
||||
map["egyp"].Insert(0, "Noto Sans Egyptian Hieroglyphs");
|
||||
map["elba"].Insert(0, "Noto Sans Elbasan");
|
||||
map["ethi"].Insert(0, "Noto Sans Ethiopic");
|
||||
map["geor"].Insert(0, "Noto Sans Georgian");
|
||||
map["glag"].Insert(0, "Noto Sans Glagolitic");
|
||||
map["goth"].Insert(0, "Noto Sans Gothic");
|
||||
map["gran"].Insert(0, "Noto Sans Grantha");
|
||||
map["gujr"].Insert(0, "Noto Sans Gujarati");
|
||||
map["gong"].Insert(0, "Noto Sans Gunjala Gondi");
|
||||
map["guru"].Insert(0, "Noto Sans Gurmukhi");
|
||||
map["rohg"].Insert(0, "Noto Sans Hanifi Rohingya");
|
||||
map["hano"].Insert(0, "Noto Sans Hanunoo");
|
||||
map["hatr"].Insert(0, "Noto Sans Hatran");
|
||||
map["hebr"].Insert(0, "Noto Sans Hebrew");
|
||||
map["armi"].Insert(0, "Noto Sans Imperial Aramaic");
|
||||
map["phli"].Insert(0, "Noto Sans Inscriptional Pahlavi");
|
||||
map["prti"].Insert(0, "Noto Sans Inscriptional Parthian");
|
||||
map["java"].Insert(0, "Noto Sans Javanese");
|
||||
map["kthi"].Insert(0, "Noto Sans Kaithi");
|
||||
map["knda"].Insert(0, "Noto Sans Kannada");
|
||||
map["kali"].Insert(0, "Noto Sans KayahLi");
|
||||
map["khar"].Insert(0, "Noto Sans Kharoshthi");
|
||||
map["khmr"].Insert(0, "Noto Sans Khmer");
|
||||
map["khoj"].Insert(0, "Noto Sans Khojki");
|
||||
map["laoo"].Insert(0, "Noto Sans Lao");
|
||||
map["lepc"].Insert(0, "Noto Sans Lepcha");
|
||||
map["limb"].Insert(0, "Noto Sans Limbu");
|
||||
map["lina"].Insert(0, "Noto Sans Linear A");
|
||||
map["linb"].Insert(0, "Noto Sans Linear B");
|
||||
map["lisu"].Insert(0, "Noto Sans Lisu");
|
||||
map["lyci"].Insert(0, "Noto Sans Lycian");
|
||||
map["lydi"].Insert(0, "Noto Sans Lydian");
|
||||
map["mlym"].Insert(0, "Noto Sans Malayalam");
|
||||
map["mand"].Insert(0, "Noto Sans Mandiac");
|
||||
map["mani"].Insert(0, "Noto Sans Manichaean");
|
||||
map["marc"].Insert(0, "Noto Sans Marchen");
|
||||
map["gonm"].Insert(0, "Noto Sans Masaram Gondi");
|
||||
map["medf"].Insert(0, "Noto Sans Medefaidrin");
|
||||
map["mtei"].Insert(0, "Noto Sans Meetei Mayek");
|
||||
map["merc"].Insert(0, "Noto Sans Meroitic");
|
||||
map["mero"].Insert(0, "Noto Sans Meroitic");
|
||||
map["plrd"].Insert(0, "Noto Sans Miao");
|
||||
map["modi"].Insert(0, "Noto Sans Modi");
|
||||
map["mong"].Insert(0, "Noto Sans Mongolian");
|
||||
map["mroo"].Insert(0, "Noto Sans Mro");
|
||||
map["mult"].Insert(0, "Noto Sans Multani");
|
||||
map["mymr"].Insert(0, "Noto Sans Myanmar");
|
||||
map["nkoo"].Insert(0, "Noto Sans Nko");
|
||||
map["nbat"].Insert(0, "Noto Sans Nabataean");
|
||||
map["talu"].Insert(0, "Noto Sans New Tai Lue");
|
||||
map["newa"].Insert(0, "Noto Sans Newa");
|
||||
map["ogam"].Insert(0, "Noto Sans Ogham");
|
||||
map["olck"].Insert(0, "Noto Sans Ol Chiki");
|
||||
map["ital"].Insert(0, "Noto Sans Old Italian");
|
||||
map["narb"].Insert(0, "Noto Sans Old North Arabian");
|
||||
map["perm"].Insert(0, "Noto Sans Old Permic");
|
||||
map["xpeo"].Insert(0, "Noto Sans Old Persian");
|
||||
map["sarb"].Insert(0, "Noto Sans Old South Arabian");
|
||||
map["orkh"].Insert(0, "Noto Sans Old Turkic");
|
||||
map["orya"].Insert(0, "Noto Sans Oriya");
|
||||
map["osge"].Insert(0, "Noto Sans Osage");
|
||||
map["osma"].Insert(0, "Noto Sans Osmanya");
|
||||
map["hmng"].Insert(0, "Noto Sans Pahawh Hmong");
|
||||
map["palm"].Insert(0, "Noto Sans Palmyrene");
|
||||
map["pauc"].Insert(0, "Noto Sans Pau Cin Hau");
|
||||
map["phag"].Insert(0, "Noto Sans Phags Pa");
|
||||
map["phnx"].Insert(0, "Noto Sans Phoenician");
|
||||
map["rjng"].Insert(0, "Noto Sans Rejang");
|
||||
map["runr"].Insert(0, "Noto Sans Runic");
|
||||
map["samr"].Insert(0, "Noto Sans Samaritan");
|
||||
map["saur"].Insert(0, "Noto Sans Saurashtra");
|
||||
map["shrd"].Insert(0, "Noto Sans Sharada");
|
||||
map["shaw"].Insert(0, "Noto Sans Shavian");
|
||||
map["sinh"].Insert(0, "Noto Sans Sinhala");
|
||||
map["sora"].Insert(0, "Noto Sans Sora Sompeng");
|
||||
map["soyo"].Insert(0, "Noto Sans Soyombo");
|
||||
map["sund"].Insert(0, "Noto Sans Sundanese");
|
||||
map["sylo"].Insert(0, "Noto Sans Syloti Nagri");
|
||||
map["zsym"].Insert(0, "Noto Sans Symbols");
|
||||
map["syrc"].Add("Noto Sans Syriac Eastern");
|
||||
map["syrc"].Add("Noto Sans Syriac Western");
|
||||
map["syrc"].Add("Noto Sans Syriac Estrangela");
|
||||
map["tglg"].Insert(0, "Noto Sans Tagalog");
|
||||
map["tagb"].Insert(0, "Noto Sans Tagbanwa");
|
||||
map["tale"].Insert(0, "Noto Sans Tai Le");
|
||||
map["lana"].Insert(0, "Noto Sans Tai Tham");
|
||||
map["tavt"].Insert(0, "Noto Sans Tai Viet");
|
||||
map["takr"].Insert(0, "Noto Sans Takri");
|
||||
map["taml"].Insert(0, "Noto Sans Tamil");
|
||||
map["telu"].Insert(0, "Noto Sans Telugu");
|
||||
map["thaa"].Insert(0, "Noto Sans Thaana");
|
||||
map["thai"].Insert(0, "Noto Sans Thai");
|
||||
map["tfng"].Insert(0, "Noto Sans Tifinagh");
|
||||
map["ugar"].Insert(0, "Noto Sans Ugaritic");
|
||||
map["vaii"].Insert(0, "Noto Sans Vai");
|
||||
map["wcho"].Insert(0, "Noto Sans Wancho");
|
||||
map["wara"].Insert(0, "Noto Sans Warang Citi");
|
||||
map["yiii"].Insert(0, "Noto Sans Yi");
|
||||
return map;
|
||||
}
|
||||
static void FillKeysWithScripts<T>(IDictionary<string, T> map, Func<T> value) {
|
||||
foreach (var s in IdValidity.Enumerate("script")) map.Add(s, value());
|
||||
}
|
||||
|
||||
public FallbackListFontMatcher(LanguageMatching matcher, FontManager manager) : base(manager) {
|
||||
_matcher = matcher;
|
||||
}
|
||||
public override IEnumerable<Typeface> MatchLanguage(LanguageId lang, bool distinctFamily = false) {
|
||||
var supported = MapScriptToTypefaces.Keys.Select(i => new LanguageId(i)).ToList();
|
||||
bool flag = false;
|
||||
while (_matcher.Match(lang, supported, out var match, out var distance)) {
|
||||
if (distance > 40) break;
|
||||
if (match.Script.Equals(UltimateFallbackScript, StringComparison.OrdinalIgnoreCase)) {
|
||||
flag = true;
|
||||
}
|
||||
var candidates = MapScriptToTypefaces[match.Script];
|
||||
foreach (var typeface in EnumerateTypefaces(candidates, distinctFamily)) {
|
||||
yield return typeface;
|
||||
}
|
||||
supported.Remove(match);
|
||||
}
|
||||
if (flag) yield break;
|
||||
foreach (var typeface in EnumerateTypefaces(MapScriptToTypefaces[UltimateFallbackScript], distinctFamily)) {
|
||||
yield return typeface;
|
||||
}
|
||||
}
|
||||
IEnumerable<Typeface> EnumerateTypefaces(List<string> candidates, bool distinctFamily) {
|
||||
foreach (var candidate in candidates) {
|
||||
if (Manager.MapFullNameToTypeface.TryGetValue(candidate, out var typeface1)) {
|
||||
yield return typeface1;
|
||||
}
|
||||
if (distinctFamily) continue;
|
||||
if (Manager.MapNameToTypefaces.TryGetValue(candidate, out var typefaces2)) {
|
||||
foreach (var typeface in typefaces2) {
|
||||
if (typeface1 == typeface) continue;
|
||||
yield return typeface;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Cryville.Common/Font/FontMatcher.cs.meta
Normal file
11
Assets/Cryville.Common/Font/FontMatcher.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 157390bd8c4b14243b9109b88480a1c6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
256
Assets/Cryville.Common/Font/FontTable.cs
Normal file
256
Assets/Cryville.Common/Font/FontTable.cs
Normal file
@@ -0,0 +1,256 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
#pragma warning disable IDE0049
|
||||
namespace Cryville.Common.Font {
|
||||
public abstract class FontTable<T> {
|
||||
protected UInt32 Offset { get; private set; }
|
||||
protected BinaryReader Reader { get; private set; }
|
||||
|
||||
protected FontTable(BinaryReader reader, UInt32 offset) {
|
||||
Reader = reader;
|
||||
Offset = offset;
|
||||
reader.BaseStream.Position = offset;
|
||||
}
|
||||
public abstract IReadOnlyList<T> GetItems();
|
||||
}
|
||||
public abstract class FontTable<T, U> : FontTable<T> {
|
||||
protected FontTable(BinaryReader reader, UInt32 offset) : base(reader, offset) { }
|
||||
public abstract U GetSubTable(T item);
|
||||
}
|
||||
public sealed class TTCHeader : FontTable<UInt32, TableDirectory> {
|
||||
readonly String ttcTag;
|
||||
readonly UInt16 majorVersion;
|
||||
readonly UInt16 minorVersion;
|
||||
readonly UInt32 numFonts;
|
||||
readonly List<UInt32> tableDirectoryOffsets = new List<UInt32>();
|
||||
#pragma warning disable IDE0052 // Reserved
|
||||
readonly String dsigTag;
|
||||
readonly UInt32 dsigLength;
|
||||
readonly UInt32 dsigOffset;
|
||||
#pragma warning restore IDE0052 // Reserved
|
||||
public TTCHeader(BinaryReader reader, UInt32 offset) : base(reader, offset) {
|
||||
ttcTag = reader.ReadTag();
|
||||
if (ttcTag != "ttcf") throw new NotSupportedException();
|
||||
majorVersion = reader.ReadUInt16();
|
||||
minorVersion = reader.ReadUInt16();
|
||||
if (minorVersion != 0) throw new NotSupportedException();
|
||||
numFonts = reader.ReadUInt32();
|
||||
for (UInt32 i = 0; i < numFonts; i++) tableDirectoryOffsets.Add(reader.ReadUInt32());
|
||||
if (majorVersion == 2) {
|
||||
dsigTag = reader.ReadTag();
|
||||
dsigLength = reader.ReadUInt32();
|
||||
dsigOffset = reader.ReadUInt32();
|
||||
}
|
||||
}
|
||||
public override IReadOnlyList<UInt32> GetItems() {
|
||||
return tableDirectoryOffsets;
|
||||
}
|
||||
public override TableDirectory GetSubTable(UInt32 item) {
|
||||
var i = item;
|
||||
return new TableDirectory(Reader, i);
|
||||
}
|
||||
}
|
||||
public sealed class TableDirectory : FontTable<TableRecord, object> {
|
||||
readonly UInt32 sfntVersion;
|
||||
readonly UInt16 numTables;
|
||||
#pragma warning disable IDE0052 // Reserved
|
||||
readonly UInt16 searchRange;
|
||||
readonly UInt16 entrySelector;
|
||||
readonly UInt16 rangeShift;
|
||||
#pragma warning restore IDE0052 // Reserved
|
||||
readonly List<TableRecord> tableRecords = new List<TableRecord>();
|
||||
public TableDirectory(BinaryReader reader, UInt32 offset) : base(reader, offset) {
|
||||
sfntVersion = reader.ReadUInt32();
|
||||
if (sfntVersion != 0x00010000 && sfntVersion != 0x4F54544F &&
|
||||
sfntVersion != 0x74727565 && sfntVersion != 0x74797031) throw new NotSupportedException();
|
||||
numTables = reader.ReadUInt16();
|
||||
searchRange = reader.ReadUInt16();
|
||||
entrySelector = reader.ReadUInt16();
|
||||
rangeShift = reader.ReadUInt16();
|
||||
for (int i = 0; i < numTables; i++)
|
||||
tableRecords.Add(new TableRecord {
|
||||
tableTag = reader.ReadTag(),
|
||||
checksum = reader.ReadUInt32(),
|
||||
offset = reader.ReadUInt32(),
|
||||
length = reader.ReadUInt32(),
|
||||
});
|
||||
}
|
||||
public override IReadOnlyList<TableRecord> GetItems() {
|
||||
return tableRecords;
|
||||
}
|
||||
public override object GetSubTable(TableRecord item) {
|
||||
switch (item.tableTag) {
|
||||
case "name": return new NameTable(Reader, item.offset);
|
||||
case "meta": return new MetaTable(Reader, item.offset);
|
||||
default: throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
public struct TableRecord {
|
||||
public string tableTag;
|
||||
public UInt32 checksum;
|
||||
public UInt32 offset;
|
||||
public UInt32 length;
|
||||
}
|
||||
public sealed class NameTable : FontTable<NameRecord> {
|
||||
readonly UInt16 version;
|
||||
readonly UInt16 count;
|
||||
readonly UInt16 storageOffset;
|
||||
readonly List<NameRecord> nameRecord = new List<NameRecord>();
|
||||
readonly UInt16 langTagCount;
|
||||
readonly List<LangTagRecord> langTagRecord = new List<LangTagRecord>();
|
||||
public NameTable(BinaryReader reader, UInt32 offset) : base(reader, offset) {
|
||||
version = reader.ReadUInt16();
|
||||
count = reader.ReadUInt16();
|
||||
storageOffset = reader.ReadUInt16();
|
||||
for (UInt16 i = 0; i < count; i++)
|
||||
nameRecord.Add(new NameRecord(
|
||||
reader.ReadUInt16(),
|
||||
reader.ReadUInt16(),
|
||||
reader.ReadUInt16(),
|
||||
(NameID)reader.ReadUInt16(),
|
||||
reader.ReadUInt16(),
|
||||
reader.ReadUInt16()
|
||||
));
|
||||
if (version == 1) {
|
||||
langTagCount = reader.ReadUInt16();
|
||||
for (UInt16 i = 0; i < langTagCount; i++)
|
||||
langTagRecord.Add(new LangTagRecord(
|
||||
reader.ReadUInt16(),
|
||||
reader.ReadUInt16()
|
||||
));
|
||||
}
|
||||
foreach (var i in nameRecord)
|
||||
i.Load(reader, offset + storageOffset);
|
||||
if (version == 1) {
|
||||
foreach (var i in langTagRecord)
|
||||
i.Load(reader, offset + storageOffset);
|
||||
}
|
||||
}
|
||||
public sealed override IReadOnlyList<NameRecord> GetItems() {
|
||||
return nameRecord;
|
||||
}
|
||||
}
|
||||
public class NameRecord {
|
||||
public UInt16 PlatformID { get; private set; }
|
||||
public UInt16 EncodingID { get; private set; }
|
||||
public UInt16 LanguageID { get; private set; }
|
||||
public NameID NameID { get; private set; }
|
||||
public UInt16 Length { get; private set; }
|
||||
public UInt16 StringOffset { get; private set; }
|
||||
public String Value { get; private set; }
|
||||
public NameRecord(UInt16 platformID, UInt16 encodingID, UInt16 languageID, NameID nameID, UInt16 length, UInt16 stringOffset) {
|
||||
PlatformID = platformID;
|
||||
EncodingID = encodingID;
|
||||
LanguageID = languageID;
|
||||
NameID = nameID;
|
||||
Length = length;
|
||||
StringOffset = stringOffset;
|
||||
}
|
||||
public void Load(BinaryReader reader, UInt32 origin) {
|
||||
reader.BaseStream.Position = origin + StringOffset;
|
||||
Encoding encoding;
|
||||
try {
|
||||
switch (PlatformID) {
|
||||
case 0: encoding = Encoding.BigEndianUnicode; break;
|
||||
case 1: encoding = Encoding.GetEncoding(10000 + EncodingID); break;
|
||||
case 3: encoding = Encoding.BigEndianUnicode; break;
|
||||
default: return;
|
||||
}
|
||||
}
|
||||
catch (NotSupportedException) { return; }
|
||||
catch (ArgumentException) { return; }
|
||||
Value = encoding.GetString(reader.ReadBytes(Length));
|
||||
}
|
||||
}
|
||||
public enum NameID : UInt16 {
|
||||
CopyrightNotice = 0,
|
||||
FontFamilyName = 1,
|
||||
FontSubfamilyName = 2,
|
||||
UniqueFontIdentifier = 3,
|
||||
FullFontName = 4,
|
||||
VersionString = 5,
|
||||
PostScriptName = 6,
|
||||
Trademark = 7,
|
||||
ManufacturerName = 8,
|
||||
Designer = 9,
|
||||
Description = 10,
|
||||
URLVendor = 11,
|
||||
URLDesigner = 12,
|
||||
LicenseDescription = 13,
|
||||
LicenseInfoURL = 14,
|
||||
|
||||
TypographicFamilyName = 16,
|
||||
TypographicSubfamilyName = 17,
|
||||
CompatibleFull = 18,
|
||||
SampleText = 19,
|
||||
PostScriptCIDFindfontName = 20,
|
||||
WWSFamilyName = 21,
|
||||
WWSSubfamilyName = 22,
|
||||
LightBackgroundPalette = 23,
|
||||
DarkBackgroundPalette = 24,
|
||||
VariationsPostScriptNamePrefix = 25,
|
||||
}
|
||||
public class LangTagRecord {
|
||||
public UInt16 Length { get; private set; }
|
||||
public UInt16 LangTagOffset { get; private set; }
|
||||
public String Value { get; private set; }
|
||||
public LangTagRecord(UInt16 length, UInt16 langTagOffset) {
|
||||
Length = length;
|
||||
LangTagOffset = langTagOffset;
|
||||
}
|
||||
public void Load(BinaryReader reader, UInt32 origin) {
|
||||
reader.BaseStream.Position = origin + LangTagOffset;
|
||||
Value = Encoding.BigEndianUnicode.GetString(reader.ReadBytes(Length));
|
||||
}
|
||||
}
|
||||
public sealed class MetaTable : FontTable<DataMap> {
|
||||
readonly UInt32 version;
|
||||
#pragma warning disable IDE0052 // Reserved
|
||||
readonly UInt32 flags;
|
||||
#pragma warning restore IDE0052 // Reserved
|
||||
readonly UInt32 dataMapCount;
|
||||
readonly List<DataMap> dataMaps = new List<DataMap>();
|
||||
public MetaTable(BinaryReader reader, UInt32 offset) : base(reader, offset) {
|
||||
version = reader.ReadUInt32();
|
||||
if (version != 1) throw new NotSupportedException();
|
||||
flags = reader.ReadUInt32();
|
||||
reader.ReadUInt32();
|
||||
dataMapCount = reader.ReadUInt32();
|
||||
for (UInt32 i = 0; i < dataMapCount; i++)
|
||||
dataMaps.Add(new DataMap (
|
||||
reader.ReadTag(),
|
||||
reader.ReadUInt32(),
|
||||
reader.ReadUInt32()
|
||||
));
|
||||
foreach (var i in dataMaps)
|
||||
i.Load(reader, offset);
|
||||
}
|
||||
public sealed override IReadOnlyList<DataMap> GetItems() {
|
||||
return dataMaps;
|
||||
}
|
||||
}
|
||||
public class DataMap {
|
||||
public String Tag { get; private set; }
|
||||
public UInt32 DataOffset { get; private set; }
|
||||
public UInt32 DataLength { get; private set; }
|
||||
public String Value { get; private set; }
|
||||
public DataMap(String tag, UInt32 dataOffset, UInt32 dataLength) {
|
||||
Tag = tag;
|
||||
DataOffset = dataOffset;
|
||||
DataLength = dataLength;
|
||||
}
|
||||
public void Load(BinaryReader reader, UInt32 origin) {
|
||||
reader.BaseStream.Position = origin + DataOffset;
|
||||
Value = Encoding.ASCII.GetString(reader.ReadBytes((int)DataLength));
|
||||
}
|
||||
}
|
||||
public static class BinaryReaderExtensions {
|
||||
public static string ReadTag(this BinaryReader reader) {
|
||||
return Encoding.ASCII.GetString(reader.ReadBytes(4));
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Cryville.Common/Font/FontTable.cs.meta
Normal file
11
Assets/Cryville.Common/Font/FontTable.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3805282a4858eec4d8ff91826ed7e896
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
31
Assets/Cryville.Common/Font/Typeface.cs
Normal file
31
Assets/Cryville.Common/Font/Typeface.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Cryville.Common.Font {
|
||||
public abstract class Typeface {
|
||||
public FileInfo File { get; private set; }
|
||||
public int IndexInFile { get; private set; }
|
||||
public string FamilyName { get; protected set; }
|
||||
public string SubfamilyName { get; protected set; }
|
||||
public string FullName { get; protected set; }
|
||||
protected abstract void GetName(BinaryReader reader);
|
||||
|
||||
public Typeface(BinaryReader reader, FileInfo file, int index) {
|
||||
File = file;
|
||||
IndexInFile = index;
|
||||
GetName(reader);
|
||||
}
|
||||
}
|
||||
public class TypefaceTTF : Typeface {
|
||||
public TypefaceTTF(BinaryReader reader, FileInfo file, int index)
|
||||
: base(reader, file, index) { }
|
||||
|
||||
protected override void GetName(BinaryReader reader) {
|
||||
var dir = new TableDirectory(reader, (uint)reader.BaseStream.Position);
|
||||
var nameTable = (NameTable)dir.GetSubTable((from i in dir.GetItems() where i.tableTag == "name" select i).Single());
|
||||
FamilyName = (from i in nameTable.GetItems() where i.NameID == NameID.FontFamilyName && i.Value != null select i.Value).First();
|
||||
SubfamilyName = (from i in nameTable.GetItems() where i.NameID == NameID.FontSubfamilyName && i.Value != null select i.Value).First();
|
||||
FullName = (from i in nameTable.GetItems() where i.NameID == NameID.FullFontName && i.Value != null select i.Value).First();
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Cryville.Common/Font/Typeface.cs.meta
Normal file
11
Assets/Cryville.Common/Font/Typeface.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 943e0cf54256f2a479996d877b2b0e84
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/Cryville.Common/IO.meta
Normal file
8
Assets/Cryville.Common/IO.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dbb2a81779b10f34489a8c83287d48bc
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
75
Assets/Cryville.Common/IO/BinaryReaderBE.cs
Normal file
75
Assets/Cryville.Common/IO/BinaryReaderBE.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace Cryville.Common.IO {
|
||||
public class BinaryReaderBE : BinaryReader {
|
||||
readonly byte[] m_buffer = new byte[8];
|
||||
|
||||
public BinaryReaderBE(Stream input) : base(input) { }
|
||||
|
||||
public BinaryReaderBE(Stream input, Encoding encoding) : base(input, encoding) { }
|
||||
|
||||
public BinaryReaderBE(Stream input, Encoding encoding, bool leaveOpen) : base(input, encoding, leaveOpen) { }
|
||||
|
||||
public override short ReadInt16() {
|
||||
FillBuffer(2);
|
||||
return (short)(m_buffer[1] | (m_buffer[0] << 8));
|
||||
}
|
||||
public override ushort ReadUInt16() {
|
||||
FillBuffer(2);
|
||||
return (ushort)(m_buffer[1] | (m_buffer[0] << 8));
|
||||
}
|
||||
|
||||
public override int ReadInt32() {
|
||||
FillBuffer(4);
|
||||
return m_buffer[3] | (m_buffer[2] << 8) | (m_buffer[1] << 16) | (m_buffer[0] << 24);
|
||||
}
|
||||
public override uint ReadUInt32() {
|
||||
FillBuffer(4);
|
||||
return (uint)(m_buffer[3] | (m_buffer[2] << 8) | (m_buffer[1] << 16) | (m_buffer[0] << 24));
|
||||
}
|
||||
|
||||
public override long ReadInt64() {
|
||||
FillBuffer(8);
|
||||
uint num = (uint)(m_buffer[7] | (m_buffer[6] << 8) | (m_buffer[5] << 16) | (m_buffer[4] << 24));
|
||||
uint num2 = (uint)(m_buffer[3] | (m_buffer[2] << 8) | (m_buffer[1] << 16) | (m_buffer[0] << 24));
|
||||
return (long)(((ulong)num2 << 32) | num);
|
||||
}
|
||||
public override ulong ReadUInt64() {
|
||||
FillBuffer(8);
|
||||
uint num = (uint)(m_buffer[7] | (m_buffer[6] << 8) | (m_buffer[5] << 16) | (m_buffer[4] << 24));
|
||||
uint num2 = (uint)(m_buffer[3] | (m_buffer[2] << 8) | (m_buffer[1] << 16) | (m_buffer[0] << 24));
|
||||
return ((ulong)num2 << 32) | num;
|
||||
}
|
||||
protected new void FillBuffer(int numBytes) {
|
||||
if (m_buffer != null && (numBytes < 0 || numBytes > m_buffer.Length)) {
|
||||
throw new ArgumentOutOfRangeException("numBytes", "Requested numBytes is larger than the internal buffer size");
|
||||
}
|
||||
|
||||
int num = 0, num2;
|
||||
if (BaseStream == null) {
|
||||
throw new IOException("File not open");
|
||||
}
|
||||
|
||||
if (numBytes == 1) {
|
||||
num2 = BaseStream.ReadByte();
|
||||
if (num2 == -1) {
|
||||
throw new EndOfStreamException("The end of the stream is reached before numBytes could be read");
|
||||
}
|
||||
m_buffer[0] = (byte)num2;
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
num2 = BaseStream.Read(m_buffer, num, numBytes - num);
|
||||
if (num2 == 0) {
|
||||
throw new EndOfStreamException("The end of the stream is reached before numBytes could be read");
|
||||
}
|
||||
|
||||
num += num2;
|
||||
}
|
||||
while (num < numBytes);
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Cryville.Common/IO/BinaryReaderBE.cs.meta
Normal file
11
Assets/Cryville.Common/IO/BinaryReaderBE.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8eaf9874d914c1349aabbb884c54b2d7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/Cryville.Common/Unity.meta
Normal file
8
Assets/Cryville.Common/Unity.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bf3461b95c913eb44b2f2e653bf5eb79
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
8
Assets/Cryville.Common/Unity/UI.meta
Normal file
8
Assets/Cryville.Common/Unity/UI.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ad96c636bfa80024f8427b7755b56639
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
113
Assets/Cryville.Common/Unity/UI/AspectRatioLayoutElement.cs
Normal file
113
Assets/Cryville.Common/Unity/UI/AspectRatioLayoutElement.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Cryville.Common.Unity.UI {
|
||||
/// <summary>
|
||||
/// A <see cref="ILayoutElement" /> that takes the length of one axis to compute the preferred length of the other axis with respect to a aspect ratio.
|
||||
/// </summary>
|
||||
[AddComponentMenu("Layout/Aspect Ratio Layout Element")]
|
||||
[ExecuteAlways]
|
||||
public class AspectRatioLayoutElement : UIBehaviour, ILayoutElement {
|
||||
[SerializeField]
|
||||
[Tooltip("The aspect ratio. Width divided by height.")]
|
||||
private float m_aspectRatio = 1;
|
||||
/// <summary>
|
||||
/// The aspect ratio. Width divided by height.
|
||||
/// </summary>
|
||||
public float AspectRatio {
|
||||
get { return m_aspectRatio; }
|
||||
set { SetProperty(ref m_aspectRatio, value); }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("Whether to compute the length of the y axis.")]
|
||||
private bool m_isVertical = false;
|
||||
/// <summary>
|
||||
/// Whether to compute the length of the y axis.
|
||||
/// </summary>
|
||||
public bool IsVertical {
|
||||
get { return m_isVertical; }
|
||||
set { SetProperty(ref m_isVertical, value); }
|
||||
}
|
||||
|
||||
private void SetProperty<T>(ref T prop, T value) {
|
||||
if (Equals(prop, value)) return;
|
||||
prop = value;
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public float minWidth {
|
||||
get {
|
||||
return m_isVertical ? -1 : (transform as RectTransform).rect.height * m_aspectRatio;
|
||||
}
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public float preferredWidth { get { return minWidth; } }
|
||||
/// <inheritdoc />
|
||||
public float flexibleWidth { get { return -1; } }
|
||||
|
||||
/// <inheritdoc />
|
||||
public float minHeight {
|
||||
get {
|
||||
return m_isVertical ? (transform as RectTransform).rect.width / m_aspectRatio : -1;
|
||||
}
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public float preferredHeight { get { return minHeight; } }
|
||||
/// <inheritdoc />
|
||||
public float flexibleHeight { get { return -1; } }
|
||||
|
||||
/// <inheritdoc />
|
||||
public int layoutPriority { get { return 1; } }
|
||||
|
||||
/// <inheritdoc />
|
||||
public void CalculateLayoutInputHorizontal() { }
|
||||
|
||||
/// <inheritdoc />
|
||||
public void CalculateLayoutInputVertical() { }
|
||||
|
||||
protected override void OnEnable() {
|
||||
base.OnEnable();
|
||||
SetDirty();
|
||||
}
|
||||
protected override void OnBeforeTransformParentChanged() {
|
||||
base.OnBeforeTransformParentChanged();
|
||||
SetDirty();
|
||||
}
|
||||
protected override void OnTransformParentChanged() {
|
||||
base.OnTransformParentChanged();
|
||||
SetDirty();
|
||||
}
|
||||
protected override void OnDidApplyAnimationProperties() {
|
||||
base.OnDidApplyAnimationProperties();
|
||||
SetDirty();
|
||||
}
|
||||
#if UNITY_EDITOR
|
||||
protected override void OnValidate() {
|
||||
base.OnValidate();
|
||||
SetDirty();
|
||||
}
|
||||
#endif
|
||||
protected override void OnDisable() {
|
||||
SetDirty();
|
||||
base.OnDisable();
|
||||
}
|
||||
protected override void OnRectTransformDimensionsChange() {
|
||||
base.OnRectTransformDimensionsChange();
|
||||
SetDirty();
|
||||
}
|
||||
bool _delayedSetDirty;
|
||||
private void SetDirty() {
|
||||
if (!IsActive()) return;
|
||||
if (CanvasUpdateRegistry.IsRebuildingLayout()) _delayedSetDirty = true;
|
||||
else LayoutRebuilder.MarkLayoutForRebuild(transform as RectTransform);
|
||||
}
|
||||
void Update() {
|
||||
if (!_delayedSetDirty) return;
|
||||
_delayedSetDirty = false;
|
||||
LayoutRebuilder.MarkLayoutForRebuild(transform as RectTransform);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 96276a3ffc8f32c4fbb7035a6fb28e74
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,24 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cryville.Common.Unity.UI {
|
||||
/// <summary>
|
||||
/// A <see cref="DockLayoutGroup" /> that sets the aspect ratio of the docking element.
|
||||
/// </summary>
|
||||
[AddComponentMenu("Layout/Dock Aspect Ratio Layout Group")]
|
||||
public sealed class DockAspectRatioLayoutGroup : DockLayoutGroup {
|
||||
[SerializeField]
|
||||
[Tooltip("The aspect ratio of the docking element.")]
|
||||
private float m_dockAspectRatio = 1;
|
||||
/// <summary>
|
||||
/// The aspect ratio of the docking element.
|
||||
/// </summary>
|
||||
public float DockAspectRatio {
|
||||
get { return m_dockAspectRatio; }
|
||||
set { base.SetProperty(ref m_dockAspectRatio, value); }
|
||||
}
|
||||
|
||||
protected override float GetDockElementSize(Vector2 groupSize) {
|
||||
return groupSize.y * m_dockAspectRatio;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9023f24d2fe04fe45bf66a9d029591c1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
136
Assets/Cryville.Common/Unity/UI/DockLayoutGroup.cs
Normal file
136
Assets/Cryville.Common/Unity/UI/DockLayoutGroup.cs
Normal file
@@ -0,0 +1,136 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Cryville.Common.Unity.UI {
|
||||
/// <summary>
|
||||
/// A <see cref="LayoutGroup" /> that docks its first child element to one side.
|
||||
/// </summary>
|
||||
public abstract class DockLayoutGroup : LayoutGroup {
|
||||
/// <summary>
|
||||
/// The dock side.
|
||||
/// </summary>
|
||||
public enum Side {
|
||||
/// <summary>
|
||||
/// Top.
|
||||
/// </summary>
|
||||
Top = 0,
|
||||
/// <summary>
|
||||
/// Right.
|
||||
/// </summary>
|
||||
Right = 1,
|
||||
/// <summary>
|
||||
/// Bottom.
|
||||
/// </summary>
|
||||
Bottom = 2,
|
||||
/// <summary>
|
||||
/// Left.
|
||||
/// </summary>
|
||||
Left = 3,
|
||||
}
|
||||
[SerializeField]
|
||||
[Tooltip("The docking side of the first child element.")]
|
||||
private Side m_side;
|
||||
/// <summary>
|
||||
/// The docking side of the first child element.
|
||||
/// </summary>
|
||||
public Side DockSide {
|
||||
get { return m_side; }
|
||||
set { SetProperty(ref m_side, value); }
|
||||
}
|
||||
|
||||
[SerializeField]
|
||||
[Tooltip("The slide index. The children slide along the cross axis.")]
|
||||
private float m_slideIndex;
|
||||
/// <summary>
|
||||
/// The slide index. The children slide along the axis.
|
||||
/// </summary>
|
||||
public float SlideIndex {
|
||||
get { return m_slideIndex; }
|
||||
set { SetProperty(ref m_slideIndex, value); }
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public sealed override void CalculateLayoutInputHorizontal() { base.CalculateLayoutInputHorizontal(); CalcAlongAxis(0); }
|
||||
/// <inheritdoc />
|
||||
public sealed override void CalculateLayoutInputVertical() { CalcAlongAxis(1); }
|
||||
/// <inheritdoc />
|
||||
public sealed override void SetLayoutHorizontal() { SetChildrenAlongAxis(0); }
|
||||
/// <inheritdoc />
|
||||
public sealed override void SetLayoutVertical() { SetChildrenAlongAxis(1); }
|
||||
|
||||
private void CalcAlongAxis(int axis) {
|
||||
int isHorizontal = (int)m_side & 1;
|
||||
if ((isHorizontal ^ axis) == 1) {
|
||||
SetLayoutInputForAxis(0, 0, 1, axis); // TODO
|
||||
}
|
||||
else {
|
||||
float padding = isHorizontal == 0 ? m_Padding.horizontal : m_Padding.vertical;
|
||||
float totalMin = 0, totalPreferred = 0, totalFlexible = 0;
|
||||
for (int i = 0; i < rectChildren.Count; i++) {
|
||||
GetChildSizes(rectChildren[i], axis, out float min, out float preferred, out float flexible);
|
||||
if (min > totalMin) totalMin = min;
|
||||
if (preferred > totalPreferred) totalPreferred = preferred;
|
||||
if (flexible > totalFlexible) totalFlexible = flexible;
|
||||
}
|
||||
SetLayoutInputForAxis(totalMin + padding, totalPreferred + padding, totalFlexible, axis);
|
||||
}
|
||||
}
|
||||
private void GetChildSizes(RectTransform child, int axis, out float min, out float preferred, out float flexible) {
|
||||
min = LayoutUtility.GetMinSize(child, axis);
|
||||
preferred = LayoutUtility.GetPreferredSize(child, axis);
|
||||
flexible = LayoutUtility.GetFlexibleSize(child, axis);
|
||||
}
|
||||
|
||||
private float GetSlidePosition(float groupHeight, float dockHeight) {
|
||||
bool d = Mathf.FloorToInt(m_slideIndex - Mathf.Floor(m_slideIndex / 2) * 2) == 0;
|
||||
int l = Mathf.FloorToInt(m_slideIndex / 2);
|
||||
float p = m_slideIndex - Mathf.Floor(m_slideIndex);
|
||||
if (d) return l * groupHeight + p * dockHeight;
|
||||
else return l * groupHeight + dockHeight + p * (groupHeight - dockHeight);
|
||||
}
|
||||
|
||||
private void SetChildrenAlongAxis(int axis) {
|
||||
int isHorizontal = (int)m_side & 1;
|
||||
bool isReversed = m_side == Side.Right || m_side == Side.Bottom;
|
||||
var rect = rectTransform.rect;
|
||||
if ((isHorizontal ^ axis) == 1) {
|
||||
float p0 = isHorizontal == 1 ? m_Padding.left : m_Padding.top;
|
||||
float p1 = isHorizontal == 1 ? m_Padding.right : m_Padding.bottom;
|
||||
var gs = rect.size - new Vector2(m_Padding.horizontal, m_Padding.vertical);
|
||||
if (isHorizontal == 0) gs = new Vector2(gs.y, gs.x);
|
||||
if (rectChildren.Count == 1) {
|
||||
SetChildAlongAxis(rectChildren[0], axis, p0, gs.x);
|
||||
}
|
||||
else {
|
||||
float s1 = GetDockElementSize(gs);
|
||||
float s0 = GetSlidePosition(gs.x, s1);
|
||||
float a1 = (isHorizontal == 0 ? rect.height : rect.width) - p0 - p1;
|
||||
for (int i = 0; i < rectChildren.Count; i++) {
|
||||
var c = rectChildren[i];
|
||||
bool d = i % 2 == 0;
|
||||
int l = i / 2;
|
||||
if (isReversed)
|
||||
SetChildAlongAxis(c, axis, (d ? a1 - s1 + p0 : p0) - a1 * l + s0, d ? s1 : a1 - s1);
|
||||
else
|
||||
SetChildAlongAxis(c, axis, (d ? p0 : s1 + p0) - s0 + a1 * l, d ? s1 : a1 - s1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
float p0 = isHorizontal == 0 ? m_Padding.left : m_Padding.top;
|
||||
float p1 = isHorizontal == 0 ? m_Padding.right : m_Padding.bottom;
|
||||
var height = (isHorizontal == 1 ? rect.height : rect.width) - p0 - p1;
|
||||
for (int i = 0; i < rectChildren.Count; i++) {
|
||||
SetChildAlongAxis(rectChildren[i], axis, p0, height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the first child element along the axis.
|
||||
/// </summary>
|
||||
/// <param name="groupSize">The size of the layout group.</param>
|
||||
/// <returns></returns>
|
||||
protected abstract float GetDockElementSize(Vector2 groupSize);
|
||||
}
|
||||
}
|
11
Assets/Cryville.Common/Unity/UI/DockLayoutGroup.cs.meta
Normal file
11
Assets/Cryville.Common/Unity/UI/DockLayoutGroup.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fb94861327965934cb429511060d45fb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,24 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cryville.Common.Unity.UI {
|
||||
/// <summary>
|
||||
/// A <see cref="DockLayoutGroup" /> that sets the occupied ratio of the docking element.
|
||||
/// </summary>
|
||||
[AddComponentMenu("Layout/Dock Occupied Ratio Layout Group")]
|
||||
public sealed class DockOccupiedRatioLayoutGroup : DockLayoutGroup {
|
||||
[SerializeField]
|
||||
[Tooltip("The occupied ratio of the docking element.")]
|
||||
private float m_dockOccupiedRatio = 1;
|
||||
/// <summary>
|
||||
/// The occupied ratio of the docking element.
|
||||
/// </summary>
|
||||
public float DockOccupiedRatio {
|
||||
get { return m_dockOccupiedRatio; }
|
||||
set { base.SetProperty(ref m_dockOccupiedRatio, value); }
|
||||
}
|
||||
|
||||
protected override float GetDockElementSize(Vector2 groupSize) {
|
||||
return groupSize.x * m_dockOccupiedRatio;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0bbf3330df0a07e46bcdf0b3c4879ecf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
97
Assets/Cryville.Common/Unity/UI/FlowLayoutGroup.cs
Normal file
97
Assets/Cryville.Common/Unity/UI/FlowLayoutGroup.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Cryville.Common.Unity.UI {
|
||||
[AddComponentMenu("Layout/Flow Layout Group")]
|
||||
public class FlowLayoutGroup : LayoutGroup {
|
||||
[SerializeField] Vector2 m_spacing;
|
||||
public Vector2 Spacing {
|
||||
get { return m_spacing; }
|
||||
set { SetProperty(ref m_spacing, value); }
|
||||
}
|
||||
|
||||
[SerializeField][Range(0, 1)] float m_itemAlignmentRatio;
|
||||
public float ItemAlignmentRatio {
|
||||
get { return m_itemAlignmentRatio; }
|
||||
set { SetProperty(ref m_itemAlignmentRatio, value); }
|
||||
}
|
||||
|
||||
[SerializeField][Range(0, 1)] float m_itemAlignmentStretchingRatio;
|
||||
public float ItemAlignmentStretchingRatio {
|
||||
get { return m_itemAlignmentStretchingRatio; }
|
||||
set { SetProperty(ref m_itemAlignmentStretchingRatio, value); }
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public sealed override void CalculateLayoutInputHorizontal() { base.CalculateLayoutInputHorizontal(); CalcAlongAxis(0); }
|
||||
/// <inheritdoc />
|
||||
public sealed override void CalculateLayoutInputVertical() { CalcAlongAxis(1); }
|
||||
/// <inheritdoc />
|
||||
public sealed override void SetLayoutHorizontal() { SetChildrenAlongAxis(0); }
|
||||
/// <inheritdoc />
|
||||
public sealed override void SetLayoutVertical() { SetChildrenAlongAxis(1); }
|
||||
|
||||
void CalcAlongAxis(int axis) {
|
||||
if (axis == 0) {
|
||||
SetLayoutInputForAxis(0, 0, 1, axis); // TODO
|
||||
}
|
||||
else {
|
||||
float width = rectTransform.rect.width - padding.horizontal;
|
||||
float x = 0, y = padding.top;
|
||||
float lineHeight = 0;
|
||||
for (int i = 0; i < rectChildren.Count; i++) {
|
||||
RectTransform child = rectChildren[i];
|
||||
float minWidth = LayoutUtility.GetMinWidth(child);
|
||||
float childWidth = Math.Max(minWidth, Math.Min(width, LayoutUtility.GetPreferredWidth(child)));
|
||||
float childHeight = LayoutUtility.GetPreferredHeight(child);
|
||||
|
||||
if (childWidth > width - x) {
|
||||
x = 0;
|
||||
if (i > 0) y += lineHeight + m_spacing.y;
|
||||
lineHeight = 0;
|
||||
}
|
||||
|
||||
x += childWidth + m_spacing.x;
|
||||
if (childHeight > lineHeight) lineHeight = childHeight;
|
||||
}
|
||||
SetLayoutInputForAxis(y + lineHeight, y + lineHeight, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void SetChildrenAlongAxis(int axis) {
|
||||
float width = rectTransform.rect.width - padding.horizontal;
|
||||
float x = 0, y = padding.top;
|
||||
float lineHeight = 0;
|
||||
int firstItemIndexOfLine = 0;
|
||||
for (int i = 0; i < rectChildren.Count; i++) {
|
||||
RectTransform child = rectChildren[i];
|
||||
float minWidth = LayoutUtility.GetMinWidth(child);
|
||||
float childWidth = Math.Max(minWidth, Math.Min(width, LayoutUtility.GetPreferredWidth(child)));
|
||||
float childHeight = LayoutUtility.GetPreferredHeight(child);
|
||||
|
||||
if (childWidth > width - x) {
|
||||
AlignItemsInLine(firstItemIndexOfLine, i, y, lineHeight);
|
||||
x = 0;
|
||||
if (i > 0) y += lineHeight + m_spacing.y;
|
||||
lineHeight = 0;
|
||||
firstItemIndexOfLine = i;
|
||||
}
|
||||
|
||||
SetChildAlongAxis(child, 0, x + padding.left, childWidth);
|
||||
SetChildAlongAxis(child, 1, y, childHeight);
|
||||
|
||||
x += childWidth + m_spacing.x;
|
||||
if (childHeight > lineHeight) lineHeight = childHeight;
|
||||
}
|
||||
AlignItemsInLine(firstItemIndexOfLine, rectChildren.Count, y, lineHeight);
|
||||
}
|
||||
void AlignItemsInLine(int startIndex, int endIndex, float y, float lineHeight) {
|
||||
for (int i = startIndex; i < endIndex; i++) {
|
||||
RectTransform child = rectChildren[i];
|
||||
float childHeight = LayoutUtility.GetPreferredHeight(child) * (1 - m_itemAlignmentStretchingRatio) + lineHeight * m_itemAlignmentStretchingRatio;
|
||||
SetChildAlongAxis(child, 1, y + (lineHeight - childHeight) * m_itemAlignmentRatio, childHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Cryville.Common/Unity/UI/FlowLayoutGroup.cs.meta
Normal file
11
Assets/Cryville.Common/Unity/UI/FlowLayoutGroup.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1ce1debad67438245a6e00e503975beb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
94
Assets/Cryville.Common/Unity/UI/SingleLayoutGroup.cs
Normal file
94
Assets/Cryville.Common/Unity/UI/SingleLayoutGroup.cs
Normal file
@@ -0,0 +1,94 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Cryville.Common.Unity.UI {
|
||||
[AddComponentMenu("Layout/Single Layout Group")]
|
||||
[ExecuteAlways]
|
||||
public class SingleLayoutGroup : LayoutGroup {
|
||||
[SerializeField]
|
||||
protected bool m_ChildForceExpandWidth = true;
|
||||
|
||||
[SerializeField]
|
||||
protected bool m_ChildForceExpandHeight = true;
|
||||
|
||||
[SerializeField]
|
||||
protected bool m_ChildControlWidth = true;
|
||||
|
||||
[SerializeField]
|
||||
protected bool m_ChildControlHeight = true;
|
||||
|
||||
[SerializeField]
|
||||
protected bool m_ChildScaleWidth;
|
||||
|
||||
[SerializeField]
|
||||
protected bool m_ChildScaleHeight;
|
||||
|
||||
public override void CalculateLayoutInputHorizontal() {
|
||||
base.CalculateLayoutInputHorizontal();
|
||||
CalcAlongAxis(0);
|
||||
}
|
||||
public override void CalculateLayoutInputVertical() { CalcAlongAxis(1); }
|
||||
public override void SetLayoutHorizontal() { SetChildrenAlongAxis(0); }
|
||||
public override void SetLayoutVertical() { SetChildrenAlongAxis(1); }
|
||||
|
||||
protected void CalcAlongAxis(int axis) {
|
||||
float combinedPadding = (axis == 0) ? padding.horizontal : padding.vertical;
|
||||
bool controlSize = (axis == 0) ? m_ChildControlWidth : m_ChildControlHeight;
|
||||
bool useScale = (axis == 0) ? m_ChildScaleWidth : m_ChildScaleHeight;
|
||||
bool childForceExpandSize = (axis == 0) ? m_ChildForceExpandWidth : m_ChildForceExpandHeight;
|
||||
float totalMin = combinedPadding;
|
||||
float totalPreferred = combinedPadding;
|
||||
float totalFlexible = 0f;
|
||||
RectTransform child = rectChildren[0];
|
||||
GetChildSizes(child, axis, controlSize, childForceExpandSize, out var min, out var preferred, out var flexible);
|
||||
if (useScale) {
|
||||
float scaleFactor = child.localScale[axis];
|
||||
min *= scaleFactor;
|
||||
preferred *= scaleFactor;
|
||||
flexible *= scaleFactor;
|
||||
}
|
||||
totalMin = Mathf.Max(min + combinedPadding, totalMin);
|
||||
totalPreferred = Mathf.Max(preferred + combinedPadding, totalPreferred);
|
||||
totalFlexible = Mathf.Max(flexible, totalFlexible);
|
||||
totalPreferred = Mathf.Max(totalMin, totalPreferred);
|
||||
SetLayoutInputForAxis(totalMin, totalPreferred, totalFlexible, axis);
|
||||
}
|
||||
|
||||
protected void SetChildrenAlongAxis(int axis) {
|
||||
float size = rectTransform.rect.size[axis];
|
||||
bool controlSize = (axis == 0) ? m_ChildControlWidth : m_ChildControlHeight;
|
||||
bool useScale = (axis == 0) ? m_ChildScaleWidth : m_ChildScaleHeight;
|
||||
bool childForceExpandSize = (axis == 0) ? m_ChildForceExpandWidth : m_ChildForceExpandHeight;
|
||||
float alignmentOnAxis = GetAlignmentOnAxis(axis);
|
||||
float innerSize = size - ((axis == 0) ? padding.horizontal : padding.vertical);
|
||||
RectTransform child = rectChildren[0];
|
||||
GetChildSizes(child, axis, controlSize, childForceExpandSize, out var min2, out var preferred2, out var flexible2);
|
||||
float scaleFactor2 = useScale ? child.localScale[axis] : 1f;
|
||||
float requiredSpace = Mathf.Clamp(innerSize, min2, (flexible2 > 0f) ? size : preferred2);
|
||||
float startOffset = GetStartOffset(axis, requiredSpace * scaleFactor2);
|
||||
if (controlSize) {
|
||||
SetChildAlongAxisWithScale(child, axis, startOffset, requiredSpace, scaleFactor2);
|
||||
}
|
||||
else {
|
||||
float offsetInCell2 = (requiredSpace - child.sizeDelta[axis]) * alignmentOnAxis;
|
||||
SetChildAlongAxisWithScale(child, axis, startOffset + offsetInCell2, scaleFactor2);
|
||||
}
|
||||
}
|
||||
|
||||
void GetChildSizes(RectTransform child, int axis, bool controlSize, bool childForceExpand, out float min, out float preferred, out float flexible) {
|
||||
if (!controlSize) {
|
||||
min = child.sizeDelta[axis];
|
||||
preferred = min;
|
||||
flexible = 0f;
|
||||
}
|
||||
else {
|
||||
min = LayoutUtility.GetMinSize(child, axis);
|
||||
preferred = LayoutUtility.GetPreferredSize(child, axis);
|
||||
flexible = LayoutUtility.GetFlexibleSize(child, axis);
|
||||
}
|
||||
if (childForceExpand) {
|
||||
flexible = Mathf.Max(flexible, 1f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Cryville.Common/Unity/UI/SingleLayoutGroup.cs.meta
Normal file
11
Assets/Cryville.Common/Unity/UI/SingleLayoutGroup.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 21255a16576b76f4280a765b43a4ae1c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
80
Assets/Cryville.Common/Unity/UI/TMPLocalizedText.cs
Normal file
80
Assets/Cryville.Common/Unity/UI/TMPLocalizedText.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using Cryville.Common.Font;
|
||||
using Cryville.Culture;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TextCore.LowLevel;
|
||||
using UnityEngine.TextCore.Text;
|
||||
|
||||
namespace Cryville.Common.Unity.UI {
|
||||
[RequireComponent(typeof(TMP_Text))]
|
||||
public class TMPLocalizedText : MonoBehaviour {
|
||||
public static Shader DefaultShader;
|
||||
public static FontMatcher FontMatcher;
|
||||
public static int MaxFallbackCount = 4;
|
||||
|
||||
static readonly Dictionary<CultureInfo, FontAsset> _cachedFonts = new();
|
||||
|
||||
[SerializeField]
|
||||
Shader m_shader;
|
||||
|
||||
public TMP_Text Text { get; private set; }
|
||||
void Awake() {
|
||||
Text = GetComponent<TMP_Text>();
|
||||
}
|
||||
|
||||
public void SetText(string text, CultureInfo culture = null) {
|
||||
Text.text = text;
|
||||
SetCulture(culture ?? CultureInfo.CurrentCulture);
|
||||
}
|
||||
|
||||
void SetCulture(CultureInfo culture) {
|
||||
if (FontMatcher == null) return;
|
||||
string cultureName = culture.Name;
|
||||
if (string.IsNullOrEmpty(cultureName)) cultureName = CultureInfo.CurrentCulture.Name;
|
||||
if (!_cachedFonts.TryGetValue(culture, out var font)) {
|
||||
foreach (var typeface in FontMatcher.MatchLanguage(new LanguageId(cultureName), true)) {
|
||||
try {
|
||||
var ifont = CreateFontAsset(typeface.File.FullName, typeface.IndexInFile);
|
||||
if (m_shader) ifont.material.shader = m_shader;
|
||||
else if (DefaultShader) ifont.material.shader = DefaultShader;
|
||||
if (font == null) {
|
||||
font = ifont;
|
||||
if (MaxFallbackCount <= 0) break;
|
||||
}
|
||||
else {
|
||||
font.fallbackFontAssetTable ??= new List<FontAsset>();
|
||||
font.fallbackFontAssetTable.Add(ifont);
|
||||
if (font.fallbackFontAssetTable.Count >= MaxFallbackCount) break;
|
||||
}
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
_cachedFonts.Add(culture, font);
|
||||
}
|
||||
Text.font = font;
|
||||
}
|
||||
|
||||
static MethodInfo _methodCreateFontAsset;
|
||||
static readonly object[] _paramsCreateFontAsset = new object[] { null, null, 90, 9, GlyphRenderMode.SDFAA, 1024, 1024, Type.Missing, Type.Missing };
|
||||
static FontAsset CreateFontAsset(string path, int index) {
|
||||
if (_methodCreateFontAsset == null) {
|
||||
_methodCreateFontAsset = typeof(FontAsset).GetMethod(
|
||||
"CreateFontAsset", BindingFlags.Static | BindingFlags.NonPublic, null,
|
||||
new Type[] {
|
||||
typeof(string), typeof(int), typeof(int), typeof(int),
|
||||
typeof(GlyphRenderMode), typeof(int), typeof(int),
|
||||
typeof(AtlasPopulationMode), typeof(bool)
|
||||
},
|
||||
null
|
||||
);
|
||||
}
|
||||
_paramsCreateFontAsset[0] = path;
|
||||
_paramsCreateFontAsset[1] = index;
|
||||
return (FontAsset)_methodCreateFontAsset.Invoke(null, _paramsCreateFontAsset);
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Cryville.Common/Unity/UI/TMPLocalizedText.cs.meta
Normal file
11
Assets/Cryville.Common/Unity/UI/TMPLocalizedText.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c03870a7d4386e846be005a0ac36e987
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user