changeset 1196:a5f2a6b04a31

RadiographyScene: windowing is now only applied to the Dicom layer
author Alain Mazy <alain@mazy.be>
date Wed, 27 Nov 2019 17:51:33 +0100
parents c6a36ecd641d
children a34ba19d2060
files Applications/Samples/SingleFrameEditorApplication.h Framework/Radiography/RadiographyAlphaLayer.cpp Framework/Radiography/RadiographyAlphaLayer.h Framework/Radiography/RadiographyDicomLayer.cpp Framework/Radiography/RadiographyDicomLayer.h Framework/Radiography/RadiographyLayer.h Framework/Radiography/RadiographyMaskLayer.cpp Framework/Radiography/RadiographyMaskLayer.h Framework/Radiography/RadiographyScene.cpp Framework/Radiography/RadiographyScene.h Framework/Radiography/RadiographySceneWriter.cpp Framework/Radiography/RadiographyTextLayer.cpp Framework/Radiography/RadiographyTextLayer.h Framework/Radiography/RadiographyWidget.cpp
diffstat 14 files changed, 204 insertions(+), 147 deletions(-) [+]
line wrap: on
line diff
--- a/Applications/Samples/SingleFrameEditorApplication.h	Tue Nov 26 16:32:29 2019 +0100
+++ b/Applications/Samples/SingleFrameEditorApplication.h	Wed Nov 27 17:51:33 2019 +0100
@@ -38,7 +38,6 @@
 #include "../../Framework/Toolbox/TextRenderer.h"
 
 #include <Core/HttpClient.h>
-#include <Core/Images/FontRegistry.h>
 #include <Core/Logging.h>
 #include <Core/OrthancException.h>
 #include <Core/Images/PngWriter.h>
@@ -318,11 +317,6 @@
 
           boost::shared_ptr<RadiographyScene> scene(new RadiographyScene(GetBroker()));
           RadiographySceneReader reader(*scene, context_->GetOrthancApiClient());
-
-          Orthanc::FontRegistry fontRegistry;
-          fontRegistry.AddFromResource(Orthanc::EmbeddedResources::FONT_UBUNTU_MONO_BOLD_16);
-
-          reader.SetFontRegistry(fontRegistry);
           reader.Read(snapshot);
 
           widget.SetScene(scene);
@@ -493,8 +487,6 @@
         std::string instance = parameters["instance"].as<std::string>();
         //int frame = parameters["frame"].as<unsigned int>();
 
-        fontRegistry_.AddFromResource(Orthanc::EmbeddedResources::FONT_UBUNTU_MONO_BOLD_16);
-        
         scene_.reset(new RadiographyScene(GetBroker()));
         
         RadiographyLayer& dicomLayer = scene_->LoadDicomFrame(context->GetOrthancApiClient(), instance, 0, false, NULL);
@@ -520,11 +512,12 @@
           std::auto_ptr<Orthanc::ImageAccessor> renderedTextAlpha(TextRenderer::Render(Orthanc::EmbeddedResources::UBUNTU_FONT, 100,
                                                                                     "%öÇaA&#"));
           RadiographyLayer& layer = scene_->LoadAlphaBitmap(renderedTextAlpha.release(), NULL);
-          dynamic_cast<RadiographyAlphaLayer&>(layer).SetForegroundValue(200);
+          dynamic_cast<RadiographyAlphaLayer&>(layer).SetForegroundValue(200.0f * 256.0f);
         }
 
         {
-          RadiographyLayer& layer = scene_->LoadText(fontRegistry_.GetFont(0), "Hello\nworld", NULL);
+          RadiographyTextLayer::SetFont(Orthanc::EmbeddedResources::UBUNTU_FONT);
+          RadiographyLayer& layer = scene_->LoadText("Hello\nworld", 20, 128, NULL);
           layer.SetResizeable(true);
         }
         
--- a/Framework/Radiography/RadiographyAlphaLayer.cpp	Tue Nov 26 16:32:29 2019 +0100
+++ b/Framework/Radiography/RadiographyAlphaLayer.cpp	Wed Nov 27 17:51:33 2019 +0100
@@ -25,6 +25,7 @@
 
 #include <Core/Images/Image.h>
 #include <Core/OrthancException.h>
+#include "../Toolbox/ImageGeometry.h"
 
 namespace OrthancStone
 {
@@ -51,7 +52,10 @@
 
   void RadiographyAlphaLayer::Render(Orthanc::ImageAccessor& buffer,
                                      const AffineTransform2D& viewTransform,
-                                     ImageInterpolation interpolation) const
+                                     ImageInterpolation interpolation,
+                                     float windowCenter,
+                                     float windowWidth,
+                                     bool applyWindowing) const
   {
     if (alpha_.get() == NULL)
     {
@@ -77,27 +81,37 @@
 
     t.Apply(tmp, cropped, interpolation, true /* clear */);
 
-    // Blit
-    const unsigned int width = buffer.GetWidth();
-    const unsigned int height = buffer.GetHeight();
+    unsigned int x1, y1, x2, y2;
+    OrthancStone::GetProjectiveTransformExtent(x1, y1, x2, y2,
+                                               t.GetHomogeneousMatrix(),
+                                               cropped.GetWidth(),
+                                               cropped.GetHeight(),
+                                               buffer.GetWidth(),
+                                               buffer.GetHeight());
 
     float value = foreground_;
 
-    if (useWindowing_)
+    if (!applyWindowing) // if applying the windowing, it means we are ie rendering the image for a realtime visualization -> the foreground_ value is the value we want to see on the screen -> don't change it
     {
-      float center, width;
-      if (GetScene().GetWindowing(center, width))
+      // if not applying the windowing, it means ie that we are saving a dicom image to file and the windowing will be applied by a viewer later on -> we want the "foreground" value to be correct once the windowing will be applied
+      value = windowCenter - windowWidth/2 + (foreground_ / 65535.0f) * windowWidth;
+
+      if (value < 0.0f)
       {
-        value = center + width / 2.0f;  // set it to the maximum pixel value of the image
+        value = 0.0f;
+      }
+      if (value > 65535.0f)
+      {
+        value = 65535.0f;
       }
     }
 
-    for (unsigned int y = 0; y < height; y++)
+    for (unsigned int y = y1; y <= y2; y++)
     {
-      float *q = reinterpret_cast<float*>(buffer.GetRow(y));
-      const uint8_t *p = reinterpret_cast<uint8_t*>(tmp.GetRow(y));
+      float *q = reinterpret_cast<float*>(buffer.GetRow(y)) + x1;
+      const uint8_t *p = reinterpret_cast<uint8_t*>(tmp.GetRow(y)) + x1;
 
-      for (unsigned int x = 0; x < width; x++, p++, q++)
+      for (unsigned int x = x1; x <= x2; x++, p++, q++)
       {
         float a = static_cast<float>(*p) / 255.0f;
 
@@ -109,26 +123,19 @@
   bool RadiographyAlphaLayer::GetRange(float& minValue,
                                        float& maxValue) const
   {
-    if (useWindowing_)
-    {
-      return false;
-    }
-    else
-    {
-      minValue = 0;
-      maxValue = 0;
+    minValue = 0;
+    maxValue = 0;
 
-      if (foreground_ < 0)
-      {
-        minValue = foreground_;
-      }
+    if (foreground_ < 0)
+    {
+      minValue = foreground_;
+    }
 
-      if (foreground_ > 0)
-      {
-        maxValue = foreground_;
-      }
+    if (foreground_ > 0)
+    {
+      maxValue = foreground_;
+    }
 
-      return true;
-    }
+    return true;
   }
 }
--- a/Framework/Radiography/RadiographyAlphaLayer.h	Tue Nov 26 16:32:29 2019 +0100
+++ b/Framework/Radiography/RadiographyAlphaLayer.h	Wed Nov 27 17:51:33 2019 +0100
@@ -33,14 +33,12 @@
   class RadiographyAlphaLayer : public RadiographyLayer
   {
   private:
-    std::auto_ptr<Orthanc::ImageAccessor>  alpha_;      // Grayscale8
-    bool                                   useWindowing_;
-    float                                  foreground_;
+    std::auto_ptr<Orthanc::ImageAccessor>  alpha_;       // Grayscale8 in the range [0, 255]  0 = transparent, 255 = opaque -> the foreground value will be displayed
+    float                                  foreground_;  // in the range [0.0, 65535.0]
 
   public:
     RadiographyAlphaLayer(MessageBroker& broker, const RadiographyScene& scene) :
       RadiographyLayer(broker, scene),
-      useWindowing_(true),
       foreground_(0)
     {
     }
@@ -48,7 +46,6 @@
 
     void SetForegroundValue(float foreground)
     {
-      useWindowing_ = false;
       foreground_ = foreground;
     }
 
@@ -57,11 +54,6 @@
       return foreground_;
     }
 
-    bool IsUsingWindowing() const
-    {
-      return useWindowing_;
-    }
-
     void SetAlpha(Orthanc::ImageAccessor* image);
 
     virtual bool GetDefaultWindowing(float& center,
@@ -73,7 +65,10 @@
 
     virtual void Render(Orthanc::ImageAccessor& buffer,
                         const AffineTransform2D& viewTransform,
-                        ImageInterpolation interpolation) const;
+                        ImageInterpolation interpolation,
+                        float windowCenter,
+                        float windowWidth,
+                        bool applyWindowing) const;
 
     virtual bool GetRange(float& minValue,
                           float& maxValue) const;
--- a/Framework/Radiography/RadiographyDicomLayer.cpp	Tue Nov 26 16:32:29 2019 +0100
+++ b/Framework/Radiography/RadiographyDicomLayer.cpp	Wed Nov 27 17:51:33 2019 +0100
@@ -28,6 +28,7 @@
 #include <Core/Images/Image.h>
 #include <Core/Images/ImageProcessing.h>
 #include <Plugins/Samples/Common/DicomDatasetReader.h>
+#include "../Toolbox/ImageGeometry.h"
 
 static OrthancPlugins::DicomTag  ConvertTag(const Orthanc::DicomTag& tag)
 {
@@ -138,7 +139,10 @@
 
   void RadiographyDicomLayer::Render(Orthanc::ImageAccessor& buffer,
                                      const AffineTransform2D& viewTransform,
-                                     ImageInterpolation interpolation) const
+                                     ImageInterpolation interpolation,
+                                     float windowCenter,
+                                     float windowWidth,
+                                     bool applyWindowing) const
   {
     if (converted_.get() != NULL)
     {
@@ -158,6 +162,47 @@
       converted_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight);
 
       t.Apply(buffer, cropped, interpolation, false);
+      unsigned int x1, y1, x2, y2;
+      OrthancStone::GetProjectiveTransformExtent(x1, y1, x2, y2,
+                                                 t.GetHomogeneousMatrix(),
+                                                 cropped.GetWidth(),
+                                                 cropped.GetHeight(),
+                                                 buffer.GetWidth(),
+                                                 buffer.GetHeight());
+
+      if (applyWindowing)
+      {
+        // apply windowing but stay in the range [0.0, 65535.0]
+        float w0 = windowCenter - windowWidth / 2.0f;
+        float w1 = windowCenter + windowWidth / 2.0f;
+
+        if (windowWidth >= 0.001f)  // Avoid division by zero at (*)
+        {
+          float scaling = 1.0f / (w1 - w0) * 65535.0f;
+          for (unsigned int y = y1; y <= y2; y++)
+          {
+            float* p = reinterpret_cast<float*>(buffer.GetRow(y)) + x1;
+
+            for (unsigned int x = x1; x <= x2; x++, p++)
+            {
+              if (*p >= w1)
+              {
+                *p = 65535.0;
+              }
+              else if (*p <= w0)
+              {
+                *p = 0;
+              }
+              else
+              {
+                // https://en.wikipedia.org/wiki/Linear_interpolation
+                *p = scaling * (*p - w0);  // (*)
+              }
+            }
+          }
+        }
+      }
+
     }
   }
 
--- a/Framework/Radiography/RadiographyDicomLayer.h	Tue Nov 26 16:32:29 2019 +0100
+++ b/Framework/Radiography/RadiographyDicomLayer.h	Wed Nov 27 17:51:33 2019 +0100
@@ -91,7 +91,10 @@
 
     virtual void Render(Orthanc::ImageAccessor& buffer,
                         const AffineTransform2D& viewTransform,
-                        ImageInterpolation interpolation) const;
+                        ImageInterpolation interpolation,
+                        float windowCenter,
+                        float windowWidth,
+                        bool applyWindowing) const;
 
     virtual bool GetDefaultWindowing(float& center,
                                      float& width) const;
--- a/Framework/Radiography/RadiographyLayer.h	Tue Nov 26 16:32:29 2019 +0100
+++ b/Framework/Radiography/RadiographyLayer.h	Wed Nov 27 17:51:33 2019 +0100
@@ -350,7 +350,10 @@
 
     virtual void Render(Orthanc::ImageAccessor& buffer,
                         const AffineTransform2D& viewTransform,
-                        ImageInterpolation interpolation) const = 0;
+                        ImageInterpolation interpolation,
+                        float windowCenter,
+                        float windowWidth,
+                        bool applyWindowing) const = 0;
 
     virtual bool GetRange(float& minValue,
                           float& maxValue) const = 0;
--- a/Framework/Radiography/RadiographyMaskLayer.cpp	Tue Nov 26 16:32:29 2019 +0100
+++ b/Framework/Radiography/RadiographyMaskLayer.cpp	Wed Nov 27 17:51:33 2019 +0100
@@ -26,10 +26,11 @@
 #include "Core/Images/Image.h"
 #include "Core/Images/ImageProcessing.h"
 #include <Core/OrthancException.h>
+#include "../Toolbox/ImageGeometry.h"
 
 namespace OrthancStone
 {
-  const unsigned char IN_MASK_VALUE = 0x00;
+  const unsigned char IN_MASK_VALUE = 0x77;
   const unsigned char OUT_MASK_VALUE = 0xFF;
 
   const AffineTransform2D& RadiographyMaskLayer::GetTransform() const
@@ -76,7 +77,10 @@
 
   void RadiographyMaskLayer::Render(Orthanc::ImageAccessor& buffer,
                                     const AffineTransform2D& viewTransform,
-                                    ImageInterpolation interpolation) const
+                                    ImageInterpolation interpolation,
+                                    float windowCenter,
+                                    float windowWidth,
+                                    bool applyWindowing) const
   {
     if (dicomLayer_.GetWidth() == 0) // nothing to do if the DICOM layer is not displayed (or not loaded)
       return;
@@ -108,20 +112,36 @@
 
       Orthanc::Image tmp(Orthanc::PixelFormat_Grayscale8, buffer.GetWidth(), buffer.GetHeight(), false);
 
-      t.Apply(tmp, cropped, interpolation, true /* clear */);
+      t.Apply(tmp, cropped, ImageInterpolation_Nearest, true /* clear */);
+
+      unsigned int x1, y1, x2, y2;
+      OrthancStone::GetProjectiveTransformExtent(x1, y1, x2, y2,
+                                                 t.GetHomogeneousMatrix(),
+                                                 cropped.GetWidth(),
+                                                 cropped.GetHeight(),
+                                                 buffer.GetWidth(),
+                                                 buffer.GetHeight());
+
+      // we have observed vertical lines at the image border (probably due to bilinear filtering of the DICOM image when it is not aligned with the buffer pixels)
+      // -> draw the mask one line further on each side
+      if (x1 >= 1)
+      {
+        x1 = x1 - 1;
+      }
+      if (x2 < buffer.GetWidth() - 2)
+      {
+        x2 = x2 + 1;
+      }
 
       // Blit
-      const unsigned int width = buffer.GetWidth();
-      const unsigned int height = buffer.GetHeight();
-
-      for (unsigned int y = 0; y < height; y++)
+      for (unsigned int y = y1; y <= y2; y++)
       {
-        float *q = reinterpret_cast<float*>(buffer.GetRow(y));
-        const uint8_t *p = reinterpret_cast<uint8_t*>(tmp.GetRow(y));
+        float *q = reinterpret_cast<float*>(buffer.GetRow(y)) + x1;
+        const uint8_t *p = reinterpret_cast<uint8_t*>(tmp.GetRow(y)) + x1;
 
-        for (unsigned int x = 0; x < width; x++, p++, q++)
+        for (unsigned int x = x1; x <= x2; x++, p++, q++)
         {
-          if (*p == OUT_MASK_VALUE)
+          if (*p != IN_MASK_VALUE)
             *q = foreground_;
           // else keep the underlying pixel value
         }
--- a/Framework/Radiography/RadiographyMaskLayer.h	Tue Nov 26 16:32:29 2019 +0100
+++ b/Framework/Radiography/RadiographyMaskLayer.h	Wed Nov 27 17:51:33 2019 +0100
@@ -76,7 +76,10 @@
 
     virtual void Render(Orthanc::ImageAccessor& buffer,
                         const AffineTransform2D& viewTransform,
-                        ImageInterpolation interpolation) const;
+                        ImageInterpolation interpolation,
+                        float windowCenter,
+                        float windowWidth,
+                        bool applyWindowing) const;
 
     std::string GetInstanceId() const;
 
--- a/Framework/Radiography/RadiographyScene.cpp	Tue Nov 26 16:32:29 2019 +0100
+++ b/Framework/Radiography/RadiographyScene.cpp	Wed Nov 27 17:51:33 2019 +0100
@@ -283,7 +283,7 @@
 
 
   RadiographyLayer& RadiographyScene::LoadText(const std::string& utf8,
-                                               size_t fontSize,
+                                               unsigned int fontSize,
                                                uint8_t foreground,
                                                RadiographyLayer::Geometry* geometry)
   {
@@ -509,7 +509,8 @@
 
   void RadiographyScene::Render(Orthanc::ImageAccessor& buffer,
                                 const AffineTransform2D& viewTransform,
-                                ImageInterpolation interpolation) const
+                                ImageInterpolation interpolation,
+                                bool applyWindowing) const
   {
     // Render layers in the background-to-foreground order
     for (size_t index = 0; index < countLayers_; index++)
@@ -518,7 +519,7 @@
       if (it != layers_.end())
       {
         assert(it->second != NULL);
-        it->second->Render(buffer, viewTransform, interpolation);
+        it->second->Render(buffer, viewTransform, interpolation, windowingCenter_, windowingWidth_, applyWindowing);
       }
     }
   }
@@ -599,7 +600,8 @@
                                                   double pixelSpacingY,
                                                   ImageInterpolation interpolation,
                                                   bool invert,
-                                                  int64_t maxValue /* for inversion */)
+                                                  int64_t maxValue /* for inversion */,
+                                                  bool applyWindowing)
   {
     if (pixelSpacingX <= 0 ||
         pixelSpacingY <= 0)
@@ -628,7 +630,7 @@
     // wipe background before rendering
     Orthanc::ImageProcessing::Set(layers, 0);
 
-    Render(layers, view, interpolation);
+    Render(layers, view, interpolation, applyWindowing);
 
     std::auto_ptr<Orthanc::Image> rendered(new Orthanc::Image(Orthanc::PixelFormat_Grayscale16,
                                                               layers.GetWidth(), layers.GetHeight(), false));
@@ -651,7 +653,7 @@
   {
     LOG(INFO) << "Exporting RadiographyScene to DICOM";
 
-    std::auto_ptr<Orthanc::Image> rendered(ExportToImage(pixelSpacingX, pixelSpacingY, interpolation)); // note: we don't invert the image in the pixels data because we'll set the PhotometricDisplayMode correctly in the DICOM tags
+    std::auto_ptr<Orthanc::Image> rendered(ExportToImage(pixelSpacingX, pixelSpacingY, interpolation, false)); // note: we don't invert the image in the pixels data because we'll set the PhotometricDisplayMode correctly in the DICOM tags
 
     createDicomRequestContent["Tags"] = dicomTags;
 
--- a/Framework/Radiography/RadiographyScene.h	Tue Nov 26 16:32:29 2019 +0100
+++ b/Framework/Radiography/RadiographyScene.h	Wed Nov 27 17:51:33 2019 +0100
@@ -198,7 +198,7 @@
     RadiographyPhotometricDisplayMode GetPreferredPhotomotricDisplayMode() const;
 
     RadiographyLayer& LoadText(const std::string& utf8,
-                               size_t fontSize,
+                               unsigned int fontSize,
                                uint8_t foreground,
                                RadiographyLayer::Geometry* geometry);
     
@@ -287,7 +287,8 @@
 
     virtual void Render(Orthanc::ImageAccessor& buffer,
                         const AffineTransform2D& viewTransform,
-                        ImageInterpolation interpolation) const;
+                        ImageInterpolation interpolation,
+                        bool applyWindowing) const;
 
     bool LookupLayer(size_t& index /* out */,
                      double x,
@@ -339,15 +340,17 @@
 
     Orthanc::Image* ExportToImage(double pixelSpacingX,
                                   double pixelSpacingY,
-                                  ImageInterpolation interpolation)
+                                  ImageInterpolation interpolation,
+                                  bool applyWindowing)
     {
-      return ExportToImage(pixelSpacingX, pixelSpacingY, interpolation, false, 0);
+      return ExportToImage(pixelSpacingX, pixelSpacingY, interpolation, false, 0, applyWindowing);
     }
 
     Orthanc::Image* ExportToImage(double pixelSpacingX,
                                   double pixelSpacingY,
                                   ImageInterpolation interpolation,
                                   bool invert,
-                                  int64_t maxValue /* for inversion */);
+                                  int64_t maxValue /* for inversion */,
+                                  bool applyWindowing);  // i.e.: when
   };
 }
--- a/Framework/Radiography/RadiographySceneWriter.cpp	Tue Nov 26 16:32:29 2019 +0100
+++ b/Framework/Radiography/RadiographySceneWriter.cpp	Wed Nov 27 17:51:33 2019 +0100
@@ -97,7 +97,6 @@
     Orthanc::Toolbox::EncodeDataUriScheme(pngContentBase64, "image/png", pngContent);
     output["content"] = pngContentBase64;
     output["foreground"] = layer.GetForegroundValue();
-    output["isUsingWindowing"] = layer.IsUsingWindowing();
   }
 
   void RadiographySceneWriter::WriteLayer(Json::Value& output, const RadiographyLayer& layer)
--- a/Framework/Radiography/RadiographyTextLayer.cpp	Tue Nov 26 16:32:29 2019 +0100
+++ b/Framework/Radiography/RadiographyTextLayer.cpp	Wed Nov 27 17:51:33 2019 +0100
@@ -30,7 +30,7 @@
   Orthanc::EmbeddedResources::FileResourceId RadiographyTextLayer::fontResourceId_;
 
   void RadiographyTextLayer::LoadText(const std::string& utf8,
-                                      size_t fontSize,
+                                      unsigned int fontSize,
                                       uint8_t foreground)
   {
     if (!fontHasBeenConfigured_)
@@ -45,5 +45,6 @@
     SetAlpha(TextRenderer::Render(fontResourceId_,
                                   fontSize_,
                                   text_));
+    SetForegroundValue(foreground * 256.0f);
   }
 }
--- a/Framework/Radiography/RadiographyTextLayer.h	Tue Nov 26 16:32:29 2019 +0100
+++ b/Framework/Radiography/RadiographyTextLayer.h	Wed Nov 27 17:51:33 2019 +0100
@@ -31,7 +31,7 @@
   {
   private:
     std::string                 text_;
-    size_t                      fontSize_;
+    unsigned int                fontSize_;
     uint8_t                     foreground_;
 
     static bool                                       fontHasBeenConfigured_;
@@ -42,14 +42,14 @@
     {
     }
 
-    void LoadText(const std::string& utf8, size_t fontSize, uint8_t foreground);
+    void LoadText(const std::string& utf8, unsigned int fontSize, uint8_t foreground);
 
     const std::string& GetText() const
     {
       return text_;
     }
 
-    size_t GetFontSize() const
+    unsigned int GetFontSize() const
     {
       return fontSize_;
     }
--- a/Framework/Radiography/RadiographyWidget.cpp	Tue Nov 26 16:32:29 2019 +0100
+++ b/Framework/Radiography/RadiographyWidget.cpp	Wed Nov 27 17:51:33 2019 +0100
@@ -70,83 +70,66 @@
                                          unsigned int height,
                                          ImageInterpolation interpolation)
   {
-    float windowCenter, windowWidth;
-    scene_->GetWindowingWithDefault(windowCenter, windowWidth);
-
-    float x0 = windowCenter - windowWidth / 2.0f;
-    float x1 = windowCenter + windowWidth / 2.0f;
+    if (floatBuffer_.get() == NULL ||
+        floatBuffer_->GetWidth() != width ||
+        floatBuffer_->GetHeight() != height)
+    {
+      floatBuffer_.reset(new Orthanc::Image(
+        Orthanc::PixelFormat_Float32, width, height, false));
+    }
 
-    if (windowWidth <= 0.001f)  // Avoid division by zero at (*)
-    {
-      return false;
-    }
-    else
+    if (cairoBuffer_.get() == NULL ||
+        cairoBuffer_->GetWidth() != width ||
+        cairoBuffer_->GetHeight() != height)
     {
-      if (floatBuffer_.get() == NULL ||
-          floatBuffer_->GetWidth() != width ||
-          floatBuffer_->GetHeight() != height)
-      {
-        floatBuffer_.reset(new Orthanc::Image(
-          Orthanc::PixelFormat_Float32, width, height, false));
-      }
+      cairoBuffer_.reset(new CairoSurface(width, height, false /* no alpha */));
+    }
+
+    RenderBackground(*floatBuffer_, 0.0, 65535.0);
+
+    scene_->Render(*floatBuffer_, GetView().GetMatrix(), interpolation, true);
 
-      if (cairoBuffer_.get() == NULL ||
-          cairoBuffer_->GetWidth() != width ||
-          cairoBuffer_->GetHeight() != height)
-      {
-        cairoBuffer_.reset(new CairoSurface(width, height, false /* no alpha */));
-      }
-
-      RenderBackground(*floatBuffer_, x0, x1);
+    // Conversion from Float32 to BGRA32 (cairo). Very similar to
+    // GrayscaleFrameRenderer => TODO MERGE?
+    Orthanc::ImageAccessor target;
+    cairoBuffer_->GetWriteableAccessor(target);
 
-      scene_->Render(*floatBuffer_, GetView().GetMatrix(), interpolation);
-
-      // Conversion from Float32 to BGRA32 (cairo). Very similar to
-      // GrayscaleFrameRenderer => TODO MERGE?
-
-      Orthanc::ImageAccessor target;
-      cairoBuffer_->GetWriteableAccessor(target);
-
-      float scaling = 255.0f / (x1 - x0);
+    bool invert = IsInvertedInternal();
 
-      bool invert = IsInvertedInternal();
+    for (unsigned int y = 0; y < height; y++)
+    {
+      const float* p = reinterpret_cast<const float*>(floatBuffer_->GetConstRow(y));
+      uint8_t* q = reinterpret_cast<uint8_t*>(target.GetRow(y));
 
-      for (unsigned int y = 0; y < height; y++)
+      for (unsigned int x = 0; x < width; x++, p++, q += 4)
       {
-        const float* p = reinterpret_cast<const float*>(floatBuffer_->GetConstRow(y));
-        uint8_t* q = reinterpret_cast<uint8_t*>(target.GetRow(y));
-
-        for (unsigned int x = 0; x < width; x++, p++, q += 4)
+        uint8_t v = 0;
+        if (*p >= 65535.0)
+        {
+          v = 255;
+        }
+        else if (*p <= 0.0)
         {
-          uint8_t v = 0;
-          if (*p >= x1)
-          {
-            v = 255;
-          }
-          else if (*p <= x0)
-          {
-            v = 0;
-          }
-          else
-          {
-            // https://en.wikipedia.org/wiki/Linear_interpolation
-            v = static_cast<uint8_t>(scaling * (*p - x0));  // (*)
-          }
+          v = 0;
+        }
+        else
+        {
+          v = static_cast<uint8_t>(*p / 256.0);
+        }
 
-          if (invert)
-          {
-            v = 255 - v;
-          }
+        if (invert)
+        {
+          v = 255 - v;
+        }
 
-          q[0] = v;
-          q[1] = v;
-          q[2] = v;
-          q[3] = 255;
-        }
+        q[0] = v;
+        q[1] = v;
+        q[2] = v;
+        q[3] = 255;
       }
+    }
 
-      return true;
-    }
+    return true;
   }