• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2009-2010 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 
33 package com.jme3.font;
34 
35 import com.jme3.export.*;
36 import com.jme3.material.Material;
37 import java.io.IOException;
38 
39 /**
40  * Represents a font within jME that is generated with the AngelCode Bitmap Font Generator
41  * @author dhdd
42  */
43 public class BitmapFont implements Savable {
44 
45     /**
46      * Specifies horizontal alignment for text.
47      *
48      * @see BitmapText#setAlignment(com.jme3.font.BitmapFont.Align)
49      */
50     public enum Align {
51 
52         /**
53          * Align text on the left of the text block
54          */
55         Left,
56 
57         /**
58          * Align text in the center of the text block
59          */
60         Center,
61 
62         /**
63          * Align text on the right of the text block
64          */
65         Right
66     }
67 
68     /**
69      * Specifies vertical alignment for text.
70      *
71      * @see BitmapText#setVerticalAlignment(com.jme3.font.BitmapFont.VAlign)
72      */
73     public enum VAlign {
74         /**
75          * Align text on the top of the text block
76          */
77         Top,
78 
79         /**
80          * Align text in the center of the text block
81          */
82         Center,
83 
84         /**
85          * Align text at the bottom of the text block
86          */
87         Bottom
88     }
89 
90     private BitmapCharacterSet charSet;
91     private Material[] pages;
92 
BitmapFont()93     public BitmapFont() {
94     }
95 
createLabel(String content)96     public BitmapText createLabel(String content){
97         BitmapText label = new BitmapText(this);
98         label.setSize(getCharSet().getRenderedSize());
99         label.setText(content);
100         return label;
101     }
102 
getPreferredSize()103     public float getPreferredSize(){
104         return getCharSet().getRenderedSize();
105     }
106 
setCharSet(BitmapCharacterSet charSet)107     public void setCharSet(BitmapCharacterSet charSet) {
108         this.charSet = charSet;
109     }
110 
setPages(Material[] pages)111     public void setPages(Material[] pages) {
112         this.pages = pages;
113         charSet.setPageSize(pages.length);
114     }
115 
getPage(int index)116     public Material getPage(int index) {
117         return pages[index];
118     }
119 
getPageSize()120     public int getPageSize() {
121         return pages.length;
122     }
123 
getCharSet()124     public BitmapCharacterSet getCharSet() {
125         return charSet;
126     }
127 
128     /**
129      * Gets the line height of a StringBlock.
130      * @param sb
131      * @return
132      */
getLineHeight(StringBlock sb)133     public float getLineHeight(StringBlock sb) {
134         return charSet.getLineHeight() * (sb.getSize() / charSet.getRenderedSize());
135     }
136 
getCharacterAdvance(char curChar, char nextChar, float size)137     public float getCharacterAdvance(char curChar, char nextChar, float size){
138         BitmapCharacter c = charSet.getCharacter(curChar);
139         if (c == null)
140             return 0f;
141 
142         float advance = size * c.getXAdvance();
143         advance += c.getKerning(nextChar) * size;
144         return advance;
145     }
146 
findKerningAmount(int newLineLastChar, int nextChar)147     private int findKerningAmount(int newLineLastChar, int nextChar) {
148         BitmapCharacter c = charSet.getCharacter(newLineLastChar);
149         if (c == null)
150             return 0;
151         return c.getKerning(nextChar);
152     }
153 
154     @Override
write(JmeExporter ex)155     public void write(JmeExporter ex) throws IOException {
156         OutputCapsule oc = ex.getCapsule(this);
157         oc.write(charSet, "charSet", null);
158         oc.write(pages, "pages", null);
159     }
160 
161     @Override
read(JmeImporter im)162     public void read(JmeImporter im) throws IOException {
163         InputCapsule ic = im.getCapsule(this);
164         charSet = (BitmapCharacterSet) ic.readSavable("charSet", null);
165         Savable[] pagesSavable = ic.readSavableArray("pages", null);
166         pages = new Material[pagesSavable.length];
167         System.arraycopy(pagesSavable, 0, pages, 0, pages.length);
168     }
169 
getLineWidth(CharSequence text)170     public float getLineWidth(CharSequence text){
171 
172         // This method will probably always be a bit of a maintenance
173         // nightmare since it basis its calculation on a different
174         // routine than the Letters class.  The ideal situation would
175         // be to abstract out letter position and size into its own
176         // class that both BitmapFont and Letters could use for
177         // positioning.
178         // If getLineWidth() here ever again returns a different value
179         // than Letters does with the same text then it might be better
180         // just to create a Letters object for the sole purpose of
181         // getting a text size.  It's less efficient but at least it
182         // would be accurate.
183 
184         // And here I am mucking around in here again...
185         //
186         // A font character has a few values that are pertinent to the
187         // line width:
188         //  xOffset
189         //  xAdvance
190         //  kerningAmount(nextChar)
191         //
192         // The way BitmapText ultimately works is that the first character
193         // starts with xOffset included (ie: it is rendered at -xOffset).
194         // Its xAdvance is wider to accomodate that initial offset.
195         // The cursor position is advanced by xAdvance each time.
196         //
197         // So, a width should be calculated in a similar way.  Start with
198         // -xOffset + xAdvance for the first character and then each subsequent
199         // character is just xAdvance more 'width'.
200         //
201         // The kerning amount from one character to the next affects the
202         // cursor position of that next character and thus the ultimate width
203         // and so must be factored in also.
204 
205         float lineWidth = 0f;
206         float maxLineWidth = 0f;
207         char lastChar = 0;
208         boolean firstCharOfLine = true;
209 //        float sizeScale = (float) block.getSize() / charSet.getRenderedSize();
210         float sizeScale = 1f;
211         for (int i = 0; i < text.length(); i++){
212             char theChar = text.charAt(i);
213             if (theChar == '\n'){
214                 maxLineWidth = Math.max(maxLineWidth, lineWidth);
215                 lineWidth = 0f;
216                 firstCharOfLine = true;
217                 continue;
218             }
219             BitmapCharacter c = charSet.getCharacter((int) theChar);
220             if (c != null){
221                 if (theChar == '\\' && i<text.length()-1 && text.charAt(i+1)=='#'){
222                     if (i+5<text.length() && text.charAt(i+5)=='#'){
223                         i+=5;
224                         continue;
225                     }else if (i+8<text.length() && text.charAt(i+8)=='#'){
226                         i+=8;
227                         continue;
228                     }
229                 }
230                 if (!firstCharOfLine){
231                     lineWidth += findKerningAmount(lastChar, theChar) * sizeScale;
232                 } else {
233                     // The first character needs to add in its xOffset but it
234                     // is the only one... and negative offsets = postive width
235                     // because we're trying to account for the part that hangs
236                     // over the left.  So we subtract.
237                     lineWidth -= c.getXOffset() * sizeScale;
238                     firstCharOfLine = false;
239                 }
240                 float xAdvance = c.getXAdvance() * sizeScale;
241 
242                 // If this is the last character, then we really should have
243                 // only add its width.  The advance may include extra spacing
244                 // that we don't care about.
245                 if (i == text.length() - 1) {
246                     lineWidth += c.getWidth() * sizeScale;
247 
248                     // Since theh width includes the xOffset then we need
249                     // to take it out again by adding it, ie: offset the width
250                     // we just added by the appropriate amount.
251                     lineWidth += c.getXOffset() * sizeScale;
252                 } else {
253                     lineWidth += xAdvance;
254                 }
255             }
256         }
257         return Math.max(maxLineWidth, lineWidth);
258     }
259 
260 
261     /**
262      * Merge two fonts.
263      * If two font have the same style, merge will fail.
264      * @param styleSet Style must be assigned to this.
265      * @author Yonghoon
266      */
merge(BitmapFont newFont)267     public void merge(BitmapFont newFont) {
268         charSet.merge(newFont.charSet);
269         final int size1 = this.pages.length;
270         final int size2 = newFont.pages.length;
271 
272         Material[] tmp = new Material[size1+size2];
273         System.arraycopy(this.pages, 0, tmp, 0, size1);
274         System.arraycopy(newFont.pages, 0, tmp, size1, size2);
275 
276         this.pages = tmp;
277 
278 //        this.pages = Arrays.copyOf(this.pages, size1+size2);
279 //        System.arraycopy(newFont.pages, 0, this.pages, size1, size2);
280     }
281 
setStyle(int style)282     public void setStyle(int style) {
283         charSet.setStyle(style);
284     }
285 
286 }