From 4ed955792fc13e138da619fa394a82b669239cd8 Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Mon, 18 Jul 2016 18:52:12 +0300 Subject: [PATCH] IDEA-97406 improve performance of wave and dotted line --- .../com/intellij/ui/paint/EffectPainter.java | 147 +++++++++++++----- 1 file changed, 104 insertions(+), 43 deletions(-) diff --git a/platform/platform-api/src/com/intellij/ui/paint/EffectPainter.java b/platform/platform-api/src/com/intellij/ui/paint/EffectPainter.java index 530f846350e9..cea09dec043a 100644 --- a/platform/platform-api/src/com/intellij/ui/paint/EffectPainter.java +++ b/platform/platform-api/src/com/intellij/ui/paint/EffectPainter.java @@ -17,6 +17,7 @@ package com.intellij.ui.paint; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.registry.Registry; +import com.intellij.util.JBHiDPIScaledImage; import com.intellij.util.ui.JBUI; import com.intellij.util.ui.RegionPainter; import com.intellij.util.ui.UIUtil; @@ -24,6 +25,8 @@ import com.intellij.util.ui.WavePainter; import java.awt.*; import java.awt.geom.*; +import java.awt.image.BufferedImage; +import java.util.concurrent.ConcurrentHashMap; /** * @author Sergey.Malenkov @@ -111,8 +114,6 @@ public enum EffectPainter implements RegionPainter { * @see com.intellij.openapi.editor.markup.EffectType#WAVE_UNDERSCORE */ WAVE_UNDERSCORE { - private final BasicStroke STROKE = new BasicStroke(.7f); - /** * Draws a horizontal wave under a text. * @@ -130,44 +131,16 @@ public enum EffectPainter implements RegionPainter { WavePainter.forColor(g.getColor()).paint(g, x, x + width, y + height); } else if (width > 0 && height > 0) { + if (paint == null) paint = g.getPaint(); g = (Graphics2D)g.create(x, y, width, height); + g.setComposite(AlphaComposite.SrcOver); g.clipRect(0, 0, width, height); - if (paint != null) g.setPaint(paint); - int h = getMaxHeight(height); - int length = 2 * h - 2; // the spatial period of the wave - - double dx = -((x % length + length) % length); // normalize - double upper = height - h; - double lower = height - 1; - Path2D path = new Path2D.Double(); - path.moveTo(dx, lower); - if (height < 6) { - g.setStroke(STROKE); - double size = (double)length / 2; - while (true) { - path.lineTo(dx += size, upper); - if (dx > width) break; - path.lineTo(dx += size, lower); - if (dx > width) break; - } + BufferedImage image = WAVE_FACTORY.get(g, paint, height); + int length = image.getWidth(); // the spatial period of the wave + int dx = -((x % length + length) % length); // normalize + for (; dx < width; dx += length) { + UIUtil.drawImage(g, image, dx, 0, null); } - else { - double size = (double)length / 4; - double prev = dx - size / 2; - double center = (upper + lower) / 2; - while (true) { - path.quadTo(prev += size, lower, dx += size, center); - if (dx > width) break; - path.quadTo(prev += size, upper, dx += size, upper); - if (dx > width) break; - path.quadTo(prev += size, upper, dx += size, center); - if (dx > width) break; - path.quadTo(prev += size, lower, dx += size, lower); - if (dx > width) break; - } - } - g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - g.draw(path); g.dispose(); } } @@ -219,20 +192,108 @@ public enum EffectPainter implements RegionPainter { } if (painter == BOLD_DOTTED_UNDERSCORE) { int length = 2 * height; // the spatial period - int dx = -((x % length + length) % length); // normalize if (-dx >= height) { dx += length; } - Integer round = height > 2 ? height : null; - while (dx <= width) { - //noinspection SuspiciousNameCombination - RectanglePainter.FILL.paint(g, x + dx, y, height, height, round); - dx += length; + Composite old = g.getComposite(); + g.setComposite(AlphaComposite.SrcOver); + BufferedImage image = BOLD_DOTTED_FACTORY.get(g, g.getPaint(), height); + for (; dx < width; dx += length) { + if (image != null) { + UIUtil.drawImage(g, image, x + dx, y, null); + } + else { + //noinspection SuspiciousNameCombination + RectanglePainter.FILL.paint(g, x + dx, y, height, height, null); + } } + g.setComposite(old); } else { g.fillRect(x, y, width, height); } } + + private static abstract class Factory { + private final ConcurrentHashMap myCache = new ConcurrentHashMap<>(); + + abstract BufferedImage create(Graphics2D g, Paint paint, int height); + + BufferedImage get(Graphics2D g, Paint paint, int height) { + if (paint instanceof Color) { + Color color = (Color)paint; + Long key = color.getRGB() ^ ((long)height << 32); + BufferedImage image = myCache.get(key); + boolean exists = image != null; + if (!exists || UIUtil.isRetina(g) != (image instanceof JBHiDPIScaledImage)) { + image = create(g, paint, height); + if (image != null) { + myCache.put(key, image); + } + else if (exists) { + myCache.remove(key); + } + } + return image; + } + return create(g, paint, height); + } + } + + private static final Factory BOLD_DOTTED_FACTORY = new Factory() { + @Override + BufferedImage create(Graphics2D graphics, Paint paint, int height) { + if (height <= 2 && !UIUtil.isRetina(graphics)) return null; + //noinspection SuspiciousNameCombination + BufferedImage image = UIUtil.createImageForGraphics(graphics, height, height, BufferedImage.TYPE_INT_ARGB); + Graphics2D g = image.createGraphics(); + try { + g.setPaint(paint); + //noinspection SuspiciousNameCombination + RectanglePainter.FILL.paint(g, 0, 0, height, height, height); + } + finally { + g.dispose(); + } + return image; + } + }; + + private static final BasicStroke WAVE_THIN_STROKE = new BasicStroke(.7f); + private static final Factory WAVE_FACTORY = new Factory() { + @Override + BufferedImage create(Graphics2D graphics, Paint paint, int height) { + int h = getMaxHeight(height); + int width = 2 * h - 2; // the spatial period of the wave + BufferedImage image = UIUtil.createImageForGraphics(graphics, width, height, BufferedImage.TYPE_INT_ARGB); + Graphics2D g = image.createGraphics(); + try { + double upper = height - h; + double lower = height - 1; + Path2D path = new Path2D.Double(); + path.moveTo(0, lower); + if (height < 6) { + g.setStroke(WAVE_THIN_STROKE); + path.lineTo((double)width / 2, upper); + path.lineTo((double)width, lower); + } + else { + double size = (double)width / 8; + double center = (upper + lower) / 2; + path.quadTo(size, lower, size * 2, center); + path.quadTo(size * 3, upper, size * 4, upper); + path.quadTo(size * 5, upper, size * 6, center); + path.quadTo(size * 7, lower, (double)width, lower); + } + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.setPaint(paint); + g.draw(path); + } + finally { + g.dispose(); + } + return image; + } + }; }