1 /*
2 * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
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
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
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 *
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.
24 */
25
26 package sun.font;
27
28 import java.awt.Rectangle;
29 import java.awt.geom.*;
30 import java.util.*;
31
32 import sun.awt.SunHints;
33
34 public class CStrike extends FontStrike {
35
36 // Creates the native strike
37 private static native long createNativeStrikePtr(long nativeFontPtr,
38 double[] glyphTx,
39 double[] invDevTxMatrix,
40 int aaHint,
41 int fmHint);
42
43 // Disposes the native strike
44 private static native void disposeNativeStrikePtr(long nativeStrikePtr);
45
46 // Creates a StrikeMetrics from the underlying native system fonts
47 private static native StrikeMetrics getFontMetrics(long nativeStrikePtr);
48
49 // Returns native struct pointers used by the Sun 2D Renderer
50 private static native void getGlyphImagePtrsNative(long nativeStrikePtr,
51 long[] glyphInfos,
52 int[] uniCodes, int len);
53
54 // Returns the advance give a glyph code. It should be used only
55 // when the glyph code belongs to the CFont passed in.
56 private static native float getNativeGlyphAdvance(long nativeStrikePtr,
57 int glyphCode);
58
59 // Returns the outline shape of a glyph
60 private static native GeneralPath getNativeGlyphOutline(long nativeStrikePtr,
61 int glyphCode,
62 double x,
63 double y);
64
65 // returns the bounding rect for a glyph
66 private static native void getNativeGlyphImageBounds(long nativeStrikePtr,
67 int glyphCode,
68 Rectangle2D.Float result,
69 double x, double y);
70
71 private CFont nativeFont;
72 private AffineTransform invDevTx;
73 private GlyphInfoCache glyphInfoCache;
74 private GlyphAdvanceCache glyphAdvanceCache;
75 private long nativeStrikePtr;
76
77 CStrike(final CFont font, final FontStrikeDesc inDesc) {
78 nativeFont = font;
79 desc = inDesc;
80 glyphInfoCache = new GlyphInfoCache(font, desc);
81 glyphAdvanceCache = new GlyphAdvanceCache();
82 disposer = glyphInfoCache;
83
84 // Normally the device transform should be the identity transform
85 // for screen operations. The device transform only becomes
86 // interesting when we are outputting between different dpi surfaces,
87 // like when we are printing to postscript.
88 if (inDesc.devTx != null && !inDesc.devTx.isIdentity()) {
89 try {
90 invDevTx = inDesc.devTx.createInverse();
91 } catch (NoninvertibleTransformException e) {
92 // ignored, since device transforms should not be that
93 // complicated, and if they are - there is nothing we can do,
94 // so we won't worry about it.
95 }
96 }
97 }
98
99 public long getNativeStrikePtr() {
100 if (nativeStrikePtr != 0) {
101 return nativeStrikePtr;
102 }
103
104 final double[] glyphTx = new double[6];
105 desc.glyphTx.getMatrix(glyphTx);
106
107 final double[] invDevTxMatrix = new double[6];
108 if (invDevTx == null) {
109 invDevTxMatrix[0] = 1;
110 invDevTxMatrix[3] = 1;
111 } else {
117
118 synchronized (this) {
119 if (nativeStrikePtr != 0) {
120 return nativeStrikePtr;
121 }
122 nativeStrikePtr =
123 createNativeStrikePtr(nativeFont.getNativeFontPtr(),
124 glyphTx, invDevTxMatrix, aaHint, fmHint);
125 }
126
127 return nativeStrikePtr;
128 }
129
130 protected synchronized void finalize() throws Throwable {
131 if (nativeStrikePtr != 0) {
132 disposeNativeStrikePtr(nativeStrikePtr);
133 }
134 nativeStrikePtr = 0;
135 }
136
137 // the fractional metrics default on our platform is OFF
138 private boolean useFractionalMetrics() {
139 return desc.fmHint == SunHints.INTVAL_FRACTIONALMETRICS_ON;
140 }
141
142 public int getNumGlyphs() {
143 return nativeFont.getNumGlyphs();
144 }
145
146 StrikeMetrics getFontMetrics() {
147 if (strikeMetrics == null) {
148 StrikeMetrics metrics = getFontMetrics(getNativeStrikePtr());
149 if (invDevTx != null) {
150 metrics.convertToUserSpace(invDevTx);
151 }
152 metrics.convertToUserSpace(desc.glyphTx);
153 strikeMetrics = metrics;
154 }
155 return strikeMetrics;
156 }
157
158 float getGlyphAdvance(int glyphCode) {
159 return getScaledAdvanceForAdvance(getCachedNativeGlyphAdvance(glyphCode));
160 }
161
162 float getCodePointAdvance(int cp) {
163 float advance = getCachedNativeGlyphAdvance(nativeFont.getMapper().charToGlyph(cp));
164
165 double glyphScaleX = desc.glyphTx.getScaleX();
166 double devScaleX = desc.devTx.getScaleX();
167
168 if (devScaleX == 0) {
169 glyphScaleX = Math.sqrt(desc.glyphTx.getDeterminant());
170 devScaleX = Math.sqrt(desc.devTx.getDeterminant());
171 }
172
173 if (devScaleX == 0) {
174 devScaleX = Double.NaN; // this an undefined graphics state
175 }
176 advance = (float) (advance * glyphScaleX / devScaleX);
177 return useFractionalMetrics() ? advance : Math.round(advance);
178 }
179
180 // calculate an advance, and round if not using fractional metrics
181 private float getScaledAdvanceForAdvance(float advance) {
182 if (invDevTx != null) {
183 advance *= invDevTx.getScaleX();
184 }
185 advance *= desc.glyphTx.getScaleX();
186 return useFractionalMetrics() ? advance : Math.round(advance);
187 }
188
189 Point2D.Float getCharMetrics(char ch) {
190 return getScaledPointForAdvance(getCachedNativeGlyphAdvance(nativeFont.getMapper().charToGlyph(ch)));
191 }
192
193 Point2D.Float getGlyphMetrics(int glyphCode) {
194 return getScaledPointForAdvance(getCachedNativeGlyphAdvance(glyphCode));
195 }
196
197 // calculate an advance point, and round if not using fractional metrics
198 private Point2D.Float getScaledPointForAdvance(float advance) {
199 Point2D.Float pt = new Point2D.Float(advance, 0);
200
201 if (!desc.glyphTx.isIdentity()) {
202 return scalePoint(pt);
203 }
204
205 if (!useFractionalMetrics()) {
206 pt.x = Math.round(pt.x);
207 }
208 return pt;
209 }
210
211 private Point2D.Float scalePoint(Point2D.Float pt) {
212 if (invDevTx != null) {
213 // transform the point out of the device space first
214 invDevTx.transform(pt, pt);
215 }
216 desc.glyphTx.transform(pt, pt);
217 pt.x -= desc.glyphTx.getTranslateX();
218 pt.y -= desc.glyphTx.getTranslateY();
219
220 if (!useFractionalMetrics()) {
221 pt.x = Math.round(pt.x);
222 pt.y = Math.round(pt.y);
223 }
224
225 return pt;
226 }
227
228 Rectangle2D.Float getGlyphOutlineBounds(int glyphCode) {
229 GeneralPath gp = getGlyphOutline(glyphCode, 0f, 0f);
230 Rectangle2D r2d = gp.getBounds2D();
231 Rectangle2D.Float r2df;
232 if (r2d instanceof Rectangle2D.Float) {
233 r2df = (Rectangle2D.Float)r2d;
234 } else {
235 float x = (float)r2d.getX();
236 float y = (float)r2d.getY();
237 float w = (float)r2d.getWidth();
238 float h = (float)r2d.getHeight();
239 r2df = new Rectangle2D.Float(x, y, w, h);
240 }
241 return r2df;
242 }
243
244 // pt, result in device space
245 void getGlyphImageBounds(int glyphCode, Point2D.Float pt, Rectangle result) {
397 advance = getNativeGlyphAdvance(getNativeStrikePtr(), glyphCode);
398 glyphAdvanceCache.put(glyphCode, advance);
399 return advance;
400 }
401 }
402
403 // This class stores glyph pointers, and is indexed based on glyph codes,
404 // and negative unicode values. See the comments in
405 // CCharToGlyphMapper for more details on our glyph code strategy.
406 private static class GlyphInfoCache extends CStrikeDisposer {
407 private static final int FIRST_LAYER_SIZE = 256;
408 private static final int SECOND_LAYER_SIZE = 16384; // 16384 = 128x128
409
410 // rdar://problem/5204197
411 private boolean disposed = false;
412
413 private final long[] firstLayerCache;
414 private SparseBitShiftingTwoLayerArray secondLayerCache;
415 private HashMap<Integer, Long> generalCache;
416
417 public GlyphInfoCache(final Font2D nativeFont,
418 final FontStrikeDesc desc)
419 {
420 super(nativeFont, desc);
421 firstLayerCache = new long[FIRST_LAYER_SIZE];
422 }
423
424 public synchronized long get(final int index) {
425 if (index < 0) {
426 if (-index < SECOND_LAYER_SIZE) {
427 // catch common unicodes
428 if (secondLayerCache == null) {
429 return 0L;
430 }
431 return secondLayerCache.get(-index);
432 }
433 } else {
434 if (index < FIRST_LAYER_SIZE) {
435 // catch common glyphcodes
436 return firstLayerCache[index];
437 }
438 }
439
510 // rdar://problem/5204197
511 // Finally, set the flag.
512 disposed = true;
513 }
514
515 private static void disposeLongArray(final long[] longArray) {
516 for (int i = 0; i < longArray.length; i++) {
517 final long ptr = longArray[i];
518 if (ptr != 0 && ptr != -1) {
519 removeGlyphInfoFromCache(ptr);
520 StrikeCache.freeLongPointer(ptr); // free's the native struct pointer
521 }
522 }
523 }
524
525 private static class SparseBitShiftingTwoLayerArray {
526 final long[][] cache;
527 final int shift;
528 final int secondLayerLength;
529
530 public SparseBitShiftingTwoLayerArray(final int size, final int shift) {
531 this.shift = shift;
532 this.cache = new long[1 << shift][];
533 this.secondLayerLength = size >> shift;
534 }
535
536 public long get(final int index) {
537 final int firstIndex = index >> shift;
538 final long[] firstLayerRow = cache[firstIndex];
539 if (firstLayerRow == null) return 0L;
540 return firstLayerRow[index - (firstIndex * (1 << shift))];
541 }
542
543 public void put(final int index, final long value) {
544 final int firstIndex = index >> shift;
545 long[] firstLayerRow = cache[firstIndex];
546 if (firstLayerRow == null) {
547 cache[firstIndex] = firstLayerRow = new long[secondLayerLength];
548 }
549 firstLayerRow[index - (firstIndex * (1 << shift))] = value;
550 }
551 }
552 }
553
554 private static class GlyphAdvanceCache {
555 private static final int FIRST_LAYER_SIZE = 256;
556 private static final int SECOND_LAYER_SIZE = 16384; // 16384 = 128x128
557
558 private final float[] firstLayerCache = new float[FIRST_LAYER_SIZE];
559 private SparseBitShiftingTwoLayerArray secondLayerCache;
560 private HashMap<Integer, Float> generalCache;
561
562 public synchronized float get(final int index) {
563 if (index < 0) {
564 if (-index < SECOND_LAYER_SIZE) {
565 // catch common unicodes
566 if (secondLayerCache == null) return 0;
567 return secondLayerCache.get(-index);
568 }
569 } else {
570 if (index < FIRST_LAYER_SIZE) {
571 // catch common glyphcodes
572 return firstLayerCache[index];
573 }
574 }
575
576 if (generalCache == null) return 0;
577 final Float value = generalCache.get(new Integer(index));
578 if (value == null) return 0;
579 return value.floatValue();
580 }
581
592 } else {
593 if (index < FIRST_LAYER_SIZE) {
594 // catch common glyphcodes
595 firstLayerCache[index] = value;
596 return;
597 }
598 }
599
600 if (generalCache == null) {
601 generalCache = new HashMap<Integer, Float>();
602 }
603
604 generalCache.put(new Integer(index), new Float(value));
605 }
606
607 private static class SparseBitShiftingTwoLayerArray {
608 final float[][] cache;
609 final int shift;
610 final int secondLayerLength;
611
612 public SparseBitShiftingTwoLayerArray(final int size,
613 final int shift)
614 {
615 this.shift = shift;
616 this.cache = new float[1 << shift][];
617 this.secondLayerLength = size >> shift;
618 }
619
620 public float get(final int index) {
621 final int firstIndex = index >> shift;
622 final float[] firstLayerRow = cache[firstIndex];
623 if (firstLayerRow == null) return 0L;
624 return firstLayerRow[index - (firstIndex * (1 << shift))];
625 }
626
627 public void put(final int index, final float value) {
628 final int firstIndex = index >> shift;
629 float[] firstLayerRow = cache[firstIndex];
630 if (firstLayerRow == null) {
631 cache[firstIndex] = firstLayerRow =
632 new float[secondLayerLength];
633 }
634 firstLayerRow[index - (firstIndex * (1 << shift))] = value;
|
1 /*
2 * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
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
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
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 *
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.
24 */
25
26 package sun.font;
27
28 import java.awt.Rectangle;
29 import java.awt.geom.*;
30 import java.util.*;
31
32 import sun.awt.SunHints;
33
34 public final class CStrike extends FontStrike {
35
36 // Creates the native strike
37 private static native long createNativeStrikePtr(long nativeFontPtr,
38 double[] glyphTx,
39 double[] invDevTxMatrix,
40 int aaHint,
41 int fmHint);
42
43 // Disposes the native strike
44 private static native void disposeNativeStrikePtr(long nativeStrikePtr);
45
46 // Creates a StrikeMetrics from the underlying native system fonts
47 private static native StrikeMetrics getFontMetrics(long nativeStrikePtr);
48
49 // Returns native struct pointers used by the Sun 2D Renderer
50 private static native void getGlyphImagePtrsNative(long nativeStrikePtr,
51 long[] glyphInfos,
52 int[] uniCodes, int len);
53
54 // Returns the advance give a glyph code. It should be used only
55 // when the glyph code belongs to the CFont passed in.
56 private static native float getNativeGlyphAdvance(long nativeStrikePtr,
57 int glyphCode);
58
59 // Returns the outline shape of a glyph
60 private static native GeneralPath getNativeGlyphOutline(long nativeStrikePtr,
61 int glyphCode,
62 double x,
63 double y);
64
65 // returns the bounding rect for a glyph
66 private static native void getNativeGlyphImageBounds(long nativeStrikePtr,
67 int glyphCode,
68 Rectangle2D.Float result,
69 double x, double y);
70
71 private final CFont nativeFont;
72 private AffineTransform invDevTx;
73 private final GlyphInfoCache glyphInfoCache;
74 private final GlyphAdvanceCache glyphAdvanceCache;
75 private long nativeStrikePtr;
76
77 CStrike(final CFont font, final FontStrikeDesc inDesc) {
78 nativeFont = font;
79 desc = inDesc;
80 glyphInfoCache = new GlyphInfoCache(font, desc);
81 glyphAdvanceCache = new GlyphAdvanceCache();
82 disposer = glyphInfoCache;
83
84 // Normally the device transform should be the identity transform
85 // for screen operations. The device transform only becomes
86 // interesting when we are outputting between different dpi surfaces,
87 // like when we are printing to postscript or use retina.
88 if (inDesc.devTx != null && !inDesc.devTx.isIdentity()) {
89 try {
90 invDevTx = inDesc.devTx.createInverse();
91 } catch (NoninvertibleTransformException ignored) {
92 // ignored, since device transforms should not be that
93 // complicated, and if they are - there is nothing we can do,
94 // so we won't worry about it.
95 }
96 }
97 }
98
99 public long getNativeStrikePtr() {
100 if (nativeStrikePtr != 0) {
101 return nativeStrikePtr;
102 }
103
104 final double[] glyphTx = new double[6];
105 desc.glyphTx.getMatrix(glyphTx);
106
107 final double[] invDevTxMatrix = new double[6];
108 if (invDevTx == null) {
109 invDevTxMatrix[0] = 1;
110 invDevTxMatrix[3] = 1;
111 } else {
117
118 synchronized (this) {
119 if (nativeStrikePtr != 0) {
120 return nativeStrikePtr;
121 }
122 nativeStrikePtr =
123 createNativeStrikePtr(nativeFont.getNativeFontPtr(),
124 glyphTx, invDevTxMatrix, aaHint, fmHint);
125 }
126
127 return nativeStrikePtr;
128 }
129
130 protected synchronized void finalize() throws Throwable {
131 if (nativeStrikePtr != 0) {
132 disposeNativeStrikePtr(nativeStrikePtr);
133 }
134 nativeStrikePtr = 0;
135 }
136
137
138 @Override
139 public int getNumGlyphs() {
140 return nativeFont.getNumGlyphs();
141 }
142
143 @Override
144 StrikeMetrics getFontMetrics() {
145 if (strikeMetrics == null) {
146 StrikeMetrics metrics = getFontMetrics(getNativeStrikePtr());
147 if (invDevTx != null) {
148 metrics.convertToUserSpace(invDevTx);
149 }
150 metrics.convertToUserSpace(desc.glyphTx);
151 strikeMetrics = metrics;
152 }
153 return strikeMetrics;
154 }
155
156 @Override
157 float getGlyphAdvance(final int glyphCode) {
158 return getCachedNativeGlyphAdvance(glyphCode);
159 }
160
161 @Override
162 float getCodePointAdvance(final int cp) {
163 return getGlyphAdvance(nativeFont.getMapper().charToGlyph(cp));
164 }
165
166 @Override
167 Point2D.Float getCharMetrics(final char ch) {
168 return getGlyphMetrics(nativeFont.getMapper().charToGlyph(ch));
169 }
170
171 @Override
172 Point2D.Float getGlyphMetrics(final int glyphCode) {
173 return new Point2D.Float(getGlyphAdvance(glyphCode), 0.0f);
174 }
175
176 Rectangle2D.Float getGlyphOutlineBounds(int glyphCode) {
177 GeneralPath gp = getGlyphOutline(glyphCode, 0f, 0f);
178 Rectangle2D r2d = gp.getBounds2D();
179 Rectangle2D.Float r2df;
180 if (r2d instanceof Rectangle2D.Float) {
181 r2df = (Rectangle2D.Float)r2d;
182 } else {
183 float x = (float)r2d.getX();
184 float y = (float)r2d.getY();
185 float w = (float)r2d.getWidth();
186 float h = (float)r2d.getHeight();
187 r2df = new Rectangle2D.Float(x, y, w, h);
188 }
189 return r2df;
190 }
191
192 // pt, result in device space
193 void getGlyphImageBounds(int glyphCode, Point2D.Float pt, Rectangle result) {
345 advance = getNativeGlyphAdvance(getNativeStrikePtr(), glyphCode);
346 glyphAdvanceCache.put(glyphCode, advance);
347 return advance;
348 }
349 }
350
351 // This class stores glyph pointers, and is indexed based on glyph codes,
352 // and negative unicode values. See the comments in
353 // CCharToGlyphMapper for more details on our glyph code strategy.
354 private static class GlyphInfoCache extends CStrikeDisposer {
355 private static final int FIRST_LAYER_SIZE = 256;
356 private static final int SECOND_LAYER_SIZE = 16384; // 16384 = 128x128
357
358 // rdar://problem/5204197
359 private boolean disposed = false;
360
361 private final long[] firstLayerCache;
362 private SparseBitShiftingTwoLayerArray secondLayerCache;
363 private HashMap<Integer, Long> generalCache;
364
365 GlyphInfoCache(final Font2D nativeFont, final FontStrikeDesc desc) {
366 super(nativeFont, desc);
367 firstLayerCache = new long[FIRST_LAYER_SIZE];
368 }
369
370 public synchronized long get(final int index) {
371 if (index < 0) {
372 if (-index < SECOND_LAYER_SIZE) {
373 // catch common unicodes
374 if (secondLayerCache == null) {
375 return 0L;
376 }
377 return secondLayerCache.get(-index);
378 }
379 } else {
380 if (index < FIRST_LAYER_SIZE) {
381 // catch common glyphcodes
382 return firstLayerCache[index];
383 }
384 }
385
456 // rdar://problem/5204197
457 // Finally, set the flag.
458 disposed = true;
459 }
460
461 private static void disposeLongArray(final long[] longArray) {
462 for (int i = 0; i < longArray.length; i++) {
463 final long ptr = longArray[i];
464 if (ptr != 0 && ptr != -1) {
465 removeGlyphInfoFromCache(ptr);
466 StrikeCache.freeLongPointer(ptr); // free's the native struct pointer
467 }
468 }
469 }
470
471 private static class SparseBitShiftingTwoLayerArray {
472 final long[][] cache;
473 final int shift;
474 final int secondLayerLength;
475
476 SparseBitShiftingTwoLayerArray(final int size, final int shift) {
477 this.shift = shift;
478 this.cache = new long[1 << shift][];
479 this.secondLayerLength = size >> shift;
480 }
481
482 public long get(final int index) {
483 final int firstIndex = index >> shift;
484 final long[] firstLayerRow = cache[firstIndex];
485 if (firstLayerRow == null) return 0L;
486 return firstLayerRow[index - (firstIndex * (1 << shift))];
487 }
488
489 public void put(final int index, final long value) {
490 final int firstIndex = index >> shift;
491 long[] firstLayerRow = cache[firstIndex];
492 if (firstLayerRow == null) {
493 cache[firstIndex] = firstLayerRow = new long[secondLayerLength];
494 }
495 firstLayerRow[index - (firstIndex * (1 << shift))] = value;
496 }
497 }
498 }
499
500 private static class GlyphAdvanceCache {
501 private static final int FIRST_LAYER_SIZE = 256;
502 private static final int SECOND_LAYER_SIZE = 16384; // 16384 = 128x128
503
504 private final float[] firstLayerCache = new float[FIRST_LAYER_SIZE];
505 private SparseBitShiftingTwoLayerArray secondLayerCache;
506 private HashMap<Integer, Float> generalCache;
507
508 // Empty non private constructor was added because access to this
509 // class shouldn't be emulated by a synthetic accessor method.
510 GlyphAdvanceCache() {
511 super();
512 }
513
514 public synchronized float get(final int index) {
515 if (index < 0) {
516 if (-index < SECOND_LAYER_SIZE) {
517 // catch common unicodes
518 if (secondLayerCache == null) return 0;
519 return secondLayerCache.get(-index);
520 }
521 } else {
522 if (index < FIRST_LAYER_SIZE) {
523 // catch common glyphcodes
524 return firstLayerCache[index];
525 }
526 }
527
528 if (generalCache == null) return 0;
529 final Float value = generalCache.get(new Integer(index));
530 if (value == null) return 0;
531 return value.floatValue();
532 }
533
544 } else {
545 if (index < FIRST_LAYER_SIZE) {
546 // catch common glyphcodes
547 firstLayerCache[index] = value;
548 return;
549 }
550 }
551
552 if (generalCache == null) {
553 generalCache = new HashMap<Integer, Float>();
554 }
555
556 generalCache.put(new Integer(index), new Float(value));
557 }
558
559 private static class SparseBitShiftingTwoLayerArray {
560 final float[][] cache;
561 final int shift;
562 final int secondLayerLength;
563
564 SparseBitShiftingTwoLayerArray(final int size, final int shift) {
565 this.shift = shift;
566 this.cache = new float[1 << shift][];
567 this.secondLayerLength = size >> shift;
568 }
569
570 public float get(final int index) {
571 final int firstIndex = index >> shift;
572 final float[] firstLayerRow = cache[firstIndex];
573 if (firstLayerRow == null) return 0L;
574 return firstLayerRow[index - (firstIndex * (1 << shift))];
575 }
576
577 public void put(final int index, final float value) {
578 final int firstIndex = index >> shift;
579 float[] firstLayerRow = cache[firstIndex];
580 if (firstLayerRow == null) {
581 cache[firstIndex] = firstLayerRow =
582 new float[secondLayerLength];
583 }
584 firstLayerRow[index - (firstIndex * (1 << shift))] = value;
|