tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

cs_blur.glsl (7670B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2  * License, v. 2.0. If a copy of the MPL was not distributed with this
      3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #define WR_FEATURE_TEXTURE_2D
      6 
      7 #include shared,prim_shared
      8 
      9 varying highp vec2 vUv;
     10 flat varying highp vec4 vUvRect;
     11 flat varying highp vec4 vUvClampRect;
     12 flat varying mediump vec2 vOffsetScale;
     13 // The number of pixels on each end that we apply the blur filter over.
     14 // Packed in to vector to work around bug 1630356.
     15 flat varying mediump ivec2 vSupport;
     16 flat varying mediump vec2 vGaussCoefficients;
     17 
     18 #define EDGE_MODE_DUPLICATE     0
     19 #define EDGE_MODE_MIRROR        1
     20 
     21 #ifdef WR_VERTEX_SHADER
     22 // Applies a separable gaussian blur in one direction, as specified
     23 // by the dir field in the blur command.
     24 
     25 #define DIR_HORIZONTAL  0
     26 #define DIR_VERTICAL    1
     27 
     28 PER_INSTANCE in int aBlurRenderTaskAddress;
     29 PER_INSTANCE in int aBlurSourceTaskAddress;
     30 PER_INSTANCE in int aBlurDirection;
     31 PER_INSTANCE in int aBlurEdgeMode;
     32 PER_INSTANCE in vec3 aBlurParams;
     33 
     34 struct BlurTask {
     35     RectWithEndpoint task_rect;
     36     float blur_radius;
     37     vec2 blur_region;
     38 };
     39 
     40 BlurTask fetch_blur_task(int address) {
     41     RectWithEndpoint task_rect = fetch_render_task_rect(address);
     42 
     43     BlurTask task = BlurTask(
     44         task_rect,
     45         aBlurParams.x,
     46         aBlurParams.yz
     47     );
     48 
     49     return task;
     50 }
     51 
     52 void calculate_gauss_coefficients(float sigma) {
     53     // Incremental Gaussian Coefficent Calculation (See GPU Gems 3 pp. 877 - 889)
     54     vGaussCoefficients = vec2(1.0 / (sqrt(2.0 * 3.14159265) * sigma),
     55                               exp(-0.5 / (sigma * sigma)));
     56 
     57     // Pre-calculate the coefficient total in the vertex shader so that
     58     // we can avoid having to do it per-fragment and also avoid division
     59     // by zero in the degenerate case.
     60     vec3 gauss_coefficient = vec3(vGaussCoefficients,
     61                                   vGaussCoefficients.y * vGaussCoefficients.y);
     62     float gauss_coefficient_total = gauss_coefficient.x;
     63     for (int i = 1; i <= vSupport.x; i += 2) {
     64         gauss_coefficient.xy *= gauss_coefficient.yz;
     65         float gauss_coefficient_subtotal = gauss_coefficient.x;
     66         gauss_coefficient.xy *= gauss_coefficient.yz;
     67         gauss_coefficient_subtotal += gauss_coefficient.x;
     68         gauss_coefficient_total += 2.0 * gauss_coefficient_subtotal;
     69     }
     70 
     71     // Scale initial coefficient by total to avoid passing the total separately
     72     // to the fragment shader.
     73     vGaussCoefficients.x /= gauss_coefficient_total;
     74 }
     75 
     76 void main(void) {
     77     BlurTask blur_task = fetch_blur_task(aBlurRenderTaskAddress);
     78     RectWithEndpoint src_rect = fetch_render_task_rect(aBlurSourceTaskAddress);
     79 
     80     RectWithEndpoint target_rect = blur_task.task_rect;
     81 
     82     vec2 texture_size = vec2(TEX_SIZE(sColor0).xy);
     83 
     84     // Ensure that the support is an even number of pixels to simplify the
     85     // fragment shader logic.
     86     //
     87     // TODO(pcwalton): Actually make use of this fact and use the texture
     88     // hardware for linear filtering.
     89     vSupport.x = int(ceil(1.5 * blur_task.blur_radius)) * 2;
     90     vSupport.y = aBlurEdgeMode;
     91 
     92     if (vSupport.x > 0) {
     93         calculate_gauss_coefficients(blur_task.blur_radius);
     94     } else {
     95         // The gauss function gets NaNs when blur radius is zero.
     96         vGaussCoefficients = vec2(1.0, 1.0);
     97     }
     98 
     99     switch (aBlurDirection) {
    100         case DIR_HORIZONTAL:
    101             vOffsetScale = vec2(
    102                 1.0 / (src_rect.p1.x - src_rect.p0.x),
    103                 0.0
    104             );
    105             break;
    106         case DIR_VERTICAL:
    107             vOffsetScale = vec2(
    108                 0.0,
    109                 1.0 / (src_rect.p1.y - src_rect.p0.y)
    110             );
    111             break;
    112         default:
    113             vOffsetScale = vec2(0.0);
    114     }
    115 
    116     vUvRect = vec4(src_rect.p0, src_rect.p1);
    117     vUvRect /= texture_size.xyxy;
    118 
    119     vUvClampRect = vec4(src_rect.p0 + vec2(0.5),
    120                         src_rect.p0 + blur_task.blur_region - vec2(0.5));
    121     vUvClampRect /= texture_size.xyxy;
    122 
    123     vec2 pos = mix(target_rect.p0, target_rect.p1, aPosition.xy);
    124 
    125     vUv = aPosition.xy;
    126 
    127     gl_Position = uTransform * vec4(pos, 0.0, 1.0);
    128 }
    129 #endif
    130 
    131 #ifdef WR_FRAGMENT_SHADER
    132 
    133 #if defined WR_FEATURE_COLOR_TARGET
    134 #define SAMPLE_TYPE vec4
    135 #define SAMPLE_TEXTURE(uv)  texture(sColor0, uv)
    136 #else
    137 #define SAMPLE_TYPE float
    138 #define SAMPLE_TEXTURE(uv)  texture(sColor0, uv).r
    139 #endif
    140 
    141 // TODO(gw): Write a fast path blur that handles smaller blur radii
    142 //           with a offset / weight uniform table and a constant
    143 //           loop iteration count!
    144 
    145 // GLSL implementation of GL_MIRRORED_REPEAT
    146 vec2 mirrored_repeat(vec2 uv) {
    147     vec2 abs_uv = abs(uv);
    148     vec2 uv_int = floor(abs_uv);
    149     vec2 uv_frac = abs_uv - uv_int;
    150     vec2 f = mod(uv_int, vec2(2.0));
    151 
    152     return mix(uv_frac, vec2(1.0) - uv_frac, f);
    153 }
    154 
    155 void main(void) {
    156     vec2 uv = mix(vUvRect.xy, vUvRect.zw, vUv);
    157     SAMPLE_TYPE original_color = SAMPLE_TEXTURE(uv);
    158 
    159     // Incremental Gaussian Coefficent Calculation (See GPU Gems 3 pp. 877 - 889)
    160     vec3 gauss_coefficient = vec3(vGaussCoefficients,
    161                                   vGaussCoefficients.y * vGaussCoefficients.y);
    162 
    163     SAMPLE_TYPE avg_color = original_color * gauss_coefficient.x;
    164 
    165     // Evaluate two adjacent texels at a time. We can do this because, if c0
    166     // and c1 are colors of adjacent texels and k0 and k1 are arbitrary
    167     // factors, this formula:
    168     //
    169     //     k0 * c0 + k1 * c1          (Equation 1)
    170     //
    171     // is equivalent to:
    172     //
    173     //                                 k1
    174     //     (k0 + k1) * lerp(c0, c1, -------)
    175     //                              k0 + k1
    176     //
    177     // A texture lookup of adjacent texels evaluates this formula:
    178     //
    179     //     lerp(c0, c1, t)
    180     //
    181     // for some t. So we can let `t = k1/(k0 + k1)` and effectively evaluate
    182     // Equation 1 with a single texture lookup.
    183     //
    184     // Clamp loop condition variable to a statically known value to workaround
    185     // driver bug on Adreno 3xx. vSupport should not exceed 300 anyway, due to
    186     // the max blur radius being 100. See bug 1720841 for details.
    187     int support = min(vSupport.x, 300);
    188     for (int i = 1; i <= support; i += 2) {
    189         gauss_coefficient.xy *= gauss_coefficient.yz;
    190 
    191         float gauss_coefficient_subtotal = gauss_coefficient.x;
    192         gauss_coefficient.xy *= gauss_coefficient.yz;
    193         gauss_coefficient_subtotal += gauss_coefficient.x;
    194         float gauss_ratio = gauss_coefficient.x / gauss_coefficient_subtotal;
    195 
    196         vec2 offset = vOffsetScale * (float(i) + gauss_ratio);
    197 
    198         vec2 uv0 = vUv - offset;
    199         vec2 uv1 = vUv + offset;
    200 
    201         if (vSupport.y == EDGE_MODE_MIRROR) {
    202             uv0 = mirrored_repeat(uv0);
    203             uv1 = mirrored_repeat(uv1);
    204         }
    205 
    206         uv0 = mix(vUvRect.xy, vUvRect.zw, uv0);
    207         uv1 = mix(vUvRect.xy, vUvRect.zw, uv1);
    208 
    209         uv0 = max(uv0, vUvClampRect.xy);
    210         uv1 = min(uv1, vUvClampRect.zw);
    211 
    212         avg_color += (SAMPLE_TEXTURE(uv0) + SAMPLE_TEXTURE(uv1)) *
    213                      gauss_coefficient_subtotal;
    214     }
    215 
    216     oFragColor = vec4(avg_color);
    217 }
    218 
    219 #ifdef SWGL_DRAW_SPAN
    220     #ifdef WR_FEATURE_COLOR_TARGET
    221 void swgl_drawSpanRGBA8() {
    222     vec2 uv = mix(vUvRect.xy, vUvRect.zw, vUv);
    223     swgl_commitGaussianBlurRGBA8(sColor0, uv, vUvClampRect, vOffsetScale.x != 0.0,
    224                                  vSupport.x, vGaussCoefficients);
    225 }
    226     #else
    227 void swgl_drawSpanR8() {
    228     vec2 uv = mix(vUvRect.xy, vUvRect.zw, vUv);
    229     swgl_commitGaussianBlurR8(sColor0, uv, vUvClampRect, vOffsetScale.x != 0.0,
    230                               vSupport.x, vGaussCoefficients);
    231 }
    232     #endif
    233 #endif
    234 
    235 #endif