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