mirror of
https://github.com/godotengine/godot.git
synced 2024-12-15 10:12:40 +08:00
dbf52c63cc
Those checks were *very likely* meant to clamp the max value, not the min one. Fixes https://github.com/godotengine/godot/issues/10059#issuecomment-606993001.
1820 lines
53 KiB
C++
1820 lines
53 KiB
C++
/*
|
|
* 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 <stdio.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
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)
|
|
{
|
|
iMaxRed1 = 15;
|
|
}
|
|
|
|
int iMinGreen1 = iColor1Green - (int)a_uiRadius;
|
|
if (iMinGreen1 < 0)
|
|
{
|
|
iMinGreen1 = 0;
|
|
}
|
|
int iMaxGreen1 = iColor1Green + (int)a_uiRadius;
|
|
if (iMaxGreen1 > 15)
|
|
{
|
|
iMaxGreen1 = 15;
|
|
}
|
|
|
|
int iMinBlue1 = iColor1Blue - (int)a_uiRadius;
|
|
if (iMinBlue1 < 0)
|
|
{
|
|
iMinBlue1 = 0;
|
|
}
|
|
int iMaxBlue1 = iColor1Blue + (int)a_uiRadius;
|
|
if (iMaxBlue1 > 15)
|
|
{
|
|
iMaxBlue1 = 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)
|
|
{
|
|
iMaxRed2 = 15;
|
|
}
|
|
|
|
int iMinGreen2 = iColor2Green - (int)a_uiRadius;
|
|
if (iMinGreen2 < 0)
|
|
{
|
|
iMinGreen2 = 0;
|
|
}
|
|
int iMaxGreen2 = iColor2Green + (int)a_uiRadius;
|
|
if (iMaxGreen2 > 15)
|
|
{
|
|
iMaxGreen2 = 15;
|
|
}
|
|
|
|
int iMinBlue2 = iColor2Blue - (int)a_uiRadius;
|
|
if (iMinBlue2 < 0)
|
|
{
|
|
iMinBlue2 = 0;
|
|
}
|
|
int iMaxBlue2 = iColor2Blue + (int)a_uiRadius;
|
|
if (iMaxBlue2 > 15)
|
|
{
|
|
iMaxBlue2 = 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)
|
|
{
|
|
iMaxRed1 = 15;
|
|
}
|
|
|
|
int iMinGreen1 = iColor1Green - (int)a_uiRadius;
|
|
if (iMinGreen1 < 0)
|
|
{
|
|
iMinGreen1 = 0;
|
|
}
|
|
int iMaxGreen1 = iColor1Green + (int)a_uiRadius;
|
|
if (iMaxGreen1 > 15)
|
|
{
|
|
iMaxGreen1 = 15;
|
|
}
|
|
|
|
int iMinBlue1 = iColor1Blue - (int)a_uiRadius;
|
|
if (iMinBlue1 < 0)
|
|
{
|
|
iMinBlue1 = 0;
|
|
}
|
|
int iMaxBlue1 = iColor1Blue + (int)a_uiRadius;
|
|
if (iMaxBlue1 > 15)
|
|
{
|
|
iMaxBlue1 = 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)
|
|
{
|
|
iMaxRed2 = 15;
|
|
}
|
|
|
|
int iMinGreen2 = iColor2Green - (int)a_uiRadius;
|
|
if (iMinGreen2 < 0)
|
|
{
|
|
iMinGreen2 = 0;
|
|
}
|
|
int iMaxGreen2 = iColor2Green + (int)a_uiRadius;
|
|
if (iMaxGreen2 > 15)
|
|
{
|
|
iMaxGreen2 = 15;
|
|
}
|
|
|
|
int iMinBlue2 = iColor2Blue - (int)a_uiRadius;
|
|
if (iMinBlue2 < 0)
|
|
{
|
|
iMinBlue2 = 0;
|
|
}
|
|
int iMaxBlue2 = iColor2Blue + (int)a_uiRadius;
|
|
if (iMaxBlue2 > 15)
|
|
{
|
|
iMaxBlue2 = 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++;
|
|
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------------------
|
|
//
|
|
}
|