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