JAL-1807 - Bob's last(?) before leaving Dundee -- adds fast file loading
[jalviewjs.git] / src / jalview / renderer / seqfeatures / FeatureRenderer.java
1 package jalview.renderer.seqfeatures;
2
3 import jalview.datamodel.SequenceFeature;
4 import jalview.datamodel.SequenceI;
5 import jalview.util.Comparison;
6 import jalview.viewmodel.seqfeatures.FeatureRendererModel;
7
8 import java.awt.AlphaComposite;
9 import java.awt.Color;
10 import java.awt.FontMetrics;
11 import java.awt.Graphics;
12 import java.awt.Graphics2D;
13 import java.awt.image.BufferedImage;
14
15 public class FeatureRenderer extends FeatureRendererModel
16 {
17
18   FontMetrics fm;
19
20   int charOffset;
21
22   boolean offscreenRender = false;
23
24   protected SequenceI lastSeq;
25
26   char s;
27
28   int i;
29
30   int av_charHeight, av_charWidth;
31
32   boolean av_validCharWidth, av_isShowSeqFeatureHeight;
33
34   protected void updateAvConfig()
35   {
36     av_charHeight = av.getCharHeight();
37     av_charWidth = av.getCharWidth();
38     av_validCharWidth = av.isValidCharWidth();
39     av_isShowSeqFeatureHeight = av.isShowSequenceFeaturesHeight();
40   }
41
42   void renderFeature(Graphics g, SequenceI seq, int fstart, int fend,
43           Color featureColour, int start, int end, int y1)
44   {
45     updateAvConfig();
46     if (((fstart <= end) && (fend >= start)))
47     {
48       if (fstart < start)
49       { // fix for if the feature we have starts before the sequence start,
50         fstart = start; // but the feature end is still valid!!
51       }
52
53       if (fend >= end)
54       {
55         fend = end;
56       }
57       int pady = (y1 + av_charHeight) - av_charHeight / 5;
58       for (i = fstart; i <= fend; i++)
59       {
60         s = seq.getCharAt(i);
61
62         if (Comparison.isGap(s))
63         {
64           continue;
65         }
66
67         g.setColor(featureColour);
68
69         g.fillRect((i - start) * av_charWidth, y1, av_charWidth,
70                 av_charHeight);
71
72         if (offscreenRender || !av_validCharWidth)
73         {
74           continue;
75         }
76
77         g.setColor(Color.white);
78         charOffset = (av_charWidth - fm.charWidth(s)) / 2;
79         awt2swing.Util.drawString(g, String.valueOf(s), charOffset
80                 + (av_charWidth * (i - start)), pady);
81
82       }
83     }
84   }
85
86   void renderScoreFeature(Graphics g, SequenceI seq, int fstart, int fend,
87           Color featureColour, int start, int end, int y1, byte[] bs)
88   {
89     updateAvConfig();
90     if (((fstart <= end) && (fend >= start)))
91     {
92       if (fstart < start)
93       { // fix for if the feature we have starts before the sequence start,
94         fstart = start; // but the feature end is still valid!!
95       }
96
97       if (fend >= end)
98       {
99         fend = end;
100       }
101       int pady = (y1 + av_charHeight) - av_charHeight / 5;
102       int ystrt = 0, yend = av_charHeight;
103       if (bs[0] != 0)
104       {
105         // signed - zero is always middle of residue line.
106         if (bs[1] < 128)
107         {
108           yend = av_charHeight * (128 - bs[1]) / 512;
109           ystrt = av_charHeight - yend / 2;
110         }
111         else
112         {
113           ystrt = av_charHeight / 2;
114           yend = av_charHeight * (bs[1] - 128) / 512;
115         }
116       }
117       else
118       {
119         yend = av_charHeight * bs[1] / 255;
120         ystrt = av_charHeight - yend;
121
122       }
123       for (i = fstart; i <= fend; i++)
124       {
125         s = seq.getCharAt(i);
126
127         if (Comparison.isGap(s))
128         {
129           continue;
130         }
131
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);
136
137         if (offscreenRender || !av_validCharWidth)
138         {
139           continue;
140         }
141
142         g.setColor(Color.black);
143         charOffset = (av_charWidth - fm.charWidth(s)) / 2;
144         awt2swing.Util.drawString(g, String.valueOf(s), charOffset
145                 + (av_charWidth * (i - start)), pady);
146       }
147     }
148   }
149
150   BufferedImage offscreenImage;
151
152   public Color findFeatureColour(Color initialCol, SequenceI seq, int res)
153   {
154     return new Color(findFeatureColour(initialCol.getRGB(), seq, res));
155   }
156
157   /**
158    * This is used by the Molecule Viewer and Overview to get the accurate
159    * colourof the rendered sequence
160    */
161   public synchronized int findFeatureColour(int initialCol, final SequenceI seq,
162           int column)
163   {
164     if (!av.isShowSequenceFeatures())
165     {
166       return initialCol;
167     }
168
169     SequenceFeature[] sequenceFeatures = seq.getSequenceFeatures();
170     if (seq != lastSeq)
171     {
172       lastSeq = seq;
173       lastSequenceFeatures = sequenceFeatures;
174       if (lastSequenceFeatures != null)
175       {
176         sfSize = lastSequenceFeatures.length;
177       }
178     }
179     else
180     {
181       if (lastSequenceFeatures != sequenceFeatures)
182       {
183         lastSequenceFeatures = sequenceFeatures;
184         if (lastSequenceFeatures != null)
185         {
186           sfSize = lastSequenceFeatures.length;
187         }
188       }
189     }
190
191     if (lastSequenceFeatures == null || sfSize == 0)
192     {
193       return initialCol;
194     }
195
196     if (Comparison.isGap(lastSeq.getCharAt(column)))
197     {
198       return Color.white.getRGB();
199     }
200
201     // Only bother making an offscreen image if transparency is applied
202     if (transparency != 1.0f && offscreenImage == null)
203     {
204       offscreenImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
205     }
206
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;
211
212     if (offscreenImage != null)
213     {
214       offscreenImage.setRGB(0, 0, initialCol);
215       drawSequence(offscreenImage.getGraphics(), lastSeq, column, column, 0);
216
217       return offscreenImage.getRGB(0, 0);
218     }
219     else
220     {
221       drawSequence(null, lastSeq, lastSeq.findPosition(column), -1, -1);
222
223       if (currentColour == null)
224       {
225         return initialCol;
226       }
227       else
228       {
229         return ((Integer) currentColour).intValue();
230       }
231     }
232
233   }
234
235   private volatile SequenceFeature[] lastSequenceFeatures;
236
237   int sfSize;
238
239   int sfindex;
240
241   int spos;
242
243   int epos;
244
245   public synchronized void drawSequence(Graphics g, final SequenceI seq,
246           int start, int end, int y1)
247   {
248     SequenceFeature[] sequenceFeatures = seq.getSequenceFeatures();
249     if (sequenceFeatures == null || sequenceFeatures.length == 0)
250     {
251       return;
252     }
253
254     if (g != null)
255     {
256       fm = g.getFontMetrics();
257     }
258
259     updateFeatures();
260
261     if (lastSeq == null || seq != lastSeq
262             || sequenceFeatures != lastSequenceFeatures)
263     {
264       lastSeq = seq;
265       lastSequenceFeatures = sequenceFeatures;
266     }
267
268     if (transparency != 1 && g != null)
269     {
270       Graphics2D g2 = (Graphics2D) g;
271       g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
272               transparency));
273     }
274
275     if (!offscreenRender)
276     {
277       spos = lastSeq.findPosition(start);
278       epos = lastSeq.findPosition(end);
279     }
280
281     sfSize = lastSequenceFeatures.length;
282     String type;
283     for (int renderIndex = 0; renderIndex < renderOrder.length; renderIndex++)
284     {
285       type = renderOrder[renderIndex];
286
287       if (type == null || !showFeatureOfType(type))
288       {
289         continue;
290       }
291
292       // loop through all features in sequence to find
293       // current feature to render
294       for (sfindex = 0; sfindex < sfSize; sfindex++)
295       {
296         final SequenceFeature sequenceFeature = lastSequenceFeatures[sfindex];
297         if (!sequenceFeature.type.equals(type))
298         {
299           continue;
300         }
301
302         if (featureGroups != null
303                 && sequenceFeature.featureGroup != null
304                 && sequenceFeature.featureGroup.length() != 0
305                 && featureGroups.containsKey(sequenceFeature.featureGroup)
306                 && !featureGroups.get(sequenceFeature.featureGroup)
307                         .booleanValue())
308         {
309           continue;
310         }
311
312         if (!offscreenRender
313                 && (sequenceFeature.getBegin() > epos || sequenceFeature
314                         .getEnd() < spos))
315         {
316           continue;
317         }
318
319         if (offscreenRender && offscreenImage == null)
320         {      
321           if (sequenceFeature.begin <= start
322                   && sequenceFeature.end >= start)
323           {
324             // this is passed out to the overview and other sequence renderers
325             // (e.g. molecule viewer) to get displayed colour for rendered
326             // sequence
327             currentColour = new Integer(getColour(sequenceFeature).getRGB());
328             // used to be retreived from av.featuresDisplayed
329             // currentColour = av.featuresDisplayed
330             // .get(sequenceFeatures[sfindex].type);
331
332           }
333         }
334         else if (sequenceFeature.type.equals("disulfide bond"))
335         {
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())
341                   , start, end, y1);
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())
347                   , start, end, y1);
348
349         }
350         else if (showFeature(sequenceFeature))
351         {
352           if (av_isShowSeqFeatureHeight
353                   && !Float.isNaN(sequenceFeature.score))
354           {
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));
360           }
361           else
362           {
363             renderFeature(g, seq, seq.findIndex(sequenceFeature.begin) - 1,
364                     seq.findIndex(sequenceFeature.end) - 1,
365                     getColour(sequenceFeature), start, end, y1);
366           }
367         }
368
369       }
370
371     }
372
373     if (transparency != 1.0f && g != null && transparencyAvailable)
374     {
375       Graphics2D g2 = (Graphics2D) g;
376       g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
377               1.0f));
378     }
379   }
380
381   boolean transparencyAvailable = true;
382
383   protected void setTransparencyAvailable(boolean isTransparencyAvailable)
384   {
385     transparencyAvailable = isTransparencyAvailable;
386   }
387
388   @Override
389   public boolean isTransparencyAvailable()
390   {
391     return transparencyAvailable;
392   }
393
394   /**
395    * Called when alignment in associated view has new/modified features to
396    * discover and display.
397    * 
398    */
399   public void featuresAdded()
400   {
401     lastSeq = null;
402     findAllFeatures();
403   }
404 }