Charlie Jiang pushed to branch gsoc-2022-chariri-3 at FreeType / FreeType Demo Programs
Commits:
-
794b613d
by Charlie Jiang at 2022-07-27T18:32:42+08:00
5 changed files:
- src/ftinspect/engine/stringrenderer.cpp
- src/ftinspect/engine/stringrenderer.hpp
- src/ftinspect/panels/continuous.cpp
- src/ftinspect/rendering/glyphcontinuous.cpp
- src/ftinspect/rendering/glyphcontinuous.hpp
Changes:
... | ... | @@ -510,7 +510,7 @@ StringRenderer::renderLine(int x, |
510 | 510 | {
|
511 | 511 | rect.setX(rect.x() + (pen.x >> 6));
|
512 | 512 | rect.setY(height - rect.y() - (pen.y >> 6));
|
513 | - renderImageCallback_(colorLayerImage, rect);
|
|
513 | + renderImageCallback_(colorLayerImage, rect, ctx);
|
|
514 | 514 | }
|
515 | 515 | else
|
516 | 516 | {
|
... | ... | @@ -569,7 +569,7 @@ StringRenderer::renderLine(int x, |
569 | 569 | && bbox.yMin <= height)
|
570 | 570 | {
|
571 | 571 | FT_Vector penPos = { (pen.x >> 6), height - (pen.y >> 6) };
|
572 | - renderCallback_(image, penPos);
|
|
572 | + renderCallback_(image, penPos, ctx);
|
|
573 | 573 | }
|
574 | 574 | |
575 | 575 | FT_Done_Glyph(image);
|
... | ... | @@ -61,13 +61,17 @@ public: |
61 | 61 | * contains no points, and thus can't be translated to the desired pen
|
62 | 62 | * position.
|
63 | 63 | */
|
64 | - using RenderCallback = std::function<void(FT_Glyph, FT_Vector)>;
|
|
64 | + using RenderCallback = std::function<void(FT_Glyph,
|
|
65 | + FT_Vector,
|
|
66 | + GlyphContext&)>;
|
|
65 | 67 | /*
|
66 | 68 | * For color layered fonts, this will direct render the QImage for you.
|
67 | 69 | * TODO: Remove `RenderCallback` and do QImage creation in this class?
|
68 | 70 | * The receiver is responsible for deleteing the QImage.
|
69 | 71 | */
|
70 | - using RenderImageCallback = std::function<void(QImage*, QRect)>;
|
|
72 | + using RenderImageCallback = std::function<void(QImage*,
|
|
73 | + QRect,
|
|
74 | + GlyphContext&)>;
|
|
71 | 75 | /*
|
72 | 76 | * The glyph pointer may be replaced. In that case, ownership is transfered
|
73 | 77 | * to the renderer, and the new glyph will be eventually freed by
|
... | ... | @@ -30,6 +30,7 @@ ContinuousTab::repaintGlyph() |
30 | 30 | sizeSelector_->applyToEngine(engine_);
|
31 | 31 |
|
32 | 32 | syncSettings();
|
33 | + canvas_->purgeCache();
|
|
33 | 34 | canvas_->repaint();
|
34 | 35 | }
|
35 | 36 | |
... | ... | @@ -41,6 +42,7 @@ ContinuousTab::reloadFont() |
41 | 42 | setGlyphCount(qBound(0, currentGlyphCount_, INT_MAX));
|
42 | 43 | setCharMaps(engine_->currentFontCharMaps());
|
43 | 44 | canvas_->stringRenderer().reloadAll();
|
45 | + canvas_->purgeCache();
|
|
44 | 46 | repaintGlyph();
|
45 | 47 | }
|
46 | 48 |
... | ... | @@ -12,6 +12,37 @@ |
12 | 12 | #include <freetype/ftbitmap.h>
|
13 | 13 | |
14 | 14 | |
15 | +GlyphCacheEntry::~GlyphCacheEntry()
|
|
16 | +{
|
|
17 | + delete image;
|
|
18 | +}
|
|
19 | + |
|
20 | + |
|
21 | +GlyphCacheEntry::GlyphCacheEntry(GlyphCacheEntry&& other) noexcept
|
|
22 | +{
|
|
23 | + *this = std::move(other);
|
|
24 | +}
|
|
25 | + |
|
26 | + |
|
27 | +GlyphCacheEntry&
|
|
28 | +GlyphCacheEntry::operator=(GlyphCacheEntry&& other) noexcept
|
|
29 | +{
|
|
30 | + if (this == &other)
|
|
31 | + return *this;
|
|
32 | + |
|
33 | + auto oldImage = image;
|
|
34 | + image = other.image;
|
|
35 | + basePosition = other.basePosition;
|
|
36 | + penPos = other.penPos;
|
|
37 | + charCode = other.charCode;
|
|
38 | + glyphIndex = other.glyphIndex;
|
|
39 | + advance = other.advance;
|
|
40 | + hasAdvance = other.hasAdvance;
|
|
41 | + other.image = oldImage;
|
|
42 | + return *this;
|
|
43 | +}
|
|
44 | + |
|
45 | + |
|
15 | 46 | GlyphContinuous::GlyphContinuous(QWidget* parent, Engine* engine)
|
16 | 47 | : QWidget(parent),
|
17 | 48 | engine_(engine),
|
... | ... | @@ -38,9 +69,11 @@ GlyphContinuous::setSource(Source source) |
38 | 69 | {
|
39 | 70 | case SRC_AllGlyphs:
|
40 | 71 | stringRenderer_.setUseAllGlyphs();
|
72 | + positionDelta_ = {};
|
|
41 | 73 | break;
|
42 | 74 | |
43 | 75 | case SRC_TextStringRepeated:
|
76 | + positionDelta_ = {};
|
|
44 | 77 | case SRC_TextString:
|
45 | 78 | updateRendererText();
|
46 | 79 | break;
|
... | ... | @@ -56,6 +89,14 @@ GlyphContinuous::setSourceText(QString text) |
56 | 89 | }
|
57 | 90 | |
58 | 91 | |
92 | +void
|
|
93 | +GlyphContinuous::purgeCache()
|
|
94 | +{
|
|
95 | + glyphCache_.clear();
|
|
96 | + currentWritingLine_ = NULL;
|
|
97 | +}
|
|
98 | + |
|
99 | + |
|
59 | 100 | void
|
60 | 101 | GlyphContinuous::paintEvent(QPaintEvent* event)
|
61 | 102 | {
|
... | ... | @@ -63,10 +104,9 @@ GlyphContinuous::paintEvent(QPaintEvent* event) |
63 | 104 | painter.begin(this);
|
64 | 105 | painter.fillRect(rect(), Qt::white);
|
65 | 106 | |
66 | - prePaint();
|
|
67 | - |
|
68 | - paintByRenderer(&painter);
|
|
69 | - emit displayingCountUpdated(displayingCount_);
|
|
107 | + if (glyphCache_.empty())
|
|
108 | + fillCache();
|
|
109 | + paintCache(&painter);
|
|
70 | 110 | |
71 | 111 | painter.end();
|
72 | 112 | }
|
... | ... | @@ -84,27 +124,28 @@ GlyphContinuous::wheelEvent(QWheelEvent* event) |
84 | 124 | |
85 | 125 | |
86 | 126 | void
|
87 | -GlyphContinuous::paintByRenderer(QPainter* painter)
|
|
127 | +GlyphContinuous::resizeEvent(QResizeEvent* event)
|
|
88 | 128 | {
|
89 | - if (mode_ == M_Stroked)
|
|
90 | - {
|
|
91 | - auto radius = static_cast<FT_Fixed>(metrics_.y_ppem * 64 * strokeRadius_);
|
|
92 | - FT_Stroker_Set(stroker_, radius,
|
|
93 | - FT_STROKER_LINECAP_ROUND,
|
|
94 | - FT_STROKER_LINEJOIN_ROUND,
|
|
95 | - 0);
|
|
96 | - }
|
|
129 | + purgeCache();
|
|
130 | + QWidget::resizeEvent(event);
|
|
131 | +}
|
|
132 | + |
|
133 | + |
|
134 | +void
|
|
135 | +GlyphContinuous::paintByRenderer()
|
|
136 | +{
|
|
137 | + purgeCache();
|
|
97 | 138 | |
98 | 139 | stringRenderer_.setRepeated(source_ == SRC_TextStringRepeated);
|
99 | 140 | stringRenderer_.setCallback(
|
100 | - [&](FT_Glyph glyph, FT_Vector penPos)
|
|
141 | + [&](FT_Glyph glyph, FT_Vector penPos, GlyphContext& ctx)
|
|
101 | 142 | {
|
102 | - drawSingleGlyph(painter, glyph, penPos);
|
|
143 | + saveSingleGlyph(glyph, penPos, ctx);
|
|
103 | 144 | });
|
104 | 145 | stringRenderer_.setImageCallback(
|
105 | - [&](QImage* image, QRect pos)
|
|
146 | + [&](QImage* image, QRect pos, GlyphContext& ctx)
|
|
106 | 147 | {
|
107 | - drawSingleGlyphImage(painter, image, pos);
|
|
148 | + saveSingleGlyphImage(image, pos, ctx);
|
|
108 | 149 | });
|
109 | 150 | stringRenderer_.setPreprocessCallback(
|
110 | 151 | [&](FT_Glyph* ptr)
|
... | ... | @@ -114,7 +155,7 @@ GlyphContinuous::paintByRenderer(QPainter* painter) |
114 | 155 | stringRenderer_.setLineBeginCallback(
|
115 | 156 | [&](FT_Vector pos, double size)
|
116 | 157 | {
|
117 | - beginLine(painter, pos, size);
|
|
158 | + beginSaveLine(pos, size);
|
|
118 | 159 | });
|
119 | 160 | displayingCount_ = stringRenderer_.render(width(), height(), beginIndex_);
|
120 | 161 | }
|
... | ... | @@ -168,6 +209,31 @@ GlyphContinuous::transformGlyphStroked(FT_Glyph glyph) |
168 | 209 | }
|
169 | 210 | |
170 | 211 | |
212 | +void
|
|
213 | +GlyphContinuous::paintCache(QPainter* painter)
|
|
214 | +{
|
|
215 | + if (stringRenderer_.isWaterfall())
|
|
216 | + positionDelta_.setY(0);
|
|
217 | + for (auto& line : glyphCache_)
|
|
218 | + {
|
|
219 | + beginDrawCacheLine(painter, line);
|
|
220 | + for (auto& glyph : line.entries)
|
|
221 | + {
|
|
222 | + drawCacheGlyph(painter, glyph);
|
|
223 | + }
|
|
224 | + }
|
|
225 | +}
|
|
226 | + |
|
227 | + |
|
228 | +void
|
|
229 | +GlyphContinuous::fillCache()
|
|
230 | +{
|
|
231 | + prePaint();
|
|
232 | + paintByRenderer();
|
|
233 | + emit displayingCountUpdated(displayingCount_);
|
|
234 | +}
|
|
235 | + |
|
236 | + |
|
171 | 237 | void
|
172 | 238 | GlyphContinuous::prePaint()
|
173 | 239 | {
|
... | ... | @@ -204,6 +270,15 @@ GlyphContinuous::prePaint() |
204 | 270 | |
205 | 271 | emboldeningX_ = (FT_Pos)(metrics_.y_ppem * 64 * boldX_);
|
206 | 272 | emboldeningY_ = (FT_Pos)(metrics_.y_ppem * 64 * boldY_);
|
273 | + |
|
274 | + if (mode_ == M_Stroked)
|
|
275 | + {
|
|
276 | + auto radius = static_cast<FT_Fixed>(metrics_.y_ppem * 64 * strokeRadius_);
|
|
277 | + FT_Stroker_Set(stroker_, radius,
|
|
278 | + FT_STROKER_LINECAP_ROUND,
|
|
279 | + FT_STROKER_LINEJOIN_ROUND,
|
|
280 | + 0);
|
|
281 | + }
|
|
207 | 282 | }
|
208 | 283 | |
209 | 284 | |
... | ... | @@ -240,9 +315,65 @@ GlyphContinuous::preprocessGlyph(FT_Glyph* glyphPtr) |
240 | 315 | |
241 | 316 | |
242 | 317 | void
|
243 | -GlyphContinuous::beginLine(QPainter* painter,
|
|
244 | - FT_Vector pos,
|
|
245 | - double sizePoint)
|
|
318 | +GlyphContinuous::beginSaveLine(FT_Vector pos,
|
|
319 | + double sizePoint)
|
|
320 | +{
|
|
321 | + glyphCache_.emplace_back();
|
|
322 | + currentWritingLine_ = &glyphCache_.back();
|
|
323 | + currentWritingLine_->sizePoint = sizePoint;
|
|
324 | + currentWritingLine_->basePosition = { static_cast<int>(pos.x),
|
|
325 | + static_cast<int>(pos.y) };
|
|
326 | +}
|
|
327 | + |
|
328 | + |
|
329 | +void
|
|
330 | +GlyphContinuous::saveSingleGlyph(FT_Glyph glyph,
|
|
331 | + FT_Vector penPos,
|
|
332 | + GlyphContext gctx)
|
|
333 | +{
|
|
334 | + if (!currentWritingLine_)
|
|
335 | + return;
|
|
336 | + |
|
337 | + currentWritingLine_->entries.push_back(std::move(GlyphCacheEntry{}));
|
|
338 | + auto& entry = currentWritingLine_->entries.back();
|
|
339 | + |
|
340 | + QRect rect;
|
|
341 | + QImage* image = engine_->convertGlyphToQImage(glyph, &rect, false);
|
|
342 | + rect.setTop(height() - rect.top()); // TODO Don't place this here...
|
|
343 | + |
|
344 | + entry.image = image;
|
|
345 | + entry.basePosition = rect;
|
|
346 | + entry.charCode = gctx.charCode;
|
|
347 | + entry.glyphIndex = gctx.glyphIndex;
|
|
348 | + entry.advance = glyph->advance;
|
|
349 | + entry.penPos = { penPos.x, penPos.y };
|
|
350 | + |
|
351 | + // todo more info
|
|
352 | +}
|
|
353 | + |
|
354 | + |
|
355 | +void
|
|
356 | +GlyphContinuous::saveSingleGlyphImage(QImage* image,
|
|
357 | + QRect pos,
|
|
358 | + GlyphContext gctx)
|
|
359 | +{
|
|
360 | + if (!currentWritingLine_)
|
|
361 | + return;
|
|
362 | + |
|
363 | + currentWritingLine_->entries.push_back(std::move(GlyphCacheEntry{}));
|
|
364 | + auto& entry = currentWritingLine_->entries.back();
|
|
365 | + |
|
366 | + entry.image = image;
|
|
367 | + entry.basePosition = pos;
|
|
368 | + entry.charCode = gctx.charCode;
|
|
369 | + entry.glyphIndex = gctx.glyphIndex;
|
|
370 | + entry.hasAdvance = false;
|
|
371 | +}
|
|
372 | + |
|
373 | + |
|
374 | +void
|
|
375 | +GlyphContinuous::beginDrawCacheLine(QPainter* painter,
|
|
376 | + const GlyphCacheLine& line)
|
|
246 | 377 | {
|
247 | 378 | // Now only used by waterfall mode to draw a size indicator.
|
248 | 379 | if (!stringRenderer_.isWaterfall())
|
... | ... | @@ -252,54 +383,38 @@ GlyphContinuous::beginLine(QPainter* painter, |
252 | 383 | }
|
253 | 384 | |
254 | 385 | auto oldFont = painter->font();
|
255 | - oldFont.setPointSizeF(sizePoint);
|
|
386 | + oldFont.setPointSizeF(line.sizePoint);
|
|
256 | 387 | painter->setFont(oldFont);
|
257 | 388 | auto metrics = painter->fontMetrics();
|
258 | 389 | |
259 | - auto sizePrefix = QString("%1: ").arg(sizePoint);
|
|
260 | - QPoint posQ = { static_cast<int>(pos.x),
|
|
261 | - static_cast<int>(pos.y) };
|
|
262 | - painter->drawText(posQ, sizePrefix);
|
|
390 | + auto sizePrefix = QString("%1: ").arg(line.sizePoint);
|
|
391 | + painter->drawText(line.basePosition, sizePrefix);
|
|
263 | 392 | |
264 | 393 | sizeIndicatorOffset_ = metrics.horizontalAdvance(sizePrefix);
|
265 | 394 | }
|
266 | 395 | |
267 | 396 | |
268 | 397 | void
|
269 | -GlyphContinuous::drawSingleGlyph(QPainter* painter,
|
|
270 | - FT_Glyph glyph,
|
|
271 | - FT_Vector penPos)
|
|
398 | +GlyphContinuous::drawCacheGlyph(QPainter* painter,
|
|
399 | + const GlyphCacheEntry& entry)
|
|
272 | 400 | {
|
273 | 401 | // ftview.c:557
|
274 | - int width = glyph->advance.x ? glyph->advance.x >> 16
|
|
275 | - : metrics_.y_ppem / 2;
|
|
402 | + // Well, metrics is also part of the cache...
|
|
403 | + int width = entry.advance.x ? entry.advance.x >> 16
|
|
404 | + : metrics_.y_ppem / 2;
|
|
276 | 405 | |
277 | - if (glyph->advance.x == 0 && !stringRenderer_.isWaterfall())
|
|
406 | + if (entry.advance.x == 0 && !stringRenderer_.isWaterfall())
|
|
278 | 407 | {
|
279 | 408 | // Draw a red square to indicate
|
280 | - painter->fillRect(penPos.x, penPos.y - width, width, width, Qt::red);
|
|
409 | + auto squarePoint = entry.penPos;
|
|
410 | + squarePoint.setY(squarePoint.y() - width);
|
|
411 | + painter->fillRect(QRect(squarePoint, QSize(width, width)), Qt::red);
|
|
281 | 412 | }
|
282 | 413 | |
283 | - QRect rect;
|
|
284 | - QImage* image = engine_->convertGlyphToQImage(glyph, &rect, false);
|
|
285 | - rect.setTop(height() - rect.top()); // TODO Don't place this here...
|
|
414 | + QRect rect = entry.basePosition;
|
|
286 | 415 | rect.setLeft(rect.left() + sizeIndicatorOffset_);
|
287 | 416 | |
288 | - painter->drawImage(rect.topLeft(), *image);
|
|
289 | - delete image;
|
|
290 | -}
|
|
291 | - |
|
292 | - |
|
293 | -void
|
|
294 | -GlyphContinuous::drawSingleGlyphImage(QPainter* painter,
|
|
295 | - QImage* image,
|
|
296 | - QRect pos)
|
|
297 | -{
|
|
298 | - // TODO red square?
|
|
299 | - |
|
300 | - pos.setLeft(pos.left() + sizeIndicatorOffset_);
|
|
301 | - painter->drawImage(pos, *image);
|
|
302 | - delete image;
|
|
417 | + painter->drawImage(rect.topLeft(), *entry.image);
|
|
303 | 418 | }
|
304 | 419 | |
305 | 420 |
... | ... | @@ -8,8 +8,10 @@ |
8 | 8 | #include "../engine/stringrenderer.hpp"
|
9 | 9 | |
10 | 10 | #include <utility>
|
11 | +#include <vector>
|
|
11 | 12 | |
12 | 13 | #include <QWidget>
|
14 | +#include <QImage>
|
|
13 | 15 | |
14 | 16 | #include <freetype/freetype.h>
|
15 | 17 | #include <freetype/ftglyph.h>
|
... | ... | @@ -17,6 +19,34 @@ |
17 | 19 | #include <freetype/ftstroke.h>
|
18 | 20 | |
19 | 21 | |
22 | +struct GlyphCacheEntry
|
|
23 | +{
|
|
24 | + QImage* image = NULL;
|
|
25 | + QRect basePosition = {};
|
|
26 | + QPoint penPos = {};
|
|
27 | + int charCode = -1;
|
|
28 | + int glyphIndex = -1;
|
|
29 | + |
|
30 | + FT_Vector advance = {};
|
|
31 | + bool hasAdvance = false;
|
|
32 | + |
|
33 | + GlyphCacheEntry() {}
|
|
34 | + ~GlyphCacheEntry();
|
|
35 | + GlyphCacheEntry(const GlyphCacheEntry& other) = delete;
|
|
36 | + GlyphCacheEntry& operator=(const GlyphCacheEntry& other) = delete;
|
|
37 | + GlyphCacheEntry(GlyphCacheEntry&& other) noexcept;
|
|
38 | + GlyphCacheEntry& operator=(GlyphCacheEntry&& other) noexcept;
|
|
39 | +};
|
|
40 | + |
|
41 | + |
|
42 | +struct GlyphCacheLine
|
|
43 | +{
|
|
44 | + QPoint basePosition = {};
|
|
45 | + double sizePoint = 0.0;
|
|
46 | + std::vector<GlyphCacheEntry> entries;
|
|
47 | +};
|
|
48 | + |
|
49 | + |
|
20 | 50 | class Engine;
|
21 | 51 | class GlyphContinuous
|
22 | 52 | : public QWidget
|
... | ... | @@ -56,6 +86,8 @@ public: |
56 | 86 | void setStrokeRadius(double radius) { strokeRadius_ = radius; }
|
57 | 87 | void setSourceText(QString text);
|
58 | 88 | |
89 | + void purgeCache();
|
|
90 | + |
|
59 | 91 | signals:
|
60 | 92 | void wheelNavigate(int steps);
|
61 | 93 | void wheelResize(int steps);
|
... | ... | @@ -64,6 +96,7 @@ signals: |
64 | 96 | protected:
|
65 | 97 | void paintEvent(QPaintEvent* event) override;
|
66 | 98 | void wheelEvent(QWheelEvent* event) override;
|
99 | + void resizeEvent(QResizeEvent* event) override;
|
|
67 | 100 | |
68 | 101 | private:
|
69 | 102 | Engine* engine_;
|
... | ... | @@ -86,7 +119,12 @@ private: |
86 | 119 | |
87 | 120 | FT_Stroker stroker_;
|
88 | 121 | |
89 | - void paintByRenderer(QPainter* painter);
|
|
122 | + std::vector<GlyphCacheLine> glyphCache_;
|
|
123 | + GlyphCacheLine* currentWritingLine_ = NULL;
|
|
124 | + |
|
125 | + QPoint positionDelta_;
|
|
126 | + |
|
127 | + void paintByRenderer();
|
|
90 | 128 | |
91 | 129 | // These two are used indendpent of current glyph variables
|
92 | 130 | // and assumes ownership of glyphs, but don't free them.
|
... | ... | @@ -94,18 +132,23 @@ private: |
94 | 132 | void transformGlyphFancy(FT_Glyph glyph);
|
95 | 133 | FT_Glyph transformGlyphStroked(FT_Glyph glyph);
|
96 | 134 | |
135 | + void paintCache(QPainter* painter);
|
|
136 | + void fillCache();
|
|
97 | 137 | void prePaint();
|
98 | 138 | void updateRendererText();
|
99 | 139 | void preprocessGlyph(FT_Glyph* glyphPtr);
|
100 | - void beginLine(QPainter* painter,
|
|
101 | - FT_Vector pos,
|
|
102 | - double sizePoint);
|
|
103 | - void drawSingleGlyph(QPainter* painter,
|
|
104 | - FT_Glyph glyph,
|
|
105 | - FT_Vector penPos);
|
|
106 | - void drawSingleGlyphImage(QPainter* painter,
|
|
107 | - QImage* image,
|
|
108 | - QRect pos);
|
|
140 | + void beginSaveLine(FT_Vector pos,
|
|
141 | + double sizePoint);
|
|
142 | + void saveSingleGlyph(FT_Glyph glyph,
|
|
143 | + FT_Vector penPos,
|
|
144 | + GlyphContext gctx);
|
|
145 | + void saveSingleGlyphImage(QImage* image,
|
|
146 | + QRect pos,
|
|
147 | + GlyphContext gctx);
|
|
148 | + void beginDrawCacheLine(QPainter* painter,
|
|
149 | + const GlyphCacheLine& line);
|
|
150 | + void drawCacheGlyph(QPainter* painter,
|
|
151 | + const GlyphCacheEntry& entry);
|
|
109 | 152 | };
|
110 | 153 | |
111 | 154 |