mirror of
https://github.com/Relintai/pandemonium_engine.git
synced 2024-12-24 12:47:12 +01:00
262 lines
8.1 KiB
C++
262 lines
8.1 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.cpp
|
||
|
|
||
|
Block4x4Encoding is the abstract base class for the different encoders. Each encoder targets a
|
||
|
particular file format (e.g. ETC1, RGB8, RGBA8, R11)
|
||
|
|
||
|
*/
|
||
|
|
||
|
#include "EtcConfig.h"
|
||
|
#include "EtcBlock4x4Encoding.h"
|
||
|
|
||
|
#include "EtcBlock4x4EncodingBits.h"
|
||
|
#include "EtcBlock4x4.h"
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include <assert.h>
|
||
|
|
||
|
namespace Etc
|
||
|
{
|
||
|
// ----------------------------------------------------------------------------------------------------
|
||
|
//
|
||
|
const float Block4x4Encoding::LUMA_WEIGHT = 3.0f;
|
||
|
const float Block4x4Encoding::CHROMA_BLUE_WEIGHT = 0.5f;
|
||
|
|
||
|
// ----------------------------------------------------------------------------------------------------
|
||
|
//
|
||
|
Block4x4Encoding::Block4x4Encoding(void)
|
||
|
{
|
||
|
|
||
|
m_pblockParent = nullptr;
|
||
|
|
||
|
m_pafrgbaSource = nullptr;
|
||
|
|
||
|
m_boolBorderPixels = false;
|
||
|
|
||
|
m_fError = -1.0f;
|
||
|
|
||
|
m_mode = MODE_UNKNOWN;
|
||
|
|
||
|
m_uiEncodingIterations = 0;
|
||
|
m_boolDone = false;
|
||
|
|
||
|
for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
|
||
|
{
|
||
|
m_afrgbaDecodedColors[uiPixel] = ColorFloatRGBA(-1.0f, -1.0f, -1.0f, -1.0f);
|
||
|
m_afDecodedAlphas[uiPixel] = -1.0f;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------------------------------
|
||
|
// initialize the generic encoding for a 4x4 block
|
||
|
// a_pblockParent points to the block associated with this encoding
|
||
|
// a_errormetric is used to choose the best encoding
|
||
|
// init the decoded pixels to -1 to mark them as undefined
|
||
|
// init the error to -1 to mark it as undefined
|
||
|
//
|
||
|
void Block4x4Encoding::Init(Block4x4 *a_pblockParent,
|
||
|
ColorFloatRGBA *a_pafrgbaSource,
|
||
|
ErrorMetric a_errormetric)
|
||
|
{
|
||
|
|
||
|
m_pblockParent = a_pblockParent;
|
||
|
|
||
|
m_pafrgbaSource = a_pafrgbaSource;
|
||
|
|
||
|
m_boolBorderPixels = m_pblockParent->HasBorderPixels();
|
||
|
|
||
|
m_fError = -1.0f;
|
||
|
|
||
|
m_uiEncodingIterations = 0;
|
||
|
|
||
|
m_errormetric = a_errormetric;
|
||
|
|
||
|
for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
|
||
|
{
|
||
|
m_afrgbaDecodedColors[uiPixel] = ColorFloatRGBA(-1.0f, -1.0f, -1.0f, -1.0f);
|
||
|
m_afDecodedAlphas[uiPixel] = -1.0f;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------------------------------
|
||
|
// calculate the error for the block by summing the pixel errors
|
||
|
//
|
||
|
void Block4x4Encoding::CalcBlockError(void)
|
||
|
{
|
||
|
m_fError = 0.0f;
|
||
|
|
||
|
for (unsigned int uiPixel = 0; uiPixel < PIXELS; uiPixel++)
|
||
|
{
|
||
|
m_fError += CalcPixelError(m_afrgbaDecodedColors[uiPixel], m_afDecodedAlphas[uiPixel],
|
||
|
m_pafrgbaSource[uiPixel]);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------------------------------
|
||
|
// calculate the error between the source pixel and the decoded pixel
|
||
|
// the error amount is base on the error metric
|
||
|
//
|
||
|
float Block4x4Encoding::CalcPixelError(ColorFloatRGBA a_frgbaDecodedColor, float a_fDecodedAlpha,
|
||
|
ColorFloatRGBA a_frgbaSourcePixel)
|
||
|
{
|
||
|
|
||
|
// if a border pixel
|
||
|
if (isnan(a_frgbaSourcePixel.fA))
|
||
|
{
|
||
|
return 0.0f;
|
||
|
}
|
||
|
|
||
|
if (m_errormetric == ErrorMetric::RGBA)
|
||
|
{
|
||
|
assert(a_fDecodedAlpha >= 0.0f);
|
||
|
|
||
|
float fDRed = (a_fDecodedAlpha * a_frgbaDecodedColor.fR) -
|
||
|
(a_frgbaSourcePixel.fA * a_frgbaSourcePixel.fR);
|
||
|
float fDGreen = (a_fDecodedAlpha * a_frgbaDecodedColor.fG) -
|
||
|
(a_frgbaSourcePixel.fA * a_frgbaSourcePixel.fG);
|
||
|
float fDBlue = (a_fDecodedAlpha * a_frgbaDecodedColor.fB) -
|
||
|
(a_frgbaSourcePixel.fA * a_frgbaSourcePixel.fB);
|
||
|
|
||
|
float fDAlpha = a_fDecodedAlpha - a_frgbaSourcePixel.fA;
|
||
|
|
||
|
return fDRed*fDRed + fDGreen*fDGreen + fDBlue*fDBlue + fDAlpha*fDAlpha;
|
||
|
}
|
||
|
else if (m_errormetric == ErrorMetric::RGBX)
|
||
|
{
|
||
|
assert(a_fDecodedAlpha >= 0.0f);
|
||
|
|
||
|
float fDRed = a_frgbaDecodedColor.fR - a_frgbaSourcePixel.fR;
|
||
|
float fDGreen = a_frgbaDecodedColor.fG - a_frgbaSourcePixel.fG;
|
||
|
float fDBlue = a_frgbaDecodedColor.fB - a_frgbaSourcePixel.fB;
|
||
|
float fDAlpha = a_fDecodedAlpha - a_frgbaSourcePixel.fA;
|
||
|
|
||
|
return fDRed*fDRed + fDGreen*fDGreen + fDBlue*fDBlue + fDAlpha*fDAlpha;
|
||
|
}
|
||
|
else if (m_errormetric == ErrorMetric::REC709)
|
||
|
{
|
||
|
assert(a_fDecodedAlpha >= 0.0f);
|
||
|
|
||
|
float fLuma1 = a_frgbaSourcePixel.fR*0.2126f + a_frgbaSourcePixel.fG*0.7152f + a_frgbaSourcePixel.fB*0.0722f;
|
||
|
float fChromaR1 = 0.5f * ((a_frgbaSourcePixel.fR - fLuma1) * (1.0f / (1.0f - 0.2126f)));
|
||
|
float fChromaB1 = 0.5f * ((a_frgbaSourcePixel.fB - fLuma1) * (1.0f / (1.0f - 0.0722f)));
|
||
|
|
||
|
float fLuma2 = a_frgbaDecodedColor.fR*0.2126f +
|
||
|
a_frgbaDecodedColor.fG*0.7152f +
|
||
|
a_frgbaDecodedColor.fB*0.0722f;
|
||
|
float fChromaR2 = 0.5f * ((a_frgbaDecodedColor.fR - fLuma2) * (1.0f / (1.0f - 0.2126f)));
|
||
|
float fChromaB2 = 0.5f * ((a_frgbaDecodedColor.fB - fLuma2) * (1.0f / (1.0f - 0.0722f)));
|
||
|
|
||
|
float fDeltaL = a_frgbaSourcePixel.fA * fLuma1 - a_fDecodedAlpha * fLuma2;
|
||
|
float fDeltaCr = a_frgbaSourcePixel.fA * fChromaR1 - a_fDecodedAlpha * fChromaR2;
|
||
|
float fDeltaCb = a_frgbaSourcePixel.fA * fChromaB1 - a_fDecodedAlpha * fChromaB2;
|
||
|
|
||
|
float fDAlpha = a_fDecodedAlpha - a_frgbaSourcePixel.fA;
|
||
|
|
||
|
// Favor Luma accuracy over Chroma, and Red over Blue
|
||
|
return LUMA_WEIGHT*fDeltaL*fDeltaL +
|
||
|
fDeltaCr*fDeltaCr +
|
||
|
CHROMA_BLUE_WEIGHT*fDeltaCb*fDeltaCb +
|
||
|
fDAlpha*fDAlpha;
|
||
|
#if 0
|
||
|
float fDRed = a_frgbaDecodedPixel.fR - a_frgbaSourcePixel.fR;
|
||
|
float fDGreen = a_frgbaDecodedPixel.fG - a_frgbaSourcePixel.fG;
|
||
|
float fDBlue = a_frgbaDecodedPixel.fB - a_frgbaSourcePixel.fB;
|
||
|
return 2.0f * 3.0f * fDeltaL * fDeltaL + fDRed*fDRed + fDGreen*fDGreen + fDBlue*fDBlue;
|
||
|
#endif
|
||
|
}
|
||
|
else if (m_errormetric == ErrorMetric::NORMALXYZ)
|
||
|
{
|
||
|
float fDecodedX = 2.0f * a_frgbaDecodedColor.fR - 1.0f;
|
||
|
float fDecodedY = 2.0f * a_frgbaDecodedColor.fG - 1.0f;
|
||
|
float fDecodedZ = 2.0f * a_frgbaDecodedColor.fB - 1.0f;
|
||
|
|
||
|
float fDecodedLength = sqrtf(fDecodedX*fDecodedX + fDecodedY*fDecodedY + fDecodedZ*fDecodedZ);
|
||
|
|
||
|
if (fDecodedLength < 0.5f)
|
||
|
{
|
||
|
return 1.0f;
|
||
|
}
|
||
|
else if (fDecodedLength == 0.0f)
|
||
|
{
|
||
|
fDecodedX = 1.0f;
|
||
|
fDecodedY = 0.0f;
|
||
|
fDecodedZ = 0.0f;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
fDecodedX /= fDecodedLength;
|
||
|
fDecodedY /= fDecodedLength;
|
||
|
fDecodedZ /= fDecodedLength;
|
||
|
}
|
||
|
|
||
|
float fSourceX = 2.0f * a_frgbaSourcePixel.fR - 1.0f;
|
||
|
float fSourceY = 2.0f * a_frgbaSourcePixel.fG - 1.0f;
|
||
|
float fSourceZ = 2.0f * a_frgbaSourcePixel.fB - 1.0f;
|
||
|
|
||
|
float fSourceLength = sqrtf(fSourceX*fSourceX + fSourceY*fSourceY + fSourceZ*fSourceZ);
|
||
|
|
||
|
if (fSourceLength == 0.0f)
|
||
|
{
|
||
|
fSourceX = 1.0f;
|
||
|
fSourceY = 0.0f;
|
||
|
fSourceZ = 0.0f;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
fSourceX /= fSourceLength;
|
||
|
fSourceY /= fSourceLength;
|
||
|
fSourceZ /= fSourceLength;
|
||
|
}
|
||
|
|
||
|
float fDotProduct = fSourceX*fDecodedX + fSourceY*fDecodedY + fSourceZ*fDecodedZ;
|
||
|
float fNormalizedDotProduct = 1.0f - 0.5f * (fDotProduct + 1.0f);
|
||
|
float fDotProductError = fNormalizedDotProduct * fNormalizedDotProduct;
|
||
|
|
||
|
float fLength2 = fDecodedX*fDecodedX + fDecodedY*fDecodedY + fDecodedZ*fDecodedZ;
|
||
|
float fLength2Error = fabsf(1.0f - fLength2);
|
||
|
|
||
|
float fDeltaW = a_frgbaDecodedColor.fA - a_frgbaSourcePixel.fA;
|
||
|
float fErrorW = fDeltaW * fDeltaW;
|
||
|
|
||
|
return fDotProductError + fLength2Error + fErrorW;
|
||
|
}
|
||
|
else // ErrorMetric::NUMERIC
|
||
|
{
|
||
|
assert(a_fDecodedAlpha >= 0.0f);
|
||
|
|
||
|
float fDX = a_frgbaDecodedColor.fR - a_frgbaSourcePixel.fR;
|
||
|
float fDY = a_frgbaDecodedColor.fG - a_frgbaSourcePixel.fG;
|
||
|
float fDZ = a_frgbaDecodedColor.fB - a_frgbaSourcePixel.fB;
|
||
|
float fDW = a_frgbaDecodedColor.fA - a_frgbaSourcePixel.fA;
|
||
|
|
||
|
return fDX*fDX + fDY*fDY + fDZ*fDZ + fDW*fDW;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------------------------------
|
||
|
//
|
||
|
|
||
|
} // namespace Etc
|
||
|
|