Reputation: 12339
Given a .png
image with a transparent background, I want to find the bounding box of the non-transparent data. Using nested for
loops with QImage.pixel()
is painfully slow. Is there a built-in method of doing this in Qt?
Upvotes: 8
Views: 2803
Reputation: 2112
The easiest and also relatively fast solution is to do as follows:
QRegion(QBitmap::fromImage(image.createMaskFromColor(0x00000000))).boundingRect()
If you have a QPixmap
rather than QImage
, then you can use:
QRegion(pixmap.createMaskFromColor(Qt::transparent)).boundingRect()
QPixmap::createMaskFromColor
internally will convert the pixmap to an image and do the same as above. An even shorter solution for QPixmap
is:
QRegion(pixmap.mask()).boundingRect()
In this case, a QPixmap
without alpha channel will result in an empty region, so you may need to check for that explicitly. Incidentally, this is also what QGraphicsPixmapItem::opaqueArea
mentioned by @Arnold Spence is based on.
You may also want to try QImage::createAlphaMask
, though the cutoff point will not be at 0 alpha but rather somewhere at half opacity.
Upvotes: 1
Reputation: 52337
If pixel() is too slow for you, consider more efficient row-wise data adressing, given a QImage p:
int l =p.width(), r = 0, t = p.height(), b = 0;
for (int y = 0; y < p.height(); ++y) {
QRgb *row = (QRgb*)p.scanLine(y);
bool rowFilled = false;
for (int x = 0; x < p.width(); ++x) {
if (qAlpha(row[x])) {
rowFilled = true;
r = std::max(r, x);
if (l > x) {
l = x;
x = r; // shortcut to only search for new right bound from here
}
}
}
if (rowFilled) {
t = std::min(t, y);
b = y;
}
}
I doubt it will get any faster than this.
Upvotes: 4
Reputation: 22272
There is one option that involves using a QGraphicsPixmapItem
and querying for the bounding box of the opaque area (QGraphicsPixmapItem::opaqueArea().boundingRect()
). Not sure if it is the best way but it works :) It might be worth digging into Qt's source code to see what code is at the heart of it.
The following code will print out the width and height of the image followed by the width and height of the opaque portions of the image:
QPixmap p("image.png");
QGraphicsPixmapItem *item = new QGraphicsPixmapItem(p);
std::cout << item->boundingRect().width() << "," << item->boundingRect().height() << std::endl;
std::cout << item->opaqueArea().boundingRect().width() << "," << item->opaqueArea().boundingRect().height() << std::endl;
Upvotes: 5