Skip to content

Commit f6888be

Browse files
committed
Eliminated some inconsistencies in RGBE-format colour handling.
1 parent 9762bc5 commit f6888be

File tree

3 files changed

+95
-64
lines changed

3 files changed

+95
-64
lines changed

source/base/colour.h

Lines changed: 93 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ class GenericColour;
8787
template<typename T>
8888
class GenericTransColour;
8989

90-
template<int BIAS, typename T = unsigned char>
90+
template<int BIAS, bool QUANTIZE_TO_NEAREST, typename T = unsigned char>
9191
class 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>
20832086
class 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

source/base/version.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
#define OFFICIAL_VERSION_STRING "3.7.1"
4646
#define OFFICIAL_VERSION_NUMBER 371
4747

48-
#define POV_RAY_PRERELEASE "alpha.8492620"
48+
#define POV_RAY_PRERELEASE "alpha.8497793"
4949

5050
#if (POV_RAY_IS_AUTOBUILD == 1) && ((POV_RAY_IS_OFFICIAL == 1) || (POV_RAY_IS_SEMI_OFFICIAL == 1))
5151
#ifdef POV_RAY_PRERELEASE

unix/VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
3.7.1-alpha.8492620
1+
3.7.1-alpha.8497793

0 commit comments

Comments
 (0)