Path: blob/master/src/java.desktop/macosx/classes/sun/font/CCharToGlyphMapper.java
41153 views
/*1* Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.font;2627import java.util.HashMap;2829public class CCharToGlyphMapper extends CharToGlyphMapper {30private static native int countGlyphs(final long nativeFontPtr);3132private Cache cache = new Cache();33CFont fFont;34int numGlyphs = -1;3536public CCharToGlyphMapper(CFont font) {37fFont = font;38missingGlyph = 0; // for getMissingGlyphCode()39}4041public int getNumGlyphs() {42if (numGlyphs == -1) {43numGlyphs = countGlyphs(fFont.getNativeFontPtr());44}45return numGlyphs;46}4748public boolean canDisplay(char ch) {49int glyph = charToGlyph(ch);50return glyph != missingGlyph;51}5253public boolean canDisplay(int cp) {54int glyph = charToGlyph(cp);55return glyph != missingGlyph;56}5758public synchronized boolean charsToGlyphsNS(int count,59char[] unicodes, int[] glyphs)60{61charsToGlyphs(count, unicodes, glyphs);6263// The following shaping checks are from either64// TrueTypeGlyphMapper or Type1GlyphMapper65for (int i = 0; i < count; i++) {66int code = unicodes[i];6768if (code >= HI_SURROGATE_START && code <= HI_SURROGATE_END && i < count - 1) {69char low = unicodes[i + 1];7071if (low >= LO_SURROGATE_START && low <= LO_SURROGATE_END) {72code = (code - HI_SURROGATE_START) * 0x400 + low - LO_SURROGATE_START + 0x10000;73glyphs[i + 1] = INVISIBLE_GLYPH_ID;74}75}7677if (code < FontUtilities.MIN_LAYOUT_CHARCODE) {78continue;79} else if (FontUtilities.isComplexCharCode(code)) {80return true;81} else if (code >= 0x10000) {82i += 1; // Empty glyph slot after surrogate83continue;84}85}8687return false;88}8990public synchronized int charToGlyph(char unicode) {91final int glyph = cache.get(unicode);92if (glyph != 0) return glyph;9394final char[] unicodeArray = new char[] { unicode };95final int[] glyphArray = new int[1];9697nativeCharsToGlyphs(fFont.getNativeFontPtr(), 1, unicodeArray, glyphArray);98cache.put(unicode, glyphArray[0]);99100return glyphArray[0];101}102103public synchronized int charToGlyph(int unicode) {104if (unicode >= 0x10000) {105int[] glyphs = new int[2];106char[] surrogates = new char[2];107int base = unicode - 0x10000;108surrogates[0] = (char)((base >>> 10) + HI_SURROGATE_START);109surrogates[1] = (char)((base % 0x400) + LO_SURROGATE_START);110charsToGlyphs(2, surrogates, glyphs);111return glyphs[0];112} else {113return charToGlyph((char)unicode);114}115}116117public synchronized void charsToGlyphs(int count, char[] unicodes, int[] glyphs) {118cache.get(count, unicodes, glyphs);119}120121public synchronized void charsToGlyphs(int count, int[] unicodes, int[] glyphs) {122for (int i = 0; i < count; i++) {123glyphs[i] = charToGlyph(unicodes[i]);124};125}126127// This mapper returns either the glyph code, or if the character can be128// replaced on-the-fly using CoreText substitution; the negative unicode129// value. If this "glyph code int" is treated as an opaque code, it will130// strike and measure exactly as a real glyph code - whether the character131// is present or not. Missing characters for any font on the system will132// be returned as 0, as the getMissingGlyphCode() function above indicates.133private static native void nativeCharsToGlyphs(final long nativeFontPtr,134int count, char[] unicodes,135int[] glyphs);136137private class Cache {138private static final int FIRST_LAYER_SIZE = 256;139private static final int SECOND_LAYER_SIZE = 16384; // 16384 = 128x128140141private final int[] firstLayerCache = new int[FIRST_LAYER_SIZE];142private SparseBitShiftingTwoLayerArray secondLayerCache;143private HashMap<Integer, Integer> generalCache;144145Cache() {146// <rdar://problem/5331678> need to prevent getting '-1' stuck in the cache147firstLayerCache[1] = 1;148}149150public synchronized int get(final int index) {151if (index < FIRST_LAYER_SIZE) {152// catch common glyphcodes153return firstLayerCache[index];154}155156if (index < SECOND_LAYER_SIZE) {157// catch common unicodes158if (secondLayerCache == null) return 0;159return secondLayerCache.get(index);160}161162if (generalCache == null) return 0;163final Integer value = generalCache.get(index);164if (value == null) return 0;165return value.intValue();166}167168public synchronized void put(final int index, final int value) {169if (index < FIRST_LAYER_SIZE) {170// catch common glyphcodes171firstLayerCache[index] = value;172return;173}174175if (index < SECOND_LAYER_SIZE) {176// catch common unicodes177if (secondLayerCache == null) {178secondLayerCache = new SparseBitShiftingTwoLayerArray(SECOND_LAYER_SIZE, 7); // 128x128179}180secondLayerCache.put(index, value);181return;182}183184if (generalCache == null) {185generalCache = new HashMap<Integer, Integer>();186}187188generalCache.put(index, value);189}190191private class SparseBitShiftingTwoLayerArray {192final int[][] cache;193final int shift;194final int secondLayerLength;195196public SparseBitShiftingTwoLayerArray(final int size,197final int shift)198{199this.shift = shift;200this.cache = new int[1 << shift][];201this.secondLayerLength = size >> shift;202}203204public int get(final int index) {205final int firstIndex = index >> shift;206final int[] firstLayerRow = cache[firstIndex];207if (firstLayerRow == null) return 0;208return firstLayerRow[index - (firstIndex * (1 << shift))];209}210211public void put(final int index, final int value) {212final int firstIndex = index >> shift;213int[] firstLayerRow = cache[firstIndex];214if (firstLayerRow == null) {215cache[firstIndex] = firstLayerRow = new int[secondLayerLength];216}217firstLayerRow[index - (firstIndex * (1 << shift))] = value;218}219}220221public synchronized void get(int count, char[] indicies, int[] values)222{223// "missed" is the count of 'char' that are not mapped.224// Surrogates count for 2.225// unmappedChars is the unique list of these chars.226// unmappedCharIndices is the location in the original array227int missed = 0;228char[] unmappedChars = null;229int [] unmappedCharIndices = null;230231for (int i = 0; i < count; i++){232int code = indicies[i];233if (code >= HI_SURROGATE_START &&234code <= HI_SURROGATE_END && i < count - 1)235{236char low = indicies[i + 1];237if (low >= LO_SURROGATE_START && low <= LO_SURROGATE_END) {238code = (code - HI_SURROGATE_START) * 0x400 +239low - LO_SURROGATE_START + 0x10000;240}241}242243final int value = get(code);244if (value != 0 && value != -1) {245values[i] = value;246if (code >= 0x10000) {247values[i+1] = INVISIBLE_GLYPH_ID;248i++;249}250} else {251values[i] = 0;252put(code, -1);253if (unmappedChars == null) {254// This is likely to be longer than we need,255// but is the simplest and cheapest option.256unmappedChars = new char[indicies.length];257unmappedCharIndices = new int[indicies.length];258}259unmappedChars[missed] = indicies[i];260unmappedCharIndices[missed] = i;261if (code >= 0x10000) { // was a surrogate pair262unmappedChars[++missed] = indicies[++i];263}264missed++;265}266}267268if (missed == 0) {269return;270}271272final int[] glyphCodes = new int[missed];273274// bulk call to fill in the unmapped code points.275nativeCharsToGlyphs(fFont.getNativeFontPtr(),276missed, unmappedChars, glyphCodes);277278for (int m = 0; m < missed; m++){279int i = unmappedCharIndices[m];280int code = unmappedChars[m];281if (code >= HI_SURROGATE_START &&282code <= HI_SURROGATE_END && m < missed - 1)283{284char low = unmappedChars[m + 1];285if (low >= LO_SURROGATE_START && low <= LO_SURROGATE_END) {286code = (code - HI_SURROGATE_START) * 0x400 +287low - LO_SURROGATE_START + 0x10000;288}289}290values[i] = glyphCodes[m];291put(code, values[i]);292if (code >= 0x10000) {293m++;294values[i + 1] = INVISIBLE_GLYPH_ID;295}296}297}298}299}300301302