|
|
@@ -15,7 +15,7 @@ |
|
|
|
#include "core.h" |
|
|
|
|
|
|
|
/* |
|
|
|
* Stock images |
|
|
|
* Stock images and kernels |
|
|
|
*/ |
|
|
|
|
|
|
|
namespace lol |
|
|
@@ -23,50 +23,136 @@ namespace lol |
|
|
|
|
|
|
|
bool Image::Stock(char const *name) |
|
|
|
{ |
|
|
|
/* Generate a Bayer dithering pattern. */ |
|
|
|
if (!strncmp(name, "bayer:", 6)) |
|
|
|
/* Generate an error diffusion matrix. */ |
|
|
|
if (!strncmp(name, "ediff:", 6)) |
|
|
|
{ |
|
|
|
float const *ker = nullptr; |
|
|
|
ivec2 size(0); |
|
|
|
size.x = atoi(name + 6); |
|
|
|
name = strchr(name + 6, 'x'); |
|
|
|
if (name) |
|
|
|
size.y = atoi(name + 1); |
|
|
|
if (!size.y) |
|
|
|
size.y = size.x; |
|
|
|
|
|
|
|
return RenderBayer(size); |
|
|
|
|
|
|
|
SetSize(size); |
|
|
|
float *pixels = Lock<PixelFormat::Y_F32>(); |
|
|
|
memcpy(pixels, ker, size.x * size.y * sizeof(float)); |
|
|
|
Unlock(pixels); |
|
|
|
|
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
/* Generate a clustered dithering pattern. */ |
|
|
|
if (!strncmp(name, "halftone:", 9)) |
|
|
|
/* Generate a completely random image. */ |
|
|
|
if (!strncmp(name, "random:", 7)) |
|
|
|
{ |
|
|
|
ivec2 size(0); |
|
|
|
size.x = atoi(name + 9); |
|
|
|
name = strchr(name + 9, 'x'); |
|
|
|
|
|
|
|
size.x = atoi(name + 7); |
|
|
|
name = strchr(name + 7, 'x'); |
|
|
|
if (name) |
|
|
|
size.y = atoi(name + 1); |
|
|
|
if (!size.y) |
|
|
|
size.y = size.x; |
|
|
|
if (size.x <= 0 || size.y <= 0) |
|
|
|
return false; |
|
|
|
|
|
|
|
return RenderHalftone(size); |
|
|
|
return RenderRandom(size); |
|
|
|
} |
|
|
|
|
|
|
|
/* Generate an error diffusion matrix. */ |
|
|
|
if (!strncmp(name, "ediff:", 6)) |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
Array2D<float> Image::BayerKernel(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; |
|
|
|
} |
|
|
|
|
|
|
|
struct Dot |
|
|
|
{ |
|
|
|
int x, y; |
|
|
|
float dist; |
|
|
|
}; |
|
|
|
|
|
|
|
static int cmpdot(const void *p1, const void *p2) |
|
|
|
{ |
|
|
|
return ((Dot const *)p1)->dist > ((Dot const *)p2)->dist; |
|
|
|
} |
|
|
|
|
|
|
|
Array2D<float> Image::HalftoneKernel(ivec2 size) |
|
|
|
{ |
|
|
|
Array2D<float> ret(size); |
|
|
|
|
|
|
|
ivec2 csize = (size + ivec2(1)) / 2; |
|
|
|
Array<Dot> circle; |
|
|
|
circle.Resize(csize.x * csize.y); |
|
|
|
for (int y = 0; y < csize.y; y++) |
|
|
|
for (int x = 0; x < csize.x; x++) |
|
|
|
{ |
|
|
|
float dy = ((float)y + 0.07f) / csize.y - 0.5f; |
|
|
|
float dx = (float)x / csize.x - 0.5f; |
|
|
|
/* Using dx²+dy² here creates another interesting halftone. */ |
|
|
|
float r = - lol::cos(F_PI * (dx - dy)) - lol::cos(F_PI * (dx + dy)); |
|
|
|
circle[y * csize.x + x].x = x; |
|
|
|
circle[y * csize.x + x].y = y; |
|
|
|
circle[y * csize.x + x].dist = r; |
|
|
|
} |
|
|
|
/* FIXME: use std::sort here */ |
|
|
|
std::qsort(circle.Data(), csize.x * csize.y, sizeof(Dot), cmpdot); |
|
|
|
|
|
|
|
float mul = 1.f / (csize.x * csize.y * 4 + 1); |
|
|
|
for (int n = 0; n < csize.x * csize.y; n++) |
|
|
|
{ |
|
|
|
float const *ker = nullptr; |
|
|
|
ivec2 size(0); |
|
|
|
int x = circle[n].x; |
|
|
|
int y = circle[n].y; |
|
|
|
|
|
|
|
ret[x][y] = (float)(2 * n + 1) * mul; |
|
|
|
ret[x + csize.x][y + csize.y] = (float)(2 * n + 2) * mul; |
|
|
|
ret[x][y + csize.y] = 1.0f - (float)(2 * n + 1) * mul; |
|
|
|
ret[x + csize.x][y] = 1.0f - (float)(2 * n + 2) * mul; |
|
|
|
} |
|
|
|
|
|
|
|
if (!strcmp(name + 6, "fs")) |
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
|
Array2D<float> EdiffKernel(EdiffAlgorithm algorithm) |
|
|
|
{ |
|
|
|
Array2D<float> ret; |
|
|
|
|
|
|
|
switch(algorithm) |
|
|
|
{ |
|
|
|
case EdiffAlgorithm::FloydSteinberg: |
|
|
|
ret.SetSize(ivec2(3, 2)); |
|
|
|
{ |
|
|
|
static float const myker[] = |
|
|
|
{ |
|
|
|
0., 1., 7./16, |
|
|
|
3./16, 5./16, 1./16, |
|
|
|
}; |
|
|
|
ker = myker; size = ivec2(3, 2); |
|
|
|
memcpy(&ret[0][0], myker, sizeof(myker)); |
|
|
|
} |
|
|
|
else if (!strcmp(name + 6, "jajuni")) |
|
|
|
return ret; |
|
|
|
case EdiffAlgorithm::JaJuNi: |
|
|
|
ret.SetSize(ivec2(5, 3)); |
|
|
|
{ |
|
|
|
static float const myker[] = |
|
|
|
{ |
|
|
@@ -74,9 +160,11 @@ bool Image::Stock(char const *name) |
|
|
|
3./48, 5./48, 7./48, 5./48, 3./48, |
|
|
|
1./48, 3./48, 5./48, 3./48, 1./48, |
|
|
|
}; |
|
|
|
ker = myker; size = ivec2(5, 3); |
|
|
|
memcpy(&ret[0][0], myker, sizeof(myker)); |
|
|
|
} |
|
|
|
else if (!strcmp(name + 6, "atkinson")) |
|
|
|
return ret; |
|
|
|
case EdiffAlgorithm::Atkinson: |
|
|
|
ret.SetSize(ivec2(4, 3)); |
|
|
|
{ |
|
|
|
static float const myker[] = |
|
|
|
{ |
|
|
@@ -84,36 +172,44 @@ bool Image::Stock(char const *name) |
|
|
|
1./8, 1./8, 1./8, 0., |
|
|
|
0., 1./8, 0., 0., |
|
|
|
}; |
|
|
|
ker = myker; size = ivec2(4, 3); |
|
|
|
memcpy(&ret[0][0], myker, sizeof(myker)); |
|
|
|
} |
|
|
|
else if (!strcmp(name + 6, "fan")) |
|
|
|
return ret; |
|
|
|
case EdiffAlgorithm::Fan: |
|
|
|
ret.SetSize(ivec2(4, 2)); |
|
|
|
{ |
|
|
|
static float const myker[] = |
|
|
|
{ |
|
|
|
0., 0., 1., 7./16, |
|
|
|
1./16, 3./16, 5./16, 0., |
|
|
|
}; |
|
|
|
ker = myker; size = ivec2(4, 2); |
|
|
|
memcpy(&ret[0][0], myker, sizeof(myker)); |
|
|
|
} |
|
|
|
else if (!strcmp(name + 6, "shiaufan")) |
|
|
|
return ret; |
|
|
|
case EdiffAlgorithm::ShiauFan: |
|
|
|
ret.SetSize(ivec2(4, 2)); |
|
|
|
{ |
|
|
|
static float const myker[] = |
|
|
|
{ |
|
|
|
0., 0., 1., 1./2, |
|
|
|
1./8, 1./8, 1./4, 0., |
|
|
|
}; |
|
|
|
ker = myker; size = ivec2(4, 2); |
|
|
|
memcpy(&ret[0][0], myker, sizeof(myker)); |
|
|
|
} |
|
|
|
else if (!strcmp(name + 6, "shiaufan2")) |
|
|
|
return ret; |
|
|
|
case EdiffAlgorithm::ShiauFan2: |
|
|
|
ret.SetSize(ivec2(5, 2)); |
|
|
|
{ |
|
|
|
static float const myker[] = |
|
|
|
{ |
|
|
|
0., 0., 0., 1., 1./2, |
|
|
|
1./16, 1./16, 1./8, 1./4, 0., |
|
|
|
}; |
|
|
|
ker = myker; size = ivec2(5, 2); |
|
|
|
memcpy(&ret[0][0], myker, sizeof(myker)); |
|
|
|
} |
|
|
|
else if (!strcmp(name + 6, "stucki")) |
|
|
|
return ret; |
|
|
|
case EdiffAlgorithm::Stucki: |
|
|
|
ret.SetSize(ivec2(5, 3)); |
|
|
|
{ |
|
|
|
static float const myker[] = |
|
|
|
{ |
|
|
@@ -121,18 +217,22 @@ bool Image::Stock(char const *name) |
|
|
|
2./42, 4./42, 8./42, 4./42, 2./42, |
|
|
|
1./42, 2./42, 4./42, 2./42, 1./42, |
|
|
|
}; |
|
|
|
ker = myker; size = ivec2(5, 3); |
|
|
|
memcpy(&ret[0][0], myker, sizeof(myker)); |
|
|
|
} |
|
|
|
else if (!strcmp(name + 6, "burkes")) |
|
|
|
return ret; |
|
|
|
case EdiffAlgorithm::Burkes: |
|
|
|
ret.SetSize(ivec2(5, 2)); |
|
|
|
{ |
|
|
|
static float const myker[] = |
|
|
|
{ |
|
|
|
0., 0., 1., 4./16, 2./16, |
|
|
|
1./16, 2./16, 4./16, 2./16, 1./16, |
|
|
|
}; |
|
|
|
ker = myker; size = ivec2(5, 2); |
|
|
|
memcpy(&ret[0][0], myker, sizeof(myker)); |
|
|
|
} |
|
|
|
else if (!strcmp(name + 6, "sierra")) |
|
|
|
return ret; |
|
|
|
case EdiffAlgorithm::Sierra: |
|
|
|
ret.SetSize(ivec2(5, 3)); |
|
|
|
{ |
|
|
|
static float const myker[] = |
|
|
|
{ |
|
|
@@ -140,53 +240,34 @@ bool Image::Stock(char const *name) |
|
|
|
2./32, 4./32, 5./32, 4./32, 2./32, |
|
|
|
0., 2./32, 3./32, 2./32, 0., |
|
|
|
}; |
|
|
|
ker = myker; size = ivec2(5, 3); |
|
|
|
memcpy(&ret[0][0], myker, sizeof(myker)); |
|
|
|
} |
|
|
|
else if (!strcmp(name + 6, "sierra2")) |
|
|
|
return ret; |
|
|
|
case EdiffAlgorithm::Sierra2: |
|
|
|
ret.SetSize(ivec2(5, 2)); |
|
|
|
{ |
|
|
|
static float const myker[] = |
|
|
|
{ |
|
|
|
0., 0., 1., 4./16, 3./16, |
|
|
|
1./16, 2./16, 3./16, 2./16, 1./16, |
|
|
|
}; |
|
|
|
ker = myker; size = ivec2(5, 2); |
|
|
|
memcpy(&ret[0][0], myker, sizeof(myker)); |
|
|
|
} |
|
|
|
else if (!strcmp(name + 6, "lite")) |
|
|
|
return ret; |
|
|
|
case EdiffAlgorithm::Lite: |
|
|
|
ret.SetSize(ivec2(3, 2)); |
|
|
|
{ |
|
|
|
static float const myker[] = |
|
|
|
{ |
|
|
|
0., 1., 1./2, |
|
|
|
1./4, 1./4, 0., |
|
|
|
}; |
|
|
|
ker = myker; size = ivec2(3, 2); |
|
|
|
memcpy(&ret[0][0], myker, sizeof(myker)); |
|
|
|
} |
|
|
|
|
|
|
|
SetSize(size); |
|
|
|
float *pixels = Lock<PixelFormat::Y_F32>(); |
|
|
|
memcpy(pixels, ker, size.x * size.y * sizeof(float)); |
|
|
|
Unlock(pixels); |
|
|
|
|
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
/* Generate a completely random image. */ |
|
|
|
if (!strncmp(name, "random:", 7)) |
|
|
|
{ |
|
|
|
ivec2 size(0); |
|
|
|
|
|
|
|
size.x = atoi(name + 7); |
|
|
|
name = strchr(name + 7, 'x'); |
|
|
|
if (name) |
|
|
|
size.y = atoi(name + 1); |
|
|
|
if (!size.y) |
|
|
|
size.y = size.x; |
|
|
|
if (size.x <= 0 || size.y <= 0) |
|
|
|
return false; |
|
|
|
|
|
|
|
return RenderRandom(size); |
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
|
return false; |
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
|
} /* namespace lol */ |
|
|
|