PDA

View Full Version : Loading a custom image into a QPixmap



KShots
19th July 2006, 02:42
Hello all...

I have an application that loads all its images into a headerless-format in memory (I store width, height, and bpp... and that's enough for OGL textures), but I'm having some difficulty in writing up a "texture manager" using Qt. Whenever I attempt to load in an image from memory, it pops up an QPixmap::convertFromImage: Cannot convert a null image... here's a snippet of what I'm doing:

const Texture * t = myTextures->at(index.row());
QPixmap p(t->GetWidth(), t->GetHeight());
bool OK = p.loadFromData((const uchar *) t->GetData(), t->GetSize());
assert(OK);
return QIcon(p.scaled(60, 60, Qt::KeepAspectRatio, Qt::SmoothTransformation));The p.loadFromData() line returns false, which means that it failed to load the image. I've checked with my debugger, and it appears that all my internal texture data is there... is there something else I should be doing?

A breakdown of what's going on: Texture is a class that holds the image. I have a QList of Texture pointers called myTextures. I grab the texture being pointed at by a QModelIndex, initialize a QPixmap with the width and height from the texture, and load from data (memory) the actual texture into the QPixmap.

wysota
19th July 2006, 12:57
Maybe the pixmap or the data is not valid?

jpn
19th July 2006, 13:30
QPixmap::loadFromData() requires the data to contain a header (at least when not passing the format). I think you could construct a QImage of appropriate size and then set the data by hand through uchar* QImage::bits() (http://doc.trolltech.com/4.1/qimage.html#bits) or uchar* QImage::scanLine() (http://doc.trolltech.com/4.1/qimage.html#scanLine).



const int bpl = image.bytesPerLine();
uchar* data = image.bits();

for (int y = 0; y < height; y++)
{
quint32* line = reinterpret_cast<quint32*>(data);
for (int x = 0; x < width; x++)
line[x] = qRgb(...);
data += bpl;
}

KShots
19th July 2006, 17:58
Yeah... I had thought that may be the case, that it wasn't able to auto-detect the image due to the lack of header... I'd noted the format parameter, but there didn't seem to be any documentation at all on what the different options were for it (though there was some for the QImage... but they're different types of parameters and don't look compatible):confused:.

I'd like to avoid (if at all possible) having to manually re-create the image (though if it comes to that, I'll find some way of optimizing it). As you may have been able to guess (with the QModelIndex), I'm trying to have Qt do this on the fly in my model's data() function... and the less it does for each element, the better.

EDIT: I found the format docs in QPixmap... it just wasn't obvious what it was. I thought it was simply describing the formats it was capable of, but was missing how to use each format - until I saw an example. QPixmap takes in a const char * for its format. The example I saw simply used "PNG" for a .png type file.

Anyways, that turned into a dead end. I declared the source as a "BMP" as a test, loaded in a texture from a .bmp file as a test (this won't work for my .tga files), and it came up with the same error.

OpenGL takes that same texture and pastes it in 3D over my models, so I'm pretty sure it's valid data. I guess my next step is to try to start from a QImage and manually define each byte, then convert to a QPixmap and scale to something usable as a thumbnail, then convert to a QIcon :confused:. Surely this shouldn't be this hard to deal with...

EDIT2: OK, I've found that I can let it load something via one of QImage's constructors (QImage ( uchar * data, int width, int height, Format format )), with format being (for the bitmap) QImage::Format_RGB32, and QImage::Format_ARGB32 for the targa.

Unfortunately, while this does give a good chunk of the texture, most of the texture is corrupted (not sure what I'm viewing). Attached is an example (1st pic), and a thumbnail of what it should be (2nd pic, under "real deal").

EDIT3: I should also note that when I load in .BMP files, I do byte-swap them so they are RGB instead of BGR.

KShots
19th July 2006, 19:49
New data... I just tried loading in one of my .tga files, and it has a slightly different problem... looks like a byte-swapping issue. It's also rotated 90 degrees counter-clockwise (how did it manage that?)

Take a look at the two thumbnails below - the first shows what it comes up as when I load it all the way to the QIcon, the 2nd shows what they are supposed to be (in the same order - the brown first, then the blue).

Looks like I'm dealing with incompatible formats. Any ideas?

jacek
19th July 2006, 19:57
It's also rotated 90 degrees counter-clockwise (how did it manage that?)
They're not rotated by 90 degrees --- they're upside-down.

Also .tga files are not .bmp files. Maybe you should create a QImageIOPlugin plugin that loads .tga files?

KShots
20th July 2006, 01:56
They look like they're rotated to me... and I'm well aware that a .tga is not a .bmp - my texture loader handles them both in different ways. If it's a 24-bit image, it looks like RRGGBB. If it's a 32-bit image, it looks like AARRGGBB. This is not the case with either .bmp nor .tga files, so I have to byte swap them.

It looks like blue and red are swapped in the above two snapshots (the red one looks blue, and the blue one looks brown)

jacek
20th July 2006, 02:04
They look like they're rotated to me...
AFAIR .bmp numbers rows bottom to top, while .tga --- the other way around. That's why those images are flipped.

KShots
20th July 2006, 04:28
Ah, I think I see what's going on, then... though I'm still confused why OGL handles it correctly.

Yeah, it looks like you're right, it's not rotated 90 degrees counter-clockwise, it's upside down. It's really hard to tell with the size of the icons, but I think you're right. It also seems that the red and the blue bytes are swapped.

I'll probably have to have a function that goes in and adjusts that inside a QImage in the case of targa files. I'm not entirely sure what writing a QImageIOPlugin gives me if I'm reading the data from memory rather than a file (with the header stripped out).

I'm still not sure what's going on with the BMP stuff above, either... that corruption really bothers me.

EDIT: Fixing the targa was... well... easy enough. I had to do an image = image.rgbSwapped(); and an image = image.mirrored() to get it to show up correctly... but it did. I guess that'll have to do for the targas.

Still mystified with the .bmp corruption :(

EDIT2: I think I've figured out what's going on with the bitmaps... by definition, as bitmaps, they're 24-bits per pixel. QImage only seems to take 32-bits per pixel (unless I'm missing something important). Therefore, when QImage reads in a pixel, it's expecting 0xFFRRGGBB... and I'm giving it 0xRRGGBB... so the 1st pixel is 0xRRGGBBRR, then 2nd pixel is 0xGGBBRRGG, and so on. No wonder it looks corrupted. Any ideas on how to force QImage to read in 24-bits rather than 32?

Methedrine
21st July 2006, 20:03
You are looking for QImage::Format_ARGB32 to read in 0xAARRGGBB. However, it won't work for targa since targa is 0xBBGGRRAA (AA = alpha-channel) - if I remember correctly (should be, wrote my own targa reader two days ago... but left it at work).

If you are loading targa images keep in mind that the targa header is 18 bytes so if you read it as a struct the sizeof(tgaheaderstruct) will still be 20 (unless #pragma pack(1) was specified). That reading 20 instead of 18 bytes also explains wrong colours as the pointer onto the datastream starts with the wrong color code.

KShots
24th July 2006, 03:28
You are looking for QImage::Format_ARGB32 to read in 0xAARRGGBB. However, it won't work for targa since targa is 0xBBGGRRAA (AA = alpha-channel) - if I remember correctly (should be, wrote my own targa reader two days ago... but left it at work).Yeah, I got my targas working a while ago, I used the functions mentioned in my last post to correct it - my problem now is with my bitmaps
If you are loading targa images keep in mind that the targa header is 18 bytes so if you read it as a struct the sizeof(tgaheaderstruct) will still be 20 (unless #pragma pack(1) was specified). That reading 20 instead of 18 bytes also explains wrong colours as the pointer onto the datastream starts with the wrong color code.Hmm... that explains why I recently had to rebuild my texture loader recently - it just suddenly refused to load the header correctly. I ended up having to read it in variable by variable to get it to read the right sized chunk.

Anyways... the targa's been behaving well... any ideas on the bitmaps?

KShots
27th July 2006, 16:44
Alright... I think I solved my problem(s). It appears Qt does not support loading in bitmaps from raw data - I had to convert my (already loaded) bitmap into a 32-bit image through opengl, then pass it over to Qt, like so:

//I start with a 24-bit bitmap raw-loaded into OpenGL
unsigned char * pixelData = new unsigned char * [4 * texture->GetWidth() * texture->GetHeight()];
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixelData);
//Now I have a 32-bit texture loaded into memory...
QImage image((uchar *) pixelData, texture->GetWidth(), texture->GetHeight(), QImage::Format_ARGB32);
delete [] pixelData;
image = image.rgbSwapped();
image = image.mirrored();
QPixmap pixmap(QPixmap::fromImage(image));
return QIcon(pixmap.scaled(60, 60, Qt::KeepAspectRatio, Qt::SmoothTransformation));On the one hand, I'm a little disappointed that Qt has no way of handling raw 24-bit images... but on the other hand, I found that I didn't need to store my textures in memory if I can retrieve them from OGL easily

Methedrine
5th August 2006, 00:16
I think I'd have used QFile to read the targa into a QByteArray (simply add the missing byte for a rgb32!) which in turn I'd then use to construct a QImage which would then be assigned to a QPixmap.

But then again if you are writing something based upon OpenGL you're probably doing it correctly anyway.