3

My current code follows this: Disk > bytearray > QImage (or QPixmap) > Painter

// Step 1: disk to qbytearray
QFile file("/path/to/file");
file.open(QIODevice::ReadOnly);
QByteArray blob = file.readAll();
file.close();

// Step 2: bytearray to QImage
this->mRenderImg = new QImage();
this->mRenderImg->loadFromData(blob, "JPG");

// Step 3: render
QPainter p(this);
QRect target; // draw rectangle
p.drawImage(target, *(this->mRenderImg));

Step 1 takes 0.5s
Step 2 takes 3.0s - decoding jpeg is the slowest step
Step 3 takes 0.1s

Apparently, decoding jpeg data is the slowest step. How do I make it faster?

Is there a third party library that can transform bytearray of jpeg data to bytearray of ppm that can be loaded to QImage faster?

Btw, Using QPixmap takes the same time as QImage.

S B
  • 8,134
  • 10
  • 54
  • 108
  • Are you decoding jpeg in `paintEvent`? Is it necessary? Did you try to use `QLabel` and `QLabel::setPixmap`? – Pavel Strakhov Feb 12 '14 at 13:19
  • My goal is to minimize the time required to read a .jpg image file from the disk and load it to a `QImage` (or `QPixmap`). Once the `QImage` is loaded, rendering it is quite fast anyway - be it on `QLabel` or any other widget. The reason I mentioned the rendering part is to indicate I am open to a solution that does not involve using Qt provided classes like `QImage` or `QPixmap` – S B Feb 12 '14 at 15:50
  • 1
    `QByteArray` and `QImage` steps are not necessary. You should create `QPixmap` directly by passing filename to its constructor: `QPixmap pixmap("/path/to/file");` – Pavel Strakhov Feb 12 '14 at 20:42
  • @PavelStrakhov `QPixmap px("path/to/file")` just shortens the code but takes the same time as steps 1 & 2 combined. I had tested this already. In my question, I intentionally kept these two steps separate because I don't see any scope of optimization in step 1 due to disk latency. But I see a scope of optimization in step 2. – S B Feb 13 '14 at 03:28

3 Answers3

2

I was able to reduce QImage load time significantly using libjpeg-turbo. Here are the steps

  • Step 1: Load .jpeg in memory filebuffer
  • Step 2a: Use tjDecompress2() to decompress filebuffer to uncompressedbuffer
  • Step 2b: Use QImage(uncompressedbuffer, int width, int height, Format pixfmt) to load the QImage
  • Step 3: Render

Step 2a and 2b combined offers atleast 3x speedup compared to QImage::loadFromData()

Notes

  • PixelFormat used in libjpeg's tjDecompress2() should match with format specified in step 2b
  • You can derive the width and height used in step 2b using tjDecompressHeader2()
S B
  • 8,134
  • 10
  • 54
  • 108
1

I think your question is more addressing a end result of a design issue rather than the design issue itself. Here are my thoughts related to the loading times of jpegs or images in general in GUIs.

Loading of data from a harddrive is a common bottleneck of software design. You have to get the harddrive to spin to that location and pull it out and copy it to the ram. Then when you are ready, you have the ram push it to the video buffer or maybe to the graphics card.

An SSD will be much faster, but demanding this of your end user is not practical in most situations. Loading the image once on startup and never closing your program is a way to avoid hitting this delay multiple times.

It is a big reason why lots of programs have a loading bar or a splash screen when it is starting up, so that it doesn't have a slow user experience when pulling up data.

Some other ways that programs handle longer processes are with the classic hour glass, or the spinning beach ball, or the "wait a bit" gifs are common.

Probably the best example of handing lots of large jpegs is google maps or some of the higher quality photo manager programs such as Picasa.

Google maps stores many different resolutions of the same area, and tiles and then load then specific to the resolution that can be viewed. Picasa "processes" all the images it finds, and stores a few different thumbnails for each one that can be loaded much faster than the full resolution image (in most cases).

My suggestion would be to either store a copy of your jpeg at a lower resolution, load that one, and then after it is loaded, replace it with the high resolution one when it is loaded, or to look into breaking your image into tiles and load them as needed.

On another related note, if you UI is getting slowed down by the jpeg loading, move the loading part into a thread and keep your UI responsive!

Hope that helps.

phyatt
  • 18,472
  • 5
  • 61
  • 80
  • I already have hourglass / spinner and threads in place - so UI freeze is not a concern. Having said that, faster loading of images would have other performance-related benefits in our case. Now loading a file to `QImage` can be broken down to 2 steps - reading from disk (Step 1 in my question) and initializing the QImage object from that data (Step 2). I know that Step 1 cannot be optimized because it involves disk io latency, but Step 2 probably can be optimized. Hence the question – S B Feb 13 '14 at 03:54
  • We don't have control on user's data. So those options are closed unfortunately – S B Feb 14 '14 at 04:22
-2

You asked about third-party software - -

ImageMagick can do this task quickly (even with larger files):

convert 1.jpg 2.jpg 3.jpg outputfilenamehere.ppm

While you have the filestream open, you can do numerous operations... Hope that helps, Gette

gette
  • 1
  • 4