Paint_Delegate.java revision 8e7e4cf314f05d82250d0d4429c02d760d988acd
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.ArrayList; 38import java.util.Collections; 39import java.util.List; 40import java.util.Locale; 41 42import 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 */ 57public 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, char[] text, 968 int contextStart, int contextLength, int flags, int offset, int cursorOpt) { 969 // FIXME 970 Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, 971 "Paint.getTextRunCursor is not supported.", null, null /*data*/); 972 return 0; 973 } 974 975 @LayoutlibDelegate 976 /*package*/ static int nGetTextRunCursor(Paint paint, long native_object, String text, 977 int contextStart, int contextEnd, int flags, int offset, int cursorOpt) { 978 // FIXME 979 Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, 980 "Paint.getTextRunCursor is not supported.", null, null /*data*/); 981 return 0; 982 } 983 984 @LayoutlibDelegate 985 /*package*/ static void nGetTextPath(long native_object, long native_typeface, 986 int bidiFlags, char[] text, int index, int count, float x, float y, long path) { 987 // FIXME 988 Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, 989 "Paint.getTextPath is not supported.", null, null /*data*/); 990 } 991 992 @LayoutlibDelegate 993 /*package*/ static void nGetTextPath(long native_object, long native_typeface, 994 int bidiFlags, String text, int start, int end, float x, float y, long path) { 995 // FIXME 996 Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, 997 "Paint.getTextPath is not supported.", null, null /*data*/); 998 } 999 1000 @LayoutlibDelegate 1001 /*package*/ static void nGetStringBounds(long nativePaint, long native_typeface, 1002 String text, int start, int end, int bidiFlags, Rect bounds) { 1003 nGetCharArrayBounds(nativePaint, native_typeface, text.toCharArray(), start, 1004 end - start, bidiFlags, bounds); 1005 } 1006 1007 @LayoutlibDelegate 1008 /*package*/ static void nGetCharArrayBounds(long nativePaint, long native_typeface, 1009 char[] text, int index, int count, int bidiFlags, Rect bounds) { 1010 1011 // get the delegate from the native int. 1012 Paint_Delegate delegate = sManager.getDelegate(nativePaint); 1013 if (delegate == null) { 1014 return; 1015 } 1016 1017 // assert that the typeface passed is actually the one that we had stored. 1018 assert (native_typeface == delegate.mNativeTypeface); 1019 1020 delegate.measureText(text, index, count, null, 0, bidiFlags).roundOut(bounds); 1021 } 1022 1023 @LayoutlibDelegate 1024 /*package*/ static long nGetNativeFinalizer() { 1025 synchronized (Paint_Delegate.class) { 1026 if (sFinalizer == -1) { 1027 sFinalizer = NativeAllocationRegistry_Delegate.createFinalizer( 1028 sManager::removeJavaReferenceFor); 1029 } 1030 } 1031 return sFinalizer; 1032 } 1033 1034 @LayoutlibDelegate 1035 /*package*/ static float nGetLetterSpacing(long nativePaint) { 1036 Paint_Delegate delegate = sManager.getDelegate(nativePaint); 1037 if (delegate == null) { 1038 return 0; 1039 } 1040 return delegate.mLetterSpacing; 1041 } 1042 1043 @LayoutlibDelegate 1044 /*package*/ static void nSetLetterSpacing(long nativePaint, float letterSpacing) { 1045 Bridge.getLog().fidelityWarning(LayoutLog.TAG_TEXT_RENDERING, 1046 "Paint.setLetterSpacing() not supported.", null, null); 1047 Paint_Delegate delegate = sManager.getDelegate(nativePaint); 1048 if (delegate == null) { 1049 return; 1050 } 1051 delegate.mLetterSpacing = letterSpacing; 1052 } 1053 1054 @LayoutlibDelegate 1055 /*package*/ static float nGetWordSpacing(long nativePaint) { 1056 Paint_Delegate delegate = sManager.getDelegate(nativePaint); 1057 if (delegate == null) { 1058 return 0; 1059 } 1060 return delegate.mWordSpacing; 1061 } 1062 1063 @LayoutlibDelegate 1064 /*package*/ static void nSetWordSpacing(long nativePaint, float wordSpacing) { 1065 Bridge.getLog().fidelityWarning(LayoutLog.TAG_TEXT_RENDERING, 1066 "Paint.setWordSpacing() not supported.", null, null); 1067 Paint_Delegate delegate = sManager.getDelegate(nativePaint); 1068 if (delegate == null) { 1069 return; 1070 } 1071 delegate.mWordSpacing = wordSpacing; 1072 } 1073 1074 @LayoutlibDelegate 1075 /*package*/ static void nSetFontFeatureSettings(long nativePaint, String settings) { 1076 Bridge.getLog().fidelityWarning(LayoutLog.TAG_TEXT_RENDERING, 1077 "Paint.setFontFeatureSettings() not supported.", null, null); 1078 } 1079 1080 @LayoutlibDelegate 1081 /*package*/ static int nGetHyphenEdit(long nativePaint) { 1082 Paint_Delegate delegate = sManager.getDelegate(nativePaint); 1083 if (delegate == null) { 1084 return 0; 1085 } 1086 return delegate.mHyphenEdit; 1087 } 1088 1089 @LayoutlibDelegate 1090 /*package*/ static void nSetHyphenEdit(long nativePaint, int hyphen) { 1091 Paint_Delegate delegate = sManager.getDelegate(nativePaint); 1092 if (delegate == null) { 1093 return; 1094 } 1095 delegate.mHyphenEdit = hyphen; 1096 } 1097 1098 @LayoutlibDelegate 1099 /*package*/ static boolean nHasGlyph(long nativePaint, long nativeTypeface, int bidiFlags, 1100 String string) { 1101 Paint_Delegate delegate = sManager.getDelegate(nativePaint); 1102 if (delegate == null) { 1103 return false; 1104 } 1105 if (string.length() == 0) { 1106 return false; 1107 } 1108 if (string.length() > 1) { 1109 Bridge.getLog().fidelityWarning(LayoutLog.TAG_TEXT_RENDERING, 1110 "Paint.hasGlyph() is not supported for ligatures.", null, null); 1111 return false; 1112 } 1113 assert nativeTypeface == delegate.mNativeTypeface; 1114 Typeface_Delegate typeface_delegate = Typeface_Delegate.getDelegate(nativeTypeface); 1115 1116 char c = string.charAt(0); 1117 for (Font font : typeface_delegate.getFonts(delegate.mFontVariant)) { 1118 if (font.canDisplay(c)) { 1119 return true; 1120 } 1121 } 1122 return false; 1123 } 1124 1125 1126 @LayoutlibDelegate 1127 /*package*/ static float nGetRunAdvance(long nativePaint, long nativeTypeface, 1128 @NonNull char[] text, int start, int end, int contextStart, int contextEnd, 1129 boolean isRtl, int offset) { 1130 int count = end - start; 1131 float[] advances = new float[count]; 1132 int bidiFlags = isRtl ? Paint.BIDI_FORCE_RTL : Paint.BIDI_FORCE_LTR; 1133 nGetTextAdvances(nativePaint, nativeTypeface, text, start, count, 1134 contextStart, contextEnd - contextStart, bidiFlags, advances, 0); 1135 int startOffset = offset - start; // offset from start. 1136 float sum = 0; 1137 for (int i = 0; i < startOffset; i++) { 1138 sum += advances[i]; 1139 } 1140 return sum; 1141 } 1142 1143 @LayoutlibDelegate 1144 /*package*/ static int nGetOffsetForAdvance(long nativePaint, long nativeTypeface, 1145 char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl, 1146 float advance) { 1147 int count = end - start; 1148 float[] advances = new float[count]; 1149 int bidiFlags = isRtl ? Paint.BIDI_FORCE_RTL : Paint.BIDI_FORCE_LTR; 1150 nGetTextAdvances(nativePaint, nativeTypeface, text, start, count, 1151 contextStart, contextEnd - contextStart, bidiFlags, advances, 0); 1152 float sum = 0; 1153 int i; 1154 for (i = 0; i < count && sum < advance; i++) { 1155 sum += advances[i]; 1156 } 1157 float distanceToI = sum - advance; 1158 float distanceToIMinus1 = advance - (sum - advances[i]); 1159 return distanceToI > distanceToIMinus1 ? i : i - 1; 1160 } 1161 1162 // ---- Private delegate/helper methods ---- 1163 1164 /*package*/ Paint_Delegate() { 1165 reset(); 1166 } 1167 1168 private Paint_Delegate(Paint_Delegate paint) { 1169 set(paint); 1170 } 1171 1172 private void set(Paint_Delegate paint) { 1173 mFlags = paint.mFlags; 1174 mColor = paint.mColor; 1175 mStyle = paint.mStyle; 1176 mCap = paint.mCap; 1177 mJoin = paint.mJoin; 1178 mTextAlign = paint.mTextAlign; 1179 1180 boolean needsFontUpdate = false; 1181 if (mTypeface != paint.mTypeface || mNativeTypeface != paint.mNativeTypeface) { 1182 mTypeface = paint.mTypeface; 1183 mNativeTypeface = paint.mNativeTypeface; 1184 needsFontUpdate = true; 1185 } 1186 1187 if (mTextSize != paint.mTextSize) { 1188 mTextSize = paint.mTextSize; 1189 needsFontUpdate = true; 1190 } 1191 1192 if (mTextScaleX != paint.mTextScaleX) { 1193 mTextScaleX = paint.mTextScaleX; 1194 needsFontUpdate = true; 1195 } 1196 1197 if (mTextSkewX != paint.mTextSkewX) { 1198 mTextSkewX = paint.mTextSkewX; 1199 needsFontUpdate = true; 1200 } 1201 1202 mStrokeWidth = paint.mStrokeWidth; 1203 mStrokeMiter = paint.mStrokeMiter; 1204 mPorterDuffMode = paint.mPorterDuffMode; 1205 mColorFilter = paint.mColorFilter; 1206 mShader = paint.mShader; 1207 mPathEffect = paint.mPathEffect; 1208 mMaskFilter = paint.mMaskFilter; 1209 mHintingMode = paint.mHintingMode; 1210 1211 if (needsFontUpdate) { 1212 updateFontObject(); 1213 } 1214 } 1215 1216 private void reset() { 1217 mFlags = Paint.HIDDEN_DEFAULT_PAINT_FLAGS; 1218 mColor = 0xFF000000; 1219 mStyle = Paint.Style.FILL.nativeInt; 1220 mCap = Paint.Cap.BUTT.nativeInt; 1221 mJoin = Paint.Join.MITER.nativeInt; 1222 mTextAlign = 0; 1223 mTypeface = Typeface_Delegate.getDelegate(Typeface.sDefaults[0].native_instance); 1224 mNativeTypeface = 0; 1225 mStrokeWidth = 1.f; 1226 mStrokeMiter = 4.f; 1227 mTextSize = 20.f; 1228 mTextScaleX = 1.f; 1229 mTextSkewX = 0.f; 1230 mPorterDuffMode = Xfermode.DEFAULT; 1231 mColorFilter = null; 1232 mShader = null; 1233 mPathEffect = null; 1234 mMaskFilter = null; 1235 updateFontObject(); 1236 mHintingMode = Paint.HINTING_ON; 1237 } 1238 1239 /** 1240 * Update the {@link Font} object from the typeface, text size and scaling 1241 */ 1242 @SuppressWarnings("deprecation") 1243 private void updateFontObject() { 1244 if (mTypeface != null) { 1245 // Get the fonts from the TypeFace object. 1246 List<Font> fonts = mTypeface.getFonts(mFontVariant); 1247 1248 if (fonts.isEmpty()) { 1249 mFonts = Collections.emptyList(); 1250 return; 1251 } 1252 1253 // create new font objects as well as FontMetrics, based on the current text size 1254 // and skew info. 1255 int nFonts = fonts.size(); 1256 ArrayList<FontInfo> infoList = new ArrayList<FontInfo>(nFonts); 1257 //noinspection ForLoopReplaceableByForEach (avoid iterator instantiation) 1258 for (int i = 0; i < nFonts; i++) { 1259 Font font = fonts.get(i); 1260 if (font == null) { 1261 // If the font is null, add null to infoList. When rendering the text, if this 1262 // null is reached, a warning will be logged. 1263 infoList.add(null); 1264 continue; 1265 } 1266 FontInfo info = new FontInfo(); 1267 info.mFont = font.deriveFont(mTextSize); 1268 if (mTextScaleX != 1.0 || mTextSkewX != 0) { 1269 // TODO: support skew 1270 info.mFont = info.mFont.deriveFont(new AffineTransform( 1271 mTextScaleX, mTextSkewX, 0, 1, 0, 0)); 1272 } 1273 // The metrics here don't have anti-aliasing set. 1274 info.mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(info.mFont); 1275 1276 infoList.add(info); 1277 } 1278 1279 mFonts = Collections.unmodifiableList(infoList); 1280 } 1281 } 1282 1283 /*package*/ RectF measureText(char[] text, int index, int count, float[] advances, 1284 int advancesIndex, int bidiFlags) { 1285 return new BidiRenderer(null, this, text) 1286 .renderText(index, index + count, bidiFlags, advances, advancesIndex, false); 1287 } 1288 1289 /*package*/ RectF measureText(char[] text, int index, int count, float[] advances, 1290 int advancesIndex, boolean isRtl) { 1291 return new BidiRenderer(null, this, text) 1292 .renderText(index, index + count, isRtl, advances, advancesIndex, false); 1293 } 1294 1295 private float getFontMetrics(FontMetrics metrics) { 1296 if (mFonts.size() > 0) { 1297 java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics; 1298 if (metrics != null) { 1299 // Android expects negative ascent so we invert the value from Java. 1300 metrics.top = - javaMetrics.getMaxAscent(); 1301 metrics.ascent = - javaMetrics.getAscent(); 1302 metrics.descent = javaMetrics.getDescent(); 1303 metrics.bottom = javaMetrics.getMaxDescent(); 1304 metrics.leading = javaMetrics.getLeading(); 1305 } 1306 1307 return javaMetrics.getHeight(); 1308 } 1309 1310 return 0; 1311 } 1312 1313 private void setTextLocale(String locale) { 1314 mLocale = new Locale(locale); 1315 } 1316 1317 private static void setFlag(long nativePaint, int flagMask, boolean flagValue) { 1318 // get the delegate from the native int. 1319 Paint_Delegate delegate = sManager.getDelegate(nativePaint); 1320 if (delegate == null) { 1321 return; 1322 } 1323 1324 if (flagValue) { 1325 delegate.mFlags |= flagMask; 1326 } else { 1327 delegate.mFlags &= ~flagMask; 1328 } 1329 } 1330} 1331