diff options
author | commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2013-10-07 18:00:17 +0000 |
---|---|---|
committer | commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2013-10-07 18:00:17 +0000 |
commit | 6c1ee2d4e727357451c8a6fcf4a08e75890b5d6d (patch) | |
tree | 144de7ce27c8400a39866d0f53f7be49aa935cda | |
parent | dd6cde5235d5d36607f4f1df66057d807b432b99 (diff) |
Luminance-to-alpha color filter (SkLumaColorFilter).
Adding a color filter luma implementation. The plan is to convert
existing clients and then deprecate SkLumaXfermode.
R=bsalomon@google.com, reed@google.com, robertphillips@google.com
Author: fmalita@chromium.org
Review URL: https://codereview.chromium.org/25453004
git-svn-id: http://skia.googlecode.com/svn/trunk@11636 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r-- | bench/ColorFilterBench.cpp | 30 | ||||
-rw-r--r-- | gm/lumafilter.cpp | 152 | ||||
-rw-r--r-- | gyp/effects.gypi | 2 | ||||
-rw-r--r-- | gyp/gmslides.gypi | 1 | ||||
-rw-r--r-- | gyp/public_headers.gypi | 1 | ||||
-rw-r--r-- | include/effects/SkLumaColorFilter.h | 46 | ||||
-rw-r--r-- | src/effects/SkLumaColorFilter.cpp | 152 | ||||
-rw-r--r-- | src/ports/SkGlobalInitialization_chromium.cpp | 2 | ||||
-rw-r--r-- | src/ports/SkGlobalInitialization_default.cpp | 2 | ||||
-rw-r--r-- | tests/ColorFilterTest.cpp | 35 |
10 files changed, 423 insertions, 0 deletions
diff --git a/bench/ColorFilterBench.cpp b/bench/ColorFilterBench.cpp index e30b005964..6d98a0eabc 100644 --- a/bench/ColorFilterBench.cpp +++ b/bench/ColorFilterBench.cpp @@ -8,6 +8,7 @@ #include "SkCanvas.h" #include "SkColorFilterImageFilter.h" #include "SkColorMatrixFilter.h" +#include "SkLumaColorFilter.h" #include "SkTableColorFilter.h" #define FILTER_WIDTH_SMALL SkIntToScalar(32) @@ -314,6 +315,33 @@ private: typedef ColorFilterBaseBench INHERITED; }; +class LumaColorFilterBench : public ColorFilterBaseBench { + +public: + LumaColorFilterBench(bool small) : INHERITED(small) { + } + +protected: + virtual const char* onGetName() SK_OVERRIDE { + return isSmall() ? "luma_colorfilter_small" : "luma_colorfilter_large"; + } + + virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE { + SkRect r = getFilterRect(); + SkPaint paint; + paint.setColor(SK_ColorRED); + + for (int i = 0; i < this->getLoops(); i++) { + SkAutoTUnref<SkColorFilter> luma_filter(SkLumaColorFilter::Create()); + paint.setColorFilter(luma_filter); + canvas->drawRect(r, paint); + } + } + +private: + typedef ColorFilterBaseBench INHERITED; +}; + /////////////////////////////////////////////////////////////////////////////// DEF_BENCH( return new ColorFilterDimBrightBench(true); ) @@ -325,6 +353,7 @@ DEF_BENCH( return new ColorFilterBrightBench(true); ) DEF_BENCH( return new ColorFilterBlueBench(true); ) DEF_BENCH( return new ColorFilterGrayBench(true); ) DEF_BENCH( return new TableColorFilterBench(true); ) +DEF_BENCH( return new LumaColorFilterBench(true); ) DEF_BENCH( return new ColorFilterDimBrightBench(false); ) DEF_BENCH( return new ColorFilterBrightGrayBench(false); ) @@ -335,3 +364,4 @@ DEF_BENCH( return new ColorFilterBrightBench(false); ) DEF_BENCH( return new ColorFilterBlueBench(false); ) DEF_BENCH( return new ColorFilterGrayBench(false); ) DEF_BENCH( return new TableColorFilterBench(false); ) +DEF_BENCH( return new LumaColorFilterBench(false); ) diff --git a/gm/lumafilter.cpp b/gm/lumafilter.cpp new file mode 100644 index 0000000000..69a4f073f0 --- /dev/null +++ b/gm/lumafilter.cpp @@ -0,0 +1,152 @@ +/* + * Copyright 2013 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 "SkCanvas.h" +#include "SkGradientShader.h" +#include "SkLumaColorFilter.h" + +static SkScalar kSize = 80; +static SkScalar kInset = 10; +static SkColor kColor1 = SkColorSetARGB(0xff, 0xff, 0xff, 0); +static SkColor kColor2 = SkColorSetARGB(0xff, 0x80, 0xff, 0); + +static void draw_label(SkCanvas* canvas, const char* label, + const SkPoint& offset) { + SkPaint paint; + size_t len = strlen(label); + + SkScalar width = paint.measureText(label, len); + canvas->drawText(label, len, offset.x() - width / 2, offset.y(), + paint); +} + +static void draw_scene(SkCanvas* canvas, SkColorFilter* filter, + SkXfermode::Mode mode, SkShader* s1, + SkShader* s2) { + SkPaint paint; + paint.setAntiAlias(true); + SkRect r, c, bounds = SkRect::MakeWH(kSize, kSize); + + c = bounds; + c.fRight = bounds.centerX(); + canvas->drawRect(bounds, paint); + + canvas->saveLayer(&bounds, NULL); + + r = bounds; + r.inset(kInset, 0); + paint.setShader(s1); + paint.setColor(s1 ? SK_ColorBLACK : SkColorSetA(kColor1, 0x80)); + canvas->drawOval(r, paint); + if (!s1) { + canvas->save(); + canvas->clipRect(c); + paint.setColor(kColor1); + canvas->drawOval(r, paint); + canvas->restore(); + } + + SkPaint xferPaint; + xferPaint.setXfermodeMode(mode); + canvas->saveLayer(&bounds, &xferPaint); + + r = bounds; + r.inset(0, kInset); + paint.setShader(s2); + paint.setColor(s2 ? SK_ColorBLACK : SkColorSetA(kColor2, 0x80)); + paint.setColorFilter(filter); + canvas->drawOval(r, paint); + if (!s2) { + canvas->save(); + canvas->clipRect(c); + paint.setColor(kColor2); + canvas->drawOval(r, paint); + canvas->restore(); + } + + canvas->restore(); + canvas->restore(); +} + +class LumaFilterGM : public skiagm::GM { +public: + LumaFilterGM() { + SkColor g1Colors[] = { kColor1, SkColorSetA(kColor1, 0x20) }; + SkColor g2Colors[] = { kColor2, SkColorSetA(kColor2, 0x20) }; + SkPoint g1Points[] = { { 0, 0 }, { 0, 100 } }; + SkPoint g2Points[] = { { 0, 0 }, { kSize, 0 } }; + SkScalar pos[] = { 0.2f, 1.0f }; + + fFilter.reset(SkLumaColorFilter::Create()); + fGr1.reset(SkGradientShader::CreateLinear(g1Points, + g1Colors, + pos, + SK_ARRAY_COUNT(g1Colors), + SkShader::kClamp_TileMode)); + fGr2.reset(SkGradientShader::CreateLinear(g2Points, + g2Colors, + pos, + SK_ARRAY_COUNT(g2Colors), + SkShader::kClamp_TileMode)); + } + +protected: + virtual SkString onShortName() SK_OVERRIDE { + return SkString("lumafilter"); + } + + virtual SkISize onISize() SK_OVERRIDE { + return SkISize::Make(600, 420); + } + + virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE { + SkXfermode::Mode modes[] = { SkXfermode::kSrcOver_Mode, + SkXfermode::kDstOver_Mode, + SkXfermode::kSrcATop_Mode, + SkXfermode::kDstATop_Mode, + SkXfermode::kSrcIn_Mode, + SkXfermode::kDstIn_Mode, + }; + struct { + SkShader* fShader1; + SkShader* fShader2; + } shaders[] = { + { NULL, NULL }, + { NULL, fGr2 }, + { fGr1, NULL }, + { fGr1, fGr2 }, + }; + + SkScalar gridStep = kSize + 2 * kInset; + for (size_t i = 0; i < SK_ARRAY_COUNT(modes); ++i) { + draw_label(canvas, SkXfermode::ModeName(modes[i]), + SkPoint::Make(gridStep * (0.5f + i), 20)); + } + + for (size_t i = 0; i < SK_ARRAY_COUNT(shaders); ++i) { + canvas->save(); + canvas->translate(kInset, gridStep * i + 30); + for (size_t m = 0; m < SK_ARRAY_COUNT(modes); ++m) { + draw_scene(canvas, fFilter, modes[m], shaders[i].fShader1, + shaders[i].fShader2); + canvas->translate(gridStep, 0); + } + canvas->restore(); + } + } + +private: + SkAutoTUnref<SkColorFilter> fFilter; + SkAutoTUnref<SkShader> fGr1, fGr2; + + typedef skiagm::GM INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +DEF_GM( return SkNEW(LumaFilterGM); ) diff --git a/gyp/effects.gypi b/gyp/effects.gypi index 19e55e89ed..b640a36ff8 100644 --- a/gyp/effects.gypi +++ b/gyp/effects.gypi @@ -40,6 +40,7 @@ '<(skia_src_path)/effects/SkLayerRasterizer.cpp', '<(skia_src_path)/effects/SkLerpXfermode.cpp', '<(skia_src_path)/effects/SkLightingImageFilter.cpp', + '<(skia_src_path)/effects/SkLumaColorFilter.cpp', '<(skia_src_path)/effects/SkLumaXfermode.cpp', '<(skia_src_path)/effects/SkMagnifierImageFilter.cpp', '<(skia_src_path)/effects/SkMatrixConvolutionImageFilter.cpp', @@ -103,6 +104,7 @@ '<(skia_include_path)/effects/SkLayerRasterizer.h', '<(skia_include_path)/effects/SkLerpXfermode.h', '<(skia_include_path)/effects/SkLightingImageFilter.h', + '<(skia_include_path)/effects/SkLumaColorFilter.h', '<(skia_include_path)/effects/SkLumaXfermode.h', '<(skia_include_path)/effects/SkOffsetImageFilter.h', '<(skia_include_path)/effects/SkMorphologyImageFilter.h', diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi index 5e2dfc17e1..9307388850 100644 --- a/gyp/gmslides.gypi +++ b/gyp/gmslides.gypi @@ -72,6 +72,7 @@ '../gm/inversepaths.cpp', '../gm/lerpmode.cpp', '../gm/lighting.cpp', + '../gm/lumafilter.cpp', '../gm/lumamode.cpp', '../gm/image.cpp', '../gm/imagefiltersbase.cpp', diff --git a/gyp/public_headers.gypi b/gyp/public_headers.gypi index 569ecc7dee..f84d21b40d 100644 --- a/gyp/public_headers.gypi +++ b/gyp/public_headers.gypi @@ -76,6 +76,7 @@ 'effects/SkMergeImageFilter.h', 'effects/SkPerlinNoiseShader.h', 'effects/SkLerpXfermode.h', + 'effects/SkLumaColorFilter.h', 'effects/SkLumaXfermode.h', 'effects/SkRectShaderImageFilter.h', 'effects/SkMagnifierImageFilter.h', diff --git a/include/effects/SkLumaColorFilter.h b/include/effects/SkLumaColorFilter.h new file mode 100644 index 0000000000..fef06ab12a --- /dev/null +++ b/include/effects/SkLumaColorFilter.h @@ -0,0 +1,46 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkLumaColorFilter_DEFINED +#define SkLumaColorFilter_DEFINED + +#include "SkColorFilter.h" + +/** + * Luminance-to-alpha color filter, as defined in + * http://www.w3.org/TR/SVG/masking.html#Masking + * http://www.w3.org/TR/css-masking/#MaskValues + * + * Each color is scaled by the (unpremultiplied) luminance value: + * + * C' = [Lum * a, Lum * r, Lum * g, Lum * b] + * + */ +class SK_API SkLumaColorFilter : public SkColorFilter { +public: + static SkColorFilter* Create(); + + virtual void filterSpan(const SkPMColor src[], int count, SkPMColor[]) const SK_OVERRIDE; + +#if SK_SUPPORT_GPU + virtual GrEffectRef* asNewEffect(GrContext*) const SK_OVERRIDE; +#endif + + SkDEVCODE(virtual void toString(SkString* str) const SK_OVERRIDE;) + SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLumaColorFilter) + +protected: + SkLumaColorFilter(SkFlattenableReadBuffer& buffer); + virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE; + +private: + SkLumaColorFilter(); + + typedef SkColorFilter INHERITED; +}; + +#endif diff --git a/src/effects/SkLumaColorFilter.cpp b/src/effects/SkLumaColorFilter.cpp new file mode 100644 index 0000000000..c9f1fb02c3 --- /dev/null +++ b/src/effects/SkLumaColorFilter.cpp @@ -0,0 +1,152 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkLumaColorFilter.h" + +#include "SkColorPriv.h" +#include "SkString.h" +#include "SkUnPreMultiply.h" + +#if SK_SUPPORT_GPU +#include "gl/GrGLEffect.h" +#include "GrContext.h" +#include "GrTBackendEffectFactory.h" +#endif + +void SkLumaColorFilter::filterSpan(const SkPMColor src[], int count, + SkPMColor dst[]) const { + const SkUnPreMultiply::Scale* table = SkUnPreMultiply::GetScaleTable(); + + for (int i = 0; i < count; ++i) { + SkPMColor c = src[i]; + + unsigned r = SkGetPackedR32(c); + unsigned g = SkGetPackedG32(c); + unsigned b = SkGetPackedB32(c); + unsigned a = SkGetPackedA32(c); + + // No need to do anything for white (luminance == 1.0) + if (a != r || a != g || a != b) { + /* + * To avoid un-premultiplying multiple components, we can start + * with the luminance computed in PM space: + * + * Lum = i * (r / a) + j * (g / a) + k * (b / a) + * Lum = (i * r + j * g + k * b) / a + * Lum = Lum'(PM) / a + * + * Then the filter function is: + * + * C' = [ Lum * a, Lum * r, Lum * g, Lum * b ] + * + * which is equivalent to: + * + * C' = [ Lum'(PM), Lum * r, Lum * g, Lum * b ] + */ + unsigned pm_lum = SkComputeLuminance(r, g, b); + unsigned lum = SkUnPreMultiply::ApplyScale(table[a], pm_lum); + + c = SkPackARGB32(pm_lum, + SkMulDiv255Round(r, lum), + SkMulDiv255Round(g, lum), + SkMulDiv255Round(b, lum)); + } + + dst[i] = c; + } +} + +SkColorFilter* SkLumaColorFilter::Create() { + return SkNEW(SkLumaColorFilter); +} + +SkLumaColorFilter::SkLumaColorFilter() + : INHERITED() { +} + +SkLumaColorFilter::SkLumaColorFilter(SkFlattenableReadBuffer& buffer) + : INHERITED(buffer) { +} + +void SkLumaColorFilter::flatten(SkFlattenableWriteBuffer&) const { +} + +#ifdef SK_DEVELOPER +void SkLumaColorFilter::toString(SkString* str) const { + str->append("SkLumaColorFilter "); +} +#endif + +#if SK_SUPPORT_GPU +class LumaColorFilterEffect : public GrEffect { +public: + static GrEffectRef* Create() { + AutoEffectUnref effect(SkNEW(LumaColorFilterEffect)); + return CreateEffectRef(effect); + } + + static const char* Name() { return "Luminance-to-Alpha"; } + + virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE { + return GrTBackendEffectFactory<LumaColorFilterEffect>::getInstance(); + } + + virtual void getConstantColorComponents(GrColor* color, + uint32_t* validFlags) const SK_OVERRIDE { + *validFlags = 0; + } + + class GLEffect : public GrGLEffect { + public: + GLEffect(const GrBackendEffectFactory& factory, + const GrDrawEffect&) + : INHERITED(factory) { + } + + static EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&) { + // this class always generates the same code. + return 0; + } + + virtual void emitCode(GrGLShaderBuilder* builder, + const GrDrawEffect&, + EffectKey, + const char* outputColor, + const char* inputColor, + const TransformedCoordsArray&, + const TextureSamplerArray&) SK_OVERRIDE { + if (NULL == inputColor) { + inputColor = GrGLSLOnesVecf(4); + } + + // The max() is to guard against 0 / 0 during unpremul when the incoming color is + // transparent black. + builder->fsCodeAppendf("\tfloat nonZeroAlpha = max(%s.a, 0.00001);\n", inputColor); + builder->fsCodeAppendf("\tfloat luma = dot(vec3(%f, %f, %f), %s.rgb);\n", + SK_ITU_BT709_LUM_COEFF_R, + SK_ITU_BT709_LUM_COEFF_G, + SK_ITU_BT709_LUM_COEFF_B, + inputColor); + builder->fsCodeAppendf("\t%s = vec4(%s.rgb * luma / nonZeroAlpha, luma);\n", + outputColor, inputColor); + + } + + private: + typedef GrGLEffect INHERITED; + }; + +private: + virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE { + return true; + } +}; + +GrEffectRef* SkLumaColorFilter::asNewEffect(GrContext*) const { + return LumaColorFilterEffect::Create(); +} +#endif diff --git a/src/ports/SkGlobalInitialization_chromium.cpp b/src/ports/SkGlobalInitialization_chromium.cpp index 6b25a4d6b0..5a8d6bbc07 100644 --- a/src/ports/SkGlobalInitialization_chromium.cpp +++ b/src/ports/SkGlobalInitialization_chromium.cpp @@ -13,6 +13,7 @@ #include "SkDashPathEffect.h" #include "SkGradientShader.h" #include "SkLayerDrawLooper.h" +#include "SkLumaColorFilter.h" #include "SkMallocPixelRef.h" #include "SkXfermode.h" #include "SkLumaXfermode.h" @@ -25,6 +26,7 @@ void SkFlattenable::InitializeFlattenables() { SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkCornerPathEffect) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDashPathEffect) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLayerDrawLooper) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLumaColorFilter) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkMallocPixelRef) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkMagnifierImageFilter) diff --git a/src/ports/SkGlobalInitialization_default.cpp b/src/ports/SkGlobalInitialization_default.cpp index 968c5ebcfa..902ec64642 100644 --- a/src/ports/SkGlobalInitialization_default.cpp +++ b/src/ports/SkGlobalInitialization_default.cpp @@ -46,6 +46,7 @@ #include "SkLayerRasterizer.h" #include "SkLerpXfermode.h" #include "SkLightingImageFilter.h" +#include "SkLumaColorFilter.h" #include "SkLumaXfermode.h" #include "SkMagnifierImageFilter.h" #include "SkMatrixConvolutionImageFilter.h" @@ -89,6 +90,7 @@ void SkFlattenable::InitializeFlattenables() { SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLayerDrawLooper) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLayerRasterizer) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLerpXfermode) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLumaColorFilter) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPath1DPathEffect) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(Sk2DPathEffect) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLine2DPathEffect) diff --git a/tests/ColorFilterTest.cpp b/tests/ColorFilterTest.cpp index 4016f2193d..b333ad68f0 100644 --- a/tests/ColorFilterTest.cpp +++ b/tests/ColorFilterTest.cpp @@ -7,7 +7,9 @@ */ #include "Test.h" #include "SkColor.h" +#include "SkColorPriv.h" #include "SkColorFilter.h" +#include "SkLumaColorFilter.h" #include "SkRandom.h" #include "SkXfermode.h" #include "SkOrderedReadBuffer.h" @@ -93,5 +95,38 @@ static void test_asColorMode(skiatest::Reporter* reporter) { } } +/////////////////////////////////////////////////////////////////////////////// + +static void test_lumaColorFilter(skiatest::Reporter* reporter) { + SkPMColor in, out; + SkAutoTUnref<SkColorFilter> lf(SkLumaColorFilter::Create()); + + // Applying luma to white is a nop (luminance(white) == 1.0) + for (unsigned i = 0; i < 256; ++i) { + in = SkPackARGB32(i, i, i, i); + lf->filterSpan(&in, 1, &out); + REPORTER_ASSERT(reporter, out == in); + } + + // Applying luma to black yields transparent black (luminance(black) == 0) + for (unsigned i = 0; i < 256; ++i) { + in = SkPackARGB32(i, 0, 0, 0); + lf->filterSpan(&in, 1, &out); + REPORTER_ASSERT(reporter, out == SK_ColorTRANSPARENT); + } + + // For general colors, a luma filter has an attenuating effect. + for (unsigned i = 1; i < 256; ++i) { + in = SkPackARGB32(i, i, i / 2, i / 3); + lf->filterSpan(&in, 1, &out); + REPORTER_ASSERT(reporter, out != in); + REPORTER_ASSERT(reporter, SkGetPackedA32(out) <= i); + REPORTER_ASSERT(reporter, SkGetPackedR32(out) <= i); + REPORTER_ASSERT(reporter, SkGetPackedG32(out) <= i / 2); + REPORTER_ASSERT(reporter, SkGetPackedB32(out) <= i / 3); + } +} + #include "TestClassDef.h" DEFINE_TESTCLASS("ColorFilter", ColorFilterTestClass, test_asColorMode) +DEFINE_TESTCLASS("LumaColorFilter", LumaColorFilterTestClass, test_lumaColorFilter) |