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