freetype-commit
[Top][All Lists]
Advanced

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

[freetype2-demos] mpsuzuki-gsoc-2022-chariri-3 794b613 1/5: [ftinspect]


From: Werner Lemberg
Subject: [freetype2-demos] mpsuzuki-gsoc-2022-chariri-3 794b613 1/5: [ftinspect] Add caching of `QImage` and dimension info in Continuous View.
Date: Wed, 27 Jul 2022 23:06:08 -0400 (EDT)

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

    [ftinspect] Add caching of `QImage` and dimension info in Continuous View.
    
    This gives further possibility of implementation click-for-detail and
    dragging features.
    Also improves performance, but very slightly because the cache is purged
    aggresively.
    
    * src/ftinspect/rendering/glyphcontinuous.cpp,
      src/ftinspect/rendering/glyphcontinuous.hpp:
      Implementation the caching, remove non-cached drawing of the glyphs.
    
    * src/ftinspect/engine/stringrenderer.cpp,
      src/ftinspect/engine/stringrenderer.hpp:
      Add an argument to pass the current `GlyphContext` to both callbacks.
    
    * src/ftinspect/panels/continuous.cpp: Purge caches at proper time.
---
 src/ftinspect/engine/stringrenderer.cpp     |   4 +-
 src/ftinspect/engine/stringrenderer.hpp     |   8 +-
 src/ftinspect/panels/continuous.cpp         |   2 +
 src/ftinspect/rendering/glyphcontinuous.cpp | 217 +++++++++++++++++++++-------
 src/ftinspect/rendering/glyphcontinuous.hpp |  63 ++++++--
 5 files changed, 229 insertions(+), 65 deletions(-)

diff --git a/src/ftinspect/engine/stringrenderer.cpp 
b/src/ftinspect/engine/stringrenderer.cpp
index 8d8adbd..a3c2608 100644
--- a/src/ftinspect/engine/stringrenderer.cpp
+++ b/src/ftinspect/engine/stringrenderer.cpp
@@ -510,7 +510,7 @@ StringRenderer::renderLine(int x,
     {
       rect.setX(rect.x() + (pen.x >> 6));
       rect.setY(height - rect.y() - (pen.y >> 6));
-      renderImageCallback_(colorLayerImage, rect);
+      renderImageCallback_(colorLayerImage, rect, ctx);
     }
     else
     {
@@ -569,7 +569,7 @@ StringRenderer::renderLine(int x,
           && bbox.yMin <= height)
       {
         FT_Vector penPos = { (pen.x >> 6), height - (pen.y >> 6) };
-        renderCallback_(image, penPos);
+        renderCallback_(image, penPos, ctx);
       }
 
       FT_Done_Glyph(image);
diff --git a/src/ftinspect/engine/stringrenderer.hpp 
b/src/ftinspect/engine/stringrenderer.hpp
index 610b341..8597587 100644
--- a/src/ftinspect/engine/stringrenderer.hpp
+++ b/src/ftinspect/engine/stringrenderer.hpp
@@ -61,13 +61,17 @@ public:
    * contains no points, and thus can't be translated to the desired pen
    * position.
    */
-  using RenderCallback = std::function<void(FT_Glyph, FT_Vector)>;
+  using RenderCallback = std::function<void(FT_Glyph, 
+                                            FT_Vector, 
+                                            GlyphContext&)>;
   /*
    * For color layered fonts, this will direct render the QImage for you.
    * TODO: Remove `RenderCallback` and do QImage creation in this class?
    * The receiver is responsible for deleteing the QImage.
    */
-  using RenderImageCallback = std::function<void(QImage*, QRect)>;
+  using RenderImageCallback = std::function<void(QImage*, 
+                                                 QRect, 
+                                                 GlyphContext&)>;
   /*
    * The glyph pointer may be replaced. In that case, ownership is transfered
    * to the renderer, and the new glyph will be eventually freed by
diff --git a/src/ftinspect/panels/continuous.cpp 
b/src/ftinspect/panels/continuous.cpp
index be80c57..ebb6a88 100644
--- a/src/ftinspect/panels/continuous.cpp
+++ b/src/ftinspect/panels/continuous.cpp
@@ -30,6 +30,7 @@ ContinuousTab::repaintGlyph()
   sizeSelector_->applyToEngine(engine_);
   
   syncSettings();
+  canvas_->purgeCache();
   canvas_->repaint();
 }
 
@@ -41,6 +42,7 @@ ContinuousTab::reloadFont()
   setGlyphCount(qBound(0, currentGlyphCount_, INT_MAX));
   setCharMaps(engine_->currentFontCharMaps());
   canvas_->stringRenderer().reloadAll();
+  canvas_->purgeCache();
   repaintGlyph();
 }
 
diff --git a/src/ftinspect/rendering/glyphcontinuous.cpp 
b/src/ftinspect/rendering/glyphcontinuous.cpp
index 9497bec..41fd0b8 100644
--- a/src/ftinspect/rendering/glyphcontinuous.cpp
+++ b/src/ftinspect/rendering/glyphcontinuous.cpp
@@ -12,6 +12,37 @@
 #include <freetype/ftbitmap.h>
 
 
+GlyphCacheEntry::~GlyphCacheEntry()
+{
+  delete image;
+}
+
+
+GlyphCacheEntry::GlyphCacheEntry(GlyphCacheEntry&& other) noexcept
+{
+  *this = std::move(other);
+}
+
+
+GlyphCacheEntry&
+GlyphCacheEntry::operator=(GlyphCacheEntry&& other) noexcept
+{
+  if (this == &other)
+    return *this;
+
+  auto oldImage = image;
+  image = other.image;
+  basePosition = other.basePosition;
+  penPos = other.penPos;
+  charCode = other.charCode;
+  glyphIndex = other.glyphIndex;
+  advance = other.advance;
+  hasAdvance = other.hasAdvance;
+  other.image = oldImage;
+  return *this;
+}
+
+
 GlyphContinuous::GlyphContinuous(QWidget* parent, Engine* engine)
 : QWidget(parent),
   engine_(engine),
@@ -38,9 +69,11 @@ GlyphContinuous::setSource(Source source)
   {
   case SRC_AllGlyphs:
     stringRenderer_.setUseAllGlyphs();
+    positionDelta_ = {};
     break;
 
   case SRC_TextStringRepeated:
+    positionDelta_ = {};
   case SRC_TextString:
     updateRendererText();
     break;
@@ -56,6 +89,14 @@ GlyphContinuous::setSourceText(QString text)
 }
 
 
+void
+GlyphContinuous::purgeCache()
+{
+  glyphCache_.clear();
+  currentWritingLine_ = NULL;
+}
+
+
 void
 GlyphContinuous::paintEvent(QPaintEvent* event)
 {
@@ -63,10 +104,9 @@ GlyphContinuous::paintEvent(QPaintEvent* event)
   painter.begin(this);
   painter.fillRect(rect(), Qt::white);
 
-  prePaint();
-
-  paintByRenderer(&painter);
-  emit displayingCountUpdated(displayingCount_);
+  if (glyphCache_.empty())
+    fillCache();
+  paintCache(&painter);
 
   painter.end();
 }
@@ -84,27 +124,28 @@ GlyphContinuous::wheelEvent(QWheelEvent* event)
 
 
 void
-GlyphContinuous::paintByRenderer(QPainter* painter)
+GlyphContinuous::resizeEvent(QResizeEvent* event)
 {
-  if (mode_ == M_Stroked)
-  {
-    auto radius = static_cast<FT_Fixed>(metrics_.y_ppem * 64 * strokeRadius_);
-    FT_Stroker_Set(stroker_, radius,
-                   FT_STROKER_LINECAP_ROUND,
-                   FT_STROKER_LINEJOIN_ROUND,
-                   0);
-  }
+  purgeCache();
+  QWidget::resizeEvent(event);
+}
+
+
+void
+GlyphContinuous::paintByRenderer()
+{
+  purgeCache();
 
   stringRenderer_.setRepeated(source_ == SRC_TextStringRepeated);
   stringRenderer_.setCallback(
-    [&](FT_Glyph glyph, FT_Vector penPos)
+    [&](FT_Glyph glyph, FT_Vector penPos, GlyphContext& ctx)
     {
-      drawSingleGlyph(painter, glyph, penPos);
+      saveSingleGlyph(glyph, penPos, ctx);
     });
   stringRenderer_.setImageCallback(
-    [&](QImage* image, QRect pos)
+    [&](QImage* image, QRect pos, GlyphContext& ctx)
     {
-      drawSingleGlyphImage(painter, image, pos);
+      saveSingleGlyphImage(image, pos, ctx);
     });
   stringRenderer_.setPreprocessCallback(
     [&](FT_Glyph* ptr)
@@ -114,7 +155,7 @@ GlyphContinuous::paintByRenderer(QPainter* painter)
   stringRenderer_.setLineBeginCallback(
     [&](FT_Vector pos, double size)
     {
-      beginLine(painter, pos, size);
+      beginSaveLine(pos, size);
     });
   displayingCount_ = stringRenderer_.render(width(), height(), beginIndex_);
 }
@@ -168,6 +209,31 @@ GlyphContinuous::transformGlyphStroked(FT_Glyph glyph)
 }
 
 
+void
+GlyphContinuous::paintCache(QPainter* painter)
+{
+  if (stringRenderer_.isWaterfall())
+    positionDelta_.setY(0);
+  for (auto& line : glyphCache_)
+  {
+    beginDrawCacheLine(painter, line);
+    for (auto& glyph : line.entries)
+    {
+      drawCacheGlyph(painter, glyph);
+    }
+  }
+}
+
+
+void
+GlyphContinuous::fillCache()
+{
+  prePaint();
+  paintByRenderer();
+  emit displayingCountUpdated(displayingCount_);
+}
+
+
 void
 GlyphContinuous::prePaint()
 {
@@ -204,6 +270,15 @@ GlyphContinuous::prePaint()
 
   emboldeningX_ = (FT_Pos)(metrics_.y_ppem * 64 * boldX_);
   emboldeningY_ = (FT_Pos)(metrics_.y_ppem * 64 * boldY_);
+
+  if (mode_ == M_Stroked)
+  {
+    auto radius = static_cast<FT_Fixed>(metrics_.y_ppem * 64 * strokeRadius_);
+    FT_Stroker_Set(stroker_, radius,
+                   FT_STROKER_LINECAP_ROUND,
+                   FT_STROKER_LINEJOIN_ROUND,
+                   0);
+  }
 }
 
 
@@ -240,9 +315,65 @@ GlyphContinuous::preprocessGlyph(FT_Glyph* glyphPtr)
 
 
 void
-GlyphContinuous::beginLine(QPainter* painter,
-                           FT_Vector pos,
-                           double sizePoint)
+GlyphContinuous::beginSaveLine(FT_Vector pos,
+                               double sizePoint)
+{
+  glyphCache_.emplace_back();
+  currentWritingLine_ = &glyphCache_.back();
+  currentWritingLine_->sizePoint = sizePoint;
+  currentWritingLine_->basePosition = { static_cast<int>(pos.x),
+                                        static_cast<int>(pos.y) };
+}
+
+
+void
+GlyphContinuous::saveSingleGlyph(FT_Glyph glyph,
+                                 FT_Vector penPos,
+                                 GlyphContext gctx)
+{
+  if (!currentWritingLine_)
+    return;
+
+  currentWritingLine_->entries.push_back(std::move(GlyphCacheEntry{}));
+  auto& entry = currentWritingLine_->entries.back();
+
+  QRect rect;
+  QImage* image = engine_->convertGlyphToQImage(glyph, &rect, false);
+  rect.setTop(height() - rect.top()); // TODO Don't place this here...
+
+  entry.image = image;
+  entry.basePosition = rect;
+  entry.charCode = gctx.charCode;
+  entry.glyphIndex = gctx.glyphIndex;
+  entry.advance = glyph->advance;
+  entry.penPos = { penPos.x, penPos.y };
+
+  // todo more info
+}
+
+
+void
+GlyphContinuous::saveSingleGlyphImage(QImage* image,
+                                      QRect pos,
+                                      GlyphContext gctx)
+{
+  if (!currentWritingLine_)
+    return;
+
+  currentWritingLine_->entries.push_back(std::move(GlyphCacheEntry{}));
+  auto& entry = currentWritingLine_->entries.back();
+
+  entry.image = image;
+  entry.basePosition = pos;
+  entry.charCode = gctx.charCode;
+  entry.glyphIndex = gctx.glyphIndex;
+  entry.hasAdvance = false;
+}
+
+
+void
+GlyphContinuous::beginDrawCacheLine(QPainter* painter,
+                                    const GlyphCacheLine& line)
 {
   // Now only used by waterfall mode to draw a size indicator.
   if (!stringRenderer_.isWaterfall())
@@ -252,54 +383,38 @@ GlyphContinuous::beginLine(QPainter* painter,
   }
 
   auto oldFont = painter->font();
-  oldFont.setPointSizeF(sizePoint);
+  oldFont.setPointSizeF(line.sizePoint);
   painter->setFont(oldFont);
   auto metrics = painter->fontMetrics();
 
-  auto sizePrefix = QString("%1: ").arg(sizePoint);
-  QPoint posQ = { static_cast<int>(pos.x),
-                  static_cast<int>(pos.y) };
-  painter->drawText(posQ, sizePrefix);
+  auto sizePrefix = QString("%1: ").arg(line.sizePoint);
+  painter->drawText(line.basePosition, sizePrefix);
 
   sizeIndicatorOffset_ = metrics.horizontalAdvance(sizePrefix);
 }
 
 
 void
-GlyphContinuous::drawSingleGlyph(QPainter* painter, 
-                                 FT_Glyph glyph,
-                                 FT_Vector penPos)
+GlyphContinuous::drawCacheGlyph(QPainter* painter,
+                                const GlyphCacheEntry& entry)
 {
   // ftview.c:557
-  int width = glyph->advance.x ? glyph->advance.x >> 16
-                               : metrics_.y_ppem / 2;
+  // Well, metrics is also part of the cache...
+  int width = entry.advance.x ? entry.advance.x >> 16
+                              : metrics_.y_ppem / 2;
 
-  if (glyph->advance.x == 0 && !stringRenderer_.isWaterfall())
+  if (entry.advance.x == 0 && !stringRenderer_.isWaterfall())
   {
     // Draw a red square to indicate
-    painter->fillRect(penPos.x, penPos.y - width, width, width, Qt::red);
+    auto squarePoint = entry.penPos;
+    squarePoint.setY(squarePoint.y() - width);
+    painter->fillRect(QRect(squarePoint, QSize(width, width)), Qt::red);
   }
 
-  QRect rect;
-  QImage* image = engine_->convertGlyphToQImage(glyph, &rect, false);
-  rect.setTop(height() - rect.top()); // TODO Don't place this here...
+  QRect rect = entry.basePosition;
   rect.setLeft(rect.left() + sizeIndicatorOffset_);
 
-  painter->drawImage(rect.topLeft(), *image);
-  delete image;
-}
-
-
-void
-GlyphContinuous::drawSingleGlyphImage(QPainter* painter,
-                                      QImage* image,
-                                      QRect pos)
-{
-  // TODO red square?
-
-  pos.setLeft(pos.left() + sizeIndicatorOffset_);
-  painter->drawImage(pos, *image);
-  delete image;
+  painter->drawImage(rect.topLeft(), *entry.image);
 }
 
 
diff --git a/src/ftinspect/rendering/glyphcontinuous.hpp 
b/src/ftinspect/rendering/glyphcontinuous.hpp
index 109d022..5362a0c 100644
--- a/src/ftinspect/rendering/glyphcontinuous.hpp
+++ b/src/ftinspect/rendering/glyphcontinuous.hpp
@@ -8,8 +8,10 @@
 #include "../engine/stringrenderer.hpp"
 
 #include <utility>
+#include <vector>
 
 #include <QWidget>
+#include <QImage>
 
 #include <freetype/freetype.h>
 #include <freetype/ftglyph.h>
@@ -17,6 +19,34 @@
 #include <freetype/ftstroke.h>
 
 
+struct GlyphCacheEntry
+{
+  QImage* image = NULL;
+  QRect basePosition = {};
+  QPoint penPos = {};
+  int charCode = -1;
+  int glyphIndex = -1;
+
+  FT_Vector advance = {};
+  bool hasAdvance = false;
+
+  GlyphCacheEntry() {}
+  ~GlyphCacheEntry();
+  GlyphCacheEntry(const GlyphCacheEntry& other) = delete;
+  GlyphCacheEntry& operator=(const GlyphCacheEntry& other) = delete;
+  GlyphCacheEntry(GlyphCacheEntry&& other) noexcept;
+  GlyphCacheEntry& operator=(GlyphCacheEntry&& other) noexcept;
+};
+
+
+struct GlyphCacheLine
+{
+  QPoint basePosition = {};
+  double sizePoint = 0.0;
+  std::vector<GlyphCacheEntry> entries;
+};
+
+
 class Engine;
 class GlyphContinuous
 : public QWidget
@@ -56,6 +86,8 @@ public:
   void setStrokeRadius(double radius) { strokeRadius_ = radius; }
   void setSourceText(QString text);
 
+  void purgeCache();
+
 signals:
   void wheelNavigate(int steps);
   void wheelResize(int steps);
@@ -64,6 +96,7 @@ signals:
 protected:
   void paintEvent(QPaintEvent* event) override;
   void wheelEvent(QWheelEvent* event) override;
+  void resizeEvent(QResizeEvent* event) override;
 
 private:
   Engine* engine_;
@@ -86,7 +119,12 @@ private:
 
   FT_Stroker stroker_;
 
-  void paintByRenderer(QPainter* painter);
+  std::vector<GlyphCacheLine> glyphCache_;
+  GlyphCacheLine* currentWritingLine_ = NULL;
+
+  QPoint positionDelta_;
+
+  void paintByRenderer();
 
   // These two are used indendpent of current glyph variables
   // and assumes ownership of glyphs, but don't free them.
@@ -94,18 +132,23 @@ private:
   void transformGlyphFancy(FT_Glyph glyph);
   FT_Glyph transformGlyphStroked(FT_Glyph glyph);
 
+  void paintCache(QPainter* painter);
+  void fillCache();
   void prePaint();
   void updateRendererText();
   void preprocessGlyph(FT_Glyph* glyphPtr);
-  void beginLine(QPainter* painter,
-                 FT_Vector pos, 
-                 double sizePoint);
-  void drawSingleGlyph(QPainter* painter,
-                       FT_Glyph glyph,
-                       FT_Vector penPos);
-  void drawSingleGlyphImage(QPainter* painter,
-                            QImage* image,
-                            QRect pos);
+  void beginSaveLine(FT_Vector pos,
+                     double sizePoint);
+  void saveSingleGlyph(FT_Glyph glyph,
+                       FT_Vector penPos,
+                       GlyphContext gctx);
+  void saveSingleGlyphImage(QImage* image,
+                            QRect pos,
+                            GlyphContext gctx);
+  void beginDrawCacheLine(QPainter* painter,
+                          const GlyphCacheLine& line);
+  void drawCacheGlyph(QPainter* painter,
+                      const GlyphCacheEntry& entry);
 };
 
 



reply via email to

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