Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.desktop/macosx/native/libawt_lwawt/font/CGGlyphImages.m
41152 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
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
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
static void
313
CGGI_CopyImageFromCanvasToARGBInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
314
{
315
CGBitmapInfo bitmapInfo = CGBitmapContextGetBitmapInfo(canvas->context);
316
bool littleEndian = (bitmapInfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Little;
317
318
UInt32 *src = (UInt32 *)canvas->image->data;
319
size_t srcRowWidth = canvas->image->width;
320
321
UInt8 *dest = (UInt8 *)info->image;
322
size_t destRowWidth = info->width;
323
324
size_t height = info->height;
325
326
size_t y;
327
328
for (y = 0; y < height; y++) {
329
size_t srcRow = y * srcRowWidth;
330
if (littleEndian) {
331
UInt16 destRowBytes = info->rowBytes;
332
memcpy(dest, src + srcRow, destRowBytes);
333
dest += destRowBytes;
334
} else {
335
size_t x;
336
for (x = 0; x < destRowWidth; x++) {
337
UInt32 p = src[srcRow + x];
338
*dest++ = (p >> 24 & 0xFF); // blue (alpha-premultiplied)
339
*dest++ = (p >> 16 & 0xFF); // green (alpha-premultiplied)
340
*dest++ = (p >> 8 & 0xFF); // red (alpha-premultiplied)
341
*dest++ = (p & 0xFF); // alpha
342
}
343
}
344
}
345
}
346
347
#pragma mark --- Pixel Size, Modes, and Canvas Shaping Helper Functions ---
348
349
typedef struct CGGI_GlyphInfoDescriptor {
350
size_t pixelSize;
351
void (*copyFxnPtr)(CGGI_GlyphCanvas *canvas, GlyphInfo *info);
352
} CGGI_GlyphInfoDescriptor;
353
354
typedef struct CGGI_RenderingMode {
355
CGGI_GlyphInfoDescriptor *glyphDescriptor;
356
JRSFontRenderingStyle cgFontMode;
357
} CGGI_RenderingMode;
358
359
static CGGI_GlyphInfoDescriptor grey =
360
{ 1, &CGGI_CopyImageFromCanvasToAlphaInfo };
361
static CGGI_GlyphInfoDescriptor rgb =
362
{ 3, &CGGI_CopyImageFromCanvasToRGBInfo };
363
static CGGI_GlyphInfoDescriptor argb =
364
{ 4, &CGGI_CopyImageFromCanvasToARGBInfo };
365
366
static inline CGGI_GlyphInfoDescriptor*
367
CGGI_GetGlyphInfoDescriptor(const CGGI_RenderingMode *mode, CTFontRef font)
368
{
369
return IsEmojiFont(font) ? &argb : mode->glyphDescriptor;
370
}
371
372
static inline CGGI_RenderingMode
373
CGGI_GetRenderingMode(const AWTStrike *strike)
374
{
375
CGGI_RenderingMode mode;
376
mode.cgFontMode = strike->fStyle;
377
NSException *e = nil;
378
379
switch (strike->fAAStyle) {
380
case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_OFF:
381
case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_ON:
382
mode.glyphDescriptor = &grey;
383
break;
384
case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HRGB:
385
case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HBGR:
386
case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_VRGB:
387
case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_VBGR:
388
mode.glyphDescriptor = &rgb;
389
break;
390
case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_GASP:
391
case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_DEFAULT:
392
default:
393
/* we expect that text antialiasing hint has been already
394
* evaluated. Report an error if we get 'unevaluated' hint here.
395
*/
396
e = [NSException
397
exceptionWithName:@"IllegalArgumentException"
398
reason:@"Invalid hint value"
399
userInfo:nil];
400
@throw e;
401
}
402
403
return mode;
404
}
405
406
407
#pragma mark --- Canvas Managment ---
408
409
/*
410
* Creates a new canvas of a fixed size, and initializes the CGContext as
411
* an 32-bit ARGB BitmapContext with some generic RGB color space.
412
*/
413
static inline void
414
CGGI_InitCanvas(CGGI_GlyphCanvas *canvas,
415
const vImagePixelCount width, const vImagePixelCount height,
416
const CGGI_RenderingMode* mode)
417
{
418
// our canvas is *always* 4-byte ARGB
419
size_t bytesPerRow = width * sizeof(UInt32);
420
size_t byteCount = bytesPerRow * height;
421
422
canvas->image = malloc(sizeof(vImage_Buffer));
423
canvas->image->width = width;
424
canvas->image->height = height;
425
canvas->image->rowBytes = bytesPerRow;
426
427
canvas->image->data = (void *)calloc(byteCount, sizeof(UInt8));
428
if (canvas->image->data == NULL) {
429
[[NSException exceptionWithName:NSMallocException
430
reason:@"Failed to allocate memory for the buffer which backs the CGContext for glyph strikes." userInfo:nil] raise];
431
}
432
433
uint32_t bmpInfo = kCGImageAlphaPremultipliedFirst;
434
if (mode->glyphDescriptor == &rgb) {
435
bmpInfo |= kCGBitmapByteOrder32Host;
436
}
437
438
CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
439
canvas->context = CGBitmapContextCreate(canvas->image->data,
440
width, height, 8, bytesPerRow,
441
colorSpace,
442
bmpInfo);
443
444
// set foreground color
445
CGContextSetRGBFillColor(canvas->context, 0.0f, 0.0f, 0.0f, 1.0f);
446
447
CGContextSetFontSize(canvas->context, 1);
448
CGContextSaveGState(canvas->context);
449
450
CGColorSpaceRelease(colorSpace);
451
}
452
453
/*
454
* Releases the BitmapContext and the associated memory backing it.
455
*/
456
static inline void
457
CGGI_FreeCanvas(CGGI_GlyphCanvas *canvas)
458
{
459
if (canvas->context != NULL) {
460
CGContextRelease(canvas->context);
461
}
462
463
if (canvas->image != NULL) {
464
if (canvas->image->data != NULL) {
465
free(canvas->image->data);
466
}
467
free(canvas->image);
468
}
469
}
470
471
/*
472
* This is the slack space that is preallocated for the global GlyphCanvas
473
* when it needs to be expanded. It has been set somewhat liberally to
474
* avoid re-upsizing frequently.
475
*/
476
#define CGGI_GLYPH_CANVAS_SLACK 2.5
477
478
/*
479
* Quick and easy inline to check if this canvas is big enough.
480
*/
481
static inline void
482
CGGI_SizeCanvas(CGGI_GlyphCanvas *canvas, const vImagePixelCount width,
483
const vImagePixelCount height,
484
const CGGI_RenderingMode* mode)
485
{
486
if (canvas->image != NULL &&
487
width < canvas->image->width &&
488
height < canvas->image->height)
489
{
490
return;
491
}
492
493
// if we don't have enough space to strike the largest glyph in the
494
// run, resize the canvas
495
CGGI_FreeCanvas(canvas);
496
CGGI_InitCanvas(canvas,
497
width * CGGI_GLYPH_CANVAS_SLACK,
498
height * CGGI_GLYPH_CANVAS_SLACK,
499
mode);
500
JRSFontSetRenderingStyleOnContext(canvas->context, mode->cgFontMode);
501
}
502
503
/*
504
* Clear the canvas by blitting white (or transparent background for color glyphs) only into the region of interest
505
* (the rect which we will copy out of once the glyph is struck).
506
*/
507
static inline void
508
CGGI_ClearCanvas(CGGI_GlyphCanvas *canvas, GlyphInfo *info, bool transparent)
509
{
510
vImage_Buffer canvasRectToClear;
511
canvasRectToClear.data = canvas->image->data;
512
canvasRectToClear.height = info->height;
513
canvasRectToClear.width = info->width;
514
// use the row stride of the canvas, not the info
515
canvasRectToClear.rowBytes = canvas->image->rowBytes;
516
517
// clean the canvas
518
#ifdef CGGI_DEBUG
519
Pixel_8888 background = { 0xE0, 0xE0, 0xE0, 0xE0 };
520
#else
521
Pixel_8888 background = { transparent ? 0 : 0xFF,
522
transparent ? 0 : 0xFF,
523
transparent ? 0 : 0xFF,
524
transparent ? 0 : 0xFF };
525
#endif
526
527
// clear canvas background
528
vImageBufferFill_ARGB8888(&canvasRectToClear, background, kvImageNoFlags);
529
}
530
531
532
#pragma mark --- GlyphInfo Creation & Copy Functions ---
533
534
/*
535
* Creates a GlyphInfo with exactly the correct size image and measurements.
536
*/
537
#define CGGI_GLYPH_BBOX_PADDING 2.0f
538
static inline GlyphInfo *
539
CGGI_CreateNewGlyphInfoFrom(CGSize advance, CGRect bbox,
540
const AWTStrike *strike,
541
const CGGI_GlyphInfoDescriptor *glyphDescriptor)
542
{
543
size_t pixelSize = glyphDescriptor->pixelSize;
544
545
// adjust the bounding box to be 1px bigger on each side than what
546
// CGFont-whatever suggests - because it gives a bounding box that
547
// is too tight
548
bbox.size.width += CGGI_GLYPH_BBOX_PADDING * 2.0f;
549
bbox.size.height += CGGI_GLYPH_BBOX_PADDING * 2.0f;
550
bbox.origin.x -= CGGI_GLYPH_BBOX_PADDING;
551
bbox.origin.y -= CGGI_GLYPH_BBOX_PADDING;
552
553
vImagePixelCount width = ceilf(bbox.size.width);
554
vImagePixelCount height = ceilf(bbox.size.height);
555
556
// if the glyph is larger than 1MB, don't even try...
557
// the GlyphVector path should have taken over by now
558
// and zero pixels is ok
559
if (width * height > 1024 * 1024) {
560
width = 1;
561
height = 1;
562
}
563
advance = CGSizeApplyAffineTransform(advance, strike->fFontTx);
564
if (!JRSFontStyleUsesFractionalMetrics(strike->fStyle)) {
565
advance.width = round(advance.width);
566
advance.height = round(advance.height);
567
}
568
advance = CGSizeApplyAffineTransform(advance, strike->fDevTx);
569
570
#ifdef USE_IMAGE_ALIGNED_MEMORY
571
// create separate memory
572
GlyphInfo *glyphInfo = (GlyphInfo *)malloc(sizeof(GlyphInfo));
573
void *image = (void *)malloc(height * width * pixelSize);
574
#else
575
// create a GlyphInfo struct fused to the image it points to
576
GlyphInfo *glyphInfo = (GlyphInfo *)malloc(sizeof(GlyphInfo) +
577
height * width * pixelSize);
578
#endif
579
580
glyphInfo->advanceX = advance.width;
581
glyphInfo->advanceY = advance.height;
582
glyphInfo->topLeftX = round(bbox.origin.x);
583
glyphInfo->topLeftY = round(bbox.origin.y);
584
glyphInfo->width = width;
585
glyphInfo->height = height;
586
glyphInfo->rowBytes = width * pixelSize;
587
glyphInfo->cellInfo = NULL;
588
589
#ifdef USE_IMAGE_ALIGNED_MEMORY
590
glyphInfo->image = image;
591
#else
592
glyphInfo->image = ((void *)glyphInfo) + sizeof(GlyphInfo);
593
#endif
594
595
return glyphInfo;
596
}
597
598
599
#pragma mark --- Glyph Striking onto Canvas ---
600
601
/*
602
* Clears the canvas, strikes the glyph with CoreGraphics, and then
603
* copies the struck pixels into the GlyphInfo image.
604
*/
605
static inline void
606
CGGI_CreateImageForGlyph
607
(CGGI_GlyphCanvas *canvas, const CGGlyph glyph,
608
GlyphInfo *info, const CGGI_GlyphInfoDescriptor *glyphDescriptor, const AWTStrike *strike, CTFontRef font)
609
{
610
if (isnan(info->topLeftX) || isnan(info->topLeftY)) {
611
// Explicitly set glyphInfo width/height to be 0 to ensure
612
// zero length glyph image is copied into GlyphInfo from canvas
613
info->width = 0;
614
info->height = 0;
615
616
// copy the "empty" glyph from the canvas into the info
617
(*glyphDescriptor->copyFxnPtr)(canvas, info);
618
return;
619
}
620
621
// clean the canvas
622
CGGI_ClearCanvas(canvas, info, glyphDescriptor == &argb);
623
624
// strike the glyph in the upper right corner
625
CGFloat x = -info->topLeftX;
626
CGFloat y = canvas->image->height + info->topLeftY;
627
628
if (glyphDescriptor == &argb) {
629
// Emoji glyphs are not rendered by CGContextShowGlyphsAtPoint.
630
// Also, it's not possible to use transformation matrix to get the emoji glyph
631
// rendered for the desired font size - actual-size font object is needed.
632
// The logic here must match the logic in CGGlyphImages_GetGlyphMetrics,
633
// which calculates glyph metrics.
634
635
CGAffineTransform matrix = CGContextGetTextMatrix(canvas->context);
636
CGFloat fontSize = sqrt(fabs(matrix.a * matrix.d - matrix.b * matrix.c));
637
CTFontRef sizedFont = CTFontCreateCopyWithSymbolicTraits(font, fontSize, NULL, 0, 0);
638
639
CGFloat normFactor = 1.0 / fontSize;
640
CGAffineTransform normalizedMatrix = CGAffineTransformScale(matrix, normFactor, normFactor);
641
CGContextSetTextMatrix(canvas->context, normalizedMatrix);
642
643
CGPoint userPoint = CGPointMake(x, y);
644
CGAffineTransform normalizedMatrixInv = CGAffineTransformInvert(normalizedMatrix);
645
CGPoint textPoint = CGPointApplyAffineTransform(userPoint, normalizedMatrixInv);
646
647
CTFontDrawGlyphs(sizedFont, &glyph, &textPoint, 1, canvas->context);
648
649
CFRelease(sizedFont);
650
// restore context's original state
651
CGContextSetTextMatrix(canvas->context, matrix);
652
CGContextSetFontSize(canvas->context, 1); // CTFontDrawGlyphs tampers with it
653
} else {
654
CGContextShowGlyphsAtPoint(canvas->context, x, y, &glyph, 1);
655
}
656
657
// copy the glyph from the canvas into the info
658
(*glyphDescriptor->copyFxnPtr)(canvas, info);
659
}
660
661
/*
662
* CoreText path...
663
*/
664
static inline GlyphInfo *
665
CGGI_CreateImageForUnicode
666
(CGGI_GlyphCanvas *canvas, const AWTStrike *strike,
667
const CGGI_RenderingMode *mode, const UnicodeScalarValue uniChar)
668
{
669
// save the state of the world
670
CGContextSaveGState(canvas->context);
671
672
// get the glyph, measure it using CG
673
CGGlyph glyph;
674
CTFontRef fallback;
675
if (uniChar > 0xFFFF) {
676
UTF16Char charRef[2];
677
CTS_BreakupUnicodeIntoSurrogatePairs(uniChar, charRef);
678
CGGlyph glyphTmp[2];
679
fallback = CTS_CopyCTFallbackFontAndGlyphForUnicode(strike->fAWTFont, (const UTF16Char *)&charRef, (CGGlyph *)&glyphTmp, 2);
680
glyph = glyphTmp[0];
681
} else {
682
UTF16Char charRef;
683
charRef = (UTF16Char) uniChar; // truncate.
684
fallback = CTS_CopyCTFallbackFontAndGlyphForUnicode(strike->fAWTFont, (const UTF16Char *)&charRef, &glyph, 1);
685
}
686
687
CGAffineTransform tx = strike->fTx;
688
JRSFontRenderingStyle style = JRSFontAlignStyleForFractionalMeasurement(strike->fStyle);
689
690
CGRect bbox;
691
CGSize advance;
692
CGGlyphImages_GetGlyphMetrics(fallback, &tx, style, &glyph, 1, &bbox, &advance);
693
694
CGGI_GlyphInfoDescriptor *glyphDescriptor = CGGI_GetGlyphInfoDescriptor(mode, fallback);
695
696
// create the Sun2D GlyphInfo we are going to strike into
697
GlyphInfo *info = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, glyphDescriptor);
698
699
// fix the context size, just in case the substituted character is unexpectedly large
700
CGGI_SizeCanvas(canvas, info->width, info->height, mode);
701
702
// align the transform for the real CoreText strike
703
CGContextSetTextMatrix(canvas->context, strike->fAltTx);
704
705
const CGFontRef cgFallback = CTFontCopyGraphicsFont(fallback, NULL);
706
CGContextSetFont(canvas->context, cgFallback);
707
CFRelease(cgFallback);
708
709
// clean the canvas - align, strike, and copy the glyph from the canvas into the info
710
CGGI_CreateImageForGlyph(canvas, glyph, info, glyphDescriptor, strike, fallback);
711
712
// restore the state of the world
713
CGContextRestoreGState(canvas->context);
714
715
CFRelease(fallback);
716
#ifdef CGGI_DEBUG
717
DUMP_GLYPHINFO(info);
718
#endif
719
720
#ifdef CGGI_DEBUG_DUMP
721
DUMP_IMG_PIXELS("CGGI Canvas", canvas->image);
722
#if 0
723
PRINT_CGSTATES_INFO(NULL);
724
#endif
725
#endif
726
727
return info;
728
}
729
730
731
#pragma mark --- GlyphInfo Filling and Canvas Managment ---
732
733
/*
734
* Sets all the per-run properties for the canvas, and then iterates through
735
* the character run, and creates images in the GlyphInfo structs.
736
*
737
* Not inlined because it would create two copies in the function below
738
*/
739
static void
740
CGGI_FillImagesForGlyphsWithSizedCanvas(CGGI_GlyphCanvas *canvas,
741
const AWTStrike *strike,
742
const CGGI_RenderingMode *mode,
743
jlong glyphInfos[],
744
const UnicodeScalarValue uniChars[],
745
const CGGlyph glyphs[],
746
const CFIndex len)
747
{
748
CGContextSetTextMatrix(canvas->context, strike->fAltTx);
749
750
CGContextSetFont(canvas->context, strike->fAWTFont->fNativeCGFont);
751
JRSFontSetRenderingStyleOnContext(canvas->context, strike->fStyle);
752
753
CTFontRef mainFont = (CTFontRef)strike->fAWTFont->fFont;
754
CGGI_GlyphInfoDescriptor* mainFontDescriptor = CGGI_GetGlyphInfoDescriptor(mode, mainFont);
755
756
CFIndex i;
757
for (i = 0; i < len; i++) {
758
GlyphInfo *info = (GlyphInfo *)jlong_to_ptr(glyphInfos[i]);
759
if (info != NULL) {
760
CGGI_CreateImageForGlyph(canvas, glyphs[i], info, mainFontDescriptor, strike, mainFont);
761
} else {
762
info = CGGI_CreateImageForUnicode(canvas, strike, mode, uniChars[i]);
763
glyphInfos[i] = ptr_to_jlong(info);
764
}
765
#ifdef CGGI_DEBUG
766
DUMP_GLYPHINFO(info);
767
#endif
768
769
#ifdef CGGI_DEBUG_DUMP
770
DUMP_IMG_PIXELS("CGGI Canvas", canvas->image);
771
#endif
772
}
773
#ifdef CGGI_DEBUG_DUMP
774
DUMP_IMG_PIXELS("CGGI Canvas", canvas->image);
775
PRINT_CGSTATES_INFO(canvas->context);
776
#endif
777
}
778
779
static NSString *threadLocalAACanvasKey =
780
@"Java CoreGraphics Text Renderer Cached Canvas for AA";
781
782
static NSString *threadLocalLCDCanvasKey =
783
@"Java CoreGraphics Text Renderer Cached Canvas for LCD";
784
785
/*
786
* This is the maximum length and height times the above slack squared
787
* to determine if we go with the global canvas, or malloc one on the spot.
788
*/
789
#define CGGI_GLYPH_CANVAS_MAX 100
790
791
/*
792
* Based on the space needed to strike the largest character in the run,
793
* either use the global shared canvas, or make one up on the spot, strike
794
* the glyphs, and destroy it.
795
*/
796
static inline void
797
CGGI_FillImagesForGlyphs(jlong *glyphInfos, const AWTStrike *strike,
798
const CGGI_RenderingMode *mode,
799
const UnicodeScalarValue uniChars[], const CGGlyph glyphs[],
800
const size_t maxWidth, const size_t maxHeight,
801
const CFIndex len)
802
{
803
if (maxWidth*maxHeight*CGGI_GLYPH_CANVAS_SLACK*CGGI_GLYPH_CANVAS_SLACK >
804
CGGI_GLYPH_CANVAS_MAX*CGGI_GLYPH_CANVAS_MAX*CGGI_GLYPH_CANVAS_SLACK*CGGI_GLYPH_CANVAS_SLACK)
805
{
806
CGGI_GlyphCanvas *tmpCanvas = [[CGGI_GlyphCanvas alloc] init];
807
CGGI_InitCanvas(tmpCanvas, maxWidth, maxHeight, mode);
808
CGGI_FillImagesForGlyphsWithSizedCanvas(tmpCanvas, strike,
809
mode, glyphInfos, uniChars,
810
glyphs, len);
811
CGGI_FreeCanvas(tmpCanvas);
812
813
[tmpCanvas release];
814
return;
815
}
816
NSMutableDictionary *threadDict =
817
[[NSThread currentThread] threadDictionary];
818
819
NSString* theKey = (mode->glyphDescriptor == &rgb) ?
820
threadLocalLCDCanvasKey : threadLocalAACanvasKey;
821
822
CGGI_GlyphCanvas *canvas = [threadDict objectForKey:theKey];
823
if (canvas == nil) {
824
canvas = [[CGGI_GlyphCanvas alloc] init];
825
[threadDict setObject:canvas forKey:theKey];
826
}
827
828
CGGI_SizeCanvas(canvas, maxWidth, maxHeight, mode);
829
CGGI_FillImagesForGlyphsWithSizedCanvas(canvas, strike, mode,
830
glyphInfos, uniChars, glyphs, len);
831
}
832
833
/*
834
* Finds the advances and bounding boxes of the characters in the run,
835
* cycles through all the bounds and calculates the maximum canvas space
836
* required by the largest glyph.
837
*
838
* Creates a GlyphInfo struct with a malloc that also encapsulates the
839
* image the struct points to. This is done to meet memory layout
840
* expectations in the Sun text rasterizer memory managment code.
841
* The image immediately follows the struct physically in memory.
842
*/
843
static inline void
844
CGGI_CreateGlyphInfos(jlong *glyphInfos, const AWTStrike *strike,
845
const CGGI_RenderingMode *mode,
846
const UnicodeScalarValue uniChars[], const CGGlyph glyphs[],
847
CGSize advances[], CGRect bboxes[], const CFIndex len)
848
{
849
AWTFont *font = strike->fAWTFont;
850
CGAffineTransform tx = strike->fTx;
851
JRSFontRenderingStyle bboxCGMode = JRSFontAlignStyleForFractionalMeasurement(strike->fStyle);
852
853
CTFontRef fontRef = (CTFontRef)font->fFont;
854
CGGlyphImages_GetGlyphMetrics(fontRef, &tx, bboxCGMode, glyphs, len, bboxes, advances);
855
CGGI_GlyphInfoDescriptor* mainFontDescriptor = CGGI_GetGlyphInfoDescriptor(mode, fontRef);
856
857
size_t maxWidth = 1;
858
size_t maxHeight = 1;
859
860
CFIndex i;
861
for (i = 0; i < len; i++)
862
{
863
if (uniChars[i] != 0)
864
{
865
glyphInfos[i] = 0L;
866
continue; // will be handled later
867
}
868
869
CGSize advance = advances[i];
870
CGRect bbox = bboxes[i];
871
872
GlyphInfo *glyphInfo = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, mainFontDescriptor);
873
874
if (maxWidth < glyphInfo->width) maxWidth = glyphInfo->width;
875
if (maxHeight < glyphInfo->height) maxHeight = glyphInfo->height;
876
877
glyphInfos[i] = ptr_to_jlong(glyphInfo);
878
}
879
880
CGGI_FillImagesForGlyphs(glyphInfos, strike, mode, uniChars,
881
glyphs, maxWidth, maxHeight, len);
882
}
883
884
885
#pragma mark --- Temporary Buffer Allocations and Initialization ---
886
887
/*
888
* This stage separates the already valid glyph codes from the unicode values
889
* that need special handling - the rawGlyphCodes array is no longer used
890
* after this stage.
891
*/
892
static void
893
CGGI_CreateGlyphsAndScanForComplexities(jlong *glyphInfos,
894
const AWTStrike *strike,
895
const CGGI_RenderingMode *mode,
896
jint rawGlyphCodes[],
897
UnicodeScalarValue uniChars[], CGGlyph glyphs[],
898
CGSize advances[], CGRect bboxes[],
899
const CFIndex len)
900
{
901
CFIndex i;
902
for (i = 0; i < len; i++) {
903
jint code = rawGlyphCodes[i];
904
if (code < 0) {
905
glyphs[i] = 0;
906
uniChars[i] = -code;
907
} else {
908
glyphs[i] = code;
909
uniChars[i] = 0;
910
}
911
}
912
913
CGGI_CreateGlyphInfos(glyphInfos, strike, mode,
914
uniChars, glyphs, advances, bboxes, len);
915
916
#ifdef CGGI_DEBUG_HIT_COUNT
917
static size_t hitCount = 0;
918
hitCount++;
919
printf("%d\n", (int)hitCount);
920
#endif
921
}
922
923
/*
924
* Conditionally stack allocates buffers for glyphs, bounding boxes,
925
* and advances. Unfortunately to use CG or CT in bulk runs (which is
926
* faster than calling them per character), we have to copy into and out
927
* of these buffers. Still a net win though.
928
*/
929
void
930
CGGlyphImages_GetGlyphImagePtrs(jlong glyphInfos[],
931
const AWTStrike *strike,
932
jint rawGlyphCodes[], const CFIndex len)
933
{
934
const CGGI_RenderingMode mode = CGGI_GetRenderingMode(strike);
935
936
if (len < MAX_STACK_ALLOC_GLYPH_BUFFER_SIZE) {
937
CGRect bboxes[len];
938
CGSize advances[len];
939
CGGlyph glyphs[len];
940
UnicodeScalarValue uniChars[len];
941
942
CGGI_CreateGlyphsAndScanForComplexities(glyphInfos, strike, &mode,
943
rawGlyphCodes, uniChars, glyphs,
944
advances, bboxes, len);
945
946
return;
947
}
948
949
// just do one malloc, and carve it up for all the buffers
950
void *buffer = malloc(sizeof(CGRect) * sizeof(CGSize) *
951
sizeof(CGGlyph) * sizeof(UnicodeScalarValue) * len);
952
if (buffer == NULL) {
953
[[NSException exceptionWithName:NSMallocException
954
reason:@"Failed to allocate memory for the temporary glyph strike and measurement buffers." userInfo:nil] raise];
955
}
956
957
CGRect *bboxes = (CGRect *)(buffer);
958
CGSize *advances = (CGSize *)(bboxes + sizeof(CGRect) * len);
959
CGGlyph *glyphs = (CGGlyph *)(advances + sizeof(CGGlyph) * len);
960
UnicodeScalarValue *uniChars = (UnicodeScalarValue *)(glyphs + sizeof(UnicodeScalarValue) * len);
961
962
CGGI_CreateGlyphsAndScanForComplexities(glyphInfos, strike, &mode,
963
rawGlyphCodes, uniChars, glyphs,
964
advances, bboxes, len);
965
966
free(buffer);
967
}
968
969
/*
970
* Calculates bounding boxes (for given transform) and advance (for untransformed 1pt-size font) for specified glyphs.
971
*/
972
void
973
CGGlyphImages_GetGlyphMetrics(const CTFontRef font,
974
const CGAffineTransform *tx,
975
const JRSFontRenderingStyle style,
976
const CGGlyph glyphs[],
977
size_t count,
978
CGRect bboxes[],
979
CGSize advances[]) {
980
if (IsEmojiFont(font)) {
981
// Glyph metrics for emoji font are not strictly proportional to font size,
982
// so we need to construct real-sized font object to calculate them.
983
// The logic here must match the logic in CGGI_CreateImageForGlyph,
984
// which performs glyph drawing.
985
986
CGFloat fontSize = sqrt(fabs(tx->a * tx->d - tx->b * tx->c));
987
CTFontRef sizedFont = CTFontCreateCopyWithSymbolicTraits(font, fontSize, NULL, 0, 0);
988
989
if (bboxes) {
990
// JRSFontGetBoundingBoxesForGlyphsAndStyle works incorrectly for AppleColorEmoji font:
991
// it uses bottom left corner of the glyph's bounding box as a fixed point of transformation
992
// instead of glyph's origin point (used at drawing). So, as a workaround,
993
// we request a bounding box for the untransformed glyph, and apply the transform ourselves.
994
JRSFontGetBoundingBoxesForGlyphsAndStyle(sizedFont, &CGAffineTransformIdentity, style, glyphs, count, bboxes);
995
CGAffineTransform txNormalized = CGAffineTransformMake(tx->a / fontSize,
996
tx->b / fontSize,
997
tx->c / fontSize,
998
tx->d / fontSize,
999
0, 0);
1000
for (int i = 0; i < count; i++) {
1001
bboxes[i] = CGRectApplyAffineTransform(bboxes[i], txNormalized);
1002
}
1003
}
1004
1005
if (advances) {
1006
CTFontGetAdvancesForGlyphs(sizedFont, kCTFontDefaultOrientation, glyphs, advances, count);
1007
for (int i = 0; i < count; i++) {
1008
// Calling code will scale the result back
1009
advances[i].width /= fontSize;
1010
advances[i].height /= fontSize;
1011
}
1012
}
1013
1014
CFRelease(sizedFont);
1015
} else {
1016
if (bboxes) {
1017
JRSFontGetBoundingBoxesForGlyphsAndStyle(font, tx, style, glyphs, count, bboxes);
1018
}
1019
if (advances) {
1020
CTFontGetAdvancesForGlyphs(font, kCTFontDefaultOrientation, glyphs, advances, count);
1021
}
1022
}
1023
}
1024