Quellcode durchsuchen

WIP

wip/image-kernel
Sam Hocevar vor 2 Monaten
Ursprung
Commit
66fdc2e6eb
11 geänderte Dateien mit 700 neuen und 21 gelöschten Zeilen
  1. +1
    -0
      .gitignore
  2. +18
    -0
      include/lol/image/image
  3. +18
    -0
      include/lol/image/kernel
  4. +0
    -16
      include/lol/private/image/image.h
  5. +59
    -0
      include/lol/private/image/kernel.h
  6. +357
    -0
      include/lol/private/image/kernel.ipp
  7. +29
    -0
      include/lol/private/signal/audio.h
  8. +2
    -2
      include/lol/signal/audio
  9. +10
    -3
      t/Makefile
  10. +18
    -0
      t/base-narray.cpp
  11. +188
    -0
      t/image-kernel.cpp

+ 1
- 0
.gitignore Datei anzeigen

@@ -2,5 +2,6 @@
.*.swp
*~
# Binaries
*.o
*.exe
t/test

+ 18
- 0
include/lol/image/image Datei anzeigen

@@ -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"


+ 18
- 0
include/lol/image/kernel Datei anzeigen

@@ -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"


+ 0
- 16
include/lol/private/image/image.h Datei anzeigen

@@ -142,20 +142,4 @@ enum class ResampleAlgorithm : uint8_t
Bresenham,
};

enum class EdiffAlgorithm : uint8_t
{
FloydSteinberg,
JaJuNi,
Atkinson,
Fan,
ShiauFan,
ShiauFan2,
Stucki,
Burkes,
Sierra,
Sierra2,
Lite,
};

} // namespace lol


+ 59
- 0
include/lol/private/image/kernel.h Datei anzeigen

@@ -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"

+ 357
- 0
include/lol/private/image/kernel.ipp Datei anzeigen

@@ -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

+ 29
- 0
include/lol/private/signal/audio.h Datei anzeigen

@@ -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:
};

include/lol/image → include/lol/signal/audio Datei anzeigen

@@ -1,7 +1,7 @@
//
// 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
// the extent permitted by applicable law. You can redistribute it
@@ -13,6 +13,6 @@
#pragma once

#include "private/push_macros.h"
#include "private/image/image.h"
#include "private/signal/audio.h"
#include "private/pop_macros.h"


+ 10
- 3
t/Makefile Datei anzeigen

@@ -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

@@ -9,5 +13,8 @@ clean:
check: test
./test

test: $(SRC)
$(CXX) -I../include $^ -o $@
test: $(SRC:%.cpp=%.o)
$(CXX) $^ -o $@

%.o: %.cpp
$(CXX) -std=c++23 -I../include -c $^ -o $@

+ 18
- 0
t/base-narray.cpp Datei anzeigen

@@ -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);
}

+ 188
- 0
t/image-kernel.cpp Datei anzeigen

@@ -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);
}
*/

Laden…
Abbrechen
Speichern