1 /* 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 17 package android.graphics; 18 19 import com.android.ide.common.rendering.api.LayoutLog; 20 import com.android.layoutlib.bridge.Bridge; 21 import com.android.layoutlib.bridge.impl.DelegateManager; 22 import com.android.tools.layoutlib.annotations.LayoutlibDelegate; 23 24 import android.graphics.Paint.FontMetrics; 25 import android.graphics.Paint.FontMetricsInt; 26 import android.text.TextUtils; 27 28 import java.awt.BasicStroke; 29 import java.awt.Font; 30 import java.awt.Shape; 31 import java.awt.Stroke; 32 import java.awt.Toolkit; 33 import java.awt.font.FontRenderContext; 34 import java.awt.geom.AffineTransform; 35 import java.util.ArrayList; 36 import java.util.Collections; 37 import java.util.List; 38 import java.util.Locale; 39 40 /** 41 * Delegate implementing the native methods of android.graphics.Paint 42 * 43 * Through the layoutlib_create tool, the original native methods of Paint have been replaced 44 * by calls to methods of the same name in this delegate class. 45 * 46 * This class behaves like the original native implementation, but in Java, keeping previously 47 * native data into its own objects and mapping them to int that are sent back and forth between 48 * it and the original Paint class. 49 * 50 * @see DelegateManager 51 * 52 */ 53 public class Paint_Delegate { 54 55 /** 56 * Class associating a {@link Font} and it's {@link java.awt.FontMetrics}. 57 */ 58 /*package*/ static final class FontInfo { 59 Font mFont; 60 java.awt.FontMetrics mMetrics; 61 } 62 63 // ---- delegate manager ---- 64 private static final DelegateManager<Paint_Delegate> sManager = 65 new DelegateManager<Paint_Delegate>(Paint_Delegate.class); 66 67 // ---- delegate helper data ---- 68 private List<FontInfo> mFonts; 69 private final FontRenderContext mFontContext = new FontRenderContext( 70 new AffineTransform(), true, true); 71 72 // ---- delegate data ---- 73 private int mFlags; 74 private int mColor; 75 private int mStyle; 76 private int mCap; 77 private int mJoin; 78 private int mTextAlign; 79 private Typeface_Delegate mTypeface; 80 private float mStrokeWidth; 81 private float mStrokeMiter; 82 private float mTextSize; 83 private float mTextScaleX; 84 private float mTextSkewX; 85 private int mHintingMode = Paint.HINTING_ON; 86 87 private Xfermode_Delegate mXfermode; 88 private ColorFilter_Delegate mColorFilter; 89 private Shader_Delegate mShader; 90 private PathEffect_Delegate mPathEffect; 91 private MaskFilter_Delegate mMaskFilter; 92 private Rasterizer_Delegate mRasterizer; 93 94 private Locale mLocale = Locale.getDefault(); 95 96 97 // ---- Public Helper methods ---- 98 99 public static Paint_Delegate getDelegate(int native_paint) { 100 return sManager.getDelegate(native_paint); 101 } 102 103 /** 104 * Returns the list of {@link Font} objects. The first item is the main font, the rest 105 * are fall backs for characters not present in the main font. 106 */ 107 public List<FontInfo> getFonts() { 108 return mFonts; 109 } 110 111 public boolean isAntiAliased() { 112 return (mFlags & Paint.ANTI_ALIAS_FLAG) != 0; 113 } 114 115 public boolean isFilterBitmap() { 116 return (mFlags & Paint.FILTER_BITMAP_FLAG) != 0; 117 } 118 119 public int getStyle() { 120 return mStyle; 121 } 122 123 public int getColor() { 124 return mColor; 125 } 126 127 public int getAlpha() { 128 return mColor >>> 24; 129 } 130 131 public void setAlpha(int alpha) { 132 mColor = (alpha << 24) | (mColor & 0x00FFFFFF); 133 } 134 135 public int getTextAlign() { 136 return mTextAlign; 137 } 138 139 public float getStrokeWidth() { 140 return mStrokeWidth; 141 } 142 143 /** 144 * returns the value of stroke miter needed by the java api. 145 */ 146 public float getJavaStrokeMiter() { 147 float miter = mStrokeMiter * mStrokeWidth; 148 if (miter < 1.f) { 149 miter = 1.f; 150 } 151 return miter; 152 } 153 154 public int getJavaCap() { 155 switch (Paint.sCapArray[mCap]) { 156 case BUTT: 157 return BasicStroke.CAP_BUTT; 158 case ROUND: 159 return BasicStroke.CAP_ROUND; 160 default: 161 case SQUARE: 162 return BasicStroke.CAP_SQUARE; 163 } 164 } 165 166 public int getJavaJoin() { 167 switch (Paint.sJoinArray[mJoin]) { 168 default: 169 case MITER: 170 return BasicStroke.JOIN_MITER; 171 case ROUND: 172 return BasicStroke.JOIN_ROUND; 173 case BEVEL: 174 return BasicStroke.JOIN_BEVEL; 175 } 176 } 177 178 public Stroke getJavaStroke() { 179 if (mPathEffect != null) { 180 if (mPathEffect.isSupported()) { 181 Stroke stroke = mPathEffect.getStroke(this); 182 assert stroke != null; 183 if (stroke != null) { 184 return stroke; 185 } 186 } else { 187 Bridge.getLog().fidelityWarning(LayoutLog.TAG_PATHEFFECT, 188 mPathEffect.getSupportMessage(), 189 null, null /*data*/); 190 } 191 } 192 193 // if no custom stroke as been set, set the default one. 194 return new BasicStroke( 195 getStrokeWidth(), 196 getJavaCap(), 197 getJavaJoin(), 198 getJavaStrokeMiter()); 199 } 200 201 /** 202 * Returns the {@link Xfermode} delegate or null if none have been set 203 * 204 * @return the delegate or null. 205 */ 206 public Xfermode_Delegate getXfermode() { 207 return mXfermode; 208 } 209 210 /** 211 * Returns the {@link ColorFilter} delegate or null if none have been set 212 * 213 * @return the delegate or null. 214 */ 215 public ColorFilter_Delegate getColorFilter() { 216 return mColorFilter; 217 } 218 219 /** 220 * Returns the {@link Shader} delegate or null if none have been set 221 * 222 * @return the delegate or null. 223 */ 224 public Shader_Delegate getShader() { 225 return mShader; 226 } 227 228 /** 229 * Returns the {@link MaskFilter} delegate or null if none have been set 230 * 231 * @return the delegate or null. 232 */ 233 public MaskFilter_Delegate getMaskFilter() { 234 return mMaskFilter; 235 } 236 237 /** 238 * Returns the {@link Rasterizer} delegate or null if none have been set 239 * 240 * @return the delegate or null. 241 */ 242 public Rasterizer_Delegate getRasterizer() { 243 return mRasterizer; 244 } 245 246 // ---- native methods ---- 247 248 @LayoutlibDelegate 249 /*package*/ static int getFlags(Paint thisPaint) { 250 // get the delegate from the native int. 251 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 252 if (delegate == null) { 253 return 0; 254 } 255 256 return delegate.mFlags; 257 } 258 259 260 261 @LayoutlibDelegate 262 /*package*/ static void setFlags(Paint thisPaint, int flags) { 263 // get the delegate from the native int. 264 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 265 if (delegate == null) { 266 return; 267 } 268 269 delegate.mFlags = flags; 270 } 271 272 @LayoutlibDelegate 273 /*package*/ static void setFilterBitmap(Paint thisPaint, boolean filter) { 274 setFlag(thisPaint, Paint.FILTER_BITMAP_FLAG, filter); 275 } 276 277 @LayoutlibDelegate 278 /*package*/ static int getHinting(Paint thisPaint) { 279 // get the delegate from the native int. 280 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 281 if (delegate == null) { 282 return Paint.HINTING_ON; 283 } 284 285 return delegate.mHintingMode; 286 } 287 288 @LayoutlibDelegate 289 /*package*/ static void setHinting(Paint thisPaint, int mode) { 290 // get the delegate from the native int. 291 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 292 if (delegate == null) { 293 return; 294 } 295 296 delegate.mHintingMode = mode; 297 } 298 299 @LayoutlibDelegate 300 /*package*/ static void setAntiAlias(Paint thisPaint, boolean aa) { 301 setFlag(thisPaint, Paint.ANTI_ALIAS_FLAG, aa); 302 } 303 304 @LayoutlibDelegate 305 /*package*/ static void setSubpixelText(Paint thisPaint, boolean subpixelText) { 306 setFlag(thisPaint, Paint.SUBPIXEL_TEXT_FLAG, subpixelText); 307 } 308 309 @LayoutlibDelegate 310 /*package*/ static void setUnderlineText(Paint thisPaint, boolean underlineText) { 311 setFlag(thisPaint, Paint.UNDERLINE_TEXT_FLAG, underlineText); 312 } 313 314 @LayoutlibDelegate 315 /*package*/ static void setStrikeThruText(Paint thisPaint, boolean strikeThruText) { 316 setFlag(thisPaint, Paint.STRIKE_THRU_TEXT_FLAG, strikeThruText); 317 } 318 319 @LayoutlibDelegate 320 /*package*/ static void setFakeBoldText(Paint thisPaint, boolean fakeBoldText) { 321 setFlag(thisPaint, Paint.FAKE_BOLD_TEXT_FLAG, fakeBoldText); 322 } 323 324 @LayoutlibDelegate 325 /*package*/ static void setDither(Paint thisPaint, boolean dither) { 326 setFlag(thisPaint, Paint.DITHER_FLAG, dither); 327 } 328 329 @LayoutlibDelegate 330 /*package*/ static void setLinearText(Paint thisPaint, boolean linearText) { 331 setFlag(thisPaint, Paint.LINEAR_TEXT_FLAG, linearText); 332 } 333 334 @LayoutlibDelegate 335 /*package*/ static int getColor(Paint thisPaint) { 336 // get the delegate from the native int. 337 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 338 if (delegate == null) { 339 return 0; 340 } 341 342 return delegate.mColor; 343 } 344 345 @LayoutlibDelegate 346 /*package*/ static void setColor(Paint thisPaint, int color) { 347 // get the delegate from the native int. 348 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 349 if (delegate == null) { 350 return; 351 } 352 353 delegate.mColor = color; 354 } 355 356 @LayoutlibDelegate 357 /*package*/ static int getAlpha(Paint thisPaint) { 358 // get the delegate from the native int. 359 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 360 if (delegate == null) { 361 return 0; 362 } 363 364 return delegate.getAlpha(); 365 } 366 367 @LayoutlibDelegate 368 /*package*/ static void setAlpha(Paint thisPaint, int a) { 369 // get the delegate from the native int. 370 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 371 if (delegate == null) { 372 return; 373 } 374 375 delegate.setAlpha(a); 376 } 377 378 @LayoutlibDelegate 379 /*package*/ static float getStrokeWidth(Paint thisPaint) { 380 // get the delegate from the native int. 381 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 382 if (delegate == null) { 383 return 1.f; 384 } 385 386 return delegate.mStrokeWidth; 387 } 388 389 @LayoutlibDelegate 390 /*package*/ static void setStrokeWidth(Paint thisPaint, float width) { 391 // get the delegate from the native int. 392 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 393 if (delegate == null) { 394 return; 395 } 396 397 delegate.mStrokeWidth = width; 398 } 399 400 @LayoutlibDelegate 401 /*package*/ static float getStrokeMiter(Paint thisPaint) { 402 // get the delegate from the native int. 403 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 404 if (delegate == null) { 405 return 1.f; 406 } 407 408 return delegate.mStrokeMiter; 409 } 410 411 @LayoutlibDelegate 412 /*package*/ static void setStrokeMiter(Paint thisPaint, float miter) { 413 // get the delegate from the native int. 414 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 415 if (delegate == null) { 416 return; 417 } 418 419 delegate.mStrokeMiter = miter; 420 } 421 422 @LayoutlibDelegate 423 /*package*/ static void nSetShadowLayer(Paint thisPaint, float radius, float dx, float dy, 424 int color) { 425 // FIXME 426 Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, 427 "Paint.setShadowLayer is not supported.", null, null /*data*/); 428 } 429 430 @LayoutlibDelegate 431 /*package*/ static float getTextSize(Paint thisPaint) { 432 // get the delegate from the native int. 433 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 434 if (delegate == null) { 435 return 1.f; 436 } 437 438 return delegate.mTextSize; 439 } 440 441 @LayoutlibDelegate 442 /*package*/ static void setTextSize(Paint thisPaint, float textSize) { 443 // get the delegate from the native int. 444 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 445 if (delegate == null) { 446 return; 447 } 448 449 delegate.mTextSize = textSize; 450 delegate.updateFontObject(); 451 } 452 453 @LayoutlibDelegate 454 /*package*/ static float getTextScaleX(Paint thisPaint) { 455 // get the delegate from the native int. 456 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 457 if (delegate == null) { 458 return 1.f; 459 } 460 461 return delegate.mTextScaleX; 462 } 463 464 @LayoutlibDelegate 465 /*package*/ static void setTextScaleX(Paint thisPaint, float scaleX) { 466 // get the delegate from the native int. 467 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 468 if (delegate == null) { 469 return; 470 } 471 472 delegate.mTextScaleX = scaleX; 473 delegate.updateFontObject(); 474 } 475 476 @LayoutlibDelegate 477 /*package*/ static float getTextSkewX(Paint thisPaint) { 478 // get the delegate from the native int. 479 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 480 if (delegate == null) { 481 return 1.f; 482 } 483 484 return delegate.mTextSkewX; 485 } 486 487 @LayoutlibDelegate 488 /*package*/ static void setTextSkewX(Paint thisPaint, float skewX) { 489 // get the delegate from the native int. 490 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 491 if (delegate == null) { 492 return; 493 } 494 495 delegate.mTextSkewX = skewX; 496 delegate.updateFontObject(); 497 } 498 499 @LayoutlibDelegate 500 /*package*/ static float ascent(Paint thisPaint) { 501 // get the delegate 502 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 503 if (delegate == null) { 504 return 0; 505 } 506 507 if (delegate.mFonts.size() > 0) { 508 java.awt.FontMetrics javaMetrics = delegate.mFonts.get(0).mMetrics; 509 // Android expects negative ascent so we invert the value from Java. 510 return - javaMetrics.getAscent(); 511 } 512 513 return 0; 514 } 515 516 @LayoutlibDelegate 517 /*package*/ static float descent(Paint thisPaint) { 518 // get the delegate 519 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 520 if (delegate == null) { 521 return 0; 522 } 523 524 if (delegate.mFonts.size() > 0) { 525 java.awt.FontMetrics javaMetrics = delegate.mFonts.get(0).mMetrics; 526 return javaMetrics.getDescent(); 527 } 528 529 return 0; 530 531 } 532 533 @LayoutlibDelegate 534 /*package*/ static float getFontMetrics(Paint thisPaint, FontMetrics metrics) { 535 // get the delegate 536 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 537 if (delegate == null) { 538 return 0; 539 } 540 541 return delegate.getFontMetrics(metrics); 542 } 543 544 @LayoutlibDelegate 545 /*package*/ static int getFontMetricsInt(Paint thisPaint, FontMetricsInt fmi) { 546 // get the delegate 547 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 548 if (delegate == null) { 549 return 0; 550 } 551 552 if (delegate.mFonts.size() > 0) { 553 java.awt.FontMetrics javaMetrics = delegate.mFonts.get(0).mMetrics; 554 if (fmi != null) { 555 // Android expects negative ascent so we invert the value from Java. 556 fmi.top = - javaMetrics.getMaxAscent(); 557 fmi.ascent = - javaMetrics.getAscent(); 558 fmi.descent = javaMetrics.getDescent(); 559 fmi.bottom = javaMetrics.getMaxDescent(); 560 fmi.leading = javaMetrics.getLeading(); 561 } 562 563 return javaMetrics.getHeight(); 564 } 565 566 return 0; 567 } 568 569 @LayoutlibDelegate 570 /*package*/ static float native_measureText(Paint thisPaint, char[] text, int index, 571 int count, int bidiFlags) { 572 // get the delegate 573 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 574 if (delegate == null) { 575 return 0; 576 } 577 578 return delegate.measureText(text, index, count, isRtl(bidiFlags)); 579 } 580 581 @LayoutlibDelegate 582 /*package*/ static float native_measureText(Paint thisPaint, String text, int start, int end, 583 int bidiFlags) { 584 return native_measureText(thisPaint, text.toCharArray(), start, end - start, bidiFlags); 585 } 586 587 @LayoutlibDelegate 588 /*package*/ static float native_measureText(Paint thisPaint, String text, int bidiFlags) { 589 return native_measureText(thisPaint, text.toCharArray(), 0, text.length(), bidiFlags); 590 } 591 592 @LayoutlibDelegate 593 /*package*/ static int native_breakText(Paint thisPaint, char[] text, int index, int count, 594 float maxWidth, int bidiFlags, float[] measuredWidth) { 595 596 // get the delegate 597 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 598 if (delegate == null) { 599 return 0; 600 } 601 602 int inc = count > 0 ? 1 : -1; 603 604 int measureIndex = 0; 605 float measureAcc = 0; 606 for (int i = index; i != index + count; i += inc, measureIndex++) { 607 int start, end; 608 if (i < index) { 609 start = i; 610 end = index; 611 } else { 612 start = index; 613 end = i; 614 } 615 616 // measure from start to end 617 float res = delegate.measureText(text, start, end - start + 1, isRtl(bidiFlags)); 618 619 if (measuredWidth != null) { 620 measuredWidth[measureIndex] = res; 621 } 622 623 measureAcc += res; 624 if (res > maxWidth) { 625 // we should not return this char index, but since it's 0-based 626 // and we need to return a count, we simply return measureIndex; 627 return measureIndex; 628 } 629 630 } 631 632 return measureIndex; 633 } 634 635 @LayoutlibDelegate 636 /*package*/ static int native_breakText(Paint thisPaint, String text, boolean measureForwards, 637 float maxWidth, int bidiFlags, float[] measuredWidth) { 638 return native_breakText(thisPaint, text.toCharArray(), 0, text.length(), maxWidth, 639 bidiFlags, measuredWidth); 640 } 641 642 @LayoutlibDelegate 643 /*package*/ static int native_init() { 644 Paint_Delegate newDelegate = new Paint_Delegate(); 645 return sManager.addNewDelegate(newDelegate); 646 } 647 648 @LayoutlibDelegate 649 /*package*/ static int native_initWithPaint(int paint) { 650 // get the delegate from the native int. 651 Paint_Delegate delegate = sManager.getDelegate(paint); 652 if (delegate == null) { 653 return 0; 654 } 655 656 Paint_Delegate newDelegate = new Paint_Delegate(delegate); 657 return sManager.addNewDelegate(newDelegate); 658 } 659 660 @LayoutlibDelegate 661 /*package*/ static void native_reset(int native_object) { 662 // get the delegate from the native int. 663 Paint_Delegate delegate = sManager.getDelegate(native_object); 664 if (delegate == null) { 665 return; 666 } 667 668 delegate.reset(); 669 } 670 671 @LayoutlibDelegate 672 /*package*/ static void native_set(int native_dst, int native_src) { 673 // get the delegate from the native int. 674 Paint_Delegate delegate_dst = sManager.getDelegate(native_dst); 675 if (delegate_dst == null) { 676 return; 677 } 678 679 // get the delegate from the native int. 680 Paint_Delegate delegate_src = sManager.getDelegate(native_src); 681 if (delegate_src == null) { 682 return; 683 } 684 685 delegate_dst.set(delegate_src); 686 } 687 688 @LayoutlibDelegate 689 /*package*/ static int native_getStyle(int native_object) { 690 // get the delegate from the native int. 691 Paint_Delegate delegate = sManager.getDelegate(native_object); 692 if (delegate == null) { 693 return 0; 694 } 695 696 return delegate.mStyle; 697 } 698 699 @LayoutlibDelegate 700 /*package*/ static void native_setStyle(int native_object, int style) { 701 // get the delegate from the native int. 702 Paint_Delegate delegate = sManager.getDelegate(native_object); 703 if (delegate == null) { 704 return; 705 } 706 707 delegate.mStyle = style; 708 } 709 710 @LayoutlibDelegate 711 /*package*/ static int native_getStrokeCap(int native_object) { 712 // get the delegate from the native int. 713 Paint_Delegate delegate = sManager.getDelegate(native_object); 714 if (delegate == null) { 715 return 0; 716 } 717 718 return delegate.mCap; 719 } 720 721 @LayoutlibDelegate 722 /*package*/ static void native_setStrokeCap(int native_object, int cap) { 723 // get the delegate from the native int. 724 Paint_Delegate delegate = sManager.getDelegate(native_object); 725 if (delegate == null) { 726 return; 727 } 728 729 delegate.mCap = cap; 730 } 731 732 @LayoutlibDelegate 733 /*package*/ static int native_getStrokeJoin(int native_object) { 734 // get the delegate from the native int. 735 Paint_Delegate delegate = sManager.getDelegate(native_object); 736 if (delegate == null) { 737 return 0; 738 } 739 740 return delegate.mJoin; 741 } 742 743 @LayoutlibDelegate 744 /*package*/ static void native_setStrokeJoin(int native_object, int join) { 745 // get the delegate from the native int. 746 Paint_Delegate delegate = sManager.getDelegate(native_object); 747 if (delegate == null) { 748 return; 749 } 750 751 delegate.mJoin = join; 752 } 753 754 @LayoutlibDelegate 755 /*package*/ static boolean native_getFillPath(int native_object, int src, int dst) { 756 Paint_Delegate paint = sManager.getDelegate(native_object); 757 if (paint == null) { 758 return false; 759 } 760 761 Path_Delegate srcPath = Path_Delegate.getDelegate(src); 762 if (srcPath == null) { 763 return true; 764 } 765 766 Path_Delegate dstPath = Path_Delegate.getDelegate(dst); 767 if (dstPath == null) { 768 return true; 769 } 770 771 Stroke stroke = paint.getJavaStroke(); 772 Shape strokeShape = stroke.createStrokedShape(srcPath.getJavaShape()); 773 774 dstPath.setJavaShape(strokeShape); 775 776 // FIXME figure out the return value? 777 return true; 778 } 779 780 @LayoutlibDelegate 781 /*package*/ static int native_setShader(int native_object, int shader) { 782 // get the delegate from the native int. 783 Paint_Delegate delegate = sManager.getDelegate(native_object); 784 if (delegate == null) { 785 return shader; 786 } 787 788 delegate.mShader = Shader_Delegate.getDelegate(shader); 789 790 return shader; 791 } 792 793 @LayoutlibDelegate 794 /*package*/ static int native_setColorFilter(int native_object, int filter) { 795 // get the delegate from the native int. 796 Paint_Delegate delegate = sManager.getDelegate(native_object); 797 if (delegate == null) { 798 return filter; 799 } 800 801 delegate.mColorFilter = ColorFilter_Delegate.getDelegate(filter);; 802 803 // since none of those are supported, display a fidelity warning right away 804 if (delegate.mColorFilter != null && delegate.mColorFilter.isSupported() == false) { 805 Bridge.getLog().fidelityWarning(LayoutLog.TAG_COLORFILTER, 806 delegate.mColorFilter.getSupportMessage(), null, null /*data*/); 807 } 808 809 return filter; 810 } 811 812 @LayoutlibDelegate 813 /*package*/ static int native_setXfermode(int native_object, int xfermode) { 814 // get the delegate from the native int. 815 Paint_Delegate delegate = sManager.getDelegate(native_object); 816 if (delegate == null) { 817 return xfermode; 818 } 819 820 delegate.mXfermode = Xfermode_Delegate.getDelegate(xfermode); 821 822 return xfermode; 823 } 824 825 @LayoutlibDelegate 826 /*package*/ static int native_setPathEffect(int native_object, int effect) { 827 // get the delegate from the native int. 828 Paint_Delegate delegate = sManager.getDelegate(native_object); 829 if (delegate == null) { 830 return effect; 831 } 832 833 delegate.mPathEffect = PathEffect_Delegate.getDelegate(effect); 834 835 return effect; 836 } 837 838 @LayoutlibDelegate 839 /*package*/ static int native_setMaskFilter(int native_object, int maskfilter) { 840 // get the delegate from the native int. 841 Paint_Delegate delegate = sManager.getDelegate(native_object); 842 if (delegate == null) { 843 return maskfilter; 844 } 845 846 delegate.mMaskFilter = MaskFilter_Delegate.getDelegate(maskfilter); 847 848 // since none of those are supported, display a fidelity warning right away 849 if (delegate.mMaskFilter != null && delegate.mMaskFilter.isSupported() == false) { 850 Bridge.getLog().fidelityWarning(LayoutLog.TAG_MASKFILTER, 851 delegate.mMaskFilter.getSupportMessage(), null, null /*data*/); 852 } 853 854 return maskfilter; 855 } 856 857 @LayoutlibDelegate 858 /*package*/ static int native_setTypeface(int native_object, int typeface) { 859 // get the delegate from the native int. 860 Paint_Delegate delegate = sManager.getDelegate(native_object); 861 if (delegate == null) { 862 return 0; 863 } 864 865 delegate.mTypeface = Typeface_Delegate.getDelegate(typeface); 866 delegate.updateFontObject(); 867 return typeface; 868 } 869 870 @LayoutlibDelegate 871 /*package*/ static int native_setRasterizer(int native_object, int rasterizer) { 872 // get the delegate from the native int. 873 Paint_Delegate delegate = sManager.getDelegate(native_object); 874 if (delegate == null) { 875 return rasterizer; 876 } 877 878 delegate.mRasterizer = Rasterizer_Delegate.getDelegate(rasterizer); 879 880 // since none of those are supported, display a fidelity warning right away 881 if (delegate.mRasterizer != null && delegate.mRasterizer.isSupported() == false) { 882 Bridge.getLog().fidelityWarning(LayoutLog.TAG_RASTERIZER, 883 delegate.mRasterizer.getSupportMessage(), null, null /*data*/); 884 } 885 886 return rasterizer; 887 } 888 889 @LayoutlibDelegate 890 /*package*/ static int native_getTextAlign(int native_object) { 891 // get the delegate from the native int. 892 Paint_Delegate delegate = sManager.getDelegate(native_object); 893 if (delegate == null) { 894 return 0; 895 } 896 897 return delegate.mTextAlign; 898 } 899 900 @LayoutlibDelegate 901 /*package*/ static void native_setTextAlign(int native_object, int align) { 902 // get the delegate from the native int. 903 Paint_Delegate delegate = sManager.getDelegate(native_object); 904 if (delegate == null) { 905 return; 906 } 907 908 delegate.mTextAlign = align; 909 } 910 911 @LayoutlibDelegate 912 /*package*/ static void native_setTextLocale(int native_object, String locale) { 913 // get the delegate from the native int. 914 Paint_Delegate delegate = sManager.getDelegate(native_object); 915 if (delegate == null) { 916 return; 917 } 918 919 delegate.setTextLocale(locale); 920 } 921 922 @LayoutlibDelegate 923 /*package*/ static int native_getTextWidths(int native_object, char[] text, int index, 924 int count, int bidiFlags, float[] widths) { 925 // get the delegate from the native int. 926 Paint_Delegate delegate = sManager.getDelegate(native_object); 927 if (delegate == null) { 928 return 0; 929 } 930 931 if (delegate.mFonts.size() > 0) { 932 // FIXME: handle multi-char characters (see measureText) 933 float totalAdvance = 0; 934 for (int i = 0; i < count; i++) { 935 char c = text[i + index]; 936 boolean found = false; 937 for (FontInfo info : delegate.mFonts) { 938 if (info.mFont.canDisplay(c)) { 939 float adv = info.mMetrics.charWidth(c); 940 totalAdvance += adv; 941 if (widths != null) { 942 widths[i] = adv; 943 } 944 945 found = true; 946 break; 947 } 948 } 949 950 if (found == false) { 951 // no advance for this char. 952 if (widths != null) { 953 widths[i] = 0.f; 954 } 955 } 956 } 957 958 return (int) totalAdvance; 959 } 960 961 return 0; 962 } 963 964 @LayoutlibDelegate 965 /*package*/ static int native_getTextWidths(int native_object, String text, int start, 966 int end, int bidiFlags, float[] widths) { 967 return native_getTextWidths(native_object, text.toCharArray(), start, end - start, 968 bidiFlags, widths); 969 } 970 971 @LayoutlibDelegate 972 /* package */static int native_getTextGlyphs(int native_object, String text, int start, 973 int end, int contextStart, int contextEnd, int flags, char[] glyphs) { 974 // FIXME 975 return 0; 976 } 977 978 @LayoutlibDelegate 979 /*package*/ static float native_getTextRunAdvances(int native_object, 980 char[] text, int index, int count, int contextIndex, int contextCount, 981 int flags, float[] advances, int advancesIndex) { 982 983 if (advances != null) 984 for (int i = advancesIndex; i< advancesIndex+count; i++) 985 advances[i]=0; 986 // get the delegate from the native int. 987 Paint_Delegate delegate = sManager.getDelegate(native_object); 988 if (delegate == null || delegate.mFonts == null || delegate.mFonts.size() == 0) { 989 return 0.f; 990 } 991 boolean isRtl = isRtl(flags); 992 993 int limit = index + count; 994 return new BidiRenderer(null, delegate, text).renderText( 995 index, limit, isRtl, advances, advancesIndex, false, 0, 0); 996 } 997 998 @LayoutlibDelegate 999 /*package*/ static float native_getTextRunAdvances(int native_object, 1000 String text, int start, int end, int contextStart, int contextEnd, 1001 int flags, float[] advances, int advancesIndex) { 1002 // FIXME: support contextStart and contextEnd 1003 int count = end - start; 1004 char[] buffer = TemporaryBuffer.obtain(count); 1005 TextUtils.getChars(text, start, end, buffer, 0); 1006 1007 return native_getTextRunAdvances(native_object, buffer, 0, count, contextStart, 1008 contextEnd - contextStart, flags, advances, advancesIndex); 1009 } 1010 1011 @LayoutlibDelegate 1012 /*package*/ static int native_getTextRunCursor(Paint thisPaint, int native_object, char[] text, 1013 int contextStart, int contextLength, int flags, int offset, int cursorOpt) { 1014 // FIXME 1015 Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, 1016 "Paint.getTextRunCursor is not supported.", null, null /*data*/); 1017 return 0; 1018 } 1019 1020 @LayoutlibDelegate 1021 /*package*/ static int native_getTextRunCursor(Paint thisPaint, int native_object, String text, 1022 int contextStart, int contextEnd, int flags, int offset, int cursorOpt) { 1023 // FIXME 1024 Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, 1025 "Paint.getTextRunCursor is not supported.", null, null /*data*/); 1026 return 0; 1027 } 1028 1029 @LayoutlibDelegate 1030 /*package*/ static void native_getTextPath(int native_object, int bidiFlags, 1031 char[] text, int index, int count, float x, float y, int path) { 1032 // FIXME 1033 Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, 1034 "Paint.getTextPath is not supported.", null, null /*data*/); 1035 } 1036 1037 @LayoutlibDelegate 1038 /*package*/ static void native_getTextPath(int native_object, int bidiFlags, 1039 String text, int start, int end, float x, float y, int path) { 1040 // FIXME 1041 Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, 1042 "Paint.getTextPath is not supported.", null, null /*data*/); 1043 } 1044 1045 @LayoutlibDelegate 1046 /*package*/ static void nativeGetStringBounds(int nativePaint, String text, int start, 1047 int end, int bidiFlags, Rect bounds) { 1048 nativeGetCharArrayBounds(nativePaint, text.toCharArray(), start, end - start, bidiFlags, 1049 bounds); 1050 } 1051 1052 @LayoutlibDelegate 1053 /*package*/ static void nativeGetCharArrayBounds(int nativePaint, char[] text, int index, 1054 int count, int bidiFlags, Rect bounds) { 1055 1056 // get the delegate from the native int. 1057 Paint_Delegate delegate = sManager.getDelegate(nativePaint); 1058 if (delegate == null || delegate.mFonts == null || delegate.mFonts.size() == 0) { 1059 return; 1060 } 1061 int w = (int) delegate.measureText(text, index, count, isRtl(bidiFlags)); 1062 int h= delegate.getFonts().get(0).mMetrics.getHeight(); 1063 bounds.set(0, 0, w, h); 1064 } 1065 1066 @LayoutlibDelegate 1067 /*package*/ static void finalizer(int nativePaint) { 1068 sManager.removeJavaReferenceFor(nativePaint); 1069 } 1070 1071 // ---- Private delegate/helper methods ---- 1072 1073 /*package*/ Paint_Delegate() { 1074 reset(); 1075 } 1076 1077 private Paint_Delegate(Paint_Delegate paint) { 1078 set(paint); 1079 } 1080 1081 private void set(Paint_Delegate paint) { 1082 mFlags = paint.mFlags; 1083 mColor = paint.mColor; 1084 mStyle = paint.mStyle; 1085 mCap = paint.mCap; 1086 mJoin = paint.mJoin; 1087 mTextAlign = paint.mTextAlign; 1088 mTypeface = paint.mTypeface; 1089 mStrokeWidth = paint.mStrokeWidth; 1090 mStrokeMiter = paint.mStrokeMiter; 1091 mTextSize = paint.mTextSize; 1092 mTextScaleX = paint.mTextScaleX; 1093 mTextSkewX = paint.mTextSkewX; 1094 mXfermode = paint.mXfermode; 1095 mColorFilter = paint.mColorFilter; 1096 mShader = paint.mShader; 1097 mPathEffect = paint.mPathEffect; 1098 mMaskFilter = paint.mMaskFilter; 1099 mRasterizer = paint.mRasterizer; 1100 mHintingMode = paint.mHintingMode; 1101 updateFontObject(); 1102 } 1103 1104 private void reset() { 1105 mFlags = Paint.DEFAULT_PAINT_FLAGS; 1106 mColor = 0xFF000000; 1107 mStyle = Paint.Style.FILL.nativeInt; 1108 mCap = Paint.Cap.BUTT.nativeInt; 1109 mJoin = Paint.Join.MITER.nativeInt; 1110 mTextAlign = 0; 1111 mTypeface = Typeface_Delegate.getDelegate(Typeface.sDefaults[0].native_instance); 1112 mStrokeWidth = 1.f; 1113 mStrokeMiter = 4.f; 1114 mTextSize = 20.f; 1115 mTextScaleX = 1.f; 1116 mTextSkewX = 0.f; 1117 mXfermode = null; 1118 mColorFilter = null; 1119 mShader = null; 1120 mPathEffect = null; 1121 mMaskFilter = null; 1122 mRasterizer = null; 1123 updateFontObject(); 1124 mHintingMode = Paint.HINTING_ON; 1125 } 1126 1127 /** 1128 * Update the {@link Font} object from the typeface, text size and scaling 1129 */ 1130 @SuppressWarnings("deprecation") 1131 private void updateFontObject() { 1132 if (mTypeface != null) { 1133 // Get the fonts from the TypeFace object. 1134 List<Font> fonts = mTypeface.getFonts(); 1135 1136 // create new font objects as well as FontMetrics, based on the current text size 1137 // and skew info. 1138 ArrayList<FontInfo> infoList = new ArrayList<FontInfo>(fonts.size()); 1139 for (Font font : fonts) { 1140 FontInfo info = new FontInfo(); 1141 info.mFont = font.deriveFont(mTextSize); 1142 if (mTextScaleX != 1.0 || mTextSkewX != 0) { 1143 // TODO: support skew 1144 info.mFont = info.mFont.deriveFont(new AffineTransform( 1145 mTextScaleX, mTextSkewX, 0, 1, 0, 0)); 1146 } 1147 // The metrics here don't have anti-aliasing set. 1148 info.mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(info.mFont); 1149 1150 infoList.add(info); 1151 } 1152 1153 mFonts = Collections.unmodifiableList(infoList); 1154 } 1155 } 1156 1157 /*package*/ float measureText(char[] text, int index, int count, boolean isRtl) { 1158 return new BidiRenderer(null, this, text).renderText( 1159 index, index + count, isRtl, null, 0, false, 0, 0); 1160 } 1161 1162 private float getFontMetrics(FontMetrics metrics) { 1163 if (mFonts.size() > 0) { 1164 java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics; 1165 if (metrics != null) { 1166 // Android expects negative ascent so we invert the value from Java. 1167 metrics.top = - javaMetrics.getMaxAscent(); 1168 metrics.ascent = - javaMetrics.getAscent(); 1169 metrics.descent = javaMetrics.getDescent(); 1170 metrics.bottom = javaMetrics.getMaxDescent(); 1171 metrics.leading = javaMetrics.getLeading(); 1172 } 1173 1174 return javaMetrics.getHeight(); 1175 } 1176 1177 return 0; 1178 } 1179 1180 private void setTextLocale(String locale) { 1181 mLocale = new Locale(locale); 1182 } 1183 1184 private static void setFlag(Paint thisPaint, int flagMask, boolean flagValue) { 1185 // get the delegate from the native int. 1186 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 1187 if (delegate == null) { 1188 return; 1189 } 1190 1191 if (flagValue) { 1192 delegate.mFlags |= flagMask; 1193 } else { 1194 delegate.mFlags &= ~flagMask; 1195 } 1196 } 1197 1198 private static boolean isRtl(int flag) { 1199 switch(flag) { 1200 case Paint.BIDI_RTL: 1201 case Paint.BIDI_FORCE_RTL: 1202 case Paint.BIDI_DEFAULT_RTL: 1203 return true; 1204 default: 1205 return false; 1206 } 1207 } 1208 } 1209