diff options
author | Brian Salomon <bsalomon@google.com> | 2017-07-18 12:22:58 -0400 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2017-07-18 16:43:23 +0000 |
commit | 1806e33e6a09b1361de4925d32389a01e0e2c7e7 (patch) | |
tree | c435d7761eafdbf1c23d3a85baaad9b3da89227a /src/gpu/effects | |
parent | 086a427b0cee3862f25c492fc5082ff24105dc53 (diff) |
Improvements to GPU dither.
1) Makes the range of the offset dependent on the config.
2) Uses an ordered dither on GPUs that support integers in shaders.
3) Enables dithering for all paints with dither flag when the color type of the dst is 4444
4) Dithers r,g,b and clamps to 0,a rather than dithering all four channels (same as CPU backend).
Bug: skia:
Change-Id: Ie22c3adc38c6d1dbbcd97e4b7d16fc843e392c2e
Reviewed-on: https://skia-review.googlesource.com/23485
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
Diffstat (limited to 'src/gpu/effects')
-rw-r--r-- | src/gpu/effects/GrDitherEffect.cpp | 7 | ||||
-rw-r--r-- | src/gpu/effects/GrDitherEffect.fp | 83 | ||||
-rw-r--r-- | src/gpu/effects/GrDitherEffect.h | 36 |
3 files changed, 107 insertions, 19 deletions
diff --git a/src/gpu/effects/GrDitherEffect.cpp b/src/gpu/effects/GrDitherEffect.cpp index 052ed4e36d..753bbce185 100644 --- a/src/gpu/effects/GrDitherEffect.cpp +++ b/src/gpu/effects/GrDitherEffect.cpp @@ -23,7 +23,7 @@ public: GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; const GrDitherEffect& _outer = args.fFp.cast<GrDitherEffect>(); (void) _outer; - fragBuilder->codeAppendf("float r = fract(sin(dot(sk_FragCoord.xy, vec2(12.989800000000001, 78.233000000000004))) * 43758.545299999998) - 0.5;\n%s = clamp(0.0039215686274509803 * vec4(r) + %s, 0.0, 1.0);\n", args.fOutputColor, args.fInputColor ? args.fInputColor : "vec4(1)"); + fragBuilder->codeAppendf("float value;\nfloat range;\n@switch (%d) {\n case 0:\n range = 0.0039215686274509803;\n break;\n case 1:\n range = 0.015873015873015872;\n break;\n default:\n range = 0.0083333333333333332;\n break;\n}\n@if (sk_Caps.integerSupport) {\n int x = int(sk_FragCoord.x);\n int y = int(sk_FragCoord.y);\n uint m = uint((((((y & 1) << 5 | (x & 1) << 4) | (y & 2) << 2) | (x & 2) << 1) | (y & 4) >> 1) | (x & 4) >> 2);\n value = float(m) / 64.0 - 0.4921875;\n} else {\n value = fract(sin(dot(sk_FragCoord.xy, vec2(12.989800000000001, 78.233000000000004))) * 43758.545299999998) - 0.5;\n}\n%s = vec4(clamp(%s.xyz + value * range, 0.0, %s.w), %s.w);\n", _outer.rangeType(), args.fOutputColor, args.fInputColor ? args.fInputColor : "vec4(1)", args.fInputColor ? args.fInputColor : "vec4(1)", args.fInputColor ? args.fInputColor : "vec4(1)"); } private: void onSetData(const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& _proc) override { @@ -33,17 +33,20 @@ GrGLSLFragmentProcessor* GrDitherEffect::onCreateGLSLInstance() const { return new GrGLSLDitherEffect(); } void GrDitherEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const { + b->add32(fRangeType); } bool GrDitherEffect::onIsEqual(const GrFragmentProcessor& other) const { const GrDitherEffect& that = other.cast<GrDitherEffect>(); (void) that; + if (fRangeType != that.fRangeType) return false; return true; } GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrDitherEffect); #if GR_TEST_UTILS sk_sp<GrFragmentProcessor> GrDitherEffect::TestCreate(GrProcessorTestData* testData) { - return GrDitherEffect::Make(); + float range = testData->fRandom->nextRangeF(0.001f, 0.05f); + return sk_sp<GrFragmentProcessor>(new GrDitherEffect(range)); } #endif #endif diff --git a/src/gpu/effects/GrDitherEffect.fp b/src/gpu/effects/GrDitherEffect.fp index 1c35b38cd2..937efae264 100644 --- a/src/gpu/effects/GrDitherEffect.fp +++ b/src/gpu/effects/GrDitherEffect.fp @@ -1,17 +1,74 @@ -void main() { - // Generate a random number based on the fragment position. For this - // random number generator, we use the "GLSL rand" function - // that seems to be floating around on the internet. It works under - // the assumption that sin(<big number>) oscillates with high frequency - // and sampling it will generate "randomness". Since we're using this - // for rendering and not cryptography it should be OK. +// This controls the range of values added to color channels +layout(key) in int rangeType; - // For each channel c, add the random offset to the pixel to either bump - // it up or let it remain constant during quantization. - float r = fract(sin(dot(sk_FragCoord.xy, vec2(12.9898, 78.233))) * 43758.5453) - .5; - sk_OutColor = clamp(1 / 255.0 * vec4(r) + sk_InColor, 0, 1); +@make { + static sk_sp<GrFragmentProcessor> Make(GrPixelConfig dstConfig) { + int rangeType; + switch (dstConfig) { + case kGray_8_GrPixelConfig: + case kRGBA_8888_GrPixelConfig: + case kBGRA_8888_GrPixelConfig: + case kSRGBA_8888_GrPixelConfig: + case kSBGRA_8888_GrPixelConfig: + rangeType = 0; + break; + case kRGB_565_GrPixelConfig: + rangeType = 1; + break; + case kRGBA_4444_GrPixelConfig: + rangeType = 2; + break; + case kUnknown_GrPixelConfig: + case kAlpha_half_GrPixelConfig: + case kRGBA_8888_sint_GrPixelConfig: + case kRGBA_float_GrPixelConfig: + case kRG_float_GrPixelConfig: + case kRGBA_half_GrPixelConfig: + case kAlpha_8_GrPixelConfig: + return nullptr; + } + return sk_sp<GrFragmentProcessor>(new GrDitherEffect(rangeType)); + } +} + +void main() { + float value; + float range; + @switch (rangeType) { + case 0: + range = 1.0 / 255.0; + break; + case 1: + range = 1.0 / 63.0; + break; + default: + // Experimentally this looks better than the expected value of 1/15. + range = 0.125 / 15.0; + break; + } + @if (sk_Caps.integerSupport) { + // This ordered-dither code is lifted from the cpu backend. + int x = int(sk_FragCoord.x); + int y = int(sk_FragCoord.y); + uint m = (y & 1) << 5 | (x & 1) << 4 | + (y & 2) << 2 | (x & 2) << 1 | + (y & 4) >> 1 | (x & 4) >> 2; + value = float(m) * 1.0 / 64.0 - 63.0 / 128.0; + } else { + // Generate a random number based on the fragment position. For this + // random number generator, we use the "GLSL rand" function + // that seems to be floating around on the internet. It works under + // the assumption that sin(<big number>) oscillates with high frequency + // and sampling it will generate "randomness". Since we're using this + // for rendering and not cryptography it should be OK. + value = fract(sin(dot(sk_FragCoord.xy, vec2(12.9898, 78.233))) * 43758.5453) - .5; + } + // For each color channel, add the random offset to the channel value and then clamp + // between 0 and alpha to keep the color premultiplied. + sk_OutColor = vec4(clamp(sk_InColor.rgb + value * range, 0, sk_InColor.a), sk_InColor.a); } @test(testData) { - return GrDitherEffect::Make(); -}
\ No newline at end of file + float range = testData->fRandom->nextRangeF(0.001f, 0.05f); + return sk_sp<GrFragmentProcessor>(new GrDitherEffect(range)); +} diff --git a/src/gpu/effects/GrDitherEffect.h b/src/gpu/effects/GrDitherEffect.h index a996ad94c2..63c0df7eb2 100644 --- a/src/gpu/effects/GrDitherEffect.h +++ b/src/gpu/effects/GrDitherEffect.h @@ -18,19 +18,47 @@ #include "effects/GrProxyMove.h" class GrDitherEffect : public GrFragmentProcessor { public: - static sk_sp<GrFragmentProcessor> Make() { - return sk_sp<GrFragmentProcessor>(new GrDitherEffect()); + int rangeType() const { return fRangeType; } + + static sk_sp<GrFragmentProcessor> Make(GrPixelConfig dstConfig) { + int rangeType; + switch (dstConfig) { + case kGray_8_GrPixelConfig: + case kRGBA_8888_GrPixelConfig: + case kBGRA_8888_GrPixelConfig: + case kSRGBA_8888_GrPixelConfig: + case kSBGRA_8888_GrPixelConfig: + rangeType = 0; + break; + case kRGB_565_GrPixelConfig: + rangeType = 1; + break; + case kRGBA_4444_GrPixelConfig: + rangeType = 2; + break; + case kUnknown_GrPixelConfig: + case kAlpha_half_GrPixelConfig: + case kRGBA_8888_sint_GrPixelConfig: + case kRGBA_float_GrPixelConfig: + case kRG_float_GrPixelConfig: + case kRGBA_half_GrPixelConfig: + case kAlpha_8_GrPixelConfig: + return nullptr; + } + return sk_sp<GrFragmentProcessor>(new GrDitherEffect(rangeType)); } const char* name() const override { return "DitherEffect"; } private: - GrDitherEffect() - : INHERITED(kNone_OptimizationFlags) { + GrDitherEffect(int rangeType) + : INHERITED(kNone_OptimizationFlags) + , fRangeType(rangeType) { this->initClassID<GrDitherEffect>(); } GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; void onGetGLSLProcessorKey(const GrShaderCaps&,GrProcessorKeyBuilder*) const override; bool onIsEqual(const GrFragmentProcessor&) const override; GR_DECLARE_FRAGMENT_PROCESSOR_TEST + int fRangeType; typedef GrFragmentProcessor INHERITED; }; #endif |