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