package jalview.gui; import jalview.api.AlignViewportI; import jalview.datamodel.SequenceI; import jalview.renderer.AnnotationRenderer; import jalview.viewmodel.OverviewDimensions; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.image.BufferedImage; import javax.swing.JComponent; public class OverviewCanvas extends JComponent { private static final Color TRANS_GREY = new Color(100, 100, 100, 25); private BufferedImage miniMe; private BufferedImage lastMiniMe = null; public boolean resizing = false; // This is set true if the user resizes whilst // the overview is being calculated public boolean resizeAgain = false; // Can set different properties in this seqCanvas than // main visible SeqCanvas private SequenceRenderer sr; private jalview.renderer.seqfeatures.FeatureRenderer fr; private final AnnotationRenderer renderer = new AnnotationRenderer(); OverviewDimensions od; OverviewPanel op; AlignViewport av; AlignmentPanel ap; public OverviewCanvas(OverviewDimensions overviewDims, AlignViewportI alignvp, AlignmentPanel alignp, OverviewPanel overp) { od = overviewDims; av = alignp.av; ap = alignp; op = overp; sr = new SequenceRenderer(av); sr.renderGaps = false; sr.forOverview = true; fr = new FeatureRenderer(ap); } public void draw(boolean showSequenceFeatures, boolean showAnnotation) { miniMe = null; if (showSequenceFeatures) { fr.transferSettings(ap.getSeqPanel().seqCanvas.getFeatureRenderer()); } // why do we need to set preferred size again? was set in // updateOverviewImage setPreferredSize(new Dimension(od.getWidth(), od.getHeight())); miniMe = new BufferedImage(od.getWidth(), od.getHeight(), BufferedImage.TYPE_INT_RGB); Graphics mg = miniMe.getGraphics(); mg.setColor(Color.orange); mg.fillRect(0, 0, od.getWidth(), miniMe.getHeight()); // calculate sampleCol and sampleRow // alignment width is max number of residues/bases // alignment height is number of sequences int alwidth = av.getAlignment().getWidth(); int alheight = av.getAlignment().getAbsoluteHeight(); // sampleCol or sampleRow is the width/height allocated to each residue // in particular, sometimes we may need more than one row/col of the // BufferedImage allocated // sampleCol is how much of a residue to assign to each pixel // sampleRow is how many sequences to assign to each pixel float sampleCol = alwidth / (float) od.getWidth(); float sampleRow = alheight / (float) od.getSequencesHeight(); buildImage(sampleRow, sampleCol); if (showAnnotation) { renderer.updateFromAlignViewport(av); for (int col = 0; col < od.getWidth() && !resizeAgain; col++) { mg.translate(col, od.getSequencesHeight()); renderer.drawGraph(mg, av.getAlignmentConservationAnnotation(), av.getAlignmentConservationAnnotation().annotations, (int) (sampleCol) + 1, od.getGraphHeight(), (int) (col * sampleCol), (int) (col * sampleCol) + 1); mg.translate(-col, -od.getSequencesHeight()); } } System.gc(); resizing = false; if (resizeAgain) { resizeAgain = false; op.updateOverviewImage(); } else { lastMiniMe = miniMe; } } @Override public void paintComponent(Graphics g) { if (resizing || resizeAgain) { if (lastMiniMe == null) { g.setColor(Color.white); g.fillRect(0, 0, getWidth(), getHeight()); } else { g.drawImage(lastMiniMe, 0, 0, getWidth(), getHeight(), this); } g.setColor(TRANS_GREY); g.fillRect(0, 0, getWidth(), getHeight()); } else if (lastMiniMe != null) { g.drawImage(lastMiniMe, 0, 0, this); if (lastMiniMe != miniMe) { g.setColor(TRANS_GREY); g.fillRect(0, 0, getWidth(), getHeight()); } } g.setColor(Color.red); od.drawBox(g); } /* * Build the overview panel image */ private void buildImage(float sampleRow, float sampleCol) { int lastcol = -1; int lastrow = -1; int color = Color.white.getRGB(); SequenceI seq = null; final boolean hasHiddenCols = av.hasHiddenColumns(); boolean hiddenRow = false; // get hidden row and hidden column map once at beginning. // clone featureRenderer settings to avoid race conditions... if state is // updated just need to refresh again for (int row = 0; row < od.getSequencesHeight() && !resizeAgain; row++) { boolean doCopy = true; int currentrow = (int) (row * sampleRow); if (currentrow != lastrow) { doCopy = false; lastrow = currentrow; // get the sequence which would be at alignment index 'lastrow' if no // rows were hidden, and determine whether it is hidden or not hiddenRow = av.getAlignment().isHidden(lastrow); seq = av.getAlignment().getSequenceAtAbsoluteIndex(lastrow); } for (int col = 0; col < od.getWidth() && !resizeAgain; col++) { if (doCopy) { color = miniMe.getRGB(col, row - 1); } else if ((int) (col * sampleCol) != lastcol || (int) (row * sampleRow) != lastrow) { lastcol = (int) (col * sampleCol); color = getColumnColourFromSequence(seq, hiddenRow, hasHiddenCols, lastcol); } // else we just use the color we already have , so don't need to set it miniMe.setRGB(col, row, color); } } } /* * Find the colour of a sequence at a specified column position */ private int getColumnColourFromSequence(jalview.datamodel.SequenceI seq, boolean hiddenRow, boolean hasHiddenCols, int lastcol) { int color; if (seq == null) { color = Color.white.getRGB(); } else if (seq.getLength() > lastcol) { color = sr.getResidueBoxColour(seq, lastcol).getRGB(); if (av.isShowSequenceFeatures()) { color = fr.findFeatureColour(color, seq, lastcol); } } else { color = Color.white.getRGB(); } if (hiddenRow || (hasHiddenCols && !av.getColumnSelection() .isVisible(lastcol))) { color = new Color(color).darker().darker().getRGB(); } return color; } }