JurassicParkTrespasser/jp2_pc/Source/GUIApp/TerrainTest.cpp

803 lines
24 KiB
C++

/***********************************************************************************************
*
* Copyright © DreamWorks Interactive. 1997
*
* Implementation of TerrainTest.hpp.
*
***********************************************************************************************
*
* $Log:: /JP2_PC/Source/GUIApp/TerrainTest.cpp $
*
* 56 9/23/98 7:41p Mlange
* Wavelet quad tree nodes now have a single pointer to the first descendant and a pointer to
* the next sibling, instead of four pointers for each of the descendants.
*
* 55 9/09/98 11:30a Pkeet
* Needed to add an include to be compatible with a debug switch in 'Texture.hpp.'
*
* 54 8/30/98 4:49p Asouth
* changes in usage rules; loop variable
*
* 53 8/13/98 6:10p Mlange
* Paint message now requires registration of the recipients.
*
* 52 8/13/98 1:47p Mlange
* The step message now requires registration of its recipients.
*
**********************************************************************************************/
#include "StdAfx.h"
#include "gblinc/common.hpp"
#include "Lib/W95/WinInclude.hpp"
#include "Lib/GeomDBase/PartitionPriv.hpp"
#include "Lib/Renderer/GeomTypes.hpp"
#include "TerrainTest.hpp"
#include "Lib/GeomDBase/WaveletQuadTree.hpp"
#include "Lib/GeomDBase/WaveletQuadTreeTForm.hpp"
#include "Lib/GeomDBase/WaveletQuadTreeQuery.hpp"
#include "Lib/GeomDBase/WaveletStaticData.hpp"
#include "Lib/GeomDBase/WaveletDataFormat.hpp"
#include "Lib/GeomDBase/TerrainLoad.hpp"
#include "Lib/GeomDBase/TerrainTexture.hpp"
#include "Lib/Std/Random.hpp"
#include "Lib/Renderer/ScreenRender.hpp"
#include "Lib/Sys/ConIO.hpp"
#include "Lib/EntityDBase/MessageTypes/MsgPaint.hpp"
#include "Lib/EntityDBase/MessageTypes/MsgStep.hpp"
#include "Lib/EntityDBase/Query/QRenderer.hpp"
#include "Lib/Renderer/Camera.hpp"
#include "Lib/View/LineDraw.hpp"
//TODO:
//-Determine if getting rid of in influence check in projection is possible. Also determine if it generally speeds
// up evaluation times.
//-Fix edge of world terrain probs.
//-Allow negative terrain height values?
//-Calc vertex normals by averaging terrain tris.
//-Alloc mem for quad tree funcs should take # of bytes instead of # of nodes. Remove silly type ratio #defines.
//-Key of screen mode changed message and update pixel tolerance accordingly, e.g. tolerance should be size of
// projection on say a 15" monitor.
//-Test Coef projection code. Test w/ various screen modes and make sure it works when screen modes and camera
// properties change.
//-In terrain test code check for potential memory leak bugs by occasionally decimating the entire terrain
// for TIN and query quad tree. All memory should be freed at that point.
//-Determine sensible settings for max num of quad nodes etc.
//OPTIMISATIONS:
//-Consider initialising array of triangle pointers in texture branch function.
//-Consider constructing an array of pointers to vertices within view, instead of iterating trhough each triangle
// and then through each vertex of that triangle.
//-Consider storing quad node types on separate fast heaps for better cache efficiency.
//-Consider making the descendants link to each other instead of the parent linking all four descendants. This will
// save two pointers per node.
//-Do not recurse quad tree to find leaf combine/leaf nodes in evaluate func. Use linked list of tri info types
// instead. This might save about 1/2 ms per frame.
#ifdef __MWERKS__
using namespace NMultiResolution;
#else
using NMultiResolution::CQuadNodeTIN;
using NMultiResolution::CQuadNodeQuery;
using NMultiResolution::CQuadRootTForm;
using NMultiResolution::CTransform;
using NMultiResolution::CTransformedData;
using NMultiResolution::CQuadVertexQuery;
using NMultiResolution::CQueryRect;
#endif
//**********************************************************************************************
//
// CQueryTest implementation.
//
//*****************************************************************************************
CQueryTest::~CQueryTest()
{
delete pqrQuery;
}
//*****************************************************************************************
void CQueryTest::Update(CQuadRootQuery* pqnq_root)
{
const uint uLIFETIME_RANGE = 40; // Max number of steps a query lives or is dead.
const TReal rCREATE_SIZE_RANGE = 5; // Max radius of a new query.
const TReal rMOVE_PROB = .8; // Probability of an active query actually moving this step.
const TReal rMOVE_DIST_RANGE = 5; // Max distance an active query moves (in xy) per step.
const TReal rWORLD_EXTENT_INCREASE = 10;// Increment for the extents of world, so that queries are occasionally
// performed off the edge of the world.
static CRandom rnd;
// If the current life counter is zero, this query either dies or is reborn.
if (iLifeStepsRemaining == 0)
{
iLifeStepsRemaining = rnd(0u, uLIFETIME_RANGE);
// If no query class was allocated, this query will be reborn.
if (pqrQuery == 0)
{
// Construct a query new volume, its initial presence and movement direction.
bvsVolume = CBoundVolSphere((TReal)Max(rnd(0, rCREATE_SIZE_RANGE), .001));
CVector2<> v2_start = pqnq_root->mpConversions.rcWorldSpaceExtents.v2Start() - CVector2<>(rWORLD_EXTENT_INCREASE, rWORLD_EXTENT_INCREASE);
CVector2<> v2_end = pqnq_root->mpConversions.rcWorldSpaceExtents.v2End() + CVector2<>(rWORLD_EXTENT_INCREASE, rWORLD_EXTENT_INCREASE);
pr3Pos = CPresence3<>(CVector3<>(rnd(v2_start.tX, v2_end.tX), rnd(v2_start.tY, v2_end.tY), 0));
d3MoveDir = CDir3<>(rnd(0.0, 1.0), rnd(0.0, 1.0), rnd(0.0, 1.0));
pqrQuery = new CQueryRect(pqnq_root, bvsVolume, pr3Pos);
}
else
{
// A query was active, kill it now.
delete pqrQuery;
pqrQuery = 0;
iLifeStepsRemaining = -iLifeStepsRemaining;
}
}
// If the query is currently dead, update the counter until it is time to resurrect it.
else if (iLifeStepsRemaining < 0)
{
Assert(pqrQuery == 0);
++iLifeStepsRemaining;
}
else
{
Assert(pqrQuery != 0);
if (rnd(0.0, 1.0) <= rMOVE_PROB)
{
// The query is living, move it by a random amount.
TReal r_dist = rnd(0, rMOVE_DIST_RANGE);
pr3Pos.v3Pos += d3MoveDir * r_dist;
pqrQuery->SetVolume(bvsVolume, pr3Pos);
}
// Decrement the counter until it is time to kill this query.
--iLifeStepsRemaining;
}
}
//**********************************************************************************************
//
// CTerrainTest implementation.
//
//*****************************************************************************************
CTerrainTest::CTerrainTest()
: iNumTestSteps(0), iNumCoefCheckedTIN(0), iNumCoefCheckedQuery(0),
ptdhData(0), prasQuadTreeVertices(0), prasTransformVertices(0), prasOrigVertices(0), prasOrigWavelets(0)
{
SetInstanceName("Terrain Test");
NMultiResolution::CTextureNode::SetTextureMemSize();
NMultiResolution::AllocMemory();
// Register this entity with the message types it needs to receive.
CMessageStep::RegisterRecipient(this);
CMessagePaint::RegisterRecipient(this);
}
//*****************************************************************************************
CTerrainTest::~CTerrainTest()
{
CMessagePaint::UnregisterRecipient(this);
CMessageStep::UnregisterRecipient(this);
DeleteAll();
NMultiResolution::FreeMemory();
NMultiResolution::CTextureNode::SetTextureMemSize(0);
}
//*****************************************************************************************
void CTerrainTest::Process(const CMessageStep&)
{
if (iNumTestSteps % 30 == 0)
TestInitTransform();
static CRandom rnd;
if ((iNumTestSteps & 1) == 0)
{
//
// Evaluate the wavelet quad tree with a random tolerance and observer location.
//
NMultiResolution::psWaveletTIN.Reset();
// Set the dummy tolerances.
pqntinRoot->rvarPixelTolerance.SetRange(0, 1700);
pqntinRoot->rvarPixelToleranceFar.SetRange(0, 1700);
const double d_min_tolerance = 100;
const double d_max_tolerance = 200;
TReal r_tolerance = rnd(d_min_tolerance, d_max_tolerance);
pqntinRoot->rvarPixelTolerance = r_tolerance;
pqntinRoot->rvarPixelToleranceFar = r_tolerance;
CVector3<> v3_pos
(
rnd( double(pqntinRoot->mpConversions.rcWorldSpaceExtents.tX0()), pqntinRoot->mpConversions.rcWorldSpaceExtents.tX1() ),
rnd( double(pqntinRoot->mpConversions.rcWorldSpaceExtents.tY0()), pqntinRoot->mpConversions.rcWorldSpaceExtents.tY1() ),
rnd(0.0, 255.0)
);
CWDbQueryActiveCamera().tGet()->SetPos(v3_pos);
pqntinRoot->Update(CWDbQueryActiveCamera().tGet());
pqntinRoot->CheckTriangulation();
//
// Ensure the vertices of the quad tree are valid.
//
prasQuadTreeVertices->Clear(cfNAN);
prasTransformVertices->Clear(cfZERO);
prasQuadTreeVertices->tPix(0, 0) = pqntinRoot->pqvtGetVertex(0)->cfScaling();
RecurseTree(pqntinRoot);
// Synthesise the current quad tree transform.
CTransform tf_synthesised
(
prasTransformVertices,
ptdhData->cfRoot
);
tf_synthesised.Synthesise();
// Compare the quadtree vertices with the synthesised wavelet transform data.
iNumCoefCheckedTIN = 0;
for (int i_y = 0; i_y < prasQuadTreeVertices->iHeight; i_y++)
{
for (int i_x = 0; i_x < prasQuadTreeVertices->iWidth; i_x++)
{
CCoef cf_quadtree_tin = prasQuadTreeVertices->tPix(i_x, i_y);
if (cf_quadtree_tin != cfNAN)
{
CCoef cf_synthesised = prasTransformVertices->tPix(i_x, i_y);
CCoef cf_abs_diff = (cf_quadtree_tin - cf_synthesised).cfAbs();
AlwaysAssert(cf_abs_diff == cfZERO);
iNumCoefCheckedTIN++;
}
}
}
}
else
{
//
// Update the height queries.
//
NMultiResolution::psWaveletQuery.Reset();
if (iNumTestSteps % 3 == 0)
pqnqRoot->Update();
int i;
for (i = 0; i < saqtQueries.uLen; i++)
saqtQueries[i].Update(pqnqRoot);
//
// Ensure the height queries are valid.
//
iNumCoefCheckedQuery = 0;
for (i = 0; i < saqtQueries.uLen; i++)
{
CQueryRect* pwqttop_curr = saqtQueries[i].pqrQuery;
if (pwqttop_curr != 0)
{
CQueryRect::CIterator it = pwqttop_curr->itBegin();
for (; it; ++it)
{
for (int i_corner = 0; i_corner < 3; i_corner++)
{
const NMultiResolution::CQuadVertexRecalc* pqvt = (*it)->pqvtGetVertex(i_corner);
if (pqvt->iX() != prasOrigVertices->iWidth && pqvt->iY() != prasOrigVertices->iHeight)
{
CCoef cf_quadtree_query = pqvt->cfScaling();
CCoef cf_original = prasOrigVertices->tPix(pqvt->iX(), pqvt->iY());
CCoef cf_abs_diff = (cf_quadtree_query - cf_original).cfAbs();
AlwaysAssert(cf_abs_diff == cfZERO);
iNumCoefCheckedQuery++;
}
}
}
}
}
}
iNumTestSteps++;
}
//*****************************************************************************************
void CTerrainTest::Process(const CMessagePaint& msgpaint)
{
if (pqntinRoot == 0)
return;
// Attempt to cast to a raster win.
CRasterWin* pras_win = dynamic_cast<CRasterWin*>(msgpaint.renContext.pScreenRender->prasScreen);
if (pras_win == 0)
return;
// Get camera origin, in quad space.
const CCamera& cam = *CWDbQueryActiveCamera().tGet();
CVector2<> v2_quad_pos = CVector2<>(cam.v3Pos()) * pqntinRoot->mpConversions.tlr2WorldToQuad;
// Visualise state.
if ((iNumTestSteps & 1) == 0)
pqntinRoot->rcDrawWireframe(pras_win, CColour(1.0, 0.0, 0.0), true, false, v2_quad_pos);
else
pqnqRoot->rcDrawWireframe(pras_win, CColour(1.0, 1.0, 0.0), true, false, v2_quad_pos);
// Update terrain console.
conTerrain.ClearScreen();
conTerrain.Print("Steps: %d, TIN coef checked: %d, Query coef checked: %d\n", iNumTestSteps, iNumCoefCheckedTIN, iNumCoefCheckedQuery);
/*
double d_num_nodes = CQuadNodeTIN::uNumAlloc();
double d_node_to_vert_ratio = NMultiResolution::CQuadVertexTIN::uNumAlloc() / d_num_nodes;
double d_node_to_tri_ratio = NMultiResolution::CTriangleTIN::uNumAlloc() / d_num_nodes;
double d_node_to_tri_info_ratio = NMultiResolution::CTriNodeInfoTIN::uNumAlloc() / d_num_nodes;
double d_num_verts = NMultiResolution::CQuadVertexTIN::uNumAlloc() + NMultiResolution::CQuadVertexQuery::uNumAlloc();
double d_vert_to_scaling_array_ratio = NMultiResolution::CQuadVertexRecalc::aaScalingAlloc.uNumAllocBytes() / d_num_verts;
conTerrain.Print("%f, %f, %f, %f\n", d_node_to_vert_ratio, d_node_to_tri_ratio, d_node_to_tri_info_ratio, d_vert_to_scaling_array_ratio);
*/
NMultiResolution::PrintProfileStats(conTerrain, pqntinRoot, pqnqRoot);
}
//******************************************************************************************
void CTerrainTest::TestInitTransform()
{
DeleteAll();
conTerrain.ClearScreen();
static CRandom rnd;
// Set up a random terrain info structure.
SExportDataInfo edi_rnd;
TReal r_res = .25 * (1 << rnd(0u, 4u));
edi_rnd.iNumDataPoints = rnd(1000u, 3000u);
edi_rnd.v2Min = CVector2<>(rnd(10u, 100u) * -r_res, rnd(10u, 100u) * -r_res);
edi_rnd.rHighestRes = r_res;
edi_rnd.rMinHeight = rnd(0.0, 100.0);
edi_rnd.rMaxHeight = edi_rnd.rMinHeight + rnd(0.0, 1000.0);
edi_rnd.iMaxIndices = NextPowerOfTwo(rnd(16u, 256u));
edi_rnd.iNumQuantisationBits = 20;
// Allocate and set the fastheap used for allocations of the transform quad tree classes.
CQuadRootTForm::AllocMemory();
{
CQuadRootTForm qntr_test(edi_rnd, conTerrain);
qntr_test.RandomisePoints(edi_rnd, rnd);
// CTerrainExportedData ted("c:/jp2_pc/NewTerrainTest.trr", conTerrain);
// CTerrainExportedData ted("c:/jp2_pc/PineValleyNF.trr", conTerrain);
// CQuadRootTForm qntr_test(ted.ediGetInfo(), conTerrain);
// qntr_test.AddPoints(ted);
prasQuadTreeVertices = new CRasterT<CCoef>(qntr_test.iGetSize(), qntr_test.iGetSize());
prasTransformVertices = new CRasterT<CCoef>(qntr_test.iGetSize(), qntr_test.iGetSize());
prasOrigWavelets = qntr_test.prasGetData();
CTransform tf_analyse(prasOrigWavelets);
tf_analyse.Analyse();
prasOrigVertices = new CRasterT<CCoef>(qntr_test.iGetSize(), qntr_test.iGetSize());
memcpy(*prasOrigVertices, *prasOrigWavelets, prasOrigVertices->iSize() * sizeof(CCoef));
CTransform tf_synthesise(prasOrigVertices, prasOrigWavelets->tPix(0, 0));
tf_synthesise.Synthesise();
qntr_test.Analyse();
qntr_test.CheckData(prasOrigWavelets);
ptdhData = new CTransformedDataHeader(qntr_test);
qntr_test.CheckData(*ptdhData);
}
CQuadRootTForm::FreeMemory();
pqntinRoot = ::new CQuadRootTIN( ptdhData);
pqnqRoot = ::new CQuadRootQuery(ptdhData);
for (int i = 0; i < iMAX_QUERY_TESTS; i++)
saqtQueries << CQueryTest();
}
//******************************************************************************************
void CTerrainTest::DeleteAll()
{
for (int i = 0; i < saqtQueries.uLen; i++)
delete saqtQueries[i].pqrQuery;
saqtQueries.uLen = 0;
delete ptdhData;
delete prasQuadTreeVertices;
delete prasTransformVertices;
delete prasOrigVertices;
delete prasOrigWavelets;
::delete pqntinRoot;
::delete pqnqRoot;
}
//******************************************************************************************
void CTerrainTest::RecurseTree(const CQuadNodeTIN* pqntin)
{
if (pqntin->bHasDescendants())
{
for (int i = 0; i < 3; i++)
{
const NMultiResolution::CQuadVertexTIN* pqvt = pqntin->ptqnGetDescendant(0)->pqvtGetVertex(i + 1);
Assert(prasOrigWavelets->tPix(pqvt->iX(), pqvt->iY()) == pqvt->cfWavelet());
prasQuadTreeVertices->tPix(pqvt->iX(), pqvt->iY()) = pqvt->cfScaling();
prasTransformVertices->tPix(pqvt->iX(), pqvt->iY()) = pqvt->cfWavelet();
}
RecurseTree(pqntin->ptqnGetDescendant(0));
RecurseTree(pqntin->ptqnGetDescendant(1));
RecurseTree(pqntin->ptqnGetDescendant(2));
RecurseTree(pqntin->ptqnGetDescendant(3));
}
}
namespace NMultiResolution
{
//**********************************************************************************************
//
// NMultiResolution::CTransform implementation.
//
//******************************************************************************************
CTransform::CTransform(CRasterT<CCoef>* pras_analyse)
: prasTransformed(pras_analyse), uSize(pras_analyse->iWidth), iLevel(0)
{
Assert(pras_analyse != 0);
InterpolateMissingData();
}
//******************************************************************************************
CTransform::CTransform(CRasterT<CCoef>* pras_synthesise, CCoef cf_root)
: prasTransformed(pras_synthesise), uSize(pras_synthesise->iWidth), iLevel(-int(uLog2(pras_synthesise->iWidth)))
{
Assert(prasTransformed->iWidth == prasTransformed->iHeight);
Assert(bPowerOfTwo(uSize));
Assert(iDimScaling() == 1);
prasTransformed->tPix(0, 0) = cf_root;
}
//******************************************************************************************
void CTransform::InterpolateMissingData()
{
iLevel = -int(uLog2(uSize));
do
{
iLevel++;
int i_stride = iScalingStride();
bool b_x_odd_node = false;
bool b_y_odd_node = false;
for (int i_y = 0; i_y < uSize; i_y += i_stride * 2)
{
for (int i_x = 0; i_x < uSize; i_x += i_stride * 2)
{
CCoef cf_predict;
if (prasTransformed->tPix(i_x + i_stride, i_y) == cfNAN)
prasTransformed->tPix(i_x + i_stride, i_y) = cfPredict(i_x + i_stride, i_y);
if (prasTransformed->tPix(i_x, i_y + i_stride) == cfNAN)
prasTransformed->tPix(i_x, i_y + i_stride) = cfPredict(i_x, i_y + i_stride);
if (prasTransformed->tPix(i_x + i_stride, i_y + i_stride) == cfNAN)
{
if (b_x_odd_node == b_y_odd_node)
{
cf_predict = cfReadReflected(i_x + i_stride, i_y + i_stride, i_stride, i_stride);
cf_predict += cfReadReflected(i_x + i_stride, i_y + i_stride, -i_stride, -i_stride);
}
else
{
cf_predict = cfReadReflected(i_x + i_stride, i_y + i_stride, i_stride, -i_stride);
cf_predict += cfReadReflected(i_x + i_stride, i_y + i_stride, -i_stride, i_stride);
}
prasTransformed->tPix(i_x + i_stride, i_y + i_stride) = cf_predict.cfPredict();
}
b_x_odd_node = !b_x_odd_node;
}
b_y_odd_node = !b_y_odd_node;
}
}
while (iLevel != 0);
}
//******************************************************************************************
void CTransform::Analyse()
{
Assert(iDimScaling() == uSize);
do
{
int i_stride = iScalingStride();
int i_x, i_y;
bool b_x_odd_node = false;
bool b_y_odd_node = false;
for (i_y = 0; i_y < uSize; i_y += i_stride * 2)
{
for (i_x = 0; i_x < uSize; i_x += i_stride * 2)
{
CCoef cf_predict;
// Horizontal.
cf_predict = cfPredict(i_x + i_stride, i_y);
prasTransformed->tPix(i_x + i_stride, i_y) -= cf_predict;
// Vertical.
cf_predict = cfPredict(i_x, i_y + i_stride);
prasTransformed->tPix(i_x, i_y + i_stride) -= cf_predict;
// Diagonal.
if (b_x_odd_node == b_y_odd_node)
{
cf_predict = cfReadReflected(i_x + i_stride, i_y + i_stride, i_stride, i_stride);
cf_predict += cfReadReflected(i_x + i_stride, i_y + i_stride, -i_stride, -i_stride);
}
else
{
cf_predict = cfReadReflected(i_x + i_stride, i_y + i_stride, i_stride, -i_stride);
cf_predict += cfReadReflected(i_x + i_stride, i_y + i_stride, -i_stride, i_stride);
}
cf_predict = cf_predict.cfPredict();
prasTransformed->tPix(i_x + i_stride, i_y + i_stride) -= cf_predict;
b_x_odd_node = !b_x_odd_node;
}
b_y_odd_node = !b_y_odd_node;
}
b_x_odd_node = false;
b_y_odd_node = false;
for (i_y = 0; i_y < uSize; i_y += i_stride * 2)
{
for (i_x = 0; i_x < uSize; i_x += i_stride * 2)
{
if (b_x_odd_node == b_y_odd_node)
prasTransformed->tPix(i_x, i_y) += cfLift8(i_x, i_y);
else
prasTransformed->tPix(i_x, i_y) += cfLift4(i_x, i_y);
b_x_odd_node = !b_x_odd_node;
}
b_y_odd_node = !b_y_odd_node;
}
--iLevel;
}
while (iDimScaling() != 1);
}
//******************************************************************************************
void CTransform::Synthesise()
{
Assert(iDimScaling() == 1);
do
{
++iLevel;
int i_stride = iScalingStride();
int i_x, i_y;
bool b_x_odd_node = false;
bool b_y_odd_node = false;
for (i_y = 0; i_y < uSize; i_y += i_stride * 2)
{
for (i_x = 0; i_x < uSize; i_x += i_stride * 2)
{
if (b_x_odd_node == b_y_odd_node)
prasTransformed->tPix(i_x, i_y) -= cfLift8(i_x, i_y);
else
prasTransformed->tPix(i_x, i_y) -= cfLift4(i_x, i_y);
b_x_odd_node = !b_x_odd_node;
}
b_y_odd_node = !b_y_odd_node;
}
b_x_odd_node = false;
b_y_odd_node = false;
for (i_y = 0; i_y < uSize; i_y += i_stride * 2)
{
for (i_x = 0; i_x < uSize; i_x += i_stride * 2)
{
CCoef cf_predict;
// Horizontal.
cf_predict = cfPredict(i_x + i_stride, i_y);
prasTransformed->tPix(i_x + i_stride, i_y) += cf_predict;
// Vertical.
cf_predict = cfPredict(i_x, i_y + i_stride);
prasTransformed->tPix(i_x, i_y + i_stride) += cf_predict;
// Diagonal.
if (b_x_odd_node == b_y_odd_node)
{
cf_predict = cfReadReflected(i_x + i_stride, i_y + i_stride, i_stride, i_stride);
cf_predict += cfReadReflected(i_x + i_stride, i_y + i_stride, -i_stride, -i_stride);
}
else
{
cf_predict = cfReadReflected(i_x + i_stride, i_y + i_stride, i_stride, -i_stride);
cf_predict += cfReadReflected(i_x + i_stride, i_y + i_stride, -i_stride, i_stride);
}
cf_predict = cf_predict.cfPredict();
prasTransformed->tPix(i_x + i_stride, i_y + i_stride) += cf_predict;
b_x_odd_node = !b_x_odd_node;
}
b_y_odd_node = !b_y_odd_node;
}
}
while (iDimScaling() != uSize);
}
//******************************************************************************************
CCoef CTransform::cfReadReflected(int i_x, int i_y, int i_x_off, int i_y_off) const
{
uint u_r_x = uint(i_x + i_x_off);
uint u_r_y = uint(i_y + i_y_off);
if ((u_r_x & (uSize - 1)) != u_r_x)
i_x_off = -i_x_off;
if ((u_r_y & (uSize - 1)) != u_r_y)
i_y_off = -i_y_off;
CCoef cf_val = prasTransformed->tPix(i_x + i_x_off, i_y + i_y_off);
Assert(cf_val != cfNAN);
return cf_val;
}
//******************************************************************************************
CCoef CTransform::cfPredict(int i_x, int i_y) const
{
int i_x_mod = i_x & iScalingStride();
int i_y_mod = i_y & iScalingStride();
Assert(i_x_mod != 0 || i_y_mod != 0);
return (cfReadReflected(i_x, i_y, i_x_mod, i_y_mod) +
cfReadReflected(i_x, i_y, -i_x_mod, -i_y_mod) ).cfPredict();
}
//******************************************************************************************
CCoef CTransform::cfLift4(int i_x, int i_y) const
{
int i_pos_off = iScalingStride();
CCoef cf_lift = cfZERO;
cf_lift += cfReadReflected(i_x, i_y, 0, i_pos_off);
cf_lift += cfReadReflected(i_x, i_y, 0, -i_pos_off);
cf_lift += cfReadReflected(i_x, i_y, i_pos_off, 0);
cf_lift += cfReadReflected(i_x, i_y, -i_pos_off, 0);
return cf_lift.cfLift();
}
//******************************************************************************************
CCoef CTransform::cfLift8(int i_x, int i_y) const
{
int i_pos_off = iScalingStride();
CCoef cf_lift = cfZERO;
cf_lift += cfReadReflected(i_x, i_y, 0, i_pos_off);
cf_lift += cfReadReflected(i_x, i_y, 0, -i_pos_off);
cf_lift += cfReadReflected(i_x, i_y, i_pos_off, 0);
cf_lift += cfReadReflected(i_x, i_y, -i_pos_off, 0);
cf_lift += cfReadReflected(i_x, i_y, i_pos_off, i_pos_off);
cf_lift += cfReadReflected(i_x, i_y, -i_pos_off, -i_pos_off);
cf_lift += cfReadReflected(i_x, i_y, i_pos_off, -i_pos_off);
cf_lift += cfReadReflected(i_x, i_y, -i_pos_off, i_pos_off);
return cf_lift.cfLift();
}
};