353 line
10 KiB

  1. //
  2. // Lol Engine
  3. //
  4. // Copyright: (c) 2004-2014 Sam Hocevar <sam@hocevar.net>
  5. // This program is free software; you can redistribute it and/or
  6. // modify it under the terms of the Do What The Fuck You Want To
  7. // Public License, Version 2, as published by Sam Hocevar. See
  8. // http://www.wtfpl.net/ for more details.
  9. //
  10. #if defined HAVE_CONFIG_H
  11. # include "config.h"
  12. #endif
  13. #include <lol/main.h>
  14. /*
  15. * Stock kernels
  16. */
  17. namespace lol
  18. {
  19. array2d<float> Image::BayerKernel(ivec2 size)
  20. {
  21. array2d<float> ret(size);
  22. int n = 1;
  23. while (n < size.x || n < size.y)
  24. n *= 2;
  25. for (int j = 0; j < size.y; j++)
  26. for (int i = 0; i < size.x; i++)
  27. {
  28. int x = 0;
  29. for (int k = 1, l = n * n / 4; k < n; k *= 2, l /= 4)
  30. {
  31. if ((i & k) && (j & k))
  32. x += l;
  33. else if (i & k)
  34. x += 3 * l;
  35. else if (j & k)
  36. x += 2 * l;
  37. }
  38. ret[i][j] = (float)(x + 1) / (n * n + 1);
  39. }
  40. return ret;
  41. }
  42. array2d<float> Image::HalftoneKernel(ivec2 size)
  43. {
  44. array2d<float> ret(size);
  45. for (int y = 0; y < size.y; y++)
  46. for (int x = 0; x < size.x; x++)
  47. {
  48. float dx = 2.f * (x + 0.02f) / size.x - 0.5f;
  49. float dy = 2.f * (y + 0.03f) / size.y - 0.5f;
  50. bool flip = false;
  51. if (dx > 0.5f)
  52. {
  53. flip = !flip;
  54. dx -= 1.0f;
  55. }
  56. if (dy > 0.5f)
  57. {
  58. flip = !flip;
  59. dy -= 1.0f;
  60. }
  61. /* Using dx²+dy² here creates another interesting halftone. */
  62. float r = - lol::cos(F_PI * (dx - dy)) - lol::cos(F_PI * (dx + dy));
  63. ret[x][y] = flip ? 10.f - r : r;
  64. }
  65. return NormalizeKernel(ret);
  66. }
  67. array2d<float> Image::BlueNoiseKernel(ivec2 size, ivec2 gsize)
  68. {
  69. float const epsilon = 1.f / (size.x * size.y + 1);
  70. gsize = lol::min(size, gsize);
  71. array2d<float> ret(size);
  72. array2d<vec2> dots(size);
  73. /* Create a small Gaussian kernel for filtering */
  74. array2d<float> gaussian(gsize);
  75. for (int j = 0; j < gsize.y; ++j)
  76. for (int i = 0; i < gsize.x; ++i)
  77. {
  78. ivec2 const distance = gsize / 2 - ivec2(i, j);
  79. gaussian[i][j] = lol::exp(-lol::sqlength(distance)
  80. / (0.05f * gsize.x * gsize.y));
  81. }
  82. /* Helper function to find voids and clusters */
  83. auto setdot = [&] (ivec2 pos, float val)
  84. {
  85. float const delta = val - dots[pos][0];
  86. dots[pos][0] = val;
  87. for (int j = 0; j < gsize.y; ++j)
  88. for (int i = 0; i < gsize.x; ++i)
  89. dots[(pos.x + i - gsize.x / 2 + size.x) % size.x]
  90. [(pos.y + j - gsize.y / 2 + size.y) % size.y]
  91. [1] += gaussian[i][j] * delta;
  92. };
  93. auto best = [&] (float val, float mul) -> ivec2
  94. {
  95. float maxval = -size.x * size.y;
  96. ivec2 coord(0, 0);
  97. for (int y = 0; y < size.y; ++y)
  98. for (int x = 0; x < size.x; ++x)
  99. {
  100. if (dots[x][y][0] != val)
  101. continue;
  102. float total = dots[x][y][1];
  103. if (total * mul > maxval)
  104. {
  105. maxval = total * mul;
  106. coord = ivec2(x, y);
  107. }
  108. }
  109. return coord;
  110. };
  111. /* Generate an array with about 10% random dots */
  112. int const ndots = (size.x * size.y + 9) / 10;
  113. memset(dots.Data(), 0, dots.Bytes());
  114. for (int n = 0; n < ndots; )
  115. {
  116. ivec2 pos(lol::rand(size.x), lol::rand(size.y));
  117. if (dots[pos][0])
  118. continue;
  119. setdot(ivec2(pos), 1.0f);
  120. ++n;
  121. }
  122. /* Rearrange 1s so that they occupy the largest voids */
  123. for (;;)
  124. {
  125. ivec2 bestcluster = best(1.0f, 1.0f);
  126. setdot(bestcluster, 0.0f);
  127. ivec2 bestvoid = best(0.0f, -1.0f);
  128. setdot(bestvoid, 1.0f);
  129. if (bestcluster == bestvoid)
  130. break;
  131. }
  132. /* Reorder all 1s and replace them with 0.0001 */
  133. for (int n = ndots; n--; )
  134. {
  135. ivec2 bestcluster = best(1.0f, 1.0f);
  136. ret[bestcluster] = (n + 1.0f) * epsilon;
  137. setdot(bestcluster, 0.0001f);
  138. }
  139. /* Reorder all 0s and replace them with 0.0001 */
  140. for (int n = ndots; n < size.x * size.y; ++n)
  141. {
  142. ivec2 bestvoid = best(0.0f, -1.0f);
  143. ret[bestvoid] = (n + 1.0f) * epsilon;
  144. setdot(bestvoid, 0.0001f);
  145. }
  146. return ret;
  147. }
  148. struct Dot
  149. {
  150. int x, y;
  151. float val;
  152. };
  153. static int cmpdot(const void *p1, const void *p2)
  154. {
  155. return ((Dot const *)p1)->val > ((Dot const *)p2)->val;
  156. }
  157. array2d<float> Image::NormalizeKernel(array2d<float> const &kernel)
  158. {
  159. ivec2 size = (ivec2)kernel.GetSize();
  160. array<Dot> tmp;
  161. tmp.Resize(size.x * size.y);
  162. for (int y = 0; y < size.y; y++)
  163. for (int x = 0; x < size.x; x++)
  164. {
  165. tmp[y * size.x + x].x = x;
  166. tmp[y * size.x + x].y = y;
  167. tmp[y * size.x + x].val = kernel[x][y];
  168. }
  169. std::qsort(tmp.Data(), size.x * size.y, sizeof(Dot), cmpdot);
  170. array2d<float> dst(size);
  171. float const epsilon = 1.f / (size.x * size.y + 1);
  172. for (int n = 0; n < size.x * size.y; n++)
  173. {
  174. int x = tmp[n].x;
  175. int y = tmp[n].y;
  176. dst[x][y] = (n + 1.f) * epsilon;
  177. }
  178. return dst;
  179. }
  180. array2d<float> Image::EdiffKernel(EdiffAlgorithm algorithm)
  181. {
  182. switch (algorithm)
  183. {
  184. case EdiffAlgorithm::FloydSteinberg:
  185. return { { 0.f, 1.f, 7.f/16, },
  186. { 3.f/16, 5.f/16, 1.f/16, }, };
  187. case EdiffAlgorithm::JaJuNi:
  188. return { { 0.f, 0.f, 1.f, 7.f/48, 5.f/48, },
  189. { 3.f/48, 5.f/48, 7.f/48, 5.f/48, 3.f/48, },
  190. { 1.f/48, 3.f/48, 5.f/48, 3.f/48, 1.f/48, }, };
  191. case EdiffAlgorithm::Atkinson:
  192. return { { 0.f, 1.f, 1.f/8, 1.f/8, },
  193. { 1.f/8, 1.f/8, 1.f/8, 0.f, },
  194. { 0.f, 1.f/8, 0.f, 0.f, }, };
  195. case EdiffAlgorithm::Fan:
  196. return { { 0.f, 0.f, 1.f, 7.f/16, },
  197. { 1.f/16, 3.f/16, 5.f/16, 0.f, }, };
  198. case EdiffAlgorithm::ShiauFan:
  199. return { { 0.f, 0.f, 1.f, 1.f/2, },
  200. { 1.f/8, 1.f/8, 1.f/4, 0.f, }, };
  201. case EdiffAlgorithm::ShiauFan2:
  202. return { { 0.f, 0.f, 0.f, 1.f, 1.f/2, },
  203. { 1.f/16, 1.f/16, 1.f/8, 1.f/4, 0.f, }, };
  204. case EdiffAlgorithm::Stucki:
  205. return { { 0.f, 0.f, 1.f, 8.f/42, 4.f/42, },
  206. { 2.f/42, 4.f/42, 8.f/42, 4.f/42, 2.f/42, },
  207. { 1.f/42, 2.f/42, 4.f/42, 2.f/42, 1.f/42, }, };
  208. case EdiffAlgorithm::Burkes:
  209. return { { 0.f, 0.f, 1.f, 4.f/16, 2.f/16, },
  210. { 1.f/16, 2.f/16, 4.f/16, 2.f/16, 1.f/16, }, };
  211. case EdiffAlgorithm::Sierra:
  212. return { { 0.f, 0.f, 1.f, 5.f/32, 3.f/32, },
  213. { 2.f/32, 4.f/32, 5.f/32, 4.f/32, 2.f/32, },
  214. { 0.f, 2.f/32, 3.f/32, 2.f/32, 0.f, }, };
  215. case EdiffAlgorithm::Sierra2:
  216. return { { 0.f, 0.f, 1.f, 4.f/16, 3.f/16, },
  217. { 1.f/16, 2.f/16, 3.f/16, 2.f/16, 1.f/16, }, };
  218. case EdiffAlgorithm::Lite:
  219. return { { 0.f, 1.f, 1.f/2, },
  220. { 1.f/4, 1.f/4, 0.f, }, };
  221. }
  222. return { { 1.f } };
  223. }
  224. /* Any standard deviation below this value will be rounded up, in order
  225. * to avoid ridiculously low values. exp(-1/(2*0.2*0.2)) is < 10^-5 so
  226. * there is little chance that any value below 0.2 will be useful. */
  227. #define BLUR_EPSILON 0.2f
  228. array2d<float> Image::GaussianKernel(vec2 radius, float angle, vec2 delta)
  229. {
  230. array2d<float> kernel;
  231. if (radius.x < BLUR_EPSILON)
  232. radius.x = BLUR_EPSILON;
  233. if (radius.y < BLUR_EPSILON)
  234. radius.y = BLUR_EPSILON;
  235. float const sint = lol::sin(angle);
  236. float const cost = lol::cos(angle);
  237. /* Compute the final ellipse's bounding box */
  238. float const bbx = lol::sqrt(sq(radius.x * cost) + sq(radius.y * sint));
  239. float const bby = lol::sqrt(sq(radius.y * cost) + sq(radius.x * sint));
  240. /* FIXME: the kernel becomes far too big with large values of dx, because
  241. * we grow both left and right. Fix the growing direction. */
  242. int const krx = (int)(3.f * bbx + .99999f + lol::ceil(lol::abs(delta.x)));
  243. int const kry = (int)(3.f * bby + .99999f + lol::ceil(lol::abs(delta.y)));
  244. ivec2 size(2 * krx + 1, 2 * kry + 1);
  245. float const Kx = -1.f / (2.f * radius.x * radius.x);
  246. float const Ky = -1.f / (2.f * radius.y * radius.y);
  247. kernel.SetSize(size);
  248. float t = 0.f;
  249. for (int j = -kry; j <= kry; j++)
  250. {
  251. for (int i = -krx; i <= krx; i++)
  252. {
  253. /* FIXME: this level of interpolation sucks. We should
  254. * interpolate on the full NxN grid for better quality. */
  255. static vec3 const samples[] =
  256. {
  257. vec3( 0.0f, 0.0f, 1.0f),
  258. vec3(-0.4f, -0.4f, 0.8f),
  259. vec3(-0.3f, 0.0f, 0.9f),
  260. vec3(-0.4f, 0.4f, 0.8f),
  261. vec3( 0.0f, 0.3f, 0.9f),
  262. vec3( 0.4f, 0.4f, 0.8f),
  263. vec3( 0.3f, 0.0f, 0.9f),
  264. vec3( 0.4f, -0.4f, 0.8f),
  265. vec3( 0.0f, -0.3f, 0.9f),
  266. };
  267. float d = 0.f;
  268. for (auto p : samples)
  269. {
  270. float u = (i + p.x) * cost - (j + p.y) * sint + delta.x;
  271. float v = (i + p.x) * sint + (j + p.y) * cost + delta.y;
  272. float ex = Kx * u * u;
  273. float ey = Ky * v * v;
  274. d += p.z * lol::exp(ex + ey);
  275. /* Do not interpolate if this is a standard gaussian. */
  276. if (!delta.x && !delta.y && !angle)
  277. break;
  278. }
  279. kernel[i + krx][j + kry] = d;
  280. t += d;
  281. }
  282. }
  283. for (int j = 0; j < size.y; j++)
  284. for (int i = 0; i < size.x; i++)
  285. kernel[i][j] *= (1.f / t);
  286. return kernel;
  287. }
  288. } /* namespace lol */