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