blob: bbf295fe2e9581f08d6ee062c068a68706d5acb9 [file] [log] [blame]
 /* * 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 { (inputFP ? ProcessorOptimizationFlags(inputFP.get()) : kAll_OptimizationFlags) & 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 Make(std::unique_ptr inputFP, float range) { if (range == 0.0 || inputFP == nullptr) { return inputFP; } return std::unique_ptr(new GrDitherEffect(std::move(inputFP), range)); } }