113 } 114 } 115 116 return false; 117 } 118 119 public synchronized int charToGlyph(char unicode) { 120 final int glyph = cache.get(unicode); 121 if (glyph != 0) return glyph; 122 123 final char[] unicodeArray = new char[] { unicode }; 124 final int[] glyphArray = new int[1]; 125 126 nativeCharsToGlyphs(fFont.getNativeFontPtr(), 1, unicodeArray, glyphArray); 127 cache.put(unicode, glyphArray[0]); 128 129 return glyphArray[0]; 130 } 131 132 public synchronized int charToGlyph(int unicode) { 133 return charToGlyph((char)unicode); 134 } 135 136 public synchronized void charsToGlyphs(int count, char[] unicodes, int[] glyphs) { 137 cache.get(count, unicodes, glyphs); 138 } 139 140 public synchronized void charsToGlyphs(int count, int[] unicodes, int[] glyphs) { 141 final char[] unicodeChars = new char[count]; 142 for (int i = 0; i < count; i++) unicodeChars[i] = (char)unicodes[i]; 143 cache.get(count, unicodeChars, glyphs); 144 } 145 146 // This mapper returns either the glyph code, or if the character can be 147 // replaced on-the-fly using CoreText substitution; the negative unicode 148 // value. If this "glyph code int" is treated as an opaque code, it will 149 // strike and measure exactly as a real glyph code - whether the character 150 // is present or not. Missing characters for any font on the system will 151 // be returned as 0, as the getMissingGlyphCode() function above indicates. 152 private static native void nativeCharsToGlyphs(final long nativeFontPtr, 153 int count, char[] unicodes, 154 int[] glyphs); 155 156 private class Cache { 157 private static final int FIRST_LAYER_SIZE = 256; 158 private static final int SECOND_LAYER_SIZE = 16384; // 16384 = 128x128 159 160 private final int[] firstLayerCache = new int[FIRST_LAYER_SIZE]; 161 private SparseBitShiftingTwoLayerArray secondLayerCache; 162 private HashMap<Integer, Integer> generalCache; 163 164 Cache() { 165 // <rdar://problem/5331678> need to prevent getting '-1' stuck in the cache 166 firstLayerCache[1] = 1; 167 } 168 169 public int get(final char index) { 170 if (index < FIRST_LAYER_SIZE) { 171 // catch common glyphcodes 172 return firstLayerCache[index]; 173 } 174 175 if (index < SECOND_LAYER_SIZE) { 176 // catch common unicodes 177 if (secondLayerCache == null) return 0; 178 return secondLayerCache.get(index); 179 } 180 181 if (generalCache == null) return 0; 182 final Integer value = generalCache.get(new Integer(index)); 183 if (value == null) return 0; 184 return value.intValue(); 185 } 186 187 public void put(final char index, final int value) { 188 if (index < FIRST_LAYER_SIZE) { 189 // catch common glyphcodes 190 firstLayerCache[index] = value; 191 return; 192 } 193 194 if (index < SECOND_LAYER_SIZE) { 195 // catch common unicodes 196 if (secondLayerCache == null) { 197 secondLayerCache = new SparseBitShiftingTwoLayerArray(SECOND_LAYER_SIZE, 7); // 128x128 198 } 199 secondLayerCache.put(index, value); 200 return; 201 } 202 203 if (generalCache == null) { 204 generalCache = new HashMap<Integer, Integer>(); 205 } 206 207 generalCache.put(new Integer(index), new Integer(value)); 208 } 209 210 private class SparseBitShiftingTwoLayerArray { 211 final int[][] cache; 212 final int shift; 213 final int secondLayerLength; 214 215 public SparseBitShiftingTwoLayerArray(final int size, 216 final int shift) 217 { 218 this.shift = shift; 219 this.cache = new int[1 << shift][]; 220 this.secondLayerLength = size >> shift; 221 } 222 223 public int get(final char index) { 224 final int firstIndex = index >> shift; 225 final int[] firstLayerRow = cache[firstIndex]; 226 if (firstLayerRow == null) return 0; 227 return firstLayerRow[index - (firstIndex * (1 << shift))]; 228 } 229 230 public void put(final char index, final int value) { 231 final int firstIndex = index >> shift; 232 int[] firstLayerRow = cache[firstIndex]; 233 if (firstLayerRow == null) { 234 cache[firstIndex] = firstLayerRow = new int[secondLayerLength]; 235 } 236 firstLayerRow[index - (firstIndex * (1 << shift))] = value; 237 } 238 } 239 240 public void get(int count, char[] indicies, int[] values){ 241 int missed = 0; 242 for(int i = 0; i < count; i++){ 243 char code = indicies[i]; 244 245 final int value = get(code); 246 if(value != 0){ 247 values[i] = value; 248 }else{ 249 // zero this element out, because the caller does not 250 // promise to keep it clean 251 values[i] = 0; 252 missed++; 253 } 254 } 255 256 if (missed == 0) return; // horray! everything is already cached! 257 258 final char[] filteredCodes = new char[missed]; // all index codes requested (partially filled) 259 final int[] filteredIndicies = new int[missed]; // local indicies into filteredCodes array (totally filled) 260 261 // scan, mark, and store the index codes again to send into native 262 int j = 0; 263 int dupes = 0; 264 for (int i = 0; i < count; i++){ 265 if (values[i] != 0L) continue; // already filled 266 267 final char code = indicies[i]; 268 269 // we have already promised to fill this code - this is a dupe 270 if (get(code) == -1){ 271 filteredIndicies[j] = -1; 272 dupes++; 273 j++; 274 continue; 275 } 276 277 // this is a code we have not obtained before 278 // mark this one as "promise to get" in the global cache with a -1 279 final int k = j - dupes; 280 filteredCodes[k] = code; 281 put(code, -1); 282 filteredIndicies[j] = k; 283 j++; 284 } 285 286 final int filteredRunLen = j - dupes; 287 final int[] filteredValues = new int[filteredRunLen]; 288 289 // bulk call to fill in the distinct values 290 nativeCharsToGlyphs(fFont.getNativeFontPtr(), filteredRunLen, filteredCodes, filteredValues); 291 292 // scan the requested list, and fill in values from our 293 // distinct code list which has been filled from "getDistinct" 294 j = 0; 295 for (int i = 0; i < count; i++){ 296 if (values[i] != 0L && values[i] != -1L) continue; // already placed 297 298 final int k = filteredIndicies[j]; // index into filteredImages array 299 final char code = indicies[i]; 300 if(k == -1L){ 301 // we should have already filled the cache with this value 302 values[i] = get(code); 303 }else{ 304 // fill the particular code request, and store in the cache 305 final int ptr = filteredValues[k]; 306 values[i] = ptr; 307 put(code, ptr); 308 } 309 310 j++; 311 } 312 } 313 } 314 } | 113 } 114 } 115 116 return false; 117 } 118 119 public synchronized int charToGlyph(char unicode) { 120 final int glyph = cache.get(unicode); 121 if (glyph != 0) return glyph; 122 123 final char[] unicodeArray = new char[] { unicode }; 124 final int[] glyphArray = new int[1]; 125 126 nativeCharsToGlyphs(fFont.getNativeFontPtr(), 1, unicodeArray, glyphArray); 127 cache.put(unicode, glyphArray[0]); 128 129 return glyphArray[0]; 130 } 131 132 public synchronized int charToGlyph(int unicode) { 133 if (unicode >= 0x10000) { 134 int[] glyphs = new int[2]; 135 char[] surrogates = new char[2]; 136 int base = unicode - 0x10000; 137 surrogates[0] = (char)((base >>> 10) + HI_SURROGATE_START); 138 surrogates[1] = (char)((base % 0x400) + LO_SURROGATE_START); 139 charsToGlyphs(2, surrogates, glyphs); 140 return glyphs[0]; 141 } else 142 return charToGlyph((char)unicode); 143 } 144 145 public synchronized void charsToGlyphs(int count, char[] unicodes, int[] glyphs) { 146 cache.get(count, unicodes, glyphs); 147 } 148 149 public synchronized void charsToGlyphs(int count, int[] unicodes, int[] glyphs) { 150 for (int i = 0; i < count; i++) { 151 glyphs[i] = charToGlyph(unicodes[i]); 152 }; 153 } 154 155 // This mapper returns either the glyph code, or if the character can be 156 // replaced on-the-fly using CoreText substitution; the negative unicode 157 // value. If this "glyph code int" is treated as an opaque code, it will 158 // strike and measure exactly as a real glyph code - whether the character 159 // is present or not. Missing characters for any font on the system will 160 // be returned as 0, as the getMissingGlyphCode() function above indicates. 161 private static native void nativeCharsToGlyphs(final long nativeFontPtr, 162 int count, char[] unicodes, 163 int[] glyphs); 164 165 private class Cache { 166 private static final int FIRST_LAYER_SIZE = 256; 167 private static final int SECOND_LAYER_SIZE = 16384; // 16384 = 128x128 168 169 private final int[] firstLayerCache = new int[FIRST_LAYER_SIZE]; 170 private SparseBitShiftingTwoLayerArray secondLayerCache; 171 private HashMap<Integer, Integer> generalCache; 172 173 Cache() { 174 // <rdar://problem/5331678> need to prevent getting '-1' stuck in the cache 175 firstLayerCache[1] = 1; 176 } 177 178 public synchronized int get(final int index) { 179 if (index < FIRST_LAYER_SIZE) { 180 // catch common glyphcodes 181 return firstLayerCache[index]; 182 } 183 184 if (index < SECOND_LAYER_SIZE) { 185 // catch common unicodes 186 if (secondLayerCache == null) return 0; 187 return secondLayerCache.get(index); 188 } 189 190 if (generalCache == null) return 0; 191 final Integer value = generalCache.get(index); 192 if (value == null) return 0; 193 return value.intValue(); 194 } 195 196 public synchronized void put(final int index, final int value) { 197 if (index < FIRST_LAYER_SIZE) { 198 // catch common glyphcodes 199 firstLayerCache[index] = value; 200 return; 201 } 202 203 if (index < SECOND_LAYER_SIZE) { 204 // catch common unicodes 205 if (secondLayerCache == null) { 206 secondLayerCache = new SparseBitShiftingTwoLayerArray(SECOND_LAYER_SIZE, 7); // 128x128 207 } 208 secondLayerCache.put(index, value); 209 return; 210 } 211 212 if (generalCache == null) { 213 generalCache = new HashMap<Integer, Integer>(); 214 } 215 216 generalCache.put(index, value); 217 } 218 219 private class SparseBitShiftingTwoLayerArray { 220 final int[][] cache; 221 final int shift; 222 final int secondLayerLength; 223 224 public SparseBitShiftingTwoLayerArray(final int size, 225 final int shift) 226 { 227 this.shift = shift; 228 this.cache = new int[1 << shift][]; 229 this.secondLayerLength = size >> shift; 230 } 231 232 public int get(final int index) { 233 final int firstIndex = index >> shift; 234 final int[] firstLayerRow = cache[firstIndex]; 235 if (firstLayerRow == null) return 0; 236 return firstLayerRow[index - (firstIndex * (1 << shift))]; 237 } 238 239 public void put(final int index, final int value) { 240 final int firstIndex = index >> shift; 241 int[] firstLayerRow = cache[firstIndex]; 242 if (firstLayerRow == null) { 243 cache[firstIndex] = firstLayerRow = new int[secondLayerLength]; 244 } 245 firstLayerRow[index - (firstIndex * (1 << shift))] = value; 246 } 247 } 248 249 public synchronized void get(int count, char[] indicies, int[] values) 250 { 251 // "missed" is the count of 'char' that are not mapped. 252 // Surrogates count for 2. 253 // unmappedChars is the unique list of these chars. 254 // unmappedCharIndices is the location in the original array 255 int missed = 0; 256 char[] unmappedChars = null; 257 int [] unmappedCharIndices = null; 258 259 for (int i = 0; i < count; i++){ 260 int code = indicies[i]; 261 if (code >= HI_SURROGATE_START && 262 code <= HI_SURROGATE_END && i < count - 1) 263 { 264 char low = indicies[i + 1]; 265 if (low >= LO_SURROGATE_START && low <= LO_SURROGATE_END) { 266 code = (code - HI_SURROGATE_START) * 0x400 + 267 low - LO_SURROGATE_START + 0x10000; 268 } 269 } 270 271 final int value = get(code); 272 if (value != 0 && value != -1) { 273 values[i] = value; 274 if (code >= 0x10000) { 275 values[i+1] = INVISIBLE_GLYPH_ID; 276 i++; 277 } 278 } else { 279 values[i] = 0; 280 put(code, -1); 281 if (unmappedChars == null) { 282 // This is likely to be longer than we need, 283 // but is the simplest and cheapest option. 284 unmappedChars = new char[indicies.length]; 285 unmappedCharIndices = new int[indicies.length]; 286 } 287 unmappedChars[missed] = indicies[i]; 288 unmappedCharIndices[missed] = i; 289 if (code >= 0x10000) { // was a surrogate pair 290 unmappedChars[++missed] = indicies[++i]; 291 } 292 missed++; 293 } 294 } 295 296 if (missed == 0) { 297 return; 298 } 299 300 final int[] glyphCodes = new int[missed]; 301 302 // bulk call to fill in the unmapped code points. 303 nativeCharsToGlyphs(fFont.getNativeFontPtr(), 304 missed, unmappedChars, glyphCodes); 305 306 for (int m = 0; m < missed; m++){ 307 int i = unmappedCharIndices[m]; 308 int code = unmappedChars[m]; 309 if (code >= HI_SURROGATE_START && 310 code <= HI_SURROGATE_END && m < missed - 1) 311 { 312 char low = indicies[m + 1]; 313 if (low >= LO_SURROGATE_START && low <= LO_SURROGATE_END) { 314 code = (code - HI_SURROGATE_START) * 0x400 + 315 low - LO_SURROGATE_START + 0x10000; 316 } 317 } 318 values[i] = glyphCodes[m]; 319 put(code, values[i]); 320 if (code >= 0x10000) { 321 m++; 322 values[i + 1] = INVISIBLE_GLYPH_ID; 323 } 324 } 325 } 326 } 327 } |