import jalview.api.AlignmentColsCollectionI;
import jalview.api.AlignmentRowsCollectionI;
+import jalview.api.RendererListenerI;
import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.AlignmentI;
import jalview.datamodel.Annotation;
+import jalview.datamodel.SequenceGroup;
import jalview.datamodel.SequenceI;
import jalview.renderer.seqfeatures.FeatureColourFinder;
import jalview.renderer.seqfeatures.FeatureRenderer;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
+import java.beans.PropertyChangeSupport;
public class OverviewRenderer
{
// transparency of hidden cols/seqs overlay
private final float TRANSPARENCY = 0.5f;
- private FeatureColourFinder finder;
+ public static final String UPDATE = "OverviewUpdate";
+
+ private static final int MAX_PROGRESS = 100;
- private jalview.api.SequenceRenderer sr;
+ private PropertyChangeSupport changeSupport = new PropertyChangeSupport(
+ this);
+
+ private FeatureColourFinder finder;
// image to render on
private BufferedImage miniMe;
// raw number of pixels to allocate to each row
private float pixelsPerSeq;
+ // height in pixels of graph
+ private int graphHeight;
+
// flag to indicate whether to halt drawing
private volatile boolean redraw = false;
- public OverviewRenderer(jalview.api.SequenceRenderer seqRenderer,
- FeatureRenderer fr, OverviewDimensions od)
+ // reference to alignment, needed to get sequence groups
+ private AlignmentI al;
+
+ private ResidueShaderI shader;
+
+ private OverviewResColourFinder resColFinder;
+
+ public OverviewRenderer(FeatureRenderer fr, OverviewDimensions od,
+ AlignmentI alignment,
+ ResidueShaderI resshader, OverviewResColourFinder colFinder)
{
- sr = seqRenderer;
finder = new FeatureColourFinder(fr);
+ resColFinder = colFinder;
+
+ al = alignment;
+ shader = resshader;
pixelsPerCol = od.getPixelsPerCol();
pixelsPerSeq = od.getPixelsPerSeq();
+ graphHeight = od.getGraphHeight();
miniMe = new BufferedImage(od.getWidth(), od.getHeight(),
BufferedImage.TYPE_INT_RGB);
}
int rgbcolor = Color.white.getRGB();
int seqIndex = 0;
int pixelRow = 0;
+ int alignmentHeight = miniMe.getHeight() - graphHeight;
+ int totalPixels = miniMe.getWidth() * alignmentHeight;
- BufferedImage hiddenImage = buildHiddenImage(rows, cols,
- miniMe.getWidth(), miniMe.getHeight());
+ int lastRowUpdate = 0;
+ int lastUpdate = 0;
+ changeSupport.firePropertyChange(UPDATE, -1, 0);
for (int alignmentRow : rows)
{
// 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 row extends to in pixels
int endRow = Math.min(Math.round((seqIndex + 1) * pixelsPerSeq) - 1,
miniMe.getHeight() - 1);
}
// calculate where this column extends to in pixels
- int endCol = Math.min(
- Math.round((colIndex + 1) * pixelsPerCol) - 1,
+ 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
// panel
if (pixelCol <= endCol)
{
- rgbcolor = getColumnColourFromSequence(seq,
- alignmentCol,
- finder);
+ rgbcolor = getColumnColourFromSequence(allGroups, seq,
+ alignmentCol);
// fill in the appropriate number of pixels
for (int row = pixelRow; row <= endRow; ++row)
miniMe.setRGB(col, row, rgbcolor);
}
}
-
+
+ // store last update value
+ lastUpdate = sendProgressUpdate(
+ (pixelCol + 1) * (endRow - pixelRow), totalPixels,
+ lastRowUpdate, lastUpdate);
+
pixelCol = endCol + 1;
}
colIndex++;
}
- pixelRow = endRow + 1;
+
+ if (pixelRow != endRow + 1)
+ {
+ // store row offset and last update value
+ lastRowUpdate = sendProgressUpdate(endRow + 1, alignmentHeight, 0,
+ lastUpdate);
+ lastUpdate = lastRowUpdate;
+ pixelRow = endRow + 1;
+ }
seqIndex++;
}
- return applyMask(hiddenImage, miniMe);
+ overlayHiddenRegions(rows, cols);
+ // final update to progress bar if present
+ if (redraw)
+ {
+ sendProgressUpdate(pixelRow - 1, alignmentHeight, 0, 0);
+ }
+ else
+ {
+ sendProgressUpdate(alignmentHeight, miniMe.getHeight(), 0, 0);
+ }
+ return miniMe;
}
/*
- * Find the colour of a sequence at a specified column position
+ * Calculate progress update value and fire event
+ * @param rowOffset number of rows to offset calculation by
+ * @return new rowOffset - return value only to be used when at end of a row
*/
- private int getColumnColourFromSequence(jalview.datamodel.SequenceI seq,
- int lastcol, FeatureColourFinder fcfinder)
+ private int sendProgressUpdate(int position, int maximum, int rowOffset,
+ int lastUpdate)
{
- Color color = Color.white;
+ int newUpdate = rowOffset
+ + Math.round(MAX_PROGRESS * ((float) position / maximum));
+ if (newUpdate > lastUpdate)
+ {
+ changeSupport.firePropertyChange(UPDATE, rowOffset, newUpdate);
+ return newUpdate;
+ }
+ return newUpdate;
+ }
+
+ /*
+ * Find the RGB value of the colour of a sequence at a specified column position
+ *
+ * @param seq
+ * sequence to get colour for
+ * @param lastcol
+ * column position to get colour for
+ * @return colour of sequence at this position, as RGB
+ */
+ int getColumnColourFromSequence(SequenceGroup[] allGroups,
+ SequenceI seq, int lastcol)
+ {
+ Color color = resColFinder.GAP_COLOUR;
if ((seq != null) && (seq.getLength() > lastcol))
{
- color = sr.getResidueColour(seq, lastcol, fcfinder);
+ color = resColFinder.getResidueColour(true, shader, allGroups, seq,
+ lastcol, finder);
}
return color.getRGB();
}
- /*
- * Overlay a buffered image (mask) onto another image (image) where mask
- * has some transparency so image shows through from below
+ /**
+ * 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 BufferedImage applyMask(BufferedImage mask, BufferedImage image)
+ private void overlayHiddenRegions(AlignmentRowsCollectionI rows,
+ AlignmentColsCollectionI cols)
{
- Graphics2D g = (Graphics2D) image.getGraphics();
- g.setComposite(
- AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
- TRANSPARENCY));
- g.drawImage(mask, 0, 0, image.getWidth(), image.getHeight(), null);
- return image;
+ if (cols.hasHidden() || rows.hasHidden())
+ {
+ BufferedImage mask = buildHiddenImage(rows, cols, miniMe.getWidth(),
+ miniMe.getHeight());
+
+ Graphics2D g = (Graphics2D) miniMe.getGraphics();
+ g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
+ TRANSPARENCY));
+ g.drawImage(mask, 0, 0, miniMe.getWidth(), miniMe.getHeight(), null);
+ }
}
- /*
- * Build a masking image of hidden columns and rows to be applied on top
- * of the main overview image.
+ /**
+ * Build a masking image of hidden columns and rows to be applied on top of
+ * the main overview image.
+ *
+ * @param rows
+ * collection of rows the overview is built over
+ * @param cols
+ * collection of columns the overview is built over
+ * @param width
+ * width of overview in pixels
+ * @param height
+ * height of overview in pixels
+ * @return BufferedImage containing mask of hidden regions
*/
private BufferedImage buildHiddenImage(AlignmentRowsCollectionI rows,
AlignmentColsCollectionI cols, int width, int height)
int colIndex = 0;
int pixelCol = 0;
- Color hidden = Color.DARK_GRAY.darker();
+ Color hidden = resColFinder.getHiddenColour();
Graphics2D g2d = (Graphics2D) hiddenImage.getGraphics();
// set background to transparent
- g2d.setComposite(AlphaComposite.Clear);
- g2d.fillRect(0, 0, width, height);
+ // g2d.setComposite(AlphaComposite.Clear);
+ // g2d.fillRect(0, 0, width, height);
// set next colour to opaque
g2d.setComposite(AlphaComposite.Src);
* the graphics object to draw on
* @param anno
* alignment annotation information
- * @param charWidth
- * alignment character width value
* @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 charWidth,
- int y, AlignmentColsCollectionI cols)
+ public void drawGraph(Graphics g, AlignmentAnnotation anno, int y,
+ AlignmentColsCollectionI cols)
{
Annotation[] annotations = anno.annotations;
g.setColor(Color.white);
{
if (redraw)
{
+ changeSupport.firePropertyChange(UPDATE, MAX_PROGRESS - 1, 0);
break;
}
+
if (alignmentCol >= annotations.length)
{
break; // no more annotations to draw here
}
else
{
- int endCol = Math.min(
- Math.round((colIndex + 1) * pixelsPerCol) - 1,
+ int endCol = Math.min(Math.round((colIndex + 1) * pixelsPerCol) - 1,
miniMe.getWidth() - 1);
if (annotations[alignmentCol] != null)
g.setColor(annotations[alignmentCol].colour);
}
- height = (int) ((annotations[alignmentCol].value / anno.graphMax) * y);
+ height = (int) ((annotations[alignmentCol].value / anno.graphMax)
+ * y);
if (height > y)
{
height = y;
g.fillRect(pixelCol, y - height, endCol - pixelCol + 1, height);
}
+
pixelCol = endCol + 1;
colIndex++;
}
}
+ changeSupport.firePropertyChange(UPDATE, MAX_PROGRESS - 1,
+ MAX_PROGRESS);
}
+ /**
+ * Allows redraw flag to be set
+ *
+ * @param b
+ * value to set redraw to: true = redraw is occurring, false = no
+ * redraw
+ */
public void setRedraw(boolean b)
{
synchronized (this)
redraw = b;
}
}
+
+ public void addPropertyChangeListener(RendererListenerI listener)
+ {
+ changeSupport.addPropertyChangeListener(listener);
+ }
+
+ public void removePropertyChangeListener(RendererListenerI listener)
+ {
+ changeSupport.removePropertyChangeListener(listener);
+ }
}