Commits:
-
2747cd47
by Charlie Jiang
at 2022-08-22T23:44:24+08:00
[ftinspect] Properly support bitmap-only fonts.
Fixes #16.
Now the size selector will only accept available sizes if the font is
bitmap-only.
* src/ftinspect/widgets/fontsizeselector.cpp,
src/ftinspect/widgets/fontsizeselector.hpp:
Add limits to the font selector so it only accepts sizes provided by the
font if it's not scalable. Now it will jump to next available size when
adjusted. But when edit with the keyboard, it functions weird.
* src/ftinspect/engine/engine.cpp, src/ftinspect/engine/engine.hpp:
Fix minor bugs with `reloadFont` and `glyphName`.
Add `currentFontBitmapOnly` and `currentFontFixedSizes` functions.
* src/ftinspect/panels/settingpanel.cpp: Force "Enable embedded bitmap" for
bitmap-only fonts.
* src/ftinspect/panels/comparator.cpp, src/ftinspect/panels/continuous.cpp,
src/ftinspect/panels/singular.cpp:
Update the size selector when font changes.
-
bd50cb4b
by Charlie Jiang
at 2022-08-23T00:00:11+08:00
[ftinspect] Properly support waterfall for unscalable fonts.
Now the waterfall will show all available sizes when the font is not
scalable.
* src/ftinspect/engine/stringrenderer.cpp: Add special handling to
unscalable fonts.
* src/ftinspect/panels/continuous.cpp:
Disable "WF Config" button when the font is not scalable.
* src/ftinspect/rendering/glyphcontinuous.cpp:
Show sizes in pixels when the font is not scalable.
* src/ftinspect/panels/settingpanel.cpp: Fix bug.
10 changed files:
Changes:
src/ftinspect/engine/engine.cpp
... |
... |
@@ -381,8 +381,15 @@ Engine::reloadFont() |
381
|
381
|
if (!scaler_.face_id)
|
382
|
382
|
return;
|
383
|
383
|
imageType_.face_id = scaler_.face_id;
|
384
|
|
- FTC_Manager_LookupFace(cacheManager_, scaler_.face_id, &ftFallbackFace_);
|
385
|
|
- FTC_Manager_LookupSize(cacheManager_, &scaler_, &ftSize_);
|
|
384
|
+
|
|
385
|
+ if (FTC_Manager_LookupFace(cacheManager_, scaler_.face_id, &ftFallbackFace_))
|
|
386
|
+ {
|
|
387
|
+ ftFallbackFace_ = NULL;
|
|
388
|
+ ftSize_ = NULL;
|
|
389
|
+ return;
|
|
390
|
+ }
|
|
391
|
+ if (FTC_Manager_LookupSize(cacheManager_, &scaler_, &ftSize_))
|
|
392
|
+ ftSize_ = NULL; // Good font, bad size.
|
386
|
393
|
}
|
387
|
394
|
|
388
|
395
|
|
... |
... |
@@ -531,6 +538,29 @@ Engine::currentFontSFNTTableInfo() |
531
|
538
|
}
|
532
|
539
|
|
533
|
540
|
|
|
541
|
+bool
|
|
542
|
+Engine::currentFontBitmapOnly()
|
|
543
|
+{
|
|
544
|
+ if (!ftFallbackFace_)
|
|
545
|
+ return false;
|
|
546
|
+ return !FT_IS_SCALABLE(ftFallbackFace_);
|
|
547
|
+}
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+std::vector<int>
|
|
551
|
+Engine::currentFontFixedSizes()
|
|
552
|
+{
|
|
553
|
+ if (!ftFallbackFace_ || !FT_HAS_FIXED_SIZES(ftFallbackFace_)
|
|
554
|
+ || !ftFallbackFace_->available_sizes)
|
|
555
|
+ return {};
|
|
556
|
+ std::vector<int> result;
|
|
557
|
+ result.resize(ftFallbackFace_->num_fixed_sizes);
|
|
558
|
+ for (int i = 0; i < ftFallbackFace_->num_fixed_sizes; i++)
|
|
559
|
+ result[i] = ftFallbackFace_->available_sizes[i].size >> 6; // XXX: ????
|
|
560
|
+ return result;
|
|
561
|
+}
|
|
562
|
+
|
|
563
|
+
|
534
|
564
|
QString
|
535
|
565
|
Engine::glyphName(int index)
|
536
|
566
|
{
|
... |
... |
@@ -539,14 +569,11 @@ Engine::glyphName(int index) |
539
|
569
|
if (index < 0)
|
540
|
570
|
throw std::runtime_error("Invalid glyph index");
|
541
|
571
|
|
542
|
|
- FT_Face face = NULL;
|
543
|
|
- if (FTC_Manager_LookupFace(cacheManager_, scaler_.face_id, &face))
|
544
|
|
- return name;
|
545
|
|
-
|
546
|
|
- if (face && FT_HAS_GLYPH_NAMES(face))
|
|
572
|
+ reloadFont();
|
|
573
|
+ if (ftFallbackFace_ && FT_HAS_GLYPH_NAMES(ftFallbackFace_))
|
547
|
574
|
{
|
548
|
575
|
char buffer[256];
|
549
|
|
- if (!FT_Get_Glyph_Name(face,
|
|
576
|
+ if (!FT_Get_Glyph_Name(ftFallbackFace_,
|
550
|
577
|
static_cast<unsigned int>(index),
|
551
|
578
|
buffer,
|
552
|
579
|
sizeof(buffer)))
|
src/ftinspect/engine/engine.hpp
... |
... |
@@ -166,6 +166,8 @@ public: |
166
|
166
|
MMGXState currentFontMMGXState() { return curMMGXState_; }
|
167
|
167
|
std::vector<MMGXAxisInfo>& currentFontMMGXAxes() { return curMMGXAxes_; }
|
168
|
168
|
std::vector<SFNTTableInfo>& currentFontSFNTTableInfo();
|
|
169
|
+ bool currentFontBitmapOnly();
|
|
170
|
+ std::vector<int> currentFontFixedSizes();
|
169
|
171
|
FontFileManager& fontFileManager() { return fontFileManager_; }
|
170
|
172
|
EngineDefaultValues& engineDefaults() { return engineDefaults_; }
|
171
|
173
|
bool antiAliasingEnabled() { return antiAliasingEnabled_; }
|
src/ftinspect/engine/stringrenderer.cpp
... |
... |
@@ -364,10 +364,16 @@ StringRenderer::render(int width, |
364
|
364
|
// Waterfall
|
365
|
365
|
|
366
|
366
|
vertical_ = false;
|
|
367
|
+ // They're only effective for non-bitmap-only (scalable) fonts!
|
367
|
368
|
auto originalSize = static_cast<int>(engine_->pointSize() * 64);
|
368
|
369
|
auto ptSize = originalSize;
|
369
|
370
|
auto ptHeight = 64 * 72 * height / engine_->dpi();
|
370
|
|
- int step;
|
|
371
|
+ int step = 0;
|
|
372
|
+
|
|
373
|
+ auto bitmapOnly = engine_->currentFontBitmapOnly();
|
|
374
|
+ auto fixedSizes = engine_->currentFontFixedSizes();
|
|
375
|
+ std::sort(fixedSizes.begin(), fixedSizes.end());
|
|
376
|
+ auto fixedSizesIter = fixedSizes.begin();
|
371
|
377
|
|
372
|
378
|
if (waterfallStart_ <= 0)
|
373
|
379
|
{
|
... |
... |
@@ -376,7 +382,7 @@ StringRenderer::render(int width, |
376
|
382
|
ptSize = ptSize - step * (ptSize / step); // modulo
|
377
|
383
|
ptSize += step;
|
378
|
384
|
}
|
379
|
|
- else
|
|
385
|
+ else if (!bitmapOnly)
|
380
|
386
|
{
|
381
|
387
|
ptSize = static_cast<int>(waterfallStart_ * 64.0) & ~31;
|
382
|
388
|
// we first get a ratio since height & ppem are near proportional...
|
... |
... |
@@ -384,7 +390,7 @@ StringRenderer::render(int width, |
384
|
390
|
engine_->setSizeByPoint(64.0);
|
385
|
391
|
engine_->reloadFont();
|
386
|
392
|
if (!engine_->renderReady())
|
387
|
|
- return -1; // TODO: Handle bitmap-only fonts
|
|
393
|
+ return -1;
|
388
|
394
|
auto pixelActual = engine_->currentFontMetrics().height >> 6;
|
389
|
395
|
|
390
|
396
|
auto heightPt = height * 64.0 / pixelActual;
|
... |
... |
@@ -407,7 +413,14 @@ StringRenderer::render(int width, |
407
|
413
|
|
408
|
414
|
while (true)
|
409
|
415
|
{
|
410
|
|
- engine_->setSizeByPoint(ptSize / 64.0);
|
|
416
|
+ if (!bitmapOnly)
|
|
417
|
+ engine_->setSizeByPoint(ptSize / 64.0);
|
|
418
|
+ else
|
|
419
|
+ {
|
|
420
|
+ if (fixedSizesIter == fixedSizes.end())
|
|
421
|
+ break;
|
|
422
|
+ engine_->setSizeByPixel(*fixedSizesIter);
|
|
423
|
+ }
|
411
|
424
|
clearActive(true);
|
412
|
425
|
prepareRendering(); // set size/face for engine, so metrics are valid
|
413
|
426
|
auto& metrics = engine_->currentFontMetrics();
|
... |
... |
@@ -419,7 +432,7 @@ StringRenderer::render(int width, |
419
|
432
|
|
420
|
433
|
y += static_cast<int>(metrics.height >> 6) + 1;
|
421
|
434
|
|
422
|
|
- if (y >= height)
|
|
435
|
+ if (y >= height && !bitmapOnly)
|
423
|
436
|
break;
|
424
|
437
|
|
425
|
438
|
if (ptSize == originalSize)
|
... |
... |
@@ -433,9 +446,14 @@ StringRenderer::render(int width, |
433
|
446
|
offset);
|
434
|
447
|
count = std::max(count, lcount);
|
435
|
448
|
|
436
|
|
- if (step == 0)
|
437
|
|
- break;
|
438
|
|
- ptSize += step;
|
|
449
|
+ if (!bitmapOnly)
|
|
450
|
+ {
|
|
451
|
+ if (step == 0)
|
|
452
|
+ break;
|
|
453
|
+ ptSize += step;
|
|
454
|
+ }
|
|
455
|
+ else
|
|
456
|
+ ++fixedSizesIter;
|
439
|
457
|
}
|
440
|
458
|
engine_->setSizeByPoint(originalSize / 64.0);
|
441
|
459
|
|
src/ftinspect/panels/comparator.cpp
... |
... |
@@ -49,6 +49,7 @@ ComperatorTab::repaintGlyph() |
49
|
49
|
void
|
50
|
50
|
ComperatorTab::reloadFont()
|
51
|
51
|
{
|
|
52
|
+ sizeSelector_->reloadFromFont(engine_);
|
52
|
53
|
charMapSelector_->repopulate();
|
53
|
54
|
for (auto panel : settingPanels_)
|
54
|
55
|
panel->onFontChanged();
|
src/ftinspect/panels/continuous.cpp
... |
... |
@@ -47,7 +47,10 @@ void |
47
|
47
|
ContinuousTab::reloadFont()
|
48
|
48
|
{
|
49
|
49
|
currentGlyphCount_ = engine_->currentFontNumberOfGlyphs();
|
|
50
|
+ sizeSelector_->reloadFromFont(engine_);
|
50
|
51
|
setGlyphCount(qBound(0, currentGlyphCount_, INT_MAX));
|
|
52
|
+ checkModeSource();
|
|
53
|
+
|
51
|
54
|
charMapSelector_->repopulate();
|
52
|
55
|
canvas_->stringRenderer().reloadAll();
|
53
|
56
|
canvas_->purgeCache();
|
... |
... |
@@ -163,7 +166,8 @@ ContinuousTab::checkModeSource() |
163
|
166
|
waterfallCheckBox_->setEnabled(!vert);
|
164
|
167
|
}
|
165
|
168
|
|
166
|
|
- waterfallConfigButton_->setEnabled(waterfallCheckBox_->isChecked());
|
|
169
|
+ waterfallConfigButton_->setEnabled(waterfallCheckBox_->isChecked()
|
|
170
|
+ && !engine_->currentFontBitmapOnly());
|
167
|
171
|
|
168
|
172
|
repaintGlyph();
|
169
|
173
|
}
|
src/ftinspect/panels/settingpanel.cpp
... |
... |
@@ -113,6 +113,12 @@ SettingPanel::onFontChanged() |
113
|
113
|
populatePalettes();
|
114
|
114
|
mmgxPanel_->reloadFont();
|
115
|
115
|
blockSignals(blockState);
|
|
116
|
+
|
|
117
|
+ // Place this after `blockSignals` to let the signals emitted normally
|
|
118
|
+ engine_->reloadFont();
|
|
119
|
+ embeddedBitmapCheckBox_->setEnabled(!engine_->currentFontBitmapOnly());
|
|
120
|
+ if (engine_->currentFontBitmapOnly())
|
|
121
|
+ embeddedBitmapCheckBox_->setChecked(true);
|
116
|
122
|
}
|
117
|
123
|
|
118
|
124
|
|
src/ftinspect/panels/singular.cpp
... |
... |
@@ -409,6 +409,7 @@ SingularTab::reloadFont() |
409
|
409
|
{
|
410
|
410
|
currentGlyphCount_ = engine_->currentFontNumberOfGlyphs();
|
411
|
411
|
indexSelector_->setMinMax(0, currentGlyphCount_);
|
|
412
|
+ sizeSelector_->reloadFromFont(engine_);
|
412
|
413
|
drawGlyph();
|
413
|
414
|
}
|
414
|
415
|
|
src/ftinspect/rendering/glyphcontinuous.cpp
... |
... |
@@ -472,7 +472,10 @@ GlyphContinuous::beginDrawCacheLine(QPainter* painter, |
472
|
472
|
painter->setFont(oldFont);
|
473
|
473
|
auto metrics = painter->fontMetrics();
|
474
|
474
|
|
475
|
|
- auto sizePrefix = QString("%1: ").arg(line.sizePoint);
|
|
475
|
+ auto printSize = line.sizePoint;
|
|
476
|
+ if (engine_->currentFontBitmapOnly())
|
|
477
|
+ printSize = printSize * engine_->dpi() / 72.0; // convert back
|
|
478
|
+ auto sizePrefix = QString("%1: ").arg(printSize);
|
476
|
479
|
painter->drawText(line.basePosition, sizePrefix);
|
477
|
480
|
|
478
|
481
|
sizeIndicatorOffset_ = metrics.horizontalAdvance(sizePrefix);
|
src/ftinspect/widgets/fontsizeselector.cpp
... |
... |
@@ -6,6 +6,8 @@ |
6
|
6
|
|
7
|
7
|
#include "../engine/engine.hpp"
|
8
|
8
|
|
|
9
|
+#include <algorithm>
|
|
10
|
+
|
9
|
11
|
FontSizeSelector::FontSizeSelector(QWidget* parent)
|
10
|
12
|
: QWidget(parent)
|
11
|
13
|
{
|
... |
... |
@@ -45,6 +47,25 @@ FontSizeSelector::setSizePoint(double sizePoint) |
45
|
47
|
}
|
46
|
48
|
|
47
|
49
|
|
|
50
|
+void
|
|
51
|
+FontSizeSelector::reloadFromFont(Engine* engine)
|
|
52
|
+{
|
|
53
|
+ bitmapOnly_ = engine->currentFontBitmapOnly();
|
|
54
|
+ fixedSizes_ = engine->currentFontFixedSizes();
|
|
55
|
+ std::sort(fixedSizes_.begin(), fixedSizes_.end());
|
|
56
|
+ if (fixedSizes_.empty())
|
|
57
|
+ bitmapOnly_ = false; // Well this won't work...
|
|
58
|
+
|
|
59
|
+ unitsComboBox_->setEnabled(!bitmapOnly_);
|
|
60
|
+
|
|
61
|
+ {
|
|
62
|
+ QSignalBlocker blocker(this);
|
|
63
|
+ unitsComboBox_->setCurrentIndex(Units_px);
|
|
64
|
+ }
|
|
65
|
+ checkFixedSizeAndEmit();
|
|
66
|
+}
|
|
67
|
+
|
|
68
|
+
|
48
|
69
|
void
|
49
|
70
|
FontSizeSelector::applyToEngine(Engine* engine)
|
50
|
71
|
{
|
... |
... |
@@ -193,18 +214,19 @@ void |
193
|
214
|
FontSizeSelector::createConnections()
|
194
|
215
|
{
|
195
|
216
|
connect(sizeDoubleSpinBox_, QOverload<double>::of(&QDoubleSpinBox::valueChanged),
|
196
|
|
- this, &FontSizeSelector::valueChanged);
|
|
217
|
+ this, &FontSizeSelector::checkFixedSizeAndEmit);
|
197
|
218
|
connect(unitsComboBox_, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
198
|
219
|
this, &FontSizeSelector::checkUnits);
|
199
|
220
|
connect(dpiSpinBox_, QOverload<int>::of(&QSpinBox::valueChanged),
|
200
|
|
- this, &FontSizeSelector::valueChanged);
|
|
221
|
+ this, &FontSizeSelector::checkFixedSizeAndEmit);
|
201
|
222
|
}
|
202
|
223
|
|
203
|
224
|
|
204
|
225
|
void
|
205
|
226
|
FontSizeSelector::setDefaults(bool sizeOnly)
|
206
|
227
|
{
|
207
|
|
- sizeDoubleSpinBox_->setValue(20);
|
|
228
|
+ lastValue_ = 20;
|
|
229
|
+ sizeDoubleSpinBox_->setValue(lastValue_);
|
208
|
230
|
if (sizeOnly)
|
209
|
231
|
return;
|
210
|
232
|
dpiSpinBox_->setValue(96);
|
... |
... |
@@ -212,4 +234,51 @@ FontSizeSelector::setDefaults(bool sizeOnly) |
212
|
234
|
}
|
213
|
235
|
|
214
|
236
|
|
|
237
|
+void
|
|
238
|
+FontSizeSelector::checkFixedSizeAndEmit()
|
|
239
|
+{
|
|
240
|
+ if (bitmapOnly_ && !fixedSizes_.empty())
|
|
241
|
+ {
|
|
242
|
+ auto newValue = sizeDoubleSpinBox_->value();
|
|
243
|
+ auto intNewValue = static_cast<int>(newValue);
|
|
244
|
+ if (newValue != static_cast<double>(intNewValue))
|
|
245
|
+ {
|
|
246
|
+ sizeDoubleSpinBox_->setValue(intNewValue);
|
|
247
|
+ return; // Don't emit.
|
|
248
|
+ }
|
|
249
|
+
|
|
250
|
+ if (!std::binary_search(fixedSizes_.begin(), fixedSizes_.end(), newValue))
|
|
251
|
+ {
|
|
252
|
+ // Value not available, find next value.
|
|
253
|
+ if (intNewValue > lastValue_)
|
|
254
|
+ {
|
|
255
|
+ // find next larger value...
|
|
256
|
+ auto it = std::upper_bound(fixedSizes_.begin(), fixedSizes_.end(),
|
|
257
|
+ lastValue_);
|
|
258
|
+ if (it == fixedSizes_.end())
|
|
259
|
+ sizeDoubleSpinBox_->setValue(lastValue_);
|
|
260
|
+ else
|
|
261
|
+ sizeDoubleSpinBox_->setValue(*it);
|
|
262
|
+ }
|
|
263
|
+ else
|
|
264
|
+ {
|
|
265
|
+ // find next smaller value...
|
|
266
|
+ auto it = std::lower_bound(fixedSizes_.begin(), fixedSizes_.end(),
|
|
267
|
+ lastValue_);
|
|
268
|
+
|
|
269
|
+ // there's no element >= lastValue => all elements < last value
|
|
270
|
+ if (it == fixedSizes_.begin())
|
|
271
|
+ sizeDoubleSpinBox_->setValue(fixedSizes_.front());
|
|
272
|
+ else
|
|
273
|
+ sizeDoubleSpinBox_->setValue(*(it - 1));
|
|
274
|
+ }
|
|
275
|
+ return;
|
|
276
|
+ }
|
|
277
|
+ }
|
|
278
|
+
|
|
279
|
+ lastValue_ = sizeDoubleSpinBox_->value();
|
|
280
|
+ emit valueChanged();
|
|
281
|
+}
|
|
282
|
+
|
|
283
|
+
|
215
|
284
|
// end of fontsizeselector.cpp |
src/ftinspect/widgets/fontsizeselector.hpp
... |
... |
@@ -31,6 +31,7 @@ public: |
31
|
31
|
void setSizePixel(int sizePixel);
|
32
|
32
|
void setSizePoint(double sizePoint);
|
33
|
33
|
|
|
34
|
+ void reloadFromFont(Engine* engine);
|
34
|
35
|
void applyToEngine(Engine* engine);
|
35
|
36
|
void handleWheelResizeBySteps(int steps);
|
36
|
37
|
void handleWheelResizeFromGrid(QWheelEvent* event);
|
... |
... |
@@ -56,9 +57,15 @@ private: |
56
|
57
|
|
57
|
58
|
QHBoxLayout* layout_;
|
58
|
59
|
|
|
60
|
+ double lastValue_;
|
|
61
|
+ bool bitmapOnly_ = false;
|
|
62
|
+ std::vector<int> fixedSizes_;
|
|
63
|
+
|
59
|
64
|
void createLayout();
|
60
|
65
|
void createConnections();
|
61
|
66
|
void setDefaults(bool sizeOnly = false);
|
|
67
|
+
|
|
68
|
+ void checkFixedSizeAndEmit();
|
62
|
69
|
};
|
63
|
70
|
|
64
|
71
|
|
|