blend.glsl (9349B)
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 #include gpu_buffer 6 7 #define COMPONENT_TRANSFER_IDENTITY 0 8 #define COMPONENT_TRANSFER_TABLE 1 9 #define COMPONENT_TRANSFER_DISCRETE 2 10 #define COMPONENT_TRANSFER_LINEAR 3 11 #define COMPONENT_TRANSFER_GAMMA 4 12 13 // Must be kept in sync with `Filter::as_int` in internal_types.rs 14 // Not all filters are defined here because some filter use different shaders. 15 #define FILTER_CONTRAST 0 16 #define FILTER_GRAYSCALE 1 17 #define FILTER_HUE_ROTATE 2 18 #define FILTER_INVERT 3 19 #define FILTER_SATURATE 4 20 #define FILTER_SEPIA 5 21 #define FILTER_BRIGHTNESS 6 22 #define FILTER_COLOR_MATRIX 7 23 #define FILTER_SRGB_TO_LINEAR 8 24 #define FILTER_LINEAR_TO_SRGB 9 25 #define FILTER_FLOOD 10 26 #define FILTER_COMPONENT_TRANSFER 11 27 28 #ifdef WR_VERTEX_SHADER 29 void SetupFilterParams( 30 int op, 31 float amount, 32 int gpu_data_address, 33 out vec4 color_offset, 34 out mat4 color_mat, 35 out highp int table_address 36 ) { 37 float lumR = 0.2126; 38 float lumG = 0.7152; 39 float lumB = 0.0722; 40 float oneMinusLumR = 1.0 - lumR; 41 float oneMinusLumG = 1.0 - lumG; 42 float oneMinusLumB = 1.0 - lumB; 43 float invAmount = 1.0 - amount; 44 45 if (op == FILTER_GRAYSCALE) { 46 color_mat = mat4( 47 vec4(lumR + oneMinusLumR * invAmount, lumR - lumR * invAmount, lumR - lumR * invAmount, 0.0), 48 vec4(lumG - lumG * invAmount, lumG + oneMinusLumG * invAmount, lumG - lumG * invAmount, 0.0), 49 vec4(lumB - lumB * invAmount, lumB - lumB * invAmount, lumB + oneMinusLumB * invAmount, 0.0), 50 vec4(0.0, 0.0, 0.0, 1.0) 51 ); 52 color_offset = vec4(0.0); 53 } else if (op == FILTER_HUE_ROTATE) { 54 float c = cos(amount); 55 float s = sin(amount); 56 color_mat = mat4( 57 vec4(lumR + oneMinusLumR * c - lumR * s, lumR - lumR * c + 0.143 * s, lumR - lumR * c - oneMinusLumR * s, 0.0), 58 vec4(lumG - lumG * c - lumG * s, lumG + oneMinusLumG * c + 0.140 * s, lumG - lumG * c + lumG * s, 0.0), 59 vec4(lumB - lumB * c + oneMinusLumB * s, lumB - lumB * c - 0.283 * s, lumB + oneMinusLumB * c + lumB * s, 0.0), 60 vec4(0.0, 0.0, 0.0, 1.0) 61 ); 62 color_offset = vec4(0.0); 63 } else if (op == FILTER_SATURATE) { 64 color_mat = mat4( 65 vec4(invAmount * lumR + amount, invAmount * lumR, invAmount * lumR, 0.0), 66 vec4(invAmount * lumG, invAmount * lumG + amount, invAmount * lumG, 0.0), 67 vec4(invAmount * lumB, invAmount * lumB, invAmount * lumB + amount, 0.0), 68 vec4(0.0, 0.0, 0.0, 1.0) 69 ); 70 color_offset = vec4(0.0); 71 } else if (op == FILTER_SEPIA) { 72 color_mat = mat4( 73 vec4(0.393 + 0.607 * invAmount, 0.349 - 0.349 * invAmount, 0.272 - 0.272 * invAmount, 0.0), 74 vec4(0.769 - 0.769 * invAmount, 0.686 + 0.314 * invAmount, 0.534 - 0.534 * invAmount, 0.0), 75 vec4(0.189 - 0.189 * invAmount, 0.168 - 0.168 * invAmount, 0.131 + 0.869 * invAmount, 0.0), 76 vec4(0.0, 0.0, 0.0, 1.0) 77 ); 78 color_offset = vec4(0.0); 79 } else if (op == FILTER_COLOR_MATRIX) { 80 vec4 mat_data[4] = fetch_from_gpu_buffer_4f(gpu_data_address); 81 vec4 offset_data = fetch_from_gpu_buffer_1f(gpu_data_address + 4); 82 color_mat = mat4(mat_data[0], mat_data[1], mat_data[2], mat_data[3]); 83 color_offset = offset_data; 84 } else if (op == FILTER_COMPONENT_TRANSFER) { 85 table_address = gpu_data_address; 86 } else if (op == FILTER_FLOOD) { 87 color_offset = fetch_from_gpu_buffer_1f(gpu_data_address); 88 } 89 } 90 #endif 91 92 #ifdef WR_FRAGMENT_SHADER 93 vec3 Contrast(vec3 Cs, float amount) { 94 return clamp(Cs.rgb * amount - 0.5 * amount + 0.5, 0.0, 1.0); 95 } 96 97 vec3 Invert(vec3 Cs, float amount) { 98 return mix(Cs.rgb, vec3(1.0) - Cs.rgb, amount); 99 } 100 101 vec3 Brightness(vec3 Cs, float amount) { 102 // Apply the brightness factor. 103 // Resulting color needs to be clamped to output range 104 // since we are pre-multiplying alpha in the shader. 105 return clamp(Cs.rgb * amount, vec3(0.0), vec3(1.0)); 106 } 107 108 // Based on the Gecko's implementation in 109 // https://hg.mozilla.org/mozilla-central/file/91b4c3687d75/gfx/src/FilterSupport.cpp#l24 110 // These could be made faster by sampling a lookup table stored in a float texture 111 // with linear interpolation. 112 113 vec3 SrgbToLinear(vec3 color) { 114 vec3 c1 = color / 12.92; 115 vec3 c2 = pow(color / 1.055 + vec3(0.055 / 1.055), vec3(2.4)); 116 return if_then_else(lessThanEqual(color, vec3(0.04045)), c1, c2); 117 } 118 119 vec3 LinearToSrgb(vec3 color) { 120 vec3 c1 = color * 12.92; 121 vec3 c2 = vec3(1.055) * pow(color, vec3(1.0 / 2.4)) - vec3(0.055); 122 return if_then_else(lessThanEqual(color, vec3(0.0031308)), c1, c2); 123 } 124 125 // This function has to be factored out due to the following issue: 126 // https://github.com/servo/webrender/wiki/Driver-issues#bug-1532245---switch-statement-inside-control-flow-inside-switch-statement-fails-to-compile-on-some-android-phones 127 // (and now the words "default: default:" so angle_shader_validation.rs passes) 128 vec4 ComponentTransfer(vec4 colora, vec4 vfuncs, highp int table_address) { 129 // We push a different amount of data to the gpu cache depending on the 130 // function type. 131 // Identity => 0 blocks 132 // Table/Discrete => 64 blocks (256 values) 133 // Linear => 1 block (2 values) 134 // Gamma => 1 block (3 values) 135 // We loop through the color components and increment the offset (for the 136 // next color component) into the gpu cache based on how many blocks that 137 // function type put into the gpu cache. 138 // Table/Discrete use a 256 entry look up table. 139 // Linear/Gamma are a simple calculation. 140 141 // Both offset and k must be marked as highp due to a Adreno 3xx bug likely 142 // to do with converting between precisions (as they would otherwise be 143 // promoted when adding to table_address). 144 highp int offset = 0; 145 highp int k; 146 147 vec4 texel; 148 149 // Dynamically indexing a vector is buggy on some platforms, so use a temporary array 150 int[4] funcs = int[4](int(vfuncs.r), int(vfuncs.g), int(vfuncs.b), int(vfuncs.a)); 151 for (int i = 0; i < 4; i++) { 152 switch (funcs[i]) { 153 case COMPONENT_TRANSFER_IDENTITY: 154 break; 155 case COMPONENT_TRANSFER_TABLE: 156 case COMPONENT_TRANSFER_DISCRETE: { 157 // fetch value from lookup table 158 k = int(floor(colora[i]*255.0 + 0.5)); 159 texel = fetch_from_gpu_buffer_1f(table_address + offset + k/4); 160 colora[i] = clamp(texel[k % 4], 0.0, 1.0); 161 // offset plus 256/4 blocks 162 offset = offset + 64; 163 break; 164 } 165 case COMPONENT_TRANSFER_LINEAR: { 166 // fetch the two values for use in the linear equation 167 texel = fetch_from_gpu_buffer_1f(table_address + offset); 168 colora[i] = clamp(texel[0] * colora[i] + texel[1], 0.0, 1.0); 169 // offset plus 1 block 170 offset = offset + 1; 171 break; 172 } 173 case COMPONENT_TRANSFER_GAMMA: { 174 // fetch the three values for use in the gamma equation 175 texel = fetch_from_gpu_buffer_1f(table_address + offset); 176 colora[i] = clamp(texel[0] * pow(colora[i], texel[1]) + texel[2], 0.0, 1.0); 177 // offset plus 1 block 178 offset = offset + 1; 179 break; 180 } 181 default: 182 // shouldn't happen 183 break; 184 } 185 } 186 return colora; 187 } 188 189 void CalculateFilter( 190 vec4 Cs, 191 int op, 192 float amount, 193 highp int table_address, 194 vec4 color_offset, 195 mat4 color_mat, 196 vec4 v_funcs, 197 out vec3 color, 198 out float alpha 199 ) { 200 // Un-premultiply the input. 201 alpha = Cs.a; 202 color = alpha != 0.0 ? Cs.rgb / alpha : Cs.rgb; 203 204 switch (op) { 205 case FILTER_CONTRAST: 206 color = Contrast(color, amount); 207 break; 208 case FILTER_INVERT: 209 color = Invert(color, amount); 210 break; 211 case FILTER_BRIGHTNESS: 212 color = Brightness(color, amount); 213 break; 214 case FILTER_SRGB_TO_LINEAR: 215 color = SrgbToLinear(color); 216 break; 217 case FILTER_LINEAR_TO_SRGB: 218 color = LinearToSrgb(color); 219 break; 220 case FILTER_COMPONENT_TRANSFER: { 221 // Get the unpremultiplied color with alpha. 222 vec4 colora = vec4(color, alpha); 223 colora = ComponentTransfer(colora, v_funcs, table_address); 224 color = colora.rgb; 225 alpha = colora.a; 226 break; 227 } 228 case FILTER_FLOOD: 229 color = color_offset.rgb; 230 alpha = color_offset.a; 231 break; 232 default: 233 // Color matrix type filters (sepia, hue-rotate, etc...) 234 vec4 result = color_mat * vec4(color, alpha) + color_offset; 235 result = clamp(result, vec4(0.0), vec4(1.0)); 236 color = result.rgb; 237 alpha = result.a; 238 } 239 } 240 #endif