1db200429191e04666f47f8769a3c9c96b1adb00
[jalview.git] / src / jalview / renderer / seqfeatures / FeatureColourFinder.java
1 package jalview.renderer.seqfeatures;
2
3 import jalview.api.FeatureRenderer;
4 import jalview.api.FeaturesDisplayedI;
5 import jalview.datamodel.SequenceI;
6 import jalview.viewmodel.seqfeatures.FeatureRendererModel;
7
8 import java.awt.Color;
9 import java.awt.Graphics;
10 import java.awt.image.BufferedImage;
11
12 /**
13  * A helper class to find feature colour using an associated FeatureRenderer
14  * 
15  * @author gmcarstairs
16  *
17  */
18 public class FeatureColourFinder
19 {
20   /*
21    * the class we delegate feature finding to
22    */
23   private FeatureRenderer featureRenderer;
24
25   /*
26    * a 1-pixel image on which features can be drawn, for the case where
27    * transparency allows 'see-through' of multiple feature colours
28    */
29   private BufferedImage offscreenImage;
30
31   /**
32    * Constructor
33    * 
34    * @param fr
35    */
36   public FeatureColourFinder(FeatureRenderer fr)
37   {
38     featureRenderer = fr;
39     offscreenImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
40   }
41
42   /**
43    * Answers the feature colour to show for the given sequence and column
44    * position. This delegates to the FeatureRenderer to find the colour, which
45    * will depend on feature location, visibility, ordering, colour scheme, and
46    * whether or not transparency is applied. For feature rendering with
47    * transparency, this class provides a dummy 'offscreen' graphics context
48    * where multiple feature colours can be overlaid and the combined colour read
49    * back.
50    * <p>
51    * This method is not thread-safe when transparency is applied, since a shared
52    * BufferedImage would be used by all threads to hold the composite colour at
53    * a position. Each thread should use a separate instance of this class.
54    * 
55    * @param defaultColour
56    * @param seq
57    * @param column
58    *          alignment column position (base zero)
59    * @return
60    */
61   public Color findFeatureColour(Color defaultColour, SequenceI seq,
62           int column)
63   {
64     if (noFeaturesDisplayed())
65     {
66       return defaultColour;
67     }
68
69     Graphics g = null;
70
71     /*
72      * if transparency applies, provide a notional 1x1 graphics context 
73      * that has been primed with the default colour
74      */
75     if (featureRenderer.getTransparency() != 1f)
76     {
77       g = offscreenImage.getGraphics();
78       if (defaultColour != null)
79       {
80         offscreenImage.setRGB(0, 0, defaultColour.getRGB());
81       }
82     }
83
84     Color c = featureRenderer.findFeatureColour(seq, column, g);
85     if (c == null)
86     {
87       return defaultColour;
88     }
89
90     if (g != null)
91     {
92       c = new Color(offscreenImage.getRGB(0, 0));
93     }
94     return c;
95   }
96
97   /**
98    * Answers true if feature display is turned off, or there are no features
99    * configured to be visible
100    * 
101    * @return
102    */
103   boolean noFeaturesDisplayed()
104   {
105     if (featureRenderer == null
106             || !featureRenderer.getViewport().isShowSequenceFeatures())
107     {
108       return true;
109     }
110
111     if (!((FeatureRendererModel) featureRenderer).hasRenderOrder())
112     {
113       return true;
114     }
115
116     FeaturesDisplayedI displayed = featureRenderer.getFeaturesDisplayed();
117     if (displayed == null || displayed.getVisibleFeatureCount() == 0)
118     {
119       return true;
120     }
121
122     return false;
123   }
124 }