diff options
author | krajcevski <krajcevski@google.com> | 2014-06-19 14:14:06 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-06-19 14:14:06 -0700 |
commit | f461a8fdf642ba713dcdfb217534652df1eac278 (patch) | |
tree | 6e1e18eaf27c8904e403ad5aa819f14972eff3df | |
parent | 8b2fac4b25dfb8180c54707f8e19698a9fddde07 (diff) |
Simple GPU based dithering:
If dithering is turned on, apply an effect that filters the pixel through
the following pipeline:
for each channel c:
1. Compute quantized colors [low, high] that c is between
2. Pick high by flipping a coin weighted by (c - low)
R=bsalomon@google.com, egdaniel@google.com, robertphillips@google.com
Author: krajcevski@google.com
Review URL: https://codereview.chromium.org/321253002
-rw-r--r-- | bench/GradientBench.cpp | 48 | ||||
-rw-r--r-- | expectations/gm/ignored-tests.txt | 23 | ||||
-rw-r--r-- | gyp/gpu.gypi | 2 | ||||
-rw-r--r-- | gyp/skia_for_chromium_defines.gypi | 1 | ||||
-rw-r--r-- | src/gpu/SkGr.cpp | 53 | ||||
-rw-r--r-- | src/gpu/effects/GrDitherEffect.cpp | 115 | ||||
-rw-r--r-- | src/gpu/effects/GrDitherEffect.h | 23 |
7 files changed, 248 insertions, 17 deletions
diff --git a/bench/GradientBench.cpp b/bench/GradientBench.cpp index 90d45c812c..f3e783e78f 100644 --- a/bench/GradientBench.cpp +++ b/bench/GradientBench.cpp @@ -34,12 +34,15 @@ static const SkColor gColors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK, // 10 lines, 50 colors }; +static const SkColor gShallowColors[] = { 0xFF555555, 0xFF444444 }; + // We have several special-cases depending on the number (and spacing) of colors, so // try to exercise those here. static const GradData gGradData[] = { { 2, gColors, NULL, "" }, { 50, gColors, NULL, "_hicolor" }, // many color gradient { 3, gColors, NULL, "_3color" }, + { 2, gShallowColors, NULL, "_shallow" }, }; /// Ignores scale @@ -200,12 +203,22 @@ static const char* geomtypename(GeomType gt) { class GradientBench : public Benchmark { SkString fName; SkShader* fShader; + bool fDither; enum { W = 400, H = 400, kRepeat = 15, }; public: + SkShader* makeShader(GradType gradType, GradData data, SkShader::TileMode tm, float scale) { + const SkPoint pts[2] = { + { 0, 0 }, + { SkIntToScalar(W), SkIntToScalar(H) } + }; + + return gGrads[gradType].fMaker(pts, data, tm, scale); + } + GradientBench(GradType gradType, GradData data = gGradData[0], SkShader::TileMode tm = SkShader::kClamp_TileMode, @@ -224,15 +237,25 @@ public: fName.append(data.fName); - const SkPoint pts[2] = { - { 0, 0 }, - { SkIntToScalar(W), SkIntToScalar(H) } - }; - - fShader = gGrads[gradType].fMaker(pts, data, tm, scale); + fDither = false; + fShader = this->makeShader(gradType, data, tm, scale); fGeomType = geomType; } + GradientBench(GradType gradType, GradData data, bool dither) { + const char *tmname = tilemodename(SkShader::kClamp_TileMode); + fName.printf("gradient_%s_%s", gGrads[gradType].fName, tmname); + fName.append(data.fName); + + fDither = dither; + if (dither) { + fName.appendf("_dither"); + } + + fShader = this->makeShader(gradType, data, SkShader::kClamp_TileMode, 1.0f); + fGeomType = kRect_GeomType; + } + virtual ~GradientBench() { fShader->unref(); } @@ -247,6 +270,9 @@ protected: this->setupPaint(&paint); paint.setShader(fShader); + if (fDither) { + paint.setDither(true); + } SkRect r = { 0, 0, SkIntToScalar(W), SkIntToScalar(H) }; for (int i = 0; i < loops * kRepeat; i++) { @@ -304,6 +330,16 @@ DEF_BENCH( return new GradientBench(kConicalOutZero_GradType); ) DEF_BENCH( return new GradientBench(kConicalOutZero_GradType, gGradData[1]); ) DEF_BENCH( return new GradientBench(kConicalOutZero_GradType, gGradData[2]); ) +// Dithering +DEF_BENCH( return new GradientBench(kLinear_GradType, gGradData[3], true); ) +DEF_BENCH( return new GradientBench(kLinear_GradType, gGradData[3], false); ) +DEF_BENCH( return new GradientBench(kRadial_GradType, gGradData[3], true); ) +DEF_BENCH( return new GradientBench(kRadial_GradType, gGradData[3], false); ) +DEF_BENCH( return new GradientBench(kSweep_GradType, gGradData[3], true); ) +DEF_BENCH( return new GradientBench(kSweep_GradType, gGradData[3], false); ) +DEF_BENCH( return new GradientBench(kConical_GradType, gGradData[3], true); ) +DEF_BENCH( return new GradientBench(kConical_GradType, gGradData[3], false); ) + /////////////////////////////////////////////////////////////////////////////// class Gradient2Bench : public Benchmark { diff --git a/expectations/gm/ignored-tests.txt b/expectations/gm/ignored-tests.txt index bd5c6ec59b..2cc1b8506b 100644 --- a/expectations/gm/ignored-tests.txt +++ b/expectations/gm/ignored-tests.txt @@ -50,3 +50,26 @@ bigblurs # This CL actually fixes this GM's image distantclip +# krajcevski: +# Added GPU-based dithering for all SkPaints that have that flag set +# https://codereview.chromium.org/321253002/ +radial_gradient +modecolorfilters +scaled_tilemodes +tilemodes +scaled_tilemodes_npot +lerpmode +xfermodes2 +xfermodes +drawbitmapmatrix +complexclip_bw_layer +complexclip_aa_layer +hairmodes +aarectmodes +tilemodes_npot +convex_poly_clip +lumafilter +shadertext +bitmapfilters +arithmode +optimizations diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi index fed08f2c1a..c03f8eff3b 100644 --- a/gyp/gpu.gypi +++ b/gyp/gpu.gypi @@ -157,6 +157,8 @@ '<(skia_src_path)/gpu/effects/GrDashingEffect.h', '<(skia_src_path)/gpu/effects/GrDistanceFieldTextureEffect.cpp', '<(skia_src_path)/gpu/effects/GrDistanceFieldTextureEffect.h', + '<(skia_src_path)/gpu/effects/GrDitherEffect.cpp', + '<(skia_src_path)/gpu/effects/GrDitherEffect.h', '<(skia_src_path)/gpu/effects/GrOvalEffect.cpp', '<(skia_src_path)/gpu/effects/GrOvalEffect.h', '<(skia_src_path)/gpu/effects/GrRRectEffect.cpp', diff --git a/gyp/skia_for_chromium_defines.gypi b/gyp/skia_for_chromium_defines.gypi index 4f4b8d07e4..2212519671 100644 --- a/gyp/skia_for_chromium_defines.gypi +++ b/gyp/skia_for_chromium_defines.gypi @@ -19,6 +19,7 @@ 'SK_SUPPORT_LEGACY_N32_NAME', 'SK_SUPPORT_LEGACY_SETCONFIG', 'SK_IGNORE_ETC1_SUPPORT', + 'SK_IGNORE_GPU_DITHER', ], }, } diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp index 8e7cea64d4..a1b379fe8c 100644 --- a/src/gpu/SkGr.cpp +++ b/src/gpu/SkGr.cpp @@ -13,6 +13,7 @@ #include "SkPixelRef.h" #include "GrResourceCache.h" #include "GrGpu.h" +#include "effects/GrDitherEffect.h" #include "GrDrawTargetCaps.h" #ifndef SK_IGNORE_ETC1_SUPPORT @@ -460,6 +461,29 @@ void SkPaint2GrPaintNoShader(GrContext* context, const SkPaint& skPaint, GrColor } } } + +#ifndef SK_IGNORE_GPU_DITHER + // If the dither flag is set, then we need to see if the underlying context + // supports it. If not, then install a dither effect. + if (skPaint.isDither() && grPaint->numColorStages() > 0) { + // What are we rendering into? + const GrRenderTarget *target = context->getRenderTarget(); + SkASSERT(NULL != target); + + // Suspect the dithering flag has no effect on these configs, otherwise + // fall back on setting the appropriate state. + if (target->config() == kRGBA_8888_GrPixelConfig || + target->config() == kBGRA_8888_GrPixelConfig) { + // The dither flag is set and the target is likely + // not going to be dithered by the GPU. + SkAutoTUnref<GrEffectRef> effect(GrDitherEffect::Create()); + if (NULL != effect.get()) { + grPaint->addColorEffect(effect); + grPaint->setDither(false); + } + } + } +#endif } /** @@ -494,19 +518,26 @@ void SkPaint2GrPaintShader(GrContext* context, const SkPaint& skPaint, // SkShader::asNewEffect() may do offscreen rendering. Save off the current RT, clip, and // matrix. We don't reset the matrix on the context because SkShader::asNewEffect may use // GrContext::getMatrix() to know the transformation from local coords to device space. - GrContext::AutoRenderTarget art(context, NULL); - GrContext::AutoClip ac(context, GrContext::AutoClip::kWideOpen_InitialClip); - AutoMatrix am(context); - - // setup the shader as the first color effect on the paint - // the default grColor is the paint's color GrColor grColor = SkColor2GrColor(skPaint.getColor()); - GrEffectRef* grEffect = NULL; - if (shader->asNewEffect(context, skPaint, NULL, &grColor, &grEffect) && NULL != grEffect) { - SkAutoTUnref<GrEffectRef> effect(grEffect); - grPaint->addColorEffect(effect); - constantColor = false; + + // Start a new block here in order to preserve our context state after calling + // asNewEffect(). Since these calls get passed back to the client, we don't really + // want them messing around with the context. + { + GrContext::AutoRenderTarget art(context, NULL); + GrContext::AutoClip ac(context, GrContext::AutoClip::kWideOpen_InitialClip); + AutoMatrix am(context); + + // setup the shader as the first color effect on the paint + // the default grColor is the paint's color + GrEffectRef* grEffect = NULL; + if (shader->asNewEffect(context, skPaint, NULL, &grColor, &grEffect) && NULL != grEffect) { + SkAutoTUnref<GrEffectRef> effect(grEffect); + grPaint->addColorEffect(effect); + constantColor = false; + } } + // The grcolor is automatically set when calling asneweffect. // If the shader can be seen as an effect it returns true and adds its effect to the grpaint. SkPaint2GrPaintNoShader(context, skPaint, grColor, constantColor, grPaint); diff --git a/src/gpu/effects/GrDitherEffect.cpp b/src/gpu/effects/GrDitherEffect.cpp new file mode 100644 index 0000000000..be226570fa --- /dev/null +++ b/src/gpu/effects/GrDitherEffect.cpp @@ -0,0 +1,115 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrDitherEffect.h" + +#include "gl/GrGLEffect.h" +#include "gl/GrGLSL.h" +#include "GrTBackendEffectFactory.h" + +#include "SkRect.h" + +////////////////////////////////////////////////////////////////////////////// + +class GLDitherEffect; + +class DitherEffect : public GrEffect { +public: + static GrEffectRef* Create() { + return CreateEffectRef(AutoEffectUnref(SkNEW(DitherEffect))); + } + + virtual ~DitherEffect() {}; + static const char* Name() { return "Dither"; } + + typedef GLDitherEffect GLEffect; + + virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE; + + virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE { + return GrTBackendEffectFactory<DitherEffect>::getInstance(); + } + +private: + DitherEffect() { + this->setWillReadFragmentPosition(); + } + + // All dither effects are equal + virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE { return true; } + + GR_DECLARE_EFFECT_TEST; + + typedef GrEffect INHERITED; +}; + +void DitherEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const { + *validFlags = 0; +} + +////////////////////////////////////////////////////////////////////////////// + +GR_DEFINE_EFFECT_TEST(DitherEffect); + +GrEffectRef* DitherEffect::TestCreate(SkRandom*, + GrContext*, + const GrDrawTargetCaps&, + GrTexture*[]) { + return DitherEffect::Create(); +} + +////////////////////////////////////////////////////////////////////////////// + +class GLDitherEffect : public GrGLEffect { +public: + GLDitherEffect(const GrBackendEffectFactory&, const GrDrawEffect&); + + virtual void emitCode(GrGLShaderBuilder* builder, + const GrDrawEffect& drawEffect, + EffectKey key, + const char* outputColor, + const char* inputColor, + const TransformedCoordsArray&, + const TextureSamplerArray&) SK_OVERRIDE; + +private: + typedef GrGLEffect INHERITED; +}; + +GLDitherEffect::GLDitherEffect(const GrBackendEffectFactory& factory, + const GrDrawEffect& drawEffect) + : INHERITED (factory) { +} + +void GLDitherEffect::emitCode(GrGLShaderBuilder* builder, + const GrDrawEffect& drawEffect, + EffectKey key, + const char* outputColor, + const char* inputColor, + const TransformedCoordsArray&, + const TextureSamplerArray& samplers) { + // 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. + + // For each channel c, add the random offset to the pixel to either bump + // it up or let it remain constant during quantization. + builder->fsCodeAppendf("\t\tfloat r = " + "fract(sin(dot(%s.xy ,vec2(12.9898,78.233))) * 43758.5453);\n", + builder->fragmentPosition()); + builder->fsCodeAppendf("\t\t%s = (1.0f/255.0f) * vec4(r, r, r, r) + %s;\n", + outputColor, GrGLSLExpr4(inputColor).c_str()); +} + +////////////////////////////////////////////////////////////////////////////// + +GrEffectRef* GrDitherEffect::Create() { + return DitherEffect::Create(); +} diff --git a/src/gpu/effects/GrDitherEffect.h b/src/gpu/effects/GrDitherEffect.h new file mode 100644 index 0000000000..63036c0c7f --- /dev/null +++ b/src/gpu/effects/GrDitherEffect.h @@ -0,0 +1,23 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrDitherEffect_DEFINED +#define GrDitherEffect_DEFINED + +#include "GrTypes.h" +#include "GrTypesPriv.h" + +class GrEffectRef; + +namespace GrDitherEffect { + /** + * Creates an effect that dithers the resulting color to an RGBA8 framebuffer + */ + GrEffectRef* Create(); +}; + +#endif |