changeset 597:9e51fb773bbd

CairoCompositor
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 26 Apr 2019 17:28:18 +0200
parents b716763571ad
children a806abb497b8
files Framework/Scene2D/CairoCompositor.cpp Framework/Scene2D/CairoCompositor.h Framework/Scene2D/Internals/CairoColorTextureRenderer.cpp Framework/Scene2D/Internals/CairoColorTextureRenderer.h Framework/Scene2D/Internals/CairoFloatTextureRenderer.cpp Framework/Scene2D/Internals/CairoFloatTextureRenderer.h Framework/Scene2D/Internals/CairoInfoPanelRenderer.cpp Framework/Scene2D/Internals/CairoInfoPanelRenderer.h Framework/Scene2D/Internals/CairoTextRenderer.cpp Framework/Scene2D/Internals/CairoTextRenderer.h Framework/Scene2D/OpenGLCompositor.h Resources/CMake/OrthancStoneConfiguration.cmake
diffstat 12 files changed, 852 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Scene2D/CairoCompositor.cpp	Fri Apr 26 17:28:18 2019 +0200
@@ -0,0 +1,177 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "CairoCompositor.h"
+
+#include "Internals/CairoColorTextureRenderer.h"
+#include "Internals/CairoFloatTextureRenderer.h"
+#include "Internals/CairoInfoPanelRenderer.h"
+#include "Internals/CairoPolylineRenderer.h"
+#include "Internals/CairoTextRenderer.h"
+
+#include <Core/OrthancException.h>
+
+namespace OrthancStone
+{
+  cairo_t* CairoCompositor::GetCairoContext()
+  {
+    if (context_.get() == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+    else
+    {
+      return context_->GetObject();
+    }
+  }
+
+    
+  Internals::CompositorHelper::ILayerRenderer* CairoCompositor::Create(const ISceneLayer& layer)
+  {
+    switch (layer.GetType())
+    {
+      case ISceneLayer::Type_Polyline:
+        return new Internals::CairoPolylineRenderer(*this, layer);
+
+      case ISceneLayer::Type_InfoPanel:
+        return new Internals::CairoInfoPanelRenderer(*this, layer);
+
+      case ISceneLayer::Type_ColorTexture:
+        return new Internals::CairoColorTextureRenderer(*this, layer);
+
+      case ISceneLayer::Type_FloatTexture:
+        return new Internals::CairoFloatTextureRenderer(*this, layer);
+
+      case ISceneLayer::Type_Text:
+      {
+        const TextSceneLayer& l = dynamic_cast<const TextSceneLayer&>(layer);
+
+        Fonts::const_iterator found = fonts_.find(l.GetFontIndex());
+        if (found == fonts_.end())
+        {
+          return NULL;
+        }
+        else
+        {
+          assert(found->second != NULL);
+          return new Internals::CairoTextRenderer(*this, *found->second, l);
+        }
+      }
+
+      default:
+        return NULL;
+    }
+  }
+
+
+  CairoCompositor::CairoCompositor(Scene2D& scene,
+                                   unsigned int canvasWidth,
+                                   unsigned int canvasHeight) :
+    helper_(scene, *this)
+  {
+    canvas_.SetSize(canvasWidth, canvasHeight, false);
+  }
+
+    
+  CairoCompositor::~CairoCompositor()
+  {
+    for (Fonts::iterator it = fonts_.begin(); it != fonts_.end(); ++it)
+    {
+      assert(it->second != NULL);
+      delete it->second;
+    }
+  }
+
+
+  void CairoCompositor::SetFont(size_t index,
+                                GlyphBitmapAlphabet* dict)  // Takes ownership
+  {
+    if (dict == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
+    }
+    else
+    {
+      std::auto_ptr<GlyphBitmapAlphabet> protection(dict);
+      
+      Fonts::iterator found = fonts_.find(index);
+
+      if (found == fonts_.end())
+      {
+        fonts_[index] = protection.release();
+      }
+      else
+      {
+        assert(found->second != NULL);
+        delete found->second;
+
+        found->second = protection.release();
+      }
+    }
+  }
+    
+
+#if ORTHANC_ENABLE_LOCALE == 1
+  void CairoCompositor::SetFont(size_t index,
+                                Orthanc::EmbeddedResources::FileResourceId resource,
+                                unsigned int fontSize,
+                                Orthanc::Encoding codepage)
+  {
+    FontRenderer renderer;
+    renderer.LoadFont(font, fontSize);
+
+    std::auto_ptr<GlyphBitmapAlphabet> alphabet(new GlyphBitmapAlphabet);
+    alphabet->LoadCodepage(renderer, codepage);
+
+    SetFont(index, alphabet.release());
+  }
+#endif
+
+
+  void CairoCompositor::Refresh()
+  {
+    context_.reset(new CairoContext(canvas_));
+
+    // https://www.cairographics.org/FAQ/#clear_a_surface
+    cairo_set_source_rgba(context_->GetObject(), 0, 0, 0, 255);
+    cairo_paint(context_->GetObject());
+
+    helper_.Refresh(canvas_.GetWidth(), canvas_.GetHeight());
+    context_.reset();
+  }
+
+
+  Orthanc::ImageAccessor* CairoCompositor::RenderText(size_t fontIndex,
+                                                      const std::string& utf8) const
+  {
+    Fonts::const_iterator found = fonts_.find(fontIndex);
+
+    if (found == fonts_.end())
+    {
+      return NULL;
+    }
+    else
+    {
+      assert(found->second != NULL);
+      return found->second->RenderText(utf8);
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Scene2D/CairoCompositor.h	Fri Apr 26 17:28:18 2019 +0200
@@ -0,0 +1,86 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../Fonts/GlyphBitmapAlphabet.h"
+#include "../Viewport/CairoContext.h"
+#include "Internals/CompositorHelper.h"
+#include "Internals/ICairoContextProvider.h"
+
+namespace OrthancStone
+{
+  class CairoCompositor :
+    private Internals::CompositorHelper::IRendererFactory,
+    private Internals::ICairoContextProvider
+  {
+  private:
+    typedef std::map<size_t, GlyphBitmapAlphabet*>   Fonts;
+
+    Internals::CompositorHelper  helper_;
+    CairoSurface                 canvas_;
+    Fonts                        fonts_;
+
+    // Only valid during a call to "Refresh()"
+    std::auto_ptr<CairoContext>  context_;
+
+    virtual cairo_t* GetCairoContext();
+
+    virtual unsigned int GetCairoWidth()
+    {
+      return canvas_.GetWidth();
+    }
+
+    virtual unsigned int GetCairoHeight()
+    {
+      return canvas_.GetHeight();
+    }
+    
+    virtual Internals::CompositorHelper::ILayerRenderer* Create(const ISceneLayer& layer);
+
+  public:
+    CairoCompositor(Scene2D& scene,
+                    unsigned int canvasWidth,
+                    unsigned int canvasHeight);
+    
+    ~CairoCompositor();
+
+    const CairoSurface& GetCanvas() const
+    {
+      return canvas_;
+    }
+    
+    void SetFont(size_t index,
+                 GlyphBitmapAlphabet* dict);  // Takes ownership
+
+#if ORTHANC_ENABLE_LOCALE == 1
+    void SetFont(size_t index,
+                 Orthanc::EmbeddedResources::FileResourceId resource,
+                 unsigned int fontSize,
+                 Orthanc::Encoding codepage);
+#endif
+
+    void Refresh();
+
+    Orthanc::ImageAccessor* RenderText(size_t fontIndex,
+                                       const std::string& utf8) const;
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Scene2D/Internals/CairoColorTextureRenderer.cpp	Fri Apr 26 17:28:18 2019 +0200
@@ -0,0 +1,79 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "CairoColorTextureRenderer.h"
+
+#include "../ColorTextureSceneLayer.h"
+
+namespace OrthancStone
+{
+  namespace Internals
+  {
+    CairoColorTextureRenderer::CairoColorTextureRenderer(ICairoContextProvider& target,
+                                                         const ISceneLayer& layer) :
+      target_(target)
+    {
+      Update(layer);
+    }
+
+    
+    void CairoColorTextureRenderer::Update(const ISceneLayer& layer)
+    {
+      const ColorTextureSceneLayer& l = dynamic_cast<const ColorTextureSceneLayer&>(layer);
+
+      texture_.Copy(l.GetTexture(), true);
+      textureTransform_ = l.GetTransform();
+      isLinearInterpolation_ = l.IsLinearInterpolation();
+    }
+
+    
+    void CairoColorTextureRenderer::Render(const AffineTransform2D& transform)
+    {
+      cairo_t* cr = target_.GetCairoContext();
+
+      AffineTransform2D t =
+        AffineTransform2D::Combine(transform, textureTransform_);
+      Matrix h = t.GetHomogeneousMatrix();
+      
+      cairo_save(cr);
+
+      cairo_matrix_t m;
+      cairo_matrix_init(&m, h(0, 0), h(1, 0), h(0, 1), h(1, 1), h(0, 2), h(1, 2));
+      cairo_transform(cr, &m);
+
+      cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+      cairo_set_source_surface(cr, texture_.GetObject(), 0, 0);
+
+      if (isLinearInterpolation_)
+      {
+        cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_BILINEAR);
+      }
+      else
+      {
+        cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST);
+      }
+
+      cairo_paint(cr);
+
+      cairo_restore(cr);
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Scene2D/Internals/CairoColorTextureRenderer.h	Fri Apr 26 17:28:18 2019 +0200
@@ -0,0 +1,49 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../../Viewport/CairoSurface.h"
+#include "CompositorHelper.h"
+#include "ICairoContextProvider.h"
+
+namespace OrthancStone
+{
+  namespace Internals
+  {
+    class CairoColorTextureRenderer : public CompositorHelper::ILayerRenderer
+    {
+    private:
+      ICairoContextProvider&  target_;
+      CairoSurface            texture_;
+      AffineTransform2D       textureTransform_;
+      bool                    isLinearInterpolation_;
+    
+    public:
+      CairoColorTextureRenderer(ICairoContextProvider& target,
+                                const ISceneLayer& layer);
+
+      virtual void Update(const ISceneLayer& layer);
+    
+      virtual void Render(const AffineTransform2D& transform);
+    };
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Scene2D/Internals/CairoFloatTextureRenderer.cpp	Fri Apr 26 17:28:18 2019 +0200
@@ -0,0 +1,116 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "CairoFloatTextureRenderer.h"
+
+#include "../FloatTextureSceneLayer.h"
+
+namespace OrthancStone
+{
+  namespace Internals
+  {
+    void CairoFloatTextureRenderer::Update(const ISceneLayer& layer)
+    {
+      const FloatTextureSceneLayer& l = dynamic_cast<const FloatTextureSceneLayer&>(layer);
+
+      textureTransform_ = l.GetTransform();
+      isLinearInterpolation_ = l.IsLinearInterpolation();
+
+      float windowCenter, windowWidth;
+      l.GetWindowing(windowCenter, windowWidth);
+
+      const float a = windowCenter - windowWidth;
+      const float slope = 256.0f / (2.0f * windowWidth);
+
+      const Orthanc::ImageAccessor& source = l.GetTexture();
+      const unsigned int width = source.GetWidth();
+      const unsigned int height = source.GetHeight();
+      texture_.SetSize(width, height, false);
+
+      Orthanc::ImageAccessor target;
+      texture_.GetWriteableAccessor(target);
+
+      assert(source.GetFormat() == Orthanc::PixelFormat_Float32 &&
+             target.GetFormat() == Orthanc::PixelFormat_BGRA32 &&
+             sizeof(float) == 4);
+
+      for (unsigned int y = 0; y < height; y++)
+      {
+        const float* p = reinterpret_cast<const float*>(source.GetConstRow(y));
+        uint8_t* q = reinterpret_cast<uint8_t*>(target.GetRow(y));
+
+        for (unsigned int x = 0; x < width; x++)
+        {
+          float v = (*p - a) * slope;
+          if (v <= 0)
+          {
+            v = 0;
+          }
+          else if (v >= 255)
+          {
+            v = 255;
+          }
+
+          uint8_t vv = static_cast<uint8_t>(v);
+
+          q[0] = vv;
+          q[1] = vv;
+          q[2] = vv;
+
+          p++;
+          q += 4;
+        }
+      }
+    }
+
+      
+    void CairoFloatTextureRenderer::Render(const AffineTransform2D& transform)
+    {
+      cairo_t* cr = target_.GetCairoContext();
+
+      AffineTransform2D t =
+        AffineTransform2D::Combine(transform, textureTransform_);
+      Matrix h = t.GetHomogeneousMatrix();
+      
+      cairo_save(cr);
+
+      cairo_matrix_t m;
+      cairo_matrix_init(&m, h(0, 0), h(1, 0), h(0, 1), h(1, 1), h(0, 2), h(1, 2));
+      cairo_transform(cr, &m);
+
+      cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+      cairo_set_source_surface(cr, texture_.GetObject(), 0, 0);
+
+      if (isLinearInterpolation_)
+      {
+        cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_BILINEAR);
+      }
+      else
+      {
+        cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST);
+      }
+
+      cairo_paint(cr);
+
+      cairo_restore(cr);
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Scene2D/Internals/CairoFloatTextureRenderer.h	Fri Apr 26 17:28:18 2019 +0200
@@ -0,0 +1,53 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../../Viewport/CairoSurface.h"
+#include "CompositorHelper.h"
+#include "ICairoContextProvider.h"
+
+namespace OrthancStone
+{
+  namespace Internals
+  {
+    class CairoFloatTextureRenderer : public CompositorHelper::ILayerRenderer
+    {
+    private:
+      ICairoContextProvider&  target_;
+      CairoSurface            texture_;
+      AffineTransform2D       textureTransform_;
+      bool                    isLinearInterpolation_;
+    
+    public:
+      CairoFloatTextureRenderer(ICairoContextProvider& target,
+                                const ISceneLayer& layer) :
+        target_(target)
+      {
+        Update(layer);
+      }
+
+      virtual void Update(const ISceneLayer& layer);
+    
+      virtual void Render(const AffineTransform2D& transform);
+    };
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Scene2D/Internals/CairoInfoPanelRenderer.cpp	Fri Apr 26 17:28:18 2019 +0200
@@ -0,0 +1,73 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "CairoInfoPanelRenderer.h"
+
+#include "../InfoPanelSceneLayer.h"
+
+namespace OrthancStone
+{
+  namespace Internals
+  {
+    void CairoInfoPanelRenderer::Update(const ISceneLayer& layer)
+    {
+      const InfoPanelSceneLayer& l = dynamic_cast<const InfoPanelSceneLayer&>(layer);
+
+      texture_.Copy(l.GetTexture(), true);
+      anchor_ = l.GetAnchor();
+      isLinearInterpolation_ = l.IsLinearInterpolation();
+    }
+
+    
+    void CairoInfoPanelRenderer::Render(const AffineTransform2D& transform)
+    {
+      int dx, dy;
+      InfoPanelSceneLayer::ComputeAnchorLocation(
+        dx, dy, anchor_, texture_.GetWidth(), texture_.GetHeight(),
+        target_.GetCairoWidth(), target_.GetCairoHeight());
+
+      cairo_t* cr = target_.GetCairoContext();
+
+      cairo_save(cr);
+
+      cairo_matrix_t t;
+      cairo_matrix_init_identity(&t);
+      cairo_matrix_translate(&t, dx, dy);
+      cairo_transform(cr, &t);
+
+      cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+      cairo_set_source_surface(cr, texture_.GetObject(), 0, 0);
+
+      if (isLinearInterpolation_)
+      {
+        cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_BILINEAR);
+      }
+      else
+      {
+        cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST);
+      }
+
+      cairo_paint(cr);
+
+      cairo_restore(cr);
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Scene2D/Internals/CairoInfoPanelRenderer.h	Fri Apr 26 17:28:18 2019 +0200
@@ -0,0 +1,53 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../../Viewport/CairoSurface.h"
+#include "CompositorHelper.h"
+#include "ICairoContextProvider.h"
+
+namespace OrthancStone
+{
+  namespace Internals
+  {
+    class CairoInfoPanelRenderer : public CompositorHelper::ILayerRenderer
+    {
+    private:
+      ICairoContextProvider& target_;
+      CairoSurface           texture_;
+      BitmapAnchor           anchor_;
+      bool                   isLinearInterpolation_;
+
+    public:
+      CairoInfoPanelRenderer(ICairoContextProvider& target,
+                             const ISceneLayer& layer) :
+        target_(target)
+      {
+        Update(layer);
+      }
+
+      virtual void Update(const ISceneLayer& layer);
+      
+      virtual void Render(const AffineTransform2D& transform);
+    };
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Scene2D/Internals/CairoTextRenderer.cpp	Fri Apr 26 17:28:18 2019 +0200
@@ -0,0 +1,114 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "CairoTextRenderer.h"
+
+#include <Core/OrthancException.h>
+
+namespace OrthancStone
+{
+  namespace Internals
+  {
+    CairoTextRenderer::CairoTextRenderer(ICairoContextProvider& target,
+                                         const GlyphBitmapAlphabet& alphabet,
+                                         const TextSceneLayer& layer) :
+      CairoBaseRenderer(target, layer)
+    {
+      std::auto_ptr<Orthanc::ImageAccessor> source(alphabet.RenderText(layer.GetText()));
+
+      if (source.get() != NULL)
+      {
+        text_.SetSize(source->GetWidth(), source->GetHeight(), true);
+
+        Orthanc::ImageAccessor target;
+        text_.GetWriteableAccessor(target);
+        
+        if (source->GetFormat() != Orthanc::PixelFormat_Grayscale8 ||
+            target.GetFormat() != Orthanc::PixelFormat_BGRA32)
+        {
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+        }
+        
+        const unsigned int width = source->GetWidth();
+        const unsigned int red = layer.GetRed();
+        const unsigned int green = layer.GetGreen();
+        const unsigned int blue = layer.GetBlue();
+
+        for (unsigned int y = 0; y < source->GetHeight(); y++)
+        {
+          const uint8_t* p = reinterpret_cast<const uint8_t*>(source->GetConstRow(y));
+          uint8_t* q = reinterpret_cast<uint8_t*>(target.GetRow(y));
+          
+          for (unsigned int x = 0; x < width; x++)
+          {
+            unsigned int alpha = *p;
+
+            // Premultiplied alpha
+            q[0] = static_cast<uint8_t>((blue * alpha) / 255);
+            q[1] = static_cast<uint8_t>((green * alpha) / 255);
+            q[2] = static_cast<uint8_t>((red * alpha) / 255);
+            q[3] = *p;
+            
+            p++;
+            q += 4;
+          }
+        }
+
+        cairo_surface_mark_dirty(text_.GetObject());
+      }
+    }
+
+      
+    void CairoTextRenderer::Render(const AffineTransform2D& transform)
+    {
+      if (text_.GetWidth() != 0 &&
+          text_.GetHeight() != 0)
+      {
+        const TextSceneLayer& layer = GetLayer<TextSceneLayer>();
+      
+        cairo_t* cr = GetCairoContext();
+        cairo_set_source_rgb(cr, layer.GetRedAsFloat(), layer.GetGreenAsFloat(), layer.GetBlueAsFloat());
+
+        double dx, dy;  // In pixels
+        ComputeAnchorTranslation(dx, dy, layer.GetAnchor(), text_.GetWidth(),
+                                 text_.GetHeight(), layer.GetBorder());
+      
+        double x = layer.GetX();
+        double y = layer.GetY();
+        transform.Apply(x, y);
+
+        cairo_save(cr);
+
+        cairo_matrix_t t;
+        cairo_matrix_init_identity(&t);
+        cairo_matrix_translate(&t, x + dx, y + dy);
+        cairo_transform(cr, &t);
+
+        cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+        cairo_set_source_surface(cr, text_.GetObject(), 0, 0);
+        cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_BILINEAR);
+        cairo_paint(cr);
+
+        cairo_restore(cr);
+      }
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Scene2D/Internals/CairoTextRenderer.h	Fri Apr 26 17:28:18 2019 +0200
@@ -0,0 +1,46 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2019 Osimis S.A., Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Affero General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "../../Fonts/GlyphBitmapAlphabet.h"
+#include "../../Viewport/CairoSurface.h"
+#include "../TextSceneLayer.h"
+#include "CairoBaseRenderer.h"
+
+namespace OrthancStone
+{
+  namespace Internals
+  {
+    class CairoTextRenderer : public CairoBaseRenderer
+    {
+    private:
+      CairoSurface  text_;
+    
+    public:
+      CairoTextRenderer(ICairoContextProvider& target,
+                        const GlyphBitmapAlphabet& alphabet,
+                        const TextSceneLayer& layer);
+    
+      virtual void Render(const AffineTransform2D& transform);
+    };
+  }
+}
--- a/Framework/Scene2D/OpenGLCompositor.h	Fri Apr 26 17:05:14 2019 +0200
+++ b/Framework/Scene2D/OpenGLCompositor.h	Fri Apr 26 17:28:18 2019 +0200
@@ -29,7 +29,7 @@
 
 namespace OrthancStone
 {
-  class OpenGLCompositor : protected Internals::CompositorHelper::IRendererFactory
+  class OpenGLCompositor : private Internals::CompositorHelper::IRendererFactory
   {
   private:
     class Font;
@@ -48,7 +48,6 @@
     
     const Font* GetFont(size_t fontIndex) const;
 
-  protected:
     virtual Internals::CompositorHelper::ILayerRenderer* Create(const ISceneLayer& layer);
 
   public:
--- a/Resources/CMake/OrthancStoneConfiguration.cmake	Fri Apr 26 17:05:14 2019 +0200
+++ b/Resources/CMake/OrthancStoneConfiguration.cmake	Fri Apr 26 17:28:18 2019 +0200
@@ -257,10 +257,15 @@
   #${ORTHANC_STONE_ROOT}/Framework/Layers/SeriesFrameRendererFactory.cpp
   #${ORTHANC_STONE_ROOT}/Framework/Layers/SingleFrameRendererFactory.cpp
 
+  ${ORTHANC_STONE_ROOT}/Framework/Scene2D/CairoCompositor.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Scene2D/ColorTextureSceneLayer.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Scene2D/FloatTextureSceneLayer.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Scene2D/InfoPanelSceneLayer.cpp
+  ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/CairoColorTextureRenderer.cpp
+  ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/CairoFloatTextureRenderer.cpp
+  ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/CairoInfoPanelRenderer.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/CairoPolylineRenderer.cpp
+  ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/CairoTextRenderer.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/CompositorHelper.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/FixedPointAligner.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Scene2D/PanSceneTracker.cpp