123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548 |
- //-------------------------------------------------------------------------------------
- // BC4BC5.cpp
- //
- // Block-compression (BC) functionality for BC4 and BC5 (DirectX 10 texture compression)
- //
- // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
- // ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
- // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
- // PARTICULAR PURPOSE.
- //
- // Copyright (c) Microsoft Corporation. All rights reserved.
- //
- // http://go.microsoft.com/fwlink/?LinkId=248926
- //-------------------------------------------------------------------------------------
- #include "directxtexp.h"
- #include "BC.h"
- 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.
- //------------------------------------------------------------------------------------
- // 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 (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 (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
- //-------------------------------------------------------------------------------------
- static 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 * (int8_t) ( dwMostNeg - 1 );
- if( fVal >= 0 )
- fVal += .5f;
- else
- fVal -= .5f;
- *piSNorm = (int8_t) (fVal);
- }
- //------------------------------------------------------------------------------
- static 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
- float MIN_NORM;
- float MAX_NORM = 1.0f;
- int8_t iStart, iEnd;
- size_t i;
- MIN_NORM = 0.0f;
- // Find max/min of input texels
- float fBlockMax = theTexelsU[0];
- float fBlockMin = theTexelsU[0];
- for (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 block-codec to guarantee
- // the exact code of the boundary values.
- bool bUsing4BlockCodec = ( MIN_NORM == fBlockMin || MAX_NORM == fBlockMax );
- // Using Optimize
- float fStart, fEnd;
- if (!bUsing4BlockCodec)
- {
- OptimizeAlpha<false>(&fStart, &fEnd, theTexelsU, 8);
- iStart = (uint8_t) (fStart * 255.0f);
- iEnd = (uint8_t) (fEnd * 255.0f);
- endpointU_0 = iEnd;
- endpointU_1 = iStart;
- }
- else
- {
- OptimizeAlpha<false>(&fStart, &fEnd, theTexelsU, 6);
- iStart = (uint8_t) (fStart * 255.0f);
- iEnd = (uint8_t) (fEnd * 255.0f);
- endpointU_1 = iEnd;
- endpointU_0 = iStart;
- }
- }
- static 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
- float MIN_NORM;
- float MAX_NORM = 1.0f;
- int8_t iStart, iEnd;
- size_t i;
- MIN_NORM = -1.0f;
- // Find max/min of input texels
- float fBlockMax = theTexelsU[0];
- float fBlockMin = theTexelsU[0];
- for (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 block-codec to guarantee
- // the exact code of the boundary values.
- bool bUsing4BlockCodec = ( MIN_NORM == fBlockMin || MAX_NORM == fBlockMax );
- // Using Optimize
- float fStart, fEnd;
- if (!bUsing4BlockCodec)
- {
- OptimizeAlpha<true>(&fStart, &fEnd, theTexelsU, 8);
- FloatToSNorm(fStart, &iStart);
- FloatToSNorm(fEnd, &iEnd);
- endpointU_0 = iEnd;
- endpointU_1 = iStart;
- }
- else
- {
- OptimizeAlpha<true>(&fStart, &fEnd, theTexelsU, 6);
- FloatToSNorm(fStart, &iStart);
- FloatToSNorm(fEnd, &iEnd);
- endpointU_1 = iEnd;
- endpointU_0 = iStart;
- }
- }
- //------------------------------------------------------------------------------
- static 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);
- }
- static 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);
- }
- //------------------------------------------------------------------------------
- static void FindClosestUNORM(_Inout_ BC4_UNORM* pBC, _In_reads_(NUM_PIXELS_PER_BLOCK) const float theTexelsU[])
- {
- float rGradient[8];
- int i;
- for (i = 0; i < 8; ++i)
- {
- rGradient[i] = pBC->DecodeFromIndex(i);
- }
- for (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);
- }
- }
- static void FindClosestSNORM(_Inout_ BC4_SNORM* pBC, _In_reads_(NUM_PIXELS_PER_BLOCK) const float theTexelsU[])
- {
- float rGradient[8];
- int i;
- for (i = 0; i < 8; ++i)
- {
- rGradient[i] = pBC->DecodeFromIndex(i);
- }
- for (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 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 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 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 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 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 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 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 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);
- }
- } // namespace
|