nickbabcock / Pfim

.NET Standard targa (.tga) and direct draw surface (.dds) image decoder
https://nickbabcock.github.io/Pfim/
MIT License
109 stars 18 forks source link

DXT1 Alpha support #70

Closed LiamKarlMitchell closed 3 years ago

LiamKarlMitchell commented 3 years ago

DXT1 can contain alpha.

RGB 5:6:5 RGBA 5:5:5:1

When a DDS DXT1 is loaded into Paint.Net, Window Texture Viewer, Photoshop, previewed in explorer with the DDS extension etc it has a transparency.

When loading with Pfim this alpha is not available.

Screenshot showing Paint.Net and Pfim Viewer. image

Sample dds attached. 0004a283.zip

References: https://docs.microsoft.com/en-gb/windows/win32/direct3d9/opaque-and-1-bit-alpha-textures?redirectedfrom=MSDN https://stackoverflow.com/questions/19448/in-a-dds-file-can-you-detect-textures-with-0-1-alpha-bits https://www.buckarooshangar.com/flightgear/tut_dds.html https://www.khronos.org/registry/DataFormat/specs/1.1/dataformat.1.1.html#_bc1_with_alpha https://en.wikipedia.org/wiki/S3_Texture_Compression#DXT1

Paint.Net plugin for loading DDS. https://github.com/0xC0000054/pdn-ddsfiletype-plus/tree/master/3rdParty/DirectXTex/DirectXTex

Dxt1Dds changes: Change DIV_SIZE to 4.

private const int DIV_SIZE = 4;

Change image format to Rgba32

public override ImageFormat Format => ImageFormat.Rgba32;

And writing out an alpha byte after the rgb. The part I am stuck on at the moment how to get out the correct alpha bit for each pixel.

nickbabcock commented 3 years ago

Thank you for the test case and the resources. I can confirm the bug. I may mull over potential solutions for a bit. Seems at the very least, Dxt1 will need to change it's image format at decode time from Rgb24 to something else (or always use Rgba32 like you suggest)

LiamKarlMitchell commented 3 years ago

I thought about changing at decode time. It could allocate the extra bytes with PIXEL_DEPTH = 4 have a private variable for the real ImageFormat. If a transparent part is found, then set it to Rgba32 Otherwise Rgb24 and truncate the Dds bytes returned to remove the excess.

But this could be messy with mip maps have not looked yet at how that is done.

https://www.khronos.org/opengl/wiki/S3_Texture_Compression Actually gave me the way to get it in clear English,

DXT1 with 1-bit Alpha
There is a form of DXT1 available that provides a simple on/off alpha value. This format therefore uses an RGBA base format. To get this format, use the GL_COMPRESSED_RGBA_S3TC_DXT1_EXT internal format.

The format of the data is identical to the above case, which is why this is still DXT1 compression. The interpretation differs slightly. You always get an alpha value of 1 unless the pixel uses the code for Black in the above table. In that case, you get an alpha value of 0.

Note that this means that the RGB colors will also be 0 on any pixel with a 0 alpha. This also means that bilinear filtering between neighboring texels will result in colors combined with black. If you are using premultipled alpha blending, this is what you want. If you aren't, then it almost certainly is not what you want.

The filtering thing can be handled by adjusting the alpha cut off value.

This write up has an example of the black line issue when rendering with filtering. http://www.reedbeta.com/blog/understanding-bcn-texture-compression-formats/ image

if (color0 <= color1 && ((rowVal >> j) & 0x3) == 3)
{
    data[dataIndex++] = 0x00;
}
else
{
    data[dataIndex++] = 0xFF;
}

image

I'm sure it could be done nicer, I would assume that just using Rgba32 format would be faster as there would not be a constant setting of the format.

LiamKarlMitchell commented 3 years ago

Hi @nickbabcock ,

Thank you very much for implementing this I had neglected to the mip map information. Suggest you use an alternative sample image MIT license etc.

Just made this one up with some transparent radio gradients. It has different sizes of transparent blocks and colors.

image image

Please see attached. dxt1-alpha.zip

nickbabcock commented 3 years ago

Thank you, this actually uncovered another bug where no floor was applied to mipmaps (ie: the last entry could have a height / width of 0). This will be fixed in #73 as well as your requested image change