Path: blob/master/src/java.desktop/share/classes/sun/font/GlyphList.java
41154 views
/*1* Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.font;2627import java.awt.font.GlyphVector;28import java.util.concurrent.atomic.AtomicBoolean;2930import sun.java2d.SurfaceData;31import sun.java2d.loops.FontInfo;3233/*34* This class represents a list of actual renderable glyphs.35* It can be constructed from a number of text sources, representing36* the various ways in which a programmer can ask a Graphics2D object37* to render some text. Once constructed, it provides a way of iterating38* through the device metrics and graybits of the individual glyphs that39* need to be rendered to the screen.40*41* Note that this class holds pointers to native data which must be42* disposed. It is not marked as finalizable since it is intended43* to be very lightweight and finalization is a comparitively expensive44* procedure. The caller must specifically use try{} finally{} to45* manually ensure that the object is disposed after use, otherwise46* native data structures might be leaked.47*48* Here is a code sample for using this class:49*50* public void drawString(String str, FontInfo info, float x, float y) {51* GlyphList gl = GlyphList.getInstance();52* try {53* gl.setFromString(info, str, x, y);54* gl.startGlyphIteration();55* int numglyphs = gl.getNumGlyphs();56* for (int i = 0; i < numglyphs; i++) {57* gl.setGlyphIndex(i);58* int metrics[] = gl.getMetrics();59* byte bits[] = gl.getGrayBits();60* int glyphx = metrics[0];61* int glyphy = metrics[1];62* int glyphw = metrics[2];63* int glyphh = metrics[3];64* int off = 0;65* for (int j = 0; j < glyphh; j++) {66* for (int i = 0; i < glyphw; i++) {67* int dx = glyphx + i;68* int dy = glyphy + j;69* int alpha = bits[off++];70* drawPixel(alpha, dx, dy);71* }72* }73* }74* } finally {75* gl.dispose();76* }77* }78*/79public final class GlyphList {80private static final int MINGRAYLENGTH = 1024;81private static final int MAXGRAYLENGTH = 8192;82private static final int DEFAULT_LENGTH = 32;8384int glyphindex;85int[] metrics;86byte[] graybits;8788/* A reference to the strike is needed for the case when the GlyphList89* may be added to a queue for batch processing, (e.g. OpenGL) and we need90* to be completely certain that the strike is still valid when the glyphs91* images are later referenced. This does mean that if such code discards92* GlyphList and places only the data it contains on the queue, that the93* strike needs to be part of that data held by a strong reference.94* In the cases of drawString() and drawChars(), this is a single strike,95* although it may be a composite strike. In the case of96* drawGlyphVector() it may be a single strike, or a list of strikes.97*/98Object strikelist; // hold multiple strikes during rendering of complex gv99100/* In normal usage, the same GlyphList will get recycled, so101* it makes sense to allocate arrays that will get reused along with102* it, rather than generating garbage. Garbage will be generated only103* in MP envts where multiple threads are executing. Throughput should104* still be higher in those cases.105*/106int len = 0;107int maxLen = 0;108int maxPosLen = 0;109int[] glyphData;110char[] chData;111long[] images;112float[] positions;113float x, y;114float gposx, gposy;115boolean usePositions;116117/* lcdRGBOrder is used only by LCD text rendering. Its here because118* the Graphics may have a different hint value than the one used119* by a GlyphVector, so it has to be stored here - and is obtained120* from the right FontInfo. Another approach would have been to have121* install a separate pipe for that case but that's a lot of extra122* code when a simple boolean will suffice. The overhead to non-LCD123* text is a redundant boolean assign per call.124*/125boolean lcdRGBOrder;126127/*128* lcdSubPixPos is used only by LCD text rendering. Its here because129* the Graphics may have a different hint value than the one used130* by a GlyphVector, so it has to be stored here - and is obtained131* from the right FontInfo. Its also needed by the code which132* calculates glyph positions which already needs to access this133* GlyphList and would otherwise need the FontInfo.134* This is true only if LCD text and fractional metrics hints135* are selected on the graphics.136* When this is true and the glyph positions as determined by the137* advances are non-integral, it requests adjustment of the positions.138* Setting this for surfaces which do not support it through accelerated139* loops may cause a slow-down as software loops are invoked instead.140*/141boolean lcdSubPixPos;142143/* This scheme creates a singleton GlyphList which is checked out144* for use. Callers who find its checked out create one that after use145* is discarded. This means that in a MT-rendering environment,146* there's no need to synchronise except for that one instance.147* Fewer threads will then need to synchronise, perhaps helping148* throughput on a MP system. If for some reason the reusable149* GlyphList is checked out for a long time (or never returned?) then150* we would end up always creating new ones. That situation should not151* occur and if it did, it would just lead to some extra garbage being152* created.153*/154private static final GlyphList reusableGL = new GlyphList();155private static final AtomicBoolean inUse = new AtomicBoolean();156157private ColorGlyphSurfaceData glyphSurfaceData;158159void ensureCapacity(int len) {160/* Note len must not be -ve! only setFromChars should be capable161* of passing down a -ve len, and this guards against it.162*/163if (len < 0) {164len = 0;165}166if (usePositions && len > maxPosLen) {167positions = new float[len * 2 + 2];168maxPosLen = len;169}170171if (maxLen == 0 || len > maxLen) {172glyphData = new int[len];173chData = new char[len];174images = new long[len];175maxLen = len;176}177}178179private GlyphList() {180// ensureCapacity(DEFAULT_LENGTH);181}182183// private GlyphList(int arraylen) {184// ensureCapacity(arraylen);185// }186187public static GlyphList getInstance() {188if (inUse.compareAndSet(false, true)) {189return reusableGL;190} else {191return new GlyphList();192}193}194195/* In some cases the caller may be able to estimate the size of196* array needed, and it will usually be long enough. This avoids197* the unnecessary reallocation that occurs if our default198* values are too small. This is useful because this object199* will be discarded so the re-allocation overhead is high.200*/201// public static GlyphList getInstance(int sz) {202// if (inUse.compareAndSet(false, true) {203// return reusableGL;204// } else {205// return new GlyphList(sz);206// }207// }208209/* GlyphList is in an invalid state until setFrom* method is called.210* After obtaining a new GlyphList it is the caller's responsibility211* that one of these methods is executed before handing off the212* GlyphList213*/214215public boolean setFromString(FontInfo info, String str, float x, float y) {216this.x = x;217this.y = y;218this.strikelist = info.fontStrike;219this.lcdRGBOrder = info.lcdRGBOrder;220this.lcdSubPixPos = info.lcdSubPixPos;221len = str.length();222ensureCapacity(len);223str.getChars(0, len, chData, 0);224return mapChars(info, len);225}226227public boolean setFromChars(FontInfo info, char[] chars, int off, int alen,228float x, float y) {229this.x = x;230this.y = y;231this.strikelist = info.fontStrike;232this.lcdRGBOrder = info.lcdRGBOrder;233this.lcdSubPixPos = info.lcdSubPixPos;234len = alen;235if (alen < 0) {236len = 0;237} else {238len = alen;239}240ensureCapacity(len);241System.arraycopy(chars, off, chData, 0, len);242return mapChars(info, len);243}244245private boolean mapChars(FontInfo info, int len) {246/* REMIND.Is it worthwhile for the iteration to convert247* chars to glyph ids to directly map to images?248*/249if (info.font2D.getMapper().charsToGlyphsNS(len, chData, glyphData)) {250return false;251}252info.fontStrike.getGlyphImagePtrs(glyphData, images, len);253glyphindex = -1;254return true;255}256257258public void setFromGlyphVector(FontInfo info, GlyphVector gv,259float x, float y) {260this.x = x;261this.y = y;262this.lcdRGBOrder = info.lcdRGBOrder;263this.lcdSubPixPos = info.lcdSubPixPos;264/* A GV may be rendered in different Graphics. It is possible it is265* used for one case where LCD text is available, and another where266* it is not. Pass in the "info". to ensure get a suitable one.267*/268StandardGlyphVector sgv = StandardGlyphVector.getStandardGV(gv, info);269// call before ensureCapacity :-270usePositions = sgv.needsPositions(info.devTx);271len = sgv.getNumGlyphs();272ensureCapacity(len);273strikelist = sgv.setupGlyphImages(images,274usePositions ? positions : null,275info.devTx);276glyphindex = -1;277}278279public void startGlyphIteration() {280if (glyphindex >= 0) {281throw new InternalError("glyph iteration restarted");282}283if (metrics == null) {284metrics = new int[5];285}286/* gposx and gposy are used to accumulate the advance.287* Add 0.5f for consistent rounding to pixel position. */288gposx = x + 0.5f;289gposy = y + 0.5f;290}291292/*293* Must be called after 'startGlyphIteration'.294* Returns overall bounds for glyphs starting from the next glyph295* in iteration till the glyph with specified index.296* The underlying storage for bounds is shared with metrics,297* so this method (and the array it returns) shouldn't be used between298* 'setGlyphIndex' call and matching 'getMetrics' call.299*/300public int[] getBounds(int endGlyphIndex) {301fillBounds(metrics, endGlyphIndex);302return metrics;303}304305/* This method now assumes "state", so must be called 0->len306* The metrics it returns are accumulated on the fly307* So it could be renamed "nextGlyph()".308* Note that a laid out GlyphVector which has assigned glyph positions309* doesn't have this stricture..310*/311public void setGlyphIndex(int i) {312glyphindex = i;313if (images[i] == 0L) {314metrics[0] = (int)gposx;315metrics[1] = (int)gposy;316metrics[2] = 0;317metrics[3] = 0;318metrics[4] = 0;319return;320}321float gx =322StrikeCache.unsafe.getFloat(images[i]+StrikeCache.topLeftXOffset);323float gy =324StrikeCache.unsafe.getFloat(images[i]+StrikeCache.topLeftYOffset);325326if (usePositions) {327metrics[0] = (int)Math.floor(positions[(i<<1)] + gposx + gx);328metrics[1] = (int)Math.floor(positions[(i<<1)+1] + gposy + gy);329} else {330metrics[0] = (int)Math.floor(gposx + gx);331metrics[1] = (int)Math.floor(gposy + gy);332/* gposx and gposy are used to accumulate the advance */333gposx += StrikeCache.unsafe.getFloat334(images[i]+StrikeCache.xAdvanceOffset);335gposy += StrikeCache.unsafe.getFloat336(images[i]+StrikeCache.yAdvanceOffset);337}338metrics[2] =339StrikeCache.unsafe.getChar(images[i]+StrikeCache.widthOffset);340metrics[3] =341StrikeCache.unsafe.getChar(images[i]+StrikeCache.heightOffset);342metrics[4] =343StrikeCache.unsafe.getChar(images[i]+StrikeCache.rowBytesOffset);344}345346public int[] getMetrics() {347return metrics;348}349350public byte[] getGrayBits() {351int len = metrics[4] * metrics[3];352if (graybits == null) {353graybits = new byte[Math.max(len, MINGRAYLENGTH)];354} else {355if (len > graybits.length) {356graybits = new byte[len];357}358}359if (images[glyphindex] == 0L) {360return graybits;361}362long pixelDataAddress =363StrikeCache.unsafe.getAddress(images[glyphindex] +364StrikeCache.pixelDataOffset);365366if (pixelDataAddress == 0L) {367return graybits;368}369/* unsafe is supposed to be fast, but I doubt if this loop can beat370* a native call which does a getPrimitiveArrayCritical and a371* memcpy for the typical amount of image data (30-150 bytes)372* Consider a native method if there is a performance problem (which373* I haven't seen so far).374*/375for (int i=0; i<len; i++) {376graybits[i] = StrikeCache.unsafe.getByte(pixelDataAddress+i);377}378return graybits;379}380381public long[] getImages() {382return images;383}384385public boolean usePositions() {386return usePositions;387}388389public float[] getPositions() {390return positions;391}392393public float getX() {394return x;395}396397public float getY() {398return y;399}400401public Object getStrike() {402return strikelist;403}404405public boolean isSubPixPos() {406return lcdSubPixPos;407}408409public boolean isRGBOrder() {410return lcdRGBOrder;411}412413/* There's a reference equality test overhead here, but it allows us414* to avoid synchronizing for GL's that will just be GC'd. This415* helps MP throughput.416*/417public void dispose() {418if (this == reusableGL) {419if (graybits != null && graybits.length > MAXGRAYLENGTH) {420graybits = null;421}422usePositions = false;423strikelist = null; // remove reference to the strike list424inUse.set(false);425}426}427428/* The value here is for use by the rendering engine as it reflects429* the number of glyphs in the array to be blitted. Surrogates pairs430* may have two slots (the second of these being a dummy entry of the431* invisible glyph), whereas an application client would expect only432* one glyph. In other words don't propagate this value up to client code.433*434* {dlf} an application client should have _no_ expectations about the435* number of glyphs per char. This ultimately depends on the font436* technology and layout process used, which in general clients will437* know nothing about.438*/439public int getNumGlyphs() {440return len;441}442443/* We re-do all this work as we iterate through the glyphs444* but it seems unavoidable without re-working the Java TextRenderers.445*/446private void fillBounds(int[] bounds, int endGlyphIndex) {447/* Faster to access local variables in the for loop? */448int xOffset = StrikeCache.topLeftXOffset;449int yOffset = StrikeCache.topLeftYOffset;450int wOffset = StrikeCache.widthOffset;451int hOffset = StrikeCache.heightOffset;452int xAdvOffset = StrikeCache.xAdvanceOffset;453int yAdvOffset = StrikeCache.yAdvanceOffset;454455int startGlyphIndex = glyphindex + 1;456if (startGlyphIndex >= endGlyphIndex) {457bounds[0] = bounds[1] = bounds[2] = bounds[3] = 0;458return;459}460float bx0, by0, bx1, by1;461bx0 = by0 = Float.POSITIVE_INFINITY;462bx1 = by1 = Float.NEGATIVE_INFINITY;463464int posIndex = startGlyphIndex<<1;465float glx = gposx;466float gly = gposy;467char gw, gh;468float gx, gy, gx0, gy0, gx1, gy1;469for (int i=startGlyphIndex; i<endGlyphIndex; i++) {470if (images[i] == 0L) {471continue;472}473gx = StrikeCache.unsafe.getFloat(images[i]+xOffset);474gy = StrikeCache.unsafe.getFloat(images[i]+yOffset);475gw = StrikeCache.unsafe.getChar(images[i]+wOffset);476gh = StrikeCache.unsafe.getChar(images[i]+hOffset);477478if (usePositions) {479gx0 = positions[posIndex++] + gx + glx;480gy0 = positions[posIndex++] + gy + gly;481} else {482gx0 = glx + gx;483gy0 = gly + gy;484glx += StrikeCache.unsafe.getFloat(images[i]+xAdvOffset);485gly += StrikeCache.unsafe.getFloat(images[i]+yAdvOffset);486}487gx1 = gx0 + gw;488gy1 = gy0 + gh;489if (bx0 > gx0) bx0 = gx0;490if (by0 > gy0) by0 = gy0;491if (bx1 < gx1) bx1 = gx1;492if (by1 < gy1) by1 = gy1;493}494/* floor is safe and correct because all glyph widths, heights495* and offsets are integers496*/497bounds[0] = (int)Math.floor(bx0);498bounds[1] = (int)Math.floor(by0);499bounds[2] = (int)Math.floor(bx1);500bounds[3] = (int)Math.floor(by1);501}502503public static boolean canContainColorGlyphs() {504return FontUtilities.isMacOSX;505}506507public boolean isColorGlyph(int glyphIndex) {508int width = StrikeCache.unsafe.getChar(images[glyphIndex] +509StrikeCache.widthOffset);510int rowBytes = StrikeCache.unsafe.getChar(images[glyphIndex] +511StrikeCache.rowBytesOffset);512return rowBytes == width * 4;513}514515public SurfaceData getColorGlyphData() {516if (glyphSurfaceData == null) {517glyphSurfaceData = new ColorGlyphSurfaceData();518}519glyphSurfaceData.setCurrentGlyph(images[glyphindex]);520return glyphSurfaceData;521}522}523524525