Skip to content

Commit

Permalink
Some render optimizations
Browse files Browse the repository at this point in the history
  • Loading branch information
mbystryantsev committed Dec 16, 2022
1 parent d107300 commit db6ef45
Show file tree
Hide file tree
Showing 2 changed files with 176 additions and 55 deletions.
218 changes: 164 additions & 54 deletions src/qhexedit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,65 @@
#include "qhexedit.h"
#include <algorithm>

namespace
{

const QString singleArgFormatStr = QString("%1");

inline QString toHex(int value, int width, bool upper)
{
const QString hex = singleArgFormatStr.arg(value, width, 16, QChar('0'));
return upper ? hex.toUpper() : hex;
}

QStringList createHexValues(bool upper)
{
QStringList result;
result.reserve(256);

for (int i = 0; i < 256; i++)
{
result.append(toHex(i, 2, upper));
}

return result;
}

QStringList createHexDigits(bool upper)
{
QStringList result;
result.reserve(16);

for (int i = 0; i < 16; i++)
{
result.append(toHex(i, 1, upper));
}

return result;
}

const QString dot(QChar('.'));

QStringList createChars()
{
QStringList result;
result.reserve(256);

for (int ch = 0; ch < 256; ch++)
{
result.append((ch < ' ' || ch > '~') ? dot : QString(QChar(ch)));
}

return result;
}

const QStringList hexValuesStringsUpper = createHexValues(true);
const QStringList hexValuesStringsLower = createHexValues(false);
const QStringList hexDigitsStringsUpper = createHexDigits(true);
const QStringList hexDigitsStringsLower = createHexDigits(false);
const QStringList charStrings = createChars();

}

// ********************************************************************** Constructor, destructor

Expand Down Expand Up @@ -228,9 +287,9 @@ void QHexEdit::setCursorPosition(qint64 position)
}

if (_overwriteMode)
_cursorRect = QRect(_pxCursorX - horizontalScrollBar()->value(), _pxCursorY + _pxCursorWidth, _pxCharWidth, _pxCursorWidth);
_cursorRect = QRect(_pxCursorX - horizontalScrollBar()->value(), _pxCursorY + _pxCursorWidth - _pxDescent, _pxCharWidth, _pxCursorWidth);
else
_cursorRect = QRect(_pxCursorX - horizontalScrollBar()->value(), _pxCursorY - _pxCharHeight + 4, _pxCursorWidth, _pxCharHeight);
_cursorRect = QRect(_pxCursorX - horizontalScrollBar()->value(), _pxCursorY - _pxCharHeight, _pxCursorWidth, _pxCharHeight);

// 4. Immediately draw new cursor
_blink = true;
Expand Down Expand Up @@ -341,6 +400,8 @@ void QHexEdit::setHexCaps(const bool isCaps)
if (_hexCaps != isCaps)
{
_hexCaps = isCaps;
readBuffers();
prepareHexStrings();
viewport()->update();
}
}
Expand Down Expand Up @@ -490,6 +551,7 @@ void QHexEdit::setFont(const QFont &font)
_pxCharWidth = metrics.width(QLatin1Char('2'));
#endif
_pxCharHeight = metrics.height();
_pxDescent = metrics.descent();
_pxGapAdr = _pxCharWidth / 2;
_pxGapAdrHex = _pxCharWidth;
_pxGapHexAscii = 2 * _pxCharWidth;
Expand Down Expand Up @@ -890,14 +952,17 @@ void QHexEdit::paintEvent(QPaintEvent *event)
QPainter painter(viewport());
int pxOfsX = horizontalScrollBar()->value();

if (event->rect() != _cursorRect)
if (event->type() != QEvent::CursorChange)
{
int pxPosStartY = _pxCharHeight;

// draw some patterns if needed
painter.fillRect(event->rect(), viewport()->palette().color(QPalette::Base));
if (_addressArea)
const QColor baseColor = viewport()->palette().color(QPalette::Base);
painter.fillRect(event->rect(), baseColor);
if (_addressArea && _addressAreaColor != baseColor)
{
painter.fillRect(QRect(-pxOfsX, event->rect().top(), _pxPosHexX - _pxGapAdrHex/2, height()), _addressAreaColor);
}
if (_asciiArea)
{
int linePos = _pxPosAsciiX - (_pxGapHexAscii / 2);
Expand All @@ -910,12 +975,17 @@ void QHexEdit::paintEvent(QPaintEvent *event)
// paint address area
if (_addressArea)
{
QString address;
for (int row=0, pxPosY = _pxCharHeight; row <= (_dataShown.size()/_bytesPerLine); row++, pxPosY +=_pxCharHeight)
const QPair<int, int> curArea = qMakePair(_bPosFirst, _rowsShown);
if (_addrCachedRows != curArea)
{
prepareAddressStrings();
_addrCachedRows = curArea;
}

for (int row=0, pxPosY = _pxCharHeight; row < _rowsShown; row++, pxPosY +=_pxCharHeight)
{
address = QString("%1").arg(_bPosFirst + row*_bytesPerLine + _addressOffset, _addrDigits, 16, QChar('0'));
painter.setPen(QPen(_addressFontColor));
painter.drawText(_pxPosAdrX - pxOfsX, pxPosY, hexCaps() ? address.toUpper() : address);
painter.drawStaticText(_pxPosAdrX - pxOfsX, pxPosY - _pxCharHeight, _addrStringsPrepared[row]);
}
}

Expand All @@ -926,54 +996,48 @@ void QHexEdit::paintEvent(QPaintEvent *event)

for (int row = 0, pxPosY = pxPosStartY; row <= _rowsShown; row++, pxPosY +=_pxCharHeight)
{
QByteArray hex;
int pxPosX = _pxPosHexX - pxOfsX;
int pxPosAsciiX2 = _pxPosAsciiX - pxOfsX;
qint64 bPosLine = row * _bytesPerLine;
for (int colIdx = 0; ((bPosLine + colIdx) < _dataShown.size() && (colIdx < _bytesPerLine)); colIdx++)
{
QColor c = viewport()->palette().color(QPalette::Base);
painter.setPen(QPen(_hexFontColor));

const int ry = pxPosY - _pxCharHeight + _pxSelectionSub - _pxDescent;
const QRect hexRect = colIdx == 0
? QRect(pxPosX, ry, 2 * _pxCharWidth, _pxCharHeight)
: QRect(pxPosX - _pxCharWidth, ry, 3 * _pxCharWidth, _pxCharHeight);
const QRect asciiRect(pxPosAsciiX2, ry, _pxCharWidth, _pxCharHeight);

qint64 posBa = _bPosFirst + bPosLine + colIdx;
if ((getSelectionBegin() <= posBa) && (getSelectionEnd() > posBa))
{
c = _brushSelection.color();
painter.fillRect(hexRect, _brushSelection.color());
if (_asciiArea)
{
painter.fillRect(asciiRect, _brushSelection.color());
}
painter.setPen(_penSelection);
}
else
else if (_highlighting && _markedShown.at((int)(posBa - _bPosFirst)))
{
if (_highlighting)
if (_markedShown.at((int)(posBa - _bPosFirst)))
{
c = _brushHighlighted.color();
painter.setPen(_penHighlighted);
}
painter.fillRect(hexRect, _brushHighlighted.color());
if (_asciiArea)
{
painter.fillRect(asciiRect, _brushHighlighted.color());
}
painter.setPen(_penHighlighted);
}

// render hex value
QRect r;
if (colIdx == 0)
r.setRect(pxPosX, pxPosY - _pxCharHeight + _pxSelectionSub, 2*_pxCharWidth, _pxCharHeight);
else
r.setRect(pxPosX - _pxCharWidth, pxPosY - _pxCharHeight + _pxSelectionSub, 3*_pxCharWidth, _pxCharHeight);
painter.fillRect(r, c);
hex = _hexDataShown.mid((bPosLine + colIdx) * 2, 2);
painter.drawText(pxPosX, pxPosY, hexCaps()?hex.toUpper():hex);
painter.drawStaticText(pxPosX, pxPosY - _pxCharHeight, _hexStringsPrepared[(uchar)_dataShown.at(bPosLine + colIdx)]);
pxPosX += 3*_pxCharWidth;

// render ascii value
if (_asciiArea)
{
if (c == viewport()->palette().color(QPalette::Base))
c = _asciiAreaColor;
int ch = (uchar)_dataShown.at(bPosLine + colIdx);
if ( ch < ' ' || ch > '~' )
ch = '.';
r.setRect(pxPosAsciiX2, pxPosY - _pxCharHeight + _pxSelectionSub, _pxCharWidth, _pxCharHeight);
painter.fillRect(r, c);
painter.setPen(QPen(_asciiFontColor));
painter.drawText(pxPosAsciiX2, pxPosY, QChar(ch));
painter.drawStaticText(pxPosAsciiX2, pxPosY - _pxCharHeight, charStringPrepared(ch));
pxPosAsciiX2 += _pxCharWidth;
}
}
Expand All @@ -986,33 +1050,35 @@ void QHexEdit::paintEvent(QPaintEvent *event)
int hexPositionInShowData = _cursorPosition - 2 * _bPosFirst;

// due to scrolling the cursor can go out of the currently displayed data
if ((hexPositionInShowData >= 0) && (hexPositionInShowData < _hexDataShown.size()))
if ((hexPositionInShowData >= 0) && (hexPositionInShowData < _dataShown.size() * 2))
{
const int pxHexPosY = _pxCursorY - _pxCharHeight;

// paint cursor
if (_readOnly)
{
QColor color = viewport()->palette().dark().color();
painter.fillRect(QRect(_pxCursorX - pxOfsX, _pxCursorY - _pxCharHeight + _pxSelectionSub, _pxCharWidth, _pxCharHeight), color);
const QColor color = viewport()->palette().dark().color();
painter.fillRect(QRect(_pxCursorX - pxOfsX, pxHexPosY + _pxSelectionSub - _pxDescent, _pxCharWidth, _pxCharHeight), color);
}
else
{
if (_blink && hasFocus())
painter.fillRect(_cursorRect, this->palette().color(QPalette::WindowText));
}
if (_editAreaIsAscii)
{
// every 2 hex there is 1 ascii
int asciiPositionInShowData = hexPositionInShowData / 2;
int ch = (uchar)_dataShown.at(asciiPositionInShowData);
if (ch < ' ' || ch > '~')
ch = '.';

painter.drawText(_pxCursorX - pxOfsX, _pxCursorY, QChar(ch));
}
else
{
painter.drawText(_pxCursorX - pxOfsX, _pxCursorY, hexCaps() ? _hexDataShown.mid(hexPositionInShowData, 1).toUpper() : _hexDataShown.mid(hexPositionInShowData, 1));
}
// every 2 hex there is 1 ascii
int bytePositionInShowData = hexPositionInShowData / 2;
int ch = (uchar)_dataShown.at(bytePositionInShowData);

if (_editAreaIsAscii)
{
painter.drawStaticText(_pxCursorX - pxOfsX, pxHexPosY, charStringPrepared(ch));
}
else
{
int digit = (hexPositionInShowData % 2 == 0) ? (ch >> 4) : (ch & 0xF);
painter.drawStaticText(_pxCursorX - pxOfsX, pxHexPosY, _hexDigitStringsPrepared[digit]);
}
}

// emit event, if size has changed
Expand Down Expand Up @@ -1117,6 +1183,8 @@ void QHexEdit::init()
resetSelection(0);
setCursorPosition(0);
verticalScrollBar()->setValue(0);
prepareHexStrings();
prepareAsciiStrings();
_modified = false;
}

Expand Down Expand Up @@ -1171,7 +1239,49 @@ void QHexEdit::refresh()
void QHexEdit::readBuffers()
{
_dataShown = _chunks->data(_bPosFirst, _bPosLast - _bPosFirst + _bytesPerLine + 1, &_markedShown);
_hexDataShown = QByteArray(_dataShown.toHex());
}

void QHexEdit::prepareHexStrings()
{
const QStringList& valueStrings = hexCaps() ? hexValuesStringsUpper : hexValuesStringsLower;

for (int i = 0; i < 256; i++)
{
_hexStringsPrepared[i].setText(valueStrings.at(i));
}

const QStringList& digitStrings = hexCaps() ? hexDigitsStringsUpper : hexDigitsStringsLower;

for (int i = 0; i < 16; i++)
{
_hexDigitStringsPrepared[i].setText(digitStrings.at(i));
}
}

void QHexEdit::prepareAsciiStrings()
{
for (int i = 0; i < 0x60; i++)
{
_asciiStringsPrepared[i].setText(charStrings[i + 0x20]);
}

_asciiStringsPrepared[0x60].setText(dot);
}

void QHexEdit::prepareAddressStrings()
{
_addrStringsPrepared.resize(_rowsShown);

for (int row = 0; row < _rowsShown; row++)
{
const QString address = toHex(_bPosFirst + row * _bytesPerLine + _addressOffset, _addrDigits, hexCaps());
_addrStringsPrepared[row].setText(address);
}
}

const QStaticText& QHexEdit::charStringPrepared(int ch) const
{
return _asciiStringsPrepared[(ch < ' ' || ch > '~') ? 0x60 : ch - 0x20];
}

QString QHexEdit::toReadable(const QByteArray &ba)
Expand All @@ -1180,7 +1290,7 @@ QString QHexEdit::toReadable(const QByteArray &ba)

for (int i=0; i < ba.size(); i += 16)
{
QString addrStr = QString("%1").arg(_addressOffset + i, addressWidth(), 16, QChar('0'));
QString addrStr = toHex(_addressOffset + i, addressWidth(), hexCaps());
QString hexStr;
QString ascStr;
for (int j=0; j<16; j++)
Expand All @@ -1194,7 +1304,7 @@ QString QHexEdit::toReadable(const QByteArray &ba)
ascStr.append(QChar(ch));
}
}
result += addrStr + " " + QString("%1").arg(hexStr, -48) + " " + QString("%1").arg(ascStr, -17) + "\n";
result += addrStr + " " + singleArgFormatStr.arg(hexStr, -48) + " " + singleArgFormatStr.arg(ascStr, -17) + "\n";
}
return result;
}
Expand Down
13 changes: 12 additions & 1 deletion src/qhexedit.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include <QAbstractScrollArea>
#include <QPen>
#include <QBrush>
#include <QStaticText>
#include <QPair>

#include "chunks.h"
#include "commands.h"
Expand Down Expand Up @@ -384,6 +386,10 @@ public slots:
// Private utility functions
void init();
void readBuffers();
void prepareHexStrings();
void prepareAsciiStrings();
void prepareAddressStrings();
const QStaticText& charStringPrepared(int ch) const;
QString toReadable(const QByteArray &ba);

private slots:
Expand All @@ -405,6 +411,7 @@ private slots:
int _pxSelectionSub; // offset selection rect
int _pxCursorX; // current cursor pos
int _pxCursorY; // current cursor pos
int _pxDescent; // distance from baseline to char bottom

// Name convention: absolute byte positions in chunks start with _b
qint64 _bSelectionBegin; // first position of Selection
Expand Down Expand Up @@ -447,7 +454,11 @@ private slots:
QRect _cursorRect; // physical dimensions of cursor
QByteArray _data; // QHexEdit's data, when setup with QByteArray
QByteArray _dataShown; // data in the current View
QByteArray _hexDataShown; // data in view, transformed to hex
QStaticText _hexStringsPrepared[256]; // Cached hex strings prepared to draw
QStaticText _hexDigitStringsPrepared[16]; // Cached hex single digit strings prepared to draw
QStaticText _asciiStringsPrepared[0x61]; // Cached ascii strings prepared to draw
QVector<QStaticText> _addrStringsPrepared; // Cached address strings prepared to draw
QPair<int, int> _addrCachedRows; // Row number and count of cached rows
qint64 _lastEventSize; // size, which was emitted last time
QByteArray _markedShown; // marked data in view
bool _modified; // Is any data in editor modified?
Expand Down

0 comments on commit db6ef45

Please sign in to comment.