Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 
 

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