Java-Gaming.org Hi !
Featured games (90)
games approved by the League of Dukes
Games in Showcase (739)
Games in Android Showcase (224)
games submitted by our members
Games in WIP (820)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1] 2
  ignore  |  Print  
  [Solved] Rendering text in lwjgl without slick  (Read 20054 times)
0 Members and 1 Guest are viewing this topic.
Offline Ed_RockStarGuy
« Posted 2015-02-08 18:07:56 »

I was looking around on how to draw text and allow for resizing, fonts, etc. All i could find was extremely complicated code and it can only handle ether capital or not. Anyone got some code i can work from to achieve my desired goal or any res i might have overlooked that could help?


Solved, im using TheBoneJarmer example:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;

import static org.lwjgl.opengl.GL11.*;

public class Font {
   
   //Constants
   private final Map<Integer,String> CHARS = new HashMap<Integer,String>() {{
        put(0, "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
        put(1, "abcdefghijklmnopqrstuvwxyz");
        put(2, "0123456789");
        put(3, "ÄÖÜäöüß");
        put(4, " $+-*/=%\"'#@&_(),.;:?!\\|<>[]§`^~");
    }};
   
   //Variables
    private java.awt.Font font;
    private FontMetrics fontMetrics;
    private BufferedImage bufferedImage;
    private int fontTextureId;
   
    //Getters
    public float getFontImageWidth() {
        return (float) CHARS.values().stream().mapToDouble(e -> fontMetrics.getStringBounds(e, null).getWidth()).max().getAsDouble();
    }
    public float getFontImageHeight() {
        return (float) CHARS.keySet().size() * (this.getCharHeight());
    }
    public float getCharX(char c) {
        String originStr = CHARS.values().stream().filter(e -> e.contains("" + c)).findFirst().orElse("" + c);
        return (float) fontMetrics.getStringBounds(originStr.substring(0, originStr.indexOf(c)), null).getWidth();
    }
    public float getCharY(char c) {
        float lineId = (float) CHARS.keySet().stream().filter(i -> CHARS.get(i).contains("" + c)).findFirst().orElse(0);
        return this.getCharHeight() * lineId;
    }
    public float getCharWidth(char c) {
        return fontMetrics.charWidth(c);
    }
    public float getCharHeight() {
        return (float) (fontMetrics.getMaxAscent() + fontMetrics.getMaxDescent());
    }
   
    //Constructors
    public Font(String path, float size) throws Exception {
        this.font = java.awt.Font.createFont(java.awt.Font.TRUETYPE_FONT, new File(path)).deriveFont(size);
       
        //Generate buffered image
        GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
        Graphics2D graphics = gc.createCompatibleImage(1, 1, Transparency.TRANSLUCENT).createGraphics();
        graphics.setFont(font);
       
        fontMetrics = graphics.getFontMetrics();
        bufferedImage = graphics.getDeviceConfiguration().createCompatibleImage((int) getFontImageWidth(),(int) getFontImageHeight(),Transparency.TRANSLUCENT);
       
      //Generate texture
      fontTextureId = glGenTextures();
        glEnable(GL_TEXTURE_2D);
        glBindTexture(GL_TEXTURE_2D, fontTextureId);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,(int) getFontImageWidth(),(int) getFontImageHeight(),0, GL_RGBA, GL_UNSIGNED_BYTE, asByteBuffer());
 
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    }
   
    //Functions
    public void drawText(String text, int x, int y) {
        glBindTexture(GL_TEXTURE_2D, this.fontTextureId);
        glBegin(GL_QUADS);
       
        int xTmp = x;
        for (char c : text.toCharArray()) {
            float width = getCharWidth(c);
            float height = getCharHeight();
            float cw = 1f / getFontImageWidth() * width;
            float ch = 1f / getFontImageHeight() * height;
            float cx = 1f / getFontImageWidth() * getCharX(c);
            float cy = 1f / getFontImageHeight() * getCharY(c);
 
            glTexCoord2f(cx, cy);
            glVertex3f(xTmp, y, 0);
 
            glTexCoord2f(cx + cw, cy);
            glVertex3f(xTmp + width, y, 0);
 
            glTexCoord2f(cx + cw, cy + ch);
            glVertex3f(xTmp + width, y + height, 0);
 
            glTexCoord2f(cx, cy + ch);
            glVertex3f(xTmp, y + height, 0);
 
            xTmp += width;
        }
       
        glEnd();
    }
   
    //Conversions
    public ByteBuffer asByteBuffer() {
 
        ByteBuffer byteBuffer;
     
        //Draw the characters on our image
        Graphics2D imageGraphics = (Graphics2D) bufferedImage.getGraphics();
        imageGraphics.setFont(font);
        imageGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
        imageGraphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
 
        // draw every CHAR by line...
        imageGraphics.setColor(java.awt.Color.WHITE);
        CHARS.keySet().stream().forEach(i -> imageGraphics.drawString(CHARS.get(i), 0, fontMetrics.getMaxAscent() + (this.getCharHeight() * i)));
       
        //Generate texture data
        int[] pixels = new int[bufferedImage.getWidth() * bufferedImage.getHeight()];
        bufferedImage.getRGB(0, 0, bufferedImage.getWidth(), bufferedImage.getHeight(), pixels, 0, bufferedImage.getWidth());
        byteBuffer = ByteBuffer.allocateDirect((bufferedImage.getWidth() * bufferedImage.getHeight() * 4));
 
        for (int y = 0; y < bufferedImage.getHeight(); y++) {
            for (int x = 0; x < bufferedImage.getWidth(); x++) {
                int pixel = pixels[y * bufferedImage.getWidth() + x];
                byteBuffer.put((byte) ((pixel >> 16) & 0xFF));   // Red component
                byteBuffer.put((byte) ((pixel >> 8) & 0xFF));    // Green component
                byteBuffer.put((byte) (pixel & 0xFF));           // Blue component
                byteBuffer.put((byte) ((pixel >> 24) & 0xFF));   // Alpha component. Only for RGBA
            }
        }
       
        byteBuffer.flip();
 
        return byteBuffer;
    }
}
Offline trollwarrior1
« Reply #1 - Posted 2015-02-08 18:37:57 »

Are you looking only for Windows or also other platforms?
Offline Ed_RockStarGuy
« Reply #2 - Posted 2015-02-08 18:39:04 »

Mac, Linux and Windows
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline CopyableCougar4
« Reply #3 - Posted 2015-02-08 18:47:04 »

https://github.com/alanhorizon/Java-Slick/blob/master/Slick/src/org/newdawn/slick/TrueTypeFont.java

https://github.com/CopyableCougar4/Font-Rendering/tree/master/src/com/digiturtle/ui
(more complex but not slick dependent)

Either wandering the forum or programming. Most likely the latter Smiley

Github: http://github.com/CopyableCougar4
Offline wessles

JGO Kernel


Medals: 326
Projects: 4


All coding knowledge will be useless at peak oil.


« Reply #4 - Posted 2015-02-08 18:48:19 »

It depends on what format you want.

Bitmap is pretty easy to implement if you are going monospace. Thecodinguniverse made an excellent tutorial on this here.

If you don't want monospace and need to resize, TrueTypeFont is the way to go. You can use ttf libraries for java, or render the font to a BufferedImage using AWT, then convert it to a texture like bitmaps. I did this in Mercury, based off of Slick2D's original implementation, and it works quite well, although is probably slower than using a full library for it.

Just remember for each of these techniques that you should throw a pixel or two of padding between all the characters in the font texture. OpenGL works differently on different platforms, it seems, and can result in texture bleeding.

RFLEX.
Offline Ed_RockStarGuy
« Reply #5 - Posted 2015-02-08 18:50:31 »

Okay thanks for the replies i shall take a look now and see how it goes
Offline Ed_RockStarGuy
« Reply #6 - Posted 2015-02-08 18:54:40 »

If you don't want monospace and need to resize, TrueTypeFont is the way to go. You can use ttf libraries for java, or render the font to a BufferedImage using AWT, then convert it to a texture like bitmaps. I did this in Mercury, based off of Slick2D's original implementation, and it works quite well, although is probably slower than using a full library for it.


Is it okay if i use your font code and adapt it for my project?
Offline CopyableCougar4
« Reply #7 - Posted 2015-02-08 19:05:53 »

My second link is more efficient than my first link, because my second link doesn't create a buffered image per letter.

Either wandering the forum or programming. Most likely the latter Smiley

Github: http://github.com/CopyableCougar4
Offline wessles

JGO Kernel


Medals: 326
Projects: 4


All coding knowledge will be useless at peak oil.


« Reply #8 - Posted 2015-02-08 19:14:54 »

Is it okay if i use your font code and adapt it for my project?

Mercury is under the MIT license, so as long as you print the copyright, go crazy. However, @CopyableCougar4 just pointed out an improvement on the system that I could make; I would recommend his right now for performance. I will update Mercury's code on the next commit.

UPDATE:
The improved system is up on the unstable branch. Thanks Cougar!

RFLEX.
Offline Ed_RockStarGuy
« Reply #9 - Posted 2015-02-08 22:24:49 »

Thanks ill check it out
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline TheBoneJarmer

Junior Devvie


Medals: 6
Exp: 1-3 months


hi!


« Reply #10 - Posted 2015-02-09 00:37:36 »

Hey Ed!

I asked the same question a couple of weeks ago on the LWJGL forums and managed to write my own truetypefont class thanks to the help of some other users. Here's the url http://forum.lwjgl.org/index.php?topic=5573.0. I've attached to code as well. I don't know if you already settled down with something but maybe it can help others as well.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;

import static org.lwjgl.opengl.GL11.*;

public class Font {
   
   //Constants
   private final Map<Integer,String> CHARS = new HashMap<Integer,String>() {{
        put(0, "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
        put(1, "abcdefghijklmnopqrstuvwxyz");
        put(2, "0123456789");
        put(3, "ÄÖÜäöüß");
        put(4, " $+-*/=%\"'#@&_(),.;:?!\\|<>[]§`^~");
    }};
   
   //Variables
    private java.awt.Font font;
    private FontMetrics fontMetrics;
    private BufferedImage bufferedImage;
    private int fontTextureId;
   
    //Getters
    public float getFontImageWidth() {
        return (float) CHARS.values().stream().mapToDouble(e -> fontMetrics.getStringBounds(e, null).getWidth()).max().getAsDouble();
    }
    public float getFontImageHeight() {
        return (float) CHARS.keySet().size() * (this.getCharHeight());
    }
    public float getCharX(char c) {
        String originStr = CHARS.values().stream().filter(e -> e.contains("" + c)).findFirst().orElse("" + c);
        return (float) fontMetrics.getStringBounds(originStr.substring(0, originStr.indexOf(c)), null).getWidth();
    }
    public float getCharY(char c) {
        float lineId = (float) CHARS.keySet().stream().filter(i -> CHARS.get(i).contains("" + c)).findFirst().orElse(0);
        return this.getCharHeight() * lineId;
    }
    public float getCharWidth(char c) {
        return fontMetrics.charWidth(c);
    }
    public float getCharHeight() {
        return (float) (fontMetrics.getMaxAscent() + fontMetrics.getMaxDescent());
    }
   
    //Constructors
    public Font(String path, float size) throws Exception {
        this.font = java.awt.Font.createFont(java.awt.Font.TRUETYPE_FONT, new File(path)).deriveFont(size);
       
        //Generate buffered image
        GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
        Graphics2D graphics = gc.createCompatibleImage(1, 1, Transparency.TRANSLUCENT).createGraphics();
        graphics.setFont(font);
       
        fontMetrics = graphics.getFontMetrics();
        bufferedImage = graphics.getDeviceConfiguration().createCompatibleImage((int) getFontImageWidth(),(int) getFontImageHeight(),Transparency.TRANSLUCENT);
       
      //Generate texture
      fontTextureId = glGenTextures();
        glEnable(GL_TEXTURE_2D);
        glBindTexture(GL_TEXTURE_2D, fontTextureId);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,(int) getFontImageWidth(),(int) getFontImageHeight(),0, GL_RGBA, GL_UNSIGNED_BYTE, asByteBuffer());
 
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    }
   
    //Functions
    public void drawText(String text, int x, int y) {
        glBindTexture(GL_TEXTURE_2D, this.fontTextureId);
        glBegin(GL_QUADS);
       
        int xTmp = x;
        for (char c : text.toCharArray()) {
            float width = getCharWidth(c);
            float height = getCharHeight();
            float cw = 1f / getFontImageWidth() * width;
            float ch = 1f / getFontImageHeight() * height;
            float cx = 1f / getFontImageWidth() * getCharX(c);
            float cy = 1f / getFontImageHeight() * getCharY(c);
 
            glTexCoord2f(cx, cy);
            glVertex3f(xTmp, y, 0);
 
            glTexCoord2f(cx + cw, cy);
            glVertex3f(xTmp + width, y, 0);
 
            glTexCoord2f(cx + cw, cy + ch);
            glVertex3f(xTmp + width, y + height, 0);
 
            glTexCoord2f(cx, cy + ch);
            glVertex3f(xTmp, y + height, 0);
 
            xTmp += width;
        }
       
        glEnd();
    }
   
    //Conversions
    public ByteBuffer asByteBuffer() {
 
        ByteBuffer byteBuffer;
     
        //Draw the characters on our image
        Graphics2D imageGraphics = (Graphics2D) bufferedImage.getGraphics();
        imageGraphics.setFont(font);
        imageGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
        imageGraphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
 
        // draw every CHAR by line...
        imageGraphics.setColor(java.awt.Color.WHITE);
        CHARS.keySet().stream().forEach(i -> imageGraphics.drawString(CHARS.get(i), 0, fontMetrics.getMaxAscent() + (this.getCharHeight() * i)));
       
        //Generate texture data
        int[] pixels = new int[bufferedImage.getWidth() * bufferedImage.getHeight()];
        bufferedImage.getRGB(0, 0, bufferedImage.getWidth(), bufferedImage.getHeight(), pixels, 0, bufferedImage.getWidth());
        byteBuffer = ByteBuffer.allocateDirect((bufferedImage.getWidth() * bufferedImage.getHeight() * 4));
 
        for (int y = 0; y < bufferedImage.getHeight(); y++) {
            for (int x = 0; x < bufferedImage.getWidth(); x++) {
                int pixel = pixels[y * bufferedImage.getWidth() + x];
                byteBuffer.put((byte) ((pixel >> 16) & 0xFF));   // Red component
                byteBuffer.put((byte) ((pixel >> 8) & 0xFF));    // Green component
                byteBuffer.put((byte) (pixel & 0xFF));           // Blue component
                byteBuffer.put((byte) ((pixel >> 24) & 0xFF));   // Alpha component. Only for RGBA
            }
        }
       
        byteBuffer.flip();
 
        return byteBuffer;
    }
}


I hope this can help you out!
TBJ
Offline Ed_RockStarGuy
« Reply #11 - Posted 2015-02-09 00:55:22 »

Hey Ed!

I asked the same question a couple of weeks ago on the LWJGL forums and managed to write my own truetypefont class thanks to the help of some other users. Here's the url http://forum.lwjgl.org/index.php?topic=5573.0. I've attached to code as well. I don't know if you already settled down with something but maybe it can help others as well.

I still could not find anything that would work to what i need, ill give your code a try, thanks
Offline Ed_RockStarGuy
« Reply #12 - Posted 2015-02-09 01:40:37 »

Hey Ed!

I asked the same question a couple of weeks ago on the LWJGL forums and managed to write my own truetypefont class thanks to the help of some other users. Here's the url http://forum.lwjgl.org/index.php?topic=5573.0. I've attached to code as well. I don't know if you already settled down with something but maybe it can help others as well.

Thanks man, i got it working using this! ill add you as a contributor to http://www.java-gaming.org/topics/iconified/35090/view.html
Offline SHC
« Reply #13 - Posted 2015-02-09 04:08:58 »

How on earth could you forget my implementation? Don't you want support for over-hanging glyphs?



The solution I went with, is to use Texture pages for the font, to add account for the padding to support overhanging glyphs. The padding is calculated automatically based on the font average width and the font size.

https://github.com/sriharshachilakapati/SilenceEngine/blob/master/src/main/java/com/shc/silenceengine/graphics/TrueTypeFont.java

Another thing, since this is using texture-pages, it is easy enough to convert this to a BitmapFont, and load from BMFont format. The only issue for you is the dependency on batcher, but I think you can easily convert it, because my Batcher simulates the immediate mode.

Offline TheBoneJarmer

Junior Devvie


Medals: 6
Exp: 1-3 months


hi!


« Reply #14 - Posted 2015-02-09 09:38:07 »

Hey Ed!

Thanks for the medal and a place as contributor! I appreciate that! But please, refer to Leycarno as well. He's a member from the LWJGL community and wrote the original font code for me. The code I gave to you was just a cleaned up version of it.

@SHC

I didn't refer to your code because he did not mentioned he want features like over-hanging glyphs. As far as I know, he just wanted to display small readable text on the screen for things like dialogs, hp, player names. No fancy fonts. That is why I posted my code and not yours because my code only contains required methods and variables to render text.
Offline Ed_RockStarGuy
« Reply #15 - Posted 2015-02-09 14:57:47 »

Yeah thats right, i could not for the life of me find a simple implementation for rendered text anywhere xD
Offline jmguillemette
« Reply #16 - Posted 2015-02-09 17:36:26 »

Im noticing that the sample code in this thread is all old openGL.
Do you want a sample using shaders + modern openGL?

Msg me if thats what you need and ill post up a chunk here.


-=Like a post.. give the author a medal!=-
Offline divxdede
« Reply #17 - Posted 2015-02-09 19:33:21 »

Most of sample codes don't handle unicode charsets that is not trivial with theses 250K valid codepoints (1 000 000 total codepoints).
If you want a general fonts system, it's a pain to write it.

i will give it a try soon !!!

Offline Ed_RockStarGuy
« Reply #18 - Posted 2015-02-09 19:49:46 »

Im noticing that the sample code in this thread is all old openGL.
Do you want a sample using shaders + modern openGL?

Msg me if thats what you need and ill post up a chunk here.

Yeah man, that would be appropriated, the code works but has some performance issues
Offline minigame

Senior Devvie


Medals: 14
Projects: 1


LWJGLException


« Reply #19 - Posted 2015-02-11 00:30:26 »

Hey Ed!

I asked the same question a couple of weeks ago on the LWJGL forums and managed to write my own truetypefont class thanks to the help of some other users. Here's the url http://forum.lwjgl.org/index.php?topic=5573.0. I've attached to code as well. I don't know if you already settled down with something but maybe it can help others as well.

.. code here ..

I hope this can help you out!
TBJ

Is there any reason that drawing more than 10 strings effects FPS so dramatically? I went from 60fps to 3fps drawing 50 test strings. The rest of the application I tested this in is well written so I don't know why the font class would effect frame count so much.

while (!deadFromWork) {
    if (hasItem("Dry Erase Board")) {
        chanceOfSuccess++;// Based on a true story..
    }
}
Offline trollwarrior1
« Reply #20 - Posted 2015-02-11 07:32:32 »

@minigame

You are not supposed to be drawing 50 strings each frame. You are supposed to render text to a texture which you draw every frame and only render text to textures again once the text changes.
Offline minigame

Senior Devvie


Medals: 14
Projects: 1


LWJGLException


« Reply #21 - Posted 2015-02-12 22:33:38 »

@minigame

You are not supposed to be drawing 50 strings each frame. You are supposed to render text to a texture which you draw every frame and only render text to textures again once the text changes.

Oh, right. Well I implemented a TrueTypeFont class and GLFont for managing custom font instances and drawing them. With a few "logical fonts" integrated into the class.

It's not the best, but it's pretty efficient. Not a single loss of frame count, even when drawing 500+ strings per frame.

Source:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
161  
162  
163  
164  
165  
166  
167  
168  
169  
170  
171  
172  
173  
174  
175  
176  
177  
178  
179  
180  
181  
182  
183  
184  
185  
186  
187  
188  
189  
190  
191  
192  
193  
194  
195  
196  
197  
198  
199  
200  
201  
202  
203  
204  
205  
206  
207  
208  
209  
210  
211  
212  
213  
214  
215  
216  
217  
218  
219  
220  
221  
222  
223  
224  
225  
226  
227  
228  
229  
230  
231  
232  
233  
234  
235  
236  
237  
238  
239  
240  
241  
242  
243  
244  
245  
246  
247  
248  
249  
250  
251  
252  
253  
254  
255  
256  
257  
258  
259  
260  
261  
262  
263  
264  
265  
266  
267  
268  
269  
270  
271  
272  
273  
274  
275  
276  
277  
278  
279  
280  
281  
282  
283  
284  
285  
286  
287  
288  
289  
290  
291  
292  
293  
294  
295  
296  
297  
298  
299  
300  
301  
302  
303  
304  
305  
306  
307  
308  
309  
310  
311  
312  
313  
314  
315  
316  
317  
318  
319  
320  
321  
322  
323  
324  
325  
326  
327  
328  
329  
330  
331  
332  
333  
334  
335  
336  
337  
338  
339  
340  
341  
342  
343  
344  
345  
346  
347  
348  
349  
350  
351  
352  
353  
354  
355  
356  
357  
358  
359  
360  
361  
362  
363  
364  
365  
366  
367  
368  
369  
370  
371  
372  
373  
374  
375  
376  
377  
378  
379  
380  
381  
382  
383  
384  
385  
386  
387  
388  
389  
390  
391  
392  
393  
394  
395  
396  
397  
398  
399  
400  
401  
402  
403  
404  
405  
406  
407  
408  
409  
410  
411  
412  
413  
414  
415  
416  
417  
418  
419  
420  
421  
422  
423  
424  
425  
426  
427  
428  
429  
430  
431  
432  
433  
434  
435  
436  
437  
438  
439  
440  
441  
442  
443  
444  
445  
446  
447  
448  
449  
450  
451  
452  
453  
454  
455  
456  
457  
458  
459  
460  
461  
462  
463  
464  
465  
466  
467  
468  
package com.javarpg.twod.font;

import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferInt;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.glu.GLU;

/**
 * A TrueType font implementation originally for Slick, edited for Bobjob's
 * Engine by David Aaron Muhar (bobjob), then edited by Yousef Amar
 * (Paraknight).
 *
 * Further modified on 2/11/2015 for use with the Two-D lwjgl 2D wrapper, by Minigame.
 *
 * @author James Chambers (Jimmy)
 * @author Jeremy Adams (elias4444)
 * @author Kevin Glass (kevglass)
 * @author Peter Korzuszek (genail)
 * @author David Aaron Muhar (bobjob)
 * @author Yousef Amar (Paraknight)
 * @author Corey K****** (Minigame)
 */

public class TrueTypeFont {
   
   public final static int ALIGN_LEFT = 0, ALIGN_RIGHT = 1, ALIGN_CENTER = 2;
   /** Array that holds necessary information about the font characters */
   private IntObject[] charArray = new IntObject[256];

   /** Map of user defined font characters (Character <-> IntObject) */
   private Map<Character, IntObject> customChars = new HashMap<Character, IntObject>();

   /** Boolean flag on whether AntiAliasing is enabled or not */
   private boolean antiAlias;

   /** Font's size */
   private int fontSize = 0;

   /** Font's height */
   private int fontHeight = 0;

   /** Texture used to cache the font 0-255 characters */
   private int fontTextureID;

   /** Default font texture width */
   private int textureWidth = 512;

   /** Default font texture height */
   private int textureHeight = 512;

   /** A reference to Java's AWT Font that we create our font texture from */
   private Font font;

   /** The font metrics for our Java AWT font */
   private FontMetrics fontMetrics;

   private int correctL = 9, correctR = 8;

   // TODO : create a texture cache in GLFont specifically for font textures!
   // we will need to enable alpha for the texture instance, so that we can draw Strings
   // without having to call gl blend/alpha settings locally in the TTF class.
   // until that is done, there could be compatibility issues with games powered by
   // Two-D, or possible unnecessary overhead due to settings not being disabled
   private static AtomicInteger fontId = new AtomicInteger(0);

   private class IntObject {
      /** Character's width */
      public int width;

      /** Character's height */
      public int height;

      /** Character's stored x position */
      public int storedX;

      /** Character's stored y position */
      public int storedY;
   }

   public TrueTypeFont(Font font, boolean antiAlias, char[] additionalChars) {
      this.font = font;
      this.fontSize = font.getSize() + 3;
      this.antiAlias = antiAlias;

      createSet(additionalChars);

      fontHeight -= 1;
      if (fontHeight <= 0)
         fontHeight = 1;
   }

   public TrueTypeFont(Font font, boolean antiAlias) {
      this(font, antiAlias, null);
   }

   public void setCorrection(boolean on) {
      if (on) {
         correctL = 2;
         correctR = 1;
      } else {
         correctL = 0;
         correctR = 0;
      }
   }

   private BufferedImage getFontImage(char ch) {
      // Create a temporary image to extract the character's size
      BufferedImage tempfontImage = new BufferedImage(1, 1,
            BufferedImage.TYPE_INT_ARGB);
      Graphics2D g = (Graphics2D) tempfontImage.getGraphics();
      if (antiAlias == true) {
         g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
               RenderingHints.VALUE_ANTIALIAS_ON);
      }
      g.setFont(font);
      fontMetrics = g.getFontMetrics();
      int charwidth = fontMetrics.charWidth(ch) + 8;

      if (charwidth <= 0) {
         charwidth = 7;
      }
      int charheight = fontMetrics.getHeight() + 3;
      if (charheight <= 0) {
         charheight = fontSize;
      }

      // Create another image holding the character we are creating
      BufferedImage fontImage;
      fontImage = new BufferedImage(charwidth, charheight,
            BufferedImage.TYPE_INT_ARGB);
      Graphics2D gt = (Graphics2D) fontImage.getGraphics();
      if (antiAlias == true) {
         gt.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
      }
      gt.setFont(font);
      gt.setColor(Color.WHITE);
      int charx = 3;
      int chary = 1;
      gt.drawString(String.valueOf(ch), (charx), (chary) + fontMetrics.getAscent());
      return fontImage;

   }

   private void createSet(char[] customCharsArray) {
      // If there are custom chars then I expand the font texture twice
      if (customCharsArray != null && customCharsArray.length > 0) {
         textureWidth *= 2;
      }

      // In any case this should be done in other way. Texture with size
      // 512x512
      // can maintain only 256 characters with resolution of 32x32. The
      // texture
      // size should be calculated dynamicaly by looking at character sizes.

      try {

         BufferedImage imgTemp = new BufferedImage(textureWidth, textureHeight, BufferedImage.TYPE_INT_ARGB);
         Graphics2D g = (Graphics2D) imgTemp.getGraphics();

         g.setColor(new Color(0, 0, 0, 1));
         g.fillRect(0, 0, textureWidth, textureHeight);

         int rowHeight = 0;
         int positionX = 0;
         int positionY = 0;

         int customCharsLength = (customCharsArray != null) ? customCharsArray.length : 0;

         for (int i = 0; i < 256 + customCharsLength; i++) {

            // get 0-255 characters and then custom characters
            char ch = (i < 256) ? (char) i : customCharsArray[i - 256];

            BufferedImage fontImage = getFontImage(ch);

            IntObject newIntObject = new IntObject();

            newIntObject.width = fontImage.getWidth();
            newIntObject.height = fontImage.getHeight();

            if (positionX + newIntObject.width >= textureWidth) {
               positionX = 0;
               positionY += rowHeight;
               rowHeight = 0;
            }

            newIntObject.storedX = positionX;
            newIntObject.storedY = positionY;

            if (newIntObject.height > fontHeight) {
               fontHeight = newIntObject.height;
            }

            if (newIntObject.height > rowHeight) {
               rowHeight = newIntObject.height;
            }

            // Draw it here
            g.drawImage(fontImage, positionX, positionY, null);

            positionX += newIntObject.width;

            if (i < 256) { // standard characters
               charArray[i] = newIntObject;
            } else { // custom characters
               customChars.put(new Character(ch), newIntObject);
            }

            fontImage = null;
         }

         fontTextureID = loadImage(imgTemp);

         // .getTexture(font.toString(), imgTemp);

      } catch (Exception e) {
         System.err.println("Failed to create font.");
         e.printStackTrace();
      }
   }

   private void drawQuad(float drawX, float drawY, float drawX2, float drawY2, float srcX, float srcY, float srcX2, float srcY2) {
      float DrawWidth = drawX2 - drawX;
      float DrawHeight = drawY2 - drawY;
      float TextureSrcX = srcX / textureWidth;
      float TextureSrcY = srcY / textureHeight;
      float SrcWidth = srcX2 - srcX;
      float SrcHeight = srcY2 - srcY;
      float RenderWidth = (SrcWidth / textureWidth);
      float RenderHeight = (SrcHeight / textureHeight);

      GL11.glTexCoord2f(TextureSrcX, TextureSrcY);
      GL11.glVertex2f(drawX, drawY + DrawHeight);
      GL11.glTexCoord2f(TextureSrcX + RenderWidth, TextureSrcY);
      GL11.glVertex2f(drawX + DrawWidth, drawY + DrawHeight);
      GL11.glTexCoord2f(TextureSrcX + RenderWidth, TextureSrcY + RenderHeight);
      GL11.glVertex2f(drawX + DrawWidth, drawY);
      GL11.glTexCoord2f(TextureSrcX, TextureSrcY + RenderHeight);
      GL11.glVertex2f(drawX, drawY);
   }

   public int getWidth(String whatchars) {
      int totalwidth = 0;
      IntObject intObject = null;
      int currentChar = 0;
      for (int i = 0; i < whatchars.length(); i++) {
         currentChar = whatchars.charAt(i);
         if (currentChar < 256) {
            intObject = charArray[currentChar];
         } else {
            intObject = (IntObject) customChars.get(new Character((char) currentChar));
         }
         if (intObject != null) {
            totalwidth += intObject.width;
         }
      }
      return totalwidth;
   }

   public int getHeight() {
      return fontHeight;
   }

   public int getHeight(String HeightString) {
      return fontHeight;
   }

   public int getLineHeight() {
      return fontHeight;
   }

   public void drawString(float x, float y, String whatchars, float scaleX, float scaleY) {
      drawString(x, y, whatchars, 0, whatchars.length() - 1, scaleX, scaleY, ALIGN_LEFT);
   }

   public void drawString(float x, float y, String whatchars, float scaleX, float scaleY, int format) {
      drawString(x, y, whatchars, 0, whatchars.length() - 1, scaleX, scaleY, format);
   }

   public void drawString(float x, float y, String whatchars, int startIndex, int endIndex, float scaleX, float scaleY, int format) {
      IntObject intObject = null;
      int charCurrent;
      int totalwidth = 0;
      int i = startIndex, d, c;
      float startY = 0;

      switch (format) {
      case ALIGN_RIGHT: {
         d = -1;
         c = correctR;
         while (i < endIndex) {
            if (whatchars.charAt(i) == '\n') {
               startY -= fontHeight;
            }
            i++;
         }
      }
      case ALIGN_CENTER: {
         for (int l = startIndex; l <= endIndex; l++) {
            charCurrent = whatchars.charAt(l);
            if (charCurrent == '\n') {
               break;
            }
            if (charCurrent < 256) {
               intObject = charArray[charCurrent];
            } else {
               intObject = (IntObject) customChars.get(new Character((char) charCurrent));
            }
            totalwidth += intObject.width - correctL;
         }
         totalwidth /= -2;
      }
      case ALIGN_LEFT:
         default:
            d = 1;
            c = correctL;
            break;
      }

      GL11.glEnable(GL11.GL_TEXTURE_2D);
      GL11.glEnable(GL11.GL_BLEND);
      GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
      GL11.glBindTexture(GL11.GL_TEXTURE_2D, fontTextureID);
      //TextureManager.getInstance().getTextureById(fontTextureID).bind();
      GL11.glBegin(GL11.GL_QUADS);

      while (i >= startIndex && i <= endIndex) {
         charCurrent = whatchars.charAt(i);
         if (charCurrent < 256) {
            intObject = charArray[charCurrent];
         } else {
            intObject = (IntObject) customChars.get(new Character((char) charCurrent));
         }
         if (intObject != null) {
            if (d < 0)
               totalwidth += (intObject.width - c) * d;
            if (charCurrent == '\n') {
               startY -= fontHeight * d;
               totalwidth = 0;
               if (format == ALIGN_CENTER) {
                  for (int l = i + 1; l <= endIndex; l++) {
                     charCurrent = whatchars.charAt(l);
                     if (charCurrent == '\n')
                        break;
                     if (charCurrent < 256) {
                        intObject = charArray[charCurrent];
                     } else {
                        intObject = (IntObject) customChars
                              .get(new Character((char) charCurrent));
                     }
                     totalwidth += intObject.width - correctL;
                  }
                  totalwidth /= -2;
               }
               // if center get next lines total width/2;
            } else {
               drawQuad((totalwidth + intObject.width) * scaleX + x,
                     startY * scaleY + y, totalwidth * scaleX + x,
                     (startY + intObject.height) * scaleY + y,
                     intObject.storedX + intObject.width,
                     intObject.storedY + intObject.height,
                     intObject.storedX, intObject.storedY);
               if (d > 0)
                  totalwidth += (intObject.width - c) * d;
            }
            i += d;

         }
      }
      GL11.glEnd();
      //GL11.glDisable(GL11.GL_BLEND);
      //GL11.glDisable(GL11.GL_TEXTURE_2D);
   }

   public static int loadImage(BufferedImage bufferedImage) {
      // TODO : store the bufferedimage as a com.javarpg.twod.Texture instance
      // in the GLFont class. we need to keep track of font textures,
      // but cannot store them locally in the TTF class..
      try {
         short width = (short) bufferedImage.getWidth();
         short height = (short) bufferedImage.getHeight();
         // textureLoader.bpp = bufferedImage.getColorModel().hasAlpha() ?
         // (byte)32 : (byte)24;
         int bpp = (byte) bufferedImage.getColorModel().getPixelSize();
         ByteBuffer byteBuffer;
         DataBuffer db = bufferedImage.getData().getDataBuffer();
         if (db instanceof DataBufferInt) {
            int intI[] = ((DataBufferInt) (bufferedImage.getData().getDataBuffer())).getData();
            byte newI[] = new byte[intI.length * 4];
            for (int i = 0; i < intI.length; i++) {
               byte b[] = intToByteArray(intI[i]);
               int newIndex = i * 4;
               newI[newIndex] = b[1];
               newI[newIndex + 1] = b[2];
               newI[newIndex + 2] = b[3];
               newI[newIndex + 3] = b[0];
            }
            byteBuffer = ByteBuffer.allocateDirect(width * height * (bpp / 8)).order(ByteOrder.nativeOrder()).put(newI);
         } else {
            byteBuffer = ByteBuffer.allocateDirect(width * height * (bpp / 8)).order(ByteOrder.nativeOrder()).put(((DataBufferByte) (bufferedImage.getData() .getDataBuffer())).getData());
         }
         byteBuffer.flip();

         int internalFormat = GL11.GL_RGBA8, format = GL11.GL_RGBA;
         IntBuffer textureId = BufferUtils.createIntBuffer(1);
         
         GL11.glGenTextures(textureId);
         GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureId.get(0));

         GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP);
         GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP);

         GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
         GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);

         GL11.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);

         GLU.gluBuild2DMipmaps(GL11.GL_TEXTURE_2D, internalFormat, width, height, format, GL11.GL_UNSIGNED_BYTE, byteBuffer);
         return textureId.get(0);

      } catch (Exception e) {
         e.printStackTrace();
         System.exit(-1);
      }
      return -1;
   }

   public static boolean isSupported(String fontname) {
      Font font[] = getFonts();
      for (int i = font.length - 1; i >= 0; i--) {
         if (font[i].getName().equalsIgnoreCase(fontname)) {
            return true;
         }
      }
      return false;
   }

   public static Font[] getFonts() {
      return GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
   }

   public static byte[] intToByteArray(int value) {
      return new byte[] { (byte) (value >>> 24), (byte) (value >>> 16), (byte) (value >>> 8), (byte) value };
   }

   public void destroy() {
      IntBuffer scratch = BufferUtils.createIntBuffer(1);
      scratch.put(0, fontTextureID);
      GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
      GL11.glDeleteTextures(scratch);
   }
}


1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
package com.javarpg.twod.font;

import java.awt.Font;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;

import com.javarpg.twod.texture.Texture;

public class GLFont {

   private static final GLFont INSTANCE = new GLFont();

   /*
    * A list of "logical fonts", based on an article from the oracle website.
    */

   private final TrueTypeFont logicalFonts[] = new TrueTypeFont[5];
   private final String fontNames[] = { "Dialog", "DialogInput", "Monospaced", "Serif", "SansSerif" };

   /*
    * A list of custom fonts which the Two-D developer can implement.
    */

   private Map<String, TrueTypeFont> customFonts = new HashMap<String, TrueTypeFont>();

   // TODO store a texture instance of the custom fonts, instead of using a seperate system.
   // If you are using slick2D, just ignore this. I use my own texture/texturemanager classes
   // as part of the learning process for me is to not use slick2d
   private List<Texture> fontTextures = new CopyOnWriteArrayList<Texture>();
   
   public enum FontType {
      Dialog, DialogInput, Monospaced, Serif, SansSerif
   }

   public GLFont() {
      for (int i = 0; i < logicalFonts.length; i++) {
         logicalFonts[i] = new TrueTypeFont(new Font(fontNames[i], Font.PLAIN, 12), true);
      }
     
      // just testing custom fonts
      addCustomFont("Arial", false, 12, true);
      addCustomFont("Helvetica", false, 12, true);
   }

   /**
    * Draws a default logicalFont. The list includes a small array of Two-D integrated fonts, see the {@link #GLFont.FontType} enum
    * to see which fonts are available by default. Note Arail and Helvetica and be drawn using drawCustomString (as they are
    * both integrated custom fonts, but not listed as a logic font or part of the FonType enum)
    *
    * @param string The string to be rendered.
    * @param x The x coordinate in 2D space.
    * @param y The y coordinate in 2D space.
    * @param scaleX The scale (size) you want to use while drawing the string.
    * @param scaleY The scale (size) you want to use while drawing the string.
    * @param format The string format. 0 = align left, 1 = align right, 2 = align center
    * @param customFont The name of the custom font. This would have been set when adding a
    * new font using {@link #addCustomFont(String, boolean, int, boolean)}
    * @param fontType See {@link #GLFont.FontType} enum for a list of default integrated logical fonts.
    */

   public void drawString(String string, int x, int y, int scaleX, int scaleY, int format, FontType fontType) {
      switch (fontType) {
      case Dialog:
         logicalFonts[0].drawString(x, y, string, scaleX, scaleY, format);
         break;
      case DialogInput:
         logicalFonts[1].drawString(x, y, string, scaleX, scaleY, format);
         break;
      case Monospaced:
         logicalFonts[2].drawString(x, y, string, scaleX, scaleY, format);
         break;
      case Serif:
         logicalFonts[3].drawString(x, y, string, scaleX, scaleY, format);
         break;
      case SansSerif:
         logicalFonts[4].drawString(x, y, string, scaleX, scaleY, format);
         break;
      default:
         System.err.println("Unknown font type selected!");
         System.exit(1);
         break;
      }
   }

   /**
    * Adds a custom font to memory using the given variables. This essentially just creates a java.awt.Font
    * and converts it to a TrueFontType instance for lwjgl display.
    *
    * @param fontName The name of the given font. This will use a java.awt.Font instance to create
    * an lwjgl suitable TrueTypeFont.
    * @param bold Whether or not the font will be bold.
    * @param fontSize The size of the font.
    * @param antiAlias Whether or not you want to use anti aliasing for the custom font.
    */

   public void addCustomFont(String fontName, boolean bold, int fontSize, boolean antiAlias) {
      try {
         TrueTypeFont ttf = new TrueTypeFont(new Font(fontName, bold ? Font.BOLD : Font.PLAIN, fontSize), antiAlias);
         customFonts.put(fontName.toLowerCase(), ttf);
      } catch (Exception e) {
         e.printStackTrace();
         System.err.println("Error adding custom font: " + fontName);
      }
   }

   /**
    * Draws a String at the given x, y coordinates, using the specified customFont type.
    *
    * @param string The string to be rendered.
    * @param x The x coordinate in 2D space.
    * @param y The y coordinate in 2D space.
    * @param format The string format. 0 = align left, 1 = align right, 2 = align center
    * @param customFont The name of the custom font. This would have been set when adding a
    * new font using {@link #addCustomFont(String, boolean, int, boolean)}
    */

   public void drawString(String string, int x, int y, int format, String customFont) {
      try {
         customFonts.get(customFont.toLowerCase()).drawString(x, y, string, 1, 1, format);
      } catch (Exception e) {
         e.printStackTrace();
         System.err.println("Error drawing string using custom font: " + customFont);
      }
   }

   /**
    *
    * Draws a String at the given x, y coordinates, using the specified customFont type.
    *
    * @param string The string to be rendered.
    * @param x The x coordinate in 2D space.
    * @param y The y coordinate in 2D space.
    * @param scaleX The scale (size) you want to use while drawing the string.
    * @param scaleY The scale (size) you want to use while drawing the string.
    * @param format The string format. 0 = align left, 1 = align right, 2 = align center
    * @param customFont The name of the custom font. This would have been set when adding a
    * new font using {@link #addCustomFont(String, boolean, int, boolean)}
    */

   public void drawString(String string, int x, int y, int scaleX, int scaleY, int format, String customFont) {
      try {
         customFonts.get(customFont.toLowerCase()).drawString(x, y, string, scaleX, scaleY, format);
      } catch (Exception e) {
         e.printStackTrace();
         System.err.println("Error drawing string using custom font: " + customFont);
      }
   }

   public static GLFont getInstance() {
      return INSTANCE;
   }

}

while (!deadFromWork) {
    if (hasItem("Dry Erase Board")) {
        chanceOfSuccess++;// Based on a true story..
    }
}
Offline CopyableCougar4
« Reply #22 - Posted 2015-02-13 00:34:58 »

1  
BufferedImage fontImage = getFontImage(ch);


Why do I see so many people doing this?

You only need to create that one image, use font metrics to get dimensions and render the character to the buffered image. If you just draw to one image, it decreases the load time per-font dramatically from 513+ images to one image created. While making 513+ buffered images is fine for one or two fonts, if you need to create a lot of fonts, then you are just adding unnecessary load time.

Even if you want font images to possibly be bigger than 512x512, the most images you should need is two (one for scratch and finding out how big the second one is, and the other for the font).

Either wandering the forum or programming. Most likely the latter Smiley

Github: http://github.com/CopyableCougar4
Offline wessles

JGO Kernel


Medals: 326
Projects: 4


All coding knowledge will be useless at peak oil.


« Reply #23 - Posted 2015-02-13 04:28:35 »

Because everyone copied Slick2D Tongue.

RFLEX.
Offline CopyableCougar4
« Reply #24 - Posted 2015-02-13 04:55:07 »

Quote
Because everyone copied Slick2D Tongue.
Well I wish people would realize the optimization to font loading that could be done Tongue

Either wandering the forum or programming. Most likely the latter Smiley

Github: http://github.com/CopyableCougar4
Offline minigame

Senior Devvie


Medals: 14
Projects: 1


LWJGLException


« Reply #25 - Posted 2015-02-17 20:12:46 »

Because everyone copied Slick2D Tongue.
Yeah, it's hard for beginners. But there's still a small amount of pride (and learning experience) compiling the snippets together into our own wrappers.

Quote
Because everyone copied Slick2D Tongue.
Well I wish people would realize the optimization to font loading that could be done Tongue
Feel free to explain in depth Wink

while (!deadFromWork) {
    if (hasItem("Dry Erase Board")) {
        chanceOfSuccess++;// Based on a true story..
    }
}
Offline CopyableCougar4
« Reply #26 - Posted 2015-02-17 21:53:36 »

minigame:

So the way Slick2D loads fonts is by:

1  
2  
3  
4  
5  
6  
7  
// load a backend buffered image for all the letters
// for (unicode characters 0-255) {
//         create an image for this letter that just calculates font metrics
//         create an image for this letter that draws with the font to it
//         draw the second image to the backend image
// }
// load the texture etc.

Resulting in 513 buffered images

But you can simply:
1  
2  
3  
4  
5  
6  
// load a backend buffered image for all the letters
// for (unicode characters 0-255) {
//         draw this letter at the correct position on the final image
//         adjust offsets per-character using backend's font metrics
// }
// load the texture etc

Resulting in 1 buffered image

Which should have a nice increase on font loading on anybody's system.

Either wandering the forum or programming. Most likely the latter Smiley

Github: http://github.com/CopyableCougar4
Offline CopyableCougar4
« Reply #27 - Posted 2015-02-17 22:01:01 »

Although if you really want a better speedup, then you can load the font once and then serialize character regions and RGBA data to feed directly to OpenGL and only create 1 buffered image ever per-font in a project.

Either wandering the forum or programming. Most likely the latter Smiley

Github: http://github.com/CopyableCougar4
Offline basil_

« JGO Bitwise Duke »


Medals: 418
Exp: 13 years



« Reply #28 - Posted 2015-02-18 10:33:32 »

i dont think that loading 3 to 5 fonts at startup will make a difference if it takes 800ms or 200ms.
Offline lcass
« Reply #29 - Posted 2015-02-18 11:16:10 »

Yer looking at all of that makes my eyes water , mine is an extremely crude but effective system that generates each letter from a bitmap onto a vbo.
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
public class TextGenerator {
   private String alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789. ";
   private spritesheet alphabetsprite;
   private Core core;
   public TextGenerator(Core core){
      this.core = core;
      alphabetsprite = new spritesheet("textures/font1.png");
   }
   public Progressive_buffer[] generate_text(String text , Vertex2d position,int size){
   
      if(text == null || text.length() == 0 || position == null){
         return null;
      }
      Progressive_buffer[] drawdata = new Progressive_buffer[2];
      drawdata[0] = new Progressive_buffer(null,false);
      drawdata[1] = new Progressive_buffer(null,true);
      for(int i = 0; i < text.length();i++){
         char charat = text.charAt(i);
         int code = alphabet.indexOf(charat);
         if(code == -1){
            continue;
         }
         
         Progressive_buffer[] tempdata = core.G.rectangle((int)(position.x + (i * size)) ,(int)(position.y),size,(int)(size * 1.5), alphabetsprite.getcoords(code * 6, 0, (code+1)*6, 8));
         
         
         drawdata[0].extend(tempdata[0]);
         drawdata[1].extend(tempdata[1]);
   
     
     
      }
     
     
      return drawdata;
   }
   public Texture gettexture(){
      return alphabetsprite.gettexture();
   }
   public spritesheet getsprite(){
      return alphabetsprite;
   }
}

literally iterates over a string and then converts the product into a vbo. Don't know much about its performance but I haven't had any issues with it. It only works with fonts that have the same width though , bit of a downside could probably implement that.
Pages: [1] 2
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 
Ecumene (49 views)
2017-09-30 02:57:34

theagentd (74 views)
2017-09-26 18:23:31

cybrmynd (183 views)
2017-08-02 12:28:51

cybrmynd (181 views)
2017-08-02 12:19:43

cybrmynd (188 views)
2017-08-02 12:18:09

Sralse (195 views)
2017-07-25 17:13:48

Archive (745 views)
2017-04-27 17:45:51

buddyBro (878 views)
2017-04-05 03:38:00

CopyableCougar4 (1428 views)
2017-03-24 15:39:42

theagentd (1317 views)
2017-03-24 15:32:08
List of Learning Resources
by elect
2017-03-13 14:05:44

List of Learning Resources
by elect
2017-03-13 14:04:45

SF/X Libraries
by philfrei
2017-03-02 08:45:19

SF/X Libraries
by philfrei
2017-03-02 08:44:05

SF/X Libraries
by SkyAphid
2017-03-02 06:38:56

SF/X Libraries
by SkyAphid
2017-03-02 06:38:32

SF/X Libraries
by SkyAphid
2017-03-02 06:38:05

SF/X Libraries
by SkyAphid
2017-03-02 06:37:51
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!