changeset 1576:92fca2b3ba3d

sanitizing the handling of canvas size
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 24 Sep 2020 16:40:30 +0200
parents e4a52cbbdd70
children e8a120dd05bd
files Applications/Samples/Sdl/RtViewer/RtViewerSdl.cpp Applications/Samples/Sdl/SingleFrameViewer/SdlSimpleViewer.cpp Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp OrthancStone/Sources/OpenGL/IOpenGLContext.h OrthancStone/Sources/OpenGL/SdlOpenGLContext.h OrthancStone/Sources/OpenGL/WebAssemblyOpenGLContext.cpp OrthancStone/Sources/OpenGL/WebAssemblyOpenGLContext.h OrthancStone/Sources/Scene2D/CairoCompositor.cpp OrthancStone/Sources/Scene2D/CairoCompositor.h OrthancStone/Sources/Scene2D/ICompositor.h OrthancStone/Sources/Scene2D/Internals/OpenGLAdvancedPolylineRenderer.h OrthancStone/Sources/Scene2D/Internals/OpenGLColorTextureProgram.cpp OrthancStone/Sources/Scene2D/Internals/OpenGLColorTextureProgram.h OrthancStone/Sources/Scene2D/Internals/OpenGLColorTextureRenderer.cpp OrthancStone/Sources/Scene2D/Internals/OpenGLFloatTextureProgram.cpp OrthancStone/Sources/Scene2D/Internals/OpenGLFloatTextureProgram.h OrthancStone/Sources/Scene2D/Internals/OpenGLFloatTextureRenderer.cpp OrthancStone/Sources/Scene2D/Internals/OpenGLInfoPanelRenderer.cpp OrthancStone/Sources/Scene2D/Internals/OpenGLLinesProgram.cpp OrthancStone/Sources/Scene2D/Internals/OpenGLLinesProgram.h OrthancStone/Sources/Scene2D/Internals/OpenGLLookupTableTextureRenderer.cpp OrthancStone/Sources/Scene2D/Internals/OpenGLTextProgram.cpp OrthancStone/Sources/Scene2D/Internals/OpenGLTextProgram.h OrthancStone/Sources/Scene2D/Internals/OpenGLTextRenderer.cpp OrthancStone/Sources/Scene2D/Internals/OpenGLTextureProgram.cpp OrthancStone/Sources/Scene2D/Internals/OpenGLTextureProgram.h OrthancStone/Sources/Scene2D/OpenGLCompositor.cpp OrthancStone/Sources/Scene2D/OpenGLCompositor.h OrthancStone/Sources/Scene2DViewport/CreateLineMeasureTracker.cpp OrthancStone/Sources/Scene2DViewport/MeasureTool.cpp OrthancStone/Sources/Scene2DViewport/MeasureTool.h OrthancStone/Sources/Viewport/IViewport.h OrthancStone/Sources/Viewport/SdlViewport.cpp OrthancStone/Sources/Viewport/SdlViewport.h OrthancStone/Sources/Viewport/WebAssemblyCairoViewport.cpp OrthancStone/Sources/Viewport/WebAssemblyCairoViewport.h OrthancStone/Sources/Viewport/WebAssemblyViewport.cpp OrthancStone/Sources/Viewport/WebAssemblyViewport.h OrthancStone/Sources/Viewport/WebGLViewport.h
diffstat 39 files changed, 217 insertions(+), 284 deletions(-) [+]
line wrap: on
line diff
--- a/Applications/Samples/Sdl/RtViewer/RtViewerSdl.cpp	Wed Sep 23 17:25:25 2020 +0200
+++ b/Applications/Samples/Sdl/RtViewer/RtViewerSdl.cpp	Thu Sep 24 16:40:30 2020 +0200
@@ -327,7 +327,11 @@
               views, sdlEvent.window.windowID);
             boost::shared_ptr<SdlViewport> sdlViewport =
               boost::dynamic_pointer_cast<SdlViewport>(view->GetViewport());
-            sdlViewport->Paint();
+
+            {
+              std::unique_ptr<OrthancStone::IViewport::ILock> lock(sdlViewport->Lock());
+              lock->RefreshCanvasSize();
+            }
           }
           else if (sdlEvent.type == SDL_KEYDOWN &&
                    sdlEvent.key.repeat == 0 /* Ignore key bounce */)
--- a/Applications/Samples/Sdl/SingleFrameViewer/SdlSimpleViewer.cpp	Wed Sep 23 17:25:25 2020 +0200
+++ b/Applications/Samples/Sdl/SingleFrameViewer/SdlSimpleViewer.cpp	Thu Sep 24 16:40:30 2020 +0200
@@ -176,7 +176,8 @@
                        (event.window.event == SDL_WINDOWEVENT_SHOWN ||
                         event.window.event == SDL_WINDOWEVENT_EXPOSED))
               {
-                paint = true;
+                std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport->Lock());
+                lock->RefreshCanvasSize();
               }
               else if (event.type == SDL_KEYDOWN &&
                        event.key.repeat == 0 /* Ignore key bounce */)
--- a/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp	Wed Sep 23 17:25:25 2020 +0200
+++ b/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp	Thu Sep 24 16:40:30 2020 +0200
@@ -1466,7 +1466,7 @@
 
         if (fitNextContent_)
         {
-          lock->GetCompositor().RefreshCanvasSize();
+          lock->RefreshCanvasSize();
           lock->GetCompositor().FitContent(scene);
           fitNextContent_ = false;
         }
@@ -1750,7 +1750,7 @@
   void UpdateSize(bool fitContent)
   {
     std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock());
-    lock->GetCompositor().RefreshCanvasSize();
+    lock->RefreshCanvasSize();
 
     if (fitContent)
     {
--- a/OrthancStone/Sources/OpenGL/IOpenGLContext.h	Wed Sep 23 17:25:25 2020 +0200
+++ b/OrthancStone/Sources/OpenGL/IOpenGLContext.h	Thu Sep 24 16:40:30 2020 +0200
@@ -39,15 +39,6 @@
       virtual void MakeCurrent() = 0;
 
       virtual void SwapBuffer() = 0;
-
-      virtual unsigned int GetCanvasWidth() const = 0;
-
-      virtual unsigned int GetCanvasHeight() const = 0;
-
-      // Getting the size of the canvas can be expensive, especially
-      // in WebAssembly => avoid calling this method too often
-      // (e.g. on each refresh)
-      virtual void RefreshCanvasSize() = 0;
     };
   }
 }
--- a/OrthancStone/Sources/OpenGL/SdlOpenGLContext.h	Wed Sep 23 17:25:25 2020 +0200
+++ b/OrthancStone/Sources/OpenGL/SdlOpenGLContext.h	Thu Sep 24 16:40:30 2020 +0200
@@ -63,19 +63,14 @@
 
     virtual void SwapBuffer() ORTHANC_OVERRIDE;
 
-    virtual unsigned int GetCanvasWidth() const ORTHANC_OVERRIDE;
+    unsigned int GetCanvasWidth() const;
 
-    virtual unsigned int GetCanvasHeight() const ORTHANC_OVERRIDE;
+    unsigned int GetCanvasHeight() const;
 
     void ToggleMaximize()
     {
       window_.ToggleMaximize();
     }
-
-    virtual void RefreshCanvasSize() ORTHANC_OVERRIDE
-    {
-      // Nothing to do for SDL
-    }
   };
 }
 
--- a/OrthancStone/Sources/OpenGL/WebAssemblyOpenGLContext.cpp	Wed Sep 23 17:25:25 2020 +0200
+++ b/OrthancStone/Sources/OpenGL/WebAssemblyOpenGLContext.cpp	Thu Sep 24 16:40:30 2020 +0200
@@ -39,8 +39,6 @@
     private:
       std::string                     canvasSelector_;
       EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context_;
-      unsigned int                    canvasWidth_;
-      unsigned int                    canvasHeight_;
       bool                            isContextLost_;
 
     public:
@@ -52,7 +50,7 @@
         EmscriptenWebGLContextAttributes attr; 
         emscripten_webgl_init_context_attributes(&attr);
 
-#if 0
+#if 1
         // The next line might be necessary to print on Firefox 71
         // using WebGL. Sometimes, if set to "false" (the default
         // value), the canvas was rendered as a fully white or black
@@ -69,8 +67,6 @@
           LOG(ERROR) << message;
           throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, message);
         }
-
-        UpdateSize();
       }
 
       void* DebugGetInternalContext() const
@@ -161,46 +157,6 @@
          * "explicitSwapControl" option were set to "true".
          **/
       }
-
-      unsigned int GetCanvasWidth() const
-      {
-        return canvasWidth_;
-      }
-
-      unsigned int GetCanvasHeight() const
-      {
-        return canvasHeight_;
-      }
-
-      void UpdateSize()
-      {
-        double w, h;
-        emscripten_get_element_css_size(canvasSelector_.c_str(), &w, &h);
-
-        /**
-         * Emscripten has the function emscripten_get_element_css_size()
-         * to query the width and height of a named HTML element. I'm
-         * calling this first to get the initial size of the canvas DOM
-         * element, and then call emscripten_set_canvas_size() to
-         * initialize the framebuffer size of the canvas to the same
-         * size as its DOM element.
-         * https://floooh.github.io/2017/02/22/emsc-html.html
-         **/
-
-        if (w <= 0 ||
-            h <= 0)
-        {
-          canvasWidth_ = 0;
-          canvasHeight_ = 0;
-        }
-        else
-        {
-          canvasWidth_ = static_cast<unsigned int>(boost::math::iround(w));
-          canvasHeight_ = static_cast<unsigned int>(boost::math::iround(h));
-        }
-        
-        emscripten_set_canvas_element_size(canvasSelector_.c_str(), canvasWidth_, canvasHeight_);
-      }
     };
 
 
@@ -236,40 +192,6 @@
       pimpl_->SwapBuffer();
     }
 
-    unsigned int WebAssemblyOpenGLContext::GetCanvasWidth() const
-    {
-      assert(pimpl_.get() != NULL);
-      return pimpl_->GetCanvasWidth();
-    }
-
-    unsigned int WebAssemblyOpenGLContext::GetCanvasHeight() const
-    {
-      assert(pimpl_.get() != NULL);
-      return pimpl_->GetCanvasHeight();
-    }
-
-    void WebAssemblyOpenGLContext::RefreshCanvasSize()
-    {
-      assert(pimpl_.get() != NULL);
-
-      try
-      {
-        pimpl_->UpdateSize();
-      }
-      catch (const StoneException& e)
-      {
-        // Ignore problems about the loss of the WebGL context (edge case)
-        if (e.GetErrorCode() == ErrorCode_WebGLContextLost)
-        {
-          return;
-        }
-        else
-        {
-          throw;
-        }
-      }
-    }
-
     const std::string& WebAssemblyOpenGLContext::GetCanvasSelector() const
     {
       assert(pimpl_.get() != NULL);
--- a/OrthancStone/Sources/OpenGL/WebAssemblyOpenGLContext.h	Wed Sep 23 17:25:25 2020 +0200
+++ b/OrthancStone/Sources/OpenGL/WebAssemblyOpenGLContext.h	Thu Sep 24 16:40:30 2020 +0200
@@ -62,17 +62,11 @@
 
       virtual void SwapBuffer() ORTHANC_OVERRIDE;
 
-      virtual unsigned int GetCanvasWidth() const ORTHANC_OVERRIDE;
-
-      virtual unsigned int GetCanvasHeight() const ORTHANC_OVERRIDE;
-
       /**
       Returns true if the underlying context has been successfully recreated
       */
       //bool TryRecreate();
 
-      virtual void RefreshCanvasSize() ORTHANC_OVERRIDE;
-
       const std::string& GetCanvasSelector() const;
 
 
--- a/OrthancStone/Sources/Scene2D/CairoCompositor.cpp	Wed Sep 23 17:25:25 2020 +0200
+++ b/OrthancStone/Sources/Scene2D/CairoCompositor.cpp	Thu Sep 24 16:40:30 2020 +0200
@@ -89,11 +89,11 @@
                                    unsigned int canvasHeight)
   {
     ResetScene();
-    UpdateSize(canvasWidth, canvasHeight);
+    canvas_.SetSize(canvasWidth, canvasHeight, false);
   }
 
-  void CairoCompositor::UpdateSize(unsigned int canvasWidth,
-                                   unsigned int canvasHeight)
+  void CairoCompositor::SetCanvasSize(unsigned int canvasWidth,
+                                      unsigned int canvasHeight)
   {
     canvas_.SetSize(canvasWidth, canvasHeight, false);
   }
--- a/OrthancStone/Sources/Scene2D/CairoCompositor.h	Wed Sep 23 17:25:25 2020 +0200
+++ b/OrthancStone/Sources/Scene2D/CairoCompositor.h	Thu Sep 24 16:40:30 2020 +0200
@@ -59,11 +59,8 @@
       return canvas_;
     }
 
-    virtual void RefreshCanvasSize() ORTHANC_OVERRIDE
-    {
-      // The canvas size is constant in Cairo, except if
-      // "UpdateSize()" is called
-    }
+    virtual void SetCanvasSize(unsigned int canvasWidth,
+                               unsigned int canvasHeight) ORTHANC_OVERRIDE;
 
     virtual unsigned int GetCanvasWidth() const ORTHANC_OVERRIDE
     {
@@ -92,9 +89,6 @@
       helper_.reset(new Internals::CompositorHelper(*this));
     }
 
-    void UpdateSize(unsigned int canvasWidth,
-                    unsigned int canvasHeight);
-
     Orthanc::ImageAccessor* RenderText(size_t fontIndex,
                                        const std::string& utf8) const;
   };
--- a/OrthancStone/Sources/Scene2D/ICompositor.h	Wed Sep 23 17:25:25 2020 +0200
+++ b/OrthancStone/Sources/Scene2D/ICompositor.h	Thu Sep 24 16:40:30 2020 +0200
@@ -33,8 +33,8 @@
     {
     }
 
-    // This function can be expensive (notably in wasm)
-    virtual void RefreshCanvasSize() = 0; 
+    virtual void SetCanvasSize(unsigned int canvasWidth,
+                               unsigned int canvasHeight) = 0; 
     
     virtual unsigned int GetCanvasWidth() const = 0;
 
--- a/OrthancStone/Sources/Scene2D/Internals/OpenGLAdvancedPolylineRenderer.h	Wed Sep 23 17:25:25 2020 +0200
+++ b/OrthancStone/Sources/Scene2D/Internals/OpenGLAdvancedPolylineRenderer.h	Thu Sep 24 16:40:30 2020 +0200
@@ -49,7 +49,7 @@
       {
         if (!context_.IsContextLost())
         {
-          program_.Apply(*data_, transform, true, true);
+          program_.Apply(*data_, transform, canvasWidth, canvasHeight, true, true);
         }
       }
 
--- a/OrthancStone/Sources/Scene2D/Internals/OpenGLColorTextureProgram.cpp	Wed Sep 23 17:25:25 2020 +0200
+++ b/OrthancStone/Sources/Scene2D/Internals/OpenGLColorTextureProgram.cpp	Thu Sep 24 16:40:30 2020 +0200
@@ -45,11 +45,13 @@
     
     void OpenGLColorTextureProgram::Apply(OpenGL::OpenGLTexture& texture,
                                           const AffineTransform2D& transform,
+                                          unsigned int canvasWidth,
+                                          unsigned int canvasHeight,
                                           bool useAlpha)
     {
       if (!context_.IsContextLost())
       {
-        OpenGLTextureProgram::Execution execution(program_, texture, transform);
+        OpenGLTextureProgram::Execution execution(program_, texture, transform, canvasWidth, canvasHeight);
 
         if (useAlpha)
         {
--- a/OrthancStone/Sources/Scene2D/Internals/OpenGLColorTextureProgram.h	Wed Sep 23 17:25:25 2020 +0200
+++ b/OrthancStone/Sources/Scene2D/Internals/OpenGLColorTextureProgram.h	Thu Sep 24 16:40:30 2020 +0200
@@ -38,6 +38,8 @@
 
       void Apply(OpenGL::OpenGLTexture& texture,
                  const AffineTransform2D& transform,
+                 unsigned int canvasWidth,
+                 unsigned int canvasHeight,
                  bool useAlpha);
     };
   }
--- a/OrthancStone/Sources/Scene2D/Internals/OpenGLColorTextureRenderer.cpp	Wed Sep 23 17:25:25 2020 +0200
+++ b/OrthancStone/Sources/Scene2D/Internals/OpenGLColorTextureRenderer.cpp	Thu Sep 24 16:40:30 2020 +0200
@@ -53,7 +53,8 @@
     {
       if (!context_.IsContextLost() && texture_.get() != NULL)
       {
-        program_.Apply(*texture_, AffineTransform2D::Combine(transform, layerTransform_), true);
+        program_.Apply(*texture_, AffineTransform2D::Combine(transform, layerTransform_),
+                       canvasWidth, canvasHeight, true);
       }
     }
 
--- a/OrthancStone/Sources/Scene2D/Internals/OpenGLFloatTextureProgram.cpp	Wed Sep 23 17:25:25 2020 +0200
+++ b/OrthancStone/Sources/Scene2D/Internals/OpenGLFloatTextureProgram.cpp	Thu Sep 24 16:40:30 2020 +0200
@@ -141,13 +141,16 @@
 
     void OpenGLFloatTextureProgram::Apply(Data& data,
                                           const AffineTransform2D& transform,
+                                          unsigned int canvasWidth,
+                                          unsigned int canvasHeight,
                                           float windowCenter,
                                           float windowWidth,
                                           bool invert)
     {
       if (!context_.IsContextLost())
       {
-        OpenGLTextureProgram::Execution execution(program_, data.GetTexture(), transform);
+        OpenGLTextureProgram::Execution execution(
+          program_, data.GetTexture(), transform, canvasWidth, canvasHeight);
 
         glUniform1f(execution.GetUniformLocation("u_slope"), data.GetSlope());
         glUniform1f(execution.GetUniformLocation("u_offset"), data.GetOffset());
--- a/OrthancStone/Sources/Scene2D/Internals/OpenGLFloatTextureProgram.h	Wed Sep 23 17:25:25 2020 +0200
+++ b/OrthancStone/Sources/Scene2D/Internals/OpenGLFloatTextureProgram.h	Thu Sep 24 16:40:30 2020 +0200
@@ -66,6 +66,8 @@
 
       void Apply(Data& data,
                  const AffineTransform2D& transform,
+                 unsigned int canvasWidth,
+                 unsigned int canvasHeight,
                  float windowCenter,
                  float windowWidth,
                  bool  invert);
--- a/OrthancStone/Sources/Scene2D/Internals/OpenGLFloatTextureRenderer.cpp	Wed Sep 23 17:25:25 2020 +0200
+++ b/OrthancStone/Sources/Scene2D/Internals/OpenGLFloatTextureRenderer.cpp	Thu Sep 24 16:40:30 2020 +0200
@@ -67,8 +67,8 @@
     {
       if (!context_.IsContextLost() && texture_.get() != NULL)
       {
-        program_.Apply(*texture_, AffineTransform2D::Combine(transform, layerTransform_), 
-                       windowCenter_, windowWidth_, invert_);
+        program_.Apply(*texture_, AffineTransform2D::Combine(transform, layerTransform_),
+                       canvasWidth, canvasHeight, windowCenter_, windowWidth_, invert_);
       }
     }
 
--- a/OrthancStone/Sources/Scene2D/Internals/OpenGLInfoPanelRenderer.cpp	Wed Sep 23 17:25:25 2020 +0200
+++ b/OrthancStone/Sources/Scene2D/Internals/OpenGLInfoPanelRenderer.cpp	Thu Sep 24 16:40:30 2020 +0200
@@ -110,7 +110,7 @@
                                                        translation1);
         }
 
-        program_.Apply(*texture_, actualTransform, true);
+        program_.Apply(*texture_, actualTransform, canvasWidth, canvasHeight, true);
       }
     }
   }
--- a/OrthancStone/Sources/Scene2D/Internals/OpenGLLinesProgram.cpp	Wed Sep 23 17:25:25 2020 +0200
+++ b/OrthancStone/Sources/Scene2D/Internals/OpenGLLinesProgram.cpp	Thu Sep 24 16:40:30 2020 +0200
@@ -415,6 +415,8 @@
 
     void OpenGLLinesProgram::Apply(const Data& data,
                                    const AffineTransform2D& transform,
+                                   unsigned int canvasWidth,
+                                   unsigned int canvasHeight,
                                    bool antialiasing,
                                    bool scaleIndependantThickness)
     {
@@ -428,7 +430,7 @@
         GLint locationColor = program_->GetAttributeLocation("a_color");
 
         float m[16];
-        transform.ConvertToOpenGLMatrix(m, context_.GetCanvasWidth(), context_.GetCanvasHeight());
+        transform.ConvertToOpenGLMatrix(m, canvasWidth, canvasHeight);
 
         glUniformMatrix4fv(program_->GetUniformLocation("u_matrix"), 1, GL_FALSE, m);
 
--- a/OrthancStone/Sources/Scene2D/Internals/OpenGLLinesProgram.h	Wed Sep 23 17:25:25 2020 +0200
+++ b/OrthancStone/Sources/Scene2D/Internals/OpenGLLinesProgram.h	Thu Sep 24 16:40:30 2020 +0200
@@ -82,6 +82,8 @@
 
       void Apply(const Data& data,
                  const AffineTransform2D& transform,
+                 unsigned int canvasWidth,
+                 unsigned int canvasHeight,
                  bool antialiasing,
                  bool scaleIndependantThickness);
     };
--- a/OrthancStone/Sources/Scene2D/Internals/OpenGLLookupTableTextureRenderer.cpp	Wed Sep 23 17:25:25 2020 +0200
+++ b/OrthancStone/Sources/Scene2D/Internals/OpenGLLookupTableTextureRenderer.cpp	Thu Sep 24 16:40:30 2020 +0200
@@ -75,7 +75,7 @@
         program_.Apply(
           *glTexture_, 
           AffineTransform2D::Combine(transform, layerTransform_), 
-          true);
+          canvasWidth, canvasHeight, true);
       }
     }
 
--- a/OrthancStone/Sources/Scene2D/Internals/OpenGLTextProgram.cpp	Wed Sep 23 17:25:25 2020 +0200
+++ b/OrthancStone/Sources/Scene2D/Internals/OpenGLTextProgram.cpp	Thu Sep 24 16:40:30 2020 +0200
@@ -181,7 +181,9 @@
 
     void OpenGLTextProgram::Apply(OpenGL::OpenGLTexture& fontTexture,
                                   const Data& data,
-                                  const AffineTransform2D& transform)
+                                  const AffineTransform2D& transform,
+                                  unsigned int canvasWidth,
+                                  unsigned int canvasHeight)
     {
       if (!context_.IsContextLost() && !data.IsEmpty())
       {
@@ -200,7 +202,7 @@
         const AffineTransform2D t = AffineTransform2D::CreateOffset(x + dx, y + dy);
 
         float m[16];
-        t.ConvertToOpenGLMatrix(m, context_.GetCanvasWidth(), context_.GetCanvasHeight());
+        t.ConvertToOpenGLMatrix(m, canvasWidth, canvasHeight);
 
         fontTexture.Bind(program_->GetUniformLocation("u_texture"));
         glUniformMatrix4fv(program_->GetUniformLocation("u_matrix"), 1, GL_FALSE, m);
--- a/OrthancStone/Sources/Scene2D/Internals/OpenGLTextProgram.h	Wed Sep 23 17:25:25 2020 +0200
+++ b/OrthancStone/Sources/Scene2D/Internals/OpenGLTextProgram.h	Thu Sep 24 16:40:30 2020 +0200
@@ -131,7 +131,9 @@
 
       void Apply(OpenGL::OpenGLTexture& fontTexture,
                  const Data& data,
-                 const AffineTransform2D& transform);
+                 const AffineTransform2D& transform,
+                 unsigned int canvasWidth,
+                 unsigned int canvasHeight);
     };
   }
 }
--- a/OrthancStone/Sources/Scene2D/Internals/OpenGLTextRenderer.cpp	Wed Sep 23 17:25:25 2020 +0200
+++ b/OrthancStone/Sources/Scene2D/Internals/OpenGLTextRenderer.cpp	Thu Sep 24 16:40:30 2020 +0200
@@ -54,7 +54,7 @@
     {
       if (!context_.IsContextLost() && data_.get() != NULL)
       {
-        program_.Apply(texture_, *data_, transform);
+        program_.Apply(texture_, *data_, transform, canvasWidth, canvasHeight);
       }
     }
 
--- a/OrthancStone/Sources/Scene2D/Internals/OpenGLTextureProgram.cpp	Wed Sep 23 17:25:25 2020 +0200
+++ b/OrthancStone/Sources/Scene2D/Internals/OpenGLTextureProgram.cpp	Thu Sep 24 16:40:30 2020 +0200
@@ -45,7 +45,9 @@
   namespace Internals
   {
     void OpenGLTextureProgram::InitializeExecution(OpenGL::OpenGLTexture& texture,
-                                                   const AffineTransform2D& transform)
+                                                   const AffineTransform2D& transform,
+                                                   unsigned int canvasWidth,
+                                                   unsigned int canvasHeight)
     {
       if (!context_.IsContextLost())
       {
@@ -58,7 +60,7 @@
         AffineTransform2D t = AffineTransform2D::Combine(transform, scale);
 
         float m[16];
-        t.ConvertToOpenGLMatrix(m, context_.GetCanvasWidth(), context_.GetCanvasHeight());
+        t.ConvertToOpenGLMatrix(m, canvasWidth, canvasHeight);
 
         texture.Bind(program_->GetUniformLocation("u_texture"));
         glUniformMatrix4fv(program_->GetUniformLocation("u_matrix"), 1, GL_FALSE, m);
--- a/OrthancStone/Sources/Scene2D/Internals/OpenGLTextureProgram.h	Wed Sep 23 17:25:25 2020 +0200
+++ b/OrthancStone/Sources/Scene2D/Internals/OpenGLTextureProgram.h	Thu Sep 24 16:40:30 2020 +0200
@@ -42,7 +42,9 @@
       GLuint                                buffers_[2];
 
       void InitializeExecution(OpenGL::OpenGLTexture& texture,
-                               const AffineTransform2D& transform);
+                               const AffineTransform2D& transform,
+                               unsigned int canvasWidth,
+                               unsigned int canvasHeight);
 
       void FinalizeExecution();
 
@@ -60,10 +62,12 @@
       public:
         Execution(OpenGLTextureProgram& that,
                   OpenGL::OpenGLTexture& texture,
-                  const AffineTransform2D& transform) :
+                  const AffineTransform2D& transform,
+                  unsigned int canvasWidth,
+                  unsigned int canvasHeight) :
           that_(that)
         {
-          that_.InitializeExecution(texture, transform);
+          that_.InitializeExecution(texture, transform, canvasWidth, canvasHeight);
         }
 
         ~Execution()
--- a/OrthancStone/Sources/Scene2D/OpenGLCompositor.cpp	Wed Sep 23 17:25:25 2020 +0200
+++ b/OrthancStone/Sources/Scene2D/OpenGLCompositor.cpp	Thu Sep 24 16:40:30 2020 +0200
@@ -137,12 +137,6 @@
     canvasWidth_(0),
     canvasHeight_(0)
   {
-    if (!context_.IsContextLost())
-    {
-      canvasWidth_ = context_.GetCanvasWidth();
-      canvasHeight_ = context_.GetCanvasHeight();
-    }
-
     ResetScene();
   }
 
@@ -187,8 +181,6 @@
     if (!context_.IsContextLost())
     {
       context_.MakeCurrent(); // this can throw if context lost!
-      canvasWidth_ = context_.GetCanvasWidth();
-      canvasHeight_ = context_.GetCanvasHeight();
 
       glViewport(0, 0, canvasWidth_, canvasHeight_);
       glClearColor(0, 0, 0, 1);
@@ -245,14 +237,10 @@
 #endif
 
 
-  void OpenGLCompositor::RefreshCanvasSize()
+  void OpenGLCompositor::SetCanvasSize(unsigned int canvasWidth,
+                                       unsigned int canvasHeight)
   {
-    if (!context_.IsContextLost())
-    {
-      context_.MakeCurrent(); // this can throw if context lost!
-      context_.RefreshCanvasSize();  // Difference with Refresh(scene)
-      canvasWidth_ = context_.GetCanvasWidth();
-      canvasHeight_ = context_.GetCanvasHeight();
-    }
+    canvasWidth_ = canvasWidth;
+    canvasHeight_ = canvasHeight;
   }
 }
--- a/OrthancStone/Sources/Scene2D/OpenGLCompositor.h	Wed Sep 23 17:25:25 2020 +0200
+++ b/OrthancStone/Sources/Scene2D/OpenGLCompositor.h	Thu Sep 24 16:40:30 2020 +0200
@@ -72,7 +72,8 @@
                  Orthanc::Encoding codepage) ORTHANC_OVERRIDE;
 #endif
 
-    virtual void RefreshCanvasSize() ORTHANC_OVERRIDE;
+    virtual void SetCanvasSize(unsigned int canvasWidth,
+                               unsigned int canvasHeight) ORTHANC_OVERRIDE;
 
     virtual unsigned int GetCanvasWidth() const ORTHANC_OVERRIDE
     {
--- a/OrthancStone/Sources/Scene2DViewport/CreateLineMeasureTracker.cpp	Wed Sep 23 17:25:25 2020 +0200
+++ b/OrthancStone/Sources/Scene2DViewport/CreateLineMeasureTracker.cpp	Thu Sep 24 16:40:30 2020 +0200
@@ -64,9 +64,10 @@
     //LOG(TRACE) << "scenePos.GetX() = " << scenePos.GetX() << "     " <<
     //  "scenePos.GetY() = " << scenePos.GetY();
       
-    CreateLineMeasureTracker* concreteThis =
+    /*CreateLineMeasureTracker* concreteThis =
       dynamic_cast<CreateLineMeasureTracker*>(this);
-    assert(concreteThis != NULL);
+      assert(concreteThis != NULL);*/
+
     GetCommand()->SetEnd(scenePos);
   }
 
--- a/OrthancStone/Sources/Scene2DViewport/MeasureTool.cpp	Wed Sep 23 17:25:25 2020 +0200
+++ b/OrthancStone/Sources/Scene2DViewport/MeasureTool.cpp	Thu Sep 24 16:40:30 2020 +0200
@@ -47,12 +47,10 @@
     return enabled_;
   }
 
-  MeasureTool::MeasureTool(
-    boost::shared_ptr<IViewport> viewport)
-    : viewport_(viewport)
-    , enabled_(true)
+  MeasureTool::MeasureTool(boost::shared_ptr<IViewport> viewport) :
+    enabled_(true),
+    viewport_(viewport)
   {
-
   }
 
   void MeasureTool::PostConstructor()
@@ -78,7 +76,4 @@
   {
     RefreshScene();
   }
-
-
 }
-
--- a/OrthancStone/Sources/Scene2DViewport/MeasureTool.h	Wed Sep 23 17:25:25 2020 +0200
+++ b/OrthancStone/Sources/Scene2DViewport/MeasureTool.h	Thu Sep 24 16:40:30 2020 +0200
@@ -43,6 +43,39 @@
   private:
     bool     enabled_;
 
+
+  protected:
+    explicit MeasureTool(boost::shared_ptr<IViewport> viewport);
+
+    void PostConstructor();
+
+    /**
+       The measuring tool may exist in a standalone fashion, without any available
+       scene (because the controller is dead or dying). This call allows to check 
+       before accessing the scene.
+    */
+    bool IsSceneAlive() const;
+    
+    /**
+       This is the meat of the tool: this method must [create (if needed) and]
+       update the layers and their data according to the measure tool kind and
+       current state. This is repeatedly called during user interaction
+    */
+    virtual void RefreshScene() = 0;
+
+    /**
+       enabled_ is not accessible by subclasses because there is a state machine
+       that we do not wanna mess with
+    */
+    bool IsEnabled() const;
+
+    /**
+       Protected to allow sub-classes to use this weak pointer in factory methods
+       (pass them to created objects)
+    */
+    boost::shared_ptr<IViewport> viewport_;
+
+
   public:
     virtual ~MeasureTool()
     {
@@ -114,37 +147,6 @@
        A description of the measuring tool, useful in debug logs
     */
     virtual std::string GetDescription() = 0;
-
-  protected:
-    explicit MeasureTool(boost::shared_ptr<IViewport> viewport);
-
-    void PostConstructor();
-
-    /**
-       The measuring tool may exist in a standalone fashion, without any available
-       scene (because the controller is dead or dying). This call allows to check 
-       before accessing the scene.
-    */
-    bool IsSceneAlive() const;
-    
-    /**
-       This is the meat of the tool: this method must [create (if needed) and]
-       update the layers and their data according to the measure tool kind and
-       current state. This is repeatedly called during user interaction
-    */
-    virtual void RefreshScene() = 0;
-
-    /**
-       enabled_ is not accessible by subclasses because there is a state machine
-       that we do not wanna mess with
-    */
-    bool IsEnabled() const;
-
-    /**
-       Protected to allow sub-classes to use this weak pointer in factory methods
-       (pass them to created objects)
-    */
-    boost::shared_ptr<IViewport> viewport_;
   };
 
   class MeasureToolMemento
--- a/OrthancStone/Sources/Viewport/IViewport.h	Wed Sep 23 17:25:25 2020 +0200
+++ b/OrthancStone/Sources/Viewport/IViewport.h	Thu Sep 24 16:40:30 2020 +0200
@@ -62,6 +62,14 @@
       virtual ViewportController& GetController() = 0;
 
       virtual void Invalidate() = 0;
+
+
+      /**
+       * This function must be called when the layout has changed, and
+       * thus the size of the canvas must be re-computed. Avoid
+       * calling this method too often for performance.
+       **/
+      virtual void RefreshCanvasSize() = 0;
     };   
     
     virtual ~IViewport()
--- a/OrthancStone/Sources/Viewport/SdlViewport.cpp	Wed Sep 23 17:25:25 2020 +0200
+++ b/OrthancStone/Sources/Viewport/SdlViewport.cpp	Thu Sep 24 16:40:30 2020 +0200
@@ -52,6 +52,7 @@
     compositor_.reset(compositor);
   }
 
+
   SdlViewport::SdlViewport()
   {
     refreshEvent_ = SDL_RegisterEvents(1);
@@ -76,6 +77,14 @@
   }
 
   
+  void SdlViewport::UpdateSize(unsigned int width, unsigned int height)
+  {
+    SdlLock lock(*this);
+    lock.GetCompositor().SetCanvasSize(width, height);
+    lock.Invalidate();
+  }
+
+
   SdlOpenGLViewport::SdlOpenGLViewport(const std::string& title,
                                        unsigned int width,
                                        unsigned int height,
@@ -85,6 +94,13 @@
     AcquireCompositor(new OpenGLCompositor(context_));  // (*)
   }
 
+
+  void SdlOpenGLViewport::RefreshCanvasSize()
+  {
+    UpdateSize(context_.GetCanvasWidth(), context_.GetCanvasHeight());
+  }
+
+
   boost::shared_ptr<SdlOpenGLViewport> SdlOpenGLViewport::Create(
     const std::string& title,
     unsigned int width,
@@ -120,14 +136,6 @@
   }
 
 
-  void SdlOpenGLViewport::UpdateSize(unsigned int width, unsigned int height)
-  {
-    // nothing to do in OpenGL, the OpenGLCompositor::UpdateSize will be called automatically
-    SdlLock lock(*this);
-    lock.Invalidate();
-  }
-
-
   void SdlOpenGLViewport::ToggleMaximize()
   {
     // No need to call "Invalidate()" here, as "UpdateSize()" will
@@ -138,6 +146,11 @@
 
 
 
+  void SdlCairoViewport::RefreshCanvasSize()
+  {
+    UpdateSize(window_.GetWidth(), window_.GetHeight());
+  }
+
   SdlCairoViewport::SdlCairoViewport(const char* title,
                                      unsigned int width,
                                      unsigned int height,
@@ -177,15 +190,6 @@
   }
 
 
-  void SdlCairoViewport::UpdateSize(unsigned int width,
-                                    unsigned int height)
-  {
-    SdlLock lock(*this);
-    dynamic_cast<CairoCompositor&>(lock.GetCompositor()).UpdateSize(width, height);
-    lock.Invalidate();
-  }
-  
-
   void SdlCairoViewport::ToggleMaximize()
   {
     // No need to call "Invalidate()" here, as "UpdateSize()" will
--- a/OrthancStone/Sources/Viewport/SdlViewport.h	Wed Sep 23 17:25:25 2020 +0200
+++ b/OrthancStone/Sources/Viewport/SdlViewport.h	Thu Sep 24 16:40:30 2020 +0200
@@ -95,6 +95,11 @@
       {
         that_.SendRefreshEvent();
       }
+      
+      virtual void RefreshCanvasSize() ORTHANC_OVERRIDE
+      {
+        that_.RefreshCanvasSize();
+      }
     };
 
     void ClearCompositor()
@@ -104,12 +109,14 @@
 
     void AcquireCompositor(ICompositor* compositor /* takes ownership */);
 
+    virtual void RefreshCanvasSize() = 0;
+    
   protected:
     SdlViewport();
+
     void PostConstructor();
 
   public:
-
     bool IsRefreshEvent(const SDL_Event& event) const
     {
       return (event.type == refreshEvent_);
@@ -122,8 +129,8 @@
 
     virtual uint32_t GetSdlWindowId() = 0;
 
-    virtual void UpdateSize(unsigned int width,
-                            unsigned int height) = 0;
+    void UpdateSize(unsigned int width,
+                    unsigned int height);
 
     virtual void ToggleMaximize() = 0;
 
@@ -137,11 +144,14 @@
   private:
     SdlOpenGLContext  context_;
 
-  private:
     SdlOpenGLViewport(const std::string& title,
                       unsigned int       width,
                       unsigned int       height,
                       bool               allowDpiScaling = true);
+
+  protected:
+    virtual void RefreshCanvasSize() ORTHANC_OVERRIDE;
+    
   public:
     static boost::shared_ptr<SdlOpenGLViewport> Create(const std::string&,
                                                        unsigned int width,
@@ -155,9 +165,6 @@
 
     virtual void Paint() ORTHANC_OVERRIDE;
 
-    virtual void UpdateSize(unsigned int width, 
-                            unsigned int height) ORTHANC_OVERRIDE;
-
     virtual void ToggleMaximize() ORTHANC_OVERRIDE;
   };
 
@@ -170,11 +177,14 @@
 
     void CreateSdlSurfaceFromCompositor(const CairoCompositor& compositor);
 
-  private:
     SdlCairoViewport(const char* title,
                      unsigned int width,
                      unsigned int height,
                      bool allowDpiScaling = true);
+
+  protected:
+    virtual void RefreshCanvasSize() ORTHANC_OVERRIDE;
+    
   public:
     static boost::shared_ptr<SdlCairoViewport> Create(const char* title,
                      unsigned int width,
@@ -188,9 +198,6 @@
 
     virtual void Paint() ORTHANC_OVERRIDE;
 
-    virtual void UpdateSize(unsigned int width,
-                            unsigned int height) ORTHANC_OVERRIDE;
-
     virtual void ToggleMaximize() ORTHANC_OVERRIDE;
   };
 }
--- a/OrthancStone/Sources/Viewport/WebAssemblyCairoViewport.cpp	Wed Sep 23 17:25:25 2020 +0200
+++ b/OrthancStone/Sources/Viewport/WebAssemblyCairoViewport.cpp	Thu Sep 24 16:40:30 2020 +0200
@@ -34,39 +34,8 @@
 
 #include <Images/Image.h>
 
-#include <boost/math/special_functions/round.hpp>
-
 namespace OrthancStone
 {
-  void WebAssemblyCairoViewport::GetCanvasSize(unsigned int& width,
-                                               unsigned int& height)
-  {
-    double w, h;
-    emscripten_get_element_css_size(GetCanvasCssSelector().c_str(), &w, &h);
-
-    /**
-     * Emscripten has the function emscripten_get_element_css_size()
-     * to query the width and height of a named HTML element. I'm
-     * calling this first to get the initial size of the canvas DOM
-     * element, and then call emscripten_set_canvas_size() to
-     * initialize the framebuffer size of the canvas to the same
-     * size as its DOM element.
-     * https://floooh.github.io/2017/02/22/emsc-html.html
-     **/
-    if (w > 0 &&
-        h > 0)
-    {
-      width = static_cast<unsigned int>(boost::math::iround(w));
-      height = static_cast<unsigned int>(boost::math::iround(h));
-    }
-    else
-    {
-      width = 0;
-      height = 0;
-    }
-  }
-
-
   void WebAssemblyCairoViewport::Paint(ICompositor& compositor,
                                        ViewportController& controller)
   {
@@ -123,25 +92,12 @@
   }
     
 
-  void WebAssemblyCairoViewport::UpdateSize(ICompositor& compositor)
-  {
-    unsigned int width, height;
-    GetCanvasSize(width, height);
-    emscripten_set_canvas_element_size(GetCanvasCssSelector().c_str(), width, height);
-
-    dynamic_cast<CairoCompositor&>(compositor).UpdateSize(width, height);
-  }
-
-
   WebAssemblyCairoViewport::WebAssemblyCairoViewport(const std::string& canvasId,
                                                      bool enableEmscriptenMouseEvents) :
     WebAssemblyViewport(canvasId,enableEmscriptenMouseEvents)
   {
-    unsigned int width, height;
-    GetCanvasSize(width, height);
-    emscripten_set_canvas_element_size(GetCanvasCssSelector().c_str(), width, height);
-
-    AcquireCompositor(new CairoCompositor(width, height));
+    RefreshCanvasSize();
+    AcquireCompositor(new CairoCompositor(GetCanvasWidth(), GetCanvasHeight()));
   }
   
 
--- a/OrthancStone/Sources/Viewport/WebAssemblyCairoViewport.h	Wed Sep 23 17:25:25 2020 +0200
+++ b/OrthancStone/Sources/Viewport/WebAssemblyCairoViewport.h	Thu Sep 24 16:40:30 2020 +0200
@@ -30,9 +30,6 @@
   private:
     std::unique_ptr<Orthanc::ImageAccessor>  javascript_;
         
-    void GetCanvasSize(unsigned int& width,
-                       unsigned int& height);
-
     WebAssemblyCairoViewport(const std::string& canvasId,
                              bool enableEmscriptenMouseEvents);
 
@@ -40,8 +37,6 @@
     virtual void Paint(ICompositor& compositor,
                        ViewportController& controller) ORTHANC_OVERRIDE;
     
-    virtual void UpdateSize(ICompositor& compositor) ORTHANC_OVERRIDE;
-
   public:
     static boost::shared_ptr<WebAssemblyCairoViewport> Create(const std::string& canvasId,
                                                               bool enableEmscriptenMouseEvents = true);
--- a/OrthancStone/Sources/Viewport/WebAssemblyViewport.cpp	Wed Sep 23 17:25:25 2020 +0200
+++ b/OrthancStone/Sources/Viewport/WebAssemblyViewport.cpp	Thu Sep 24 16:40:30 2020 +0200
@@ -38,6 +38,7 @@
 
 #include <boost/make_shared.hpp>
 #include <boost/enable_shared_from_this.hpp>
+#include <boost/math/special_functions/round.hpp>
 
 namespace OrthancStone
 {
@@ -112,6 +113,11 @@
     {
       that_.Invalidate();
     }
+
+    virtual void RefreshCanvasSize() ORTHANC_OVERRIDE
+    {
+      that_.RefreshCanvasSize();
+    }
   };
 
 
@@ -137,7 +143,7 @@
 
     if (that->compositor_.get() != NULL)
     {
-      that->UpdateSize(*that->compositor_);
+      that->RefreshCanvasSize();
       that->Invalidate();
     }
       
@@ -215,7 +221,7 @@
     if (compositor_.get() != NULL &&
         controller_ /* should always be true */)
     {
-      UpdateSize(*compositor_);
+      RefreshCanvasSize();
       compositor_->FitContent(controller_->GetScene());
       OnRequestAnimationFrame(0, reinterpret_cast<void*>(this));
     }
@@ -248,7 +254,9 @@
     canvasCssSelector_(canvasId),
 #endif
     interactor_(new DefaultViewportInteractor),
-    enableEmscriptenMouseEvents_(enableEmscriptenMouseEvents)
+    enableEmscriptenMouseEvents_(enableEmscriptenMouseEvents),
+    canvasWidth_(0),
+    canvasHeight_(0)
   {
   }
 
@@ -312,7 +320,7 @@
 
   void WebAssemblyViewport::UpdateCanvasSize()
   {
-    UpdateSize(*compositor_);
+    RefreshCanvasSize();
   }
 
   WebAssemblyViewport::~WebAssemblyViewport()
@@ -358,4 +366,39 @@
       interactor_.reset(interactor);
     }
   }
+
+
+  void WebAssemblyViewport::RefreshCanvasSize()
+  {
+    double w, h;
+    emscripten_get_element_css_size(GetCanvasCssSelector().c_str(), &w, &h);
+
+    /**
+     * Emscripten has the function emscripten_get_element_css_size()
+     * to query the width and height of a named HTML element. I'm
+     * calling this first to get the initial size of the canvas DOM
+     * element, and then call emscripten_set_canvas_size() to
+     * initialize the framebuffer size of the canvas to the same
+     * size as its DOM element.
+     * https://floooh.github.io/2017/02/22/emsc-html.html
+     **/
+    if (w > 0 &&
+        h > 0)
+    {
+      canvasWidth_ = static_cast<unsigned int>(boost::math::iround(w));
+      canvasHeight_ = static_cast<unsigned int>(boost::math::iround(h));
+    }
+    else
+    {
+      canvasWidth_ = 0;
+      canvasHeight_ = 0;
+    }
+
+    emscripten_set_canvas_element_size(GetCanvasCssSelector().c_str(), canvasWidth_, canvasHeight_);
+
+    if (compositor_.get() != NULL)
+    {
+      compositor_->SetCanvasSize(canvasWidth_, canvasHeight_);
+    }
+  }
 }
--- a/OrthancStone/Sources/Viewport/WebAssemblyViewport.h	Wed Sep 23 17:25:25 2020 +0200
+++ b/OrthancStone/Sources/Viewport/WebAssemblyViewport.h	Thu Sep 24 16:40:30 2020 +0200
@@ -58,6 +58,8 @@
     std::unique_ptr<ViewportController>   controller_;
     std::unique_ptr<IViewportInteractor>  interactor_;
     bool                                  enableEmscriptenMouseEvents_;
+    unsigned int                          canvasWidth_;
+    unsigned int                          canvasHeight_;
 
     static EM_BOOL OnRequestAnimationFrame(double time, void *userData);
     
@@ -87,8 +89,6 @@
     virtual void Paint(ICompositor& compositor,
                        ViewportController& controller) = 0;
 
-    virtual void UpdateSize(ICompositor& compositor) = 0;
-
     /**
     The second argument is temporary and should be deleted once the migration 
     to interactors is finished. It should be set to "true" for new applications.
@@ -124,6 +124,19 @@
       return canvasCssSelector_;
     }
 
+
+    void RefreshCanvasSize();
+
+    unsigned int GetCanvasWidth() const
+    {
+      return canvasWidth_;
+    }
+    
+    unsigned int GetCanvasHeight()
+    {
+      return canvasHeight_;
+    }
+    
     void FitForPrint();  // TODO - REMOVE
   };
 }
--- a/OrthancStone/Sources/Viewport/WebGLViewport.h	Wed Sep 23 17:25:25 2020 +0200
+++ b/OrthancStone/Sources/Viewport/WebGLViewport.h	Thu Sep 24 16:40:30 2020 +0200
@@ -38,11 +38,6 @@
     virtual void Paint(ICompositor& compositor,
                        ViewportController& controller) ORTHANC_OVERRIDE;
     
-    virtual void UpdateSize(ICompositor& compositor) ORTHANC_OVERRIDE
-    {
-      context_.RefreshCanvasSize();
-    }
-
   public:
     static boost::shared_ptr<WebGLViewport> Create(const std::string& canvasId,
                                                    bool enableEmscriptenMouseEvents = true);