X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Frenderer%2FOverviewRenderer.java;h=45cc944c0010a88edc1fc29590b8168337abb1c1;hb=04e88679fd837e9fe36d186e5eacdd5f13a89927;hp=c9096e290e5a417d8ee813168744477ce6e02ee5;hpb=7d52ef194d39e75fed7730041da25c48e456cf8c;p=jalview.git diff --git a/src/jalview/renderer/OverviewRenderer.java b/src/jalview/renderer/OverviewRenderer.java index c9096e2..45cc944 100644 --- a/src/jalview/renderer/OverviewRenderer.java +++ b/src/jalview/renderer/OverviewRenderer.java @@ -22,6 +22,7 @@ package jalview.renderer; import jalview.api.AlignmentColsCollectionI; import jalview.api.AlignmentRowsCollectionI; +import jalview.api.AlignmentViewPanel; import jalview.api.RendererListenerI; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; @@ -30,14 +31,23 @@ import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; import jalview.renderer.seqfeatures.FeatureColourFinder; import jalview.renderer.seqfeatures.FeatureRenderer; +import jalview.util.Platform; import jalview.viewmodel.OverviewDimensions; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.awt.image.BufferedImage; +import java.awt.image.DataBufferInt; +import java.awt.image.WritableRaster; import java.beans.PropertyChangeSupport; +import java.util.BitSet; +import java.util.Iterator; + +import javax.swing.Timer; public class OverviewRenderer { @@ -75,10 +85,25 @@ public class OverviewRenderer private OverviewResColourFinder resColFinder; - public OverviewRenderer(FeatureRenderer fr, OverviewDimensions od, + private boolean showProgress; + + private AlignmentViewPanel panel; + + public OverviewRenderer(AlignmentViewPanel panel, FeatureRenderer fr, + OverviewDimensions od, AlignmentI alignment, ResidueShaderI resshader, OverviewResColourFinder colFinder) { + this(panel, fr, od, alignment, resshader, colFinder, true); + } + + public OverviewRenderer(AlignmentViewPanel panel, + jalview.api.FeatureRenderer fr, + OverviewDimensions od, + AlignmentI alignment, ResidueShaderI resshader, + OverviewResColourFinder colFinder, boolean showProgress) + { + this.panel = panel; finder = new FeatureColourFinder(fr); resColFinder = colFinder; @@ -90,108 +115,243 @@ public class OverviewRenderer graphHeight = od.getGraphHeight(); miniMe = new BufferedImage(od.getWidth(), od.getHeight(), BufferedImage.TYPE_INT_RGB); + this.showProgress = showProgress; } - /** - * 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) + 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; + + void mainLoop() + { + while (!redraw) + { + switch (state) + { + case STATE_INIT: + seqIndex = 0; + pixelRow = 0; + state = STATE_NEXT; + continue; + case STATE_NEXT: + if (iter.hasNext()) + { + nextRow(); + } + else + { + state = STATE_DONE; + } + break; + case STATE_DONE: + done(); + return; + } + if (delay > 0) + { + jsloop(); + return; + } + } + done(); + } + + private void jsloop() + { + if (timer == null) + { + timer = new Timer(delay, new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + mainLoop(); + } + + }); + timer.setRepeats(false); + timer.start(); + } + else + { + timer.restart(); + } + } + + private void nextRow() { - int rgbcolor = Color.white.getRGB(); - int seqIndex = 0; - int pixelRow = 0; - int alignmentHeight = miniMe.getHeight() - graphHeight; - int totalPixels = miniMe.getWidth() * alignmentHeight; + row = iter.next(); + // System.out.println("OR row " + r); + // get details of this alignment row + SequenceI seq = rows.getSequence(row); - int lastRowUpdate = 0; - int lastUpdate = 0; - changeSupport.firePropertyChange(UPDATE, -1, 0); + // rate limiting step when rendering overview for lots of groups + SequenceGroup[] allGroups = al.findAllGroups(seq); - for (int alignmentRow : rows) + // 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)) { if (redraw) { 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 column extends to in pixels + int endCol = Math.min(Math.round((++colIndex) * pixelsPerCol), w); - // 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) + // 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) { - if (redraw) - { - break; - } - - // calculate where this column extends to in pixels - 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) + // System.out.println("OR pc ec " + pixelCol + " " + endCol); + int rgb = getColumnColourFromSequence(allGroups, seq, c); + // fill in the appropriate number of pixels + for (int row = pixelRow; row < endRow; ++row) { - rgbcolor = getColumnColourFromSequence(allGroups, seq, - alignmentCol); - - // fill in the appropriate number of pixels - for (int row = pixelRow; row <= endRow; ++row) + for (int col = pixelCol; col < endCol; ++col) { - for (int col = pixelCol; col <= endCol; ++col) - { - miniMe.setRGB(col, row, rgbcolor); - } + // BH 2019.07.27 was: + // + // miniMe.setRGB(col, row, rgbcolor); + // + // but just directly writing to the int[] pixel buffer + // is three times faster by my experimentation + pixels[row * w + col] = rgb; + ndone++; } + } + // } - // store last update value - lastUpdate = sendProgressUpdate( - (pixelCol + 1) * (endRow - pixelRow), totalPixels, - lastRowUpdate, lastUpdate); - - pixelCol = endCol + 1; + pixelCol = endCol; + // store last update value + if (showProgress) + { + lastUpdate = sendProgressUpdate(endCol * (endRow - 1 - pixelRow), + totalPixels, lastRowUpdate, lastUpdate); } - colIndex++; } - - if (pixelRow != endRow + 1) + } + if (pixelRow < endRow) + { + pixelRow = endRow; + // store row offset and last update value + if (showProgress) { - // store row offset and last update value - lastRowUpdate = sendProgressUpdate(endRow + 1, alignmentHeight, 0, + // BH 2019.07.29 was (old) endRow + 1 (now endRow), but should be + // pixelRow + 1, surely + lastRowUpdate = sendProgressUpdate(endRow, alignmentHeight, 0, lastUpdate); lastUpdate = lastRowUpdate; - pixelRow = endRow + 1; } - seqIndex++; } + } + + private void done() + { + Platform.timeCheck( + "overviewrender " + ndone + " pixels row:" + row + " redraw:" + + redraw, + Platform.TIME_MARK); overlayHiddenRegions(rows, cols); - // final update to progress bar if present - if (redraw) + if (showProgress) { - sendProgressUpdate(pixelRow - 1, alignmentHeight, 0, 0); + // final update to progress bar if present + if (redraw) + { + // aborted in Java + // BH was pixelRow - 1, but that could go negative + sendProgressUpdate(pixelRow, alignmentHeight, 0, 0); + } + else + { + // sendProgressUpdate(alignmentHeight, miniMe.getHeight(), 0, 0); + sendProgressUpdate(1, 1, 0, 0); + } } - else + panel.overviewDone(miniMe); + } + + int ndone = 0; + + private AlignmentRowsCollectionI rows; + + private AlignmentColsCollectionI cols; + + Iterator iter; + + int alignmentHeight; + + int totalPixels; + + int lastRowUpdate; + + int lastUpdate; + + int[] pixels; + + BitSet bscol = new BitSet(); + + 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) { - sendProgressUpdate(alignmentHeight, miniMe.getHeight(), 0, 0); + bscol.set(c); } + state = STATE_INIT; + mainLoop(); + return miniMe; } @@ -222,18 +382,13 @@ public class OverviewRenderer * column position to get colour for * @return colour of sequence at this position, as RGB */ - int getColumnColourFromSequence(SequenceGroup[] allGroups, - SequenceI seq, int lastcol) + int getColumnColourFromSequence(SequenceGroup[] allGroups, SequenceI seq, + int icol) { - Color color = resColFinder.GAP_COLOUR; - - if ((seq != null) && (seq.getLength() > lastcol)) - { - color = resColFinder.getResidueColour(true, shader, allGroups, seq, - lastcol, finder); - } - - return color.getRGB(); + return (seq == null || icol >= seq.getLength() + ? resColFinder.GAP_COLOUR + : resColFinder.getResidueColour(true, shader, allGroups, seq, + icol, finder)).getRGB(); } /** @@ -256,6 +411,7 @@ public class OverviewRenderer g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, TRANSPARENCY)); g.drawImage(mask, 0, 0, miniMe.getWidth(), miniMe.getHeight(), null); + g.dispose(); } } @@ -280,9 +436,6 @@ public class OverviewRenderer BufferedImage hiddenImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); - int colIndex = 0; - int pixelCol = 0; - Color hidden = resColFinder.getHiddenColour(); Graphics2D g2d = (Graphics2D) hiddenImage.getGraphics(); @@ -294,55 +447,59 @@ public class OverviewRenderer // set next colour to opaque g2d.setComposite(AlphaComposite.Src); - for (int alignmentCol : cols) + if (cols.hasHidden()) { - if (redraw) + int colIndex = 0; + int pixelCol = 0; + for (int alignmentCol : cols) { - break; - } + if (redraw) + { + break; + } - // calculate where this column extends to in pixels - int endCol = Math.min(Math.round((colIndex + 1) * pixelsPerCol) - 1, - hiddenImage.getWidth() - 1); + // calculate where this column extends to in pixels + int endCol = Math.min(Math.round((++colIndex) * pixelsPerCol), + width); - if (pixelCol <= endCol) - { - // determine the colour based on the sequence and column position - if (cols.isHidden(alignmentCol)) + // endCol is one more than old endCol + if (pixelCol < endCol) { - g2d.setColor(hidden); - g2d.fillRect(pixelCol, 0, endCol - pixelCol + 1, height); + // 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 = endCol + 1; } - colIndex++; - } - - int seqIndex = 0; - int pixelRow = 0; - for (int alignmentRow : rows) + if (rows.hasHidden()) { - if (redraw) + int seqIndex = 0; + int pixelRow = 0; + for (int alignmentRow : rows) { - break; - } + if (redraw) + { + break; + } - // calculate where this row extends to in pixels - int endRow = Math.min(Math.round((seqIndex + 1) * pixelsPerSeq) - 1, - miniMe.getHeight() - 1); + // calculate where this row extends to in pixels + int endRow = Math.min(Math.round((++seqIndex) * pixelsPerSeq), + height); - // get details of this alignment row - if (rows.isHidden(alignmentRow)) - { - g2d.setColor(hidden); - g2d.fillRect(0, pixelRow, width, endRow - pixelRow + 1); + // get details of this alignment row + if (rows.isHidden(alignmentRow)) + { + g2d.setColor(hidden); + g2d.fillRect(0, pixelRow, width, endRow - 1 - pixelRow); + } + pixelRow = endRow; } - pixelRow = endRow + 1; - seqIndex++; } - + g2d.dispose(); return hiddenImage; } @@ -362,56 +519,45 @@ public class OverviewRenderer AlignmentColsCollectionI cols) { Annotation[] annotations = anno.annotations; + float max = anno.graphMax; g.setColor(Color.white); - g.fillRect(0, 0, miniMe.getWidth(), y); + int width = miniMe.getWidth(); + g.fillRect(0, 0, width, y); - int height; int colIndex = 0; int pixelCol = 0; - for (int alignmentCol : cols) + for (int icol : cols) { if (redraw) { - changeSupport.firePropertyChange(UPDATE, MAX_PROGRESS - 1, 0); + if (showProgress) + { + changeSupport.firePropertyChange(UPDATE, MAX_PROGRESS - 1, 0); + } break; } - if (alignmentCol >= annotations.length) + if (icol >= annotations.length) { break; // no more annotations to draw here } - else + int endCol = Math.min(Math.round((++colIndex) * pixelsPerCol), width); + Annotation ann = annotations[icol]; + if (ann != null) { - int endCol = Math.min(Math.round((colIndex + 1) * pixelsPerCol) - 1, - miniMe.getWidth() - 1); - - if (annotations[alignmentCol] != null) - { - if (annotations[alignmentCol].colour == null) - { - g.setColor(Color.black); - } - else - { - g.setColor(annotations[alignmentCol].colour); - } - - height = (int) ((annotations[alignmentCol].value / anno.graphMax) - * y); - if (height > y) - { - height = y; - } - - g.fillRect(pixelCol, y - height, endCol - pixelCol + 1, height); - } + Color color = ann.colour; + g.setColor(color == null ? Color.black : color); - pixelCol = endCol + 1; - colIndex++; + int height = Math.min(y, (int) ((ann.value / max) * y)); + g.fillRect(pixelCol, y - height, endCol - pixelCol, height); } + pixelCol = endCol; + } + if (showProgress) + { + changeSupport.firePropertyChange(UPDATE, MAX_PROGRESS - 1, + MAX_PROGRESS); } - changeSupport.firePropertyChange(UPDATE, MAX_PROGRESS - 1, - MAX_PROGRESS); } /**