//===============================================================================
// Copyright (c) 2007-2024  Advanced Micro Devices, Inc. All rights reserved.
// Copyright (c) 2004-2006 ATI Technologies Inc.
//===============================================================================
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
//
//  File Name:   Codec_ATC.cpp
//  Description: implementation of the CCodec_ATC class
//
//////////////////////////////////////////////////////////////////////////////

#include "common.h"
#include "codec_atc.h"
#include "compressonator_tc.h"
#include "compressonatorxcodec.h"

using namespace CMP;

//////////////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////////////

CCodec_ATC::CCodec_ATC(CodecType codecType)
    : CCodec_Block_4x4(codecType)
{
}

CCodec_ATC::~CCodec_ATC()
{
}

CodecError CCodec_ATC::CompressRGBBlock(CMP_BYTE rgbBlock[BLOCK_SIZE_4X4X4], CMP_DWORD compressedBlock[2])
{
    Color888_t   srcRGB[4][4];
    unsigned int colorLow565or1555, colorHigh565;

    for (int y = 0; y < 4; y++)
    {
        for (DWORD x = 0; x < 4; x++)
        {
            srcRGB[x][y].red   = rgbBlock[(x * 16) + (y * 4) + ATC_RGBA8888_CHANNEL_R];
            srcRGB[x][y].green = rgbBlock[(x * 16) + (y * 4) + ATC_RGBA8888_CHANNEL_G];
            srcRGB[x][y].blue  = rgbBlock[(x * 16) + (y * 4) + ATC_RGBA8888_CHANNEL_B];
        }
    }

    compressedBlock[1] = atiEncodeRGBBlockATITC(&srcRGB, &colorLow565or1555, &colorHigh565);
    compressedBlock[0] = (colorHigh565 << 16) | colorLow565or1555;

    return CE_OK;
}

void CCodec_ATC::DecompressRGBBlock(CMP_BYTE rgbBlock[BLOCK_SIZE_4X4X4], CMP_DWORD compressedBlock[2])
{
    Color888_t destRGB[4][4];

    unsigned int colorLow565or1555 = compressedBlock[0] & 0xffff;
    unsigned int colorHigh565      = (compressedBlock[0] >> 16) & 0xffff;

    atiDecodeRGBBlockATITC(&destRGB, compressedBlock[1], colorLow565or1555, colorHigh565);

    for (DWORD y = 0; y < 4; y++)
    {
        for (DWORD x = 0; x < 4; x++)
        {
            rgbBlock[(x * 16) + (y * 4) + ATC_RGBA8888_CHANNEL_R] = destRGB[x][y].red;
            rgbBlock[(x * 16) + (y * 4) + ATC_RGBA8888_CHANNEL_G] = destRGB[x][y].green;
            rgbBlock[(x * 16) + (y * 4) + ATC_RGBA8888_CHANNEL_B] = destRGB[x][y].blue;
        }
    }
}

CodecError CCodec_ATC::CompressRGBABlock_ExplicitAlpha(CMP_BYTE rgbaBlock[BLOCK_SIZE_4X4X4], CMP_DWORD compressedBlock[4])
{
    CMP_BYTE alphaBlock[BLOCK_SIZE_4X4];
    for (CMP_DWORD i = 0; i < 16; i++)
        alphaBlock[i] = static_cast<CMP_BYTE>(((DWORD*)rgbaBlock)[i] >> ATC_RGBA8888_OFFSET_A);

    CodecError err = CompressExplicitAlphaBlock(alphaBlock, &compressedBlock[ATC_OFFSET_ALPHA]);
    if (err != CE_OK)
        return err;

    return CompressRGBBlock(rgbaBlock, &compressedBlock[ATC_OFFSET_RGB]);
}

void CCodec_ATC::DecompressRGBABlock_ExplicitAlpha(CMP_BYTE rgbaBlock[BLOCK_SIZE_4X4X4], CMP_DWORD compressedBlock[4])
{
    CMP_BYTE alphaBlock[BLOCK_SIZE_4X4];

    DecompressExplicitAlphaBlock(alphaBlock, &compressedBlock[ATC_OFFSET_ALPHA]);
    DecompressRGBBlock(rgbaBlock, &compressedBlock[ATC_OFFSET_RGB]);

    for (CMP_DWORD i = 0; i < 16; i++)
        ((DWORD*)rgbaBlock)[i] = (alphaBlock[i] << ATC_RGBA8888_OFFSET_A) | (((DWORD*)rgbaBlock)[i] & ~(BYTE_MASK << ATC_RGBA8888_OFFSET_A));
}

CodecError CCodec_ATC::CompressRGBABlock_InterpolatedAlpha(CMP_BYTE rgbaBlock[BLOCK_SIZE_4X4X4], CMP_DWORD compressedBlock[4])
{
    CMP_BYTE alphaBlock[BLOCK_SIZE_4X4];
    for (CMP_DWORD i = 0; i < 16; i++)
        alphaBlock[i] = static_cast<CMP_BYTE>(((DWORD*)rgbaBlock)[i] >> ATC_RGBA8888_OFFSET_A);

    CodecError err = CompressInterpolatedAlphaBlock(alphaBlock, &compressedBlock[ATC_OFFSET_ALPHA]);
    if (err != CE_OK)
        return err;

    return CompressRGBBlock(rgbaBlock, &compressedBlock[ATC_OFFSET_RGB]);
}

void CCodec_ATC::DecompressRGBABlock_InterpolatedAlpha(CMP_BYTE rgbaBlock[BLOCK_SIZE_4X4X4], CMP_DWORD compressedBlock[4])
{
    CMP_BYTE alphaBlock[BLOCK_SIZE_4X4];

    DecompressInterpolatedAlphaBlock(alphaBlock, &compressedBlock[ATC_OFFSET_ALPHA]);
    DecompressRGBBlock(rgbaBlock, &compressedBlock[ATC_OFFSET_RGB]);

    for (CMP_DWORD i = 0; i < 16; i++)
        ((DWORD*)rgbaBlock)[i] = (alphaBlock[i] << ATC_RGBA8888_OFFSET_A) | (((DWORD*)rgbaBlock)[i] & ~(BYTE_MASK << ATC_RGBA8888_OFFSET_A));
}

#define EXPLICIT_ALPHA_PIXEL_MASK 0xf
#define EXPLICIT_ALPHA_PIXEL_BPP 4
CodecError CCodec_ATC::CompressExplicitAlphaBlock(CMP_BYTE alphaBlock[BLOCK_SIZE_4X4], CMP_DWORD compressedBlock[2])
{
    compressedBlock[0] = compressedBlock[1] = 0;
    for (int i = 0; i < 16; i++)
    {
        int      nBlock = i < 8 ? 0 : 1;
        CMP_BYTE cAlpha =
            (CMP_BYTE)((alphaBlock[i] + ((alphaBlock[i] >> EXPLICIT_ALPHA_PIXEL_BPP) < 0x8 ? 7 : 8) - (alphaBlock[i] >> EXPLICIT_ALPHA_PIXEL_BPP)) >>
                       EXPLICIT_ALPHA_PIXEL_BPP);
        if (cAlpha > EXPLICIT_ALPHA_PIXEL_MASK)
            cAlpha = EXPLICIT_ALPHA_PIXEL_MASK;
        compressedBlock[nBlock] |= (cAlpha << ((i % 8) * EXPLICIT_ALPHA_PIXEL_BPP));
    }

    return CE_OK;
}

void CCodec_ATC::DecompressExplicitAlphaBlock(CMP_BYTE alphaBlock[BLOCK_SIZE_4X4], CMP_DWORD compressedBlock[2])
{
    for (int i = 0; i < 16; i++)
    {
        int      nBlock = i < 8 ? 0 : 1;
        CMP_BYTE cAlpha = (CMP_BYTE)((compressedBlock[nBlock] >> ((i % 8) * EXPLICIT_ALPHA_PIXEL_BPP)) & EXPLICIT_ALPHA_PIXEL_MASK);
        alphaBlock[i]   = (CMP_BYTE)((cAlpha << EXPLICIT_ALPHA_PIXEL_BPP) | cAlpha);
    }
}

CodecError CCodec_ATC::CompressInterpolatedAlphaBlock(CMP_BYTE alphaBlock[BLOCK_SIZE_4X4], CMP_DWORD compressedBlock[2])
{
    BYTE  nEndpoints[2][2];
    BYTE  nIndices[2][BLOCK_SIZE_4X4];
    float fError8 = CompBlock1X(alphaBlock, BLOCK_SIZE_4X4, nEndpoints[0], nIndices[0], 8, false, m_bUseSSE2, 8, 0, true);
    float fError6 = (fError8 == 0.f) ? FLT_MAX : CompBlock1X(alphaBlock, BLOCK_SIZE_4X4, nEndpoints[1], nIndices[1], 6, true, m_bUseSSE2, 8, 0, true);
    if (fError8 <= fError6)
        EncodeAlphaBlock(compressedBlock, nEndpoints[0], nIndices[0]);
    else
        EncodeAlphaBlock(compressedBlock, nEndpoints[1], nIndices[1]);

    return CE_OK;
}

void CCodec_ATC::EncodeAlphaBlock(CMP_DWORD compressedBlock[2], BYTE nEndpoints[2], BYTE nIndices[BLOCK_SIZE_4X4])
{
    compressedBlock[0] = ((int)nEndpoints[0]) | (((int)nEndpoints[1]) << 8);
    compressedBlock[1] = 0;

    for (int i = 0; i < BLOCK_SIZE_4X4; i++)
    {
        if (i < 5)
            compressedBlock[0] |= (nIndices[i] & 0x7) << (16 + (i * 3));
        else if (i > 5)
            compressedBlock[1] |= (nIndices[i] & 0x7) << (2 + (i - 6) * 3);
        else
        {
            compressedBlock[0] |= (nIndices[i] & 0x1) << 31;
            compressedBlock[1] |= (nIndices[i] & 0x6) >> 1;
        }
    }
}

void CCodec_ATC::DecompressInterpolatedAlphaBlock(CMP_BYTE alphaBlock[BLOCK_SIZE_4X4], CMP_DWORD compressedBlock[2])
{
    CMP_BYTE alpha[8];
    GetCompressedAlphaRamp(alpha, compressedBlock);

    for (int i = 0; i < BLOCK_SIZE_4X4; i++)
    {
        CMP_DWORD index;
        if (i < 5)
            index = (compressedBlock[0] & (0x7 << (16 + (i * 3)))) >> (16 + (i * 3));
        else if (i > 5)
            index = (compressedBlock[1] & (0x7 << (2 + (i - 6) * 3))) >> (2 + (i - 6) * 3);
        else
        {
            index = (compressedBlock[0] & 0x80000000) >> 31;
            index |= (compressedBlock[1] & 0x3) << 1;
        }

        alphaBlock[i] = alpha[index];
    }
}

void CCodec_ATC::GetCompressedAlphaRamp(CMP_BYTE alpha[8], CMP_DWORD compressedBlock[2])
{
    alpha[0] = (CMP_BYTE)(compressedBlock[0] & 0xff);
    alpha[1] = (CMP_BYTE)((compressedBlock[0] >> 8) & 0xff);

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