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);
84 void renderScoreFeature(Graphics g, SequenceI seq, int fstart, int fend,
85 Color featureColour, int start, int end, int y1, byte[] bs)
88 if (((fstart <= end) && (fend >= start)))
91 { // fix for if the feature we have starts before the sequence start,
92 fstart = start; // but the feature end is still valid!!
99 int pady = (y1 + av_charHeight) - av_charHeight / 5;
100 int ystrt = 0, yend = av_charHeight;
103 // signed - zero is always middle of residue line.
106 yend = av_charHeight * (128 - bs[1]) / 512;
107 ystrt = av_charHeight - yend / 2;
111 ystrt = av_charHeight / 2;
112 yend = av_charHeight * (bs[1] - 128) / 512;
117 yend = av_charHeight * bs[1] / 255;
118 ystrt = av_charHeight - yend;
121 for (i = fstart; i <= fend; i++)
123 s = seq.getCharAt(i);
125 if (jalview.util.Comparison.isGap(s))
130 g.setColor(featureColour);
131 int x = (i - start) * av_charWidth;
132 g.drawRect(x, y1, av_charWidth, av_charHeight);
133 g.fillRect(x, y1 + ystrt, av_charWidth, yend);
135 if (offscreenRender || !av_validCharWidth)
140 g.setColor(Color.black);
141 charOffset = (av_charWidth - fm.charWidth(s)) / 2;
142 g.drawString(String.valueOf(s), charOffset
143 + (av_charWidth * (i - start)), pady);
148 BufferedImage offscreenImage;
150 public Color findFeatureColour(Color initialCol, SequenceI seq, int res)
152 return new Color(findFeatureColour(initialCol.getRGB(), seq, res));
156 * This is used by the Molecule Viewer and Overview to get the accurate
157 * colourof the rendered sequence
159 public synchronized int findFeatureColour(int initialCol, final SequenceI seq,
162 if (!av.isShowSequenceFeatures())
167 SequenceFeature[] sequenceFeatures = seq.getSequenceFeatures();
171 lastSequenceFeatures = sequenceFeatures;
172 if (lastSequenceFeatures != null)
174 sfSize = lastSequenceFeatures.length;
179 if (lastSequenceFeatures != sequenceFeatures)
181 lastSequenceFeatures = sequenceFeatures;
182 if (lastSequenceFeatures != null)
184 sfSize = lastSequenceFeatures.length;
189 if (lastSequenceFeatures == null || sfSize == 0)
194 if (jalview.util.Comparison.isGap(lastSeq.getCharAt(column)))
196 return Color.white.getRGB();
199 // Only bother making an offscreen image if transparency is applied
200 if (transparency != 1.0f && offscreenImage == null)
202 offscreenImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
205 currentColour = null;
206 // TODO: non-threadsafe - each rendering thread needs its own instance of
207 // the feature renderer - or this should be synchronized.
208 offscreenRender = true;
210 if (offscreenImage != null)
212 offscreenImage.setRGB(0, 0, initialCol);
213 drawSequence(offscreenImage.getGraphics(), lastSeq, column, column, 0);
215 return offscreenImage.getRGB(0, 0);
219 drawSequence(null, lastSeq, lastSeq.findPosition(column), -1, -1);
221 if (currentColour == null)
227 return ((Integer) currentColour).intValue();
233 private volatile SequenceFeature[] lastSequenceFeatures;
243 public synchronized void drawSequence(Graphics g, final SequenceI seq,
244 int start, int end, int y1)
246 SequenceFeature[] sequenceFeatures = seq.getSequenceFeatures();
247 if (sequenceFeatures == null || sequenceFeatures.length == 0)
254 fm = g.getFontMetrics();
259 if (lastSeq == null || seq != lastSeq
260 || sequenceFeatures != lastSequenceFeatures)
263 lastSequenceFeatures = sequenceFeatures;
266 if (transparency != 1 && g != null)
268 Graphics2D g2 = (Graphics2D) g;
269 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
273 if (!offscreenRender)
275 spos = lastSeq.findPosition(start);
276 epos = lastSeq.findPosition(end);
279 sfSize = lastSequenceFeatures.length;
281 for (int renderIndex = 0; renderIndex < renderOrder.length; renderIndex++)
283 type = renderOrder[renderIndex];
285 if (type == null || !showFeatureOfType(type))
290 // loop through all features in sequence to find
291 // current feature to render
292 for (sfindex = 0; sfindex < sfSize; sfindex++)
294 final SequenceFeature sequenceFeature = lastSequenceFeatures[sfindex];
295 if (!sequenceFeature.type.equals(type))
300 if (featureGroups != null
301 && sequenceFeature.featureGroup != null
302 && sequenceFeature.featureGroup.length() != 0
303 && featureGroups.containsKey(sequenceFeature.featureGroup)
304 && !featureGroups.get(sequenceFeature.featureGroup)
311 && (sequenceFeature.getBegin() > epos || sequenceFeature
317 if (offscreenRender && offscreenImage == null)
319 if (sequenceFeature.begin <= start
320 && sequenceFeature.end >= start)
322 // this is passed out to the overview and other sequence renderers
323 // (e.g. molecule viewer) to get displayed colour for rendered
325 currentColour = new Integer(getColour(sequenceFeature).getRGB());
326 // used to be retreived from av.featuresDisplayed
327 // currentColour = av.featuresDisplayed
328 // .get(sequenceFeatures[sfindex].type);
332 else if (sequenceFeature.type.equals("disulfide bond"))
334 renderFeature(g, seq, seq.findIndex(sequenceFeature.begin) - 1,
335 seq.findIndex(sequenceFeature.begin) - 1,
336 getColour(sequenceFeature)
337 // new Color(((Integer) av.featuresDisplayed
338 // .get(sequenceFeatures[sfindex].type)).intValue())
340 renderFeature(g, seq, seq.findIndex(sequenceFeature.end) - 1,
341 seq.findIndex(sequenceFeature.end) - 1,
342 getColour(sequenceFeature)
343 // new Color(((Integer) av.featuresDisplayed
344 // .get(sequenceFeatures[sfindex].type)).intValue())
348 else if (showFeature(sequenceFeature))
350 if (av_isShowSeqFeatureHeight
351 && sequenceFeature.score != Float.NaN)
353 renderScoreFeature(g, seq,
354 seq.findIndex(sequenceFeature.begin) - 1,
355 seq.findIndex(sequenceFeature.end) - 1,
356 getColour(sequenceFeature), start, end, y1,
357 normaliseScore(sequenceFeature));
361 renderFeature(g, seq, seq.findIndex(sequenceFeature.begin) - 1,
362 seq.findIndex(sequenceFeature.end) - 1,
363 getColour(sequenceFeature), start, end, y1);
371 if (transparency != 1.0f && g != null && transparencyAvailable)
373 Graphics2D g2 = (Graphics2D) g;
374 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
379 boolean transparencyAvailable = true;
381 protected void setTransparencyAvailable(boolean isTransparencyAvailable)
383 transparencyAvailable = isTransparencyAvailable;
387 public boolean isTransparencyAvailable()
389 return transparencyAvailable;
393 * Called when alignment in associated view has new/modified features to
394 * discover and display.
397 public void featuresAdded()