JAL-2664 Updates following review
[jalview.git] / src / jalview / renderer / OverviewRenderer.java
index 75b8198..1fb2d89 100644 (file)
@@ -26,14 +26,20 @@ import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.Annotation;
 import jalview.datamodel.SequenceI;
 import jalview.renderer.seqfeatures.FeatureColourFinder;
+import jalview.renderer.seqfeatures.FeatureRenderer;
 import jalview.viewmodel.OverviewDimensions;
 
+import java.awt.AlphaComposite;
 import java.awt.Color;
 import java.awt.Graphics;
+import java.awt.Graphics2D;
 import java.awt.image.BufferedImage;
 
 public class OverviewRenderer
 {
+  // transparency of hidden cols/seqs overlay
+  private final float TRANSPARENCY = 0.5f;
+
   private FeatureColourFinder finder;
 
   private jalview.api.SequenceRenderer sr;
@@ -47,11 +53,14 @@ public class OverviewRenderer
   // raw number of pixels to allocate to each row
   private float pixelsPerSeq;
 
+  // flag to indicate whether to halt drawing
+  private volatile boolean redraw = false;
+
   public OverviewRenderer(jalview.api.SequenceRenderer seqRenderer,
-          FeatureColourFinder colfinder, OverviewDimensions od)
+          FeatureRenderer fr, OverviewDimensions od)
   {
     sr = seqRenderer;
-    finder = colfinder;
+    finder = new FeatureColourFinder(fr);
 
     pixelsPerCol = od.getPixelsPerCol();
     pixelsPerSeq = od.getPixelsPerSeq();
@@ -74,52 +83,78 @@ public class OverviewRenderer
     int rgbcolor = Color.white.getRGB();
     int seqIndex = 0;
     int pixelRow = 0;
+
     for (int alignmentRow : rows)
     {
+      if (redraw)
+      {
+        break;
+      }
+    
       // get details of this alignment row
-      boolean hidden = rows.isHidden(alignmentRow);
       SequenceI seq = rows.getSequence(alignmentRow);
-
+    
       // calculate where this row extends to in pixels
       int endRow = Math.min(Math.round((seqIndex + 1) * pixelsPerSeq) - 1,
               miniMe.getHeight() - 1);
-
+    
       int colIndex = 0;
       int pixelCol = 0;
       for (int alignmentCol : cols)
       {
+        if (redraw)
+        {
+          break;
+        }
+    
         // calculate where this column extends to in pixels
         int endCol = Math.min(
                 Math.round((colIndex + 1) * pixelsPerCol) - 1,
                 miniMe.getWidth() - 1);
-
-        // determine the colour based on the sequence and column position
-        rgbcolor = getColumnColourFromSequence(seq,
-                hidden || cols.isHidden(alignmentCol), alignmentCol, finder);
-
-        // fill in the appropriate number of pixels
-        for (int row = pixelRow; row <= endRow; ++row)
+    
+        // don't do expensive colour determination if we're not going to use it
+        // NB this is important to avoid performance issues in the overview
+        // panel
+        if (pixelCol <= endCol)
         {
-          for (int col = pixelCol; col <= endCol; ++col)
+          rgbcolor = getColumnColourFromSequence(seq,
+                  alignmentCol,
+                  finder);
+    
+          // fill in the appropriate number of pixels
+          for (int row = pixelRow; row <= endRow; ++row)
           {
-            miniMe.setRGB(col, row, rgbcolor);
+            for (int col = pixelCol; col <= endCol; ++col)
+            {
+              miniMe.setRGB(col, row, rgbcolor);
+            }
           }
+    
+          pixelCol = endCol + 1;
         }
-
-        pixelCol = endCol + 1;
         colIndex++;
       }
       pixelRow = endRow + 1;
       seqIndex++;
     }
+
+    overlayHiddenRegions(rows, cols);
     return miniMe;
   }
 
-  /*
+  /**
    * Find the colour of a sequence at a specified column position
+   * 
+   * @param seq
+   *          sequence to get colour for
+   * @param lastcol
+   *          column position to get colour for
+   * @param fcfinder
+   *          FeatureColourFinder to use
+   * @return colour of sequence at this position, as RGB
    */
   private int getColumnColourFromSequence(jalview.datamodel.SequenceI seq,
-          boolean isHidden, int lastcol, FeatureColourFinder fcfinder)
+          int lastcol, FeatureColourFinder fcfinder)
   {
     Color color = Color.white;
 
@@ -128,12 +163,117 @@ public class OverviewRenderer
       color = sr.getResidueColour(seq, lastcol, fcfinder);
     }
 
-    if (isHidden)
+    return color.getRGB();
+  }
+
+  /**
+   * Overlay the hidden regions on the overview image
+   * 
+   * @param rows
+   *          collection of rows the overview is built over
+   * @param cols
+   *          collection of columns the overview is built over
+   */
+  private void overlayHiddenRegions(AlignmentRowsCollectionI rows,
+          AlignmentColsCollectionI cols)
+  {
+    if (cols.hasHidden() || rows.hasHidden())
     {
-      color = color.darker().darker();
+      BufferedImage mask = buildHiddenImage(rows, cols, miniMe.getWidth(),
+              miniMe.getHeight());
+
+      Graphics2D g = (Graphics2D) miniMe.getGraphics();
+      g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
+              TRANSPARENCY));
+      g.drawImage(mask, 0, 0, miniMe.getWidth(), miniMe.getHeight(), null);
     }
+  }
 
-    return color.getRGB();
+  /**
+   * Build a masking image of hidden columns and rows to be applied on top of
+   * the main overview image.
+   * 
+   * @param rows
+   *          collection of rows the overview is built over
+   * @param cols
+   *          collection of columns the overview is built over
+   * @param width
+   *          width of overview in pixels
+   * @param height
+   *          height of overview in pixels
+   * @return BufferedImage containing mask of hidden regions
+   */
+  private BufferedImage buildHiddenImage(AlignmentRowsCollectionI rows,
+          AlignmentColsCollectionI cols, int width, int height)
+  {
+    // new masking image
+    BufferedImage hiddenImage = new BufferedImage(width, height,
+            BufferedImage.TYPE_INT_ARGB);
+
+    int colIndex = 0;
+    int pixelCol = 0;
+
+    Color hidden = Color.DARK_GRAY.darker();
+
+    Graphics2D g2d = (Graphics2D) hiddenImage.getGraphics();
+
+    // set background to transparent
+    g2d.setComposite(AlphaComposite.Clear);
+    g2d.fillRect(0, 0, width, height);
+
+    // set next colour to opaque
+    g2d.setComposite(AlphaComposite.Src);
+
+    for (int alignmentCol : cols)
+    {
+      if (redraw)
+      {
+        break;
+      }
+
+      // calculate where this column extends to in pixels
+      int endCol = Math.min(Math.round((colIndex + 1) * pixelsPerCol) - 1,
+              hiddenImage.getWidth() - 1);
+
+      if (pixelCol <= endCol)
+      {
+        // determine the colour based on the sequence and column position
+        if (cols.isHidden(alignmentCol))
+        {
+          g2d.setColor(hidden);
+          g2d.fillRect(pixelCol, 0, endCol - pixelCol + 1, height);
+        }
+
+        pixelCol = endCol + 1;
+      }
+      colIndex++;
+
+    }
+
+    int seqIndex = 0;
+    int pixelRow = 0;
+    for (int alignmentRow : rows)
+    {
+      if (redraw)
+      {
+        break;
+      }
+
+      // calculate where this row extends to in pixels
+      int endRow = Math.min(Math.round((seqIndex + 1) * pixelsPerSeq) - 1,
+              miniMe.getHeight() - 1);
+
+      // get details of this alignment row
+      if (rows.isHidden(alignmentRow))
+      {
+        g2d.setColor(hidden);
+        g2d.fillRect(0, pixelRow, width, endRow - pixelRow + 1);
+      }
+      pixelRow = endRow + 1;
+      seqIndex++;
+    }
+
+    return hiddenImage;
   }
 
   /**
@@ -162,6 +302,10 @@ public class OverviewRenderer
     int pixelCol = 0;
     for (int alignmentCol : cols)
     {
+      if (redraw)
+      {
+        break;
+      }
       if (alignmentCol >= annotations.length)
       {
         break; // no more annotations to draw here
@@ -196,4 +340,19 @@ public class OverviewRenderer
       }
     }
   }
+
+  /**
+   * Allows redraw flag to be set
+   * 
+   * @param b
+   *          value to set redraw to: true = redraw is occurring, false = no
+   *          redraw
+   */
+  public void setRedraw(boolean b)
+  {
+    synchronized (this)
+    {
+      redraw = b;
+    }
+  }
 }