/*
* BitmapFonts.cs - Manages bitmap font resources and provides methods for
* font rendering.
*
* Copyright (C) 2004 - 2011 Andreas Scherrer
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
namespace Level9
{
///
/// Manages bitmap font resources.
///
public class BitmapFonts
{
// -- Attributes -----------------------------------------------------
///
/// The available font types.
///
static string[] fontTypes =
{
"Amiga",
"Amstrad CPC",
"Atari ST",
"Atari 8-bit",
"BBC Micro",
"C64",
"Enterprise 64",
"IBM PC",
"Macintosh",
"Memotech MTX",
"Nascom",
"MSX",
"Spectrum"
};
///
/// The available font scaling factors.
///
static int[] fontScalings =
{
1, 2, 3
};
///
/// The font resource names.
///
static string[] resources =
{
//
// Font sixe x 1
//
"amiga_x1",
"amstrad_cpc_x1",
"atari_st_x1",
"atari_8bit_x1",
"bbc_micro_x1",
"c64_x1",
"enterprise64_x1",
"ibm_pc_x1",
"macintosh_x1",
"memotech_mtx_x1",
"nascom_x1",
"msx_x1",
"spectrum_x1",
//
// Font sixe x 2
//
"amiga_x2",
"amstrad_cpc_x2",
"atari_st_x2",
"atari_8bit_x2",
"bbc_micro_x2",
"c64_x2",
"enterprise64_x2",
"ibm_pc_x2",
"macintosh_x2",
"memotech_mtx_x2",
"nascom_x2",
"msx_x2",
"spectrum_x2",
//
// Font sixe x 3
//
"amiga_x3",
"amstrad_cpc_x3",
"atari_st_x3",
"atari_8bit_x3",
"bbc_micro_x3",
"c64_x3",
"enterprise64_x3",
"ibm_pc_x3",
"macintosh_x3",
"memotech_mtx_x3",
"nascom_x3",
"msx_x3",
"spectrum_x3"
};
///
/// The font sizes.
///
static Size[] sizes =
{
//
// Font sixe x 1
//
new Size( 8, 16), // Amiga
new Size( 8, 8), // Amstrad CPC
new Size( 8, 16), // Atari ST
new Size( 8, 8), // Atari 8-bit
new Size( 6, 9), // BBC Micro
new Size( 8, 8), // C64
new Size( 8, 9), // Enterprise 64
new Size( 8, 12), // IBM PC
new Size( 6, 10), // Macintosh
new Size( 6, 8), // Memotech MTX
new Size( 8, 12), // Nascom
new Size( 6, 8), // MSX
new Size( 8, 7), // Sinclair
//
// Font sixe x 2
//
new Size(16, 32), // Amiga
new Size(16, 16), // Amstrad CPC
new Size(16, 32), // Atari ST
new Size(16, 16), // Atari 8-bit
new Size(12, 18), // BBC Micro
new Size(16, 16), // C64
new Size(16, 18), // Enterprise 64
new Size(16, 24), // IBM PC
new Size(12, 20), // Macintosh
new Size(12, 16), // Memotech MTX
new Size(16, 24), // Nascom
new Size(12, 16), // MSX
new Size(16, 14), // Sinclair
//
// Font sixe x 3
//
new Size(24, 48), // Amiga
new Size(24, 24), // Amstrad CPC
new Size(24, 48), // Atari ST
new Size(24, 24), // Atari 8-bit
new Size(18, 27), // BBC Micro
new Size(24, 24), // C64
new Size(24, 27), // Enterprise 64
new Size(24, 36), // IBM PC
new Size(18, 30), // Macintosh
new Size(18, 24), // Memotech MTX
new Size(24, 36), // Nascom
new Size(18, 24), // MSX
new Size(24, 21) // Sinclair
};
///
/// A list with BitmapFont elements.
///
static BitmapFont[] bitmapFonts;
// -- Constructors ---------------------------------------------------
///
/// Initializes static members of the BitmapFonts class.
///
static BitmapFonts()
{
bitmapFonts = new BitmapFont[resources.Length];
try {
System.Resources.ResourceManager manager =
new System.Resources.ResourceManager(typeof(BitmapFonts));
for (int i = 0; i < resources.Length; i++) {
Bitmap bitmap = (Bitmap)manager.GetObject(resources[i]);
if (bitmap == null) {
continue;
}
bitmapFonts[i] = new BitmapFont(
fontTypes[i % fontTypes.Length], sizes[i], bitmap);
}
}
catch(Exception e) {
ErrorHandler.Handle(typeof(BitmapFonts),
"Error getting bitmap resource.", e);
}
}
// -- Methods --------------------------------------------------------
///
/// Provides a bitmap font for the given font type and scaling factor.
/// The available font types are:
///
/// - Amiga
/// - Amstrad CPC
/// - Atari ST
/// - Atari 8-bit
/// - BBC Micro
/// - C64
/// - Enterprise 64
/// - IBM PC
/// - Macintosh
/// - Memotech MTX
/// - Nascom
/// - MSX
/// - Spectrum
///
/// The available scaling factors are: 1, 2, 3
///
/// the font type
/// the font scaling factor
/// a bitmap font
public static BitmapFont Get(string type, int scaling)
{
BitmapFont font = null;
if (scaling > 3 || scaling < 1) {
return null;
}
for (int i = 0; i < fontTypes.Length; i++) {
if (!fontTypes[i].Equals(type)) {
continue;
}
font = bitmapFonts[(scaling - 1) * resources.Length / 3 + i];
break;
}
return font;
}
// -- Accessors ------------------------------------------------------
///
/// Gets the available font types.
///
public static string[] FontTypes
{
get {
return fontTypes;
}
}
///
/// Gets the avialiable font scaling factors.
///
public static int[] FontScalings
{
get {
return fontScalings;
}
}
}
///
/// Represents a bitmap font.
///
public class BitmapFont
{
// -- Attributes -----------------------------------------------------
///
/// The font size in pixels.
///
Size size;
///
/// The font height in pixels.
///
int height;
///
/// The font width in pixels.
///
int width;
///
/// The bitmap that contains font data.
///
Bitmap bitmap;
///
/// The font type.
///
string type;
// -- Constructors ---------------------------------------------------
///
/// Initializes a new instance of the BitmapFont class.
///
/// the font type
/// the font size in pixels
/// a bitmap that contains the font data
public BitmapFont(string type, Size size, Bitmap bitmap)
{
this.type = type;
this.bitmap = bitmap;
this.size = size;
this.height = size.Height;
this.width = size.Width;
}
// -- Methods --------------------------------------------------------
///
/// Determines whether the specified Object is equal to the current
/// Object.
///
///
/// the Object to compare with the current Object
///
///
/// true if the specified Object is equal to the current Object;
/// otherwise, false
///
public override bool Equals(Object obj)
{
if (obj == null) return false;
if (this.GetType() != obj.GetType()) return false;
BitmapFont font = (BitmapFont) obj;
if (type.Equals(font.Type) && size.Equals(font.Size)) {
return true;
}
else {
return false;
}
}
///
/// Serves as a hash function for a particular type.
///
/// a hash code for the current Object
public override int GetHashCode()
{
return base.GetHashCode();
}
// -- Accessors ------------------------------------------------------
///
/// Gets the font type.
///
public string Type
{
get {
return type;
}
}
///
/// Gets the font size in pixels.
///
public Size Size
{
get {
return size;
}
}
///
/// Gets the font height in pixels.
///
public int Height
{
get {
return height;
}
}
///
/// Gets the font width in pixels.
///
public int Width
{
get {
return width;
}
}
///
/// Gets the bitmap that contains font data.
///
public Bitmap Bitmap
{
get {
return bitmap;
}
}
}
///
/// Renders text with a bitmap fonts.
///
public class BitmapFontRenderer
{
// -- Constants ------------------------------------------------------
///
/// The XOR mask for rendering text with the foreground color.
///
int MASK_FORECOLOR = 0x00000000;
///
/// The XOR mask for rendering text with the input color.
///
int MASK_INPUTCOLOR = 0x03030303;
///
/// The XOR mask for rendering text with the inverse input color.
///
int MASK_INPUTCOLOR_INV = 0x02020202;
// -- Attributes -----------------------------------------------------
///
/// The Bitmap to draw the text into.
///
Bitmap textBitmap = null;
///
/// The bitmap font.
///
BitmapFont font = null;
///
/// The spacing between lines, in pixels.
///
int lineSpacing = 0;
// -- Methods --------------------------------------------------------
///
/// Creates a bitmap (8 bit per pixel, indexed) and draws the given
/// text with a bitmap font into it. The bitmap must be disposed after
/// use.
///
/// the text to be drawn
/// the bitmap font
/// the background color
/// the foreground color
/// a text bitmap, or null if an error occurred
public Bitmap RenderText(string text, BitmapFont font, Color backColor,
Color foreColor)
{
if (text == null || text.Length == 0 || font == null
|| backColor == null || foreColor == null) {
return null;
}
UpdateTextBitmap(new Size(font.Width * text.Length, font.Height),
font, 0, backColor, foreColor, foreColor);
unsafe {
//
// Prepare bitmap access
//
Bitmap fontBitmap = font.Bitmap;
BitmapData bmDataFont = fontBitmap.LockBits(
new Rectangle(0, 0, fontBitmap.Width, fontBitmap.Height),
ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed);
BitmapData bmDataText = textBitmap.LockBits(
new Rectangle(0, 0, textBitmap.Width, textBitmap.Height),
ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
byte *bFont = (byte *)bmDataFont.Scan0.ToPointer();
byte *bText = (byte *)bmDataText.Scan0.ToPointer();
//
// Set local variables to avoid accessor calls
//
int fWidth = font.Width;
int ifWidth = fWidth >> 2;
int fHeight = font.Height;
int fbWidth = fontBitmap.Width;
int tbWidth = textBitmap.Width;
int tbAlign = tbWidth % 4 > 0 ? 4 - (tbWidth % 4) : 0;
int fwRemain = fWidth % 4 > 0 ? fWidth - 4 : 0;
//
// Blit font pixels into text bitmap
//
for (int j = 0; j < text.Length; j++) {
char c = text[j] > 127 ? '\0' : text[j];
byte *bFont2 = bFont + fWidth * c;
byte *bText2 = bText + fWidth * j;
for (int k = 0; k < fHeight; k++) {
int *iFont = (int *)(bFont2);
int *iText = (int *)(bText2);
for (int l = 0; l < ifWidth; l++) {
*(iText + l) = *(iFont + l) ^ MASK_FORECOLOR;
}
if (fwRemain > 0) {
iFont = (int *)(bFont2 + fwRemain);
iText = (int *)(bText2 + fwRemain);
*iText = *iFont ^ MASK_FORECOLOR;
}
bFont2 += fbWidth;
bText2 += tbWidth + tbAlign;
}
}
fontBitmap.UnlockBits(bmDataFont);
textBitmap.UnlockBits(bmDataText);
}
return textBitmap;
}
///
/// Creates a bitmap (8 bit per pixel, indexed) and draws the given
/// text with a bitmap font into it. The bitmap must be disposed after
/// use.
///
/// a list of lines to be drawn
/// the bitmap font
/// the line spacing in pixels
/// the offset position in outputLines
/// the background color
/// the foreground color
/// the user input color
/// a text bitmap, or null if an error occurred
public Bitmap RenderText(List outputLines,
BitmapFont font, int lineSpacing, int linesOffset,
Color backColor, Color foreColor, Color inputColor)
{
if (outputLines == null
|| (outputLines != null && outputLines.Count == 0)
|| font == null || lineSpacing < 0 || linesOffset < 0
|| backColor == null || foreColor == null
|| inputColor == null) {
return null;
}
int textWidth = outputLines[0].GetLength() * font.Width;
int textHeight = outputLines.Count * (font.Height + lineSpacing);
bool newBitmap = UpdateTextBitmap(new Size(textWidth, textHeight),
font, lineSpacing, backColor, foreColor, inputColor);
unsafe {
//
// Prepare bitmap access
//
Bitmap fontBitmap = font.Bitmap;
BitmapData bmDataFont = fontBitmap.LockBits(
new Rectangle(0, 0, fontBitmap.Width, fontBitmap.Height),
ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed);
BitmapData bmDataText = textBitmap.LockBits(
new Rectangle(0, 0, textBitmap.Width, textBitmap.Height),
ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
byte *bFont = (byte *)bmDataFont.Scan0.ToPointer();
byte *bText = (byte *)bmDataText.Scan0.ToPointer();
//
// Set local variables to avoid accessor calls
//
int fWidth = font.Size.Width;
int ifWidth = fWidth >> 2;
int fHeight = font.Size.Height;
int fbWidth = fontBitmap.Width;
int tbWidth = textBitmap.Width;
int tbAlign = tbWidth % 4 > 0 ? 4 - (tbWidth % 4) : 0;
int fwRemain = fWidth % 4 > 0 ? fWidth - 4 : 0;
int xorPerm = MASK_FORECOLOR;
int xorMask = MASK_FORECOLOR;
//
// Blit font pixels into text bitmap
//
for (int i = 0; i < outputLines.Count; i++) {
char[] line = outputLines[i].Text;
uint[] attr = outputLines[i].Attributes;
int tbOffset = (tbWidth + tbAlign)
* (lineSpacing + (fHeight + lineSpacing) * i);
for (int j = 0; j < line.Length; j++) {
if ((attr[j] & AttributedText.ATTR_INPUT_START) != 0) {
xorPerm = MASK_INPUTCOLOR;
}
if ((attr[j] & AttributedText.ATTR_INPUT_CARET) != 0) {
xorMask = MASK_INPUTCOLOR_INV;
}
else {
xorMask = xorPerm;
}
if (newBitmap || i >= linesOffset) {
char c = line[j] > 127 ? '\0' : line[j];
byte *bFont2 = bFont + fWidth * c;
byte *bText2 = bText + tbOffset + fWidth * j;
for (int k = 0; k < fHeight; k++) {
int *iFont = (int *)(bFont2);
int *iText = (int *)(bText2);
for (int l = 0; l < ifWidth; l++) {
*(iText + l) = *(iFont + l) ^ xorMask;
}
if (fwRemain > 0) {
iFont = (int *)(bFont2 + fwRemain);
iText = (int *)(bText2 + fwRemain);
*iText = *iFont ^ xorMask;
}
bFont2 += fbWidth;
bText2 += tbWidth + tbAlign;
}
}
if ((attr[j] & AttributedText.ATTR_INPUT_END) != 0) {
xorPerm = MASK_FORECOLOR;
}
}
}
fontBitmap.UnlockBits(bmDataFont);
textBitmap.UnlockBits(bmDataText);
}
return textBitmap;
}
///
/// Updates the text bitmap and its palette.
///
/// the Bitmap to draw the text into
/// the bitmap font
/// the line spacing in pixels
/// the background color
/// the foreground color
/// the user input color
/// true if a new bitmap was created; otherwise, false
bool UpdateTextBitmap(Size bitmapSize, BitmapFont font, int lineSpacing,
Color backColor, Color foreColor, Color inputColor)
{
bool newBitmap = false;
if ((textBitmap != null && !bitmapSize.Equals(textBitmap.Size))
|| textBitmap == null
|| !font.Equals(this.font)
|| lineSpacing != this.lineSpacing) {
if (textBitmap != null) {
textBitmap.Dispose();
}
textBitmap = new Bitmap(bitmapSize.Width, bitmapSize.Height,
PixelFormat.Format8bppIndexed);
this.font = font;
this.lineSpacing = lineSpacing;
newBitmap = true;
}
ColorPalette bitmapPalette = textBitmap.Palette;
bitmapPalette.Entries[0] = backColor;
bitmapPalette.Entries[1] = foreColor;
bitmapPalette.Entries[2] = inputColor;
bitmapPalette.Entries[3] = backColor;
textBitmap.Palette = bitmapPalette;
return newBitmap;
}
}
}