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 }