changeset 577:b098a3aaf694

alphabet of glyphs
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 19 Apr 2019 15:42:57 +0200
parents 529c9617654b
children 21fd70df3fc9
files Framework/Fonts/GlyphBitmapAlphabet.cpp Framework/Fonts/GlyphBitmapAlphabet.h Framework/Fonts/GlyphTextureAlphabet.cpp Framework/Fonts/GlyphTextureAlphabet.h Framework/Fonts/OpenGLTextCoordinates.cpp Framework/Fonts/OpenGLTextCoordinates.h Framework/Fonts/TextBoundingBox.cpp Framework/Fonts/TextBoundingBox.h Resources/CMake/OrthancStoneConfiguration.cmake
diffstat 9 files changed, 906 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Fonts/GlyphBitmapAlphabet.cpp	Fri Apr 19 15:42:57 2019 +0200
@@ -0,0 +1,113 @@
+/**
+ * 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 "GlyphBitmapAlphabet.h"
+
+#include "TextBoundingBox.h"
+#include "../Toolbox/DynamicBitmap.h"
+
+#include <Core/Images/Image.h>
+#include <Core/Images/ImageProcessing.h>
+
+namespace OrthancStone
+{
+  class GlyphBitmapAlphabet::RenderTextVisitor : public GlyphAlphabet::ITextVisitor
+  {
+  private:
+    Orthanc::ImageAccessor&     target_;
+    const GlyphBitmapAlphabet&  that_;
+    int                         offsetX_;
+    int                         offsetY_;
+      
+  public:
+    RenderTextVisitor(Orthanc::ImageAccessor&  target,
+                      const GlyphBitmapAlphabet&  that,
+                      int  offsetX,
+                      int  offsetY) :
+      target_(target),
+      that_(that),
+      offsetX_(offsetX),
+      offsetY_(offsetY)
+    {
+    }
+
+    virtual void Visit(uint32_t unicode,
+                       int x,
+                       int y,
+                       unsigned int width,
+                       unsigned int height,
+                       const Orthanc::IDynamicObject* payload)
+    {
+      int left = x + offsetX_;
+      int top = y + offsetY_;
+
+      assert(payload != NULL);
+      const DynamicBitmap& glyph = *dynamic_cast<const DynamicBitmap*>(payload);
+        
+      assert(left >= 0 &&
+             top >= 0 &&
+             static_cast<unsigned int>(left) + width <= target_.GetWidth() &&
+             static_cast<unsigned int>(top) + height <= target_.GetHeight() &&
+             width == glyph.GetBitmap().GetWidth() &&
+             height == glyph.GetBitmap().GetHeight());
+        
+      {
+        Orthanc::ImageAccessor region;
+        target_.GetRegion(region, left, top, width, height);
+        Orthanc::ImageProcessing::Copy(region, glyph.GetBitmap());
+      }
+    }
+  };
+  
+    
+#if ORTHANC_ENABLE_LOCALE == 1
+  void GlyphBitmapAlphabet::LoadCodepage(FontRenderer& renderer,
+                                         Orthanc::Encoding codepage)
+  {
+    for (unsigned int i = 0; i < 256; i++)
+    {
+      uint32_t unicode;
+      if (GlyphAlphabet::GetUnicodeFromCodepage(unicode, i, codepage))
+      {
+        AddUnicodeCharacter(renderer, unicode);
+      }
+    }
+  }
+#endif
+
+    
+  Orthanc::ImageAccessor* GlyphBitmapAlphabet::RenderText(const std::string& utf8) const
+  {
+    TextBoundingBox box(alphabet_, utf8);
+
+    std::auto_ptr<Orthanc::ImageAccessor> bitmap(
+      new Orthanc::Image(Orthanc::PixelFormat_Grayscale8,
+                         box.GetWidth(), box.GetHeight(),
+                         true /* force minimal pitch */));
+
+    Orthanc::ImageProcessing::Set(*bitmap, 0);
+
+    RenderTextVisitor visitor(*bitmap, *this, -box.GetLeft(), -box.GetTop());
+    alphabet_.Apply(visitor, utf8);
+
+    return bitmap.release();
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Fonts/GlyphBitmapAlphabet.h	Fri Apr 19 15:42:57 2019 +0200
@@ -0,0 +1,58 @@
+/**
+ * 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 "GlyphAlphabet.h"
+
+#include <Core/Images/ImageAccessor.h>
+
+namespace OrthancStone
+{
+  class GlyphBitmapAlphabet : public boost::noncopyable
+  {
+  private:
+    class RenderTextVisitor;
+
+    GlyphAlphabet  alphabet_;
+
+  public:
+    const GlyphAlphabet& GetAlphabet() const
+    {
+      return alphabet_;
+    }    
+    
+    void AddUnicodeCharacter(FontRenderer& renderer,
+                             uint32_t unicode)
+    {
+      alphabet_.Register(renderer, unicode);
+    }
+
+
+#if ORTHANC_ENABLE_LOCALE == 1
+    void LoadCodepage(FontRenderer& renderer,
+                      Orthanc::Encoding codepage);
+#endif
+    
+    
+    Orthanc::ImageAccessor* RenderText(const std::string& utf8) const;
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Fonts/GlyphTextureAlphabet.cpp	Fri Apr 19 15:42:57 2019 +0200
@@ -0,0 +1,295 @@
+/**
+ * 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 "GlyphTextureAlphabet.h"
+
+#include "TextBoundingBox.h"
+#include "../Toolbox/DynamicBitmap.h"
+
+#include <Core/Images/Image.h>
+#include <Core/Images/ImageProcessing.h>
+#include <Core/OrthancException.h>
+
+#include <boost/math/special_functions/round.hpp>
+
+
+namespace OrthancStone
+{
+  class GlyphTextureAlphabet::GlyphSizeVisitor : public GlyphAlphabet::IGlyphVisitor
+  {
+  private:
+    unsigned int maxWidth_;
+    unsigned int maxHeight_;
+
+  public:
+    GlyphSizeVisitor() :
+      maxWidth_(0),
+      maxHeight_(0)
+    {
+    }
+      
+    virtual void Visit(uint32_t unicode,
+                       const Glyph& glyph)
+    {
+      maxWidth_ = std::max(maxWidth_, glyph.GetWidth());
+      maxHeight_ = std::max(maxHeight_, glyph.GetHeight());
+    }
+
+    unsigned int GetMaxWidth() const
+    {
+      return maxWidth_;
+    }
+
+    unsigned int GetMaxHeight() const
+    {
+      return maxHeight_;
+    }
+  };
+
+    
+  class GlyphTextureAlphabet::TextureGenerator : public GlyphAlphabet::IGlyphVisitor
+  {
+  private:
+    std::auto_ptr<Orthanc::ImageAccessor>  texture_;
+
+    unsigned int    countColumns_;
+    unsigned int    countRows_;
+    GlyphAlphabet&  targetAlphabet_;
+    unsigned int    glyphMaxWidth_;
+    unsigned int    glyphMaxHeight_;
+    unsigned int    column_;
+    unsigned int    row_;
+
+  public:
+    TextureGenerator(GlyphAlphabet& targetAlphabet,
+                     unsigned int countGlyphs,
+                     unsigned int glyphMaxWidth,
+                     unsigned int glyphMaxHeight) :
+      targetAlphabet_(targetAlphabet),
+      glyphMaxWidth_(glyphMaxWidth),
+      glyphMaxHeight_(glyphMaxHeight),
+      column_(0),
+      row_(0)
+    {
+      int c = boost::math::iround<int>(sqrt(static_cast<float>(countGlyphs)));
+
+      if (c <= 0)
+      {
+        countColumns_ = 1;
+      }
+      else
+      {
+        countColumns_ = static_cast<unsigned int>(c);
+      }
+
+      countRows_ = countGlyphs / countColumns_;
+      if (countGlyphs % countColumns_ != 0)
+      {
+        countRows_++;
+      }
+
+      texture_.reset(new Orthanc::Image(Orthanc::PixelFormat_RGBA32,
+                                        countColumns_ * glyphMaxWidth_,
+                                        countRows_ * glyphMaxHeight_,
+                                        true /* force minimal pitch */));
+
+      Orthanc::ImageProcessing::Set(*texture_, 0, 0, 0, 0);
+    }
+      
+      
+    virtual void Visit(uint32_t unicode,
+                       const Glyph& glyph)
+    {
+      if (!glyph.HasPayload())
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+      }
+
+      if (column_ >= countColumns_ ||
+          row_ >= countRows_)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      }
+
+      unsigned int x = column_ * glyphMaxWidth_;
+      unsigned int y = row_ * glyphMaxHeight_;
+
+      const Orthanc::ImageAccessor& source = dynamic_cast<const DynamicBitmap&>(glyph.GetPayload()).GetBitmap();
+
+      if (source.GetFormat() != Orthanc::PixelFormat_Grayscale8)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      }
+        
+      targetAlphabet_.Register(unicode, glyph, new TextureLocation(x, y));
+
+      Orthanc::ImageAccessor target;
+      texture_->GetRegion(target, x, y, source.GetWidth(), source.GetHeight());
+
+      //Orthanc::ImageProcessing::Copy(target, bitmap->GetBitmap());
+
+      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 < source.GetWidth(); x++)
+        {
+          // Premultiplied alpha
+          q[0] = 0;
+          q[1] = 0;
+          q[2] = 0;
+          q[3] = *p;
+            
+          p++;
+          q += 4;
+        }
+      }
+        
+      column_++;
+      if (column_ == countColumns_)
+      {
+        column_ = 0;
+        row_++;
+      }
+    }
+
+
+    Orthanc::ImageAccessor* ReleaseTexture()
+    {
+      return texture_.release();
+    }
+  };
+
+
+  class GlyphTextureAlphabet::RenderTextVisitor : public GlyphAlphabet::ITextVisitor
+  {
+  private:
+    Orthanc::ImageAccessor&        target_;
+    const Orthanc::ImageAccessor&  texture_;
+    int                            offsetX_;
+    int                            offsetY_;
+      
+  public:
+    RenderTextVisitor(Orthanc::ImageAccessor&  target,
+                      const GlyphTextureAlphabet&  that,
+                      int  offsetX,
+                      int  offsetY) :
+      target_(target),
+      texture_(that.GetTexture()),
+      offsetX_(offsetX),
+      offsetY_(offsetY)
+    {
+    }
+
+    virtual void Visit(uint32_t unicode,
+                       int x,
+                       int y,
+                       unsigned int width,
+                       unsigned int height,
+                       const Orthanc::IDynamicObject* payload)
+    {
+      int left = x + offsetX_;
+      int top = y + offsetY_;
+
+      assert(payload != NULL);
+      const TextureLocation& location = *dynamic_cast<const TextureLocation*>(payload);
+        
+      assert(left >= 0 &&
+             top >= 0 &&
+             static_cast<unsigned int>(left) + width <= target_.GetWidth() &&
+             static_cast<unsigned int>(top) + height <= target_.GetHeight());
+        
+      {
+        Orthanc::ImageAccessor to;
+        target_.GetRegion(to, left, top, width, height);
+
+        Orthanc::ImageAccessor from;
+        texture_.GetRegion(from, location.GetX(), location.GetY(), width, height);
+                                       
+        Orthanc::ImageProcessing::Copy(to, from);
+      }
+    }
+  };
+
+    
+  GlyphTextureAlphabet::GlyphTextureAlphabet(const GlyphBitmapAlphabet& sourceAlphabet) :
+    textureWidth_(0),
+    textureHeight_(0)
+  {
+    GlyphSizeVisitor size;
+    sourceAlphabet.GetAlphabet().Apply(size);
+
+    TextureGenerator generator(alphabet_,
+                               sourceAlphabet.GetAlphabet().GetSize(),
+                               size.GetMaxWidth(),
+                               size.GetMaxHeight());
+    sourceAlphabet.GetAlphabet().Apply(generator);
+
+    texture_.reset(generator.ReleaseTexture());
+    textureWidth_ = texture_->GetWidth();
+    textureHeight_ = texture_->GetHeight();
+  }
+
+    
+  const Orthanc::ImageAccessor& GlyphTextureAlphabet::GetTexture() const
+  {
+    if (texture_.get() == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+    else
+    {
+      return *texture_;
+    }
+  }
+    
+    
+  Orthanc::ImageAccessor* GlyphTextureAlphabet::ReleaseTexture()
+  {
+    if (texture_.get() == NULL)
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
+    }
+    else
+    {
+      return texture_.release();
+    }
+  }
+    
+
+  Orthanc::ImageAccessor* GlyphTextureAlphabet::RenderText(const std::string& utf8)
+  {
+    TextBoundingBox box(alphabet_, utf8);
+
+    std::auto_ptr<Orthanc::ImageAccessor> bitmap(
+      new Orthanc::Image(Orthanc::PixelFormat_RGBA32,
+                         box.GetWidth(), box.GetHeight(),
+                         true /* force minimal pitch */));
+
+    Orthanc::ImageProcessing::Set(*bitmap, 0, 0, 0, 0);
+
+    RenderTextVisitor visitor(*bitmap, *this, -box.GetLeft(), -box.GetTop());
+    alphabet_.Apply(visitor, utf8);
+
+    return bitmap.release();
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Fonts/GlyphTextureAlphabet.h	Fri Apr 19 15:42:57 2019 +0200
@@ -0,0 +1,92 @@
+/**
+ * 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 "GlyphBitmapAlphabet.h"
+
+#include <Core/Images/ImageAccessor.h>
+
+namespace OrthancStone
+{
+  class GlyphTextureAlphabet : public boost::noncopyable
+  {
+  public:
+    class TextureLocation : public Orthanc::IDynamicObject
+    {
+    private:
+      unsigned int  x_;
+      unsigned int  y_;
+
+    public:
+      TextureLocation(unsigned int x,
+                      unsigned int y) :
+        x_(x),
+        y_(y)
+      {
+      }
+
+      unsigned int GetX() const
+      {
+        return x_;
+      }
+
+      unsigned int GetY() const
+      {
+        return y_;
+      }
+    };
+
+  private:
+    class GlyphSizeVisitor;
+    class TextureGenerator;
+    class RenderTextVisitor;
+    
+    GlyphAlphabet                          alphabet_;
+    std::auto_ptr<Orthanc::ImageAccessor>  texture_;
+    unsigned int                           textureWidth_;
+    unsigned int                           textureHeight_;
+    
+  public:
+    GlyphTextureAlphabet(const GlyphBitmapAlphabet& sourceAlphabet);
+    
+    const Orthanc::ImageAccessor& GetTexture() const;
+    
+    Orthanc::ImageAccessor* ReleaseTexture();
+
+    Orthanc::ImageAccessor* RenderText(const std::string& utf8);
+
+    const GlyphAlphabet& GetAlphabet() const
+    {
+      return alphabet_;
+    }
+
+    unsigned int GetTextureWidth() const
+    {
+      return textureWidth_;
+    }
+
+    unsigned int GetTextureHeight() const
+    {
+      return textureHeight_;
+    }
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Fonts/OpenGLTextCoordinates.cpp	Fri Apr 19 15:42:57 2019 +0200
@@ -0,0 +1,117 @@
+/**
+ * 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 "OpenGLTextCoordinates.h"
+
+#include <Core/OrthancException.h>
+
+namespace OrthancStone
+{
+  namespace OpenGL
+  {
+    void OpenGLTextCoordinates::Visit(uint32_t unicode,
+                                      int x,
+                                      int y,
+                                      unsigned int width,
+                                      unsigned int height,
+                                      const Orthanc::IDynamicObject* payload)
+    {
+      // Rendering coordinates
+      float rx1 = x - box_.GetLeft();
+      float ry1 = y - box_.GetTop();
+      float rx2 = rx1 + static_cast<float>(width);
+      float ry2 = ry1 + static_cast<float>(height);
+
+      // Texture coordinates
+      assert(payload != NULL);
+      const GlyphTextureAlphabet::TextureLocation& location =
+        *dynamic_cast<const GlyphTextureAlphabet::TextureLocation*>(payload);
+
+      float tx1 = location.GetX() / textureWidth_;
+      float ty1 = location.GetY() / textureHeight_;
+      float tx2 = tx1 + (static_cast<float>(width) / textureWidth_);
+      float ty2 = ty1 + (static_cast<float>(height) / textureHeight_);
+
+      const float rpos[6][2] = {
+        { rx1, ry1 },
+        { rx1, ry2 },
+        { rx2, ry1 },
+        { rx2, ry1 },
+        { rx1, ry2 },
+        { rx2, ry2 }
+      };
+
+      const float tpos[6][2] = {
+        { tx1, ty1 },
+        { tx1, ty2 },
+        { tx2, ty1 },
+        { tx2, ty1 },
+        { tx1, ty2 },
+        { tx2, ty2 }
+      };
+
+      for (unsigned int i = 0; i < 6; i++)
+      {
+        renderingCoords_.push_back(rpos[i][0]);
+        renderingCoords_.push_back(rpos[i][1]);
+        textureCoords_.push_back(tpos[i][0]);
+        textureCoords_.push_back(tpos[i][1]);
+      }
+    }
+
+
+    OpenGLTextCoordinates::OpenGLTextCoordinates(const GlyphTextureAlphabet& alphabet,
+                                                 const std::string& utf8) :
+      box_(alphabet.GetAlphabet(), utf8),
+      textureWidth_(alphabet.GetTextureWidth()),
+      textureHeight_(alphabet.GetTextureHeight())
+    {
+      if (textureWidth_ <= 0 ||
+          textureHeight_ <= 0)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
+      }
+      
+      width_ = static_cast<float>(box_.GetWidth());
+      height_ = static_cast<float>(box_.GetHeight());
+
+      // Each character is made of two 2D triangles (= 2 * 3 * 2 = 12)
+      renderingCoords_.reserve(box_.GetCharactersCount() * 12);
+      textureCoords_.reserve(box_.GetCharactersCount() * 12);
+
+      alphabet.GetAlphabet().Apply(*this, utf8);
+    }
+
+
+    const std::vector<float>& OpenGLTextCoordinates::GetRenderingCoords() const
+    {
+      assert(renderingCoords_.size() == textureCoords_.size());
+      return renderingCoords_;
+    }
+
+    
+    const std::vector<float>& OpenGLTextCoordinates::GetTextureCoords() const
+    {
+      assert(renderingCoords_.size() == textureCoords_.size());
+      return textureCoords_;
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Fonts/OpenGLTextCoordinates.h	Fri Apr 19 15:42:57 2019 +0200
@@ -0,0 +1,74 @@
+/**
+ * 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 "GlyphTextureAlphabet.h"
+#include "TextBoundingBox.h"
+
+namespace OrthancStone
+{
+  namespace OpenGL
+  {
+    class OpenGLTextCoordinates : protected GlyphAlphabet::ITextVisitor
+    {
+    private:
+      TextBoundingBox     box_;
+      float               width_;
+      float               height_;
+      std::vector<float>  renderingCoords_;
+      std::vector<float>  textureCoords_;
+      float               textureWidth_;
+      float               textureHeight_;
+    
+    protected:
+      virtual void Visit(uint32_t unicode,
+                         int x,
+                         int y,
+                         unsigned int width,
+                         unsigned int height,
+                         const Orthanc::IDynamicObject* payload);
+
+    public:
+      OpenGLTextCoordinates(const GlyphTextureAlphabet& alphabet,
+                            const std::string& utf8);
+
+      unsigned int GetTextWidth() const
+      {
+        return box_.GetWidth();
+      }
+
+      unsigned int GetTextHeight() const
+      {
+        return box_.GetHeight();
+      }
+
+      bool IsEmpty() const
+      {
+        return renderingCoords_.empty();
+      }
+
+      const std::vector<float>& GetRenderingCoords() const;
+
+      const std::vector<float>& GetTextureCoords() const;
+    };
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Fonts/TextBoundingBox.cpp	Fri Apr 19 15:42:57 2019 +0200
@@ -0,0 +1,80 @@
+/**
+ * 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 "TextBoundingBox.h"
+
+namespace OrthancStone
+{
+  void TextBoundingBox::AddPoint(int x,
+                                 int y)
+  {
+    left_ = std::min(left_, x);
+    right_ = std::max(right_, x);
+    top_ = std::min(top_, y);
+    bottom_ = std::max(bottom_, y);
+  }
+
+
+  void TextBoundingBox::Clear()
+  {
+    left_ = 0;
+    top_ = 0;
+    right_ = 0;
+    bottom_ = 0;
+    countCharacters_ = 0;
+  }
+
+
+  void TextBoundingBox::Visit(uint32_t unicode,
+                              int x,
+                              int y,
+                              unsigned int width,
+                              unsigned int height,
+                              const Orthanc::IDynamicObject* payload /* ignored */)
+  {
+    AddPoint(x, y);
+    AddPoint(x + static_cast<int>(width),
+             y + static_cast<int>(height));
+    countCharacters_++;
+  }
+
+
+  TextBoundingBox::TextBoundingBox(const GlyphAlphabet& alphabet,
+                                   const std::string& utf8)
+  {
+    Clear();
+    alphabet.Apply(*this, utf8);
+  }
+
+
+  unsigned int TextBoundingBox::GetWidth() const
+  {
+    assert(left_ <= right_);
+    return static_cast<unsigned int>(right_ - left_ + 1);
+  }
+
+  
+  unsigned int TextBoundingBox::GetHeight() const
+  {
+    assert(top_ <= bottom_);
+    return static_cast<unsigned int>(bottom_ - top_ + 1);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Framework/Fonts/TextBoundingBox.h	Fri Apr 19 15:42:57 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/>.
+ **/
+
+
+#pragma once
+
+#include "GlyphAlphabet.h"
+
+namespace OrthancStone
+{
+  class TextBoundingBox : protected GlyphAlphabet::ITextVisitor
+  {
+  private:
+    int          left_;
+    int          top_;
+    int          right_;
+    int          bottom_;
+    unsigned int countCharacters_;
+
+    void AddPoint(int x,
+                  int y);
+
+    void Clear();
+
+  protected:
+    virtual void Visit(uint32_t unicode,
+                       int x,
+                       int y,
+                       unsigned int width,
+                       unsigned int height,
+                       const Orthanc::IDynamicObject* payload /* ignored */);
+
+  public:
+    TextBoundingBox(const GlyphAlphabet& alphabet,
+                    const std::string& utf8);
+
+    int GetLeft() const
+    {
+      return left_;
+    }
+
+    int GetTop() const
+    {
+      return top_;
+    }
+
+    unsigned int GetWidth() const;
+
+    unsigned int GetHeight() const;
+
+    unsigned int GetCharactersCount() const
+    {
+      return countCharacters_;
+    }
+  };
+}
--- a/Resources/CMake/OrthancStoneConfiguration.cmake	Fri Apr 19 15:11:16 2019 +0200
+++ b/Resources/CMake/OrthancStoneConfiguration.cmake	Fri Apr 19 15:42:57 2019 +0200
@@ -247,6 +247,10 @@
   ${ORTHANC_STONE_ROOT}/Framework/Fonts/FontRenderer.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Fonts/Glyph.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Fonts/GlyphAlphabet.cpp
+  ${ORTHANC_STONE_ROOT}/Framework/Fonts/GlyphBitmapAlphabet.cpp
+  ${ORTHANC_STONE_ROOT}/Framework/Fonts/GlyphTextureAlphabet.cpp
+  ${ORTHANC_STONE_ROOT}/Framework/Fonts/OpenGLTextCoordinates.cpp
+  ${ORTHANC_STONE_ROOT}/Framework/Fonts/TextBoundingBox.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Layers/CircleMeasureTracker.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Layers/ColorFrameRenderer.cpp
   ${ORTHANC_STONE_ROOT}/Framework/Layers/DicomSeriesVolumeSlicer.cpp