[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);
};
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [freetype2-demos] mpsuzuki-gsoc-2022-chariri-3 794b613 1/5: [ftinspect] Add caching of `QImage` and dimension info in Continuous View.,
Werner Lemberg <=