if (rtl) flags |= FLAG_RUN_RTL; if (!rtl && !ltr) flags |= FLAG_COMPLEX_GLYPHS; } } return flags; } public float[] getGlyphPositions(int start, int count, float[] result) { if (count < 0) { throw new IllegalArgumentException("count = " + count); } if (start < 0) { throw new IndexOutOfBoundsException("start = " + start); } if (start > glyphs.length + 1 - count) { // watch for overflow throw new IndexOutOfBoundsException("start + count = " + (start + count)); } return internalGetGlyphPositions(start, count, 0, result); } public Shape getGlyphLogicalBounds(int ix) { if (ix < 0 || ix >= glyphs.length) { throw new IndexOutOfBoundsException("ix = " + ix); } Shape[] lbcache; if (lbcacheRef == null || (lbcache = (Shape[])lbcacheRef.get()) == null) { lbcache = new Shape[glyphs.length]; lbcacheRef = new SoftReference(lbcache); } Shape result = lbcache[ix]; if (result == null) { setFRCTX(); initPositions(); // !!! ought to return a rectangle2d for simple cases, though the following works for all // get the position, the tx offset, and the x,y advance and x,y adl. The // shape is the box formed by adv (width) and adl (height) offset by // the position plus the tx offset minus the ascent. ADL adl = new ADL(); GlyphStrike gs = getGlyphStrike(ix); gs.getADL(adl); Point2D.Float adv = gs.strike.getGlyphMetrics(glyphs[ix]); float wx = adv.x; float wy = adv.y; float hx = adl.descentX + adl.leadingX + adl.ascentX; float hy = adl.descentY + adl.leadingY + adl.ascentY; float x = positions[ix*2] + gs.dx - adl.ascentX; float y = positions[ix*2+1] + gs.dy - adl.ascentY; GeneralPath gp = new GeneralPath(); gp.moveTo(x, y); gp.lineTo(x + wx, y + wy); gp.lineTo(x + wx + hx, y + wy + hy); gp.lineTo(x + hx, y + hy); gp.closePath(); result = new DelegatingShape(gp); lbcache[ix] = result; } return result; } private SoftReference lbcacheRef; public Shape getGlyphVisualBounds(int ix) { if (ix < 0 || ix >= glyphs.length) { throw new IndexOutOfBoundsException("ix = " + ix); } Shape[] vbcache; if (vbcacheRef == null || (vbcache = (Shape[])vbcacheRef.get()) == null) { vbcache = new Shape[glyphs.length]; vbcacheRef = new SoftReference(vbcache); } Shape result = vbcache[ix]; if (result == null) { result = new DelegatingShape(getGlyphOutlineBounds(ix)); vbcache[ix] = result; } return result; } private SoftReference vbcacheRef; public Rectangle getGlyphPixelBounds(int index, FontRenderContext renderFRC, float x, float y) { return getGlyphsPixelBounds(renderFRC, x, y, index, 1); } public GlyphMetrics getGlyphMetrics(int ix) { if (ix < 0 || ix >= glyphs.length) { throw new IndexOutOfBoundsException("ix = " + ix); } Rectangle2D vb = getGlyphVisualBounds(ix).getBounds2D(); Point2D pt = getGlyphPosition(ix); vb.setRect(vb.getMinX() - pt.getX(), vb.getMinY() - pt.getY(), vb.getWidth(), vb.getHeight()); Point2D.Float adv = getGlyphStrike(ix).strike.getGlyphMetrics(glyphs[ix]); GlyphMetrics gm = new GlyphMetrics(true, adv.x, adv.y, vb, GlyphMetrics.STANDARD); return gm; } public GlyphJustificationInfo getGlyphJustificationInfo(int ix) { if (ix < 0 || ix >= glyphs.length) { throw new IndexOutOfBoundsException("ix = " + ix); } // currently we don't have enough information to do this right. should // get info from the font and use real OT/GX justification. Right now // sun/font/ExtendedTextSourceLabel assigns one of three infos // based on whether the char is kanji, space, or other. return null; } public boolean equals(GlyphVector rhs) { if (this == rhs) { return true; } if (rhs == null) { return false; } try { StandardGlyphVector other = (StandardGlyphVector)rhs; if (glyphs.length != other.glyphs.length) { return false; } for (int i = 0; i < glyphs.length; ++i) { if (glyphs[i] != other.glyphs[i]) { return false; } } if (!font.equals(other.font)) { return false; } if (!frc.equals(other.frc)) { return false; } if ((other.positions == null) != (positions == null)) { if (positions == null) { initPositions(); } else { other.initPositions(); } } if (positions != null) { for (int i = 0; i < positions.length; ++i) { if (positions[i] != other.positions[i]) { return false; } } } if (gti == null) { return other.gti == null; } else { return gti.equals(other.gti); } } catch (ClassCastException e) { // assume they are different simply by virtue of the class difference return false; } } /** * As a concrete subclass of Object that implements equality, this must * implement hashCode. */ public int hashCode() { return font.hashCode() ^ glyphs.length; } /** * Since we implement equality comparisons for GlyphVector, we implement * the inherited Object.equals(Object) as well. GlyphVector should do * this, and define two glyphvectors as not equal if the classes differ. */ public boolean equals(Object rhs) { try { return equals((GlyphVector)rhs); } catch (ClassCastException e) { return false; } } /** * Sometimes I wish java had covariant return types... */ public StandardGlyphVector copy() { return (StandardGlyphVector)clone(); } /** * As a concrete subclass of GlyphVector, this must implement clone. */ public Object clone() { // positions, gti are mutable so we have to clone them // font2d can be shared // fsref is a cache and can be shared try { StandardGlyphVector result = (StandardGlyphVector)super.clone(); result.clearCaches(); if (positions != null) { result.positions = (float[])positions.clone(); } if (gti != null) { result.gti = new GlyphTransformInfo(result, gti); } return result; } catch (CloneNotSupportedException e) { } return this; } ////////////////////// // StandardGlyphVector new public methods ///////////////////// /* * Set a multiple glyph positions at one time. GlyphVector only * provides API to set a single glyph at a time. */ public void setGlyphPositions(float[] srcPositions, int srcStart, int start, int count) { if (count < 0) { throw new IllegalArgumentException("count = " + count); } initPositions(); for (int i = start * 2, e = i + count * 2, p = srcStart; i < e; ++i, ++p) { positions[i] = srcPositions[p]; } clearCaches(); addFlags(FLAG_HAS_POSITION_ADJUSTMENTS); } /** * Set all the glyph positions, including the 'after last glyph' position. * The srcPositions array must be of length (numGlyphs + 1) * 2. */ public void setGlyphPositions(float[] srcPositions) { int requiredLength = glyphs.length * 2 + 2; if (srcPositions.length != requiredLength) { throw new IllegalArgumentException("srcPositions.length != " + requiredLength); } positions = (float[])srcPositions.clone(); clearCaches(); addFlags(FLAG_HAS_POSITION_ADJUSTMENTS); } /** * This is a convenience overload that gets all the glyph positions, which * is what you usually want to do if you're getting more than one. * !!! should I bother taking result parameter? */ public float[] getGlyphPositions(float[] result) { return internalGetGlyphPositions(0, glyphs.length + 1, 0, result); } /** * Get transform information for the requested range of glyphs. * If no glyphs have a transform, return null. * If a glyph has no transform (or is the identity transform) its entry in the result array will be null. * If the passed-in result is null an array will be allocated for the caller. * Each transform instance in the result array will unique, and independent of the GlyphVector's transform. */ public AffineTransform[] getGlyphTransforms(int start, int count, AffineTransform[] result) { if (start < 0 || count < 0 || start + count > glyphs.length) { throw new IllegalArgumentException("start: " + start + " count: " + count); } if (gti == null) { return null; } if (result == null) { result = new AffineTransform[count]; } for (int i = 0; i < count; ++i, ++start) { result[i] = gti.getGlyphTransform(start); } return result; } /** * Convenience overload for getGlyphTransforms(int, int, AffineTransform[], int); */ public AffineTransform[] getGlyphTransforms() { return getGlyphTransforms(0, glyphs.length, null); } /** * Set a number of glyph transforms. * Original transforms are unchanged. The array may contain nulls, and also may * contain multiple references to the same transform instance. */ public void setGlyphTransforms(AffineTransform[] srcTransforms, int srcStart, int start, int count) { for (int i = start, e = start + count; i < e; ++i) { setGlyphTransform(i, srcTransforms[srcStart + i]); } } /** * Convenience overload of setGlyphTransforms(AffineTransform[], int, int, int). */ public void setGlyphTransforms(AffineTransform[] srcTransforms) { setGlyphTransforms(srcTransforms, 0, 0, glyphs.length); } /** * For each glyph return posx, posy, advx, advy, visx, visy, visw, vish. */ public float[] getGlyphInfo() { setFRCTX(); initPositions(); float[] result = new float[glyphs.length * 8]; for (int i = 0, n = 0; i < glyphs.length; ++i, n += 8) { float x = positions[i*2]; float y = positions[i*2+1]; result[n] = x; result[n+1] = y; int glyphID = glyphs[i]; GlyphStrike s = getGlyphStrike(i); Point2D.Float adv = s.strike.getGlyphMetrics(glyphID); result[n+2] = adv.x; result[n+3] = adv.y; Rectangle2D vb = getGlyphVisualBounds(i).getBounds2D(); result[n+4] = (float)(vb.getMinX()); result[n+5] = (float)(vb.getMinY()); result[n+6] = (float)(vb.getWidth()); result[n+7] = (float)(vb.getHeight()); } return result; } /** * !!! not used currently, but might be by getPixelbounds? */ public void pixellate(FontRenderContext renderFRC, Point2D loc, Point pxResult) { if (renderFRC == null) { renderFRC = frc; } // it is a total pain that you have to copy the transform. AffineTransform at = renderFRC.getTransform(); at.transform(loc, loc); pxResult.x = (int)loc.getX(); // but must not behave oddly around zero pxResult.y = (int)loc.getY(); loc.setLocation(pxResult.x, pxResult.y); try { at.inverseTransform(loc, loc); } catch (NoninvertibleTransformException e) { throw new IllegalArgumentException("must be able to invert frc transform"); } } ////////////////////// // StandardGlyphVector package private methods ///////////////////// // used by glyphlist to determine if it needs to allocate/size positions array // gti always uses positions because the gtx might have translation. We also // need positions if the rendering dtx is different from the frctx. boolean needsPositions(double[] devTX) { return gti != null || (getLayoutFlags() & FLAG_HAS_POSITION_ADJUSTMENTS) != 0 || !matchTX(devTX, frctx); } // used by glyphList to get strong refs to font strikes for duration of rendering call // if devTX matches current devTX, we're ready to go // if we don't have multiple transforms, we're already ok // !!! I'm not sure fontInfo works so well for glyphvector, since we have to be able to handle // the multiple-strikes case /* * GlyphList calls this to set up its images data. First it calls needsPositions, * passing the devTX, to see if it should provide us a positions array to fill. * It only doesn't need them if we're a simple glyph vector whose frctx matches the * devtx. * Then it calls setupGlyphImages. If we need positions, we make sure we have our * default positions based on the frctx first. Then we set the devTX, and use * strikes based on it to generate the images. Finally, we fill in the positions * array. * If we have transforms, we delegate to gti. It depends on our having first * initialized the positions and devTX. */ Object setupGlyphImages(long[] images, float[] positions, double[] devTX) { initPositions(); // FIRST ensure we have positions based on our frctx setRenderTransform(devTX); // THEN make sure we are using the desired devTX if (gti != null) { return gti.setupGlyphImages(images, positions, dtx); } GlyphStrike gs = getDefaultStrike(); gs.strike.getGlyphImagePtrs(glyphs, images, glyphs.length); if (positions != null) { if (dtx.isIdentity()) { System.arraycopy(this.positions, 0, positions, 0, glyphs.length * 2); } else { dtx.transform(this.positions, 0, positions, 0, glyphs.length); } } return gs; } ////////////////////// // StandardGlyphVector private methods ///////////////////// // We keep translation in our frctx since getPixelBounds uses it. But // GlyphList pulls out the translation and applies it separately, so // we strip it out when we set the dtx. Basically nothing uses the // translation except getPixelBounds. // called by needsPositions, setRenderTransform private static boolean matchTX(double[] lhs, AffineTransform rhs) { return lhs[0] == rhs.getScaleX() && lhs[1] == rhs.getShearY() && lhs[2] == rhs.getShearX() && lhs[3] == rhs.getScaleY(); } // returns new tx if old one has translation, otherwise returns old one private static AffineTransform getNonTranslateTX(AffineTransform tx) { if (tx.getTranslateX() != 0 || tx.getTranslateY() != 0) { tx = new AffineTransform(tx.getScaleX(), tx.getShearY(), tx.getShearX(), tx.getScaleY(), 0, 0); } return tx; } private static boolean equalNonTranslateTX(AffineTransform lhs, AffineTransform rhs) { return lhs.getScaleX() == rhs.getScaleX() && lhs.getShearY() == rhs.getShearY() && lhs.getShearX() == rhs.getShearX() && lhs.getScaleY() == rhs.getScaleY(); } // called by setupGlyphImages (after needsPositions, so redundant match check?) private void setRenderTransform(double[] devTX) { assert(devTX.length == 4); if (!matchTX(devTX, dtx)) { resetDTX(new AffineTransform(devTX)); // no translation since devTX len == 4. } } // called by getGlyphsPixelBounds private final void setDTX(AffineTransform tx) { if (!equalNonTranslateTX(dtx, tx)) { resetDTX(getNonTranslateTX(tx)); } } Lines 501–1000 of 1935. Previous lines Load more
if (rtl) flags |= FLAG_RUN_RTL; if (!rtl && !ltr) flags |= FLAG_COMPLEX_GLYPHS; } } return flags; } public float[] getGlyphPositions(int start, int count, float[] result) { if (count < 0) { throw new IllegalArgumentException("count = " + count); } if (start < 0) { throw new IndexOutOfBoundsException("start = " + start); } if (start > glyphs.length + 1 - count) { // watch for overflow throw new IndexOutOfBoundsException("start + count = " + (start + count)); } return internalGetGlyphPositions(start, count, 0, result); } public Shape getGlyphLogicalBounds(int ix) { if (ix < 0 || ix >= glyphs.length) { throw new IndexOutOfBoundsException("ix = " + ix); } Shape[] lbcache; if (lbcacheRef == null || (lbcache = (Shape[])lbcacheRef.get()) == null) { lbcache = new Shape[glyphs.length]; lbcacheRef = new SoftReference(lbcache); } Shape result = lbcache[ix]; if (result == null) { setFRCTX(); initPositions(); // !!! ought to return a rectangle2d for simple cases, though the following works for all // get the position, the tx offset, and the x,y advance and x,y adl. The // shape is the box formed by adv (width) and adl (height) offset by // the position plus the tx offset minus the ascent. ADL adl = new ADL(); GlyphStrike gs = getGlyphStrike(ix); gs.getADL(adl); Point2D.Float adv = gs.strike.getGlyphMetrics(glyphs[ix]); float wx = adv.x; float wy = adv.y; float hx = adl.descentX + adl.leadingX + adl.ascentX; float hy = adl.descentY + adl.leadingY + adl.ascentY; float x = positions[ix*2] + gs.dx - adl.ascentX; float y = positions[ix*2+1] + gs.dy - adl.ascentY; GeneralPath gp = new GeneralPath(); gp.moveTo(x, y); gp.lineTo(x + wx, y + wy); gp.lineTo(x + wx + hx, y + wy + hy); gp.lineTo(x + hx, y + hy); gp.closePath(); result = new DelegatingShape(gp); lbcache[ix] = result; } return result; } private SoftReference lbcacheRef; public Shape getGlyphVisualBounds(int ix) { if (ix < 0 || ix >= glyphs.length) { throw new IndexOutOfBoundsException("ix = " + ix); } Shape[] vbcache; if (vbcacheRef == null || (vbcache = (Shape[])vbcacheRef.get()) == null) { vbcache = new Shape[glyphs.length]; vbcacheRef = new SoftReference(vbcache); } Shape result = vbcache[ix]; if (result == null) { result = new DelegatingShape(getGlyphOutlineBounds(ix)); vbcache[ix] = result; } return result; } private SoftReference vbcacheRef; public Rectangle getGlyphPixelBounds(int index, FontRenderContext renderFRC, float x, float y) { return getGlyphsPixelBounds(renderFRC, x, y, index, 1); } public GlyphMetrics getGlyphMetrics(int ix) { if (ix < 0 || ix >= glyphs.length) { throw new IndexOutOfBoundsException("ix = " + ix); } Rectangle2D vb = getGlyphVisualBounds(ix).getBounds2D(); Point2D pt = getGlyphPosition(ix); vb.setRect(vb.getMinX() - pt.getX(), vb.getMinY() - pt.getY(), vb.getWidth(), vb.getHeight()); Point2D.Float adv = getGlyphStrike(ix).strike.getGlyphMetrics(glyphs[ix]); GlyphMetrics gm = new GlyphMetrics(true, adv.x, adv.y, vb, GlyphMetrics.STANDARD); return gm; } public GlyphJustificationInfo getGlyphJustificationInfo(int ix) { if (ix < 0 || ix >= glyphs.length) { throw new IndexOutOfBoundsException("ix = " + ix); } // currently we don't have enough information to do this right. should // get info from the font and use real OT/GX justification. Right now // sun/font/ExtendedTextSourceLabel assigns one of three infos // based on whether the char is kanji, space, or other. return null; } public boolean equals(GlyphVector rhs) { if (this == rhs) { return true; } if (rhs == null) { return false; } try { StandardGlyphVector other = (StandardGlyphVector)rhs; if (glyphs.length != other.glyphs.length) { return false; } for (int i = 0; i < glyphs.length; ++i) { if (glyphs[i] != other.glyphs[i]) { return false; } } if (!font.equals(other.font)) { return false; } if (!frc.equals(other.frc)) { return false; } if ((other.positions == null) != (positions == null)) { if (positions == null) { initPositions(); } else { other.initPositions(); } } if (positions != null) { for (int i = 0; i < positions.length; ++i) { if (positions[i] != other.positions[i]) { return false; } } } if (gti == null) { return other.gti == null; } else { return gti.equals(other.gti); } } catch (ClassCastException e) { // assume they are different simply by virtue of the class difference return false; } } /** * As a concrete subclass of Object that implements equality, this must * implement hashCode. */ public int hashCode() { return font.hashCode() ^ glyphs.length; } /** * Since we implement equality comparisons for GlyphVector, we implement * the inherited Object.equals(Object) as well. GlyphVector should do * this, and define two glyphvectors as not equal if the classes differ. */ public boolean equals(Object rhs) { try { return equals((GlyphVector)rhs); } catch (ClassCastException e) { return false; } } /** * Sometimes I wish java had covariant return types... */ public StandardGlyphVector copy() { return (StandardGlyphVector)clone(); } /** * As a concrete subclass of GlyphVector, this must implement clone. */ public Object clone() { // positions, gti are mutable so we have to clone them // font2d can be shared // fsref is a cache and can be shared try { StandardGlyphVector result = (StandardGlyphVector)super.clone(); result.clearCaches(); if (positions != null) { result.positions = (float[])positions.clone(); } if (gti != null) { result.gti = new GlyphTransformInfo(result, gti); } return result; } catch (CloneNotSupportedException e) { } return this; } ////////////////////// // StandardGlyphVector new public methods ///////////////////// /* * Set a multiple glyph positions at one time. GlyphVector only * provides API to set a single glyph at a time. */ public void setGlyphPositions(float[] srcPositions, int srcStart, int start, int count) { if (count < 0) { throw new IllegalArgumentException("count = " + count); } initPositions(); for (int i = start * 2, e = i + count * 2, p = srcStart; i < e; ++i, ++p) { positions[i] = srcPositions[p]; } clearCaches(); addFlags(FLAG_HAS_POSITION_ADJUSTMENTS); } /** * Set all the glyph positions, including the 'after last glyph' position. * The srcPositions array must be of length (numGlyphs + 1) * 2. */ public void setGlyphPositions(float[] srcPositions) { int requiredLength = glyphs.length * 2 + 2; if (srcPositions.length != requiredLength) { throw new IllegalArgumentException("srcPositions.length != " + requiredLength); } positions = (float[])srcPositions.clone(); clearCaches(); addFlags(FLAG_HAS_POSITION_ADJUSTMENTS); } /** * This is a convenience overload that gets all the glyph positions, which * is what you usually want to do if you're getting more than one. * !!! should I bother taking result parameter? */ public float[] getGlyphPositions(float[] result) { return internalGetGlyphPositions(0, glyphs.length + 1, 0, result); } /** * Get transform information for the requested range of glyphs. * If no glyphs have a transform, return null. * If a glyph has no transform (or is the identity transform) its entry in the result array will be null. * If the passed-in result is null an array will be allocated for the caller. * Each transform instance in the result array will unique, and independent of the GlyphVector's transform. */ public AffineTransform[] getGlyphTransforms(int start, int count, AffineTransform[] result) { if (start < 0 || count < 0 || start + count > glyphs.length) { throw new IllegalArgumentException("start: " + start + " count: " + count); } if (gti == null) { return null; } if (result == null) { result = new AffineTransform[count]; } for (int i = 0; i < count; ++i, ++start) { result[i] = gti.getGlyphTransform(start); } return result; } /** * Convenience overload for getGlyphTransforms(int, int, AffineTransform[], int); */ public AffineTransform[] getGlyphTransforms() { return getGlyphTransforms(0, glyphs.length, null); } /** * Set a number of glyph transforms. * Original transforms are unchanged. The array may contain nulls, and also may * contain multiple references to the same transform instance. */ public void setGlyphTransforms(AffineTransform[] srcTransforms, int srcStart, int start, int count) { for (int i = start, e = start + count; i < e; ++i) { setGlyphTransform(i, srcTransforms[srcStart + i]); } } /** * Convenience overload of setGlyphTransforms(AffineTransform[], int, int, int). */ public void setGlyphTransforms(AffineTransform[] srcTransforms) { setGlyphTransforms(srcTransforms, 0, 0, glyphs.length); } /** * For each glyph return posx, posy, advx, advy, visx, visy, visw, vish. */ public float[] getGlyphInfo() { setFRCTX(); initPositions(); float[] result = new float[glyphs.length * 8]; for (int i = 0, n = 0; i < glyphs.length; ++i, n += 8) { float x = positions[i*2]; float y = positions[i*2+1]; result[n] = x; result[n+1] = y; int glyphID = glyphs[i]; GlyphStrike s = getGlyphStrike(i); Point2D.Float adv = s.strike.getGlyphMetrics(glyphID); result[n+2] = adv.x; result[n+3] = adv.y; Rectangle2D vb = getGlyphVisualBounds(i).getBounds2D(); result[n+4] = (float)(vb.getMinX()); result[n+5] = (float)(vb.getMinY()); result[n+6] = (float)(vb.getWidth()); result[n+7] = (float)(vb.getHeight()); } return result; } /** * !!! not used currently, but might be by getPixelbounds? */ public void pixellate(FontRenderContext renderFRC, Point2D loc, Point pxResult) { if (renderFRC == null) { renderFRC = frc; } // it is a total pain that you have to copy the transform. AffineTransform at = renderFRC.getTransform(); at.transform(loc, loc); pxResult.x = (int)loc.getX(); // but must not behave oddly around zero pxResult.y = (int)loc.getY(); loc.setLocation(pxResult.x, pxResult.y); try { at.inverseTransform(loc, loc); } catch (NoninvertibleTransformException e) { throw new IllegalArgumentException("must be able to invert frc transform"); } } ////////////////////// // StandardGlyphVector package private methods ///////////////////// // used by glyphlist to determine if it needs to allocate/size positions array // gti always uses positions because the gtx might have translation. We also // need positions if the rendering dtx is different from the frctx. boolean needsPositions(double[] devTX) { return gti != null || (getLayoutFlags() & FLAG_HAS_POSITION_ADJUSTMENTS) != 0 || !matchTX(devTX, frctx); } // used by glyphList to get strong refs to font strikes for duration of rendering call // if devTX matches current devTX, we're ready to go // if we don't have multiple transforms, we're already ok // !!! I'm not sure fontInfo works so well for glyphvector, since we have to be able to handle // the multiple-strikes case /* * GlyphList calls this to set up its images data. First it calls needsPositions, * passing the devTX, to see if it should provide us a positions array to fill. * It only doesn't need them if we're a simple glyph vector whose frctx matches the * devtx. * Then it calls setupGlyphImages. If we need positions, we make sure we have our * default positions based on the frctx first. Then we set the devTX, and use * strikes based on it to generate the images. Finally, we fill in the positions * array. * If we have transforms, we delegate to gti. It depends on our having first * initialized the positions and devTX. */ Object setupGlyphImages(long[] images, float[] positions, double[] devTX) { initPositions(); // FIRST ensure we have positions based on our frctx setRenderTransform(devTX); // THEN make sure we are using the desired devTX if (gti != null) { return gti.setupGlyphImages(images, positions, dtx); } GlyphStrike gs = getDefaultStrike(); gs.strike.getGlyphImagePtrs(glyphs, images, glyphs.length); if (positions != null) { if (dtx.isIdentity()) { System.arraycopy(this.positions, 0, positions, 0, glyphs.length * 2); } else { dtx.transform(this.positions, 0, positions, 0, glyphs.length); } } return gs; } ////////////////////// // StandardGlyphVector private methods ///////////////////// // We keep translation in our frctx since getPixelBounds uses it. But // GlyphList pulls out the translation and applies it separately, so // we strip it out when we set the dtx. Basically nothing uses the // translation except getPixelBounds. // called by needsPositions, setRenderTransform private static boolean matchTX(double[] lhs, AffineTransform rhs) { return lhs[0] == rhs.getScaleX() && lhs[1] == rhs.getShearY() && lhs[2] == rhs.getShearX() && lhs[3] == rhs.getScaleY(); } // returns new tx if old one has translation, otherwise returns old one private static AffineTransform getNonTranslateTX(AffineTransform tx) { if (tx.getTranslateX() != 0 || tx.getTranslateY() != 0) { tx = new AffineTransform(tx.getScaleX(), tx.getShearY(), tx.getShearX(), tx.getScaleY(), 0, 0); } return tx; } private static boolean equalNonTranslateTX(AffineTransform lhs, AffineTransform rhs) { return lhs.getScaleX() == rhs.getScaleX() && lhs.getShearY() == rhs.getShearY() && lhs.getShearX() == rhs.getShearX() && lhs.getScaleY() == rhs.getScaleY(); } // called by setupGlyphImages (after needsPositions, so redundant match check?) private void setRenderTransform(double[] devTX) { assert(devTX.length == 4); if (!matchTX(devTX, dtx)) { resetDTX(new AffineTransform(devTX)); // no translation since devTX len == 4. } } // called by getGlyphsPixelBounds private final void setDTX(AffineTransform tx) { if (!equalNonTranslateTX(dtx, tx)) { resetDTX(getNonTranslateTX(tx)); } }