Path: blob/master/src/java.desktop/share/classes/sun/font/Font2D.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;2627import java.awt.Font;28import java.awt.font.FontRenderContext;29import java.awt.geom.AffineTransform;30import java.lang.ref.Reference;31import java.lang.ref.SoftReference;32import java.lang.ref.WeakReference;33import java.util.concurrent.ConcurrentHashMap;34import java.util.Locale;35import java.util.Set;3637public abstract class Font2D {3839/* Note: JRE and FONT_CONFIG ranks are identical. I don't know of a reason40* to distingish these. Possibly if a user adds fonts to the JRE font41* directory that are the same font as the ones specified in the font42* configuration but that is more likely to be the legitimate intention43* than a problem. One reason why these should be the same is that on44* Linux the JRE fonts ARE the font configuration fonts, and although I45* believe all are assigned FONT_CONFIG rank, it is conceivable that if46* this were not so, that some JRE font would not be allowed to joint the47* family of its siblings which were assigned FONT_CONFIG rank. Giving48* them the same rank is the easy solution for now at least.49*/50public static final int FONT_CONFIG_RANK = 2;51public static final int JRE_RANK = 2;52public static final int TTF_RANK = 3;53public static final int TYPE1_RANK = 4;54public static final int NATIVE_RANK = 5;55public static final int UNKNOWN_RANK = 6;56public static final int DEFAULT_RANK = 4;5758private static final String[] boldNames = {59"bold", "demibold", "demi-bold", "demi bold", "negreta", "demi", };6061private static final String[] italicNames = {62"italic", "cursiva", "oblique", "inclined", };6364private static final String[] boldItalicNames = {65"bolditalic", "bold-italic", "bold italic",66"boldoblique", "bold-oblique", "bold oblique",67"demibold italic", "negreta cursiva","demi oblique", };6869private static final FontRenderContext DEFAULT_FRC =70new FontRenderContext(null, false, false);7172public Font2DHandle handle;73protected String familyName; /* Family font name (english) */74protected String fullName; /* Full font name (english) */75protected int style = Font.PLAIN;76protected FontFamily family;77protected int fontRank = DEFAULT_RANK;7879/*80* A mapper can be independent of the strike.81* Perhaps the reference to the mapper ought to be held on the82* scaler, as it may be implemented via scaler functionality anyway83* and so the mapper would be useless if its native portion was84* freed when the scaler was GC'd.85*/86protected CharToGlyphMapper mapper;8788/*89* The strike cache is maintained per "Font2D" as that is the90* principal object by which you look up fonts.91* It means more Hashmaps, but look ups can be quicker because92* the map will have fewer entries, and there's no need to try to93* make the Font2D part of the key.94*/95protected ConcurrentHashMap<FontStrikeDesc, Reference<FontStrike>>96strikeCache = new ConcurrentHashMap<>();9798/* Store the last Strike in a Reference object.99* Similarly to the strike that was stored on a C++ font object,100* this is an optimisation which helps if multiple clients (ie101* typically SunGraphics2D instances) are using the same font, then102* as may be typical of many UIs, they are probably using it in the103* same style, so it can be a win to first quickly check if the last104* strike obtained from this Font2D satifies the needs of the next105* client too.106* This pre-supposes that a FontStrike is a shareable object, which107* it should.108*/109protected Reference<FontStrike> lastFontStrike = new WeakReference<>(null);110111/*112* if useWeak is true, proactively clear the cache after this113* many strikes are present. 0 means leave it alone.114*/115private int strikeCacheMax = 0;116/*117* Whether to use weak refs for this font, even if soft refs is the default.118*/119private boolean useWeak;120121void setUseWeakRefs(boolean weak, int maxStrikes) {122this.useWeak = weak;123this.strikeCacheMax = weak && maxStrikes > 0 ? maxStrikes : 0;124}125126/*127* POSSIBLE OPTIMISATION:128* Array of length 1024 elements of 64 bits indicating if a font129* contains these. This kind of information can be shared between130* all point sizes.131* if corresponding bit in knownBitmaskMap is set then canDisplayBitmaskMap132* is valid. This is 16Kbytes of data per composite font style.133* What about UTF-32 and surrogates?134* REMIND: This is too much storage. Probably can only cache this135* information for latin range, although possibly OK to store all136* for just the "logical" fonts.137* Or instead store arrays of subranges of 1024 bits (128 bytes) in138* the range below surrogate pairs.139*/140// protected long[] knownBitmaskMap;141// protected long[] canDisplayBitmaskMap;142143/* Returns the "real" style of this Font2D. Eg the font face144* Lucida Sans Bold" has a real style of Font.BOLD, even though145* it may be able to used to simulate bold italic146*/147public int getStyle() {148return style;149}150protected void setStyle() {151152String fName = fullName.toLowerCase();153154for (int i=0; i < boldItalicNames.length; i++) {155if (fName.indexOf(boldItalicNames[i]) != -1) {156style = Font.BOLD|Font.ITALIC;157return;158}159}160161for (int i=0; i < italicNames.length; i++) {162if (fName.indexOf(italicNames[i]) != -1) {163style = Font.ITALIC;164return;165}166}167168for (int i=0; i < boldNames.length; i++) {169if (fName.indexOf(boldNames[i]) != -1 ) {170style = Font.BOLD;171return;172}173}174}175176public static final int FWIDTH_NORMAL = 5; // OS/2 usWidthClass177public static final int FWEIGHT_NORMAL = 400; // OS/2 usWeightClass178public static final int FWEIGHT_BOLD = 700; // OS/2 usWeightClass179180public int getWidth() {181return FWIDTH_NORMAL;182}183184public int getWeight() {185if ((style & Font.BOLD) !=0) {186return FWEIGHT_BOLD;187} else {188return FWEIGHT_NORMAL;189}190}191192int getRank() {193return fontRank;194}195196void setRank(int rank) {197fontRank = rank;198}199200abstract CharToGlyphMapper getMapper();201202203204/* This isn't very efficient but its infrequently used.205* StandardGlyphVector uses it when the client assigns the glyph codes.206* These may not be valid. This validates them substituting the missing207* glyph elsewhere.208*/209protected int getValidatedGlyphCode(int glyphCode) {210if (glyphCode < 0 || glyphCode >= getMapper().getNumGlyphs()) {211glyphCode = getMapper().getMissingGlyphCode();212}213return glyphCode;214}215216/*217* Creates an appropriate strike for the Font2D subclass218*/219abstract FontStrike createStrike(FontStrikeDesc desc);220221/* this may be useful for APIs like canDisplay where the answer222* is dependent on the font and its scaler, but not the strike.223* If no strike has ever been returned, then create a one that matches224* this font with the default FRC. It will become the lastStrike and225* there's a good chance that the next call will be to get exactly that226* strike.227*/228public FontStrike getStrike(Font font) {229FontStrike strike = lastFontStrike.get();230if (strike != null) {231return strike;232} else {233return getStrike(font, DEFAULT_FRC);234}235}236237/* SunGraphics2D has font, tx, aa and fm. From this info238* can get a Strike object from the cache, creating it if necessary.239* This code is designed for multi-threaded access.240* For that reason it creates a local FontStrikeDesc rather than filling241* in a shared one. Up to two AffineTransforms and one FontStrikeDesc will242* be created by every lookup. This appears to perform more than243* adequately. But it may make sense to expose FontStrikeDesc244* as a parameter so a caller can use its own.245* In such a case if a FontStrikeDesc is stored as a key then246* we would need to use a private copy.247*248* Note that this code doesn't prevent two threads from creating249* two different FontStrike instances and having one of the threads250* overwrite the other in the map. This is likely to be a rare251* occurrence and the only consequence is that these callers will have252* different instances of the strike, and there'd be some duplication of253* population of the strikes. However since users of these strikes are254* transient, then the one that was overwritten would soon be freed.255* If there is any problem then a small synchronized block would be256* required with its attendant consequences for MP scaleability.257*/258public FontStrike getStrike(Font font, AffineTransform devTx,259int aa, int fm) {260261/* Create the descriptor which is used to identify a strike262* in the strike cache/map. A strike is fully described by263* the attributes of this descriptor.264*/265/* REMIND: generating garbage and doing computation here in order266* to include pt size in the tx just for a lookup! Figure out a267* better way.268*/269double ptSize = font.getSize2D();270AffineTransform glyphTx = (AffineTransform)devTx.clone();271glyphTx.scale(ptSize, ptSize);272if (font.isTransformed()) {273glyphTx.concatenate(font.getTransform());274}275if (glyphTx.getTranslateX() != 0 || glyphTx.getTranslateY() != 0) {276glyphTx.setTransform(glyphTx.getScaleX(),277glyphTx.getShearY(),278glyphTx.getShearX(),279glyphTx.getScaleY(),2800.0, 0.0);281}282FontStrikeDesc desc = new FontStrikeDesc(devTx, glyphTx,283font.getStyle(), aa, fm);284return getStrike(desc, false);285}286287public FontStrike getStrike(Font font, AffineTransform devTx,288AffineTransform glyphTx,289int aa, int fm) {290291/* Create the descriptor which is used to identify a strike292* in the strike cache/map. A strike is fully described by293* the attributes of this descriptor.294*/295FontStrikeDesc desc = new FontStrikeDesc(devTx, glyphTx,296font.getStyle(), aa, fm);297return getStrike(desc, false);298}299300public FontStrike getStrike(Font font, FontRenderContext frc) {301302AffineTransform at = frc.getTransform();303double ptSize = font.getSize2D();304at.scale(ptSize, ptSize);305if (font.isTransformed()) {306at.concatenate(font.getTransform());307if (at.getTranslateX() != 0 || at.getTranslateY() != 0) {308at.setTransform(at.getScaleX(),309at.getShearY(),310at.getShearX(),311at.getScaleY(),3120.0, 0.0);313}314}315int aa = FontStrikeDesc.getAAHintIntVal(this, font, frc);316int fm = FontStrikeDesc.getFMHintIntVal(frc.getFractionalMetricsHint());317FontStrikeDesc desc = new FontStrikeDesc(frc.getTransform(),318at, font.getStyle(),319aa, fm);320return getStrike(desc, false);321}322323void updateLastStrikeRef(FontStrike strike) {324lastFontStrike.clear();325if (useWeak) {326lastFontStrike = new WeakReference<>(strike);327} else {328lastFontStrike = new SoftReference<>(strike);329}330}331332FontStrike getStrike(FontStrikeDesc desc) {333return getStrike(desc, true);334}335336private FontStrike getStrike(FontStrikeDesc desc, boolean copy) {337/* Before looking in the map, see if the descriptor matches the338* last strike returned from this Font2D. This should often be a win339* since its common for the same font, in the same size to be340* used frequently, for example in many parts of a UI.341*342* If its not the same then we use the descriptor to locate a343* Reference to the strike. If it exists and points to a strike,344* then we update the last strike to refer to that and return it.345*346* If the key isn't in the map, or its reference object has been347* collected, then we create a new strike, put it in the map and348* set it to be the last strike.349*/350FontStrike strike = lastFontStrike.get();351if (strike != null && desc.equals(strike.desc)) {352return strike;353} else {354Reference<FontStrike> strikeRef = strikeCache.get(desc);355if (strikeRef != null) {356strike = strikeRef.get();357if (strike != null) {358updateLastStrikeRef(strike);359StrikeCache.refStrike(strike);360return strike;361}362}363/* When we create a new FontStrike instance, we *must*364* ask the StrikeCache for a reference. We must then ensure365* this reference remains reachable, by storing it in the366* Font2D's strikeCache map.367* So long as the Reference is there (reachable) then if the368* reference is cleared, it will be enqueued for disposal.369* If for some reason we explicitly remove this reference, it370* must only be done when holding a strong reference to the371* referent (the FontStrike), or if the reference is cleared,372* then we must explicitly "dispose" of the native resources.373* The only place this currently happens is in this same method,374* where we find a cleared reference and need to overwrite it375* here with a new reference.376* Clearing the whilst holding a strong reference, should only377* be done if the378*/379if (copy) {380desc = new FontStrikeDesc(desc);381}382strike = createStrike(desc);383//StrikeCache.addStrike();384/* If we are creating many strikes on this font which385* involve non-quadrant rotations, or more general386* transforms which include shears, then force the use387* of weak references rather than soft references.388* This means that it won't live much beyond the next GC,389* which is what we want for what is likely a transient strike.390*/391int txType = desc.glyphTx.getType();392if (useWeak ||393txType == AffineTransform.TYPE_GENERAL_TRANSFORM ||394(txType & AffineTransform.TYPE_GENERAL_ROTATION) != 0 &&395strikeCache.size() > 10) {396strikeRef = StrikeCache.getStrikeRef(strike, true);397} else {398strikeRef = StrikeCache.getStrikeRef(strike, useWeak);399}400strikeCache.put(desc, strikeRef);401updateLastStrikeRef(strike);402StrikeCache.refStrike(strike);403return strike;404}405}406407/**408* The length of the metrics array must be >= 8. This method will409* store the following elements in that array before returning:410* metrics[0]: ascent411* metrics[1]: descent412* metrics[2]: leading413* metrics[3]: max advance414* metrics[4]: strikethrough offset415* metrics[5]: strikethrough thickness416* metrics[6]: underline offset417* metrics[7]: underline thickness418*/419public void getFontMetrics(Font font, AffineTransform at,420Object aaHint, Object fmHint,421float[] metrics) {422/* This is called in just one place in Font with "at" == identity.423* Perhaps this can be eliminated.424*/425int aa = FontStrikeDesc.getAAHintIntVal(aaHint, this, font.getSize());426int fm = FontStrikeDesc.getFMHintIntVal(fmHint);427FontStrike strike = getStrike(font, at, aa, fm);428StrikeMetrics strikeMetrics = strike.getFontMetrics();429metrics[0] = strikeMetrics.getAscent();430metrics[1] = strikeMetrics.getDescent();431metrics[2] = strikeMetrics.getLeading();432metrics[3] = strikeMetrics.getMaxAdvance();433434getStyleMetrics(font.getSize2D(), metrics, 4);435}436437/**438* The length of the metrics array must be >= offset+4, and offset must be439* >= 0. Typically offset is 4. This method will440* store the following elements in that array before returning:441* metrics[off+0]: strikethrough offset442* metrics[off+1]: strikethrough thickness443* metrics[off+2]: underline offset444* metrics[off+3]: underline thickness445*446* Note that this implementation simply returns default values;447* subclasses can override this method to provide more accurate values.448*/449public void getStyleMetrics(float pointSize, float[] metrics, int offset) {450metrics[offset] = -metrics[0] / 2.5f;451metrics[offset+1] = pointSize / 12;452metrics[offset+2] = metrics[offset+1] / 1.5f;453metrics[offset+3] = metrics[offset+1];454}455456/**457* The length of the metrics array must be >= 4. This method will458* store the following elements in that array before returning:459* metrics[0]: ascent460* metrics[1]: descent461* metrics[2]: leading462* metrics[3]: max advance463*/464public void getFontMetrics(Font font, FontRenderContext frc,465float[] metrics) {466StrikeMetrics strikeMetrics = getStrike(font, frc).getFontMetrics();467metrics[0] = strikeMetrics.getAscent();468metrics[1] = strikeMetrics.getDescent();469metrics[2] = strikeMetrics.getLeading();470metrics[3] = strikeMetrics.getMaxAdvance();471}472473/* Currently the layout code calls this. May be better for layout code474* to check the font class before attempting to run, rather than needing475* to promote this method up from TrueTypeFont476*/477protected byte[] getTableBytes(int tag) {478return null;479}480481/* Used only on OS X.482*/483protected long getPlatformNativeFontPtr() {484return 0L;485}486487/* for layout code */488protected long getUnitsPerEm() {489return 2048;490}491492boolean supportsEncoding(String encoding) {493return false;494}495496public boolean canDoStyle(int style) {497return (style == this.style);498}499500/*501* All the important subclasses override this which is principally for502* the TrueType 'gasp' table.503*/504public boolean useAAForPtSize(int ptsize) {505return true;506}507508public boolean hasSupplementaryChars() {509return false;510}511512/* The following methods implement public methods on java.awt.Font */513public String getPostscriptName() {514return fullName;515}516517public String getFontName(Locale l) {518return fullName;519}520521public String getFamilyName(Locale l) {522return familyName;523}524525public int getNumGlyphs() {526return getMapper().getNumGlyphs();527}528529public int charToGlyph(int wchar) {530return getMapper().charToGlyph(wchar);531}532533public int charToVariationGlyph(int wchar, int variationSelector) {534return getMapper().charToVariationGlyph(wchar, variationSelector);535}536537public int getMissingGlyphCode() {538return getMapper().getMissingGlyphCode();539}540541public boolean canDisplay(char c) {542return getMapper().canDisplay(c);543}544545public boolean canDisplay(int cp) {546return getMapper().canDisplay(cp);547}548549public byte getBaselineFor(char c) {550return Font.ROMAN_BASELINE;551}552553public float getItalicAngle(Font font, AffineTransform at,554Object aaHint, Object fmHint) {555/* hardwire psz=12 as that's typical and AA vs non-AA for 'gasp' mode556* isn't important for the caret slope of this rarely used API.557*/558int aa = FontStrikeDesc.getAAHintIntVal(aaHint, this, 12);559int fm = FontStrikeDesc.getFMHintIntVal(fmHint);560FontStrike strike = getStrike(font, at, aa, fm);561StrikeMetrics metrics = strike.getFontMetrics();562if (metrics.ascentY == 0 || metrics.ascentX == 0) {563return 0f;564} else {565/* ascent is "up" from the baseline so its typically566* a negative value, so we need to compensate567*/568return metrics.ascentX/-metrics.ascentY;569}570}571572}573574575