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