Source code

Revision control

Copy as Markdown

Other Tools

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#define WR_FEATURE_TEXTURE_2D
#include shared,prim_shared
varying highp vec2 vInput1Uv;
varying highp vec2 vInput2Uv;
flat varying highp vec4 vInput1UvRect;
flat varying highp vec4 vInput2UvRect;
flat varying mediump ivec4 vData;
flat varying mediump vec4 vFilterData0;
flat varying mediump vec4 vFilterData1;
// x: Filter input count, y: Filter kind.
// Packed in to a vector to work around bug 1630356.
flat varying mediump ivec2 vFilterInputCountFilterKindVec;
#define vFilterInputCount vFilterInputCountFilterKindVec.x
#define vFilterKind vFilterInputCountFilterKindVec.y
// Packed in to a vector to work around bug 1630356.
flat varying mediump vec2 vFloat0;
flat varying mediump mat4 vColorMat;
flat varying mediump ivec4 vFuncs;
#define FILTER_BLEND 0
#define FILTER_FLOOD 1
#define FILTER_LINEAR_TO_SRGB 2
#define FILTER_SRGB_TO_LINEAR 3
#define FILTER_OPACITY 4
#define FILTER_COLOR_MATRIX 5
#define FILTER_DROP_SHADOW 6
#define FILTER_OFFSET 7
#define FILTER_COMPONENT_TRANSFER 8
#define FILTER_IDENTITY 9
#define FILTER_COMPOSITE 10
#define COMPOSITE_OVER 0
#define COMPOSITE_IN 1
#define COMPOSITE_OUT 2
#define COMPOSITE_ATOP 3
#define COMPOSITE_XOR 4
#define COMPOSITE_LIGHTER 5
#define COMPOSITE_ARITHMETIC 6
#ifdef WR_VERTEX_SHADER
PER_INSTANCE in int aFilterRenderTaskAddress;
PER_INSTANCE in int aFilterInput1TaskAddress;
PER_INSTANCE in int aFilterInput2TaskAddress;
PER_INSTANCE in int aFilterKind;
PER_INSTANCE in int aFilterInputCount;
PER_INSTANCE in int aFilterGenericInt;
PER_INSTANCE in ivec2 aFilterExtraDataAddress;
struct FilterTask {
RectWithEndpoint task_rect;
vec3 user_data;
};
FilterTask fetch_filter_task(int address) {
RenderTaskData task_data = fetch_render_task_data(address);
FilterTask task = FilterTask(
task_data.task_rect,
task_data.user_data.xyz
);
return task;
}
vec4 compute_uv_rect(RectWithEndpoint task_rect, vec2 texture_size) {
vec4 uvRect = vec4(task_rect.p0 + vec2(0.5),
task_rect.p1 - vec2(0.5));
uvRect /= texture_size.xyxy;
return uvRect;
}
vec2 compute_uv(RectWithEndpoint task_rect, vec2 texture_size) {
vec2 uv0 = task_rect.p0 / texture_size;
vec2 uv1 = floor(task_rect.p1) / texture_size;
return mix(uv0, uv1, aPosition.xy);
}
void main(void) {
FilterTask filter_task = fetch_filter_task(aFilterRenderTaskAddress);
RectWithEndpoint target_rect = filter_task.task_rect;
vec2 pos = mix(target_rect.p0, target_rect.p1, aPosition.xy);
RectWithEndpoint input_1_task;
if (aFilterInputCount > 0) {
vec2 texture_size = vec2(TEX_SIZE(sColor0).xy);
input_1_task = fetch_render_task_rect(aFilterInput1TaskAddress);
vInput1UvRect = compute_uv_rect(input_1_task, texture_size);
vInput1Uv = compute_uv(input_1_task, texture_size);
}
RectWithEndpoint input_2_task;
if (aFilterInputCount > 1) {
vec2 texture_size = vec2(TEX_SIZE(sColor1).xy);
input_2_task = fetch_render_task_rect(aFilterInput2TaskAddress);
vInput2UvRect = compute_uv_rect(input_2_task, texture_size);
vInput2Uv = compute_uv(input_2_task, texture_size);
}
vFilterInputCount = aFilterInputCount;
vFilterKind = aFilterKind;
// This assignment is only used for component transfer filters but this
// assignment has to be done here and not in the component transfer case
// below because it doesn't get executed on Windows because of a suspected
// miscompile of this shader on Windows. See
// default: just to satisfy angle_shader_validation.rs which needs one
// default: for every switch, even in comments.
vFuncs.r = (aFilterGenericInt >> 12) & 0xf; // R
vFuncs.g = (aFilterGenericInt >> 8) & 0xf; // G
vFuncs.b = (aFilterGenericInt >> 4) & 0xf; // B
vFuncs.a = (aFilterGenericInt) & 0xf; // A
switch (aFilterKind) {
case FILTER_BLEND:
vData = ivec4(aFilterGenericInt, 0, 0, 0);
break;
case FILTER_FLOOD:
vFilterData0 = fetch_from_gpu_cache_1_direct(aFilterExtraDataAddress);
break;
case FILTER_OPACITY:
vFloat0.x = filter_task.user_data.x;
break;
case FILTER_COLOR_MATRIX:
vec4 mat_data[4] = fetch_from_gpu_cache_4_direct(aFilterExtraDataAddress);
vColorMat = mat4(mat_data[0], mat_data[1], mat_data[2], mat_data[3]);
vFilterData0 = fetch_from_gpu_cache_1_direct(aFilterExtraDataAddress + ivec2(4, 0));
break;
case FILTER_DROP_SHADOW:
vFilterData0 = fetch_from_gpu_cache_1_direct(aFilterExtraDataAddress);
break;
case FILTER_OFFSET:
vec2 texture_size = vec2(TEX_SIZE(sColor0).xy);
vFilterData0 = vec4(-filter_task.user_data.xy / texture_size, vec2(0.0));
RectWithEndpoint task_rect = input_1_task;
vec4 clipRect = vec4(task_rect.p0, task_rect.p1);
clipRect /= texture_size.xyxy;
vFilterData1 = clipRect;
break;
case FILTER_COMPONENT_TRANSFER:
vData = ivec4(aFilterExtraDataAddress, 0, 0);
break;
case FILTER_COMPOSITE:
vData = ivec4(aFilterGenericInt, 0, 0, 0);
if (aFilterGenericInt == COMPOSITE_ARITHMETIC) {
vFilterData0 = fetch_from_gpu_cache_1_direct(aFilterExtraDataAddress);
}
break;
default:
break;
}
gl_Position = uTransform * vec4(pos, 0.0, 1.0);
}
#endif
#ifdef WR_FRAGMENT_SHADER
#define COMPONENT_TRANSFER_IDENTITY 0
#define COMPONENT_TRANSFER_TABLE 1
#define COMPONENT_TRANSFER_DISCRETE 2
#define COMPONENT_TRANSFER_LINEAR 3
#define COMPONENT_TRANSFER_GAMMA 4
vec3 Multiply(vec3 Cb, vec3 Cs) {
return Cb * Cs;
}
vec3 Screen(vec3 Cb, vec3 Cs) {
return Cb + Cs - (Cb * Cs);
}
vec3 HardLight(vec3 Cb, vec3 Cs) {
vec3 m = Multiply(Cb, 2.0 * Cs);
vec3 s = Screen(Cb, 2.0 * Cs - 1.0);
vec3 edge = vec3(0.5, 0.5, 0.5);
return mix(m, s, step(edge, Cs));
}
// TODO: Worth doing with mix/step? Check GLSL output.
float ColorDodge(float Cb, float Cs) {
if (Cb == 0.0)
return 0.0;
else if (Cs == 1.0)
return 1.0;
else
return min(1.0, Cb / (1.0 - Cs));
}
// TODO: Worth doing with mix/step? Check GLSL output.
float ColorBurn(float Cb, float Cs) {
if (Cb == 1.0)
return 1.0;
else if (Cs == 0.0)
return 0.0;
else
return 1.0 - min(1.0, (1.0 - Cb) / Cs);
}
float SoftLight(float Cb, float Cs) {
if (Cs <= 0.5) {
return Cb - (1.0 - 2.0 * Cs) * Cb * (1.0 - Cb);
} else {
float D;
if (Cb <= 0.25)
D = ((16.0 * Cb - 12.0) * Cb + 4.0) * Cb;
else
D = sqrt(Cb);
return Cb + (2.0 * Cs - 1.0) * (D - Cb);
}
}
vec3 Difference(vec3 Cb, vec3 Cs) {
return abs(Cb - Cs);
}
vec3 Exclusion(vec3 Cb, vec3 Cs) {
return Cb + Cs - 2.0 * Cb * Cs;
}
// These functions below are taken from the spec.
// There's probably a much quicker way to implement
// them in GLSL...
float Sat(vec3 c) {
return max(c.r, max(c.g, c.b)) - min(c.r, min(c.g, c.b));
}
float Lum(vec3 c) {
vec3 f = vec3(0.3, 0.59, 0.11);
return dot(c, f);
}
vec3 ClipColor(vec3 C) {
float L = Lum(C);
float n = min(C.r, min(C.g, C.b));
float x = max(C.r, max(C.g, C.b));
if (n < 0.0)
C = L + (((C - L) * L) / (L - n));
if (x > 1.0)
C = L + (((C - L) * (1.0 - L)) / (x - L));
return C;
}
vec3 SetLum(vec3 C, float l) {
float d = l - Lum(C);
return ClipColor(C + d);
}
void SetSatInner(inout float Cmin, inout float Cmid, inout float Cmax, float s) {
if (Cmax > Cmin) {
Cmid = (((Cmid - Cmin) * s) / (Cmax - Cmin));
Cmax = s;
} else {
Cmid = 0.0;
Cmax = 0.0;
}
Cmin = 0.0;
}
vec3 SetSat(vec3 C, float s) {
if (C.r <= C.g) {
if (C.g <= C.b) {
SetSatInner(C.r, C.g, C.b, s);
} else {
if (C.r <= C.b) {
SetSatInner(C.r, C.b, C.g, s);
} else {
SetSatInner(C.b, C.r, C.g, s);
}
}
} else {
if (C.r <= C.b) {
SetSatInner(C.g, C.r, C.b, s);
} else {
if (C.g <= C.b) {
SetSatInner(C.g, C.b, C.r, s);
} else {
SetSatInner(C.b, C.g, C.r, s);
}
}
}
return C;
}
vec3 Hue(vec3 Cb, vec3 Cs) {
return SetLum(SetSat(Cs, Sat(Cb)), Lum(Cb));
}
vec3 Saturation(vec3 Cb, vec3 Cs) {
return SetLum(SetSat(Cb, Sat(Cs)), Lum(Cb));
}
vec3 Color(vec3 Cb, vec3 Cs) {
return SetLum(Cs, Lum(Cb));
}
vec3 Luminosity(vec3 Cb, vec3 Cs) {
return SetLum(Cb, Lum(Cs));
}
const int BlendMode_Normal = 0;
const int BlendMode_Multiply = 1;
const int BlendMode_Screen = 2;
const int BlendMode_Overlay = 3;
const int BlendMode_Darken = 4;
const int BlendMode_Lighten = 5;
const int BlendMode_ColorDodge = 6;
const int BlendMode_ColorBurn = 7;
const int BlendMode_HardLight = 8;
const int BlendMode_SoftLight = 9;
const int BlendMode_Difference = 10;
const int BlendMode_Exclusion = 11;
const int BlendMode_Hue = 12;
const int BlendMode_Saturation = 13;
const int BlendMode_Color = 14;
const int BlendMode_Luminosity = 15;
vec4 blend(vec4 Cs, vec4 Cb, int mode) {
vec4 result = vec4(1.0, 0.0, 0.0, 1.0);
switch (mode) {
case BlendMode_Normal:
result.rgb = Cs.rgb;
break;
case BlendMode_Multiply:
result.rgb = Multiply(Cb.rgb, Cs.rgb);
break;
case BlendMode_Screen:
result.rgb = Screen(Cb.rgb, Cs.rgb);
break;
case BlendMode_Overlay:
// Overlay is inverse of Hardlight
result.rgb = HardLight(Cs.rgb, Cb.rgb);
break;
case BlendMode_Darken:
result.rgb = min(Cs.rgb, Cb.rgb);
break;
case BlendMode_Lighten:
result.rgb = max(Cs.rgb, Cb.rgb);
break;
case BlendMode_ColorDodge:
result.r = ColorDodge(Cb.r, Cs.r);
result.g = ColorDodge(Cb.g, Cs.g);
result.b = ColorDodge(Cb.b, Cs.b);
break;
case BlendMode_ColorBurn:
result.r = ColorBurn(Cb.r, Cs.r);
result.g = ColorBurn(Cb.g, Cs.g);
result.b = ColorBurn(Cb.b, Cs.b);
break;
case BlendMode_HardLight:
result.rgb = HardLight(Cb.rgb, Cs.rgb);
break;
case BlendMode_SoftLight:
result.r = SoftLight(Cb.r, Cs.r);
result.g = SoftLight(Cb.g, Cs.g);
result.b = SoftLight(Cb.b, Cs.b);
break;
case BlendMode_Difference:
result.rgb = Difference(Cb.rgb, Cs.rgb);
break;
case BlendMode_Exclusion:
result.rgb = Exclusion(Cb.rgb, Cs.rgb);
break;
case BlendMode_Hue:
result.rgb = Hue(Cb.rgb, Cs.rgb);
break;
case BlendMode_Saturation:
result.rgb = Saturation(Cb.rgb, Cs.rgb);
break;
case BlendMode_Color:
result.rgb = Color(Cb.rgb, Cs.rgb);
break;
case BlendMode_Luminosity:
result.rgb = Luminosity(Cb.rgb, Cs.rgb);
break;
default: break;
}
vec3 rgb = (1.0 - Cb.a) * Cs.rgb + Cb.a * result.rgb;
result = mix(vec4(Cb.rgb * Cb.a, Cb.a), vec4(rgb, 1.0), Cs.a);
return result;
}
// Based on the Gecko's implementation in
// These could be made faster by sampling a lookup table stored in a float texture
// with linear interpolation.
vec3 SrgbToLinear(vec3 color) {
vec3 c1 = color / 12.92;
vec3 c2 = pow(color / 1.055 + vec3(0.055 / 1.055), vec3(2.4));
return if_then_else(lessThanEqual(color, vec3(0.04045)), c1, c2);
}
vec3 LinearToSrgb(vec3 color) {
vec3 c1 = color * 12.92;
vec3 c2 = vec3(1.055) * pow(color, vec3(1.0 / 2.4)) - vec3(0.055);
return if_then_else(lessThanEqual(color, vec3(0.0031308)), c1, c2);
}
// This function has to be factored out due to the following issue:
// (and now the words "default: default:" so angle_shader_validation.rs passes)
vec4 ComponentTransfer(vec4 colora) {
// We push a different amount of data to the gpu cache depending on the
// function type.
// Identity => 0 blocks
// Table/Discrete => 64 blocks (256 values)
// Linear => 1 block (2 values)
// Gamma => 1 block (3 values)
// We loop through the color components and increment the offset (for the
// next color component) into the gpu cache based on how many blocks that
// function type put into the gpu cache.
// Table/Discrete use a 256 entry look up table.
// Linear/Gamma are a simple calculation.
int offset = 0;
vec4 texel;
int k;
// Dynamically indexing a vector is buggy on some devices, so use a temporary array.
int[4] funcs = int[4](vFuncs.r, vFuncs.g, vFuncs.b, vFuncs.a);
for (int i = 0; i < 4; i++) {
switch (funcs[i]) {
case COMPONENT_TRANSFER_IDENTITY:
break;
case COMPONENT_TRANSFER_TABLE:
case COMPONENT_TRANSFER_DISCRETE:
// fetch value from lookup table
k = int(floor(colora[i]*255.0 + 0.5));
texel = fetch_from_gpu_cache_1_direct(vData.xy + ivec2(offset + k/4, 0));
colora[i] = clamp(texel[k % 4], 0.0, 1.0);
// offset plus 256/4 blocks
offset = offset + 64;
break;
case COMPONENT_TRANSFER_LINEAR:
// fetch the two values for use in the linear equation
texel = fetch_from_gpu_cache_1_direct(vData.xy + ivec2(offset, 0));
colora[i] = clamp(texel[0] * colora[i] + texel[1], 0.0, 1.0);
// offset plus 1 block
offset = offset + 1;
break;
case COMPONENT_TRANSFER_GAMMA:
// fetch the three values for use in the gamma equation
texel = fetch_from_gpu_cache_1_direct(vData.xy + ivec2(offset, 0));
colora[i] = clamp(texel[0] * pow(colora[i], texel[1]) + texel[2], 0.0, 1.0);
// offset plus 1 block
offset = offset + 1;
break;
default:
// shouldn't happen
break;
}
}
return colora;
}
// Composite Filter
vec4 composite(vec4 Cs, vec4 Cb, int mode) {
vec4 Cr = vec4(0.0, 1.0, 0.0, 1.0);
switch (mode) {
case COMPOSITE_OVER:
Cr.rgb = Cs.a * Cs.rgb + Cb.a * Cb.rgb * (1.0 - Cs.a);
Cr.a = Cs.a + Cb.a * (1.0 - Cs.a);
break;
case COMPOSITE_IN:
Cr.rgb = Cs.a * Cs.rgb * Cb.a;
Cr.a = Cs.a * Cb.a;
break;
case COMPOSITE_OUT:
Cr.rgb = Cs.a * Cs.rgb * (1.0 - Cb.a);
Cr.a = Cs.a * (1.0 - Cb.a);
break;
case COMPOSITE_ATOP:
Cr.rgb = Cs.a * Cs.rgb * Cb.a + Cb.a * Cb.rgb * (1.0 - Cs.a);
Cr.a = Cs.a * Cb.a + Cb.a * (1.0 - Cs.a);
break;
case COMPOSITE_XOR:
Cr.rgb = Cs.a * Cs.rgb * (1.0 - Cb.a) + Cb.a * Cb.rgb * (1.0 - Cs.a);
Cr.a = Cs.a * (1.0 - Cb.a) + Cb.a * (1.0 - Cs.a);
break;
case COMPOSITE_LIGHTER:
Cr.rgb = Cs.a * Cs.rgb + Cb.a * Cb.rgb;
Cr.a = Cs.a + Cb.a;
Cr = clamp(Cr, vec4(0.0), vec4(1.0));
break;
case COMPOSITE_ARITHMETIC:
Cr = vec4(vFilterData0.x) * Cs * Cb + vec4(vFilterData0.y) * Cs + vec4(vFilterData0.z) * Cb + vec4(vFilterData0.w);
Cr = clamp(Cr, vec4(0.0), vec4(1.0));
break;
default:
break;
}
return Cr;
}
vec4 sampleInUvRect(sampler2D sampler, vec2 uv, vec4 uvRect) {
vec2 clamped = clamp(uv.xy, uvRect.xy, uvRect.zw);
return texture(sampler, clamped);
}
void main(void) {
vec4 Ca = vec4(0.0, 0.0, 0.0, 0.0);
vec4 Cb = vec4(0.0, 0.0, 0.0, 0.0);
if (vFilterInputCount > 0) {
Ca = sampleInUvRect(sColor0, vInput1Uv, vInput1UvRect);
if (Ca.a != 0.0) {
Ca.rgb /= Ca.a;
}
}
if (vFilterInputCount > 1) {
Cb = sampleInUvRect(sColor1, vInput2Uv, vInput2UvRect);
if (Cb.a != 0.0) {
Cb.rgb /= Cb.a;
}
}
vec4 result = vec4(1.0, 0.0, 0.0, 1.0);
bool needsPremul = true;
switch (vFilterKind) {
case FILTER_BLEND:
result = blend(Ca, Cb, vData.x);
needsPremul = false;
break;
case FILTER_FLOOD:
result = vFilterData0;
needsPremul = false;
break;
case FILTER_LINEAR_TO_SRGB:
result.rgb = LinearToSrgb(Ca.rgb);
result.a = Ca.a;
break;
case FILTER_SRGB_TO_LINEAR:
result.rgb = SrgbToLinear(Ca.rgb);
result.a = Ca.a;
break;
case FILTER_OPACITY:
result.rgb = Ca.rgb;
result.a = Ca.a * vFloat0.x;
break;
case FILTER_COLOR_MATRIX:
result = vColorMat * Ca + vFilterData0;
result = clamp(result, vec4(0.0), vec4(1.0));
break;
case FILTER_DROP_SHADOW:
vec4 shadow = vec4(vFilterData0.rgb, Cb.a * vFilterData0.a);
// Normal blend + source-over coposite
result = blend(Ca, shadow, BlendMode_Normal);
needsPremul = false;
break;
case FILTER_OFFSET:
vec2 offsetUv = vInput1Uv + vFilterData0.xy;
result = sampleInUvRect(sColor0, offsetUv, vInput1UvRect);
result *= point_inside_rect(offsetUv, vFilterData1.xy, vFilterData1.zw);
needsPremul = false;
break;
case FILTER_COMPONENT_TRANSFER:
result = ComponentTransfer(Ca);
break;
case FILTER_IDENTITY:
result = Ca;
break;
case FILTER_COMPOSITE:
result = composite(Ca, Cb, vData.x);
needsPremul = false;
default:
break;
}
if (needsPremul) {
result.rgb *= result.a;
}
oFragColor = result;
}
#endif