Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

187 rader
5.4 KiB

  1. //
  2. // Lol Engine
  3. //
  4. // Copyright © 2004—2017 Sam Hocevar <sam@hocevar.net>
  5. //
  6. // Lol Engine is free software. It comes without any warranty, to
  7. // the extent permitted by applicable law. You can redistribute it
  8. // and/or modify it under the terms of the Do What the Fuck You Want
  9. // to Public License, Version 2, as published by the WTFPL Task Force.
  10. // See http://www.wtfpl.net/ for more details.
  11. //
  12. #include <lol/engine-internal.h>
  13. /*
  14. * Direct Binary Search dithering
  15. */
  16. #define CELL 16
  17. #define N 7
  18. #define NN ((N * 2 + 1))
  19. namespace lol
  20. {
  21. /* FIXME: though the algorithm is supposed to stop, we do not have a real,
  22. * guaranteed stop condition here. */
  23. image image::dither_dbs() const
  24. {
  25. ivec2 isize = size();
  26. /* Build our human visual system kernel. */
  27. array2d<float> ker;
  28. ker.resize(ivec2(NN, NN));
  29. float t = 0.f;
  30. for (int j = 0; j < NN; j++)
  31. for (int i = 0; i < NN; i++)
  32. {
  33. vec2 v = vec2(i - N, j - N);
  34. ker[i][j] = exp(-sqlength(v / 1.6f) / 2.f)
  35. + exp(-sqlength(v / 0.6f) / 2.f);
  36. t += ker[i][j];
  37. }
  38. for (int j = 0; j < NN; j++)
  39. for (int i = 0; i < NN; i++)
  40. ker[i][j] /= t;
  41. /* A list of cells in our picture. If no change is done to a cell
  42. * for two iterations, we stop considering changes to it. */
  43. ivec2 const csize = (isize + ivec2(CELL - 1)) / CELL;
  44. array2d<int> changelist(csize);
  45. memset(changelist.data(), 0, changelist.bytes());
  46. image dst = *this;
  47. dst.set_format(PixelFormat::Y_F32);
  48. image tmp1 = dst.Convolution(ker);
  49. array2d<float> &tmp1data = tmp1.lock2d<PixelFormat::Y_F32>();
  50. dst = dst.dither_random();
  51. array2d<float> &dstdata = dst.lock2d<PixelFormat::Y_F32>();
  52. image tmp2 = dst.Convolution(ker);
  53. array2d<float> &tmp2data = tmp2.lock2d<PixelFormat::Y_F32>();
  54. for (int run = 0, last_change = 0; ; ++run)
  55. {
  56. int const cell = run % (csize.x * csize.y);
  57. int const cx = cell % csize.x;
  58. int const cy = cell / csize.x;
  59. /* Bail out if no change was done for the last full image run */
  60. if (run > last_change + csize.x * csize.y)
  61. break;
  62. /* Skip cell if it was already ignored twice */
  63. if (changelist[cx][cy] >= 2)
  64. continue;
  65. int changes = 0;
  66. for (int pixel = 0; pixel < CELL * CELL; ++pixel)
  67. {
  68. ivec2 const pos(cx * CELL + pixel % CELL,
  69. cy * CELL + pixel / CELL);
  70. if (!(pos >= ivec2(0)) || !(pos < isize))
  71. continue;
  72. /* The best operation we can do */
  73. ivec2 best_op(0);
  74. float best_error = 0.f;
  75. float d = dstdata[pos];
  76. /* Compute the effect of all possible toggle and swaps */
  77. static ivec2 const op_list[] =
  78. {
  79. { 0, 0 },
  80. { 0, 1 }, { 0, -1 }, { -1, 0 }, { 1, 0 },
  81. { -1, -1 }, { -1, 1 }, { 1, -1 }, { 1, 1 },
  82. };
  83. for (ivec2 const op : op_list)
  84. {
  85. if (!(pos + op >= ivec2(0)) || !(pos + op < isize))
  86. continue;
  87. bool flip = (op == ivec2(0));
  88. float d2 = flip ? 1 - d : dstdata[pos + op];
  89. if (!flip && d2 == d)
  90. continue;
  91. /* TODO: implement min/max for 3+ arguments */
  92. int imin = max(max(-N, op.x - N), -pos.x);
  93. int imax = min(min(N + 1, op.x + NN - N), isize.x - pos.x);
  94. int jmin = max(max(-N, op.y - N), -pos.y);
  95. int jmax = min(min(N + 1, op.y + NN - N), isize.y - pos.y);
  96. float error = 0.f;
  97. for (int j = jmin; j < jmax; j++)
  98. for (int i = imin; i < imax; i++)
  99. {
  100. ivec2 pos2 = pos + ivec2(i, j);
  101. float m = ker[i + N][j + N];
  102. if (!flip)
  103. m -= ker[i - op.x + N][j - op.y + N];
  104. float p = tmp1data[pos2];
  105. float q1 = tmp2data[pos2];
  106. float q2 = q1 + m * (d2 - d);
  107. error += sq(q1 - p) - sq(q2 - p);
  108. }
  109. if (error > best_error)
  110. {
  111. best_error = error;
  112. best_op = op;
  113. }
  114. }
  115. /* Only apply the change if interesting */
  116. if (best_error > 0.f)
  117. {
  118. bool flip = (best_op == ivec2(0));
  119. float d2 = flip ? 1 - d : dstdata[pos + best_op];
  120. dstdata[pos + best_op] = d;
  121. dstdata[pos] = d2;
  122. for (int j = -N; j <= N; j++)
  123. for (int i = -N; i <= N; i++)
  124. {
  125. ivec2 off(i, j);
  126. float delta = (d2 - d) * ker[i + N][j + N];
  127. if (pos + off >= ivec2(0) && pos + off < isize)
  128. tmp2data[pos + off] += delta;
  129. if (!flip && pos + off + best_op >= ivec2(0)
  130. && pos + off + best_op < isize)
  131. tmp2data[pos + off + best_op] -= delta;
  132. }
  133. ++changes;
  134. last_change = run;
  135. }
  136. }
  137. if (changes == 0)
  138. ++changelist[cx][cy];
  139. }
  140. tmp1.unlock2d(tmp1data);
  141. tmp2.unlock2d(tmp2data);
  142. dst.unlock2d(dstdata);
  143. return dst;
  144. }
  145. } /* namespace lol */