1 package jalview.renderer.seqfeatures;
3 import jalview.datamodel.SequenceFeature;
4 import jalview.datamodel.SequenceI;
6 import java.awt.AlphaComposite;
8 import java.awt.FontMetrics;
9 import java.awt.Graphics;
10 import java.awt.Graphics2D;
11 import java.awt.image.BufferedImage;
13 public class FeatureRenderer extends
14 jalview.viewmodel.seqfeatures.FeatureRendererModel
21 boolean offscreenRender = false;
23 protected SequenceI lastSeq;
29 int av_charHeight, av_charWidth;
31 boolean av_validCharWidth, av_isShowSeqFeatureHeight;
33 protected void updateAvConfig()
35 av_charHeight = av.getCharHeight();
36 av_charWidth = av.getCharWidth();
37 av_validCharWidth = av.isValidCharWidth();
38 av_isShowSeqFeatureHeight = av.isShowSequenceFeaturesHeight();
41 void renderFeature(Graphics g, SequenceI seq, int fstart, int fend,
42 Color featureColour, int start, int end, int y1)
45 if (((fstart <= end) && (fend >= start)))
48 { // fix for if the feature we have starts before the sequence start,
49 fstart = start; // but the feature end is still valid!!
56 int pady = (y1 + av_charHeight) - av_charHeight / 5;
57 for (i = fstart; i <= fend; i++)
61 if (jalview.util.Comparison.isGap(s))
66 g.setColor(featureColour);
68 g.fillRect((i - start) * av_charWidth, y1, av_charWidth,
71 if (offscreenRender || !av_validCharWidth)
76 g.setColor(Color.white);
77 charOffset = (av_charWidth - fm.charWidth(s)) / 2;
78 g.drawString(String.valueOf(s), charOffset
79 + (av_charWidth * (i - start)), pady);
85 void renderScoreFeature(Graphics g, SequenceI seq, int fstart, int fend,
86 Color featureColour, int start, int end, int y1, byte[] bs)
89 if (((fstart <= end) && (fend >= start)))
92 { // fix for if the feature we have starts before the sequence start,
93 fstart = start; // but the feature end is still valid!!
100 int pady = (y1 + av_charHeight) - av_charHeight / 5;
101 int ystrt = 0, yend = av_charHeight;
104 // signed - zero is always middle of residue line.
107 yend = av_charHeight * (128 - bs[1]) / 512;
108 ystrt = av_charHeight - yend / 2;
112 ystrt = av_charHeight / 2;
113 yend = av_charHeight * (bs[1] - 128) / 512;
118 yend = av_charHeight * bs[1] / 255;
119 ystrt = av_charHeight - yend;
122 for (i = fstart; i <= fend; i++)
124 s = seq.getCharAt(i);
126 if (jalview.util.Comparison.isGap(s))
131 g.setColor(featureColour);
132 int x = (i - start) * av_charWidth;
133 g.drawRect(x, y1, av_charWidth, av_charHeight);
134 g.fillRect(x, y1 + ystrt, av_charWidth, yend);
136 if (offscreenRender || !av_validCharWidth)
141 g.setColor(Color.black);
142 charOffset = (av_charWidth - fm.charWidth(s)) / 2;
143 g.drawString(String.valueOf(s), charOffset
144 + (av_charWidth * (i - start)), pady);
149 BufferedImage offscreenImage;
151 public Color findFeatureColour(Color initialCol, SequenceI seq, int res)
153 return new Color(findFeatureColour(initialCol.getRGB(), seq, res));
157 * This is used by the Molecule Viewer and Overview to get the accurate
158 * colourof the rendered sequence
160 public synchronized int findFeatureColour(int initialCol, final SequenceI seq,
163 if (!av.isShowSequenceFeatures())
168 SequenceFeature[] sequenceFeatures = seq.getSequenceFeatures();
172 lastSequenceFeatures = sequenceFeatures;
173 if (lastSequenceFeatures != null)
175 sfSize = lastSequenceFeatures.length;
180 if (lastSequenceFeatures != sequenceFeatures)
182 lastSequenceFeatures = sequenceFeatures;
183 if (lastSequenceFeatures != null)
185 sfSize = lastSequenceFeatures.length;
190 if (lastSequenceFeatures == null || sfSize == 0)
195 if (jalview.util.Comparison.isGap(lastSeq.getCharAt(column)))
197 return Color.white.getRGB();
200 // Only bother making an offscreen image if transparency is applied
201 if (transparency != 1.0f && offscreenImage == null)
203 offscreenImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
206 currentColour = null;
207 // TODO: non-threadsafe - each rendering thread needs its own instance of
208 // the feature renderer - or this should be synchronized.
209 offscreenRender = true;
211 if (offscreenImage != null)
213 offscreenImage.setRGB(0, 0, initialCol);
214 drawSequence(offscreenImage.getGraphics(), lastSeq, column, column, 0);
216 return offscreenImage.getRGB(0, 0);
220 drawSequence(null, lastSeq, lastSeq.findPosition(column), -1, -1);
222 if (currentColour == null)
228 return ((Integer) currentColour).intValue();
234 private volatile SequenceFeature[] lastSequenceFeatures;
244 public synchronized void drawSequence(Graphics g, final SequenceI seq,
245 int start, int end, int y1)
247 SequenceFeature[] sequenceFeatures = seq.getSequenceFeatures();
248 if (sequenceFeatures == null || sequenceFeatures.length == 0)
255 fm = g.getFontMetrics();
260 if (lastSeq == null || seq != lastSeq
261 || sequenceFeatures != lastSequenceFeatures)
264 lastSequenceFeatures = sequenceFeatures;
267 if (transparency != 1 && g != null)
269 Graphics2D g2 = (Graphics2D) g;
270 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
274 if (!offscreenRender)
276 spos = lastSeq.findPosition(start);
277 epos = lastSeq.findPosition(end);
280 sfSize = lastSequenceFeatures.length;
282 for (int renderIndex = 0; renderIndex < renderOrder.length; renderIndex++)
284 type = renderOrder[renderIndex];
286 if (type == null || !showFeatureOfType(type))
291 // loop through all features in sequence to find
292 // current feature to render
293 for (sfindex = 0; sfindex < sfSize; sfindex++)
295 final SequenceFeature sequenceFeature = lastSequenceFeatures[sfindex];
296 if (!sequenceFeature.type.equals(type))
301 if (featureGroups != null
302 && sequenceFeature.featureGroup != null
303 && sequenceFeature.featureGroup.length() != 0
304 && featureGroups.containsKey(sequenceFeature.featureGroup)
305 && !featureGroups.get(sequenceFeature.featureGroup)
312 && (sequenceFeature.getBegin() > epos || sequenceFeature
318 if (offscreenRender && offscreenImage == null)
320 if (sequenceFeature.begin <= start
321 && sequenceFeature.end >= start)
323 // this is passed out to the overview and other sequence renderers
324 // (e.g. molecule viewer) to get displayed colour for rendered
326 currentColour = new Integer(getColour(sequenceFeature).getRGB());
327 // used to be retreived from av.featuresDisplayed
328 // currentColour = av.featuresDisplayed
329 // .get(sequenceFeatures[sfindex].type);
333 else if (sequenceFeature.type.equals("disulfide bond"))
335 renderFeature(g, seq, seq.findIndex(sequenceFeature.begin) - 1,
336 seq.findIndex(sequenceFeature.begin) - 1,
337 getColour(sequenceFeature)
338 // new Color(((Integer) av.featuresDisplayed
339 // .get(sequenceFeatures[sfindex].type)).intValue())
341 renderFeature(g, seq, seq.findIndex(sequenceFeature.end) - 1,
342 seq.findIndex(sequenceFeature.end) - 1,
343 getColour(sequenceFeature)
344 // new Color(((Integer) av.featuresDisplayed
345 // .get(sequenceFeatures[sfindex].type)).intValue())
349 else if (showFeature(sequenceFeature))
351 if (av_isShowSeqFeatureHeight
352 && sequenceFeature.score != Float.NaN)
354 renderScoreFeature(g, seq,
355 seq.findIndex(sequenceFeature.begin) - 1,
356 seq.findIndex(sequenceFeature.end) - 1,
357 getColour(sequenceFeature), start, end, y1,
358 normaliseScore(sequenceFeature));
362 renderFeature(g, seq, seq.findIndex(sequenceFeature.begin) - 1,
363 seq.findIndex(sequenceFeature.end) - 1,
364 getColour(sequenceFeature), start, end, y1);
372 if (transparency != 1.0f && g != null && transparencyAvailable)
374 Graphics2D g2 = (Graphics2D) g;
375 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
380 boolean transparencyAvailable = true;
382 protected void setTransparencyAvailable(boolean isTransparencyAvailable)
384 transparencyAvailable = isTransparencyAvailable;
388 public boolean isTransparencyAvailable()
390 return transparencyAvailable;
394 * Called when alignment in associated view has new/modified features to
395 * discover and display.
398 public void featuresAdded()