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.api.ColorI;
25 import jalview.datamodel.SequenceFeature;
26 import jalview.datamodel.SequenceI;
27 import jalview.schemes.Colour;
28 import jalview.viewmodel.seqfeatures.FeatureRendererModel;
30 import java.awt.AlphaComposite;
31 import java.awt.Color;
32 import java.awt.FontMetrics;
33 import java.awt.Graphics;
34 import java.awt.Graphics2D;
35 import java.awt.image.BufferedImage;
37 public class FeatureRenderer extends FeatureRendererModel
44 boolean offscreenRender = false;
46 protected SequenceI lastSeq;
52 int av_charHeight, av_charWidth;
54 boolean av_validCharWidth, av_isShowSeqFeatureHeight;
56 private Integer currentColour;
59 * Constructor given a viewport
63 public FeatureRenderer(AlignViewportI viewport)
68 protected void updateAvConfig()
70 av_charHeight = av.getCharHeight();
71 av_charWidth = av.getCharWidth();
72 av_validCharWidth = av.isValidCharWidth();
73 av_isShowSeqFeatureHeight = av.isShowSequenceFeaturesHeight();
76 void renderFeature(Graphics g, SequenceI seq, int fstart, int fend,
77 Color featureColour, int start, int end, int y1)
80 if (((fstart <= end) && (fend >= start)))
83 { // fix for if the feature we have starts before the sequence start,
84 fstart = start; // but the feature end is still valid!!
91 int pady = (y1 + av_charHeight) - av_charHeight / 5;
92 for (i = fstart; i <= fend; i++)
96 if (jalview.util.Comparison.isGap(s))
101 g.setColor(featureColour);
103 g.fillRect((i - start) * av_charWidth, y1, av_charWidth,
106 if (offscreenRender || !av_validCharWidth)
111 g.setColor(Color.white);
112 charOffset = (av_charWidth - fm.charWidth(s)) / 2;
113 g.drawString(String.valueOf(s), charOffset
114 + (av_charWidth * (i - start)), pady);
120 void renderScoreFeature(Graphics g, SequenceI seq, int fstart, int fend,
121 Color featureColour, int start, int end, int y1, byte[] bs)
124 if (((fstart <= end) && (fend >= start)))
127 { // fix for if the feature we have starts before the sequence start,
128 fstart = start; // but the feature end is still valid!!
135 int pady = (y1 + av_charHeight) - av_charHeight / 5;
136 int ystrt = 0, yend = av_charHeight;
139 // signed - zero is always middle of residue line.
142 yend = av_charHeight * (128 - bs[1]) / 512;
143 ystrt = av_charHeight - yend / 2;
147 ystrt = av_charHeight / 2;
148 yend = av_charHeight * (bs[1] - 128) / 512;
153 yend = av_charHeight * bs[1] / 255;
154 ystrt = av_charHeight - yend;
157 for (i = fstart; i <= fend; i++)
159 s = seq.getCharAt(i);
161 if (jalview.util.Comparison.isGap(s))
166 g.setColor(featureColour);
167 int x = (i - start) * av_charWidth;
168 g.drawRect(x, y1, av_charWidth, av_charHeight);
169 g.fillRect(x, y1 + ystrt, av_charWidth, yend);
171 if (offscreenRender || !av_validCharWidth)
176 g.setColor(Color.black);
177 charOffset = (av_charWidth - fm.charWidth(s)) / 2;
178 g.drawString(String.valueOf(s), charOffset
179 + (av_charWidth * (i - start)), pady);
184 BufferedImage offscreenImage;
187 public ColorI findFeatureColour(ColorI initialCol, SequenceI seq, int res)
189 return new Colour(findFeatureColour(initialCol.getRGB(), seq, res));
192 public ColorI findFeatureColour(Color initialCol, SequenceI seq, int res)
194 return findFeatureColour(new Colour(initialCol), seq, res);
198 * This is used by the Molecule Viewer and Overview to get the accurate colour
199 * of the rendered sequence
201 public synchronized int findFeatureColour(int initialCol,
202 final SequenceI seq, int column)
204 if (!av.isShowSequenceFeatures())
209 SequenceFeature[] sequenceFeatures = seq.getSequenceFeatures();
213 lastSequenceFeatures = sequenceFeatures;
214 if (lastSequenceFeatures != null)
216 sfSize = lastSequenceFeatures.length;
221 if (lastSequenceFeatures != sequenceFeatures)
223 lastSequenceFeatures = sequenceFeatures;
224 if (lastSequenceFeatures != null)
226 sfSize = lastSequenceFeatures.length;
231 if (lastSequenceFeatures == null || sfSize == 0)
236 if (jalview.util.Comparison.isGap(lastSeq.getCharAt(column)))
238 return Color.white.getRGB();
241 // Only bother making an offscreen image if transparency is applied
242 if (transparency != 1.0f && offscreenImage == null)
244 offscreenImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
247 currentColour = null;
248 // TODO: non-threadsafe - each rendering thread needs its own instance of
249 // the feature renderer - or this should be synchronized.
250 offscreenRender = true;
252 if (offscreenImage != null)
254 offscreenImage.setRGB(0, 0, initialCol);
255 drawSequence(offscreenImage.getGraphics(), lastSeq, column, column, 0);
257 return offscreenImage.getRGB(0, 0);
261 drawSequence(null, lastSeq, lastSeq.findPosition(column), -1, -1);
263 if (currentColour == null)
269 return currentColour.intValue();
275 private volatile SequenceFeature[] lastSequenceFeatures;
285 public synchronized void drawSequence(Graphics g, final SequenceI seq,
286 int start, int end, int y1)
288 SequenceFeature[] sequenceFeatures = seq.getSequenceFeatures();
289 if (sequenceFeatures == null || sequenceFeatures.length == 0)
296 fm = g.getFontMetrics();
301 if (lastSeq == null || seq != lastSeq
302 || sequenceFeatures != lastSequenceFeatures)
305 lastSequenceFeatures = sequenceFeatures;
308 if (transparency != 1 && g != null)
310 Graphics2D g2 = (Graphics2D) g;
311 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
315 if (!offscreenRender)
317 spos = lastSeq.findPosition(start);
318 epos = lastSeq.findPosition(end);
321 sfSize = lastSequenceFeatures.length;
323 for (int renderIndex = 0; renderIndex < renderOrder.length; renderIndex++)
325 type = renderOrder[renderIndex];
327 if (type == null || !showFeatureOfType(type))
332 // loop through all features in sequence to find
333 // current feature to render
334 for (sfindex = 0; sfindex < sfSize; sfindex++)
336 final SequenceFeature sequenceFeature = lastSequenceFeatures[sfindex];
337 if (!sequenceFeature.type.equals(type))
342 if (featureGroups != null
343 && sequenceFeature.featureGroup != null
344 && sequenceFeature.featureGroup.length() != 0
345 && featureGroups.containsKey(sequenceFeature.featureGroup)
346 && !featureGroups.get(sequenceFeature.featureGroup)
353 && (sequenceFeature.getBegin() > epos || sequenceFeature
359 if (offscreenRender && offscreenImage == null)
361 if (sequenceFeature.begin <= start
362 && sequenceFeature.end >= start)
364 // this is passed out to the overview and other sequence renderers
365 // (e.g. molecule viewer) to get displayed colour for rendered
367 currentColour = new Integer(getColour(sequenceFeature).getRGB());
368 // used to be retreived from av.featuresDisplayed
369 // currentColour = av.featuresDisplayed
370 // .get(sequenceFeatures[sfindex].type);
374 else if (sequenceFeature.type.equals("disulfide bond"))
376 renderFeature(g, seq, seq.findIndex(sequenceFeature.begin) - 1,
377 seq.findIndex(sequenceFeature.begin) - 1,
378 getColour(sequenceFeature)
379 // new Color(((Integer) av.featuresDisplayed
380 // .get(sequenceFeatures[sfindex].type)).intValue())
382 renderFeature(g, seq, seq.findIndex(sequenceFeature.end) - 1,
383 seq.findIndex(sequenceFeature.end) - 1,
384 getColour(sequenceFeature)
385 // new Color(((Integer) av.featuresDisplayed
386 // .get(sequenceFeatures[sfindex].type)).intValue())
390 else if (showFeature(sequenceFeature))
392 if (av_isShowSeqFeatureHeight
393 && !Float.isNaN(sequenceFeature.score))
395 renderScoreFeature(g, seq,
396 seq.findIndex(sequenceFeature.begin) - 1,
397 seq.findIndex(sequenceFeature.end) - 1,
398 getColour(sequenceFeature), start, end, y1,
399 normaliseScore(sequenceFeature));
403 renderFeature(g, seq, seq.findIndex(sequenceFeature.begin) - 1,
404 seq.findIndex(sequenceFeature.end) - 1,
405 getColour(sequenceFeature), start, end, y1);
413 if (transparency != 1.0f && g != null)
415 Graphics2D g2 = (Graphics2D) g;
416 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
422 * Called when alignment in associated view has new/modified features to
423 * discover and display.
427 public void featuresAdded()