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.newdawn.slick.Color;
19 import org.newdawn.slick.Image;
20 import org.newdawn.slick.SlickException;
21 import org.newdawn.slick.UnicodeFont;
22 import org.newdawn.slick.font.effects.Effect;
23 import org.newdawn.slick.opengl.TextureImpl;
24 import org.newdawn.slick.opengl.renderer.Renderer;
25 import org.newdawn.slick.opengl.renderer.SGL;
26
27 import javax.annotation.Nonnull;
28
29
30
31
32
33
34 public class GlyphPage {
35
36 private static final SGL GL = Renderer.get();
37
38
39 public static final int MAX_GLYPH_SIZE = 256;
40
41
42 private static final ByteBuffer scratchByteBuffer = ByteBuffer.allocateDirect(MAX_GLYPH_SIZE * MAX_GLYPH_SIZE * 4);
43
44 static {
45 scratchByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
46 }
47
48
49 @Nonnull
50 private static final IntBuffer scratchIntBuffer = scratchByteBuffer.asIntBuffer();
51
52
53
54 @Nonnull
55 private static final BufferedImage scratchImage = new BufferedImage(MAX_GLYPH_SIZE, MAX_GLYPH_SIZE, BufferedImage.TYPE_INT_ARGB);
56
57 @Nonnull
58 private static final Graphics2D scratchGraphics = (Graphics2D)scratchImage.getGraphics();
59
60 static {
61 scratchGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
62 scratchGraphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
63 scratchGraphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
64 }
65
66
67 public static final FontRenderContext renderContext = scratchGraphics.getFontRenderContext();
68
69
70
71
72
73
74 @Nonnull
75 public static Graphics2D getScratchGraphics() {
76 return scratchGraphics;
77 }
78
79
80 private final UnicodeFont unicodeFont;
81
82 private final int pageWidth;
83
84 private final int pageHeight;
85
86 @Nonnull
87 private final Image pageImage;
88
89 private int pageX;
90
91 private int pageY;
92
93 private int rowHeight;
94
95 private boolean orderAscending;
96
97 private final List<Glyph> pageGlyphs = new ArrayList<>(32);
98
99
100
101
102
103
104
105
106
107 public GlyphPage(UnicodeFont unicodeFont, int pageWidth, int pageHeight) {
108 this.unicodeFont = unicodeFont;
109 this.pageWidth = pageWidth;
110 this.pageHeight = pageHeight;
111
112
113 pageImage = new Image(pageWidth, pageHeight);
114 }
115
116
117
118
119
120
121
122
123
124
125
126
127 public int loadGlyphs (@Nonnull List<Glyph> glyphs, int maxGlyphsToLoad) {
128 if (rowHeight != 0 && maxGlyphsToLoad == -1) {
129
130 int testX = pageX;
131 int testY = pageY;
132 int testRowHeight = rowHeight;
133 for (Iterator<?> iter = getIterator(glyphs); iter.hasNext();) {
134 Glyph glyph = (Glyph)iter.next();
135 int width = glyph.getWidth();
136 int height = glyph.getHeight();
137 if (testX + width >= pageWidth) {
138 testX = 0;
139 testY += testRowHeight;
140 testRowHeight = height;
141 } else if (height > testRowHeight) {
142 testRowHeight = height;
143 }
144 if (testY + testRowHeight >= pageWidth) return 0;
145 testX += width;
146 }
147 }
148
149 Color.white.bind();
150 pageImage.bind();
151
152 int i = 0;
153 for (Iterator<?> iter = getIterator(glyphs); iter.hasNext();) {
154 Glyph glyph = (Glyph)iter.next();
155 int width = Math.min(MAX_GLYPH_SIZE, glyph.getWidth());
156 int height = Math.min(MAX_GLYPH_SIZE, glyph.getHeight());
157
158 if (rowHeight == 0) {
159
160 rowHeight = height;
161 } else {
162
163 if (pageX + width >= pageWidth) {
164 if (pageY + rowHeight + height >= pageHeight) break;
165 pageX = 0;
166 pageY += rowHeight;
167 rowHeight = height;
168 } else if (height > rowHeight) {
169 if (pageY + height >= pageHeight) break;
170 rowHeight = height;
171 }
172 }
173
174 renderGlyph(glyph, width, height);
175 pageGlyphs.add(glyph);
176
177 pageX += width;
178
179 iter.remove();
180 i++;
181 if (i == maxGlyphsToLoad) {
182
183 orderAscending = !orderAscending;
184 break;
185 }
186 }
187
188 TextureImpl.bindNone();
189
190
191 orderAscending = !orderAscending;
192
193 return i;
194 }
195
196
197
198
199
200
201
202
203
204 private void renderGlyph(@Nonnull Glyph glyph, int width, int height) {
205
206 scratchGraphics.setComposite(AlphaComposite.Clear);
207 scratchGraphics.fillRect(0, 0, MAX_GLYPH_SIZE, MAX_GLYPH_SIZE);
208 scratchGraphics.setComposite(AlphaComposite.SrcOver);
209 scratchGraphics.setColor(java.awt.Color.white);
210 for (Effect effect : unicodeFont.getEffects()) (effect).draw(scratchImage, scratchGraphics, unicodeFont, glyph);
211 glyph.setShape(null);
212
213 WritableRaster raster = scratchImage.getRaster();
214 int[] row = new int[width];
215 for (int y = 0; y < height; y++) {
216 raster.getDataElements(0, y, width, 1, row);
217 scratchIntBuffer.put(row);
218 }
219 GL.glTexSubImage2D(SGL.GL_TEXTURE_2D, 0, pageX, pageY, width, height, SGL.GL_BGRA, SGL.GL_UNSIGNED_BYTE,
220 scratchByteBuffer);
221 scratchIntBuffer.clear();
222
223 glyph.setImage(pageImage.getSubImage(pageX, pageY, width, height));
224 }
225
226
227
228
229
230
231
232 @Nonnull
233 private Iterator<Glyph> getIterator(@Nonnull List<Glyph> glyphs) {
234 if (orderAscending) return glyphs.iterator();
235 final ListIterator<Glyph> iter = glyphs.listIterator(glyphs.size());
236 return new Iterator<Glyph>() {
237 public boolean hasNext () {
238 return iter.hasPrevious();
239 }
240
241 public Glyph next () {
242 return iter.previous();
243 }
244
245 public void remove () {
246 iter.remove();
247 }
248 };
249 }
250
251
252
253
254
255
256 @Nonnull
257 public List<Glyph> getGlyphs () {
258 return pageGlyphs;
259 }
260
261
262
263
264
265
266 @Nonnull
267 public Image getImage () {
268 return pageImage;
269 }
270 }