Path: blob/master/src/java.desktop/macosx/classes/sun/font/CStrike.java
41153 views
/*1* Copyright (c) 2011, 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.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}128129@SuppressWarnings("deprecation")130protected synchronized void finalize() throws Throwable {131if (nativeStrikePtr != 0) {132disposeNativeStrikePtr(nativeStrikePtr);133}134nativeStrikePtr = 0;135}136137138@Override139public int getNumGlyphs() {140return nativeFont.getNumGlyphs();141}142143@Override144StrikeMetrics getFontMetrics() {145if (strikeMetrics == null) {146StrikeMetrics metrics = getFontMetrics(getNativeStrikePtr());147if (invDevTx != null) {148metrics.convertToUserSpace(invDevTx);149}150metrics.convertToUserSpace(desc.glyphTx);151strikeMetrics = metrics;152}153return strikeMetrics;154}155156@Override157float getGlyphAdvance(final int glyphCode) {158return getCachedNativeGlyphAdvance(glyphCode);159}160161@Override162float getCodePointAdvance(final int cp) {163return getGlyphAdvance(nativeFont.getMapper().charToGlyph(cp));164}165166@Override167Point2D.Float getCharMetrics(final char ch) {168return getGlyphMetrics(nativeFont.getMapper().charToGlyph(ch));169}170171@Override172Point2D.Float getGlyphMetrics(final int glyphCode) {173return new Point2D.Float(getGlyphAdvance(glyphCode), 0.0f);174}175176Rectangle2D.Float getGlyphOutlineBounds(int glyphCode) {177GeneralPath gp = getGlyphOutline(glyphCode, 0f, 0f);178Rectangle2D r2d = gp.getBounds2D();179Rectangle2D.Float r2df;180if (r2d instanceof Rectangle2D.Float) {181r2df = (Rectangle2D.Float)r2d;182} else {183float x = (float)r2d.getX();184float y = (float)r2d.getY();185float w = (float)r2d.getWidth();186float h = (float)r2d.getHeight();187r2df = new Rectangle2D.Float(x, y, w, h);188}189return r2df;190}191192// pt, result in device space193void getGlyphImageBounds(int glyphCode, Point2D.Float pt, Rectangle result) {194Rectangle2D.Float floatRect = new Rectangle2D.Float();195196if (invDevTx != null) {197invDevTx.transform(pt, pt);198}199200getGlyphImageBounds(glyphCode, pt.x, pt.y, floatRect);201202if (floatRect.width == 0 && floatRect.height == 0) {203result.setRect(0, 0, -1, -1);204return;205}206207result.setRect(floatRect.x + pt.x, floatRect.y + pt.y, floatRect.width, floatRect.height);208}209210private void getGlyphImageBounds(int glyphCode, float x, float y, Rectangle2D.Float floatRect) {211getNativeGlyphImageBounds(getNativeStrikePtr(), glyphCode, floatRect, x, y);212}213214GeneralPath getGlyphOutline(int glyphCode, float x, float y) {215return getNativeGlyphOutline(getNativeStrikePtr(), glyphCode, x, y);216}217218// should implement, however not called though any path that is publicly exposed219GeneralPath getGlyphVectorOutline(int[] glyphs, float x, float y) {220throw new Error("not implemented yet");221}222223// called from the Sun2D renderer224long getGlyphImagePtr(int glyphCode) {225synchronized (glyphInfoCache) {226long ptr = glyphInfoCache.get(glyphCode);227if (ptr != 0L) return ptr;228229long[] ptrs = new long[1];230int[] codes = new int[1];231codes[0] = glyphCode;232233getGlyphImagePtrs(codes, ptrs, 1);234235ptr = ptrs[0];236glyphInfoCache.put(glyphCode, ptr);237238return ptr;239}240}241242// called from the Sun2D renderer243void getGlyphImagePtrs(int[] glyphCodes, long[] images, int len) {244synchronized (glyphInfoCache) {245// fill the image pointer array with existing pointers246// from the cache247int missed = 0;248for (int i = 0; i < len; i++) {249int code = glyphCodes[i];250251final long ptr = glyphInfoCache.get(code);252if (ptr != 0L) {253images[i] = ptr;254} else {255// zero this element out, because the caller does not256// promise to keep it clean257images[i] = 0L;258missed++;259}260}261262if (missed == 0) {263return; // horray! we got away without touching native!264}265266// all distinct glyph codes requested (partially filled)267final int[] filteredCodes = new int[missed];268// indices into filteredCodes array (totally filled)269final int[] filteredIndicies = new int[missed];270271// scan, mark, and store the requested glyph codes again to272// send into native273int j = 0;274int dupes = 0;275for (int i = 0; i < len; i++) {276if (images[i] != 0L) continue; // already filled277278final int code = glyphCodes[i];279280// we have already promised to strike this glyph - this is281// a dupe282if (glyphInfoCache.get(code) == -1L) {283filteredIndicies[j] = -1;284dupes++;285j++;286continue;287}288289// this is a distinct glyph we have not struck before, or290// promised to strike mark this one as "promise to strike"291// in the global cache with a -1L292final int k = j - dupes;293filteredCodes[k] = code;294glyphInfoCache.put(code, -1L);295filteredIndicies[j] = k;296j++;297}298299final int filteredRunLen = j - dupes;300final long[] filteredImages = new long[filteredRunLen];301302// bulk call to fill in the distinct glyph pointers from native303getFilteredGlyphImagePtrs(filteredImages, filteredCodes, filteredRunLen);304305// scan the requested glyph list, and fill in pointers from our306// distinct glyph list which has been filled from native307j = 0;308for (int i = 0; i < len; i++) {309if (images[i] != 0L && images[i] != -1L) {310continue; // already placed311}312313// index into filteredImages array314final int k = filteredIndicies[j];315final int code = glyphCodes[i];316if (k == -1L) {317// we should have already filled the cache with this pointer318images[i] = glyphInfoCache.get(code);319} else {320// fill the particular glyph code request, and store321// in the cache322final long ptr = filteredImages[k];323images[i] = ptr;324glyphInfoCache.put(code, ptr);325}326327j++;328}329}330}331332private void getFilteredGlyphImagePtrs(long[] glyphInfos,333int[] uniCodes, int len)334{335getGlyphImagePtrsNative(getNativeStrikePtr(), glyphInfos, uniCodes, len);336}337338private float getCachedNativeGlyphAdvance(int glyphCode) {339synchronized(glyphAdvanceCache) {340float advance = glyphAdvanceCache.get(glyphCode);341if (advance != 0) {342return advance;343}344345advance = getNativeGlyphAdvance(getNativeStrikePtr(), glyphCode);346glyphAdvanceCache.put(glyphCode, advance);347return advance;348}349}350351// This class stores glyph pointers, and is indexed based on glyph codes,352// and negative unicode values. See the comments in353// CCharToGlyphMapper for more details on our glyph code strategy.354private static class GlyphInfoCache extends CStrikeDisposer {355private static final int FIRST_LAYER_SIZE = 256;356private static final int SECOND_LAYER_SIZE = 16384; // 16384 = 128x128357358// rdar://problem/5204197359private boolean disposed = false;360361private final long[] firstLayerCache;362private SparseBitShiftingTwoLayerArray secondLayerCache;363private HashMap<Integer, Long> generalCache;364365GlyphInfoCache(final Font2D nativeFont, final FontStrikeDesc desc) {366super(nativeFont, desc);367firstLayerCache = new long[FIRST_LAYER_SIZE];368}369370public synchronized long get(final int index) {371if (index < 0) {372if (-index < SECOND_LAYER_SIZE) {373// catch common unicodes374if (secondLayerCache == null) {375return 0L;376}377return secondLayerCache.get(-index);378}379} else {380if (index < FIRST_LAYER_SIZE) {381// catch common glyphcodes382return firstLayerCache[index];383}384}385386if (generalCache == null) {387return 0L;388}389final Long value = generalCache.get(Integer.valueOf(index));390if (value == null) {391return 0L;392}393return value.longValue();394}395396public synchronized void put(final int index, final long value) {397if (index < 0) {398if (-index < SECOND_LAYER_SIZE) {399// catch common unicodes400if (secondLayerCache == null) {401secondLayerCache = new SparseBitShiftingTwoLayerArray(SECOND_LAYER_SIZE, 7); // 128x128402}403secondLayerCache.put(-index, value);404return;405}406} else {407if (index < FIRST_LAYER_SIZE) {408// catch common glyphcodes409firstLayerCache[index] = value;410return;411}412}413414if (generalCache == null) {415generalCache = new HashMap<Integer, Long>();416}417418generalCache.put(Integer.valueOf(index), Long.valueOf(value));419}420421public synchronized void dispose() {422// rdar://problem/5204197423// Note that sun.font.Font2D.getStrike() actively disposes424// cleared strikeRef. We need to check the disposed flag to425// prevent double frees of native resources.426if (disposed) {427return;428}429430super.dispose();431432// clean out the first array433disposeLongArray(firstLayerCache);434435// clean out the two layer arrays436if (secondLayerCache != null) {437final long[][] secondLayerLongArrayArray = secondLayerCache.cache;438for (int i = 0; i < secondLayerLongArrayArray.length; i++) {439final long[] longArray = secondLayerLongArrayArray[i];440if (longArray != null) disposeLongArray(longArray);441}442}443444// clean up everyone else445if (generalCache != null) {446final Iterator<Long> i = generalCache.values().iterator();447while (i.hasNext()) {448final long longValue = i.next().longValue();449if (longValue != -1 && longValue != 0) {450removeGlyphInfoFromCache(longValue);451StrikeCache.freeLongPointer(longValue);452}453}454}455456// rdar://problem/5204197457// Finally, set the flag.458disposed = true;459}460461private static void disposeLongArray(final long[] longArray) {462for (int i = 0; i < longArray.length; i++) {463final long ptr = longArray[i];464if (ptr != 0 && ptr != -1) {465removeGlyphInfoFromCache(ptr);466StrikeCache.freeLongPointer(ptr); // free's the native struct pointer467}468}469}470471private static class SparseBitShiftingTwoLayerArray {472final long[][] cache;473final int shift;474final int secondLayerLength;475476SparseBitShiftingTwoLayerArray(final int size, final int shift) {477this.shift = shift;478this.cache = new long[1 << shift][];479this.secondLayerLength = size >> shift;480}481482public long get(final int index) {483final int firstIndex = index >> shift;484final long[] firstLayerRow = cache[firstIndex];485if (firstLayerRow == null) return 0L;486return firstLayerRow[index - (firstIndex * (1 << shift))];487}488489public void put(final int index, final long value) {490final int firstIndex = index >> shift;491long[] firstLayerRow = cache[firstIndex];492if (firstLayerRow == null) {493cache[firstIndex] = firstLayerRow = new long[secondLayerLength];494}495firstLayerRow[index - (firstIndex * (1 << shift))] = value;496}497}498}499500private static class GlyphAdvanceCache {501private static final int FIRST_LAYER_SIZE = 256;502private static final int SECOND_LAYER_SIZE = 16384; // 16384 = 128x128503504private final float[] firstLayerCache = new float[FIRST_LAYER_SIZE];505private SparseBitShiftingTwoLayerArray secondLayerCache;506private HashMap<Integer, Float> generalCache;507508public synchronized float get(final int index) {509if (index < 0) {510if (-index < SECOND_LAYER_SIZE) {511// catch common unicodes512if (secondLayerCache == null) return 0;513return secondLayerCache.get(-index);514}515} else {516if (index < FIRST_LAYER_SIZE) {517// catch common glyphcodes518return firstLayerCache[index];519}520}521522if (generalCache == null) return 0;523final Float value = generalCache.get(Integer.valueOf(index));524if (value == null) return 0;525return value.floatValue();526}527528public synchronized void put(final int index, final float value) {529if (index < 0) {530if (-index < SECOND_LAYER_SIZE) {531// catch common unicodes532if (secondLayerCache == null) {533secondLayerCache = new SparseBitShiftingTwoLayerArray(SECOND_LAYER_SIZE, 7); // 128x128534}535secondLayerCache.put(-index, value);536return;537}538} else {539if (index < FIRST_LAYER_SIZE) {540// catch common glyphcodes541firstLayerCache[index] = value;542return;543}544}545546if (generalCache == null) {547generalCache = new HashMap<Integer, Float>();548}549550generalCache.put(Integer.valueOf(index), Float.valueOf(value));551}552553private static class SparseBitShiftingTwoLayerArray {554final float[][] cache;555final int shift;556final int secondLayerLength;557558SparseBitShiftingTwoLayerArray(final int size, final int shift) {559this.shift = shift;560this.cache = new float[1 << shift][];561this.secondLayerLength = size >> shift;562}563564public float get(final int index) {565final int firstIndex = index >> shift;566final float[] firstLayerRow = cache[firstIndex];567if (firstLayerRow == null) return 0L;568return firstLayerRow[index - (firstIndex * (1 << shift))];569}570571public void put(final int index, final float value) {572final int firstIndex = index >> shift;573float[] firstLayerRow = cache[firstIndex];574if (firstLayerRow == null) {575cache[firstIndex] = firstLayerRow =576new float[secondLayerLength];577}578firstLayerRow[index - (firstIndex * (1 << shift))] = value;579}580}581}582}583584585