#region CPL License
/*
Nuclex Framework
Copyright (C) 2002-2008 Nuclex Development Labs
This library is free software; you can redistribute it and/or
modify it under the terms of the IBM Common Public License as
published by the IBM Corporation; either version 1.0 of the
License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
IBM Common Public License for more details.
You should have received a copy of the IBM Common Public
License along with this library
*/
#endregion
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Nuclex.Fonts {
/// Stores the vertices for an outlined string
public class OutlinedText : Text {
/// Initializes a new outlined string
/// Font from which the vertices will be taken
/// String of which to build a mesh
internal OutlinedText(VectorFont font, string text) {
resizeVertexAndIndexArrays(font, text);
buildStringOutline(font, text);
}
/// Builds the combined outline for the letters in this string
/// Vector font to take the vertex data from
/// String of which a mesh is to be built
private void buildStringOutline(VectorFont font, string text) {
Vector2 position = Vector2.Zero;
int baseVertexIndex = 0; // base index in the vertex array
int baseIndexIndex = 0; // base index in the index array
for (int characterIndex = 0; characterIndex < text.Length; ++characterIndex) {
int fontCharacterIndex;
// Only add this character to the mesh if there is an actual font character
// for this unicode symbol in the font (characters not imported by the user
// will be silently skipped -- this is the only sane option imho)
if (font.CharacterMap.TryGetValue(text[characterIndex], out fontCharacterIndex)) {
VectorFontCharacter character = font.Characters[fontCharacterIndex];
if (character.Outlines.Count > 0) {
VectorFontCharacter.Outline finalOutline =
character.Outlines[character.Outlines.Count - 1];
// We calculate the vertex count from the outline instead of just taking
// the size of the vertex array because we might not need all vertices to
// render an outline (some have been added by the tessellation processor and
// are only used as supporting vertices to fill the faces)
int outlineVertexCount = finalOutline.StartVertexIndex + finalOutline.VertexCount;
// Import all of the vertices of this font (we need both the outline vertices
// as well as the mesh supporting vertices)
for (int index = 0; index < outlineVertexCount; ++index) {
this.vertices[baseVertexIndex + index] = new VertexPositionNormalTexture(
new Vector3(character.Vertices[index] + position, 0.0f),
Vector3.Forward, Vector2.Zero
);
}
// Transform the outline index ranges into single big line list pointing
// to the vertices we just imported from the font
for (int index = 0; index < character.Outlines.Count; ++index) {
VectorFontCharacter.Outline outline = character.Outlines[index];
// Set up the indices for the outline exluding the final connection from
// the outline's end to its start
for (int lineIndex = 0; lineIndex < outline.VertexCount - 1; ++lineIndex) {
this.indices[baseIndexIndex + lineIndex * 2 + 0] =
(short)(baseVertexIndex + outline.StartVertexIndex + lineIndex);
this.indices[baseIndexIndex + lineIndex * 2 + 1] =
(short)(baseVertexIndex + outline.StartVertexIndex + lineIndex + 1);
}
// Add the connection from the end of the outline to its start
this.indices[baseIndexIndex + outline.VertexCount * 2 - 2] =
(short)(baseVertexIndex + outline.StartVertexIndex + outline.VertexCount - 1);
this.indices[baseIndexIndex + outline.VertexCount * 2 - 1] =
(short)(baseVertexIndex + outline.StartVertexIndex);
// Advance the index pointer for the next run
baseIndexIndex += outline.VertexCount * 2;
}
// Adjust the base vertex index for the next character
baseVertexIndex += outlineVertexCount;
}
// Advance to the next character
position += character.Advancement;
} // if
} // for
this.width = position.X;
this.height = font.LineHeight;
this.primitiveType = PrimitiveType.LineList;
}
/// Reserves the required space in the vertex and index arrays
/// Font the vertices for the letters will be taken from
/// String of which a mesh will be built
private void resizeVertexAndIndexArrays(VectorFont font, string text) {
int vertexCount = 0;
int indexCount = 0;
// Count the vertices and indices requires for all characters in the string
for (int index = 0; index < text.Length; ++index) {
int fontCharacterIndex;
// Try to find the current character in the font's character map. If it isn't
// there, we'll ignore it, just like the vertex creation routine does.
if (font.CharacterMap.TryGetValue(text[index], out fontCharacterIndex)) {
VectorFontCharacter character = font.Characters[fontCharacterIndex];
// There may be empty characters (characters without a visual representation
// in the font, so we need to check this before accessing the outline array
if (character.Outlines.Count > 0) {
VectorFontCharacter.Outline finalOutline =
character.Outlines[character.Outlines.Count - 1];
// We calculate the vertex count from the outline instead of just taking
// the size of the vertex array because we might not need all vertices to
// render an outline (some have been added by the tessellation processor and
// are only used as supporting vertices to fill the faces)
int outlineVertexCount = finalOutline.StartVertexIndex + finalOutline.VertexCount;
// Advance the counters
vertexCount += outlineVertexCount;
indexCount += outlineVertexCount * 2;
}
}
}
this.vertices = new VertexPositionNormalTexture[vertexCount];
this.indices = new short[indexCount];
}
}
} // namespace Nuclex.Fonts