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