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 }