/* * Copyright (c) 2000, 2018, 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 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 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); } }