Fix non-integer Font Metrics in Graph (#1545)

This commit is contained in:
Florian Märkl 2019-05-17 13:00:54 +02:00 committed by GitHub
parent 61b0374a82
commit ef22f20548
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 390 additions and 333 deletions

View File

@ -1,60 +1,62 @@
#ifndef CACHEDFONTMETRICS_H #ifndef CACHEDFONTMETRICS_H
#define CACHEDFONTMETRICS_H #define CACHEDFONTMETRICS_H
#include "common/Metrics.h"
#include <QObject> #include <QObject>
#include <QFont> #include <QFont>
#include <QFontMetrics> #include <QFontMetrics>
class CachedFontMetrics : public QObject template<typename T>
class CachedFontMetrics
{ {
Q_OBJECT
public: public:
explicit CachedFontMetrics(QObject *parent, const QFont &font) explicit CachedFontMetrics(const QFont &font)
: QObject(parent), : mFontMetrics(font)
mFontMetrics(font)
{ {
memset(mWidths, 0, sizeof(mWidths)); memset(mWidths, 0, sizeof(mWidths));
mHeight = mFontMetrics.height(); mHeight = mFontMetrics.height();
} }
int width(const QChar &ch) T width(const QChar &ch)
{ {
//return mFontMetrics.width(ch);
auto unicode = ch.unicode(); auto unicode = ch.unicode();
if (unicode >= 0xD800) { if (unicode >= 0xD800) {
if (unicode >= 0xE000) if (unicode >= 0xE000)
unicode -= 0xE000 - 0xD800; unicode -= 0xE000 - 0xD800;
else else
// is lonely surrogate // is lonely surrogate
return mFontMetrics.width(ch); return fetchWidth(ch);
} }
if (!mWidths[unicode]) if (!mWidths[unicode])
return mWidths[unicode] = mFontMetrics.width(ch); return mWidths[unicode] = fetchWidth(ch);
return mWidths[unicode]; return mWidths[unicode];
} }
int width(const QString &text) T width(const QString &text)
{ {
int result = 0; T result = 0;
QChar temp; QChar temp;
for (const QChar &ch : text) { for (const QChar &ch : text) {
if (ch.isHighSurrogate()) if (ch.isHighSurrogate())
temp = ch; temp = ch;
else if (ch.isLowSurrogate()) else if (ch.isLowSurrogate())
result += mFontMetrics.width(QString(temp) + ch); result += fetchWidth(QString(temp) + ch);
else else
result += width(ch); result += width(ch);
} }
return result; return result;
} }
int height() T height()
{ {
return mHeight; return mHeight;
} }
int position(const QString &text, int offset) T position(const QString &text, T offset)
{ {
int curWidth = 0; T curWidth = 0;
QChar temp; QChar temp;
for (int i = 0; i < text.length(); i++) { for (int i = 0; i < text.length(); i++) {
@ -63,7 +65,7 @@ public:
if (ch.isHighSurrogate()) if (ch.isHighSurrogate())
temp = ch; temp = ch;
else if (ch.isLowSurrogate()) else if (ch.isLowSurrogate())
curWidth += mFontMetrics.width(QString(temp) + ch); curWidth += fetchWidth(QString(temp) + ch);
else else
curWidth += width(ch); curWidth += width(ch);
@ -76,9 +78,27 @@ public:
} }
private: private:
QFontMetrics mFontMetrics; typename Metrics<T>::FontMetrics mFontMetrics;
uchar mWidths[0x10000 - 0xE000 + 0xD800]; T mWidths[0x10000 - 0xE000 + 0xD800];
int mHeight; T mHeight;
T fetchWidth(QChar c)
{
#if QT_VERSION < QT_VERSION_CHECK(5,11,0)
return mFontMetrics.width(c);
#else
return mFontMetrics.horizontalAdvance(c);
#endif
}
T fetchWidth(const QString &s)
{
#if QT_VERSION < QT_VERSION_CHECK(5,11,0)
return mFontMetrics.width(s);
#else
return mFontMetrics.horizontalAdvance(s);
#endif
}
}; };
#endif // CACHEDFONTMETRICS_H #endif // CACHEDFONTMETRICS_H

26
src/common/Metrics.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef METRICS_H
#define METRICS_H
#include <QtGlobal>
class QRect;
class QRectF;
class QFontMetrics;
class QFontMetricsF;
template<typename T> struct Metrics {};
template<> struct Metrics<int>
{
using Rect = QRect;
using FontMetrics = QFontMetrics;
};
template<> struct Metrics<qreal>
{
using Rect = QRectF;
using FontMetrics = QFontMetricsF;
};
#endif //METRICS_H

View File

@ -7,15 +7,18 @@
#include <QTextFragment> #include <QTextFragment>
//TODO: fix performance (possibly use QTextLayout?) //TODO: fix performance (possibly use QTextLayout?)
void RichTextPainter::paintRichText(QPainter *painter, int x, int y, int w, int h, int xinc,
const List &richText, CachedFontMetrics *fontMetrics)
template<typename T>
void RichTextPainter::paintRichText(QPainter *painter, T x, T y, T w, T h, T xinc,
const List &richText, CachedFontMetrics<T> *fontMetrics)
{ {
QPen pen; QPen pen;
QPen highlightPen; QPen highlightPen;
QBrush brush(Qt::cyan); QBrush brush(Qt::cyan);
for (const CustomRichText_t &curRichText : richText) { for (const CustomRichText_t &curRichText : richText) {
int textWidth = fontMetrics->width(curRichText.text); T textWidth = fontMetrics->width(curRichText.text);
int backgroundWidth = textWidth; T backgroundWidth = textWidth;
if (backgroundWidth + xinc > w) if (backgroundWidth + xinc > w)
backgroundWidth = w - xinc; backgroundWidth = w - xinc;
if (backgroundWidth <= 0) //stop drawing when going outside the specified width if (backgroundWidth <= 0) //stop drawing when going outside the specified width
@ -32,24 +35,24 @@ void RichTextPainter::paintRichText(QPainter *painter, int x, int y, int w, int
case FlagBackground: //background only case FlagBackground: //background only
if (backgroundWidth > 0 && curRichText.textBackground.alpha()) { if (backgroundWidth > 0 && curRichText.textBackground.alpha()) {
brush.setColor(curRichText.textBackground); brush.setColor(curRichText.textBackground);
painter->fillRect(QRect(x + xinc, y, backgroundWidth, h), brush); painter->fillRect(QRectF(x + xinc, y, backgroundWidth, h), brush);
} }
break; break;
case FlagAll: //color+background case FlagAll: //color+background
if (backgroundWidth > 0 && curRichText.textBackground.alpha()) { if (backgroundWidth > 0 && curRichText.textBackground.alpha()) {
brush.setColor(curRichText.textBackground); brush.setColor(curRichText.textBackground);
painter->fillRect(QRect(x + xinc, y, backgroundWidth, h), brush); painter->fillRect(QRectF(x + xinc, y, backgroundWidth, h), brush);
} }
pen.setColor(curRichText.textColor); pen.setColor(curRichText.textColor);
painter->setPen(pen); painter->setPen(pen);
break; break;
} }
painter->drawText(QRect(x + xinc, y, w - xinc, h), Qt::TextBypassShaping, curRichText.text); painter->drawText(typename Metrics<T>::Rect(x + xinc, y, w - xinc, h), Qt::TextBypassShaping, curRichText.text);
if (curRichText.highlight && curRichText.highlightColor.alpha()) { if (curRichText.highlight && curRichText.highlightColor.alpha()) {
highlightPen.setColor(curRichText.highlightColor); highlightPen.setColor(curRichText.highlightColor);
highlightPen.setWidth(curRichText.highlightWidth); highlightPen.setWidth(curRichText.highlightWidth);
painter->setPen(highlightPen); painter->setPen(highlightPen);
int highlightOffsetX = curRichText.highlightConnectPrev ? -1 : 1; T highlightOffsetX = curRichText.highlightConnectPrev ? -1 : 1;
painter->drawLine(x + xinc + highlightOffsetX, y + h - 1, x + xinc + backgroundWidth - 1, painter->drawLine(x + xinc + highlightOffsetX, y + h - 1, x + xinc + backgroundWidth - 1,
y + h - 1); y + h - 1);
} }
@ -57,6 +60,11 @@ void RichTextPainter::paintRichText(QPainter *painter, int x, int y, int w, int
} }
} }
template
void RichTextPainter::paintRichText<qreal>(QPainter *painter, qreal x, qreal y, qreal w, qreal h, qreal xinc,
const List &richText, CachedFontMetrics<qreal> *fontMetrics);
/** /**
* @brief RichTextPainter::htmlRichText Convert rich text in x64dbg to HTML, for use by other applications * @brief RichTextPainter::htmlRichText Convert rich text in x64dbg to HTML, for use by other applications
* @param richText The rich text to be converted to HTML format * @param richText The rich text to be converted to HTML format

View File

@ -2,12 +2,15 @@
#ifndef RICHTEXTPAINTER_H #ifndef RICHTEXTPAINTER_H
#define RICHTEXTPAINTER_H #define RICHTEXTPAINTER_H
#include "common/Metrics.h"
#include <QString> #include <QString>
#include <QTextDocument> #include <QTextDocument>
#include <QColor> #include <QColor>
#include <vector> #include <vector>
class CachedFontMetrics; class QFontMetricsF;
template<typename T> class CachedFontMetrics;
class QPainter; class QPainter;
class RichTextPainter class RichTextPainter
@ -35,8 +38,9 @@ public:
typedef std::vector<CustomRichText_t> List; typedef std::vector<CustomRichText_t> List;
//functions //functions
static void paintRichText(QPainter *painter, int x, int y, int w, int h, int xinc, template<typename T = qreal>
const List &richText, CachedFontMetrics *fontMetrics); static void paintRichText(QPainter *painter, T x, T y, T w, T h, T xinc,
const List &richText, CachedFontMetrics<T> *fontMetrics);
static void htmlRichText(const List &richText, QString &textHtml, QString &textPlain); static void htmlRichText(const List &richText, QString &textHtml, QString &textPlain);
static List fromTextDocument(const QTextDocument &doc); static List fromTextDocument(const QTextDocument &doc);

View File

@ -377,8 +377,7 @@ void DisassemblerGraphView::initFont()
charWidth = metrics.width('X'); charWidth = metrics.width('X');
charHeight = static_cast<int>(metrics.height()); charHeight = static_cast<int>(metrics.height());
charOffset = 0; charOffset = 0;
delete mFontMetrics; mFontMetrics.reset(new CachedFontMetrics<qreal>(font()));
mFontMetrics = new CachedFontMetrics(this, font());
} }
void DisassemblerGraphView::drawBlock(QPainter &p, GraphView::GraphBlock &block) void DisassemblerGraphView::drawBlock(QPainter &p, GraphView::GraphBlock &block)
@ -464,7 +463,7 @@ void DisassemblerGraphView::drawBlock(QPainter &p, GraphView::GraphBlock &block)
// Highlight selected tokens // Highlight selected tokens
if (highlight_token != nullptr) { if (highlight_token != nullptr) {
int y = static_cast<int>(blockY + (2 * charWidth) + (db.header_text.lines.size() * charHeight)); int y = static_cast<int>(blockY + (2 * charWidth) + (db.header_text.lines.size() * charHeight));
int tokenWidth = mFontMetrics->width(highlight_token->content); qreal tokenWidth = mFontMetrics->width(highlight_token->content);
for (const Instr &instr : db.instrs) { for (const Instr &instr : db.instrs) {
int pos = -1; int pos = -1;
@ -477,19 +476,19 @@ void DisassemblerGraphView::drawBlock(QPainter &p, GraphView::GraphBlock &block)
continue; continue;
} }
int widthBefore = mFontMetrics->width(instr.plainText.left(pos)); qreal widthBefore = mFontMetrics->width(instr.plainText.left(pos));
if (charWidth * 3 + widthBefore > block.width - (10 + 2 * charWidth)) { if (charWidth * 3 + widthBefore > block.width - (10 + 2 * charWidth)) {
continue; continue;
} }
int highlightWidth = tokenWidth; qreal highlightWidth = tokenWidth;
if (charWidth * 3 + widthBefore + tokenWidth >= block.width - (10 + 2 * charWidth)) { if (charWidth * 3 + widthBefore + tokenWidth >= block.width - (10 + 2 * charWidth)) {
highlightWidth = static_cast<int>(block.width - widthBefore - (10 + 4 * charWidth)); highlightWidth = block.width - widthBefore - (10 + 4 * charWidth);
} }
QColor selectionColor = ConfigColor("wordhl"); QColor selectionColor = ConfigColor("wordhl");
p.fillRect(QRect(static_cast<int>(blockX + charWidth * 3 + widthBefore), y, highlightWidth, p.fillRect(QRectF(blockX + charWidth * 3 + widthBefore, y, highlightWidth,
charHeight), selectionColor); charHeight), selectionColor);
} }
@ -535,8 +534,8 @@ void DisassemblerGraphView::drawBlock(QPainter &p, GraphView::GraphBlock &block)
continue; continue;
} }
RichTextPainter::paintRichText(&p, static_cast<int>(x), y, block.width, charHeight, 0, line, RichTextPainter::paintRichText<qreal>(&p, x, y, block.width, charHeight, 0, line,
mFontMetrics); mFontMetrics.get());
y += charHeight; y += charHeight;
} }
@ -569,9 +568,9 @@ void DisassemblerGraphView::drawBlock(QPainter &p, GraphView::GraphBlock &block)
QRectF bpRect(x - rectSize / 3.0, y + (charHeight - rectSize) / 2.0, rectSize, rectSize); QRectF bpRect(x - rectSize / 3.0, y + (charHeight - rectSize) / 2.0, rectSize, rectSize);
Q_UNUSED(bpRect); Q_UNUSED(bpRect);
RichTextPainter::paintRichText(&p, static_cast<int>(x + charWidth), y, RichTextPainter::paintRichText<qreal>(&p, x + charWidth, y,
static_cast<int>(block.width - charWidth), charHeight, 0, line, block.width - charWidth, charHeight, 0, line,
mFontMetrics); mFontMetrics.get());
y += charHeight; y += charHeight;
} }

View File

@ -141,7 +141,7 @@ private:
Token *highlight_token; Token *highlight_token;
// Font data // Font data
CachedFontMetrics *mFontMetrics; std::unique_ptr<CachedFontMetrics<qreal>> mFontMetrics;
qreal charWidth; qreal charWidth;
int charHeight; int charHeight;
int charOffset; int charOffset;