You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1305 lines
22 KiB

/*****************************************************************************/
// Copyright 2006-2012 Adobe Systems Incorporated
// All Rights Reserved.
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in
// accordance with the terms of the Adobe license agreement accompanying it.
/*****************************************************************************/
/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_utils.h#3 $ */
/* $DateTime: 2012/06/14 20:24:41 $ */
/* $Change: 835078 $ */
/* $Author: tknoll $ */
/*****************************************************************************/
#ifndef __dng_utils__
#define __dng_utils__
/*****************************************************************************/
#include <cmath>
#include <limits>
#include "dng_classes.h"
#include "dng_flags.h"
#include "dng_memory.h"
#include "dng_safe_arithmetic.h"
#include "dng_types.h"
/*****************************************************************************/
// The unsigned integer overflow is intended here since a wrap around is used to
// calculate the abs() in the branchless version.
#if defined(__clang__) && defined(__has_attribute)
#if __has_attribute(no_sanitize)
__attribute__((no_sanitize("unsigned-integer-overflow")))
#endif
#endif
inline uint32 Abs_int32 (int32 x)
{
#if 0
// Reference version.
return (uint32) (x < 0 ? -x : x);
#else
// Branchless version.
uint32 mask = (uint32) (x >> 31);
return (uint32) (((uint32) x + mask) ^ mask);
#endif
}
inline int32 Min_int32 (int32 x, int32 y)
{
return (x <= y ? x : y);
}
inline int32 Max_int32 (int32 x, int32 y)
{
return (x >= y ? x : y);
}
inline int32 Pin_int32 (int32 min, int32 x, int32 max)
{
return Max_int32 (min, Min_int32 (x, max));
}
inline int32 Pin_int32_between (int32 a, int32 x, int32 b)
{
int32 min, max;
if (a < b) { min = a; max = b; }
else { min = b; max = a; }
return Pin_int32 (min, x, max);
}
/*****************************************************************************/
inline uint16 Min_uint16 (uint16 x, uint16 y)
{
return (x <= y ? x : y);
}
inline uint16 Max_uint16 (uint16 x, uint16 y)
{
return (x >= y ? x : y);
}
inline int16 Pin_int16 (int32 x)
{
x = Pin_int32 (-32768, x, 32767);
return (int16) x;
}
/*****************************************************************************/
inline uint32 Min_uint32 (uint32 x, uint32 y)
{
return (x <= y ? x : y);
}
inline uint32 Min_uint32 (uint32 x, uint32 y, uint32 z)
{
return Min_uint32 (x, Min_uint32 (y, z));
}
inline uint32 Max_uint32 (uint32 x, uint32 y)
{
return (x >= y ? x : y);
}
inline uint32 Max_uint32 (uint32 x, uint32 y, uint32 z)
{
return Max_uint32 (x, Max_uint32 (y, z));
}
inline uint32 Pin_uint32 (uint32 min, uint32 x, uint32 max)
{
return Max_uint32 (min, Min_uint32 (x, max));
}
/*****************************************************************************/
inline uint16 Pin_uint16 (int32 x)
{
#if 0
// Reference version.
x = Pin_int32 (0, x, 0x0FFFF);
#else
// Single branch version.
if (x & ~65535)
{
x = ~x >> 31;
}
#endif
return (uint16) x;
}
/*****************************************************************************/
inline uint32 RoundDown2 (uint32 x)
{
return x & (uint32) ~1;
}
inline uint32 RoundDown4 (uint32 x)
{
return x & (uint32) ~3;
}
inline uint32 RoundDown8 (uint32 x)
{
return x & (uint32) ~7;
}
inline uint32 RoundDown16 (uint32 x)
{
return x & (uint32) ~15;
}
/******************************************************************************/
inline bool RoundUpForPixelSize (uint32 x, uint32 pixelSize, uint32 *result)
{
uint32 multiple;
switch (pixelSize)
{
case 1:
case 2:
case 4:
case 8:
multiple = 16 / pixelSize;
break;
default:
multiple = 16;
break;
}
return RoundUpUint32ToMultiple(x, multiple, result);
}
/******************************************************************************/
// Type of padding to be performed by ComputeBufferSize().
enum PaddingType
{
// Don't perform any padding.
padNone,
// Pad each scanline to an integer multiple of 16 bytes (in the same way
// that RoundUpForPixelSize() does).
pad16Bytes
};
// Returns the number of bytes required for an image tile with the given pixel
// type, tile size, number of image planes, and desired padding. Throws a
// dng_exception with dng_error_memory error code if one of the components of
// tileSize is negative or if arithmetic overflow occurs during the computation.
uint32 ComputeBufferSize(uint32 pixelType, const dng_point &tileSize,
uint32 numPlanes, PaddingType paddingType);
/******************************************************************************/
inline uint64 Abs_int64 (int64 x)
{
return (uint64) (x < 0 ? -x : x);
}
inline int64 Min_int64 (int64 x, int64 y)
{
return (x <= y ? x : y);
}
inline int64 Max_int64 (int64 x, int64 y)
{
return (x >= y ? x : y);
}
inline int64 Pin_int64 (int64 min, int64 x, int64 max)
{
return Max_int64 (min, Min_int64 (x, max));
}
/******************************************************************************/
inline uint64 Min_uint64 (uint64 x, uint64 y)
{
return (x <= y ? x : y);
}
inline uint64 Max_uint64 (uint64 x, uint64 y)
{
return (x >= y ? x : y);
}
inline uint64 Pin_uint64 (uint64 min, uint64 x, uint64 max)
{
return Max_uint64 (min, Min_uint64 (x, max));
}
/*****************************************************************************/
inline real32 Abs_real32 (real32 x)
{
return (x < 0.0f ? -x : x);
}
inline real32 Min_real32 (real32 x, real32 y)
{
return (x < y ? x : y);
}
inline real32 Max_real32 (real32 x, real32 y)
{
return (x > y ? x : y);
}
inline real32 Pin_real32 (real32 min, real32 x, real32 max)
{
return Max_real32 (min, Min_real32 (x, max));
}
inline real32 Pin_real32 (real32 x)
{
return Pin_real32 (0.0f, x, 1.0f);
}
inline real32 Pin_real32_Overrange (real32 min,
real32 x,
real32 max)
{
// Normal numbers in (min,max). No change.
if (x > min && x < max)
{
return x;
}
// Map large numbers (including positive infinity) to max.
else if (x > min)
{
return max;
}
// Map everything else (including negative infinity and all NaNs) to min.
return min;
}
inline real32 Pin_Overrange (real32 x)
{
// Normal in-range numbers, except for plus and minus zero.
if (x > 0.0f && x <= 1.0f)
{
return x;
}
// Large numbers, including positive infinity.
else if (x > 0.5f)
{
return 1.0f;
}
// Plus and minus zero, negative numbers, negative infinity, and all NaNs.
return 0.0f;
}
inline real32 Lerp_real32 (real32 a, real32 b, real32 t)
{
return a + t * (b - a);
}
/*****************************************************************************/
inline real64 Abs_real64 (real64 x)
{
return (x < 0.0 ? -x : x);
}
inline real64 Min_real64 (real64 x, real64 y)
{
return (x < y ? x : y);
}
inline real64 Max_real64 (real64 x, real64 y)
{
return (x > y ? x : y);
}
inline real64 Pin_real64 (real64 min, real64 x, real64 max)
{
return Max_real64 (min, Min_real64 (x, max));
}
inline real64 Pin_real64 (real64 x)
{
return Pin_real64 (0.0, x, 1.0);
}
inline real64 Pin_real64_Overrange (real64 min,
real64 x,
real64 max)
{
// Normal numbers in (min,max). No change.
if (x > min && x < max)
{
return x;
}
// Map large numbers (including positive infinity) to max.
else if (x > min)
{
return max;
}
// Map everything else (including negative infinity and all NaNs) to min.
return min;
}
inline real64 Lerp_real64 (real64 a, real64 b, real64 t)
{
return a + t * (b - a);
}
/*****************************************************************************/
inline int32 Round_int32 (real32 x)
{
return (int32) (x > 0.0f ? x + 0.5f : x - 0.5f);
}
inline int32 Round_int32 (real64 x)
{
const real64 temp = x > 0.0 ? x + 0.5 : x - 0.5;
// NaNs will fail this test (because NaNs compare false against
// everything) and will therefore also take the else branch.
if (temp > real64(std::numeric_limits<int32>::min()) - 1.0 &&
temp < real64(std::numeric_limits<int32>::max()) + 1.0)
{
return (int32) temp;
}
else
{
ThrowProgramError("Overflow in Round_int32");
// Dummy return.
return 0;
}
}
inline uint32 Floor_uint32 (real32 x)
{
return (uint32) Max_real32 (0.0f, x);
}
inline uint32 Floor_uint32 (real64 x)
{
const real64 temp = Max_real64 (0.0, x);
// NaNs will fail this test (because NaNs compare false against
// everything) and will therefore also take the else branch.
if (temp < real64(std::numeric_limits<uint32>::max()) + 1.0)
{
return (uint32) temp;
}
else
{
ThrowProgramError("Overflow in Floor_uint32");
// Dummy return.
return 0;
}
}
inline uint32 Round_uint32 (real32 x)
{
return Floor_uint32 (x + 0.5f);
}
inline uint32 Round_uint32 (real64 x)
{
return Floor_uint32 (x + 0.5);
}
/******************************************************************************/
inline int64 Round_int64 (real64 x)
{
return (int64) (x >= 0.0 ? x + 0.5 : x - 0.5);
}
/*****************************************************************************/
const int64 kFixed64_One = (((int64) 1) << 32);
const int64 kFixed64_Half = (((int64) 1) << 31);
/******************************************************************************/
inline int64 Real64ToFixed64 (real64 x)
{
return Round_int64 (x * (real64) kFixed64_One);
}
/******************************************************************************/
inline real64 Fixed64ToReal64 (int64 x)
{
return x * (1.0 / (real64) kFixed64_One);
}
/*****************************************************************************/
inline char ForceUppercase (char c)
{
if (c >= 'a' && c <= 'z')
{
c -= 'a' - 'A';
}
return c;
}
/*****************************************************************************/
inline uint16 SwapBytes16 (uint16 x)
{
return (uint16) ((x << 8) |
(x >> 8));
}
inline uint32 SwapBytes32 (uint32 x)
{
return (x << 24) +
((x << 8) & 0x00FF0000) +
((x >> 8) & 0x0000FF00) +
(x >> 24);
}
/*****************************************************************************/
inline bool IsAligned16 (const void *p)
{
return (((uintptr) p) & 1) == 0;
}
inline bool IsAligned32 (const void *p)
{
return (((uintptr) p) & 3) == 0;
}
inline bool IsAligned64 (const void *p)
{
return (((uintptr) p) & 7) == 0;
}
inline bool IsAligned128 (const void *p)
{
return (((uintptr) p) & 15) == 0;
}
/******************************************************************************/
// Converts from RGB values (range 0.0 to 1.0) to HSV values (range 0.0 to
// 6.0 for hue, and 0.0 to 1.0 for saturation and value).
inline void DNG_RGBtoHSV (real32 r,
real32 g,
real32 b,
real32 &h,
real32 &s,
real32 &v)
{
v = Max_real32 (r, Max_real32 (g, b));
real32 gap = v - Min_real32 (r, Min_real32 (g, b));
if (gap > 0.0f)
{
if (r == v)
{
h = (g - b) / gap;
if (h < 0.0f)
{
h += 6.0f;
}
}
else if (g == v)
{
h = 2.0f + (b - r) / gap;
}
else
{
h = 4.0f + (r - g) / gap;
}
s = gap / v;
}
else
{
h = 0.0f;
s = 0.0f;
}
}
/*****************************************************************************/
// Converts from HSV values (range 0.0 to 6.0 for hue, and 0.0 to 1.0 for
// saturation and value) to RGB values (range 0.0 to 1.0).
inline void DNG_HSVtoRGB (real32 h,
real32 s,
real32 v,
real32 &r,
real32 &g,
real32 &b)
{
if (s > 0.0f)
{
if (!std::isfinite(h))
ThrowProgramError("Unexpected NaN or Inf");
h = std::fmod(h, 6.0f);
if (h < 0.0f)
h += 6.0f;
int32 i = (int32) h;
real32 f = h - (real32) i;
real32 p = v * (1.0f - s);
#define q (v * (1.0f - s * f))
#define t (v * (1.0f - s * (1.0f - f)))
switch (i)
{
case 0: r = v; g = t; b = p; break;
case 1: r = q; g = v; b = p; break;
case 2: r = p; g = v; b = t; break;
case 3: r = p; g = q; b = v; break;
case 4: r = t; g = p; b = v; break;
case 5: r = v; g = p; b = q; break;
}
#undef q
#undef t
}
else
{
r = v;
g = v;
b = v;
}
}
/******************************************************************************/
// High resolution timer, for code profiling.
real64 TickTimeInSeconds ();
// Lower resolution timer, but more stable.
real64 TickCountInSeconds ();
/******************************************************************************/
class dng_timer
{
public:
dng_timer (const char *message);
~dng_timer ();
private:
// Hidden copy constructor and assignment operator.
dng_timer (const dng_timer &timer);
dng_timer & operator= (const dng_timer &timer);
private:
const char *fMessage;
real64 fStartTime;
};
/*****************************************************************************/
// Returns the maximum squared Euclidean distance from the specified point to the
// specified rectangle rect.
real64 MaxSquaredDistancePointToRect (const dng_point_real64 &point,
const dng_rect_real64 &rect);
/*****************************************************************************/
// Returns the maximum Euclidean distance from the specified point to the specified
// rectangle rect.
real64 MaxDistancePointToRect (const dng_point_real64 &point,
const dng_rect_real64 &rect);
/*****************************************************************************/
inline uint32 DNG_HalfToFloat (uint16 halfValue)
{
int32 sign = (halfValue >> 15) & 0x00000001;
int32 exponent = (halfValue >> 10) & 0x0000001f;
int32 mantissa = halfValue & 0x000003ff;
if (exponent == 0)
{
if (mantissa == 0)
{
// Plus or minus zero
return (uint32) (sign << 31);
}
else
{
// Denormalized number -- renormalize it
while (!(mantissa & 0x00000400))
{
mantissa <<= 1;
exponent -= 1;
}
exponent += 1;
mantissa &= ~0x00000400;
}
}
else if (exponent == 31)
{
if (mantissa == 0)
{
// Positive or negative infinity, convert to maximum (16 bit) values.
return (uint32) ((sign << 31) | ((0x1eL + 127 - 15) << 23) | (0x3ffL << 13));
}
else
{
// Nan -- Just set to zero.
return 0;
}
}
// Normalized number
exponent += (127 - 15);
mantissa <<= 13;
// Assemble sign, exponent and mantissa.
return (uint32) ((sign << 31) | (exponent << 23) | mantissa);
}
/*****************************************************************************/
inline uint16 DNG_FloatToHalf (uint32 i)
{
int32 sign = (i >> 16) & 0x00008000;
int32 exponent = ((i >> 23) & 0x000000ff) - (127 - 15);
int32 mantissa = i & 0x007fffff;
if (exponent <= 0)
{
if (exponent < -10)
{
// Zero or underflow to zero.
return (uint16)sign;
}
// E is between -10 and 0. We convert f to a denormalized half.
mantissa = (mantissa | 0x00800000) >> (1 - exponent);
// Round to nearest, round "0.5" up.
//
// Rounding may cause the significand to overflow and make
// our number normalized. Because of the way a half's bits
// are laid out, we don't have to treat this case separately;
// the code below will handle it correctly.
if (mantissa & 0x00001000)
mantissa += 0x00002000;
// Assemble the half from sign, exponent (zero) and mantissa.
return (uint16)(sign | (mantissa >> 13));
}
else if (exponent == 0xff - (127 - 15))
{
if (mantissa == 0)
{
// F is an infinity; convert f to a half
// infinity with the same sign as f.
return (uint16)(sign | 0x7c00);
}
else
{
// F is a NAN; produce a half NAN that preserves
// the sign bit and the 10 leftmost bits of the
// significand of f.
return (uint16)(sign | 0x7c00 | (mantissa >> 13));
}
}
// E is greater than zero. F is a normalized float.
// We try to convert f to a normalized half.
// Round to nearest, round "0.5" up
if (mantissa & 0x00001000)
{
mantissa += 0x00002000;
if (mantissa & 0x00800000)
{
mantissa = 0; // overflow in significand,
exponent += 1; // adjust exponent
}
}
// Handle exponent overflow
if (exponent > 30)
{
return (uint16)(sign | 0x7c00); // infinity with the same sign as f.
}
// Assemble the half from sign, exponent and mantissa.
return (uint16)(sign | (exponent << 10) | (mantissa >> 13));
}
/*****************************************************************************/
inline uint32 DNG_FP24ToFloat (const uint8 *input)
{
int32 sign = (input [0] >> 7) & 0x01;
int32 exponent = (input [0] ) & 0x7F;
int32 mantissa = (((int32) input [1]) << 8) | input[2];
if (exponent == 0)
{
if (mantissa == 0)
{
// Plus or minus zero
return (uint32) (sign << 31);
}
else
{
// Denormalized number -- renormalize it
while (!(mantissa & 0x00010000))
{
mantissa <<= 1;
exponent -= 1;
}
exponent += 1;
mantissa &= ~0x00010000;
}
}
else if (exponent == 127)
{
if (mantissa == 0)
{
// Positive or negative infinity, convert to maximum (24 bit) values.
return (uint32) ((sign << 31) | ((0x7eL + 128 - 64) << 23) | (0xffffL << 7));
}
else
{
// Nan -- Just set to zero.
return 0;
}
}
// Normalized number
exponent += (128 - 64);
mantissa <<= 7;
// Assemble sign, exponent and mantissa.
return (uint32) ((sign << 31) | (exponent << 23) | mantissa);
}
/*****************************************************************************/
inline void DNG_FloatToFP24 (uint32 input, uint8 *output)
{
int32 exponent = (int32) ((input >> 23) & 0xFF) - 128;
int32 mantissa = input & 0x007FFFFF;
if (exponent == 127) // infinity or NaN
{
// Will the NaN alais to infinity?
if (mantissa != 0x007FFFFF && ((mantissa >> 7) == 0xFFFF))
{
mantissa &= 0x003FFFFF; // knock out msb to make it a NaN
}
}
else if (exponent > 63) // overflow, map to infinity
{
exponent = 63;
mantissa = 0x007FFFFF;
}
else if (exponent <= -64)
{
if (exponent >= -79) // encode as denorm
{
mantissa = (mantissa | 0x00800000) >> (-63 - exponent);
}
else // underflow to zero
{
mantissa = 0;
}
exponent = -64;
}
output [0] = (uint8)(((input >> 24) & 0x80) | (uint32) (exponent + 64));
output [1] = (mantissa >> 15) & 0x00FF;
output [2] = (mantissa >> 7) & 0x00FF;
}
/******************************************************************************/
// The following code was from PSDivide.h in Photoshop.
// High order 32-bits of an unsigned 32 by 32 multiply.
#ifndef MULUH
#if defined(_X86_) && defined(_MSC_VER)
inline uint32 Muluh86 (uint32 x, uint32 y)
{
uint32 result;
__asm
{
MOV EAX, x
MUL y
MOV result, EDX
}
return (result);
}
#define MULUH Muluh86
#else
#define MULUH(x,y) ((uint32) (((x) * (uint64) (y)) >> 32))
#endif
#endif
// High order 32-bits of an signed 32 by 32 multiply.
#ifndef MULSH
#if defined(_X86_) && defined(_MSC_VER)
inline int32 Mulsh86 (int32 x, int32 y)
{
int32 result;
__asm
{
MOV EAX, x
IMUL y
MOV result, EDX
}
return (result);
}
#define MULSH Mulsh86
#else
#define MULSH(x,y) ((int32) (((x) * (int64) (y)) >> 32))
#endif
#endif
/******************************************************************************/
// Random number generator (identical to Apple's) for portable use.
// This implements the "minimal standard random number generator"
// as proposed by Park and Miller in CACM October, 1988.
// It has a period of 2147483647 (0x7fffffff)
// This is the ACM standard 30 bit generator:
// x' = (x * 16807) mod 2^31-1
// This function intentionally exploits the defined behavior of unsigned integer
// overflow.
#if defined(__clang__) && defined(__has_attribute)
#if __has_attribute(no_sanitize)
__attribute__((no_sanitize("unsigned-integer-overflow")))
#endif
#endif
inline uint32 DNG_Random (uint32 seed)
{
// high = seed / 127773
uint32 temp = MULUH (0x069C16BD, seed);
uint32 high = (temp + ((seed - temp) >> 1)) >> 16;
// low = seed % 127773
uint32 low = seed - high * 127773;
// seed = (seed * 16807) % 2147483647
seed = 16807 * low - 2836 * high;
if (seed & 0x80000000)
seed += 2147483647;
return seed;
}
/*****************************************************************************/
class dng_dither
{
public:
static const uint32 kRNGBits = 7;
static const uint32 kRNGSize = 1 << kRNGBits;
static const uint32 kRNGMask = kRNGSize - 1;
static const uint32 kRNGSize2D = kRNGSize * kRNGSize;
private:
dng_memory_data fNoiseBuffer;
private:
dng_dither ();
// Hidden copy constructor and assignment operator.
dng_dither (const dng_dither &);
dng_dither & operator= (const dng_dither &);
public:
static const dng_dither & Get ();
public:
const uint16 *NoiseBuffer16 () const
{
return fNoiseBuffer.Buffer_uint16 ();
}
};
/*****************************************************************************/
void HistogramArea (dng_host &host,
const dng_image &image,
const dng_rect &area,
uint32 *hist,
uint32 histLimit,
uint32 plane = 0);
/*****************************************************************************/
void LimitFloatBitDepth (dng_host &host,
const dng_image &srcImage,
dng_image &dstImage,
uint32 bitDepth,
real32 scale = 1.0f);
/*****************************************************************************/
#if qMacOS
/*****************************************************************************/
template<typename T>
class CFReleaseHelper
{
private:
T fRef;
public:
CFReleaseHelper (T ref)
: fRef (ref)
{
}
~CFReleaseHelper ()
{
if (fRef)
{
CFRelease (fRef);
}
}
T Get () const
{
return fRef;
}
};
/*****************************************************************************/
#endif // qMacOS
/*****************************************************************************/
#endif
/*****************************************************************************/