changeset 2222:22975e748165

added configuration options "AnnotationsColor" and "HighlightedAnnotationsColor"
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 23 Apr 2025 13:14:42 +0200 (7 days ago)
parents 900fb75351cd
children e928629d7df0
files Applications/StoneWebViewer/NEWS Applications/StoneWebViewer/WebApplication/app.js Applications/StoneWebViewer/WebApplication/configuration.json Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp OrthancStone/Sources/Scene2D/AnnotationsSceneLayer.cpp OrthancStone/Sources/Scene2D/AnnotationsSceneLayer.h
diffstat 6 files changed, 248 insertions(+), 91 deletions(-) [+]
line wrap: on
line diff
--- a/Applications/StoneWebViewer/NEWS	Wed Apr 23 09:31:59 2025 +0200
+++ b/Applications/StoneWebViewer/NEWS	Wed Apr 23 13:14:42 2025 +0200
@@ -3,10 +3,12 @@
 
 * Experimental support for DICOM SR "Measurement Report" (TID 1500 - only polylines)
 * Added "Print" and "Download" buttons in the PDF viewer toolbar
-* New configuration "ScreenshotTemplate" to define the filename generated by the
-  "Download as JPEG" button. New default value is:
+* New configuration option "ScreenshotTemplate" to define the filename
+  generated by the "Download as JPEG" button. New default value is:
   "{PatientID}-{PatientName}-{StudyDate}-{SeriesDescription}-{InstanceNumber}-{CurrentFrame}.jpg"
 * Remember the previous layout when re-opening the viewer
+* New configuration options "AnnotationsColor" and "HighlightedAnnotationsColor"
+  to change the color of annotations
 
 Maintenance
 -----------
--- a/Applications/StoneWebViewer/WebApplication/app.js	Wed Apr 23 09:31:59 2025 +0200
+++ b/Applications/StoneWebViewer/WebApplication/app.js	Wed Apr 23 13:14:42 2025 +0200
@@ -1610,6 +1610,16 @@
     alert('Bad value for option "ShowInfoPanelAtStartup": ' + app.globalConfiguration.ShowInfoPanelAtStartup);
   }
 
+  var color = app.globalConfiguration['AnnotationsColor'];
+  if (color !== undefined) {
+    stone.SetAnnotationsColor(color[0], color[1], color[2]);
+  }
+
+  color = app.globalConfiguration['HighlightedAnnotationsColor'];
+  if (color !== undefined) {
+    stone.SetHighlightedAnnotationsColor(color[0], color[1], color[2]);
+  }
+
   console.warn('Stone properly initialized');
 
   app.stoneWebViewerVersion = stone.GetStoneWebViewerVersion();
--- a/Applications/StoneWebViewer/WebApplication/configuration.json	Wed Apr 23 09:31:59 2025 +0200
+++ b/Applications/StoneWebViewer/WebApplication/configuration.json	Wed Apr 23 13:14:42 2025 +0200
@@ -151,18 +151,28 @@
       /* "Authorization" : "Bearer ${USER}" */
     },
 
+    /**
+     * Color that is used to draw the annotations, as a RGB
+     * triple. (New in Stone Web viewer 2.7)
+     **/
+    "AnnotationsColor" : [ 64, 130, 173 ],
+
+    /**
+     * Color that is used to draw highlighted annotations, as a RGB
+     * triple. (New in Stone Web viewer 2.7)
+     **/
+    "HighlightedAnnotationsColor" : [ 64, 173, 121 ],
 
     /**
-     * Define the the filename of the 'Download as Jpeg' screenshots.
-     * The template can either contain Patient, Study or Series tags 
+     * Define the the filename of the "Download as JPEG" screenshots.
+     * The template can either contain Patient, Study or Series tags
      * in the group,element form (e.g. {0008,103e}) or the DICOM tag
-     * common name (e.g. {SeriesDescription}).  A few Instance tags are
+     * common name (e.g. {SeriesDescription}). Some instance tags are
      * also available: {InstanceNumber}, {ContentDate}, {ContentTime}.
-     * {CurrentFrame} is also available although not a DICOM Tag.
-     * (New in Stone Web viewer 2.7).  In prior versions, the filename
-     * was always "StoneWebViewerScreenshot.jpg".
+     * {CurrentFrame} is also available although not a DICOM tag. In
+     * prior versions, the filename was always
+     * "StoneWebViewerScreenshot.jpg". (New in Stone Web viewer 2.7)
      **/
     "ScreenshotTemplate" : "{PatientID}-{PatientName}-{StudyDate}-{SeriesDescription}-{InstanceNumber}-{CurrentFrame}.jpg"
-
   }
 }
--- a/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp	Wed Apr 23 09:31:59 2025 +0200
+++ b/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp	Wed Apr 23 13:14:42 2025 +0200
@@ -405,6 +405,12 @@
     }
   }
 
+  static OrthancStone::Color& GetColorInternal()
+  {
+    static OrthancStone::Color color_(0, 255, 0);  // Default color: green
+    return color_;
+  }
+
 public:
   DicomStructuredReportFrames(const OrthancStone::DicomStructuredReport& sr,
                               const OrthancStone::LoadedDicomResources& instances) :
@@ -512,10 +518,9 @@
            structure.GetFrameNumber() == frameNumber))
       {
 #if 1
-        // Default color: green
-        const OrthancStone::Color color(0, 255, 0);
+        const OrthancStone::Color& color = GetColorInternal();
 #else
-        OrthancStone::Color color(0, 0, 255);
+        OrthancStone::Color color(GetColorInternal());
 
         if (structure.HasProbabilityOfCancer())
         {
@@ -566,6 +571,17 @@
 
     return layer.release();
   }
+
+
+  static const OrthancStone::Color& GetAnnotationsColor()
+  {
+    return GetColorInternal();
+  }
+
+  static void SetAnnotationsColor(const OrthancStone::Color& color)
+  {
+    GetColorInternal() = color;
+  }
 };
 
 
@@ -3210,6 +3226,8 @@
 
     stoneAnnotations_.reset(new OrthancStone::AnnotationsSceneLayer(LAYER_ANNOTATIONS_STONE));
     stoneAnnotations_->SetProbedLayer(LAYER_TEXTURE);
+    stoneAnnotations_->SetColor(GetAnnotationsColorInternal());
+    stoneAnnotations_->SetHoverColor(GetHighlightedColorInternal());
   }
 
 
@@ -3347,6 +3365,18 @@
     }
   }
 
+  static OrthancStone::Color& GetAnnotationsColorInternal()
+  {
+    static OrthancStone::Color color_;
+    return color_;
+  }
+
+  static OrthancStone::Color& GetHighlightedColorInternal()
+  {
+    static OrthancStone::Color color_;
+    return color_;
+  }
+
 public:
   virtual ~ViewerViewport()
   {
@@ -4169,6 +4199,18 @@
   {
     return pendingSeriesInstanceUid_;
   }
+
+
+  static void SetAnnotationsColor(const OrthancStone::Color& color)
+  {
+    GetAnnotationsColorInternal() = color;
+  }
+
+
+  static void SetHighlightedColor(const OrthancStone::Color& color)
+  {
+    GetHighlightedColorInternal() = color;
+  }
 };
 
 
@@ -4240,13 +4282,24 @@
 private:
   // The coordinates of OsiriX annotations are expressed in 3D world coordinates
   OrthancStone::OsiriX::CollectionOfAnnotations  annotations_;
+  OrthancStone::Color                            color_;
 
 public:
+  OsiriXLayerSource() :
+    color_(0, 255, 0)
+  {
+  }
+
   OrthancStone::OsiriX::CollectionOfAnnotations& GetAnnotations()
   {
     return annotations_;
   }
 
+  void SetColor(const OrthancStone::Color& color)
+  {
+    color_ = color;
+  }
+
   virtual int GetDepth() const ORTHANC_OVERRIDE
   {
     return LAYER_ANNOTATIONS_OSIRIX;
@@ -4268,7 +4321,7 @@
       // layer->Reserve(a.size());
 
       OrthancStone::OsiriXLayerFactory factory;
-      factory.SetColor(0, 255, 0);
+      factory.SetColor(color_);
 
       for (std::set<size_t>::const_iterator it = a.begin(); it != a.end(); ++it)
       {
@@ -4754,6 +4807,21 @@
 };
 
 
+
+static void SetAnnotationsColor(const OrthancStone::Color& color)
+{
+  ViewerViewport::SetAnnotationsColor(color);
+  DicomStructuredReportFrames::SetAnnotationsColor(color);
+  osiriXLayerSource_->SetColor(color);
+}
+
+
+static void SetHighlightedColor(const OrthancStone::Color& color)
+{
+  ViewerViewport::SetHighlightedColor(color);
+}
+
+
 extern "C"
 {
   int main(int argc, char const *argv[]) 
@@ -4774,6 +4842,9 @@
     osiriXLayerSource_.reset(new OsiriXLayerSource);
     orientationMarkersSource_.reset(new OrientationMarkersSource);
 
+    SetAnnotationsColor(OrthancStone::Color(0x40, 0x82, 0xad));  // This was COLOR_PRIMITIVES until 2.6
+    SetHighlightedColor(OrthancStone::Color(0x40, 0xad, 0x79));  // This was COLOR_HOVER until 2.6
+
     for (size_t i = 0; pluginsInitializers_[i] != NULL; i++)
     {
       std::unique_ptr<IStoneWebViewerPlugin> plugin(pluginsInitializers_[i] (StoneWebViewerContext::GetInstance()));
@@ -5557,6 +5628,34 @@
 
 
   EMSCRIPTEN_KEEPALIVE
+  void SetAnnotationsColor(int red,
+                           int green,
+                           int blue)
+  {
+    try
+    {
+      OrthancStone::Color color(red, green, blue);
+      SetAnnotationsColor(color);
+    }
+    EXTERN_CATCH_EXCEPTIONS;
+  }
+
+
+  EMSCRIPTEN_KEEPALIVE
+  void SetHighlightedAnnotationsColor(int red,
+                                      int green,
+                                      int blue)
+  {
+    try
+    {
+      OrthancStone::Color color(red, green, blue);
+      SetHighlightedColor(color);
+    }
+    EXTERN_CATCH_EXCEPTIONS;
+  }
+
+
+  EMSCRIPTEN_KEEPALIVE
   void *Allocate(size_t size)
   {
     return malloc(size);
--- a/OrthancStone/Sources/Scene2D/AnnotationsSceneLayer.cpp	Wed Apr 23 09:31:59 2025 +0200
+++ b/OrthancStone/Sources/Scene2D/AnnotationsSceneLayer.cpp	Wed Apr 23 13:14:42 2025 +0200
@@ -64,18 +64,92 @@
 static const char* const VALUE_TEXT_ANNOTATION = "text";
 
 #if 0
-static OrthancStone::Color COLOR_PRIMITIVES(192, 192, 192);
-static OrthancStone::Color COLOR_HOVER(0, 255, 0);
-static OrthancStone::Color COLOR_TEXT(255, 0, 0);
-#else
+// Color codes that were used in Stone Web viewer <= 2.6
 static OrthancStone::Color COLOR_PRIMITIVES(0x40, 0x82, 0xad);
 static OrthancStone::Color COLOR_HOVER(0x40, 0xad, 0x79);
-static OrthancStone::Color COLOR_TEXT(0x4e, 0xde, 0x99);
+static OrthancStone::Color COLOR_TEXT(0x4e, 0xde, 0x99);   // This was replaced by COLOR_HOVER in 2.7
 #endif
 
 
 namespace OrthancStone
 {
+  class AnnotationsSceneLayer::Annotation : public boost::noncopyable
+  {
+  private:
+    typedef std::list<GeometricPrimitive*>  GeometricPrimitives;
+
+    AnnotationsSceneLayer&  that_;
+    GeometricPrimitives     primitives_;
+    Color                   color_;
+    Color                   hoverColor_;
+
+  public:
+    explicit Annotation(AnnotationsSceneLayer& that) :
+      that_(that),
+      color_(that.GetColor()),
+      hoverColor_(that.GetHoverColor())
+    {
+      that.AddAnnotation(this);
+    }
+
+    virtual ~Annotation()
+    {
+      for (GeometricPrimitives::iterator it = primitives_.begin(); it != primitives_.end(); ++it)
+      {
+        that_.DeletePrimitive(*it);
+      }
+    }
+
+    AnnotationsSceneLayer& GetParentLayer() const
+    {
+      return that_;
+    }
+
+    GeometricPrimitive* AddPrimitive(GeometricPrimitive* primitive)
+    {
+      if (primitive == NULL)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+      }
+      else
+      {
+        assert(that_.primitives_.find(primitive) == that_.primitives_.end());
+        primitives_.push_back(primitive);  // For automated deallocation
+        that_.primitives_.insert(primitive);
+        return primitive;
+      }
+    }
+
+    const Color& GetColor() const
+    {
+      return color_;
+    }
+
+    const Color& GetHoverColor() const
+    {
+      return hoverColor_;
+    }
+
+    template <typename T>
+    T& AddTypedPrimitive(T* primitive)
+    {
+      AddPrimitive(primitive);
+      return *primitive;
+    }
+
+    virtual unsigned int GetHandlesCount() const = 0;
+
+    virtual Handle& GetHandle(unsigned int index) const = 0;
+
+    virtual void SignalMove(GeometricPrimitive& primitive,
+                            const Scene2D& scene) = 0;
+
+    virtual void UpdateProbe(const Scene2D& scene) = 0;
+
+    virtual void Serialize(Json::Value& target) = 0;
+  };
+
+
   class AnnotationsSceneLayer::GeometricPrimitive : public boost::noncopyable
   {
   private:
@@ -91,8 +165,8 @@
                        int depth) :
       modified_(true),
       parentAnnotation_(parentAnnotation),
-      color_(COLOR_PRIMITIVES),
-      hoverColor_(COLOR_HOVER),
+      color_(parentAnnotation.GetColor()),
+      hoverColor_(parentAnnotation.GetHoverColor()),
       isHover_(false),
       depth_(depth)
     {
@@ -182,69 +256,6 @@
   };
     
 
-  class AnnotationsSceneLayer::Annotation : public boost::noncopyable
-  {
-  private:
-    typedef std::list<GeometricPrimitive*>  GeometricPrimitives;
-      
-    AnnotationsSceneLayer&  that_;
-    GeometricPrimitives     primitives_;
-      
-  public:
-    explicit Annotation(AnnotationsSceneLayer& that) :
-      that_(that)
-    {
-      that.AddAnnotation(this);
-    }
-      
-    virtual ~Annotation()
-    {
-      for (GeometricPrimitives::iterator it = primitives_.begin(); it != primitives_.end(); ++it)
-      {
-        that_.DeletePrimitive(*it);
-      }
-    }
-
-    AnnotationsSceneLayer& GetParentLayer() const
-    {
-      return that_;
-    }
-
-    GeometricPrimitive* AddPrimitive(GeometricPrimitive* primitive)
-    {
-      if (primitive == NULL)
-      {
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
-      }
-      else
-      {
-        assert(that_.primitives_.find(primitive) == that_.primitives_.end());
-        primitives_.push_back(primitive);  // For automated deallocation
-        that_.primitives_.insert(primitive);
-        return primitive;
-      }
-    }
-
-    template <typename T>
-    T& AddTypedPrimitive(T* primitive)
-    {
-      AddPrimitive(primitive);
-      return *primitive;
-    }
-
-    virtual unsigned int GetHandlesCount() const = 0;
-
-    virtual Handle& GetHandle(unsigned int index) const = 0;
-
-    virtual void SignalMove(GeometricPrimitive& primitive,
-                            const Scene2D& scene) = 0;
-
-    virtual void UpdateProbe(const Scene2D& scene) = 0;
-
-    virtual void Serialize(Json::Value& target) = 0;
-  };
-
-
   class AnnotationsSceneLayer::Handle : public GeometricPrimitive
   {
   public:
@@ -1123,7 +1134,7 @@
       segment_(AddTypedPrimitive<Segment>(new Segment(*this, p1, p2))),
       label_(AddTypedPrimitive<Text>(new Text(that, *this)))
     {
-      label_.SetColor(COLOR_TEXT);
+      label_.SetColor(that.GetHoverColor());
     }
 
     virtual unsigned int GetHandlesCount() const ORTHANC_OVERRIDE
@@ -1494,7 +1505,7 @@
       content.SetText("?");
 
       label_.SetContent(content);      
-      label_.SetColor(COLOR_TEXT);
+      label_.SetColor(that.GetHoverColor());
     }
 
     virtual unsigned int GetHandlesCount() const ORTHANC_OVERRIDE
@@ -1602,7 +1613,7 @@
       arc_(AddTypedPrimitive<Arc>(new Arc(*this, start, middle, end))),
       label_(AddTypedPrimitive<Text>(new Text(that, *this)))
     {
-      label_.SetColor(COLOR_TEXT);
+      label_.SetColor(that.GetHoverColor());
       UpdateLabel();
     }
 
@@ -1793,7 +1804,7 @@
       circle_(AddTypedPrimitive<Circle>(new Circle(*this, p1, p2))),
       label_(AddTypedPrimitive<Text>(new Text(that, *this)))
     {
-      label_.SetColor(COLOR_TEXT);
+      label_.SetColor(that.GetHoverColor());
       UpdateLabel();
     }
 
@@ -2007,7 +2018,7 @@
       content.SetText("?");
 
       label_.SetContent(content);
-      label_.SetColor(COLOR_TEXT);
+      label_.SetColor(that.GetHoverColor());
     }
 
     virtual unsigned int GetHandlesCount() const ORTHANC_OVERRIDE
@@ -2264,7 +2275,7 @@
       content.SetText("?");
 
       label_.SetContent(content);
-      label_.SetColor(COLOR_TEXT);
+      label_.SetColor(that.GetHoverColor());
     }
 
     virtual unsigned int GetHandlesCount() const ORTHANC_OVERRIDE
@@ -2655,7 +2666,9 @@
     macroLayerIndex_(macroLayerIndex),
     polylineSubLayer_(0),  // dummy initialization
     units_(Units_Pixels),
-    probedLayer_(0)
+    probedLayer_(0),
+    color_(0, 255, 0),
+    hoverColor_(255, 0, 0)
   {
   }
     
--- a/OrthancStone/Sources/Scene2D/AnnotationsSceneLayer.h	Wed Apr 23 09:31:59 2025 +0200
+++ b/OrthancStone/Sources/Scene2D/AnnotationsSceneLayer.h	Wed Apr 23 13:14:42 2025 +0200
@@ -22,8 +22,9 @@
 
 
 #include "../Messages/IObservable.h"
+#include "../Scene2DViewport/IFlexiblePointerTracker.h"
+#include "Color.h"
 #include "Scene2D.h"
-#include "../Scene2DViewport/IFlexiblePointerTracker.h"
 
 namespace OrthancStone
 {
@@ -116,6 +117,8 @@
     SubLayers            subLayersToRemove_;
     Units                units_;
     int                  probedLayer_;
+    Color                color_;
+    Color                hoverColor_;
 
     void AddAnnotation(Annotation* annotation);
     
@@ -189,5 +192,25 @@
     void AddTextAnnotation(const std::string& label,
                            const ScenePoint2D& pointedPosition,
                            const ScenePoint2D& labelPosition);
+
+    const Color& GetColor() const
+    {
+      return color_;
+    }
+
+    void SetColor(const Color& color)
+    {
+      color_ = color;
+    }
+
+    const Color& GetHoverColor() const
+    {
+      return hoverColor_;
+    }
+
+    void SetHoverColor(const Color& color)
+    {
+      hoverColor_ = color;
+    }
   };
 }