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;
35 public class FeatureRenderer extends FeatureRendererModel
42 boolean offscreenRender = false;
44 protected SequenceI lastSeq;
50 int av_charHeight, av_charWidth;
52 boolean av_validCharWidth, av_isShowSeqFeatureHeight;
54 private Integer currentColour;
57 * Constructor given a viewport
61 public FeatureRenderer(AlignViewportI viewport)
66 protected void updateAvConfig()
68 av_charHeight = av.getCharHeight();
69 av_charWidth = av.getCharWidth();
70 av_validCharWidth = av.isValidCharWidth();
71 av_isShowSeqFeatureHeight = av.isShowSequenceFeaturesHeight();
74 void renderFeature(Graphics g, SequenceI seq, int fstart, int fend,
75 Color featureColour, int start, int end, int y1)
78 if (((fstart <= end) && (fend >= start)))
81 { // fix for if the feature we have starts before the sequence start,
82 fstart = start; // but the feature end is still valid!!
89 int pady = (y1 + av_charHeight) - av_charHeight / 5;
90 for (i = fstart; i <= fend; i++)
94 if (jalview.util.Comparison.isGap(s))
99 g.setColor(featureColour);
101 g.fillRect((i - start) * av_charWidth, y1, av_charWidth,
104 if (offscreenRender || !av_validCharWidth)
109 g.setColor(Color.white);
110 charOffset = (av_charWidth - fm.charWidth(s)) / 2;
111 g.drawString(String.valueOf(s), charOffset
112 + (av_charWidth * (i - start)), pady);
118 void renderScoreFeature(Graphics g, SequenceI seq, int fstart, int fend,
119 Color featureColour, int start, int end, int y1, byte[] bs)
122 if (((fstart <= end) && (fend >= start)))
125 { // fix for if the feature we have starts before the sequence start,
126 fstart = start; // but the feature end is still valid!!
133 int pady = (y1 + av_charHeight) - av_charHeight / 5;
134 int ystrt = 0, yend = av_charHeight;
137 // signed - zero is always middle of residue line.
140 yend = av_charHeight * (128 - bs[1]) / 512;
141 ystrt = av_charHeight - yend / 2;
145 ystrt = av_charHeight / 2;
146 yend = av_charHeight * (bs[1] - 128) / 512;
151 yend = av_charHeight * bs[1] / 255;
152 ystrt = av_charHeight - yend;
155 for (i = fstart; i <= fend; i++)
157 s = seq.getCharAt(i);
159 if (jalview.util.Comparison.isGap(s))
164 g.setColor(featureColour);
165 int x = (i - start) * av_charWidth;
166 g.drawRect(x, y1, av_charWidth, av_charHeight);
167 g.fillRect(x, y1 + ystrt, av_charWidth, yend);
169 if (offscreenRender || !av_validCharWidth)
174 g.setColor(Color.black);
175 charOffset = (av_charWidth - fm.charWidth(s)) / 2;
176 g.drawString(String.valueOf(s), charOffset
177 + (av_charWidth * (i - start)), pady);
182 BufferedImage offscreenImage;
185 public Color findFeatureColour(Color initialCol, SequenceI seq, int res)
187 return new Color(findFeatureColour(initialCol.getRGB(), seq, res));
191 * This is used by Structure Viewers and the Overview Window to get the
192 * feature colour of the rendered sequence, returned as an RGB value
194 * @param defaultColour
199 public synchronized int findFeatureColour(int defaultColour,
200 final SequenceI seq, int column)
202 if (!av.isShowSequenceFeatures())
204 return defaultColour;
207 SequenceFeature[] sequenceFeatures = seq.getSequenceFeatures();
211 lastSequenceFeatures = sequenceFeatures;
212 if (lastSequenceFeatures != null)
214 sfSize = lastSequenceFeatures.length;
219 if (lastSequenceFeatures != sequenceFeatures)
221 lastSequenceFeatures = sequenceFeatures;
222 if (lastSequenceFeatures != null)
224 sfSize = lastSequenceFeatures.length;
229 if (lastSequenceFeatures == null || sfSize == 0)
231 return defaultColour;
234 if (jalview.util.Comparison.isGap(lastSeq.getCharAt(column)))
236 return Color.white.getRGB();
239 // Only bother making an offscreen image if transparency is applied
240 if (transparency != 1.0f && offscreenImage == null)
242 offscreenImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
245 currentColour = null;
246 // TODO: non-threadsafe - each rendering thread needs its own instance of
247 // the feature renderer - or this should be synchronized.
248 offscreenRender = true;
250 if (offscreenImage != null)
252 offscreenImage.setRGB(0, 0, defaultColour);
253 drawSequence(offscreenImage.getGraphics(), lastSeq, column, column, 0);
255 return offscreenImage.getRGB(0, 0);
259 drawSequence(null, lastSeq, lastSeq.findPosition(column), -1, -1);
261 if (currentColour == null)
263 return defaultColour;
267 return currentColour.intValue();
273 private volatile SequenceFeature[] lastSequenceFeatures;
284 * Draws the sequence on the graphics context, or just determines the colour
285 * that would be drawn (if flag offscreenrender is true).
290 * start column (or sequence position in offscreenrender mode)
292 * end column (not used in offscreenrender mode)
294 * vertical offset at which to draw on the graphics
296 public synchronized void drawSequence(Graphics g, final SequenceI seq,
297 int start, int end, int y1)
299 SequenceFeature[] sequenceFeatures = seq.getSequenceFeatures();
300 if (sequenceFeatures == null || sequenceFeatures.length == 0)
307 fm = g.getFontMetrics();
312 if (lastSeq == null || seq != lastSeq
313 || sequenceFeatures != lastSequenceFeatures)
316 lastSequenceFeatures = sequenceFeatures;
319 if (transparency != 1 && g != null)
321 Graphics2D g2 = (Graphics2D) g;
322 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
326 if (!offscreenRender)
328 spos = lastSeq.findPosition(start);
329 epos = lastSeq.findPosition(end);
332 sfSize = lastSequenceFeatures.length;
333 for (int renderIndex = 0; renderIndex < renderOrder.length; renderIndex++)
335 String type = renderOrder[renderIndex];
336 if (!showFeatureOfType(type))
341 // loop through all features in sequence to find
342 // current feature to render
343 for (sfindex = 0; sfindex < sfSize; sfindex++)
345 final SequenceFeature sequenceFeature = lastSequenceFeatures[sfindex];
346 if (!sequenceFeature.type.equals(type))
351 if (featureGroupNotShown(sequenceFeature))
357 * check feature overlaps the visible part of the alignment,
358 * unless doing offscreenRender (to the Overview window or a
359 * structure viewer) which is not limited
362 && (sequenceFeature.getBegin() > epos || sequenceFeature
368 Color featureColour = getColour(sequenceFeature);
369 boolean isContactFeature = sequenceFeature.isContactFeature();
371 if (offscreenRender && offscreenImage == null)
374 * offscreen mode with no image (image is only needed if transparency
375 * is applied to feature colours) - just check feature is rendered at
376 * the requested position (start == sequence position in this mode)
378 boolean featureIsAtPosition = sequenceFeature.begin <= start
379 && sequenceFeature.end >= start;
380 if (isContactFeature)
382 featureIsAtPosition = sequenceFeature.begin == start
383 || sequenceFeature.end == start;
385 if (featureIsAtPosition)
387 // this is passed out to the overview and other sequence renderers
388 // (e.g. molecule viewer) to get displayed colour for rendered
390 currentColour = new Integer(featureColour.getRGB());
391 // used to be retreived from av.featuresDisplayed
392 // currentColour = av.featuresDisplayed
393 // .get(sequenceFeatures[sfindex].type);
397 else if (isContactFeature)
399 renderFeature(g, seq, seq.findIndex(sequenceFeature.begin) - 1,
400 seq.findIndex(sequenceFeature.begin) - 1, featureColour,
402 renderFeature(g, seq, seq.findIndex(sequenceFeature.end) - 1,
403 seq.findIndex(sequenceFeature.end) - 1, featureColour,
407 else if (showFeature(sequenceFeature))
409 if (av_isShowSeqFeatureHeight
410 && !Float.isNaN(sequenceFeature.score))
412 renderScoreFeature(g, seq,
413 seq.findIndex(sequenceFeature.begin) - 1,
414 seq.findIndex(sequenceFeature.end) - 1,
415 featureColour, start, end, y1,
416 normaliseScore(sequenceFeature));
420 renderFeature(g, seq, seq.findIndex(sequenceFeature.begin) - 1,
421 seq.findIndex(sequenceFeature.end) - 1,
422 featureColour, start, end, y1);
428 if (transparency != 1.0f && g != null)
430 Graphics2D g2 = (Graphics2D) g;
431 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
437 * Answers true if the feature belongs to a feature group which is not
438 * currently displayed, else false
440 * @param sequenceFeature
443 protected boolean featureGroupNotShown(
444 final SequenceFeature sequenceFeature)
446 return featureGroups != null
447 && sequenceFeature.featureGroup != null
448 && sequenceFeature.featureGroup.length() != 0
449 && featureGroups.containsKey(sequenceFeature.featureGroup)
450 && !featureGroups.get(sequenceFeature.featureGroup)
455 * Called when alignment in associated view has new/modified features to
456 * discover and display.
460 public void featuresAdded()