From 6225f7a4c869d86e05bde0da681e63f14b2fc8e6 Mon Sep 17 00:00:00 2001 From: Dmitry Batrak Date: Wed, 8 Jun 2016 11:49:40 +0300 Subject: [PATCH] IDEA-157087 slow text rendering on OS X when ligatures are enabled --- .../editor/impl/view/ComplexTextFragment.java | 69 +++++++++++++++++-- .../editor/impl/view/EditorPainter.java | 3 +- 2 files changed, 66 insertions(+), 6 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/ComplexTextFragment.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/ComplexTextFragment.java index 47f245207a47..17b8aa13ac04 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/ComplexTextFragment.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/ComplexTextFragment.java @@ -15,6 +15,7 @@ */ package com.intellij.openapi.editor.impl.view; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.util.BitUtil; import org.jetbrains.annotations.NotNull; @@ -27,6 +28,7 @@ import java.awt.geom.Rectangle2D; * GlyphVector-based text fragment. Used for non-Latin text or when ligatures are enabled */ class ComplexTextFragment extends TextFragment { + private static final Logger LOG = Logger.getInstance(ComplexTextFragment.class); private static final double CLIP_MARGIN = 1e4; @NotNull @@ -90,18 +92,57 @@ class ComplexTextFragment extends TextFragment { } } + boolean isRtl() { + return BitUtil.isSet(myGlyphVector.getLayoutFlags(), GlyphVector.FLAG_RUN_RTL); + } + + // Drawing a portion of glyph vector using clipping might be not very effective + // (we still pass all glyphs to the rendering code, and filtering by clipping might occur late in the processing, + // on OS X larger number of glyphs passed for processing is known to slow down rendering significantly). + // So we try to merge drawing of adjacent glyph vector fragments, if possible. + private static ComplexTextFragment lastFragment; + private static int lastStartColumn; + private static int lastEndColumn; + private static Color lastColor; + private static float lastStartX; + private static float lastEndX; + private static float lastY; + + @SuppressWarnings("AssignmentToStaticFieldFromInstanceMethod") @Override public void draw(Graphics2D g, float x, float y, int startColumn, int endColumn) { - assert startColumn >= 0; + assert startColumn >= 0; assert endColumn <= myCharPositions.length; assert startColumn < endColumn; + + Color color = g.getColor(); + assert color != null; + float newX = x - getX(startColumn) + getX(endColumn); + if (lastFragment == this && lastEndColumn == startColumn && lastEndX == x && lastY == y && color.equals(lastColor)) { + lastEndColumn = endColumn; + lastEndX = newX; + return; + } + + flushDrawingCache(g); + lastFragment = this; + lastStartColumn = startColumn; + lastEndColumn = endColumn; + lastColor = color; + lastStartX = x; + lastEndX = newX; + lastY = y; + } + + private void doDraw(Graphics2D g, float x, float y, int startColumn, int endColumn) { + updateStats(endColumn - startColumn, myCharPositions.length); if (startColumn == 0 && endColumn == myCharPositions.length) { g.drawGlyphVector(myGlyphVector, x, y); } else { Shape savedClip = g.getClip(); float startX = x - getX(startColumn); - // We define clip region here assuming that glyphs do not extend further than CLIP_MARGIN pixels from baseline + // We define clip region here assuming that glyphs do not extend further than CLIP_MARGIN pixels from baseline // vertically (both up and down) and horizontally (from the region defined by glyph vector's total advance) double xMin = x - (startColumn == 0 ? CLIP_MARGIN : 0); double xMax = startX + getX(endColumn) + (endColumn == myCharPositions.length ? CLIP_MARGIN : 0); @@ -112,8 +153,26 @@ class ComplexTextFragment extends TextFragment { g.setClip(savedClip); } } - - boolean isRtl() { - return BitUtil.isSet(myGlyphVector.getLayoutFlags(), GlyphVector.FLAG_RUN_RTL); + + public static void flushDrawingCache(Graphics2D g) { + if (lastFragment != null) { + g.setColor(lastColor); + lastFragment.doDraw(g, lastStartX, lastY, lastStartColumn, lastEndColumn); + lastFragment = null; + } + } + + private static long ourDrawingCount; + private static long ourCharsProcessed; + private static long ourGlyphsProcessed; + + private static void updateStats(int charCount, int glyphCount) { + if (!LOG.isDebugEnabled()) return; + ourCharsProcessed += charCount; + ourGlyphsProcessed += glyphCount; + if ((++ourDrawingCount) % 10000 == 0) { + LOG.debug("Text rendering stats: " + ourCharsProcessed + " chars, " + ourGlyphsProcessed + " glyps, ratio - " + + ((float) ourGlyphsProcessed) / ourCharsProcessed); + } } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorPainter.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorPainter.java index 5083126add33..07332eabc058 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorPainter.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/view/EditorPainter.java @@ -35,7 +35,6 @@ import com.intellij.openapi.wm.impl.IdeBackgroundUtil; import com.intellij.ui.ColorUtil; import com.intellij.ui.Gray; import com.intellij.ui.JBColor; -import com.intellij.util.Processor; import com.intellij.util.ui.JBUI; import com.intellij.util.ui.UIUtil; import gnu.trove.TFloatArrayList; @@ -400,6 +399,7 @@ class EditorPainter implements TextDrawingCallback { }); visLinesIterator.advance(); } + ComplexTextFragment.flushDrawingCache(g); } private float paintLineLayoutWithEffect(Graphics2D g, LineLayout layout, float x, float y, @@ -832,6 +832,7 @@ class EditorPainter implements TextDrawingCallback { break; } } + ComplexTextFragment.flushDrawingCache(g); } } }