Path: blob/master/src/java.desktop/share/classes/sun/font/CompositeGlyphMapper.java
41154 views
/*1* Copyright (c) 2003, 2018, 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;2627/* remember that the API requires a Font use a28* consistent glyph id. for a code point, and this is a29* problem if a particular strike uses native scaler sometimes30* and the JDK scaler others. That needs to be dealt with somewhere, but31* here we can just always get the same glyph code without32* needing a strike.33*34* The C implementation would cache the results of anything up35* to the maximum surrogate pair code point.36* This implementation will not cache as much, since the storage37* requirements are not justifiable. Even so it still can use up38* to 216*256*4 bytes of storage per composite font. If an app39* calls canDisplay on this range for all 20 composite fonts that's40* over 1Mb of cached data. May need to employ WeakReferences if41* this appears to cause problems.42*/4344public class CompositeGlyphMapper extends CharToGlyphMapper {4546public static final int SLOTMASK = 0xff000000;47public static final int GLYPHMASK = 0x00ffffff;4849public static final int NBLOCKS = 216;50public static final int BLOCKSZ = 256;51public static final int MAXUNICODE = NBLOCKS*BLOCKSZ;525354CompositeFont font;55CharToGlyphMapper[] slotMappers;56int[][] glyphMaps;57private boolean hasExcludes;5859public CompositeGlyphMapper(CompositeFont compFont) {60font = compFont;61initMapper();62/* This is often false which saves the overhead of a63* per-mapped char method call.64*/65hasExcludes = compFont.exclusionRanges != null &&66compFont.maxIndices != null;67}6869public int compositeGlyphCode(int slot, int glyphCode) {70return (slot << 24 | (glyphCode & GLYPHMASK));71}7273private void initMapper() {74if (missingGlyph == CharToGlyphMapper.UNINITIALIZED_GLYPH) {75if (glyphMaps == null) {76glyphMaps = new int[NBLOCKS][];77}78slotMappers = new CharToGlyphMapper[font.numSlots];79/* This requires that slot 0 is never empty. */80missingGlyph = font.getSlotFont(0).getMissingGlyphCode();81missingGlyph = compositeGlyphCode(0, missingGlyph);82}83}8485private int getCachedGlyphCode(int unicode) {86if (unicode >= MAXUNICODE) {87return UNINITIALIZED_GLYPH; // don't cache surrogates88}89int[] gmap;90if ((gmap = glyphMaps[unicode >> 8]) == null) {91return UNINITIALIZED_GLYPH;92}93return gmap[unicode & 0xff];94}9596private void setCachedGlyphCode(int unicode, int glyphCode) {97if (unicode >= MAXUNICODE) {98return; // don't cache surrogates99}100int index0 = unicode >> 8;101if (glyphMaps[index0] == null) {102glyphMaps[index0] = new int[BLOCKSZ];103for (int i=0;i<BLOCKSZ;i++) {104glyphMaps[index0][i] = UNINITIALIZED_GLYPH;105}106}107glyphMaps[index0][unicode & 0xff] = glyphCode;108}109110private CharToGlyphMapper getSlotMapper(int slot) {111CharToGlyphMapper mapper = slotMappers[slot];112if (mapper == null) {113mapper = font.getSlotFont(slot).getMapper();114slotMappers[slot] = mapper;115}116return mapper;117}118119private int convertToGlyph(int unicode) {120121for (int slot = 0; slot < font.numSlots; slot++) {122if (!hasExcludes || !font.isExcludedChar(slot, unicode)) {123CharToGlyphMapper mapper = getSlotMapper(slot);124int glyphCode = mapper.charToGlyph(unicode);125if (glyphCode != mapper.getMissingGlyphCode()) {126glyphCode = compositeGlyphCode(slot, glyphCode);127setCachedGlyphCode(unicode, glyphCode);128return glyphCode;129}130}131}132return missingGlyph;133}134135public int getNumGlyphs() {136int numGlyphs = 0;137/* The number of glyphs in a composite is affected by138* exclusion ranges and duplicates (ie the same code point is139* mapped by two different fonts) and also whether or not to140* count fallback fonts. A nearly correct answer would be very141* expensive to generate. A rough ballpark answer would142* just count the glyphs in all the slots. However this would143* initialize mappers for all slots when they aren't necessarily144* needed. For now just use the first slot as JDK 1.4 did.145*/146for (int slot=0; slot<1 /*font.numSlots*/; slot++) {147CharToGlyphMapper mapper = slotMappers[slot];148if (mapper == null) {149mapper = font.getSlotFont(slot).getMapper();150slotMappers[slot] = mapper;151}152numGlyphs += mapper.getNumGlyphs();153}154return numGlyphs;155}156157public int charToGlyph(int unicode) {158159int glyphCode = getCachedGlyphCode(unicode);160if (glyphCode == UNINITIALIZED_GLYPH) {161glyphCode = convertToGlyph(unicode);162}163return glyphCode;164}165166public int charToGlyph(int unicode, int prefSlot) {167if (prefSlot >= 0) {168CharToGlyphMapper mapper = getSlotMapper(prefSlot);169int glyphCode = mapper.charToGlyph(unicode);170if (glyphCode != mapper.getMissingGlyphCode()) {171return compositeGlyphCode(prefSlot, glyphCode);172}173}174return charToGlyph(unicode);175}176177public int charToGlyph(char unicode) {178179int glyphCode = getCachedGlyphCode(unicode);180if (glyphCode == UNINITIALIZED_GLYPH) {181glyphCode = convertToGlyph(unicode);182}183return glyphCode;184}185186/* This variant checks if shaping is needed and immediately187* returns true if it does. A caller of this method should be expecting188* to check the return type because it needs to know how to handle189* the character data for display.190*/191public boolean charsToGlyphsNS(int count, char[] unicodes, int[] glyphs) {192193for (int i=0; i<count; i++) {194int code = unicodes[i]; // char is unsigned.195196if (code >= HI_SURROGATE_START &&197code <= HI_SURROGATE_END && i < count - 1) {198char low = unicodes[i + 1];199200if (low >= LO_SURROGATE_START &&201low <= LO_SURROGATE_END) {202code = (code - HI_SURROGATE_START) *2030x400 + low - LO_SURROGATE_START + 0x10000;204glyphs[i + 1] = INVISIBLE_GLYPH_ID;205}206}207208int gc = glyphs[i] = getCachedGlyphCode(code);209if (gc == UNINITIALIZED_GLYPH) {210glyphs[i] = convertToGlyph(code);211}212213if (code < FontUtilities.MIN_LAYOUT_CHARCODE) {214continue;215}216else if (FontUtilities.isComplexCharCode(code) ||217CharToGlyphMapper.isVariationSelector(code)) {218return true;219}220else if (code >= 0x10000) {221i += 1; // Empty glyph slot after surrogate222continue;223}224}225226return false;227}228229/* The conversion is not very efficient - looping as it does, converting230* one char at a time. However the cache should fill very rapidly.231*/232public void charsToGlyphs(int count, char[] unicodes, int[] glyphs) {233for (int i=0; i<count; i++) {234int code = unicodes[i]; // char is unsigned.235236if (code >= HI_SURROGATE_START &&237code <= HI_SURROGATE_END && i < count - 1) {238char low = unicodes[i + 1];239240if (low >= LO_SURROGATE_START &&241low <= LO_SURROGATE_END) {242code = (code - HI_SURROGATE_START) *2430x400 + low - LO_SURROGATE_START + 0x10000;244245int gc = glyphs[i] = getCachedGlyphCode(code);246if (gc == UNINITIALIZED_GLYPH) {247glyphs[i] = convertToGlyph(code);248}249i += 1; // Empty glyph slot after surrogate250glyphs[i] = INVISIBLE_GLYPH_ID;251continue;252}253}254255int gc = glyphs[i] = getCachedGlyphCode(code);256if (gc == UNINITIALIZED_GLYPH) {257glyphs[i] = convertToGlyph(code);258}259}260}261262public void charsToGlyphs(int count, int[] unicodes, int[] glyphs) {263for (int i=0; i<count; i++) {264int code = unicodes[i];265266glyphs[i] = getCachedGlyphCode(code);267if (glyphs[i] == UNINITIALIZED_GLYPH) {268glyphs[i] = convertToGlyph(code);269}270}271}272273}274275276