8334495: Use FFM instead of jdk.internal.misc.Unsafe in java.desktop font implementation

Reviewed-by: jdv, dnguyen, achung
This commit is contained in:
Phil Race 2024-07-18 17:35:06 +00:00
parent bbc79a5e01
commit 02be7b8ddc
5 changed files with 223 additions and 189 deletions

View File

@ -315,8 +315,7 @@ public class FileFontStrike extends PhysicalStrike {
* work to the FM case.
*/
float advance = getGlyphAdvance(glyphCode, false);
StrikeCache.unsafe.putFloat(ptr + StrikeCache.xAdvanceOffset,
advance);
StrikeCache.setGlyphXAdvance(ptr, advance);
return ptr;
} else {
if (FontUtilities.isLogging()) {
@ -665,8 +664,7 @@ public class FileFontStrike extends PhysicalStrike {
glyphPtr = getCachedGlyphPtr(glyphCode);
}
if (glyphPtr != 0L) {
advance = StrikeCache.unsafe.getFloat
(glyphPtr + StrikeCache.xAdvanceOffset);
advance = StrikeCache.getGlyphXAdvance(glyphPtr);
} else {
advance = fileFont.getGlyphAdvance(pScalerContext, glyphCode);
@ -726,15 +724,13 @@ public class FileFontStrike extends PhysicalStrike {
return;
}
topLeftX = StrikeCache.unsafe.getFloat(ptr+StrikeCache.topLeftXOffset);
topLeftY = StrikeCache.unsafe.getFloat(ptr+StrikeCache.topLeftYOffset);
topLeftX = StrikeCache.getGlyphTopLeftX(ptr);
topLeftY = StrikeCache.getGlyphTopLeftY(ptr);
result.x = (int)Math.floor(pt.x + topLeftX + 0.5f);
result.y = (int)Math.floor(pt.y + topLeftY + 0.5f);
result.width =
StrikeCache.unsafe.getShort(ptr+StrikeCache.widthOffset) &0x0ffff;
result.height =
StrikeCache.unsafe.getShort(ptr+StrikeCache.heightOffset) &0x0ffff;
result.width = StrikeCache.getGlyphWidth(ptr) & 0x0ffff;
result.height = StrikeCache.getGlyphHeight(ptr) & 0x0ffff;
/* HRGB LCD text may have padding that is empty. This is almost always
* going to be when topLeftX is -2 or less.
@ -755,25 +751,22 @@ public class FileFontStrike extends PhysicalStrike {
private int getGlyphImageMinX(long ptr, int origMinX) {
int width = StrikeCache.unsafe.getChar(ptr+StrikeCache.widthOffset);
int height = StrikeCache.unsafe.getChar(ptr+StrikeCache.heightOffset);
int rowBytes =
StrikeCache.unsafe.getChar(ptr+StrikeCache.rowBytesOffset);
int width = StrikeCache.getGlyphWidth(ptr);
int height = StrikeCache.getGlyphHeight(ptr);
int rowBytes = StrikeCache.getGlyphRowBytes(ptr);
if (rowBytes == width) {
return origMinX;
}
long pixelData =
StrikeCache.unsafe.getAddress(ptr + StrikeCache.pixelDataOffset);
if (pixelData == 0L) {
if (StrikeCache.getGlyphImagePtr(ptr) == 0L) {
return origMinX;
}
byte[] pixelData = StrikeCache.getGlyphPixelBytes(ptr);
for (int y=0;y<height;y++) {
for (int x=0;x<3;x++) {
if (StrikeCache.unsafe.getByte(pixelData+y*rowBytes+x) != 0) {
if (pixelData[(y*rowBytes)+x] != 0) {
return origMinX;
}
}
@ -819,10 +812,8 @@ public class FileFontStrike extends PhysicalStrike {
}
if (glyphPtr != 0L) {
metrics = new Point2D.Float();
metrics.x = StrikeCache.unsafe.getFloat
(glyphPtr + StrikeCache.xAdvanceOffset);
metrics.y = StrikeCache.unsafe.getFloat
(glyphPtr + StrikeCache.yAdvanceOffset);
metrics.x = StrikeCache.getGlyphXAdvance(glyphPtr);
metrics.y = StrikeCache.getGlyphYAdvance(glyphPtr);
/* advance is currently in device space, need to convert back
* into user space.
* This must not include the translation component. */

View File

@ -319,10 +319,9 @@ public final class GlyphList {
metrics[4] = 0;
return;
}
float gx =
StrikeCache.unsafe.getFloat(images[i]+StrikeCache.topLeftXOffset);
float gy =
StrikeCache.unsafe.getFloat(images[i]+StrikeCache.topLeftYOffset);
float gx = StrikeCache.getGlyphTopLeftX(images[i]);
float gy = StrikeCache.getGlyphTopLeftY(images[i]);
if (usePositions) {
metrics[0] = (int)Math.floor(positions[(i<<1)] + gposx + gx);
@ -331,17 +330,12 @@ public final class GlyphList {
metrics[0] = (int)Math.floor(gposx + gx);
metrics[1] = (int)Math.floor(gposy + gy);
/* gposx and gposy are used to accumulate the advance */
gposx += StrikeCache.unsafe.getFloat
(images[i]+StrikeCache.xAdvanceOffset);
gposy += StrikeCache.unsafe.getFloat
(images[i]+StrikeCache.yAdvanceOffset);
gposx += StrikeCache.getGlyphXAdvance(images[i]);
gposy += StrikeCache.getGlyphYAdvance(images[i]);
}
metrics[2] =
StrikeCache.unsafe.getChar(images[i]+StrikeCache.widthOffset);
metrics[3] =
StrikeCache.unsafe.getChar(images[i]+StrikeCache.heightOffset);
metrics[4] =
StrikeCache.unsafe.getChar(images[i]+StrikeCache.rowBytesOffset);
metrics[2] = StrikeCache.getGlyphWidth(images[i]);
metrics[3] = StrikeCache.getGlyphHeight(images[i]);
metrics[4] = StrikeCache.getGlyphRowBytes(images[i]);
}
public int[] getMetrics() {
@ -360,22 +354,12 @@ public final class GlyphList {
if (images[glyphindex] == 0L) {
return graybits;
}
long pixelDataAddress =
StrikeCache.unsafe.getAddress(images[glyphindex] +
StrikeCache.pixelDataOffset);
long pixelDataAddress = StrikeCache.getGlyphImagePtr(images[glyphindex]);
if (pixelDataAddress == 0L) {
return graybits;
}
/* unsafe is supposed to be fast, but I doubt if this loop can beat
* a native call which does a getPrimitiveArrayCritical and a
* memcpy for the typical amount of image data (30-150 bytes)
* Consider a native method if there is a performance problem (which
* I haven't seen so far).
*/
for (int i=0; i<len; i++) {
graybits[i] = StrikeCache.unsafe.getByte(pixelDataAddress+i);
}
byte[] bytes = StrikeCache.getGlyphPixelBytes(images[glyphindex]);
System.arraycopy(bytes, 0, graybits, 0, bytes.length);
return graybits;
}
@ -445,13 +429,6 @@ public final class GlyphList {
* but it seems unavoidable without re-working the Java TextRenderers.
*/
private void fillBounds(int[] bounds, int endGlyphIndex) {
/* Faster to access local variables in the for loop? */
int xOffset = StrikeCache.topLeftXOffset;
int yOffset = StrikeCache.topLeftYOffset;
int wOffset = StrikeCache.widthOffset;
int hOffset = StrikeCache.heightOffset;
int xAdvOffset = StrikeCache.xAdvanceOffset;
int yAdvOffset = StrikeCache.yAdvanceOffset;
int startGlyphIndex = glyphindex + 1;
if (startGlyphIndex >= endGlyphIndex) {
@ -471,10 +448,10 @@ public final class GlyphList {
if (images[i] == 0L) {
continue;
}
gx = StrikeCache.unsafe.getFloat(images[i]+xOffset);
gy = StrikeCache.unsafe.getFloat(images[i]+yOffset);
gw = StrikeCache.unsafe.getChar(images[i]+wOffset);
gh = StrikeCache.unsafe.getChar(images[i]+hOffset);
gx = StrikeCache.getGlyphTopLeftX(images[i]);
gy = StrikeCache.getGlyphTopLeftY(images[i]);
gw = StrikeCache.getGlyphWidth(images[i]);
gh = StrikeCache.getGlyphHeight(images[i]);
if (usePositions) {
gx0 = positions[posIndex++] + gx + glx;
@ -482,8 +459,8 @@ public final class GlyphList {
} else {
gx0 = glx + gx;
gy0 = gly + gy;
glx += StrikeCache.unsafe.getFloat(images[i]+xAdvOffset);
gly += StrikeCache.unsafe.getFloat(images[i]+yAdvOffset);
glx += StrikeCache.getGlyphXAdvance(images[i]);
gly += StrikeCache.getGlyphYAdvance(images[i]);
}
gx1 = gx0 + gw;
gy1 = gy0 + gh;
@ -506,10 +483,8 @@ public final class GlyphList {
}
public boolean isColorGlyph(int glyphIndex) {
int width = StrikeCache.unsafe.getChar(images[glyphIndex] +
StrikeCache.widthOffset);
int rowBytes = StrikeCache.unsafe.getChar(images[glyphIndex] +
StrikeCache.rowBytesOffset);
int width = StrikeCache.getGlyphWidth(images[glyphIndex]);
int rowBytes = StrikeCache.getGlyphRowBytes(images[glyphIndex]);
return rowBytes == width * 4;
}

View File

@ -27,6 +27,16 @@ package sun.font;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.MemorySegment;
import static java.lang.foreign.MemorySegment.NULL;
import java.lang.foreign.StructLayout;
import java.lang.foreign.ValueLayout;
import static java.lang.foreign.ValueLayout.*;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
@ -37,7 +47,6 @@ import sun.java2d.Disposer;
import sun.java2d.pipe.BufferedContext;
import sun.java2d.pipe.RenderQueue;
import sun.java2d.pipe.hw.AccelGraphicsConfig;
import jdk.internal.misc.Unsafe;
/**
@ -63,8 +72,6 @@ free native memory resources.
public final class StrikeCache {
static final Unsafe unsafe = Unsafe.getUnsafe();
static ReferenceQueue<Object> refQueue = Disposer.getQueue();
static ArrayList<GlyphDisposedListener> disposeListeners = new ArrayList<GlyphDisposedListener>(1);
@ -98,40 +105,159 @@ public final class StrikeCache {
static boolean cacheRefTypeWeak;
/*
* Native sizes and offsets for glyph cache
* There are 10 values.
* Native sizes and accessors for glyph cache structure.
* There are 10 values. Also need native address size and a long which
* references a memory address for a "null" glyph image.
*/
static int nativeAddressSize;
static int glyphInfoSize;
static int xAdvanceOffset;
static int yAdvanceOffset;
static int boundsOffset;
static int widthOffset;
static int heightOffset;
static int rowBytesOffset;
static int topLeftXOffset;
static int topLeftYOffset;
static int pixelDataOffset;
static int cacheCellOffset;
static int managedOffset;
static long invisibleGlyphPtr;
static final int nativeAddressSize = (int)ValueLayout.ADDRESS.byteSize();
static final long invisibleGlyphPtr = getInvisibleGlyphPtr(); // a singleton.
/* Native method used to return information used for unsafe
* access to native data.
* return values as follows:-
* arr[0] = size of an address/pointer.
* arr[1] = size of a GlyphInfo
* arr[2] = offset of advanceX
* arr[3] = offset of advanceY
* arr[4] = offset of width
* arr[5] = offset of height
* arr[6] = offset of rowBytes
* arr[7] = offset of topLeftX
* arr[8] = offset of topLeftY
* arr[9] = offset of pixel data.
* arr[10] = address of a GlyphImageRef representing the invisible glyph
*/
static native void getGlyphCacheDescription(long[] infoArray);
static native long getInvisibleGlyphPtr();
public static final StructLayout GlyphImageLayout = MemoryLayout.structLayout(
JAVA_FLOAT.withName("xAdvance"), // 0+4=4,
JAVA_FLOAT.withName("yAdvance"), // 4+4=8,
JAVA_CHAR.withName("width"), // 8+2=10,
JAVA_CHAR.withName("height"), // 10+2=12
JAVA_CHAR.withName("rowBytes"), // 12+2=14
JAVA_BYTE.withName("managed"), // 14+1=15
MemoryLayout.paddingLayout(1), // 15+1=16
JAVA_FLOAT.withName("topLeftX"), // 16+4=20
JAVA_FLOAT.withName("topLeftY"), // 20+4=24
ADDRESS.withName("cellInfo"), // 24+8=32
ADDRESS.withName("image") // 32+8=40
);
private static final long GLYPHIMAGESIZE = GlyphImageLayout.byteSize();
private static VarHandle getVarHandle(StructLayout struct, String name) {
VarHandle h = struct.varHandle(PathElement.groupElement(name));
/* insert 0 offset so don't need to pass arg every time */
return MethodHandles.insertCoordinates(h, 1, 0L).withInvokeExactBehavior();
}
private static final VarHandle xAdvanceHandle = getVarHandle(GlyphImageLayout, "xAdvance");
private static final VarHandle yAdvanceHandle = getVarHandle(GlyphImageLayout, "yAdvance");
private static final VarHandle widthHandle = getVarHandle(GlyphImageLayout, "width");
private static final VarHandle heightHandle = getVarHandle(GlyphImageLayout, "height");
private static final VarHandle rowBytesHandle = getVarHandle(GlyphImageLayout, "rowBytes");
private static final VarHandle managedHandle = getVarHandle(GlyphImageLayout, "managed");
private static final VarHandle topLeftXHandle = getVarHandle(GlyphImageLayout, "topLeftX");
private static final VarHandle topLeftYHandle = getVarHandle(GlyphImageLayout, "topLeftY");
private static final VarHandle cellInfoHandle = getVarHandle(GlyphImageLayout, "cellInfo");
private static final VarHandle imageHandle = getVarHandle(GlyphImageLayout, "image");
@SuppressWarnings("restricted")
static final float getGlyphXAdvance(long ptr) {
MemorySegment seg = MemorySegment.ofAddress(ptr);
seg = seg.reinterpret(GLYPHIMAGESIZE);
return (float)xAdvanceHandle.get(seg);
}
@SuppressWarnings("restricted")
static final void setGlyphXAdvance(long ptr, float val) {
MemorySegment seg = MemorySegment.ofAddress(ptr);
seg = seg.reinterpret(GLYPHIMAGESIZE);
xAdvanceHandle.set(seg, val);
}
@SuppressWarnings("restricted")
static final float getGlyphYAdvance(long ptr) {
MemorySegment seg = MemorySegment.ofAddress(ptr);
seg = seg.reinterpret(GLYPHIMAGESIZE);
return (float)yAdvanceHandle.get(seg);
}
@SuppressWarnings("restricted")
static final char getGlyphWidth(long ptr) {
MemorySegment seg = MemorySegment.ofAddress(ptr);
seg = seg.reinterpret(GLYPHIMAGESIZE);
return (char)widthHandle.get(seg);
}
@SuppressWarnings("restricted")
static final char getGlyphHeight(long ptr) {
MemorySegment seg = MemorySegment.ofAddress(ptr);
seg = seg.reinterpret(GLYPHIMAGESIZE);
return (char)heightHandle.get(seg);
}
@SuppressWarnings("restricted")
static final char getGlyphRowBytes(long ptr) {
MemorySegment seg = MemorySegment.ofAddress(ptr);
seg = seg.reinterpret(GLYPHIMAGESIZE);
return (char)rowBytesHandle.get(seg);
}
@SuppressWarnings("restricted")
static final byte getGlyphManaged(long ptr) {
MemorySegment seg = MemorySegment.ofAddress(ptr);
seg = seg.reinterpret(GLYPHIMAGESIZE);
return (byte)managedHandle.get(seg);
}
@SuppressWarnings("restricted")
static final float getGlyphTopLeftX(long ptr) {
MemorySegment seg = MemorySegment.ofAddress(ptr);
seg = seg.reinterpret(GLYPHIMAGESIZE);
return (float)topLeftXHandle.get(seg);
}
@SuppressWarnings("restricted")
static final float getGlyphTopLeftY(long ptr) {
MemorySegment seg = MemorySegment.ofAddress(ptr);
seg = seg.reinterpret(GLYPHIMAGESIZE);
return (float)topLeftYHandle.get(seg);
}
@SuppressWarnings("restricted")
static final long getGlyphCellInfo(long ptr) {
MemorySegment seg = MemorySegment.ofAddress(ptr);
seg = seg.reinterpret(GLYPHIMAGESIZE);
return ((MemorySegment)cellInfoHandle.get(seg)).address();
}
@SuppressWarnings("restricted")
static final void setGlyphCellInfo(long ptr, long val) {
MemorySegment seg = MemorySegment.ofAddress(ptr);
seg = seg.reinterpret(GLYPHIMAGESIZE);
MemorySegment segval = MemorySegment.ofAddress(val);
cellInfoHandle.set(seg, segval);
}
@SuppressWarnings("restricted")
static final long getGlyphImagePtr(long ptr) {
MemorySegment seg = MemorySegment.ofAddress(ptr);
seg = seg.reinterpret(GLYPHIMAGESIZE);
return ((MemorySegment)imageHandle.get(seg)).address();
}
@SuppressWarnings("restricted")
static final MemorySegment getGlyphPixelData(long ptr) {
MemorySegment seg = MemorySegment.ofAddress(ptr);
seg = seg.reinterpret(GLYPHIMAGESIZE);
char hgt = (char)heightHandle.get(seg);
char rb = (char)rowBytesHandle.get(seg);
MemorySegment pixelData = (MemorySegment)imageHandle.get(seg);
pixelData = pixelData.reinterpret(rb * hgt);
return pixelData;
}
@SuppressWarnings("restricted")
static final byte[] getGlyphPixelBytes(long ptr) {
MemorySegment seg = MemorySegment.ofAddress(ptr);
seg = seg.reinterpret(GLYPHIMAGESIZE);
char hgt = (char)heightHandle.get(seg);
char rb = (char)rowBytesHandle.get(seg);
MemorySegment pixelData = (MemorySegment)imageHandle.get(seg);
int sz = rb * hgt;
pixelData = pixelData.reinterpret(sz);
return pixelData.toArray(ValueLayout.JAVA_BYTE);
}
static final byte getPixelByte(MemorySegment pixelData, long index) {
return pixelData.getAtIndex(JAVA_BYTE, index);
}
static {
initStatic();
@ -140,24 +266,6 @@ public final class StrikeCache {
@SuppressWarnings("removal")
private static void initStatic() {
long[] nativeInfo = new long[13];
getGlyphCacheDescription(nativeInfo);
//Can also get address size from Unsafe class :-
//nativeAddressSize = unsafe.addressSize();
nativeAddressSize = (int)nativeInfo[0];
glyphInfoSize = (int)nativeInfo[1];
xAdvanceOffset = (int)nativeInfo[2];
yAdvanceOffset = (int)nativeInfo[3];
widthOffset = (int)nativeInfo[4];
heightOffset = (int)nativeInfo[5];
rowBytesOffset = (int)nativeInfo[6];
topLeftXOffset = (int)nativeInfo[7];
topLeftYOffset = (int)nativeInfo[8];
pixelDataOffset = (int)nativeInfo[9];
invisibleGlyphPtr = nativeInfo[10];
cacheCellOffset = (int) nativeInfo[11];
managedOffset = (int) nativeInfo[12];
if (nativeAddressSize < 4) {
throw new InternalError("Unexpected address size for font data: " +
nativeAddressSize);
@ -324,7 +432,7 @@ public final class StrikeCache {
ArrayList<Long> gids = null;
for (int i = 0; i < glyphPtrs.length; i++) {
if (glyphPtrs[i] != 0 && unsafe.getByte(glyphPtrs[i] + managedOffset) == 0) {
if ((glyphPtrs[i] != 0) && getGlyphManaged(glyphPtrs[i]) == 0) {
if (gids == null) {
gids = new ArrayList<Long>();
@ -350,8 +458,7 @@ public final class StrikeCache {
ArrayList<Long> gids = null;
for (int i=0; i < glyphPtrs.length; i++) {
if (glyphPtrs[i] != 0
&& unsafe.getByte(glyphPtrs[i] + managedOffset) == 0) {
if ((glyphPtrs[i] != 0) && getGlyphManaged(glyphPtrs[i]) == 0) {
if (gids == null) {
gids = new ArrayList<Long>();

View File

@ -300,41 +300,9 @@ JNIEXPORT void JNICALL Java_sun_font_StrikeCache_freeLongMemory
}
}
JNIEXPORT void JNICALL
Java_sun_font_StrikeCache_getGlyphCacheDescription
(JNIEnv *env, jclass cls, jlongArray results) {
JNIEXPORT jlong JNICALL
Java_sun_font_StrikeCache_getInvisibleGlyphPtr(JNIEnv *env, jclass cls) {
jlong* nresults;
GlyphInfo *info;
size_t baseAddr;
if ((*env)->GetArrayLength(env, results) < 13) {
return;
}
nresults = (jlong*)(*env)->GetPrimitiveArrayCritical(env, results, NULL);
if (nresults == NULL) {
return;
}
info = (GlyphInfo*) calloc(1, sizeof(GlyphInfo));
if (info == NULL) {
(*env)->ReleasePrimitiveArrayCritical(env, results, nresults, 0);
return;
}
baseAddr = (size_t)info;
nresults[0] = sizeof(void*);
nresults[1] = sizeof(GlyphInfo);
nresults[2] = 0;
nresults[3] = (size_t)&(info->advanceY)-baseAddr;
nresults[4] = (size_t)&(info->width)-baseAddr;
nresults[5] = (size_t)&(info->height)-baseAddr;
nresults[6] = (size_t)&(info->rowBytes)-baseAddr;
nresults[7] = (size_t)&(info->topLeftX)-baseAddr;
nresults[8] = (size_t)&(info->topLeftY)-baseAddr;
nresults[9] = (size_t)&(info->image)-baseAddr;
nresults[10] = (jlong)(uintptr_t)info; /* invisible glyph */
nresults[11] = (size_t)&(info->cellInfo)-baseAddr;
nresults[12] = (size_t)&(info->managed)-baseAddr;
(*env)->ReleasePrimitiveArrayCritical(env, results, nresults, 0);
GlyphInfo *info = (GlyphInfo*) calloc(1, sizeof(GlyphInfo));
return (jlong)(uintptr_t)info; /* invisible glyph */
}

View File

@ -69,28 +69,25 @@ public class XRGlyphCacheEntry {
}
public static int getGlyphID(long glyphInfoPtr) {
// We need to access the GlyphID with Unsafe.getAddress() because the
// We need to access the GlyphID as a long because the
// corresponding field in the underlying C data-structure is of type
// 'void*' (see field 'cellInfo' of struct 'GlyphInfo'
// in src/share/native/sun/font/fontscalerdefs.h).
// On 64-bit Big-endian architectures it would be wrong to access this
// field with Unsafe.getInt().
return (int) StrikeCache.unsafe.getAddress(glyphInfoPtr +
StrikeCache.cacheCellOffset);
// field as an int.
return (int)StrikeCache.getGlyphCellInfo(glyphInfoPtr);
}
public static void setGlyphID(long glyphInfoPtr, int id) {
// We need to access the GlyphID with Unsafe.putAddress() because the
// We need to access the GlyphID as a long because the
// corresponding field in the underlying C data-structure is of type
// 'void*' (see field 'cellInfo' of struct 'GlyphInfo' in
// src/share/native/sun/font/fontscalerdefs.h).
// On 64-bit Big-endian architectures it would be wrong to write this
// field with Unsafe.putInt() because it is also accessed from native
// code as a 'long'.
// 'void*' (see field 'cellInfo' of struct 'GlyphInfo'
// in src/share/native/sun/font/fontscalerdefs.h).
// On 64-bit Big-endian architectures it would be wrong to access this
// field as an int
// See Java_sun_java2d_xr_XRBackendNative_XRAddGlyphsNative()
// in src/solaris/native/sun/java2d/x11/XRBackendNative.c
StrikeCache.unsafe.putAddress(glyphInfoPtr +
StrikeCache.cacheCellOffset, (long)id);
// in src/unix/native/sun/java2d/x11/XRBackendNative.c
StrikeCache.setGlyphCellInfo(glyphInfoPtr, (long)id);
}
public int getGlyphID() {
@ -102,29 +99,27 @@ public class XRGlyphCacheEntry {
}
public float getXAdvance() {
return StrikeCache.unsafe.getFloat(glyphInfoPtr + StrikeCache.xAdvanceOffset);
return StrikeCache.getGlyphXAdvance(glyphInfoPtr);
}
public float getYAdvance() {
return StrikeCache.unsafe.getFloat(glyphInfoPtr + StrikeCache.yAdvanceOffset);
return StrikeCache.getGlyphYAdvance(glyphInfoPtr);
}
public int getSourceRowBytes() {
return StrikeCache.unsafe.getShort(glyphInfoPtr + StrikeCache.rowBytesOffset);
return StrikeCache.getGlyphRowBytes(glyphInfoPtr);
}
public int getWidth() {
return StrikeCache.unsafe.getShort(glyphInfoPtr + StrikeCache.widthOffset);
return StrikeCache.getGlyphWidth(glyphInfoPtr);
}
public int getHeight() {
return StrikeCache.unsafe.getShort(glyphInfoPtr + StrikeCache.heightOffset);
return StrikeCache.getGlyphHeight(glyphInfoPtr);
}
public void writePixelData(ByteArrayOutputStream os, boolean uploadAsLCD) {
long pixelDataAddress =
StrikeCache.unsafe.getAddress(glyphInfoPtr +
StrikeCache.pixelDataOffset);
long pixelDataAddress = StrikeCache.getGlyphImagePtr(glyphInfoPtr);
if (pixelDataAddress == 0L) {
return;
}
@ -134,11 +129,12 @@ public class XRGlyphCacheEntry {
int rowBytes = getSourceRowBytes();
int paddedWidth = getPaddedWidth(uploadAsLCD);
byte[] pixelBytes = StrikeCache.getGlyphPixelBytes(glyphInfoPtr);
if (!uploadAsLCD) {
for (int line = 0; line < height; line++) {
for(int x = 0; x < paddedWidth; x++) {
if(x < width) {
os.write(StrikeCache.unsafe.getByte(pixelDataAddress + (line * rowBytes + x)));
os.write(pixelBytes[(line * rowBytes + x)]);
}else {
/*pad to multiple of 4 bytes per line*/
os.write(0);
@ -151,12 +147,9 @@ public class XRGlyphCacheEntry {
int rowBytesWidth = width * 3;
int srcpix = 0;
while (srcpix < rowBytesWidth) {
os.write(StrikeCache.unsafe.getByte
(pixelDataAddress + (rowStart + srcpix + 2)));
os.write(StrikeCache.unsafe.getByte
(pixelDataAddress + (rowStart + srcpix + 1)));
os.write(StrikeCache.unsafe.getByte
(pixelDataAddress + (rowStart + srcpix + 0)));
os.write(pixelBytes[rowStart + srcpix + 2]);
os.write(pixelBytes[rowStart + srcpix + 1]);
os.write(pixelBytes[rowStart + srcpix + 0]);
os.write(255);
srcpix += 3;
}
@ -165,11 +158,11 @@ public class XRGlyphCacheEntry {
}
public float getTopLeftXOffset() {
return StrikeCache.unsafe.getFloat(glyphInfoPtr + StrikeCache.topLeftXOffset);
return StrikeCache.getGlyphTopLeftX(glyphInfoPtr);
}
public float getTopLeftYOffset() {
return StrikeCache.unsafe.getFloat(glyphInfoPtr + StrikeCache.topLeftYOffset);
return StrikeCache.getGlyphTopLeftY(glyphInfoPtr);
}
public long getGlyphInfoPtr() {