changeset 911:64e5f3ff6360 am-dev

Merge
author Alain Mazy <alain@mazy.be>
date Thu, 18 Jul 2019 10:50:59 +0200
parents ef6e425dc79f (current diff) a6c12fe88bcb (diff)
children e4ac54cb8771
files Applications/Generic/GuiAdapter.cpp Framework/Scene2D/CairoCompositor.h Framework/Scene2D/OpenGLCompositor.h Resources/CMake/OrthancStoneConfiguration.cmake Samples/Sdl/BasicScene.cpp Samples/WebAssembly/NOTES.txt
diffstat 21 files changed, 454 insertions(+), 99 deletions(-) [+]
line wrap: on
line diff
--- a/Applications/Generic/GuiAdapter.cpp	Wed Jul 17 15:33:07 2019 +0200
+++ b/Applications/Generic/GuiAdapter.cpp	Thu Jul 18 10:50:59 2019 +0200
@@ -725,10 +725,13 @@
   // SDL ONLY
   void GuiAdapter::Run()
   {
+#if 0 
+    // TODO: MAKE THIS DYNAMIC !!! See SdlOpenGLViewport vs Cairo in ViewportWrapper
 # if ORTHANC_ENABLE_OPENGL == 1 && !defined(__APPLE__)
     glEnable(GL_DEBUG_OUTPUT);
     glDebugMessageCallback(OpenGLMessageCallback, 0);
 # endif
+#endif
 
     // Uint32 SDL_GetWindowID(SDL_Window* window)
     // SDL_Window* SDL_GetWindowFromID(Uint32 id)   // may return NULL
--- a/Framework/Scene2D/CairoCompositor.cpp	Wed Jul 17 15:33:07 2019 +0200
+++ b/Framework/Scene2D/CairoCompositor.cpp	Thu Jul 18 10:50:59 2019 +0200
@@ -91,9 +91,14 @@
                                    unsigned int canvasHeight) :
     helper_(scene, *this)
   {
+    UpdateSize(canvasWidth, canvasHeight);
+  }
+
+  void CairoCompositor::UpdateSize(unsigned int canvasWidth,
+                                   unsigned int canvasHeight)
+  {
     canvas_.SetSize(canvasWidth, canvasHeight, false);
   }
-
     
   CairoCompositor::~CairoCompositor()
   {
--- a/Framework/Scene2D/CairoCompositor.h	Wed Jul 17 15:33:07 2019 +0200
+++ b/Framework/Scene2D/CairoCompositor.h	Thu Jul 18 10:50:59 2019 +0200
@@ -13,7 +13,7 @@
  * 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/>.
  **/
@@ -21,6 +21,7 @@
 
 #pragma once
 
+#include "ICompositor.h"
 #include "../Fonts/GlyphBitmapAlphabet.h"
 #include "../Wrappers/CairoContext.h"
 #include "Internals/CompositorHelper.h"
@@ -29,9 +30,9 @@
 namespace OrthancStone
 {
   class CairoCompositor :
-    public ICompositor,
-    private Internals::CompositorHelper::IRendererFactory,
-    private Internals::ICairoContextProvider
+      public ICompositor,
+      private Internals::CompositorHelper::IRendererFactory,
+      private Internals::ICairoContextProvider
   {
   private:
     typedef std::map<size_t, GlyphBitmapAlphabet*>   Fonts;
@@ -52,7 +53,7 @@
                     unsigned int canvasWidth,
                     unsigned int canvasHeight);
     
-    ~CairoCompositor();
+    virtual ~CairoCompositor();
 
     const CairoSurface& GetCanvas() const
     {
@@ -73,14 +74,17 @@
                  GlyphBitmapAlphabet* dict);  // Takes ownership
 
 #if ORTHANC_ENABLE_LOCALE == 1
-    void SetFont(size_t index,
-                 Orthanc::EmbeddedResources::FileResourceId resource,
-                 unsigned int fontSize,
-                 Orthanc::Encoding codepage);
+    virtual void SetFont(size_t index,
+                         Orthanc::EmbeddedResources::FileResourceId resource,
+                         unsigned int fontSize,
+                         Orthanc::Encoding codepage);
 #endif
 
     virtual void Refresh();
 
+    void UpdateSize(unsigned int canvasWidth,
+                    unsigned int canvasHeight);
+
     Orthanc::ImageAccessor* RenderText(size_t fontIndex,
                                        const std::string& utf8) const;
   };
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Scene2D/ICompositor.h	Thu Jul 18 10:50:59 2019 +0200
@@ -0,0 +1,29 @@
+#pragma once
+
+#include <boost/noncopyable.hpp>
+#include <EmbeddedResources.h>
+#include <Core/Enumerations.h>
+
+namespace OrthancStone
+{
+  class ICompositor : public boost::noncopyable
+  {
+
+  public:
+    virtual ~ICompositor() {}
+
+    virtual unsigned int GetCanvasWidth() const = 0;
+
+    virtual unsigned int GetCanvasHeight() const = 0;
+
+    virtual void Refresh() = 0;
+
+#if ORTHANC_ENABLE_LOCALE == 1
+    virtual void SetFont(size_t index,
+                         Orthanc::EmbeddedResources::FileResourceId resource,
+                         unsigned int fontSize,
+                         Orthanc::Encoding codepage) = 0;
+#endif
+
+  };
+}
--- a/Framework/Scene2D/OpenGLCompositor.h	Wed Jul 17 15:33:07 2019 +0200
+++ b/Framework/Scene2D/OpenGLCompositor.h	Thu Jul 18 10:50:59 2019 +0200
@@ -21,6 +21,7 @@
 
 #pragma once
 
+#include "ICompositor.h"
 #include "Internals/CompositorHelper.h"
 #include "Internals/OpenGLColorTextureProgram.h"
 #include "Internals/OpenGLFloatTextureProgram.h"
@@ -29,7 +30,9 @@
 
 namespace OrthancStone
 {
-  class OpenGLCompositor : private Internals::CompositorHelper::IRendererFactory
+  class OpenGLCompositor :
+      public ICompositor,
+      private Internals::CompositorHelper::IRendererFactory
   {
   private:
     class Font;
@@ -54,9 +57,9 @@
     OpenGLCompositor(OpenGL::IOpenGLContext& context,
                      const Scene2D& scene);
 
-    ~OpenGLCompositor();
+    virtual ~OpenGLCompositor();
 
-    void Refresh();
+    virtual void Refresh();
 
     void SetFont(size_t index,
                  const GlyphBitmapAlphabet& dict);
@@ -68,12 +71,12 @@
                  Orthanc::Encoding codepage);
 #endif
 
-    unsigned int GetCanvasWidth() const
+    virtual unsigned int GetCanvasWidth() const
     {
       return canvasWidth_;
     }
 
-    unsigned int GetCanvasHeight() const
+    virtual unsigned int GetCanvasHeight() const
     {
       return canvasHeight_;
     }
--- a/Framework/Scene2DViewport/AngleMeasureTool.cpp	Wed Jul 17 15:33:07 2019 +0200
+++ b/Framework/Scene2DViewport/AngleMeasureTool.cpp	Thu Jul 18 10:50:59 2019 +0200
@@ -43,7 +43,11 @@
   AngleMeasureTool::AngleMeasureTool(
     MessageBroker& broker, boost::weak_ptr<ViewportController> controllerW)
     : MeasureTool(broker, controllerW)
+#if ORTHANC_STONE_ENABLE_OUTLINED_TEXT == 1
     , layerHolder_(boost::make_shared<LayerHolder>(controllerW,1,5))
+#else
+    , layerHolder_(boost::make_shared<LayerHolder>(controllerW, 1, 1))
+#endif
     , angleHighlightArea_(AngleHighlightArea_None)
   {
     RefreshScene();
@@ -293,8 +297,13 @@
           // http://www.ltg.ed.ac.uk/~richard/utf-8.cgi?input=00B0&mode=hex
           sprintf(buf, "%0.02f\xc2\xb0", angleDeg);
 
+#if ORTHANC_STONE_ENABLE_OUTLINED_TEXT == 1
           SetTextLayerOutlineProperties(
-            GetController()->GetScene(), layerHolder_, buf, ScenePoint2D(pointX, pointY));
+            GetController()->GetScene(), layerHolder_, buf, ScenePoint2D(pointX, pointY), 0);
+#else
+          SetTextLayerProperties(
+            GetController()->GetScene(), layerHolder_, buf, ScenePoint2D(pointX, pointY) , 0);
+#endif
 
 #if 0
           // TODO:make it togglable
--- a/Framework/Scene2DViewport/LayerHolder.cpp	Wed Jul 17 15:33:07 2019 +0200
+++ b/Framework/Scene2DViewport/LayerHolder.cpp	Thu Jul 18 10:50:59 2019 +0200
@@ -30,9 +30,11 @@
   LayerHolder::LayerHolder(
     boost::weak_ptr<ViewportController> controllerW,
     int                    polylineLayerCount,
-    int                    textLayerCount)
+    int                    textLayerCount,
+    int                    infoTextCount)
     : textLayerCount_(textLayerCount)
     , polylineLayerCount_(polylineLayerCount)
+    , infoTextCount_(infoTextCount)
     , controllerW_(controllerW)
     , baseLayerIndex_(-1)
   {
--- a/Framework/Scene2DViewport/LayerHolder.h	Wed Jul 17 15:33:07 2019 +0200
+++ b/Framework/Scene2DViewport/LayerHolder.h	Thu Jul 18 10:50:59 2019 +0200
@@ -44,7 +44,7 @@
     */
     LayerHolder(
       boost::weak_ptr<ViewportController> controllerW,
-      int polylineLayerCount, int textLayerCount);
+      int polylineLayerCount, int textLayerCount, int infoTextCount = 0);
 
     /**
     This actually creates the layers
@@ -86,13 +86,17 @@
     */
     TextSceneLayer* GetTextLayer(int index = 0);
 
+    //TextSceneLayer* GetTextLayer(int index = 0);
+
   private:
     int GetPolylineLayerIndex(int index = 0);
     int GetTextLayerIndex(int index = 0);
+    int GetInfoTextLayerIndex(int index = 0);
     Scene2D& GetScene();
 
     int textLayerCount_;
     int polylineLayerCount_;
+    int infoTextCount_;
     boost::weak_ptr<ViewportController> controllerW_;
     int baseLayerIndex_;
   };
--- a/Framework/Scene2DViewport/LineMeasureTool.cpp	Wed Jul 17 15:33:07 2019 +0200
+++ b/Framework/Scene2DViewport/LineMeasureTool.cpp	Thu Jul 18 10:50:59 2019 +0200
@@ -33,7 +33,11 @@
   LineMeasureTool::LineMeasureTool(
     MessageBroker& broker, boost::weak_ptr<ViewportController> controllerW)
     : MeasureTool(broker, controllerW)
+#if ORTHANC_STONE_ENABLE_OUTLINED_TEXT == 1
     , layerHolder_(boost::make_shared<LayerHolder>(controllerW, 1, 5))
+#else
+    , layerHolder_(boost::make_shared<LayerHolder>(controllerW, 1, 1))
+#endif
     , lineHighlightArea_(LineHighlightArea_None)
   {
 
@@ -234,8 +238,13 @@
           double midX = 0.5 * (end_.GetX() + start_.GetX());
           double midY = 0.5 * (end_.GetY() + start_.GetY());
 
+#if ORTHANC_STONE_ENABLE_OUTLINED_TEXT == 1
           SetTextLayerOutlineProperties(
-            GetController()->GetScene(), layerHolder_, buf, ScenePoint2D(midX, midY));
+            GetController()->GetScene(), layerHolder_, buf, ScenePoint2D(midX, midY), 0);
+#else
+          SetTextLayerProperties(
+            GetController()->GetScene(), layerHolder_, buf, ScenePoint2D(midX, midY), 0);
+#endif
         }
       }
       else
--- a/Framework/Scene2DViewport/MeasureToolsToolbox.cpp	Wed Jul 17 15:33:07 2019 +0200
+++ b/Framework/Scene2DViewport/MeasureToolsToolbox.cpp	Thu Jul 18 10:50:59 2019 +0200
@@ -282,6 +282,7 @@
   }
 #endif
 
+#if ORTHANC_STONE_ENABLE_OUTLINED_TEXT == 1
   /**
   This utility function assumes that the layer holder contains 5 text layers
   and will use the first four ones for the text background and the fifth one
@@ -307,7 +308,7 @@
       ORTHANC_ASSERT(textLayer != NULL);
       textLayer->SetText(text);
 
-      if (i == 4)
+      if (i == startingLayerIndex + 4)
       {
         textLayer->SetColor(TEXT_COLOR_RED,
           TEXT_COLOR_GREEN,
@@ -321,11 +322,35 @@
       }
 
       ScenePoint2D textAnchor;
+      int offIndex = i - startingLayerIndex;
+      ORTHANC_ASSERT(offIndex >= 0 && offIndex < 5);
       textLayer->SetPosition(
-        p.GetX() + xoffsets[i] * pixelToScene,
-        p.GetY() + yoffsets[i] * pixelToScene);
+        p.GetX() + xoffsets[offIndex] * pixelToScene,
+        p.GetY() + yoffsets[offIndex] * pixelToScene);
     }
   }
+#else
+  void SetTextLayerProperties(
+    Scene2D& scene
+    , boost::shared_ptr<LayerHolder> layerHolder
+    , const char* text
+    , ScenePoint2D p
+    , int layerIndex)
+  {
+    // get the scaling factor 
+    const double pixelToScene =
+      scene.GetCanvasToSceneTransform().ComputeZoom();
+
+    TextSceneLayer* textLayer = layerHolder->GetTextLayer(layerIndex);
+    ORTHANC_ASSERT(textLayer != NULL);
+    textLayer->SetText(text);
+
+    textLayer->SetColor(TEXT_COLOR_RED, TEXT_COLOR_GREEN, TEXT_COLOR_BLUE);
+
+    ScenePoint2D textAnchor;
+    textLayer->SetPosition(p.GetX(), p.GetY());
+  }
+#endif 
 
   std::ostream& operator<<(std::ostream& os, const ScenePoint2D& p)
   {
--- a/Framework/Scene2DViewport/MeasureToolsToolbox.h	Wed Jul 17 15:33:07 2019 +0200
+++ b/Framework/Scene2DViewport/MeasureToolsToolbox.h	Thu Jul 18 10:50:59 2019 +0200
@@ -170,6 +170,7 @@
     , const double d);
 
 
+#if ORTHANC_STONE_ENABLE_OUTLINED_TEXT == 1
   /**
   This helper is used when drawing text with an outline.
   It set the properties for several text layers at once : first the
@@ -177,14 +178,22 @@
   layer.
 
   The five text layers are supposed to already exist in the scene, starting
-  from layerIndex, up to (and not including) layerIndex+5.
+  from startingLayerIndex, up to (and not including) startingLayerIndex+5.
   */
   void SetTextLayerOutlineProperties(
       Scene2D& scene
     , boost::shared_ptr<LayerHolder> layerHolder
     , const char* text
     , ScenePoint2D p
-    , int startingLayerIndex = 0);
+    , int startingLayerIndex);
+#else
+  void SetTextLayerProperties(
+    Scene2D& scene
+    , boost::shared_ptr<LayerHolder> layerHolder
+    , const char* text
+    , ScenePoint2D p
+    , int layerIndex);
+#endif
 
   std::ostream& operator<<(std::ostream& os, const ScenePoint2D& p);
 }
--- a/Framework/Scene2DViewport/ViewportController.cpp	Wed Jul 17 15:33:07 2019 +0200
+++ b/Framework/Scene2DViewport/ViewportController.cpp	Thu Jul 18 10:50:59 2019 +0200
@@ -51,27 +51,59 @@
 
   void ViewportController::PushCommand(boost::shared_ptr<TrackerCommand> command)
   {
-    GetUndoStack()->PushCommand(command);
+    boost::shared_ptr<UndoStack> undoStack = undoStackW_.lock();
+    if(undoStack.get() != NULL)
+      undoStack->PushCommand(command);
+    else
+    {
+      LOG(ERROR) << "Internal error: no undo stack in the viewport controller!";
+    }
   }
 
   void ViewportController::Undo()
   {
-    GetUndoStack()->Undo();
+    boost::shared_ptr<UndoStack> undoStack = undoStackW_.lock();
+    if (undoStack.get() != NULL)
+      undoStack->Undo();
+    else
+    {
+      LOG(ERROR) << "Internal error: no undo stack in the viewport controller!";
+    }
   }
 
   void ViewportController::Redo()
   {
-    GetUndoStack()->Redo();
+    boost::shared_ptr<UndoStack> undoStack = undoStackW_.lock();
+    if (undoStack.get() != NULL)
+      undoStack->Redo();
+    else
+    {
+      LOG(ERROR) << "Internal error: no undo stack in the viewport controller!";
+    }
   }
 
   bool ViewportController::CanUndo() const
   {
-    return GetUndoStack()->CanUndo();
+    boost::shared_ptr<UndoStack> undoStack = undoStackW_.lock();
+    if (undoStack.get() != NULL)
+      return undoStack->CanUndo();
+    else
+    {
+      LOG(ERROR) << "Internal error: no undo stack in the viewport controller!";
+      return false;
+    }
   }
 
   bool ViewportController::CanRedo() const
   {
-    return GetUndoStack()->CanRedo();
+    boost::shared_ptr<UndoStack> undoStack = undoStackW_.lock();
+    if (undoStack.get() != NULL)
+      return undoStack->CanRedo();
+    else
+    {
+      LOG(ERROR) << "Internal error: no undo stack in the viewport controller!";
+      return false;
+    }
   }
   
   bool ViewportController::HandlePointerEvent(PointerEvent e)
--- a/Framework/Viewport/IViewport.h	Wed Jul 17 15:33:07 2019 +0200
+++ b/Framework/Viewport/IViewport.h	Thu Jul 18 10:50:59 2019 +0200
@@ -20,6 +20,7 @@
 
 #pragma once
 
+#include "../Scene2D/ICompositor.h"
 #include "../Scene2D/Scene2D.h"
 #include "../Scene2D/ScenePoint2D.h"
 
@@ -48,5 +49,13 @@
     virtual const std::string& GetCanvasIdentifier() const = 0;
 
     virtual ScenePoint2D GetPixelCenterCoordinates(int x, int y) const = 0;
+
+    virtual ICompositor& GetCompositor() = 0;
+
+    virtual const ICompositor& GetCompositor() const
+    {
+      IViewport* self = const_cast<IViewport*>(this);
+      return self->GetCompositor();
+    }
   };
 }
--- a/Framework/Viewport/SdlViewport.cpp	Wed Jul 17 15:33:07 2019 +0200
+++ b/Framework/Viewport/SdlViewport.cpp	Thu Jul 18 10:50:59 2019 +0200
@@ -26,24 +26,78 @@
 
 namespace OrthancStone
 {
-  SdlViewport::SdlViewport(const char* title,
-                           unsigned int width,
-                           unsigned int height,
-                           bool allowDpiScaling) :
-    ViewportBase(title),
+  SdlOpenGLViewport::SdlOpenGLViewport(const char* title,
+                                       unsigned int width,
+                                       unsigned int height,
+                                       bool allowDpiScaling) :
+    SdlViewport(title),
+    context_(title, width, height, allowDpiScaling),
+    compositor_(context_, GetScene())
+  {
+  }
+
+  SdlOpenGLViewport::SdlOpenGLViewport(const char* title,
+                                       unsigned int width,
+                                       unsigned int height,
+                                       boost::shared_ptr<Scene2D>& scene,
+                                       bool allowDpiScaling) :
+    SdlViewport(title, scene),
     context_(title, width, height, allowDpiScaling),
     compositor_(context_, GetScene())
   {
   }
 
-  SdlViewport::SdlViewport(const char* title,
-                           unsigned int width,
-                           unsigned int height,
-                           boost::shared_ptr<Scene2D>& scene,
-                           bool allowDpiScaling) :
-    ViewportBase(title, scene),
-    context_(title, width, height, allowDpiScaling),
-    compositor_(context_, GetScene())
+
+  SdlCairoViewport::SdlCairoViewport(const char* title,
+                                     unsigned int width,
+                                     unsigned int height,
+                                     bool allowDpiScaling) :
+    SdlViewport(title),
+    window_(title, width, height, false /* enable OpenGL */, allowDpiScaling),
+    compositor_(GetScene(), width, height)
+  {
+    UpdateSdlSurfaceSize(width, height);
+  }
+
+
+  SdlCairoViewport::~SdlCairoViewport()
+  {
+    if (sdlSurface_)
+    {
+      SDL_FreeSurface(sdlSurface_);
+    }
+  }
+
+
+  void SdlCairoViewport::Refresh()
   {
+    GetCompositor().Refresh();
+    window_.Render(sdlSurface_);
   }
+
+  void SdlCairoViewport::UpdateSize(unsigned int width,
+                                    unsigned int height)
+  {
+    compositor_.UpdateSize(width, height);
+    UpdateSdlSurfaceSize(width, height);
+    Refresh();
+  }
+
+
+  void SdlCairoViewport::UpdateSdlSurfaceSize(unsigned int width,
+                                              unsigned int height)
+  {
+    static const uint32_t rmask = 0x00ff0000;
+    static const uint32_t gmask = 0x0000ff00;
+    static const uint32_t bmask = 0x000000ff;
+
+    sdlSurface_ = SDL_CreateRGBSurfaceFrom((void*)(compositor_.GetCanvas().GetBuffer()), width, height, 32,
+                                           compositor_.GetCanvas().GetPitch(), rmask, gmask, bmask, 0);
+    if (!sdlSurface_)
+    {
+      LOG(ERROR) << "Cannot create a SDL surface from a Cairo surface";
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+    }
+  }
+
 }
--- a/Framework/Viewport/SdlViewport.h	Wed Jul 17 15:33:07 2019 +0200
+++ b/Framework/Viewport/SdlViewport.h	Thu Jul 18 10:50:59 2019 +0200
@@ -38,51 +38,106 @@
 
 #include "../../Applications/Sdl/SdlOpenGLContext.h"
 #include "../Scene2D/OpenGLCompositor.h"
+#include "../Scene2D/CairoCompositor.h"
 #include "ViewportBase.h"
 
 namespace OrthancStone
 {
   class SdlViewport : public ViewportBase
   {
+  public:
+    SdlViewport(const std::string& identifier)
+      : ViewportBase(identifier)
+    {}
+
+    SdlViewport(const std::string& identifier,
+                boost::shared_ptr<Scene2D>& scene)
+      : ViewportBase(identifier, scene)
+    {
+
+    }
+
+    virtual SdlWindow& GetWindow() = 0;
+    
+    virtual void UpdateSize(unsigned int width,
+                            unsigned int height) = 0;
+  };
+
+  class SdlOpenGLViewport : public SdlViewport
+  {
   private:
     SdlOpenGLContext  context_;
     OpenGLCompositor  compositor_;
 
   public:
-    SdlViewport(const char* title,
-                unsigned int width,
-                unsigned int height,
-                bool allowDpiScaling = true);
-
-    SdlViewport(const char* title,
-                unsigned int width,
-                unsigned int height,
-                boost::shared_ptr<Scene2D>& scene,
-                bool allowDpiScaling = true);
+    SdlOpenGLViewport(const char* title,
+                      unsigned int width,
+                      unsigned int height,
+                      bool allowDpiScaling = true);
 
-    virtual void Refresh()
-    {
-      compositor_.Refresh();
-    }
+    SdlOpenGLViewport(const char* title,
+                      unsigned int width,
+                      unsigned int height,
+                      boost::shared_ptr<Scene2D>& scene,
+                      bool allowDpiScaling = true);
 
-    virtual unsigned int GetCanvasWidth() const
-    {
-      return compositor_.GetCanvasWidth();
-    }
 
-    virtual unsigned int GetCanvasHeight() const
-    {
-      return compositor_.GetCanvasHeight();
-    }
-
-    OpenGLCompositor& GetCompositor()
+    virtual ICompositor& GetCompositor()
     {
       return compositor_;
     }
 
-    SdlOpenGLContext& GetContext()
+    virtual SdlWindow& GetWindow()
     {
-      return context_;
+      return context_.GetWindow();
+    }
+
+    virtual void UpdateSize(unsigned int width,
+                            unsigned int height)
+    {
+      // nothing to do in OpenGL, the OpenGLCompositor::UpdateSize will be called automatically
     }
   };
+
+
+  class SdlCairoViewport : public SdlViewport
+  {
+  private:
+    SdlWindow         window_;
+    CairoCompositor   compositor_;
+    SDL_Surface*      sdlSurface_;
+
+  public:
+    SdlCairoViewport(const char* title,
+                     unsigned int width,
+                     unsigned int height,
+                     bool allowDpiScaling = true);
+
+    SdlCairoViewport(const char* title,
+                     unsigned int width,
+                     unsigned int height,
+                     boost::shared_ptr<Scene2D>& scene,
+                     bool allowDpiScaling = true);
+
+    ~SdlCairoViewport();
+
+    virtual ICompositor& GetCompositor()
+    {
+      return compositor_;
+    }
+
+    virtual SdlWindow& GetWindow()
+    {
+      return window_;
+    }
+
+    virtual void Refresh();
+
+    virtual void UpdateSize(unsigned int width,
+                            unsigned int height);
+
+  private:
+    void UpdateSdlSurfaceSize(unsigned int width,
+                              unsigned int height);
+  };
 }
--- a/Framework/Viewport/ViewportBase.h	Wed Jul 17 15:33:07 2019 +0200
+++ b/Framework/Viewport/ViewportBase.h	Thu Jul 18 10:50:59 2019 +0200
@@ -49,5 +49,21 @@
     }
 
     virtual ScenePoint2D GetPixelCenterCoordinates(int x, int y) const;
+
+    virtual void Refresh()
+    {
+      GetCompositor().Refresh();
+    }
+
+    virtual unsigned int GetCanvasWidth() const
+    {
+      return GetCompositor().GetCanvasWidth();
+    }
+
+    virtual unsigned int GetCanvasHeight() const
+    {
+      return GetCompositor().GetCanvasHeight();
+    }
+
   };
 }
--- a/Framework/Viewport/WebAssemblyViewport.cpp	Wed Jul 17 15:33:07 2019 +0200
+++ b/Framework/Viewport/WebAssemblyViewport.cpp	Thu Jul 18 10:50:59 2019 +0200
@@ -20,29 +20,84 @@
 
 
 #include "WebAssemblyViewport.h"
+#include <emscripten/html5.h>
 
 namespace OrthancStone
 {
-  WebAssemblyViewport::WebAssemblyViewport(const std::string& canvas) :
-    ViewportBase(canvas),
+  WebAssemblyOpenGLViewport::WebAssemblyOpenGLViewport(const std::string& canvas) :
+    WebAssemblyViewport(canvas),
     context_(canvas),
     compositor_(context_, GetScene())
   {
   }
 
     
-  WebAssemblyViewport::WebAssemblyViewport(const std::string& canvas,
-                                           boost::shared_ptr<Scene2D>& scene) :
-    ViewportBase(canvas, scene),
+  WebAssemblyOpenGLViewport::WebAssemblyOpenGLViewport(const std::string& canvas,
+                                                       boost::shared_ptr<Scene2D>& scene) :
+    WebAssemblyViewport(canvas, scene),
     context_(canvas),
     compositor_(context_, GetScene())
   {
   }
     
 
-  void WebAssemblyViewport::UpdateSize()
+  void WebAssemblyOpenGLViewport::UpdateSize()
   {
     context_.UpdateSize();  // First read the size of the canvas
     compositor_.Refresh();  // Then refresh the content of the canvas
   }
+
+
+  WebAssemblyCairoViewport::WebAssemblyCairoViewport(const std::string& canvas) :
+    WebAssemblyViewport(canvas),
+    canvas_(canvas),
+    compositor_(GetScene(), 1024, 768)
+  {
+  }
+
+    
+  WebAssemblyCairoViewport::WebAssemblyCairoViewport(const std::string& canvas,
+                                                     boost::shared_ptr<Scene2D>& scene) :
+    WebAssemblyViewport(canvas, scene),
+    canvas_(canvas),
+    compositor_(GetScene(), 1024, 768)
+  {
+  }
+
+
+  void WebAssemblyCairoViewport::UpdateSize()
+  {
+    LOG(INFO) << "updating cairo viewport size";
+    double w, h;
+    emscripten_get_element_css_size(canvas_.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
+     **/
+    unsigned int canvasWidth = 0;
+    unsigned int canvasHeight = 0;
+
+    if (w > 0 ||
+        h > 0)
+    {
+      canvasWidth = static_cast<unsigned int>(boost::math::iround(w));
+      canvasHeight = static_cast<unsigned int>(boost::math::iround(h));
+    }
+
+    emscripten_set_canvas_element_size(canvas_.c_str(), canvasWidth, canvasHeight);
+    compositor_.UpdateSize(canvasWidth, canvasHeight);
+  }
+
+  void WebAssemblyCairoViewport::Refresh()
+  {
+    LOG(INFO) << "refreshing cairo viewport, TODO: blit to the canvans.getContext('2d')";
+    GetCompositor().Refresh();
+  }
+
 }
--- a/Framework/Viewport/WebAssemblyViewport.h	Wed Jul 17 15:33:07 2019 +0200
+++ b/Framework/Viewport/WebAssemblyViewport.h	Thu Jul 18 10:50:59 2019 +0200
@@ -23,43 +23,67 @@
 
 #include "../OpenGL/WebAssemblyOpenGLContext.h"
 #include "../Scene2D/OpenGLCompositor.h"
+#include "../Scene2D/CairoCompositor.h"
 #include "ViewportBase.h"
 
 namespace OrthancStone
 {
   class WebAssemblyViewport : public ViewportBase
   {
+  public:
+    WebAssemblyViewport(const std::string& identifier)
+      : ViewportBase(identifier)
+    {
+    }
+
+    WebAssemblyViewport(const std::string& identifier,
+                        boost::shared_ptr<Scene2D>& scene)
+      : ViewportBase(identifier, scene)
+    {
+    }
+  };
+
+  class WebAssemblyOpenGLViewport : public WebAssemblyViewport
+  {
   private:
     OpenGL::WebAssemblyOpenGLContext  context_;
     OpenGLCompositor                  compositor_;
 
   public:
-    WebAssemblyViewport(const std::string& canvas);
-    
-    WebAssemblyViewport(const std::string& canvas,
-                        boost::shared_ptr<Scene2D>& scene);
+    WebAssemblyOpenGLViewport(const std::string& canvas);
     
-    virtual void Refresh()
-    {
-      compositor_.Refresh();
-    }
-
-    virtual unsigned int GetCanvasWidth() const
-    {
-      return context_.GetCanvasWidth();
-    }
-
-    virtual unsigned int GetCanvasHeight() const
-    {
-      return context_.GetCanvasHeight();
-    }
-
+    WebAssemblyOpenGLViewport(const std::string& canvas,
+                              boost::shared_ptr<Scene2D>& scene);
+    
     // This function must be called each time the browser window is resized
     void UpdateSize();
 
-    OpenGLCompositor& GetCompositor()
+    virtual ICompositor& GetCompositor()
     {
       return compositor_;
     }
   };
+
+  class WebAssemblyCairoViewport : public WebAssemblyViewport
+  {
+  private:
+    CairoCompositor                  compositor_;
+    std::string                      canvas_;
+
+  public:
+    WebAssemblyCairoViewport(const std::string& canvas);
+    
+    WebAssemblyCairoViewport(const std::string& canvas,
+                             boost::shared_ptr<Scene2D>& scene);
+    
+    void UpdateSize(); 
+
+    virtual void Refresh();
+
+    virtual ICompositor& GetCompositor()
+    {
+      return compositor_;
+    }
+  };
+
 }
--- a/Resources/CMake/OrthancStoneConfiguration.cmake	Wed Jul 17 15:33:07 2019 +0200
+++ b/Resources/CMake/OrthancStoneConfiguration.cmake	Thu Jul 18 10:50:59 2019 +0200
@@ -444,6 +444,7 @@
   ${ORTHANC_STONE_ROOT}/Framework/Scene2D/ColorTextureSceneLayer.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Scene2D/FloatTextureSceneLayer.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Scene2D/GrayscaleStyleConfigurator.cpp
+  ${ORTHANC_STONE_ROOT}/Framework/Scene2D/ICompositor.h
   ${ORTHANC_STONE_ROOT}/Framework/Scene2D/InfoPanelSceneLayer.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/CairoColorTextureRenderer.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/CairoFloatTextureRenderer.cpp
--- a/Samples/Sdl/BasicScene.cpp	Wed Jul 17 15:33:07 2019 +0200
+++ b/Samples/Sdl/BasicScene.cpp	Thu Jul 18 10:50:59 2019 +0200
@@ -48,6 +48,7 @@
 static const unsigned int FONT_SIZE = 32;
 static const int LAYER_POSITION = 150;
 
+#define OPENGL_ENABLED 0
 
 void PrepareScene(OrthancStone::Scene2D& scene)
 {
@@ -259,7 +260,7 @@
   }
 }
 
-
+#if OPENGL_ENABLED==1
 static void GLAPIENTRY
 OpenGLMessageCallback(GLenum source,
                       GLenum type,
@@ -276,7 +277,7 @@
             type, severity, message );
   }
 }
-
+#endif
 
 void Run(OrthancStone::MessageBroker& broker,
          OrthancStone::SdlViewport& viewport)
@@ -286,8 +287,10 @@
   boost::shared_ptr<ViewportController> controller(
     new ViewportController(boost::make_shared<UndoStack>(), broker, viewport));
   
+#if OPENGL_ENABLED==1
   glEnable(GL_DEBUG_OUTPUT);
   glDebugMessageCallback(OpenGLMessageCallback, 0);
+#endif
 
   boost::shared_ptr<IFlexiblePointerTracker> tracker;
 
@@ -334,6 +337,7 @@
         {
           case SDL_WINDOWEVENT_SIZE_CHANGED:
             tracker.reset();
+            viewport.UpdateSize(event.window.data1, event.window.data2);
             break;
 
           case SDL_WINDOWEVENT_SHOWN:
@@ -356,7 +360,7 @@
         switch (event.key.keysym.sym)
         {
           case SDLK_f:
-            viewport.GetContext().GetWindow().ToggleMaximize();
+            viewport.GetWindow().ToggleMaximize();
             break;
               
           case SDLK_q:
@@ -390,7 +394,11 @@
 
   try
   {
-    OrthancStone::SdlViewport viewport("Hello", 1024, 768);
+#if OPENGL_ENABLED==1
+    OrthancStone::SdlOpenGLViewport viewport("Hello", 1024, 768);
+#else
+    OrthancStone::SdlCairoViewport viewport("Hello", 1024, 768);
+#endif
     PrepareScene(viewport.GetScene());
 
     viewport.GetCompositor().SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, 
--- a/Samples/WebAssembly/NOTES.txt	Wed Jul 17 15:33:07 2019 +0200
+++ b/Samples/WebAssembly/NOTES.txt	Thu Jul 18 10:50:59 2019 +0200
@@ -74,4 +74,3 @@
 cd build_stone_newsamples_wasm_wsl
 cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=${EMSDK}/fastcomp/emscripten/cmake/Modules/Platform/Emscripten.cmake -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT=/mnt/c/o/orthanc/ -DCMAKE_BUILD_TYPE=Release -DALLOW_DOWNLOADS=ON /mnt/c/o/orthanc-stone/Samples/WebAssembly -DCMAKE_INSTALL_PREFIX=/mnt/c/o/build_install_stone_newsamples_wasm_wsl
 ninja
-