Path: blob/master/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/shaders.metal
66646 views
/*
* Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include <simd/simd.h>
#include <metal_stdlib>
#include "common.h"
using namespace metal;
struct VertexInput {
float2 position [[attribute(VertexAttributePosition)]];
};
struct TxtVertexInput {
float2 position [[attribute(VertexAttributePosition)]];
float2 texCoords [[attribute(VertexAttributeTexPos)]];
};
struct AAVertexInput {
float2 position [[attribute(VertexAttributePosition)]];
float2 oTexCoords [[attribute(VertexAttributeTexPos)]];
float2 iTexCoords [[attribute(VertexAttributeITexPos)]];
};
struct ColShaderInOut {
float4 position [[position]];
float ptSize [[point_size]];
half4 color;
};
struct AAShaderInOut {
float4 position [[position]];
float2 outerTexCoords;
float2 innerTexCoords;
half4 color;
};
struct StencilShaderInOut {
float4 position [[position]];
float ptSize [[point_size]];
char color;
};
struct TxtShaderInOut {
float4 position [[position]];
float2 texCoords;
float2 tpCoords;
};
struct LCDShaderInOut {
float4 position [[position]];
float2 orig_pos;
float2 texCoords;
};
struct GradShaderInOut {
float4 position [[position]];
float2 texCoords;
};
struct ColShaderInOut_XOR {
float4 position [[position]];
float ptSize [[point_size]];
float2 orig_pos;
half4 color;
};
struct TxtShaderInOut_XOR {
float4 position [[position]];
float2 orig_pos;
float2 texCoords;
float2 tpCoords;
};
inline float fromLinear(float c)
{
if (isnan(c)) c = 0.0;
if (c > 1.0)
c = 1.0;
else if (c < 0.0)
c = 0.0;
else if (c < 0.0031308)
c = 12.92 * c;
else
c = 1.055 * powr(c, 1.0/2.4) - 0.055;
return c;
}
inline float3 fromLinear3(float3 c) {
//c.r = fromLinear(c.r);
//c.g = fromLinear(c.g);
//c.b = fromLinear(c.b);
// Use approximated calculations to match software rendering
c.rgb = 1.055 * pow(c.rgb, float3(0.416667)) - 0.055;
return c;
}
template <typename Uniforms> inline
float4 frag_single_grad(float a, Uniforms uniforms)
{
int fa = floor(a);
if (uniforms.isCyclic) {
if (fa%2) {
a = 1.0 + fa - a;
} else {
a = a - fa;
}
} else {
a = saturate(a);
}
return mix(uniforms.color1, uniforms.color2, a);
}
template <typename Uniforms> inline
float4 frag_multi_grad(float a, Uniforms uniforms)
{
if (uniforms.cycleMethod > GradNoCycle) {
int fa = floor(a);
a = a - fa;
if (uniforms.cycleMethod == GradReflect && fa%2) {
a = 1.0 - a;
}
} else {
a = saturate(a);
}
int n = 0;
for (;n < GRAD_MAX_FRACTIONS - 1; n++) {
if (a <= uniforms.fract[n + 1]) break;
}
a = (a - uniforms.fract[n]) / (uniforms.fract[n + 1] - uniforms.fract[n]);
float4 c = mix(uniforms.color[n], uniforms.color[n + 1], a);
if (uniforms.isLinear) {
c.rgb = fromLinear3(c.rgb);
}
return c;
}
vertex ColShaderInOut vert_col(VertexInput in [[stage_in]],
constant FrameUniforms& uniforms [[buffer(FrameUniformBuffer)]],
constant TransformMatrix& transform [[buffer(MatrixBuffer)]]) {
ColShaderInOut out;
float4 pos4 = float4(in.position, 0.0, 1.0);
out.position = transform.transformMatrix*pos4;
out.ptSize = 1.0;
out.color = half4(uniforms.color.r, uniforms.color.g, uniforms.color.b, uniforms.color.a);
return out;
}
vertex AAShaderInOut vert_col_aa(AAVertexInput in [[stage_in]],
constant FrameUniforms& uniforms [[buffer(FrameUniformBuffer)]],
constant TransformMatrix& transform [[buffer(MatrixBuffer)]]) {
AAShaderInOut out;
float4 pos4 = float4(in.position, 0.0, 1.0);
out.position = transform.transformMatrix*pos4;
out.color = half4(uniforms.color.r, uniforms.color.g, uniforms.color.b, uniforms.color.a);
out.outerTexCoords = in.oTexCoords;
out.innerTexCoords = in.iTexCoords;
return out;
}
vertex StencilShaderInOut vert_stencil(VertexInput in [[stage_in]],
constant FrameUniforms& uniforms [[buffer(FrameUniformBuffer)]],
constant TransformMatrix& transform [[buffer(MatrixBuffer)]]) {
StencilShaderInOut out;
float4 pos4 = float4(in.position, 0.0, 1.0);
out.position = transform.transformMatrix * pos4;
out.ptSize = 1.0;
out.color = 0xFF;
return out;
}
vertex GradShaderInOut vert_grad(VertexInput in [[stage_in]], constant TransformMatrix& transform [[buffer(MatrixBuffer)]]) {
GradShaderInOut out;
float4 pos4 = float4(in.position, 0.0, 1.0);
out.position = transform.transformMatrix*pos4;
return out;
}
vertex TxtShaderInOut vert_txt(TxtVertexInput in [[stage_in]], constant TransformMatrix& transform [[buffer(MatrixBuffer)]]) {
TxtShaderInOut out;
float4 pos4 = float4(in.position, 0.0, 1.0);
out.position = transform.transformMatrix*pos4;
out.texCoords = in.texCoords;
return out;
}
vertex LCDShaderInOut vert_txt_lcd(TxtVertexInput in [[stage_in]], constant TransformMatrix& transform [[buffer(MatrixBuffer)]]) {
LCDShaderInOut out;
float4 pos4 = float4(in.position, 0.0, 1.0);
out.position = transform.transformMatrix*pos4;
out.orig_pos = in.position;
out.texCoords = in.texCoords;
return out;
}
vertex TxtShaderInOut vert_txt_tp(TxtVertexInput in [[stage_in]], constant AnchorData& anchorData [[buffer(FrameUniformBuffer)]], constant TransformMatrix& transform [[buffer(MatrixBuffer)]])
{
TxtShaderInOut out;
float4 pos4 = float4(in.position, 0.0, 1.0);
out.position = transform.transformMatrix * pos4;
// Compute texture coordinates here w.r.t. anchor rect of texture paint
out.tpCoords.x = (anchorData.xParams[0] * in.position.x) +
(anchorData.xParams[1] * in.position.y) +
(anchorData.xParams[2] * out.position.w);
out.tpCoords.y = (anchorData.yParams[0] * in.position.x) +
(anchorData.yParams[1] * in.position.y) +
(anchorData.yParams[2] * out.position.w);
out.texCoords = in.texCoords;
return out;
}
vertex GradShaderInOut vert_txt_grad(TxtVertexInput in [[stage_in]],
constant TransformMatrix& transform [[buffer(MatrixBuffer)]]) {
GradShaderInOut out;
float4 pos4 = float4(in.position, 0.0, 1.0);
out.position = transform.transformMatrix*pos4;
out.texCoords = in.texCoords;
return out;
}
fragment half4 frag_col(ColShaderInOut in [[stage_in]]) {
return in.color;
}
fragment half4 frag_col_aa(AAShaderInOut in [[stage_in]]) {
float2 oleg1 = dfdx(in.outerTexCoords);
float2 oleg2 = dfdy(in.outerTexCoords);
// Calculate the bounds of the distorted pixel parallelogram.
float2 corner = in.outerTexCoords - (oleg1+oleg2)/2.0;
float2 omin = min(corner, corner+oleg1);
omin = min(omin, corner+oleg2);
omin = min(omin, corner+oleg1+oleg2);
float2 omax = max(corner, corner+oleg1);
omax = max(omax, corner+oleg2);
omax = max(omax, corner+oleg1+oleg2);
// Calculate the vectors for the "legs" of the pixel parallelogram
// for the inner parallelogram.
float2 ileg1 = dfdx(in.innerTexCoords);
float2 ileg2 = dfdy(in.innerTexCoords);
// Calculate the bounds of the distorted pixel parallelogram.
corner = in.innerTexCoords - (ileg1+ileg2)/2.0;
float2 imin = min(corner, corner+ileg1);
imin = min(imin, corner+ileg2);
imin = min(imin, corner+ileg1+ileg2);
float2 imax = max(corner, corner+ileg1);
imax = max(imax, corner+ileg2);
imax = max(imax, corner+ileg1+ileg2);
// Clamp the bounds of the parallelograms to the unit square to
// estimate the intersection of the pixel parallelogram with
// the unit square. The ratio of the 2 rectangle areas is a
// reasonable estimate of the proportion of coverage.
float2 o1 = clamp(omin, 0.0, 1.0);
float2 o2 = clamp(omax, 0.0, 1.0);
float oint = (o2.y-o1.y)*(o2.x-o1.x);
float oarea = (omax.y-omin.y)*(omax.x-omin.x);
float2 i1 = clamp(imin, 0.0, 1.0);
float2 i2 = clamp(imax, 0.0, 1.0);
float iint = (i2.y-i1.y)*(i2.x-i1.x);
float iarea = (imax.y-imin.y)*(imax.x-imin.x);
// Proportion of pixel in outer shape minus the proportion
// of pixel in the inner shape == the coverage of the pixel
// in the area between the two.
float coverage = oint/oarea - iint / iarea;
return (in.color * coverage);
}
fragment unsigned int frag_stencil(StencilShaderInOut in [[stage_in]]) {
return in.color;
}
// NOTE:
// 1. consider to make shaders without IF-conditions
// 2. we can pass interpolation mode via uniforms and select corresponding sampler in shader
// but it can cause performance problems (something like getTextureSampler(hint) will be invoked
// for every pixel)
fragment half4 frag_txt(
TxtShaderInOut vert [[stage_in]],
texture2d<float, access::sample> renderTexture [[texture(0)]],
constant TxtFrameUniforms& uniforms [[buffer(1)]],
sampler textureSampler [[sampler(0)]]
) {
float4 pixelColor = renderTexture.sample(textureSampler, vert.texCoords);
float srcA = uniforms.isSrcOpaque ? 1 : pixelColor.a;
if (uniforms.mode) {
float3 c = mix(pixelColor.rgb, uniforms.color.rgb, srcA);
return half4(c.r, c.g, c.b ,
(uniforms.isSrcOpaque) ?
uniforms.color.a : pixelColor.a*uniforms.color.a);
}
return half4(pixelColor.r,
pixelColor.g,
pixelColor.b, srcA)*uniforms.extraAlpha;
}
fragment half4 frag_text(
TxtShaderInOut vert [[stage_in]],
texture2d<float, access::sample> renderTexture [[texture(0)]],
constant TxtFrameUniforms& uniforms [[buffer(1)]],
sampler textureSampler [[sampler(0)]]
) {
float4 pixelColor = renderTexture.sample(textureSampler, vert.texCoords);
return half4(uniforms.color * pixelColor.a);
}
fragment half4 frag_txt_tp(TxtShaderInOut vert [[stage_in]],
texture2d<float, access::sample> renderTexture [[texture(0)]],
texture2d<float, access::sample> paintTexture [[texture(1)]],
constant TxtFrameUniforms& uniforms [[buffer(1)]],
sampler textureSampler [[sampler(0)]]
) {
float4 renderColor = renderTexture.sample(textureSampler, vert.texCoords);
float4 paintColor = paintTexture.sample(textureSampler, vert.tpCoords);
const float srcA = uniforms.isSrcOpaque ? 1 : paintColor.a;
return half4(paintColor.r*renderColor.a,
paintColor.g*renderColor.a,
paintColor.b*renderColor.a,
srcA*renderColor.a) * uniforms.extraAlpha;
}
fragment half4 frag_txt_grad(GradShaderInOut in [[stage_in]],
constant GradFrameUniforms& uniforms [[buffer(0)]],
texture2d<float, access::sample> renderTexture [[texture(0)]])
{
constexpr sampler textureSampler (address::repeat, mag_filter::nearest,
min_filter::nearest);
float4 renderColor = renderTexture.sample(textureSampler, in.texCoords);
float3 v = float3(in.position.x-0.5, in.position.y-0.5, 1);
float a = (dot(v,uniforms.params)-0.25)*2.0;
return half4(frag_single_grad(a, uniforms)*renderColor.a)*uniforms.extraAlpha;
}
fragment half4 frag_txt_lin_grad(GradShaderInOut in [[stage_in]],
constant LinGradFrameUniforms& uniforms [[buffer(0)]],
texture2d<float, access::sample> renderTexture [[texture(0)]])
{
constexpr sampler textureSampler (address::repeat, mag_filter::nearest,
min_filter::nearest);
float4 renderColor = renderTexture.sample(textureSampler, in.texCoords);
float3 v = float3(in.position.x, in.position.y, 1);
float a = dot(v,uniforms.params);
return half4(frag_multi_grad(a, uniforms)*renderColor.a)*uniforms.extraAlpha;
}
fragment half4 frag_txt_rad_grad(GradShaderInOut in [[stage_in]],
constant RadGradFrameUniforms& uniforms [[buffer(0)]],
texture2d<float, access::sample> renderTexture [[texture(0)]])
{
constexpr sampler textureSampler (address::repeat, mag_filter::nearest,
min_filter::nearest);
float4 renderColor = renderTexture.sample(textureSampler, in.texCoords);
float3 fragCoord = float3(in.position.x-0.5, in.position.y-0.5, 1);
float x = dot(fragCoord, uniforms.m0);
float y = dot(fragCoord, uniforms.m1);
float xfx = x - uniforms.precalc.x;
float a = (uniforms.precalc.x*xfx + sqrt(xfx*xfx + y*y*uniforms.precalc.y))*uniforms.precalc.z;
return half4(frag_multi_grad(a, uniforms)*renderColor.a)*uniforms.extraAlpha;
}
fragment half4 aa_frag_txt(
TxtShaderInOut vert [[stage_in]],
texture2d<float, access::sample> renderTexture [[texture(0)]],
texture2d<float, access::sample> stencilTexture [[texture(1)]],
constant TxtFrameUniforms& uniforms [[buffer(1)]],
sampler textureSampler [[sampler(0)]]
) {
float4 pixelColor = renderTexture.sample(textureSampler, vert.texCoords);
if (!is_null_texture(stencilTexture)) {
float4 stencil = stencilTexture.sample(textureSampler, vert.texCoords);
if (stencil.r == 0.0) {
discard_fragment();
}
}
return half4(pixelColor.r, pixelColor.g, pixelColor.b, pixelColor.a);
}
fragment half4 frag_txt_op_rescale(
TxtShaderInOut vert [[stage_in]],
texture2d<float, access::sample> srcTex [[texture(0)]],
constant TxtFrameOpRescaleUniforms& uniforms [[buffer(1)]],
sampler textureSampler [[sampler(0)]]
) {
float4 srcColor = srcTex.sample(textureSampler, vert.texCoords);
const float srcA = uniforms.isSrcOpaque ? 1 : srcColor.a;
// TODO: check uniforms.isNonPremult and pre-multiply if necessary
return half4(srcColor.r*uniforms.normScaleFactors.r + uniforms.normOffsets.r,
srcColor.g*uniforms.normScaleFactors.g + uniforms.normOffsets.g,
srcColor.b*uniforms.normScaleFactors.b + uniforms.normOffsets.b, srcA)*uniforms.extraAlpha;
// NOTE: GL-shader multiplies result with glColor (in order to apply extra alpha), probably it's better to do the
// same here.
//
// GL-shader impl:
//" vec4 srcColor = texture%s(baseImage, gl_TexCoord[0].st);"
//" %s" // (placeholder for un-premult code: srcColor.rgb /= srcColor.a;)
//" vec4 result = (srcColor * scaleFactors) + offsets;" // rescale source value
//" %s" // (placeholder for re-premult code: result.rgb *= result.a;)
//" gl_FragColor = result * gl_Color;" // modulate with gl_Color in order to apply extra alpha
}
fragment half4 frag_txt_op_convolve(
TxtShaderInOut vert [[stage_in]],
texture2d<float, access::sample> srcTex [[texture(0)]],
constant TxtFrameOpConvolveUniforms& uniforms [[buffer(1)]],
const device float * kernelVals [[buffer(2)]],
sampler textureSampler [[sampler(0)]]
) {
float4 sum = float4(0, 0, 0, 0);
if (vert.texCoords[0] < uniforms.imgEdge[0]
|| vert.texCoords[1] < uniforms.imgEdge[1]
|| vert.texCoords[0] > uniforms.imgEdge[2]
|| vert.texCoords[1] > uniforms.imgEdge[3]
) {
if (!uniforms.isEdgeZeroFill) {
sum = srcTex.sample(textureSampler, vert.texCoords);
}
}
for (int i = 0; i < uniforms.kernelSize; i++) {
float3 kern = float3(kernelVals[i*3], kernelVals[i*3 + 1], kernelVals[i*3 + 2]);
float2 pos = float2(vert.texCoords.x + kern.x, vert.texCoords.y + kern.y);
float4 pixCol = srcTex.sample(textureSampler, pos);
sum.r += kern.z * pixCol.r;
sum.g += kern.z * pixCol.g;
sum.b += kern.z * pixCol.b;
sum.a += kern.z * pixCol.a;
}
const float srcA = uniforms.isSrcOpaque ? 1 : sum.a;
return half4(sum.r, sum.g, sum.b, srcA)*uniforms.extraAlpha;
// NOTE: GL-shader multiplies result with glColor (in order to apply extra alpha), probably it's better to do the
// same here.
//
// GL-shader impl:
//" if (any(lessThan(gl_TexCoord[0].st, imgEdge.xy)) ||"
//" any(greaterThan(gl_TexCoord[0].st, imgEdge.zw)))"
//" {"
//" %s" // (placeholder for edge condition code)
//" } else {"
//" sum = vec4(0.0);"
//" for (i = 0; i < MAX_KERNEL_SIZE; i++) {"
//" sum +="
//" kernelVals[i].z *"
//" texture%s(baseImage,"
//" gl_TexCoord[0].st + kernelVals[i].xy);"
//" }"
//" }"
//""
//" gl_FragColor = sum * gl_Color;" // modulate with gl_Color in order to apply extra alpha
}
fragment half4 frag_txt_op_lookup(
TxtShaderInOut vert [[stage_in]],
texture2d<float, access::sample> srcTex [[texture(0)]],
texture2d<float, access::sample> lookupTex [[texture(1)]],
constant TxtFrameOpLookupUniforms& uniforms [[buffer(1)]],
sampler textureSampler [[sampler(0)]]
) {
float4 srcColor = srcTex.sample(textureSampler, vert.texCoords);
float4 srcIndex = srcColor - uniforms.offset;
const float2 posR = float2(srcIndex.r, 0.125);
const float2 posG = float2(srcIndex.g, 0.375);
const float2 posB = float2(srcIndex.b, 0.625);
float4 lookupR = lookupTex.sample(textureSampler, posR);
float4 lookupG = lookupTex.sample(textureSampler, posG);
float4 lookupB = lookupTex.sample(textureSampler, posB);
const float srcA = uniforms.isSrcOpaque ? 1 : srcColor.a;
const float a = uniforms.isUseSrcAlpha ? srcA : lookupTex.sample(textureSampler, float2(srcIndex.a, 0.875)).a;
// TODO: check uniforms.isNonPremult and pre-multiply if necessary
return half4(lookupR.a, lookupG.a, lookupB.a, a)*uniforms.extraAlpha;
// NOTE: GL-shader multiplies result with glColor (in order to apply extra alpha), probably it's better to do the
// same here.
//
// GL-shader impl:
//" vec4 srcColor = texture%s(baseImage, gl_TexCoord[0].st);"
//" %s" // (placeholder for un-premult code)
//" vec4 srcIndex = srcColor - offset;" // subtract offset from original index
//
// // use source value as input to lookup table (note that
// // "v" texcoords are hardcoded to hit texel centers of
// // each row/band in texture)
//" vec4 result;"
//" result.r = texture2D(lookupTable, vec2(srcIndex.r, 0.125)).r;"
//" result.g = texture2D(lookupTable, vec2(srcIndex.g, 0.375)).r;"
//" result.b = texture2D(lookupTable, vec2(srcIndex.b, 0.625)).r;"
//" %s" // (placeholder for alpha store code)
//" %s" // (placeholder for re-premult code)
//" gl_FragColor = result * gl_Color;" // modulate with gl_Color in order to apply extra alpha
}
fragment half4 frag_grad(GradShaderInOut in [[stage_in]],
constant GradFrameUniforms& uniforms [[buffer(0)]]) {
float3 v = float3(in.position.x-0.5, in.position.y-0.5, 1);
float a = (dot(v,uniforms.params)-0.25)*2.0;
return half4(frag_single_grad(a, uniforms)) * uniforms.extraAlpha;
}
// LinGradFrameUniforms
fragment half4 frag_lin_grad(GradShaderInOut in [[stage_in]],
constant LinGradFrameUniforms& uniforms [[buffer(0)]]) {
float3 v = float3(in.position.x-0.5, in.position.y-0.5, 1);
float a = dot(v, uniforms.params);
return half4(frag_multi_grad(a, uniforms))*uniforms.extraAlpha;
}
fragment half4 frag_rad_grad(GradShaderInOut in [[stage_in]],
constant RadGradFrameUniforms& uniforms [[buffer(0)]]) {
float3 fragCoord = float3(in.position.x-0.5, in.position.y-0.5, 1);
float x = dot(fragCoord, uniforms.m0);
float y = dot(fragCoord, uniforms.m1);
float xfx = x - uniforms.precalc.x;
float a = (uniforms.precalc.x*xfx + sqrt(xfx*xfx + y*y*uniforms.precalc.y))*uniforms.precalc.z;
return half4(frag_multi_grad(a, uniforms))*uniforms.extraAlpha;
}
vertex TxtShaderInOut vert_tp(VertexInput in [[stage_in]],
constant AnchorData& anchorData [[buffer(FrameUniformBuffer)]],
constant TransformMatrix& transform [[buffer(MatrixBuffer)]])
{
TxtShaderInOut out;
float4 pos4 = float4(in.position, 0.0, 1.0);
out.position = transform.transformMatrix * pos4;
// Compute texture coordinates here w.r.t. anchor rect of texture paint
out.texCoords.x = (anchorData.xParams[0] * in.position.x) +
(anchorData.xParams[1] * in.position.y) +
(anchorData.xParams[2] * out.position.w);
out.texCoords.y = (anchorData.yParams[0] * in.position.x) +
(anchorData.yParams[1] * in.position.y) +
(anchorData.yParams[2] * out.position.w);
return out;
}
fragment half4 frag_tp(
TxtShaderInOut vert [[stage_in]],
texture2d<float, access::sample> renderTexture [[texture(0)]],
constant TxtFrameUniforms& uniforms [[buffer(1)]],
sampler textureSampler [[sampler(0)]])
{
float4 pixelColor = renderTexture.sample(textureSampler, vert.texCoords);
const float srcA = uniforms.isSrcOpaque ? 1 : pixelColor.a;
return half4(pixelColor.r, pixelColor.g, pixelColor.b, srcA) * uniforms.extraAlpha;
}
/* The variables involved in the equation can be expressed as follows:
*
* Cs = Color component of the source (foreground color) [0.0, 1.0]
* Cd = Color component of the destination (background color) [0.0, 1.0]
* Cr = Color component to be written to the destination [0.0, 1.0]
* Ag = Glyph alpha (aka intensity or coverage) [0.0, 1.0]
* Ga = Gamma adjustment in the range [1.0, 2.5]
* (^ means raised to the power)
*
* And here is the theoretical equation approximated by this shader:
*
* Cr = (Ag*(Cs^Ga) + (1-Ag)*(Cd^Ga)) ^ (1/Ga)
*/
fragment float4 lcd_color(
LCDShaderInOut vert [[stage_in]],
texture2d<float, access::sample> glyphTexture [[texture(0)]],
texture2d<float, access::sample> dstTexture [[texture(1)]],
constant LCDFrameUniforms& uniforms [[buffer(1)]])
{
float3 src_adj = uniforms.src_adj;
float3 gamma = uniforms.gamma;
float3 invgamma = uniforms.invgamma;
constexpr sampler glyphTextureSampler (address::repeat,
mag_filter::nearest,
min_filter::nearest);
// load the RGB value from the glyph image at the current texcoord
float3 glyph_clr = float3(glyphTexture.sample(glyphTextureSampler, vert.texCoords));
if (glyph_clr.r == 0.0f && glyph_clr.g == 0.0f && glyph_clr.b == 0.0f) {
// zero coverage, so skip this fragment
discard_fragment();
}
// load the RGB value from the corresponding destination pixel
uint2 texCoord = {(unsigned int)(vert.orig_pos.x), (unsigned int)(vert.orig_pos.y)};
float4 dst_clr = dstTexture.read(texCoord);
// gamma adjust the dest color
float3 dst_adj = pow(dst_clr.rgb, gamma);
// linearly interpolate the three color values
float3 result = mix(dst_adj, src_adj, glyph_clr);
// gamma re-adjust the resulting color (alpha is always set to 1.0)
return float4(pow(result.rgb, invgamma), 1.0);
}
// Compute shader to transfer clipping data to the texture used for manual clipping in
// aa_frag_txt shader
kernel void stencil2tex(const device uchar *imageBuffer [[buffer(0)]],
device uchar4 *outputBuffer [[buffer(1)]],
uint gid [[thread_position_in_grid]])
{
uchar p = imageBuffer[gid];
outputBuffer[gid] = uchar4(p, p, p, p);
}
// work item deals with 4 byte pixel
// assuming that data is aligned
kernel void swizzle_to_rgba(const device uchar *imageBuffer [[buffer(0)]],
device uchar *outputBuffer [[buffer(1)]],
constant SwizzleUniforms& uniforms [[buffer(2)]],
constant uint& size [[buffer(3)]],
uint gid [[thread_position_in_grid]])
{
if (gid > size) {
return;
}
outputBuffer[4 * gid] = imageBuffer[4 * gid + uniforms.swizzle[0]]; // r
outputBuffer[4 * gid + 1] = imageBuffer[4 * gid + uniforms.swizzle[1]]; // g
outputBuffer[4 * gid + 2] = imageBuffer[4 * gid + uniforms.swizzle[2]]; // b
if (uniforms.hasAlpha) {
outputBuffer[4 * gid + 3] = imageBuffer[4 * gid + uniforms.swizzle[3]];
} else {
outputBuffer[4 * gid + 3] = 255;
}
}
// ----------------------------------------------------------------------------
// Shaders for rendering in XOR Mode
// ----------------------------------------------------------------------------
vertex ColShaderInOut_XOR vert_col_xorMode(VertexInput in [[stage_in]],
constant FrameUniforms& uniforms [[buffer(FrameUniformBuffer)]],
constant TransformMatrix& transform [[buffer(MatrixBuffer)]])
{
ColShaderInOut_XOR out;
float4 pos4 = float4(in.position, 0.0, 1.0);
out.position = transform.transformMatrix*pos4;
out.ptSize = 1.0;
out.orig_pos = in.position;
out.color = half4(uniforms.color.r, uniforms.color.g, uniforms.color.b, uniforms.color.a);
return out;
}
fragment half4 frag_col_xorMode(ColShaderInOut_XOR in [[stage_in]],
texture2d<float, access::read> renderTexture [[texture(0)]])
{
uint2 texCoord = {(unsigned int)(in.orig_pos.x), (unsigned int)(in.orig_pos.y)};
float4 pixelColor = renderTexture.read(texCoord);
half4 color = in.color;
half4 c;
c.r = float( (unsigned char)(pixelColor.r * 255.0) ^ (unsigned char)(color.r * 255.0)) / 255.0f;
c.g = float( (unsigned char)(pixelColor.g * 255.0) ^ (unsigned char)(color.g * 255.0)) / 255.0f;
c.b = float( (unsigned char)(pixelColor.b * 255.0) ^ (unsigned char)(color.b * 255.0)) / 255.0f;
c.a = 1.0;
return c;
}
vertex TxtShaderInOut_XOR vert_txt_xorMode(
TxtVertexInput in [[stage_in]],
constant TransformMatrix& transform [[buffer(MatrixBuffer)]])
{
TxtShaderInOut_XOR out;
float4 pos4 = float4(in.position, 0.0, 1.0);
out.position = transform.transformMatrix*pos4;
out.orig_pos = in.position;
out.texCoords = in.texCoords;
return out;
}
fragment half4 frag_txt_xorMode(
TxtShaderInOut_XOR vert [[stage_in]],
texture2d<float, access::sample> renderTexture [[texture(0)]],
texture2d<float, access::read> backgroundTexture [[texture(1)]],
constant TxtFrameUniforms& uniforms [[buffer(1)]],
sampler textureSampler [[sampler(0)]])
{
uint2 texCoord = {(unsigned int)(vert.orig_pos.x), (unsigned int)(vert.orig_pos.y)};
float4 bgColor = backgroundTexture.read(texCoord);
float4 pixelColor = renderTexture.sample(textureSampler, vert.texCoords);
float srcA = uniforms.isSrcOpaque ? 1 : pixelColor.a;
float4 c;
if (uniforms.mode) {
c = mix(pixelColor, uniforms.color, srcA);
} else {
c = float4(pixelColor.r,
pixelColor.g,
pixelColor.b, srcA)*uniforms.extraAlpha;
}
half4 ret;
ret.r = half( (unsigned char)(c.r * 255.0) ^ (unsigned char)(bgColor.r * 255.0)) / 255.0f;
ret.g = half( (unsigned char)(c.g * 255.0) ^ (unsigned char)(bgColor.g * 255.0)) / 255.0f;
ret.b = half( (unsigned char)(c.b * 255.0) ^ (unsigned char)(bgColor.b * 255.0)) / 255.0f;
ret.a = c.a + (1.0 - c.a) * bgColor.a;
return ret;
}
/*
// --------------------------------------------------------------------------------------
Currently, gradient paint and texture paint XOR mode rendering has been implemented
through tile based rendering (similar to OGL) that uses MTLBlitLoops_SurfaceToSwBlit method for
getting framebuffer tiles and render using a different render pipe (not MTLRenderer)
In metal, we can avoid tile based rendering and use below shaders.
NOTE: These two shaders are incomplete and need some tweak.
// --------------------------------------------------------------------------------------
fragment half4 frag_grad_xorMode(GradShaderInOut_XOR in [[stage_in]],
texture2d<float, access::read> renderTexture [[texture(0)]],
constant GradFrameUniforms& uniforms [[buffer(0)]]) {
uint2 texCoord = {(unsigned int)(in.orig_pos.x), (unsigned int)(in.orig_pos.y)};
float4 pixelColor = renderTexture.read(texCoord);
float3 v = float3(in.position.x, in.position.y, 1);
float a = (dot(v,uniforms.params)-0.25)*2.0;
float4 c = mix(uniforms.color1, uniforms.color2, a);
half4 ret;
ret.r = float( (unsigned char)(pixelColor.r * 255.0) ^ (unsigned char)(c.r * 255.0)) / 255.0f;
ret.g = float( (unsigned char)(pixelColor.g * 255.0) ^ (unsigned char)(c.g * 255.0)) / 255.0f;
ret.b = float( (unsigned char)(pixelColor.b * 255.0) ^ (unsigned char)(c.b * 255.0)) / 255.0f;
return half4(ret);
}
fragment half4 frag_tp_xorMode(
TxtShaderInOut vert [[stage_in]],
texture2d<float, access::sample> renderTexture [[texture(0)]],
texture2d<float, access::read> backgroundTexture [[texture(1)]],
constant int& xorColor[[buffer(0)]])
{
uint2 texCoord = {(unsigned int)(vert.orig_pos.x), (unsigned int)(vert.orig_pos.y)};
float4 bgColor = backgroundTexture.read(texCoord);
constexpr sampler textureSampler (address::repeat,
mag_filter::nearest,
min_filter::nearest);
float4 pixelColor = renderTexture.sample(textureSampler, vert.texCoords);
pixelColor.r = float( (unsigned char)(pixelColor.r * 255.0) ^ ((xorColor >> 16) & 0xFF) ) / 255.0f;
pixelColor.g = float( (unsigned char)(pixelColor.g * 255.0) ^ ((xorColor >> 8) & 0xFF)) / 255.0f;
pixelColor.b = float( (unsigned char)(pixelColor.b * 255.0) ^ (xorColor & 0xFF)) / 255.0f;
pixelColor.a = 1.0;
half4 ret;
ret.r = half( (unsigned char)(pixelColor.r * 255.0) ^ (unsigned char)(bgColor.r * 255.0)) / 255.0f;
ret.g = half( (unsigned char)(pixelColor.g * 255.0) ^ (unsigned char)(bgColor.g * 255.0)) / 255.0f;
ret.b = half( (unsigned char)(pixelColor.b * 255.0) ^ (unsigned char)(bgColor.b * 255.0)) / 255.0f;
ret.a = 1.0;
return ret;
// This implementation defaults alpha to 1.0 as if source is opaque
//TODO : implement alpha component value if source is transparent
}
*/