Back to Main Page

A short page discussing the issues caused by DXT texture compression to Black or Grey textures


Purple and Green - Textures that is, not aliens.

I thought I would address an issue I see popping up all the time, because despite the internet being full of information, nobody seems to read any of it.

The Problem... what exactly is it?

The issue is that importing some textures into OpenIV, that are initially black or grey, will end up with various green or purple blocks all over them.

Let me first clarify one issue, this isn't a "quality" issue, it's a bit-depth issue caused by DXT compression. As such there is no solution that "maintains quality"... so if you're looking for one, stop right now, you're looking for the wrong kind of solution.

Bit-depth != Quality.

Let me explain further...

An image is made up of pixels, in a typical 24-bit image, those pixels are made up of 8-bits of Red, 8-bits of Green and 8-bits of Blue. For each step of one colour, there is a matching step of the other two. 32-bit images also have 8-bits of Alpha. You can see this type of image defined in OpenIV as A8R8G8B8.

DXT compressed images are typically 16-bits of colour data and as I am sure you are already thinking, you can't divide 16 by 3 colours and get a matching number of steps... therein lies the problem.

DXT images are 5-6-5 images, that's 5-bits Red, 6-bits Green and 5-bits Blue.

So how does that cause the problem?

Take a look at these 3 images.

3 very similar shades of black... here's the images combined so you can see the slight difference.

From left to right, the RGB values are 21,21,21 then 24,24,24 and finally 27,27,27. Let's look at those values on a set of values for each bit type. I can't get them to exactly line up because of the text formatting.

5-bit = 000 ---- 008 ---- 016 ---- 024 ---- 032

6-bit = 000 004 008 012 016 020 024 028 032

What you can see from that, is the 6-bit range has a value in-between the values in the 5-bit range. So let's take those RGB values I set.

21 doesn't exist in either range, so DXT compression will pick the nearest values. For Red and Blue, that value is 24 because it is nearer to 21 than 16. For Green, it's 20, because that's nearer than 24. That gives you an RGB value of 24-20-24, which is going to be a colour with higher Red and Blue levels, making it tinted towards... Purple, that's right.

24 exists in all 3 ranges, so you will get a matching set of values, meaning a pure grey colour.

27 is like 21, in that there is no matching colour in all 3 ranges. However, this time the nearest Red and Blue colours are 24 and the nearest Green colour is 28. That means you're going to get a colour with a higher Green level.

Is this sounding familiar yet?

Well that's all a bit complicated, can you show something a bit more explanatory?

Let's look at the combined image after importing and then re-exporting from OpenIV. I have put a black border around it so it makes things a bit clearer but it's probably better to save the image and then view it in zoomed-up form.

Notice we now have a purple block, a grey block and a green block. The red and blue values in the green block are different to what I expected but it still demonstrates how the problem is caused quite nicely. It's actually been converted to 25-28-25 and I expected 24-28-24.

So that's all well and good but how do we solve it?

Well the solution is quite simple really, put everything on an even setting and there is no problem. Reducing to 16-bits causes the problem but reducing to 15 bits cures it, because now all of the components have matching values. So the solution is to reduce all your images to 5-5-5 bits, and then import them into OpenIV, where converting them to 5-6-5 will leave the image with the same values.

This solution will cause some slight colour variation on any colours that are primarily green based but the change should be minor and barely noticeable compared to purple and green blocks on your black textures.

I have some images... can you fix it plz?

I don't have a colour-reduction app that I know of but I already have some bitmap manipulation code I wrote a while back that I might be able to do something with... watch this space. I thought the important part was to get the information out there and then hopefully, provide the solution to it.

First build of my BitDepthAdjuster app, showing again the problems with 5-6-5 conversion to verify it works okay.

It seems my bitmap code breaks though when the image width isn't byte-aligned, so I have to work out how to fix that.

Bitmap code now fixed, images work no matter what resolution they are.