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