Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/macosx/native_NOTIOS/sun/font/CGGlyphImages.m
38829 views
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
#import <JavaNativeFoundation/JavaNativeFoundation.h>
28
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
75
static void
76
DUMP_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
123
static void
124
DUMP_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
134
static void
135
PRINT_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
179
static void
180
DUMP_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 ---
198
static Int32 reverseGamma = 0;
199
200
static UInt8 reverseGammaLut[256] = { 0 };
201
202
static 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
225
static inline void
226
CGGI_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
235
static void
236
CGGI_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
281
static inline UInt8
282
CGGI_ConvertBWPixelToByteGray(UInt32 p)
283
{
284
return 0xFF - (((p >> 24 & 0xFF) + (p >> 16 & 0xFF) + (p >> 8 & 0xFF)) / 3);
285
}
286
287
static void
288
CGGI_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
315
typedef struct CGGI_GlyphInfoDescriptor {
316
size_t pixelSize;
317
void (*copyFxnPtr)(CGGI_GlyphCanvas *canvas, GlyphInfo *info);
318
} CGGI_GlyphInfoDescriptor;
319
320
typedef struct CGGI_RenderingMode {
321
CGGI_GlyphInfoDescriptor *glyphDescriptor;
322
JRSFontRenderingStyle cgFontMode;
323
} CGGI_RenderingMode;
324
325
static CGGI_GlyphInfoDescriptor grey =
326
{ 1, &CGGI_CopyImageFromCanvasToAlphaInfo };
327
static CGGI_GlyphInfoDescriptor rgb =
328
{ 3, &CGGI_CopyImageFromCanvasToRGBInfo };
329
330
static inline CGGI_RenderingMode
331
CGGI_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
*/
371
static inline void
372
CGGI_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
*/
414
static inline void
415
CGGI_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
*/
439
static inline void
440
CGGI_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
*/
465
static inline void
466
CGGI_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
493
static inline GlyphInfo *
494
CGGI_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
*/
560
static inline void
561
CGGI_CreateImageForGlyph
562
(CGGI_GlyphCanvas *canvas, const CGGlyph glyph,
563
GlyphInfo *info, const CGGI_RenderingMode *mode)
564
{
565
// clean the canvas
566
CGGI_ClearCanvas(canvas, info);
567
568
// strike the glyph in the upper right corner
569
CGContextShowGlyphsAtPoint(canvas->context,
570
-info->topLeftX,
571
canvas->image->height + info->topLeftY,
572
&glyph, 1);
573
574
// copy the glyph from the canvas into the info
575
(*mode->glyphDescriptor->copyFxnPtr)(canvas, info);
576
}
577
578
/*
579
* CoreText path...
580
*/
581
static inline GlyphInfo *
582
CGGI_CreateImageForUnicode
583
(CGGI_GlyphCanvas *canvas, const AWTStrike *strike,
584
const CGGI_RenderingMode *mode, const UniChar uniChar)
585
{
586
// save the state of the world
587
CGContextSaveGState(canvas->context);
588
589
// get the glyph, measure it using CG
590
CGGlyph glyph;
591
CTFontRef fallback;
592
if (uniChar > 0xFFFF) {
593
UTF16Char charRef[2];
594
CTS_BreakupUnicodeIntoSurrogatePairs(uniChar, charRef);
595
CGGlyph glyphTmp[2];
596
fallback = CTS_CopyCTFallbackFontAndGlyphForUnicode(strike->fAWTFont, (const UTF16Char *)&charRef, (CGGlyph *)&glyphTmp, 2);
597
glyph = glyphTmp[0];
598
} else {
599
UTF16Char charRef;
600
charRef = (UTF16Char) uniChar; // truncate.
601
fallback = CTS_CopyCTFallbackFontAndGlyphForUnicode(strike->fAWTFont, (const UTF16Char *)&charRef, &glyph, 1);
602
}
603
604
CGAffineTransform tx = strike->fTx;
605
JRSFontRenderingStyle style = JRSFontAlignStyleForFractionalMeasurement(strike->fStyle);
606
607
CGRect bbox;
608
JRSFontGetBoundingBoxesForGlyphsAndStyle(fallback, &tx, style, &glyph, 1, &bbox);
609
610
CGSize advance;
611
CTFontGetAdvancesForGlyphs(fallback, kCTFontDefaultOrientation, &glyph, &advance, 1);
612
613
// create the Sun2D GlyphInfo we are going to strike into
614
GlyphInfo *info = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, mode);
615
616
// fix the context size, just in case the substituted character is unexpectedly large
617
CGGI_SizeCanvas(canvas, info->width, info->height, mode);
618
619
// align the transform for the real CoreText strike
620
CGContextSetTextMatrix(canvas->context, strike->fAltTx);
621
622
const CGFontRef cgFallback = CTFontCopyGraphicsFont(fallback, NULL);
623
CGContextSetFont(canvas->context, cgFallback);
624
CFRelease(cgFallback);
625
626
// clean the canvas - align, strike, and copy the glyph from the canvas into the info
627
CGGI_CreateImageForGlyph(canvas, glyph, info, mode);
628
629
// restore the state of the world
630
CGContextRestoreGState(canvas->context);
631
632
CFRelease(fallback);
633
#ifdef CGGI_DEBUG
634
DUMP_GLYPHINFO(info);
635
#endif
636
637
#ifdef CGGI_DEBUG_DUMP
638
DUMP_IMG_PIXELS("CGGI Canvas", canvas->image);
639
#if 0
640
PRINT_CGSTATES_INFO(NULL);
641
#endif
642
#endif
643
644
return info;
645
}
646
647
648
#pragma mark --- GlyphInfo Filling and Canvas Managment ---
649
650
/*
651
* Sets all the per-run properties for the canvas, and then iterates through
652
* the character run, and creates images in the GlyphInfo structs.
653
*
654
* Not inlined because it would create two copies in the function below
655
*/
656
static void
657
CGGI_FillImagesForGlyphsWithSizedCanvas(CGGI_GlyphCanvas *canvas,
658
const AWTStrike *strike,
659
const CGGI_RenderingMode *mode,
660
jlong glyphInfos[],
661
const UniChar uniChars[],
662
const CGGlyph glyphs[],
663
const CFIndex len)
664
{
665
CGContextSetTextMatrix(canvas->context, strike->fAltTx);
666
667
CGContextSetFont(canvas->context, strike->fAWTFont->fNativeCGFont);
668
JRSFontSetRenderingStyleOnContext(canvas->context, strike->fStyle);
669
670
CFIndex i;
671
for (i = 0; i < len; i++) {
672
GlyphInfo *info = (GlyphInfo *)jlong_to_ptr(glyphInfos[i]);
673
if (info != NULL) {
674
CGGI_CreateImageForGlyph(canvas, glyphs[i], info, mode);
675
} else {
676
info = CGGI_CreateImageForUnicode(canvas, strike, mode, uniChars[i]);
677
glyphInfos[i] = ptr_to_jlong(info);
678
}
679
#ifdef CGGI_DEBUG
680
DUMP_GLYPHINFO(info);
681
#endif
682
683
#ifdef CGGI_DEBUG_DUMP
684
DUMP_IMG_PIXELS("CGGI Canvas", canvas->image);
685
#endif
686
}
687
#ifdef CGGI_DEBUG_DUMP
688
DUMP_IMG_PIXELS("CGGI Canvas", canvas->image);
689
PRINT_CGSTATES_INFO(canvas->context);
690
#endif
691
}
692
693
static NSString *threadLocalAACanvasKey =
694
@"Java CoreGraphics Text Renderer Cached Canvas for AA";
695
696
static NSString *threadLocalLCDCanvasKey =
697
@"Java CoreGraphics Text Renderer Cached Canvas for LCD";
698
699
/*
700
* This is the maximum length and height times the above slack squared
701
* to determine if we go with the global canvas, or malloc one on the spot.
702
*/
703
#define CGGI_GLYPH_CANVAS_MAX 100
704
705
/*
706
* Based on the space needed to strike the largest character in the run,
707
* either use the global shared canvas, or make one up on the spot, strike
708
* the glyphs, and destroy it.
709
*/
710
static inline void
711
CGGI_FillImagesForGlyphs(jlong *glyphInfos, const AWTStrike *strike,
712
const CGGI_RenderingMode *mode,
713
const UniChar uniChars[], const CGGlyph glyphs[],
714
const size_t maxWidth, const size_t maxHeight,
715
const CFIndex len)
716
{
717
if (maxWidth*maxHeight*CGGI_GLYPH_CANVAS_SLACK*CGGI_GLYPH_CANVAS_SLACK >
718
CGGI_GLYPH_CANVAS_MAX*CGGI_GLYPH_CANVAS_MAX*CGGI_GLYPH_CANVAS_SLACK*CGGI_GLYPH_CANVAS_SLACK)
719
{
720
CGGI_GlyphCanvas *tmpCanvas = [[CGGI_GlyphCanvas alloc] init];
721
CGGI_InitCanvas(tmpCanvas, maxWidth, maxHeight, mode);
722
CGGI_FillImagesForGlyphsWithSizedCanvas(tmpCanvas, strike,
723
mode, glyphInfos, uniChars,
724
glyphs, len);
725
CGGI_FreeCanvas(tmpCanvas);
726
727
[tmpCanvas release];
728
return;
729
}
730
NSMutableDictionary *threadDict =
731
[[NSThread currentThread] threadDictionary];
732
733
NSString* theKey = (mode->glyphDescriptor == &rgb) ?
734
threadLocalLCDCanvasKey : threadLocalAACanvasKey;
735
736
CGGI_GlyphCanvas *canvas = [threadDict objectForKey:theKey];
737
if (canvas == nil) {
738
canvas = [[CGGI_GlyphCanvas alloc] init];
739
[threadDict setObject:canvas forKey:theKey];
740
}
741
742
CGGI_SizeCanvas(canvas, maxWidth, maxHeight, mode);
743
CGGI_FillImagesForGlyphsWithSizedCanvas(canvas, strike, mode,
744
glyphInfos, uniChars, glyphs, len);
745
}
746
747
/*
748
* Finds the advances and bounding boxes of the characters in the run,
749
* cycles through all the bounds and calculates the maximum canvas space
750
* required by the largest glyph.
751
*
752
* Creates a GlyphInfo struct with a malloc that also encapsulates the
753
* image the struct points to. This is done to meet memory layout
754
* expectations in the Sun text rasterizer memory managment code.
755
* The image immediately follows the struct physically in memory.
756
*/
757
static inline void
758
CGGI_CreateGlyphInfos(jlong *glyphInfos, const AWTStrike *strike,
759
const CGGI_RenderingMode *mode,
760
const UniChar uniChars[], const CGGlyph glyphs[],
761
CGSize advances[], CGRect bboxes[], const CFIndex len)
762
{
763
AWTFont *font = strike->fAWTFont;
764
CGAffineTransform tx = strike->fTx;
765
JRSFontRenderingStyle bboxCGMode = JRSFontAlignStyleForFractionalMeasurement(strike->fStyle);
766
767
JRSFontGetBoundingBoxesForGlyphsAndStyle((CTFontRef)font->fFont, &tx, bboxCGMode, glyphs, len, bboxes);
768
CTFontGetAdvancesForGlyphs((CTFontRef)font->fFont, kCTFontDefaultOrientation, glyphs, advances, len);
769
770
size_t maxWidth = 1;
771
size_t maxHeight = 1;
772
773
CFIndex i;
774
for (i = 0; i < len; i++)
775
{
776
if (uniChars[i] != 0)
777
{
778
glyphInfos[i] = 0L;
779
continue; // will be handled later
780
}
781
782
CGSize advance = advances[i];
783
CGRect bbox = bboxes[i];
784
785
GlyphInfo *glyphInfo = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, mode);
786
787
if (maxWidth < glyphInfo->width) maxWidth = glyphInfo->width;
788
if (maxHeight < glyphInfo->height) maxHeight = glyphInfo->height;
789
790
glyphInfos[i] = ptr_to_jlong(glyphInfo);
791
}
792
793
CGGI_FillImagesForGlyphs(glyphInfos, strike, mode, uniChars,
794
glyphs, maxWidth, maxHeight, len);
795
}
796
797
798
#pragma mark --- Temporary Buffer Allocations and Initialization ---
799
800
/*
801
* This stage separates the already valid glyph codes from the unicode values
802
* that need special handling - the rawGlyphCodes array is no longer used
803
* after this stage.
804
*/
805
static void
806
CGGI_CreateGlyphsAndScanForComplexities(jlong *glyphInfos,
807
const AWTStrike *strike,
808
const CGGI_RenderingMode *mode,
809
jint rawGlyphCodes[],
810
UniChar uniChars[], CGGlyph glyphs[],
811
CGSize advances[], CGRect bboxes[],
812
const CFIndex len)
813
{
814
CFIndex i;
815
for (i = 0; i < len; i++) {
816
jint code = rawGlyphCodes[i];
817
if (code < 0) {
818
glyphs[i] = 0;
819
uniChars[i] = -code;
820
} else {
821
glyphs[i] = code;
822
uniChars[i] = 0;
823
}
824
}
825
826
CGGI_CreateGlyphInfos(glyphInfos, strike, mode,
827
uniChars, glyphs, advances, bboxes, len);
828
829
#ifdef CGGI_DEBUG_HIT_COUNT
830
static size_t hitCount = 0;
831
hitCount++;
832
printf("%d\n", (int)hitCount);
833
#endif
834
}
835
836
/*
837
* Conditionally stack allocates buffers for glyphs, bounding boxes,
838
* and advances. Unfortunately to use CG or CT in bulk runs (which is
839
* faster than calling them per character), we have to copy into and out
840
* of these buffers. Still a net win though.
841
*/
842
void
843
CGGlyphImages_GetGlyphImagePtrs(jlong glyphInfos[],
844
const AWTStrike *strike,
845
jint rawGlyphCodes[], const CFIndex len)
846
{
847
const CGGI_RenderingMode mode = CGGI_GetRenderingMode(strike);
848
849
if (len < MAX_STACK_ALLOC_GLYPH_BUFFER_SIZE) {
850
CGRect bboxes[len];
851
CGSize advances[len];
852
CGGlyph glyphs[len];
853
UniChar uniChars[len];
854
855
CGGI_CreateGlyphsAndScanForComplexities(glyphInfos, strike, &mode,
856
rawGlyphCodes, uniChars, glyphs,
857
advances, bboxes, len);
858
859
return;
860
}
861
862
// just do one malloc, and carve it up for all the buffers
863
void *buffer = malloc(sizeof(CGRect) * sizeof(CGSize) *
864
sizeof(CGGlyph) * sizeof(UniChar) * len);
865
if (buffer == NULL) {
866
[[NSException exceptionWithName:NSMallocException
867
reason:@"Failed to allocate memory for the temporary glyph strike and measurement buffers." userInfo:nil] raise];
868
}
869
870
CGRect *bboxes = (CGRect *)(buffer);
871
CGSize *advances = (CGSize *)(bboxes + sizeof(CGRect) * len);
872
CGGlyph *glyphs = (CGGlyph *)(advances + sizeof(CGGlyph) * len);
873
UniChar *uniChars = (UniChar *)(glyphs + sizeof(UniChar) * len);
874
875
CGGI_CreateGlyphsAndScanForComplexities(glyphInfos, strike, &mode,
876
rawGlyphCodes, uniChars, glyphs,
877
advances, bboxes, len);
878
879
free(buffer);
880
}
881
882