3364 lines
136 KiB
C++
3364 lines
136 KiB
C++
//=====================================================================
|
|
// Copyright (c) 2020-2023 Advanced Micro Devices, Inc. All rights reserved.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files(the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions :
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
//
|
|
// File: bc1_cmp.h
|
|
//--------------------------------------------------------------------------------------
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// Licensed under the MIT License.
|
|
//--------------------------------------------------------------------------------------
|
|
|
|
#define USE_CMP
|
|
|
|
#include "common_def.h"
|
|
#include "bcn_common_kernel.h"
|
|
#include "bcn_common_api.h"
|
|
|
|
#ifndef ASPM_GPU
|
|
#include "cpu_extensions.h"
|
|
#include "core_simd.h"
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------
|
|
// When build is for CPU, we have some missing API calls common to GPU
|
|
// Use CPU CMP_Core replacements
|
|
//-----------------------------------------------------------------------
|
|
#if defined(ASPM_GPU) || defined(ASPM_HLSL) || defined(ASPM_OPENCL)
|
|
#define ALIGN_16
|
|
#define ALIGN_32
|
|
#define ALIGN_64
|
|
#else
|
|
#include INC_cmp_math_func
|
|
#if defined(_WIN32) || defined(_WIN64)
|
|
#define ALIGN_16 __declspec(align(16))
|
|
#define ALIGN_32 __declspec(align(32))
|
|
#define ALIGN_64 __declspec(align(64))
|
|
#else // !WIN32 && !_WIN64
|
|
#define ALIGN_16 __attribute__((aligned(16)))
|
|
#define ALIGN_32 __attribute__((aligned(32)))
|
|
#define ALIGN_64 __attribute__((aligned(64)))
|
|
#endif // !WIN32 && !_WIN64
|
|
#endif
|
|
|
|
#define USE_REFINE3D
|
|
#define USE_REFINE
|
|
|
|
#ifndef MAX_ERROR
|
|
#define MAX_ERROR 128000.f
|
|
#endif
|
|
|
|
#define NUM_CHANNELS 4
|
|
#define NUM_ENDPOINTS 2
|
|
|
|
#ifndef CMP_QUALITY0
|
|
#define CMP_QUALITY0 0.25f
|
|
#endif
|
|
|
|
#ifndef CMP_QUALITY1
|
|
#define CMP_QUALITY1 0.50f
|
|
#endif
|
|
|
|
#ifndef CMP_QUALITY2
|
|
#define CMP_QUALITY2 0.75f
|
|
#endif
|
|
|
|
#define EPS (2.f / 255.f) * (2.f / 255.f)
|
|
#define EPS2 3.f * (2.f / 255.f) * (2.f / 255.f)
|
|
|
|
// Disable SIMD code during GPU builds
|
|
#if !defined(ASPM_GPU)
|
|
CMP_STATIC CGU_BOOL g_bc1FunctionPointersSet = false;
|
|
|
|
// declarations for SIMD function variations
|
|
CMP_STATIC CGU_FLOAT _cpu_bc1ComputeBestEndpoints(CGU_FLOAT*, CGU_FLOAT*, CGU_FLOAT*, CGU_FLOAT*, CGU_FLOAT*, int, int);
|
|
|
|
// function pointers
|
|
CMP_STATIC CGU_FLOAT (*cpu_bc1ComputeBestEndpoints)(CGU_FLOAT*, CGU_FLOAT*, CGU_FLOAT*, CGU_FLOAT*, CGU_FLOAT*, int, int) = 0;
|
|
|
|
// Toggle which SIMD instruction set extensions to use. Setting this to EXTENSION_COUNT will enable auto-detection of supported extensions.
|
|
// NOTE: The requested extension will only be enabled if it is supported by the current CPU.
|
|
CMP_STATIC bool bc1ToggleSIMD(CGU_INT newExtension)
|
|
{
|
|
CGU_BOOL useAVX512 = true;
|
|
CGU_BOOL useAVX2 = true;
|
|
CGU_BOOL useSSE42 = true;
|
|
|
|
CPUExtensions extensions = GetCPUExtensions();
|
|
|
|
if (newExtension < EXTENSION_COUNT) // user requested a specific instruction set extension
|
|
{
|
|
useAVX512 = newExtension == EXTENSION_AVX512_F;
|
|
useAVX2 = newExtension == EXTENSION_AVX2;
|
|
useSSE42 = newExtension == EXTENSION_SSE42;
|
|
}
|
|
|
|
if (useAVX512 && IsAvailableAVX512(extensions))
|
|
{
|
|
cpu_bc1ComputeBestEndpoints = avx512_bc1ComputeBestEndpoints;
|
|
}
|
|
else if (useAVX2 && IsAvailableAVX2(extensions))
|
|
{
|
|
cpu_bc1ComputeBestEndpoints = avx_bc1ComputeBestEndpoints;
|
|
}
|
|
else if (useSSE42 && IsAvailableSSE4(extensions))
|
|
{
|
|
cpu_bc1ComputeBestEndpoints = sse_bc1ComputeBestEndpoints;
|
|
}
|
|
else
|
|
{
|
|
cpu_bc1ComputeBestEndpoints = _cpu_bc1ComputeBestEndpoints;
|
|
}
|
|
|
|
g_bc1FunctionPointersSet = true;
|
|
|
|
bool result = true;
|
|
|
|
if (newExtension != EXTENSION_COUNT && (useAVX512 && !IsAvailableAVX512(extensions)) || (useAVX2 && !IsAvailableAVX2(extensions)) || (useSSE42 && !IsAvailableSSE4(extensions)))
|
|
result = false;
|
|
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
static CGU_FLOAT cgu_getRampErr(CGU_FLOAT Prj[BLOCK_SIZE_4X4],
|
|
CGU_FLOAT PrjErr[BLOCK_SIZE_4X4],
|
|
CGU_FLOAT PreMRep[BLOCK_SIZE_4X4],
|
|
CGU_FLOAT StepErr,
|
|
CGU_FLOAT lowPosStep,
|
|
CGU_FLOAT highPosStep,
|
|
CGU_UINT32 dwUniqueColors)
|
|
{
|
|
CGU_FLOAT error = 0;
|
|
CGU_FLOAT step = (highPosStep - lowPosStep) / 3; // using (dwNumChannels=4 - 1);
|
|
CGU_FLOAT step_h = step * (CGU_FLOAT)0.5;
|
|
CGU_FLOAT rstep = (CGU_FLOAT)1.0f / step;
|
|
|
|
for (CGU_UINT32 i = 0; i < dwUniqueColors; i++)
|
|
{
|
|
CGU_FLOAT v;
|
|
// Work out which value in the block this select
|
|
CGU_FLOAT del;
|
|
|
|
if ((del = Prj[i] - lowPosStep) <= 0)
|
|
v = lowPosStep;
|
|
else if (Prj[i] - highPosStep >= 0)
|
|
v = highPosStep;
|
|
else
|
|
v = cmp_floor((del + step_h) * rstep) * step + lowPosStep;
|
|
|
|
// And accumulate the error
|
|
CGU_FLOAT d = (Prj[i] - v);
|
|
d *= d;
|
|
CGU_FLOAT err = PreMRep[i] * d + PrjErr[i];
|
|
error += err;
|
|
if (StepErr < error)
|
|
{
|
|
error = StepErr;
|
|
break;
|
|
}
|
|
}
|
|
return error;
|
|
}
|
|
|
|
|
|
CMP_STATIC CMP_EndPoints cgu_CompressRGBBlockX( CMP_IN CGU_Vec3f BlkInBGRf_UV[BLOCK_SIZE_4X4],
|
|
CMP_IN CGU_FLOAT Rpt[BLOCK_SIZE_4X4],
|
|
CMP_IN CGU_UINT32 dwUniqueColors,
|
|
CMP_IN CGU_Vec3f channelWeightsBGR,
|
|
CMP_IN CGU_BOOL b3DRefinement
|
|
)
|
|
{
|
|
CMP_UNUSED(channelWeightsBGR);
|
|
CMP_UNUSED(b3DRefinement);
|
|
CGU_FLOAT ALIGN_16 Prj0[BLOCK_SIZE_4X4];
|
|
CGU_FLOAT ALIGN_16 Prj[BLOCK_SIZE_4X4];
|
|
CGU_FLOAT ALIGN_16 PrjErr[BLOCK_SIZE_4X4];
|
|
CGU_FLOAT ALIGN_16 RmpIndxs[BLOCK_SIZE_4X4];
|
|
|
|
CGU_Vec3f LineDirG;
|
|
CGU_Vec3f LineDir;
|
|
CGU_FLOAT LineDir0[NUM_CHANNELS];
|
|
CGU_Vec3f BlkUV[BLOCK_SIZE_4X4];
|
|
CGU_Vec3f BlkSh[BLOCK_SIZE_4X4];
|
|
CGU_Vec3f Mdl;
|
|
|
|
CGU_Vec3f rsltC0;
|
|
CGU_Vec3f rsltC1;
|
|
CGU_Vec3f PosG0 = {0.0f, 0.0f, 0.0f};
|
|
CGU_Vec3f PosG1 = {0.0f, 0.0f, 0.0f};
|
|
CGU_UINT32 i;
|
|
|
|
for (i = 0; i < dwUniqueColors; i++)
|
|
{
|
|
BlkUV[i] = BlkInBGRf_UV[i];
|
|
}
|
|
|
|
// if not more then 2 different colors, we've done
|
|
if (dwUniqueColors <= 2)
|
|
{
|
|
rsltC0 = BlkInBGRf_UV[0] * 255.0f;
|
|
rsltC1 = BlkInBGRf_UV[dwUniqueColors - 1] * 255.0f;
|
|
}
|
|
else
|
|
{
|
|
// This is our first attempt to find an axis we will go along.
|
|
// The cumulation is done to find a line minimizing the MSE from the
|
|
// input 3D points.
|
|
|
|
// While trying to find the axis we found that the diameter of the input
|
|
// set is quite small. Do not bother.
|
|
|
|
// FindAxisIsSmall(BlkSh, LineDir0, Mdl, Blk, Rpt,dwUniqueColors);
|
|
{
|
|
CGU_UINT32 ii;
|
|
CGU_UINT32 jj;
|
|
CGU_UINT32 kk;
|
|
|
|
// These vars cannot be Vec3 as index to them are varying
|
|
CGU_FLOAT Crrl[NUM_CHANNELS];
|
|
CGU_FLOAT RGB2[NUM_CHANNELS];
|
|
|
|
LineDir0[0] = LineDir0[1] = LineDir0[2] = RGB2[0] = RGB2[1] = RGB2[2] = Crrl[0] = Crrl[1] = Crrl[2] = Mdl.x = Mdl.y = Mdl.z = 0.f;
|
|
|
|
// sum position of all points
|
|
CGU_FLOAT fNumPoints = 0.0f;
|
|
for (ii = 0; ii < dwUniqueColors; ii++)
|
|
{
|
|
Mdl.x += BlkUV[ii].x * Rpt[ii];
|
|
Mdl.y += BlkUV[ii].y * Rpt[ii];
|
|
Mdl.z += BlkUV[ii].z * Rpt[ii];
|
|
fNumPoints += Rpt[ii];
|
|
}
|
|
|
|
// and then average to calculate center coordinate of block
|
|
Mdl /= fNumPoints;
|
|
|
|
for (ii = 0; ii < dwUniqueColors; ii++)
|
|
{
|
|
// calculate output block as offsets around block center
|
|
BlkSh[ii] = BlkUV[ii] - Mdl;
|
|
|
|
// compute correlation matrix
|
|
// RGB2 = sum of ((distance from point from center) squared)
|
|
RGB2[0] += BlkSh[ii].x * BlkSh[ii].x * Rpt[ii];
|
|
RGB2[1] += BlkSh[ii].y * BlkSh[ii].y * Rpt[ii];
|
|
RGB2[2] += BlkSh[ii].z * BlkSh[ii].z * Rpt[ii];
|
|
|
|
Crrl[0] += BlkSh[ii].x * BlkSh[ii].y * Rpt[ii];
|
|
Crrl[1] += BlkSh[ii].y * BlkSh[ii].z * Rpt[ii];
|
|
Crrl[2] += BlkSh[ii].z * BlkSh[ii].x * Rpt[ii];
|
|
}
|
|
|
|
// if set's diameter is small
|
|
CGU_UINT32 i0 = 0, i1 = 1;
|
|
CGU_FLOAT mxRGB2 = 0.0f;
|
|
|
|
CGU_FLOAT fEPS = fNumPoints * EPS;
|
|
for (kk = 0, jj = 0; jj < 3; jj++)
|
|
{
|
|
if (RGB2[jj] >= fEPS)
|
|
kk++;
|
|
else
|
|
RGB2[jj] = 0.0f;
|
|
|
|
if (mxRGB2 < RGB2[jj])
|
|
{
|
|
mxRGB2 = RGB2[jj];
|
|
i0 = jj;
|
|
}
|
|
}
|
|
|
|
CGU_FLOAT fEPS2 = fNumPoints * EPS2;
|
|
CGU_BOOL AxisIsSmall;
|
|
|
|
AxisIsSmall = (RGB2[0] < fEPS2);
|
|
AxisIsSmall = AxisIsSmall && (RGB2[1] < fEPS2);
|
|
AxisIsSmall = AxisIsSmall && (RGB2[2] < fEPS2);
|
|
|
|
// all are very small to avoid division on the small determinant
|
|
if (AxisIsSmall)
|
|
{
|
|
rsltC0 = BlkInBGRf_UV[0] * 255.0f;
|
|
rsltC1 = BlkInBGRf_UV[dwUniqueColors - 1] * 255.0f;
|
|
}
|
|
else
|
|
{
|
|
// !AxisIsSmall
|
|
if (kk == 1) // really only 1 dimension
|
|
LineDir0[i0] = 1.;
|
|
else if (kk == 2)
|
|
{ // really only 2 dimensions
|
|
i1 = (RGB2[(i0 + 1) % 3] > 0.f) ? (i0 + 1) % 3 : (i0 + 2) % 3;
|
|
CGU_FLOAT Crl = (i1 == (i0 + 1) % 3) ? Crrl[i0] : Crrl[(i0 + 2) % 3];
|
|
LineDir0[i1] = Crl / RGB2[i0];
|
|
LineDir0[i0] = 1.;
|
|
}
|
|
else
|
|
{
|
|
CGU_FLOAT maxDet = 100000.f;
|
|
CGU_FLOAT Cs[3];
|
|
// select max det for precision
|
|
for (jj = 0; jj < 3; jj++)
|
|
{
|
|
// 3 = nDimensions
|
|
CGU_FLOAT Det = RGB2[jj] * RGB2[(jj + 1) % 3] - Crrl[jj] * Crrl[jj];
|
|
Cs[jj] = cmp_fabs(Crrl[jj] / sqrt(RGB2[jj] * RGB2[(jj + 1) % 3]));
|
|
if (maxDet < Det)
|
|
{
|
|
maxDet = Det;
|
|
i0 = jj;
|
|
}
|
|
}
|
|
|
|
// inverse correl matrix
|
|
// -- -- -- --
|
|
// | A B | | C -B |
|
|
// | B C | => | -B A |
|
|
// -- -- -- --
|
|
CGU_FLOAT mtrx1[2][2];
|
|
CGU_FLOAT vc1[2];
|
|
CGU_FLOAT vc[2];
|
|
vc1[0] = Crrl[(i0 + 2) % 3];
|
|
vc1[1] = Crrl[(i0 + 1) % 3];
|
|
// C
|
|
mtrx1[0][0] = RGB2[(i0 + 1) % 3];
|
|
// A
|
|
mtrx1[1][1] = RGB2[i0];
|
|
// -B
|
|
mtrx1[1][0] = mtrx1[0][1] = -Crrl[i0];
|
|
// find a solution
|
|
vc[0] = mtrx1[0][0] * vc1[0] + mtrx1[0][1] * vc1[1];
|
|
vc[1] = mtrx1[1][0] * vc1[0] + mtrx1[1][1] * vc1[1];
|
|
// normalize
|
|
vc[0] /= maxDet;
|
|
vc[1] /= maxDet;
|
|
// find a line direction vector
|
|
LineDir0[i0] = 1.;
|
|
LineDir0[(i0 + 1) % 3] = 1.;
|
|
LineDir0[(i0 + 2) % 3] = vc[0] + vc[1];
|
|
}
|
|
|
|
// normalize direction vector
|
|
CGU_FLOAT Len = LineDir0[0] * LineDir0[0] + LineDir0[1] * LineDir0[1] + LineDir0[2] * LineDir0[2];
|
|
Len = sqrt(Len);
|
|
|
|
LineDir0[0] = (Len > 0.f) ? LineDir0[0] / Len : 0.0f;
|
|
LineDir0[1] = (Len > 0.f) ? LineDir0[1] / Len : 0.0f;
|
|
LineDir0[2] = (Len > 0.f) ? LineDir0[2] / Len : 0.0f;
|
|
}
|
|
} // FindAxisIsSmall
|
|
|
|
// GCC is being an awful being when it comes to goto-jumps.
|
|
// So please bear with this.
|
|
CGU_FLOAT ErrG = 10000000.f;
|
|
CGU_FLOAT PrjBnd0;
|
|
CGU_FLOAT PrjBnd1;
|
|
CGU_FLOAT ALIGN_16 PreMRep[BLOCK_SIZE_4X4];
|
|
|
|
LineDir.x = LineDir0[0];
|
|
LineDir.y = LineDir0[1];
|
|
LineDir.z = LineDir0[2];
|
|
|
|
// Here is the main loop.
|
|
// 1. Project input set on the axis in consideration.
|
|
// 2. Run 1 dimensional search (see scalar case) to find an (sub) optimal pair of end points.
|
|
// 3. Compute the vector of indexes (or clusters) for the current approximate ramp.
|
|
// 4. Present our color channels as 3 16DIM vectors.
|
|
// 5. Find closest approximation of each of 16DIM color vector with the projection of the 16DIM index vector.
|
|
// 6. Plug the projections as a new directional vector for the axis.
|
|
// 7. Goto 1.
|
|
// D - is 16 dim "index" vector (or 16 DIM vector of indexes - {0, 1/3,2/3, 0, ...,}, but shifted and normalized).
|
|
// Ci - is a 16 dim vector of color i. for each Ci find a scalar Ai such that (Ai * D - Ci) (Ai * D - Ci) -> min ,
|
|
// i.e distance between vector AiD and C is min. You can think of D as a unit interval(vector) "clusterizer", and Ai is a scale
|
|
// you need to apply to the clusterizer to approximate the Ci vector instead of the unit vector.
|
|
// Solution is
|
|
// Ai = (D . Ci) / (D . D); . - is a dot product.
|
|
// in 3 dim space Ai(s) represent a line direction, along which
|
|
// we again try to find (sub)optimal quantizer.
|
|
// That's what our for(;;) loop is about.
|
|
for (;;)
|
|
{
|
|
// 1. Project input set on the axis in consideration.
|
|
// From Foley & Van Dam: Closest point of approach of a line (P + v) to a
|
|
// point (R) is
|
|
// P + ((R-P).v) / (v.v))v
|
|
// The distance along v is therefore (R-P).v / (v.v)
|
|
// (v.v) is 1 if v is a unit vector.
|
|
//
|
|
PrjBnd0 = 1000.0f;
|
|
PrjBnd1 = -1000.0f;
|
|
for (i = 0; i < BLOCK_SIZE_4X4; i++)
|
|
Prj0[i] = Prj[i] = PrjErr[i] = PreMRep[i] = 0.f;
|
|
|
|
for (i = 0; i < dwUniqueColors; i++)
|
|
{
|
|
Prj0[i] = Prj[i] = dot(BlkSh[i], LineDir);
|
|
PrjErr[i] = dot(BlkSh[i] - LineDir * Prj[i], BlkSh[i] - LineDir * Prj[i]);
|
|
PrjBnd0 = min(PrjBnd0, Prj[i]);
|
|
PrjBnd1 = max(PrjBnd1, Prj[i]);
|
|
}
|
|
|
|
// 2. Run 1 dimensional search (see scalar case) to find an (sub) optimal
|
|
// pair of end points.
|
|
|
|
// min and max of the search interval
|
|
CGU_FLOAT Scl0;
|
|
CGU_FLOAT Scl1;
|
|
Scl0 = PrjBnd0 - (PrjBnd1 - PrjBnd0) * 0.125f;
|
|
Scl1 = PrjBnd1 + (PrjBnd1 - PrjBnd0) * 0.125f;
|
|
|
|
// compute scaling factor to scale down the search interval to [0.,1]
|
|
const CGU_FLOAT Scl2 = (Scl1 - Scl0) * (Scl1 - Scl0);
|
|
const CGU_FLOAT overScl = 1.f / (Scl1 - Scl0);
|
|
|
|
for (i = 0; i < dwUniqueColors; i++)
|
|
{
|
|
// scale them
|
|
Prj[i] = (Prj[i] - Scl0) * overScl;
|
|
// premultiply the scale square to plug into error computation later
|
|
PreMRep[i] = Rpt[i] * Scl2;
|
|
}
|
|
|
|
// scale first approximation of end points
|
|
PrjBnd0 = (PrjBnd0 - Scl0) * overScl;
|
|
PrjBnd1 = (PrjBnd1 - Scl0) * overScl;
|
|
|
|
CGU_FLOAT StepErr = MAX_ERROR;
|
|
|
|
// search step
|
|
CGU_FLOAT searchStep = 0.025f;
|
|
|
|
// low Start/End; high Start/End
|
|
const CGU_FLOAT lowStartEnd = (PrjBnd0 - 2.f * searchStep > 0.f) ? PrjBnd0 - 2.f * searchStep : 0.f;
|
|
const CGU_FLOAT highStartEnd = (PrjBnd1 + 2.f * searchStep < 1.f) ? PrjBnd1 + 2.f * searchStep : 1.f;
|
|
|
|
// find the best endpoints
|
|
CGU_FLOAT Pos0 = 0;
|
|
CGU_FLOAT Pos1 = 0;
|
|
CGU_FLOAT lowPosStep, highPosStep;
|
|
CGU_FLOAT err;
|
|
|
|
int l, h;
|
|
for (l = 0, lowPosStep = lowStartEnd; l < 8; l++, lowPosStep += searchStep)
|
|
{
|
|
for (h = 0, highPosStep = highStartEnd; h < 8; h++, highPosStep -= searchStep)
|
|
{
|
|
// compute an error for the current pair of end points.
|
|
err = cgu_getRampErr(Prj, PrjErr, PreMRep, StepErr, lowPosStep, highPosStep, dwUniqueColors);
|
|
|
|
if (err < StepErr)
|
|
{
|
|
// save better result
|
|
StepErr = err;
|
|
Pos0 = lowPosStep;
|
|
Pos1 = highPosStep;
|
|
}
|
|
}
|
|
}
|
|
|
|
// inverse the scaling
|
|
Pos0 = Pos0 * (Scl1 - Scl0) + Scl0;
|
|
Pos1 = Pos1 * (Scl1 - Scl0) + Scl0;
|
|
|
|
// did we find somthing better from the previous run?
|
|
if (StepErr + 0.001 < ErrG)
|
|
{
|
|
// yes, remember it
|
|
ErrG = StepErr;
|
|
LineDirG = LineDir;
|
|
|
|
PosG0.x = Pos0;
|
|
PosG0.y = Pos0;
|
|
PosG0.z = Pos0;
|
|
PosG1.x = Pos1;
|
|
PosG1.y = Pos1;
|
|
PosG1.z = Pos1;
|
|
|
|
// 3. Compute the vector of indexes (or clusters) for the current
|
|
// approximate ramp.
|
|
// indexes
|
|
const CGU_FLOAT step = (Pos1 - Pos0) / 3.0f; // (dwNumChannels=4 - 1);
|
|
const CGU_FLOAT step_h = step * (CGU_FLOAT)0.5;
|
|
const CGU_FLOAT rstep = (CGU_FLOAT)1.0f / step;
|
|
const CGU_FLOAT overBlkTp = 1.f / 3.0f; // (dwNumChannels=4 - 1);
|
|
|
|
// here the index vector is computed,
|
|
// shifted and normalized
|
|
CGU_FLOAT indxAvrg = 3.0f / 2.0f; // (dwNumChannels=4 - 1);
|
|
|
|
for (i = 0; i < dwUniqueColors; i++)
|
|
{
|
|
CGU_FLOAT del;
|
|
// CGU_UINT32 n = (CGU_UINT32)((b - _min_ex + (step*0.5f)) * rstep);
|
|
if ((del = Prj0[i] - Pos0) <= 0)
|
|
RmpIndxs[i] = 0.f;
|
|
else if (Prj0[i] - Pos1 >= 0)
|
|
RmpIndxs[i] = 3.0f; // (dwNumChannels=4 - 1);
|
|
else
|
|
RmpIndxs[i] = cmp_floor((del + step_h) * rstep);
|
|
// shift and normalization
|
|
RmpIndxs[i] = (RmpIndxs[i] - indxAvrg) * overBlkTp;
|
|
}
|
|
|
|
// 4. Present our color channels as 3 16 DIM vectors.
|
|
// 5. Find closest aproximation of each of 16DIM color vector with the
|
|
// pojection of the 16DIM index vector.
|
|
CGU_Vec3f Crs = {0.0f, 0.0f, 0.0f};
|
|
CGU_FLOAT Len = 0.0f;
|
|
|
|
for (i = 0; i < dwUniqueColors; i++)
|
|
{
|
|
const CGU_FLOAT PreMlt = RmpIndxs[i] * Rpt[i];
|
|
Len += RmpIndxs[i] * PreMlt;
|
|
Crs.x += BlkSh[i].x * PreMlt;
|
|
Crs.y += BlkSh[i].y * PreMlt;
|
|
Crs.z += BlkSh[i].z * PreMlt;
|
|
}
|
|
|
|
LineDir.x = LineDir.y = LineDir.z = 0.0f;
|
|
if (Len > 0.0f)
|
|
{
|
|
CGU_FLOAT Len2;
|
|
LineDir = Crs / Len;
|
|
// 6. Plug the projections as a new directional vector for the axis.
|
|
// 7. Goto 1.
|
|
Len2 = dot(LineDir, LineDir); // LineDir.x * LineDir.x + LineDir.y * LineDir.y + LineDir.z * LineDir.z;
|
|
Len2 = sqrt(Len2);
|
|
LineDir /= Len2;
|
|
}
|
|
}
|
|
else // We was not able to find anything better. Drop out.
|
|
break;
|
|
}
|
|
|
|
// inverse transform to find end-points of 3-color ramp
|
|
rsltC0 = (PosG0 * LineDirG + Mdl) * 255.f;
|
|
rsltC1 = (PosG1 * LineDirG + Mdl) * 255.f;
|
|
} // !isDone
|
|
|
|
// We've dealt with (almost) unrestricted full precision realm.
|
|
// Now back digital world.
|
|
|
|
// round the end points to make them look like compressed ones
|
|
CGU_Vec3f inpRmpEndPts0 = {0.0f, 255.0f, 0.0f};
|
|
CGU_Vec3f inpRmpEndPts1 = {0.0f, 255.0f, 0.0f};
|
|
CGU_Vec3f Fctrs0 = {8.0f, 4.0f, 8.0f}; //(1 << (PIX_GRID - BG)); x (1 << (PIX_GRID - GG)); y (1 << (PIX_GRID - RG)); z
|
|
CGU_Vec3f Fctrs1 = {32.0f, 64.0f, 32.0f}; //(CGU_FLOAT)(1 << RG); z (CGU_FLOAT)(1 << GG); y (CGU_FLOAT)(1 << BG); x
|
|
CGU_FLOAT _Min = 0.0f;
|
|
CGU_FLOAT _Max = 255.0f;
|
|
|
|
{
|
|
// MkRmpOnGrid(inpRmpEndPts, rsltC, _Min, _Max);
|
|
|
|
inpRmpEndPts0 = cmp_floorVec3f(rsltC0);
|
|
|
|
if (inpRmpEndPts0.x <= _Min)
|
|
inpRmpEndPts0.x = _Min;
|
|
else
|
|
{
|
|
inpRmpEndPts0.x += cmp_floor(128.f / Fctrs1.x) - cmp_floor(inpRmpEndPts0.x / Fctrs1.x);
|
|
inpRmpEndPts0.x = min(inpRmpEndPts0.x, _Max);
|
|
}
|
|
if (inpRmpEndPts0.y <= _Min)
|
|
inpRmpEndPts0.y = _Min;
|
|
else
|
|
{
|
|
inpRmpEndPts0.y += cmp_floor(128.f / Fctrs1.y) - cmp_floor(inpRmpEndPts0.y / Fctrs1.y);
|
|
inpRmpEndPts0.y = min(inpRmpEndPts0.y, _Max);
|
|
}
|
|
if (inpRmpEndPts0.z <= _Min)
|
|
inpRmpEndPts0.z = _Min;
|
|
else
|
|
{
|
|
inpRmpEndPts0.z += cmp_floor(128.f / Fctrs1.z) - cmp_floor(inpRmpEndPts0.z / Fctrs1.z);
|
|
inpRmpEndPts0.z = min(inpRmpEndPts0.z, _Max);
|
|
}
|
|
|
|
inpRmpEndPts0 = cmp_floorVec3f(inpRmpEndPts0 / Fctrs0) * Fctrs0;
|
|
|
|
inpRmpEndPts1 = cmp_floorVec3f(rsltC1);
|
|
if (inpRmpEndPts1.x <= _Min)
|
|
inpRmpEndPts1.x = _Min;
|
|
else
|
|
{
|
|
inpRmpEndPts1.x += cmp_floor(128.f / Fctrs1.x) - cmp_floor(inpRmpEndPts1.x / Fctrs1.x);
|
|
inpRmpEndPts1.x = min(inpRmpEndPts1.x, _Max);
|
|
}
|
|
if (inpRmpEndPts1.y <= _Min)
|
|
inpRmpEndPts1.y = _Min;
|
|
else
|
|
{
|
|
inpRmpEndPts1.y += cmp_floor(128.f / Fctrs1.y) - cmp_floor(inpRmpEndPts1.y / Fctrs1.y);
|
|
inpRmpEndPts1.y = min(inpRmpEndPts1.y, _Max);
|
|
}
|
|
if (inpRmpEndPts1.z <= _Min)
|
|
inpRmpEndPts1.z = _Min;
|
|
else
|
|
{
|
|
inpRmpEndPts1.z += cmp_floor(128.f / Fctrs1.z) - cmp_floor(inpRmpEndPts1.z / Fctrs1.z);
|
|
inpRmpEndPts1.z = min(inpRmpEndPts1.z, _Max);
|
|
}
|
|
|
|
inpRmpEndPts1 = cmp_floorVec3f(inpRmpEndPts1 / Fctrs0) * Fctrs0;
|
|
} // MkRmpOnGrid
|
|
|
|
CMP_EndPoints EndPoints;
|
|
EndPoints.Color0 = inpRmpEndPts0;
|
|
EndPoints.Color1 = inpRmpEndPts1;
|
|
|
|
return EndPoints;
|
|
}
|
|
|
|
CMP_STATIC CMP_EndPoints cgu_MkRmpOnGridBGR(CMP_IN CGU_Vec3f rsltC0,
|
|
CMP_IN CGU_Vec3f rsltC1,
|
|
CMP_IN CGU_UINT32 nRedBits,
|
|
CMP_IN CGU_UINT32 nGreenBits,
|
|
CMP_IN CGU_UINT32 nBlueBits)
|
|
{
|
|
CGU_Vec3f inpRmpEndPts0 = {0.0f, 255.0f, 0.0f};
|
|
CGU_Vec3f inpRmpEndPts1 = {0.0f, 255.0f, 0.0f};
|
|
CGU_Vec3f Fctrs0 = {8.0f, 4.0f, 8.0f};
|
|
CGU_Vec3f Fctrs1 = {32.0f, 64.0f, 32.0f};
|
|
CGU_FLOAT _Min = 0.0f;
|
|
CGU_FLOAT _Max = 255.0f;
|
|
|
|
// user override 565 default setting
|
|
if ((nRedBits!=5)||(nGreenBits!=6)||(nBlueBits!=5)) {
|
|
Fctrs1[RC] = (CGU_FLOAT)(1 << nRedBits);
|
|
Fctrs1[GC] = (CGU_FLOAT)(1 << nGreenBits);
|
|
Fctrs1[BC] = (CGU_FLOAT)(1 << nBlueBits);
|
|
Fctrs0[RC] = (CGU_FLOAT)(1 << (PIX_GRID-nRedBits));
|
|
Fctrs0[GC] = (CGU_FLOAT)(1 << (PIX_GRID-nGreenBits));
|
|
Fctrs0[BC] = (CGU_FLOAT)(1 << (PIX_GRID-nBlueBits));
|
|
}
|
|
|
|
inpRmpEndPts0 = cmp_floorVec3f(rsltC0);
|
|
|
|
if (inpRmpEndPts0.x <= _Min)
|
|
inpRmpEndPts0.x = _Min;
|
|
else
|
|
{
|
|
inpRmpEndPts0.x += cmp_floor(128.f / Fctrs1.x) - cmp_floor(inpRmpEndPts0.x / Fctrs1.x);
|
|
inpRmpEndPts0.x = cmp_minf(inpRmpEndPts0.x, _Max);
|
|
}
|
|
if (inpRmpEndPts0.y <= _Min)
|
|
inpRmpEndPts0.y = _Min;
|
|
else
|
|
{
|
|
inpRmpEndPts0.y += cmp_floor(128.f / Fctrs1.y) - cmp_floor(inpRmpEndPts0.y / Fctrs1.y);
|
|
inpRmpEndPts0.y = cmp_minf(inpRmpEndPts0.y, _Max);
|
|
}
|
|
if (inpRmpEndPts0.z <= _Min)
|
|
inpRmpEndPts0.z = _Min;
|
|
else
|
|
{
|
|
inpRmpEndPts0.z += cmp_floor(128.f / Fctrs1.z) - cmp_floor(inpRmpEndPts0.z / Fctrs1.z);
|
|
inpRmpEndPts0.z = cmp_minf(inpRmpEndPts0.z, _Max);
|
|
}
|
|
|
|
inpRmpEndPts0 = cmp_floorVec3f(inpRmpEndPts0 / Fctrs0) * Fctrs0;
|
|
|
|
inpRmpEndPts1 = cmp_floorVec3f(rsltC1);
|
|
if (inpRmpEndPts1.x <= _Min)
|
|
inpRmpEndPts1.x = _Min;
|
|
else
|
|
{
|
|
inpRmpEndPts1.x += cmp_floor(128.f / Fctrs1.x) - cmp_floor(inpRmpEndPts1.x / Fctrs1.x);
|
|
inpRmpEndPts1.x = cmp_minf(inpRmpEndPts1.x, _Max);
|
|
}
|
|
if (inpRmpEndPts1.y <= _Min)
|
|
inpRmpEndPts1.y = _Min;
|
|
else
|
|
{
|
|
inpRmpEndPts1.y += cmp_floor(128.f / Fctrs1.y) - cmp_floor(inpRmpEndPts1.y / Fctrs1.y);
|
|
inpRmpEndPts1.y = cmp_minf(inpRmpEndPts1.y, _Max);
|
|
}
|
|
if (inpRmpEndPts1.z <= _Min)
|
|
inpRmpEndPts1.z = _Min;
|
|
else
|
|
{
|
|
inpRmpEndPts1.z += cmp_floor(128.f / Fctrs1.z) - cmp_floor(inpRmpEndPts1.z / Fctrs1.z);
|
|
inpRmpEndPts1.z = cmp_minf(inpRmpEndPts1.z, _Max);
|
|
}
|
|
|
|
inpRmpEndPts1 = cmp_floorVec3f(inpRmpEndPts1 / Fctrs0) * Fctrs0;
|
|
|
|
CMP_EndPoints EndPoints;
|
|
EndPoints.Color0 = inpRmpEndPts0;
|
|
EndPoints.Color1 = inpRmpEndPts1;
|
|
|
|
return EndPoints;
|
|
|
|
} // MkRmpOnGrid
|
|
|
|
|
|
//===================================================================
|
|
// Replaces CompressBlockBC1_RGBA_Internal()
|
|
// if ((errLQ > 0.0f) && (fquality > CMP_QUALITY2)) code block
|
|
//===================================================================
|
|
CMP_STATIC CGU_Vec2ui cgu_CompRGBBlock(CMP_IN CGU_Vec4f src_imageNorm[BLOCK_SIZE_4X4],
|
|
CMP_IN CMP_BC15Options BC15Options)
|
|
{
|
|
//CGU_FLOAT errLQ = 1e6f;
|
|
CGU_UINT32 m_nRefinementSteps = BC15Options.m_nRefinementSteps;
|
|
CGU_UINT32 dwAlphaThreshold = BC15Options.m_nAlphaThreshold;
|
|
CGU_Vec3f channelWeights = {BC15Options.m_fChannelWeights[0],BC15Options.m_fChannelWeights[1],BC15Options.m_fChannelWeights[2]};
|
|
CGU_BOOL isSRGB = BC15Options.m_bIsSRGB;
|
|
|
|
CGU_Vec3f rgbBlock_normal[BLOCK_SIZE_4X4];
|
|
CGU_UINT32 nCmpIndices = 0;
|
|
CGU_UINT32 c0, c1;
|
|
// High Quality
|
|
CMP_EndPoints EndPoints = {{0, 0, 0xFF}, {0, 0, 0xFF}};
|
|
CGU_UINT32 i;
|
|
|
|
CGU_FLOAT ALIGN_16 Rpt[BLOCK_SIZE_4X4];
|
|
CGU_UINT32 pcIndices = 0;
|
|
|
|
m_nRefinementSteps = 0;
|
|
|
|
CGU_Vec3f BlkInBGRf_UV[BLOCK_SIZE_4X4]; // Normalized Block Input (0..1) in BGR channel format
|
|
// Default inidices & endpoints for Transparent Block
|
|
CGU_Vec3ui nEndpoints0 = {0, 0, 0}; // Endpoints are stored BGR as x,y,z
|
|
CGU_Vec3ui nEndpoints1 = {0xFF, 0xFF, 0xFF}; // Endpoints are stored BGR as x,y,z
|
|
|
|
for (i = 0; i < BLOCK_SIZE_4X4; i++)
|
|
{
|
|
Rpt[i] = 0.0f;
|
|
}
|
|
|
|
//===============================================================
|
|
// Check if we have more then 2 colors and process Alpha block
|
|
CGU_UINT32 dwColors = 0;
|
|
CGU_UINT32 dwBlk[BLOCK_SIZE_4X4];
|
|
CGU_UINT32 R, G, B, A;
|
|
for (i = 0; i < BLOCK_SIZE_4X4; i++)
|
|
{
|
|
// Do any color conversion prior to processing the block
|
|
rgbBlock_normal[i] = isSRGB ? cmp_linearToSrgb(src_imageNorm[i].rgb) : src_imageNorm[i].rgb;
|
|
|
|
R = (CGU_UINT32)(rgbBlock_normal[i].x * 255.0f);
|
|
G = (CGU_UINT32)(rgbBlock_normal[i].y * 255.0f);
|
|
B = (CGU_UINT32)(rgbBlock_normal[i].z * 255.0f);
|
|
|
|
//if (dwAlphaThreshold > 0)
|
|
// A = (CGU_UINT32)src_imageNorm[i].w * 255.0f;
|
|
//else
|
|
A = 255;
|
|
|
|
// Punch Through Alpha in BC1 Codec (1 bit alpha)
|
|
//if ((dwAlphaThreshold == 0) || (A >= dwAlphaThreshold))
|
|
//{
|
|
// copy to local RGB data and have alpha set to 0xFF
|
|
dwBlk[dwColors++] = A << 24 | R << 16 | G << 8 | B;
|
|
//}
|
|
}
|
|
|
|
if (!dwColors)
|
|
{
|
|
// All are colors transparent
|
|
EndPoints.Color0.x = EndPoints.Color0.y = EndPoints.Color0.z = 0.0f;
|
|
EndPoints.Color1.x = EndPoints.Color1.y = EndPoints.Color0.z = 255.0f;
|
|
nCmpIndices = 0xFFFFFFFF;
|
|
}
|
|
else
|
|
{
|
|
// We have colors to process
|
|
nCmpIndices = 0;
|
|
// Punch Through Alpha Support ToDo
|
|
// CGU_BOOL bHasAlpha = (dwColors != BLOCK_SIZE_4X4);
|
|
// bHasAlpha = bHasAlpha && (dwAlphaThreshold > 0); // valid for (dwNumChannels=4);
|
|
// if (bHasAlpha) {
|
|
// CGU_Vec2ui compBlock = {0xf800f800,0};
|
|
// return compBlock;
|
|
// }
|
|
|
|
// Here we are computing an unique number of sorted colors.
|
|
// For each unique value we compute the number of it appearences.
|
|
// qsort((void *)dwBlk, (size_t)dwColors, sizeof(CGU_UINT32), QSortIntCmp);
|
|
{
|
|
CGU_UINT32 j;
|
|
CMP_di what[BLOCK_SIZE_4X4];
|
|
|
|
for (i = 0; i < dwColors; i++)
|
|
{
|
|
what[i].index = i;
|
|
what[i].data = dwBlk[i];
|
|
}
|
|
|
|
CGU_UINT32 tmp_index;
|
|
CGU_UINT32 tmp_data;
|
|
|
|
for (i = 1; i < dwColors; i++)
|
|
{
|
|
for (j = i; j > 0; j--)
|
|
{
|
|
if (what[j - 1].data > what[j].data)
|
|
{
|
|
tmp_index = what[j].index;
|
|
tmp_data = what[j].data;
|
|
what[j].index = what[j - 1].index;
|
|
what[j].data = what[j - 1].data;
|
|
what[j - 1].index = tmp_index;
|
|
what[j - 1].data = tmp_data;
|
|
}
|
|
}
|
|
}
|
|
for (i = 0; i < dwColors; i++)
|
|
dwBlk[i] = what[i].data;
|
|
}
|
|
CGU_UINT32 new_p;
|
|
CGU_UINT32 dwBlkU[BLOCK_SIZE_4X4];
|
|
CGU_UINT32 dwUniqueColors = 0;
|
|
new_p = dwBlkU[0] = dwBlk[0];
|
|
Rpt[dwUniqueColors] = 1.f;
|
|
for (i = 1; i < dwColors; i++)
|
|
{
|
|
if (new_p != dwBlk[i])
|
|
{
|
|
dwUniqueColors++;
|
|
new_p = dwBlkU[dwUniqueColors] = dwBlk[i];
|
|
Rpt[dwUniqueColors] = 1.f;
|
|
}
|
|
else
|
|
Rpt[dwUniqueColors] += 1.f;
|
|
}
|
|
dwUniqueColors++;
|
|
|
|
// Simple case of only 2 colors to process
|
|
// no need for futher processing as lowest quality methods work best for this case
|
|
if (dwUniqueColors <= 2)
|
|
{
|
|
CGU_Vec3f rsltC0;
|
|
CGU_Vec3f rsltC1;
|
|
rsltC0.r = rgbBlock_normal[0].b * 255.0f;
|
|
rsltC0.g = rgbBlock_normal[0].g * 255.0f;
|
|
rsltC0.b = rgbBlock_normal[0].r * 255.0f;
|
|
rsltC1.r = rgbBlock_normal[dwUniqueColors - 1].b * 255.0f;
|
|
rsltC1.g = rgbBlock_normal[dwUniqueColors - 1].g * 255.0f;
|
|
rsltC1.b = rgbBlock_normal[dwUniqueColors - 1].r * 255.0f;
|
|
EndPoints = cgu_MkRmpOnGridBGR(rsltC0, rsltC1,5, 6, 5);
|
|
}
|
|
else
|
|
{
|
|
// switch from int range back to UV floats
|
|
for (i = 0; i < dwUniqueColors; i++)
|
|
{
|
|
R = (dwBlkU[i] >> 16) & 0xff;
|
|
G = (dwBlkU[i] >> 8) & 0xff;
|
|
B = (dwBlkU[i] >> 0) & 0xff;
|
|
BlkInBGRf_UV[i].z = (CGU_FLOAT)R / 255.0f;
|
|
BlkInBGRf_UV[i].y = (CGU_FLOAT)G / 255.0f;
|
|
BlkInBGRf_UV[i].x = (CGU_FLOAT)B / 255.0f;
|
|
}
|
|
|
|
CGU_Vec3f channelWeightsBGR;
|
|
channelWeightsBGR.x = channelWeights.z;
|
|
channelWeightsBGR.y = channelWeights.y;
|
|
channelWeightsBGR.z = channelWeights.x;
|
|
|
|
EndPoints = cgu_CompressRGBBlockX(BlkInBGRf_UV, Rpt, dwUniqueColors, channelWeightsBGR, m_nRefinementSteps);
|
|
}
|
|
} // colors
|
|
|
|
//===================================================================
|
|
// Process Cluster INPUT is constant EndPointsf OUTPUT is pcIndices
|
|
//===================================================================
|
|
if (nCmpIndices == 0)
|
|
{
|
|
R = (CGU_UINT32)(EndPoints.Color0.z);
|
|
G = (CGU_UINT32)(EndPoints.Color0.y);
|
|
B = (CGU_UINT32)(EndPoints.Color0.x);
|
|
CGU_INT32 cluster0 = cmp_constructColor(R, G, B);
|
|
|
|
R = (CGU_UINT32)(EndPoints.Color1.z);
|
|
G = (CGU_UINT32)(EndPoints.Color1.y);
|
|
B = (CGU_UINT32)(EndPoints.Color1.x);
|
|
CGU_INT32 cluster1 = cmp_constructColor(R, G, B);
|
|
|
|
CGU_Vec3f InpRmp[NUM_ENDPOINTS];
|
|
if ((cluster0 <= cluster1) // valid for 4 channels
|
|
// || (cluster0 > cluster1) // valid for 3 channels
|
|
)
|
|
{
|
|
// inverse endpoints
|
|
InpRmp[0] = EndPoints.Color1;
|
|
InpRmp[1] = EndPoints.Color0;
|
|
}
|
|
else
|
|
{
|
|
InpRmp[0] = EndPoints.Color0;
|
|
InpRmp[1] = EndPoints.Color1;
|
|
}
|
|
|
|
CGU_Vec3f srcblockBGR[BLOCK_SIZE_4X4];
|
|
CGU_FLOAT srcblockA[BLOCK_SIZE_4X4];
|
|
|
|
// Swizzle the source RGB to BGR for processing
|
|
for (i = 0; i < BLOCK_SIZE_4X4; i++)
|
|
{
|
|
srcblockBGR[i].z = rgbBlock_normal[i].x * 255.0f;
|
|
srcblockBGR[i].y = rgbBlock_normal[i].y * 255.0f;
|
|
srcblockBGR[i].x = rgbBlock_normal[i].z * 255.0f;
|
|
srcblockA[i] = 255.0f;
|
|
if (dwAlphaThreshold > 0)
|
|
{
|
|
CGU_UINT32 alpha = (CGU_UINT32)src_imageNorm[i].w*255.0f;
|
|
if (alpha >= dwAlphaThreshold)
|
|
srcblockA[i] = alpha;
|
|
}
|
|
}
|
|
|
|
// input ramp is on the coarse grid
|
|
// make ramp endpoints the way they'll going to be decompressed
|
|
CGU_Vec3f InpRmpL[NUM_ENDPOINTS];
|
|
CGU_Vec3f Fctrs = {32.0F, 64.0F, 32.0F}; // 1 << RG,1 << GG,1 << BG
|
|
|
|
{
|
|
// ConstantRamp = MkWkRmpPts(InpRmpL, InpRmp);
|
|
InpRmpL[0] = InpRmp[0] + cmp_floorVec3f(InpRmp[0] / Fctrs);
|
|
InpRmpL[0] = cmp_clampVec3f(InpRmpL[0], 0.0f, 255.0f);
|
|
InpRmpL[1] = InpRmp[1] + cmp_floorVec3f(InpRmp[1] / Fctrs);
|
|
InpRmpL[1] = cmp_clampVec3f(InpRmpL[1], 0.0f, 255.0f);
|
|
} // MkWkRmpPts
|
|
|
|
// build ramp
|
|
CGU_Vec3f LerpRmp[4];
|
|
CGU_Vec3f offset = {1.0f, 1.0f, 1.0f};
|
|
{
|
|
//BldRmp(Rmp, InpRmpL, dwNumChannels);
|
|
// linear interpolate end points to get the ramp
|
|
LerpRmp[0] = InpRmpL[0];
|
|
LerpRmp[3] = InpRmpL[1];
|
|
LerpRmp[1] = cmp_floorVec3f((InpRmpL[0] * 2.0f + LerpRmp[3] + offset) / 3.0f);
|
|
LerpRmp[2] = cmp_floorVec3f((InpRmpL[0] + LerpRmp[3] * 2.0f + offset) / 3.0f);
|
|
} // BldRmp
|
|
|
|
//=========================================================================
|
|
// Clusterize, Compute error and find DXTC indexes for the current cluster
|
|
//=========================================================================
|
|
{
|
|
// Clusterize
|
|
CGU_UINT32 alpha;
|
|
|
|
// For each colour in the original block assign it
|
|
// to the closest cluster and compute the cumulative error
|
|
for (i = 0; i < BLOCK_SIZE_4X4; i++)
|
|
{
|
|
alpha = (CGU_UINT32)srcblockA[i];
|
|
if ((dwAlphaThreshold > 0) && alpha == 0)
|
|
{ //*((CGU_DWORD *)&_Blk[i][AC]) == 0)
|
|
pcIndices |= cmp_set2Bit32(4, i); // dwNumChannels 3 or 4 (default is 4)
|
|
}
|
|
else
|
|
{
|
|
CGU_FLOAT shortest = 99999999999.f;
|
|
CGU_UINT8 shortestIndex = 0;
|
|
|
|
CGU_Vec3f channelWeightsBGR;
|
|
channelWeightsBGR.x = channelWeights.z;
|
|
channelWeightsBGR.y = channelWeights.y;
|
|
channelWeightsBGR.z = channelWeights.x;
|
|
|
|
for (CGU_UINT8 rampindex = 0; rampindex < 4; rampindex++)
|
|
{
|
|
// r is either 1 or 4
|
|
// calculate the distance for each component
|
|
CGU_FLOAT distance =
|
|
dot(((srcblockBGR[i] - LerpRmp[rampindex]) * channelWeightsBGR), ((srcblockBGR[i] - LerpRmp[rampindex]) * channelWeightsBGR));
|
|
if (distance < shortest)
|
|
{
|
|
shortest = distance;
|
|
shortestIndex = rampindex;
|
|
}
|
|
}
|
|
|
|
// The total is a sum of (error += shortest)
|
|
// We have the index of the best cluster, so assign this in the block
|
|
// Reorder indices to match correct DXTC ordering
|
|
if (shortestIndex == 3) // dwNumChannels - 1
|
|
shortestIndex = 1;
|
|
else if (shortestIndex)
|
|
shortestIndex++;
|
|
pcIndices |= cmp_set2Bit32(shortestIndex, i);
|
|
}
|
|
} // BLOCK_SIZE_4X4
|
|
} // Clusterize
|
|
} // Process Cluster
|
|
|
|
//==============================================================
|
|
// Generate Compressed Result from nEndpoints & pcIndices
|
|
//==============================================================
|
|
c0 = cmp_constructColorBGR(EndPoints.Color0);
|
|
c1 = cmp_constructColorBGR(EndPoints.Color1);
|
|
|
|
// Get Processed indices if not set
|
|
if (nCmpIndices == 0)
|
|
nCmpIndices = pcIndices;
|
|
|
|
CGU_Vec2ui cmpBlock;
|
|
if (c0 <= c1)
|
|
{
|
|
cmpBlock.x = c1 | (c0 << 16);
|
|
}
|
|
else
|
|
cmpBlock.x = c0 | (c1 << 16);
|
|
|
|
cmpBlock.y = nCmpIndices;
|
|
|
|
return cmpBlock;
|
|
}
|
|
|
|
CMP_STATIC void cgu_ProcessColors(CMP_INOUT CGU_Vec3f CMP_PTRINOUT colorMin,
|
|
CMP_INOUT CGU_Vec3f CMP_PTRINOUT colorMax,
|
|
CMP_INOUT CGU_UINT32 CMP_PTRINOUT c0,
|
|
CMP_INOUT CGU_UINT32 CMP_PTRINOUT c1,
|
|
CMP_IN CGU_INT setopt,
|
|
CMP_IN CGU_BOOL isSRGB)
|
|
{
|
|
// CGU_UINT32 srbMap[32] = {0,5,8,11,12,13,14,15,16,17,18,19,20,21,22,23,23,24,24,25,25,26,26,27,27,28,28,29,29,30,30,31};
|
|
// CGU_UINT32 sgMap[64] = {0,10,14,16,19,20,22,24,25,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,42,43,43,44,45,45,
|
|
// 46,47,47,48,48,49,50,50,51,52,52,53,53,54,54,55,55,56,56,57,57,58,58,59,59,60,60,61,61,62,62,63,63};
|
|
CGU_INT32 x, y, z;
|
|
CGU_Vec3f scale = {31.0f, 63.0f, 31.0f};
|
|
CGU_Vec3f MinColorScaled;
|
|
CGU_Vec3f MaxColorScaled;
|
|
|
|
// Clamp or Transform is needed, the transforms have built in clamps
|
|
if (isSRGB)
|
|
{
|
|
MinColorScaled = cmp_linearToSrgb(CMP_PTRINOUT colorMin);
|
|
MaxColorScaled = cmp_linearToSrgb(CMP_PTRINOUT colorMax);
|
|
}
|
|
else
|
|
{
|
|
MinColorScaled = cmp_clampVec3f(CMP_PTRINOUT colorMin, 0.0f, 1.0f);
|
|
MaxColorScaled = cmp_clampVec3f(CMP_PTRINOUT colorMax, 0.0f, 1.0f);
|
|
}
|
|
|
|
switch (setopt)
|
|
{
|
|
case 0: // Use Min Max processing
|
|
MinColorScaled = cmp_floorVec3f(MinColorScaled * scale);
|
|
MaxColorScaled = cmp_ceilVec3f(MaxColorScaled * scale);
|
|
CMP_PTRINOUT colorMin = MinColorScaled / scale;
|
|
CMP_PTRINOUT colorMax = MaxColorScaled / scale;
|
|
break;
|
|
default: // Use round processing
|
|
MinColorScaled = round(MinColorScaled * scale);
|
|
MaxColorScaled = round(MaxColorScaled * scale);
|
|
break;
|
|
}
|
|
|
|
x = (CGU_UINT32)(MinColorScaled.x);
|
|
y = (CGU_UINT32)(MinColorScaled.y);
|
|
z = (CGU_UINT32)(MinColorScaled.z);
|
|
|
|
//if (isSRGB) {
|
|
// // scale RB
|
|
// x = srbMap[x]; // &0x1F];
|
|
// y = sgMap [y]; // &0x3F];
|
|
// z = srbMap[z]; // &0x1F];
|
|
// // scale G
|
|
//}
|
|
CMP_PTRINOUT c0 = (x << 11) | (y << 5) | z;
|
|
|
|
x = (CGU_UINT32)(MaxColorScaled.x);
|
|
y = (CGU_UINT32)(MaxColorScaled.y);
|
|
z = (CGU_UINT32)(MaxColorScaled.z);
|
|
CMP_PTRINOUT c1 = (x << 11) | (y << 5) | z;
|
|
}
|
|
|
|
CMP_STATIC CGU_FLOAT cgu_getIndicesRGB(CMP_INOUT CGU_UINT32 CMP_PTRINOUT cmpindex,
|
|
CMP_IN const CGU_Vec3f block[16],
|
|
CMP_IN CGU_Vec3f minColor,
|
|
CMP_IN CGU_Vec3f maxColor,
|
|
CMP_IN CGU_BOOL getErr)
|
|
{
|
|
CGU_UINT32 PackedIndices = 0;
|
|
CGU_FLOAT err = 0.0f;
|
|
CGU_Vec3f cn[4];
|
|
CGU_FLOAT minDistance;
|
|
|
|
if (getErr)
|
|
{
|
|
// remap to BC1 spec for decoding offsets,
|
|
// where cn[0] > cn[1] Max Color = index 0, 2/3 offset =index 2, 1/3 offset = index 3, Min Color = index 1
|
|
cn[0] = maxColor;
|
|
cn[1] = minColor;
|
|
cn[2] = cn[0] * 2.0f / 3.0f + cn[1] * 1.0f / 3.0f;
|
|
cn[3] = cn[0] * 1.0f / 3.0f + cn[1] * 2.0f / 3.0f;
|
|
}
|
|
|
|
CGU_FLOAT Scale = 3.f / cmp_dotVec3f(minColor - maxColor, minColor - maxColor);
|
|
CGU_Vec3f ScaledRange = (minColor - maxColor) * Scale;
|
|
CGU_FLOAT Bias = (cmp_dotVec3f(maxColor, maxColor) - cmp_dotVec3f(maxColor, minColor)) * Scale;
|
|
CGU_INT indexMap[4] = {0, 2, 3, 1}; // mapping based on BC1 Spec for color0 > color1
|
|
CGU_UINT32 index;
|
|
CGU_FLOAT diff;
|
|
|
|
for (CGU_UINT32 i = 0; i < 16; i++)
|
|
{
|
|
// Get offset from base scale
|
|
diff = cmp_dotVec3f(block[i], ScaledRange) + Bias;
|
|
index = ((CGU_UINT32)round(diff)) & 0x3;
|
|
|
|
// remap linear offset to spec offset
|
|
index = indexMap[index];
|
|
|
|
// use err calc for use in higher quality code
|
|
if (getErr)
|
|
{
|
|
minDistance = cmp_dotVec3f(block[i] - cn[index], block[i] - cn[index]);
|
|
err += minDistance;
|
|
}
|
|
|
|
// Map the 2 bit index into compress 32 bit block
|
|
if (index)
|
|
PackedIndices |= (index << (2 * i));
|
|
}
|
|
|
|
if (getErr)
|
|
err = err * 0.0208333f;
|
|
|
|
CMP_PTRINOUT cmpindex = PackedIndices;
|
|
return err;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------------------
|
|
// Decompress is RGB (0.0f..255.0f)
|
|
//--------------------------------------------------------------------------------------------------------
|
|
CMP_STATIC void cgu_decompressRGBBlock(CMP_INOUT CGU_Vec3f rgbBlock[BLOCK_SIZE_4X4], const CGU_Vec2ui compressedBlock)
|
|
{
|
|
CGU_UINT32 n0 = compressedBlock.x & 0xffff;
|
|
CGU_UINT32 n1 = compressedBlock.x >> 16;
|
|
CGU_UINT32 index;
|
|
|
|
//-------------------------------------------------------
|
|
// Decode the compressed block 0..255 color range
|
|
//-------------------------------------------------------
|
|
CGU_Vec3f c0 = cmp_565ToLinear(n0); // max color
|
|
CGU_Vec3f c1 = cmp_565ToLinear(n1); // min color
|
|
CGU_Vec3f c2;
|
|
CGU_Vec3f c3;
|
|
|
|
if (n0 > n1)
|
|
{
|
|
c2 = (c0 * 2.0f + c1) / 3.0f;
|
|
c3 = (c1 * 2.0f + c0) / 3.0f;
|
|
|
|
for (CGU_UINT32 i = 0; i < 16; i++)
|
|
{
|
|
index = (compressedBlock.y >> (2 * i)) & 3;
|
|
switch (index)
|
|
{
|
|
case 0:
|
|
rgbBlock[i] = c0;
|
|
break;
|
|
case 1:
|
|
rgbBlock[i] = c1;
|
|
break;
|
|
case 2:
|
|
rgbBlock[i] = c2;
|
|
break;
|
|
case 3:
|
|
rgbBlock[i] = c3;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Transparent decode
|
|
c2 = (c0 + c1) / 2.0f;
|
|
|
|
for (CGU_UINT32 i = 0; i < 16; i++)
|
|
{
|
|
index = (compressedBlock.y >> (2 * i)) & 3;
|
|
switch (index)
|
|
{
|
|
case 0:
|
|
rgbBlock[i] = c0;
|
|
break;
|
|
case 1:
|
|
rgbBlock[i] = c1;
|
|
break;
|
|
case 2:
|
|
rgbBlock[i] = c2;
|
|
break;
|
|
case 3:
|
|
rgbBlock[i] = 0.0f;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// The source is 0..255
|
|
CMP_STATIC float cgu_RGBABlockErrorLinear(const CGU_Vec4uc src_rgbBlock[BLOCK_SIZE_4X4], const CGU_Vec2ui compressedBlock)
|
|
{
|
|
CGU_Vec3f rgbBlock[BLOCK_SIZE_4X4];
|
|
|
|
// Decompressed block channels are 0..255
|
|
cgu_decompressRGBBlock(rgbBlock, compressedBlock);
|
|
|
|
//------------------------------------------------------------------
|
|
// Calculate MSE of the block
|
|
// Note : pow is used as Float type for the code to be usable on CPU
|
|
//------------------------------------------------------------------
|
|
CGU_Vec3f serr;
|
|
serr = 0.0f;
|
|
|
|
float sR, sG, sB, R, G, B;
|
|
|
|
for (int j = 0; j < 16; j++)
|
|
{
|
|
sR = src_rgbBlock[j].x;
|
|
sG = src_rgbBlock[j].y;
|
|
sB = src_rgbBlock[j].z;
|
|
|
|
R = rgbBlock[j].x;
|
|
G = rgbBlock[j].y;
|
|
B = rgbBlock[j].z;
|
|
|
|
// Norm colors
|
|
serr.x += pow(sR - R, 2.0f);
|
|
serr.y += pow(sG - G, 2.0f);
|
|
serr.z += pow(sB - B, 2.0f);
|
|
}
|
|
|
|
// MSE for 16 texels
|
|
return (serr.x + serr.y + serr.z) / 48.0f;
|
|
}
|
|
|
|
// The source is 0..1, decompressed data using cmp_decompressRGBBlock2 is 0..255 which is converted down to 0..1
|
|
CMP_STATIC float cgu_RGBBlockError(const CGU_Vec3f src_rgbBlock[BLOCK_SIZE_4X4], const CGU_Vec2ui compressedBlock, CGU_BOOL isSRGB)
|
|
{
|
|
CGU_Vec3f rgbBlock[BLOCK_SIZE_4X4];
|
|
|
|
// Decompressed block channels are 0..255
|
|
cgu_decompressRGBBlock(rgbBlock, compressedBlock);
|
|
|
|
//------------------------------------------------------------------
|
|
// Calculate MSE of the block
|
|
// Note : pow is used as Float type for the code to be usable on CPU
|
|
//------------------------------------------------------------------
|
|
CGU_Vec3f serr;
|
|
serr = 0.0f;
|
|
|
|
float sR, sG, sB, R, G, B;
|
|
|
|
for (int j = 0; j < 16; j++)
|
|
{
|
|
if (isSRGB)
|
|
{
|
|
sR = round(cmp_linearToSrgbf(src_rgbBlock[j].x) * 255.0f);
|
|
sG = round(cmp_linearToSrgbf(src_rgbBlock[j].y) * 255.0f);
|
|
sB = round(cmp_linearToSrgbf(src_rgbBlock[j].z) * 255.0f);
|
|
}
|
|
else
|
|
{
|
|
sR = round(src_rgbBlock[j].x * 255.0f);
|
|
sG = round(src_rgbBlock[j].y * 255.0f);
|
|
sB = round(src_rgbBlock[j].z * 255.0f);
|
|
}
|
|
|
|
R = rgbBlock[j].x;
|
|
G = rgbBlock[j].y;
|
|
B = rgbBlock[j].z;
|
|
|
|
// Norm colors
|
|
serr.x += pow(sR - R, 2.0f);
|
|
serr.y += pow(sG - G, 2.0f);
|
|
serr.z += pow(sB - B, 2.0f);
|
|
}
|
|
|
|
// MSE for 16 texels
|
|
return (serr.x + serr.y + serr.z) / 48.0f;
|
|
}
|
|
|
|
CMP_STATIC CGU_Vec2ui cgu_CompressRGBBlock_MinMax(CMP_IN const CGU_Vec3f src_imageRGB[16],
|
|
CMP_IN CGU_FLOAT fquality,
|
|
CMP_IN CGU_BOOL isSRGB,
|
|
CMP_INOUT CGU_Vec3f srcRGB[16], // The list of source colors with blue channel altered
|
|
CMP_INOUT CGU_Vec3f CMP_REFINOUT average_rgb, // The centrepoint of the axis
|
|
CMP_INOUT CGU_FLOAT CMP_REFINOUT errout
|
|
)
|
|
{
|
|
CGU_Vec2ui Q1CompData = {0,0};
|
|
CGU_Vec3f rgb = {0,0,0};
|
|
|
|
// -------------------------------------------------------------------------------------
|
|
// (1) Find the array of unique pixel values and sum them to find their average position
|
|
// -------------------------------------------------------------------------------------
|
|
CGU_FLOAT errLQ = 0.0f;
|
|
CGU_BOOL fastProcess = (fquality <= CMP_QUALITY0); // Min Max only
|
|
CGU_Vec3f srcMin = 1.0f; // Min source color
|
|
CGU_Vec3f srcMax = 0.0f; // Max source color
|
|
CGU_Vec2ui Q1compressedBlock = {0, 0};
|
|
CGU_UINT32 c0 = 0;
|
|
CGU_UINT32 c1 = 0;
|
|
|
|
average_rgb = 0.0f;
|
|
// Get average and modifed src
|
|
// find average position and save list of pixels as 0F..255F range for processing
|
|
// Note: z (blue) is average of blue+green channels
|
|
for (CGU_INT32 i = 0; i < BLOCK_SIZE_4X4; i++)
|
|
{
|
|
srcMin = cmp_minVec3f(srcMin, src_imageRGB[i]);
|
|
srcMax = cmp_maxVec3f(srcMax, src_imageRGB[i]);
|
|
if (!fastProcess)
|
|
{
|
|
rgb = isSRGB ? cmp_linearToSrgb(src_imageRGB[i]) : cmp_saturate(src_imageRGB[i]);
|
|
rgb.z = (rgb.y + rgb.z) * 0.5F; // Z-axiz => (R+G)/2
|
|
srcRGB[i] = rgb;
|
|
average_rgb = average_rgb + rgb;
|
|
}
|
|
}
|
|
|
|
// Process two colors for saving in 565 format as C0 and C1
|
|
cgu_ProcessColors(CMP_REFINOUT srcMin, CMP_REFINOUT srcMax, CMP_REFINOUT c0, CMP_REFINOUT c1, isSRGB ? 1 : 0, isSRGB);
|
|
|
|
// Save simple min-max encoding
|
|
if (c0 < c1)
|
|
{
|
|
Q1CompData.x = (c0 << 16) | c1;
|
|
CGU_UINT32 index = 0;
|
|
errLQ = cgu_getIndicesRGB(CMP_REFINOUT index, src_imageRGB, srcMin, srcMax, false);
|
|
Q1CompData.y = index;
|
|
errout = cgu_RGBBlockError(src_imageRGB, Q1CompData, isSRGB);
|
|
}
|
|
else
|
|
{
|
|
// Most simple case all colors are equal or 0.0f
|
|
Q1compressedBlock.x = (c1 << 16) | c0;
|
|
Q1compressedBlock.y = 0;
|
|
errout = 0.0f;
|
|
return Q1compressedBlock;
|
|
}
|
|
// 0.0625F is (1/BLOCK_SIZE_4X4)
|
|
average_rgb = average_rgb * 0.0625F;
|
|
|
|
return Q1CompData;
|
|
}
|
|
|
|
|
|
CMP_STATIC CGU_Vec2ui cgu_CompressRGBBlock_Fast(CMP_IN const CGU_Vec3f src_imageRGB[16],
|
|
CMP_IN CGU_FLOAT fquality,
|
|
CMP_IN CGU_BOOL isSRGB,
|
|
CMP_IN CGU_Vec3f srcRGB[16],
|
|
CMP_IN CGU_Vec3f CMP_REFINOUT average_rgb,
|
|
CMP_INOUT CGU_FLOAT CMP_REFINOUT errout)
|
|
{
|
|
CMP_UNUSED(fquality);
|
|
|
|
CGU_Vec3f axisVectorRGB = {0.0f, 0.0f, 0.0f}; // The axis vector for index projection
|
|
CGU_FLOAT pos_on_axis[16]; // The distance each unique falls along the compression axis
|
|
CGU_FLOAT axisleft = 0; // The extremities and centre (average of left/right) of srcRGB along the compression axis
|
|
CGU_FLOAT axisright = 0; // The extremities and centre (average of left/right) of srcRGB along the compression axis
|
|
CGU_FLOAT axiscentre = 0; // The extremities and centre (average of left/right) of srcRGB along the compression axis
|
|
CGU_INT32 swap = 0; // Indicator if the RGB values need swapping to generate an opaque result
|
|
CGU_Vec3f srcBlock[16]; // The list of source colors with any color space transforms and clipping
|
|
CGU_UINT32 c0 = 0;
|
|
CGU_UINT32 c1 = 0;
|
|
CGU_Vec2ui compressedBlock = {0, 0};
|
|
CGU_FLOAT Q1CompErr;
|
|
CGU_Vec2ui Q1CompData = {0,0};
|
|
|
|
|
|
|
|
CGU_Vec3f rgb = {0,0,0};
|
|
|
|
// -------------------------------------------------------------------------------------
|
|
// (4) For each component, reflect points about the average so all lie on the same side
|
|
// of the average, and compute the new average - this gives a second point that defines the axis
|
|
// To compute the sign of the axis sum the positive differences of G for each of R and B (the
|
|
// G axis is always positive in this implementation
|
|
// -------------------------------------------------------------------------------------
|
|
// An interesting situation occurs if the G axis contains no information, in which case the RB
|
|
// axis is also compared. I am not entirely sure if this is the correct implementation - should
|
|
// the priority axis be determined by magnitude?
|
|
{
|
|
CGU_FLOAT rg_pos = 0.0f;
|
|
CGU_FLOAT bg_pos = 0.0f;
|
|
CGU_FLOAT rb_pos = 0.0f;
|
|
|
|
for (CGU_INT32 i = 0; i < BLOCK_SIZE_4X4; i++)
|
|
{
|
|
rgb = srcRGB[i] - average_rgb;
|
|
axisVectorRGB = axisVectorRGB + cmp_fabsVec3f(rgb);
|
|
if (rgb.x > 0)
|
|
{
|
|
rg_pos += rgb.y;
|
|
rb_pos += rgb.z;
|
|
}
|
|
if (rgb.z > 0)
|
|
bg_pos += rgb.y;
|
|
}
|
|
|
|
// Average over BLOCK_SIZE_4X4
|
|
axisVectorRGB = axisVectorRGB * 0.0625F;
|
|
|
|
// New average position
|
|
if (rg_pos < 0)
|
|
axisVectorRGB.x = -axisVectorRGB.x;
|
|
if (bg_pos < 0)
|
|
axisVectorRGB.z = -axisVectorRGB.z;
|
|
if ((rg_pos == bg_pos) && (rg_pos == 0))
|
|
{
|
|
if (rb_pos < 0)
|
|
axisVectorRGB.z = -axisVectorRGB.z;
|
|
}
|
|
}
|
|
|
|
// -------------------------------------------------------------------------------------
|
|
// (5) Axis projection and remapping
|
|
// -------------------------------------------------------------------------------------
|
|
{
|
|
CGU_FLOAT v2_recip;
|
|
// Normalize the axis for simplicity of future calculation
|
|
v2_recip = cmp_dotVec3f(axisVectorRGB, axisVectorRGB);
|
|
if (v2_recip > 0)
|
|
v2_recip = 1.0f / (CGU_FLOAT)cmp_sqrt(v2_recip);
|
|
else
|
|
v2_recip = 1.0f;
|
|
axisVectorRGB = axisVectorRGB * v2_recip;
|
|
}
|
|
|
|
// -------------------------------------------------------------------------------------
|
|
// (6) Map the axis
|
|
// -------------------------------------------------------------------------------------
|
|
// the line joining (and extended on either side of) average and axis
|
|
// defines the axis onto which the points will be projected
|
|
// Project all the points onto the axis, calculate the distance along
|
|
// the axis from the centre of the axis (average)
|
|
// From Foley & Van Dam: Closest point of approach of a line (P + v) to a point (R) is
|
|
// P + ((R-P).v) / (v.v))v
|
|
// The distance along v is therefore (R-P).v / (v.v) where (v.v) is 1 if v is a unit vector.
|
|
//
|
|
// Calculate the extremities at the same time - these need to be reasonably accurately
|
|
// represented in all cases
|
|
{
|
|
axisleft = CMP_FLOAT_MAX;
|
|
axisright = -CMP_FLOAT_MAX;
|
|
for (CGU_INT32 i = 0; i < BLOCK_SIZE_4X4; i++)
|
|
{
|
|
// Compute the distance along the axis of the point of closest approach
|
|
CGU_Vec3f temp = (srcRGB[i] - average_rgb);
|
|
pos_on_axis[i] = cmp_dotVec3f(temp, axisVectorRGB);
|
|
|
|
// Work out the extremities
|
|
if (pos_on_axis[i] < axisleft)
|
|
axisleft = pos_on_axis[i];
|
|
if (pos_on_axis[i] > axisright)
|
|
axisright = pos_on_axis[i];
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------------
|
|
// (7) Now we have a good axis and the basic information about how the points are mapped to it
|
|
// Our initial guess is to represent the endpoints accurately, by moving the average
|
|
// to the centre and recalculating the point positions along the line
|
|
// ---------------------------------------------------------------------------------------------
|
|
{
|
|
axiscentre = (axisleft + axisright) * 0.5F;
|
|
average_rgb = average_rgb + (axisVectorRGB * axiscentre);
|
|
for (CGU_INT32 i = 0; i < BLOCK_SIZE_4X4; i++)
|
|
pos_on_axis[i] -= axiscentre;
|
|
axisright -= axiscentre;
|
|
axisleft -= axiscentre;
|
|
}
|
|
|
|
// -------------------------------------------------------------------------------------
|
|
// (8) Calculate the high and low output colour values
|
|
// Involved in this is a rounding procedure which is undoubtedly slightly twitchy. A
|
|
// straight rounded average is not correct, as the decompressor 'unrounds' by replicating
|
|
// the top bits to the bottom.
|
|
// In order to take account of this process, we don't just apply a straight rounding correction,
|
|
// but base our rounding on the input value (a straight rounding is actually pretty good in terms of
|
|
// error measure, but creates a visual colour and/or brightness shift relative to the original image)
|
|
// The method used here is to apply a centre-biased rounding dependent on the input value, which was
|
|
// (mostly by experiment) found to give minimum MSE while preserving the visual characteristics of
|
|
// the image.
|
|
// rgb = (average_rgb + (left|right)*axisVectorRGB);
|
|
// -------------------------------------------------------------------------------------
|
|
{
|
|
CGU_Vec3f MinColor, MaxColor;
|
|
|
|
MinColor = average_rgb + (axisVectorRGB * axisleft);
|
|
MaxColor = average_rgb + (axisVectorRGB * axisright);
|
|
MinColor.z = (MinColor.z * 2) - MinColor.y;
|
|
MaxColor.z = (MaxColor.z * 2) - MaxColor.y;
|
|
|
|
cgu_ProcessColors(CMP_REFINOUT MinColor, CMP_REFINOUT MaxColor, CMP_REFINOUT c0, CMP_REFINOUT c1, 1, false);
|
|
|
|
// Force to be a 4-colour opaque block - in which case, c0 is greater than c1
|
|
swap = 0;
|
|
if (c0 < c1)
|
|
{
|
|
CGU_UINT32 t;
|
|
t = c0;
|
|
c0 = c1;
|
|
c1 = t;
|
|
swap = 1;
|
|
}
|
|
else if (c0 == c1)
|
|
{
|
|
// This block will always be encoded in 3-colour mode
|
|
// Need to ensure that only one of the two points gets used,
|
|
// avoiding accidentally setting some transparent pixels into the block
|
|
for (CGU_INT32 i = 0; i < BLOCK_SIZE_4X4; i++)
|
|
pos_on_axis[i] = axisleft;
|
|
}
|
|
|
|
compressedBlock.x = c0 | (c1 << 16);
|
|
|
|
// -------------------------------------------------------------------------------------
|
|
// (9) Final clustering, creating the 2-bit values that define the output
|
|
// -------------------------------------------------------------------------------------
|
|
|
|
CGU_UINT32 index;
|
|
CGU_FLOAT division;
|
|
{
|
|
compressedBlock.y = 0;
|
|
division = axisright * 2.0f / 3.0f;
|
|
axiscentre = (axisleft + axisright) / 2; // Actually, this code only works if centre is 0 or approximately so
|
|
|
|
CGU_FLOAT CompMinErr;
|
|
|
|
// This feature is work in progress
|
|
// remap to BC1 spec for decoding offsets,
|
|
// where cn[0] > cn[1] Max Color = index 0, 2/3 offset =index 2, 1/3 offset = index 3, Min Color = index 1
|
|
// CGU_Vec3f cn[4];
|
|
// cn[0] = MaxColor;
|
|
// cn[1] = MinColor;
|
|
// cn[2] = cn[0]*2.0f/3.0f + cn[1]*1.0f/3.0f;
|
|
// cn[3] = cn[0]*1.0f/3.0f + cn[1]*2.0f/3.0f;
|
|
|
|
for (CGU_INT32 i = 0; i < BLOCK_SIZE_4X4; i++)
|
|
{
|
|
// Endpoints (indicated by block > average) are 0 and 1, while
|
|
// interpolants are 2 and 3
|
|
if (cmp_fabs(pos_on_axis[i]) >= division)
|
|
index = 0;
|
|
else
|
|
index = 2;
|
|
// Positive is in the latter half of the block
|
|
if (pos_on_axis[i] >= axiscentre)
|
|
index += 1;
|
|
|
|
index = index ^ swap;
|
|
// Set the output, taking swapping into account
|
|
compressedBlock.y |= (index << (2 * i));
|
|
|
|
// use err calc for use in higher quality code
|
|
//CompMinErr += cmp_dotVec3f(srcRGBRef[i] - cn[index],srcRGBRef[i] - cn[index]);
|
|
}
|
|
|
|
//CompMinErr = CompMinErr * 0.0208333f;
|
|
|
|
CompMinErr = cgu_RGBBlockError(src_imageRGB, compressedBlock, isSRGB);
|
|
Q1CompErr = cgu_RGBBlockError(src_imageRGB, Q1CompData, isSRGB);
|
|
|
|
if (CompMinErr > Q1CompErr)
|
|
{
|
|
compressedBlock = Q1CompData;
|
|
errout = Q1CompErr;
|
|
}
|
|
else
|
|
errout = CompMinErr;
|
|
}
|
|
}
|
|
// done
|
|
|
|
return compressedBlock;
|
|
}
|
|
|
|
CMP_STATIC CGU_UINT8 g_Match5Bit[256][2] = {
|
|
{ 0, 0},{ 0, 0},{ 1, 0},{ 1, 0},{ 0, 1},{ 0, 1},{ 0, 1},{ 1, 1},{ 1, 1},{ 1, 1},{ 0, 2},{ 4, 0},{ 1, 2},{ 1, 2},{ 1, 2},{ 2, 2},
|
|
{ 2, 2},{ 2, 2},{ 1, 3},{ 5, 1},{ 2, 3},{ 2, 3},{ 0, 4},{ 3, 3},{ 3, 3},{ 3, 3},{ 2, 4},{ 2, 4},{ 2, 4},{ 5, 3},{ 1, 5},{ 1, 5},
|
|
{ 2, 5},{ 4, 4},{ 4, 4},{ 3, 5},{ 3, 5},{ 2, 6},{ 2, 6},{ 2, 6},{ 3, 6},{ 5, 5},{ 5, 5},{ 4, 6},{ 8, 4},{ 3, 7},{ 3, 7},{ 3, 7},
|
|
{ 6, 6},{ 6, 6},{ 6, 6},{ 5, 7},{ 9, 5},{ 6, 7},{ 6, 7},{ 4, 8},{ 7, 7},{ 7, 7},{ 7, 7},{ 6, 8},{ 6, 8},{ 6, 8},{ 9, 7},{ 5, 9},
|
|
{ 5, 9},{ 6, 9},{ 8, 8},{ 8, 8},{ 7, 9},{ 7, 9},{ 6,10},{ 6,10},{ 6,10},{ 7,10},{ 9, 9},{ 9, 9},{ 8,10},{12, 8},{ 7,11},{ 7,11},
|
|
{ 7,11},{10,10},{10,10},{10,10},{ 9,11},{13, 9},{10,11},{10,11},{ 8,12},{11,11},{11,11},{11,11},{10,12},{10,12},{10,12},{13,11},
|
|
{ 9,13},{ 9,13},{10,13},{12,12},{12,12},{11,13},{11,13},{10,14},{10,14},{10,14},{11,14},{13,13},{13,13},{12,14},{16,12},{11,15},
|
|
{11,15},{11,15},{14,14},{14,14},{14,14},{13,15},{17,13},{14,15},{14,15},{12,16},{15,15},{15,15},{15,15},{14,16},{14,16},{14,16},
|
|
{17,15},{13,17},{13,17},{14,17},{16,16},{16,16},{15,17},{15,17},{14,18},{14,18},{14,18},{15,18},{17,17},{17,17},{16,18},{20,16},
|
|
{15,19},{15,19},{15,19},{18,18},{18,18},{18,18},{17,19},{21,17},{18,19},{18,19},{16,20},{19,19},{19,19},{19,19},{18,20},{18,20},
|
|
{18,20},{21,19},{17,21},{17,21},{18,21},{20,20},{20,20},{19,21},{19,21},{18,22},{18,22},{18,22},{19,22},{21,21},{21,21},{20,22},
|
|
{24,20},{19,23},{19,23},{19,23},{22,22},{22,22},{22,22},{21,23},{25,21},{22,23},{22,23},{20,24},{23,23},{23,23},{23,23},{22,24},
|
|
{22,24},{22,24},{25,23},{21,25},{21,25},{22,25},{24,24},{24,24},{23,25},{23,25},{22,26},{22,26},{22,26},{23,26},{25,25},{25,25},
|
|
{24,26},{28,24},{23,27},{23,27},{23,27},{26,26},{26,26},{26,26},{25,27},{29,25},{26,27},{26,27},{24,28},{27,27},{27,27},{27,27},
|
|
{26,28},{26,28},{26,28},{29,27},{25,29},{25,29},{26,29},{28,28},{28,28},{27,29},{27,29},{26,30},{26,30},{26,30},{27,30},{29,29},
|
|
{29,29},{28,30},{28,30},{27,31},{27,31},{27,31},{30,30},{30,30},{30,30},{29,31},{29,31},{30,31},{30,31},{30,31},{31,31},{31,31}};
|
|
|
|
CMP_STATIC CGU_UINT8 g_Match6Bit[256][2] = {
|
|
{ 0, 0},{ 1, 0},{ 0, 1},{ 1, 1},{ 1, 1},{ 0, 2},{ 1, 2},{ 2, 2},{ 2, 2},{ 1, 3},{ 0, 4},{ 3, 3},{ 3, 3},{ 0, 5},{ 1, 5},{ 4, 4},
|
|
{ 4, 4},{ 1, 6},{ 0, 7},{ 5, 5},{ 5, 5},{ 0, 8},{ 1, 8},{ 6, 6},{ 6, 6},{ 1, 9},{ 2, 9},{ 7, 7},{ 7, 7},{ 2,10},{ 3,10},{ 8, 8},
|
|
{ 8, 8},{ 3,11},{ 4,11},{ 9, 9},{ 9, 9},{ 4,12},{ 5,12},{10,10},{10,10},{ 5,13},{ 6,13},{16, 8},{11,11},{ 6,14},{ 7,14},{17, 9},
|
|
{12,12},{ 7,15},{ 8,15},{16,11},{13,13},{10,15},{ 8,16},{ 9,16},{14,14},{13,15},{ 9,17},{10,17},{15,15},{16,15},{10,18},{11,18},
|
|
{12,18},{16,16},{11,19},{12,19},{13,19},{17,17},{12,20},{13,20},{14,20},{18,18},{13,21},{14,21},{15,21},{19,19},{14,22},{15,22},
|
|
{20,20},{20,20},{15,23},{16,23},{21,21},{21,21},{16,24},{17,24},{22,22},{22,22},{17,25},{18,25},{23,23},{23,23},{18,26},{19,26},
|
|
{24,24},{24,24},{19,27},{20,27},{25,25},{25,25},{20,28},{21,28},{26,26},{26,26},{21,29},{22,29},{32,24},{27,27},{22,30},{23,30},
|
|
{33,25},{28,28},{23,31},{24,31},{32,27},{29,29},{26,31},{24,32},{25,32},{30,30},{29,31},{25,33},{26,33},{31,31},{32,31},{26,34},
|
|
{27,34},{28,34},{32,32},{27,35},{28,35},{29,35},{33,33},{28,36},{29,36},{30,36},{34,34},{29,37},{30,37},{31,37},{35,35},{30,38},
|
|
{31,38},{36,36},{36,36},{31,39},{32,39},{37,37},{37,37},{32,40},{33,40},{38,38},{38,38},{33,41},{34,41},{39,39},{39,39},{34,42},
|
|
{35,42},{40,40},{40,40},{35,43},{36,43},{41,41},{41,41},{36,44},{37,44},{42,42},{42,42},{37,45},{38,45},{48,40},{43,43},{38,46},
|
|
{39,46},{49,41},{44,44},{39,47},{40,47},{48,43},{45,45},{42,47},{40,48},{41,48},{46,46},{45,47},{41,49},{42,49},{47,47},{48,47},
|
|
{42,50},{43,50},{44,50},{48,48},{43,51},{44,51},{45,51},{49,49},{44,52},{45,52},{46,52},{50,50},{45,53},{46,53},{47,53},{51,51},
|
|
{46,54},{47,54},{52,52},{52,52},{47,55},{48,55},{53,53},{53,53},{48,56},{49,56},{54,54},{54,54},{49,57},{50,57},{55,55},{55,55},
|
|
{50,58},{51,58},{56,56},{56,56},{51,59},{52,59},{57,57},{57,57},{52,60},{53,60},{58,58},{58,58},{53,61},{54,61},{59,59},{59,59},
|
|
{54,62},{55,62},{60,60},{60,60},{55,63},{56,63},{61,61},{61,61},{58,63},{59,63},{62,62},{62,62},{61,63},{62,63},{63,63},{63,63}};
|
|
|
|
CMP_STATIC CGU_Vec2ui cgu_solidColorBlock(CMP_IN CGU_UINT8 Red, CMP_IN CGU_UINT8 Green, CMP_IN CGU_UINT8 Blue)
|
|
{
|
|
CGU_UINT32 maxEndp16;
|
|
CGU_UINT32 minEndp16;
|
|
|
|
CGU_UINT32 mask = 0xAAAAAAAAu;
|
|
|
|
minEndp16 = g_Match5Bit[Red][0] * 2048U + g_Match6Bit[Green][0] * 32U + g_Match5Bit[Blue][0];
|
|
maxEndp16 = g_Match5Bit[Red][1] * 2048U + g_Match6Bit[Green][1] * 32U + g_Match5Bit[Blue][1];
|
|
|
|
// write the color block
|
|
if( maxEndp16 < minEndp16 )
|
|
{
|
|
CGU_UINT32 tmpValue = minEndp16;
|
|
minEndp16 = maxEndp16;
|
|
maxEndp16 = tmpValue;
|
|
mask ^= 0x55555555u;
|
|
}
|
|
|
|
CGU_Vec2ui outputBytes;
|
|
outputBytes.x = CGU_UINT32(maxEndp16) | (CGU_UINT32(minEndp16) << 16u);
|
|
outputBytes.y = mask;
|
|
|
|
return outputBytes;
|
|
}
|
|
|
|
CMP_STATIC void cmp_get_encode_data(CMP_IN CMP_EncodeData CMP_REFINOUT edata, CMP_IN CMP_CONSTANT CGU_Vec4uc src_image[16])
|
|
{
|
|
CMP_CONSTANT CGU_UINT32 fr = src_image[0].r, fg = src_image[0].g, fb = src_image[0].b;
|
|
|
|
edata.all_colors_equal = false;
|
|
|
|
edata.total.r = fr;
|
|
edata.total.g = fg;
|
|
edata.total.b = fb;
|
|
edata.max.r = fr;
|
|
edata.max.g = fg;
|
|
edata.max.b = fb;
|
|
edata.min.r = fr;
|
|
edata.min.g = fg;
|
|
edata.min.b = fb;
|
|
|
|
edata.grayscale_flag = (fr == fg) && (fr == fb);
|
|
edata.any_black_pixels = (fr | fg | fb) < 4;
|
|
|
|
for (CGU_UINT32 i = 1; i < 16; i++)
|
|
{
|
|
CMP_CONSTANT CGU_INT r = src_image[i].r, g = src_image[i].g, b = src_image[i].b;
|
|
|
|
edata.grayscale_flag &= ((r == g) && (r == b));
|
|
edata.any_black_pixels |= ((r | g | b) < 4);
|
|
|
|
edata.max.r = CMP_MAX(edata.max.r, r);
|
|
edata.max.g = CMP_MAX(edata.max.g, g);
|
|
edata.max.b = CMP_MAX(edata.max.b, b);
|
|
edata.min.r = CMP_MIN(edata.min.r, r);
|
|
edata.min.g = CMP_MIN(edata.min.g, g);
|
|
edata.min.b = CMP_MIN(edata.min.b, b);
|
|
edata.total.r += r;
|
|
edata.total.g += g;
|
|
edata.total.b += b;
|
|
}
|
|
|
|
edata.avg.r = (edata.total.r + 8) >> 4;
|
|
edata.avg.g = (edata.total.g + 8) >> 4;
|
|
edata.avg.b = (edata.total.b + 8) >> 4;
|
|
}
|
|
|
|
#ifndef ASPM_GPU
|
|
/*------------------------------------------------------------------------------------------------
|
|
1 DIM ramp
|
|
------------------------------------------------------------------------------------------------*/
|
|
CMP_STATIC inline void cpu_BldClrRmp(CGU_FLOAT _Rmp[MAX_POINTS], CGU_FLOAT _InpRmp[NUM_ENDPOINTS], CGU_UINT32 dwNumPoints)
|
|
{
|
|
CGU_UINT32 dwRndAmount[9] = {0, 0, 0, 0, 1, 1, 2, 2, 3};
|
|
|
|
// linear interpolate end points to get the ramp
|
|
_Rmp[0] = _InpRmp[0];
|
|
_Rmp[dwNumPoints - 1] = _InpRmp[1];
|
|
if(dwNumPoints % 2)
|
|
_Rmp[dwNumPoints] = 1000000.f; // for 3 point ramp; not to select the 4th point as min
|
|
for(CGU_UINT32 e = 1; e < dwNumPoints - 1; e++)
|
|
_Rmp[e] = cmp_floor((_Rmp[0] * (dwNumPoints - 1 - e) + _Rmp[dwNumPoints - 1] * e + dwRndAmount[dwNumPoints])/ (CGU_FLOAT)(dwNumPoints - 1));
|
|
}
|
|
|
|
/*------------------------------------------------------------------------------------------------
|
|
// build 3D ramp
|
|
------------------------------------------------------------------------------------------------*/
|
|
CMP_STATIC inline void cpu_BldRmp(CGU_FLOAT _Rmp[NUM_CHANNELS][MAX_POINTS], CGU_FLOAT _InpRmp[NUM_CHANNELS][NUM_ENDPOINTS],CGU_UINT32 dwNumPoints) {
|
|
for(CGU_UINT32 j = 0; j < 3; j++)
|
|
cpu_BldClrRmp(_Rmp[j], _InpRmp[j], dwNumPoints);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------------------------------
|
|
// this is how the end points is going to be look like when decompressed
|
|
------------------------------------------------------------------------------------------------*/
|
|
CMP_STATIC inline void cpu_MkWkRmpPts(CMP_INOUT CGU_UINT8 CMP_REFINOUT _bEq,
|
|
CGU_FLOAT _OutRmpPts[NUM_CHANNELS][NUM_ENDPOINTS],
|
|
CGU_FLOAT _InpRmpPts[NUM_CHANNELS][NUM_ENDPOINTS],
|
|
CGU_UINT8 nRedBits,
|
|
CGU_UINT8 nGreenBits,
|
|
CGU_UINT8 nBlueBits)
|
|
{
|
|
CGU_FLOAT Fctrs[3];
|
|
Fctrs[RC] = (CGU_FLOAT)(1 << nRedBits);
|
|
Fctrs[GC] = (CGU_FLOAT)(1 << nGreenBits);
|
|
Fctrs[BC] = (CGU_FLOAT)(1 << nBlueBits);
|
|
|
|
CGU_BOOL bEq = true;
|
|
// find whether input ramp is flat
|
|
for(CGU_UINT32 j = 0; j < 3; j++)
|
|
bEq &= (_InpRmpPts[j][0] == _InpRmpPts[j][1]);
|
|
|
|
_bEq = bEq?1:0;
|
|
|
|
// end points on the integer grid
|
|
for(CGU_UINT32 j = 0; j <3; j++) {
|
|
for(CGU_UINT32 k = 0; k <2; k++) {
|
|
// Apply the lower bit replication to give full dynamic range
|
|
_OutRmpPts[j][k] = _InpRmpPts[j][k] + cmp_floor(_InpRmpPts[j][k] / Fctrs[j]);
|
|
_OutRmpPts[j][k] = cmp_max(_OutRmpPts[j][k], 0.f);
|
|
_OutRmpPts[j][k] = cmp_min(_OutRmpPts[j][k], 255.f);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Compute error and find DXTC indexes for the current cluster
|
|
CMP_STATIC CGU_FLOAT cpu_ClstrIntnl(CGU_FLOAT _Blk[BLOCK_SIZE_4X4][NUM_CHANNELS],
|
|
CGU_UINT8 pcIndices[BLOCK_SIZE_4X4],
|
|
CGU_FLOAT _Rmp[NUM_CHANNELS][MAX_POINTS],
|
|
int dwBlockSize,
|
|
CGU_UINT8 dwNumPoints,
|
|
bool _ConstRamp,
|
|
CGU_FLOAT _pfWeights[3],
|
|
bool _bUseAlpha)
|
|
{
|
|
CGU_FLOAT Err = 0.f;
|
|
CGU_UINT8 rmp_l = (_ConstRamp) ? 1 : dwNumPoints;
|
|
|
|
// For each colour in the original block assign it
|
|
// to the closest cluster and compute the cumulative error
|
|
for(int i=0; i< dwBlockSize; i++) {
|
|
if(_bUseAlpha && *((CGU_UINT32*) &_Blk[i][AC]) == 0)
|
|
pcIndices[i] = dwNumPoints;
|
|
else {
|
|
CGU_FLOAT shortest = 99999999999.f;
|
|
CGU_UINT8 shortestIndex = 0;
|
|
CGU_UINT8 r;
|
|
if ((_pfWeights[0] != 1.0f)||(_pfWeights[1] != 1.0f)||(_pfWeights[2] != 1.0f))
|
|
for(r=0; r < rmp_l; r++) {
|
|
// calculate the distance for each component
|
|
CGU_FLOAT distance = (_Blk[i][RC] - _Rmp[RC][r]) * (_Blk[i][RC] - _Rmp[RC][r]) * _pfWeights[0] +
|
|
(_Blk[i][GC] - _Rmp[GC][r]) * (_Blk[i][GC] - _Rmp[GC][r]) * _pfWeights[1] +
|
|
(_Blk[i][BC] - _Rmp[BC][r]) * (_Blk[i][BC] - _Rmp[BC][r]) * _pfWeights[2];
|
|
|
|
if(distance < shortest) {
|
|
shortest = distance;
|
|
shortestIndex = r;
|
|
}
|
|
} else
|
|
for(r=0; r < rmp_l; r++) {
|
|
// calculate the distance for each component
|
|
CGU_FLOAT distance = (_Blk[i][RC] - _Rmp[RC][r]) * (_Blk[i][RC] - _Rmp[RC][r]) +
|
|
(_Blk[i][GC] - _Rmp[GC][r]) * (_Blk[i][GC] - _Rmp[GC][r]) +
|
|
(_Blk[i][BC] - _Rmp[BC][r]) * (_Blk[i][BC] - _Rmp[BC][r]);
|
|
|
|
if(distance < shortest) {
|
|
shortest = distance;
|
|
shortestIndex = r;
|
|
}
|
|
}
|
|
|
|
Err += shortest;
|
|
|
|
// We have the index of the best cluster, so assign this in the block
|
|
// Reorder indices to match correct DXTC ordering
|
|
if(shortestIndex == dwNumPoints - 1)
|
|
shortestIndex = 1;
|
|
else if(shortestIndex)
|
|
shortestIndex++;
|
|
pcIndices[i] = shortestIndex;
|
|
}
|
|
}
|
|
|
|
return Err;
|
|
}
|
|
|
|
/*------------------------------------------------------------------------------------------------
|
|
// input ramp is on the coarse grid
|
|
------------------------------------------------------------------------------------------------*/
|
|
CMP_STATIC CGU_FLOAT cpu_ClstrBas( CGU_UINT8 pcIndices[BLOCK_SIZE_4X4],
|
|
CGU_FLOAT _Blk[BLOCK_SIZE_4X4][NUM_CHANNELS],
|
|
CGU_FLOAT _InpRmp[NUM_CHANNELS][NUM_ENDPOINTS],
|
|
int dwBlockSize,
|
|
CGU_UINT8 dwNumPoints,
|
|
CGU_FLOAT _pfWeights[3],
|
|
bool _bUseAlpha,
|
|
CGU_UINT8 nRedBits,
|
|
CGU_UINT8 nGreenBits,
|
|
CGU_UINT8 nBlueBits)
|
|
{
|
|
// make ramp endpoints the way they'll going to be decompressed
|
|
CGU_UINT8 Eq = 1;
|
|
CGU_FLOAT InpRmp[NUM_CHANNELS][NUM_ENDPOINTS];
|
|
cpu_MkWkRmpPts(Eq, InpRmp, _InpRmp, nRedBits, nGreenBits, nBlueBits);
|
|
|
|
// build ramp as it would be built by decompressor
|
|
CGU_FLOAT Rmp[NUM_CHANNELS][MAX_POINTS];
|
|
cpu_BldRmp(Rmp, InpRmp, dwNumPoints);
|
|
|
|
// clusterize and find a cumulative error
|
|
return cpu_ClstrIntnl(_Blk, pcIndices, Rmp, dwBlockSize, dwNumPoints, Eq, _pfWeights, _bUseAlpha);
|
|
}
|
|
|
|
CMP_STATIC CGU_UINT8 nByteBitsMask2[9] = {0x00,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff};
|
|
|
|
CMP_STATIC CGU_UINT32 cpu_ConstructColor2(CGU_UINT8 R, CGU_UINT8 nRedBits, CGU_UINT8 G, CGU_UINT8 nGreenBits, CGU_UINT8 B, CGU_UINT8 nBlueBits) {
|
|
return ( ((R & nByteBitsMask2[nRedBits]) << (nGreenBits + nBlueBits - (PIX_GRID - nRedBits))) |
|
|
((G & nByteBitsMask2[nGreenBits])<< (nBlueBits - (PIX_GRID - nGreenBits))) |
|
|
((B & nByteBitsMask2[nBlueBits]) >> ((PIX_GRID - nBlueBits))));
|
|
}
|
|
|
|
CMP_STATIC CGU_FLOAT cpu_Clstr( CGU_UINT32 block_32[BLOCK_SIZE_4X4],
|
|
CGU_UINT32 dwBlockSize,
|
|
CGU_UINT8 nEndpoints[3][NUM_ENDPOINTS],
|
|
CGU_UINT8 pcIndices[BLOCK_SIZE_4X4],
|
|
CGU_UINT8 dwNumPoints,
|
|
CGU_FLOAT _pfWeights[3],
|
|
bool _bUseAlpha,
|
|
CGU_UINT8 _nAlphaThreshold,
|
|
CGU_UINT8 nRedBits,
|
|
CGU_UINT8 nGreenBits,
|
|
CGU_UINT8 nBlueBits)
|
|
{
|
|
CGU_UINT32 c0 = cpu_ConstructColor2(nEndpoints[RC][0], nRedBits, nEndpoints[GC][0], nGreenBits, nEndpoints[BC][0], nBlueBits);
|
|
CGU_UINT32 c1 = cpu_ConstructColor2(nEndpoints[RC][1], nRedBits, nEndpoints[GC][1], nGreenBits, nEndpoints[BC][1], nBlueBits);
|
|
CGU_UINT32 nEndpointIndex0 = 0;
|
|
CGU_UINT32 nEndpointIndex1 = 1;
|
|
if((!(dwNumPoints & 0x1) && c0 <= c1) || ((dwNumPoints & 0x1) && c0 > c1)) {
|
|
nEndpointIndex0 = 1;
|
|
nEndpointIndex1 = 0;
|
|
}
|
|
|
|
CGU_FLOAT InpRmp[NUM_CHANNELS][NUM_ENDPOINTS];
|
|
InpRmp[RC][0] = (CGU_FLOAT)nEndpoints[RC][nEndpointIndex0];
|
|
InpRmp[RC][1] = (CGU_FLOAT)nEndpoints[RC][nEndpointIndex1];
|
|
InpRmp[GC][0] = (CGU_FLOAT)nEndpoints[GC][nEndpointIndex0];
|
|
InpRmp[GC][1] = (CGU_FLOAT)nEndpoints[GC][nEndpointIndex1];
|
|
InpRmp[BC][0] = (CGU_FLOAT)nEndpoints[BC][nEndpointIndex0];
|
|
InpRmp[BC][1] = (CGU_FLOAT)nEndpoints[BC][nEndpointIndex1];
|
|
|
|
CGU_UINT32 dwAlphaThreshold = _nAlphaThreshold << 24;
|
|
CGU_FLOAT Blk[BLOCK_SIZE_4X4][NUM_CHANNELS];
|
|
for(CGU_UINT32 i = 0; i < dwBlockSize; i++) {
|
|
Blk[i][RC] = (CGU_FLOAT)((block_32[i] & 0xff0000) >> 16);
|
|
Blk[i][GC] = (CGU_FLOAT)((block_32[i] & 0xff00) >> 8);
|
|
Blk[i][BC] = (CGU_FLOAT)(block_32[i] & 0xff);
|
|
if(_bUseAlpha)
|
|
Blk[i][AC] = ((block_32[i] & 0xff000000) >= dwAlphaThreshold) ? 1.f : 0.f;
|
|
}
|
|
|
|
return cpu_ClstrBas(pcIndices, Blk, InpRmp, dwBlockSize, dwNumPoints, _pfWeights, _bUseAlpha, nRedBits, nGreenBits, nBlueBits);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------------------------------
|
|
Compute cumulative error for the current cluster
|
|
------------------------------------------------------------------------------------------------*/
|
|
CMP_STATIC CGU_FLOAT cpu_ClstrErr(CGU_FLOAT _Blk[BLOCK_SIZE_4X4][NUM_CHANNELS],
|
|
CGU_FLOAT _Rpt[BLOCK_SIZE_4X4],
|
|
CGU_FLOAT _Rmp[NUM_CHANNELS][MAX_POINTS],
|
|
CGU_UINT32 _NmbClrs,
|
|
CGU_UINT32 _blcktp,
|
|
bool _ConstRamp,
|
|
CGU_Vec3f channelWeights)
|
|
{
|
|
CGU_FLOAT fError = 0.f;
|
|
CGU_UINT32 rmp_l = (_ConstRamp) ? 1 : _blcktp;
|
|
|
|
CGU_BOOL useWeights = ((channelWeights[0] != 1.0f) || (channelWeights[1] != 1.0f) || (channelWeights[2] != 1.0f));
|
|
|
|
// For each colour in the original block, find the closest cluster
|
|
// and compute the comulative error
|
|
for(CGU_UINT32 i=0; i<_NmbClrs; i++) {
|
|
CGU_FLOAT fShortest = 99999999999.f;
|
|
|
|
if(useWeights)
|
|
for(CGU_UINT32 r=0; r < rmp_l; r++) {
|
|
// calculate the distance for each component
|
|
CGU_FLOAT fDistance = (_Blk[i][RC] - _Rmp[RC][r]) * (_Blk[i][RC] - _Rmp[RC][r]) * channelWeights[0] +
|
|
(_Blk[i][GC] - _Rmp[GC][r]) * (_Blk[i][GC] - _Rmp[GC][r]) * channelWeights[1] +
|
|
(_Blk[i][BC] - _Rmp[BC][r]) * (_Blk[i][BC] - _Rmp[BC][r]) * channelWeights[2];
|
|
|
|
if(fDistance < fShortest)
|
|
fShortest = fDistance;
|
|
} else
|
|
for(CGU_UINT32 r=0; r < rmp_l; r++) {
|
|
// calculate the distance for each component
|
|
CGU_FLOAT fDistance = (_Blk[i][RC] - _Rmp[RC][r]) * (_Blk[i][RC] - _Rmp[RC][r]) +
|
|
(_Blk[i][GC] - _Rmp[GC][r]) * (_Blk[i][GC] - _Rmp[GC][r]) +
|
|
(_Blk[i][BC] - _Rmp[BC][r]) * (_Blk[i][BC] - _Rmp[BC][r]);
|
|
|
|
if(fDistance < fShortest)
|
|
fShortest = fDistance;
|
|
}
|
|
|
|
// accumulate the error
|
|
fError += fShortest * _Rpt[i];
|
|
}
|
|
|
|
return fError;
|
|
}
|
|
|
|
|
|
#if defined(USE_REFINE3D)
|
|
|
|
CMP_STATIC CGU_FLOAT cmp_Refine3D( CGU_FLOAT _OutRmpPnts[NUM_CHANNELS][NUM_ENDPOINTS],
|
|
CGU_FLOAT _InpRmpPnts[NUM_CHANNELS][NUM_ENDPOINTS],
|
|
CGU_FLOAT _Blk[BLOCK_SIZE_4X4][NUM_CHANNELS],
|
|
CGU_FLOAT _Rpt[BLOCK_SIZE_4X4],
|
|
CGU_UINT32 _NmrClrs,
|
|
CGU_UINT32 dwNumPoints,
|
|
CGU_Vec3f channelWeights,
|
|
CGU_UINT8 nRedBits,
|
|
CGU_UINT8 nGreenBits,
|
|
CGU_UINT8 nBlueBits,
|
|
CGU_UINT32 nRefineSteps)
|
|
{
|
|
CGU_FLOAT ALIGN_16 Rmp[NUM_CHANNELS][MAX_POINTS];
|
|
|
|
CGU_FLOAT Blk[BLOCK_SIZE_4X4][NUM_CHANNELS];
|
|
for(CGU_UINT32 i = 0; i < _NmrClrs; i++)
|
|
for(CGU_UINT32 j = 0; j < 3; j++)
|
|
Blk[i][j] = _Blk[i][j];
|
|
|
|
CGU_FLOAT fWeightRed = channelWeights.r;
|
|
CGU_FLOAT fWeightGreen = channelWeights.g;
|
|
CGU_FLOAT fWeightBlue = channelWeights.b;
|
|
|
|
// here is our grid
|
|
CGU_FLOAT Fctrs[3];
|
|
Fctrs[RC] = (CGU_FLOAT)(1 << (PIX_GRID-nRedBits));
|
|
Fctrs[GC] = (CGU_FLOAT)(1 << (PIX_GRID-nGreenBits));
|
|
Fctrs[BC] = (CGU_FLOAT)(1 << (PIX_GRID-nBlueBits));
|
|
|
|
CGU_FLOAT InpRmp0[NUM_CHANNELS][NUM_ENDPOINTS];
|
|
CGU_FLOAT InpRmp[NUM_CHANNELS][NUM_ENDPOINTS];
|
|
for(CGU_UINT32 k = 0; k < 2; k++)
|
|
for(CGU_UINT32 j = 0; j < 3; j++)
|
|
InpRmp0[j][k] = InpRmp[j][k] = _OutRmpPnts[j][k] = _InpRmpPnts[j][k];
|
|
|
|
// make ramp endpoints the way they'll going to be decompressed
|
|
// plus check whether the ramp is flat
|
|
CGU_UINT8 Eq;
|
|
CGU_FLOAT WkRmpPts[NUM_CHANNELS][NUM_ENDPOINTS];
|
|
cpu_MkWkRmpPts(Eq, WkRmpPts, InpRmp, nRedBits, nGreenBits, nBlueBits);
|
|
|
|
// build ramp for all 3 colors
|
|
cpu_BldRmp(Rmp, WkRmpPts, dwNumPoints);
|
|
|
|
// clusterize for the current ramp
|
|
CGU_FLOAT bestE = cpu_ClstrErr(Blk, _Rpt, Rmp, _NmrClrs, dwNumPoints, Eq, channelWeights);
|
|
if(bestE == 0.f) // if exact, we've done
|
|
return bestE;
|
|
|
|
// Jitter endpoints in each direction
|
|
CGU_INT nRefineStart = 0 - (cmp_min(nRefineSteps, (CGU_UINT8)8));
|
|
CGU_INT nRefineEnd = cmp_min(nRefineSteps, (CGU_UINT8)8);
|
|
for(CGU_INT nJitterG0 = nRefineStart; nJitterG0 <= nRefineEnd; nJitterG0++) {
|
|
InpRmp[GC][0] = cmp_min(cmp_max(InpRmp0[GC][0] + nJitterG0 * Fctrs[GC], 0.f), 255.f);
|
|
for(CGU_INT nJitterG1 = nRefineStart; nJitterG1 <= nRefineEnd; nJitterG1++) {
|
|
InpRmp[GC][1] = cmp_min(cmp_max(InpRmp0[GC][1] + nJitterG1 * Fctrs[GC], 0.f), 255.f);
|
|
cpu_MkWkRmpPts(Eq, WkRmpPts, InpRmp, nRedBits, nGreenBits, nBlueBits);
|
|
cpu_BldClrRmp(Rmp[GC], WkRmpPts[GC], dwNumPoints);
|
|
|
|
CGU_FLOAT RmpErrG[MAX_POINTS][BLOCK_SIZE_4X4];
|
|
for(CGU_UINT32 i=0; i < _NmrClrs; i++) {
|
|
for(CGU_UINT32 r = 0; r < dwNumPoints; r++) {
|
|
CGU_FLOAT DistG = (Rmp[GC][r] - Blk[i][GC]);
|
|
RmpErrG[r][i] = DistG * DistG * fWeightGreen;
|
|
}
|
|
}
|
|
|
|
for(CGU_INT nJitterB0 = nRefineStart; nJitterB0 <= nRefineEnd; nJitterB0++) {
|
|
InpRmp[BC][0] = cmp_min(cmp_max(InpRmp0[BC][0] + nJitterB0 * Fctrs[BC], 0.f), 255.f);
|
|
for(CGU_INT nJitterB1 = nRefineStart; nJitterB1 <= nRefineEnd; nJitterB1++) {
|
|
InpRmp[BC][1] = cmp_min(cmp_max(InpRmp0[BC][1] + nJitterB1 * Fctrs[BC], 0.f), 255.f);
|
|
cpu_MkWkRmpPts(Eq, WkRmpPts, InpRmp, nRedBits, nGreenBits, nBlueBits);
|
|
cpu_BldClrRmp(Rmp[BC], WkRmpPts[BC], dwNumPoints);
|
|
|
|
CGU_FLOAT RmpErr[MAX_POINTS][BLOCK_SIZE_4X4];
|
|
for(CGU_UINT32 i=0; i < _NmrClrs; i++) {
|
|
for(CGU_UINT32 r = 0; r < dwNumPoints; r++) {
|
|
CGU_FLOAT DistB = (Rmp[BC][r] - Blk[i][BC]);
|
|
RmpErr[r][i] = RmpErrG[r][i] + DistB * DistB * fWeightBlue;
|
|
}
|
|
}
|
|
|
|
for(CGU_INT nJitterR0 = nRefineStart; nJitterR0 <= nRefineEnd; nJitterR0++) {
|
|
InpRmp[RC][0] = cmp_min(cmp_max(InpRmp0[RC][0] + nJitterR0 * Fctrs[RC], 0.f), 255.f);
|
|
for(CGU_INT nJitterR1 = nRefineStart; nJitterR1 <= nRefineEnd; nJitterR1++) {
|
|
InpRmp[RC][1] = cmp_min(cmp_max(InpRmp0[RC][1] + nJitterR1 * Fctrs[RC], 0.f), 255.f);
|
|
cpu_MkWkRmpPts(Eq, WkRmpPts, InpRmp, nRedBits, nGreenBits, nBlueBits);
|
|
cpu_BldClrRmp(Rmp[RC], WkRmpPts[RC], dwNumPoints);
|
|
|
|
// compute cumulative error
|
|
CGU_FLOAT mse = 0.f;
|
|
CGU_INT rmp_l = (Eq > 0) ? 1 : dwNumPoints;
|
|
for(CGU_UINT32 k = 0; k < _NmrClrs; k++) {
|
|
CGU_FLOAT MinErr = 10000000.f;
|
|
for(CGU_INT r = 0; r < rmp_l; r++) {
|
|
CGU_FLOAT Dist = (Rmp[RC][r] - Blk[k][RC]);
|
|
CGU_FLOAT Err = RmpErr[r][k] + Dist * Dist * fWeightRed;
|
|
MinErr = cmp_min(MinErr, Err);
|
|
}
|
|
mse += MinErr * _Rpt[k];
|
|
}
|
|
|
|
// save if we achieve better result
|
|
if(mse < bestE) {
|
|
bestE = mse;
|
|
for(CGU_UINT32 k = 0; k < 2; k++)
|
|
for(CGU_UINT32 j = 0; j < 3; j++)
|
|
_OutRmpPnts[j][k] = InpRmp[j][k];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return bestE;
|
|
}
|
|
#endif
|
|
|
|
#if defined(USE_REFINE)
|
|
CMP_STATIC CGU_FLOAT cmp_Refine(CGU_FLOAT _OutRmpPnts[NUM_CHANNELS][NUM_ENDPOINTS],
|
|
CGU_FLOAT _InpRmpPnts[NUM_CHANNELS][NUM_ENDPOINTS],
|
|
CGU_FLOAT _Blk[BLOCK_SIZE_4X4][NUM_CHANNELS],
|
|
CGU_FLOAT _Rpt[BLOCK_SIZE_4X4],
|
|
CGU_INT _NmrClrs,
|
|
CGU_UINT8 dwNumPoints,
|
|
CGU_Vec3f channelWeights,
|
|
CGU_UINT32 nRedBits,
|
|
CGU_UINT32 nGreenBits,
|
|
CGU_UINT32 nBlueBits,
|
|
CGU_UINT32 nRefineSteps )
|
|
{
|
|
CGU_FLOAT ALIGN_16 Rmp[NUM_CHANNELS][MAX_POINTS];
|
|
|
|
if (nRefineSteps == 0) nRefineSteps = 1;
|
|
|
|
|
|
CGU_FLOAT Blk[BLOCK_SIZE_4X4][NUM_CHANNELS];
|
|
for(CGU_INT i = 0; i < _NmrClrs; i++)
|
|
for(CGU_INT j = 0; j < 3; j++)
|
|
Blk[i][j] = _Blk[i][j];
|
|
|
|
CGU_FLOAT fWeightRed = channelWeights.r;
|
|
CGU_FLOAT fWeightGreen = channelWeights.g;
|
|
CGU_FLOAT fWeightBlue = channelWeights.b;
|
|
|
|
// here is our grid
|
|
CGU_FLOAT Fctrs[3];
|
|
Fctrs[RC] = (CGU_FLOAT)(1 << (PIX_GRID-nRedBits));
|
|
Fctrs[GC] = (CGU_FLOAT)(1 << (PIX_GRID-nGreenBits));
|
|
Fctrs[BC] = (CGU_FLOAT)(1 << (PIX_GRID-nBlueBits));
|
|
|
|
CGU_FLOAT InpRmp0[NUM_CHANNELS][NUM_ENDPOINTS];
|
|
CGU_FLOAT InpRmp[NUM_CHANNELS][NUM_ENDPOINTS];
|
|
for(CGU_INT k = 0; k < 2; k++)
|
|
for(CGU_INT j = 0; j < 3; j++)
|
|
InpRmp0[j][k] = InpRmp[j][k] = _OutRmpPnts[j][k] = _InpRmpPnts[j][k];
|
|
|
|
// make ramp endpoints the way they'll going to be decompressed
|
|
// plus check whether the ramp is flat
|
|
CGU_UINT8 Eq;
|
|
CGU_FLOAT WkRmpPts[NUM_CHANNELS][NUM_ENDPOINTS];
|
|
cpu_MkWkRmpPts(Eq, WkRmpPts, InpRmp, nRedBits, nGreenBits, nBlueBits);
|
|
|
|
// build ramp for all 3 colors
|
|
cpu_BldRmp(Rmp, WkRmpPts, dwNumPoints);
|
|
|
|
// clusterize for the current ramp
|
|
CGU_FLOAT bestE = cpu_ClstrErr(Blk, _Rpt, Rmp, _NmrClrs, dwNumPoints, Eq, channelWeights);
|
|
if(bestE == 0.f) // || !nRefineSteps) // if exact, we've done
|
|
return bestE;
|
|
|
|
// Tweak each component in isolation and get the best values
|
|
|
|
// precompute ramp errors for Green and Blue
|
|
CGU_FLOAT RmpErr[MAX_POINTS][BLOCK_SIZE_4X4];
|
|
for(CGU_INT i=0; i < _NmrClrs; i++) {
|
|
for(CGU_INT r = 0; r < dwNumPoints; r++) {
|
|
CGU_FLOAT DistG = (Rmp[GC][r] - Blk[i][GC]);
|
|
CGU_FLOAT DistB = (Rmp[BC][r] - Blk[i][BC]);
|
|
RmpErr[r][i] = DistG * DistG * fWeightGreen + DistB * DistB * fWeightBlue;
|
|
}
|
|
}
|
|
|
|
// First Red
|
|
CGU_FLOAT bstC0 = InpRmp0[RC][0];
|
|
CGU_FLOAT bstC1 = InpRmp0[RC][1];
|
|
CGU_INT nRefineStart = 0 - (cmp_min(nRefineSteps, (CGU_UINT8)8));
|
|
CGU_INT nRefineEnd = cmp_min(nRefineSteps, (CGU_UINT8)8);
|
|
for(CGU_INT i = nRefineStart; i <= nRefineEnd; i++) {
|
|
for(CGU_INT j = nRefineStart; j <= nRefineEnd; j++) {
|
|
// make a move; both sides of interval.
|
|
InpRmp[RC][0] = cmp_min(cmp_max(InpRmp0[RC][0] + i * Fctrs[RC], 0.f), 255.f);
|
|
InpRmp[RC][1] = cmp_min(cmp_max(InpRmp0[RC][1] + j * Fctrs[RC], 0.f), 255.f);
|
|
|
|
// make ramp endpoints the way they'll going to be decompressed
|
|
// plus check whether the ramp is flat
|
|
cpu_MkWkRmpPts(Eq, WkRmpPts, InpRmp, nRedBits, nGreenBits, nBlueBits);
|
|
|
|
// build ramp only for red
|
|
cpu_BldClrRmp(Rmp[RC], WkRmpPts[RC], dwNumPoints);
|
|
|
|
// compute cumulative error
|
|
CGU_FLOAT mse = 0.f;
|
|
CGU_INT rmp_l = (Eq > 0) ? 1 : dwNumPoints;
|
|
for(CGU_INT k = 0; k < _NmrClrs; k++) {
|
|
CGU_FLOAT MinErr = 10000000.f;
|
|
for(CGU_INT r = 0; r < rmp_l; r++) {
|
|
CGU_FLOAT Dist = (Rmp[RC][r] - Blk[k][RC]);
|
|
CGU_FLOAT Err = RmpErr[r][k] + Dist * Dist * fWeightRed;
|
|
MinErr = cmp_minf(MinErr, Err);
|
|
}
|
|
mse += MinErr * _Rpt[k];
|
|
}
|
|
|
|
// save if we achieve better result
|
|
if(mse < bestE) {
|
|
bstC0 = InpRmp[RC][0];
|
|
bstC1 = InpRmp[RC][1];
|
|
bestE = mse;
|
|
}
|
|
}
|
|
}
|
|
|
|
// our best REDs
|
|
InpRmp[RC][0] = bstC0;
|
|
InpRmp[RC][1] = bstC1;
|
|
|
|
// make ramp endpoints the way they'll going to be decompressed
|
|
// plus check whether the ramp is flat
|
|
cpu_MkWkRmpPts(Eq, WkRmpPts, InpRmp, nRedBits, nGreenBits, nBlueBits);
|
|
|
|
// build ramp only for green
|
|
cpu_BldRmp(Rmp, WkRmpPts, dwNumPoints);
|
|
|
|
// precompute ramp errors for Red and Blue
|
|
for(CGU_INT i=0; i < _NmrClrs; i++) {
|
|
for(CGU_INT r = 0; r < dwNumPoints; r++) {
|
|
CGU_FLOAT DistR = (Rmp[RC][r] - Blk[i][RC]);
|
|
CGU_FLOAT DistB = (Rmp[BC][r] - Blk[i][BC]);
|
|
RmpErr[r][i] = DistR * DistR * fWeightRed + DistB * DistB * fWeightBlue;
|
|
}
|
|
}
|
|
|
|
// Now green
|
|
bstC0 = InpRmp0[GC][0];
|
|
bstC1 = InpRmp0[GC][1];
|
|
for(CGU_INT i = nRefineStart; i <= nRefineEnd; i++) {
|
|
for(CGU_INT j = nRefineStart; j <= nRefineEnd; j++) {
|
|
InpRmp[GC][0] = cmp_minf(cmp_maxf(InpRmp0[GC][0] + i * Fctrs[GC], 0.f), 255.f);
|
|
InpRmp[GC][1] = cmp_minf(cmp_maxf(InpRmp0[GC][1] + j * Fctrs[GC], 0.f), 255.f);
|
|
|
|
cpu_MkWkRmpPts(Eq, WkRmpPts, InpRmp, nRedBits, nGreenBits, nBlueBits);
|
|
cpu_BldClrRmp(Rmp[GC], WkRmpPts[GC], dwNumPoints);
|
|
|
|
CGU_FLOAT mse = 0.f;
|
|
CGU_INT rmp_l = (Eq > 0) ? 1 : dwNumPoints;
|
|
for(CGU_INT k = 0; k < _NmrClrs; k++) {
|
|
CGU_FLOAT MinErr = 10000000.f;
|
|
for(CGU_INT r = 0; r < rmp_l; r++) {
|
|
CGU_FLOAT Dist = (Rmp[GC][r] - Blk[k][GC]);
|
|
CGU_FLOAT Err = RmpErr[r][k] + Dist * Dist * fWeightGreen;
|
|
MinErr = cmp_minf(MinErr, Err);
|
|
}
|
|
mse += MinErr * _Rpt[k];
|
|
}
|
|
|
|
if(mse < bestE) {
|
|
bstC0 = InpRmp[GC][0];
|
|
bstC1 = InpRmp[GC][1];
|
|
bestE = mse;
|
|
}
|
|
}
|
|
}
|
|
|
|
// our best GREENs
|
|
InpRmp[GC][0] = bstC0;
|
|
InpRmp[GC][1] = bstC1;
|
|
|
|
cpu_MkWkRmpPts(Eq, WkRmpPts, InpRmp, nRedBits, nGreenBits, nBlueBits);
|
|
cpu_BldRmp(Rmp, WkRmpPts, dwNumPoints);
|
|
|
|
// ramp err for Red and Green
|
|
for(CGU_INT i=0; i < _NmrClrs; i++) {
|
|
for(CGU_INT r = 0; r < dwNumPoints; r++) {
|
|
CGU_FLOAT DistR = (Rmp[RC][r] - Blk[i][RC]);
|
|
CGU_FLOAT DistG = (Rmp[GC][r] - Blk[i][GC]);
|
|
RmpErr[r][i] = DistR * DistR * fWeightRed + DistG * DistG * fWeightGreen;
|
|
}
|
|
}
|
|
|
|
bstC0 = InpRmp0[BC][0];
|
|
bstC1 = InpRmp0[BC][1];
|
|
// Now blue
|
|
for(CGU_INT i = nRefineStart; i <= nRefineEnd; i++) {
|
|
for(CGU_INT j = nRefineStart; j <= nRefineEnd; j++) {
|
|
InpRmp[BC][0] = min(max(InpRmp0[BC][0] + i * Fctrs[BC], 0.f), 255.f);
|
|
InpRmp[BC][1] = min(max(InpRmp0[BC][1] + j * Fctrs[BC], 0.f), 255.f);
|
|
|
|
cpu_MkWkRmpPts(Eq, WkRmpPts, InpRmp, nRedBits, nGreenBits, nBlueBits);
|
|
cpu_BldClrRmp(Rmp[BC], WkRmpPts[BC], dwNumPoints);
|
|
|
|
CGU_FLOAT mse = 0.f;
|
|
CGU_INT rmp_l = (Eq > 0) ? 1 : dwNumPoints;
|
|
for(CGU_INT k = 0; k < _NmrClrs; k++) {
|
|
CGU_FLOAT MinErr = 10000000.f;
|
|
for(CGU_INT r = 0; r < rmp_l; r++) {
|
|
CGU_FLOAT Dist = (Rmp[BC][r] - Blk[k][BC]);
|
|
CGU_FLOAT Err = RmpErr[r][k] + Dist * Dist * fWeightBlue;
|
|
MinErr = min(MinErr, Err);
|
|
}
|
|
mse += MinErr * _Rpt[k];
|
|
}
|
|
|
|
if(mse < bestE) {
|
|
bstC0 = InpRmp[BC][0];
|
|
bstC1 = InpRmp[BC][1];
|
|
bestE = mse;
|
|
}
|
|
}
|
|
}
|
|
|
|
// our best BLUEs
|
|
InpRmp[BC][0] = bstC0;
|
|
InpRmp[BC][1] = bstC1;
|
|
|
|
// return our best choice
|
|
for(CGU_INT j = 0; j < 3; j++)
|
|
for(CGU_INT k = 0; k < 2; k++)
|
|
_OutRmpPnts[j][k] = InpRmp[j][k];
|
|
|
|
return bestE;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
//======================================================================================
|
|
// Codec from CompressonatorLib
|
|
//======================================================================================
|
|
#define BLOCK_SIZE_4X4 16
|
|
#define RG 5
|
|
#define GG 6
|
|
#define BG 5
|
|
|
|
/*------------------------------------------------------------------------------------------------
|
|
// this is how the end points is going to be rounded in compressed format
|
|
------------------------------------------------------------------------------------------------*/
|
|
CMP_STATIC void cpu_MkRmpOnGrid(CGU_FLOAT _RmpF[NUM_CHANNELS][NUM_ENDPOINTS],
|
|
CGU_FLOAT _MnMx[NUM_CHANNELS][NUM_ENDPOINTS],
|
|
CGU_FLOAT _Min,
|
|
CGU_FLOAT _Max,
|
|
CGU_UINT8 nRedBits,
|
|
CGU_UINT8 nGreenBits,
|
|
CGU_UINT8 nBlueBits)
|
|
{
|
|
CGU_FLOAT Fctrs0[3];
|
|
CGU_FLOAT Fctrs1[3];
|
|
|
|
Fctrs1[RC] = (CGU_FLOAT)(1 << nRedBits);
|
|
Fctrs1[GC] = (CGU_FLOAT)(1 << nGreenBits);
|
|
Fctrs1[BC] = (CGU_FLOAT)(1 << nBlueBits);
|
|
Fctrs0[RC] = (CGU_FLOAT)(1 << (PIX_GRID-nRedBits));
|
|
Fctrs0[GC] = (CGU_FLOAT)(1 << (PIX_GRID-nGreenBits));
|
|
Fctrs0[BC] = (CGU_FLOAT)(1 << (PIX_GRID-nBlueBits));
|
|
|
|
for(int j = 0; j < 3; j++) {
|
|
for(int k = 0; k < 2; k++) {
|
|
_RmpF[j][k] = cmp_floor(_MnMx[j][k]);
|
|
if(_RmpF[j][k] <= _Min)
|
|
_RmpF[j][k] = _Min;
|
|
else {
|
|
_RmpF[j][k] += cmp_floor(128.f / Fctrs1[j]) - cmp_floor(_RmpF[j][k] / Fctrs1[j]);
|
|
_RmpF[j][k] = cmp_minf(_RmpF[j][k], _Max);
|
|
}
|
|
|
|
_RmpF[j][k] = cmp_floor(_RmpF[j][k] / Fctrs0[j]) * Fctrs0[j];
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Find the first approximation of the line
|
|
// Assume there is a linear relation
|
|
// Z = a * X_In
|
|
// Z = b * Y_In
|
|
// Find a,b to minimize MSE between Z and Z_In
|
|
CMP_STATIC void cpu_FindAxis(CMP_OUT CGU_FLOAT BlkSh[BLOCK_SIZE_4X4][NUM_CHANNELS],
|
|
CMP_IN CGU_FLOAT LineDir0[NUM_CHANNELS],
|
|
CMP_IN CGU_FLOAT fBlockCenter[NUM_CHANNELS],
|
|
CMP_OUT CGU_UINT8 CMP_REFINOUT AxisIsSmall,
|
|
CMP_IN CGU_FLOAT BlkUV[BLOCK_SIZE_4X4][NUM_CHANNELS],
|
|
CMP_IN CGU_FLOAT _inpRpt[BLOCK_SIZE_4X4],
|
|
CMP_IN int nDimensions,
|
|
CMP_IN int dwUniqueColors)
|
|
{
|
|
CGU_FLOAT Crrl[NUM_CHANNELS];
|
|
CGU_FLOAT RGB2[NUM_CHANNELS];
|
|
CGU_INT i;
|
|
|
|
LineDir0[0] = LineDir0[1] = LineDir0[2] = RGB2[0] = RGB2[1] = RGB2[2] =
|
|
Crrl[0] = Crrl[1] = Crrl[2] = fBlockCenter[0] = fBlockCenter[1] = fBlockCenter[2] = 0.f;
|
|
|
|
// sum position of all points
|
|
CGU_FLOAT fNumPoints = 0.f;
|
|
for(i=0; i < dwUniqueColors; i++) {
|
|
fBlockCenter[0] += BlkUV[i][0] * _inpRpt[i];
|
|
fBlockCenter[1] += BlkUV[i][1] * _inpRpt[i];
|
|
fBlockCenter[2] += BlkUV[i][2] * _inpRpt[i];
|
|
fNumPoints += _inpRpt[i];
|
|
}
|
|
|
|
// and then average to calculate center coordinate of block
|
|
fBlockCenter[0] /= fNumPoints;
|
|
fBlockCenter[1] /= fNumPoints;
|
|
fBlockCenter[2] /= fNumPoints;
|
|
|
|
for(i = 0; i < dwUniqueColors; i++) {
|
|
// calculate output block as offsets around block center
|
|
BlkSh[i][0] = BlkUV[i][0] - fBlockCenter[0];
|
|
BlkSh[i][1] = BlkUV[i][1] - fBlockCenter[1];
|
|
BlkSh[i][2] = BlkUV[i][2] - fBlockCenter[2];
|
|
|
|
// compute correlation matrix
|
|
// RGB2 = sum of ((distance from point from center) squared)
|
|
// Crrl = ???????. Seems to be be some calculation based on distance from point center in two dimensions
|
|
for(int j = 0; j < nDimensions; j++) {
|
|
RGB2[j] += BlkSh[i][j] * BlkSh[i][j] * _inpRpt[i];
|
|
Crrl[j] += BlkSh[i][j] * BlkSh[i][(j+1)%3] * _inpRpt[i];
|
|
}
|
|
}
|
|
|
|
// if set's diameter is small
|
|
int i0 = 0, i1 = 1;
|
|
CGU_FLOAT mxRGB2 = 0.f;
|
|
int k = 0, j = 0;
|
|
CGU_FLOAT fEPS = fNumPoints * EPS;
|
|
for(k = 0, j = 0; j < 3; j++) {
|
|
if(RGB2[j] >= fEPS)
|
|
k++;
|
|
else
|
|
RGB2[j] = 0.f;
|
|
|
|
if(mxRGB2 < RGB2[j]) {
|
|
mxRGB2 = RGB2[j];
|
|
i0 = j;
|
|
}
|
|
}
|
|
|
|
CGU_FLOAT fEPS2 = fNumPoints * EPS2;
|
|
AxisIsSmall = 1;
|
|
for(j = 0; j < 3; j++)
|
|
{
|
|
AxisIsSmall &= (RGB2[j] < fEPS2);
|
|
}
|
|
|
|
if(AxisIsSmall) // all are very small to avoid division on the small determinant
|
|
return;
|
|
|
|
if(k == 1) // really only 1 dimension
|
|
LineDir0[i0]= 1.;
|
|
else if(k == 2) { // really only 2 dimensions
|
|
i1 = (RGB2[(i0+1)%3] > 0.f) ? (i0+1)%3 : (i0+2)%3;
|
|
CGU_FLOAT Crl = (i1 == (i0+1)%3) ? Crrl[i0] : Crrl[(i0+2)%3];
|
|
LineDir0[i1] = Crl/ RGB2[i0];
|
|
LineDir0[i0]= 1.;
|
|
} else {
|
|
CGU_FLOAT maxDet = 100000.f;
|
|
CGU_FLOAT Cs[3];
|
|
// select max det for precision
|
|
for(j = 0; j < nDimensions; j++) {
|
|
CGU_FLOAT Det = RGB2[j] * RGB2[(j+1)%3] - Crrl[j] * Crrl[j];
|
|
Cs[j] = abs(Crrl[j]/sqrt(RGB2[j] * RGB2[(j+1)%3]));
|
|
if(maxDet < Det) {
|
|
maxDet = Det;
|
|
i0 = j;
|
|
}
|
|
}
|
|
|
|
// inverse correl matrix
|
|
// -- -- -- --
|
|
// | A B | | C -B |
|
|
// | B C | => | -B A |
|
|
// -- -- -- --
|
|
CGU_FLOAT mtrx1[2][2];
|
|
CGU_FLOAT vc1[2];
|
|
CGU_FLOAT vc[2];
|
|
vc1[0] = Crrl[(i0 + 2) %3];
|
|
vc1[1] = Crrl[(i0 + 1) %3];
|
|
// C
|
|
mtrx1[0][0] = RGB2[(i0+1)%3];
|
|
// A
|
|
mtrx1[1][1] = RGB2[i0];
|
|
// -B
|
|
mtrx1[1][0] = mtrx1[0][1] = -Crrl[i0];
|
|
// find a solution
|
|
vc[0] = mtrx1[0][0] * vc1[0] + mtrx1[0][1] * vc1[1];
|
|
vc[1] = mtrx1[1][0] * vc1[0] + mtrx1[1][1] * vc1[1];
|
|
// normalize
|
|
vc[0] /= maxDet;
|
|
vc[1] /= maxDet;
|
|
// find a line direction vector
|
|
LineDir0[i0] = 1.;
|
|
LineDir0[(i0 + 1) %3] = 1.;
|
|
LineDir0[(i0 + 2) %3] = vc[0] + vc[1];
|
|
}
|
|
|
|
// normalize direction vector
|
|
CGU_FLOAT Len = LineDir0[0] * LineDir0[0] + LineDir0[1] * LineDir0[1] + LineDir0[2] * LineDir0[2];
|
|
Len = sqrt(Len);
|
|
|
|
for(j = 0; j < 3; j++)
|
|
LineDir0[j] = (Len > 0.f) ? LineDir0[j] / Len : 0.f;
|
|
}
|
|
|
|
CMP_STATIC CGU_FLOAT cpu_RampSrchW(CGU_FLOAT Prj[BLOCK_SIZE_4X4],
|
|
CGU_FLOAT PrjErr[BLOCK_SIZE_4X4],
|
|
CGU_FLOAT PreMRep[BLOCK_SIZE_4X4],
|
|
CGU_FLOAT StepErr,
|
|
CGU_FLOAT lowPosStep,
|
|
CGU_FLOAT highPosStep,
|
|
int dwUniqueColors,
|
|
int dwNumPoints)
|
|
{
|
|
CGU_FLOAT error = 0.0f;
|
|
CGU_FLOAT step = (highPosStep - lowPosStep)/(dwNumPoints - 1);
|
|
CGU_FLOAT step_h = step * 0.5f;
|
|
CGU_FLOAT rstep = (CGU_FLOAT)1.0f / step;
|
|
CGU_INT i;
|
|
|
|
for(i=0; i < dwUniqueColors; i++)
|
|
{
|
|
// Work out which value in the block this select
|
|
CGU_FLOAT del = Prj[i] - lowPosStep;
|
|
|
|
CGU_FLOAT v;
|
|
|
|
if(del <= 0)
|
|
v = lowPosStep;
|
|
else if(Prj[i] - highPosStep >= 0)
|
|
v = highPosStep;
|
|
else
|
|
v = cmp_floor((del + step_h) * rstep) * step + lowPosStep;
|
|
|
|
// And accumulate the error
|
|
CGU_FLOAT d = (Prj[i] - v);
|
|
d *= d;
|
|
CGU_FLOAT err = PreMRep[i] * d + PrjErr[i];
|
|
error += err;
|
|
if(StepErr < error) {
|
|
error = StepErr;
|
|
break;
|
|
}
|
|
}
|
|
return error;
|
|
}
|
|
|
|
CMP_STATIC CGU_FLOAT _cpu_bc1ComputeBestEndpoints(CGU_FLOAT endpointsOut[NUM_ENDPOINTS], CGU_FLOAT endpointsIn[NUM_ENDPOINTS],
|
|
CGU_FLOAT prj[BLOCK_SIZE_4X4], CGU_FLOAT prjError[BLOCK_SIZE_4X4], CGU_FLOAT preMRep[BLOCK_SIZE_4X4],
|
|
int numColours, int numPoints)
|
|
{
|
|
CGU_FLOAT minError = MAX_ERROR;
|
|
|
|
static const CGU_FLOAT searchStep = 0.025f;
|
|
|
|
const CGU_FLOAT lowStart = (endpointsIn[0] - 2.0f*searchStep > 0.0f) ? endpointsIn[0] - 2.0f*searchStep : 0.0f;
|
|
const CGU_FLOAT highStart = (endpointsIn[1] + 2.0f*searchStep < 1.0f) ? endpointsIn[1] + 2.0f*searchStep : 1.0f;
|
|
|
|
CGU_FLOAT lowStep = lowStart;
|
|
CGU_FLOAT highStep = highStart;
|
|
|
|
for(int low = 0; low < 8; ++low)
|
|
{
|
|
for(int high = 0; high < 8; ++high)
|
|
{
|
|
// compute an error for the current pair of end points.
|
|
CGU_FLOAT error = cpu_RampSrchW(prj, prjError, preMRep, minError, lowStep, highStep, numColours, numPoints);
|
|
|
|
if(error < minError) {
|
|
// save better result
|
|
minError = error;
|
|
endpointsOut[0] = lowStep;
|
|
endpointsOut[1] = highStep;
|
|
}
|
|
|
|
highStep -= searchStep;
|
|
}
|
|
|
|
lowStep += searchStep;
|
|
}
|
|
|
|
return minError;
|
|
}
|
|
|
|
// This is a float point-based compression
|
|
// it assumes that the number of unique colors is already known; input is in [0., 255.] range.
|
|
// This is C version.
|
|
CMP_STATIC bool cpu_CompressRGBBlockX( CMP_OUT CGU_FLOAT _RsltRmpPnts[NUM_CHANNELS][NUM_ENDPOINTS],
|
|
CMP_IN CGU_FLOAT src_image[BLOCK_SIZE_4X4][NUM_CHANNELS],
|
|
CMP_IN CGU_FLOAT Rpt[BLOCK_SIZE_4X4],
|
|
CMP_IN int dwUniqueColors,
|
|
CMP_IN CGU_UINT8 dwNumPoints,
|
|
CMP_IN bool b3DRefinement,
|
|
CMP_IN CGU_UINT8 nRefinementSteps,
|
|
CMP_IN CGU_FLOAT pfWeights[3],
|
|
CMP_IN CGU_UINT8 nRedBits,
|
|
CMP_IN CGU_UINT8 nGreenBits,
|
|
CMP_IN CGU_UINT8 nBlueBits,
|
|
CMP_IN CGU_FLOAT fquality )
|
|
{
|
|
#if !defined(ASPM_GPU)
|
|
if (!g_bc1FunctionPointersSet)
|
|
{
|
|
bc1ToggleSIMD(EXTENSION_COUNT);
|
|
}
|
|
#endif
|
|
|
|
CGU_FLOAT ALIGN_16 Prj0[BLOCK_SIZE_4X4];
|
|
CGU_FLOAT ALIGN_16 Prj[BLOCK_SIZE_4X4];
|
|
CGU_FLOAT ALIGN_16 PrjErr[BLOCK_SIZE_4X4];
|
|
CGU_FLOAT ALIGN_16 LineDir[NUM_CHANNELS];
|
|
CGU_FLOAT ALIGN_16 RmpIndxs[BLOCK_SIZE_4X4];
|
|
|
|
CMP_UNUSED(fquality);
|
|
CMP_UNUSED(b3DRefinement)
|
|
|
|
CGU_FLOAT LineDirG[NUM_CHANNELS];
|
|
CGU_FLOAT PosG[NUM_ENDPOINTS];
|
|
CGU_FLOAT BlkUV[BLOCK_SIZE_4X4][NUM_CHANNELS];
|
|
CGU_FLOAT BlkSh[BLOCK_SIZE_4X4][NUM_CHANNELS];
|
|
CGU_FLOAT LineDir0[NUM_CHANNELS];
|
|
CGU_FLOAT Mdl[NUM_CHANNELS];
|
|
|
|
CGU_FLOAT rsltC[NUM_CHANNELS][NUM_ENDPOINTS];
|
|
int i, j, k;
|
|
|
|
// down to [0., 1.]
|
|
for(i = 0; i < dwUniqueColors; i++)
|
|
for(j = 0; j < 3; j++)
|
|
BlkUV[i][j] = src_image[i][j] / 255.f;
|
|
|
|
bool isDONE = false;
|
|
|
|
// as usual if not more then 2 different colors, we've done
|
|
if(dwUniqueColors <= 2) {
|
|
for(j = 0; j < 3; j++) {
|
|
rsltC[j][0] = src_image[0][j];
|
|
rsltC[j][1] = src_image[dwUniqueColors - 1][j];
|
|
}
|
|
isDONE = true;
|
|
}
|
|
|
|
if ( !isDONE ) {
|
|
// This is our first attempt to find an axis we will go along.
|
|
// The cumulation is done to find a line minimizing the MSE from the input 3D points.
|
|
CGU_UINT8 bSmall;
|
|
cpu_FindAxis(BlkSh, LineDir0, Mdl, bSmall, BlkUV, Rpt, 3, dwUniqueColors);
|
|
|
|
// While trying to find the axis we found that the diameter of the input set is quite small.
|
|
// Do not bother.
|
|
if(bSmall) {
|
|
for(j = 0; j < 3; j++) {
|
|
rsltC[j][0] = src_image[0][j];
|
|
rsltC[j][1] = src_image[dwUniqueColors - 1][j];
|
|
}
|
|
isDONE = true;
|
|
}
|
|
}
|
|
|
|
// GCC is being an awful being when it comes to goto-jumps.
|
|
// So please bear with this.
|
|
if ( !isDONE ) {
|
|
CGU_FLOAT ErrG = 10000000.f;
|
|
CGU_FLOAT PrjBnd[NUM_ENDPOINTS];
|
|
CGU_FLOAT ALIGN_16 PreMRep[BLOCK_SIZE_4X4];
|
|
for(j =0; j < 3; j++)
|
|
LineDir[j] = LineDir0[j];
|
|
|
|
// Here is the main loop.
|
|
// 1. Project input set on the axis in consideration.
|
|
// 2. Run 1 dimensional search (see scalar case) to find an (sub) optimal pair of end points.
|
|
// 3. Compute the vector of indexes (or clusters) for the current approximate ramp.
|
|
// 4. Present our color channels as 3 16DIM vectors.
|
|
// 5. Find closest approximation of each of 16DIM color vector with the projection of the 16DIM index vector.
|
|
// 6. Plug the projections as a new directional vector for the axis.
|
|
// 7. Goto 1.
|
|
// D - is 16 dim "index" vector (or 16 DIM vector of indexes - {0, 1/3, 2/3, 0, ...,}, but shifted and normalized).
|
|
// Ci - is a 16 dim vector of color i.
|
|
// for each Ci find a scalar Ai such that
|
|
// (Ai * D - Ci) (Ai * D - Ci) -> min , i.e distance between vector AiD and C is min.
|
|
// You can think of D as a unit interval(vector) "clusterizer",
|
|
// and Ai is a scale you need to apply to the clusterizer to
|
|
// approximate the Ci vector instead of the unit vector.
|
|
// Solution is
|
|
// Ai = (D . Ci) / (D . D); . - is a dot product.
|
|
// in 3 dim space Ai(s) represent a line direction, along which
|
|
// we again try to find (sub)optimal quantizer.
|
|
|
|
// That's what our for(;;) loop is about.
|
|
for(;;) {
|
|
// 1. Project input set on the axis in consideration.
|
|
// From Foley & Van Dam: Closest point of approach of a line (P + v) to a point (R) is
|
|
// P + ((R-P).v) / (v.v))v
|
|
// The distance along v is therefore (R-P).v / (v.v)
|
|
// (v.v) is 1 if v is a unit vector.
|
|
//
|
|
PrjBnd[0] = 1000.;
|
|
PrjBnd[1] = -1000.;
|
|
for(i = 0; i < BLOCK_SIZE_4X4; i++)
|
|
Prj0[i] = Prj[i] = PrjErr[i] = PreMRep[i] = 0.f;
|
|
|
|
for(i = 0; i < dwUniqueColors; i++) {
|
|
Prj0[i] = Prj[i] = BlkSh[i][0] * LineDir[0] + BlkSh[i][1] * LineDir[1] + BlkSh[i][2] * LineDir[2];
|
|
|
|
PrjErr[i] = (BlkSh[i][0] - LineDir[0] * Prj[i]) * (BlkSh[i][0] - LineDir[0] * Prj[i])
|
|
+ (BlkSh[i][1] - LineDir[1] * Prj[i]) * (BlkSh[i][1] - LineDir[1] * Prj[i])
|
|
+ (BlkSh[i][2] - LineDir[2] * Prj[i]) * (BlkSh[i][2] - LineDir[2] * Prj[i]);
|
|
|
|
PrjBnd[0] = min(PrjBnd[0], Prj[i]);
|
|
PrjBnd[1] = max(PrjBnd[1], Prj[i]);
|
|
}
|
|
|
|
// 2. Run 1 dimensional search (see scalar case) to find an (sub) optimal pair of end points.
|
|
|
|
// min and max of the search interval
|
|
CGU_FLOAT stepf = 0.125f;
|
|
|
|
CGU_FLOAT Scl[NUM_ENDPOINTS];
|
|
Scl[0] = PrjBnd[0] - (PrjBnd[1] - PrjBnd[0]) * stepf;
|
|
Scl[1] = PrjBnd[1] + (PrjBnd[1] - PrjBnd[0]) * stepf;
|
|
|
|
// No range found exit
|
|
if (Scl[0] == Scl[1]) {
|
|
return false;
|
|
}
|
|
|
|
// compute scaling factor to scale down the search interval to [0.,1]
|
|
const CGU_FLOAT Scl2 = (Scl[1] - Scl[0]) * (Scl[1] - Scl[0]);
|
|
const CGU_FLOAT overScl = 1.f/(Scl[1] - Scl[0]);
|
|
|
|
for(i = 0; i < dwUniqueColors; i++) {
|
|
// scale them
|
|
Prj[i] = (Prj[i] - Scl[0]) * overScl;
|
|
// premultiply the scale squire to plug into error computation later
|
|
PreMRep[i] = Rpt[i] * Scl2;
|
|
}
|
|
|
|
// scale first approximation of end points
|
|
PrjBnd[0] = (PrjBnd[0] - Scl[0]) * overScl;
|
|
PrjBnd[1] = (PrjBnd[1] - Scl[0]) * overScl;
|
|
|
|
// find the best endpoints
|
|
CGU_FLOAT Pos[NUM_ENDPOINTS];
|
|
#if defined(ASPM_GPU)
|
|
CGU_FLOAT StepErr = _cpu_bc1ComputeBestEndpoints(Pos, PrjBnd, Prj, PrjErr, PreMRep, dwUniqueColors, dwNumPoints);
|
|
#else
|
|
CGU_FLOAT StepErr = cpu_bc1ComputeBestEndpoints(Pos, PrjBnd, Prj, PrjErr, PreMRep, dwUniqueColors, dwNumPoints);
|
|
#endif
|
|
|
|
// inverse the scaling
|
|
Pos[0] = Pos[0] * (Scl[1] - Scl[0])+ Scl[0];
|
|
Pos[1] = Pos[1] * (Scl[1] - Scl[0])+ Scl[0];
|
|
|
|
// did we find somthing better from the previous run?
|
|
if(StepErr + 0.001 < ErrG) {
|
|
// yes, remember it
|
|
ErrG = StepErr;
|
|
LineDirG[0] = LineDir[0];
|
|
LineDirG[1] = LineDir[1];
|
|
LineDirG[2] = LineDir[2];
|
|
PosG[0] = Pos[0];
|
|
PosG[1] = Pos[1];
|
|
// 3. Compute the vector of indexes (or clusters) for the current approximate ramp.
|
|
// indexes
|
|
const CGU_FLOAT step = (Pos[1] - Pos[0]) / (CGU_FLOAT)(dwNumPoints - 1);
|
|
const CGU_FLOAT step_h = step * (CGU_FLOAT)0.5;
|
|
const CGU_FLOAT rstep = (CGU_FLOAT)1.0f / step;
|
|
const CGU_FLOAT overBlkTp = 1.f/ (CGU_FLOAT)(dwNumPoints - 1) ;
|
|
|
|
// here the index vector is computed,
|
|
// shifted and normalized
|
|
CGU_FLOAT indxAvrg = (CGU_FLOAT)(dwNumPoints - 1) / 2.f;
|
|
|
|
for(i=0; i < dwUniqueColors; i++) {
|
|
CGU_FLOAT del;
|
|
//int n = (int)((b - _min_ex + (step*0.5f)) * rstep);
|
|
if((del = Prj0[i] - Pos[0]) <= 0)
|
|
RmpIndxs[i] = 0.f;
|
|
else if(Prj0[i] - Pos[1] >= 0)
|
|
RmpIndxs[i] = (CGU_FLOAT)(dwNumPoints - 1);
|
|
else
|
|
RmpIndxs[i] = cmp_floor((del + step_h) * rstep);
|
|
// shift and normalization
|
|
RmpIndxs[i] = (RmpIndxs[i] - indxAvrg) * overBlkTp;
|
|
}
|
|
|
|
// 4. Present our color channels as 3 16DIM vectors.
|
|
// 5. Find closest aproximation of each of 16DIM color vector with the pojection of the 16DIM index vector.
|
|
CGU_FLOAT Crs[3], Len, Len2;
|
|
for(i = 0, Crs[0] = Crs[1] = Crs[2] = Len = 0.f; i < dwUniqueColors; i++) {
|
|
const CGU_FLOAT PreMlt = RmpIndxs[i] * Rpt[i];
|
|
Len += RmpIndxs[i] * PreMlt;
|
|
for(j = 0; j < 3; j++)
|
|
Crs[j] += BlkSh[i][j] * PreMlt;
|
|
}
|
|
|
|
LineDir[0] = LineDir[1] = LineDir[2] = 0.f;
|
|
if(Len > 0.f) {
|
|
LineDir[0] = Crs[0]/ Len;
|
|
LineDir[1] = Crs[1]/ Len;
|
|
LineDir[2] = Crs[2]/ Len;
|
|
|
|
// 6. Plug the projections as a new directional vector for the axis.
|
|
// 7. Goto 1.
|
|
Len2 = LineDir[0] * LineDir[0] + LineDir[1] * LineDir[1] + LineDir[2] * LineDir[2];
|
|
Len2 = sqrt(Len2);
|
|
|
|
LineDir[0] /= Len2;
|
|
LineDir[1] /= Len2;
|
|
LineDir[2] /= Len2;
|
|
}
|
|
} else // We was not able to find anything better. Drop dead.
|
|
break;
|
|
}
|
|
|
|
// inverse transform to find end-points of 3-color ramp
|
|
for(k = 0; k < 2; k++)
|
|
for(j = 0; j < 3; j++)
|
|
rsltC[j][k] = (PosG[k] * LineDirG[j] + Mdl[j]) * 255.f;
|
|
}
|
|
|
|
// We've dealt with (almost) unrestricted full precision realm.
|
|
// Now back to the dirty digital world.
|
|
|
|
// round the end points to make them look like compressed ones
|
|
CGU_FLOAT inpRmpEndPts[NUM_CHANNELS][NUM_ENDPOINTS];
|
|
cpu_MkRmpOnGrid(inpRmpEndPts, rsltC, 0.f, 255.f, nRedBits, nGreenBits, nBlueBits);
|
|
|
|
// Try using this on 3 channels
|
|
// static CGU_Vec2i cmp_getLinearEndPoints(CGU_FLOAT _Blk[BLOCK_SIZE_4X4], CMP_IN CGU_FLOAT fquality, CMP_IN CGU_BOOL isSigned);
|
|
|
|
// This not a small procedure squeezes and stretches the ramp along each axis (R,G,B) separately while other 2 are fixed.
|
|
// It does it only over coarse grid - 565 that is. It tries to squeeze more precision for the real world ramp.
|
|
#if defined(USE_REFINE) || defined(USE_REFINE3D)
|
|
switch(nRefinementSteps) {
|
|
case 1:
|
|
cmp_Refine(_RsltRmpPnts, inpRmpEndPts, src_image, Rpt, dwUniqueColors, dwNumPoints, pfWeights, nRedBits, nGreenBits, nBlueBits,3);
|
|
break;
|
|
case 2:
|
|
if (dwUniqueColors > 2)
|
|
cmp_Refine3D(_RsltRmpPnts, inpRmpEndPts, src_image, Rpt, dwUniqueColors, dwNumPoints, pfWeights, nRedBits, nGreenBits, nBlueBits, 1);
|
|
else
|
|
cmp_Refine(_RsltRmpPnts, inpRmpEndPts, src_image, Rpt, dwUniqueColors, dwNumPoints, pfWeights, nRedBits, nGreenBits, nBlueBits,3);
|
|
break;
|
|
default:
|
|
cmp_Refine(_RsltRmpPnts, inpRmpEndPts, src_image, Rpt, dwUniqueColors, dwNumPoints, pfWeights, nRedBits, nGreenBits, nBlueBits, 1);
|
|
break;
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
// CPU: CompRGBBlock()
|
|
CMP_STATIC CGU_FLOAT cpu_CompRGBBlock32(CGU_UINT32 block_32[16],
|
|
CGU_UINT32 compressedBlock[2],
|
|
CGU_UINT32 dwBlockSize,
|
|
CGU_UINT8 nRedBits,
|
|
CGU_UINT8 nGreenBits,
|
|
CGU_UINT8 nBlueBits,
|
|
CGU_UINT8 nEndpoints[3][NUM_ENDPOINTS],
|
|
CGU_UINT8 pcIndices[BLOCK_SIZE_4X4],
|
|
CGU_UINT8 dwNumPoints,
|
|
bool b3DRefinement,
|
|
CGU_UINT8 m_nRefinementSteps,
|
|
CGU_FLOAT _pfChannelWeights[3],
|
|
bool _bUseAlpha,
|
|
CGU_UINT8 _nAlphaThreshold)
|
|
{
|
|
CGU_FLOAT ALIGN_16 Rpt[BLOCK_SIZE_4X4];
|
|
CGU_FLOAT ALIGN_16 BlkIn[BLOCK_SIZE_4X4][NUM_CHANNELS];
|
|
CGU_UINT32 mx;
|
|
for (mx=0; mx < BLOCK_SIZE_4X4; mx++) {
|
|
Rpt[mx] = 0;
|
|
BlkIn[mx][0] = 0;
|
|
BlkIn[mx][1] = 0;
|
|
BlkIn[mx][2] = 0;
|
|
BlkIn[mx][3] = 0;
|
|
}
|
|
|
|
compressedBlock[0] = 0;
|
|
|
|
CGU_UINT32 dwAlphaThreshold = _nAlphaThreshold << 24;
|
|
CGU_UINT32 dwColors = 0;
|
|
CGU_UINT32 dwBlk[BLOCK_SIZE];
|
|
for(CGU_UINT32 i = 0; i < dwBlockSize; i++)
|
|
if(!_bUseAlpha || (block_32[i] & 0xff000000) >= dwAlphaThreshold)
|
|
dwBlk[dwColors++] = block_32[i] | 0xff000000;
|
|
|
|
// Do we have any colors ?
|
|
static int id=0;
|
|
if(dwColors) {
|
|
bool bHasAlpha = (dwColors != dwBlockSize);
|
|
if(bHasAlpha && _bUseAlpha && !(dwNumPoints & 0x1))
|
|
return CMP_FLT_MAX;
|
|
|
|
// Here we are computing an unique number of colors.
|
|
// For each unique value we compute the number of it appearences.
|
|
//qsort((void *)dwBlk, (size_t)dwColors, sizeof(CGU_UINT32), QSortIntCmp);
|
|
#ifndef ASPM_GPU // this is here for reminder when code moves to GPU
|
|
std::sort(dwBlk, dwBlk + 15);
|
|
#else
|
|
{
|
|
CGU_UINT32 j;
|
|
CMP_di what[BLOCK_SIZE_4X4];
|
|
|
|
for (i = 0; i < dwColors; i++)
|
|
{
|
|
what[i].index = i;
|
|
what[i].data = dwBlk[i];
|
|
}
|
|
|
|
CGU_UINT32 tmp_index;
|
|
CGU_UINT32 tmp_data;
|
|
|
|
for (i = 1; i < dwColors; i++)
|
|
{
|
|
for (j = i; j > 0; j--)
|
|
{
|
|
if (what[j - 1].data > what[j].data)
|
|
{
|
|
tmp_index = what[j].index;
|
|
tmp_data = what[j].data;
|
|
what[j].index = what[j - 1].index;
|
|
what[j].data = what[j - 1].data;
|
|
what[j - 1].index = tmp_index;
|
|
what[j - 1].data = tmp_data;
|
|
}
|
|
}
|
|
}
|
|
for (i = 0; i < dwColors; i++)
|
|
dwBlk[i] = what[i].data;
|
|
}
|
|
#endif
|
|
|
|
|
|
CGU_UINT32 new_p;
|
|
CGU_UINT32 dwBlkU[BLOCK_SIZE_4X4];
|
|
CGU_UINT32 dwUniqueColors = 0;
|
|
new_p = dwBlkU[0] = dwBlk[0];
|
|
Rpt[dwUniqueColors] = 1.f;
|
|
CGU_UINT32 i;
|
|
for( i = 1; i < dwColors; i++) {
|
|
if(new_p != dwBlk[i]) {
|
|
dwUniqueColors++;
|
|
new_p = dwBlkU[dwUniqueColors] = dwBlk[i];
|
|
Rpt[dwUniqueColors] = 1.f;
|
|
} else
|
|
Rpt[dwUniqueColors] += 1.f;
|
|
}
|
|
dwUniqueColors++;
|
|
|
|
// switch to float
|
|
for( i=0; i<dwUniqueColors; i++) {
|
|
BlkIn[i][RC] = (CGU_FLOAT)((dwBlkU[i] >> 16) & 0xff); // R
|
|
BlkIn[i][GC] = (CGU_FLOAT)((dwBlkU[i] >> 8) & 0xff); // G
|
|
BlkIn[i][BC] = (CGU_FLOAT)((dwBlkU[i] >> 0) & 0xff); // B
|
|
BlkIn[i][AC] = 255.0f;
|
|
}
|
|
|
|
CGU_FLOAT rsltC[NUM_CHANNELS][NUM_ENDPOINTS];
|
|
if (cpu_CompressRGBBlockX(rsltC, // CMP_EndPoints = CompressRGBBlock_Slow2 (
|
|
BlkIn, // CGU_Vec3f src_imageNorm[BLOCK_SIZE_4X4]
|
|
Rpt, // CGU_FLOAT Rpt[BLOCK_SIZE_4X4],
|
|
dwUniqueColors, // CGU_UINT32 dwUniqueColors,
|
|
dwNumPoints, // CGU_UINT32 dwNumPoints,
|
|
b3DRefinement, //
|
|
m_nRefinementSteps, // CGU_UINT32 m_nRefinementSteps,
|
|
_pfChannelWeights, // CGU_Vec3f channelWeightsBGR,
|
|
nRedBits, // );
|
|
nGreenBits,
|
|
nBlueBits,
|
|
1.0f) )
|
|
{
|
|
// return to integer realm
|
|
for(int ch = 0; ch < 3; ch++)
|
|
for(int j = 0; j < 2; j++)
|
|
nEndpoints[ch][j] = (CGU_UINT8 )rsltC[ch][j];
|
|
//printf("Endpoints {%3d,%3d,%3d} {%3d,%3d,%3d} ", nEndpoints[0][0],nEndpoints[1][0],nEndpoints[2][0],
|
|
// nEndpoints[0][1],nEndpoints[1][1],nEndpoints[2][1]);
|
|
|
|
// Now get the indices using the new end points
|
|
return cpu_Clstr(block_32, dwBlockSize, nEndpoints, pcIndices, dwNumPoints, _pfChannelWeights, _bUseAlpha,_nAlphaThreshold, nRedBits, nGreenBits, nBlueBits);
|
|
}
|
|
else {
|
|
CGU_FLOAT CompErr = CMP_FLT_MAX;
|
|
if (dwNumPoints < 4) {
|
|
CGU_Vec3f src_imageNorm[BLOCK_SIZE_4X4];
|
|
|
|
for (CGU_UINT32 px = 0; px < 16; px++)
|
|
{
|
|
src_imageNorm[px].r = (CGU_FLOAT)((block_32[px] >> 16) & 0xff)/ 255.0f;
|
|
src_imageNorm[px].g = (CGU_FLOAT)((block_32[px] >> 8) & 0xff)/ 255.0f;
|
|
src_imageNorm[px].b = (CGU_FLOAT)((block_32[px] >> 0) & 0xff)/ 255.0f;
|
|
}
|
|
|
|
// Do a quick compression test
|
|
CGU_Vec3f srcRGB[16]; // The list of source colors with blue channel altered
|
|
CGU_Vec3f average_rgb; // The centrepoint of the axis
|
|
CGU_FLOAT errLQ = CMP_FLT_MAX;
|
|
cgu_CompressRGBBlock_MinMax(src_imageNorm, 1.0f, false,srcRGB, average_rgb, errLQ);
|
|
CGU_Vec2ui cmp = cgu_CompressRGBBlock_Fast(src_imageNorm, 1.0f, false,srcRGB, average_rgb, CompErr);
|
|
|
|
compressedBlock[0] = cmp.x;
|
|
compressedBlock[1] = cmp.y;
|
|
}
|
|
return CompErr;
|
|
}
|
|
} else {
|
|
// All colors transparent
|
|
nEndpoints[0][0] = nEndpoints[1][0] = nEndpoints[2][0] = 0;
|
|
nEndpoints[0][1] = nEndpoints[1][1] = nEndpoints[2][1] = 0xff;
|
|
for (CGU_UINT32 ms=0; ms<dwBlockSize; ms++)
|
|
pcIndices[ms] = 0xff;
|
|
return 0.0;
|
|
}
|
|
}
|
|
|
|
|
|
CMP_STATIC CGU_Vec2ui cpu_CompRGBBlock(CMP_IN CGU_Vec4uc bgraBlock[BLOCK_SIZE_4X4],
|
|
CMP_IN CMP_BC15Options BC15Options,
|
|
CMP_INOUT CGU_FLOAT CMP_REFINOUT err)
|
|
{
|
|
CGU_Vec2ui cmpBlock = {0U,0U};
|
|
CGU_FLOAT pfChannelWeights[3] = {1.0f,1.0f,1.0f};
|
|
CGU_UINT8 nEndpoints[2][3][2];
|
|
CGU_UINT8 nIndices[2][BLOCK_SIZE_4X4];
|
|
CGU_UINT32 compressedBlock[2] = {0,0};
|
|
|
|
CGU_FLOAT fError3 = CMP_FLT_MAX;
|
|
|
|
fError3 = cpu_CompRGBBlock32((CGU_UINT32*)bgraBlock,
|
|
compressedBlock,
|
|
BLOCK_SIZE_4X4, RG, GG, BG,
|
|
nEndpoints[0],
|
|
nIndices[0],
|
|
3,
|
|
BC15Options.m_b3DRefinement,
|
|
BC15Options.m_nRefinementSteps,
|
|
pfChannelWeights,
|
|
BC15Options.m_bUseAlpha,
|
|
BC15Options.m_nAlphaThreshold);
|
|
// use case of small min max ranges
|
|
if (compressedBlock[0] > 0)
|
|
{
|
|
//return cmpBlockBlue;
|
|
cmpBlock.x = compressedBlock[0];
|
|
cmpBlock.y = compressedBlock[1];
|
|
err = fError3;
|
|
}
|
|
else
|
|
{
|
|
CGU_FLOAT fError4 = CMP_FLT_MAX;
|
|
fError4 = (fError3 == 0.0) ? CMP_FLT_MAX :cpu_CompRGBBlock32((CGU_UINT32*)bgraBlock,
|
|
compressedBlock,
|
|
BLOCK_SIZE_4X4, RG, GG, BG,
|
|
nEndpoints[1],
|
|
nIndices[1],
|
|
4,
|
|
BC15Options.m_b3DRefinement,
|
|
BC15Options.m_nRefinementSteps,
|
|
pfChannelWeights,
|
|
BC15Options.m_bUseAlpha,
|
|
BC15Options.m_nAlphaThreshold);
|
|
|
|
CGU_UINT32 nMethod;
|
|
if (fError3 <= fError4) {
|
|
err = fError3;
|
|
nMethod = 0;
|
|
}
|
|
else {
|
|
err = fError4;
|
|
nMethod = 1;
|
|
}
|
|
|
|
|
|
CGU_UINT32 c0 = BC1ConstructColour((nEndpoints[nMethod][RC][0] >> (8-RG)), (nEndpoints[nMethod][GC][0] >> (8-GG)), (nEndpoints[nMethod][BC][0] >> (8-BG)));
|
|
CGU_UINT32 c1 = BC1ConstructColour((nEndpoints[nMethod][RC][1] >> (8-RG)), (nEndpoints[nMethod][GC][1] >> (8-GG)), (nEndpoints[nMethod][BC][1] >> (8-BG)));
|
|
if(nMethod == 1 && c0 <= c1 || nMethod == 0 && c0 > c1)
|
|
compressedBlock[0] = c1 | (c0<<16);
|
|
else
|
|
compressedBlock[0] = c0 | (c1<<16);
|
|
|
|
compressedBlock[1] = 0;
|
|
for(CGU_UINT32 i=0; i<16; i++)
|
|
compressedBlock[1] |= (nIndices[nMethod][i] << (2*i));
|
|
|
|
cmpBlock.x = compressedBlock[0];
|
|
cmpBlock.y = compressedBlock[1];
|
|
}
|
|
|
|
return cmpBlock;
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef ENABLE_NEW_CODE
|
|
|
|
//---------------------------------------- Common Utility Code -------------------------------------------------------
|
|
// 1 - Dim error
|
|
CMP_STATIC CGU_FLOAT cgu_RampSrchW( CGU_FLOAT Prj[BLOCK_SIZE_4X4],
|
|
CGU_FLOAT PrjErr[BLOCK_SIZE_4X4],
|
|
CGU_FLOAT PreMRep[BLOCK_SIZE_4X4],
|
|
CGU_FLOAT StepErr,
|
|
CGU_FLOAT lowPosStep,
|
|
CGU_FLOAT highPosStep,
|
|
CGU_UINT32 dwUniqueColors,
|
|
CGU_UINT32 dwNumPoints)
|
|
{
|
|
CGU_FLOAT error = 0;
|
|
CGU_FLOAT step = (highPosStep - lowPosStep) / (dwNumPoints - 1);
|
|
CGU_FLOAT step_h = step * (CGU_FLOAT)0.5;
|
|
CGU_FLOAT rstep = (CGU_FLOAT)1.0f / step;
|
|
|
|
for (CGU_UINT32 i = 0; i < dwUniqueColors; i++)
|
|
{
|
|
CGU_FLOAT v;
|
|
// Work out which value in the block this select
|
|
CGU_FLOAT del;
|
|
|
|
if ((del = Prj[i] - lowPosStep) <= 0)
|
|
v = lowPosStep;
|
|
else if (Prj[i] - highPosStep >= 0)
|
|
v = highPosStep;
|
|
else
|
|
v = cmp_floor((del + step_h) * rstep) * step + lowPosStep;
|
|
|
|
// And accumulate the error
|
|
CGU_FLOAT d = (Prj[i] - v);
|
|
d *= d;
|
|
CGU_FLOAT err = PreMRep[i] * d + PrjErr[i];
|
|
error += err;
|
|
if (StepErr < error)
|
|
{
|
|
error = StepErr;
|
|
break;
|
|
}
|
|
}
|
|
return error;
|
|
}
|
|
|
|
CMP_STATIC CGU_UINT32 cgu_processCluster( CMP_IN CMP_EndPoints EndPoints,
|
|
CMP_IN CGU_Vec4f rgbBlock_normal[BLOCK_SIZE_4X4],
|
|
CMP_IN CGU_UINT32 dwAlphaThreshold,
|
|
CMP_IN CGU_Vec3f channelWeights,
|
|
CMP_IN CGU_UINT8 indices[BLOCK_SIZE_4X4],
|
|
CMP_OUT CGU_FLOAT CMP_REFINOUT Err )
|
|
{
|
|
Err = 0.f;
|
|
CGU_UINT32 pcIndices = 0;
|
|
CGU_UINT32 R, G, B;
|
|
|
|
R = (CGU_UINT32)(EndPoints.Color0.z);
|
|
G = (CGU_UINT32)(EndPoints.Color0.y);
|
|
B = (CGU_UINT32)(EndPoints.Color0.x);
|
|
CGU_INT32 cluster0 = cmp_constructColor(R, G, B);
|
|
|
|
R = (CGU_UINT32)(EndPoints.Color1.z);
|
|
G = (CGU_UINT32)(EndPoints.Color1.y);
|
|
B = (CGU_UINT32)(EndPoints.Color1.x);
|
|
CGU_INT32 cluster1 = cmp_constructColor(R, G, B);
|
|
|
|
CGU_Vec3f InpRmp[NUM_ENDPOINTS];
|
|
if ((cluster0 <= cluster1) // valid for 4 channels
|
|
// || (cluster0 > cluster1) // valid for 3 channels
|
|
)
|
|
{
|
|
// inverse endpoints
|
|
InpRmp[0] = EndPoints.Color1;
|
|
InpRmp[1] = EndPoints.Color0;
|
|
}
|
|
else
|
|
{
|
|
InpRmp[0] = EndPoints.Color0;
|
|
InpRmp[1] = EndPoints.Color1;
|
|
}
|
|
|
|
CGU_Vec3f srcblockLinear[BLOCK_SIZE_4X4];
|
|
CGU_FLOAT srcblockA[BLOCK_SIZE_4X4];
|
|
|
|
// Swizzle the source RGB to BGR for processing
|
|
for (CGU_UINT32 i = 0; i < BLOCK_SIZE_4X4; i++)
|
|
{
|
|
srcblockLinear[i].z = rgbBlock_normal[i].x * 255.0f;
|
|
srcblockLinear[i].y = rgbBlock_normal[i].y * 255.0f;
|
|
srcblockLinear[i].x = rgbBlock_normal[i].z * 255.0f;
|
|
srcblockA[i] = 0.0f;
|
|
//if (dwAlphaThreshold > 0)
|
|
//{
|
|
// CGU_UINT32 alpha = (CGU_UINT32)BlockA[i];
|
|
// if (alpha >= dwAlphaThreshold)
|
|
// srcblockA[i] = BlockA[i];
|
|
//}
|
|
}
|
|
|
|
|
|
// cmp_ClstrBas2()
|
|
// input ramp is on the coarse grid
|
|
// make ramp endpoints the way they'll going to be decompressed
|
|
CGU_Vec3f InpRmpL[NUM_ENDPOINTS];
|
|
CGU_Vec3f Fctrs = {32.0F, 64.0F, 32.0F}; // 1 << RG,1 << GG,1 << BG
|
|
|
|
{
|
|
// ConstantRamp = MkWkRmpPts(InpRmpL, InpRmp);
|
|
InpRmpL[0] = InpRmp[0] + cmp_floorVec3f(InpRmp[0] / Fctrs);
|
|
InpRmpL[0] = cmp_clampVec3f(InpRmpL[0], 0.0f, 255.0f);
|
|
InpRmpL[1] = InpRmp[1] + cmp_floorVec3f(InpRmp[1] / Fctrs);
|
|
InpRmpL[1] = cmp_clampVec3f(InpRmpL[1], 0.0f, 255.0f);
|
|
} // MkWkRmpPts
|
|
|
|
// build ramp
|
|
CGU_Vec3f LerpRmp[4];
|
|
CGU_Vec3f offset = {1.0f, 1.0f, 1.0f};
|
|
{
|
|
//BldRmp(Rmp, InpRmpL, dwNumChannels);
|
|
// linear interpolate end points to get the ramp
|
|
LerpRmp[0] = InpRmpL[0];
|
|
LerpRmp[3] = InpRmpL[1];
|
|
LerpRmp[1] = cmp_floorVec3f((InpRmpL[0] * 2.0f + LerpRmp[3] + offset) / 3.0f);
|
|
LerpRmp[2] = cmp_floorVec3f((InpRmpL[0] + LerpRmp[3] * 2.0f + offset) / 3.0f);
|
|
} // BldRmp
|
|
|
|
//=========================================================================
|
|
// Clusterize, Compute error and find DXTC indexes for the current cluster
|
|
//=========================================================================
|
|
{
|
|
// Clusterize
|
|
CGU_UINT32 alpha;
|
|
|
|
// For each colour in the original block assign it
|
|
// to the closest cluster and compute the cumulative error
|
|
for (CGU_UINT32 i = 0; i < BLOCK_SIZE_4X4; i++)
|
|
{
|
|
alpha = (CGU_UINT32)srcblockA[i];
|
|
if ((dwAlphaThreshold > 0) && alpha == 0)
|
|
{ //*((CGU_UINT32 *)&_Blk[i][AC]) == 0)
|
|
pcIndices |= cmp_set2Bit32(4, i); // dwNumChannels 3 or 4 (default is 4)
|
|
indices[i] = 4;
|
|
}
|
|
else
|
|
{
|
|
CGU_FLOAT shortest = 99999999999.f;
|
|
CGU_UINT8 shortestIndex = 0;
|
|
|
|
CGU_Vec3f channelWeightsBGR;
|
|
channelWeightsBGR.x = channelWeights.z;
|
|
channelWeightsBGR.y = channelWeights.y;
|
|
channelWeightsBGR.z = channelWeights.x;
|
|
|
|
for (CGU_UINT8 rampindex = 0; rampindex < 4; rampindex++)
|
|
{
|
|
// r is either 1 or 4
|
|
// calculate the distance for each component
|
|
CGU_FLOAT distance = cmp_dotVec3f(((srcblockLinear[i] - LerpRmp[rampindex]) * channelWeightsBGR),
|
|
((srcblockLinear[i] - LerpRmp[rampindex]) * channelWeightsBGR));
|
|
if (distance < shortest)
|
|
{
|
|
shortest = distance;
|
|
shortestIndex = rampindex;
|
|
}
|
|
}
|
|
|
|
Err += shortest;
|
|
|
|
// The total is a sum of (error += shortest)
|
|
// We have the index of the best cluster, so assign this in the block
|
|
// Reorder indices to match correct DXTC ordering
|
|
if (shortestIndex == 3) // dwNumChannels - 1
|
|
shortestIndex = 1;
|
|
else if (shortestIndex)
|
|
shortestIndex++;
|
|
pcIndices |= cmp_set2Bit32(shortestIndex, i);
|
|
indices[i] = shortestIndex;
|
|
}
|
|
} // BLOCK_SIZE_4X4
|
|
} // Clusterize
|
|
|
|
return pcIndices;
|
|
}
|
|
#endif
|
|
|
|
// Process a rgbBlock which is normalized (0.0f ... 1.0f), signed normal is not implemented
|
|
CMP_STATIC CGU_Vec2ui CompressBlockBC1_NORMALIZED(CMP_IN CGU_Vec4f src_imageNorm[BLOCK_SIZE_4X4],
|
|
CMP_IN CMP_BC15Options BC15Options)
|
|
{
|
|
bool usingMaxQualityOnly = false;
|
|
|
|
#ifndef ASPM_GPU
|
|
if (BC15Options.m_fquality > 0.75)
|
|
usingMaxQualityOnly = true;
|
|
#endif
|
|
|
|
CGU_FLOAT CompErr = CMP_FLT_MAX;
|
|
CGU_Vec2ui cmpBlock = {0U,0U};
|
|
CGU_Vec2ui cmpBlockTemp = {0U,0U};
|
|
CGU_FLOAT CompErrTemp;
|
|
|
|
// Transfer to RGB Norm from RGBA Norm
|
|
CGU_Vec3f src_imageRGBNorm[16];
|
|
CGU_Vec4uc pixels[16];
|
|
CGU_Vec4uc pixelsBGRA[16];
|
|
|
|
for (CGU_UINT32 sr = 0; sr < 16; sr++) {
|
|
src_imageRGBNorm[sr] = src_imageNorm[sr].rgb;
|
|
pixelsBGRA[sr].b = pixels[sr].r = src_imageNorm[sr].r * 255.0f;
|
|
pixelsBGRA[sr].g = pixels[sr].g = src_imageNorm[sr].g * 255.0f;
|
|
pixelsBGRA[sr].r = pixels[sr].b = src_imageNorm[sr].b * 255.0f;
|
|
pixelsBGRA[sr].a = pixels[sr].a = src_imageNorm[sr].a * 255.0f;
|
|
}
|
|
|
|
|
|
// check for a punch through transparent alpha setting
|
|
if ((BC15Options.m_fquality < 0.75) && (BC15Options.m_bUseAlpha)) {
|
|
CGU_Vec2ui cmpBlockAlpha = {0xffff0000,0xffffffffU};
|
|
for (CGU_UINT32 sr = 0; sr < 16; sr++)
|
|
if (pixels[sr].a < BC15Options.m_nAlphaThreshold) {
|
|
return cmpBlockAlpha;
|
|
}
|
|
}
|
|
|
|
//================
|
|
// extern codec
|
|
//================
|
|
// For debugging
|
|
// CGU_Vec2ui cmpBlockRed = {0xF800F800,0x00000000};
|
|
// CGU_Vec2ui cmpBlockGreen = {0x7E007E00,0x00000000};
|
|
// CGU_Vec2ui cmpBlockBlue = {0x1F001F00,0x00000000};
|
|
|
|
if (!BC15Options.m_bUseAlpha ) {
|
|
//==========================================
|
|
// Gain +0.3 dB for images with soild blocks
|
|
//==========================================
|
|
bool bAllColoursEqual = true;
|
|
|
|
// Load the whole 4x4 block
|
|
for (CGU_UINT32 i = 0u; (i < 16u) && bAllColoursEqual; ++i)
|
|
{
|
|
for (CGU_INT c = 0; c < 3; c++)
|
|
bAllColoursEqual = bAllColoursEqual && (pixels[0][c] == pixels[i][c]);
|
|
}
|
|
|
|
if (bAllColoursEqual) {
|
|
cmpBlock = cgu_solidColorBlock(pixels[0].x,pixels[0].y,pixels[0].z);
|
|
CompErr = cgu_RGBABlockErrorLinear(pixels, cmpBlock);
|
|
if (BC15Options.m_nRefinementSteps < 1) return cmpBlock;
|
|
}
|
|
}
|
|
|
|
if (!usingMaxQualityOnly) {
|
|
//====================================
|
|
// Get src image data, min,max...
|
|
//=====================================
|
|
//CMP_EncodeData edata;
|
|
//cmp_get_encode_data(edata,pixels);
|
|
|
|
if (!BC15Options.m_bUseAlpha) {
|
|
//====================================
|
|
// Fast Compression, low quality
|
|
//=====================================
|
|
CGU_Vec3f srcRGB[16]; // The list of source colors with blue channel altered
|
|
CGU_Vec3f average_rgb; // The centrepoint of the axis
|
|
CGU_FLOAT errLQ = CMP_FLT_MAX;
|
|
cmpBlockTemp = cgu_CompressRGBBlock_MinMax(src_imageRGBNorm, BC15Options.m_fquality, BC15Options.m_bIsSRGB,srcRGB, average_rgb, errLQ);
|
|
if ((BC15Options.m_fquality < CMP_QUALITY0) || (errLQ == 0.0f))
|
|
return cmpBlockTemp;
|
|
|
|
if (CompErr > errLQ) {
|
|
CompErr = errLQ;
|
|
cmpBlock = cmpBlockTemp;
|
|
}
|
|
|
|
cmpBlockTemp = cgu_CompressRGBBlock_Fast(src_imageRGBNorm, BC15Options.m_fquality, BC15Options.m_bIsSRGB,srcRGB, average_rgb, errLQ);
|
|
if (CompErr > errLQ) {
|
|
CompErr = errLQ;
|
|
cmpBlock = cmpBlockTemp;
|
|
}
|
|
if (BC15Options.m_fquality < CMP_QUALITY1)
|
|
return cmpBlock;
|
|
}
|
|
|
|
//========================================
|
|
// use GPU codec lower quality then CPU
|
|
//========================================
|
|
cmpBlockTemp = cgu_CompRGBBlock(src_imageNorm,BC15Options);
|
|
CompErrTemp = cgu_RGBABlockErrorLinear(pixels, cmpBlockTemp);
|
|
if (CompErr > CompErrTemp) {
|
|
CompErr = CompErrTemp;
|
|
cmpBlock = cmpBlockTemp;
|
|
}
|
|
|
|
if (BC15Options.m_fquality < CMP_QUALITY2) return cmpBlock;
|
|
}// if useCGUCodecs
|
|
|
|
//====================================
|
|
// High Quality Codec CPU only
|
|
//=====================================
|
|
#ifndef ASPM_GPU
|
|
cmpBlockTemp = cpu_CompRGBBlock(pixelsBGRA,BC15Options,CompErrTemp);
|
|
|
|
CompErrTemp = cgu_RGBABlockErrorLinear(pixels, cmpBlockTemp);
|
|
|
|
if (CompErr > CompErrTemp) {
|
|
CompErr = CompErrTemp;
|
|
cmpBlock = cmpBlockTemp;
|
|
}
|
|
#endif
|
|
|
|
return cmpBlock;
|
|
}
|