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