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