QGraphicsScene
を使用しても改善されません。これはQWidget
の上に追加されたレイヤーです。生の演奏が終わったので、それを使ってはいけません。
はあなたのメモリ・バッファ/ファイルの可視部分のためのviewmodelとしてQTextDocument
を実装することができますが、あなたはスクロール新鮮QTextDocument
たびに塗装しても、速くQWidget
に直接物事を描画するよりもではないでしょう。
QStaticText
を使用することは、正しい方向のステップですが、不十分です。レンダリングQStaticText
では、依然としてグリフのシェイプのラスタライズが必要です。レンダリングしたいそれぞれのQChar, QColor
の組み合わせのピクスマップをよりよくキャッシュしてキャッシュすることができます。QStaticText
を使用するかどうかにかかわらず、キャラクタのアウトラインをラスタライズするよりもはるかに高速です。
個々の文字を描画する代わりに、キャッシュからピックスマップを描画します。 This commitはこのアプローチを実証しています。文字描画方法は次のとおりです。
各(文字、前景、背景)タプルをキャッシュすることもできます。悲しいかな、前景と背景の組み合わせがたくさんある場合、これはすぐに手に入らなくなります。
背景がすべて同じ色(白など)の場合は、文字のネガティブマスクを保存することをお勧めします。glyph
は白い背景と透明な形をしています。 This commitはこのアプローチを実証しています。
void drawChar(const QPointF & pos, QChar ch, QColor color, QPainter & p) {
auto & glyph = m_glyphs[ch];
if (glyph.isNull()) {
glyph = QImage{m_glyphRect.size().toSize(), QImage::Format_ARGB32_Premultiplied};
glyph.fill(Qt::white);
QPainter p{&glyph};
p.setCompositionMode(QPainter::CompositionMode_DestinationOut);
p.setFont(m_font);
p.drawText(m_glyphPos, {ch});
}
auto rect = m_glyphRect;
rect.moveTo(pos);
p.fillRect(rect, color);
p.drawImage(pos, glyph);
}
の代わりに与えられた色の完全プリレンダリング文字を格納し、あなただけのアルファマスクと複合して上に格納することができ:グリフの矩形はその後、白いマスクが上に適用され、グリフの色で満たされています-demand:
- 透明な背景にあらかじめレンダリングされた白いグリフから始めます。
CompositionMode_SourceOut
に背景のグリフ矩形を塗りつぶします。背景には、文字自体の穴が残っています。
- フォアグラウンドでグリフの矩形を塗りつぶします。
CompositionMode_DestinationOver
:フォアグラウンドで穴が埋められます。
- ウィジェットにコンポジットを描画します。
これは、かなり高速であることが判明しています。そうしたい場合は、コンポジットのレンダリングを完全に並列化できます(読者の練習として残します)。
注:プリレンダリングされたグリフは、アルファとの色のあらかじめ乗算を使用して、より薄く表示することができます。
優れたパフォーマンスを備えたさらに別のアプローチは、GPUを使用してテキストモードの表示をエミュレートすることです。プリレンダリングされたグリフのアウトラインをテクスチャに格納し、レンダリングするグリフのインデックスと色を配列に格納し、OpenGLと2つのシェーダを使用してレンダリングします。 This exampleは、このようなアプローチを実装するための出発点かもしれません。
完全な例として、CPUレンダリングを使用します。
// https://github.com/KubaO/stackoverflown/tree/master/questions/hex-widget-40458515
#include <QtWidgets>
#include <algorithm>
#include <cmath>
const QString & CP437() {
static auto const set = QStringLiteral(
" ☺☻♥♦♣♠•◘○◙♂♀♪♫☼▶◀↕‼¶§▬↨↑↓→←∟↔▲▼"
"␣!\"#$%&'()*+,-./:;<=>?"
"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
"`abcdefghijklmnopqrstuvwxyz{|}~ "
"ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢£¥₧ƒ"
"áíóúñѪº¿⌐¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐"
"└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀"
"αßΓπΣσµτΦΘΩδ∞φε∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■ ");
return set;
}
HexView
ウィジェットはQAbstractScrollArea
から派生し、データのメモリマップされたチャンクを可視化:
class HexView : public QAbstractScrollArea {
const int m_addressChars = 8;
const qreal m_dataMargin = 4.;
const char * m_data;
size_t m_size;
size_t m_start = 0;
QRectF m_glyphRect{0.,0.,1.,1.};
QPointF m_glyphPos;
int m_chars, m_lines;
QMap<QChar, QImage> m_glyphs;
QFont m_font{"Monaco"};
qreal xStep() const { return m_glyphRect.width(); }
qreal yStep() const { return m_glyphRect.height(); }
static QChar decode(char ch) { return CP437()[(uchar)ch]; }
void drawChar(const QPointF & pos, QChar ch, QColor fg, QColor bg, QPainter & p) {
auto & glyph = m_glyphs[ch];
if (glyph.isNull()) {
glyph = QImage{m_glyphRect.size().toSize(), QImage::Format_ARGB32_Premultiplied};
glyph.fill(Qt::transparent);
QPainter p{&glyph};
p.setPen(Qt::white);
p.setFont(m_font);
p.drawText(m_glyphPos, {ch});
}
QImage composite = glyph;
{
QPainter p{&composite};
p.setCompositionMode(QPainter::CompositionMode_SourceOut);
p.fillRect(composite.rect(), bg);
p.setCompositionMode(QPainter::CompositionMode_DestinationOver);
p.fillRect(composite.rect(), fg);
}
auto rect = m_glyphRect;
rect.moveTo(pos);
p.drawImage(pos, composite);
}
void initData() {
qreal width = viewport()->width() - m_addressChars*xStep() - m_dataMargin;
m_chars = (width > 0.) ? width/xStep() : 0.;
m_lines = viewport()->height()/yStep();
if (m_chars && m_lines) {
verticalScrollBar()->setRange(0, m_size/m_chars);
verticalScrollBar()->setValue(m_start/m_chars);
} else {
verticalScrollBar()->setRange(0, 0);
}
}
void paintEvent(QPaintEvent *) override {
QPainter p{viewport()};
QPointF pos;
QPointF step{xStep(), 0.};
auto dividerX = m_addressChars*xStep() + m_dataMargin/2.;
p.drawLine(dividerX, 0, dividerX, viewport()->height());
int offset = 0;
while (offset < m_chars*m_lines && m_start + offset < m_size) {
auto rawAddress = QString::number(m_start + offset, 16);
auto address = QString{m_addressChars-rawAddress.size(), ' '} + rawAddress;
for (auto c : address) {
drawChar(pos, c, Qt::black, Qt::white, p);
pos += step;
}
pos += QPointF{m_dataMargin, 0.};
auto bytes = std::min(m_size - offset, (size_t)m_chars);
for (int n = bytes; n; n--) {
drawChar(pos, decode(m_data[m_start + offset++]), Qt::red, Qt::white, p);
pos += step;
}
pos = QPointF{0., pos.y() + yStep()};
}
}
void resizeEvent(QResizeEvent *) override {
initData();
}
void scrollContentsBy(int, int) override {
m_start = verticalScrollBar()->value() * (size_t)m_chars;
viewport()->update();
}
public:
HexView(QWidget * parent = nullptr) : HexView(nullptr, 0, parent) {}
HexView(const char * data, size_t size, QWidget * parent = nullptr) :
QAbstractScrollArea{parent}, m_data(data), m_size(size)
{
auto fm = QFontMetrics(m_font);
for (int i = 0x20; i < 0xE0; ++i)
m_glyphRect = m_glyphRect.united(fm.boundingRect(CP437()[i]));
m_glyphPos = {-m_glyphRect.left(), -m_glyphRect.top()};
initData();
}
void setData(const char * data, size_t size) {
if (data == m_data && size == m_size) return;
m_data = data;
m_size = size;
m_start = 0;
initData();
viewport()->update();
}
};
私たちは、現代の活用私たちは、CP437文字セットで始まる
64ビットシステムとメモリマップは、ウィジェットによって可視化されるソースファイルをマップします。テスト目的のために、文字セットのビューも利用できます。
int main(int argc, char ** argv) {
QApplication app{argc, argv};
QFile file{app.applicationFilePath()};
if (!file.open(QIODevice::ReadOnly)) return 1;
const char * const map = (char*)file.map(0, file.size(), QFile::MapPrivateOption);
if (!map) return 2;
QWidget ui;
QGridLayout layout{&ui};
HexView view;
QRadioButton exe{"Executable"};
QRadioButton charset{"Character Set"};
layout.addWidget(&view, 0, 0, 1, 3);
layout.addWidget(&exe, 1, 0);
layout.addWidget(&charset, 1, 1);
QObject::connect(&exe, &QPushButton::clicked, [&]{
view.setData(map, (size_t)file.size());
});
QObject::connect(&charset, &QPushButton::clicked, [&]{
static QByteArray data;
if (data.isNull()) {
data.resize(256);
for (int i = 0; i < data.size(); ++i) data[i] = (char)i;
}
view.setData(data.constData(), (size_t)data.size());
});
charset.click();
ui.show();
return app.exec();
}
コードは現在何がわかりましたか?あなたはQTextEditを使用していますか?テキストを挿入するのにQTextCursorを使用していますか?あなたはQGraphicsSceneを使っていますか? – GabeWeiss
ベースラインを設定しましたか? QTextEditまたはQGraphicsScene、またはQMLでテキストを描画しますか? – rubenvb
関連情報とともに投稿を更新しました。私は本当に 'QTextEdit'を使うことはできません。なぜなら、それはそれ自身のバッファを管理しているようです。膨大なデータ(10テラバイトなど)を遅延なく管理(コピー/貼り付け)できるようにするためには自分のアプリケーションが必要なので、私自身のバッファを用意する必要があります。 – antonone