1 package jalview.renderer.seqfeatures;
3 import jalview.datamodel.SequenceFeature;
4 import jalview.datamodel.SequenceI;
5 import jalview.util.Comparison;
6 import jalview.viewmodel.seqfeatures.FeatureRendererModel;
8 import java.awt.AlphaComposite;
10 import java.awt.FontMetrics;
11 import java.awt.Graphics;
12 import java.awt.Graphics2D;
13 import java.awt.image.BufferedImage;
15 public class FeatureRenderer extends FeatureRendererModel
22 boolean offscreenRender = false;
24 protected SequenceI lastSeq;
30 int av_charHeight, av_charWidth;
32 boolean av_validCharWidth, av_isShowSeqFeatureHeight;
34 protected void updateAvConfig()
36 av_charHeight = av.getCharHeight();
37 av_charWidth = av.getCharWidth();
38 av_validCharWidth = av.isValidCharWidth();
39 av_isShowSeqFeatureHeight = av.isShowSequenceFeaturesHeight();
42 void renderFeature(Graphics g, SequenceI seq, int fstart, int fend,
43 Color featureColour, int start, int end, int y1)
46 if (((fstart <= end) && (fend >= start)))
49 { // fix for if the feature we have starts before the sequence start,
50 fstart = start; // but the feature end is still valid!!
57 int pady = (y1 + av_charHeight) - av_charHeight / 5;
58 for (i = fstart; i <= fend; i++)
62 if (Comparison.isGap(s))
67 g.setColor(featureColour);
69 g.fillRect((i - start) * av_charWidth, y1, av_charWidth,
72 if (offscreenRender || !av_validCharWidth)
77 g.setColor(Color.white);
78 charOffset = (av_charWidth - fm.charWidth(s)) / 2;
79 g.drawString(String.valueOf(s), charOffset
80 + (av_charWidth * (i - start)), pady);
86 void renderScoreFeature(Graphics g, SequenceI seq, int fstart, int fend,
87 Color featureColour, int start, int end, int y1, byte[] bs)
90 if (((fstart <= end) && (fend >= start)))
93 { // fix for if the feature we have starts before the sequence start,
94 fstart = start; // but the feature end is still valid!!
101 int pady = (y1 + av_charHeight) - av_charHeight / 5;
102 int ystrt = 0, yend = av_charHeight;
105 // signed - zero is always middle of residue line.
108 yend = av_charHeight * (128 - bs[1]) / 512;
109 ystrt = av_charHeight - yend / 2;
113 ystrt = av_charHeight / 2;
114 yend = av_charHeight * (bs[1] - 128) / 512;
119 yend = av_charHeight * bs[1] / 255;
120 ystrt = av_charHeight - yend;
123 for (i = fstart; i <= fend; i++)
125 s = seq.getCharAt(i);
127 if (Comparison.isGap(s))
132 g.setColor(featureColour);
133 int x = (i - start) * av_charWidth;
134 g.drawRect(x, y1, av_charWidth, av_charHeight);
135 g.fillRect(x, y1 + ystrt, av_charWidth, yend);
137 if (offscreenRender || !av_validCharWidth)
142 g.setColor(Color.black);
143 charOffset = (av_charWidth - fm.charWidth(s)) / 2;
144 g.drawString(String.valueOf(s), charOffset
145 + (av_charWidth * (i - start)), pady);
150 BufferedImage offscreenImage;
152 public Color findFeatureColour(Color initialCol, SequenceI seq, int res)
154 return new Color(findFeatureColour(initialCol.getRGB(), seq, res));
158 * This is used by the Molecule Viewer and Overview to get the accurate
159 * colourof the rendered sequence
161 public synchronized int findFeatureColour(int initialCol, final SequenceI seq,
164 if (!av.isShowSequenceFeatures())
169 SequenceFeature[] sequenceFeatures = seq.getSequenceFeatures();
173 lastSequenceFeatures = sequenceFeatures;
174 if (lastSequenceFeatures != null)
176 sfSize = lastSequenceFeatures.length;
181 if (lastSequenceFeatures != sequenceFeatures)
183 lastSequenceFeatures = sequenceFeatures;
184 if (lastSequenceFeatures != null)
186 sfSize = lastSequenceFeatures.length;
191 if (lastSequenceFeatures == null || sfSize == 0)
196 if (Comparison.isGap(lastSeq.getCharAt(column)))
198 return Color.white.getRGB();
201 // Only bother making an offscreen image if transparency is applied
202 if (transparency != 1.0f && offscreenImage == null)
204 offscreenImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
207 currentColour = null;
208 // TODO: non-threadsafe - each rendering thread needs its own instance of
209 // the feature renderer - or this should be synchronized.
210 offscreenRender = true;
212 if (offscreenImage != null)
214 offscreenImage.setRGB(0, 0, initialCol);
215 drawSequence(offscreenImage.getGraphics(), lastSeq, column, column, 0);
217 return offscreenImage.getRGB(0, 0);
221 drawSequence(null, lastSeq, lastSeq.findPosition(column), -1, -1);
223 if (currentColour == null)
229 return ((Integer) currentColour).intValue();
235 private volatile SequenceFeature[] lastSequenceFeatures;
245 public synchronized void drawSequence(Graphics g, final SequenceI seq,
246 int start, int end, int y1)
248 SequenceFeature[] sequenceFeatures = seq.getSequenceFeatures();
249 if (sequenceFeatures == null || sequenceFeatures.length == 0)
256 fm = g.getFontMetrics();
261 if (lastSeq == null || seq != lastSeq
262 || sequenceFeatures != lastSequenceFeatures)
265 lastSequenceFeatures = sequenceFeatures;
268 if (transparency != 1 && g != null)
270 Graphics2D g2 = (Graphics2D) g;
271 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
275 if (!offscreenRender)
277 spos = lastSeq.findPosition(start);
278 epos = lastSeq.findPosition(end);
281 sfSize = lastSequenceFeatures.length;
283 for (int renderIndex = 0; renderIndex < renderOrder.length; renderIndex++)
285 type = renderOrder[renderIndex];
287 if (type == null || !showFeatureOfType(type))
292 // loop through all features in sequence to find
293 // current feature to render
294 for (sfindex = 0; sfindex < sfSize; sfindex++)
296 final SequenceFeature sequenceFeature = lastSequenceFeatures[sfindex];
297 if (!sequenceFeature.type.equals(type))
302 if (featureGroups != null
303 && sequenceFeature.featureGroup != null
304 && sequenceFeature.featureGroup.length() != 0
305 && featureGroups.containsKey(sequenceFeature.featureGroup)
306 && !featureGroups.get(sequenceFeature.featureGroup)
313 && (sequenceFeature.getBegin() > epos || sequenceFeature
319 if (offscreenRender && offscreenImage == null)
321 if (sequenceFeature.begin <= start
322 && sequenceFeature.end >= start)
324 // this is passed out to the overview and other sequence renderers
325 // (e.g. molecule viewer) to get displayed colour for rendered
327 currentColour = new Integer(getColour(sequenceFeature).getRGB());
328 // used to be retreived from av.featuresDisplayed
329 // currentColour = av.featuresDisplayed
330 // .get(sequenceFeatures[sfindex].type);
334 else if (sequenceFeature.type.equals("disulfide bond"))
336 renderFeature(g, seq, seq.findIndex(sequenceFeature.begin) - 1,
337 seq.findIndex(sequenceFeature.begin) - 1,
338 getColour(sequenceFeature)
339 // new Color(((Integer) av.featuresDisplayed
340 // .get(sequenceFeatures[sfindex].type)).intValue())
342 renderFeature(g, seq, seq.findIndex(sequenceFeature.end) - 1,
343 seq.findIndex(sequenceFeature.end) - 1,
344 getColour(sequenceFeature)
345 // new Color(((Integer) av.featuresDisplayed
346 // .get(sequenceFeatures[sfindex].type)).intValue())
350 else if (showFeature(sequenceFeature))
352 if (av_isShowSeqFeatureHeight
353 && sequenceFeature.score != Float.NaN)
355 renderScoreFeature(g, seq,
356 seq.findIndex(sequenceFeature.begin) - 1,
357 seq.findIndex(sequenceFeature.end) - 1,
358 getColour(sequenceFeature), start, end, y1,
359 normaliseScore(sequenceFeature));
363 renderFeature(g, seq, seq.findIndex(sequenceFeature.begin) - 1,
364 seq.findIndex(sequenceFeature.end) - 1,
365 getColour(sequenceFeature), start, end, y1);
373 if (transparency != 1.0f && g != null && transparencyAvailable)
375 Graphics2D g2 = (Graphics2D) g;
376 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
381 boolean transparencyAvailable = true;
383 protected void setTransparencyAvailable(boolean isTransparencyAvailable)
385 transparencyAvailable = isTransparencyAvailable;
389 public boolean isTransparencyAvailable()
391 return transparencyAvailable;
395 * Called when alignment in associated view has new/modified features to
396 * discover and display.
399 public void featuresAdded()