diff options
author | Phillip Webb | 2015-12-22 10:50:14 +0000 |
---|---|---|
committer | Lakshmi Shanmugam | 2018-12-14 05:49:43 +0000 |
commit | 2a2325b0699615424fc557f8d6db2190dd304048 (patch) | |
tree | 949659c1d5eb5a14830b64b3a8609d17af6bc842 | |
parent | 4490549bb9093dc44dc579a5947718c93cc5a4ec (diff) | |
download | eclipse.platform.swt-2a2325b0699615424fc557f8d6db2190dd304048.tar.gz eclipse.platform.swt-2a2325b0699615424fc557f8d6db2190dd304048.tar.xz eclipse.platform.swt-2a2325b0699615424fc557f8d6db2190dd304048.zip |
Bug 366471 - Improve OSX scroll performance with drawText caching
Add caching to GC.drawText to prevent multiple calls with the same
single
character from needed to perform expensive layout operations. Any call
to
GC.drawText() that is rendering a single character on a transparent
background is now a cache candidate.
The cache is limited to 20 items and is specifically designed to work
well
with classes such as WhitespaceCharacterPainter which frequently call
drawText() but operate with a relatively small set of characters.
Change-Id: I0ac4e3478e212ba5e410c5a06a6827279143f7be
Also-by: Karsten Thoms <karsten.thoms@itemis.de>
Signed-off-by: Phillip Webb <pwebb@pivotal.io>
-rw-r--r-- | bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/GC.java | 166 |
1 files changed, 138 insertions, 28 deletions
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/GC.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/GC.java index 7bb0d7a7fa..f62acd37ad 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/GC.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/GC.java @@ -13,6 +13,8 @@ *******************************************************************************/ package org.eclipse.swt.graphics; +import java.util.*; + import org.eclipse.swt.*; import org.eclipse.swt.internal.*; import org.eclipse.swt.internal.cocoa.*; @@ -74,6 +76,7 @@ public final class GC extends Resource { Drawable drawable; GCData data; + GCTextData.Cache textDataCache = new GCTextData.Cache(20); CGPathElement element; int count, typeCount; @@ -108,6 +111,88 @@ public final class GC extends Resource { static final float[] LINE_DASHDOT_ZERO = new float[]{9, 6, 3, 6}; static final float[] LINE_DASHDOTDOT_ZERO = new float[]{9, 3, 3, 3, 3, 3}; + /** + * Instances of this class are descriptions of GCs for text drawing in terms of + * platform-specific data fields. Can be cached to improve repeat render + * performance. + */ + private static class GCTextData { + private NSPoint pt = new NSPoint(); + private NSLayoutManager layoutManager; + private NSTextStorage textStorage; + private NSRange range; + + /** + * Simple {@link GCTextData} cache limited to a set number of items. + */ + private static class Cache { + private final int cacheSize; + private final Map<String, GCTextData> cache = new LinkedHashMap<String, GCTextData>() { + private static final long serialVersionUID = 1L; + + @Override + protected boolean removeEldestEntry(Map.Entry<String, GCTextData> eldest) { + if (size() >= cacheSize) { + ((GCTextData) eldest.getValue()).release(); + return true; + } + return false; + }; + }; + + public Cache(int cacheSize) { + this.cacheSize = cacheSize; + } + + public void release() { + for (GCTextData data : cache.values()) { + data.release(); + } + cache.clear(); + } + + public GCTextData get(String string) { + return cache.get(string); + } + + public void put(String string, GCTextData data) { + cache.put(string, data); + } + } + + public GCTextData(NSAttributedString attribStr) { + NSSize size = new NSSize(); + size.width = OS.MAX_TEXT_CONTAINER_SIZE; + size.height = OS.MAX_TEXT_CONTAINER_SIZE; + NSTextStorage textStorage = (NSTextStorage) new NSTextStorage().alloc().init(); + NSLayoutManager layoutManager = (NSLayoutManager) new NSLayoutManager().alloc().init(); + layoutManager.setBackgroundLayoutEnabled(NSThread.isMainThread()); + NSTextContainer textContainer = (NSTextContainer) new NSTextContainer().alloc(); + textContainer = textContainer.initWithContainerSize(size); + textContainer.setLineFragmentPadding(0); + textStorage.addLayoutManager(layoutManager); + layoutManager.addTextContainer(textContainer); + layoutManager.release(); + textContainer.release(); + this.layoutManager = layoutManager; + this.textStorage = textStorage; + textStorage.setAttributedString(attribStr); + this.range = layoutManager.glyphRangeForTextContainer(textContainer); + } + + public void release() { + if (textStorage != null) textStorage.release(); + textStorage = null; + layoutManager = null; + } + + public void draw(int x, int y) { + pt.x = x; + pt.y = y; + layoutManager.drawGlyphsForGlyphRange(range, pt); + } + } + GC() { } @@ -915,6 +1000,7 @@ void destroy() { data.path = data.clipPath = data.visiblePath = null; data.transform = data.inverseTransform = null; data.fg = data.bg = null; + textDataCache.release(); /* Dispose the GC */ if (drawable != null) drawable.internal_dispose_GC(handle.id, data); @@ -1680,41 +1766,65 @@ public void drawText (String string, int x, int y, int flags) { } handle.saveGraphicsState(); handle.setShouldAntialias(mode); - if (data.textStorage == null) createLayout(); - NSAttributedString attribStr = createString(string, flags, true); - data.textStorage.setAttributedString(attribStr); - attribStr.release(); - NSPoint pt = new NSPoint(); - pt.x = x; - pt.y = y; - NSRange range = data.layoutManager.glyphRangeForTextContainer(data.textContainer); - if ((flags & SWT.DRAW_TRANSPARENT) == 0) { - NSRect rect = data.layoutManager.usedRectForTextContainer(data.textContainer); - rect.x = x; - rect.y = y; - Pattern pattern = data.backgroundPattern; - if (pattern != null) setPatternPhase(pattern); - if (pattern != null && pattern.gradient != null) { - NSBezierPath path = NSBezierPath.bezierPathWithRect(rect); - fillPattern(path, pattern); - } else { - NSColor bg = data.bg; - if (bg == null) { - double /*float*/ [] color = data.background; - bg = data.bg = NSColor.colorWithDeviceRed(color[0], color[1], color[2], data.alpha / 255f); - bg.retain(); - } - bg.setFill(); - NSBezierPath.fillRect(rect); - } + if (length == 1 && (flags & SWT.DRAW_TRANSPARENT) != 0) { + doFastDrawText(string, x, y); + } else { + doDrawText(string, x, y, flags); } - data.layoutManager.drawGlyphsForGlyphRange(range, pt); handle.restoreGraphicsState(); } finally { uncheckGC(pool); } } +private void doFastDrawText(String string, int x, int y) { + GCTextData data = getTextData(string); + data.draw(x, y); +} + +private GCTextData getTextData(String string) { + GCTextData data = (GCTextData) textDataCache.get(string); + if (data == null) { + NSAttributedString attribStr = createString(string, 0, true); + data = new GCTextData(attribStr); + attribStr.release(); + textDataCache.put(string, data); + } + return data; +} + +private void doDrawText(String string, int x, int y, int flags) { + if (data.textStorage == null) createLayout(); + NSAttributedString attribStr = createString(string, flags, true); + data.textStorage.setAttributedString(attribStr); + attribStr.release(); + NSPoint pt = new NSPoint(); + pt.x = x; + pt.y = y; + NSRange range = data.layoutManager.glyphRangeForTextContainer(data.textContainer); + if ((flags & SWT.DRAW_TRANSPARENT) == 0) { + NSRect rect = data.layoutManager.usedRectForTextContainer(data.textContainer); + rect.x = x; + rect.y = y; + Pattern pattern = data.backgroundPattern; + if (pattern != null) setPatternPhase(pattern); + if (pattern != null && pattern.gradient != null) { + NSBezierPath path = NSBezierPath.bezierPathWithRect(rect); + fillPattern(path, pattern); + } else { + NSColor bg = data.bg; + if (bg == null) { + double /*float*/ [] color = data.background; + bg = data.bg = NSColor.colorWithDeviceRed(color[0], color[1], color[2], data.alpha / 255f); + bg.retain(); + } + bg.setFill(); + NSBezierPath.fillRect(rect); + } + } + data.layoutManager.drawGlyphsForGlyphRange(range, pt); +} + /** * Compares the argument to the receiver, and returns true * if they represent the <em>same</em> object using a class |