1 /* 2 * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 #import <JavaNativeFoundation/JavaNativeFoundation.h> 27 #import "java_awt_geom_PathIterator.h" 28 #import "sun_awt_SunHints.h" 29 #import "sun_font_CStrike.h" 30 #import "CGGlyphImages.h" 31 #import "CGGlyphOutlines.h" 32 #import "AWTStrike.h" 33 #import "CoreTextSupport.h" 34 //#import "jni_util.h" 35 36 /* Use THIS_FILE when it is available. */ 37 #ifndef THIS_FILE 38 #define THIS_FILE __FILE__ 39 #endif 40 41 @implementation AWTStrike 42 43 static CGAffineTransform sInverseTX = { 1, 0, 0, -1, 0, 0 }; 44 45 - (id) initWithFont:(AWTFont *)awtFont 46 tx:(CGAffineTransform)tx 47 invDevTx:(CGAffineTransform)invDevTx 48 style:(JRSFontRenderingStyle)style 49 aaStyle:(jint)aaStyle { 50 51 self = [super init]; 52 if (self) { 53 fAWTFont = [awtFont retain]; 54 fStyle = style; 55 fAAStyle = aaStyle; 56 57 fTx = tx; // composited glyph and device transform 58 59 fAltTx = tx; 60 fAltTx.b *= -1; 61 fAltTx.d *= -1; 62 63 invDevTx.b *= -1; 64 invDevTx.c *= -1; 65 fFontTx = CGAffineTransformConcat(CGAffineTransformConcat(tx, invDevTx), sInverseTX); 66 67 // the "font size" is the square root of the determinant of the matrix 68 fSize = sqrt(abs(fFontTx.a * fFontTx.d - fFontTx.b * fFontTx.c)); 69 } 70 return self; 71 } 72 73 - (void) dealloc { 74 [fAWTFont release]; 75 fAWTFont = nil; 76 77 [super dealloc]; 78 } 79 80 + (AWTStrike *) awtStrikeForFont:(AWTFont *)awtFont 81 tx:(CGAffineTransform)tx 82 invDevTx:(CGAffineTransform)invDevTx 83 style:(JRSFontRenderingStyle)style 84 aaStyle:(jint)aaStyle { 85 86 return [[[AWTStrike alloc] initWithFont:awtFont 87 tx:tx invDevTx:invDevTx 88 style:style 89 aaStyle:aaStyle] autorelease]; 90 } 91 92 @end 93 94 95 #define AWT_FONT_CLEANUP_SETUP \ 96 BOOL _fontThrowJavaException = NO; 97 98 #define AWT_FONT_CLEANUP_CHECK(a) \ 99 if ((a) == NULL) { \ 100 _fontThrowJavaException = YES; \ 101 goto cleanup; \ 102 } \ 103 if ((*env)->ExceptionCheck(env) == JNI_TRUE) { \ 104 goto cleanup; \ 105 } 106 107 #define AWT_FONT_CLEANUP_FINISH \ 108 if (_fontThrowJavaException == YES) { \ 109 char s[512]; \ 110 sprintf(s, "%s-%s:%d", THIS_FILE, __FUNCTION__, __LINE__); \ 111 [JNFException raise:env as:kRuntimeException reason:s]; \ 112 } 113 114 115 /* 116 * Creates an affine transform from the corresponding doubles sent 117 * from CStrike.getGlyphTx(). 118 */ 119 static inline CGAffineTransform 120 GetTxFromDoubles(JNIEnv *env, jdoubleArray txArray) 121 { 122 if (txArray == NULL) { 123 return CGAffineTransformIdentity; 124 } 125 126 jdouble *txPtr = (*env)->GetPrimitiveArrayCritical(env, txArray, NULL); 127 128 CGAffineTransform tx = 129 CGAffineTransformMake(txPtr[0], txPtr[1], txPtr[2], 130 txPtr[3], txPtr[4], txPtr[5]); 131 tx = CGAffineTransformConcat(sInverseTX, tx); 132 133 (*env)->ReleasePrimitiveArrayCritical(env, txArray, txPtr, JNI_ABORT); 134 135 return tx; 136 } 137 138 /* 139 * Class: sun_font_CStrike 140 * Method: getNativeGlyphAdvance 141 * Signature: (JI)F 142 */ 143 JNIEXPORT jfloat JNICALL 144 Java_sun_font_CStrike_getNativeGlyphAdvance 145 (JNIEnv *env, jclass clazz, jlong awtStrikePtr, jint glyphCode) 146 { 147 CGSize advance; 148 JNF_COCOA_ENTER(env); 149 AWTFont *awtFont = ((AWTStrike *)jlong_to_ptr(awtStrikePtr))->fAWTFont; 150 151 // negative glyph codes are really unicodes, which were placed there by the mapper 152 // to indicate we should use CoreText to substitute the character 153 CGGlyph glyph; 154 const CTFontRef fallback = CTS_CopyCTFallbackFontAndGlyphForJavaGlyphCode(awtFont, glyphCode, &glyph); 155 CTFontGetAdvancesForGlyphs(fallback, kCTFontDefaultOrientation, &glyph, &advance, 1); 156 CFRelease(fallback); 157 158 JNF_COCOA_EXIT(env); 159 return advance.width; 160 } 161 162 /* 163 * Class: sun_font_CStrike 164 * Method: getNativeGlyphImageBounds 165 * Signature: (JJILjava/awt/geom/Rectangle2D/Float;DD)V 166 */ 167 JNIEXPORT void JNICALL 168 Java_sun_font_CStrike_getNativeGlyphImageBounds 169 (JNIEnv *env, jclass clazz, 170 jlong awtStrikePtr, jint glyphCode, 171 jobject result /*Rectangle*/, jdouble x, jdouble y) 172 { 173 JNF_COCOA_ENTER(env); 174 175 AWTStrike *awtStrike = (AWTStrike *)jlong_to_ptr(awtStrikePtr); 176 AWTFont *awtFont = awtStrike->fAWTFont; 177 178 CGAffineTransform tx = awtStrike->fAltTx; 179 tx.tx += x; 180 tx.ty += y; 181 182 // negative glyph codes are really unicodes, which were placed there by the mapper 183 // to indicate we should use CoreText to substitute the character 184 CGGlyph glyph; 185 const CTFontRef fallback = CTS_CopyCTFallbackFontAndGlyphForJavaGlyphCode(awtFont, glyphCode, &glyph); 186 187 CGRect bbox; 188 JRSFontGetBoundingBoxesForGlyphsAndStyle(fallback, &tx, awtStrike->fStyle, &glyph, 1, &bbox); 189 CFRelease(fallback); 190 191 // the origin of this bounding box is relative to the bottom-left corner baseline 192 CGFloat decender = -bbox.origin.y; 193 bbox.origin.y = -bbox.size.height + decender; 194 195 // Rectangle2D.Float.setRect(float x, float y, float width, float height); 196 static JNF_CLASS_CACHE(sjc_Rectangle2D_Float, "java/awt/geom/Rectangle2D$Float"); // cache class id for Rectangle 197 static JNF_MEMBER_CACHE(sjr_Rectangle2DFloat_setRect, sjc_Rectangle2D_Float, "setRect", "(FFFF)V"); 198 JNFCallVoidMethod(env, result, sjr_Rectangle2DFloat_setRect, (jfloat)bbox.origin.x, (jfloat)bbox.origin.y, (jfloat)bbox.size.width, (jfloat)bbox.size.height); 199 200 JNF_COCOA_EXIT(env); 201 } 202 203 /* 204 * Class: sun_font_CStrike 205 * Method: getNativeGlyphOutline 206 * Signature: (JJIDD)Ljava/awt/geom/GeneralPath; 207 */ 208 JNIEXPORT jobject JNICALL 209 Java_sun_font_CStrike_getNativeGlyphOutline 210 (JNIEnv *env, jclass clazz, 211 jlong awtStrikePtr, jint glyphCode, jdouble xPos, jdouble yPos) 212 { 213 jobject generalPath = NULL; 214 215 JNF_COCOA_ENTER(env); 216 217 AWTPathRef path = NULL; 218 jfloatArray pointCoords = NULL; 219 jbyteArray pointTypes = NULL; 220 221 AWT_FONT_CLEANUP_SETUP; 222 223 AWTStrike *awtStrike = (AWTStrike *)jlong_to_ptr(awtStrikePtr); 224 AWTFont *awtfont = awtStrike->fAWTFont; 225 226 AWT_FONT_CLEANUP_CHECK(awtfont); 227 228 // inverting the shear order and sign to compensate for the flipped coordinate system 229 CGAffineTransform tx = awtStrike->fTx; 230 tx.tx += xPos; 231 tx.ty += yPos; 232 233 // get the right font and glyph for this "Java GlyphCode" 234 235 CGGlyph glyph; 236 const CTFontRef font = CTS_CopyCTFallbackFontAndGlyphForJavaGlyphCode(awtfont, glyphCode, &glyph); 237 238 // get the advance of this glyph 239 CGSize advance; 240 CTFontGetAdvancesForGlyphs(font, kCTFontDefaultOrientation, &glyph, &advance, 1); 241 242 // Create AWTPath 243 path = AWTPathCreate(CGSizeMake(xPos, yPos)); 244 AWT_FONT_CLEANUP_CHECK(path); 245 246 // Get the paths 247 tx = awtStrike->fTx; 248 tx = CGAffineTransformConcat(tx, sInverseTX); 249 AWTGetGlyphOutline(&glyph, (NSFont *)font, &advance, &tx, 0, 1, &path); 250 CFRelease(font); 251 252 pointCoords = (*env)->NewFloatArray(env, path->fNumberOfDataElements); 253 AWT_FONT_CLEANUP_CHECK(pointCoords); 254 255 (*env)->SetFloatArrayRegion(env, pointCoords, 0, path->fNumberOfDataElements, (jfloat*)path->fSegmentData); 256 257 // Copy the pointTypes to the general path 258 pointTypes = (*env)->NewByteArray(env, path->fNumberOfSegments); 259 AWT_FONT_CLEANUP_CHECK(pointTypes); 260 261 (*env)->SetByteArrayRegion(env, pointTypes, 0, path->fNumberOfSegments, (jbyte*)path->fSegmentType); 262 263 static JNF_CLASS_CACHE(jc_GeneralPath, "java/awt/geom/GeneralPath"); 264 static JNF_CTOR_CACHE(jc_GeneralPath_ctor, jc_GeneralPath, "(I[BI[FI)V"); 265 generalPath = JNFNewObject(env, jc_GeneralPath_ctor, java_awt_geom_PathIterator_WIND_NON_ZERO, pointTypes, path->fNumberOfSegments, pointCoords, path->fNumberOfDataElements); // AWT_THREADING Safe (known object) 266 267 // Cleanup 268 cleanup: 269 if (path != NULL) { 270 AWTPathFree(path); 271 path = NULL; 272 } 273 274 if (pointCoords != NULL) { 275 (*env)->DeleteLocalRef(env, pointCoords); 276 pointCoords = NULL; 277 } 278 279 if (pointTypes != NULL) { 280 (*env)->DeleteLocalRef(env, pointTypes); 281 pointTypes = NULL; 282 } 283 284 AWT_FONT_CLEANUP_FINISH; 285 JNF_COCOA_EXIT(env); 286 return generalPath; 287 } 288 289 /* 290 * Class: sun_font_CStrike 291 * Method: getGlyphImagePtrsNative 292 * Signature: (JJ[J[II)V 293 */ 294 JNIEXPORT void JNICALL 295 Java_sun_font_CStrike_getGlyphImagePtrsNative 296 (JNIEnv *env, jclass clazz, 297 jlong awtStrikePtr, jlongArray glyphInfoLongArray, 298 jintArray glyphCodes, jint len) 299 { 300 JNF_COCOA_ENTER(env); 301 302 AWTStrike *awtStrike = (AWTStrike *)jlong_to_ptr(awtStrikePtr); 303 304 jlong *glyphInfos = 305 (*env)->GetPrimitiveArrayCritical(env, glyphInfoLongArray, NULL); 306 jint *rawGlyphCodes = 307 (*env)->GetPrimitiveArrayCritical(env, glyphCodes, NULL); 308 309 CGGlyphImages_GetGlyphImagePtrs(glyphInfos, awtStrike, 310 rawGlyphCodes, len); 311 312 (*env)->ReleasePrimitiveArrayCritical(env, glyphCodes, 313 rawGlyphCodes, JNI_ABORT); 314 // Do not use JNI_COMMIT, as that will not free the buffer copy 315 // when +ProtectJavaHeap is on. 316 (*env)->ReleasePrimitiveArrayCritical(env, glyphInfoLongArray, 317 glyphInfos, 0); 318 319 JNF_COCOA_EXIT(env); 320 } 321 322 /* 323 * Class: sun_font_CStrike 324 * Method: createNativeStrikePtr 325 * Signature: (J[D[DII)J 326 */ 327 JNIEXPORT jlong JNICALL Java_sun_font_CStrike_createNativeStrikePtr 328 (JNIEnv *env, jclass clazz, jlong nativeFontPtr, jdoubleArray glyphTxArray, jdoubleArray invDevTxArray, jint aaStyle, jint fmHint) 329 { 330 AWTStrike *awtStrike = nil; 331 JNF_COCOA_ENTER(env); 332 333 AWTFont *awtFont = (AWTFont *)jlong_to_ptr(nativeFontPtr); 334 JRSFontRenderingStyle style = JRSFontGetRenderingStyleForHints(fmHint, aaStyle); 335 336 CGAffineTransform glyphTx = GetTxFromDoubles(env, glyphTxArray); 337 CGAffineTransform invDevTx = GetTxFromDoubles(env, invDevTxArray); 338 339 awtStrike = [AWTStrike awtStrikeForFont:awtFont tx:glyphTx invDevTx:invDevTx style:style aaStyle:aaStyle]; // autoreleased 340 341 if (awtStrike) 342 { 343 CFRetain(awtStrike); // GC 344 } 345 346 JNF_COCOA_EXIT(env); 347 return ptr_to_jlong(awtStrike); 348 } 349 350 /* 351 * Class: sun_font_CStrike 352 * Method: disposeNativeStrikePtr 353 * Signature: (J)V 354 */ 355 JNIEXPORT void JNICALL 356 Java_sun_font_CStrike_disposeNativeStrikePtr 357 (JNIEnv *env, jclass clazz, jlong awtStrike) 358 { 359 JNF_COCOA_ENTER(env); 360 361 if (awtStrike) { 362 CFRelease((AWTStrike *)jlong_to_ptr(awtStrike)); // GC 363 } 364 365 JNF_COCOA_EXIT(env); 366 } 367 368 /* 369 * Class: sun_font_CStrike 370 * Method: getFontMetrics 371 * Signature: (J)Lsun/font/StrikeMetrics; 372 */ 373 JNIEXPORT jobject JNICALL 374 Java_sun_font_CStrike_getFontMetrics 375 (JNIEnv *env, jclass clazz, jlong awtStrikePtr) 376 { 377 jobject metrics = NULL; 378 379 JNF_COCOA_ENTER(env); 380 AWT_FONT_CLEANUP_SETUP; 381 382 AWTFont *awtfont = ((AWTStrike *)jlong_to_ptr(awtStrikePtr))->fAWTFont; 383 AWT_FONT_CLEANUP_CHECK(awtfont); 384 385 CGFontRef cgFont = awtfont->fNativeCGFont; 386 387 jfloat ay=0.0, dy=0.0, mx=0.0, ly=0.0; 388 int unitsPerEm = CGFontGetUnitsPerEm(cgFont); 389 CGFloat scaleX = (1.0 / unitsPerEm); 390 CGFloat scaleY = (1.0 / unitsPerEm); 391 392 // Ascent 393 ay = -(CGFloat)CGFontGetAscent(cgFont) * scaleY; 394 395 // Descent 396 dy = -(CGFloat)CGFontGetDescent(cgFont) * scaleY; 397 398 // Leading 399 ly = (CGFloat)CGFontGetLeading(cgFont) * scaleY; 400 401 // Max Advance for Font Direction (Strictly horizontal) 402 mx = [awtfont->fFont maximumAdvancement].width; 403 404 /* 405 * ascent: no need to set ascentX - it will be zero. 406 * descent: no need to set descentX - it will be zero. 407 * baseline: old releases "made up" a number and also seemed to 408 * make it up for "X" and set "Y" to 0. 409 * leadingX: no need to set leadingX - it will be zero. 410 * leadingY: made-up number, but being compatible with what 1.4.x did. 411 * advance: no need to set yMaxLinearAdvanceWidth - it will be zero. 412 */ 413 414 JNF_CLASS_CACHE(sjc_StrikeMetrics, "sun/font/StrikeMetrics"); 415 JNF_CTOR_CACHE(strikeMetricsCtr, sjc_StrikeMetrics, "(FFFFFFFFFF)V"); 416 metrics = JNFNewObject(env, strikeMetricsCtr, 417 0.0, ay, 0.0, dy, 1.0, 418 0.0, 0.0, ly, mx, 0.0); 419 420 cleanup: 421 AWT_FONT_CLEANUP_FINISH; 422 JNF_COCOA_EXIT(env); 423 424 return metrics; 425 }