blob: e62b034cc694577e71c9bda2b5158f2b1027f02b [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/* libs/android_runtime/android/graphics/Paint.cpp
2**
3** Copyright 2006, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9** http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080018#include "jni.h"
19#include "GraphicsJNI.h"
20#include <android_runtime/AndroidRuntime.h>
21
22#include "SkBlurDrawLooper.h"
23#include "SkColorFilter.h"
24#include "SkMaskFilter.h"
25#include "SkRasterizer.h"
26#include "SkShader.h"
27#include "SkTypeface.h"
28#include "SkXfermode.h"
Doug Felt0c702b82010-05-14 10:55:42 -070029#include "unicode/ushape.h"
Doug Feltf7cb1f72010-07-01 16:20:43 -070030#include "TextLayout.h"
Doug Felt0c702b82010-05-14 10:55:42 -070031
32// temporary for debugging
Chet Haase5c13d892010-10-08 08:37:55 -070033#include <Caches.h>
Doug Felt0c702b82010-05-14 10:55:42 -070034#include <utils/Log.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035
36namespace android {
37
38struct JMetricsID {
39 jfieldID top;
40 jfieldID ascent;
41 jfieldID descent;
42 jfieldID bottom;
43 jfieldID leading;
44};
45
46static jclass gFontMetrics_class;
47static JMetricsID gFontMetrics_fieldID;
48
49static jclass gFontMetricsInt_class;
50static JMetricsID gFontMetricsInt_fieldID;
51
Mike Reed3d63e012009-07-27 09:50:31 -040052static void defaultSettingsForAndroid(SkPaint* paint) {
53 // looks best we decided
54 paint->setHinting(SkPaint::kSlight_Hinting);
55 // utf16 is required for java
56 paint->setTextEncoding(SkPaint::kUTF16_TextEncoding);
57}
58
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059class SkPaintGlue {
60public:
Doug Felt0c702b82010-05-14 10:55:42 -070061 enum MoveOpt {
62 AFTER, AT_OR_AFTER, BEFORE, AT_OR_BEFORE, AT
63 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064
65 static void finalizer(JNIEnv* env, jobject clazz, SkPaint* obj) {
66 delete obj;
67 }
68
69 static SkPaint* init(JNIEnv* env, jobject clazz) {
70 SkPaint* obj = new SkPaint();
Mike Reed3d63e012009-07-27 09:50:31 -040071 defaultSettingsForAndroid(obj);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072 return obj;
73 }
74
75 static SkPaint* intiWithPaint(JNIEnv* env, jobject clazz, SkPaint* paint) {
76 SkPaint* obj = new SkPaint(*paint);
77 return obj;
78 }
79
80 static void reset(JNIEnv* env, jobject clazz, SkPaint* obj) {
81 obj->reset();
Mike Reed3d63e012009-07-27 09:50:31 -040082 defaultSettingsForAndroid(obj);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083 }
84
85 static void assign(JNIEnv* env, jobject clazz, SkPaint* dst, const SkPaint* src) {
86 *dst = *src;
87 }
88
89 static jint getFlags(JNIEnv* env, jobject paint) {
90 NPE_CHECK_RETURN_ZERO(env, paint);
91 return GraphicsJNI::getNativePaint(env, paint)->getFlags();
92 }
93
94 static void setFlags(JNIEnv* env, jobject paint, jint flags) {
95 NPE_CHECK_RETURN_VOID(env, paint);
96 GraphicsJNI::getNativePaint(env, paint)->setFlags(flags);
97 }
98
99 static void setAntiAlias(JNIEnv* env, jobject paint, jboolean aa) {
100 NPE_CHECK_RETURN_VOID(env, paint);
101 GraphicsJNI::getNativePaint(env, paint)->setAntiAlias(aa);
102 }
103
104 static void setLinearText(JNIEnv* env, jobject paint, jboolean linearText) {
105 NPE_CHECK_RETURN_VOID(env, paint);
106 GraphicsJNI::getNativePaint(env, paint)->setLinearText(linearText);
107 }
108
109 static void setSubpixelText(JNIEnv* env, jobject paint, jboolean subpixelText) {
110 NPE_CHECK_RETURN_VOID(env, paint);
111 GraphicsJNI::getNativePaint(env, paint)->setSubpixelText(subpixelText);
112 }
113
114 static void setUnderlineText(JNIEnv* env, jobject paint, jboolean underlineText) {
115 NPE_CHECK_RETURN_VOID(env, paint);
116 GraphicsJNI::getNativePaint(env, paint)->setUnderlineText(underlineText);
117 }
118
119 static void setStrikeThruText(JNIEnv* env, jobject paint, jboolean strikeThruText) {
120 NPE_CHECK_RETURN_VOID(env, paint);
121 GraphicsJNI::getNativePaint(env, paint)->setStrikeThruText(strikeThruText);
122 }
123
124 static void setFakeBoldText(JNIEnv* env, jobject paint, jboolean fakeBoldText) {
125 NPE_CHECK_RETURN_VOID(env, paint);
126 GraphicsJNI::getNativePaint(env, paint)->setFakeBoldText(fakeBoldText);
127 }
128
129 static void setFilterBitmap(JNIEnv* env, jobject paint, jboolean filterBitmap) {
130 NPE_CHECK_RETURN_VOID(env, paint);
131 GraphicsJNI::getNativePaint(env, paint)->setFilterBitmap(filterBitmap);
132 }
133
134 static void setDither(JNIEnv* env, jobject paint, jboolean dither) {
135 NPE_CHECK_RETURN_VOID(env, paint);
136 GraphicsJNI::getNativePaint(env, paint)->setDither(dither);
137 }
138
139 static jint getStyle(JNIEnv* env, jobject clazz, SkPaint* obj) {
140 return obj->getStyle();
141 }
142
143 static void setStyle(JNIEnv* env, jobject clazz, SkPaint* obj, SkPaint::Style style) {
144 obj->setStyle(style);
145 }
146
147 static jint getColor(JNIEnv* env, jobject paint) {
148 NPE_CHECK_RETURN_ZERO(env, paint);
149 return GraphicsJNI::getNativePaint(env, paint)->getColor();
150 }
151
152 static jint getAlpha(JNIEnv* env, jobject paint) {
153 NPE_CHECK_RETURN_ZERO(env, paint);
154 return GraphicsJNI::getNativePaint(env, paint)->getAlpha();
155 }
156
157 static void setColor(JNIEnv* env, jobject paint, jint color) {
158 NPE_CHECK_RETURN_VOID(env, paint);
159 GraphicsJNI::getNativePaint(env, paint)->setColor(color);
160 }
161
162 static void setAlpha(JNIEnv* env, jobject paint, jint a) {
163 NPE_CHECK_RETURN_VOID(env, paint);
164 GraphicsJNI::getNativePaint(env, paint)->setAlpha(a);
165 }
166
167 static jfloat getStrokeWidth(JNIEnv* env, jobject paint) {
168 NPE_CHECK_RETURN_ZERO(env, paint);
169 return SkScalarToFloat(GraphicsJNI::getNativePaint(env, paint)->getStrokeWidth());
170 }
171
172 static void setStrokeWidth(JNIEnv* env, jobject paint, jfloat width) {
173 NPE_CHECK_RETURN_VOID(env, paint);
174 GraphicsJNI::getNativePaint(env, paint)->setStrokeWidth(SkFloatToScalar(width));
175 }
176
177 static jfloat getStrokeMiter(JNIEnv* env, jobject paint) {
178 NPE_CHECK_RETURN_ZERO(env, paint);
179 return SkScalarToFloat(GraphicsJNI::getNativePaint(env, paint)->getStrokeMiter());
180 }
181
182 static void setStrokeMiter(JNIEnv* env, jobject paint, jfloat miter) {
183 NPE_CHECK_RETURN_VOID(env, paint);
184 GraphicsJNI::getNativePaint(env, paint)->setStrokeMiter(SkFloatToScalar(miter));
185 }
186
187 static jint getStrokeCap(JNIEnv* env, jobject clazz, SkPaint* obj) {
188 return obj->getStrokeCap();
189 }
190
191 static void setStrokeCap(JNIEnv* env, jobject clazz, SkPaint* obj, SkPaint::Cap cap) {
192 obj->setStrokeCap(cap);
193 }
194
195 static jint getStrokeJoin(JNIEnv* env, jobject clazz, SkPaint* obj) {
196 return obj->getStrokeJoin();
197 }
198
199 static void setStrokeJoin(JNIEnv* env, jobject clazz, SkPaint* obj, SkPaint::Join join) {
200 obj->setStrokeJoin(join);
201 }
202
203 static jboolean getFillPath(JNIEnv* env, jobject clazz, SkPaint* obj, SkPath* src, SkPath* dst) {
204 return obj->getFillPath(*src, dst);
205 }
206
207 static SkShader* setShader(JNIEnv* env, jobject clazz, SkPaint* obj, SkShader* shader) {
208 return obj->setShader(shader);
209 }
210
211 static SkColorFilter* setColorFilter(JNIEnv* env, jobject clazz, SkPaint* obj, SkColorFilter* filter) {
212 return obj->setColorFilter(filter);
213 }
214
215 static SkXfermode* setXfermode(JNIEnv* env, jobject clazz, SkPaint* obj, SkXfermode* xfermode) {
216 return obj->setXfermode(xfermode);
217 }
218
219 static SkPathEffect* setPathEffect(JNIEnv* env, jobject clazz, SkPaint* obj, SkPathEffect* effect) {
220 return obj->setPathEffect(effect);
221 }
222
223 static SkMaskFilter* setMaskFilter(JNIEnv* env, jobject clazz, SkPaint* obj, SkMaskFilter* maskfilter) {
224 return obj->setMaskFilter(maskfilter);
225 }
226
227 static SkTypeface* setTypeface(JNIEnv* env, jobject clazz, SkPaint* obj, SkTypeface* typeface) {
228 return obj->setTypeface(typeface);
229 }
230
231 static SkRasterizer* setRasterizer(JNIEnv* env, jobject clazz, SkPaint* obj, SkRasterizer* rasterizer) {
232 return obj->setRasterizer(rasterizer);
233 }
234
235 static jint getTextAlign(JNIEnv* env, jobject clazz, SkPaint* obj) {
236 return obj->getTextAlign();
237 }
238
239 static void setTextAlign(JNIEnv* env, jobject clazz, SkPaint* obj, SkPaint::Align align) {
240 obj->setTextAlign(align);
241 }
242
243 static jfloat getTextSize(JNIEnv* env, jobject paint) {
244 NPE_CHECK_RETURN_ZERO(env, paint);
245 return SkScalarToFloat(GraphicsJNI::getNativePaint(env, paint)->getTextSize());
246 }
247
248 static void setTextSize(JNIEnv* env, jobject paint, jfloat textSize) {
249 NPE_CHECK_RETURN_VOID(env, paint);
250 GraphicsJNI::getNativePaint(env, paint)->setTextSize(SkFloatToScalar(textSize));
251 }
252
253 static jfloat getTextScaleX(JNIEnv* env, jobject paint) {
254 NPE_CHECK_RETURN_ZERO(env, paint);
255 return SkScalarToFloat(GraphicsJNI::getNativePaint(env, paint)->getTextScaleX());
256 }
257
258 static void setTextScaleX(JNIEnv* env, jobject paint, jfloat scaleX) {
259 NPE_CHECK_RETURN_VOID(env, paint);
260 GraphicsJNI::getNativePaint(env, paint)->setTextScaleX(SkFloatToScalar(scaleX));
261 }
262
263 static jfloat getTextSkewX(JNIEnv* env, jobject paint) {
264 NPE_CHECK_RETURN_ZERO(env, paint);
265 return SkScalarToFloat(GraphicsJNI::getNativePaint(env, paint)->getTextSkewX());
266 }
267
268 static void setTextSkewX(JNIEnv* env, jobject paint, jfloat skewX) {
269 NPE_CHECK_RETURN_VOID(env, paint);
270 GraphicsJNI::getNativePaint(env, paint)->setTextSkewX(SkFloatToScalar(skewX));
271 }
272
273 static jfloat ascent(JNIEnv* env, jobject paint) {
274 NPE_CHECK_RETURN_ZERO(env, paint);
275 SkPaint::FontMetrics metrics;
276 (void)GraphicsJNI::getNativePaint(env, paint)->getFontMetrics(&metrics);
277 return SkScalarToFloat(metrics.fAscent);
278 }
279
280 static jfloat descent(JNIEnv* env, jobject paint) {
281 NPE_CHECK_RETURN_ZERO(env, paint);
282 SkPaint::FontMetrics metrics;
283 (void)GraphicsJNI::getNativePaint(env, paint)->getFontMetrics(&metrics);
284 return SkScalarToFloat(metrics.fDescent);
285 }
286
287 static jfloat getFontMetrics(JNIEnv* env, jobject paint, jobject metricsObj) {
288 NPE_CHECK_RETURN_ZERO(env, paint);
289 SkPaint::FontMetrics metrics;
290 SkScalar spacing = GraphicsJNI::getNativePaint(env, paint)->getFontMetrics(&metrics);
291
292 if (metricsObj) {
293 SkASSERT(env->IsInstanceOf(metricsObj, gFontMetrics_class));
294 env->SetFloatField(metricsObj, gFontMetrics_fieldID.top, SkScalarToFloat(metrics.fTop));
295 env->SetFloatField(metricsObj, gFontMetrics_fieldID.ascent, SkScalarToFloat(metrics.fAscent));
296 env->SetFloatField(metricsObj, gFontMetrics_fieldID.descent, SkScalarToFloat(metrics.fDescent));
297 env->SetFloatField(metricsObj, gFontMetrics_fieldID.bottom, SkScalarToFloat(metrics.fBottom));
298 env->SetFloatField(metricsObj, gFontMetrics_fieldID.leading, SkScalarToFloat(metrics.fLeading));
299 }
300 return SkScalarToFloat(spacing);
301 }
302
303 static jint getFontMetricsInt(JNIEnv* env, jobject paint, jobject metricsObj) {
304 NPE_CHECK_RETURN_ZERO(env, paint);
305 SkPaint::FontMetrics metrics;
306
307 GraphicsJNI::getNativePaint(env, paint)->getFontMetrics(&metrics);
308 int ascent = SkScalarRound(metrics.fAscent);
309 int descent = SkScalarRound(metrics.fDescent);
310 int leading = SkScalarRound(metrics.fLeading);
311
312 if (metricsObj) {
313 SkASSERT(env->IsInstanceOf(metricsObj, gFontMetricsInt_class));
314 env->SetIntField(metricsObj, gFontMetricsInt_fieldID.top, SkScalarFloor(metrics.fTop));
315 env->SetIntField(metricsObj, gFontMetricsInt_fieldID.ascent, ascent);
316 env->SetIntField(metricsObj, gFontMetricsInt_fieldID.descent, descent);
317 env->SetIntField(metricsObj, gFontMetricsInt_fieldID.bottom, SkScalarCeil(metrics.fBottom));
318 env->SetIntField(metricsObj, gFontMetricsInt_fieldID.leading, leading);
319 }
320 return descent - ascent + leading;
321 }
322
323 static jfloat measureText_CII(JNIEnv* env, jobject jpaint, jcharArray text, int index, int count) {
324 NPE_CHECK_RETURN_ZERO(env, jpaint);
325 NPE_CHECK_RETURN_ZERO(env, text);
326
327 size_t textLength = env->GetArrayLength(text);
328
329 if ((index | count) < 0 || (size_t)(index + count) > textLength) {
330 doThrow(env, "java/lang/ArrayIndexOutOfBoundsException");
331 return 0;
332 }
333
334 const SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint);
335 const jchar* textArray = env->GetCharArrayElements(text, NULL);
336 // we double count, since measureText wants a byteLength
337 SkScalar width = paint->measureText(textArray + index, count << 1);
338 env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray),
339 JNI_ABORT);
340
341 return SkScalarToFloat(width);
342 }
343
344 static jfloat measureText_StringII(JNIEnv* env, jobject jpaint, jstring text, int start, int end) {
345 NPE_CHECK_RETURN_ZERO(env, jpaint);
346 NPE_CHECK_RETURN_ZERO(env, text);
347
348 SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint);
349 const jchar* textArray = env->GetStringChars(text, NULL);
350 size_t textLength = env->GetStringLength(text);
351
352 int count = end - start;
353 if ((start | count) < 0 || (size_t)count > textLength) {
354 doThrow(env, "java/lang/IndexOutOfBoundsException");
355 return 0;
356 }
357
358 jfloat width = SkScalarToFloat(paint->measureText(textArray + start, count << 1));
359 env->ReleaseStringChars(text, textArray);
360 return width;
361 }
362
363 static jfloat measureText_String(JNIEnv* env, jobject jpaint, jstring text) {
364 NPE_CHECK_RETURN_ZERO(env, jpaint);
365 NPE_CHECK_RETURN_ZERO(env, text);
366
367 SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint);
368 const jchar* textArray = env->GetStringChars(text, NULL);
369 size_t textLength = env->GetStringLength(text);
370
371 jfloat width = SkScalarToFloat(paint->measureText(textArray, textLength << 1));
372 env->ReleaseStringChars(text, textArray);
373 return width;
374 }
375
376 static int dotextwidths(JNIEnv* env, SkPaint* paint, const jchar text[], int count, jfloatArray widths) {
377 AutoJavaFloatArray autoWidths(env, widths, count);
378 jfloat* widthsArray = autoWidths.ptr();
379 SkScalar* scalarArray = (SkScalar*)widthsArray;
380
381 count = paint->getTextWidths(text, count << 1, scalarArray);
382 for (int i = 0; i < count; i++) {
383 widthsArray[i] = SkScalarToFloat(scalarArray[i]);
384 }
385 return count;
386 }
387
388 static int getTextWidths___CII_F(JNIEnv* env, jobject clazz, SkPaint* paint, jcharArray text, int index, int count, jfloatArray widths) {
389 const jchar* textArray = env->GetCharArrayElements(text, NULL);
390 count = dotextwidths(env, paint, textArray + index, count, widths);
391 env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray),
392 JNI_ABORT);
393 return count;
394 }
395
396 static int getTextWidths__StringII_F(JNIEnv* env, jobject clazz, SkPaint* paint, jstring text, int start, int end, jfloatArray widths) {
397 const jchar* textArray = env->GetStringChars(text, NULL);
398 int count = dotextwidths(env, paint, textArray + start, end - start, widths);
399 env->ReleaseStringChars(text, textArray);
400 return count;
401 }
Doug Felt0c702b82010-05-14 10:55:42 -0700402
Doug Feltf7cb1f72010-07-01 16:20:43 -0700403 static jfloat doTextRunAdvances(JNIEnv *env, SkPaint *paint, const jchar *text,
404 jint start, jint count, jint contextCount, jint flags,
Doug Felt0c702b82010-05-14 10:55:42 -0700405 jfloatArray advances, jint advancesIndex) {
406 jfloat advancesArray[count];
Doug Feltf7cb1f72010-07-01 16:20:43 -0700407 jfloat totalAdvance;
Doug Felt0c702b82010-05-14 10:55:42 -0700408
Doug Feltf7cb1f72010-07-01 16:20:43 -0700409 TextLayout::getTextRunAdvances(paint, text, start, count, contextCount, flags,
410 advancesArray, totalAdvance);
Doug Felt0c702b82010-05-14 10:55:42 -0700411
412 if (advances != NULL) {
413 env->SetFloatArrayRegion(advances, advancesIndex, count, advancesArray);
414 }
415 return totalAdvance;
416 }
417
418 static float getTextRunAdvances___CIIIII_FI(JNIEnv* env, jobject clazz, SkPaint* paint,
419 jcharArray text, jint index, jint count, jint contextIndex, jint contextCount,
420 jint flags, jfloatArray advances, jint advancesIndex) {
421 jchar* textArray = env->GetCharArrayElements(text, NULL);
422 jfloat result = doTextRunAdvances(env, paint, textArray + contextIndex,
423 index - contextIndex, count, contextCount, flags, advances, advancesIndex);
424 env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
425 return result;
426 }
427
428 static float getTextRunAdvances__StringIIIII_FI(JNIEnv* env, jobject clazz, SkPaint* paint,
429 jstring text, jint start, jint end, jint contextStart, jint contextEnd, jint flags,
430 jfloatArray advances, jint advancesIndex) {
431 const jchar* textArray = env->GetStringChars(text, NULL);
432 jfloat result = doTextRunAdvances(env, paint, textArray + contextStart,
433 start - contextStart, end - start, contextEnd - contextStart, flags, advances,
434 advancesIndex);
435 env->ReleaseStringChars(text, textArray);
436 return result;
437 }
438
439 static jint doTextRunCursor(JNIEnv *env, SkPaint* paint, const jchar *text, jint start,
440 jint count, jint flags, jint offset, jint opt) {
441 SkScalar scalarArray[count];
442 jchar buffer[count];
443
444 // this is where we'd call harfbuzz
445 // for now we just use ushape.c and widths returned from skia
446
447 int widths;
448 if (flags & 0x1) { // rtl, call arabic shaping in case
449 UErrorCode status = U_ZERO_ERROR;
450 // Use fixed length since we need to keep start and count valid
451 u_shapeArabic(text + start, count, buffer, count,
452 U_SHAPE_LENGTH_FIXED_SPACES_NEAR | U_SHAPE_TEXT_DIRECTION_LOGICAL |
453 U_SHAPE_LETTERS_SHAPE | U_SHAPE_X_LAMALEF_SUB_ALTERNATE, &status);
454 // we shouldn't fail unless there's an out of memory condition,
455 // in which case we're hosed anyway
456 for (int i = 0; i < count; ++i) {
457 if (buffer[i] == 0xffff) {
458 buffer[i] = 0x200b; // zero-width-space for skia
459 }
460 }
461 widths = paint->getTextWidths(buffer, count << 1, scalarArray);
462 } else {
463 widths = paint->getTextWidths(text + start, count << 1, scalarArray);
464 }
465
466 if (widths < count) {
467 // Skia operates on code points, not code units, so surrogate pairs return only one
468 // value. Expand the result so we have one value per UTF-16 code unit.
469
470 // Note, skia's getTextWidth gets confused if it encounters a surrogate pair,
471 // leaving the remaining widths zero. Not nice.
472 const jchar *chars = text + start;
473 for (int i = count, p = widths - 1; --i > p;) {
474 if (chars[i] >= 0xdc00 && chars[i] < 0xe000 &&
475 chars[i-1] >= 0xd800 && chars[i-1] < 0xdc00) {
476 scalarArray[i] = 0;
477 } else {
478 scalarArray[i] = scalarArray[--p];
479 }
480 }
481 }
482
483 jint pos = offset - start;
484 switch (opt) {
485 case AFTER:
486 if (pos < count) {
487 pos += 1;
488 }
489 // fall through
490 case AT_OR_AFTER:
491 while (pos < count && scalarArray[pos] == 0) {
492 ++pos;
493 }
494 break;
495 case BEFORE:
496 if (pos > 0) {
497 --pos;
498 }
499 // fall through
500 case AT_OR_BEFORE:
501 while (pos > 0 && scalarArray[pos] == 0) {
502 --pos;
503 }
504 break;
505 case AT:
506 default:
507 if (scalarArray[pos] == 0) {
508 pos = -1;
509 }
510 break;
511 }
512
513 if (pos != -1) {
514 pos += start;
515 }
516
517 return pos;
518 }
519
520 static jint getTextRunCursor___C(JNIEnv* env, jobject clazz, SkPaint* paint, jcharArray text,
521 jint contextStart, jint contextCount, jint flags, jint offset, jint cursorOpt) {
522 jchar* textArray = env->GetCharArrayElements(text, NULL);
523 jint result = doTextRunCursor(env, paint, textArray, contextStart, contextCount, flags,
524 offset, cursorOpt);
525 env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
526 return result;
527 }
528
529 static jint getTextRunCursor__String(JNIEnv* env, jobject clazz, SkPaint* paint, jstring text,
530 jint contextStart, jint contextEnd, jint flags, jint offset, jint cursorOpt) {
531 const jchar* textArray = env->GetStringChars(text, NULL);
532 jint result = doTextRunCursor(env, paint, textArray, contextStart,
533 contextEnd - contextStart, flags, offset, cursorOpt);
534 env->ReleaseStringChars(text, textArray);
535 return result;
536 }
537
Doug Feltf7cb1f72010-07-01 16:20:43 -0700538 static void getTextPath(JNIEnv* env, SkPaint* paint, const jchar* text, jint count,
539 jint bidiFlags, jfloat x, jfloat y, SkPath *path) {
540 TextLayout::getTextPath(paint, text, count, bidiFlags, x, y, path);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800541 }
Doug Feltf7cb1f72010-07-01 16:20:43 -0700542
543 static void getTextPath___C(JNIEnv* env, jobject clazz, SkPaint* paint, jint bidiFlags,
544 jcharArray text, int index, int count, jfloat x, jfloat y, SkPath* path) {
545 const jchar* textArray = env->GetCharArrayElements(text, NULL);
546 getTextPath(env, paint, textArray + index, count, bidiFlags, x, y, path);
547 env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), JNI_ABORT);
548 }
549
550 static void getTextPath__String(JNIEnv* env, jobject clazz, SkPaint* paint, jint bidiFlags,
551 jstring text, int start, int end, jfloat x, jfloat y, SkPath* path) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800552 const jchar* textArray = env->GetStringChars(text, NULL);
Doug Feltf7cb1f72010-07-01 16:20:43 -0700553 getTextPath(env, paint, textArray + start, end - start, bidiFlags, x, y, path);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800554 env->ReleaseStringChars(text, textArray);
555 }
Doug Feltf7cb1f72010-07-01 16:20:43 -0700556
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800557 static void setShadowLayer(JNIEnv* env, jobject jpaint, jfloat radius,
558 jfloat dx, jfloat dy, int color) {
559 NPE_CHECK_RETURN_VOID(env, jpaint);
560
561 SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint);
562 if (radius <= 0) {
563 paint->setLooper(NULL);
564 }
565 else {
566 paint->setLooper(new SkBlurDrawLooper(SkFloatToScalar(radius),
567 SkFloatToScalar(dx),
568 SkFloatToScalar(dy),
569 (SkColor)color))->unref();
570 }
571 }
572
573 static int breakText(JNIEnv* env, const SkPaint& paint, const jchar text[],
574 int count, float maxWidth, jfloatArray jmeasured,
575 SkPaint::TextBufferDirection tbd) {
576 SkASSERT(paint.getTextEncoding() == SkPaint::kUTF16_TextEncoding);
577
578 SkScalar measured;
579 size_t bytes = paint.breakText(text, count << 1,
580 SkFloatToScalar(maxWidth), &measured, tbd);
581 SkASSERT((bytes & 1) == 0);
582
583 if (jmeasured && env->GetArrayLength(jmeasured) > 0) {
584 AutoJavaFloatArray autoMeasured(env, jmeasured, 1);
585 jfloat* array = autoMeasured.ptr();
586 array[0] = SkScalarToFloat(measured);
587 }
588 return bytes >> 1;
589 }
590
591 static int breakTextC(JNIEnv* env, jobject jpaint, jcharArray jtext,
592 int index, int count, float maxWidth, jfloatArray jmeasuredWidth) {
593 NPE_CHECK_RETURN_ZERO(env, jpaint);
594 NPE_CHECK_RETURN_ZERO(env, jtext);
595
596 SkPaint::TextBufferDirection tbd;
597 if (count < 0) {
598 tbd = SkPaint::kBackward_TextBufferDirection;
599 count = -count;
600 }
601 else {
602 tbd = SkPaint::kForward_TextBufferDirection;
603 }
604
605 if ((index < 0) || (index + count > env->GetArrayLength(jtext))) {
606 doThrow(env, "java/lang/ArrayIndexOutOfBoundsException");
607 return 0;
608 }
609
610 SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint);
611 const jchar* text = env->GetCharArrayElements(jtext, NULL);
612 count = breakText(env, *paint, text + index, count, maxWidth,
613 jmeasuredWidth, tbd);
614 env->ReleaseCharArrayElements(jtext, const_cast<jchar*>(text),
615 JNI_ABORT);
616 return count;
617 }
618
619 static int breakTextS(JNIEnv* env, jobject jpaint, jstring jtext,
620 bool forwards, float maxWidth, jfloatArray jmeasuredWidth) {
621 NPE_CHECK_RETURN_ZERO(env, jpaint);
622 NPE_CHECK_RETURN_ZERO(env, jtext);
623
624 SkPaint::TextBufferDirection tbd = forwards ?
625 SkPaint::kForward_TextBufferDirection :
626 SkPaint::kBackward_TextBufferDirection;
627
628 SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint);
629 int count = env->GetStringLength(jtext);
630 const jchar* text = env->GetStringChars(jtext, NULL);
631 count = breakText(env, *paint, text, count, maxWidth,
632 jmeasuredWidth, tbd);
633 env->ReleaseStringChars(jtext, text);
634 return count;
635 }
636
637 static void doTextBounds(JNIEnv* env, const jchar* text, int count,
638 jobject bounds, const SkPaint& paint)
639 {
640 SkRect r;
641 SkIRect ir;
642
643 paint.measureText(text, count << 1, &r);
644 r.roundOut(&ir);
645 GraphicsJNI::irect_to_jrect(ir, env, bounds);
646 }
647
648 static void getStringBounds(JNIEnv* env, jobject, const SkPaint* paint,
649 jstring text, int start, int end, jobject bounds)
650 {
651 const jchar* textArray = env->GetStringChars(text, NULL);
652 doTextBounds(env, textArray + start, end - start, bounds, *paint);
653 env->ReleaseStringChars(text, textArray);
654 }
655
656 static void getCharArrayBounds(JNIEnv* env, jobject, const SkPaint* paint,
657 jcharArray text, int index, int count, jobject bounds)
658 {
659 const jchar* textArray = env->GetCharArrayElements(text, NULL);
660 doTextBounds(env, textArray + index, count, bounds, *paint);
661 env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray),
662 JNI_ABORT);
663 }
664
665};
666
667static JNINativeMethod methods[] = {
668 {"finalizer", "(I)V", (void*) SkPaintGlue::finalizer},
669 {"native_init","()I", (void*) SkPaintGlue::init},
670 {"native_initWithPaint","(I)I", (void*) SkPaintGlue::intiWithPaint},
671 {"native_reset","(I)V", (void*) SkPaintGlue::reset},
672 {"native_set","(II)V", (void*) SkPaintGlue::assign},
673 {"getFlags","()I", (void*) SkPaintGlue::getFlags},
674 {"setFlags","(I)V", (void*) SkPaintGlue::setFlags},
675 {"setAntiAlias","(Z)V", (void*) SkPaintGlue::setAntiAlias},
676 {"setSubpixelText","(Z)V", (void*) SkPaintGlue::setSubpixelText},
677 {"setLinearText","(Z)V", (void*) SkPaintGlue::setLinearText},
678 {"setUnderlineText","(Z)V", (void*) SkPaintGlue::setUnderlineText},
679 {"setStrikeThruText","(Z)V", (void*) SkPaintGlue::setStrikeThruText},
680 {"setFakeBoldText","(Z)V", (void*) SkPaintGlue::setFakeBoldText},
681 {"setFilterBitmap","(Z)V", (void*) SkPaintGlue::setFilterBitmap},
682 {"setDither","(Z)V", (void*) SkPaintGlue::setDither},
683 {"native_getStyle","(I)I", (void*) SkPaintGlue::getStyle},
684 {"native_setStyle","(II)V", (void*) SkPaintGlue::setStyle},
685 {"getColor","()I", (void*) SkPaintGlue::getColor},
686 {"setColor","(I)V", (void*) SkPaintGlue::setColor},
687 {"getAlpha","()I", (void*) SkPaintGlue::getAlpha},
688 {"setAlpha","(I)V", (void*) SkPaintGlue::setAlpha},
689 {"getStrokeWidth","()F", (void*) SkPaintGlue::getStrokeWidth},
690 {"setStrokeWidth","(F)V", (void*) SkPaintGlue::setStrokeWidth},
691 {"getStrokeMiter","()F", (void*) SkPaintGlue::getStrokeMiter},
692 {"setStrokeMiter","(F)V", (void*) SkPaintGlue::setStrokeMiter},
693 {"native_getStrokeCap","(I)I", (void*) SkPaintGlue::getStrokeCap},
694 {"native_setStrokeCap","(II)V", (void*) SkPaintGlue::setStrokeCap},
695 {"native_getStrokeJoin","(I)I", (void*) SkPaintGlue::getStrokeJoin},
696 {"native_setStrokeJoin","(II)V", (void*) SkPaintGlue::setStrokeJoin},
697 {"native_getFillPath","(III)Z", (void*) SkPaintGlue::getFillPath},
698 {"native_setShader","(II)I", (void*) SkPaintGlue::setShader},
699 {"native_setColorFilter","(II)I", (void*) SkPaintGlue::setColorFilter},
700 {"native_setXfermode","(II)I", (void*) SkPaintGlue::setXfermode},
701 {"native_setPathEffect","(II)I", (void*) SkPaintGlue::setPathEffect},
702 {"native_setMaskFilter","(II)I", (void*) SkPaintGlue::setMaskFilter},
703 {"native_setTypeface","(II)I", (void*) SkPaintGlue::setTypeface},
704 {"native_setRasterizer","(II)I", (void*) SkPaintGlue::setRasterizer},
705 {"native_getTextAlign","(I)I", (void*) SkPaintGlue::getTextAlign},
706 {"native_setTextAlign","(II)V", (void*) SkPaintGlue::setTextAlign},
707 {"getTextSize","()F", (void*) SkPaintGlue::getTextSize},
708 {"setTextSize","(F)V", (void*) SkPaintGlue::setTextSize},
709 {"getTextScaleX","()F", (void*) SkPaintGlue::getTextScaleX},
710 {"setTextScaleX","(F)V", (void*) SkPaintGlue::setTextScaleX},
711 {"getTextSkewX","()F", (void*) SkPaintGlue::getTextSkewX},
712 {"setTextSkewX","(F)V", (void*) SkPaintGlue::setTextSkewX},
713 {"ascent","()F", (void*) SkPaintGlue::ascent},
714 {"descent","()F", (void*) SkPaintGlue::descent},
715 {"getFontMetrics", "(Landroid/graphics/Paint$FontMetrics;)F", (void*)SkPaintGlue::getFontMetrics},
716 {"getFontMetricsInt", "(Landroid/graphics/Paint$FontMetricsInt;)I", (void*)SkPaintGlue::getFontMetricsInt},
Dianne Hackbornafa78962009-09-28 17:33:54 -0700717 {"native_measureText","([CII)F", (void*) SkPaintGlue::measureText_CII},
718 {"native_measureText","(Ljava/lang/String;)F", (void*) SkPaintGlue::measureText_String},
719 {"native_measureText","(Ljava/lang/String;II)F", (void*) SkPaintGlue::measureText_StringII},
720 {"native_breakText","([CIIF[F)I", (void*) SkPaintGlue::breakTextC},
721 {"native_breakText","(Ljava/lang/String;ZF[F)I", (void*) SkPaintGlue::breakTextS},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800722 {"native_getTextWidths","(I[CII[F)I", (void*) SkPaintGlue::getTextWidths___CII_F},
723 {"native_getTextWidths","(ILjava/lang/String;II[F)I", (void*) SkPaintGlue::getTextWidths__StringII_F},
Doug Felt0c702b82010-05-14 10:55:42 -0700724 {"native_getTextRunAdvances","(I[CIIIII[FI)F", (void*)
725 SkPaintGlue::getTextRunAdvances___CIIIII_FI},
726 {"native_getTextRunAdvances","(ILjava/lang/String;IIIII[FI)F",
727 (void*) SkPaintGlue::getTextRunAdvances__StringIIIII_FI},
728 {"native_getTextRunCursor", "(I[CIIIII)I", (void*) SkPaintGlue::getTextRunCursor___C},
729 {"native_getTextRunCursor", "(ILjava/lang/String;IIIII)I",
730 (void*) SkPaintGlue::getTextRunCursor__String},
Doug Feltf7cb1f72010-07-01 16:20:43 -0700731 {"native_getTextPath","(II[CIIFFI)V", (void*) SkPaintGlue::getTextPath___C},
732 {"native_getTextPath","(IILjava/lang/String;IIFFI)V", (void*) SkPaintGlue::getTextPath__String},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800733 {"nativeGetStringBounds", "(ILjava/lang/String;IILandroid/graphics/Rect;)V",
734 (void*) SkPaintGlue::getStringBounds },
735 {"nativeGetCharArrayBounds", "(I[CIILandroid/graphics/Rect;)V",
736 (void*) SkPaintGlue::getCharArrayBounds },
Romain Guy1e45aae2010-08-13 19:39:53 -0700737 {"nSetShadowLayer", "(FFFI)V", (void*)SkPaintGlue::setShadowLayer}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800738};
739
740static jfieldID req_fieldID(jfieldID id) {
741 SkASSERT(id);
742 return id;
743}
744
745int register_android_graphics_Paint(JNIEnv* env) {
746 gFontMetrics_class = env->FindClass("android/graphics/Paint$FontMetrics");
747 SkASSERT(gFontMetrics_class);
748 gFontMetrics_class = (jclass)env->NewGlobalRef(gFontMetrics_class);
749
750 gFontMetrics_fieldID.top = req_fieldID(env->GetFieldID(gFontMetrics_class, "top", "F"));
751 gFontMetrics_fieldID.ascent = req_fieldID(env->GetFieldID(gFontMetrics_class, "ascent", "F"));
752 gFontMetrics_fieldID.descent = req_fieldID(env->GetFieldID(gFontMetrics_class, "descent", "F"));
753 gFontMetrics_fieldID.bottom = req_fieldID(env->GetFieldID(gFontMetrics_class, "bottom", "F"));
754 gFontMetrics_fieldID.leading = req_fieldID(env->GetFieldID(gFontMetrics_class, "leading", "F"));
755
756 gFontMetricsInt_class = env->FindClass("android/graphics/Paint$FontMetricsInt");
757 SkASSERT(gFontMetricsInt_class);
758 gFontMetricsInt_class = (jclass)env->NewGlobalRef(gFontMetricsInt_class);
759
760 gFontMetricsInt_fieldID.top = req_fieldID(env->GetFieldID(gFontMetricsInt_class, "top", "I"));
761 gFontMetricsInt_fieldID.ascent = req_fieldID(env->GetFieldID(gFontMetricsInt_class, "ascent", "I"));
762 gFontMetricsInt_fieldID.descent = req_fieldID(env->GetFieldID(gFontMetricsInt_class, "descent", "I"));
763 gFontMetricsInt_fieldID.bottom = req_fieldID(env->GetFieldID(gFontMetricsInt_class, "bottom", "I"));
764 gFontMetricsInt_fieldID.leading = req_fieldID(env->GetFieldID(gFontMetricsInt_class, "leading", "I"));
765
766 int result = AndroidRuntime::registerNativeMethods(env, "android/graphics/Paint", methods,
767 sizeof(methods) / sizeof(methods[0]));
768 return result;
769}
770
771}