Paint_Delegate.java revision 9f63ff263b0a97f0fa63e97136c18f6abccbfc68
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.layoutlib.bridge.DelegateManager; 20 21import android.graphics.Paint.FontMetrics; 22import android.graphics.Paint.FontMetricsInt; 23 24import java.awt.Font; 25import java.awt.Toolkit; 26import java.awt.font.FontRenderContext; 27import java.awt.geom.AffineTransform; 28import java.util.ArrayList; 29import java.util.Collections; 30import java.util.List; 31 32/** 33 * Delegate implementing the native methods of android.graphics.Paint 34 * 35 * Through the layoutlib_create tool, the original native methods of Paint have been replaced 36 * by calls to methods of the same name in this delegate class. 37 * 38 * This class behaves like the original native implementation, but in Java, keeping previously 39 * native data into its own objects and mapping them to int that are sent back and forth between 40 * it and the original Paint class. 41 * 42 * @see DelegateManager 43 * 44 */ 45public class Paint_Delegate { 46 47 /** 48 * Class associating a {@link Font} and it's {@link java.awt.FontMetrics}. 49 */ 50 public static final class FontInfo { 51 Font mFont; 52 java.awt.FontMetrics mMetrics; 53 } 54 55 // ---- delegate manager ---- 56 private static final DelegateManager<Paint_Delegate> sManager = 57 new DelegateManager<Paint_Delegate>(); 58 59 // ---- delegate helper data ---- 60 private List<FontInfo> mFonts; 61 private final FontRenderContext mFontContext = new FontRenderContext( 62 new AffineTransform(), true, true); 63 64 // ---- delegate data ---- 65 private int mFlags; 66 private int mColor; 67 private int mStyle; 68 private int mCap; 69 private int mJoin; 70 private int mAlign; 71 private int mTypeface; 72 private float mStrokeWidth; 73 private float mStrokeMiter; 74 private float mTextSize; 75 private float mTextScaleX; 76 private float mTextSkewX; 77 78 79 // ---- Public Helper methods ---- 80 81 /** 82 * Returns the list of {@link Font} objects. The first item is the main font, the rest 83 * are fall backs for characters not present in the main font. 84 */ 85 public List<FontInfo> getFonts() { 86 return mFonts; 87 } 88 89 90 // ---- native methods ---- 91 92 /*package*/ static int getFlags(Paint thisPaint) { 93 // get the delegate from the native int. 94 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 95 if (delegate == null) { 96 assert false; 97 return 0; 98 } 99 100 return delegate.mFlags; 101 } 102 103 /*package*/ static void setFlags(Paint thisPaint, int flags) { 104 // get the delegate from the native int. 105 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 106 if (delegate == null) { 107 assert false; 108 return; 109 } 110 111 delegate.mFlags = flags; 112 } 113 114 /*package*/ static void setFilterBitmap(Paint thisPaint, boolean filter) { 115 // FIXME 116 throw new UnsupportedOperationException(); 117 } 118 119 /*package*/ static void setAntiAlias(Paint thisPaint, boolean aa) { 120 setFlag(thisPaint, Paint.ANTI_ALIAS_FLAG, aa); 121 } 122 123 /*package*/ static void setSubpixelText(Paint thisPaint, boolean subpixelText) { 124 setFlag(thisPaint, Paint.SUBPIXEL_TEXT_FLAG, subpixelText); 125 } 126 127 /*package*/ static void setUnderlineText(Paint thisPaint, boolean underlineText) { 128 setFlag(thisPaint, Paint.UNDERLINE_TEXT_FLAG, underlineText); 129 } 130 131 /*package*/ static void setStrikeThruText(Paint thisPaint, boolean strikeThruText) { 132 setFlag(thisPaint, Paint.STRIKE_THRU_TEXT_FLAG, strikeThruText); 133 } 134 135 /*package*/ static void setFakeBoldText(Paint thisPaint, boolean fakeBoldText) { 136 setFlag(thisPaint, Paint.FAKE_BOLD_TEXT_FLAG, fakeBoldText); 137 } 138 139 /*package*/ static void setDither(Paint thisPaint, boolean dither) { 140 setFlag(thisPaint, Paint.DITHER_FLAG, dither); 141 } 142 143 /*package*/ static void setLinearText(Paint thisPaint, boolean linearText) { 144 setFlag(thisPaint, Paint.LINEAR_TEXT_FLAG, linearText); 145 } 146 147 /*package*/ static int getColor(Paint thisPaint) { 148 // get the delegate from the native int. 149 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 150 if (delegate == null) { 151 assert false; 152 return 0; 153 } 154 155 return delegate.mColor; 156 } 157 158 /*package*/ static void setColor(Paint thisPaint, int color) { 159 // get the delegate from the native int. 160 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 161 if (delegate == null) { 162 assert false; 163 return; 164 } 165 166 delegate.mColor = color; 167 } 168 169 /*package*/ static int getAlpha(Paint thisPaint) { 170 // get the delegate from the native int. 171 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 172 if (delegate == null) { 173 assert false; 174 return 0; 175 } 176 177 return delegate.mColor >>> 24; 178 } 179 180 /*package*/ static void setAlpha(Paint thisPaint, int a) { 181 // get the delegate from the native int. 182 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 183 if (delegate == null) { 184 assert false; 185 return; 186 } 187 188 delegate.mColor = (a << 24) | (delegate.mColor & 0x00FFFFFF); 189 } 190 191 /*package*/ static float getStrokeWidth(Paint thisPaint) { 192 // get the delegate from the native int. 193 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 194 if (delegate == null) { 195 assert false; 196 return 1.f; 197 } 198 199 return delegate.mStrokeWidth; 200 } 201 202 /*package*/ static void setStrokeWidth(Paint thisPaint, float width) { 203 // get the delegate from the native int. 204 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 205 if (delegate == null) { 206 assert false; 207 return; 208 } 209 210 delegate.mStrokeWidth = width; 211 } 212 213 /*package*/ static float getStrokeMiter(Paint thisPaint) { 214 // get the delegate from the native int. 215 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 216 if (delegate == null) { 217 assert false; 218 return 1.f; 219 } 220 221 return delegate.mStrokeMiter; 222 } 223 224 /*package*/ static void setStrokeMiter(Paint thisPaint, float miter) { 225 // get the delegate from the native int. 226 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 227 if (delegate == null) { 228 assert false; 229 return; 230 } 231 232 delegate.mStrokeMiter = miter; 233 } 234 235 /*package*/ static void nSetShadowLayer(Paint thisPaint, float radius, float dx, float dy, 236 int color) { 237 // FIXME 238 throw new UnsupportedOperationException(); 239 } 240 241 /*package*/ static float getTextSize(Paint thisPaint) { 242 // get the delegate from the native int. 243 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 244 if (delegate == null) { 245 assert false; 246 return 1.f; 247 } 248 249 return delegate.mTextSize; 250 } 251 252 /*package*/ static void setTextSize(Paint thisPaint, float textSize) { 253 // get the delegate from the native int. 254 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 255 if (delegate == null) { 256 assert false; 257 return; 258 } 259 260 delegate.mTextSize = textSize; 261 } 262 263 /*package*/ static float getTextScaleX(Paint thisPaint) { 264 // get the delegate from the native int. 265 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 266 if (delegate == null) { 267 assert false; 268 return 1.f; 269 } 270 271 return delegate.mTextScaleX; 272 } 273 274 /*package*/ static void setTextScaleX(Paint thisPaint, float scaleX) { 275 // get the delegate from the native int. 276 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 277 if (delegate == null) { 278 assert false; 279 return; 280 } 281 282 delegate.mTextScaleX = scaleX; 283 } 284 285 /*package*/ static float getTextSkewX(Paint thisPaint) { 286 // get the delegate from the native int. 287 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 288 if (delegate == null) { 289 assert false; 290 return 1.f; 291 } 292 293 return delegate.mTextSkewX; 294 } 295 296 /*package*/ static void setTextSkewX(Paint thisPaint, float skewX) { 297 // get the delegate from the native int. 298 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 299 if (delegate == null) { 300 assert false; 301 return; 302 } 303 304 delegate.mTextSkewX = skewX; 305 } 306 307 /*package*/ static float ascent(Paint thisPaint) { 308 // FIXME 309 throw new UnsupportedOperationException(); 310 } 311 312 /*package*/ static float descent(Paint thisPaint) { 313 // FIXME 314 throw new UnsupportedOperationException(); 315 } 316 317 /*package*/ static float getFontMetrics(Paint thisPaint, FontMetrics metrics) { 318 // FIXME 319 throw new UnsupportedOperationException(); 320 } 321 322 /*package*/ static int getFontMetricsInt(Paint thisPaint, FontMetricsInt fmi) { 323 // FIXME 324 throw new UnsupportedOperationException(); 325 } 326 327 /*package*/ static float native_measureText(Paint thisPaint, char[] text, int index, 328 int count) { 329 // WARNING: the logic in this method is similar to Canvas.drawText. 330 // Any change to this method should be reflected in Canvas.drawText 331 332 // get the delegate 333 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 334 if (delegate == null) { 335 assert false; 336 return 0; 337 } 338 339 if (delegate.mFonts.size() > 0) { 340 FontInfo mainFont = delegate.mFonts.get(0); 341 int i = index; 342 int lastIndex = index + count; 343 float total = 0f; 344 while (i < lastIndex) { 345 // always start with the main font. 346 int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex); 347 if (upTo == -1) { 348 // shortcut to exit 349 return total + mainFont.mMetrics.charsWidth(text, i, lastIndex - i); 350 } else if (upTo > 0) { 351 total += mainFont.mMetrics.charsWidth(text, i, upTo - i); 352 i = upTo; 353 // don't call continue at this point. Since it is certain the main font 354 // cannot display the font a index upTo (now ==i), we move on to the 355 // fallback fonts directly. 356 } 357 358 // no char supported, attempt to read the next char(s) with the 359 // fallback font. In this case we only test the first character 360 // and then go back to test with the main font. 361 // Special test for 2-char characters. 362 boolean foundFont = false; 363 for (int f = 1 ; f < delegate.mFonts.size() ; f++) { 364 FontInfo fontInfo = delegate.mFonts.get(f); 365 366 // need to check that the font can display the character. We test 367 // differently if the char is a high surrogate. 368 int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; 369 upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount); 370 if (upTo == -1) { 371 total += fontInfo.mMetrics.charsWidth(text, i, charCount); 372 i += charCount; 373 foundFont = true; 374 break; 375 376 } 377 } 378 379 // in case no font can display the char, measure it with the main font. 380 if (foundFont == false) { 381 int size = Character.isHighSurrogate(text[i]) ? 2 : 1; 382 total += mainFont.mMetrics.charsWidth(text, i, size); 383 i += size; 384 } 385 } 386 } 387 388 return 0; 389 } 390 391 /*package*/ static float native_measureText(Paint thisPaint, String text, int start, int end) { 392 return native_measureText(thisPaint, text.toCharArray(), start, end - start); 393 } 394 395 /*package*/ static float native_measureText(Paint thisPaint, String text) { 396 return native_measureText(thisPaint, text.toCharArray(), 0, text.length()); 397 } 398 399 /*package*/ static int native_breakText(Paint thisPaint, char[] text, int index, int count, 400 float maxWidth, float[] measuredWidth) { 401 // FIXME 402 throw new UnsupportedOperationException(); 403 } 404 405 /*package*/ static int native_breakText(Paint thisPaint, String text, boolean measureForwards, 406 float maxWidth, float[] measuredWidth) { 407 // FIXME 408 throw new UnsupportedOperationException(); 409 } 410 411 412 /*package*/ static int native_init() { 413 Paint_Delegate newDelegate = new Paint_Delegate(); 414 return sManager.addDelegate(newDelegate); 415 } 416 417 /*package*/ static int native_initWithPaint(int paint) { 418 // get the delegate from the native int. 419 Paint_Delegate delegate = sManager.getDelegate(paint); 420 if (delegate == null) { 421 assert false; 422 return 0; 423 } 424 425 Paint_Delegate newDelegate = new Paint_Delegate(delegate); 426 return sManager.addDelegate(newDelegate); 427 } 428 429 /*package*/ static void native_reset(int native_object) { 430 // get the delegate from the native int. 431 Paint_Delegate delegate = sManager.getDelegate(native_object); 432 if (delegate == null) { 433 assert false; 434 return; 435 } 436 437 delegate.reset(); 438 } 439 440 /*package*/ static void native_set(int native_dst, int native_src) { 441 // get the delegate from the native int. 442 Paint_Delegate delegate_dst = sManager.getDelegate(native_dst); 443 if (delegate_dst == null) { 444 assert false; 445 return; 446 } 447 448 // get the delegate from the native int. 449 Paint_Delegate delegate_src = sManager.getDelegate(native_src); 450 if (delegate_src == null) { 451 assert false; 452 return; 453 } 454 455 delegate_dst.set(delegate_src); 456 } 457 458 /*package*/ static int native_getStyle(int native_object) { 459 // get the delegate from the native int. 460 Paint_Delegate delegate = sManager.getDelegate(native_object); 461 if (delegate == null) { 462 assert false; 463 return 0; 464 } 465 466 return delegate.mStyle; 467 } 468 469 /*package*/ static void native_setStyle(int native_object, int style) { 470 // get the delegate from the native int. 471 Paint_Delegate delegate = sManager.getDelegate(native_object); 472 if (delegate == null) { 473 assert false; 474 return; 475 } 476 477 delegate.mStyle = style; 478 } 479 480 /*package*/ static int native_getStrokeCap(int native_object) { 481 // get the delegate from the native int. 482 Paint_Delegate delegate = sManager.getDelegate(native_object); 483 if (delegate == null) { 484 assert false; 485 return 0; 486 } 487 488 return delegate.mCap; 489 } 490 491 /*package*/ static void native_setStrokeCap(int native_object, int cap) { 492 // get the delegate from the native int. 493 Paint_Delegate delegate = sManager.getDelegate(native_object); 494 if (delegate == null) { 495 assert false; 496 return; 497 } 498 499 delegate.mCap = cap; 500 } 501 502 /*package*/ static int native_getStrokeJoin(int native_object) { 503 // get the delegate from the native int. 504 Paint_Delegate delegate = sManager.getDelegate(native_object); 505 if (delegate == null) { 506 assert false; 507 return 0; 508 } 509 510 return delegate.mJoin; 511 } 512 513 /*package*/ static void native_setStrokeJoin(int native_object, int join) { 514 // get the delegate from the native int. 515 Paint_Delegate delegate = sManager.getDelegate(native_object); 516 if (delegate == null) { 517 assert false; 518 return; 519 } 520 521 delegate.mJoin = join; 522 } 523 524 /*package*/ static boolean native_getFillPath(int native_object, int src, int dst) { 525 // FIXME 526 throw new UnsupportedOperationException(); 527 } 528 529 /*package*/ static int native_setShader(int native_object, int shader) { 530 // FIXME 531 throw new UnsupportedOperationException(); 532 } 533 534 /*package*/ static int native_setColorFilter(int native_object, int filter) { 535 // FIXME 536 throw new UnsupportedOperationException(); 537 } 538 539 /*package*/ static int native_setXfermode(int native_object, int xfermode) { 540 // FIXME 541 throw new UnsupportedOperationException(); 542 } 543 544 /*package*/ static int native_setPathEffect(int native_object, int effect) { 545 // FIXME 546 throw new UnsupportedOperationException(); 547 } 548 549 /*package*/ static int native_setMaskFilter(int native_object, int maskfilter) { 550 // FIXME 551 throw new UnsupportedOperationException(); 552 } 553 554 /*package*/ static int native_setTypeface(int native_object, int typeface) { 555 // get the delegate from the native int. 556 Paint_Delegate delegate = sManager.getDelegate(native_object); 557 if (delegate == null) { 558 assert false; 559 return 0; 560 } 561 562 return delegate.mTypeface = typeface; 563 } 564 565 /*package*/ static int native_setRasterizer(int native_object, int rasterizer) { 566 // FIXME 567 throw new UnsupportedOperationException(); 568 } 569 570 571 /*package*/ static int native_getTextAlign(int native_object) { 572 // get the delegate from the native int. 573 Paint_Delegate delegate = sManager.getDelegate(native_object); 574 if (delegate == null) { 575 assert false; 576 return 0; 577 } 578 579 return delegate.mAlign; 580 } 581 582 /*package*/ static void native_setTextAlign(int native_object, int align) { 583 // get the delegate from the native int. 584 Paint_Delegate delegate = sManager.getDelegate(native_object); 585 if (delegate == null) { 586 assert false; 587 return; 588 } 589 590 delegate.mAlign = align; 591 } 592 593 /*package*/ static float native_getFontMetrics(int native_paint, FontMetrics metrics) { 594 // FIXME 595 throw new UnsupportedOperationException(); 596 } 597 598 /*package*/ static int native_getTextWidths(int native_object, char[] text, int index, 599 int count, float[] widths) { 600 // FIXME 601 throw new UnsupportedOperationException(); 602 } 603 604 /*package*/ static int native_getTextWidths(int native_object, String text, int start, 605 int end, float[] widths) { 606 // FIXME 607 throw new UnsupportedOperationException(); 608 } 609 610 /*package*/ static float native_getTextRunAdvances(int native_object, 611 char[] text, int index, int count, int contextIndex, int contextCount, 612 int flags, float[] advances, int advancesIndex) { 613 // FIXME 614 throw new UnsupportedOperationException(); 615 } 616 617 /*package*/ static float native_getTextRunAdvances(int native_object, 618 String text, int start, int end, int contextStart, int contextEnd, 619 int flags, float[] advances, int advancesIndex) { 620 // FIXME 621 throw new UnsupportedOperationException(); 622 } 623 624 /*package*/ static int native_getTextRunCursor(Paint thisPaint, int native_object, char[] text, 625 int contextStart, int contextLength, int flags, int offset, int cursorOpt) { 626 // FIXME 627 throw new UnsupportedOperationException(); 628 } 629 630 /*package*/ static int native_getTextRunCursor(Paint thisPaint, int native_object, String text, 631 int contextStart, int contextEnd, int flags, int offset, int cursorOpt) { 632 // FIXME 633 throw new UnsupportedOperationException(); 634 } 635 636 /*package*/ static void native_getTextPath(int native_object, int bidiFlags, 637 char[] text, int index, int count, float x, float y, int path) { 638 // FIXME 639 throw new UnsupportedOperationException(); 640 } 641 642 /*package*/ static void native_getTextPath(int native_object, int bidiFlags, 643 String text, int start, int end, float x, float y, int path) { 644 // FIXME 645 throw new UnsupportedOperationException(); 646 } 647 648 /*package*/ static void nativeGetStringBounds(int nativePaint, String text, int start, 649 int end, Rect bounds) { 650 // FIXME 651 throw new UnsupportedOperationException(); 652 } 653 654 /*package*/ static void nativeGetCharArrayBounds(int nativePaint, char[] text, int index, 655 int count, Rect bounds) { 656 // FIXME 657 throw new UnsupportedOperationException(); 658 } 659 660 /*package*/ static void finalizer(int nativePaint) { 661 sManager.removeDelegate(nativePaint); 662 } 663 664 // ---- Private delegate/helper methods ---- 665 666 private Paint_Delegate() { 667 reset(); 668 669 mTypeface = Typeface.sDefaults[0].native_instance; 670 updateFontObject(); 671 } 672 673 private Paint_Delegate(Paint_Delegate paint) { 674 set(paint); 675 updateFontObject(); 676 } 677 678 private void set(Paint_Delegate paint) { 679 mFlags = paint.mFlags; 680 mColor = paint.mColor; 681 mStyle = paint.mStyle; 682 mCap = paint.mCap; 683 mJoin = paint.mJoin; 684 mAlign = paint.mAlign; 685 mTypeface = paint.mTypeface; 686 mStrokeWidth = paint.mStrokeWidth; 687 mStrokeMiter = paint.mStrokeMiter; 688 mTextSize = paint.mTextSize; 689 mTextScaleX = paint.mTextScaleX; 690 mTextSkewX = paint.mTextSkewX; 691 } 692 693 private void reset() { 694 mFlags = Paint.DEFAULT_PAINT_FLAGS; 695 mColor = 0; 696 mStyle = 0; 697 mCap = 0; 698 mJoin = 0; 699 mAlign = 0; 700 mTypeface = 0; 701 mStrokeWidth = 1.f; 702 mStrokeMiter = 2.f; 703 mTextSize = 20.f; 704 mTextScaleX = 1.f; 705 mTextSkewX = 0.f; 706 } 707 708 /** 709 * Update the {@link Font} object from the typeface, text size and scaling 710 */ 711 private void updateFontObject() { 712 if (mTypeface != 0) { 713 // Get the fonts from the TypeFace object. 714 List<Font> fonts = Typeface_Delegate.getFonts(mTypeface); 715 716 // create new font objects as well as FontMetrics, based on the current text size 717 // and skew info. 718 ArrayList<FontInfo> infoList = new ArrayList<FontInfo>(fonts.size()); 719 for (Font font : fonts) { 720 FontInfo info = new FontInfo(); 721 info.mFont = font.deriveFont(mTextSize); 722 if (mTextScaleX != 1.0 || mTextSkewX != 0) { 723 // TODO: support skew 724 info.mFont = info.mFont.deriveFont(new AffineTransform( 725 mTextScaleX, mTextSkewX, 0, 0, 1, 0)); 726 } 727 info.mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(info.mFont); 728 729 infoList.add(info); 730 } 731 732 mFonts = Collections.unmodifiableList(infoList); 733 } 734 } 735 736 private static void setFlag(Paint thisPaint, int flagMask, boolean flagValue) { 737 // get the delegate from the native int. 738 Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint); 739 if (delegate == null) { 740 assert false; 741 return; 742 } 743 744 if (flagValue) { 745 delegate.mFlags |= flagMask; 746 } else { 747 delegate.mFlags &= ~flagMask; 748 } 749 } 750} 751