• 10
• 12
• 12
• 14
• 16

# C# - Detect If Pre-Existing .DDS Contains Alpha Channel?

This topic is 606 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

For the past wee while, I've been scouring the net to find a way to tell if a .dds file contains an alpha channel. And I've come up completely dry.

I've been working on a tool that sorts through all of the .dds file in a given directory, including it's sub-directories, and resizing all .dds images of a specified size to another. Pretty much just takes text from the gui, reads a couple bytes from the header of the .dds image, and then chucks it all to texconv.exe.

Thing is, I need to detect whether or not a .dds image uses transparency. If it does, include the -pmalpha argument when launching texconv.exe

How would I go about doing this? I've not much experience with C# (this program is my first foray into the language), and my sum total of programming experience is this and some Pascal for Tes5Edit, so I'll probably need to be explained to as if I'm 5 years old or somethin'.

I'm thinking that maybe SharpDX has some use for this? The documentation isn't, err, beginner friendly to say the least, sadly.

Anywho, if it helps at all, 'eres a couple links:

Current release of the program: http://www.nexusmods.com/fallout4/mods/17624/?

Current source code: https://github.com/MajinCry/Fallout-4-Texture-Resizer

##### Share on other sites
You could write a pretty simple check by reading the DDS file's headers:

https://msdn.microsoft.com/en-us/library/bb943991.aspx

Note: These documents outline the structures in C style, but you can easily convert that to C#.

DWORD = uint
WORD = ushort
etc.

It looks like the information you want can be found in the DDS_HEADER -> DDS_PIXELFORMAT -> DDPF_ALPHAPIXELS bit in the flags field.

Personally I would just read the file using a BinaryReader and seek to the correct offset in the file that corresponds to that field. Just count the # of DWORDs you need to skip in order to get to the dwFlags field, multiply that by 4, say filestream.Offset = whatever_that_is, then dwFlags = binaryReader.ReadInt32() and then bool containsAlpha = (dwFlags & 1) == 1; Edited by Nypyren

##### Share on other sites

The header contains alpha data? I swear I tried comparing the headers between various formats (with and without alpha), and couldn't find any consistency between them. Finding the x & y resolution, as well as the mipmap levels, was easy on the other hand.

Me findings can be found here: http://pastebin.com/isBKwaas

I think the format is stored in 4 bytes, starting @ offset +54, but that's about it. Comparing the >100 different .dds formats, with the various alpha combinations...Err, hmm.

The microsoft documentation seems to be really awkward. It was rather good for the simpler functions, but it seems to rather terse about this directx stuff.

DWORD               dwMagic;
DDS_HEADER_DXT10    header10;

dwMagic starts at the beginning of the file, and looks like this in a hex viewer: 44 44 53 20

Maybe header starts @ offset +04?

And header10 is probably the four bytes @ offset +54.

No clue where to find the PIXELFORMAT bytes, though. https://msdn.microsoft.com/en-us/library/bb943984.aspx

##### Share on other sites
The way the C++ struct layouts are defined here, each field comes in the order listed, and nested structs exist at that position in the file as well (they aren't pointers to arbitrary locations).

You are correct that DWORDs are 4 bytes (or, 32-bits since 1 byte = 8 bits). The corresponding type in C# to a DWORD is called a uint, or sometimes seen as UInt32. For example the BinaryReader function to read a uint from a file is called ReadUInt32.

so here's how you would find the offset you want:

DWORD               dwMagic; = offset 0
{
DWORD           dwSize; = offset 4
DWORD           dwFlags; = offset 8
DWORD           dwHeight; = offset 12
DWORD           dwWidth; = offset 16
DWORD           dwPitchOrLinearSize; = offset 20
DWORD           dwDepth; = offset 24
DWORD           dwMipMapCount; = offset 28
DWORD           dwReserved1[11]; == offset 32, notice this is an array of 11 DWORDs.
DDS_PIXELFORMAT ddspf; = offset 32 + sizeof(DWORD) * 11 = offset 32 + 44 = offset 76
{
DWORD dwSize; = offset 76
DWORD dwFlags; = offset 80   <============================== This is what you're interested in
DWORD dwFourCC;
DWORD dwRGBBitCount;
}
DWORD           dwCaps;
DWORD           dwCaps2;
DWORD           dwCaps3;
DWORD           dwCaps4;
DWORD           dwReserved2;
}

dwFlags is a bitfield (i.e. it is a number composed of several other numbers ORed together). the value you care about is:

DDPF_ALPHAPIXELS       Texture contains alpha data; dwRGBAlphaBitMask contains valid data.     Value = 0x1

What this means is, if the texture contains alpha data, the dwFlags should have bits corresponding to the value 0x1 set.

To check whether that value is set, you say:

bool isValueSetInBitfield = (bitfield & mask) == value;

where 'bitfield' is 'dwFlags' and both 'mask' and 'value' are 0x1, since it's a one-bit field.

If you discover that you need to also examine the DXT10 header, figuring out which offset it's at works the same; it just comes immediately after the first header. Edited by Nypyren

##### Share on other sites
In your existing code, the function GetDDSDimensions already looks like it's using this technique to get the width and height (the offsets are written in hexadecimal in your code, and my previous post is using decimal instead). You could easily add a read of the dwFlags field and check for alpha in there as well, if you want. Edited by Nypyren

##### Share on other sites

Sorry for the late reply, been trying to wrap my head around this, trying to get the concept down. Honestly, it's making my brain hurt.

Been converting the code you posted to a C# equivalent, and here's what I've got so far:

        public bool IsAlphaPresent(string strDDSFileName)

{

using (FileStream fsSourceDDS = new FileStream(strDDSFileName, FileMode.Open, FileAccess.Read))

{

fsSourceDDS.Seek(0x04, SeekOrigin.Begin);

fsSourceDDS.Seek(0x08, SeekOrigin.Begin);

fsSourceDDS.Seek(0x0c, SeekOrigin.Begin);

fsSourceDDS.Seek(0x10, SeekOrigin.Begin);

fsSourceDDS.Seek(0x14, SeekOrigin.Begin);

fsSourceDDS.Seek(0x18, SeekOrigin.Begin);

fsSourceDDS.Seek(0x1c, SeekOrigin.Begin);

fsSourceDDS.Seek(0x20, SeekOrigin.Begin);

}

}


I've been using hexadecimal values because I've a teensy bit of experience with CheatEngine, in that I've hooked a couple instructions and documented the npc data structure for Dark Souls. Feels more sane to me to use hex, especially since I'm having to reference the header in the .dds file via a hex editor.

Is the dwFlags always at a specific offset (i.e, always at the same offset in every BCn format .dds texture), or does it change depending on the format?

##### Share on other sites

Is the dwFlags always at a specific offset (i.e, always at the same offset in every BCn format .dds texture), or does it change depending on the format?

As far as I can tell from MSDN, yes, it's always at a specific offset.

Also, you don't need to seek and read all of the intermediate fields if you don't care about them. you can seek directly to the dwFlags field, skipping everything inbetween.

fsSourceDDS.Position = 0x50;  // 0x50 == 80; using .Position does the same thing as Seek does.  I pretty much never use Seek anymore.
bool containsAlpha = (dwFlags & 1) == 1;

FileStream.Position: https://msdn.microsoft.com/en-us/library/system.io.filestream.position(v=vs.110).aspx Edited by Nypyren

##### Share on other sites

Sweet, I'll give it a shot.

What I've done, is added the following code:

 public bool IsAlphaPresent(string strDDSFileName)
{
using (FileStream fsSourceDDS = new FileStream(strDDSFileName, FileMode.Open, FileAccess.Read))
{
fsSourceDDS.Position = 0x50;
bool boolIsValueSetInBitfield = (uintDWFlags & 1) == 1;

if (boolIsValueSetInBitfield)
{
return true;
}
else
{
return false;
}

}
}


At the beginning of RunTexConv():

string strAlphaArgument;
if (IsAlphaPresent(strFinalDDS))
{
strAlphaArgument = "-pmalpha ";
MessageBox.Show(strFinalDDS + " Has an alpha channel!");
}
else
{
strAlphaArgument = "";
}


If everything goes as planned, it should just throw up a message for the .dds files that have an alpha channel. Time to see if it works.

Edit: The code above didn't work, had to do a wee bit of editing to a couple functions, to include the source .dds filepath (due to the destination .dds not existing yet). Alas, no alpha was found in any of the files, despite there being an alpha channel in several of them.

Made a new branch with the edited code: https://github.com/MajinCry/Fallout-4-Texture-Resizer/tree/Test---Check-For-Alpha

Couple of .dds images I'm using to test: http://www66.zippyshare.com/v/cEJnMsOj/file.html

Edited by MajinCry

##### Share on other sites

I think testing DDPF_ALPHAPIXELS is only going to work reliably for uncompressed textures. If it's say a DXT5 texture then it could leave that flag clear and still work correctly. It would also be possible to generate a file where that flag is set, but the alpha channel is actually set to opaque for every pixel.

If you want to be certain, the easiest option is probably to convert to an uncompressed format, and read back the alpha channel. Of course for DXT5 you can be fairly certain there is an alpha channel - for opaque textures DXT1 is equivalent quality, and half the file size as it doesn't store full alpha data. However, a DXT1 image may or may not use the single bit alpha channel feature.

Of course most of the time game toolchains work the other way round - they start with uncompressed RGBA images and convert to .DDS to reduce the memory cost. When you're doing that you can make sure your converter sets a flag somewhere.

Also note that DXT is lossy compression. You will get better quality results if your tools work with uncompressed (or losslessly compressed) textures, and convert to .DDS as the final step.

##### Share on other sites

I think testing DDPF_ALPHAPIXELS is only going to work reliably for uncompressed textures. If it's say a DXT5 texture then it could leave that flag clear and still work correctly. It would also be possible to generate a file where that flag is set, but the alpha channel is actually set to opaque for every pixel.

If you want to be certain, the easiest option is probably to convert to an uncompressed format, and read back the alpha channel. Of course for DXT5 you can be fairly certain there is an alpha channel - for opaque textures DXT1 is equivalent quality, and half the file size as it doesn't store full alpha data. However, a DXT1 image may or may not use the single bit alpha channel feature.

Of course most of the time game toolchains work the other way round - they start with uncompressed RGBA images and convert to .DDS to reduce the memory cost. When you're doing that you can make sure your converter sets a flag somewhere.

Also note that DXT is lossy compression. You will get better quality results if your tools work with uncompressed (or losslessly compressed) textures, and convert to .DDS as the final step.

Y'know, I think you might be on to something. I checked a couple of the transparent textures, and they had DXT5 in the header. Checked a few that weren't transparent, and they had DXT1. Hmm.

If the image has an alpha channel but doesn't use it for anything, that's of no consequence; I don't plan on "optimizing" textures, as that would be more work and an absolute pain seeing how I can't even check if an alpha is present or not, for now at least. Would be good to have, but I'm aiming to get the base functionality down first.

Aye, it'd be great if Bethesda supplied the source textures (and the PSDs while they're at it), but, alas, they've only given us peasants the final .dds textures. 'Tis all I've got to work with, and it's also what the modders supply everyone with. Not ideal for this project, but, well, there's no alternative.

I wonder if SharpDX would be any use. The documentation is pretty curt, and there aren't any tutorials on how to use it for something like this (identifying pre-existing .dds image properties), though. Are there any users kicking around here who are fairly pro with that? I could try firin' them a wee message.