blob: e8079edda22bc59af948e68bef8d4a8b9c9dbbca [file] [log] [blame]
Xavier Ducrohetef44aea2010-10-28 11:52:00 -07001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.graphics;
18
19import com.android.layoutlib.bridge.DelegateManager;
20
21import android.graphics.Paint.FontMetrics;
22import android.graphics.Paint.FontMetricsInt;
23
24import java.awt.Font;
25import java.awt.Toolkit;
26import java.awt.font.FontRenderContext;
27import java.awt.geom.AffineTransform;
28import java.util.ArrayList;
29import java.util.Collections;
30import java.util.List;
31
32/**
33 * Delegate implementing the native methods of android.graphics.Paint
34 *
35 * Through the layoutlib_create tool, the original native methods of Paint have been replaced
36 * by calls to methods of the same name in this delegate class.
37 *
38 * This class behaves like the original native implementation, but in Java, keeping previously
39 * native data into its own objects and mapping them to int that are sent back and forth between
40 * it and the original Paint class.
41 *
42 * @see DelegateManager
43 *
44 */
45public class Paint_Delegate {
46
47 /**
48 * Class associating a {@link Font} and it's {@link java.awt.FontMetrics}.
49 */
50 public static final class FontInfo {
51 Font mFont;
52 java.awt.FontMetrics mMetrics;
53 }
54
55 // ---- delegate manager ----
56 private static final DelegateManager<Paint_Delegate> sManager =
57 new DelegateManager<Paint_Delegate>();
58
59 // ---- delegate helper data ----
60 private List<FontInfo> mFonts;
61 private final FontRenderContext mFontContext = new FontRenderContext(
62 new AffineTransform(), true, true);
63
64 // ---- delegate data ----
65 private int mFlags;
66 private int mColor;
67 private int mStyle;
68 private int mCap;
69 private int mJoin;
70 private int mAlign;
71 private int mTypeface;
72 private float mStrokeWidth;
73 private float mStrokeMiter;
74 private float mTextSize;
75 private float mTextScaleX;
76 private float mTextSkewX;
77
78
79 // ---- Public Helper methods ----
80
81 /**
82 * Returns the list of {@link Font} objects. The first item is the main font, the rest
83 * are fall backs for characters not present in the main font.
84 */
85 public List<FontInfo> getFonts() {
86 return mFonts;
87 }
88
89
90 // ---- native methods ----
91
92 /*package*/ static int getFlags(Paint thisPaint) {
93 // get the delegate from the native int.
94 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
95 if (delegate == null) {
96 assert false;
97 return 0;
98 }
99
100 return delegate.mFlags;
101 }
102
103 /*package*/ static void setFlags(Paint thisPaint, int flags) {
104 // get the delegate from the native int.
105 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
106 if (delegate == null) {
107 assert false;
108 return;
109 }
110
111 delegate.mFlags = flags;
112 }
113
114 /*package*/ static void setFilterBitmap(Paint thisPaint, boolean filter) {
115 // FIXME
116 throw new UnsupportedOperationException();
117 }
118
119 /*package*/ static void setAntiAlias(Paint thisPaint, boolean aa) {
120 setFlag(thisPaint, Paint.ANTI_ALIAS_FLAG, aa);
121 }
122
123 /*package*/ static void setSubpixelText(Paint thisPaint, boolean subpixelText) {
124 setFlag(thisPaint, Paint.SUBPIXEL_TEXT_FLAG, subpixelText);
125 }
126
127 /*package*/ static void setUnderlineText(Paint thisPaint, boolean underlineText) {
128 setFlag(thisPaint, Paint.UNDERLINE_TEXT_FLAG, underlineText);
129 }
130
131 /*package*/ static void setStrikeThruText(Paint thisPaint, boolean strikeThruText) {
132 setFlag(thisPaint, Paint.STRIKE_THRU_TEXT_FLAG, strikeThruText);
133 }
134
135 /*package*/ static void setFakeBoldText(Paint thisPaint, boolean fakeBoldText) {
136 setFlag(thisPaint, Paint.FAKE_BOLD_TEXT_FLAG, fakeBoldText);
137 }
138
139 /*package*/ static void setDither(Paint thisPaint, boolean dither) {
140 setFlag(thisPaint, Paint.DITHER_FLAG, dither);
141 }
142
143 /*package*/ static void setLinearText(Paint thisPaint, boolean linearText) {
144 setFlag(thisPaint, Paint.LINEAR_TEXT_FLAG, linearText);
145 }
146
147 /*package*/ static int getColor(Paint thisPaint) {
148 // get the delegate from the native int.
149 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
150 if (delegate == null) {
151 assert false;
152 return 0;
153 }
154
155 return delegate.mColor;
156 }
157
158 /*package*/ static void setColor(Paint thisPaint, int color) {
159 // get the delegate from the native int.
160 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
161 if (delegate == null) {
162 assert false;
163 return;
164 }
165
166 delegate.mColor = color;
167 }
168
169 /*package*/ static int getAlpha(Paint thisPaint) {
170 // get the delegate from the native int.
171 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
172 if (delegate == null) {
173 assert false;
174 return 0;
175 }
176
177 return delegate.mColor >>> 24;
178 }
179
180 /*package*/ static void setAlpha(Paint thisPaint, int a) {
181 // get the delegate from the native int.
182 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
183 if (delegate == null) {
184 assert false;
185 return;
186 }
187
188 delegate.mColor = (a << 24) | (delegate.mColor & 0x00FFFFFF);
189 }
190
191 /*package*/ static float getStrokeWidth(Paint thisPaint) {
192 // get the delegate from the native int.
193 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
194 if (delegate == null) {
195 assert false;
196 return 1.f;
197 }
198
199 return delegate.mStrokeWidth;
200 }
201
202 /*package*/ static void setStrokeWidth(Paint thisPaint, float width) {
203 // get the delegate from the native int.
204 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
205 if (delegate == null) {
206 assert false;
207 return;
208 }
209
210 delegate.mStrokeWidth = width;
211 }
212
213 /*package*/ static float getStrokeMiter(Paint thisPaint) {
214 // get the delegate from the native int.
215 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
216 if (delegate == null) {
217 assert false;
218 return 1.f;
219 }
220
221 return delegate.mStrokeMiter;
222 }
223
224 /*package*/ static void setStrokeMiter(Paint thisPaint, float miter) {
225 // get the delegate from the native int.
226 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
227 if (delegate == null) {
228 assert false;
229 return;
230 }
231
232 delegate.mStrokeMiter = miter;
233 }
234
235 /*package*/ static void nSetShadowLayer(Paint thisPaint, float radius, float dx, float dy,
236 int color) {
237 // FIXME
238 throw new UnsupportedOperationException();
239 }
240
241 /*package*/ static float getTextSize(Paint thisPaint) {
242 // get the delegate from the native int.
243 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
244 if (delegate == null) {
245 assert false;
246 return 1.f;
247 }
248
249 return delegate.mTextSize;
250 }
251
252 /*package*/ static void setTextSize(Paint thisPaint, float textSize) {
253 // get the delegate from the native int.
254 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
255 if (delegate == null) {
256 assert false;
257 return;
258 }
259
260 delegate.mTextSize = textSize;
261 }
262
263 /*package*/ static float getTextScaleX(Paint thisPaint) {
264 // get the delegate from the native int.
265 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
266 if (delegate == null) {
267 assert false;
268 return 1.f;
269 }
270
271 return delegate.mTextScaleX;
272 }
273
274 /*package*/ static void setTextScaleX(Paint thisPaint, float scaleX) {
275 // get the delegate from the native int.
276 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
277 if (delegate == null) {
278 assert false;
279 return;
280 }
281
282 delegate.mTextScaleX = scaleX;
283 }
284
285 /*package*/ static float getTextSkewX(Paint thisPaint) {
286 // get the delegate from the native int.
287 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
288 if (delegate == null) {
289 assert false;
290 return 1.f;
291 }
292
293 return delegate.mTextSkewX;
294 }
295
296 /*package*/ static void setTextSkewX(Paint thisPaint, float skewX) {
297 // get the delegate from the native int.
298 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
299 if (delegate == null) {
300 assert false;
301 return;
302 }
303
304 delegate.mTextSkewX = skewX;
305 }
306
307 /*package*/ static float ascent(Paint thisPaint) {
308 // FIXME
309 throw new UnsupportedOperationException();
310 }
311
312 /*package*/ static float descent(Paint thisPaint) {
313 // FIXME
314 throw new UnsupportedOperationException();
315 }
316
317 /*package*/ static float getFontMetrics(Paint thisPaint, FontMetrics metrics) {
318 // FIXME
319 throw new UnsupportedOperationException();
320 }
321
322 /*package*/ static int getFontMetricsInt(Paint thisPaint, FontMetricsInt fmi) {
323 // FIXME
324 throw new UnsupportedOperationException();
325 }
326
327 /*package*/ static float native_measureText(Paint thisPaint, char[] text, int index,
328 int count) {
329 // WARNING: the logic in this method is similar to Canvas.drawText.
330 // Any change to this method should be reflected in Canvas.drawText
331
332 // get the delegate
333 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
334 if (delegate == null) {
335 assert false;
336 return 0;
337 }
338
339 if (delegate.mFonts.size() > 0) {
340 FontInfo mainFont = delegate.mFonts.get(0);
341 int i = index;
342 int lastIndex = index + count;
343 float total = 0f;
344 while (i < lastIndex) {
345 // always start with the main font.
346 int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex);
347 if (upTo == -1) {
348 // shortcut to exit
349 return total + mainFont.mMetrics.charsWidth(text, i, lastIndex - i);
350 } else if (upTo > 0) {
351 total += mainFont.mMetrics.charsWidth(text, i, upTo - i);
352 i = upTo;
353 // don't call continue at this point. Since it is certain the main font
354 // cannot display the font a index upTo (now ==i), we move on to the
355 // fallback fonts directly.
356 }
357
358 // no char supported, attempt to read the next char(s) with the
359 // fallback font. In this case we only test the first character
360 // and then go back to test with the main font.
361 // Special test for 2-char characters.
362 boolean foundFont = false;
363 for (int f = 1 ; f < delegate.mFonts.size() ; f++) {
364 FontInfo fontInfo = delegate.mFonts.get(f);
365
366 // need to check that the font can display the character. We test
367 // differently if the char is a high surrogate.
368 int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
369 upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount);
370 if (upTo == -1) {
371 total += fontInfo.mMetrics.charsWidth(text, i, charCount);
372 i += charCount;
373 foundFont = true;
374 break;
375
376 }
377 }
378
379 // in case no font can display the char, measure it with the main font.
380 if (foundFont == false) {
381 int size = Character.isHighSurrogate(text[i]) ? 2 : 1;
382 total += mainFont.mMetrics.charsWidth(text, i, size);
383 i += size;
384 }
385 }
386 }
387
388 return 0;
389 }
390
391 /*package*/ static float native_measureText(Paint thisPaint, String text, int start, int end) {
392 return native_measureText(thisPaint, text.toCharArray(), start, end - start);
393 }
394
395 /*package*/ static float native_measureText(Paint thisPaint, String text) {
396 return native_measureText(thisPaint, text.toCharArray(), 0, text.length());
397 }
398
399 /*package*/ static int native_breakText(Paint thisPaint, char[] text, int index, int count,
400 float maxWidth, float[] measuredWidth) {
401 // FIXME
402 throw new UnsupportedOperationException();
403 }
404
405 /*package*/ static int native_breakText(Paint thisPaint, String text, boolean measureForwards,
406 float maxWidth, float[] measuredWidth) {
407 // FIXME
408 throw new UnsupportedOperationException();
409 }
410
411
412 /*package*/ static int native_init() {
413 Paint_Delegate newDelegate = new Paint_Delegate();
414 return sManager.addDelegate(newDelegate);
415 }
416
417 /*package*/ static int native_initWithPaint(int paint) {
418 // get the delegate from the native int.
419 Paint_Delegate delegate = sManager.getDelegate(paint);
420 if (delegate == null) {
421 assert false;
422 return 0;
423 }
424
425 Paint_Delegate newDelegate = new Paint_Delegate(delegate);
426 return sManager.addDelegate(newDelegate);
427 }
428
429 /*package*/ static void native_reset(int native_object) {
430 // get the delegate from the native int.
431 Paint_Delegate delegate = sManager.getDelegate(native_object);
432 if (delegate == null) {
433 assert false;
434 return;
435 }
436
437 delegate.reset();
438 }
439
440 /*package*/ static void native_set(int native_dst, int native_src) {
441 // get the delegate from the native int.
442 Paint_Delegate delegate_dst = sManager.getDelegate(native_dst);
443 if (delegate_dst == null) {
444 assert false;
445 return;
446 }
447
448 // get the delegate from the native int.
449 Paint_Delegate delegate_src = sManager.getDelegate(native_src);
450 if (delegate_src == null) {
451 assert false;
452 return;
453 }
454
455 delegate_dst.set(delegate_src);
456 }
457
458 /*package*/ static int native_getStyle(int native_object) {
459 // get the delegate from the native int.
460 Paint_Delegate delegate = sManager.getDelegate(native_object);
461 if (delegate == null) {
462 assert false;
463 return 0;
464 }
465
466 return delegate.mStyle;
467 }
468
469 /*package*/ static void native_setStyle(int native_object, int style) {
470 // get the delegate from the native int.
471 Paint_Delegate delegate = sManager.getDelegate(native_object);
472 if (delegate == null) {
473 assert false;
474 return;
475 }
476
477 delegate.mStyle = style;
478 }
479
480 /*package*/ static int native_getStrokeCap(int native_object) {
481 // get the delegate from the native int.
482 Paint_Delegate delegate = sManager.getDelegate(native_object);
483 if (delegate == null) {
484 assert false;
485 return 0;
486 }
487
488 return delegate.mCap;
489 }
490
491 /*package*/ static void native_setStrokeCap(int native_object, int cap) {
492 // get the delegate from the native int.
493 Paint_Delegate delegate = sManager.getDelegate(native_object);
494 if (delegate == null) {
495 assert false;
496 return;
497 }
498
499 delegate.mCap = cap;
500 }
501
502 /*package*/ static int native_getStrokeJoin(int native_object) {
503 // get the delegate from the native int.
504 Paint_Delegate delegate = sManager.getDelegate(native_object);
505 if (delegate == null) {
506 assert false;
507 return 0;
508 }
509
510 return delegate.mJoin;
511 }
512
513 /*package*/ static void native_setStrokeJoin(int native_object, int join) {
514 // get the delegate from the native int.
515 Paint_Delegate delegate = sManager.getDelegate(native_object);
516 if (delegate == null) {
517 assert false;
518 return;
519 }
520
521 delegate.mJoin = join;
522 }
523
524 /*package*/ static boolean native_getFillPath(int native_object, int src, int dst) {
525 // FIXME
526 throw new UnsupportedOperationException();
527 }
528
529 /*package*/ static int native_setShader(int native_object, int shader) {
530 // FIXME
531 throw new UnsupportedOperationException();
532 }
533
534 /*package*/ static int native_setColorFilter(int native_object, int filter) {
535 // FIXME
536 throw new UnsupportedOperationException();
537 }
538
539 /*package*/ static int native_setXfermode(int native_object, int xfermode) {
540 // FIXME
541 throw new UnsupportedOperationException();
542 }
543
544 /*package*/ static int native_setPathEffect(int native_object, int effect) {
545 // FIXME
546 throw new UnsupportedOperationException();
547 }
548
549 /*package*/ static int native_setMaskFilter(int native_object, int maskfilter) {
550 // FIXME
551 throw new UnsupportedOperationException();
552 }
553
554 /*package*/ static int native_setTypeface(int native_object, int typeface) {
555 // get the delegate from the native int.
556 Paint_Delegate delegate = sManager.getDelegate(native_object);
557 if (delegate == null) {
558 assert false;
559 return 0;
560 }
561
562 return delegate.mTypeface = typeface;
563 }
564
565 /*package*/ static int native_setRasterizer(int native_object, int rasterizer) {
566 // FIXME
567 throw new UnsupportedOperationException();
568 }
569
570
571 /*package*/ static int native_getTextAlign(int native_object) {
572 // get the delegate from the native int.
573 Paint_Delegate delegate = sManager.getDelegate(native_object);
574 if (delegate == null) {
575 assert false;
576 return 0;
577 }
578
579 return delegate.mAlign;
580 }
581
582 /*package*/ static void native_setTextAlign(int native_object, int align) {
583 // get the delegate from the native int.
584 Paint_Delegate delegate = sManager.getDelegate(native_object);
585 if (delegate == null) {
586 assert false;
587 return;
588 }
589
590 delegate.mAlign = align;
591 }
592
593 /*package*/ static float native_getFontMetrics(int native_paint, FontMetrics metrics) {
594 // FIXME
595 throw new UnsupportedOperationException();
596 }
597
598 /*package*/ static int native_getTextWidths(int native_object, char[] text, int index,
599 int count, float[] widths) {
600 // FIXME
601 throw new UnsupportedOperationException();
602 }
603
604 /*package*/ static int native_getTextWidths(int native_object, String text, int start,
605 int end, float[] widths) {
606 // FIXME
607 throw new UnsupportedOperationException();
608 }
609
610 /*package*/ static float native_getTextRunAdvances(int native_object,
611 char[] text, int index, int count, int contextIndex, int contextCount,
612 int flags, float[] advances, int advancesIndex) {
613 // FIXME
614 throw new UnsupportedOperationException();
615 }
616
617 /*package*/ static float native_getTextRunAdvances(int native_object,
618 String text, int start, int end, int contextStart, int contextEnd,
619 int flags, float[] advances, int advancesIndex) {
620 // FIXME
621 throw new UnsupportedOperationException();
622 }
623
624 /*package*/ static int native_getTextRunCursor(Paint thisPaint, int native_object, char[] text,
625 int contextStart, int contextLength, int flags, int offset, int cursorOpt) {
626 // FIXME
627 throw new UnsupportedOperationException();
628 }
629
630 /*package*/ static int native_getTextRunCursor(Paint thisPaint, int native_object, String text,
631 int contextStart, int contextEnd, int flags, int offset, int cursorOpt) {
632 // FIXME
633 throw new UnsupportedOperationException();
634 }
635
636 /*package*/ static void native_getTextPath(int native_object, int bidiFlags,
637 char[] text, int index, int count, float x, float y, int path) {
638 // FIXME
639 throw new UnsupportedOperationException();
640 }
641
642 /*package*/ static void native_getTextPath(int native_object, int bidiFlags,
643 String text, int start, int end, float x, float y, int path) {
644 // FIXME
645 throw new UnsupportedOperationException();
646 }
647
648 /*package*/ static void nativeGetStringBounds(int nativePaint, String text, int start,
649 int end, Rect bounds) {
650 // FIXME
651 throw new UnsupportedOperationException();
652 }
653
654 /*package*/ static void nativeGetCharArrayBounds(int nativePaint, char[] text, int index,
655 int count, Rect bounds) {
656 // FIXME
657 throw new UnsupportedOperationException();
658 }
659
660 /*package*/ static void finalizer(int nativePaint) {
661 sManager.removeDelegate(nativePaint);
662 }
663
664 // ---- Private delegate/helper methods ----
665
666 private Paint_Delegate() {
667 reset();
668
669 mTypeface = Typeface.sDefaults[0].native_instance;
670 updateFontObject();
671 }
672
673 private Paint_Delegate(Paint_Delegate paint) {
674 set(paint);
675 updateFontObject();
676 }
677
678 private void set(Paint_Delegate paint) {
679 mFlags = paint.mFlags;
680 mColor = paint.mColor;
681 mStyle = paint.mStyle;
682 mCap = paint.mCap;
683 mJoin = paint.mJoin;
684 mAlign = paint.mAlign;
685 mTypeface = paint.mTypeface;
686 mStrokeWidth = paint.mStrokeWidth;
687 mStrokeMiter = paint.mStrokeMiter;
688 mTextSize = paint.mTextSize;
689 mTextScaleX = paint.mTextScaleX;
690 mTextSkewX = paint.mTextSkewX;
691 }
692
693 private void reset() {
694 mFlags = Paint.DEFAULT_PAINT_FLAGS;
695 mColor = 0;
696 mStyle = 0;
697 mCap = 0;
698 mJoin = 0;
699 mAlign = 0;
700 mTypeface = 0;
701 mStrokeWidth = 1.f;
702 mStrokeMiter = 2.f;
703 mTextSize = 20.f;
704 mTextScaleX = 1.f;
705 mTextSkewX = 0.f;
706 }
707
708 /**
709 * Update the {@link Font} object from the typeface, text size and scaling
710 */
711 private void updateFontObject() {
712 if (mTypeface != 0) {
713 // Get the fonts from the TypeFace object.
714 List<Font> fonts = Typeface_Delegate.getFonts(mTypeface);
715
716 // create new font objects as well as FontMetrics, based on the current text size
717 // and skew info.
718 ArrayList<FontInfo> infoList = new ArrayList<FontInfo>(fonts.size());
719 for (Font font : fonts) {
720 FontInfo info = new FontInfo();
721 info.mFont = font.deriveFont(mTextSize);
722 if (mTextScaleX != 1.0 || mTextSkewX != 0) {
723 // TODO: support skew
724 info.mFont = info.mFont.deriveFont(new AffineTransform(
725 mTextScaleX, mTextSkewX, 0, 0, 1, 0));
726 }
727 info.mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(info.mFont);
728
729 infoList.add(info);
730 }
731
732 mFonts = Collections.unmodifiableList(infoList);
733 }
734 }
735
736 private static void setFlag(Paint thisPaint, int flagMask, boolean flagValue) {
737 // get the delegate from the native int.
738 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
739 if (delegate == null) {
740 assert false;
741 return;
742 }
743
744 if (flagValue) {
745 delegate.mFlags |= flagMask;
746 } else {
747 delegate.mFlags &= ~flagMask;
748 }
749 }
750}