X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Frenderer%2FOverviewRenderer.java;h=6defca7d0db6e60180e7049b644aab2904568b57;hb=4435c731351c802772dd1355fdb5747cfacd7838;hp=45cc944c0010a88edc1fc29590b8168337abb1c1;hpb=a435a8668f8df568d3bdcf4e286210b69c18920c;p=jalview.git diff --git a/src/jalview/renderer/OverviewRenderer.java b/src/jalview/renderer/OverviewRenderer.java index 45cc944..6defca7 100644 --- a/src/jalview/renderer/OverviewRenderer.java +++ b/src/jalview/renderer/OverviewRenderer.java @@ -66,9 +66,17 @@ public class OverviewRenderer // image to render on private BufferedImage miniMe; - // raw number of pixels to allocate to each column + /** + * Number of pixelsPerCol; + */ private float pixelsPerCol; + /** + * Number of visible columns per pixel. + * + */ + private float colsPerPixel; + // raw number of pixels to allocate to each row private float pixelsPerSeq; @@ -89,6 +97,8 @@ public class OverviewRenderer private AlignmentViewPanel panel; + private int sequencesHeight; + public OverviewRenderer(AlignmentViewPanel panel, FeatureRenderer fr, OverviewDimensions od, AlignmentI alignment, @@ -98,24 +108,28 @@ public class OverviewRenderer } public OverviewRenderer(AlignmentViewPanel panel, - jalview.api.FeatureRenderer fr, - OverviewDimensions od, + jalview.api.FeatureRenderer fr, OverviewDimensions od, AlignmentI alignment, ResidueShaderI resshader, OverviewResColourFinder colFinder, boolean showProgress) { this.panel = panel; finder = new FeatureColourFinder(fr); - resColFinder = colFinder; - al = alignment; shader = resshader; + resColFinder = colFinder; + this.showProgress = showProgress; - pixelsPerCol = od.getPixelsPerCol(); - pixelsPerSeq = od.getPixelsPerSeq(); + w = od.getWidth(); + h = od.getHeight(); + rows = od.getRows(alignment); + cols = od.getColumns(alignment); graphHeight = od.getGraphHeight(); - miniMe = new BufferedImage(od.getWidth(), od.getHeight(), - BufferedImage.TYPE_INT_RGB); - this.showProgress = showProgress; + alignmentHeight = od.getSequencesHeight(); + + pixelsPerSeq = od.getPixelsPerSeq(); + pixelsPerCol = od.getPixelsPerCol(); + colsPerPixel = Math.max(1, 1f / pixelsPerCol); + } final static int STATE_INIT = 0; @@ -135,65 +149,87 @@ public class OverviewRenderer private Integer row; - void mainLoop() + /** + * Draw alignment rows and columns onto an image. This method is asynchronous + * in JavaScript and interruptible in Java. + * + * Whether hidden rows or columns are drawn depends upon the type of + * collection. + * + * Updated to skip through high-density sequences, where columns/pixels > 1. + * + * When the process is complete, the image is passed to the AlignmentViewPanel + * provided by the constructor. + * + * @param rows + * collection of rows to be drawn + * @param cols + * collection of columns to be drawn + * @return image containing the drawing + * + * @author Bob Hanson 2019.07.30 + */ + public void drawMiniMe() { - while (!redraw) + state = STATE_INIT; + mainLoop(); + } + + protected void mainLoop() + { + out: while (!redraw) { switch (state) { case STATE_INIT: - seqIndex = 0; - pixelRow = 0; + init(); state = STATE_NEXT; continue; case STATE_NEXT: - if (iter.hasNext()) + if (!rowIterator.hasNext()) { - nextRow(); + state = STATE_DONE; + continue; } - else + nextRow(); + if (!loop()) { - state = STATE_DONE; + continue; } - break; - case STATE_DONE: - done(); - return; - } - if (delay > 0) - { - jsloop(); return; + case STATE_DONE: + break out; } + // Java will continue without a timeout } done(); } - private void jsloop() + private void init() { - if (timer == null) - { - timer = new Timer(delay, new ActionListener() - { - @Override - public void actionPerformed(ActionEvent e) - { - mainLoop(); - } + rowIterator = rows.iterator(); + seqIndex = 0; + pixelRow = 0; + lastRowUpdate = 0; + lastUpdate = 0; + totalPixels = w * alignmentHeight; - }); - timer.setRepeats(false); - timer.start(); - } - else + if (showProgress) { - timer.restart(); + changeSupport.firePropertyChange(UPDATE, -1, 0); } + + miniMe = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); + WritableRaster raster = miniMe.getRaster(); + DataBufferInt db = (DataBufferInt) raster.getDataBuffer(); + Platform.timeCheck(null, Platform.TIME_MARK); + pixels = db.getBankData()[0]; + bscol = cols.getOverviewBitSet(); } private void nextRow() { - row = iter.next(); + row = rowIterator.next(); // System.out.println("OR row " + r); // get details of this alignment row SequenceI seq = rows.getSequence(row); @@ -203,30 +239,32 @@ public class OverviewRenderer // calculate where this row extends to in pixels int endRow = Math.min(Math.round((++seqIndex) * pixelsPerSeq), h); - - for (int pixelCol = 0, colIndex = 0, c = bscol - .nextSetBit(0); c >= 0; c = bscol.nextSetBit(c + 1)) + for (int pixelCol = 0, colNext = 0, pixelEnd = 0, icol = bscol + .nextSetBit(0); icol >= 0; icol = getNextCol(icol, pixelEnd)) { if (redraw) { break; } - // calculate where this column extends to in pixels - int endCol = Math.min(Math.round((++colIndex) * pixelsPerCol), w); - - // 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 + ++colNext; + pixelEnd = getNextPixel(colNext, colNext); - if (pixelCol < endCol) + if (pixelCol == pixelEnd) { - // System.out.println("OR pc ec " + pixelCol + " " + endCol); - int rgb = getColumnColourFromSequence(allGroups, seq, c); + break; + } + else if (pixelCol < pixelEnd) + { + 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 < endCol; ++col) + for (int col = pixelCol; col < pixelEnd; ++col) { // BH 2019.07.27 was: // @@ -238,16 +276,16 @@ public class OverviewRenderer ndone++; } } - // } - - pixelCol = endCol; + pixelCol = pixelEnd; // store last update value if (showProgress) { - lastUpdate = sendProgressUpdate(endCol * (endRow - 1 - pixelRow), + lastUpdate = sendProgressUpdate( + pixelEnd * (endRow - 1 - pixelRow), totalPixels, lastRowUpdate, lastUpdate); } } + } if (pixelRow < endRow) { @@ -264,6 +302,57 @@ 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). + * + * @param icol + * @param pixel + * @return + */ + private int getNextCol(int icol, int pixel) + { + return bscol.nextSetBit( + pixelsPerCol >= 1 ? icol + 1 : (int) (pixel * colsPerPixel)); + } + + private int getNextPixel(int icol, int pixel) + { + return Math.min( + pixelsPerCol >= 1 || pixel == 0 + ? Math.round(icol * pixelsPerCol) + : pixel, + w); + } + + private boolean loop() + { + if (delay <= 0) + { + return false; + } + if (timer == null) + { + timer = new Timer(delay, new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + mainLoop(); + } + + }); + timer.setRepeats(false); + timer.start(); + } + else + { + timer.restart(); + } + return true; + } + private void done() { Platform.timeCheck( @@ -271,7 +360,7 @@ public class OverviewRenderer + redraw, Platform.TIME_MARK); - overlayHiddenRegions(rows, cols); + overlayHiddenRegions(); if (showProgress) { // final update to progress bar if present @@ -296,7 +385,7 @@ public class OverviewRenderer private AlignmentColsCollectionI cols; - Iterator iter; + Iterator rowIterator; int alignmentHeight; @@ -308,53 +397,10 @@ public class OverviewRenderer int[] pixels; - BitSet bscol = new BitSet(); + BitSet bscol; int w, h; - /** - * Draw alignment rows and columns onto an image - * - * @param rit - * Iterator over rows to be drawn - * @param cit - * Iterator over columns to be drawn - * @return image containing the drawing - */ - public BufferedImage draw(AlignmentRowsCollectionI rows, - AlignmentColsCollectionI cols) - { - this.rows = rows; - this.cols = cols; - iter = rows.iterator(); - - w = miniMe.getWidth(); - h = miniMe.getHeight(); - alignmentHeight = h - graphHeight; - totalPixels = w * alignmentHeight; - lastRowUpdate = 0; - lastUpdate = 0; - - if (showProgress) - { - changeSupport.firePropertyChange(UPDATE, -1, 0); - } - - WritableRaster raster = miniMe.getRaster(); - DataBufferInt db = (DataBufferInt) raster.getDataBuffer(); - Platform.timeCheck(null, Platform.TIME_MARK); - pixels = db.getBankData()[0]; - bscol.clear(); - for (int c : cols) - { - bscol.set(c); - } - state = STATE_INIT; - mainLoop(); - - return miniMe; - } - /* * Calculate progress update value and fire event * @param rowOffset number of rows to offset calculation by @@ -387,25 +433,19 @@ public class OverviewRenderer { return (seq == null || icol >= seq.getLength() ? resColFinder.GAP_COLOUR - : resColFinder.getResidueColour(true, shader, allGroups, seq, - icol, finder)).getRGB(); + : resColFinder.getResidueColourInt(true, shader, allGroups, seq, + icol, finder)); } /** * 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) + private void overlayHiddenRegions() { if (cols.hasHidden() || rows.hasHidden()) { - BufferedImage mask = buildHiddenImage(rows, cols, miniMe.getWidth(), - miniMe.getHeight()); + BufferedImage mask = buildHiddenImage(); Graphics2D g = (Graphics2D) miniMe.getGraphics(); g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, @@ -429,17 +469,17 @@ public class OverviewRenderer * height of overview in pixels * @return BufferedImage containing mask of hidden regions */ - private BufferedImage buildHiddenImage(AlignmentRowsCollectionI rows, - AlignmentColsCollectionI cols, int width, int height) + private BufferedImage buildHiddenImage() { // new masking image - BufferedImage hiddenImage = new BufferedImage(width, height, + BufferedImage hiddenImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); Color hidden = resColFinder.getHiddenColour(); Graphics2D g2d = (Graphics2D) hiddenImage.getGraphics(); + g2d.setColor(hidden); // set background to transparent // g2d.setComposite(AlphaComposite.Clear); // g2d.fillRect(0, 0, width, height); @@ -447,31 +487,26 @@ public class OverviewRenderer // set next colour to opaque g2d.setComposite(AlphaComposite.Src); + // System.out.println(cols.getClass().getName()); if (cols.hasHidden()) { - int colIndex = 0; - int pixelCol = 0; - for (int alignmentCol : cols) + // AllColsCollection only + BitSet bs = cols.getHiddenBitSet(); + for (int pixelCol = -1, icol2 = 0, icol = bs + .nextSetBit(0); icol >= 0; icol = bs.nextSetBit(icol2)) { if (redraw) { break; } - - // calculate where this column extends to in pixels - int endCol = Math.min(Math.round((++colIndex) * pixelsPerCol), - width); - - // endCol is one more than old endCol - if (pixelCol < endCol) + icol2 = bs.nextClearBit(icol + 1); + int pixelEnd = getNextPixel(icol2, 0); + if (pixelEnd > pixelCol) { - // determine the colour based on the sequence and column position - if (cols.isHidden(alignmentCol)) - { - g2d.setColor(hidden); - g2d.fillRect(pixelCol, 0, endCol - pixelCol, height); - } - pixelCol = endCol; + pixelCol = getNextPixel(icol, 0); + g2d.fillRect(pixelCol, 0, Math.max(1, pixelEnd - pixelCol), + h); + pixelCol = pixelEnd; } } } @@ -488,13 +523,12 @@ public class OverviewRenderer // calculate where this row extends to in pixels int endRow = Math.min(Math.round((++seqIndex) * pixelsPerSeq), - height); + h); // get details of this alignment row if (rows.isHidden(alignmentRow)) { - g2d.setColor(hidden); - g2d.fillRect(0, pixelRow, width, endRow - 1 - pixelRow); + g2d.fillRect(0, pixelRow, w, endRow - 1 - pixelRow); } pixelRow = endRow; } @@ -506,27 +540,23 @@ public class OverviewRenderer /** * Draw the alignment annotation in the overview panel * - * @param g - * the graphics object to draw on * @param anno * alignment annotation information - * @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 y, - AlignmentColsCollectionI cols) + public void drawGraph(AlignmentAnnotation anno) { + int y = graphHeight; + Graphics g = miniMe.getGraphics(); + g.translate(0, alignmentHeight); + Annotation[] annotations = anno.annotations; float max = anno.graphMax; g.setColor(Color.white); - int width = miniMe.getWidth(); - g.fillRect(0, 0, width, y); + g.fillRect(0, 0, w, y); - int colIndex = 0; - int pixelCol = 0; - for (int icol : cols) + for (int pixelCol = 0, colNext = 0, pixelEnd = 0, len = annotations.length, icol = bscol + .nextSetBit(0); icol >= 0 + && icol < len; icol = getNextCol(icol, pixelEnd)) { if (redraw) { @@ -537,27 +567,29 @@ public class OverviewRenderer break; } - if (icol >= annotations.length) - { - break; // no more annotations to draw here - } - int endCol = Math.min(Math.round((++colIndex) * pixelsPerCol), width); + ++colNext; + pixelEnd = getNextPixel(colNext, colNext); Annotation ann = annotations[icol]; if (ann != null) { Color color = ann.colour; g.setColor(color == null ? Color.black : color); - int height = Math.min(y, (int) ((ann.value / max) * y)); - g.fillRect(pixelCol, y - height, endCol - pixelCol, height); + g.fillRect(pixelCol, y - height, Math.max(1, pixelEnd - pixelCol), + height); } - pixelCol = endCol; + pixelCol = pixelEnd; } + + g.translate(0, -alignmentHeight); + g.dispose(); + if (showProgress) { changeSupport.firePropertyChange(UPDATE, MAX_PROGRESS - 1, MAX_PROGRESS); } + } /**