Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/macosx/native_NOTIOS/sun/font/CGGlyphImages.m
38829 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_Buffer26#import <JavaNativeFoundation/JavaNativeFoundation.h>2728#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}310311312#pragma mark --- Pixel Size, Modes, and Canvas Shaping Helper Functions ---313314typedef struct CGGI_GlyphInfoDescriptor {315size_t pixelSize;316void (*copyFxnPtr)(CGGI_GlyphCanvas *canvas, GlyphInfo *info);317} CGGI_GlyphInfoDescriptor;318319typedef struct CGGI_RenderingMode {320CGGI_GlyphInfoDescriptor *glyphDescriptor;321JRSFontRenderingStyle cgFontMode;322} CGGI_RenderingMode;323324static CGGI_GlyphInfoDescriptor grey =325{ 1, &CGGI_CopyImageFromCanvasToAlphaInfo };326static CGGI_GlyphInfoDescriptor rgb =327{ 3, &CGGI_CopyImageFromCanvasToRGBInfo };328329static inline CGGI_RenderingMode330CGGI_GetRenderingMode(const AWTStrike *strike)331{332CGGI_RenderingMode mode;333mode.cgFontMode = strike->fStyle;334NSException *e = nil;335336switch (strike->fAAStyle) {337case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_OFF:338case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_ON:339mode.glyphDescriptor = &grey;340break;341case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HRGB:342case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HBGR:343case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_VRGB:344case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_VBGR:345mode.glyphDescriptor = &rgb;346break;347case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_GASP:348case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_DEFAULT:349default:350/* we expect that text antialiasing hint has been already351* evaluated. Report an error if we get 'unevaluated' hint here.352*/353e = [NSException354exceptionWithName:@"IllegalArgumentException"355reason:@"Invalid hint value"356userInfo:nil];357@throw e;358}359360return mode;361}362363364#pragma mark --- Canvas Managment ---365366/*367* Creates a new canvas of a fixed size, and initializes the CGContext as368* an 32-bit ARGB BitmapContext with some generic RGB color space.369*/370static inline void371CGGI_InitCanvas(CGGI_GlyphCanvas *canvas,372const vImagePixelCount width, const vImagePixelCount height,373const CGGI_RenderingMode* mode)374{375// our canvas is *always* 4-byte ARGB376size_t bytesPerRow = width * sizeof(UInt32);377size_t byteCount = bytesPerRow * height;378379canvas->image = malloc(sizeof(vImage_Buffer));380canvas->image->width = width;381canvas->image->height = height;382canvas->image->rowBytes = bytesPerRow;383384canvas->image->data = (void *)calloc(byteCount, sizeof(UInt8));385if (canvas->image->data == NULL) {386[[NSException exceptionWithName:NSMallocException387reason:@"Failed to allocate memory for the buffer which backs the CGContext for glyph strikes." userInfo:nil] raise];388}389390uint32_t bmpInfo = kCGImageAlphaPremultipliedFirst;391if (mode->glyphDescriptor == &rgb) {392bmpInfo |= kCGBitmapByteOrder32Host;393}394395CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);396canvas->context = CGBitmapContextCreate(canvas->image->data,397width, height, 8, bytesPerRow,398colorSpace,399bmpInfo);400401// set foreground color402CGContextSetRGBFillColor(canvas->context, 0.0f, 0.0f, 0.0f, 1.0f);403404CGContextSetFontSize(canvas->context, 1);405CGContextSaveGState(canvas->context);406407CGColorSpaceRelease(colorSpace);408}409410/*411* Releases the BitmapContext and the associated memory backing it.412*/413static inline void414CGGI_FreeCanvas(CGGI_GlyphCanvas *canvas)415{416if (canvas->context != NULL) {417CGContextRelease(canvas->context);418}419420if (canvas->image != NULL) {421if (canvas->image->data != NULL) {422free(canvas->image->data);423}424free(canvas->image);425}426}427428/*429* This is the slack space that is preallocated for the global GlyphCanvas430* when it needs to be expanded. It has been set somewhat liberally to431* avoid re-upsizing frequently.432*/433#define CGGI_GLYPH_CANVAS_SLACK 2.5434435/*436* Quick and easy inline to check if this canvas is big enough.437*/438static inline void439CGGI_SizeCanvas(CGGI_GlyphCanvas *canvas, const vImagePixelCount width,440const vImagePixelCount height,441const CGGI_RenderingMode* mode)442{443if (canvas->image != NULL &&444width < canvas->image->width &&445height < canvas->image->height)446{447return;448}449450// if we don't have enough space to strike the largest glyph in the451// run, resize the canvas452CGGI_FreeCanvas(canvas);453CGGI_InitCanvas(canvas,454width * CGGI_GLYPH_CANVAS_SLACK,455height * CGGI_GLYPH_CANVAS_SLACK,456mode);457JRSFontSetRenderingStyleOnContext(canvas->context, mode->cgFontMode);458}459460/*461* Clear the canvas by blitting white only into the region of interest462* (the rect which we will copy out of once the glyph is struck).463*/464static inline void465CGGI_ClearCanvas(CGGI_GlyphCanvas *canvas, GlyphInfo *info)466{467vImage_Buffer canvasRectToClear;468canvasRectToClear.data = canvas->image->data;469canvasRectToClear.height = info->height;470canvasRectToClear.width = info->width;471// use the row stride of the canvas, not the info472canvasRectToClear.rowBytes = canvas->image->rowBytes;473474// clean the canvas475#ifdef CGGI_DEBUG476Pixel_8888 opaqueWhite = { 0xE0, 0xE0, 0xE0, 0xE0 };477#else478Pixel_8888 opaqueWhite = { 0xFF, 0xFF, 0xFF, 0xFF };479#endif480481// clear canvas background and set foreground color482vImageBufferFill_ARGB8888(&canvasRectToClear, opaqueWhite, kvImageNoFlags);483}484485486#pragma mark --- GlyphInfo Creation & Copy Functions ---487488/*489* Creates a GlyphInfo with exactly the correct size image and measurements.490*/491#define CGGI_GLYPH_BBOX_PADDING 2.0f492static inline GlyphInfo *493CGGI_CreateNewGlyphInfoFrom(CGSize advance, CGRect bbox,494const AWTStrike *strike,495const CGGI_RenderingMode *mode)496{497size_t pixelSize = mode->glyphDescriptor->pixelSize;498499// adjust the bounding box to be 1px bigger on each side than what500// CGFont-whatever suggests - because it gives a bounding box that501// is too tight502bbox.size.width += CGGI_GLYPH_BBOX_PADDING * 2.0f;503bbox.size.height += CGGI_GLYPH_BBOX_PADDING * 2.0f;504bbox.origin.x -= CGGI_GLYPH_BBOX_PADDING;505bbox.origin.y -= CGGI_GLYPH_BBOX_PADDING;506507vImagePixelCount width = ceilf(bbox.size.width);508vImagePixelCount height = ceilf(bbox.size.height);509510// if the glyph is larger than 1MB, don't even try...511// the GlyphVector path should have taken over by now512// and zero pixels is ok513if (width * height > 1024 * 1024) {514width = 1;515height = 1;516}517advance = CGSizeApplyAffineTransform(advance, strike->fFontTx);518if (!JRSFontStyleUsesFractionalMetrics(strike->fStyle)) {519advance.width = round(advance.width);520advance.height = round(advance.height);521}522advance = CGSizeApplyAffineTransform(advance, strike->fDevTx);523524#ifdef USE_IMAGE_ALIGNED_MEMORY525// create separate memory526GlyphInfo *glyphInfo = (GlyphInfo *)malloc(sizeof(GlyphInfo));527void *image = (void *)malloc(height * width * pixelSize);528#else529// create a GlyphInfo struct fused to the image it points to530GlyphInfo *glyphInfo = (GlyphInfo *)malloc(sizeof(GlyphInfo) +531height * width * pixelSize);532#endif533534glyphInfo->advanceX = advance.width;535glyphInfo->advanceY = advance.height;536glyphInfo->topLeftX = round(bbox.origin.x);537glyphInfo->topLeftY = round(bbox.origin.y);538glyphInfo->width = width;539glyphInfo->height = height;540glyphInfo->rowBytes = width * pixelSize;541glyphInfo->cellInfo = NULL;542543#ifdef USE_IMAGE_ALIGNED_MEMORY544glyphInfo->image = image;545#else546glyphInfo->image = ((void *)glyphInfo) + sizeof(GlyphInfo);547#endif548549return glyphInfo;550}551552553#pragma mark --- Glyph Striking onto Canvas ---554555/*556* Clears the canvas, strikes the glyph with CoreGraphics, and then557* copies the struck pixels into the GlyphInfo image.558*/559static inline void560CGGI_CreateImageForGlyph561(CGGI_GlyphCanvas *canvas, const CGGlyph glyph,562GlyphInfo *info, const CGGI_RenderingMode *mode)563{564// clean the canvas565CGGI_ClearCanvas(canvas, info);566567// strike the glyph in the upper right corner568CGContextShowGlyphsAtPoint(canvas->context,569-info->topLeftX,570canvas->image->height + info->topLeftY,571&glyph, 1);572573// copy the glyph from the canvas into the info574(*mode->glyphDescriptor->copyFxnPtr)(canvas, info);575}576577/*578* CoreText path...579*/580static inline GlyphInfo *581CGGI_CreateImageForUnicode582(CGGI_GlyphCanvas *canvas, const AWTStrike *strike,583const CGGI_RenderingMode *mode, const UniChar uniChar)584{585// save the state of the world586CGContextSaveGState(canvas->context);587588// get the glyph, measure it using CG589CGGlyph glyph;590CTFontRef fallback;591if (uniChar > 0xFFFF) {592UTF16Char charRef[2];593CTS_BreakupUnicodeIntoSurrogatePairs(uniChar, charRef);594CGGlyph glyphTmp[2];595fallback = CTS_CopyCTFallbackFontAndGlyphForUnicode(strike->fAWTFont, (const UTF16Char *)&charRef, (CGGlyph *)&glyphTmp, 2);596glyph = glyphTmp[0];597} else {598UTF16Char charRef;599charRef = (UTF16Char) uniChar; // truncate.600fallback = CTS_CopyCTFallbackFontAndGlyphForUnicode(strike->fAWTFont, (const UTF16Char *)&charRef, &glyph, 1);601}602603CGAffineTransform tx = strike->fTx;604JRSFontRenderingStyle style = JRSFontAlignStyleForFractionalMeasurement(strike->fStyle);605606CGRect bbox;607JRSFontGetBoundingBoxesForGlyphsAndStyle(fallback, &tx, style, &glyph, 1, &bbox);608609CGSize advance;610CTFontGetAdvancesForGlyphs(fallback, kCTFontDefaultOrientation, &glyph, &advance, 1);611612// create the Sun2D GlyphInfo we are going to strike into613GlyphInfo *info = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, mode);614615// fix the context size, just in case the substituted character is unexpectedly large616CGGI_SizeCanvas(canvas, info->width, info->height, mode);617618// align the transform for the real CoreText strike619CGContextSetTextMatrix(canvas->context, strike->fAltTx);620621const CGFontRef cgFallback = CTFontCopyGraphicsFont(fallback, NULL);622CGContextSetFont(canvas->context, cgFallback);623CFRelease(cgFallback);624625// clean the canvas - align, strike, and copy the glyph from the canvas into the info626CGGI_CreateImageForGlyph(canvas, glyph, info, mode);627628// restore the state of the world629CGContextRestoreGState(canvas->context);630631CFRelease(fallback);632#ifdef CGGI_DEBUG633DUMP_GLYPHINFO(info);634#endif635636#ifdef CGGI_DEBUG_DUMP637DUMP_IMG_PIXELS("CGGI Canvas", canvas->image);638#if 0639PRINT_CGSTATES_INFO(NULL);640#endif641#endif642643return info;644}645646647#pragma mark --- GlyphInfo Filling and Canvas Managment ---648649/*650* Sets all the per-run properties for the canvas, and then iterates through651* the character run, and creates images in the GlyphInfo structs.652*653* Not inlined because it would create two copies in the function below654*/655static void656CGGI_FillImagesForGlyphsWithSizedCanvas(CGGI_GlyphCanvas *canvas,657const AWTStrike *strike,658const CGGI_RenderingMode *mode,659jlong glyphInfos[],660const UniChar uniChars[],661const CGGlyph glyphs[],662const CFIndex len)663{664CGContextSetTextMatrix(canvas->context, strike->fAltTx);665666CGContextSetFont(canvas->context, strike->fAWTFont->fNativeCGFont);667JRSFontSetRenderingStyleOnContext(canvas->context, strike->fStyle);668669CFIndex i;670for (i = 0; i < len; i++) {671GlyphInfo *info = (GlyphInfo *)jlong_to_ptr(glyphInfos[i]);672if (info != NULL) {673CGGI_CreateImageForGlyph(canvas, glyphs[i], info, mode);674} else {675info = CGGI_CreateImageForUnicode(canvas, strike, mode, uniChars[i]);676glyphInfos[i] = ptr_to_jlong(info);677}678#ifdef CGGI_DEBUG679DUMP_GLYPHINFO(info);680#endif681682#ifdef CGGI_DEBUG_DUMP683DUMP_IMG_PIXELS("CGGI Canvas", canvas->image);684#endif685}686#ifdef CGGI_DEBUG_DUMP687DUMP_IMG_PIXELS("CGGI Canvas", canvas->image);688PRINT_CGSTATES_INFO(canvas->context);689#endif690}691692static NSString *threadLocalAACanvasKey =693@"Java CoreGraphics Text Renderer Cached Canvas for AA";694695static NSString *threadLocalLCDCanvasKey =696@"Java CoreGraphics Text Renderer Cached Canvas for LCD";697698/*699* This is the maximum length and height times the above slack squared700* to determine if we go with the global canvas, or malloc one on the spot.701*/702#define CGGI_GLYPH_CANVAS_MAX 100703704/*705* Based on the space needed to strike the largest character in the run,706* either use the global shared canvas, or make one up on the spot, strike707* the glyphs, and destroy it.708*/709static inline void710CGGI_FillImagesForGlyphs(jlong *glyphInfos, const AWTStrike *strike,711const CGGI_RenderingMode *mode,712const UniChar uniChars[], const CGGlyph glyphs[],713const size_t maxWidth, const size_t maxHeight,714const CFIndex len)715{716if (maxWidth*maxHeight*CGGI_GLYPH_CANVAS_SLACK*CGGI_GLYPH_CANVAS_SLACK >717CGGI_GLYPH_CANVAS_MAX*CGGI_GLYPH_CANVAS_MAX*CGGI_GLYPH_CANVAS_SLACK*CGGI_GLYPH_CANVAS_SLACK)718{719CGGI_GlyphCanvas *tmpCanvas = [[CGGI_GlyphCanvas alloc] init];720CGGI_InitCanvas(tmpCanvas, maxWidth, maxHeight, mode);721CGGI_FillImagesForGlyphsWithSizedCanvas(tmpCanvas, strike,722mode, glyphInfos, uniChars,723glyphs, len);724CGGI_FreeCanvas(tmpCanvas);725726[tmpCanvas release];727return;728}729NSMutableDictionary *threadDict =730[[NSThread currentThread] threadDictionary];731732NSString* theKey = (mode->glyphDescriptor == &rgb) ?733threadLocalLCDCanvasKey : threadLocalAACanvasKey;734735CGGI_GlyphCanvas *canvas = [threadDict objectForKey:theKey];736if (canvas == nil) {737canvas = [[CGGI_GlyphCanvas alloc] init];738[threadDict setObject:canvas forKey:theKey];739}740741CGGI_SizeCanvas(canvas, maxWidth, maxHeight, mode);742CGGI_FillImagesForGlyphsWithSizedCanvas(canvas, strike, mode,743glyphInfos, uniChars, glyphs, len);744}745746/*747* Finds the advances and bounding boxes of the characters in the run,748* cycles through all the bounds and calculates the maximum canvas space749* required by the largest glyph.750*751* Creates a GlyphInfo struct with a malloc that also encapsulates the752* image the struct points to. This is done to meet memory layout753* expectations in the Sun text rasterizer memory managment code.754* The image immediately follows the struct physically in memory.755*/756static inline void757CGGI_CreateGlyphInfos(jlong *glyphInfos, const AWTStrike *strike,758const CGGI_RenderingMode *mode,759const UniChar uniChars[], const CGGlyph glyphs[],760CGSize advances[], CGRect bboxes[], const CFIndex len)761{762AWTFont *font = strike->fAWTFont;763CGAffineTransform tx = strike->fTx;764JRSFontRenderingStyle bboxCGMode = JRSFontAlignStyleForFractionalMeasurement(strike->fStyle);765766JRSFontGetBoundingBoxesForGlyphsAndStyle((CTFontRef)font->fFont, &tx, bboxCGMode, glyphs, len, bboxes);767CTFontGetAdvancesForGlyphs((CTFontRef)font->fFont, kCTFontDefaultOrientation, glyphs, advances, len);768769size_t maxWidth = 1;770size_t maxHeight = 1;771772CFIndex i;773for (i = 0; i < len; i++)774{775if (uniChars[i] != 0)776{777glyphInfos[i] = 0L;778continue; // will be handled later779}780781CGSize advance = advances[i];782CGRect bbox = bboxes[i];783784GlyphInfo *glyphInfo = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, mode);785786if (maxWidth < glyphInfo->width) maxWidth = glyphInfo->width;787if (maxHeight < glyphInfo->height) maxHeight = glyphInfo->height;788789glyphInfos[i] = ptr_to_jlong(glyphInfo);790}791792CGGI_FillImagesForGlyphs(glyphInfos, strike, mode, uniChars,793glyphs, maxWidth, maxHeight, len);794}795796797#pragma mark --- Temporary Buffer Allocations and Initialization ---798799/*800* This stage separates the already valid glyph codes from the unicode values801* that need special handling - the rawGlyphCodes array is no longer used802* after this stage.803*/804static void805CGGI_CreateGlyphsAndScanForComplexities(jlong *glyphInfos,806const AWTStrike *strike,807const CGGI_RenderingMode *mode,808jint rawGlyphCodes[],809UniChar uniChars[], CGGlyph glyphs[],810CGSize advances[], CGRect bboxes[],811const CFIndex len)812{813CFIndex i;814for (i = 0; i < len; i++) {815jint code = rawGlyphCodes[i];816if (code < 0) {817glyphs[i] = 0;818uniChars[i] = -code;819} else {820glyphs[i] = code;821uniChars[i] = 0;822}823}824825CGGI_CreateGlyphInfos(glyphInfos, strike, mode,826uniChars, glyphs, advances, bboxes, len);827828#ifdef CGGI_DEBUG_HIT_COUNT829static size_t hitCount = 0;830hitCount++;831printf("%d\n", (int)hitCount);832#endif833}834835/*836* Conditionally stack allocates buffers for glyphs, bounding boxes,837* and advances. Unfortunately to use CG or CT in bulk runs (which is838* faster than calling them per character), we have to copy into and out839* of these buffers. Still a net win though.840*/841void842CGGlyphImages_GetGlyphImagePtrs(jlong glyphInfos[],843const AWTStrike *strike,844jint rawGlyphCodes[], const CFIndex len)845{846const CGGI_RenderingMode mode = CGGI_GetRenderingMode(strike);847848if (len < MAX_STACK_ALLOC_GLYPH_BUFFER_SIZE) {849CGRect bboxes[len];850CGSize advances[len];851CGGlyph glyphs[len];852UniChar uniChars[len];853854CGGI_CreateGlyphsAndScanForComplexities(glyphInfos, strike, &mode,855rawGlyphCodes, uniChars, glyphs,856advances, bboxes, len);857858return;859}860861// just do one malloc, and carve it up for all the buffers862void *buffer = malloc(sizeof(CGRect) * sizeof(CGSize) *863sizeof(CGGlyph) * sizeof(UniChar) * len);864if (buffer == NULL) {865[[NSException exceptionWithName:NSMallocException866reason:@"Failed to allocate memory for the temporary glyph strike and measurement buffers." userInfo:nil] raise];867}868869CGRect *bboxes = (CGRect *)(buffer);870CGSize *advances = (CGSize *)(bboxes + sizeof(CGRect) * len);871CGGlyph *glyphs = (CGGlyph *)(advances + sizeof(CGGlyph) * len);872UniChar *uniChars = (UniChar *)(glyphs + sizeof(UniChar) * len);873874CGGI_CreateGlyphsAndScanForComplexities(glyphInfos, strike, &mode,875rawGlyphCodes, uniChars, glyphs,876advances, bboxes, len);877878free(buffer);879}880881882