void CalcAnchor(out float offsetX, out float offsetY) { if (_font != null) { // calc string width float maxWidth = 0.0f; float width = 0.0f; int numLines = 1; for (int i = 0; i < _maxChars && i < _text.Length; ++i) { if (_text[i] == '\n') { numLines++; maxWidth = Mathf.Max(maxWidth, width); width = 0.0f; } else { tk2dFontChar chr = _font.chars[_text[i]]; width += chr.advance * _scale.x; } } maxWidth = Mathf.Max(maxWidth, width); float lineHeight = _font.lineHeight * _scale.y; float height = lineHeight * numLines; switch (_anchor) { case TextAnchor.LowerLeft: offsetX = 0.0f; offsetY = height - lineHeight; break; case TextAnchor.MiddleLeft: offsetX = 0.0f; offsetY = height / 2.0f - lineHeight; break; case TextAnchor.UpperLeft: offsetX = 0.0f; offsetY = -lineHeight; break; case TextAnchor.LowerCenter: offsetX = -maxWidth / 2.0f; offsetY = height - lineHeight; break; case TextAnchor.MiddleCenter: offsetX = -maxWidth / 2.0f; offsetY = height / 2.0f - lineHeight; break; case TextAnchor.UpperCenter: offsetX = -maxWidth / 2.0f; offsetY = -lineHeight; break; case TextAnchor.LowerRight: offsetX = -maxWidth; offsetY = height - lineHeight; break; case TextAnchor.MiddleRight: offsetX = -maxWidth; offsetY = height / 2.0f - lineHeight; break; case TextAnchor.UpperRight: offsetX = -maxWidth; offsetY = -lineHeight; break; default: offsetX = 0.0f; offsetY = 0.0f; break; } } else { offsetX = 0.0f; offsetY = 0.0f; } }
/// <summary> /// Calculates the mesh dimensions for the given string /// and returns a width and height. /// </summary> public static Vector2 GetMeshDimensionsForString(this tk2dTextMeshData geomData, tk2dColoredText inputText, int startIdx, int count) { tk2dTextMeshData data = geomData; tk2dFontData _fontInst = data.FontInst; float maxWidth = 0.0f; float cursorX = 0.0f; float cursorY = 0.0f; for (int i = startIdx; (i < inputText.Length) && (i < (startIdx + count)); ++i) { int idx = inputText[i]; if (idx == '\n') { maxWidth = Mathf.Max(cursorX, maxWidth); cursorX = 0.0f; cursorY -= data.ActualLineSpaceHeight; } else { // Get the character from dictionary / array tk2dFontChar chr = _fontInst.GetCharForIndex(idx, 0); cursorX += (chr.advance + data.Spacing) * data.TotalScale.x; if (data.kerning && i < inputText.Length - 1) { foreach (var k in _fontInst.kerning) { if (k.c0 == inputText[i] && k.c1 == inputText[i + 1]) { cursorX += k.amount * data.TotalScale.x; break; } } } } } maxWidth = Mathf.Max(cursorX, maxWidth); cursorY -= data.ActualLineHeight; return(new Vector2(maxWidth, cursorY)); }
public static bool BuildFont(Info fontInfo, tk2dFontData target, float scale, int charPadX, bool dupeCaps, bool flipTextureY, Texture2D gradientTexture, int gradientCount) { float texWidth = fontInfo.scaleW; float texHeight = fontInfo.scaleH; float lineHeight = fontInfo.lineHeight; target.lineHeight = lineHeight * scale; // Get number of characters (lastindex + 1) int maxCharId = 0; int maxUnicodeChar = 100000; foreach (var theChar in fontInfo.chars) { if (theChar.id > maxUnicodeChar) { // in most cases the font contains unwanted characters! Debug.LogError("Unicode character id exceeds allowed limit: " + theChar.id.ToString() + ". Skipping."); continue; } if (theChar.id > maxCharId) maxCharId = theChar.id; } // decide to use dictionary if necessary // 2048 is a conservative lower floor bool useDictionary = maxCharId > 2048; Dictionary<int, tk2dFontChar> charDict = (useDictionary)?new Dictionary<int, tk2dFontChar>():null; tk2dFontChar[] chars = (useDictionary)?null:new tk2dFontChar[maxCharId + 1]; int minChar = 0x7fffffff; int maxCharWithinBounds = 0; int numLocalChars = 0; float largestWidth = 0.0f; foreach (var theChar in fontInfo.chars) { tk2dFontChar thisChar = new tk2dFontChar(); int id = theChar.id; int x = theChar.x; int y = theChar.y; int width = theChar.width; int height = theChar.height; int xoffset = theChar.xoffset; int yoffset = theChar.yoffset; int xadvance = theChar.xadvance + charPadX; // special case, if the width and height are zero, the origin doesn't need to be offset // handles problematic case highlighted here: // http://2dtoolkit.com/forum/index.php/topic,89.msg220.html if (width == 0 && height == 0) { xoffset = 0; yoffset = 0; } // precompute required data float px = xoffset * scale; float py = (lineHeight - yoffset) * scale; if (theChar.texOverride) { int w = theChar.texW; int h = theChar.texH; if (theChar.texFlipped) { h = theChar.texW; w = theChar.texH; } thisChar.p0 = new Vector3(px + theChar.texOffsetX * scale, py - theChar.texOffsetY * scale, 0); thisChar.p1 = new Vector3(px + (theChar.texOffsetX + w) * scale, py - (theChar.texOffsetY + h) * scale, 0); thisChar.uv0 = new Vector2((theChar.texX) / texWidth, (theChar.texY + theChar.texH) / texHeight); thisChar.uv1 = new Vector2((theChar.texX + theChar.texW) / texWidth, (theChar.texY) / texHeight); thisChar.flipped = theChar.texFlipped; } else { thisChar.p0 = new Vector3(px, py, 0); thisChar.p1 = new Vector3(px + width * scale, py - height * scale, 0); if (flipTextureY) { thisChar.uv0 = new Vector2(x / texWidth, y / texHeight); thisChar.uv1 = new Vector2(thisChar.uv0.x + width / texWidth, thisChar.uv0.y + height / texHeight); } else { thisChar.uv0 = new Vector2(x / texWidth, 1.0f - y / texHeight); thisChar.uv1 = new Vector2(thisChar.uv0.x + width / texWidth, thisChar.uv0.y - height / texHeight); } thisChar.flipped = false; } thisChar.advance = xadvance * scale; largestWidth = Mathf.Max(thisChar.advance, largestWidth); // Needs gradient data if (gradientTexture != null) { // build it up assuming the first gradient float x0 = (float)(0.0f / gradientCount); float x1 = (float)(1.0f / gradientCount); float y0 = 1.0f; float y1 = 0.0f; // align to glyph if necessary thisChar.gradientUv = new Vector2[4]; thisChar.gradientUv[0] = new Vector2(x0, y0); thisChar.gradientUv[1] = new Vector2(x1, y0); thisChar.gradientUv[2] = new Vector2(x0, y1); thisChar.gradientUv[3] = new Vector2(x1, y1); } if (id <= maxCharId) { maxCharWithinBounds = (id > maxCharWithinBounds) ? id : maxCharWithinBounds; minChar = (id < minChar) ? id : minChar; if (useDictionary) charDict[id] = thisChar; else chars[id] = thisChar; ++numLocalChars; } } // duplicate capitals to lower case, or vice versa depending on which ones exist if (dupeCaps) { for (int uc = 'A'; uc <= 'Z'; ++uc) { int lc = uc + ('a' - 'A'); if (useDictionary) { if (charDict.ContainsKey(uc)) charDict[lc] = charDict[uc]; else if (charDict.ContainsKey(lc)) charDict[uc] = charDict[lc]; } else { if (chars[lc] == null) chars[lc] = chars[uc]; else if (chars[uc] == null) chars[uc] = chars[lc]; } } } // share null char, same pointer var nullChar = new tk2dFontChar(); nullChar.gradientUv = new Vector2[4]; // this would be null otherwise target.largestWidth = largestWidth; if (useDictionary) { // guarantee at least the first 256 characters for (int i = 0; i < 256; ++i) { if (!charDict.ContainsKey(i)) charDict[i] = nullChar; } target.chars = null; target.SetDictionary(charDict); target.useDictionary = true; } else { target.chars = new tk2dFontChar[maxCharId + 1]; for (int i = 0; i <= maxCharId; ++i) { target.chars[i] = chars[i]; if (target.chars[i] == null) { target.chars[i] = nullChar; // zero everything, null char } } target.charDict = null; target.useDictionary = false; } // kerning target.kerning = new tk2dFontKerning[fontInfo.kernings.Count]; for (int i = 0; i < target.kerning.Length; ++i) { tk2dFontKerning kerning = new tk2dFontKerning(); kerning.c0 = fontInfo.kernings[i].first; kerning.c1 = fontInfo.kernings[i].second; kerning.amount = fontInfo.kernings[i].amount * scale; target.kerning[i] = kerning; } return true; }
public static bool BuildFont(Info fontInfo, tk2dFontData target, float scale, int charPadX, bool dupeCaps, bool flipTextureY, Texture2D gradientTexture, int gradientCount) { float texWidth = fontInfo.scaleW; float texHeight = fontInfo.scaleH; float lineHeight = fontInfo.lineHeight; target.lineHeight = lineHeight * scale; // Get number of characters (lastindex + 1) int maxCharId = 0; int maxUnicodeChar = 100000; foreach (var theChar in fontInfo.chars) { if (theChar.id > maxUnicodeChar) { // in most cases the font contains unwanted characters! Debug.LogError("Unicode character id exceeds allowed limit: " + theChar.id.ToString() + ". Skipping."); continue; } if (theChar.id > maxCharId) { maxCharId = theChar.id; } } // decide to use dictionary if necessary // 2048 is a conservative lower floor bool useDictionary = maxCharId > 2048; Dictionary <int, tk2dFontChar> charDict = (useDictionary)?new Dictionary <int, tk2dFontChar>():null; tk2dFontChar[] chars = (useDictionary)?null:new tk2dFontChar[maxCharId + 1]; int minChar = 0x7fffffff; int maxCharWithinBounds = 0; int numLocalChars = 0; float largestWidth = 0.0f; foreach (var theChar in fontInfo.chars) { tk2dFontChar thisChar = new tk2dFontChar(); int id = theChar.id; int x = theChar.x; int y = theChar.y; int width = theChar.width; int height = theChar.height; int xoffset = theChar.xoffset; int yoffset = theChar.yoffset; int xadvance = theChar.xadvance + charPadX; // special case, if the width and height are zero, the origin doesn't need to be offset // handles problematic case highlighted here: // http://unikronsoftware.com/2dtoolkit/forum/index.php/topic,89.msg220.html if (width == 0 && height == 0) { xoffset = 0; yoffset = 0; } // precompute required data float px = xoffset * scale; float py = (lineHeight - yoffset) * scale; if (theChar.texOverride) { int w = theChar.texW; int h = theChar.texH; if (theChar.texFlipped) { h = theChar.texW; w = theChar.texH; } thisChar.p0 = new Vector3(px + theChar.texOffsetX * scale, py - theChar.texOffsetY * scale, 0); thisChar.p1 = new Vector3(px + (theChar.texOffsetX + w) * scale, py - (theChar.texOffsetY + h) * scale, 0); thisChar.uv0 = new Vector2((theChar.texX) / texWidth, (theChar.texY + theChar.texH) / texHeight); thisChar.uv1 = new Vector2((theChar.texX + theChar.texW) / texWidth, (theChar.texY) / texHeight); thisChar.flipped = theChar.texFlipped; } else { thisChar.p0 = new Vector3(px, py, 0); thisChar.p1 = new Vector3(px + width * scale, py - height * scale, 0); if (flipTextureY) { thisChar.uv0 = new Vector2(x / texWidth, y / texHeight); thisChar.uv1 = new Vector2(thisChar.uv0.x + width / texWidth, thisChar.uv0.y + height / texHeight); } else { thisChar.uv0 = new Vector2(x / texWidth, 1.0f - y / texHeight); thisChar.uv1 = new Vector2(thisChar.uv0.x + width / texWidth, thisChar.uv0.y - height / texHeight); } thisChar.flipped = false; } thisChar.advance = xadvance * scale; largestWidth = Mathf.Max(thisChar.advance, largestWidth); // Needs gradient data if (gradientTexture != null) { // build it up assuming the first gradient float x0 = (float)(0.0f / gradientCount); float x1 = (float)(1.0f / gradientCount); float y0 = 1.0f; float y1 = 0.0f; // align to glyph if necessary thisChar.gradientUv = new Vector2[4]; thisChar.gradientUv[0] = new Vector2(x0, y0); thisChar.gradientUv[1] = new Vector2(x1, y0); thisChar.gradientUv[2] = new Vector2(x0, y1); thisChar.gradientUv[3] = new Vector2(x1, y1); } if (id <= maxCharId) { maxCharWithinBounds = (id > maxCharWithinBounds) ? id : maxCharWithinBounds; minChar = (id < minChar) ? id : minChar; if (useDictionary) { charDict[id] = thisChar; } else { chars[id] = thisChar; } ++numLocalChars; } } // duplicate capitals to lower case, or vice versa depending on which ones exist if (dupeCaps) { for (int uc = 'A'; uc <= 'Z'; ++uc) { int lc = uc + ('a' - 'A'); if (useDictionary) { if (charDict.ContainsKey(uc)) { charDict[lc] = charDict[uc]; } else if (charDict.ContainsKey(lc)) { charDict[uc] = charDict[lc]; } } else { if (chars[lc] == null) { chars[lc] = chars[uc]; } else if (chars[uc] == null) { chars[uc] = chars[lc]; } } } } // share null char, same pointer var nullChar = new tk2dFontChar(); nullChar.gradientUv = new Vector2[4]; // this would be null otherwise target.largestWidth = largestWidth; if (useDictionary) { // guarantee at least the first 256 characters for (int i = 0; i < 256; ++i) { if (!charDict.ContainsKey(i)) { charDict[i] = nullChar; } } target.chars = null; target.SetDictionary(charDict); target.useDictionary = true; } else { target.chars = new tk2dFontChar[maxCharId + 1]; for (int i = 0; i <= maxCharId; ++i) { target.chars[i] = chars[i]; if (target.chars[i] == null) { target.chars[i] = nullChar; // zero everything, null char } } target.charDict = null; target.useDictionary = false; } // kerning target.kerning = new tk2dFontKerning[fontInfo.kernings.Count]; for (int i = 0; i < target.kerning.Length; ++i) { tk2dFontKerning kerning = new tk2dFontKerning(); kerning.c0 = fontInfo.kernings[i].first; kerning.c1 = fontInfo.kernings[i].second; kerning.amount = fontInfo.kernings[i].amount * scale; target.kerning[i] = kerning; } return(true); }
int CalcAnchor(bool useInlineStyling, out float offsetX, out float offsetY) { int numChars = 0; if (_font != null) { // calc string width float maxWidth = 0.0f; float width = 0.0f; int numLines = 1; int count = 0; for (int i = 0; count < _maxChars && i < _text.Length; ++i) { int idx = _text[i]; if (idx >= _font.chars.Length) { idx = 0; // should be space } tk2dFontChar chr = _font.chars[idx]; if (idx == '\n') { numLines++; maxWidth = Mathf.Max(maxWidth, width); width = 0.0f; continue; } else if (useInlineStyling) { if (idx == '^') { if (i + 1 < _text.Length) { i++; if (_text[i] != '^') { continue; } } } } width += chr.advance * _scale.x; numChars++; count++; } maxWidth = Mathf.Max(maxWidth, width); float lineHeight = _font.lineHeight * _scale.y; float height = lineHeight * numLines; switch (_anchor) { case TextAnchor.LowerLeft: offsetX = 0.0f; offsetY = height - lineHeight; break; case TextAnchor.MiddleLeft: offsetX = 0.0f; offsetY = height / 2.0f - lineHeight; break; case TextAnchor.UpperLeft: offsetX = 0.0f; offsetY = -lineHeight; break; case TextAnchor.LowerCenter: offsetX = -maxWidth / 2.0f; offsetY = height - lineHeight; break; case TextAnchor.MiddleCenter: offsetX = -maxWidth / 2.0f; offsetY = height / 2.0f - lineHeight; break; case TextAnchor.UpperCenter: offsetX = -maxWidth / 2.0f; offsetY = -lineHeight; break; case TextAnchor.LowerRight: offsetX = -maxWidth; offsetY = height - lineHeight; break; case TextAnchor.MiddleRight: offsetX = -maxWidth; offsetY = height / 2.0f - lineHeight; break; case TextAnchor.UpperRight: offsetX = -maxWidth; offsetY = -lineHeight; break; default: offsetX = 0.0f; offsetY = 0.0f; break; } } else { offsetX = 0.0f; offsetY = 0.0f; } return(numChars); }
// returns number of characters written int FillTextData() { Vector2 gradientOffset = new Vector2((float)_textureGradient / font.gradientCount, 0); bool useInlineStyling = inlineStyling && _font.textureGradients; float offsetX, offsetY; CalcAnchor(useInlineStyling, out offsetX, out offsetY); float cursorX = 0.0f; float cursorY = 0.0f; int target = 0; for (int i = 0; i < _text.Length && target < _maxChars; ++i) { int idx = _text[i]; if (idx >= _font.chars.Length) { idx = 0; // should be space } tk2dFontChar chr = _font.chars[idx]; if (idx == '\n') { cursorX = 0.0f; cursorY -= _font.lineHeight * _scale.y; continue; } else if (useInlineStyling) { if (idx == '^') { if (i + 1 < _text.Length) { i++; if (_text[i] != '^') { int data = _text[i] - '0'; gradientOffset = new Vector2((float)data / font.gradientCount, 0); continue; } } } } vertices[target * 4 + 0] = new Vector3(offsetX + cursorX + chr.p0.x * _scale.x, offsetY + cursorY + chr.p0.y * _scale.y, 0); vertices[target * 4 + 1] = new Vector3(offsetX + cursorX + chr.p1.x * _scale.x, offsetY + cursorY + chr.p0.y * _scale.y, 0); vertices[target * 4 + 2] = new Vector3(offsetX + cursorX + chr.p0.x * _scale.x, offsetY + cursorY + chr.p1.y * _scale.y, 0); vertices[target * 4 + 3] = new Vector3(offsetX + cursorX + chr.p1.x * _scale.x, offsetY + cursorY + chr.p1.y * _scale.y, 0); uvs[target * 4 + 0] = new Vector2(chr.uv0.x, chr.uv0.y); uvs[target * 4 + 1] = new Vector2(chr.uv1.x, chr.uv0.y); uvs[target * 4 + 2] = new Vector2(chr.uv0.x, chr.uv1.y); uvs[target * 4 + 3] = new Vector2(chr.uv1.x, chr.uv1.y); if (_font.textureGradients) { uv2[target * 4 + 0] = gradientOffset + chr.gradientUv[0]; uv2[target * 4 + 1] = gradientOffset + chr.gradientUv[1]; uv2[target * 4 + 2] = gradientOffset + chr.gradientUv[2]; uv2[target * 4 + 3] = gradientOffset + chr.gradientUv[3]; } cursorX += chr.advance * _scale.x; if (_kerning && i < _text.Length - 1) { foreach (var k in _font.kerning) { if (k.c0 == _text[i] && k.c1 == _text[i + 1]) { cursorX += k.amount * _scale.x; break; } } } ++target; } return(target); }
bool ParseBMFont(string path, tk2dFontData bmFont, tk2dFont source) { IntFontInfo fontInfo = null; try { XmlDocument doc = new XmlDocument(); doc.Load(path); fontInfo = ParseBMFontXml(doc); } catch { fontInfo = ParseBMFontText(path); } if (fontInfo == null || fontInfo.chars.Count == 0) { return(false); } float texWidth = fontInfo.scaleW; float texHeight = fontInfo.scaleH; float lineHeight = fontInfo.lineHeight; float scale = 2.0f * source.targetOrthoSize / source.targetHeight; bmFont.lineHeight = lineHeight * scale; tk2dFontChar[] chars = new tk2dFontChar[source.numCharacters]; int minChar = 65536; int maxCharWithinBounds = 0; int numLocalChars = 0; float largestWidth = 0.0f; foreach (var theChar in fontInfo.chars) { tk2dFontChar thisChar = new tk2dFontChar(); int id = theChar.id; int x = theChar.x; int y = theChar.y; int width = theChar.width; int height = theChar.height; int xoffset = theChar.xoffset; int yoffset = theChar.yoffset; int xadvance = theChar.xadvance; // precompute required data float px = xoffset * scale; float py = (lineHeight - yoffset) * scale; thisChar.p0 = new Vector3(px, py, 0); thisChar.p1 = new Vector3(px + width * scale, py - height * scale, 0); if (source.flipTextureY) { thisChar.uv0 = new Vector2(x / texWidth, y / texHeight); thisChar.uv1 = new Vector2(thisChar.uv0.x + width / texWidth, thisChar.uv0.y + height / texHeight); } else { thisChar.uv0 = new Vector2(x / texWidth, 1.0f - y / texHeight); thisChar.uv1 = new Vector2(thisChar.uv0.x + width / texWidth, thisChar.uv0.y - height / texHeight); } thisChar.advance = xadvance * scale; largestWidth = Mathf.Max(thisChar.advance, largestWidth); // Needs gradient data if (source.gradientTexture != null) { // build it up assuming the first gradient float x0 = (float)(0.0f / source.gradientCount); float x1 = (float)(1.0f / source.gradientCount); float y0 = 1.0f; float y1 = 0.0f; // align to glyph if necessary thisChar.gradientUv = new Vector2[4]; thisChar.gradientUv[0] = new Vector2(x0, y0); thisChar.gradientUv[1] = new Vector2(x1, y0); thisChar.gradientUv[2] = new Vector2(x0, y1); thisChar.gradientUv[3] = new Vector2(x1, y1); } if (id < source.numCharacters) { maxCharWithinBounds = (id > maxCharWithinBounds) ? id : maxCharWithinBounds; minChar = (id < minChar) ? id : minChar; chars[id] = thisChar; ++numLocalChars; } } if (source.dupeCaps) { for (int uc = 'A'; uc <= 'Z'; ++uc) { int lc = uc + ('a' - 'A'); if (chars[lc] == null) { chars[lc] = chars[uc]; } else if (chars[uc] == null) { chars[uc] = chars[lc]; } } } bmFont.largestWidth = largestWidth; bmFont.chars = new tk2dFontChar[source.numCharacters]; for (int i = 0; i < source.numCharacters; ++i) { bmFont.chars[i] = chars[i]; if (bmFont.chars[i] == null) { bmFont.chars[i] = new tk2dFontChar(); // zero everything, null char } } // kerning bmFont.kerning = new tk2dFontKerning[fontInfo.kernings.Count]; for (int i = 0; i < bmFont.kerning.Length; ++i) { tk2dFontKerning kerning = new tk2dFontKerning(); kerning.c0 = fontInfo.kernings[i].first; kerning.c1 = fontInfo.kernings[i].second; kerning.amount = fontInfo.kernings[i].amount * scale; bmFont.kerning[i] = kerning; } return(true); }
Vector2 GetMeshDimensionsForString(string str) { bool _useInlineStyling = useInlineStyling; float maxWidth = 0.0f; float cursorX = 0.0f; float cursorY = 0.0f; int target = 0; for (int i = 0; i < _text.Length && target < _maxChars; ++i) { int idx = _text[i]; if (idx >= _font.chars.Length) { idx = 0; // should be space } tk2dFontChar chr = _font.chars[idx]; if (idx == '\n') { maxWidth = Mathf.Max(cursorX, maxWidth); cursorX = 0.0f; cursorY -= (_font.lineHeight + lineSpacing) * _scale.y; continue; } else if (_useInlineStyling) { if (idx == '^') { if (i + 1 < _text.Length) { i++; if (_text[i] != '^') { continue; } } } } cursorX += (chr.advance + spacing) * _scale.x; if (_kerning && i < _text.Length - 1) { foreach (var k in _font.kerning) { if (k.c0 == _text[i] && k.c1 == _text[i + 1]) { cursorX += k.amount * _scale.x; break; } } } ++target; } maxWidth = Mathf.Max(cursorX, maxWidth); cursorY -= (_font.lineHeight + lineSpacing) * _scale.y; return(new Vector2(maxWidth, cursorY)); }
int FillTextData() { Vector2 gradientOffset = new Vector2((float)_textureGradient / font.gradientCount, 0); Vector2 dims = GetMeshDimensionsForString(_text); float offsetY = GetYAnchorForHeight(dims.y); bool _useInlineStyling = useInlineStyling; float cursorX = 0.0f; float cursorY = 0.0f; int target = 0; int alignStartTarget = 0; for (int i = 0; i < _text.Length && target < _maxChars; ++i) { int idx = _text[i]; if (idx >= _font.chars.Length) { idx = 0; // should be space } tk2dFontChar chr = _font.chars[idx]; if (idx == '\n') { float lineWidth = cursorX; int alignEndTarget = target; // this is one after the last filled character if (alignStartTarget != target) { float xOffset = GetXAnchorForWidth(lineWidth); PostAlignTextData(alignStartTarget, alignEndTarget, xOffset); } alignStartTarget = target; cursorX = 0.0f; cursorY -= (_font.lineHeight + lineSpacing) * _scale.y; continue; } else if (_useInlineStyling) { if (idx == '^') { if (i + 1 < _text.Length) { i++; if (_text[i] != '^') { int data = _text[i] - '0'; gradientOffset = new Vector2((float)data / font.gradientCount, 0); continue; } } } } vertices[target * 4 + 0] = new Vector3(cursorX + chr.p0.x * _scale.x, offsetY + cursorY + chr.p0.y * _scale.y, 0); vertices[target * 4 + 1] = new Vector3(cursorX + chr.p1.x * _scale.x, offsetY + cursorY + chr.p0.y * _scale.y, 0); vertices[target * 4 + 2] = new Vector3(cursorX + chr.p0.x * _scale.x, offsetY + cursorY + chr.p1.y * _scale.y, 0); vertices[target * 4 + 3] = new Vector3(cursorX + chr.p1.x * _scale.x, offsetY + cursorY + chr.p1.y * _scale.y, 0); uvs[target * 4 + 0] = new Vector2(chr.uv0.x, chr.uv0.y); uvs[target * 4 + 1] = new Vector2(chr.uv1.x, chr.uv0.y); uvs[target * 4 + 2] = new Vector2(chr.uv0.x, chr.uv1.y); uvs[target * 4 + 3] = new Vector2(chr.uv1.x, chr.uv1.y); if (_font.textureGradients) { uv2[target * 4 + 0] = gradientOffset + chr.gradientUv[0]; uv2[target * 4 + 1] = gradientOffset + chr.gradientUv[1]; uv2[target * 4 + 2] = gradientOffset + chr.gradientUv[2]; uv2[target * 4 + 3] = gradientOffset + chr.gradientUv[3]; } cursorX += (chr.advance + spacing) * _scale.x; if (_kerning && i < _text.Length - 1) { foreach (var k in _font.kerning) { if (k.c0 == _text[i] && k.c1 == _text[i + 1]) { cursorX += k.amount * _scale.x; break; } } } ++target; } if (alignStartTarget != target) { float lineWidth = cursorX; int alignEndTarget = target; float xOffset = GetXAnchorForWidth(lineWidth); PostAlignTextData(alignStartTarget, alignEndTarget, xOffset); } return(target); }
int FillTextData() { Vector2 gradientOffset = new Vector2((float)_textureGradient / font.gradientCount, 0); Vector2 dims = GetMeshDimensionsForString(_text); float offsetY = GetYAnchorForHeight(dims.y); bool _useInlineStyling = useInlineStyling; float cursorX = 0.0f; float cursorY = 0.0f; int target = 0; int alignStartTarget = 0; Color topColor = _color; Color bottomColor = _useGradient?_color2:_color; for (int i = 0; i < _text.Length && target < _maxChars; ++i) { int idx = _text[i]; tk2dFontChar chr = null; if (idx == '[' && _text[i + 1] == 'c' && _text.IndexOf(']', i) == (i + 11)) { int iFrom = i + 3; int iTo = _text.IndexOf(']', i); i = iTo; string color_str = _text.Substring(iFrom, iTo - iFrom); uint color_code = System.UInt32.Parse(color_str, System.Globalization.NumberStyles.HexNumber); uint r = (color_code & 0xff000000) >> 24; uint g = (color_code & 0x00ff0000) >> 16; uint b = (color_code & 0x0000ff00) >> 8; uint a = (color_code & 0x000000ff) >> 0; float rf = (float)r / 255.0f; float gf = (float)g / 255.0f; float bf = (float)b / 255.0f; float af = (float)a / 255.0f; topColor = new Color(rf, gf, bf, af); bottomColor = topColor; continue; } else if (_font.useDictionary) { if (!_font.charDict.ContainsKey(idx)) { idx = 0; } chr = _font.charDict[idx]; } else { if (idx >= _font.chars.Length) { idx = 0; // should be space } chr = _font.chars[idx]; } if (idx == '\n') { float lineWidth = cursorX; int alignEndTarget = target; // this is one after the last filled character if (alignStartTarget != target) { float xOffset = GetXAnchorForWidth(lineWidth); PostAlignTextData(alignStartTarget, alignEndTarget, xOffset); } alignStartTarget = target; cursorX = 0.0f; cursorY -= (_font.lineHeight + lineSpacing) * _scale.y; continue; } else if (_useInlineStyling) { if (idx == '^') { if (i + 1 < _text.Length) { i++; if (_text[i] != '^') { int data = _text[i] - '0'; gradientOffset = new Vector2((float)data / font.gradientCount, 0); continue; } } } } vertices[target * 4 + 0] = new Vector3(cursorX + chr.p0.x * _scale.x, offsetY + cursorY + chr.p0.y * _scale.y, 0); vertices[target * 4 + 1] = new Vector3(cursorX + chr.p1.x * _scale.x, offsetY + cursorY + chr.p0.y * _scale.y, 0); vertices[target * 4 + 2] = new Vector3(cursorX + chr.p0.x * _scale.x, offsetY + cursorY + chr.p1.y * _scale.y, 0); vertices[target * 4 + 3] = new Vector3(cursorX + chr.p1.x * _scale.x, offsetY + cursorY + chr.p1.y * _scale.y, 0); colors[target * 4 + 0] = colors[target * 4 + 1] = topColor; colors[target * 4 + 2] = colors[target * 4 + 3] = bottomColor; if (chr.flipped) { uvs[target * 4 + 0] = new Vector2(chr.uv1.x, chr.uv1.y); uvs[target * 4 + 1] = new Vector2(chr.uv1.x, chr.uv0.y); uvs[target * 4 + 2] = new Vector2(chr.uv0.x, chr.uv1.y); uvs[target * 4 + 3] = new Vector2(chr.uv0.x, chr.uv0.y); } else { uvs[target * 4 + 0] = new Vector2(chr.uv0.x, chr.uv0.y); uvs[target * 4 + 1] = new Vector2(chr.uv1.x, chr.uv0.y); uvs[target * 4 + 2] = new Vector2(chr.uv0.x, chr.uv1.y); uvs[target * 4 + 3] = new Vector2(chr.uv1.x, chr.uv1.y); } if (_font.textureGradients) { uv2[target * 4 + 0] = gradientOffset + chr.gradientUv[0]; uv2[target * 4 + 1] = gradientOffset + chr.gradientUv[1]; uv2[target * 4 + 2] = gradientOffset + chr.gradientUv[2]; uv2[target * 4 + 3] = gradientOffset + chr.gradientUv[3]; } cursorX += (chr.advance + spacing) * _scale.x; if (_kerning && i < _text.Length - 1) { foreach (var k in _font.kerning) { if (k.c0 == _text[i] && k.c1 == _text[i + 1]) { cursorX += k.amount * _scale.x; break; } } } ++target; } if (alignStartTarget != target) { float lineWidth = cursorX; int alignEndTarget = target; float xOffset = GetXAnchorForWidth(lineWidth); PostAlignTextData(alignStartTarget, alignEndTarget, xOffset); } return(target); }
public static void SetTextMeshGeom(this GeomData geomData, Vector3[] pos, Vector2[] uv, Vector2[] uv2, Color32[] color, int offset) { tk2dTextMeshData data = geomData.TextMeshData; tk2dFontData fontInst = data.FontInst; tk2dColoredText formattedText = data.FormattedText; InlineStyler curStyler = new InlineStyler(); curStyler.meshTopColor = new Color32(255, 255, 255, 255); curStyler.meshBottomColor = new Color32(255, 255, 255, 255); curStyler.meshGradientTexU = (float)data.textureGradient / (float)((fontInst.gradientCount > 0) ? fontInst.gradientCount : 1); curStyler.curGradientCount = fontInst.gradientCount; Vector2 dims = data.GetMeshDimensionsForString(formattedText); float offsetY = data.GetYAnchorForHeight(dims.y); float cursorX = 0.0f; float cursorY = 0.0f; // target is required due to invisible '\n' character int target = 0; int alignStartTarget = 0; for (int i = 0; i < formattedText.Length && target < geomData.CurrentAllocatedCharacters; ++i) { formattedText.ApplyColorCommand(curStyler, i); int idx = formattedText[i]; tk2dFontChar chr = fontInst.GetCharForIndex(idx, 0); if (idx == '\n') { float lineWidth = cursorX; int alignEndTarget = target; // this is one after the last filled character if (alignStartTarget != target) { float xOffset = data.GetXAnchorForWidth(lineWidth); PostAlignTextData(pos, offset, alignStartTarget, alignEndTarget, xOffset); } alignStartTarget = target; cursorX = 0.0f; cursorY -= data.ActualLineSpaceHeight; } else { pos[offset + target * 4 + 0] = new Vector3(cursorX + chr.p0.x * data.TotalScale.x, offsetY + cursorY + chr.p0.y * data.TotalScale.y, 0); pos[offset + target * 4 + 1] = new Vector3(cursorX + chr.p1.x * data.TotalScale.x, offsetY + cursorY + chr.p0.y * data.TotalScale.y, 0); pos[offset + target * 4 + 2] = new Vector3(cursorX + chr.p0.x * data.TotalScale.x, offsetY + cursorY + chr.p1.y * data.TotalScale.y, 0); pos[offset + target * 4 + 3] = new Vector3(cursorX + chr.p1.x * data.TotalScale.x, offsetY + cursorY + chr.p1.y * data.TotalScale.y, 0); if (chr.flipped) { uv[offset + target * 4 + 0] = new Vector2(chr.uv1.x, chr.uv1.y); uv[offset + target * 4 + 1] = new Vector2(chr.uv1.x, chr.uv0.y); uv[offset + target * 4 + 2] = new Vector2(chr.uv0.x, chr.uv1.y); uv[offset + target * 4 + 3] = new Vector2(chr.uv0.x, chr.uv0.y); } else { uv[offset + target * 4 + 0] = new Vector2(chr.uv0.x, chr.uv0.y); uv[offset + target * 4 + 1] = new Vector2(chr.uv1.x, chr.uv0.y); uv[offset + target * 4 + 2] = new Vector2(chr.uv0.x, chr.uv1.y); uv[offset + target * 4 + 3] = new Vector2(chr.uv1.x, chr.uv1.y); } if (fontInst.textureGradients) { uv2[offset + target * 4 + 0] = chr.gradientUv[0] + new Vector2(curStyler.meshGradientTexU, 0); uv2[offset + target * 4 + 1] = chr.gradientUv[1] + new Vector2(curStyler.meshGradientTexU, 0); uv2[offset + target * 4 + 2] = chr.gradientUv[2] + new Vector2(curStyler.meshGradientTexU, 0); uv2[offset + target * 4 + 3] = chr.gradientUv[3] + new Vector2(curStyler.meshGradientTexU, 0); } if (fontInst.isPacked) { Color32 c = channelSelectColors[chr.channel]; color[offset + target * 4 + 0] = c; color[offset + target * 4 + 1] = c; color[offset + target * 4 + 2] = c; color[offset + target * 4 + 3] = c; } else { color[offset + target * 4 + 0] = curStyler.meshTopColor; color[offset + target * 4 + 1] = curStyler.meshTopColor; color[offset + target * 4 + 2] = curStyler.meshBottomColor; color[offset + target * 4 + 3] = curStyler.meshBottomColor; } cursorX += (chr.advance + data.Spacing) * data.TotalScale.x; if (data.kerning && i < formattedText.Length - 1) { foreach (var k in fontInst.kerning) { if (k.c0 == formattedText[i] && k.c1 == formattedText[i + 1]) { cursorX += k.amount * data.TotalScale.x; break; } } } target++; } } if (alignStartTarget != target) { float lineWidth = cursorX; int alignEndTarget = target; float xOffset = data.GetXAnchorForWidth(lineWidth); PostAlignTextData(pos, offset, alignStartTarget, alignEndTarget, xOffset); } for (int i = target; i < geomData.CurrentAllocatedCharacters; ++i) { pos[offset + i * 4 + 0] = pos[offset + i * 4 + 1] = pos[offset + i * 4 + 2] = pos[offset + i * 4 + 3] = Vector3.zero; uv[offset + i * 4 + 0] = uv[offset + i * 4 + 1] = uv[offset + i * 4 + 2] = uv[offset + i * 4 + 3] = Vector2.zero; if (fontInst.textureGradients) { uv2[offset + i * 4 + 0] = uv2[offset + i * 4 + 1] = uv2[offset + i * 4 + 2] = uv2[offset + i * 4 + 3] = Vector2.zero; } color[offset + i * 4 + 0] = color[offset + i * 4 + 1] = color[offset + i * 4 + 2] = color[offset + i * 4 + 3] = Color.clear; } }
bool ParseBMFont(string path, tk2dFontData bmFont, tk2dFont source) { IntFontInfo fontInfo = null; try { XmlDocument doc = new XmlDocument(); doc.Load(path); fontInfo = ParseBMFontXml(doc); } catch { fontInfo = ParseBMFontText(path); } if (fontInfo == null || fontInfo.chars.Count == 0) return false; float texWidth = fontInfo.scaleW; float texHeight = fontInfo.scaleH; float lineHeight = fontInfo.lineHeight; float scale = 2.0f * source.targetOrthoSize / source.targetHeight; bmFont.lineHeight = lineHeight * scale; // Get largest index int numCharacters = 0; foreach (var theChar in fontInfo.chars) { if (theChar.id > numCharacters) numCharacters = theChar.id; } tk2dFontChar[] chars = new tk2dFontChar[numCharacters]; int minChar = 65536; int maxCharWithinBounds = 0; int numLocalChars = 0; float largestWidth = 0.0f; foreach (var theChar in fontInfo.chars) { tk2dFontChar thisChar = new tk2dFontChar(); int id = theChar.id; int x = theChar.x; int y = theChar.y; int width = theChar.width; int height = theChar.height; int xoffset = theChar.xoffset; int yoffset = theChar.yoffset; int xadvance = theChar.xadvance + source.charPadX; // precompute required data float px = xoffset * scale; float py = (lineHeight - yoffset) * scale; thisChar.p0 = new Vector3(px, py, 0); thisChar.p1 = new Vector3(px + width * scale, py - height * scale, 0); if (source.flipTextureY) { thisChar.uv0 = new Vector2(x / texWidth, y / texHeight); thisChar.uv1 = new Vector2(thisChar.uv0.x + width / texWidth, thisChar.uv0.y + height / texHeight); } else { thisChar.uv0 = new Vector2(x / texWidth, 1.0f - y / texHeight); thisChar.uv1 = new Vector2(thisChar.uv0.x + width / texWidth, thisChar.uv0.y - height / texHeight); } thisChar.advance = xadvance * scale; largestWidth = Mathf.Max(thisChar.advance, largestWidth); // Needs gradient data if (source.gradientTexture != null) { // build it up assuming the first gradient float x0 = (float)(0.0f / source.gradientCount); float x1 = (float)(1.0f / source.gradientCount); float y0 = 1.0f; float y1 = 0.0f; // align to glyph if necessary thisChar.gradientUv = new Vector2[4]; thisChar.gradientUv[0] = new Vector2(x0, y0); thisChar.gradientUv[1] = new Vector2(x1, y0); thisChar.gradientUv[2] = new Vector2(x0, y1); thisChar.gradientUv[3] = new Vector2(x1, y1); } if (id < numCharacters) { maxCharWithinBounds = (id > maxCharWithinBounds) ? id : maxCharWithinBounds; minChar = (id < minChar) ? id : minChar; chars[id] = thisChar; ++numLocalChars; } } if (source.dupeCaps) { for (int uc = 'A'; uc <= 'Z'; ++uc) { int lc = uc + ('a' - 'A'); if (chars[lc] == null) chars[lc] = chars[uc]; else if (chars[uc] == null) chars[uc] = chars[lc]; } } // share null char, same pointer var nullChar = new tk2dFontChar(); bmFont.largestWidth = largestWidth; bmFont.chars = new tk2dFontChar[numCharacters]; for (int i = 0; i < numCharacters; ++i) { bmFont.chars[i] = chars[i]; if (bmFont.chars[i] == null) { bmFont.chars[i] = nullChar; // zero everything, null char } } // kerning bmFont.kerning = new tk2dFontKerning[fontInfo.kernings.Count]; for (int i = 0; i < bmFont.kerning.Length; ++i) { tk2dFontKerning kerning = new tk2dFontKerning(); kerning.c0 = fontInfo.kernings[i].first; kerning.c1 = fontInfo.kernings[i].second; kerning.amount = fontInfo.kernings[i].amount * scale; bmFont.kerning[i] = kerning; } return true; }
public void Init() { if (_font && (updateFlags & UpdateFlags.UpdateBuffers) != 0) { MeshFilter meshFilter = GetComponent <MeshFilter>(); Mesh newMesh = new Mesh(); Color topColor = _color; Color bottomColor = _useGradient?_color2:_color; // volatile data vertices = new Vector3[_maxChars * 4]; uvs = new Vector2[_maxChars * 4]; colors = new Color[_maxChars * 4]; int[] triangles = new int[_maxChars * 6]; float offsetX, offsetY; CalcAnchor(out offsetX, out offsetY); float cursorX = 0.0f; float cursorY = 0.0f; for (int i = 0; i < _maxChars; ++i) { if (i < _text.Length) { int idx = _text[i]; if (idx >= _font.chars.Length) { idx = 0; // should be space } tk2dFontChar chr = _font.chars[idx]; if (idx == '\n') { cursorX = 0.0f; cursorY -= _font.lineHeight * _scale.y; continue; } vertices[i * 4 + 0] = new Vector3(offsetX + cursorX + chr.p0.x * _scale.x, offsetY + cursorY + chr.p0.y * _scale.y, 0); vertices[i * 4 + 1] = new Vector3(offsetX + cursorX + chr.p1.x * _scale.x, offsetY + cursorY + chr.p0.y * _scale.y, 0); vertices[i * 4 + 2] = new Vector3(offsetX + cursorX + chr.p0.x * _scale.x, offsetY + cursorY + chr.p1.y * _scale.y, 0); vertices[i * 4 + 3] = new Vector3(offsetX + cursorX + chr.p1.x * _scale.x, offsetY + cursorY + chr.p1.y * _scale.y, 0); uvs[i * 4 + 0] = new Vector2(chr.uv0.x, chr.uv0.y); uvs[i * 4 + 1] = new Vector2(chr.uv1.x, chr.uv0.y); uvs[i * 4 + 2] = new Vector2(chr.uv0.x, chr.uv1.y); uvs[i * 4 + 3] = new Vector2(chr.uv1.x, chr.uv1.y); cursorX += chr.advance * _scale.x; } else { vertices[i * 4 + 0] = vertices[i * 4 + 1] = vertices[i * 4 + 2] = vertices[i * 4 + 3] = Vector3.zero; uvs[i * 4 + 0] = uvs[i * 4 + 1] = uvs[i * 4 + 2] = uvs[i * 4 + 3] = Vector2.zero; } colors[i * 4 + 0] = colors[i * 4 + 1] = topColor; colors[i * 4 + 2] = colors[i * 4 + 3] = bottomColor; triangles[i * 6 + 0] = i * 4 + 0; triangles[i * 6 + 1] = i * 4 + 1; triangles[i * 6 + 2] = i * 4 + 3; triangles[i * 6 + 3] = i * 4 + 2; triangles[i * 6 + 4] = i * 4 + 0; triangles[i * 6 + 5] = i * 4 + 3; if (_kerning && i < _text.Length - 1) { foreach (var k in _font.kerning) { if (k.c0 == _text[i] && k.c1 == _text[i + 1]) { cursorX += k.amount * _scale.x; break; } } } } newMesh.vertices = vertices; newMesh.uv = uvs; newMesh.triangles = triangles; newMesh.colors = colors; newMesh.RecalculateBounds(); meshFilter.mesh = newMesh; mesh = meshFilter.sharedMesh; updateFlags = UpdateFlags.UpdateNone; } }
public void Commit() { // Can come in here without anything initalized when // instantiated in code if ((updateFlags & UpdateFlags.UpdateBuffers) != 0) { Init(); } else { if ((updateFlags & UpdateFlags.UpdateText) != 0) { float cursorX = 0.0f; float cursorY = 0.0f; float offsetX, offsetY; CalcAnchor(out offsetX, out offsetY); for (int i = 0; i < _maxChars; ++i) { if (i < _text.Length) { int idx = _text[i]; if (idx >= _font.chars.Length) { idx = 0; // should be space } tk2dFontChar chr = _font.chars[idx]; if (idx == '\n') { cursorX = 0.0f; cursorY -= _font.lineHeight * _scale.y; continue; } vertices[i * 4 + 0] = new Vector3(offsetX + cursorX + chr.p0.x * _scale.x, offsetY + cursorY + chr.p0.y * _scale.y, 0); vertices[i * 4 + 1] = new Vector3(offsetX + cursorX + chr.p1.x * _scale.x, offsetY + cursorY + chr.p0.y * _scale.y, 0); vertices[i * 4 + 2] = new Vector3(offsetX + cursorX + chr.p0.x * _scale.x, offsetY + cursorY + chr.p1.y * _scale.y, 0); vertices[i * 4 + 3] = new Vector3(offsetX + cursorX + chr.p1.x * _scale.x, offsetY + cursorY + chr.p1.y * _scale.y, 0); uvs[i * 4 + 0] = new Vector2(chr.uv0.x, chr.uv0.y); uvs[i * 4 + 1] = new Vector2(chr.uv1.x, chr.uv0.y); uvs[i * 4 + 2] = new Vector2(chr.uv0.x, chr.uv1.y); uvs[i * 4 + 3] = new Vector2(chr.uv1.x, chr.uv1.y); cursorX += chr.advance * _scale.x; if (_kerning && i < _text.Length - 1) { foreach (var k in _font.kerning) { if (k.c0 == _text[i] && k.c1 == _text[i + 1]) { cursorX += k.amount * _scale.x; break; } } } } else { vertices[i * 4 + 0] = vertices[i * 4 + 1] = vertices[i * 4 + 2] = vertices[i * 4 + 3] = Vector3.zero; uvs[i * 4 + 0] = uvs[i * 4 + 1] = uvs[i * 4 + 2] = uvs[i * 4 + 3] = Vector2.zero; } } mesh.vertices = vertices; mesh.uv = uvs; // comment this in for game if it becomes a problem #if UNITY_EDITOR mesh.RecalculateBounds(); #endif } if ((updateFlags & UpdateFlags.UpdateColors) != 0) { Color topColor = _color; Color bottomColor = _useGradient ? _color2 : _color; for (int i = 0; i < colors.Length; i += 4) { colors[i + 0] = colors[i + 1] = topColor; colors[i + 2] = colors[i + 3] = bottomColor; } mesh.colors = colors; } } updateFlags = UpdateFlags.UpdateNone; }