diff options
author | Gilles Debunne <debunne@google.com> | 2011-12-05 15:54:00 -0800 |
---|---|---|
committer | Gilles Debunne <debunne@google.com> | 2012-01-12 15:56:37 -0800 |
commit | b35ab7b72967adcfd01cec483a705dafe8b951d1 (patch) | |
tree | 3c82a2653c3a9854ddcb92f4528ddb8e464f54e6 | |
parent | cf5cecd854376b1a08b3f28a34e7555b44c594f3 (diff) | |
download | frameworks_base-b35ab7b72967adcfd01cec483a705dafe8b951d1.zip frameworks_base-b35ab7b72967adcfd01cec483a705dafe8b951d1.tar.gz frameworks_base-b35ab7b72967adcfd01cec483a705dafe8b951d1.tar.bz2 |
Sub display list in TextView
TextView uses a sub-display list to 'cache' the rendering of its
text. This saves time when drawing an editable text, where the blinking
cursor forces a re-draw twice per second, which creates pauses during
scrolling.
Added a sub-display list invalidation when an appearance span is
modified/added/removed.
Also added an invalidation of the display list when selection range
is changed.
Change-Id: I41e8068a12902b8a745c5bb77de8c77def76a270
-rw-r--r-- | core/java/android/view/DisplayList.java | 10 | ||||
-rw-r--r-- | core/java/android/view/GLES20Canvas.java | 4 | ||||
-rw-r--r-- | core/java/android/view/GLES20DisplayList.java | 10 | ||||
-rw-r--r-- | core/java/android/view/HardwareCanvas.java | 6 | ||||
-rw-r--r-- | core/java/android/view/HardwareRenderer.java | 4 | ||||
-rw-r--r-- | core/java/android/view/View.java | 13 | ||||
-rw-r--r-- | core/java/android/widget/TextView.java | 61 |
7 files changed, 72 insertions, 36 deletions
diff --git a/core/java/android/view/DisplayList.java b/core/java/android/view/DisplayList.java index 8f4ece0..fec0d4b 100644 --- a/core/java/android/view/DisplayList.java +++ b/core/java/android/view/DisplayList.java @@ -32,20 +32,20 @@ public abstract class DisplayList { * * @return A canvas to record drawing operations. */ - abstract HardwareCanvas start(); + public abstract HardwareCanvas start(); /** * Ends the recording for this display list. A display list cannot be * replayed if recording is not finished. */ - abstract void end(); + public abstract void end(); /** * Invalidates the display list, indicating that it should be repopulated * with new drawing commands prior to being used again. Calling this method * causes calls to {@link #isValid()} to return <code>false</code>. */ - abstract void invalidate(); + public abstract void invalidate(); /** * Returns whether the display list is currently usable. If this returns false, @@ -53,12 +53,12 @@ public abstract class DisplayList { * * @return boolean true if the display list is able to be replayed, false otherwise. */ - abstract boolean isValid(); + public abstract boolean isValid(); /** * Return the amount of memory used by this display list. * * @return The size of this display list in bytes */ - abstract int getSize(); + public abstract int getSize(); } diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index 43a451d..8a9be85 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -247,7 +247,7 @@ class GLES20Canvas extends HardwareCanvas { private static native void nDisableVsync(); @Override - void onPreDraw(Rect dirty) { + public void onPreDraw(Rect dirty) { if (dirty != null) { nPrepareDirty(mRenderer, dirty.left, dirty.top, dirty.right, dirty.bottom, mOpaque); } else { @@ -260,7 +260,7 @@ class GLES20Canvas extends HardwareCanvas { boolean opaque); @Override - void onPostDraw() { + public void onPostDraw() { nFinish(mRenderer); } diff --git a/core/java/android/view/GLES20DisplayList.java b/core/java/android/view/GLES20DisplayList.java index 4ca5e98..0cb9449 100644 --- a/core/java/android/view/GLES20DisplayList.java +++ b/core/java/android/view/GLES20DisplayList.java @@ -43,7 +43,7 @@ class GLES20DisplayList extends DisplayList { } @Override - HardwareCanvas start() { + public HardwareCanvas start() { if (mCanvas != null) { throw new IllegalStateException("Recording has already started"); } @@ -55,7 +55,7 @@ class GLES20DisplayList extends DisplayList { } @Override - void invalidate() { + public void invalidate() { if (mCanvas != null) { mCanvas.recycle(); mCanvas = null; @@ -64,12 +64,12 @@ class GLES20DisplayList extends DisplayList { } @Override - boolean isValid() { + public boolean isValid() { return mValid; } @Override - void end() { + public void end() { if (mCanvas != null) { if (mFinalizer != null) { mCanvas.end(mFinalizer.mNativeDisplayList); @@ -83,7 +83,7 @@ class GLES20DisplayList extends DisplayList { } @Override - int getSize() { + public int getSize() { if (mFinalizer == null) return 0; return GLES20Canvas.getDisplayListSize(mFinalizer.mNativeDisplayList); } diff --git a/core/java/android/view/HardwareCanvas.java b/core/java/android/view/HardwareCanvas.java index 23b3abc..cbdbbde 100644 --- a/core/java/android/view/HardwareCanvas.java +++ b/core/java/android/view/HardwareCanvas.java @@ -42,12 +42,12 @@ public abstract class HardwareCanvas extends Canvas { * * @param dirty The dirty rectangle to update, can be null. */ - abstract void onPreDraw(Rect dirty); + public abstract void onPreDraw(Rect dirty); /** * Invoked after all drawing operation have been performed. */ - abstract void onPostDraw(); + public abstract void onPostDraw(); /** * Draws the specified display list onto this canvas. @@ -61,7 +61,7 @@ public abstract class HardwareCanvas extends Canvas { * @return True if the content of the display list requires another * drawing pass (invalidate()), false otherwise */ - abstract boolean drawDisplayList(DisplayList displayList, int width, int height, Rect dirty); + public abstract boolean drawDisplayList(DisplayList displayList, int width, int height, Rect dirty); /** * Outputs the specified display list to the log. This method exists for use by diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index 3f793bb..b7ccc7a 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -276,7 +276,7 @@ public abstract class HardwareRenderer { * * @return A new display list. */ - abstract DisplayList createDisplayList(); + public abstract DisplayList createDisplayList(); /** * Creates a new hardware layer. A hardware layer built by calling this @@ -1083,7 +1083,7 @@ public abstract class HardwareRenderer { } @Override - DisplayList createDisplayList() { + public DisplayList createDisplayList() { return new GLES20DisplayList(); } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 64f862a..e280286 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -10433,6 +10433,19 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal } /** + * @return The HardwareRenderer associated with that view or null if hardware rendering + * is not supported or this this has not been attached to a window. + * + * @hide + */ + public HardwareRenderer getHardwareRenderer() { + if (mAttachInfo != null) { + return mAttachInfo.mHardwareRenderer; + } + return null; + } + + /** * <p>Returns a display list that can be used to draw this view again * without executing its draw method.</p> * diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 3dd7a9f..acea1a1 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -84,6 +84,7 @@ import android.text.method.TimeKeyListener; import android.text.method.TransformationMethod; import android.text.method.TransformationMethod2; import android.text.method.WordIterator; +import android.text.style.CharacterStyle; import android.text.style.ClickableSpan; import android.text.style.EasyEditSpan; import android.text.style.ParagraphStyle; @@ -101,9 +102,11 @@ import android.util.Log; import android.util.TypedValue; import android.view.ActionMode; import android.view.ActionMode.Callback; +import android.view.DisplayList; import android.view.DragEvent; import android.view.Gravity; import android.view.HapticFeedbackConstants; +import android.view.HardwareCanvas; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -283,6 +286,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } private Drawables mDrawables; + private DisplayList mTextDisplayList; + private boolean mTextDisplayListIsValid; + private CharSequence mError; private boolean mErrorWasChanged; private ErrorPopup mPopup; @@ -4520,6 +4526,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener resetResolvedDrawables(); + if (mTextDisplayList != null) { + mTextDisplayList.invalidate(); + } + if (mSpellChecker != null) { mSpellChecker.closeSession(); // Forces the creation of a new SpellChecker next time this window is created. @@ -4970,17 +4980,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - /* Comment out until we decide what to do about animations - boolean isLinearTextOn = false; - if (currentTransformation != null) { - isLinearTextOn = mTextPaint.isLinearTextOn(); - Matrix m = currentTransformation.getMatrix(); - if (!m.isIdentity()) { - // mTextPaint.setLinearTextOn(true); - } - } - */ - final InputMethodState ims = mInputMethodState; final int cursorOffsetVertical = voffsetCursor - voffsetText; if (ims != null && ims.mBatchEditNesting == 0) { @@ -5038,18 +5037,38 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener highlight = null; } - layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical); + if (canHaveDisplayList() && canvas.isHardwareAccelerated()) { + final int width = mRight - mLeft; + final int height = mBottom - mTop; - if (mMarquee != null && mMarquee.shouldDrawGhost()) { - canvas.translate((int) mMarquee.getGhostOffset(), 0.0f); + if (mTextDisplayList == null || !mTextDisplayList.isValid() || + !mTextDisplayListIsValid) { + if (mTextDisplayList == null) { + mTextDisplayList = getHardwareRenderer().createDisplayList(); + } + + final HardwareCanvas hardwareCanvas = mTextDisplayList.start(); + try { + hardwareCanvas.setViewport(width, height); + // The dirty rect should always be null for a display list + hardwareCanvas.onPreDraw(null); + layout.draw(hardwareCanvas, highlight, mHighlightPaint, cursorOffsetVertical); + } finally { + hardwareCanvas.onPostDraw(); + mTextDisplayList.end(); + mTextDisplayListIsValid = true; + } + } + ((HardwareCanvas) canvas).drawDisplayList(mTextDisplayList, + mScrollX + width, mScrollY + height, null); + } else { layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical); } - /* Comment out until we decide what to do about animations - if (currentTransformation != null) { - mTextPaint.setLinearTextOn(isLinearTextOn); + if (mMarquee != null && mMarquee.shouldDrawGhost()) { + canvas.translate((int) mMarquee.getGhostOffset(), 0.0f); + layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical); } - */ canvas.restore(); } @@ -7562,6 +7581,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ protected void onSelectionChanged(int selStart, int selEnd) { sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED); + mTextDisplayListIsValid = false; } /** @@ -7641,6 +7661,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } updateSpellCheckSpans(start, start + after, false); + mTextDisplayListIsValid = false; // Hide the controllers as soon as text is modified (typing, procedural...) // We do not hide the span controllers, since they can be added when a new text is @@ -7743,7 +7764,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - if (what instanceof UpdateAppearance || what instanceof ParagraphStyle) { + if (what instanceof UpdateAppearance || what instanceof ParagraphStyle || + what instanceof CharacterStyle) { if (ims == null || ims.mBatchEditNesting == 0) { invalidate(); mHighlightPathBogus = true; @@ -7751,6 +7773,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } else { ims.mContentChanged = true; } + mTextDisplayListIsValid = false; } if (MetaKeyKeyListener.isMetaTracker(buf, what)) { |