text: //-------------------------------------------------------------------------------------
  // BC4BC5.cpp
  //  
  // Block-compression (BC) functionality for BC4 and BC5 (DirectX 10 texture compression)
  //
  // Copyright (c) Microsoft Corporation. All rights reserved.
  // Licensed under the MIT License.
  //
  // http://go.microsoft.com/fwlink/?LinkId=248926
  //-------------------------------------------------------------------------------------
  
  #include "DirectXTexp.h"
  
  #include "BC.h"
  
  using namespace DirectX;
  
  //------------------------------------------------------------------------------------
  // Constants
  //------------------------------------------------------------------------------------
  
  // Because these are used in SAL annotations, they need to remain macros rather than const values
  #define BLOCK_LEN 4
  // length of each block in texel
  
  #define BLOCK_SIZE (BLOCK_LEN * BLOCK_LEN)
  // total texels in a 4x4 block.
  
  namespace
  {
      //------------------------------------------------------------------------------------
      // Structures
      //-------------------------------------------------------------------------------------
  
  #pragma warning(push)
  #pragma warning(disable : 4201)
  
      // BC4U/BC5U
      struct BC4_UNORM
      {
          float R(size_t uOffset) const
          {
              size_t uIndex = GetIndex(uOffset);
              return DecodeFromIndex(uIndex);
          }
  
          float DecodeFromIndex(size_t uIndex) const
          {
              if (uIndex == 0)
                  return red_0 / 255.0f;
              if (uIndex == 1)
                  return red_1 / 255.0f;
              float fred_0 = red_0 / 255.0f;
              float fred_1 = red_1 / 255.0f;
              if (red_0 > red_1)
              {
                  uIndex -= 1;
                  return (fred_0 * (7 - uIndex) + fred_1 * uIndex) / 7.0f;
              }
              else
              {
                  if (uIndex == 6)
                      return 0.0f;
                  if (uIndex == 7)
                      return 1.0f;
                  uIndex -= 1;
                  return (fred_0 * (5 - uIndex) + fred_1 * uIndex) / 5.0f;
              }
          }
  
          size_t GetIndex(size_t uOffset) const
          {
              return static_cast<size_t>((data >> (3 * uOffset + 16)) & 0x07);
          }
  
          void SetIndex(size_t uOffset, size_t uIndex)
          {
              data &= ~(uint64_t(0x07) << (3 * uOffset + 16));
              data |= (uint64_t(uIndex) << (3 * uOffset + 16));
          }
  
          union
          {
              struct
              {
                  uint8_t red_0;
                  uint8_t red_1;
                  uint8_t indices[6];
              };
              uint64_t data;
          };
      };
  
      // BC4S/BC5S
      struct BC4_SNORM
      {
          float R(size_t uOffset) const
          {
              size_t uIndex = GetIndex(uOffset);
              return DecodeFromIndex(uIndex);
          }
  
          float DecodeFromIndex(size_t uIndex) const
          {
              int8_t sred_0 = (red_0 == -128) ? -127 : red_0;
              int8_t sred_1 = (red_1 == -128) ? -127 : red_1;
  
              if (uIndex == 0)
                  return sred_0 / 127.0f;
              if (uIndex == 1)
                  return sred_1 / 127.0f;
              float fred_0 = sred_0 / 127.0f;
              float fred_1 = sred_1 / 127.0f;
              if (red_0 > red_1)
              {
                  uIndex -= 1;
                  return (fred_0 * (7 - uIndex) + fred_1 * uIndex) / 7.0f;
              }
              else
              {
                  if (uIndex == 6)
                      return -1.0f;
                  if (uIndex == 7)
                      return 1.0f;
                  uIndex -= 1;
                  return (fred_0 * (5 - uIndex) + fred_1 * uIndex) / 5.0f;
              }
          }
  
          size_t GetIndex(size_t uOffset) const
          {
              return static_cast<size_t>((data >> (3 * uOffset + 16)) & 0x07);
          }
  
          void SetIndex(size_t uOffset, size_t uIndex)
          {
              data &= ~(uint64_t(0x07) << (3 * uOffset + 16));
              data |= (uint64_t(uIndex) << (3 * uOffset + 16));
          }
  
          union
          {
              struct
              {
                  int8_t red_0;
                  int8_t red_1;
                  uint8_t indices[6];
              };
              uint64_t data;
          };
      };
  
  #pragma warning(pop)
  
      //-------------------------------------------------------------------------------------
      // Convert a floating point value to an 8-bit SNORM
      //-------------------------------------------------------------------------------------
      void inline FloatToSNorm(_In_ float fVal, _Out_ int8_t *piSNorm)
      {
          const uint32_t dwMostNeg = (1 << (8 * sizeof(int8_t) - 1));
  
          if (_isnan(fVal))
              fVal = 0;
          else
              if (fVal > 1)
                  fVal = 1;    // Clamp to 1
              else
                  if (fVal < -1)
                      fVal = -1;    // Clamp to -1
  
          fVal = fVal * static_cast<int8_t>(dwMostNeg - 1);
  
          if (fVal >= 0)
              fVal += .5f;
          else
              fVal -= .5f;
  
          *piSNorm = static_cast<int8_t>(fVal);
      }
  
  
      //------------------------------------------------------------------------------
      void FindEndPointsBC4U(
          _In_reads_(BLOCK_SIZE) const float theTexelsU[],
          _Out_ uint8_t &endpointU_0,
          _Out_ uint8_t &endpointU_1)
      {
          // The boundary of codec for signed/unsigned format
          const float MIN_NORM = 0.f;
          const float MAX_NORM = 1.f;
  
          // Find max/min of input texels
          float fBlockMax = theTexelsU[0];
          float fBlockMin = theTexelsU[0];
          for (size_t i = 0; i < BLOCK_SIZE; ++i)
          {
              if (theTexelsU[i] < fBlockMin)
              {
                  fBlockMin = theTexelsU[i];
              }
              else if (theTexelsU[i] > fBlockMax)
              {
                  fBlockMax = theTexelsU[i];
              }
          }
  
          //  If there are boundary values in input texels, should use 4 interpolated color values to guarantee
          //  the exact code of the boundary values.
          bool bUsing4BlockCodec = (MIN_NORM == fBlockMin || MAX_NORM == fBlockMax);
  
          // Using Optimize
          float fStart, fEnd;
  
          if (!bUsing4BlockCodec)
          {
              // 6 interpolated color values
              OptimizeAlpha<false>(&fStart, &fEnd, theTexelsU, 8);
  
              auto iStart = static_cast<uint8_t>(fStart * 255.0f);
              auto iEnd = static_cast<uint8_t>(fEnd * 255.0f);
  
              endpointU_0 = iEnd;
              endpointU_1 = iStart;
          }
          else
          {
              // 4 interpolated color values
              OptimizeAlpha<false>(&fStart, &fEnd, theTexelsU, 6);
  
              auto iStart = static_cast<uint8_t>(fStart * 255.0f);
              auto iEnd = static_cast<uint8_t>(fEnd * 255.0f);
  
              endpointU_1 = iEnd;
              endpointU_0 = iStart;
          }
      }
  
      void FindEndPointsBC4S(
          _In_reads_(BLOCK_SIZE) const float theTexelsU[],
          _Out_ int8_t &endpointU_0,
          _Out_ int8_t &endpointU_1)
      {
          //  The boundary of codec for signed/unsigned format
          const float MIN_NORM = -1.f;
          const float MAX_NORM = 1.f;
  
          // Find max/min of input texels
          float fBlockMax = theTexelsU[0];
          float fBlockMin = theTexelsU[0];
          for (size_t i = 0; i < BLOCK_SIZE; ++i)
          {
              if (theTexelsU[i] < fBlockMin)
              {
                  fBlockMin = theTexelsU[i];
              }
              else if (theTexelsU[i] > fBlockMax)
              {
                  fBlockMax = theTexelsU[i];
              }
          }
  
          //  If there are boundary values in input texels, should use 4 interpolated color values to guarantee
          //  the exact code of the boundary values.
          bool bUsing4BlockCodec = (MIN_NORM == fBlockMin || MAX_NORM == fBlockMax);
  
          // Using Optimize
          float fStart, fEnd;
  
          if (!bUsing4BlockCodec)
          {
              // 6 interpolated color values
              OptimizeAlpha<true>(&fStart, &fEnd, theTexelsU, 8);
  
              int8_t iStart, iEnd;
              FloatToSNorm(fStart, &iStart);
              FloatToSNorm(fEnd, &iEnd);
  
              endpointU_0 = iEnd;
              endpointU_1 = iStart;
          }
          else
          {
              // 4 interpolated color values
              OptimizeAlpha<true>(&fStart, &fEnd, theTexelsU, 6);
  
              int8_t iStart, iEnd;
              FloatToSNorm(fStart, &iStart);
              FloatToSNorm(fEnd, &iEnd);
  
              endpointU_1 = iEnd;
              endpointU_0 = iStart;
          }
      }
  
  
      //------------------------------------------------------------------------------
      inline void FindEndPointsBC5U(
          _In_reads_(BLOCK_SIZE) const float theTexelsU[],
          _In_reads_(BLOCK_SIZE) const float theTexelsV[],
          _Out_ uint8_t &endpointU_0,
          _Out_ uint8_t &endpointU_1,
          _Out_ uint8_t &endpointV_0,
          _Out_ uint8_t &endpointV_1)
      {
          //Encoding the U and V channel by BC4 codec separately.
          FindEndPointsBC4U(theTexelsU, endpointU_0, endpointU_1);
          FindEndPointsBC4U(theTexelsV, endpointV_0, endpointV_1);
      }
  
      inline void FindEndPointsBC5S(
          _In_reads_(BLOCK_SIZE) const float theTexelsU[],
          _In_reads_(BLOCK_SIZE) const float theTexelsV[],
          _Out_ int8_t &endpointU_0,
          _Out_ int8_t &endpointU_1,
          _Out_ int8_t &endpointV_0,
          _Out_ int8_t &endpointV_1)
      {
          //Encoding the U and V channel by BC4 codec separately.
          FindEndPointsBC4S(theTexelsU, endpointU_0, endpointU_1);
          FindEndPointsBC4S(theTexelsV, endpointV_0, endpointV_1);
      }
  
  
      //------------------------------------------------------------------------------
      void FindClosestUNORM(
          _Inout_ BC4_UNORM* pBC,
          _In_reads_(NUM_PIXELS_PER_BLOCK) const float theTexelsU[])
      {
          float rGradient[8];
          for (size_t i = 0; i < 8; ++i)
          {
              rGradient[i] = pBC->DecodeFromIndex(i);
          }
  
          for (size_t i = 0; i < NUM_PIXELS_PER_BLOCK; ++i)
          {
              size_t uBestIndex = 0;
              float fBestDelta = 100000;
              for (size_t uIndex = 0; uIndex < 8; uIndex++)
              {
                  float fCurrentDelta = fabsf(rGradient[uIndex] - theTexelsU[i]);
                  if (fCurrentDelta < fBestDelta)
                  {
                      uBestIndex = uIndex;
                      fBestDelta = fCurrentDelta;
                  }
              }
              pBC->SetIndex(i, uBestIndex);
          }
      }
  
      void FindClosestSNORM(
          _Inout_ BC4_SNORM* pBC,
          _In_reads_(NUM_PIXELS_PER_BLOCK) const float theTexelsU[])
      {
          float rGradient[8];
          for (size_t i = 0; i < 8; ++i)
          {
              rGradient[i] = pBC->DecodeFromIndex(i);
          }
  
          for (size_t i = 0; i < NUM_PIXELS_PER_BLOCK; ++i)
          {
              size_t uBestIndex = 0;
              float fBestDelta = 100000;
              for (size_t uIndex = 0; uIndex < 8; uIndex++)
              {
                  float fCurrentDelta = fabsf(rGradient[uIndex] - theTexelsU[i]);
                  if (fCurrentDelta < fBestDelta)
                  {
                      uBestIndex = uIndex;
                      fBestDelta = fCurrentDelta;
                  }
              }
              pBC->SetIndex(i, uBestIndex);
          }
      }
  }
  
  
  //=====================================================================================
  // Entry points
  //=====================================================================================
  
  //-------------------------------------------------------------------------------------
  // BC4 Compression
  //-------------------------------------------------------------------------------------
  _Use_decl_annotations_
  void DirectX::D3DXDecodeBC4U(XMVECTOR *pColor, const uint8_t *pBC)
  {
      assert(pColor && pBC);
      static_assert(sizeof(BC4_UNORM) == 8, "BC4_UNORM should be 8 bytes");
  
      auto pBC4 = reinterpret_cast<const BC4_UNORM*>(pBC);
  
      for (size_t i = 0; i < NUM_PIXELS_PER_BLOCK; ++i)
      {
  #pragma prefast(suppress:22103, "writing blocks in two halves confuses tool")
          pColor[i] = XMVectorSet(pBC4->R(i), 0, 0, 1.0f);
      }
  }
  
  _Use_decl_annotations_
  void DirectX::D3DXDecodeBC4S(XMVECTOR *pColor, const uint8_t *pBC)
  {
      assert(pColor && pBC);
      static_assert(sizeof(BC4_SNORM) == 8, "BC4_SNORM should be 8 bytes");
  
      auto pBC4 = reinterpret_cast<const BC4_SNORM*>(pBC);
  
      for (size_t i = 0; i < NUM_PIXELS_PER_BLOCK; ++i)
      {
  #pragma prefast(suppress:22103, "writing blocks in two halves confuses tool")
          pColor[i] = XMVectorSet(pBC4->R(i), 0, 0, 1.0f);
      }
  }
  
  _Use_decl_annotations_
  void DirectX::D3DXEncodeBC4U(uint8_t *pBC, const XMVECTOR *pColor, DWORD flags)
  {
      UNREFERENCED_PARAMETER(flags);
  
      assert(pBC && pColor);
      static_assert(sizeof(BC4_UNORM) == 8, "BC4_UNORM should be 8 bytes");
  
      memset(pBC, 0, sizeof(BC4_UNORM));
      auto pBC4 = reinterpret_cast<BC4_UNORM*>(pBC);
      float theTexelsU[NUM_PIXELS_PER_BLOCK];
  
      for (size_t i = 0; i < NUM_PIXELS_PER_BLOCK; ++i)
      {
          theTexelsU[i] = XMVectorGetX(pColor[i]);
      }
  
      FindEndPointsBC4U(theTexelsU, pBC4->red_0, pBC4->red_1);
      FindClosestUNORM(pBC4, theTexelsU);
  }
  
  _Use_decl_annotations_
  void DirectX::D3DXEncodeBC4S(uint8_t *pBC, const XMVECTOR *pColor, DWORD flags)
  {
      UNREFERENCED_PARAMETER(flags);
  
      assert(pBC && pColor);
      static_assert(sizeof(BC4_SNORM) == 8, "BC4_SNORM should be 8 bytes");
  
      memset(pBC, 0, sizeof(BC4_UNORM));
      auto pBC4 = reinterpret_cast<BC4_SNORM*>(pBC);
      float theTexelsU[NUM_PIXELS_PER_BLOCK];
  
      for (size_t i = 0; i < NUM_PIXELS_PER_BLOCK; ++i)
      {
          theTexelsU[i] = XMVectorGetX(pColor[i]);
      }
  
      FindEndPointsBC4S(theTexelsU, pBC4->red_0, pBC4->red_1);
      FindClosestSNORM(pBC4, theTexelsU);
  }
  
  
  //-------------------------------------------------------------------------------------
  // BC5 Compression
  //-------------------------------------------------------------------------------------
  _Use_decl_annotations_
  void DirectX::D3DXDecodeBC5U(XMVECTOR *pColor, const uint8_t *pBC)
  {
      assert(pColor && pBC);
      static_assert(sizeof(BC4_UNORM) == 8, "BC4_UNORM should be 8 bytes");
  
      auto pBCR = reinterpret_cast<const BC4_UNORM*>(pBC);
      auto pBCG = reinterpret_cast<const BC4_UNORM*>(pBC + sizeof(BC4_UNORM));
  
      for (size_t i = 0; i < NUM_PIXELS_PER_BLOCK; ++i)
      {
  #pragma prefast(suppress:22103, "writing blocks in two halves confuses tool")
          pColor[i] = XMVectorSet(pBCR->R(i), pBCG->R(i), 0, 1.0f);
      }
  }
  
  _Use_decl_annotations_
  void DirectX::D3DXDecodeBC5S(XMVECTOR *pColor, const uint8_t *pBC)
  {
      assert(pColor && pBC);
      static_assert(sizeof(BC4_SNORM) == 8, "BC4_SNORM should be 8 bytes");
  
      auto pBCR = reinterpret_cast<const BC4_SNORM*>(pBC);
      auto pBCG = reinterpret_cast<const BC4_SNORM*>(pBC + sizeof(BC4_SNORM));
  
      for (size_t i = 0; i < NUM_PIXELS_PER_BLOCK; ++i)
      {
  #pragma prefast(suppress:22103, "writing blocks in two halves confuses tool")
          pColor[i] = XMVectorSet(pBCR->R(i), pBCG->R(i), 0, 1.0f);
      }
  }
  
  _Use_decl_annotations_
  void DirectX::D3DXEncodeBC5U(uint8_t *pBC, const XMVECTOR *pColor, DWORD flags)
  {
      UNREFERENCED_PARAMETER(flags);
  
      assert(pBC && pColor);
      static_assert(sizeof(BC4_UNORM) == 8, "BC4_UNORM should be 8 bytes");
  
      memset(pBC, 0, sizeof(BC4_UNORM) * 2);
      auto pBCR = reinterpret_cast<BC4_UNORM*>(pBC);
      auto pBCG = reinterpret_cast<BC4_UNORM*>(pBC + sizeof(BC4_UNORM));
      float theTexelsU[NUM_PIXELS_PER_BLOCK];
      float theTexelsV[NUM_PIXELS_PER_BLOCK];
  
      for (size_t i = 0; i < NUM_PIXELS_PER_BLOCK; ++i)
      {
          XMFLOAT4A clr;
          XMStoreFloat4A(&clr, pColor[i]);
          theTexelsU[i] = clr.x;
          theTexelsV[i] = clr.y;
      }
  
      FindEndPointsBC5U(
          theTexelsU,
          theTexelsV,
          pBCR->red_0,
          pBCR->red_1,
          pBCG->red_0,
          pBCG->red_1);
  
      FindClosestUNORM(pBCR, theTexelsU);
      FindClosestUNORM(pBCG, theTexelsV);
  }
  
  _Use_decl_annotations_
  void DirectX::D3DXEncodeBC5S(uint8_t *pBC, const XMVECTOR *pColor, DWORD flags)
  {
      UNREFERENCED_PARAMETER(flags);
  
      assert(pBC && pColor);
      static_assert(sizeof(BC4_SNORM) == 8, "BC4_SNORM should be 8 bytes");
  
      memset(pBC, 0, sizeof(BC4_UNORM) * 2);
      auto pBCR = reinterpret_cast<BC4_SNORM*>(pBC);
      auto pBCG = reinterpret_cast<BC4_SNORM*>(pBC + sizeof(BC4_SNORM));
      float theTexelsU[NUM_PIXELS_PER_BLOCK];
      float theTexelsV[NUM_PIXELS_PER_BLOCK];
  
      for (size_t i = 0; i < NUM_PIXELS_PER_BLOCK; ++i)
      {
          XMFLOAT4A clr;
          XMStoreFloat4A(&clr, pColor[i]);
          theTexelsU[i] = clr.x;
          theTexelsV[i] = clr.y;
      }
  
      FindEndPointsBC5S(
          theTexelsU,
          theTexelsV,
          pBCR->red_0,
          pBCR->red_1,
          pBCG->red_0,
          pBCG->red_1);
  
      FindClosestSNORM(pBCR, theTexelsU);
      FindClosestSNORM(pBCG, theTexelsV);
  }
  