freetype-commit
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[freetype2-demos] master 631161f 11/41: [ftinspect] Refactor `Engine` an


From: Werner Lemberg
Subject: [freetype2-demos] master 631161f 11/41: [ftinspect] Refactor `Engine` and fix the singular tab.
Date: Mon, 3 Oct 2022 11:27:01 -0400 (EDT)

branch: master
commit 631161f18904c6e4f3a37e8808bb366609ba98b9
Author: Charlie Jiang <w@chariri.moe>
Commit: Werner Lemberg <wl@gnu.org>

    [ftinspect] Refactor `Engine` and fix the singular tab.
    
    This comment brings the new glyph loading and bitmap rendering code.
    The bitmap rendering part is in `RenderingEngine` which can be obtained
    from the `Engine`. The `Engine` now supports loading the glyph by loading
    with image cache and without image cache.
    The `loadOutline` function is removed.
    
    Accompanying this change, some new features are introduced as well. Most
    notable the improvements in rendering: back/foreground color and gamma,
    support bitmap glyphs (not enabled in the UI). Support for LCD and color
    layer font is absent yet.
    
    The singular tab is now enabled by uncommented functional code that was
    commented in the last commit.
    
    In `Engine`, a `ftFallbackFace_` is introduced for all non-rendering work,
    and the old `ftSize_` is only used for rendering. This helps dealing with
    non-scalable fonts since we can safely retrieve info with the fallback face.
    
    * src/ftinspect/engine/engine.cpp, src/ftinspect/engine/engine.hpp:
      Integrate the `RenderingEngine`.
      Change `loadFont` to update the `ftFallbackFace_` as well.
      Change all info-retrieving functions to use `ftFallbackFace_`.
      Add `loadGlyph`, `loadGlyphIntoSlotWithoutCache` and
      `loadGlyphWithoutUpdate`, and remove `loadOutline`.
      Extract code about "get a face object from the cache and do sth."
      into a new function `withFace`.
      Add property `antiAliasingEnabled` and `renderMode` for the rendering
      engine.
      Some minor changes are included.
    
    * src/ftinspect/engine/rendering.cpp, src/ftinspect/engine/rendering.hpp:
      New files, as described.
    
    * src/ftinspect/panels/settingpanel.cpp,
      src/ftinspect/glyphcomponents/glyphbitmap.cpp,
      src/ftinspect/panels/singular.cpp: Uncomment functional code.
    
    * src/ftinspect/CMakeLists.txt, src/ftinspect/meson.build: Updated.
---
 src/ftinspect/CMakeLists.txt                  |   1 +
 src/ftinspect/engine/engine.cpp               | 306 ++++++++++++++-----------
 src/ftinspect/engine/engine.hpp               |  50 ++++-
 src/ftinspect/engine/rendering.cpp            | 311 ++++++++++++++++++++++++++
 src/ftinspect/engine/rendering.hpp            |  60 +++++
 src/ftinspect/glyphcomponents/glyphbitmap.cpp |  11 +-
 src/ftinspect/meson.build                     |   1 +
 src/ftinspect/panels/settingpanel.cpp         |  16 +-
 src/ftinspect/panels/singular.cpp             |   9 +-
 9 files changed, 614 insertions(+), 151 deletions(-)

diff --git a/src/ftinspect/CMakeLists.txt b/src/ftinspect/CMakeLists.txt
index db3f93a..40e2903 100644
--- a/src/ftinspect/CMakeLists.txt
+++ b/src/ftinspect/CMakeLists.txt
@@ -22,6 +22,7 @@ add_executable(ftinspect
   
   "engine/engine.cpp"
   "engine/fontfilemanager.cpp"
+  "engine/rendering.cpp"
 
   "glyphcomponents/glyphbitmap.cpp"
   "glyphcomponents/glyphoutline.cpp"
diff --git a/src/ftinspect/engine/engine.cpp b/src/ftinspect/engine/engine.cpp
index b0801f8..1b4deaf 100644
--- a/src/ftinspect/engine/engine.cpp
+++ b/src/ftinspect/engine/engine.cpp
@@ -28,12 +28,12 @@ FaceID::FaceID()
 }
 
 
-FaceID::FaceID(int fontIdx,
-               long faceIdx,
-               int namedInstanceIdx)
-: fontIndex(fontIdx),
-  faceIndex(faceIdx),
-  namedInstanceIndex(namedInstanceIdx)
+FaceID::FaceID(int fontIndex,
+               long faceIndex,
+               int namedInstanceIndex)
+: fontIndex(fontIndex),
+  faceIndex(faceIndex),
+  namedInstanceIndex(namedInstanceIndex)
 {
   // empty
 }
@@ -102,6 +102,7 @@ faceRequester(FTC_FaceID ftcFaceID,
   if (faceID.namedInstanceIndex > 0)
     faceIndex += faceID.namedInstanceIndex << 16;
 
+  *faceP = NULL;
   return FT_New_Face(library,
                      qPrintable(font),
                      faceIndex,
@@ -119,6 +120,7 @@ Engine::Engine()
 : fontFileManager_(this)
 {
   ftSize_ = NULL;
+  ftFallbackFace_ = NULL;
   // we reserve value 0 for the `invalid face ID'
   faceCounter_ = 1;
 
@@ -150,6 +152,8 @@ Engine::Engine()
   }
 
   queryEngine();
+  renderingEngine_
+    = std::unique_ptr<RenderingEngine>(new RenderingEngine(this));
 }
 
 
@@ -160,38 +164,51 @@ Engine::~Engine()
 }
 
 
-long
-Engine::numberOfFaces(int fontIndex)
+template <class Func>
+void
+Engine::withFace(FaceID id, Func func)
 {
   FT_Face face;
-  long numFaces = -1;
-
-  // search triplet (fontIndex, 0, 0)
-  FTC_FaceID ftcFaceID = reinterpret_cast<FTC_FaceID>
-                           (faceIDMap_.value(FaceID(fontIndex,
-                                                   0,
-                                                   0)));
-  if (ftcFaceID)
+  // search triplet (fontIndex, faceIndex, namedInstanceIndex)
+  auto numId = reinterpret_cast<FTC_FaceID>(faceIDMap_.value(id));
+  if (numId)
   {
     // found
-    if (!FTC_Manager_LookupFace(cacheManager_, ftcFaceID, &face))
-      numFaces = face->num_faces;
+    if (!FTC_Manager_LookupFace(cacheManager_, numId, &face))
+      func(face);
   }
-  else
+  else if (id.fontIndex >= 0)
   {
-    // not found; try to load triplet (fontIndex, 0, 0)
-    ftcFaceID = reinterpret_cast<FTC_FaceID>(faceCounter_);
-    faceIDMap_.insert(FaceID(fontIndex, 0, 0),
-                     faceCounter_++);
+    if (faceCounter_ >= INT_MAX) // prevent overflowing
+      return;
+
+    // not found; try to load triplet
+    // (fontIndex, faceIndex, namedInstanceIndex)
+    numId = reinterpret_cast<FTC_FaceID>(faceCounter_);
+    faceIDMap_.insert(id, faceCounter_++);
 
-    if (!FTC_Manager_LookupFace(cacheManager_, ftcFaceID, &face))
-      numFaces = face->num_faces;
+    if (!FTC_Manager_LookupFace(cacheManager_, numId, &face))
+      func(face);
     else
     {
-      faceIDMap_.remove(FaceID(fontIndex, 0, 0));
+      faceIDMap_.remove(id);
       faceCounter_--;
     }
   }
+}
+
+
+long
+Engine::numberOfFaces(int fontIndex)
+{
+  long numFaces = -1;
+
+  if (fontIndex < 0)
+    return -1;
+
+  // search triplet (fontIndex, 0, 0)
+  withFace(FaceID(fontIndex, 0, 0),
+           [&](FT_Face face) { numFaces = face->num_faces; });
 
   return numFaces;
 }
@@ -201,37 +218,18 @@ int
 Engine::numberOfNamedInstances(int fontIndex,
                                long faceIndex)
 {
-  FT_Face face;
   // we return `n' named instances plus one;
   // instance index 0 represents a face without a named instance selected
   int numNamedInstances = -1;
+  if (fontIndex < 0)
+    return -1;
 
-  // search triplet (fontIndex, faceIndex, 0)
-  FTC_FaceID ftcFaceID = reinterpret_cast<FTC_FaceID>
-                           (faceIDMap_.value(FaceID(fontIndex,
-                                                   faceIndex,
-                                                   0)));
-  if (ftcFaceID)
-  {
-    // found
-    if (!FTC_Manager_LookupFace(cacheManager_, ftcFaceID, &face))
-      numNamedInstances = static_cast<int>((face->style_flags >> 16) + 1);
-  }
-  else
-  {
-    // not found; try to load triplet (fontIndex, faceIndex, 0)
-    ftcFaceID = reinterpret_cast<FTC_FaceID>(faceCounter_);
-    faceIDMap_.insert(FaceID(fontIndex, faceIndex, 0),
-                     faceCounter_++);
-
-    if (!FTC_Manager_LookupFace(cacheManager_, ftcFaceID, &face))
-      numNamedInstances = static_cast<int>((face->style_flags >> 16) + 1);
-    else
-    {
-      faceIDMap_.remove(FaceID(fontIndex, faceIndex, 0));
-      faceCounter_--;
-    }
-  }
+  withFace(FaceID(fontIndex, faceIndex, 0), 
+           [&](FT_Face face)
+           {
+             numNamedInstances
+               = static_cast<int>((face->style_flags >> 16) + 1);
+           });
 
   return numNamedInstances;
 }
@@ -243,40 +241,13 @@ Engine::namedInstanceName(int fontIndex, long faceIndex, 
int index)
   if (fontIndex < 0)
     return {};
 
-  FT_Face face;
   QString name;
-
-  // search triplet (fontIndex, faceIndex, index)
-  FTC_FaceID ftcFaceID = reinterpret_cast<FTC_FaceID>
-                           (faceIDMap_.value(FaceID(fontIndex,
-                                                    faceIndex,
-                                                    index)));
-  if (ftcFaceID)
-  {
-    // found
-    if (!FTC_Manager_LookupFace(cacheManager_, ftcFaceID, &face))
-      name = QString("%1 %2")
-               .arg(face->family_name)
-               .arg(face->style_name);
-  }
-  else
-  {
-    // not found; try to load triplet (fontIndex, faceIndex, index)
-    ftcFaceID = reinterpret_cast<FTC_FaceID>(faceCounter_);
-    faceIDMap_.insert(FaceID(fontIndex, faceIndex, index),
-                      faceCounter_++);
-
-    if (!FTC_Manager_LookupFace(cacheManager_, ftcFaceID, &face))
-      name = QString("%1 %2")
-               .arg(face->family_name)
-               .arg(face->style_name);
-    else
-    {
-      faceIDMap_.remove(FaceID(fontIndex, faceIndex, 0));
-      faceCounter_--;
-    }
-  }
-
+  withFace(FaceID(fontIndex, faceIndex, index),
+           [&](FT_Face face) {
+             name = QString("%1 %2")
+                      .arg(face->family_name)
+                      .arg(face->style_name);
+           });
   return name;
 }
 
@@ -291,56 +262,76 @@ Engine::loadFont(int fontIndex,
 
   update();
 
+  curFontIndex_ = fontIndex;
+  auto id = FaceID(fontIndex, faceIndex, namedInstanceIndex);
+
   // search triplet (fontIndex, faceIndex, namedInstanceIndex)
-  scaler_.face_id = reinterpret_cast<FTC_FaceID>
-                     (faceIDMap_.value(FaceID(fontIndex,
-                                             faceIndex,
-                                             namedInstanceIndex)));
+  scaler_.face_id = reinterpret_cast<FTC_FaceID>(faceIDMap_.value(id));
   if (scaler_.face_id)
   {
     // found
-    if (!FTC_Manager_LookupSize(cacheManager_, &scaler_, &ftSize_))
-      numGlyphs = ftSize_->face->num_glyphs;
+    if (!FTC_Manager_LookupFace(cacheManager_, scaler_.face_id,
+                               &ftFallbackFace_))
+    {
+      numGlyphs = ftFallbackFace_->num_glyphs;
+      if (FTC_Manager_LookupSize(cacheManager_, &scaler_, &ftSize_))
+        ftSize_ = NULL; // Good font, bad size.
+    }
+    else
+    {
+      ftFallbackFace_ = NULL;
+      ftSize_ = NULL;
+    }
   }
-  else
+  else if (fontIndex >= 0)
   {
+    if (faceCounter_ >= INT_MAX) // prevent overflowing
+      return -1;
+
     // not found; try to load triplet
     // (fontIndex, faceIndex, namedInstanceIndex)
     scaler_.face_id = reinterpret_cast<FTC_FaceID>(faceCounter_);
-    faceIDMap_.insert(FaceID(fontIndex,
-                            faceIndex,
-                            namedInstanceIndex),
-                     faceCounter_++);
+    faceIDMap_.insert(id, faceCounter_++);
 
-    if (!FTC_Manager_LookupSize(cacheManager_, &scaler_, &ftSize_))
-      numGlyphs = ftSize_->face->num_glyphs;
+    if (!FTC_Manager_LookupFace(cacheManager_, scaler_.face_id,
+                                &ftFallbackFace_))
+    {
+      numGlyphs = ftFallbackFace_->num_glyphs;
+      if (FTC_Manager_LookupSize(cacheManager_, &scaler_, &ftSize_))
+        ftSize_ = NULL; // Good font, bad size.
+    }
     else
     {
-      faceIDMap_.remove(FaceID(fontIndex,
-                              faceIndex,
-                              namedInstanceIndex));
+      faceIDMap_.remove(id);
       faceCounter_--;
+      ftFallbackFace_ = NULL;
+      ftSize_ = NULL;
     }
   }
 
+  imageType_.face_id = scaler_.face_id;
+
   if (numGlyphs < 0)
   {
+    ftFallbackFace_ = NULL;
     ftSize_ = NULL;
     curFamilyName_ = QString();
     curStyleName_ = QString();
   }
   else
   {
-    curFamilyName_ = QString(ftSize_->face->family_name);
-    curStyleName_ = QString(ftSize_->face->style_name);
+    curFamilyName_ = QString(ftFallbackFace_->family_name);
+    curStyleName_ = QString(ftFallbackFace_->style_name);
 
-    const char* moduleName = FT_FACE_DRIVER_NAME( ftSize_->face );
+    const char* moduleName = FT_FACE_DRIVER_NAME(ftFallbackFace_);
 
     // XXX cover all available modules
     if (!strcmp(moduleName, "cff"))
       fontType_ = FontType_CFF;
     else if (!strcmp(moduleName, "truetype"))
       fontType_ = FontType_TrueType;
+    else
+      fontType_ = FontType_Other;
   }
 
   curNumGlyphs_ = numGlyphs;
@@ -348,6 +339,25 @@ Engine::loadFont(int fontIndex,
 }
 
 
+void
+Engine::reloadFont()
+{
+  update();
+  if (!scaler_.face_id)
+    return;
+  imageType_.face_id = scaler_.face_id;
+  
+  if (FTC_Manager_LookupFace(cacheManager_, scaler_.face_id, &ftFallbackFace_))
+  {
+    ftFallbackFace_ = NULL;
+    ftSize_ = NULL;
+    return;
+  }
+  if (FTC_Manager_LookupSize(cacheManager_, &scaler_, &ftSize_))
+    ftSize_ = NULL; // Good font, bad size.
+}
+
+
 void
 Engine::removeFont(int fontIndex, bool closeFile)
 {
@@ -356,7 +366,7 @@ Engine::removeFont(int fontIndex, bool closeFile)
   QMap<FaceID, FTC_IDType>::iterator iter
     = faceIDMap_.lowerBound(FaceID(fontIndex, 0, 0));
 
-  for (;;)
+  while (true)
   {
     if (iter == faceIDMap_.end())
       break;
@@ -365,7 +375,7 @@ Engine::removeFont(int fontIndex, bool closeFile)
     if (faceID.fontIndex != fontIndex)
       break;
 
-    FTC_FaceID ftcFaceID = reinterpret_cast<FTC_FaceID>(iter.value());
+    auto ftcFaceID = reinterpret_cast<FTC_FaceID>(iter.value());
     FTC_Manager_RemoveFaceID(cacheManager_, ftcFaceID);
 
     iter = faceIDMap_.erase(iter);
@@ -384,10 +394,11 @@ Engine::glyphName(int index)
   if (index < 0)
     throw std::runtime_error("Invalid glyph index");
 
-  if (ftSize_ && FT_HAS_GLYPH_NAMES(ftSize_->face))
+  reloadFont();
+  if (ftFallbackFace_ && FT_HAS_GLYPH_NAMES(ftFallbackFace_))
   {
     char buffer[256];
-    if (!FT_Get_Glyph_Name(ftSize_->face,
+    if (!FT_Get_Glyph_Name(ftFallbackFace_,
                            static_cast<unsigned int>(index),
                            buffer,
                            sizeof(buffer)))
@@ -398,23 +409,31 @@ Engine::glyphName(int index)
 }
 
 
-FT_Outline*
-Engine::loadOutline(int glyphIndex)
+int
+Engine::numberOfOpenedFonts()
+{
+  return fontFileManager_.size();
+}
+
+
+FT_Glyph
+Engine::loadGlyph(int glyphIndex)
 {
   update();
 
   if (glyphIndex < 0)
     throw std::runtime_error("Invalid glyph index");
 
-  FT_Glyph glyph;
+  if (curNumGlyphs_ <= 0)
+    return NULL;
 
-  // XXX handle bitmap fonts
+  FT_Glyph glyph;
 
   // the `scaler' object is set up by the
   // `update' and `loadFont' methods
   if (FTC_ImageCache_LookupScaler(imageCache_,
                                   &scaler_,
-                                  loadFlags_ | FT_LOAD_NO_BITMAP,
+                                  loadFlags_,
                                   static_cast<unsigned int>(glyphIndex),
                                   &glyph,
                                   NULL))
@@ -423,32 +442,64 @@ Engine::loadOutline(int glyphIndex)
     return NULL;
   }
 
-  if (glyph->format != FT_GLYPH_FORMAT_OUTLINE)
-    return NULL;
+  return glyph;
+}
 
-  FT_OutlineGlyph outlineGlyph = reinterpret_cast<FT_OutlineGlyph>(glyph);
 
-  return &outlineGlyph->outline;
+int
+Engine::loadGlyphIntoSlotWithoutCache(int glyphIndex,
+                                      bool noScale)
+{
+  auto flags = static_cast<int>(loadFlags_);
+  if (noScale)
+    flags |= FT_LOAD_NO_SCALE;
+  return FT_Load_Glyph(ftSize_->face, glyphIndex, flags);
 }
 
 
-int
-Engine::numberOfOpenedFonts()
+// When continuous rendering, we don't need to call `update`
+// This is currently unused since the cache API don't support obtaining glyph
+// metrics. See `StringRenderer::loadSingleContext`
+FT_Glyph
+Engine::loadGlyphWithoutUpdate(int glyphIndex, 
+                               FTC_Node* outNode,
+                               bool forceRender)
 {
-  return fontFileManager_.size();
+  FT_Glyph glyph;
+  auto oldFlags = imageType_.flags;
+  if (forceRender)
+    imageType_.flags |= FT_LOAD_RENDER;
+  if (FTC_ImageCache_Lookup(imageCache_,
+                            &imageType_,
+                            glyphIndex,
+                            &glyph,
+                            outNode))
+  {
+    // XXX error handling?
+    return NULL;
+  }
+
+  imageType_.flags = oldFlags;
+  return glyph;
 }
 
 
 bool
-Engine::fontValid()
+Engine::renderReady()
 {
-  // TODO: use fallback font
   return ftSize_ != NULL;
 }
 
 
+bool
+Engine::fontValid()
+{
+  return ftFallbackFace_ != NULL;
+}
+
+
 void
-Engine::openFonts(QStringList fontFileNames)
+Engine::openFonts(QStringList const& fontFileNames)
 {
   fontFileManager_.append(fontFileNames, true);
 }
@@ -520,7 +571,7 @@ Engine::update()
   {
     loadFlags_ |= FT_LOAD_NO_HINTING;
 
-    if (antiAliasingTarget_ | FT_LOAD_TARGET_MONO)
+    if (!antiAliasingEnabled_)
       loadFlags_ |= FT_LOAD_MONOCHROME;
   }
 
@@ -542,6 +593,10 @@ Engine::update()
     scaler_.x_res = dpi_;
     scaler_.y_res = dpi_;
   }
+
+  imageType_.width = static_cast<unsigned int>(pixelSize_);
+  imageType_.height = static_cast<unsigned int>(pixelSize_);
+  imageType_.flags = static_cast<int>(loadFlags_);
 }
 
 
@@ -550,6 +605,7 @@ Engine::resetCache()
 {
   // reset the cache
   FTC_Manager_Reset(cacheManager_);
+  ftFallbackFace_ = NULL;
   ftSize_ = NULL;
 }
 
diff --git a/src/ftinspect/engine/engine.hpp b/src/ftinspect/engine/engine.hpp
index 7021c77..8698a70 100644
--- a/src/ftinspect/engine/engine.hpp
+++ b/src/ftinspect/engine/engine.hpp
@@ -7,6 +7,10 @@
 
 #include "fontfilemanager.hpp"
 
+#include "rendering.hpp"
+
+#include <memory>
+#include <utility>
 #include <QString>
 #include <QMap>
 
@@ -68,9 +72,20 @@ public:
   int loadFont(int fontIndex,
                long faceIndex,
                int namedInstanceIndex); // return number of glyphs
-  FT_Outline* loadOutline(int glyphIndex);
+  FT_Glyph loadGlyph(int glyphIndex);
+  int loadGlyphIntoSlotWithoutCache(int glyphIndex, bool noScale = false);
+
+  // Sometimes the engine is already updated, and we want to be faster
+  FT_Glyph loadGlyphWithoutUpdate(int glyphIndex,
+                                  FTC_Node* outNode = NULL,
+                                  bool forceRender = false);
 
-  void openFonts(QStringList fontFileNames);
+  // reload current triplet, but with updated settings, useful for updating
+  // `ftSize_` and `ftFallbackFace_` only - more convenient than `loadFont`
+  void reloadFont();
+  void loadPalette();
+
+  void openFonts(QStringList const& fontFileNames);
   void removeFont(int fontIndex, bool closeFile = true);
   
   void update();
@@ -81,11 +96,14 @@ public:
   FT_Library ftLibrary() const { return library_; }
   FontFileManager& fontFileManager() { return fontFileManager_; }
   EngineDefaultValues& engineDefaults() { return engineDefaults_; }
+  RenderingEngine* renderingEngine() { return renderingEngine_.get(); }
 
   int numberOfOpenedFonts();
 
   // (for current fonts)
-  bool fontValid();
+  bool renderReady(); // Can we render bitmaps? (implys `fontValid`)
+  bool fontValid(); // Is the current font valid (valid font may be unavailable
+                    // to render, such as non-scalable font with invalid sizes)
   int currentFontType() const { return fontType_; }
   const QString& currentFamilyName() { return curFamilyName_; }
   const QString& currentStyleName() { return curStyleName_; }
@@ -99,6 +117,12 @@ public:
 
   // (settings)
   int dpi() { return dpi_; }
+  bool antiAliasingEnabled() { return antiAliasingEnabled_; }
+  FT_Render_Mode
+  renderMode()
+  {
+    return static_cast<FT_Render_Mode>(renderMode_);
+  }
 
   //////// Setters (direct or indirect)
 
@@ -120,8 +144,9 @@ public:
     doBlueZoneHinting_ = blueZoneHinting;
   }
   void setShowSegments(bool showSegments) { showSegments_ = showSegments; }
-  void setGamma(double gamma) { gamma_ = gamma; }
   void setAntiAliasingTarget(int target) { antiAliasingTarget_ = target; }
+  void setRenderMode(int mode) { renderMode_ = mode; }
+  void setAntiAliasingEnabled(bool enabled) { antiAliasingEnabled_ = enabled; }
 
   // Note: These 3 functions now takes actual mode/version from FreeType,
   // instead of values from enum in MainGUI!
@@ -143,6 +168,7 @@ private:
 
   FontFileManager fontFileManager_;
 
+  int curFontIndex_ = -1;
   QString curFamilyName_;
   QString curStyleName_;
   int curNumGlyphs_ = -1;
@@ -152,13 +178,19 @@ private:
   FTC_ImageCache imageCache_;
   FTC_SBitCache sbitsCache_;
 
-  FTC_ScalerRec scaler_;
+  FTC_ScalerRec scaler_ = {};
+  FTC_ImageTypeRec imageType_; // for `loadGlyphWithoutUpdate`
+  // Sometimes the font may be valid (i.e. a face object can be retrieved), but
+  // the size may be invalid (e.g. non-scalable fonts).
+  // Therefore, we use a fallback face for all non-rendering work.
+  FT_Face ftFallbackFace_; // Never perform rendering or write to this!
   FT_Size ftSize_;
 
   EngineDefaultValues engineDefaults_;
 
   int fontType_;
 
+  bool antiAliasingEnabled_ = true;
   bool usingPixelSize_ = false;
   double pointSize_;
   double pixelSize_;
@@ -171,12 +203,16 @@ private:
   bool doBlueZoneHinting_;
   bool showSegments_;
   int antiAliasingTarget_;
-
-  double gamma_;
+  int renderMode_;
 
   unsigned long loadFlags_;
 
+  std::unique_ptr<RenderingEngine> renderingEngine_;
+
   void queryEngine();
+  // Safe to put the impl to the cpp.
+  template <class Func>
+  void withFace(FaceID id, Func func);
 
 public:
 
diff --git a/src/ftinspect/engine/rendering.cpp 
b/src/ftinspect/engine/rendering.cpp
new file mode 100644
index 0000000..7c7e8c7
--- /dev/null
+++ b/src/ftinspect/engine/rendering.cpp
@@ -0,0 +1,311 @@
+// rendering.cpp
+
+// Copyright (C) 2022 by Charlie Jiang.
+
+#include "rendering.hpp"
+
+#include <cmath>
+#include <freetype/ftbitmap.h>
+
+#include "engine.hpp"
+
+RenderingEngine::RenderingEngine(Engine* engine)
+: engine_(engine)
+{
+  setForeground(QColor(Qt::black).rgba());
+  setBackground(QColor(Qt::white).rgba());
+}
+
+
+void
+RenderingEngine::setForeground(QRgb foreground)
+{
+  if (foregroundTable_.size() != 256 || foreground != foregroundColor_)
+  {
+    foregroundColor_ = foreground;
+    calculateForegroundTable();
+  }
+}
+
+
+void
+RenderingEngine::setBackground(QRgb background)
+{
+  if (foregroundTable_.size() != 256 || background != backgroundColor_)
+  {
+    backgroundColor_ = background;
+    calculateForegroundTable();
+  }
+}
+
+
+void
+RenderingEngine::setGamma(double gamma)
+{
+  if (gamma_ == gamma)
+    return;
+  gamma_ = gamma;
+  calculateForegroundTable();
+}
+
+
+void
+RenderingEngine::calculateForegroundTable()
+{
+  foregroundTable_.resize(256);
+  auto gamma = gamma_;
+
+  // Yes I know this is horribly slow, but we're only calculating the table 
once
+  // and can use it for all rendering if the color and gamma isn't changing.
+
+  double br = std::pow(qRed(backgroundColor_) / 255.0, gamma);
+  double bg = std::pow(qGreen(backgroundColor_) / 255.0, gamma);
+  double bb = std::pow(qBlue(backgroundColor_) / 255.0, gamma);
+  double invGamma = 1 / gamma;
+
+  for (int i = 0; i <= 0xFF; i++)
+  {
+    double foreAlpha = i * qAlpha(foregroundColor_) / 255.0 / 255.0;
+    double backAlpha = 1 - foreAlpha;
+    double r = std::pow(qRed(foregroundColor_) / 255.0, gamma);
+    double g = std::pow(qGreen(foregroundColor_) / 255.0, gamma);
+    double b = std::pow(qBlue(foregroundColor_) / 255.0, gamma);
+
+    r = br * backAlpha + r * foreAlpha;
+    g = bg * backAlpha + g * foreAlpha;
+    b = bb * backAlpha + b * foreAlpha;
+
+    r = std::pow(r, invGamma);
+    g = std::pow(g, invGamma);
+    b = std::pow(b, invGamma);
+
+    foregroundTable_[i]
+        = qRgba(static_cast<int>(r * 255), 
+                static_cast<int>(g * 255),
+                static_cast<int>(b * 255), 
+                255);
+  }
+}
+
+
+bool
+RenderingEngine::convertGlyphToBitmapGlyph(FT_Glyph src,
+                      FT_Glyph* out)
+{
+  if (src->format == FT_GLYPH_FORMAT_BITMAP)
+  {
+    // No need to convert.
+    *out = src;
+    return false;
+  }
+  if (src->format != FT_GLYPH_FORMAT_OUTLINE)
+  {
+    *out = NULL;
+    return false;
+    // TODO support SVG
+  }
+
+  if (src->format == FT_GLYPH_FORMAT_OUTLINE)
+  {
+    FT_Glyph out2 = src;
+    // This will create a new glyph object.
+    auto error = FT_Glyph_To_Bitmap(&out2, 
+                                    engine_->renderMode(),
+                                    nullptr,
+                                    false);
+    if (error)
+    {
+      *out = NULL;
+      return false;
+    }
+    *out = out2;
+    return true;
+  }
+
+  *out = NULL;
+  return false;
+}
+
+
+FT_Bitmap
+RenderingEngine::convertBitmapTo8Bpp(FT_Bitmap* bitmap)
+{
+  FT_Bitmap out;
+  out.buffer = NULL;
+  // This will create a new bitmap object
+  auto error = FT_Bitmap_Convert(engine_->ftLibrary(), bitmap, &out, 1);
+  if (error)
+  {
+    // XXX handling?
+  }
+  return out;
+}
+
+
+QImage*
+RenderingEngine::convertBitmapToQImage(FT_Bitmap* src)
+{
+  QImage* result = NULL;
+  
+  auto& bmap = *src;
+  bool ownBitmap = false; // if true, we need to cleanup `bmap`
+
+  int width = INT_MAX, height = INT_MAX;
+  if (bmap.width < INT_MAX)
+    width = static_cast<int>(bmap.width);
+  if (bmap.rows < INT_MAX)
+    height = static_cast<int>(bmap.rows);
+  auto format = QImage::Format_Indexed8; // goto crossing init
+
+  if (bmap.pixel_mode == FT_PIXEL_MODE_GRAY2
+      || bmap.pixel_mode == FT_PIXEL_MODE_GRAY4)
+  {
+    bmap = convertBitmapTo8Bpp(&bmap);
+    if (!bmap.buffer)
+      goto cleanup;
+    ownBitmap = true;
+  }
+
+  if (bmap.pixel_mode == FT_PIXEL_MODE_LCD)
+    width /= 3;
+  else if (bmap.pixel_mode == FT_PIXEL_MODE_LCD_V)
+    height /= 3;
+
+  switch (bmap.pixel_mode)
+  {
+  case FT_PIXEL_MODE_MONO:
+    format = QImage::Format_Mono;
+    break;
+  case FT_PIXEL_MODE_GRAY:
+    format = QImage::Format_Indexed8;
+    break;
+  case FT_PIXEL_MODE_BGRA:
+    // XXX "ARGB" here means BGRA due to endianness - may be problematic
+    // on big-endian machines
+    format = QImage::Format_ARGB32_Premultiplied;
+    break;
+  case FT_PIXEL_MODE_LCD:
+  case FT_PIXEL_MODE_LCD_V:
+    format = QImage::Format_ARGB32;
+    break;
+  default:
+    goto cleanup;
+  }
+
+  switch (bmap.pixel_mode) 
+  {
+  case FT_PIXEL_MODE_MONO:
+  case FT_PIXEL_MODE_GRAY:
+  case FT_PIXEL_MODE_BGRA:
+    {
+      QImage image(bmap.buffer, 
+                   width, height, 
+                   bmap.pitch, 
+                   format);
+      if (bmap.pixel_mode == FT_PIXEL_MODE_GRAY)
+        image.setColorTable(foregroundTable_);
+      else if (bmap.pixel_mode == FT_PIXEL_MODE_MONO)
+      {
+        image.setColorCount(2);
+        image.setColor(0, static_cast<QRgb>(0)); // transparent
+        image.setColor(1, foregroundTable_[0xFF]);
+      }
+      result = new QImage(image.copy());
+      // Don't directly use `image` since we're destroying the `bmap`
+    }
+    break;
+  case FT_PIXEL_MODE_LCD:;
+    result = new QImage(width, height, format);
+    //convertLCDToARGB(bmap, *result, engine_->lcdUsesBGR(), foregroundTable_);
+    break;
+  case FT_PIXEL_MODE_LCD_V:;
+    result = new QImage(width, height, format);
+    //convertLCDVToARGB(bmap, *result, engine_->lcdUsesBGR(), 
foregroundTable_);
+    break;
+  }
+
+cleanup:
+  if (ownBitmap)
+    FT_Bitmap_Done(engine_->ftLibrary(), &bmap);
+
+  return result;
+}
+
+
+QImage*
+RenderingEngine::convertGlyphToQImage(FT_Glyph src,
+                             QRect* outRect,
+                             bool inverseRectY)
+{
+  FT_BitmapGlyph bitmapGlyph;
+  bool ownBitmapGlyph
+    = convertGlyphToBitmapGlyph(src, 
reinterpret_cast<FT_Glyph*>(&bitmapGlyph));
+  if (!bitmapGlyph)
+    return NULL;
+
+  auto result = convertBitmapToQImage(&bitmapGlyph->bitmap);
+
+  if (result && outRect)
+  {
+    outRect->setLeft(bitmapGlyph->left);
+    if (inverseRectY)
+      outRect->setTop(-bitmapGlyph->top);
+    else
+      outRect->setTop(bitmapGlyph->top);
+    if (bitmapGlyph->bitmap.width < INT_MAX)
+      outRect->setWidth(static_cast<int>(bitmapGlyph->bitmap.width));
+    else
+      outRect->setWidth(INT_MAX);
+
+    if (bitmapGlyph->bitmap.rows < INT_MAX)
+      outRect->setHeight(static_cast<int>(bitmapGlyph->bitmap.rows));
+    else
+      outRect->setHeight(INT_MAX);
+  }
+
+  if (ownBitmapGlyph)
+    FT_Done_Glyph(reinterpret_cast<FT_Glyph>(bitmapGlyph));
+
+  return result;
+}
+
+
+QPoint
+RenderingEngine::computeGlyphOffset(FT_Glyph glyph, bool inverseY)
+{
+  if (glyph->format == FT_GLYPH_FORMAT_OUTLINE)
+  {
+    FT_BBox cbox;
+    FT_Outline_Get_CBox(&reinterpret_cast<FT_OutlineGlyph>(glyph)->outline, 
+                        &cbox);
+    cbox.xMin &= ~63;
+    cbox.yMin &= ~63;
+    cbox.xMax = (cbox.xMax + 63) & ~63;
+    cbox.yMax = (cbox.yMax + 63) & ~63;
+    if (inverseY)
+      cbox.yMax = -cbox.yMax;
+    return { static_cast<int>(cbox.xMin / 64),
+             static_cast<int>(cbox.yMax / 64) };
+  }
+  if (glyph->format == FT_GLYPH_FORMAT_BITMAP)
+  {
+    auto bg = reinterpret_cast<FT_BitmapGlyph>(glyph);
+    if (inverseY)
+      return { bg->left, -bg->top };
+    return { bg->left, bg->top };
+  }
+
+  return {};
+}
+
+
+QImage*
+RenderingEngine::tryDirectRenderColorLayers(int glyphIndex,
+                                   QRect* outRect,
+                                   bool inverseRectY)
+{
+  return NULL; // TODO: impl
+}
+
+
+// end of rendering.cpp
diff --git a/src/ftinspect/engine/rendering.hpp 
b/src/ftinspect/engine/rendering.hpp
new file mode 100644
index 0000000..6dcedf0
--- /dev/null
+++ b/src/ftinspect/engine/rendering.hpp
@@ -0,0 +1,60 @@
+// rendering.hpp
+
+// Copyright (C) 2022 by Charlie Jiang.
+
+#pragma once
+
+#include <QColor>
+#include <QImage>
+#include <freetype/freetype.h>
+#include <freetype/ftglyph.h>
+
+class Engine;
+class RenderingEngine
+{
+public:
+  RenderingEngine(Engine* engine);
+
+  void setForeground(QRgb foreground);
+  void setBackground(QRgb background);
+  void setGamma(double gamma);
+  void calculateForegroundTable();
+
+  QRgb foreground() { return foregroundColor_; }
+  QRgb background() { return backgroundColor_; }
+  double gamma() { return gamma_; }
+
+  // Return `true` if you need to free `out`
+  // `out` will be set to NULL in cases of error
+  bool convertGlyphToBitmapGlyph(FT_Glyph src, FT_Glyph* out);
+  FT_Bitmap convertBitmapTo8Bpp(FT_Bitmap* bitmap);
+  QImage* convertBitmapToQImage(FT_Bitmap* src);
+  QImage* convertGlyphToQImage(FT_Glyph src, 
+                               QRect* outRect,
+                               bool inverseRectY);
+  QPoint computeGlyphOffset(FT_Glyph glyph, bool inverseY);
+
+  /*
+   * Directly render the glyph at the specified index
+   * to a `QImage`. If you want to perform color-layer
+   * rendering, call this before trying to load the
+   * glyph and do normal rendering, If the returning
+   * value is non-NULL, then there's no need to
+   * load the glyph the normal way, just draw the `QImage`.
+   * Will return NULL if not enabled or color layers not available.
+   */
+  QImage* tryDirectRenderColorLayers(int glyphIndex,
+                                     QRect* outRect,
+                                     bool inverseRectY = false);
+
+private:
+  Engine* engine_;
+
+  QRgb backgroundColor_;
+  QRgb foregroundColor_;
+  double gamma_;
+  QVector<QRgb> foregroundTable_;
+};
+
+
+// end of rendering.hpp
diff --git a/src/ftinspect/glyphcomponents/glyphbitmap.cpp 
b/src/ftinspect/glyphcomponents/glyphbitmap.cpp
index 0479807..a25b5a1 100644
--- a/src/ftinspect/glyphcomponents/glyphbitmap.cpp
+++ b/src/ftinspect/glyphcomponents/glyphbitmap.cpp
@@ -29,13 +29,12 @@ GlyphBitmap::GlyphBitmap(int glyphIndex,
                          Engine* engine)
 {
   QRect bRect;
-  image_ = NULL; // TODO: refactr Engine
-  //image_ = engine->renderingEngine()->tryDirectRenderColorLayers(glyphIndex,
-  //                                                               &bRect, 
true);
+  image_ = engine->renderingEngine()->tryDirectRenderColorLayers(glyphIndex,
+                                                                 &bRect, true);
 
-  //if (!image_)
-  //  image_ = engine->renderingEngine()->convertGlyphToQImage(glyph, &bRect, 
-  //                                                           true);
+  if (!image_)
+    image_ = engine->renderingEngine()->convertGlyphToQImage(glyph, &bRect, 
+                                                             true);
   boundingRect_ = bRect; // QRect to QRectF
 }
 
diff --git a/src/ftinspect/meson.build b/src/ftinspect/meson.build
index 6329b6e..02f1eed 100644
--- a/src/ftinspect/meson.build
+++ b/src/ftinspect/meson.build
@@ -23,6 +23,7 @@ if qt5_dep.found()
   sources = files([
     'engine/engine.cpp',
     'engine/fontfilemanager.cpp',
+    'engine/rendering.cpp',
 
     'glyphcomponents/glyphbitmap.cpp',
     'glyphcomponents/glyphoutline.cpp',
diff --git a/src/ftinspect/panels/settingpanel.cpp 
b/src/ftinspect/panels/settingpanel.cpp
index cf5871c..8b29509 100644
--- a/src/ftinspect/panels/settingpanel.cpp
+++ b/src/ftinspect/panels/settingpanel.cpp
@@ -103,7 +103,7 @@ void
 SettingPanel::checkHintingMode()
 {
   //if (!comparatorMode_)
-  //  applyDelayedSettings();
+  applyDelayedSettings();
 
   emit fontReloadNeeded();
 }
@@ -195,7 +195,7 @@ void
 SettingPanel::checkStemDarkening()
 {
   //if (!comparatorMode_)
-  //  applyDelayedSettings();
+  applyDelayedSettings();
 
   emit fontReloadNeeded();
 }
@@ -314,10 +314,10 @@ SettingPanel::applySettings()
   auto aaSettings = antiAliasingComboBoxModel_->indexToValue(
     antiAliasingComboBox_->currentIndex());
   engine_->setAntiAliasingTarget(aaSettings.loadFlag);
-  //engine_->setRenderMode(aaSettings.renderMode);
+  engine_->setRenderMode(aaSettings.renderMode);
 
-  //engine_->setAntiAliasingEnabled(antiAliasingComboBox_->currentIndex()
-  //  != AntiAliasingComboBoxModel::AntiAliasing_None);
+  engine_->setAntiAliasingEnabled(antiAliasingComboBox_->currentIndex()
+    != AntiAliasingComboBoxModel::AntiAliasing_None);
   engine_->setHinting(hintingCheckBox_->isChecked());
   engine_->setAutoHinting(autoHintingCheckBox_->isChecked());
 
@@ -329,7 +329,7 @@ SettingPanel::applySettings()
     engine_->setShowSegments(segmentDrawingCheckBox_->isChecked());
   }
 
-  engine_->setGamma(gammaSlider_->value() / 10.0);
+  engine_->renderingEngine()->setGamma(gammaSlider_->value() / 10.0);
 
   //engine_->setEmbeddedBitmap(embeddedBitmapCheckBox_->isChecked());
   //engine_->setPaletteIndex(paletteComboBox_->currentIndex());
@@ -340,8 +340,8 @@ SettingPanel::applySettings()
   //  antiAliasingComboBox_->currentIndex()
   //    == AntiAliasingComboBoxModel::AntiAliasing_Light_SubPixel);
 
-  //engine_->renderingEngine()->setForeground(foregroundColor_.rgba());
-  //engine_->renderingEngine()->setBackground(backgroundColor_.rgba());
+  engine_->renderingEngine()->setForeground(foregroundColor_.rgba());
+  engine_->renderingEngine()->setBackground(backgroundColor_.rgba());
   //mmgxPanel_->applySettings();
 }
 
diff --git a/src/ftinspect/panels/singular.cpp 
b/src/ftinspect/panels/singular.cpp
index c8c925c..60ffef6 100644
--- a/src/ftinspect/panels/singular.cpp
+++ b/src/ftinspect/panels/singular.cpp
@@ -87,13 +87,12 @@ SingularTab::drawGlyph()
     currentGlyphPointNumbersItem_ = NULL;
   }
 
-  // TODO refactor engine.
-  //glyphView_->setBackgroundBrush(
-  //  QColor(engine_->renderingEngine()->background()));
+  glyphView_->setBackgroundBrush(
+    QColor(engine_->renderingEngine()->background()));
+
 
   applySettings();
-  FT_Glyph glyph = NULL; // TODO: refactor engine!
-  //FT_Glyph glyph = engine_->loadGlyph(currentGlyphIndex_);
+  FT_Glyph glyph = engine_->loadGlyph(currentGlyphIndex_);
   if (glyph)
   {
     if (showBitmapCheckBox_->isChecked())



reply via email to

[Prev in Thread] Current Thread [Next in Thread]