/*****************************************************************************/ // 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); /*****************************************************************************/ #endif /*****************************************************************************/