2020-06-23 15:27:04 -04:00
|
|
|
|
// pathfinder/shaders/tile_fragment.inc.glsl
|
2020-03-19 19:44:45 -04:00
|
|
|
|
//
|
|
|
|
|
// Copyright © 2020 The Pathfinder Project Developers.
|
|
|
|
|
//
|
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
|
// except according to those terms.
|
|
|
|
|
|
|
|
|
|
// Mask UV 0 Mask UV 1
|
|
|
|
|
// + +
|
|
|
|
|
// | |
|
|
|
|
|
// +-----v-----+ +-----v-----+
|
|
|
|
|
// | | MIN | |
|
|
|
|
|
// | Mask 0 +-----> Mask 1 +------+
|
|
|
|
|
// | | | | |
|
|
|
|
|
// +-----------+ +-----------+ v +-------------+
|
|
|
|
|
// Apply | | GPU
|
|
|
|
|
// Mask +----> Composite +---->Blender
|
|
|
|
|
// ^ | |
|
|
|
|
|
// +-----------+ +-----------+ | +-------------+
|
|
|
|
|
// | | | | |
|
|
|
|
|
// | Color 0 +-----> Color 1 +------+
|
|
|
|
|
// | Filter | × | |
|
|
|
|
|
// | | | |
|
|
|
|
|
// +-----^-----+ +-----^-----+
|
|
|
|
|
// | |
|
|
|
|
|
// + +
|
|
|
|
|
// Color UV 0 Color UV 1
|
|
|
|
|
|
|
|
|
|
#define FRAC_6_PI 1.9098593171027443
|
|
|
|
|
#define FRAC_PI_3 1.0471975511965976
|
|
|
|
|
|
2020-04-22 14:04:16 -04:00
|
|
|
|
#define TILE_CTRL_MASK_MASK 0x3
|
|
|
|
|
#define TILE_CTRL_MASK_WINDING 0x1
|
|
|
|
|
#define TILE_CTRL_MASK_EVEN_ODD 0x2
|
|
|
|
|
|
|
|
|
|
#define TILE_CTRL_MASK_0_SHIFT 0
|
2020-03-19 19:44:45 -04:00
|
|
|
|
|
2020-04-13 14:52:10 -04:00
|
|
|
|
#define COMBINER_CTRL_COLOR_COMBINE_MASK 0x3
|
|
|
|
|
#define COMBINER_CTRL_COLOR_COMBINE_SRC_IN 0x1
|
|
|
|
|
#define COMBINER_CTRL_COLOR_COMBINE_DEST_IN 0x2
|
2020-03-19 19:44:45 -04:00
|
|
|
|
|
2020-07-21 12:56:47 -04:00
|
|
|
|
#define COMBINER_CTRL_FILTER_MASK 0xf
|
2020-03-19 19:44:45 -04:00
|
|
|
|
#define COMBINER_CTRL_FILTER_RADIAL_GRADIENT 0x1
|
|
|
|
|
#define COMBINER_CTRL_FILTER_TEXT 0x2
|
|
|
|
|
#define COMBINER_CTRL_FILTER_BLUR 0x3
|
2020-07-21 12:56:47 -04:00
|
|
|
|
#define COMBINER_CTRL_FILTER_COLOR_MATRIX 0x4
|
2020-03-19 19:44:45 -04:00
|
|
|
|
|
|
|
|
|
#define COMBINER_CTRL_COMPOSITE_MASK 0xf
|
|
|
|
|
#define COMBINER_CTRL_COMPOSITE_NORMAL 0x0
|
|
|
|
|
#define COMBINER_CTRL_COMPOSITE_MULTIPLY 0x1
|
|
|
|
|
#define COMBINER_CTRL_COMPOSITE_SCREEN 0x2
|
|
|
|
|
#define COMBINER_CTRL_COMPOSITE_OVERLAY 0x3
|
|
|
|
|
#define COMBINER_CTRL_COMPOSITE_DARKEN 0x4
|
|
|
|
|
#define COMBINER_CTRL_COMPOSITE_LIGHTEN 0x5
|
|
|
|
|
#define COMBINER_CTRL_COMPOSITE_COLOR_DODGE 0x6
|
|
|
|
|
#define COMBINER_CTRL_COMPOSITE_COLOR_BURN 0x7
|
|
|
|
|
#define COMBINER_CTRL_COMPOSITE_HARD_LIGHT 0x8
|
|
|
|
|
#define COMBINER_CTRL_COMPOSITE_SOFT_LIGHT 0x9
|
|
|
|
|
#define COMBINER_CTRL_COMPOSITE_DIFFERENCE 0xa
|
|
|
|
|
#define COMBINER_CTRL_COMPOSITE_EXCLUSION 0xb
|
|
|
|
|
#define COMBINER_CTRL_COMPOSITE_HUE 0xc
|
|
|
|
|
#define COMBINER_CTRL_COMPOSITE_SATURATION 0xd
|
|
|
|
|
#define COMBINER_CTRL_COMPOSITE_COLOR 0xe
|
|
|
|
|
#define COMBINER_CTRL_COMPOSITE_LUMINOSITY 0xf
|
|
|
|
|
|
2020-04-13 14:52:10 -04:00
|
|
|
|
#define COMBINER_CTRL_COLOR_FILTER_SHIFT 4
|
2020-07-21 12:56:47 -04:00
|
|
|
|
#define COMBINER_CTRL_COLOR_COMBINE_SHIFT 8
|
|
|
|
|
#define COMBINER_CTRL_COMPOSITE_SHIFT 10
|
2020-03-19 19:44:45 -04:00
|
|
|
|
|
|
|
|
|
// Color sampling
|
|
|
|
|
|
|
|
|
|
vec4 sampleColor(sampler2D colorTexture, vec2 colorTexCoord) {
|
|
|
|
|
return texture(colorTexture, colorTexCoord);
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-13 14:52:10 -04:00
|
|
|
|
// Color combining
|
|
|
|
|
|
|
|
|
|
vec4 combineColor0(vec4 destColor, vec4 srcColor, int op) {
|
|
|
|
|
switch (op) {
|
|
|
|
|
case COMBINER_CTRL_COLOR_COMBINE_SRC_IN:
|
|
|
|
|
return vec4(srcColor.rgb, srcColor.a * destColor.a);
|
|
|
|
|
case COMBINER_CTRL_COLOR_COMBINE_DEST_IN:
|
|
|
|
|
return vec4(destColor.rgb, srcColor.a * destColor.a);
|
|
|
|
|
}
|
|
|
|
|
return destColor;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-19 19:44:45 -04:00
|
|
|
|
// Text filter
|
|
|
|
|
|
|
|
|
|
float filterTextSample1Tap(float offset, sampler2D colorTexture, vec2 colorTexCoord) {
|
|
|
|
|
return texture(colorTexture, colorTexCoord + vec2(offset, 0.0)).r;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Samples 9 taps around the current pixel.
|
|
|
|
|
void filterTextSample9Tap(out vec4 outAlphaLeft,
|
|
|
|
|
out float outAlphaCenter,
|
|
|
|
|
out vec4 outAlphaRight,
|
|
|
|
|
sampler2D colorTexture,
|
|
|
|
|
vec2 colorTexCoord,
|
|
|
|
|
vec4 kernel,
|
|
|
|
|
float onePixel) {
|
|
|
|
|
bool wide = kernel.x > 0.0;
|
|
|
|
|
outAlphaLeft =
|
|
|
|
|
vec4(wide ? filterTextSample1Tap(-4.0 * onePixel, colorTexture, colorTexCoord) : 0.0,
|
|
|
|
|
filterTextSample1Tap(-3.0 * onePixel, colorTexture, colorTexCoord),
|
|
|
|
|
filterTextSample1Tap(-2.0 * onePixel, colorTexture, colorTexCoord),
|
|
|
|
|
filterTextSample1Tap(-1.0 * onePixel, colorTexture, colorTexCoord));
|
|
|
|
|
outAlphaCenter = filterTextSample1Tap(0.0, colorTexture, colorTexCoord);
|
|
|
|
|
outAlphaRight =
|
|
|
|
|
vec4(filterTextSample1Tap(1.0 * onePixel, colorTexture, colorTexCoord),
|
|
|
|
|
filterTextSample1Tap(2.0 * onePixel, colorTexture, colorTexCoord),
|
|
|
|
|
filterTextSample1Tap(3.0 * onePixel, colorTexture, colorTexCoord),
|
|
|
|
|
wide ? filterTextSample1Tap(4.0 * onePixel, colorTexture, colorTexCoord) : 0.0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float filterTextConvolve7Tap(vec4 alpha0, vec3 alpha1, vec4 kernel) {
|
|
|
|
|
return dot(alpha0, kernel) + dot(alpha1, kernel.zyx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float filterTextGammaCorrectChannel(float bgColor, float fgColor, sampler2D gammaLUT) {
|
|
|
|
|
return texture(gammaLUT, vec2(fgColor, 1.0 - bgColor)).r;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// `fgColor` is in linear space.
|
|
|
|
|
vec3 filterTextGammaCorrect(vec3 bgColor, vec3 fgColor, sampler2D gammaLUT) {
|
|
|
|
|
return vec3(filterTextGammaCorrectChannel(bgColor.r, fgColor.r, gammaLUT),
|
|
|
|
|
filterTextGammaCorrectChannel(bgColor.g, fgColor.g, gammaLUT),
|
|
|
|
|
filterTextGammaCorrectChannel(bgColor.b, fgColor.b, gammaLUT));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// | x y z w
|
|
|
|
|
// --------------+--------------------------------------------------------
|
|
|
|
|
// filterParams0 | kernel[0] kernel[1] kernel[2] kernel[3]
|
|
|
|
|
// filterParams1 | bgColor.r bgColor.g bgColor.b -
|
|
|
|
|
// filterParams2 | fgColor.r fgColor.g fgColor.b gammaCorrectionEnabled
|
|
|
|
|
vec4 filterText(vec2 colorTexCoord,
|
|
|
|
|
sampler2D colorTexture,
|
|
|
|
|
sampler2D gammaLUT,
|
|
|
|
|
vec2 colorTextureSize,
|
|
|
|
|
vec4 filterParams0,
|
|
|
|
|
vec4 filterParams1,
|
|
|
|
|
vec4 filterParams2) {
|
|
|
|
|
// Unpack.
|
|
|
|
|
vec4 kernel = filterParams0;
|
|
|
|
|
vec3 bgColor = filterParams1.rgb;
|
|
|
|
|
vec3 fgColor = filterParams2.rgb;
|
|
|
|
|
bool gammaCorrectionEnabled = filterParams2.a != 0.0;
|
|
|
|
|
|
|
|
|
|
// Apply defringing if necessary.
|
|
|
|
|
vec3 alpha;
|
|
|
|
|
if (kernel.w == 0.0) {
|
|
|
|
|
alpha = texture(colorTexture, colorTexCoord).rrr;
|
|
|
|
|
} else {
|
|
|
|
|
vec4 alphaLeft, alphaRight;
|
|
|
|
|
float alphaCenter;
|
|
|
|
|
filterTextSample9Tap(alphaLeft,
|
|
|
|
|
alphaCenter,
|
|
|
|
|
alphaRight,
|
|
|
|
|
colorTexture,
|
|
|
|
|
colorTexCoord,
|
|
|
|
|
kernel,
|
|
|
|
|
1.0 / colorTextureSize.x);
|
|
|
|
|
|
|
|
|
|
float r = filterTextConvolve7Tap(alphaLeft, vec3(alphaCenter, alphaRight.xy), kernel);
|
|
|
|
|
float g = filterTextConvolve7Tap(vec4(alphaLeft.yzw, alphaCenter), alphaRight.xyz, kernel);
|
|
|
|
|
float b = filterTextConvolve7Tap(vec4(alphaLeft.zw, alphaCenter, alphaRight.x),
|
|
|
|
|
alphaRight.yzw,
|
|
|
|
|
kernel);
|
|
|
|
|
|
|
|
|
|
alpha = vec3(r, g, b);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Apply gamma correction if necessary.
|
|
|
|
|
if (gammaCorrectionEnabled)
|
|
|
|
|
alpha = filterTextGammaCorrect(bgColor, alpha, gammaLUT);
|
|
|
|
|
|
|
|
|
|
// Finish.
|
|
|
|
|
return vec4(mix(bgColor, fgColor, alpha), 1.0);
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-26 23:01:17 -04:00
|
|
|
|
// Other filters
|
|
|
|
|
|
|
|
|
|
// This is based on Pixman (MIT license). Copy and pasting the excellent comment
|
|
|
|
|
// from there:
|
|
|
|
|
|
|
|
|
|
// Implementation of radial gradients following the PDF specification.
|
|
|
|
|
// See section 8.7.4.5.4 Type 3 (Radial) Shadings of the PDF Reference
|
|
|
|
|
// Manual (PDF 32000-1:2008 at the time of this writing).
|
|
|
|
|
//
|
|
|
|
|
// In the radial gradient problem we are given two circles (c₁,r₁) and
|
|
|
|
|
// (c₂,r₂) that define the gradient itself.
|
|
|
|
|
//
|
|
|
|
|
// Mathematically the gradient can be defined as the family of circles
|
|
|
|
|
//
|
|
|
|
|
// ((1-t)·c₁ + t·(c₂), (1-t)·r₁ + t·r₂)
|
|
|
|
|
//
|
|
|
|
|
// excluding those circles whose radius would be < 0. When a point
|
|
|
|
|
// belongs to more than one circle, the one with a bigger t is the only
|
|
|
|
|
// one that contributes to its color. When a point does not belong
|
|
|
|
|
// to any of the circles, it is transparent black, i.e. RGBA (0, 0, 0, 0).
|
|
|
|
|
// Further limitations on the range of values for t are imposed when
|
|
|
|
|
// the gradient is not repeated, namely t must belong to [0,1].
|
|
|
|
|
//
|
|
|
|
|
// The graphical result is the same as drawing the valid (radius > 0)
|
|
|
|
|
// circles with increasing t in [-∞, +∞] (or in [0,1] if the gradient
|
|
|
|
|
// is not repeated) using SOURCE operator composition.
|
|
|
|
|
//
|
|
|
|
|
// It looks like a cone pointing towards the viewer if the ending circle
|
|
|
|
|
// is smaller than the starting one, a cone pointing inside the page if
|
|
|
|
|
// the starting circle is the smaller one and like a cylinder if they
|
|
|
|
|
// have the same radius.
|
|
|
|
|
//
|
|
|
|
|
// What we actually do is, given the point whose color we are interested
|
|
|
|
|
// in, compute the t values for that point, solving for t in:
|
|
|
|
|
//
|
|
|
|
|
// length((1-t)·c₁ + t·(c₂) - p) = (1-t)·r₁ + t·r₂
|
|
|
|
|
//
|
|
|
|
|
// Let's rewrite it in a simpler way, by defining some auxiliary
|
|
|
|
|
// variables:
|
|
|
|
|
//
|
|
|
|
|
// cd = c₂ - c₁
|
|
|
|
|
// pd = p - c₁
|
|
|
|
|
// dr = r₂ - r₁
|
|
|
|
|
// length(t·cd - pd) = r₁ + t·dr
|
|
|
|
|
//
|
|
|
|
|
// which actually means
|
|
|
|
|
//
|
|
|
|
|
// hypot(t·cdx - pdx, t·cdy - pdy) = r₁ + t·dr
|
|
|
|
|
//
|
|
|
|
|
// or
|
|
|
|
|
//
|
|
|
|
|
// ⎷((t·cdx - pdx)² + (t·cdy - pdy)²) = r₁ + t·dr.
|
|
|
|
|
//
|
|
|
|
|
// If we impose (as stated earlier) that r₁ + t·dr ≥ 0, it becomes:
|
|
|
|
|
//
|
|
|
|
|
// (t·cdx - pdx)² + (t·cdy - pdy)² = (r₁ + t·dr)²
|
|
|
|
|
//
|
|
|
|
|
// where we can actually expand the squares and solve for t:
|
|
|
|
|
//
|
|
|
|
|
// t²cdx² - 2t·cdx·pdx + pdx² + t²cdy² - 2t·cdy·pdy + pdy² =
|
|
|
|
|
// = r₁² + 2·r₁·t·dr + t²·dr²
|
|
|
|
|
//
|
|
|
|
|
// (cdx² + cdy² - dr²)t² - 2(cdx·pdx + cdy·pdy + r₁·dr)t +
|
|
|
|
|
// (pdx² + pdy² - r₁²) = 0
|
|
|
|
|
//
|
|
|
|
|
// A = cdx² + cdy² - dr²
|
|
|
|
|
// B = pdx·cdx + pdy·cdy + r₁·dr
|
|
|
|
|
// C = pdx² + pdy² - r₁²
|
|
|
|
|
// At² - 2Bt + C = 0
|
|
|
|
|
//
|
|
|
|
|
// The solutions (unless the equation degenerates because of A = 0) are:
|
|
|
|
|
//
|
|
|
|
|
// t = (B ± ⎷(B² - A·C)) / A
|
|
|
|
|
//
|
|
|
|
|
// The solution we are going to prefer is the bigger one, unless the
|
|
|
|
|
// radius associated to it is negative (or it falls outside the valid t
|
|
|
|
|
// range).
|
|
|
|
|
//
|
|
|
|
|
// Additional observations (useful for optimizations):
|
|
|
|
|
// A does not depend on p
|
|
|
|
|
//
|
|
|
|
|
// A < 0 ⟺ one of the two circles completely contains the other one
|
|
|
|
|
// ⟺ for every p, the radii associated with the two t solutions have
|
|
|
|
|
// opposite sign
|
|
|
|
|
//
|
|
|
|
|
// | x y z w
|
2020-04-20 16:03:49 -04:00
|
|
|
|
// --------------+-----------------------------------------------------
|
2020-03-26 23:01:17 -04:00
|
|
|
|
// filterParams0 | lineFrom.x lineFrom.y lineVector.x lineVector.y
|
|
|
|
|
// filterParams1 | radii.x radii.y uvOrigin.x uvOrigin.y
|
|
|
|
|
// filterParams2 | - - - -
|
|
|
|
|
vec4 filterRadialGradient(vec2 colorTexCoord,
|
|
|
|
|
sampler2D colorTexture,
|
|
|
|
|
vec2 colorTextureSize,
|
|
|
|
|
vec2 fragCoord,
|
|
|
|
|
vec2 framebufferSize,
|
|
|
|
|
vec4 filterParams0,
|
|
|
|
|
vec4 filterParams1) {
|
|
|
|
|
vec2 lineFrom = filterParams0.xy, lineVector = filterParams0.zw;
|
|
|
|
|
vec2 radii = filterParams1.xy, uvOrigin = filterParams1.zw;
|
|
|
|
|
|
2020-04-20 16:03:49 -04:00
|
|
|
|
vec2 dP = colorTexCoord - lineFrom, dC = lineVector;
|
2020-03-26 23:01:17 -04:00
|
|
|
|
float dR = radii.y - radii.x;
|
|
|
|
|
|
|
|
|
|
float a = dot(dC, dC) - dR * dR;
|
|
|
|
|
float b = dot(dP, dC) + radii.x * dR;
|
|
|
|
|
float c = dot(dP, dP) - radii.x * radii.x;
|
|
|
|
|
float discrim = b * b - a * c;
|
|
|
|
|
|
|
|
|
|
vec4 color = vec4(0.0);
|
2020-07-23 16:45:20 -04:00
|
|
|
|
if (discrim != 0.0) {
|
2020-03-26 23:01:17 -04:00
|
|
|
|
vec2 ts = vec2(sqrt(discrim) * vec2(1.0, -1.0) + vec2(b)) / vec2(a);
|
2020-04-14 19:13:32 -04:00
|
|
|
|
if (ts.x > ts.y)
|
|
|
|
|
ts = ts.yx;
|
|
|
|
|
float t = ts.x >= 0.0 ? ts.x : ts.y;
|
2020-07-16 13:30:39 -04:00
|
|
|
|
color = texture(colorTexture, uvOrigin + vec2(t, 0.0));
|
2020-03-26 23:01:17 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return color;
|
|
|
|
|
}
|
2020-03-19 19:44:45 -04:00
|
|
|
|
|
|
|
|
|
// | x y z w
|
|
|
|
|
// --------------+----------------------------------------------------
|
|
|
|
|
// filterParams0 | srcOffset.x srcOffset.y support -
|
|
|
|
|
// filterParams1 | gaussCoeff.x gaussCoeff.y gaussCoeff.z -
|
|
|
|
|
// filterParams2 | - - - -
|
|
|
|
|
vec4 filterBlur(vec2 colorTexCoord,
|
|
|
|
|
sampler2D colorTexture,
|
|
|
|
|
vec2 colorTextureSize,
|
|
|
|
|
vec4 filterParams0,
|
|
|
|
|
vec4 filterParams1) {
|
|
|
|
|
// Unpack.
|
|
|
|
|
vec2 srcOffsetScale = filterParams0.xy / colorTextureSize;
|
|
|
|
|
int support = int(filterParams0.z);
|
|
|
|
|
vec3 gaussCoeff = filterParams1.xyz;
|
|
|
|
|
|
|
|
|
|
// Set up our incremental calculation.
|
|
|
|
|
float gaussSum = gaussCoeff.x;
|
|
|
|
|
vec4 color = texture(colorTexture, colorTexCoord) * gaussCoeff.x;
|
|
|
|
|
gaussCoeff.xy *= gaussCoeff.yz;
|
|
|
|
|
|
|
|
|
|
// This is a common trick that lets us use the texture filtering hardware to evaluate two
|
|
|
|
|
// texels at a time. The basic principle is that, if c0 and c1 are colors of adjacent texels
|
|
|
|
|
// and k0 and k1 are arbitrary factors, the formula `k0 * c0 + k1 * c1` is equivalent to
|
|
|
|
|
// `(k0 + k1) * lerp(c0, c1, k1 / (k0 + k1))`. Linear interpolation, as performed by the
|
|
|
|
|
// texturing hardware when sampling adjacent pixels in one direction, evaluates
|
|
|
|
|
// `lerp(c0, c1, t)` where t is the offset from the texel with color `c0`. To evaluate the
|
|
|
|
|
// formula `k0 * c0 + k1 * c1`, therefore, we can use the texture hardware to perform linear
|
|
|
|
|
// interpolation with `t = k1 / (k0 + k1)`.
|
|
|
|
|
for (int i = 1; i <= support; i += 2) {
|
|
|
|
|
float gaussPartialSum = gaussCoeff.x;
|
|
|
|
|
gaussCoeff.xy *= gaussCoeff.yz;
|
|
|
|
|
gaussPartialSum += gaussCoeff.x;
|
|
|
|
|
|
|
|
|
|
vec2 srcOffset = srcOffsetScale * (float(i) + gaussCoeff.x / gaussPartialSum);
|
|
|
|
|
color += (texture(colorTexture, colorTexCoord - srcOffset) +
|
|
|
|
|
texture(colorTexture, colorTexCoord + srcOffset)) * gaussPartialSum;
|
|
|
|
|
|
|
|
|
|
gaussSum += 2.0 * gaussPartialSum;
|
|
|
|
|
gaussCoeff.xy *= gaussCoeff.yz;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Finish.
|
|
|
|
|
return color / gaussSum;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-21 12:56:47 -04:00
|
|
|
|
vec4 filterColorMatrix(vec2 colorTexCoord,
|
|
|
|
|
sampler2D colorTexture,
|
|
|
|
|
vec4 filterParams0,
|
|
|
|
|
vec4 filterParams1,
|
|
|
|
|
vec4 filterParams2,
|
|
|
|
|
vec4 filterParams3,
|
|
|
|
|
vec4 filterParams4) {
|
|
|
|
|
vec4 srcColor = texture(colorTexture, colorTexCoord);
|
|
|
|
|
mat4 colorMatrix = mat4(filterParams0, filterParams1, filterParams2, filterParams3);
|
|
|
|
|
return colorMatrix * srcColor + filterParams4;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-19 19:44:45 -04:00
|
|
|
|
vec4 filterNone(vec2 colorTexCoord, sampler2D colorTexture) {
|
|
|
|
|
return sampleColor(colorTexture, colorTexCoord);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vec4 filterColor(vec2 colorTexCoord,
|
|
|
|
|
sampler2D colorTexture,
|
|
|
|
|
sampler2D gammaLUT,
|
|
|
|
|
vec2 colorTextureSize,
|
2020-03-26 23:01:17 -04:00
|
|
|
|
vec2 fragCoord,
|
|
|
|
|
vec2 framebufferSize,
|
2020-03-19 19:44:45 -04:00
|
|
|
|
vec4 filterParams0,
|
|
|
|
|
vec4 filterParams1,
|
|
|
|
|
vec4 filterParams2,
|
2020-07-21 12:56:47 -04:00
|
|
|
|
vec4 filterParams3,
|
|
|
|
|
vec4 filterParams4,
|
2020-03-19 19:44:45 -04:00
|
|
|
|
int colorFilter) {
|
|
|
|
|
switch (colorFilter) {
|
2020-03-26 23:01:17 -04:00
|
|
|
|
case COMBINER_CTRL_FILTER_RADIAL_GRADIENT:
|
|
|
|
|
return filterRadialGradient(colorTexCoord,
|
|
|
|
|
colorTexture,
|
|
|
|
|
colorTextureSize,
|
|
|
|
|
fragCoord,
|
|
|
|
|
framebufferSize,
|
|
|
|
|
filterParams0,
|
|
|
|
|
filterParams1);
|
2020-03-19 19:44:45 -04:00
|
|
|
|
case COMBINER_CTRL_FILTER_BLUR:
|
|
|
|
|
return filterBlur(colorTexCoord,
|
|
|
|
|
colorTexture,
|
|
|
|
|
colorTextureSize,
|
|
|
|
|
filterParams0,
|
|
|
|
|
filterParams1);
|
|
|
|
|
case COMBINER_CTRL_FILTER_TEXT:
|
|
|
|
|
return filterText(colorTexCoord,
|
|
|
|
|
colorTexture,
|
|
|
|
|
gammaLUT,
|
|
|
|
|
colorTextureSize,
|
|
|
|
|
filterParams0,
|
|
|
|
|
filterParams1,
|
|
|
|
|
filterParams2);
|
2020-07-21 12:56:47 -04:00
|
|
|
|
case COMBINER_CTRL_FILTER_COLOR_MATRIX:
|
|
|
|
|
return filterColorMatrix(colorTexCoord,
|
|
|
|
|
colorTexture,
|
|
|
|
|
filterParams0,
|
|
|
|
|
filterParams1,
|
|
|
|
|
filterParams2,
|
|
|
|
|
filterParams3,
|
|
|
|
|
filterParams4);
|
2020-03-19 19:44:45 -04:00
|
|
|
|
}
|
|
|
|
|
return filterNone(colorTexCoord, colorTexture);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Compositing
|
|
|
|
|
|
|
|
|
|
vec3 compositeSelect(bvec3 cond, vec3 ifTrue, vec3 ifFalse) {
|
|
|
|
|
return vec3(cond.x ? ifTrue.x : ifFalse.x,
|
|
|
|
|
cond.y ? ifTrue.y : ifFalse.y,
|
|
|
|
|
cond.z ? ifTrue.z : ifFalse.z);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float compositeDivide(float num, float denom) {
|
|
|
|
|
return denom != 0.0 ? num / denom : 0.0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vec3 compositeColorDodge(vec3 destColor, vec3 srcColor) {
|
|
|
|
|
bvec3 destZero = equal(destColor, vec3(0.0)), srcOne = equal(srcColor, vec3(1.0));
|
|
|
|
|
return compositeSelect(destZero,
|
|
|
|
|
vec3(0.0),
|
|
|
|
|
compositeSelect(srcOne, vec3(1.0), destColor / (vec3(1.0) - srcColor)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// https://en.wikipedia.org/wiki/HSL_and_HSV#HSL_to_RGB_alternative
|
|
|
|
|
vec3 compositeHSLToRGB(vec3 hsl) {
|
|
|
|
|
float a = hsl.y * min(hsl.z, 1.0 - hsl.z);
|
|
|
|
|
vec3 ks = mod(vec3(0.0, 8.0, 4.0) + vec3(hsl.x * FRAC_6_PI), 12.0);
|
|
|
|
|
return hsl.zzz - clamp(min(ks - vec3(3.0), vec3(9.0) - ks), -1.0, 1.0) * a;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// https://en.wikipedia.org/wiki/HSL_and_HSV#From_RGB
|
|
|
|
|
vec3 compositeRGBToHSL(vec3 rgb) {
|
|
|
|
|
float v = max(max(rgb.r, rgb.g), rgb.b), xMin = min(min(rgb.r, rgb.g), rgb.b);
|
|
|
|
|
float c = v - xMin, l = mix(xMin, v, 0.5);
|
|
|
|
|
vec3 terms = rgb.r == v ? vec3(0.0, rgb.gb) :
|
|
|
|
|
rgb.g == v ? vec3(2.0, rgb.br) :
|
|
|
|
|
vec3(4.0, rgb.rg);
|
|
|
|
|
float h = FRAC_PI_3 * compositeDivide(terms.x * c + terms.y - terms.z, c);
|
|
|
|
|
float s = compositeDivide(c, v);
|
|
|
|
|
return vec3(h, s, l);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vec3 compositeScreen(vec3 destColor, vec3 srcColor) {
|
|
|
|
|
return destColor + srcColor - destColor * srcColor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vec3 compositeHardLight(vec3 destColor, vec3 srcColor) {
|
|
|
|
|
return compositeSelect(lessThanEqual(srcColor, vec3(0.5)),
|
|
|
|
|
destColor * vec3(2.0) * srcColor,
|
|
|
|
|
compositeScreen(destColor, vec3(2.0) * srcColor - vec3(1.0)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vec3 compositeSoftLight(vec3 destColor, vec3 srcColor) {
|
|
|
|
|
vec3 darkenedDestColor =
|
|
|
|
|
compositeSelect(lessThanEqual(destColor, vec3(0.25)),
|
|
|
|
|
((vec3(16.0) * destColor - 12.0) * destColor + 4.0) * destColor,
|
|
|
|
|
sqrt(destColor));
|
|
|
|
|
vec3 factor = compositeSelect(lessThanEqual(srcColor, vec3(0.5)),
|
|
|
|
|
destColor * (vec3(1.0) - destColor),
|
|
|
|
|
darkenedDestColor - destColor);
|
|
|
|
|
return destColor + (srcColor * 2.0 - 1.0) * factor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vec3 compositeHSL(vec3 destColor, vec3 srcColor, int op) {
|
|
|
|
|
switch (op) {
|
|
|
|
|
case COMBINER_CTRL_COMPOSITE_HUE:
|
|
|
|
|
return vec3(srcColor.x, destColor.y, destColor.z);
|
|
|
|
|
case COMBINER_CTRL_COMPOSITE_SATURATION:
|
|
|
|
|
return vec3(destColor.x, srcColor.y, destColor.z);
|
|
|
|
|
case COMBINER_CTRL_COMPOSITE_COLOR:
|
|
|
|
|
return vec3(srcColor.x, srcColor.y, destColor.z);
|
|
|
|
|
default:
|
|
|
|
|
return vec3(destColor.x, destColor.y, srcColor.z);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vec3 compositeRGB(vec3 destColor, vec3 srcColor, int op) {
|
|
|
|
|
switch (op) {
|
|
|
|
|
case COMBINER_CTRL_COMPOSITE_MULTIPLY:
|
|
|
|
|
return destColor * srcColor;
|
|
|
|
|
case COMBINER_CTRL_COMPOSITE_SCREEN:
|
|
|
|
|
return compositeScreen(destColor, srcColor);
|
|
|
|
|
case COMBINER_CTRL_COMPOSITE_OVERLAY:
|
|
|
|
|
return compositeHardLight(srcColor, destColor);
|
|
|
|
|
case COMBINER_CTRL_COMPOSITE_DARKEN:
|
|
|
|
|
return min(destColor, srcColor);
|
|
|
|
|
case COMBINER_CTRL_COMPOSITE_LIGHTEN:
|
|
|
|
|
return max(destColor, srcColor);
|
|
|
|
|
case COMBINER_CTRL_COMPOSITE_COLOR_DODGE:
|
|
|
|
|
return compositeColorDodge(destColor, srcColor);
|
|
|
|
|
case COMBINER_CTRL_COMPOSITE_COLOR_BURN:
|
|
|
|
|
return vec3(1.0) - compositeColorDodge(vec3(1.0) - destColor, vec3(1.0) - srcColor);
|
|
|
|
|
case COMBINER_CTRL_COMPOSITE_HARD_LIGHT:
|
|
|
|
|
return compositeHardLight(destColor, srcColor);
|
|
|
|
|
case COMBINER_CTRL_COMPOSITE_SOFT_LIGHT:
|
|
|
|
|
return compositeSoftLight(destColor, srcColor);
|
|
|
|
|
case COMBINER_CTRL_COMPOSITE_DIFFERENCE:
|
|
|
|
|
return abs(destColor - srcColor);
|
|
|
|
|
case COMBINER_CTRL_COMPOSITE_EXCLUSION:
|
|
|
|
|
return destColor + srcColor - vec3(2.0) * destColor * srcColor;
|
|
|
|
|
case COMBINER_CTRL_COMPOSITE_HUE:
|
|
|
|
|
case COMBINER_CTRL_COMPOSITE_SATURATION:
|
|
|
|
|
case COMBINER_CTRL_COMPOSITE_COLOR:
|
|
|
|
|
case COMBINER_CTRL_COMPOSITE_LUMINOSITY:
|
|
|
|
|
return compositeHSLToRGB(compositeHSL(compositeRGBToHSL(destColor),
|
|
|
|
|
compositeRGBToHSL(srcColor),
|
|
|
|
|
op));
|
|
|
|
|
}
|
|
|
|
|
return srcColor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vec4 composite(vec4 srcColor,
|
|
|
|
|
sampler2D destTexture,
|
|
|
|
|
vec2 destTextureSize,
|
|
|
|
|
vec2 fragCoord,
|
|
|
|
|
int op) {
|
|
|
|
|
if (op == COMBINER_CTRL_COMPOSITE_NORMAL)
|
|
|
|
|
return srcColor;
|
|
|
|
|
|
|
|
|
|
// FIXME(pcwalton): What should the output alpha be here?
|
|
|
|
|
vec2 destTexCoord = fragCoord / destTextureSize;
|
|
|
|
|
vec4 destColor = texture(destTexture, destTexCoord);
|
|
|
|
|
vec3 blendedRGB = compositeRGB(destColor.rgb, srcColor.rgb, op);
|
|
|
|
|
return vec4(srcColor.a * (1.0 - destColor.a) * srcColor.rgb +
|
|
|
|
|
srcColor.a * destColor.a * blendedRGB +
|
|
|
|
|
(1.0 - srcColor.a) * destColor.rgb,
|
|
|
|
|
1.0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Masks
|
|
|
|
|
|
|
|
|
|
float sampleMask(float maskAlpha,
|
|
|
|
|
sampler2D maskTexture,
|
2020-05-04 16:26:40 -04:00
|
|
|
|
vec2 maskTextureSize,
|
2020-03-19 19:44:45 -04:00
|
|
|
|
vec3 maskTexCoord,
|
|
|
|
|
int maskCtrl) {
|
|
|
|
|
if (maskCtrl == 0)
|
|
|
|
|
return maskAlpha;
|
2020-05-04 16:26:40 -04:00
|
|
|
|
|
|
|
|
|
ivec2 maskTexCoordI = ivec2(floor(maskTexCoord.xy));
|
|
|
|
|
vec4 texel = texture(maskTexture, (vec2(maskTexCoordI / ivec2(1, 4)) + 0.5) / maskTextureSize);
|
|
|
|
|
float coverage = texel[maskTexCoordI.y % 4] + maskTexCoord.z;
|
|
|
|
|
|
2020-04-22 14:04:16 -04:00
|
|
|
|
if ((maskCtrl & TILE_CTRL_MASK_WINDING) != 0)
|
2020-03-19 19:44:45 -04:00
|
|
|
|
coverage = abs(coverage);
|
|
|
|
|
else
|
|
|
|
|
coverage = 1.0 - abs(1.0 - mod(coverage, 2.0));
|
|
|
|
|
return min(maskAlpha, coverage);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Main function
|
|
|
|
|
|
2020-06-23 15:27:04 -04:00
|
|
|
|
vec4 calculateColor(vec2 fragCoord,
|
|
|
|
|
sampler2D colorTexture0,
|
|
|
|
|
sampler2D maskTexture0,
|
|
|
|
|
sampler2D destTexture,
|
|
|
|
|
sampler2D gammaLUT,
|
|
|
|
|
vec2 colorTextureSize0,
|
|
|
|
|
vec2 maskTextureSize0,
|
|
|
|
|
vec4 filterParams0,
|
|
|
|
|
vec4 filterParams1,
|
|
|
|
|
vec4 filterParams2,
|
2020-07-21 12:56:47 -04:00
|
|
|
|
vec4 filterParams3,
|
|
|
|
|
vec4 filterParams4,
|
2020-06-23 15:27:04 -04:00
|
|
|
|
vec2 framebufferSize,
|
|
|
|
|
int ctrl,
|
|
|
|
|
vec3 maskTexCoord0,
|
|
|
|
|
vec2 colorTexCoord0,
|
|
|
|
|
vec4 baseColor,
|
|
|
|
|
int tileCtrl) {
|
2020-03-19 19:44:45 -04:00
|
|
|
|
// Sample mask.
|
2020-04-22 14:04:16 -04:00
|
|
|
|
int maskCtrl0 = (tileCtrl >> TILE_CTRL_MASK_0_SHIFT) & TILE_CTRL_MASK_MASK;
|
2020-03-19 19:44:45 -04:00
|
|
|
|
float maskAlpha = 1.0;
|
2020-06-23 15:27:04 -04:00
|
|
|
|
maskAlpha = sampleMask(maskAlpha, maskTexture0, maskTextureSize0, maskTexCoord0, maskCtrl0);
|
2020-03-19 19:44:45 -04:00
|
|
|
|
|
|
|
|
|
// Sample color.
|
2020-06-23 15:27:04 -04:00
|
|
|
|
vec4 color = baseColor;
|
2020-04-13 14:52:10 -04:00
|
|
|
|
int color0Combine = (ctrl >> COMBINER_CTRL_COLOR_COMBINE_SHIFT) &
|
|
|
|
|
COMBINER_CTRL_COLOR_COMBINE_MASK;
|
|
|
|
|
if (color0Combine != 0) {
|
|
|
|
|
int color0Filter = (ctrl >> COMBINER_CTRL_COLOR_FILTER_SHIFT) & COMBINER_CTRL_FILTER_MASK;
|
2020-06-23 15:27:04 -04:00
|
|
|
|
vec4 color0 = filterColor(colorTexCoord0,
|
|
|
|
|
colorTexture0,
|
|
|
|
|
gammaLUT,
|
|
|
|
|
colorTextureSize0,
|
|
|
|
|
fragCoord,
|
|
|
|
|
framebufferSize,
|
|
|
|
|
filterParams0,
|
|
|
|
|
filterParams1,
|
|
|
|
|
filterParams2,
|
2020-07-21 12:56:47 -04:00
|
|
|
|
filterParams3,
|
|
|
|
|
filterParams4,
|
2020-04-13 14:52:10 -04:00
|
|
|
|
color0Filter);
|
|
|
|
|
color = combineColor0(color, color0, color0Combine);
|
2020-03-19 19:44:45 -04:00
|
|
|
|
}
|
|
|
|
|
|
2020-04-13 14:52:10 -04:00
|
|
|
|
// Apply mask.
|
|
|
|
|
color.a *= maskAlpha;
|
2020-03-19 19:44:45 -04:00
|
|
|
|
|
|
|
|
|
// Apply composite.
|
|
|
|
|
int compositeOp = (ctrl >> COMBINER_CTRL_COMPOSITE_SHIFT) & COMBINER_CTRL_COMPOSITE_MASK;
|
2020-06-23 15:27:04 -04:00
|
|
|
|
color = composite(color, destTexture, framebufferSize, fragCoord, compositeOp);
|
2020-03-19 19:44:45 -04:00
|
|
|
|
|
|
|
|
|
// Premultiply alpha.
|
|
|
|
|
color.rgb *= color.a;
|
2020-06-23 15:27:04 -04:00
|
|
|
|
return color;
|
2020-03-19 19:44:45 -04:00
|
|
|
|
}
|