951 lines · 38.7 KB · Java
RawDownload
/* * 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.