--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.desktop/share/classes/sun/font/GlyphList.java Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,480 @@
+/*
+ * Copyright (c) 2000, 2013, 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 java.util.concurrent.atomic.AtomicBoolean;
+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 it did, it would just lead to some extra garbage being
+ * created.
+ */
+ private static final GlyphList reusableGL = new GlyphList();
+ private static final AtomicBoolean inUse = new AtomicBoolean();
+
+
+ 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() {
+ if (inUse.compareAndSet(false, true)) {
+ return reusableGL;
+ } else {
+ return new GlyphList();
+ }
+ }
+
+ /* 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.compareAndSet(false, true) {
+// return reusableGL;
+// } else {
+// return new GlyphList(sz);
+// }
+// }
+
+ /* 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 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 =
+ StrikeCache.unsafe.getAddress(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.set(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
+ */
+ bounds[0] = (int)Math.floor(bx0);
+ bounds[1] = (int)Math.floor(by0);
+ bounds[2] = (int)Math.floor(bx1);
+ bounds[3] = (int)Math.floor(by1);
+ }
+}