JAL-2438 handle null FeatureRenderer in FeatureColourFinder
[jalview.git] / src / jalview / renderer / seqfeatures / FeatureColourFinder.java
index 6f4d4de..1db2004 100644 (file)
@@ -1,5 +1,6 @@
 package jalview.renderer.seqfeatures;
 
+import jalview.api.FeatureRenderer;
 import jalview.api.FeaturesDisplayedI;
 import jalview.datamodel.SequenceI;
 import jalview.viewmodel.seqfeatures.FeatureRendererModel;
@@ -19,7 +20,7 @@ public class FeatureColourFinder
   /*
    * the class we delegate feature finding to
    */
-  private jalview.api.FeatureRenderer featureRenderer;
+  private FeatureRenderer featureRenderer;
 
   /*
    * a 1-pixel image on which features can be drawn, for the case where
@@ -32,7 +33,7 @@ public class FeatureColourFinder
    * 
    * @param fr
    */
-  public FeatureColourFinder(jalview.api.FeatureRenderer fr)
+  public FeatureColourFinder(FeatureRenderer fr)
   {
     featureRenderer = fr;
     offscreenImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
@@ -41,38 +42,83 @@ public class FeatureColourFinder
   /**
    * Answers the feature colour to show for the given sequence and column
    * position. This delegates to the FeatureRenderer to find the colour, which
-   * will depend on feature location, visibility, ordering, and whether or not
-   * transparency is applied. For feature rendering with transparency, this
-   * class provides a dummy 'offscreen' graphics context where multiple feature
-   * colours can be overlaid and the combined colour read back.
+   * will depend on feature location, visibility, ordering, colour scheme, and
+   * whether or not transparency is applied. For feature rendering with
+   * transparency, this class provides a dummy 'offscreen' graphics context
+   * where multiple feature colours can be overlaid and the combined colour read
+   * back.
+   * <p>
+   * This method is not thread-safe when transparency is applied, since a shared
+   * BufferedImage would be used by all threads to hold the composite colour at
+   * a position. Each thread should use a separate instance of this class.
    * 
    * @param defaultColour
    * @param seq
    * @param column
+   *          alignment column position (base zero)
    * @return
    */
   public Color findFeatureColour(Color defaultColour, SequenceI seq,
           int column)
   {
-    if (!((FeatureRendererModel) featureRenderer).hasRenderOrder())
+    if (noFeaturesDisplayed())
     {
       return defaultColour;
     }
 
-    FeaturesDisplayedI displayed = featureRenderer.getFeaturesDisplayed();
-    if (displayed == null || displayed.getVisibleFeatureCount() == 0)
+    Graphics g = null;
+
+    /*
+     * if transparency applies, provide a notional 1x1 graphics context 
+     * that has been primed with the default colour
+     */
+    if (featureRenderer.getTransparency() != 1f)
     {
-      return defaultColour; // nothing to see here folks
+      g = offscreenImage.getGraphics();
+      if (defaultColour != null)
+      {
+        offscreenImage.setRGB(0, 0, defaultColour.getRGB());
+      }
     }
 
-    Graphics g = featureRenderer.getTransparency() == 1f ? null
-            : offscreenImage.getGraphics();
-
     Color c = featureRenderer.findFeatureColour(seq, column, g);
-    if (c != null && g != null)
+    if (c == null)
+    {
+      return defaultColour;
+    }
+
+    if (g != null)
     {
       c = new Color(offscreenImage.getRGB(0, 0));
     }
-    return c == null ? defaultColour : c;
+    return c;
+  }
+
+  /**
+   * Answers true if feature display is turned off, or there are no features
+   * configured to be visible
+   * 
+   * @return
+   */
+  boolean noFeaturesDisplayed()
+  {
+    if (featureRenderer == null
+            || !featureRenderer.getViewport().isShowSequenceFeatures())
+    {
+      return true;
+    }
+
+    if (!((FeatureRendererModel) featureRenderer).hasRenderOrder())
+    {
+      return true;
+    }
+
+    FeaturesDisplayedI displayed = featureRenderer.getFeaturesDisplayed();
+    if (displayed == null || displayed.getVisibleFeatureCount() == 0)
+    {
+      return true;
+    }
+
+    return false;
   }
 }