JAL-3438 spotless for 2.11.2.0
[jalview.git] / src / jalview / renderer / OverviewRenderer.java
index 48dcf3c..1b5d5da 100644 (file)
@@ -22,8 +22,11 @@ package jalview.renderer;
 
 import jalview.api.AlignmentColsCollectionI;
 import jalview.api.AlignmentRowsCollectionI;
+import jalview.api.RendererListenerI;
 import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Annotation;
+import jalview.datamodel.SequenceGroup;
 import jalview.datamodel.SequenceI;
 import jalview.renderer.seqfeatures.FeatureColourFinder;
 import jalview.renderer.seqfeatures.FeatureRenderer;
@@ -34,12 +37,21 @@ import java.awt.Color;
 import java.awt.Graphics;
 import java.awt.Graphics2D;
 import java.awt.image.BufferedImage;
+import java.beans.PropertyChangeSupport;
 
 public class OverviewRenderer
 {
-  private FeatureColourFinder finder;
+  // transparency of hidden cols/seqs overlay
+  private final float TRANSPARENCY = 0.5f;
+
+  public static final String UPDATE = "OverviewUpdate";
+
+  private static final int MAX_PROGRESS = 100;
+
+  private PropertyChangeSupport changeSupport = new PropertyChangeSupport(
+          this);
 
-  private jalview.api.SequenceRenderer sr;
+  private FeatureColourFinder finder;
 
   // image to render on
   private BufferedImage miniMe;
@@ -50,17 +62,32 @@ public class OverviewRenderer
   // raw number of pixels to allocate to each row
   private float pixelsPerSeq;
 
+  // height in pixels of graph
+  private int graphHeight;
+
   // flag to indicate whether to halt drawing
   private volatile boolean redraw = false;
 
-  public OverviewRenderer(jalview.api.SequenceRenderer seqRenderer,
-          FeatureRenderer fr, OverviewDimensions od)
+  // reference to alignment, needed to get sequence groups
+  private AlignmentI al;
+
+  private ResidueShaderI shader;
+
+  private OverviewResColourFinder resColFinder;
+
+  public OverviewRenderer(FeatureRenderer fr, OverviewDimensions od,
+          AlignmentI alignment, ResidueShaderI resshader,
+          OverviewResColourFinder colFinder)
   {
-    sr = seqRenderer;
     finder = new FeatureColourFinder(fr);
+    resColFinder = colFinder;
+
+    al = alignment;
+    shader = resshader;
 
     pixelsPerCol = od.getPixelsPerCol();
     pixelsPerSeq = od.getPixelsPerSeq();
+    graphHeight = od.getGraphHeight();
     miniMe = new BufferedImage(od.getWidth(), od.getHeight(),
             BufferedImage.TYPE_INT_RGB);
   }
@@ -80,9 +107,12 @@ public class OverviewRenderer
     int rgbcolor = Color.white.getRGB();
     int seqIndex = 0;
     int pixelRow = 0;
+    int alignmentHeight = miniMe.getHeight() - graphHeight;
+    int totalPixels = miniMe.getWidth() * alignmentHeight;
 
-    BufferedImage hiddenImage = buildHiddenImage(rows, cols,
-            miniMe.getWidth(), miniMe.getHeight());
+    int lastRowUpdate = 0;
+    int lastUpdate = 0;
+    changeSupport.firePropertyChange(UPDATE, -1, 0);
 
     for (int alignmentRow : rows)
     {
@@ -90,14 +120,17 @@ public class OverviewRenderer
       {
         break;
       }
-    
+
       // get details of this alignment row
       SequenceI seq = rows.getSequence(alignmentRow);
-    
+
+      // rate limiting step when rendering overview for lots of groups
+      SequenceGroup[] allGroups = al.findAllGroups(seq);
+
       // 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)
@@ -106,21 +139,19 @@ public class OverviewRenderer
         {
           break;
         }
-    
+
         // calculate where this column extends to in pixels
-        int endCol = Math.min(
-                Math.round((colIndex + 1) * pixelsPerCol) - 1,
+        int endCol = Math.min(Math.round((colIndex + 1) * pixelsPerCol) - 1,
                 miniMe.getWidth() - 1);
-    
+
         // 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)
         {
-          rgbcolor = getColumnColourFromSequence(seq,
-                  alignmentCol,
-                  finder);
-    
+          rgbcolor = getColumnColourFromSequence(allGroups, seq,
+                  alignmentCol);
+
           // fill in the appropriate number of pixels
           for (int row = pixelRow; row <= endRow; ++row)
           {
@@ -129,46 +160,118 @@ public class OverviewRenderer
               miniMe.setRGB(col, row, rgbcolor);
             }
           }
-    
+
+          // store last update value
+          lastUpdate = sendProgressUpdate(
+                  (pixelCol + 1) * (endRow - pixelRow), totalPixels,
+                  lastRowUpdate, lastUpdate);
+
           pixelCol = endCol + 1;
         }
         colIndex++;
       }
-      pixelRow = endRow + 1;
+
+      if (pixelRow != endRow + 1)
+      {
+        // store row offset and last update value
+        lastRowUpdate = sendProgressUpdate(endRow + 1, alignmentHeight, 0,
+                lastUpdate);
+        lastUpdate = lastRowUpdate;
+        pixelRow = endRow + 1;
+      }
       seqIndex++;
     }
 
-    return applyMask(hiddenImage, miniMe);
+    overlayHiddenRegions(rows, cols);
+    // final update to progress bar if present
+    if (redraw)
+    {
+      sendProgressUpdate(pixelRow - 1, alignmentHeight, 0, 0);
+    }
+    else
+    {
+      sendProgressUpdate(alignmentHeight, miniMe.getHeight(), 0, 0);
+    }
+    return miniMe;
   }
 
   /*
-   * Find the colour of a sequence at a specified column position
+   * Calculate progress update value and fire event
+   * @param rowOffset number of rows to offset calculation by
+   * @return new rowOffset - return value only to be used when at end of a row
    */
-  private int getColumnColourFromSequence(jalview.datamodel.SequenceI seq,
-          int lastcol, FeatureColourFinder fcfinder)
+  private int sendProgressUpdate(int position, int maximum, int rowOffset,
+          int lastUpdate)
   {
-    Color color = Color.white;
+    int newUpdate = rowOffset
+            + Math.round(MAX_PROGRESS * ((float) position / maximum));
+    if (newUpdate > lastUpdate)
+    {
+      changeSupport.firePropertyChange(UPDATE, rowOffset, newUpdate);
+      return newUpdate;
+    }
+    return newUpdate;
+  }
+
+  /*
+   * Find the RGB value of 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
+   * @return colour of sequence at this position, as RGB
+   */
+  int getColumnColourFromSequence(SequenceGroup[] allGroups, SequenceI seq,
+          int lastcol)
+  {
+    Color color = resColFinder.GAP_COLOUR;
 
     if ((seq != null) && (seq.getLength() > lastcol))
     {
-      color = sr.getResidueColour(seq, lastcol, fcfinder);
+      color = resColFinder.getResidueColour(true, shader, allGroups, seq,
+              lastcol, finder);
     }
 
     return color.getRGB();
   }
 
-  private BufferedImage applyMask(BufferedImage mask, BufferedImage image)
+  /**
+   * 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)
   {
-    Graphics2D g = (Graphics2D) image.getGraphics();
-    g.setComposite(
-            AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
-    g.drawImage(mask, 0, 0, image.getWidth(), image.getHeight(), null);
-    return image;
+    if (cols.hasHidden() || rows.hasHidden())
+    {
+      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);
+    }
   }
 
-  /*
-   * Build a masking image of hidden columns and rows to be applied on top
-   * of the main overview image.
+  /**
+   * 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)
@@ -180,13 +283,13 @@ public class OverviewRenderer
     int colIndex = 0;
     int pixelCol = 0;
 
-    Color hidden = Color.DARK_GRAY;
+    Color hidden = resColFinder.getHiddenColour();
 
     Graphics2D g2d = (Graphics2D) hiddenImage.getGraphics();
 
     // set background to transparent
-    g2d.setComposite(AlphaComposite.Clear);
-    g2d.fillRect(0, 0, width, height);
+    // g2d.setComposite(AlphaComposite.Clear);
+    // g2d.fillRect(0, 0, width, height);
 
     // set next colour to opaque
     g2d.setComposite(AlphaComposite.Src);
@@ -250,15 +353,13 @@ public class OverviewRenderer
    *          the graphics object to draw on
    * @param anno
    *          alignment annotation information
-   * @param charWidth
-   *          alignment character width value
    * @param y
    *          y-position for the annotation graph
    * @param cols
    *          the collection of columns used in the overview panel
    */
-  public void drawGraph(Graphics g, AlignmentAnnotation anno, int charWidth,
-          int y, AlignmentColsCollectionI cols)
+  public void drawGraph(Graphics g, AlignmentAnnotation anno, int y,
+          AlignmentColsCollectionI cols)
   {
     Annotation[] annotations = anno.annotations;
     g.setColor(Color.white);
@@ -271,16 +372,17 @@ public class OverviewRenderer
     {
       if (redraw)
       {
+        changeSupport.firePropertyChange(UPDATE, MAX_PROGRESS - 1, 0);
         break;
       }
+
       if (alignmentCol >= annotations.length)
       {
         break; // no more annotations to draw here
       }
       else
       {
-        int endCol = Math.min(
-                Math.round((colIndex + 1) * pixelsPerCol) - 1,
+        int endCol = Math.min(Math.round((colIndex + 1) * pixelsPerCol) - 1,
                 miniMe.getWidth() - 1);
 
         if (annotations[alignmentCol] != null)
@@ -294,7 +396,8 @@ public class OverviewRenderer
             g.setColor(annotations[alignmentCol].colour);
           }
 
-          height = (int) ((annotations[alignmentCol].value / anno.graphMax) * y);
+          height = (int) ((annotations[alignmentCol].value / anno.graphMax)
+                  * y);
           if (height > y)
           {
             height = y;
@@ -302,12 +405,22 @@ public class OverviewRenderer
 
           g.fillRect(pixelCol, y - height, endCol - pixelCol + 1, height);
         }
+
         pixelCol = endCol + 1;
         colIndex++;
       }
     }
+    changeSupport.firePropertyChange(UPDATE, MAX_PROGRESS - 1,
+            MAX_PROGRESS);
   }
 
+  /**
+   * 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)
@@ -315,4 +428,14 @@ public class OverviewRenderer
       redraw = b;
     }
   }
+
+  public void addPropertyChangeListener(RendererListenerI listener)
+  {
+    changeSupport.addPropertyChangeListener(listener);
+  }
+
+  public void removePropertyChangeListener(RendererListenerI listener)
+  {
+    changeSupport.removePropertyChangeListener(listener);
+  }
 }