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 
 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     jint *rawGlyphCodes =
 315         (*env)->GetPrimitiveArrayCritical(env, glyphCodes, NULL);
 316 
 317     CGGlyphImages_GetGlyphImagePtrs(glyphInfos, awtStrike,
 318                                     rawGlyphCodes, len);
 319 
 320     (*env)->ReleasePrimitiveArrayCritical(env, glyphCodes,
 321                                           rawGlyphCodes, JNI_ABORT);
 322     // Do not use JNI_COMMIT, as that will not free the buffer copy
 323     // when +ProtectJavaHeap is on.
 324     (*env)->ReleasePrimitiveArrayCritical(env, glyphInfoLongArray,
 325                                           glyphInfos, 0);
 326 
 327 JNF_COCOA_EXIT(env);
 328 }
 329 
 330 /*
 331  * Class:     sun_font_CStrike
 332  * Method:    createNativeStrikePtr
 333  * Signature: (J[D[DII)J
 334  */
 335 JNIEXPORT jlong JNICALL Java_sun_font_CStrike_createNativeStrikePtr
 336 (JNIEnv *env, jclass clazz, jlong nativeFontPtr, jdoubleArray glyphTxArray, jdoubleArray invDevTxArray, jint aaStyle, jint fmHint)
 337 {
 338     AWTStrike *awtStrike = nil;
 339 JNF_COCOA_ENTER(env);
 340 
 341     AWTFont *awtFont = (AWTFont *)jlong_to_ptr(nativeFontPtr);
 342     JRSFontRenderingStyle style = JRSFontGetRenderingStyleForHints(fmHint, aaStyle);
 343 
 344     CGAffineTransform glyphTx = GetTxFromDoubles(env, glyphTxArray);
 345     CGAffineTransform invDevTx = GetTxFromDoubles(env, invDevTxArray);
 346 
 347     awtStrike = [AWTStrike awtStrikeForFont:awtFont tx:glyphTx invDevTx:invDevTx style:style aaStyle:aaStyle]; // autoreleased
 348 
 349     if (awtStrike)
 350     {
 351         CFRetain(awtStrike); // GC
 352     }
 353 
 354 JNF_COCOA_EXIT(env);
 355     return ptr_to_jlong(awtStrike);
 356 }
 357 
 358 /*
 359  * Class:     sun_font_CStrike
 360  * Method:    disposeNativeStrikePtr
 361  * Signature: (J)V
 362  */
 363 JNIEXPORT void JNICALL
 364 Java_sun_font_CStrike_disposeNativeStrikePtr
 365     (JNIEnv *env, jclass clazz, jlong awtStrike)
 366 {
 367 JNF_COCOA_ENTER(env);
 368 
 369     if (awtStrike) {
 370         CFRelease((AWTStrike *)jlong_to_ptr(awtStrike)); // GC
 371     }
 372 
 373 JNF_COCOA_EXIT(env);
 374 }
 375 
 376 /*
 377  * Class:     sun_font_CStrike
 378  * Method:    getFontMetrics
 379  * Signature: (J)Lsun/font/StrikeMetrics;
 380  */
 381 JNIEXPORT jobject JNICALL
 382 Java_sun_font_CStrike_getFontMetrics
 383     (JNIEnv *env, jclass clazz, jlong awtStrikePtr)
 384 {
 385     jobject metrics = NULL;
 386 
 387 JNF_COCOA_ENTER(env);
 388     AWT_FONT_CLEANUP_SETUP;
 389 
 390     AWTFont *awtfont = ((AWTStrike *)jlong_to_ptr(awtStrikePtr))->fAWTFont;
 391     AWT_FONT_CLEANUP_CHECK(awtfont);
 392 
 393     CGFontRef cgFont = awtfont->fNativeCGFont;
 394 
 395     jfloat ay=0.0, dy=0.0, mx=0.0, ly=0.0;
 396     int unitsPerEm = CGFontGetUnitsPerEm(cgFont);
 397     CGFloat scaleX = (1.0 / unitsPerEm);
 398     CGFloat scaleY = (1.0 / unitsPerEm);
 399 
 400     // Ascent
 401     ay = -(CGFloat)CGFontGetAscent(cgFont) * scaleY;
 402 
 403     // Descent
 404     dy = -(CGFloat)CGFontGetDescent(cgFont) * scaleY;
 405 
 406     // Leading
 407     ly = (CGFloat)CGFontGetLeading(cgFont) * scaleY;
 408 
 409     // Max Advance for Font Direction (Strictly horizontal)
 410     mx = [awtfont->fFont maximumAdvancement].width;
 411 
 412     /*
 413      * ascent:   no need to set ascentX - it will be zero.
 414      * descent:  no need to set descentX - it will be zero.
 415      * baseline: old releases "made up" a number and also seemed to
 416      *           make it up for "X" and set "Y" to 0.
 417      * leadingX: no need to set leadingX - it will be zero.
 418      * leadingY: made-up number, but being compatible with what 1.4.x did.
 419      * advance:  no need to set yMaxLinearAdvanceWidth - it will be zero.
 420      */
 421 
 422     JNF_CLASS_CACHE(sjc_StrikeMetrics, "sun/font/StrikeMetrics");
 423     JNF_CTOR_CACHE(strikeMetricsCtr, sjc_StrikeMetrics, "(FFFFFFFFFF)V");
 424     metrics = JNFNewObject(env, strikeMetricsCtr,
 425                            0.0, ay, 0.0, dy, 1.0,
 426                            0.0, 0.0, ly, mx, 0.0);
 427 
 428 cleanup:
 429     AWT_FONT_CLEANUP_FINISH;
 430 JNF_COCOA_EXIT(env);
 431 
 432     return metrics;
 433 }
 434 
 435 extern void AccelGlyphCache_RemoveAllInfos(GlyphInfo* glyph);
 436 /*
 437  * Class:     sun_font_CStrikeDisposer
 438  * Method:    removeGlyphInfoFromCache
 439  * Signature: (J)V
 440  */
 441 JNIEXPORT void JNICALL Java_sun_font_CStrikeDisposer_removeGlyphInfoFromCache
 442 (JNIEnv *env, jclass cls, jlong glyphInfo)
 443 {
 444     JNF_COCOA_ENTER(env);
 445 
 446     AccelGlyphCache_RemoveAllCellInfos((GlyphInfo*)jlong_to_ptr(glyphInfo));
 447 
 448     JNF_COCOA_EXIT(env);
 449 }