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