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