uniform shader image; uniform float2 imageSize; uniform float amount; uniform float passthrough; half4 main(float2 coord) { float3x3 G[2]; G[0] = mat3(1.0, 2.0, 1.0, 0.0, 0.0, 0.0, -1.0, -2.0, -1.0); G[1] = mat3(1.0, 0.0, -1.0, 2.0, 0.0, -2.0, 1.0, 0.0, -1.0); // There used to be a bug in this shader which caused us to always use 1/512 as the // relative texel size. Using correct pixel offsets instead results in far better // outputs for lower resolution images but also makes the outline a lot less pronounced // in higher resolution images. In order to mitigate this, we now always offset at // least 1/512 of the image size so that the output is the same as before for higher resolution // images. float2 texelScale = max(float2(1.0), imageSize / float2(512.0)); /* fetch the 3x3 neighbourhood and use the RGB vectors length as intensity value */ float3x3 I; for (float i = 0.0; i < 3.0; i++) { for (float j = 0.0; j < 3.0; j++) { float3 probe = unpremul(image.eval(coord + texelScale * float2(i - 1, j - 1))).rgb; I[int(i)][int(j)] = length(probe); } } /* calculate the convolution values for all the masks */ float cnv[2]; for (int i = 0; i < 2; i++) { float dp3 = dot(G[i][0], I[0]) + dot(G[i][1], I[1]) + dot(G[i][2], I[2]); cnv[i] = dp3 * dp3; } float4 orig = unpremul(image.eval(coord)); float4 color = orig * passthrough + float4(0.5 * sqrt(cnv[0] * cnv[0] + cnv[1] * cnv[1])) * amount; color.rgb *= color.a; return color; }