freetype-commit
[Top][All Lists]
Advanced

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

[freetype2-demos] gsoc-2022-chariri-3 bb053ff 01/36: [ftinspect] Add Fan


From: Werner Lemberg
Subject: [freetype2-demos] gsoc-2022-chariri-3 bb053ff 01/36: [ftinspect] Add Fancy submode to "Continuous View"
Date: Wed, 27 Jul 2022 06:32:43 -0400 (EDT)

branch: gsoc-2022-chariri-3
commit bb053ffd3ffd3b145aea84b53009e285004fa805
Author: Charlie Jiang <w@chariri.moe>
Commit: Charlie Jiang <w@chariri.moe>

    [ftinspect] Add Fancy submode to "Continuous View"
    
    Fancy mode adds slanting and hori./vert. emboldening to the glyph.
    
    `GlyphContinuous::paintChar` is broken down to better support additional
    operations during rendering. Add proper cloning so the glyph and the
    outline which are stored in the `GlyphContinuous` class can be safely
    modified.
    
    * src/ftinspect/rendering/glyphcontinuous.cpp,
      src/ftinspect/rendering/glyphcontinuous.hpp: As described, refactored and
      supported Fancy mode.
    
    * src/ftinspect/rendering/glyphbitmap.cpp: Edited to match the new
      `renderutils.cpp`.
    
    * src/ftinspect/rendering/renderutils.cpp,
      src/ftinspect/rendering/renderutils.hpp: Broken down
      `transformOutlineToOrigin`, add `cloneOutline` and `cloneGlyph`.
    
    * src/ftinspect/panels/continuous.cpp, src/ftinspect/panels/continuous.hpp:
      Add necessary GUI components and data passing to support fancy mode.
---
 src/ftinspect/panels/continuous.cpp         |  88 +++++++++++++++-
 src/ftinspect/panels/continuous.hpp         |  14 +++
 src/ftinspect/rendering/glyphbitmap.cpp     |   3 +-
 src/ftinspect/rendering/glyphcontinuous.cpp | 158 ++++++++++++++++++++++++----
 src/ftinspect/rendering/glyphcontinuous.hpp |  30 +++++-
 src/ftinspect/rendering/renderutils.cpp     |  31 ++++--
 src/ftinspect/rendering/renderutils.hpp     |   9 +-
 7 files changed, 290 insertions(+), 43 deletions(-)

diff --git a/src/ftinspect/panels/continuous.cpp 
b/src/ftinspect/panels/continuous.cpp
index c296530..b3c955a 100644
--- a/src/ftinspect/panels/continuous.cpp
+++ b/src/ftinspect/panels/continuous.cpp
@@ -130,6 +130,10 @@ ContinuousTab::updateFromCurrentSubTab()
     canvas_->setBeginIndex(allGlyphsTab_->glyphBeginindex());
     canvas_->setLimitIndex(allGlyphsTab_->glyphLimitIndex());
     canvas_->setCharMapIndex(allGlyphsTab_->charMapIndex());
+
+    canvas_->setFancyParams(allGlyphsTab_->xEmboldening(),
+                            allGlyphsTab_->yEmboldening(),
+                            allGlyphsTab_->slanting());
     break;
   }
 }
@@ -143,6 +147,8 @@ ContinousAllGlyphsTab::ContinousAllGlyphsTab(QWidget* 
parent)
   QVector<CharMapInfo> tempCharMaps;
   setCharMaps(tempCharMaps); // pass in an empty one
 
+  checkSubMode();
+  setDefaults();
   createConnections();
 }
 
@@ -169,6 +175,27 @@ ContinousAllGlyphsTab::subMode()
 }
 
 
+double
+ContinousAllGlyphsTab::xEmboldening()
+{
+  return xEmboldeningSpinBox_->value();
+}
+
+
+double
+ContinousAllGlyphsTab::yEmboldening()
+{
+  return yEmboldeningSpinBox_->value();
+}
+
+
+double
+ContinousAllGlyphsTab::slanting()
+{
+  return slantSpinBox_->value();
+}
+
+
 int
 ContinousAllGlyphsTab::charMapIndex()
 {
@@ -263,6 +290,18 @@ ContinousAllGlyphsTab::updateLimitIndex()
 }
 
 
+void
+ContinousAllGlyphsTab::checkSubMode()
+{
+  auto isFancy = subMode() == GlyphContinuous::AG_Fancy;
+  xEmboldeningSpinBox_->setEnabled(isFancy);
+  yEmboldeningSpinBox_->setEnabled(isFancy);
+  slantSpinBox_->setEnabled(isFancy);
+
+  emit changed();
+}
+
+
 void
 ContinousAllGlyphsTab::createLayout()
 {
@@ -276,13 +315,31 @@ ContinousAllGlyphsTab::createLayout()
 
   // Note: in sync with the enum!!
   modeSelector_->insertItem(GlyphContinuous::AG_AllGlyphs, tr("All Glyphs"));
-  modeSelector_->insertItem(GlyphContinuous::AG_Fancy, tr("Fancy"));
+  modeSelector_->insertItem(GlyphContinuous::AG_Fancy, 
+                            tr("Fancy (Embolding & Slanting)"));
   modeSelector_->insertItem(GlyphContinuous::AG_Stroked, tr("Stroked"));
   modeSelector_->insertItem(GlyphContinuous::AG_Waterfall, tr("Waterfall"));
   modeSelector_->setCurrentIndex(GlyphContinuous::AG_AllGlyphs);
 
   modeLabel_ = new QLabel(tr("Mode:"), this);
   charMapLabel_ = new QLabel(tr("Char Map:"), this);
+  xEmboldeningLabel_ = new QLabel(tr("Hori. Embolding (for Fancy):"), this);
+  yEmboldeningLabel_ = new QLabel(tr("Vert. Embolding (for Fancy):"), this);
+  slantLabel_ = new QLabel(tr("Slanting (for Fancy):"), this);
+
+  xEmboldeningSpinBox_ = new QDoubleSpinBox(this);
+  yEmboldeningSpinBox_ = new QDoubleSpinBox(this);
+  slantSpinBox_ = new QDoubleSpinBox(this);
+
+  xEmboldeningSpinBox_->setSingleStep(0.005);
+  xEmboldeningSpinBox_->setMinimum(-0.1);
+  xEmboldeningSpinBox_->setMaximum(0.1);
+  yEmboldeningSpinBox_->setSingleStep(0.005);
+  yEmboldeningSpinBox_->setMinimum(-0.1);
+  yEmboldeningSpinBox_->setMaximum(0.1);
+  slantSpinBox_->setSingleStep(0.02);
+  slantSpinBox_->setMinimum(-1);
+  slantSpinBox_->setMaximum(1);
 
   layout_ = new QGridLayout;
   layout_->addWidget(indexSelector_, 0, 0, 1, 2);
@@ -291,7 +348,15 @@ ContinousAllGlyphsTab::createLayout()
   layout_->addWidget(modeSelector_, 1, 1);
   layout_->addWidget(charMapSelector_, 2, 1);
 
+  layout_->addWidget(xEmboldeningLabel_, 1, 2);
+  layout_->addWidget(yEmboldeningLabel_, 2, 2);
+  layout_->addWidget(slantLabel_, 3, 2);
+  layout_->addWidget(xEmboldeningSpinBox_, 1, 3);
+  layout_->addWidget(yEmboldeningSpinBox_, 2, 3);
+  layout_->addWidget(slantSpinBox_, 3, 3);
+
   layout_->setColumnStretch(1, 1);
+  layout_->setColumnStretch(3, 1);
 
   setLayout(layout_);
 }
@@ -302,9 +367,19 @@ ContinousAllGlyphsTab::createConnections()
   connect(indexSelector_, &GlyphIndexSelector::currentIndexChanged,
           this, &ContinousAllGlyphsTab::changed);
   connect(modeSelector_, QOverload<int>::of(&QComboBox::currentIndexChanged),
-          this, &ContinousAllGlyphsTab::changed);
+          this, &ContinousAllGlyphsTab::checkSubMode);
   connect(charMapSelector_, 
QOverload<int>::of(&QComboBox::currentIndexChanged),
           this, &ContinousAllGlyphsTab::charMapChanged);
+
+  connect(xEmboldeningSpinBox_, 
+          QOverload<double>::of(&QDoubleSpinBox::valueChanged),
+          this, &ContinousAllGlyphsTab::changed);
+  connect(yEmboldeningSpinBox_, 
+          QOverload<double>::of(&QDoubleSpinBox::valueChanged),
+          this, &ContinousAllGlyphsTab::changed);
+  connect(slantSpinBox_, 
+          QOverload<double>::of(&QDoubleSpinBox::valueChanged),
+          this, &ContinousAllGlyphsTab::changed);
 }
 
 
@@ -339,4 +414,13 @@ ContinousAllGlyphsTab::charMapChanged()
 }
 
 
+void
+ContinousAllGlyphsTab::setDefaults()
+{
+  xEmboldeningSpinBox_->setValue(0.04);
+  yEmboldeningSpinBox_->setValue(0.04);
+  slantSpinBox_->setValue(0.22);
+}
+
+
 // end of continuous.cpp
diff --git a/src/ftinspect/panels/continuous.hpp 
b/src/ftinspect/panels/continuous.hpp
index fb37928..c716aca 100644
--- a/src/ftinspect/panels/continuous.hpp
+++ b/src/ftinspect/panels/continuous.hpp
@@ -79,6 +79,9 @@ public:
   int glyphBeginindex();
   int glyphLimitIndex();
   GlyphContinuous::SubModeAllGlyphs subMode();
+  double xEmboldening();
+  double yEmboldening();
+  double slanting();
 
   // -1: Glyph order, otherwise the char map index in the original list
   int charMapIndex();
@@ -92,6 +95,8 @@ public:
   // This doesn't trigger either.
   void updateLimitIndex();
 
+  void checkSubMode();
+
 signals:
   void changed();
 
@@ -106,6 +111,13 @@ private:
 
   QLabel* modeLabel_;
   QLabel* charMapLabel_;
+  QLabel* xEmboldeningLabel_;
+  QLabel* yEmboldeningLabel_;
+  QLabel* slantLabel_;
+
+  QDoubleSpinBox* xEmboldeningSpinBox_;
+  QDoubleSpinBox* yEmboldeningSpinBox_;
+  QDoubleSpinBox* slantSpinBox_;
 
   QGridLayout* layout_;
 
@@ -116,6 +128,8 @@ private:
 
   QString formatIndex(int index);
   void charMapChanged();
+
+  void setDefaults();
 };
 
 
diff --git a/src/ftinspect/rendering/glyphbitmap.cpp 
b/src/ftinspect/rendering/glyphbitmap.cpp
index 20bf92e..78623a6 100644
--- a/src/ftinspect/rendering/glyphbitmap.cpp
+++ b/src/ftinspect/rendering/glyphbitmap.cpp
@@ -24,7 +24,8 @@ GlyphBitmap::GlyphBitmap(FT_Outline* outline,
 {
   // make a copy of the outline since we are going to manipulate it
   FT_BBox cbox;
-  transformed_ = transformOutlineToOrigin(lib, outline, &cbox);
+  transformed_ = cloneOutline(lib, outline);
+  transformOutlineToOrigin(&transformed_, &cbox);
   boundingRect_.setCoords(cbox.xMin / 64, -cbox.yMax / 64,
                   cbox.xMax / 64, -cbox.yMin / 64);
 }
diff --git a/src/ftinspect/rendering/glyphcontinuous.cpp 
b/src/ftinspect/rendering/glyphcontinuous.cpp
index 524af4c..33e4954 100644
--- a/src/ftinspect/rendering/glyphcontinuous.cpp
+++ b/src/ftinspect/rendering/glyphcontinuous.cpp
@@ -38,12 +38,9 @@ GlyphContinuous::paintEvent(QPaintEvent* event)
       switch (modeAG_)
       {
       case AG_AllGlyphs:
-        paintAGAllGlyphs(&painter);
-        break;
-        // TODO more modes
       case AG_Fancy:
-        break;
       case AG_Stroked:
+        paintAG(&painter);
         break;
       case AG_Waterfall:
         break;
@@ -71,7 +68,7 @@ GlyphContinuous::wheelEvent(QWheelEvent* event)
 
 
 void
-GlyphContinuous::paintAGAllGlyphs(QPainter* painter)
+GlyphContinuous::paintAG(QPainter* painter)
 {
   for (int i = beginIndex_; i < limitIndex_; i++)
   {
@@ -79,11 +76,85 @@ GlyphContinuous::paintAGAllGlyphs(QPainter* painter)
     if (charMapIndex_ >= 0)
       index = engine_->glyphIndexFromCharCode(i, charMapIndex_);
 
-    if (!paintChar(painter, index))
+    if (!loadGlyph(index))
       break;
 
+    // All Glyphs need no tranformation, and Waterfall isn't handled here.
+    switch (modeAG_)
+    {
+    case AG_Fancy:
+      transformGlyphAGFancy();
+      break;
+    case AG_Stroked:
+      transformGlyphAGStroked();
+      break;
+    default:;
+    }
+
+    if (!paintChar(painter))
+      break;
+    cleanCloned();
+
     displayingCount_++;
   }
+  cleanCloned();
+}
+
+
+void
+GlyphContinuous::transformGlyphAGFancy()
+{
+  // adopted from ftview.c:289
+  /***************************************************************/
+  /*                                                             */
+  /*  2*2 affine transformation matrix, 16.16 fixed float format */
+  /*                                                             */
+  /*  Shear matrix:                                              */
+  /*                                                             */
+  /*         | x' |     | 1  k |   | x |          x' = x + ky    */
+  /*         |    |  =  |      | * |   |   <==>                  */
+  /*         | y' |     | 0  1 |   | y |          y' = y         */
+  /*                                                             */
+  /*        outline'     shear    outline                        */
+  /*                                                             */
+  /***************************************************************/
+
+  FT_Matrix shear;
+  FT_Pos xstr, ystr;
+
+  shear.xx = 1 << 16;
+  shear.xy = (FT_Fixed)(slant_ * (1 << 16));
+  shear.yx = 0;
+  shear.yy = 1 << 16;
+
+  xstr = (FT_Pos)(metrics_.y_ppem * 64 * boldX_);
+  ystr = (FT_Pos)(metrics_.y_ppem * 64 * boldY_);
+
+  if (!isGlyphCloned_)
+    cloneGlyph();
+
+  if (glyph_->format != FT_GLYPH_FORMAT_OUTLINE)
+    return; // TODO suuport non-outline: code below all depend on `outline_`!
+
+  FT_Outline_Transform(&outline_, &shear);
+  FT_Outline_EmboldenXY(&outline_, xstr, ystr);
+
+  if (glyph_->advance.x)
+    glyph_->advance.x += xstr;
+
+  if (glyph_->advance.y)
+    glyph_->advance.y += ystr;
+  
+  //glyph_->metrics.width += xstr;
+  //glyph_->metrics.height += ystr;
+  //glyph_->metrics.horiAdvance += xstr;
+  //glyph_->metrics.vertAdvance += ystr;
+}
+
+
+void
+GlyphContinuous::transformGlyphAGStroked()
+{
 }
 
 
@@ -101,16 +172,11 @@ GlyphContinuous::prePaint()
 
 
 bool
-GlyphContinuous::paintChar(QPainter* painter,
-                           int index)
+GlyphContinuous::paintChar(QPainter* painter)
 {
-  auto glyph = engine_->loadGlyphWithoutUpdate(index);
-  if (!glyph)
-    return false;
-
   // ftview.c:557
-  int width = glyph->advance.x ? glyph->advance.x >> 16
-                               : metrics_.y_ppem / 2;
+  int width = glyph_->advance.x ? glyph_->advance.x >> 16
+                                : metrics_.y_ppem / 2;
 
   if (!checkFitX(x_ + width))
   {
@@ -122,7 +188,7 @@ GlyphContinuous::paintChar(QPainter* painter,
   }
 
   x_++; // extra space
-  if (glyph->advance.x == 0)
+  if (glyph_->advance.x == 0)
   {
     // Draw a red square to indicate
       painter->fillRect(x_, y_ - width, width, width,
@@ -136,15 +202,15 @@ GlyphContinuous::paintChar(QPainter* painter,
 
   // First translate the outline
 
-  if (glyph->format != FT_GLYPH_FORMAT_OUTLINE)
+  if (glyph_->format != FT_GLYPH_FORMAT_OUTLINE)
     return true; // XXX only outline is supported - need to impl others later
 
   FT_BBox cbox;
   // Don't forget to free this when returning
-  auto outline = transformOutlineToOrigin(
-                   engine_->ftLibrary(),
-                   &reinterpret_cast<FT_OutlineGlyph>(glyph)->outline,
-                   &cbox);
+  if (!isOutlineCloned_ && !isGlyphCloned_)
+    cloneOutline();
+  
+  transformOutlineToOrigin(&outline_, &cbox);
   
   auto outlineWidth = (cbox.xMax - cbox.xMin) / 64;
   auto outlineHeight = (cbox.yMax - cbox.yMin) / 64;
@@ -175,12 +241,11 @@ GlyphContinuous::paintChar(QPainter* painter,
   bitmap.pixel_mode = aaEnabled ? FT_PIXEL_MODE_GRAY : FT_PIXEL_MODE_MONO;
 
   FT_Error error = FT_Outline_Get_Bitmap(engine_->ftLibrary(),
-                                         &outline,
+                                         &outline_,
                                          &bitmap);
   if (error)
   {
     // XXX error handling
-    FT_Outline_Done(engine_->ftLibrary(), &outline);
     return true;
   }
 
@@ -189,12 +254,59 @@ GlyphContinuous::paintChar(QPainter* painter,
       image.convertToFormat(QImage::Format_ARGB32_Premultiplied));
 
   x_ += width;
+  
+  return true;
+}
 
-  FT_Outline_Done(engine_->ftLibrary(), &outline);
+
+bool
+GlyphContinuous::loadGlyph(int index)
+{
+  glyph_ = engine_->loadGlyphWithoutUpdate(index);
+  isGlyphCloned_ = false;
+  if (!glyph_)
+    return false;
+  if (glyph_->format == FT_GLYPH_FORMAT_OUTLINE)
+  {
+    isOutlineCloned_ = false;
+    outline_ = reinterpret_cast<FT_OutlineGlyph>(glyph_)->outline;
+  }
   return true;
 }
 
 
+void
+GlyphContinuous::cloneGlyph()
+{
+  glyph_ = ::cloneGlyph(glyph_);
+  isGlyphCloned_ = true;
+}
+
+
+void
+GlyphContinuous::cloneOutline()
+{
+  outline_ = ::cloneOutline(engine_->ftLibrary(), &outline_);
+  isOutlineCloned_ = true;
+}
+
+
+void
+GlyphContinuous::cleanCloned()
+{
+  if (isGlyphCloned_)
+  {
+    FT_Done_Glyph(glyph_);
+    isGlyphCloned_ = false;
+  }
+  if (isOutlineCloned_)
+  {
+    FT_Outline_Done(engine_->ftLibrary(), &outline_);
+    isOutlineCloned_ = false;
+  }
+}
+
+
 bool
 GlyphContinuous::checkFitX(int x)
 {
diff --git a/src/ftinspect/rendering/glyphcontinuous.hpp 
b/src/ftinspect/rendering/glyphcontinuous.hpp
index 9a87e7e..5206170 100644
--- a/src/ftinspect/rendering/glyphcontinuous.hpp
+++ b/src/ftinspect/rendering/glyphcontinuous.hpp
@@ -7,6 +7,8 @@
 #include "graphicsdefault.hpp"
 #include <QWidget>
 #include <freetype/freetype.h>
+#include <freetype/ftglyph.h>
+#include <freetype/ftoutln.h>
 
 class Engine;
 class GlyphContinuous
@@ -39,6 +41,12 @@ public:
   void setCharMapIndex(int index) { charMapIndex_ = index; }
   void setMode(Mode mode) { mode_ = mode; }
   void setSubModeAllGlyphs(SubModeAllGlyphs modeAg) { modeAG_ = modeAg; }
+  void setFancyParams(double boldX, double boldY, double slant)
+  {
+    boldX_ = boldX;
+    boldY_ = boldY;
+    slant_ = slant;
+  }
 
 signals:
   void wheelNavigate(int steps);
@@ -53,21 +61,33 @@ private:
   Engine* engine_;
   GraphicsDefault* graphicsDefault_;
 
+  Mode mode_ = AllGlyphs;
+  SubModeAllGlyphs modeAG_ = AG_AllGlyphs;
   int beginIndex_;
   int limitIndex_;
   int charMapIndex_;
-  Mode mode_ = AllGlyphs;
-  SubModeAllGlyphs modeAG_ = AG_AllGlyphs;
+  double boldX_ = 0.04, boldY_ = 0.04, slant_ = 0.22;
 
   int displayingCount_ = 0;
   FT_Size_Metrics metrics_;
   int x_ = 0, y_ = 0;
   int stepY_ = 0;
-
-  void paintAGAllGlyphs(QPainter* painter);
+  FT_Glyph glyph_;
+  FT_Outline outline_;
+  // when glyph is cloned, outline is factually also cloned
+  // but `isOutlineCloned` won't be set!
+  bool isGlyphCloned_ = false, isOutlineCloned_ = false;
+
+  void paintAG(QPainter* painter);
+  void transformGlyphAGFancy();
+  void transformGlyphAGStroked();
   void prePaint();
   // return if there's enough space to paint the current char
-  bool paintChar(QPainter* painter, int index);
+  bool paintChar(QPainter* painter);
+  bool loadGlyph(int index);
+  void cloneGlyph();
+  void cloneOutline();
+  void cleanCloned();
 
   bool checkFitX(int x);
   bool checkFitY(int y);
diff --git a/src/ftinspect/rendering/renderutils.cpp 
b/src/ftinspect/rendering/renderutils.cpp
index 9866db8..260f499 100644
--- a/src/ftinspect/rendering/renderutils.cpp
+++ b/src/ftinspect/rendering/renderutils.cpp
@@ -5,16 +5,30 @@
 #include "renderutils.hpp"
 
 FT_Outline
-transformOutlineToOrigin(FT_Library library, 
-                         FT_Outline* outline,
-                         FT_BBox* outControlBox)
+cloneOutline(FT_Library library, 
+             FT_Outline* src)
 {
   FT_Outline transformed;
-  FT_Outline_New(library,
-                 static_cast<unsigned int>(outline->n_points),
-                 outline->n_contours, &transformed);
-  FT_Outline_Copy(outline, &transformed);
+  FT_Outline_New(library, static_cast<unsigned int>(src->n_points),
+                 src->n_contours, &transformed);
+  FT_Outline_Copy(src, &transformed);
+  return transformed;
+}
 
+
+FT_Glyph
+cloneGlyph(FT_Glyph src)
+{
+  FT_Glyph target = NULL;
+  FT_Glyph_Copy(src, &target);
+  return target;
+}
+
+
+void
+transformOutlineToOrigin(FT_Outline* outline,
+                         FT_BBox* outControlBox)
+{
   FT_BBox cbox;
   FT_Outline_Get_CBox(outline, &cbox);
 
@@ -23,11 +37,10 @@ transformOutlineToOrigin(FT_Library library,
   cbox.xMax = (cbox.xMax + 63) & ~63;
   cbox.yMax = (cbox.yMax + 63) & ~63;
   // we shift the outline to the origin for rendering later on
-  FT_Outline_Translate(&transformed, -cbox.xMin, -cbox.yMin);
+  FT_Outline_Translate(outline, -cbox.xMin, -cbox.yMin);
 
   if (outControlBox)
     *outControlBox = cbox;
-  return transformed;
 }
 
 
diff --git a/src/ftinspect/rendering/renderutils.hpp 
b/src/ftinspect/rendering/renderutils.hpp
index ba4caf4..55175fa 100644
--- a/src/ftinspect/rendering/renderutils.hpp
+++ b/src/ftinspect/rendering/renderutils.hpp
@@ -4,12 +4,15 @@
 
 #pragma once
 
+#include <freetype/ftglyph.h>
 #include <freetype/ftoutln.h>
 
 // The constructed `outline` must be freed by the caller
-FT_Outline transformOutlineToOrigin(FT_Library library, 
-                                    FT_Outline* outline,
-                                    FT_BBox* outControlBox);
+FT_Outline cloneOutline(FT_Library library, FT_Outline* src);
+FT_Glyph cloneGlyph(FT_Glyph src);
+
+void transformOutlineToOrigin(FT_Outline* outline,
+                              FT_BBox* outControlBox);
 
 
 // end of renderutils.hpp



reply via email to

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