blob: f9227deeb071e724da5f2df2c02d5a2f1d0d4841 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2003-2006 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.font;
27
28/* remember that the API requires a Font use a
29 * consistent glyph id. for a code point, and this is a
30 * problem if a particular strike uses native scaler sometimes
31 * and T2K others. That needs to be dealt with somewhere, but
32 * here we can just always get the same glyph code without
33 * needing a strike.
34 *
35 * The C implementation would cache the results of anything up
36 * to the maximum surrogate pair code point.
37 * This implementation will not cache as much, since the storage
38 * requirements are not justifiable. Even so it still can use up
39 * to 216*256*4 bytes of storage per composite font. If an app
40 * calls canDisplay on this range for all 20 composite fonts that's
41 * over 1Mb of cached data. May need to employ WeakReferences if
42 * this appears to cause problems.
43 */
44
45public final class CompositeGlyphMapper extends CharToGlyphMapper {
46
47 public static final int SLOTMASK = 0xff000000;
48 public static final int GLYPHMASK = 0x00ffffff;
49
50 public static final int NBLOCKS = 216;
51 public static final int BLOCKSZ = 256;
52 public static final int MAXUNICODE = NBLOCKS*BLOCKSZ;
53
54
55 CompositeFont font;
56 CharToGlyphMapper slotMappers[];
57 int[][] glyphMaps;
58 private boolean hasExcludes;
59
60 public CompositeGlyphMapper(CompositeFont compFont) {
61 font = compFont;
62 initMapper();
63 /* This is often false which saves the overhead of a
64 * per-mapped char method call.
65 */
66 hasExcludes = compFont.exclusionRanges != null &&
67 compFont.maxIndices != null;
68 }
69
70 public final int compositeGlyphCode(int slot, int glyphCode) {
71 return (slot << 24 | (glyphCode & GLYPHMASK));
72 }
73
74 private final void initMapper() {
75 if (missingGlyph == CharToGlyphMapper.UNINITIALIZED_GLYPH) {
76 if (glyphMaps == null) {
77 glyphMaps = new int[NBLOCKS][];
78 }
79 slotMappers = new CharToGlyphMapper[font.numSlots];
80 /* This requires that slot 0 is never empty. */
81 missingGlyph = font.getSlotFont(0).getMissingGlyphCode();
82 missingGlyph = compositeGlyphCode(0, missingGlyph);
83 }
84 }
85
86 private int getCachedGlyphCode(int unicode) {
87 if (unicode >= MAXUNICODE) {
88 return UNINITIALIZED_GLYPH; // don't cache surrogates
89 }
90 int[] gmap;
91 if ((gmap = glyphMaps[unicode >> 8]) == null) {
92 return UNINITIALIZED_GLYPH;
93 }
94 return gmap[unicode & 0xff];
95 }
96
97 private void setCachedGlyphCode(int unicode, int glyphCode) {
98 if (unicode >= MAXUNICODE) {
99 return; // don't cache surrogates
100 }
101 int index0 = unicode >> 8;
102 if (glyphMaps[index0] == null) {
103 glyphMaps[index0] = new int[BLOCKSZ];
104 for (int i=0;i<BLOCKSZ;i++) {
105 glyphMaps[index0][i] = UNINITIALIZED_GLYPH;
106 }
107 }
108 glyphMaps[index0][unicode & 0xff] = glyphCode;
109 }
110
111 private final CharToGlyphMapper getSlotMapper(int slot) {
112 CharToGlyphMapper mapper = slotMappers[slot];
113 if (mapper == null) {
114 mapper = font.getSlotFont(slot).getMapper();
115 slotMappers[slot] = mapper;
116 }
117 return mapper;
118 }
119
120 private final int convertToGlyph(int unicode) {
121
122 for (int slot = 0; slot < font.numSlots; slot++) {
123 if (!hasExcludes || !font.isExcludedChar(slot, unicode)) {
124 CharToGlyphMapper mapper = getSlotMapper(slot);
125 int glyphCode = mapper.charToGlyph(unicode);
126 if (glyphCode != mapper.getMissingGlyphCode()) {
127 glyphCode = compositeGlyphCode(slot, glyphCode);
128 setCachedGlyphCode(unicode, glyphCode);
129 return glyphCode;
130 }
131 }
132 }
133 return missingGlyph;
134 }
135
136 public int getNumGlyphs() {
137 int numGlyphs = 0;
138 /* The number of glyphs in a composite is affected by
139 * exclusion ranges and duplicates (ie the same code point is
140 * mapped by two different fonts) and also whether or not to
141 * count fallback fonts. A nearly correct answer would be very
142 * expensive to generate. A rough ballpark answer would
143 * just count the glyphs in all the slots. However this would
144 * initialize mappers for all slots when they aren't necessarily
145 * needed. For now just use the first slot as JDK 1.4 did.
146 */
147 for (int slot=0; slot<1 /*font.numSlots*/; slot++) {
148 CharToGlyphMapper mapper = slotMappers[slot];
149 if (mapper == null) {
150 mapper = font.getSlotFont(slot).getMapper();
151 slotMappers[slot] = mapper;
152 }
153 numGlyphs += mapper.getNumGlyphs();
154 }
155 return numGlyphs;
156 }
157
158 public int charToGlyph(int unicode) {
159
160 int glyphCode = getCachedGlyphCode(unicode);
161 if (glyphCode == UNINITIALIZED_GLYPH) {
162 glyphCode = convertToGlyph(unicode);
163 }
164 return glyphCode;
165 }
166
167 public int charToGlyph(int unicode, int prefSlot) {
168 if (prefSlot >= 0) {
169 CharToGlyphMapper mapper = getSlotMapper(prefSlot);
170 int glyphCode = mapper.charToGlyph(unicode);
171 if (glyphCode != mapper.getMissingGlyphCode()) {
172 return compositeGlyphCode(prefSlot, glyphCode);
173 }
174 }
175 return charToGlyph(unicode);
176 }
177
178 public int charToGlyph(char unicode) {
179
180 int glyphCode = getCachedGlyphCode(unicode);
181 if (glyphCode == UNINITIALIZED_GLYPH) {
182 glyphCode = convertToGlyph(unicode);
183 }
184 return glyphCode;
185 }
186
187 /* This variant checks if shaping is needed and immediately
188 * returns true if it does. A caller of this method should be expecting
189 * to check the return type because it needs to know how to handle
190 * the character data for display.
191 */
192 public boolean charsToGlyphsNS(int count, char[] unicodes, int[] glyphs) {
193
194 for (int i=0; i<count; i++) {
195 int code = unicodes[i]; // char is unsigned.
196
197 if (code >= HI_SURROGATE_START &&
198 code <= HI_SURROGATE_END && i < count - 1) {
199 char low = unicodes[i + 1];
200
201 if (low >= LO_SURROGATE_START &&
202 low <= LO_SURROGATE_END) {
203 code = (code - HI_SURROGATE_START) *
204 0x400 + low - LO_SURROGATE_START + 0x10000;
205 glyphs[i + 1] = INVISIBLE_GLYPH_ID;
206 }
207 }
208
209 int gc = glyphs[i] = getCachedGlyphCode(code);
210 if (gc == UNINITIALIZED_GLYPH) {
211 glyphs[i] = convertToGlyph(code);
212 }
213
214 if (code < FontManager.MIN_LAYOUT_CHARCODE) {
215 continue;
216 }
217 else if (FontManager.isComplexCharCode(code)) {
218 return true;
219 }
220 else if (code >= 0x10000) {
221 i += 1; // Empty glyph slot after surrogate
222 continue;
223 }
224 }
225
226 return false;
227 }
228
229 /* The conversion is not very efficient - looping as it does, converting
230 * one char at a time. However the cache should fill very rapidly.
231 */
232 public void charsToGlyphs(int count, char[] unicodes, int[] glyphs) {
233 for (int i=0; i<count; i++) {
234 int code = unicodes[i]; // char is unsigned.
235
236 if (code >= HI_SURROGATE_START &&
237 code <= HI_SURROGATE_END && i < count - 1) {
238 char low = unicodes[i + 1];
239
240 if (low >= LO_SURROGATE_START &&
241 low <= LO_SURROGATE_END) {
242 code = (code - HI_SURROGATE_START) *
243 0x400 + low - LO_SURROGATE_START + 0x10000;
244
245 int gc = glyphs[i] = getCachedGlyphCode(code);
246 if (gc == UNINITIALIZED_GLYPH) {
247 glyphs[i] = convertToGlyph(code);
248 }
249 i += 1; // Empty glyph slot after surrogate
250 glyphs[i] = INVISIBLE_GLYPH_ID;
251 continue;
252 }
253 }
254
255 int gc = glyphs[i] = getCachedGlyphCode(code);
256 if (gc == UNINITIALIZED_GLYPH) {
257 glyphs[i] = convertToGlyph(code);
258 }
259 }
260 }
261
262 public void charsToGlyphs(int count, int[] unicodes, int[] glyphs) {
263 for (int i=0; i<count; i++) {
264 int code = unicodes[i];
265
266 glyphs[i] = getCachedGlyphCode(code);
267 if (glyphs[i] == UNINITIALIZED_GLYPH) {
268 glyphs[i] = convertToGlyph(code);
269 }
270 }
271 }
272
273}