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