Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
epoxy
GitHub Repository: epoxy/proj11
Path: blob/master/SLICK_HOME/src/org/newdawn/slick/font/GlyphPage.java
1460 views
1
2
package org.newdawn.slick.font;
3
4
import java.awt.AlphaComposite;
5
import java.awt.Graphics2D;
6
import java.awt.RenderingHints;
7
import java.awt.font.FontRenderContext;
8
import java.awt.image.BufferedImage;
9
import java.awt.image.WritableRaster;
10
import java.nio.ByteBuffer;
11
import java.nio.ByteOrder;
12
import java.nio.IntBuffer;
13
import java.util.ArrayList;
14
import java.util.Iterator;
15
import java.util.List;
16
import java.util.ListIterator;
17
18
import org.lwjgl.opengl.GL11;
19
import org.lwjgl.opengl.GL12;
20
import org.newdawn.slick.Color;
21
import org.newdawn.slick.Image;
22
import org.newdawn.slick.SlickException;
23
import org.newdawn.slick.UnicodeFont;
24
import org.newdawn.slick.font.effects.Effect;
25
import org.newdawn.slick.opengl.TextureImpl;
26
import org.newdawn.slick.opengl.renderer.Renderer;
27
import org.newdawn.slick.opengl.renderer.SGL;
28
29
/**
30
* Stores a number of glyphs on a single texture.
31
*
32
* @author Nathan Sweet <[email protected]>
33
*/
34
public class GlyphPage {
35
/** The interface to OpenGL */
36
private static final SGL GL = Renderer.get();
37
38
/** The maxium size of an individual glyph */
39
public static final int MAX_GLYPH_SIZE = 256;
40
41
/** A temporary working buffer */
42
private static ByteBuffer scratchByteBuffer = ByteBuffer.allocateDirect(MAX_GLYPH_SIZE * MAX_GLYPH_SIZE * 4);
43
44
static {
45
scratchByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
46
}
47
48
/** A temporary working buffer */
49
private static IntBuffer scratchIntBuffer = scratchByteBuffer.asIntBuffer();
50
51
52
/** A temporary image used to generate the glyph page */
53
private static BufferedImage scratchImage = new BufferedImage(MAX_GLYPH_SIZE, MAX_GLYPH_SIZE, BufferedImage.TYPE_INT_ARGB);
54
/** The graphics context form the temporary image */
55
private static Graphics2D scratchGraphics = (Graphics2D)scratchImage.getGraphics();
56
57
static {
58
scratchGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
59
scratchGraphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
60
scratchGraphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
61
}
62
63
/** The render context in which the glyphs will be generated */
64
public static FontRenderContext renderContext = scratchGraphics.getFontRenderContext();
65
66
/**
67
* Get the scratch graphics used to generate the page of glyphs
68
*
69
* @return The scratch graphics used to build the page
70
*/
71
public static Graphics2D getScratchGraphics() {
72
return scratchGraphics;
73
}
74
75
/** The font this page is part of */
76
private final UnicodeFont unicodeFont;
77
/** The width of this page's image */
78
private final int pageWidth;
79
/** The height of this page's image */
80
private final int pageHeight;
81
/** The image containing the glyphs */
82
private final Image pageImage;
83
/** The x position of the page */
84
private int pageX;
85
/** The y position of the page */
86
private int pageY;
87
/** The height of the last row on the page */
88
private int rowHeight;
89
/** True if the glyphs are ordered */
90
private boolean orderAscending;
91
/** The list of glyphs on this page */
92
private final List pageGlyphs = new ArrayList(32);
93
94
/**
95
* Create a new page of glyphs
96
*
97
* @param unicodeFont The font this page forms part of
98
* @param pageWidth The width of the backing texture.
99
* @param pageHeight The height of the backing texture.
100
* @throws SlickException if the backing texture could not be created.
101
*/
102
public GlyphPage(UnicodeFont unicodeFont, int pageWidth, int pageHeight) throws SlickException {
103
this.unicodeFont = unicodeFont;
104
this.pageWidth = pageWidth;
105
this.pageHeight = pageHeight;
106
107
pageImage = new Image(pageWidth, pageHeight);
108
}
109
110
/**
111
* Loads glyphs to the backing texture and sets the image on each loaded glyph. Loaded glyphs are removed from the list.
112
*
113
* If this page already has glyphs and maxGlyphsToLoad is -1, then this method will return 0 if all the new glyphs don't fit.
114
* This reduces texture binds when drawing since glyphs loaded at once are typically displayed together.
115
* @param glyphs The glyphs to load.
116
* @param maxGlyphsToLoad This is the maximum number of glyphs to load from the list. Set to -1 to attempt to load all the
117
* glyphs.
118
* @return The number of glyphs that were actually loaded.
119
* @throws SlickException if the glyph could not be rendered.
120
*/
121
public int loadGlyphs (List glyphs, int maxGlyphsToLoad) throws SlickException {
122
if (rowHeight != 0 && maxGlyphsToLoad == -1) {
123
// If this page has glyphs and we are not loading incrementally, return zero if any of the glyphs don't fit.
124
int testX = pageX;
125
int testY = pageY;
126
int testRowHeight = rowHeight;
127
for (Iterator iter = getIterator(glyphs); iter.hasNext();) {
128
Glyph glyph = (Glyph)iter.next();
129
int width = glyph.getWidth();
130
int height = glyph.getHeight();
131
if (testX + width >= pageWidth) {
132
testX = 0;
133
testY += testRowHeight;
134
testRowHeight = height;
135
} else if (height > testRowHeight) {
136
testRowHeight = height;
137
}
138
if (testY + testRowHeight >= pageWidth) return 0;
139
testX += width;
140
}
141
}
142
143
Color.white.bind();
144
pageImage.bind();
145
146
int i = 0;
147
for (Iterator iter = getIterator(glyphs); iter.hasNext();) {
148
Glyph glyph = (Glyph)iter.next();
149
int width = Math.min(MAX_GLYPH_SIZE, glyph.getWidth());
150
int height = Math.min(MAX_GLYPH_SIZE, glyph.getHeight());
151
152
if (rowHeight == 0) {
153
// The first glyph always fits.
154
rowHeight = height;
155
} else {
156
// Wrap to the next line if needed, or break if no more fit.
157
if (pageX + width >= pageWidth) {
158
if (pageY + rowHeight + height >= pageHeight) break;
159
pageX = 0;
160
pageY += rowHeight;
161
rowHeight = height;
162
} else if (height > rowHeight) {
163
if (pageY + height >= pageHeight) break;
164
rowHeight = height;
165
}
166
}
167
168
renderGlyph(glyph, width, height);
169
pageGlyphs.add(glyph);
170
171
pageX += width;
172
173
iter.remove();
174
i++;
175
if (i == maxGlyphsToLoad) {
176
// If loading incrementally, flip orderAscending so it won't change, since we'll probably load the rest next time.
177
orderAscending = !orderAscending;
178
break;
179
}
180
}
181
182
TextureImpl.bindNone();
183
184
// Every other batch of glyphs added to a page are sorted the opposite way to attempt to keep same size glyps together.
185
orderAscending = !orderAscending;
186
187
return i;
188
}
189
190
/**
191
* Loads a single glyph to the backing texture, if it fits.
192
*
193
* @param glyph The glyph to be rendered
194
* @param width The expected width of the glyph
195
* @param height The expected height of the glyph
196
* @throws SlickException if the glyph could not be rendered.
197
*/
198
private void renderGlyph(Glyph glyph, int width, int height) throws SlickException {
199
// Draw the glyph to the scratch image using Java2D.
200
scratchGraphics.setComposite(AlphaComposite.Clear);
201
scratchGraphics.fillRect(0, 0, MAX_GLYPH_SIZE, MAX_GLYPH_SIZE);
202
scratchGraphics.setComposite(AlphaComposite.SrcOver);
203
scratchGraphics.setColor(java.awt.Color.white);
204
for (Iterator iter = unicodeFont.getEffects().iterator(); iter.hasNext();)
205
((Effect)iter.next()).draw(scratchImage, scratchGraphics, unicodeFont, glyph);
206
glyph.setShape(null); // The shape will never be needed again.
207
208
WritableRaster raster = scratchImage.getRaster();
209
int[] row = new int[width];
210
for (int y = 0; y < height; y++) {
211
raster.getDataElements(0, y, width, 1, row);
212
scratchIntBuffer.put(row);
213
}
214
GL11.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, pageX, pageY, width, height, GL12.GL_BGRA, GL11.GL_UNSIGNED_BYTE,
215
scratchByteBuffer);
216
scratchIntBuffer.clear();
217
218
glyph.setImage(pageImage.getSubImage(pageX, pageY, width, height));
219
}
220
221
/**
222
* Returns an iterator for the specified glyphs, sorted either ascending or descending.
223
*
224
* @param glyphs The glyphs to return if present
225
* @return An iterator of the sorted list of glyphs
226
*/
227
private Iterator getIterator(List glyphs) {
228
if (orderAscending) return glyphs.iterator();
229
final ListIterator iter = glyphs.listIterator(glyphs.size());
230
return new Iterator() {
231
public boolean hasNext () {
232
return iter.hasPrevious();
233
}
234
235
public Object next () {
236
return iter.previous();
237
}
238
239
public void remove () {
240
iter.remove();
241
}
242
};
243
}
244
245
/**
246
* Returns the glyphs stored on this page.
247
*
248
* @return A list of {@link Glyph} elements on this page
249
*/
250
public List getGlyphs () {
251
return pageGlyphs;
252
}
253
254
/**
255
* Returns the backing texture for this page.
256
*
257
* @return The image of this page of glyphs
258
*/
259
public Image getImage () {
260
return pageImage;
261
}
262
}
263
264