1 #include <QDebug>
2 #include <QMouseEvent>
3 #include <QPainter>
4 #include <QScrollBar>
5 
6 #include <cmath>
7 
8 #include "glyphsview.h"
9 
10 static const int COLUMNS_COUNT = 100;
11 
GlyphsView(QWidget * parent)12 GlyphsView::GlyphsView(QWidget *parent) : QAbstractScrollArea(parent)
13 {
14     setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
15     setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
16 }
17 
setFontInfo(const FontInfo & fi)18 void GlyphsView::setFontInfo(const FontInfo &fi)
19 {
20     m_fontInfo = fi;
21     m_glyphs.resize(fi.numberOfGlyphs);
22 #ifdef WITH_FREETYPE
23     m_ftGlyphs.resize(fi.numberOfGlyphs);
24 #endif
25 #ifdef WITH_HARFBUZZ
26     m_hbGlyphs.resize(fi.numberOfGlyphs);
27 #endif
28 
29     m_indexes.clear();
30     for (int i = 0; i < fi.numberOfGlyphs; ++i) {
31         QStaticText text(QString::number(i));
32         text.prepare();
33         m_indexes << text;
34     }
35 
36     updateScrollBars();
37     horizontalScrollBar()->setValue(0);
38     verticalScrollBar()->setValue(0);
39 }
40 
setGlyph(int idx,const Glyph & glyph)41 void GlyphsView::setGlyph(int idx, const Glyph &glyph)
42 {
43     m_glyphs.replace(idx, glyph);
44 }
45 
46 #ifdef WITH_FREETYPE
setFTGlyph(int idx,const Glyph & glyph)47 void GlyphsView::setFTGlyph(int idx, const Glyph &glyph)
48 {
49     m_ftGlyphs.replace(idx, glyph);
50 }
51 #endif
52 
53 #ifdef WITH_HARFBUZZ
setHBGlyph(int idx,const Glyph & glyph)54 void GlyphsView::setHBGlyph(int idx, const Glyph &glyph)
55 {
56     m_hbGlyphs.replace(idx, glyph);
57 }
58 #endif
59 
setDrawBboxes(const bool flag)60 void GlyphsView::setDrawBboxes(const bool flag)
61 {
62     m_drawBboxes = flag;
63     viewport()->update();
64 }
65 
setDrawGlyphs(const bool flag)66 void GlyphsView::setDrawGlyphs(const bool flag)
67 {
68     m_drawGlyphs = flag;
69     viewport()->update();
70 }
71 
setDrawFTGlyphs(const bool flag)72 void GlyphsView::setDrawFTGlyphs(const bool flag)
73 {
74     m_drawFTGlyphs = flag;
75     viewport()->update();
76 }
77 
setDrawHBGlyphs(const bool flag)78 void GlyphsView::setDrawHBGlyphs(const bool flag)
79 {
80     m_drawHBGlyphs = flag;
81     viewport()->update();
82 }
83 
paintEvent(QPaintEvent *)84 void GlyphsView::paintEvent(QPaintEvent *)
85 {
86     QPainter p(viewport());
87     p.translate(-horizontalScrollBar()->value(), -verticalScrollBar()->value());
88 
89     const double cellHeight = m_fontInfo.height * m_scale;
90     drawGrid(p, cellHeight);
91 
92     p.setRenderHint(QPainter::Antialiasing);
93 
94     {
95         auto font = p.font();
96         font.setPointSize(10);
97         p.setFont(font);
98     }
99 
100     int x = 0;
101     int y = m_fontInfo.ascender;
102     int num_y = m_fontInfo.height;
103     for (int i = 0; i < m_glyphs.size(); ++i) {
104         // Text rendering is the slowest part, so we are using preprocessed text.
105         p.setPen(palette().color(QPalette::Text));
106         p.drawStaticText(
107             qRound(x * m_scale + 1),
108             qRound(num_y * m_scale - p.fontMetrics().ascent() - 2),
109             m_indexes.at(i)
110         );
111 
112         if (m_drawGlyphs) {
113             p.save();
114 
115             const int dx = qRound((m_fontInfo.height - m_glyphs.at(i).bbox.width()) / 2.0)
116                 - m_glyphs.at(i).bbox.x();
117 
118             p.scale(m_scale, m_scale);
119             p.translate(x + dx, y);
120 
121             if (m_drawBboxes) {
122                 p.setPen(QPen(Qt::darkGreen, 0.5 / m_scale));
123                 p.setBrush(Qt::NoBrush);
124                 p.drawRect(m_glyphs.at(i).bbox);
125             }
126 
127             p.setPen(Qt::NoPen);
128             p.setPen(Qt::NoPen);
129             if (m_drawFTGlyphs || m_drawHBGlyphs) {
130                 p.setBrush(Qt::red);
131             } else {
132                 p.setBrush(palette().color(QPalette::Text));
133             }
134 
135             p.drawPath(m_glyphs.at(i).outline);
136 
137             p.restore();
138         }
139 
140 #ifdef WITH_HARFBUZZ
141         if (m_drawHBGlyphs) {
142             p.save();
143 
144             const int dx = qRound((m_fontInfo.height - m_hbGlyphs.at(i).bbox.width()) / 2.0)
145                 - m_hbGlyphs.at(i).bbox.x();
146 
147             p.scale(m_scale, m_scale);
148             p.translate(x + dx, y);
149 
150             if (m_drawBboxes) {
151                 p.setPen(QPen(Qt::darkGreen, 0.5 / m_scale));
152                 p.setBrush(Qt::NoBrush);
153                 p.drawRect(m_hbGlyphs.at(i).bbox);
154             }
155 
156             p.setPen(Qt::NoPen);
157             if (m_drawFTGlyphs) {
158                 p.setBrush(Qt::blue);
159             } else {
160                 p.setBrush(palette().color(QPalette::Text));
161             }
162 
163             p.drawPath(m_hbGlyphs.at(i).outline);
164 
165             p.restore();
166         }
167 #endif
168 
169 #ifdef WITH_FREETYPE
170         if (m_drawFTGlyphs) {
171             p.save();
172 
173             const int dx = qRound((m_fontInfo.height - m_ftGlyphs.at(i).bbox.width()) / 2.0)
174                 - m_ftGlyphs.at(i).bbox.x();
175 
176             p.scale(m_scale, m_scale);
177             p.translate(x + dx, y);
178 
179             if (m_drawBboxes) {
180                 p.setPen(QPen(Qt::darkGreen, 0.5 / m_scale));
181                 p.setBrush(Qt::NoBrush);
182                 p.drawRect(m_ftGlyphs.at(i).bbox);
183             }
184 
185             p.setPen(Qt::NoPen);
186             p.setBrush(palette().color(QPalette::Text));
187 
188             if (m_drawGlyphs || m_drawHBGlyphs) {
189                 p.setBrush(palette().color(QPalette::Base));
190             }
191 
192             p.drawPath(m_ftGlyphs.at(i).outline);
193 
194             p.restore();
195         }
196 #endif
197 
198         x += m_fontInfo.height;
199         if (i > 0 && (i + 1) % COLUMNS_COUNT == 0) {
200             x = 0;
201             y += m_fontInfo.height;
202             num_y += m_fontInfo.height;
203         }
204     }
205 }
206 
drawGrid(QPainter & p,const double cellHeight)207 void GlyphsView::drawGrid(QPainter &p, const double cellHeight)
208 {
209     p.setRenderHint(QPainter::Antialiasing, false);
210     p.setPen(QPen(palette().color(QPalette::Text), 0.25));
211     p.setBrush(Qt::NoBrush);
212 
213     const int rows = qRound(floor(m_glyphs.size() / COLUMNS_COUNT)) + 1;
214     const auto maxH = qMin(rows * cellHeight, (double)horizontalScrollBar()->maximum());
215 
216     double x = cellHeight;
217     for (int c = 1; c < COLUMNS_COUNT; ++c) {
218         p.drawLine(QLineF(x, 0, x, maxH));
219         x += cellHeight;
220     }
221 
222     double y = cellHeight;
223     for (int r = 1; r <= rows; ++r) {
224         p.drawLine(QLineF(0, y, horizontalScrollBar()->maximum() + viewport()->width(), y));
225         y += cellHeight;
226     }
227 }
228 
mousePressEvent(QMouseEvent * e)229 void GlyphsView::mousePressEvent(QMouseEvent *e)
230 {
231     if (e->button() & Qt::LeftButton) {
232         m_mousePressPos = e->pos();
233         m_origOffset = QPoint(horizontalScrollBar()->value(), verticalScrollBar()->value());
234     }
235 }
236 
mouseMoveEvent(QMouseEvent * e)237 void GlyphsView::mouseMoveEvent(QMouseEvent *e)
238 {
239     if (m_mousePressPos.isNull()) {
240         return;
241     }
242 
243     const auto diff = m_mousePressPos - e->pos();
244     horizontalScrollBar()->setValue(m_origOffset.x() + diff.x());
245     verticalScrollBar()->setValue(m_origOffset.y() + diff.y());
246 }
247 
mouseReleaseEvent(QMouseEvent *)248 void GlyphsView::mouseReleaseEvent(QMouseEvent *)
249 {
250     m_mousePressPos = QPoint();
251     m_origOffset = QPoint();
252 }
253 
wheelEvent(QWheelEvent * e)254 void GlyphsView::wheelEvent(QWheelEvent *e)
255 {
256     e->accept();
257 
258     if (e->delta() > 0) {
259         m_scale += 0.01;
260     } else {
261         m_scale -= 0.01;
262     }
263 
264     m_scale = qBound(0.03, m_scale, 1.0);
265 
266     updateScrollBars();
267     viewport()->update();
268 }
269 
resizeEvent(QResizeEvent * e)270 void GlyphsView::resizeEvent(QResizeEvent *e)
271 {
272     QAbstractScrollArea::resizeEvent(e);
273     updateScrollBars();
274 }
275 
updateScrollBars()276 void GlyphsView::updateScrollBars()
277 {
278     const double cellHeight = m_fontInfo.height * m_scale;
279     const int rows = qRound(floor(m_glyphs.size() / COLUMNS_COUNT)) + 1;
280     const auto w = COLUMNS_COUNT * cellHeight - viewport()->width();
281     const auto h = rows * cellHeight - viewport()->height();
282     horizontalScrollBar()->setMinimum(0);
283     verticalScrollBar()->setMinimum(0);
284     horizontalScrollBar()->setMaximum(qMax(0, qRound(w)));
285     verticalScrollBar()->setMaximum(qMax(0, qRound(h)));
286 }
287