Russian Qt Forum
Сентябрь 30, 2024, 10:28 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2 3   Вниз
  Печать  
Автор Тема: QGraphicsView, QGraphicsScene для отображения огромного файла (2хОЗУ)  (Прочитано 31308 раз)
sk8ter
Гость
« : Март 08, 2009, 17:22 »

Общая задача: Есть графический файл *.bmp с разрешением примерно (40 000 х 30 000 точек), ясно что в ОЗУ не запихнешь, его нужно отобразить с возможностью перемещения БЕЗ каких либо тормозов.
Для того, чтобы отобразить такое большое изображение ясно что надо читать его с диска, для этого создаю свою структуру данных (т.е. свой файл, с которым буду работать). В эти подробности вникать не стоит, важно то, что могу из него прочитать заданный квадрат, разрешением (допустим) 500х500 пикселей в матрицу такого же размера (RGBTRIPLE mas[divide][divide];   //divide = 500).
Есть массив, каждый елемент которого указывает на соответствующую загруженную матрицу (указатели в матрице постоянно меняются при перемещении - загружаются новые блоки, а старые стираются) (Считываются только нужные блоки).
Сам вопрос: Как сделать чтобы при перемещении изображения полосами прокрутки или мышью, актуальные массивы отображались на экране?

Записан
BRE
Гость
« Ответ #1 : Март 08, 2009, 18:03 »

Такой вопрос, как работают тайловые (tile) карты в 2D играх ты представляшь?
Такие вещи легко обьяснить с листом бумаги и карандашом, а так писанины очень много может получиться.
« Последнее редактирование: Март 15, 2009, 10:08 от BRE » Записан
sk8ter
Гость
« Ответ #2 : Март 08, 2009, 18:24 »

нет, к сожалению не представляю..., спс - обращу на них внимание...
Согласен что с листом бумаги объясняется за 2 мин, но ... это не листок бумаги
Записан
BRE
Гость
« Ответ #3 : Март 08, 2009, 18:53 »

Размер карты в точках mapSize = 40000x30000
Размер тайла tileSize = 500x500
Вся карта 80x60 тайлов

Для отображения части карты на экране вводим понятия - окно-просмотра (viewport), для простоты - 1000x1000 точки или 2x2 тайла.
Это окно, как бы накладывается на карту и его можно по ней двигать.
Делаем переменную posViewport, которая показывает положение viewport в точках.
Когда posViewport == [0, 0], это значит окно-просмотра находиться в верхнем левом углу карты.
Конечные координаты на которые можно сдвинуть это окно [39000,29000].

Теперь нам нужно понять какие тайлы сейчас видны на экране.
viewTile.x = posViewport.x / tileSize.x;
viewTile.y = posViewport.y / tileSize.y;

Пример:
posViewport = 1024,370;
scrollTile.x = 2;
scrollTile.y = 0;

Значит нам нужны тайлы (т.к. на viewport помещается только два тайла):
0 1 2 3 4 5 6 7 8 ...
1    * *
2    * *
3
4
5

Но, нам нужно еще сделать скролинг по-пиксельно, поэтому в память загружаем еще по одному ряду и столбцу тайлов.
0 1 2 3 4 5 6 7 8 ...
1    * *  +
2    * *  +
3    + + +
4
5

Для получения сдвига в точках уже относительно тайлов, делаем:
scrollPixel.x = posViewport.x % tileSize.x;
scrollPixel.y = posViewport.y % tileSize.y;
Для нашего примера:
scrollPixel = [ 24, 370 ]

Пока так, а то может я непонятно объясняю.  Подмигивающий
Записан
sk8ter
Гость
« Ответ #4 : Март 08, 2009, 19:08 »

Почему же не понятно, пока все понятно!
Интересный вариант Подмигивающий
а можно так сделать, чтобы в памяти с каждой стороны было еще допустим по 2 тайла? чтобы если переташить совсем немного к жесткому диску заново не обращаться?
Записан
BRE
Гость
« Ответ #5 : Март 08, 2009, 19:16 »

Почему же не понятно, пока все понятно!
Интересный вариант Подмигивающий
а можно так сделать, чтобы в памяти с каждой стороны было еще допустим по 2 тайла? чтобы если переташить совсем немного к жесткому диску заново не обращаться?
Почему нет, это тебе решать...
Теперь к QGraphics:
Делаем сцену размером 40000x30000, на ней один к одному размещаем объекты tile, каждый из которых будет знать какую часть карты он должен выводить, но сами картинки еще не загружаем.
И теперь в зависимости от текущего сдвига определяем какие тайлы в данный момент видны в viewport и соответствующим тайлам говорим (+их сосетям) говорим загрузить картинки из файла. После изменения текущего сдивга viewport, определяем какие теперь тайлы видны и невидимых говорим удалить картинки из памяти.

Можно динамически создавать необходимые тайлы и размещать их на сцене. Вообщем много чего можно сделать.  Улыбающийся
Записан
sk8ter
Гость
« Ответ #6 : Март 08, 2009, 19:38 »

спс, уже пробую...      Ты не вкурсе, почему этих тайлов в qt4 нет? Это немного настораживает.
если будут другие варинты - внимательно выслушаю.
Записан
BRE
Гость
« Ответ #7 : Март 08, 2009, 19:40 »

спс, уже пробую...      Ты не вкурсе, почему этих тайлов в qt4 нет? Это немного настораживает.

Код
C++ (Qt)
class Tile : public QGraphicsItem
{
...
};
 
Записан
sk8ter
Гость
« Ответ #8 : Март 08, 2009, 19:51 »

похоже все намного проще чем я думал )
Записан
sk8ter
Гость
« Ответ #9 : Март 09, 2009, 21:52 »

Кстати, если комуто надо - могу после того как сделаю выложить исходники!!!
Записан
sk8ter
Гость
« Ответ #10 : Март 14, 2009, 21:05 »

Делал по разному...  много способов пробовал, вот один из способов:
При нажатии на кнопку, поидее память должна освободится, но .....  увы...

main.cpp
Код:
#include <QtGui>
#include <iostream.h>
#include "MyView.h"


int main(int argc, char** argv)
{
    QApplication   app(argc, argv);
    QGraphicsScene scene(QRectF(0, 0, 30000, 30000));
    MyView * pView      = new MyView(&scene);
    QWidget w;
    QPushButton *btn = new QPushButton("Button", pView);
    QHBoxLayout *lay = new QHBoxLayout;
    QObject::connect(btn, SIGNAL(clicked()), pView, SLOT(pressBotton()));

    std::cout<<"run\n";
    pView->init();
    //pView->show();

    lay->addWidget(btn);
    lay->addWidget(pView);

    w.setLayout(lay);
    w.show();
 
    return app.exec();
}

tile.h
Код:
#ifndef TILE_H
#define TILE_H

#include <QtGui>

class Tile
{
private:
    int x;
    int y;
    QGraphicsRectItem * rect;
public:
    Tile(int pos_x, int pos_y, QGraphicsRectItem * qGraphicsRectItem)
    {
        x = pos_x;
        y = pos_y;
        rect = qGraphicsRectItem;
    }
    int pos_x()
    { return x; }
    int pos_y()
    { return y; }
    QGraphicsRectItem* item()
    { return rect; }
};

#endif // TILE_H

MyView.h
Код:
#ifndef _MyView_h_
#define _MyView_h_

#include <QGraphicsView>
#include <windows.h>
#include "tile.h"


class MyView: public QGraphicsView {
    Q_OBJECT
public:
    MyView(QGraphicsScene* pScene, QWidget* pwgt = 0)
        : QGraphicsView(pScene, pwgt)
    {
        resize(1000, 800);
        setDragMode(QGraphicsView::ScrollHandDrag);
    }

private:
    static const int divide = 500;
    RGBTRIPLE mas[divide][divide];
    tagBITMAPFILEHEADER stHead;
    tagBITMAPINFOHEADER stInfoHead;
    FILE *pInFile2;
    QList<Tile *> tileList;

public slots:
    void pressBotton();

private slots:
    void scrollBars();

public:
    void moved(int verticalScrollBar, int horizontalScrollBar);
    void init();
    Tile * showRect(int x, int y);
};

#endif  //_MyView_h_

MyView.cpp
Код:
#include "MyView.h"
#include "tile.h"
#include <QtGui>
#include <iostream>
using namespace std;


void MyView::scrollBars()
{
    moved(this->verticalScrollBar()->value(), this->horizontalScrollBar()->value());
}

void MyView::moved(int verticalScrollBar, int horizontalScrollBar)
{
    static int qw = 1;
    if (qw == 1)
    {
        qw--;
        return;
    }

    // Сохраняем
    static int xx = 0, yy = 0;


    //Теперь нам нужно понять какие тайлы сейчас видны на экране.
    int viewTile_x = horizontalScrollBar / divide;
    int viewTile_y = verticalScrollBar / divide;
    int colTile_x = ((this->rect().height() + (verticalScrollBar % divide)) / divide) + 1;
    int colTile_y = ((this->rect().width() + (horizontalScrollBar % divide)) / divide) + 1;
    cout<<"\nhor: "<< viewTile_x <<" "<<colTile_x;
    cout<<"\nver: "<< viewTile_y <<" "<<colTile_y;


    std::cout<< "\nvert x hor: " << verticalScrollBar << " x " << horizontalScrollBar;
    if ((abs(xx - viewTile_x) + abs(yy - viewTile_y)) < 1)
        return;


    // Отображение
    for (int y = viewTile_y; y < viewTile_y + colTile_y; y++)
        for (int x = viewTile_x; x < viewTile_x + colTile_x + 1; x++)
        {
            tileList.append(showRect(x, y));
        }

    xx = viewTile_x;
    yy = viewTile_y;
}


void MyView::init()
{
    connect(this->horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(scrollBars()));
    connect(this->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(scrollBars()));

    // Чтение изображения из файла
    FILE *pInFile;
    if ((pInFile = fopen("1.bmp", "rb")) != NULL)
    {
        // Чтение заголовков
        fread(&stHead, sizeof(tagBITMAPFILEHEADER), 1, pInFile);
        fread(&stInfoHead, sizeof(tagBITMAPINFOHEADER), 1, pInFile);
        fclose(pInFile);
        cout<<"file readed\n";
    }

    cout<<"\nWidth: "<<stInfoHead.biWidth;
    cout<<"\nHeight: "<<stInfoHead.biHeight;


    // Преобразование файла в свою структуру данных
    if ((pInFile2 = fopen("1.slko", "rb")) == NULL)
    {
        RGBTRIPLE ** BitMap = new RGBTRIPLE *[divide];
        FILE *pInFile1 = fopen("1.slko", "wb");

        for(int i = 0; i < divide; i++)
        {
            BitMap[i] = new RGBTRIPLE[stInfoHead.biWidth];
        }
        cout<<"\nkontrol point 1\n";
        for(int col_h = stInfoHead.biHeight / divide; col_h > 0 ; col_h--)
        {
            for (int i = 0; i < divide; i++)
            {
                fseek(pInFile, (stHead.bfOffBits + ((stInfoHead.biWidth * 3) * ((divide * col_h) - i))), SEEK_SET);
                fread(BitMap[i], sizeof(RGBTRIPLE), stInfoHead.biWidth, pInFile);
            }
            cout<<"kontrol point 2\n";

            // Осталось разбить матрицу (500 х stInfoHead.biWidth) на квадраты
            for (int y = 0; y < stInfoHead.biWidth / divide; y++)
            {
                for (int j = 0; j < divide; j++)
                {
                    for (int k = 0; k < divide; k++)
                    {
                        mas[j][k] = BitMap[j][y * divide + k];
                    }
                }
                fwrite(mas, sizeof(RGBTRIPLE), divide*divide, pInFile1);
            }
        }
        cout<<"kontrol point";
        fclose(pInFile1);
        pInFile2 = fopen("1.slko", "rb");
    }
}


Tile * MyView::showRect(int x, int y)
{
    // Просто отображаем один квадрат
    int kolW = stInfoHead.biWidth / divide;
    fseek(pInFile2, (divide * divide * 3 * (y * kolW + x)), SEEK_SET);
    fread(mas, sizeof(RGBTRIPLE), divide*divide, pInFile2);
    QImage *img = new QImage(divide, divide, QImage::Format_RGB32);
    for(int i = 0; i < divide; i++)
        for(int j = 0; j < divide; j++)
        {
            QRgb rgb = qRgb(mas[i][j].rgbtRed, mas[i][j].rgbtGreen, mas[i][j].rgbtBlue);
            img->setPixel(j, i, rgb);
        }
    QGraphicsRectItem * item = new QGraphicsRectItem(x * divide, y * divide, divide, divide, 0, this->scene());
    //item->setPen(Qt::NoPen);
    item->setBrush(QPixmap::fromImage(*img));
    Tile *tile = new Tile(x, y, item);

    return tile;

    //this->scene()->itemAt(x * divide, y * divide)->~QGraphicsItem();
    //return this->scene()->addRect(x * divide, y * divide, divide, divide, Qt::NoPen, QBrush(QPixmap::fromImage(*img)));
}


void MyView::pressBotton()
{
    cout<<"\nPress";

    cout<<"\ntile count: "<<tileList.count();
    QList<QGraphicsItem *> oldList;
    oldList = this->scene()->items();
    cout<<"\noldList: "<<oldList.count();

    for (int i = 0; i < tileList.count(); ++i)
    {
        cout<<'\n'<<tileList[i]->pos_x();
        tileList[i]->item()->~QGraphicsRectItem();
        tileList.removeAt(i);
    }
}
Записан
Rcus
Гость
« Ответ #11 : Март 14, 2009, 21:29 »

/*:D*/
new QGraphicsRectItem...
удалять надо через delete, а еще для удаления всех объектов в контейнере указателей есть qDeleteAll, а еще есть
Цитировать
void QGraphicsScene::clear ()   [slot]

Removes and deletes all items from the scene, but otherwise leaves the state of the scene unchanged.
и создавать QGraphicsRectItem можно через
Цитировать
QGraphicsRectItem * addRect ( const QRectF & rect, const QPen & pen = QPen(), const QBrush & brush = QBrush() )
запись с указанием сцены последним параметром вообще недокументирована,
Код:
    QGraphicsItem(QGraphicsItem *parent = 0
#ifndef Q_QDOC
                  // obsolete argument
                  , QGraphicsScene *scene = 0
#endif
        );
Записан
sk8ter
Гость
« Ответ #12 : Март 14, 2009, 21:54 »

Даже если сделать так, в Диспетчере задач память не уменьшится...
Уже не знаю что делать....   в итоге она закончится и программа вылетит...

Код:
void MyView::pressBotton()
{
    cout<<"\nPress";

    cout<<"\ntile count: "<<tileList.count();
    cout<<"\noldList: "<<this->scene()->items().count();

    tileList.clear();
    this->scene()->clear();
}
Записан
Rcus
Гость
« Ответ #13 : Март 14, 2009, 22:02 »

Диспетчер задач никогда не пытался заменить средства по анализу утечек памяти. Можно что-то компилируемое посмотреть? А то я не вижу как удаляются QImage и Tile.
Записан
sk8ter
Гость
« Ответ #14 : Март 14, 2009, 22:26 »

Я все тексты выложил и все должно компилироваться.
Вот:

Только что обнаружил такую фишку:
в развернутом состоянии, когда прокручиваешь изображение прога весит > 20 Мб, и растет при загрузке новых тайлов, в свернутом состоянии прога весит 1.5 Мб, , потом обратно разворачиваешь - опять растет с 20 и дальше...
Что за бред Улыбающийся

Цитировать
А то я не вижу как удаляются QImage и Tile.
Они и не удаляются, я не знаю ка это сделать Подмигивающий
Записан
Страниц: [1] 2 3   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.201 секунд. Запросов: 21.