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 <Accelerate/Accelerate.h> // for vImage_Buffer
27
28#import "JNIUtilities.h"
29#import "CGGlyphImages.h"
30#import "CoreTextSupport.h"
31#import "fontscalerdefs.h" // contains the definition of GlyphInfo struct
32
33#import "sun_awt_SunHints.h"
34
35//#define USE_IMAGE_ALIGNED_MEMORY 1
36//#define CGGI_DEBUG 1
37//#define CGGI_DEBUG_DUMP 1
38//#define CGGI_DEBUG_HIT_COUNT 1
39
40#define PRINT_TX(x) \
41    NSLog(@"(%f, %f, %f, %f, %f, %f)", x.a, x.b, x.c, x.d, x.tx, x.ty);
42
43/*
44 * The GlyphCanvas is a global shared CGContext that characters are struck into.
45 * For each character, the glyph is struck, copied into a GlyphInfo struct, and
46 * the canvas is cleared for the next glyph.
47 *
48 * If the necessary canvas is too large, the shared one will not be used and a
49 * temporary one will be provided.
50 */
51@interface CGGI_GlyphCanvas : NSObject {
52@public
53    CGContextRef context;
54    vImage_Buffer *image;
55}
56@end;
57
58@implementation CGGI_GlyphCanvas
59@end
60
61
62#pragma mark --- Debugging Helpers ---
63
64/*
65 * These debug functions are only compiled when CGGI_DEBUG is activated.
66 * They will print out a full UInt8 canvas and any pixels struck (assuming
67 * the canvas is not too big).
68 *
69 * As another debug feature, the entire canvas will be filled with a light
70 * alpha value so it is easy to see where the glyph painting regions are
71 * at runtime.
72 */
73
74#ifdef CGGI_DEBUG_DUMP
75static void
76DUMP_PIXELS(const char msg[], const UInt8 pixels[],
77            const size_t bytesPerPixel, const int width, const int height)
78{
79    printf("| %s: (%d, %d)\n", msg, width, height);
80
81    if (width > 80 || height > 80) {
82        printf("| too big\n");
83        return;
84    }
85
86    size_t i, j = 0, k, size = width * height;
87    for (i = 0; i < size; i++) {
88        for (k = 0; k < bytesPerPixel; k++) {
89            if (pixels[i * bytesPerPixel + k] > 0x80) j++;
90        }
91    }
92
93    if (j == 0) {
94        printf("| empty\n");
95        return;
96    }
97
98    printf("|_");
99    int x, y;
100    for (x = 0; x < width; x++) {
101        printf("__");
102    }
103    printf("_\n");
104
105    for (y = 0; y < height; y++) {
106        printf("| ");
107        for (x = 0; x < width; x++) {
108            int p = 0;
109            for(k = 0; k < bytesPerPixel; k++) {
110                p += pixels[(y * width + x) * bytesPerPixel + k];
111            }
112
113            if (p < 0x80) {
114                printf("  ");
115            } else {
116                printf("[]");
117            }
118        }
119        printf(" |\n");
120    }
121}
122
123static void
124DUMP_IMG_PIXELS(const char msg[], const vImage_Buffer *image)
125{
126    const void *pixels = image->data;
127    const size_t pixelSize = image->rowBytes / image->width;
128    const size_t width = image->width;
129    const size_t height = image->height;
130
131    DUMP_PIXELS(msg, pixels, pixelSize, width, height);
132}
133
134static void
135PRINT_CGSTATES_INFO(const CGContextRef cgRef)
136{
137    // TODO(cpc): lots of SPI use in this method; remove/rewrite?
138#if 0
139    CGRect clip = CGContextGetClipBoundingBox(cgRef);
140    fprintf(stderr, "    clip: ((%f, %f), (%f, %f))\n",
141            clip.origin.x, clip.origin.y, clip.size.width, clip.size.height);
142
143    CGAffineTransform ctm = CGContextGetCTM(cgRef);
144    fprintf(stderr, "    ctm: (%f, %f, %f, %f, %f, %f)\n",
145            ctm.a, ctm.b, ctm.c, ctm.d, ctm.tx, ctm.ty);
146
147    CGAffineTransform txtTx = CGContextGetTextMatrix(cgRef);
148    fprintf(stderr, "    txtTx: (%f, %f, %f, %f, %f, %f)\n",
149            txtTx.a, txtTx.b, txtTx.c, txtTx.d, txtTx.tx, txtTx.ty);
150
151    if (CGContextIsPathEmpty(cgRef) == 0) {
152        CGPoint pathpoint = CGContextGetPathCurrentPoint(cgRef);
153        CGRect pathbbox = CGContextGetPathBoundingBox(cgRef);
154        fprintf(stderr, "    [pathpoint: (%f, %f)] [pathbbox: ((%f, %f), (%f, %f))]\n",
155                pathpoint.x, pathpoint.y, pathbbox.origin.x, pathbbox.origin.y,
156                pathbbox.size.width, pathbbox.size.width);
157    }
158
159    CGFloat linewidth = CGContextGetLineWidth(cgRef);
160    CGLineCap linecap = CGContextGetLineCap(cgRef);
161    CGLineJoin linejoin = CGContextGetLineJoin(cgRef);
162    CGFloat miterlimit = CGContextGetMiterLimit(cgRef);
163    size_t dashcount = CGContextGetLineDashCount(cgRef);
164    fprintf(stderr, "    [linewidth: %f] [linecap: %d] [linejoin: %d] [miterlimit: %f] [dashcount: %lu]\n",
165            linewidth, linecap, linejoin, miterlimit, (unsigned long)dashcount);
166
167    CGFloat smoothness = CGContextGetSmoothness(cgRef);
168    bool antialias = CGContextGetShouldAntialias(cgRef);
169    bool smoothfont = CGContextGetShouldSmoothFonts(cgRef);
170    JRSFontRenderingStyle fRendMode = CGContextGetFontRenderingMode(cgRef);
171    fprintf(stderr, "    [smoothness: %f] [antialias: %d] [smoothfont: %d] [fontrenderingmode: %d]\n",
172            smoothness, antialias, smoothfont, fRendMode);
173#endif
174}
175#endif
176
177#ifdef CGGI_DEBUG
178
179static void
180DUMP_GLYPHINFO(const GlyphInfo *info)
181{
182    printf("size: (%d, %d) pixelSize: %d\n",
183           info->width, info->height, info->rowBytes / info->width);
184    printf("adv: (%f, %f) top: (%f, %f)\n",
185           info->advanceX, info->advanceY, info->topLeftX, info->topLeftY);
186
187#ifdef CGGI_DEBUG_DUMP
188    DUMP_PIXELS("Glyph Info Struct",
189                info->image, info->rowBytes / info->width,
190                info->width, info->height);
191#endif
192}
193
194#endif
195
196
197#pragma mark --- Font Rendering Mode Descriptors ---
198static Int32 reverseGamma = 0;
199
200static UInt8 reverseGammaLut[256] = { 0 };
201
202static inline UInt8* getReverseGammaLut() {
203    if (reverseGamma == 0) {
204        // initialize gamma lut
205        double gamma;
206        int i;
207        const char* pGammaEnv = getenv("J2D_LCD_REVERSE_GAMMA");
208        if (pGammaEnv != NULL) {
209            reverseGamma = atol(pGammaEnv);
210        }
211
212        if (reverseGamma < 100 || reverseGamma > 250) {
213            reverseGamma = 180;
214        }
215
216        gamma = 100.0 / reverseGamma;
217        for (i = 0; i < 256; i++) {
218            double x = ((double)i) / 255.0;
219            reverseGammaLut[i] = (UInt8)(255 * pow(x, gamma));
220        }
221    }
222    return reverseGammaLut;
223}
224
225static inline void
226CGGI_CopyARGBPixelToRGBPixel(const UInt32 p, UInt8 *dst)
227{
228    UInt8* lut = getReverseGammaLut();
229
230    *(dst + 0) = lut[0xFF - (p >> 16 & 0xFF)];  // red
231    *(dst + 1) = lut[0xFF - (p >>  8 & 0xFF)];  // green
232    *(dst + 2) = lut[0xFF - (p & 0xFF)];        // blue
233}
234
235static void
236CGGI_CopyImageFromCanvasToRGBInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
237{
238    UInt32 *src = (UInt32 *)canvas->image->data;
239    size_t srcRowWidth = canvas->image->width;
240
241    UInt8 *dest = (UInt8 *)info->image;
242    size_t destRowWidth = info->width;
243
244    size_t height = info->height;
245
246    size_t y;
247
248    // fill empty glyph image with black-on-white glyph
249    for (y = 0; y < height; y++) {
250        size_t destRow = y * destRowWidth * 3;
251        size_t srcRow = y * srcRowWidth;
252
253        size_t x;
254        for (x = 0; x < destRowWidth; x++) {
255            CGGI_CopyARGBPixelToRGBPixel(src[srcRow + x],
256                                         dest + destRow + x * 3);
257        }
258    }
259}
260
261//static void CGGI_copyImageFromCanvasToAlphaInfo
262//(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
263//{
264//    vImage_Buffer infoBuffer;
265//    infoBuffer.data = info->image;
266//    infoBuffer.width = info->width;
267//    infoBuffer.height = info->height;
268//    infoBuffer.rowBytes = info->width; // three bytes per RGB pixel
269//
270//    UInt8 scrapPixel[info->width * info->height];
271//    vImage_Buffer scrapBuffer;
272//    scrapBuffer.data = &scrapPixel;
273//    scrapBuffer.width = info->width;
274//    scrapBuffer.height = info->height;
275//    scrapBuffer.rowBytes = info->width;
276//
277//    vImageConvert_ARGB8888toPlanar8(canvas->image, &infoBuffer,
278//        &scrapBuffer, &scrapBuffer, &scrapBuffer, kvImageNoFlags);
279//}
280
281static inline UInt8
282CGGI_ConvertBWPixelToByteGray(UInt32 p)
283{
284    return 0xFF - (((p >> 24 & 0xFF) + (p >> 16 & 0xFF) + (p >> 8 & 0xFF)) / 3);
285}
286
287static void
288CGGI_CopyImageFromCanvasToAlphaInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
289{
290    UInt32 *src = (UInt32 *)canvas->image->data;
291    size_t srcRowWidth = canvas->image->width;
292
293    UInt8 *dest = (UInt8 *)info->image;
294    size_t destRowWidth = info->width;
295
296    size_t height = info->height;
297
298    size_t y;
299
300    // fill empty glyph image with black-on-white glyph
301    for (y = 0; y < height; y++) {
302        size_t destRow = y * destRowWidth;
303        size_t srcRow = y * srcRowWidth;
304        size_t x;
305        for (x = 0; x < destRowWidth; x++) {
306            UInt32 p = src[srcRow + x];
307            dest[destRow + x] = CGGI_ConvertBWPixelToByteGray(p);
308        }
309    }
310}
311
312
313#pragma mark --- Pixel Size, Modes, and Canvas Shaping Helper Functions ---
314
315typedef struct CGGI_GlyphInfoDescriptor {
316    size_t pixelSize;
317    void (*copyFxnPtr)(CGGI_GlyphCanvas *canvas, GlyphInfo *info);
318} CGGI_GlyphInfoDescriptor;
319
320typedef struct CGGI_RenderingMode {
321    CGGI_GlyphInfoDescriptor *glyphDescriptor;
322    JRSFontRenderingStyle cgFontMode;
323} CGGI_RenderingMode;
324
325static CGGI_GlyphInfoDescriptor grey =
326    { 1, &CGGI_CopyImageFromCanvasToAlphaInfo };
327static CGGI_GlyphInfoDescriptor rgb =
328    { 3, &CGGI_CopyImageFromCanvasToRGBInfo };
329
330static inline CGGI_RenderingMode
331CGGI_GetRenderingMode(const AWTStrike *strike)
332{
333    CGGI_RenderingMode mode;
334    mode.cgFontMode = strike->fStyle;
335    NSException *e = nil;
336
337    switch (strike->fAAStyle) {
338    case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_OFF:
339    case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_ON:
340        mode.glyphDescriptor = &grey;
341        break;
342    case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HRGB:
343    case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HBGR:
344    case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_VRGB:
345    case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_VBGR:
346        mode.glyphDescriptor = &rgb;
347        break;
348    case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_GASP:
349    case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_DEFAULT:
350    default:
351        /* we expect that text antialiasing hint has been already
352         * evaluated. Report an error if we get 'unevaluated' hint here.
353         */
354        e = [NSException
355                exceptionWithName:@"IllegalArgumentException"
356                reason:@"Invalid hint value"
357                userInfo:nil];
358        @throw e;
359    }
360
361    return mode;
362}
363
364
365#pragma mark --- Canvas Managment ---
366
367/*
368 * Creates a new canvas of a fixed size, and initializes the CGContext as
369 * an 32-bit ARGB BitmapContext with some generic RGB color space.
370 */
371static inline void
372CGGI_InitCanvas(CGGI_GlyphCanvas *canvas,
373                const vImagePixelCount width, const vImagePixelCount height,
374                const CGGI_RenderingMode* mode)
375{
376    // our canvas is *always* 4-byte ARGB
377    size_t bytesPerRow = width * sizeof(UInt32);
378    size_t byteCount = bytesPerRow * height;
379
380    canvas->image = malloc(sizeof(vImage_Buffer));
381    canvas->image->width = width;
382    canvas->image->height = height;
383    canvas->image->rowBytes = bytesPerRow;
384
385    canvas->image->data = (void *)calloc(byteCount, sizeof(UInt8));
386    if (canvas->image->data == NULL) {
387        [[NSException exceptionWithName:NSMallocException
388            reason:@"Failed to allocate memory for the buffer which backs the CGContext for glyph strikes." userInfo:nil] raise];
389    }
390
391    uint32_t bmpInfo = kCGImageAlphaPremultipliedFirst;
392    if (mode->glyphDescriptor == &rgb) {
393        bmpInfo |= kCGBitmapByteOrder32Host;
394    }
395
396    CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
397    canvas->context = CGBitmapContextCreate(canvas->image->data,
398                                            width, height, 8, bytesPerRow,
399                                            colorSpace,
400                                            bmpInfo);
401
402    // set foreground color
403    CGContextSetRGBFillColor(canvas->context, 0.0f, 0.0f, 0.0f, 1.0f);
404
405    CGContextSetFontSize(canvas->context, 1);
406    CGContextSaveGState(canvas->context);
407
408    CGColorSpaceRelease(colorSpace);
409}
410
411/*
412 * Releases the BitmapContext and the associated memory backing it.
413 */
414static inline void
415CGGI_FreeCanvas(CGGI_GlyphCanvas *canvas)
416{
417    if (canvas->context != NULL) {
418        CGContextRelease(canvas->context);
419    }
420
421    if (canvas->image != NULL) {
422        if (canvas->image->data != NULL) {
423            free(canvas->image->data);
424        }
425        free(canvas->image);
426    }
427}
428
429/*
430 * This is the slack space that is preallocated for the global GlyphCanvas
431 * when it needs to be expanded. It has been set somewhat liberally to
432 * avoid re-upsizing frequently.
433 */
434#define CGGI_GLYPH_CANVAS_SLACK 2.5
435
436/*
437 * Quick and easy inline to check if this canvas is big enough.
438 */
439static inline void
440CGGI_SizeCanvas(CGGI_GlyphCanvas *canvas, const vImagePixelCount width,
441        const vImagePixelCount height,
442        const CGGI_RenderingMode* mode)
443{
444    if (canvas->image != NULL &&
445        width  < canvas->image->width &&
446        height < canvas->image->height)
447    {
448        return;
449    }
450
451    // if we don't have enough space to strike the largest glyph in the
452    // run, resize the canvas
453    CGGI_FreeCanvas(canvas);
454    CGGI_InitCanvas(canvas,
455                    width * CGGI_GLYPH_CANVAS_SLACK,
456                    height * CGGI_GLYPH_CANVAS_SLACK,
457                    mode);
458    JRSFontSetRenderingStyleOnContext(canvas->context, mode->cgFontMode);
459}
460
461/*
462 * Clear the canvas by blitting white only into the region of interest
463 * (the rect which we will copy out of once the glyph is struck).
464 */
465static inline void
466CGGI_ClearCanvas(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
467{
468    vImage_Buffer canvasRectToClear;
469    canvasRectToClear.data = canvas->image->data;
470    canvasRectToClear.height = info->height;
471    canvasRectToClear.width = info->width;
472    // use the row stride of the canvas, not the info
473    canvasRectToClear.rowBytes = canvas->image->rowBytes;
474
475    // clean the canvas
476#ifdef CGGI_DEBUG
477    Pixel_8888 opaqueWhite = { 0xE0, 0xE0, 0xE0, 0xE0 };
478#else
479    Pixel_8888 opaqueWhite = { 0xFF, 0xFF, 0xFF, 0xFF };
480#endif
481
482    // clear canvas background and set foreground color
483    vImageBufferFill_ARGB8888(&canvasRectToClear, opaqueWhite, kvImageNoFlags);
484}
485
486
487#pragma mark --- GlyphInfo Creation & Copy Functions ---
488
489/*
490 * Creates a GlyphInfo with exactly the correct size image and measurements.
491 */
492#define CGGI_GLYPH_BBOX_PADDING 2.0f
493static inline GlyphInfo *
494CGGI_CreateNewGlyphInfoFrom(CGSize advance, CGRect bbox,
495                            const AWTStrike *strike,
496                            const CGGI_RenderingMode *mode)
497{
498    size_t pixelSize = mode->glyphDescriptor->pixelSize;
499
500    // adjust the bounding box to be 1px bigger on each side than what
501    // CGFont-whatever suggests - because it gives a bounding box that
502    // is too tight
503    bbox.size.width += CGGI_GLYPH_BBOX_PADDING * 2.0f;
504    bbox.size.height += CGGI_GLYPH_BBOX_PADDING * 2.0f;
505    bbox.origin.x -= CGGI_GLYPH_BBOX_PADDING;
506    bbox.origin.y -= CGGI_GLYPH_BBOX_PADDING;
507
508    vImagePixelCount width = ceilf(bbox.size.width);
509    vImagePixelCount height = ceilf(bbox.size.height);
510
511    // if the glyph is larger than 1MB, don't even try...
512    // the GlyphVector path should have taken over by now
513    // and zero pixels is ok
514    if (width * height > 1024 * 1024) {
515        width = 1;
516        height = 1;
517    }
518    advance = CGSizeApplyAffineTransform(advance, strike->fFontTx);
519    if (!JRSFontStyleUsesFractionalMetrics(strike->fStyle)) {
520        advance.width = round(advance.width);
521        advance.height = round(advance.height);
522    }
523    advance = CGSizeApplyAffineTransform(advance, strike->fDevTx);
524
525#ifdef USE_IMAGE_ALIGNED_MEMORY
526    // create separate memory
527    GlyphInfo *glyphInfo = (GlyphInfo *)malloc(sizeof(GlyphInfo));
528    void *image = (void *)malloc(height * width * pixelSize);
529#else
530    // create a GlyphInfo struct fused to the image it points to
531    GlyphInfo *glyphInfo = (GlyphInfo *)malloc(sizeof(GlyphInfo) +
532                                               height * width * pixelSize);
533#endif
534
535    glyphInfo->advanceX = advance.width;
536    glyphInfo->advanceY = advance.height;
537    glyphInfo->topLeftX = round(bbox.origin.x);
538    glyphInfo->topLeftY = round(bbox.origin.y);
539    glyphInfo->width = width;
540    glyphInfo->height = height;
541    glyphInfo->rowBytes = width * pixelSize;
542    glyphInfo->cellInfo = NULL;
543
544#ifdef USE_IMAGE_ALIGNED_MEMORY
545    glyphInfo->image = image;
546#else
547    glyphInfo->image = ((void *)glyphInfo) + sizeof(GlyphInfo);
548#endif
549
550    return glyphInfo;
551}
552
553
554#pragma mark --- Glyph Striking onto Canvas ---
555
556/*
557 * Clears the canvas, strikes the glyph with CoreGraphics, and then
558 * copies the struck pixels into the GlyphInfo image.
559 */
560static inline void
561CGGI_CreateImageForGlyph
562    (CGGI_GlyphCanvas *canvas, const CGGlyph glyph,
563     GlyphInfo *info, const CGGI_RenderingMode *mode)
564{
565    if (isnan(info->topLeftX) || isnan(info->topLeftY)) {
566        // Explicitly set glyphInfo width/height to be 0 to ensure
567        // zero length glyph image is copied into GlyphInfo from canvas
568        info->width = 0;
569        info->height = 0;
570
571        // copy the "empty" glyph from the canvas into the info
572        (*mode->glyphDescriptor->copyFxnPtr)(canvas, info);
573        return;
574    }
575
576    // clean the canvas
577    CGGI_ClearCanvas(canvas, info);
578
579    // strike the glyph in the upper right corner
580    CGContextShowGlyphsAtPoint(canvas->context,
581                               -info->topLeftX,
582                               canvas->image->height + info->topLeftY,
583                               &glyph, 1);
584    // copy the glyph from the canvas into the info
585    (*mode->glyphDescriptor->copyFxnPtr)(canvas, info);
586}
587
588/*
589 * CoreText path...
590 */
591static inline GlyphInfo *
592CGGI_CreateImageForUnicode
593    (CGGI_GlyphCanvas *canvas, const AWTStrike *strike,
594     const CGGI_RenderingMode *mode, const UnicodeScalarValue uniChar)
595{
596    // save the state of the world
597    CGContextSaveGState(canvas->context);
598
599    // get the glyph, measure it using CG
600    CGGlyph glyph;
601    CTFontRef fallback;
602    if (uniChar > 0xFFFF) {
603        UTF16Char charRef[2];
604        CTS_BreakupUnicodeIntoSurrogatePairs(uniChar, charRef);
605        CGGlyph glyphTmp[2];
606        fallback = CTS_CopyCTFallbackFontAndGlyphForUnicode(strike->fAWTFont, (const UTF16Char *)&charRef, (CGGlyph *)&glyphTmp, 2);
607        glyph = glyphTmp[0];
608    } else {
609        UTF16Char charRef;
610        charRef = (UTF16Char) uniChar; // truncate.
611        fallback = CTS_CopyCTFallbackFontAndGlyphForUnicode(strike->fAWTFont, (const UTF16Char *)&charRef, &glyph, 1);
612    }
613
614    CGAffineTransform tx = strike->fTx;
615    JRSFontRenderingStyle style = JRSFontAlignStyleForFractionalMeasurement(strike->fStyle);
616
617    CGRect bbox;
618    JRSFontGetBoundingBoxesForGlyphsAndStyle(fallback, &tx, style, &glyph, 1, &bbox);
619
620    CGSize advance;
621    CTFontGetAdvancesForGlyphs(fallback, kCTFontDefaultOrientation, &glyph, &advance, 1);
622
623    // create the Sun2D GlyphInfo we are going to strike into
624    GlyphInfo *info = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, mode);
625
626    // fix the context size, just in case the substituted character is unexpectedly large
627    CGGI_SizeCanvas(canvas, info->width, info->height, mode);
628
629    // align the transform for the real CoreText strike
630    CGContextSetTextMatrix(canvas->context, strike->fAltTx);
631
632    const CGFontRef cgFallback = CTFontCopyGraphicsFont(fallback, NULL);
633    CGContextSetFont(canvas->context, cgFallback);
634    CFRelease(cgFallback);
635
636    // clean the canvas - align, strike, and copy the glyph from the canvas into the info
637    CGGI_CreateImageForGlyph(canvas, glyph, info, mode);
638
639    // restore the state of the world
640    CGContextRestoreGState(canvas->context);
641
642    CFRelease(fallback);
643#ifdef CGGI_DEBUG
644    DUMP_GLYPHINFO(info);
645#endif
646
647#ifdef CGGI_DEBUG_DUMP
648    DUMP_IMG_PIXELS("CGGI Canvas", canvas->image);
649#if 0
650    PRINT_CGSTATES_INFO(NULL);
651#endif
652#endif
653
654    return info;
655}
656
657
658#pragma mark --- GlyphInfo Filling and Canvas Managment ---
659
660/*
661 * Sets all the per-run properties for the canvas, and then iterates through
662 * the character run, and creates images in the GlyphInfo structs.
663 *
664 * Not inlined because it would create two copies in the function below
665 */
666static void
667CGGI_FillImagesForGlyphsWithSizedCanvas(CGGI_GlyphCanvas *canvas,
668                                        const AWTStrike *strike,
669                                        const CGGI_RenderingMode *mode,
670                                        jlong glyphInfos[],
671                                        const UnicodeScalarValue uniChars[],
672                                        const CGGlyph glyphs[],
673                                        const CFIndex len)
674{
675    CGContextSetTextMatrix(canvas->context, strike->fAltTx);
676
677    CGContextSetFont(canvas->context, strike->fAWTFont->fNativeCGFont);
678    JRSFontSetRenderingStyleOnContext(canvas->context, strike->fStyle);
679
680    CFIndex i;
681    for (i = 0; i < len; i++) {
682        GlyphInfo *info = (GlyphInfo *)jlong_to_ptr(glyphInfos[i]);
683        if (info != NULL) {
684            CGGI_CreateImageForGlyph(canvas, glyphs[i], info, mode);
685        } else {
686            info = CGGI_CreateImageForUnicode(canvas, strike, mode, uniChars[i]);
687            glyphInfos[i] = ptr_to_jlong(info);
688        }
689#ifdef CGGI_DEBUG
690        DUMP_GLYPHINFO(info);
691#endif
692
693#ifdef CGGI_DEBUG_DUMP
694        DUMP_IMG_PIXELS("CGGI Canvas", canvas->image);
695#endif
696    }
697#ifdef CGGI_DEBUG_DUMP
698    DUMP_IMG_PIXELS("CGGI Canvas", canvas->image);
699    PRINT_CGSTATES_INFO(canvas->context);
700#endif
701}
702
703static NSString *threadLocalAACanvasKey =
704    @"Java CoreGraphics Text Renderer Cached Canvas for AA";
705
706static NSString *threadLocalLCDCanvasKey =
707    @"Java CoreGraphics Text Renderer Cached Canvas for LCD";
708
709/*
710 * This is the maximum length and height times the above slack squared
711 * to determine if we go with the global canvas, or malloc one on the spot.
712 */
713#define CGGI_GLYPH_CANVAS_MAX 100
714
715/*
716 * Based on the space needed to strike the largest character in the run,
717 * either use the global shared canvas, or make one up on the spot, strike
718 * the glyphs, and destroy it.
719 */
720static inline void
721CGGI_FillImagesForGlyphs(jlong *glyphInfos, const AWTStrike *strike,
722                         const CGGI_RenderingMode *mode,
723                         const UnicodeScalarValue uniChars[], const CGGlyph glyphs[],
724                         const size_t maxWidth, const size_t maxHeight,
725                         const CFIndex len)
726{
727    if (maxWidth*maxHeight*CGGI_GLYPH_CANVAS_SLACK*CGGI_GLYPH_CANVAS_SLACK >
728        CGGI_GLYPH_CANVAS_MAX*CGGI_GLYPH_CANVAS_MAX*CGGI_GLYPH_CANVAS_SLACK*CGGI_GLYPH_CANVAS_SLACK)
729    {
730        CGGI_GlyphCanvas *tmpCanvas = [[CGGI_GlyphCanvas alloc] init];
731        CGGI_InitCanvas(tmpCanvas, maxWidth, maxHeight, mode);
732        CGGI_FillImagesForGlyphsWithSizedCanvas(tmpCanvas, strike,
733                mode, glyphInfos, uniChars,
734                glyphs, len);
735        CGGI_FreeCanvas(tmpCanvas);
736
737        [tmpCanvas release];
738        return;
739    }
740    NSMutableDictionary *threadDict =
741        [[NSThread currentThread] threadDictionary];
742
743    NSString* theKey = (mode->glyphDescriptor == &rgb) ?
744        threadLocalLCDCanvasKey : threadLocalAACanvasKey;
745
746    CGGI_GlyphCanvas *canvas = [threadDict objectForKey:theKey];
747    if (canvas == nil) {
748        canvas = [[CGGI_GlyphCanvas alloc] init];
749        [threadDict setObject:canvas forKey:theKey];
750    }
751
752    CGGI_SizeCanvas(canvas, maxWidth, maxHeight, mode);
753    CGGI_FillImagesForGlyphsWithSizedCanvas(canvas, strike, mode,
754                                            glyphInfos, uniChars, glyphs, len);
755}
756
757/*
758 * Finds the advances and bounding boxes of the characters in the run,
759 * cycles through all the bounds and calculates the maximum canvas space
760 * required by the largest glyph.
761 *
762 * Creates a GlyphInfo struct with a malloc that also encapsulates the
763 * image the struct points to.  This is done to meet memory layout
764 * expectations in the Sun text rasterizer memory managment code.
765 * The image immediately follows the struct physically in memory.
766 */
767static inline void
768CGGI_CreateGlyphInfos(jlong *glyphInfos, const AWTStrike *strike,
769                      const CGGI_RenderingMode *mode,
770                      const UnicodeScalarValue uniChars[], const CGGlyph glyphs[],
771                      CGSize advances[], CGRect bboxes[], const CFIndex len)
772{
773    AWTFont *font = strike->fAWTFont;
774    CGAffineTransform tx = strike->fTx;
775    JRSFontRenderingStyle bboxCGMode = JRSFontAlignStyleForFractionalMeasurement(strike->fStyle);
776
777    JRSFontGetBoundingBoxesForGlyphsAndStyle((CTFontRef)font->fFont, &tx, bboxCGMode, glyphs, len, bboxes);
778    CTFontGetAdvancesForGlyphs((CTFontRef)font->fFont, kCTFontDefaultOrientation, glyphs, advances, len);
779
780    size_t maxWidth = 1;
781    size_t maxHeight = 1;
782
783    CFIndex i;
784    for (i = 0; i < len; i++)
785    {
786        if (uniChars[i] != 0)
787        {
788            glyphInfos[i] = 0L;
789            continue; // will be handled later
790        }
791
792        CGSize advance = advances[i];
793        CGRect bbox = bboxes[i];
794
795        GlyphInfo *glyphInfo = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, mode);
796
797        if (maxWidth < glyphInfo->width)   maxWidth = glyphInfo->width;
798        if (maxHeight < glyphInfo->height) maxHeight = glyphInfo->height;
799
800        glyphInfos[i] = ptr_to_jlong(glyphInfo);
801    }
802
803    CGGI_FillImagesForGlyphs(glyphInfos, strike, mode, uniChars,
804                             glyphs, maxWidth, maxHeight, len);
805}
806
807
808#pragma mark --- Temporary Buffer Allocations and Initialization ---
809
810/*
811 * This stage separates the already valid glyph codes from the unicode values
812 * that need special handling - the rawGlyphCodes array is no longer used
813 * after this stage.
814 */
815static void
816CGGI_CreateGlyphsAndScanForComplexities(jlong *glyphInfos,
817                                        const AWTStrike *strike,
818                                        const CGGI_RenderingMode *mode,
819                                        jint rawGlyphCodes[],
820                                        UnicodeScalarValue uniChars[], CGGlyph glyphs[],
821                                        CGSize advances[], CGRect bboxes[],
822                                        const CFIndex len)
823{
824    CFIndex i;
825    for (i = 0; i < len; i++) {
826        jint code = rawGlyphCodes[i];
827        if (code < 0) {
828            glyphs[i] = 0;
829            uniChars[i] = -code;
830        } else {
831            glyphs[i] = code;
832            uniChars[i] = 0;
833        }
834    }
835
836    CGGI_CreateGlyphInfos(glyphInfos, strike, mode,
837                          uniChars, glyphs, advances, bboxes, len);
838
839#ifdef CGGI_DEBUG_HIT_COUNT
840    static size_t hitCount = 0;
841    hitCount++;
842    printf("%d\n", (int)hitCount);
843#endif
844}
845
846/*
847 * Conditionally stack allocates buffers for glyphs, bounding boxes,
848 * and advances.  Unfortunately to use CG or CT in bulk runs (which is
849 * faster than calling them per character), we have to copy into and out
850 * of these buffers. Still a net win though.
851 */
852void
853CGGlyphImages_GetGlyphImagePtrs(jlong glyphInfos[],
854                                const AWTStrike *strike,
855                                jint rawGlyphCodes[], const CFIndex len)
856{
857    const CGGI_RenderingMode mode = CGGI_GetRenderingMode(strike);
858
859    if (len < MAX_STACK_ALLOC_GLYPH_BUFFER_SIZE) {
860        CGRect bboxes[len];
861        CGSize advances[len];
862        CGGlyph glyphs[len];
863        UnicodeScalarValue uniChars[len];
864
865        CGGI_CreateGlyphsAndScanForComplexities(glyphInfos, strike, &mode,
866                                                rawGlyphCodes, uniChars, glyphs,
867                                                advances, bboxes, len);
868
869        return;
870    }
871
872    // just do one malloc, and carve it up for all the buffers
873    void *buffer = malloc(sizeof(CGRect) * sizeof(CGSize) *
874                          sizeof(CGGlyph) * sizeof(UnicodeScalarValue) * len);
875    if (buffer == NULL) {
876        [[NSException exceptionWithName:NSMallocException
877            reason:@"Failed to allocate memory for the temporary glyph strike and measurement buffers." userInfo:nil] raise];
878    }
879
880    CGRect *bboxes = (CGRect *)(buffer);
881    CGSize *advances = (CGSize *)(bboxes + sizeof(CGRect) * len);
882    CGGlyph *glyphs = (CGGlyph *)(advances + sizeof(CGGlyph) * len);
883    UnicodeScalarValue *uniChars = (UnicodeScalarValue *)(glyphs + sizeof(UnicodeScalarValue) * len);
884
885    CGGI_CreateGlyphsAndScanForComplexities(glyphInfos, strike, &mode,
886                                            rawGlyphCodes, uniChars, glyphs,
887                                            advances, bboxes, len);
888
889    free(buffer);
890}
891