import jalview.api.AlignmentColsCollectionI;
import jalview.api.AlignmentRowsCollectionI;
+import jalview.api.AlignmentViewPanel;
import jalview.api.RendererListenerI;
import jalview.datamodel.AlignmentAnnotation;
import jalview.datamodel.AlignmentI;
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
{
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;
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<Integer> 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;
}
* 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();
}
/**
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
TRANSPARENCY));
g.drawImage(mask, 0, 0, miniMe.getWidth(), miniMe.getHeight(), null);
+ g.dispose();
}
}
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();
// 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;
}
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);
}
/**