gpt4 book ai didi

c++ - 如何解决 QPixmap(大型绘图作业)的性能问题?

转载 作者:行者123 更新时间:2023-11-28 06:43:50 29 4
gpt4 key购买 nike

我正在编写一个小型 map 编辑器(带有矩形图 block ),我需要一种方法来绘制大量图像或一个大图像。该应用程序很简单:您可以使用鼠标在空白屏幕上绘制图像,完成后可以将其保存。一个图 block 由一个小图像组成。

我尝试了几种显示磁贴的解决方案:

  1. 每个图 block 都有自己的QGraphicsItem(这会起作用,直到你有一个1000x1000 map )
  2. 每个图 block 都绘制在一个大的 QPixmap 上(这意味着一个非常大的图像。示例:1000x100 的 map ,每个图 block 的大小为 32x32 意味着 QPixmap大小为 32000x32000。这是 QPainter 的问题。)
  3. 当前的解决方案:遍历 TileLayer 的宽度和高度,并使用 painter->drawPixmap() 绘制每个单独的图 block >。我的 TileLayerpaint() 方法如下所示:

    void TileLayerGraphicsItem::paint(QPainter* painter, const QStyleOptionGraphicsItem*     option,QWidget* /*widget*/)
    {
    painter->setClipRect(option->exposedRect);
    int m_width=m_layer->getSize().width();
    int m_height=m_layer->getSize().height();
    for(int i=0;i<m_width;i++)
    {
    for(int j=0;j<(m_height);j++)
    {
    Tile* thetile=m_layer->getTile(i,j);
    if(thetile==NULL)continue;
    const QRectF target(thetile->getLayerPos().x()*thetile->getSize().width(),thetile->getLayerPos().y()*thetile->getSize().height(),thetile->getSize().width(),thetile->getSize().height());
    const QRectF source(0, 0, thetile->getSize().width(), thetile->getSize().height());
    painter->drawImage(target,*thetile->getImage(),source);
    }
    }}

这适用于具有 100x100 甚至 1000x100 block 的小 map 。但不适用于 1000x1000。整个应用程序开始滞后,这当然是因为我有一个非常昂贵的 for 循环。为了使我的工具有用,我需要能够制作至少 1000x1000 的瓷砖 map 而不会出现延迟。有谁知道我能做什么?我应该如何表示图 block ?

更新:

我更改了以下内容:只有超过小 map 窗口大小的 map 才会被绘制为每个图 block 绘制单个像素。现在这是我的渲染函数:

void RectangleRenderer::renderMinimapImage(QPainter* painter, TileMap* map,QSize windowSize)
{
for(int i=0;i<map->getLayers().size();i++)
{
TileLayer* currLayer=map->getLayers().at(i);
//if the layer is small draw it completly
if(windowSize.width()>currLayer->getSize().width()&&windowSize.height()>currLayer->getSize().height())
{
...
}
else // This is the part where the map is so big that only some pixels are drawn!
{
painter->fillRect(0,0,windowSize.width(),windowSize.height(),QBrush(QColor(map->MapColor)));
for(float i=0;i<windowSize.width();i++)
for(float j=0;j<windowSize.height();j++)
{
float tX=i/windowSize.width();
float tY=j/windowSize.height();
float pX=lerp(i,currLayer->getSize().width(),tX);
float pY=lerp(j,currLayer->getSize().height(),tY);
Tile* thetile=currLayer->getTile((int)pX,(int)pY);
if(thetile==NULL)continue;
QRgb pixelcolor=thetile->getImage()->toImage().pixel(thetile->getSize().width()/2,thetile->getSize().height()/2);
QPen pen;
pen.setColor(QColor::fromRgb(pixelcolor));
painter->setPen(pen);
painter->drawPoint(i,j);
}
}
}

这不正确,但速度非常快。问题是我的 lerp(线性插值)函数获取正确的图 block 以从中绘制像素。当我遍历小 map 像素时,有人有更好的解决方案来获得正确的图 block 吗?目前,我在 0 和 tilemap 的最大尺寸之间使用线性插值,但它无法正常工作。

更新 2

    //currLayer->getSize() returns how many tiles are in the map
// currLayer->getTileSize() returns how big each tile is (32 pixels width for example)
int raw_width = currLayer->getSize().width()*currLayer->getTileSize().width();
int raw_height = currLayer->getSize().height()*currLayer->getTileSize().height();
int desired_width = windowSize.width();
int desired_height = windowSize.height();
int calculated_width = 0;
int calculated_height = 0;

// if dealing with a one dimensional image buffer, this ensures
// the rows come out clean, and you don't lose a pixel occasionally
desired_width -= desired_width%2;

// http://qt-project.org/doc/qt-5/qt.html#AspectRatioMode-enum
// Qt::KeepAspectRatio, and the offset can be used for centering
qreal ratio_x = (qreal)desired_width / raw_width;
qreal ratio_y = (qreal)desired_height / raw_height;

qreal floating_factor = 1;
QPointF offset;
if(ratio_x < ratio_y)
{
floating_factor = ratio_x;
calculated_height = raw_height*ratio_x;
calculated_width = desired_width;
offset = QPointF(0, (qreal)(desired_height - calculated_height)/2);
}
else
{
floating_factor = ratio_y;
calculated_width = raw_width*ratio_y;
calculated_height = desired_height;
offset = QPointF((qreal)(desired_width - calculated_width)/2,0);
}

for (int r = 0; r < calculated_height; r++)
{
for (int c = 0; c < calculated_width; c++)
{
//trying to do the following: use your code to get the desired pixel. Then divide that number by the size of the tile to get the correct pixel
Tile* thetile=currLayer->getTile((int)((r * floating_factor)*raw_width)/currLayer->getTileSize().width(),(int)(((c * floating_factor)*raw_height)/currLayer->getTileSize().height()));
if(thetile==NULL)continue;
QRgb pixelcolor=thetile->getImage()->toImage().pixel(thetile->getSize().width()/2,thetile->getSize().height()/2);
QPen pen;
pen.setColor(QColor::fromRgb(pixelcolor));
painter->setPen(pen);
painter->drawPoint(r,c);
}
}

尝试对示例代码进行逆向工程,但仍然无法正常工作。

更新3

我再次尝试(更新 1)线性插值。当我查看代码时,我看到了错误:

float pX=lerp(i,currLayer->getSize().width(),tX);
float pY=lerp(j,currLayer->getSize().height(),tY);

应该是:

float pX=lerp(0,currLayer->getSize().width(),tX);
float pY=lerp(0,currLayer->getSize().height(),tY);

就是这样。现在可以了。

最佳答案

这显示了如何正确地做到这一点。您使用细节级别 (lod) 变量来确定如何根据缩放比例绘制屏幕上当前可见的元素。

http://qt-project.org/doc/qt-5/qtwidgets-graphicsview-chip-example.html

也不要遍历所有可能可见的元素,而只遍历已更改的元素,并且仅遍历那些当前可见的元素。

您的下一个选择是使用其他一些手动缓存,因此您不必不断地重复迭代 O(n^2)。

如果您不能针对 QGraphicsView/QGraphicsScene 对其进行优化...那么 OpenGL 可能是您想要研究的对象。它可以直接在显卡上执行大量绘图和缓存操作,因此您不必担心太多。

更新:在工作线程上推送对 QImage 的更改可以让您缓存和更新缓存,同时让程序的其余部分保持响应,然后您使用排队连接返回 GUI 线程以将 QImage 绘制为 Pixmap。

如果您友好地询问,QGraphicsView 会让您知道哪些图 block 是可见的:

http://qt-project.org/doc/qt-5/qgraphicsview.html#items-5

更新 2: http://qt-project.org/doc/qt-5/qtwidgets-graphicsview-chip-chip-cpp.html

您可能需要调整项目允许的缩小范围以测试此功能...

在它有的地方

const qreal lod = option->levelOfDetailFromTransform(painter->worldTransform());
if (lod < 0.2) {
if (lod < 0.125) {
painter->fillRect(QRectF(0, 0, 110, 70), fillColor);
return;
}

QBrush b = painter->brush();
painter->setBrush(fillColor);
painter->drawRect(13, 13, 97, 57);
painter->setBrush(b);
return;
}

添加如下内容:

if(lod < 0.05)
{
// using some sort of row/col value to know which ones to not draw...
// This below would only draw 1/3 of the rows and 1/3 of the column
// speeding up the redraw by about 9x.
if(row%3 != 0 || col%3 != 0)
return;// don't do any painting, return
}

更新 3:

抽取示例:

// How to decimate an image to any size, properly
// aka fast scaling
int raw_width = 1000;
int raw_height = 1000;
int desired_width = 300;
int desired_height = 200;
int calculated_width = 0;
int calculated_height = 0;

// if dealing with a one dimensional image buffer, this ensures
// the rows come out clean, and you don't lose a pixel occasionally
desired_width -= desired_width%2;

// http://qt-project.org/doc/qt-5/qt.html#AspectRatioMode-enum
// Qt::KeepAspectRatio, and the offset can be used for centering
qreal ratio_x = (qreal)desired_width / raw_width();
qreal ratio_y = (qreal)desired_height / raw_height();

qreal floating_factor = 1;
QPointF offset;
if(ratio_x < ratio_y)
{
floating_factor = ratio_x;
calculated_height = raw_height*ratio_x;
calculated_width = desired_width;
offset = QPointF(0, (qreal)(desired_height - calculated_height)/2);
}
else
{
floating_factor = ratio_y;
calculated_width = raw_width*ratio_y;
calculated_height = desired_height;
offset = QPointF((qreal)(desired_width - calculated_width)/2);
}

for (int r = 0; r < calculated_height; r++)
{
for (int c = 0; c < calculated_width; c++)
{
pixel[r][c] = raw_pixel[(int)(r * floating_factor)*raw_width][(int)(c * floating_factor)];
}
}

希望对您有所帮助。

关于c++ - 如何解决 QPixmap(大型绘图作业)的性能问题?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25412993/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com