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