1935 lines · 69.8 KB · Java
RawDownload
    // called by most functions    private final void setFRCTX() {        if (!equalNonTranslateTX(frctx, dtx)) {            resetDTX(getNonTranslateTX(frctx));        }    }     /**     * Change the dtx for the strike refs we use.  Keeps a reference to the at.  At     * must not contain translation.     * Called by setRenderTransform, setDTX, initFontData.     */    private final void resetDTX(AffineTransform at) {        fsref = null;        dtx = at;        invdtx = null;        if (!dtx.isIdentity()) {            try {                invdtx = dtx.createInverse();            }            catch (NoninvertibleTransformException e) {                // we needn't care for rendering            }        }        if (gti != null) {            gti.strikesRef = null;        }    }     /**     * Utility used by getStandardGV.     * Constructs a StandardGlyphVector from a generic glyph vector.     * Do not call this from new contexts without considering the comment     * about "userGlyphs".     */    private StandardGlyphVector(GlyphVector gv, FontRenderContext frc) {        this.font = gv.getFont();        this.frc = frc;        initFontData();         int nGlyphs = gv.getNumGlyphs();        this.userGlyphs = gv.getGlyphCodes(0, nGlyphs, null);        if (gv instanceof StandardGlyphVector) {            /* userGlyphs will be OK because this is a private constructor             * and the returned instance is used only for rendering.             * It's not constructable by user code, nor returned to the             * application. So we know "userGlyphs" are valid as having             * been either already validated or are the result of layout.             */            this.glyphs = userGlyphs;        } else {            this.glyphs = getValidatedGlyphs(this.userGlyphs);        }        this.flags = gv.getLayoutFlags() & FLAG_MASK;         if ((flags & FLAG_HAS_POSITION_ADJUSTMENTS) != 0) {            this.positions = gv.getGlyphPositions(0, nGlyphs + 1, null);        }         if ((flags & FLAG_COMPLEX_GLYPHS) != 0) {            this.charIndices = gv.getGlyphCharIndices(0, nGlyphs, null);        }         if ((flags & FLAG_HAS_TRANSFORMS) != 0) {            AffineTransform[] txs = new AffineTransform[nGlyphs]; // worst case            for (int i = 0; i < nGlyphs; ++i) {                txs[i] = gv.getGlyphTransform(i); // gv doesn't have getGlyphsTransforms            }             setGlyphTransforms(txs);        }    }     /* Before asking the Font we see if the glyph code is     * FFFE or FFFF which are special values that we should be internally     * ready to handle as meaning invisible glyphs. The Font would report     * those as the missing glyph.     */    int[] getValidatedGlyphs(int[] oglyphs) {        int len = oglyphs.length;        int[] vglyphs = new int[len];        for (int i=0; i<len; i++) {            if (oglyphs[i] == 0xFFFE || oglyphs[i] == 0xFFFF) {                vglyphs[i] = oglyphs[i];            } else {                vglyphs[i] = font2D.getValidatedGlyphCode(oglyphs[i]);            }        }        return vglyphs;    }     // utility used by constructors    private void init(Font font, char[] text, int start, int count,                      FontRenderContext frc, int flags) {         if (start < 0 || count < 0 || start + count > text.length) {            throw new ArrayIndexOutOfBoundsException("start or count out of bounds");        }         this.font = font;        this.frc = frc;        this.flags = flags;         if (getTracking(font) != 0) {            addFlags(FLAG_HAS_POSITION_ADJUSTMENTS);        }         // !!! change mapper interface?        if (start != 0) {            char[] temp = new char[count];            System.arraycopy(text, start, temp, 0, count);            text = temp;        }         initFontData(); // sets up font2D         // !!! no layout for now, should add checks        // !!! need to support creating a StandardGlyphVector from a TextMeasurer's info...        glyphs = new int[count]; // hmmm        /* Glyphs obtained here are already validated by the font */        userGlyphs = glyphs;        font2D.getMapper().charsToGlyphs(count, text, glyphs);    }     private void initFontData() {        font2D = FontUtilities.getFont2D(font);        float s = font.getSize2D();        if (font.isTransformed()) {            ftx = font.getTransform();            if (ftx.getTranslateX() != 0 || ftx.getTranslateY() != 0) {                addFlags(FLAG_HAS_POSITION_ADJUSTMENTS);            }            ftx.setTransform(ftx.getScaleX(), ftx.getShearY(), ftx.getShearX(), ftx.getScaleY(), 0, 0);            ftx.scale(s, s);        } else {            ftx = AffineTransform.getScaleInstance(s, s);        }         frctx = frc.getTransform();        resetDTX(getNonTranslateTX(frctx));    }     /**     * Copy glyph position data into a result array starting at the indicated     * offset in the array.  If the passed-in result array is null, a new     * array will be allocated and returned.     *     * This is an internal method and does no extra argument checking.     *     * @param start the index of the first glyph to get     * @param count the number of glyphs to get     * @param offset the offset into result at which to put the data     * @param result an array to hold the x,y positions     * @return the modified position array     */    private float[] internalGetGlyphPositions(int start, int count, int offset, float[] result) {        if (result == null) {            result = new float[offset + count * 2];        }         initPositions();         // System.arraycopy is slow for stuff like this        for (int i = offset, e = offset + count * 2, p = start * 2; i < e; ++i, ++p) {            result[i] = positions[p];        }         return result;    }     private Rectangle2D getGlyphOutlineBounds(int ix) {        setFRCTX();        initPositions();        return getGlyphStrike(ix).getGlyphOutlineBounds(glyphs[ix], positions[ix*2], positions[ix*2+1]);    }     /**     * Used by getOutline, getGlyphsOutline     */    private Shape getGlyphsOutline(int start, int count, float x, float y) {        setFRCTX();        initPositions();         GeneralPath result = new GeneralPath(GeneralPath.WIND_NON_ZERO);        for (int i = start, e = start + count, n = start * 2; i < e; ++i, n += 2) {            float px = x + positions[n];            float py = y + positions[n+1];             getGlyphStrike(i).appendGlyphOutline(glyphs[i], result, px, py);        }         return result;    }     private Rectangle getGlyphsPixelBounds(FontRenderContext frc, float x, float y, int start, int count) {        initPositions(); // FIRST ensure we have positions based on our frctx         AffineTransform tx = null;        if (frc == null || frc.equals(this.frc)) {            tx = frctx;        } else {            tx = frc.getTransform();        }        setDTX(tx); // need to get the right strikes, but we use tx itself to translate the points         if (gti != null) {            return gti.getGlyphsPixelBounds(tx, x, y, start, count);        }         FontStrike fs = getDefaultStrike().strike;        Rectangle result = null;        Rectangle r = new Rectangle();        Point2D.Float pt = new Point.Float();        int n = start * 2;        while (--count >= 0) {            pt.x = x + positions[n++];            pt.y = y + positions[n++];            tx.transform(pt, pt);            fs.getGlyphImageBounds(glyphs[start++], pt, r);            if (!r.isEmpty()) {                if (result == null) {                    result = new Rectangle(r);                } else {                    result.add(r);                }            }        }        return result != null ? result : r;    }     private void clearCaches(int ix) {        if (lbcacheRef != null) {            Shape[] lbcache = (Shape[])lbcacheRef.get();            if (lbcache != null) {                lbcache[ix] = null;            }        }         if (vbcacheRef != null) {            Shape[] vbcache = (Shape[])vbcacheRef.get();            if (vbcache != null) {                vbcache[ix] = null;            }        }    }     private void clearCaches() {        lbcacheRef = null;        vbcacheRef = null;    }     // internal use only for possible future extension     /**     * A flag used with getLayoutFlags that indicates whether this <code>GlyphVector</code> uses     * a vertical baseline.     */    public static final int FLAG_USES_VERTICAL_BASELINE = 128;     /**     * A flag used with getLayoutFlags that indicates whether this <code>GlyphVector</code> uses     * vertical glyph metrics.  A <code>GlyphVector</code> can use vertical metrics on a     * horizontal line, or vice versa.     */    public static final int FLAG_USES_VERTICAL_METRICS = 256;     /**     * A flag used with getLayoutFlags that indicates whether this <code>GlyphVector</code> uses     * the 'alternate orientation.'  Glyphs have a default orientation given a     * particular baseline and metrics orientation, this is the orientation appropriate     * for left-to-right text.  For example, the letter 'A' can have four orientations,     * with the point at 12, 3, 6, or 9 'o clock.  The following table shows where the     * point displays for different values of vertical baseline (vb), vertical     * metrics (vm) and alternate orientation (fo):<br>     * <blockquote>     * vb vm ao     * -- -- --  --     *  f  f  f  12   ^  horizontal metrics on horizontal lines     *  f  f  t   6   v     *  f  t  f   9   <  vertical metrics on horizontal lines     *  f  t  t   3   >     *  t  f  f   3   >  horizontal metrics on vertical lines     *  t  f  t   9   <     *  t  t  f  12   ^  vertical metrics on vertical lines     *  t  t  t   6   v     * </blockquote>     */    public static final int FLAG_USES_ALTERNATE_ORIENTATION = 512;      /**     * Ensure that the positions array exists and holds position data.     * If the array is null, this allocates it and sets default positions.     */    private void initPositions() {        if (positions == null) {            setFRCTX();             positions = new float[glyphs.length * 2 + 2];             Point2D.Float trackPt = null;            float track = getTracking(font);            if (track != 0) {                track *= font.getSize2D();                trackPt = new Point2D.Float(track, 0); // advance delta            }             Point2D.Float pt = new Point2D.Float(0, 0);            if (font.isTransformed()) {                AffineTransform at = font.getTransform();                at.transform(pt, pt);                positions[0] = pt.x;                positions[1] = pt.y;                 if (trackPt != null) {                    at.deltaTransform(trackPt, trackPt);                }            }            for (int i = 0, n = 2; i < glyphs.length; ++i, n += 2) {                getGlyphStrike(i).addDefaultGlyphAdvance(glyphs[i], pt);                if (trackPt != null) {                    pt.x += trackPt.x;                    pt.y += trackPt.y;                }                positions[n] = pt.x;                positions[n+1] = pt.y;            }        }    }     /**     * OR newFlags with existing flags.  First computes existing flags if needed.     */    private void addFlags(int newflags) {        flags = getLayoutFlags() | newflags;    }     /**     * AND the complement of clearedFlags with existing flags.  First computes existing flags if needed.     */    private void clearFlags(int clearedFlags) {        flags = getLayoutFlags() & ~clearedFlags;    }     // general utility methods     // encapsulate the test to check whether we have per-glyph transforms    private GlyphStrike getGlyphStrike(int ix) {        if (gti == null) {            return getDefaultStrike();        } else {            return gti.getStrike(ix);        }    }     // encapsulate access to cached default glyph strike    private GlyphStrike getDefaultStrike() {        GlyphStrike gs = null;        if (fsref != null) {            gs = (GlyphStrike)fsref.get();        }        if (gs == null) {            gs = GlyphStrike.create(this, dtx, null);            fsref = new SoftReference(gs);        }        return gs;    }      /////////////////////    // Internal utility classes    /////////////////////     // !!! I have this as a separate class instead of just inside SGV,    // but I previously didn't bother.  Now I'm trying this again.    // Probably still not worth it, but I'd like to keep sgv's small in the common case.     static final class GlyphTransformInfo {        StandardGlyphVector sgv;  // reference back to glyph vector - yuck        int[] indices;            // index into unique strikes        double[] transforms;      // six doubles per unique transform, because AT is a pain to manipulate        SoftReference strikesRef; // ref to unique strikes, one per transform        boolean haveAllStrikes;   // true if the strike array has been filled by getStrikes().         // used when first setting a transform        GlyphTransformInfo(StandardGlyphVector sgv) {            this.sgv = sgv;        }         // used when cloning a glyph vector, need to set back link        GlyphTransformInfo(StandardGlyphVector sgv, GlyphTransformInfo rhs) {            this.sgv = sgv;             this.indices = rhs.indices == null ? null : (int[])rhs.indices.clone();            this.transforms = rhs.transforms == null ? null : (double[])rhs.transforms.clone();            this.strikesRef = null; // can't share cache, so rather than clone, we just null out        }         // used in sgv equality        public boolean equals(GlyphTransformInfo rhs) {            if (rhs == null) {                return false;            }            if (rhs == this) {                return true;            }            if (this.indices.length != rhs.indices.length) {                return false;            }            if (this.transforms.length != rhs.transforms.length) {                return false;            }             // slow since we end up processing the same transforms multiple            // times, but since transforms can be in any order, we either do            // this or create a mapping.  Equality tests aren't common so            // leave it like this.            for (int i = 0; i < this.indices.length; ++i) {                int tix = this.indices[i];                int rix = rhs.indices[i];                if ((tix == 0) != (rix == 0)) {                    return false;                }                if (tix != 0) {                    tix *= 6;                    rix *= 6;                    for (int j = 6; j > 0; --j) {                        if (this.indices[--tix] != rhs.indices[--rix]) {                            return false;                        }                    }                }            }            return true;        }         // implements sgv.setGlyphTransform        void setGlyphTransform(int glyphIndex, AffineTransform newTX) {             // we store all the glyph transforms as a double array, and for each glyph there            // is an entry in the txIndices array indicating which transform to use.  0 means            // there's no transform, 1 means use the first transform (the 6 doubles at offset            // 0), 2 means use the second transform (the 6 doubles at offset 6), etc.            //            // Since this can be called multiple times, and since the number of transforms            // affects the time it takes to construct the glyphs, we try to keep the arrays as            // compact as possible, by removing transforms that are no longer used, and reusing            // transforms where we already have them.             double[] temp = new double[6];            boolean isIdentity = true;            if (newTX == null || newTX.isIdentity()) {                // Fill in temp                temp[0] = temp[3] = 1.0;            }            else {                isIdentity = false;                newTX.getMatrix(temp);            }             if (indices == null) {                if (isIdentity) { // no change                    return;                }                 indices = new int[sgv.glyphs.length];                indices[glyphIndex] = 1;                transforms = temp;            } else {                boolean addSlot = false; // assume we're not growing                int newIndex = -1;                if (isIdentity) {                    newIndex = 0; // might shrink                } else {                    addSlot = true; // assume no match                    int i;                loop:                    for (i = 0; i < transforms.length; i += 6) {                        for (int j = 0; j < 6; ++j) {                            if (transforms[i + j] != temp[j]) {                                continue loop;                            }                        }                        addSlot = false;                        break;                    }                    newIndex = i / 6 + 1; // if no match, end of list                }                 // if we're using the same transform, nothing to do                int oldIndex = indices[glyphIndex];                if (newIndex != oldIndex) {                    // see if we are removing last use of the old slot                    boolean removeSlot = false;                    if (oldIndex != 0) {                        removeSlot = true;                        for (int i = 0; i < indices.length; ++i) {                            if (indices[i] == oldIndex && i != glyphIndex) {                                removeSlot = false;                                break;

Lines 1001–1500 of 1935. Previous lines