--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/native/sun/font/freetypeScaler.c Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,1410 @@
+/*
+ * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved.
+ * 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+#include "jni.h"
+#include "jni_util.h"
+#include "jlong.h"
+#include "sunfontids.h"
+#include "sun_font_FreetypeFontScaler.h"
+
+#include<stdlib.h>
+#include <math.h>
+#include "ft2build.h"
+#include FT_FREETYPE_H
+#include FT_GLYPH_H
+#include FT_BBOX_H
+#include FT_SIZES_H
+#include FT_OUTLINE_H
+#include FT_SYNTHESIS_H
+
+#include "fontscaler.h"
+
+#define ftFixed1 (FT_Fixed) (1 << 16)
+#define FloatToFTFixed(f) (FT_Fixed)((f) * (float)(ftFixed1))
+#define FTFixedToFloat(x) ((x) / (float)(ftFixed1))
+#define FT26Dot6ToFloat(x) ((x) / ((float) (1<<6)))
+#define ROUND(x) ((int) (x+0.5))
+
+typedef struct {
+ /* Important note:
+ JNI forbids sharing same env between different threads.
+ We are safe, because pointer is overwritten every time we get into
+ JNI call (see setupFTContext).
+
+ Pointer is used by font data reading callbacks
+ such as ReadTTFontFileFunc.
+
+ NB: We may consider switching to JNI_GetEnv. */
+ JNIEnv* env;
+ FT_Library library;
+ FT_Face face;
+ jobject font2D;
+ jobject directBuffer;
+
+ unsigned char* fontData;
+ unsigned fontDataOffset;
+ unsigned fontDataLength;
+ unsigned fileSize;
+ TTLayoutTableCache* layoutTables;
+} FTScalerInfo;
+
+typedef struct FTScalerContext {
+ FT_Matrix transform; /* glyph transform, including device transform */
+ jboolean useSbits; /* sbit usage enabled? */
+ jint aaType; /* antialiasing mode (off/on/grey/lcd) */
+ jint fmType; /* fractional metrics - on/off */
+ jboolean doBold; /* perform algorithmic bolding? */
+ jboolean doItalize; /* perform algorithmic italicizing? */
+ int renderFlags; /* configuration specific to particular engine */
+ int pathType;
+ int ptsz; /* size in points */
+} FTScalerContext;
+
+#ifdef DEBUG
+/* These are referenced in the freetype sources if DEBUG macro is defined.
+ To simplify work with debuging version of freetype we define
+ them here. */
+int z_verbose;
+void z_error(char *s) {}
+#endif
+
+/**************** Error handling utilities *****************/
+
+static jmethodID invalidateScalerMID;
+
+JNIEXPORT void JNICALL
+Java_sun_font_FreetypeFontScaler_initIDs(
+ JNIEnv *env, jobject scaler, jclass FFSClass) {
+ invalidateScalerMID =
+ (*env)->GetMethodID(env, FFSClass, "invalidateScaler", "()V");
+}
+
+static void freeNativeResources(JNIEnv *env, FTScalerInfo* scalerInfo) {
+ if (scalerInfo == NULL)
+ return;
+
+ FT_Done_Face(scalerInfo->face);
+ FT_Done_FreeType(scalerInfo->library);
+
+ if (scalerInfo->directBuffer != NULL) {
+ (*env)->DeleteGlobalRef(env, scalerInfo->directBuffer);
+ }
+
+ if (scalerInfo->fontData != NULL) {
+ free(scalerInfo->fontData);
+ }
+
+ free(scalerInfo);
+}
+
+/* invalidates state of java scaler object */
+static void invalidateJavaScaler(JNIEnv *env,
+ jobject scaler,
+ FTScalerInfo* scalerInfo) {
+ freeNativeResources(env, scalerInfo);
+ (*env)->CallVoidMethod(env, scaler, invalidateScalerMID);
+}
+
+/******************* I/O handlers ***************************/
+
+#define FILEDATACACHESIZE 1024
+
+/* NB: is it ever called? */
+static void CloseTTFontFileFunc(FT_Stream stream) {
+ FTScalerInfo *scalerInfo = (FTScalerInfo *) stream->pathname.pointer;
+ JNIEnv* env = scalerInfo->env;
+ jclass tmpClass = (*env)->FindClass(env, "sun/font/TrueTypeFont");
+ jfieldID platNameField =
+ (*env)->GetFieldID(env, tmpClass, "platName", "Ljava/lang/String;");
+ jstring platName = (*env)->GetObjectField(env,
+ scalerInfo->font2D,
+ platNameField);
+ const char *name = JNU_GetStringPlatformChars(env, platName, NULL);
+ JNU_ReleaseStringPlatformChars(env, platName, name);
+}
+
+static unsigned long ReadTTFontFileFunc(FT_Stream stream,
+ unsigned long offset,
+ unsigned char* destBuffer,
+ unsigned long numBytes)
+{
+ FTScalerInfo *scalerInfo = (FTScalerInfo *) stream->pathname.pointer;
+ JNIEnv* env = scalerInfo->env;
+ jobject bBuffer;
+ int bread = 0;
+
+ if (numBytes == 0) return 0;
+
+ /* Large reads will bypass the cache and data copying */
+ if (numBytes > FILEDATACACHESIZE) {
+ bBuffer = (*env)->NewDirectByteBuffer(env, destBuffer, numBytes);
+ if (bBuffer != NULL) {
+ /* Loop until the read succeeds (or EOF).
+ * This should improve robustness in the event of a problem in
+ * the I/O system. If we find that we ever end up spinning here
+ * we are going to have to do some serious work to recover.
+ * Just returning without reading the data will cause a crash.
+ */
+ while (bread == 0) {
+ bread = (*env)->CallIntMethod(env,
+ scalerInfo->font2D,
+ sunFontIDs.ttReadBlockMID,
+ bBuffer, offset, numBytes);
+ }
+ return bread;
+ } else {
+ /* We probably hit bug bug 4845371. For reasons that
+ * are currently unclear, the call stacks after the initial
+ * createScaler call that read large amounts of data seem to
+ * be OK and can create the byte buffer above, but this code
+ * is here just in case.
+ * 4845371 is fixed now so I don't expect this code path to
+ * ever get called but its harmless to leave it here on the
+ * small chance its needed.
+ */
+ jbyteArray byteArray = (jbyteArray)
+ (*env)->CallObjectMethod(env, scalerInfo->font2D,
+ sunFontIDs.ttReadBytesMID,
+ offset, numBytes);
+ (*env)->GetByteArrayRegion(env, byteArray,
+ 0, numBytes, (jbyte*)destBuffer);
+ return numBytes;
+ }
+ } /* Do we have a cache hit? */
+ else if (scalerInfo->fontDataOffset <= offset &&
+ scalerInfo->fontDataOffset + scalerInfo->fontDataLength >=
+ offset + numBytes)
+ {
+ unsigned cacheOffset = offset - scalerInfo->fontDataOffset;
+
+ memcpy(destBuffer, scalerInfo->fontData+(size_t)cacheOffset, numBytes);
+ return numBytes;
+ } else {
+ /* Must fill the cache */
+ scalerInfo->fontDataOffset = offset;
+ scalerInfo->fontDataLength =
+ (offset + FILEDATACACHESIZE > scalerInfo->fileSize) ?
+ scalerInfo->fileSize - offset : FILEDATACACHESIZE;
+ bBuffer = scalerInfo->directBuffer;
+ /* Loop until all the read succeeds (or EOF).
+ * This should improve robustness in the event of a problem in
+ * the I/O system. If we find that we ever end up spinning here
+ * we are going to have to do some serious work to recover.
+ * Just returning without reading the data will cause a crash.
+ */
+ while (bread == 0) {
+ bread = (*env)->CallIntMethod(env, scalerInfo->font2D,
+ sunFontIDs.ttReadBlockMID,
+ bBuffer, offset,
+ scalerInfo->fontDataLength);
+ }
+
+ memcpy(destBuffer, scalerInfo->fontData, numBytes);
+ return numBytes;
+ }
+}
+
+/*
+ * Class: sun_font_FreetypeFontScaler
+ * Method: initNativeScaler
+ * Signature: (Lsun/font/Font2D;IIZI)J
+ */
+JNIEXPORT jlong JNICALL
+Java_sun_font_FreetypeFontScaler_initNativeScaler(
+ JNIEnv *env, jobject scaler, jobject font2D, jint type,
+ jint indexInCollection, jboolean supportsCJK, jint filesize) {
+ FTScalerInfo* scalerInfo = NULL;
+ FT_Stream ftstream;
+ FT_Open_Args ft_open_args;
+ int error;
+ jobject bBuffer;
+ scalerInfo = (FTScalerInfo*) calloc(1, sizeof(FTScalerInfo));
+
+ if (scalerInfo == NULL)
+ return 0;
+
+ scalerInfo->env = env;
+ scalerInfo->font2D = font2D;
+ scalerInfo->fontDataOffset = 0;
+ scalerInfo->fontDataLength = 0;
+ scalerInfo->fileSize = filesize;
+
+ /*
+ We can consider sharing freetype library between different
+ scalers. However, Freetype docs suggest to use different libraries
+ for different threads. Also, our architecture implies that single
+ FontScaler object is shared for for different sizes/transforms/styles
+ of the same font.
+
+ On other hand these methods can not be concurrently executed
+ becaused they are "synchronized" in java.
+ */
+ error = FT_Init_FreeType(&scalerInfo->library);
+ if (error) {
+ free(scalerInfo);
+ return 0;
+ }
+
+#define TYPE1_FROM_JAVA 2
+
+ error = 1; /* triggers memory freeing unless we clear it */
+ 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) {
+ bBuffer = (*env)->NewDirectByteBuffer(env,
+ scalerInfo->fontData,
+ scalerInfo->fontDataLength);
+ if (bBuffer != NULL) {
+ (*env)->CallObjectMethod(env, font2D,
+ sunFontIDs.readFileMID, bBuffer);
+
+ error = FT_New_Memory_Face(scalerInfo->library,
+ scalerInfo->fontData,
+ scalerInfo->fontDataLength,
+ indexInCollection,
+ &scalerInfo->face);
+ }
+ }
+ } else { /* Truetype */
+ scalerInfo->fontData = (unsigned char*) malloc(FILEDATACACHESIZE);
+ ftstream = (FT_Stream) calloc(1, sizeof(FT_StreamRec));
+
+ if (ftstream != NULL && scalerInfo->fontData != NULL) {
+ scalerInfo->directBuffer = (*env)->NewDirectByteBuffer(env,
+ scalerInfo->fontData,
+ FILEDATACACHESIZE);
+ if (scalerInfo->directBuffer != NULL) {
+ scalerInfo->directBuffer = (*env)->NewGlobalRef(env,
+ scalerInfo->directBuffer);
+ ftstream->base = NULL;
+ ftstream->size = filesize;
+ ftstream->pos = 0;
+ ftstream->read = (FT_Stream_IoFunc) ReadTTFontFileFunc;
+ ftstream->close = (FT_Stream_CloseFunc) CloseTTFontFileFunc;
+ ftstream->pathname.pointer = (void *) scalerInfo;
+
+ memset(&ft_open_args, 0, sizeof(FT_Open_Args));
+ ft_open_args.flags = FT_OPEN_STREAM;
+ ft_open_args.stream = ftstream;
+
+ error = FT_Open_Face(scalerInfo->library,
+ &ft_open_args,
+ indexInCollection,
+ &scalerInfo->face);
+ }
+ if (error || scalerInfo->directBuffer == NULL) {
+ free(ftstream);
+ }
+ }
+ }
+
+ if (error) {
+ FT_Done_FreeType(scalerInfo->library);
+ if (scalerInfo->directBuffer != NULL) {
+ (*env)->DeleteGlobalRef(env, scalerInfo->directBuffer);
+ }
+ if (scalerInfo->fontData != NULL)
+ free(scalerInfo->fontData);
+ free(scalerInfo);
+ return 0;
+ }
+
+ return ptr_to_jlong(scalerInfo);
+}
+
+static double euclidianDistance(double a, double b) {
+ if (a < 0) a=-a;
+ if (b < 0) b=-b;
+
+ if (a == 0) return b;
+ if (b == 0) return a;
+
+ return sqrt(a*a+b*b);
+}
+
+JNIEXPORT jlong JNICALL
+Java_sun_font_FreetypeFontScaler_createScalerContextNative(
+ JNIEnv *env, jobject scaler, jlong pScaler, jdoubleArray matrix,
+ jboolean ttFont, jint aa, jint fm, jfloat boldness, jfloat italic) {
+ double dmat[4], ptsz;
+ FTScalerContext *context =
+ (FTScalerContext*) calloc(1, sizeof(FTScalerContext));
+ FTScalerInfo *scalerInfo =
+ (FTScalerInfo*) jlong_to_ptr(pScaler);
+
+ if (context == NULL) {
+ invalidateJavaScaler(env, scaler, NULL);
+ return (jlong) 0;
+ }
+ (*env)->GetDoubleArrayRegion(env, matrix, 0, 4, dmat);
+ ptsz = euclidianDistance(dmat[2], dmat[3]); //i.e. y-size
+ if (ptsz < 1.0) {
+ //text can not be smaller than 1 point
+ ptsz = 1.0;
+ }
+ context->ptsz = (((int) ptsz) << 6);
+ context->transform.xx = FloatToFTFixed((float)dmat[0]/ptsz);
+ context->transform.yx = -FloatToFTFixed((float)dmat[1]/ptsz);
+ context->transform.xy = -FloatToFTFixed((float)dmat[2]/ptsz);
+ context->transform.yy = FloatToFTFixed((float)dmat[3]/ptsz);
+ context->aaType = aa;
+ context->fmType = fm;
+
+ /* If using algorithmic styling, the base values are
+ * boldness = 1.0, italic = 0.0.
+ */
+ context->doBold = (boldness != 1.0);
+ context->doItalize = (italic != 0);
+
+ return ptr_to_jlong(context);
+}
+
+static int setupFTContext(JNIEnv *env,
+ jobject font2D,
+ FTScalerInfo *scalerInfo,
+ FTScalerContext *context) {
+ int errCode = 0;
+
+ scalerInfo->env = env;
+ scalerInfo->font2D = font2D;
+
+ FT_Set_Transform(scalerInfo->face, &context->transform, NULL);
+
+ errCode = FT_Set_Char_Size(scalerInfo->face, 0, context->ptsz, 72, 72);
+
+ if (errCode == 0) {
+ errCode = FT_Activate_Size(scalerInfo->face->size);
+ }
+
+ return errCode;
+}
+
+/* ftsynth.c uses (0x10000, 0x06000, 0x0, 0x10000) matrix to get oblique
+ outline. Therefore x coordinate will change by 0x06000*y.
+ Note that y coordinate does not change. */
+#define OBLIQUE_MODIFIER(y) (context->doItalize ? ((y)*6/16) : 0)
+
+/*
+ * Class: sun_font_FreetypeFontScaler
+ * Method: getFontMetricsNative
+ * Signature: (Lsun/font/Font2D;J)Lsun/font/StrikeMetrics;
+ */
+JNIEXPORT jobject JNICALL
+Java_sun_font_FreetypeFontScaler_getFontMetricsNative(
+ JNIEnv *env, jobject scaler, jobject font2D,
+ jlong pScalerContext, jlong pScaler) {
+
+ jobject metrics;
+ jfloat ax, ay, dx, dy, bx, by, lx, ly, mx, my;
+ jfloat f0 = 0.0;
+ FT_Pos bmodifier = 0;
+ FTScalerContext *context =
+ (FTScalerContext*) jlong_to_ptr(pScalerContext);
+ FTScalerInfo *scalerInfo =
+ (FTScalerInfo*) jlong_to_ptr(pScaler);
+
+ int errCode;
+
+ if (isNullScalerContext(context) || scalerInfo == NULL) {
+ return (*env)->NewObject(env,
+ sunFontIDs.strikeMetricsClass,
+ sunFontIDs.strikeMetricsCtr,
+ f0, f0, f0, f0, f0, f0, f0, f0, f0, f0);
+ }
+
+ errCode = setupFTContext(env, font2D, scalerInfo, context);
+
+ if (errCode) {
+ metrics = (*env)->NewObject(env,
+ sunFontIDs.strikeMetricsClass,
+ sunFontIDs.strikeMetricsCtr,
+ f0, f0, f0, f0, f0, f0, f0, f0, f0, f0);
+ invalidateJavaScaler(env, scaler, scalerInfo);
+ return metrics;
+ }
+
+ /* This is ugly and has to be reworked.
+ Freetype provide means to add style to glyph but
+ it seems there is no way to adjust metrics accordingly.
+
+ So, we have to do adust them explicitly and stay consistent with what
+ freetype does to outlines. */
+
+ /* For bolding glyphs are not just widened. Height is also changed
+ (see ftsynth.c).
+
+ TODO: In vertical direction we could do better job and adjust metrics
+ proportionally to glyoh shape. */
+ if (context->doBold) {
+ bmodifier = FT_MulFix(
+ scalerInfo->face->units_per_EM,
+ scalerInfo->face->size->metrics.y_scale)/24;
+ }
+
+
+ /**** Note: only some metrics are affected by styling ***/
+
+ /* ascent */
+ ax = 0;
+ ay = -(jfloat) FT26Dot6ToFloat(
+ scalerInfo->face->size->metrics.ascender +
+ bmodifier/2);
+ /* descent */
+ dx = 0;
+ dy = -(jfloat) FT26Dot6ToFloat(
+ scalerInfo->face->size->metrics.descender +
+ bmodifier/2);
+ /* baseline */
+ bx = by = 0;
+
+ /* leading */
+ lx = 0;
+ ly = (jfloat) FT26Dot6ToFloat(
+ scalerInfo->face->size->metrics.height +
+ bmodifier) + ay - dy;
+ /* max advance */
+ mx = (jfloat) FT26Dot6ToFloat(
+ scalerInfo->face->size->metrics.max_advance +
+ 2*bmodifier +
+ OBLIQUE_MODIFIER(scalerInfo->face->size->metrics.height));
+ my = 0;
+
+ metrics = (*env)->NewObject(env,
+ sunFontIDs.strikeMetricsClass,
+ sunFontIDs.strikeMetricsCtr,
+ ax, ay, dx, dy, bx, by, lx, ly, mx, my);
+
+ return metrics;
+}
+
+/*
+ * Class: sun_font_FreetypeFontScaler
+ * Method: getGlyphAdvanceNative
+ * Signature: (Lsun/font/Font2D;JI)F
+ */
+JNIEXPORT jfloat JNICALL
+Java_sun_font_FreetypeFontScaler_getGlyphAdvanceNative(
+ JNIEnv *env, jobject scaler, jobject font2D,
+ jlong pScalerContext, jlong pScaler, jint glyphCode) {
+
+ /* This method is rarely used because requests for metrics are usually
+ coupled with request for bitmap and to large extend work can be reused
+ (to find out metrics we need to hint glyph).
+ So, we typically go through getGlyphImage code path.
+
+ For initial freetype implementation we delegate
+ all work to getGlyphImage but drop result image.
+ This is waste of work related to scan conversion and conversion from
+ freetype format to our format but for now this seems to be ok.
+
+ NB: investigate performance benefits of refactoring code
+ to avoid unnecesary work with bitmaps. */
+
+ GlyphInfo *info;
+ jfloat advance;
+ jlong image;
+
+ image = Java_sun_font_FreetypeFontScaler_getGlyphImageNative(
+ env, scaler, font2D, pScalerContext, pScaler, glyphCode);
+ info = (GlyphInfo*) jlong_to_ptr(image);
+
+ advance = info->advanceX;
+
+ free(info);
+
+ return advance;
+}
+
+/*
+ * Class: sun_font_FreetypeFontScaler
+ * Method: getGlyphMetricsNative
+ * Signature: (Lsun/font/Font2D;JILjava/awt/geom/Point2D/Float;)V
+ */
+JNIEXPORT void JNICALL
+Java_sun_font_FreetypeFontScaler_getGlyphMetricsNative(
+ JNIEnv *env, jobject scaler, jobject font2D, jlong pScalerContext,
+ jlong pScaler, jint glyphCode, jobject metrics) {
+
+ /* As initial implementation we delegate all work to getGlyphImage
+ but drop result image. This is clearly waste of resorces.
+
+ TODO: investigate performance benefits of refactoring code
+ by avoiding bitmap generation and conversion from FT
+ bitmap format. */
+ GlyphInfo *info;
+
+ jlong image = Java_sun_font_FreetypeFontScaler_getGlyphImageNative(
+ env, scaler, font2D,
+ pScalerContext, pScaler, glyphCode);
+ info = (GlyphInfo*) jlong_to_ptr(image);
+
+ (*env)->SetFloatField(env, metrics, sunFontIDs.xFID, info->advanceX);
+ (*env)->SetFloatField(env, metrics, sunFontIDs.yFID, info->advanceY);
+
+ free(info);
+}
+
+
+static GlyphInfo* getNullGlyphImage() {
+ GlyphInfo *glyphInfo = (GlyphInfo*) calloc(1, sizeof(GlyphInfo));
+ return glyphInfo;
+}
+
+static void CopyBW2Grey8(const void* srcImage, int srcRowBytes,
+ void* dstImage, int dstRowBytes,
+ int width, int height) {
+ const UInt8* srcRow = (UInt8*)srcImage;
+ UInt8* dstRow = (UInt8*)dstImage;
+ int wholeByteCount = width >> 3;
+ int remainingBitsCount = width & 7;
+ int i, j;
+
+ while (height--) {
+ const UInt8* src8 = srcRow;
+ UInt8* dstByte = dstRow;
+ unsigned srcValue;
+
+ srcRow += srcRowBytes;
+ dstRow += dstRowBytes;
+
+ for (i = 0; i < wholeByteCount; i++) {
+ srcValue = *src8++;
+ for (j = 0; j < 8; j++) {
+ *dstByte++ = (srcValue & 0x80) ? 0xFF : 0;
+ srcValue <<= 1;
+ }
+ }
+ if (remainingBitsCount) {
+ srcValue = *src8;
+ for (j = 0; j < remainingBitsCount; j++) {
+ *dstByte++ = (srcValue & 0x80) ? 0xFF : 0;
+ srcValue <<= 1;
+ }
+ }
+ }
+}
+
+#define Grey4ToAlpha255(value) (((value) << 4) + ((value) >> 3))
+
+static void CopyGrey4ToGrey8(const void* srcImage, int srcRowBytes,
+ void* dstImage, int dstRowBytes, int width, int height) {
+ const UInt8* srcRow = (UInt8*) srcImage;
+ UInt8* dstRow = (UInt8*) dstImage;
+ int i;
+
+ while (height--) {
+ const UInt8* src8 = srcRow;
+ UInt8* dstByte = dstRow;
+ unsigned srcValue;
+
+ srcRow += srcRowBytes;
+ dstRow += dstRowBytes;
+
+ for (i = 0; i < width; i++) {
+ srcValue = *src8++;
+ *dstByte++ = Grey4ToAlpha255(srcValue & 0x0f);
+ *dstByte++ = Grey4ToAlpha255(srcValue >> 4);
+ }
+ }
+}
+
+/* We need it because FT rows are often padded to 4 byte boundaries
+ and our internal format is not padded */
+static void CopyFTSubpixelToSubpixel(const void* srcImage, int srcRowBytes,
+ void* dstImage, int dstRowBytes,
+ int width, int height) {
+ unsigned char *srcRow = (unsigned char *) srcImage;
+ unsigned char *dstRow = (unsigned char *) dstImage;
+
+ while (height--) {
+ memcpy(dstRow, srcRow, width);
+ srcRow += srcRowBytes;
+ dstRow += dstRowBytes;
+ }
+}
+
+/* We need it because FT rows are often padded to 4 byte boundaries
+ and our internal format is not padded */
+static void CopyFTSubpixelVToSubpixel(const void* srcImage, int srcRowBytes,
+ void* dstImage, int dstRowBytes,
+ int width, int height) {
+ unsigned char *srcRow = (unsigned char *) srcImage, *srcByte;
+ unsigned char *dstRow = (unsigned char *) dstImage, *dstByte;
+ int i;
+
+ while (height > 0) {
+ srcByte = srcRow;
+ dstByte = dstRow;
+ for (i = 0; i < width; i++) {
+ *dstByte++ = *srcByte;
+ *dstByte++ = *(srcByte + srcRowBytes);
+ *dstByte++ = *(srcByte + 2*srcRowBytes);
+ srcByte++;
+ }
+ srcRow += 3*srcRowBytes;
+ dstRow += dstRowBytes;
+ height -= 3;
+ }
+}
+
+
+/*
+ * Class: sun_font_FreetypeFontScaler
+ * Method: getGlyphImageNative
+ * Signature: (Lsun/font/Font2D;JI)J
+ */
+JNIEXPORT jlong JNICALL
+Java_sun_font_FreetypeFontScaler_getGlyphImageNative(
+ JNIEnv *env, jobject scaler, jobject font2D,
+ jlong pScalerContext, jlong pScaler, jint glyphCode) {
+
+ int error, imageSize;
+ UInt16 width, height;
+ GlyphInfo *glyphInfo;
+ int glyph_index;
+ int renderFlags = FT_LOAD_RENDER, target;
+ FT_GlyphSlot ftglyph;
+
+ FTScalerContext* context =
+ (FTScalerContext*) jlong_to_ptr(pScalerContext);
+ FTScalerInfo *scalerInfo =
+ (FTScalerInfo*) jlong_to_ptr(pScaler);
+
+ if (isNullScalerContext(context) || scalerInfo == NULL) {
+ return ptr_to_jlong(getNullGlyphImage());
+ }
+
+ error = setupFTContext(env, font2D, scalerInfo, context);
+ if (error) {
+ invalidateJavaScaler(env, scaler, scalerInfo);
+ return ptr_to_jlong(getNullGlyphImage());
+ }
+
+ /* if algorithmic styling is required then we do not request bitmap */
+ if (context->doBold || context->doItalize) {
+ renderFlags = FT_LOAD_DEFAULT;
+ }
+
+ /* NB: in case of non identity transform
+ we might also prefer to disable transform before hinting,
+ and apply it explicitly after hinting is performed.
+ Or we can disable hinting. */
+
+ /* select appropriate hinting mode */
+ if (context->aaType == TEXT_AA_OFF) {
+ target = FT_LOAD_TARGET_MONO;
+ } else if (context->aaType == TEXT_AA_ON) {
+ target = FT_LOAD_TARGET_NORMAL;
+ } else if (context->aaType == TEXT_AA_LCD_HRGB ||
+ context->aaType == TEXT_AA_LCD_HBGR) {
+ target = FT_LOAD_TARGET_LCD;
+ } else {
+ target = FT_LOAD_TARGET_LCD_V;
+ }
+ renderFlags |= target;
+
+ glyph_index = FT_Get_Char_Index(scalerInfo->face, glyphCode);
+
+ error = FT_Load_Glyph(scalerInfo->face, glyphCode, renderFlags);
+ if (error) {
+ //do not destroy scaler yet.
+ //this can be problem of particular context (e.g. with bad transform)
+ return ptr_to_jlong(getNullGlyphImage());
+ }
+
+ ftglyph = scalerInfo->face->glyph;
+
+ /* apply styles */
+ if (context->doBold) { /* if bold style */
+ FT_GlyphSlot_Embolden(ftglyph);
+ }
+ if (context->doItalize) { /* if oblique */
+ FT_GlyphSlot_Oblique(ftglyph);
+ }
+
+ /* generate bitmap if it is not done yet
+ e.g. if algorithmic styling is performed and style was added to outline */
+ if (ftglyph->format == FT_GLYPH_FORMAT_OUTLINE) {
+ FT_Render_Glyph(ftglyph, FT_LOAD_TARGET_MODE(target));
+ }
+
+ width = (UInt16) ftglyph->bitmap.width;
+ height = (UInt16) ftglyph->bitmap.rows;
+
+ imageSize = width*height;
+ glyphInfo = (GlyphInfo*) malloc(sizeof(GlyphInfo) + imageSize);
+ if (glyphInfo == NULL) {
+ glyphInfo = getNullGlyphImage();
+ return ptr_to_jlong(glyphInfo);
+ }
+ glyphInfo->cellInfo = NULL;
+ glyphInfo->rowBytes = width;
+ glyphInfo->width = width;
+ glyphInfo->height = height;
+ glyphInfo->topLeftX = (float) ftglyph->bitmap_left;
+ glyphInfo->topLeftY = (float) -ftglyph->bitmap_top;
+
+ if (context->aaType == TEXT_AA_LCD_HRGB ||
+ context->aaType == TEXT_AA_LCD_HBGR) {
+ glyphInfo->width = width/3;
+ } else if (context->aaType == TEXT_AA_LCD_VRGB ||
+ context->aaType == TEXT_AA_LCD_VBGR) {
+ glyphInfo->height = glyphInfo->height/3;
+ }
+
+ if (context->fmType == TEXT_FM_ON) {
+ glyphInfo->advanceX = FT26Dot6ToFloat(ftglyph->advance.x);
+ glyphInfo->advanceY = FT26Dot6ToFloat(-ftglyph->advance.y);
+ } else {
+ glyphInfo->advanceX =
+ (float) ROUND(FT26Dot6ToFloat(ftglyph->advance.x));
+ glyphInfo->advanceY =
+ (float) ROUND(FT26Dot6ToFloat(-ftglyph->advance.y));
+ }
+
+ if (imageSize == 0) {
+ glyphInfo->image = NULL;
+ } else {
+ glyphInfo->image = (unsigned char*) glyphInfo + sizeof(GlyphInfo);
+ //convert result to output format
+ //output format is either 3 bytes per pixel (for subpixel modes)
+ // or 1 byte per pixel for AA and B&W
+ if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
+ /* convert from 8 pixels per byte to 1 byte per pixel */
+ CopyBW2Grey8(ftglyph->bitmap.buffer,
+ ftglyph->bitmap.pitch,
+ (void *) glyphInfo->image,
+ width,
+ width,
+ height);
+ } else if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) {
+ /* byte per pixel to byte per pixel => just copy */
+ memcpy(glyphInfo->image, ftglyph->bitmap.buffer, imageSize);
+ } else if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY4) {
+ /* 4 bits per pixel to byte per pixel */
+ CopyGrey4ToGrey8(ftglyph->bitmap.buffer,
+ ftglyph->bitmap.pitch,
+ (void *) glyphInfo->image,
+ width,
+ width,
+ height);
+ } else if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_LCD) {
+ /* 3 bytes per pixel to 3 bytes per pixel */
+ CopyFTSubpixelToSubpixel(ftglyph->bitmap.buffer,
+ ftglyph->bitmap.pitch,
+ (void *) glyphInfo->image,
+ width,
+ width,
+ height);
+ } else if (ftglyph->bitmap.pixel_mode == FT_PIXEL_MODE_LCD_V) {
+ /* 3 bytes per pixel to 3 bytes per pixel */
+ CopyFTSubpixelVToSubpixel(ftglyph->bitmap.buffer,
+ ftglyph->bitmap.pitch,
+ (void *) glyphInfo->image,
+ width*3,
+ width,
+ height);
+ glyphInfo->rowBytes *=3;
+ } else {
+ free(glyphInfo);
+ glyphInfo = getNullGlyphImage();
+ }
+ }
+
+ 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
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL
+Java_sun_font_FreetypeFontScaler_disposeNativeScaler(
+ JNIEnv *env, jobject scaler, jlong pScaler) {
+ FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
+
+ freeNativeResources(env, scalerInfo);
+}
+
+/*
+ * Class: sun_font_FreetypeFontScaler
+ * Method: getNumGlyphsNative
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL
+Java_sun_font_FreetypeFontScaler_getNumGlyphsNative(
+ JNIEnv *env, jobject scaler, jlong pScaler) {
+ FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
+
+ if (scalerInfo == NULL || scalerInfo->face == NULL) { /* bad/null scaler */
+ /* null scaler can render 1 glyph - "missing glyph" with code 0
+ (all glyph codes requested by user are mapped to code 0 at
+ validation step) */
+ invalidateJavaScaler(env, scaler, scalerInfo);
+ return (jint) 1;
+ }
+
+ return (jint) scalerInfo->face->num_glyphs;
+}
+
+/*
+ * Class: sun_font_FreetypeFontScaler
+ * Method: getMissingGlyphCodeNative
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL
+Java_sun_font_FreetypeFontScaler_getMissingGlyphCodeNative(
+ JNIEnv *env, jobject scaler, jlong pScaler) {
+
+ /* Is it always 0 for freetype? */
+ return 0;
+}
+
+/*
+ * Class: sun_font_FreetypeFontScaler
+ * Method: getGlyphCodeNative
+ * Signature: (C)I
+ */
+JNIEXPORT jint JNICALL
+Java_sun_font_FreetypeFontScaler_getGlyphCodeNative(
+ JNIEnv *env, jobject scaler, jlong pScaler, jchar charCode) {
+
+ FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
+
+ if (scaler == NULL || scalerInfo->face == NULL) { /* bad/null scaler */
+ invalidateJavaScaler(env, scaler, scalerInfo);
+ return 0;
+ }
+
+ return FT_Get_Char_Index(scalerInfo->face, charCode);
+}
+
+
+#define FloatToF26Dot6(x) ((unsigned int) ((x)*64))
+
+static FT_Outline* getFTOutline(JNIEnv* env, jobject font2D,
+ FTScalerContext *context, FTScalerInfo* scalerInfo,
+ jint glyphCode, jfloat xpos, jfloat ypos) {
+ int renderFlags;
+ int glyph_index;
+ FT_Error error;
+ FT_GlyphSlot ftglyph;
+
+ if (glyphCode >= INVISIBLE_GLYPHS ||
+ isNullScalerContext(context) || scalerInfo == NULL) {
+ return NULL;
+ }
+
+ error = setupFTContext(env, font2D, scalerInfo, context);
+ if (error) {
+ return NULL;
+ }
+
+ renderFlags = FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP;
+
+ glyph_index = FT_Get_Char_Index(scalerInfo->face, glyphCode);
+
+ error = FT_Load_Glyph(scalerInfo->face, glyphCode, renderFlags);
+ if (error) {
+ return NULL;
+ }
+
+ ftglyph = scalerInfo->face->glyph;
+
+ /* apply styles */
+ if (context->doBold) { /* if bold style */
+ FT_GlyphSlot_Embolden(ftglyph);
+ }
+ if (context->doItalize) { /* if oblique */
+ FT_GlyphSlot_Oblique(ftglyph);
+ }
+
+ FT_Outline_Translate(&ftglyph->outline,
+ FloatToF26Dot6(xpos),
+ FloatToF26Dot6(ypos));
+
+ return &ftglyph->outline;
+}
+
+#define F26Dot6ToFloat(n) (((float)(n))/((float) 64))
+
+/* Types of GeneralPath segments.
+ TODO: pull constants from other place? */
+
+#define SEG_UNKNOWN -1
+#define SEG_MOVETO 0
+#define SEG_LINETO 1
+#define SEG_QUADTO 2
+#define SEG_CUBICTO 3
+#define SEG_CLOSE 4
+
+#define WIND_NON_ZERO 0
+#define WIND_EVEN_ODD 1
+
+/* Placeholder to accumulate GeneralPath data */
+typedef struct {
+ jint numTypes;
+ jint numCoords;
+ jint lenTypes;
+ jint lenCoords;
+ jint wr;
+ jbyte* pointTypes;
+ jfloat* pointCoords;
+} GPData;
+
+/* returns 0 on failure */
+static int allocateSpaceForGP(GPData* gpdata, int npoints, int ncontours) {
+ int maxTypes, maxCoords;
+
+ /* we may have up to N intermediate points per contour
+ (and for each point can actually cause new curve to be generated)
+ In addition we can also have 2 extra point per outline.
+ */
+ maxTypes = 2*npoints + 2*ncontours;
+ maxCoords = 4*(npoints + 2*ncontours); //we may need to insert
+ //up to n-1 intermediate points
+
+ /* first usage - allocate space and intialize all fields */
+ if (gpdata->pointTypes == NULL || gpdata->pointCoords == NULL) {
+ gpdata->lenTypes = maxTypes;
+ gpdata->lenCoords = maxCoords;
+ gpdata->pointTypes = (jbyte*)
+ malloc(gpdata->lenTypes*sizeof(jbyte));
+ gpdata->pointCoords = (jfloat*)
+ malloc(gpdata->lenCoords*sizeof(jfloat));
+ gpdata->numTypes = 0;
+ gpdata->numCoords = 0;
+ gpdata->wr = WIND_NON_ZERO; /* By default, outlines are filled
+ using the non-zero winding rule. */
+ } else {
+ /* do we have enough space? */
+ if (gpdata->lenTypes - gpdata->numTypes < maxTypes) {
+ gpdata->lenTypes += maxTypes;
+ gpdata->pointTypes = (jbyte*)
+ realloc(gpdata->pointTypes, gpdata->lenTypes*sizeof(jbyte));
+ }
+
+ if (gpdata->lenCoords - gpdata->numCoords < maxCoords) {
+ gpdata->lenCoords += maxCoords;
+ gpdata->pointCoords = (jfloat*)
+ realloc(gpdata->pointCoords, gpdata->lenCoords*sizeof(jfloat));
+ }
+ }
+
+ /* failure if any of mallocs failed */
+ if (gpdata->pointTypes == NULL || gpdata->pointCoords == NULL)
+ return 0;
+ else
+ return 1;
+}
+
+static void addToGP(GPData* gpdata, FT_Outline*outline) {
+ jbyte current_type=SEG_UNKNOWN;
+ int i, j;
+ jfloat x, y;
+
+ j = 0;
+ for(i=0; i<outline->n_points; i++) {
+ x = F26Dot6ToFloat(outline->points[i].x);
+ y = -F26Dot6ToFloat(outline->points[i].y);
+
+ if (FT_CURVE_TAG(outline->tags[i]) == FT_CURVE_TAG_ON) {
+ /* If bit 0 is unset, the point is "off" the curve,
+ i.e., a Bezier control point, while it is "on" when set. */
+ if (current_type == SEG_UNKNOWN) { /* special case:
+ very first point */
+ /* add segment */
+ gpdata->pointTypes[gpdata->numTypes++] = SEG_MOVETO;
+ current_type = SEG_LINETO;
+ } else {
+ gpdata->pointTypes[gpdata->numTypes++] = current_type;
+ current_type = SEG_LINETO;
+ }
+ } else {
+ if (current_type == SEG_UNKNOWN) { /* special case:
+ very first point */
+ if (FT_CURVE_TAG(outline->tags[i+1]) == FT_CURVE_TAG_ON) {
+ /* just skip first point. Adhoc heuristic? */
+ continue;
+ } else {
+ x = (x + F26Dot6ToFloat(outline->points[i+1].x))/2;
+ y = (y - F26Dot6ToFloat(outline->points[i+1].y))/2;
+ gpdata->pointTypes[gpdata->numTypes++] = SEG_MOVETO;
+ current_type = SEG_LINETO;
+ }
+ } else if (FT_CURVE_TAG(outline->tags[i]) == FT_CURVE_TAG_CUBIC) {
+ /* Bit 1 is meaningful for ‘off’ points only.
+ If set, it indicates a third-order Bezier arc control
+ point; and a second-order control point if unset. */
+ current_type = SEG_CUBICTO;
+ } else {
+ /* two successive conic "off" points forces the rasterizer
+ to create (during the scan-line conversion process
+ exclusively) a virtual "on" point amidst them, at their
+ exact middle. This greatly facilitates the definition of
+ successive conic Bezier arcs. Moreover, it is the way
+ outlines are described in the TrueType specification. */
+ if (current_type == SEG_QUADTO) {
+ gpdata->pointCoords[gpdata->numCoords++] =
+ F26Dot6ToFloat(outline->points[i].x +
+ outline->points[i-1].x)/2;
+ gpdata->pointCoords[gpdata->numCoords++] =
+ - F26Dot6ToFloat(outline->points[i].y +
+ outline->points[i-1].y)/2;
+ gpdata->pointTypes[gpdata->numTypes++] = SEG_QUADTO;
+ }
+ current_type = SEG_QUADTO;
+ }
+ }
+ gpdata->pointCoords[gpdata->numCoords++] = x;
+ gpdata->pointCoords[gpdata->numCoords++] = y;
+ if (outline->contours[j] == i) { //end of contour
+ int start = j > 0 ? outline->contours[j-1]+1 : 0;
+ gpdata->pointTypes[gpdata->numTypes++] = current_type;
+ if (current_type == SEG_QUADTO &&
+ FT_CURVE_TAG(outline->tags[start]) != FT_CURVE_TAG_ON) {
+ gpdata->pointCoords[gpdata->numCoords++] =
+ (F26Dot6ToFloat(outline->points[start].x) + x)/2;
+ gpdata->pointCoords[gpdata->numCoords++] =
+ (-F26Dot6ToFloat(outline->points[start].y) + y)/2;
+ } else {
+ gpdata->pointCoords[gpdata->numCoords++] =
+ F26Dot6ToFloat(outline->points[start].x);
+ gpdata->pointCoords[gpdata->numCoords++] =
+ -F26Dot6ToFloat(outline->points[start].y);
+ }
+ gpdata->pointTypes[gpdata->numTypes++] = SEG_CLOSE;
+ current_type = SEG_UNKNOWN;
+ j++;
+ }
+ }
+
+ /* If set to 1, the outline will be filled using the even-odd fill rule */
+ if (outline->flags & FT_OUTLINE_EVEN_ODD_FILL) {
+ gpdata->wr = WIND_EVEN_ODD;
+ }
+}
+
+static void freeGP(GPData* gpdata) {
+ if (gpdata->pointCoords != NULL) {
+ free(gpdata->pointCoords);
+ gpdata->pointCoords = NULL;
+ gpdata->numCoords = 0;
+ gpdata->lenCoords = 0;
+ }
+ if (gpdata->pointTypes != NULL) {
+ free(gpdata->pointTypes);
+ gpdata->pointTypes = NULL;
+ gpdata->numTypes = 0;
+ gpdata->lenTypes = 0;
+ }
+}
+
+static jobject getGlyphGeneralPath(JNIEnv* env, jobject font2D,
+ FTScalerContext *context, FTScalerInfo *scalerInfo,
+ jint glyphCode, jfloat xpos, jfloat ypos) {
+
+ FT_Outline* outline;
+ jobject gp = NULL;
+ jbyteArray types;
+ jfloatArray coords;
+ GPData gpdata;
+
+ outline = getFTOutline(env, font2D, context, scalerInfo,
+ glyphCode, xpos, ypos);
+
+ if (outline == NULL || outline->n_points == 0) {
+ return gp;
+ }
+
+ gpdata.pointTypes = NULL;
+ gpdata.pointCoords = NULL;
+ if (!allocateSpaceForGP(&gpdata, outline->n_points, outline->n_contours)) {
+ return gp;
+ }
+
+ addToGP(&gpdata, outline);
+
+ types = (*env)->NewByteArray(env, gpdata.numTypes);
+ coords = (*env)->NewFloatArray(env, gpdata.numCoords);
+
+ if (types && coords) {
+ (*env)->SetByteArrayRegion(env, types, 0,
+ gpdata.numTypes,
+ gpdata.pointTypes);
+ (*env)->SetFloatArrayRegion(env, coords, 0,
+ gpdata.numCoords,
+ gpdata.pointCoords);
+ gp = (*env)->NewObject(env,
+ sunFontIDs.gpClass,
+ sunFontIDs.gpCtr,
+ gpdata.wr,
+ types,
+ gpdata.numTypes,
+ coords,
+ gpdata.numCoords);
+ }
+
+ freeGP(&gpdata);
+
+ return gp;
+}
+
+/*
+ * Class: sun_font_FreetypeFontScaler
+ * Method: getGlyphOutlineNative
+ * Signature: (Lsun/font/Font2D;JIFF)Ljava/awt/geom/GeneralPath;
+ */
+JNIEXPORT jobject JNICALL
+Java_sun_font_FreetypeFontScaler_getGlyphOutlineNative(
+ JNIEnv *env, jobject scaler, jobject font2D, jlong pScalerContext,
+ jlong pScaler, jint glyphCode, jfloat xpos, jfloat ypos) {
+
+ FTScalerContext *context =
+ (FTScalerContext*) jlong_to_ptr(pScalerContext);
+ FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
+
+ jobject gp = getGlyphGeneralPath(env,
+ font2D,
+ context,
+ scalerInfo,
+ glyphCode,
+ xpos,
+ ypos);
+ if (gp == NULL) { /* can be legal */
+ gp = (*env)->NewObject(env,
+ sunFontIDs.gpClass,
+ sunFontIDs.gpCtrEmpty);
+ }
+ return gp;
+}
+
+/*
+ * Class: sun_font_FreetypeFontScaler
+ * Method: getGlyphOutlineBoundsNative
+ * Signature: (Lsun/font/Font2D;JI)Ljava/awt/geom/Rectangle2D/Float;
+ */
+JNIEXPORT jobject JNICALL
+Java_sun_font_FreetypeFontScaler_getGlyphOutlineBoundsNative(
+ JNIEnv *env, jobject scaler, jobject font2D,
+ jlong pScalerContext, jlong pScaler, jint glyphCode) {
+
+ FT_Outline *outline;
+ FT_BBox bbox;
+ int error;
+ jobject bounds;
+
+ FTScalerContext *context =
+ (FTScalerContext*) jlong_to_ptr(pScalerContext);
+ FTScalerInfo* scalerInfo = (FTScalerInfo *) jlong_to_ptr(pScaler);
+
+ outline = getFTOutline(env, font2D, context, scalerInfo, glyphCode, 0, 0);
+ if (outline == NULL || outline->n_points == 0) {
+ /* it is legal case, e.g. invisible glyph */
+ bounds = (*env)->NewObject(env,
+ sunFontIDs.rect2DFloatClass,
+ sunFontIDs.rect2DFloatCtr);
+ return bounds;
+ }
+
+ error = FT_Outline_Get_BBox(outline, &bbox);
+
+ //convert bbox
+ if (error || bbox.xMin >= bbox.xMax || bbox.yMin >= bbox.yMax) {
+ bounds = (*env)->NewObject(env,
+ sunFontIDs.rect2DFloatClass,
+ sunFontIDs.rect2DFloatCtr);
+ } else {
+ bounds = (*env)->NewObject(env,
+ sunFontIDs.rect2DFloatClass,
+ sunFontIDs.rect2DFloatCtr4,
+ F26Dot6ToFloat(bbox.xMin),
+ F26Dot6ToFloat(bbox.yMax),
+ F26Dot6ToFloat(bbox.xMax-bbox.xMin),
+ F26Dot6ToFloat(bbox.yMax-bbox.yMin));
+ }
+
+ return bounds;
+}
+
+/*
+ * Class: sun_font_FreetypeFontScaler
+ * Method: getGlyphVectorOutlineNative
+ * Signature: (Lsun/font/Font2D;J[IIFF)Ljava/awt/geom/GeneralPath;
+ */
+JNIEXPORT jobject
+JNICALL
+Java_sun_font_FreetypeFontScaler_getGlyphVectorOutlineNative(
+ JNIEnv *env, jobject scaler, jobject font2D,
+ jlong pScalerContext, jlong pScaler,
+ jintArray glyphArray, jint numGlyphs, jfloat xpos, jfloat ypos) {
+
+ FT_Outline* outline;
+ jobject gp = NULL;
+ jbyteArray types;
+ jfloatArray coords;
+ GPData gpdata;
+ int i;
+ jint *glyphs;
+
+ FTScalerContext *context =
+ (FTScalerContext*) jlong_to_ptr(pScalerContext);
+ FTScalerInfo *scalerInfo =
+ (FTScalerInfo*) jlong_to_ptr(pScaler);
+
+ glyphs = (jint*) malloc(numGlyphs*sizeof(jint));
+ if (glyphs == NULL) {
+ gp = (*env)->NewObject(env, sunFontIDs.gpClass, sunFontIDs.gpCtrEmpty);
+ if (!isNullScalerContext(context) && scalerInfo != NULL) {
+ invalidateJavaScaler(env, scaler, scalerInfo);
+ }
+ return gp;
+ }
+
+ (*env)->GetIntArrayRegion(env, glyphArray, 0, numGlyphs, glyphs);
+
+ for (i=0; i<numGlyphs;i++) {
+ if (glyphs[i] >= INVISIBLE_GLYPHS) {
+ continue;
+ }
+ outline = getFTOutline(env,
+ font2D,
+ context,
+ scalerInfo,
+ glyphs[i],
+ xpos, ypos);
+
+ if (outline == NULL || outline->n_points == 0) {
+ continue;
+ }
+
+ gpdata.pointTypes = NULL;
+ gpdata.pointCoords = NULL;
+ if (!allocateSpaceForGP(&gpdata, outline->n_points,
+ outline->n_contours)) {
+ break;
+ }
+
+ addToGP(&gpdata, outline);
+ }
+ free(glyphs);
+
+ if (gpdata.numCoords != 0) {
+ types = (*env)->NewByteArray(env, gpdata.numTypes);
+ coords = (*env)->NewFloatArray(env, gpdata.numCoords);
+
+ if (types && coords) {
+ (*env)->SetByteArrayRegion(env, types, 0,
+ gpdata.numTypes, gpdata.pointTypes);
+ (*env)->SetFloatArrayRegion(env, coords, 0,
+ gpdata.numCoords, gpdata.pointCoords);
+
+ gp=(*env)->NewObject(env,
+ sunFontIDs.gpClass,
+ sunFontIDs.gpCtr,
+ gpdata.wr,
+ types,
+ gpdata.numTypes,
+ coords,
+ gpdata.numCoords);
+ return gp;
+ }
+ }
+ return (*env)->NewObject(env, sunFontIDs.gpClass, sunFontIDs.gpCtrEmpty);
+}
+
+JNIEXPORT jlong JNICALL
+Java_sun_font_FreetypeFontScaler_getUnitsPerEMNative(
+ JNIEnv *env, jobject scaler, jlong pScaler) {
+
+ FTScalerInfo *s = (FTScalerInfo* ) jlong_to_ptr(pScaler);
+
+ /* Freetype doc says:
+ The number of font units per EM square for this face.
+ This is typically 2048 for TrueType fonts, and 1000 for Type 1 fonts.
+ Only relevant for scalable formats.
+ However, layout engine might be not tested with anything but 2048.
+
+ NB: test it! */
+ if (s != NULL) {
+ return s->face->units_per_EM;
+ }
+ return 2048;
+}
+
+/* This native method is called by the OpenType layout engine. */
+JNIEXPORT jobject JNICALL
+Java_sun_font_FreetypeFontScaler_getGlyphPointNative(
+ JNIEnv *env, jobject scaler, jobject font2D, jlong pScalerContext,
+ jlong pScaler, jint glyphCode, jint pointNumber) {
+
+ FT_Outline* outline;
+ jobject point = NULL;
+ jfloat x=0, y=0;
+ FTScalerContext *context =
+ (FTScalerContext*) jlong_to_ptr(pScalerContext);
+ FTScalerInfo *scalerInfo = (FTScalerInfo*) jlong_to_ptr(pScaler);
+
+ outline = getFTOutline(env, font2D, context, scalerInfo, glyphCode, 0, 0);
+
+ if (outline != NULL && outline->n_points > pointNumber) {
+ x = F26Dot6ToFloat(outline->points[pointNumber].x);
+ y = -F26Dot6ToFloat(outline->points[pointNumber].y);
+ }
+
+ return (*env)->NewObject(env, sunFontIDs.pt2DFloatClass,
+ sunFontIDs.pt2DFloatCtr, x, y);
+}