From 157aafcbee0eabda798a3be406ccc4200ee86756 Mon Sep 17 00:00:00 2001 From: Gilles Debunne Date: Thu, 19 Apr 2012 17:21:57 -0700 Subject: [PATCH] TextView's sub display lists have tighten bounds An editable TextView caches text rendering inside an adaptive number of sub display lists. The bounds of these use to be those of the entire View. This CL creates block display lists with tighten bounds, so that (a still-to-be-implemented) quick rejection can occur. Also cleaned-up the contradictory translations that were used to handle the TextView's internal scroll and removed the invalidation of display lists in that case. TODO: When internal scroll sets a tighter clipping rect, quick reject the creation and display of the clipped display lists. Also renamed blockEnds to a more explicit blockEndLines. Change-Id: I7d79bea78d06d19b6935aef75ff7aa7df2594050 --- core/java/android/text/DynamicLayout.java | 52 +++++++++---------- core/java/android/widget/Editor.java | 43 ++++++++------- .../android/text/DynamicLayoutBlocksTest.java | 10 ++-- 3 files changed, 55 insertions(+), 50 deletions(-) diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java index ff5a4679c6d05..dc58ef27280e8 100644 --- a/core/java/android/text/DynamicLayout.java +++ b/core/java/android/text/DynamicLayout.java @@ -117,7 +117,7 @@ public class DynamicLayout extends Layout mObjects = new PackedObjectVector(1); - mBlockEnds = new int[] { 0 }; + mBlockEndLines = new int[] { 0 }; mBlockIndices = new int[] { INVALID_BLOCK_INDEX }; mNumberOfBlocks = 1; @@ -391,23 +391,23 @@ public class DynamicLayout extends Layout int firstBlock = -1; int lastBlock = -1; for (int i = 0; i < mNumberOfBlocks; i++) { - if (mBlockEnds[i] >= startLine) { + if (mBlockEndLines[i] >= startLine) { firstBlock = i; break; } } for (int i = firstBlock; i < mNumberOfBlocks; i++) { - if (mBlockEnds[i] >= endLine) { + if (mBlockEndLines[i] >= endLine) { lastBlock = i; break; } } - final int lastBlockEndLine = mBlockEnds[lastBlock]; + final int lastBlockEndLine = mBlockEndLines[lastBlock]; boolean createBlockBefore = startLine > (firstBlock == 0 ? 0 : - mBlockEnds[firstBlock - 1] + 1); + mBlockEndLines[firstBlock - 1] + 1); boolean createBlock = newLineCount > 0; - boolean createBlockAfter = endLine < mBlockEnds[lastBlock]; + boolean createBlockAfter = endLine < mBlockEndLines[lastBlock]; int numAddedBlocks = 0; if (createBlockBefore) numAddedBlocks++; @@ -419,27 +419,27 @@ public class DynamicLayout extends Layout if (newNumberOfBlocks == 0) { // Even when text is empty, there is actually one line and hence one block - mBlockEnds[0] = 0; + mBlockEndLines[0] = 0; mBlockIndices[0] = INVALID_BLOCK_INDEX; mNumberOfBlocks = 1; return; } - if (newNumberOfBlocks > mBlockEnds.length) { + if (newNumberOfBlocks > mBlockEndLines.length) { final int newSize = ArrayUtils.idealIntArraySize(newNumberOfBlocks); - int[] blockEnds = new int[newSize]; + int[] blockEndLines = new int[newSize]; int[] blockIndices = new int[newSize]; - System.arraycopy(mBlockEnds, 0, blockEnds, 0, firstBlock); + System.arraycopy(mBlockEndLines, 0, blockEndLines, 0, firstBlock); System.arraycopy(mBlockIndices, 0, blockIndices, 0, firstBlock); - System.arraycopy(mBlockEnds, lastBlock + 1, - blockEnds, firstBlock + numAddedBlocks, mNumberOfBlocks - lastBlock - 1); + System.arraycopy(mBlockEndLines, lastBlock + 1, + blockEndLines, firstBlock + numAddedBlocks, mNumberOfBlocks - lastBlock - 1); System.arraycopy(mBlockIndices, lastBlock + 1, blockIndices, firstBlock + numAddedBlocks, mNumberOfBlocks - lastBlock - 1); - mBlockEnds = blockEnds; + mBlockEndLines = blockEndLines; mBlockIndices = blockIndices; } else { - System.arraycopy(mBlockEnds, lastBlock + 1, - mBlockEnds, firstBlock + numAddedBlocks, mNumberOfBlocks - lastBlock - 1); + System.arraycopy(mBlockEndLines, lastBlock + 1, + mBlockEndLines, firstBlock + numAddedBlocks, mNumberOfBlocks - lastBlock - 1); System.arraycopy(mBlockIndices, lastBlock + 1, mBlockIndices, firstBlock + numAddedBlocks, mNumberOfBlocks - lastBlock - 1); } @@ -447,24 +447,24 @@ public class DynamicLayout extends Layout mNumberOfBlocks = newNumberOfBlocks; final int deltaLines = newLineCount - (endLine - startLine + 1); for (int i = firstBlock + numAddedBlocks; i < mNumberOfBlocks; i++) { - mBlockEnds[i] += deltaLines; + mBlockEndLines[i] += deltaLines; } int blockIndex = firstBlock; if (createBlockBefore) { - mBlockEnds[blockIndex] = startLine - 1; + mBlockEndLines[blockIndex] = startLine - 1; mBlockIndices[blockIndex] = INVALID_BLOCK_INDEX; blockIndex++; } if (createBlock) { - mBlockEnds[blockIndex] = startLine + newLineCount - 1; + mBlockEndLines[blockIndex] = startLine + newLineCount - 1; mBlockIndices[blockIndex] = INVALID_BLOCK_INDEX; blockIndex++; } if (createBlockAfter) { - mBlockEnds[blockIndex] = lastBlockEndLine + deltaLines; + mBlockEndLines[blockIndex] = lastBlockEndLine + deltaLines; mBlockIndices[blockIndex] = INVALID_BLOCK_INDEX; } } @@ -473,10 +473,10 @@ public class DynamicLayout extends Layout * This package private method is used for test purposes only * @hide */ - void setBlocksDataForTest(int[] blockEnds, int[] blockIndices, int numberOfBlocks) { - mBlockEnds = new int[blockEnds.length]; + void setBlocksDataForTest(int[] blockEndLines, int[] blockIndices, int numberOfBlocks) { + mBlockEndLines = new int[blockEndLines.length]; mBlockIndices = new int[blockIndices.length]; - System.arraycopy(blockEnds, 0, mBlockEnds, 0, blockEnds.length); + System.arraycopy(blockEndLines, 0, mBlockEndLines, 0, blockEndLines.length); System.arraycopy(blockIndices, 0, mBlockIndices, 0, blockIndices.length); mNumberOfBlocks = numberOfBlocks; } @@ -484,8 +484,8 @@ public class DynamicLayout extends Layout /** * @hide */ - public int[] getBlockEnds() { - return mBlockEnds; + public int[] getBlockEndLines() { + return mBlockEndLines; } /** @@ -633,8 +633,8 @@ public class DynamicLayout extends Layout * @hide */ public static final int INVALID_BLOCK_INDEX = -1; - // Stores the line numbers of the last line of each block - private int[] mBlockEnds; + // Stores the line numbers of the last line of each block (inclusive) + private int[] mBlockEndLines; // The indices of this block's display list in TextView's internal display list array or // INVALID_BLOCK_INDEX if this block has been invalidated during an edition private int[] mBlockIndices; diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 040a3853a2735..900f0d3e9e77a 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -1241,24 +1241,21 @@ public class Editor { } DynamicLayout dynamicLayout = (DynamicLayout) layout; - int[] blockEnds = dynamicLayout.getBlockEnds(); + int[] blockEndLines = dynamicLayout.getBlockEndLines(); int[] blockIndices = dynamicLayout.getBlockIndices(); final int numberOfBlocks = dynamicLayout.getNumberOfBlocks(); - final int mScrollX = mTextView.getScrollX(); - final int mScrollY = mTextView.getScrollY(); - canvas.translate(mScrollX, mScrollY); int endOfPreviousBlock = -1; int searchStartIndex = 0; for (int i = 0; i < numberOfBlocks; i++) { - int blockEnd = blockEnds[i]; + int blockEndLine = blockEndLines[i]; int blockIndex = blockIndices[i]; final boolean blockIsInvalid = blockIndex == DynamicLayout.INVALID_BLOCK_INDEX; if (blockIsInvalid) { blockIndex = getAvailableDisplayListIndex(blockIndices, numberOfBlocks, searchStartIndex); - // Dynamic layout internal block indices structure is updated from Editor + // Note how dynamic layout's internal block indices get updated from Editor blockIndices[i] = blockIndex; searchStartIndex = blockIndex + 1; } @@ -1272,28 +1269,38 @@ public class Editor { } if (!blockDisplayList.isValid()) { + final int blockBeginLine = endOfPreviousBlock + 1; + final int top = layout.getLineTop(blockBeginLine); + final int bottom = layout.getLineBottom(blockEndLine); + final HardwareCanvas hardwareCanvas = blockDisplayList.start(); try { - hardwareCanvas.setViewport(width, height); + hardwareCanvas.setViewport(width, bottom - top); // The dirty rect should always be null for a display list hardwareCanvas.onPreDraw(null); - hardwareCanvas.translate(-mScrollX, -mScrollY); - layout.drawText(hardwareCanvas, endOfPreviousBlock + 1, blockEnd); - hardwareCanvas.translate(mScrollX, mScrollY); + // drawText is always relative to TextView's origin, this translation brings + // this range of text back to the top of the viewport + hardwareCanvas.translate(0, -top); + layout.drawText(hardwareCanvas, blockBeginLine, blockEndLine); + hardwareCanvas.translate(0, top); } finally { hardwareCanvas.onPostDraw(); blockDisplayList.end(); if (View.USE_DISPLAY_LIST_PROPERTIES) { - blockDisplayList.setLeftTopRightBottom(0, 0, width, height); + blockDisplayList.setLeftTopRightBottom(0, top, width, bottom); + // Same as drawDisplayList below, handled by our TextView's parent + blockDisplayList.setClipChildren(false); } } } + // TODO When View.USE_DISPLAY_LIST_PROPERTIES is the only code path, the + // width and height parameters should be removed and the bounds set above in + // setLeftTopRightBottom should be used instead for quick rejection. ((HardwareCanvas) canvas).drawDisplayList(blockDisplayList, width, height, null, - DisplayList.FLAG_CLIP_CHILDREN); - endOfPreviousBlock = blockEnd; + 0 /* no child clipping, our TextView parent enforces it */); + endOfPreviousBlock = blockEndLine; } - canvas.translate(-mScrollX, -mScrollY); } else { // Boring layout is used for empty and hint text layout.drawText(canvas, firstLine, lastLine); @@ -1572,11 +1579,9 @@ public class Editor { } void onScrollChanged() { - if (mPositionListener != null) { - mPositionListener.onScrollChanged(); - } - // Internal scroll affects the clip boundaries - invalidateTextDisplayList(); + if (mPositionListener != null) { + mPositionListener.onScrollChanged(); + } } /** diff --git a/core/tests/coretests/src/android/text/DynamicLayoutBlocksTest.java b/core/tests/coretests/src/android/text/DynamicLayoutBlocksTest.java index 73da84f4e35ab..2e42e5a1d3846 100644 --- a/core/tests/coretests/src/android/text/DynamicLayoutBlocksTest.java +++ b/core/tests/coretests/src/android/text/DynamicLayoutBlocksTest.java @@ -45,18 +45,18 @@ public class DynamicLayoutBlocksTest extends TestCase { public void printBlocks(String message) { System.out.print(message); for (int i = 0; i < dl.getNumberOfBlocks(); i++) { - System.out.print(" " + Integer.toString(dl.getBlockEnds()[i])); + System.out.print(" " + Integer.toString(dl.getBlockEndLines()[i])); } System.out.println(); } public void checkInvariants() { assertTrue(dl.getNumberOfBlocks() > 0); - assertTrue(dl.getNumberOfBlocks() <= dl.getBlockEnds().length); - assertEquals(dl.getBlockEnds().length, dl.getBlockIndices().length); + assertTrue(dl.getNumberOfBlocks() <= dl.getBlockEndLines().length); + assertEquals(dl.getBlockEndLines().length, dl.getBlockIndices().length); for (int i = 1; i < dl.getNumberOfBlocks(); i++) { - assertTrue(dl.getBlockEnds()[i] > dl.getBlockEnds()[i-1]); + assertTrue(dl.getBlockEndLines()[i] > dl.getBlockEndLines()[i-1]); } } @@ -78,7 +78,7 @@ public class DynamicLayoutBlocksTest extends TestCase { } for (int i = 0; i < dl.getNumberOfBlocks(); i++) { - assertEquals(ends[i], dl.getBlockEnds()[i]); + assertEquals(ends[i], dl.getBlockEndLines()[i]); assertEquals(indices[i], dl.getBlockIndices()[i]); } }