/ src / Ryujinx.Graphics.OpenGL / Effects / Shaders / area_scaling.glsl
area_scaling.glsl
  1  #version 430 core
  2  precision mediump float;
  3  layout (local_size_x = 16, local_size_y = 16) in;
  4  layout(rgba8, binding = 0, location=0) uniform image2D imgOutput;
  5  layout( location=1 ) uniform sampler2D Source;
  6  layout( location=2 ) uniform float srcX0;
  7  layout( location=3 ) uniform float srcX1;
  8  layout( location=4 ) uniform float srcY0;
  9  layout( location=5 ) uniform float srcY1;
 10  layout( location=6 ) uniform float dstX0;
 11  layout( location=7 ) uniform float dstX1;
 12  layout( location=8 ) uniform float dstY0;
 13  layout( location=9 ) uniform float dstY1;
 14  
 15  /***** Area Sampling *****/
 16  
 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  
 30      // Compute the top-left and bottom-right corners of the target pixel box.
 31      vec2 t_beg = floor(xy - vec2(dstX0 < dstX1 ? dstX0 : dstX1, dstY0 < dstY1 ? dstY0 : dstY1));
 32      vec2 t_end = t_beg + vec2(1.0, 1.0);
 33  
 34      // Convert the target pixel box to source pixel box.
 35      vec2 beg = t_beg * inverted_target_size * source_size;
 36      vec2 end = t_end * inverted_target_size * source_size;
 37  
 38      // Compute the top-left and bottom-right corners of the pixel box.
 39      ivec2 f_beg = ivec2(beg);
 40      ivec2 f_end = ivec2(end);
 41  
 42      // Compute how much of the start and end pixels are covered horizontally & vertically.
 43      float area_w = 1.0 - fract(beg.x);
 44      float area_n = 1.0 - fract(beg.y);
 45      float area_e = fract(end.x);
 46      float area_s = fract(end.y);
 47  
 48      // Compute the areas of the corner pixels in the pixel box.
 49      float area_nw = area_n * area_w;
 50      float area_ne = area_n * area_e;
 51      float area_sw = area_s * area_w;
 52      float area_se = area_s * area_e;
 53  
 54      // Initialize the color accumulator.
 55      vec4 avg_color = vec4(0.0, 0.0, 0.0, 0.0);
 56  
 57      // Accumulate corner pixels.
 58      avg_color += area_nw * texelFetch(Source, ivec2(f_beg.x, f_beg.y), 0);
 59      avg_color += area_ne * texelFetch(Source, ivec2(f_end.x, f_beg.y), 0);
 60      avg_color += area_sw * texelFetch(Source, ivec2(f_beg.x, f_end.y), 0);
 61      avg_color += area_se * texelFetch(Source, ivec2(f_end.x, f_end.y), 0);
 62  
 63      // Determine the size of the pixel box.
 64      int x_range = int(f_end.x - f_beg.x - 0.5);
 65      int y_range = int(f_end.y - f_beg.y - 0.5);
 66  
 67      // Accumulate top and bottom edge pixels.
 68      for (int x = f_beg.x + 1; x <= f_beg.x + x_range; ++x)
 69      {
 70          avg_color += area_n * texelFetch(Source, ivec2(x, f_beg.y), 0);
 71          avg_color += area_s * texelFetch(Source, ivec2(x, f_end.y), 0);
 72      }
 73  
 74      // Accumulate left and right edge pixels and all the pixels in between.
 75      for (int y = f_beg.y + 1; y <= f_beg.y + y_range; ++y)
 76      {
 77          avg_color += area_w * texelFetch(Source, ivec2(f_beg.x, y), 0);
 78          avg_color += area_e * texelFetch(Source, ivec2(f_end.x, y), 0);
 79  
 80          for (int x = f_beg.x + 1; x <= f_beg.x + x_range; ++x)
 81          {
 82              avg_color += texelFetch(Source, ivec2(x, y), 0);
 83          }
 84      }
 85  
 86      // Compute the area of the pixel box that was sampled.
 87      float area_corners = area_nw + area_ne + area_sw + area_se;
 88      float area_edges = float(x_range) * (area_n + area_s) + float(y_range) * (area_w + area_e);
 89      float area_center = float(x_range) * float(y_range);
 90  
 91      // Return the normalized average color.
 92      return avg_color / (area_corners + area_edges + area_center);
 93  }
 94  
 95  float insideBox(vec2 v, vec2 bLeft, vec2 tRight) {
 96      vec2 s = step(bLeft, v) - step(tRight, v);
 97      return s.x * s.y;
 98  }
 99  
100  vec2 translateDest(vec2 pos) {
101      vec2 translatedPos = vec2(pos.x, pos.y);
102      translatedPos.x = dstX1 < dstX0 ? dstX1 - translatedPos.x : translatedPos.x;
103      translatedPos.y = dstY0 > dstY1 ? dstY0 + dstY1 - translatedPos.y - 1 : translatedPos.y;
104      return translatedPos;
105  }
106  
107  void main()
108  {
109      vec2 bLeft = vec2(dstX0 < dstX1 ? dstX0 : dstX1, dstY0 < dstY1 ? dstY0 : dstY1);
110      vec2 tRight = vec2(dstX1 > dstX0 ? dstX1 : dstX0, dstY1 > dstY0 ? dstY1 : dstY0);
111      ivec2 loc = ivec2(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y);
112      if (insideBox(loc, bLeft, tRight) == 0) {
113          imageStore(imgOutput, loc, vec4(0, 0, 0, 1));
114          return;
115      }
116  
117      vec4 outColor = AreaSampling(loc);
118      imageStore(imgOutput, ivec2(translateDest(loc)), vec4(outColor.rgb, 1));
119  }