/* * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.font; import java.lang.ref.SoftReference;import java.lang.ref.WeakReference;import java.awt.Font;import java.awt.GraphicsEnvironment;import java.awt.Rectangle;import java.awt.geom.AffineTransform;import java.awt.geom.GeneralPath;import java.awt.geom.NoninvertibleTransformException;import java.awt.geom.Point2D;import java.awt.geom.Rectangle2D;import java.util.concurrent.ConcurrentHashMap;import static sun.awt.SunHints.*; public class FileFontStrike extends PhysicalStrike { /* fffe and ffff are values we specially interpret as meaning * invisible glyphs. */ static final int INVISIBLE_GLYPHS = 0x0fffe; private FileFont fileFont; /* REMIND: replace this scheme with one that installs a cache * instance of the appropriate type. It will require changes in * FontStrikeDisposer and NativeStrike etc. */ private static final int UNINITIALISED = 0; private static final int INTARRAY = 1; private static final int LONGARRAY = 2; private static final int SEGINTARRAY = 3; private static final int SEGLONGARRAY = 4; private volatile int glyphCacheFormat = UNINITIALISED; /* segmented arrays are blocks of 32 */ private static final int SEGSHIFT = 5; private static final int SEGSIZE = 1 << SEGSHIFT; private boolean segmentedCache; private int[][] segIntGlyphImages; private long[][] segLongGlyphImages; /* The "metrics" information requested by clients is usually nothing * more than the horizontal advance of the character. * In most cases this advance and other metrics information is stored * in the glyph image cache. * But in some cases we do not automatically retrieve the glyph * image when the advance is requested. In those cases we want to * cache the advances since this has been shown to be important for * performance. * The segmented cache is used in cases when the single array * would be too large. */ private float[] horizontalAdvances; private float[][] segHorizontalAdvances; /* Outline bounds are used when printing and when drawing outlines * to the screen. On balance the relative rarity of these cases * and the fact that getting this requires generating a path at * the scaler level means that its probably OK to store these * in a Java-level hashmap as the trade-off between time and space. * Later can revisit whether to cache these at all, or elsewhere. * Should also profile whether subsequent to getting the bounds, the * outline itself is also requested. The 1.4 implementation doesn't * cache outlines so you could generate the path twice - once to get * the bounds and again to return the outline to the client. * If the two uses are coincident then also look into caching outlines. * One simple optimisation is that we could store the last single * outline retrieved. This assumes that bounds then outline will always * be retrieved for a glyph rather than retrieving bounds for all glyphs * then outlines for all glyphs. */ ConcurrentHashMap<Integer, Rectangle2D.Float> boundsMap; SoftReference<ConcurrentHashMap<Integer, Point2D.Float>> glyphMetricsMapRef; AffineTransform invertDevTx; boolean useNatives; NativeStrike[] nativeStrikes; /* Used only for communication to native layer */ private int intPtSize; /* Perform global initialisation needed for Windows native rasterizer */ private static native boolean initNative(); private static boolean isXPorLater = false; static { if (FontUtilities.isWindows && !FontUtilities.useT2K && !GraphicsEnvironment.isHeadless()) { isXPorLater = initNative(); } } FileFontStrike(FileFont fileFont, FontStrikeDesc desc) { super(fileFont, desc); this.fileFont = fileFont; if (desc.style != fileFont.style) { /* If using algorithmic styling, the base values are * boldness = 1.0, italic = 0.0. The superclass constructor * initialises these. */ if ((desc.style & Font.ITALIC) == Font.ITALIC && (fileFont.style & Font.ITALIC) == 0) { algoStyle = true; italic = 0.7f; } if ((desc.style & Font.BOLD) == Font.BOLD && ((fileFont.style & Font.BOLD) == 0)) { algoStyle = true; boldness = 1.33f; } } double[] matrix = new double[4]; AffineTransform at = desc.glyphTx; at.getMatrix(matrix); if (!desc.devTx.isIdentity() && desc.devTx.getType() != AffineTransform.TYPE_TRANSLATION) { try { invertDevTx = desc.devTx.createInverse(); } catch (NoninvertibleTransformException e) { } } /* Amble fonts are better rendered unhinted although there's the * inevitable fuzziness that accompanies this due to no longer * snapping stems to the pixel grid. The exception is that in B&W * mode they are worse without hinting. The down side to that is that * B&W metrics will differ which normally isn't the case, although * since AA mode is part of the measuring context that should be OK. * We don't expect Amble to be installed in the Windows fonts folder. * If we were to, then we'd also might want to disable using the * native rasteriser path which is used for LCD mode for platform * fonts. since we have no way to disable hinting by GDI. * In the case of Amble, since its 'gasp' table says to disable * hinting, I'd expect GDI to follow that, so likely it should * all be consistent even if GDI used. */ boolean disableHinting = desc.aaHint != INTVAL_TEXT_ANTIALIAS_OFF && fileFont.familyName.startsWith("Amble"); /* If any of the values is NaN then substitute the null scaler context. * This will return null images, zero advance, and empty outlines * as no rendering need take place in this case. * We pass in the null scaler as the singleton null context * requires it. However */ if (Double.isNaN(matrix[0]) || Double.isNaN(matrix[1]) || Double.isNaN(matrix[2]) || Double.isNaN(matrix[3]) || fileFont.getScaler() == null) { pScalerContext = NullFontScaler.getNullScalerContext(); } else { pScalerContext = fileFont.getScaler().createScalerContext(matrix, desc.aaHint, desc.fmHint, boldness, italic, disableHinting); } mapper = fileFont.getMapper(); int numGlyphs = mapper.getNumGlyphs(); /* Always segment for fonts with > 256 glyphs, but also for smaller * fonts with non-typical sizes and transforms. * Segmenting for all non-typical pt sizes helps to minimise memory * usage when very many distinct strikes are created. * The size range of 0->5 and 37->INF for segmenting is arbitrary * but the intention is that typical GUI integer point sizes (6->36) * should not segment unless there's another reason to do so. */ float ptSize = (float)matrix[3]; // interpreted only when meaningful. int iSize = intPtSize = (int)ptSize; boolean isSimpleTx = (at.getType() & complexTX) == 0; segmentedCache = (numGlyphs > SEGSIZE << 3) || ((numGlyphs > SEGSIZE << 1) && (!isSimpleTx || ptSize != iSize || iSize < 6 || iSize > 36)); /* This can only happen if we failed to allocate memory for context. * NB: in such case we may still have some memory in java heap * but subsequent attempt to allocate null scaler context * may fail too (cause it is allocate in the native heap). * It is not clear how to make this more robust but on the * other hand getting NULL here seems to be extremely unlikely. */ if (pScalerContext == 0L) { /* REMIND: when the code is updated to install cache objects * rather than using a switch this will be more efficient. */ this.disposer = new FontStrikeDisposer(fileFont, desc); initGlyphCache(); pScalerContext = NullFontScaler.getNullScalerContext(); SunFontManager.getInstance().deRegisterBadFont(fileFont); return; } /* First, see if native code should be used to create the glyph. * GDI will return the integer metrics, not fractional metrics, which * may be requested for this strike, so we would require here that : * desc.fmHint != INTVAL_FRACTIONALMETRICS_ON * except that the advance returned by GDI is always overwritten by * the JDK rasteriser supplied one (see getGlyphImageFromWindows()). */ if (FontUtilities.isWindows && isXPorLater && !FontUtilities.useT2K && !GraphicsEnvironment.isHeadless() && !fileFont.useJavaRasterizer && (desc.aaHint == INTVAL_TEXT_ANTIALIAS_LCD_HRGB || desc.aaHint == INTVAL_TEXT_ANTIALIAS_LCD_HBGR) && (matrix[1] == 0.0 && matrix[2] == 0.0 && matrix[0] == matrix[3] && matrix[0] >= 3.0 && matrix[0] <= 100.0) && !((TrueTypeFont)fileFont).useEmbeddedBitmapsForSize(intPtSize)) { useNatives = true; } else if (fileFont.checkUseNatives() && desc.aaHint==0 && !algoStyle) { /* Check its a simple scale of a pt size in the range * where native bitmaps typically exist (6-36 pts) */ if (matrix[1] == 0.0 && matrix[2] == 0.0 && matrix[0] >= 6.0 && matrix[0] <= 36.0 && matrix[0] == matrix[3]) { useNatives = true; int numNatives = fileFont.nativeFonts.length; nativeStrikes = new NativeStrike[numNatives]; /* Maybe initialise these strikes lazily?. But we * know we need at least one */ for (int i=0; i<numNatives; i++) { nativeStrikes[i] = new NativeStrike(fileFont.nativeFonts[i], desc, false); } } } if (FontUtilities.isLogging() && FontUtilities.isWindows) { FontUtilities.getLogger().info ("Strike for " + fileFont + " at size = " + intPtSize + " use natives = " + useNatives + " useJavaRasteriser = " + fileFont.useJavaRasterizer + " AAHint = " + desc.aaHint + " Has Embedded bitmaps = " + ((TrueTypeFont)fileFont). useEmbeddedBitmapsForSize(intPtSize)); } this.disposer = new FontStrikeDisposer(fileFont, desc, pScalerContext); /* Always get the image and the advance together for smaller sizes * that are likely to be important to rendering performance. * The pixel size of 48.0 can be thought of as * "maximumSizeForGetImageWithAdvance". * This should be no greater than OutlineTextRender.THRESHOLD. */ double maxSz = 48.0; getImageWithAdvance = Math.abs(at.getScaleX()) <= maxSz && Math.abs(at.getScaleY()) <= maxSz && Math.abs(at.getShearX()) <= maxSz && Math.abs(at.getShearY()) <= maxSz; /* Some applications request advance frequently during layout. * If we are not getting and caching the image with the advance, * there is a potentially significant performance penalty if the * advance is repeatedly requested before requesting the image. * We should at least cache the horizontal advance. * REMIND: could use info in the font, eg hmtx, to retrieve some * advances. But still want to cache it here. */ if (!getImageWithAdvance) { if (!segmentedCache) { horizontalAdvances = new float[numGlyphs]; /* use max float as uninitialised advance */ for (int i=0; i<numGlyphs; i++) { horizontalAdvances[i] = Float.MAX_VALUE; } } else { int numSegments = (numGlyphs + SEGSIZE-1)/SEGSIZE; segHorizontalAdvances = new float[numSegments][]; } } } /* A number of methods are delegated by the strike to the scaler * context which is a shared resource on a physical font. */ public int getNumGlyphs() { return fileFont.getNumGlyphs(); } long getGlyphImageFromNative(int glyphCode) { if (FontUtilities.isWindows) { return getGlyphImageFromWindows(glyphCode); } else { return getGlyphImageFromX11(glyphCode); } } /* There's no global state conflicts, so this method is not * presently synchronized. */ private native long _getGlyphImageFromWindows(String family, int style, int size, int glyphCode, boolean fracMetrics); long getGlyphImageFromWindows(int glyphCode) { String family = fileFont.getFamilyName(null); int style = desc.style & Font.BOLD | desc.style & Font.ITALIC | fileFont.getStyle(); int size = intPtSize; long ptr = _getGlyphImageFromWindows (family, style, size, glyphCode, desc.fmHint == INTVAL_FRACTIONALMETRICS_ON); if (ptr != 0) { /* Get the advance from the JDK rasterizer. This is mostly * necessary for the fractional metrics case, but there are * also some very small number (<0.25%) of marginal cases where * there is some rounding difference between windows and JDK. * After these are resolved, we can restrict this extra * work to the FM case. */ float advance = getGlyphAdvance(glyphCode, false); StrikeCache.unsafe.putFloat(ptr + StrikeCache.xAdvanceOffset, advance); return ptr; } else { return fileFont.getGlyphImage(pScalerContext, glyphCode); } } /* Try the native strikes first, then try the fileFont strike */ long getGlyphImageFromX11(int glyphCode) { long glyphPtr; char charCode = fileFont.glyphToCharMap[glyphCode]; for (int i=0;i<nativeStrikes.length;i++) { CharToGlyphMapper mapper = fileFont.nativeFonts[i].getMapper(); int gc = mapper.charToGlyph(charCode)&0xffff; if (gc != mapper.getMissingGlyphCode()) { glyphPtr = nativeStrikes[i].getGlyphImagePtrNoCache(gc); if (glyphPtr != 0L) { return glyphPtr; } } } return fileFont.getGlyphImage(pScalerContext, glyphCode); } long getGlyphImagePtr(int glyphCode) { if (glyphCode >= INVISIBLE_GLYPHS) { return StrikeCache.invisibleGlyphPtr; } long glyphPtr = 0L; if ((glyphPtr = getCachedGlyphPtr(glyphCode)) != 0L) { return glyphPtr; } else { if (useNatives) { glyphPtr = getGlyphImageFromNative(glyphCode); if (glyphPtr == 0L && FontUtilities.isLogging()) { FontUtilities.getLogger().info ("Strike for " + fileFont + " at size = " + intPtSize + " couldn't get native glyph for code = " + glyphCode); } } if (glyphPtr == 0L) { glyphPtr = fileFont.getGlyphImage(pScalerContext, glyphCode); } return setCachedGlyphPtr(glyphCode, glyphPtr); } } void getGlyphImagePtrs(int[] glyphCodes, long[] images, int len) { for (int i=0; i<len; i++) { int glyphCode = glyphCodes[i]; if (glyphCode >= INVISIBLE_GLYPHS) { images[i] = StrikeCache.invisibleGlyphPtr; continue; } else if ((images[i] = getCachedGlyphPtr(glyphCode)) != 0L) { continue; } else { long glyphPtr = 0L; if (useNatives) { glyphPtr = getGlyphImageFromNative(glyphCode); } if (glyphPtr == 0L) { glyphPtr = fileFont.getGlyphImage(pScalerContext, glyphCode); } images[i] = setCachedGlyphPtr(glyphCode, glyphPtr); } } } /* The following method is called from CompositeStrike as a special case. */ private static final int SLOTZEROMAX = 0xffffff; int getSlot0GlyphImagePtrs(int[] glyphCodes, long[] images, int len) { int convertedCnt = 0; for (int i=0; i<len; i++) { int glyphCode = glyphCodes[i]; if (glyphCode >= SLOTZEROMAX) { return convertedCnt; } else { convertedCnt++; } if (glyphCode >= INVISIBLE_GLYPHS) { images[i] = StrikeCache.invisibleGlyphPtr; continue; } else if ((images[i] = getCachedGlyphPtr(glyphCode)) != 0L) { continue; } else { long glyphPtr = 0L; if (useNatives) { glyphPtr = getGlyphImageFromNative(glyphCode); } if (glyphPtr == 0L) { glyphPtr = fileFont.getGlyphImage(pScalerContext, glyphCode); } images[i] = setCachedGlyphPtr(glyphCode, glyphPtr); } } return convertedCnt; } /* Only look in the cache */ long getCachedGlyphPtr(int glyphCode) { switch (glyphCacheFormat) { case INTARRAY: return intGlyphImages[glyphCode] & INTMASK; case SEGINTARRAY: int segIndex = glyphCode >> SEGSHIFT; if (segIntGlyphImages[segIndex] != null) { int subIndex = glyphCode % SEGSIZE; return segIntGlyphImages[segIndex][subIndex] & INTMASK; } else { return 0L; } case LONGARRAY: return longGlyphImages[glyphCode]; case SEGLONGARRAY: segIndex = glyphCode >> SEGSHIFT; if (segLongGlyphImages[segIndex] != null) { int subIndex = glyphCode % SEGSIZE; return segLongGlyphImages[segIndex][subIndex]; } else { return 0L; } } /* If reach here cache is UNINITIALISED. */ return 0L; } private synchronized long setCachedGlyphPtr(int glyphCode, long glyphPtr) { switch (glyphCacheFormat) { case INTARRAY: if (intGlyphImages[glyphCode] == 0) { intGlyphImages[glyphCode] = (int)glyphPtr; return glyphPtr; } else { StrikeCache.freeIntPointer((int)glyphPtr); return intGlyphImages[glyphCode] & INTMASK; } case SEGINTARRAY: int segIndex = glyphCode >> SEGSHIFT; int subIndex = glyphCode % SEGSIZE; if (segIntGlyphImages[segIndex] == null) { segIntGlyphImages[segIndex] = new int[SEGSIZE]; } if (segIntGlyphImages[segIndex][subIndex] == 0) {Lines 1–500 of 951. Load more
/* * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.font; import java.lang.ref.SoftReference; import java.lang.ref.WeakReference; import java.awt.Font; import java.awt.GraphicsEnvironment; import java.awt.Rectangle; import java.awt.geom.AffineTransform; import java.awt.geom.GeneralPath; import java.awt.geom.NoninvertibleTransformException; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.concurrent.ConcurrentHashMap; import static sun.awt.SunHints.*; public class FileFontStrike extends PhysicalStrike { /* fffe and ffff are values we specially interpret as meaning * invisible glyphs. */ static final int INVISIBLE_GLYPHS = 0x0fffe; private FileFont fileFont; /* REMIND: replace this scheme with one that installs a cache * instance of the appropriate type. It will require changes in * FontStrikeDisposer and NativeStrike etc. */ private static final int UNINITIALISED = 0; private static final int INTARRAY = 1; private static final int LONGARRAY = 2; private static final int SEGINTARRAY = 3; private static final int SEGLONGARRAY = 4; private volatile int glyphCacheFormat = UNINITIALISED; /* segmented arrays are blocks of 32 */ private static final int SEGSHIFT = 5; private static final int SEGSIZE = 1 << SEGSHIFT; private boolean segmentedCache; private int[][] segIntGlyphImages; private long[][] segLongGlyphImages; /* The "metrics" information requested by clients is usually nothing * more than the horizontal advance of the character. * In most cases this advance and other metrics information is stored * in the glyph image cache. * But in some cases we do not automatically retrieve the glyph * image when the advance is requested. In those cases we want to * cache the advances since this has been shown to be important for * performance. * The segmented cache is used in cases when the single array * would be too large. */ private float[] horizontalAdvances; private float[][] segHorizontalAdvances; /* Outline bounds are used when printing and when drawing outlines * to the screen. On balance the relative rarity of these cases * and the fact that getting this requires generating a path at * the scaler level means that its probably OK to store these * in a Java-level hashmap as the trade-off between time and space. * Later can revisit whether to cache these at all, or elsewhere. * Should also profile whether subsequent to getting the bounds, the * outline itself is also requested. The 1.4 implementation doesn't * cache outlines so you could generate the path twice - once to get * the bounds and again to return the outline to the client. * If the two uses are coincident then also look into caching outlines. * One simple optimisation is that we could store the last single * outline retrieved. This assumes that bounds then outline will always * be retrieved for a glyph rather than retrieving bounds for all glyphs * then outlines for all glyphs. */ ConcurrentHashMap<Integer, Rectangle2D.Float> boundsMap; SoftReference<ConcurrentHashMap<Integer, Point2D.Float>> glyphMetricsMapRef; AffineTransform invertDevTx; boolean useNatives; NativeStrike[] nativeStrikes; /* Used only for communication to native layer */ private int intPtSize; /* Perform global initialisation needed for Windows native rasterizer */ private static native boolean initNative(); private static boolean isXPorLater = false; static { if (FontUtilities.isWindows && !FontUtilities.useT2K && !GraphicsEnvironment.isHeadless()) { isXPorLater = initNative(); } } FileFontStrike(FileFont fileFont, FontStrikeDesc desc) { super(fileFont, desc); this.fileFont = fileFont; if (desc.style != fileFont.style) { /* If using algorithmic styling, the base values are * boldness = 1.0, italic = 0.0. The superclass constructor * initialises these. */ if ((desc.style & Font.ITALIC) == Font.ITALIC && (fileFont.style & Font.ITALIC) == 0) { algoStyle = true; italic = 0.7f; } if ((desc.style & Font.BOLD) == Font.BOLD && ((fileFont.style & Font.BOLD) == 0)) { algoStyle = true; boldness = 1.33f; } } double[] matrix = new double[4]; AffineTransform at = desc.glyphTx; at.getMatrix(matrix); if (!desc.devTx.isIdentity() && desc.devTx.getType() != AffineTransform.TYPE_TRANSLATION) { try { invertDevTx = desc.devTx.createInverse(); } catch (NoninvertibleTransformException e) { } } /* Amble fonts are better rendered unhinted although there's the * inevitable fuzziness that accompanies this due to no longer * snapping stems to the pixel grid. The exception is that in B&W * mode they are worse without hinting. The down side to that is that * B&W metrics will differ which normally isn't the case, although * since AA mode is part of the measuring context that should be OK. * We don't expect Amble to be installed in the Windows fonts folder. * If we were to, then we'd also might want to disable using the * native rasteriser path which is used for LCD mode for platform * fonts. since we have no way to disable hinting by GDI. * In the case of Amble, since its 'gasp' table says to disable * hinting, I'd expect GDI to follow that, so likely it should * all be consistent even if GDI used. */ boolean disableHinting = desc.aaHint != INTVAL_TEXT_ANTIALIAS_OFF && fileFont.familyName.startsWith("Amble"); /* If any of the values is NaN then substitute the null scaler context. * This will return null images, zero advance, and empty outlines * as no rendering need take place in this case. * We pass in the null scaler as the singleton null context * requires it. However */ if (Double.isNaN(matrix[0]) || Double.isNaN(matrix[1]) || Double.isNaN(matrix[2]) || Double.isNaN(matrix[3]) || fileFont.getScaler() == null) { pScalerContext = NullFontScaler.getNullScalerContext(); } else { pScalerContext = fileFont.getScaler().createScalerContext(matrix, desc.aaHint, desc.fmHint, boldness, italic, disableHinting); } mapper = fileFont.getMapper(); int numGlyphs = mapper.getNumGlyphs(); /* Always segment for fonts with > 256 glyphs, but also for smaller * fonts with non-typical sizes and transforms. * Segmenting for all non-typical pt sizes helps to minimise memory * usage when very many distinct strikes are created. * The size range of 0->5 and 37->INF for segmenting is arbitrary * but the intention is that typical GUI integer point sizes (6->36) * should not segment unless there's another reason to do so. */ float ptSize = (float)matrix[3]; // interpreted only when meaningful. int iSize = intPtSize = (int)ptSize; boolean isSimpleTx = (at.getType() & complexTX) == 0; segmentedCache = (numGlyphs > SEGSIZE << 3) || ((numGlyphs > SEGSIZE << 1) && (!isSimpleTx || ptSize != iSize || iSize < 6 || iSize > 36)); /* This can only happen if we failed to allocate memory for context. * NB: in such case we may still have some memory in java heap * but subsequent attempt to allocate null scaler context * may fail too (cause it is allocate in the native heap). * It is not clear how to make this more robust but on the * other hand getting NULL here seems to be extremely unlikely. */ if (pScalerContext == 0L) { /* REMIND: when the code is updated to install cache objects * rather than using a switch this will be more efficient. */ this.disposer = new FontStrikeDisposer(fileFont, desc); initGlyphCache(); pScalerContext = NullFontScaler.getNullScalerContext(); SunFontManager.getInstance().deRegisterBadFont(fileFont); return; } /* First, see if native code should be used to create the glyph. * GDI will return the integer metrics, not fractional metrics, which * may be requested for this strike, so we would require here that : * desc.fmHint != INTVAL_FRACTIONALMETRICS_ON * except that the advance returned by GDI is always overwritten by * the JDK rasteriser supplied one (see getGlyphImageFromWindows()). */ if (FontUtilities.isWindows && isXPorLater && !FontUtilities.useT2K && !GraphicsEnvironment.isHeadless() && !fileFont.useJavaRasterizer && (desc.aaHint == INTVAL_TEXT_ANTIALIAS_LCD_HRGB || desc.aaHint == INTVAL_TEXT_ANTIALIAS_LCD_HBGR) && (matrix[1] == 0.0 && matrix[2] == 0.0 && matrix[0] == matrix[3] && matrix[0] >= 3.0 && matrix[0] <= 100.0) && !((TrueTypeFont)fileFont).useEmbeddedBitmapsForSize(intPtSize)) { useNatives = true; } else if (fileFont.checkUseNatives() && desc.aaHint==0 && !algoStyle) { /* Check its a simple scale of a pt size in the range * where native bitmaps typically exist (6-36 pts) */ if (matrix[1] == 0.0 && matrix[2] == 0.0 && matrix[0] >= 6.0 && matrix[0] <= 36.0 && matrix[0] == matrix[3]) { useNatives = true; int numNatives = fileFont.nativeFonts.length; nativeStrikes = new NativeStrike[numNatives]; /* Maybe initialise these strikes lazily?. But we * know we need at least one */ for (int i=0; i<numNatives; i++) { nativeStrikes[i] = new NativeStrike(fileFont.nativeFonts[i], desc, false); } } } if (FontUtilities.isLogging() && FontUtilities.isWindows) { FontUtilities.getLogger().info ("Strike for " + fileFont + " at size = " + intPtSize + " use natives = " + useNatives + " useJavaRasteriser = " + fileFont.useJavaRasterizer + " AAHint = " + desc.aaHint + " Has Embedded bitmaps = " + ((TrueTypeFont)fileFont). useEmbeddedBitmapsForSize(intPtSize)); } this.disposer = new FontStrikeDisposer(fileFont, desc, pScalerContext); /* Always get the image and the advance together for smaller sizes * that are likely to be important to rendering performance. * The pixel size of 48.0 can be thought of as * "maximumSizeForGetImageWithAdvance". * This should be no greater than OutlineTextRender.THRESHOLD. */ double maxSz = 48.0; getImageWithAdvance = Math.abs(at.getScaleX()) <= maxSz && Math.abs(at.getScaleY()) <= maxSz && Math.abs(at.getShearX()) <= maxSz && Math.abs(at.getShearY()) <= maxSz; /* Some applications request advance frequently during layout. * If we are not getting and caching the image with the advance, * there is a potentially significant performance penalty if the * advance is repeatedly requested before requesting the image. * We should at least cache the horizontal advance. * REMIND: could use info in the font, eg hmtx, to retrieve some * advances. But still want to cache it here. */ if (!getImageWithAdvance) { if (!segmentedCache) { horizontalAdvances = new float[numGlyphs]; /* use max float as uninitialised advance */ for (int i=0; i<numGlyphs; i++) { horizontalAdvances[i] = Float.MAX_VALUE; } } else { int numSegments = (numGlyphs + SEGSIZE-1)/SEGSIZE; segHorizontalAdvances = new float[numSegments][]; } } } /* A number of methods are delegated by the strike to the scaler * context which is a shared resource on a physical font. */ public int getNumGlyphs() { return fileFont.getNumGlyphs(); } long getGlyphImageFromNative(int glyphCode) { if (FontUtilities.isWindows) { return getGlyphImageFromWindows(glyphCode); } else { return getGlyphImageFromX11(glyphCode); } } /* There's no global state conflicts, so this method is not * presently synchronized. */ private native long _getGlyphImageFromWindows(String family, int style, int size, int glyphCode, boolean fracMetrics); long getGlyphImageFromWindows(int glyphCode) { String family = fileFont.getFamilyName(null); int style = desc.style & Font.BOLD | desc.style & Font.ITALIC | fileFont.getStyle(); int size = intPtSize; long ptr = _getGlyphImageFromWindows (family, style, size, glyphCode, desc.fmHint == INTVAL_FRACTIONALMETRICS_ON); if (ptr != 0) { /* Get the advance from the JDK rasterizer. This is mostly * necessary for the fractional metrics case, but there are * also some very small number (<0.25%) of marginal cases where * there is some rounding difference between windows and JDK. * After these are resolved, we can restrict this extra * work to the FM case. */ float advance = getGlyphAdvance(glyphCode, false); StrikeCache.unsafe.putFloat(ptr + StrikeCache.xAdvanceOffset, advance); return ptr; } else { return fileFont.getGlyphImage(pScalerContext, glyphCode); } } /* Try the native strikes first, then try the fileFont strike */ long getGlyphImageFromX11(int glyphCode) { long glyphPtr; char charCode = fileFont.glyphToCharMap[glyphCode]; for (int i=0;i<nativeStrikes.length;i++) { CharToGlyphMapper mapper = fileFont.nativeFonts[i].getMapper(); int gc = mapper.charToGlyph(charCode)&0xffff; if (gc != mapper.getMissingGlyphCode()) { glyphPtr = nativeStrikes[i].getGlyphImagePtrNoCache(gc); if (glyphPtr != 0L) { return glyphPtr; } } } return fileFont.getGlyphImage(pScalerContext, glyphCode); } long getGlyphImagePtr(int glyphCode) { if (glyphCode >= INVISIBLE_GLYPHS) { return StrikeCache.invisibleGlyphPtr; } long glyphPtr = 0L; if ((glyphPtr = getCachedGlyphPtr(glyphCode)) != 0L) { return glyphPtr; } else { if (useNatives) { glyphPtr = getGlyphImageFromNative(glyphCode); if (glyphPtr == 0L && FontUtilities.isLogging()) { FontUtilities.getLogger().info ("Strike for " + fileFont + " at size = " + intPtSize + " couldn't get native glyph for code = " + glyphCode); } } if (glyphPtr == 0L) { glyphPtr = fileFont.getGlyphImage(pScalerContext, glyphCode); } return setCachedGlyphPtr(glyphCode, glyphPtr); } } void getGlyphImagePtrs(int[] glyphCodes, long[] images, int len) { for (int i=0; i<len; i++) { int glyphCode = glyphCodes[i]; if (glyphCode >= INVISIBLE_GLYPHS) { images[i] = StrikeCache.invisibleGlyphPtr; continue; } else if ((images[i] = getCachedGlyphPtr(glyphCode)) != 0L) { continue; } else { long glyphPtr = 0L; if (useNatives) { glyphPtr = getGlyphImageFromNative(glyphCode); } if (glyphPtr == 0L) { glyphPtr = fileFont.getGlyphImage(pScalerContext, glyphCode); } images[i] = setCachedGlyphPtr(glyphCode, glyphPtr); } } } /* The following method is called from CompositeStrike as a special case. */ private static final int SLOTZEROMAX = 0xffffff; int getSlot0GlyphImagePtrs(int[] glyphCodes, long[] images, int len) { int convertedCnt = 0; for (int i=0; i<len; i++) { int glyphCode = glyphCodes[i]; if (glyphCode >= SLOTZEROMAX) { return convertedCnt; } else { convertedCnt++; } if (glyphCode >= INVISIBLE_GLYPHS) { images[i] = StrikeCache.invisibleGlyphPtr; continue; } else if ((images[i] = getCachedGlyphPtr(glyphCode)) != 0L) { continue; } else { long glyphPtr = 0L; if (useNatives) { glyphPtr = getGlyphImageFromNative(glyphCode); } if (glyphPtr == 0L) { glyphPtr = fileFont.getGlyphImage(pScalerContext, glyphCode); } images[i] = setCachedGlyphPtr(glyphCode, glyphPtr); } } return convertedCnt; } /* Only look in the cache */ long getCachedGlyphPtr(int glyphCode) { switch (glyphCacheFormat) { case INTARRAY: return intGlyphImages[glyphCode] & INTMASK; case SEGINTARRAY: int segIndex = glyphCode >> SEGSHIFT; if (segIntGlyphImages[segIndex] != null) { int subIndex = glyphCode % SEGSIZE; return segIntGlyphImages[segIndex][subIndex] & INTMASK; } else { return 0L; } case LONGARRAY: return longGlyphImages[glyphCode]; case SEGLONGARRAY: segIndex = glyphCode >> SEGSHIFT; if (segLongGlyphImages[segIndex] != null) { int subIndex = glyphCode % SEGSIZE; return segLongGlyphImages[segIndex][subIndex]; } else { return 0L; } } /* If reach here cache is UNINITIALISED. */ return 0L; } private synchronized long setCachedGlyphPtr(int glyphCode, long glyphPtr) { switch (glyphCacheFormat) { case INTARRAY: if (intGlyphImages[glyphCode] == 0) { intGlyphImages[glyphCode] = (int)glyphPtr; return glyphPtr; } else { StrikeCache.freeIntPointer((int)glyphPtr); return intGlyphImages[glyphCode] & INTMASK; } case SEGINTARRAY: int segIndex = glyphCode >> SEGSHIFT; int subIndex = glyphCode % SEGSIZE; if (segIntGlyphImages[segIndex] == null) { segIntGlyphImages[segIndex] = new int[SEGSIZE]; } if (segIntGlyphImages[segIndex][subIndex] == 0) {