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