/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/

#include "stdafx.h"
#include "TextureDDS.h"

void DxtcReadColors(const GLubyte *Data, Color8888 *result)
{
    GLubyte r0, g0, b0, r1, g1, b1;

    b0 = Data[0] & 0x1F;
    g0 = ((Data[0] & 0xE0) >> 5) | ((Data[1] & 0x7) << 3);
    r0 = (Data[1] & 0xF8) >> 3;

    b1 = Data[2] & 0x1F;
    g1 = ((Data[2] & 0xE0) >> 5) | ((Data[3] & 0x7) << 3);
    r1 = (Data[3] & 0xF8) >> 3;

    result[0].r = r0 << 3 | r0 >> 2;
    result[0].g = g0 << 2 | g0 >> 3;
    result[0].b = b0 << 3 | b0 >> 2;

    result[1].r = r1 << 3 | r1 >> 2;
    result[1].g = g1 << 2 | g1 >> 3;
    result[1].b = b1 << 3 | b1 >> 2;
};

void DxtcReadColor(GLushort Data, Color8888 *Out)
{
    GLubyte r, g, b;

    b = Data & 0x1f;
    g = (Data & 0x7E0) >> 5;
    r = (Data & 0xF800) >> 11;

    Out->r = r << 3 | r >> 2;
    Out->g = g << 2 | g >> 3;
    Out->b = b << 3 | r >> 2;
};

void DecompressDXT1(DDS_IMAGE_DATA lImage, const GLubyte *lCompData, GLubyte *Data)
{
    GLint x, y, i, j, k;
    GLuint Select;
    const GLubyte *Temp;
    Color8888 colours[4], *col;
    GLushort color_0, color_1;
    GLuint bitmask, Offset;

    Temp = lCompData;
    colours[0].a = 0xFF;
    colours[1].a = 0xFF;
    colours[2].a = 0xFF;

    for (y = 0; y < lImage.height; y += 4)
    {
        for (x = 0; x < lImage.width; x += 4)
        {
            color_0 = *((const GLushort *)Temp);
            color_1 = *((const GLushort *)(Temp + 2));

            DxtcReadColor(color_0, colours);
            DxtcReadColor(color_1, colours + 1);
            bitmask = ((const GLuint *)Temp)[1];

            Temp += 8;

            if (color_0 > color_1)
            {
                // 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].b = (2 * colours[0].b + colours[1].b + 1) / 3;
                colours[2].g = (2 * colours[0].g + colours[1].g + 1) / 3;
                colours[2].r = (2 * colours[0].r + colours[1].r + 1) / 3;

                colours[3].b = (colours[0].b + 2 * colours[1].b + 1) / 3;
                colours[3].g = (colours[0].g + 2 * colours[1].g + 1) / 3;
                colours[3].r = (colours[0].r + 2 * colours[1].r + 1) / 3;
                colours[3].a = 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].b = (colours[0].b + colours[1].b) / 2;
                colours[2].g = (colours[0].g + colours[1].g) / 2;
                colours[2].r = (colours[0].r + colours[1].r) / 2;

                colours[3].b = (colours[0].b + 2 * colours[1].b + 1) / 3;
                colours[3].g = (colours[0].g + 2 * colours[1].g + 1) / 3;
                colours[3].r = (colours[0].r + 2 * colours[1].r + 1) / 3;
                colours[3].a = 0x00;
            }

            for (j = 0, k = 0; j < 4; j++)
            {
                for (i = 0; i < 4; i++, k++)
                {
                    Select = (bitmask & (0x03 << k * 2)) >> k * 2;
                    col = &colours[Select];

                    if (((x + i) < lImage.width) && ((y + j) < lImage.height))
                    {
                        Offset = (y + j) * lImage.width * lImage.components +
                                 (x + i) * lImage.components;
                        Data[Offset + 0] = col->r;
                        Data[Offset + 1] = col->g;
                        Data[Offset + 2] = col->b;
                        Data[Offset + 3] = col->a;
                    }
                }
            }
        }
    }
}

void DecompressDXT3(DDS_IMAGE_DATA lImage, const GLubyte *lCompData, GLubyte *Data)
{
    const GLubyte *Temp = lCompData;

    Color8888 colours[4], *col;
    GLuint bitmask, Offset;
    const GLubyte *alpha;

    for (GLint y = 0; y < lImage.height; y += 4)
    {
        for (GLint x = 0; x < lImage.width; x += 4)
        {
            alpha = Temp;
            Temp += 8;
            DxtcReadColors(Temp, colours);
            bitmask = ((GLuint *)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].b = (2 * colours[0].b + colours[1].b + 1) / 3;
            colours[2].g = (2 * colours[0].g + colours[1].g + 1) / 3;
            colours[2].r = (2 * colours[0].r + colours[1].r + 1) / 3;

            colours[3].b = (colours[0].b + 2 * colours[1].b + 1) / 3;
            colours[3].g = (colours[0].g + 2 * colours[1].g + 1) / 3;
            colours[3].r = (colours[0].r + 2 * colours[1].r + 1) / 3;

            GLuint k = 0;
            for (GLint j = 0; j < 4; j++)
            {
                for (GLint i = 0; i < 4; i++, k++)
                {
                    GLuint Select = (bitmask & (0x03 << k * 2)) >> k * 2;
                    col = &colours[Select];

                    if (((x + i) < lImage.width) && ((y + j) < lImage.height))
                    {
                        Offset = (y + j) * lImage.width * lImage.components +
                                 (x + i) * lImage.components;
                        Data[Offset + 0] = col->r;
                        Data[Offset + 1] = col->g;
                        Data[Offset + 2] = col->b;
                    }
                }
            }

            for (GLint j = 0; j < 4; j++)
            {
                GLushort word = alpha[2 * j] + 256 * alpha[2 * j + 1];
                for (GLint i = 0; i < 4; i++)
                {
                    if (((x + i) < lImage.width) && ((y + j) < lImage.height))
                    {
                        Offset = (y + j) * lImage.width * lImage.components +
                                 (x + i) * lImage.components + 3;
                        Data[Offset] = word & 0x0F;
                        Data[Offset] = Data[Offset] | (Data[Offset] << 4);
                    }
                    word >>= 4;
                }
            }
        }
    }
}

void DecompressDXT5(DDS_IMAGE_DATA lImage, const GLubyte *lCompData, GLubyte *Data)
{
    GLint x, y, i, j, k;
    GLuint Select;
    const GLubyte *Temp; //, r0, g0, b0, r1, g1, b1;
    Color8888 colours[4], *col;
    GLuint bitmask, Offset;
    GLubyte alphas[8];
    GLuint bits;

    Temp = lCompData;

    for (y = 0; y < lImage.height; y += 4)
    {
        for (x = 0; x < lImage.width; x += 4)
        {
            if (y >= lImage.height || x >= lImage.width)
                break;
            alphas[0] = Temp[0];
            alphas[1] = Temp[1];
            const GLubyte *alphamask = Temp + 2;
            Temp += 8;

            DxtcReadColors(Temp, colours);
            bitmask = ((const GLuint *)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].b = (2 * colours[0].b + colours[1].b + 1) / 3;
            colours[2].g = (2 * colours[0].g + colours[1].g + 1) / 3;
            colours[2].r = (2 * colours[0].r + colours[1].r + 1) / 3;

            colours[3].b = (colours[0].b + 2 * colours[1].b + 1) / 3;
            colours[3].g = (colours[0].g + 2 * colours[1].g + 1) / 3;
            colours[3].r = (colours[0].r + 2 * colours[1].r + 1) / 3;

            k = 0;
            for (j = 0; j < 4; j++)
            {
                for (i = 0; i < 4; i++, k++)
                {

                    Select = (bitmask & (0x03 << k * 2)) >> k * 2;
                    col = &colours[Select];

                    // only put pixels out < width or height
                    if (((x + i) < lImage.width) && ((y + j) < lImage.height))
                    {
                        Offset = (y + j) * lImage.width * lImage.components +
                                 (x + i) * lImage.components;
                        Data[Offset + 0] = col->r;
                        Data[Offset + 1] = col->g;
                        Data[Offset + 2] = col->b;
                    }
                }
            }

            // 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] = (6 * alphas[0] + 1 * alphas[1] + 3) / 7; // bit code 010
                alphas[3] = (5 * alphas[0] + 2 * alphas[1] + 3) / 7; // bit code 011
                alphas[4] = (4 * alphas[0] + 3 * alphas[1] + 3) / 7; // bit code 100
                alphas[5] = (3 * alphas[0] + 4 * alphas[1] + 3) / 7; // bit code 101
                alphas[6] = (2 * alphas[0] + 5 * alphas[1] + 3) / 7; // bit code 110
                alphas[7] = (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] = (4 * alphas[0] + 1 * alphas[1] + 2) / 5; // Bit code 010
                alphas[3] = (3 * alphas[0] + 2 * alphas[1] + 2) / 5; // Bit code 011
                alphas[4] = (2 * alphas[0] + 3 * alphas[1] + 2) / 5; // Bit code 100
                alphas[5] = (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
            // bits = *((ILint*)alphamask);
            bits = (alphamask[0]) | (alphamask[1] << 8) | (alphamask[2] << 16);
            for (j = 0; j < 2; j++)
            {
                for (i = 0; i < 4; i++)
                {
                    // only put pixels out < width or height
                    if (((x + i) < lImage.width) && ((y + j) < lImage.height))
                    {
                        Offset = (y + j) * lImage.width * lImage.components +
                                 (x + i) * lImage.components + 3;
                        Data[Offset] = alphas[bits & 0x07];
                    }
                    bits >>= 3;
                }
            }

            // Last three bytes
            // bits = *((ILint*)&alphamask[3]);
            bits = (alphamask[3]) | (alphamask[4] << 8) | (alphamask[5] << 16);
            for (j = 2; j < 4; j++)
            {
                for (i = 0; i < 4; i++)
                {
                    // only put pixels out < width or height
                    if (((x + i) < lImage.width) && ((y + j) < lImage.height))
                    {
                        Offset = (y + j) * lImage.width * lImage.components +
                                 (x + i) * lImage.components + 3;
                        Data[Offset] = alphas[bits & 0x07];
                    }
                    bits >>= 3;
                }
            }
        }
    }
}

void DecompressDXT(DDS_IMAGE_DATA lImage, const GLubyte *lCompData, GLubyte *Data)
{
    switch (lImage.format)
    {
    case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
        DecompressDXT1(lImage, lCompData, Data);
        break;

    case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
        DecompressDXT3(lImage, lCompData, Data);
        break;

    case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
        DecompressDXT5(lImage, lCompData, Data);
        break;
    };
}
