8220231: Cache HarfBuzz face object for same font's text layout calls

Reviewed-by: prr, avu, serb
This commit is contained in:
Dmitry Batrak 2019-04-11 10:49:36 -07:00
parent a9bfe45ca8
commit b9f32c8148
16 changed files with 242 additions and 272 deletions

@ -77,18 +77,11 @@ public final class CFont extends PhysicalFont implements FontSubstitution {
throw new InternalError("Not implemented");
}
@Override
protected long getLayoutTableCache() {
return getLayoutTableCacheNative(getNativeFontPtr());
}
@Override
protected byte[] getTableBytes(int tag) {
return getTableBytesNative(getNativeFontPtr(), tag);
}
private native synchronized long getLayoutTableCacheNative(long nativeFontPtr);
private native byte[] getTableBytesNative(long nativeFontPtr, int tag);
private static native long createNativeFont(final String nativeFontName,

@ -26,8 +26,6 @@
#import <Cocoa/Cocoa.h>
#import <JavaRuntimeSupport/JavaRuntimeSupport.h>
#import "fontscalerdefs.h"
#define MAX_STACK_ALLOC_GLYPH_BUFFER_SIZE 256
@interface AWTFont : NSObject {
@ -35,7 +33,6 @@
NSFont *fFont;
CGFontRef fNativeCGFont;
BOOL fIsFakeItalic;
TTLayoutTableCache* layoutTableCache;
}
+ (AWTFont *) awtFontForName:(NSString *)name

@ -42,33 +42,10 @@
if (self) {
fFont = [font retain];
fNativeCGFont = CTFontCopyGraphicsFont((CTFontRef)font, NULL);
layoutTableCache = NULL;
}
return self;
}
static TTLayoutTableCache* newCFontLayoutTableCache() {
TTLayoutTableCache* ltc = calloc(1, sizeof(TTLayoutTableCache));
if (ltc) {
int i;
for(i=0;i<LAYOUTCACHE_ENTRIES;i++) {
ltc->entries[i].len = -1;
}
}
return ltc;
}
static void freeCFontLayoutTableCache(TTLayoutTableCache* ltc) {
if (ltc) {
int i;
for(i=0;i<LAYOUTCACHE_ENTRIES;i++) {
if(ltc->entries[i].ptr) free (ltc->entries[i].ptr);
}
if (ltc->kernPairs) free(ltc->kernPairs);
free(ltc);
}
}
- (void) dealloc {
[fFont release];
fFont = nil;
@ -76,10 +53,6 @@ static void freeCFontLayoutTableCache(TTLayoutTableCache* ltc) {
if (fNativeCGFont) {
CGFontRelease(fNativeCGFont);
fNativeCGFont = NULL;
if (layoutTableCache != NULL) {
freeCFontLayoutTableCache(layoutTableCache);
layoutTableCache = NULL;
}
}
[super dealloc];
@ -90,10 +63,6 @@ static void freeCFontLayoutTableCache(TTLayoutTableCache* ltc) {
CGFontRelease(fNativeCGFont);
fNativeCGFont = NULL;
}
if (layoutTableCache != NULL) {
freeCFontLayoutTableCache(layoutTableCache);
layoutTableCache = NULL;
}
[super finalize];
}
@ -431,23 +400,6 @@ Java_sun_font_CFont_getCGFontPtrNative
return (jlong)(awtFont->fNativeCGFont);
}
/*
* Class: sun_font_CFont
* Method: getLayoutTableCacheNative
* Signature: (J)J
*/
JNIEXPORT jlong JNICALL
Java_sun_font_CFont_getLayoutTableCacheNative
(JNIEnv *env, jclass clazz,
jlong awtFontPtr)
{
AWTFont *awtFont = (AWTFont *)jlong_to_ptr(awtFontPtr);
if (awtFont->layoutTableCache == NULL) {
awtFont->layoutTableCache = newCFontLayoutTableCache();
}
return (jlong)(awtFont->layoutTableCache);
}
/*
* Class: sun_font_CFont
* Method: getTableBytesNative

@ -465,13 +465,6 @@ public abstract class Font2D {
return null;
}
/* implemented for fonts backed by an sfnt that has
* OpenType or AAT layout tables.
*/
protected long getLayoutTableCache() {
return 0L;
}
/* Used only on OS X.
*/
protected long getPlatformNativeFontPtr() {

@ -181,25 +181,6 @@ public abstract class FontScaler implements DisposerRecord {
abstract int getMissingGlyphCode() throws FontScalerException;
abstract int getGlyphCode(char charCode) throws FontScalerException;
/* This method returns table cache used by native layout engine.
* This cache is essentially just small collection of
* pointers to various truetype tables. See definition of TTLayoutTableCache
* in the fontscalerdefs.h for more details.
*
* Note that tables themselves have same format as defined in the truetype
* specification, i.e. font scaler do not need to perform any preprocessing.
*
* Probably it is better to have API to request pointers to each table
* separately instead of requesting pointer to some native structure.
* (then there is not need to share its definition by different
* implementations of scaler).
* However, this means multiple JNI calls and potential impact on performance.
*
* Note: return value 0 is legal.
* This means tables are not available (e.g. type1 font).
*/
abstract long getLayoutTableCache() throws FontScalerException;
/* Used by the OpenType engine for mark positioning. */
abstract Point2D.Float getGlyphPoint(long pScalerContext,
int glyphCode, int ptNumber)

@ -163,10 +163,6 @@ class FreetypeFontScaler extends FontScaler {
.getNullScaler().getGlyphVectorOutline(0L, glyphs, numGlyphs, x, y);
}
synchronized long getLayoutTableCache() throws FontScalerException {
return getLayoutTableCacheNative(nativeScaler);
}
public synchronized void dispose() {
if (nativeScaler != 0L) {
disposeNativeScaler(font.get(), nativeScaler);
@ -243,8 +239,6 @@ class FreetypeFontScaler extends FontScaler {
native Point2D.Float getGlyphPointNative(Font2D font,
long pScalerContext, long pScaler, int glyphCode, int ptNumber);
private native long getLayoutTableCacheNative(long pScaler);
private native void disposeNativeScaler(Font2D font2D, long pScaler);
private native int getGlyphCodeNative(Font2D font, long pScaler, char charCode);

@ -64,8 +64,6 @@ class NullFontScaler extends FontScaler {
return new GeneralPath();
}
long getLayoutTableCache() {return 0L;}
long createScalerContext(double[] matrix, int aa,
int fm, float boldness, float italic, boolean disableHinting) {
return getNullScalerContext();

@ -31,10 +31,12 @@
package sun.font;
import sun.font.GlyphLayout.*;
import sun.java2d.Disposer;
import sun.java2d.DisposerRecord;
import java.awt.geom.Point2D;
import java.lang.ref.SoftReference;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Locale;
import java.util.WeakHashMap;
/*
@ -150,8 +152,10 @@ public final class SunLayoutEngine implements LayoutEngine, LayoutEngineFactory
}
static WeakHashMap<Font2D, Boolean> aatInfo = new WeakHashMap<>();
private static final WeakHashMap<Font2D, FaceRef> facePtr =
new WeakHashMap<>();
private boolean isAAT(Font2D font) {
private static boolean isAAT(Font2D font) {
Boolean aatObj;
synchronized (aatInfo) {
aatObj = aatInfo.get(font);
@ -175,30 +179,67 @@ public final class SunLayoutEngine implements LayoutEngine, LayoutEngineFactory
return aat;
}
private long getFacePtr(Font2D font2D) {
FaceRef ref;
synchronized (facePtr) {
ref = facePtr.computeIfAbsent(font2D, FaceRef::new);
}
return ref.getNativePtr();
}
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);
long layoutTables = font.getLayoutTableCache();
long pNativeFont = font.getPlatformNativeFontPtr(); // used on OSX
// pScaler probably not needed long term.
long pScaler = 0L;
if (font instanceof FileFont) {
pScaler = ((FileFont)font).getScaler().nativeScaler;
long pFace = getFacePtr(font);
if (pFace != 0) {
shape(font, strike, ptSize, mat, pNativeFont,
pFace, isAAT(font),
tr.text, data, key.script(),
tr.start, tr.limit, baseIndex, pt,
typo_flags, gmask);
}
shape(font, strike, ptSize, mat, pScaler, pNativeFont,
layoutTables, isAAT(font),
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 pscaler, long pNativeFont, long layoutTables, boolean aat,
long pNativeFont, long pFace, boolean aat,
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,
boolean aat,
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, isAAT(font),
font.getPlatformNativeFontPtr());
if (facePtr != 0) {
Disposer.addObjectRecord(font, this);
}
font = null;
}
return facePtr;
}
@Override
public void dispose() {
disposeFace(facePtr);
}
}
}

@ -896,15 +896,6 @@ public class TrueTypeFont extends FileFont {
}
}
@Override
protected long getLayoutTableCache() {
try {
return getScaler().getLayoutTableCache();
} catch(FontScalerException fe) {
return 0L;
}
}
@Override
protected byte[] getTableBytes(int tag) {
ByteBuffer buffer = getTableBuffer(tag);

@ -88,32 +88,8 @@ typedef struct GlyphInfo {
*/
#define INVISIBLE_GLYPHS 0xfffe
#define GSUB_TAG 0x47535542 /* 'GSUB' */
#define GPOS_TAG 0x47504F53 /* 'GPOS' */
#define GDEF_TAG 0x47444546 /* 'GDEF' */
#define HEAD_TAG 0x68656164 /* 'head' */
#define MORT_TAG 0x6D6F7274 /* 'mort' */
#define MORX_TAG 0x6D6F7278 /* 'morx' */
#define KERN_TAG 0x6B65726E /* 'kern' */
typedef struct TTLayoutTableCacheEntry {
const void* ptr;
int len;
int tag;
} TTLayoutTableCacheEntry;
#define LAYOUTCACHE_ENTRIES 7
typedef struct TTLayoutTableCache {
TTLayoutTableCacheEntry entries[LAYOUTCACHE_ENTRIES];
void* kernPairs;
} TTLayoutTableCache;
#include "sunfontids.h"
JNIEXPORT extern TTLayoutTableCache* newLayoutTableCache();
JNIEXPORT extern void freeLayoutTableCache(TTLayoutTableCache* ltc);
/* If font is malformed then scaler context created by particular scaler
* will be replaced by null scaler context.
* Note that this context is not compatible with structure of the context

@ -201,9 +201,7 @@ JDKFontInfo*
jobject font2D,
jobject fontStrike,
jfloat ptSize,
jlong pScaler,
jlong pNativeFont,
jlong layoutTables,
jfloatArray matrix,
jboolean aat) {
@ -216,7 +214,6 @@ JDKFontInfo*
fi->font2D = font2D;
fi->fontStrike = fontStrike;
fi->nativeFont = pNativeFont;
fi->layoutTables = (TTLayoutTableCache*)layoutTables;
fi->aat = aat;
(*env)->GetFloatArrayRegion(env, matrix, 0, 4, fi->matrix);
fi->ptSize = ptSize;
@ -241,9 +238,8 @@ JNIEXPORT jboolean JNICALL Java_sun_font_SunLayoutEngine_shape
jobject fontStrike,
jfloat ptSize,
jfloatArray matrix,
jlong pScaler,
jlong pNativeFont,
jlong layoutTables,
jlong pFace,
jboolean aat,
jcharArray text,
jobject gvdata,
@ -256,6 +252,7 @@ JNIEXPORT jboolean JNICALL Java_sun_font_SunLayoutEngine_shape
jint slot) {
hb_buffer_t *buffer;
hb_face_t* hbface;
hb_font_t* hbfont;
jchar *chars;
jsize len;
@ -272,7 +269,7 @@ JNIEXPORT jboolean JNICALL Java_sun_font_SunLayoutEngine_shape
JDKFontInfo *jdkFontInfo =
createJDKFontInfo(env, font2D, fontStrike, ptSize,
pScaler, pNativeFont, layoutTables, matrix, aat);
pNativeFont, matrix, aat);
if (!jdkFontInfo) {
return JNI_FALSE;
}
@ -280,7 +277,8 @@ JNIEXPORT jboolean JNICALL Java_sun_font_SunLayoutEngine_shape
jdkFontInfo->font2D = font2D;
jdkFontInfo->fontStrike = fontStrike;
hbfont = hb_jdk_font_create(jdkFontInfo, NULL);
hbface = (hb_face_t*) jlong_to_ptr(pFace);
hbfont = hb_jdk_font_create(hbface, jdkFontInfo, NULL);
buffer = hb_buffer_create();
hb_buffer_set_script(buffer, getHBScriptCode(script));

@ -69,7 +69,6 @@ typedef struct {
unsigned fontDataOffset;
unsigned fontDataLength;
unsigned fileSize;
TTLayoutTableCache* layoutTables;
} FTScalerInfo;
typedef struct FTScalerContext {
@ -251,7 +250,6 @@ Java_sun_font_FreetypeFontScaler_initNativeScaler(
if (type == TYPE1_FROM_JAVA) { /* TYPE1 */
scalerInfo->fontData = (unsigned char*) malloc(filesize);
scalerInfo->directBuffer = NULL;
scalerInfo->layoutTables = NULL;
scalerInfo->fontDataLength = filesize;
if (scalerInfo->fontData != NULL) {
@ -866,32 +864,6 @@ Java_sun_font_FreetypeFontScaler_getGlyphImageNative(
return ptr_to_jlong(glyphInfo);
}
/*
* Class: sun_font_FreetypeFontScaler
* Method: getLayoutTableCacheNative
* Signature: (J)J
*/
JNIEXPORT jlong JNICALL
Java_sun_font_FreetypeFontScaler_getLayoutTableCacheNative(
JNIEnv *env, jobject scaler, jlong pScaler) {
FTScalerInfo *scalerInfo = (FTScalerInfo*) jlong_to_ptr(pScaler);
if (scalerInfo == NULL) {
invalidateJavaScaler(env, scaler, scalerInfo);
return 0L;
}
// init layout table cache in font
// we're assuming the font is a file font and moreover it is Truetype font
// otherwise we shouldn't be able to get here...
if (scalerInfo->layoutTables == NULL) {
scalerInfo->layoutTables = newLayoutTableCache();
}
return ptr_to_jlong(scalerInfo->layoutTables);
}
/*
* Class: sun_font_FreetypeFontScaler
* Method: disposeNativeScaler

@ -23,6 +23,9 @@
* questions.
*/
#include "jlong.h"
#include "sun_font_SunLayoutEngine.h"
#include "hb.h"
#include "hb-jdk.h"
#ifdef MACOSX
@ -304,79 +307,113 @@ static void _do_nothing(void) {
static void _free_nothing(void*) {
}
struct Font2DPtr {
JavaVM* vmPtr;
jweak font2DRef;
};
static void cleanupFontInfo(void* data) {
Font2DPtr* fontInfo;
JNIEnv* env;
fontInfo = (Font2DPtr*) data;
fontInfo->vmPtr->GetEnv((void**)&env, JNI_VERSION_1_1);
env->DeleteWeakGlobalRef(fontInfo->font2DRef);
free(data);
}
static hb_blob_t *
reference_table(hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) {
JDKFontInfo *jdkFontInfo = (JDKFontInfo*)user_data;
JNIEnv* env = jdkFontInfo->env;
jobject font2D = jdkFontInfo->font2D;
jsize length = 0;
void* buffer = NULL;
int cacheIdx = 0;
Font2DPtr *fontInfo;
JNIEnv* env;
jobject font2D;
jsize length;
void* buffer;
// HB_TAG_NONE is 0 and is used to get the whole font file.
// It is not expected to be needed for JDK.
if (tag == 0 || jdkFontInfo->layoutTables == NULL) {
if (tag == 0) {
return NULL;
}
for (cacheIdx=0; cacheIdx<LAYOUTCACHE_ENTRIES; cacheIdx++) {
if (tag == jdkFontInfo->layoutTables->entries[cacheIdx].tag) break;
fontInfo = (Font2DPtr*)user_data;
fontInfo->vmPtr->GetEnv((void**)&env, JNI_VERSION_1_1);
if (env == NULL) {
return NULL;
}
font2D = fontInfo->font2DRef;
if (cacheIdx < LAYOUTCACHE_ENTRIES) { // if found
if (jdkFontInfo->layoutTables->entries[cacheIdx].len != -1) {
length = jdkFontInfo->layoutTables->entries[cacheIdx].len;
buffer = (void*)jdkFontInfo->layoutTables->entries[cacheIdx].ptr;
}
}
if (buffer == NULL) {
jbyteArray tableBytes = (jbyteArray)
env->CallObjectMethod(font2D, sunFontIDs.getTableBytesMID, tag);
if (tableBytes == NULL) {
return NULL;
}
length = env->GetArrayLength(tableBytes);
buffer = calloc(length, sizeof(jbyte));
env->GetByteArrayRegion(tableBytes, 0, length, (jbyte*)buffer);
if (cacheIdx >= LAYOUTCACHE_ENTRIES) { // not a cacheable table
return hb_blob_create((const char *)buffer, length,
HB_MEMORY_MODE_WRITABLE,
buffer, free);
} else {
jdkFontInfo->layoutTables->entries[cacheIdx].len = length;
jdkFontInfo->layoutTables->entries[cacheIdx].ptr = buffer;
}
jbyteArray tableBytes = (jbyteArray)
env->CallObjectMethod(font2D, sunFontIDs.getTableBytesMID, tag);
if (tableBytes == NULL) {
return NULL;
}
length = env->GetArrayLength(tableBytes);
buffer = calloc(length, sizeof(jbyte));
env->GetByteArrayRegion(tableBytes, 0, length, (jbyte*)buffer);
return hb_blob_create((const char *)buffer, length,
HB_MEMORY_MODE_READONLY,
NULL, _free_nothing);
HB_MEMORY_MODE_WRITABLE,
buffer, free);
}
extern "C" {
hb_face_t*
hb_jdk_face_create(JDKFontInfo *jdkFontInfo,
hb_destroy_func_t destroy) {
hb_face_t *face =
hb_face_create_for_tables(reference_table, jdkFontInfo, destroy);
return face;
/*
* Class: sun_font_SunLayoutEngine
* Method: createFace
* Signature: (Lsun/font/Font2D;ZJJ)J
*/
JNIEXPORT jlong JNICALL Java_sun_font_SunLayoutEngine_createFace(JNIEnv *env,
jclass cls,
jobject font2D,
jboolean aat,
jlong platformFontPtr) {
#ifdef MACOSX
if (aat && platformFontPtr) {
hb_face_t *face = hb_coretext_face_create((CGFontRef)platformFontPtr);
return ptr_to_jlong(face);
}
#endif
Font2DPtr *fi = (Font2DPtr*)malloc(sizeof(Font2DPtr));
if (!fi) {
return 0;
}
JavaVM* vmPtr;
env->GetJavaVM(&vmPtr);
fi->vmPtr = vmPtr;
fi->font2DRef = env->NewWeakGlobalRef(font2D);
if (!fi->font2DRef) {
free(fi);
return 0;
}
hb_face_t *face = hb_face_create_for_tables(reference_table, fi,
cleanupFontInfo);
return ptr_to_jlong(face);
}
static hb_font_t* _hb_jdk_font_create(JDKFontInfo *jdkFontInfo,
/*
* Class: sun_font_SunLayoutEngine
* Method: disposeFace
* Signature: (J)V
*/
JNIEXPORT void JNICALL Java_sun_font_SunLayoutEngine_disposeFace(JNIEnv *env,
jclass cls,
jlong ptr) {
hb_face_t* face = (hb_face_t*) jlong_to_ptr(ptr);
hb_face_destroy(face);
}
} // extern "C"
static hb_font_t* _hb_jdk_font_create(hb_face_t* face,
JDKFontInfo *jdkFontInfo,
hb_destroy_func_t destroy) {
hb_font_t *font;
hb_face_t *face;
face = hb_jdk_face_create(jdkFontInfo, destroy);
font = hb_font_create(face);
hb_face_destroy (face);
hb_font_set_funcs (font,
_hb_jdk_get_font_funcs (),
jdkFontInfo, (hb_destroy_func_t) _do_nothing);
@ -387,17 +424,11 @@ static hb_font_t* _hb_jdk_font_create(JDKFontInfo *jdkFontInfo,
}
#ifdef MACOSX
static hb_font_t* _hb_jdk_ct_font_create(JDKFontInfo *jdkFontInfo) {
static hb_font_t* _hb_jdk_ct_font_create(hb_face_t* face,
JDKFontInfo *jdkFontInfo) {
hb_font_t *font = NULL;
hb_face_t *face = NULL;
if (jdkFontInfo->nativeFont == 0) {
return NULL;
}
face = hb_coretext_face_create((CGFontRef)(jdkFontInfo->nativeFont));
font = hb_font_create(face);
hb_face_destroy(face);
hb_font_set_scale(font,
HBFloatToFixed(jdkFontInfo->ptSize),
HBFloatToFixed(jdkFontInfo->ptSize));
@ -405,18 +436,13 @@ static hb_font_t* _hb_jdk_ct_font_create(JDKFontInfo *jdkFontInfo) {
}
#endif
hb_font_t* hb_jdk_font_create(JDKFontInfo *jdkFontInfo,
hb_font_t* hb_jdk_font_create(hb_face_t* hbFace,
JDKFontInfo *jdkFontInfo,
hb_destroy_func_t destroy) {
hb_font_t* font = NULL;
#ifdef MACOSX
if (jdkFontInfo->aat) {
font = _hb_jdk_ct_font_create(jdkFontInfo);
if (jdkFontInfo->aat && jdkFontInfo->nativeFont) {
return _hb_jdk_ct_font_create(hbFace, jdkFontInfo);
}
#endif
if (font == NULL) {
font = _hb_jdk_font_create(jdkFontInfo, destroy);
}
return font;
return _hb_jdk_font_create(hbFace, jdkFontInfo, destroy);
}

@ -29,7 +29,6 @@
#include "hb.h"
#include <jni.h>
#include <sunfontids.h>
#include <fontscalerdefs.h>
# ifdef __cplusplus
extern "C" {
@ -40,7 +39,6 @@ typedef struct JDKFontInfo_Struct {
jobject font2D;
jobject fontStrike;
long nativeFont;
TTLayoutTableCache *layoutTables;
float matrix[4];
float ptSize;
float xPtSize;
@ -65,7 +63,8 @@ hb_face_t *
hb_jdk_face_create(JDKFontInfo* jdkFontInfo,
hb_destroy_func_t destroy);
hb_font_t *
hb_jdk_font_create(JDKFontInfo* jdkFontInfo,
hb_jdk_font_create(hb_face_t* hbFace,
JDKFontInfo* jdkFontInfo,
hb_destroy_func_t destroy);

@ -344,32 +344,3 @@ Java_sun_font_StrikeCache_getGlyphCacheDescription
(*env)->ReleasePrimitiveArrayCritical(env, results, nresults, 0);
}
JNIEXPORT TTLayoutTableCache* newLayoutTableCache() {
TTLayoutTableCache* ltc = calloc(1, sizeof(TTLayoutTableCache));
if (ltc) {
int i;
for(i=0;i<LAYOUTCACHE_ENTRIES;i++) {
ltc->entries[i].len = -1;
}
ltc->entries[0].tag = GDEF_TAG;
ltc->entries[1].tag = GPOS_TAG;
ltc->entries[2].tag = GSUB_TAG;
ltc->entries[3].tag = HEAD_TAG;
ltc->entries[4].tag = KERN_TAG;
ltc->entries[5].tag = MORT_TAG;
ltc->entries[6].tag = MORX_TAG;
}
return ltc;
}
JNIEXPORT void freeLayoutTableCache(TTLayoutTableCache* ltc) {
if (ltc) {
int i;
for(i=0;i<LAYOUTCACHE_ENTRIES;i++) {
if(ltc->entries[i].ptr) free (ltc->entries[i].ptr);
}
if (ltc->kernPairs) free(ltc->kernPairs);
free(ltc);
}
}

@ -0,0 +1,88 @@
/*
* Copyright (C) 2019 JetBrains s.r.o.
* 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.
*
* 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.
*/
/*
* @test
* @bug 8220231
* @summary Cache HarfBuzz face object for same font's text layout calls
* @comment Test layout operations for the same font performed simultaneously
* from multiple threads
*/
import java.awt.Font;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.atomic.AtomicReference;
public class FontLayoutStressTest {
private static final int NUMBER_OF_THREADS =
Runtime.getRuntime().availableProcessors() * 2;
private static final long TIME_TO_RUN_NS = 1_000_000_000; // 1 second
private static final Font FONT = new Font(Font.SERIF, Font.PLAIN, 12);
private static final FontRenderContext FRC = new FontRenderContext(null,
false, false);
private static final char[] TEXT = "Lorem ipsum dolor sit amet, ..."
.toCharArray();
private static double doLayout() {
GlyphVector gv = FONT.layoutGlyphVector(FRC, TEXT, 0, TEXT.length,
Font.LAYOUT_LEFT_TO_RIGHT);
return gv.getGlyphPosition(gv.getNumGlyphs()).getX();
}
public static void main(String[] args) throws Throwable {
double expectedWidth = doLayout();
AtomicReference<Throwable> throwableRef = new AtomicReference<>();
CyclicBarrier barrier = new CyclicBarrier(NUMBER_OF_THREADS);
List<Thread> threads = new ArrayList<>();
for (int i = 0; i < NUMBER_OF_THREADS; i++) {
Thread thread = new Thread(() -> {
try {
barrier.await();
long timeToStop = System.nanoTime() + TIME_TO_RUN_NS;
while (System.nanoTime() < timeToStop) {
double width = doLayout();
if (width != expectedWidth) {
throw new RuntimeException(
"Unexpected layout result");
}
}
} catch (Throwable e) {
throwableRef.set(e);
}
});
threads.add(thread);
thread.start();
}
for (Thread thread : threads) {
thread.join();
}
Throwable throwable = throwableRef.get();
if (throwable != null) {
throw throwable;
}
}
}