Need help with byte to rgba DXT5 converter

Mang0e

New member
Joined
Jun 5, 2020
Messages
3
Programming Experience
Beginner
I'm attempting to make myself a tool that will convert a byte array into an image, specifically one that is rgba (DXT5)

The program below works fine, but I can't figure out where to input my byte array. It's in C#, maybe someone could take a look at it and give me a summary of what I should do? I'm guessing I'm going to have to write a program that will utilize the program below, and run my byte array through the program below.

Basically, I need help figuring out where I need to input my byte array in the program below, and how I should go about doing it. Thanks and stay safe.

C#:
using System;
using System.IO;
using PakReader.Parsers.Objects;
using SkiaSharp;

namespace PakReader
{
    static class TextureDecoder
    {
        public static SKImage DecodeImage(byte[] sequence, int width, int height, int depth, EPixelFormat format)
        {
            byte[] data;
            SKColorType colorType;
            switch (format)
            {
                case EPixelFormat.PF_DXT5:
                    data = DXTDecoder.DecodeDXT5(sequence, width, height, depth);
                    colorType = SKColorType.Rgba8888;
                    break;
                case EPixelFormat.PF_DXT1:
                    data = DXTDecoder.DecodeDXT1(sequence, width, height, depth);
                    colorType = SKColorType.Rgba8888;
                    break;
                case EPixelFormat.PF_B8G8R8A8:
                    data = sequence;
                    colorType = SKColorType.Bgra8888;
                    break;
                case EPixelFormat.PF_BC5:
                    data = BCDecoder.DecodeBC5(sequence, width, height);
                    colorType = SKColorType.Bgra8888;
                    break;
                case EPixelFormat.PF_BC4:
                    data = BCDecoder.DecodeBC4(sequence, width, height);
                    colorType = SKColorType.Bgra8888;
                    break;
                case EPixelFormat.PF_G8:
                    data = sequence;
                    colorType = SKColorType.Gray8;
                    break;
                case EPixelFormat.PF_FloatRGBA:
                    data = sequence;
                    colorType = SKColorType.RgbaF16;
                    break;
                default:
                    throw new NotImplementedException($"Cannot decode {format} format");
            }

            using (var bitmap = new SKBitmap(new SKImageInfo(width, height, colorType, SKAlphaType.Unpremul)))
            {
                unsafe
                {
                    fixed (byte* p = data)
                    {
                        bitmap.SetPixels(new IntPtr(p));
                    }
                }
                return SKImage.FromBitmap(bitmap);
            }
        }

        static class BCDecoder
        {
            public static byte[] DecodeBC4(byte[] inp, int width, int height)
            {
                byte[] ret = new byte[width * height * 4];
                using var reader = new BinaryReader(new MemoryStream(inp));
                for (int y_block = 0; y_block < height / 4; y_block++)
                {
                    for (int x_block = 0; x_block < width / 4; x_block++)
                    {
                        var r_bytes = DecodeBC3Block(reader);
                        for (int i = 0; i < 16; i++)
                        {
                            ret[GetPixelLoc(width, x_block * 4 + (i % 4), y_block * 4 + (i / 4), 4, 0)] = r_bytes[i];
                        }
                    }
                }
                return ret;
            }

            public static byte[] DecodeBC5(byte[] inp, int width, int height)
            {
                byte[] ret = new byte[width * height * 4];
                using var reader = new BinaryReader(new MemoryStream(inp));
                for (int y_block = 0; y_block < height / 4; y_block++)
                {
                    for (int x_block = 0; x_block < width / 4; x_block++)
                    {
                        var r_bytes = DecodeBC3Block(reader);
                        var g_bytes = DecodeBC3Block(reader);

                        for (int i = 0; i < 16; i++)
                        {
                            ret[GetPixelLoc(width, x_block * 4 + (i % 4), y_block * 4 + (i / 4), 4, 0)] = r_bytes[i];
                            ret[GetPixelLoc(width, x_block * 4 + (i % 4), y_block * 4 + (i / 4), 4, 1)] = g_bytes[i];
                            ret[GetPixelLoc(width, x_block * 4 + (i % 4), y_block * 4 + (i / 4), 4, 2)] = GetZNormal(r_bytes[i], g_bytes[i]);
                        }
                    }
                }
                return ret;
            }

            static int GetPixelLoc(int width, int x, int y, int bpp, int off) => (y * width + x) * bpp + off;

            static byte GetZNormal(byte x, byte y)
            {
                var xf = (x / 127.5f) - 1;
                var yf = (y / 127.5f) - 1;
                var zval = 1 - xf * xf - yf * yf;
                var zval_ = (float)Math.Sqrt(zval > 0 ? zval : 0);
                zval = zval_ < 1 ? zval_ : 1;
                return (byte)((zval * 127) + 128);
            }

            static byte[] DecodeBC3Block(BinaryReader reader)
            {
                float ref0 = reader.ReadByte();
                float ref1 = reader.ReadByte();

                float[] ref_sl = new float[8];
                ref_sl[0] = ref0;
                ref_sl[1] = ref1;

                if (ref0 > ref1)
                {
                    ref_sl[2] = (6 * ref0 + 1 * ref1) / 7;
                    ref_sl[3] = (5 * ref0 + 2 * ref1) / 7;
                    ref_sl[4] = (4 * ref0 + 3 * ref1) / 7;
                    ref_sl[5] = (3 * ref0 + 4 * ref1) / 7;
                    ref_sl[6] = (2 * ref0 + 5 * ref1) / 7;
                    ref_sl[7] = (1 * ref0 + 6 * ref1) / 7;
                }
                else
                {
                    ref_sl[2] = (4 * ref0 + 1 * ref1) / 5;
                    ref_sl[3] = (3 * ref0 + 2 * ref1) / 5;
                    ref_sl[4] = (2 * ref0 + 3 * ref1) / 5;
                    ref_sl[5] = (1 * ref0 + 4 * ref1) / 5;
                    ref_sl[6] = 0;
                    ref_sl[7] = 255;
                }

                byte[] index_block1 = GetBC3Indices(reader.ReadBytes(3));

                byte[] index_block2 = GetBC3Indices(reader.ReadBytes(3));

                byte[] bytes = new byte[16];
                for (int i = 0; i < 8; i++)
                {
                    bytes[7 - i] = (byte)ref_sl[index_block1[i]];
                }
                for (int i = 0; i < 8; i++)
                {
                    bytes[15 - i] = (byte)ref_sl[index_block2[i]];
                }

                return bytes;
            }

            static byte[] GetBC3Indices(byte[] buf_block) =>
                new byte[] {
                (byte)((buf_block[2] & 0b1110_0000) >> 5),
                (byte)((buf_block[2] & 0b0001_1100) >> 2),
                (byte)(((buf_block[2] & 0b0000_0011) << 1) | ((buf_block[1] & 0b1 << 7) >> 7)),
                (byte)((buf_block[1] & 0b0111_0000) >> 4),
                (byte)((buf_block[1] & 0b0000_1110) >> 1),
                (byte)(((buf_block[1] & 0b0000_0001) << 2) | ((buf_block[0] & 0b11 << 6) >> 6)),
                (byte)((buf_block[0] & 0b0011_1000) >> 3),
                (byte)(buf_block[0] & 0b0000_0111)
                };
        }

        static class DXTDecoder
        {
            struct Colour8888
            {
                public byte red;
                public byte green;
                public byte blue;
                public byte alpha;
            }

            public static byte[] DecodeDXT1(byte[] inp, int width, int height, int depth)
            {
                var bpp = 4;
                var bps = width * bpp * 1;
                var sizeofplane = bps * height;

                byte[] rawData = new byte[depth * sizeofplane + height * bps + width * bpp];
                var colours = new Colour8888[4];
                colours[0].alpha = 0xFF;
                colours[1].alpha = 0xFF;
                colours[2].alpha = 0xFF;

                unsafe
                {
                    fixed (byte* bytePtr = inp)
                    {
                        byte* temp = bytePtr;
                        for (int z = 0; z < depth; z++)
                        {
                            for (int y = 0; y < height; y += 4)
                            {
                                for (int x = 0; x < width; x += 4)
                                {
                                    ushort colour0 = *((ushort*)temp);
                                    ushort colour1 = *((ushort*)(temp + 2));
                                    DxtcReadColor(colour0, ref colours[0]);
                                    DxtcReadColor(colour1, ref colours[1]);

                                    uint bitmask = ((uint*)temp)[1];
                                    temp += 8;

                                    if (colour0 > colour1)
                                    {
                                        // Four-color block: derive the other two colors.
                                        // 00 = color_0, 01 = color_1, 10 = color_2, 11 = color_3
                                        // These 2-bit codes correspond to the 2-bit fields
                                        // stored in the 64-bit block.
                                        colours[2].blue = (byte)((2 * colours[0].blue + colours[1].blue + 1) / 3);
                                        colours[2].green = (byte)((2 * colours[0].green + colours[1].green + 1) / 3);
                                        colours[2].red = (byte)((2 * colours[0].red + colours[1].red + 1) / 3);
                                        //colours[2].alpha = 0xFF;

                                        colours[3].blue = (byte)((colours[0].blue + 2 * colours[1].blue + 1) / 3);
                                        colours[3].green = (byte)((colours[0].green + 2 * colours[1].green + 1) / 3);
                                        colours[3].red = (byte)((colours[0].red + 2 * colours[1].red + 1) / 3);
                                        colours[3].alpha = 0xFF;
                                    }
                                    else
                                    {
                                        // Three-color block: derive the other color.
                                        // 00 = color_0,  01 = color_1,  10 = color_2,
                                        // 11 = transparent.
                                        // These 2-bit codes correspond to the 2-bit fields
                                        // stored in the 64-bit block.
                                        colours[2].blue = (byte)((colours[0].blue + colours[1].blue) / 2);
                                        colours[2].green = (byte)((colours[0].green + colours[1].green) / 2);
                                        colours[2].red = (byte)((colours[0].red + colours[1].red) / 2);
                                        //colours[2].alpha = 0xFF;

                                        colours[3].blue = (byte)((colours[0].blue + 2 * colours[1].blue + 1) / 3);
                                        colours[3].green = (byte)((colours[0].green + 2 * colours[1].green + 1) / 3);
                                        colours[3].red = (byte)((colours[0].red + 2 * colours[1].red + 1) / 3);
                                        colours[3].alpha = 0x00;
                                    }

                                    for (int j = 0, k = 0; j < 4; j++)
                                    {
                                        for (int i = 0; i < 4; i++, k++)
                                        {
                                            int select = (int)((bitmask & (0x03 << k * 2)) >> k * 2);
                                            Colour8888 col = colours[select];
                                            if (((x + i) < width) && ((y + j) < height))
                                            {
                                                uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp);
                                                rawData[offset + 0] = col.red;
                                                rawData[offset + 1] = col.green;
                                                rawData[offset + 2] = col.blue;
                                                rawData[offset + 3] = col.alpha;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }

                return rawData;
            }

            public static byte[] DecodeDXT5(byte[] inp, int width, int height, int depth)
            {
                var bpp = 4;
                var bps = width * bpp * 1;
                var sizeofplane = bps * height;

                byte[] rawData = new byte[depth * sizeofplane + height * bps + width * bpp];
                var colours = new Colour8888[4];
                ushort[] alphas = new ushort[8];

                unsafe
                {
                    fixed (byte* bytePtr = inp)
                    {
                        byte* temp = bytePtr;
                        for (int z = 0; z < depth; z++)
                        {
                            for (int y = 0; y < height; y += 4)
                            {
                                for (int x = 0; x < width; x += 4)
                                {
                                    if (y >= height || x >= width)
                                        break;

                                    alphas[0] = temp[0];
                                    alphas[1] = temp[1];
                                    byte* alphamask = (temp + 2);
                                    temp += 8;

                                    DxtcReadColors(temp, colours);
                                    uint bitmask = ((uint*)temp)[1];
                                    temp += 8;

                                    // Four-color block: derive the other two colors.
                                    // 00 = color_0, 01 = color_1, 10 = color_2, 11    = color_3
                                    // These 2-bit codes correspond to the 2-bit fields
                                    // stored in the 64-bit block.
                                    colours[2].blue = (byte)((2 * colours[0].blue + colours[1].blue + 1) / 3);
                                    colours[2].green = (byte)((2 * colours[0].green + colours[1].green + 1) / 3);
                                    colours[2].red = (byte)((2 * colours[0].red + colours[1].red + 1) / 3);
                                    //colours[2].alpha = 0xFF;

                                    colours[3].blue = (byte)((colours[0].blue + 2 * colours[1].blue + 1) / 3);
                                    colours[3].green = (byte)((colours[0].green + 2 * colours[1].green + 1) / 3);
                                    colours[3].red = (byte)((colours[0].red + 2 * colours[1].red + 1) / 3);
                                    //colours[3].alpha = 0xFF;

                                    int k = 0;
                                    for (int j = 0; j < 4; j++)
                                    {
                                        for (int i = 0; i < 4; k++, i++)
                                        {
                                            int select = (int)((bitmask & (0x03 << k * 2)) >> k * 2);
                                            Colour8888 col = colours[select];
                                            // only put pixels out < width or height
                                            if (((x + i) < width) && ((y + j) < height))
                                            {
                                                uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp);
                                                rawData[offset] = col.red;
                                                rawData[offset + 1] = col.green;
                                                rawData[offset + 2] = col.blue;
                                            }
                                        }
                                    }

                                    // 8-alpha or 6-alpha block?
                                    if (alphas[0] > alphas[1])
                                    {
                                        // 8-alpha block:  derive the other six alphas.
                                        // Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated.
                                        alphas[2] = (ushort)((6 * alphas[0] + 1 * alphas[1] + 3) / 7); // bit code 010
                                        alphas[3] = (ushort)((5 * alphas[0] + 2 * alphas[1] + 3) / 7); // bit code 011
                                        alphas[4] = (ushort)((4 * alphas[0] + 3 * alphas[1] + 3) / 7); // bit code 100
                                        alphas[5] = (ushort)((3 * alphas[0] + 4 * alphas[1] + 3) / 7); // bit code 101
                                        alphas[6] = (ushort)((2 * alphas[0] + 5 * alphas[1] + 3) / 7); // bit code 110
                                        alphas[7] = (ushort)((1 * alphas[0] + 6 * alphas[1] + 3) / 7); // bit code 111
                                    }
                                    else
                                    {
                                        // 6-alpha block.
                                        // Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated.
                                        alphas[2] = (ushort)((4 * alphas[0] + 1 * alphas[1] + 2) / 5); // Bit code 010
                                        alphas[3] = (ushort)((3 * alphas[0] + 2 * alphas[1] + 2) / 5); // Bit code 011
                                        alphas[4] = (ushort)((2 * alphas[0] + 3 * alphas[1] + 2) / 5); // Bit code 100
                                        alphas[5] = (ushort)((1 * alphas[0] + 4 * alphas[1] + 2) / 5); // Bit code 101
                                        alphas[6] = 0x00; // Bit code 110
                                        alphas[7] = 0xFF; // Bit code 111
                                    }

                                    // Note: Have to separate the next two loops,
                                    // it operates on a 6-byte system.

                                    // First three bytes
                                    //uint bits = (uint)(alphamask[0]);
                                    uint bits = (uint)((alphamask[0]) | (alphamask[1] << 8) | (alphamask[2] << 16));
                                    for (int j = 0; j < 2; j++)
                                    {
                                        for (int i = 0; i < 4; i++)
                                        {
                                            // only put pixels out < width or height
                                            if (((x + i) < width) && ((y + j) < height))
                                            {
                                                uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp + 3);
                                                rawData[offset] = (byte)alphas[bits & 0x07];
                                            }
                                            bits >>= 3;
                                        }
                                    }

                                    // Last three bytes
                                    //bits = (uint)(alphamask[3]);
                                    bits = (uint)((alphamask[3]) | (alphamask[4] << 8) | (alphamask[5] << 16));
                                    for (int j = 2; j < 4; j++)
                                    {
                                        for (int i = 0; i < 4; i++)
                                        {
                                            // only put pixels out < width or height
                                            if (((x + i) < width) && ((y + j) < height))
                                            {
                                                uint offset = (uint)(z * sizeofplane + (y + j) * bps + (x + i) * bpp + 3);
                                                rawData[offset] = (byte)alphas[bits & 0x07];
                                            }
                                            bits >>= 3;
                                        }
                                    }
                                }
                            }
                        }
                    }
                    return rawData;
                }
            }

            static unsafe void DxtcReadColors(byte* data, Colour8888[] op)
            {
                byte buf = (byte)((data[1] & 0xF8) >> 3);
                op[0].red = (byte)(buf << 3 | buf >> 2);
                buf = (byte)(((data[0] & 0xE0) >> 5) | ((data[1] & 0x7) << 3));
                op[0].green = (byte)(buf << 2 | buf >> 3);
                buf = (byte)(data[0] & 0x1F);
                op[0].blue = (byte)(buf << 3 | buf >> 2);

                buf = (byte)((data[3] & 0xF8) >> 3);
                op[1].red = (byte)(buf << 3 | buf >> 2);
                buf = (byte)(((data[2] & 0xE0) >> 5) | ((data[3] & 0x7) << 3));
                op[1].green = (byte)(buf << 2 | buf >> 3);
                buf = (byte)(data[2] & 0x1F);
                op[1].blue = (byte)(buf << 3 | buf >> 2);
            }

            static void DxtcReadColor(ushort data, ref Colour8888 op)
            {
                byte buf = (byte)((data & 0xF800) >> 11);
                op.red = (byte)(buf << 3 | buf >> 2);
                buf = (byte)((data & 0x7E0) >> 5);
                op.green = (byte)(buf << 2 | buf >> 3);
                buf = (byte)(data & 0x1f);
                op.blue = (byte)(buf << 3 | buf >> 2);
            }
        }
    }
}
 
Basically, I need help figuring out where I need to input my byte array in the program below, and how I should go about doing it. Thanks and stay safe.
Welcome to the forums.

Basically the first place to start is to ask the author of where ever you got this code from.

If you wrote it yourself then you should know how it works. If you didn't write it yourself. This is often a good reason why not to use other peoples code.

It's also impractical to post 434 lines of code and expect that someone else can work it our for you.

Where did you get the code from?
 
I'm extremely new to C#, and I apologize for asking too much from others and asking a stupid question.

I got the code from a person off of GitHub. I've already talked to him, but he wasn't really willing to help me. I've sort of figured out what I need to do to get it to work for me.
I'm using someone elses code, because for this kind of converter it can only be done in C# I think and it's extremely complex stuff imo.

Where I got the code
The Code

I need to create a small program that will call TextureDecoder.DecodeImage and inputs the byte array when I paste it into the code.

Only problem is I'm pretty bad a C# and have no idea where I would start with the small program.
 
I would then recommend learning a bit more C# before tackling this project of doing image processing. In my opinion all you need is about a week more worth of experience, after which when you see line #10 above in the code, it will be self evident to you about how to pass in bytes in DXT5 format and get back an image. I'm not surprised that the author blew you off. I would to.

You were basically asking Michael Jordan to teach you how to dribble a basketball.
 
That's an extremely fair answer, do you have a recommendation as to a resource I could use to learn for a little while?
 
Back
Top Bottom