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 getDelegate(int native_paint)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 */ getFonts()108 public List<FontInfo> getFonts() { 109 return mFonts; 110 } 111 isAntiAliased()112 public boolean isAntiAliased() { 113 return (mFlags & Paint.ANTI_ALIAS_FLAG) != 0; 114 } 115 isFilterBitmap()116 public boolean isFilterBitmap() { 117 return (mFlags & Paint.FILTER_BITMAP_FLAG) != 0; 118 } 119 getStyle()120 public int getStyle() { 121 return mStyle; 122 } 123 getColor()124 public int getColor() { 125 return mColor; 126 } 127 getAlpha()128 public int getAlpha() { 129 return mColor >>> 24; 130 } 131 setAlpha(int alpha)132 public void setAlpha(int alpha) { 133 mColor = (alpha << 24) | (mColor & 0x00FFFFFF); 134 } 135 getTextAlign()136 public int getTextAlign() { 137 return mTextAlign; 138 } 139 getStrokeWidth()140 public float getStrokeWidth() { 141 return mStrokeWidth; 142 } 143 144 /** 145 * returns the value of stroke miter needed by the java api. 146 */ getJavaStrokeMiter()147 public float getJavaStrokeMiter() { 148 float miter = mStrokeMiter * mStrokeWidth; 149 if (miter < 1.f) { 150 miter = 1.f; 151 } 152 return miter; 153 } 154 getJavaCap()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 getJavaJoin()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 getJavaStroke()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 */ getXfermode()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 */ getColorFilter()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 */ getShader()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 */ getMaskFilter()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 */ getRasterizer()243 public Rasterizer_Delegate getRasterizer() { 244 return mRasterizer; 245 } 246 247 // ---- native methods ---- 248 249 @LayoutlibDelegate getFlags(Paint thisPaint)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 setFlags(Paint thisPaint, int flags)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 setFilterBitmap(Paint thisPaint, boolean filter)274 /*package*/ static void setFilterBitmap(Paint thisPaint, boolean filter) { 275 setFlag(thisPaint, Paint.FILTER_BITMAP_FLAG, filter); 276 } 277 278 @LayoutlibDelegate getHinting(Paint thisPaint)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 setHinting(Paint thisPaint, int mode)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 setAntiAlias(Paint thisPaint, boolean aa)301 /*package*/ static void setAntiAlias(Paint thisPaint, boolean aa) { 302 setFlag(thisPaint, Paint.ANTI_ALIAS_FLAG, aa); 303 } 304 305 @LayoutlibDelegate setSubpixelText(Paint thisPaint, boolean subpixelText)306 /*package*/ static void setSubpixelText(Paint thisPaint, boolean subpixelText) { 307 setFlag(thisPaint, Paint.SUBPIXEL_TEXT_FLAG, subpixelText); 308 } 309 310 @LayoutlibDelegate setUnderlineText(Paint thisPaint, boolean underlineText)311 /*package*/ static void setUnderlineText(Paint thisPaint, boolean underlineText) { 312 setFlag(thisPaint, Paint.UNDERLINE_TEXT_FLAG, underlineText); 313 } 314 315 @LayoutlibDelegate setStrikeThruText(Paint thisPaint, boolean strikeThruText)316 /*package*/ static void setStrikeThruText(Paint thisPaint, boolean strikeThruText) { 317 setFlag(thisPaint, Paint.STRIKE_THRU_TEXT_FLAG, strikeThruText); 318 } 319 320 @LayoutlibDelegate setFakeBoldText(Paint thisPaint, boolean fakeBoldText)321 /*package*/ static void setFakeBoldText(Paint thisPaint, boolean fakeBoldText) { 322 setFlag(thisPaint, Paint.FAKE_BOLD_TEXT_FLAG, fakeBoldText); 323 } 324 325 @LayoutlibDelegate setDither(Paint thisPaint, boolean dither)326 /*package*/ static void setDither(Paint thisPaint, boolean dither) { 327 setFlag(thisPaint, Paint.DITHER_FLAG, dither); 328 } 329 330 @LayoutlibDelegate setLinearText(Paint thisPaint, boolean linearText)331 /*package*/ static void setLinearText(Paint thisPaint, boolean linearText) { 332 setFlag(thisPaint, Paint.LINEAR_TEXT_FLAG, linearText); 333 } 334 335 @LayoutlibDelegate getColor(Paint thisPaint)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 setColor(Paint thisPaint, int color)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 getAlpha(Paint thisPaint)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 setAlpha(Paint thisPaint, int a)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 getStrokeWidth(Paint thisPaint)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 setStrokeWidth(Paint thisPaint, float width)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 getStrokeMiter(Paint thisPaint)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 setStrokeMiter(Paint thisPaint, float miter)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 nSetShadowLayer(Paint thisPaint, float radius, float dx, float dy, int color)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 getTextSize(Paint thisPaint)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 setTextSize(Paint thisPaint, float textSize)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 getTextScaleX(Paint thisPaint)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 setTextScaleX(Paint thisPaint, float scaleX)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 getTextSkewX(Paint thisPaint)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 setTextSkewX(Paint thisPaint, float skewX)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 ascent(Paint thisPaint)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 descent(Paint thisPaint)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 getFontMetrics(Paint thisPaint, FontMetrics metrics)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 getFontMetricsInt(Paint thisPaint, FontMetricsInt fmi)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 native_measureText(Paint thisPaint, char[] text, int index, int count, int bidiFlags)571 /*package*/ static float native_measureText(Paint thisPaint, char[] text, int index, 572 int count, int bidiFlags) { 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, bidiFlags); 580 } 581 582 @LayoutlibDelegate native_measureText(Paint thisPaint, String text, int start, int end, int bidiFlags)583 /*package*/ static float native_measureText(Paint thisPaint, String text, int start, int end, 584 int bidiFlags) { 585 return native_measureText(thisPaint, text.toCharArray(), start, end - start, bidiFlags); 586 } 587 588 @LayoutlibDelegate native_measureText(Paint thisPaint, String text, int bidiFlags)589 /*package*/ static float native_measureText(Paint thisPaint, String text, int bidiFlags) { 590 return native_measureText(thisPaint, text.toCharArray(), 0, text.length(), bidiFlags); 591 } 592 593 @LayoutlibDelegate native_breakText(Paint thisPaint, char[] text, int index, int count, float maxWidth, int bidiFlags, float[] measuredWidth)594 /*package*/ static int native_breakText(Paint thisPaint, char[] text, int index, int count, 595 float maxWidth, int bidiFlags, float[] measuredWidth) { 596 597 // get the delegate 598 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 599 if (delegate == null) { 600 return 0; 601 } 602 603 int inc = count > 0 ? 1 : -1; 604 605 int measureIndex = 0; 606 float measureAcc = 0; 607 for (int i = index; i != index + count; i += inc, measureIndex++) { 608 int start, end; 609 if (i < index) { 610 start = i; 611 end = index; 612 } else { 613 start = index; 614 end = i; 615 } 616 617 // measure from start to end 618 float res = delegate.measureText(text, start, end - start + 1, bidiFlags); 619 620 if (measuredWidth != null) { 621 measuredWidth[measureIndex] = res; 622 } 623 624 measureAcc += res; 625 if (res > maxWidth) { 626 // we should not return this char index, but since it's 0-based 627 // and we need to return a count, we simply return measureIndex; 628 return measureIndex; 629 } 630 631 } 632 633 return measureIndex; 634 } 635 636 @LayoutlibDelegate native_breakText(Paint thisPaint, String text, boolean measureForwards, float maxWidth, int bidiFlags, float[] measuredWidth)637 /*package*/ static int native_breakText(Paint thisPaint, String text, boolean measureForwards, 638 float maxWidth, int bidiFlags, float[] measuredWidth) { 639 return native_breakText(thisPaint, text.toCharArray(), 0, text.length(), maxWidth, 640 bidiFlags, measuredWidth); 641 } 642 643 @LayoutlibDelegate native_init()644 /*package*/ static int native_init() { 645 Paint_Delegate newDelegate = new Paint_Delegate(); 646 return sManager.addNewDelegate(newDelegate); 647 } 648 649 @LayoutlibDelegate native_initWithPaint(int paint)650 /*package*/ static int native_initWithPaint(int paint) { 651 // get the delegate from the native int. 652 Paint_Delegate delegate = sManager.getDelegate(paint); 653 if (delegate == null) { 654 return 0; 655 } 656 657 Paint_Delegate newDelegate = new Paint_Delegate(delegate); 658 return sManager.addNewDelegate(newDelegate); 659 } 660 661 @LayoutlibDelegate native_reset(int native_object)662 /*package*/ static void native_reset(int native_object) { 663 // get the delegate from the native int. 664 Paint_Delegate delegate = sManager.getDelegate(native_object); 665 if (delegate == null) { 666 return; 667 } 668 669 delegate.reset(); 670 } 671 672 @LayoutlibDelegate native_set(int native_dst, int native_src)673 /*package*/ static void native_set(int native_dst, int native_src) { 674 // get the delegate from the native int. 675 Paint_Delegate delegate_dst = sManager.getDelegate(native_dst); 676 if (delegate_dst == null) { 677 return; 678 } 679 680 // get the delegate from the native int. 681 Paint_Delegate delegate_src = sManager.getDelegate(native_src); 682 if (delegate_src == null) { 683 return; 684 } 685 686 delegate_dst.set(delegate_src); 687 } 688 689 @LayoutlibDelegate native_getStyle(int native_object)690 /*package*/ static int native_getStyle(int native_object) { 691 // get the delegate from the native int. 692 Paint_Delegate delegate = sManager.getDelegate(native_object); 693 if (delegate == null) { 694 return 0; 695 } 696 697 return delegate.mStyle; 698 } 699 700 @LayoutlibDelegate native_setStyle(int native_object, int style)701 /*package*/ static void native_setStyle(int native_object, int style) { 702 // get the delegate from the native int. 703 Paint_Delegate delegate = sManager.getDelegate(native_object); 704 if (delegate == null) { 705 return; 706 } 707 708 delegate.mStyle = style; 709 } 710 711 @LayoutlibDelegate native_getStrokeCap(int native_object)712 /*package*/ static int native_getStrokeCap(int native_object) { 713 // get the delegate from the native int. 714 Paint_Delegate delegate = sManager.getDelegate(native_object); 715 if (delegate == null) { 716 return 0; 717 } 718 719 return delegate.mCap; 720 } 721 722 @LayoutlibDelegate native_setStrokeCap(int native_object, int cap)723 /*package*/ static void native_setStrokeCap(int native_object, int cap) { 724 // get the delegate from the native int. 725 Paint_Delegate delegate = sManager.getDelegate(native_object); 726 if (delegate == null) { 727 return; 728 } 729 730 delegate.mCap = cap; 731 } 732 733 @LayoutlibDelegate native_getStrokeJoin(int native_object)734 /*package*/ static int native_getStrokeJoin(int native_object) { 735 // get the delegate from the native int. 736 Paint_Delegate delegate = sManager.getDelegate(native_object); 737 if (delegate == null) { 738 return 0; 739 } 740 741 return delegate.mJoin; 742 } 743 744 @LayoutlibDelegate native_setStrokeJoin(int native_object, int join)745 /*package*/ static void native_setStrokeJoin(int native_object, int join) { 746 // get the delegate from the native int. 747 Paint_Delegate delegate = sManager.getDelegate(native_object); 748 if (delegate == null) { 749 return; 750 } 751 752 delegate.mJoin = join; 753 } 754 755 @LayoutlibDelegate native_getFillPath(int native_object, int src, int dst)756 /*package*/ static boolean native_getFillPath(int native_object, int src, int dst) { 757 Paint_Delegate paint = sManager.getDelegate(native_object); 758 if (paint == null) { 759 return false; 760 } 761 762 Path_Delegate srcPath = Path_Delegate.getDelegate(src); 763 if (srcPath == null) { 764 return true; 765 } 766 767 Path_Delegate dstPath = Path_Delegate.getDelegate(dst); 768 if (dstPath == null) { 769 return true; 770 } 771 772 Stroke stroke = paint.getJavaStroke(); 773 Shape strokeShape = stroke.createStrokedShape(srcPath.getJavaShape()); 774 775 dstPath.setJavaShape(strokeShape); 776 777 // FIXME figure out the return value? 778 return true; 779 } 780 781 @LayoutlibDelegate native_setShader(int native_object, int shader)782 /*package*/ static int native_setShader(int native_object, int shader) { 783 // get the delegate from the native int. 784 Paint_Delegate delegate = sManager.getDelegate(native_object); 785 if (delegate == null) { 786 return shader; 787 } 788 789 delegate.mShader = Shader_Delegate.getDelegate(shader); 790 791 return shader; 792 } 793 794 @LayoutlibDelegate native_setColorFilter(int native_object, int filter)795 /*package*/ static int native_setColorFilter(int native_object, int filter) { 796 // get the delegate from the native int. 797 Paint_Delegate delegate = sManager.getDelegate(native_object); 798 if (delegate == null) { 799 return filter; 800 } 801 802 delegate.mColorFilter = ColorFilter_Delegate.getDelegate(filter);; 803 804 // since none of those are supported, display a fidelity warning right away 805 if (delegate.mColorFilter != null && delegate.mColorFilter.isSupported() == false) { 806 Bridge.getLog().fidelityWarning(LayoutLog.TAG_COLORFILTER, 807 delegate.mColorFilter.getSupportMessage(), null, null /*data*/); 808 } 809 810 return filter; 811 } 812 813 @LayoutlibDelegate native_setXfermode(int native_object, int xfermode)814 /*package*/ static int native_setXfermode(int native_object, int xfermode) { 815 // get the delegate from the native int. 816 Paint_Delegate delegate = sManager.getDelegate(native_object); 817 if (delegate == null) { 818 return xfermode; 819 } 820 821 delegate.mXfermode = Xfermode_Delegate.getDelegate(xfermode); 822 823 return xfermode; 824 } 825 826 @LayoutlibDelegate native_setPathEffect(int native_object, int effect)827 /*package*/ static int native_setPathEffect(int native_object, int effect) { 828 // get the delegate from the native int. 829 Paint_Delegate delegate = sManager.getDelegate(native_object); 830 if (delegate == null) { 831 return effect; 832 } 833 834 delegate.mPathEffect = PathEffect_Delegate.getDelegate(effect); 835 836 return effect; 837 } 838 839 @LayoutlibDelegate native_setMaskFilter(int native_object, int maskfilter)840 /*package*/ static int native_setMaskFilter(int native_object, int maskfilter) { 841 // get the delegate from the native int. 842 Paint_Delegate delegate = sManager.getDelegate(native_object); 843 if (delegate == null) { 844 return maskfilter; 845 } 846 847 delegate.mMaskFilter = MaskFilter_Delegate.getDelegate(maskfilter); 848 849 // since none of those are supported, display a fidelity warning right away 850 if (delegate.mMaskFilter != null && delegate.mMaskFilter.isSupported() == false) { 851 Bridge.getLog().fidelityWarning(LayoutLog.TAG_MASKFILTER, 852 delegate.mMaskFilter.getSupportMessage(), null, null /*data*/); 853 } 854 855 return maskfilter; 856 } 857 858 @LayoutlibDelegate native_setTypeface(int native_object, int typeface)859 /*package*/ static int native_setTypeface(int native_object, int typeface) { 860 // get the delegate from the native int. 861 Paint_Delegate delegate = sManager.getDelegate(native_object); 862 if (delegate == null) { 863 return 0; 864 } 865 866 delegate.mTypeface = Typeface_Delegate.getDelegate(typeface); 867 delegate.updateFontObject(); 868 return typeface; 869 } 870 871 @LayoutlibDelegate native_setRasterizer(int native_object, int rasterizer)872 /*package*/ static int native_setRasterizer(int native_object, int rasterizer) { 873 // get the delegate from the native int. 874 Paint_Delegate delegate = sManager.getDelegate(native_object); 875 if (delegate == null) { 876 return rasterizer; 877 } 878 879 delegate.mRasterizer = Rasterizer_Delegate.getDelegate(rasterizer); 880 881 // since none of those are supported, display a fidelity warning right away 882 if (delegate.mRasterizer != null && delegate.mRasterizer.isSupported() == false) { 883 Bridge.getLog().fidelityWarning(LayoutLog.TAG_RASTERIZER, 884 delegate.mRasterizer.getSupportMessage(), null, null /*data*/); 885 } 886 887 return rasterizer; 888 } 889 890 @LayoutlibDelegate native_getTextAlign(int native_object)891 /*package*/ static int native_getTextAlign(int native_object) { 892 // get the delegate from the native int. 893 Paint_Delegate delegate = sManager.getDelegate(native_object); 894 if (delegate == null) { 895 return 0; 896 } 897 898 return delegate.mTextAlign; 899 } 900 901 @LayoutlibDelegate native_setTextAlign(int native_object, int align)902 /*package*/ static void native_setTextAlign(int native_object, int align) { 903 // get the delegate from the native int. 904 Paint_Delegate delegate = sManager.getDelegate(native_object); 905 if (delegate == null) { 906 return; 907 } 908 909 delegate.mTextAlign = align; 910 } 911 912 @LayoutlibDelegate native_setTextLocale(int native_object, String locale)913 /*package*/ static void native_setTextLocale(int native_object, String locale) { 914 // get the delegate from the native int. 915 Paint_Delegate delegate = sManager.getDelegate(native_object); 916 if (delegate == null) { 917 return; 918 } 919 920 delegate.setTextLocale(locale); 921 } 922 923 @LayoutlibDelegate native_getTextWidths(int native_object, char[] text, int index, int count, int bidiFlags, float[] widths)924 /*package*/ static int native_getTextWidths(int native_object, char[] text, int index, 925 int count, int bidiFlags, float[] widths) { 926 // get the delegate from the native int. 927 Paint_Delegate delegate = sManager.getDelegate(native_object); 928 if (delegate == null) { 929 return 0; 930 } 931 932 if (delegate.mFonts.size() > 0) { 933 // FIXME: handle multi-char characters (see measureText) 934 float totalAdvance = 0; 935 for (int i = 0; i < count; i++) { 936 char c = text[i + index]; 937 boolean found = false; 938 for (FontInfo info : delegate.mFonts) { 939 if (info.mFont.canDisplay(c)) { 940 float adv = info.mMetrics.charWidth(c); 941 totalAdvance += adv; 942 if (widths != null) { 943 widths[i] = adv; 944 } 945 946 found = true; 947 break; 948 } 949 } 950 951 if (found == false) { 952 // no advance for this char. 953 if (widths != null) { 954 widths[i] = 0.f; 955 } 956 } 957 } 958 959 return (int) totalAdvance; 960 } 961 962 return 0; 963 } 964 965 @LayoutlibDelegate native_getTextWidths(int native_object, String text, int start, int end, int bidiFlags, float[] widths)966 /*package*/ static int native_getTextWidths(int native_object, String text, int start, 967 int end, int bidiFlags, float[] widths) { 968 return native_getTextWidths(native_object, text.toCharArray(), start, end - start, 969 bidiFlags, widths); 970 } 971 972 @LayoutlibDelegate native_getTextGlyphs(int native_object, String text, int start, int end, int contextStart, int contextEnd, int flags, char[] glyphs)973 /* package */static int native_getTextGlyphs(int native_object, String text, int start, 974 int end, int contextStart, int contextEnd, int flags, char[] glyphs) { 975 // FIXME 976 return 0; 977 } 978 979 @LayoutlibDelegate native_getTextRunAdvances(int native_object, char[] text, int index, int count, int contextIndex, int contextCount, int flags, float[] advances, int advancesIndex)980 /*package*/ static float native_getTextRunAdvances(int native_object, 981 char[] text, int index, int count, int contextIndex, int contextCount, 982 int flags, float[] advances, int advancesIndex) { 983 // get the delegate from the native int. 984 Paint_Delegate delegate = sManager.getDelegate(native_object); 985 if (delegate == null) { 986 return 0.f; 987 } 988 989 if (delegate.mFonts.size() > 0) { 990 // FIXME: handle multi-char characters (see measureText) 991 float totalAdvance = 0; 992 for (int i = 0; i < count; i++) { 993 char c = text[i + index]; 994 boolean found = false; 995 for (FontInfo info : delegate.mFonts) { 996 if (info.mFont.canDisplay(c)) { 997 float adv = info.mMetrics.charWidth(c); 998 totalAdvance += adv; 999 if (advances != null) { 1000 advances[i] = adv; 1001 } 1002 1003 found = true; 1004 break; 1005 } 1006 } 1007 1008 if (found == false) { 1009 // no advance for this char. 1010 if (advances != null) { 1011 advances[i] = 0.f; 1012 } 1013 } 1014 } 1015 1016 return totalAdvance; 1017 } 1018 1019 return 0; 1020 1021 } 1022 1023 @LayoutlibDelegate native_getTextRunAdvances(int native_object, String text, int start, int end, int contextStart, int contextEnd, int flags, float[] advances, int advancesIndex)1024 /*package*/ static float native_getTextRunAdvances(int native_object, 1025 String text, int start, int end, int contextStart, int contextEnd, 1026 int flags, float[] advances, int advancesIndex) { 1027 // FIXME: support contextStart, contextEnd and direction flag 1028 int count = end - start; 1029 char[] buffer = TemporaryBuffer.obtain(count); 1030 TextUtils.getChars(text, start, end, buffer, 0); 1031 1032 return native_getTextRunAdvances(native_object, buffer, 0, count, contextStart, 1033 contextEnd - contextStart, flags, advances, advancesIndex); 1034 } 1035 1036 @LayoutlibDelegate native_getTextRunCursor(Paint thisPaint, int native_object, char[] text, int contextStart, int contextLength, int flags, int offset, int cursorOpt)1037 /*package*/ static int native_getTextRunCursor(Paint thisPaint, int native_object, char[] text, 1038 int contextStart, int contextLength, int flags, int offset, int cursorOpt) { 1039 // FIXME 1040 Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, 1041 "Paint.getTextRunCursor is not supported.", null, null /*data*/); 1042 return 0; 1043 } 1044 1045 @LayoutlibDelegate native_getTextRunCursor(Paint thisPaint, int native_object, String text, int contextStart, int contextEnd, int flags, int offset, int cursorOpt)1046 /*package*/ static int native_getTextRunCursor(Paint thisPaint, int native_object, String text, 1047 int contextStart, int contextEnd, int flags, int offset, int cursorOpt) { 1048 // FIXME 1049 Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, 1050 "Paint.getTextRunCursor is not supported.", null, null /*data*/); 1051 return 0; 1052 } 1053 1054 @LayoutlibDelegate native_getTextPath(int native_object, int bidiFlags, char[] text, int index, int count, float x, float y, int path)1055 /*package*/ static void native_getTextPath(int native_object, int bidiFlags, 1056 char[] text, int index, int count, float x, float y, int path) { 1057 // FIXME 1058 Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, 1059 "Paint.getTextPath is not supported.", null, null /*data*/); 1060 } 1061 1062 @LayoutlibDelegate native_getTextPath(int native_object, int bidiFlags, String text, int start, int end, float x, float y, int path)1063 /*package*/ static void native_getTextPath(int native_object, int bidiFlags, 1064 String text, int start, int end, float x, float y, int path) { 1065 // FIXME 1066 Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, 1067 "Paint.getTextPath is not supported.", null, null /*data*/); 1068 } 1069 1070 @LayoutlibDelegate nativeGetStringBounds(int nativePaint, String text, int start, int end, int bidiFlags, Rect bounds)1071 /*package*/ static void nativeGetStringBounds(int nativePaint, String text, int start, 1072 int end, int bidiFlags, Rect bounds) { 1073 nativeGetCharArrayBounds(nativePaint, text.toCharArray(), start, end - start, bidiFlags, 1074 bounds); 1075 } 1076 1077 @LayoutlibDelegate nativeGetCharArrayBounds(int nativePaint, char[] text, int index, int count, int bidiFlags, Rect bounds)1078 /*package*/ static void nativeGetCharArrayBounds(int nativePaint, char[] text, int index, 1079 int count, int bidiFlags, Rect bounds) { 1080 1081 // get the delegate from the native int. 1082 Paint_Delegate delegate = sManager.getDelegate(nativePaint); 1083 if (delegate == null) { 1084 return; 1085 } 1086 1087 // FIXME should test if the main font can display all those characters. 1088 // See MeasureText 1089 if (delegate.mFonts.size() > 0) { 1090 FontInfo mainInfo = delegate.mFonts.get(0); 1091 1092 Rectangle2D rect = mainInfo.mFont.getStringBounds(text, index, index + count, 1093 delegate.mFontContext); 1094 bounds.set(0, 0, (int) rect.getWidth(), (int) rect.getHeight()); 1095 } 1096 } 1097 1098 @LayoutlibDelegate finalizer(int nativePaint)1099 /*package*/ static void finalizer(int nativePaint) { 1100 sManager.removeJavaReferenceFor(nativePaint); 1101 } 1102 1103 // ---- Private delegate/helper methods ---- 1104 Paint_Delegate()1105 /*package*/ Paint_Delegate() { 1106 reset(); 1107 } 1108 Paint_Delegate(Paint_Delegate paint)1109 private Paint_Delegate(Paint_Delegate paint) { 1110 set(paint); 1111 } 1112 set(Paint_Delegate paint)1113 private void set(Paint_Delegate paint) { 1114 mFlags = paint.mFlags; 1115 mColor = paint.mColor; 1116 mStyle = paint.mStyle; 1117 mCap = paint.mCap; 1118 mJoin = paint.mJoin; 1119 mTextAlign = paint.mTextAlign; 1120 mTypeface = paint.mTypeface; 1121 mStrokeWidth = paint.mStrokeWidth; 1122 mStrokeMiter = paint.mStrokeMiter; 1123 mTextSize = paint.mTextSize; 1124 mTextScaleX = paint.mTextScaleX; 1125 mTextSkewX = paint.mTextSkewX; 1126 mXfermode = paint.mXfermode; 1127 mColorFilter = paint.mColorFilter; 1128 mShader = paint.mShader; 1129 mPathEffect = paint.mPathEffect; 1130 mMaskFilter = paint.mMaskFilter; 1131 mRasterizer = paint.mRasterizer; 1132 mHintingMode = paint.mHintingMode; 1133 updateFontObject(); 1134 } 1135 reset()1136 private void reset() { 1137 mFlags = Paint.DEFAULT_PAINT_FLAGS; 1138 mColor = 0xFF000000; 1139 mStyle = Paint.Style.FILL.nativeInt; 1140 mCap = Paint.Cap.BUTT.nativeInt; 1141 mJoin = Paint.Join.MITER.nativeInt; 1142 mTextAlign = 0; 1143 mTypeface = Typeface_Delegate.getDelegate(Typeface.sDefaults[0].native_instance); 1144 mStrokeWidth = 1.f; 1145 mStrokeMiter = 4.f; 1146 mTextSize = 20.f; 1147 mTextScaleX = 1.f; 1148 mTextSkewX = 0.f; 1149 mXfermode = null; 1150 mColorFilter = null; 1151 mShader = null; 1152 mPathEffect = null; 1153 mMaskFilter = null; 1154 mRasterizer = null; 1155 updateFontObject(); 1156 mHintingMode = Paint.HINTING_ON; 1157 } 1158 1159 /** 1160 * Update the {@link Font} object from the typeface, text size and scaling 1161 */ 1162 @SuppressWarnings("deprecation") updateFontObject()1163 private void updateFontObject() { 1164 if (mTypeface != null) { 1165 // Get the fonts from the TypeFace object. 1166 List<Font> fonts = mTypeface.getFonts(); 1167 1168 // create new font objects as well as FontMetrics, based on the current text size 1169 // and skew info. 1170 ArrayList<FontInfo> infoList = new ArrayList<FontInfo>(fonts.size()); 1171 for (Font font : fonts) { 1172 FontInfo info = new FontInfo(); 1173 info.mFont = font.deriveFont(mTextSize); 1174 if (mTextScaleX != 1.0 || mTextSkewX != 0) { 1175 // TODO: support skew 1176 info.mFont = info.mFont.deriveFont(new AffineTransform( 1177 mTextScaleX, mTextSkewX, 0, 1, 0, 0)); 1178 } 1179 info.mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(info.mFont); 1180 1181 infoList.add(info); 1182 } 1183 1184 mFonts = Collections.unmodifiableList(infoList); 1185 } 1186 } 1187 measureText(char[] text, int index, int count, int bidiFlags)1188 /*package*/ float measureText(char[] text, int index, int count, int bidiFlags) { 1189 // TODO: find out what bidiFlags actually does. 1190 1191 // WARNING: the logic in this method is similar to Canvas_Delegate.native_drawText 1192 // Any change to this method should be reflected there as well 1193 1194 if (mFonts.size() > 0) { 1195 FontInfo mainFont = mFonts.get(0); 1196 int i = index; 1197 int lastIndex = index + count; 1198 float total = 0f; 1199 while (i < lastIndex) { 1200 // always start with the main font. 1201 int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex); 1202 if (upTo == -1) { 1203 // shortcut to exit 1204 return total + mainFont.mMetrics.charsWidth(text, i, lastIndex - i); 1205 } else if (upTo > 0) { 1206 total += mainFont.mMetrics.charsWidth(text, i, upTo - i); 1207 i = upTo; 1208 // don't call continue at this point. Since it is certain the main font 1209 // cannot display the font a index upTo (now ==i), we move on to the 1210 // fallback fonts directly. 1211 } 1212 1213 // no char supported, attempt to read the next char(s) with the 1214 // fallback font. In this case we only test the first character 1215 // and then go back to test with the main font. 1216 // Special test for 2-char characters. 1217 boolean foundFont = false; 1218 for (int f = 1 ; f < mFonts.size() ; f++) { 1219 FontInfo fontInfo = mFonts.get(f); 1220 1221 // need to check that the font can display the character. We test 1222 // differently if the char is a high surrogate. 1223 int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; 1224 upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount); 1225 if (upTo == -1) { 1226 total += fontInfo.mMetrics.charsWidth(text, i, charCount); 1227 i += charCount; 1228 foundFont = true; 1229 break; 1230 1231 } 1232 } 1233 1234 // in case no font can display the char, measure it with the main font. 1235 if (foundFont == false) { 1236 int size = Character.isHighSurrogate(text[i]) ? 2 : 1; 1237 total += mainFont.mMetrics.charsWidth(text, i, size); 1238 i += size; 1239 } 1240 } 1241 1242 return total; 1243 } 1244 1245 return 0; 1246 } 1247 getFontMetrics(FontMetrics metrics)1248 private float getFontMetrics(FontMetrics metrics) { 1249 if (mFonts.size() > 0) { 1250 java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics; 1251 if (metrics != null) { 1252 // Android expects negative ascent so we invert the value from Java. 1253 metrics.top = - javaMetrics.getMaxAscent(); 1254 metrics.ascent = - javaMetrics.getAscent(); 1255 metrics.descent = javaMetrics.getDescent(); 1256 metrics.bottom = javaMetrics.getMaxDescent(); 1257 metrics.leading = javaMetrics.getLeading(); 1258 } 1259 1260 return javaMetrics.getHeight(); 1261 } 1262 1263 return 0; 1264 } 1265 setTextLocale(String locale)1266 private void setTextLocale(String locale) { 1267 mLocale = new Locale(locale); 1268 } 1269 setFlag(Paint thisPaint, int flagMask, boolean flagValue)1270 private static void setFlag(Paint thisPaint, int flagMask, boolean flagValue) { 1271 // get the delegate from the native int. 1272 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 1273 if (delegate == null) { 1274 return; 1275 } 1276 1277 if (flagValue) { 1278 delegate.mFlags |= flagMask; 1279 } else { 1280 delegate.mFlags &= ~flagMask; 1281 } 1282 } 1283 1284 } 1285