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