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