blob: 86d2c0bf0a0d01804d1edf430f7a22f857fd4928 [file] [log] [blame]
/*
* Copyright (c) 2011, 2014, Oracle and/or its affiliates. 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. 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.
*/
#import <JavaNativeFoundation/JavaNativeFoundation.h>
#import "java_awt_geom_PathIterator.h"
#import "sun_font_CStrike.h"
#import "sun_font_CStrikeDisposer.h"
#import "CGGlyphImages.h"
#import "CGGlyphOutlines.h"
#import "CoreTextSupport.h"
#include "fontscalerdefs.h"
/* Use THIS_FILE when it is available. */
#ifndef THIS_FILE
#define THIS_FILE __FILE__
#endif
@implementation AWTStrike
static CGAffineTransform sInverseTX = { 1, 0, 0, -1, 0, 0 };
- (id) initWithFont:(AWTFont *)awtFont
tx:(CGAffineTransform)tx
invDevTx:(CGAffineTransform)invDevTx
style:(JRSFontRenderingStyle)style
aaStyle:(jint)aaStyle
fmHint:(jint)fmHint {
self = [super init];
if (self) {
fAWTFont = [awtFont retain];
fStyle = style;
fAAStyle = aaStyle;
fFmHint = fmHint;
fTx = tx; // composited glyph and device transform
fAltTx = tx;
fAltTx.b *= -1;
fAltTx.d *= -1;
invDevTx.b *= -1;
invDevTx.c *= -1;
fFontTx = CGAffineTransformConcat(CGAffineTransformConcat(tx, invDevTx), sInverseTX);
fDevTx = CGAffineTransformInvert(CGAffineTransformConcat(invDevTx, sInverseTX));
// the "font size" is the square root of the determinant of the matrix
fSize = sqrt(fabs(fFontTx.a * fFontTx.d - fFontTx.b * fFontTx.c));
}
return self;
}
- (void) dealloc {
[fAWTFont release];
fAWTFont = nil;
[super dealloc];
}
+ (AWTStrike *) awtStrikeForFont:(AWTFont *)awtFont
tx:(CGAffineTransform)tx
invDevTx:(CGAffineTransform)invDevTx
style:(JRSFontRenderingStyle)style
aaStyle:(jint)aaStyle
fmHint:(jint)fmHint {
return [[[AWTStrike alloc] initWithFont:awtFont
tx:tx invDevTx:invDevTx
style:style
aaStyle:aaStyle
fmHint:fmHint] autorelease];
}
@end
#define AWT_FONT_CLEANUP_SETUP \
BOOL _fontThrowJavaException = NO;
#define AWT_FONT_CLEANUP_CHECK(a) \
if ((a) == NULL) { \
_fontThrowJavaException = YES; \
goto cleanup; \
} \
if ((*env)->ExceptionCheck(env) == JNI_TRUE) { \
goto cleanup; \
}
#define AWT_FONT_CLEANUP_FINISH \
if (_fontThrowJavaException == YES) { \
char s[512]; \
sprintf(s, "%s-%s:%d", THIS_FILE, __FUNCTION__, __LINE__); \
[JNFException raise:env as:kRuntimeException reason:s]; \
}
/*
* Creates an affine transform from the corresponding doubles sent
* from CStrike.getGlyphTx().
*/
static inline CGAffineTransform
GetTxFromDoubles(JNIEnv *env, jdoubleArray txArray)
{
if (txArray == NULL) {
return CGAffineTransformIdentity;
}
jdouble *txPtr = (*env)->GetPrimitiveArrayCritical(env, txArray, NULL);
if (txPtr == NULL) {
return CGAffineTransformIdentity;
}
CGAffineTransform tx =
CGAffineTransformMake(txPtr[0], txPtr[1], txPtr[2],
txPtr[3], txPtr[4], txPtr[5]);
tx = CGAffineTransformConcat(sInverseTX, tx);
(*env)->ReleasePrimitiveArrayCritical(env, txArray, txPtr, JNI_ABORT);
return tx;
}
/*
* Class: sun_font_CStrike
* Method: getNativeGlyphAdvance
* Signature: (JI)F
*/
JNIEXPORT jfloat JNICALL
Java_sun_font_CStrike_getNativeGlyphAdvance
(JNIEnv *env, jclass clazz, jlong awtStrikePtr, jint glyphCode)
{
CGSize advance;
JNF_COCOA_ENTER(env);
AWTStrike *awtStrike = (AWTStrike *)jlong_to_ptr(awtStrikePtr);
AWTFont *awtFont = awtStrike->fAWTFont;
// negative glyph codes are really unicodes, which were placed there by the mapper
// to indicate we should use CoreText to substitute the character
CGGlyph glyph;
const CTFontRef fallback = CTS_CopyCTFallbackFontAndGlyphForJavaGlyphCode(awtFont, glyphCode, &glyph);
const CGFontRef cgFallback = CTFontCopyGraphicsFont(fallback, NULL);
if (CGGI_IsColorFont(cgFallback)) {
CGAffineTransform matrix = awtStrike->fAltTx;
CGFloat fontSize = sqrt(fabs(matrix.a * matrix.d - matrix.b * matrix.c));
CTFontRef font = CTFontCreateWithGraphicsFont(cgFallback, fontSize, NULL, NULL);
CTFontGetAdvancesForGlyphs(font, kCTFontDefaultOrientation, &glyph, &advance, 1);
CFRelease(font);
advance.width /= fontSize;
advance.height /= fontSize;
} else {
CTFontGetAdvancesForGlyphs(fallback, kCTFontDefaultOrientation, &glyph, &advance, 1);
}
CFRelease(cgFallback);
CFRelease(fallback);
advance = CGSizeApplyAffineTransform(advance, awtStrike->fFontTx);
if (!JRSFontStyleUsesFractionalMetrics(awtStrike->fStyle)) {
advance.width = round(advance.width);
}
JNF_COCOA_EXIT(env);
return advance.width;
}
/*
* Class: sun_font_CStrike
* Method: getNativeGlyphImageBounds
* Signature: (JJILjava/awt/geom/Rectangle2D/Float;DD)V
*/
JNIEXPORT void JNICALL
Java_sun_font_CStrike_getNativeGlyphImageBounds
(JNIEnv *env, jclass clazz,
jlong awtStrikePtr, jint glyphCode,
jobject result /*Rectangle*/, jdouble x, jdouble y)
{
JNF_COCOA_ENTER(env);
AWTStrike *awtStrike = (AWTStrike *)jlong_to_ptr(awtStrikePtr);
AWTFont *awtFont = awtStrike->fAWTFont;
CGAffineTransform tx = awtStrike->fAltTx;
tx.tx += x;
tx.ty += y;
// negative glyph codes are really unicodes, which were placed there by the mapper
// to indicate we should use CoreText to substitute the character
CGGlyph glyph;
const CTFontRef fallback = CTS_CopyCTFallbackFontAndGlyphForJavaGlyphCode(awtFont, glyphCode, &glyph);
CGRect bbox;
JRSFontGetBoundingBoxesForGlyphsAndStyle(fallback, &tx, awtStrike->fStyle, &glyph, 1, &bbox);
CFRelease(fallback);
// the origin of this bounding box is relative to the bottom-left corner baseline
CGFloat decender = -bbox.origin.y;
bbox.origin.y = -bbox.size.height + decender;
// Rectangle2D.Float.setRect(float x, float y, float width, float height);
static JNF_CLASS_CACHE(sjc_Rectangle2D_Float, "java/awt/geom/Rectangle2D$Float"); // cache class id for Rectangle
static JNF_MEMBER_CACHE(sjr_Rectangle2DFloat_setRect, sjc_Rectangle2D_Float, "setRect", "(FFFF)V");
JNFCallVoidMethod(env, result, sjr_Rectangle2DFloat_setRect, (jfloat)bbox.origin.x, (jfloat)bbox.origin.y, (jfloat)bbox.size.width, (jfloat)bbox.size.height);
JNF_COCOA_EXIT(env);
}
/*
* Class: sun_font_CStrike
* Method: getNativeGlyphOutline
* Signature: (JJIDD)Ljava/awt/geom/GeneralPath;
*/
JNIEXPORT jobject JNICALL
Java_sun_font_CStrike_getNativeGlyphOutline
(JNIEnv *env, jclass clazz,
jlong awtStrikePtr, jint glyphCode, jdouble xPos, jdouble yPos)
{
jobject generalPath = NULL;
JNF_COCOA_ENTER(env);
AWTPathRef path = NULL;
jfloatArray pointCoords = NULL;
jbyteArray pointTypes = NULL;
AWT_FONT_CLEANUP_SETUP;
AWTStrike *awtStrike = (AWTStrike *)jlong_to_ptr(awtStrikePtr);
AWTFont *awtfont = awtStrike->fAWTFont;
AWT_FONT_CLEANUP_CHECK(awtfont);
// inverting the shear order and sign to compensate for the flipped coordinate system
CGAffineTransform tx = awtStrike->fTx;
tx.tx += xPos;
tx.ty += yPos;
// get the right font and glyph for this "Java GlyphCode"
CGGlyph glyph;
const CTFontRef font = CTS_CopyCTFallbackFontAndGlyphForJavaGlyphCode(awtfont, glyphCode, &glyph);
// get the advance of this glyph
CGSize advance;
CTFontGetAdvancesForGlyphs(font, kCTFontDefaultOrientation, &glyph, &advance, 1);
// Create AWTPath
path = AWTPathCreate(CGSizeMake(xPos, yPos));
AWT_FONT_CLEANUP_CHECK(path);
// Get the paths
tx = awtStrike->fTx;
tx = CGAffineTransformConcat(tx, sInverseTX);
AWTGetGlyphOutline(&glyph, (NSFont *)font, &advance, &tx, 0, 1, &path);
CFRelease(font);
pointCoords = (*env)->NewFloatArray(env, path->fNumberOfDataElements);
AWT_FONT_CLEANUP_CHECK(pointCoords);
(*env)->SetFloatArrayRegion(env, pointCoords, 0, path->fNumberOfDataElements, (jfloat*)path->fSegmentData);
// Copy the pointTypes to the general path
pointTypes = (*env)->NewByteArray(env, path->fNumberOfSegments);
AWT_FONT_CLEANUP_CHECK(pointTypes);
(*env)->SetByteArrayRegion(env, pointTypes, 0, path->fNumberOfSegments, (jbyte*)path->fSegmentType);
static JNF_CLASS_CACHE(jc_GeneralPath, "java/awt/geom/GeneralPath");
static JNF_CTOR_CACHE(jc_GeneralPath_ctor, jc_GeneralPath, "(I[BI[FI)V");
generalPath = JNFNewObject(env, jc_GeneralPath_ctor, java_awt_geom_PathIterator_WIND_NON_ZERO, pointTypes, path->fNumberOfSegments, pointCoords, path->fNumberOfDataElements); // AWT_THREADING Safe (known object)
// Cleanup
cleanup:
if (path != NULL) {
AWTPathFree(path);
path = NULL;
}
if (pointCoords != NULL) {
(*env)->DeleteLocalRef(env, pointCoords);
pointCoords = NULL;
}
if (pointTypes != NULL) {
(*env)->DeleteLocalRef(env, pointTypes);
pointTypes = NULL;
}
AWT_FONT_CLEANUP_FINISH;
JNF_COCOA_EXIT(env);
return generalPath;
}
/*
* Class: sun_font_CStrike
* Method: getNativeGlyphOutlineBounds
* Signature: (JILjava/awt/geom/Rectangle2D/Float;DD)V
*/
JNIEXPORT void JNICALL Java_sun_font_CStrike_getNativeGlyphOutlineBounds
(JNIEnv *env, jclass clazz, jlong awtStrikePtr, jint glyphCode,
jobject result, jdouble xPos, jdouble yPos)
{
JNF_COCOA_ENTER(env);
AWTStrike *awtStrike = (AWTStrike *)jlong_to_ptr(awtStrikePtr);
AWTFont *awtfont = awtStrike->fAWTFont;
AWT_FONT_CLEANUP_SETUP;
AWT_FONT_CLEANUP_CHECK(awtfont);
// get the right font and glyph for this "Java GlyphCode"
CGGlyph glyph;
const CTFontRef font = CTS_CopyCTFallbackFontAndGlyphForJavaGlyphCode(
awtfont, glyphCode, &glyph);
CGAffineTransform tx = CGAffineTransformConcat(awtStrike->fTx,
sInverseTX);
CGPathRef cgPath = CTFontCreatePathForGlyph((CTFontRef) font, glyph,
&tx);
CGRect bbox = CGPathGetPathBoundingBox(cgPath);
CFRelease(font);
CGPathRelease(cgPath);
if (CGRectIsNull(bbox)) {
bbox.origin.x = 0;
bbox.origin.y = 0;
bbox.size.width = 0;
bbox.size.height = 0;
}
static JNF_CLASS_CACHE(sjc_Rectangle2D_Float,
"java/awt/geom/Rectangle2D$Float");
static JNF_MEMBER_CACHE(sjr_Rectangle2DFloat_setRect,
sjc_Rectangle2D_Float, "setRect", "(FFFF)V");
JNFCallVoidMethod(env, result, sjr_Rectangle2DFloat_setRect,
(jfloat) (bbox.origin.x + xPos),
(jfloat) (yPos - bbox.origin.y - bbox.size.height),
(jfloat) bbox.size.width,
(jfloat) bbox.size.height);
// Cleanup
cleanup:
AWT_FONT_CLEANUP_FINISH;
JNF_COCOA_EXIT(env);
}
/*
* Class: sun_font_CStrike
* Method: getGlyphImagePtrsNative
* Signature: (JJ[J[II)V
*/
JNIEXPORT void JNICALL
Java_sun_font_CStrike_getGlyphImagePtrsNative
(JNIEnv *env, jclass clazz,
jlong awtStrikePtr, jlongArray glyphInfoLongArray,
jintArray glyphCodes, jint len)
{
JNF_COCOA_ENTER(env);
AWTStrike *awtStrike = (AWTStrike *)jlong_to_ptr(awtStrikePtr);
jlong *glyphInfos =
(*env)->GetPrimitiveArrayCritical(env, glyphInfoLongArray, NULL);
jint *rawGlyphCodes =
(*env)->GetPrimitiveArrayCritical(env, glyphCodes, NULL);
@try {
if (rawGlyphCodes != NULL && glyphInfos != NULL) {
CGGlyphImages_GetGlyphImagePtrs(glyphInfos, awtStrike,
rawGlyphCodes, len);
}
}
@finally {
if (rawGlyphCodes != NULL) {
(*env)->ReleasePrimitiveArrayCritical(env, glyphCodes,
rawGlyphCodes, JNI_ABORT);
}
if (glyphInfos != NULL) {
// Do not use JNI_COMMIT, as that will not free the buffer copy
// when +ProtectJavaHeap is on.
(*env)->ReleasePrimitiveArrayCritical(env, glyphInfoLongArray,
glyphInfos, 0);
}
}
JNF_COCOA_EXIT(env);
}
/*
* Class: sun_font_CStrike
* Method: createNativeStrikePtr
* Signature: (J[D[DII)J
*/
JNIEXPORT jlong JNICALL Java_sun_font_CStrike_createNativeStrikePtr
(JNIEnv *env, jclass clazz, jlong nativeFontPtr, jdoubleArray glyphTxArray, jdoubleArray invDevTxArray, jint aaStyle, jint fmHint)
{
AWTStrike *awtStrike = nil;
JNF_COCOA_ENTER(env);
AWTFont *awtFont = (AWTFont *)jlong_to_ptr(nativeFontPtr);
JRSFontRenderingStyle style = JRSFontGetRenderingStyleForHints(fmHint, aaStyle);
CGAffineTransform glyphTx = GetTxFromDoubles(env, glyphTxArray);
CGAffineTransform invDevTx = GetTxFromDoubles(env, invDevTxArray);
awtStrike = [AWTStrike awtStrikeForFont:awtFont tx:glyphTx invDevTx:invDevTx style:style aaStyle:aaStyle fmHint:fmHint]; // autoreleased
if (awtStrike)
{
CFRetain(awtStrike); // GC
}
JNF_COCOA_EXIT(env);
return ptr_to_jlong(awtStrike);
}
/*
* Class: sun_font_CStrike
* Method: disposeNativeStrikePtr
* Signature: (J)V
*/
JNIEXPORT void JNICALL
Java_sun_font_CStrike_disposeNativeStrikePtr
(JNIEnv *env, jclass clazz, jlong awtStrike)
{
JNF_COCOA_ENTER(env);
if (awtStrike) {
CFRelease((AWTStrike *)jlong_to_ptr(awtStrike)); // GC
}
JNF_COCOA_EXIT(env);
}
/*
* Class: sun_font_CStrike
* Method: getFontMetrics
* Signature: (J)Lsun/font/StrikeMetrics;
*/
JNIEXPORT jobject JNICALL
Java_sun_font_CStrike_getFontMetrics
(JNIEnv *env, jclass clazz, jlong awtStrikePtr)
{
jobject metrics = NULL;
JNF_COCOA_ENTER(env);
AWT_FONT_CLEANUP_SETUP;
AWTFont *awtfont = ((AWTStrike *)jlong_to_ptr(awtStrikePtr))->fAWTFont;
AWT_FONT_CLEANUP_CHECK(awtfont);
CGFontRef cgFont = awtfont->fNativeCGFont;
jfloat ay=0.0, dy=0.0, mx=0.0, ly=0.0;
int unitsPerEm = CGFontGetUnitsPerEm(cgFont);
CGFloat scaleX = (1.0 / unitsPerEm);
CGFloat scaleY = (1.0 / unitsPerEm);
// Ascent
ay = -(CGFloat)CGFontGetAscent(cgFont) * scaleY;
// Descent
dy = -(CGFloat)CGFontGetDescent(cgFont) * scaleY;
// Leading
ly = (CGFloat)CGFontGetLeading(cgFont) * scaleY;
// Max Advance for Font Direction (Strictly horizontal)
mx = [awtfont->fFont maximumAdvancement].width;
/*
* ascent: no need to set ascentX - it will be zero.
* descent: no need to set descentX - it will be zero.
* baseline: old releases "made up" a number and also seemed to
* make it up for "X" and set "Y" to 0.
* leadingX: no need to set leadingX - it will be zero.
* leadingY: made-up number, but being compatible with what 1.4.x did.
* advance: no need to set yMaxLinearAdvanceWidth - it will be zero.
*/
JNF_CLASS_CACHE(sjc_StrikeMetrics, "sun/font/StrikeMetrics");
JNF_CTOR_CACHE(strikeMetricsCtr, sjc_StrikeMetrics, "(FFFFFFFFFF)V");
metrics = JNFNewObject(env, strikeMetricsCtr,
0.0, ay, 0.0, dy, 1.0,
0.0, 0.0, ly, mx, 0.0);
cleanup:
AWT_FONT_CLEANUP_FINISH;
JNF_COCOA_EXIT(env);
return metrics;
}
extern void AccelGlyphCache_RemoveAllInfos(GlyphInfo* glyph);
/*
* Class: sun_font_CStrikeDisposer
* Method: removeGlyphInfoFromCache
* Signature: (J)V
*/
JNIEXPORT void JNICALL Java_sun_font_CStrikeDisposer_removeGlyphInfoFromCache
(JNIEnv *env, jclass cls, jlong glyphInfo)
{
JNF_COCOA_ENTER(env);
AccelGlyphCache_RemoveAllCellInfos((GlyphInfo*)jlong_to_ptr(glyphInfo));
JNF_COCOA_EXIT(env);
}