author | prr |
Wed, 09 Nov 2016 11:28:13 -0800 | |
changeset 42208 | 7c1017f0ade5 |
parent 39870 | 55d674017343 |
permissions | -rw-r--r-- |
2 | 1 |
/* |
23010
6dadb192ad81
8029235: Update copyright year to match last edit in jdk8 jdk repository for 2013
lana
parents:
23006
diff
changeset
|
2 |
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. |
2 | 3 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 |
* |
|
5 |
* This code is free software; you can redistribute it and/or modify it |
|
6 |
* under the terms of the GNU General Public License version 2 only, as |
|
5506 | 7 |
* published by the Free Software Foundation. Oracle designates this |
2 | 8 |
* particular file as subject to the "Classpath" exception as provided |
5506 | 9 |
* by Oracle in the LICENSE file that accompanied this code. |
2 | 10 |
* |
11 |
* This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 |
* version 2 for more details (a copy is included in the LICENSE file that |
|
15 |
* accompanied this code). |
|
16 |
* |
|
17 |
* You should have received a copy of the GNU General Public License version |
|
18 |
* 2 along with this work; if not, write to the Free Software Foundation, |
|
19 |
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 |
* |
|
5506 | 21 |
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
22 |
* or visit www.oracle.com if you need additional information or have any |
|
23 |
* questions. |
|
2 | 24 |
*/ |
25 |
||
26 |
package sun.font; |
|
27 |
||
28 |
import java.awt.Font; |
|
29 |
import java.awt.font.GlyphVector; |
|
30 |
import java.awt.font.FontRenderContext; |
|
39870
55d674017343
8054991: sun.font.GlyphList uses broken double-checked locking
prr
parents:
26037
diff
changeset
|
31 |
import java.util.concurrent.atomic.AtomicBoolean; |
2 | 32 |
import sun.java2d.loops.FontInfo; |
33 |
||
34 |
/* |
|
35 |
* This class represents a list of actual renderable glyphs. |
|
36 |
* It can be constructed from a number of text sources, representing |
|
37 |
* the various ways in which a programmer can ask a Graphics2D object |
|
38 |
* to render some text. Once constructed, it provides a way of iterating |
|
39 |
* through the device metrics and graybits of the individual glyphs that |
|
40 |
* need to be rendered to the screen. |
|
41 |
* |
|
42 |
* Note that this class holds pointers to native data which must be |
|
43 |
* disposed. It is not marked as finalizable since it is intended |
|
44 |
* to be very lightweight and finalization is a comparitively expensive |
|
45 |
* procedure. The caller must specifically use try{} finally{} to |
|
46 |
* manually ensure that the object is disposed after use, otherwise |
|
47 |
* native data structures might be leaked. |
|
48 |
* |
|
49 |
* Here is a code sample for using this class: |
|
50 |
* |
|
51 |
* public void drawString(String str, FontInfo info, float x, float y) { |
|
52 |
* GlyphList gl = GlyphList.getInstance(); |
|
53 |
* try { |
|
54 |
* gl.setFromString(info, str, x, y); |
|
55 |
* int strbounds[] = gl.getBounds(); |
|
56 |
* int numglyphs = gl.getNumGlyphs(); |
|
57 |
* for (int i = 0; i < numglyphs; i++) { |
|
58 |
* gl.setGlyphIndex(i); |
|
59 |
* int metrics[] = gl.getMetrics(); |
|
60 |
* byte bits[] = gl.getGrayBits(); |
|
61 |
* int glyphx = metrics[0]; |
|
62 |
* int glyphy = metrics[1]; |
|
63 |
* int glyphw = metrics[2]; |
|
64 |
* int glyphh = metrics[3]; |
|
65 |
* int off = 0; |
|
66 |
* for (int j = 0; j < glyphh; j++) { |
|
67 |
* for (int i = 0; i < glyphw; i++) { |
|
68 |
* int dx = glyphx + i; |
|
69 |
* int dy = glyphy + j; |
|
70 |
* int alpha = bits[off++]; |
|
71 |
* drawPixel(alpha, dx, dy); |
|
72 |
* } |
|
73 |
* } |
|
74 |
* } |
|
75 |
* } finally { |
|
76 |
* gl.dispose(); |
|
77 |
* } |
|
78 |
* } |
|
79 |
*/ |
|
80 |
public final class GlyphList { |
|
81 |
private static final int MINGRAYLENGTH = 1024; |
|
82 |
private static final int MAXGRAYLENGTH = 8192; |
|
83 |
private static final int DEFAULT_LENGTH = 32; |
|
84 |
||
85 |
int glyphindex; |
|
86 |
int metrics[]; |
|
87 |
byte graybits[]; |
|
88 |
||
89 |
/* A reference to the strike is needed for the case when the GlyphList |
|
90 |
* may be added to a queue for batch processing, (e.g. OpenGL) and we need |
|
91 |
* to be completely certain that the strike is still valid when the glyphs |
|
92 |
* images are later referenced. This does mean that if such code discards |
|
93 |
* GlyphList and places only the data it contains on the queue, that the |
|
94 |
* strike needs to be part of that data held by a strong reference. |
|
95 |
* In the cases of drawString() and drawChars(), this is a single strike, |
|
96 |
* although it may be a composite strike. In the case of |
|
97 |
* drawGlyphVector() it may be a single strike, or a list of strikes. |
|
98 |
*/ |
|
99 |
Object strikelist; // hold multiple strikes during rendering of complex gv |
|
100 |
||
101 |
/* In normal usage, the same GlyphList will get recycled, so |
|
102 |
* it makes sense to allocate arrays that will get reused along with |
|
103 |
* it, rather than generating garbage. Garbage will be generated only |
|
104 |
* in MP envts where multiple threads are executing. Throughput should |
|
105 |
* still be higher in those cases. |
|
106 |
*/ |
|
107 |
int len = 0; |
|
108 |
int maxLen = 0; |
|
109 |
int maxPosLen = 0; |
|
110 |
int glyphData[]; |
|
111 |
char chData[]; |
|
112 |
long images[]; |
|
113 |
float positions[]; |
|
114 |
float x, y; |
|
115 |
float gposx, gposy; |
|
116 |
boolean usePositions; |
|
117 |
||
118 |
/* lcdRGBOrder is used only by LCD text rendering. Its here because |
|
119 |
* the Graphics may have a different hint value than the one used |
|
120 |
* by a GlyphVector, so it has to be stored here - and is obtained |
|
121 |
* from the right FontInfo. Another approach would have been to have |
|
122 |
* install a separate pipe for that case but that's a lot of extra |
|
123 |
* code when a simple boolean will suffice. The overhead to non-LCD |
|
124 |
* text is a redundant boolean assign per call. |
|
125 |
*/ |
|
126 |
boolean lcdRGBOrder; |
|
127 |
||
128 |
/* |
|
129 |
* lcdSubPixPos is used only by LCD text rendering. Its here because |
|
130 |
* the Graphics may have a different hint value than the one used |
|
131 |
* by a GlyphVector, so it has to be stored here - and is obtained |
|
132 |
* from the right FontInfo. Its also needed by the code which |
|
133 |
* calculates glyph positions which already needs to access this |
|
134 |
* GlyphList and would otherwise need the FontInfo. |
|
135 |
* This is true only if LCD text and fractional metrics hints |
|
136 |
* are selected on the graphics. |
|
137 |
* When this is true and the glyph positions as determined by the |
|
138 |
* advances are non-integral, it requests adjustment of the positions. |
|
139 |
* Setting this for surfaces which do not support it through accelerated |
|
140 |
* loops may cause a slow-down as software loops are invoked instead. |
|
141 |
*/ |
|
142 |
boolean lcdSubPixPos; |
|
143 |
||
144 |
/* This scheme creates a singleton GlyphList which is checked out |
|
145 |
* for use. Callers who find its checked out create one that after use |
|
146 |
* is discarded. This means that in a MT-rendering environment, |
|
147 |
* there's no need to synchronise except for that one instance. |
|
148 |
* Fewer threads will then need to synchronise, perhaps helping |
|
149 |
* throughput on a MP system. If for some reason the reusable |
|
150 |
* GlyphList is checked out for a long time (or never returned?) then |
|
151 |
* we would end up always creating new ones. That situation should not |
|
23006 | 152 |
* occur and if it did, it would just lead to some extra garbage being |
2 | 153 |
* created. |
154 |
*/ |
|
39870
55d674017343
8054991: sun.font.GlyphList uses broken double-checked locking
prr
parents:
26037
diff
changeset
|
155 |
private static final GlyphList reusableGL = new GlyphList(); |
55d674017343
8054991: sun.font.GlyphList uses broken double-checked locking
prr
parents:
26037
diff
changeset
|
156 |
private static final AtomicBoolean inUse = new AtomicBoolean(); |
2 | 157 |
|
158 |
||
159 |
void ensureCapacity(int len) { |
|
160 |
/* Note len must not be -ve! only setFromChars should be capable |
|
161 |
* of passing down a -ve len, and this guards against it. |
|
162 |
*/ |
|
163 |
if (len < 0) { |
|
164 |
len = 0; |
|
165 |
} |
|
166 |
if (usePositions && len > maxPosLen) { |
|
167 |
positions = new float[len * 2 + 2]; |
|
168 |
maxPosLen = len; |
|
169 |
} |
|
170 |
||
171 |
if (maxLen == 0 || len > maxLen) { |
|
172 |
glyphData = new int[len]; |
|
173 |
chData = new char[len]; |
|
174 |
images = new long[len]; |
|
175 |
maxLen = len; |
|
176 |
} |
|
177 |
} |
|
178 |
||
179 |
private GlyphList() { |
|
180 |
// ensureCapacity(DEFAULT_LENGTH); |
|
181 |
} |
|
182 |
||
183 |
// private GlyphList(int arraylen) { |
|
184 |
// ensureCapacity(arraylen); |
|
185 |
// } |
|
186 |
||
187 |
public static GlyphList getInstance() { |
|
39870
55d674017343
8054991: sun.font.GlyphList uses broken double-checked locking
prr
parents:
26037
diff
changeset
|
188 |
if (inUse.compareAndSet(false, true)) { |
55d674017343
8054991: sun.font.GlyphList uses broken double-checked locking
prr
parents:
26037
diff
changeset
|
189 |
return reusableGL; |
55d674017343
8054991: sun.font.GlyphList uses broken double-checked locking
prr
parents:
26037
diff
changeset
|
190 |
} else { |
2 | 191 |
return new GlyphList(); |
192 |
} |
|
193 |
} |
|
194 |
||
195 |
/* In some cases the caller may be able to estimate the size of |
|
196 |
* array needed, and it will usually be long enough. This avoids |
|
197 |
* the unnecessary reallocation that occurs if our default |
|
198 |
* values are too small. This is useful because this object |
|
199 |
* will be discarded so the re-allocation overhead is high. |
|
200 |
*/ |
|
201 |
// public static GlyphList getInstance(int sz) { |
|
39870
55d674017343
8054991: sun.font.GlyphList uses broken double-checked locking
prr
parents:
26037
diff
changeset
|
202 |
// if (inUse.compareAndSet(false, true) { |
55d674017343
8054991: sun.font.GlyphList uses broken double-checked locking
prr
parents:
26037
diff
changeset
|
203 |
// return reusableGL; |
2 | 204 |
// } else { |
39870
55d674017343
8054991: sun.font.GlyphList uses broken double-checked locking
prr
parents:
26037
diff
changeset
|
205 |
// return new GlyphList(sz); |
2 | 206 |
// } |
207 |
// } |
|
208 |
||
209 |
/* GlyphList is in an invalid state until setFrom* method is called. |
|
210 |
* After obtaining a new GlyphList it is the caller's responsibility |
|
211 |
* that one of these methods is executed before handing off the |
|
212 |
* GlyphList |
|
213 |
*/ |
|
214 |
||
215 |
public boolean setFromString(FontInfo info, String str, float x, float y) { |
|
216 |
this.x = x; |
|
217 |
this.y = y; |
|
218 |
this.strikelist = info.fontStrike; |
|
219 |
this.lcdRGBOrder = info.lcdRGBOrder; |
|
220 |
this.lcdSubPixPos = info.lcdSubPixPos; |
|
221 |
len = str.length(); |
|
222 |
ensureCapacity(len); |
|
223 |
str.getChars(0, len, chData, 0); |
|
224 |
return mapChars(info, len); |
|
225 |
} |
|
226 |
||
227 |
public boolean setFromChars(FontInfo info, char[] chars, int off, int alen, |
|
228 |
float x, float y) { |
|
229 |
this.x = x; |
|
230 |
this.y = y; |
|
231 |
this.strikelist = info.fontStrike; |
|
232 |
this.lcdRGBOrder = info.lcdRGBOrder; |
|
233 |
this.lcdSubPixPos = info.lcdSubPixPos; |
|
234 |
len = alen; |
|
235 |
if (alen < 0) { |
|
236 |
len = 0; |
|
237 |
} else { |
|
238 |
len = alen; |
|
239 |
} |
|
240 |
ensureCapacity(len); |
|
241 |
System.arraycopy(chars, off, chData, 0, len); |
|
242 |
return mapChars(info, len); |
|
243 |
} |
|
244 |
||
26004
7507a1b93f67
6521783: Unnecessary final modifier for a method in a final class
serb
parents:
23010
diff
changeset
|
245 |
private boolean mapChars(FontInfo info, int len) { |
2 | 246 |
/* REMIND.Is it worthwhile for the iteration to convert |
247 |
* chars to glyph ids to directly map to images? |
|
248 |
*/ |
|
249 |
if (info.font2D.getMapper().charsToGlyphsNS(len, chData, glyphData)) { |
|
250 |
return false; |
|
251 |
} |
|
252 |
info.fontStrike.getGlyphImagePtrs(glyphData, images, len); |
|
253 |
glyphindex = -1; |
|
254 |
return true; |
|
255 |
} |
|
256 |
||
257 |
||
258 |
public void setFromGlyphVector(FontInfo info, GlyphVector gv, |
|
259 |
float x, float y) { |
|
260 |
this.x = x; |
|
261 |
this.y = y; |
|
262 |
this.lcdRGBOrder = info.lcdRGBOrder; |
|
263 |
this.lcdSubPixPos = info.lcdSubPixPos; |
|
264 |
/* A GV may be rendered in different Graphics. It is possible it is |
|
265 |
* used for one case where LCD text is available, and another where |
|
266 |
* it is not. Pass in the "info". to ensure get a suitable one. |
|
267 |
*/ |
|
268 |
StandardGlyphVector sgv = StandardGlyphVector.getStandardGV(gv, info); |
|
269 |
// call before ensureCapacity :- |
|
270 |
usePositions = sgv.needsPositions(info.devTx); |
|
271 |
len = sgv.getNumGlyphs(); |
|
272 |
ensureCapacity(len); |
|
273 |
strikelist = sgv.setupGlyphImages(images, |
|
274 |
usePositions ? positions : null, |
|
275 |
info.devTx); |
|
276 |
glyphindex = -1; |
|
277 |
} |
|
278 |
||
279 |
public int[] getBounds() { |
|
280 |
/* We co-opt the 5 element array that holds per glyph metrics in order |
|
281 |
* to return the bounds. So a caller must copy the data out of the |
|
282 |
* array before calling any other methods on this GlyphList |
|
283 |
*/ |
|
284 |
if (glyphindex >= 0) { |
|
285 |
throw new InternalError("calling getBounds after setGlyphIndex"); |
|
286 |
} |
|
287 |
if (metrics == null) { |
|
288 |
metrics = new int[5]; |
|
289 |
} |
|
290 |
/* gposx and gposy are used to accumulate the advance. |
|
291 |
* Add 0.5f for consistent rounding to pixel position. */ |
|
292 |
gposx = x + 0.5f; |
|
293 |
gposy = y + 0.5f; |
|
294 |
fillBounds(metrics); |
|
295 |
return metrics; |
|
296 |
} |
|
297 |
||
298 |
/* This method now assumes "state", so must be called 0->len |
|
299 |
* The metrics it returns are accumulated on the fly |
|
300 |
* So it could be renamed "nextGlyph()". |
|
301 |
* Note that a laid out GlyphVector which has assigned glyph positions |
|
302 |
* doesn't have this stricture.. |
|
303 |
*/ |
|
304 |
public void setGlyphIndex(int i) { |
|
305 |
glyphindex = i; |
|
306 |
float gx = |
|
307 |
StrikeCache.unsafe.getFloat(images[i]+StrikeCache.topLeftXOffset); |
|
308 |
float gy = |
|
309 |
StrikeCache.unsafe.getFloat(images[i]+StrikeCache.topLeftYOffset); |
|
310 |
||
311 |
if (usePositions) { |
|
312 |
metrics[0] = (int)Math.floor(positions[(i<<1)] + gposx + gx); |
|
313 |
metrics[1] = (int)Math.floor(positions[(i<<1)+1] + gposy + gy); |
|
314 |
} else { |
|
315 |
metrics[0] = (int)Math.floor(gposx + gx); |
|
316 |
metrics[1] = (int)Math.floor(gposy + gy); |
|
317 |
/* gposx and gposy are used to accumulate the advance */ |
|
318 |
gposx += StrikeCache.unsafe.getFloat |
|
319 |
(images[i]+StrikeCache.xAdvanceOffset); |
|
320 |
gposy += StrikeCache.unsafe.getFloat |
|
321 |
(images[i]+StrikeCache.yAdvanceOffset); |
|
322 |
} |
|
323 |
metrics[2] = |
|
324 |
StrikeCache.unsafe.getChar(images[i]+StrikeCache.widthOffset); |
|
325 |
metrics[3] = |
|
326 |
StrikeCache.unsafe.getChar(images[i]+StrikeCache.heightOffset); |
|
327 |
metrics[4] = |
|
328 |
StrikeCache.unsafe.getChar(images[i]+StrikeCache.rowBytesOffset); |
|
329 |
} |
|
330 |
||
331 |
public int[] getMetrics() { |
|
332 |
return metrics; |
|
333 |
} |
|
334 |
||
335 |
public byte[] getGrayBits() { |
|
336 |
int len = metrics[4] * metrics[3]; |
|
337 |
if (graybits == null) { |
|
338 |
graybits = new byte[Math.max(len, MINGRAYLENGTH)]; |
|
339 |
} else { |
|
340 |
if (len > graybits.length) { |
|
341 |
graybits = new byte[len]; |
|
342 |
} |
|
343 |
} |
|
17404
47af135a3e95
7191872: Xrender: No text displayed using 64 bit JDK on solaris11-sparc
simonis
parents:
5506
diff
changeset
|
344 |
long pixelDataAddress = |
47af135a3e95
7191872: Xrender: No text displayed using 64 bit JDK on solaris11-sparc
simonis
parents:
5506
diff
changeset
|
345 |
StrikeCache.unsafe.getAddress(images[glyphindex] + |
2 | 346 |
StrikeCache.pixelDataOffset); |
17404
47af135a3e95
7191872: Xrender: No text displayed using 64 bit JDK on solaris11-sparc
simonis
parents:
5506
diff
changeset
|
347 |
|
2 | 348 |
if (pixelDataAddress == 0L) { |
349 |
return graybits; |
|
350 |
} |
|
351 |
/* unsafe is supposed to be fast, but I doubt if this loop can beat |
|
352 |
* a native call which does a getPrimitiveArrayCritical and a |
|
353 |
* memcpy for the typical amount of image data (30-150 bytes) |
|
354 |
* Consider a native method if there is a performance problem (which |
|
355 |
* I haven't seen so far). |
|
356 |
*/ |
|
357 |
for (int i=0; i<len; i++) { |
|
358 |
graybits[i] = StrikeCache.unsafe.getByte(pixelDataAddress+i); |
|
359 |
} |
|
360 |
return graybits; |
|
361 |
} |
|
362 |
||
363 |
public long[] getImages() { |
|
364 |
return images; |
|
365 |
} |
|
366 |
||
367 |
public boolean usePositions() { |
|
368 |
return usePositions; |
|
369 |
} |
|
370 |
||
371 |
public float[] getPositions() { |
|
372 |
return positions; |
|
373 |
} |
|
374 |
||
375 |
public float getX() { |
|
376 |
return x; |
|
377 |
} |
|
378 |
||
379 |
public float getY() { |
|
380 |
return y; |
|
381 |
} |
|
382 |
||
383 |
public Object getStrike() { |
|
384 |
return strikelist; |
|
385 |
} |
|
386 |
||
387 |
public boolean isSubPixPos() { |
|
388 |
return lcdSubPixPos; |
|
389 |
} |
|
390 |
||
391 |
public boolean isRGBOrder() { |
|
392 |
return lcdRGBOrder; |
|
393 |
} |
|
394 |
||
395 |
/* There's a reference equality test overhead here, but it allows us |
|
396 |
* to avoid synchronizing for GL's that will just be GC'd. This |
|
397 |
* helps MP throughput. |
|
398 |
*/ |
|
399 |
public void dispose() { |
|
400 |
if (this == reusableGL) { |
|
401 |
if (graybits != null && graybits.length > MAXGRAYLENGTH) { |
|
402 |
graybits = null; |
|
403 |
} |
|
404 |
usePositions = false; |
|
405 |
strikelist = null; // remove reference to the strike list |
|
39870
55d674017343
8054991: sun.font.GlyphList uses broken double-checked locking
prr
parents:
26037
diff
changeset
|
406 |
inUse.set(false); |
2 | 407 |
} |
408 |
} |
|
409 |
||
410 |
/* The value here is for use by the rendering engine as it reflects |
|
411 |
* the number of glyphs in the array to be blitted. Surrogates pairs |
|
412 |
* may have two slots (the second of these being a dummy entry of the |
|
413 |
* invisible glyph), whereas an application client would expect only |
|
414 |
* one glyph. In other words don't propagate this value up to client code. |
|
415 |
* |
|
416 |
* {dlf} an application client should have _no_ expectations about the |
|
417 |
* number of glyphs per char. This ultimately depends on the font |
|
418 |
* technology and layout process used, which in general clients will |
|
419 |
* know nothing about. |
|
420 |
*/ |
|
421 |
public int getNumGlyphs() { |
|
422 |
return len; |
|
423 |
} |
|
424 |
||
425 |
/* We re-do all this work as we iterate through the glyphs |
|
426 |
* but it seems unavoidable without re-working the Java TextRenderers. |
|
427 |
*/ |
|
428 |
private void fillBounds(int[] bounds) { |
|
429 |
/* Faster to access local variables in the for loop? */ |
|
430 |
int xOffset = StrikeCache.topLeftXOffset; |
|
431 |
int yOffset = StrikeCache.topLeftYOffset; |
|
432 |
int wOffset = StrikeCache.widthOffset; |
|
433 |
int hOffset = StrikeCache.heightOffset; |
|
434 |
int xAdvOffset = StrikeCache.xAdvanceOffset; |
|
435 |
int yAdvOffset = StrikeCache.yAdvanceOffset; |
|
436 |
||
437 |
if (len == 0) { |
|
438 |
bounds[0] = bounds[1] = bounds[2] = bounds[3] = 0; |
|
439 |
return; |
|
440 |
} |
|
441 |
float bx0, by0, bx1, by1; |
|
442 |
bx0 = by0 = Float.POSITIVE_INFINITY; |
|
443 |
bx1 = by1 = Float.NEGATIVE_INFINITY; |
|
444 |
||
445 |
int posIndex = 0; |
|
446 |
float glx = x + 0.5f; |
|
447 |
float gly = y + 0.5f; |
|
448 |
char gw, gh; |
|
449 |
float gx, gy, gx0, gy0, gx1, gy1; |
|
450 |
for (int i=0; i<len; i++) { |
|
451 |
gx = StrikeCache.unsafe.getFloat(images[i]+xOffset); |
|
452 |
gy = StrikeCache.unsafe.getFloat(images[i]+yOffset); |
|
453 |
gw = StrikeCache.unsafe.getChar(images[i]+wOffset); |
|
454 |
gh = StrikeCache.unsafe.getChar(images[i]+hOffset); |
|
455 |
||
456 |
if (usePositions) { |
|
457 |
gx0 = positions[posIndex++] + gx + glx; |
|
458 |
gy0 = positions[posIndex++] + gy + gly; |
|
459 |
} else { |
|
460 |
gx0 = glx + gx; |
|
461 |
gy0 = gly + gy; |
|
462 |
glx += StrikeCache.unsafe.getFloat(images[i]+xAdvOffset); |
|
463 |
gly += StrikeCache.unsafe.getFloat(images[i]+yAdvOffset); |
|
464 |
} |
|
465 |
gx1 = gx0 + gw; |
|
466 |
gy1 = gy0 + gh; |
|
467 |
if (bx0 > gx0) bx0 = gx0; |
|
468 |
if (by0 > gy0) by0 = gy0; |
|
469 |
if (bx1 < gx1) bx1 = gx1; |
|
470 |
if (by1 < gy1) by1 = gy1; |
|
471 |
} |
|
472 |
/* floor is safe and correct because all glyph widths, heights |
|
473 |
* and offsets are integers |
|
474 |
*/ |
|
475 |
bounds[0] = (int)Math.floor(bx0); |
|
476 |
bounds[1] = (int)Math.floor(by0); |
|
477 |
bounds[2] = (int)Math.floor(bx1); |
|
478 |
bounds[3] = (int)Math.floor(by1); |
|
479 |
} |
|
480 |
} |