Skip to content

Instantly share code, notes, and snippets.

@BoarK
Forked from madgreg99/FDFontLabel
Created April 4, 2014 06:24
Show Gist options
  • Save BoarK/9969200 to your computer and use it in GitHub Desktop.
Save BoarK/9969200 to your computer and use it in GitHub Desktop.
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
//parts of this were inspired by https://github.com/prime31/UIToolkit/blob/master/Assets/Plugins/UIToolkit/UIElements/UIText.cs
public class FDFontLabel : FLabel
{
protected int dynamicFontSize = 32;
protected FontStyle dynamicFontStyle = FontStyle.Normal;
public FDFontLabel (string fontName, string text, int fontSize)
: this(fontName, text, fontSize, new FTextParams())
{
}
public FDFontLabel (string fontName, string text, float fontSize)
: this(fontName, text, (int)fontSize, new FTextParams())
{
}
public FDFontLabel (string fontName, string text, float fontSize, FTextParams textParams)
: this(fontName, text, (int)fontSize, textParams)
{
}
public FDFontLabel (string fontName, string text, int fontSize, FTextParams textParams)
{
dynamicFontSize = fontSize;
_fontName = fontName;
_text = text;
_textParams = textParams;
FDynamicFont font = FDynamicFont.dynamicsFonts[fontName];
_font = font;
font.dynamicFont.RequestCharactersInTexture(text, dynamicFontSize, dynamicFontStyle);
this.shader = FShader.AdditiveColor;
Init(FFacetType.Quad, _font.element, 0);
CreateTextQuads();
font.labels.Add(this);
}
~FDFontLabel()
{
FDynamicFont font = _font as FDynamicFont;
font.labels.Remove(this);
}
override public void HandleAddedToStage()
{
base.HandleAddedToStage();
CreateTextQuads(); //lazily creating the quads was causing too many issues, so just create them when .text is set
FDynamicFont font = _font as FDynamicFont;
}
override public void HandleRemovedFromStage()
{
base.HandleRemovedFromStage();
_doesTextNeedUpdate = true;
_isMeshDirty = true;
FDynamicFont font = _font as FDynamicFont;
//font.labels.Remove(this);
}
public void MarkHasChanged()
{
_doesTextNeedUpdate = true;
_isMeshDirty = true;
FDynamicFont font = _font as FDynamicFont;
font.dynamicFont.RequestCharactersInTexture(_text, dynamicFontSize, dynamicFontStyle);
CreateTextQuads(); //lazily creating the quads was causing too many issues, so just create them when .text is set
}
override public string text
{
get
{
return _text;
}
set
{
if(_text != value)
{
_text = value;
FDynamicFont font = _font as FDynamicFont;
font.dynamicFont.RequestCharactersInTexture(_text, dynamicFontSize, dynamicFontStyle);
_doesTextNeedUpdate = true;
CreateTextQuads(); //lazily creating the quads was causing too many issues, so just create them when .text is set
}
}
}
public FontStyle Style
{
get
{
return dynamicFontStyle;
}
set
{
dynamicFontStyle = value;
MarkHasChanged();
}
}
override public void CreateTextQuads()
{
_doesTextNeedUpdate = false;
int oldFacetsNeeded = _numberOfFacetsNeeded;
FDynamicFont font = _font as FDynamicFont;
_letterQuadLines = font.GetQuadInfoForText(_text, dynamicFontSize, dynamicFontStyle, _textParams);
_numberOfFacetsNeeded = 0;
int lineCount = _letterQuadLines.Length;
for(int i = 0; i< lineCount; i++)
{
_numberOfFacetsNeeded += _letterQuadLines[i].quads.Length;
}
if(_isOnStage)
{
int delta = _numberOfFacetsNeeded - oldFacetsNeeded;
if(delta != 0) //if the number of letter quads has changed, tell the stage
{
_stage.HandleFacetsChanged();
}
}
UpdateLocalPosition(); //figures out the bounds and alignment, and sets the mesh dirty
}
public int FontSize
{
get
{
return dynamicFontSize;
}
/* // not currently working
set
{
dynamicFontSize = value;
CreateTextQuads();
_doesLocalPositionNeedUpdate = true;
}
*/
}
}
public class FDynamicFont : FFont
{
public Font dynamicFont;
public List<FDFontLabel> labels = new List<FDFontLabel>();
public static Dictionary<string, FDynamicFont> dynamicsFonts = new Dictionary<string, FDynamicFont>();
public static void LoadDynamicFont(string fontName)
{
FDynamicFont font;
if (!dynamicsFonts.TryGetValue(fontName, out font))
{
Font dynamicFont = Resources.Load<Font>(fontName);
Futile.atlasManager.LoadAtlasFromTexture(fontName, dynamicFont.material.mainTexture);
FAtlasElement element = Futile.atlasManager.GetElementWithName(fontName);
font = new FDynamicFont(fontName, element);
font.dynamicFont = dynamicFont;
dynamicsFonts.Add(fontName, font);
dynamicFont.material.mainTexture.filterMode = FilterMode.Point;
font.dynamicFont.textureRebuildCallback += () => { font.OnFontRebuilt(); };
}
}
public FDynamicFont(string fontname, FAtlasElement element)
: base(fontname, element)
{
}
void OnFontRebuilt()
{
foreach(FDFontLabel label in labels)
{
label.MarkHasChanged();
}
}
public FLetterQuadLine[] GetQuadInfoForText(string text, int dynamicFontSize, FontStyle dynamicFontStyle, FTextParams labelTextParams)
{
int lineCount = 0;
int letterCount = 0;
char[] letters = text.ToCharArray();
//at some point these should probably be pooled and reused so we're not allocing new ones all the time
//now they're structs though, so it might not be an issue
FLetterQuadLine[] lines = new FLetterQuadLine[10];
int lettersLength = letters.Length;
for(int c = 0; c<lettersLength; ++c)
{
char letter = letters[c];
if(letter == FFont.ASCII_NEWLINE)
{
lines[lineCount] = new FLetterQuadLine();
lines[lineCount].letterCount = letterCount;
lines[lineCount].quads = new FLetterQuad[letterCount];
lineCount++;
letterCount = 0;
}
else
{
letterCount++;
}
}
lines[lineCount] = new FLetterQuadLine();
lines[lineCount].letterCount = letterCount;
lines[lineCount].quads = new FLetterQuad[letterCount];
FLetterQuadLine[] oldLines = lines;
lines = new FLetterQuadLine[lineCount+1];
for(int c = 0; c<lineCount+1; ++c)
{
lines[c] = oldLines[c];
}
lineCount = 0;
letterCount = 0;
float nextX = 0;
float nextY = 0;
float minX = float.MaxValue;
float maxX = float.MinValue;
float minY = float.MaxValue;
float maxY = float.MinValue;
float usableLineHeight = dynamicFontSize + labelTextParams.scaledLineHeightOffset + labelTextParams.scaledLineHeightOffset;
for(int c = 0; c<lettersLength; ++c)
{
char letter = letters[c];
if(letter == FFont.ASCII_NEWLINE)
{
if(letterCount == 0)
{
lines[lineCount].bounds = new Rect(0,0,nextY,nextY - usableLineHeight);
}
else
{
lines[lineCount].bounds = new Rect(minX,minY,maxX-minX,maxY-minY);
}
minX = float.MaxValue;
maxX = float.MinValue;
minY = float.MaxValue;
maxY = float.MinValue;
nextX = 0;
nextY -= usableLineHeight;
lineCount++;
letterCount = 0;
}
else
{
//TODO: Reuse letterquads with pooling!
FLetterQuad letterQuad = new FLetterQuad();
//v0 v1 are the two corners
CharacterInfo charInfo;
if (!dynamicFont.GetCharacterInfo(letter, out charInfo, dynamicFontSize, dynamicFontStyle))
{
//blank, character (could consider using the "char not found square")
dynamicFont.GetCharacterInfo(' ', out charInfo, dynamicFontSize, dynamicFontStyle);
}
float totalKern = labelTextParams.scaledKerningOffset + labelTextParams.scaledKerningOffset;
/* if(letterCount == 0)
{
nextX = -charInfo.offsetX; //don't offset the first character
}
else*/
{
nextX += totalKern;
}
letterQuad.charInfo = new FCharInfo();
if (charInfo.flipped)
{
letterQuad.charInfo.uvBottomRight = new Vector2(charInfo.uv.xMin, charInfo.uv.yMax);
letterQuad.charInfo.uvTopRight = new Vector2(charInfo.uv.xMax, charInfo.uv.yMax);
letterQuad.charInfo.uvTopLeft = new Vector2(charInfo.uv.xMax, charInfo.uv.yMin);
letterQuad.charInfo.uvBottomLeft = new Vector2(charInfo.uv.xMin, charInfo.uv.yMin);
}
else
{
letterQuad.charInfo.uvTopLeft = new Vector2(charInfo.uv.xMin, charInfo.uv.yMax);
letterQuad.charInfo.uvTopRight = new Vector2(charInfo.uv.xMax, charInfo.uv.yMax);
letterQuad.charInfo.uvBottomRight = new Vector2(charInfo.uv.xMax, charInfo.uv.yMin);
letterQuad.charInfo.uvBottomLeft = new Vector2(charInfo.uv.xMin, charInfo.uv.yMin);
}
float m = 0.8203215f;
float n = 1.0f - m;
float offsetY = (int)(m * dynamicFontSize) + n;
Rect quadRect = new Rect(nextX + charInfo.vert.x, nextY + charInfo.vert.yMax - offsetY, Mathf.Abs(charInfo.vert.width), Mathf.Abs(charInfo.vert.height));
letterQuad.rect = quadRect;
lines[lineCount].quads[letterCount] = letterQuad;
minX = Math.Min (minX, quadRect.xMin);
maxX = Math.Max (maxX, quadRect.xMax);
minY = Math.Min (minY, nextY - usableLineHeight);
maxY = Math.Max (maxY, nextY);
nextX += charInfo.width;
letterCount++;
}
}
if(letterCount == 0) //there were no letters, so minX and minY would be crazy if we used them
{
lines[lineCount].bounds = new Rect(0,0,nextY,nextY - usableLineHeight);
}
else
{
lines[lineCount].bounds = new Rect(minX,minY,maxX-minX,maxY-minY);
}
return lines;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment