| /* | |
| * 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. | |
| * | |
| */ | |
| /* | |
| * | |
| * (C) Copyright IBM Corp. 2003 - All Rights Reserved | |
| */ | |
| package sun.font; | |
| import sun.font.GlyphLayout.*; | |
| import sun.java2d.Disposer; | |
| import sun.java2d.DisposerRecord; | |
| import java.awt.geom.Point2D; | |
| import java.lang.foreign.MemorySegment; | |
| import java.lang.ref.SoftReference; | |
| import java.util.concurrent.ConcurrentHashMap; | |
| import java.util.WeakHashMap; | |
| /* | |
| * different ways to do this | |
| * 1) each physical font2d keeps a hashtable mapping scripts to layout | |
| * engines, we query and fill this cache. | |
| * 2) we keep a mapping independent of font using the key Most likely | |
| * few fonts will be used, so option 2 seems better | |
| * | |
| * Once we know which engine to use for a font, we always know, so we | |
| * shouldn't have to recheck each time we do layout. So the cache is | |
| * ok. | |
| * | |
| * Should we reuse engines? We could instantiate an engine for each | |
| * font/script pair. The engine would hold onto the table(s) from the | |
| * font that it needs. If we have multiple threads using the same | |
| * engine, we still need to keep the state separate, so the native | |
| * engines would still need to be allocated for each call, since they | |
| * keep their state in themselves. If they used the passed-in GVData | |
| * arrays directly (with some checks for space) then since each GVData | |
| * is different per thread, we could reuse the layout engines. This | |
| * still requires a separate layout engine per font, because of the | |
| * table state in the engine. If we pushed that out too and passed it | |
| * in with the native call as well, we'd be ok if the layout engines | |
| * keep all their process state on the stack, but I don't know if this | |
| * is true. Then we'd basically just be down to an engine index which | |
| * we pass into native and then invoke the engine code (now a | |
| * procedure call, not an object invocation) based on a switch on the | |
| * index. There would be only half a dozen engine objects then, not | |
| * potentially half a dozen per font. But we'd have to stack-allocate | |
| * some state that included the pointer to the required font tables. | |
| * | |
| * Seems for now that the way to do things is to come in with a | |
| * selector and the font. The selector indicates which engine to use, | |
| * the engine is stack allocated and initialized with the required | |
| * font tables (the selector indicates which). Then layout is called, | |
| * the contents are copied (or not), and the stack is destroyed on | |
| * exit. So the association is between the font/script (layout engine | |
| * desc) and one of a few permanent engine objects, which are | |
| * handed the key when they need to process something. In the native | |
| * case, the engine holds an index, and just passes it together with | |
| * the key info down to native. Some default cases are the 'default | |
| * layout' case that just runs the c2gmapper, this stays in java and | |
| * just uses the mapper from the font/strike. Another default case | |
| * might be the unicode arabic shaper, since this doesn't care about | |
| * the font (or script or lang?) it wouldn't need to extract this | |
| * data. It could be (yikes) ported back to java even to avoid | |
| * upcalls to check if the font supports a particular unicode | |
| * character. | |
| * | |
| * I'd expect that the majority of scripts use the default mapper for | |
| * a particular font. Loading the hashtable with 40 or so keys 30+ of | |
| * which all map to the same object is unfortunate. It might be worth | |
| * instead having a per-font list of 'scripts with non-default | |
| * engines', e.g. the factory has a hashtable mapping fonts to 'script | |
| * lists' (the factory has this since the design potentially has other | |
| * factories, though I admit there's no client for this yet and no | |
| * public api) and then the script list is queried for the script in | |
| * question. it can be preloaded at creation time with all the | |
| * scripts that don't have default engines-- either a list or a hash | |
| * table, so a null return from the table means 'default' and not 'i | |
| * don't know yet'. | |
| * | |
| * On the other hand, in most all cases the number of unique | |
| * script/font combinations will be small, so a flat hashtable should | |
| * suffice. | |
| * */ | |
| public final class SunLayoutEngine implements LayoutEngine, LayoutEngineFactory { | |
| static { | |
| FontManagerNativeLibrary.load(); | |
| } | |
| private LayoutEngineKey key; | |
| private static LayoutEngineFactory instance; | |
| public static LayoutEngineFactory instance() { | |
| if (instance == null) { | |
| instance = new SunLayoutEngine(); | |
| } | |
| return instance; | |
| } | |
| private SunLayoutEngine() { | |
| // actually a factory, key is null so layout cannot be called on it | |
| } | |
| public LayoutEngine getEngine(Font2D font, int script, int lang) { | |
| return getEngine(new LayoutEngineKey(font, script, lang)); | |
| } | |
| // !!! don't need this unless we have more than one sun layout engine... | |
| public LayoutEngine getEngine(LayoutEngineKey key) { | |
| ConcurrentHashMap<LayoutEngineKey, LayoutEngine> cache = cacheref.get(); | |
| if (cache == null) { | |
| cache = new ConcurrentHashMap<>(); | |
| cacheref = new SoftReference<>(cache); | |
| } | |
| LayoutEngine e = cache.get(key); | |
| if (e == null) { | |
| LayoutEngineKey copy = key.copy(); | |
| e = new SunLayoutEngine(copy); | |
| cache.put(copy, e); | |
| } | |
| return e; | |
| } | |
| private SoftReference<ConcurrentHashMap<LayoutEngineKey, LayoutEngine>> cacheref = | |
| new SoftReference<>(null); | |
| private SunLayoutEngine(LayoutEngineKey key) { | |
| this.key = key; | |
| } | |
| private static final WeakHashMap<Font2D, FaceRef> facePtr = | |
| new WeakHashMap<>(); | |
| private long getFacePtr(Font2D font2D) { | |
| FaceRef ref; | |
| synchronized (facePtr) { | |
| ref = facePtr.computeIfAbsent(font2D, FaceRef::new); | |
| } | |
| return ref.getNativePtr(); | |
| } | |
| static boolean useFFM = true; | |
| static { | |
| String prop = System.getProperty("sun.font.layout.ffm", "true"); | |
| useFFM = "true".equals(prop); | |
| } | |
| public void layout(FontStrikeDesc desc, float[] mat, float ptSize, int gmask, | |
| int baseIndex, TextRecord tr, int typo_flags, | |
| Point2D.Float pt, GVData data) { | |
| Font2D font = key.font(); | |
| FontStrike strike = font.getStrike(desc); | |
| if (useFFM) { | |
| MemorySegment face = HBShaper.getFace(font); | |
| if (face != null) { | |
| HBShaper.shape(font, strike, ptSize, mat, face, | |
| tr.text, data, key.script(), | |
| tr.start, tr.limit, baseIndex, pt, | |
| typo_flags, gmask); | |
| } | |
| } else { | |
| long pFace = getFacePtr(font); | |
| if (pFace != 0) { | |
| shape(font, strike, ptSize, mat, pFace, | |
| tr.text, data, key.script(), | |
| tr.start, tr.limit, baseIndex, pt, | |
| typo_flags, gmask); | |
| } | |
| } | |
| } | |
| /* Native method to invoke harfbuzz layout engine */ | |
| private static native boolean | |
| shape(Font2D font, FontStrike strike, float ptSize, float[] mat, | |
| long pFace, | |
| char[] chars, GVData data, | |
| int script, int offset, int limit, | |
| int baseIndex, Point2D.Float pt, int typo_flags, int slot); | |
| private static native long createFace(Font2D font, | |
| long platformNativeFontPtr); | |
| private static native void disposeFace(long facePtr); | |
| private static class FaceRef implements DisposerRecord { | |
| private Font2D font; | |
| private Long facePtr; | |
| private FaceRef(Font2D font) { | |
| this.font = font; | |
| } | |
| private synchronized long getNativePtr() { | |
| if (facePtr == null) { | |
| facePtr = createFace(font, | |
| font.getPlatformNativeFontPtr()); | |
| if (facePtr != 0) { | |
| Disposer.addObjectRecord(font, this); | |
| } | |
| font = null; | |
| } | |
| return facePtr; | |
| } | |
| @Override | |
| public void dispose() { | |
| disposeFace(facePtr); | |
| } | |
| } | |
| } |