// 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 Load more
// 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;