aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar krajcevski <krajcevski@google.com>2014-06-19 14:14:06 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2014-06-19 14:14:06 -0700
commitf461a8fdf642ba713dcdfb217534652df1eac278 (patch)
tree6e1e18eaf27c8904e403ad5aa819f14972eff3df
parent8b2fac4b25dfb8180c54707f8e19698a9fddde07 (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.cpp48
-rw-r--r--expectations/gm/ignored-tests.txt23
-rw-r--r--gyp/gpu.gypi2
-rw-r--r--gyp/skia_for_chromium_defines.gypi1
-rw-r--r--src/gpu/SkGr.cpp53
-rw-r--r--src/gpu/effects/GrDitherEffect.cpp115
-rw-r--r--src/gpu/effects/GrDitherEffect.h23
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