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