Charlie Jiang pushed to branch gsoc-2022-chariri-2 at FreeType / FreeType Demo Programs
Commits:
-
38f60d85
by Charlie Jiang at 2022-07-14T16:31:43+08:00
-
60bb6f09
by Charlie Jiang at 2022-07-14T17:04:02+08:00
-
6f5ac858
by Charlie Jiang at 2022-07-14T17:28:47+08:00
-
a42eb7f9
by Charlie Jiang at 2022-07-15T14:23:36+08:00
-
7b4fb4f7
by Charlie Jiang at 2022-07-15T14:31:57+08:00
-
469443c4
by Charlie Jiang at 2022-07-15T14:53:34+08:00
-
a1c3e64a
by Charlie Jiang at 2022-07-15T16:01:09+08:00
-
c09da8bc
by Charlie Jiang at 2022-07-15T18:29:07+08:00
12 changed files:
- src/ftinspect/engine/engine.cpp
- src/ftinspect/engine/engine.hpp
- src/ftinspect/panels/continuous.cpp
- src/ftinspect/panels/continuous.hpp
- src/ftinspect/panels/singular.cpp
- src/ftinspect/panels/singular.hpp
- src/ftinspect/rendering/glyphbitmap.cpp
- src/ftinspect/rendering/glyphcontinuous.cpp
- src/ftinspect/rendering/glyphcontinuous.hpp
- src/ftinspect/rendering/grid.cpp
- src/ftinspect/rendering/renderutils.cpp
- src/ftinspect/rendering/renderutils.hpp
Changes:
... | ... | @@ -384,7 +384,10 @@ Engine::glyphName(int index) |
384 | 384 | if (index < 0)
|
385 | 385 | throw std::runtime_error("Invalid glyph index");
|
386 | 386 | |
387 | - if (!FTC_Manager_LookupSize(cacheManager_, &scaler_, &ftSize_))
|
|
387 | + if (!ftSize_)
|
|
388 | + return "";
|
|
389 | + |
|
390 | + if (!FTC_Manager_LookupSize(cacheManager_, &scaler_, &ftSize_))
|
|
388 | 391 | return name;
|
389 | 392 | |
390 | 393 | if (ftSize_ && FT_HAS_GLYPH_NAMES(ftSize_->face))
|
... | ... | @@ -409,6 +412,9 @@ Engine::loadGlyph(int glyphIndex) |
409 | 412 | if (glyphIndex < 0)
|
410 | 413 | throw std::runtime_error("Invalid glyph index");
|
411 | 414 | |
415 | + if (curNumGlyphs_ <= 0)
|
|
416 | + return NULL;
|
|
417 | + |
|
412 | 418 | FT_Glyph glyph;
|
413 | 419 | |
414 | 420 | // the `scaler' object is set up by the
|
... | ... | @@ -447,7 +453,7 @@ Engine::loadGlyphWithoutUpdate(int glyphIndex) |
447 | 453 | |
448 | 454 | |
449 | 455 | bool
|
450 | -Engine::glyphToBitmap(FT_Glyph src,
|
|
456 | +Engine::convertGlyphToBitmapGlyph(FT_Glyph src,
|
|
451 | 457 | FT_Glyph* out)
|
452 | 458 | {
|
453 | 459 | if (src->format == FT_GLYPH_FORMAT_BITMAP)
|
... | ... | @@ -733,16 +739,11 @@ convertLCDVToARGB(FT_Bitmap& bitmap, |
733 | 739 | |
734 | 740 | |
735 | 741 | QImage*
|
736 | -Engine::convertBitmapToQImage(FT_Glyph src,
|
|
737 | - QRect* outRect)
|
|
742 | +Engine::convertBitmapToQImage(FT_Bitmap* src)
|
|
738 | 743 | {
|
739 | 744 | QImage* result = NULL;
|
740 | - FT_BitmapGlyph bitmapGlyph;
|
|
741 | - bool ownBitmapGlyph
|
|
742 | - = glyphToBitmap(src, reinterpret_cast<FT_Glyph*>(&bitmapGlyph));
|
|
743 | - if (!bitmapGlyph)
|
|
744 | - return result;
|
|
745 | - auto& bmap = bitmapGlyph->bitmap;
|
|
745 | +
|
|
746 | + auto& bmap = *src;
|
|
746 | 747 | bool ownBitmap = false;
|
747 | 748 | |
748 | 749 | int width = bmap.width;
|
... | ... | @@ -763,14 +764,6 @@ Engine::convertBitmapToQImage(FT_Glyph src, |
763 | 764 | else if (bmap.pixel_mode == FT_PIXEL_MODE_LCD_V)
|
764 | 765 | height /= 3;
|
765 | 766 | |
766 | - if (outRect)
|
|
767 | - {
|
|
768 | - outRect->setLeft(bitmapGlyph->left);
|
|
769 | - outRect->setTop(-bitmapGlyph->top);
|
|
770 | - outRect->setWidth(width);
|
|
771 | - outRect->setHeight(height);
|
|
772 | - }
|
|
773 | - |
|
774 | 767 | switch (bmap.pixel_mode)
|
775 | 768 | {
|
776 | 769 | case FT_PIXEL_MODE_MONO:
|
... | ... | @@ -821,8 +814,6 @@ Engine::convertBitmapToQImage(FT_Glyph src, |
821 | 814 | }
|
822 | 815 | |
823 | 816 | cleanup:
|
824 | - if (ownBitmapGlyph)
|
|
825 | - FT_Done_Glyph(reinterpret_cast<FT_Glyph>(bitmapGlyph));
|
|
826 | 817 | if (ownBitmap)
|
827 | 818 | FT_Bitmap_Done(library_, &bmap);
|
828 | 819 | |
... | ... | @@ -830,6 +821,56 @@ cleanup: |
830 | 821 | }
|
831 | 822 | |
832 | 823 | |
824 | +QImage*
|
|
825 | +Engine::convertGlyphToQImage(FT_Glyph src, QRect* outRect)
|
|
826 | +{
|
|
827 | + FT_BitmapGlyph bitmapGlyph;
|
|
828 | + bool ownBitmapGlyph
|
|
829 | + = convertGlyphToBitmapGlyph(src, reinterpret_cast<FT_Glyph*>(&bitmapGlyph));
|
|
830 | + if (!bitmapGlyph)
|
|
831 | + return NULL;
|
|
832 | + |
|
833 | + auto result = convertBitmapToQImage(&bitmapGlyph->bitmap);
|
|
834 | + |
|
835 | + if (result && outRect)
|
|
836 | + {
|
|
837 | + outRect->setLeft(bitmapGlyph->left);
|
|
838 | + outRect->setTop(-bitmapGlyph->top);
|
|
839 | + outRect->setWidth(bitmapGlyph->bitmap.width);
|
|
840 | + outRect->setHeight(bitmapGlyph->bitmap.rows);
|
|
841 | + }
|
|
842 | + |
|
843 | + if (ownBitmapGlyph)
|
|
844 | + FT_Done_Glyph(reinterpret_cast<FT_Glyph>(bitmapGlyph));
|
|
845 | + |
|
846 | + return result;
|
|
847 | +}
|
|
848 | + |
|
849 | + |
|
850 | +QPoint
|
|
851 | +Engine::computeGlyphOffset(FT_Glyph glyph)
|
|
852 | +{
|
|
853 | + if (glyph->format == FT_GLYPH_FORMAT_OUTLINE)
|
|
854 | + {
|
|
855 | + FT_BBox cbox;
|
|
856 | + FT_Outline_Get_CBox(&reinterpret_cast<FT_OutlineGlyph>(glyph)->outline,
|
|
857 | + &cbox);
|
|
858 | + cbox.xMin &= ~63;
|
|
859 | + cbox.yMin &= ~63;
|
|
860 | + cbox.xMax = (cbox.xMax + 63) & ~63;
|
|
861 | + cbox.yMax = (cbox.yMax + 63) & ~63;
|
|
862 | + return { cbox.xMin / 64, -cbox.yMax / 64 };
|
|
863 | + }
|
|
864 | + if (glyph->format == FT_GLYPH_FORMAT_BITMAP)
|
|
865 | + {
|
|
866 | + auto bg = reinterpret_cast<FT_BitmapGlyph>(glyph);
|
|
867 | + return { bg->left, -bg->top };
|
|
868 | + }
|
|
869 | + |
|
870 | + return {};
|
|
871 | +}
|
|
872 | + |
|
873 | + |
|
833 | 874 | QHash<FT_Glyph_Format, QString> glyphFormatNamesCache;
|
834 | 875 | QHash<FT_Glyph_Format, QString>&
|
835 | 876 | glyphFormatNames()
|
... | ... | @@ -841,7 +882,9 @@ glyphFormatNames() |
841 | 882 | glyphFormatNamesCache[FT_GLYPH_FORMAT_BITMAP] = "Bitmap";
|
842 | 883 | glyphFormatNamesCache[FT_GLYPH_FORMAT_OUTLINE] = "Outline";
|
843 | 884 | glyphFormatNamesCache[FT_GLYPH_FORMAT_PLOTTER] = "Plotter";
|
885 | +#if FREETYPE_MINOR >= 12
|
|
844 | 886 | glyphFormatNamesCache[FT_GLYPH_FORMAT_SVG] = "SVG";
|
887 | +#endif
|
|
845 | 888 | }
|
846 | 889 | return glyphFormatNamesCache;
|
847 | 890 | }
|
... | ... | @@ -105,9 +105,11 @@ public: |
105 | 105 | |
106 | 106 | // Return `true` if you need to free `out`
|
107 | 107 | // `out` will be set to NULL in cases of error
|
108 | - bool glyphToBitmap(FT_Glyph src, FT_Glyph* out);
|
|
108 | + bool convertGlyphToBitmapGlyph(FT_Glyph src, FT_Glyph* out);
|
|
109 | 109 | FT_Bitmap convertBitmapTo8Bpp(FT_Bitmap* bitmap);
|
110 | - QImage* convertBitmapToQImage(FT_Glyph src, QRect* outRect);
|
|
110 | + QImage* convertBitmapToQImage(FT_Bitmap* src);
|
|
111 | + QImage* convertGlyphToQImage(FT_Glyph src, QRect* outRect);
|
|
112 | + QPoint computeGlyphOffset(FT_Glyph glyph);
|
|
111 | 113 | |
112 | 114 | // reload current triplet, but with updated settings, useful for updating
|
113 | 115 | // `ftSize_` only
|
... | ... | @@ -13,6 +13,14 @@ ContinuousTab::ContinuousTab(QWidget* parent, |
13 | 13 | : QWidget(parent), engine_(engine)
|
14 | 14 | {
|
15 | 15 | createLayout();
|
16 | + |
|
17 | + std::vector<CharMapInfo> tempCharMaps;
|
|
18 | + setCharMaps(tempCharMaps); // pass in an empty one
|
|
19 | + |
|
20 | + checkMode();
|
|
21 | + checkSource();
|
|
22 | + setDefaults();
|
|
23 | + |
|
16 | 24 | createConnections();
|
17 | 25 | }
|
18 | 26 | |
... | ... | @@ -22,7 +30,7 @@ ContinuousTab::repaintGlyph() |
22 | 30 | {
|
23 | 31 | sizeSelector_->applyToEngine(engine_);
|
24 | 32 |
|
25 | - updateFromCurrentSubTab();
|
|
33 | + syncSettings();
|
|
26 | 34 | canvas_->repaint();
|
27 | 35 | }
|
28 | 36 | |
... | ... | @@ -31,181 +39,36 @@ void |
31 | 39 | ContinuousTab::reloadFont()
|
32 | 40 | {
|
33 | 41 | currentGlyphCount_ = engine_->currentFontNumberOfGlyphs();
|
34 | - updateCurrentSubTab();
|
|
42 | + setGlyphCount(qBound(0, currentGlyphCount_, INT_MAX));
|
|
43 | + setCharMaps(engine_->currentFontCharMaps());
|
|
35 | 44 | repaintGlyph();
|
36 | 45 | }
|
37 | 46 | |
38 | 47 | |
39 | 48 | void
|
40 | -ContinuousTab::changeTab()
|
|
41 | -{
|
|
42 | - updateCurrentSubTab();
|
|
43 | - repaintGlyph();
|
|
44 | -}
|
|
45 | - |
|
46 | - |
|
47 | -void
|
|
48 | -ContinuousTab::wheelNavigate(int steps)
|
|
49 | -{
|
|
50 | - if (tabWidget_->currentIndex() == AllGlyphs)
|
|
51 | - allGlyphsTab_->setGlyphBeginindex(allGlyphsTab_->glyphBeginindex()
|
|
52 | - + steps);
|
|
53 | -}
|
|
54 | - |
|
55 | - |
|
56 | -void
|
|
57 | -ContinuousTab::wheelResize(int steps)
|
|
58 | -{
|
|
59 | - sizeSelector_->handleWheelResizeBySteps(steps);
|
|
60 | -}
|
|
61 | - |
|
62 | - |
|
63 | -void
|
|
64 | -ContinuousTab::createLayout()
|
|
65 | -{
|
|
66 | - canvas_ = new GlyphContinuous(this, engine_);
|
|
67 | - sizeSelector_ = new FontSizeSelector(this);
|
|
68 | - allGlyphsTab_ = new ContinousAllGlyphsTab(this);
|
|
69 | - |
|
70 | - tabWidget_ = new QTabWidget(this);
|
|
71 | - tabWidget_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
|
|
72 | - // Must be in sync with `Tabs` enum.
|
|
73 | - tabWidget_->addTab(allGlyphsTab_, tr("All Glyphs"));
|
|
74 | - |
|
75 | - mainLayout_ = new QVBoxLayout;
|
|
76 | - mainLayout_->addWidget(canvas_);
|
|
77 | - mainLayout_->addWidget(sizeSelector_);
|
|
78 | - mainLayout_->addWidget(tabWidget_);
|
|
79 | - |
|
80 | - setLayout(mainLayout_);
|
|
81 | -}
|
|
82 | - |
|
83 | - |
|
84 | -void
|
|
85 | -ContinuousTab::createConnections()
|
|
86 | -{
|
|
87 | - connect(tabWidget_, &QTabWidget::currentChanged,
|
|
88 | - this, &ContinuousTab::changeTab);
|
|
89 | - |
|
90 | - connect(allGlyphsTab_, &ContinousAllGlyphsTab::changed,
|
|
91 | - this, &ContinuousTab::repaintGlyph);
|
|
92 | - |
|
93 | - connect(sizeSelector_, &FontSizeSelector::valueChanged,
|
|
94 | - this, &ContinuousTab::repaintGlyph);
|
|
95 | - |
|
96 | - connect(canvas_, &GlyphContinuous::wheelResize,
|
|
97 | - this, &ContinuousTab::wheelResize);
|
|
98 | - connect(canvas_, &GlyphContinuous::wheelNavigate,
|
|
99 | - this, &ContinuousTab::wheelNavigate);
|
|
100 | - connect(canvas_, &GlyphContinuous::displayingCountUpdated,
|
|
101 | - allGlyphsTab_, &ContinousAllGlyphsTab::setDisplayingCount);
|
|
102 | -}
|
|
103 | - |
|
104 | - |
|
105 | -void
|
|
106 | -ContinuousTab::updateCurrentSubTab()
|
|
49 | +ContinuousTab::syncSettings()
|
|
107 | 50 | {
|
108 | - switch (tabWidget_->currentIndex())
|
|
109 | - {
|
|
110 | - case AllGlyphs:
|
|
111 | - allGlyphsTab_->setGlyphCount(qBound(0,
|
|
112 | - currentGlyphCount_,
|
|
113 | - INT_MAX));
|
|
114 | - allGlyphsTab_->setCharMaps(engine_->currentFontCharMaps());
|
|
115 | - break;
|
|
116 | - }
|
|
117 | -}
|
|
118 | - |
|
119 | - |
|
120 | -void
|
|
121 | -ContinuousTab::updateFromCurrentSubTab()
|
|
122 | -{
|
|
123 | - switch (tabWidget_->currentIndex())
|
|
124 | - {
|
|
125 | - case AllGlyphs:
|
|
126 | - canvas_->setMode(GlyphContinuous::AllGlyphs);
|
|
127 | - canvas_->setSubModeAllGlyphs(allGlyphsTab_->subMode());
|
|
128 | - // Begin index is selected from All Glyphs subtab,
|
|
129 | - // and Limit index is calculated by All Glyphs subtab
|
|
130 | - canvas_->setBeginIndex(allGlyphsTab_->glyphBeginindex());
|
|
131 | - canvas_->setLimitIndex(allGlyphsTab_->glyphLimitIndex());
|
|
132 | - canvas_->setCharMapIndex(allGlyphsTab_->charMapIndex());
|
|
133 | - |
|
134 | - canvas_->setFancyParams(allGlyphsTab_->xEmboldening(),
|
|
135 | - allGlyphsTab_->yEmboldening(),
|
|
136 | - allGlyphsTab_->slanting());
|
|
137 | - canvas_->setStrokeRadius(allGlyphsTab_->strokeRadius());
|
|
138 | - break;
|
|
139 | - }
|
|
140 | -}
|
|
141 | - |
|
142 | - |
|
143 | -ContinousAllGlyphsTab::ContinousAllGlyphsTab(QWidget* parent)
|
|
144 | -: QWidget(parent)
|
|
145 | -{
|
|
146 | - createLayout();
|
|
147 | - |
|
148 | - std::vector<CharMapInfo> tempCharMaps;
|
|
149 | - setCharMaps(tempCharMaps); // pass in an empty one
|
|
150 | - |
|
151 | - checkSubMode();
|
|
152 | - setDefaults();
|
|
153 | - createConnections();
|
|
154 | -}
|
|
155 | - |
|
156 | - |
|
157 | -int
|
|
158 | -ContinousAllGlyphsTab::glyphBeginindex()
|
|
159 | -{
|
|
160 | - return indexSelector_->currentIndex();
|
|
51 | + auto mode = static_cast<GlyphContinuous::Mode>(modeSelector_->currentIndex());
|
|
52 | + auto src
|
|
53 | + = static_cast<GlyphContinuous::Source>(sourceSelector_->currentIndex());
|
|
54 | + canvas_->setMode(mode);
|
|
55 | + canvas_->setSource(src);
|
|
56 | + canvas_->setBeginIndex(indexSelector_->currentIndex());
|
|
57 | + canvas_->setLimitIndex(glyphLimitIndex_);
|
|
58 | + canvas_->setCharMapIndex(charMapIndex()); // Not directly from the combo box
|
|
59 | + |
|
60 | + canvas_->setFancyParams(xEmboldeningSpinBox_->value(),
|
|
61 | + yEmboldeningSpinBox_->value(),
|
|
62 | + slantSpinBox_->value());
|
|
63 | + canvas_->setStrokeRadius(strokeRadiusSpinBox_->value());
|
|
64 | + canvas_->setRotation(rotationSpinBox_->value());
|
|
65 | + canvas_->setWaterfall(waterfallCheckBox_->isChecked());
|
|
66 | + canvas_->setVertical(verticalCheckBox_->isChecked());
|
|
161 | 67 | }
|
162 | 68 | |
163 | 69 | |
164 | 70 | int
|
165 | -ContinousAllGlyphsTab::glyphLimitIndex()
|
|
166 | -{
|
|
167 | - return glyphLimitIndex_;
|
|
168 | -}
|
|
169 | - |
|
170 | - |
|
171 | -GlyphContinuous::SubModeAllGlyphs
|
|
172 | -ContinousAllGlyphsTab::subMode()
|
|
173 | -{
|
|
174 | - return static_cast<GlyphContinuous::SubModeAllGlyphs>(
|
|
175 | - modeSelector_->currentIndex());
|
|
176 | -}
|
|
177 | - |
|
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 | - |
|
207 | -int
|
|
208 | -ContinousAllGlyphsTab::charMapIndex()
|
|
71 | +ContinuousTab::charMapIndex()
|
|
209 | 72 | {
|
210 | 73 | auto index = charMapSelector_->currentIndex() - 1;
|
211 | 74 | if (index <= -1)
|
... | ... | @@ -217,30 +80,30 @@ ContinousAllGlyphsTab::charMapIndex() |
217 | 80 | |
218 | 81 | |
219 | 82 | void
|
220 | -ContinousAllGlyphsTab::setGlyphBeginindex(int index)
|
|
83 | +ContinuousTab::setGlyphCount(int count)
|
|
221 | 84 | {
|
222 | - indexSelector_->setCurrentIndex(index);
|
|
85 | + currentGlyphCount_ = count;
|
|
86 | + updateLimitIndex();
|
|
223 | 87 | }
|
224 | 88 | |
225 | 89 | |
226 | 90 | void
|
227 | -ContinousAllGlyphsTab::setGlyphCount(int count)
|
|
91 | +ContinuousTab::setDisplayingCount(int count)
|
|
228 | 92 | {
|
229 | - currentGlyphCount_ = count;
|
|
230 | - updateLimitIndex();
|
|
93 | + indexSelector_->setShowingCount(count);
|
|
231 | 94 | }
|
232 | 95 | |
233 | 96 | |
234 | 97 | void
|
235 | -ContinousAllGlyphsTab::setDisplayingCount(int count)
|
|
98 | +ContinuousTab::setGlyphBeginindex(int index)
|
|
236 | 99 | {
|
237 | - indexSelector_->setShowingCount(count);
|
|
100 | + indexSelector_->setCurrentIndex(index);
|
|
238 | 101 | }
|
239 | 102 | |
240 | 103 | |
241 | 104 | #define EncodingRole (Qt::UserRole + 10)
|
242 | 105 | void
|
243 | -ContinousAllGlyphsTab::setCharMaps(std::vector<CharMapInfo>& charMaps)
|
|
106 | +ContinuousTab::setCharMaps(std::vector<CharMapInfo>& charMaps)
|
|
244 | 107 | {
|
245 | 108 | charMaps_ = charMaps;
|
246 | 109 | int oldIndex = charMapSelector_->currentIndex();
|
... | ... | @@ -287,7 +150,7 @@ ContinousAllGlyphsTab::setCharMaps(std::vector<CharMapInfo>& charMaps) |
287 | 150 | |
288 | 151 | |
289 | 152 | void
|
290 | -ContinousAllGlyphsTab::updateLimitIndex()
|
|
153 | +ContinuousTab::updateLimitIndex()
|
|
291 | 154 | {
|
292 | 155 | if (charMapSelector_->currentIndex() <= 0)
|
293 | 156 | glyphLimitIndex_ = currentGlyphCount_;
|
... | ... | @@ -299,49 +162,127 @@ ContinousAllGlyphsTab::updateLimitIndex() |
299 | 162 | |
300 | 163 | |
301 | 164 | void
|
302 | -ContinousAllGlyphsTab::checkSubMode()
|
|
165 | +ContinuousTab::checkMode()
|
|
303 | 166 | {
|
304 | - auto isFancy = subMode() == GlyphContinuous::AG_Fancy;
|
|
305 | - auto isStroked = subMode() == GlyphContinuous::AG_Stroked;
|
|
167 | + auto isFancy = modeSelector_->currentIndex() == GlyphContinuous::M_Fancy;
|
|
168 | + auto isStroked = modeSelector_->currentIndex() == GlyphContinuous::M_Stroked;
|
|
306 | 169 | xEmboldeningSpinBox_->setEnabled(isFancy);
|
307 | 170 | yEmboldeningSpinBox_->setEnabled(isFancy);
|
308 | 171 | slantSpinBox_->setEnabled(isFancy);
|
309 | 172 | strokeRadiusSpinBox_->setEnabled(isStroked);
|
310 | 173 | |
311 | - emit changed();
|
|
174 | + repaintGlyph();
|
|
312 | 175 | }
|
313 | 176 | |
314 | 177 | |
315 | 178 | void
|
316 | -ContinousAllGlyphsTab::createLayout()
|
|
179 | +ContinuousTab::checkSource()
|
|
317 | 180 | {
|
181 | + auto src
|
|
182 | + = static_cast<GlyphContinuous::Source>(sourceSelector_->currentIndex());
|
|
183 | + auto isText = src == GlyphContinuous::SRC_TextString
|
|
184 | + || src == GlyphContinuous::SRC_TextStringRepeated;
|
|
185 | + indexSelector_->setEnabled(src == GlyphContinuous::SRC_AllGlyphs);
|
|
186 | + sourceTextEdit_->setEnabled(isText);
|
|
187 | + verticalCheckBox_->setEnabled(isText);
|
|
188 | + |
|
189 | + repaintGlyph();
|
|
190 | +}
|
|
191 | + |
|
192 | + |
|
193 | +void
|
|
194 | +ContinuousTab::charMapChanged()
|
|
195 | +{
|
|
196 | + int newIndex = charMapSelector_->currentIndex();
|
|
197 | + if (newIndex != lastCharMapIndex_)
|
|
198 | + {
|
|
199 | + if (newIndex <= 0
|
|
200 | + || charMaps_.size() <= static_cast<unsigned>(newIndex - 1))
|
|
201 | + setGlyphBeginindex(0);
|
|
202 | + else if (charMaps_[newIndex - 1].maxIndex <= 20)
|
|
203 | + setGlyphBeginindex(charMaps_[newIndex - 1].maxIndex - 1);
|
|
204 | + else
|
|
205 | + setGlyphBeginindex(0x20);
|
|
206 | + }
|
|
207 | + updateLimitIndex();
|
|
208 | + |
|
209 | + repaintGlyph();
|
|
210 | + |
|
211 | + lastCharMapIndex_ = newIndex;
|
|
212 | +}
|
|
213 | + |
|
214 | + |
|
215 | +void
|
|
216 | +ContinuousTab::sourceTextChanged()
|
|
217 | +{
|
|
218 | + canvas_->setSourceText(sourceTextEdit_->toPlainText());
|
|
219 | + repaintGlyph();
|
|
220 | +}
|
|
221 | + |
|
222 | + |
|
223 | +void
|
|
224 | +ContinuousTab::wheelNavigate(int steps)
|
|
225 | +{
|
|
226 | + if (sourceSelector_->currentIndex() == GlyphContinuous::SRC_AllGlyphs)
|
|
227 | + setGlyphBeginindex(indexSelector_->currentIndex() + steps);
|
|
228 | +}
|
|
229 | + |
|
230 | + |
|
231 | +void
|
|
232 | +ContinuousTab::wheelResize(int steps)
|
|
233 | +{
|
|
234 | + sizeSelector_->handleWheelResizeBySteps(steps);
|
|
235 | +}
|
|
236 | + |
|
237 | + |
|
238 | +void
|
|
239 | +ContinuousTab::createLayout()
|
|
240 | +{
|
|
241 | + canvas_ = new GlyphContinuous(this, engine_);
|
|
242 | + sizeSelector_ = new FontSizeSelector(this);
|
|
243 | + |
|
318 | 244 | indexSelector_ = new GlyphIndexSelector(this);
|
319 | 245 | indexSelector_->setSingleMode(false);
|
320 | 246 | indexSelector_->setNumberRenderer([this](int index)
|
321 | 247 | { return formatIndex(index); });
|
248 | + sourceTextEdit_ = new QPlainTextEdit(
|
|
249 | + tr("The quick brown fox jumps over the lazy dog."), this);
|
|
322 | 250 | |
323 | 251 | modeSelector_ = new QComboBox(this);
|
324 | 252 | charMapSelector_ = new QComboBox(this);
|
253 | + sourceSelector_ = new QComboBox(this);
|
|
325 | 254 | |
326 | 255 | // Note: in sync with the enum!!
|
327 | - modeSelector_->insertItem(GlyphContinuous::AG_AllGlyphs, tr("All Glyphs"));
|
|
328 | - modeSelector_->insertItem(GlyphContinuous::AG_Fancy,
|
|
329 | - tr("Fancy (Embolding & Slanting)"));
|
|
330 | - modeSelector_->insertItem(GlyphContinuous::AG_Stroked, tr("Stroked"));
|
|
331 | - modeSelector_->insertItem(GlyphContinuous::AG_Waterfall, tr("Waterfall"));
|
|
332 | - modeSelector_->setCurrentIndex(GlyphContinuous::AG_AllGlyphs);
|
|
256 | + modeSelector_->insertItem(GlyphContinuous::M_Normal, tr("Normal"));
|
|
257 | + modeSelector_->insertItem(GlyphContinuous::M_Fancy, tr("Fancy"));
|
|
258 | + modeSelector_->insertItem(GlyphContinuous::M_Stroked, tr("Stroked"));
|
|
259 | + modeSelector_->setCurrentIndex(GlyphContinuous::M_Normal);
|
|
260 | + |
|
261 | + // Note: in sync with the enum!!
|
|
262 | + sourceSelector_->insertItem(GlyphContinuous::SRC_AllGlyphs,
|
|
263 | + tr("All Glyphs"));
|
|
264 | + sourceSelector_->insertItem(GlyphContinuous::SRC_TextString,
|
|
265 | + tr("Text String"));
|
|
266 | + sourceSelector_->insertItem(GlyphContinuous::SRC_TextStringRepeated,
|
|
267 | + tr("Text String (Repeated)"));
|
|
268 | + |
|
269 | + verticalCheckBox_ = new QCheckBox(tr("Vertical Layout"), this);
|
|
270 | + waterfallCheckBox_ = new QCheckBox(tr("Waterfall"), this);
|
|
333 | 271 | |
334 | 272 | modeLabel_ = new QLabel(tr("Mode:"), this);
|
273 | + sourceLabel_ = new QLabel(tr("Text Source:"), this);
|
|
335 | 274 | charMapLabel_ = new QLabel(tr("Char Map:"), this);
|
336 | - xEmboldeningLabel_ = new QLabel(tr("Hori. Embolding:"), this);
|
|
337 | - yEmboldeningLabel_ = new QLabel(tr("Vert. Embolding:"), this);
|
|
275 | + xEmboldeningLabel_ = new QLabel(tr("Horz. Emb.:"), this);
|
|
276 | + yEmboldeningLabel_ = new QLabel(tr("Vert. Emb.:"), this);
|
|
338 | 277 | slantLabel_ = new QLabel(tr("Slanting:"), this);
|
339 | 278 | strokeRadiusLabel_ = new QLabel(tr("Stroke Radius:"), this);
|
279 | + rotationLabel_ = new QLabel(tr("Rotation:"), this);
|
|
340 | 280 | |
341 | 281 | xEmboldeningSpinBox_ = new QDoubleSpinBox(this);
|
342 | 282 | yEmboldeningSpinBox_ = new QDoubleSpinBox(this);
|
343 | 283 | slantSpinBox_ = new QDoubleSpinBox(this);
|
344 | 284 | strokeRadiusSpinBox_ = new QDoubleSpinBox(this);
|
285 | + rotationSpinBox_ = new QDoubleSpinBox(this);
|
|
345 | 286 | |
346 | 287 | xEmboldeningSpinBox_->setSingleStep(0.005);
|
347 | 288 | xEmboldeningSpinBox_->setMinimum(-0.1);
|
... | ... | @@ -355,93 +296,111 @@ ContinousAllGlyphsTab::createLayout() |
355 | 296 | strokeRadiusSpinBox_->setSingleStep(0.005);
|
356 | 297 | strokeRadiusSpinBox_->setMinimum(0);
|
357 | 298 | strokeRadiusSpinBox_->setMaximum(0.05);
|
299 | + rotationSpinBox_->setSingleStep(5);
|
|
300 | + rotationSpinBox_->setMinimum(-180);
|
|
301 | + rotationSpinBox_->setMaximum(180);
|
|
302 | + |
|
303 | + bottomLayout_ = new QGridLayout;
|
|
304 | + bottomLayout_->addWidget(sourceLabel_, 0, 0);
|
|
305 | + bottomLayout_->addWidget(modeLabel_, 1, 0);
|
|
306 | + bottomLayout_->addWidget(charMapLabel_, 2, 0);
|
|
307 | + bottomLayout_->addWidget(sourceSelector_, 0, 1);
|
|
308 | + bottomLayout_->addWidget(modeSelector_, 1, 1);
|
|
309 | + bottomLayout_->addWidget(charMapSelector_, 2, 1);
|
|
310 | + |
|
311 | + bottomLayout_->addWidget(xEmboldeningLabel_, 1, 2);
|
|
312 | + bottomLayout_->addWidget(yEmboldeningLabel_, 2, 2);
|
|
313 | + bottomLayout_->addWidget(slantLabel_, 3, 2);
|
|
314 | + bottomLayout_->addWidget(strokeRadiusLabel_, 3, 0);
|
|
315 | + bottomLayout_->addWidget(rotationLabel_, 0, 2);
|
|
316 | + |
|
317 | + bottomLayout_->addWidget(xEmboldeningSpinBox_, 1, 3);
|
|
318 | + bottomLayout_->addWidget(yEmboldeningSpinBox_, 2, 3);
|
|
319 | + bottomLayout_->addWidget(slantSpinBox_, 3, 3);
|
|
320 | + bottomLayout_->addWidget(strokeRadiusSpinBox_, 3, 1);
|
|
321 | + bottomLayout_->addWidget(rotationSpinBox_, 0, 3);
|
|
322 | + |
|
323 | + bottomLayout_->addWidget(indexSelector_, 0, 4, 1, 1);
|
|
324 | + bottomLayout_->addWidget(sourceTextEdit_, 1, 4, 3, 3);
|
|
325 | + bottomLayout_->addWidget(waterfallCheckBox_, 0, 5);
|
|
326 | + bottomLayout_->addWidget(verticalCheckBox_, 0, 6);
|
|
327 | + |
|
328 | + bottomLayout_->setColumnStretch(4, 1);
|
|
329 | + |
|
330 | + mainLayout_ = new QVBoxLayout;
|
|
331 | + mainLayout_->addWidget(canvas_);
|
|
332 | + mainLayout_->addWidget(sizeSelector_);
|
|
333 | + mainLayout_->addLayout(bottomLayout_);
|
|
358 | 334 | |
359 | - layout_ = new QGridLayout;
|
|
360 | - layout_->addWidget(indexSelector_, 0, 0, 1, 2);
|
|
361 | - layout_->addWidget(modeLabel_, 1, 0);
|
|
362 | - layout_->addWidget(charMapLabel_, 2, 0);
|
|
363 | - layout_->addWidget(modeSelector_, 1, 1);
|
|
364 | - layout_->addWidget(charMapSelector_, 2, 1);
|
|
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 | - |
|
375 | - layout_->setColumnStretch(1, 1);
|
|
376 | - layout_->setColumnStretch(3, 1);
|
|
377 | - |
|
378 | - setLayout(layout_);
|
|
335 | + setLayout(mainLayout_);
|
|
379 | 336 | }
|
380 | 337 | |
338 | + |
|
381 | 339 | void
|
382 | -ContinousAllGlyphsTab::createConnections()
|
|
340 | +ContinuousTab::createConnections()
|
|
383 | 341 | {
|
342 | + connect(sizeSelector_, &FontSizeSelector::valueChanged,
|
|
343 | + this, &ContinuousTab::repaintGlyph);
|
|
344 | + |
|
345 | + connect(canvas_, &GlyphContinuous::wheelResize,
|
|
346 | + this, &ContinuousTab::wheelResize);
|
|
347 | + connect(canvas_, &GlyphContinuous::wheelNavigate,
|
|
348 | + this, &ContinuousTab::wheelNavigate);
|
|
349 | + connect(canvas_, &GlyphContinuous::displayingCountUpdated,
|
|
350 | + this, &ContinuousTab::setDisplayingCount);
|
|
351 | + |
|
384 | 352 | connect(indexSelector_, &GlyphIndexSelector::currentIndexChanged,
|
385 | - this, &ContinousAllGlyphsTab::changed);
|
|
353 | + this, &ContinuousTab::repaintGlyph);
|
|
386 | 354 | connect(modeSelector_, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
387 | - this, &ContinousAllGlyphsTab::checkSubMode);
|
|
355 | + this, &ContinuousTab::checkMode);
|
|
388 | 356 | connect(charMapSelector_, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
389 | - this, &ContinousAllGlyphsTab::charMapChanged);
|
|
357 | + this, &ContinuousTab::charMapChanged);
|
|
358 | + connect(sourceSelector_, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
|
359 | + this, &ContinuousTab::checkSource);
|
|
390 | 360 | |
391 | 361 | connect(xEmboldeningSpinBox_,
|
392 | 362 | QOverload<double>::of(&QDoubleSpinBox::valueChanged),
|
393 | - this, &ContinousAllGlyphsTab::changed);
|
|
363 | + this, &ContinuousTab::repaintGlyph);
|
|
394 | 364 | connect(yEmboldeningSpinBox_,
|
395 | 365 | QOverload<double>::of(&QDoubleSpinBox::valueChanged),
|
396 | - this, &ContinousAllGlyphsTab::changed);
|
|
366 | + this, &ContinuousTab::repaintGlyph);
|
|
397 | 367 | connect(slantSpinBox_,
|
398 | 368 | QOverload<double>::of(&QDoubleSpinBox::valueChanged),
|
399 | - this, &ContinousAllGlyphsTab::changed);
|
|
369 | + this, &ContinuousTab::repaintGlyph);
|
|
400 | 370 | connect(strokeRadiusSpinBox_,
|
401 | 371 | QOverload<double>::of(&QDoubleSpinBox::valueChanged),
|
402 | - this, &ContinousAllGlyphsTab::changed);
|
|
403 | -}
|
|
404 | - |
|
405 | - |
|
406 | -QString
|
|
407 | -ContinousAllGlyphsTab::formatIndex(int index)
|
|
408 | -{
|
|
409 | - if (charMapSelector_->currentIndex() <= 0) // glyph order
|
|
410 | - return QString::number(index);
|
|
411 | - return charMaps_[charMapSelector_->currentIndex() - 1]
|
|
412 | - .stringifyIndexShort(index);
|
|
413 | -}
|
|
414 | - |
|
415 | - |
|
416 | -void
|
|
417 | -ContinousAllGlyphsTab::charMapChanged()
|
|
418 | -{
|
|
419 | - int newIndex = charMapSelector_->currentIndex();
|
|
420 | - if (newIndex != lastCharMapIndex_)
|
|
421 | - {
|
|
422 | - if (newIndex <= 0
|
|
423 | - || charMaps_.size() <= static_cast<unsigned>(newIndex - 1))
|
|
424 | - setGlyphBeginindex(0);
|
|
425 | - else if (charMaps_[newIndex - 1].maxIndex <= 20)
|
|
426 | - setGlyphBeginindex(charMaps_[newIndex - 1].maxIndex - 1);
|
|
427 | - else
|
|
428 | - setGlyphBeginindex(0x20);
|
|
429 | - }
|
|
430 | - updateLimitIndex();
|
|
431 | - |
|
432 | - emit changed();
|
|
372 | + this, &ContinuousTab::repaintGlyph);
|
|
373 | + connect(rotationSpinBox_,
|
|
374 | + QOverload<double>::of(&QDoubleSpinBox::valueChanged),
|
|
375 | + this, &ContinuousTab::repaintGlyph);
|
|
433 | 376 | |
434 | - lastCharMapIndex_ = newIndex;
|
|
377 | + connect(waterfallCheckBox_, &QCheckBox::clicked,
|
|
378 | + this, &ContinuousTab::repaintGlyph);
|
|
379 | + connect(verticalCheckBox_, &QCheckBox::clicked,
|
|
380 | + this, &ContinuousTab::repaintGlyph);
|
|
381 | + connect(sourceTextEdit_, &QPlainTextEdit::textChanged,
|
|
382 | + this, &ContinuousTab::sourceTextChanged);
|
|
435 | 383 | }
|
436 | 384 | |
437 | 385 | |
438 | 386 | void
|
439 | -ContinousAllGlyphsTab::setDefaults()
|
|
387 | +ContinuousTab::setDefaults()
|
|
440 | 388 | {
|
441 | 389 | xEmboldeningSpinBox_->setValue(0.04);
|
442 | 390 | yEmboldeningSpinBox_->setValue(0.04);
|
443 | 391 | slantSpinBox_->setValue(0.22);
|
444 | 392 | strokeRadiusSpinBox_->setValue(0.02);
|
393 | + rotationSpinBox_->setValue(0);
|
|
394 | +}
|
|
395 | + |
|
396 | + |
|
397 | +QString
|
|
398 | +ContinuousTab::formatIndex(int index)
|
|
399 | +{
|
|
400 | + if (charMapSelector_->currentIndex() <= 0) // glyph order
|
|
401 | + return QString::number(index);
|
|
402 | + return charMaps_[charMapSelector_->currentIndex() - 1].stringifyIndexShort(
|
|
403 | + index);
|
|
445 | 404 | }
|
446 | 405 | |
447 | 406 |
... | ... | @@ -18,8 +18,8 @@ |
18 | 18 | #include <QComboBox>
|
19 | 19 | #include <QGridLayout>
|
20 | 20 | #include <QBoxLayout>
|
21 | - |
|
22 | -class ContinousAllGlyphsTab;
|
|
21 | +#include <QPlainTextEdit>
|
|
22 | +#include <QCheckBox>
|
|
23 | 23 | |
24 | 24 | class ContinuousTab
|
25 | 25 | : public QWidget, public AbstractTab
|
... | ... | @@ -31,106 +31,72 @@ public: |
31 | 31 | |
32 | 32 | void repaintGlyph() override;
|
33 | 33 | void reloadFont() override;
|
34 | - |
|
35 | - // Info about current font (glyph count, charmaps...) is flowed to subtab
|
|
36 | - // via `updateCurrentSubTab`.
|
|
37 | - // Settings and parameters (e.g. mode) are flowed from subtab to `this` via
|
|
38 | - // `updateFromCurrentSubTab`.
|
|
39 | - // SubTabs can notify `this` via signals, see `createConnections`
|
|
40 | - void updateCurrentSubTab();
|
|
41 | - void updateFromCurrentSubTab();
|
|
42 | - |
|
43 | -private slots:
|
|
44 | - void changeTab();
|
|
45 | - void wheelNavigate(int steps);
|
|
46 | - void wheelResize(int steps);
|
|
47 | - |
|
48 | -private:
|
|
49 | - Engine* engine_;
|
|
50 | - |
|
51 | - int currentGlyphCount_;
|
|
52 | - GlyphContinuous* canvas_;
|
|
53 | -
|
|
54 | - FontSizeSelector* sizeSelector_;
|
|
55 | - |
|
56 | - QTabWidget* tabWidget_;
|
|
57 | - ContinousAllGlyphsTab* allGlyphsTab_;
|
|
58 | - |
|
59 | - enum Tabs
|
|
60 | - {
|
|
61 | - AllGlyphs = 0
|
|
62 | - };
|
|
63 | - |
|
64 | - QVBoxLayout* mainLayout_;
|
|
65 | -
|
|
66 | - void createLayout();
|
|
67 | - void createConnections();
|
|
68 | -};
|
|
69 | - |
|
70 | - |
|
71 | -class ContinousAllGlyphsTab
|
|
72 | -: public QWidget
|
|
73 | -{
|
|
74 | - Q_OBJECT
|
|
75 | -public:
|
|
76 | - explicit ContinousAllGlyphsTab(QWidget* parent);
|
|
77 | - ~ContinousAllGlyphsTab() override = default;
|
|
78 | - |
|
79 | - int glyphBeginindex();
|
|
80 | - int glyphLimitIndex();
|
|
81 | - GlyphContinuous::SubModeAllGlyphs subMode();
|
|
82 | - double xEmboldening();
|
|
83 | - double yEmboldening();
|
|
84 | - double slanting();
|
|
85 | - double strokeRadius();
|
|
34 | + void syncSettings();
|
|
86 | 35 | |
87 | 36 | // -1: Glyph order, otherwise the char map index in the original list
|
88 | 37 | int charMapIndex();
|
89 | - void setGlyphBeginindex(int index);
|
|
90 | 38 | |
91 | 39 | // This doesn't trigger immediate repaint
|
92 | 40 | void setGlyphCount(int count);
|
93 | 41 | void setDisplayingCount(int count);
|
42 | + void setGlyphBeginindex(int index);
|
|
94 | 43 | |
95 | 44 | void setCharMaps(std::vector<CharMapInfo>& charMaps);
|
96 | 45 | // This doesn't trigger either.
|
97 | 46 | void updateLimitIndex();
|
47 | + void checkMode();
|
|
48 | + void checkSource();
|
|
49 | + void charMapChanged();
|
|
50 | + void sourceTextChanged();
|
|
98 | 51 | |
99 | - void checkSubMode();
|
|
100 | - |
|
101 | -signals:
|
|
102 | - void changed();
|
|
52 | +private slots:
|
|
53 | + void wheelNavigate(int steps);
|
|
54 | + void wheelResize(int steps);
|
|
103 | 55 | |
104 | 56 | private:
|
105 | - int lastCharMapIndex_ = 0;
|
|
57 | + Engine* engine_;
|
|
58 | + |
|
106 | 59 | int currentGlyphCount_;
|
60 | + int lastCharMapIndex_ = 0;
|
|
107 | 61 | int glyphLimitIndex_ = 0;
|
108 | 62 | |
109 | - GlyphIndexSelector* indexSelector_;
|
|
63 | + GlyphContinuous* canvas_;
|
|
64 | + FontSizeSelector* sizeSelector_;
|
|
65 | + |
|
110 | 66 | QComboBox* modeSelector_;
|
67 | + QComboBox* sourceSelector_;
|
|
111 | 68 | QComboBox* charMapSelector_;
|
112 | 69 | |
113 | 70 | QLabel* modeLabel_;
|
71 | + QLabel* sourceLabel_;
|
|
114 | 72 | QLabel* charMapLabel_;
|
115 | 73 | QLabel* xEmboldeningLabel_;
|
116 | 74 | QLabel* yEmboldeningLabel_;
|
117 | 75 | QLabel* slantLabel_;
|
118 | 76 | QLabel* strokeRadiusLabel_;
|
77 | + QLabel* rotationLabel_;
|
|
119 | 78 | |
120 | 79 | QDoubleSpinBox* xEmboldeningSpinBox_;
|
121 | 80 | QDoubleSpinBox* yEmboldeningSpinBox_;
|
122 | 81 | QDoubleSpinBox* slantSpinBox_;
|
123 | 82 | QDoubleSpinBox* strokeRadiusSpinBox_;
|
83 | + QDoubleSpinBox* rotationSpinBox_;
|
|
84 | + |
|
85 | + QCheckBox* verticalCheckBox_;
|
|
86 | + QCheckBox* waterfallCheckBox_;
|
|
124 | 87 | |
125 | - QGridLayout* layout_;
|
|
88 | + GlyphIndexSelector* indexSelector_;
|
|
89 | + QPlainTextEdit* sourceTextEdit_;
|
|
126 | 90 | |
127 | 91 | std::vector<CharMapInfo> charMaps_;
|
128 | 92 | |
93 | + QGridLayout* bottomLayout_;
|
|
94 | + QVBoxLayout* mainLayout_;
|
|
95 | +
|
|
129 | 96 | void createLayout();
|
130 | 97 | void createConnections();
|
131 | 98 | |
132 | 99 | QString formatIndex(int index);
|
133 | - void charMapChanged();
|
|
134 | 100 | |
135 | 101 | void setDefaults();
|
136 | 102 | };
|
... | ... | @@ -5,6 +5,7 @@ |
5 | 5 | #include "singular.hpp"
|
6 | 6 | |
7 | 7 | #include <QSizePolicy>
|
8 | +#include <QToolTip>
|
|
8 | 9 | #include <QWheelEvent>
|
9 | 10 | |
10 | 11 | |
... | ... | @@ -197,6 +198,25 @@ SingularTab::wheelResize(QWheelEvent* event) |
197 | 198 | }
|
198 | 199 | |
199 | 200 | |
201 | +void
|
|
202 | +SingularTab::setGridVisible()
|
|
203 | +{
|
|
204 | + gridItem_->setVisible(showGridCheckBox_->isChecked());
|
|
205 | +}
|
|
206 | + |
|
207 | + |
|
208 | +void
|
|
209 | +SingularTab::showToolTip()
|
|
210 | +{
|
|
211 | + QToolTip::showText(helpButton_->pos(),
|
|
212 | + tr("Scroll: Grid Up/Down\n"
|
|
213 | + "Alt + Scroll: Grid Left/Right\n"
|
|
214 | + "Ctrl + Scroll: Adjust Zoom (Relative to cursor)\n"
|
|
215 | + "Shift + Scroll: Adjust Font Size"),
|
|
216 | + helpButton_);
|
|
217 | +}
|
|
218 | + |
|
219 | + |
|
200 | 220 | void
|
201 | 221 | SingularTab::createLayout()
|
202 | 222 | {
|
... | ... | @@ -215,6 +235,7 @@ SingularTab::createLayout() |
215 | 235 | glyphView_->setViewportUpdateMode(QGraphicsView::SmartViewportUpdate);
|
216 | 236 | glyphView_->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
|
217 | 237 | glyphView_->setScene(glyphScene_);
|
238 | + glyphView_->setBackgroundBrush(Qt::white);
|
|
218 | 239 | |
219 | 240 | gridItem_ = new Grid(glyphView_, graphicsDefault_->gridPen,
|
220 | 241 | graphicsDefault_->axisPen);
|
... | ... | @@ -222,26 +243,21 @@ SingularTab::createLayout() |
222 | 243 | |
223 | 244 | // Don't use QGraphicsTextItem: We want this hint to be anchored at the
|
224 | 245 | // top-left corner.
|
225 | - mouseUsageHint_ = new QLabel(tr(
|
|
226 | - "Scroll: Grid Up/Down\n"
|
|
227 | - "Alt + Scroll: Grid Left/Right\n"
|
|
228 | - "Ctrl + Scroll: Adjust Zoom (Relative to cursor)\n"
|
|
229 | - "Shift + Scroll: Adjust Font Size"),
|
|
230 | - glyphView_);
|
|
231 | - auto hintFont = font();
|
|
232 | - hintFont.setPixelSize(24);
|
|
233 | - mouseUsageHint_->setFont(hintFont);
|
|
234 | - mouseUsageHint_->setAttribute(Qt::WA_TransparentForMouseEvents, true);
|
|
246 | + auto overlayFont = font();
|
|
247 | + overlayFont.setPixelSize(24);
|
|
235 | 248 | |
236 | 249 | glyphIndexLabel_ = new QLabel(glyphView_);
|
237 | 250 | glyphNameLabel_ = new QLabel(glyphView_);
|
238 | - glyphIndexLabel_->setFont(hintFont);
|
|
239 | - glyphNameLabel_->setFont(hintFont);
|
|
251 | + glyphIndexLabel_->setFont(overlayFont);
|
|
252 | + glyphNameLabel_->setFont(overlayFont);
|
|
240 | 253 | glyphIndexLabel_->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
241 | 254 | glyphNameLabel_->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
242 | 255 | glyphIndexLabel_->setAttribute(Qt::WA_TransparentForMouseEvents, true);
|
243 | 256 | glyphNameLabel_->setAttribute(Qt::WA_TransparentForMouseEvents, true);
|
244 | 257 | |
258 | + glyphIndexLabel_->setStyleSheet("QLabel { color : black; }");
|
|
259 | + glyphNameLabel_->setStyleSheet("QLabel { color : black; }");
|
|
260 | + |
|
245 | 261 | indexSelector_ = new GlyphIndexSelector(this);
|
246 | 262 | indexSelector_->setSingleMode(true);
|
247 | 263 | |
... | ... | @@ -256,11 +272,18 @@ SingularTab::createLayout() |
256 | 272 | zoomLabel_->setBuddy(zoomSpinBox_);
|
257 | 273 | |
258 | 274 | centerGridButton_ = new QPushButton("Go Back to Grid Center", this);
|
275 | + helpButton_ = new QPushButton("?", this);
|
|
276 | + helpButton_->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
|
|
259 | 277 | |
260 | 278 | showBitmapCheckBox_ = new QCheckBox(tr("Show Bitmap"), this);
|
261 | 279 | showPointsCheckBox_ = new QCheckBox(tr("Show Points"), this);
|
262 | 280 | showPointNumbersCheckBox_ = new QCheckBox(tr("Show Point Numbers"), this);
|
263 | 281 | showOutlinesCheckBox_ = new QCheckBox(tr("Show Outlines"), this);
|
282 | + showGridCheckBox_ = new QCheckBox(tr("Show Grid"), this);
|
|
283 | + |
|
284 | + indexHelpLayout_ = new QHBoxLayout;
|
|
285 | + indexHelpLayout_->addWidget(indexSelector_, 1);
|
|
286 | + indexHelpLayout_->addWidget(helpButton_);
|
|
264 | 287 | |
265 | 288 | sizeLayout_ = new QHBoxLayout;
|
266 | 289 | sizeLayout_->addStretch(2);
|
... | ... | @@ -278,20 +301,19 @@ SingularTab::createLayout() |
278 | 301 | checkBoxesLayout_->addWidget(showPointsCheckBox_);
|
279 | 302 | checkBoxesLayout_->addWidget(showPointNumbersCheckBox_);
|
280 | 303 | checkBoxesLayout_->addWidget(showOutlinesCheckBox_);
|
304 | + checkBoxesLayout_->addWidget(showGridCheckBox_);
|
|
281 | 305 | |
282 | 306 | glyphOverlayIndexLayout_ = new QHBoxLayout;
|
283 | 307 | glyphOverlayIndexLayout_->addWidget(glyphIndexLabel_);
|
284 | 308 | glyphOverlayIndexLayout_->addWidget(glyphNameLabel_);
|
285 | 309 | glyphOverlayLayout_ = new QGridLayout;
|
286 | - glyphOverlayLayout_->addWidget(mouseUsageHint_, 0, 0,
|
|
287 | - Qt::AlignTop | Qt::AlignLeft);
|
|
288 | 310 | glyphOverlayLayout_->addLayout(glyphOverlayIndexLayout_, 0, 1,
|
289 | 311 | Qt::AlignTop | Qt::AlignRight);
|
290 | 312 | glyphView_->setLayout(glyphOverlayLayout_);
|
291 | 313 | |
292 | 314 | mainLayout_ = new QVBoxLayout;
|
293 | 315 | mainLayout_->addWidget(glyphView_);
|
294 | - mainLayout_->addWidget(indexSelector_);
|
|
316 | + mainLayout_->addLayout(indexHelpLayout_);
|
|
295 | 317 | mainLayout_->addSpacing(10); // XXX px
|
296 | 318 | mainLayout_->addLayout(sizeLayout_);
|
297 | 319 | mainLayout_->addLayout(checkBoxesLayout_);
|
... | ... | @@ -322,6 +344,8 @@ SingularTab::createConnections() |
322 | 344 | |
323 | 345 | connect(centerGridButton_, &QPushButton::clicked,
|
324 | 346 | this, &SingularTab::backToCenter);
|
347 | + connect(helpButton_, &QPushButton::clicked,
|
|
348 | + this, &SingularTab::showToolTip);
|
|
325 | 349 | |
326 | 350 | connect(showBitmapCheckBox_, &QCheckBox::clicked,
|
327 | 351 | this, &SingularTab::drawGlyph);
|
... | ... | @@ -331,6 +355,8 @@ SingularTab::createConnections() |
331 | 355 | this, &SingularTab::drawGlyph);
|
332 | 356 | connect(showOutlinesCheckBox_, &QCheckBox::clicked,
|
333 | 357 | this, &SingularTab::drawGlyph);
|
358 | + connect(showGridCheckBox_, &QCheckBox::clicked,
|
|
359 | + this, &SingularTab::setGridVisible);
|
|
334 | 360 | }
|
335 | 361 | |
336 | 362 | |
... | ... | @@ -365,6 +391,7 @@ SingularTab::setDefaults() |
365 | 391 | zoomSpinBox_->setValue(20);
|
366 | 392 | showBitmapCheckBox_->setChecked(true);
|
367 | 393 | showOutlinesCheckBox_->setChecked(true);
|
394 | + showGridCheckBox_->setChecked(true);
|
|
368 | 395 |
|
369 | 396 | indexSelector_->setCurrentIndex(indexSelector_->currentIndex(), true);
|
370 | 397 | zoom();
|
... | ... | @@ -52,6 +52,8 @@ private slots: |
52 | 52 | void backToCenter();
|
53 | 53 | void wheelZoom(QWheelEvent* event);
|
54 | 54 | void wheelResize(QWheelEvent* event);
|
55 | + void setGridVisible();
|
|
56 | + void showToolTip();
|
|
55 | 57 | |
56 | 58 | private:
|
57 | 59 | int currentGlyphIndex_;
|
... | ... | @@ -67,13 +69,13 @@ private: |
67 | 69 | GlyphPointNumbers* currentGlyphPointNumbersItem_;
|
68 | 70 | GlyphBitmap* currentGlyphBitmapItem_;
|
69 | 71 | Grid* gridItem_ = NULL;
|
70 | - QLabel* mouseUsageHint_;
|
|
71 | 72 | |
72 | 73 | GlyphIndexSelector* indexSelector_;
|
73 | 74 | FontSizeSelector* sizeSelector_;
|
74 | 75 | QLabel* zoomLabel_;
|
75 | 76 | ZoomSpinBox* zoomSpinBox_;
|
76 | 77 | QPushButton* centerGridButton_;
|
78 | + QPushButton* helpButton_;
|
|
77 | 79 | |
78 | 80 | QLabel* glyphIndexLabel_;
|
79 | 81 | QLabel* glyphNameLabel_;
|
... | ... | @@ -82,9 +84,11 @@ private: |
82 | 84 | QCheckBox* showOutlinesCheckBox_;
|
83 | 85 | QCheckBox* showPointNumbersCheckBox_;
|
84 | 86 | QCheckBox* showPointsCheckBox_;
|
87 | + QCheckBox* showGridCheckBox_;
|
|
85 | 88 | |
86 | 89 | QVBoxLayout* mainLayout_;
|
87 | 90 | QHBoxLayout* checkBoxesLayout_;
|
91 | + QHBoxLayout* indexHelpLayout_;
|
|
88 | 92 | QHBoxLayout* sizeLayout_;
|
89 | 93 | QGridLayout* glyphOverlayLayout_;
|
90 | 94 | QHBoxLayout* glyphOverlayIndexLayout_;
|
... | ... | @@ -18,7 +18,7 @@ GlyphBitmap::GlyphBitmap(FT_Glyph glyph, |
18 | 18 | Engine* engine)
|
19 | 19 | {
|
20 | 20 | QRect bRect;
|
21 | - image_ = engine->convertBitmapToQImage(glyph, &bRect);
|
|
21 | + image_ = engine->convertGlyphToQImage(glyph, &bRect);
|
|
22 | 22 | boundingRect_ = bRect; // QRectF to QRect
|
23 | 23 | }
|
24 | 24 |
... | ... | @@ -11,13 +11,14 @@ |
11 | 11 | #include <QPainter>
|
12 | 12 | #include <QWheelEvent>
|
13 | 13 | |
14 | +#include <freetype/ftbitmap.h>
|
|
15 | + |
|
14 | 16 | |
15 | 17 | GlyphContinuous::GlyphContinuous(QWidget* parent, Engine* engine)
|
16 | 18 | : QWidget(parent), engine_(engine)
|
17 | 19 | {
|
18 | 20 | setAcceptDrops(false);
|
19 | 21 | setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
20 | - graphicsDefault_ = GraphicsDefault::deafultInstance();
|
|
21 | 22 | |
22 | 23 | FT_Stroker_New(engine_->ftLibrary(), &stroker_);
|
23 | 24 | }
|
... | ... | @@ -41,21 +42,19 @@ GlyphContinuous::paintEvent(QPaintEvent* event) |
41 | 42 | {
|
42 | 43 | prePaint();
|
43 | 44 | |
44 | - switch (mode_)
|
|
45 | + switch (source_)
|
|
45 | 46 | {
|
46 | - case AllGlyphs:
|
|
47 | - switch (modeAG_)
|
|
47 | + case SRC_AllGlyphs:
|
|
48 | + switch (mode_)
|
|
48 | 49 | {
|
49 | - case AG_AllGlyphs:
|
|
50 | - case AG_Fancy:
|
|
51 | - case AG_Stroked:
|
|
50 | + case M_Normal:
|
|
51 | + case M_Fancy:
|
|
52 | + case M_Stroked:
|
|
52 | 53 | paintAG(&painter);
|
53 | 54 | break;
|
54 | - case AG_Waterfall:
|
|
55 | - break;
|
|
56 | 55 | }
|
57 | 56 | break;
|
58 | - case TextString:
|
|
57 | + case SRC_TextString:
|
|
59 | 58 | break;
|
60 | 59 | }
|
61 | 60 | emit displayingCountUpdated(displayingCount_);
|
... | ... | @@ -79,7 +78,7 @@ GlyphContinuous::wheelEvent(QWheelEvent* event) |
79 | 78 | void
|
80 | 79 | GlyphContinuous::paintAG(QPainter* painter)
|
81 | 80 | {
|
82 | - if (modeAG_ == AG_Stroked)
|
|
81 | + if (mode_ == M_Stroked)
|
|
83 | 82 | {
|
84 | 83 | auto radius = static_cast<FT_Fixed>(metrics_.y_ppem * 64 * strokeRadius_);
|
85 | 84 | FT_Stroker_Set(stroker_, radius,
|
... | ... | @@ -98,13 +97,13 @@ GlyphContinuous::paintAG(QPainter* painter) |
98 | 97 | break;
|
99 | 98 | |
100 | 99 | // All Glyphs need no tranformation, and Waterfall isn't handled here.
|
101 | - switch (modeAG_)
|
|
100 | + switch (mode_)
|
|
102 | 101 | {
|
103 | - case AG_Fancy:
|
|
104 | - transformGlyphAGFancy();
|
|
102 | + case M_Fancy:
|
|
103 | + transformGlyphFancy();
|
|
105 | 104 | break;
|
106 | - case AG_Stroked:
|
|
107 | - transformGlyphAGStroked();
|
|
105 | + case M_Stroked:
|
|
106 | + transformGlyphStroked();
|
|
108 | 107 | break;
|
109 | 108 | default:;
|
110 | 109 | }
|
... | ... | @@ -120,7 +119,7 @@ GlyphContinuous::paintAG(QPainter* painter) |
120 | 119 | |
121 | 120 | |
122 | 121 | void
|
123 | -GlyphContinuous::transformGlyphAGFancy()
|
|
122 | +GlyphContinuous::transformGlyphFancy()
|
|
124 | 123 | {
|
125 | 124 | // adopted from ftview.c:289
|
126 | 125 | /***************************************************************/
|
... | ... | @@ -148,41 +147,52 @@ GlyphContinuous::transformGlyphAGFancy() |
148 | 147 | xstr = (FT_Pos)(metrics_.y_ppem * 64 * boldX_);
|
149 | 148 | ystr = (FT_Pos)(metrics_.y_ppem * 64 * boldY_);
|
150 | 149 | |
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);
|
|
150 | + if (glyph_->format == FT_GLYPH_FORMAT_OUTLINE)
|
|
151 | + {
|
|
152 | + if (!isGlyphCloned_)
|
|
153 | + cloneGlyph();
|
|
154 | + FT_Outline_Transform(&outline_, &shear);
|
|
155 | + if (FT_Outline_EmboldenXY(&outline_, xstr, ystr))
|
|
156 | + {
|
|
157 | + // XXX error handling?
|
|
158 | + return;
|
|
159 | + }
|
|
159 | 160 | |
160 | - if (glyph_->advance.x)
|
|
161 | - glyph_->advance.x += xstr;
|
|
161 | + if (glyph_->advance.x)
|
|
162 | + glyph_->advance.x += xstr;
|
|
162 | 163 | |
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;
|
|
164 | + if (glyph_->advance.y)
|
|
165 | + glyph_->advance.y += ystr;
|
|
166 | + }
|
|
167 | + else if (glyph_->format == FT_GLYPH_FORMAT_BITMAP)
|
|
168 | + {
|
|
169 | + if (!isBitmapCloned_)
|
|
170 | + cloneBitmap();
|
|
171 | + xstr &= ~63;
|
|
172 | + ystr &= ~63;
|
|
173 | + |
|
174 | + // No shearing support for bitmap
|
|
175 | + FT_Bitmap_Embolden(engine_->ftLibrary(), &bitmap_,
|
|
176 | + xstr, ystr);
|
|
177 | + }
|
|
178 | + else
|
|
179 | + return; // XXX no support for SVG
|
|
170 | 180 | }
|
171 | 181 | |
172 | 182 | |
173 | 183 | void
|
174 | -GlyphContinuous::transformGlyphAGStroked()
|
|
184 | +GlyphContinuous::transformGlyphStroked()
|
|
175 | 185 | {
|
176 | - //if (!isGlyphCloned_)
|
|
177 | - //cloneGlyph();
|
|
178 | - // Well, now here only outline glyph is supported.
|
|
186 | + // Well, here only outline glyph is supported.
|
|
179 | 187 | if (glyph_->format != FT_GLYPH_FORMAT_OUTLINE)
|
180 | 188 | return;
|
189 | + auto oldGlyph = glyph_;
|
|
181 | 190 | auto error = FT_Glyph_Stroke(&glyph_, stroker_, 0);
|
182 | 191 | if (!error)
|
183 | 192 | {
|
193 | + if (isGlyphCloned_)
|
|
194 | + FT_Done_Glyph(oldGlyph);
|
|
184 | 195 | isGlyphCloned_ = true;
|
185 | - isOutlineCloned_ = false;
|
|
186 | 196 | outline_ = reinterpret_cast<FT_OutlineGlyph>(glyph_)->outline;
|
187 | 197 | }
|
188 | 198 | }
|
... | ... | @@ -230,61 +240,19 @@ GlyphContinuous::paintChar(QPainter* painter) |
230 | 240 | // XXX: this is different from what's being done in
|
231 | 241 | // `ftcommon.c`:FTDemo_Draw_Slot: is this correct??
|
232 | 242 | |
233 | - // First translate the outline
|
|
234 | - |
|
235 | - if (glyph_->format != FT_GLYPH_FORMAT_OUTLINE)
|
|
236 | - return true; // XXX only outline is supported - need to impl others later
|
|
237 | - |
|
238 | - FT_BBox cbox;
|
|
239 | - // Don't forget to free this when returning
|
|
240 | - if (!isOutlineCloned_ && !isGlyphCloned_)
|
|
241 | - cloneOutline();
|
|
243 | + QImage* image;
|
|
242 | 244 |
|
243 | - transformOutlineToOrigin(&outline_, &cbox);
|
|
244 | -
|
|
245 | - auto outlineWidth = (cbox.xMax - cbox.xMin) / 64;
|
|
246 | - auto outlineHeight = (cbox.yMax - cbox.yMin) / 64;
|
|
247 | - |
|
248 | - // Then convert to bitmap
|
|
249 | - FT_Bitmap bitmap;
|
|
250 | - QImage::Format format = QImage::Format_Indexed8;
|
|
251 | - auto aaEnabled = engine_->antiAliasingEnabled();
|
|
252 | - |
|
253 | - // TODO cover LCD and color
|
|
254 | - if (!aaEnabled)
|
|
255 | - format = QImage::Format_Mono;
|
|
256 | - |
|
257 | - // TODO optimization: reuse QImage?
|
|
258 | - QImage image(QSize(outlineWidth, outlineHeight), format);
|
|
259 | - |
|
260 | - if (!aaEnabled)
|
|
261 | - image.setColorTable(graphicsDefault_->monoColorTable);
|
|
245 | + if (bitmap_.buffer) // Always prefer `bitmap_` since it can be manipulated
|
|
246 | + image = engine_->convertBitmapToQImage(&bitmap_);
|
|
262 | 247 | else
|
263 | - image.setColorTable(graphicsDefault_->grayColorTable);
|
|
264 | - |
|
265 | - image.fill(0);
|
|
266 | - |
|
267 | - bitmap.rows = static_cast<unsigned int>(outlineHeight);
|
|
268 | - bitmap.width = static_cast<unsigned int>(outlineWidth);
|
|
269 | - bitmap.buffer = image.bits();
|
|
270 | - bitmap.pitch = image.bytesPerLine();
|
|
271 | - bitmap.pixel_mode = aaEnabled ? FT_PIXEL_MODE_GRAY : FT_PIXEL_MODE_MONO;
|
|
272 | - |
|
273 | - FT_Error error = FT_Outline_Get_Bitmap(engine_->ftLibrary(),
|
|
274 | - &outline_,
|
|
275 | - &bitmap);
|
|
276 | - if (error)
|
|
277 | - {
|
|
278 | - // XXX error handling
|
|
279 | - return true;
|
|
280 | - }
|
|
248 | + image = engine_->convertGlyphToQImage(glyph_, NULL);
|
|
249 | + auto offset = engine_->computeGlyphOffset(glyph_);
|
|
281 | 250 | |
282 | - painter->drawImage(
|
|
283 | - QPoint(x_ + cbox.xMin / 64, y_ + (-cbox.yMax / 64)),
|
|
284 | - image.convertToFormat(QImage::Format_ARGB32_Premultiplied));
|
|
251 | + painter->drawImage(offset + QPoint(x_, y_),
|
|
252 | + *image);
|
|
253 | + delete image;
|
|
285 | 254 | |
286 | 255 | x_ += width;
|
287 | -
|
|
288 | 256 | return true;
|
289 | 257 | }
|
290 | 258 | |
... | ... | @@ -292,15 +260,13 @@ GlyphContinuous::paintChar(QPainter* painter) |
292 | 260 | bool
|
293 | 261 | GlyphContinuous::loadGlyph(int index)
|
294 | 262 | {
|
263 | + if (isGlyphCloned_)
|
|
264 | + FT_Done_Glyph(glyph_);
|
|
295 | 265 | glyph_ = engine_->loadGlyphWithoutUpdate(index);
|
296 | 266 | isGlyphCloned_ = false;
|
297 | 267 | if (!glyph_)
|
298 | 268 | return false;
|
299 | - if (glyph_->format == FT_GLYPH_FORMAT_OUTLINE)
|
|
300 | - {
|
|
301 | - isOutlineCloned_ = false;
|
|
302 | - outline_ = reinterpret_cast<FT_OutlineGlyph>(glyph_)->outline;
|
|
303 | - }
|
|
269 | + refreshOutlineOrBitmapFromGlyph();
|
|
304 | 270 | return true;
|
305 | 271 | }
|
306 | 272 | |
... | ... | @@ -308,16 +274,57 @@ GlyphContinuous::loadGlyph(int index) |
308 | 274 | void
|
309 | 275 | GlyphContinuous::cloneGlyph()
|
310 | 276 | {
|
277 | + if (isGlyphCloned_)
|
|
278 | + return;
|
|
311 | 279 | glyph_ = ::cloneGlyph(glyph_);
|
280 | + refreshOutlineOrBitmapFromGlyph();
|
|
312 | 281 | isGlyphCloned_ = true;
|
313 | 282 | }
|
314 | 283 | |
315 | 284 | |
316 | 285 | void
|
317 | -GlyphContinuous::cloneOutline()
|
|
286 | +GlyphContinuous::cloneBitmap()
|
|
287 | +{
|
|
288 | + if (isBitmapCloned_)
|
|
289 | + return;
|
|
290 | + bitmap_ = ::cloneBitmap(engine_->ftLibrary(), &bitmap_);
|
|
291 | + isBitmapCloned_ = true;
|
|
292 | +}
|
|
293 | + |
|
294 | + |
|
295 | +void
|
|
296 | +GlyphContinuous::refreshOutlineOrBitmapFromGlyph()
|
|
318 | 297 | {
|
319 | - outline_ = ::cloneOutline(engine_->ftLibrary(), &outline_);
|
|
320 | - isOutlineCloned_ = true;
|
|
298 | + if (glyph_->format == FT_GLYPH_FORMAT_OUTLINE)
|
|
299 | + {
|
|
300 | + outline_ = reinterpret_cast<FT_OutlineGlyph>(glyph_)->outline;
|
|
301 | + |
|
302 | + // Make `bitmap_` invalid
|
|
303 | + if (isBitmapCloned_)
|
|
304 | + FT_Bitmap_Done(engine_->ftLibrary(), &bitmap_);
|
|
305 | + isBitmapCloned_ = false;
|
|
306 | + bitmap_.buffer = NULL;
|
|
307 | + }
|
|
308 | + else if (glyph_->format == FT_GLYPH_FORMAT_BITMAP)
|
|
309 | + {
|
|
310 | + // Initialize `bitmap_`
|
|
311 | + if (isBitmapCloned_)
|
|
312 | + FT_Bitmap_Done(engine_->ftLibrary(), &bitmap_);
|
|
313 | + isBitmapCloned_ = false;
|
|
314 | + bitmap_ = reinterpret_cast<FT_BitmapGlyph>(glyph_)->bitmap;
|
|
315 | + |
|
316 | + outline_.points = NULL;
|
|
317 | + }
|
|
318 | + else
|
|
319 | + {
|
|
320 | + // Both invalid.
|
|
321 | + outline_.points = NULL;
|
|
322 | + |
|
323 | + if (isBitmapCloned_)
|
|
324 | + FT_Bitmap_Done(engine_->ftLibrary(), &bitmap_);
|
|
325 | + isBitmapCloned_ = false;
|
|
326 | + bitmap_.buffer = NULL;
|
|
327 | + }
|
|
321 | 328 | }
|
322 | 329 | |
323 | 330 | |
... | ... | @@ -326,13 +333,17 @@ GlyphContinuous::cleanCloned() |
326 | 333 | {
|
327 | 334 | if (isGlyphCloned_)
|
328 | 335 | {
|
336 | + if (glyph_->format == FT_GLYPH_FORMAT_BITMAP && !isBitmapCloned_)
|
|
337 | + bitmap_.buffer = NULL;
|
|
338 | + |
|
329 | 339 | FT_Done_Glyph(glyph_);
|
330 | 340 | isGlyphCloned_ = false;
|
331 | 341 | }
|
332 | - if (isOutlineCloned_)
|
|
342 | + if (isBitmapCloned_)
|
|
333 | 343 | {
|
334 | - FT_Outline_Done(engine_->ftLibrary(), &outline_);
|
|
335 | - isOutlineCloned_ = false;
|
|
344 | + FT_Bitmap_Done(engine_->ftLibrary(), &bitmap_);
|
|
345 | + bitmap_.buffer = NULL;
|
|
346 | + isBitmapCloned_ = false;
|
|
336 | 347 | }
|
337 | 348 | }
|
338 | 349 |
... | ... | @@ -5,12 +5,17 @@ |
5 | 5 | #pragma once
|
6 | 6 | |
7 | 7 | #include "graphicsdefault.hpp"
|
8 | + |
|
9 | +#include <utility>
|
|
10 | + |
|
8 | 11 | #include <QWidget>
|
12 | + |
|
9 | 13 | #include <freetype/freetype.h>
|
10 | 14 | #include <freetype/ftglyph.h>
|
11 | 15 | #include <freetype/ftoutln.h>
|
12 | 16 | #include <freetype/ftstroke.h>
|
13 | 17 | |
18 | + |
|
14 | 19 | class Engine;
|
15 | 20 | class GlyphContinuous
|
16 | 21 | : public QWidget
|
... | ... | @@ -20,18 +25,18 @@ public: |
20 | 25 | GlyphContinuous(QWidget* parent, Engine* engine);
|
21 | 26 | ~GlyphContinuous() override;
|
22 | 27 | |
23 | - enum Mode : int
|
|
28 | + enum Source : int
|
|
24 | 29 | {
|
25 | - AllGlyphs,
|
|
26 | - TextString
|
|
30 | + SRC_AllGlyphs,
|
|
31 | + SRC_TextString,
|
|
32 | + SRC_TextStringRepeated
|
|
27 | 33 | };
|
28 | 34 | |
29 | - enum SubModeAllGlyphs : int
|
|
35 | + enum Mode : int
|
|
30 | 36 | {
|
31 | - AG_AllGlyphs,
|
|
32 | - AG_Fancy,
|
|
33 | - AG_Stroked,
|
|
34 | - AG_Waterfall
|
|
37 | + M_Normal,
|
|
38 | + M_Fancy,
|
|
39 | + M_Stroked
|
|
35 | 40 | };
|
36 | 41 | |
37 | 42 | int displayingCount() { return displayingCount_; }
|
... | ... | @@ -40,8 +45,8 @@ public: |
40 | 45 | void setBeginIndex(int index) { beginIndex_ = index; }
|
41 | 46 | void setLimitIndex(int index) { limitIndex_ = index; }
|
42 | 47 | void setCharMapIndex(int index) { charMapIndex_ = index; }
|
48 | + void setSource(Source mode) { source_ = mode; }
|
|
43 | 49 | void setMode(Mode mode) { mode_ = mode; }
|
44 | - void setSubModeAllGlyphs(SubModeAllGlyphs modeAg) { modeAG_ = modeAg; }
|
|
45 | 50 | void setFancyParams(double boldX, double boldY, double slant)
|
46 | 51 | {
|
47 | 52 | boldX_ = boldX;
|
... | ... | @@ -49,6 +54,10 @@ public: |
49 | 54 | slant_ = slant;
|
50 | 55 | }
|
51 | 56 | void setStrokeRadius(double radius) { strokeRadius_ = radius; }
|
57 | + void setRotation(double rotation) { rotation_ = rotation; }
|
|
58 | + void setVertical(bool vertical) { vertical_ = vertical; }
|
|
59 | + void setWaterfall(bool waterfall) { waterfall_ = waterfall; }
|
|
60 | + void setSourceText(QString text) { text_ = std::move(text); }
|
|
52 | 61 | |
53 | 62 | signals:
|
54 | 63 | void wheelNavigate(int steps);
|
... | ... | @@ -61,37 +70,52 @@ protected: |
61 | 70 | |
62 | 71 | private:
|
63 | 72 | Engine* engine_;
|
64 | - GraphicsDefault* graphicsDefault_;
|
|
65 | 73 | |
66 | - Mode mode_ = AllGlyphs;
|
|
67 | - SubModeAllGlyphs modeAG_ = AG_AllGlyphs;
|
|
74 | + Source source_ = SRC_AllGlyphs;
|
|
75 | + Mode mode_ = M_Normal;
|
|
68 | 76 | int beginIndex_;
|
69 | 77 | int limitIndex_;
|
70 | 78 | int charMapIndex_;
|
71 | 79 | double boldX_, boldY_, slant_;
|
72 | 80 | double strokeRadius_;
|
81 | + double rotation_;
|
|
82 | + bool vertical_;
|
|
83 | + bool waterfall_;
|
|
84 | + QString text_;
|
|
73 | 85 | |
74 | 86 | int displayingCount_ = 0;
|
75 | 87 | FT_Size_Metrics metrics_;
|
76 | 88 | int x_ = 0, y_ = 0;
|
77 | 89 | int stepY_ = 0;
|
90 | + |
|
91 | + // Pay especially attention to life cycles & ownerships of those objects:
|
|
92 | + // Note that outline and bitmap can be either invalid, owned by glyph or
|
|
93 | + // owned by `this`.
|
|
94 | + // If owned by `this`, then it's safe to do manipulation, and need to cleanup
|
|
95 | + // If owned by glyph, then must clone to do manipulation, and no cleanup
|
|
96 | + // In `loadGraph`, these 3 values will all be updated.
|
|
97 | + // Note that `glyph_` is a pointer, while `outline_` and `bitmap_` are structs
|
|
78 | 98 | FT_Glyph glyph_;
|
79 | - FT_Outline outline_;
|
|
99 | + FT_Outline outline_; // Using outline_->points == NULL to determine validity
|
|
100 | + FT_Bitmap bitmap_; // Using bitmap_->buffer == NULL to determine validity
|
|
80 | 101 | // when glyph is cloned, outline is factually also cloned
|
81 | - // but `isOutlineCloned` won't be set!
|
|
82 | - bool isGlyphCloned_ = false, isOutlineCloned_ = false;
|
|
102 | + // never manually clone your outline or you can't easily render it!
|
|
103 | + bool isGlyphCloned_ = false;
|
|
104 | + bool isBitmapCloned_ = false;
|
|
83 | 105 | |
84 | 106 | FT_Stroker stroker_;
|
85 | 107 | |
86 | 108 | void paintAG(QPainter* painter);
|
87 | - void transformGlyphAGFancy();
|
|
88 | - void transformGlyphAGStroked();
|
|
109 | + void transformGlyphFancy();
|
|
110 | + void transformGlyphStroked();
|
|
89 | 111 | void prePaint();
|
90 | 112 | // return if there's enough space to paint the current char
|
91 | 113 | bool paintChar(QPainter* painter);
|
92 | 114 | bool loadGlyph(int index);
|
115 | + |
|
93 | 116 | void cloneGlyph();
|
94 | - void cloneOutline();
|
|
117 | + void cloneBitmap();
|
|
118 | + void refreshOutlineOrBitmapFromGlyph();
|
|
95 | 119 | void cleanCloned();
|
96 | 120 | |
97 | 121 | bool checkFitX(int x);
|
... | ... | @@ -55,9 +55,6 @@ Grid::updateRect() |
55 | 55 | }
|
56 | 56 | |
57 | 57 | |
58 | -// XXX call this in a `myQDraphicsView::drawBackground' derived method
|
|
59 | -// to always fill the complete viewport
|
|
60 | - |
|
61 | 58 | void
|
62 | 59 | Grid::paint(QPainter* painter,
|
63 | 60 | const QStyleOptionGraphicsItem* option,
|
... | ... | @@ -25,6 +25,19 @@ cloneGlyph(FT_Glyph src) |
25 | 25 | }
|
26 | 26 | |
27 | 27 | |
28 | +FT_Bitmap
|
|
29 | +cloneBitmap(FT_Library library,
|
|
30 | + FT_Bitmap* src)
|
|
31 | +{
|
|
32 | + FT_Bitmap target = *src;
|
|
33 | + target.buffer = NULL;
|
|
34 | + target.palette = NULL;
|
|
35 | + FT_Bitmap_Init(&target);
|
|
36 | + FT_Bitmap_Copy(library, src, &target);
|
|
37 | + return target;
|
|
38 | +}
|
|
39 | + |
|
40 | + |
|
28 | 41 | void
|
29 | 42 | transformOutlineToOrigin(FT_Outline* outline,
|
30 | 43 | FT_BBox* outControlBox)
|
... | ... | @@ -5,11 +5,13 @@ |
5 | 5 | #pragma once
|
6 | 6 | |
7 | 7 | #include <freetype/ftglyph.h>
|
8 | +#include <freetype/ftbitmap.h>
|
|
8 | 9 | #include <freetype/ftoutln.h>
|
9 | 10 | |
10 | 11 | // The constructed `outline` must be freed by the caller
|
11 | 12 | FT_Outline cloneOutline(FT_Library library, FT_Outline* src);
|
12 | 13 | FT_Glyph cloneGlyph(FT_Glyph src);
|
14 | +FT_Bitmap cloneBitmap(FT_Library library, FT_Bitmap* src);
|
|
13 | 15 | |
14 | 16 | void transformOutlineToOrigin(FT_Outline* outline,
|
15 | 17 | FT_BBox* outControlBox);
|