Commits:
-
f2d927ec
by Charlie Jiang
at 2022-07-11T16:35:29+08:00
[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.
-
274212df
by Charlie Jiang
at 2022-07-11T17:10:31+08:00
[ftinspect] Support Stroked SubMode in "Continuous Mode"
* src/ftinspect/panels/continuous.cpp, src/ftinspect/panels/continuous.hpp:
GUI update.
* src/ftinspect/rendering/glyphcontinuous.cpp,
src/ftinspect/rendering/glyphcontinuous.hpp: Add support.
7 changed files:
Changes:
src/ftinspect/panels/continuous.cpp
... |
... |
@@ -130,6 +130,11 @@ ContinuousTab::updateFromCurrentSubTab() |
130
|
130
|
canvas_->setBeginIndex(allGlyphsTab_->glyphBeginindex());
|
131
|
131
|
canvas_->setLimitIndex(allGlyphsTab_->glyphLimitIndex());
|
132
|
132
|
canvas_->setCharMapIndex(allGlyphsTab_->charMapIndex());
|
|
133
|
+
|
|
134
|
+ canvas_->setFancyParams(allGlyphsTab_->xEmboldening(),
|
|
135
|
+ allGlyphsTab_->yEmboldening(),
|
|
136
|
+ allGlyphsTab_->slanting());
|
|
137
|
+ canvas_->setStrokeRadius(allGlyphsTab_->strokeRadius());
|
133
|
138
|
break;
|
134
|
139
|
}
|
135
|
140
|
}
|
... |
... |
@@ -143,6 +148,8 @@ ContinousAllGlyphsTab::ContinousAllGlyphsTab(QWidget* parent) |
143
|
148
|
QVector<CharMapInfo> tempCharMaps;
|
144
|
149
|
setCharMaps(tempCharMaps); // pass in an empty one
|
145
|
150
|
|
|
151
|
+ checkSubMode();
|
|
152
|
+ setDefaults();
|
146
|
153
|
createConnections();
|
147
|
154
|
}
|
148
|
155
|
|
... |
... |
@@ -169,6 +176,34 @@ ContinousAllGlyphsTab::subMode() |
169
|
176
|
}
|
170
|
177
|
|
171
|
178
|
|
|
179
|
+double
|
|
180
|
+ContinousAllGlyphsTab::xEmboldening()
|
|
181
|
+{
|
|
182
|
+ return xEmboldeningSpinBox_->value();
|
|
183
|
+}
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+double
|
|
187
|
+ContinousAllGlyphsTab::yEmboldening()
|
|
188
|
+{
|
|
189
|
+ return yEmboldeningSpinBox_->value();
|
|
190
|
+}
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+double
|
|
194
|
+ContinousAllGlyphsTab::slanting()
|
|
195
|
+{
|
|
196
|
+ return slantSpinBox_->value();
|
|
197
|
+}
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+double
|
|
201
|
+ContinousAllGlyphsTab::strokeRadius()
|
|
202
|
+{
|
|
203
|
+ return strokeRadiusSpinBox_->value();
|
|
204
|
+}
|
|
205
|
+
|
|
206
|
+
|
172
|
207
|
int
|
173
|
208
|
ContinousAllGlyphsTab::charMapIndex()
|
174
|
209
|
{
|
... |
... |
@@ -263,6 +298,20 @@ ContinousAllGlyphsTab::updateLimitIndex() |
263
|
298
|
}
|
264
|
299
|
|
265
|
300
|
|
|
301
|
+void
|
|
302
|
+ContinousAllGlyphsTab::checkSubMode()
|
|
303
|
+{
|
|
304
|
+ auto isFancy = subMode() == GlyphContinuous::AG_Fancy;
|
|
305
|
+ auto isStroked = subMode() == GlyphContinuous::AG_Stroked;
|
|
306
|
+ xEmboldeningSpinBox_->setEnabled(isFancy);
|
|
307
|
+ yEmboldeningSpinBox_->setEnabled(isFancy);
|
|
308
|
+ slantSpinBox_->setEnabled(isFancy);
|
|
309
|
+ strokeRadiusSpinBox_->setEnabled(isStroked);
|
|
310
|
+
|
|
311
|
+ emit changed();
|
|
312
|
+}
|
|
313
|
+
|
|
314
|
+
|
266
|
315
|
void
|
267
|
316
|
ContinousAllGlyphsTab::createLayout()
|
268
|
317
|
{
|
... |
... |
@@ -276,13 +325,36 @@ ContinousAllGlyphsTab::createLayout() |
276
|
325
|
|
277
|
326
|
// Note: in sync with the enum!!
|
278
|
327
|
modeSelector_->insertItem(GlyphContinuous::AG_AllGlyphs, tr("All Glyphs"));
|
279
|
|
- modeSelector_->insertItem(GlyphContinuous::AG_Fancy, tr("Fancy"));
|
|
328
|
+ modeSelector_->insertItem(GlyphContinuous::AG_Fancy,
|
|
329
|
+ tr("Fancy (Embolding & Slanting)"));
|
280
|
330
|
modeSelector_->insertItem(GlyphContinuous::AG_Stroked, tr("Stroked"));
|
281
|
331
|
modeSelector_->insertItem(GlyphContinuous::AG_Waterfall, tr("Waterfall"));
|
282
|
332
|
modeSelector_->setCurrentIndex(GlyphContinuous::AG_AllGlyphs);
|
283
|
333
|
|
284
|
334
|
modeLabel_ = new QLabel(tr("Mode:"), this);
|
285
|
335
|
charMapLabel_ = new QLabel(tr("Char Map:"), this);
|
|
336
|
+ xEmboldeningLabel_ = new QLabel(tr("Hori. Embolding (for Fancy):"), this);
|
|
337
|
+ yEmboldeningLabel_ = new QLabel(tr("Vert. Embolding (for Fancy):"), this);
|
|
338
|
+ slantLabel_ = new QLabel(tr("Slanting (for Fancy):"), this);
|
|
339
|
+ strokeRadiusLabel_ = new QLabel(tr("Stroke Radius (for Stroked):"), this);
|
|
340
|
+
|
|
341
|
+ xEmboldeningSpinBox_ = new QDoubleSpinBox(this);
|
|
342
|
+ yEmboldeningSpinBox_ = new QDoubleSpinBox(this);
|
|
343
|
+ slantSpinBox_ = new QDoubleSpinBox(this);
|
|
344
|
+ strokeRadiusSpinBox_ = new QDoubleSpinBox(this);
|
|
345
|
+
|
|
346
|
+ xEmboldeningSpinBox_->setSingleStep(0.005);
|
|
347
|
+ xEmboldeningSpinBox_->setMinimum(-0.1);
|
|
348
|
+ xEmboldeningSpinBox_->setMaximum(0.1);
|
|
349
|
+ yEmboldeningSpinBox_->setSingleStep(0.005);
|
|
350
|
+ yEmboldeningSpinBox_->setMinimum(-0.1);
|
|
351
|
+ yEmboldeningSpinBox_->setMaximum(0.1);
|
|
352
|
+ slantSpinBox_->setSingleStep(0.02);
|
|
353
|
+ slantSpinBox_->setMinimum(-1);
|
|
354
|
+ slantSpinBox_->setMaximum(1);
|
|
355
|
+ strokeRadiusSpinBox_->setSingleStep(0.005);
|
|
356
|
+ strokeRadiusSpinBox_->setMinimum(0);
|
|
357
|
+ strokeRadiusSpinBox_->setMaximum(0.05);
|
286
|
358
|
|
287
|
359
|
layout_ = new QGridLayout;
|
288
|
360
|
layout_->addWidget(indexSelector_, 0, 0, 1, 2);
|
... |
... |
@@ -291,7 +363,17 @@ ContinousAllGlyphsTab::createLayout() |
291
|
363
|
layout_->addWidget(modeSelector_, 1, 1);
|
292
|
364
|
layout_->addWidget(charMapSelector_, 2, 1);
|
293
|
365
|
|
|
366
|
+ layout_->addWidget(xEmboldeningLabel_, 1, 2);
|
|
367
|
+ layout_->addWidget(yEmboldeningLabel_, 2, 2);
|
|
368
|
+ layout_->addWidget(slantLabel_, 3, 2);
|
|
369
|
+ layout_->addWidget(strokeRadiusLabel_, 3, 0);
|
|
370
|
+ layout_->addWidget(xEmboldeningSpinBox_, 1, 3);
|
|
371
|
+ layout_->addWidget(yEmboldeningSpinBox_, 2, 3);
|
|
372
|
+ layout_->addWidget(slantSpinBox_, 3, 3);
|
|
373
|
+ layout_->addWidget(strokeRadiusSpinBox_, 3, 1);
|
|
374
|
+
|
294
|
375
|
layout_->setColumnStretch(1, 1);
|
|
376
|
+ layout_->setColumnStretch(3, 1);
|
295
|
377
|
|
296
|
378
|
setLayout(layout_);
|
297
|
379
|
}
|
... |
... |
@@ -302,9 +384,22 @@ ContinousAllGlyphsTab::createConnections() |
302
|
384
|
connect(indexSelector_, &GlyphIndexSelector::currentIndexChanged,
|
303
|
385
|
this, &ContinousAllGlyphsTab::changed);
|
304
|
386
|
connect(modeSelector_, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
305
|
|
- this, &ContinousAllGlyphsTab::changed);
|
|
387
|
+ this, &ContinousAllGlyphsTab::checkSubMode);
|
306
|
388
|
connect(charMapSelector_, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
307
|
389
|
this, &ContinousAllGlyphsTab::charMapChanged);
|
|
390
|
+
|
|
391
|
+ connect(xEmboldeningSpinBox_,
|
|
392
|
+ QOverload<double>::of(&QDoubleSpinBox::valueChanged),
|
|
393
|
+ this, &ContinousAllGlyphsTab::changed);
|
|
394
|
+ connect(yEmboldeningSpinBox_,
|
|
395
|
+ QOverload<double>::of(&QDoubleSpinBox::valueChanged),
|
|
396
|
+ this, &ContinousAllGlyphsTab::changed);
|
|
397
|
+ connect(slantSpinBox_,
|
|
398
|
+ QOverload<double>::of(&QDoubleSpinBox::valueChanged),
|
|
399
|
+ this, &ContinousAllGlyphsTab::changed);
|
|
400
|
+ connect(strokeRadiusSpinBox_,
|
|
401
|
+ QOverload<double>::of(&QDoubleSpinBox::valueChanged),
|
|
402
|
+ this, &ContinousAllGlyphsTab::changed);
|
308
|
403
|
}
|
309
|
404
|
|
310
|
405
|
|
... |
... |
@@ -339,4 +434,14 @@ ContinousAllGlyphsTab::charMapChanged() |
339
|
434
|
}
|
340
|
435
|
|
341
|
436
|
|
|
437
|
+void
|
|
438
|
+ContinousAllGlyphsTab::setDefaults()
|
|
439
|
+{
|
|
440
|
+ xEmboldeningSpinBox_->setValue(0.04);
|
|
441
|
+ yEmboldeningSpinBox_->setValue(0.04);
|
|
442
|
+ slantSpinBox_->setValue(0.22);
|
|
443
|
+ strokeRadiusSpinBox_->setValue(0.02);
|
|
444
|
+}
|
|
445
|
+
|
|
446
|
+
|
342
|
447
|
// end of continuous.cpp |
src/ftinspect/panels/continuous.hpp
... |
... |
@@ -79,6 +79,10 @@ public: |
79
|
79
|
int glyphBeginindex();
|
80
|
80
|
int glyphLimitIndex();
|
81
|
81
|
GlyphContinuous::SubModeAllGlyphs subMode();
|
|
82
|
+ double xEmboldening();
|
|
83
|
+ double yEmboldening();
|
|
84
|
+ double slanting();
|
|
85
|
+ double strokeRadius();
|
82
|
86
|
|
83
|
87
|
// -1: Glyph order, otherwise the char map index in the original list
|
84
|
88
|
int charMapIndex();
|
... |
... |
@@ -92,6 +96,8 @@ public: |
92
|
96
|
// This doesn't trigger either.
|
93
|
97
|
void updateLimitIndex();
|
94
|
98
|
|
|
99
|
+ void checkSubMode();
|
|
100
|
+
|
95
|
101
|
signals:
|
96
|
102
|
void changed();
|
97
|
103
|
|
... |
... |
@@ -106,6 +112,15 @@ private: |
106
|
112
|
|
107
|
113
|
QLabel* modeLabel_;
|
108
|
114
|
QLabel* charMapLabel_;
|
|
115
|
+ QLabel* xEmboldeningLabel_;
|
|
116
|
+ QLabel* yEmboldeningLabel_;
|
|
117
|
+ QLabel* slantLabel_;
|
|
118
|
+ QLabel* strokeRadiusLabel_;
|
|
119
|
+
|
|
120
|
+ QDoubleSpinBox* xEmboldeningSpinBox_;
|
|
121
|
+ QDoubleSpinBox* yEmboldeningSpinBox_;
|
|
122
|
+ QDoubleSpinBox* slantSpinBox_;
|
|
123
|
+ QDoubleSpinBox* strokeRadiusSpinBox_;
|
109
|
124
|
|
110
|
125
|
QGridLayout* layout_;
|
111
|
126
|
|
... |
... |
@@ -116,6 +131,8 @@ private: |
116
|
131
|
|
117
|
132
|
QString formatIndex(int index);
|
118
|
133
|
void charMapChanged();
|
|
134
|
+
|
|
135
|
+ void setDefaults();
|
119
|
136
|
};
|
120
|
137
|
|
121
|
138
|
|
src/ftinspect/rendering/glyphbitmap.cpp
... |
... |
@@ -24,7 +24,8 @@ GlyphBitmap::GlyphBitmap(FT_Outline* outline, |
24
|
24
|
{
|
25
|
25
|
// make a copy of the outline since we are going to manipulate it
|
26
|
26
|
FT_BBox cbox;
|
27
|
|
- transformed_ = transformOutlineToOrigin(lib, outline, &cbox);
|
|
27
|
+ transformed_ = cloneOutline(lib, outline);
|
|
28
|
+ transformOutlineToOrigin(&transformed_, &cbox);
|
28
|
29
|
boundingRect_.setCoords(cbox.xMin / 64, -cbox.yMax / 64,
|
29
|
30
|
cbox.xMax / 64, -cbox.yMin / 64);
|
30
|
31
|
}
|
src/ftinspect/rendering/glyphcontinuous.cpp
... |
... |
@@ -4,13 +4,13 @@ |
4
|
4
|
|
5
|
5
|
#include "glyphcontinuous.hpp"
|
6
|
6
|
|
|
7
|
+#include "../engine/engine.hpp"
|
|
8
|
+#include "../rendering/renderutils.hpp"
|
|
9
|
+
|
7
|
10
|
#include <cmath>
|
8
|
11
|
#include <QPainter>
|
9
|
12
|
#include <QWheelEvent>
|
10
|
13
|
|
11
|
|
-#include "../engine/engine.hpp"
|
12
|
|
-#include "../rendering/renderutils.hpp"
|
13
|
|
-
|
14
|
14
|
|
15
|
15
|
GlyphContinuous::GlyphContinuous(QWidget* parent, Engine* engine)
|
16
|
16
|
: QWidget(parent), engine_(engine)
|
... |
... |
@@ -18,6 +18,15 @@ GlyphContinuous::GlyphContinuous(QWidget* parent, Engine* engine) |
18
|
18
|
setAcceptDrops(false);
|
19
|
19
|
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
20
|
20
|
graphicsDefault_ = GraphicsDefault::deafultInstance();
|
|
21
|
+
|
|
22
|
+ FT_Stroker_New(engine_->ftLibrary(), &stroker_);
|
|
23
|
+}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+GlyphContinuous::~GlyphContinuous()
|
|
27
|
+{
|
|
28
|
+ cleanCloned();
|
|
29
|
+ FT_Stroker_Done(stroker_);
|
21
|
30
|
}
|
22
|
31
|
|
23
|
32
|
|
... |
... |
@@ -38,12 +47,9 @@ GlyphContinuous::paintEvent(QPaintEvent* event) |
38
|
47
|
switch (modeAG_)
|
39
|
48
|
{
|
40
|
49
|
case AG_AllGlyphs:
|
41
|
|
- paintAGAllGlyphs(&painter);
|
42
|
|
- break;
|
43
|
|
- // TODO more modes
|
44
|
50
|
case AG_Fancy:
|
45
|
|
- break;
|
46
|
51
|
case AG_Stroked:
|
|
52
|
+ paintAG(&painter);
|
47
|
53
|
break;
|
48
|
54
|
case AG_Waterfall:
|
49
|
55
|
break;
|
... |
... |
@@ -71,19 +77,114 @@ GlyphContinuous::wheelEvent(QWheelEvent* event) |
71
|
77
|
|
72
|
78
|
|
73
|
79
|
void
|
74
|
|
-GlyphContinuous::paintAGAllGlyphs(QPainter* painter)
|
|
80
|
+GlyphContinuous::paintAG(QPainter* painter)
|
75
|
81
|
{
|
|
82
|
+ if (modeAG_ == AG_Stroked)
|
|
83
|
+ {
|
|
84
|
+ auto radius = static_cast<FT_Fixed>(metrics_.y_ppem * 64 * strokeRadius_);
|
|
85
|
+ FT_Stroker_Set(stroker_, radius,
|
|
86
|
+ FT_STROKER_LINECAP_ROUND,
|
|
87
|
+ FT_STROKER_LINEJOIN_ROUND,
|
|
88
|
+ 0);
|
|
89
|
+ }
|
|
90
|
+
|
76
|
91
|
for (int i = beginIndex_; i < limitIndex_; i++)
|
77
|
92
|
{
|
78
|
93
|
unsigned index = i;
|
79
|
94
|
if (charMapIndex_ >= 0)
|
80
|
95
|
index = engine_->glyphIndexFromCharCode(i, charMapIndex_);
|
81
|
96
|
|
82
|
|
- if (!paintChar(painter, index))
|
|
97
|
+ if (!loadGlyph(index))
|
|
98
|
+ break;
|
|
99
|
+
|
|
100
|
+ // All Glyphs need no tranformation, and Waterfall isn't handled here.
|
|
101
|
+ switch (modeAG_)
|
|
102
|
+ {
|
|
103
|
+ case AG_Fancy:
|
|
104
|
+ transformGlyphAGFancy();
|
|
105
|
+ break;
|
|
106
|
+ case AG_Stroked:
|
|
107
|
+ transformGlyphAGStroked();
|
83
|
108
|
break;
|
|
109
|
+ default:;
|
|
110
|
+ }
|
|
111
|
+
|
|
112
|
+ if (!paintChar(painter))
|
|
113
|
+ break;
|
|
114
|
+ cleanCloned();
|
84
|
115
|
|
85
|
116
|
displayingCount_++;
|
86
|
117
|
}
|
|
118
|
+ cleanCloned();
|
|
119
|
+}
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+void
|
|
123
|
+GlyphContinuous::transformGlyphAGFancy()
|
|
124
|
+{
|
|
125
|
+ // adopted from ftview.c:289
|
|
126
|
+ /***************************************************************/
|
|
127
|
+ /* */
|
|
128
|
+ /* 2*2 affine transformation matrix, 16.16 fixed float format */
|
|
129
|
+ /* */
|
|
130
|
+ /* Shear matrix: */
|
|
131
|
+ /* */
|
|
132
|
+ /* | x' | | 1 k | | x | x' = x + ky */
|
|
133
|
+ /* | | = | | * | | <==> */
|
|
134
|
+ /* | y' | | 0 1 | | y | y' = y */
|
|
135
|
+ /* */
|
|
136
|
+ /* outline' shear outline */
|
|
137
|
+ /* */
|
|
138
|
+ /***************************************************************/
|
|
139
|
+
|
|
140
|
+ FT_Matrix shear;
|
|
141
|
+ FT_Pos xstr, ystr;
|
|
142
|
+
|
|
143
|
+ shear.xx = 1 << 16;
|
|
144
|
+ shear.xy = static_cast<FT_Fixed>(slant_ * (1 << 16));
|
|
145
|
+ shear.yx = 0;
|
|
146
|
+ shear.yy = 1 << 16;
|
|
147
|
+
|
|
148
|
+ xstr = (FT_Pos)(metrics_.y_ppem * 64 * boldX_);
|
|
149
|
+ ystr = (FT_Pos)(metrics_.y_ppem * 64 * boldY_);
|
|
150
|
+
|
|
151
|
+ if (!isGlyphCloned_)
|
|
152
|
+ cloneGlyph();
|
|
153
|
+
|
|
154
|
+ if (glyph_->format != FT_GLYPH_FORMAT_OUTLINE)
|
|
155
|
+ return; // TODO suuport non-outline: code below all depend on `outline_`!
|
|
156
|
+
|
|
157
|
+ FT_Outline_Transform(&outline_, &shear);
|
|
158
|
+ FT_Outline_EmboldenXY(&outline_, xstr, ystr);
|
|
159
|
+
|
|
160
|
+ if (glyph_->advance.x)
|
|
161
|
+ glyph_->advance.x += xstr;
|
|
162
|
+
|
|
163
|
+ if (glyph_->advance.y)
|
|
164
|
+ glyph_->advance.y += ystr;
|
|
165
|
+
|
|
166
|
+ //glyph_->metrics.width += xstr;
|
|
167
|
+ //glyph_->metrics.height += ystr;
|
|
168
|
+ //glyph_->metrics.horiAdvance += xstr;
|
|
169
|
+ //glyph_->metrics.vertAdvance += ystr;
|
|
170
|
+}
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+void
|
|
174
|
+GlyphContinuous::transformGlyphAGStroked()
|
|
175
|
+{
|
|
176
|
+ //if (!isGlyphCloned_)
|
|
177
|
+ //cloneGlyph();
|
|
178
|
+ // Well, now here only outline glyph is supported.
|
|
179
|
+ if (glyph_->format != FT_GLYPH_FORMAT_OUTLINE)
|
|
180
|
+ return;
|
|
181
|
+ auto error = FT_Glyph_Stroke(&glyph_, stroker_, 0);
|
|
182
|
+ if (!error)
|
|
183
|
+ {
|
|
184
|
+ isGlyphCloned_ = true;
|
|
185
|
+ isOutlineCloned_ = false;
|
|
186
|
+ outline_ = reinterpret_cast<FT_OutlineGlyph>(glyph_)->outline;
|
|
187
|
+ }
|
87
|
188
|
}
|
88
|
189
|
|
89
|
190
|
|
... |
... |
@@ -101,16 +202,11 @@ GlyphContinuous::prePaint() |
101
|
202
|
|
102
|
203
|
|
103
|
204
|
bool
|
104
|
|
-GlyphContinuous::paintChar(QPainter* painter,
|
105
|
|
- int index)
|
|
205
|
+GlyphContinuous::paintChar(QPainter* painter)
|
106
|
206
|
{
|
107
|
|
- auto glyph = engine_->loadGlyphWithoutUpdate(index);
|
108
|
|
- if (!glyph)
|
109
|
|
- return false;
|
110
|
|
-
|
111
|
207
|
// ftview.c:557
|
112
|
|
- int width = glyph->advance.x ? glyph->advance.x >> 16
|
113
|
|
- : metrics_.y_ppem / 2;
|
|
208
|
+ int width = glyph_->advance.x ? glyph_->advance.x >> 16
|
|
209
|
+ : metrics_.y_ppem / 2;
|
114
|
210
|
|
115
|
211
|
if (!checkFitX(x_ + width))
|
116
|
212
|
{
|
... |
... |
@@ -122,7 +218,7 @@ GlyphContinuous::paintChar(QPainter* painter, |
122
|
218
|
}
|
123
|
219
|
|
124
|
220
|
x_++; // extra space
|
125
|
|
- if (glyph->advance.x == 0)
|
|
221
|
+ if (glyph_->advance.x == 0)
|
126
|
222
|
{
|
127
|
223
|
// Draw a red square to indicate
|
128
|
224
|
painter->fillRect(x_, y_ - width, width, width,
|
... |
... |
@@ -136,15 +232,15 @@ GlyphContinuous::paintChar(QPainter* painter, |
136
|
232
|
|
137
|
233
|
// First translate the outline
|
138
|
234
|
|
139
|
|
- if (glyph->format != FT_GLYPH_FORMAT_OUTLINE)
|
|
235
|
+ if (glyph_->format != FT_GLYPH_FORMAT_OUTLINE)
|
140
|
236
|
return true; // XXX only outline is supported - need to impl others later
|
141
|
237
|
|
142
|
238
|
FT_BBox cbox;
|
143
|
239
|
// Don't forget to free this when returning
|
144
|
|
- auto outline = transformOutlineToOrigin(
|
145
|
|
- engine_->ftLibrary(),
|
146
|
|
- &reinterpret_cast<FT_OutlineGlyph>(glyph)->outline,
|
147
|
|
- &cbox);
|
|
240
|
+ if (!isOutlineCloned_ && !isGlyphCloned_)
|
|
241
|
+ cloneOutline();
|
|
242
|
+
|
|
243
|
+ transformOutlineToOrigin(&outline_, &cbox);
|
148
|
244
|
|
149
|
245
|
auto outlineWidth = (cbox.xMax - cbox.xMin) / 64;
|
150
|
246
|
auto outlineHeight = (cbox.yMax - cbox.yMin) / 64;
|
... |
... |
@@ -175,12 +271,11 @@ GlyphContinuous::paintChar(QPainter* painter, |
175
|
271
|
bitmap.pixel_mode = aaEnabled ? FT_PIXEL_MODE_GRAY : FT_PIXEL_MODE_MONO;
|
176
|
272
|
|
177
|
273
|
FT_Error error = FT_Outline_Get_Bitmap(engine_->ftLibrary(),
|
178
|
|
- &outline,
|
|
274
|
+ &outline_,
|
179
|
275
|
&bitmap);
|
180
|
276
|
if (error)
|
181
|
277
|
{
|
182
|
278
|
// XXX error handling
|
183
|
|
- FT_Outline_Done(engine_->ftLibrary(), &outline);
|
184
|
279
|
return true;
|
185
|
280
|
}
|
186
|
281
|
|
... |
... |
@@ -189,12 +284,59 @@ GlyphContinuous::paintChar(QPainter* painter, |
189
|
284
|
image.convertToFormat(QImage::Format_ARGB32_Premultiplied));
|
190
|
285
|
|
191
|
286
|
x_ += width;
|
|
287
|
+
|
|
288
|
+ return true;
|
|
289
|
+}
|
|
290
|
+
|
192
|
291
|
|
193
|
|
- FT_Outline_Done(engine_->ftLibrary(), &outline);
|
|
292
|
+bool
|
|
293
|
+GlyphContinuous::loadGlyph(int index)
|
|
294
|
+{
|
|
295
|
+ glyph_ = engine_->loadGlyphWithoutUpdate(index);
|
|
296
|
+ isGlyphCloned_ = false;
|
|
297
|
+ if (!glyph_)
|
|
298
|
+ return false;
|
|
299
|
+ if (glyph_->format == FT_GLYPH_FORMAT_OUTLINE)
|
|
300
|
+ {
|
|
301
|
+ isOutlineCloned_ = false;
|
|
302
|
+ outline_ = reinterpret_cast<FT_OutlineGlyph>(glyph_)->outline;
|
|
303
|
+ }
|
194
|
304
|
return true;
|
195
|
305
|
}
|
196
|
306
|
|
197
|
307
|
|
|
308
|
+void
|
|
309
|
+GlyphContinuous::cloneGlyph()
|
|
310
|
+{
|
|
311
|
+ glyph_ = ::cloneGlyph(glyph_);
|
|
312
|
+ isGlyphCloned_ = true;
|
|
313
|
+}
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+void
|
|
317
|
+GlyphContinuous::cloneOutline()
|
|
318
|
+{
|
|
319
|
+ outline_ = ::cloneOutline(engine_->ftLibrary(), &outline_);
|
|
320
|
+ isOutlineCloned_ = true;
|
|
321
|
+}
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+void
|
|
325
|
+GlyphContinuous::cleanCloned()
|
|
326
|
+{
|
|
327
|
+ if (isGlyphCloned_)
|
|
328
|
+ {
|
|
329
|
+ FT_Done_Glyph(glyph_);
|
|
330
|
+ isGlyphCloned_ = false;
|
|
331
|
+ }
|
|
332
|
+ if (isOutlineCloned_)
|
|
333
|
+ {
|
|
334
|
+ FT_Outline_Done(engine_->ftLibrary(), &outline_);
|
|
335
|
+ isOutlineCloned_ = false;
|
|
336
|
+ }
|
|
337
|
+}
|
|
338
|
+
|
|
339
|
+
|
198
|
340
|
bool
|
199
|
341
|
GlyphContinuous::checkFitX(int x)
|
200
|
342
|
{
|
src/ftinspect/rendering/glyphcontinuous.hpp
... |
... |
@@ -7,6 +7,9 @@ |
7
|
7
|
#include "graphicsdefault.hpp"
|
8
|
8
|
#include <QWidget>
|
9
|
9
|
#include <freetype/freetype.h>
|
|
10
|
+#include <freetype/ftglyph.h>
|
|
11
|
+#include <freetype/ftoutln.h>
|
|
12
|
+#include <freetype/ftstroke.h>
|
10
|
13
|
|
11
|
14
|
class Engine;
|
12
|
15
|
class GlyphContinuous
|
... |
... |
@@ -15,7 +18,7 @@ class GlyphContinuous |
15
|
18
|
Q_OBJECT
|
16
|
19
|
public:
|
17
|
20
|
GlyphContinuous(QWidget* parent, Engine* engine);
|
18
|
|
- ~GlyphContinuous() override = default;
|
|
21
|
+ ~GlyphContinuous() override;
|
19
|
22
|
|
20
|
23
|
enum Mode : int
|
21
|
24
|
{
|
... |
... |
@@ -39,6 +42,13 @@ public: |
39
|
42
|
void setCharMapIndex(int index) { charMapIndex_ = index; }
|
40
|
43
|
void setMode(Mode mode) { mode_ = mode; }
|
41
|
44
|
void setSubModeAllGlyphs(SubModeAllGlyphs modeAg) { modeAG_ = modeAg; }
|
|
45
|
+ void setFancyParams(double boldX, double boldY, double slant)
|
|
46
|
+ {
|
|
47
|
+ boldX_ = boldX;
|
|
48
|
+ boldY_ = boldY;
|
|
49
|
+ slant_ = slant;
|
|
50
|
+ }
|
|
51
|
+ void setStrokeRadius(double radius) { strokeRadius_ = radius; }
|
42
|
52
|
|
43
|
53
|
signals:
|
44
|
54
|
void wheelNavigate(int steps);
|
... |
... |
@@ -53,21 +63,36 @@ private: |
53
|
63
|
Engine* engine_;
|
54
|
64
|
GraphicsDefault* graphicsDefault_;
|
55
|
65
|
|
|
66
|
+ Mode mode_ = AllGlyphs;
|
|
67
|
+ SubModeAllGlyphs modeAG_ = AG_AllGlyphs;
|
56
|
68
|
int beginIndex_;
|
57
|
69
|
int limitIndex_;
|
58
|
70
|
int charMapIndex_;
|
59
|
|
- Mode mode_ = AllGlyphs;
|
60
|
|
- SubModeAllGlyphs modeAG_ = AG_AllGlyphs;
|
|
71
|
+ double boldX_, boldY_, slant_;
|
|
72
|
+ double strokeRadius_;
|
61
|
73
|
|
62
|
74
|
int displayingCount_ = 0;
|
63
|
75
|
FT_Size_Metrics metrics_;
|
64
|
76
|
int x_ = 0, y_ = 0;
|
65
|
77
|
int stepY_ = 0;
|
|
78
|
+ FT_Glyph glyph_;
|
|
79
|
+ FT_Outline outline_;
|
|
80
|
+ // when glyph is cloned, outline is factually also cloned
|
|
81
|
+ // but `isOutlineCloned` won't be set!
|
|
82
|
+ bool isGlyphCloned_ = false, isOutlineCloned_ = false;
|
|
83
|
+
|
|
84
|
+ FT_Stroker stroker_;
|
66
|
85
|
|
67
|
|
- void paintAGAllGlyphs(QPainter* painter);
|
|
86
|
+ void paintAG(QPainter* painter);
|
|
87
|
+ void transformGlyphAGFancy();
|
|
88
|
+ void transformGlyphAGStroked();
|
68
|
89
|
void prePaint();
|
69
|
90
|
// return if there's enough space to paint the current char
|
70
|
|
- bool paintChar(QPainter* painter, int index);
|
|
91
|
+ bool paintChar(QPainter* painter);
|
|
92
|
+ bool loadGlyph(int index);
|
|
93
|
+ void cloneGlyph();
|
|
94
|
+ void cloneOutline();
|
|
95
|
+ void cleanCloned();
|
71
|
96
|
|
72
|
97
|
bool checkFitX(int x);
|
73
|
98
|
bool checkFitY(int y);
|
src/ftinspect/rendering/renderutils.cpp
... |
... |
@@ -5,16 +5,30 @@ |
5
|
5
|
#include "renderutils.hpp"
|
6
|
6
|
|
7
|
7
|
FT_Outline
|
8
|
|
-transformOutlineToOrigin(FT_Library library,
|
9
|
|
- FT_Outline* outline,
|
10
|
|
- FT_BBox* outControlBox)
|
|
8
|
+cloneOutline(FT_Library library,
|
|
9
|
+ FT_Outline* src)
|
11
|
10
|
{
|
12
|
11
|
FT_Outline transformed;
|
13
|
|
- FT_Outline_New(library,
|
14
|
|
- static_cast<unsigned int>(outline->n_points),
|
15
|
|
- outline->n_contours, &transformed);
|
16
|
|
- FT_Outline_Copy(outline, &transformed);
|
|
12
|
+ FT_Outline_New(library, static_cast<unsigned int>(src->n_points),
|
|
13
|
+ src->n_contours, &transformed);
|
|
14
|
+ FT_Outline_Copy(src, &transformed);
|
|
15
|
+ return transformed;
|
|
16
|
+}
|
17
|
17
|
|
|
18
|
+
|
|
19
|
+FT_Glyph
|
|
20
|
+cloneGlyph(FT_Glyph src)
|
|
21
|
+{
|
|
22
|
+ FT_Glyph target = NULL;
|
|
23
|
+ FT_Glyph_Copy(src, &target);
|
|
24
|
+ return target;
|
|
25
|
+}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+void
|
|
29
|
+transformOutlineToOrigin(FT_Outline* outline,
|
|
30
|
+ FT_BBox* outControlBox)
|
|
31
|
+{
|
18
|
32
|
FT_BBox cbox;
|
19
|
33
|
FT_Outline_Get_CBox(outline, &cbox);
|
20
|
34
|
|
... |
... |
@@ -23,11 +37,10 @@ transformOutlineToOrigin(FT_Library library, |
23
|
37
|
cbox.xMax = (cbox.xMax + 63) & ~63;
|
24
|
38
|
cbox.yMax = (cbox.yMax + 63) & ~63;
|
25
|
39
|
// we shift the outline to the origin for rendering later on
|
26
|
|
- FT_Outline_Translate(&transformed, -cbox.xMin, -cbox.yMin);
|
|
40
|
+ FT_Outline_Translate(outline, -cbox.xMin, -cbox.yMin);
|
27
|
41
|
|
28
|
42
|
if (outControlBox)
|
29
|
43
|
*outControlBox = cbox;
|
30
|
|
- return transformed;
|
31
|
44
|
}
|
32
|
45
|
|
33
|
46
|
|
src/ftinspect/rendering/renderutils.hpp
... |
... |
@@ -4,12 +4,15 @@ |
4
|
4
|
|
5
|
5
|
#pragma once
|
6
|
6
|
|
|
7
|
+#include <freetype/ftglyph.h>
|
7
|
8
|
#include <freetype/ftoutln.h>
|
8
|
9
|
|
9
|
10
|
// The constructed `outline` must be freed by the caller
|
10
|
|
-FT_Outline transformOutlineToOrigin(FT_Library library,
|
11
|
|
- FT_Outline* outline,
|
12
|
|
- FT_BBox* outControlBox);
|
|
11
|
+FT_Outline cloneOutline(FT_Library library, FT_Outline* src);
|
|
12
|
+FT_Glyph cloneGlyph(FT_Glyph src);
|
|
13
|
+
|
|
14
|
+void transformOutlineToOrigin(FT_Outline* outline,
|
|
15
|
+ FT_BBox* outControlBox);
|
13
|
16
|
|
14
|
17
|
|
15
|
18
|
// end of renderutils.hpp |
|