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>
This commit is contained in:
parent
086a427b0c
commit
1806e33e6a
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -1,17 +1,74 @@
|
||||
// This controls the range of values added to color channels
|
||||
layout(key) in int rangeType;
|
||||
|
||||
@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.
|
||||
|
||||
// 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);
|
||||
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();
|
||||
float range = testData->fRandom->nextRangeF(0.001f, 0.05f);
|
||||
return sk_sp<GrFragmentProcessor>(new GrDitherEffect(range));
|
||||
}
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user