5

I'm pretty new to C++/Qt and I'm trying to create an application with Visual Studio C++ and Qt (4.8.3). The application displays images using a QGraphicsView, I need to change the images at pixel level.

The basic code is (simplified):

QImage* img = new QImage(img_width,img_height,QImage::Format_RGB32);
while(do_some_stuff) {
  img->setPixel(x,y,color);
}
QGraphicsPixmapItem* pm = new QGraphicsPixmapItem(QPixmap::fromImage(*img));
QGraphicsScene* sc = new QGraphicsScene;
sc->setSceneRect(0,0,img->width(),img->height());
sc->addItem(pm);
ui.graphicsView->setScene(sc);

This works well for images up to around 12000x6000 pixel. The weird thing happens beyond this size. When I set img_width=16000 and img_height=8000, for example, the line img = new QImage(...) returns a null image. The image data should be around 512,000,000 bytes, so it shouldn't be too large, even on a 32 bit system. Also, my machine (Win 7 64bit, 8 GB RAM) should be capable of holding the data.

I've also tried this version:

uchar* imgbuf = (uchar*) malloc(img_width*img_height*4);
QImage* img = new QImage(imgbuf,img_width,img_height,QImage::Format_RGB32);

At first, this works. The img pointer is valid and calling img->width() for example returns the correct image width (instead of 0, in case the image pointer is null). But as soon as I call img->setPixel(), the pointer becomes null and img->width() returns 0.

So what am I doing wrong? Or is there a better way of modifying large images on pixel level?

Regards, David

David Günzel
  • 73
  • 1
  • 4
  • Try to manually zero the malloc'd memory first to see if the allocation is actually working. – Tomas Andrle Nov 26 '12 at 23:43
  • Take a look at this: [Qt Project Wiki: Loading Large Images](http://qt-project.org/wiki/LoadingLargeImages) – dschulz Nov 27 '12 at 00:02
  • Remember that on a 32 bit windows application the default largest allocation you can make a result of address space fragmentation will be around 1.2GB (of the 2GB application address space) without using the /LARGEADDRESSAWARE linker flag and/or rebasing the dlls your application uses to reduce the fragmentation. – drescherjm Nov 27 '12 at 01:57
  • ---UPDATE--- Meanwhile i was able to narrow down the problem. Now I can create images up to ~16000x8000 pixels and can even use `img->setPixel()`. Previously I updated the image during "rendering". It seems like sending the image to the scene makes it impossible to use `setPixel()` again (the image becomes "null"). With only sending the image to the graphicsView (after the while-loop) it works. Now I just need to figure out how I can update the image during "rendering" nethertheless. – David Günzel Nov 28 '12 at 17:08

3 Answers3

4

QImage supports a maximum of 32768x32768 px images (signed short). This follows from the condition: width * height * colordepth < INT_MAX (4 billion) -> 32768 * 32768 * 4 = 4 billion. The second condition is of course that malloc is able to allocate the requested memory.

If you really need bigger images you will have to use another wrapper or split into multiple QImage's.

2

I tracked down the problem. I just forgot to add the /LARGEADDRESSAWARE flag to the Linker options.

I did also really appreciate Stephen Chu's answer with the scanLine() hint. First it saves some memory, second it's really a lot faster.

Now I can safely create images up to 32000x16000 pixel which was my desired aim.

David Günzel
  • 73
  • 1
  • 4
1

Your second approach is the right way to go. The problem you are having with it is when you call setPixel(), QImage makes a copy of the external buffer you provided and runs out of memory for it.

Try changing the pixel value directly in the supplied buffer. You can use scanLine() to get the pointer to the buffer of a line. I would not use setPixel() anyway since it's really slow.

Stephen Chu
  • 12,724
  • 1
  • 35
  • 46