// // 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 class FourChanDeepRgbaContinuous : public BaseDeepHelper< RGBA_T, FourChanDeepRgbaContinuous, SpanRgba> { public: typedef BaseDeepHelper, SpanRgba> super_type; typedef FourChanDeepRgbaContinuous this_type; typedef typename super_type::span_type span_type; FourChanDeepRgbaContinuous ( DtexFile* i_dtexFile, int i_numDtexChans, const Parameters& i_params) : BaseDeepHelper, SpanRgba> ( i_dtexFile, i_numDtexChans, i_params) {} void processDeepPixel (int i_numPts); }; //-***************************************************************************** // FOUR CHANNEL DEEP RGBA DISCRETE //-***************************************************************************** template class FourChanDeepRgbaDiscrete : public BaseDeepHelper, SpanRgba> { public: typedef BaseDeepHelper, SpanRgba> super_type; typedef FourChanDeepRgbaDiscrete this_type; typedef typename super_type::span_type span_type; FourChanDeepRgbaDiscrete ( DtexFile* i_dtexFile, int i_numDtexChans, const Parameters& i_params) : BaseDeepHelper, SpanRgba> ( i_dtexFile, i_numDtexChans, i_params) {} void processDeepPixel (int i_numPts); }; //-***************************************************************************** template void FourChanDeepRgbaContinuous::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 void FourChanDeepRgbaDiscrete::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