changeset 841:266e2b0b9abc

better error reporting in DicomStructureSetLoader + fixed POST request logic in WebAssemblyOracle + support for LookupTableTextureSceneLayer in OpenGL (NOT using shaders!) (2 new files) + a few small non-functional changes
author Benjamin Golinvaux <bgo@osimis.io>
date Tue, 11 Jun 2019 15:41:21 +0200
parents 47fc7919977d
children 2b245953b44b 67f9c27214c5
files Framework/Loaders/DicomStructureSetLoader.cpp Framework/Oracle/WebAssemblyOracle.cpp Framework/Scene2D/Internals/CompositorHelper.h Framework/Scene2D/Internals/OpenGLLookupTableTextureRenderer.cpp Framework/Scene2D/Internals/OpenGLLookupTableTextureRenderer.h Framework/Scene2D/OpenGLCompositor.cpp Framework/Scene2D/RotateSceneTracker.h Framework/Toolbox/DicomStructureSet.cpp Resources/CMake/OrthancStoneConfiguration.cmake Samples/Sdl/FusionMprSdl.cpp
diffstat 10 files changed, 268 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
--- a/Framework/Loaders/DicomStructureSetLoader.cpp	Sun Jun 09 18:47:34 2019 +0200
+++ b/Framework/Loaders/DicomStructureSetLoader.cpp	Tue Jun 11 15:41:21 2019 +0200
@@ -92,6 +92,14 @@
           lookup[0]["ID"].type() != Json::stringValue ||
           lookup[0]["Type"].asString() != "Instance")
       {
+        std::stringstream msg;
+        msg << "Unknown resource! message.GetAnswer() = " << message.GetAnswer() << " message.GetAnswerHeaders() = ";
+        for (const auto& it : message.GetAnswerHeaders())
+        {
+          msg << "\nkey: \"" << it.first << "\" value: \"" << it.second << "\"\n";
+        }
+        auto msgStr = msg.str();
+        LOG(ERROR) << msgStr;
         throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource);          
       }
 
@@ -138,6 +146,7 @@
         command->SetMethod(Orthanc::HttpMethod_Post);
         command->SetBody(*it);
         command->SetPayload(new LookupInstance(loader, *it));
+        //LOG(TRACE) << "About to schedule a /tools/lookup POST request. URI = " << command->GetUri() << " Body size = " << (*it).size() << " Body = " << (*it) << "\n";
         Schedule(command.release());
       }
     }
--- a/Framework/Oracle/WebAssemblyOracle.cpp	Sun Jun 09 18:47:34 2019 +0200
+++ b/Framework/Oracle/WebAssemblyOracle.cpp	Tue Jun 11 15:41:21 2019 +0200
@@ -284,7 +284,12 @@
 
     void SetBody(std::string& body /* will be swapped */)
     {
+      if (body != "")
+      {
+        LOG(ERROR) << "Setting non-empty body. body size = " << body.size() << " body = " << body;
+      }
       body_.swap(body);
+      LOG(ERROR) << "After setting non-empty body. body_ size = " << body_.size() << " body_ = " << body_;
     }
 
     void SetHttpHeaders(const HttpHeaders& headers)
@@ -365,19 +370,32 @@
 
       attr.requestHeaders = &headers[0];
 
+      char* requestData = NULL;
       if (!body_.empty())
+        requestData = reinterpret_cast<char*>(malloc(body_.size()));
+        
+      try 
       {
-        attr.requestDataSize = body_.size();
-        attr.requestData = body_.c_str();
-      }
+        if (!body_.empty())
+        {
+          memcpy(requestData, &(body_[0]), body_.size());
+          attr.requestDataSize = body_.size();
+          attr.requestData = requestData;
+        }
+        attr.userData = new FetchContext(oracle_, receiver_, command_.release(), expectedContentType);
 
-      // Must be the last call to prevent memory leak on error
-      attr.userData = new FetchContext(oracle_, receiver_, command_.release(), expectedContentType);
-      emscripten_fetch(&attr, uri_.c_str());
-    }        
+        // Must be the last call to prevent memory leak on error
+        emscripten_fetch(&attr, uri_.c_str());
+      }        
+      catch(...)
+      {
+        if(requestData != NULL)
+          free(requestData);
+        throw;
+      }
+    }
   };
     
-    
   void WebAssemblyOracle::Execute(const IObserver& receiver,
                                   OrthancRestApiCommand* command)
   {
@@ -388,14 +406,14 @@
     fetch.SetHttpHeaders(command->GetHttpHeaders());
     fetch.SetTimeout(command->GetTimeout());
       
-    if (command->GetMethod() == Orthanc::HttpMethod_Put ||
+    if (command->GetMethod() == Orthanc::HttpMethod_Post ||
         command->GetMethod() == Orthanc::HttpMethod_Put)
     {
       std::string body;
       command->SwapBody(body);
       fetch.SetBody(body);
     }
-      
+
     fetch.Execute();
   }
     
--- a/Framework/Scene2D/Internals/CompositorHelper.h	Sun Jun 09 18:47:34 2019 +0200
+++ b/Framework/Scene2D/Internals/CompositorHelper.h	Tue Jun 11 15:41:21 2019 +0200
@@ -23,6 +23,10 @@
 
 #include "../Scene2D.h"
 
+#include <boost/noncopyable.hpp>
+
+#include <map>
+
 namespace OrthancStone
 {
   namespace Internals
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Scene2D/Internals/OpenGLLookupTableTextureRenderer.cpp	Tue Jun 11 15:41:21 2019 +0200
@@ -0,0 +1,140 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "OpenGLLookupTableTextureRenderer.h"
+
+namespace OrthancStone
+{
+  namespace Internals
+  {
+    void OpenGLLookupTableTextureRenderer::LoadTexture(
+      const LookupTableTextureSceneLayer& layer)
+    {
+      const Orthanc::ImageAccessor& source = layer.GetTexture();
+      const unsigned int width = source.GetWidth();
+      const unsigned int height = source.GetHeight();
+
+      if ((texture_.get() == NULL) || 
+          (texture_->GetWidth() != width) || 
+          (texture_->GetHeight() != height))
+      {
+
+        texture_.reset(new Orthanc::Image(
+          Orthanc::PixelFormat_RGBA32,
+          width,
+          height,
+          false));
+      }
+
+      {
+
+        const float a = layer.GetMinValue();
+        float slope = 0;
+
+        if (layer.GetMinValue() >= layer.GetMaxValue())
+        {
+          slope = 0;
+        }
+        else
+        {
+          slope = 256.0f / (layer.GetMaxValue() - layer.GetMinValue());
+        }
+
+        Orthanc::ImageAccessor target;
+        texture_->GetWriteableAccessor(target);
+
+        const std::vector<uint8_t>& lut = layer.GetLookupTable();
+        if (lut.size() != 4 * 256)
+        {
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+        }
+
+        assert(source.GetFormat() == Orthanc::PixelFormat_Float32 &&
+          target.GetFormat() == Orthanc::PixelFormat_RGBA32 &&
+          sizeof(float) == 4);
+
+        for (unsigned int y = 0; y < height; y++)
+        {
+          const float* p = reinterpret_cast<const float*>(source.GetConstRow(y));
+          uint8_t* q = reinterpret_cast<uint8_t*>(target.GetRow(y));
+
+          for (unsigned int x = 0; x < width; x++)
+          {
+            float v = (*p - a) * slope;
+            if (v <= 0)
+            {
+              v = 0;
+            }
+            else if (v >= 255)
+            {
+              v = 255;
+            }
+
+            uint8_t vv = static_cast<uint8_t>(v);
+
+            q[0] = lut[4 * vv + 0];  // R
+            q[1] = lut[4 * vv + 1];  // G
+            q[2] = lut[4 * vv + 2];  // B
+            q[3] = lut[4 * vv + 3];  // A
+
+            p++;
+            q += 4;
+          }
+        }
+      }
+
+      context_.MakeCurrent();
+      glTexture_.reset(new OpenGL::OpenGLTexture);
+      glTexture_->Load(*texture_, layer.IsLinearInterpolation());
+      layerTransform_ = layer.GetTransform();
+    }
+
+    
+    OpenGLLookupTableTextureRenderer::OpenGLLookupTableTextureRenderer(
+      OpenGL::IOpenGLContext&                 context,
+      OpenGLColorTextureProgram&              program,
+      const LookupTableTextureSceneLayer&     layer)
+      : context_(context)
+      , program_(program)
+    {
+      LoadTexture(layer);
+    }
+
+    
+    void OpenGLLookupTableTextureRenderer::Render(const AffineTransform2D& transform)
+    {
+      if (glTexture_.get() != NULL)
+      {
+        program_.Apply(
+          *glTexture_, 
+          AffineTransform2D::Combine(transform, layerTransform_), 
+          true);
+      }
+    }
+
+    
+    void OpenGLLookupTableTextureRenderer::Update(const ISceneLayer& layer)
+    {
+      // Should never happen (no revisions in color textures)
+      LoadTexture(dynamic_cast<const LookupTableTextureSceneLayer&>(layer));
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Scene2D/Internals/OpenGLLookupTableTextureRenderer.h	Tue Jun 11 15:41:21 2019 +0200
@@ -0,0 +1,56 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "OpenGLColorTextureProgram.h"
+#include "CompositorHelper.h"
+#include "../LookupTableTextureSceneLayer.h"
+
+#include <Core/Images/Image.h>
+
+namespace OrthancStone
+{
+  namespace Internals
+  {
+    class OpenGLLookupTableTextureRenderer : public CompositorHelper::ILayerRenderer
+    {
+    private:
+      OpenGL::IOpenGLContext&                context_;
+      OpenGLColorTextureProgram&             program_;
+      std::auto_ptr<OpenGL::OpenGLTexture>   glTexture_;
+      std::auto_ptr<Orthanc::Image>          texture_;
+      AffineTransform2D                      layerTransform_;
+
+      void LoadTexture(const LookupTableTextureSceneLayer& layer);
+
+    public:
+      OpenGLLookupTableTextureRenderer(
+        OpenGL::IOpenGLContext&             context,
+        OpenGLColorTextureProgram&          program,
+        const LookupTableTextureSceneLayer& layer);
+
+      virtual void Render(const AffineTransform2D& transform);
+
+      virtual void Update(const ISceneLayer& layer);
+    };
+  }
+}
--- a/Framework/Scene2D/OpenGLCompositor.cpp	Sun Jun 09 18:47:34 2019 +0200
+++ b/Framework/Scene2D/OpenGLCompositor.cpp	Tue Jun 11 15:41:21 2019 +0200
@@ -26,6 +26,7 @@
 #include "Internals/OpenGLColorTextureRenderer.h"
 #include "Internals/OpenGLFloatTextureRenderer.h"
 #include "Internals/OpenGLInfoPanelRenderer.h"
+#include "Internals/OpenGLLookupTableTextureRenderer.h"
 #include "Internals/OpenGLTextRenderer.h"
 
 namespace OrthancStone
@@ -92,6 +93,10 @@
         return new Internals::OpenGLFloatTextureRenderer
           (context_, floatTextureProgram_, dynamic_cast<const FloatTextureSceneLayer&>(layer));
 
+      case ISceneLayer::Type_LookupTableTexture:
+        return new Internals::OpenGLLookupTableTextureRenderer
+        (context_, colorTextureProgram_, dynamic_cast<const LookupTableTextureSceneLayer&>(layer));
+
       case ISceneLayer::Type_Polyline:
         return new Internals::OpenGLAdvancedPolylineRenderer
           (context_, linesProgram_, dynamic_cast<const PolylineSceneLayer&>(layer));
--- a/Framework/Scene2D/RotateSceneTracker.h	Sun Jun 09 18:47:34 2019 +0200
+++ b/Framework/Scene2D/RotateSceneTracker.h	Tue Jun 11 15:41:21 2019 +0200
@@ -23,6 +23,7 @@
 
 #include "../Scene2DViewport/OneGesturePointerTracker.h"
 #include "Internals/FixedPointAligner.h"
+#include <memory>
 
 namespace OrthancStone
 {
--- a/Framework/Toolbox/DicomStructureSet.cpp	Sun Jun 09 18:47:34 2019 +0200
+++ b/Framework/Toolbox/DicomStructureSet.cpp	Tue Jun 11 15:41:21 2019 +0200
@@ -413,7 +413,7 @@
         countSlices = 0;
       }
 
-      LOG(WARNING) << "New RT structure: \"" << structures_[i].name_ 
+      LOG(INFO) << "New RT structure: \"" << structures_[i].name_ 
                    << "\" with interpretation \"" << structures_[i].interpretation_
                    << "\" containing " << countSlices << " slices (color: " 
                    << static_cast<int>(structures_[i].red_) << "," 
--- a/Resources/CMake/OrthancStoneConfiguration.cmake	Sun Jun 09 18:47:34 2019 +0200
+++ b/Resources/CMake/OrthancStoneConfiguration.cmake	Tue Jun 11 15:41:21 2019 +0200
@@ -535,21 +535,39 @@
 
 if (ENABLE_OPENGL)
   list(APPEND ORTHANC_STONE_SOURCES
+    ${ORTHANC_STONE_ROOT}/Framework/Fonts/OpenGLTextCoordinates.h
     ${ORTHANC_STONE_ROOT}/Framework/Fonts/OpenGLTextCoordinates.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/OpenGL/OpenGLProgram.h
     ${ORTHANC_STONE_ROOT}/Framework/OpenGL/OpenGLProgram.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/OpenGL/OpenGLShader.h
     ${ORTHANC_STONE_ROOT}/Framework/OpenGL/OpenGLShader.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/OpenGL/OpenGLTexture.h
     ${ORTHANC_STONE_ROOT}/Framework/OpenGL/OpenGLTexture.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Scene2D/OpenGLCompositor.h
     ${ORTHANC_STONE_ROOT}/Framework/Scene2D/OpenGLCompositor.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/OpenGLAdvancedPolylineRenderer.h
     ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/OpenGLAdvancedPolylineRenderer.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/OpenGLBasicPolylineRenderer.h
     ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/OpenGLBasicPolylineRenderer.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/OpenGLColorTextureProgram.h
     ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/OpenGLColorTextureProgram.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/OpenGLColorTextureRenderer.h
     ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/OpenGLColorTextureRenderer.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/OpenGLFloatTextureProgram.h
     ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/OpenGLFloatTextureProgram.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/OpenGLFloatTextureRenderer.h
     ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/OpenGLFloatTextureRenderer.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/OpenGLInfoPanelRenderer.h
     ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/OpenGLInfoPanelRenderer.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/OpenGLLinesProgram.h
     ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/OpenGLLinesProgram.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/OpenGLLookupTableTextureRenderer.h
+    ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/OpenGLLookupTableTextureRenderer.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/OpenGLTextProgram.h
     ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/OpenGLTextProgram.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/OpenGLTextRenderer.h
     ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/OpenGLTextRenderer.cpp
+    ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/OpenGLTextureProgram.h
     ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/OpenGLTextureProgram.cpp
     )
 
--- a/Samples/Sdl/FusionMprSdl.cpp	Sun Jun 09 18:47:34 2019 +0200
+++ b/Samples/Sdl/FusionMprSdl.cpp	Tue Jun 11 15:41:21 2019 +0200
@@ -715,6 +715,11 @@
             window.GetWindow().ToggleMaximize();
             break;
 
+          case SDLK_s:
+            controller_->FitContent(
+              window.GetCanvasWidth(), window.GetCanvasHeight());
+            break;
+
           case SDLK_q:
             g_stopApplication = true;
             break;
@@ -733,7 +738,7 @@
 
     //// from loader
 
-    Orthanc::SystemToolbox::ServerBarrier();
+    //Orthanc::SystemToolbox::ServerBarrier();
 
     /**
      * WARNING => The oracle must be stopped BEFORE the objects using