diff Core/Images/Font.cpp @ 1612:96582230ddcb

Core/ImageFormats folder renamed as Core/Images
author Sebastien Jodogne <s.jodogne@gmail.com>
date Fri, 04 Sep 2015 16:36:18 +0200
parents Core/ImageFormats/Font.cpp@5e9b2aac8b89
children 644c32c07306
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Core/Images/Font.cpp	Fri Sep 04 16:36:18 2015 +0200
@@ -0,0 +1,298 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "../PrecompiledHeaders.h"
+#include "Font.h"
+
+#include "../Toolbox.h"
+#include "../OrthancException.h"
+
+#include <stdio.h>
+#include <memory>
+#include <boost/lexical_cast.hpp>
+
+namespace Orthanc
+{
+  Font::~Font()
+  {
+    for (Characters::iterator it = characters_.begin();
+         it != characters_.end(); it++)
+    {
+      delete it->second;
+    }
+  }
+
+
+  void Font::LoadFromMemory(const std::string& font)
+  {
+    Json::Value v;
+    Json::Reader reader;
+    if (!reader.parse(font, v) ||
+        v.type() != Json::objectValue ||
+        !v.isMember("Name") ||
+        !v.isMember("Size") ||
+        !v.isMember("Characters") ||
+        v["Name"].type() != Json::stringValue ||
+        v["Size"].type() != Json::intValue ||
+        v["Characters"].type() != Json::objectValue)
+    {
+      throw OrthancException(ErrorCode_BadFont);
+    }
+
+    name_ = v["Name"].asString();
+    size_ = v["Size"].asUInt();
+    maxHeight_ = 0;
+
+    Json::Value::Members characters = v["Characters"].getMemberNames();
+
+    for (size_t i = 0; i < characters.size(); i++)
+    {
+      const Json::Value& info = v["Characters"][characters[i]];
+      if (info.type() != Json::objectValue ||
+          !info.isMember("Advance") ||
+          !info.isMember("Bitmap") ||
+          !info.isMember("Height") ||
+          !info.isMember("Top") ||
+          !info.isMember("Width") ||
+          info["Advance"].type() != Json::intValue ||
+          info["Bitmap"].type() != Json::arrayValue ||
+          info["Height"].type() != Json::intValue ||
+          info["Top"].type() != Json::intValue ||
+          info["Width"].type() != Json::intValue)
+      {
+        throw OrthancException(ErrorCode_BadFont);
+      }
+
+      std::auto_ptr<Character> c(new Character);
+      
+      c->advance_ = info["Advance"].asUInt();
+      c->height_ = info["Height"].asUInt();
+      c->top_ = info["Top"].asUInt();
+      c->width_ = info["Width"].asUInt();
+      c->bitmap_.resize(info["Bitmap"].size());
+
+      if (c->height_ > maxHeight_)
+      {
+        maxHeight_ = c->height_;
+      }
+      
+      for (Json::Value::ArrayIndex j = 0; j < info["Bitmap"].size(); j++)
+      {
+        if (info["Bitmap"][j].type() != Json::intValue)
+        {
+          throw OrthancException(ErrorCode_BadFont);
+        }
+
+        int value = info["Bitmap"][j].asInt();
+        if (value < 0 || value > 255)
+        {
+          throw OrthancException(ErrorCode_BadFont);
+        }
+
+        c->bitmap_[j] = value;
+      }
+
+      int index = boost::lexical_cast<int>(characters[i]);
+      if (index < 0 || index > 255)
+      {
+        throw OrthancException(ErrorCode_BadFont);
+      }
+
+      characters_[static_cast<char>(index)] = c.release();
+    }
+  }
+
+
+  void Font::LoadFromFile(const std::string& path)
+  {
+    std::string font;
+    Toolbox::ReadFile(font, path);
+    LoadFromMemory(font);
+  }
+
+
+  static unsigned int MyMin(unsigned int a, 
+                            unsigned int b)
+  {
+    return a < b ? a : b;
+  }
+
+
+  void Font::DrawCharacter(ImageAccessor& target,
+                           const Character& character,
+                           int x,
+                           int y,
+                           const uint8_t color[4]) const
+  {
+    // Compute the bounds of the character
+    if (x >= static_cast<int>(target.GetWidth()) ||
+        y >= static_cast<int>(target.GetHeight()))
+    {
+      // The character is out of the image
+      return;
+    }
+
+    unsigned int left = x < 0 ? -x : 0;
+    unsigned int top = y < 0 ? -y : 0;
+    unsigned int width = MyMin(character.width_, target.GetWidth() - x);
+    unsigned int height = MyMin(character.height_, target.GetHeight() - y);
+
+    uint8_t bpp = target.GetBytesPerPixel();
+
+    // Blit the font bitmap OVER the target image
+    // https://en.wikipedia.org/wiki/Alpha_compositing
+
+    for (unsigned int cy = top; cy < height; cy++)
+    {
+      uint8_t* p = reinterpret_cast<uint8_t*>(target.GetRow(y + cy)) + (x + left) * bpp;
+      unsigned int pos = cy * character.width_ + left;
+
+      switch (target.GetFormat())
+      {
+        case PixelFormat_Grayscale8:
+        {
+          assert(bpp == 1);
+          for (unsigned int cx = left; cx < width; cx++, pos++, p++)
+          {
+            uint16_t alpha = character.bitmap_[pos];
+            *p = (alpha * static_cast<uint16_t>(color[0]) + (255 - alpha) * static_cast<uint16_t>(*p)) >> 8;
+          }
+
+          break;
+        }
+
+        case PixelFormat_RGB24:
+        {
+          assert(bpp == 3);
+          for (unsigned int cx = left; cx < width; cx++, pos++, p += 3)
+          {
+            uint16_t alpha = character.bitmap_[pos];
+            p[0] = (alpha * static_cast<uint16_t>(color[0]) + (255 - alpha) * static_cast<uint16_t>(p[0])) >> 8;
+            p[1] = (alpha * static_cast<uint16_t>(color[1]) + (255 - alpha) * static_cast<uint16_t>(p[1])) >> 8;
+            p[2] = (alpha * static_cast<uint16_t>(color[2]) + (255 - alpha) * static_cast<uint16_t>(p[2])) >> 8;
+          }
+
+          break;
+        }
+
+        case PixelFormat_RGBA32:
+        {
+          assert(bpp == 4);
+
+          for (unsigned int cx = left; cx < width; cx++, pos++, p += 4)
+          {
+            float alpha = static_cast<float>(character.bitmap_[pos]) / 255.0f;
+            float beta = (1.0f - alpha) * static_cast<float>(p[3]) / 255.0f;
+            float denom = 1.0f / (alpha + beta);
+
+            for (uint8_t i = 0; i < 3; i++)
+            {
+              p[i] = static_cast<uint8_t>((alpha * static_cast<float>(color[i]) +
+                                           beta * static_cast<float>(p[i])) * denom);
+            }
+
+            p[3] = static_cast<uint8_t>(255.0f * (alpha + beta));
+          }
+
+          break;
+        }
+
+        default:
+          throw OrthancException(ErrorCode_NotImplemented);
+      }
+    }
+
+  }
+
+
+  void Font::DrawInternal(ImageAccessor& target,
+                          const std::string& utf8,
+                          int x,
+                          int y,
+                          const uint8_t color[4]) const
+  {
+    if (target.GetFormat() != PixelFormat_Grayscale8 &&
+        target.GetFormat() != PixelFormat_RGB24 &&
+        target.GetFormat() != PixelFormat_RGBA32)
+    {
+      throw OrthancException(ErrorCode_NotImplemented);
+    }
+
+    int a = x;
+
+    std::string s = Toolbox::ConvertFromUtf8(utf8, Encoding_Latin1);
+
+    for (size_t i = 0; i < s.size(); i++)
+    {
+      if (s[i] == '\n')
+      {
+        // Go to the next line
+        a = x;
+        y += maxHeight_ + 1;
+      }
+      else
+      {
+        Characters::const_iterator c = characters_.find(s[i]);
+        if (c != characters_.end())
+        {
+          DrawCharacter(target, *c->second, a, y + static_cast<int>(c->second->top_), color);
+          a += c->second->advance_;
+        }
+      }
+    }
+  }
+
+
+  void Font::Draw(ImageAccessor& target,
+                  const std::string& utf8,
+                  int x,
+                  int y,
+                  uint8_t grayscale) const
+  {
+    uint8_t color[4] = { grayscale, grayscale, grayscale, 255 };
+    DrawInternal(target, utf8, x, y, color);
+  }
+
+
+  void Font::Draw(ImageAccessor& target,
+                  const std::string& utf8,
+                  int x,
+                  int y,
+                  uint8_t r,
+                  uint8_t g,
+                  uint8_t b) const
+  {
+    uint8_t color[4] = { r, g, b, 255 };
+    DrawInternal(target, utf8, x, y, color);
+  }
+
+}