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 9varying vec2 vInput1Uv; 10varying vec2 vInput2Uv; 11flat varying vec4 vInput1UvRect; 12flat varying vec4 vInput2UvRect; 13flat varying ivec4 vData; 14flat varying vec4 vFilterData0; 15flat varying vec4 vFilterData1; 16 17// x: Filter input count, y: Filter kind. 18// Packed in to a vector to work around bug 1630356. 19flat varying ivec2 vFilterInputCountFilterKindVec; 20#define vFilterInputCount vFilterInputCountFilterKindVec.x 21#define vFilterKind vFilterInputCountFilterKindVec.y 22// Packed in to a vector to work around bug 1630356. 23flat varying vec2 vFloat0; 24 25flat varying mat4 vColorMat; 26flat varying ivec4 vFuncs; 27 28#define FILTER_BLEND 0 29#define FILTER_FLOOD 1 30#define FILTER_LINEAR_TO_SRGB 2 31#define FILTER_SRGB_TO_LINEAR 3 32#define FILTER_OPACITY 4 33#define FILTER_COLOR_MATRIX 5 34#define FILTER_DROP_SHADOW 6 35#define FILTER_OFFSET 7 36#define FILTER_COMPONENT_TRANSFER 8 37#define FILTER_IDENTITY 9 38#define FILTER_COMPOSITE 10 39 40#define COMPOSITE_OVER 0 41#define COMPOSITE_IN 1 42#define COMPOSITE_OUT 2 43#define COMPOSITE_ATOP 3 44#define COMPOSITE_XOR 4 45#define COMPOSITE_LIGHTER 5 46#define COMPOSITE_ARITHMETIC 6 47 48#ifdef WR_VERTEX_SHADER 49 50PER_INSTANCE in int aFilterRenderTaskAddress; 51PER_INSTANCE in int aFilterInput1TaskAddress; 52PER_INSTANCE in int aFilterInput2TaskAddress; 53PER_INSTANCE in int aFilterKind; 54PER_INSTANCE in int aFilterInputCount; 55PER_INSTANCE in int aFilterGenericInt; 56PER_INSTANCE in ivec2 aFilterExtraDataAddress; 57 58struct FilterTask { 59 RectWithEndpoint task_rect; 60 vec3 user_data; 61}; 62 63FilterTask fetch_filter_task(int address) { 64 RenderTaskData task_data = fetch_render_task_data(address); 65 66 FilterTask task = FilterTask( 67 task_data.task_rect, 68 task_data.user_data.xyz 69 ); 70 71 return task; 72} 73 74vec4 compute_uv_rect(RectWithEndpoint task_rect, vec2 texture_size) { 75 vec4 uvRect = vec4(task_rect.p0 + vec2(0.5), 76 task_rect.p1 - vec2(0.5)); 77 uvRect /= texture_size.xyxy; 78 return uvRect; 79} 80 81vec2 compute_uv(RectWithEndpoint task_rect, vec2 texture_size) { 82 vec2 uv0 = task_rect.p0 / texture_size; 83 vec2 uv1 = floor(task_rect.p1) / texture_size; 84 return mix(uv0, uv1, aPosition.xy); 85} 86 87void main(void) { 88 FilterTask filter_task = fetch_filter_task(aFilterRenderTaskAddress); 89 RectWithEndpoint target_rect = filter_task.task_rect; 90 91 vec2 pos = mix(target_rect.p0, target_rect.p1, aPosition.xy); 92 93 RectWithEndpoint input_1_task; 94 if (aFilterInputCount > 0) { 95 vec2 texture_size = vec2(TEX_SIZE(sColor0).xy); 96 input_1_task = fetch_render_task_rect(aFilterInput1TaskAddress); 97 vInput1UvRect = compute_uv_rect(input_1_task, texture_size); 98 vInput1Uv = compute_uv(input_1_task, texture_size); 99 } 100 101 RectWithEndpoint input_2_task; 102 if (aFilterInputCount > 1) { 103 vec2 texture_size = vec2(TEX_SIZE(sColor1).xy); 104 input_2_task = fetch_render_task_rect(aFilterInput2TaskAddress); 105 vInput2UvRect = compute_uv_rect(input_2_task, texture_size); 106 vInput2Uv = compute_uv(input_2_task, texture_size); 107 } 108 109 vFilterInputCount = aFilterInputCount; 110 vFilterKind = aFilterKind; 111 112 // This assignment is only used for component transfer filters but this 113 // assignment has to be done here and not in the component transfer case 114 // below because it doesn't get executed on Windows because of a suspected 115 // miscompile of this shader on Windows. See 116 // https://github.com/servo/webrender/wiki/Driver-issues#bug-1505871---assignment-to-varying-flat-arrays-inside-switch-statement-of-vertex-shader-suspected-miscompile-on-windows 117 // default: just to satisfy angle_shader_validation.rs which needs one 118 // default: for every switch, even in comments. 119 vFuncs.r = (aFilterGenericInt >> 12) & 0xf; // R 120 vFuncs.g = (aFilterGenericInt >> 8) & 0xf; // G 121 vFuncs.b = (aFilterGenericInt >> 4) & 0xf; // B 122 vFuncs.a = (aFilterGenericInt) & 0xf; // A 123 124 switch (aFilterKind) { 125 case FILTER_BLEND: 126 vData = ivec4(aFilterGenericInt, 0, 0, 0); 127 break; 128 case FILTER_FLOOD: 129 vFilterData0 = fetch_from_gpu_cache_1_direct(aFilterExtraDataAddress); 130 break; 131 case FILTER_OPACITY: 132 vFloat0.x = filter_task.user_data.x; 133 break; 134 case FILTER_COLOR_MATRIX: 135 vec4 mat_data[4] = fetch_from_gpu_cache_4_direct(aFilterExtraDataAddress); 136 vColorMat = mat4(mat_data[0], mat_data[1], mat_data[2], mat_data[3]); 137 vFilterData0 = fetch_from_gpu_cache_1_direct(aFilterExtraDataAddress + ivec2(4, 0)); 138 break; 139 case FILTER_DROP_SHADOW: 140 vFilterData0 = fetch_from_gpu_cache_1_direct(aFilterExtraDataAddress); 141 break; 142 case FILTER_OFFSET: 143 vec2 texture_size = vec2(TEX_SIZE(sColor0).xy); 144 vFilterData0 = vec4(-filter_task.user_data.xy / texture_size, vec2(0.0)); 145 146 RectWithEndpoint task_rect = input_1_task; 147 vec4 clipRect = vec4(task_rect.p0, task_rect.p1); 148 clipRect /= texture_size.xyxy; 149 vFilterData1 = clipRect; 150 break; 151 case FILTER_COMPONENT_TRANSFER: 152 vData = ivec4(aFilterExtraDataAddress, 0, 0); 153 break; 154 case FILTER_COMPOSITE: 155 vData = ivec4(aFilterGenericInt, 0, 0, 0); 156 if (aFilterGenericInt == COMPOSITE_ARITHMETIC) { 157 vFilterData0 = fetch_from_gpu_cache_1_direct(aFilterExtraDataAddress); 158 } 159 break; 160 default: 161 break; 162 } 163 164 gl_Position = uTransform * vec4(pos, 0.0, 1.0); 165} 166#endif 167 168#ifdef WR_FRAGMENT_SHADER 169 170#define COMPONENT_TRANSFER_IDENTITY 0 171#define COMPONENT_TRANSFER_TABLE 1 172#define COMPONENT_TRANSFER_DISCRETE 2 173#define COMPONENT_TRANSFER_LINEAR 3 174#define COMPONENT_TRANSFER_GAMMA 4 175 176vec3 Multiply(vec3 Cb, vec3 Cs) { 177 return Cb * Cs; 178} 179 180vec3 Screen(vec3 Cb, vec3 Cs) { 181 return Cb + Cs - (Cb * Cs); 182} 183 184vec3 HardLight(vec3 Cb, vec3 Cs) { 185 vec3 m = Multiply(Cb, 2.0 * Cs); 186 vec3 s = Screen(Cb, 2.0 * Cs - 1.0); 187 vec3 edge = vec3(0.5, 0.5, 0.5); 188 return mix(m, s, step(edge, Cs)); 189} 190 191// TODO: Worth doing with mix/step? Check GLSL output. 192float ColorDodge(float Cb, float Cs) { 193 if (Cb == 0.0) 194 return 0.0; 195 else if (Cs == 1.0) 196 return 1.0; 197 else 198 return min(1.0, Cb / (1.0 - Cs)); 199} 200 201// TODO: Worth doing with mix/step? Check GLSL output. 202float ColorBurn(float Cb, float Cs) { 203 if (Cb == 1.0) 204 return 1.0; 205 else if (Cs == 0.0) 206 return 0.0; 207 else 208 return 1.0 - min(1.0, (1.0 - Cb) / Cs); 209} 210 211float SoftLight(float Cb, float Cs) { 212 if (Cs <= 0.5) { 213 return Cb - (1.0 - 2.0 * Cs) * Cb * (1.0 - Cb); 214 } else { 215 float D; 216 217 if (Cb <= 0.25) 218 D = ((16.0 * Cb - 12.0) * Cb + 4.0) * Cb; 219 else 220 D = sqrt(Cb); 221 222 return Cb + (2.0 * Cs - 1.0) * (D - Cb); 223 } 224} 225 226vec3 Difference(vec3 Cb, vec3 Cs) { 227 return abs(Cb - Cs); 228} 229 230vec3 Exclusion(vec3 Cb, vec3 Cs) { 231 return Cb + Cs - 2.0 * Cb * Cs; 232} 233 234// These functions below are taken from the spec. 235// There's probably a much quicker way to implement 236// them in GLSL... 237float Sat(vec3 c) { 238 return max(c.r, max(c.g, c.b)) - min(c.r, min(c.g, c.b)); 239} 240 241float Lum(vec3 c) { 242 vec3 f = vec3(0.3, 0.59, 0.11); 243 return dot(c, f); 244} 245 246vec3 ClipColor(vec3 C) { 247 float L = Lum(C); 248 float n = min(C.r, min(C.g, C.b)); 249 float x = max(C.r, max(C.g, C.b)); 250 251 if (n < 0.0) 252 C = L + (((C - L) * L) / (L - n)); 253 254 if (x > 1.0) 255 C = L + (((C - L) * (1.0 - L)) / (x - L)); 256 257 return C; 258} 259 260vec3 SetLum(vec3 C, float l) { 261 float d = l - Lum(C); 262 return ClipColor(C + d); 263} 264 265void SetSatInner(inout float Cmin, inout float Cmid, inout float Cmax, float s) { 266 if (Cmax > Cmin) { 267 Cmid = (((Cmid - Cmin) * s) / (Cmax - Cmin)); 268 Cmax = s; 269 } else { 270 Cmid = 0.0; 271 Cmax = 0.0; 272 } 273 Cmin = 0.0; 274} 275 276vec3 SetSat(vec3 C, float s) { 277 if (C.r <= C.g) { 278 if (C.g <= C.b) { 279 SetSatInner(C.r, C.g, C.b, s); 280 } else { 281 if (C.r <= C.b) { 282 SetSatInner(C.r, C.b, C.g, s); 283 } else { 284 SetSatInner(C.b, C.r, C.g, s); 285 } 286 } 287 } else { 288 if (C.r <= C.b) { 289 SetSatInner(C.g, C.r, C.b, s); 290 } else { 291 if (C.g <= C.b) { 292 SetSatInner(C.g, C.b, C.r, s); 293 } else { 294 SetSatInner(C.b, C.g, C.r, s); 295 } 296 } 297 } 298 return C; 299} 300 301vec3 Hue(vec3 Cb, vec3 Cs) { 302 return SetLum(SetSat(Cs, Sat(Cb)), Lum(Cb)); 303} 304 305vec3 Saturation(vec3 Cb, vec3 Cs) { 306 return SetLum(SetSat(Cb, Sat(Cs)), Lum(Cb)); 307} 308 309vec3 Color(vec3 Cb, vec3 Cs) { 310 return SetLum(Cs, Lum(Cb)); 311} 312 313vec3 Luminosity(vec3 Cb, vec3 Cs) { 314 return SetLum(Cb, Lum(Cs)); 315} 316 317const int BlendMode_Normal = 0; 318const int BlendMode_Multiply = 1; 319const int BlendMode_Screen = 2; 320const int BlendMode_Overlay = 3; 321const int BlendMode_Darken = 4; 322const int BlendMode_Lighten = 5; 323const int BlendMode_ColorDodge = 6; 324const int BlendMode_ColorBurn = 7; 325const int BlendMode_HardLight = 8; 326const int BlendMode_SoftLight = 9; 327const int BlendMode_Difference = 10; 328const int BlendMode_Exclusion = 11; 329const int BlendMode_Hue = 12; 330const int BlendMode_Saturation = 13; 331const int BlendMode_Color = 14; 332const int BlendMode_Luminosity = 15; 333 334vec4 blend(vec4 Cs, vec4 Cb, int mode) { 335 vec4 result = vec4(1.0, 0.0, 0.0, 1.0); 336 337 switch (mode) { 338 case BlendMode_Normal: 339 result.rgb = Cs.rgb; 340 break; 341 case BlendMode_Multiply: 342 result.rgb = Multiply(Cb.rgb, Cs.rgb); 343 break; 344 case BlendMode_Screen: 345 result.rgb = Screen(Cb.rgb, Cs.rgb); 346 break; 347 case BlendMode_Overlay: 348 // Overlay is inverse of Hardlight 349 result.rgb = HardLight(Cs.rgb, Cb.rgb); 350 break; 351 case BlendMode_Darken: 352 result.rgb = min(Cs.rgb, Cb.rgb); 353 break; 354 case BlendMode_Lighten: 355 result.rgb = max(Cs.rgb, Cb.rgb); 356 break; 357 case BlendMode_ColorDodge: 358 result.r = ColorDodge(Cb.r, Cs.r); 359 result.g = ColorDodge(Cb.g, Cs.g); 360 result.b = ColorDodge(Cb.b, Cs.b); 361 break; 362 case BlendMode_ColorBurn: 363 result.r = ColorBurn(Cb.r, Cs.r); 364 result.g = ColorBurn(Cb.g, Cs.g); 365 result.b = ColorBurn(Cb.b, Cs.b); 366 break; 367 case BlendMode_HardLight: 368 result.rgb = HardLight(Cb.rgb, Cs.rgb); 369 break; 370 case BlendMode_SoftLight: 371 result.r = SoftLight(Cb.r, Cs.r); 372 result.g = SoftLight(Cb.g, Cs.g); 373 result.b = SoftLight(Cb.b, Cs.b); 374 break; 375 case BlendMode_Difference: 376 result.rgb = Difference(Cb.rgb, Cs.rgb); 377 break; 378 case BlendMode_Exclusion: 379 result.rgb = Exclusion(Cb.rgb, Cs.rgb); 380 break; 381 case BlendMode_Hue: 382 result.rgb = Hue(Cb.rgb, Cs.rgb); 383 break; 384 case BlendMode_Saturation: 385 result.rgb = Saturation(Cb.rgb, Cs.rgb); 386 break; 387 case BlendMode_Color: 388 result.rgb = Color(Cb.rgb, Cs.rgb); 389 break; 390 case BlendMode_Luminosity: 391 result.rgb = Luminosity(Cb.rgb, Cs.rgb); 392 break; 393 default: break; 394 } 395 vec3 rgb = (1.0 - Cb.a) * Cs.rgb + Cb.a * result.rgb; 396 result = mix(vec4(Cb.rgb * Cb.a, Cb.a), vec4(rgb, 1.0), Cs.a); 397 return result; 398} 399 400// Based on the Gecko's implementation in 401// https://hg.mozilla.org/mozilla-central/file/91b4c3687d75/gfx/src/FilterSupport.cpp#l24 402// These could be made faster by sampling a lookup table stored in a float texture 403// with linear interpolation. 404 405vec3 SrgbToLinear(vec3 color) { 406 vec3 c1 = color / 12.92; 407 vec3 c2 = pow(color / 1.055 + vec3(0.055 / 1.055), vec3(2.4)); 408 return if_then_else(lessThanEqual(color, vec3(0.04045)), c1, c2); 409} 410 411vec3 LinearToSrgb(vec3 color) { 412 vec3 c1 = color * 12.92; 413 vec3 c2 = vec3(1.055) * pow(color, vec3(1.0 / 2.4)) - vec3(0.055); 414 return if_then_else(lessThanEqual(color, vec3(0.0031308)), c1, c2); 415} 416 417// This function has to be factored out due to the following issue: 418// 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 419// (and now the words "default: default:" so angle_shader_validation.rs passes) 420vec4 ComponentTransfer(vec4 colora) { 421 // We push a different amount of data to the gpu cache depending on the 422 // function type. 423 // Identity => 0 blocks 424 // Table/Discrete => 64 blocks (256 values) 425 // Linear => 1 block (2 values) 426 // Gamma => 1 block (3 values) 427 // We loop through the color components and increment the offset (for the 428 // next color component) into the gpu cache based on how many blocks that 429 // function type put into the gpu cache. 430 // Table/Discrete use a 256 entry look up table. 431 // Linear/Gamma are a simple calculation. 432 int offset = 0; 433 vec4 texel; 434 int k; 435 436 // Dynamically indexing a vector is buggy on some devices, so use a temporary array. 437 int[4] funcs = int[4](vFuncs.r, vFuncs.g, vFuncs.b, vFuncs.a); 438 for (int i = 0; i < 4; i++) { 439 switch (funcs[i]) { 440 case COMPONENT_TRANSFER_IDENTITY: 441 break; 442 case COMPONENT_TRANSFER_TABLE: 443 case COMPONENT_TRANSFER_DISCRETE: 444 // fetch value from lookup table 445 k = int(floor(colora[i]*255.0)); 446 texel = fetch_from_gpu_cache_1_direct(vData.xy + ivec2(offset + k/4, 0)); 447 colora[i] = clamp(texel[k % 4], 0.0, 1.0); 448 // offset plus 256/4 blocks 449 offset = offset + 64; 450 break; 451 case COMPONENT_TRANSFER_LINEAR: 452 // fetch the two values for use in the linear equation 453 texel = fetch_from_gpu_cache_1_direct(vData.xy + ivec2(offset, 0)); 454 colora[i] = clamp(texel[0] * colora[i] + texel[1], 0.0, 1.0); 455 // offset plus 1 block 456 offset = offset + 1; 457 break; 458 case COMPONENT_TRANSFER_GAMMA: 459 // fetch the three values for use in the gamma equation 460 texel = fetch_from_gpu_cache_1_direct(vData.xy + ivec2(offset, 0)); 461 colora[i] = clamp(texel[0] * pow(colora[i], texel[1]) + texel[2], 0.0, 1.0); 462 // offset plus 1 block 463 offset = offset + 1; 464 break; 465 default: 466 // shouldn't happen 467 break; 468 } 469 } 470 return colora; 471} 472 473// Composite Filter 474 475vec4 composite(vec4 Cs, vec4 Cb, int mode) { 476 vec4 Cr = vec4(0.0, 1.0, 0.0, 1.0); 477 switch (mode) { 478 case COMPOSITE_OVER: 479 Cr.rgb = Cs.a * Cs.rgb + Cb.a * Cb.rgb * (1.0 - Cs.a); 480 Cr.a = Cs.a + Cb.a * (1.0 - Cs.a); 481 break; 482 case COMPOSITE_IN: 483 Cr.rgb = Cs.a * Cs.rgb * Cb.a; 484 Cr.a = Cs.a * Cb.a; 485 break; 486 case COMPOSITE_OUT: 487 Cr.rgb = Cs.a * Cs.rgb * (1.0 - Cb.a); 488 Cr.a = Cs.a * (1.0 - Cb.a); 489 break; 490 case COMPOSITE_ATOP: 491 Cr.rgb = Cs.a * Cs.rgb * Cb.a + Cb.a * Cb.rgb * (1.0 - Cs.a); 492 Cr.a = Cs.a * Cb.a + Cb.a * (1.0 - Cs.a); 493 break; 494 case COMPOSITE_XOR: 495 Cr.rgb = Cs.a * Cs.rgb * (1.0 - Cb.a) + Cb.a * Cb.rgb * (1.0 - Cs.a); 496 Cr.a = Cs.a * (1.0 - Cb.a) + Cb.a * (1.0 - Cs.a); 497 break; 498 case COMPOSITE_LIGHTER: 499 Cr.rgb = Cs.a * Cs.rgb + Cb.a * Cb.rgb; 500 Cr.a = Cs.a + Cb.a; 501 Cr = clamp(Cr, vec4(0.0), vec4(1.0)); 502 break; 503 case COMPOSITE_ARITHMETIC: 504 Cr = vec4(vFilterData0.x) * Cs * Cb + vec4(vFilterData0.y) * Cs + vec4(vFilterData0.z) * Cb + vec4(vFilterData0.w); 505 Cr = clamp(Cr, vec4(0.0), vec4(1.0)); 506 break; 507 default: 508 break; 509 } 510 return Cr; 511} 512 513vec4 sampleInUvRect(sampler2D sampler, vec2 uv, vec4 uvRect) { 514 vec2 clamped = clamp(uv.xy, uvRect.xy, uvRect.zw); 515 return texture(sampler, clamped); 516} 517 518void main(void) { 519 vec4 Ca = vec4(0.0, 0.0, 0.0, 0.0); 520 vec4 Cb = vec4(0.0, 0.0, 0.0, 0.0); 521 if (vFilterInputCount > 0) { 522 Ca = sampleInUvRect(sColor0, vInput1Uv, vInput1UvRect); 523 if (Ca.a != 0.0) { 524 Ca.rgb /= Ca.a; 525 } 526 } 527 if (vFilterInputCount > 1) { 528 Cb = sampleInUvRect(sColor1, vInput2Uv, vInput2UvRect); 529 if (Cb.a != 0.0) { 530 Cb.rgb /= Cb.a; 531 } 532 } 533 534 vec4 result = vec4(1.0, 0.0, 0.0, 1.0); 535 536 bool needsPremul = true; 537 538 switch (vFilterKind) { 539 case FILTER_BLEND: 540 result = blend(Ca, Cb, vData.x); 541 needsPremul = false; 542 break; 543 case FILTER_FLOOD: 544 result = vFilterData0; 545 needsPremul = false; 546 break; 547 case FILTER_LINEAR_TO_SRGB: 548 result.rgb = LinearToSrgb(Ca.rgb); 549 result.a = Ca.a; 550 break; 551 case FILTER_SRGB_TO_LINEAR: 552 result.rgb = SrgbToLinear(Ca.rgb); 553 result.a = Ca.a; 554 break; 555 case FILTER_OPACITY: 556 result.rgb = Ca.rgb; 557 result.a = Ca.a * vFloat0.x; 558 break; 559 case FILTER_COLOR_MATRIX: 560 result = vColorMat * Ca + vFilterData0; 561 result = clamp(result, vec4(0.0), vec4(1.0)); 562 break; 563 case FILTER_DROP_SHADOW: 564 vec4 shadow = vec4(vFilterData0.rgb, Cb.a * vFilterData0.a); 565 // Normal blend + source-over coposite 566 result = blend(Ca, shadow, BlendMode_Normal); 567 needsPremul = false; 568 break; 569 case FILTER_OFFSET: 570 vec2 offsetUv = vInput1Uv + vFilterData0.xy; 571 result = sampleInUvRect(sColor0, offsetUv, vInput1UvRect); 572 result *= point_inside_rect(offsetUv, vFilterData1.xy, vFilterData1.zw); 573 needsPremul = false; 574 break; 575 case FILTER_COMPONENT_TRANSFER: 576 result = ComponentTransfer(Ca); 577 break; 578 case FILTER_IDENTITY: 579 result = Ca; 580 break; 581 case FILTER_COMPOSITE: 582 result = composite(Ca, Cb, vData.x); 583 needsPremul = false; 584 default: 585 break; 586 } 587 588 if (needsPremul) { 589 result.rgb *= result.a; 590 } 591 592 oFragColor = result; 593} 594#endif 595