From: hansonr Date: Wed, 4 Sep 2019 06:27:00 +0000 (-0500) Subject: JAL-3429 JAL-3253-applet X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=6f2c3b8cb30d8ccf7ff9ab949a7b244f0c415bc1;p=jalview.git JAL-3429 JAL-3253-applet OverviewRenderer not implementing hidden column mode using BitSet. --- diff --git a/src/jalview/renderer/OverviewRenderer.java b/src/jalview/renderer/OverviewRenderer.java index 36b5847..c3350a5 100644 --- a/src/jalview/renderer/OverviewRenderer.java +++ b/src/jalview/renderer/OverviewRenderer.java @@ -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) {