summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoman Kennke <roman@kennke.org>2006-11-19 18:54:19 +0000
committerRoman Kennke <roman@kennke.org>2006-11-19 18:54:19 +0000
commitb1d0c928622637949dfb9158cdc3f75851850992 (patch)
tree1f58e36b6ce4b8b2c93c6f234014f158488ecce7
parentdcde416ef391ce7365b8b05416697a26c8ea3139 (diff)
downloadclasspath-b1d0c928622637949dfb9158cdc3f75851850992.tar.gz
2006-11-19 Roman Kennke <kennke@aicas.com>
* javax/swing/text/BoxView.java (clipRect): New field. (tmpRect): New field. (layout): Reorganized code. Now uses layoutAxis() helper method. (layoutAxis): New helper method. (paint): Optimized by using cached Rectangle objects and a binary search for child views inside the clip. * javax/swing/text/CompositeView.java (insideAllocation): Made private and initialized in constructor. (getInsideAllocation): Removed initialization block for insideAllocation field. Avoid unnecessary allocations. * javax/swing/text/GlyphView.java (DefaultGlyphPainter.paint): Only paint the actual glyphs here The remaining stuff (background, underline and striking) is done in the GlpyhView itself. Avoid unnecessary allocations. (cached): A cached Segment instance. (getText): Return cached segment. (paint): Paint underline, strike and background here. Avoid unecessary allocs.
-rw-r--r--ChangeLog22
-rw-r--r--javax/swing/text/BoxView.java167
-rw-r--r--javax/swing/text/CompositeView.java17
-rw-r--r--javax/swing/text/GlyphView.java122
4 files changed, 220 insertions, 108 deletions
diff --git a/ChangeLog b/ChangeLog
index f7c988ab3..254afccae 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,27 @@
2006-11-19 Roman Kennke <kennke@aicas.com>
+ * javax/swing/text/BoxView.java
+ (clipRect): New field.
+ (tmpRect): New field.
+ (layout): Reorganized code. Now uses layoutAxis() helper method.
+ (layoutAxis): New helper method.
+ (paint): Optimized by using cached Rectangle objects and
+ a binary search for child views inside the clip.
+ * javax/swing/text/CompositeView.java
+ (insideAllocation): Made private and initialized in constructor.
+ (getInsideAllocation): Removed initialization block for
+ insideAllocation field. Avoid unnecessary allocations.
+ * javax/swing/text/GlyphView.java
+ (DefaultGlyphPainter.paint): Only paint the actual glyphs here
+ The remaining stuff (background, underline and striking) is
+ done in the GlpyhView itself. Avoid unnecessary allocations.
+ (cached): A cached Segment instance.
+ (getText): Return cached segment.
+ (paint): Paint underline, strike and background here. Avoid
+ unecessary allocs.
+
+2006-11-19 Roman Kennke <kennke@aicas.com>
+
* javax/swing/text/html/StyleSheet.java
(getFontSize): Removed debug output.
(ListPainter.tmpRect): New field.
diff --git a/javax/swing/text/BoxView.java b/javax/swing/text/BoxView.java
index 962d06219..902927980 100644
--- a/javax/swing/text/BoxView.java
+++ b/javax/swing/text/BoxView.java
@@ -282,6 +282,13 @@ public class BoxView
}
/**
+ * A Rectangle instance to be reused in the paint() method below.
+ */
+ private final Rectangle tmpRect = new Rectangle();
+
+ private Rectangle clipRect = new Rectangle();
+
+ /**
* Renders the <code>Element</code> that is associated with this
* <code>View</code>.
*
@@ -290,26 +297,93 @@ public class BoxView
*/
public void paint(Graphics g, Shape a)
{
- Rectangle alloc;
- if (a instanceof Rectangle)
- alloc = (Rectangle) a;
- else
- alloc = a.getBounds();
-
- int x = alloc.x + getLeftInset();
- int y = alloc.y + getTopInset();
+ // Try to avoid allocation if possible (almost all cases).
+ Rectangle alloc = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+
+ // This returns a cached instance.
+ alloc = getInsideAllocation(alloc);
+
+ // The following algorithm optimizes painting of a BoxView by taking
+ // advantage of the layout order of the box children.
+ //
+ // 1. It first searches a child that which's allocation is inside the clip.
+ // This is accomplished by an efficient binary search. This assumes
+ // that the children of the BoxView are laid out in the same order
+ // as their index within the view. This is true for the BoxView, but
+ // might not be the case for all subclasses.
+ // 2. Starting from the found view, it paints the children in both
+ // directions until the first view is hit that is outside the clip.
+
+ // First we search a child view that is inside the clip.
+
+ // Fetch the clip rect and calculate the center point of it.
+ clipRect = g.getClipBounds(clipRect);
+ int cX = clipRect.x + clipRect.width / 2;
+ int cY = clipRect.y + clipRect.height / 2;
+
+ int viewCount = getViewCount();
+ int up = viewCount;
+ int low = 0;
+ int mid = (up - low) / 2;
+ View start = getView(mid);
+
+ int newMid;
+ // Use another cached instance here to avoid allocations during
+ // painting.
+ tmpRect.setBounds(alloc);
+ // This modifies tmpRect.
+ childAllocation(mid, tmpRect);
+ while (! clipRect.intersects(tmpRect))
+ {
+ if (isBefore(cX, cY, tmpRect))
+ {
+ up = mid;
+ newMid = (up - low) / 2 + low;
+ mid = (newMid == mid) ? newMid - 1 : newMid;
+ }
+ else
+ {
+ low = mid;
+ newMid = (up - low) / 2 + low;
+ mid = (newMid == mid) ? newMid + 1 : newMid;
+ }
+ if (mid >= 0 && mid < viewCount)
+ {
+ start = getView(mid);
+ tmpRect.setBounds(alloc);
+ childAllocation(mid, tmpRect);
+ }
+ else
+ break;
+ }
- Rectangle clip = g.getClipBounds();
- Rectangle tmp = new Rectangle();
- int count = getViewCount();
- for (int i = 0; i < count; ++i)
+ if (mid >= 0 && mid < viewCount)
{
- tmp.x = x + getOffset(X_AXIS, i);
- tmp.y = y + getOffset(Y_AXIS, i);
- tmp.width = getSpan(X_AXIS, i);
- tmp.height = getSpan(Y_AXIS, i);
- if (tmp.intersects(clip))
- paintChild(g, tmp, i);
+ // Ok, we found one view that is inside the clip rect. Now paint the
+ // children before it that are inside the clip.
+ boolean inClip = true;
+ for (int i = mid - 1; i >= 0 && inClip; i--)
+ {
+ start = getView(i);
+ tmpRect.setBounds(alloc);
+ childAllocation(i, tmpRect);
+ inClip = clipRect.intersects(tmpRect);
+ if (inClip)
+ paintChild(g, tmpRect, i);
+ }
+
+ // Now paint the found view and all views after it that lie inside the
+ // clip.
+ inClip = true;
+ for (int i = mid; i < viewCount && inClip; i++)
+ {
+ start = getView(i);
+ tmpRect.setBounds(alloc);
+ childAllocation(i, tmpRect);
+ inClip = clipRect.intersects(tmpRect);
+ if (inClip)
+ paintChild(g, tmpRect, i);
+ }
}
}
@@ -742,49 +816,32 @@ public class BoxView
*/
protected void layout(int width, int height)
{
- int[] newSpan = new int[]{ width, height };
- int count = getViewCount();
-
- // Update minor axis as appropriate. We need to first update the minor
- // axis layout because that might affect the children's preferences along
- // the major axis.
- int minorAxis = myAxis == X_AXIS ? Y_AXIS : X_AXIS;
- if ((! isLayoutValid(minorAxis)) || newSpan[minorAxis] != span[minorAxis])
- {
- layoutValid[minorAxis] = false;
- span[minorAxis] = newSpan[minorAxis];
- layoutMinorAxis(span[minorAxis], minorAxis, offsets[minorAxis],
- spans[minorAxis]);
-
- // Update the child view's sizes.
- for (int i = 0; i < count; ++i)
- {
- getView(i).setSize(spans[X_AXIS][i], spans[Y_AXIS][i]);
- }
- layoutValid[minorAxis] = true;
- }
-
+ layoutAxis(X_AXIS, width);
+ layoutAxis(Y_AXIS, height);
+ }
- // Update major axis as appropriate.
- if ((! isLayoutValid(myAxis)) || newSpan[myAxis] != span[myAxis])
+ private void layoutAxis(int axis, int s)
+ {
+ if (span[axis] != s)
+ layoutValid[axis] = false;
+ if (! layoutValid[axis])
{
- layoutValid[myAxis] = false;
- span[myAxis] = newSpan[myAxis];
- layoutMajorAxis(span[myAxis], myAxis, offsets[myAxis],
- spans[myAxis]);
+ span[axis] = s;
+ updateRequirements(axis);
+ if (axis == myAxis)
+ layoutMajorAxis(span[axis], axis, offsets[axis], spans[axis]);
+ else
+ layoutMinorAxis(span[axis], axis, offsets[axis], spans[axis]);
+ layoutValid[axis] = true;
- // Update the child view's sizes.
- for (int i = 0; i < count; ++i)
+ // Push out child layout.
+ int viewCount = getViewCount();
+ for (int i = 0; i < viewCount; i++)
{
- getView(i).setSize(spans[X_AXIS][i], spans[Y_AXIS][i]);
+ View v = getView(i);
+ v.setSize(spans[X_AXIS][i], spans[Y_AXIS][i]);
}
- layoutValid[myAxis] = true;
}
-
- if (layoutValid[myAxis] == false)
- System.err.println("WARNING: Major axis layout must be valid after layout");
- if (layoutValid[minorAxis] == false)
- System.err.println("Minor axis layout must be valid after layout");
}
/**
diff --git a/javax/swing/text/CompositeView.java b/javax/swing/text/CompositeView.java
index d4467f314..ac81e544f 100644
--- a/javax/swing/text/CompositeView.java
+++ b/javax/swing/text/CompositeView.java
@@ -68,7 +68,7 @@ public abstract class CompositeView
* initialized in {@link #getInsideAllocation} and reused and modified in
* {@link #childAllocation(int, Rectangle)}.
*/
- Rectangle insideAllocation;
+ private final Rectangle insideAllocation = new Rectangle();
/**
* The insets of this <code>CompositeView</code>. This is initialized
@@ -527,20 +527,13 @@ public abstract class CompositeView
if (a == null)
return null;
- Rectangle alloc = a.getBounds();
+ // Try to avoid allocation of Rectangle here.
+ Rectangle alloc = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+
// Initialize the inside allocation rectangle. This is done inside
// a synchronized block in order to avoid multiple threads creating
// this instance simultanously.
- Rectangle inside;
- synchronized(this)
- {
- inside = insideAllocation;
- if (inside == null)
- {
- inside = new Rectangle();
- insideAllocation = inside;
- }
- }
+ Rectangle inside = insideAllocation;
inside.x = alloc.x + left;
inside.y = alloc.y + top;
inside.width = alloc.width - left - right;
diff --git a/javax/swing/text/GlyphView.java b/javax/swing/text/GlyphView.java
index c654cee46..e2303bbc9 100644
--- a/javax/swing/text/GlyphView.java
+++ b/javax/swing/text/GlyphView.java
@@ -278,44 +278,27 @@ public class GlyphView extends View implements TabableView, Cloneable
public void paint(GlyphView view, Graphics g, Shape a, int p0,
int p1)
{
- Color oldColor = g.getColor();
- int height = (int) getHeight(view);
+ updateFontMetrics(view);
+ Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+ TabExpander tabEx = view.getTabExpander();
Segment txt = view.getText(p0, p1);
- Rectangle bounds = a.getBounds();
- TabExpander tabEx = null;
- View parent = view.getParent();
- if (parent instanceof TabExpander)
- tabEx = (TabExpander) parent;
-
- int width = Utilities.getTabbedTextWidth(txt, g.getFontMetrics(),
- bounds.x, tabEx, txt.offset);
- // Fill the background of the text run.
- Color background = view.getBackground();
- if (background != null)
- {
- g.setColor(background);
- g.fillRect(bounds.x, bounds.y, width, height);
- }
- // Draw the actual text.
- g.setColor(view.getForeground());
- g.setFont(view.getFont());
- int ascent = g.getFontMetrics().getAscent();
- Utilities.drawTabbedText(txt, bounds.x, bounds.y + ascent, g, tabEx,
- txt.offset);
-
- if (view.isStrikeThrough())
- {
- int strikeHeight = (int) (getAscent(view) / 2);
- g.drawLine(bounds.x, bounds.y + strikeHeight, bounds.x + width,
- bounds.y + strikeHeight);
- }
- if (view.isUnderline())
+
+ // Find out the X location at which we have to paint.
+ int x = r.x;
+ int p = view.getStartOffset();
+ if (p != p0)
{
- int lineHeight = (int) getAscent(view);
- g.drawLine(bounds.x, bounds.y + lineHeight, bounds.x + width,
- bounds.y + lineHeight);
+ int width = Utilities.getTabbedTextWidth(txt, fontMetrics,x, tabEx,
+ p);
+ x += width;
}
- g.setColor(oldColor);
+ // Find out Y location.
+ int y = r.y + fontMetrics.getHeight() - fontMetrics.getDescent();
+
+ // Render the thing.
+ g.setFont(fontMetrics.getFont());
+ Utilities.drawTabbedText(txt, x, y, g, tabEx, p0);
+
}
/**
@@ -565,11 +548,29 @@ public class GlyphView extends View implements TabableView, Cloneable
int p0 = getStartOffset();
int p1 = getEndOffset();
+ Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
Container c = getContainer();
- // Paint layered highlights if there are any.
+
+ Color fg = getForeground();
+ JTextComponent tc = null;
if (c instanceof JTextComponent)
{
- JTextComponent tc = (JTextComponent) c;
+ tc = (JTextComponent) c;
+ if (! tc.isEnabled())
+ fg = tc.getDisabledTextColor();
+ }
+ Color bg = getBackground();
+ if (bg != null)
+ {
+ g.setColor(bg);
+ System.err.println("fill background: " + bg);
+ g.fillRect(r.x, r.y, r.width, r.height);
+ }
+
+
+ // Paint layered highlights if there are any.
+ if (tc != null)
+ {
Highlighter h = tc.getHighlighter();
if (h instanceof LayeredHighlighter)
{
@@ -578,7 +579,45 @@ public class GlyphView extends View implements TabableView, Cloneable
}
}
- getGlyphPainter().paint(this, g, a, p0, p1);
+ g.setColor(fg);
+ glyphPainter.paint(this, g, a, p0, p1);
+ boolean underline = isUnderline();
+ boolean striked = isStrikeThrough();
+ if (underline || striked)
+ {
+ View parent = getParent();
+ // X coordinate.
+ if (parent != null && parent.getEndOffset() == p1)
+ {
+ // Strip whitespace.
+ Segment s = getText(p0, p1);
+ while (s.count > 0 && Character.isWhitespace(s.array[s.count - 1]))
+ {
+ p1--;
+ s.count--;
+ }
+ }
+ int x0 = r.x;
+ int p = getStartOffset();
+ TabExpander tabEx = getTabExpander();
+ if (p != p0)
+ x0 += (int) glyphPainter.getSpan(this, p, p0, tabEx, x0);
+ int x1 = x0 + (int) glyphPainter.getSpan(this, p0, p1, tabEx, x0);
+ // Y coordinate.
+ int y = r.y + r.height - (int) glyphPainter.getDescent(this);
+ if (underline)
+ {
+ int yTmp = y;
+ yTmp += 1;
+ g.drawLine(x0, yTmp, x1, yTmp);
+ }
+ if (striked)
+ {
+ int yTmp = y;
+ yTmp -= (int) glyphPainter.getAscent(this);
+ g.drawLine(x0, yTmp, x1, yTmp);
+ }
+ }
}
@@ -743,6 +782,8 @@ public class GlyphView extends View implements TabableView, Cloneable
return offs;
}
+ private Segment cached = new Segment();
+
/**
* Returns the text segment that this view is responsible for.
*
@@ -753,10 +794,9 @@ public class GlyphView extends View implements TabableView, Cloneable
*/
public Segment getText(int p0, int p1)
{
- Segment txt = new Segment();
try
{
- getDocument().getText(p0, p1 - p0, txt);
+ getDocument().getText(p0, p1 - p0, cached);
}
catch (BadLocationException ex)
{
@@ -767,7 +807,7 @@ public class GlyphView extends View implements TabableView, Cloneable
throw ae;
}
- return txt;
+ return cached;
}
/**