BitmapFont.java 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. /*
  2. * Copyright (c) 2009-2012 jMonkeyEngine
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are
  7. * met:
  8. *
  9. * * Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. *
  12. * * Redistributions in binary form must reproduce the above copyright
  13. * notice, this list of conditions and the following disclaimer in the
  14. * documentation and/or other materials provided with the distribution.
  15. *
  16. * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
  17. * may be used to endorse or promote products derived from this software
  18. * without specific prior written permission.
  19. *
  20. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
  22. * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  23. * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  24. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  25. * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  26. * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  27. * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  28. * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  29. * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  30. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31. */
  32. package com.jme3.font;
  33. import com.jme3.export.*;
  34. import com.jme3.material.Material;
  35. import java.io.IOException;
  36. /**
  37. * Represents a font within jME that is generated with the AngelCode Bitmap Font Generator
  38. * @author dhdd
  39. */
  40. public class BitmapFont implements Savable {
  41. /**
  42. * Specifies horizontal alignment for text.
  43. *
  44. * @see BitmapText#setAlignment(com.jme3.font.BitmapFont.Align)
  45. */
  46. public enum Align {
  47. /**
  48. * Align text on the left of the text block
  49. */
  50. Left,
  51. /**
  52. * Align text in the center of the text block
  53. */
  54. Center,
  55. /**
  56. * Align text on the right of the text block
  57. */
  58. Right
  59. }
  60. /**
  61. * Specifies vertical alignment for text.
  62. *
  63. * @see BitmapText#setVerticalAlignment(com.jme3.font.BitmapFont.VAlign)
  64. */
  65. public enum VAlign {
  66. /**
  67. * Align text on the top of the text block
  68. */
  69. Top,
  70. /**
  71. * Align text in the center of the text block
  72. */
  73. Center,
  74. /**
  75. * Align text at the bottom of the text block
  76. */
  77. Bottom
  78. }
  79. private BitmapCharacterSet charSet;
  80. private Material[] pages;
  81. public BitmapFont() {
  82. }
  83. public BitmapText createLabel(String content){
  84. BitmapText label = new BitmapText(this);
  85. label.setSize(getCharSet().getRenderedSize());
  86. label.setText(content);
  87. return label;
  88. }
  89. public float getPreferredSize(){
  90. return getCharSet().getRenderedSize();
  91. }
  92. public void setCharSet(BitmapCharacterSet charSet) {
  93. this.charSet = charSet;
  94. }
  95. public void setPages(Material[] pages) {
  96. this.pages = pages;
  97. charSet.setPageSize(pages.length);
  98. }
  99. public Material getPage(int index) {
  100. return pages[index];
  101. }
  102. public int getPageSize() {
  103. return pages.length;
  104. }
  105. public BitmapCharacterSet getCharSet() {
  106. return charSet;
  107. }
  108. /**
  109. * Gets the line height of a StringBlock.
  110. * @param sb
  111. * @return the line height
  112. */
  113. public float getLineHeight(StringBlock sb) {
  114. return charSet.getLineHeight() * (sb.getSize() / charSet.getRenderedSize());
  115. }
  116. public float getCharacterAdvance(char curChar, char nextChar, float size){
  117. BitmapCharacter c = charSet.getCharacter(curChar);
  118. if (c == null)
  119. return 0f;
  120. float advance = size * c.getXAdvance();
  121. advance += c.getKerning(nextChar) * size;
  122. return advance;
  123. }
  124. private int findKerningAmount(int newLineLastChar, int nextChar) {
  125. BitmapCharacter c = charSet.getCharacter(newLineLastChar);
  126. if (c == null)
  127. return 0;
  128. return c.getKerning(nextChar);
  129. }
  130. @Override
  131. public void write(JmeExporter ex) throws IOException {
  132. OutputCapsule oc = ex.getCapsule(this);
  133. oc.write(charSet, "charSet", null);
  134. oc.write(pages, "pages", null);
  135. }
  136. @Override
  137. public void read(JmeImporter im) throws IOException {
  138. InputCapsule ic = im.getCapsule(this);
  139. charSet = (BitmapCharacterSet) ic.readSavable("charSet", null);
  140. Savable[] pagesSavable = ic.readSavableArray("pages", null);
  141. pages = new Material[pagesSavable.length];
  142. System.arraycopy(pagesSavable, 0, pages, 0, pages.length);
  143. }
  144. public float getLineWidth(CharSequence text){
  145. // This method will probably always be a bit of a maintenance
  146. // nightmare since it basis its calculation on a different
  147. // routine than the Letters class. The ideal situation would
  148. // be to abstract out letter position and size into its own
  149. // class that both BitmapFont and Letters could use for
  150. // positioning.
  151. // If getLineWidth() here ever again returns a different value
  152. // than Letters does with the same text then it might be better
  153. // just to create a Letters object for the sole purpose of
  154. // getting a text size. It's less efficient but at least it
  155. // would be accurate.
  156. // And here I am mucking around in here again...
  157. //
  158. // A font character has a few values that are pertinent to the
  159. // line width:
  160. // xOffset
  161. // xAdvance
  162. // kerningAmount(nextChar)
  163. //
  164. // The way BitmapText ultimately works is that the first character
  165. // starts with xOffset included (ie: it is rendered at -xOffset).
  166. // Its xAdvance is wider to accomodate that initial offset.
  167. // The cursor position is advanced by xAdvance each time.
  168. //
  169. // So, a width should be calculated in a similar way. Start with
  170. // -xOffset + xAdvance for the first character and then each subsequent
  171. // character is just xAdvance more 'width'.
  172. //
  173. // The kerning amount from one character to the next affects the
  174. // cursor position of that next character and thus the ultimate width
  175. // and so must be factored in also.
  176. float lineWidth = 0f;
  177. float maxLineWidth = 0f;
  178. char lastChar = 0;
  179. boolean firstCharOfLine = true;
  180. // float sizeScale = (float) block.getSize() / charSet.getRenderedSize();
  181. float sizeScale = 1f;
  182. for (int i = 0; i < text.length(); i++){
  183. char theChar = text.charAt(i);
  184. if (theChar == '\n'){
  185. maxLineWidth = Math.max(maxLineWidth, lineWidth);
  186. lineWidth = 0f;
  187. firstCharOfLine = true;
  188. continue;
  189. }
  190. BitmapCharacter c = charSet.getCharacter((int) theChar);
  191. if (c != null){
  192. if (theChar == '\\' && i<text.length()-1 && text.charAt(i+1)=='#'){
  193. if (i+5<text.length() && text.charAt(i+5)=='#'){
  194. i+=5;
  195. continue;
  196. }else if (i+8<text.length() && text.charAt(i+8)=='#'){
  197. i+=8;
  198. continue;
  199. }
  200. }
  201. if (!firstCharOfLine){
  202. lineWidth += findKerningAmount(lastChar, theChar) * sizeScale;
  203. } else {
  204. // The first character needs to add in its xOffset but it
  205. // is the only one... and negative offsets = postive width
  206. // because we're trying to account for the part that hangs
  207. // over the left. So we subtract.
  208. lineWidth -= c.getXOffset() * sizeScale;
  209. firstCharOfLine = false;
  210. }
  211. float xAdvance = c.getXAdvance() * sizeScale;
  212. // If this is the last character, then we really should have
  213. // only add its width. The advance may include extra spacing
  214. // that we don't care about.
  215. if (i == text.length() - 1) {
  216. lineWidth += c.getWidth() * sizeScale;
  217. // Since theh width includes the xOffset then we need
  218. // to take it out again by adding it, ie: offset the width
  219. // we just added by the appropriate amount.
  220. lineWidth += c.getXOffset() * sizeScale;
  221. } else {
  222. lineWidth += xAdvance;
  223. }
  224. }
  225. }
  226. return Math.max(maxLineWidth, lineWidth);
  227. }
  228. /**
  229. * Merge two fonts.
  230. * If two font have the same style, merge will fail.
  231. * @param newFont Style must be assigned to this.
  232. * @author Yonghoon
  233. */
  234. public void merge(BitmapFont newFont) {
  235. charSet.merge(newFont.charSet);
  236. final int size1 = this.pages.length;
  237. final int size2 = newFont.pages.length;
  238. Material[] tmp = new Material[size1+size2];
  239. System.arraycopy(this.pages, 0, tmp, 0, size1);
  240. System.arraycopy(newFont.pages, 0, tmp, size1, size2);
  241. this.pages = tmp;
  242. // this.pages = Arrays.copyOf(this.pages, size1+size2);
  243. // System.arraycopy(newFont.pages, 0, this.pages, size1, size2);
  244. }
  245. public void setStyle(int style) {
  246. charSet.setStyle(style);
  247. }
  248. }