import jalview.datamodel.SequenceI;
import jalview.renderer.seqfeatures.FeatureColourFinder;
import jalview.renderer.seqfeatures.FeatureRenderer;
-import jalview.util.Comparison;
import jalview.viewmodel.OverviewDimensions;
+import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics;
+import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
public class OverviewRenderer
{
- private FeatureColourFinder finder;
+ // transparency of hidden cols/seqs overlay
+ private final float TRANSPARENCY = 0.5f;
+
+ private final Color HIDDEN_COLOUR = Color.DARK_GRAY.darker();
- private jalview.api.SequenceRenderer sr;
+ private FeatureColourFinder finder;
// image to render on
private BufferedImage miniMe;
private ResidueShaderI shader;
- public OverviewRenderer(jalview.api.SequenceRenderer seqRenderer,
- FeatureRenderer fr, OverviewDimensions od, AlignmentI alignment,
- ResidueShaderI resshader)
+ 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;
{
break;
}
-
+
// get details of this alignment row
- boolean hidden = rows.isHidden(alignmentRow);
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);
-
+
int colIndex = 0;
int pixelCol = 0;
for (int alignmentCol : cols)
{
break;
}
-
+
// 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
// NB this is important to avoid performance issues in the overview
// panel
if (pixelCol <= endCol)
{
- // determine the colour based on the sequence and column position
rgbcolor = getColumnColourFromSequence(allGroups, seq,
- hidden || cols.isHidden(alignmentCol), alignmentCol,
- finder);
-
+ alignmentCol, finder);
+
// fill in the appropriate number of pixels
for (int row = pixelRow; row <= endRow; ++row)
{
miniMe.setRGB(col, row, rgbcolor);
}
}
-
+
pixelCol = endCol + 1;
}
colIndex++;
pixelRow = endRow + 1;
seqIndex++;
}
+
+ overlayHiddenRegions(rows, cols);
return miniMe;
}
- /*
+ /**
* Find 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
+ * @param fcfinder
+ * FeatureColourFinder to use
+ * @return colour of sequence at this position, as RGB
*/
private int getColumnColourFromSequence(SequenceGroup[] allGroups,
jalview.datamodel.SequenceI seq,
- boolean isHidden, int lastcol, FeatureColourFinder fcfinder)
+ int lastcol, FeatureColourFinder fcfinder)
{
Color color = Color.white;
if ((seq != null) && (seq.getLength() > lastcol))
{
- color = getResidueColour(allGroups, seq, lastcol, fcfinder);
- }
-
- if (isHidden)
- {
- color = color.darker().darker();
+ color = resColFinder.getResidueColour(true, shader, allGroups, seq,
+ lastcol,
+ fcfinder);
}
return color.getRGB();
}
- private Color getResidueColour(SequenceGroup[] allGroups,
- final SequenceI seq, int position,
- FeatureColourFinder 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)
{
- Color col = getResidueBoxColour(allGroups, seq, position);
-
- if (finder != null)
+ if (cols.hasHidden() || rows.hasHidden())
{
- col = finder.findFeatureColour(col, seq, position);
+ 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);
}
- return col;
}
- protected Color getResidueBoxColour(SequenceGroup[] allGroups,
- SequenceI seq, int i)
+ /**
+ * 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)
{
+ // new masking image
+ BufferedImage hiddenImage = new BufferedImage(width, height,
+ BufferedImage.TYPE_INT_ARGB);
- ResidueShaderI currentShader;
+ int colIndex = 0;
+ int pixelCol = 0;
- SequenceGroup currentSequenceGroup = inCurrentSequenceGroup(allGroups,
- i);
- if (currentSequenceGroup != null)
- {
- currentShader = currentSequenceGroup.getGroupColourScheme();
- }
- else
- {
- currentShader = shader;
- }
+ Color hidden = resColFinder.getHiddenColour();
- return getBoxColour(currentShader, seq, i);
- }
+ Graphics2D g2d = (Graphics2D) hiddenImage.getGraphics();
- SequenceGroup inCurrentSequenceGroup(SequenceGroup[] allGroups, int res)
- {
- if (allGroups == null)
- {
- return null;
- }
+ // set background to transparent
+ g2d.setComposite(AlphaComposite.Clear);
+ g2d.fillRect(0, 0, width, height);
+
+ // set next colour to opaque
+ g2d.setComposite(AlphaComposite.Src);
- for (int i = 0; i < allGroups.length; i++)
+ for (int alignmentCol : cols)
{
- if ((allGroups[i].getStartRes() <= res)
- && (allGroups[i].getEndRes() >= res))
+ if (redraw)
{
- return (allGroups[i]);
+ break;
}
- }
- return null;
- }
+ // calculate where this column extends to in pixels
+ int endCol = Math.min(Math.round((colIndex + 1) * pixelsPerCol) - 1,
+ hiddenImage.getWidth() - 1);
- Color getBoxColour(ResidueShaderI shader, SequenceI seq, int i)
- {
- Color resBoxColour = Color.white;
- char currentChar = seq.getCharAt(i);
+ if (pixelCol <= endCol)
+ {
+ // determine the colour based on the sequence and column position
+ if (cols.isHidden(alignmentCol))
+ {
+ g2d.setColor(hidden);
+ g2d.fillRect(pixelCol, 0, endCol - pixelCol + 1, height);
+ }
- if (shader.getColourScheme() != null)
+ pixelCol = endCol + 1;
+ }
+ colIndex++;
+
+ }
+
+ int seqIndex = 0;
+ int pixelRow = 0;
+ for (int alignmentRow : rows)
{
- if (Comparison.isGap(currentChar)
- && !shader.getColourScheme().hasGapColour())
+ if (redraw)
{
- resBoxColour = Color.lightGray;
+ break;
}
- else
+
+ // calculate where this row extends to in pixels
+ int endRow = Math.min(Math.round((seqIndex + 1) * pixelsPerSeq) - 1,
+ miniMe.getHeight() - 1);
+
+ // get details of this alignment row
+ if (rows.isHidden(alignmentRow))
{
- resBoxColour = shader.findColour(currentChar, i, seq);
+ g2d.setColor(hidden);
+ g2d.fillRect(0, pixelRow, width, endRow - pixelRow + 1);
}
- }
- else if (Comparison.isGap(currentChar))
- {
- resBoxColour = Color.lightGray;
+ pixelRow = endRow + 1;
+ seqIndex++;
}
- return resBoxColour;
+ return hiddenImage;
}
/**
}
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;
}
}
+ /**
+ * 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)