/* * 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; } } }