0

I already know how to flip an image vertically or horizontally. I have the following code that does so horizontally. The image data here is stored in a QImage as I was working with Qt here.

QImage image(imageFileName);
QImage newImage(image);

if(image.depth() > 8)
{
    for (int idx_Y = 0; idx_Y < image.height(); idx_Y++)
    {
        for (int idx_X = 0; idx_X < image.width(); idx_X++)
        {
            QRgb rgb = image.pixel(image.width() - 1 - idx_X, idx_Y);
            newImage.setPixel(idx_X, idx_Y, rgb);
        }
    }
}

I'm sure there are faster methods to get it done. However, I don't want any memory allocation on the heap. Could you please tell me what other much faster algorithms there could be?

Thank you.

the_naive
  • 2,936
  • 6
  • 39
  • 68
  • 5
    Have you looked at [`QImage::mirrored`](https://doc.qt.io/qt-5/qimage.html#mirrored)? – G.M. Jul 19 '19 at 10:35
  • @G.M. Thanks, I totally forgot about it. Having said that, what other ways my method of mirroring could be made faster? – the_naive Jul 19 '19 at 10:56
  • 1
    Probably by manipulating the pixel buffer directly instead of going through that virtual method call, but after that you're bound by memory bandwidth. Or you leave flipping to the (hardware-accelerated) renderer on the display end. – Botje Jul 19 '19 at 11:32
  • Who is rendering the image? If it's your code, then you can just render it mirrored instead of mirroring it first and then rendering it (you just need to extend the class and add a property that says how it should be rendered) – ChatterOne Jul 19 '19 at 11:55
  • 1
    2 nested for loops are not the problem... the `setPixel` and `pixel` functions are usually crawlingly slooow on most gfx APIs. Using direct pixel access instead usually boost speed ~1000 times or more ... – Spektre Jul 19 '19 at 12:26
  • 2
    Flipping each line can be done in-place by swapping pixels. You don’t need the second buffer. – Cris Luengo Jul 19 '19 at 12:51

1 Answers1

2

Elaborating on @Spektres hint

2 nested for loops are not the problem... the setPixel and pixel functions are usually crawlingly slooow on most gfx APIs. Using direct pixel access instead usually boost speed ~1000 times or more ...

This could look like:

QImage image(imageFileName);
QImage newImage(image);

if (image.depth() >= 8) {
  const int bytesPerPixel = image.depth() / 8;
  for (int y = 0; y < image.height(); ++y) {
    char *dataSrc = image.bits() + y * image.bytesPerLine();
    char *dataDst = newImage.bits() + y * newImage.bytesPerLine()
      + (newImage.width() - 1) * bytesPerPixel;
    for (int i = image.width(); i--;
      dataSrc += bytesPerPixel, dataDst -= bytesPerPixel) {
      for (int i = 0; i < bytesPerPixel; ++i) dataDst[i] = dataSrc[i];
    }
  }
}

Please, note that I changed image.depth() > 8 into image.depth() >= 8. (I saw no reason to exclude e.g. QImage::Format_Grayscale8.)

A slightly modified version for mirroring the QImage newImage in-place (considering that it is already copied):

QImage image(imageFileName);
QImage newImage(image);

if (newImage.depth() >= 8) {
  const int bytesPerPixel = newImage.depth() / 8;
  for (int y = 0; y < image.height(); ++y) {
    char *dataL = newImage.bits() + y * newImage.bytesPerLine();
    char *dataR = dataL + (newImage.width() - 1) * bytesPerPixel;
    for (; dataL < dataR; dataL += bytesPerPixel, dataR -= bytesPerPixel) {
      for (int i = 0; i < bytesPerPixel; ++i) std::swap(dataL[i], dataR[i]);
    }
  }
}

Concerning QImage and qRgb(), you may also notice that Qt supports QImages with 16 bits per component (since Qt 5.12).

I fiddled a bit with this in
SO: Set pixel value of 16 bit grayscale QImage
which might be interesting as well.

Scheff's Cat
  • 19,528
  • 6
  • 28
  • 56