Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/macosx/classes/sun/font/CStrike.java
38827 views
/*1* Copyright (c) 2011, 2013, 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.Rectangle;28import java.awt.geom.*;29import java.util.*;3031import sun.awt.SunHints;3233public final class CStrike extends PhysicalStrike {3435// Creates the native strike36private static native long createNativeStrikePtr(long nativeFontPtr,37double[] glyphTx,38double[] invDevTxMatrix,39int aaHint,40int fmHint);4142// Disposes the native strike43private static native void disposeNativeStrikePtr(long nativeStrikePtr);4445// Creates a StrikeMetrics from the underlying native system fonts46private static native StrikeMetrics getFontMetrics(long nativeStrikePtr);4748// Returns native struct pointers used by the Sun 2D Renderer49private static native void getGlyphImagePtrsNative(long nativeStrikePtr,50long[] glyphInfos,51int[] uniCodes, int len);5253// Returns the advance give a glyph code. It should be used only54// when the glyph code belongs to the CFont passed in.55private static native float getNativeGlyphAdvance(long nativeStrikePtr,56int glyphCode);5758// Returns the outline shape of a glyph59private static native GeneralPath getNativeGlyphOutline(long nativeStrikePtr,60int glyphCode,61double x,62double y);6364// returns the bounding rect for a glyph65private static native void getNativeGlyphImageBounds(long nativeStrikePtr,66int glyphCode,67Rectangle2D.Float result,68double x, double y);6970private final CFont nativeFont;71private AffineTransform invDevTx;72private final GlyphInfoCache glyphInfoCache;73private final GlyphAdvanceCache glyphAdvanceCache;74private long nativeStrikePtr;7576CStrike(final CFont font, final FontStrikeDesc inDesc) {77nativeFont = font;78desc = inDesc;79glyphInfoCache = new GlyphInfoCache(font, desc);80glyphAdvanceCache = new GlyphAdvanceCache();81disposer = glyphInfoCache;8283// Normally the device transform should be the identity transform84// for screen operations. The device transform only becomes85// interesting when we are outputting between different dpi surfaces,86// like when we are printing to postscript or use retina.87if (inDesc.devTx != null && !inDesc.devTx.isIdentity()) {88try {89invDevTx = inDesc.devTx.createInverse();90} catch (NoninvertibleTransformException ignored) {91// ignored, since device transforms should not be that92// complicated, and if they are - there is nothing we can do,93// so we won't worry about it.94}95}96}9798public long getNativeStrikePtr() {99if (nativeStrikePtr != 0) {100return nativeStrikePtr;101}102103final double[] glyphTx = new double[6];104desc.glyphTx.getMatrix(glyphTx);105106final double[] invDevTxMatrix = new double[6];107if (invDevTx == null) {108invDevTxMatrix[0] = 1;109invDevTxMatrix[3] = 1;110} else {111invDevTx.getMatrix(invDevTxMatrix);112}113114final int aaHint = desc.aaHint;115final int fmHint = desc.fmHint;116117synchronized (this) {118if (nativeStrikePtr != 0) {119return nativeStrikePtr;120}121nativeStrikePtr =122createNativeStrikePtr(nativeFont.getNativeFontPtr(),123glyphTx, invDevTxMatrix, aaHint, fmHint);124}125126return nativeStrikePtr;127}128129protected synchronized void finalize() throws Throwable {130if (nativeStrikePtr != 0) {131disposeNativeStrikePtr(nativeStrikePtr);132}133nativeStrikePtr = 0;134}135136137@Override138public int getNumGlyphs() {139return nativeFont.getNumGlyphs();140}141142@Override143StrikeMetrics getFontMetrics() {144if (strikeMetrics == null) {145StrikeMetrics metrics = getFontMetrics(getNativeStrikePtr());146if (invDevTx != null) {147metrics.convertToUserSpace(invDevTx);148}149metrics.convertToUserSpace(desc.glyphTx);150strikeMetrics = metrics;151}152return strikeMetrics;153}154155@Override156float getGlyphAdvance(final int glyphCode) {157return getCachedNativeGlyphAdvance(glyphCode);158}159160@Override161float getCodePointAdvance(final int cp) {162return getGlyphAdvance(nativeFont.getMapper().charToGlyph(cp));163}164165@Override166Point2D.Float getCharMetrics(final char ch) {167return getGlyphMetrics(nativeFont.getMapper().charToGlyph(ch));168}169170@Override171Point2D.Float getGlyphMetrics(final int glyphCode) {172return new Point2D.Float(getGlyphAdvance(glyphCode), 0.0f);173}174175Rectangle2D.Float getGlyphOutlineBounds(int glyphCode) {176GeneralPath gp = getGlyphOutline(glyphCode, 0f, 0f);177Rectangle2D r2d = gp.getBounds2D();178Rectangle2D.Float r2df;179if (r2d instanceof Rectangle2D.Float) {180r2df = (Rectangle2D.Float)r2d;181} else {182float x = (float)r2d.getX();183float y = (float)r2d.getY();184float w = (float)r2d.getWidth();185float h = (float)r2d.getHeight();186r2df = new Rectangle2D.Float(x, y, w, h);187}188return r2df;189}190191// pt, result in device space192void getGlyphImageBounds(int glyphCode, Point2D.Float pt, Rectangle result) {193Rectangle2D.Float floatRect = new Rectangle2D.Float();194195if (invDevTx != null) {196invDevTx.transform(pt, pt);197}198199getGlyphImageBounds(glyphCode, pt.x, pt.y, floatRect);200201if (floatRect.width == 0 && floatRect.height == 0) {202result.setRect(0, 0, -1, -1);203return;204}205206result.setRect(floatRect.x + pt.x, floatRect.y + pt.y, floatRect.width, floatRect.height);207}208209private void getGlyphImageBounds(int glyphCode, float x, float y, Rectangle2D.Float floatRect) {210getNativeGlyphImageBounds(getNativeStrikePtr(), glyphCode, floatRect, x, y);211}212213GeneralPath getGlyphOutline(int glyphCode, float x, float y) {214return getNativeGlyphOutline(getNativeStrikePtr(), glyphCode, x, y);215}216217// should implement, however not called though any path that is publicly exposed218GeneralPath getGlyphVectorOutline(int[] glyphs, float x, float y) {219throw new Error("not implemented yet");220}221222// called from the Sun2D renderer223long getGlyphImagePtr(int glyphCode) {224synchronized (glyphInfoCache) {225long ptr = glyphInfoCache.get(glyphCode);226if (ptr != 0L) return ptr;227228long[] ptrs = new long[1];229int[] codes = new int[1];230codes[0] = glyphCode;231232getGlyphImagePtrs(codes, ptrs, 1);233234ptr = ptrs[0];235glyphInfoCache.put(glyphCode, ptr);236237return ptr;238}239}240241// called from the Sun2D renderer242void getGlyphImagePtrs(int[] glyphCodes, long[] images, int len) {243synchronized (glyphInfoCache) {244// fill the image pointer array with existing pointers245// from the cache246int missed = 0;247for (int i = 0; i < len; i++) {248int code = glyphCodes[i];249250final long ptr = glyphInfoCache.get(code);251if (ptr != 0L) {252images[i] = ptr;253} else {254// zero this element out, because the caller does not255// promise to keep it clean256images[i] = 0L;257missed++;258}259}260261if (missed == 0) {262return; // horray! we got away without touching native!263}264265// all distinct glyph codes requested (partially filled)266final int[] filteredCodes = new int[missed];267// indices into filteredCodes array (totally filled)268final int[] filteredIndicies = new int[missed];269270// scan, mark, and store the requested glyph codes again to271// send into native272int j = 0;273int dupes = 0;274for (int i = 0; i < len; i++) {275if (images[i] != 0L) continue; // already filled276277final int code = glyphCodes[i];278279// we have already promised to strike this glyph - this is280// a dupe281if (glyphInfoCache.get(code) == -1L) {282filteredIndicies[j] = -1;283dupes++;284j++;285continue;286}287288// this is a distinct glyph we have not struck before, or289// promised to strike mark this one as "promise to strike"290// in the global cache with a -1L291final int k = j - dupes;292filteredCodes[k] = code;293glyphInfoCache.put(code, -1L);294filteredIndicies[j] = k;295j++;296}297298final int filteredRunLen = j - dupes;299final long[] filteredImages = new long[filteredRunLen];300301// bulk call to fill in the distinct glyph pointers from native302getFilteredGlyphImagePtrs(filteredImages, filteredCodes, filteredRunLen);303304// scan the requested glyph list, and fill in pointers from our305// distinct glyph list which has been filled from native306j = 0;307for (int i = 0; i < len; i++) {308if (images[i] != 0L && images[i] != -1L) {309continue; // already placed310}311312// index into filteredImages array313final int k = filteredIndicies[j];314final int code = glyphCodes[i];315if (k == -1L) {316// we should have already filled the cache with this pointer317images[i] = glyphInfoCache.get(code);318} else {319// fill the particular glyph code request, and store320// in the cache321final long ptr = filteredImages[k];322images[i] = ptr;323glyphInfoCache.put(code, ptr);324}325326j++;327}328}329}330331private void getFilteredGlyphImagePtrs(long[] glyphInfos,332int[] uniCodes, int len)333{334getGlyphImagePtrsNative(getNativeStrikePtr(), glyphInfos, uniCodes, len);335}336337private float getCachedNativeGlyphAdvance(int glyphCode) {338synchronized(glyphAdvanceCache) {339float advance = glyphAdvanceCache.get(glyphCode);340if (advance != 0) {341return advance;342}343344advance = getNativeGlyphAdvance(getNativeStrikePtr(), glyphCode);345glyphAdvanceCache.put(glyphCode, advance);346return advance;347}348}349350// This class stores glyph pointers, and is indexed based on glyph codes,351// and negative unicode values. See the comments in352// CCharToGlyphMapper for more details on our glyph code strategy.353private static class GlyphInfoCache extends CStrikeDisposer {354private static final int FIRST_LAYER_SIZE = 256;355private static final int SECOND_LAYER_SIZE = 16384; // 16384 = 128x128356357// rdar://problem/5204197358private boolean disposed = false;359360private final long[] firstLayerCache;361private SparseBitShiftingTwoLayerArray secondLayerCache;362private HashMap<Integer, Long> generalCache;363364GlyphInfoCache(final Font2D nativeFont, final FontStrikeDesc desc) {365super(nativeFont, desc);366firstLayerCache = new long[FIRST_LAYER_SIZE];367}368369public synchronized long get(final int index) {370if (index < 0) {371if (-index < SECOND_LAYER_SIZE) {372// catch common unicodes373if (secondLayerCache == null) {374return 0L;375}376return secondLayerCache.get(-index);377}378} else {379if (index < FIRST_LAYER_SIZE) {380// catch common glyphcodes381return firstLayerCache[index];382}383}384385if (generalCache == null) {386return 0L;387}388final Long value = generalCache.get(new Integer(index));389if (value == null) {390return 0L;391}392return value.longValue();393}394395public synchronized void put(final int index, final long value) {396if (index < 0) {397if (-index < SECOND_LAYER_SIZE) {398// catch common unicodes399if (secondLayerCache == null) {400secondLayerCache = new SparseBitShiftingTwoLayerArray(SECOND_LAYER_SIZE, 7); // 128x128401}402secondLayerCache.put(-index, value);403return;404}405} else {406if (index < FIRST_LAYER_SIZE) {407// catch common glyphcodes408firstLayerCache[index] = value;409return;410}411}412413if (generalCache == null) {414generalCache = new HashMap<Integer, Long>();415}416417generalCache.put(new Integer(index), new Long(value));418}419420public synchronized void dispose() {421// rdar://problem/5204197422// Note that sun.font.Font2D.getStrike() actively disposes423// cleared strikeRef. We need to check the disposed flag to424// prevent double frees of native resources.425if (disposed) {426return;427}428429super.dispose();430431// clean out the first array432disposeLongArray(firstLayerCache);433434// clean out the two layer arrays435if (secondLayerCache != null) {436final long[][] secondLayerLongArrayArray = secondLayerCache.cache;437for (int i = 0; i < secondLayerLongArrayArray.length; i++) {438final long[] longArray = secondLayerLongArrayArray[i];439if (longArray != null) disposeLongArray(longArray);440}441}442443// clean up everyone else444if (generalCache != null) {445final Iterator<Long> i = generalCache.values().iterator();446while (i.hasNext()) {447final long longValue = i.next().longValue();448if (longValue != -1 && longValue != 0) {449removeGlyphInfoFromCache(longValue);450StrikeCache.freeLongPointer(longValue);451}452}453}454455// rdar://problem/5204197456// Finally, set the flag.457disposed = true;458}459460private static void disposeLongArray(final long[] longArray) {461for (int i = 0; i < longArray.length; i++) {462final long ptr = longArray[i];463if (ptr != 0 && ptr != -1) {464removeGlyphInfoFromCache(ptr);465StrikeCache.freeLongPointer(ptr); // free's the native struct pointer466}467}468}469470private static class SparseBitShiftingTwoLayerArray {471final long[][] cache;472final int shift;473final int secondLayerLength;474475SparseBitShiftingTwoLayerArray(final int size, final int shift) {476this.shift = shift;477this.cache = new long[1 << shift][];478this.secondLayerLength = size >> shift;479}480481public long get(final int index) {482final int firstIndex = index >> shift;483final long[] firstLayerRow = cache[firstIndex];484if (firstLayerRow == null) return 0L;485return firstLayerRow[index - (firstIndex * (1 << shift))];486}487488public void put(final int index, final long value) {489final int firstIndex = index >> shift;490long[] firstLayerRow = cache[firstIndex];491if (firstLayerRow == null) {492cache[firstIndex] = firstLayerRow = new long[secondLayerLength];493}494firstLayerRow[index - (firstIndex * (1 << shift))] = value;495}496}497}498499private static class GlyphAdvanceCache {500private static final int FIRST_LAYER_SIZE = 256;501private static final int SECOND_LAYER_SIZE = 16384; // 16384 = 128x128502503private final float[] firstLayerCache = new float[FIRST_LAYER_SIZE];504private SparseBitShiftingTwoLayerArray secondLayerCache;505private HashMap<Integer, Float> generalCache;506507// Empty non private constructor was added because access to this508// class shouldn't be emulated by a synthetic accessor method.509GlyphAdvanceCache() {510super();511}512513public synchronized float get(final int index) {514if (index < 0) {515if (-index < SECOND_LAYER_SIZE) {516// catch common unicodes517if (secondLayerCache == null) return 0;518return secondLayerCache.get(-index);519}520} else {521if (index < FIRST_LAYER_SIZE) {522// catch common glyphcodes523return firstLayerCache[index];524}525}526527if (generalCache == null) return 0;528final Float value = generalCache.get(new Integer(index));529if (value == null) return 0;530return value.floatValue();531}532533public synchronized void put(final int index, final float value) {534if (index < 0) {535if (-index < SECOND_LAYER_SIZE) {536// catch common unicodes537if (secondLayerCache == null) {538secondLayerCache = new SparseBitShiftingTwoLayerArray(SECOND_LAYER_SIZE, 7); // 128x128539}540secondLayerCache.put(-index, value);541return;542}543} else {544if (index < FIRST_LAYER_SIZE) {545// catch common glyphcodes546firstLayerCache[index] = value;547return;548}549}550551if (generalCache == null) {552generalCache = new HashMap<Integer, Float>();553}554555generalCache.put(new Integer(index), new Float(value));556}557558private static class SparseBitShiftingTwoLayerArray {559final float[][] cache;560final int shift;561final int secondLayerLength;562563SparseBitShiftingTwoLayerArray(final int size, final int shift) {564this.shift = shift;565this.cache = new float[1 << shift][];566this.secondLayerLength = size >> shift;567}568569public float get(final int index) {570final int firstIndex = index >> shift;571final float[] firstLayerRow = cache[firstIndex];572if (firstLayerRow == null) return 0L;573return firstLayerRow[index - (firstIndex * (1 << shift))];574}575576public void put(final int index, final float value) {577final int firstIndex = index >> shift;578float[] firstLayerRow = cache[firstIndex];579if (firstLayerRow == null) {580cache[firstIndex] = firstLayerRow =581new float[secondLayerLength];582}583firstLayerRow[index - (firstIndex * (1 << shift))] = value;584}585}586}587}588589590