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

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>
//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 highlightPen;
QBrush brush(Qt::cyan);
for (const CustomRichText_t &curRichText : richText) {
int textWidth = fontMetrics->width(curRichText.text);
int backgroundWidth = textWidth;
T textWidth = fontMetrics->width(curRichText.text);
T backgroundWidth = textWidth;
if (backgroundWidth + xinc > w)
backgroundWidth = w - xinc;
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
if (backgroundWidth > 0 && curRichText.textBackground.alpha()) {
brush.setColor(curRichText.textBackground);
painter->fillRect(QRect(x + xinc, y, backgroundWidth, h), brush);
painter->fillRect(QRectF(x + xinc, y, backgroundWidth, h), brush);
}
break;
case FlagAll: //color+background
if (backgroundWidth > 0 && curRichText.textBackground.alpha()) {
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);
painter->setPen(pen);
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()) {
highlightPen.setColor(curRichText.highlightColor);
highlightPen.setWidth(curRichText.highlightWidth);
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,
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
* @param richText The rich text to be converted to HTML format

View File

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

View File

@ -377,8 +377,7 @@ void DisassemblerGraphView::initFont()
charWidth = metrics.width('X');
charHeight = static_cast<int>(metrics.height());
charOffset = 0;
delete mFontMetrics;
mFontMetrics = new CachedFontMetrics(this, font());
mFontMetrics.reset(new CachedFontMetrics<qreal>(font()));
}
void DisassemblerGraphView::drawBlock(QPainter &p, GraphView::GraphBlock &block)
@ -464,7 +463,7 @@ void DisassemblerGraphView::drawBlock(QPainter &p, GraphView::GraphBlock &block)
// Highlight selected tokens
if (highlight_token != nullptr) {
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) {
int pos = -1;
@ -477,19 +476,19 @@ void DisassemblerGraphView::drawBlock(QPainter &p, GraphView::GraphBlock &block)
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)) {
continue;
}
int highlightWidth = tokenWidth;
qreal highlightWidth = tokenWidth;
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");
p.fillRect(QRect(static_cast<int>(blockX + charWidth * 3 + widthBefore), y, highlightWidth,
p.fillRect(QRectF(blockX + charWidth * 3 + widthBefore, y, highlightWidth,
charHeight), selectionColor);
}
@ -535,8 +534,8 @@ void DisassemblerGraphView::drawBlock(QPainter &p, GraphView::GraphBlock &block)
continue;
}
RichTextPainter::paintRichText(&p, static_cast<int>(x), y, block.width, charHeight, 0, line,
mFontMetrics);
RichTextPainter::paintRichText<qreal>(&p, x, y, block.width, charHeight, 0, line,
mFontMetrics.get());
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);
Q_UNUSED(bpRect);
RichTextPainter::paintRichText(&p, static_cast<int>(x + charWidth), y,
static_cast<int>(block.width - charWidth), charHeight, 0, line,
mFontMetrics);
RichTextPainter::paintRichText<qreal>(&p, x + charWidth, y,
block.width - charWidth, charHeight, 0, line,
mFontMetrics.get());
y += charHeight;
}

View File

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