home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2006 December / PCWDEC06.iso / Software / HandsOn / WindowsGame1 / BitmapFont.cs < prev    next >
Encoding:
Text File  |  2006-09-07  |  12.9 KB  |  469 lines

  1. // BitmapFont.cs
  2. // Bitmap Font class for XNA
  3. // Copyright 2006 Microsoft Corp.
  4. // Revision: 2006-Aug-30
  5.  
  6. //see http://blogs.msdn.com/garykac/articles/732007.aspx
  7.  
  8. using System;
  9. using System.Collections.Generic;
  10. using System.Text;
  11. using System.Xml;
  12.  
  13. using Microsoft.Xna.Framework;
  14. using Microsoft.Xna.Framework.Graphics;
  15.  
  16. namespace XNAExtras
  17. {
  18.     /// <summary>
  19.     /// Info for each glyph in the font - where to find the glyph image and 
  20.     /// other properties
  21.     /// </summary>
  22.     struct GlyphInfo
  23.     {
  24.         public int nBitmapID;
  25.         public int nOriginX;
  26.         public int nOriginY;
  27.         public int nWidth;
  28.         public int nHeight;
  29.         public int nAdvanceWidth;
  30.         public int nLeftSideBearing;
  31.     }
  32.  
  33.     /// <summary>
  34.     /// Info for each font bitmap
  35.     /// </summary>
  36.     struct BitmapInfo
  37.     {
  38.         public string strFilename;
  39.         public int nX, nY;
  40.     }
  41.  
  42.     /// <summary>
  43.     /// Bitmap font class for XNA
  44.     /// </summary>
  45.     public class BitmapFont
  46.     {
  47.         private SpriteBatch m_sb;
  48.         private Dictionary<int, BitmapInfo> m_dictBitmapID2BitmapInfo;
  49.         private Dictionary<int, Texture2D> m_dictBitmapID2Texture;
  50.         private Dictionary<char, GlyphInfo> m_dictUnicode2GlyphInfo;
  51.         private Dictionary<char, Dictionary<char, int>> m_dictKern;
  52.         private int m_nBase = 0;
  53.         private int m_nHeight = 0;
  54.  
  55.         /// <summary>
  56.         /// Create a new font from the info in the specified font descriptor (XML) file
  57.         /// </summary>
  58.         /// <param name="strFontFilename">font file (.xml)</param>
  59.         public BitmapFont(string strFontFilename)
  60.         {
  61.             m_dictBitmapID2BitmapInfo = new Dictionary<int, BitmapInfo>();
  62.             m_dictBitmapID2Texture = new Dictionary<int, Texture2D>();
  63.  
  64.             m_dictUnicode2GlyphInfo = new Dictionary<char, GlyphInfo>();
  65.             m_dictKern = new Dictionary<char, Dictionary<char, int>>();
  66.  
  67.             XmlDocument xd = new XmlDocument();
  68.             xd.Load(strFontFilename);
  69.             LoadFontXML(xd.ChildNodes);
  70.         }
  71.         
  72.         /// <summary>
  73.         /// Reset the font when the device has changed
  74.         /// </summary>
  75.         /// <param name="device">The new device</param>
  76.         public void Reset(GraphicsDevice device)
  77.         {
  78.             m_sb = new SpriteBatch(device);
  79.             foreach (KeyValuePair<int, BitmapInfo> kv in m_dictBitmapID2BitmapInfo)
  80.                 m_dictBitmapID2Texture[kv.Key] = Texture2D.FromFile(device, kv.Value.strFilename, kv.Value.nX, kv.Value.nY);
  81.         }
  82.  
  83.         /// <summary>
  84.         /// Should we kern adjacent characters?
  85.         /// </summary>
  86.         private bool m_fKern = true;
  87.  
  88.         /// <summary>
  89.         /// Enable/disable kerning
  90.         /// </summary>
  91.         public bool KernEnable
  92.         {
  93.             get { return m_fKern; }
  94.             set { m_fKern = value; }
  95.         }
  96.  
  97.         /// <summary>
  98.         /// Distance from top of font to the baseline
  99.         /// </summary>
  100.         public int Baseline
  101.         {
  102.             get { return m_nBase; }
  103.         }
  104.  
  105.         /// <summary>
  106.         /// Distance from top to bottom of the font
  107.         /// </summary>
  108.         public int LineHeight
  109.         {
  110.             get { return m_nHeight; }
  111.         }
  112.  
  113.         /// <summary>
  114.         /// Calculate the width of the given string.
  115.         /// </summary>
  116.         /// <param name="format">String format</param>
  117.         /// <param name="args">String format arguments</param>
  118.         /// <returns>Width (in pixels) of the string</returns>
  119.         public int MeasureString(string format, params object[] args)
  120.         {
  121.             string str = string.Format(format, args);
  122.             int nWidth = 0;
  123.             char cLast = '\0';
  124.  
  125.             foreach (char c in str)
  126.             {
  127.                 if (!m_dictUnicode2GlyphInfo.ContainsKey(c))
  128.                 {
  129.                     //TODO: print out undefined char glyph
  130.                     continue;
  131.                 }
  132.  
  133.                 GlyphInfo ginfo = m_dictUnicode2GlyphInfo[c];
  134.  
  135.                 // if kerning is enabled, get the kern adjustment for this char pair
  136.                 if (m_fKern)
  137.                 {
  138.                     nWidth += CalcKern(cLast, c);
  139.                     cLast = c;
  140.                 }
  141.  
  142.                 // update the string width
  143.                 nWidth += ginfo.nAdvanceWidth;
  144.             }
  145.  
  146.             return nWidth;
  147.         }
  148.  
  149.         /// <summary>
  150.         /// Current pen position
  151.         /// </summary>
  152.         private Vector2 m_vPen = new Vector2(0, 0);
  153.  
  154.         /// <summary>
  155.         /// Current pen position
  156.         /// </summary>
  157.         public Vector2 Pen
  158.         {
  159.             get { return m_vPen; }
  160.             set { m_vPen = value; }
  161.         }
  162.  
  163.         /// <summary>
  164.         /// Set the current pen position
  165.         /// </summary>
  166.         /// <param name="x">X-coord</param>
  167.         /// <param name="y">Y-coord</param>
  168.         public void SetPen(int x, int y)
  169.         {
  170.             m_vPen = new Vector2(x, y);
  171.         }
  172.  
  173.  
  174.         /// <summary>
  175.         /// Current color used for drawing text
  176.         /// </summary>
  177.         Color m_color = Color.White;
  178.  
  179.         /// <summary>
  180.         /// Current color used for drawing text
  181.         /// </summary>
  182.         public Color TextColor
  183.         {
  184.             get { return m_color; }
  185.             set { m_color = value; }
  186.         }
  187.  
  188.  
  189.         /// <summary>
  190.         /// Draw the given string at (x,y).
  191.         /// The text color is inherited from the last draw command (default=White).
  192.         /// </summary>
  193.         /// <param name="x">X-coord</param>
  194.         /// <param name="y">Y-coord</param>
  195.         /// <param name="format">String format</param>
  196.         /// <param name="args">String format args</param>
  197.         /// <returns>Width of string (in pixels)</returns>
  198.         public int DrawString(int x, int y, string format, params object[] args)
  199.         {
  200.             Vector2 v = new Vector2(x, y);
  201.             return DrawString(v, m_color, format, args);
  202.         }
  203.  
  204.         /// <summary>
  205.         /// Draw the given string at (x,y) using the specified color
  206.         /// </summary>
  207.         /// <param name="x">X-coord</param>
  208.         /// <param name="y">Y-coord</param>
  209.         /// <param name="color">Text color</param>
  210.         /// <param name="format">String format</param>
  211.         /// <param name="args">String format args</param>
  212.         /// <returns>Width of string (in pixels)</returns>
  213.         public int DrawString(int x, int y, Color color, string format, params object[] args)
  214.         {
  215.             Vector2 v = new Vector2(x, y);
  216.             return DrawString(v, color, format, args);
  217.         }
  218.  
  219.         /// <summary>
  220.         /// Draw the given string using the specified color.
  221.         /// The text drawing location is immediately after the last drawn text (default=0,0).
  222.         /// </summary>
  223.         /// <param name="color">Text color</param>
  224.         /// <param name="format">String format</param>
  225.         /// <param name="args">String format args</param>
  226.         /// <returns>Width of string (in pixels)</returns>
  227.         public int DrawString(Color color, string format, params object[] args)
  228.         {
  229.             return DrawString(m_vPen, color, format, args);
  230.         }
  231.  
  232.         /// <summary>
  233.         /// Draw the given string at (x,y).
  234.         /// The text drawing location is immediately after the last drawn text (default=0,0).
  235.         /// The text color is inherited from the last draw command (default=White).
  236.         /// </summary>
  237.         /// <param name="format">String format</param>
  238.         /// <param name="args">String format args</param>
  239.         /// <returns>Width of string (in pixels)</returns>
  240.         public int DrawString(string format, params object[] args)
  241.         {
  242.             return DrawString(m_vPen, m_color, format, args);
  243.         }
  244.  
  245.         /// <summary>
  246.         /// Draw the given string at vOrigin using the specified color
  247.         /// </summary>
  248.         /// <param name="vOrigin">(x,y) coord</param>
  249.         /// <param name="color">Text color</param>
  250.         /// <param name="format">String format</param>
  251.         /// <param name="args">String format args</param>
  252.         /// <returns>Width of string (in pixels)</returns>
  253.         public int DrawString(Vector2 vOrigin, Color color, string format, params object[] args)
  254.         {
  255.             string str = string.Format(format, args);
  256.  
  257.             Vector2 vAt = vOrigin;
  258.             int nWidth = 0;
  259.             char cLast = '\0';
  260.  
  261.             m_sb.Begin(SpriteBlendMode.AlphaBlend);
  262.  
  263.             // draw each character in the string
  264.             foreach (char c in str)
  265.             {
  266.                 if (!m_dictUnicode2GlyphInfo.ContainsKey(c))
  267.                 {
  268.                     //TODO: print out undefined char glyph
  269.                     continue;
  270.                 }
  271.  
  272.                 GlyphInfo ginfo = m_dictUnicode2GlyphInfo[c];
  273.  
  274.                 // if kerning is enabled, get the kern adjustment for this char pair
  275.                 if (m_fKern)
  276.                 {
  277.                     int nKern = CalcKern(cLast, c);
  278.                     vAt.X += nKern;
  279.                     nWidth += nKern;
  280.                     cLast = c;
  281.                 }
  282.     
  283.                 // draw the glyph
  284.                 vAt.X += ginfo.nLeftSideBearing;
  285.                 if (ginfo.nWidth != 0 && ginfo.nHeight != 0)
  286.                 {
  287.                     Rectangle r = new Rectangle(ginfo.nOriginX, ginfo.nOriginY, ginfo.nWidth, ginfo.nHeight);
  288.                     m_sb.Draw(m_dictBitmapID2Texture[ginfo.nBitmapID], vAt, r, color);
  289.                 }
  290.  
  291.                 // update the string width and advance the pen to the next drawing position
  292.                 nWidth += ginfo.nAdvanceWidth;
  293.                 vAt.X += ginfo.nAdvanceWidth - ginfo.nLeftSideBearing;
  294.             }
  295.  
  296.             m_sb.End();
  297.  
  298.             // record final pen position and color
  299.             m_vPen = vAt;
  300.             m_color = color;
  301.  
  302.             return nWidth;
  303.         }
  304.  
  305.         /// <summary>
  306.         /// Get the kern value for the given pair of characters
  307.         /// </summary>
  308.         /// <param name="chLeft">Left character</param>
  309.         /// <param name="chRight">Right character</param>
  310.         /// <returns>Amount to kern (in pixels)</returns>
  311.         private int CalcKern(char chLeft, char chRight)
  312.         {
  313.             if (m_dictKern.ContainsKey(chLeft))
  314.             {
  315.                 Dictionary<char, int> kern2 = m_dictKern[chLeft];
  316.                 if (kern2.ContainsKey(chRight))
  317.                     return kern2[chRight];
  318.             }
  319.             return 0;
  320.         }
  321.  
  322.         #region Load Font from XML
  323.  
  324.         /// <summary>
  325.         /// Load the font data from an XML font descriptor file
  326.         /// </summary>
  327.         /// <param name="xnl">XML node list containing the entire font descriptor file</param>
  328.         private void LoadFontXML(XmlNodeList xnl)
  329.         {
  330.             foreach (XmlNode xn in xnl)
  331.             {
  332.                 if (xn.Name == "font")
  333.                 {
  334.                     m_nBase = Int32.Parse(GetXMLAttribute(xn, "base"));
  335.                     m_nHeight = Int32.Parse(GetXMLAttribute(xn, "height"));
  336.  
  337.                     LoadFontXML_font(xn.ChildNodes);
  338.                 }
  339.             }
  340.         }
  341.  
  342.         /// <summary>
  343.         /// Load the data from the "font" node
  344.         /// </summary>
  345.         /// <param name="xnl">XML node list containing the "font" node's children</param>
  346.         private void LoadFontXML_font(XmlNodeList xnl)
  347.         {
  348.             foreach (XmlNode xn in xnl)
  349.             {
  350.                 if (xn.Name == "bitmaps")
  351.                     LoadFontXML_bitmaps(xn.ChildNodes);
  352.                 if (xn.Name == "glyphs")
  353.                     LoadFontXML_glyphs(xn.ChildNodes);
  354.                 if (xn.Name == "kernpairs")
  355.                     LoadFontXML_kernpairs(xn.ChildNodes);
  356.             }
  357.         }
  358.  
  359.         /// <summary>
  360.         /// Load the data from the "bitmaps" node
  361.         /// </summary>
  362.         /// <param name="xnl">XML node list containing the "bitmaps" node's children</param>
  363.         private void LoadFontXML_bitmaps(XmlNodeList xnl)
  364.         {
  365.             foreach (XmlNode xn in xnl)
  366.             {
  367.                 if (xn.Name == "bitmap")
  368.                 {
  369.                     string strID = GetXMLAttribute(xn, "id");
  370.                     string strFilename = GetXMLAttribute(xn, "name");
  371.                     string strSize = GetXMLAttribute(xn, "size");
  372.                     string[] aSize = strSize.Split('x');
  373.                     
  374.                     BitmapInfo bminfo;
  375.                     bminfo.strFilename = strFilename;
  376.                     bminfo.nX = Int32.Parse(aSize[0]);
  377.                     bminfo.nY = Int32.Parse(aSize[1]);
  378.  
  379.                     m_dictBitmapID2BitmapInfo[Int32.Parse(strID)] = bminfo;
  380.                 }
  381.             }
  382.         }
  383.  
  384.         /// <summary>
  385.         /// Load the data from the "glyphs" node
  386.         /// </summary>
  387.         /// <param name="xnl">XML node list containing the "glyphs" node's children</param>
  388.         private void LoadFontXML_glyphs(XmlNodeList xnl)
  389.         {
  390.             foreach (XmlNode xn in xnl)
  391.             {
  392.                 if (xn.Name == "glyph")
  393.                 {
  394.                     string strChar = GetXMLAttribute(xn, "ch");
  395.                     string strBitmapID = GetXMLAttribute(xn, "bm");
  396.                     string strOrigin = GetXMLAttribute(xn, "origin");
  397.                     string strSize = GetXMLAttribute(xn, "size");
  398.                     string strAW = GetXMLAttribute(xn, "aw");
  399.                     string strLSB = GetXMLAttribute(xn, "lsb");
  400.  
  401.                     string[] aOrigin = strOrigin.Split(',');
  402.                     string[] aSize = strSize.Split('x');
  403.  
  404.                     GlyphInfo ginfo = new GlyphInfo();
  405.                     ginfo.nBitmapID = Int32.Parse(strBitmapID);
  406.                     ginfo.nOriginX = Int32.Parse(aOrigin[0]);
  407.                     ginfo.nOriginY = Int32.Parse(aOrigin[1]);
  408.                     ginfo.nWidth = Int32.Parse(aSize[0]);
  409.                     ginfo.nHeight = Int32.Parse(aSize[1]);
  410.                     ginfo.nAdvanceWidth = Int32.Parse(strAW);
  411.                     ginfo.nLeftSideBearing = Int32.Parse(strLSB);
  412.  
  413.                     m_dictUnicode2GlyphInfo[strChar[0]] = ginfo;
  414.                 }
  415.             }
  416.         }
  417.  
  418.         /// <summary>
  419.         /// Load the data from the "kernpairs" node
  420.         /// </summary>
  421.         /// <param name="xnl">XML node list containing the "kernpairs" node's children</param>
  422.         private void LoadFontXML_kernpairs(XmlNodeList xnl)
  423.         {
  424.             foreach (XmlNode xn in xnl)
  425.             {
  426.                 if (xn.Name == "kernpair")
  427.                 {
  428.                     string strLeft = GetXMLAttribute(xn, "left");
  429.                     string strRight = GetXMLAttribute(xn, "right");
  430.                     string strAdjust = GetXMLAttribute(xn, "adjust");
  431.  
  432.                     char chLeft = strLeft[0];
  433.                     char chRight = strRight[0];
  434.  
  435.                     // create a kern dict for the left char if needed
  436.                     if (!m_dictKern.ContainsKey(chLeft))
  437.                         m_dictKern[chLeft] = new Dictionary<char,int>();
  438.  
  439.                     // add the right char to the left char's kern dict
  440.                     Dictionary<char, int> kern2 = m_dictKern[chLeft];
  441.                     kern2[chRight] = Int32.Parse(strAdjust);
  442.                 }
  443.             }
  444.         }
  445.  
  446.         /// <summary>
  447.         /// Get the XML attribute value (without throwing if the attribute doesn't exist)
  448.         /// </summary>
  449.         /// <param name="n">XML node</param>
  450.         /// <param name="strAttr">Attribute name</param>
  451.         /// <returns>Attribute value, or the empty string if the attribute doesn't exist</returns>
  452.         private static string GetXMLAttribute(XmlNode n, string strAttr)
  453.         {
  454.             string strVal = "";
  455.             try
  456.             {
  457.                 strVal = n.Attributes[strAttr].Value;
  458.             }
  459.             catch
  460.             {
  461.                 strVal = "";
  462.             }
  463.             return strVal;
  464.         }
  465.  
  466.         #endregion
  467.     }
  468. }
  469.