712c9f445f20ef6d3ad6eb45b1d85e4dee10814a
[jalview.git] / src / jalview / renderer / seqfeatures / FeatureColourFinder.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.api.FeatureRenderer;
24 import jalview.api.FeaturesDisplayedI;
25 import jalview.datamodel.SequenceI;
26 import jalview.util.Platform;
27 import jalview.viewmodel.seqfeatures.FeatureRendererModel;
28
29 import java.awt.Color;
30 import java.awt.Graphics;
31 import java.awt.Graphics2D;
32 import java.awt.image.BufferedImage;
33
34 /**
35  * A helper class to find feature colour using an associated FeatureRenderer
36  * 
37  * @author gmcarstairs
38  *
39  */
40 public class FeatureColourFinder
41 {
42   /*
43    * the class we delegate feature finding to
44    */
45   private FeatureRenderer featureRenderer;
46
47   /*
48    * a 1-pixel image on which features can be drawn, for the case where
49    * transparency allows 'see-through' of multiple feature colours
50    */
51   private BufferedImage offscreenImage;
52
53   private Graphics goff;
54
55   /**
56    * Constructor
57    * 
58    * @param fr
59    */
60   public FeatureColourFinder(FeatureRenderer fr)
61   {
62     featureRenderer = fr;
63     offscreenImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
64     goff = offscreenImage.getGraphics();
65   }
66
67   /**
68    * Answers the feature colour to show for the given sequence and column
69    * position. This delegates to the FeatureRenderer to find the colour, which
70    * will depend on feature location, visibility, ordering, colour scheme, and
71    * whether or not transparency is applied. For feature rendering with
72    * transparency, this class provides a dummy 'offscreen' graphics context
73    * where multiple feature colours can be overlaid and the combined colour read
74    * back.
75    * <p>
76    * This method is not thread-safe when transparency is applied, since a shared
77    * BufferedImage would be used by all threads to hold the composite colour at
78    * a position. Each thread should use a separate instance of this class.
79    * 
80    * @param defaultColour
81    * @param seq
82    * @param column
83    *          alignment column position (0..)
84    * @return
85    */
86   public Color findFeatureColour(Color defaultColour, SequenceI seq,
87           int column)
88   {
89     if (noFeaturesDisplayed())
90     {
91       return defaultColour;
92     }
93
94     Graphics g = null;
95
96     /*
97      * if transparency applies, provide a notional 1x1 graphics context 
98      * that has been primed with the default colour
99      */
100     if (featureRenderer.getTransparency() != 1f)
101     {
102       g = goff;
103       if (defaultColour != null)
104       {
105         offscreenImage.setRGB(0, 0, defaultColour.getRGB());
106       }
107     }
108
109     Color c = featureRenderer.findFeatureColour(seq, column + 1, g);
110     if (c == null)
111     {
112       return defaultColour;
113     }
114
115     if (g != null)
116     {
117       c = new Color(offscreenImage.getRGB(0, 0));
118     }
119     return c;
120   }
121
122   public int findFeatureColourInt(int defaultColour, SequenceI seq,
123           int column)
124   {
125     // if (noFeaturesDisplayed())
126     // {
127     // return defaultColour;
128     // }
129
130     Graphics2D g = null;
131
132     /*
133      * if transparency applies, provide a notional 1x1 graphics context 
134      * that has been primed with the default colour
135      */
136     if (featureRenderer.getTransparency() != 1f)
137     {
138       g = (Graphics2D) goff;
139       if (Platform.isJS())
140       {
141         // Clear the HTML5 canvas color.
142         // otherwise we get a smearing.
143         // For whatever reason, this is necessary BH 2019.10.01.
144         g.setColor(Color.white);
145         g.fillRect(0, 0, 1, 1);
146       }
147       if (defaultColour != 0)
148       {
149         offscreenImage.setRGB(0, 0, defaultColour);
150       }
151     }
152     Color c = featureRenderer.findFeatureColour(seq, column + 1, g);
153     if (c == null)
154     {
155       return defaultColour;
156     }
157     if (g == null)
158     {
159       return c.getRGB();
160     }
161     if (Platform.isJS())
162     {
163       // BH 2019.10.01 because for JavaScript the pixel itself
164       // is a resource that needs to be recreated in getRGB(0,0)
165       offscreenImage.flush();
166     }
167     return offscreenImage.getRGB(0, 0);
168   }
169   /**
170    * Answers true if feature display is turned off, or there are no features
171    * configured to be visible
172    * 
173    * @return
174    */
175   public boolean noFeaturesDisplayed()
176   {
177     if (featureRenderer == null
178             || !featureRenderer.getViewport().isShowSequenceFeatures())
179     {
180       return true;
181     }
182
183     if (!((FeatureRendererModel) featureRenderer).hasRenderOrder())
184     {
185       return true;
186     }
187
188     FeaturesDisplayedI displayed = featureRenderer.getFeaturesDisplayed();
189     if (displayed == null || displayed.getVisibleFeatureCount() == 0)
190     {
191       return true;
192     }
193
194     return false;
195   }
196 }