From: hansonr Date: Mon, 29 Jul 2019 21:05:34 +0000 (-0500) Subject: JAL-3253-applet JAL-3383 X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=4435c731351c802772dd1355fdb5747cfacd7838;p=jalview.git JAL-3253-applet JAL-3383 Significant improvement to Overview rendering. See JAL-3383 issue for details. --- 4435c731351c802772dd1355fdb5747cfacd7838 diff --cc src/jalview/api/AlignmentColsCollectionI.java index 06b1675,06b1675..70dda87 --- a/src/jalview/api/AlignmentColsCollectionI.java +++ b/src/jalview/api/AlignmentColsCollectionI.java @@@ -20,6 -20,6 +20,8 @@@ */ package jalview.api; ++import java.util.BitSet; ++ public interface AlignmentColsCollectionI extends Iterable { /** @@@ -37,4 -37,4 +39,20 @@@ * @return true if there is at least 1 hidden column */ public boolean hasHidden(); ++ ++ /** ++ * Get the visible-column bitset, possibly containing hidden columns (which ++ * may or may not be hidden in the overview). ++ * ++ * @return a BitSet ++ */ ++ public BitSet getOverviewBitSet(); ++ ++ /** ++ * Get the hidden-column bitset, (which may or may not be hidden in the ++ * overview). ++ * ++ * @return ++ */ ++ BitSet getHiddenBitSet(); } diff --cc src/jalview/appletgui/OverviewCanvas.java index 5ea9c04,5ea9c04..7f4e962 --- a/src/jalview/appletgui/OverviewCanvas.java +++ b/src/jalview/appletgui/OverviewCanvas.java @@@ -129,8 -129,8 +129,7 @@@ public class OverviewCanvas extends Com or = new OverviewRenderer(panel.ap, fr, od, av.getAlignment(), av.getResidueShading(), new OverviewResColourFinder()); offscreen = nullFrame.createImage(od.getWidth(), od.getHeight()); -- or.draw(od.getRows(av.getAlignment()), -- od.getColumns(av.getAlignment())); ++ or.drawMiniMe(); } @Override @@@ -163,18 -163,18 +162,6 @@@ public void finalizeDraw(BufferedImage miniMe) { -- Graphics mg = miniMe.getGraphics(); -- -- // checks for conservation annotation to make sure overview works for DNA -- // too -- if (showAnnotation) -- { -- mg.translate(0, od.getSequencesHeight()); -- or.drawGraph(mg, av.getAlignmentConservationAnnotation(), -- od.getGraphHeight(), od.getColumns(av.getAlignment())); -- mg.translate(0, -od.getSequencesHeight()); -- } -- if (restart) { restart = false; @@@ -182,7 -182,7 +169,15 @@@ } else { ++ this.miniMe = miniMe; ++ // checks for conservation annotation to make sure overview works for DNA ++ // too ++ if (showAnnotation) ++ { ++ or.drawGraph(av.getAlignmentConservationAnnotation()); ++ } updaterunning = false; ++ repaint(); } } diff --cc src/jalview/appletgui/OverviewPanel.java index ef684f7,ef684f7..328841c --- a/src/jalview/appletgui/OverviewPanel.java +++ b/src/jalview/appletgui/OverviewPanel.java @@@ -255,11 -255,11 +255,11 @@@ public class OverviewPanel extends Pane @Override public void run() { ++ setBoxPosition(); canvas.draw(av.isShowSequenceFeatures(), (av.isShowAnnotation() && av.getAlignmentConservationAnnotation() != null), ap.seqPanel.seqCanvas.getFeatureRenderer()); -- setBoxPosition(); } /** diff --cc src/jalview/datamodel/Alignment.java index 91b29da,91b29da..98510e3 --- a/src/jalview/datamodel/Alignment.java +++ b/src/jalview/datamodel/Alignment.java @@@ -414,12 -414,12 +414,12 @@@ public class Alignment implements Align synchronized (groups) { -- temp.clear(); int gSize = groups.size(); if (gSize == 0) { return noGroups; } ++ temp.clear(); for (int i = 0; i < gSize; i++) { SequenceGroup sg = groups.get(i); @@@ -2034,4 -2034,4 +2034,17 @@@ } } ++ @Override ++ public void resetColors() ++ { ++ for (int i = getHeight(); --i >= 0;) ++ { ++ sequences.get(i).resetColors(); ++ } ++ // if (dataset != null) ++ // { ++ // dataset.resetColors(); ++ // } ++ } ++ } diff --cc src/jalview/datamodel/AlignmentI.java index 93a2456,93a2456..ebf4da3 --- a/src/jalview/datamodel/AlignmentI.java +++ b/src/jalview/datamodel/AlignmentI.java @@@ -624,4 -624,4 +624,10 @@@ public interface AlignmentI extends Ann public HiddenColumns propagateInsertions(SequenceI profileseq, AlignmentView input); ++ /** ++ * Remove all color already assigned to sequences, causing them to be be ++ * recalculated. ++ */ ++ void resetColors(); ++ } diff --cc src/jalview/datamodel/AllColsCollection.java index e216c46,e216c46..f3077fa --- a/src/jalview/datamodel/AllColsCollection.java +++ b/src/jalview/datamodel/AllColsCollection.java @@@ -22,6 -22,6 +22,7 @@@ package jalview.datamodel import jalview.api.AlignmentColsCollectionI; ++import java.util.BitSet; import java.util.Iterator; public class AllColsCollection implements AlignmentColsCollectionI @@@ -56,4 -56,4 +57,26 @@@ { return hidden.hasHiddenColumns(); } ++ ++ private BitSet bsVisible; ++ ++ @Override ++ public BitSet getHiddenBitSet() ++ { ++ return hidden.getBitset(); ++ } ++ ++ /** ++ * return ALL columns, not just the truly visible ones ++ */ ++ @Override ++ public BitSet getOverviewBitSet() ++ { ++ if (bsVisible == null) ++ { ++ bsVisible = new BitSet(end + 1); ++ bsVisible.set(0, end + 1); ++ } ++ return bsVisible; ++ } } diff --cc src/jalview/datamodel/HiddenColumns.java index 2d43f02,2d43f02..458bde7 --- a/src/jalview/datamodel/HiddenColumns.java +++ b/src/jalview/datamodel/HiddenColumns.java @@@ -86,6 -86,6 +86,20 @@@ public class HiddenColumn */ private List hiddenColumns = new ArrayList<>(); ++ private BitSet hiddenBitSet; ++ ++ public BitSet getBitset() ++ { ++ if (hiddenBitSet == null) ++ { ++ hiddenBitSet = new BitSet(); ++ for (int[] range : hiddenColumns) ++ { ++ hiddenBitSet.set(range[0], range[1] + 1); ++ } ++ } ++ return hiddenBitSet; ++ } /** * Constructor */ @@@ -213,6 -213,6 +227,7 @@@ prevHiddenCount); } finally { ++ hiddenBitSet = null; LOCK.writeLock().unlock(); } } @@@ -264,6 -264,6 +279,7 @@@ insertRangeAtOverlap(i, start, end, region); added = true; } ++ hiddenBitSet = null; return added; } @@@ -310,6 -310,6 +326,7 @@@ } numColumns += region[1] - oldend; hiddenColumns.subList(i + 1, endi + 1).clear(); ++ hiddenBitSet = null; } /** @@@ -330,6 -330,6 +347,7 @@@ } finally { ++ hiddenBitSet = null; LOCK.writeLock().unlock(); } } @@@ -356,6 -356,6 +374,7 @@@ } finally { ++ hiddenBitSet = null; LOCK.writeLock().unlock(); } } @@@ -399,6 -399,6 +418,7 @@@ } } finally { ++ hiddenBitSet = null; LOCK.writeLock().unlock(); } } @@@ -620,6 -620,6 +640,7 @@@ } finally { ++ hiddenBitSet = null; LOCK.readLock().unlock(); } } @@@ -798,7 -798,7 +819,7 @@@ for (int firstSet = tohide .nextSetBit(start), lastSet = start; firstSet >= start && lastSet <= end; firstSet = tohide -- .nextSetBit(lastSet)) ++ .nextSetBit(lastSet)) { lastSet = tohide.nextClearBit(firstSet); if (lastSet <= end) @@@ -813,6 -813,6 +834,7 @@@ cursor = new HiddenColumnsCursor(hiddenColumns); } finally { ++ hiddenBitSet = null; LOCK.writeLock().unlock(); } } @@@ -894,6 -894,6 +916,7 @@@ } } finally { ++ hiddenBitSet = null; LOCK.writeLock().unlock(); } } @@@ -909,16 -909,16 +932,12 @@@ { LOCK.writeLock().lock(); -- BitSet hiddenBitSet = new BitSet(); -- for (int[] range : hiddenColumns) -- { -- hiddenBitSet.set(range[0], range[1] + 1); -- } -- hiddenBitSet.andNot(updates); ++ getBitset().andNot(updates); hiddenColumns.clear(); hideColumns(hiddenBitSet); } finally { ++ hiddenBitSet = null; LOCK.writeLock().unlock(); } } diff --cc src/jalview/datamodel/Sequence.java index 4c46522,4c46522..6b52480 --- a/src/jalview/datamodel/Sequence.java +++ b/src/jalview/datamodel/Sequence.java @@@ -28,7 -28,7 +28,6 @@@ import jalview.util.DBRefUtils import jalview.util.MapList; import jalview.util.StringUtils; --import java.awt.Color; import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; @@@ -2027,6 -2027,6 +2026,7 @@@ public class Sequence extends ASequenc @Override public void sequenceChanged() { ++ argb = null; changeCount++; } @@@ -2129,20 -2129,20 +2129,27 @@@ return 0; } ++ private int[] argb; ++ @Override -- public Color getColor(int i) ++ public int getColor(int i) { -- return null; ++ return argb == null ? 0 : argb[i]; } @Override -- public Color setColor(int i, Color c) ++ public int setColor(int i, int rgb) { -- return c; ++ if (argb == null) ++ { ++ argb = new int[this.sequence.length]; ++ } ++ return (argb[i] = rgb); } @Override public void resetColors() { ++ argb = null; } } diff --cc src/jalview/datamodel/SequenceI.java index 5e2355d,5e2355d..e521029 --- a/src/jalview/datamodel/SequenceI.java +++ b/src/jalview/datamodel/SequenceI.java @@@ -25,7 -25,7 +25,6 @@@ import jalview.datamodel.features.Seque import jalview.util.MapList; import jalview.ws.params.InvalidArgumentException; --import java.awt.Color; import java.util.BitSet; import java.util.Iterator; import java.util.List; @@@ -585,9 -585,9 +584,9 @@@ public interface SequenceI extends ASeq */ public int firstResidueOutsideIterator(Iterator it); -- public Color getColor(int i); ++ public int getColor(int i); -- public Color setColor(int i, Color c); ++ public int setColor(int i, int rgb); public void resetColors(); diff --cc src/jalview/datamodel/VisibleColsCollection.java index 4ca51b5,4ca51b5..cd812a1 --- a/src/jalview/datamodel/VisibleColsCollection.java +++ b/src/jalview/datamodel/VisibleColsCollection.java @@@ -22,6 -22,6 +22,7 @@@ package jalview.datamodel import jalview.api.AlignmentColsCollectionI; ++import java.util.BitSet; import java.util.Iterator; public class VisibleColsCollection implements AlignmentColsCollectionI @@@ -32,6 -32,6 +33,8 @@@ HiddenColumns hidden; ++ private BitSet bsVisible; ++ public VisibleColsCollection(int s, int e, HiddenColumns h) { start = s; @@@ -57,4 -57,4 +60,27 @@@ return false; } ++ /** ++ * Only the visible columns. ++ */ ++ @Override ++ public BitSet getOverviewBitSet() ++ { ++ if (bsVisible == null) ++ { ++ bsVisible = new BitSet(end + 1); ++ } ++ bsVisible.clear(); ++ bsVisible.set(start, end + 1); ++ bsVisible.andNot(hidden.getBitset()); ++ ++ return bsVisible; ++ } ++ ++ @Override ++ public BitSet getHiddenBitSet() ++ { ++ return new BitSet(); ++ } ++ } diff --cc src/jalview/gui/AlignFrame.java index 4fa8408,4fa8408..bac06e9 --- a/src/jalview/gui/AlignFrame.java +++ b/src/jalview/gui/AlignFrame.java @@@ -4525,6 -4525,6 +4525,7 @@@ public class AlignFrame extends GAlignF { viewport.setShowSequenceFeatures(true); showSeqFeatures.setSelected(true); ++ alignPanel.getAlignment().resetColors(); } } diff --cc src/jalview/gui/AlignmentPanel.java index d305183,d305183..c8f0194 --- a/src/jalview/gui/AlignmentPanel.java +++ b/src/jalview/gui/AlignmentPanel.java @@@ -70,7 -70,7 +70,14 @@@ import java.util.List import javax.swing.SwingUtilities; /** -- * DOCUMENT ME! ++ * The main panel of an AlignFrame, containing holders for the IdPanel, ++ * SeqPanel, AnnotationLabels (a JPanel), and AnnotationPanel. ++ * ++ * Additional holders contain an IdPanelWidthAdjuster space above the idPanel, ++ * AnnotationScroller (JScrollPane for AnnotationPanel), and vertical and ++ * horizontal scrollbars. ++ * ++ * * * @author $author$ * @version $Revision: 1.161 $ @@@ -838,6 -838,6 +845,7 @@@ public class AlignmentPanel extends GAl if (overviewPanel != null) { ++ getAlignment().resetColors(); overviewPanel.updateOverviewImage(); } } diff --cc src/jalview/gui/FeatureRenderer.java index 2c54906,2c54906..cc034dd --- a/src/jalview/gui/FeatureRenderer.java +++ b/src/jalview/gui/FeatureRenderer.java @@@ -45,11 -45,11 +45,11 @@@ public class FeatureRendere { super(alignPanel.av); this.ap = alignPanel; -- if (alignPanel.getSeqPanel() != null -- && alignPanel.getSeqPanel().seqCanvas != null -- && alignPanel.getSeqPanel().seqCanvas.fr != null) ++ SeqPanel sp = alignPanel.getSeqPanel(); ++ if (sp != null && sp.seqCanvas != null && sp.seqCanvas.fr != null) { -- transferSettings(alignPanel.getSeqPanel().seqCanvas.fr); ++ sp.clearColors(); ++ transferSettings(sp.seqCanvas.fr); } } } diff --cc src/jalview/gui/OverviewCanvas.java index aafac38,aafac38..b6df722 --- a/src/jalview/gui/OverviewCanvas.java +++ b/src/jalview/gui/OverviewCanvas.java @@@ -73,15 -73,15 +73,18 @@@ public class OverviewCanvas extends JPa private OverviewPanel panel; ++ private boolean showProgress; ++ public OverviewCanvas(OverviewPanel panel, OverviewDimensions overviewDims, AlignViewportI alignvp, ProgressPanel pp) { this.panel = panel; od = overviewDims; ++ lastMiniMe = null; av = alignvp; progressPanel = pp; -- ++ showProgress = (pp != null); sr = new SequenceRenderer(av); sr.renderGaps = false; fr = new jalview.renderer.seqfeatures.FeatureRenderer(av); @@@ -106,6 -106,6 +109,7 @@@ public void resetOviewDims(OverviewDimensions overviewDims) { od = overviewDims; ++ lastMiniMe = null; } /** @@@ -123,6 -123,6 +127,7 @@@ else { updaterunning = true; ++ restart = false; } return restart; } @@@ -154,46 -154,46 +159,35 @@@ this.showSequenceFeatures = showSequenceFeatures; this.showAnnotation = showAnnotation; this.featureRenderer = featureRenderer; -- -- // System.out.println("OC draw " + ++ndraw + " showseqf=" -- // + showSequenceFeatures + " showAnno=" + showAnnotation); -- if (showSequenceFeatures) { fr.transferSettings(featureRenderer); } -- setPreferredSize(new Dimension(od.getWidth(), od.getHeight())); -- AlignmentI al = av.getAlignment(); -- or = new OverviewRenderer(panel.ap, fr, od, al, -- av.getResidueShading(), cf, -- progressPanel != null); -- if (progressPanel != null) ++ or = new OverviewRenderer(panel.ap, fr, od, al, av.getResidueShading(), ++ cf, showProgress); ++ if (showProgress) { or.addPropertyChangeListener(progressPanel); } -- or.draw(od.getRows(al), od.getColumns(al)); ++ or.drawMiniMe(); } -- void finalizeDraw(BufferedImage miniMe) ++ synchronized void finalizeDraw(BufferedImage miniMe) { -- Graphics mg = miniMe.getGraphics(); -- if (showAnnotation) ++ ++ if (or == null) { -- mg.translate(0, od.getSequencesHeight()); -- or.drawGraph(mg, av.getAlignmentConservationAnnotation(), -- od.getGraphHeight(), od.getColumns(av.getAlignment())); -- mg.translate(0, -od.getSequencesHeight()); ++ System.out.println("OC or is null"); } -- mg.dispose(); // BH 2019 -- if (progressPanel != null) ++ else if (showProgress) { or.removePropertyChangeListener(progressPanel); } -- or = null; if (restart) { ++ or = null; restart = false; if (!disposed) { @@@ -202,12 -202,12 +196,17 @@@ } else { ++ if (showAnnotation) ++ { ++ or.drawGraph(av.getAlignmentConservationAnnotation()); ++ } ++ or = null; updaterunning = false; lastMiniMe = miniMe; repaint(); } -- } ++ @Override public void paintComponent(Graphics g) { @@@ -240,40 -240,40 +239,56 @@@ else if (drawMe) { // is this a resize? -- if (w != od.getWidth() || h != od.getHeight()) { -- // if there is annotation, scale the alignment and annotation -- // separately -- if (od.getGraphHeight() <= 0 && od.getSequencesHeight() <= 0) ++ if (w != od.getWidth() || h != od.getHeight()) { -- od.setWidth(w); -- od.setHeight(h); ++ ++ lastMiniMe = null; return; -- } -- // System.out.println("OC new subimages"); -- 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(w); -- od.setHeight(h); -- -- // stick the images back together so lastMiniMe is consistent in the -- // event of a repaint - BUT probably not thread safe -- // System.out.println("OC new lastminime " + w + " " + h); -- lastMiniMe = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); -- Graphics lg = lastMiniMe.getGraphics(); -- lg.drawImage(topImage, 0, 0, w, od.getSequencesHeight(), null); -- lg.drawImage(bottomImage, 0, od.getSequencesHeight(), w, -- od.getGraphHeight(), this); -- lg.dispose(); -- // BH 2019: removed -- this is now taken care of using vpbox in -- // OverviewDimension -- // // make sure the box is in the right place -- // od.setBoxPosition(av.getAlignment().getHiddenSequences(), -- // av.getAlignment().getHiddenColumns()); ++ // // if there is annotation, scale the alignment and annotation ++ // // separately ++ // if (od.getGraphHeight() <= 0 && od.getSequencesHeight() <= 0) ++ // { ++ // od.setWidth(w); ++ // od.setHeight(h); ++ // return; ++ // } ++ // try ++ // { ++ // 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(w); ++ // od.setHeight(h); ++ // ++ // // stick the images back together so lastMiniMe is consistent in the ++ // // event of a repaint - BUT probably not thread safe ++ // ++ // // right -- this fails with fast user action. ++ // ++ // lastMiniMe = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); ++ // Graphics lg = lastMiniMe.getGraphics(); ++ // lg.drawImage(topImage, 0, 0, w, od.getSequencesHeight(), null); ++ // lg.drawImage(bottomImage, 0, od.getSequencesHeight(), w, ++ // od.getGraphHeight(), this); ++ // lg.dispose(); ++ // ++ // } catch (RasterFormatException e) ++ // { ++ // System.out.println("OC Raster Exception " + lastMiniMe.getWidth() ++ // + "/" + w + "," + lastMiniMe.getHeight() + "/" + h + " " ++ // + od.getSequencesHeight() + " " + od.getGraphHeight()); ++ // } ++ // BH 2019: removed -- this is now taken care of using vpbox in ++ // OverviewDimension ++ // // make sure the box is in the right place ++ // od.setBoxPosition(av.getAlignment().getHiddenSequences(), ++ // av.getAlignment().getHiddenColumns()); } } @@@ -301,6 -301,6 +316,7 @@@ { disposed = true; od = null; ++ lastMiniMe = null; synchronized (this) { setRestart("dispose"); diff --cc src/jalview/gui/OverviewPanel.java index 387d3dd,8f24185..cc647a8 --- a/src/jalview/gui/OverviewPanel.java +++ b/src/jalview/gui/OverviewPanel.java @@@ -130,6 -130,6 +130,7 @@@ public class OverviewPanel extends JPan if (getWidth() == od.getWidth() && getHeight() == od.getHeight() + ph) { ++ // BH: resizing is now exceptionally fast. updateOverviewImage(); } else @@@ -144,7 -144,7 +145,8 @@@ } od.setWidth(w); od.setHeight(h - ph); -- repaint(); ++ updateOverviewImage(); ++ // repaint(); } // BH 2019.07.29 this is unnecessary -- it is what layout managers are // for: @@@ -326,11 -326,7 +328,20 @@@ } /** - * Updates the overview image when the related alignment panel is updated + * Updates the overview image when the related alignment panel is updated. + * + * Cases: + * ++ * AlignFrame.setFeatureGroupState ++ * ++ * AlignmentPanel.paintAlignment(true,...) (117 references) ++ * ++ * OverviewPanel..componentResized() OverviewPanel.toggleHiddenColumns() ++ * ++ * PopupMenu for action.reveal_sequences, action.reveal_all ++ * ++ * SliderPanel.mouseReleased() + * */ public void updateOverviewImage() { @@@ -367,11 -363,11 +378,11 @@@ { if (canvas != null) { ++ setBoxPosition(); canvas.draw(av.isShowSequenceFeatures(), (av.isShowAnnotation() && av.getAlignmentConservationAnnotation() != null), ap.getFeatureRenderer()); -- setBoxPosition(); } } diff --cc src/jalview/gui/ScalePanel.java index aa58a88,aa58a88..b06647d --- a/src/jalview/gui/ScalePanel.java +++ b/src/jalview/gui/ScalePanel.java @@@ -569,31 -569,31 +569,29 @@@ public class ScalePanel extends JPane @Override public void propertyChange(PropertyChangeEvent evt) { -- // Respond to viewport change events (e.g. alignment panel was scrolled) -- // Both scrolling and resizing change viewport ranges: scrolling changes -- // both start and end points, but resize only changes end values. -- // Here we only want to fastpaint on a scroll, with resize using a normal -- // paint, so scroll events are identified as changes to the horizontal or -- // vertical start value. switch (evt.getPropertyName()) { case ViewportRanges.STARTRES: case ViewportRanges.STARTRESANDSEQ: case ViewportRanges.MOVE_VIEWPORT: // scroll event, repaint panel -- // TODO: check this? -- // BH: This is actually quite strange. AlignmentPanel is taking care of -- // all of -- // this with fast paint, so why indirectly trigger a repaint from the -- // ScalePanel? -- ++ // original comment: // Call repaint on alignment panel so that repaints from other alignment // panel components can be aggregated. Otherwise performance of the // overview // window and others may be adversely affected. -- // av.getAlignPanel().repaint(); -- System.out.println("ScalePanel propertyChange disabled " -- + evt.getPropertyName()); ++ ++ // TODO: check this? ++ // BH: This is actually quite strange. AlignmentPanel is taking care of ++ // all of this with fast paint, so why indirectly trigger a repaint from ++ // the ScalePanel? Where do we see this behavior necessary? ++ // I have set this to check for a trigger from some other ViewportRanges, ++ // but I don't actually think that is possible. ++ ++ if (evt.getSource() != av.getRanges()) ++ { ++ av.getAlignPanel().repaint(); ++ } break; } } diff --cc src/jalview/gui/SeqCanvas.java index 0d27354,0d27354..2d83e9e --- a/src/jalview/gui/SeqCanvas.java +++ b/src/jalview/gui/SeqCanvas.java @@@ -375,15 -375,15 +375,15 @@@ public class SeqCanvas extends JPanel i int charHeight = av.getCharHeight(); int charWidth = av.getCharWidth(); -- int width = getWidth(); -- int height = getHeight(); ++ int availWidth = getWidth(); ++ int availHeight = getHeight(); -- width -= (width % charWidth); -- height -= (height % charHeight); ++ availWidth -= (availWidth % charWidth); ++ availHeight -= (availHeight % charHeight); // BH 2019 can't possibly fastPaint if either width or height is 0 -- if (width == 0 || height == 0) ++ if (availWidth == 0 || availHeight == 0) { return; } @@@ -433,10 -433,10 +433,11 @@@ // img is a cached version of the last view we drew. // If we have no img or the size has changed, make a new one. // -- if (img == null || width != img.getWidth() -- || height != img.getHeight()) ++ if (img == null || availWidth != img.getWidth() ++ || availHeight != img.getHeight()) { -- img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); ++ img = new BufferedImage(availWidth, availHeight, ++ BufferedImage.TYPE_INT_RGB); } Graphics2D gg = (Graphics2D) img.getGraphics(); @@@ -449,11 -449,11 +450,11 @@@ } gg.setColor(Color.white); -- gg.fillRect(0, 0, img.getWidth(), img.getHeight()); ++ gg.fillRect(0, 0, availWidth, availHeight); if (av.getWrapAlignment()) { -- drawWrappedPanel(gg, width, height, ranges.getStartRes()); ++ drawWrappedPanel(gg, availWidth, availHeight, ranges.getStartRes()); } else { @@@ -521,32 -521,32 +522,31 @@@ } /** -- * Returns the visible width of the canvas in residues, after allowing for -- * East or West scales (if shown) ++ * Using the current font, determine fields labelWidthEast and labelWidthWest, ++ * and return the number of residues that can fill the remaining width. * -- * @param canvasWidth ++ * @param width * the width in pixels (possibly including scales) * -- * @return ++ * @return the visible width in residues, after allowing for East or West ++ * scales (if shown) ++ * */ -- public int getWrappedCanvasWidth(int canvasWidth) ++ public int getWrappedCanvasWidth(int width) { int charWidth = av.getCharWidth(); FontMetrics fm = getFontMetrics(av.getFont()); -- int labelWidth = 0; -- -- if (av.getScaleRightWrapped() || av.getScaleLeftWrapped()) -- { -- labelWidth = getLabelWidth(fm); -- } ++ int labelWidth = (av.getScaleRightWrapped() || av.getScaleLeftWrapped() ++ ? getLabelWidth(fm) ++ : 0); labelWidthEast = av.getScaleRightWrapped() ? labelWidth : 0; labelWidthWest = av.getScaleLeftWrapped() ? labelWidth : 0; -- return (canvasWidth - labelWidthEast - labelWidthWest) / charWidth; ++ return (width - labelWidthEast - labelWidthWest) / charWidth; } /** @@@ -572,6 -572,6 +572,7 @@@ maxWidth = Math.max(maxWidth, alignment.getSequenceAt(i).getEnd()); } ++ // quick int log10 int length = 0; for (int i = maxWidth; i > 0; i /= 10) { @@@ -586,18 -586,18 +587,18 @@@ * window * * @param g -- * @param canvasWidth ++ * @param availWidth * available width in pixels -- * @param canvasHeight ++ * @param availHeight * available height in pixels * @param startColumn * the first column (0...) of the alignment to draw */ -- public void drawWrappedPanel(Graphics g, int canvasWidth, -- int canvasHeight, final int startColumn) ++ public void drawWrappedPanel(Graphics g, int availWidth, int availHeight, ++ final int startColumn) { -- int wrappedWidthInResidues = calculateWrappedGeometry(canvasWidth, -- canvasHeight); ++ int wrappedWidthInResidues = calculateWrappedGeometry(availWidth, ++ availHeight); av.setWrappedWidth(wrappedWidthInResidues); @@@ -607,7 -607,7 +608,7 @@@ // we need to call this again to make sure the startColumn + // wrappedWidthInResidues values are used to calculate wrappedVisibleWidths // correctly. -- calculateWrappedGeometry(canvasWidth, canvasHeight); ++ calculateWrappedGeometry(availWidth, availHeight); /* * draw one width at a time (excluding any scales shown), @@@ -622,7 -622,7 +623,7 @@@ { int endColumn = Math .min(maxWidth, start + wrappedWidthInResidues - 1); -- drawWrappedWidth(g, ypos, start, endColumn, canvasHeight); ++ drawWrappedWidth(g, ypos, start, endColumn, availHeight); ypos += wrappedRepeatHeightPx; start += wrappedWidthInResidues; currentWidth++; @@@ -641,11 -641,11 +642,11 @@@ *
  • whether scales are shown left, right or above the alignment
  • * * -- * @param canvasWidth -- * @param canvasHeight ++ * @param availWidth ++ * @param availHeight * @return the number of residue columns in each width */ -- protected int calculateWrappedGeometry(int canvasWidth, int canvasHeight) ++ protected int calculateWrappedGeometry(int availWidth, int availHeight) { int charHeight = av.getCharHeight(); @@@ -679,8 -679,8 +680,8 @@@ * ensuring a part height includes at least one sequence */ ViewportRanges ranges = av.getRanges(); -- wrappedVisibleWidths = canvasHeight / wrappedRepeatHeightPx; -- int remainder = canvasHeight % wrappedRepeatHeightPx; ++ wrappedVisibleWidths = availHeight / wrappedRepeatHeightPx; ++ int remainder = availHeight % wrappedRepeatHeightPx; if (remainder >= (wrappedSpaceAboveAlignment + charHeight)) { wrappedVisibleWidths++; @@@ -689,7 -689,7 +690,7 @@@ /* * compute width in residues; this also sets East and West label widths */ -- int wrappedWidthInResidues = getWrappedCanvasWidth(canvasWidth); ++ int wrappedWidthInResidues = getWrappedCanvasWidth(availWidth); /* * limit visibleWidths to not exceed width of alignment diff --cc src/jalview/gui/SeqPanel.java index de67c39,de67c39..e5a5962 --- a/src/jalview/gui/SeqPanel.java +++ b/src/jalview/gui/SeqPanel.java @@@ -74,7 -74,7 +74,8 @@@ import javax.swing.Timer import javax.swing.ToolTipManager; /** -- * DOCUMENT ME! ++ * The main scrollable region containing the alignment and just to the right of ++ * the IDPanel. * * @author $author$ * @version $Revision: 1.130 $ @@@ -223,7 -223,7 +224,7 @@@ public class SeqPanel extends JPane SearchResultsI lastSearchResults; /** -- * Creates a new SeqPanel object ++ * Create a new SeqPanel. * * @param viewport * @param alignPanel @@@ -2811,4 -2811,4 +2812,11 @@@ true); } ++ public void clearColors() ++ { ++ av.getAlignment().getSequences(); ++ // TODO Auto-generated method stub ++ ++ } ++ } diff --cc src/jalview/io/AlignFile.java index cea2870,cea2870..c2cf667 --- a/src/jalview/io/AlignFile.java +++ b/src/jalview/io/AlignFile.java @@@ -323,14 -323,14 +323,15 @@@ public abstract class AlignFile extend */ protected void initData() { -- seqs = new Vector(); -- annotations = new Vector(); -- seqGroups = new ArrayList(); ++ seqs = new Vector<>(); ++ annotations = new Vector<>(); ++ seqGroups = new ArrayList<>(); parseCalled = false; } /** -- * DOCUMENT ME! ++ * Create the seqs Vector from a set of parsed sequences in an AlignFile, ++ * FeaturesFile, RnamlFile, or StockholmFile. * * @param s * DOCUMENT ME! @@@ -338,7 -338,7 +339,7 @@@ @Override public void setSeqs(SequenceI[] s) { -- seqs = new Vector(); ++ seqs = new Vector<>(); for (int i = 0; i < s.length; i++) { @@@ -409,7 -409,7 +410,7 @@@ { if (newickStrings == null) { -- newickStrings = new Vector(); ++ newickStrings = new Vector<>(); } newickStrings.addElement(new String[] { treeName, newickString }); } diff --cc src/jalview/io/FeaturesFile.java index 1fb45bc,1fb45bc..aa21b0f --- a/src/jalview/io/FeaturesFile.java +++ b/src/jalview/io/FeaturesFile.java @@@ -829,14 -829,14 +829,15 @@@ public class FeaturesFile extends Align AlignViewportI av = getViewport(); if (av != null) { -- if (av.getAlignment() != null) ++ AlignmentI a = av.getAlignment(); ++ if (a != null) { -- dataset = av.getAlignment().getDataset(); ++ dataset = a.getDataset(); } if (dataset == null) { // working in the applet context ? -- dataset = av.getAlignment(); ++ dataset = a; } } else diff --cc src/jalview/jbgui/GAlignmentPanel.java index b703b47,b703b47..3dd0205 --- a/src/jalview/jbgui/GAlignmentPanel.java +++ b/src/jalview/jbgui/GAlignmentPanel.java @@@ -123,6 -123,6 +123,7 @@@ public class GAlignmentPanel extends JP hscrollFillerPanel.setPreferredSize(new Dimension(70, 10)); hscrollHolder.setBackground(Color.white); annotationScroller.setBorder(null); ++ annotationScroller.setBackground(Color.BLUE); annotationScroller.setPreferredSize(new Dimension(10, 80)); this.setPreferredSize(new Dimension(220, 166)); diff --cc src/jalview/renderer/OverviewRenderer.java index 45cc944,45cc944..6defca7 --- a/src/jalview/renderer/OverviewRenderer.java +++ b/src/jalview/renderer/OverviewRenderer.java @@@ -66,9 -66,9 +66,17 @@@ public class OverviewRendere // image to render on private BufferedImage miniMe; -- // raw number of pixels to allocate to each column ++ /** ++ * Number of pixelsPerCol; ++ */ private float pixelsPerCol; ++ /** ++ * Number of visible columns per pixel. ++ * ++ */ ++ private float colsPerPixel; ++ // raw number of pixels to allocate to each row private float pixelsPerSeq; @@@ -89,6 -89,6 +97,8 @@@ private AlignmentViewPanel panel; ++ private int sequencesHeight; ++ public OverviewRenderer(AlignmentViewPanel panel, FeatureRenderer fr, OverviewDimensions od, AlignmentI alignment, @@@ -98,24 -98,24 +108,28 @@@ } public OverviewRenderer(AlignmentViewPanel panel, -- jalview.api.FeatureRenderer fr, -- OverviewDimensions od, ++ jalview.api.FeatureRenderer fr, OverviewDimensions od, AlignmentI alignment, ResidueShaderI resshader, OverviewResColourFinder colFinder, boolean showProgress) { this.panel = panel; finder = new FeatureColourFinder(fr); -- resColFinder = colFinder; -- al = alignment; shader = resshader; ++ resColFinder = colFinder; ++ this.showProgress = showProgress; -- pixelsPerCol = od.getPixelsPerCol(); -- pixelsPerSeq = od.getPixelsPerSeq(); ++ w = od.getWidth(); ++ h = od.getHeight(); ++ rows = od.getRows(alignment); ++ cols = od.getColumns(alignment); graphHeight = od.getGraphHeight(); -- miniMe = new BufferedImage(od.getWidth(), od.getHeight(), -- BufferedImage.TYPE_INT_RGB); -- this.showProgress = showProgress; ++ alignmentHeight = od.getSequencesHeight(); ++ ++ pixelsPerSeq = od.getPixelsPerSeq(); ++ pixelsPerCol = od.getPixelsPerCol(); ++ colsPerPixel = Math.max(1, 1f / pixelsPerCol); ++ } final static int STATE_INIT = 0; @@@ -135,65 -135,65 +149,87 @@@ private Integer row; -- void mainLoop() ++ /** ++ * Draw alignment rows and columns onto an image. This method is asynchronous ++ * in JavaScript and interruptible in Java. ++ * ++ * Whether hidden rows or columns are drawn depends upon the type of ++ * collection. ++ * ++ * Updated to skip through high-density sequences, where columns/pixels > 1. ++ * ++ * When the process is complete, the image is passed to the AlignmentViewPanel ++ * provided by the constructor. ++ * ++ * @param rows ++ * collection of rows to be drawn ++ * @param cols ++ * collection of columns to be drawn ++ * @return image containing the drawing ++ * ++ * @author Bob Hanson 2019.07.30 ++ */ ++ public void drawMiniMe() { -- while (!redraw) ++ state = STATE_INIT; ++ mainLoop(); ++ } ++ ++ protected void mainLoop() ++ { ++ out: while (!redraw) { switch (state) { case STATE_INIT: -- seqIndex = 0; -- pixelRow = 0; ++ init(); state = STATE_NEXT; continue; case STATE_NEXT: -- if (iter.hasNext()) ++ if (!rowIterator.hasNext()) { -- nextRow(); ++ state = STATE_DONE; ++ continue; } -- else ++ nextRow(); ++ if (!loop()) { -- state = STATE_DONE; ++ continue; } -- break; -- case STATE_DONE: -- done(); -- return; -- } -- if (delay > 0) -- { -- jsloop(); return; ++ case STATE_DONE: ++ break out; } ++ // Java will continue without a timeout } done(); } -- private void jsloop() ++ private void init() { -- if (timer == null) -- { -- timer = new Timer(delay, new ActionListener() -- { -- @Override -- public void actionPerformed(ActionEvent e) -- { -- mainLoop(); -- } ++ rowIterator = rows.iterator(); ++ seqIndex = 0; ++ pixelRow = 0; ++ lastRowUpdate = 0; ++ lastUpdate = 0; ++ totalPixels = w * alignmentHeight; -- }); -- timer.setRepeats(false); -- timer.start(); -- } -- else ++ if (showProgress) { -- timer.restart(); ++ changeSupport.firePropertyChange(UPDATE, -1, 0); } ++ ++ miniMe = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); ++ WritableRaster raster = miniMe.getRaster(); ++ DataBufferInt db = (DataBufferInt) raster.getDataBuffer(); ++ Platform.timeCheck(null, Platform.TIME_MARK); ++ pixels = db.getBankData()[0]; ++ bscol = cols.getOverviewBitSet(); } private void nextRow() { -- row = iter.next(); ++ row = rowIterator.next(); // System.out.println("OR row " + r); // get details of this alignment row SequenceI seq = rows.getSequence(row); @@@ -203,30 -203,30 +239,32 @@@ // 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)) ++ for (int pixelCol = 0, colNext = 0, pixelEnd = 0, icol = bscol ++ .nextSetBit(0); icol >= 0; icol = getNextCol(icol, pixelEnd)) { if (redraw) { break; } -- // calculate where this column extends to in pixels -- int endCol = Math.min(Math.round((++colIndex) * pixelsPerCol), w); -- -- // 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 ++ ++colNext; ++ pixelEnd = getNextPixel(colNext, colNext); -- if (pixelCol < endCol) ++ if (pixelCol == pixelEnd) { -- // System.out.println("OR pc ec " + pixelCol + " " + endCol); -- int rgb = getColumnColourFromSequence(allGroups, seq, c); ++ break; ++ } ++ else if (pixelCol < pixelEnd) ++ { ++ int rgb = getColumnColourFromSequence(allGroups, seq, icol); // fill in the appropriate number of pixels ++ // System.out.println( ++ // "OR colNext=" + colNext + " " + pixelCol ++ // + "-" + pixelEnd + " icol=" + icol + " " + rgb + " " ++ // + pixelsPerCol); for (int row = pixelRow; row < endRow; ++row) { -- for (int col = pixelCol; col < endCol; ++col) ++ for (int col = pixelCol; col < pixelEnd; ++col) { // BH 2019.07.27 was: // @@@ -238,16 -238,16 +276,16 @@@ ndone++; } } -- // } -- -- pixelCol = endCol; ++ pixelCol = pixelEnd; // store last update value if (showProgress) { -- lastUpdate = sendProgressUpdate(endCol * (endRow - 1 - pixelRow), ++ lastUpdate = sendProgressUpdate( ++ pixelEnd * (endRow - 1 - pixelRow), totalPixels, lastRowUpdate, lastUpdate); } } ++ } if (pixelRow < endRow) { @@@ -264,6 -264,6 +302,57 @@@ } } ++ /** ++ * The next column is either the next set bit (when there are multiple pixels ++ * per column) or the next set bit for the column that aligns with the next ++ * pixel (when there are more columns than pixels). ++ * ++ * @param icol ++ * @param pixel ++ * @return ++ */ ++ private int getNextCol(int icol, int pixel) ++ { ++ return bscol.nextSetBit( ++ pixelsPerCol >= 1 ? icol + 1 : (int) (pixel * colsPerPixel)); ++ } ++ ++ private int getNextPixel(int icol, int pixel) ++ { ++ return Math.min( ++ pixelsPerCol >= 1 || pixel == 0 ++ ? Math.round(icol * pixelsPerCol) ++ : pixel, ++ w); ++ } ++ ++ private boolean loop() ++ { ++ if (delay <= 0) ++ { ++ return false; ++ } ++ if (timer == null) ++ { ++ timer = new Timer(delay, new ActionListener() ++ { ++ @Override ++ public void actionPerformed(ActionEvent e) ++ { ++ mainLoop(); ++ } ++ ++ }); ++ timer.setRepeats(false); ++ timer.start(); ++ } ++ else ++ { ++ timer.restart(); ++ } ++ return true; ++ } ++ private void done() { Platform.timeCheck( @@@ -271,7 -271,7 +360,7 @@@ + redraw, Platform.TIME_MARK); -- overlayHiddenRegions(rows, cols); ++ overlayHiddenRegions(); if (showProgress) { // final update to progress bar if present @@@ -296,7 -296,7 +385,7 @@@ private AlignmentColsCollectionI cols; -- Iterator iter; ++ Iterator rowIterator; int alignmentHeight; @@@ -308,53 -308,53 +397,10 @@@ int[] pixels; -- BitSet bscol = new BitSet(); ++ BitSet bscol; 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) -- { -- bscol.set(c); -- } -- state = STATE_INIT; -- mainLoop(); -- -- return miniMe; -- } -- /* * Calculate progress update value and fire event * @param rowOffset number of rows to offset calculation by @@@ -387,25 -387,25 +433,19 @@@ { return (seq == null || icol >= seq.getLength() ? resColFinder.GAP_COLOUR -- : resColFinder.getResidueColour(true, shader, allGroups, seq, -- icol, finder)).getRGB(); ++ : resColFinder.getResidueColourInt(true, shader, allGroups, seq, ++ icol, 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) ++ private void overlayHiddenRegions() { if (cols.hasHidden() || rows.hasHidden()) { -- BufferedImage mask = buildHiddenImage(rows, cols, miniMe.getWidth(), -- miniMe.getHeight()); ++ BufferedImage mask = buildHiddenImage(); Graphics2D g = (Graphics2D) miniMe.getGraphics(); g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, @@@ -429,17 -429,17 +469,17 @@@ * height of overview in pixels * @return BufferedImage containing mask of hidden regions */ -- private BufferedImage buildHiddenImage(AlignmentRowsCollectionI rows, -- AlignmentColsCollectionI cols, int width, int height) ++ private BufferedImage buildHiddenImage() { // new masking image -- BufferedImage hiddenImage = new BufferedImage(width, height, ++ BufferedImage hiddenImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); Color hidden = resColFinder.getHiddenColour(); Graphics2D g2d = (Graphics2D) hiddenImage.getGraphics(); ++ g2d.setColor(hidden); // set background to transparent // g2d.setComposite(AlphaComposite.Clear); // g2d.fillRect(0, 0, width, height); @@@ -447,31 -447,31 +487,26 @@@ // set next colour to opaque g2d.setComposite(AlphaComposite.Src); ++ // System.out.println(cols.getClass().getName()); if (cols.hasHidden()) { -- int colIndex = 0; -- int pixelCol = 0; -- for (int alignmentCol : cols) ++ // AllColsCollection only ++ BitSet bs = cols.getHiddenBitSet(); ++ for (int pixelCol = -1, icol2 = 0, icol = bs ++ .nextSetBit(0); icol >= 0; icol = bs.nextSetBit(icol2)) { if (redraw) { break; } -- -- // calculate where this column extends to in pixels -- int endCol = Math.min(Math.round((++colIndex) * pixelsPerCol), -- width); -- -- // endCol is one more than old endCol -- if (pixelCol < endCol) ++ icol2 = bs.nextClearBit(icol + 1); ++ int pixelEnd = getNextPixel(icol2, 0); ++ if (pixelEnd > pixelCol) { -- // 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 = getNextPixel(icol, 0); ++ g2d.fillRect(pixelCol, 0, Math.max(1, pixelEnd - pixelCol), ++ h); ++ pixelCol = pixelEnd; } } } @@@ -488,13 -488,13 +523,12 @@@ // calculate where this row extends to in pixels int endRow = Math.min(Math.round((++seqIndex) * pixelsPerSeq), -- height); ++ h); // get details of this alignment row if (rows.isHidden(alignmentRow)) { -- g2d.setColor(hidden); -- g2d.fillRect(0, pixelRow, width, endRow - 1 - pixelRow); ++ g2d.fillRect(0, pixelRow, w, endRow - 1 - pixelRow); } pixelRow = endRow; } @@@ -506,27 -506,27 +540,23 @@@ /** * Draw the alignment annotation in the overview panel * -- * @param g -- * the graphics object to draw on * @param anno * alignment annotation information -- * @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 y, -- AlignmentColsCollectionI cols) ++ public void drawGraph(AlignmentAnnotation anno) { ++ int y = graphHeight; ++ Graphics g = miniMe.getGraphics(); ++ g.translate(0, alignmentHeight); ++ Annotation[] annotations = anno.annotations; float max = anno.graphMax; g.setColor(Color.white); -- int width = miniMe.getWidth(); -- g.fillRect(0, 0, width, y); ++ g.fillRect(0, 0, w, y); -- int colIndex = 0; -- int pixelCol = 0; -- for (int icol : cols) ++ for (int pixelCol = 0, colNext = 0, pixelEnd = 0, len = annotations.length, icol = bscol ++ .nextSetBit(0); icol >= 0 ++ && icol < len; icol = getNextCol(icol, pixelEnd)) { if (redraw) { @@@ -537,27 -537,27 +567,29 @@@ break; } -- if (icol >= annotations.length) -- { -- break; // no more annotations to draw here -- } -- int endCol = Math.min(Math.round((++colIndex) * pixelsPerCol), width); ++ ++colNext; ++ pixelEnd = getNextPixel(colNext, colNext); Annotation ann = annotations[icol]; if (ann != null) { Color color = ann.colour; g.setColor(color == null ? Color.black : color); -- int height = Math.min(y, (int) ((ann.value / max) * y)); -- g.fillRect(pixelCol, y - height, endCol - pixelCol, height); ++ g.fillRect(pixelCol, y - height, Math.max(1, pixelEnd - pixelCol), ++ height); } -- pixelCol = endCol; ++ pixelCol = pixelEnd; } ++ ++ g.translate(0, -alignmentHeight); ++ g.dispose(); ++ if (showProgress) { changeSupport.firePropertyChange(UPDATE, MAX_PROGRESS - 1, MAX_PROGRESS); } ++ } /** diff --cc src/jalview/renderer/OverviewResColourFinder.java index ff52f1d,ff52f1d..b950542 --- a/src/jalview/renderer/OverviewResColourFinder.java +++ b/src/jalview/renderer/OverviewResColourFinder.java @@@ -22,15 -22,15 +22,16 @@@ package jalview.renderer import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; ++import jalview.renderer.seqfeatures.FeatureColourFinder; import jalview.util.Comparison; import java.awt.Color; public class OverviewResColourFinder extends ResidueColourFinder { -- final Color GAP_COLOUR; // default colour to use at gaps ++ final int GAP_COLOUR; // default colour to use at gaps -- final Color RESIDUE_COLOUR; // default colour to use at residues ++ final int RESIDUE_COLOUR; // default colour to use at residues final Color HIDDEN_COLOUR; // colour for hidden regions @@@ -66,27 -66,27 +67,19 @@@ { if (useLegacyColouring) { -- GAP_COLOUR = Color.white; -- RESIDUE_COLOUR = Color.lightGray; -- HIDDEN_COLOUR = hiddenCol; ++ GAP_COLOUR = Color.white.getRGB(); ++ RESIDUE_COLOUR = Color.lightGray.getRGB(); } else { -- GAP_COLOUR = gapCol; -- RESIDUE_COLOUR = Color.white; -- HIDDEN_COLOUR = hiddenCol; ++ GAP_COLOUR = gapCol.getRGB(); ++ RESIDUE_COLOUR = Color.white.getRGB(); } ++ HIDDEN_COLOUR = hiddenCol; } -- @Override -- public Color getBoxColour(ResidueShaderI shader, SequenceI seq, int i) ++ public int getBoxColourInt(ResidueShaderI shader, SequenceI seq, int i) { -- seq.resetColors(); -- Color c = seq.getColor(i); -- if (c != null) -- { -- return c; -- } char currentChar = seq.getCharAt(i); // In the overview window, gaps are coloured grey, unless the colour scheme // specifies a gap colour, in which case gaps honour the colour scheme @@@ -94,19 -94,19 +87,39 @@@ boolean isGap = Comparison.isGap(currentChar); if (shader.getColourScheme() == null) { -- return seq.setColor(i, isGap ? GAP_COLOUR : RESIDUE_COLOUR); ++ return (isGap ? GAP_COLOUR : RESIDUE_COLOUR); } -- return seq.setColor(i, -- isGap && !shader.getColourScheme().hasGapColour() ? GAP_COLOUR -- : shader.findColour(currentChar, i, seq)); ++ return (isGap && !shader.getColourScheme().hasGapColour() ? GAP_COLOUR ++ : shader.findColour(currentChar, i, seq).getRGB()); } ++ public int getResidueColourInt(boolean showBoxes, ResidueShaderI shader, ++ SequenceGroup[] allGroups, final SequenceI seq, int i, ++ FeatureColourFinder finder) ++ { ++ ++ int c = seq.getColor(i); ++ if (c != 0) ++ { ++ return c; ++ } ++ ++ int col = getResidueBoxColourInt(showBoxes, shader, allGroups, seq, ++ i); ++ ++ ++ // if there's a FeatureColourFinder we might override the residue colour ++ // here with feature colouring ++ return seq.setColor(i, ++ finder == null || finder.noFeaturesDisplayed() ? col ++ : finder.findFeatureColourInt(col, seq, i)); ++ } ++ /** -- * {@inheritDoc} In the overview, the showBoxes setting is ignored, as the -- * overview displays the colours regardless. ++ * In the overview, the showBoxes setting is ignored, as the overview displays ++ * the colours regardless. */ -- @Override -- protected Color getResidueBoxColour(boolean showBoxes, ++ protected int getResidueBoxColourInt(boolean showBoxes, ResidueShaderI shader, SequenceGroup[] allGroups, SequenceI seq, int i) { @@@ -114,7 -114,7 +127,7 @@@ i); ResidueShaderI currentShader = (currentSequenceGroup == null ? shader : currentSequenceGroup.getGroupColourScheme()); -- return getBoxColour(currentShader, seq, i); ++ return getBoxColourInt(currentShader, seq, i); } /** diff --cc src/jalview/renderer/ResidueColourFinder.java index 2da7233,2da7233..2e45117 --- a/src/jalview/renderer/ResidueColourFinder.java +++ b/src/jalview/renderer/ResidueColourFinder.java @@@ -116,12 -116,12 +116,13 @@@ public class ResidueColourFinde public SequenceGroup getCurrentSequenceGroup(SequenceGroup[] allGroups, int res) { -- if (allGroups == null) ++ int n; ++ if (allGroups == null || (n = allGroups.length) == 0) { return null; } -- for (int i = 0; i < allGroups.length; i++) ++ for (int i = 0; i < n; i++) { if ((allGroups[i].getStartRes() <= res) && (allGroups[i].getEndRes() >= res)) diff --cc src/jalview/renderer/seqfeatures/FeatureColourFinder.java index 7fce08b,7fce08b..8da880a --- a/src/jalview/renderer/seqfeatures/FeatureColourFinder.java +++ b/src/jalview/renderer/seqfeatures/FeatureColourFinder.java @@@ -117,13 -117,13 +117,48 @@@ public class FeatureColourFinde return c; } ++ public int findFeatureColourInt(int defaultColour, SequenceI seq, ++ int column) ++ { ++ // if (noFeaturesDisplayed()) ++ // { ++ // return defaultColour; ++ // } ++ ++ Graphics g = null; ++ ++ /* ++ * if transparency applies, provide a notional 1x1 graphics context ++ * that has been primed with the default colour ++ */ ++ if (featureRenderer.getTransparency() != 1f) ++ { ++ g = goff; ++ if (defaultColour != 0) ++ { ++ offscreenImage.setRGB(0, 0, defaultColour); ++ } ++ } ++ ++ Color c = featureRenderer.findFeatureColour(seq, column + 1, g); ++ if (c == null) ++ { ++ return defaultColour; ++ } ++ ++ if (g != null) ++ { ++ return offscreenImage.getRGB(0, 0); ++ } ++ return c.getRGB(); ++ } /** * Answers true if feature display is turned off, or there are no features * configured to be visible * * @return */ -- boolean noFeaturesDisplayed() ++ public boolean noFeaturesDisplayed() { if (featureRenderer == null || !featureRenderer.getViewport().isShowSequenceFeatures()) diff --cc src/jalview/viewmodel/OverviewDimensions.java index 3ac236d,3ac236d..8dc7dd8 --- a/src/jalview/viewmodel/OverviewDimensions.java +++ b/src/jalview/viewmodel/OverviewDimensions.java @@@ -304,10 -304,10 +304,10 @@@ public abstract class OverviewDimension boxY = Math.round(vpbox.y / heightRatio); // boxWidth is the width in residues translated to pixels -- boxWidth = Math.round(vpbox.width / widthRatio); ++ boxWidth = Math.max(1, Math.round(vpbox.width / widthRatio)); // boxHeight is the height in sequences translated to pixels -- boxHeight = Math.round(vpbox.height / heightRatio); ++ boxHeight = Math.max(1, Math.round(vpbox.height / heightRatio)); } /** diff --cc src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java index 6bf1f45,6bf1f45..5923e1f --- a/src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java +++ b/src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java @@@ -266,11 -266,11 +266,14 @@@ public abstract class FeatureRendererMo boolean findingFeatures = false; ++ int nup; ++ protected boolean updateFeatures() { if (av.getFeaturesDisplayed() == null || renderOrder == null || newFeatureAdded) { ++ System.out.println("updateFeatures " + ++nup); findAllFeatures(); if (av.getFeaturesDisplayed().getVisibleFeatureCount() < 1) {