| /*****************************************************************************/ |
| // Copyright 2006-2023 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. |
| /*****************************************************************************/ |
| |
| #ifndef __dng_utils__ |
| #define __dng_utils__ |
| |
| /*****************************************************************************/ |
| |
| #include <cmath> |
| #include <limits> |
| #include <utility> |
| |
| #include "dng_classes.h" |
| #include "dng_flags.h" |
| #include "dng_memory.h" |
| #include "dng_safe_arithmetic.h" |
| #include "dng_string.h" |
| #include "dng_types.h" |
| #include "dng_uncopyable.h" |
| |
| /*****************************************************************************/ |
| |
| #if qWinOS |
| #undef min |
| #undef max |
| #endif |
| |
| /*****************************************************************************/ |
| |
| // The unsigned integer overflow is intended here since a wrap around is used to |
| // calculate the abs() in the branchless version. |
| |
| DNG_ATTRIB_NO_SANITIZE("unsigned-integer-overflow") |
| 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 RoundUp2 (uint32 x) |
| { |
| |
| return (x + 1) & (uint32) ~1; |
| |
| } |
| |
| inline uint32 RoundUp4 (uint32 x) |
| { |
| |
| return (x + 3) & (uint32) ~3; |
| |
| } |
| |
| inline uint32 RoundUp8 (uint32 x) |
| { |
| |
| return (x + 7) & (uint32) ~7; |
| |
| } |
| |
| inline uint32 RoundUp16 (uint32 x) |
| { |
| |
| return (x + 15) & (uint32) ~15; |
| |
| } |
| |
| inline uint32 RoundUp32 (uint32 x) |
| { |
| |
| return (x + 31) & (uint32) ~31; |
| |
| } |
| |
| inline uint32 RoundUpSIMD (uint32 x) |
| { |
| |
| #if qDNGAVXSupport |
| return RoundUp32 (x); |
| #else |
| return RoundUp16 (x); |
| #endif |
| |
| } |
| |
| inline uint32 RoundUp4096 (uint32 x) |
| { |
| |
| return (x + 4095) & (uint32) ~4095; |
| |
| } |
| |
| /******************************************************************************/ |
| |
| 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 uint32 RoundDown32 (uint32 x) |
| { |
| |
| return x & (uint32) ~31; |
| |
| } |
| |
| inline uint32 RoundDownSIMD (uint32 x) |
| { |
| |
| #if qDNGAVXSupport |
| return RoundDown32 (x); |
| #else |
| return RoundDown16 (x); |
| #endif |
| |
| } |
| |
| /******************************************************************************/ |
| |
| inline bool RoundUpForPixelSize (uint32 x, |
| uint32 pixelSize, |
| uint32 *result) |
| { |
| |
| #if qDNGAVXSupport |
| static const uint32 kTargetMultiple = 32; |
| #else |
| static const uint32 kTargetMultiple = 16; |
| #endif |
| |
| uint32 multiple; |
| |
| switch (pixelSize) |
| { |
| |
| case 1: |
| case 2: |
| case 4: |
| case 8: |
| { |
| multiple = kTargetMultiple / pixelSize; |
| break; |
| } |
| |
| default: |
| { |
| multiple = kTargetMultiple; |
| break; |
| } |
| |
| } |
| |
| return RoundUpUint32ToMultiple (x, multiple, result); |
| |
| } |
| |
| /******************************************************************************/ |
| |
| inline uint32 RoundUpForPixelSize (uint32 x, |
| uint32 pixelSize) |
| { |
| |
| uint32 result = 0; |
| |
| if (!RoundUpForPixelSize (x, pixelSize, &result)) |
| { |
| ThrowOverflow ("RoundUpForPixelSize"); |
| } |
| |
| return result; |
| |
| } |
| |
| /******************************************************************************/ |
| |
| inline int32 RoundUpForPixelSizeAsInt32 (uint32 x, |
| uint32 pixelSize) |
| { |
| |
| uint32 result = 0; |
| |
| if (!RoundUpForPixelSize (x, pixelSize, &result)) |
| { |
| ThrowOverflow ("RoundUpForPixelSize"); |
| } |
| |
| dng_safe_uint32 safeResult (result); |
| |
| return dng_safe_int32 (safeResult).Get (); |
| |
| } |
| |
| /******************************************************************************/ |
| |
| // Type of padding to be performed by ComputeBufferSize. |
| |
| enum PaddingType |
| { |
| |
| // Don't perform any padding. |
| |
| padNone, |
| |
| // Pad each scanline to an integer multiple of SIMD vector width (16 or |
| // 32) bytes (in the same way that RoundUpForPixelSize() does). |
| |
| padSIMDBytes |
| |
| }; |
| |
| // 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)); |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| DNG_ALWAYS_INLINE |
| real32 Abs_real32 (real32 x) |
| { |
| |
| return (x < 0.0f ? -x : x); |
| |
| } |
| |
| DNG_ALWAYS_INLINE |
| real32 Min_real32 (real32 x, real32 y) |
| { |
| |
| return (x < y ? x : y); |
| |
| } |
| |
| DNG_ALWAYS_INLINE |
| real32 Max_real32 (real32 x, real32 y) |
| { |
| |
| return (x > y ? x : y); |
| |
| } |
| |
| DNG_ALWAYS_INLINE |
| real32 Pin_real32 (real32 min, real32 x, real32 max) |
| { |
| |
| return Max_real32 (min, Min_real32 (x, max)); |
| |
| } |
| |
| DNG_ALWAYS_INLINE |
| real32 Pin_real32 (real32 x) |
| { |
| |
| return Pin_real32 (0.0f, x, 1.0f); |
| |
| } |
| |
| DNG_ALWAYS_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; |
| |
| } |
| |
| DNG_ALWAYS_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; |
| |
| } |
| |
| DNG_ALWAYS_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 uint8 Floor_uint8 (real32 x) |
| { |
| |
| return (uint8) Max_real32 (0.0f, x); |
| |
| } |
| |
| inline uint8 Floor_uint8 (real64 x) |
| { |
| |
| return (uint8) Max_real64 (0.0, x); |
| |
| } |
| |
| inline uint8 Round_uint8 (real32 x) |
| { |
| |
| return Floor_uint8 (x + 0.5f); |
| |
| } |
| |
| inline uint8 Round_uint8 (real64 x) |
| { |
| |
| return Floor_uint8 (x + 0.5); |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| 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 (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 Floor_uint32 (real32 x) |
| { |
| |
| return Floor_uint32 (static_cast<real64> (x)); |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| 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). |
| // |
| // INPUT REQUIREMENTS: |
| // All input RGB values are expected to be >= 0. |
| // If any RGB value is < 0 the resulting behavior is undefined and in practice |
| // can generate out of range S values (even NaN) and negative v values. |
| |
| 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 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). |
| // |
| // This function pins input RGB >= 0 before calling DNG_RGBtoHSV() |
| |
| inline void DNG_PinnedNonnegativeRGBtoHSV (real32 r, |
| real32 g, |
| real32 b, |
| real32 &h, |
| real32 &s, |
| real32 &v) |
| { |
| |
| // Pin RGB >= 0. |
| |
| r = Max_real32 (r, 0.0f); |
| g = Max_real32 (g, 0.0f); |
| b = Max_real32 (b, 0.0f); |
| |
| DNG_RGBtoHSV (r, g, b, h, s, v); |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| // 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))) |
| |
| // The integer 'i' is normally one of {0,1,2,3,4,5}. |
| // However, in rare cases it is possible that i = 6. |
| // Namely, if the input h is a negative number with very small |
| // magnitude (or if the fmod operation produces such an h value) |
| // then "h += 6.0f" can produce a value of EXACTLY 6.0f, |
| // in which case 'i' will be 6. This edge case is handled by |
| // treating the i=6 case the same as the i=0 case. |
| |
| 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; |
| case 6: r = v; g = t; b = p; break; // see comment above |
| } |
| |
| #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 (); |
| |
| /******************************************************************************/ |
| |
| void DNGIncrementTimerLevel (); |
| |
| int32 DNGDecrementTimerLevel (); |
| |
| /******************************************************************************/ |
| |
| class dng_timer: private dng_uncopyable |
| { |
| |
| public: |
| |
| dng_timer (const char *message); |
| |
| ~dng_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 |
| |
| DNG_ATTRIB_NO_SANITIZE("unsigned-integer-overflow") |
| 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: private dng_uncopyable |
| { |
| |
| 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 (); |
| |
| 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 |
| |
| /*****************************************************************************/ |
| |
| // x is assumed to be in [0,1]. |
| // Result will also be in [0,1]. |
| // |
| // Applies smooth cubic function f(x) such that first and second derivatives |
| // at endpoints are both zero, i.e., f'(x) = 0 and f''(x) = 0 for x = 0 and x |
| // = 1. |
| |
| static inline real64 SmoothStep (real64 x) |
| { |
| |
| return x * x * (3.0 - 2.0 * x); |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| uint32 MinBackwardVersionForCompression (uint32 compression); |
| |
| /*****************************************************************************/ |
| |
| class dng_line_real32 |
| { |
| |
| public: |
| |
| real32 fX; |
| real32 fY; |
| real32 fSlope; |
| |
| public: |
| |
| dng_line_real32 (real32 x0, |
| real32 y0, |
| real32 x1, |
| real32 y1) |
| |
| : fX (x0) |
| , fY (y0) |
| |
| , fSlope ((x0 == x1) ? 0.0f : ((y0 - y1) / (x0 - x1))) |
| |
| { |
| |
| } |
| |
| DNG_ALWAYS_INLINE real32 Evaluate (real32 x) const |
| { |
| |
| return fY + fSlope * (x - fX); |
| |
| } |
| |
| }; |
| |
| /*****************************************************************************/ |
| |
| // Implements ImageSequenceInfo tag introduced in DNG 1.7. |
| |
| class dng_image_sequence_info |
| { |
| |
| public: |
| |
| dng_string fSequenceID; |
| |
| dng_string fSequenceType; |
| |
| dng_string fFrameInfo; |
| |
| uint32 fIndex = 0; |
| |
| uint32 fCount = 0; |
| |
| uint8 fIsFinal = 2; // unknown |
| |
| public: |
| |
| bool IsValid () const |
| { |
| return (fSequenceID .Length () >= 8 && |
| fSequenceType.Length () >= 1); |
| } |
| |
| uint32 TagCount () const |
| { |
| |
| uint32 sum = SafeUint32Add (fSequenceID .Length (), |
| fSequenceType.Length ()); |
| |
| sum = SafeUint32Add (fFrameInfo.Length (), sum); |
| |
| sum = SafeUint32Add (sum, |
| 1 + // null-term for sequence ID |
| 1 + // null-term for sequence type |
| 1 + // null-term for frame info |
| 4 + // index |
| 4 + // count |
| 1); // final |
| |
| return sum; |
| |
| } |
| |
| tiff_tag * MakeTag (dng_memory_allocator &allocator) const; |
| |
| bool operator== (const dng_image_sequence_info &src) const |
| { |
| return (fSequenceID == src.fSequenceID && |
| fSequenceType == src.fSequenceType && |
| fFrameInfo == src.fFrameInfo && |
| fIndex == src.fIndex && |
| fCount == src.fCount && |
| fIsFinal == src.fIsFinal); |
| } |
| |
| }; |
| |
| /*****************************************************************************/ |
| |
| // Implements ImageStats tag introduced in DNG 1.7. |
| |
| class dng_image_stats |
| { |
| |
| public: |
| |
| static constexpr uint32 kTag_WeightedAverage = 1; |
| static constexpr uint32 kTag_WeightedSamples = 2; |
| static constexpr uint32 kTag_Weights = 3; |
| static constexpr uint32 kTag_ColorAverage = 4; |
| static constexpr uint32 kTag_ColorSamples = 5; |
| |
| static constexpr size_t kMaxSamples = 1024; |
| |
| public: |
| |
| struct weighted_sample |
| { |
| |
| real32 fFrac = 0.0f; |
| real32 fValue = 0.0f; |
| |
| bool operator== (const weighted_sample &src) const |
| { |
| return (fFrac == src.fFrac && |
| fValue == src.fValue); |
| } |
| |
| }; |
| |
| struct color_sample |
| { |
| |
| real32 fFrac = 0.0f; |
| |
| std::vector<real32> fValues; |
| |
| bool operator== (const color_sample &src) const |
| { |
| return (fFrac == src.fFrac && |
| fValues == src.fValues); |
| } |
| |
| }; |
| |
| public: |
| |
| // Empty vectors mean invalid/missing. |
| |
| std::vector<real32> fWeightedAverage; |
| |
| std::vector<weighted_sample> fWeightedSamples; |
| |
| std::vector<real32> fWeights; |
| |
| std::vector<real32> fColorAverage; |
| |
| std::vector<color_sample> fColorSamples; |
| |
| public: |
| |
| bool IsValidForPlaneCount (uint32 planeCount) const; |
| |
| uint32 TagCount () const; |
| |
| tiff_tag * MakeTag (dng_memory_allocator &allocator) const; |
| |
| bool operator== (const dng_image_stats &src) const; |
| |
| void Parse (dng_stream &stream); |
| |
| #if qDNGValidate |
| void Dump () const; |
| #endif |
| |
| }; |
| |
| /*****************************************************************************/ |
| |
| DNG_ALWAYS_INLINE real32 EncodeOverrange (real32 x) |
| { |
| |
| x = Max_real32 (x, 0.0f); |
| |
| return x * (256.0f + x) / (256.0f * (1.0f + x)); |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| DNG_ALWAYS_INLINE real32 DecodeOverrange (real32 x) |
| { |
| |
| x = Max_real32 (x, 0.0f); |
| |
| return 16.0f * ((8.0f * x) - 8.0f + sqrtf (64.0f * x * x - 127.0f * x + 64.0f)); |
| |
| } |
| |
| /*****************************************************************************/ |
| |
| #endif // __dng_utils__ |
| |
| /*****************************************************************************/ |