JAL-3429 JAL-3253-applet
authorhansonr <hansonr@STO24954W.ad.stolaf.edu>
Wed, 4 Sep 2019 06:27:00 +0000 (01:27 -0500)
committerhansonr <hansonr@STO24954W.ad.stolaf.edu>
Wed, 4 Sep 2019 06:27:00 +0000 (01:27 -0500)
OverviewRenderer not implementing hidden column mode using BitSet.

src/jalview/renderer/OverviewRenderer.java

index 36b5847..c3350a5 100644 (file)
@@ -57,6 +57,26 @@ public class OverviewRenderer
 
   private static final int MAX_PROGRESS = 100;
 
+  final static int STATE_INIT = 0;
+
+  final static int STATE_NEXT = 1;
+
+  final static int STATE_DONE = 2;
+
+  private int state;
+
+  private Timer timer;
+
+  private boolean isJS = Platform.isJS();
+
+  private int delay = (isJS ? 1 : 0);
+
+  private int seqIndex;
+
+  private int pixelRow;
+
+  private Integer row;
+
   private PropertyChangeSupport changeSupport = new PropertyChangeSupport(
           this);
 
@@ -76,9 +96,23 @@ public class OverviewRenderer
    */
   private float colsPerPixel;
 
-  // raw number of pixels to allocate to each row
+  /**
+   * raw number of pixels to allocate to each row
+   */
   private float pixelsPerSeq;
 
+  /**
+   * true when colsPerPixel > 1
+   */
+  private boolean skippingColumns;
+
+  /**
+   * pre-calculated list of columns needed for a "dense" overview, where there
+   * are more columns than pixels
+   */
+
+  private int[] columnsToShow;
+
   // height in pixels of graph
   private int graphHeight;
 
@@ -122,43 +156,29 @@ public class OverviewRenderer
           OverviewResColourFinder colFinder, boolean showProgress)
   {
     {
-    this.panel = panel;
-    finder = new FeatureColourFinder(fr);
-    al = alignment;
-    shader = resshader;
-    resColFinder = colFinder;
-    this.showProgress = showProgress;
-
-    w = od.getWidth();
-    h = od.getHeight();
-    rows = od.getRows(alignment);
-    cols = od.getColumns(alignment);
-    graphHeight = od.getGraphHeight();
-    alignmentHeight = od.getSequencesHeight();
-
-    pixelsPerSeq = od.getPixelsPerSeq();
-    pixelsPerCol = od.getPixelsPerCol();
-    colsPerPixel = Math.max(1, 1f / pixelsPerCol);
+      this.panel = panel;
+      finder = new FeatureColourFinder(fr);
+      al = alignment;
+      shader = resshader;
+      resColFinder = colFinder;
+      this.showProgress = showProgress;
+
+      w = od.getWidth();
+      h = od.getHeight();
+      rows = od.getRows(alignment);
+      cols = od.getColumns(alignment);
+      graphHeight = od.getGraphHeight();
+      alignmentHeight = od.getSequencesHeight();
+
+      pixelsPerSeq = od.getPixelsPerSeq();
+      pixelsPerCol = od.getPixelsPerCol();
+      colsPerPixel = Math.max(1, 1f / pixelsPerCol);
+
+      skippingColumns = (pixelsPerCol < 1);
+
     }
   }
 
-  final static int STATE_INIT = 0;
-  final static int STATE_NEXT = 1;
-  final static int STATE_DONE = 2;
-
-  int state;
-
-  boolean isJS = Platform.isJS();
-
-  Timer timer;
-  int delay = (isJS ? 1 : 0);
-
-  int seqIndex;
-
-  int pixelRow;
-
-  private Integer row;
-
   /**
    * Draw alignment rows and columns onto an image. This method is asynchronous
    * in JavaScript and interruptible in Java.
@@ -225,7 +245,6 @@ public class OverviewRenderer
     lastRowUpdate = 0;
     lastUpdate = 0;
     totalPixels = w * alignmentHeight;
-
     if (showProgress)
     {
       changeSupport.firePropertyChange(UPDATE, -1, 0);
@@ -236,13 +255,17 @@ public class OverviewRenderer
     DataBufferInt db = (DataBufferInt) raster.getDataBuffer();
     pixels = db.getBankData()[0];
     bscol = cols.getOverviewBitSet();
+    if (skippingColumns)
+    {
+      columnsToShow = calcColumnsToShow();
+    }
+
     Platform.timeCheck(null, Platform.TIME_MARK);
   }
 
   private void nextRow()
   {
     row = rowIterator.next();
-    // get details of this alignment row
     SequenceI seq = rows.getSequence(row);
 
     // rate limiting step when rendering overview for lots of groups
@@ -250,9 +273,13 @@ public class OverviewRenderer
 
     // calculate where this row extends to in pixels
     int endRow = Math.min(Math.round((++seqIndex) * pixelsPerSeq), h);
+    // this is the key modification -- we use bscol to jump to the next column
+    // when there are more columns than pixels.
+
     for (int pixelCol = 0, colNext = 0, pixelEnd = 0, icol = bscol
-            .nextSetBit(0); icol >= 0; icol = getNextCol(icol, pixelEnd))
+            .nextSetBit(0); icol >= 0; icol = getNextCol(icol, colNext))
     {
+      // asynchronous exit flag
       if (redraw)
       {
         break;
@@ -269,10 +296,6 @@ public class OverviewRenderer
       {
         int rgb = getColumnColourFromSequence(allGroups, seq, icol);
         // fill in the appropriate number of pixels
-        // System.out.println(
-        // "OR colNext=" + colNext + " " + pixelCol
-        // + "-" + pixelEnd + " icol=" + icol + " " + rgb + " "
-        // + pixelsPerCol);
         for (int row = pixelRow; row < endRow; ++row)
         {
           for (int col = pixelCol; col < pixelEnd; ++col)
@@ -292,8 +315,8 @@ public class OverviewRenderer
         if (showProgress)
         {
           lastUpdate = sendProgressUpdate(
-                  pixelEnd * (endRow - 1 - pixelRow),
-                  totalPixels, lastRowUpdate, lastUpdate);
+                  pixelEnd * (endRow - 1 - pixelRow), totalPixels,
+                  lastRowUpdate, lastUpdate);
         }
       }
 
@@ -314,27 +337,92 @@ public class OverviewRenderer
   }
 
   /**
-   * The next column is either the next set bit (when there are multiple pixels
-   * per column) or the next set bit for the column that aligns with the next
-   * pixel (when there are more columns than pixels).
+   * Precalculate the columns that will be used for each pixel in a dense
+   * overview. So we have to skip through the bscol BitSet to pick up one
+   * (representative?) column for each pixel.
+   * 
+   * Note that there is no easy solution if we want to do color averaging, but
+   * this method might be adapted to do that. Or it could be adapted to pick the
+   * "most representative color" for a group of columns.
+   * 
+   * @author Bob Hanson 2019.09.03
+   * @return a -1 terminated int[]
+   */
+  private int[] calcColumnsToShow()
+  {
+    int[] a = new int[w + 1];
+    float colBuffer = 0;
+    float offset = bscol.nextSetBit(0);
+    if (offset < 0)
+    {
+      return new int[] { -1 };
+    }
+    int pixel = 0;
+    a[pixel++] = (int) offset;
+    // for example, say we have 10 pixels per column:
+    // ...............xxxxxxxx....xxxxxx.........xxxxxx......
+    // nextSet(i).....^...........^..............^...........
+    // nextClear..............^.........^..............^.....
+    // run lengths....|--n1--|....|-n2-|.........|-n3-|......
+    // 10 pixel/col...|---pixel1---||-----pixel2------|......
+    // pixel..........^0............^1.......................
+    for (int i, iClear = -1; pixel < w
+            && (i = bscol.nextSetBit(iClear + 1)) >= 0;)
+    {
+      // find the next clear bit
+      iClear = bscol.nextClearBit(i + 1);
+      // add the run length n1, n2, n3 to grow the column buffer
+      colBuffer += iClear - i; // n1, n2, etc.
+      // add columns if we have accumulated enough pixels
+
+      while (colBuffer > colsPerPixel && pixel < w)
+      {
+        colBuffer -= colsPerPixel;
+        offset += colsPerPixel;
+        a[pixel++] = i + (int) offset;
+      }
+      // set back column pointer relative to the next run
+      offset = -colBuffer;
+    }
+    // add a terminator
+    a[pixel] = -1;
+    return a;
+  }
+
+  /**
+   * The next column is either a precalculated pixel (when there are multiple
+   * pixels per column) or the next set bit for the column that aligns with the
+   * next pixel (when there are more columns than pixels).
+   * 
+   * When columns are hidden, this value is precalculated; otherwise it is
+   * calculated here.
    * 
    * @param icol
    * @param pixel
+   *          pixel pointer into columnsToShow
    * @return
    */
   private int getNextCol(int icol, int pixel)
   {
-    return bscol.nextSetBit(
-            pixelsPerCol >= 1 ? icol + 1 : (int) (pixel * colsPerPixel));
+    return (skippingColumns ? columnsToShow[pixel]
+            : bscol.nextSetBit(icol + 1));
   }
 
+  /**
+   * Derive the next pixel from either as the given pixel (when we are skipping
+   * columns because this is a dense overview and the pixel known), or from the
+   * current column based on pixels/column. The latter is used for drawing the
+   * hidden-column mask or for overviews that have more pixels across than
+   * columns.
+   * 
+   * @param icol
+   * @param pixel
+   * @return
+   */
   private int getNextPixel(int icol, int pixel)
   {
-    return Math.min(
-            pixelsPerCol >= 1 || pixel == 0
-                    ? Math.round(icol * pixelsPerCol)
-                    : pixel,
-            w);
+    return Math.min(skippingColumns && pixel > 0 ? pixel
+            : Math.round(icol * pixelsPerCol), w);
   }
 
   private ActionListener listener = new ActionListener()
@@ -572,7 +660,7 @@ public class OverviewRenderer
 
     for (int pixelCol = 0, colNext = 0, pixelEnd = 0, len = annotations.length, icol = bscol
             .nextSetBit(0); icol >= 0
-                    && icol < len; icol = getNextCol(icol, pixelEnd))
+                    && icol < len; icol = getNextCol(icol, colNext))
     {
       if (redraw)
       {