JAL-2778 try another way to draw sequences with reduced synchronisation
[jalview.git] / src / jalview / gui / SeqCanvas.java
index ce835a3..1be4728 100755 (executable)
@@ -40,10 +40,10 @@ import java.awt.Graphics;
 import java.awt.Graphics2D;
 import java.awt.RenderingHints;
 import java.awt.Shape;
-import java.awt.Transparency;
 import java.awt.image.BufferedImage;
 import java.beans.PropertyChangeEvent;
 import java.util.List;
+import java.util.function.Consumer;
 
 import javax.swing.JComponent;
 
@@ -340,8 +340,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
 
       // Call repaint on alignment panel so that repaints from other alignment
       // panel components can be aggregated. Otherwise performance of the
-      // overview
-      // window and others may be adversely affected.
+      // overview window and others may be adversely affected.
       av.getAlignPanel().repaint();
     } finally
     {
@@ -1100,71 +1099,97 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
    * @param yOffset
    *          vertical offset at which to draw (for wrapped alignments)
    */
-  private void draw(Graphics g, int startRes, int endRes, int startSeq,
-          int endSeq, int offset)
+  private void draw(final Graphics g, final int startRes, final int endRes,
+          final int startSeq, final int endSeq, final int offset)
   {
-    int charHeight = av.getCharHeight();
-    int charWidth = av.getCharWidth();
-
     g.setFont(av.getFont());
     seqRdr.prepare(g, av.isRenderGaps());
 
-    SequenceI nextSeq;
-
-    // / First draw the sequences
-    // ///////////////////////////
-    for (int i = startSeq; i <= endSeq; i++)
+    // First draw the sequences
+    av.getAlignment().forEachSequence(new Consumer<SequenceI>()
     {
-      nextSeq = av.getAlignment().getSequenceAt(i);
-      if (nextSeq == null)
-      {
-        // occasionally, a race condition occurs such that the alignment row is
-        // empty
-        continue;
-      }
-      seqRdr.drawSequence(nextSeq, av.getAlignment().findAllGroups(nextSeq),
-              startRes, endRes, offset + ((i - startSeq) * charHeight));
+      int i = startSeq;
 
-      if (av.isShowSequenceFeatures())
+      @Override
+      public void accept(SequenceI s)
       {
-        fr.drawSequence(g, nextSeq, startRes, endRes,
-                offset + ((i - startSeq) * charHeight), false);
+        int heightPosition = offset + ((i - startSeq) * av.getCharHeight());
+        drawSequence(s, g, startRes, endRes, heightPosition, i);
+        i++;
       }
+    }, startSeq, endSeq + 1);
 
-      /*
-       * highlight search Results once sequence has been drawn
-       */
-      if (av.hasSearchResults())
+    // now selection groups
+    if (av.getSelectionGroup() != null
+            || av.getAlignment().getGroups().size() > 0)
+    {
+      drawGroupsBoundaries(g, startRes, endRes, startSeq, endSeq, offset);
+    }
+
+  }
+
+  /**
+   * Draw a single sequence
+   * 
+   * @param nextSeq
+   *          the next sequence to draw
+   * @param g
+   *          graphics context
+   * @param startRes
+   *          offset of the first column in the visible region (0..)
+   * @param endRes
+   *          offset of the last column in the visible region (0..)
+   * @param heightPosition
+   *          vertical location of the sequence in the alignment
+   * @param i
+   *          index of sequence
+   */
+  private void drawSequence(SequenceI nextSeq, Graphics g, int startRes,
+          int endRes, int heightPosition, int i)
+  {
+    int charWidth = av.getCharWidth();
+
+    if (nextSeq == null)
+    {
+      // occasionally, a race condition occurs such that the alignment row is
+      // empty
+      // TODO Don't think this will happen any more?
+      return;
+    }
+    seqRdr.drawSequence(nextSeq, av.getAlignment().findAllGroups(nextSeq),
+            startRes, endRes, heightPosition);
+
+    if (av.isShowSequenceFeatures())
+    {
+      fr.drawSequence(g, nextSeq, startRes, endRes, heightPosition, false);
+    }
+
+    /*
+     * highlight search Results once sequence has been drawn
+     */
+    if (av.hasSearchResults())
+    {
+      SearchResultsI searchResults = av.getSearchResults();
+      int[] visibleResults = searchResults.getResults(nextSeq, startRes,
+              endRes);
+      if (visibleResults != null)
       {
-        SearchResultsI searchResults = av.getSearchResults();
-        int[] visibleResults = searchResults.getResults(nextSeq,
-                startRes, endRes);
-        if (visibleResults != null)
+        for (int r = 0; r < visibleResults.length; r += 2)
         {
-          for (int r = 0; r < visibleResults.length; r += 2)
-          {
-            seqRdr.drawHighlightedText(nextSeq, visibleResults[r],
-                    visibleResults[r + 1], (visibleResults[r] - startRes)
-                            * charWidth, offset
-                            + ((i - startSeq) * charHeight));
-          }
+          seqRdr.drawHighlightedText(nextSeq, visibleResults[r],
+                  visibleResults[r + 1],
+                  (visibleResults[r] - startRes) * charWidth,
+                  heightPosition);
         }
       }
-
-      if (av.cursorMode && cursorY == i && cursorX >= startRes
-              && cursorX <= endRes)
-      {
-        seqRdr.drawCursor(nextSeq, cursorX, (cursorX - startRes) * charWidth,
-                offset + ((i - startSeq) * charHeight));
-      }
     }
 
-    if (av.getSelectionGroup() != null
-            || av.getAlignment().getGroups().size() > 0)
+    if (av.cursorMode && cursorY == i && cursorX >= startRes
+            && cursorX <= endRes)
     {
-      drawGroupsBoundaries(g, startRes, endRes, startSeq, endSeq, offset);
+      seqRdr.drawCursor(nextSeq, cursorX, (cursorX - startRes) * charWidth,
+              heightPosition);
     }
-
   }
 
   void drawGroupsBoundaries(Graphics g1, int startRes, int endRes,
@@ -1696,37 +1721,37 @@ public class SeqCanvas extends JComponent implements ViewportListenerI
       {
         scrollX = -range;
       }
+    }
 
-      // Both scrolling and resizing change viewport ranges: scrolling changes
-      // both start and end points, but resize only changes end values.
-      // Here we only want to fastpaint on a scroll, with resize using a normal
-      // paint, so scroll events are identified as changes to the horizontal or
-      // vertical start value.
-      if (eventName.equals(ViewportRanges.STARTRES))
-      {
-         if (av.getWrapAlignment())
-          {
-            fastPaintWrapped(scrollX);
-          }
-          else
-          {
-          fastPaint(scrollX, 0);
-          }
-      }
-      else if (eventName.equals(ViewportRanges.STARTSEQ))
+    // Both scrolling and resizing change viewport ranges: scrolling changes
+    // both start and end points, but resize only changes end values.
+    // Here we only want to fastpaint on a scroll, with resize using a normal
+    // paint, so scroll events are identified as changes to the horizontal or
+    // vertical start value.
+    if (eventName.equals(ViewportRanges.STARTRES))
+    {
+      if (av.getWrapAlignment())
       {
-        // scroll
-        fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue());
+        fastPaintWrapped(scrollX);
       }
-      else if (eventName.equals(ViewportRanges.STARTRESANDSEQ))
+      else
       {
         fastPaint(scrollX, 0);
-        // bizarrely, we only need to scroll on the x value here as fastpaint
-        // copies the full height of the image anyway. Passing in the y value
-        // causes nasty repaint artefacts, which only disappear on a full
-        // repaint.
       }
     }
+    else if (eventName.equals(ViewportRanges.STARTSEQ))
+    {
+      // scroll
+      fastPaint(0, (int) evt.getNewValue() - (int) evt.getOldValue());
+    }
+    else if (eventName.equals(ViewportRanges.STARTRESANDSEQ))
+    {
+      fastPaint(scrollX, 0);
+      // bizarrely, we only need to scroll on the x value here as fastpaint
+      // copies the full height of the image anyway. Passing in the y value
+      // causes nasty repaint artefacts, which only disappear on a full
+      // repaint.
+    }
   }
 
   /**