1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
|
/// \file
/// \brief Arithmetic helper functions
/// \ingroup cgraph_utils
#pragma once
#include <assert.h>
#include <float.h>
#include <limits.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
/// comparator for doubles
static inline int fcmp(double a, double b) {
if (a < b) {
return -1;
}
if (a > b) {
return 1;
}
return 0;
}
/// maximum of two integers
static inline int imax(int a, int b) { return a > b ? a : b; }
/// maximum of two sizes
static inline size_t zmax(size_t a, size_t b) { return a > b ? a : b; }
/// minimum of two integers
static inline int imin(int a, int b) { return a < b ? a : b; }
/**
* \brief are two values precisely the same?
*
* This function should only be used when you know you want comparison with no
* tolerance, which is rare. Floating-point arithmetic accumulates imprecision,
* so equality comparisons should generally include a non-zero tolerance to
* account for this. In general, this function is only applicable for checking
* things like “is this variable unchanged since a previous assignment from a
* literal?”
*
* \param a First operand to comparison
* \param b Second operand to comparison
* \return True if the values are equal
*/
static inline bool is_exactly_equal(double a, double b) {
return memcmp(&a, &b, sizeof(a)) == 0;
}
/**
* \brief is a value precisely 0.0?
*
* This function should only be used when you know you want comparison with no
* tolerance, which is rare. Floating-point arithmetic accumulates imprecision,
* so equality comparisons should generally include a non-zero tolerance to
* account for this. Valid `double` representations even include -0.0, for which
* this function will return false. In general, this function is only applicable
* for checking things like “is this variable unchanged since a previous
* assignment from the literal `0`?” or “did this value we parsed from user
* input originate from the string "0.0"?”
*
* \param v Value to check
* \return True if the value is equal to exactly 0.0
*/
static inline bool is_exactly_zero(double v) { return is_exactly_equal(v, 0); }
/**
* \brief scale up or down a non-negative integer, clamping to \p [0, INT_MAX]
*
* \param original Value to scale
* \param scale Scale factor to apply
* \return Clamped result
*/
static inline int scale_clamp(int original, double scale) {
assert(original >= 0);
if (scale < 0) {
return 0;
}
if (scale > 1 && original > INT_MAX / scale) {
return INT_MAX;
}
return (int)(original * scale);
}
/// byte length of data per pixel in image data buffers
enum { BYTES_PER_PIXEL = 4 };
/// in-place conversion of ARGB32 big endian to RGBA32 little endian
///
/// Image data originating from sources like Cairo comes in a 4-byte-per-pixel
/// format ordered {blue, green, red, alpha}. Some output libraries/devices
/// instead consume a 4-byte-per-pixel format ordered {red, green, blue, alpha}.
/// This function converts the former to the latter.
///
/// It is confusing to refer to these formats as ARGB32 and RGBA32 when the
/// first is big endian and the second is little endian, and thus the mappings
/// of letters in the acronym to bytes in memory are opposites. Nevertheless
/// that appears to be what is in common usage.
///
/// @param width Width of the image in pixels
/// @param height Height of the image in pixels
/// @param data [inout] Image data in ARGB32 format on entry and RGBA32 format
/// on exit
static inline void argb2rgba(size_t width, size_t height, unsigned char *data) {
assert(data != NULL || (width == 0 && height == 0));
// indices to color bytes in each format
enum { Ba = 0, Ga = 1, Ra = 2, Aa = 3 };
enum { Rb = 0, Gb = 1, Bb = 2, Ab = 3 };
for (size_t y = 0; y < height; ++y) {
for (size_t x = 0; x < width; ++x) {
const unsigned char red = data[Ra];
const unsigned char blue = data[Ba];
data[Rb] = red;
data[Bb] = blue;
data += BYTES_PER_PIXEL;
}
}
}
/// swap data referenced by two pointers
///
/// You can think of this macro as having the following C type:
///
/// void SWAP(<t1> *a, <t1> *b);
///
/// Both `a` and `b` are expected to be pure expressions.
#define SWAP(a, b) \
do { \
/* trigger a -Wcompare-distinct-pointer-types compiler warning if `a` */ \
/* and `b` have differing types */ \
(void)((a) == (b)); \
\
/* Swap their targets. Contemporary compilers will optimize the `memcpy`s \
* into direct writes for primitive types. \
*/ \
char tmp_[sizeof(*(a))]; \
memcpy(tmp_, (a), sizeof(*(a))); \
*(a) = *(b); \
memcpy((b), tmp_, sizeof(*(b))); \
} while (0)
/// convert a double-precision floating-point to single-precision
///
/// Values below or above the range of representable floats are clamped. There
/// is loss of precision resulting from both this and that single-precision
/// space inherently has less resolution than double-precision space. The caller
/// should verify this loss of precision is acceptable/desirable.
///
/// @param v Double value to convert
/// @return Closest float value
static inline float d2f(double v) {
if (v > FLT_MAX) {
return FLT_MAX;
}
if (v < -FLT_MAX) {
return -FLT_MAX;
}
return (float)v;
}
|