Path: blob/master/src/java.desktop/macosx/native/libawt_lwawt/font/CGGlyphImages.m
41152 views
/*1* Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425#import <Accelerate/Accelerate.h> // for vImage_Buffer2627#import "JNIUtilities.h"28#import "CGGlyphImages.h"29#import "CoreTextSupport.h"30#import "fontscalerdefs.h" // contains the definition of GlyphInfo struct3132#import "sun_awt_SunHints.h"3334//#define USE_IMAGE_ALIGNED_MEMORY 135//#define CGGI_DEBUG 136//#define CGGI_DEBUG_DUMP 137//#define CGGI_DEBUG_HIT_COUNT 13839#define PRINT_TX(x) \40NSLog(@"(%f, %f, %f, %f, %f, %f)", x.a, x.b, x.c, x.d, x.tx, x.ty);4142/*43* The GlyphCanvas is a global shared CGContext that characters are struck into.44* For each character, the glyph is struck, copied into a GlyphInfo struct, and45* the canvas is cleared for the next glyph.46*47* If the necessary canvas is too large, the shared one will not be used and a48* temporary one will be provided.49*/50@interface CGGI_GlyphCanvas : NSObject {51@public52CGContextRef context;53vImage_Buffer *image;54}55@end;5657@implementation CGGI_GlyphCanvas58@end596061#pragma mark --- Debugging Helpers ---6263/*64* These debug functions are only compiled when CGGI_DEBUG is activated.65* They will print out a full UInt8 canvas and any pixels struck (assuming66* the canvas is not too big).67*68* As another debug feature, the entire canvas will be filled with a light69* alpha value so it is easy to see where the glyph painting regions are70* at runtime.71*/7273#ifdef CGGI_DEBUG_DUMP74static void75DUMP_PIXELS(const char msg[], const UInt8 pixels[],76const size_t bytesPerPixel, const int width, const int height)77{78printf("| %s: (%d, %d)\n", msg, width, height);7980if (width > 80 || height > 80) {81printf("| too big\n");82return;83}8485size_t i, j = 0, k, size = width * height;86for (i = 0; i < size; i++) {87for (k = 0; k < bytesPerPixel; k++) {88if (pixels[i * bytesPerPixel + k] > 0x80) j++;89}90}9192if (j == 0) {93printf("| empty\n");94return;95}9697printf("|_");98int x, y;99for (x = 0; x < width; x++) {100printf("__");101}102printf("_\n");103104for (y = 0; y < height; y++) {105printf("| ");106for (x = 0; x < width; x++) {107int p = 0;108for(k = 0; k < bytesPerPixel; k++) {109p += pixels[(y * width + x) * bytesPerPixel + k];110}111112if (p < 0x80) {113printf(" ");114} else {115printf("[]");116}117}118printf(" |\n");119}120}121122static void123DUMP_IMG_PIXELS(const char msg[], const vImage_Buffer *image)124{125const void *pixels = image->data;126const size_t pixelSize = image->rowBytes / image->width;127const size_t width = image->width;128const size_t height = image->height;129130DUMP_PIXELS(msg, pixels, pixelSize, width, height);131}132133static void134PRINT_CGSTATES_INFO(const CGContextRef cgRef)135{136// TODO(cpc): lots of SPI use in this method; remove/rewrite?137#if 0138CGRect clip = CGContextGetClipBoundingBox(cgRef);139fprintf(stderr, " clip: ((%f, %f), (%f, %f))\n",140clip.origin.x, clip.origin.y, clip.size.width, clip.size.height);141142CGAffineTransform ctm = CGContextGetCTM(cgRef);143fprintf(stderr, " ctm: (%f, %f, %f, %f, %f, %f)\n",144ctm.a, ctm.b, ctm.c, ctm.d, ctm.tx, ctm.ty);145146CGAffineTransform txtTx = CGContextGetTextMatrix(cgRef);147fprintf(stderr, " txtTx: (%f, %f, %f, %f, %f, %f)\n",148txtTx.a, txtTx.b, txtTx.c, txtTx.d, txtTx.tx, txtTx.ty);149150if (CGContextIsPathEmpty(cgRef) == 0) {151CGPoint pathpoint = CGContextGetPathCurrentPoint(cgRef);152CGRect pathbbox = CGContextGetPathBoundingBox(cgRef);153fprintf(stderr, " [pathpoint: (%f, %f)] [pathbbox: ((%f, %f), (%f, %f))]\n",154pathpoint.x, pathpoint.y, pathbbox.origin.x, pathbbox.origin.y,155pathbbox.size.width, pathbbox.size.width);156}157158CGFloat linewidth = CGContextGetLineWidth(cgRef);159CGLineCap linecap = CGContextGetLineCap(cgRef);160CGLineJoin linejoin = CGContextGetLineJoin(cgRef);161CGFloat miterlimit = CGContextGetMiterLimit(cgRef);162size_t dashcount = CGContextGetLineDashCount(cgRef);163fprintf(stderr, " [linewidth: %f] [linecap: %d] [linejoin: %d] [miterlimit: %f] [dashcount: %lu]\n",164linewidth, linecap, linejoin, miterlimit, (unsigned long)dashcount);165166CGFloat smoothness = CGContextGetSmoothness(cgRef);167bool antialias = CGContextGetShouldAntialias(cgRef);168bool smoothfont = CGContextGetShouldSmoothFonts(cgRef);169JRSFontRenderingStyle fRendMode = CGContextGetFontRenderingMode(cgRef);170fprintf(stderr, " [smoothness: %f] [antialias: %d] [smoothfont: %d] [fontrenderingmode: %d]\n",171smoothness, antialias, smoothfont, fRendMode);172#endif173}174#endif175176#ifdef CGGI_DEBUG177178static void179DUMP_GLYPHINFO(const GlyphInfo *info)180{181printf("size: (%d, %d) pixelSize: %d\n",182info->width, info->height, info->rowBytes / info->width);183printf("adv: (%f, %f) top: (%f, %f)\n",184info->advanceX, info->advanceY, info->topLeftX, info->topLeftY);185186#ifdef CGGI_DEBUG_DUMP187DUMP_PIXELS("Glyph Info Struct",188info->image, info->rowBytes / info->width,189info->width, info->height);190#endif191}192193#endif194195196#pragma mark --- Font Rendering Mode Descriptors ---197static Int32 reverseGamma = 0;198199static UInt8 reverseGammaLut[256] = { 0 };200201static inline UInt8* getReverseGammaLut() {202if (reverseGamma == 0) {203// initialize gamma lut204double gamma;205int i;206const char* pGammaEnv = getenv("J2D_LCD_REVERSE_GAMMA");207if (pGammaEnv != NULL) {208reverseGamma = atol(pGammaEnv);209}210211if (reverseGamma < 100 || reverseGamma > 250) {212reverseGamma = 180;213}214215gamma = 100.0 / reverseGamma;216for (i = 0; i < 256; i++) {217double x = ((double)i) / 255.0;218reverseGammaLut[i] = (UInt8)(255 * pow(x, gamma));219}220}221return reverseGammaLut;222}223224static inline void225CGGI_CopyARGBPixelToRGBPixel(const UInt32 p, UInt8 *dst)226{227UInt8* lut = getReverseGammaLut();228229*(dst + 0) = lut[0xFF - (p >> 16 & 0xFF)]; // red230*(dst + 1) = lut[0xFF - (p >> 8 & 0xFF)]; // green231*(dst + 2) = lut[0xFF - (p & 0xFF)]; // blue232}233234static void235CGGI_CopyImageFromCanvasToRGBInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info)236{237UInt32 *src = (UInt32 *)canvas->image->data;238size_t srcRowWidth = canvas->image->width;239240UInt8 *dest = (UInt8 *)info->image;241size_t destRowWidth = info->width;242243size_t height = info->height;244245size_t y;246247// fill empty glyph image with black-on-white glyph248for (y = 0; y < height; y++) {249size_t destRow = y * destRowWidth * 3;250size_t srcRow = y * srcRowWidth;251252size_t x;253for (x = 0; x < destRowWidth; x++) {254CGGI_CopyARGBPixelToRGBPixel(src[srcRow + x],255dest + destRow + x * 3);256}257}258}259260//static void CGGI_copyImageFromCanvasToAlphaInfo261//(CGGI_GlyphCanvas *canvas, GlyphInfo *info)262//{263// vImage_Buffer infoBuffer;264// infoBuffer.data = info->image;265// infoBuffer.width = info->width;266// infoBuffer.height = info->height;267// infoBuffer.rowBytes = info->width; // three bytes per RGB pixel268//269// UInt8 scrapPixel[info->width * info->height];270// vImage_Buffer scrapBuffer;271// scrapBuffer.data = &scrapPixel;272// scrapBuffer.width = info->width;273// scrapBuffer.height = info->height;274// scrapBuffer.rowBytes = info->width;275//276// vImageConvert_ARGB8888toPlanar8(canvas->image, &infoBuffer,277// &scrapBuffer, &scrapBuffer, &scrapBuffer, kvImageNoFlags);278//}279280static inline UInt8281CGGI_ConvertBWPixelToByteGray(UInt32 p)282{283return 0xFF - (((p >> 24 & 0xFF) + (p >> 16 & 0xFF) + (p >> 8 & 0xFF)) / 3);284}285286static void287CGGI_CopyImageFromCanvasToAlphaInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info)288{289UInt32 *src = (UInt32 *)canvas->image->data;290size_t srcRowWidth = canvas->image->width;291292UInt8 *dest = (UInt8 *)info->image;293size_t destRowWidth = info->width;294295size_t height = info->height;296297size_t y;298299// fill empty glyph image with black-on-white glyph300for (y = 0; y < height; y++) {301size_t destRow = y * destRowWidth;302size_t srcRow = y * srcRowWidth;303size_t x;304for (x = 0; x < destRowWidth; x++) {305UInt32 p = src[srcRow + x];306dest[destRow + x] = CGGI_ConvertBWPixelToByteGray(p);307}308}309}310311static void312CGGI_CopyImageFromCanvasToARGBInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info)313{314CGBitmapInfo bitmapInfo = CGBitmapContextGetBitmapInfo(canvas->context);315bool littleEndian = (bitmapInfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Little;316317UInt32 *src = (UInt32 *)canvas->image->data;318size_t srcRowWidth = canvas->image->width;319320UInt8 *dest = (UInt8 *)info->image;321size_t destRowWidth = info->width;322323size_t height = info->height;324325size_t y;326327for (y = 0; y < height; y++) {328size_t srcRow = y * srcRowWidth;329if (littleEndian) {330UInt16 destRowBytes = info->rowBytes;331memcpy(dest, src + srcRow, destRowBytes);332dest += destRowBytes;333} else {334size_t x;335for (x = 0; x < destRowWidth; x++) {336UInt32 p = src[srcRow + x];337*dest++ = (p >> 24 & 0xFF); // blue (alpha-premultiplied)338*dest++ = (p >> 16 & 0xFF); // green (alpha-premultiplied)339*dest++ = (p >> 8 & 0xFF); // red (alpha-premultiplied)340*dest++ = (p & 0xFF); // alpha341}342}343}344}345346#pragma mark --- Pixel Size, Modes, and Canvas Shaping Helper Functions ---347348typedef struct CGGI_GlyphInfoDescriptor {349size_t pixelSize;350void (*copyFxnPtr)(CGGI_GlyphCanvas *canvas, GlyphInfo *info);351} CGGI_GlyphInfoDescriptor;352353typedef struct CGGI_RenderingMode {354CGGI_GlyphInfoDescriptor *glyphDescriptor;355JRSFontRenderingStyle cgFontMode;356} CGGI_RenderingMode;357358static CGGI_GlyphInfoDescriptor grey =359{ 1, &CGGI_CopyImageFromCanvasToAlphaInfo };360static CGGI_GlyphInfoDescriptor rgb =361{ 3, &CGGI_CopyImageFromCanvasToRGBInfo };362static CGGI_GlyphInfoDescriptor argb =363{ 4, &CGGI_CopyImageFromCanvasToARGBInfo };364365static inline CGGI_GlyphInfoDescriptor*366CGGI_GetGlyphInfoDescriptor(const CGGI_RenderingMode *mode, CTFontRef font)367{368return IsEmojiFont(font) ? &argb : mode->glyphDescriptor;369}370371static inline CGGI_RenderingMode372CGGI_GetRenderingMode(const AWTStrike *strike)373{374CGGI_RenderingMode mode;375mode.cgFontMode = strike->fStyle;376NSException *e = nil;377378switch (strike->fAAStyle) {379case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_OFF:380case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_ON:381mode.glyphDescriptor = &grey;382break;383case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HRGB:384case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HBGR:385case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_VRGB:386case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_VBGR:387mode.glyphDescriptor = &rgb;388break;389case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_GASP:390case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_DEFAULT:391default:392/* we expect that text antialiasing hint has been already393* evaluated. Report an error if we get 'unevaluated' hint here.394*/395e = [NSException396exceptionWithName:@"IllegalArgumentException"397reason:@"Invalid hint value"398userInfo:nil];399@throw e;400}401402return mode;403}404405406#pragma mark --- Canvas Managment ---407408/*409* Creates a new canvas of a fixed size, and initializes the CGContext as410* an 32-bit ARGB BitmapContext with some generic RGB color space.411*/412static inline void413CGGI_InitCanvas(CGGI_GlyphCanvas *canvas,414const vImagePixelCount width, const vImagePixelCount height,415const CGGI_RenderingMode* mode)416{417// our canvas is *always* 4-byte ARGB418size_t bytesPerRow = width * sizeof(UInt32);419size_t byteCount = bytesPerRow * height;420421canvas->image = malloc(sizeof(vImage_Buffer));422canvas->image->width = width;423canvas->image->height = height;424canvas->image->rowBytes = bytesPerRow;425426canvas->image->data = (void *)calloc(byteCount, sizeof(UInt8));427if (canvas->image->data == NULL) {428[[NSException exceptionWithName:NSMallocException429reason:@"Failed to allocate memory for the buffer which backs the CGContext for glyph strikes." userInfo:nil] raise];430}431432uint32_t bmpInfo = kCGImageAlphaPremultipliedFirst;433if (mode->glyphDescriptor == &rgb) {434bmpInfo |= kCGBitmapByteOrder32Host;435}436437CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);438canvas->context = CGBitmapContextCreate(canvas->image->data,439width, height, 8, bytesPerRow,440colorSpace,441bmpInfo);442443// set foreground color444CGContextSetRGBFillColor(canvas->context, 0.0f, 0.0f, 0.0f, 1.0f);445446CGContextSetFontSize(canvas->context, 1);447CGContextSaveGState(canvas->context);448449CGColorSpaceRelease(colorSpace);450}451452/*453* Releases the BitmapContext and the associated memory backing it.454*/455static inline void456CGGI_FreeCanvas(CGGI_GlyphCanvas *canvas)457{458if (canvas->context != NULL) {459CGContextRelease(canvas->context);460}461462if (canvas->image != NULL) {463if (canvas->image->data != NULL) {464free(canvas->image->data);465}466free(canvas->image);467}468}469470/*471* This is the slack space that is preallocated for the global GlyphCanvas472* when it needs to be expanded. It has been set somewhat liberally to473* avoid re-upsizing frequently.474*/475#define CGGI_GLYPH_CANVAS_SLACK 2.5476477/*478* Quick and easy inline to check if this canvas is big enough.479*/480static inline void481CGGI_SizeCanvas(CGGI_GlyphCanvas *canvas, const vImagePixelCount width,482const vImagePixelCount height,483const CGGI_RenderingMode* mode)484{485if (canvas->image != NULL &&486width < canvas->image->width &&487height < canvas->image->height)488{489return;490}491492// if we don't have enough space to strike the largest glyph in the493// run, resize the canvas494CGGI_FreeCanvas(canvas);495CGGI_InitCanvas(canvas,496width * CGGI_GLYPH_CANVAS_SLACK,497height * CGGI_GLYPH_CANVAS_SLACK,498mode);499JRSFontSetRenderingStyleOnContext(canvas->context, mode->cgFontMode);500}501502/*503* Clear the canvas by blitting white (or transparent background for color glyphs) only into the region of interest504* (the rect which we will copy out of once the glyph is struck).505*/506static inline void507CGGI_ClearCanvas(CGGI_GlyphCanvas *canvas, GlyphInfo *info, bool transparent)508{509vImage_Buffer canvasRectToClear;510canvasRectToClear.data = canvas->image->data;511canvasRectToClear.height = info->height;512canvasRectToClear.width = info->width;513// use the row stride of the canvas, not the info514canvasRectToClear.rowBytes = canvas->image->rowBytes;515516// clean the canvas517#ifdef CGGI_DEBUG518Pixel_8888 background = { 0xE0, 0xE0, 0xE0, 0xE0 };519#else520Pixel_8888 background = { transparent ? 0 : 0xFF,521transparent ? 0 : 0xFF,522transparent ? 0 : 0xFF,523transparent ? 0 : 0xFF };524#endif525526// clear canvas background527vImageBufferFill_ARGB8888(&canvasRectToClear, background, kvImageNoFlags);528}529530531#pragma mark --- GlyphInfo Creation & Copy Functions ---532533/*534* Creates a GlyphInfo with exactly the correct size image and measurements.535*/536#define CGGI_GLYPH_BBOX_PADDING 2.0f537static inline GlyphInfo *538CGGI_CreateNewGlyphInfoFrom(CGSize advance, CGRect bbox,539const AWTStrike *strike,540const CGGI_GlyphInfoDescriptor *glyphDescriptor)541{542size_t pixelSize = glyphDescriptor->pixelSize;543544// adjust the bounding box to be 1px bigger on each side than what545// CGFont-whatever suggests - because it gives a bounding box that546// is too tight547bbox.size.width += CGGI_GLYPH_BBOX_PADDING * 2.0f;548bbox.size.height += CGGI_GLYPH_BBOX_PADDING * 2.0f;549bbox.origin.x -= CGGI_GLYPH_BBOX_PADDING;550bbox.origin.y -= CGGI_GLYPH_BBOX_PADDING;551552vImagePixelCount width = ceilf(bbox.size.width);553vImagePixelCount height = ceilf(bbox.size.height);554555// if the glyph is larger than 1MB, don't even try...556// the GlyphVector path should have taken over by now557// and zero pixels is ok558if (width * height > 1024 * 1024) {559width = 1;560height = 1;561}562advance = CGSizeApplyAffineTransform(advance, strike->fFontTx);563if (!JRSFontStyleUsesFractionalMetrics(strike->fStyle)) {564advance.width = round(advance.width);565advance.height = round(advance.height);566}567advance = CGSizeApplyAffineTransform(advance, strike->fDevTx);568569#ifdef USE_IMAGE_ALIGNED_MEMORY570// create separate memory571GlyphInfo *glyphInfo = (GlyphInfo *)malloc(sizeof(GlyphInfo));572void *image = (void *)malloc(height * width * pixelSize);573#else574// create a GlyphInfo struct fused to the image it points to575GlyphInfo *glyphInfo = (GlyphInfo *)malloc(sizeof(GlyphInfo) +576height * width * pixelSize);577#endif578579glyphInfo->advanceX = advance.width;580glyphInfo->advanceY = advance.height;581glyphInfo->topLeftX = round(bbox.origin.x);582glyphInfo->topLeftY = round(bbox.origin.y);583glyphInfo->width = width;584glyphInfo->height = height;585glyphInfo->rowBytes = width * pixelSize;586glyphInfo->cellInfo = NULL;587588#ifdef USE_IMAGE_ALIGNED_MEMORY589glyphInfo->image = image;590#else591glyphInfo->image = ((void *)glyphInfo) + sizeof(GlyphInfo);592#endif593594return glyphInfo;595}596597598#pragma mark --- Glyph Striking onto Canvas ---599600/*601* Clears the canvas, strikes the glyph with CoreGraphics, and then602* copies the struck pixels into the GlyphInfo image.603*/604static inline void605CGGI_CreateImageForGlyph606(CGGI_GlyphCanvas *canvas, const CGGlyph glyph,607GlyphInfo *info, const CGGI_GlyphInfoDescriptor *glyphDescriptor, const AWTStrike *strike, CTFontRef font)608{609if (isnan(info->topLeftX) || isnan(info->topLeftY)) {610// Explicitly set glyphInfo width/height to be 0 to ensure611// zero length glyph image is copied into GlyphInfo from canvas612info->width = 0;613info->height = 0;614615// copy the "empty" glyph from the canvas into the info616(*glyphDescriptor->copyFxnPtr)(canvas, info);617return;618}619620// clean the canvas621CGGI_ClearCanvas(canvas, info, glyphDescriptor == &argb);622623// strike the glyph in the upper right corner624CGFloat x = -info->topLeftX;625CGFloat y = canvas->image->height + info->topLeftY;626627if (glyphDescriptor == &argb) {628// Emoji glyphs are not rendered by CGContextShowGlyphsAtPoint.629// Also, it's not possible to use transformation matrix to get the emoji glyph630// rendered for the desired font size - actual-size font object is needed.631// The logic here must match the logic in CGGlyphImages_GetGlyphMetrics,632// which calculates glyph metrics.633634CGAffineTransform matrix = CGContextGetTextMatrix(canvas->context);635CGFloat fontSize = sqrt(fabs(matrix.a * matrix.d - matrix.b * matrix.c));636CTFontRef sizedFont = CTFontCreateCopyWithSymbolicTraits(font, fontSize, NULL, 0, 0);637638CGFloat normFactor = 1.0 / fontSize;639CGAffineTransform normalizedMatrix = CGAffineTransformScale(matrix, normFactor, normFactor);640CGContextSetTextMatrix(canvas->context, normalizedMatrix);641642CGPoint userPoint = CGPointMake(x, y);643CGAffineTransform normalizedMatrixInv = CGAffineTransformInvert(normalizedMatrix);644CGPoint textPoint = CGPointApplyAffineTransform(userPoint, normalizedMatrixInv);645646CTFontDrawGlyphs(sizedFont, &glyph, &textPoint, 1, canvas->context);647648CFRelease(sizedFont);649// restore context's original state650CGContextSetTextMatrix(canvas->context, matrix);651CGContextSetFontSize(canvas->context, 1); // CTFontDrawGlyphs tampers with it652} else {653CGContextShowGlyphsAtPoint(canvas->context, x, y, &glyph, 1);654}655656// copy the glyph from the canvas into the info657(*glyphDescriptor->copyFxnPtr)(canvas, info);658}659660/*661* CoreText path...662*/663static inline GlyphInfo *664CGGI_CreateImageForUnicode665(CGGI_GlyphCanvas *canvas, const AWTStrike *strike,666const CGGI_RenderingMode *mode, const UnicodeScalarValue uniChar)667{668// save the state of the world669CGContextSaveGState(canvas->context);670671// get the glyph, measure it using CG672CGGlyph glyph;673CTFontRef fallback;674if (uniChar > 0xFFFF) {675UTF16Char charRef[2];676CTS_BreakupUnicodeIntoSurrogatePairs(uniChar, charRef);677CGGlyph glyphTmp[2];678fallback = CTS_CopyCTFallbackFontAndGlyphForUnicode(strike->fAWTFont, (const UTF16Char *)&charRef, (CGGlyph *)&glyphTmp, 2);679glyph = glyphTmp[0];680} else {681UTF16Char charRef;682charRef = (UTF16Char) uniChar; // truncate.683fallback = CTS_CopyCTFallbackFontAndGlyphForUnicode(strike->fAWTFont, (const UTF16Char *)&charRef, &glyph, 1);684}685686CGAffineTransform tx = strike->fTx;687JRSFontRenderingStyle style = JRSFontAlignStyleForFractionalMeasurement(strike->fStyle);688689CGRect bbox;690CGSize advance;691CGGlyphImages_GetGlyphMetrics(fallback, &tx, style, &glyph, 1, &bbox, &advance);692693CGGI_GlyphInfoDescriptor *glyphDescriptor = CGGI_GetGlyphInfoDescriptor(mode, fallback);694695// create the Sun2D GlyphInfo we are going to strike into696GlyphInfo *info = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, glyphDescriptor);697698// fix the context size, just in case the substituted character is unexpectedly large699CGGI_SizeCanvas(canvas, info->width, info->height, mode);700701// align the transform for the real CoreText strike702CGContextSetTextMatrix(canvas->context, strike->fAltTx);703704const CGFontRef cgFallback = CTFontCopyGraphicsFont(fallback, NULL);705CGContextSetFont(canvas->context, cgFallback);706CFRelease(cgFallback);707708// clean the canvas - align, strike, and copy the glyph from the canvas into the info709CGGI_CreateImageForGlyph(canvas, glyph, info, glyphDescriptor, strike, fallback);710711// restore the state of the world712CGContextRestoreGState(canvas->context);713714CFRelease(fallback);715#ifdef CGGI_DEBUG716DUMP_GLYPHINFO(info);717#endif718719#ifdef CGGI_DEBUG_DUMP720DUMP_IMG_PIXELS("CGGI Canvas", canvas->image);721#if 0722PRINT_CGSTATES_INFO(NULL);723#endif724#endif725726return info;727}728729730#pragma mark --- GlyphInfo Filling and Canvas Managment ---731732/*733* Sets all the per-run properties for the canvas, and then iterates through734* the character run, and creates images in the GlyphInfo structs.735*736* Not inlined because it would create two copies in the function below737*/738static void739CGGI_FillImagesForGlyphsWithSizedCanvas(CGGI_GlyphCanvas *canvas,740const AWTStrike *strike,741const CGGI_RenderingMode *mode,742jlong glyphInfos[],743const UnicodeScalarValue uniChars[],744const CGGlyph glyphs[],745const CFIndex len)746{747CGContextSetTextMatrix(canvas->context, strike->fAltTx);748749CGContextSetFont(canvas->context, strike->fAWTFont->fNativeCGFont);750JRSFontSetRenderingStyleOnContext(canvas->context, strike->fStyle);751752CTFontRef mainFont = (CTFontRef)strike->fAWTFont->fFont;753CGGI_GlyphInfoDescriptor* mainFontDescriptor = CGGI_GetGlyphInfoDescriptor(mode, mainFont);754755CFIndex i;756for (i = 0; i < len; i++) {757GlyphInfo *info = (GlyphInfo *)jlong_to_ptr(glyphInfos[i]);758if (info != NULL) {759CGGI_CreateImageForGlyph(canvas, glyphs[i], info, mainFontDescriptor, strike, mainFont);760} else {761info = CGGI_CreateImageForUnicode(canvas, strike, mode, uniChars[i]);762glyphInfos[i] = ptr_to_jlong(info);763}764#ifdef CGGI_DEBUG765DUMP_GLYPHINFO(info);766#endif767768#ifdef CGGI_DEBUG_DUMP769DUMP_IMG_PIXELS("CGGI Canvas", canvas->image);770#endif771}772#ifdef CGGI_DEBUG_DUMP773DUMP_IMG_PIXELS("CGGI Canvas", canvas->image);774PRINT_CGSTATES_INFO(canvas->context);775#endif776}777778static NSString *threadLocalAACanvasKey =779@"Java CoreGraphics Text Renderer Cached Canvas for AA";780781static NSString *threadLocalLCDCanvasKey =782@"Java CoreGraphics Text Renderer Cached Canvas for LCD";783784/*785* This is the maximum length and height times the above slack squared786* to determine if we go with the global canvas, or malloc one on the spot.787*/788#define CGGI_GLYPH_CANVAS_MAX 100789790/*791* Based on the space needed to strike the largest character in the run,792* either use the global shared canvas, or make one up on the spot, strike793* the glyphs, and destroy it.794*/795static inline void796CGGI_FillImagesForGlyphs(jlong *glyphInfos, const AWTStrike *strike,797const CGGI_RenderingMode *mode,798const UnicodeScalarValue uniChars[], const CGGlyph glyphs[],799const size_t maxWidth, const size_t maxHeight,800const CFIndex len)801{802if (maxWidth*maxHeight*CGGI_GLYPH_CANVAS_SLACK*CGGI_GLYPH_CANVAS_SLACK >803CGGI_GLYPH_CANVAS_MAX*CGGI_GLYPH_CANVAS_MAX*CGGI_GLYPH_CANVAS_SLACK*CGGI_GLYPH_CANVAS_SLACK)804{805CGGI_GlyphCanvas *tmpCanvas = [[CGGI_GlyphCanvas alloc] init];806CGGI_InitCanvas(tmpCanvas, maxWidth, maxHeight, mode);807CGGI_FillImagesForGlyphsWithSizedCanvas(tmpCanvas, strike,808mode, glyphInfos, uniChars,809glyphs, len);810CGGI_FreeCanvas(tmpCanvas);811812[tmpCanvas release];813return;814}815NSMutableDictionary *threadDict =816[[NSThread currentThread] threadDictionary];817818NSString* theKey = (mode->glyphDescriptor == &rgb) ?819threadLocalLCDCanvasKey : threadLocalAACanvasKey;820821CGGI_GlyphCanvas *canvas = [threadDict objectForKey:theKey];822if (canvas == nil) {823canvas = [[CGGI_GlyphCanvas alloc] init];824[threadDict setObject:canvas forKey:theKey];825}826827CGGI_SizeCanvas(canvas, maxWidth, maxHeight, mode);828CGGI_FillImagesForGlyphsWithSizedCanvas(canvas, strike, mode,829glyphInfos, uniChars, glyphs, len);830}831832/*833* Finds the advances and bounding boxes of the characters in the run,834* cycles through all the bounds and calculates the maximum canvas space835* required by the largest glyph.836*837* Creates a GlyphInfo struct with a malloc that also encapsulates the838* image the struct points to. This is done to meet memory layout839* expectations in the Sun text rasterizer memory managment code.840* The image immediately follows the struct physically in memory.841*/842static inline void843CGGI_CreateGlyphInfos(jlong *glyphInfos, const AWTStrike *strike,844const CGGI_RenderingMode *mode,845const UnicodeScalarValue uniChars[], const CGGlyph glyphs[],846CGSize advances[], CGRect bboxes[], const CFIndex len)847{848AWTFont *font = strike->fAWTFont;849CGAffineTransform tx = strike->fTx;850JRSFontRenderingStyle bboxCGMode = JRSFontAlignStyleForFractionalMeasurement(strike->fStyle);851852CTFontRef fontRef = (CTFontRef)font->fFont;853CGGlyphImages_GetGlyphMetrics(fontRef, &tx, bboxCGMode, glyphs, len, bboxes, advances);854CGGI_GlyphInfoDescriptor* mainFontDescriptor = CGGI_GetGlyphInfoDescriptor(mode, fontRef);855856size_t maxWidth = 1;857size_t maxHeight = 1;858859CFIndex i;860for (i = 0; i < len; i++)861{862if (uniChars[i] != 0)863{864glyphInfos[i] = 0L;865continue; // will be handled later866}867868CGSize advance = advances[i];869CGRect bbox = bboxes[i];870871GlyphInfo *glyphInfo = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, mainFontDescriptor);872873if (maxWidth < glyphInfo->width) maxWidth = glyphInfo->width;874if (maxHeight < glyphInfo->height) maxHeight = glyphInfo->height;875876glyphInfos[i] = ptr_to_jlong(glyphInfo);877}878879CGGI_FillImagesForGlyphs(glyphInfos, strike, mode, uniChars,880glyphs, maxWidth, maxHeight, len);881}882883884#pragma mark --- Temporary Buffer Allocations and Initialization ---885886/*887* This stage separates the already valid glyph codes from the unicode values888* that need special handling - the rawGlyphCodes array is no longer used889* after this stage.890*/891static void892CGGI_CreateGlyphsAndScanForComplexities(jlong *glyphInfos,893const AWTStrike *strike,894const CGGI_RenderingMode *mode,895jint rawGlyphCodes[],896UnicodeScalarValue uniChars[], CGGlyph glyphs[],897CGSize advances[], CGRect bboxes[],898const CFIndex len)899{900CFIndex i;901for (i = 0; i < len; i++) {902jint code = rawGlyphCodes[i];903if (code < 0) {904glyphs[i] = 0;905uniChars[i] = -code;906} else {907glyphs[i] = code;908uniChars[i] = 0;909}910}911912CGGI_CreateGlyphInfos(glyphInfos, strike, mode,913uniChars, glyphs, advances, bboxes, len);914915#ifdef CGGI_DEBUG_HIT_COUNT916static size_t hitCount = 0;917hitCount++;918printf("%d\n", (int)hitCount);919#endif920}921922/*923* Conditionally stack allocates buffers for glyphs, bounding boxes,924* and advances. Unfortunately to use CG or CT in bulk runs (which is925* faster than calling them per character), we have to copy into and out926* of these buffers. Still a net win though.927*/928void929CGGlyphImages_GetGlyphImagePtrs(jlong glyphInfos[],930const AWTStrike *strike,931jint rawGlyphCodes[], const CFIndex len)932{933const CGGI_RenderingMode mode = CGGI_GetRenderingMode(strike);934935if (len < MAX_STACK_ALLOC_GLYPH_BUFFER_SIZE) {936CGRect bboxes[len];937CGSize advances[len];938CGGlyph glyphs[len];939UnicodeScalarValue uniChars[len];940941CGGI_CreateGlyphsAndScanForComplexities(glyphInfos, strike, &mode,942rawGlyphCodes, uniChars, glyphs,943advances, bboxes, len);944945return;946}947948// just do one malloc, and carve it up for all the buffers949void *buffer = malloc(sizeof(CGRect) * sizeof(CGSize) *950sizeof(CGGlyph) * sizeof(UnicodeScalarValue) * len);951if (buffer == NULL) {952[[NSException exceptionWithName:NSMallocException953reason:@"Failed to allocate memory for the temporary glyph strike and measurement buffers." userInfo:nil] raise];954}955956CGRect *bboxes = (CGRect *)(buffer);957CGSize *advances = (CGSize *)(bboxes + sizeof(CGRect) * len);958CGGlyph *glyphs = (CGGlyph *)(advances + sizeof(CGGlyph) * len);959UnicodeScalarValue *uniChars = (UnicodeScalarValue *)(glyphs + sizeof(UnicodeScalarValue) * len);960961CGGI_CreateGlyphsAndScanForComplexities(glyphInfos, strike, &mode,962rawGlyphCodes, uniChars, glyphs,963advances, bboxes, len);964965free(buffer);966}967968/*969* Calculates bounding boxes (for given transform) and advance (for untransformed 1pt-size font) for specified glyphs.970*/971void972CGGlyphImages_GetGlyphMetrics(const CTFontRef font,973const CGAffineTransform *tx,974const JRSFontRenderingStyle style,975const CGGlyph glyphs[],976size_t count,977CGRect bboxes[],978CGSize advances[]) {979if (IsEmojiFont(font)) {980// Glyph metrics for emoji font are not strictly proportional to font size,981// so we need to construct real-sized font object to calculate them.982// The logic here must match the logic in CGGI_CreateImageForGlyph,983// which performs glyph drawing.984985CGFloat fontSize = sqrt(fabs(tx->a * tx->d - tx->b * tx->c));986CTFontRef sizedFont = CTFontCreateCopyWithSymbolicTraits(font, fontSize, NULL, 0, 0);987988if (bboxes) {989// JRSFontGetBoundingBoxesForGlyphsAndStyle works incorrectly for AppleColorEmoji font:990// it uses bottom left corner of the glyph's bounding box as a fixed point of transformation991// instead of glyph's origin point (used at drawing). So, as a workaround,992// we request a bounding box for the untransformed glyph, and apply the transform ourselves.993JRSFontGetBoundingBoxesForGlyphsAndStyle(sizedFont, &CGAffineTransformIdentity, style, glyphs, count, bboxes);994CGAffineTransform txNormalized = CGAffineTransformMake(tx->a / fontSize,995tx->b / fontSize,996tx->c / fontSize,997tx->d / fontSize,9980, 0);999for (int i = 0; i < count; i++) {1000bboxes[i] = CGRectApplyAffineTransform(bboxes[i], txNormalized);1001}1002}10031004if (advances) {1005CTFontGetAdvancesForGlyphs(sizedFont, kCTFontDefaultOrientation, glyphs, advances, count);1006for (int i = 0; i < count; i++) {1007// Calling code will scale the result back1008advances[i].width /= fontSize;1009advances[i].height /= fontSize;1010}1011}10121013CFRelease(sizedFont);1014} else {1015if (bboxes) {1016JRSFontGetBoundingBoxesForGlyphsAndStyle(font, tx, style, glyphs, count, bboxes);1017}1018if (advances) {1019CTFontGetAdvancesForGlyphs(font, kCTFontDefaultOrientation, glyphs, advances, count);1020}1021}1022}10231024