Merge remote-tracking branch 'origin/bug/JAL-3002fastPaintWrapped' into develop
authorgmungoc <g.m.carstairs@dundee.ac.uk>
Tue, 5 Mar 2019 11:20:44 +0000 (11:20 +0000)
committergmungoc <g.m.carstairs@dundee.ac.uk>
Tue, 5 Mar 2019 11:20:44 +0000 (11:20 +0000)
1  2 
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
  
      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());
        }
          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)
    {
      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);
 -    }
    }
  
    /**
    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;
    }
  
    /**
      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;
              * (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),
     * @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();
  
      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);
  
        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);
      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
        // update horizontal offset
        startx += cWidth;
      }
 +    g.setStroke(new BasicStroke());
    }
  
    int getAnnotationHeight()
  
    }
  
 +  /**
 +   * 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;
  
      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;
  
    }
  
 -
 -  /*
 -   * 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,
        drawWrappedSelection(g, group, getWidth(), getHeight(),
                av.getRanges().getStartRes());
      }
 -
 -    g.dispose();
 -    return selectionImage;
 +    g.setStroke(new BasicStroke());
    }
  
    /**
    }
  
  
 -  /*
 -   * 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)
      }
    }
  
 -  /*
 -   * 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,
        }
        ViewportRanges vpRanges = av.getRanges();
  
-       int range = vpRanges.getEndRes() - vpRanges.getStartRes();
+       int range = vpRanges.getEndRes() - vpRanges.getStartRes() + 1;
        if (scrollX > range)
        {
          scrollX = range;
    {
      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;
  
        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);