(ded4a3e0a) v0.9.0.7
This commit is contained in:
+49
@@ -0,0 +1,49 @@
|
||||
// MonoGame - Copyright (C) The MonoGame Team
|
||||
// This file is subject to the terms and conditions defined in
|
||||
// file 'LICENSE.txt', which is part of this source code package.
|
||||
|
||||
using System;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
// Assorted helpers for doing useful things with bitmaps.
|
||||
internal static class BitmapUtils
|
||||
{
|
||||
// Checks whether an area of a bitmap contains entirely the specified alpha value.
|
||||
public static bool IsAlphaEntirely(byte expectedAlpha, BitmapContent bitmap, Rectangle? region = null)
|
||||
{
|
||||
var bitmapRegion = region.HasValue ? region.Value : new Rectangle(0, 0, bitmap.Width, bitmap.Height);
|
||||
// Works with PixelBitmapContent<byte> at this stage
|
||||
if (bitmap is PixelBitmapContent<byte>)
|
||||
{
|
||||
var bmp = bitmap as PixelBitmapContent<byte>;
|
||||
for (int y = 0; y < bitmapRegion.Height; y++)
|
||||
{
|
||||
for (int x = 0; x < bitmapRegion.Width; x++)
|
||||
{
|
||||
var alpha = bmp.GetPixel(bitmapRegion.X + x, bitmapRegion.Y + y);
|
||||
if (alpha != expectedAlpha)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (bitmap is PixelBitmapContent<Color>)
|
||||
{
|
||||
var bmp = bitmap as PixelBitmapContent<Color>;
|
||||
for (int y = 0; y < bitmapRegion.Height; y++)
|
||||
{
|
||||
for (int x = 0; x < bitmapRegion.Width; x++)
|
||||
{
|
||||
var alpha = bmp.GetPixel(bitmapRegion.X + x, bitmapRegion.Y + y).A;
|
||||
if (alpha != expectedAlpha)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
throw new ArgumentException("Expected PixelBitmapContent<byte> or PixelBitmapContent<Color>, got " + bitmap.GetType().Name, "bitmap");
|
||||
}
|
||||
}
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
// Describes a range of consecutive characters that should be included in the font.
|
||||
[TypeConverter(typeof(CharacterRegionTypeConverter))]
|
||||
public struct CharacterRegion
|
||||
{
|
||||
public char Start;
|
||||
public char End;
|
||||
|
||||
// Enumerates all characters within the region.
|
||||
public IEnumerable<Char> Characters()
|
||||
{
|
||||
for (var c = Start; c <= End; c++)
|
||||
{
|
||||
yield return c;
|
||||
}
|
||||
}
|
||||
|
||||
// Constructor.
|
||||
public CharacterRegion(char start, char end)
|
||||
{
|
||||
if (start > end)
|
||||
throw new ArgumentException();
|
||||
|
||||
Start = start;
|
||||
End = end;
|
||||
}
|
||||
|
||||
// Default to just the base ASCII character set.
|
||||
public static CharacterRegion Default = new CharacterRegion(' ', '~');
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Test if there is an element in this enumeration.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the element</typeparam>
|
||||
/// <param name="source">The enumerable source.</param>
|
||||
/// <returns><c>true</c> if there is an element in this enumeration, <c>false</c> otherwise</returns>
|
||||
public static bool Any<T>(IEnumerable<T> source)
|
||||
{
|
||||
return source.GetEnumerator().MoveNext();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Select elements from an enumeration.
|
||||
/// </summary>
|
||||
/// <typeparam name="TSource">The type of the T source.</typeparam>
|
||||
/// <typeparam name="TResult">The type of the T result.</typeparam>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="selector">The selector.</param>
|
||||
/// <returns>A enumeration of selected values</returns>
|
||||
public static IEnumerable<TResult> SelectMany<TSource, TResult>(IEnumerable<TSource> source, Func<TSource, IEnumerable<TResult>> selector)
|
||||
{
|
||||
foreach (TSource sourceItem in source)
|
||||
{
|
||||
foreach (TResult result in selector(sourceItem))
|
||||
yield return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Selects distinct elements from an enumeration.
|
||||
/// </summary>
|
||||
/// <typeparam name="TSource">The type of the T source.</typeparam>
|
||||
/// <param name="source">The source.</param>
|
||||
/// <param name="comparer">The comparer.</param>
|
||||
/// <returns>A enumeration of selected values</returns>
|
||||
public static IEnumerable<TSource> Distinct<TSource>(IEnumerable<TSource> source, IEqualityComparer<TSource> comparer = null)
|
||||
{
|
||||
if (comparer == null)
|
||||
comparer = EqualityComparer<TSource>.Default;
|
||||
|
||||
// using Dictionary is not really efficient but easy to implement
|
||||
var values = new Dictionary<TSource, object>(comparer);
|
||||
foreach (TSource sourceItem in source)
|
||||
{
|
||||
if (!values.ContainsKey(sourceItem))
|
||||
{
|
||||
values.Add(sourceItem, null);
|
||||
yield return sourceItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
public class CharacterRegionTypeConverter : TypeConverter
|
||||
{
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
|
||||
{
|
||||
return sourceType == typeof(string);
|
||||
}
|
||||
|
||||
|
||||
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
|
||||
{
|
||||
// Input must be a string.
|
||||
string source = value as string;
|
||||
|
||||
if (string.IsNullOrEmpty(source))
|
||||
{
|
||||
throw new ArgumentException();
|
||||
}
|
||||
|
||||
// Supported input formats:
|
||||
// A
|
||||
// A-Z
|
||||
// 32-127
|
||||
// 0x20-0x7F
|
||||
|
||||
var splitStr = source.Split('-');
|
||||
var split = new char[splitStr.Length];
|
||||
for (int i = 0; i < splitStr.Length; i++)
|
||||
{
|
||||
split[i] = ConvertCharacter(splitStr[i]);
|
||||
}
|
||||
|
||||
switch (split.Length)
|
||||
{
|
||||
case 1:
|
||||
// Only a single character (eg. "a").
|
||||
return new CharacterRegion(split[0], split[0]);
|
||||
|
||||
case 2:
|
||||
// Range of characters (eg. "a-z").
|
||||
return new CharacterRegion(split[0], split[1]);
|
||||
|
||||
default:
|
||||
throw new ArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static char ConvertCharacter(string value)
|
||||
{
|
||||
if (value.Length == 1)
|
||||
{
|
||||
// Single character directly specifies a codepoint.
|
||||
return value[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise it must be an integer (eg. "32" or "0x20").
|
||||
return (char)(int)intConverter.ConvertFromInvariantString(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static TypeConverter intConverter = TypeDescriptor.GetConverter(typeof(int));
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
// MonoGame - Copyright (C) The MonoGame Team
|
||||
// This file is subject to the terms and conditions defined in
|
||||
// file 'LICENSE.txt', which is part of this source code package.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct ABCFloat
|
||||
{
|
||||
public float A;
|
||||
public float B;
|
||||
public float C;
|
||||
}
|
||||
|
||||
// Represents a single character within a font.
|
||||
internal class Glyph
|
||||
{
|
||||
// Constructor.
|
||||
public Glyph(char character, BitmapContent bitmap, Rectangle? subrect = null)
|
||||
{
|
||||
this.Character = character;
|
||||
this.Bitmap = bitmap;
|
||||
this.Subrect = subrect.GetValueOrDefault(new Rectangle(0, 0, bitmap.Width, bitmap.Height));
|
||||
this.Width = bitmap.Width;
|
||||
this.Height = bitmap.Height;
|
||||
}
|
||||
|
||||
// Unicode codepoint.
|
||||
public char Character;
|
||||
|
||||
// Glyph image data (may only use a portion of a larger bitmap).
|
||||
public BitmapContent Bitmap;
|
||||
public Rectangle Subrect;
|
||||
|
||||
// Layout information.
|
||||
public float XOffset;
|
||||
public float YOffset;
|
||||
public int Width;
|
||||
public int Height;
|
||||
|
||||
public float XAdvance;
|
||||
|
||||
public ABCFloat CharacterWidths;
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
// MonoGame - Copyright (C) The MonoGame Team
|
||||
// This file is subject to the terms and conditions defined in
|
||||
// file 'LICENSE.txt', which is part of this source code package.
|
||||
|
||||
using System;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
// Crops unused space from around the edge of a glyph bitmap.
|
||||
internal static class GlyphCropper
|
||||
{
|
||||
public static void Crop(Glyph glyph)
|
||||
{
|
||||
// Crop the top.
|
||||
while ((glyph.Subrect.Height > 1) && BitmapUtils.IsAlphaEntirely(0, glyph.Bitmap, new Rectangle(glyph.Subrect.X, glyph.Subrect.Y, glyph.Subrect.Width, 1)))
|
||||
{
|
||||
glyph.Subrect.Y++;
|
||||
glyph.Subrect.Height--;
|
||||
|
||||
glyph.YOffset++;
|
||||
}
|
||||
|
||||
// Crop the bottom.
|
||||
while ((glyph.Subrect.Height > 1) && BitmapUtils.IsAlphaEntirely(0, glyph.Bitmap, new Rectangle(glyph.Subrect.X, glyph.Subrect.Bottom - 1, glyph.Subrect.Width, 1)))
|
||||
{
|
||||
glyph.Subrect.Height--;
|
||||
}
|
||||
|
||||
// Crop the left.
|
||||
while ((glyph.Subrect.Width > 1) && BitmapUtils.IsAlphaEntirely(0, glyph.Bitmap, new Rectangle(glyph.Subrect.X, glyph.Subrect.Y, 1, glyph.Subrect.Height)))
|
||||
{
|
||||
glyph.Subrect.X++;
|
||||
glyph.Subrect.Width--;
|
||||
|
||||
glyph.XOffset++;
|
||||
}
|
||||
|
||||
// Crop the right.
|
||||
while ((glyph.Subrect.Width > 1) && BitmapUtils.IsAlphaEntirely(0, glyph.Bitmap, new Rectangle(glyph.Subrect.Right - 1, glyph.Subrect.Y, 1, glyph.Subrect.Height)))
|
||||
{
|
||||
glyph.Subrect.Width--;
|
||||
|
||||
glyph.XAdvance++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
// MonoGame - Copyright (C) The MonoGame Team
|
||||
// This file is subject to the terms and conditions defined in
|
||||
// file 'LICENSE.txt', which is part of this source code package.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
// Helper for arranging many small bitmaps onto a single larger surface.
|
||||
internal static class GlyphPacker
|
||||
{
|
||||
public static BitmapContent ArrangeGlyphs(Glyph[] sourceGlyphs, bool requirePOT, bool requireSquare)
|
||||
{
|
||||
// Build up a list of all the glyphs needing to be arranged.
|
||||
var glyphs = new List<ArrangedGlyph>();
|
||||
|
||||
for (int i = 0; i < sourceGlyphs.Length; i++)
|
||||
{
|
||||
var glyph = new ArrangedGlyph();
|
||||
|
||||
glyph.Source = sourceGlyphs[i];
|
||||
|
||||
// Leave a one pixel border around every glyph in the output bitmap.
|
||||
glyph.Width = sourceGlyphs[i].Subrect.Width + 2;
|
||||
glyph.Height = sourceGlyphs[i].Subrect.Height + 2;
|
||||
|
||||
glyphs.Add(glyph);
|
||||
}
|
||||
|
||||
// Sort so the largest glyphs get arranged first.
|
||||
glyphs.Sort(CompareGlyphSizes);
|
||||
|
||||
// Work out how big the output bitmap should be.
|
||||
int outputWidth = GuessOutputWidth(sourceGlyphs);
|
||||
int outputHeight = 0;
|
||||
|
||||
// Choose positions for each glyph, one at a time.
|
||||
for (int i = 0; i < glyphs.Count; i++)
|
||||
{
|
||||
PositionGlyph(glyphs, i, outputWidth);
|
||||
|
||||
outputHeight = Math.Max(outputHeight, glyphs[i].Y + glyphs[i].Height);
|
||||
}
|
||||
|
||||
// Create the merged output bitmap.
|
||||
outputHeight = MakeValidTextureSize(outputHeight, requirePOT);
|
||||
|
||||
if (requireSquare)
|
||||
{
|
||||
outputHeight = Math.Max (outputWidth, outputHeight);
|
||||
outputWidth = outputHeight;
|
||||
}
|
||||
|
||||
return CopyGlyphsToOutput(glyphs, outputWidth, outputHeight);
|
||||
}
|
||||
|
||||
// Once arranging is complete, copies each glyph to its chosen position in the single larger output bitmap.
|
||||
static BitmapContent CopyGlyphsToOutput(List<ArrangedGlyph> glyphs, int width, int height)
|
||||
{
|
||||
var output = new PixelBitmapContent<Color>(width, height);
|
||||
|
||||
foreach (var glyph in glyphs)
|
||||
{
|
||||
var sourceGlyph = glyph.Source;
|
||||
var sourceRegion = sourceGlyph.Subrect;
|
||||
var destinationRegion = new Rectangle(glyph.X + 1, glyph.Y + 1, sourceRegion.Width, sourceRegion.Height);
|
||||
|
||||
BitmapContent.Copy(sourceGlyph.Bitmap, sourceRegion, output, destinationRegion);
|
||||
|
||||
sourceGlyph.Bitmap = output;
|
||||
sourceGlyph.Subrect = destinationRegion;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
// Internal helper class keeps track of a glyph while it is being arranged.
|
||||
class ArrangedGlyph
|
||||
{
|
||||
public Glyph Source;
|
||||
|
||||
public int X;
|
||||
public int Y;
|
||||
|
||||
public int Width;
|
||||
public int Height;
|
||||
}
|
||||
|
||||
|
||||
// Works out where to position a single glyph.
|
||||
static void PositionGlyph(List<ArrangedGlyph> glyphs, int index, int outputWidth)
|
||||
{
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
// Is this position free for us to use?
|
||||
int intersects = FindIntersectingGlyph(glyphs, index, x, y);
|
||||
|
||||
if (intersects < 0)
|
||||
{
|
||||
glyphs[index].X = x;
|
||||
glyphs[index].Y = y;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip past the existing glyph that we collided with.
|
||||
x = glyphs[intersects].X + glyphs[intersects].Width;
|
||||
|
||||
// If we ran out of room to move to the right, try the next line down instead.
|
||||
if (x + glyphs[index].Width > outputWidth)
|
||||
{
|
||||
x = 0;
|
||||
y++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Checks if a proposed glyph position collides with anything that we already arranged.
|
||||
static int FindIntersectingGlyph(List<ArrangedGlyph> glyphs, int index, int x, int y)
|
||||
{
|
||||
int w = glyphs[index].Width;
|
||||
int h = glyphs[index].Height;
|
||||
|
||||
for (int i = 0; i < index; i++)
|
||||
{
|
||||
if (glyphs[i].X >= x + w)
|
||||
continue;
|
||||
|
||||
if (glyphs[i].X + glyphs[i].Width <= x)
|
||||
continue;
|
||||
|
||||
if (glyphs[i].Y >= y + h)
|
||||
continue;
|
||||
|
||||
if (glyphs[i].Y + glyphs[i].Height <= y)
|
||||
continue;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
// Comparison function for sorting glyphs by size.
|
||||
static int CompareGlyphSizes(ArrangedGlyph a, ArrangedGlyph b)
|
||||
{
|
||||
const int heightWeight = 1024;
|
||||
|
||||
int aSize = a.Height * heightWeight + a.Width;
|
||||
int bSize = b.Height * heightWeight + b.Width;
|
||||
|
||||
if (aSize != bSize)
|
||||
return bSize.CompareTo(aSize);
|
||||
else
|
||||
return a.Source.Character.CompareTo(b.Source.Character);
|
||||
}
|
||||
|
||||
|
||||
// Heuristic guesses what might be a good output width for a list of glyphs.
|
||||
static int GuessOutputWidth(Glyph[] sourceGlyphs)
|
||||
{
|
||||
int maxWidth = 0;
|
||||
int totalSize = 0;
|
||||
|
||||
foreach (var glyph in sourceGlyphs)
|
||||
{
|
||||
maxWidth = Math.Max(maxWidth, glyph.Bitmap.Width);
|
||||
totalSize += glyph.Bitmap.Width * glyph.Bitmap.Height;
|
||||
}
|
||||
|
||||
int width = Math.Max((int)Math.Sqrt(totalSize), maxWidth);
|
||||
|
||||
return MakeValidTextureSize(width, true);
|
||||
}
|
||||
|
||||
|
||||
// Rounds a value up to the next larger valid texture size.
|
||||
static int MakeValidTextureSize(int value, bool requirePowerOfTwo)
|
||||
{
|
||||
// In case we want to compress the texture, make sure the size is a multiple of 4.
|
||||
const int blockSize = 4;
|
||||
|
||||
if (requirePowerOfTwo)
|
||||
{
|
||||
// Round up to a power of two.
|
||||
int powerOfTwo = blockSize;
|
||||
|
||||
while (powerOfTwo < value)
|
||||
powerOfTwo <<= 1;
|
||||
|
||||
return powerOfTwo;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Round up to the specified block size.
|
||||
return (value + blockSize - 1) & ~(blockSize - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
// Importer interface allows the conversion tool to support multiple source font formats.
|
||||
internal interface IFontImporter
|
||||
{
|
||||
void Import(FontDescription options, string fontName);
|
||||
|
||||
IEnumerable<Glyph> Glyphs { get; }
|
||||
|
||||
float LineSpacing { get; }
|
||||
|
||||
int YOffsetMin { get; }
|
||||
}
|
||||
}
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
// MonoGame - Copyright (C) The MonoGame Team
|
||||
// This file is subject to the terms and conditions defined in
|
||||
// file 'LICENSE.txt', which is part of this source code package.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using SharpFont;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content.Pipeline.Graphics
|
||||
{
|
||||
// Uses FreeType to rasterize TrueType fonts into a series of glyph bitmaps.
|
||||
internal class SharpFontImporter : IFontImporter
|
||||
{
|
||||
// Properties hold the imported font data.
|
||||
public IEnumerable<Glyph> Glyphs { get; private set; }
|
||||
|
||||
public float LineSpacing { get; private set; }
|
||||
|
||||
public int YOffsetMin { get; private set; }
|
||||
|
||||
// Size of the temp surface used for GDI+ rasterization.
|
||||
const int MaxGlyphSize = 1024;
|
||||
|
||||
Library lib = null;
|
||||
|
||||
public void Import(FontDescription options, string fontName)
|
||||
{
|
||||
lib = new Library();
|
||||
// Create a bunch of GDI+ objects.
|
||||
var face = CreateFontFace(options, fontName);
|
||||
try
|
||||
{
|
||||
// Which characters do we want to include?
|
||||
var characters = options.Characters;
|
||||
|
||||
var glyphList = new List<Glyph>();
|
||||
// Rasterize each character in turn.
|
||||
foreach (char character in characters)
|
||||
{
|
||||
var glyph = ImportGlyph(character, face);
|
||||
glyphList.Add(glyph);
|
||||
}
|
||||
Glyphs = glyphList;
|
||||
|
||||
// Store the font height.
|
||||
LineSpacing = face.Size.Metrics.Height >> 6;
|
||||
|
||||
// The height used to calculate the Y offset for each character.
|
||||
YOffsetMin = -face.Size.Metrics.Ascender >> 6;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (face != null)
|
||||
face.Dispose();
|
||||
if (lib != null)
|
||||
{
|
||||
lib.Dispose();
|
||||
lib = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Attempts to instantiate the requested GDI+ font object.
|
||||
private Face CreateFontFace(FontDescription options, string fontName)
|
||||
{
|
||||
try
|
||||
{
|
||||
const uint dpi = 96;
|
||||
var face = lib.NewFace(fontName, 0);
|
||||
var fixedSize = ((int)options.Size) << 6;
|
||||
face.SetCharSize(0, fixedSize, dpi, dpi);
|
||||
|
||||
if (face.FamilyName == "Microsoft Sans Serif" && options.FontName != "Microsoft Sans Serif")
|
||||
throw new PipelineException(string.Format("Font {0} is not installed on this computer.", options.FontName));
|
||||
|
||||
return face;
|
||||
|
||||
// A font substitution must have occurred.
|
||||
//throw new Exception(string.Format("Can't find font '{0}'.", options.FontName));
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
// Rasterizes a single character glyph.
|
||||
private Glyph ImportGlyph(char character, Face face)
|
||||
{
|
||||
uint glyphIndex = face.GetCharIndex(character);
|
||||
face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal);
|
||||
face.Glyph.RenderGlyph(RenderMode.Normal);
|
||||
|
||||
// Render the character.
|
||||
BitmapContent glyphBitmap = null;
|
||||
if (face.Glyph.Bitmap.Width > 0 && face.Glyph.Bitmap.Rows > 0)
|
||||
{
|
||||
glyphBitmap = new PixelBitmapContent<byte>(face.Glyph.Bitmap.Width, face.Glyph.Bitmap.Rows);
|
||||
byte[] gpixelAlphas = new byte[face.Glyph.Bitmap.Width * face.Glyph.Bitmap.Rows];
|
||||
//if the character bitmap has 1bpp we have to expand the buffer data to get the 8bpp pixel data
|
||||
//each byte in bitmap.bufferdata contains the value of to 8 pixels in the row
|
||||
//if bitmap is of width 10, each row has 2 bytes with 10 valid bits, and the last 6 bits of 2nd byte must be discarded
|
||||
if(face.Glyph.Bitmap.PixelMode == PixelMode.Mono)
|
||||
{
|
||||
//variables needed for the expansion, amount of written data, length of the data to write
|
||||
int written = 0, length = face.Glyph.Bitmap.Width * face.Glyph.Bitmap.Rows;
|
||||
for(int i = 0; written < length; i++)
|
||||
{
|
||||
//width in pixels of each row
|
||||
int width = face.Glyph.Bitmap.Width;
|
||||
while(width > 0)
|
||||
{
|
||||
//valid data in the current byte
|
||||
int stride = MathHelper.Min(8, width);
|
||||
//copy the valid bytes to pixeldata
|
||||
//System.Array.Copy(ExpandByte(face.Glyph.Bitmap.BufferData[i]), 0, gpixelAlphas, written, stride);
|
||||
ExpandByteAndCopy(face.Glyph.Bitmap.BufferData[i], stride, gpixelAlphas, written);
|
||||
written += stride;
|
||||
width -= stride;
|
||||
if(width > 0)
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
Marshal.Copy(face.Glyph.Bitmap.Buffer, gpixelAlphas, 0, gpixelAlphas.Length);
|
||||
glyphBitmap.SetPixelData(gpixelAlphas);
|
||||
}
|
||||
|
||||
if (glyphBitmap == null)
|
||||
{
|
||||
var gHA = face.Glyph.Metrics.HorizontalAdvance >> 6;
|
||||
var gVA = face.Size.Metrics.Height >> 6;
|
||||
|
||||
gHA = gHA > 0 ? gHA : gVA;
|
||||
gVA = gVA > 0 ? gVA : gHA;
|
||||
|
||||
glyphBitmap = new PixelBitmapContent<byte>(gHA, gVA);
|
||||
}
|
||||
|
||||
// not sure about this at all
|
||||
var abc = new ABCFloat ();
|
||||
abc.A = face.Glyph.Metrics.HorizontalBearingX >> 6;
|
||||
abc.B = face.Glyph.Metrics.Width >> 6;
|
||||
abc.C = (face.Glyph.Metrics.HorizontalAdvance >> 6) - (abc.A + abc.B);
|
||||
|
||||
// Construct the output Glyph object.
|
||||
return new Glyph(character, glyphBitmap)
|
||||
{
|
||||
XOffset = -(face.Glyph.Advance.X >> 6),
|
||||
XAdvance = face.Glyph.Metrics.HorizontalAdvance >> 6,
|
||||
YOffset = -(face.Glyph.Metrics.HorizontalBearingY >> 6),
|
||||
CharacterWidths = abc
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Reads each individual bit of a byte from left to right and expands it to a full byte,
|
||||
/// ones get byte.maxvalue, and zeros get byte.minvalue.
|
||||
/// </summary>
|
||||
/// <param name="origin">Byte to expand and copy</param>
|
||||
/// <param name="length">Number of Bits of the Byte to copy, from 1 to 8</param>
|
||||
/// <param name="destination">Byte array where to copy the results</param>
|
||||
/// <param name="startIndex">Position where to begin copying the results in destination</param>
|
||||
private static void ExpandByteAndCopy(byte origin, int length, byte[] destination, int startIndex)
|
||||
{
|
||||
byte tmp;
|
||||
for(int i = 7; i > 7 - length; i--)
|
||||
{
|
||||
tmp = (byte) (1 << i);
|
||||
if(origin / tmp == 1)
|
||||
{
|
||||
destination[startIndex + 7 - i] = byte.MaxValue;
|
||||
origin -= tmp;
|
||||
}
|
||||
else
|
||||
destination[startIndex + 7 - i] = byte.MinValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user