506 lines · 18.7 KB · Java
RawDownload
/* * Copyright (c) 2000, 2006, 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.awt.Font;import java.awt.font.GlyphVector;import java.awt.font.FontRenderContext;import sun.java2d.loops.FontInfo; /* * This class represents a list of actual renderable glyphs. * It can be constructed from a number of text sources, representing * the various ways in which a programmer can ask a Graphics2D object * to render some text.  Once constructed, it provides a way of iterating * through the device metrics and graybits of the individual glyphs that * need to be rendered to the screen. * * Note that this class holds pointers to native data which must be * disposed.  It is not marked as finalizable since it is intended * to be very lightweight and finalization is a comparitively expensive * procedure.  The caller must specifically use try{} finally{} to * manually ensure that the object is disposed after use, otherwise * native data structures might be leaked. * * Here is a code sample for using this class: * * public void drawString(String str, FontInfo info, float x, float y) { *     GlyphList gl = GlyphList.getInstance(); *     try { *         gl.setFromString(info, str, x, y); *         int strbounds[] = gl.getBounds(); *         int numglyphs = gl.getNumGlyphs(); *         for (int i = 0; i < numglyphs; i++) { *             gl.setGlyphIndex(i); *             int metrics[] = gl.getMetrics(); *             byte bits[] = gl.getGrayBits(); *             int glyphx = metrics[0]; *             int glyphy = metrics[1]; *             int glyphw = metrics[2]; *             int glyphh = metrics[3]; *             int off = 0; *             for (int j = 0; j < glyphh; j++) { *                 for (int i = 0; i < glyphw; i++) { *                     int dx = glyphx + i; *                     int dy = glyphy + j; *                     int alpha = bits[off++]; *                     drawPixel(alpha, dx, dy); *                 } *             } *         } *     } finally { *         gl.dispose(); *     } * } */public final class GlyphList {    private static final int MINGRAYLENGTH = 1024;    private static final int MAXGRAYLENGTH = 8192;    private static final int DEFAULT_LENGTH = 32;     int glyphindex;    int metrics[];    byte graybits[];     /* A reference to the strike is needed for the case when the GlyphList     * may be added to a queue for batch processing, (e.g. OpenGL) and we need     * to be completely certain that the strike is still valid when the glyphs     * images are later referenced.  This does mean that if such code discards     * GlyphList and places only the data it contains on the queue, that the     * strike needs to be part of that data held by a strong reference.     * In the cases of drawString() and drawChars(), this is a single strike,     * although it may be a composite strike.  In the case of     * drawGlyphVector() it may be a single strike, or a list of strikes.     */    Object strikelist; // hold multiple strikes during rendering of complex gv     /* In normal usage, the same GlyphList will get recycled, so     * it makes sense to allocate arrays that will get reused along with     * it, rather than generating garbage. Garbage will be generated only     * in MP envts where multiple threads are executing. Throughput should     * still be higher in those cases.     */    int len = 0;    int maxLen = 0;    int maxPosLen = 0;    int glyphData[];    char chData[];    long images[];    float positions[];    float x, y;    float gposx, gposy;    boolean usePositions;     /* lcdRGBOrder is used only by LCD text rendering. Its here because     * the Graphics may have a different hint value than the one used     * by a GlyphVector, so it has to be stored here - and is obtained     * from the right FontInfo. Another approach would have been to have     * install a separate pipe for that case but that's a lot of extra     * code when a simple boolean will suffice. The overhead to non-LCD     * text is a redundant boolean assign per call.     */    boolean lcdRGBOrder;     /*     * lcdSubPixPos is used only by LCD text rendering. Its here because     * the Graphics may have a different hint value than the one used     * by a GlyphVector, so it has to be stored here - and is obtained     * from the right FontInfo. Its also needed by the code which     * calculates glyph positions which already needs to access this     * GlyphList and would otherwise need the FontInfo.     * This is true only if LCD text and fractional metrics hints     * are selected on the graphics.     * When this is true and the glyph positions as determined by the     * advances are non-integral, it requests adjustment of the positions.     * Setting this for surfaces which do not support it through accelerated     * loops may cause a slow-down as software loops are invoked instead.     */    boolean lcdSubPixPos;     /* This scheme creates a singleton GlyphList which is checked out     * for use. Callers who find its checked out create one that after use     * is discarded. This means that in a MT-rendering environment,     * there's no need to synchronise except for that one instance.     * Fewer threads will then need to synchronise, perhaps helping     * throughput on a MP system. If for some reason the reusable     * GlyphList is checked out for a long time (or never returned?) then     * we would end up always creating new ones. That situation should not     * occur and if if did, it would just lead to some extra garbage being     * created.     */    private static GlyphList reusableGL = new GlyphList();    private static boolean inUse;      void ensureCapacity(int len) {      /* Note len must not be -ve! only setFromChars should be capable       * of passing down a -ve len, and this guards against it.       */        if (len < 0) {          len = 0;        }        if (usePositions && len > maxPosLen) {            positions = new float[len * 2 + 2];            maxPosLen = len;        }         if (maxLen == 0 || len > maxLen) {            glyphData = new int[len];            chData = new char[len];            images = new long[len];            maxLen = len;        }    }     private GlyphList() {//         ensureCapacity(DEFAULT_LENGTH);    } //     private GlyphList(int arraylen) {//          ensureCapacity(arraylen);//     }     public static GlyphList getInstance() {        /* The following heuristic is that if the reusable instance is         * in use, it probably still will be in a micro-second, so avoid         * synchronising on the class and just allocate a new instance.         * The cost is one extra boolean test for the normal case, and some         * small number of cases where we allocate an extra object when         * in fact the reusable one would be freed very soon.         */        if (inUse) {            return new GlyphList();        } else {            synchronized(GlyphList.class) {                if (inUse) {                    return new GlyphList();                } else {                    inUse = true;                    return reusableGL;                }            }        }    }     /* In some cases the caller may be able to estimate the size of     * array needed, and it will usually be long enough. This avoids     * the unnecessary reallocation that occurs if our default     * values are too small. This is useful because this object     * will be discarded so the re-allocation overhead is high.     *///     public static GlyphList getInstance(int sz) {//      if (inUse) {//          return new GlyphList(sz);//      } else {//          synchronized(GlyphList.class) {//              if (inUse) {//                  return new GlyphList();//              } else {//                  inUse = true;//                  return reusableGL;//              }//          }//      }//     }     /* GlyphList is in an invalid state until setFrom* method is called.     * After obtaining a new GlyphList it is the caller's responsibility     * that one of these methods is executed before handing off the     * GlyphList     */     public boolean setFromString(FontInfo info, String str, float x, float y) {        this.x = x;        this.y = y;        this.strikelist = info.fontStrike;        this.lcdRGBOrder = info.lcdRGBOrder;        this.lcdSubPixPos = info.lcdSubPixPos;        len = str.length();        ensureCapacity(len);        str.getChars(0, len, chData, 0);        return mapChars(info, len);    }     public boolean setFromChars(FontInfo info, char[] chars, int off, int alen,                                float x, float y) {        this.x = x;        this.y = y;        this.strikelist = info.fontStrike;        this.lcdRGBOrder = info.lcdRGBOrder;        this.lcdSubPixPos = info.lcdSubPixPos;        len = alen;        if (alen < 0) {            len = 0;        } else {            len = alen;        }        ensureCapacity(len);        System.arraycopy(chars, off, chData, 0, len);        return mapChars(info, len);    }     private final boolean mapChars(FontInfo info, int len) {        /* REMIND.Is it worthwhile for the iteration to convert         * chars to glyph ids to directly map to images?         */        if (info.font2D.getMapper().charsToGlyphsNS(len, chData, glyphData)) {            return false;        }        info.fontStrike.getGlyphImagePtrs(glyphData, images, len);        glyphindex = -1;        return true;    }      public void setFromGlyphVector(FontInfo info, GlyphVector gv,                                   float x, float y) {        this.x = x;        this.y = y;        this.lcdRGBOrder = info.lcdRGBOrder;        this.lcdSubPixPos = info.lcdSubPixPos;        /* A GV may be rendered in different Graphics. It is possible it is         * used for one case where LCD text is available, and another where         * it is not. Pass in the "info". to ensure get a suitable one.         */        StandardGlyphVector sgv = StandardGlyphVector.getStandardGV(gv, info);        // call before ensureCapacity :-        usePositions = sgv.needsPositions(info.devTx);        len = sgv.getNumGlyphs();        ensureCapacity(len);        strikelist = sgv.setupGlyphImages(images,                                          usePositions ? positions : null,                                          info.devTx);        glyphindex = -1;    }     public int[] getBounds() {        /* We co-opt the 5 element array that holds per glyph metrics in order         * to return the bounds. So a caller must copy the data out of the         * array before calling any other methods on this GlyphList         */        if (glyphindex >= 0) {            throw new InternalError("calling getBounds after setGlyphIndex");        }        if (metrics == null) {            metrics = new int[5];        }        /* gposx and gposy are used to accumulate the advance.         * Add 0.5f for consistent rounding to pixel position. */        gposx = x + 0.5f;        gposy = y + 0.5f;        fillBounds(metrics);        return metrics;    }     /* This method now assumes "state", so must be called 0->len     * The metrics it returns are accumulated on the fly     * So it could be renamed "nextGlyph()".     * Note that a laid out GlyphVector which has assigned glyph positions     * doesn't have this stricture..     */    public void setGlyphIndex(int i) {        glyphindex = i;        float gx =            StrikeCache.unsafe.getFloat(images[i]+StrikeCache.topLeftXOffset);        float gy =            StrikeCache.unsafe.getFloat(images[i]+StrikeCache.topLeftYOffset);         if (usePositions) {            metrics[0] = (int)Math.floor(positions[(i<<1)]   + gposx + gx);            metrics[1] = (int)Math.floor(positions[(i<<1)+1] + gposy + gy);        } else {            metrics[0] = (int)Math.floor(gposx + gx);            metrics[1] = (int)Math.floor(gposy + gy);            /* gposx and gposy are used to accumulate the advance */            gposx += StrikeCache.unsafe.getFloat                (images[i]+StrikeCache.xAdvanceOffset);            gposy += StrikeCache.unsafe.getFloat                (images[i]+StrikeCache.yAdvanceOffset);        }        metrics[2] =            StrikeCache.unsafe.getChar(images[i]+StrikeCache.widthOffset);        metrics[3] =            StrikeCache.unsafe.getChar(images[i]+StrikeCache.heightOffset);        metrics[4] =            StrikeCache.unsafe.getChar(images[i]+StrikeCache.rowBytesOffset);    }     public int[] getMetrics() {        return metrics;    }     public byte[] getGrayBits() {        int len = metrics[4] * metrics[3];        if (graybits == null) {            graybits = new byte[Math.max(len, MINGRAYLENGTH)];        } else {            if (len > graybits.length) {                graybits = new byte[len];            }        }        long pixelDataAddress;        if (StrikeCache.nativeAddressSize == 4) {            pixelDataAddress = 0xffffffff &                StrikeCache.unsafe.getInt(images[glyphindex] +                                          StrikeCache.pixelDataOffset);        } else {            pixelDataAddress =            StrikeCache.unsafe.getLong(images[glyphindex] +                                       StrikeCache.pixelDataOffset);        }        if (pixelDataAddress == 0L) {            return graybits;        }        /* unsafe is supposed to be fast, but I doubt if this loop can beat         * a native call which does a getPrimitiveArrayCritical and a         * memcpy for the typical amount of image data (30-150 bytes)         * Consider a native method if there is a performance problem (which         * I haven't seen so far).         */        for (int i=0; i<len; i++) {            graybits[i] = StrikeCache.unsafe.getByte(pixelDataAddress+i);        }        return graybits;    }     public long[] getImages() {        return images;    }     public boolean usePositions() {        return usePositions;    }     public float[] getPositions() {        return positions;    }     public float getX() {        return x;    }     public float getY() {        return y;    }     public Object getStrike() {        return strikelist;    }     public boolean isSubPixPos() {        return lcdSubPixPos;    }     public boolean isRGBOrder() {        return lcdRGBOrder;    }     /* There's a reference equality test overhead here, but it allows us     * to avoid synchronizing for GL's that will just be GC'd. This     * helps MP throughput.     */    public void dispose() {        if (this == reusableGL) {            if (graybits != null && graybits.length > MAXGRAYLENGTH) {                graybits = null;            }            usePositions = false;            strikelist = null; // remove reference to the strike list            inUse = false;        }    }     /* The value here is for use by the rendering engine as it reflects     * the number of glyphs in the array to be blitted. Surrogates pairs     * may have two slots (the second of these being a dummy entry of the     * invisible glyph), whereas an application client would expect only     * one glyph. In other words don't propagate this value up to client code.     *     * {dlf} an application client should have _no_ expectations about the     * number of glyphs per char.  This ultimately depends on the font     * technology and layout process used, which in general clients will     * know nothing about.     */    public int getNumGlyphs() {        return len;    }     /* We re-do all this work as we iterate through the glyphs     * but it seems unavoidable without re-working the Java TextRenderers.     */    private void fillBounds(int[] bounds) {        /* Faster to access local variables in the for loop? */        int xOffset = StrikeCache.topLeftXOffset;        int yOffset = StrikeCache.topLeftYOffset;        int wOffset = StrikeCache.widthOffset;        int hOffset = StrikeCache.heightOffset;        int xAdvOffset = StrikeCache.xAdvanceOffset;        int yAdvOffset = StrikeCache.yAdvanceOffset;         if (len == 0) {            bounds[0] = bounds[1] = bounds[2] = bounds[3] = 0;            return;        }        float bx0, by0, bx1, by1;        bx0 = by0 = Float.POSITIVE_INFINITY;        bx1 = by1 = Float.NEGATIVE_INFINITY;         int posIndex = 0;        float glx = x + 0.5f;        float gly = y + 0.5f;        char gw, gh;        float gx, gy, gx0, gy0, gx1, gy1;        for (int i=0; i<len; i++) {            gx = StrikeCache.unsafe.getFloat(images[i]+xOffset);            gy = StrikeCache.unsafe.getFloat(images[i]+yOffset);            gw = StrikeCache.unsafe.getChar(images[i]+wOffset);            gh = StrikeCache.unsafe.getChar(images[i]+hOffset);             if (usePositions) {                gx0 = positions[posIndex++] + gx + glx;                gy0 = positions[posIndex++] + gy + gly;            } else {                gx0 = glx + gx;                gy0 = gly + gy;                glx += StrikeCache.unsafe.getFloat(images[i]+xAdvOffset);                gly += StrikeCache.unsafe.getFloat(images[i]+yAdvOffset);            }            gx1 = gx0 + gw;            gy1 = gy0 + gh;            if (bx0 > gx0) bx0 = gx0;            if (by0 > gy0) by0 = gy0;            if (bx1 < gx1) bx1 = gx1;            if (by1 < gy1) by1 = gy1;        }        /* floor is safe and correct because all glyph widths, heights         * and offsets are integers         */

Lines 1–500 of 506.