/* * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) * Copyright (C) $$Year-Rel$$ The Jalview Authors * * This file is part of Jalview. * * Jalview is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * Jalview is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Jalview. If not, see . * The Jalview Authors are detailed in the 'AUTHORS' file. */ package jalview.gui; import jalview.api.AlignViewportI; import jalview.renderer.OverviewRenderer; 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 javax.swing.JComponent; import javax.swing.Timer; public class OverviewCanvas extends JComponent { private static final long RUNNING_TIME = 2000; private static final int SPEED = 40; private static final Color TRANS_GREY = new Color(100, 100, 100, 25); // This is set true if the alignment view changes whilst // the overview is being calculated private volatile boolean restart = false; private volatile boolean updaterunning = false; private BufferedImage miniMe; private BufferedImage lastMiniMe = null; private BufferedImage veryLastMiniMe = null; // Can set different properties in this seqCanvas than // main visible SeqCanvas private SequenceRenderer sr; private jalview.renderer.seqfeatures.FeatureRenderer fr; private OverviewDimensions od; private OverviewRenderer or = null; private AlignViewportI av; private float alpha = 0f; private long startTime = -1; private final Timer timer; private ProgressPanel progressPanel; public OverviewCanvas(OverviewDimensions overviewDims, AlignViewportI alignvp, ProgressPanel pp) { od = overviewDims; av = alignvp; progressPanel = pp; sr = new SequenceRenderer(av); sr.renderGaps = false; sr.forOverview = true; fr = new jalview.renderer.seqfeatures.FeatureRenderer(av); setSize(od.getWidth(), od.getHeight()); timer = new Timer(SPEED, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (startTime < 0) { startTime = System.currentTimeMillis(); } else { long time = System.currentTimeMillis(); long duration = time - startTime; if (duration >= RUNNING_TIME) { startTime = -1; ((Timer) e.getSource()).stop(); alpha = 0f; } else { alpha = 1f - ((float) duration / (float) RUNNING_TIME); } repaint(); } } }); } /** * Update the overview dimensions object used by the canvas (e.g. if we change * from showing hidden columns to hiding them or vice versa) * * @param overviewDims */ public void resetOviewDims(OverviewDimensions overviewDims) { od = overviewDims; } /** * Signals to drawing code that the associated alignment viewport has changed * and a redraw will be required */ public boolean restartDraw() { synchronized (this) { if (updaterunning) { restart = true; if (or != null) { or.setRedraw(true); } } else { updaterunning = true; } return restart; } } /** * Draw the overview sequences * * @param showSequenceFeatures * true if sequence features are to be shown * @param showAnnotation * true if the annotation is to be shown * @param transferRenderer * the renderer to transfer feature colouring from */ public void draw(boolean showSequenceFeatures, boolean showAnnotation, FeatureRenderer transferRenderer) { miniMe = null; veryLastMiniMe = lastMiniMe; if (showSequenceFeatures) { fr.transferSettings(transferRenderer); } or = new OverviewRenderer(sr, fr, od); or.addPropertyChangeListener(progressPanel); miniMe = or.draw(od.getRows(av.getAlignment()), od.getColumns(av.getAlignment())); Graphics mg = miniMe.getGraphics(); if (showAnnotation) { mg.translate(0, od.getSequencesHeight()); or.drawGraph(mg, av.getAlignmentConservationAnnotation(), av.getCharWidth(), od.getGraphHeight(), od.getColumns(av.getAlignment())); mg.translate(0, -od.getSequencesHeight()); } System.gc(); or.removePropertyChangeListener(progressPanel); if (restart) { restart = false; draw(showSequenceFeatures, showAnnotation, transferRenderer); } else { updaterunning = false; lastMiniMe = miniMe; alpha = 1f; timer.start(); } } @Override public void paintComponent(Graphics g) { if (restart) { 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) { // is this a resize? if ((getWidth() > 0) && (getHeight() > 0) && ((getWidth() != od.getWidth()) || (getHeight() != od.getHeight()))) { // if there is annotation, scale the alignment and annotation separately if (od.getGraphHeight() > 0) { BufferedImage topImage = lastMiniMe.getSubimage(0, 0, od.getWidth(), od.getSequencesHeight()); BufferedImage bottomImage = lastMiniMe.getSubimage(0, od.getSequencesHeight(), od.getWidth(), od.getGraphHeight()); // must be done at this point as we rely on using old width/height // above, and new width/height below od.setWidth(getWidth()); od.setHeight(getHeight()); // stick the images back together so lastMiniMe is consistent in the // event of a repaint - BUT probably not thread safe lastMiniMe = new BufferedImage(od.getWidth(), od.getHeight(), BufferedImage.TYPE_INT_RGB); Graphics lg = lastMiniMe.getGraphics(); lg.drawImage(topImage, 0, 0, od.getWidth(), od.getSequencesHeight(), null); lg.drawImage(bottomImage, 0, od.getSequencesHeight(), od.getWidth(), od.getGraphHeight(), this); lg.dispose(); } else { od.setWidth(getWidth()); od.setHeight(getHeight()); } // scale lastMiniMe to the new size g.drawImage(lastMiniMe, 0, 0, getWidth(), getHeight(), this); // make sure the box is in the right place od.setBoxPosition(av.getAlignment().getHiddenSequences(), av.getAlignment().getHiddenColumns()); } else // not a resize { if (alpha != 0) // this is a timer triggered dissolve { Graphics2D g2d = (Graphics2D) g.create(); // draw the original image g2d.drawImage(veryLastMiniMe, 0, 0, getWidth(), getHeight(), this); // draw the new image on top with varying degrees of transparency g2d.setComposite(AlphaComposite.SrcOver.derive(1f - alpha)); g2d.drawImage(lastMiniMe, 0, 0, getWidth(), getHeight(), this); g2d.dispose(); } else { // fall back to normal behaviour g.drawImage(lastMiniMe, 0, 0, getWidth(), getHeight(), this); } } } // draw the box g.setColor(Color.red); od.drawBox(g); } }