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