2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
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.
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.
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.
21 package jalview.renderer.seqfeatures;
23 import jalview.api.AlignViewportI;
24 import jalview.datamodel.SequenceFeature;
25 import jalview.datamodel.SequenceI;
26 import jalview.viewmodel.seqfeatures.FeatureRendererModel;
28 import java.awt.AlphaComposite;
29 import java.awt.Color;
30 import java.awt.FontMetrics;
31 import java.awt.Graphics;
32 import java.awt.Graphics2D;
33 import java.awt.image.BufferedImage;
34 import java.util.List;
36 public class FeatureRenderer extends FeatureRendererModel
43 boolean offscreenRender = false;
45 protected SequenceI lastSeq;
51 int av_charHeight, av_charWidth;
53 boolean av_validCharWidth, av_isShowSeqFeatureHeight;
55 private Integer currentColour;
58 * Constructor given a viewport
62 public FeatureRenderer(AlignViewportI viewport)
67 protected void updateAvConfig()
69 av_charHeight = av.getCharHeight();
70 av_charWidth = av.getCharWidth();
71 av_validCharWidth = av.isValidCharWidth();
72 av_isShowSeqFeatureHeight = av.isShowSequenceFeaturesHeight();
75 void renderFeature(Graphics g, SequenceI seq, int fstart, int fend,
76 Color featureColour, int start, int end, int y1)
79 if (((fstart <= end) && (fend >= start)))
82 { // fix for if the feature we have starts before the sequence start,
83 fstart = start; // but the feature end is still valid!!
90 int pady = (y1 + av_charHeight) - av_charHeight / 5;
91 for (i = fstart; i <= fend; i++)
95 if (jalview.util.Comparison.isGap(s))
100 g.setColor(featureColour);
102 g.fillRect((i - start) * av_charWidth, y1, av_charWidth,
105 if (offscreenRender || !av_validCharWidth)
110 g.setColor(Color.white);
111 charOffset = (av_charWidth - fm.charWidth(s)) / 2;
112 g.drawString(String.valueOf(s), charOffset
113 + (av_charWidth * (i - start)), pady);
119 void renderScoreFeature(Graphics g, SequenceI seq, int fstart, int fend,
120 Color featureColour, int start, int end, int y1, byte[] bs)
123 if (((fstart <= end) && (fend >= start)))
126 { // fix for if the feature we have starts before the sequence start,
127 fstart = start; // but the feature end is still valid!!
134 int pady = (y1 + av_charHeight) - av_charHeight / 5;
135 int ystrt = 0, yend = av_charHeight;
138 // signed - zero is always middle of residue line.
141 yend = av_charHeight * (128 - bs[1]) / 512;
142 ystrt = av_charHeight - yend / 2;
146 ystrt = av_charHeight / 2;
147 yend = av_charHeight * (bs[1] - 128) / 512;
152 yend = av_charHeight * bs[1] / 255;
153 ystrt = av_charHeight - yend;
156 for (i = fstart; i <= fend; i++)
158 s = seq.getCharAt(i);
160 if (jalview.util.Comparison.isGap(s))
165 g.setColor(featureColour);
166 int x = (i - start) * av_charWidth;
167 g.drawRect(x, y1, av_charWidth, av_charHeight);
168 g.fillRect(x, y1 + ystrt, av_charWidth, yend);
170 if (offscreenRender || !av_validCharWidth)
175 g.setColor(Color.black);
176 charOffset = (av_charWidth - fm.charWidth(s)) / 2;
177 g.drawString(String.valueOf(s), charOffset
178 + (av_charWidth * (i - start)), pady);
183 BufferedImage offscreenImage;
186 public Color findFeatureColour(Color initialCol, SequenceI seq, int res)
188 return new Color(findFeatureColour(initialCol.getRGB(), seq, res));
192 * This is used by Structure Viewers and the Overview Window to get the
193 * feature colour of the rendered sequence, returned as an RGB value
195 * @param defaultColour
200 public synchronized int findFeatureColour(int defaultColour,
201 final SequenceI seq, int column)
203 if (!av.isShowSequenceFeatures())
205 return defaultColour;
208 SequenceFeature[] sequenceFeatures = seq.getSequenceFeatures();
212 lastSequenceFeatures = sequenceFeatures;
213 if (lastSequenceFeatures != null)
215 sfSize = lastSequenceFeatures.length;
220 if (lastSequenceFeatures != sequenceFeatures)
222 lastSequenceFeatures = sequenceFeatures;
223 if (lastSequenceFeatures != null)
225 sfSize = lastSequenceFeatures.length;
230 if (lastSequenceFeatures == null || sfSize == 0)
232 return defaultColour;
235 if (jalview.util.Comparison.isGap(lastSeq.getCharAt(column)))
237 return Color.white.getRGB();
240 // Only bother making an offscreen image if transparency is applied
241 if (transparency != 1.0f && offscreenImage == null)
243 offscreenImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
246 currentColour = null;
247 // TODO: non-threadsafe - each rendering thread needs its own instance of
248 // the feature renderer - or this should be synchronized.
249 offscreenRender = true;
251 if (offscreenImage != null)
253 offscreenImage.setRGB(0, 0, defaultColour);
254 drawSequence(offscreenImage.getGraphics(), lastSeq, column, column, 0);
256 return offscreenImage.getRGB(0, 0);
260 drawSequence(null, lastSeq, lastSeq.findPosition(column), -1, -1);
262 if (currentColour == null)
264 return defaultColour;
268 return currentColour.intValue();
274 private volatile SequenceFeature[] lastSequenceFeatures;
285 * Draws the sequence on the graphics context, or just determines the colour
286 * that would be drawn (if flag offscreenrender is true).
291 * start column (or sequence position in offscreenrender mode)
293 * end column (not used in offscreenrender mode)
295 * vertical offset at which to draw on the graphics
297 public synchronized void drawSequence(Graphics g, final SequenceI seq,
298 int start, int end, int y1)
300 SequenceFeature[] sequenceFeatures = seq.getSequenceFeatures();
301 if (sequenceFeatures == null || sequenceFeatures.length == 0)
308 fm = g.getFontMetrics();
313 if (lastSeq == null || seq != lastSeq
314 || sequenceFeatures != lastSequenceFeatures)
317 lastSequenceFeatures = sequenceFeatures;
320 if (transparency != 1 && g != null)
322 Graphics2D g2 = (Graphics2D) g;
323 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
327 if (!offscreenRender)
329 spos = lastSeq.findPosition(start);
330 epos = lastSeq.findPosition(end);
333 sfSize = lastSequenceFeatures.length;
334 for (int renderIndex = 0; renderIndex < renderOrder.length; renderIndex++)
336 String type = renderOrder[renderIndex];
337 if (!showFeatureOfType(type))
342 // loop through all features in sequence to find
343 // current feature to render
344 // for (sfindex = 0; sfindex < sfSize; sfindex++)
346 // final SequenceFeature sequenceFeature = lastSequenceFeatures[sfindex];
347 int from = offscreenRender ? start : spos;
348 int to = offscreenRender ? start : epos;
349 List<SequenceFeature> overlaps = seq.findFeatures(type, from, to);
350 for (SequenceFeature sequenceFeature : overlaps)
352 if (!sequenceFeature.type.equals(type))
357 if (featureGroupNotShown(sequenceFeature))
363 * check feature overlaps the visible part of the alignment,
364 * unless doing offscreenRender (to the Overview window or a
365 * structure viewer) which is not limited
368 && (sequenceFeature.getBegin() > epos || sequenceFeature
374 Color featureColour = getColour(sequenceFeature);
375 boolean isContactFeature = sequenceFeature.isContactFeature();
377 if (offscreenRender && offscreenImage == null)
380 * offscreen mode with no image (image is only needed if transparency
381 * is applied to feature colours) - just check feature is rendered at
382 * the requested position (start == sequence position in this mode)
384 boolean featureIsAtPosition = sequenceFeature.begin <= start
385 && sequenceFeature.end >= start;
386 if (isContactFeature)
388 featureIsAtPosition = sequenceFeature.begin == start
389 || sequenceFeature.end == start;
391 if (featureIsAtPosition)
393 // this is passed out to the overview and other sequence renderers
394 // (e.g. molecule viewer) to get displayed colour for rendered
396 currentColour = new Integer(featureColour.getRGB());
397 // used to be retreived from av.featuresDisplayed
398 // currentColour = av.featuresDisplayed
399 // .get(sequenceFeatures[sfindex].type);
403 else if (isContactFeature)
405 renderFeature(g, seq, seq.findIndex(sequenceFeature.begin) - 1,
406 seq.findIndex(sequenceFeature.begin) - 1, featureColour,
408 renderFeature(g, seq, seq.findIndex(sequenceFeature.end) - 1,
409 seq.findIndex(sequenceFeature.end) - 1, featureColour,
413 else if (showFeature(sequenceFeature))
415 if (av_isShowSeqFeatureHeight
416 && !Float.isNaN(sequenceFeature.score))
418 renderScoreFeature(g, seq,
419 seq.findIndex(sequenceFeature.begin) - 1,
420 seq.findIndex(sequenceFeature.end) - 1,
421 featureColour, start, end, y1,
422 normaliseScore(sequenceFeature));
426 renderFeature(g, seq, seq.findIndex(sequenceFeature.begin) - 1,
427 seq.findIndex(sequenceFeature.end) - 1,
428 featureColour, start, end, y1);
434 if (transparency != 1.0f && g != null)
436 Graphics2D g2 = (Graphics2D) g;
437 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
443 * Answers true if the feature belongs to a feature group which is not
444 * currently displayed, else false
446 * @param sequenceFeature
449 protected boolean featureGroupNotShown(
450 final SequenceFeature sequenceFeature)
452 return featureGroups != null
453 && sequenceFeature.featureGroup != null
454 && sequenceFeature.featureGroup.length() != 0
455 && featureGroups.containsKey(sequenceFeature.featureGroup)
456 && !featureGroups.get(sequenceFeature.featureGroup)
461 * Called when alignment in associated view has new/modified features to
462 * discover and display.
466 public void featuresAdded()