Qimage from tga with Alpha
-
How can i open a tga file that contains an alpha channel?
i need to open multiple tga files and i don't know whether it is tga 16, 24, 32 bits/pixel or RLE compressed.
24 bits/pixel seams to work correctly, but the 32 bits/pixel (with alpha channel) do not work. Any solutions for that??
-
"do not work" is kinda vague, but I'm guessing you mean your images load as opaque, loosing alpha channel info or don't load at all.
Qt tga plugin does not support RLE compression, but alpha channel should work just fine.
Looking at which tga options you named I'm guessing you're using Photoshop (or other Adobe app) to save your images. Please note that Photoshop treats alpha in tga files kinda weird. It's not enough to save it in 32 bits. You need to actually go to layers panel, create a new alpha layer and fill it with the right alpha data, otherwise it will write the file with alpha channel all white (opaque).All in all tga is an old and poorly supported format (lots of weird gotchas in different apps). If possible consider switching to something else. For example png's are universally supported lossless format with alpha and metadata support.
-
don't work in the meaning of load() returns NULL.
I need the tga format, because the mesh files need them. You are right, I'm using PhotoShop elements 11 with some features from cs2 for layer support.
Since I don't take care of the actually data stored in the alpha channel, it shouldn't matter how PhotoShop fills it. But I know PS good, so I know how to pass the data to alpha correctly. I'm not sure about the rle. I'll check that and tell you whether the file causing problems is rle compressed or not.
-
@Chris-Kawa i think you were right. I just opened the picture in PSE and restored it. Now it is working. So i guess it was rle compressed before
Before i changed the project to Qt i used my own tga importer and stored the important information in a struct.
Later i used glTexImage2D to add my texture.So would it be possible to use my code to support RLE, too?? My importer worked fine for 32 and 24 bit (16 did not for some reason) with and without RLE
-
If you're using it with OpenGL then sure. You can use one of the QOpenGLTexture's setData overloads to transfer the data you get from your importer. You can also just use glTexImage2D directly and bypass Qt entirely. It's up to you.
You can also write your own image plugin if you want to keep the convenient QImage interface.
-
all right. i think i'll stay with Qt for now and maybe add rle support later ;)
first i need to fix this problem:
-
Is that a stormtrooper's helmet ? :) Looks like the vertices indexing is somehow messed up.
-
imperial commando. it's used to look like that:
I think it's something about the index, too. But i don't understand what. Because things like this works:
ok, at the last one the rotating is wrong (no idea why)
-
@Chris-Kawa
I just added my own tga load function for those that are rle compressed. But it is not compressed. It's RGB 24 bit. Or can Qt not load tga with 24 bit??==EDIT==
Seams that qt can only open 32 bit images. But i have a different problem:
This needs to be solid green.
img = QImage(ui32Width, ui32Height, ui32BpP == 32? QImage::Format_ARGB32 : QImage::Format_RGB32); int pixelSize = ui32BpP == 32 ? 4 : 3; for (unsigned int x = 0; x < ui32Width; x++) { for (unsigned int y = 0; y < ui32Height; y++) { int valr = vui8Pixels->at(x * ui32Width * pixelSize + y + 2); int valg = vui8Pixels->at(x * ui32Width * pixelSize + y + 1); int valb = vui8Pixels->at(x * ui32Width * pixelSize + y); int vala = vui8Pixels->at(x * ui32Width * pixelSize + y + 3); QRgb value; if (ui32BpP == 32) value = qRgba( valr, valg, valb, vala ); else value = qRgb( vui8Pixels->at(x * ui32Width * pixelSize + y), vui8Pixels->at(x * ui32Width * pixelSize + y + 1), vui8Pixels->at(x * ui32Width * pixelSize + y + 2) ); img.setPixel(x, y, value); } }
vui8Pixel holds all pixel information (b,g,r,a,b,g,r,a,b,g,r,a,....)
i checked the valr, valg, valb,... values and they are correctly. So it need to be something with qRgba() or my QImage format. Any ideas?? -
I've checked and QImage reads 24bit images fine.
As for the problem - you seem to have your indexing wrong:x * ui32Width * pixelSize + y + 2
This should rather be
y * ui32Width * pixelSize + x + 2 //i.e. y * scanline + x
Same for the other values.
Btw. This is very inefficient. First of all you should switch the loops like this:
for (unsigned int y = 0; y < ui32Height; y++) { for (unsigned int x = 0; x < ui32Width; x++) {
so that you look at the neighboring x values. This is much much more cache friendly.
Similarly, take a note at the docs - usingsetPixel
is terribly inefficient. At each y iteration take a pointer to the scanline and use that instead (read the warning note in the docs). -
oops, thanks. It works now.
The whole function is a bit more complicate
So how it works:
Try to open the image file using QImage. Since it is not very good for tga files, it sometimes does not work (in my case a 24 bit uncompressed does not worked, others do).
So if it cannot be loaded, i use my own old import function and if this function has problems, it just returns a solid red image.Question:
is there a fast way to read from the file and write directly to the QImage?
The values are always unsigned int 8 bgr(a) if ui32BpP = 24 there is no alpha, if it is 32 there is an alpha value after bgr.if (ui32PicType == 2 && (ui32BpP == 24 || ui32BpP == 32)) { fsPicture.read(reinterpret_cast<char*>(vui8Pixels->data()), ui32Size); }
in the other case it is compressed and i need to do some more tricks to reconstruct the image. Here i'd calculate the image position with modulo and simply write directly on the QImage instead of the vector.
else if (ui32PicType == 10 && (ui32BpP == 24 || ui32BpP == 32)) // compressed { std::uint8_t tempChunkHeader; std::uint8_t tempData[5]; unsigned int tempByteIndex = 0; do { fsPicture.read(reinterpret_cast<char*>(&tempChunkHeader), sizeof(tempChunkHeader)); if (tempChunkHeader >> 7) // repeat count { // just use the first 7 bits tempChunkHeader = (uint8_t(tempChunkHeader << 1) >> 1); fsPicture.read(reinterpret_cast<char*>(&tempData), ui32BpP / 8); for (int i = 0; i <= tempChunkHeader; i++) { vui8Pixels->at(tempByteIndex++) = tempData[0]; vui8Pixels->at(tempByteIndex++) = tempData[1]; vui8Pixels->at(tempByteIndex++) = tempData[2]; if (ui32BpP == 32) vui8Pixels->at(tempByteIndex++) = tempData[3]; } } else // data count { // just use the first 7 bits tempChunkHeader = (uint8_t(tempChunkHeader << 1) >> 1); for (int i = 0; i <= tempChunkHeader; i++) { fsPicture.read(reinterpret_cast<char*>(&tempData), ui32BpP / 8); vui8Pixels->at(tempByteIndex++) = tempData[0]; vui8Pixels->at(tempByteIndex++) = tempData[1]; vui8Pixels->at(tempByteIndex++) = tempData[2]; if (ui32BpP == 32) vui8Pixels->at(tempByteIndex++) = tempData[3]; } } } while (tempByteIndex < ui32Size); }
Here is the whole function
QImage loadTga(const char* filePath, bool &success) { QImage img; if (!img.load(filePath)) { // open the file std::fstream fsPicture(filePath, std::ios::in | std::ios::binary); if (!fsPicture.is_open()) { img = QImage(1, 1, QImage::Format_RGB32); img.fill(Qt::red); success = false; return img; } // some variables std::vector<std::uint8_t>* vui8Pixels; std::uint32_t ui32BpP; std::uint32_t ui32Width; std::uint32_t ui32Height; // read in the header std::uint8_t ui8x18Header[19] = { 0 }; fsPicture.read(reinterpret_cast<char*>(&ui8x18Header), sizeof(ui8x18Header) - 1); //get variables vui8Pixels = new std::vector<std::uint8_t>; bool bCompressed; std::uint32_t ui32IDLength; std::uint32_t ui32PicType; std::uint32_t ui32PaletteLength; std::uint32_t ui32Size; // extract all information from header ui32IDLength = ui8x18Header[0]; ui32PicType = ui8x18Header[2]; ui32PaletteLength = ui8x18Header[6] * 0x100 + ui8x18Header[5]; ui32Width = ui8x18Header[13] * 0x100 + ui8x18Header[12]; ui32Height = ui8x18Header[15] * 0x100 + ui8x18Header[14]; ui32BpP = ui8x18Header[16]; // calculate some more information ui32Size = ui32Width * ui32Height * ui32BpP / 8; bCompressed = ui32PicType == 9 || ui32PicType == 10; vui8Pixels->resize(ui32Size); // jump to the data block fsPicture.seekg(ui32IDLength + ui32PaletteLength, std::ios_base::cur); if (ui32PicType == 2 && (ui32BpP == 24 || ui32BpP == 32)) { fsPicture.read(reinterpret_cast<char*>(vui8Pixels->data()), ui32Size); } // else if compressed 24 or 32 bit else if (ui32PicType == 10 && (ui32BpP == 24 || ui32BpP == 32)) // compressed { std::uint8_t tempChunkHeader; std::uint8_t tempData[5]; unsigned int tempByteIndex = 0; do { fsPicture.read(reinterpret_cast<char*>(&tempChunkHeader), sizeof(tempChunkHeader)); if (tempChunkHeader >> 7) // repeat count { // just use the first 7 bits tempChunkHeader = (uint8_t(tempChunkHeader << 1) >> 1); fsPicture.read(reinterpret_cast<char*>(&tempData), ui32BpP / 8); for (int i = 0; i <= tempChunkHeader; i++) { vui8Pixels->at(tempByteIndex++) = tempData[0]; vui8Pixels->at(tempByteIndex++) = tempData[1]; vui8Pixels->at(tempByteIndex++) = tempData[2]; if (ui32BpP == 32) vui8Pixels->at(tempByteIndex++) = tempData[3]; } } else // data count { // just use the first 7 bits tempChunkHeader = (uint8_t(tempChunkHeader << 1) >> 1); for (int i = 0; i <= tempChunkHeader; i++) { fsPicture.read(reinterpret_cast<char*>(&tempData), ui32BpP / 8); vui8Pixels->at(tempByteIndex++) = tempData[0]; vui8Pixels->at(tempByteIndex++) = tempData[1]; vui8Pixels->at(tempByteIndex++) = tempData[2]; if (ui32BpP == 32) vui8Pixels->at(tempByteIndex++) = tempData[3]; } } } while (tempByteIndex < ui32Size); } // not useable format else { fsPicture.close(); img = QImage(1, 1, QImage::Format_RGB32); img.fill(Qt::red); success = false; return img; } fsPicture.close(); img = QImage(ui32Width, ui32Height, QImage::Format_RGB888); int pixelSize = ui32BpP == 32 ? 4 : 3; //TODO: write direct into img for (unsigned int x = 0; x < ui32Width; x++) { for (unsigned int y = 0; y < ui32Height; y++) { int valr = vui8Pixels->at(y * ui32Width * pixelSize + x * pixelSize + 2); int valg = vui8Pixels->at(y * ui32Width * pixelSize + x * pixelSize + 1); int valb = vui8Pixels->at(y * ui32Width * pixelSize + x * pixelSize); QColor value(valr, valg, valb); img.setPixelColor(x, y, value); } } img = img.mirrored(); } success = true; return img; }