Files
UnrealEngine/Engine/Source/ThirdParty/openexr/openexr-3.3.2/Contrib/DtexToExr/PxFourChanDeepRgba.h
2025-05-18 13:04:45 +08:00

444 lines
15 KiB
C++

//
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) Contributors to the OpenEXR Project.
//
#ifndef _PxFourChanDeepRgba_h_
#define _PxFourChanDeepRgba_h_
#include "PxBaseDeepHelper.h"
#include "PxDeepUtils.h"
namespace PxDeep
{
//-*****************************************************************************
// FOUR CHANNEL DEEP RGBA CONTINUOUS
//-*****************************************************************************
template <typename RGBA_T>
class FourChanDeepRgbaContinuous : public BaseDeepHelper<
RGBA_T,
FourChanDeepRgbaContinuous<RGBA_T>,
SpanRgba>
{
public:
typedef BaseDeepHelper<RGBA_T, FourChanDeepRgbaContinuous<RGBA_T>, SpanRgba>
super_type;
typedef FourChanDeepRgbaContinuous<RGBA_T> this_type;
typedef typename super_type::span_type span_type;
FourChanDeepRgbaContinuous (
DtexFile* i_dtexFile, int i_numDtexChans, const Parameters& i_params)
: BaseDeepHelper<RGBA_T, FourChanDeepRgbaContinuous<RGBA_T>, SpanRgba> (
i_dtexFile, i_numDtexChans, i_params)
{}
void processDeepPixel (int i_numPts);
};
//-*****************************************************************************
// FOUR CHANNEL DEEP RGBA DISCRETE
//-*****************************************************************************
template <typename RGBA_T>
class FourChanDeepRgbaDiscrete
: public BaseDeepHelper<RGBA_T, FourChanDeepRgbaDiscrete<RGBA_T>, SpanRgba>
{
public:
typedef BaseDeepHelper<RGBA_T, FourChanDeepRgbaDiscrete<RGBA_T>, SpanRgba>
super_type;
typedef FourChanDeepRgbaDiscrete<RGBA_T> this_type;
typedef typename super_type::span_type span_type;
FourChanDeepRgbaDiscrete (
DtexFile* i_dtexFile, int i_numDtexChans, const Parameters& i_params)
: BaseDeepHelper<RGBA_T, FourChanDeepRgbaDiscrete<RGBA_T>, SpanRgba> (
i_dtexFile, i_numDtexChans, i_params)
{}
void processDeepPixel (int i_numPts);
};
//-*****************************************************************************
template <typename RGBA_T>
void
FourChanDeepRgbaContinuous<RGBA_T>::processDeepPixel (int i_numPts)
{
assert (i_numPts > 0);
// Loop over all the dtex points and get their deepAlphas
// and their depths. Enforce the case that deepAlpha
// is always between 0 and 1.
(this->m_spans).resize ((size_t) i_numPts);
for (int j = 0; j < i_numPts; ++j)
{
float z;
float pts[4];
DtexPixelGetPoint ((this->m_pixel), j, &z, (float*) pts);
z = ClampDepth (z);
double red = ZeroNAN (pts[0]);
double green = ZeroNAN (pts[1]);
double blue = ZeroNAN (pts[2]);
double alpha = ClampAlpha (pts[3]);
span_type& spanJ = (this->m_spans)[j];
spanJ.clear ();
spanJ.in = z;
spanJ.out = z;
spanJ.viz = ClampViz (1.0 - alpha);
spanJ.index = j;
// Only unpremult if the data is assumed to be premultiplied,
// which is when the params say DON'T multiply color by alpha.
if (alpha > 0.0 && !(this->m_params).multiplyColorByAlpha)
{
spanJ.rgb[0] = red / alpha;
spanJ.rgb[1] = green / alpha;
spanJ.rgb[2] = blue / alpha;
}
else
{
spanJ.rgb[0] = red;
spanJ.rgb[1] = green;
spanJ.rgb[2] = blue;
}
}
// Sort the spans.
std::sort ((this->m_spans).begin (), (this->m_spans).end ());
// Combine identical depths.
double maxDensity = PXDU_MIN_NON_ZERO_DENSITY;
{
int activeBegin = 0;
int activeEnd = 0;
float interestingDepth = 0.0f;
int numRemoved = 0;
while (activeBegin < i_numPts)
{
span_type& spanActiveBegin = (this->m_spans)[activeBegin];
float nextInterestingDepth = spanActiveBegin.in;
assert (nextInterestingDepth > interestingDepth);
activeEnd = i_numPts;
for (int a = activeBegin + 1; a < i_numPts; ++a)
{
span_type& spanNext = (this->m_spans)[a];
assert (spanNext.in > interestingDepth);
assert (spanNext.in >= nextInterestingDepth);
if (spanNext.in > nextInterestingDepth)
{
// This span is not active in this round,
// set activeEnd and get out.
activeEnd = a;
break;
}
else
{
// This span has an identical depth to
// the previous one, so we must combine their
// alphas and eliminate the depth.
// We simply add their unpremultiplied color.
spanActiveBegin.viz *= spanNext.viz;
spanActiveBegin.rgb[0] += spanNext.rgb[0];
spanActiveBegin.rgb[1] += spanNext.rgb[1];
spanActiveBegin.rgb[2] += spanNext.rgb[2];
spanNext.in = FLT_MAX;
spanNext.out = FLT_MAX;
++numRemoved;
}
}
spanActiveBegin.viz = ClampViz (spanActiveBegin.viz);
// Accumulate density from here to the next point.
if (activeEnd < i_numPts)
{
span_type& spanNext = (this->m_spans)[activeEnd];
double dz = spanNext.in - spanActiveBegin.in;
assert (spanNext.in > spanActiveBegin.in);
assert (dz > 0.0);
double density = DensityFromVizDz (spanActiveBegin.viz, dz);
maxDensity = std::max (maxDensity, density);
}
activeBegin = activeEnd;
interestingDepth = nextInterestingDepth;
}
// If any removed, re-sort the list and remove the end
// points.
if (numRemoved > 0)
{
assert (numRemoved < i_numPts);
std::sort ((this->m_spans).begin (), (this->m_spans).end ());
i_numPts -= numRemoved;
(this->m_spans).resize (i_numPts);
}
}
// Handle the single point case.
if (i_numPts == 1)
{
span_type& span0 = (this->m_spans)[0];
if ((this->m_params).discardZeroAlphaSamples && span0.viz >= 1.0)
{
// Nothing!
return;
}
span0.out = ClampDepth (IncrementPositiveFloat (span0.in));
double alpha = ClampAlpha (1.0 - span0.viz);
// If the alpha is zero, and we're still here, it means
// that the spans are either completely transparent and
// the user has elected to keep them anyway, in which case
// no multiplication by alpha is needed, or alternatively,
// they're "glow" spans which have zero alpha but non-zero
// color. Those glow spans were not unpremultiplied above,
// and therefore do not need to be premultiplied here.
// SO! If alpha is zero, we don't premultiply.
if (alpha > 0.0)
{
span0.rgb[0] *= alpha;
span0.rgb[1] *= alpha;
span0.rgb[2] *= alpha;
}
(this->m_deepOutPixel)
.push_back (
span0.in,
span0.out,
span0.rgb[0],
span0.rgb[1],
span0.rgb[2],
alpha);
return;
}
// Put the spans back out.
// If the last point has a non-zero alpha, extrapolate the
// maximum density to create an end point.
for (int j = 0; j < i_numPts; ++j)
{
span_type& spanJ = (this->m_spans)[j];
if ((this->m_params).discardZeroAlphaSamples && spanJ.viz >= 1.0)
{
// This span is transparent, ignore it.
continue;
}
if (j < i_numPts - 1) { spanJ.out = (this->m_spans)[j + 1].in; }
else
{
// This is the last point.
// If it has non-zero alpha, it needs depth,
// which we use the max density for.
if (spanJ.viz >= 1.0)
{
// Don't need to worry about this last span!
// It is at the end of the continuous span, and
// is completely transparent.
continue;
}
double dz = DzFromVizDensity (spanJ.viz, maxDensity);
spanJ.out = ClampDepth (spanJ.in + dz);
if (spanJ.out <= spanJ.in)
{
spanJ.out = ClampDepth (IncrementPositiveFloat (spanJ.in));
}
}
double alpha = ClampAlpha (1.0 - spanJ.viz);
// If the alpha is zero, and we're still here, it means
// that the spans are either completely transparent and
// the user has elected to keep them anyway, in which case
// no multiplication by alpha is needed, or alternatively,
// they're "glow" spans which have zero alpha but non-zero
// color. Those glow spans were not unpremultiplied above,
// and therefore do not need to be premultiplied here.
// SO! If alpha is zero, we don't premultiply.
if (alpha > 0.0)
{
spanJ.rgb[0] *= alpha;
spanJ.rgb[1] *= alpha;
spanJ.rgb[2] *= alpha;
}
// Set the channels!
(this->m_deepOutPixel)
.push_back (
spanJ.in,
spanJ.out,
spanJ.rgb[0],
spanJ.rgb[1],
spanJ.rgb[2],
alpha);
}
}
//-*****************************************************************************
template <typename RGBA_T>
void
FourChanDeepRgbaDiscrete<RGBA_T>::processDeepPixel (int i_numPts)
{
assert (i_numPts > 0);
// Loop over all the dtex points and get their deepAlphas
// and their depths. Enforce the case that deepAlpha
// is always between 0 and 1.
(this->m_spans).resize ((size_t) i_numPts);
for (int j = 0; j < i_numPts; ++j)
{
float z;
float pts[4];
DtexPixelGetPoint ((this->m_pixel), j, &z, (float*) pts);
z = ClampDepth (z);
double red = ZeroNAN (pts[0]);
double green = ZeroNAN (pts[1]);
double blue = ZeroNAN (pts[2]);
double alpha = ClampAlpha (pts[3]);
span_type& spanJ = (this->m_spans)[j];
spanJ.clear ();
spanJ.in = z;
spanJ.out = z;
spanJ.viz = ClampViz (1.0 - alpha);
spanJ.index = j;
// Only unpremult if the data is assumed to be premultiplied,
// which is when the params say DON'T multiply color by alpha.
if (alpha > 0.0 && !(this->m_params).multiplyColorByAlpha)
{
spanJ.rgb[0] = red / alpha;
spanJ.rgb[1] = green / alpha;
spanJ.rgb[2] = blue / alpha;
}
else
{
spanJ.rgb[0] = red;
spanJ.rgb[1] = green;
spanJ.rgb[2] = blue;
}
}
// Sort the spans.
std::sort ((this->m_spans).begin (), (this->m_spans).end ());
// Combine identical depths.
{
int activeBegin = 0;
int activeEnd = 0;
float interestingDepth = 0.0f;
int numRemoved = 0;
while (activeBegin < i_numPts)
{
span_type& spanActiveBegin = (this->m_spans)[activeBegin];
float nextInterestingDepth = spanActiveBegin.in;
assert (nextInterestingDepth > interestingDepth);
activeEnd = i_numPts;
for (int a = activeBegin + 1; a < i_numPts; ++a)
{
span_type& spanNext = (this->m_spans)[a];
assert (spanNext.in > interestingDepth);
assert (spanNext.in >= nextInterestingDepth);
if (spanNext.in > nextInterestingDepth)
{
// This span is not active in this round,
// set activeEnd and get out.
activeEnd = a;
break;
}
else
{
// This span has an identical depth to
// the previous one, so we must combine their
// alphas and eliminate the depth.
// We simply add their unpremultiplied color.
spanActiveBegin.viz *= spanNext.viz;
spanActiveBegin.rgb[0] += spanNext.rgb[0];
spanActiveBegin.rgb[1] += spanNext.rgb[1];
spanActiveBegin.rgb[2] += spanNext.rgb[2];
spanNext.in = FLT_MAX;
spanNext.out = FLT_MAX;
++numRemoved;
}
}
spanActiveBegin.viz = ClampViz (spanActiveBegin.viz);
activeBegin = activeEnd;
interestingDepth = nextInterestingDepth;
}
// If any removed, re-sort the list and remove the end
// points.
if (numRemoved > 0)
{
assert (numRemoved < i_numPts);
std::sort ((this->m_spans).begin (), (this->m_spans).end ());
i_numPts -= numRemoved;
(this->m_spans).resize (i_numPts);
}
}
// Put the spans back out.
// If the last point has a non-zero alpha, extrapolate the
// maximum density to create an end point.
for (int j = 0; j < i_numPts; ++j)
{
span_type& spanJ = (this->m_spans)[j];
if ((this->m_params).discardZeroAlphaSamples && spanJ.viz >= 1.0)
{
// This span is transparent, ignore it.
continue;
}
double alpha = ClampAlpha (1.0 - spanJ.viz);
// If the alpha is zero, and we're still here, it means
// that the spans are either completely transparent and
// the user has elected to keep them anyway, in which case
// no multiplication by alpha is needed, or alternatively,
// they're "glow" spans which have zero alpha but non-zero
// color. Those glow spans were not unpremultiplied above,
// and therefore do not need to be premultiplied here.
// SO! If alpha is zero, we don't premultiply.
if (alpha > 0.0)
{
spanJ.rgb[0] *= alpha;
spanJ.rgb[1] *= alpha;
spanJ.rgb[2] *= alpha;
}
// Set the channels!
(this->m_deepOutPixel)
.push_back (
spanJ.in, spanJ.rgb[0], spanJ.rgb[1], spanJ.rgb[2], alpha);
}
}
} // End namespace PxDeep
#endif