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