/* * Copyright 2015 The Etc2Comp Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* EtcBlock4x4Encoding_RGB8A1.cpp contains: Block4x4Encoding_RGB8A1 Block4x4Encoding_RGB8A1_Opaque Block4x4Encoding_RGB8A1_Transparent These encoders are used when targetting file format RGB8A1. Block4x4Encoding_RGB8A1_Opaque is used when all pixels in the 4x4 block are opaque Block4x4Encoding_RGB8A1_Transparent is used when all pixels in the 4x4 block are transparent Block4x4Encoding_RGB8A1 is used when there is a mixture of alphas in the 4x4 block */ #include "EtcConfig.h" #include "EtcBlock4x4Encoding_RGB8A1.h" #include "EtcBlock4x4.h" #include "EtcBlock4x4EncodingBits.h" #include "EtcBlock4x4Encoding_RGB8.h" #include #include #include namespace Etc { // #################################################################################################### // Block4x4Encoding_RGB8A1 // #################################################################################################### float Block4x4Encoding_RGB8A1::s_aafCwOpaqueUnsetTable[CW_RANGES][SELECTORS] = { { 0.0f / 255.0f, 8.0f / 255.0f, 0.0f / 255.0f, -8.0f / 255.0f }, { 0.0f / 255.0f, 17.0f / 255.0f, 0.0f / 255.0f, -17.0f / 255.0f }, { 0.0f / 255.0f, 29.0f / 255.0f, 0.0f / 255.0f, -29.0f / 255.0f }, { 0.0f / 255.0f, 42.0f / 255.0f, 0.0f / 255.0f, -42.0f / 255.0f }, { 0.0f / 255.0f, 60.0f / 255.0f, 0.0f / 255.0f, -60.0f / 255.0f }, { 0.0f / 255.0f, 80.0f / 255.0f, 0.0f / 255.0f, -80.0f / 255.0f }, { 0.0f / 255.0f, 106.0f / 255.0f, 0.0f / 255.0f, -106.0f / 255.0f }, { 0.0f / 255.0f, 183.0f / 255.0f, 0.0f / 255.0f, -183.0f / 255.0f } }; // ---------------------------------------------------------------------------------------------------- // Block4x4Encoding_RGB8A1::Block4x4Encoding_RGB8A1(void) { m_pencodingbitsRGB8 = nullptr; m_boolOpaque = false; m_boolTransparent = false; m_boolPunchThroughPixels = true; } Block4x4Encoding_RGB8A1::~Block4x4Encoding_RGB8A1(void) {} // ---------------------------------------------------------------------------------------------------- // initialization prior to encoding // a_pblockParent points to the block associated with this encoding // a_errormetric is used to choose the best encoding // a_pafrgbaSource points to a 4x4 block subset of the source image // a_paucEncodingBits points to the final encoding bits // void Block4x4Encoding_RGB8A1::InitFromSource(Block4x4 *a_pblockParent, ColorFloatRGBA *a_pafrgbaSource, unsigned char *a_paucEncodingBits, ErrorMetric a_errormetric) { Block4x4Encoding_RGB8::InitFromSource(a_pblockParent, a_pafrgbaSource, a_paucEncodingBits, a_errormetric); m_boolOpaque = a_pblockParent->GetSourceAlphaMix() == Block4x4::SourceAlphaMix::OPAQUE; m_boolTransparent = a_pblockParent->GetSourceAlphaMix() == Block4x4::SourceAlphaMix::TRANSPARENT; m_boolPunchThroughPixels = a_pblockParent->HasPunchThroughPixels(); for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) { if (m_pafrgbaSource[uiPixel].fA >= 0.5f) { m_afDecodedAlphas[uiPixel] = 1.0f; } else { m_afDecodedAlphas[uiPixel] = 0.0f; } } } // ---------------------------------------------------------------------------------------------------- // initialization from the encoding bits of a previous encoding // a_pblockParent points to the block associated with this encoding // a_errormetric is used to choose the best encoding // a_pafrgbaSource points to a 4x4 block subset of the source image // a_paucEncodingBits points to the final encoding bits of a previous encoding // void Block4x4Encoding_RGB8A1::InitFromEncodingBits(Block4x4 *a_pblockParent, unsigned char *a_paucEncodingBits, ColorFloatRGBA *a_pafrgbaSource, ErrorMetric a_errormetric) { InitFromEncodingBits_ETC1(a_pblockParent, a_paucEncodingBits, a_pafrgbaSource, a_errormetric); m_pencodingbitsRGB8 = (Block4x4EncodingBits_RGB8 *)a_paucEncodingBits; // detect if there is a T, H or Planar mode present int iRed1 = m_pencodingbitsRGB8->differential.red1; int iDRed2 = m_pencodingbitsRGB8->differential.dred2; int iRed2 = iRed1 + iDRed2; int iGreen1 = m_pencodingbitsRGB8->differential.green1; int iDGreen2 = m_pencodingbitsRGB8->differential.dgreen2; int iGreen2 = iGreen1 + iDGreen2; int iBlue1 = m_pencodingbitsRGB8->differential.blue1; int iDBlue2 = m_pencodingbitsRGB8->differential.dblue2; int iBlue2 = iBlue1 + iDBlue2; if (iRed2 < 0 || iRed2 > 31) { InitFromEncodingBits_T(); } else if (iGreen2 < 0 || iGreen2 > 31) { InitFromEncodingBits_H(); } else if (iBlue2 < 0 || iBlue2 > 31) { Block4x4Encoding_RGB8::InitFromEncodingBits_Planar(); } } // ---------------------------------------------------------------------------------------------------- // initialization from the encoding bits of a previous encoding assuming the encoding is an ETC1 mode. // if it isn't an ETC1 mode, this will be overwritten later // void Block4x4Encoding_RGB8A1::InitFromEncodingBits_ETC1(Block4x4 *a_pblockParent, unsigned char *a_paucEncodingBits, ColorFloatRGBA *a_pafrgbaSource, ErrorMetric a_errormetric) { Block4x4Encoding::Init(a_pblockParent, a_pafrgbaSource, a_errormetric); m_pencodingbitsRGB8 = (Block4x4EncodingBits_RGB8 *)a_paucEncodingBits; m_mode = MODE_ETC1; m_boolDiff = true; m_boolFlip = m_pencodingbitsRGB8->differential.flip; m_boolOpaque = m_pencodingbitsRGB8->differential.diff; int iR2 = m_pencodingbitsRGB8->differential.red1 + m_pencodingbitsRGB8->differential.dred2; if (iR2 < 0) { iR2 = 0; } else if (iR2 > 31) { iR2 = 31; } int iG2 = m_pencodingbitsRGB8->differential.green1 + m_pencodingbitsRGB8->differential.dgreen2; if (iG2 < 0) { iG2 = 0; } else if (iG2 > 31) { iG2 = 31; } int iB2 = m_pencodingbitsRGB8->differential.blue1 + m_pencodingbitsRGB8->differential.dblue2; if (iB2 < 0) { iB2 = 0; } else if (iB2 > 31) { iB2 = 31; } m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB5(m_pencodingbitsRGB8->differential.red1, m_pencodingbitsRGB8->differential.green1, m_pencodingbitsRGB8->differential.blue1); m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB5((unsigned char)iR2, (unsigned char)iG2, (unsigned char)iB2); m_uiCW1 = m_pencodingbitsRGB8->differential.cw1; m_uiCW2 = m_pencodingbitsRGB8->differential.cw2; Block4x4Encoding_ETC1::InitFromEncodingBits_Selectors(); Decode_ETC1(); CalcBlockError(); } // ---------------------------------------------------------------------------------------------------- // initialization from the encoding bits of a previous encoding if T mode is detected // void Block4x4Encoding_RGB8A1::InitFromEncodingBits_T(void) { m_mode = MODE_T; unsigned char ucRed1 = (unsigned char)((m_pencodingbitsRGB8->t.red1a << 2) + m_pencodingbitsRGB8->t.red1b); unsigned char ucGreen1 = m_pencodingbitsRGB8->t.green1; unsigned char ucBlue1 = m_pencodingbitsRGB8->t.blue1; unsigned char ucRed2 = m_pencodingbitsRGB8->t.red2; unsigned char ucGreen2 = m_pencodingbitsRGB8->t.green2; unsigned char ucBlue2 = m_pencodingbitsRGB8->t.blue2; m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4(ucRed1, ucGreen1, ucBlue1); m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4(ucRed2, ucGreen2, ucBlue2); m_uiCW1 = (m_pencodingbitsRGB8->t.da << 1) + m_pencodingbitsRGB8->t.db; Block4x4Encoding_ETC1::InitFromEncodingBits_Selectors(); DecodePixels_T(); CalcBlockError(); } // ---------------------------------------------------------------------------------------------------- // initialization from the encoding bits of a previous encoding if H mode is detected // void Block4x4Encoding_RGB8A1::InitFromEncodingBits_H(void) { m_mode = MODE_H; unsigned char ucRed1 = m_pencodingbitsRGB8->h.red1; unsigned char ucGreen1 = (unsigned char)((m_pencodingbitsRGB8->h.green1a << 1) + m_pencodingbitsRGB8->h.green1b); unsigned char ucBlue1 = (unsigned char)((m_pencodingbitsRGB8->h.blue1a << 3) + (m_pencodingbitsRGB8->h.blue1b << 1) + m_pencodingbitsRGB8->h.blue1c); unsigned char ucRed2 = m_pencodingbitsRGB8->h.red2; unsigned char ucGreen2 = (unsigned char)((m_pencodingbitsRGB8->h.green2a << 1) + m_pencodingbitsRGB8->h.green2b); unsigned char ucBlue2 = m_pencodingbitsRGB8->h.blue2; m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4(ucRed1, ucGreen1, ucBlue1); m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4(ucRed2, ucGreen2, ucBlue2); // used to determine the LSB of the CW unsigned int uiRGB1 = (unsigned int)(((int)ucRed1 << 16) + ((int)ucGreen1 << 8) + (int)ucBlue1); unsigned int uiRGB2 = (unsigned int)(((int)ucRed2 << 16) + ((int)ucGreen2 << 8) + (int)ucBlue2); m_uiCW1 = (m_pencodingbitsRGB8->h.da << 2) + (m_pencodingbitsRGB8->h.db << 1); if (uiRGB1 >= uiRGB2) { m_uiCW1++; } Block4x4Encoding_ETC1::InitFromEncodingBits_Selectors(); DecodePixels_H(); CalcBlockError(); } // ---------------------------------------------------------------------------------------------------- // for ETC1 modes, set the decoded colors and decoded alpha based on the encoding state // void Block4x4Encoding_RGB8A1::Decode_ETC1(void) { const unsigned int *pauiPixelOrder = m_boolFlip ? s_auiPixelOrderFlip1 : s_auiPixelOrderFlip0; for (unsigned int uiPixelOrder = 0; uiPixelOrder < PIXELS; uiPixelOrder++) { ColorFloatRGBA *pfrgbaCenter = uiPixelOrder < 8 ? &m_frgbaColor1 : &m_frgbaColor2; unsigned int uiCW = uiPixelOrder < 8 ? m_uiCW1 : m_uiCW2; unsigned int uiPixel = pauiPixelOrder[uiPixelOrder]; float fDelta; if (m_boolOpaque) fDelta = Block4x4Encoding_ETC1::s_aafCwTable[uiCW][m_auiSelectors[uiPixel]]; else fDelta = s_aafCwOpaqueUnsetTable[uiCW][m_auiSelectors[uiPixel]]; if (m_boolOpaque == false && m_auiSelectors[uiPixel] == TRANSPARENT_SELECTOR) { m_afrgbaDecodedColors[uiPixel] = ColorFloatRGBA(); m_afDecodedAlphas[uiPixel] = 0.0f; } else { m_afrgbaDecodedColors[uiPixel] = (*pfrgbaCenter + fDelta).ClampRGB(); m_afDecodedAlphas[uiPixel] = 1.0f; } } } // ---------------------------------------------------------------------------------------------------- // for T mode, set the decoded colors and decoded alpha based on the encoding state // void Block4x4Encoding_RGB8A1::DecodePixels_T(void) { float fDistance = s_afTHDistanceTable[m_uiCW1]; ColorFloatRGBA frgbaDistance(fDistance, fDistance, fDistance, 0.0f); for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) { switch (m_auiSelectors[uiPixel]) { case 0: m_afrgbaDecodedColors[uiPixel] = m_frgbaColor1; m_afDecodedAlphas[uiPixel] = 1.0f; break; case 1: m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor2 + frgbaDistance).ClampRGB(); m_afDecodedAlphas[uiPixel] = 1.0f; break; case 2: if (m_boolOpaque == false) { m_afrgbaDecodedColors[uiPixel] = ColorFloatRGBA(); m_afDecodedAlphas[uiPixel] = 0.0f; } else { m_afrgbaDecodedColors[uiPixel] = m_frgbaColor2; m_afDecodedAlphas[uiPixel] = 1.0f; } break; case 3: m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor2 - frgbaDistance).ClampRGB(); m_afDecodedAlphas[uiPixel] = 1.0f; break; } } } // ---------------------------------------------------------------------------------------------------- // for H mode, set the decoded colors and decoded alpha based on the encoding state // void Block4x4Encoding_RGB8A1::DecodePixels_H(void) { float fDistance = s_afTHDistanceTable[m_uiCW1]; ColorFloatRGBA frgbaDistance(fDistance, fDistance, fDistance, 0.0f); for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) { switch (m_auiSelectors[uiPixel]) { case 0: m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor1 + frgbaDistance).ClampRGB(); m_afDecodedAlphas[uiPixel] = 1.0f; break; case 1: m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor1 - frgbaDistance).ClampRGB(); m_afDecodedAlphas[uiPixel] = 1.0f; break; case 2: if (m_boolOpaque == false) { m_afrgbaDecodedColors[uiPixel] = ColorFloatRGBA(); m_afDecodedAlphas[uiPixel] = 0.0f; } else { m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor2 + frgbaDistance).ClampRGB(); m_afDecodedAlphas[uiPixel] = 1.0f; } break; case 3: m_afrgbaDecodedColors[uiPixel] = (m_frgbaColor2 - frgbaDistance).ClampRGB(); m_afDecodedAlphas[uiPixel] = 1.0f; break; } } } // ---------------------------------------------------------------------------------------------------- // perform a single encoding iteration // replace the encoding if a better encoding was found // subsequent iterations generally take longer for each iteration // set m_boolDone if encoding is perfect or encoding is finished based on a_fEffort // // RGB8A1 can't use individual mode // RGB8A1 with transparent pixels can't use planar mode // void Block4x4Encoding_RGB8A1::PerformIteration(float a_fEffort) { assert(!m_boolOpaque); assert(!m_boolTransparent); assert(!m_boolDone); switch (m_uiEncodingIterations) { case 0: PerformFirstIteration(); break; case 1: TryDifferential(m_boolMostLikelyFlip, 1, 0, 0); break; case 2: TryDifferential(!m_boolMostLikelyFlip, 1, 0, 0); if (a_fEffort <= 39.5f) { m_boolDone = true; } break; case 3: Block4x4Encoding_RGB8::CalculateBaseColorsForTAndH(); TryT(1); TryH(1); if (a_fEffort <= 49.5f) { m_boolDone = true; } break; case 4: TryDegenerates1(); if (a_fEffort <= 59.5f) { m_boolDone = true; } break; case 5: TryDegenerates2(); if (a_fEffort <= 69.5f) { m_boolDone = true; } break; case 6: TryDegenerates3(); if (a_fEffort <= 79.5f) { m_boolDone = true; } break; case 7: TryDegenerates4(); m_boolDone = true; break; default: assert(0); break; } m_uiEncodingIterations++; SetDoneIfPerfect(); } // ---------------------------------------------------------------------------------------------------- // find best initial encoding to ensure block has a valid encoding // void Block4x4Encoding_RGB8A1::PerformFirstIteration(void) { Block4x4Encoding_ETC1::CalculateMostLikelyFlip(); m_fError = FLT_MAX; TryDifferential(m_boolMostLikelyFlip, 0, 0, 0); SetDoneIfPerfect(); if (m_boolDone) { return; } TryDifferential(!m_boolMostLikelyFlip, 0, 0, 0); SetDoneIfPerfect(); } // ---------------------------------------------------------------------------------------------------- // mostly copied from ETC1 // differences: // Block4x4Encoding_RGB8A1 encodingTry = *this; // void Block4x4Encoding_RGB8A1::TryDifferential(bool a_boolFlip, unsigned int a_uiRadius, int a_iGrayOffset1, int a_iGrayOffset2) { ColorFloatRGBA frgbaColor1; ColorFloatRGBA frgbaColor2; const unsigned int *pauiPixelMapping1; const unsigned int *pauiPixelMapping2; if (a_boolFlip) { frgbaColor1 = m_frgbaSourceAverageTop; frgbaColor2 = m_frgbaSourceAverageBottom; pauiPixelMapping1 = s_auiTopPixelMapping; pauiPixelMapping2 = s_auiBottomPixelMapping; } else { frgbaColor1 = m_frgbaSourceAverageLeft; frgbaColor2 = m_frgbaSourceAverageRight; pauiPixelMapping1 = s_auiLeftPixelMapping; pauiPixelMapping2 = s_auiRightPixelMapping; } DifferentialTrys trys(frgbaColor1, frgbaColor2, pauiPixelMapping1, pauiPixelMapping2, a_uiRadius, a_iGrayOffset1, a_iGrayOffset2); Block4x4Encoding_RGB8A1 encodingTry = *this; encodingTry.m_boolFlip = a_boolFlip; encodingTry.TryDifferentialHalf(&trys.m_half1); encodingTry.TryDifferentialHalf(&trys.m_half2); // find best halves that are within differential range DifferentialTrys::Try *ptryBest1 = nullptr; DifferentialTrys::Try *ptryBest2 = nullptr; encodingTry.m_fError = FLT_MAX; // see if the best of each half are in differential range int iDRed = trys.m_half2.m_ptryBest->m_iRed - trys.m_half1.m_ptryBest->m_iRed; int iDGreen = trys.m_half2.m_ptryBest->m_iGreen - trys.m_half1.m_ptryBest->m_iGreen; int iDBlue = trys.m_half2.m_ptryBest->m_iBlue - trys.m_half1.m_ptryBest->m_iBlue; if (iDRed >= -4 && iDRed <= 3 && iDGreen >= -4 && iDGreen <= 3 && iDBlue >= -4 && iDBlue <= 3) { ptryBest1 = trys.m_half1.m_ptryBest; ptryBest2 = trys.m_half2.m_ptryBest; encodingTry.m_fError = trys.m_half1.m_ptryBest->m_fError + trys.m_half2.m_ptryBest->m_fError; } else { // else, find the next best halves that are in differential range for (DifferentialTrys::Try *ptry1 = &trys.m_half1.m_atry[0]; ptry1 < &trys.m_half1.m_atry[trys.m_half1.m_uiTrys]; ptry1++) { for (DifferentialTrys::Try *ptry2 = &trys.m_half2.m_atry[0]; ptry2 < &trys.m_half2.m_atry[trys.m_half2.m_uiTrys]; ptry2++) { iDRed = ptry2->m_iRed - ptry1->m_iRed; bool boolValidRedDelta = iDRed <= 3 && iDRed >= -4; iDGreen = ptry2->m_iGreen - ptry1->m_iGreen; bool boolValidGreenDelta = iDGreen <= 3 && iDGreen >= -4; iDBlue = ptry2->m_iBlue - ptry1->m_iBlue; bool boolValidBlueDelta = iDBlue <= 3 && iDBlue >= -4; if (boolValidRedDelta && boolValidGreenDelta && boolValidBlueDelta) { float fError = ptry1->m_fError + ptry2->m_fError; if (fError < encodingTry.m_fError) { encodingTry.m_fError = fError; ptryBest1 = ptry1; ptryBest2 = ptry2; } } } } assert(encodingTry.m_fError < FLT_MAX); assert(ptryBest1 != nullptr); assert(ptryBest2 != nullptr); } if (encodingTry.m_fError < m_fError) { m_mode = MODE_ETC1; m_boolDiff = true; m_boolFlip = encodingTry.m_boolFlip; m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB5((unsigned char)ptryBest1->m_iRed, (unsigned char)ptryBest1->m_iGreen, (unsigned char)ptryBest1->m_iBlue); m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB5((unsigned char)ptryBest2->m_iRed, (unsigned char)ptryBest2->m_iGreen, (unsigned char)ptryBest2->m_iBlue); m_uiCW1 = ptryBest1->m_uiCW; m_uiCW2 = ptryBest2->m_uiCW; m_fError = 0.0f; for (unsigned int uiPixelOrder = 0; uiPixelOrder < PIXELS / 2; uiPixelOrder++) { unsigned int uiPixel1 = pauiPixelMapping1[uiPixelOrder]; unsigned int uiPixel2 = pauiPixelMapping2[uiPixelOrder]; unsigned int uiSelector1 = ptryBest1->m_auiSelectors[uiPixelOrder]; unsigned int uiSelector2 = ptryBest2->m_auiSelectors[uiPixelOrder]; m_auiSelectors[uiPixel1] = uiSelector1; m_auiSelectors[uiPixel2] = ptryBest2->m_auiSelectors[uiPixelOrder]; if (uiSelector1 == TRANSPARENT_SELECTOR) { m_afrgbaDecodedColors[uiPixel1] = ColorFloatRGBA(); m_afDecodedAlphas[uiPixel1] = 0.0f; } else { float fDeltaRGB1 = s_aafCwOpaqueUnsetTable[m_uiCW1][uiSelector1]; m_afrgbaDecodedColors[uiPixel1] = (m_frgbaColor1 + fDeltaRGB1).ClampRGB(); m_afDecodedAlphas[uiPixel1] = 1.0f; } if (uiSelector2 == TRANSPARENT_SELECTOR) { m_afrgbaDecodedColors[uiPixel2] = ColorFloatRGBA(); m_afDecodedAlphas[uiPixel2] = 0.0f; } else { float fDeltaRGB2 = s_aafCwOpaqueUnsetTable[m_uiCW2][uiSelector2]; m_afrgbaDecodedColors[uiPixel2] = (m_frgbaColor2 + fDeltaRGB2).ClampRGB(); m_afDecodedAlphas[uiPixel2] = 1.0f; } float fDeltaA1 = m_afDecodedAlphas[uiPixel1] - m_pafrgbaSource[uiPixel1].fA; m_fError += fDeltaA1 * fDeltaA1; float fDeltaA2 = m_afDecodedAlphas[uiPixel2] - m_pafrgbaSource[uiPixel2].fA; m_fError += fDeltaA2 * fDeltaA2; } m_fError1 = ptryBest1->m_fError; m_fError2 = ptryBest2->m_fError; m_boolSeverelyBentDifferentialColors = trys.m_boolSeverelyBentColors; m_fError = m_fError1 + m_fError2; // sanity check { int iRed1 = m_frgbaColor1.IntRed(31.0f); int iGreen1 = m_frgbaColor1.IntGreen(31.0f); int iBlue1 = m_frgbaColor1.IntBlue(31.0f); int iRed2 = m_frgbaColor2.IntRed(31.0f); int iGreen2 = m_frgbaColor2.IntGreen(31.0f); int iBlue2 = m_frgbaColor2.IntBlue(31.0f); iDRed = iRed2 - iRed1; iDGreen = iGreen2 - iGreen1; iDBlue = iBlue2 - iBlue1; assert(iDRed >= -4 && iDRed < 4); assert(iDGreen >= -4 && iDGreen < 4); assert(iDBlue >= -4 && iDBlue < 4); } } } // ---------------------------------------------------------------------------------------------------- // mostly copied from ETC1 // differences: // uses s_aafCwOpaqueUnsetTable // color for selector set to 0,0,0,0 // void Block4x4Encoding_RGB8A1::TryDifferentialHalf(DifferentialTrys::Half *a_phalf) { a_phalf->m_ptryBest = nullptr; float fBestTryError = FLT_MAX; a_phalf->m_uiTrys = 0; for (int iRed = a_phalf->m_iRed - (int)a_phalf->m_uiRadius; iRed <= a_phalf->m_iRed + (int)a_phalf->m_uiRadius; iRed++) { assert(iRed >= 0 && iRed <= 31); for (int iGreen = a_phalf->m_iGreen - (int)a_phalf->m_uiRadius; iGreen <= a_phalf->m_iGreen + (int)a_phalf->m_uiRadius; iGreen++) { assert(iGreen >= 0 && iGreen <= 31); for (int iBlue = a_phalf->m_iBlue - (int)a_phalf->m_uiRadius; iBlue <= a_phalf->m_iBlue + (int)a_phalf->m_uiRadius; iBlue++) { assert(iBlue >= 0 && iBlue <= 31); DifferentialTrys::Try *ptry = &a_phalf->m_atry[a_phalf->m_uiTrys]; assert(ptry < &a_phalf->m_atry[DifferentialTrys::Half::MAX_TRYS]); ptry->m_iRed = iRed; ptry->m_iGreen = iGreen; ptry->m_iBlue = iBlue; ptry->m_fError = FLT_MAX; ColorFloatRGBA frgbaColor = ColorFloatRGBA::ConvertFromRGB5((unsigned char)iRed, (unsigned char)iGreen, (unsigned char)iBlue); // try each CW for (unsigned int uiCW = 0; uiCW < CW_RANGES; uiCW++) { unsigned int auiPixelSelectors[PIXELS / 2]; ColorFloatRGBA afrgbaDecodedColors[PIXELS / 2]; float afPixelErrors[PIXELS / 2] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX }; // pre-compute decoded pixels for each selector ColorFloatRGBA afrgbaSelectors[SELECTORS]; assert(SELECTORS == 4); afrgbaSelectors[0] = (frgbaColor + s_aafCwOpaqueUnsetTable[uiCW][0]).ClampRGB(); afrgbaSelectors[1] = (frgbaColor + s_aafCwOpaqueUnsetTable[uiCW][1]).ClampRGB(); afrgbaSelectors[2] = ColorFloatRGBA(); afrgbaSelectors[3] = (frgbaColor + s_aafCwOpaqueUnsetTable[uiCW][3]).ClampRGB(); for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++) { ColorFloatRGBA *pfrgbaSourcePixel = &m_pafrgbaSource[a_phalf->m_pauiPixelMapping[uiPixel]]; ColorFloatRGBA frgbaDecodedPixel; for (unsigned int uiSelector = 0; uiSelector < SELECTORS; uiSelector++) { if (pfrgbaSourcePixel->fA < 0.5f) { uiSelector = TRANSPARENT_SELECTOR; } else if (uiSelector == TRANSPARENT_SELECTOR) { continue; } frgbaDecodedPixel = afrgbaSelectors[uiSelector]; float fPixelError; fPixelError = CalcPixelError(frgbaDecodedPixel, m_afDecodedAlphas[a_phalf->m_pauiPixelMapping[uiPixel]], *pfrgbaSourcePixel); if (fPixelError < afPixelErrors[uiPixel]) { auiPixelSelectors[uiPixel] = uiSelector; afrgbaDecodedColors[uiPixel] = frgbaDecodedPixel; afPixelErrors[uiPixel] = fPixelError; } if (uiSelector == TRANSPARENT_SELECTOR) { break; } } } // add up all pixel errors float fCWError = 0.0f; for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++) { fCWError += afPixelErrors[uiPixel]; } // if best CW so far if (fCWError < ptry->m_fError) { ptry->m_uiCW = uiCW; for (unsigned int uiPixel = 0; uiPixel < 8; uiPixel++) { ptry->m_auiSelectors[uiPixel] = auiPixelSelectors[uiPixel]; } ptry->m_fError = fCWError; } } if (ptry->m_fError < fBestTryError) { a_phalf->m_ptryBest = ptry; fBestTryError = ptry->m_fError; } assert(ptry->m_fError < FLT_MAX); a_phalf->m_uiTrys++; } } } } // ---------------------------------------------------------------------------------------------------- // try encoding in T mode // save this encoding if it improves the error // // since pixels that use base color1 don't use the distance table, color1 and color2 can be twiddled independently // better encoding can be found if TWIDDLE_RADIUS is set to 2, but it will be much slower // void Block4x4Encoding_RGB8A1::TryT(unsigned int a_uiRadius) { Block4x4Encoding_RGB8A1 encodingTry = *this; // init "try" { encodingTry.m_mode = MODE_T; encodingTry.m_boolDiff = true; encodingTry.m_boolFlip = false; encodingTry.m_fError = FLT_MAX; } int iColor1Red = m_frgbaOriginalColor1_TAndH.IntRed(15.0f); int iColor1Green = m_frgbaOriginalColor1_TAndH.IntGreen(15.0f); int iColor1Blue = m_frgbaOriginalColor1_TAndH.IntBlue(15.0f); int iMinRed1 = iColor1Red - (int)a_uiRadius; if (iMinRed1 < 0) { iMinRed1 = 0; } int iMaxRed1 = iColor1Red + (int)a_uiRadius; if (iMaxRed1 > 15) { iMinRed1 = 15; } int iMinGreen1 = iColor1Green - (int)a_uiRadius; if (iMinGreen1 < 0) { iMinGreen1 = 0; } int iMaxGreen1 = iColor1Green + (int)a_uiRadius; if (iMaxGreen1 > 15) { iMinGreen1 = 15; } int iMinBlue1 = iColor1Blue - (int)a_uiRadius; if (iMinBlue1 < 0) { iMinBlue1 = 0; } int iMaxBlue1 = iColor1Blue + (int)a_uiRadius; if (iMaxBlue1 > 15) { iMinBlue1 = 15; } int iColor2Red = m_frgbaOriginalColor2_TAndH.IntRed(15.0f); int iColor2Green = m_frgbaOriginalColor2_TAndH.IntGreen(15.0f); int iColor2Blue = m_frgbaOriginalColor2_TAndH.IntBlue(15.0f); int iMinRed2 = iColor2Red - (int)a_uiRadius; if (iMinRed2 < 0) { iMinRed2 = 0; } int iMaxRed2 = iColor2Red + (int)a_uiRadius; if (iMaxRed2 > 15) { iMinRed2 = 15; } int iMinGreen2 = iColor2Green - (int)a_uiRadius; if (iMinGreen2 < 0) { iMinGreen2 = 0; } int iMaxGreen2 = iColor2Green + (int)a_uiRadius; if (iMaxGreen2 > 15) { iMinGreen2 = 15; } int iMinBlue2 = iColor2Blue - (int)a_uiRadius; if (iMinBlue2 < 0) { iMinBlue2 = 0; } int iMaxBlue2 = iColor2Blue + (int)a_uiRadius; if (iMaxBlue2 > 15) { iMinBlue2 = 15; } for (unsigned int uiDistance = 0; uiDistance < TH_DISTANCES; uiDistance++) { encodingTry.m_uiCW1 = uiDistance; // twiddle m_frgbaOriginalColor2_TAndH // twiddle color2 first, since it affects 3 selectors, while color1 only affects one selector // for (int iRed2 = iMinRed2; iRed2 <= iMaxRed2; iRed2++) { for (int iGreen2 = iMinGreen2; iGreen2 <= iMaxGreen2; iGreen2++) { for (int iBlue2 = iMinBlue2; iBlue2 <= iMaxBlue2; iBlue2++) { for (unsigned int uiBaseColorSwaps = 0; uiBaseColorSwaps < 2; uiBaseColorSwaps++) { if (uiBaseColorSwaps == 0) { encodingTry.m_frgbaColor1 = m_frgbaOriginalColor1_TAndH; encodingTry.m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed2, (unsigned char)iGreen2, (unsigned char)iBlue2); } else { encodingTry.m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed2, (unsigned char)iGreen2, (unsigned char)iBlue2); encodingTry.m_frgbaColor2 = m_frgbaOriginalColor1_TAndH; } encodingTry.TryT_BestSelectorCombination(); if (encodingTry.m_fError < m_fError) { m_mode = encodingTry.m_mode; m_boolDiff = encodingTry.m_boolDiff; m_boolFlip = encodingTry.m_boolFlip; m_frgbaColor1 = encodingTry.m_frgbaColor1; m_frgbaColor2 = encodingTry.m_frgbaColor2; m_uiCW1 = encodingTry.m_uiCW1; for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) { m_auiSelectors[uiPixel] = encodingTry.m_auiSelectors[uiPixel]; m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel]; } m_fError = encodingTry.m_fError; } } } } } // twiddle m_frgbaOriginalColor1_TAndH for (int iRed1 = iMinRed1; iRed1 <= iMaxRed1; iRed1++) { for (int iGreen1 = iMinGreen1; iGreen1 <= iMaxGreen1; iGreen1++) { for (int iBlue1 = iMinBlue1; iBlue1 <= iMaxBlue1; iBlue1++) { for (unsigned int uiBaseColorSwaps = 0; uiBaseColorSwaps < 2; uiBaseColorSwaps++) { if (uiBaseColorSwaps == 0) { encodingTry.m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed1, (unsigned char)iGreen1, (unsigned char)iBlue1); encodingTry.m_frgbaColor2 = m_frgbaOriginalColor2_TAndH; } else { encodingTry.m_frgbaColor1 = m_frgbaOriginalColor2_TAndH; encodingTry.m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed1, (unsigned char)iGreen1, (unsigned char)iBlue1); } encodingTry.TryT_BestSelectorCombination(); if (encodingTry.m_fError < m_fError) { m_mode = encodingTry.m_mode; m_boolDiff = encodingTry.m_boolDiff; m_boolFlip = encodingTry.m_boolFlip; m_frgbaColor1 = encodingTry.m_frgbaColor1; m_frgbaColor2 = encodingTry.m_frgbaColor2; m_uiCW1 = encodingTry.m_uiCW1; for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) { m_auiSelectors[uiPixel] = encodingTry.m_auiSelectors[uiPixel]; m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel]; } m_fError = encodingTry.m_fError; } } } } } } } // ---------------------------------------------------------------------------------------------------- // find best selector combination for TryT // called on an encodingTry // void Block4x4Encoding_RGB8A1::TryT_BestSelectorCombination(void) { float fDistance = s_afTHDistanceTable[m_uiCW1]; unsigned int auiBestPixelSelectors[PIXELS]; float afBestPixelErrors[PIXELS] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX }; ColorFloatRGBA afrgbaBestDecodedPixels[PIXELS]; ColorFloatRGBA afrgbaDecodedPixel[SELECTORS]; assert(SELECTORS == 4); afrgbaDecodedPixel[0] = m_frgbaColor1; afrgbaDecodedPixel[1] = (m_frgbaColor2 + fDistance).ClampRGB(); afrgbaDecodedPixel[2] = ColorFloatRGBA(); afrgbaDecodedPixel[3] = (m_frgbaColor2 - fDistance).ClampRGB(); // try each selector for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) { unsigned int uiMinSelector = 0; unsigned int uiMaxSelector = SELECTORS - 1; if (m_pafrgbaSource[uiPixel].fA < 0.5f) { uiMinSelector = 2; uiMaxSelector = 2; } for (unsigned int uiSelector = uiMinSelector; uiSelector <= uiMaxSelector; uiSelector++) { float fPixelError = CalcPixelError(afrgbaDecodedPixel[uiSelector], m_afDecodedAlphas[uiPixel], m_pafrgbaSource[uiPixel]); if (fPixelError < afBestPixelErrors[uiPixel]) { afBestPixelErrors[uiPixel] = fPixelError; auiBestPixelSelectors[uiPixel] = uiSelector; afrgbaBestDecodedPixels[uiPixel] = afrgbaDecodedPixel[uiSelector]; } } } // add up all of the pixel errors float fBlockError = 0.0f; for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) { fBlockError += afBestPixelErrors[uiPixel]; } if (fBlockError < m_fError) { m_fError = fBlockError; for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) { m_auiSelectors[uiPixel] = auiBestPixelSelectors[uiPixel]; m_afrgbaDecodedColors[uiPixel] = afrgbaBestDecodedPixels[uiPixel]; } } } // ---------------------------------------------------------------------------------------------------- // try encoding in H mode // save this encoding if it improves the error // // since all pixels use the distance table, color1 and color2 can NOT be twiddled independently // TWIDDLE_RADIUS of 2 is WAY too slow // void Block4x4Encoding_RGB8A1::TryH(unsigned int a_uiRadius) { Block4x4Encoding_RGB8A1 encodingTry = *this; // init "try" { encodingTry.m_mode = MODE_H; encodingTry.m_boolDiff = true; encodingTry.m_boolFlip = false; encodingTry.m_fError = FLT_MAX; } int iColor1Red = m_frgbaOriginalColor1_TAndH.IntRed(15.0f); int iColor1Green = m_frgbaOriginalColor1_TAndH.IntGreen(15.0f); int iColor1Blue = m_frgbaOriginalColor1_TAndH.IntBlue(15.0f); int iMinRed1 = iColor1Red - (int)a_uiRadius; if (iMinRed1 < 0) { iMinRed1 = 0; } int iMaxRed1 = iColor1Red + (int)a_uiRadius; if (iMaxRed1 > 15) { iMinRed1 = 15; } int iMinGreen1 = iColor1Green - (int)a_uiRadius; if (iMinGreen1 < 0) { iMinGreen1 = 0; } int iMaxGreen1 = iColor1Green + (int)a_uiRadius; if (iMaxGreen1 > 15) { iMinGreen1 = 15; } int iMinBlue1 = iColor1Blue - (int)a_uiRadius; if (iMinBlue1 < 0) { iMinBlue1 = 0; } int iMaxBlue1 = iColor1Blue + (int)a_uiRadius; if (iMaxBlue1 > 15) { iMinBlue1 = 15; } int iColor2Red = m_frgbaOriginalColor2_TAndH.IntRed(15.0f); int iColor2Green = m_frgbaOriginalColor2_TAndH.IntGreen(15.0f); int iColor2Blue = m_frgbaOriginalColor2_TAndH.IntBlue(15.0f); int iMinRed2 = iColor2Red - (int)a_uiRadius; if (iMinRed2 < 0) { iMinRed2 = 0; } int iMaxRed2 = iColor2Red + (int)a_uiRadius; if (iMaxRed2 > 15) { iMinRed2 = 15; } int iMinGreen2 = iColor2Green - (int)a_uiRadius; if (iMinGreen2 < 0) { iMinGreen2 = 0; } int iMaxGreen2 = iColor2Green + (int)a_uiRadius; if (iMaxGreen2 > 15) { iMinGreen2 = 15; } int iMinBlue2 = iColor2Blue - (int)a_uiRadius; if (iMinBlue2 < 0) { iMinBlue2 = 0; } int iMaxBlue2 = iColor2Blue + (int)a_uiRadius; if (iMaxBlue2 > 15) { iMinBlue2 = 15; } for (unsigned int uiDistance = 0; uiDistance < TH_DISTANCES; uiDistance++) { encodingTry.m_uiCW1 = uiDistance; // twiddle m_frgbaOriginalColor1_TAndH for (int iRed1 = iMinRed1; iRed1 <= iMaxRed1; iRed1++) { for (int iGreen1 = iMinGreen1; iGreen1 <= iMaxGreen1; iGreen1++) { for (int iBlue1 = iMinBlue1; iBlue1 <= iMaxBlue1; iBlue1++) { encodingTry.m_frgbaColor1 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed1, (unsigned char)iGreen1, (unsigned char)iBlue1); encodingTry.m_frgbaColor2 = m_frgbaOriginalColor2_TAndH; // if color1 == color2, H encoding issues can pop up, so abort if (iRed1 == iColor2Red && iGreen1 == iColor2Green && iBlue1 == iColor2Blue) { continue; } encodingTry.TryH_BestSelectorCombination(); if (encodingTry.m_fError < m_fError) { m_mode = encodingTry.m_mode; m_boolDiff = encodingTry.m_boolDiff; m_boolFlip = encodingTry.m_boolFlip; m_frgbaColor1 = encodingTry.m_frgbaColor1; m_frgbaColor2 = encodingTry.m_frgbaColor2; m_uiCW1 = encodingTry.m_uiCW1; for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) { m_auiSelectors[uiPixel] = encodingTry.m_auiSelectors[uiPixel]; m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel]; } m_fError = encodingTry.m_fError; } } } } // twiddle m_frgbaOriginalColor2_TAndH for (int iRed2 = iMinRed2; iRed2 <= iMaxRed2; iRed2++) { for (int iGreen2 = iMinGreen2; iGreen2 <= iMaxGreen2; iGreen2++) { for (int iBlue2 = iMinBlue2; iBlue2 <= iMaxBlue2; iBlue2++) { encodingTry.m_frgbaColor1 = m_frgbaOriginalColor1_TAndH; encodingTry.m_frgbaColor2 = ColorFloatRGBA::ConvertFromRGB4((unsigned char)iRed2, (unsigned char)iGreen2, (unsigned char)iBlue2); // if color1 == color2, H encoding issues can pop up, so abort if (iRed2 == iColor1Red && iGreen2 == iColor1Green && iBlue2 == iColor1Blue) { continue; } encodingTry.TryH_BestSelectorCombination(); if (encodingTry.m_fError < m_fError) { m_mode = encodingTry.m_mode; m_boolDiff = encodingTry.m_boolDiff; m_boolFlip = encodingTry.m_boolFlip; m_frgbaColor1 = encodingTry.m_frgbaColor1; m_frgbaColor2 = encodingTry.m_frgbaColor2; m_uiCW1 = encodingTry.m_uiCW1; for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) { m_auiSelectors[uiPixel] = encodingTry.m_auiSelectors[uiPixel]; m_afrgbaDecodedColors[uiPixel] = encodingTry.m_afrgbaDecodedColors[uiPixel]; } m_fError = encodingTry.m_fError; } } } } } } // ---------------------------------------------------------------------------------------------------- // find best selector combination for TryH // called on an encodingTry // void Block4x4Encoding_RGB8A1::TryH_BestSelectorCombination(void) { // abort if colors and CW will pose an encoding problem { unsigned int uiRed1 = (unsigned int)m_frgbaColor1.IntRed(255.0f); unsigned int uiGreen1 = (unsigned int)m_frgbaColor1.IntGreen(255.0f); unsigned int uiBlue1 = (unsigned int)m_frgbaColor1.IntBlue(255.0f); unsigned int uiColorValue1 = (uiRed1 << 16) + (uiGreen1 << 8) + uiBlue1; unsigned int uiRed2 = (unsigned int)m_frgbaColor2.IntRed(255.0f); unsigned int uiGreen2 = (unsigned int)m_frgbaColor2.IntGreen(255.0f); unsigned int uiBlue2 = (unsigned int)m_frgbaColor2.IntBlue(255.0f); unsigned int uiColorValue2 = (uiRed2 << 16) + (uiGreen2 << 8) + uiBlue2; unsigned int uiCWLsb = m_uiCW1 & 1; if ((uiColorValue1 >= (uiColorValue2 & uiCWLsb)) == 0 || (uiColorValue1 < (uiColorValue2 & uiCWLsb)) == 1) { return; } } float fDistance = s_afTHDistanceTable[m_uiCW1]; unsigned int auiBestPixelSelectors[PIXELS]; float afBestPixelErrors[PIXELS] = { FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX, FLT_MAX }; ColorFloatRGBA afrgbaBestDecodedPixels[PIXELS]; ColorFloatRGBA afrgbaDecodedPixel[SELECTORS]; assert(SELECTORS == 4); afrgbaDecodedPixel[0] = (m_frgbaColor1 + fDistance).ClampRGB(); afrgbaDecodedPixel[1] = (m_frgbaColor1 - fDistance).ClampRGB(); afrgbaDecodedPixel[2] = ColorFloatRGBA();; afrgbaDecodedPixel[3] = (m_frgbaColor2 - fDistance).ClampRGB(); // try each selector for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) { unsigned int uiMinSelector = 0; unsigned int uiMaxSelector = SELECTORS - 1; if (m_pafrgbaSource[uiPixel].fA < 0.5f) { uiMinSelector = 2; uiMaxSelector = 2; } for (unsigned int uiSelector = uiMinSelector; uiSelector <= uiMaxSelector; uiSelector++) { float fPixelError = CalcPixelError(afrgbaDecodedPixel[uiSelector], m_afDecodedAlphas[uiPixel], m_pafrgbaSource[uiPixel]); if (fPixelError < afBestPixelErrors[uiPixel]) { afBestPixelErrors[uiPixel] = fPixelError; auiBestPixelSelectors[uiPixel] = uiSelector; afrgbaBestDecodedPixels[uiPixel] = afrgbaDecodedPixel[uiSelector]; } } } // add up all of the pixel errors float fBlockError = 0.0f; for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) { fBlockError += afBestPixelErrors[uiPixel]; } if (fBlockError < m_fError) { m_fError = fBlockError; for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) { m_auiSelectors[uiPixel] = auiBestPixelSelectors[uiPixel]; m_afrgbaDecodedColors[uiPixel] = afrgbaBestDecodedPixels[uiPixel]; } } } // ---------------------------------------------------------------------------------------------------- // try version 1 of the degenerate search // degenerate encodings use basecolor movement and a subset of the selectors to find useful encodings // each subsequent version of the degenerate search uses more basecolor movement and is less likely to // be successfull // void Block4x4Encoding_RGB8A1::TryDegenerates1(void) { TryDifferential(m_boolMostLikelyFlip, 1, -2, 0); TryDifferential(m_boolMostLikelyFlip, 1, 2, 0); TryDifferential(m_boolMostLikelyFlip, 1, 0, 2); TryDifferential(m_boolMostLikelyFlip, 1, 0, -2); } // ---------------------------------------------------------------------------------------------------- // try version 2 of the degenerate search // degenerate encodings use basecolor movement and a subset of the selectors to find useful encodings // each subsequent version of the degenerate search uses more basecolor movement and is less likely to // be successfull // void Block4x4Encoding_RGB8A1::TryDegenerates2(void) { TryDifferential(!m_boolMostLikelyFlip, 1, -2, 0); TryDifferential(!m_boolMostLikelyFlip, 1, 2, 0); TryDifferential(!m_boolMostLikelyFlip, 1, 0, 2); TryDifferential(!m_boolMostLikelyFlip, 1, 0, -2); } // ---------------------------------------------------------------------------------------------------- // try version 3 of the degenerate search // degenerate encodings use basecolor movement and a subset of the selectors to find useful encodings // each subsequent version of the degenerate search uses more basecolor movement and is less likely to // be successfull // void Block4x4Encoding_RGB8A1::TryDegenerates3(void) { TryDifferential(m_boolMostLikelyFlip, 1, -2, -2); TryDifferential(m_boolMostLikelyFlip, 1, -2, 2); TryDifferential(m_boolMostLikelyFlip, 1, 2, -2); TryDifferential(m_boolMostLikelyFlip, 1, 2, 2); } // ---------------------------------------------------------------------------------------------------- // try version 4 of the degenerate search // degenerate encodings use basecolor movement and a subset of the selectors to find useful encodings // each subsequent version of the degenerate search uses more basecolor movement and is less likely to // be successfull // void Block4x4Encoding_RGB8A1::TryDegenerates4(void) { TryDifferential(m_boolMostLikelyFlip, 1, -4, 0); TryDifferential(m_boolMostLikelyFlip, 1, 4, 0); TryDifferential(m_boolMostLikelyFlip, 1, 0, 4); TryDifferential(m_boolMostLikelyFlip, 1, 0, -4); } // ---------------------------------------------------------------------------------------------------- // set the encoding bits based on encoding state // void Block4x4Encoding_RGB8A1::SetEncodingBits(void) { switch (m_mode) { case MODE_ETC1: SetEncodingBits_ETC1(); break; case MODE_T: SetEncodingBits_T(); break; case MODE_H: SetEncodingBits_H(); break; case MODE_PLANAR: Block4x4Encoding_RGB8::SetEncodingBits_Planar(); break; default: assert(false); } } // ---------------------------------------------------------------------------------------------------- // set the encoding bits based on encoding state if ETC1 mode // void Block4x4Encoding_RGB8A1::SetEncodingBits_ETC1(void) { // there is no individual mode in RGB8A1 assert(m_boolDiff); int iRed1 = m_frgbaColor1.IntRed(31.0f); int iGreen1 = m_frgbaColor1.IntGreen(31.0f); int iBlue1 = m_frgbaColor1.IntBlue(31.0f); int iRed2 = m_frgbaColor2.IntRed(31.0f); int iGreen2 = m_frgbaColor2.IntGreen(31.0f); int iBlue2 = m_frgbaColor2.IntBlue(31.0f); int iDRed2 = iRed2 - iRed1; int iDGreen2 = iGreen2 - iGreen1; int iDBlue2 = iBlue2 - iBlue1; assert(iDRed2 >= -4 && iDRed2 < 4); assert(iDGreen2 >= -4 && iDGreen2 < 4); assert(iDBlue2 >= -4 && iDBlue2 < 4); m_pencodingbitsRGB8->differential.red1 = iRed1; m_pencodingbitsRGB8->differential.green1 = iGreen1; m_pencodingbitsRGB8->differential.blue1 = iBlue1; m_pencodingbitsRGB8->differential.dred2 = iDRed2; m_pencodingbitsRGB8->differential.dgreen2 = iDGreen2; m_pencodingbitsRGB8->differential.dblue2 = iDBlue2; m_pencodingbitsRGB8->individual.cw1 = m_uiCW1; m_pencodingbitsRGB8->individual.cw2 = m_uiCW2; SetEncodingBits_Selectors(); // in RGB8A1 encoding bits, opaque replaces differential m_pencodingbitsRGB8->differential.diff = !m_boolPunchThroughPixels; m_pencodingbitsRGB8->individual.flip = m_boolFlip; } // ---------------------------------------------------------------------------------------------------- // set the encoding bits based on encoding state if T mode // void Block4x4Encoding_RGB8A1::SetEncodingBits_T(void) { static const bool SANITY_CHECK = true; assert(m_mode == MODE_T); assert(m_boolDiff == true); unsigned int uiRed1 = (unsigned int)m_frgbaColor1.IntRed(15.0f); unsigned int uiGreen1 = (unsigned int)m_frgbaColor1.IntGreen(15.0f); unsigned int uiBlue1 = (unsigned int)m_frgbaColor1.IntBlue(15.0f); unsigned int uiRed2 = (unsigned int)m_frgbaColor2.IntRed(15.0f); unsigned int uiGreen2 = (unsigned int)m_frgbaColor2.IntGreen(15.0f); unsigned int uiBlue2 = (unsigned int)m_frgbaColor2.IntBlue(15.0f); m_pencodingbitsRGB8->t.red1a = uiRed1 >> 2; m_pencodingbitsRGB8->t.red1b = uiRed1; m_pencodingbitsRGB8->t.green1 = uiGreen1; m_pencodingbitsRGB8->t.blue1 = uiBlue1; m_pencodingbitsRGB8->t.red2 = uiRed2; m_pencodingbitsRGB8->t.green2 = uiGreen2; m_pencodingbitsRGB8->t.blue2 = uiBlue2; m_pencodingbitsRGB8->t.da = m_uiCW1 >> 1; m_pencodingbitsRGB8->t.db = m_uiCW1; // in RGB8A1 encoding bits, opaque replaces differential m_pencodingbitsRGB8->differential.diff = !m_boolPunchThroughPixels; Block4x4Encoding_ETC1::SetEncodingBits_Selectors(); // create an invalid R differential to trigger T mode m_pencodingbitsRGB8->t.detect1 = 0; m_pencodingbitsRGB8->t.detect2 = 0; int iRed2 = (int)m_pencodingbitsRGB8->differential.red1 + (int)m_pencodingbitsRGB8->differential.dred2; if (iRed2 >= 4) { m_pencodingbitsRGB8->t.detect1 = 7; m_pencodingbitsRGB8->t.detect2 = 0; } else { m_pencodingbitsRGB8->t.detect1 = 0; m_pencodingbitsRGB8->t.detect2 = 1; } if (SANITY_CHECK) { iRed2 = (int)m_pencodingbitsRGB8->differential.red1 + (int)m_pencodingbitsRGB8->differential.dred2; // make sure red overflows assert(iRed2 < 0 || iRed2 > 31); } } // ---------------------------------------------------------------------------------------------------- // set the encoding bits based on encoding state if H mode // // colors and selectors may need to swap in order to generate lsb of distance index // void Block4x4Encoding_RGB8A1::SetEncodingBits_H(void) { static const bool SANITY_CHECK = true; assert(m_mode == MODE_H); assert(m_boolDiff == true); unsigned int uiRed1 = (unsigned int)m_frgbaColor1.IntRed(15.0f); unsigned int uiGreen1 = (unsigned int)m_frgbaColor1.IntGreen(15.0f); unsigned int uiBlue1 = (unsigned int)m_frgbaColor1.IntBlue(15.0f); unsigned int uiRed2 = (unsigned int)m_frgbaColor2.IntRed(15.0f); unsigned int uiGreen2 = (unsigned int)m_frgbaColor2.IntGreen(15.0f); unsigned int uiBlue2 = (unsigned int)m_frgbaColor2.IntBlue(15.0f); unsigned int uiColor1 = (uiRed1 << 16) + (uiGreen1 << 8) + uiBlue1; unsigned int uiColor2 = (uiRed2 << 16) + (uiGreen2 << 8) + uiBlue2; bool boolOddDistance = m_uiCW1 & 1; bool boolSwapColors = (uiColor1 < uiColor2) ^ !boolOddDistance; if (boolSwapColors) { m_pencodingbitsRGB8->h.red1 = uiRed2; m_pencodingbitsRGB8->h.green1a = uiGreen2 >> 1; m_pencodingbitsRGB8->h.green1b = uiGreen2; m_pencodingbitsRGB8->h.blue1a = uiBlue2 >> 3; m_pencodingbitsRGB8->h.blue1b = uiBlue2 >> 1; m_pencodingbitsRGB8->h.blue1c = uiBlue2; m_pencodingbitsRGB8->h.red2 = uiRed1; m_pencodingbitsRGB8->h.green2a = uiGreen1 >> 1; m_pencodingbitsRGB8->h.green2b = uiGreen1; m_pencodingbitsRGB8->h.blue2 = uiBlue1; m_pencodingbitsRGB8->h.da = m_uiCW1 >> 2; m_pencodingbitsRGB8->h.db = m_uiCW1 >> 1; } else { m_pencodingbitsRGB8->h.red1 = uiRed1; m_pencodingbitsRGB8->h.green1a = uiGreen1 >> 1; m_pencodingbitsRGB8->h.green1b = uiGreen1; m_pencodingbitsRGB8->h.blue1a = uiBlue1 >> 3; m_pencodingbitsRGB8->h.blue1b = uiBlue1 >> 1; m_pencodingbitsRGB8->h.blue1c = uiBlue1; m_pencodingbitsRGB8->h.red2 = uiRed2; m_pencodingbitsRGB8->h.green2a = uiGreen2 >> 1; m_pencodingbitsRGB8->h.green2b = uiGreen2; m_pencodingbitsRGB8->h.blue2 = uiBlue2; m_pencodingbitsRGB8->h.da = m_uiCW1 >> 2; m_pencodingbitsRGB8->h.db = m_uiCW1 >> 1; } // in RGB8A1 encoding bits, opaque replaces differential m_pencodingbitsRGB8->differential.diff = !m_boolPunchThroughPixels; Block4x4Encoding_ETC1::SetEncodingBits_Selectors(); if (boolSwapColors) { m_pencodingbitsRGB8->h.selectors ^= 0x0000FFFF; } // create an invalid R differential to trigger T mode m_pencodingbitsRGB8->h.detect1 = 0; m_pencodingbitsRGB8->h.detect2 = 0; m_pencodingbitsRGB8->h.detect3 = 0; int iRed2 = (int)m_pencodingbitsRGB8->differential.red1 + (int)m_pencodingbitsRGB8->differential.dred2; int iGreen2 = (int)m_pencodingbitsRGB8->differential.green1 + (int)m_pencodingbitsRGB8->differential.dgreen2; if (iRed2 < 0 || iRed2 > 31) { m_pencodingbitsRGB8->h.detect1 = 1; } if (iGreen2 >= 4) { m_pencodingbitsRGB8->h.detect2 = 7; m_pencodingbitsRGB8->h.detect3 = 0; } else { m_pencodingbitsRGB8->h.detect2 = 0; m_pencodingbitsRGB8->h.detect3 = 1; } if (SANITY_CHECK) { iRed2 = (int)m_pencodingbitsRGB8->differential.red1 + (int)m_pencodingbitsRGB8->differential.dred2; iGreen2 = (int)m_pencodingbitsRGB8->differential.green1 + (int)m_pencodingbitsRGB8->differential.dgreen2; // make sure red doesn't overflow and green does assert(iRed2 >= 0 && iRed2 <= 31); assert(iGreen2 < 0 || iGreen2 > 31); } } // #################################################################################################### // Block4x4Encoding_RGB8A1_Opaque // #################################################################################################### // ---------------------------------------------------------------------------------------------------- // perform a single encoding iteration // replace the encoding if a better encoding was found // subsequent iterations generally take longer for each iteration // set m_boolDone if encoding is perfect or encoding is finished based on a_fEffort // void Block4x4Encoding_RGB8A1_Opaque::PerformIteration(float a_fEffort) { assert(!m_boolPunchThroughPixels); assert(!m_boolTransparent); assert(!m_boolDone); switch (m_uiEncodingIterations) { case 0: PerformFirstIteration(); break; case 1: Block4x4Encoding_ETC1::TryDifferential(m_boolMostLikelyFlip, 1, 0, 0); break; case 2: Block4x4Encoding_ETC1::TryDifferential(!m_boolMostLikelyFlip, 1, 0, 0); break; case 3: Block4x4Encoding_RGB8::TryPlanar(1); break; case 4: Block4x4Encoding_RGB8::TryTAndH(1); if (a_fEffort <= 49.5f) { m_boolDone = true; } break; case 5: Block4x4Encoding_ETC1::TryDegenerates1(); if (a_fEffort <= 59.5f) { m_boolDone = true; } break; case 6: Block4x4Encoding_ETC1::TryDegenerates2(); if (a_fEffort <= 69.5f) { m_boolDone = true; } break; case 7: Block4x4Encoding_ETC1::TryDegenerates3(); if (a_fEffort <= 79.5f) { m_boolDone = true; } break; case 8: Block4x4Encoding_ETC1::TryDegenerates4(); m_boolDone = true; break; default: assert(0); break; } m_uiEncodingIterations++; SetDoneIfPerfect(); } // ---------------------------------------------------------------------------------------------------- // find best initial encoding to ensure block has a valid encoding // void Block4x4Encoding_RGB8A1_Opaque::PerformFirstIteration(void) { // set decoded alphas // calculate alpha error m_fError = 0.0f; for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) { m_afDecodedAlphas[uiPixel] = 1.0f; float fDeltaA = 1.0f - m_pafrgbaSource[uiPixel].fA; m_fError += fDeltaA * fDeltaA; } CalculateMostLikelyFlip(); m_fError = FLT_MAX; Block4x4Encoding_ETC1::TryDifferential(m_boolMostLikelyFlip, 0, 0, 0); SetDoneIfPerfect(); if (m_boolDone) { return; } Block4x4Encoding_ETC1::TryDifferential(!m_boolMostLikelyFlip, 0, 0, 0); SetDoneIfPerfect(); if (m_boolDone) { return; } Block4x4Encoding_RGB8::TryPlanar(0); SetDoneIfPerfect(); if (m_boolDone) { return; } Block4x4Encoding_RGB8::TryTAndH(0); SetDoneIfPerfect(); } // #################################################################################################### // Block4x4Encoding_RGB8A1_Transparent // #################################################################################################### // ---------------------------------------------------------------------------------------------------- // perform a single encoding iteration // replace the encoding if a better encoding was found // subsequent iterations generally take longer for each iteration // set m_boolDone if encoding is perfect or encoding is finished based on a_fEffort // void Block4x4Encoding_RGB8A1_Transparent::PerformIteration(float ) { assert(!m_boolOpaque); assert(m_boolTransparent); assert(!m_boolDone); assert(m_uiEncodingIterations == 0); m_mode = MODE_ETC1; m_boolDiff = true; m_boolFlip = false; m_uiCW1 = 0; m_uiCW2 = 0; m_frgbaColor1 = ColorFloatRGBA(); m_frgbaColor2 = ColorFloatRGBA(); for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++) { m_auiSelectors[uiPixel] = TRANSPARENT_SELECTOR; m_afrgbaDecodedColors[uiPixel] = ColorFloatRGBA(); m_afDecodedAlphas[uiPixel] = 0.0f; } CalcBlockError(); m_boolDone = true; m_uiEncodingIterations++; } // ---------------------------------------------------------------------------------------------------- // }