aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Brian Salomon <bsalomon@google.com>2017-07-18 12:22:58 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2017-07-18 16:43:23 +0000
commit1806e33e6a09b1361de4925d32389a01e0e2c7e7 (patch)
treec435d7761eafdbf1c23d3a85baaad9b3da89227a
parent086a427b0cee3862f25c492fc5082ff24105dc53 (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>
-rw-r--r--src/core/SkPaintPriv.cpp4
-rw-r--r--src/gpu/SkGr.cpp5
-rw-r--r--src/gpu/effects/GrDitherEffect.cpp7
-rw-r--r--src/gpu/effects/GrDitherEffect.fp83
-rw-r--r--src/gpu/effects/GrDitherEffect.h36
5 files changed, 113 insertions, 22 deletions
diff --git a/src/core/SkPaintPriv.cpp b/src/core/SkPaintPriv.cpp
index dcd8dd6a5a..7d771f88a6 100644
--- a/src/core/SkPaintPriv.cpp
+++ b/src/core/SkPaintPriv.cpp
@@ -75,8 +75,8 @@ bool SkPaintPriv::ShouldDither(const SkPaint& p, SkColorType dstCT) {
return false;
}
- // We always dither 565 when requested.
- if (dstCT == SkColorType::kRGB_565_SkColorType) {
+ // We always dither 565 or 4444 when requested.
+ if (dstCT == kRGB_565_SkColorType || dstCT == kARGB_4444_SkColorType) {
return true;
}
diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp
index b37a607052..6d8e46ddca 100644
--- a/src/gpu/SkGr.cpp
+++ b/src/gpu/SkGr.cpp
@@ -535,7 +535,10 @@ static inline bool skpaint_to_grpaint_impl(GrContext* context,
GrPixelConfigToColorType(rtc->config(), &ct);
if (SkPaintPriv::ShouldDither(skPaint, ct) && grPaint->numColorFragmentProcessors() > 0
&& !rtc->isGammaCorrect()) {
- grPaint->addColorFragmentProcessor(GrDitherEffect::Make());
+ auto ditherFP = GrDitherEffect::Make(rtc->config());
+ if (ditherFP) {
+ grPaint->addColorFragmentProcessor(std::move(ditherFP));
+ }
}
#endif
return true;
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