Show More
Commit Description:
Factor out some details into Simulation.
Commit Description:
Factor out some details into Simulation.
References:
File last commit:
Show/Diff file:
Action:
SpriteFontPlus/src/FontStashSharp/FontAtlas.cs
216 lines | 4.1 KiB | text/x-csharp | CSharpLexer
216 lines | 4.1 KiB | text/x-csharp | CSharpLexer
r0 | using Microsoft.Xna.Framework; | ||
using Microsoft.Xna.Framework.Graphics; | |||
using System; | |||
namespace FontStashSharp | |||
{ | |||
internal class FontAtlas | |||
{ | |||
public int Width | |||
{ | |||
get; private set; | |||
} | |||
public int Height | |||
{ | |||
get; private set; | |||
} | |||
public int NodesNumber | |||
{ | |||
get; private set; | |||
} | |||
public FontAtlasNode[] Nodes | |||
{ | |||
get; private set; | |||
} | |||
public Texture2D Texture | |||
{ | |||
get; set; | |||
} | |||
public FontAtlas(int w, int h, int count) | |||
{ | |||
Width = w; | |||
Height = h; | |||
Nodes = new FontAtlasNode[count]; | |||
count = 0; | |||
Nodes[0].X = 0; | |||
Nodes[0].Y = 0; | |||
Nodes[0].Width = w; | |||
NodesNumber++; | |||
} | |||
public void InsertNode(int idx, int x, int y, int w) | |||
{ | |||
if (NodesNumber + 1 > Nodes.Length) | |||
{ | |||
var oldNodes = Nodes; | |||
var newLength = Nodes.Length == 0 ? 8 : Nodes.Length * 2; | |||
Nodes = new FontAtlasNode[newLength]; | |||
for (var i = 0; i < oldNodes.Length; ++i) | |||
{ | |||
Nodes[i] = oldNodes[i]; | |||
} | |||
} | |||
for (var i = NodesNumber; i > idx; i--) | |||
Nodes[i] = Nodes[i - 1]; | |||
Nodes[idx].X = x; | |||
Nodes[idx].Y = y; | |||
Nodes[idx].Width = w; | |||
NodesNumber++; | |||
} | |||
public void RemoveNode(int idx) | |||
{ | |||
if (NodesNumber == 0) | |||
return; | |||
for (var i = idx; i < NodesNumber - 1; i++) | |||
Nodes[i] = Nodes[i + 1]; | |||
NodesNumber--; | |||
} | |||
public void Expand(int w, int h) | |||
{ | |||
if (w > Width) | |||
InsertNode(NodesNumber, Width, 0, w - Width); | |||
Width = w; | |||
Height = h; | |||
} | |||
public void Reset(int w, int h) | |||
{ | |||
Width = w; | |||
Height = h; | |||
NodesNumber = 0; | |||
Nodes[0].X = 0; | |||
Nodes[0].Y = 0; | |||
Nodes[0].Width = w; | |||
NodesNumber++; | |||
} | |||
public bool AddSkylineLevel(int idx, int x, int y, int w, int h) | |||
{ | |||
InsertNode(idx, x, y + h, w); | |||
for (var i = idx + 1; i < NodesNumber; i++) | |||
if (Nodes[i].X < Nodes[i - 1].X + Nodes[i - 1].Width) | |||
{ | |||
var shrink = Nodes[i - 1].X + Nodes[i - 1].Width - Nodes[i].X; | |||
Nodes[i].X += shrink; | |||
Nodes[i].Width -= shrink; | |||
if (Nodes[i].Width <= 0) | |||
{ | |||
RemoveNode(i); | |||
i--; | |||
} | |||
else | |||
{ | |||
break; | |||
} | |||
} | |||
else | |||
{ | |||
break; | |||
} | |||
for (var i = 0; i < NodesNumber - 1; i++) | |||
if (Nodes[i].Y == Nodes[i + 1].Y) | |||
{ | |||
Nodes[i].Width += Nodes[i + 1].Width; | |||
RemoveNode(i + 1); | |||
i--; | |||
} | |||
return true; | |||
} | |||
public int RectFits(int i, int w, int h) | |||
{ | |||
var x = Nodes[i].X; | |||
var y = Nodes[i].Y; | |||
if (x + w > Width) | |||
return -1; | |||
var spaceLeft = w; | |||
while (spaceLeft > 0) | |||
{ | |||
if (i == NodesNumber) | |||
return -1; | |||
y = Math.Max(y, Nodes[i].Y); | |||
if (y + h > Height) | |||
return -1; | |||
spaceLeft -= Nodes[i].Width; | |||
++i; | |||
} | |||
return y; | |||
} | |||
public bool AddRect(int rw, int rh, ref int rx, ref int ry) | |||
{ | |||
var besth = Height; | |||
var bestw = Width; | |||
var besti = -1; | |||
var bestx = -1; | |||
var besty = -1; | |||
for (var i = 0; i < NodesNumber; i++) | |||
{ | |||
var y = RectFits(i, rw, rh); | |||
if (y != -1) | |||
if (y + rh < besth || y + rh == besth && Nodes[i].Width < bestw) | |||
{ | |||
besti = i; | |||
bestw = Nodes[i].Width; | |||
besth = y + rh; | |||
bestx = Nodes[i].X; | |||
besty = y; | |||
} | |||
} | |||
if (besti == -1) | |||
return false; | |||
if (!AddSkylineLevel(besti, bestx, besty, rw, rh)) | |||
return false; | |||
rx = bestx; | |||
ry = besty; | |||
return true; | |||
} | |||
public void RenderGlyph(GraphicsDevice device, FontGlyph glyph) | |||
{ | |||
if (glyph.Bounds.Width == 0 || glyph.Bounds.Height == 0) | |||
{ | |||
return; | |||
} | |||
// Render glyph to byte buffer | |||
var buffer = new byte[glyph.Bounds.Width * glyph.Bounds.Height]; | |||
Array.Clear(buffer, 0, buffer.Length); | |||
var g = glyph.Index; | |||
glyph.Font.RenderGlyphBitmap(buffer, | |||
glyph.Bounds.Width, | |||
glyph.Bounds.Height, | |||
glyph.Bounds.Width, | |||
g); | |||
// Byte buffer to RGBA | |||
var colorBuffer = new Color[glyph.Bounds.Width * glyph.Bounds.Height]; | |||
for (var i = 0; i < colorBuffer.Length; ++i) | |||
{ | |||
var c = buffer[i]; | |||
colorBuffer[i].R = colorBuffer[i].G = colorBuffer[i].B = colorBuffer[i].A = c; | |||
} | |||
// Write to texture | |||
if (Texture == null) | |||
{ | |||
Texture = new Texture2D(device, Width, Height); | |||
} | |||
Texture.SetData(0, glyph.Bounds, colorBuffer, 0, colorBuffer.Length); | |||
} | |||
} | |||
} |