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??


  • Moderators

    "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


  • Moderators

    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:
    Bild Text


  • Moderators

    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??


  • Moderators

    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 - using setPixel 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;
    }
    

Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.