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 the Molecule Viewer and Overview to get the accurate colour
192 * of the rendered sequence
194 public synchronized int findFeatureColour(int initialCol,
195 final SequenceI seq, int column)
197 if (!av.isShowSequenceFeatures())
202 SequenceFeature[] sequenceFeatures = seq.getSequenceFeatures();
206 lastSequenceFeatures = sequenceFeatures;
207 if (lastSequenceFeatures != null)
209 sfSize = lastSequenceFeatures.length;
214 if (lastSequenceFeatures != sequenceFeatures)
216 lastSequenceFeatures = sequenceFeatures;
217 if (lastSequenceFeatures != null)
219 sfSize = lastSequenceFeatures.length;
224 if (lastSequenceFeatures == null || sfSize == 0)
229 if (jalview.util.Comparison.isGap(lastSeq.getCharAt(column)))
231 return Color.white.getRGB();
234 // Only bother making an offscreen image if transparency is applied
235 if (transparency != 1.0f && offscreenImage == null)
237 offscreenImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
240 currentColour = null;
241 // TODO: non-threadsafe - each rendering thread needs its own instance of
242 // the feature renderer - or this should be synchronized.
243 offscreenRender = true;
245 if (offscreenImage != null)
247 offscreenImage.setRGB(0, 0, initialCol);
248 drawSequence(offscreenImage.getGraphics(), lastSeq, column, column, 0);
250 return offscreenImage.getRGB(0, 0);
254 drawSequence(null, lastSeq, lastSeq.findPosition(column), -1, -1);
256 if (currentColour == null)
262 return currentColour.intValue();
268 private volatile SequenceFeature[] lastSequenceFeatures;
278 public synchronized void drawSequence(Graphics g, final SequenceI seq,
279 int start, int end, int y1)
281 SequenceFeature[] sequenceFeatures = seq.getSequenceFeatures();
282 if (sequenceFeatures == null || sequenceFeatures.length == 0)
289 fm = g.getFontMetrics();
294 if (lastSeq == null || seq != lastSeq
295 || sequenceFeatures != lastSequenceFeatures)
298 lastSequenceFeatures = sequenceFeatures;
301 if (transparency != 1 && g != null)
303 Graphics2D g2 = (Graphics2D) g;
304 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
308 if (!offscreenRender)
310 spos = lastSeq.findPosition(start);
311 epos = lastSeq.findPosition(end);
314 sfSize = lastSequenceFeatures.length;
316 for (int renderIndex = 0; renderIndex < renderOrder.length; renderIndex++)
318 type = renderOrder[renderIndex];
320 if (type == null || !showFeatureOfType(type))
325 // loop through all features in sequence to find
326 // current feature to render
327 for (sfindex = 0; sfindex < sfSize; sfindex++)
329 final SequenceFeature sequenceFeature = lastSequenceFeatures[sfindex];
330 if (!sequenceFeature.type.equals(type))
335 if (featureGroups != null
336 && sequenceFeature.featureGroup != null
337 && sequenceFeature.featureGroup.length() != 0
338 && featureGroups.containsKey(sequenceFeature.featureGroup)
339 && !featureGroups.get(sequenceFeature.featureGroup)
346 && (sequenceFeature.getBegin() > epos || sequenceFeature
352 if (offscreenRender && offscreenImage == null)
354 if (sequenceFeature.begin <= start
355 && sequenceFeature.end >= start)
357 // this is passed out to the overview and other sequence renderers
358 // (e.g. molecule viewer) to get displayed colour for rendered
360 currentColour = new Integer(getColour(sequenceFeature).getRGB());
361 // used to be retreived from av.featuresDisplayed
362 // currentColour = av.featuresDisplayed
363 // .get(sequenceFeatures[sfindex].type);
367 else if (sequenceFeature.type.equals("disulfide bond"))
369 renderFeature(g, seq, seq.findIndex(sequenceFeature.begin) - 1,
370 seq.findIndex(sequenceFeature.begin) - 1,
371 getColour(sequenceFeature)
372 // new Color(((Integer) av.featuresDisplayed
373 // .get(sequenceFeatures[sfindex].type)).intValue())
375 renderFeature(g, seq, seq.findIndex(sequenceFeature.end) - 1,
376 seq.findIndex(sequenceFeature.end) - 1,
377 getColour(sequenceFeature)
378 // new Color(((Integer) av.featuresDisplayed
379 // .get(sequenceFeatures[sfindex].type)).intValue())
383 else if (showFeature(sequenceFeature))
385 if (av_isShowSeqFeatureHeight
386 && !Float.isNaN(sequenceFeature.score))
388 renderScoreFeature(g, seq,
389 seq.findIndex(sequenceFeature.begin) - 1,
390 seq.findIndex(sequenceFeature.end) - 1,
391 getColour(sequenceFeature), start, end, y1,
392 normaliseScore(sequenceFeature));
396 renderFeature(g, seq, seq.findIndex(sequenceFeature.begin) - 1,
397 seq.findIndex(sequenceFeature.end) - 1,
398 getColour(sequenceFeature), start, end, y1);
406 if (transparency != 1.0f && g != null)
408 Graphics2D g2 = (Graphics2D) g;
409 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
415 * Called when alignment in associated view has new/modified features to
416 * discover and display.
420 public void featuresAdded()