Charlie Jiang pushed to branch gsoc-2022-chariri-3 at FreeType / FreeType Demo Programs
Commits:
-
ac8ba78e
by Charlie Jiang at 2022-09-06T11:21:19+08:00
-
98bb151e
by Charlie Jiang at 2022-09-06T12:03:29+08:00
-
974c8a37
by Charlie Jiang at 2022-09-06T12:19:43+08:00
-
e9b3fdca
by Charlie Jiang at 2022-09-06T12:20:25+08:00
7 changed files:
- src/ftinspect/engine/rendering.cpp
- src/ftinspect/engine/rendering.hpp
- src/ftinspect/glyphcomponents/glyphcontinuous.cpp
- src/ftinspect/models/fontinfomodels.cpp
- src/ftinspect/panels/info.cpp
- src/ftinspect/panels/settingpanel.cpp
- src/ftinspect/widgets/charmapcombobox.cpp
Changes:
... | ... | @@ -5,6 +5,9 @@ |
5 | 5 | #include "rendering.hpp"
|
6 | 6 | |
7 | 7 | #include <cmath>
|
8 | +#include <QPixmap>
|
|
9 | +#include <QPainter>
|
|
10 | + |
|
8 | 11 | #include <freetype/ftbitmap.h>
|
9 | 12 | |
10 | 13 | #include "engine.hpp"
|
... | ... | @@ -446,6 +449,21 @@ RenderingEngine::tryDirectRenderColorLayers(int glyphIndex, |
446 | 449 | }
|
447 | 450 | |
448 | 451 | |
452 | +QPixmap
|
|
453 | +RenderingEngine::padToSize(QImage* image, int ppem)
|
|
454 | +{
|
|
455 | + auto width = std::max(image->width(), ppem);
|
|
456 | + auto height = std::max(image->height(), ppem);
|
|
457 | + auto result = QPixmap(width, height);
|
|
458 | + result.fill(backgroundColor_);
|
|
459 | + QPainter painter(&result);
|
|
460 | + auto pos = QPoint { width / 2 - image->width() / 2,
|
|
461 | + height / 2 - image->height() / 2};
|
|
462 | + painter.drawImage(pos, *image);
|
|
463 | + return result;
|
|
464 | +}
|
|
465 | + |
|
466 | + |
|
449 | 467 | void
|
450 | 468 | convertLCDToARGB(FT_Bitmap& bitmap,
|
451 | 469 | QImage& image,
|
... | ... | @@ -48,6 +48,8 @@ public: |
48 | 48 | QRect* outRect,
|
49 | 49 | bool inverseRectY = false);
|
50 | 50 | |
51 | + QPixmap padToSize(QImage* image, int ppem);
|
|
52 | + |
|
51 | 53 | private:
|
52 | 54 | Engine* engine_;
|
53 | 55 |
... | ... | @@ -552,6 +552,7 @@ GlyphContinuous::drawCacheGlyph(QPainter* painter, |
552 | 552 | // Well, metrics is also part of the cache...
|
553 | 553 | int width = entry.advance.x ? entry.advance.x >> 16
|
554 | 554 | : entry.nonSpacingPlaceholder;
|
555 | + auto xOffset = 0;
|
|
555 | 556 | |
556 | 557 | if (entry.advance.x == 0
|
557 | 558 | && !stringRenderer_.isWaterfall()
|
... | ... | @@ -562,10 +563,11 @@ GlyphContinuous::drawCacheGlyph(QPainter* painter, |
562 | 563 | squarePoint.setY(squarePoint.y() - width);
|
563 | 564 | auto rect = QRect(squarePoint, QSize(width, width));
|
564 | 565 | painter->fillRect(rect, Qt::red);
|
566 | + xOffset = width; // let the glyph be drawn on the red square
|
|
565 | 567 | }
|
566 | 568 | |
567 | 569 | QRect rect = entry.basePosition;
|
568 | - rect.moveLeft(rect.x() + sizeIndicatorOffset_);
|
|
570 | + rect.moveLeft(rect.x() + sizeIndicatorOffset_ + xOffset);
|
|
569 | 571 | rect.translate(positionDelta_);
|
570 | 572 | |
571 | 573 | if (colorInverted)
|
... | ... | @@ -589,8 +591,26 @@ GlyphContinuous::findGlyphByMouse(QPoint position, |
589 | 591 | for (auto& entry : line.entries)
|
590 | 592 | {
|
591 | 593 | auto rect = entry.basePosition;
|
594 | + auto rect2 = QRect();
|
|
592 | 595 | rect.moveLeft(rect.x() + line.sizeIndicatorOffset);
|
593 | - if (rect.contains(position))
|
|
596 | + |
|
597 | + if (entry.advance.x == 0
|
|
598 | + && !stringRenderer_.isWaterfall()
|
|
599 | + && source_ == SRC_AllGlyphs)
|
|
600 | + {
|
|
601 | + // Consider the red square
|
|
602 | + int width = static_cast<int>(entry.nonSpacingPlaceholder);
|
|
603 | + if (width < 0)
|
|
604 | + continue;
|
|
605 | + |
|
606 | + auto squarePoint = entry.penPos;
|
|
607 | + squarePoint.setY(squarePoint.y() - width);
|
|
608 | + |
|
609 | + rect2 = QRect(squarePoint, QSize(width, width));
|
|
610 | + rect.moveLeft(rect.x() + width);
|
|
611 | + }
|
|
612 | +
|
|
613 | + if (rect.contains(position) || rect2.contains(position))
|
|
594 | 614 | {
|
595 | 615 | if (outSizePoint)
|
596 | 616 | *outSizePoint = line.sizePoint;
|
... | ... | @@ -30,7 +30,8 @@ FixedSizeInfoModel::data(const QModelIndex& index, |
30 | 30 | if (index.row() < 0 || index.column() < 0)
|
31 | 31 | return {};
|
32 | 32 | auto r = static_cast<size_t>(index.row());
|
33 | - if (role != Qt::DisplayRole || r > storage_.size())
|
|
33 | + if ((role != Qt::DisplayRole && role != Qt::ToolTipRole)
|
|
34 | + || r > storage_.size())
|
|
34 | 35 | return {};
|
35 | 36 | |
36 | 37 | auto& obj = storage_[r];
|
... | ... | @@ -112,18 +113,19 @@ CharMapInfoModel::data(const QModelIndex& index, |
112 | 113 | if (index.row() < 0 || index.column() < 0)
|
113 | 114 | return {};
|
114 | 115 | auto r = static_cast<size_t>(index.row());
|
115 | - if (role != Qt::DisplayRole || r > storage_.size())
|
|
116 | + if ((role != Qt::DisplayRole && role != Qt::ToolTipRole)
|
|
117 | + || r > storage_.size())
|
|
116 | 118 | return {};
|
117 | 119 | |
118 | 120 | auto& obj = storage_[r];
|
119 | 121 | switch (static_cast<Columns>(index.column()))
|
120 | 122 | {
|
121 | 123 | case CMIM_Platform:
|
122 | - return QString("%1 <%2>")
|
|
124 | + return QString("%1 {%2}")
|
|
123 | 125 | .arg(obj.platformID)
|
124 | 126 | .arg(*mapTTPlatformIDToName(obj.platformID));
|
125 | 127 | case CMIM_Encoding:
|
126 | - return QString("%1 <%2>")
|
|
128 | + return QString("%1 {%2}")
|
|
127 | 129 | .arg(obj.encodingID)
|
128 | 130 | .arg(*obj.encodingName);
|
129 | 131 | case CMIM_FormatID:
|
... | ... | @@ -201,7 +203,8 @@ SFNTNameModel::data(const QModelIndex& index, |
201 | 203 | return tr("Double click to view the string.");
|
202 | 204 | |
203 | 205 | auto r = static_cast<size_t>(index.row());
|
204 | - if (role != Qt::DisplayRole || r > storage_.size())
|
|
206 | + if ((role != Qt::DisplayRole && role != Qt::ToolTipRole)
|
|
207 | + || r > storage_.size())
|
|
205 | 208 | return {};
|
206 | 209 | |
207 | 210 | auto& obj = storage_[r];
|
... | ... | @@ -210,25 +213,25 @@ SFNTNameModel::data(const QModelIndex& index, |
210 | 213 | case SNM_Name:
|
211 | 214 | if (obj.nameID >= 256)
|
212 | 215 | return QString::number(obj.nameID);
|
213 | - return QString("%1 <%2>")
|
|
214 | - .arg(obj.nameID)
|
|
215 | - .arg(*mapSFNTNameIDToName(obj.nameID));
|
|
216 | + return QString("%1 {%2}").arg(QString::number(obj.nameID),
|
|
217 | + *mapSFNTNameIDToName(obj.nameID));
|
|
218 | +
|
|
216 | 219 | case SNM_Platform:
|
217 | - return QString("%1 <%2>")
|
|
220 | + return QString("%1 {%2}")
|
|
218 | 221 | .arg(obj.platformID)
|
219 | 222 | .arg(*mapTTPlatformIDToName(obj.platformID));
|
220 | 223 | case SNM_Encoding:
|
221 | - return QString("%1 <%2>")
|
|
224 | + return QString("%1 {%2}")
|
|
222 | 225 | .arg(obj.encodingID)
|
223 | 226 | .arg(*mapTTEncodingIDToName(obj.platformID, obj.encodingID));
|
224 | 227 | case SNM_Language:
|
225 | 228 | if (obj.languageID >= 0x8000)
|
226 | 229 | return obj.langTag + "(lang tag)";
|
227 | 230 | if (obj.platformID == 3)
|
228 | - return QString("0x%1 <%2>")
|
|
231 | + return QString("0x%1 {%2}")
|
|
229 | 232 | .arg(obj.languageID, 4, 16, QChar('0'))
|
230 | 233 | .arg(*mapTTLanguageIDToName(obj.platformID, obj.languageID));
|
231 | - return QString("%1 <%2>")
|
|
234 | + return QString("%1 {%2}")
|
|
232 | 235 | .arg(obj.languageID)
|
233 | 236 | .arg(*mapTTLanguageIDToName(obj.platformID, obj.languageID));
|
234 | 237 | case SNM_Content:
|
... | ... | @@ -310,7 +313,8 @@ SFNTTableInfoModel::data(const QModelIndex& index, |
310 | 313 | if (index.row() < 0 || index.column() < 0)
|
311 | 314 | return {};
|
312 | 315 | auto r = static_cast<size_t>(index.row());
|
313 | - if (role != Qt::DisplayRole || r > storage_.size())
|
|
316 | + if ((role != Qt::DisplayRole && role != Qt::ToolTipRole)
|
|
317 | + || r > storage_.size())
|
|
314 | 318 | return {};
|
315 | 319 | |
316 | 320 | auto& obj = storage_[r];
|
... | ... | @@ -405,7 +409,8 @@ MMGXAxisInfoModel::data(const QModelIndex& index, |
405 | 409 | if (index.row() < 0 || index.column() < 0)
|
406 | 410 | return {};
|
407 | 411 | auto r = static_cast<size_t>(index.row());
|
408 | - if (role != Qt::DisplayRole || r > storage_.size())
|
|
412 | + if ((role != Qt::DisplayRole && role != Qt::ToolTipRole)
|
|
413 | + || r > storage_.size())
|
|
409 | 414 | return {};
|
410 | 415 | |
411 | 416 | auto& obj = storage_[r];
|
... | ... | @@ -618,7 +623,7 @@ CompositeGlyphsInfoModel::data(const QModelIndex& index, |
618 | 623 | {
|
619 | 624 | case CGIM_Glyph:
|
620 | 625 | if (engine_->currentFontHasGlyphName())
|
621 | - return QString("%1 <%2>").arg(glyphIdx).arg(engine_->glyphName(glyphIdx));
|
|
626 | + return QString("%1 {%2}").arg(glyphIdx).arg(engine_->glyphName(glyphIdx));
|
|
622 | 627 | return QString::number(glyphIdx);
|
623 | 628 | case CGIM_Flag:
|
624 | 629 | if (!n.subGlyphInfo)
|
... | ... | @@ -724,7 +729,7 @@ CompositeGlyphsInfoModel::renderIcon(int glyphIndex) const |
724 | 729 | if (!image)
|
725 | 730 | return {};
|
726 | 731 | |
727 | - auto result = QPixmap::fromImage(*image);
|
|
732 | + auto result = engine_->renderingEngine()->padToSize(image, 20);
|
|
728 | 733 | delete image;
|
729 | 734 | return result;
|
730 | 735 | }
|
... | ... | @@ -53,6 +53,7 @@ InfoTab::createLayout() |
53 | 53 | tabs_.append(generalTab_);
|
54 | 54 | tabs_.append(sfntTab_);
|
55 | 55 | tabs_.append(postScriptTab_);
|
56 | + tabs_.append(mmgxTab_);
|
|
56 | 57 | tabs_.append(compositeGlyphsTab_);
|
57 | 58 | |
58 | 59 | layout_ = new QHBoxLayout;
|
... | ... | @@ -885,6 +886,8 @@ MMGXInfoTab::reloadFont() |
885 | 886 | axesModel_->storage() = engine_->currentFontMMGXAxes();
|
886 | 887 | axesModel_->endModelUpdate();
|
887 | 888 | }
|
889 | + |
|
890 | + setEnabled(state != MMGXState::NoMMGX);
|
|
888 | 891 | }
|
889 | 892 | |
890 | 893 |
... | ... | @@ -298,11 +298,11 @@ SettingPanel::populatePalettes() |
298 | 298 | QSignalBlocker blocker(paletteComboBox_);
|
299 | 299 | paletteComboBox_->clear();
|
300 | 300 | for (int i = 0; i < newSize; ++i)
|
301 | - paletteComboBox_->addItem(
|
|
302 | - QString("%1: %2")
|
|
303 | - .arg(i)
|
|
304 | - .arg(newPalettes[i].name),
|
|
305 | - newPalettes[i].name);
|
|
301 | + {
|
|
302 | + auto str = QString("%1: %2").arg(i).arg(newPalettes[i].name);
|
|
303 | + paletteComboBox_->addItem(str, newPalettes[i].name);
|
|
304 | + paletteComboBox_->setItemData(i, str, Qt::ToolTipRole);
|
|
305 | + }
|
|
306 | 306 | }
|
307 | 307 | |
308 | 308 | emit fontReloadNeeded();
|
... | ... | @@ -77,23 +77,24 @@ CharMapComboBox::repopulate(std::vector<CharMapInfo>& charMaps) |
77 | 77 | addItem(tr("Glyph Order"));
|
78 | 78 | setItemData(0, 0u, EncodingRole);
|
79 | 79 | }
|
80 | - |
|
81 | - int i = 0;
|
|
80 | +
|
|
82 | 81 | int newIndex = 0;
|
83 | 82 | for (auto& map : charMaps)
|
84 | 83 | {
|
85 | - addItem(tr("%1: %2 (platform %3, encoding %4)")
|
|
86 | - .arg(i)
|
|
87 | - .arg(*map.encodingName)
|
|
88 | - .arg(map.platformID)
|
|
89 | - .arg(map.encodingID));
|
|
84 | + auto i = count();
|
|
85 | + auto str = tr("%1: %2 (platform %3, encoding %4)")
|
|
86 | + .arg(i)
|
|
87 | + .arg(*map.encodingName)
|
|
88 | + .arg(map.platformID)
|
|
89 | + .arg(map.encodingID);
|
|
90 | + addItem(str);
|
|
91 | + setItemData(i, str, Qt::ToolTipRole);
|
|
92 | + |
|
90 | 93 | auto encoding = static_cast<unsigned>(map.encoding);
|
91 | - setItemData(haveGlyphOrder_ ? i + 1 : i, encoding, EncodingRole);
|
|
94 | + setItemData(i, encoding, EncodingRole);
|
|
92 | 95 | |
93 | 96 | if (encoding == oldEncoding && i == oldIndex)
|
94 | 97 | newIndex = i;
|
95 | -
|
|
96 | - i++;
|
|
97 | 98 | }
|
98 | 99 | |
99 | 100 | // this shouldn't emit any event either, because force repainting
|