From: gmungoc Date: Tue, 5 Mar 2019 11:20:44 +0000 (+0000) Subject: Merge remote-tracking branch 'origin/bug/JAL-3002fastPaintWrapped' into develop X-Git-Tag: Release_2_11_0~17^2~70 X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=a3ca1aac6c00fc2240fa21be9df43adcc1b964ff;hp=-c;p=jalview.git Merge remote-tracking branch 'origin/bug/JAL-3002fastPaintWrapped' into develop --- a3ca1aac6c00fc2240fa21be9df43adcc1b964ff diff --combined src/jalview/gui/SeqCanvas.java index 2a370c4,c89ba56..b3fcc54 --- a/src/jalview/gui/SeqCanvas.java +++ b/src/jalview/gui/SeqCanvas.java @@@ -32,6 -32,7 +32,6 @@@ import jalview.util.Comparison import jalview.viewmodel.ViewportListenerI; import jalview.viewmodel.ViewportRanges; -import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Color; @@@ -55,11 -56,6 +55,11 @@@ import javax.swing.JComponent */ public class SeqCanvas extends JComponent implements ViewportListenerI { + /* + * pixels gap between sequences and annotations when in wrapped mode + */ + static final int SEQS_ANNOTATION_GAP = 3; + private static final String ZEROS = "0000000000"; final FeatureRenderer fr; @@@ -87,9 -83,9 +87,9 @@@ private int labelWidthWest; // label left width in pixels if shown - private int wrappedSpaceAboveAlignment; // gap between widths + int wrappedSpaceAboveAlignment; // gap between widths - private int wrappedRepeatHeightPx; // height in pixels of wrapped width + int wrappedRepeatHeightPx; // height in pixels of wrapped width private int wrappedVisibleWidths; // number of wrapped widths displayed @@@ -369,27 -365,31 +369,27 @@@ width -= (width % charWidth); height -= (height % charHeight); - // selectImage is the selection group outline image - BufferedImage selectImage = drawSelectionGroup( - ranges.getStartRes(), ranges.getEndRes(), - ranges.getStartSeq(), ranges.getEndSeq()); - if ((img != null) && (fastPaint || (getVisibleRect().width != g.getClipBounds().width) || (getVisibleRect().height != g.getClipBounds().height))) { - BufferedImage lcimg = buildLocalImage(selectImage); - g.drawImage(lcimg, 0, 0, this); + g.drawImage(img, 0, 0, this); + + drawSelectionGroup((Graphics2D) g, ranges.getStartRes(), + ranges.getEndRes(), ranges.getStartSeq(), ranges.getEndSeq()); + fastPaint = false; } - else if ((width > 0) && (height > 0)) + else if (width > 0 && height > 0) { - // img is a cached version of the last view we drew, if any - // if we have no img or the size has changed, make a new one + /* + * img is a cached version of the last view we drew, if any + * if we have no img or the size has changed, make a new one + */ if (img == null || width != img.getWidth() || height != img.getHeight()) { - img = setupImage(); - if (img == null) - { - return; - } + img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); gg = (Graphics2D) img.getGraphics(); gg.setFont(av.getFont()); } @@@ -412,11 -412,11 +412,11 @@@ drawPanel(gg, ranges.getStartRes(), ranges.getEndRes(), ranges.getStartSeq(), ranges.getEndSeq(), 0); } - - // lcimg is a local *copy* of img which we'll draw selectImage on top of - BufferedImage lcimg = buildLocalImage(selectImage); - g.drawImage(lcimg, 0, 0, this); + drawSelectionGroup(gg, ranges.getStartRes(), + ranges.getEndRes(), ranges.getStartSeq(), ranges.getEndSeq()); + + g.drawImage(img, 0, 0, this); } if (av.cursorMode) @@@ -445,8 -445,14 +445,8 @@@ { drawPanel(g1, startRes, endRes, startSeq, endSeq, 0); - BufferedImage selectImage = drawSelectionGroup(startRes, endRes, + drawSelectionGroup((Graphics2D) g1, startRes, endRes, startSeq, endSeq); - if (selectImage != null) - { - ((Graphics2D) g1).setComposite(AlphaComposite - .getInstance(AlphaComposite.SRC_OVER)); - g1.drawImage(selectImage, 0, 0, this); - } } /** @@@ -464,14 -470,102 +464,14 @@@ public void drawWrappedPanelForPrinting(Graphics g, int canvasWidth, int canvasHeight, int startRes) { - SequenceGroup group = av.getSelectionGroup(); - drawWrappedPanel(g, canvasWidth, canvasHeight, startRes); + SequenceGroup group = av.getSelectionGroup(); if (group != null) { - BufferedImage selectImage = null; - try - { - selectImage = new BufferedImage(canvasWidth, canvasHeight, - BufferedImage.TYPE_INT_ARGB); // ARGB so alpha compositing works - } catch (OutOfMemoryError er) - { - System.gc(); - System.err.println("Print image OutOfMemory Error.\n" + er); - new OOMWarning("Creating wrapped alignment image for printing", er); - } - if (selectImage != null) - { - Graphics2D g2 = selectImage.createGraphics(); - setupSelectionGroup(g2, selectImage); - drawWrappedSelection(g2, group, canvasWidth, canvasHeight, + drawWrappedSelection((Graphics2D) g, group, canvasWidth, canvasHeight, startRes); - - g2.setComposite( - AlphaComposite.getInstance(AlphaComposite.SRC_OVER)); - g.drawImage(selectImage, 0, 0, this); - g2.dispose(); - } - } - } - - /* - * Make a local image by combining the cached image img - * with any selection - */ - private BufferedImage buildLocalImage(BufferedImage selectImage) - { - // clone the cached image - BufferedImage lcimg = new BufferedImage(img.getWidth(), img.getHeight(), - img.getType()); - - // BufferedImage lcimg = new BufferedImage(img.getWidth(), img.getHeight(), - // img.getType()); - Graphics2D g2d = lcimg.createGraphics(); - g2d.drawImage(img, 0, 0, null); - - // overlay selection group on lcimg - if (selectImage != null) - { - g2d.setComposite( - AlphaComposite.getInstance(AlphaComposite.SRC_OVER)); - g2d.drawImage(selectImage, 0, 0, this); } - - g2d.dispose(); - - return lcimg; - } - - /* - * Set up a buffered image of the correct height and size for the sequence canvas - */ - private BufferedImage setupImage() - { - BufferedImage lcimg = null; - - int charWidth = av.getCharWidth(); - int charHeight = av.getCharHeight(); - - int width = getWidth(); - int height = getHeight(); - - width -= (width % charWidth); - height -= (height % charHeight); - - if ((width < 1) || (height < 1)) - { - return null; - } - - try - { - lcimg = new BufferedImage(width, height, - BufferedImage.TYPE_INT_ARGB); // ARGB so alpha compositing works - } catch (OutOfMemoryError er) - { - System.gc(); - System.err.println( - "Group image OutOfMemory Redraw Error.\n" + er); - new OOMWarning("Creating alignment image for display", er); - - return null; - } - - return lcimg; } /** @@@ -558,13 -652,8 +558,13 @@@ ViewportRanges ranges = av.getRanges(); ranges.setViewportStartAndWidth(startColumn, wrappedWidthInResidues); + // we need to call this again to make sure the startColumn + + // wrappedWidthInResidues values are used to calculate wrappedVisibleWidths + // correctly. + calculateWrappedGeometry(canvasWidth, canvasHeight); + /* - * draw one width at a time (including any scales or annotation shown), + * draw one width at a time (excluding any scales shown), * until we have run out of either alignment or vertical space available */ int ypos = wrappedSpaceAboveAlignment; @@@ -611,22 -700,14 +611,22 @@@ * (av.getScaleAboveWrapped() ? 2 : 1); /* - * height in pixels of the wrapped widths + * compute height in pixels of the wrapped widths + * - start with space above plus sequences */ wrappedRepeatHeightPx = wrappedSpaceAboveAlignment; - // add sequences - wrappedRepeatHeightPx += av.getRanges().getViewportHeight() + wrappedRepeatHeightPx += av.getAlignment().getHeight() * charHeight; - // add annotations panel height if shown - wrappedRepeatHeightPx += getAnnotationHeight(); + + /* + * add annotations panel height if shown + * also gap between sequences and annotations + */ + if (av.isShowAnnotation()) + { + wrappedRepeatHeightPx += getAnnotationHeight(); + wrappedRepeatHeightPx += SEQS_ANNOTATION_GAP; // 3px + } /* * number of visible widths (the last one may be part height), @@@ -670,9 -751,8 +670,9 @@@ * @param endColumn * @param canvasHeight */ - protected void drawWrappedWidth(Graphics g, int ypos, int startColumn, - int endColumn, int canvasHeight) + protected void drawWrappedWidth(Graphics g, final int ypos, + final int startColumn, final int endColumn, + final int canvasHeight) { ViewportRanges ranges = av.getRanges(); int viewportWidth = ranges.getViewportWidth(); @@@ -719,8 -799,7 +719,8 @@@ if (av.isShowAnnotation()) { - g.translate(0, cHeight + ypos + 3); + final int yShift = cHeight + ypos + SEQS_ANNOTATION_GAP; + g.translate(0, yShift); if (annotations == null) { annotations = new AnnotationPanel(av); @@@ -728,7 -807,7 +728,7 @@@ annotations.renderer.drawComponent(annotations, av, g, -1, startColumn, endx + 1); - g.translate(0, -cHeight - ypos - 3); + g.translate(0, -yShift); } g.setClip(clip); g.translate(-xOffset, 0); @@@ -870,14 -949,16 +870,14 @@@ int startx = startRes; int endx; int ypos = hgap; // vertical offset - int maxwidth = av.getAlignment().getWidth(); - - if (av.hasHiddenColumns()) - { - maxwidth = av.getAlignment().getHiddenColumns() - .absoluteToVisibleColumn(maxwidth); - } + int maxwidth = av.getAlignment().getVisibleWidth(); // chop the wrapped alignment extent up into panel-sized blocks and treat // each block as if it were a block from an unwrapped alignment + g.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT, + BasicStroke.JOIN_ROUND, 3f, new float[] + { 5f, 3f }, 0f)); + g.setColor(Color.RED); while ((ypos <= canvasHeight) && (startx < maxwidth)) { // set end value to be start + width, or maxwidth, whichever is smaller @@@ -902,7 -983,6 +902,7 @@@ // update horizontal offset startx += cWidth; } + g.setStroke(new BasicStroke()); } int getAnnotationHeight() @@@ -1069,21 -1149,14 +1069,21 @@@ } + /** + * Draws the outlines of any groups defined on the alignment (excluding the + * current selection group, if any) + * + * @param g1 + * @param startRes + * @param endRes + * @param startSeq + * @param endSeq + * @param offset + */ void drawGroupsBoundaries(Graphics g1, int startRes, int endRes, int startSeq, int endSeq, int offset) { Graphics2D g = (Graphics2D) g1; - // - // /////////////////////////////////// - // Now outline any areas if necessary - // /////////////////////////////////// SequenceGroup group = null; int groupIndex = -1; @@@ -1096,15 -1169,18 +1096,15 @@@ if (group != null) { - g.setStroke(new BasicStroke()); - g.setColor(group.getOutlineColour()); - do { + g.setColor(group.getOutlineColour()); + drawPartialGroupOutline(g, group, startRes, endRes, startSeq, endSeq, offset); groupIndex++; - g.setStroke(new BasicStroke()); - if (groupIndex >= av.getAlignment().getGroups().size()) { break; @@@ -1118,28 -1194,33 +1118,28 @@@ } - - /* - * Draw the selection group as a separate image and overlay + /** + * Draws the outline of the current selection group (if any) + * + * @param g + * @param startRes + * @param endRes + * @param startSeq + * @param endSeq */ - private BufferedImage drawSelectionGroup(int startRes, int endRes, + private void drawSelectionGroup(Graphics2D g, int startRes, int endRes, int startSeq, int endSeq) { SequenceGroup group = av.getSelectionGroup(); if (group == null) { - // nothing to draw - return null; + return; } - // set up drawing colour - Graphics2D g = (Graphics2D) selectionImage.getGraphics(); - - setupSelectionGroup(g, selectionImage); - + g.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT, + BasicStroke.JOIN_ROUND, 3f, new float[] + { 5f, 3f }, 0f)); + g.setColor(Color.RED); if (!av.getWrapAlignment()) { drawUnwrappedSelection(g, group, startRes, endRes, startSeq, endSeq, @@@ -1150,7 -1231,9 +1150,7 @@@ drawWrappedSelection(g, group, getWidth(), getHeight(), av.getRanges().getStartRes()); } - - g.dispose(); - return selectionImage; + g.setStroke(new BasicStroke()); } /** @@@ -1241,23 -1324,33 +1241,23 @@@ } - /* - * Set up graphics for selection group - */ - private void setupSelectionGroup(Graphics2D g, - BufferedImage selectionImage) - { - // set background to transparent - g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f)); - g.fillRect(0, 0, selectionImage.getWidth(), selectionImage.getHeight()); - - // set up foreground to draw red dashed line - g.setComposite(AlphaComposite.Src); - g.setStroke(new BasicStroke(1, BasicStroke.CAP_BUTT, - BasicStroke.JOIN_ROUND, 3f, new float[] - { 5f, 3f }, 0f)); - g.setColor(Color.RED); - } - - /* + /** * Draw a selection group over an unwrapped alignment - * @param g graphics object to draw with - * @param group selection group - * @param startRes start residue of area to draw - * @param endRes end residue of area to draw - * @param startSeq start sequence of area to draw - * @param endSeq end sequence of area to draw - * @param offset vertical offset (used when called from wrapped alignment code) + * + * @param g + * graphics object to draw with + * @param group + * selection group + * @param startRes + * start residue of area to draw + * @param endRes + * end residue of area to draw + * @param startSeq + * start sequence of area to draw + * @param endSeq + * end sequence of area to draw + * @param offset + * vertical offset (used when called from wrapped alignment code) */ private void drawUnwrappedSelection(Graphics2D g, SequenceGroup group, int startRes, int endRes, int startSeq, int endSeq, int offset) @@@ -1295,16 -1388,8 +1295,16 @@@ } } - /* - * Draw the selection group as a separate image and overlay + /** + * Draws part of a selection group outline + * + * @param g + * @param group + * @param startRes + * @param endRes + * @param startSeq + * @param endSeq + * @param verticalOffset */ private void drawPartialGroupOutline(Graphics2D g, SequenceGroup group, int startRes, int endRes, int startSeq, int endSeq, @@@ -1655,7 -1740,7 +1655,7 @@@ } ViewportRanges vpRanges = av.getRanges(); - int range = vpRanges.getEndRes() - vpRanges.getStartRes(); + int range = vpRanges.getEndRes() - vpRanges.getStartRes() + 1; if (scrollX > range) { scrollX = range; @@@ -1724,10 -1809,10 +1724,10 @@@ { ViewportRanges ranges = av.getRanges(); - if (Math.abs(scrollX) > ranges.getViewportWidth()) + if (Math.abs(scrollX) >= ranges.getViewportWidth()) { /* - * shift of more than one view width is + * shift of one view width or more is * overcomplicated to handle in this method */ fastPaint = false; @@@ -1918,10 -2003,17 +1918,17 @@@ while (y >= 0) { + /* + * shift 'widthToCopy' residues by 'positions' places to the right + */ gg.copyArea(copyFromLeftStart, y, widthToCopy, heightToCopy, positions * charWidth, 0); if (y > 0) { + /* + * copy 'positions' residue from the row above (right hand end) + * to this row's left hand end + */ gg.copyArea(copyFromRightStart, y - wrappedRepeatHeightPx, positions * charWidth, heightToCopy, -widthToCopy, wrappedRepeatHeightPx);