blob: 5452fa2ced1fa617590826add4fc08959c3274c6 (
plain) (
tree)
|
|
/* See LICENSE file for copyright and license details. */
#include "common.h"
/**
* Concatenation of all ramps
*/
#define ALL red
/**
* Preform installation in an `for (i = 0; i < n; i++)`
* loop and do a `break` afterwords
*/
#define TRANSLATE(instruction) for (i = 0; i < n; i++) instruction; break
/**
* Convert a [0, 1] `float` to a full range `uint64_t`
* and mark sure rounding errors does not cause the
* value be 0 instead of ~0 and vice versa
*
* @param value To `float` to convert
* @return The value as an `uint64_t`
*/
static uint64_t
float_to_64(float value)
{
/* TODO Which is faster? */
#if defined(HAVE_INT128) && __WORDSIZE == 64
/* `__int128` is a GNU C extension, which
* (because it is not ISO C) emits a warning
* under -pedantic */
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wpedantic"
/* In GCC we can use `__int128`, this is
* a signed 128-bit integer. It fits all
* uint64_t values but also native values,
* which is a nice because it eleminates
* some overflow condition tests. It is
* also more readable. */
/* Convert to integer */
__int128 product = (__int128)(value * (float)UINT64_MAX);
/* Negative overflow */
if (product > UINT64_MAX)
return UINT64_MAX;
/* Positive overflow */
if (product < 0)
return 0;
/* Did not overflow */
return (uint64_t)product;
# pragma GCC diagnostic pop
#else
/* If we are not using GCC we cannot be
* sure that we have `__int128` so we have
* to use `uint64_t` and perform overflow
* checkes based on the input value */
/* Convert to integer. */
uint64_t product = (uint64_t)(value * (float)UINT64_MAX);
/* Negative overflow,
* if the input is less than 0.5 but
* the output is greater then we got
* -1 when we should have gotten 0 */
if (value < 0.1f && product > 0xF000000000000000ULL)
return 0;
/* Positive overflow,
* if the input is greater than 0.5
* but the output is less then we got
* 0 when we should have gotten ~0 */
else if (value > 0.9f && product < 0x1000000000000000ULL)
return (uint64_t)~0;
/* Did not overflow */
return product;
#endif
}
/**
* Convert a [0, 1] `double` to a full range `uint64_t`
* and mark sure rounding errors does not cause the
* value be 0 instead of ~0 and vice versa
*
* @param value To `double` to convert
* @return The value as an `uint64_t`
*/
static uint64_t
double_to_64(double value)
{
/* TODO Which is faster? */
#if defined(HAVE_INT128) && __WORDSIZE == 64
/* `__int128` is a GNU C extension, which
* (because it is not ISO C) emits a warning
* under -pedantic */
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wpedantic"
/* In GCC we can use `__int128`, this is
* a signed 128-bit integer. It fits all
* uint64_t values but also native values,
* which is a nice because it eleminates
* some overflow condition tests. It is
* also more readable. */
/* Convert to integer */
__int128 product = (__int128)(value * (double)UINT64_MAX);
/* Negative overflow */
if (product > UINT64_MAX)
return UINT64_MAX;
/* Positive overflow */
if (product < 0)
return 0;
/* Did not overflow */
return (uint64_t)product;
# pragma GCC diagnostic pop
#else
/* If we are not using GCC we cannot be
* sure that we have `__int128` so we have
* to use `uint64_t` and perform overflow
* checkes based on the input value. */
/* Convert to integer. */
uint64_t product = (uint64_t)(value * (double)UINT64_MAX);
/* Negative overflow,
* if the input is less than 0.5 but
* the output is greater then we got
* -1 when we should have gotten 0 */
if (value < (double)0.1f && product > 0xF000000000000000ULL)
product = 0;
/* Positive overflow,
* if the input is greater than 0.5
* but the output is less then we got
* 0 when we should have gotten ~0 */
else if (value > (double)0.9f && (product < 0x1000000000000000ULL))
product = (uint64_t)~0;
/* Did not overflow */
return product;
#endif
}
/**
* Convert any set of gamma ramps into a 64-bit integer array with all channels
*
* @param depth The depth of the gamma ramp, `-1` for `float`, `-2` for `double`
* @param n The grand size of gamma ramps (sum of all channels' sizes)
* @param out Output array
* @param in Input gamma ramps
*/
void
libgamma_internal_translate_to_64(signed depth, size_t n, uint64_t *restrict out, const union gamma_ramps_any *restrict in)
{
size_t i;
switch (depth) {
/* Translate integer */
case 8: TRANSLATE(out[i] = (uint64_t)in->bits8. ALL[i] * UINT64_C(0x0101010101010101));
case 16: TRANSLATE(out[i] = (uint64_t)in->bits16.ALL[i] * UINT64_C(0x0001000100010001));
case 32: TRANSLATE(out[i] = (uint64_t)in->bits32.ALL[i] * UINT64_C(0x0000000100000001));
/* Identity translation */
case 64: TRANSLATE(out[i] = in->bits64.ALL[i]);
/* Translate floating point */
case -1: TRANSLATE(out[i] = float_to_64(in->float_single.ALL[i]));
case -2: TRANSLATE(out[i] = double_to_64(in->float_double.ALL[i]));
default:
/* This is not possible */
abort();
break;
}
}
|