AreaScaling.glsl 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. // Scaling
  2. #version 430 core
  3. layout (local_size_x = 16, local_size_y = 16) in;
  4. layout( rgba8, binding = 0, set = 3) uniform image2D imgOutput;
  5. layout( binding = 1, set = 2) uniform sampler2D Source;
  6. layout( binding = 2 ) uniform dimensions{
  7. float srcX0;
  8. float srcX1;
  9. float srcY0;
  10. float srcY1;
  11. float dstX0;
  12. float dstX1;
  13. float dstY0;
  14. float dstY1;
  15. };
  16. /***** Area Sampling *****/
  17. // By Sam Belliveau and Filippo Tarpini. Public Domain license.
  18. // Effectively a more accurate sharp bilinear filter when upscaling,
  19. // that also works as a mathematically perfect downscale filter.
  20. // https://entropymine.com/imageworsener/pixelmixing/
  21. // https://github.com/obsproject/obs-studio/pull/1715
  22. // https://legacy.imagemagick.org/Usage/filter/
  23. vec4 AreaSampling(vec2 xy)
  24. {
  25. // Determine the sizes of the source and target images.
  26. vec2 source_size = vec2(abs(srcX1 - srcX0), abs(srcY1 - srcY0));
  27. vec2 target_size = vec2(abs(dstX1 - dstX0), abs(dstY1 - dstY0));
  28. vec2 inverted_target_size = vec2(1.0) / target_size;
  29. // Compute the top-left and bottom-right corners of the target pixel box.
  30. vec2 t_beg = floor(xy - vec2(dstX0 < dstX1 ? dstX0 : dstX1, dstY0 < dstY1 ? dstY0 : dstY1));
  31. vec2 t_end = t_beg + vec2(1.0, 1.0);
  32. // Convert the target pixel box to source pixel box.
  33. vec2 beg = t_beg * inverted_target_size * source_size;
  34. vec2 end = t_end * inverted_target_size * source_size;
  35. // Compute the top-left and bottom-right corners of the pixel box.
  36. ivec2 f_beg = ivec2(beg);
  37. ivec2 f_end = ivec2(end);
  38. // Compute how much of the start and end pixels are covered horizontally & vertically.
  39. float area_w = 1.0 - fract(beg.x);
  40. float area_n = 1.0 - fract(beg.y);
  41. float area_e = fract(end.x);
  42. float area_s = fract(end.y);
  43. // Compute the areas of the corner pixels in the pixel box.
  44. float area_nw = area_n * area_w;
  45. float area_ne = area_n * area_e;
  46. float area_sw = area_s * area_w;
  47. float area_se = area_s * area_e;
  48. // Initialize the color accumulator.
  49. vec4 avg_color = vec4(0.0, 0.0, 0.0, 0.0);
  50. // Accumulate corner pixels.
  51. avg_color += area_nw * texelFetch(Source, ivec2(f_beg.x, f_beg.y), 0);
  52. avg_color += area_ne * texelFetch(Source, ivec2(f_end.x, f_beg.y), 0);
  53. avg_color += area_sw * texelFetch(Source, ivec2(f_beg.x, f_end.y), 0);
  54. avg_color += area_se * texelFetch(Source, ivec2(f_end.x, f_end.y), 0);
  55. // Determine the size of the pixel box.
  56. int x_range = int(f_end.x - f_beg.x - 0.5);
  57. int y_range = int(f_end.y - f_beg.y - 0.5);
  58. // Accumulate top and bottom edge pixels.
  59. for (int x = f_beg.x + 1; x <= f_beg.x + x_range; ++x)
  60. {
  61. avg_color += area_n * texelFetch(Source, ivec2(x, f_beg.y), 0);
  62. avg_color += area_s * texelFetch(Source, ivec2(x, f_end.y), 0);
  63. }
  64. // Accumulate left and right edge pixels and all the pixels in between.
  65. for (int y = f_beg.y + 1; y <= f_beg.y + y_range; ++y)
  66. {
  67. avg_color += area_w * texelFetch(Source, ivec2(f_beg.x, y), 0);
  68. avg_color += area_e * texelFetch(Source, ivec2(f_end.x, y), 0);
  69. for (int x = f_beg.x + 1; x <= f_beg.x + x_range; ++x)
  70. {
  71. avg_color += texelFetch(Source, ivec2(x, y), 0);
  72. }
  73. }
  74. // Compute the area of the pixel box that was sampled.
  75. float area_corners = area_nw + area_ne + area_sw + area_se;
  76. float area_edges = float(x_range) * (area_n + area_s) + float(y_range) * (area_w + area_e);
  77. float area_center = float(x_range) * float(y_range);
  78. // Return the normalized average color.
  79. return avg_color / (area_corners + area_edges + area_center);
  80. }
  81. float insideBox(vec2 v, vec2 bLeft, vec2 tRight) {
  82. vec2 s = step(bLeft, v) - step(tRight, v);
  83. return s.x * s.y;
  84. }
  85. vec2 translateDest(vec2 pos) {
  86. vec2 translatedPos = vec2(pos.x, pos.y);
  87. translatedPos.x = dstX1 < dstX0 ? dstX1 - translatedPos.x : translatedPos.x;
  88. translatedPos.y = dstY0 < dstY1 ? dstY1 + dstY0 - translatedPos.y - 1 : translatedPos.y;
  89. return translatedPos;
  90. }
  91. void main()
  92. {
  93. vec2 bLeft = vec2(dstX0 < dstX1 ? dstX0 : dstX1, dstY0 < dstY1 ? dstY0 : dstY1);
  94. vec2 tRight = vec2(dstX1 > dstX0 ? dstX1 : dstX0, dstY1 > dstY0 ? dstY1 : dstY0);
  95. ivec2 loc = ivec2(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y);
  96. if (insideBox(loc, bLeft, tRight) == 0) {
  97. imageStore(imgOutput, loc, vec4(0, 0, 0, 1));
  98. return;
  99. }
  100. vec4 outColor = AreaSampling(loc);
  101. imageStore(imgOutput, ivec2(translateDest(loc)), vec4(outColor.rgb, 1));
  102. }