12047
|
1 |
/*
|
|
2 |
* Copyright (c) 2011, 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 |
// Native side of the Quartz text pipe, paints on Quartz Surface Datas.
|
|
27 |
// Interesting Docs : /Developer/Documentation/Cocoa/TasksAndConcepts/ProgrammingTopics/FontHandling/FontHandling.html
|
|
28 |
|
|
29 |
#import "sun_awt_SunHints.h"
|
|
30 |
#import "sun_lwawt_macosx_CTextPipe.h"
|
|
31 |
#import "sun_java2d_OSXSurfaceData.h"
|
|
32 |
|
|
33 |
#import <JavaNativeFoundation/JavaNativeFoundation.h>
|
|
34 |
|
|
35 |
#import "CoreTextSupport.h"
|
|
36 |
#import "QuartzSurfaceData.h"
|
|
37 |
#include "AWTStrike.h"
|
|
38 |
|
|
39 |
|
|
40 |
static const CGAffineTransform sInverseTX = { 1, 0, 0, -1, 0, 0 };
|
|
41 |
|
|
42 |
|
|
43 |
#pragma mark --- CoreText Support ---
|
|
44 |
|
|
45 |
|
|
46 |
// Translates a Unicode into a CGGlyph/CTFontRef pair
|
|
47 |
// Returns the substituted font, and places the appropriate glyph into "glyphRef"
|
|
48 |
CTFontRef JavaCT_CopyCTFallbackFontAndGlyphForUnicode
|
|
49 |
(const AWTFont *font, const UTF16Char *charRef, CGGlyph *glyphRef, int count) {
|
|
50 |
CTFontRef fallback = JRSFontCreateFallbackFontForCharacters((CTFontRef)font->fFont, charRef, count);
|
|
51 |
if (fallback == NULL)
|
|
52 |
{
|
|
53 |
// use the original font if we somehow got duped into trying to fallback something we can't
|
|
54 |
fallback = (CTFontRef)font->fFont;
|
|
55 |
CFRetain(fallback);
|
|
56 |
}
|
|
57 |
|
|
58 |
CTFontGetGlyphsForCharacters(fallback, charRef, glyphRef, count);
|
|
59 |
return fallback;
|
|
60 |
}
|
|
61 |
|
|
62 |
// Translates a Java glyph code int (might be a negative unicode value) into a CGGlyph/CTFontRef pair
|
|
63 |
// Returns the substituted font, and places the appropriate glyph into "glyph"
|
|
64 |
CTFontRef JavaCT_CopyCTFallbackFontAndGlyphForJavaGlyphCode
|
|
65 |
(const AWTFont *font, const jint glyphCode, CGGlyph *glyphRef)
|
|
66 |
{
|
|
67 |
// negative glyph codes are really unicodes, which were placed there by the mapper
|
|
68 |
// to indicate we should use CoreText to substitute the character
|
|
69 |
if (glyphCode >= 0)
|
|
70 |
{
|
|
71 |
*glyphRef = glyphCode;
|
|
72 |
CFRetain(font->fFont);
|
|
73 |
return (CTFontRef)font->fFont;
|
|
74 |
}
|
|
75 |
|
|
76 |
UTF16Char character = -glyphCode;
|
|
77 |
return JavaCT_CopyCTFallbackFontAndGlyphForUnicode(font, &character, glyphRef, 1);
|
|
78 |
}
|
|
79 |
|
|
80 |
// Breakup a 32 bit unicode value into the component surrogate pairs
|
|
81 |
void JavaCT_BreakupUnicodeIntoSurrogatePairs(int uniChar, UTF16Char charRef[]) {
|
|
82 |
int value = uniChar - 0x10000;
|
|
83 |
UTF16Char low_surrogate = (value & 0x3FF) | LO_SURROGATE_START;
|
|
84 |
UTF16Char high_surrogate = (((int)(value & 0xFFC00)) >> 10) | HI_SURROGATE_START;
|
|
85 |
charRef[0] = high_surrogate;
|
|
86 |
charRef[1] = low_surrogate;
|
|
87 |
}
|
|
88 |
|
|
89 |
|
|
90 |
|
|
91 |
/*
|
|
92 |
* Callback for CoreText which uses the CoreTextProviderStruct to feed CT UniChars
|
|
93 |
* We only use it for one-off lines, and don't attempt to fragment our strings
|
|
94 |
*/
|
|
95 |
const UniChar *Java_CTProvider
|
|
96 |
(CFIndex stringIndex, CFIndex *charCount, CFDictionaryRef *attributes, void *refCon)
|
|
97 |
{
|
|
98 |
// if we have a zero length string we can just return NULL for the string
|
|
99 |
// or if the index anything other than 0 we are not using core text
|
|
100 |
// correctly since we only have one run.
|
|
101 |
if (stringIndex != 0)
|
|
102 |
{
|
|
103 |
return NULL;
|
|
104 |
}
|
|
105 |
|
|
106 |
CTS_ProviderStruct *ctps = (CTS_ProviderStruct *)refCon;
|
|
107 |
*charCount = ctps->length;
|
|
108 |
*attributes = ctps->attributes;
|
|
109 |
return ctps->unicodes;
|
|
110 |
}
|
|
111 |
|
|
112 |
|
|
113 |
/*
|
|
114 |
* Gets a Dictionary filled with common details we want to use for CoreText when we are interacting
|
|
115 |
* with it from Java.
|
|
116 |
*/
|
|
117 |
static NSDictionary* ctsDictionaryFor(const NSFont *font, BOOL useFractionalMetrics)
|
|
118 |
{
|
|
119 |
NSNumber *gZeroNumber = [NSNumber numberWithInt:0];
|
|
120 |
NSNumber *gOneNumber = [NSNumber numberWithInt:1];
|
|
121 |
|
|
122 |
return [NSDictionary dictionaryWithObjectsAndKeys:
|
|
123 |
font, NSFontAttributeName,
|
|
124 |
gOneNumber, (id)kCTForegroundColorFromContextAttributeName,
|
|
125 |
useFractionalMetrics ? gZeroNumber : gOneNumber, @"CTIntegerMetrics", // force integer hack in CoreText to help with Java's integer assumptions
|
|
126 |
gZeroNumber, NSLigatureAttributeName,
|
|
127 |
gZeroNumber, NSKernAttributeName,
|
|
128 |
nil];
|
|
129 |
}
|
|
130 |
|
|
131 |
// Itterates though each glyph, and if a transform is present for that glyph, apply it to the CGContext, and strike the glyph.
|
|
132 |
// If there is no per-glyph transform, just strike the glyph. Advances must also be transformed on-the-spot as well.
|
|
133 |
void JavaCT_DrawGlyphVector
|
|
134 |
(const QuartzSDOps *qsdo, const AWTStrike *strike, const BOOL useSubstituion, const int uniChars[], const CGGlyph glyphs[], CGSize advances[], const jint g_gvTXIndicesAsInts[], const jdouble g_gvTransformsAsDoubles[], const CFIndex length)
|
|
135 |
{
|
|
136 |
CGPoint pt = { 0, 0 };
|
|
137 |
|
|
138 |
// get our baseline transform and font
|
|
139 |
CGContextRef cgRef = qsdo->cgRef;
|
|
140 |
CGAffineTransform ctmText = CGContextGetTextMatrix(cgRef);
|
|
141 |
//CGFontRef cgFont = CGContextGetFont(cgRef);
|
|
142 |
|
|
143 |
CGContextSaveGState(cgRef);
|
|
144 |
CGAffineTransform invTx = CGAffineTransformInvert(strike->fTx);
|
|
145 |
|
|
146 |
NSUInteger i;
|
|
147 |
for (i = 0; i < length; i++)
|
|
148 |
{
|
|
149 |
CGGlyph glyph = glyphs[i];
|
|
150 |
int uniChar = uniChars[i];
|
|
151 |
// if we found a unichar instead of a glyph code, get the fallback font,
|
|
152 |
// find the glyph code for the fallback font, and set the font on the current context
|
|
153 |
if (uniChar != 0)
|
|
154 |
{
|
|
155 |
CTFontRef fallback;
|
|
156 |
if (uniChar > 0xFFFF) {
|
|
157 |
UTF16Char charRef[2];
|
|
158 |
JavaCT_BreakupUnicodeIntoSurrogatePairs(uniChar, charRef);
|
|
159 |
CGGlyph glyphTmp[2];
|
|
160 |
fallback = JavaCT_CopyCTFallbackFontAndGlyphForUnicode(strike->fAWTFont, (const UTF16Char *)&charRef, (CGGlyph *)&glyphTmp, 2);
|
|
161 |
glyph = glyphTmp[0];
|
|
162 |
} else {
|
|
163 |
const UTF16Char u = uniChar;
|
|
164 |
fallback = JavaCT_CopyCTFallbackFontAndGlyphForUnicode(strike->fAWTFont, &u, (CGGlyph *)&glyph, 1);
|
|
165 |
}
|
|
166 |
if (fallback) {
|
|
167 |
const CGFontRef cgFallback = CTFontCopyGraphicsFont(fallback, NULL);
|
|
168 |
CFRelease(fallback);
|
|
169 |
|
|
170 |
if (cgFallback) {
|
|
171 |
CGContextSetFont(cgRef, cgFallback);
|
|
172 |
CFRelease(cgFallback);
|
|
173 |
}
|
|
174 |
}
|
|
175 |
}
|
|
176 |
|
|
177 |
// if we have per-glyph transformations
|
|
178 |
int tin = (g_gvTXIndicesAsInts == NULL) ? -1 : (g_gvTXIndicesAsInts[i] - 1) * 6;
|
|
179 |
if (tin < 0)
|
|
180 |
{
|
|
181 |
CGContextShowGlyphsAtPoint(cgRef, pt.x, pt.y, &glyph, 1);
|
|
182 |
}
|
|
183 |
else
|
|
184 |
{
|
|
185 |
CGAffineTransform tx = CGAffineTransformMake(
|
|
186 |
(CGFloat)g_gvTransformsAsDoubles[tin + 0], (CGFloat)g_gvTransformsAsDoubles[tin + 2],
|
|
187 |
(CGFloat)g_gvTransformsAsDoubles[tin + 1], (CGFloat)g_gvTransformsAsDoubles[tin + 3],
|
|
188 |
0, 0);
|
|
189 |
|
|
190 |
CGPoint txOffset = { (CGFloat)g_gvTransformsAsDoubles[tin + 4], (CGFloat)g_gvTransformsAsDoubles[tin + 5] };
|
|
191 |
|
|
192 |
txOffset = CGPointApplyAffineTransform(txOffset, invTx);
|
|
193 |
|
|
194 |
// apply the transform, strike the glyph, can change the transform back
|
|
195 |
CGContextSetTextMatrix(cgRef, CGAffineTransformConcat(ctmText, tx));
|
|
196 |
CGContextShowGlyphsAtPoint(cgRef, txOffset.x + pt.x, txOffset.y + pt.y, &glyph, 1);
|
|
197 |
CGContextSetTextMatrix(cgRef, ctmText);
|
|
198 |
|
|
199 |
// transform the measured advance for this strike
|
|
200 |
advances[i] = CGSizeApplyAffineTransform(advances[i], tx);
|
|
201 |
advances[i].width += txOffset.x;
|
|
202 |
advances[i].height += txOffset.y;
|
|
203 |
}
|
|
204 |
|
|
205 |
// move our next x,y
|
|
206 |
pt.x += advances[i].width;
|
|
207 |
pt.y += advances[i].height;
|
|
208 |
|
|
209 |
// reset the font on the context after striking a unicode with CoreText
|
|
210 |
if (uniChar != 0)
|
|
211 |
{
|
|
212 |
// CGContextSetFont(cgRef, cgFont);
|
|
213 |
CGContextSaveGState(cgRef);
|
|
214 |
}
|
|
215 |
}
|
|
216 |
}
|
|
217 |
|
|
218 |
// Using the Quartz Surface Data context, draw a hot-substituted character run
|
|
219 |
void JavaCT_DrawTextUsingQSD(JNIEnv *env, const QuartzSDOps *qsdo, const AWTStrike *strike, const jchar *chars, const jsize length)
|
|
220 |
{
|
|
221 |
CGContextRef cgRef = qsdo->cgRef;
|
|
222 |
|
|
223 |
AWTFont *awtFont = strike->fAWTFont;
|
|
224 |
CGFloat ptSize = strike->fSize;
|
|
225 |
CGAffineTransform tx = strike->fFontTx;
|
|
226 |
|
|
227 |
NSFont *nsFont = [NSFont fontWithName:[awtFont->fFont fontName] size:ptSize];
|
|
228 |
|
|
229 |
if (ptSize != 0) {
|
|
230 |
CGFloat invScale = 1 / ptSize;
|
|
231 |
tx = CGAffineTransformConcat(tx, CGAffineTransformMakeScale(invScale, invScale));
|
|
232 |
CGContextConcatCTM(cgRef, tx);
|
|
233 |
}
|
|
234 |
|
|
235 |
CGContextSetTextMatrix(cgRef, CGAffineTransformIdentity); // resets the damage from CoreText
|
|
236 |
|
|
237 |
NSString *string = [NSString stringWithCharacters:chars length:length];
|
|
238 |
NSAttributedString *attribString = [[NSAttributedString alloc] initWithString:string];
|
|
239 |
|
|
240 |
CTTypesetterRef typeSetterRef = CTTypesetterCreateWithAttributedStringAndOptions((CFAttributedStringRef) attribString, (CFDictionaryRef) ctsDictionaryFor(nsFont, JRSFontStyleUsesFractionalMetrics(strike->fStyle)));
|
|
241 |
|
|
242 |
CFRange range = {0, length};
|
|
243 |
CTLineRef lineRef = CTTypesetterCreateLine(typeSetterRef, range);
|
|
244 |
|
|
245 |
CTLineDraw(lineRef, cgRef);
|
|
246 |
|
|
247 |
[attribString release];
|
|
248 |
CFRelease(lineRef);
|
|
249 |
CFRelease(typeSetterRef);
|
|
250 |
}
|
|
251 |
|
|
252 |
|
|
253 |
/*----------------------
|
|
254 |
DrawTextContext is the funnel for all of our CoreText drawing.
|
|
255 |
All three JNI apis call through this method.
|
|
256 |
----------------------*/
|
|
257 |
static void DrawTextContext
|
|
258 |
(JNIEnv *env, QuartzSDOps *qsdo, const AWTStrike *strike, const jchar *chars, const jsize length, const jdouble x, const jdouble y)
|
|
259 |
{
|
|
260 |
if (length == 0)
|
|
261 |
{
|
|
262 |
return;
|
|
263 |
}
|
|
264 |
|
|
265 |
qsdo->BeginSurface(env, qsdo, SD_Text);
|
|
266 |
if (qsdo->cgRef == NULL)
|
|
267 |
{
|
|
268 |
qsdo->FinishSurface(env, qsdo);
|
|
269 |
return;
|
|
270 |
}
|
|
271 |
|
|
272 |
CGContextRef cgRef = qsdo->cgRef;
|
|
273 |
|
|
274 |
|
|
275 |
CGContextSaveGState(cgRef);
|
|
276 |
JRSFontSetRenderingStyleOnContext(cgRef, strike->fStyle);
|
|
277 |
|
|
278 |
// we want to translate before we transform (scale or rotate) <rdar://4042541> (vm)
|
|
279 |
CGContextTranslateCTM(cgRef, x, y);
|
|
280 |
|
|
281 |
AWTFont *awtfont = strike->fAWTFont; //(AWTFont *)(qsdo->fontInfo.awtfont);
|
|
282 |
NSCharacterSet *charSet = [awtfont->fFont coveredCharacterSet];
|
|
283 |
|
|
284 |
JavaCT_DrawTextUsingQSD(env, qsdo, strike, chars, length); // Draw with CoreText
|
|
285 |
|
|
286 |
CGContextRestoreGState(cgRef);
|
|
287 |
|
|
288 |
qsdo->FinishSurface(env, qsdo);
|
|
289 |
}
|
|
290 |
|
|
291 |
#pragma mark --- Glyph Vector Pipeline ---
|
|
292 |
|
|
293 |
/*-----------------------------------
|
|
294 |
Glyph Vector Pipeline
|
|
295 |
|
|
296 |
doDrawGlyphs() has been separated into several pipelined functions to increase performance,
|
|
297 |
and improve accountability for JNI resources, malloc'd memory, and error handling.
|
|
298 |
|
|
299 |
Each stage of the pipeline is responsible for doing only one major thing, like allocating buffers,
|
|
300 |
aquiring transform arrays from JNI, filling buffers, or striking glyphs. All resources or memory
|
|
301 |
aquired at a given stage, must be released in that stage. Any error that occurs (like a failed malloc)
|
|
302 |
is to be handled in the stage it occurs in, and is to return immediatly after freeing it's resources.
|
|
303 |
|
|
304 |
-----------------------------------*/
|
|
305 |
|
|
306 |
static JNF_CLASS_CACHE(jc_StandardGlyphVector, "sun/font/StandardGlyphVector");
|
|
307 |
|
|
308 |
// Checks the GlyphVector Java object for any transforms that were applied to individual characters. If none are present,
|
|
309 |
// strike the glyphs immediately in Core Graphics. Otherwise, obtain the arrays, and defer to above.
|
|
310 |
static inline void doDrawGlyphsPipe_checkForPerGlyphTransforms
|
|
311 |
(JNIEnv *env, QuartzSDOps *qsdo, const AWTStrike *strike, jobject gVector, BOOL useSubstituion, int *uniChars, CGGlyph *glyphs, CGSize *advances, size_t length)
|
|
312 |
{
|
|
313 |
// if we have no character substitution, and no per-glyph transformations - strike now!
|
|
314 |
static JNF_MEMBER_CACHE(jm_StandardGlyphVector_gti, jc_StandardGlyphVector, "gti", "Lsun/font/StandardGlyphVector$GlyphTransformInfo;");
|
|
315 |
jobject gti = JNFGetObjectField(env, gVector, jm_StandardGlyphVector_gti);
|
|
316 |
if (gti == 0)
|
|
317 |
{
|
|
318 |
if (useSubstituion)
|
|
319 |
{
|
|
320 |
// quasi-simple case, substitution, but no per-glyph transforms
|
|
321 |
JavaCT_DrawGlyphVector(qsdo, strike, TRUE, uniChars, glyphs, advances, NULL, NULL, length);
|
|
322 |
}
|
|
323 |
else
|
|
324 |
{
|
|
325 |
// fast path, straight to CG without per-glyph transforms
|
|
326 |
CGContextShowGlyphsWithAdvances(qsdo->cgRef, glyphs, advances, length);
|
|
327 |
}
|
|
328 |
return;
|
|
329 |
}
|
|
330 |
|
|
331 |
static JNF_CLASS_CACHE(jc_StandardGlyphVector_GlyphTransformInfo, "sun/font/StandardGlyphVector$GlyphTransformInfo");
|
|
332 |
static JNF_MEMBER_CACHE(jm_StandardGlyphVector_GlyphTransformInfo_transforms, jc_StandardGlyphVector_GlyphTransformInfo, "transforms", "[D");
|
|
333 |
jdoubleArray g_gtiTransformsArray = JNFGetObjectField(env, gti, jm_StandardGlyphVector_GlyphTransformInfo_transforms); //(*env)->GetObjectField(env, gti, g_gtiTransforms);
|
|
334 |
jdouble *g_gvTransformsAsDoubles = (*env)->GetPrimitiveArrayCritical(env, g_gtiTransformsArray, NULL);
|
|
335 |
|
|
336 |
static JNF_MEMBER_CACHE(jm_StandardGlyphVector_GlyphTransformInfo_indices, jc_StandardGlyphVector_GlyphTransformInfo, "indices", "[I");
|
|
337 |
jintArray g_gtiTXIndicesArray = JNFGetObjectField(env, gti, jm_StandardGlyphVector_GlyphTransformInfo_indices);
|
|
338 |
jint *g_gvTXIndicesAsInts = (*env)->GetPrimitiveArrayCritical(env, g_gtiTXIndicesArray, NULL);
|
|
339 |
|
|
340 |
// slowest case, we have per-glyph transforms, and possibly glyph substitution as well
|
|
341 |
JavaCT_DrawGlyphVector(qsdo, strike, useSubstituion, uniChars, glyphs, advances, g_gvTXIndicesAsInts, g_gvTransformsAsDoubles, length);
|
|
342 |
|
|
343 |
(*env)->ReleasePrimitiveArrayCritical(env, g_gtiTransformsArray, g_gvTransformsAsDoubles, JNI_ABORT);
|
|
344 |
(*env)->DeleteLocalRef(env, g_gtiTransformsArray);
|
|
345 |
|
|
346 |
(*env)->ReleasePrimitiveArrayCritical(env, g_gtiTXIndicesArray, g_gvTXIndicesAsInts, JNI_ABORT);
|
|
347 |
(*env)->DeleteLocalRef(env, g_gtiTXIndicesArray);
|
|
348 |
}
|
|
349 |
|
|
350 |
// Retrieves advances for translated unicodes
|
|
351 |
// Uses "glyphs" as a temporary buffer for the glyph-to-unicode translation
|
|
352 |
void JavaCT_GetAdvancesForUnichars
|
|
353 |
(const NSFont *font, const int uniChars[], CGGlyph glyphs[], const size_t length, CGSize advances[])
|
|
354 |
{
|
|
355 |
// cycle over each spot, and if we discovered a unicode to substitute, we have to calculate the advance for it
|
|
356 |
size_t i;
|
|
357 |
for (i = 0; i < length; i++)
|
|
358 |
{
|
|
359 |
UniChar uniChar = uniChars[i];
|
|
360 |
if (uniChar == 0) continue;
|
|
361 |
|
|
362 |
CGGlyph glyph = 0;
|
|
363 |
const CTFontRef fallback = JRSFontCreateFallbackFontForCharacters((CTFontRef)font, &uniChar, 1);
|
|
364 |
if (fallback) {
|
|
365 |
CTFontGetGlyphsForCharacters(fallback, &uniChar, &glyph, 1);
|
|
366 |
CTFontGetAdvancesForGlyphs(fallback, kCTFontDefaultOrientation, &glyph, &(advances[i]), 1);
|
|
367 |
CFRelease(fallback);
|
|
368 |
}
|
|
369 |
|
|
370 |
glyphs[i] = glyph;
|
|
371 |
}
|
|
372 |
}
|
|
373 |
|
|
374 |
// Fills the glyph buffer with glyphs from the GlyphVector object. Also checks to see if the glyph's positions have been
|
|
375 |
// already caculated from GlyphVector, or we simply ask Core Graphics to make some advances for us. Pre-calculated positions
|
|
376 |
// are translated into advances, since CG only understands advances.
|
|
377 |
static inline void doDrawGlyphsPipe_fillGlyphAndAdvanceBuffers
|
|
378 |
(JNIEnv *env, QuartzSDOps *qsdo, const AWTStrike *strike, jobject gVector, CGGlyph *glyphs, int *uniChars, CGSize *advances, size_t length, jintArray glyphsArray)
|
|
379 |
{
|
|
380 |
// fill the glyph buffer
|
|
381 |
jint *glyphsAsInts = (*env)->GetPrimitiveArrayCritical(env, glyphsArray, NULL);
|
|
382 |
|
|
383 |
// if a glyph code from Java is negative, that means it is really a unicode value
|
|
384 |
// which we can use in CoreText to strike the character in another font
|
|
385 |
size_t i;
|
|
386 |
BOOL complex = NO;
|
|
387 |
for (i = 0; i < length; i++)
|
|
388 |
{
|
|
389 |
jint code = glyphsAsInts[i];
|
|
390 |
if (code < 0)
|
|
391 |
{
|
|
392 |
complex = YES;
|
|
393 |
uniChars[i] = -code;
|
|
394 |
glyphs[i] = 0;
|
|
395 |
}
|
|
396 |
else
|
|
397 |
{
|
|
398 |
uniChars[i] = 0;
|
|
399 |
glyphs[i] = code;
|
|
400 |
}
|
|
401 |
}
|
|
402 |
|
|
403 |
(*env)->ReleasePrimitiveArrayCritical(env, glyphsArray, glyphsAsInts, JNI_ABORT);
|
|
404 |
|
|
405 |
// fill the advance buffer
|
|
406 |
static JNF_MEMBER_CACHE(jm_StandardGlyphVector_positions, jc_StandardGlyphVector, "positions", "[F");
|
|
407 |
jfloatArray posArray = JNFGetObjectField(env, gVector, jm_StandardGlyphVector_positions);
|
|
408 |
if (posArray != NULL)
|
|
409 |
{
|
|
410 |
// in this case, the positions have already been pre-calculated for us on the Java side
|
|
411 |
|
|
412 |
jfloat *positions = (*env)->GetPrimitiveArrayCritical(env, posArray, NULL);
|
|
413 |
CGPoint prev;
|
|
414 |
prev.x = positions[0];
|
|
415 |
prev.y = positions[1];
|
|
416 |
|
|
417 |
// <rdar://problem/4294061> take the first point, and move the context to that location
|
|
418 |
CGContextTranslateCTM(qsdo->cgRef, prev.x, prev.y);
|
|
419 |
|
|
420 |
CGAffineTransform invTx = CGAffineTransformInvert(strike->fFontTx);
|
|
421 |
|
|
422 |
// for each position, figure out the advance (since CG won't take positions directly)
|
|
423 |
size_t i;
|
|
424 |
for (i = 0; i < length - 1; i++)
|
|
425 |
{
|
|
426 |
size_t i2 = (i+1) * 2;
|
|
427 |
CGPoint pt;
|
|
428 |
pt.x = positions[i2];
|
|
429 |
pt.y = positions[i2+1];
|
|
430 |
pt = CGPointApplyAffineTransform(pt, invTx);
|
|
431 |
advances[i].width = pt.x - prev.x;
|
|
432 |
advances[i].height = -(pt.y - prev.y); // negative to translate to device space
|
|
433 |
prev.x = pt.x;
|
|
434 |
prev.y = pt.y;
|
|
435 |
}
|
|
436 |
|
|
437 |
(*env)->ReleasePrimitiveArrayCritical(env, posArray, positions, JNI_ABORT);
|
|
438 |
(*env)->DeleteLocalRef(env, posArray);
|
|
439 |
}
|
|
440 |
else
|
|
441 |
{
|
|
442 |
// in this case, we have to go and calculate the positions ourselves
|
|
443 |
// there were no pre-calculated positions from the glyph buffer on the Java side
|
|
444 |
AWTFont *awtFont = strike->fAWTFont;
|
|
445 |
CTFontGetAdvancesForGlyphs((CTFontRef)awtFont->fFont, kCTFontDefaultOrientation, glyphs, advances, length);
|
|
446 |
|
|
447 |
if (complex)
|
|
448 |
{
|
|
449 |
JavaCT_GetAdvancesForUnichars(awtFont->fFont, uniChars, glyphs, length, advances);
|
|
450 |
}
|
|
451 |
}
|
|
452 |
|
|
453 |
// continue on to the next stage of the pipe
|
|
454 |
doDrawGlyphsPipe_checkForPerGlyphTransforms(env, qsdo, strike, gVector, complex, uniChars, glyphs, advances, length);
|
|
455 |
}
|
|
456 |
|
|
457 |
// Obtains the glyph array to determine the number of glyphs we are dealing with. If we are dealing a large number of glyphs,
|
|
458 |
// we malloc a buffer to hold the glyphs and their advances, otherwise we use stack allocated buffers.
|
|
459 |
static inline void doDrawGlyphsPipe_getGlyphVectorLengthAndAlloc
|
|
460 |
(JNIEnv *env, QuartzSDOps *qsdo, const AWTStrike *strike, jobject gVector)
|
|
461 |
{
|
|
462 |
static JNF_MEMBER_CACHE(jm_StandardGlyphVector_glyphs, jc_StandardGlyphVector, "glyphs", "[I");
|
|
463 |
jintArray glyphsArray = JNFGetObjectField(env, gVector, jm_StandardGlyphVector_glyphs);
|
|
464 |
jsize length = (*env)->GetArrayLength(env, glyphsArray);
|
|
465 |
|
|
466 |
if (length == 0)
|
|
467 |
{
|
|
468 |
// nothing to draw
|
|
469 |
(*env)->DeleteLocalRef(env, glyphsArray);
|
|
470 |
return;
|
|
471 |
}
|
|
472 |
|
|
473 |
if (length < MAX_STACK_ALLOC_GLYPH_BUFFER_SIZE)
|
|
474 |
{
|
|
475 |
// if we are small enough, fit everything onto the stack
|
|
476 |
CGGlyph glyphs[length];
|
|
477 |
int uniChars[length];
|
|
478 |
CGSize advances[length];
|
|
479 |
doDrawGlyphsPipe_fillGlyphAndAdvanceBuffers(env, qsdo, strike, gVector, glyphs, uniChars, advances, length, glyphsArray);
|
|
480 |
}
|
|
481 |
else
|
|
482 |
{
|
|
483 |
// otherwise, we should malloc and free buffers for this large run
|
|
484 |
CGGlyph *glyphs = (CGGlyph *)malloc(sizeof(CGGlyph) * length);
|
|
485 |
int *uniChars = (int *)malloc(sizeof(int) * length);
|
|
486 |
CGSize *advances = (CGSize *)malloc(sizeof(CGSize) * length);
|
|
487 |
|
|
488 |
if (glyphs == NULL || advances == NULL)
|
|
489 |
{
|
|
490 |
(*env)->DeleteLocalRef(env, glyphsArray);
|
|
491 |
[NSException raise:NSMallocException format:@"%s-%s:%d", __FILE__, __FUNCTION__, __LINE__];
|
|
492 |
return;
|
|
493 |
}
|
|
494 |
|
|
495 |
doDrawGlyphsPipe_fillGlyphAndAdvanceBuffers(env, qsdo, strike, gVector, glyphs, uniChars, advances, length, glyphsArray);
|
|
496 |
|
|
497 |
free(glyphs);
|
|
498 |
free(uniChars);
|
|
499 |
free(advances);
|
|
500 |
}
|
|
501 |
|
|
502 |
(*env)->DeleteLocalRef(env, glyphsArray);
|
|
503 |
}
|
|
504 |
|
|
505 |
// Setup and save the state of the CGContext, and apply any java.awt.Font transforms to the context.
|
|
506 |
static inline void doDrawGlyphsPipe_applyFontTransforms
|
|
507 |
(JNIEnv *env, QuartzSDOps *qsdo, const AWTStrike *strike, jobject gVector, const jfloat x, const jfloat y)
|
|
508 |
{
|
|
509 |
CGContextRef cgRef = qsdo->cgRef;
|
|
510 |
CGContextSetFontSize(cgRef, 1.0);
|
|
511 |
CGContextSetFont(cgRef, strike->fAWTFont->fNativeCGFont);
|
|
512 |
CGContextSetTextMatrix(cgRef, CGAffineTransformIdentity);
|
|
513 |
|
|
514 |
CGAffineTransform tx = strike->fFontTx;
|
|
515 |
tx.tx += x;
|
|
516 |
tx.ty += y;
|
|
517 |
CGContextConcatCTM(cgRef, tx);
|
|
518 |
|
|
519 |
doDrawGlyphsPipe_getGlyphVectorLengthAndAlloc(env, qsdo, strike, gVector);
|
|
520 |
}
|
|
521 |
|
|
522 |
|
|
523 |
#pragma mark --- CTextPipe JNI ---
|
|
524 |
|
|
525 |
|
|
526 |
/*
|
|
527 |
* Class: sun_lwawt_macosx_CTextPipe
|
|
528 |
* Method: doDrawString
|
|
529 |
* Signature: (Lsun/java2d/SurfaceData;JLjava/lang/String;DD)V
|
|
530 |
*/
|
|
531 |
JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CTextPipe_doDrawString
|
|
532 |
(JNIEnv *env, jobject jthis, jobject jsurfacedata, jlong awtStrikePtr, jstring str, jdouble x, jdouble y)
|
|
533 |
{
|
|
534 |
QuartzSDOps *qsdo = (QuartzSDOps *)SurfaceData_GetOps(env, jsurfacedata);
|
|
535 |
AWTStrike *awtStrike = (AWTStrike *)jlong_to_ptr(awtStrikePtr);
|
|
536 |
|
|
537 |
JNF_COCOA_ENTER(env);
|
|
538 |
|
|
539 |
jsize len = (*env)->GetStringLength(env, str);
|
|
540 |
|
|
541 |
if (len < MAX_STACK_ALLOC_GLYPH_BUFFER_SIZE) // optimized for stack allocation <rdar://problem/4285041>
|
|
542 |
{
|
|
543 |
jchar unichars[len];
|
|
544 |
(*env)->GetStringRegion(env, str, 0, len, unichars);
|
|
545 |
JNF_CHECK_AND_RETHROW_EXCEPTION(env);
|
|
546 |
|
|
547 |
// Draw the text context
|
|
548 |
DrawTextContext(env, qsdo, awtStrike, unichars, len, x, y);
|
|
549 |
}
|
|
550 |
else
|
|
551 |
{
|
|
552 |
// Get string to draw and the length
|
|
553 |
const jchar *unichars = JNFGetStringUTF16UniChars(env, str);
|
|
554 |
|
|
555 |
// Draw the text context
|
|
556 |
DrawTextContext(env, qsdo, awtStrike, unichars, len, x, y);
|
|
557 |
|
|
558 |
JNFReleaseStringUTF16UniChars(env, str, unichars);
|
|
559 |
}
|
|
560 |
|
|
561 |
JNF_COCOA_RENDERER_EXIT(env);
|
|
562 |
}
|
|
563 |
|
|
564 |
|
|
565 |
/*
|
|
566 |
* Class: sun_lwawt_macosx_CTextPipe
|
|
567 |
* Method: doUnicodes
|
|
568 |
* Signature: (Lsun/java2d/SurfaceData;J[CIIFF)V
|
|
569 |
*/
|
|
570 |
JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CTextPipe_doUnicodes
|
|
571 |
(JNIEnv *env, jobject jthis, jobject jsurfacedata, jlong awtStrikePtr, jcharArray unicodes, jint offset, jint length, jfloat x, jfloat y)
|
|
572 |
{
|
|
573 |
QuartzSDOps *qsdo = (QuartzSDOps *)SurfaceData_GetOps(env, jsurfacedata);
|
|
574 |
AWTStrike *awtStrike = (AWTStrike *)jlong_to_ptr(awtStrikePtr);
|
|
575 |
|
|
576 |
JNF_COCOA_ENTER(env);
|
|
577 |
|
|
578 |
// Setup the text context
|
|
579 |
if (length < MAX_STACK_ALLOC_GLYPH_BUFFER_SIZE) // optimized for stack allocation
|
|
580 |
{
|
|
581 |
jchar copyUnichars[length];
|
|
582 |
(*env)->GetCharArrayRegion(env, unicodes, offset, length, copyUnichars);
|
|
583 |
JNF_CHECK_AND_RETHROW_EXCEPTION(env);
|
|
584 |
DrawTextContext(env, qsdo, awtStrike, copyUnichars, length, x, y);
|
|
585 |
}
|
|
586 |
else
|
|
587 |
{
|
|
588 |
jchar *copyUnichars = malloc(length * sizeof(jchar));
|
|
589 |
if (!copyUnichars) {
|
|
590 |
[JNFException raise:env as:kOutOfMemoryError reason:"Failed to malloc memory to create the glyphs for string drawing"];
|
|
591 |
}
|
|
592 |
|
|
593 |
@try {
|
|
594 |
(*env)->GetCharArrayRegion(env, unicodes, offset, length, copyUnichars);
|
|
595 |
JNF_CHECK_AND_RETHROW_EXCEPTION(env);
|
|
596 |
DrawTextContext(env, qsdo, awtStrike, copyUnichars, length, x, y);
|
|
597 |
} @finally {
|
|
598 |
free(copyUnichars);
|
|
599 |
}
|
|
600 |
}
|
|
601 |
|
|
602 |
JNF_COCOA_RENDERER_EXIT(env);
|
|
603 |
}
|
|
604 |
|
|
605 |
/*
|
|
606 |
* Class: sun_lwawt_macosx_CTextPipe
|
|
607 |
* Method: doOneUnicode
|
|
608 |
* Signature: (Lsun/java2d/SurfaceData;JCFF)V
|
|
609 |
*/
|
|
610 |
JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CTextPipe_doOneUnicode
|
|
611 |
(JNIEnv *env, jobject jthis, jobject jsurfacedata, jlong awtStrikePtr, jchar aUnicode, jfloat x, jfloat y)
|
|
612 |
{
|
|
613 |
QuartzSDOps *qsdo = (QuartzSDOps *)SurfaceData_GetOps(env, jsurfacedata);
|
|
614 |
AWTStrike *awtStrike = (AWTStrike *)jlong_to_ptr(awtStrikePtr);
|
|
615 |
|
|
616 |
JNF_COCOA_ENTER(env);
|
|
617 |
|
|
618 |
DrawTextContext(env, qsdo, awtStrike, &aUnicode, 1, x, y);
|
|
619 |
|
|
620 |
JNF_COCOA_RENDERER_EXIT(env);
|
|
621 |
}
|
|
622 |
|
|
623 |
/*
|
|
624 |
* Class: sun_lwawt_macosx_CTextPipe
|
|
625 |
* Method: doDrawGlyphs
|
|
626 |
* Signature: (Lsun/java2d/SurfaceData;JLjava/awt/font/GlyphVector;FF)V
|
|
627 |
*/
|
|
628 |
JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CTextPipe_doDrawGlyphs
|
|
629 |
(JNIEnv *env, jobject jthis, jobject jsurfacedata, jlong awtStrikePtr, jobject gVector, jfloat x, jfloat y)
|
|
630 |
{
|
|
631 |
QuartzSDOps *qsdo = (QuartzSDOps *)SurfaceData_GetOps(env, jsurfacedata);
|
|
632 |
AWTStrike *awtStrike = (AWTStrike *)jlong_to_ptr(awtStrikePtr);
|
|
633 |
|
|
634 |
JNF_COCOA_ENTER(env);
|
|
635 |
|
|
636 |
qsdo->BeginSurface(env, qsdo, SD_Text);
|
|
637 |
if (qsdo->cgRef == NULL)
|
|
638 |
{
|
|
639 |
qsdo->FinishSurface(env, qsdo);
|
|
640 |
return;
|
|
641 |
}
|
|
642 |
|
|
643 |
CGContextSaveGState(qsdo->cgRef);
|
|
644 |
JRSFontSetRenderingStyleOnContext(qsdo->cgRef, JRSFontGetRenderingStyleForHints(sun_awt_SunHints_INTVAL_FRACTIONALMETRICS_ON, sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_ON));
|
|
645 |
|
|
646 |
doDrawGlyphsPipe_applyFontTransforms(env, qsdo, awtStrike, gVector, x, y);
|
|
647 |
|
|
648 |
CGContextRestoreGState(qsdo->cgRef);
|
|
649 |
|
|
650 |
qsdo->FinishSurface(env, qsdo);
|
|
651 |
|
|
652 |
JNF_COCOA_RENDERER_EXIT(env);
|
|
653 |
}
|