Clover icon

Coverage Report

  1. Project Clover database Mon Nov 18 2024 09:56:54 GMT
  2. Package jalview.renderer

File OverviewRenderer.java

 

Coverage histogram

../../img/srcFileCovDistChart7.png
29% of files have more coverage

Code metrics

40
116
10
1
441
267
32
0.28
11.6
10
3.2

Classes

Class Line # Actions
OverviewRenderer 42 116 32
0.680722968.1%
 

Contributing tests

This file is covered by 10 tests. .

Source view

1    /*
2    * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3    * Copyright (C) $$Year-Rel$$ The Jalview Authors
4    *
5    * This file is part of Jalview.
6    *
7    * Jalview is free software: you can redistribute it and/or
8    * modify it under the terms of the GNU General Public License
9    * as published by the Free Software Foundation, either version 3
10    * of the License, or (at your option) any later version.
11    *
12    * Jalview is distributed in the hope that it will be useful, but
13    * WITHOUT ANY WARRANTY; without even the implied warranty
14    * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15    * PURPOSE. See the GNU General Public License for more details.
16    *
17    * You should have received a copy of the GNU General Public License
18    * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19    * The Jalview Authors are detailed in the 'AUTHORS' file.
20    */
21    package jalview.renderer;
22   
23    import jalview.api.AlignmentColsCollectionI;
24    import jalview.api.AlignmentRowsCollectionI;
25    import jalview.api.RendererListenerI;
26    import jalview.datamodel.AlignmentAnnotation;
27    import jalview.datamodel.AlignmentI;
28    import jalview.datamodel.Annotation;
29    import jalview.datamodel.SequenceGroup;
30    import jalview.datamodel.SequenceI;
31    import jalview.renderer.seqfeatures.FeatureColourFinder;
32    import jalview.renderer.seqfeatures.FeatureRenderer;
33    import jalview.viewmodel.OverviewDimensions;
34   
35    import java.awt.AlphaComposite;
36    import java.awt.Color;
37    import java.awt.Graphics;
38    import java.awt.Graphics2D;
39    import java.awt.image.BufferedImage;
40    import java.beans.PropertyChangeSupport;
41   
 
42    public class OverviewRenderer
43    {
44    // transparency of hidden cols/seqs overlay
45    private final float TRANSPARENCY = 0.5f;
46   
47    public static final String UPDATE = "OverviewUpdate";
48   
49    private static final int MAX_PROGRESS = 100;
50   
51    private PropertyChangeSupport changeSupport = new PropertyChangeSupport(
52    this);
53   
54    private FeatureColourFinder finder;
55   
56    // image to render on
57    private BufferedImage miniMe;
58   
59    // raw number of pixels to allocate to each column
60    private float pixelsPerCol;
61   
62    // raw number of pixels to allocate to each row
63    private float pixelsPerSeq;
64   
65    // height in pixels of graph
66    private int graphHeight;
67   
68    // flag to indicate whether to halt drawing
69    private volatile boolean redraw = false;
70   
71    // reference to alignment, needed to get sequence groups
72    private AlignmentI al;
73   
74    private ResidueShaderI shader;
75   
76    private OverviewResColourFinder resColFinder;
77   
 
78  245 toggle public OverviewRenderer(FeatureRenderer fr, OverviewDimensions od,
79    AlignmentI alignment, ResidueShaderI resshader,
80    OverviewResColourFinder colFinder)
81    {
82  245 finder = new FeatureColourFinder(fr);
83  245 resColFinder = colFinder;
84   
85  245 al = alignment;
86  245 shader = resshader;
87   
88  245 pixelsPerCol = od.getPixelsPerCol();
89  245 pixelsPerSeq = od.getPixelsPerSeq();
90  245 graphHeight = od.getGraphHeight();
91  245 miniMe = new BufferedImage(od.getWidth(), od.getHeight(),
92    BufferedImage.TYPE_INT_RGB);
93    }
94   
95    /**
96    * Draw alignment rows and columns onto an image
97    *
98    * @param rit
99    * Iterator over rows to be drawn
100    * @param cit
101    * Iterator over columns to be drawn
102    * @return image containing the drawing
103    */
 
104  245 toggle public BufferedImage draw(AlignmentRowsCollectionI rows,
105    AlignmentColsCollectionI cols)
106    {
107  245 int rgbcolor = Color.white.getRGB();
108  245 int seqIndex = 0;
109  245 int pixelRow = 0;
110  245 int alignmentHeight = miniMe.getHeight() - graphHeight;
111  245 int totalPixels = miniMe.getWidth() * alignmentHeight;
112   
113  245 int lastRowUpdate = 0;
114  245 int lastUpdate = 0;
115  245 changeSupport.firePropertyChange(UPDATE, -1, 0);
116   
117  245 for (int alignmentRow : rows)
118    {
119  1963 if (redraw)
120    {
121  99 break;
122    }
123   
124    // get details of this alignment row
125  1864 SequenceI seq = rows.getSequence(alignmentRow);
126   
127    // rate limiting step when rendering overview for lots of groups
128  1864 SequenceGroup[] allGroups = al.findAllGroups(seq);
129   
130    // calculate where this row extends to in pixels
131  1864 int endRow = Math.min(Math.round((seqIndex + 1) * pixelsPerSeq) - 1,
132    miniMe.getHeight() - 1);
133   
134  1864 int colIndex = 0;
135  1864 int pixelCol = 0;
136  1864 for (int alignmentCol : cols)
137    {
138  266461 if (redraw)
139    {
140  81 break;
141    }
142   
143    // calculate where this column extends to in pixels
144  266373 int endCol = Math.min(Math.round((colIndex + 1) * pixelsPerCol) - 1,
145    miniMe.getWidth() - 1);
146   
147    // don't do expensive colour determination if we're not going to use it
148    // NB this is important to avoid performance issues in the overview
149    // panel
150  266340 if (pixelCol <= endCol)
151    {
152  266321 rgbcolor = getColumnColourFromSequence(allGroups, seq,
153    alignmentCol);
154   
155    // fill in the appropriate number of pixels
156  1074024 for (int row = pixelRow; row <= endRow; ++row)
157    {
158  2646089 for (int col = pixelCol; col <= endCol; ++col)
159    {
160  1837577 miniMe.setRGB(col, row, rgbcolor);
161    }
162    }
163   
164    // store last update value
165  266246 lastUpdate = sendProgressUpdate(
166    (pixelCol + 1) * (endRow - pixelRow), totalPixels,
167    lastRowUpdate, lastUpdate);
168   
169  266312 pixelCol = endCol + 1;
170    }
171  266321 colIndex++;
172    }
173   
174  1864 if (pixelRow != endRow + 1)
175    {
176    // store row offset and last update value
177  1864 lastRowUpdate = sendProgressUpdate(endRow + 1, alignmentHeight, 0,
178    lastUpdate);
179  1864 lastUpdate = lastRowUpdate;
180  1864 pixelRow = endRow + 1;
181    }
182  1864 seqIndex++;
183    }
184   
185  245 overlayHiddenRegions(rows, cols);
186    // final update to progress bar if present
187  245 if (redraw)
188    {
189  122 sendProgressUpdate(pixelRow - 1, alignmentHeight, 0, 0);
190    }
191    else
192    {
193  123 sendProgressUpdate(alignmentHeight, miniMe.getHeight(), 0, 0);
194    }
195  245 return miniMe;
196    }
197   
198    /*
199    * Calculate progress update value and fire event
200    * @param rowOffset number of rows to offset calculation by
201    * @return new rowOffset - return value only to be used when at end of a row
202    */
 
203  268370 toggle private int sendProgressUpdate(int position, int maximum, int rowOffset,
204    int lastUpdate)
205    {
206  268380 int newUpdate = rowOffset
207    + Math.round(MAX_PROGRESS * ((float) position / maximum));
208  268443 if (newUpdate > lastUpdate)
209    {
210  10298 changeSupport.firePropertyChange(UPDATE, rowOffset, newUpdate);
211  10298 return newUpdate;
212    }
213  258171 return newUpdate;
214    }
215   
216    /*
217    * Find the RGB value of the colour of a sequence at a specified column position
218    *
219    * @param seq
220    * sequence to get colour for
221    * @param lastcol
222    * column position to get colour for
223    * @return colour of sequence at this position, as RGB
224    */
 
225  266327 toggle int getColumnColourFromSequence(SequenceGroup[] allGroups, SequenceI seq,
226    int lastcol)
227    {
228  266333 Color color = resColFinder.gapColour;
229   
230  266360 if ((seq != null) && (seq.getLength() > lastcol))
231    {
232  263924 color = resColFinder.getResidueColour(true, shader, allGroups, seq,
233    lastcol, finder);
234    }
235   
236  266331 return color.getRGB();
237    }
238   
239    /**
240    * Overlay the hidden regions on the overview image
241    *
242    * @param rows
243    * collection of rows the overview is built over
244    * @param cols
245    * collection of columns the overview is built over
246    */
 
247  245 toggle private void overlayHiddenRegions(AlignmentRowsCollectionI rows,
248    AlignmentColsCollectionI cols)
249    {
250  245 if (cols.hasHidden() || rows.hasHidden())
251    {
252  0 BufferedImage mask = buildHiddenImage(rows, cols, miniMe.getWidth(),
253    miniMe.getHeight());
254   
255  0 Graphics2D g = (Graphics2D) miniMe.getGraphics();
256  0 g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
257    TRANSPARENCY));
258  0 g.drawImage(mask, 0, 0, miniMe.getWidth(), miniMe.getHeight(), null);
259    }
260    }
261   
262    /**
263    * Build a masking image of hidden columns and rows to be applied on top of
264    * the main overview image.
265    *
266    * @param rows
267    * collection of rows the overview is built over
268    * @param cols
269    * collection of columns the overview is built over
270    * @param width
271    * width of overview in pixels
272    * @param height
273    * height of overview in pixels
274    * @return BufferedImage containing mask of hidden regions
275    */
 
276  0 toggle private BufferedImage buildHiddenImage(AlignmentRowsCollectionI rows,
277    AlignmentColsCollectionI cols, int width, int height)
278    {
279    // new masking image
280  0 BufferedImage hiddenImage = new BufferedImage(width, height,
281    BufferedImage.TYPE_INT_ARGB);
282   
283  0 int colIndex = 0;
284  0 int pixelCol = 0;
285   
286  0 Color hidden = resColFinder.getHiddenColour();
287   
288  0 Graphics2D g2d = (Graphics2D) hiddenImage.getGraphics();
289   
290    // set background to transparent
291    // g2d.setComposite(AlphaComposite.Clear);
292    // g2d.fillRect(0, 0, width, height);
293   
294    // set next colour to opaque
295  0 g2d.setComposite(AlphaComposite.Src);
296   
297  0 for (int alignmentCol : cols)
298    {
299  0 if (redraw)
300    {
301  0 break;
302    }
303   
304    // calculate where this column extends to in pixels
305  0 int endCol = Math.min(Math.round((colIndex + 1) * pixelsPerCol) - 1,
306    hiddenImage.getWidth() - 1);
307   
308  0 if (pixelCol <= endCol)
309    {
310    // determine the colour based on the sequence and column position
311  0 if (cols.isHidden(alignmentCol))
312    {
313  0 g2d.setColor(hidden);
314  0 g2d.fillRect(pixelCol, 0, endCol - pixelCol + 1, height);
315    }
316   
317  0 pixelCol = endCol + 1;
318    }
319  0 colIndex++;
320   
321    }
322   
323  0 int seqIndex = 0;
324  0 int pixelRow = 0;
325  0 for (int alignmentRow : rows)
326    {
327  0 if (redraw)
328    {
329  0 break;
330    }
331   
332    // calculate where this row extends to in pixels
333  0 int endRow = Math.min(Math.round((seqIndex + 1) * pixelsPerSeq) - 1,
334    miniMe.getHeight() - 1);
335   
336    // get details of this alignment row
337  0 if (rows.isHidden(alignmentRow))
338    {
339  0 g2d.setColor(hidden);
340  0 g2d.fillRect(0, pixelRow, width, endRow - pixelRow + 1);
341    }
342  0 pixelRow = endRow + 1;
343  0 seqIndex++;
344    }
345   
346  0 return hiddenImage;
347    }
348   
349    /**
350    * Draw the alignment annotation in the overview panel
351    *
352    * @param g
353    * the graphics object to draw on
354    * @param anno
355    * alignment annotation information
356    * @param y
357    * y-position for the annotation graph
358    * @param cols
359    * the collection of columns used in the overview panel
360    */
 
361  236 toggle public void drawGraph(Graphics g, AlignmentAnnotation anno, int y,
362    AlignmentColsCollectionI cols)
363    {
364  236 Annotation[] annotations = anno.annotations;
365  236 g.setColor(Color.white);
366  236 g.fillRect(0, 0, miniMe.getWidth(), y);
367   
368  236 int height;
369  236 int colIndex = 0;
370  236 int pixelCol = 0;
371  236 for (int alignmentCol : cols)
372    {
373  16822 if (redraw)
374    {
375  119 changeSupport.firePropertyChange(UPDATE, MAX_PROGRESS - 1, 0);
376  119 break;
377    }
378   
379  16703 if (alignmentCol >= annotations.length)
380    {
381  0 break; // no more annotations to draw here
382    }
383    else
384    {
385  16703 int endCol = Math.min(Math.round((colIndex + 1) * pixelsPerCol) - 1,
386    miniMe.getWidth() - 1);
387   
388  16703 if (annotations[alignmentCol] != null)
389    {
390  16703 if (annotations[alignmentCol].colour == null)
391    {
392  0 g.setColor(Color.black);
393    }
394    else
395    {
396  16703 g.setColor(annotations[alignmentCol].colour);
397    }
398   
399  16703 height = (int) ((annotations[alignmentCol].value / anno.graphMax)
400    * y);
401  16703 if (height > y)
402    {
403  0 height = y;
404    }
405   
406  16703 g.fillRect(pixelCol, y - height, endCol - pixelCol + 1, height);
407    }
408   
409  16703 pixelCol = endCol + 1;
410  16703 colIndex++;
411    }
412    }
413  236 changeSupport.firePropertyChange(UPDATE, MAX_PROGRESS - 1,
414    MAX_PROGRESS);
415    }
416   
417    /**
418    * Allows redraw flag to be set
419    *
420    * @param b
421    * value to set redraw to: true = redraw is occurring, false = no
422    * redraw
423    */
 
424  205 toggle public void setRedraw(boolean b)
425    {
426  205 synchronized (this)
427    {
428  205 redraw = b;
429    }
430    }
431   
 
432  245 toggle public void addPropertyChangeListener(RendererListenerI listener)
433    {
434  245 changeSupport.addPropertyChangeListener(listener);
435    }
436   
 
437  236 toggle public void removePropertyChangeListener(RendererListenerI listener)
438    {
439  236 changeSupport.removePropertyChangeListener(listener);
440    }
441    }