| /* |
| * Copyright 2020 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| in fragmentProcessor inputFP; |
| |
| // Larger values increase the strength of the dithering effect. |
| in uniform half range; |
| |
| half4 main() { |
| half4 color = sample(inputFP); |
| half value; |
| @if (sk_Caps.integerSupport) |
| { |
| // This ordered-dither code is lifted from the cpu backend. |
| uint x = uint(sk_FragCoord.x); |
| uint y = uint(sk_FragCoord.y) ^ x; |
| uint m = (y & 1) << 5 | (x & 1) << 4 | |
| (y & 2) << 2 | (x & 2) << 1 | |
| (y & 4) >> 1 | (x & 4) >> 2; |
| value = half(m) * 1.0 / 64.0 - 63.0 / 128.0; |
| } else { |
| // Simulate the integer effect used above using step/mod/abs. For speed, simulates a 4x4 |
| // dither pattern rather than an 8x8 one. Since it's 4x4, this is effectively computing: |
| // uint m = (y & 1) << 3 | (x & 1) << 2 | |
| // (y & 2) << 0 | (x & 2) >> 1; |
| // where 'y' has already been XOR'ed with 'x' as in the integer-supported case. |
| |
| // To get the low bit of p.x and p.y, we compute mod 2.0; for the high bit, we mod 4.0 |
| half4 bits = mod(half4(sk_FragCoord.yxyx), half4(2.0, 2.0, 4.0, 4.0)); |
| // Use step to convert the 0-3 value in bits.zw into a 0|1 value. bits.xy is already 0|1. |
| bits.zw = step(2.0, bits.zw); |
| // bits was constructed such that the p.x bits were already in the right place for |
| // interleaving (in bits.yw). We just need to update the other bits from p.y to (p.x ^ p.y). |
| // These are in bits.xz. Since the values are 0|1, we can simulate ^ as abs(y - x). |
| bits.xz = abs(bits.xz - bits.yw); |
| |
| // Manual binary sum, divide by N^2, and offset |
| value = dot(bits, half4(8.0 / 16.0, 4.0 / 16.0, 2.0 / 16.0, 1.0 / 16.0)) - 15.0 / 32.0; |
| } |
| // For each color channel, add the random offset to the channel value and then clamp |
| // between 0 and alpha to keep the color premultiplied. |
| return half4(clamp(color.rgb + value * range, 0.0, color.a), color.a); |
| } |
| |
| @optimizationFlags { |
| ProcessorOptimizationFlags(inputFP.get()) & kPreservesOpaqueInput_OptimizationFlag |
| } |
| |
| @test(d) { |
| float range = 1.0f - d->fRandom->nextRangeF(0.0f, 1.0f); |
| return GrDitherEffect::Make(GrProcessorUnitTest::MakeChildFP(d), range); |
| } |
| |
| @make { |
| static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> inputFP, |
| float range) { |
| if (range == 0.0 || inputFP == nullptr) { |
| return inputFP; |
| } |
| return std::unique_ptr<GrFragmentProcessor>(new GrDitherEffect(std::move(inputFP), range)); |
| } |
| } |