@@ -2,5 +2,6 @@ | |||||
.*.swp | .*.swp | ||||
*~ | *~ | ||||
# Binaries | # Binaries | ||||
*.o | |||||
*.exe | *.exe | ||||
t/test | t/test |
@@ -0,0 +1,18 @@ | |||||
// | |||||
// Lol Engine | |||||
// | |||||
// Copyright © 2010–2020 Sam Hocevar <sam@hocevar.net> | |||||
// | |||||
// Lol Engine is free software. It comes without any warranty, to | |||||
// the extent permitted by applicable law. You can redistribute it | |||||
// and/or modify it under the terms of the Do What the Fuck You Want | |||||
// to Public License, Version 2, as published by the WTFPL Task Force. | |||||
// See http://www.wtfpl.net/ for more details. | |||||
// | |||||
#pragma once | |||||
#include "../private/push_macros.h" | |||||
#include "../private/image/image.h" | |||||
#include "../private/pop_macros.h" | |||||
@@ -0,0 +1,18 @@ | |||||
// | |||||
// Lol Engine | |||||
// | |||||
// Copyright © 2010–2024 Sam Hocevar <sam@hocevar.net> | |||||
// | |||||
// Lol Engine is free software. It comes without any warranty, to | |||||
// the extent permitted by applicable law. You can redistribute it | |||||
// and/or modify it under the terms of the Do What the Fuck You Want | |||||
// to Public License, Version 2, as published by the WTFPL Task Force. | |||||
// See http://www.wtfpl.net/ for more details. | |||||
// | |||||
#pragma once | |||||
#include "../private/push_macros.h" | |||||
#include "../private/image/kernel.h" | |||||
#include "../private/pop_macros.h" | |||||
@@ -142,20 +142,4 @@ enum class ResampleAlgorithm : uint8_t | |||||
Bresenham, | Bresenham, | ||||
}; | }; | ||||
enum class EdiffAlgorithm : uint8_t | |||||
{ | |||||
FloydSteinberg, | |||||
JaJuNi, | |||||
Atkinson, | |||||
Fan, | |||||
ShiauFan, | |||||
ShiauFan2, | |||||
Stucki, | |||||
Burkes, | |||||
Sierra, | |||||
Sierra2, | |||||
Lite, | |||||
}; | |||||
} // namespace lol | } // namespace lol | ||||
@@ -0,0 +1,59 @@ | |||||
// | |||||
// Lol Engine | |||||
// | |||||
// Copyright © 2010–2024 Sam Hocevar <sam@hocevar.net> | |||||
// | |||||
// Lol Engine is free software. It comes without any warranty, to | |||||
// the extent permitted by applicable law. You can redistribute it | |||||
// and/or modify it under the terms of the Do What the Fuck You Want | |||||
// to Public License, Version 2, as published by the WTFPL Task Force. | |||||
// See http://www.wtfpl.net/ for more details. | |||||
// | |||||
#pragma once | |||||
// | |||||
// The image class | |||||
// ——————————————— | |||||
// | |||||
#include <lol/narray> // lol::array2d | |||||
#include <lol/vector> // lol::ivec2 | |||||
namespace lol::image | |||||
{ | |||||
enum class EdiffAlgorithm : uint8_t | |||||
{ | |||||
FloydSteinberg, | |||||
JaJuNi, | |||||
Atkinson, | |||||
Fan, | |||||
ShiauFan, | |||||
ShiauFan2, | |||||
Stucki, | |||||
Burkes, | |||||
Sierra, | |||||
Sierra2, | |||||
Lite, | |||||
}; | |||||
struct kernel | |||||
{ | |||||
kernel() = delete; | |||||
static array2d<float> normalize(array2d<float> const &kernel); | |||||
static array2d<float> bayer(ivec2 size); | |||||
static array2d<float> halftone(ivec2 size); | |||||
static array2d<float> blue_noise(ivec2 size, | |||||
ivec2 gsize = ivec2(7, 7)); | |||||
static array2d<float> ediff(EdiffAlgorithm algorithm); | |||||
static array2d<float> gaussian(vec2 radius, | |||||
float angle = 0.f, | |||||
vec2 delta = vec2(0.f, 0.f)); | |||||
}; | |||||
} // namespace lol::image | |||||
#include "kernel.ipp" |
@@ -0,0 +1,357 @@ | |||||
// | |||||
// Lol Engine | |||||
// | |||||
// Copyright © 2004–2024 Sam Hocevar <sam@hocevar.net> | |||||
// | |||||
// Lol Engine is free software. It comes without any warranty, to | |||||
// the extent permitted by applicable law. You can redistribute it | |||||
// and/or modify it under the terms of the Do What the Fuck You Want | |||||
// to Public License, Version 2, as published by the WTFPL Task Force. | |||||
// See http://www.wtfpl.net/ for more details. | |||||
// | |||||
#pragma once | |||||
#include <cmath> // std::cos, std::sin | |||||
#include <cstring> // std::memset | |||||
#include "../math/rand.h" // lol::rand | |||||
#include <lol/format> // std::format | |||||
/* | |||||
* Stock kernels | |||||
*/ | |||||
namespace lol | |||||
{ | |||||
array2d<float> image::kernel::bayer(ivec2 size) | |||||
{ | |||||
array2d<float> ret(size); | |||||
int n = 1; | |||||
while (n < size.x || n < size.y) | |||||
n *= 2; | |||||
for (int j = 0; j < size.y; j++) | |||||
for (int i = 0; i < size.x; i++) | |||||
{ | |||||
int x = 0; | |||||
for (int k = 1, l = n * n / 4; k < n; k *= 2, l /= 4) | |||||
{ | |||||
if ((i & k) && (j & k)) | |||||
x += l; | |||||
else if (i & k) | |||||
x += 3 * l; | |||||
else if (j & k) | |||||
x += 2 * l; | |||||
} | |||||
ret(i, j) = (float)(x + 1) / (n * n + 1); | |||||
} | |||||
return ret; | |||||
} | |||||
array2d<float> image::kernel::halftone(ivec2 size) | |||||
{ | |||||
static const float pi = std::acos(-1.f); | |||||
array2d<float> ret(size); | |||||
for (int y = 0; y < size.y; y++) | |||||
for (int x = 0; x < size.x; x++) | |||||
{ | |||||
float dx = 2.f * (x + 0.02f) / size.x - 0.5f; | |||||
float dy = 2.f * (y + 0.03f) / size.y - 0.5f; | |||||
bool flip = false; | |||||
if (dx > 0.5f) | |||||
{ | |||||
flip = !flip; | |||||
dx -= 1.0f; | |||||
} | |||||
if (dy > 0.5f) | |||||
{ | |||||
flip = !flip; | |||||
dy -= 1.0f; | |||||
} | |||||
/* Using dx²+dy² here creates another interesting halftone. */ | |||||
float r = - std::cos(pi * (dx - dy)) - std::cos(pi * (dx + dy)); | |||||
ret(x, y) = flip ? 10.f - r : r; | |||||
} | |||||
return normalize(ret); | |||||
} | |||||
array2d<float> image::kernel::blue_noise(ivec2 size, ivec2 gsize) | |||||
{ | |||||
float const epsilon = 1.f / (size.x * size.y + 1); | |||||
gsize = lol::min(size, gsize); | |||||
array2d<float> ret(size); | |||||
array2d<vec2> dots(size); | |||||
/* Create a small Gaussian kernel for filtering */ | |||||
array2d<float> gaussian(gsize); | |||||
for (int j = 0; j < gsize.y; ++j) | |||||
for (int i = 0; i < gsize.x; ++i) | |||||
{ | |||||
ivec2 const distance = gsize / 2 - ivec2(i, j); | |||||
gaussian(i, j) = std::exp(-lol::sqlength(distance) | |||||
/ (0.05f * gsize.x * gsize.y)); | |||||
} | |||||
/* Helper function to find voids and clusters */ | |||||
auto setdot = [&] (ivec2 pos, float val) | |||||
{ | |||||
float const delta = val - dots(pos)[0]; | |||||
dots(pos)[0] = val; | |||||
for (int j = 0; j < gsize.y; ++j) | |||||
for (int i = 0; i < gsize.x; ++i) | |||||
dots((pos.x + i - gsize.x / 2 + size.x) % size.x, | |||||
(pos.y + j - gsize.y / 2 + size.y) % size.y) | |||||
[1] += gaussian(i, j) * delta; | |||||
}; | |||||
auto best = [&] (float val, float mul) -> ivec2 | |||||
{ | |||||
float maxval = -(float)(size.x * size.y); | |||||
ivec2 coord(0, 0); | |||||
for (int y = 0; y < size.y; ++y) | |||||
for (int x = 0; x < size.x; ++x) | |||||
{ | |||||
if (dots(x, y)[0] != val) | |||||
continue; | |||||
float total = dots(x, y)[1]; | |||||
if (total * mul > maxval) | |||||
{ | |||||
maxval = total * mul; | |||||
coord = ivec2(x, y); | |||||
} | |||||
} | |||||
return coord; | |||||
}; | |||||
/* Generate an array with about 10% random dots */ | |||||
int const ndots = (size.x * size.y + 9) / 10; | |||||
std::memset((void *)dots.data(), 0, dots.bytes()); | |||||
for (int n = 0; n < ndots; ) | |||||
{ | |||||
ivec2 pos(lol::rand(size.x), lol::rand(size.y)); | |||||
if (dots(pos)[0]) | |||||
continue; | |||||
setdot(ivec2(pos), 1.0f); | |||||
++n; | |||||
} | |||||
/* Rearrange 1s so that they occupy the largest voids */ | |||||
for (;;) | |||||
{ | |||||
ivec2 bestcluster = best(1.0f, 1.0f); | |||||
setdot(bestcluster, 0.0f); | |||||
ivec2 bestvoid = best(0.0f, -1.0f); | |||||
setdot(bestvoid, 1.0f); | |||||
if (bestcluster == bestvoid) | |||||
break; | |||||
} | |||||
/* Reorder all 1s and replace them with 0.0001 */ | |||||
for (int n = ndots; n--; ) | |||||
{ | |||||
ivec2 bestcluster = best(1.0f, 1.0f); | |||||
ret(bestcluster) = (n + 1.0f) * epsilon; | |||||
setdot(bestcluster, 0.0001f); | |||||
} | |||||
/* Reorder all 0s and replace them with 0.0001 */ | |||||
for (int n = ndots; n < size.x * size.y; ++n) | |||||
{ | |||||
ivec2 bestvoid = best(0.0f, -1.0f); | |||||
ret(bestvoid) = (n + 1.0f) * epsilon; | |||||
setdot(bestvoid, 0.0001f); | |||||
} | |||||
return ret; | |||||
} | |||||
struct Dot | |||||
{ | |||||
int x, y; | |||||
float val; | |||||
}; | |||||
static int cmpdot(const void *p1, const void *p2) | |||||
{ | |||||
return ((Dot const *)p1)->val > ((Dot const *)p2)->val; | |||||
} | |||||
array2d<float> image::kernel::normalize(array2d<float> const &kernel) | |||||
{ | |||||
ivec2 size = kernel.sizes(); | |||||
std::vector<Dot> tmp; | |||||
tmp.resize(size.x * size.y); | |||||
for (int y = 0; y < size.y; y++) | |||||
for (int x = 0; x < size.x; x++) | |||||
{ | |||||
tmp[y * size.x + x].x = x; | |||||
tmp[y * size.x + x].y = y; | |||||
tmp[y * size.x + x].val = kernel(y, x); | |||||
} | |||||
// FIXME: get rid of qsort and use proper C++ algorithms | |||||
std::qsort(tmp.data(), size.x * size.y, sizeof(Dot), cmpdot); | |||||
array2d<float> dst(size); | |||||
float const epsilon = 1.f / (size.x * size.y + 1); | |||||
for (int n = 0; n < size.x * size.y; n++) | |||||
{ | |||||
int x = tmp[n].x; | |||||
int y = tmp[n].y; | |||||
dst(x, y) = (n + 1.f) * epsilon; | |||||
} | |||||
return dst; | |||||
} | |||||
array2d<float> image::kernel::ediff(EdiffAlgorithm algorithm) | |||||
{ | |||||
switch (algorithm) | |||||
{ | |||||
case EdiffAlgorithm::FloydSteinberg: | |||||
return { { 0.f, 1.f, 7.f/16, }, | |||||
{ 3.f/16, 5.f/16, 1.f/16, }, }; | |||||
case EdiffAlgorithm::JaJuNi: | |||||
return { { 0.f, 0.f, 1.f, 7.f/48, 5.f/48, }, | |||||
{ 3.f/48, 5.f/48, 7.f/48, 5.f/48, 3.f/48, }, | |||||
{ 1.f/48, 3.f/48, 5.f/48, 3.f/48, 1.f/48, }, }; | |||||
case EdiffAlgorithm::Atkinson: | |||||
return { { 0.f, 1.f, 1.f/8, 1.f/8, }, | |||||
{ 1.f/8, 1.f/8, 1.f/8, 0.f, }, | |||||
{ 0.f, 1.f/8, 0.f, 0.f, }, }; | |||||
case EdiffAlgorithm::Fan: | |||||
return { { 0.f, 0.f, 1.f, 7.f/16, }, | |||||
{ 1.f/16, 3.f/16, 5.f/16, 0.f, }, }; | |||||
case EdiffAlgorithm::ShiauFan: | |||||
return { { 0.f, 0.f, 1.f, 1.f/2, }, | |||||
{ 1.f/8, 1.f/8, 1.f/4, 0.f, }, }; | |||||
case EdiffAlgorithm::ShiauFan2: | |||||
return { { 0.f, 0.f, 0.f, 1.f, 1.f/2, }, | |||||
{ 1.f/16, 1.f/16, 1.f/8, 1.f/4, 0.f, }, }; | |||||
case EdiffAlgorithm::Stucki: | |||||
return { { 0.f, 0.f, 1.f, 8.f/42, 4.f/42, }, | |||||
{ 2.f/42, 4.f/42, 8.f/42, 4.f/42, 2.f/42, }, | |||||
{ 1.f/42, 2.f/42, 4.f/42, 2.f/42, 1.f/42, }, }; | |||||
case EdiffAlgorithm::Burkes: | |||||
return { { 0.f, 0.f, 1.f, 4.f/16, 2.f/16, }, | |||||
{ 1.f/16, 2.f/16, 4.f/16, 2.f/16, 1.f/16, }, }; | |||||
case EdiffAlgorithm::Sierra: | |||||
return { { 0.f, 0.f, 1.f, 5.f/32, 3.f/32, }, | |||||
{ 2.f/32, 4.f/32, 5.f/32, 4.f/32, 2.f/32, }, | |||||
{ 0.f, 2.f/32, 3.f/32, 2.f/32, 0.f, }, }; | |||||
case EdiffAlgorithm::Sierra2: | |||||
return { { 0.f, 0.f, 1.f, 4.f/16, 3.f/16, }, | |||||
{ 1.f/16, 2.f/16, 3.f/16, 2.f/16, 1.f/16, }, }; | |||||
case EdiffAlgorithm::Lite: | |||||
return { { 0.f, 1.f, 1.f/2, }, | |||||
{ 1.f/4, 1.f/4, 0.f, }, }; | |||||
} | |||||
return { { 1.f } }; | |||||
} | |||||
/* Any standard deviation below this value will be rounded up, in order | |||||
* to avoid ridiculously low values. exp(-1/(2*0.2*0.2)) is < 10^-5 so | |||||
* there is little chance that any value below 0.2 will be useful. */ | |||||
#define BLUR_EPSILON 0.2f | |||||
array2d<float> image::kernel::gaussian(vec2 radius, float angle, vec2 delta) | |||||
{ | |||||
array2d<float> kernel; | |||||
if (radius.x < BLUR_EPSILON) | |||||
radius.x = BLUR_EPSILON; | |||||
if (radius.y < BLUR_EPSILON) | |||||
radius.y = BLUR_EPSILON; | |||||
float const sint = std::sin(angle); | |||||
float const cost = std::cos(angle); | |||||
// Compute the final ellipse's bounding box | |||||
float const bbx = std::sqrt(std::pow(radius.x * cost, 2.f) + std::pow(radius.y * sint, 2.f)); | |||||
float const bby = std::sqrt(std::pow(radius.y * cost, 2.f) + std::pow(radius.x * sint, 2.f)); | |||||
/* FIXME: the kernel becomes far too big with large values of dx, because | |||||
* we grow both left and right. Fix the growing direction. */ | |||||
int const krx = (int)(3.f * bbx + .99999f + std::ceil(std::fabs(delta.x))); | |||||
int const kry = (int)(3.f * bby + .99999f + std::ceil(std::fabs(delta.y))); | |||||
ivec2 size(2 * krx + 1, 2 * kry + 1); | |||||
float const Kx = -1.f / (2.f * radius.x * radius.x); | |||||
float const Ky = -1.f / (2.f * radius.y * radius.y); | |||||
kernel.resize(size); | |||||
float t = 0.f; | |||||
for (int j = -kry; j <= kry; j++) | |||||
{ | |||||
for (int i = -krx; i <= krx; i++) | |||||
{ | |||||
/* FIXME: this level of interpolation sucks. We should | |||||
* interpolate on the full NxN grid for better quality. */ | |||||
static vec3 const samples[] = | |||||
{ | |||||
vec3( 0.0f, 0.0f, 1.0f), | |||||
vec3(-0.4f, -0.4f, 0.8f), | |||||
vec3(-0.3f, 0.0f, 0.9f), | |||||
vec3(-0.4f, 0.4f, 0.8f), | |||||
vec3( 0.0f, 0.3f, 0.9f), | |||||
vec3( 0.4f, 0.4f, 0.8f), | |||||
vec3( 0.3f, 0.0f, 0.9f), | |||||
vec3( 0.4f, -0.4f, 0.8f), | |||||
vec3( 0.0f, -0.3f, 0.9f), | |||||
}; | |||||
float d = 0.f; | |||||
for (auto p : samples) | |||||
{ | |||||
float u = (i + p.x) * cost - (j + p.y) * sint + delta.x; | |||||
float v = (i + p.x) * sint + (j + p.y) * cost + delta.y; | |||||
float ex = Kx * u * u; | |||||
float ey = Ky * v * v; | |||||
d += p.z * std::exp(ex + ey); | |||||
/* Do not interpolate if this is a standard gaussian. */ | |||||
if (!delta.x && !delta.y && !angle) | |||||
break; | |||||
} | |||||
kernel(i + krx, j + kry) = d; | |||||
t += d; | |||||
} | |||||
} | |||||
for (int j = 0; j < size.y; j++) | |||||
for (int i = 0; i < size.x; i++) | |||||
kernel(i, j) *= (1.f / t); | |||||
return kernel; | |||||
} | |||||
} // namespace lol |
@@ -0,0 +1,29 @@ | |||||
// | |||||
// Lol Engine | |||||
// | |||||
// Copyright © 2010—2024 Sam Hocevar <sam@hocevar.net> | |||||
// | |||||
// Lol Engine is free software. It comes without any warranty, to | |||||
// the extent permitted by applicable law. You can redistribute it | |||||
// and/or modify it under the terms of the Do What the Fuck You Want | |||||
// to Public License, Version 2, as published by the WTFPL Task Force. | |||||
// See http://www.wtfpl.net/ for more details. | |||||
// | |||||
#pragma once | |||||
// | |||||
// The audio classes | |||||
// ————————————————— | |||||
// | |||||
namespace lol | |||||
{ | |||||
template<typename FROM, typename TO> | |||||
class converter | |||||
{ | |||||
public: | |||||
protected: | |||||
}; |
@@ -1,7 +1,7 @@ | |||||
// | // | ||||
// Lol Engine | // Lol Engine | ||||
// | // | ||||
// Copyright © 2010—2020 Sam Hocevar <sam@hocevar.net> | |||||
// Copyright © 2010—2024 Sam Hocevar <sam@hocevar.net> | |||||
// | // | ||||
// Lol Engine is free software. It comes without any warranty, to | // Lol Engine is free software. It comes without any warranty, to | ||||
// the extent permitted by applicable law. You can redistribute it | // the extent permitted by applicable law. You can redistribute it | ||||
@@ -13,6 +13,6 @@ | |||||
#pragma once | #pragma once | ||||
#include "private/push_macros.h" | #include "private/push_macros.h" | ||||
#include "private/image/image.h" | |||||
#include "private/signal/audio.h" | |||||
#include "private/pop_macros.h" | #include "private/pop_macros.h" | ||||
@@ -1,5 +1,9 @@ | |||||
SRC = test.cpp audio-convert.cpp audio-sadd.cpp | |||||
SRC = \ | |||||
test.cpp \ | |||||
audio-convert.cpp audio-sadd.cpp \ | |||||
base-narray.cpp \ | |||||
image-kernel.cpp | |||||
all: test | all: test | ||||
@@ -9,5 +13,8 @@ clean: | |||||
check: test | check: test | ||||
./test | ./test | ||||
test: $(SRC) | |||||
$(CXX) -I../include $^ -o $@ | |||||
test: $(SRC:%.cpp=%.o) | |||||
$(CXX) $^ -o $@ | |||||
%.o: %.cpp | |||||
$(CXX) -std=c++23 -I../include -c $^ -o $@ |
@@ -0,0 +1,18 @@ | |||||
#include <lol/lib/doctest> | |||||
#include <lol/narray> | |||||
TEST_CASE("array2d") | |||||
{ | |||||
auto a = lol::array2d<int>(3, 2); | |||||
for (int n = 0; n < 6; ++n) | |||||
a[n] = n; | |||||
CHECK(a(0, 0) == 0); | |||||
CHECK(a(1, 0) == 1); | |||||
CHECK(a(2, 0) == 2); | |||||
CHECK(a(0, 1) == 3); | |||||
CHECK(a(1, 1) == 4); | |||||
CHECK(a(2, 1) == 5); | |||||
} |
@@ -0,0 +1,188 @@ | |||||
#include <lol/image/kernel> | |||||
TEST_CASE("bayer kernel") | |||||
{ | |||||
auto &k = image::kernel::bayer({4, 4}); | |||||
} | |||||
/* | |||||
CHECK | |||||
// Underflow | |||||
CHECK(lol::audio::sample::sadd<int8_t>(-0x80, -0x80) == -0x80); | |||||
CHECK(lol::audio::sample::sadd<int8_t>(-0x41, -0x41) == -0x80); | |||||
CHECK(lol::audio::sample::sadd<int8_t>(-0x40, -0x41) == -0x80); | |||||
// Standard operating mode | |||||
CHECK(lol::audio::sample::sadd<int8_t>(-0x40, -0x40) == -0x80); | |||||
CHECK(lol::audio::sample::sadd<int8_t>(-0x3f, -0x3f) == -0x7e); | |||||
CHECK(lol::audio::sample::sadd<int8_t>(-0x01, -0x01) == -0x02); | |||||
CHECK(lol::audio::sample::sadd<int8_t>(-0x01, 0x00) == -0x01); | |||||
CHECK(lol::audio::sample::sadd<int8_t>( 0x00, 0x00) == 0x00); | |||||
CHECK(lol::audio::sample::sadd<int8_t>( 0x00, 0x01) == 0x01); | |||||
CHECK(lol::audio::sample::sadd<int8_t>( 0x01, 0x01) == 0x02); | |||||
CHECK(lol::audio::sample::sadd<int8_t>( 0x3f, 0x3f) == 0x7e); | |||||
CHECK(lol::audio::sample::sadd<int8_t>( 0x3f, 0x40) == 0x7f); | |||||
// Overflow | |||||
CHECK(lol::audio::sample::sadd<int8_t>( 0x40, 0x40) == 0x7f); | |||||
CHECK(lol::audio::sample::sadd<int8_t>( 0x7f, 0x7f) == 0x7f); | |||||
} | |||||
TEST_CASE("sample saturated add: uint8_t") | |||||
{ | |||||
// Underflow | |||||
CHECK(lol::audio::sample::sadd<uint8_t>(0x00, 0x00) == 0x00); | |||||
CHECK(lol::audio::sample::sadd<uint8_t>(0x3f, 0x3f) == 0x00); | |||||
// Standard operating mode | |||||
CHECK(lol::audio::sample::sadd<uint8_t>(0x40, 0x40) == 0x00); | |||||
CHECK(lol::audio::sample::sadd<uint8_t>(0x41, 0x41) == 0x02); | |||||
CHECK(lol::audio::sample::sadd<uint8_t>(0x7f, 0x7f) == 0x7e); | |||||
CHECK(lol::audio::sample::sadd<uint8_t>(0x7f, 0x80) == 0x7f); | |||||
CHECK(lol::audio::sample::sadd<uint8_t>(0x80, 0x80) == 0x80); | |||||
CHECK(lol::audio::sample::sadd<uint8_t>(0x80, 0x81) == 0x81); | |||||
CHECK(lol::audio::sample::sadd<uint8_t>(0x81, 0x81) == 0x82); | |||||
CHECK(lol::audio::sample::sadd<uint8_t>(0xbf, 0xbf) == 0xfe); | |||||
CHECK(lol::audio::sample::sadd<uint8_t>(0xbf, 0xc0) == 0xff); | |||||
// Overflow | |||||
CHECK(lol::audio::sample::sadd<uint8_t>(0xc0, 0xc0) == 0xff); | |||||
CHECK(lol::audio::sample::sadd<uint8_t>(0xff, 0xff) == 0xff); | |||||
} | |||||
TEST_CASE("sample saturated add: int16_t") | |||||
{ | |||||
// Underflow | |||||
CHECK(lol::audio::sample::sadd<int16_t>(-0x8000, -0x8000) == -0x8000); | |||||
CHECK(lol::audio::sample::sadd<int16_t>(-0x4001, -0x4001) == -0x8000); | |||||
CHECK(lol::audio::sample::sadd<int16_t>(-0x4000, -0x4001) == -0x8000); | |||||
// Standard operating mode | |||||
CHECK(lol::audio::sample::sadd<int16_t>(-0x4000, -0x4000) == -0x8000); | |||||
CHECK(lol::audio::sample::sadd<int16_t>(-0x3fff, -0x3fff) == -0x7ffe); | |||||
CHECK(lol::audio::sample::sadd<int16_t>(-0x0001, -0x0001) == -0x0002); | |||||
CHECK(lol::audio::sample::sadd<int16_t>(-0x0001, 0x0000) == -0x0001); | |||||
CHECK(lol::audio::sample::sadd<int16_t>( 0x0000, 0x0000) == 0x0000); | |||||
CHECK(lol::audio::sample::sadd<int16_t>( 0x0000, 0x0001) == 0x0001); | |||||
CHECK(lol::audio::sample::sadd<int16_t>( 0x0001, 0x0001) == 0x0002); | |||||
CHECK(lol::audio::sample::sadd<int16_t>( 0x3fff, 0x3fff) == 0x7ffe); | |||||
CHECK(lol::audio::sample::sadd<int16_t>( 0x3fff, 0x4000) == 0x7fff); | |||||
// Overflow | |||||
CHECK(lol::audio::sample::sadd<int16_t>( 0x4000, 0x4000) == 0x7fff); | |||||
CHECK(lol::audio::sample::sadd<int16_t>( 0x7fff, 0x7fff) == 0x7fff); | |||||
} | |||||
TEST_CASE("sample saturated add: uint16_t") | |||||
{ | |||||
// Underflow | |||||
CHECK(lol::audio::sample::sadd<uint16_t>(0x0000, 0x0000) == 0x0000); | |||||
CHECK(lol::audio::sample::sadd<uint16_t>(0x3fff, 0x3fff) == 0x0000); | |||||
// Standard operating mode | |||||
CHECK(lol::audio::sample::sadd<uint16_t>(0x4000, 0x4000) == 0x0000); | |||||
CHECK(lol::audio::sample::sadd<uint16_t>(0x4001, 0x4001) == 0x0002); | |||||
CHECK(lol::audio::sample::sadd<uint16_t>(0x7fff, 0x7fff) == 0x7ffe); | |||||
CHECK(lol::audio::sample::sadd<uint16_t>(0x7fff, 0x8000) == 0x7fff); | |||||
CHECK(lol::audio::sample::sadd<uint16_t>(0x8000, 0x8000) == 0x8000); | |||||
CHECK(lol::audio::sample::sadd<uint16_t>(0x8000, 0x8001) == 0x8001); | |||||
CHECK(lol::audio::sample::sadd<uint16_t>(0x8001, 0x8001) == 0x8002); | |||||
CHECK(lol::audio::sample::sadd<uint16_t>(0xbfff, 0xbfff) == 0xfffe); | |||||
CHECK(lol::audio::sample::sadd<uint16_t>(0xbfff, 0xc000) == 0xffff); | |||||
// Overflow | |||||
CHECK(lol::audio::sample::sadd<uint16_t>(0xc000, 0xc000) == 0xffff); | |||||
CHECK(lol::audio::sample::sadd<uint16_t>(0xffff, 0xffff) == 0xffff); | |||||
} | |||||
TEST_CASE("sample saturated add: uint32_t") | |||||
{ | |||||
// Underflow | |||||
CHECK(lol::audio::sample::sadd<uint32_t>(0x00000000, 0x00000000) == 0x00000000); | |||||
CHECK(lol::audio::sample::sadd<uint32_t>(0x3fffffff, 0x3fffffff) == 0x00000000); | |||||
// Standard operating mode | |||||
CHECK(lol::audio::sample::sadd<uint32_t>(0x40000000, 0x40000000) == 0x00000000); | |||||
CHECK(lol::audio::sample::sadd<uint32_t>(0x40000001, 0x40000001) == 0x00000002); | |||||
CHECK(lol::audio::sample::sadd<uint32_t>(0x7fffffff, 0x7fffffff) == 0x7ffffffe); | |||||
CHECK(lol::audio::sample::sadd<uint32_t>(0x7fffffff, 0x80000000) == 0x7fffffff); | |||||
CHECK(lol::audio::sample::sadd<uint32_t>(0x80000000, 0x80000000) == 0x80000000); | |||||
CHECK(lol::audio::sample::sadd<uint32_t>(0x80000000, 0x80000001) == 0x80000001); | |||||
CHECK(lol::audio::sample::sadd<uint32_t>(0x80000001, 0x80000001) == 0x80000002); | |||||
CHECK(lol::audio::sample::sadd<uint32_t>(0xbfffffff, 0xbfffffff) == 0xfffffffe); | |||||
CHECK(lol::audio::sample::sadd<uint32_t>(0xbfffffff, 0xc0000000) == 0xffffffff); | |||||
// Overflow | |||||
CHECK(lol::audio::sample::sadd<uint32_t>(0xc0000000, 0xc0000000) == 0xffffffff); | |||||
CHECK(lol::audio::sample::sadd<uint32_t>(0xffffffff, 0xffffffff) == 0xffffffff); | |||||
} | |||||
TEST_CASE("sample saturated add: int32_t") | |||||
{ | |||||
// Underflow | |||||
CHECK(lol::audio::sample::sadd<int32_t>(-0x80000000, -0x80000000) == -0x80000000); | |||||
CHECK(lol::audio::sample::sadd<int32_t>(-0x40000001, -0x40000001) == -0x80000000); | |||||
CHECK(lol::audio::sample::sadd<int32_t>(-0x40000000, -0x40000001) == -0x80000000); | |||||
// Standard operating mode | |||||
CHECK(lol::audio::sample::sadd<int32_t>(-0x40000000, -0x40000000) == -0x80000000); | |||||
CHECK(lol::audio::sample::sadd<int32_t>(-0x3fffffff, -0x3fffffff) == -0x7ffffffe); | |||||
CHECK(lol::audio::sample::sadd<int32_t>(-0x00000001, -0x00000001) == -0x00000002); | |||||
CHECK(lol::audio::sample::sadd<int32_t>(-0x00000001, 0x00000000) == -0x00000001); | |||||
CHECK(lol::audio::sample::sadd<int32_t>( 0x00000000, 0x00000000) == 0x00000000); | |||||
CHECK(lol::audio::sample::sadd<int32_t>( 0x00000000, 0x00000001) == 0x00000001); | |||||
CHECK(lol::audio::sample::sadd<int32_t>( 0x00000001, 0x00000001) == 0x00000002); | |||||
CHECK(lol::audio::sample::sadd<int32_t>( 0x3fffffff, 0x3fffffff) == 0x7ffffffe); | |||||
CHECK(lol::audio::sample::sadd<int32_t>( 0x3fffffff, 0x40000000) == 0x7fffffff); | |||||
// Overflow | |||||
CHECK(lol::audio::sample::sadd<int32_t>( 0x40000000, 0x40000000) == 0x7fffffff); | |||||
CHECK(lol::audio::sample::sadd<int32_t>( 0x7fffffff, 0x7fffffff) == 0x7fffffff); | |||||
} | |||||
TEST_CASE("sample saturated add: uint64_t") | |||||
{ | |||||
// Underflow | |||||
CHECK(lol::audio::sample::sadd<uint64_t>(0x0000000000000000, 0x0000000000000000) == 0x0000000000000000); | |||||
CHECK(lol::audio::sample::sadd<uint64_t>(0x3fffffffffffffff, 0x3fffffffffffffff) == 0x0000000000000000); | |||||
// Standard operating mode | |||||
CHECK(lol::audio::sample::sadd<uint64_t>(0x4000000000000000, 0x4000000000000000) == 0x0000000000000000); | |||||
CHECK(lol::audio::sample::sadd<uint64_t>(0x4000000000000001, 0x4000000000000001) == 0x0000000000000002); | |||||
CHECK(lol::audio::sample::sadd<uint64_t>(0x7fffffffffffffff, 0x7fffffffffffffff) == 0x7ffffffffffffffe); | |||||
CHECK(lol::audio::sample::sadd<uint64_t>(0x7fffffffffffffff, 0x8000000000000000) == 0x7fffffffffffffff); | |||||
CHECK(lol::audio::sample::sadd<uint64_t>(0x8000000000000000, 0x8000000000000000) == 0x8000000000000000); | |||||
CHECK(lol::audio::sample::sadd<uint64_t>(0x8000000000000000, 0x8000000000000001) == 0x8000000000000001); | |||||
CHECK(lol::audio::sample::sadd<uint64_t>(0x8000000000000001, 0x8000000000000001) == 0x8000000000000002); | |||||
CHECK(lol::audio::sample::sadd<uint64_t>(0xbfffffffffffffff, 0xbfffffffffffffff) == 0xfffffffffffffffe); | |||||
CHECK(lol::audio::sample::sadd<uint64_t>(0xbfffffffffffffff, 0xc000000000000000) == 0xffffffffffffffff); | |||||
// Overflow | |||||
CHECK(lol::audio::sample::sadd<uint64_t>(0xc000000000000000, 0xc000000000000000) == 0xffffffffffffffff); | |||||
CHECK(lol::audio::sample::sadd<uint64_t>(0xffffffffffffffff, 0xffffffffffffffff) == 0xffffffffffffffff); | |||||
} | |||||
TEST_CASE("sample saturated add: int64_t") | |||||
{ | |||||
// Underflow | |||||
CHECK(lol::audio::sample::sadd<int64_t>(-0x8000000000000000, -0x8000000000000000) == -0x8000000000000000); | |||||
CHECK(lol::audio::sample::sadd<int64_t>(-0x4000000000000001, -0x4000000000000001) == -0x8000000000000000); | |||||
CHECK(lol::audio::sample::sadd<int64_t>(-0x4000000000000000, -0x4000000000000001) == -0x8000000000000000); | |||||
// Standard operating mode | |||||
CHECK(lol::audio::sample::sadd<int64_t>(-0x4000000000000000, -0x4000000000000000) == -0x8000000000000000); | |||||
CHECK(lol::audio::sample::sadd<int64_t>(-0x3fffffffffffffff, -0x3fffffffffffffff) == -0x7ffffffffffffffe); | |||||
CHECK(lol::audio::sample::sadd<int64_t>(-0x0000000000000001, -0x0000000000000001) == -0x0000000000000002); | |||||
CHECK(lol::audio::sample::sadd<int64_t>(-0x0000000000000001, 0x0000000000000000) == -0x0000000000000001); | |||||
CHECK(lol::audio::sample::sadd<int64_t>( 0x0000000000000000, 0x0000000000000000) == 0x0000000000000000); | |||||
CHECK(lol::audio::sample::sadd<int64_t>( 0x0000000000000000, 0x0000000000000001) == 0x0000000000000001); | |||||
CHECK(lol::audio::sample::sadd<int64_t>( 0x0000000000000001, 0x0000000000000001) == 0x0000000000000002); | |||||
CHECK(lol::audio::sample::sadd<int64_t>( 0x3fffffffffffffff, 0x3fffffffffffffff) == 0x7ffffffffffffffe); | |||||
CHECK(lol::audio::sample::sadd<int64_t>( 0x3fffffffffffffff, 0x4000000000000000) == 0x7fffffffffffffff); | |||||
// Overflow | |||||
CHECK(lol::audio::sample::sadd<int64_t>( 0x4000000000000000, 0x4000000000000000) == 0x7fffffffffffffff); | |||||
CHECK(lol::audio::sample::sadd<int64_t>( 0x7fffffffffffffff, 0x7fffffffffffffff) == 0x7fffffffffffffff); | |||||
} | |||||
*/ |