@@ -87,7 +87,7 @@ class GenericColour;
8787template <typename T>
8888class GenericTransColour ;
8989
90- template <int BIAS, typename T = unsigned char >
90+ template <int BIAS, bool QUANTIZE_TO_NEAREST, typename T = unsigned char >
9191class GenericRGBEColour ;
9292
9393// / @name Colour Channel Luminance
@@ -170,15 +170,16 @@ class GenericRGBColour
170170 mColour [BLUE] = col.blue ();
171171 }
172172
173- template <int BIAS, typename T2>
174- inline explicit GenericRGBColour (const GenericRGBEColour<BIAS,T2>& col)
173+ template <int BIAS, bool QUANTIZE_TO_NEAREST, typename T2>
174+ inline explicit GenericRGBColour (const GenericRGBEColour<BIAS,QUANTIZE_TO_NEAREST, T2>& col)
175175 {
176- if (col.mData [GenericRGBEColour<BIAS,T2>::EXP] > std::numeric_limits<T2>::min ())
176+ if (col.mData [GenericRGBEColour<BIAS,QUANTIZE_TO_NEAREST, T2>::EXP] > std::numeric_limits<T2>::min ())
177177 {
178- double expFactor = ldexp (1.0 ,(int )col.mData [GenericRGBEColour<BIAS,T2>::EXP]-(int )(BIAS+8 ));
179- mColour [RED] = (col.mData [GenericRGBEColour<BIAS,T2>::RED]) * expFactor;
180- mColour [GREEN] = (col.mData [GenericRGBEColour<BIAS,T2>::GREEN]) * expFactor;
181- mColour [BLUE] = (col.mData [GenericRGBEColour<BIAS,T2>::BLUE]) * expFactor;
178+ double expFactor = ldexp (1.0 ,(int )col.mData [GenericRGBEColour<BIAS,QUANTIZE_TO_NEAREST,T2>::EXP]-(int )(BIAS+8 ));
179+ double quantizationFix = (QUANTIZE_TO_NEAREST? 0.0 : 0.5 );
180+ mColour [RED] = (col.mData [GenericRGBEColour<BIAS,QUANTIZE_TO_NEAREST,T2>::RED] + quantizationFix) * expFactor;
181+ mColour [GREEN] = (col.mData [GenericRGBEColour<BIAS,QUANTIZE_TO_NEAREST,T2>::GREEN] + quantizationFix) * expFactor;
182+ mColour [BLUE] = (col.mData [GenericRGBEColour<BIAS,QUANTIZE_TO_NEAREST,T2>::BLUE] + quantizationFix) * expFactor;
182183 }
183184 else
184185 {
@@ -2074,12 +2075,14 @@ typedef GenericTransColour<PreciseColourChannel> PreciseTransColour; ///< Hig
20742075// / @author Christoph Lipka
20752076// / @author Based on MegaPOV HDR code written by Mael and Christoph Hormann
20762077// /
2077- // / @tparam BIAS Bias to use for the exponent.
2078- // / A value of 128 matches Greg Ward's original proposal.
2079- // / @tparam T Type to use for the colour components.
2080- // / Defaults to unsigned char.
2078+ // / @tparam BIAS Bias to use for the exponent.
2079+ // / A value of 128 matches Greg Ward's original proposal.
2080+ // / @tparam QUANTIZE_TO_NEAREST Whether quantization should use round-to-nearest mode, as opposed to rounding
2081+ // / towards zero and compensating upon decoding as in Greg Ward's original proposal.
2082+ // / @tparam T Type to use for the colour components.
2083+ // / Defaults to unsigned char.
20812084// /
2082- template <int BIAS, typename T>
2085+ template <int BIAS, bool QUANTIZE_TO_NEAREST, typename T>
20832086class GenericRGBEColour
20842087{
20852088 public:
@@ -2099,10 +2102,7 @@ class GenericRGBEColour
20992102
21002103 inline GenericRGBEColour ()
21012104 {
2102- mData [RED] = 0 ;
2103- mData [GREEN] = 0 ;
2104- mData [BLUE] = 0 ;
2105- mData [EXP] = 0 ;
2105+ SetToZero (mData );
21062106 }
21072107
21082108 inline GenericRGBEColour (const GenericRGBEColour& col)
@@ -2115,51 +2115,26 @@ class GenericRGBEColour
21152115
21162116 inline explicit GenericRGBEColour (ColourChannel red, ColourChannel green, ColourChannel blue)
21172117 {
2118- RGBColour col (red, green, blue);
2119- double scaleFactor;
2120- if (ComputeExponent (col, mData [EXP], scaleFactor))
2121- {
2122- mData [RED] = clipToType<T>(floor (col.red () * scaleFactor));
2123- mData [GREEN] = clipToType<T>(floor (col.green () * scaleFactor));
2124- mData [BLUE] = clipToType<T>(floor (col.blue () * scaleFactor));
2125- }
2118+ if (QUANTIZE_TO_NEAREST)
2119+ Quantize (mData , RGBColour (red, green, blue), RGBColour (0.5 ));
21262120 else
2127- {
2128- mData [RED] = mData [GREEN] = mData [BLUE] = 0 ;
2129- mData [EXP] = std::numeric_limits<T>::min ();
2130- }
2121+ Quantize (mData , RGBColour (red, green, blue));
21312122 }
21322123
21332124 inline explicit GenericRGBEColour (const RGBColour& col)
21342125 {
2135- double scaleFactor;
2136- if (ComputeExponent (col, mData [EXP], scaleFactor))
2137- {
2138- mData [RED] = clipToType<T>(floor (col.red () * scaleFactor + 0.5 ));
2139- mData [GREEN] = clipToType<T>(floor (col.green () * scaleFactor + 0.5 ));
2140- mData [BLUE] = clipToType<T>(floor (col.blue () * scaleFactor + 0.5 ));
2141- }
2126+ if (QUANTIZE_TO_NEAREST)
2127+ Quantize (mData , col, RGBColour (0.5 ));
21422128 else
2143- {
2144- mData [RED] = mData [GREEN] = mData [BLUE] = 0 ;
2145- mData [EXP] = std::numeric_limits<T>::min ();
2146- }
2129+ Quantize (mData , col);
21472130 }
21482131
21492132 inline explicit GenericRGBEColour (const RGBColour& col, const RGBColour& dither)
21502133 {
2151- double scaleFactor;
2152- if (ComputeExponent (col, mData [EXP], scaleFactor))
2153- {
2154- mData [RED] = clip<T>(floor (col.red () * scaleFactor + 0.5 + dither.red ()));
2155- mData [GREEN] = clip<T>(floor (col.green () * scaleFactor + 0.5 + dither.green ()));
2156- mData [BLUE] = clip<T>(floor (col.blue () * scaleFactor + 0.5 + dither.blue ()));
2157- }
2134+ if (QUANTIZE_TO_NEAREST)
2135+ Quantize (mData , col, RGBColour (0.5 ) + dither);
21582136 else
2159- {
2160- mData [RED] = mData [GREEN] = mData [BLUE] = 0 ;
2161- mData [EXP] = std::numeric_limits<T>::min ();
2162- }
2137+ Quantize (mData , col, dither);
21632138 }
21642139
21652140 inline const DATA& operator *() const
@@ -2178,29 +2153,85 @@ class GenericRGBEColour
21782153
21792154 inline static bool ComputeExponent (const RGBColour& col, T& biasedExponent, double & scaleFactor)
21802155 {
2181- ColourChannel maxChannel;
2182- if (std::numeric_limits<T>::is_signed)
2183- maxChannel = col.MaxAbs ();
2184- else
2185- maxChannel = col.Max ();
2156+ // Determine the magnitude of the colour value.
2157+ ColourChannel maxChannel = (std::numeric_limits<T>::is_signed ? col.MaxAbs () : col.Max ());
21862158
21872159 if (maxChannel <= 1.0e-32 ) // TODO - magic number
21882160 return false ;
21892161
21902162 int exponent;
21912163 double maxChannelMantissa = frexp (maxChannel, &exponent);
21922164 biasedExponent = clipToType<T>(exponent + BIAS);
2165+ scaleFactor = ldexp (std::numeric_limits<T>::max () + 1.0 , BIAS-biasedExponent);
2166+ return true ;
2167+ }
21932168
2194- if (biasedExponent != exponent + BIAS)
2195- maxChannelMantissa = ldexp (maxChannelMantissa, exponent + BIAS - biasedExponent);
2169+ inline static void SetToZero (DATA& data)
2170+ {
2171+ data[RED] = data[GREEN] = data[BLUE] = 0 ;
2172+ data[EXP] = std::numeric_limits<T>::min ();
2173+ }
21962174
2197- scaleFactor = (std::numeric_limits<T>::max () + 1.0 ) * maxChannelMantissa / maxChannel;
2198- return true ;
2175+ // / @param[out] data The quantized data.
2176+ // / @param[in] col The colour to quantize.
2177+ inline static void Quantize (DATA& data, const RGBColour& col)
2178+ {
2179+ double scaleFactor;
2180+ if (ComputeExponent (col, data[EXP], scaleFactor))
2181+ {
2182+ RGBColour colMantissa = col * scaleFactor;
2183+ data[RED] = clipToType<T>(floor (colMantissa.red ()));
2184+ data[GREEN] = clipToType<T>(floor (colMantissa.green ()));
2185+ data[BLUE] = clipToType<T>(floor (colMantissa.blue ()));
2186+ }
2187+ else
2188+ SetToZero (data);
2189+ }
2190+
2191+ // / @param[out] data The quantized data.
2192+ // / @param[in] col The colour to quantize.
2193+ // / @param[in] encOff An offset to add to the mantissa before quantization.
2194+ inline static void Quantize (DATA& data, const RGBColour& col, const RGBColour& encOff)
2195+ {
2196+ double scaleFactor;
2197+ if (ComputeExponent (col, data[EXP], scaleFactor))
2198+ {
2199+ RGBColour colMantissa = col * scaleFactor + encOff;
2200+
2201+ // The additional encoding offset might have resulted in one of the mantissas to exceed the maximum,
2202+ // or make all drop below half the maximum; in both cases we want to adjust the exponent and mantissas
2203+ // accordingly.
2204+ ColourChannel maxChannel = (std::numeric_limits<T>::is_signed ? colMantissa.MaxAbs () : colMantissa.Max ());
2205+ if (maxChannel > std::numeric_limits<T>::max ())
2206+ {
2207+ if (data[EXP] < std::numeric_limits<T>::max ())
2208+ {
2209+ data[EXP] ++;
2210+ scaleFactor *= 0.5 ;
2211+ colMantissa = col * scaleFactor + encOff;
2212+ }
2213+ }
2214+ else if (maxChannel * 2.0 <= std::numeric_limits<T>::max ())
2215+ {
2216+ if (data[EXP] > std::numeric_limits<T>::min ())
2217+ {
2218+ data[EXP] --;
2219+ scaleFactor *= 2.0 ;
2220+ colMantissa = col * scaleFactor + encOff;
2221+ }
2222+ }
2223+
2224+ data[RED] = clipToType<T>(floor (colMantissa.red ()));
2225+ data[GREEN] = clipToType<T>(floor (colMantissa.green ()));
2226+ data[BLUE] = clipToType<T>(floor (colMantissa.blue ()));
2227+ }
2228+ else
2229+ SetToZero (data);
21992230 }
22002231};
22012232
2202- typedef GenericRGBEColour<128 > RadianceHDRColour; // /< RGBE format as originally proposed by Greg Ward.
2203- typedef GenericRGBEColour<250 > PhotonColour; // /< RGBE format as adapted by Nathan Kopp for photon mapping.
2233+ typedef GenericRGBEColour<128 , false > RadianceHDRColour; // /< RGBE format as originally proposed by Greg Ward.
2234+ typedef GenericRGBEColour<250 , true > PhotonColour; // /< RGBE format as adapted by Nathan Kopp for photon mapping.
22042235
22052236}
22062237
0 commit comments