diff options
author | Brian Osman <brianosman@google.com> | 2017-01-26 09:32:33 -0500 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2017-01-26 15:27:49 +0000 |
commit | 964dec3948721808491b21b4ff4ff41a466443ec (patch) | |
tree | 57e3d48e97aecafc872a9cce6285e9edea775cf8 | |
parent | d6016013bdf843013f7fb8648fc5a64ce7e77005 (diff) |
Move SkGammaColorFilter to tools, limit to sRGB
Similarly, limit GrGammaEffect to sRGB (and rename it).
BUG=skia:
Change-Id: I88feef11ab7040bca2fa4c2eed71923ded87a0d0
Reviewed-on: https://skia-review.googlesource.com/7375
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
-rw-r--r-- | gm/gammacolorfilter.cpp | 98 | ||||
-rw-r--r-- | gn/effects.gni | 2 | ||||
-rw-r--r-- | gn/gm.gni | 1 | ||||
-rw-r--r-- | gn/gpu.gni | 4 | ||||
-rw-r--r-- | include/effects/SkGammaColorFilter.h | 44 | ||||
-rw-r--r-- | samplecode/SampleApp.cpp | 3 | ||||
-rw-r--r-- | src/effects/SkGammaColorFilter.cpp | 57 | ||||
-rw-r--r-- | src/gpu/GrContext.cpp | 2 | ||||
-rw-r--r-- | src/gpu/GrYUVProvider.cpp | 4 | ||||
-rw-r--r-- | src/gpu/effects/GrGammaEffect.cpp | 148 | ||||
-rw-r--r-- | src/gpu/effects/GrSRGBEffect.cpp | 109 | ||||
-rw-r--r-- | src/gpu/effects/GrSRGBEffect.h (renamed from src/gpu/effects/GrGammaEffect.h) | 19 | ||||
-rw-r--r-- | tests/ApplyGammaTest.cpp | 147 | ||||
-rw-r--r-- | tools/sk_tool_utils.cpp | 42 | ||||
-rw-r--r-- | tools/sk_tool_utils.h | 6 | ||||
-rw-r--r-- | tools/viewer/sk_app/mac/RasterWindowContext_mac.cpp | 5 |
16 files changed, 249 insertions, 442 deletions
diff --git a/gm/gammacolorfilter.cpp b/gm/gammacolorfilter.cpp index 9e39025b0a..84ebc43def 100644 --- a/gm/gammacolorfilter.cpp +++ b/gm/gammacolorfilter.cpp @@ -1,102 +1,8 @@ /* - * Copyright 2016 Google Inc. + * Copyright 2017 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ -#include "gm.h" -#include "SkGammaColorFilter.h" -#include "SkImage.h" - -// Fill a width x height block with a horizontal ramp from 0 to 255 -static void draw_grey_ramp(SkCanvas* canvas, int width, int height, int numSteps) { - int greyPerStep = SkScalarRoundToInt(255.0f / numSteps); - int widthPerStep = SkScalarRoundToInt(width / SkIntToScalar(numSteps)); - - SkIRect rect = SkIRect::MakeWH(widthPerStep, height); - - SkPaint paint; - int grey = 0; - int x = 0; - for (int i = 0; i < numSteps-1; ++i) { - paint.setColor(SkColorSetARGB(255, grey, grey, grey)); - - rect.offsetTo(x, 0); - canvas->drawRect(SkRect::Make(rect), paint); - - x += widthPerStep; - grey += greyPerStep; - } - - paint.setColor(SK_ColorWHITE); - rect.setLTRB(x, 0, width, height); - canvas->drawRect(SkRect::Make(rect), paint); -} - -static sk_sp<SkImage> create_grey_ramp(int width, int height, int numSteps) { - SkBitmap bm; - bm.allocN32Pixels(width, height); - SkCanvas canvas(bm); - canvas.clear(0x0); - - draw_grey_ramp(&canvas, width, height, numSteps); - - return SkImage::MakeFromBitmap(bm); -} - -namespace skiagm { - -class GammaColorFilterGM : public GM { -public: - GammaColorFilterGM() { - this->setBGColor(SK_ColorBLACK); - } - -protected: - - SkString onShortName() override { - return SkString("gammacolorfilter"); - } - - SkISize onISize() override { - return SkISize::Make(4 * kCellWidth, kCellHeight); - } - - void onDraw(SkCanvas* canvas) override { - GrRenderTargetContext* renderTargetContext = - canvas->internal_private_accessTopLayerRenderTargetContext(); - if (!renderTargetContext) { - skiagm::GM::DrawGpuOnlyMessage(canvas); - return; - } - - sk_sp<SkImage> image(create_grey_ramp(kCellWidth, kCellHeight/2, kNumGreySteps)); - - // Leftmost is a non-gamma pair - draw_grey_ramp(canvas, kCellWidth, kCellHeight/2, kNumGreySteps); - canvas->drawImage(image, 0, kCellHeight/2); - canvas->translate(SkIntToScalar(image->width()), 0); - - for (auto gamma : { 1.0f, 1.0f / 1.8f, 1.0f / 2.2f }) { - SkPaint paint; - paint.setColorFilter(SkGammaColorFilter::Make(gamma)); - - draw_grey_ramp(canvas, kCellWidth, kCellHeight/2, kNumGreySteps); - canvas->drawImage(image, 0, kCellHeight/2, &paint); - canvas->translate(SkIntToScalar(image->width()), 0); - } - } - -private: - static constexpr int kCellWidth = 64; - static constexpr int kCellHeight = 64; - static constexpr int kNumGreySteps = 16; - - typedef GM INHERITED; -}; - -////////////////////////////////////////////////////////////////////////////// - -DEF_GM(return new GammaColorFilterGM;) -} +// Temporary. When gn_to_bp.py works for DM, we can remove this. diff --git a/gn/effects.gni b/gn/effects.gni index b3459e36d9..f08f57bf72 100644 --- a/gn/effects.gni +++ b/gn/effects.gni @@ -37,7 +37,6 @@ skia_effects_sources = [ "$_src/effects/SkEmbossMask_Table.h", "$_src/effects/SkEmbossMaskFilter.cpp", "$_src/effects/SkImageSource.cpp", - "$_src/effects/SkGammaColorFilter.cpp", "$_src/effects/SkGaussianEdgeShader.cpp", "$_src/effects/SkLayerDrawLooper.cpp", "$_src/effects/SkLayerRasterizer.cpp", @@ -105,7 +104,6 @@ skia_effects_sources = [ "$_include/effects/SkDiscretePathEffect.h", "$_include/effects/SkDisplacementMapEffect.h", "$_include/effects/SkDropShadowImageFilter.h", - "$_include/effects/SkGammaColorFilter.h", "$_include/effects/SkGaussianEdgeShader.h", "$_include/effects/SkGradientShader.h", "$_include/effects/SkImageSource.h", @@ -127,7 +127,6 @@ gm_sources = [ "$_gm/fontscaler.cpp", "$_gm/fontscalerdistortable.cpp", "$_gm/gamma.cpp", - "$_gm/gammacolorfilter.cpp", "$_gm/gammatext.cpp", "$_gm/gamut.cpp", "$_gm/gaussianedge.cpp", diff --git a/gn/gpu.gni b/gn/gpu.gni index 3013270f22..e7e629d26e 100644 --- a/gn/gpu.gni +++ b/gn/gpu.gni @@ -315,8 +315,6 @@ skia_gpu_sources = [ "$_src/gpu/effects/GrDistanceFieldGeoProc.h", "$_src/gpu/effects/GrDitherEffect.cpp", "$_src/gpu/effects/GrDitherEffect.h", - "$_src/gpu/effects/GrGammaEffect.cpp", - "$_src/gpu/effects/GrGammaEffect.h", "$_src/gpu/effects/GrGaussianConvolutionFragmentProcessor.cpp", "$_src/gpu/effects/GrGaussianConvolutionFragmentProcessor.h", "$_src/gpu/effects/GrMatrixConvolutionEffect.cpp", @@ -334,6 +332,8 @@ skia_gpu_sources = [ "$_src/gpu/effects/GrSimpleTextureEffect.h", "$_src/gpu/effects/GrSingleTextureEffect.cpp", "$_src/gpu/effects/GrSingleTextureEffect.h", + "$_src/gpu/effects/GrSRGBEffect.cpp", + "$_src/gpu/effects/GrSRGBEffect.h", "$_src/gpu/effects/GrTextureDomain.cpp", "$_src/gpu/effects/GrTextureDomain.h", "$_src/gpu/effects/GrTextureStripAtlas.cpp", diff --git a/include/effects/SkGammaColorFilter.h b/include/effects/SkGammaColorFilter.h deleted file mode 100644 index 72c464e33b..0000000000 --- a/include/effects/SkGammaColorFilter.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2016 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkGammaColorFilter_DEFINED -#define SkGammaColorFilter_DEFINED - -#include "SkColorFilter.h" -#include "SkRefCnt.h" - -// This colorfilter can be used to perform pixel-by-pixel conversion between linear and -// power-law color spaces. A gamma of 2.2 is interpreted to mean convert from sRGB to linear -// while a gamma of 1/2.2 is interpreted to mean convert from linear to sRGB. Any other -// values are just directly applied (i.e., out = in^gamma) -// -// More complicated color space mapping (i.e., ICC profiles) should be handled via the -// SkColorSpace object. -class SK_API SkGammaColorFilter : public SkColorFilter { -public: - static sk_sp<SkColorFilter> Make(SkScalar gamma); - - void filterSpan(const SkPMColor src[], int count, SkPMColor[]) const override; - -#if SK_SUPPORT_GPU - sk_sp<GrFragmentProcessor> asFragmentProcessor(GrContext*, SkColorSpace*) const override; -#endif - - SK_TO_STRING_OVERRIDE() - SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLumaColorFilter) - -protected: - void flatten(SkWriteBuffer&) const override; - -private: - SkGammaColorFilter(SkScalar gamma); - - SkScalar fGamma; - typedef SkColorFilter INHERITED; -}; - -#endif diff --git a/samplecode/SampleApp.cpp b/samplecode/SampleApp.cpp index bd298dfa11..5b0b9d605d 100644 --- a/samplecode/SampleApp.cpp +++ b/samplecode/SampleApp.cpp @@ -16,7 +16,6 @@ #include "SkCommandLineFlags.h" #include "SkData.h" #include "SkDocument.h" -#include "SkGammaColorFilter.h" #include "SkGraphics.h" #include "SkOSFile.h" #include "SkOSPath.h" @@ -358,7 +357,7 @@ public: SkPaint gammaPaint; gammaPaint.setBlendMode(SkBlendMode::kSrc); if (doGamma) { - gammaPaint.setColorFilter(SkGammaColorFilter::Make(1.0f / 2.2f)); + gammaPaint.setColorFilter(sk_tool_utils::MakeLinearToSRGBColorFilter()); } gpuCanvas->drawImage(offscreenImage, 0, 0, &gammaPaint); diff --git a/src/effects/SkGammaColorFilter.cpp b/src/effects/SkGammaColorFilter.cpp deleted file mode 100644 index 181ab770f4..0000000000 --- a/src/effects/SkGammaColorFilter.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2016 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkGammaColorFilter.h" - -#include "SkReadBuffer.h" -#include "SkString.h" - -#if SK_SUPPORT_GPU -#include "effects/GrGammaEffect.h" -#endif - -void SkGammaColorFilter::filterSpan(const SkPMColor src[], int count, - SkPMColor dst[]) const { - // Gamma-correcting bytes to bytes is pretty questionable. - SkASSERT(0); - for (int i = 0; i < count; ++i) { - SkPMColor c = src[i]; - - // TODO: implement cpu gamma correction? - dst[i] = c; - } -} - -sk_sp<SkColorFilter> SkGammaColorFilter::Make(SkScalar gamma) { - return sk_sp<SkColorFilter>(new SkGammaColorFilter(gamma)); -} - -SkGammaColorFilter::SkGammaColorFilter(SkScalar gamma) : fGamma(gamma) {} - -sk_sp<SkFlattenable> SkGammaColorFilter::CreateProc(SkReadBuffer& buffer) { - SkScalar gamma = buffer.readScalar(); - - return Make(gamma); -} - -void SkGammaColorFilter::flatten(SkWriteBuffer& buffer) const { - this->INHERITED::flatten(buffer); - buffer.writeScalar(fGamma); -} - -#ifndef SK_IGNORE_TO_STRING -void SkGammaColorFilter::toString(SkString* str) const { - str->appendf("SkGammaColorFilter (%.2f)", fGamma); -} -#endif - -#if SK_SUPPORT_GPU -sk_sp<GrFragmentProcessor> SkGammaColorFilter::asFragmentProcessor(GrContext*, - SkColorSpace*) const { - return GrGammaEffect::Make(fGamma); -} -#endif diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp index 1733e2853f..db8863fca0 100644 --- a/src/gpu/GrContext.cpp +++ b/src/gpu/GrContext.cpp @@ -23,8 +23,6 @@ #include "SkGrPriv.h" #include "effects/GrConfigConversionEffect.h" -#include "effects/GrGammaEffect.h" -#include "ops/GrCopySurfaceOp.h" #include "text/GrTextBlobCache.h" #define ASSERT_OWNED_RESOURCE(R) SkASSERT(!(R) || (R)->getContext() == this) diff --git a/src/gpu/GrYUVProvider.cpp b/src/gpu/GrYUVProvider.cpp index f3231a49da..ade72a6cce 100644 --- a/src/gpu/GrYUVProvider.cpp +++ b/src/gpu/GrYUVProvider.cpp @@ -10,7 +10,7 @@ #include "GrRenderTargetContext.h" #include "GrTextureProxy.h" #include "GrYUVProvider.h" -#include "effects/GrGammaEffect.h" +#include "effects/GrSRGBEffect.h" #include "effects/GrYUVEffect.h" #include "SkAutoMalloc.h" @@ -149,7 +149,7 @@ sk_sp<GrTexture> GrYUVProvider::refAsTexture(GrContext* ctx, if (ctx->caps()->srgbWriteControl()) { paint.setDisableOutputConversionToSRGB(true); } else { - paint.addColorFragmentProcessor(GrGammaEffect::Make(2.2f)); + paint.addColorFragmentProcessor(GrSRGBEffect::Make(GrSRGBEffect::Mode::kSRGBToLinear)); } } diff --git a/src/gpu/effects/GrGammaEffect.cpp b/src/gpu/effects/GrGammaEffect.cpp deleted file mode 100644 index 8b9363e843..0000000000 --- a/src/gpu/effects/GrGammaEffect.cpp +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2016 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "GrGammaEffect.h" - -#include "GrContext.h" -#include "GrCoordTransform.h" -#include "GrFragmentProcessor.h" -#include "GrInvariantOutput.h" -#include "GrProcessor.h" -#include "glsl/GrGLSLFragmentProcessor.h" -#include "glsl/GrGLSLFragmentShaderBuilder.h" - -class GrGLGammaEffect : public GrGLSLFragmentProcessor { -public: - void emitCode(EmitArgs& args) override { - const GrGammaEffect& ge = args.fFp.cast<GrGammaEffect>(); - GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; - GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; - - const char* gammaUniName = nullptr; - if (GrGammaEffect::Mode::kExponential == ge.mode()) { - fGammaUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat_GrSLType, - kDefault_GrSLPrecision, "Gamma", &gammaUniName); - } - - SkString srgbFuncName; - static const GrShaderVar gSrgbArgs[] = { - GrShaderVar("x", kFloat_GrSLType), - }; - switch (ge.mode()) { - case GrGammaEffect::Mode::kLinearToSRGB: - fragBuilder->emitFunction(kFloat_GrSLType, - "linear_to_srgb", - SK_ARRAY_COUNT(gSrgbArgs), - gSrgbArgs, - "return (x <= 0.0031308) ? (x * 12.92) " - ": (1.055 * pow(x, 0.416666667) - 0.055);", - &srgbFuncName); - break; - case GrGammaEffect::Mode::kSRGBToLinear: - fragBuilder->emitFunction(kFloat_GrSLType, - "srgb_to_linear", - SK_ARRAY_COUNT(gSrgbArgs), - gSrgbArgs, - "return (x <= 0.04045) ? (x / 12.92) " - ": pow((x + 0.055) / 1.055, 2.4);", - &srgbFuncName); - default: - // No helper function needed - break; - } - - if (nullptr == args.fInputColor) { - args.fInputColor = "vec4(1)"; - } - - if (GrGammaEffect::Mode::kExponential == ge.mode()) { - fragBuilder->codeAppendf("%s = vec4(pow(%s.rgb, vec3(%s)), %s.a);", - args.fOutputColor, args.fInputColor, gammaUniName, - args.fInputColor); - } else { - fragBuilder->codeAppendf("%s = vec4(%s(%s.r), %s(%s.g), %s(%s.b), %s.a);", - args.fOutputColor, - srgbFuncName.c_str(), args.fInputColor, - srgbFuncName.c_str(), args.fInputColor, - srgbFuncName.c_str(), args.fInputColor, - args.fInputColor); - } - } - - void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) override { - const GrGammaEffect& ge = proc.cast<GrGammaEffect>(); - if (GrGammaEffect::Mode::kExponential == ge.mode()) { - pdman.set1f(fGammaUni, ge.gamma()); - } - } - - static inline void GenKey(const GrProcessor& processor, const GrShaderCaps&, - GrProcessorKeyBuilder* b) { - const GrGammaEffect& ge = processor.cast<GrGammaEffect>(); - uint32_t key = static_cast<uint32_t>(ge.mode()); - b->add32(key); - } - -private: - GrGLSLProgramDataManager::UniformHandle fGammaUni; - - typedef GrGLSLFragmentProcessor INHERITED; -}; - -/////////////////////////////////////////////////////////////////////////////// - -GrGammaEffect::GrGammaEffect(Mode mode, SkScalar gamma) - : fMode(mode) - , fGamma(gamma) { - this->initClassID<GrGammaEffect>(); -} - -bool GrGammaEffect::onIsEqual(const GrFragmentProcessor& s) const { - const GrGammaEffect& other = s.cast<GrGammaEffect>(); - return - other.fMode == fMode && - (fMode != Mode::kExponential || other.fGamma == fGamma); -} - -void GrGammaEffect::onComputeInvariantOutput(GrInvariantOutput* inout) const { - inout->setToUnknown(); -} - -/////////////////////////////////////////////////////////////////////////////// - -GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrGammaEffect); - -sk_sp<GrFragmentProcessor> GrGammaEffect::TestCreate(GrProcessorTestData* d) { - // We want to be sure and test sRGB sometimes - Mode testMode = static_cast<Mode>(d->fRandom->nextRangeU(0, 2)); - SkScalar gamma = d->fRandom->nextRangeScalar(0.5f, 2.0f); - return sk_sp<GrFragmentProcessor>(new GrGammaEffect(testMode, gamma)); -} - -/////////////////////////////////////////////////////////////////////////////// - -void GrGammaEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps, - GrProcessorKeyBuilder* b) const { - GrGLGammaEffect::GenKey(*this, caps, b); -} - -GrGLSLFragmentProcessor* GrGammaEffect::onCreateGLSLInstance() const { - return new GrGLGammaEffect(); -} - -sk_sp<GrFragmentProcessor> GrGammaEffect::Make(SkScalar gamma) { - // TODO: Once our public-facing API for specifying gamma curves settles down, expose this, - // and allow clients to explicitly request sRGB, rather than inferring from the exponent. - // Note that AdobeRGB (for example) is speficied as x^2.2, not the Rec.709 curves. - if (SkScalarNearlyEqual(gamma, 2.2f)) { - return sk_sp<GrFragmentProcessor>(new GrGammaEffect(Mode::kSRGBToLinear, 2.2f)); - } else if (SkScalarNearlyEqual(gamma, 1.0f / 2.2f)) { - return sk_sp<GrFragmentProcessor>(new GrGammaEffect(Mode::kLinearToSRGB, 1.0f / 2.2f)); - } else { - return sk_sp<GrFragmentProcessor>(new GrGammaEffect(Mode::kExponential, gamma)); - } -} diff --git a/src/gpu/effects/GrSRGBEffect.cpp b/src/gpu/effects/GrSRGBEffect.cpp new file mode 100644 index 0000000000..67600ef320 --- /dev/null +++ b/src/gpu/effects/GrSRGBEffect.cpp @@ -0,0 +1,109 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrSRGBEffect.h" + +#include "GrContext.h" +#include "GrFragmentProcessor.h" +#include "GrInvariantOutput.h" +#include "GrProcessor.h" +#include "glsl/GrGLSLFragmentProcessor.h" +#include "glsl/GrGLSLFragmentShaderBuilder.h" + +class GrGLSRGBEffect : public GrGLSLFragmentProcessor { +public: + void emitCode(EmitArgs& args) override { + const GrSRGBEffect& srgbe = args.fFp.cast<GrSRGBEffect>(); + GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; + + SkString srgbFuncName; + static const GrShaderVar gSrgbArgs[] = { + GrShaderVar("x", kFloat_GrSLType), + }; + switch (srgbe.mode()) { + case GrSRGBEffect::Mode::kLinearToSRGB: + fragBuilder->emitFunction(kFloat_GrSLType, + "linear_to_srgb", + SK_ARRAY_COUNT(gSrgbArgs), + gSrgbArgs, + "return (x <= 0.0031308) ? (x * 12.92) " + ": (1.055 * pow(x, 0.416666667) - 0.055);", + &srgbFuncName); + break; + case GrSRGBEffect::Mode::kSRGBToLinear: + fragBuilder->emitFunction(kFloat_GrSLType, + "srgb_to_linear", + SK_ARRAY_COUNT(gSrgbArgs), + gSrgbArgs, + "return (x <= 0.04045) ? (x / 12.92) " + ": pow((x + 0.055) / 1.055, 2.4);", + &srgbFuncName); + break; + } + + if (nullptr == args.fInputColor) { + args.fInputColor = "vec4(1)"; + } + + fragBuilder->codeAppendf("%s = vec4(%s(%s.r), %s(%s.g), %s(%s.b), %s.a);", + args.fOutputColor, + srgbFuncName.c_str(), args.fInputColor, + srgbFuncName.c_str(), args.fInputColor, + srgbFuncName.c_str(), args.fInputColor, + args.fInputColor); + } + + static inline void GenKey(const GrProcessor& processor, const GrShaderCaps&, + GrProcessorKeyBuilder* b) { + const GrSRGBEffect& srgbe = processor.cast<GrSRGBEffect>(); + uint32_t key = static_cast<uint32_t>(srgbe.mode()); + b->add32(key); + } + +private: + typedef GrGLSLFragmentProcessor INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// + +GrSRGBEffect::GrSRGBEffect(Mode mode) + : fMode(mode) { + this->initClassID<GrSRGBEffect>(); +} + +bool GrSRGBEffect::onIsEqual(const GrFragmentProcessor& s) const { + const GrSRGBEffect& other = s.cast<GrSRGBEffect>(); + return other.fMode == fMode; +} + +void GrSRGBEffect::onComputeInvariantOutput(GrInvariantOutput* inout) const { + inout->setToUnknown(); +} + +/////////////////////////////////////////////////////////////////////////////// + +GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrSRGBEffect); + +sk_sp<GrFragmentProcessor> GrSRGBEffect::TestCreate(GrProcessorTestData* d) { + Mode testMode = static_cast<Mode>(d->fRandom->nextRangeU(0, 1)); + return sk_sp<GrFragmentProcessor>(new GrSRGBEffect(testMode)); +} + +/////////////////////////////////////////////////////////////////////////////// + +void GrSRGBEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps, + GrProcessorKeyBuilder* b) const { + GrGLSRGBEffect::GenKey(*this, caps, b); +} + +GrGLSLFragmentProcessor* GrSRGBEffect::onCreateGLSLInstance() const { + return new GrGLSRGBEffect(); +} + +sk_sp<GrFragmentProcessor> GrSRGBEffect::Make(Mode mode) { + return sk_sp<GrFragmentProcessor>(new GrSRGBEffect(mode)); +} diff --git a/src/gpu/effects/GrGammaEffect.h b/src/gpu/effects/GrSRGBEffect.h index a3e4b0e5e3..2952689c4e 100644 --- a/src/gpu/effects/GrGammaEffect.h +++ b/src/gpu/effects/GrSRGBEffect.h @@ -5,31 +5,29 @@ * found in the LICENSE file. */ -#ifndef GrGammaEffect_DEFINED -#define GrGammaEffect_DEFINED +#ifndef GrSRGBEffect_DEFINED +#define GrSRGBEffect_DEFINED #include "GrFragmentProcessor.h" -class GrGammaEffect : public GrFragmentProcessor { +class GrSRGBEffect : public GrFragmentProcessor { public: enum class Mode { kLinearToSRGB, kSRGBToLinear, - kExponential, }; /** - * Creates an effect that applies a gamma curve. - */ - static sk_sp<GrFragmentProcessor> Make(SkScalar gamma); + * Creates an effect that applies the sRGB transfer function (or its inverse) + */ + static sk_sp<GrFragmentProcessor> Make(Mode mode); - const char* name() const override { return "Gamma"; } + const char* name() const override { return "sRGB"; } Mode mode() const { return fMode; } - SkScalar gamma() const { return fGamma; } private: - GrGammaEffect(Mode mode, SkScalar gamma); + GrSRGBEffect(Mode mode); GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override; @@ -37,7 +35,6 @@ private: void onComputeInvariantOutput(GrInvariantOutput* inout) const override; Mode fMode; - SkScalar fGamma; GR_DECLARE_FRAGMENT_PROCESSOR_TEST; diff --git a/tests/ApplyGammaTest.cpp b/tests/ApplyGammaTest.cpp index 4f1f828966..dbb7210c1a 100644 --- a/tests/ApplyGammaTest.cpp +++ b/tests/ApplyGammaTest.cpp @@ -12,60 +12,63 @@ #include "GrContext.h" #include "SkCanvas.h" -#include "SkGammaColorFilter.h" +#include "SkColorFilter.h" #include "SkSurface.h" #include "SkUtils.h" +#include "sk_tool_utils.h" - // using anonymous namespace because these functions are used as template params. -namespace { /** convert 0..1 linear value to 0..1 srgb */ -float linear_to_srgb(float linear) { +static float linear_to_srgb(float linear) { if (linear <= 0.0031308) { return linear * 12.92f; } else { return 1.055f * powf(linear, 1.f / 2.4f) - 0.055f; } } -} -bool check_gamma(uint32_t src, uint32_t dst, float gamma, float error, uint32_t* expected) { - if (SkScalarNearlyEqual(gamma, 1.f)) { - *expected = src; - return src == dst; +/** convert 0..1 srgb value to 0..1 linear */ +static float srgb_to_linear(float srgb) { + if (srgb <= 0.04045f) { + return srgb / 12.92f; } else { - bool result = true; - uint32_t expectedColor = src & 0xff000000; + return powf((srgb + 0.055f) / 1.055f, 2.4f); + } +} - // Alpha should always be exactly preserved. - if ((src & 0xff000000) != (dst & 0xff000000)) { - result = false; - } +bool check_gamma(uint32_t src, uint32_t dst, bool toSRGB, float error, + uint32_t* expected) { + bool result = true; + uint32_t expectedColor = src & 0xff000000; - for (int c = 0; c < 3; ++c) { - uint8_t srcComponent = (src & (0xff << (c * 8))) >> (c * 8); - float lower = SkTMax(0.f, (float)srcComponent - error); - float upper = SkTMin(255.f, (float)srcComponent + error); - if (SkScalarNearlyEqual(gamma, 1.0f / 2.2f)) { - lower = linear_to_srgb(lower / 255.f); - upper = linear_to_srgb(upper / 255.f); - } else { - lower = powf(lower / 255.f, gamma); - upper = powf(upper / 255.f, gamma); - } - SkASSERT(lower >= 0.f && lower <= 255.f); - SkASSERT(upper >= 0.f && upper <= 255.f); - uint8_t dstComponent = (dst & (0xff << (c * 8))) >> (c * 8); - if (dstComponent < SkScalarFloorToInt(lower * 255.f) || - dstComponent > SkScalarCeilToInt(upper * 255.f)) { - result = false; - } - uint8_t expectedComponent = SkScalarRoundToInt((lower + upper) * 127.5f); - expectedColor |= expectedComponent << (c * 8); - } + // Alpha should always be exactly preserved. + if ((src & 0xff000000) != (dst & 0xff000000)) { + result = false; + } - *expected = expectedColor; - return result; + for (int c = 0; c < 3; ++c) { + uint8_t srcComponent = (src & (0xff << (c * 8))) >> (c * 8); + float lower = SkTMax(0.f, (float)srcComponent - error); + float upper = SkTMin(255.f, (float)srcComponent + error); + if (toSRGB) { + lower = linear_to_srgb(lower / 255.f); + upper = linear_to_srgb(upper / 255.f); + } else { + lower = srgb_to_linear(lower / 255.f); + upper = srgb_to_linear(upper / 255.f); + } + SkASSERT(lower >= 0.f && lower <= 255.f); + SkASSERT(upper >= 0.f && upper <= 255.f); + uint8_t dstComponent = (dst & (0xff << (c * 8))) >> (c * 8); + if (dstComponent < SkScalarFloorToInt(lower * 255.f) || + dstComponent > SkScalarCeilToInt(upper * 255.f)) { + result = false; + } + uint8_t expectedComponent = SkScalarRoundToInt((lower + upper) * 127.5f); + expectedColor |= expectedComponent << (c * 8); } + + *expected = expectedColor; + return result; } DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ApplyGamma, reporter, ctxInfo) { @@ -94,48 +97,46 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ApplyGamma, reporter, ctxInfo) { // We allow more error on GPUs with lower precision shader variables. float error = context->caps()->shaderCaps()->floatPrecisionVaries() ? 1.2f : 0.5f; - for (auto dOrigin : { kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin }) { - for (auto gamma : { 1.0f, 1.0f / 1.8f, 1.0f / 2.2f }) { - sk_sp<SkSurface> dst(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, - ii, 0, dOrigin, nullptr)); + for (auto toSRGB : { false, true }) { + sk_sp<SkSurface> dst(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, ii)); - if (!dst) { - ERRORF(reporter, "Could not create surfaces for copy surface test."); - continue; - } + if (!dst) { + ERRORF(reporter, "Could not create surfaces for copy surface test."); + continue; + } - SkCanvas* dstCanvas = dst->getCanvas(); + SkCanvas* dstCanvas = dst->getCanvas(); - dstCanvas->clear(SK_ColorRED); - dstCanvas->flush(); + dstCanvas->clear(SK_ColorRED); + dstCanvas->flush(); - SkPaint gammaPaint; - gammaPaint.setBlendMode(SkBlendMode::kSrc); - gammaPaint.setColorFilter(SkGammaColorFilter::Make(gamma)); + SkPaint gammaPaint; + gammaPaint.setBlendMode(SkBlendMode::kSrc); + gammaPaint.setColorFilter(toSRGB ? sk_tool_utils::MakeLinearToSRGBColorFilter() + : sk_tool_utils::MakeSRGBToLinearColorFilter()); - dstCanvas->drawBitmap(bm, 0, 0, &gammaPaint); - dstCanvas->flush(); + dstCanvas->drawBitmap(bm, 0, 0, &gammaPaint); + dstCanvas->flush(); - sk_memset32(read.get(), 0, kW * kH); - if (!dstCanvas->readPixels(ii, read.get(), kRowBytes, 0, 0)) { - ERRORF(reporter, "Error calling readPixels"); - continue; - } + sk_memset32(read.get(), 0, kW * kH); + if (!dstCanvas->readPixels(ii, read.get(), kRowBytes, 0, 0)) { + ERRORF(reporter, "Error calling readPixels"); + continue; + } - bool abort = false; - // Validate that pixels were copied/transformed correctly. - for (int y = 0; y < kH && !abort; ++y) { - for (int x = 0; x < kW && !abort; ++x) { - uint32_t r = read.get()[y * kW + x]; - uint32_t s = srcPixels.get()[y * kW + x]; - uint32_t expected; - if (!check_gamma(s, r, gamma, error, &expected)) { - ERRORF(reporter, "Expected dst %d,%d to contain 0x%08x " - "from src 0x%08x and gamma %f. Got %08x", - x, y, expected, s, gamma, r); - abort = true; - break; - } + bool abort = false; + // Validate that pixels were copied/transformed correctly. + for (int y = 0; y < kH && !abort; ++y) { + for (int x = 0; x < kW && !abort; ++x) { + uint32_t r = read.get()[y * kW + x]; + uint32_t s = srcPixels.get()[y * kW + x]; + uint32_t expected; + if (!check_gamma(s, r, toSRGB, error, &expected)) { + ERRORF(reporter, "Expected dst %d,%d to contain 0x%08x " + "from src 0x%08x and mode %s. Got %08x", x, y, expected, s, + toSRGB ? "ToSRGB" : "ToLinear", r); + abort = true; + break; } } } diff --git a/tools/sk_tool_utils.cpp b/tools/sk_tool_utils.cpp index 93f5b107f2..0383897bd8 100644 --- a/tools/sk_tool_utils.cpp +++ b/tools/sk_tool_utils.cpp @@ -22,6 +22,38 @@ DEFINE_bool(portableFonts, false, "Use portable fonts"); +#if SK_SUPPORT_GPU +#include "effects/GrSRGBEffect.h" +#include "SkColorFilter.h" + +// Color filter that just wraps GrSRGBEffect +class SkSRGBColorFilter : public SkColorFilter { +public: + static sk_sp<SkColorFilter> Make(GrSRGBEffect::Mode mode) { + return sk_sp<SkColorFilter>(new SkSRGBColorFilter(mode)); + } + + sk_sp<GrFragmentProcessor> asFragmentProcessor(GrContext*, SkColorSpace*) const override { + return GrSRGBEffect::Make(fMode); + } + + void filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const override { + SK_ABORT("SkSRGBColorFilter is only implemented for GPU"); + } + Factory getFactory() const override { return nullptr; } + +#ifndef SK_IGNORE_TO_STRING + void toString(SkString* str) const override {} +#endif + +private: + SkSRGBColorFilter(GrSRGBEffect::Mode mode) : fMode(mode) {} + + GrSRGBEffect::Mode fMode; + typedef SkColorFilter INHERITED; +}; +#endif + namespace sk_tool_utils { /* these are the default fonts chosen by Chrome for serif, sans-serif, and monospace */ @@ -586,4 +618,14 @@ void copy_to_g8(SkBitmap* dst, const SkBitmap& src) { } } +#if SK_SUPPORT_GPU +sk_sp<SkColorFilter> MakeLinearToSRGBColorFilter() { + return SkSRGBColorFilter::Make(GrSRGBEffect::Mode::kLinearToSRGB); +} + +sk_sp<SkColorFilter> MakeSRGBToLinearColorFilter() { + return SkSRGBColorFilter::Make(GrSRGBEffect::Mode::kSRGBToLinear); +} +#endif + } // namespace sk_tool_utils diff --git a/tools/sk_tool_utils.h b/tools/sk_tool_utils.h index ffe8084f7f..e319411349 100644 --- a/tools/sk_tool_utils.h +++ b/tools/sk_tool_utils.h @@ -19,6 +19,7 @@ class SkBitmap; class SkCanvas; +class SkColorFilter; class SkPaint; class SkPath; class SkRRect; @@ -254,6 +255,11 @@ namespace sk_tool_utils { void copy_to_g8(SkBitmap* dst, const SkBitmap& src); +#if SK_SUPPORT_GPU + sk_sp<SkColorFilter> MakeLinearToSRGBColorFilter(); + sk_sp<SkColorFilter> MakeSRGBToLinearColorFilter(); +#endif + } // namespace sk_tool_utils #endif // sk_tool_utils_DEFINED diff --git a/tools/viewer/sk_app/mac/RasterWindowContext_mac.cpp b/tools/viewer/sk_app/mac/RasterWindowContext_mac.cpp index fd2f2effb1..4749bc79a2 100644 --- a/tools/viewer/sk_app/mac/RasterWindowContext_mac.cpp +++ b/tools/viewer/sk_app/mac/RasterWindowContext_mac.cpp @@ -8,7 +8,8 @@ #include "../GLWindowContext.h" #include "SkCanvas.h" -#include "SkGammaColorFilter.h" +#include "SkColorFilter.h" +#include "sk_tool_utils.h" #include "WindowContextFactory_mac.h" #include "SDL.h" @@ -123,7 +124,7 @@ void RasterWindowContext_mac::onSwapBuffers() { SkPaint gammaPaint; gammaPaint.setBlendMode(SkBlendMode::kSrc); if (doGamma) { - gammaPaint.setColorFilter(SkGammaColorFilter::Make(1.0f / 2.2f)); + gammaPaint.setColorFilter(sk_tool_utils::MakeLinearToSRGBColorFilter()); } sk_sp<SkSurface> gpuSurface = INHERITED::getBackbufferSurface(); |